ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ObsNudging Class Reference

#include <ERF_ObsNudging.H>

Collaboration diagram for ObsNudging:

Public Member Functions

 ObsNudging (TerrainType terrain_type, ProblemBase *prob, bool use_datetime, double start_time)
 
bool wants_latlon () const
 
void resolve_positions (const amrex::Geometry &geom0, const LatLonMap *latlon)
 
void print_summary () const
 
void prepare_level (int lev, const amrex::Geometry &geom, const amrex::MultiFab &cons, const amrex::MultiFab *z_phys_nd, double time)
 
void add_theta_source (int lev, double time, amrex::Real dt, const amrex::Geometry &geom, const amrex::MultiFab &cons, amrex::MultiFab &cc_src, const amrex::MultiFab *z_phys_nd, const amrex::MultiFab *blank)
 
void add_momentum_sources (int lev, double time, amrex::Real dt, const amrex::Geometry &geom, const amrex::MultiFab &cons, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &zvel, amrex::MultiFab &xmom_src, amrex::MultiFab &ymom_src, amrex::MultiFab &zmom_src, const amrex::MultiFab *z_phys_nd, const amrex::MultiFab *blank_x, const amrex::MultiFab *blank_y, const amrex::MultiFab *blank_z)
 

Private Member Functions

void update_targets (double time)
 
const amrex::MultiFab & surface (int lev, const amrex::Geometry &geom, const amrex::MultiFab &cons, const amrex::MultiFab *z_phys_nd, double time)
 
obs_nudging::ObsNudgingView view (amrex::Real dt, const amrex::Geometry &geom) const
 

Private Attributes

amrex::Vector< ObsNudgingStation > m_stations
 
TerrainType m_terrain_type = TerrainType::None
 
ProblemBase * m_prob = nullptr
 
amrex::Real m_tau = amrex::Real(-1.0)
 
amrex::Real m_horizontal_radius = amrex::Real(500.0)
 
amrex::Real m_vertical_radius = amrex::Real(25.0)
 
amrex::Real m_cutoff = amrex::Real(6.0)
 
amrex::Real m_sigma_factor = amrex::Real(1.0)
 
amrex::Real m_missing_value = amrex::Real(-9999.0)
 
bool m_nudge_wind = true
 
bool m_nudge_w = true
 
bool m_nudge_theta = true
 
double m_time_origin = 0.0
 
bool m_have_targets = false
 
double m_targets_time = 0.0
 
amrex::Gpu::DeviceVector< amrex::Real > m_d_x
 
amrex::Gpu::DeviceVector< amrex::Real > m_d_y
 
amrex::Gpu::DeviceVector< amrex::Real > m_d_z
 
amrex::Gpu::DeviceVector< amrex::Real > m_d_mean
 
amrex::Gpu::DeviceVector< amrex::Real > m_d_sigma
 
amrex::Gpu::DeviceVector< int > m_d_agl
 
amrex::Gpu::DeviceVector< int > m_d_offset
 
amrex::Gpu::DeviceVector< int > m_d_count
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_zsurf
 
amrex::Vector< amrex::BoxArray > m_zsurf_ba
 
amrex::Vector< amrex::DistributionMapping > m_zsurf_dm
 
amrex::Vector< double > m_zsurf_time
 

Constructor & Destructor Documentation

◆ ObsNudging()

ObsNudging::ObsNudging ( TerrainType  terrain_type,
ProblemBase *  prob,
bool  use_datetime,
double  start_time 
)
163  : m_terrain_type(terrain_type),
164  m_prob(prob)
165 {
166  // Also refused by SolverChoice as soon as the inputs are read
167  if (terrain_type == TerrainType::EB) {
168  Abort("erf.nudging_from_observations does not support erf.terrain_type = EB; "
169  "use a terrain-fitted mesh or immersed forcing");
170  }
171 
172  ParmParse pp("erf.obs_nudging");
173 
174  if (!pp.query("tau", m_tau)) {
175  Abort("erf.nudging_from_observations needs erf.obs_nudging.tau, the relaxation time "
176  "scale in seconds; there is no default");
177  }
178  if (!(m_tau > Real(0.0))) {
179  Abort("erf.obs_nudging.tau must be positive");
180  }
181 
182  pp.query("horizontal_radius", m_horizontal_radius);
183  pp.query("vertical_radius", m_vertical_radius);
184  pp.query("cutoff", m_cutoff);
185  pp.query("sigma_factor", m_sigma_factor);
186  pp.query("missing_value", m_missing_value);
187  pp.query("nudge_wind", m_nudge_wind);
188  pp.query("nudge_w", m_nudge_w);
189  pp.query("nudge_theta", m_nudge_theta);
190 
191  if (!(m_horizontal_radius > Real(0.0))) {
192  Abort("erf.obs_nudging.horizontal_radius must be positive");
193  }
194  if (!(m_vertical_radius > Real(0.0))) {
195  Abort("erf.obs_nudging.vertical_radius must be positive");
196  }
197  if (!(m_cutoff > Real(0.0))) {
198  Abort("erf.obs_nudging.cutoff must be positive");
199  }
200  if (!(m_sigma_factor >= Real(0.0))) {
201  Abort("erf.obs_nudging.sigma_factor must not be negative");
202  }
203  if (!m_nudge_wind && !m_nudge_w && !m_nudge_theta) {
204  Abort("erf.obs_nudging: nudge_wind, nudge_w and nudge_theta are all false, so "
205  "nothing would be nudged");
206  }
207 
208  std::string time_type = "elapsed";
209  pp.query("time_type", time_type);
210  if (time_type == "elapsed") {
211  m_time_origin = 0.0;
212  } else if (time_type == "epoch") {
213  if (!use_datetime) {
214  Abort("erf.obs_nudging.time_type = epoch needs the run to know its start date "
215  "(start_datetime, or a WRF or metgrid initialization)");
216  }
217  m_time_origin = start_time;
218  } else {
219  Abort("erf.obs_nudging.time_type must be elapsed or epoch, not '" + time_type + "'");
220  }
221 
222  const int nst = pp.countval("stations");
223  if (nst == 0) {
224  Abort("erf.nudging_from_observations needs erf.obs_nudging.stations, the names of "
225  "the stations");
226  }
227  Vector<std::string> names;
228  pp.getarr("stations", names);
229 
230  for (const auto& name : names)
231  {
232  for (const auto& st : m_stations) {
233  if (st.name == name) {
234  Abort("erf.obs_nudging.stations names '" + name + "' twice");
235  }
236  }
237 
239  st.name = name;
240 
241  ParmParse pps("erf.obs_nudging." + name);
242 
243  if (!pps.query("file", st.file)) {
244  Abort("erf.obs_nudging." + name + ".file is required");
245  }
246 
247  const bool has_lat = pps.contains("lat");
248  const bool has_lon = pps.contains("long") || pps.contains("lon");
249  const bool has_x = pps.contains("x");
250  const bool has_y = pps.contains("y");
251  if ((has_lat || has_lon) && (has_x || has_y)) {
252  Abort("erf.obs_nudging." + name + ": give either lat/long or x/y, not both");
253  }
254  if (has_lat || has_lon) {
255  if (!has_lat || !has_lon) {
256  Abort("erf.obs_nudging." + name + ": lat and long must both be given");
257  }
258  st.use_latlon = true;
259  pps.get("lat", st.req_lat);
260  if (!pps.query("long", st.req_lon)) { pps.get("lon", st.req_lon); }
261  if (std::abs(st.req_lat) > Real(90.0) || std::abs(st.req_lon) > Real(360.0)) {
262  Abort("erf.obs_nudging." + name + ": lat must be in [-90, 90] and long in "
263  "[-360, 360] degrees");
264  }
265  } else {
266  if (!has_x || !has_y) {
267  Abort("erf.obs_nudging." + name + ": give lat and long, or x and y in domain "
268  "coordinates");
269  }
270  pps.get("x", st.x);
271  pps.get("y", st.y);
272  }
273 
274  std::string frame = "earth";
275  pps.query("wind_frame", frame);
276  if (frame == "earth") {
277  st.earth_frame = true;
278  } else if (frame == "grid") {
279  st.earth_frame = false;
280  } else {
281  Abort("erf.obs_nudging." + name + ".wind_frame must be earth or grid, not '" + frame + "'");
282  }
283 
284  std::string height_ref = "agl";
285  pps.query("height_ref", height_ref);
286  if (height_ref == "agl") {
287  st.agl = true;
288  } else if (height_ref == "msl") {
289  st.agl = false;
290  } else {
291  Abort("erf.obs_nudging." + name + ".height_ref must be agl or msl, not '" +
292  height_ref + "'");
293  }
294 
295  read_station_file(st, m_missing_value);
296 
297  const auto& has = st.series.has;
298  const bool used = (m_nudge_wind && has[obs_nudging::U]) ||
299  (m_nudge_w && has[obs_nudging::W]) ||
301  if (!used) {
302  // A lidar without temperature in a run that nudges only theta, say:
303  // nothing to do at this station, which is worth saying but not fatal
304  Warning("erf.obs_nudging." + name + ": the file " + st.file + " has none of the "
305  "quantities this run nudges (see nudge_wind, nudge_w, nudge_theta), so the "
306  "station is not used");
307  continue;
308  }
309 
310  m_stations.push_back(std::move(st));
311  }
312 
313  if (m_stations.empty()) {
314  Abort("erf.obs_nudging: no station measures any of the quantities this run nudges "
315  "(see nudge_wind, nudge_w, nudge_theta)");
316  }
317 
318  m_d_x.resize(m_stations.size());
319  m_d_y.resize(m_stations.size());
320  m_d_agl.resize(m_stations.size());
321  m_d_offset.resize(obs_nudging::NComp * m_stations.size());
322  m_d_count.resize(obs_nudging::NComp * m_stations.size());
323 }
ParmParse pp("prob")
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:105
amrex::Real Real
Definition: ERF_ShocInterface.H:19
bool m_nudge_w
Definition: ERF_ObsNudging.H:142
amrex::Vector< ObsNudgingStation > m_stations
Definition: ERF_ObsNudging.H:129
amrex::Real m_sigma_factor
Definition: ERF_ObsNudging.H:139
amrex::Real m_horizontal_radius
Definition: ERF_ObsNudging.H:136
amrex::Real m_vertical_radius
Definition: ERF_ObsNudging.H:137
amrex::Gpu::DeviceVector< amrex::Real > m_d_y
Definition: ERF_ObsNudging.H:149
amrex::Gpu::DeviceVector< int > m_d_agl
Definition: ERF_ObsNudging.H:150
ProblemBase * m_prob
Definition: ERF_ObsNudging.H:132
amrex::Real m_tau
Definition: ERF_ObsNudging.H:135
bool m_nudge_theta
Definition: ERF_ObsNudging.H:143
amrex::Gpu::DeviceVector< int > m_d_offset
Definition: ERF_ObsNudging.H:150
amrex::Gpu::DeviceVector< int > m_d_count
Definition: ERF_ObsNudging.H:150
TerrainType m_terrain_type
Definition: ERF_ObsNudging.H:131
double m_time_origin
Definition: ERF_ObsNudging.H:144
amrex::Gpu::DeviceVector< amrex::Real > m_d_x
Definition: ERF_ObsNudging.H:149
bool m_nudge_wind
Definition: ERF_ObsNudging.H:141
amrex::Real m_missing_value
Definition: ERF_ObsNudging.H:140
amrex::Real m_cutoff
Definition: ERF_ObsNudging.H:138
@ NComp
Definition: ERF_ObsNudgingSeries.H:36
@ U
Definition: ERF_ObsNudgingSeries.H:36
@ W
Definition: ERF_ObsNudgingSeries.H:36
@ Theta
Definition: ERF_ObsNudgingSeries.H:36
Definition: ERF_ObsNudging.H:47
amrex::Real req_lat
Definition: ERF_ObsNudging.H:52
bool use_latlon
Definition: ERF_ObsNudging.H:51
amrex::Real x
Definition: ERF_ObsNudging.H:56
bool earth_frame
Definition: ERF_ObsNudging.H:63
amrex::Real req_lon
Definition: ERF_ObsNudging.H:53
std::string name
Definition: ERF_ObsNudging.H:48
bool agl
Definition: ERF_ObsNudging.H:64
obs_nudging::StationSeries series
Definition: ERF_ObsNudging.H:70
std::string file
Definition: ERF_ObsNudging.H:49
amrex::Real y
Definition: ERF_ObsNudging.H:57
std::array< bool, NComp > has
Definition: ERF_ObsNudgingSeries.H:54
Here is the call graph for this function:

Member Function Documentation

◆ add_momentum_sources()

void ObsNudging::add_momentum_sources ( int  lev,
double  time,
amrex::Real  dt,
const amrex::Geometry &  geom,
const amrex::MultiFab &  cons,
const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  zvel,
amrex::MultiFab &  xmom_src,
amrex::MultiFab &  ymom_src,
amrex::MultiFab &  zmom_src,
const amrex::MultiFab *  z_phys_nd,
const amrex::MultiFab *  blank_x,
const amrex::MultiFab *  blank_y,
const amrex::MultiFab *  blank_z 
)
610 {
611  if (!m_nudge_wind && !m_nudge_w) { return; }
612 
613  update_targets(time);
614  const obs_nudging::ObsNudgingView v = view(dt, geom);
615  const MultiFab& zs = surface(lev, geom, cons, z_phys_nd, time);
616 
617  const auto problo = geom.ProbLoArray();
618  const auto dx = geom.CellSizeArray();
619  const int klo = geom.Domain().smallEnd(2);
620  const int khi = geom.Domain().bigEnd(2);
621  const bool has_blank = (blank_x != nullptr);
622  AMREX_ALWAYS_ASSERT((blank_x != nullptr) == (blank_y != nullptr) &&
623  (blank_x != nullptr) == (blank_z != nullptr));
624 
625 #ifdef _OPENMP
626 #pragma omp parallel if (Gpu::notInLaunchRegion())
627 #endif
628  for (MFIter mfi(cons, TilingIfNotGPU()); mfi.isValid(); ++mfi)
629  {
630  const Array4<const Real>& c = cons.const_array(mfi);
631  const Array4<const Real>& s = zs.const_array(mfi);
632  const Array4<const Real> znd = z_phys_nd ? z_phys_nd->const_array(mfi) : Array4<const Real>{};
633  const Array4<const Real> bx_ = has_blank ? blank_x->const_array(mfi) : Array4<const Real>{};
634  const Array4<const Real> by_ = has_blank ? blank_y->const_array(mfi) : Array4<const Real>{};
635  const Array4<const Real> bz_ = has_blank ? blank_z->const_array(mfi) : Array4<const Real>{};
636 
637  if (m_nudge_wind) {
638  const Box tbx = mfi.nodaltilebox(0);
639  const Box tby = mfi.nodaltilebox(1);
640  if (z_phys_nd) {
641  obs_nudging::add_face_source<0,true>(tbx, xmom_src.array(mfi), xvel.const_array(mfi), c, znd,
642  s, bx_, has_blank, problo, dx, klo, khi, v, obs_nudging::U);
643  obs_nudging::add_face_source<1,true>(tby, ymom_src.array(mfi), yvel.const_array(mfi), c, znd,
644  s, by_, has_blank, problo, dx, klo, khi, v, obs_nudging::V);
645  } else {
646  obs_nudging::add_face_source<0,false>(tbx, xmom_src.array(mfi), xvel.const_array(mfi), c, znd,
647  s, bx_, has_blank, problo, dx, klo, khi, v, obs_nudging::U);
648  obs_nudging::add_face_source<1,false>(tby, ymom_src.array(mfi), yvel.const_array(mfi), c, znd,
649  s, by_, has_blank, problo, dx, klo, khi, v, obs_nudging::V);
650  }
651  }
652  if (m_nudge_w) {
653  const Box tbz = mfi.nodaltilebox(2);
654  if (z_phys_nd) {
655  obs_nudging::add_face_source<2,true>(tbz, zmom_src.array(mfi), zvel.const_array(mfi), c, znd,
656  s, bz_, has_blank, problo, dx, klo, khi, v, obs_nudging::W);
657  } else {
658  obs_nudging::add_face_source<2,false>(tbz, zmom_src.array(mfi), zvel.const_array(mfi), c, znd,
659  s, bz_, has_blank, problo, dx, klo, khi, v, obs_nudging::W);
660  }
661  }
662  }
663 }
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:36
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
void update_targets(double time)
Definition: ERF_ObsNudging.cpp:409
obs_nudging::ObsNudgingView view(amrex::Real dt, const amrex::Geometry &geom) const
Definition: ERF_ObsNudging.cpp:466
const amrex::MultiFab & surface(int lev, const amrex::Geometry &geom, const amrex::MultiFab &cons, const amrex::MultiFab *z_phys_nd, double time)
Definition: ERF_ObsNudging.cpp:493
@ xvel
Definition: ERF_IndexDefines.H:218
@ cons
Definition: ERF_IndexDefines.H:217
@ zvel
Definition: ERF_IndexDefines.H:220
@ yvel
Definition: ERF_IndexDefines.H:219
@ V
Definition: ERF_ObsNudgingSeries.H:36
Definition: ERF_ObsNudgingKernel.H:26
Here is the call graph for this function:

◆ add_theta_source()

void ObsNudging::add_theta_source ( int  lev,
double  time,
amrex::Real  dt,
const amrex::Geometry &  geom,
const amrex::MultiFab &  cons,
amrex::MultiFab &  cc_src,
const amrex::MultiFab *  z_phys_nd,
const amrex::MultiFab *  blank 
)
571 {
572  if (!m_nudge_theta) { return; }
573 
574  update_targets(time);
575  const obs_nudging::ObsNudgingView v = view(dt, geom);
576  const MultiFab& zs = surface(lev, geom, cons, z_phys_nd, time);
577 
578  const auto problo = geom.ProbLoArray();
579  const auto dx = geom.CellSizeArray();
580  const bool has_blank = (blank != nullptr);
581 
582 #ifdef _OPENMP
583 #pragma omp parallel if (Gpu::notInLaunchRegion())
584 #endif
585  for (MFIter mfi(cons, TilingIfNotGPU()); mfi.isValid(); ++mfi)
586  {
587  const Box& bx = mfi.tilebox();
588  const Array4<Real>& src = cc_src.array(mfi);
589  const Array4<const Real>& c = cons.const_array(mfi);
590  const Array4<const Real>& s = zs.const_array(mfi);
591  const Array4<const Real> b = has_blank ? blank->const_array(mfi) : Array4<const Real>{};
592  if (z_phys_nd) {
593  obs_nudging::add_cell_theta_source<true>(bx, src, c, z_phys_nd->const_array(mfi), s, b,
594  has_blank, problo, dx, v);
595  } else {
596  obs_nudging::add_cell_theta_source<false>(bx, src, c, Array4<const Real>{}, s, b,
597  has_blank, problo, dx, v);
598  }
599  }
600 }
Here is the call graph for this function:

◆ prepare_level()

void ObsNudging::prepare_level ( int  lev,
const amrex::Geometry &  geom,
const amrex::MultiFab &  cons,
const amrex::MultiFab *  z_phys_nd,
double  time 
)
563 {
564  surface(lev, geom, cons, z_phys_nd, time);
565 }
Here is the call graph for this function:

◆ print_summary()

void ObsNudging::print_summary ( ) const
382 {
383  Print() << "Observation nudging: " << m_stations.size() << " station(s), tau = " << m_tau
384  << " s, R_h = " << m_horizontal_radius << " m, R_z = " << m_vertical_radius
385  << " m, cutoff = " << m_cutoff << ", sigma_factor = " << m_sigma_factor << "\n";
386  for (const auto& st : m_stations)
387  {
388  const auto& s = st.series;
389  std::ostringstream os;
390  os << " " << st.name << ": x = " << st.x << ", y = " << st.y;
391  if (st.use_latlon) {
392  os << " (lat " << st.req_lat << ", long " << st.req_lon << " -> "
393  << st.got_lat << ", " << st.got_lon << ")";
394  }
395  const Real alpha = std::atan2(st.sin_alpha, st.cos_alpha) * Real(180.0) / PI;
396  os << ", " << s.heights.size() << " height(s) " << s.heights.front() << " to "
397  << s.heights.back() << " m " << (st.agl ? "above the terrain" : "above z = 0")
398  << ", " << s.records.size() << " time(s) " << s.records.front().time << " to "
399  << s.records.back().time << " s, wind frame " << (st.earth_frame ? "earth" : "grid")
400  << " (rotated " << alpha << " deg), nudging";
401  if (m_nudge_wind && s.has[obs_nudging::U]) { os << " u v"; }
402  if (m_nudge_w && s.has[obs_nudging::W]) { os << " w"; }
403  if (m_nudge_theta && s.has[obs_nudging::Theta]) { os << " theta"; }
404  Print() << os.str() << "\n";
405  }
406 }
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
@ alpha
Definition: ERF_SLM.H:151

◆ resolve_positions()

void ObsNudging::resolve_positions ( const amrex::Geometry &  geom0,
const LatLonMap *  latlon 
)
336 {
337  const auto problo = geom0.ProbLoArray();
338  const auto probhi = geom0.ProbHiArray();
339 
340  for (auto& st : m_stations)
341  {
342  if (st.use_latlon) {
343  if (latlon == nullptr) {
344  Abort("erf.obs_nudging." + st.name + ": lat/long needs a run with latitude/"
345  "longitude arrays (a WRF or metgrid initialization, or a restart from "
346  "one). Place the station with x and y in domain coordinates instead");
347  }
348  LatLonLocation where;
349  const LatLonStatus status = latlon->locate(st.req_lat, st.req_lon, where);
350  if (status == LatLonStatus::Degenerate) {
351  Abort("erf.obs_nudging." + st.name + ": the latitude/longitude arrays are "
352  "degenerate near the requested point, so it cannot be inverted");
353  }
354  if (status == LatLonStatus::TooFar) {
355  Abort("erf.obs_nudging." + st.name + ": lat=" + std::to_string(st.req_lat) +
356  " long=" + std::to_string(st.req_lon) + " is more than one cell from the "
357  "nearest grid point (lat=" + std::to_string(where.near_lat) + " long=" +
358  std::to_string(where.near_lon) + "), so it is outside the domain");
359  }
360  st.x = where.x;
361  st.y = where.y;
362  st.got_lat = where.lat;
363  st.got_lon = where.lon;
364  if (st.earth_frame) {
365  st.cos_alpha = where.cos_alpha;
366  st.sin_alpha = where.sin_alpha;
367  }
368  } else if (st.earth_frame && latlon != nullptr) {
369  latlon->rotation_at(st.x, st.y, st.cos_alpha, st.sin_alpha);
370  }
371  // Otherwise the grid is aligned with the frame of the file: no rotation
372 
373  if (st.x < problo[0] || st.x > probhi[0] || st.y < problo[1] || st.y > probhi[1]) {
374  Abort("erf.obs_nudging." + st.name + ": x=" + std::to_string(st.x) + " y=" +
375  std::to_string(st.y) + " is outside the problem domain");
376  }
377  }
378 }
auto probhi
Definition: ERF_InitCustomPertVels_ABL.H:37
LatLonStatus
Definition: ERF_LatLonMap.H:105
void rotation_at(amrex::Real x, amrex::Real y, amrex::Real &cos_alpha, amrex::Real &sin_alpha) const
Definition: ERF_LatLonMap.cpp:206
LatLonStatus locate(amrex::Real req_lat, amrex::Real req_lon, LatLonLocation &loc) const
Definition: ERF_LatLonMap.cpp:180
Definition: ERF_LatLonMap.H:86
amrex::Real cos_alpha
Definition: ERF_LatLonMap.H:101
amrex::Real lat
Definition: ERF_LatLonMap.H:93
amrex::Real x
Definition: ERF_LatLonMap.H:88
amrex::Real near_lat
Definition: ERF_LatLonMap.H:97
amrex::Real y
Definition: ERF_LatLonMap.H:89
amrex::Real sin_alpha
Definition: ERF_LatLonMap.H:102
amrex::Real near_lon
Definition: ERF_LatLonMap.H:98
amrex::Real lon
Definition: ERF_LatLonMap.H:94
Here is the call graph for this function:

◆ surface()

const MultiFab & ObsNudging::surface ( int  lev,
const amrex::Geometry &  geom,
const amrex::MultiFab &  cons,
const amrex::MultiFab *  z_phys_nd,
double  time 
)
private
495 {
496  if (lev >= static_cast<int>(m_zsurf.size())) {
497  m_zsurf.resize(lev+1);
498  m_zsurf_ba.resize(lev+1);
499  m_zsurf_dm.resize(lev+1);
500  m_zsurf_time.resize(lev+1, 0.0); // read only when m_zsurf[lev] is set
501  }
502 
503  const bool fitted = (m_terrain_type == TerrainType::StaticFittedMesh ||
504  m_terrain_type == TerrainType::MovingFittedMesh);
505  const bool moving = (m_terrain_type == TerrainType::MovingFittedMesh);
506 
507  // On a moving fitted mesh the terrain is rebuilt for each RK stage from the
508  // stage's start time (update_terrain_stage), so it is a function of the
509  // grids and of that time; on every other mesh it depends on the grids
510  // alone. Both source calls of a stage pass the same time, so the slab is
511  // built once per stage rather than once per call: it costs a MultiFab, a
512  // ParallelCopy and a global reduction that would otherwise be paid twice.
513  if (m_zsurf[lev] &&
514  m_zsurf_ba[lev] == cons.boxArray() && m_zsurf_dm[lev] == cons.DistributionMap() &&
515  (!moving || m_zsurf_time[lev] == time)) {
516  return *m_zsurf[lev];
517  }
518 
519  // The nodes of the bottom slab under every grid of the level, laid out
520  // like the level so that an MFIter over the state indexes it too
521  m_zsurf[lev] = std::make_unique<MultiFab>(bottom_node_slab(cons.boxArray(), geom),
522  cons.DistributionMap(), 1, 0);
523  MultiFab& zs = *m_zsurf[lev];
524 
525  if (fitted)
526  {
527  // The bottom of the mesh is the terrain. The surface nodes live only
528  // in the boxes that touch the bottom of the domain, so gather them.
529  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(z_phys_nd != nullptr,
530  "ObsNudging: a terrain-fitted mesh without z_phys_nd");
531  zs.setVal(bogus_large_value);
532  zs.ParallelCopy(*z_phys_nd, 0, 0, 1, IntVect(0), IntVect(0));
533  const Real zmax = zs.max(0);
534  if (zmax > Real(0.5) * bogus_large_value) {
535  Abort("erf.nudging_from_observations: the grids of level " + std::to_string(lev) +
536  " do not reach the bottom of the domain everywhere, so the terrain under "
537  "them is not known on that level. With a terrain-fitted mesh every level "
538  "must reach the ground where it is refined");
539  }
540  }
541  else if (m_terrain_type == TerrainType::ImmersedForcing)
542  {
543  // The terrain surface the immersed boundary is built from, at this
544  // level's resolution
545  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_prob != nullptr, "ObsNudging: no problem object");
546  fill_terrain_surface_slab(zs, geom, *m_prob, time);
547  }
548  else
549  {
550  // Flat: the ground is the bottom of the domain
551  zs.setVal(geom.ProbLo(2));
552  }
553 
554  m_zsurf_ba[lev] = cons.boxArray();
555  m_zsurf_dm[lev] = cons.DistributionMap();
556  m_zsurf_time[lev] = time;
557  return zs;
558 }
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:19
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
BoxArray bottom_node_slab(const BoxArray &ba, const Geometry &geom)
Definition: ERF_TerrainSurfaceSlab.cpp:13
void fill_terrain_surface_slab(MultiFab &zs, const Geometry &geom, ProblemBase &prob, double time)
Definition: ERF_TerrainSurfaceSlab.cpp:25
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_zsurf
Definition: ERF_ObsNudging.H:156
amrex::Vector< double > m_zsurf_time
Definition: ERF_ObsNudging.H:159
amrex::Vector< amrex::DistributionMapping > m_zsurf_dm
Definition: ERF_ObsNudging.H:158
amrex::Vector< amrex::BoxArray > m_zsurf_ba
Definition: ERF_ObsNudging.H:157

Referenced by add_momentum_sources(), add_theta_source(), and prepare_level().

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

◆ update_targets()

void ObsNudging::update_targets ( double  time)
private
410 {
411  if (m_have_targets && time == m_targets_time) { return; }
412 
413  const int nst = static_cast<int>(m_stations.size());
415 
416  Vector<Real> h_x(nst), h_y(nst);
417  Vector<int> h_agl(nst);
418  Vector<int> h_offset(obs_nudging::NComp * nst, 0), h_count(obs_nudging::NComp * nst, 0);
419  Vector<Real> h_z, h_mean, h_sigma;
420 
421  for (int s = 0; s < nst; ++s)
422  {
423  const auto& st = m_stations[s];
424  h_x[s] = st.x;
425  h_y[s] = st.y;
426  h_agl[s] = st.agl ? 1 : 0;
427 
428  std::array<obs_nudging::Profile, obs_nudging::NComp> prof;
430  st.cos_alpha, st.sin_alpha, prof);
431 
432  for (int c = 0; c < obs_nudging::NComp; ++c) {
433  if (!nudge[c]) { continue; }
434  const int idx = c*nst + s;
435  h_offset[idx] = static_cast<int>(h_z.size());
436  h_count[idx] = static_cast<int>(prof[c].z.size());
437  h_z.insert(h_z.end(), prof[c].z.begin(), prof[c].z.end());
438  h_mean.insert(h_mean.end(), prof[c].mean.begin(), prof[c].mean.end());
439  h_sigma.insert(h_sigma.end(), prof[c].sigma.begin(), prof[c].sigma.end());
440  }
441  }
442 
443  // A zero-length device array has no data pointer to hand out; keep one
444  // element so that the view is always valid, and let the counts say it is unused
445  if (h_z.empty()) { h_z.push_back(Real(0.0)); h_mean.push_back(Real(0.0)); h_sigma.push_back(Real(0.0)); }
446 
447  m_d_z.resize(h_z.size());
448  m_d_mean.resize(h_mean.size());
449  m_d_sigma.resize(h_sigma.size());
450 
451  Gpu::copy(Gpu::hostToDevice, h_x.begin(), h_x.end(), m_d_x.begin());
452  Gpu::copy(Gpu::hostToDevice, h_y.begin(), h_y.end(), m_d_y.begin());
453  Gpu::copy(Gpu::hostToDevice, h_agl.begin(), h_agl.end(), m_d_agl.begin());
454  Gpu::copy(Gpu::hostToDevice, h_offset.begin(), h_offset.end(), m_d_offset.begin());
455  Gpu::copy(Gpu::hostToDevice, h_count.begin(), h_count.end(), m_d_count.begin());
456  Gpu::copy(Gpu::hostToDevice, h_z.begin(), h_z.end(), m_d_z.begin());
457  Gpu::copy(Gpu::hostToDevice, h_mean.begin(), h_mean.end(), m_d_mean.begin());
458  Gpu::copy(Gpu::hostToDevice, h_sigma.begin(), h_sigma.end(), m_d_sigma.begin());
459  Gpu::streamSynchronize();
460 
461  m_have_targets = true;
462  m_targets_time = time;
463 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:396
bool m_have_targets
Definition: ERF_ObsNudging.H:147
amrex::Gpu::DeviceVector< amrex::Real > m_d_z
Definition: ERF_ObsNudging.H:149
amrex::Gpu::DeviceVector< amrex::Real > m_d_sigma
Definition: ERF_ObsNudging.H:149
amrex::Gpu::DeviceVector< amrex::Real > m_d_mean
Definition: ERF_ObsNudging.H:149
double m_targets_time
Definition: ERF_ObsNudging.H:148
bool profiles_at_time(const StationSeries &series, double t, Real cos_alpha, Real sin_alpha, std::array< Profile, NComp > &profiles)
Definition: ERF_ObsNudgingSeries.cpp:299

Referenced by add_momentum_sources(), and add_theta_source().

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

◆ view()

obs_nudging::ObsNudgingView ObsNudging::view ( amrex::Real  dt,
const amrex::Geometry &  geom 
) const
private
467 {
468  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(dt > Real(0.0), "ObsNudging: the time step must be positive");
470 
472  v.nstations = static_cast<int>(m_stations.size());
473  v.x = m_d_x.data();
474  v.y = m_d_y.data();
475  v.agl = m_d_agl.data();
476  v.offset = m_d_offset.data();
477  v.count = m_d_count.data();
478  v.z = m_d_z.data();
479  v.mean = m_d_mean.data();
480  v.sigma = m_d_sigma.data();
483  v.qmax = m_cutoff * m_cutoff;
485  v.inv_tau = Real(1.0) / m_tau;
486  v.max_rate = Real(1.0) / dt;
487  v.period_x = geom.isPeriodic(0) ? static_cast<Real>(geom.ProbLength(0)) : Real(0.0);
488  v.period_y = geom.isPeriodic(1) ? static_cast<Real>(geom.ProbLength(1)) : Real(0.0);
489  return v;
490 }
amrex::Real period_x
Definition: ERF_ObsNudgingKernel.H:45
const amrex::Real * x
Definition: ERF_ObsNudgingKernel.H:28
amrex::Real inv_rh2
Definition: ERF_ObsNudgingKernel.H:37
const int * count
Definition: ERF_ObsNudgingKernel.H:32
const amrex::Real * z
Definition: ERF_ObsNudgingKernel.H:33
const amrex::Real * y
Definition: ERF_ObsNudgingKernel.H:29
const amrex::Real * sigma
Definition: ERF_ObsNudgingKernel.H:35
const amrex::Real * mean
Definition: ERF_ObsNudgingKernel.H:34
amrex::Real qmax
Definition: ERF_ObsNudgingKernel.H:39
amrex::Real sigma_factor
Definition: ERF_ObsNudgingKernel.H:40
const int * agl
Definition: ERF_ObsNudgingKernel.H:30
int nstations
Definition: ERF_ObsNudgingKernel.H:27
amrex::Real period_y
Definition: ERF_ObsNudgingKernel.H:46
amrex::Real inv_tau
Definition: ERF_ObsNudgingKernel.H:41
const int * offset
Definition: ERF_ObsNudgingKernel.H:31
amrex::Real max_rate
Definition: ERF_ObsNudgingKernel.H:42
amrex::Real inv_rz2
Definition: ERF_ObsNudgingKernel.H:38

Referenced by add_momentum_sources(), and add_theta_source().

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

◆ wants_latlon()

bool ObsNudging::wants_latlon ( ) const
327 {
328  for (const auto& st : m_stations) {
329  if (st.use_latlon || st.earth_frame) { return true; }
330  }
331  return false;
332 }

Member Data Documentation

◆ m_cutoff

amrex::Real ObsNudging::m_cutoff = amrex::Real(6.0)
private

Referenced by ObsNudging(), print_summary(), and view().

◆ m_d_agl

amrex::Gpu::DeviceVector<int> ObsNudging::m_d_agl
private

Referenced by ObsNudging(), update_targets(), and view().

◆ m_d_count

amrex::Gpu::DeviceVector<int> ObsNudging::m_d_count
private

Referenced by ObsNudging(), update_targets(), and view().

◆ m_d_mean

amrex::Gpu::DeviceVector<amrex::Real> ObsNudging::m_d_mean
private

Referenced by update_targets(), and view().

◆ m_d_offset

amrex::Gpu::DeviceVector<int> ObsNudging::m_d_offset
private

Referenced by ObsNudging(), update_targets(), and view().

◆ m_d_sigma

amrex::Gpu::DeviceVector<amrex::Real> ObsNudging::m_d_sigma
private

Referenced by update_targets(), and view().

◆ m_d_x

amrex::Gpu::DeviceVector<amrex::Real> ObsNudging::m_d_x
private

Referenced by ObsNudging(), update_targets(), and view().

◆ m_d_y

amrex::Gpu::DeviceVector<amrex::Real> ObsNudging::m_d_y
private

Referenced by ObsNudging(), update_targets(), and view().

◆ m_d_z

amrex::Gpu::DeviceVector<amrex::Real> ObsNudging::m_d_z
private

Referenced by update_targets(), and view().

◆ m_have_targets

bool ObsNudging::m_have_targets = false
private

Referenced by update_targets(), and view().

◆ m_horizontal_radius

amrex::Real ObsNudging::m_horizontal_radius = amrex::Real(500.0)
private

Referenced by ObsNudging(), print_summary(), and view().

◆ m_missing_value

amrex::Real ObsNudging::m_missing_value = amrex::Real(-9999.0)
private

Referenced by ObsNudging().

◆ m_nudge_theta

bool ObsNudging::m_nudge_theta = true
private

◆ m_nudge_w

bool ObsNudging::m_nudge_w = true
private

◆ m_nudge_wind

bool ObsNudging::m_nudge_wind = true
private

◆ m_prob

ProblemBase* ObsNudging::m_prob = nullptr
private

Referenced by surface().

◆ m_sigma_factor

amrex::Real ObsNudging::m_sigma_factor = amrex::Real(1.0)
private

Referenced by ObsNudging(), print_summary(), and view().

◆ m_stations

amrex::Vector<ObsNudgingStation> ObsNudging::m_stations
private

◆ m_targets_time

double ObsNudging::m_targets_time = 0.0
private

Referenced by update_targets().

◆ m_tau

amrex::Real ObsNudging::m_tau = amrex::Real(-1.0)
private

Referenced by ObsNudging(), print_summary(), and view().

◆ m_terrain_type

TerrainType ObsNudging::m_terrain_type = TerrainType::None
private

Referenced by surface().

◆ m_time_origin

double ObsNudging::m_time_origin = 0.0
private

Referenced by ObsNudging(), and update_targets().

◆ m_vertical_radius

amrex::Real ObsNudging::m_vertical_radius = amrex::Real(25.0)
private

Referenced by ObsNudging(), print_summary(), and view().

◆ m_zsurf

amrex::Vector<std::unique_ptr<amrex::MultiFab> > ObsNudging::m_zsurf
private

Referenced by surface().

◆ m_zsurf_ba

amrex::Vector<amrex::BoxArray> ObsNudging::m_zsurf_ba
private

Referenced by surface().

◆ m_zsurf_dm

amrex::Vector<amrex::DistributionMapping> ObsNudging::m_zsurf_dm
private

Referenced by surface().

◆ m_zsurf_time

amrex::Vector<double> ObsNudging::m_zsurf_time
private

Referenced by surface().


The documentation for this class was generated from the following files: