From cccd67aab4a84ec77dcc8a8bbbabcf81169780e0 Mon Sep 17 00:00:00 2001 From: Michael Uleysky Date: Tue, 3 Oct 2023 13:40:51 +1000 Subject: [PATCH] Added an interface class for writing netcdf files --- actions/actiongrad.cpp | 143 +++------------- actions/actiongrad.h | 19 +-- actions/actionuv.cpp | 87 ++++------ include/ncfilew.h | 378 ++++++++++++++++++++++++++++++++--------- src/ncfilew.cpp | 145 ++++++++-------- 5 files changed, 424 insertions(+), 348 deletions(-) diff --git a/actions/actiongrad.cpp b/actions/actiongrad.cpp index 1b69be0..6e62497 100644 --- a/actions/actiongrad.cpp +++ b/actions/actiongrad.cpp @@ -4,146 +4,57 @@ MString GradMethods::NCFileW::Create(const MString& name, const MString& history, const std::vector& vnames, const std::vector& lnames, const std::vector& lons, const std::vector& lats, int compress) { - int ret; - const float node_offset = 0.0; auto nx = lons.size(); auto ny = lats.size(); - int xid, yid; - - MString text; - - struct - { - union - { - struct - { - int ydimid, xdimid; - }; - int dimid[2]; - }; - } dim; - - struct Keeper - { - bool active; - int ncid; - ~Keeper() - { - if(active) nc_close(ncid); - } - operator int() { return ncid; } - } newnc; - const auto fill = static_cast(std::numeric_limits::max()); // Creating - ret = nc_create(name.Buf(), NC_CLOBBER | NC_NETCDF4, &newnc.ncid); - if(ret != NC_NOERR) return "Can't create netcdf file: " + name; - newnc.active = true; - - // Global attributes - ret = nc_put_att_text(newnc, NC_GLOBAL, "history", history.Len() + 1, history.Buf()); - if(ret != NC_NOERR) return "Can't write history attribute in the netcdf file"; - - ret = nc_put_att(newnc, NC_GLOBAL, "node_offset", NC_FLOAT, 1, &node_offset); - if(ret != NC_NOERR) return "Can't write history attribute in the netcdf file"; - - // Dimensions - ret = nc_def_dim(newnc, "longitude", nx, &dim.xdimid); - if(ret != NC_NOERR) return "Can't create x-dimension in the netcdf file"; - ret = nc_def_dim(newnc, "latitude", ny, &dim.ydimid); - if(ret != NC_NOERR) return "Can't create y-dimension in the netcdf file"; - - // lon, lat variables - ret = nc_def_var(newnc, "longitude", NC_FLOAT, 1, &dim.xdimid, &xid); - if(ret != NC_NOERR) return "Can't create longitude variable in the netcdf file"; - ret = nc_def_var(newnc, "latitude", NC_FLOAT, 1, &dim.ydimid, &yid); - if(ret != NC_NOERR) return "Can't create latitude variable in the netcdf file"; - - ret = nc_def_var_deflate(newnc, xid, 1, 1, compress); - if(ret != NC_NOERR) return "Can't set deflate parameters for longitude variable in the netcdf file"; - ret = nc_def_var_deflate(newnc, yid, 1, 1, compress); - if(ret != NC_NOERR) return "Can't set deflate parameters for latitude variable in the netcdf file"; - - text = "longitude"; - ret = nc_put_att_text(newnc, xid, "standard_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write standard_name attribute of longitude variable in the netcdf file"; - text = "latitude"; - ret = nc_put_att_text(newnc, yid, "standard_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write standard_name attribute of latitude variable in the netcdf file"; - - text = "Longitude"; - ret = nc_put_att_text(newnc, xid, "long_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of longitude variable in the netcdf file"; - text = "Latitude"; - ret = nc_put_att_text(newnc, yid, "long_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of latitude variable in the netcdf file"; + Open(name); + if(!*this) return "Can't create netcdf file " + name + ": " + ErrMessage(); + + AddAtt("history", history); + AddAtt("node_offset", node_offset); + + AddDim("longitude", nx); + AddDim("latitude", ny); + AddVar("longitude", NC_FLOAT, "longitude"); + AddVar("latitude", NC_FLOAT, "latitude"); + SetComp("longitude", compress); + SetComp("latitude", compress); + AddAtt("longitude", "standard_name", "longitude"); + AddAtt("longitude", "long_name", "Longitude"); + AddAtt("latitude", "standard_name", "latitude"); + AddAtt("latitude", "long_name", "Latitude"); // Variables for(size_t i = 0; i < vnames.size(); i++) { - int vid; - ret = nc_def_var(newnc, vnames[i].Buf(), NC_FLOAT, 2, dim.dimid, &vid); - if(ret != NC_NOERR) return "Can't create " + vnames[i] + " variable in the netcdf file"; - - ret = nc_def_var_deflate(newnc, vid, 1, 1, compress); - if(ret != NC_NOERR) return "Can't set deflate parameters for " + vnames[i] + " variable in the netcdf file"; - - if(lnames[i].Exist()) ret = nc_put_att_text(newnc, vid, "long_name", lnames[i].Len() + 1, lnames[i].Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of " + vnames[i] + " variable in the netcdf file"; - - ret = nc_put_att_float(newnc, vid, "_FillValue", NC_FLOAT, 1, &fill); - if(ret != NC_NOERR) return "Can't write _FillValue attribute of " + vnames[i] + " variable in the netcdf file"; + AddVar(vnames[i], NC_FLOAT, "latitude", "longitude"); + SetComp(vnames[i], compress); + if(lnames[i].Exist()) AddAtt(vnames[i], "long_name", lnames[i]); + AddAtt(vnames[i], "_FillValue", fill); } // End definitions - nc_enddef(newnc); + EndDef(); // Writing lon, lat - const size_t i = 0; + WriteVar("longitude", lons.data()); + WriteVar("latitude", lats.data()); - ret = nc_put_vara_float(newnc, xid, &i, &nx, lons.data()); - if(ret != NC_NOERR) return "Can't write longitude variable in the netcdf file"; - ret = nc_put_vara_float(newnc, yid, &i, &ny, lats.data()); - if(ret != NC_NOERR) return "Can't write latitude variable in the netcdf file"; + if(!*this) return "Can't set grid in the netcdf file " + name + ": " + ErrMessage(); - ncid = newnc; - newnc.active = false; return ""; } -MString GradMethods::NCFileW::WriteVar(const MString& name, const std::vector& data) +MString GradMethods::NCFileW::WriteVariable(const MString& name, const GradMethods::Matrix& data) { - int ret; - int vid; - int xid, yid; - size_t nx, ny; - - ret = nc_inq_varid(ncid, name.Buf(), &vid); - if(ret != NC_NOERR) return "Can't find variable " + name + " in the netcdf file"; - - ret = nc_inq_dimid(ncid, "longitude", &xid); - if(ret != NC_NOERR) return "Can't find longitude dimension in the netcdf file"; - ret = nc_inq_dimid(ncid, "latitude", &yid); - if(ret != NC_NOERR) return "Can't find latitude dimension in the netcdf file"; - - ret = nc_inq_dimlen(ncid, xid, &nx); - if(ret != NC_NOERR) return "Can't find longitude size in the netcdf file"; - ret = nc_inq_dimlen(ncid, yid, &ny); - if(ret != NC_NOERR) return "Can't find latitude size in the netcdf file"; - - const size_t i[2] = {0, 0}; - const size_t c[2] = {ny, nx}; - - GradMethods::DataType buf[nx * ny]; - for(size_t i = 0; i < nx * ny; i++) buf[i] = static_cast(data[i]); + WriteVar(name, data.Data().data()); + if(!*this) return "Can't write variable " + name + ": " + ErrMessage(); - ret = nc_put_vara_float(ncid, vid, i, c, buf); - if(ret != NC_NOERR) return "Can't write " + name + " variable in the netcdf file"; return ""; } diff --git a/actions/actiongrad.h b/actions/actiongrad.h index 64edc89..3651d2b 100644 --- a/actions/actiongrad.h +++ b/actions/actiongrad.h @@ -45,25 +45,12 @@ class GradMethods::Matrix const auto& Data() const { return data; } }; -class GradMethods::NCFileW +class GradMethods::NCFileW: public NCFileWBase { - bool opened; - int ncid; - public: - NCFileW(): opened(false) {} - MString Create(const MString& name, const MString& history, const std::vector& vnames, const std::vector& lnames, const std::vector& lons, const std::vector& lats, int compress); - MString WriteVar(const MString& name, const std::vector& data); - - ~NCFileW() { Close(); } - - void Close() - { - if(opened) nc_close(ncid); - opened = false; - } + MString WriteVariable(const MString& name, const GradMethods::Matrix& data); }; template @@ -139,7 +126,7 @@ template MString ActionGRAD::DoAction(const CLArgs& args, D& ds) { const MString& name = ds.VarNames()[i]; data[i].Grad(); - fw.WriteVar(name, data[i].Data()); + fw.WriteVariable(name, data[i]); } return ""; diff --git a/actions/actionuv.cpp b/actions/actionuv.cpp index 69ba2c0..d07e3fe 100644 --- a/actions/actionuv.cpp +++ b/actions/actionuv.cpp @@ -21,75 +21,48 @@ void UVMethods::StPoints::WriteBinBile(const MString& name) const MString UVMethods::StPoints::WriteNcFile(const MString& name, const MString& history, int comp) const { - int ret; - int ncid; - int dimid; - int xid, yid, tid; - MString text; + NCFileWBase nc; - ret = nc_create(name.Buf(), NC_CLOBBER | NC_NETCDF4, &ncid); - if(ret != NC_NOERR) return "Can't create netcdf file: " + name; + const MString xname = lonlat ? "longitude" : "x"; + const MString yname = lonlat ? "latitude" : "y"; - ret = nc_put_att_text(ncid, NC_GLOBAL, "history", history.Len() + 1, history.Buf()); - if(ret != NC_NOERR) return "Can't write history attribute in the netcdf file"; + nc.Open(name); + if(!nc) return "Can't create netcdf file " + name + ": " + nc.ErrMessage(); - ret = nc_def_dim(ncid, "i", N(), &dimid); - if(ret != NC_NOERR) return "Can't create dimension in the netcdf file"; - - ret = nc_def_var(ncid, lonlat ? "longitude" : "x", NC_FLOAT, 1, &dimid, &xid); - if(ret != NC_NOERR) return "Can't create x variable in the netcdf file"; - ret = nc_def_var(ncid, lonlat ? "latitude" : "y", NC_FLOAT, 1, &dimid, &yid); - if(ret != NC_NOERR) return "Can't create y variable in the netcdf file"; + nc.AddAtt("history", history); + nc.AddDim("i", N()); + nc.AddVar(xname, NC_FLOAT, "i"); + nc.AddVar(yname, NC_FLOAT, "i"); + nc.SetComp(xname, comp); + nc.SetComp(yname, comp); if(lonlat) { - text = "longitude"; - ret = nc_put_att_text(ncid, xid, "standard_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write standard_name attribute of longitude variable in the netcdf file"; - text = "latitude"; - ret = nc_put_att_text(ncid, yid, "standard_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write standard_name attribute of latitude variable in the netcdf file"; - - text = "Longitude"; - ret = nc_put_att_text(ncid, xid, "long_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of longitude variable in the netcdf file"; - text = "Latitude"; - ret = nc_put_att_text(ncid, yid, "long_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of latitude variable in the netcdf file"; + nc.AddAtt(xname, "standard_name", "longitude"); + nc.AddAtt(xname, "long_name", "Longitude"); + nc.AddAtt(yname, "standard_name", "latitude"); + nc.AddAtt(yname, "long_name", "Latitude"); + } + else + { + nc.AddAtt(xname, "long_name", "x-coordinate"); + nc.AddAtt(yname, "long_name", "y-coordinate"); } - ret = nc_def_var_deflate(ncid, xid, 1, 1, comp); - if(ret != NC_NOERR) return "Can't set deflate parameters for x variable in the netcdf file"; - ret = nc_def_var_deflate(ncid, yid, 1, 1, comp); - if(ret != NC_NOERR) return "Can't set deflate parameters for y variable in the netcdf file"; - - ret = nc_def_var(ncid, "type", NC_UBYTE, 1, &dimid, &tid); - if(ret != NC_NOERR) return "Can't create type variable in the netcdf file"; - ret = nc_def_var_deflate(ncid, tid, 1, 1, comp); - if(ret != NC_NOERR) return "Can't set deflate parameters for type variable in the netcdf file"; - text = "Stationary point type, 0 - saddle, 1 - st. anticicl. focus, 2 - st. knot, 3 - unst. anticicl. focus, 4 - unst. knot, 5 - st. cicl. focus, 6 - unst. cicl. focus"; - ret = nc_put_att_text(ncid, tid, "long_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of type variable in the netcdf file"; - - ret = nc_enddef(ncid); - if(ret != NC_NOERR) return "Can't finish definition of the netcdf file"; - - const size_t i = 0, c = N(); - float buf[c]; + nc.AddVar("type", NC_UBYTE, "i"); + nc.SetComp("type", comp); + nc.AddAtt("type", "long_name", + "Stationary point type, 0 - saddle, 1 - st. anticicl. focus, 2 - st. knot, 3 - unst. anticicl. focus, 4 - unst. knot, 5 - st. cicl. focus, 6 - unst. cicl. focus"); - for(size_t ix = 0; ix < c; ix++) buf[ix] = x[ix]; - ret = nc_put_vara_float(ncid, xid, &i, &c, buf); - if(ret != NC_NOERR) return "Can't write x variable in the netcdf file"; + if(!nc) return "Can't set grid in the netcdf file " + name + ": " + nc.ErrMessage(); - for(size_t ix = 0; ix < c; ix++) buf[ix] = y[ix]; - ret = nc_put_vara_float(ncid, yid, &i, &c, buf); - if(ret != NC_NOERR) return "Can't write y variable in the netcdf file"; + nc.EndDef(); - ret = nc_put_vara_ubyte(ncid, tid, &i, &c, t.data()); - if(ret != NC_NOERR) return "Can't write type variable in the netcdf file"; + nc.WriteVar(xname, x.data()); + nc.WriteVar(yname, y.data()); + nc.WriteVar("type", t.data()); - ret = nc_close(ncid); - if(ret != NC_NOERR) return "Can't close netcdf file"; + if(!nc) return "Can't write data to the netcdf file " + name + ": " + nc.ErrMessage(); return ""; } diff --git a/include/ncfilew.h b/include/ncfilew.h index 1cfb1a6..8974508 100644 --- a/include/ncfilew.h +++ b/include/ncfilew.h @@ -8,56 +8,260 @@ using michlib::MString; -class NCFileW +class NCFileWBase { - enum Type + template struct NCTypeD { - UNKNOWN, - G1V1, - G1V2, - G2V2 + 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); } + }; + 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); } + }; + 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); } + }; + 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); } + }; + 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); } + }; + 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); } + }; + 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); } + }; + template struct NCTypeD + { + static constexpr nc_type nc = NC_CHAR; }; - struct Var + template struct NCTypeD { - MString name; - int id; + static constexpr nc_type nc = NC_CHAR; }; - static constexpr auto fill = std::numeric_limits::max(); + template using NCType = NCTypeD; - int ncid; - Type type = UNKNOWN; - union + public: + template static constexpr nc_type Type2NCType = NCType::nc; + + // Error class + class Error { - struct + 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)); + } + + template + requires(Type2NCType != NC_NAT) + void AddAtt(const MString& name, const T& val) + { + if(err.IsErr()) return; + 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())); + } + + template + requires(Type2NCType != NC_NAT) + void AddAtt(const MString& vname, const MString& name, const T& val) + { + if(err.IsErr()) return; + 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; + + 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())); + } + + void AddAtt(const MString& vname, const MString& name, const char* val) + { + if(err.IsErr()) return; + int varid; + + err.Reset(nc_inq_varid(ncid, vname.Buf(), &varid)); + if(err.IsErr()) return; + err.Reset(nc_put_att_text(ncid, varid, name.Buf(), strlen(val) + 1, val)); + } + + 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) { { - int ydimid, xdimid; - }; - int dimid[2]; + 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)); + } + + 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 }; - int xid, yid; - int compress; - std::vector vars; + + static constexpr auto fill = std::numeric_limits::max(); + + Type type = UNKNOWN; + int compress; template static constexpr Type DetType() { - if constexpr(ReadIs2DGeoRectArray) return G1V2; - if constexpr(ReadIs2DGeoArray) return G2V2; - if constexpr(ReadIs1DGeoArray) return G1V1; + 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 Is1DType(Type t) { return t == G1V1; } + 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, const MString& history, int compression, size_t nx, size_t ny); public: static constexpr auto Fill() { return fill; } - ~NCFileW() { Close(); } - template MString Create(const D& data, const MString& name, const MString& history, int compression) { if(type != UNKNOWN) return "File already created"; @@ -67,78 +271,53 @@ class NCFileW return CreateFile(DetType(), name, history, compression, data.Nx(), data.Ny()); } - size_t VarId(const MString& name) const - { - for(size_t i = 0; i < vars.size(); i++) - if(vars[i].name == name) return i + 1; - - return 0; - } - - bool HaveVar(const MString& name) const { return VarId(name) != 0; } - - void EndDef() const - { - if(type != UNKNOWN) nc_enddef(ncid); - } void Close() { - if(type != UNKNOWN) nc_close(ncid); + 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, size_t varid, Op op) const + 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(varid == 0) return "Incorrect variable"; if constexpr(dtype == UNKNOWN) return "Unknown data type"; - size_t v = varid - 1; - EndDef(); - int ret; if constexpr(Is1DType(dtype)) { - const size_t i = 0, c = data.N(); + 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); - - ret = nc_put_vara_float(ncid, vars[v].id, &i, &c, buf); - if(ret != NC_NOERR) return "Can't write " + vars[v].name + " variable in the netcdf file"; + WriteVar(name, buf); } else { - const size_t i[2] = {0, 0}; 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); - - ret = nc_put_vara_float(ncid, vars[v].id, i, c, buf); - if(ret != NC_NOERR) return "Can't write " + vars[v].name + " variable in the netcdf file"; + WriteVar(name, buf); } + + if(!*this) return "Can't write variable " + name + ": " + ErrMessage(); + return ""; } - template MString WriteVariable(const D& data, const MString& name, Op op) const { return WriteVariable(data, VarId(name), op); } - - template MString WriteVariable(const D& data, size_t varid) const + template MString WriteVariable(const D& data, const MString& name) { if constexpr(Is1DType(DetType())) - return WriteVariable(data, varid, [&data = std::as_const(data)](size_t i) { return data(i); }); + return WriteVariable(data, name, [&data = std::as_const(data)](size_t i) { return data(i); }); else - return WriteVariable(data, varid, [&data = std::as_const(data)](size_t i, size_t j) { return data(i, j); }); + return WriteVariable(data, name, [&data = std::as_const(data)](size_t i, size_t j) { return data(i, j); }); } - template MString WriteVariable(const D& data, const MString& name) const { return WriteVariable(data, VarId(name)); } - - template MString WriteGrid(const D& data) const + template MString WriteGrid(const D& data) { static constexpr auto dtype = DetType(); if(type == UNKNOWN) return "File not open"; @@ -146,10 +325,24 @@ class NCFileW EndDef(); - int ret; - if constexpr(dtype == G1V1) + if constexpr(dtype == UNKNOWN) + return "Unknown data type"; + else if constexpr(dtype == PSET) { - const size_t i = 0, c = data.N(); + 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++) @@ -157,28 +350,45 @@ class NCFileW bufx[ix] = data.Lon(ix); bufy[ix] = data.Lat(ix); } - ret = nc_put_vara_float(ncid, xid, &i, &c, bufx); - if(ret != NC_NOERR) return "Can't write longitude variable in the netcdf file"; - ret = nc_put_vara_float(ncid, yid, &i, &c, bufy); - if(ret != NC_NOERR) return "Can't write latitude variable in the netcdf file"; + WriteVar("longitude", bufx); + WriteVar("latitude", bufy); } - else if constexpr(dtype == G1V2) + else if constexpr(dtype == RGRID) { - const size_t i = 0, cx = data.Nx(), cy = data.Ny(); + 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); - - ret = nc_put_vara_float(ncid, xid, &i, &cx, bufx); - if(ret != NC_NOERR) return "Can't write longitude variable in the netcdf file"; - ret = nc_put_vara_float(ncid, yid, &i, &cy, bufy); - if(ret != NC_NOERR) return "Can't write latitude variable in the netcdf file"; + WriteVar("longitude", bufx); + WriteVar("latitude", bufy); } - else if constexpr(dtype == G2V2) + 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 i[2] = {0, 0}; const size_t c[2] = {data.Ny(), data.Nx()}; float bufx[c[0] * c[1]]; float bufy[c[0] * c[1]]; @@ -188,14 +398,14 @@ class NCFileW bufx[iy * c[1] + ix] = data.Lon(ix, iy); bufy[iy * c[1] + ix] = data.Lat(ix, iy); } - ret = nc_put_vara_float(ncid, xid, i, c, bufx); - if(ret != NC_NOERR) return "Can't write longitude variable in the netcdf file"; - ret = nc_put_vara_float(ncid, yid, i, c, bufy); - if(ret != NC_NOERR) return "Can't write latitude variable in the netcdf file"; + WriteVar("longitude", bufx); + WriteVar("latitude", bufy); } else return "Unknown data type"; + if(!*this) return MString("Can't write grid: ") + ErrMessage(); + return ""; } }; diff --git a/src/ncfilew.cpp b/src/ncfilew.cpp index 6e658f2..31fb246 100644 --- a/src/ncfilew.cpp +++ b/src/ncfilew.cpp @@ -7,77 +7,82 @@ MString NCFileW::CreateFile(NCFileW::Type stype, const MString& name, const MStr compress = compression; - int ret; - MString text; - const float node_offset = 0.0; - ret = nc_create(name.Buf(), NC_CLOBBER | NC_NETCDF4, &ncid); - if(ret != NC_NOERR) return "Can't create netcdf file: " + name; - - ret = nc_put_att_text(ncid, NC_GLOBAL, "history", history.Len() + 1, history.Buf()); - if(ret != NC_NOERR) return "Can't write history attribute in the netcdf file"; + Open(name); + if(!*this) return "Can't create netcdf file " + name + ": " + ErrMessage(); - ret = nc_put_att(ncid, NC_GLOBAL, "node_offset", NC_FLOAT, 1, &node_offset); - if(ret != NC_NOERR) return "Can't write node_offset attribute in the netcdf file"; + AddAtt("history", history); + AddAtt("node_offset", node_offset); switch(stype) { - case(G1V1): + case(PSET): + { + AddDim("i", nx); + AddVar("x", NC_FLOAT, "i"); + AddVar("y", NC_FLOAT, "i"); + break; + } + case(GPSET): + { + AddDim("i", nx); + AddVar("longitude", NC_FLOAT, "i"); + AddVar("latitude", NC_FLOAT, "i"); + break; + } + case(RGRID): + { + AddDim("x", nx); + AddDim("y", ny); + AddVar("x", NC_FLOAT, "x"); + AddVar("y", NC_FLOAT, "y"); + break; + } + case(GRGRID): { - ret = nc_def_dim(ncid, "i", nx, &xdimid); - if(ret != NC_NOERR) return "Can't create dimension in the netcdf file"; - ret = nc_def_var(ncid, "longitude", NC_FLOAT, 1, &xdimid, &xid); - if(ret != NC_NOERR) return "Can't create longitude variable in the netcdf file"; - ret = nc_def_var(ncid, "latitude", NC_FLOAT, 1, &xdimid, &yid); - if(ret != NC_NOERR) return "Can't create latitude variable in the netcdf file"; + AddDim("longitude", nx); + AddDim("latitude", ny); + AddVar("longitude", NC_FLOAT, "longitude"); + AddVar("latitude", NC_FLOAT, "latitude"); break; } - case(G1V2): + case(GRID): { - ret = nc_def_dim(ncid, "longitude", nx, &xdimid); - if(ret != NC_NOERR) return "Can't create x-dimension in the netcdf file"; - ret = nc_def_dim(ncid, "latitude", ny, &ydimid); - if(ret != NC_NOERR) return "Can't create y-dimension in the netcdf file"; - ret = nc_def_var(ncid, "longitude", NC_FLOAT, 1, &xdimid, &xid); - if(ret != NC_NOERR) return "Can't create longitude variable in the netcdf file"; - ret = nc_def_var(ncid, "latitude", NC_FLOAT, 1, &ydimid, &yid); - if(ret != NC_NOERR) return "Can't create latitude variable in the netcdf file"; + AddDim("x", nx); + AddDim("y", ny); + AddVar("x", NC_FLOAT, "y", "x"); + AddVar("y", NC_FLOAT, "y", "x"); break; } - case(G2V2): + case(GGRID): { - ret = nc_def_dim(ncid, "longitude", nx, &xdimid); - if(ret != NC_NOERR) return "Can't create x-dimension in the netcdf file"; - ret = nc_def_dim(ncid, "latitude", ny, &ydimid); - if(ret != NC_NOERR) return "Can't create y-dimension in the netcdf file"; - ret = nc_def_var(ncid, "longitude", NC_FLOAT, 2, dimid, &xid); - if(ret != NC_NOERR) return "Can't create longitude variable in the netcdf file"; - ret = nc_def_var(ncid, "latitude", NC_FLOAT, 2, dimid, &yid); - if(ret != NC_NOERR) return "Can't create latitude variable in the netcdf file"; + AddDim("longitude", nx); + AddDim("latitude", ny); + AddVar("longitude", NC_FLOAT, "latitude", "longitude"); + AddVar("latitude", NC_FLOAT, "latitude", "longitude"); break; } case(UNKNOWN): return "Can't determine file type"; } - ret = nc_def_var_deflate(ncid, xid, 1, 1, compress); - if(ret != NC_NOERR) return "Can't set deflate parameters for longitude variable in the netcdf file"; - ret = nc_def_var_deflate(ncid, yid, 1, 1, compress); - if(ret != NC_NOERR) return "Can't set deflate parameters for latitude variable in the netcdf file"; - - text = "longitude"; - ret = nc_put_att_text(ncid, xid, "standard_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write standard_name attribute of longitude variable in the netcdf file"; - text = "latitude"; - ret = nc_put_att_text(ncid, yid, "standard_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write standard_name attribute of latitude variable in the netcdf file"; - - text = "Longitude"; - ret = nc_put_att_text(ncid, xid, "long_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of longitude variable in the netcdf file"; - text = "Latitude"; - ret = nc_put_att_text(ncid, yid, "long_name", text.Len() + 1, text.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of latitude variable in the netcdf file"; + if(IsGeoType(stype)) + { + SetComp("longitude", compress); + SetComp("latitude", compress); + AddAtt("longitude", "standard_name", "longitude"); + AddAtt("longitude", "long_name", "Longitude"); + AddAtt("latitude", "standard_name", "latitude"); + AddAtt("latitude", "long_name", "Latitude"); + } + else + { + SetComp("x", compress); + SetComp("y", compress); + AddAtt("x", "long_name", "x-coordinate"); + AddAtt("y", "long_name", "y-coordinate"); + } + if(!*this) return "Can't set grid in the netcdf file " + name + ": " + ErrMessage(); type = stype; return ""; @@ -86,32 +91,22 @@ MString NCFileW::CreateFile(NCFileW::Type stype, const MString& name, const MStr MString NCFileW::AddVariable(const MString& name, const MString& stname, const MString& lname, const MString& units, const MString& comment) { if(type == UNKNOWN) return "File not opened"; - if(HaveVar(name)) return "Variable " + name + " already defined"; - struct Var v(name, 0); - - int ret; - if(type == G1V1) - ret = nc_def_var(ncid, v.name.Buf(), NC_FLOAT, 1, &xdimid, &v.id); + if(Is1DType(type)) + AddVar(name, NC_FLOAT, "i"); + else if(IsGeoType(type)) + AddVar(name, NC_FLOAT, "latitude", "longitude"); else - ret = nc_def_var(ncid, v.name.Buf(), NC_FLOAT, 2, dimid, &v.id); - if(ret != NC_NOERR) return "Can't create " + v.name + " variable in the netcdf file"; - - ret = nc_def_var_deflate(ncid, v.id, 1, 1, compress); - if(ret != NC_NOERR) return "Can't set deflate parameters for " + v.name + " variable in the netcdf file"; + AddVar(name, NC_FLOAT, "y", "x"); - if(stname.Exist()) ret = nc_put_att_text(ncid, v.id, "standard_name", stname.Len() + 1, stname.Buf()); - if(ret != NC_NOERR) return "Can't write standard_name attribute of " + v.name + " variable in the netcdf file"; - if(lname.Exist()) ret = nc_put_att_text(ncid, v.id, "long_name", lname.Len() + 1, lname.Buf()); - if(ret != NC_NOERR) return "Can't write long_name attribute of " + v.name + " variable in the netcdf file"; - if(units.Exist()) ret = nc_put_att_text(ncid, v.id, "units", units.Len() + 1, units.Buf()); - if(ret != NC_NOERR) return "Can't write units attribute of " + v.name + " variable in the netcdf file"; - if(comment.Exist()) ret = nc_put_att_text(ncid, v.id, "comment", comment.Len() + 1, comment.Buf()); - if(ret != NC_NOERR) return "Can't write comment attribute of " + v.name + " variable in the netcdf file"; + SetComp(name, compress); + if(stname.Exist()) AddAtt(name, "standard_name", stname); + if(lname.Exist()) AddAtt(name, "long_name", lname); + if(units.Exist()) AddAtt(name, "units", units); + if(comment.Exist()) AddAtt(name, "comment", comment); + AddAtt(name, "_FillValue", fill); - ret = nc_put_att_float(ncid, v.id, "_FillValue", NC_FLOAT, 1, &fill); - if(ret != NC_NOERR) return "Can't write _FillValue attribute of " + v.name + " variable in the netcdf file"; + if(!*this) return "Can't add variable " + name + ": " + ErrMessage(); - vars.push_back(v); return ""; }