#pragma once #include "MString.h" #include "traits.h" #include #include #include #include using michlib::MString; class NCFileWBase { template struct NCTypeD { static constexpr nc_type nc = NC_NAT; }; template struct NCTypeD { static constexpr nc_type nc = NC_BYTE; }; template struct NCTypeD { static constexpr nc_type nc = NC_UBYTE; static int put_var(int nc, int vid, const michlib::uint1* data) { return nc_put_var_ubyte(nc, vid, data); } static int put_vara(int nc, int vid, const size_t* startp, const size_t* countp, const michlib::uint1* data) { return nc_put_vara_ubyte(nc, vid, startp, countp, data); } }; template struct NCTypeD { static constexpr nc_type nc = NC_SHORT; static int put_var(int nc, int vid, const michlib::int2* data) { return nc_put_var_short(nc, vid, data); } static int put_vara(int nc, int vid, const size_t* startp, const size_t* countp, const michlib::int2* data) { return nc_put_vara_short(nc, vid, startp, countp, data); } }; template struct NCTypeD { static constexpr nc_type nc = NC_USHORT; static int put_var(int nc, int vid, const michlib::uint2* data) { return nc_put_var_ushort(nc, vid, data); } static int put_vara(int nc, int vid, const size_t* startp, const size_t* countp, const michlib::uint2* data) { return nc_put_vara_ushort(nc, vid, startp, countp, data); } }; template struct NCTypeD { static constexpr nc_type nc = NC_INT; static int put_var(int nc, int vid, const michlib::int4* data) { return nc_put_var_int(nc, vid, data); } static int put_vara(int nc, int vid, const size_t* startp, const size_t* countp, const michlib::int4* data) { return nc_put_vara_int(nc, vid, startp, countp, data); } }; template struct NCTypeD { static constexpr nc_type nc = NC_UINT; static int put_var(int nc, int vid, const michlib::uint4* data) { return nc_put_var_uint(nc, vid, data); } static int put_vara(int nc, int vid, const size_t* startp, const size_t* countp, const michlib::uint4* data) { return nc_put_vara_uint(nc, vid, startp, countp, data); } }; template struct NCTypeD { static constexpr nc_type nc = NC_INT64; }; template struct NCTypeD { static constexpr nc_type nc = NC_UINT64; }; template struct NCTypeD { static constexpr nc_type nc = NC_FLOAT; static int put_var(int nc, int vid, const float* data) { return nc_put_var_float(nc, vid, data); } static int put_vara(int nc, int vid, const size_t* startp, const size_t* countp, const float* data) { return nc_put_vara_float(nc, vid, startp, countp, data); } }; template struct NCTypeD { static constexpr nc_type nc = NC_DOUBLE; static int put_var(int nc, int vid, const double* data) { return nc_put_var_double(nc, vid, data); } static int put_vara(int nc, int vid, const size_t* startp, const size_t* countp, const double* data) { return nc_put_vara_double(nc, vid, startp, countp, data); } }; template struct NCTypeD { static constexpr nc_type nc = NC_CHAR; }; template struct NCTypeD { static constexpr nc_type nc = NC_CHAR; }; template using NCType = NCTypeD; public: template static constexpr nc_type Type2NCType = NCType::nc; // Error class class Error { int err; public: constexpr Error(): err(NC_NOERR) {} constexpr Error(int n_err): err(n_err) {} explicit operator bool() const { return err == NC_NOERR; } const char* ErrMessage() const { return nc_strerror(err); } int Err() const { return err; } bool IsErr() const { return err != NC_NOERR; } void Reset() { err = NC_NOERR; } void Reset(int n_err) { err = n_err; } void Reset(const Error& e) { err = e.err; } }; ~NCFileWBase() { Close(); } int Id() const { return ncid; } int Err() const { return err.Err(); } const char* ErrMessage() const { return err.ErrMessage(); } explicit operator bool() const { return Ok(); } bool Ok() const { return opened && !err.IsErr(); } operator int() const { return ncid; } void Close() { if(opened) nc_close(ncid); err.Reset(); opened = false; } void Open(const MString& path) { if(opened) return; err.Reset(nc_create(path.Buf(), NC_CLOBBER | NC_NETCDF4, &ncid)); opened = !err.IsErr(); } void AddDim(const MString& name, size_t len) { if(err.IsErr()) return; static int dimid; err.Reset(nc_def_dim(ncid, name.Buf(), len, &dimid)); } MString AddAtts(const michlib_internal::ParameterListEx& pars) { for(const auto& p: pars.GetParameterList()) { MString ret; if(p.first.Prefix() != "") continue; // Only parameters with empty prefix considered for now switch(p.second.valtype) { case michlib_internal::Parameter::UINT1: { ret = AddAtt(p.first.Name(), p.second.value.u1); break; } case michlib_internal::Parameter::UINT2: { ret = AddAtt(p.first.Name(), p.second.value.u2); break; } case michlib_internal::Parameter::UINT4: { ret = AddAtt(p.first.Name(), p.second.value.u4); break; } case michlib_internal::Parameter::UINT8: { ret = AddAtt(p.first.Name(), p.second.value.u8); break; } case michlib_internal::Parameter::INT1: { ret = AddAtt(p.first.Name(), p.second.value.i1); break; } case michlib_internal::Parameter::INT2: { ret = AddAtt(p.first.Name(), p.second.value.i2); break; } case michlib_internal::Parameter::INT4: { ret = AddAtt(p.first.Name(), p.second.value.i4); break; } case michlib_internal::Parameter::INT8: { ret = AddAtt(p.first.Name(), p.second.value.i8); break; } case michlib_internal::Parameter::FLOAT: { ret = AddAtt(p.first.Name(), p.second.value.r4); break; } case michlib_internal::Parameter::DOUBLE: { ret = AddAtt(p.first.Name(), p.second.value.r8); break; } case michlib_internal::Parameter::LDOUBLE: { ret = AddAtt(p.first.Name(), static_cast(p.second.value.r10)); break; } case michlib_internal::Parameter::BOOL: { ret = AddAtt(p.first.Name(), p.second.value.b ? 1 : 0); break; } case michlib_internal::Parameter::STRING: { ret = AddAtt(p.first.Name(), p.second.svalue); break; } case michlib_internal::Parameter::INVALID: break; // Silently skip } if(ret.Exist()) return ret; } return ""; } template requires(Type2NCType != NC_NAT) MString AddAtt(const MString& name, const T& val) { if(err.IsErr()) return "Can't write attribute " + name + " due to previous errors"; static constexpr auto nct = Type2NCType; static constexpr bool ischar = std::is_same_v; if constexpr(nct != NC_CHAR) err.Reset(nc_put_att(ncid, NC_GLOBAL, name.Buf(), nct, 1, &val)); else if constexpr(ischar) err.Reset(nc_put_att_text(ncid, NC_GLOBAL, name.Buf(), strlen(val) + 1, val)); else err.Reset(nc_put_att_text(ncid, NC_GLOBAL, name.Buf(), val.Len() + 1, val.Buf())); return err.IsErr() ? "Can't write attribute " + name + ": " + ErrMessage() : ""; } template requires(Type2NCType != NC_NAT) MString AddAtt(const MString& vname, const MString& name, const T& val) { if(err.IsErr()) return "Can't write attribute " + name + " to the variable " + vname + " due to previous errors"; static constexpr auto nct = Type2NCType; static constexpr bool ischar = std::is_same_v; int varid; err.Reset(nc_inq_varid(ncid, vname.Buf(), &varid)); if(err.IsErr()) return "Can't find variable " + vname + ": " + ErrMessage(); if constexpr(nct != NC_CHAR) err.Reset(nc_put_att(ncid, varid, name.Buf(), nct, 1, &val)); else if constexpr(ischar) err.Reset(nc_put_att_text(ncid, varid, name.Buf(), strlen(val) + 1, val)); else err.Reset(nc_put_att_text(ncid, varid, name.Buf(), val.Len() + 1, val.Buf())); return err.IsErr() ? "Can't write attribute " + name + " to the variable " + vname + ": " + ErrMessage() : ""; } MString AddAtt(const MString& vname, const MString& name, const char* val) { if(err.IsErr()) return "Can't write attribute " + name + " to the variable " + vname + " due to previous errors"; int varid; err.Reset(nc_inq_varid(ncid, vname.Buf(), &varid)); if(err.IsErr()) return "Can't find variable " + vname + ": " + ErrMessage(); err.Reset(nc_put_att_text(ncid, varid, name.Buf(), strlen(val) + 1, val)); return err.IsErr() ? "Can't write attribute " + name + " to the variable " + vname + ": " + ErrMessage() : ""; } template void AddVar(const MString& vname, nc_type type, const T&... dnames) { AddVar(vname, type, std::vector({dnames...})); } void EndDef() const { nc_enddef(ncid); } void SetComp(const MString& vname, int comp) { if(err.IsErr()) return; int varid; err.Reset(nc_inq_varid(ncid, vname.Buf(), &varid)); if(err.IsErr()) return; err.Reset(nc_def_var_deflate(ncid, varid, 1, 1, comp)); } template requires requires(int nc, int vid, const T* d) { { NCTypeD::put_var(nc, vid, d) } -> std::same_as; } void WriteVar(const MString& vname, const T* data) { if(err.IsErr()) return; int varid; err.Reset(nc_inq_varid(ncid, vname.Buf(), &varid)); if(err.IsErr()) return; err.Reset(NCTypeD::put_var(ncid, varid, data)); } template requires requires(int nc, int vid, const size_t* start, const size_t* count, const T* d) { { NCTypeD::put_vara(nc, vid, start, count, d) } -> std::same_as; } void WriteVar(const MString& vname, size_t ind, const T* data) { if(err.IsErr()) return; int varid; err.Reset(nc_inq_varid(ncid, vname.Buf(), &varid)); if(err.IsErr()) return; int ndim; err.Reset(nc_inq_var(ncid, varid, nullptr, nullptr, &ndim, nullptr, nullptr)); if(err.IsErr()) return; std::vector dimids(ndim); err.Reset(nc_inq_var(ncid, varid, nullptr, nullptr, nullptr, dimids.data(), nullptr)); if(err.IsErr()) return; std::vector start(ndim), count(ndim); start[0] = ind; count[0] = 1; for(size_t i = 1; i < dimids.size(); i++) { start[i] = 0; err.Reset(nc_inq_dim(ncid, dimids[i], nullptr, &count[i])); if(err.IsErr()) return; } err.Reset(NCTypeD::put_vara(ncid, varid, start.data(), count.data(), data)); } private: // Members int ncid; Error err = NC_NOERR; bool opened = false; void AddVar(const MString& vname, nc_type type, std::vector&& dnames) { if(err.IsErr()) return; std::vector dimids(dnames.size()); int varid; for(size_t i = 0; i < dnames.size(); i++) { err.Reset(nc_inq_dimid(ncid, dnames[i].Buf(), &dimids[i])); if(err.IsErr()) return; } err.Reset(nc_def_var(ncid, vname.Buf(), type, dimids.size(), dimids.data(), &varid)); } }; class NCFileW: public NCFileWBase { enum Type { UNKNOWN, PSET, GPSET, GRID, GGRID, RGRID, GRGRID }; static constexpr auto fill = std::numeric_limits::max(); Type type = UNKNOWN; int compress; template static constexpr Type DetType() { if constexpr(ReadIs2DGeoRectArray) return GRGRID; if constexpr(ReadIs2DGeoArray) return GGRID; if constexpr(ReadIs1DGeoArray) return GPSET; if constexpr(ReadIs2DXYRectArray) return RGRID; if constexpr(ReadIs2DXYArray) return GRID; if constexpr(ReadIs1DArray) return PSET; return UNKNOWN; }; template static constexpr bool Is1DType() { return Is1DType(DetType()); } static constexpr bool IsGeoType(Type t) { return t == GPSET || t == GGRID || t == GRGRID; } static constexpr bool Is1DType(Type t) { return t == PSET || t == GPSET; } MString CreateFile(Type stype, const MString& name, int compression, size_t nx, size_t ny); public: static constexpr auto Fill() { return fill; } template MString Create(const D& data, const MString& name, int compression) { if(type != UNKNOWN) return "File already created"; if constexpr(Is1DType()) return CreateFile(DetType(), name, compression, data.N(), 0); else return CreateFile(DetType(), name, compression, data.Nx(), data.Ny()); } void Close() { NCFileWBase::Close(); type = UNKNOWN; } MString AddVariable(const MString& name, const MString& stname, const MString& lname, const MString& units, const MString& comment); template MString WriteVariable(const D& data, const MString& name, Op op) { static constexpr auto dtype = DetType(); if(type == UNKNOWN) return "File not open"; if(type != dtype) return "Incompatible data type"; if constexpr(dtype == UNKNOWN) return "Unknown data type"; EndDef(); if constexpr(Is1DType(dtype)) { const size_t c = data.N(); float buf[c]; for(size_t ix = 0; ix < c; ix++) buf[ix] = data.IsFill(ix) ? fill : op(ix); WriteVar(name, buf); } else { const size_t c[2] = {data.Ny(), data.Nx()}; float buf[c[0] * c[1]]; for(size_t iy = 0; iy < c[0]; iy++) for(size_t ix = 0; ix < c[1]; ix++) buf[iy * c[1] + ix] = data.IsFill(ix, iy) ? fill : op(ix, iy); WriteVar(name, buf); } if(!*this) return "Can't write variable " + name + ": " + ErrMessage(); return ""; } template MString WriteVariable(const D& data, const MString& name) { if constexpr(Is1DType(DetType())) return WriteVariable(data, name, [&data = std::as_const(data)](size_t i) { return data(i); }); else return WriteVariable(data, name, [&data = std::as_const(data)](size_t i, size_t j) { return data(i, j); }); } template MString WriteGrid(const D& data) { static constexpr auto dtype = DetType(); if(type == UNKNOWN) return "File not open"; if(type != dtype) return "Incompatible data type"; EndDef(); if constexpr(dtype == UNKNOWN) return "Unknown data type"; else if constexpr(dtype == PSET) { const size_t c = data.N(); float bufx[c]; float bufy[c]; for(size_t ix = 0; ix < c; ix++) { bufx[ix] = data.X(ix); bufy[ix] = data.Y(ix); } WriteVar("x", bufx); WriteVar("y", bufy); } else if constexpr(dtype == GPSET) { const size_t c = data.N(); float bufx[c]; float bufy[c]; for(size_t ix = 0; ix < c; ix++) { bufx[ix] = data.Lon(ix); bufy[ix] = data.Lat(ix); } WriteVar("longitude", bufx); WriteVar("latitude", bufy); } else if constexpr(dtype == RGRID) { const size_t cx = data.Nx(), cy = data.Ny(); float bufx[cx]; float bufy[cy]; for(size_t ix = 0; ix < cx; ix++) bufx[ix] = data.Ix2X(ix); for(size_t iy = 0; iy < cy; iy++) bufy[iy] = data.Iy2Y(iy); WriteVar("x", bufx); WriteVar("y", bufy); } else if constexpr(dtype == GRGRID) { const size_t cx = data.Nx(), cy = data.Ny(); float bufx[cx]; float bufy[cy]; for(size_t ix = 0; ix < cx; ix++) bufx[ix] = data.Ix2Lon(ix); for(size_t iy = 0; iy < cy; iy++) bufy[iy] = data.Iy2Lat(iy); WriteVar("longitude", bufx); WriteVar("latitude", bufy); } else if constexpr(dtype == GRID) { const size_t c[2] = {data.Ny(), data.Nx()}; float bufx[c[0] * c[1]]; float bufy[c[0] * c[1]]; for(size_t iy = 0; iy < c[0]; iy++) for(size_t ix = 0; ix < c[1]; ix++) { bufx[iy * c[1] + ix] = data.X(ix, iy); bufy[iy * c[1] + ix] = data.Y(ix, iy); } WriteVar("x", bufx); WriteVar("y", bufy); } else if constexpr(dtype == GGRID) { const size_t c[2] = {data.Ny(), data.Nx()}; float bufx[c[0] * c[1]]; float bufy[c[0] * c[1]]; for(size_t iy = 0; iy < c[0]; iy++) for(size_t ix = 0; ix < c[1]; ix++) { bufx[iy * c[1] + ix] = data.Lon(ix, iy); bufy[iy * c[1] + ix] = data.Lat(ix, iy); } WriteVar("longitude", bufx); WriteVar("latitude", bufy); } else return "Unknown data type"; if(!*this) return MString("Can't write grid: ") + ErrMessage(); return ""; } };