diff --git a/actions/actiontsc.h b/actions/actiontsc.h index fabda5a..a09312b 100644 --- a/actions/actiontsc.h +++ b/actions/actiontsc.h @@ -20,6 +20,7 @@ template MString ActionTSC::DoAction(const CLArgs& args, D& ds) michlib_internal::ParameterListEx pars; pars.UsePrefix(""); pars.SetParameter("source", args.at("source")); + pars.SetParameter("history", args.at("_cmdline")); auto [tindexes, err] = GetTIndexes(ds, args, pars); if(err.Exist()) return err; @@ -114,7 +115,8 @@ template MString ActionTSC::DoAction(const CLArgs& args, D& ds) if(args.contains("compress")) compress = args.at("compress").ToInt(); - if(!err.Exist()) err = fw.Create(data[0], name, args.at("_cmdline"), compress); + if(!err.Exist()) err = fw.Create(data[0], name, compress); + if(!err.Exist()) err = fw.AddAtts(pars); for(size_t i = 0; i < data.size(); i++) if(!err.Exist()) err = fw.AddVariable(vnames[i], data[i].StandartName(), data[i].LongName(), data[i].Unit(), data[i].Comment()); if(!err.Exist()) err = fw.WriteGrid(data[0]); diff --git a/actions/actionuv.cpp b/actions/actionuv.cpp index d07e3fe..92a6261 100644 --- a/actions/actionuv.cpp +++ b/actions/actionuv.cpp @@ -1,13 +1,14 @@ #define MICHLIB_NOSOURCE #include "actionuv.h" -void UVMethods::StPoints::WriteBinBile(const MString& name) const +void UVMethods::StPoints::WriteBinBile(const MString& name, const michlib_internal::ParameterListEx& pars) const { BFileW stp; stp.Create(name, 3); stp.SetColumnName(1, lonlat ? "Longitude" : "x"); stp.SetColumnName(2, lonlat ? "Latitude" : "x"); stp.SetColumnName(3, "Stability (0 - saddle, 1 - st. anticicl. focus, 2 - st. knot, 3 - unst. anticicl. focus, 4 - unst. knot, 5 - st. cicl. focus, 6 - unst. cicl. focus)"); + stp.SetParameters(pars); for(size_t i = 0; i < N(); i++) { @@ -19,7 +20,7 @@ void UVMethods::StPoints::WriteBinBile(const MString& name) const stp.Close(); } -MString UVMethods::StPoints::WriteNcFile(const MString& name, const MString& history, int comp) const +MString UVMethods::StPoints::WriteNcFile(const MString& name, const michlib_internal::ParameterListEx& pars, const MString& history, int comp) const { NCFileWBase nc; @@ -30,6 +31,7 @@ MString UVMethods::StPoints::WriteNcFile(const MString& name, const MString& his if(!nc) return "Can't create netcdf file " + name + ": " + nc.ErrMessage(); nc.AddAtt("history", history); + nc.AddAtts(pars); nc.AddDim("i", N()); nc.AddVar(xname, NC_FLOAT, "i"); nc.AddVar(yname, NC_FLOAT, "i"); diff --git a/actions/actionuv.h b/actions/actionuv.h index 62dbb79..d5d74f7 100644 --- a/actions/actionuv.h +++ b/actions/actionuv.h @@ -86,9 +86,9 @@ class UVMethods } } - void WriteBinBile(const MString& name) const; + void WriteBinBile(const MString& name, const michlib_internal::ParameterListEx& pars) const; - MString WriteNcFile(const MString& name, const MString& history, int comp) const; + MString WriteNcFile(const MString& name, const michlib_internal::ParameterListEx& pars, const MString& history, int comp) const; }; }; @@ -105,6 +105,7 @@ template MString ActionUV::DoAction(const CLArgs& args, D& ds) michlib_internal::ParameterListEx pars; pars.UsePrefix(""); pars.SetParameter("source", args.at("source")); + pars.SetParameter("history", args.at("_cmdline")); bool mode = args.contains("geostrophic"); @@ -178,7 +179,8 @@ template MString ActionUV::DoAction(const CLArgs& args, D& ds) if(args.contains("compress")) compress = args.at("compress").ToInt(); - if(!err.Exist()) err = fw.Create(data, name, args.at("_cmdline"), compress); + if(!err.Exist()) err = fw.Create(data, name, compress); + if(!err.Exist()) err = fw.AddAtts(pars); if(!err.Exist()) err = fw.AddVariable("u", "", "Eastward velocity", u, ""); if(!err.Exist()) err = fw.AddVariable("v", "", "Northward velocity", u, ""); if(!err.Exist()) err = fw.AddVariable("div", "", "Velocity divergence", "(" + u + ")/" + d, ""); @@ -224,6 +226,7 @@ template MString ActionUV::DoAction(const CLArgs& args, D& ds) vel.SetColumnName(2, "Latitude"); vel.SetColumnName(3, "u, " + u); vel.SetColumnName(4, "v, " + u); + vel.SetParameters(pars); for(size_t ix = 0; ix < sdata.Nx(); ix++) for(size_t iy = 0; iy < sdata.Ny(); iy++) @@ -244,7 +247,8 @@ template MString ActionUV::DoAction(const CLArgs& args, D& ds) if(args.contains("compress")) compress = args.at("compress").ToInt(); - if(!err.Exist()) err = fw.Create(sdata, name, args.at("_cmdline"), compress); + if(!err.Exist()) err = fw.Create(sdata, name, compress); + if(!err.Exist()) err = fw.AddAtts(pars); if(!err.Exist()) err = fw.AddVariable("u", "", "Eastward velocity", u, ""); if(!err.Exist()) err = fw.AddVariable("v", "", "Northward velocity", u, ""); @@ -270,11 +274,11 @@ template MString ActionUV::DoAction(const CLArgs& args, D& ds) for(size_t iy = 0; iy < data.Ny() - 1; iy++) p.Add(data.StablePoints(ix, iy)); if(outfmt == "bin") - p.WriteBinBile(name); + p.WriteBinBile(name, pars); else if(outfmt == "nc" || outfmt == "netcdf") { int compress = args.contains("compress") ? args.at("compress").ToInt() : 3; - MString err = p.WriteNcFile(name, args.at("_cmdline"), compress); + MString err = p.WriteNcFile(name, pars, args.at("_cmdline"), compress); if(err.Exist()) return err; } else diff --git a/include/ncfilew.h b/include/ncfilew.h index 8974508..d49198a 100644 --- a/include/ncfilew.h +++ b/include/ncfilew.h @@ -84,7 +84,7 @@ class NCFileWBase public: constexpr Error(): err(NC_NOERR) {} constexpr Error(int n_err): err(n_err) {} - explicit operator bool() const { return err == NC_NOERR; } + 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; } @@ -100,7 +100,7 @@ class NCFileWBase const char* ErrMessage() const { return err.ErrMessage(); } explicit operator bool() const { return Ok(); } - bool Ok() const { return opened && !err.IsErr(); } + bool Ok() const { return opened && !err.IsErr(); } operator int() const { return ncid; } void Close() @@ -124,11 +124,91 @@ class NCFileWBase 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) - void AddAtt(const MString& name, const T& val) + MString AddAtt(const MString& name, const T& val) { - if(err.IsErr()) return; + 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; @@ -138,19 +218,21 @@ class NCFileWBase 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) - void AddAtt(const MString& vname, const MString& name, const T& val) + MString AddAtt(const MString& vname, const MString& name, const T& val) { - if(err.IsErr()) return; + 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; + 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)); @@ -158,16 +240,19 @@ class NCFileWBase 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() : ""; } - void AddAtt(const MString& vname, const MString& name, const char* val) + MString AddAtt(const MString& vname, const MString& name, const char* val) { - if(err.IsErr()) return; + 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; + 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...})); } @@ -257,18 +342,18 @@ class NCFileW: public NCFileWBase 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); + 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, const MString& history, int compression) + 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, history, compression, data.N(), 0); + return CreateFile(DetType(), name, compression, data.N(), 0); else - return CreateFile(DetType(), name, history, compression, data.Nx(), data.Ny()); + return CreateFile(DetType(), name, compression, data.Nx(), data.Ny()); } void Close() diff --git a/src/ncfilew.cpp b/src/ncfilew.cpp index 31fb246..07ee85c 100644 --- a/src/ncfilew.cpp +++ b/src/ncfilew.cpp @@ -1,7 +1,7 @@ #define MICHLIB_NOSOURCE #include "ncfilew.h" -MString NCFileW::CreateFile(NCFileW::Type stype, const MString& name, const MString& history, int compression, size_t nx, size_t ny) +MString NCFileW::CreateFile(NCFileW::Type stype, const MString& name, int compression, size_t nx, size_t ny) { if(stype == UNKNOWN) return "Can't determine file type"; @@ -12,7 +12,6 @@ MString NCFileW::CreateFile(NCFileW::Type stype, const MString& name, const MStr Open(name); if(!*this) return "Can't create netcdf file " + name + ": " + ErrMessage(); - AddAtt("history", history); AddAtt("node_offset", node_offset); switch(stype)