8 #include "ioda/io/NetcdfIO.h"
18 #include <boost/math/special_functions/fpclassify.hpp>
22 #include "oops/mpi/mpi.h"
23 #include "oops/util/abor1_cpp.h"
24 #include "oops/util/Duration.h"
25 #include "oops/util/Logger.h"
26 #include "oops/util/missingValues.h"
28 #include "ioda/core/IodaUtils.h"
50 const std::size_t MaxFrameSize) :
51 IodaIO(FileName, FileMode, MaxFrameSize), have_offset_time_(false),
52 have_date_time_(false) {
53 oops::Log::trace() << __func__ <<
" fname_: " <<
fname_ <<
" fmode_: " <<
fmode_ << std::endl;
59 std::string ErrorMsg =
"NetcdfIO::NetcdfIO: Unable to open file: '" +
fname_ +
63 }
else if (
fmode_ ==
"w") {
65 }
else if (
fmode_ ==
"W") {
68 oops::Log::error() <<
"NetcdfIO::NetcdfIO: Unrecognized FileMode: " <<
fmode_ << std::endl;
69 oops::Log::error() <<
"NetcdfIO::NetcdfIO: Must use one of: 'r', 'w', 'W'" << std::endl;
70 ABORT(
"Unrecognized file mode for NetcdfIO constructor");
86 ErrorMsg =
"NetcdfIO::NetcdfIO: Unable to read file object counts";
91 for (std::size_t i = 0; i < NcNdims; i++) {
92 char NcName[NC_MAX_NAME+1];
94 ErrorMsg =
"NetcdfIO::NetcdfIO: Unable to read dimension number: " + std::to_string(i);
99 if (strcmp(NcName,
"nlocs") == 0) {
101 }
else if (strcmp(NcName,
"nvars") == 0) {
128 std::size_t MaxVarSize = 0;
129 for (std::size_t ivar=0; ivar < NcNvars; ++ivar) {
131 char NcVname[NC_MAX_NAME+1];
134 int NcDimIds[NC_MAX_VAR_DIMS];
135 ErrorMsg =
"NetcdfIO::NetcdfIO: Unable to read information for variable number: " +
136 std::to_string(ivar);
137 CheckNcCall(nc_inq_var(
ncid_, ivar, NcVname, &NcDtype, &NcNdims, NcDimIds, 0), ErrorMsg);
140 char NcDtypeName[NC_MAX_NAME+1];
141 std::size_t NcDtypeSize;
142 ErrorMsg =
"NetcdfIO::NetcdfIO: Unable to look up type name";
143 CheckNcCall(nc_inq_type(
ncid_, NcDtype, NcDtypeName, &NcDtypeSize), ErrorMsg);
144 if (strcmp(NcDtypeName,
"char") == 0) {
145 strncpy(NcDtypeName,
"string", NC_MAX_NAME);
147 std::string VarType(NcDtypeName);
149 if (((VarType !=
"string") && (NcNdims == 1)) ||
150 ((VarType ==
"string") && (NcNdims == 2))) {
152 std::vector<std::size_t> NcDimSizes;
153 for (std::size_t j = 0; j < NcNdims; j++) {
159 std::vector<std::size_t> VarShape(1, NcDimSizes[0]);
162 MaxVarSize = std::max(MaxVarSize, VarShape[0]);
166 std::string GroupName;
171 if (strcmp(NcDtypeName,
"double") == 0) {
178 if ((GroupName ==
"PreQC") && (strcmp(NcDtypeName,
"int") != 0)) {
184 if (VarName.compare(
"time") == 0) {
194 grp_var_insert(GroupName,
"datetime",
"string", VarShape, NcVname, NcDtypeName, 20);
198 if (strcmp(NcDtypeName,
"string") == 0) {
199 grp_var_insert(GroupName, VarName, VarType, VarShape, NcVname, NcDtypeName,
202 grp_var_insert(GroupName, VarName, VarType, VarShape, NcVname, NcDtypeName);
220 oops::Log::trace() << __func__ <<
" fname_: " <<
fname_ << std::endl;
240 const std::vector<std::size_t> & Starts,
241 const std::vector<std::size_t> & Counts,
242 int & FillValue, std::vector<int> & VarData) {
244 int NcVarId =
var_id(GroupName, VarName);
247 std::array<char, NC_MAX_NAME> NcVarName;
248 std::string ErrorMsg =
249 "NetcdfIO::ReadVar: Unable to get netcdf variable name: " + std::to_string(NcVarId);
253 VarData.assign(Counts[0], 0);
254 ErrorMsg =
"NetcdfIO::ReadVar: Unable to read netcdf variable: ";
255 ErrorMsg += NcVarName.data();
257 VarData.data()), ErrorMsg);
260 ErrorMsg =
"NetcdfIO::ReadVar: cannot read _FillValue attribute for variable: ";
261 ErrorMsg += NcVarName.data();
262 CheckNcCall(nc_get_att_int(
ncid_, NcVarId,
"_FillValue", &FillValue), ErrorMsg);
264 FillValue = NC_FILL_INT;
269 const std::vector<std::size_t> & Starts,
270 const std::vector<std::size_t> & Counts,
271 float & FillValue, std::vector<float> & VarData) {
273 int NcVarId =
var_id(GroupName, VarName);
276 std::array<char, NC_MAX_NAME> NcVarName;
277 std::string ErrorMsg =
278 "NetcdfIO::ReadVar: Unable to get netcdf variable name: " + std::to_string(NcVarId);
282 VarData.assign(Counts[0], 0.0);
283 ErrorMsg =
"NetcdfIO::ReadVar: Unable to read netcdf variable: ";
284 ErrorMsg += NcVarName.data();
286 VarData.data()), ErrorMsg);
289 ErrorMsg =
"NetcdfIO::ReadVar: cannot read _FillValue attribute for variable: ";
290 ErrorMsg += NcVarName.data();
291 CheckNcCall(nc_get_att_float(
ncid_, NcVarId,
"_FillValue", &FillValue), ErrorMsg);
293 FillValue = NC_FILL_FLOAT;
298 const std::vector<std::size_t> & Starts,
299 const std::vector<std::size_t> & Counts,
300 double & FillValue, std::vector<double> & VarData) {
302 int NcVarId =
var_id(GroupName, VarName);
305 std::array<char, NC_MAX_NAME> NcVarName;
306 std::string ErrorMsg =
307 "NetcdfIO::ReadVar: Unable to get netcdf variable name: " + std::to_string(NcVarId);
311 VarData.assign(Counts[0], 0.0);
312 ErrorMsg =
"NetcdfIO::ReadVar: Unable to read netcdf variable: ";
313 ErrorMsg += NcVarName.data();
314 CheckNcCall(nc_get_vara_double(
ncid_, NcVarId, Starts.data(), Counts.data(),
315 VarData.data()), ErrorMsg);
318 ErrorMsg =
"NetcdfIO::ReadVar: cannot read _FillValue attribute for variable: ";
319 ErrorMsg += NcVarName.data();
320 CheckNcCall(nc_get_att_double(
ncid_, NcVarId,
"_FillValue", &FillValue), ErrorMsg);
322 FillValue = NC_FILL_DOUBLE;
327 const std::vector<std::size_t> & Starts,
328 const std::vector<std::size_t> & Counts,
329 char & FillValue, std::vector<std::string> & VarData) {
331 int NcVarId =
var_id(GroupName, VarName);
334 std::array<char, NC_MAX_NAME> NcVarName;
335 std::string ErrorMsg =
336 "NetcdfIO::ReadVar: Unable to get netcdf variable info: " + std::to_string(NcVarId);
339 ErrorMsg =
"NetcdfIO::ReadVar: Unable to read netcdf variable: ";
340 ErrorMsg += NcVarName.data();
341 if (VarName ==
"datetime") {
344 std::unique_ptr<char[]> CharData(
new char[Counts[0] * Counts[1]]);
346 CharData.get()), ErrorMsg);
355 std::unique_ptr<char[]> CharData(
new char[Counts[0] * Counts[1]]);
357 CharData.get()), ErrorMsg);
362 ErrorMsg =
"NetcdfIO::ReadVar: cannot read _FillValue attribute for variable: ";
363 ErrorMsg += NcVarName.data();
364 CheckNcCall(nc_get_att_text(
ncid_, NcVarId,
"_FillValue", &FillValue), ErrorMsg);
366 FillValue = NC_FILL_CHAR;
381 template <
typename DataType>
383 const DataType missing_value = util::missingValue(missing_value);
384 for (std::size_t i = 0; i < VarData.size(); i++) {
385 if (VarData[i] == NcFillValue || boost::math::isinf(VarData[i])
386 || boost::math::isnan(VarData[i])) {
387 VarData[i] = missing_value;
407 const std::vector<std::size_t> & Starts,
408 const std::vector<std::size_t> & Counts,
409 const std::vector<int> & VarData) {
410 int MissVal = util::missingValue(MissVal);
411 int NcFillVal = NC_FILL_INT;
414 std::vector<int> FileData = VarData;
415 for (std::size_t i = 0; i < FileData.size(); i++) {
416 if (FileData[i] == MissVal) {
417 FileData[i] = NcFillVal;
421 int NcVarId =
var_id(GroupName, VarName);
423 std::string ErrorMsg =
"NetcdfIO::WriteVar: Unable to write dataset: " + NcVarName;
425 FileData.data()), ErrorMsg);
429 const std::vector<std::size_t> & Starts,
430 const std::vector<std::size_t> & Counts,
431 const std::vector<float> & VarData) {
432 float MissVal = util::missingValue(MissVal);
433 float NcFillVal = NC_FILL_FLOAT;
436 std::vector<float> FileData = VarData;
437 for (std::size_t i = 0; i < FileData.size(); i++) {
438 if (FileData[i] == MissVal) {
439 FileData[i] = NcFillVal;
443 int NcVarId =
var_id(GroupName, VarName);
445 std::string ErrorMsg =
"NetcdfIO::WriteVar: Unable to write dataset: " + NcVarName;
447 FileData.data()), ErrorMsg);
451 const std::vector<std::size_t> & Starts,
452 const std::vector<std::size_t> & Counts,
453 const std::vector<std::string> & VarData) {
454 std::unique_ptr<char[]> CharData(
new char[Counts[0] * Counts[1]]);
457 int NcVarId =
var_id(GroupName, VarName);
459 std::string ErrorMsg =
"NetcdfIO::WriteVar: Unable to write dataset: " + NcVarName;
461 CharData.get()), ErrorMsg);
472 std::size_t MaxSize = 0;
473 for (std::size_t i = 0; i < Strings.size(); ++i) {
474 if (Strings[i].size() > MaxSize) {
475 MaxSize = Strings[i].size();
488 const std::vector<std::size_t> & VarShape) {
507 std::vector<int> NcDimIds;
508 if (GroupName.compare(
"MetaData") == 0) {
513 }
else if (GroupName.compare(
"VarMetaData") == 0) {
518 }
else if (GroupName.compare(
"RecMetaData") == 0) {
548 std::string ErrorMsg =
"NetcdfIO::NetcdfIO: Unable to create dimension: " + Name;
573 std::vector<std::size_t> VarShape =
var_shape(ivar);
574 if (VarShape[0] > FrameStart) {
579 std::string VarName =
var_name(ivar);
582 std::vector<std::size_t> VarStarts(1, FrameStart);
584 std::vector<std::size_t> VarCounts;
585 if (FrameStart + FrameSize > VarShape[0]) {
586 VarCounts.push_back(VarShape[0] - FrameStart);
588 VarCounts.push_back(FrameSize);
597 if (VarType ==
"int") {
598 if (FileType ==
"int") {
599 std::vector<int> FileData;
601 NcReadVar(GroupName, VarName, VarStarts, VarCounts, NcFillValue, FileData);
602 ReplaceFillWithMissing<int>(FileData, NcFillValue);
604 }
else if (FileType ==
"float") {
605 std::vector<float> FileData;
607 NcReadVar(GroupName, VarName, VarStarts, VarCounts, NcFillValue, FileData);
608 ReplaceFillWithMissing<float>(FileData, NcFillValue);
610 std::vector<int> FrameData(FileData.size(), 0);
611 ConvertVarType<float, int>(FileData, FrameData);
613 }
else if (FileType ==
"double") {
614 std::vector<double> FileData;
616 NcReadVar(GroupName, VarName, VarStarts, VarCounts, NcFillValue, FileData);
617 ReplaceFillWithMissing<double>(FileData, NcFillValue);
619 std::vector<int> FrameData(FileData.size(), 0);
620 ConvertVarType<double, int>(FileData, FrameData);
623 std::string ErrorMsg =
624 "NetcdfIO::ReadFrame: Conflicting data types for conversion to int: ";
625 ErrorMsg +=
"File variable: " +
file_name(ivar) +
", File type: " + FileType;
628 }
else if (VarType ==
"float") {
629 if (FileType ==
"float") {
630 std::vector<float> FileData;
632 NcReadVar(GroupName, VarName, VarStarts, VarCounts, NcFillValue, FileData);
633 ReplaceFillWithMissing<float>(FileData, NcFillValue);
635 }
else if (FileType ==
"double") {
636 std::vector<double> FileData;
638 NcReadVar(GroupName, VarName, VarStarts, VarCounts, NcFillValue, FileData);
639 ReplaceFillWithMissing<double>(FileData, NcFillValue);
641 std::vector<float> FrameData(FileData.size(), 0.0);
642 ConvertVarType<double, float>(FileData, FrameData);
645 std::string ErrorMsg =
646 "NetcdfIO::ReadFrame: Conflicting data types for conversion to float: ";
647 ErrorMsg +=
"File variable: " +
file_name(ivar) +
", File type: " + FileType;
650 }
else if (VarType ==
"string") {
652 std::vector<std::size_t> VarFileShape =
file_shape(ivar);
653 for (std::size_t i = 1; i < VarFileShape.size(); ++i) {
654 VarStarts.push_back(0);
655 VarCounts.push_back(VarFileShape[i]);
657 std::vector<std::string> FileData;
659 NcReadVar(GroupName, VarName, VarStarts, VarCounts, NcFillValue, FileData);
677 std::vector<std::size_t> Starts(1, FrameStart);
678 std::vector<std::size_t> Counts;
680 std::string GroupName;
682 std::vector<std::size_t> VarShape;
691 Counts.assign(1, FrameData.size());
692 NcWriteVar(GroupName, VarName, Starts, Counts, FrameData);
700 Counts.assign(1, FrameData.size());
701 NcWriteVar(GroupName, VarName, Starts, Counts, FrameData);
709 std::vector<std::size_t> FileShape =
file_shape(GroupName, VarName);
710 std::vector<std::size_t> CharStarts{ Starts[0], 0 };
711 std::vector<std::size_t> CharCounts{ FrameData.size(), FileShape[1] };
712 NcWriteVar(GroupName, VarName, CharStarts, CharCounts, FrameData);
723 const std::string & VarType,
const std::vector<std::size_t> & VarShape,
724 const std::string & FileVarName,
const std::string & FileType,
725 const std::size_t MaxStringSize) {
727 std::string ErrorMsg;
731 std::vector<std::size_t> FileShape = VarShape;
732 if (VarType ==
"string") {
733 FileShape.push_back(MaxStringSize);
737 ErrorMsg =
"NetcdfIO::GrpVarInsert: Unable to get netcdf id for variable: " + FileVarName;
749 nc_type NcVarType = NC_NAT;
750 if (VarType ==
"int") {
752 }
else if (VarType ==
"float") {
753 NcVarType = NC_FLOAT;
754 }
else if (VarType ==
"string") {
757 ErrorMsg =
"NetcdfIO::GrpVarInsert: Unrecognized variable type: " + VarType +
758 ", must use one of: int, float, string";
762 std::vector<int> NcDimIds =
GetNcDimIds(GroupName, FileShape);
763 if (VarType ==
"string") {
766 ErrorMsg =
"NetcdfIO::WriteVar: Unable to create variable dataset: " + FileVarName;
767 CheckNcCall(nc_def_var(
ncid_, FileVarName.c_str(), NcVarType, NcDimIds.size(),
768 NcDimIds.data(), &NcVarId), ErrorMsg);
789 os <<
"Netcdf: In " << __FILE__ <<
" @ " << __LINE__ << std::endl;
807 if (RetCode != NC_NOERR) {
808 oops::Log::error() << ErrorMsg <<
" [NetCDF message: '"
809 << nc_strerror(RetCode) <<
"']" << std::endl;
829 return (nc_inq_att(
ncid_, AttrOwnerId, AttrName.c_str() , &AttrType, &AttrLen) == NC_NOERR);
845 std::string NcVarName = VarName +
"@" + GroupName;
872 std::string DimName =
"nstring" + std::to_string(DimSize);
882 std::string ErrorMsg =
"NetcdfIO::NetcdfIO: Unable to create dimension: " + DimName;
911 const std::vector<std::size_t> & Starts,
912 const std::vector<std::size_t> & Counts,
913 std::vector<std::string> & VarData) {
916 int NcVarId =
var_id(GroupName, VarName);
920 std::string ErrorMsg;
921 ErrorMsg =
"NetcdfIO::ReadDateTime: Unable to read attribute: date_time";
922 CheckNcCall(nc_get_att_int(
ncid_, NC_GLOBAL,
"date_time", &RefDateAttr), ErrorMsg);
924 int Year = RefDateAttr / 1000000;
925 int TempInt = RefDateAttr % 1000000;
926 int Month = TempInt / 10000;
927 TempInt = TempInt % 10000;
928 int Day = TempInt / 100;
929 int Hour = TempInt % 100;
930 util::DateTime RefDate(Year, Month, Day, Hour, 0, 0);
933 std::vector<float> OffsetTime(Counts[0], 0.0);
934 ErrorMsg =
"NetcdfIO::ReadDateTime: Unable to read variable: time@" + GroupName;
936 OffsetTime.data()), ErrorMsg);
940 util::DateTime ObsDateTime;
941 VarData.assign(Counts[0],
"");
942 for (std::size_t i = 0; i < Counts[0]; ++i) {
943 ObsDateTime = RefDate + util::Duration(round(OffsetTime[i] * 3600));
944 VarData[i] = ObsDateTime.toString();