1 /*
2 MDAL - Mesh Data Abstraction Library (MIT License)
3 Copyright (C) 2019 Peter Petrik (zilolv at gmail dot com)
4 */
5
6 #include <string>
7 #include <vector>
8 #include <assert.h>
9 #include <netcdf.h>
10 #include <cmath>
11
12 #include "mdal_netcdf.hpp"
13 #include "mdal.h"
14 #include "mdal_utils.hpp"
15 #include "mdal_logger.hpp"
16
NetCDFFile()17 NetCDFFile::NetCDFFile(): mNcid( 0 ) {}
18
~NetCDFFile()19 NetCDFFile::~NetCDFFile()
20 {
21 if ( mNcid != 0 )
22 {
23 nc_close( mNcid );
24 mNcid = 0;
25 }
26 }
27
handle() const28 int NetCDFFile::handle() const
29 {
30 return mNcid;
31 }
32
openFile(const std::string & fileName)33 void NetCDFFile::openFile( const std::string &fileName )
34 {
35 int res = nc_open( fileName.c_str(), NC_NOWRITE, &mNcid );
36 if ( res != NC_NOERR )
37 {
38 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not open file " + fileName );
39 }
40 mFileName = fileName;
41 }
42
readIntArr(const std::string & name,size_t dim) const43 std::vector<int> NetCDFFile::readIntArr( const std::string &name, size_t dim ) const
44 {
45 assert( mNcid != 0 );
46 int arr_id;
47 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Internal error in Netcfd - unknown format" );
48 std::vector<int> arr_val( dim );
49 if ( nc_get_var_int( mNcid, arr_id, arr_val.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Internal error in Netcfd - unknown format" );
50 return arr_val;
51 }
52
readIntArr(int arr_id,size_t start_dim1,size_t start_dim2,size_t count_dim1,size_t count_dim2) const53 std::vector<int> NetCDFFile::readIntArr( int arr_id, size_t start_dim1, size_t start_dim2, size_t count_dim1, size_t count_dim2 ) const
54 {
55 assert( mNcid != 0 );
56
57 const std::vector<size_t> startp = {start_dim1, start_dim2};
58 const std::vector<size_t> countp = {count_dim1, count_dim2};
59 const std::vector<ptrdiff_t> stridep = {1, 1};
60
61 std::vector<int> arr_val( count_dim1 * count_dim2 );
62 int res = nc_get_vars_int( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() );
63 if ( res != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read numeric array" );
64 return arr_val;
65 }
66
readIntArr(int arr_id,size_t start_dim,size_t count_dim) const67 std::vector<int> NetCDFFile::readIntArr( int arr_id, size_t start_dim, size_t count_dim ) const
68 {
69 assert( mNcid != 0 );
70
71 const std::vector<size_t> startp = {start_dim};
72 const std::vector<size_t> countp = {count_dim};
73 const std::vector<ptrdiff_t> stridep = {1};
74
75 std::vector<int> arr_val( count_dim );
76 int res = nc_get_vars_int( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() );
77 if ( res != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read numeric array" );
78 return arr_val;
79 }
80
readDoubleArr(const std::string & name,size_t dim) const81 std::vector<double> NetCDFFile::readDoubleArr( const std::string &name, size_t dim ) const
82 {
83 assert( mNcid != 0 );
84
85 int arr_id;
86 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
87 std::vector<double> arr_val( dim );
88
89 nc_type typep;
90 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
91 if ( nc_inq_vartype( mNcid, arr_id, &typep ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
92
93 if ( typep == NC_FLOAT )
94 {
95 std::vector<float> arr_val_f( dim );
96 if ( nc_get_var_float( mNcid, arr_id, arr_val_f.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
97 for ( size_t i = 0; i < dim; ++i )
98 {
99 const float val = arr_val_f[i];
100 if ( std::isnan( val ) )
101 arr_val[i] = std::numeric_limits<double>::quiet_NaN();
102 else
103 arr_val[i] = static_cast<double>( val );
104 }
105 }
106 else if ( typep == NC_DOUBLE )
107 {
108 if ( nc_get_var_double( mNcid, arr_id, arr_val.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
109 }
110 else
111 {
112 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
113 }
114 return arr_val;
115 }
116
readDoubleArr(int arr_id,size_t start_dim1,size_t start_dim2,size_t count_dim1,size_t count_dim2) const117 std::vector<double> NetCDFFile::readDoubleArr( int arr_id,
118 size_t start_dim1, size_t start_dim2,
119 size_t count_dim1, size_t count_dim2 ) const
120 {
121 assert( mNcid != 0 );
122
123 const std::vector<size_t> startp = {start_dim1, start_dim2};
124 const std::vector<size_t> countp = {count_dim1, count_dim2};
125 const std::vector<ptrdiff_t> stridep = {1, 1};
126
127 std::vector<double> arr_val( count_dim1 * count_dim2 );
128
129 nc_type typep;
130 if ( nc_inq_vartype( mNcid, arr_id, &typep ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
131
132 if ( typep == NC_FLOAT )
133 {
134 std::vector<float> arr_val_f( count_dim1 * count_dim2 );
135 if ( nc_get_vars_float( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_f.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
136 for ( size_t i = 0; i < count_dim1 * count_dim2; ++i )
137 {
138 const float val = arr_val_f[i];
139 if ( std::isnan( val ) )
140 arr_val[i] = std::numeric_limits<double>::quiet_NaN();
141 else
142 arr_val[i] = static_cast<double>( val );
143 }
144 }
145 else if ( typep == NC_BYTE )
146 {
147 std::vector<unsigned char> arr_val_b( count_dim1 * count_dim2 );
148 if ( nc_get_vars_uchar( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_b.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
149 for ( size_t i = 0; i < count_dim1 * count_dim2; ++i )
150 {
151 const unsigned char val = arr_val_b[i];
152 if ( val == 129 )
153 arr_val[i] = std::numeric_limits<double>::quiet_NaN();
154 else
155 arr_val[i] = double( int( val ) );
156 }
157 }
158 else if ( typep == NC_DOUBLE )
159 {
160 if ( nc_get_vars_double( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
161 }
162 else
163 {
164 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
165 }
166 return arr_val;
167 }
168
readDoubleArr(int arr_id,size_t start_dim,size_t count_dim) const169 std::vector<double> NetCDFFile::readDoubleArr( int arr_id,
170 size_t start_dim,
171 size_t count_dim
172 ) const
173 {
174 assert( mNcid != 0 );
175
176 const std::vector<size_t> startp = {start_dim};
177 const std::vector<size_t> countp = {count_dim};
178 const std::vector<ptrdiff_t> stridep = {1, 1};
179
180 std::vector<double> arr_val( count_dim );
181
182 nc_type typep;
183 if ( nc_inq_vartype( mNcid, arr_id, &typep ) != NC_NOERR )
184 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
185
186 if ( typep == NC_FLOAT )
187 {
188 std::vector<float> arr_val_f( count_dim );
189 if ( nc_get_vars_float( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_f.data() ) != NC_NOERR )
190 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
191 for ( size_t i = 0; i < count_dim; ++i )
192 {
193 const float val = arr_val_f[i];
194 if ( std::isnan( val ) )
195 arr_val[i] = std::numeric_limits<double>::quiet_NaN();
196 else
197 arr_val[i] = static_cast<double>( val );
198 }
199 }
200 else if ( typep == NC_INT )
201 {
202 std::vector<int> arr_val_int( count_dim );
203 if ( nc_get_vars_int( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_int.data() ) != NC_NOERR )
204 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
205 for ( size_t i = 0; i < count_dim; ++i )
206 {
207 arr_val[i] = static_cast<double>( arr_val_int[i] );
208 }
209 }
210 else if ( typep == NC_DOUBLE )
211 {
212 if ( nc_get_vars_double( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() ) != NC_NOERR )
213 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
214 }
215 else
216 {
217 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
218 }
219 return arr_val;
220 }
221
hasArr(const std::string & name) const222 bool NetCDFFile::hasArr( const std::string &name ) const
223 {
224 assert( mNcid != 0 );
225 int arr_id;
226 return nc_inq_varid( mNcid, name.c_str(), &arr_id ) == NC_NOERR;
227 }
228
arrId(const std::string & name) const229 int NetCDFFile::arrId( const std::string &name ) const
230 {
231 int arr_id = -1;
232 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR )
233 {
234 arr_id = -1;
235 }
236 return arr_id;
237 }
238
readArrNames() const239 std::vector<std::string> NetCDFFile::readArrNames() const
240 {
241 assert( mNcid != 0 );
242
243 std::vector<std::string> res;
244 int nvars;
245 if ( nc_inq_varids( mNcid, &nvars, nullptr ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" );
246
247 std::vector<int> varids( static_cast<size_t>( nvars ) );
248 if ( nc_inq_varids( mNcid, &nvars, varids.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" );
249
250 for ( size_t i = 0; i < static_cast<size_t>( nvars ); ++i )
251 {
252 std::vector<char> cname( NC_MAX_NAME + 1 );
253 if ( nc_inq_varname( mNcid, varids[i], cname.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" );
254 res.push_back( cname.data() );
255 }
256
257 return res;
258 }
259
hasAttrInt(const std::string & name,const std::string & attr_name) const260 bool NetCDFFile::hasAttrInt( const std::string &name, const std::string &attr_name ) const
261 {
262 assert( mNcid != 0 );
263
264 int arr_id;
265 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) return false;
266
267 int val;
268 if ( nc_get_att_int( mNcid, arr_id, attr_name.c_str(), &val ) ) return false;
269
270 return true;
271 }
272
getAttrInt(const std::string & name,const std::string & attr_name) const273 int NetCDFFile::getAttrInt( const std::string &name, const std::string &attr_name ) const
274 {
275 assert( mNcid != 0 );
276
277 int arr_id;
278 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get numeric attribute" );
279
280 int val;
281 if ( nc_get_att_int( mNcid, arr_id, attr_name.c_str(), &val ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get numeric attribute" );
282 return val;
283 }
284
getAttrStr(const std::string & name,const std::string & attr_name) const285 std::string NetCDFFile::getAttrStr( const std::string &name, const std::string &attr_name ) const
286 {
287 assert( mNcid != 0 );
288
289 int arr_id;
290 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get string attribute" );
291 return getAttrStr( attr_name, arr_id );
292 }
293
getAttrStr(const std::string & attr_name,int varid) const294 std::string NetCDFFile::getAttrStr( const std::string &attr_name, int varid ) const
295 {
296 assert( mNcid != 0 );
297
298 size_t attlen = 0;
299
300 if ( nc_inq_attlen( mNcid, varid, attr_name.c_str(), &attlen ) )
301 {
302 // attribute is missing
303 return std::string();
304 }
305
306 char *string_attr = static_cast<char *>( malloc( attlen + 1 ) );
307
308 if ( nc_get_att_text( mNcid, varid, attr_name.c_str(), string_attr ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get string attribute" );
309 string_attr[attlen] = '\0';
310
311 std::string res( string_attr );
312 free( string_attr );
313
314 return res;
315 }
316
getFillValue(int varid) const317 double NetCDFFile::getFillValue( int varid ) const
318 {
319 return getAttrDouble( varid, "_FillValue" );
320 }
321
hasAttrDouble(int varid,const std::string & attr_name) const322 bool NetCDFFile::hasAttrDouble( int varid, const std::string &attr_name ) const
323 {
324 double res;
325 if ( nc_get_att_double( mNcid, varid, attr_name.c_str(), &res ) )
326 return false;
327 return true;
328 }
329
getAttrDouble(int varid,const std::string & attr_name) const330 double NetCDFFile::getAttrDouble( int varid, const std::string &attr_name ) const
331 {
332 double res;
333 if ( nc_get_att_double( mNcid, varid, attr_name.c_str(), &res ) )
334 res = std::numeric_limits<double>::quiet_NaN(); // not present/set
335 return res;
336 }
337
338
getVarId(const std::string & name)339 int NetCDFFile::getVarId( const std::string &name )
340 {
341 int ncid_val;
342 if ( nc_inq_varid( mNcid, name.c_str(), &ncid_val ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get variable id" );
343 return ncid_val;
344 }
345
getDimension(const std::string & name,size_t * val,int * ncid_val) const346 void NetCDFFile::getDimension( const std::string &name, size_t *val, int *ncid_val ) const
347 {
348 assert( mNcid != 0 );
349
350 if ( nc_inq_dimid( mNcid, name.c_str(), ncid_val ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimension, invalid dimension ID or name" );
351 if ( nc_inq_dimlen( mNcid, *ncid_val, val ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimension, invalid dimension ID or name" );
352 }
353
getDimensions(const std::string & variableName,std::vector<size_t> & dimensions,std::vector<int> & dimensionIds)354 void NetCDFFile::getDimensions( const std::string &variableName, std::vector<size_t> &dimensions, std::vector<int> &dimensionIds )
355 {
356 assert( mNcid != 0 );
357
358 int n;
359 int varId;
360 if ( nc_inq_varid( mNcid, variableName.c_str(), &varId ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" );
361 if ( nc_inq_varndims( mNcid, varId, &n ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" );
362
363 dimensionIds.resize( size_t( n ) );
364 dimensions.resize( size_t( n ) );
365
366 if ( nc_inq_vardimid( mNcid, varId, dimensionIds.data() ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" );
367
368 for ( int i = 0; i < n; ++i )
369 {
370 nc_inq_dimlen( mNcid, dimensionIds[size_t( i )], &dimensions[size_t( i )] );
371 }
372 }
373
hasDimension(const std::string & name) const374 bool NetCDFFile::hasDimension( const std::string &name ) const
375 {
376 int ncid_val;
377 return nc_inq_dimid( mNcid, name.c_str(), &ncid_val ) == NC_NOERR;
378 }
379
createFile(const std::string & fileName)380 void NetCDFFile::createFile( const std::string &fileName )
381 {
382 int res = nc_create( fileName.c_str(), NC_CLOBBER, &mNcid );
383 if ( res != NC_NOERR )
384 {
385 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
386 }
387 }
388
defineDimension(const std::string & name,size_t size)389 int NetCDFFile::defineDimension( const std::string &name, size_t size )
390 {
391 int dimId = 0;
392 int res = nc_def_dim( mNcid, name.c_str(), size, &dimId );
393 if ( res != NC_NOERR )
394 {
395 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
396 }
397 return dimId;
398 }
399
defineVar(const std::string & varName,int ncType,int dimensionCount,const int * dimensions)400 int NetCDFFile::defineVar( const std::string &varName,
401 int ncType, int dimensionCount, const int *dimensions )
402 {
403 int varIdp;
404
405 int res = nc_def_var( mNcid, varName.c_str(), ncType, dimensionCount, dimensions, &varIdp );
406 if ( res != NC_NOERR )
407 {
408 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
409 }
410
411 return varIdp;
412 }
413
putAttrStr(int varId,const std::string & attrName,const std::string & value)414 void NetCDFFile::putAttrStr( int varId, const std::string &attrName, const std::string &value )
415 {
416 int res = nc_put_att_text( mNcid, varId, attrName.c_str(), value.size(), value.c_str() );
417 if ( res != NC_NOERR )
418 {
419 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
420 }
421 }
422
putAttrInt(int varId,const std::string & attrName,int value)423 void NetCDFFile::putAttrInt( int varId, const std::string &attrName, int value )
424 {
425 int res = nc_put_att_int( mNcid, varId, attrName.c_str(), NC_INT, 1, &value );
426 if ( res != NC_NOERR )
427 {
428 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
429 }
430 }
431
putAttrDouble(int varId,const std::string & attrName,double value)432 void NetCDFFile::putAttrDouble( int varId, const std::string &attrName, double value )
433 {
434 int res = nc_put_att_double( mNcid, varId, attrName.c_str(), NC_DOUBLE, 1, &value );
435 if ( res != NC_NOERR )
436 {
437 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
438 }
439 }
440
putDataDouble(int varId,const size_t index,const double value)441 void NetCDFFile::putDataDouble( int varId, const size_t index, const double value )
442 {
443 int res = nc_put_var1_double( mNcid, varId, &index, &value );
444 if ( res != NC_NOERR )
445 {
446 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
447 }
448 }
449
putDataArrayInt(int varId,size_t line,size_t faceVerticesMax,int * values)450 void NetCDFFile::putDataArrayInt( int varId, size_t line, size_t faceVerticesMax, int *values )
451 {
452 // Configuration of these two vectors determines how is value array read and stored in the file
453 // https://www.unidata.ucar.edu/software/netcdf/docs/programming_notes.html#specify_hyperslabfileNameToSave
454 const size_t start[] = { line, 0 };
455 const size_t count[] = { 1, faceVerticesMax };
456
457 int res = nc_put_vara_int( mNcid, varId, start, count, values );
458 if ( res != NC_NOERR )
459 {
460 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
461 }
462 }
463
getFileName() const464 std::string NetCDFFile::getFileName() const
465 {
466 return mFileName;
467 }
468