#define MICHLIB_NOSOURCE #include "layereddata.h" MString LayeredData::Info() const { if(!isOk()) return ""; MString d; for(size_t i = 0; i < NDepths(); i++) d += MString(" ") + "(" + i + " " + Depth(i) + ")"; std::set vars; for(const auto& f: nc) GetVars(f.Get(), vars); MString svars; { bool first = true; for(const auto& v: vars) { svars += (first ? "" : ", ") + v; first = false; } } // clang-format off return "Dataset: " + Title() + "\n" + " Begin date: " + Time(0).ToString() + "\n" + " End date: " + Time(NTimes()-1).ToString() + "\n" + " Time step: " + Timestep() + " seconds\n" + " Time moments: " + NTimes() + "\n" + " Region: (" + lonb + " : " + lone + ") x (" + latb + " : " + late + ")\n" + " Grid: " + dname.nx + "x" + dname.ny + " (" + lonstep + " x " + latstep + ")\n" + " Depths:" + d + "\n" + " Supported variables: " + svars; // clang-format on } MString LayeredData::Open(const MString& dataset) { nc.clear(); MString proxyurl = GPL.ParameterSValue("USEPROXY", ""); if(proxyurl.Exist()) proxy.Activate("all_proxy", proxyurl); nc.clear(); size_t i = 1; while(true) { MString url = GPL.ParameterSValue(dataset + "_URL" + i, ""); if(url.Exist()) { //michlib::message("Open "+url); nc.emplace_back(std::move(url)); if(!nc.back()) { nc.clear(); return "Can't connect to url " + url; } } else break; i++; } if(nc.size() == 0) return "No urls for dataset " + dataset + " specified in config"; dname = GetDNames(nc[0].Get()); if(!(dname.lonname.Exist() && dname.latname.Exist())) { nc.clear(); return "Can't find longitude/latitude"; } if(!dname.timename.Exist()) { nc.clear(); return "Can't find time"; } auto cn = GetCNames(nc[0].Get()); // Read times for(auto& f: nc) { MString ret = f.ReadTimes(cn.timename); if(ret.Exist()) { nc.clear(); return ret; } times.insert(times.end(), f.Times().begin(), f.Times().end()); } std::sort(times.begin(), times.end()); auto last = std::unique(times.begin(), times.end()); times.erase(last, times.end()); if(cn.depthname.Exist()) { auto rdepths = nc[0]->VR(cn.depthname); if(!rdepths) { nc.clear(); return "Can't read depths"; } depths.resize(rdepths.DimLen(0)); for(size_t i = 0; i < depths.size(); i++) depths[i] = rdepths(i); } else // Surface only data { depths.resize(1); depths[0] = 0; } auto lons = nc[0]->VR(cn.lonname); auto lats = nc[0]->VR(cn.latname); if(!(lons && lats)) { nc.clear(); return "Can't get longitudes/latitudes"; } lonb = lons(0); latb = lats(0); lone = lons(dname.nx - 1); late = lats(dname.ny - 1); lonstep = (lone - lonb) / (dname.nx - 1); latstep = (late - latb) / (dname.ny - 1); return ""; } std::pair LayeredData::Parameters(michlib_internal::ParameterListEx& pars, const CLArgs& args, const struct Region& reg) const { std::unique_ptr ppar{new struct Parameters}; ppar->layer = args.contains("layer") ? args.at("layer").ToInteger() : 0; if(!args.contains("depth") && ppar->layer >= NDepths()) return {nullptr, MString("Layer ") + ppar->layer + " is too deep!"}; real depth = args.contains("depth") ? args.at("depth").ToReal() : Depth(ppar->layer); { auto dom = DetGeoDomain(lonb, lone); real lon1 = ToGeoDomain(reg.lonb, dom); real lon2 = ToGeoDomain(reg.lone, dom); real lat1 = reg.latb; real lat2 = reg.late; // Special case when the longitude lies in a small sector between the end and the start if(lon1 < lonb) lon1 = lone; if(lon2 > lone) lon2 = lonb; ppar->yb = static_cast(Floor((lat1 - latb) / latstep)); ppar->ye = static_cast(Ceil((lat2 - latb) / latstep)); if(ppar->ye > dname.ny - 1) ppar->ye = dname.ny - 1; if(ppar->yb >= ppar->ye) return {nullptr, "Latb must be lesser then late"}; ppar->xb = static_cast(Floor((lon1 - lonb) / lonstep)); ppar->xe = static_cast(Ceil((lon2 - lonb) / lonstep)); if(ppar->xb == ppar->xe) return {nullptr, "Lonb must be not equal late"}; if(depth < 0.0 || depth > depths.back()) ppar->layer = (depth < 0.0) ? 0 : (depths.size() - 1); else for(size_t i = 0; i < depths.size() - 1; i++) { if(depth >= depths[i] && depth <= depths[i + 1]) { ppar->layer = (depth - depths[i] <= depths[i + 1] - depth) ? i : (i + 1); break; } } } pars.SetParameter("depth", Depth(ppar->layer)); pars.SetParameter("layer", ppar->layer); pars.SetParameter("dataset", Title()); pars.SetParameter("lonb", Lon(ppar->xb)); pars.SetParameter("latb", Lat(ppar->yb)); pars.SetParameter("lone", Lon(ppar->xe)); pars.SetParameter("late", Lat(ppar->ye)); return {ppar.release(), ""}; } LayeredData::Data LayeredData::Read(const MString& vname, const BaseParameters* ip, size_t i) const { if(!isOk()) return Data(); bool nodepth = false; auto p = dynamic_cast(ip); auto [name, id, tid] = VarNameLoc(vname, times[i]); if(!name.Exist()) // Conversion read { // ptemp from temp and sal if(vname == "ptemp") { auto temp = Read("temp", ip, i); auto sal = Read("sal", ip, i); if(!(temp && sal)) return Data(); auto out = temp; out.SetUnit("degrees_C"); for(size_t ind = 0; ind < out.N(); ind++) { if(temp.IsFill(ind) || sal.IsFill(ind)) out.V(ind) = out.Fillval(); else out.V(ind) = Temp2PTemp(temp.V(ind), sal.V(ind), Depth(p->layer), out.Lon(ind), out.Lat(ind)); } return out; } // temp from ptemp and sal if(vname == "temp") { auto temp = Read("ptemp", ip, i); auto sal = Read("sal", ip, i); if(!(temp && sal)) return Data(); auto out = temp; out.SetUnit("degrees_C"); for(size_t ind = 0; ind < out.N(); ind++) { if(temp.IsFill(ind) || sal.IsFill(ind)) out.V(ind) = out.Fillval(); else out.V(ind) = PTemp2Temp(temp.V(ind), sal.V(ind), Depth(p->layer), out.Lon(ind), out.Lat(ind)); } return out; } // pdens from temp and sal if(vname == "pdens") { bool tempispot = HaveVar("ptemp"); auto temp = Read(tempispot ? "ptemp" : "temp", ip, i); auto sal = Read("sal", ip, i); if(!(temp && sal)) return Data(); auto out = temp; out.SetUnit("kg m-3"); for(size_t ind = 0; ind < out.N(); ind++) { if(temp.IsFill(ind) || sal.IsFill(ind)) out.V(ind) = out.Fillval(); else out.V(ind) = tempispot ? PTemp2PDens(temp.V(ind), sal.V(ind), Depth(p->layer), out.Lon(ind), out.Lat(ind)) : Temp2PDens(temp.V(ind), sal.V(ind), Depth(p->layer), out.Lon(ind), out.Lat(ind)); } return out; } // U and U2 from u and v if(vname == "U" || vname == "U2") { bool square = vname == "U2"; auto u = Read("u", ip, i); auto v = Read("v", ip, i); if(!(u && v)) return Data(); auto out = u; out.SetUnit(square ? ("(" + u.Unit() + ")2") : u.Unit()); for(size_t ind = 0; ind < out.N(); ind++) { if(u.IsFill(ind) || v.IsFill(ind)) out.V(ind) = out.Fillval(); else out.V(ind) = square ? (u(ind) * u(ind) + v(ind) * v(ind)) : michlib::Hypot(u(ind), v(ind)); } return out; } return Data(); } // Direct read auto head = nc[id]->Header(); for(const auto& v: head.Variables()) if(v.Name() == name) { if(v.Dimensions().size() == 3) nodepth = true; if(v.Type().Id() == NC_SHORT) return ReadVarRaw(nc[id], name, tid, nodepth, p); if(v.Type().Id() == NC_INT) return ReadVarRaw(nc[id], name, tid, nodepth, p); if(v.Type().Id() == NC_FLOAT) return ReadVarRaw(nc[id], name, tid, nodepth, p); if(v.Type().Id() == NC_DOUBLE) return ReadVarRaw(nc[id], name, tid, nodepth, p); } return Data(); } template LayeredData::Data LayeredData::ReadVarRaw(const NC& f, const MString& name, size_t i, bool nodepth, const struct LayeredData::Parameters* p) const { real unitmul = 1.0; DataType fill; real offset = 0.0, scale = 1.0; { auto a_fill = f->A(name, "_FillValue"); auto a_offset_d = f->A(name, "add_offset"); auto a_scale_d = f->A(name, "scale_factor"); auto a_offset_f = f->A(name, "add_offset"); auto a_scale_f = f->A(name, "scale_factor"); if(a_fill) fill = a_fill; else { if constexpr(std::is_floating_point_v) fill = NAN; else fill = -1; } if(a_offset_d) offset = a_offset_d; if(a_scale_d) scale = a_scale_d; if(a_offset_f) offset = a_offset_f; if(a_scale_f) scale = a_scale_f; } MString unit; { auto unitatt = f->A(name, "units"); if(unitatt) unit = unitatt; } if(unit == "m s-1" || unit == "m/s") { unitmul = 100.0; unit = "cm/s"; } Data data((p->xb < p->xe) ? (p->xe - p->xb + 1) : (dname.nx + p->xe - p->xb + 1), p->ye - p->yb + 1, Lon(p->xb), Lat(p->yb), lonstep, latstep, std::move(unit)); if(p->xb < p->xe) { auto var = nodepth ? f->V(name, {dname.lonname, p->xb, p->xe - p->xb + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}) : f->V(name, {dname.lonname, p->xb, p->xe - p->xb + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}, {dname.depthname, p->layer, 1}); if(!var) return Data(); if(var.DimLen(0) != data.Nx() || var.DimLen(1) != data.Ny()) return Data(); for(size_t ix = 0; ix < var.DimLen(0); ix++) for(size_t iy = 0; iy < var.DimLen(1); iy++) { DataType v = var(ix, iy); data(ix, iy) = (v == fill || isnan(v)) ? Data::Fillval() : ((v * scale + offset) * unitmul); } } else { auto var1 = nodepth ? f->V(name, {dname.lonname, p->xb}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}) : f->V(name, {dname.lonname, p->xb}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}, {dname.depthname, p->layer, 1}); auto var2 = nodepth ? f->V(name, {dname.lonname, 0, p->xe + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}) : f->V(name, {dname.lonname, 0, p->xe + 1}, {dname.latname, p->yb, p->ye - p->yb + 1}, {dname.timename, i, 1}, {dname.depthname, p->layer, 1}); if(!(var1 && var2)) return Data(); if((var1.DimLen(0) + var2.DimLen(0)) != data.Nx() || var1.DimLen(1) != data.Ny() || var2.DimLen(1) != data.Ny()) return Data(); for(size_t ix = 0; ix < var1.DimLen(0); ix++) for(size_t iy = 0; iy < var1.DimLen(1); iy++) { DataType v = var1(ix, iy); data(ix, iy) = (v == fill || isnan(v)) ? Data::Fillval() : ((v * scale + offset) * unitmul); } for(size_t ix = 0; ix < var2.DimLen(0); ix++) for(size_t iy = 0; iy < var2.DimLen(1); iy++) { DataType v = var2(ix, iy); data(ix + var1.DimLen(0), iy) = (v == fill || isnan(v)) ? Data::Fillval() : ((v * scale + offset) * unitmul); } } return data; }