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
 
bool Runs_Own_Land_Driver () const 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 bool Lsm_Has_Atmos_State ()
 
virtual void Lsm_Set_Source_Has_Atmos_State (bool)
 
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_idealized_init = 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:420
@ NumSlots
Definition: ERF_NOAHMP_Fields.H:142
@ 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.

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

◆ Get_LSM_Step()

int NOAHMP::Get_LSM_Step ( ) const
inlineoverridevirtual

Reimplemented from NullSurf.

221  {
222  return m_itimestep;
223  }

◆ Get_Update_Status()

bool NOAHMP::Get_Update_Status ( )
inlineoverridevirtual

Reimplemented from NullSurf.

215 { 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.

267 {
268  // Install Noah-MP's fatal-error handler once: route NoahmpIO_fatal() through
269  // amrex::Abort so a fatal error propagates via MPI_Abort. See NoahmpFatal.H.
270  static const bool noahmp_fatal_installed = []() {
271  // The driver's own aborts pass no message: it WRITEs the reason to standard output
272  // on rank 0 first, and that line is usually lost when the job is killed. NOAHMP::Init
273  // checks the files the driver opens beforehand (noahmp_preflight); what reaches
274  // this default is a problem inside one of them, so point at the likely ones.
275  NoahmpIO_set_fatal_handler([](const char* msg){
276  amrex::Abort(msg ? msg :
277  "Noah-MP fatal error in the Fortran driver. Its explanation is written to "
278  "standard output on rank 0 and is usually lost when the job stops; check for a "
279  "malformed namelist.erf (every *_TIMESTEP and *_OPTION is an integer), a "
280  "NoahmpTable.TBL that does not match the land-use scheme (MMINLU), or a land "
281  "setup file missing one of the fields Noah-MP reads.");
282  });
283  return true;
284  }();
285  amrex::ignore_unused(noahmp_fatal_installed);
286 
287  // Noah-MP's own physics checks end the run with a bare Fortran STOP (27 of them, e.g.
288  // "Error: Solar radiation budget problem in NoahMP LSM"), which exits with status 0.
289  // main() installs an atexit trap that turns any exit() while AMReX is still
290  // initialised into a failure status; see install_exit_trap in main.cpp.
291 
292  // dt is a placeholder: ERF::make_lsm_at_level passes zero, because the level's step is
293  // not known yet when the level is built. Nothing here may depend on it; the step is
294  // checked against NOAH_TIMESTEP in Advance_With_State, where it is real.
295  amrex::ignore_unused(dt);
296 
297  m_lev = lev;
298  m_geom = geom;
299  m_geom0 = geom0;
300 
301  // Init runs again when a level is rebuilt (a regrid, including the one a restart
302  // can trigger), and the setVal below re-installs the lsm_undefined sentinel in the
303  // radiation inputs; make Advance_With_State re-check them rather than trust a
304  // verdict from before the wipe, and let it see that this is a rebuild.
308 
309  // Likewise drop the precip snapshots: they carry the BoxArray and DistributionMapping
310  // of the accumulators at the time they were allocated, and a rebuilt level's
311  // accumulators may have new ones. prepare_precip_snapshots re-seeds them on the next
312  // driver call, from the checkpoint (m_precip_accum_restored) or from the current values.
313  m_precip_accum_prev.clear();
314 
315  m_domain_bcs_type = domain_bcs_type;
316  m_refRatio = refRatio;
317 
318  Box domain = geom.Domain();
319 
320  // Resolve NSOIL from erf.lsm_nsoil before building the collective LSM fabs (same
321  // value the parent used via Lsm_Data_Size()); namelist NSOIL asserted below.
323 
324  // The fixed 2D fields are identity-mapped; their names mirror the enum order.
325  LsmDataMap.resize(m_lsm_data_size);
327  for (int i(0); i < LsmData_NOAHMP::NumVars; ++i) { LsmDataMap[i] = i; }
328  {
329  // Names from the same registry as the enum, so they cannot drift.
330  const std::vector<std::string> fixed_names = {
332  };
333  AMREX_ALWAYS_ASSERT(int(fixed_names.size()) == LsmData_NOAHMP::NumVars);
334  for (int i(0); i < LsmData_NOAHMP::NumVars; ++i) { LsmDataName[i] = fixed_names[i]; }
335  }
336  // Per-layer soil profile: 3 groups of m_nsoil, layer index 1-based (WRF SMOIS_k).
337  {
338  const char* group[m_num_soil_groups] = {"smois", "sh2o", "tslb"};
339  for (int g(0); g < m_num_soil_groups; ++g) {
340  for (int k(0); k < m_nsoil; ++k) {
341  int idx = soil_data_idx(g,k);
342  LsmDataMap[idx] = idx;
343  LsmDataName[idx] = std::string(group[g]) + "_" + std::to_string(k+1);
344  }
345  }
346  }
347 
350  LsmFluxName = {"t_flux" , "q_flux" ,
351  "tau13" , "tau23" };
352 
353  // NOTE: relies on all boxes in ba spanning zlo..zhi; otherwise dm/ba no longer
354  // line up and lsm data/flux vars can't be copied directly in a parfor.
355 
356  // Set 2D box array for lsm data
357  IntVect ng(1,1,0);
358  BoxArray ba = cons_in.boxArray();
359  DistributionMapping dm = cons_in.DistributionMap();
360  BoxList bl_lsm = ba.boxList();
361  for (auto& b : bl_lsm) { b.setRange(2,0); }
362  BoxArray ba_lsm(std::move(bl_lsm));
363 
364  // Set up lsm geometry
365  const RealBox& dom_rb = m_geom.ProbDomain();
366  const Real* dom_dx = m_geom.CellSize();
367  RealBox lsm_rb = dom_rb;
368  Real lsm_dx[AMREX_SPACEDIM] = {AMREX_D_DECL(dom_dx[0],dom_dx[1],m_dz_lsm)};
369  Real lsm_z_hi = dom_rb.lo(2);
370  Real lsm_z_lo = lsm_z_hi - Real(m_nz_lsm)*lsm_dx[2];
371  lsm_rb.setHi(2,lsm_z_hi); lsm_rb.setLo(2,lsm_z_lo);
372  m_lsm_geom.define( ba_lsm.minimalBox(), lsm_rb, m_geom.Coord(), m_geom.isPeriodic() );
373 
374  // Create the data (CC), runtime-sized (fixed 2D fields + 3*m_nsoil) so soil scales
375  // with NSOIL. lsm_lev0_data pointers are populated later by the parent.
377  lsm_lev0_data.resize(m_lsm_data_size, nullptr);
378  for (auto ivar = 0; ivar < m_lsm_data_size; ++ivar) {
379  lsm_fab_data[ivar] = std::make_shared<MultiFab>(ba_lsm, dm, 1, ng);
380  lsm_fab_data[ivar]->setVal(lsm_undefined);
381  }
382 
383  // Create the fluxes (CC with ghost cells for averaging)
384  for (auto ivar = 0; ivar < LsmFlux_NOAHMP::NumVars; ++ivar) {
385  lsm_fab_flux[ivar] = std::make_shared<MultiFab>(ba_lsm, dm, 1, ng);
386  lsm_fab_flux[ivar]->setVal(lsm_undefined);
387  }
388 
389  // Level 0 always runs the Noah-MP driver: the driver reads its land state from the
390  // file ERF_SETUP_FILE_01 in namelist.erf names, whatever ERF's own initialization is,
391  // and there is no coarser level for the other branch (interp_from_lev0) to take it
392  // from. This used to follow only from ERF::nc_init_file's static default being
393  // {{""}} -- one empty string at level 0, so the vector below is never empty there,
394  // with or without erf.nc_init_file_0 -- which is how an idealized run could use
395  // Noah-MP at all. Say it outright so it does not rest on that placeholder.
396  //
397  // A finer level runs the driver if it has a land setup file of its own. A run
398  // initialized from WRF or metgrid files names it with erf.nc_init_file_<lev>, which also
399  // supplies that level's atmosphere. An idealized run has no such file -- its atmosphere
400  // comes from the sounding or the problem setup -- so there the namelist's
401  // ERF_SETUP_FILE_0<lev+1> alone decides: a nested land file whose I_PARENT_START,
402  // J_PARENT_START and PARENT_GRID_RATIO place it in its parent's (the driver reads those
403  // in NoahmpReadLandHeader). Without one, the level takes its land state from level 0
404  // (interp_from_lev0) and the radiation written to its own inputs goes unused. The rule
405  // is noahmp_level_runs_driver. The namelist is read only where it decides, on every
406  // rank alike: the conditions do not depend on the rank, and its broadcast is collective.
407  const bool has_init_file = !nc_init_file[lev].empty();
408  const bool namelist_names_setup = (lev > 0 && !has_init_file && m_idealized_init)
409  ? namelist_names_setup_file(lev) : false;
411  namelist_names_setup);
412  if (lev > 0) {
413  Print() << "Noah-MP at level " << lev << ": "
414  << (m_has_nc_file ? "runs the land model on its own setup file"
415  : "takes its land state from level 0") << std::endl;
416  }
417  if (m_has_nc_file) {
418  Print() << "Noah-MP initialization started" << std::endl;
419 
420  noahmp_preflight(lev);
421 
422  // Size noahmpio_vect to the local boxes. A rank owning no boxes leaves it
423  // empty and relies on the class-level m_itimestep/m_dtbl instead.
424  if (cons_in.local_size() > 0) {
425  noahmpio_vect.resize(cons_in.local_size(), lev);
426  }
427 
428  // Pinned buffer space for all the boxes
429  noahmp_input_tmp.resize(cons_in.local_size());
430  noahmp_output_tmp.resize(cons_in.local_size());
431 
432  int klo = domain.smallEnd(2);
433 
434  // Iterate over the multifab and noahmpio objects together, using the
435  // multifab to set the per-box bounds on each noahmpio object.
436  int idb = 0;
437  for (MFIter mfi(cons_in); mfi.isValid(); ++mfi, ++idb) {
438 
439  Box bx = mfi.tilebox();
440 
441  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(bx.smallEnd(2) == klo,
442  "NoahMP Init: box does not start at klo; z-decomposed grids are unsupported.");
443 
444  bx.makeSlab(2,klo);
445 
446  // Pinned buffers per box; output carries the 2D outputs + 3 soil groups.
447  noahmp_input_tmp[idb] = std::make_unique<FArrayBox>(bx, NoahmpInputComp::NumComps , The_Pinned_Arena());
448  noahmp_output_tmp[idb] = std::make_unique<FArrayBox>(bx, NoahmpOutputComp::NumComps + m_num_soil_groups*m_nsoil, The_Pinned_Arena());
449 
450  NoahmpIO_type* noahmpio = &noahmpio_vect[idb];
451 
452  noahmpio->blkid = idb;
453  noahmpio->level = lev;
454  noahmpio->ScalarInitDefault();
455  noahmpio->rank = ParallelDescriptor::MyProc();
456  noahmpio->comm = MPI_Comm_c2f(ParallelDescriptor::Communicator());
457 
458  // namelist.erf holds noahmpio-specific parameters, read Fortran-side.
459  noahmpio->ReadNamelist();
460 
461  // Assert namelist NSOIL matches erf.lsm_nsoil (used to size the fabs) so a
462  // mismatch fails loudly rather than truncating the soil diagnostics.
463  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(noahmpio->nsoil == m_nsoil,
464  "namelist.erf NSOIL does not match erf.lsm_nsoil (default 4); "
465  "set erf.lsm_nsoil to the Noah-MP soil-layer count");
466 
467  // NetCDF land-file headers (also Fortran-side)
468  noahmpio->ReadLandHeader();
469 
470  // Set domain/memory/tile bounds from the tile. All three are set to the
471  // same bounds for now; may change for special memory management later.
472  noahmpio->xstart = bx.smallEnd(0);
473  noahmpio->xend = bx.bigEnd(0);
474  noahmpio->ystart = bx.smallEnd(1);
475  noahmpio->yend = bx.bigEnd(1);
476 
477  // Domain, tile, and memory bounds are all equal for now (single-slab model).
478  auto set_grid_bounds = [](NoahmpIO_type* io, int x0, int x1, int y0, int y1) {
479  io->ids=io->its=io->ims=x0; io->ide=io->ite=io->ime=x1;
480  io->jds=io->jts=io->jms=y0; io->jde=io->jte=io->jme=y1;
481  io->kds=io->kts=io->kms=1; io->kde=io->kte=io->kme=2;
482  };
483  set_grid_bounds(noahmpio, noahmpio->xstart, noahmpio->xend, noahmpio->ystart, noahmpio->yend);
484 
485  // Allocate Fortran IO memory from the bounds above + namelist/header info
486  noahmpio->VarInitDefault();
487 
488  // NoahmpTable.TBL input
489  noahmpio->ReadTable();
490 
491  // Read/initialize from the NetCDF land file
492  noahmpio->ReadLandMain();
493 
494  // Compute initial values not supplied by the land file
495  noahmpio->InitMain();
496  }
497 
498  // Initial land plotfile (tag 0); must run on the land-owning subcomm, not the
499  // full comm, or a land-free rank never joins the collective nf90_create.
500  Print() << "Noah-MP writing lnd.nc file at lev: " << lev << std::endl;
501  with_land_comm([](NoahmpIO_type& noahmpio) { noahmpio.WriteLand(0); });
502 
503  // Broadcast DTBL and the initial substep counter so the firing decision is
504  // identical on every rank. Land-free ranks use max-reduction-losing sentinels.
505  m_dtbl = noahmpio_vect.empty() ? std::numeric_limits<Real>::lowest()
506  : static_cast<Real>(noahmpio_vect[0].DTBL);
507  m_itimestep = noahmpio_vect.empty() ? std::numeric_limits<int>::lowest()
508  : noahmpio_vect[0].itimestep;
509  ParallelDescriptor::ReduceRealMax(m_dtbl);
510  ParallelDescriptor::ReduceIntMax(m_itimestep);
511 
512  // Guard against a decomposition in which no rank owns a land box.
513  // DTBL is real(NOAH_TIMESTEP) from namelist.erf, and NOAH_TIMESTEP defaults to
514  // -9999 in the driver, so this is what an absent NOAH_TIMESTEP looks like. (It is
515  // not about land: noahmpio_vect is sized by boxes, land or not, so every rank that
516  // owns a box contributes a DTBL to the max.)
518  "Noah-MP: the Noah-MP timestep is not positive. Set NOAH_TIMESTEP (in seconds, "
519  "an integer) in namelist.erf.");
520  // The ERF-step-versus-NOAH_TIMESTEP constraint is enforced in Advance_With_State:
521  // the dt this function receives is a placeholder zero (see the top of Init), so a
522  // check against it here -- there used to be one -- could never fire.
523 
524  Print() << "Noah-MP initialization completed" << std::endl;
525  } // has nc_init_file
526 
527 };
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:106
bool noahmp_level_runs_driver(int lev, bool has_init_file, bool idealized_init, bool namelist_names_setup) noexcept
Whether Noah-MP at AMR level lev runs the land model on a setup file of its own.
Definition: ERF_NOAHMP_LevelDriver.H:22
amrex::Real Real
Definition: ERF_ShocInterface.H:19
amrex::Vector< amrex::BCRec > m_domain_bcs_type
Definition: ERF_NOAHMP.H:373
amrex::Vector< std::string > LsmDataName
Definition: ERF_NOAHMP.H:354
amrex::Vector< int > LsmDataMap
Definition: ERF_NOAHMP.H:348
int soil_data_idx(int g, int k) const
Definition: ERF_NOAHMP.H:310
int m_nsoil
Definition: ERF_NOAHMP.H:408
amrex::IntVect m_refRatio
Definition: ERF_NOAHMP.H:370
int m_lsm_data_size
Definition: ERF_NOAHMP.H:342
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:427
amrex::Geometry m_lsm_geom
Definition: ERF_NOAHMP.H:366
amrex::Geometry m_geom0
Definition: ERF_NOAHMP.H:363
int m_nz_lsm
Definition: ERF_NOAHMP.H:388
amrex::Vector< amrex::MultiFab * > lsm_lev0_data
Definition: ERF_NOAHMP.H:401
amrex::Vector< int > LsmFluxMap
Definition: ERF_NOAHMP.H:351
static constexpr int m_num_soil_groups
Definition: ERF_NOAHMP.H:306
void m_ensure_nsoil_resolved()
Definition: ERF_NOAHMP.H:317
amrex::Vector< std::string > LsmFluxName
Definition: ERF_NOAHMP.H:357
amrex::Real m_dz_lsm
Definition: ERF_NOAHMP.H:391
@ NumVars
Definition: ERF_NOAHMP_Fields.H:116
@ t_flux
Definition: ERF_NOAHMP_Fields.H:125
@ tau13
Definition: ERF_NOAHMP_Fields.H:127
@ q_flux
Definition: ERF_NOAHMP_Fields.H:126
@ tau23
Definition: ERF_NOAHMP_Fields.H:128
@ ng
Definition: ERF_Morrison.H:51
@ NumComps
Definition: ERF_NOAHMP_Fields.H:156
@ NumComps
Definition: ERF_NOAHMP_Fields.H:172
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  // The level-0 flux still holds lsm_undefined in cells Noah-MP did not process:
57  // SurfaceLayer falls back to MOST there but does not write that flux back (see #3446).
58  // These sentinels are interpolated along with the valid values, so every consumer of
59  // the fine-level flux must go on rejecting values that are not below lsm_undefined.
60  for (int ivar(0); ivar<LsmFlux_NOAHMP::NumVars; ++ivar) {
61  InterpFromCoarseLevel(*lsm_fab_flux[ivar], lsm_fab_flux[ivar]->nGrowVect(),
62  IntVect(0,0,0), // do NOT fill ghost cells outside the domain
63  *lsm_lev0_flux[ivar], 0, 0, 1,
64  m_geom0, m_geom,
65  rr2d, &cell_cons_interp,
67  }
68  Print () << "Noah-MP interpolation at level " << lev << " completed" << std::endl;
69 }
amrex::Array< amrex::MultiFab *, LsmFlux_NOAHMP::NumVars > lsm_lev0_flux
Definition: ERF_NOAHMP.H:404
@ cons_bc
Definition: ERF_IndexDefines.H:110

◆ Lsm_Data_Ptr()

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

Reimplemented from NullSurf.

124  {
125  int lsmIdx = LsmDataMap[varIdx];
126  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_data_size && lsmIdx>=0);
127  return lsm_fab_data[lsmIdx].get();
128  }
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.

164  {
165  int varIdx = -1;
166  std::string lc_varname = amrex::toLower(varname);
167  for (int idx(0); idx<m_lsm_data_size; ++idx) {
168  if (lc_varname == amrex::toLower(LsmDataName[idx])) {
169  varIdx = idx;
170  }
171  }
172  return varIdx;
173  }
Here is the call graph for this function:

◆ Lsm_DataName()

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

Reimplemented from NullSurf.

155  {
156  int lsmIdx = LsmDataMap[varIdx];
157  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_data_size && lsmIdx>=0);
158  return LsmDataName[lsmIdx];
159  }
Here is the call graph for this function:

◆ Lsm_Flux_Ptr()

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

Reimplemented from NullSurf.

133  {
134  int lsmIdx = LsmFluxMap[varIdx];
135  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_flux_size && lsmIdx>=0);
136  return lsm_fab_flux[lsmIdx].get();
137  }
Here is the call graph for this function:

◆ Lsm_Flux_Size()

int NOAHMP::Lsm_Flux_Size ( )
inlineoverridevirtual

Reimplemented from NullSurf.

150 { return NOAHMP::m_lsm_flux_size; }
int m_lsm_flux_size
Definition: ERF_NOAHMP.H:345

◆ Lsm_FluxIndex()

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

Reimplemented from NullSurf.

187  {
188  int varIdx = -1;
189  std::string lc_varname = amrex::toLower(varname);
190  for (int idx(0); idx<LsmFlux_NOAHMP::NumVars; ++idx) {
191  if (lc_varname == amrex::toLower(LsmFluxName[idx])) {
192  varIdx = idx;
193  }
194  }
195  return varIdx; // -1 if this model does not carry that flux; callers probe with this
196  }
Here is the call graph for this function:

◆ Lsm_FluxName()

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

Reimplemented from NullSurf.

178  {
179  int lsmIdx = LsmFluxMap[varIdx];
180  AMREX_ALWAYS_ASSERT(lsmIdx < NOAHMP::m_lsm_flux_size && lsmIdx>=0);
181  return LsmFluxName[lsmIdx];
182  }
Here is the call graph for this function:

◆ Lsm_Geom()

amrex::Geometry NOAHMP::Lsm_Geom ( )
inlineoverridevirtual

Reimplemented from NullSurf.

141 { 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.

201  {
202  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < m_lsm_data_size);
203  lsm_lev0_data[varIdx] = lev0_data_ptr;
204  }
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.

209  {
210  AMREX_ALWAYS_ASSERT(varIdx >= 0 && varIdx < int(LsmFlux_NOAHMP::NumVars));
211  lsm_lev0_flux[varIdx] = lev0_flux_ptr;
212  }
Here is the call graph for this function:

◆ m_ensure_nsoil_resolved()

void NOAHMP::m_ensure_nsoil_resolved ( )
inline
317  {
318  if (m_nsoil_resolved) { return; }
319  amrex::ParmParse pp_nsoil("erf");
320  pp_nsoil.queryAdd("lsm_nsoil", m_nsoil);
321  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_nsoil >= 1, "erf.lsm_nsoil must be >= 1");
323  m_nsoil_resolved = true;
324  }
bool m_nsoil_resolved
Definition: ERF_NOAHMP.H:316

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
328  {
329  return dir + "/precip_accum_prev_Level_" + std::to_string(l)
330  + "_slot_" + std::to_string(s);
331  }

◆ 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:424

◆ 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:116
static std::string m_precip_snapshot_name(const std::string &dir, int l, int s)
Definition: ERF_NOAHMP.H:328

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

◆ Runs_Own_Land_Driver()

bool NOAHMP::Runs_Own_Land_Driver ( ) const
inlineoverridevirtual

Reimplemented from NullSurf.

90 { return m_has_nc_file; }

◆ Set_LSM_Step()

void NOAHMP::Set_LSM_Step ( int  step)
inlineoverridevirtual

Reimplemented from NullSurf.

227  {
228  m_itimestep = step;
229  for (auto& noahmpio : noahmpio_vect) {
230  noahmpio.itimestep = step;
231  }
232  }

◆ soil_data_idx()

int NOAHMP::soil_data_idx ( int  g,
int  k 
) const
inline
310  {
311  return LsmData_NOAHMP::NumVars + g*m_nsoil + k;
312  }

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

◆ 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

Referenced by Runs_Own_Land_Driver().

◆ m_idealized_init

bool NOAHMP::m_idealized_init = false
private

Referenced by Define().

◆ 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: