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

#include <ERF_NOAHMP.H>

Inheritance diagram for NOAHMP:
Collaboration diagram for NOAHMP:

Public Member Functions

 NOAHMP ()
 
virtual ~NOAHMP ()=default
 
void Define (SolverChoice &sc) override
 
void Init (const int &lev, const amrex::MultiFab &cons_in, const amrex::Geometry &geom, const amrex::Geometry &geom0, amrex::Vector< amrex::BCRec > &domain_bcs_type, amrex::IntVect &refRatio, const amrex::Real &dt, amrex::Vector< amrex::Vector< std::string >> &nc_init_file) override
 
void Advance_With_State (const int &lev, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, amrex::MultiFab *hfx3_out, amrex::MultiFab *qfx3_out, const SurfacePrecipAccumulationSources &precip_sources, const amrex::Real &elapsed_time, const amrex::Real &dt, const int &nstep, const bool updated_lev0, amrex::MultiFab *z_nd) override
 
void Plot_Landfile (const int &nstep) override
 
amrex::MultiFab * Lsm_Data_Ptr (const int &varIdx) override
 
amrex::MultiFab * Lsm_Flux_Ptr (const int &varIdx) override
 
amrex::Geometry Lsm_Geom () override
 
int Lsm_Data_Size () override
 
int Lsm_Flux_Size () override
 
std::string Lsm_DataName (const int &varIdx) override
 
int Lsm_DataIndex (const std::string &varname) override
 
std::string Lsm_FluxName (const int &varIdx) override
 
int Lsm_FluxIndex (const std::string &varname) override
 
void Lsm_Set_Lev0_Data_Ptr (const int &varIdx, amrex::MultiFab *lev0_data_ptr) override
 
void Lsm_Set_Lev0_Flux_Ptr (const int &varIdx, amrex::MultiFab *lev0_flux_ptr) override
 
bool Get_Update_Status () override
 
int Get_LSM_Step () const override
 
void Set_LSM_Step (int step) override
 
void Write_Lsm_Restart (const std::string &dir) const override
 
void Read_Lsm_Restart (const std::string &dir) override
 
void interp_from_lev0 (const int &lev, amrex::MultiFab &cons_in, const int &nstep)
 
bool time_to_fire (const amrex::Real &elapsed_time)
 
void stage_forcing (const amrex::MFIter &mfi, const erf_noahmp::NoahmpBlockViews &blk, const amrex::Box &bx, const int &klo, const int &lev, const bool is_moist, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, amrex::MultiFab *z_nd, const erf_noahmp::PrecipSlots &precip, amrex::Vector< erf_noahmp::ClampedPrecipCell > &clamped_cells, amrex::Vector< erf_noahmp::InvariantPrecipCell > &invariant_cells)
 
void read_results (const amrex::MFIter &mfi, const erf_noahmp::NoahmpBlockViews &blk, const amrex::Box &bx, const amrex::Box &gbx, amrex::MultiFab &cons_in)
 
erf_noahmp::PrecipSlots collect_precip_sources (const SurfacePrecipAccumulationSources &precip_sources)
 
void prepare_precip_snapshots (const int &lev, const erf_noahmp::PrecipSlots &precip)
 
void advance_precip_snapshots (const int &lev, const erf_noahmp::PrecipSlots &precip)
 
void report_precip_diagnostics (const int &lev, const amrex::Vector< erf_noahmp::ClampedPrecipCell > &clamped_cells, const amrex::Vector< erf_noahmp::InvariantPrecipCell > &invariant_cells)
 
int soil_data_idx (int g, int k) const
 
void m_ensure_nsoil_resolved ()
 
virtual void Init (const int &, const amrex::MultiFab &, const amrex::Geometry &, const amrex::Geometry &, amrex::Vector< amrex::BCRec > &, amrex::IntVect &, const amrex::Real &, amrex::Vector< amrex::Vector< std::string >> &)
 
virtual void Init (const int &lev, const amrex::MultiFab &cons_in, const amrex::MultiFab &, const amrex::MultiFab &, const amrex::Geometry &geom, const amrex::Geometry &geom0, amrex::Vector< amrex::BCRec > &domain_bcs_type, amrex::IntVect &refRatio, const amrex::Real &dt_advance, std::unique_ptr< amrex::MultiFab > &, amrex::Vector< amrex::Vector< std::string >> &nc_init_file)
 
- Public Member Functions inherited from NullSurf
 NullSurf ()
 
virtual ~NullSurf ()=default
 
virtual void Init (const int &lev, const amrex::MultiFab &cons_in, const amrex::MultiFab &, const amrex::MultiFab &, const amrex::Geometry &geom, const amrex::Geometry &geom0, amrex::Vector< amrex::BCRec > &domain_bcs_type, amrex::IntVect &refRatio, const amrex::Real &dt_advance, std::unique_ptr< amrex::MultiFab > &, amrex::Vector< amrex::Vector< std::string >> &nc_init_file)
 
virtual void Advance (const double &)
 
virtual void Advance (const amrex::Real &, const amrex::Real &, const amrex::Real &)
 
virtual void Update_Micro_Vars (amrex::MultiFab &)
 
virtual void Update_Lsm_Vars (amrex::MultiFab &, amrex::MultiFab &, amrex::MultiFab &)
 
virtual void Copy_State_to_Lsm (const amrex::MultiFab &, const amrex::MultiFab &, const amrex::MultiFab &)
 
virtual void Copy_Lsm_to_State (amrex::MultiFab &)
 
virtual void Update_State_Vars (amrex::MultiFab &)
 
virtual void set_precip_input (const amrex::MultiFab *)
 
virtual void set_terrain_inputs (const amrex::Vector< std::unique_ptr< amrex::MultiFab >> &, const amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &)
 
virtual int Lsm_Type ()
 
virtual void WriteCheckpoint (const int &, const std::string &) const
 
virtual void ReadCheckpoint (const int &, const std::string &)
 
virtual void Copy_State_to_Micro (const amrex::MultiFab &)
 
virtual void Copy_Micro_to_State (amrex::MultiFab &)
 
virtual std::unordered_map< std::string, std::string > & Lsm_WRFInputNames ()
 
virtual void Lsm_Set_Source_Data_Ptr (const int &, amrex::MultiFab *)
 
virtual void Lsm_Set_Source_Flux_Ptr (const int &, amrex::MultiFab *)
 
virtual int Lsm_Unmapped_Size ()
 
virtual amrex::MultiFab * Lsm_Unmapped_Ptr (const int &)
 
virtual void Lsm_Set_Source_Unmapped_Ptr (const int &, amrex::MultiFab *)
 
virtual amrex::Geometry Lsm_Atmos_Geom ()
 
virtual void Lsm_Interpolate_From_Source (const amrex::Geometry &, const amrex::Geometry &, const amrex::IntVect &, LSMTransferMode)
 
virtual void Lsm_Regrid_Level (const int &, const amrex::MultiFab &, const amrex::MultiFab &, const amrex::MultiFab &, const amrex::Geometry &, const amrex::Geometry &, amrex::Vector< amrex::BCRec > &, amrex::IntVect &, const amrex::Real &, std::unique_ptr< amrex::MultiFab > &, amrex::Vector< amrex::Vector< std::string >> &, const amrex::Geometry &, const amrex::Geometry &, const amrex::IntVect &, const bool)
 

Static Public Member Functions

static std::string m_precip_snapshot_name (const std::string &dir, int l, int s)
 

Public Attributes

bool m_nsoil_resolved = false
 

Static Public Attributes

static constexpr int m_num_soil_groups = 3
 

Private Types

using FabPtr = std::shared_ptr< amrex::MultiFab >
 

Private Member Functions

void with_land_comm (const std::function< void(NoahmpIO_type &)> &fn) const
 

Private Attributes

int m_lsm_data_size = LsmData_NOAHMP::NumVars
 
int m_lsm_flux_size = LsmFlux_NOAHMP::NumVars
 
amrex::Vector< int > LsmDataMap
 
amrex::Vector< int > LsmFluxMap
 
amrex::Vector< std::string > LsmDataName
 
amrex::Vector< std::string > LsmFluxName
 
amrex::Geometry m_geom
 
amrex::Geometry m_geom0
 
amrex::Geometry m_lsm_geom
 
amrex::IntVect m_refRatio
 
amrex::Vector< amrex::BCRec > m_domain_bcs_type
 
int m_itimestep = 0
 
amrex::Real m_dtbl = std::numeric_limits<amrex::Real>::max()
 
int khi_lsm = -1
 
int m_nz_lsm = 1
 
amrex::Real m_dz_lsm = one
 
amrex::Vector< FabPtr > lsm_fab_data
 
amrex::Array< FabPtr, LsmFlux_NOAHMP::NumVars > lsm_fab_flux
 
amrex::Vector< amrex::MultiFab * > lsm_lev0_data
 
amrex::Array< amrex::MultiFab *, LsmFlux_NOAHMP::NumVars > lsm_lev0_flux {}
 
int m_nsoil = 4
 
NoahmpIO_vector noahmpio_vect
 
amrex::Vector< std::unique_ptr< amrex::FArrayBox > > noahmp_input_tmp
 
amrex::Vector< std::unique_ptr< amrex::FArrayBox > > noahmp_output_tmp
 
amrex::Vector< amrex::Array< std::unique_ptr< amrex::MultiFab >, NoahmpPrecipSlot::NumSlots > > m_precip_accum_prev
 
amrex::Array< std::unique_ptr< amrex::MultiFab >, NoahmpPrecipSlot::NumSlots > m_precip_accum_restored
 
int m_lev = 0
 
bool m_updated = false
 
bool m_has_nc_file = false
 
bool m_checked_radiation_inputs = false
 
bool m_warned_radiation_inputs = false
 
int m_first_advance_nstep = -1
 
bool m_radiation_feeds_lsm = false
 
std::string m_radiation_model_name
 

Member Typedef Documentation

◆ FabPtr

using NOAHMP::FabPtr = std::shared_ptr<amrex::MultiFab>
private

Constructor & Destructor Documentation

◆ NOAHMP()

NOAHMP::NOAHMP ( )
inline
67 {}

◆ ~NOAHMP()

virtual NOAHMP::~NOAHMP ( )
virtualdefault

Member Function Documentation

◆ advance_precip_snapshots()

void NOAHMP::advance_precip_snapshots ( const int &  lev,
const erf_noahmp::PrecipSlots &  precip 
)
68 {
69  if (!precip.have_any) { return; }
70  for (int s(0); s < NoahmpPrecipSlot::NumSlots; ++s) {
71  if (precip.has[s]) {
72  MultiFab::Copy(*m_precip_accum_prev[lev][s], *precip.accum[s], 0, 0, 1, 0);
73  }
74  }
75 }
amrex::Vector< amrex::Array< std::unique_ptr< amrex::MultiFab >, NoahmpPrecipSlot::NumSlots > > m_precip_accum_prev
Definition: ERF_NOAHMP.H:410
@ NumSlots
Definition: ERF_NOAHMP_Fields.H:136
@ precip
Definition: ERF_SLM.H:133

◆ Advance_With_State()

void NOAHMP::Advance_With_State ( const int &  lev,
amrex::MultiFab &  cons_in,
amrex::MultiFab &  xvel_in,
amrex::MultiFab &  yvel_in,
amrex::MultiFab *  hfx3_out,
amrex::MultiFab *  qfx3_out,
const SurfacePrecipAccumulationSources &  precip_sources,
const amrex::Real &  elapsed_time,
const amrex::Real &  dt,
const int &  nstep,
const bool  updated_lev0,
amrex::MultiFab *  z_nd 
)
overridevirtual

Reimplemented from NullSurf.

390 {
391  // The ERF step this level's first Advance call saw: 0 on a run started from
392  // scratch, the restart step on a restarted run. The radiation-input check below
393  // uses it to tell a coupling that never fires from one that has not fired yet.
394  if (m_first_advance_nstep < 0) { m_first_advance_nstep = nstep; }
395 
396  if (!m_has_nc_file) {
397  // Skip interpolation if lev 0 was just updated: Noah runs post-step, so
398  // the fluxes & data still hold lsm_undefined values.
399  if (!updated_lev0) {
400  interp_from_lev0(lev, cons_in, nstep);
401  } else {
402  m_updated = false;
403  }
404  } else {
405  // time_to_fire lets Noah-MP advance one NOAH_TIMESTEP per call and fires at most
406  // once per ERF step, so an ERF step longer than NOAH_TIMESTEP leaves the land clock
407  // further behind the atmosphere's every step: with dt = 7200 s against a 3600 s
408  // land step the run completes normally while the land surface advances half as
409  // fast. Checked here because this is the first place the real step is known --
410  // Init receives a dummy dt of zero.
412  "Noah-MP: the ERF timestep exceeds the Noah-MP timestep (NOAH_TIMESTEP in "
413  "namelist.erf). Noah-MP advances at most one NOAH_TIMESTEP per ERF step, so the "
414  "land surface would fall behind the atmosphere; raise NOAH_TIMESTEP or reduce the "
415  "ERF timestep.");
416 
417  // Gate on the subcycling schedule (identical on every rank).
418  if (!time_to_fire(elapsed_time)) { return; }
419 
420  Box domain = m_geom.Domain();
421 
422  Print () << "Noah-MP driver at level " << lev << " started at time step: " << nstep+1 << std::endl;
423 
424  // stage_forcing replaces a radiation input that is not is_valid_lsm_value (still
425  // the lsm_undefined sentinel, or non-finite) with zero. Look at what is actually
426  // there before the land model integrates on it. On a run started from scratch the
427  // radiation model runs before the land model within a step (pre-dycore vs
428  // post-step) and RRTMGP always computes on step 0, so the first land step already
429  // sees whatever the radiation model is going to supply. On a restarted run that is
430  // not so: the fields are part of the LSM checkpoint data, but a regrid at restart
431  // rebuilds the level and NOAHMP::Init re-installs the sentinel, and RRTMGP
432  // recomputes only on its erf.rad_freq_in_steps schedule -- the sentinel can be
433  // legitimate and transient. So keep checking each land step until valid values
434  // arrive, and reserve the abort for the case that is certainly broken.
435  //
436  // What a missing input means depends on the radiation model. RRTMGP writes these
437  // fields, and so does TwoStream (every step, for Noah-MP), so with either an input
438  // still missing on a run started from scratch is a coupling that never reaches this
439  // level, and running on zero would be a 0 K sky (on an idealized grassland patch
440  // t_sfc falls about 48 K in one land hour): abort. On a restarted run, warn and
441  // integrate on zero until the next radiation update fills the fields. Any other
442  // radiation model does not write them, the start-up warning in
443  // SolverChoice::init_params has already said so, and zero is the intended
444  // fallback: say it once more here, at the level concerned.
446  const std::pair<int, const char*> rad_inputs[] = {
447  {LsmData_NOAHMP::sw_flux_dn, "downwelling shortwave (SWDOWN)"},
448  {LsmData_NOAHMP::lw_flux_dn, "downwelling longwave (GLW)"},
449  {LsmData_NOAHMP::cos_zenith_angle, "solar zenith angle (COSZEN)"}};
450  std::string invalid;
451  for (const auto& [idx, what] : rad_inputs) {
452  const MultiFab& mf = *lsm_fab_data[idx];
453  // The complement of is_valid_lsm_value over the whole field: max catches
454  // the sentinel and +inf, contains_inf catches -inf (which a max test alone
455  // passes while stage_forcing zeroes every cell), contains_nan the rest.
456  // All three reduce over ranks, so the verdict below is identical on every
457  // rank.
458  if (mf.max(0) >= lsm_undefined || mf.contains_nan(0, 1) || mf.contains_inf(0, 1)) {
459  invalid += std::string(invalid.empty() ? "" : ", ") + what;
460  }
461  }
462  if (invalid.empty()) {
464  } else if (m_radiation_feeds_lsm) {
465  if (m_first_advance_nstep == 0) {
466  amrex::Abort("Noah-MP at level " + std::to_string(lev) + " found no valid "
467  + invalid + " although erf.radiation_model = "
468  + m_radiation_model_name + ", which should supply them before "
469  "the first land step. Check that the radiation model updates "
470  "at all (for RRTMGP, erf.rad_freq_in_steps > 0) and that its "
471  "coupling to the land model reaches this level; running on "
472  "zero radiation would be a 0 K sky.");
473  }
476  amrex::Print() << "WARNING: Noah-MP at level " << lev << " found no valid "
477  << invalid << " on its first land step after a restart or "
478  "regrid and is using zero in its place until the next "
479  << m_radiation_model_name << " update writes them (for "
480  "RRTMGP, every erf.rad_freq_in_steps steps). This warning "
481  "is printed once; the run aborts only when the inputs are "
482  "already missing on a run started from scratch.\n";
483  }
484  } else {
486  amrex::Print() << "WARNING: Noah-MP at level " << lev << " found no valid "
487  << invalid << " from the radiation model and is using zero in "
488  "its place; the land surface will receive no radiative "
489  "forcing there. This warning is printed once.\n";
490  }
491  }
492 
493  bool is_moist = (cons_in.nComp() > RhoQ1_comp);
494 
495  int klo = domain.smallEnd(2);
496 
497  // Collect the typed precip slots, then seed the per-level previous-accumulation
498  // snapshots the delta kernel differences against (ERF_NOAHMP_Precip.cpp).
499  const erf_noahmp::PrecipSlots precip = collect_precip_sources(precip_sources);
501 
502  // Cells the guards touched this call; reported after the box loop.
503  Vector<erf_noahmp::ClampedPrecipCell> clamped_cells;
504  Vector<erf_noahmp::InvariantPrecipCell> invariant_cells;
505 
506  // Loop over blocks: ERF -> Noahmp, drive the land model, Noahmp -> ERF.
507  int idb = 0;
508  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi, ++idb) {
509 
510  Box bx = mfi.tilebox();
511  Box gbx = mfi.tilebox(IntVect(0,0,0),IntVect(1,1,0));
512 
513  // Only tiles at the lower z boundary
514  if (bx.smallEnd(2) != klo) { continue; }
515 
516  bx.makeSlab(2,klo);
517  gbx.makeSlab(2,klo);
518 
519  // The NoahmpIO block and its pinned staging buffers for this box,
520  // all co-indexed by idb, bundled into one handle.
521  const erf_noahmp::NoahmpBlockViews blk {
522  &noahmpio_vect[idb],
523  noahmp_input_tmp[idb]->array(),
524  noahmp_output_tmp[idb]->array()
525  };
526 
527  // (1-3) ERF forcing -> pinned input -> NoahmpIO arrays.
528  stage_forcing(mfi, blk, bx, klo, lev, is_moist,
529  cons_in, xvel_in, yvel_in, z_nd,
530  precip, clamped_cells, invariant_cells);
531 
532  // (4) Drive Noah-MP. Mirror the authoritative counter into the block first.
533  blk.io->itimestep = m_itimestep;
534  blk.io->DriverMain();
535 
536  // (5-6) NoahmpIO results -> pinned output -> ERF coupling fields.
537  read_results(mfi, blk, bx, gbx, cons_in);
538  }
539 
540  // Retire the firing index only after every block has consumed it, so the first
541  // firing reports ITIMESTEP == 1 and reaches Noah-MP's first-call init (#3618).
542  m_itimestep += 1; // once per firing, in lockstep on every rank
543 
544  // Advance the snapshots now that this call's RAINBL/SR have been consumed.
546 
547  // Report any cells the guards clamped / rescaled (never silent).
548  report_precip_diagnostics(lev, clamped_cells, invariant_cells);
549 
550  for (auto ivar = 0; ivar < LsmFlux_NOAHMP::NumVars; ++ivar) {
551  lsm_fab_flux[ivar]->FillBoundary(m_geom.periodicity());
552  }
553  Print () << "Noah-MP driver at level " << lev << " completed" << std::endl;
554  }
555 }
constexpr amrex::Real lsm_undefined
Definition: ERF_Constants.H:28
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:48
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int idx(int i, int j, int k, int nx, int ny)
Definition: ERF_InitForEnsemble.cpp:396
amrex::Array< FabPtr, LsmFlux_NOAHMP::NumVars > lsm_fab_flux
Definition: ERF_NOAHMP.H:388
bool m_updated
Definition: ERF_NOAHMP.H:420
void read_results(const amrex::MFIter &mfi, const erf_noahmp::NoahmpBlockViews &blk, const amrex::Box &bx, const amrex::Box &gbx, amrex::MultiFab &cons_in)
Definition: ERF_NOAHMP_Advance.cpp:243
amrex::Vector< std::unique_ptr< amrex::FArrayBox > > noahmp_output_tmp
Definition: ERF_NOAHMP.H:405
amrex::Vector< std::unique_ptr< amrex::FArrayBox > > noahmp_input_tmp
Definition: ERF_NOAHMP.H:404
void stage_forcing(const amrex::MFIter &mfi, const erf_noahmp::NoahmpBlockViews &blk, const amrex::Box &bx, const int &klo, const int &lev, const bool is_moist, amrex::MultiFab &cons_in, amrex::MultiFab &xvel_in, amrex::MultiFab &yvel_in, amrex::MultiFab *z_nd, const erf_noahmp::PrecipSlots &precip, amrex::Vector< erf_noahmp::ClampedPrecipCell > &clamped_cells, amrex::Vector< erf_noahmp::InvariantPrecipCell > &invariant_cells)
Definition: ERF_NOAHMP_Advance.cpp:91
bool m_radiation_feeds_lsm
Definition: ERF_NOAHMP.H:449
amrex::Vector< FabPtr > lsm_fab_data
Definition: ERF_NOAHMP.H:385
NoahmpIO_vector noahmpio_vect
Definition: ERF_NOAHMP.H:401
erf_noahmp::PrecipSlots collect_precip_sources(const SurfacePrecipAccumulationSources &precip_sources)
Definition: ERF_NOAHMP_Precip.cpp:20
bool m_checked_radiation_inputs
Definition: ERF_NOAHMP.H:430
void report_precip_diagnostics(const int &lev, const amrex::Vector< erf_noahmp::ClampedPrecipCell > &clamped_cells, const amrex::Vector< erf_noahmp::InvariantPrecipCell > &invariant_cells)
Definition: ERF_NOAHMP_Precip.cpp:80
int m_first_advance_nstep
Definition: ERF_NOAHMP.H:443
int m_itimestep
Definition: ERF_NOAHMP.H:368
void prepare_precip_snapshots(const int &lev, const erf_noahmp::PrecipSlots &precip)
Definition: ERF_NOAHMP_Precip.cpp:38
bool m_has_nc_file
Definition: ERF_NOAHMP.H:423
amrex::Real m_dtbl
Definition: ERF_NOAHMP.H:372
void interp_from_lev0(const int &lev, amrex::MultiFab &cons_in, const int &nstep)
Definition: ERF_NOAHMP_Advance.cpp:25
std::string m_radiation_model_name
Definition: ERF_NOAHMP.H:450
bool m_warned_radiation_inputs
Definition: ERF_NOAHMP.H:434
void advance_precip_snapshots(const int &lev, const erf_noahmp::PrecipSlots &precip)
Definition: ERF_NOAHMP_Precip.cpp:66
amrex::Geometry m_geom
Definition: ERF_NOAHMP.H:350
bool time_to_fire(const amrex::Real &elapsed_time)
Definition: ERF_NOAHMP_Advance.cpp:75
@ NumVars
Definition: ERF_NOAHMP_Fields.H:123
Definition: ERF_NOAHMP.H:50
Definition: ERF_NOAHMP.H:34
Here is the call graph for this function:

◆ collect_precip_sources()

erf_noahmp::PrecipSlots NOAHMP::collect_precip_sources ( const SurfacePrecipAccumulationSources &  precip_sources)
21 {
24  precip_sources.total, precip_sources.rain, precip_sources.snow,
25  precip_sources.graupel, precip_sources.hail };
26  for (int s(0); s < NoahmpPrecipSlot::NumSlots; ++s) {
27  p.accum[s] = slot_src[s].accum;
28  p.factor[s] = slot_src[s].native_to_kg_m2;
29  p.has[s] = (slot_src[s].accum != nullptr);
30  }
31  p.have_any = surface_precip_has_any_source(precip_sources);
32  return p;
33 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool surface_precip_has_any_source(const SurfacePrecipAccumulationSources &src) noexcept
Definition: ERF_SurfacePrecipitation.H:61
@ p
Definition: ERF_WSM6.H:280
Definition: ERF_SurfacePrecipitation.H:20
const amrex::MultiFab * accum
Definition: ERF_SurfacePrecipitation.H:21
amrex::Real native_to_kg_m2
Definition: ERF_SurfacePrecipitation.H:22
SurfacePrecipAccumulationSource rain
Definition: ERF_SurfacePrecipitation.H:36
SurfacePrecipAccumulationSource snow
Definition: ERF_SurfacePrecipitation.H:37
SurfacePrecipAccumulationSource hail
Definition: ERF_SurfacePrecipitation.H:39
SurfacePrecipAccumulationSource total
Definition: ERF_SurfacePrecipitation.H:35
SurfacePrecipAccumulationSource graupel
Definition: ERF_SurfacePrecipitation.H:38
Here is the call graph for this function:

◆ Define()

void NOAHMP::Define ( SolverChoice &  sc)
inlineoverridevirtual

Reimplemented from NullSurf.

75  {
76  // Whether the configured radiation model writes Noah-MP's radiation inputs
77  // (SWDOWN, GLW, COSZEN); see the first-land-step check in Advance_With_State.
79  m_radiation_model_name = amrex::getEnumNameString(sc.rad_type);
80  }
bool rad_feeds_lsm() const
True when the radiation model's outputs feed the land model (downwelling shortwave,...
Definition: ERF_DataStruct.H:2427
RadiationType rad_type
Radiation model.
Definition: ERF_DataStruct.H:2409
Here is the call graph for this function:

◆ Get_LSM_Step()

int NOAHMP::Get_LSM_Step ( ) const
inlineoverridevirtual

Reimplemented from NullSurf.

211  {
212  return m_itimestep;
213  }

◆ Get_Update_Status()

bool NOAHMP::Get_Update_Status ( )
inlineoverridevirtual

Reimplemented from NullSurf.

205 { return m_updated; }

◆ Init() [1/3]

virtual void NullSurf::Init
inline
34  { }

◆ Init() [2/3]

void NOAHMP::Init ( const int &  lev,
const amrex::MultiFab &  cons_in,
const amrex::Geometry &  geom,
const amrex::Geometry &  geom0,
amrex::Vector< amrex::BCRec > &  domain_bcs_type,
amrex::IntVect &  refRatio,
const amrex::Real &  dt,
amrex::Vector< amrex::Vector< std::string >> &  nc_init_file 
)
overridevirtual

Reimplemented from NullSurf.

241 {
242  // Install Noah-MP's fatal-error handler once: route NoahmpIO_fatal() through
243  // amrex::Abort so a fatal error propagates via MPI_Abort. See NoahmpFatal.H.
244  static const bool noahmp_fatal_installed = []() {
245  // The driver's own aborts pass no message: it WRITEs the reason to standard output
246  // on rank 0 first, and that line is usually lost when the job is killed. NOAHMP::Init
247  // checks the files the driver opens beforehand (noahmp_preflight); what reaches
248  // this default is a problem inside one of them, so point at the likely ones.
249  NoahmpIO_set_fatal_handler([](const char* msg){
250  amrex::Abort(msg ? msg :
251  "Noah-MP fatal error in the Fortran driver. Its explanation is written to "
252  "standard output on rank 0 and is usually lost when the job stops; check for a "
253  "malformed namelist.erf (every *_TIMESTEP and *_OPTION is an integer), a "
254  "NoahmpTable.TBL that does not match the land-use scheme (MMINLU), or a land "
255  "setup file missing one of the fields Noah-MP reads.");
256  });
257  return true;
258  }();
259  amrex::ignore_unused(noahmp_fatal_installed);
260 
261  // Noah-MP's own physics checks end the run with a bare Fortran STOP (27 of them, e.g.
262  // "Error: Solar radiation budget problem in NoahMP LSM"), which exits with status 0.
263  // main() installs an atexit trap that turns any exit() while AMReX is still
264  // initialised into a failure status; see install_exit_trap in main.cpp.
265 
266  // dt is a placeholder: ERF::make_lsm_at_level passes zero, because the level's step is
267  // not known yet when the level is built. Nothing here may depend on it; the step is
268  // checked against NOAH_TIMESTEP in Advance_With_State, where it is real.
269  amrex::ignore_unused(dt);
270 
271  m_lev = lev;
272  m_geom = geom;
273  m_geom0 = geom0;
274 
275  // Init runs again when a level is rebuilt (a regrid, including the one a restart
276  // can trigger), and the setVal below re-installs the lsm_undefined sentinel in the
277  // radiation inputs; make Advance_With_State re-check them rather than trust a
278  // verdict from before the wipe, and let it see that this is a rebuild.
282  m_domain_bcs_type = domain_bcs_type;
283  m_refRatio = refRatio;
284 
285  Box domain = geom.Domain();
286 
287  // Resolve NSOIL from erf.lsm_nsoil before building the collective LSM fabs (same
288  // value the parent used via Lsm_Data_Size()); namelist NSOIL asserted below.
290 
291  // The fixed 2D fields are identity-mapped; their names mirror the enum order.
292  LsmDataMap.resize(m_lsm_data_size);
294  for (int i(0); i < LsmData_NOAHMP::NumVars; ++i) { LsmDataMap[i] = i; }
295  {
296  // Names from the same registry as the enum, so they cannot drift.
297  const std::vector<std::string> fixed_names = {
299  };
300  AMREX_ALWAYS_ASSERT(int(fixed_names.size()) == LsmData_NOAHMP::NumVars);
301  for (int i(0); i < LsmData_NOAHMP::NumVars; ++i) { LsmDataName[i] = fixed_names[i]; }
302  }
303  // Per-layer soil profile: 3 groups of m_nsoil, layer index 1-based (WRF SMOIS_k).
304  {
305  const char* group[m_num_soil_groups] = {"smois", "sh2o", "tslb"};
306  for (int g(0); g < m_num_soil_groups; ++g) {
307  for (int k(0); k < m_nsoil; ++k) {
308  int idx = soil_data_idx(g,k);
309  LsmDataMap[idx] = idx;
310  LsmDataName[idx] = std::string(group[g]) + "_" + std::to_string(k+1);
311  }
312  }
313  }
314 
317  LsmFluxName = {"t_flux" , "q_flux" ,
318  "tau13" , "tau23" };
319 
320  // NOTE: relies on all boxes in ba spanning zlo..zhi; otherwise dm/ba no longer
321  // line up and lsm data/flux vars can't be copied directly in a parfor.
322 
323  // Set 2D box array for lsm data
324  IntVect ng(1,1,0);
325  BoxArray ba = cons_in.boxArray();
326  DistributionMapping dm = cons_in.DistributionMap();
327  BoxList bl_lsm = ba.boxList();
328  for (auto& b : bl_lsm) { b.setRange(2,0); }
329  BoxArray ba_lsm(std::move(bl_lsm));
330 
331  // Set up lsm geometry
332  const RealBox& dom_rb = m_geom.ProbDomain();
333  const Real* dom_dx = m_geom.CellSize();
334  RealBox lsm_rb = dom_rb;
335  Real lsm_dx[AMREX_SPACEDIM] = {AMREX_D_DECL(dom_dx[0],dom_dx[1],m_dz_lsm)};
336  Real lsm_z_hi = dom_rb.lo(2);
337  Real lsm_z_lo = lsm_z_hi - Real(m_nz_lsm)*lsm_dx[2];
338  lsm_rb.setHi(2,lsm_z_hi); lsm_rb.setLo(2,lsm_z_lo);
339  m_lsm_geom.define( ba_lsm.minimalBox(), lsm_rb, m_geom.Coord(), m_geom.isPeriodic() );
340 
341  // Create the data (CC), runtime-sized (fixed 2D fields + 3*m_nsoil) so soil scales
342  // with NSOIL. lsm_lev0_data pointers are populated later by the parent.
344  lsm_lev0_data.resize(m_lsm_data_size, nullptr);
345  for (auto ivar = 0; ivar < m_lsm_data_size; ++ivar) {
346  lsm_fab_data[ivar] = std::make_shared<MultiFab>(ba_lsm, dm, 1, ng);
347  lsm_fab_data[ivar]->setVal(lsm_undefined);
348  }
349 
350  // Create the fluxes (CC with ghost cells for averaging)
351  for (auto ivar = 0; ivar < LsmFlux_NOAHMP::NumVars; ++ivar) {
352  lsm_fab_flux[ivar] = std::make_shared<MultiFab>(ba_lsm, dm, 1, ng);
353  lsm_fab_flux[ivar]->setVal(lsm_undefined);
354  }
355 
356  // Level 0 always runs the Noah-MP driver: the driver reads its land state from the
357  // file ERF_SETUP_FILE_01 in namelist.erf names, whatever ERF's own initialization is,
358  // and there is no coarser level for the other branch (interp_from_lev0) to take it
359  // from. This used to follow only from ERF::nc_init_file's static default being
360  // {{""}} -- one empty string at level 0, so the vector below is never empty there,
361  // with or without erf.nc_init_file_0 -- which is how an idealized run could use
362  // Noah-MP at all. Say it outright so it does not rest on that placeholder.
363  // A finer level runs the driver only if it has an init file of its own.
364  m_has_nc_file = (lev == 0) || (!nc_init_file[lev].empty());
365  if (m_has_nc_file) {
366  Print() << "Noah-MP initialization started" << std::endl;
367 
368  noahmp_preflight(lev);
369 
370  // Size noahmpio_vect to the local boxes. A rank owning no boxes leaves it
371  // empty and relies on the class-level m_itimestep/m_dtbl instead.
372  if (cons_in.local_size() > 0) {
373  noahmpio_vect.resize(cons_in.local_size(), lev);
374  }
375 
376  // Pinned buffer space for all the boxes
377  noahmp_input_tmp.resize(cons_in.local_size());
378  noahmp_output_tmp.resize(cons_in.local_size());
379 
380  int klo = domain.smallEnd(2);
381 
382  // Iterate over the multifab and noahmpio objects together, using the
383  // multifab to set the per-box bounds on each noahmpio object.
384  int idb = 0;
385  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi, ++idb) {
386 
387  Box bx = mfi.tilebox();
388 
389  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(bx.smallEnd(2) == klo,
390  "NoahMP Init: box does not start at klo; z-decomposed grids are unsupported.");
391 
392  bx.makeSlab(2,klo);
393 
394  // Pinned buffers per box; output carries the 2D outputs + 3 soil groups.
395  noahmp_input_tmp[idb] = std::make_unique<FArrayBox>(bx, NoahmpInputComp::NumComps , The_Pinned_Arena());
396  noahmp_output_tmp[idb] = std::make_unique<FArrayBox>(bx, NoahmpOutputComp::NumComps + m_num_soil_groups*m_nsoil, The_Pinned_Arena());
397 
398  NoahmpIO_type* noahmpio = &noahmpio_vect[idb];
399 
400  noahmpio->blkid = idb;
401  noahmpio->level = lev;
402  noahmpio->ScalarInitDefault();
403  noahmpio->rank = ParallelDescriptor::MyProc();
404  noahmpio->comm = MPI_Comm_c2f(ParallelDescriptor::Communicator());
405 
406  // namelist.erf holds noahmpio-specific parameters, read Fortran-side.
407  noahmpio->ReadNamelist();
408 
409  // Assert namelist NSOIL matches erf.lsm_nsoil (used to size the fabs) so a
410  // mismatch fails loudly rather than truncating the soil diagnostics.
411  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(noahmpio->nsoil == m_nsoil,
412  "namelist.erf NSOIL does not match erf.lsm_nsoil (default 4); "
413  "set erf.lsm_nsoil to the Noah-MP soil-layer count");
414 
415  // NetCDF land-file headers (also Fortran-side)
416  noahmpio->ReadLandHeader();
417 
418  // Set domain/memory/tile bounds from the tile. All three are set to the
419  // same bounds for now; may change for special memory management later.
420  noahmpio->xstart = bx.smallEnd(0);
421  noahmpio->xend = bx.bigEnd(0);
422  noahmpio->ystart = bx.smallEnd(1);
423  noahmpio->yend = bx.bigEnd(1);
424 
425  // Domain, tile, and memory bounds are all equal for now (single-slab model).
426  auto set_grid_bounds = [](NoahmpIO_type* io, int x0, int x1, int y0, int y1) {
427  io->ids=io->its=io->ims=x0; io->ide=io->ite=io->ime=x1;
428  io->jds=io->jts=io->jms=y0; io->jde=io->jte=io->jme=y1;
429  io->kds=io->kts=io->kms=1; io->kde=io->kte=io->kme=2;
430  };
431  set_grid_bounds(noahmpio, noahmpio->xstart, noahmpio->xend, noahmpio->ystart, noahmpio->yend);
432 
433  // Allocate Fortran IO memory from the bounds above + namelist/header info
434  noahmpio->VarInitDefault();
435 
436  // NoahmpTable.TBL input
437  noahmpio->ReadTable();
438 
439  // Read/initialize from the NetCDF land file
440  noahmpio->ReadLandMain();
441 
442  // Compute initial values not supplied by the land file
443  noahmpio->InitMain();
444  }
445 
446  // Initial land plotfile (tag 0); must run on the land-owning subcomm, not the
447  // full comm, or a land-free rank never joins the collective nf90_create.
448  Print() << "Noah-MP writing lnd.nc file at lev: " << lev << std::endl;
449  with_land_comm([](NoahmpIO_type& noahmpio) { noahmpio.WriteLand(0); });
450 
451  // Broadcast DTBL and the initial substep counter so the firing decision is
452  // identical on every rank. Land-free ranks use max-reduction-losing sentinels.
453  m_dtbl = noahmpio_vect.empty() ? std::numeric_limits<Real>::lowest()
454  : static_cast<Real>(noahmpio_vect[0].DTBL);
455  m_itimestep = noahmpio_vect.empty() ? std::numeric_limits<int>::lowest()
456  : noahmpio_vect[0].itimestep;
457  ParallelDescriptor::ReduceRealMax(m_dtbl);
458  ParallelDescriptor::ReduceIntMax(m_itimestep);
459 
460  // Guard against a decomposition in which no rank owns a land box.
461  // DTBL is real(NOAH_TIMESTEP) from namelist.erf, and NOAH_TIMESTEP defaults to
462  // -9999 in the driver, so this is what an absent NOAH_TIMESTEP looks like. (It is
463  // not about land: noahmpio_vect is sized by boxes, land or not, so every rank that
464  // owns a box contributes a DTBL to the max.)
466  "Noah-MP: the Noah-MP timestep is not positive. Set NOAH_TIMESTEP (in seconds, "
467  "an integer) in namelist.erf.");
468  // The ERF-step-versus-NOAH_TIMESTEP constraint is enforced in Advance_With_State:
469  // the dt this function receives is a placeholder zero (see the top of Init), so a
470  // check against it here -- there used to be one -- could never fire.
471 
472  Print() << "Noah-MP initialization completed" << std::endl;
473  } // has nc_init_file
474 
475 };
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
#define NOAHMP_LSMDATA_FIELDS(X)
Definition: ERF_NOAHMP_Fields.H:8
#define NOAHMP_QUOTE(comp)
Definition: ERF_NOAHMP_Fields.H:100
amrex::Real Real
Definition: ERF_ShocInterface.H:19
amrex::Vector< amrex::BCRec > m_domain_bcs_type
Definition: ERF_NOAHMP.H:363
amrex::Vector< std::string > LsmDataName
Definition: ERF_NOAHMP.H:344
amrex::Vector< int > LsmDataMap
Definition: ERF_NOAHMP.H:338
int soil_data_idx(int g, int k) const
Definition: ERF_NOAHMP.H:300
int m_nsoil
Definition: ERF_NOAHMP.H:398
amrex::IntVect m_refRatio
Definition: ERF_NOAHMP.H:360
int m_lsm_data_size
Definition: ERF_NOAHMP.H:332
void with_land_comm(const std::function< void(NoahmpIO_type &)> &fn) const
Definition: ERF_NOAHMP_IO.cpp:19
int m_lev
Definition: ERF_NOAHMP.H:417
amrex::Geometry m_lsm_geom
Definition: ERF_NOAHMP.H:356
amrex::Geometry m_geom0
Definition: ERF_NOAHMP.H:353
int m_nz_lsm
Definition: ERF_NOAHMP.H:378
amrex::Vector< amrex::MultiFab * > lsm_lev0_data
Definition: ERF_NOAHMP.H:391
amrex::Vector< int > LsmFluxMap
Definition: ERF_NOAHMP.H:341
static constexpr int m_num_soil_groups
Definition: ERF_NOAHMP.H:296
void m_ensure_nsoil_resolved()
Definition: ERF_NOAHMP.H:307
amrex::Vector< std::string > LsmFluxName
Definition: ERF_NOAHMP.H:347
amrex::Real m_dz_lsm
Definition: ERF_NOAHMP.H:381
@ NumVars
Definition: ERF_NOAHMP_Fields.H:110
@ t_flux
Definition: ERF_NOAHMP_Fields.H:119
@ tau13
Definition: ERF_NOAHMP_Fields.H:121
@ q_flux
Definition: ERF_NOAHMP_Fields.H:120
@ tau23
Definition: ERF_NOAHMP_Fields.H:122
@ ng
Definition: ERF_Morrison.H:50
@ NumComps
Definition: ERF_NOAHMP_Fields.H:150
@ NumComps
Definition: ERF_NOAHMP_Fields.H:166
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19
Here is the call graph for this function:

◆ Init() [3/3]

virtual void NullSurf::Init
inline
46  {
47  Init(lev, cons_in, geom, geom0,
48  domain_bcs_type, refRatio, dt_advance,
49  nc_init_file);
50  }
virtual void Init(const int &, const amrex::MultiFab &, const amrex::Geometry &, const amrex::Geometry &, amrex::Vector< amrex::BCRec > &, amrex::IntVect &, const amrex::Real &, amrex::Vector< amrex::Vector< std::string >> &)
Definition: ERF_NullSurf.H:27

◆ interp_from_lev0()

void NOAHMP::interp_from_lev0 ( const int &  lev,
amrex::MultiFab &  cons_in,
const int &  nstep 
)
28 {
29  amrex::ignore_unused(cons_in);
30 
31  Print () << "Noah-MP interpolation at level " << lev << " started at time step: " << nstep+1 << std::endl;
32  m_updated = true;
33 
34  //
35  // These fields have no vertical extent: Init collapses them to a single z index at
36  // every level and carries the soil profile in components rather than in cells, so
37  // there is nothing in the vertical for the interpolation to do. It must therefore be
38  // handed a ratio of 1 in z even when the levels really are refined in the vertical --
39  // a ratio greater than 1 makes the interpolater ask for a layer of coarse ghost cells
40  // in z that these MultiFabs do not, and cannot, carry.
41  //
42  // NOTE: m_refRatio is the ratio between level 0 and this level, which is what we need
43  // since we interpolate from level 0 (m_geom0) rather than from the parent.
44  //
45  const IntVect rr2d(m_refRatio[0], m_refRatio[1], 1);
46 
47  for (int ivar(0); ivar<m_lsm_data_size; ++ivar) {
48  InterpFromCoarseLevel(*lsm_fab_data[ivar], lsm_fab_data[ivar]->nGrowVect(),
49  IntVect(0,0,0), // do NOT fill ghost cells outside the domain
50  *lsm_lev0_data[ivar], 0, 0, 1,
51  m_geom0, m_geom,
52  rr2d, &cell_cons_interp,
54  }
55 
56  // SurfaceLayer filled the noah flux where lsm_undefined existed, so the
57  // coarse-grid flux is complete and safe to interpolate.
58  for (int ivar(0); ivar<LsmFlux_NOAHMP::NumVars; ++ivar) {
59  InterpFromCoarseLevel(*lsm_fab_flux[ivar], lsm_fab_flux[ivar]->nGrowVect(),
60  IntVect(0,0,0), // do NOT fill ghost cells outside the domain
61  *lsm_lev0_flux[ivar], 0, 0, 1,
62  m_geom0, m_geom,
63  rr2d, &cell_cons_interp,
65  }
66  Print () << "Noah-MP interpolation at level " << lev << " completed" << std::endl;
67 }
amrex::Array< amrex::MultiFab *, LsmFlux_NOAHMP::NumVars > lsm_lev0_flux
Definition: ERF_NOAHMP.H:394
@ cons_bc
Definition: ERF_IndexDefines.H:92

◆ Lsm_Data_Ptr()

amrex::MultiFab* NOAHMP::Lsm_Data_Ptr ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

114  {
115  int lsmIdx = LsmDataMap[varIdx];
116  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_data_size && lsmIdx>=0);
117  return lsm_fab_data[lsmIdx].get();
118  }
Here is the call graph for this function:

◆ Lsm_Data_Size()

int NOAHMP::Lsm_Data_Size ( )
inlineoverridevirtual

Reimplemented from NullSurf.

Here is the call graph for this function:

◆ Lsm_DataIndex()

int NOAHMP::Lsm_DataIndex ( const std::string &  varname)
inlineoverridevirtual

Reimplemented from NullSurf.

154  {
155  int varIdx = -1;
156  std::string lc_varname = amrex::toLower(varname);
157  for (int idx(0); idx<m_lsm_data_size; ++idx) {
158  if (lc_varname == amrex::toLower(LsmDataName[idx])) {
159  varIdx = idx;
160  }
161  }
162  return varIdx;
163  }
Here is the call graph for this function:

◆ Lsm_DataName()

std::string NOAHMP::Lsm_DataName ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

145  {
146  int lsmIdx = LsmDataMap[varIdx];
147  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_data_size && lsmIdx>=0);
148  return LsmDataName[lsmIdx];
149  }
Here is the call graph for this function:

◆ Lsm_Flux_Ptr()

amrex::MultiFab* NOAHMP::Lsm_Flux_Ptr ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

123  {
124  int lsmIdx = LsmFluxMap[varIdx];
125  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_flux_size && lsmIdx>=0);
126  return lsm_fab_flux[lsmIdx].get();
127  }
Here is the call graph for this function:

◆ Lsm_Flux_Size()

int NOAHMP::Lsm_Flux_Size ( )
inlineoverridevirtual

Reimplemented from NullSurf.

140 { return NOAHMP::m_lsm_flux_size; }
int m_lsm_flux_size
Definition: ERF_NOAHMP.H:335

◆ Lsm_FluxIndex()

int NOAHMP::Lsm_FluxIndex ( const std::string &  varname)
inlineoverridevirtual

Reimplemented from NullSurf.

177  {
178  int varIdx = -1;
179  std::string lc_varname = amrex::toLower(varname);
180  for (int idx(0); idx<LsmFlux_NOAHMP::NumVars; ++idx) {
181  if (lc_varname == amrex::toLower(LsmFluxName[idx])) {
182  varIdx = idx;
183  }
184  }
185  return varIdx; // -1 if this model does not carry that flux; callers probe with this
186  }
Here is the call graph for this function:

◆ Lsm_FluxName()

std::string NOAHMP::Lsm_FluxName ( const int &  varIdx)
inlineoverridevirtual

Reimplemented from NullSurf.

168  {
169  int lsmIdx = LsmFluxMap[varIdx];
170  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_flux_size && lsmIdx>=0);
171  return LsmFluxName[lsmIdx];
172  }
Here is the call graph for this function:

◆ Lsm_Geom()

amrex::Geometry NOAHMP::Lsm_Geom ( )
inlineoverridevirtual

Reimplemented from NullSurf.

131 { return m_lsm_geom; }

◆ Lsm_Set_Lev0_Data_Ptr()

void NOAHMP::Lsm_Set_Lev0_Data_Ptr ( const int &  varIdx,
amrex::MultiFab *  lev0_data_ptr 
)
inlineoverridevirtual

Reimplemented from NullSurf.

191  {
192  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < m_lsm_data_size);
193  lsm_lev0_data[varIdx] = lev0_data_ptr;
194  }
Here is the call graph for this function:

◆ Lsm_Set_Lev0_Flux_Ptr()

void NOAHMP::Lsm_Set_Lev0_Flux_Ptr ( const int &  varIdx,
amrex::MultiFab *  lev0_flux_ptr 
)
inlineoverridevirtual

Reimplemented from NullSurf.

199  {
200  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < int(LsmFlux_NOAHMP::NumVars));
201  lsm_lev0_flux[varIdx] = lev0_flux_ptr;
202  }
Here is the call graph for this function:

◆ m_ensure_nsoil_resolved()

void NOAHMP::m_ensure_nsoil_resolved ( )
inline
307  {
308  if (m_nsoil_resolved) { return; }
309  amrex::ParmParse pp_nsoil("erf");
310  pp_nsoil.queryAdd("lsm_nsoil", m_nsoil);
311  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_nsoil >= 1, "erf.lsm_nsoil must be >= 1");
313  m_nsoil_resolved = true;
314  }
bool m_nsoil_resolved
Definition: ERF_NOAHMP.H:306

Referenced by Lsm_Data_Size().

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

◆ m_precip_snapshot_name()

static std::string NOAHMP::m_precip_snapshot_name ( const std::string &  dir,
int  l,
int  s 
)
inlinestatic
318  {
319  return dir + "/precip_accum_prev_Level_" + std::to_string(l)
320  + "_slot_" + std::to_string(s);
321  }

◆ Plot_Landfile()

void NOAHMP::Plot_Landfile ( const int &  nstep)
overridevirtual

Reimplemented from NullSurf.

47 {
48  with_land_comm([nstep](NoahmpIO_type& noahmpio) { noahmpio.WriteLand(nstep); });
49 }

◆ prepare_precip_snapshots()

void NOAHMP::prepare_precip_snapshots ( const int &  lev,
const erf_noahmp::PrecipSlots &  precip 
)
40 {
41  if (!precip.have_any) { return; }
42 
43  if (int(m_precip_accum_prev.size()) <= lev) { m_precip_accum_prev.resize(lev+1); }
44  auto& prev = m_precip_accum_prev[lev];
45  for (int s(0); s < NoahmpPrecipSlot::NumSlots; ++s) {
46  if (precip.has[s] && prev[s] == nullptr) {
47  // Zero ghost cells + setVal(0) so no cell is uninitialized (garbage prev
48  // -> ~1e25 deltas -> Noah-MP water-balance abort); only the slab is read.
49  prev[s] = std::make_unique<MultiFab>(
50  precip.accum[s]->boxArray(), precip.accum[s]->DistributionMap(), 1, 0);
51  prev[s]->setVal(0.0);
52  if (m_precip_accum_restored[s]) {
53  // Restart: ParallelCopy handles a changed decomposition; consume once.
54  prev[s]->ParallelCopy(*m_precip_accum_restored[s], 0, 0, 1);
55  m_precip_accum_restored[s].reset();
56  } else {
57  MultiFab::Copy(*prev[s], *precip.accum[s], 0, 0, 1, 0);
58  }
59  }
60  }
61 }
amrex::Array< std::unique_ptr< amrex::MultiFab >, NoahmpPrecipSlot::NumSlots > m_precip_accum_restored
Definition: ERF_NOAHMP.H:414

◆ Read_Lsm_Restart()

void NOAHMP::Read_Lsm_Restart ( const std::string &  dir)
overridevirtual

Reimplemented from NullSurf.

74 {
75  // Same collective-scope split as Write_Lsm_Restart.
76  with_land_comm([&dir](NoahmpIO_type& noahmpio) { noahmpio.ReadRestart(dir); });
77 
78  // Read saved precip snapshots into a staging store; the first Advance copies them
79  // into the live snapshots. Legacy checkpoints lack the files -> Advance cold-seeds.
80  for (int s(0); s < NoahmpPrecipSlot::NumSlots; ++s) {
81  const std::string name = m_precip_snapshot_name(dir, m_lev, s);
82  if (amrex::FileExists(name + "_H")) {
83  auto mf = std::make_unique<amrex::MultiFab>();
84  amrex::VisMF::Read(*mf, name);
85  m_precip_accum_restored[s] = std::move(mf);
86  }
87  }
88 }
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:105
static std::string m_precip_snapshot_name(const std::string &dir, int l, int s)
Definition: ERF_NOAHMP.H:318

◆ read_results()

void NOAHMP::read_results ( const amrex::MFIter &  mfi,
const erf_noahmp::NoahmpBlockViews &  blk,
const amrex::Box &  bx,
const amrex::Box &  gbx,
amrex::MultiFab &  cons_in 
)
248 {
249  NoahmpIO_type* noahmpio = blk.io;
250 
251  // For limiting when populating ghost cells
252  int i_lo = bx.smallEnd(0); int i_hi = bx.bigEnd(0);
253  int j_lo = bx.smallEnd(1); int j_hi = bx.bigEnd(1);
254 
255  const Array4<const Real>& CONS = cons_in.const_array(mfi);
256 
257  // Mechanical result destinations (RRTMGP coupling + return-term diagnostics),
258  // bound from NOAHMP_RESULT_FIELDS to stay in sync with the scatter below.
259 #define NOAHMP_BIND_RESULT(alias,lsm,out) Array4<Real> alias = lsm_fab_data[LsmData_NOAHMP::lsm]->array(mfi);
261 #undef NOAHMP_BIND_RESULT
262  // Sentinels (this core does not compute them; always lsm_undefined below).
263  Array4<Real> SMSTAV_o = lsm_fab_data[LsmData_NOAHMP::smstav]->array(mfi);
264  Array4<Real> SMSTOT_o = lsm_fab_data[LsmData_NOAHMP::smstot]->array(mfi);
265 
266  // Per-layer soil output fabs (3 groups x m_nsoil) in a device-visible vector so
267  // the scatter loops over any NSOIL. Layout: g in {0:smois,1:sh2o,2:tslb}, k in [0,nsoil).
268  const int nsoil = m_nsoil;
269  const int n_soil_fld = m_num_soil_groups*nsoil;
270  Gpu::DeviceVector<Array4<Real>> soil_arr_d(n_soil_fld);
271  {
272  Vector<Array4<Real>> soil_arr_h(n_soil_fld);
273  for (int g(0); g < 3; ++g) {
274  for (int k(0); k < nsoil; ++k) {
275  soil_arr_h[g*nsoil+k] = lsm_fab_data[soil_data_idx(g,k)]->array(mfi);
276  }
277  }
278  Gpu::copyAsync(Gpu::hostToDevice, soil_arr_h.begin(), soil_arr_h.end(), soil_arr_d.begin());
279  Gpu::streamSynchronize();
280  }
281  Array4<Real>* soil_arr = soil_arr_d.data();
282  const int soil_out_base = NoahmpOutputComp::NumComps; // soil outputs start here
283 
284  // SurfaceLayer flux destinations
285  Array4<Real> q_flux_arr = lsm_fab_flux[LsmFlux_NOAHMP::q_flux]->array(mfi);
286  Array4<Real> t_flux_arr = lsm_fab_flux[LsmFlux_NOAHMP::t_flux]->array(mfi);
287  Array4<Real> tau13_arr = lsm_fab_flux[LsmFlux_NOAHMP::tau13]->array(mfi);
288  Array4<Real> tau23_arr = lsm_fab_flux[LsmFlux_NOAHMP::tau23]->array(mfi);
289 
290  Array4<Real> noah_output_arr = blk.output;
291 
292  // (5) Copy NoahmpIO results into the pinned output buffer; banded albedos and
293  // the soil profile are read explicitly. SMSTAV/SMSTOT are emitted lsm_undefined.
294  LoopOnCpu(bx, [&] (int i, int j, int ) noexcept
295  {
296 #define NOAHMP_READ_OUT_2D(comp,member) noah_output_arr(i,j,0,NoahmpOutputComp::comp) = noahmpio->member(i,j);
298 #undef NOAHMP_READ_OUT_2D
299  // Banded radiation reads (VIS=1, NIR=2)
300  noah_output_arr(i,j,0,NoahmpOutputComp::albsfcdir_vis) = noahmpio->ALBSFCDIRXY(i,1,j);
301  noah_output_arr(i,j,0,NoahmpOutputComp::albsfcdir_nir) = noahmpio->ALBSFCDIRXY(i,2,j);
302  noah_output_arr(i,j,0,NoahmpOutputComp::albsfcdif_vis) = noahmpio->ALBSFCDIFXY(i,1,j);
303  noah_output_arr(i,j,0,NoahmpOutputComp::albsfcdif_nir) = noahmpio->ALBSFCDIFXY(i,2,j);
304  // Soil profile, one 2D component per layer (Fortran layer index is 1-based).
305  for (int k(0); k < nsoil; ++k) {
306  noah_output_arr(i,j,0,soil_out_base + 0*nsoil + k) = noahmpio->SMOIS(i,k+1,j);
307  noah_output_arr(i,j,0,soil_out_base + 1*nsoil + k) = noahmpio->SH2O (i,k+1,j);
308  noah_output_arr(i,j,0,soil_out_base + 2*nsoil + k) = noahmpio->TSLB (i,k+1,j);
309  }
310  });
311 
312  // (6) Copy Noahmp results to ERF (device; from pinned buffer): per-field math
313  // (flux ÷rho, -9999 fill guard, sentinels, soil loop), deliberately not table-driven.
314  ParallelFor(gbx, [=,Cp_d_d=Cp_d,L_v_d=L_v,lsm_undefined_d=lsm_undefined]
315  AMREX_GPU_DEVICE (int i, int j, int k) noexcept
316  {
317  // Limit indices to the valid box. FillBoundary will pick these up below.
318  int ii = std::min(std::max(i,i_lo),i_hi);
319  int jj = std::min(std::max(j,j_lo),j_hi);
320 
321  // Noah-MP returns -9999 for cells it does not process (sea-ice/open-water);
322  // HFX is Noah-MP's processed-cell gate; it is not a statement that
323  // every physical output is valid. tau13/tau23 are nodal in xz/yz;
324  // the 2D MFs carry 1 ghost cell for averaging.
325  const Real hfx_lsm = noah_output_arr(ii,jj,0,NoahmpOutputComp::hfx);
326  const noahmp_result_policy::CellPolicy cell_policy(hfx_lsm);
327  // Noah-MP returns HFX,LH [W/m2] and TAU [N/m2] (rho included); divide by
328  // rho for the kinematic MOST form ERF stores. Exner factor on t_flux is
329  // omitted to match WRF (<~1% error near surface, ~6-7% at p~800 hPa).
330  const Real rho_l = CONS(ii,jj,k,Rho_comp);
331  const auto fluxes = cell_policy.select_fluxes(
332  noah_output_arr(ii,jj,0,NoahmpOutputComp::lh),
333  noah_output_arr(ii,jj,0,NoahmpOutputComp::tau_ew),
334  noah_output_arr(ii,jj,0,NoahmpOutputComp::tau_ns),
335  rho_l, Cp_d_d, L_v_d, lsm_undefined_d);
336  t_flux_arr(i,j,k) = fluxes.temperature;
337  q_flux_arr(i,j,k) = fluxes.mixing_ratio;
338  tau13_arr(i,j,k) = fluxes.tau_ew;
339  tau23_arr(i,j,k) = fluxes.tau_ns;
340 
341  // Processed cells retain field-specific validity checks. Unprocessed
342  // cells invalidate the complete provider result set.
343  if (cell_policy.processed) {
344 #define NOAHMP_COPY_RESULT(alias,lsm,out) \
345  { \
346  const Real value = noah_output_arr(ii,jj,0,NoahmpOutputComp::out); \
347  alias(i,j,0) = cell_policy.select( \
348  value, lsm_undefined_d, NoahmpOutputComp::out); \
349  }
351 #undef NOAHMP_COPY_RESULT
352  // Not computed by this core -> sentinel, not a misleading 0.
353  SMSTAV_o(i,j,0) = lsm_undefined_d;
354  SMSTOT_o(i,j,0) = lsm_undefined_d;
355  for (int s(0); s < n_soil_fld; ++s) {
356  const Real value = noah_output_arr(ii,jj,0,soil_out_base + s);
357  soil_arr[s](i,j,0) = cell_policy.select(value, lsm_undefined_d, -1);
358  }
359  } else {
360 #define NOAHMP_UNDEF_RESULT(alias,lsm,out) alias(i,j,0) = lsm_undefined_d;
362 #undef NOAHMP_UNDEF_RESULT
363  SMSTAV_o(i,j,0) = lsm_undefined_d;
364  SMSTOT_o(i,j,0) = lsm_undefined_d;
365  for (int s(0); s < n_soil_fld; ++s) {
366  soil_arr[s](i,j,0) = lsm_undefined_d;
367  }
368  }
369  });
370  // The scatter kernel reads soil_arr_d; sync before it is destroyed.
371  Gpu::streamSynchronize();
372 }
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:48
constexpr amrex::Real L_v
Definition: ERF_Constants.H:63
#define Rho_comp
Definition: ERF_IndexDefines.H:42
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);})
#define NOAHMP_READ_OUT_2D(comp, member)
#define NOAHMP_COPY_RESULT(alias, lsm, out)
#define NOAHMP_UNDEF_RESULT(alias, lsm, out)
#define NOAHMP_BIND_RESULT(alias, lsm, out)
#define NOAHMP_RESULT_FIELDS(X)
Definition: ERF_NOAHMP_Fields.H:75
#define NOAHMP_OUTPUT_2D_FIELDS(X)
Definition: ERF_NOAHMP_Fields.H:70
@ albsfcdir_vis
Definition: ERF_NOAHMP_Fields.H:158
@ albsfcdif_nir
Definition: ERF_NOAHMP_Fields.H:161
@ albsfcdif_vis
Definition: ERF_NOAHMP_Fields.H:160
@ albsfcdir_nir
Definition: ERF_NOAHMP_Fields.H:159
NoahmpIO_type * io
Definition: ERF_NOAHMP.H:51
amrex::Array4< amrex::Real > output
Definition: ERF_NOAHMP.H:53
Definition: ERF_NOAHMP_ResultPolicy.H:60
Here is the call graph for this function:

◆ report_precip_diagnostics()

void NOAHMP::report_precip_diagnostics ( const int &  lev,
const amrex::Vector< erf_noahmp::ClampedPrecipCell > &  clamped_cells,
const amrex::Vector< erf_noahmp::InvariantPrecipCell > &  invariant_cells 
)
83 {
84  if (!clamped_cells.empty()) {
85  const int nshow = amrex::min(int(clamped_cells.size()), 50);
86  amrex::AllPrint aprint;
87  aprint << "WARNING: NOAHMP precip guard (level " << lev << "): clamped "
88  << clamped_cells.size() << " non-physical interval-precip cell(s) (> "
89  << lsm_max_precip_interval << " mm):\n";
90  for (int c(0); c < nshow; ++c) {
91  aprint << " (i,j)=(" << clamped_cells[c].i << "," << clamped_cells[c].j
92  << ") raw=" << clamped_cells[c].raw_mm << " mm\n";
93  }
94  if (int(clamped_cells.size()) > nshow) {
95  aprint << " ... and " << (int(clamped_cells.size()) - nshow) << " more\n";
96  }
97  }
98 
99  if (!invariant_cells.empty()) {
100  const int nshow = amrex::min(int(invariant_cells.size()), 50);
101  amrex::AllPrint aprint;
102  aprint << "WARNING: NOAHMP precip invariant (level " << lev << "): rescaled frozen "
103  << "components in " << invariant_cells.size() << " cell(s) where "
104  << "MP_SNOW+MP_GRAUP exceeded MP_RAINNC:\n";
105  for (int c(0); c < nshow; ++c) {
106  aprint << " (i,j)=(" << invariant_cells[c].i << "," << invariant_cells[c].j
107  << ") frozen=" << invariant_cells[c].froz_mm
108  << " mm > total=" << invariant_cells[c].total_mm << " mm\n";
109  }
110  if (int(invariant_cells.size()) > nshow) {
111  aprint << " ... and " << (int(invariant_cells.size()) - nshow) << " more\n";
112  }
113  }
114 }
constexpr amrex::Real lsm_max_precip_interval
Definition: ERF_Constants.H:43

◆ Set_LSM_Step()

void NOAHMP::Set_LSM_Step ( int  step)
inlineoverridevirtual

Reimplemented from NullSurf.

217  {
218  m_itimestep = step;
219  for (auto& noahmpio : noahmpio_vect) {
220  noahmpio.itimestep = step;
221  }
222  }

◆ soil_data_idx()

int NOAHMP::soil_data_idx ( int  g,
int  k 
) const
inline
300  {
301  return LsmData_NOAHMP::NumVars + g*m_nsoil + k;
302  }

◆ stage_forcing()

void NOAHMP::stage_forcing ( const amrex::MFIter &  mfi,
const erf_noahmp::NoahmpBlockViews &  blk,
const amrex::Box &  bx,
const int &  klo,
const int &  lev,
const bool  is_moist,
amrex::MultiFab &  cons_in,
amrex::MultiFab &  xvel_in,
amrex::MultiFab &  yvel_in,
amrex::MultiFab *  z_nd,
const erf_noahmp::PrecipSlots &  precip,
amrex::Vector< erf_noahmp::ClampedPrecipCell > &  clamped_cells,
amrex::Vector< erf_noahmp::InvariantPrecipCell > &  invariant_cells 
)
104 {
105  NoahmpIO_type* noahmpio = blk.io;
106 
107  const Array4<const Real>& U_PHY = xvel_in.const_array(mfi);
108  const Array4<const Real>& V_PHY = yvel_in.const_array(mfi);
109  const Array4<const Real>& CONS = cons_in.const_array(mfi);
110 
111  const Array4<const Real> z_nd_arr = (z_nd) ? z_nd->const_array(mfi) :
112  Array4<const Real> {};
113 
114  // Forcing pulled from the coupling data fields
115  const Array4<const Real>& SWDOWN = lsm_fab_data[LsmData_NOAHMP::sw_flux_dn]->const_array(mfi);
116  const Array4<const Real>& GLW = lsm_fab_data[LsmData_NOAHMP::lw_flux_dn]->const_array(mfi);
117  const Array4<const Real>& COSZEN = lsm_fab_data[LsmData_NOAHMP::cos_zenith_angle]->const_array(mfi);
118 
119  // Pinned (host-accessible, GPU-usable) staging buffer
120  Array4<Real> noah_input_arr = blk.input;
121 
122  // Per-slot precip views (current + previous snapshot) and their native->kg/m^2
123  // factors. Read only inside the device kernel; absent slots guarded by slot_present.
124  const bool hp = precip.have_any;
125  GpuArray<Array4<const Real>, NoahmpPrecipSlot::NumSlots> accum_now, accum_prv;
126  GpuArray<Real, NoahmpPrecipSlot::NumSlots> accum_fac;
127  GpuArray<int, NoahmpPrecipSlot::NumSlots> slot_present;
128  for (int s(0); s < NoahmpPrecipSlot::NumSlots; ++s) {
129  slot_present[s] = (hp && precip.has[s]) ? 1 : 0;
130  accum_fac[s] = precip.factor[s];
131  if (slot_present[s]) {
132  accum_now[s] = precip.accum[s]->const_array(mfi);
133  accum_prv[s] = m_precip_accum_prev[lev][s]->const_array(mfi);
134  }
135  }
136  const int kklo = klo;
137 
138  // (1) Stage ERF forcing into the pinned buffer (device).
139  Real zref_default = noahmpio->ZLVL;
140  ParallelFor(bx, [=,zero_d=zero] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
141  {
142  Real qv = (is_moist) ? CONS(i,j,k,RhoQ1_comp)/CONS(i,j,k,Rho_comp) : zero_d;
143  noah_input_arr(i,j,0,NoahmpInputComp::u_phy) = myhalf*(U_PHY(i,j,k)+U_PHY(i+1,j,k));
144  noah_input_arr(i,j,0,NoahmpInputComp::v_phy) = myhalf*(V_PHY(i,j,k)+V_PHY(i ,j+1,k));
145  noah_input_arr(i,j,0,NoahmpInputComp::t_phy) = getTgivenRandRTh(CONS(i,j,k,Rho_comp),CONS(i,j,k,RhoTheta_comp),qv);
146  noah_input_arr(i,j,0,NoahmpInputComp::qv_curr) = qv;
147  noah_input_arr(i,j,0,NoahmpInputComp::p8w) = getPgivenRTh(CONS(i,j,k,RhoTheta_comp),qv);
148  noah_input_arr(i,j,0,NoahmpInputComp::dz8w) = two * ((z_nd_arr) ? Compute_Zrel_AtCellCenter(i,j,kklo,z_nd_arr) : zref_default);
149  // The radiation inputs come from whichever radiation model writes them, and
150  // NOAHMP::Init setVal'd every lsm_fab_data to lsm_undefined (1e150 in double,
151  // 1e18 in single). A model that does not write them -- erf.radiation_model unset,
152  // or a model whose radiation -> land direction is not wired -- therefore leaves
153  // the sentinel here, and taking it raw had Noah-MP integrating on ~2e149 W/m^2 of
154  // shortwave while the run exited 0.
155  //
156  // Apply the shared validity test (is_valid_lsm_value in ERF_Constants.H, the
157  // same test RRTMGP applies to its own LSM inputs) and fall back to zero, which
158  // is what "no radiation reaching the surface" physically means.
159  {
160  const Real sw_in = SWDOWN(i,j,0);
161  const Real lw_in = GLW(i,j,0);
162  const Real cz_in = COSZEN(i,j,0);
163  noah_input_arr(i,j,0,NoahmpInputComp::swdown) = is_valid_lsm_value(sw_in) ? sw_in : zero_d;
164  noah_input_arr(i,j,0,NoahmpInputComp::glw) = is_valid_lsm_value(lw_in) ? lw_in : zero_d;
165  noah_input_arr(i,j,0,NoahmpInputComp::coszen) = is_valid_lsm_value(cz_in) ? cz_in : zero_d;
166  }
167 
168  // RAW water-equivalent interval precip [mm]: per slot d = max(0, (now-prev)
169  // * native_to_kg_m2). Host applies the guard and derives SR / MP_RAINNC.
170  Real drain = zero_d, dsnow = zero_d, dgraup = zero_d;
171  if (hp) {
173  for (int s(0); s < NoahmpPrecipSlot::NumSlots; ++s) {
174  dd[s] = slot_present[s]
175  ? amrex::max(zero_d, (accum_now[s](i,j,kklo) - accum_prv[s](i,j,kklo)) * accum_fac[s])
176  : zero_d;
177  }
178  // Hail is folded into MP_GRAUP (no ERF scheme fills the hail slot
179  // today); to feed it separately, stage dd[hail] into MP_HAIL instead.
180  dsnow = dd[NoahmpPrecipSlot::snow];
182  const Real dfroz = dsnow + dgraup;
183  // Total: scheme's `total` slot, else rain + frozen.
184  drain = slot_present[NoahmpPrecipSlot::total]
186  : (dd[NoahmpPrecipSlot::rain] + dfroz);
187  }
188  noah_input_arr(i,j,0,NoahmpInputComp::rainbl) = drain;
189  noah_input_arr(i,j,0,NoahmpInputComp::mp_snow) = dsnow;
190  noah_input_arr(i,j,0,NoahmpInputComp::mp_graup) = dgraup;
191  });
192 
193  // (2) Wait for the kernel before host access.
194  Gpu::streamSynchronize();
195 
196  // (3) Copy pinned staged data to NoahmpIO on the host (3D members take the k/j
197  // transpose (i,1,j), 2D take (i,j)); precip is derived below.
198  LoopOnCpu(bx, [&,zero_d=zero,one_d=one,lsm_max_precip_interval_d=lsm_max_precip_interval]
199  (int i, int j, int ) noexcept
200  {
201 #define NOAHMP_STAGE_IN_3D(comp,member) noahmpio->member(i,1,j) = noah_input_arr(i,j,0,NoahmpInputComp::comp);
202 #define NOAHMP_STAGE_IN_2D(comp,member) noahmpio->member(i,j) = noah_input_arr(i,j,0,NoahmpInputComp::comp);
205 #undef NOAHMP_STAGE_IN_3D
206 #undef NOAHMP_STAGE_IN_2D
207  // Guard the RAW total, then derive SR / MP_RAINNC. Clamped cells are
208  // reported after the box loop (never silent).
209  Real dsnow_h = noah_input_arr(i,j,0,NoahmpInputComp::mp_snow);
210  Real dgraup_h = noah_input_arr(i,j,0,NoahmpInputComp::mp_graup);
211  Real drain_h = noah_input_arr(i,j,0,NoahmpInputComp::rainbl);
212  if (drain_h > lsm_max_precip_interval_d) {
213  clamped_cells.push_back(erf_noahmp::ClampedPrecipCell{i, j, drain_h});
214  drain_h = lsm_max_precip_interval_d;
215  }
216  // Frozen/total invariant: MP_SNOW+MP_GRAUP are subsets of MP_RAINNC; if
217  // their sum exceeds it, rescale the frozen components down and record the cell.
218  Real dfroz_h = dsnow_h + dgraup_h;
219  const Real inv_tol = Real(1.0e-6) * (one_d + drain_h);
220  if (dfroz_h > drain_h + inv_tol) {
221  invariant_cells.push_back(erf_noahmp::InvariantPrecipCell{i, j, dfroz_h, drain_h});
222  if (dfroz_h > zero_d) {
223  const Real scale = drain_h / dfroz_h; // <= 1
224  dsnow_h *= scale;
225  dgraup_h *= scale;
226  dfroz_h = dsnow_h + dgraup_h; // == drain_h (to round-off)
227  }
228  }
229  noahmpio->RAINBL(i,j) = drain_h; // [mm]
230  noahmpio->SR(i,j) = (drain_h > zero_d) ? amrex::min(one_d, dfroz_h/drain_h) : zero_d; // [-]
231  noahmpio->MP_RAINNC(i,j) = drain_h; // [mm]
232  noahmpio->MP_SNOW(i,j) = dsnow_h; // [mm]
233  noahmpio->MP_GRAUP(i,j) = dgraup_h; // [mm] (includes folded hail)
234  // 0 = "no hail" (a real value, not a sentinel; lsm_undefined would corrupt opt_snf=4)
235  noahmpio->MP_HAIL(i,j) = zero_d; // [mm]
236  });
237 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool is_valid_lsm_value(amrex::Real v)
Definition: ERF_Constants.H:35
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
#define NOAHMP_STAGE_IN_2D(comp, member)
#define NOAHMP_STAGE_IN_3D(comp, member)
#define NOAHMP_INPUT_2D_FIELDS(X)
Definition: ERF_NOAHMP_Fields.H:38
#define NOAHMP_INPUT_3D_FIELDS(X)
Definition: ERF_NOAHMP_Fields.H:29
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Zrel_AtCellCenter(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:757
@ qv
Definition: ERF_Kessler.H:31
@ rainbl
Definition: ERF_NOAHMP_Fields.H:145
@ mp_graup
Definition: ERF_NOAHMP_Fields.H:149
@ mp_snow
Definition: ERF_NOAHMP_Fields.H:148
@ graupel
Definition: ERF_NOAHMP_Fields.H:134
@ rain
Definition: ERF_NOAHMP_Fields.H:132
@ total
Definition: ERF_NOAHMP_Fields.H:131
@ hail
Definition: ERF_NOAHMP_Fields.H:135
@ snow
Definition: ERF_NOAHMP_Fields.H:133
@ drain
Definition: ERF_SLM.H:137
Definition: ERF_NOAHMP.H:42
Definition: ERF_NOAHMP.H:46
amrex::Array4< amrex::Real > input
Definition: ERF_NOAHMP.H:52
Here is the call graph for this function:

◆ time_to_fire()

bool NOAHMP::time_to_fire ( const amrex::Real &  elapsed_time)
76 {
77  Real NOAH_time = static_cast<Real>(m_itimestep-1) * m_dtbl;
78  if (elapsed_time < NOAH_time) {
79  m_updated = false;
80  return false;
81  }
82 
83  m_updated = true;
84  return true;
85 }

◆ with_land_comm()

void NOAHMP::with_land_comm ( const std::function< void(NoahmpIO_type &)> &  fn) const
private
20 {
21  auto& blocks = const_cast<NoahmpIO_vector&>(noahmpio_vect);
22 #ifdef AMREX_USE_MPI
23  const int color = blocks.empty() ? MPI_UNDEFINED : 1;
24  MPI_Comm land_comm = MPI_COMM_NULL;
25  MPI_Comm_split(ParallelDescriptor::Communicator(), color,
26  ParallelDescriptor::MyProc(), &land_comm);
27  if (land_comm == MPI_COMM_NULL) return; // land-free rank
28  const int land_comm_f = static_cast<int>(MPI_Comm_c2f(land_comm));
29  for (NoahmpIO_type& noahmpio : blocks) {
30  const int saved = noahmpio.comm;
31  noahmpio.comm = land_comm_f;
32  fn(noahmpio);
33  noahmpio.comm = saved;
34  }
35  MPI_Comm_free(&land_comm);
36 #else
37  for (NoahmpIO_type& noahmpio : blocks) {
38  fn(noahmpio);
39  }
40 #endif
41 }

◆ Write_Lsm_Restart()

void NOAHMP::Write_Lsm_Restart ( const std::string &  dir) const
overridevirtual

Reimplemented from NullSurf.

55 {
56  // NetCDF (land-owning ranks) and VisMF below (full communicator) are separate
57  // collective scopes; a land-free early `return` here would skip the VisMF call.
58  with_land_comm([&dir](NoahmpIO_type& noahmpio) { noahmpio.WriteRestart(dir); });
59 
60  // Also save the precip snapshots so the next land call after restart differences
61  // against the same baseline (no precip dropped or double-counted).
62  if (m_lev < int(m_precip_accum_prev.size())) {
63  for (int s(0); s < NoahmpPrecipSlot::NumSlots; ++s) {
64  if (m_precip_accum_prev[m_lev][s]) {
65  amrex::VisMF::Write(*m_precip_accum_prev[m_lev][s],
66  m_precip_snapshot_name(dir, m_lev, s));
67  }
68  }
69  }
70 }

Member Data Documentation

◆ khi_lsm

int NOAHMP::khi_lsm = -1
private

◆ lsm_fab_data

amrex::Vector<FabPtr> NOAHMP::lsm_fab_data
private

Referenced by Lsm_Data_Ptr().

◆ lsm_fab_flux

amrex::Array<FabPtr, LsmFlux_NOAHMP::NumVars> NOAHMP::lsm_fab_flux
private

Referenced by Lsm_Flux_Ptr().

◆ lsm_lev0_data

amrex::Vector<amrex::MultiFab*> NOAHMP::lsm_lev0_data
private

Referenced by Lsm_Set_Lev0_Data_Ptr().

◆ lsm_lev0_flux

amrex::Array<amrex::MultiFab*, LsmFlux_NOAHMP::NumVars> NOAHMP::lsm_lev0_flux {}
private

Referenced by Lsm_Set_Lev0_Flux_Ptr().

◆ LsmDataMap

amrex::Vector<int> NOAHMP::LsmDataMap
private

Referenced by Lsm_Data_Ptr(), and Lsm_DataName().

◆ LsmDataName

amrex::Vector<std::string> NOAHMP::LsmDataName
private

Referenced by Lsm_DataIndex(), and Lsm_DataName().

◆ LsmFluxMap

amrex::Vector<int> NOAHMP::LsmFluxMap
private

Referenced by Lsm_Flux_Ptr(), and Lsm_FluxName().

◆ LsmFluxName

amrex::Vector<std::string> NOAHMP::LsmFluxName
private

Referenced by Lsm_FluxIndex(), and Lsm_FluxName().

◆ m_checked_radiation_inputs

bool NOAHMP::m_checked_radiation_inputs = false
private

◆ m_domain_bcs_type

amrex::Vector<amrex::BCRec> NOAHMP::m_domain_bcs_type
private

◆ m_dtbl

amrex::Real NOAHMP::m_dtbl = std::numeric_limits<amrex::Real>::max()
private

◆ m_dz_lsm

amrex::Real NOAHMP::m_dz_lsm = one
private

◆ m_first_advance_nstep

int NOAHMP::m_first_advance_nstep = -1
private

◆ m_geom

amrex::Geometry NOAHMP::m_geom
private

◆ m_geom0

amrex::Geometry NOAHMP::m_geom0
private

◆ m_has_nc_file

bool NOAHMP::m_has_nc_file = false
private

◆ m_itimestep

int NOAHMP::m_itimestep = 0
private

Referenced by Get_LSM_Step(), and Set_LSM_Step().

◆ m_lev

int NOAHMP::m_lev = 0
private

◆ m_lsm_data_size

◆ m_lsm_flux_size

int NOAHMP::m_lsm_flux_size = LsmFlux_NOAHMP::NumVars
private

Referenced by Lsm_Flux_Size().

◆ m_lsm_geom

amrex::Geometry NOAHMP::m_lsm_geom
private

Referenced by Lsm_Geom().

◆ m_nsoil

int NOAHMP::m_nsoil = 4
private

◆ m_nsoil_resolved

bool NOAHMP::m_nsoil_resolved = false

Referenced by m_ensure_nsoil_resolved().

◆ m_num_soil_groups

constexpr int NOAHMP::m_num_soil_groups = 3
staticconstexpr

Referenced by m_ensure_nsoil_resolved().

◆ m_nz_lsm

int NOAHMP::m_nz_lsm = 1
private

◆ m_precip_accum_prev

amrex::Vector<amrex::Array<std::unique_ptr<amrex::MultiFab>, NoahmpPrecipSlot::NumSlots> > NOAHMP::m_precip_accum_prev
private

◆ m_precip_accum_restored

amrex::Array<std::unique_ptr<amrex::MultiFab>, NoahmpPrecipSlot::NumSlots> NOAHMP::m_precip_accum_restored
private

◆ m_radiation_feeds_lsm

bool NOAHMP::m_radiation_feeds_lsm = false
private

Referenced by Define().

◆ m_radiation_model_name

std::string NOAHMP::m_radiation_model_name
private

Referenced by Define().

◆ m_refRatio

amrex::IntVect NOAHMP::m_refRatio
private

◆ m_updated

bool NOAHMP::m_updated = false
private

Referenced by Get_Update_Status().

◆ m_warned_radiation_inputs

bool NOAHMP::m_warned_radiation_inputs = false
private

◆ noahmp_input_tmp

amrex::Vector<std::unique_ptr<amrex::FArrayBox> > NOAHMP::noahmp_input_tmp
private

◆ noahmp_output_tmp

amrex::Vector<std::unique_ptr<amrex::FArrayBox> > NOAHMP::noahmp_output_tmp
private

◆ noahmpio_vect

NoahmpIO_vector NOAHMP::noahmpio_vect
private

Referenced by Set_LSM_Step().


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