NGen
Loading...
Searching...
No Matches
bmi_utilities.hpp
1#ifndef NGEN_BMI_UTILITIES_HPP
2#define NGEN_BMI_UTILITIES_HPP
3
4#include <string>
5#include <vector>
6#include <boost/type_index.hpp>
7#include "Bmi_Adapter.hpp"
8
9namespace models {
10 namespace bmi {
11 namespace helper {
12
24 template<typename TO, typename FROM>
25 static inline std::vector<TO> make_vector(const FROM* data, const size_t& count){
26 //Let Return Value Optimization (move semantics) help here (no copy of vector)
27 return std::vector<TO>(data, data+count);
28 }
29 }
30
40 template <typename T>
41 std::vector<T> GetValue(Bmi_Adapter& model, const std::string& name) {
42 //TODO make model const ref
43 int total_mem = model.GetVarNbytes(name);
44 int item_size = model.GetVarItemsize(name);
45 if( total_mem == 0 || item_size == 0){
46 // Early stop if no items to be returned (also prevents possible divide by 0 computing num_items)
47 // Becuase this function is used by others which assume the return value to have AT LEAST
48 // 1 value, then this is a terminal error. This happens when the BMI model hasn't properly
49 // initialized/allocated/set the value to something meaningful. This can happen, for example,
50 // with grid data if the variables themselves are dynamically allocated based on grid properties
51 // but the grid is 0, then we essentially get a sentinal pointer with no data in it.
52 // one possible way to handle this would be to ensure a single element vector with NaN were returned here
53 // but since this function has to deal with both int and floating point types, that isn't possible
54 // because int has not NaN representation. So the best we can do at this point is raise a runtime exception
55 throw std::runtime_error("Unable to get value of variable "+name+". Model "+ model.get_model_name() +
56 " reports no valid items (Nbytes = "+std::to_string(total_mem) +
57 ", Itemsize = "+std::to_string(item_size)+".");
58 }
59 int num_items = total_mem/item_size;
60 //Determine what type we need to cast from
61 std::string type = model.get_analogous_cxx_type(model.GetVarType(name), item_size);
62
63 //C++ form of malloc
64 void* data = ::operator new(total_mem); //Possible to allocate 0 bytes...
65 // Use smart pointer to ensure cleanup on throw/out of scope...
66 // This works, and is relatively cheap since the lambda is stateless, only one instance should be created.
67 auto sptr = std::shared_ptr<void>(data, [](void *p) { ::operator delete(p); });
68 //Delegate to specific adapter's GetValue()
69 //Note, may be able to optimize this furthur using GetValuePtr
70 //which would avoid copying in the BMI model and copying again here
71 model.GetValue(name, data);
72 std::vector<T> result;
73
74 /*
75 * Allows the std::vector constructor to type cast the values as it copies them.
76 * I don't see any other way around the typing issue other than an explicit copy of each...
77 * Now there is an early optimization that allows types that align to pass through uncopied
78 * but that also only works if GetValuePtr returns a compatible pointer that can iterate
79 * on the recieving side correctly. This may be tricky for certain langague adapters (Fortran?)
80 * Untill this becomes burdensome on memory/time, I suggest copying and converting each value
81 */
82
83 if (type == "long double"){
84 result = helper::make_vector<T>( (long double*) data, num_items);
85 }
86 else if (type == "double"){
87 result = helper::make_vector<T>( (double*) data, num_items);
88 }
89 else if (type == "float"){
90 result = helper::make_vector<T>( (float*) data, num_items);
91 }
92 else if (type == "short" || type == "short int" || type == "signed short" || type == "signed short int"){
93 result = helper::make_vector<T>( (short*) data, num_items);
94 }
95 else if (type == "unsigned short" || type == "unsigned short int"){
96 result = helper::make_vector<T>( (unsigned short*) data, num_items);
97 }
98 else if (type == "int" || type == "signed" || type == "signed int"){
99 result = helper::make_vector<T>( (int*) data, num_items);
100 }
101 else if (type == "unsigned" || type == "unsigned int"){
102 result = helper::make_vector<T>( (unsigned int*) data, num_items);
103 }
104 else if (type == "long" || type == "long int" || type == "signed long" || type == "signed long int"){
105 result = helper::make_vector<T>( (long*) data, num_items);
106 }
107 else if (type == "unsigned long" || type == "unsigned long int"){
108 result = helper::make_vector<T>( (unsigned long*) data, num_items);
109 }
110 else if (type == "long long" || type == "long long int" || type == "signed long long" || type == "signed long long int"){
111 result = helper::make_vector<T>( (long long*) data, num_items);
112 }
113 else if (type == "unsigned long long" || type == "unsigned long long int"){
114 result = helper::make_vector<T>( (unsigned long long*) data, num_items);
115 }
116 else{
117 throw std::runtime_error("Unable to get value of variable " + name +
118 " as " + boost::typeindex::type_id<T>().pretty_name() + ": no logic for converting variable type " + type);
119 }
120 return result;
121 }
122 }
123}
124
125#endif //NGEN_BMI_UTILITIES_HPP
virtual std::string GetVarType(std::string name)=0
virtual void GetValue(std::string name, void *dest)=0
virtual int GetVarItemsize(std::string name)=0
virtual int GetVarNbytes(std::string name)=0
Abstract adapter interface for C++ classes to interact with the essential aspects of external models ...
Definition Bmi_Adapter.hpp:17
std::string get_model_name()
Get the model name.
Definition Bmi_Adapter.cpp:123
virtual const std::string get_analogous_cxx_type(const std::string &external_type_name, const size_t item_size)=0
Get the name string for the C++ type analogous to the described type in the backing model.
Definition bmi.hpp:16
static std::vector< TO > make_vector(const FROM *data, const size_t &count)
make a vector of type
Definition bmi_utilities.hpp:25
std::vector< T > GetValue(Bmi_Adapter &model, const std::string &name)
Copy values from a BMI model adapter and box them into a vector.
Definition bmi_utilities.hpp:41
Definition AbstractCLibBmiAdapter.hpp:6