Michael Uleysky
2 years ago
8 changed files with 490 additions and 16 deletions
@ -0,0 +1,225 @@
|
||||
#pragma once |
||||
#include "geohelpers.h" |
||||
#include <vector> |
||||
|
||||
using michlib::M_PI; |
||||
using michlib::real; |
||||
using michlib::Sqrt; |
||||
|
||||
enum class Metric |
||||
{ |
||||
EUCLID, |
||||
SPHERIC |
||||
}; |
||||
|
||||
class UVData |
||||
{ |
||||
static constexpr real fillval = 1.0e10; |
||||
real x0 = 0.0, y0 = 0.0; |
||||
size_t nx = 0, ny = 0; |
||||
real xstep = 0.0, ystep = 0.0; |
||||
|
||||
std::vector<real> u, v; |
||||
Metric metric; |
||||
|
||||
real D(real lon1, real lat1, real lon2, real lat2) const |
||||
{ |
||||
switch(metric) |
||||
{ |
||||
case(Metric::EUCLID): return michlib::Hypot(lon2 - lon1, lat2 - lat1); |
||||
// Spheric distance in km
|
||||
case(Metric::SPHERIC): return michlib::GCD(lon1 * M_PI / 180.0, lat1 * M_PI / 180.0, lon2 * M_PI / 180.0, lat2 * M_PI / 180.0) * 6371.0; |
||||
} |
||||
return 0.0; |
||||
} |
||||
|
||||
// For derivatives
|
||||
real Ud(size_t ix, size_t iy) const { return IsCoast(ix, iy) ? 0.0 : U(ix, iy); } |
||||
real Vd(size_t ix, size_t iy) const { return IsCoast(ix, iy) ? 0.0 : V(ix, iy); } |
||||
|
||||
real dUdX(size_t ix, size_t iy) const |
||||
{ |
||||
if(IsCoast(ix, iy)) return fillval; |
||||
if(ix == 0) return (Ud(1, iy) - Ud(0, iy)) / D(Lon(0, iy), Lat(0, iy), Lon(1, iy), Lat(1, iy)); |
||||
if(ix == nx - 1) return (Ud(nx - 1, iy) - Ud(nx - 2, iy)) / D(Lon(nx - 2, iy), Lat(nx - 2, iy), Lon(nx - 1, iy), Lat(nx - 1, iy)); |
||||
return 0.5 * ((Ud(ix + 1, iy) - Ud(ix, iy)) / D(Lon(ix, iy), Lat(ix, iy), Lon(ix + 1, iy), Lat(ix + 1, iy)) + |
||||
(Ud(ix, iy) - Ud(ix - 1, iy)) / D(Lon(ix - 1, iy), Lat(ix - 1, iy), Lon(ix, iy), Lat(ix, iy))); |
||||
} |
||||
real dVdX(size_t ix, size_t iy) const |
||||
{ |
||||
if(IsCoast(ix, iy)) return fillval; |
||||
if(ix == 0) return (Vd(1, iy) - Vd(0, iy)) / D(Lon(0, iy), Lat(0, iy), Lon(1, iy), Lat(1, iy)); |
||||
if(ix == nx - 1) return (Vd(nx - 1, iy) - Vd(nx - 2, iy)) / D(Lon(nx - 2, iy), Lat(nx - 2, iy), Lon(nx - 1, iy), Lat(nx - 1, iy)); |
||||
return 0.5 * ((Vd(ix + 1, iy) - Vd(ix, iy)) / D(Lon(ix, iy), Lat(ix, iy), Lon(ix + 1, iy), Lat(ix + 1, iy)) + |
||||
(Vd(ix, iy) - Vd(ix - 1, iy)) / D(Lon(ix - 1, iy), Lat(ix - 1, iy), Lon(ix, iy), Lat(ix, iy))); |
||||
} |
||||
real dUdY(size_t ix, size_t iy) const |
||||
{ |
||||
if(IsCoast(ix, iy)) return fillval; |
||||
if(iy == 0) return (Ud(ix, 1) - Ud(ix, 0)) / D(Lon(ix, 0), Lat(ix, 0), Lon(ix, 1), Lat(ix, 1)); |
||||
if(iy == ny - 1) return (Ud(ix, ny - 1) - Ud(ix, ny - 2)) / D(Lon(ix, ny - 2), Lat(ix, ny - 2), Lon(ix, ny - 1), Lat(ix, ny - 1)); |
||||
return 0.5 * ((Ud(ix, iy + 1) - Ud(ix, iy)) / D(Lon(ix, iy), Lat(ix, iy), Lon(ix, iy + 1), Lat(ix, iy + 1)) + |
||||
(Ud(ix, iy) - Ud(ix, iy - 1)) / D(Lon(ix, iy - 1), Lat(ix, iy - 1), Lon(ix, iy), Lat(ix, iy))); |
||||
} |
||||
real dVdY(size_t ix, size_t iy) const |
||||
{ |
||||
if(IsCoast(ix, iy)) return fillval; |
||||
if(iy == 0) return (Vd(ix, 1) - Vd(ix, 0)) / D(Lon(ix, 0), Lat(ix, 0), Lon(ix, 1), Lat(ix, 1)); |
||||
if(iy == ny - 1) return (Vd(ix, ny - 1) - Vd(ix, ny - 2)) / D(Lon(ix, ny - 2), Lat(ix, ny - 2), Lon(ix, ny - 1), Lat(ix, ny - 1)); |
||||
return 0.5 * ((Vd(ix, iy + 1) - Vd(ix, iy)) / D(Lon(ix, iy), Lat(ix, iy), Lon(ix, iy + 1), Lat(ix, iy + 1)) + |
||||
(Vd(ix, iy) - Vd(ix, iy - 1)) / D(Lon(ix, iy - 1), Lat(ix, iy - 1), Lon(ix, iy), Lat(ix, iy))); |
||||
} |
||||
|
||||
// For stationary points
|
||||
real LonR(real x, [[maybe_unused]] real y) const { return x0 + x * xstep; } |
||||
real LatR([[maybe_unused]] real x, real y) const { return y0 + y * ystep; } |
||||
|
||||
public: |
||||
enum STPOINT |
||||
{ |
||||
SADDLE = 0, |
||||
SACICFOCUS, |
||||
SKNOT, |
||||
UACICFOCUS, |
||||
UKNOT, |
||||
SCICFOCUS, |
||||
UCICFOCUS, |
||||
NOPOINT |
||||
}; |
||||
|
||||
struct StPoint |
||||
{ |
||||
real x = 0.0, y = 0.0; |
||||
STPOINT type = NOPOINT; |
||||
}; |
||||
|
||||
UVData() = default; |
||||
UVData(size_t nx_, size_t ny_, real x0_, real y0_, real xs_, real ys_, Metric m_ = Metric::SPHERIC): |
||||
x0(x0_), y0(y0_), nx(nx_), ny(ny_), xstep(xs_), ystep(ys_), u(nx_ * ny_), v(nx_ * ny_), metric(m_) |
||||
{ |
||||
} |
||||
|
||||
const real& U(size_t i) const { return u[i]; } |
||||
const real& V(size_t i) const { return v[i]; } |
||||
const real& U(size_t ix, size_t iy) const { return U(iy * nx + ix); } |
||||
const real& V(size_t ix, size_t iy) const { return V(iy * nx + ix); } |
||||
|
||||
real& U(size_t i) { return u[i]; } |
||||
real& V(size_t i) { return v[i]; } |
||||
real& U(size_t ix, size_t iy) { return U(iy * nx + ix); } |
||||
real& V(size_t ix, size_t iy) { return V(iy * nx + ix); } |
||||
|
||||
size_t N() const { return u.size(); } |
||||
size_t Nx() const { return nx; } |
||||
size_t Ny() const { return ny; } |
||||
|
||||
real Lon(size_t ix, [[maybe_unused]] size_t iy) const { return x0 + ix * xstep; } |
||||
real Lat([[maybe_unused]] size_t ix, size_t iy) const { return y0 + iy * ystep; } |
||||
|
||||
real Lon(size_t i) const { return Lon(i % nx, i / nx); } |
||||
real Lat(size_t i) const { return Lat(i % nx, i / nx); } |
||||
|
||||
explicit operator bool() const { return N() != 0; } |
||||
|
||||
bool IsCoast(size_t i) const { return U(i) == fillval || V(i) == fillval; } |
||||
bool IsCoast(size_t ix, size_t iy) const { return U(ix, iy) == fillval || V(ix, iy) == fillval; } |
||||
|
||||
static real Fillval() { return fillval; } |
||||
|
||||
real Div(size_t i) const { return Div(i % nx, i / nx); } |
||||
real Rot(size_t i) const { return Rot(i % nx, i / nx); } |
||||
real Div(size_t ix, size_t iy) const |
||||
{ |
||||
if(!*this) return 0.0; |
||||
real ux = dUdX(ix, iy); |
||||
real vy = dVdY(ix, iy); |
||||
return (ux == fillval || vy == fillval) ? fillval : (ux + vy); |
||||
} |
||||
real Rot(size_t ix, size_t iy) const |
||||
{ |
||||
if(!*this) return 0.0; |
||||
real vx = dVdX(ix, iy); |
||||
real uy = dUdY(ix, iy); |
||||
return (vx == fillval || uy == fillval) ? fillval : (vx - uy); |
||||
} |
||||
// Okubo-Weiss parameter
|
||||
real OW(size_t i) const { return OW(i % nx, i / nx); } |
||||
real OW(size_t ix, size_t iy) const |
||||
{ |
||||
if(!*this) return 0.0; |
||||
real ux = dUdX(ix, iy); |
||||
real uy = dUdY(ix, iy); |
||||
real vx = dVdX(ix, iy); |
||||
real vy = dVdY(ix, iy); |
||||
|
||||
if(ux == fillval || uy == fillval || vx == fillval || vy == fillval) return fillval; |
||||
|
||||
real sn = ux - vy; |
||||
real ss = vx + uy; |
||||
real w = vx - uy; |
||||
return sn * sn + ss * ss - w * w; |
||||
} |
||||
|
||||
auto StablePoints(size_t ix, size_t iy) const |
||||
{ |
||||
std::vector<struct StPoint> points; |
||||
if(!*this) return points; |
||||
if(ix >= Nx() - 1 || iy >= Ny() - 1) return points; |
||||
if(IsCoast(ix, iy) || IsCoast(ix + 1, iy) || IsCoast(ix, iy + 1) || IsCoast(ix + 1, iy + 1)) return points; |
||||
|
||||
real au = U(ix, iy) - U(ix, iy + 1) - U(ix + 1, iy) + U(ix + 1, iy + 1); |
||||
real bu = U(ix, iy + 1) - U(ix, iy); |
||||
real cu = U(ix + 1, iy) - U(ix, iy); |
||||
real du = U(ix, iy); |
||||
|
||||
real av = V(ix, iy) - V(ix, iy + 1) - V(ix + 1, iy) + V(ix + 1, iy + 1); |
||||
real bv = V(ix, iy + 1) - V(ix, iy); |
||||
real cv = V(ix + 1, iy) - V(ix, iy); |
||||
real dv = V(ix, iy); |
||||
|
||||
real D = au * bv - av * bu; |
||||
real a = (au * av * cu - au * au * cv) / D; |
||||
real b = (au * bv * cu - au * bu * cv + au * av * du - au * au * dv) / D; |
||||
real c = (au * bv * du - au * bu * dv) / D; |
||||
real des = b * b - 4.0 * a * c; |
||||
if(des < 0) return points; |
||||
des = Sqrt(des); |
||||
|
||||
real p = av * cu - au * cv; |
||||
real q = av * bu - au * bv; |
||||
real r = av * du - au * dv; |
||||
real x1 = 0.5 * (-b + des) / a; |
||||
real x2 = 0.5 * (-b - des) / a; |
||||
real y1 = -(p * x1 + r) / q; |
||||
real y2 = -(p * x2 + r) / q; |
||||
|
||||
auto PointType = [au = au, av = av, bu = bu, bv = bv, cu = cu, cv = cv](real x, real y) -> auto |
||||
{ |
||||
real a = 1.0; |
||||
real b = -y * au - x * av - bv - cu; |
||||
real c = y * (au * bv - av * bu) + x * (av * cu - au * cv) + bv * cu - bu * cv; |
||||
real des = b * b - 4.0 * a * c; |
||||
if(des > 0.0) |
||||
{ |
||||
if(std::max(-b + Sqrt(des), -b - Sqrt(des)) > 0.0 && c > 0.0) |
||||
return UKNOT; |
||||
else if(std::max(-b + Sqrt(des), -b - Sqrt(des)) > 0.0) |
||||
return SADDLE; |
||||
else |
||||
return SKNOT; |
||||
} |
||||
else |
||||
{ |
||||
bool acyclcrit = (av * y + cv > 0.0); |
||||
if(b < 0.0) |
||||
return acyclcrit ? SACICFOCUS : SCICFOCUS; |
||||
else |
||||
return acyclcrit ? UACICFOCUS : UCICFOCUS; |
||||
} |
||||
}; |
||||
|
||||
if(x1 >= 0.0 && x1 < 1.0 && y1 >= 0.0 && y1 < 1.0) points.emplace_back(LonR(x1 + ix, y1 + iy), LatR(x1 + ix, y1 + iy), PointType(x1, y1)); |
||||
if(x2 >= 0.0 && x2 < 1.0 && y2 >= 0.0 && y2 < 1.0) points.emplace_back(LonR(x2 + ix, y2 + iy), LatR(x2 + ix, y2 + iy), PointType(x2, y2)); |
||||
return points; |
||||
} |
||||
}; |
@ -0,0 +1,169 @@
|
||||
#define MICHLIB_NOSOURCE |
||||
#include "GPL.h" |
||||
#include "odm.h" |
||||
|
||||
MString Data::ActionUV(const CLArgs& args) |
||||
{ |
||||
if(args.contains("time") && (args.contains("timeb") || args.contains("timee"))) return "Time must be set via time parameter or timeb and timee parameter but not via both"; |
||||
if(!(args.contains("time") || (args.contains("timeb") && args.contains("timee")))) return "Time must be set via time parameter or timeb and timee parameter"; |
||||
|
||||
michlib_internal::ParameterListEx pars; |
||||
pars.UsePrefix(""); |
||||
|
||||
pars.SetParameter("source", args.at("source")); |
||||
TIndex tindexes; |
||||
|
||||
if(args.contains("time")) |
||||
{ |
||||
tindexes = GetTIndexes(args.at("time")); // Regular expression or single date
|
||||
if(tindexes.size() == 0) return "There are no times matching the regular expression"; |
||||
if(tindexes.size() == 1) |
||||
pars.SetParameter("time", Time(tindexes[0]).ToString()); |
||||
else |
||||
pars.SetParameter("timeregex", args.at("time")); |
||||
} |
||||
else |
||||
{ |
||||
MDateTime tb(args.at("timeb")), te(args.at("timee")); |
||||
tindexes = GetTIndexes(tb, te); |
||||
if(tindexes.size() == 0) return "There are no times between " + tb.ToString() + " and " + te.ToString(); |
||||
pars.SetParameter("timeb", tb.ToString()); |
||||
pars.SetParameter("timee", te.ToString()); |
||||
} |
||||
|
||||
std::unique_ptr<const BaseParameters> sourcepars; |
||||
{ |
||||
auto ppar = std::visit( |
||||
[&pars = pars, &args = args](const auto& arg) -> std::pair<const BaseParameters*, MString> |
||||
{ |
||||
using T = std::decay_t<decltype(arg)>; |
||||
if constexpr(ParametersSupported<T>) |
||||
return arg.Parameters(pars, args); |
||||
else |
||||
return {nullptr, ""}; |
||||
}, |
||||
*this); |
||||
if(ppar.second.Exist()) return ppar.second; |
||||
sourcepars.reset(ppar.first); |
||||
} |
||||
|
||||
auto p = sourcepars.get(); |
||||
size_t ind; |
||||
auto read = [p = p, &ind = ind](const auto& arg) -> UVData |
||||
{ |
||||
using T = std::decay_t<decltype(arg)>; |
||||
if constexpr(ReadUVPSupported<T>) |
||||
return arg.ReadUV(p, ind); |
||||
else |
||||
return UVData(); |
||||
}; |
||||
|
||||
UVData data; |
||||
if(tindexes.size() == 1) |
||||
{ |
||||
ind = tindexes[0]; |
||||
michlib::message("Time: " + Time(ind).ToTString()); |
||||
data = std::visit(read, *this); |
||||
} |
||||
else |
||||
{ |
||||
Averager<UVData> out; |
||||
bool ok = true; |
||||
for(size_t i = 0; i < tindexes.size(); i++) |
||||
{ |
||||
if(!ok) break; |
||||
ind = tindexes[i]; |
||||
michlib::message("Time: " + Time(ind).ToTString()); |
||||
auto dat = std::visit(read, *this); |
||||
if(dat) |
||||
out.Add(dat); |
||||
else |
||||
ok = false; |
||||
} |
||||
if(ok) data = out.Div(); |
||||
} |
||||
if(!data) return "Can't read data"; |
||||
|
||||
// Main file
|
||||
MString name = args.contains("out") ? args.at("out") : ""; |
||||
if(name.Exist()) |
||||
{ |
||||
BFileW fw; |
||||
fw.Create(name, 7); |
||||
fw.SetColumnName(1, "Longitude"); |
||||
fw.SetColumnName(2, "Latitude"); |
||||
fw.SetColumnName(3, "u, cm/s"); |
||||
fw.SetColumnName(4, "v, cm/s"); |
||||
fw.SetColumnName(5, "Div, (cm/s)/km"); |
||||
fw.SetColumnName(6, "Rot, (cm/s)/km"); |
||||
fw.SetColumnName(7, "Okubo-Weiss parameter, (cm^2/s^2)/km^2"); |
||||
fw.SetParameters(pars); |
||||
for(size_t i = 0; i < data.N(); i++) |
||||
{ |
||||
fw.Write(data.Lon(i)); |
||||
fw.Write(data.Lat(i)); |
||||
fw.Write(data.U(i) == data.Fillval() ? NAN : data.U(i)); |
||||
fw.Write(data.V(i) == data.Fillval() ? NAN : data.V(i)); |
||||
fw.Write(data.Div(i) == data.Fillval() ? NAN : data.Div(i)); |
||||
fw.Write(data.Rot(i) == data.Fillval() ? NAN : data.Rot(i)); |
||||
fw.Write(data.OW(i) == data.Fillval() ? NAN : data.OW(i)); |
||||
} |
||||
fw.Finalize(); |
||||
fw.Close(); |
||||
} |
||||
|
||||
// Filtered vectors file
|
||||
name = args.contains("velout") ? args.at("velout") : ""; |
||||
if(name.Exist()) |
||||
{ |
||||
size_t shiftx = args.contains("shiftx") ? args.at("shiftx").ToInteger<size_t>() : 0; |
||||
size_t shifty = args.contains("shifty") ? args.at("shifty").ToInteger<size_t>() : 0; |
||||
size_t skipx = args.contains("skipx") ? args.at("skipx").ToInteger<size_t>() : 1; |
||||
size_t skipy = args.contains("skipy") ? args.at("skipy").ToInteger<size_t>() : 1; |
||||
|
||||
BFileW vel; |
||||
vel.Create(name, 4); |
||||
vel.SetColumnName(1, "Longitude"); |
||||
vel.SetColumnName(2, "Latitude"); |
||||
vel.SetColumnName(3, "u, cm/s"); |
||||
vel.SetColumnName(4, "v, cm/s"); |
||||
|
||||
for(size_t ix = shiftx; ix < data.Nx(); ix += skipx) |
||||
for(size_t iy = shifty; iy < data.Ny(); iy += skipy) |
||||
{ |
||||
vel.Write(data.Lon(ix, iy)); |
||||
vel.Write(data.Lat(ix, iy)); |
||||
vel.Write(data.U(ix, iy) == data.Fillval() ? NAN : data.U(ix, iy)); |
||||
vel.Write(data.V(ix, iy) == data.Fillval() ? NAN : data.V(ix, iy)); |
||||
} |
||||
vel.Finalize(); |
||||
vel.Close(); |
||||
} |
||||
|
||||
// Stationary points
|
||||
name = args.contains("stpout") ? args.at("stpout") : ""; |
||||
if(name.Exist()) |
||||
{ |
||||
BFileW stp; |
||||
stp.Create(name, 3); |
||||
stp.SetColumnName(1, "Longitude"); |
||||
stp.SetColumnName(2, "Latitude"); |
||||
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)"); |
||||
|
||||
for(size_t ix = 0; ix < data.Nx() - 1; ix++) |
||||
for(size_t iy = 0; iy < data.Ny() - 1; iy++) |
||||
{ |
||||
auto points = data.StablePoints(ix, iy); |
||||
for(size_t i = 0; i < points.size(); i++) |
||||
{ |
||||
stp.Write(points[i].x); |
||||
stp.Write(points[i].y); |
||||
stp.Write(michlib::int_cast<size_t>(points[i].type)); |
||||
} |
||||
} |
||||
stp.Finalize(); |
||||
stp.Close(); |
||||
} |
||||
|
||||
return ""; |
||||
} |
Loading…
Reference in new issue