ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
obs_nudging Namespace Reference

Classes

struct  ObsNudgingView
 
struct  StationRecord
 
struct  StationSeries
 
struct  Profile
 

Enumerations

enum  Comp : int {
  U = 0 , V = 1 , W = 2 , Theta = 3 ,
  NComp = 4
}
 

Functions

template<int Dir, bool HasZnd>
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void nudged_point (int i, int j, int k, const Array4< const Real > &cons, const Array4< const Real > &znd, const Array4< const Real > &zs, const GpuArray< Real, AMREX_SPACEDIM > &problo, const GpuArray< Real, AMREX_SPACEDIM > &dx, Real &x, Real &y, Real &z, Real &zg, Real &rho) noexcept
 
template<int Dir, bool HasZnd>
void add_face_source (const Box &bx, const Array4< Real > &src, const Array4< const Real > &vel, const Array4< const Real > &cons, const Array4< const Real > &znd, const Array4< const Real > &zs, const Array4< const Real > &blank, bool has_blank, const GpuArray< Real, AMREX_SPACEDIM > problo, const GpuArray< Real, AMREX_SPACEDIM > dx, int klo, int khi, const ObsNudgingView v, int comp)
 
template<bool HasZnd>
void add_cell_theta_source (const Box &bx, const Array4< Real > &src, const Array4< const Real > &cons, const Array4< const Real > &znd, const Array4< const Real > &zs, const Array4< const Real > &blank, bool has_blank, const GpuArray< Real, AMREX_SPACEDIM > problo, const GpuArray< Real, AMREX_SPACEDIM > dx, const ObsNudgingView v)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real periodic_separation (amrex::Real d, amrex::Real L) noexcept
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real obs_nudging_tendency (const ObsNudgingView &v, int comp, amrex::Real x, amrex::Real y, amrex::Real z, amrex::Real z_ground, amrex::Real phi) noexcept
 
const char * comp_name (int comp)
 
bool parse_station_series (std::istream &is, const std::string &source, Real missing_value, StationSeries &series, std::string &error)
 
bool profiles_at_time (const StationSeries &series, double t, Real cos_alpha, Real sin_alpha, std::array< Profile, NComp > &profiles)
 

Enumeration Type Documentation

◆ Comp

enum obs_nudging::Comp : int
Enumerator
U 
V 
W 
Theta 
NComp 
36 : int { U = 0, V = 1, W = 2, Theta = 3, NComp = 4 };
@ NComp
Definition: ERF_ObsNudgingSeries.H:36
@ U
Definition: ERF_ObsNudgingSeries.H:36
@ V
Definition: ERF_ObsNudgingSeries.H:36
@ W
Definition: ERF_ObsNudgingSeries.H:36
@ Theta
Definition: ERF_ObsNudgingSeries.H:36

Function Documentation

◆ add_cell_theta_source()

template<bool HasZnd>
void obs_nudging::add_cell_theta_source ( const Box &  bx,
const Array4< Real > &  src,
const Array4< const Real > &  cons,
const Array4< const Real > &  znd,
const Array4< const Real > &  zs,
const Array4< const Real > &  blank,
bool  has_blank,
const GpuArray< Real, AMREX_SPACEDIM >  problo,
const GpuArray< Real, AMREX_SPACEDIM >  dx,
const ObsNudgingView  v 
)
125 {
126  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
127  {
128  Real x, y, z, zg, rho;
129  nudged_point<-1, HasZnd>(i, j, k, cons, znd, zs, problo, dx, x, y, z, zg, rho);
130 
131  const Real theta = cons(i,j,k,RhoTheta_comp) / rho;
132 
133  Real tend = obs_nudging_tendency(v, Theta, x, y, z, zg, theta);
134  if (has_blank) { tend *= (Real(1.0) - blank(i,j,k)); }
135  src(i,j,k,RhoTheta_comp) += rho * tend;
136  });
137 }
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:36
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ Theta
Definition: ERF_IndexDefines.H:199
@ rho
Definition: ERF_Kessler.H:25
@ theta
Definition: ERF_Kessler.H:26
@ cons
Definition: ERF_IndexDefines.H:217
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void nudged_point(int i, int j, int k, const Array4< const Real > &cons, const Array4< const Real > &znd, const Array4< const Real > &zs, const GpuArray< Real, AMREX_SPACEDIM > &problo, const GpuArray< Real, AMREX_SPACEDIM > &dx, Real &x, Real &y, Real &z, Real &zg, Real &rho) noexcept
Definition: ERF_ObsNudging.cpp:37
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real obs_nudging_tendency(const ObsNudgingView &v, int comp, amrex::Real x, amrex::Real y, amrex::Real z, amrex::Real z_ground, amrex::Real phi) noexcept
Definition: ERF_ObsNudgingKernel.H:93
Here is the call graph for this function:

◆ add_face_source()

template<int Dir, bool HasZnd>
void obs_nudging::add_face_source ( const Box &  bx,
const Array4< Real > &  src,
const Array4< const Real > &  vel,
const Array4< const Real > &  cons,
const Array4< const Real > &  znd,
const Array4< const Real > &  zs,
const Array4< const Real > &  blank,
bool  has_blank,
const GpuArray< Real, AMREX_SPACEDIM >  problo,
const GpuArray< Real, AMREX_SPACEDIM >  dx,
int  klo,
int  khi,
const ObsNudgingView  v,
int  comp 
)
100 {
101  // w on the bottom and top of the domain is set by the boundary conditions
102  const bool skip_walls = (Dir == 2);
103 
104  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
105  {
106  if (skip_walls && (k <= klo || k > khi)) { return; }
107 
108  Real x, y, z, zg, rho;
109  nudged_point<Dir, HasZnd>(i, j, k, cons, znd, zs, problo, dx, x, y, z, zg, rho);
110 
111  Real tend = obs_nudging_tendency(v, comp, x, y, z, zg, vel(i,j,k));
112  if (has_blank) { tend *= (Real(1.0) - blank(i,j,k)); }
113  src(i,j,k) += rho * tend;
114  });
115 }
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
Here is the call graph for this function:

◆ comp_name()

const char * obs_nudging::comp_name ( int  comp)
20 {
21  switch (comp) {
22  case U: return "u";
23  case V: return "v";
24  case W: return "w";
25  case Theta: return "theta";
26  default: return "?";
27  }
28 }
@ U
Definition: ERF_IndexDefines.H:129
@ V
Definition: ERF_IndexDefines.H:130

◆ nudged_point()

template<int Dir, bool HasZnd>
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void obs_nudging::nudged_point ( int  i,
int  j,
int  k,
const Array4< const Real > &  cons,
const Array4< const Real > &  znd,
const Array4< const Real > &  zs,
const GpuArray< Real, AMREX_SPACEDIM > &  problo,
const GpuArray< Real, AMREX_SPACEDIM > &  dx,
Real &  x,
Real &  y,
Real &  z,
Real &  zg,
Real &  rho 
)
noexcept
43 {
44  if constexpr (Dir == 0) {
45  x = problo[0] + Real(i) * dx[0];
46  y = problo[1] + (Real(j) + Real(0.5)) * dx[1];
47  zg = Real(0.5) * (zs(i,j,0) + zs(i,j+1,0));
48  rho = Real(0.5) * (cons(i-1,j,k,Rho_comp) + cons(i,j,k,Rho_comp));
49  if constexpr (HasZnd) {
50  z = Real(0.25) * (znd(i,j,k) + znd(i,j+1,k) + znd(i,j,k+1) + znd(i,j+1,k+1));
51  } else {
52  z = problo[2] + (Real(k) + Real(0.5)) * dx[2];
53  }
54  } else if constexpr (Dir == 1) {
55  x = problo[0] + (Real(i) + Real(0.5)) * dx[0];
56  y = problo[1] + Real(j) * dx[1];
57  zg = Real(0.5) * (zs(i,j,0) + zs(i+1,j,0));
58  rho = Real(0.5) * (cons(i,j-1,k,Rho_comp) + cons(i,j,k,Rho_comp));
59  if constexpr (HasZnd) {
60  z = Real(0.25) * (znd(i,j,k) + znd(i+1,j,k) + znd(i,j,k+1) + znd(i+1,j,k+1));
61  } else {
62  z = problo[2] + (Real(k) + Real(0.5)) * dx[2];
63  }
64  } else if constexpr (Dir == 2) {
65  x = problo[0] + (Real(i) + Real(0.5)) * dx[0];
66  y = problo[1] + (Real(j) + Real(0.5)) * dx[1];
67  zg = Real(0.25) * (zs(i,j,0) + zs(i+1,j,0) + zs(i,j+1,0) + zs(i+1,j+1,0));
68  rho = Real(0.5) * (cons(i,j,k-1,Rho_comp) + cons(i,j,k,Rho_comp));
69  if constexpr (HasZnd) {
70  z = Real(0.25) * (znd(i,j,k) + znd(i+1,j,k) + znd(i,j+1,k) + znd(i+1,j+1,k));
71  } else {
72  z = problo[2] + Real(k) * dx[2];
73  }
74  } else {
75  x = problo[0] + (Real(i) + Real(0.5)) * dx[0];
76  y = problo[1] + (Real(j) + Real(0.5)) * dx[1];
77  zg = Real(0.25) * (zs(i,j,0) + zs(i+1,j,0) + zs(i,j+1,0) + zs(i+1,j+1,0));
78  rho = cons(i,j,k,Rho_comp);
79  if constexpr (HasZnd) {
80  z = Real(0.125) * (znd(i,j,k ) + znd(i+1,j,k ) + znd(i,j+1,k ) + znd(i+1,j+1,k ) +
81  znd(i,j,k+1) + znd(i+1,j,k+1) + znd(i,j+1,k+1) + znd(i+1,j+1,k+1));
82  } else {
83  z = problo[2] + (Real(k) + Real(0.5)) * dx[2];
84  }
85  }
86 }
#define Rho_comp
Definition: ERF_IndexDefines.H:42

Referenced by add_cell_theta_source().

Here is the caller graph for this function:

◆ obs_nudging_tendency()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real obs_nudging::obs_nudging_tendency ( const ObsNudgingView &  v,
int  comp,
amrex::Real  x,
amrex::Real  y,
amrex::Real  z,
amrex::Real  z_ground,
amrex::Real  phi 
)
noexcept
96 {
97  amrex::Real sum_w = amrex::Real(0.0);
98  amrex::Real sum_wt = amrex::Real(0.0);
99 
100  for (int s = 0; s < v.nstations; ++s)
101  {
102  const int idx = comp*v.nstations + s;
103  const int n = v.count[idx];
104  if (n == 0) { continue; }
105  const int o = v.offset[idx];
106 
107  const amrex::Real h = (v.agl[s] != 0) ? (z - z_ground) : z;
108  const amrex::Real zlo = v.z[o];
109  const amrex::Real zhi = v.z[o+n-1];
110  const amrex::Real d = (h < zlo) ? (zlo - h) : ((h > zhi) ? (h - zhi) : amrex::Real(0.0));
111  const amrex::Real dxs = periodic_separation(x - v.x[s], v.period_x);
112  const amrex::Real dys = periodic_separation(y - v.y[s], v.period_y);
113  const amrex::Real q = (dxs*dxs + dys*dys) * v.inv_rh2 + d*d * v.inv_rz2;
114  if (q > v.qmax) { continue; }
115  const amrex::Real w = std::exp(amrex::Real(-0.25) * q);
116 
117  amrex::Real mean, sig;
118  if (h <= zlo) {
119  mean = v.mean[o];
120  sig = v.sigma[o];
121  } else if (h >= zhi) {
122  mean = v.mean[o+n-1];
123  sig = v.sigma[o+n-1];
124  } else {
125  int k = o;
126  while (v.z[k+1] < h) { ++k; }
127  const amrex::Real f = (h - v.z[k]) / (v.z[k+1] - v.z[k]);
128  mean = (amrex::Real(1.0) - f) * v.mean[k] + f * v.mean[k+1];
129  sig = (amrex::Real(1.0) - f) * v.sigma[k] + f * v.sigma[k+1];
130  }
131 
132  const amrex::Real band = v.sigma_factor * sig;
133  const amrex::Real target = amrex::min(amrex::max(phi, mean - band), mean + band);
134 
135  sum_w += w;
136  sum_wt += w * target;
137  }
138 
139  if (sum_w <= amrex::Real(0.0)) { return amrex::Real(0.0); }
140 
141  const amrex::Real tbar = sum_wt / sum_w;
142  const amrex::Real rate = amrex::min(amrex::min(sum_w, amrex::Real(1.0)) * v.inv_tau, v.max_rate);
143  return -rate * (phi - tbar);
144 }
if(KE_decay_height > 0 &&state_pert.nComp() > RhoKE_comp)
Definition: ERF_InitCustomPert_ABL.H:76
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:396
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
@ q
Definition: ERF_WSM6.H:273
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real periodic_separation(amrex::Real d, amrex::Real L) noexcept
Definition: ERF_ObsNudgingKernel.H:56

Referenced by add_cell_theta_source(), and add_face_source().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_station_series()

bool obs_nudging::parse_station_series ( std::istream &  is,
const std::string &  source,
Real  missing_value,
StationSeries &  series,
std::string &  error 
)
70 {
71  series = StationSeries{};
72 
73  int col_of[NColumns];
74  std::fill(col_of, col_of + NColumns, -1);
75  int ncols = 0;
76 
77  const auto is_missing = [missing_value] (double v) {
78  return !std::isfinite(v) ||
79  std::abs(v - static_cast<double>(missing_value)) <=
80  1.0e-6 * std::max(1.0, std::abs(static_cast<double>(missing_value)));
81  };
82 
83  std::string line;
84  int line_no = 0;
85  bool have_header = false;
86  int nheights = 0; // heights per record, fixed by the first record
87  int ih = 0; // height index within the current record
88 
89  while (std::getline(is, line))
90  {
91  ++line_no;
92  if (!strip_comment(line)) { continue; }
93 
94  std::istringstream ls(line);
95  std::vector<std::string> tokens;
96  std::string tok;
97  while (ls >> tok) { tokens.push_back(tok); }
98 
99  if (!have_header)
100  {
101  for (auto& t : tokens) {
102  std::transform(t.begin(), t.end(), t.begin(),
103  [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
104  int found = -1;
105  for (int c = 0; c < NColumns; ++c) {
106  if (t == column_names[c]) { found = c; break; }
107  }
108  if (found < 0) {
109  error = where(source, line_no) + "unknown column '" + t + "'; the columns are "
110  "time z u v w theta su sv sw stheta speed direction";
111  return false;
112  }
113  if (col_of[found] >= 0) {
114  error = where(source, line_no) + "column '" + t + "' is named twice";
115  return false;
116  }
117  col_of[found] = ncols++;
118  }
119 
120  const auto has = [&](int c) { return col_of[c] >= 0; };
121  if (!has(ColTime) || !has(ColZ)) {
122  error = where(source, line_no) + "the header must name the columns time and z";
123  return false;
124  }
125  if (has(ColU) != has(ColV)) {
126  error = where(source, line_no) + "u and v must be given together";
127  return false;
128  }
129  if (has(ColSpeed) != has(ColDirection)) {
130  error = where(source, line_no) + "speed and direction must be given together";
131  return false;
132  }
133  if (has(ColU) && has(ColSpeed)) {
134  error = where(source, line_no) + "give the wind as u v or as speed direction, not both";
135  return false;
136  }
137  const bool has_wind = has(ColU) || has(ColSpeed);
138  if ((has(ColSU) || has(ColSV)) && !has_wind) {
139  error = where(source, line_no) + "su/sv need a wind column";
140  return false;
141  }
142  if (has(ColSW) && !has(ColW)) {
143  error = where(source, line_no) + "sw needs the column w";
144  return false;
145  }
146  if (has(ColSTheta) && !has(ColTheta)) {
147  error = where(source, line_no) + "stheta needs the column theta";
148  return false;
149  }
150  if (!has_wind && !has(ColW) && !has(ColTheta)) {
151  error = where(source, line_no) + "the file has none of the nudged quantities "
152  "(u v, speed direction, w, theta)";
153  return false;
154  }
155 
156  series.has[U] = has_wind;
157  series.has[V] = has_wind;
158  series.has[W] = has(ColW);
159  series.has[Theta] = has(ColTheta);
160  have_header = true;
161  continue;
162  }
163 
164  if (static_cast<int>(tokens.size()) != ncols) {
165  error = where(source, line_no) + "expected " + std::to_string(ncols) +
166  " entries, found " + std::to_string(tokens.size());
167  return false;
168  }
169 
170  double v[NColumns];
171  std::fill(v, v + NColumns, std::numeric_limits<double>::quiet_NaN());
172  for (int c = 0; c < NColumns; ++c) {
173  if (col_of[c] < 0) { continue; }
174  if (!parse_number(tokens[col_of[c]], v[c])) {
175  error = where(source, line_no) + "'" + tokens[col_of[c]] + "' in column " +
176  column_names[c] + " is not a number";
177  return false;
178  }
179  }
180 
181  if (is_missing(v[ColTime]) || is_missing(v[ColZ])) {
182  error = where(source, line_no) + "time and z may not be missing";
183  return false;
184  }
185 
186  // A new time starts a new record
187  const double time = v[ColTime];
188  const Real z = static_cast<Real>(v[ColZ]);
189  if (series.records.empty() || time != series.records.back().time)
190  {
191  if (!series.records.empty()) {
192  if (time < series.records.back().time) {
193  error = where(source, line_no) + "the times must increase down the file";
194  return false;
195  }
196  if (ih != nheights) {
197  error = where(source, line_no) + "the record at time " +
198  std::to_string(series.records.back().time) + " has " +
199  std::to_string(ih) + " heights, but the first record has " +
200  std::to_string(nheights) + "; every time needs the same heights";
201  return false;
202  }
203  }
204  StationRecord rec;
205  rec.time = time;
206  series.records.push_back(rec);
207  ih = 0;
208  }
209 
210  StationRecord& rec = series.records.back();
211 
212  if (series.records.size() == 1) {
213  if (!series.heights.empty() && z <= series.heights.back()) {
214  error = where(source, line_no) + "the heights within a time must increase";
215  return false;
216  }
217  series.heights.push_back(z);
218  nheights = static_cast<int>(series.heights.size());
219  } else if (ih >= nheights || z != series.heights[ih]) {
220  error = where(source, line_no) + "height " + std::to_string(z) + " at time " +
221  std::to_string(time) + " does not match the heights of the first time; "
222  "every time needs the same heights, in the same order";
223  return false;
224  }
225 
226  // The measured values of each component, NaN where missing
227  const Real nan = std::numeric_limits<Real>::quiet_NaN();
228  Real mean[NComp] = {nan, nan, nan, nan};
229  Real sigma[NComp] = {Real(0.0), Real(0.0), Real(0.0), Real(0.0)};
230 
231  if (col_of[ColU] >= 0 && !is_missing(v[ColU]) && !is_missing(v[ColV])) {
232  mean[U] = static_cast<Real>(v[ColU]);
233  mean[V] = static_cast<Real>(v[ColV]);
234  }
235  if (col_of[ColSpeed] >= 0 && !is_missing(v[ColSpeed]) && !is_missing(v[ColDirection])) {
236  const double speed = v[ColSpeed];
237  const double dir = v[ColDirection];
238  if (speed < 0.0) {
239  error = where(source, line_no) + "the wind speed may not be negative";
240  return false;
241  }
242  if (dir < 0.0 || dir > 360.0) {
243  error = where(source, line_no) + "the wind direction must be in [0, 360] degrees";
244  return false;
245  }
246  // Meteorological convention: the direction the wind blows from,
247  // clockwise from north
248  const double rad = dir * static_cast<double>(PI) / 180.0;
249  mean[U] = static_cast<Real>(-speed * std::sin(rad));
250  mean[V] = static_cast<Real>(-speed * std::cos(rad));
251  }
252  if (col_of[ColW] >= 0 && !is_missing(v[ColW])) {
253  mean[W] = static_cast<Real>(v[ColW]);
254  }
255  if (col_of[ColTheta] >= 0 && !is_missing(v[ColTheta])) {
256  if (v[ColTheta] <= 0.0) {
257  error = where(source, line_no) + "theta must be positive (it is in kelvin)";
258  return false;
259  }
260  mean[Theta] = static_cast<Real>(v[ColTheta]);
261  }
262 
263  const int scol[NComp] = {ColSU, ColSV, ColSW, ColSTheta};
264  for (int c = 0; c < NComp; ++c) {
265  if (col_of[scol[c]] < 0 || is_missing(v[scol[c]])) { continue; }
266  if (v[scol[c]] < 0.0) {
267  error = where(source, line_no) + "the standard deviation " +
268  column_names[scol[c]] + " may not be negative";
269  return false;
270  }
271  sigma[c] = static_cast<Real>(v[scol[c]]);
272  }
273 
274  for (int c = 0; c < NComp; ++c) {
275  rec.mean[c].push_back(mean[c]);
276  rec.sigma[c].push_back(sigma[c]);
277  }
278  ++ih;
279  }
280 
281  if (!have_header) {
282  error = source + ": the file has no header line naming its columns";
283  return false;
284  }
285  if (series.records.empty()) {
286  error = source + ": the file has a header but no measurements";
287  return false;
288  }
289  if (ih != nheights) {
290  error = source + ": the last record has " + std::to_string(ih) +
291  " heights, but the first record has " + std::to_string(nheights) +
292  "; every time needs the same heights";
293  return false;
294  }
295  return true;
296 }
amrex::Real sigma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:11
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
@ t
Definition: ERF_WSM6.H:272
void fill(MultiFab &dst, int temperature_comp, int mixing_ratio_comp, int source_comp, const Sources &sources, Real missing_value)
Definition: ERF_NearSurfaceDiagnostics.cpp:41
Here is the call graph for this function:

◆ periodic_separation()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real obs_nudging::periodic_separation ( amrex::Real  d,
amrex::Real  L 
)
noexcept
57 {
58  if (L > amrex::Real(0.0)) {
59  d -= L * amrex::Math::round(d / L);
60  }
61  return d;
62 }

Referenced by obs_nudging_tendency().

Here is the caller graph for this function:

◆ profiles_at_time()

bool obs_nudging::profiles_at_time ( const StationSeries &  series,
double  t,
Real  cos_alpha,
Real  sin_alpha,
std::array< Profile, NComp > &  profiles 
)
302 {
303  for (auto& p : profiles) { p.clear(); }
304 
305  const auto& recs = series.records;
306  const int nrec = static_cast<int>(recs.size());
307  if (nrec == 0) { return false; }
308 
309  // The two records bracketing t and the weight of the later one
310  int n0 = 0, n1 = 0;
311  double f = 0.0;
312  if (nrec > 1) {
313  if (t < recs.front().time || t > recs.back().time) { return false; }
314  while (n0 + 1 < nrec - 1 && recs[n0+1].time <= t) { ++n0; }
315  n1 = n0 + 1;
316  f = (t - recs[n0].time) / (recs[n1].time - recs[n0].time);
317  }
318 
319  const int nh = static_cast<int>(series.heights.size());
320  const Real nan = std::numeric_limits<Real>::quiet_NaN();
321 
322  // The value of component c at height h at time t, NaN when missing
323  auto at_time = [&](int c, int h, Real& mean, Real& sigma) {
324  const Real m0 = recs[n0].mean[c][h];
325  const Real m1 = recs[n1].mean[c][h];
326  const Real s0 = recs[n0].sigma[c][h];
327  const Real s1 = recs[n1].sigma[c][h];
328  if (f <= 0.0) {
329  mean = m0; sigma = s0;
330  } else if (f >= 1.0) {
331  mean = m1; sigma = s1;
332  } else if (std::isfinite(m0) && std::isfinite(m1)) {
333  const Real w1 = static_cast<Real>(f);
334  const Real w0 = Real(1.0) - w1;
335  mean = w0*m0 + w1*m1;
336  sigma = w0*s0 + w1*s1;
337  } else {
338  mean = nan; sigma = Real(0.0);
339  }
340  };
341 
342  const bool rotate = (cos_alpha != Real(1.0) || sin_alpha != Real(0.0));
343 
344  for (int h = 0; h < nh; ++h)
345  {
346  Real mean[NComp], sigma[NComp];
347  for (int c = 0; c < NComp; ++c) { at_time(c, h, mean[c], sigma[c]); }
348 
349  if (rotate) {
350  if (std::isfinite(mean[U]) && std::isfinite(mean[V])) {
351  const Real ug = cos_alpha*mean[U] + sin_alpha*mean[V];
352  const Real vg = -sin_alpha*mean[U] + cos_alpha*mean[V];
353  const Real su = std::sqrt(cos_alpha*cos_alpha*sigma[U]*sigma[U] +
355  const Real sv = std::sqrt(sin_alpha*sin_alpha*sigma[U]*sigma[U] +
357  mean[U] = ug; mean[V] = vg; sigma[U] = su; sigma[V] = sv;
358  } else {
359  mean[U] = nan; mean[V] = nan;
360  }
361  }
362 
363  for (int c = 0; c < NComp; ++c) {
364  if (!std::isfinite(mean[c])) { continue; }
365  profiles[c].z.push_back(series.heights[h]);
366  profiles[c].mean.push_back(mean[c]);
367  profiles[c].sigma.push_back(sigma[c]);
368  }
369  }
370 
371  return true;
372 }
Real sin_alpha
Definition: ERF_Plotfile2DInterpolator.cpp:28
Real cos_alpha
Definition: ERF_Plotfile2DInterpolator.cpp:27
@ p
Definition: ERF_WSM6.H:280

Referenced by ObsNudging::update_targets().

Here is the caller graph for this function: