#pragma once #include "DataAdapters/ncfilealt.h" #include "basedata.h" #include "mdatetime.h" #include "nczarr.h" #include #include #include using michlib::MDateTime; using michlib::MString; using michlib::NCFileA; class NCFuncs { public: struct CoordNames { MString lonname, latname, depthname, timename; }; struct DimNames { MString lonname, latname, depthname, timename; size_t nx, ny, nz, nt; }; static MString StName2Name(const MString& stname); static MString Name2StName(const MString& name); static MString Name2LongName(const MString& name); static std::tuple Refdate(const MString& refdate); static void GetVars(const NCFileA& nc, std::set& vars); static CoordNames GetCNames(const NCFileA& nc); static DimNames GetDNames(const NCFileA& nc); static bool HaveVar(const NCFileA& nc, const MString& vname); static void GetVars(const NCZarr& nc, std::set& vars); static CoordNames GetCNames(const NCZarr& nc); static DimNames GetDNames(const NCZarr& nc); static bool HaveVar(const NCZarr& nc, const MString& vname); template static VarPresence CheckVar(const MString& vname, HV hv) { if(!hv(vname)) { auto varexist = VarPresence::NONE; if(vname == "temp" && hv("ptemp") && hv("sal")) varexist = VarPresence::DERIVED; if(vname == "ptemp" && hv("temp") && hv("sal")) varexist = VarPresence::DERIVED; if(vname == "pdens" && (hv("ptemp") || hv("temp")) && hv("sal")) varexist = VarPresence::DERIVED; if((vname == "U" || vname == "U2") && hv("u") && hv("v")) varexist = VarPresence::DERIVED; if((vname == "ugeo" || vname == "vgeo") && hv("ssh")) varexist = VarPresence::DERIVED; return varexist; } return VarPresence::INTERNAL; } template static bool TransformationRead(const D* data, const MString& vname, std::map& cache, const BaseParameters* ip, size_t i) { if constexpr(requires(const D* d, const BaseParameters* p) { { d->Depth(p) } -> std::convertible_to; }) { real depth = data->Depth(ip); // ptemp from temp and sal if(vname == "ptemp") { if(!(data->Read("temp", cache, ip, i) && data->Read("sal", cache, ip, i))) return false; cache[vname] = cache.at("temp").CopyGrid(); auto& ptemp = cache.at(vname); const auto& temp = cache.at("temp"); const auto& sal = cache.at("sal"); ptemp.SetUnit("degrees_C"); for(size_t ind = 0; ind < ptemp.N(); ind++) { if(temp.IsFill(ind) || sal.IsFill(ind)) ptemp.V(ind) = ptemp.Fillval(); else ptemp.V(ind) = Temp2PTemp(temp.V(ind), sal.V(ind), depth, ptemp.Lon(ind), ptemp.Lat(ind)); } return true; } // temp from ptemp and sal if(vname == "temp") { if(!(data->Read("ptemp", cache, ip, i) && data->Read("sal", cache, ip, i))) return false; cache[vname] = cache.at("ptemp").CopyGrid(); auto& temp = cache.at(vname); const auto& ptemp = cache.at("ptemp"); const auto& sal = cache.at("sal"); temp.SetUnit("degrees_C"); for(size_t ind = 0; ind < temp.N(); ind++) { if(ptemp.IsFill(ind) || sal.IsFill(ind)) temp.V(ind) = temp.Fillval(); else temp.V(ind) = PTemp2Temp(ptemp.V(ind), sal.V(ind), depth, temp.Lon(ind), temp.Lat(ind)); } return true; } // pdens from temp and sal if(vname == "pdens") { bool tempispot = data->CheckVar("ptemp") == VarPresence::INTERNAL; if(!(data->Read(tempispot ? "ptemp" : "temp", cache, ip, i) && data->Read("sal", cache, ip, i))) return false; cache[vname] = cache.at("sal").CopyGrid(); auto& pdens = cache.at(vname); const auto& temp = cache.at(tempispot ? "ptemp" : "temp"); const auto& sal = cache.at("sal"); pdens.SetUnit("kg m-3"); for(size_t ind = 0; ind < pdens.N(); ind++) { if(temp.IsFill(ind) || sal.IsFill(ind)) pdens.V(ind) = pdens.Fillval(); else pdens.V(ind) = tempispot ? PTemp2PDens(temp.V(ind), sal.V(ind), depth, pdens.Lon(ind), pdens.Lat(ind)) : Temp2PDens(temp.V(ind), sal.V(ind), depth, pdens.Lon(ind), pdens.Lat(ind)); } return true; } } // U and U2 from u and v if(vname == "U" || vname == "U2") { bool square = vname == "U2"; if(!(data->Read("u", cache, ip, i) && data->Read("v", cache, ip, i))) return false; cache[vname] = cache.at("u").CopyGrid(); auto& U = cache.at(vname); const auto& u = cache.at("u"); const auto& v = cache.at("v"); if(u.Unit().Exist()) U.SetUnit(square ? ("(" + u.Unit() + ")2") : u.Unit()); for(size_t ind = 0; ind < U.N(); ind++) { if(u.IsFill(ind) || v.IsFill(ind)) U.V(ind) = U.Fillval(); else U.V(ind) = square ? (u(ind) * u(ind) + v(ind) * v(ind)) : michlib::Hypot(u(ind), v(ind)); } return true; } // ugeo if(vname == "ugeo") { if(!data->Read("ssh", cache, ip, i)) return false; const auto& ssh = cache.at("ssh"); const real mul = (ssh.Unit() == "cm") ? 1.0 : ((ssh.Unit() == "m") ? 100.0 : -1.0); if(mul < 0.0) return false; // Unknown units cache[vname] = cache.at("ssh").CopyGrid(); auto& ugeo = cache.at(vname); ugeo.SetUnit("cm/s"); for(size_t iy = 0; iy < ssh.Ny(); iy++) { const real phi = michlib::M_PI * ssh.Iy2Lat(iy) / 180.0; const real sphi = michlib::Sin(phi); const real s2phi = michlib::Sin(2.0 * phi); const real g = 978.0318 * (1.0 + 0.005302 * sphi * sphi - 0.000006 * s2phi * s2phi); // g in cm/s const real f = 2.0 * 7.29211 * sphi; // Coriolis parameter multiplied by 100'000 const real db = (iy == 0) ? 0.0 : (michlib::M_PI * 6371.0 * (ssh.Iy2Lat(iy) - ssh.Iy2Lat(iy - 1)) / 180.0); // Distance in km, compensated by multiplier in Coriolis parameter const real du = (iy == ssh.Ny() - 1) ? 0.0 : (michlib::M_PI * 6371.0 * (ssh.Iy2Lat(iy + 1) - ssh.Iy2Lat(iy)) / 180.0); // Distance in km, compensated by multiplier in Coriolis parameter for(size_t ix = 0; ix < ssh.Nx(); ix++) { const bool bok = iy != 0 && !ssh.IsFill(ix, iy - 1) && !ssh.IsFill(ix, iy); const bool uok = iy != ssh.Ny() - 1 && !ssh.IsFill(ix, iy + 1) && !ssh.IsFill(ix, iy); if(!(bok || uok)) ugeo(ix, iy) = ugeo.Fillval(); else if(!bok) ugeo(ix, iy) = -g * mul * (ssh(ix, iy + 1) - ssh(ix, iy)) / (f * du); else if(!uok) ugeo(ix, iy) = -g * mul * (ssh(ix, iy) - ssh(ix, iy - 1)) / (f * db); else { const real bder = -g * mul * (ssh(ix, iy) - ssh(ix, iy - 1)) / (f * db); const real uder = -g * mul * (ssh(ix, iy + 1) - ssh(ix, iy)) / (f * du); ugeo(ix, iy) = 0.5 * (bder + uder); } } } return true; } // vgeo if(vname == "vgeo") { if(!data->Read("ssh", cache, ip, i)) return false; const auto& ssh = cache.at("ssh"); const real mul = (ssh.Unit() == "cm") ? 1.0 : ((ssh.Unit() == "m") ? 100.0 : -1.0); if(mul < 0.0) return false; // Unknown units cache[vname] = cache.at("ssh").CopyGrid(); auto& vgeo = cache.at(vname); vgeo.SetUnit("cm/s"); for(size_t iy = 0; iy < ssh.Ny(); iy++) { const real phi = michlib::M_PI * ssh.Iy2Lat(iy) / 180.0; const real sphi = michlib::Sin(phi); const real cphi = michlib::Cos(phi); const real s2phi = michlib::Sin(2.0 * phi); const real g = 978.0318 * (1.0 + 0.005302 * sphi * sphi - 0.000006 * s2phi * s2phi); // g in cm/s const real f = 2.0 * 7.29211 * sphi; // Coriolis parameter multiplied by 100'000 for(size_t ix = 0; ix < ssh.Nx(); ix++) { const real dl = (ix == 0) ? 0.0 : (michlib::M_PI * 6371.0 * (ssh.Ix2Lon(ix) - ssh.Ix2Lon(ix - 1)) / 180.0) * cphi; // Distance in km, compensated by multiplier in Coriolis parameter const real dr = (ix == ssh.Nx() - 1) ? 0.0 : (michlib::M_PI * 6371.0 * (ssh.Ix2Lon(ix + 1) - ssh.Ix2Lon(ix)) / 180.0) * cphi; // Distance in km, compensated by multiplier in Coriolis parameter const bool lok = ix != 0 && !ssh.IsFill(ix - 1, iy) && !ssh.IsFill(ix, iy); const bool rok = ix != ssh.Nx() - 1 && !ssh.IsFill(ix + 1, iy) && !ssh.IsFill(ix, iy); if(!(lok || rok)) vgeo(ix, iy) = vgeo.Fillval(); else if(!lok) vgeo(ix, iy) = g * mul * (ssh(ix + 1, iy) - ssh(ix, iy)) / (f * dr); else if(!rok) vgeo(ix, iy) = g * mul * (ssh(ix, iy) - ssh(ix - 1, iy)) / (f * dl); else { const real lder = g * mul * (ssh(ix, iy) - ssh(ix - 1, iy)) / (f * dl); const real rder = g * mul * (ssh(ix + 1, iy) - ssh(ix, iy)) / (f * dr); vgeo(ix, iy) = 0.5 * (lder + rder); } } } return true; } return false; } };