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