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 ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
184
185 if ( typep == NC_FLOAT )
186 {
187 std::vector<float> arr_val_f( count_dim );
188 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" );
189 for ( size_t i = 0; i < count_dim; ++i )
190 {
191 const float val = arr_val_f[i];
192 if ( std::isnan( val ) )
193 arr_val[i] = std::numeric_limits<double>::quiet_NaN();
194 else
195 arr_val[i] = static_cast<double>( val );
196 }
197 }
198 else if ( typep == NC_DOUBLE )
199 {
200 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" );
201 }
202 else
203 {
204 throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" );
205 }
206 return arr_val;
207 }
208
hasArr(const std::string & name) const209 bool NetCDFFile::hasArr( const std::string &name ) const
210 {
211 assert( mNcid != 0 );
212 int arr_id;
213 return nc_inq_varid( mNcid, name.c_str(), &arr_id ) == NC_NOERR;
214 }
215
arrId(const std::string & name) const216 int NetCDFFile::arrId( const std::string &name ) const
217 {
218 int arr_id = -1;
219 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR )
220 {
221 arr_id = -1;
222 }
223 return arr_id;
224 }
225
readArrNames() const226 std::vector<std::string> NetCDFFile::readArrNames() const
227 {
228 assert( mNcid != 0 );
229
230 std::vector<std::string> res;
231 int nvars;
232 if ( nc_inq_varids( mNcid, &nvars, nullptr ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" );
233
234 std::vector<int> varids( static_cast<size_t>( nvars ) );
235 if ( nc_inq_varids( mNcid, &nvars, varids.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" );
236
237 for ( size_t i = 0; i < static_cast<size_t>( nvars ); ++i )
238 {
239 std::vector<char> cname( NC_MAX_NAME + 1 );
240 if ( nc_inq_varname( mNcid, varids[i], cname.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" );
241 res.push_back( cname.data() );
242 }
243
244 return res;
245 }
246
hasAttrInt(const std::string & name,const std::string & attr_name) const247 bool NetCDFFile::hasAttrInt( const std::string &name, const std::string &attr_name ) const
248 {
249 assert( mNcid != 0 );
250
251 int arr_id;
252 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) return false;
253
254 int val;
255 if ( nc_get_att_int( mNcid, arr_id, attr_name.c_str(), &val ) ) return false;
256
257 return true;
258 }
259
getAttrInt(const std::string & name,const std::string & attr_name) const260 int NetCDFFile::getAttrInt( 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 ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get numeric attribute" );
266
267 int val;
268 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" );
269 return val;
270 }
271
getAttrStr(const std::string & name,const std::string & attr_name) const272 std::string NetCDFFile::getAttrStr( const std::string &name, const std::string &attr_name ) const
273 {
274 assert( mNcid != 0 );
275
276 int arr_id;
277 if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get string attribute" );
278 return getAttrStr( attr_name, arr_id );
279 }
280
getAttrStr(const std::string & attr_name,int varid) const281 std::string NetCDFFile::getAttrStr( const std::string &attr_name, int varid ) const
282 {
283 assert( mNcid != 0 );
284
285 size_t attlen = 0;
286
287 if ( nc_inq_attlen( mNcid, varid, attr_name.c_str(), &attlen ) )
288 {
289 // attribute is missing
290 return std::string();
291 }
292
293 char *string_attr = static_cast<char *>( malloc( attlen + 1 ) );
294
295 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" );
296 string_attr[attlen] = '\0';
297
298 std::string res( string_attr );
299 free( string_attr );
300
301 return res;
302 }
303
getFillValue(int varid) const304 double NetCDFFile::getFillValue( int varid ) const
305 {
306 return getAttrDouble( varid, "_FillValue" );
307 }
308
hasAttrDouble(int varid,const std::string & attr_name) const309 bool NetCDFFile::hasAttrDouble( int varid, const std::string &attr_name ) const
310 {
311 double res;
312 if ( nc_get_att_double( mNcid, varid, attr_name.c_str(), &res ) )
313 return false;
314 return true;
315 }
316
getAttrDouble(int varid,const std::string & attr_name) const317 double NetCDFFile::getAttrDouble( int varid, const std::string &attr_name ) const
318 {
319 double res;
320 if ( nc_get_att_double( mNcid, varid, attr_name.c_str(), &res ) )
321 res = std::numeric_limits<double>::quiet_NaN(); // not present/set
322 return res;
323 }
324
325
getVarId(const std::string & name)326 int NetCDFFile::getVarId( const std::string &name )
327 {
328 int ncid_val;
329 if ( nc_inq_varid( mNcid, name.c_str(), &ncid_val ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get variable id" );
330 return ncid_val;
331 }
332
getDimension(const std::string & name,size_t * val,int * ncid_val) const333 void NetCDFFile::getDimension( const std::string &name, size_t *val, int *ncid_val ) const
334 {
335 assert( mNcid != 0 );
336
337 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" );
338 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" );
339 }
340
getDimensions(const std::string & variableName,std::vector<size_t> & dimensions,std::vector<int> & dimensionIds)341 void NetCDFFile::getDimensions( const std::string &variableName, std::vector<size_t> &dimensions, std::vector<int> &dimensionIds )
342 {
343 assert( mNcid != 0 );
344
345 int n;
346 int varId;
347 if ( nc_inq_varid( mNcid, variableName.c_str(), &varId ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" );
348 if ( nc_inq_varndims( mNcid, varId, &n ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" );
349
350 dimensionIds.resize( size_t( n ) );
351 dimensions.resize( size_t( n ) );
352
353 if ( nc_inq_vardimid( mNcid, varId, dimensionIds.data() ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" );
354
355 for ( int i = 0; i < n; ++i )
356 {
357 nc_inq_dimlen( mNcid, dimensionIds[size_t( i )], &dimensions[size_t( i )] );
358 }
359 }
360
hasDimension(const std::string & name) const361 bool NetCDFFile::hasDimension( const std::string &name ) const
362 {
363 int ncid_val;
364 return nc_inq_dimid( mNcid, name.c_str(), &ncid_val ) == NC_NOERR;
365 }
366
createFile(const std::string & fileName)367 void NetCDFFile::createFile( const std::string &fileName )
368 {
369 int res = nc_create( fileName.c_str(), NC_CLOBBER, &mNcid );
370 if ( res != NC_NOERR )
371 {
372 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
373 }
374 }
375
defineDimension(const std::string & name,size_t size)376 int NetCDFFile::defineDimension( const std::string &name, size_t size )
377 {
378 int dimId = 0;
379 int res = nc_def_dim( mNcid, name.c_str(), size, &dimId );
380 if ( res != NC_NOERR )
381 {
382 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
383 }
384 return dimId;
385 }
386
defineVar(const std::string & varName,int ncType,int dimensionCount,const int * dimensions)387 int NetCDFFile::defineVar( const std::string &varName,
388 int ncType, int dimensionCount, const int *dimensions )
389 {
390 int varIdp;
391
392 int res = nc_def_var( mNcid, varName.c_str(), ncType, dimensionCount, dimensions, &varIdp );
393 if ( res != NC_NOERR )
394 {
395 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
396 }
397
398 return varIdp;
399 }
400
putAttrStr(int varId,const std::string & attrName,const std::string & value)401 void NetCDFFile::putAttrStr( int varId, const std::string &attrName, const std::string &value )
402 {
403 int res = nc_put_att_text( mNcid, varId, attrName.c_str(), value.size(), value.c_str() );
404 if ( res != NC_NOERR )
405 {
406 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
407 }
408 }
409
putAttrInt(int varId,const std::string & attrName,int value)410 void NetCDFFile::putAttrInt( int varId, const std::string &attrName, int value )
411 {
412 int res = nc_put_att_int( mNcid, varId, attrName.c_str(), NC_INT, 1, &value );
413 if ( res != NC_NOERR )
414 {
415 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
416 }
417 }
418
putAttrDouble(int varId,const std::string & attrName,double value)419 void NetCDFFile::putAttrDouble( int varId, const std::string &attrName, double value )
420 {
421 int res = nc_put_att_double( mNcid, varId, attrName.c_str(), NC_DOUBLE, 1, &value );
422 if ( res != NC_NOERR )
423 {
424 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
425 }
426 }
427
putDataDouble(int varId,const size_t index,const double value)428 void NetCDFFile::putDataDouble( int varId, const size_t index, const double value )
429 {
430 int res = nc_put_var1_double( mNcid, varId, &index, &value );
431 if ( res != NC_NOERR )
432 {
433 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
434 }
435 }
436
putDataArrayInt(int varId,size_t line,size_t faceVerticesMax,int * values)437 void NetCDFFile::putDataArrayInt( int varId, size_t line, size_t faceVerticesMax, int *values )
438 {
439 // Configuration of these two vectors determines how is value array read and stored in the file
440 // https://www.unidata.ucar.edu/software/netcdf/docs/programming_notes.html#specify_hyperslabfileNameToSave
441 const size_t start[] = { line, 0 };
442 const size_t count[] = { 1, faceVerticesMax };
443
444 int res = nc_put_vara_int( mNcid, varId, start, count, values );
445 if ( res != NC_NOERR )
446 {
447 throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) );
448 }
449 }
450
getFileName() const451 std::string NetCDFFile::getFileName() const
452 {
453 return mFileName;
454 }
455