Extension: Json and NetCDF utilities
#include "dg/file/file.h"
Loading...
Searching...
No Matches
easy_atts.h
Go to the documentation of this file.
1#pragma once
2
3#include <variant>
4#include <vector>
5#include <map>
6#include <string>
7#include <cassert>
8#include <sstream>
9#include <iomanip> // std::put_time
10#include <ctime> // std::localtime
11#include <netcdf.h>
12#include "nc_error.h"
13
14namespace dg
15{
16namespace file
17{
18
25using nc_att_t = std::variant<int, unsigned, float, double, bool, std::string,
26 std::vector<int>, std::vector<unsigned>, std::vector<float>,
27 std::vector<double>, std::vector<bool>>;
28
44inline std::string timestamp( int argc, char* argv[])
45{
46 // TODO: once we're using C++20 this should be implemented using chrono
47 // Get local time
48 auto ttt = std::time(nullptr);
49 std::ostringstream oss;
50 // time string + program-name + args
51 oss << std::put_time(std::gmtime(&ttt), "%FT%TZ");
52 for( int i=0; i<argc; i++) oss << " "<<argv[i];
53 oss << std::endl;
54 return oss.str();
55}
56
76inline const std::map<std::string, std::string> version_flags =
77{
78#ifdef GIT_HASH
79 {"git_hash", GIT_HASH},
80#endif // GIT_HASH
81#ifdef GIT_BRANCH
82 {"git_branch", GIT_BRANCH},
83#endif // GIT_BRANCH
84#ifdef COMPILE_TIME
85 {"compile_time", COMPILE_TIME},
86#endif // COMPILE_TIME
87};
88
90namespace detail
91{
92template<class value_type>
93inline nc_type getNCDataType(){ assert( false && "Type not supported!\n" ); return NC_DOUBLE; }
94template<>
95inline nc_type getNCDataType<double>(){ return NC_DOUBLE;}
96template<>
97inline nc_type getNCDataType<float>(){ return NC_FLOAT;}
98template<>
99inline nc_type getNCDataType<int>(){ return NC_INT;}
100template<>
101inline nc_type getNCDataType<unsigned>(){ return NC_UINT;}
102template<>
103inline nc_type getNCDataType<bool>(){ return NC_BYTE;}
104template<>
105inline nc_type getNCDataType<std::string>(){ return NC_STRING;}
106template<>
107inline nc_type getNCDataType<const char*>(){ return NC_STRING;}
108
109// Variant for user defined data types (compound types)
110// S allows both std::string and const char* to be used
111template<class S, class T>
112void put_att( int ncid, int varid, const std::tuple<S, nc_type,
113 std::vector<T>>& att)
114{
115 // This will convert const char* to std::string
116 std::string name_string = std::get<0>(att);
117 auto name = name_string.c_str();
118 nc_type xtype = std::get<1>(att);
119 const std::vector<T>& data = std::get<2>(att);
120 unsigned size = data.size();
121 NC_Error_Handle err;
122 // Test xtype ? netcdf allows xtype to be anything ...
123 if constexpr( std::is_same_v<T, int>)
124 {
125 err = nc_put_att_int( ncid, varid, name, xtype, size, &data[0]);
126 }
127 else if constexpr( std::is_same_v<T, unsigned>)
128 {
129 err = nc_put_att_uint( ncid, varid, name, xtype, size, &data[0]);
130 }
131 else if constexpr( std::is_same_v<T, float>)
132 {
133 err = nc_put_att_float( ncid, varid, name, xtype, size, &data[0]);
134 }
135 else if constexpr( std::is_same_v<T, double>)
136 {
137 err = nc_put_att_double( ncid, varid, name, xtype, size, &data[0]);
138 }
139 else if constexpr( std::is_same_v<T, std::string> or
140 std::is_same_v<T, const char*>)
141 {
142 if( size != 1)
143 throw std::runtime_error( "Cannot write a string array attribute to NetCDF");
144 std::string tmp = data[0];
145 err = nc_put_att_text( ncid, varid, name, tmp.size(), tmp.c_str());
146 }
147 else if constexpr( std::is_same_v<T, bool>)
148 {
149 // std::vector<bool> is not necessarily contiguous
150 std::vector<signed char> dataB(size);
151 for( unsigned i=0; i<size; i++)
152 dataB[i] = data[i];
153 err = nc_put_att_schar( ncid, varid, name, NC_BYTE, size, &dataB[0]);
154 }
155 else // default
156 {
157 err = nc_put_att( ncid, varid, name, xtype, size, &data[0]);
158 }
159}
160
161template<class S, class T> // T cannot be nc_att_t
162void put_att( int ncid, int varid, std::tuple<S, nc_type, T> att)
163{
164 put_att( ncid, varid, std::make_tuple( std::get<0>(att), std::get<1>(att),
165 std::vector<T>( 1, std::get<2>(att)) ));
166}
167
168// Variants for normal types
169template<class S, class T>
170void put_att( int ncid, int varid, const std::pair<S, T>& att)
171{
172 put_att( ncid, varid, std::make_tuple( att.first,
173 detail::getNCDataType<T>(), std::vector<T>(1,att.second)));
174}
175
176template<class S, class T>
177void put_att( int ncid, int varid, const std::pair<S, std::vector<T>>& att)
178{
179 put_att( ncid, varid, std::make_tuple( att.first,
180 detail::getNCDataType<T>(), att.second));
181}
182// Amazing
183template<class S>
184void put_att( int ncid, int varid, const std::pair<S, nc_att_t>& att)
185{
186 S name = att.first;
187 const nc_att_t& v = att.second;
188 std::visit( [ncid, varid, name]( auto&& arg) { put_att( ncid, varid,
189 std::make_pair( name, arg)); }, v);
190}
191
192
193template<class Iterable> // *it must be usable in put_att
194void put_atts( int ncid, int varid, const Iterable& atts)
195{
196 for( const auto& it : atts)
197 put_att( ncid, varid, it);
198}
199
201
202template<class T>
203std::vector<T> get_att_v( int ncid, int varid, std::string att)
204{
205 auto name = att.c_str();
206 size_t size, str_len = 0;
207 NC_Error_Handle err;
208 err = nc_inq_attlen( ncid, varid, name, &size);
209 nc_type xtype;
210 err = nc_inq_atttype( ncid, varid, name, &xtype);
211 if ( xtype == NC_STRING or xtype == NC_CHAR)
212 {
213 str_len = size;
214 size = 1;
215 }
216 std::vector<T> data(size);
217 if ( xtype == NC_STRING or xtype == NC_CHAR)
218 {
219 std::string str( str_len, 'x');
220 err = nc_get_att_text( ncid, varid, name, str_len == 0 ? NULL : &str[0]);
221 if constexpr ( std::is_convertible_v<std::string,T>)
222 data[0] = str;
223 else
224 throw std::runtime_error("Cannot convert NC_STRING to given type");
225 }
226 else if ( xtype == NC_INT)
227 {
228 std::vector<int> tmp( size);
229 err = nc_get_att_int( ncid, varid, name, &tmp[0]);
230 if constexpr ( std::is_convertible_v<int,T>)
231 std::transform(tmp.begin(), tmp.end(), data.begin(), [](int val) { return T(val);});
232 else
233 throw std::runtime_error("Cannot convert NC_INT to given type");
234 }
235 else if ( xtype == NC_UINT)
236 {
237 std::vector<unsigned> tmp( size);
238 err = nc_get_att_uint( ncid, varid, name, &tmp[0]);
239 if constexpr ( std::is_convertible_v<unsigned,T>)
240 std::transform(tmp.begin(), tmp.end(), data.begin(), [](unsigned val) { return T(val);});
241 else
242 throw std::runtime_error("Cannot convert NC_UINT to given type");
243 }
244 else if ( xtype == NC_FLOAT)
245 {
246 std::vector<float> tmp( size);
247 err = nc_get_att_float( ncid, varid, name, &tmp[0]);
248 if constexpr ( std::is_convertible_v<float,T>)
249 std::transform(tmp.begin(), tmp.end(), data.begin(), [](float val) { return T(val);});
250 else
251 throw std::runtime_error("Cannot convert NC_FLOAT to given type");
252 }
253 else if ( xtype == NC_DOUBLE)
254 {
255 std::vector<double> tmp( size);
256 err = nc_get_att_double( ncid, varid, name, &tmp[0]);
257 if constexpr ( std::is_convertible_v<double,T>)
258 std::transform(tmp.begin(), tmp.end(), data.begin(), [](double val) { return T(val);});
259 else
260 throw std::runtime_error("Cannot convert NC_DOUBLE to given type");
261 }
262 else if( xtype == NC_BYTE) // counts as bool
263 {
264 std::vector<signed char> tmp(size);
265 err = nc_get_att_schar( ncid, varid, name, &tmp[0]);
266 if constexpr ( std::is_convertible_v<bool,T>)
267 std::transform(tmp.begin(), tmp.end(), data.begin(), [](signed char val) { return T(val);});
268 else
269 throw std::runtime_error("Cannot convert NC_BYTE to given type");
270 }
271 else // default
272 {
273 // std::vector<bool> is not necessarily contiguous
274 if constexpr (std::is_same_v<T, bool>)
275 {
276 std::vector<signed char> tmp(size);
277 err = nc_get_att( ncid, varid, name, &tmp[0]);
278 std::transform(tmp.begin(), tmp.end(), data.begin(), [](signed char val) { return T(val);});
279 }
280 else
281 err = nc_get_att( ncid, varid, name, &data[0]);
282 }
283 return data;
284}
285
286inline dg::file::nc_att_t get_att_t( int ncid, int varid,
287 std::string att_name)
288{
289 auto name = att_name.c_str();
290 size_t size;
291 NC_Error_Handle err;
292 err = nc_inq_attlen( ncid, varid, name, &size);
293 nc_type xtype;
294 err = nc_inq_atttype( ncid, varid, name, &xtype);
295 if ( xtype == NC_INT and size == 1)
296 return get_att_v<int>( ncid, varid, att_name)[0];
297 else if ( xtype == NC_INT and size != 1)
298 return get_att_v<int>( ncid, varid, att_name);
299 else if ( xtype == NC_UINT and size == 1)
300 return get_att_v<unsigned>( ncid, varid, att_name)[0];
301 else if ( xtype == NC_UINT and size != 1)
302 return get_att_v<unsigned>( ncid, varid, att_name);
303 else if ( xtype == NC_FLOAT and size == 1)
304 return get_att_v<float>( ncid, varid, att_name)[0];
305 else if ( xtype == NC_FLOAT and size != 1)
306 return get_att_v<float>( ncid, varid, att_name);
307 else if ( xtype == NC_DOUBLE and size == 1)
308 return get_att_v<double>( ncid, varid, att_name)[0];
309 else if ( xtype == NC_DOUBLE and size != 1)
310 return get_att_v<double>( ncid, varid, att_name);
311 else if ( xtype == NC_BYTE and size == 1)
312 {
313 // BugFix: explicitly convert to bool
314 bool value = get_att_v<bool>( ncid, varid, att_name)[0];
315 return value;
316 }
317 else if ( xtype == NC_BYTE and size != 1)
318 return get_att_v<bool>( ncid, varid, att_name);
319 else if ( xtype == NC_STRING || xtype == NC_CHAR)
320 return get_att_v<std::string>( ncid, varid, att_name)[0];
321 else
322 throw std::runtime_error( "Cannot convert attribute type to nc_att_t");
323}
324
325//namespace detail
326//{
327// utility overloads to be able to implement get_att and get_atts
328template<class T>
329void get_att_h( int ncid, int varid, std::string att_name, T& att)
330{
331 att = get_att_v<T>( ncid, varid, att_name)[0];
332}
333template<class T>
334void get_att_h( int ncid, int varid, std::string att_name, std::vector<T>& att)
335{
336 att = get_att_v<T>( ncid, varid, att_name);
337}
338inline void get_att_h( int ncid, int varid, std::string att_name, dg::file::nc_att_t& att)
339{
340 att = get_att_t( ncid, varid, att_name);
341}
342//}
343template<class T> // T can be nc_att_t
344T get_att_as( int ncid, int varid, std::string att_name)
345{
346 T att;
347 detail::get_att_h( ncid, varid, att_name, att);
348 return att;
349}
350template<class T> // T can be nc_att_t
351std::map<std::string, T> get_atts_as( int ncid, int varid)
352{
353 NC_Error_Handle err;
354 int number;
355 if( varid == NC_GLOBAL)
356 err = nc_inq_natts( ncid, &number);
357 else
358 err = nc_inq_varnatts( ncid, varid, &number);
359 std::map < std::string, T> map;
360 for( int i=0; i<number; i++)
361 {
362 char att_name[NC_MAX_NAME]; // 256
363 err = nc_inq_attname( ncid, varid, i, att_name);
364 detail::get_att_h( ncid, varid, att_name, map[att_name]);
365 }
366 return map;
367}
368} // namespace detail
370}// namespace file
371}// namespace dg
const std::map< std::string, std::string > version_flags
Version compile time constants available as a map.
Definition easy_atts.h:76
std::variant< int, unsigned, float, double, bool, std::string, std::vector< int >, std::vector< unsigned >, std::vector< float >, std::vector< double >, std::vector< bool > > nc_att_t
Utility type to simplify dealing with heterogeneous attribute types.
Definition easy_atts.h:25
std::string timestamp(int argc, char *argv[])
Generate one line entry for the history global attribute.
Definition easy_atts.h:44
Definition easy_atts.h:15