ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_DataStruct.H
Go to the documentation of this file.
1 #ifndef ERF_DATA_STRUCT_H_
2 #define ERF_DATA_STRUCT_H_
3 
4 #include <string>
5 #include <iostream>
6 #include <cmath>
7 
8 #include <AMReX_ParmParse.H>
9 #include <AMReX_Print.H>
10 #include <AMReX_Gpu.H>
11 #include <AMReX_Geometry.H>
12 
13 #include "ERF_Constants.H"
14 #include "ERF_IndexDefines.H"
15 #include "ERF_AdvStruct.H"
16 #include "ERF_DampingStruct.H"
17 #include "ERF_DiffStruct.H"
18 #include "ERF_EBStruct.H"
19 #include "ERF_SpongeStruct.H"
20 #include "ERF_TurbStruct.H"
21 #include "ERF_TurbPertStruct.H"
22 #include "ERF_RadStruct.H"
24 #include "ERF_CanopyTurbStruct.H"
25 
26 /**
27  * Enum of possible plotfile types.
28  */
29 AMREX_ENUM(PlotFileType,
30  None, Amrex, Netcdf
31 );
32 
33 /**
34  * @brief Indices for map scale factors on mass and face grids.
35  */
36 enum MapFacType {
37 // This version assumes isotropic
38  m_x, u_x, v_x, num,
39  m_y = 0, u_y = 1, v_y = 2
40 // This version allows for non-isotropic
41 // m_x, u_x, v_x,
42 // m_y, u_y, v_y, num
43 };
44 
45 /**
46  * @brief Stress tensor component indices.
47  */
48 enum TauType {
50 };
51 
52 /**
53  * @brief Initial-condition source used to populate the ERF state.
54  */
55 AMREX_ENUM(InitType,
56  None, Input_Sounding, NCFile, WRFInput, Metgrid, Uniform, ConstantDensity, ConstantDensityLinearTheta,
57  Isentropic, MoistBaseState, HindCast
58 );
59 
60 /**
61  * @brief Interpretation of input sounding profiles.
62  */
63 AMREX_ENUM(SoundingType,
64  ConstantDensity, Ideal, Isentropic, DryIsentropic
65 );
66 
67 /**
68  * @brief Large-scale atmospheric boundary-layer driver type.
69  */
70 AMREX_ENUM(ABLDriverType,
71  None, PressureGradient, GeostrophicWind
72 );
73 
74 /**
75  * @brief Multilevel coupling strategy.
76  */
77 AMREX_ENUM(CouplingType,
78  OneWay, TwoWay
79 );
80 
81 /**
82  * @brief Acoustic substepping strategy.
83  */
84 AMREX_ENUM(SubsteppingType,
85  None, Implicit
86 );
87 
88 /**
89  * @brief Two-stage scheme used by the anelastic time integrator.
90  *
91  * RK2 is the SSP (Heun) scheme: both stages advance a full timestep from the old
92  * state and the second averages the two slow sources. MidPoint advances only a
93  * half timestep in the first stage, which makes the vertical diffusion second
94  * order in time with an implicit solve in the first stage alone.
95  */
96 AMREX_ENUM(AnelasticType,
97  RK2, MidPoint
98 );
99 
100 /**
101  * @brief Vertical mesh representation.
102  */
103 AMREX_ENUM(MeshType,
104  ConstantDz, StretchedDz, VariableDz
105 );
106 
107 /**
108  * @brief Linear solver for the anelastic projection on a terrain-fitted mesh.
109  *
110  * gmres_fft is GMRES with ERF's TerrainPoisson operator and an FFT preconditioner (needs the
111  * FFT build). mlmg and gmres_mlmg use amrex::MLTerrainPoisson (AMReX-Codes/amrex#6030), the
112  * same stencil with zero flux through the Neumann faces, solved by geometric multigrid
113  * alone or as the preconditioner of GMRES; they need no FFT.
114  */
115 AMREX_ENUM(TerrainPoissonSolver,
116  gmres_fft, mlmg, gmres_mlmg
117 );
118 
119 /**
120  * @brief Terrain and immersed-boundary representation.
121  */
122 AMREX_ENUM(TerrainType,
123  None, StaticFittedMesh, MovingFittedMesh, EB, ImmersedForcing
124 );
125 
126 /**
127  * @brief Building representation.
128  */
129 AMREX_ENUM(BuildingsType,
130  None, ImmersedForcing
131 );
132 
133 /**
134  * @brief Moisture transport representation.
135  */
136 AMREX_ENUM(MoistureModelType,
137  Eulerian, Lagrangian, Undefined
138 );
139 
140 /**
141  * @brief Moisture and microphysics model.
142  */
143 AMREX_ENUM(MoistureType,
144  SAM, SAM_NoIce, SAM_NoPrecip_NoIce, Kessler, Kessler_NoRain, SatAdj, Morrison, Morrison_NoIce, WSM6, WDM6, SuperDroplets, MoistNoCondensation, SBM, None
145 );
146 
147 /**
148  * @brief Whether an anelastic SuperDroplets configuration is unsupported.
149  *
150  * The current SuperDroplets implementation diagnoses thermodynamics through
151  * the compressible equation of state and has no BaseState pressure input.
152  */
154  const MoistureType moisture_type,
155  const bool any_anelastic) noexcept
156 {
157  return any_anelastic && moisture_type == MoistureType::SuperDroplets;
158 }
159 
160 /**
161  * @brief Wind-farm model.
162  */
163 AMREX_ENUM(WindFarmType,
165 );
166 
167 /**
168  * @brief Coordinate system used for wind-farm locations.
169  */
170 AMREX_ENUM(WindFarmLocType,
171  lat_lon, x_y, None
172 );
173 
174 /**
175  * @brief Land-surface model.
176  */
177 AMREX_ENUM(LandSurfaceType,
178  SLM, NOAHMP, None
179 );
180 
181 /**
182  * @brief Urban model.
183  */
184 AMREX_ENUM(UrbanType,
185  None
186 );
187 
188 /**
189  * @brief Radiation model.
190  */
191 AMREX_ENUM(RadiationType,
192  None, RRTMGP, Simple, TwoStream
193 );
194 
195 /**
196  * @brief Coordinate-axis selector.
197  */
198 enum struct Coord {
199  x, y, z
200 };
201 
202 // These are used as integers so must be enum not enum struct
203 /**
204  * @brief Rayleigh damping profile component indices.
205  */
206 enum Rayleigh {
208 };
209 
210 // These are used as integers so must be enum not enum struct
211 /**
212  * @brief Sponge reference-state component indices.
213  */
214 enum Sponge {
216 };
217 
218 static const std::string BoundaryFaceName[] = {"xlo", "ylo", "zlo", "xhi", "yhi", "zhi"};
219 
220 /**
221  * @brief The moisture data carried by the active microphysics scheme.
222  *
223  * This struct is the single source of truth for the question "which moisture
224  * quantities does this run actually have?". Everything downstream of the
225  * microphysics -- source terms, turbulence closures, and every output path
226  * (3D plotfiles, 2D plotfiles, subvolumes) -- answers that question by querying
227  * this struct instead of enumerating MoistureType values, so a newly added
228  * scheme only has to declare its layout once, in from_moisture_model() below.
229  *
230  * Two kinds of index live here:
231  *
232  * 1. Conserved-state components (qv ... nn). Absolute component indices into
233  * the conserved state array, i.e. one of RhoQ1_comp ... RhoQ11_comp. They
234  * name the *rho-weighted* species: state(i,j,k,qv) is rho*qv, so an output
235  * path must divide by Rho_comp to recover the mixing ratio.
236  *
237  * 2. qmoist diagnostic slots (rain_accum ... cond_rate). Indices into the
238  * per-level qmoist vector that is filled from
239  * Microphysics::Get_Qmoist_Ptr(lev,idx). Each scheme chooses that
240  * layout independently, so the slot numbers differ between schemes and are
241  * recorded per scheme below.
242  *
243  * In both cases the value is `absent` when the active scheme does not carry the
244  * quantity, so `index >= 0` is the criterion for "this exists and is meaningful
245  * to write". That is deliberately *not* the same test as "the storage is
246  * allocated": several schemes allocate a wider state than they integrate (the
247  * Morrison class always allocates eleven moist components, so Morrison_NoIce
248  * owns ice slots it never fills), and writing those slots to a plotfile would
249  * publish untouched memory as if it were data.
250  */
252 
253  // Sentinel values shared by both kinds of index above.
254  static constexpr int absent = -1; //!< not carried by the active scheme
255  static constexpr int computed_from_state = -2; //!< carried, but derived at output
256  //!< time from the conserved state
257  //!< rather than read from a qmoist slot
258 
259  // ---------------------------------------------------------------------
260  // Conserved-state components: mass mixing ratios (rho-weighted)
261  // ---------------------------------------------------------------------
262  int qv = absent; //!< water vapor
263  int qc = absent; //!< cloud liquid water
264  int qi = absent; //!< cloud ice
265  int qr = absent; //!< rain
266  int qs = absent; //!< snow
267  int qg = absent; //!< graupel
268 
269  // ---------------------------------------------------------------------
270  // Conserved-state components: number concentrations (rho-weighted)
271  // ---------------------------------------------------------------------
272  int nc = absent; //!< cloud liquid water number
273  int ni = absent; //!< cloud ice number
274  int nr = absent; //!< rain number
275  int ns = absent; //!< snow number
276  int ng = absent; //!< graupel number
277  // CCN / total aerosol number. Unlike every other number concentration above,
278  // this has no companion mass species: it is an aerosol reservoir that
279  // exchanges number with nc and nr through activation and evaporation rather
280  // than a hydrometeor count. Kept distinct from ni, which is cloud ice number,
281  // even though some schemes place the two in the same conserved-state slot.
282  int nn = absent; //!< CCN / total aerosol number
283 
284  // ---------------------------------------------------------------------
285  // qmoist diagnostic slots
286  //
287  // These are *not* conserved-state components; they index the qmoist vector
288  // handed out by the microphysics interface. Slot numbers are scheme
289  // specific, which is exactly why they belong in this per-scheme map.
290  // ---------------------------------------------------------------------
291  //
292  // The accumulations are what reached the ground, in mm of liquid water:
293  // rain, snow and graupel each hold that species alone, and precip_accum
294  // holds everything that fell out -- those three plus whatever cloud water
295  // and cloud ice the scheme sediments -- so it can exceed their sum.
296  int rain_accum = absent; //!< accumulated surface rain
297  int snow_accum = absent; //!< accumulated surface snow
298  int graupel_accum = absent; //!< accumulated surface graupel
299  int precip_accum = absent; //!< accumulated surface precipitation, all species
300  int rel_hum = absent; //!< relative humidity
301  int cond_rate = absent; //!< condensation rate
302 
303  /**
304  * @brief Construct the conserved-state component map.
305  *
306  * Prefer from_moisture_model() for production code: it fills in the qmoist
307  * diagnostic slots as well. This constructor exists for the callers -- unit
308  * tests and kernels exercised in isolation -- that only need the state map.
309  *
310  * @param qv_comp Water vapor component index.
311  * @param qc_comp Cloud liquid water component index.
312  * @param qi_comp Cloud ice component index.
313  * @param qr_comp Rain component index.
314  * @param qs_comp Snow component index.
315  * @param qg_comp Graupel component index.
316  * @param nc_comp Cloud liquid water number concentration component index.
317  * @param ni_comp Cloud ice number concentration component index.
318  * @param nr_comp Rain number concentration component index.
319  * @param ns_comp Snow number concentration component index.
320  * @param ng_comp Graupel number concentration component index.
321  * @param nn_comp CCN / total aerosol number concentration component index.
322  */
323  MoistureComponentIndices (int qv_comp, int qc_comp,
324  int qi_comp=absent,
325  int qr_comp=absent,
326  int qs_comp=absent,
327  int qg_comp=absent,
328  int nc_comp=absent,
329  int ni_comp=absent,
330  int nr_comp=absent,
331  int ns_comp=absent,
332  int ng_comp=absent,
333  int nn_comp=absent)
334  : qv(qv_comp), qc(qc_comp), qi(qi_comp), qr(qr_comp), qs(qs_comp), qg(qg_comp),
335  nc(nc_comp), ni(ni_comp), nr(nr_comp), ns(ns_comp), ng(ng_comp),
336  nn(nn_comp) {}
337 
338  /**
339  * @brief Construct an empty map, i.e. a dry run.
340  */
342 
343  /**
344  * @brief Build the complete index map -- conserved-state components and
345  * qmoist diagnostic slots -- for a moisture model.
346  *
347  * This is the one place a scheme declares what it carries. The conserved
348  * layout must match the component the scheme reads and writes in its
349  * Copy_State_to_Micro / Copy_Micro_to_State pair, and the qmoist slots must
350  * match the order the scheme installs in its MicVarMap. A slot that a
351  * scheme allocates but never fills is left `absent` on purpose: see the
352  * class comment for why allocation is not the criterion here.
353  *
354  * @param moisture_type Active moisture/microphysics model.
355  */
357  from_moisture_model (const MoistureType moisture_type)
358  {
360 
361  switch (moisture_type) {
362 
363  case MoistureType::None:
364  // Dry: no moisture data of any kind.
365  break;
366 
367  case MoistureType::MoistNoCondensation:
368  case MoistureType::SAM_NoPrecip_NoIce:
369  case MoistureType::Kessler_NoRain:
370  // Vapor plus suspended cloud water, no precipitation and no
371  // accumulation diagnostics.
372  mi.qv = RhoQ1_comp;
373  mi.qc = RhoQ2_comp;
374  break;
375 
376  case MoistureType::SatAdj:
377  // As above, but relative humidity is recovered from the state at
378  // output time rather than stored: SatAdj publishes no qmoist arrays.
379  mi.qv = RhoQ1_comp;
380  mi.qc = RhoQ2_comp;
382  break;
383 
384  case MoistureType::Kessler:
385  // Warm rain in three moist components; qmoist is {rain_accum}.
386  // Rain is the only species that falls, so the precipitation total
387  // is the rain accumulation itself.
388  mi.qv = RhoQ1_comp;
389  mi.qc = RhoQ2_comp;
390  mi.qr = RhoQ3_comp;
391  mi.rain_accum = 0;
392  mi.precip_accum = 0;
393  break;
394 
395  case MoistureType::SBM:
396  // SBM stores the distribution in an auxiliary state. These two
397  // compact liquid lanes are projections for host compatibility.
398  mi.qv = RhoQ1_comp;
399  mi.qc = RhoQ2_comp;
400  mi.qr = RhoQ3_comp;
401  break;
402 
403  case MoistureType::SAM_NoIce:
404  // The SAM class always allocates six moist components, so rain sits
405  // in the fourth slot even with the ice species switched off.
406  mi.qv = RhoQ1_comp;
407  mi.qc = RhoQ2_comp;
408  mi.qr = RhoQ4_comp;
409  mi.rain_accum = 0;
410  mi.precip_accum = 3;
411  break;
412 
413  case MoistureType::SAM:
414  case MoistureType::WSM6:
415  // Single-moment mixed phase; qmoist is
416  // {rain_accum, snow_accum, graupel_accum, precip_accum}.
417  mi.qv = RhoQ1_comp;
418  mi.qc = RhoQ2_comp;
419  mi.qi = RhoQ3_comp;
420  mi.qr = RhoQ4_comp;
421  mi.qs = RhoQ5_comp;
422  mi.qg = RhoQ6_comp;
423  mi.rain_accum = 0;
424  mi.snow_accum = 1;
425  mi.graupel_accum = 2;
426  mi.precip_accum = 3;
427  break;
428 
429  case MoistureType::Morrison:
430  // Two-moment mixed phase: six mass species followed by five number
431  // concentrations.
432  mi.qv = RhoQ1_comp;
433  mi.qc = RhoQ2_comp;
434  mi.qi = RhoQ3_comp;
435  mi.qr = RhoQ4_comp;
436  mi.qs = RhoQ5_comp;
437  mi.qg = RhoQ6_comp;
438  mi.nc = RhoQ7_comp;
439  mi.ni = RhoQ8_comp;
440  mi.nr = RhoQ9_comp;
441  mi.ns = RhoQ10_comp;
442  mi.ng = RhoQ11_comp;
443  mi.rain_accum = 0;
444  mi.snow_accum = 1;
445  mi.graupel_accum = 2;
446  mi.precip_accum = 3;
447  break;
448 
449  case MoistureType::Morrison_NoIce:
450  // Liquid-only Morrison: the class still allocates all eleven moist
451  // components, but only vapor, cloud water and rain -- and the cloud
452  // and rain numbers -- are integrated, so the frozen species and
453  // their numbers stay absent here.
454  mi.qv = RhoQ1_comp;
455  mi.qc = RhoQ2_comp;
456  mi.qr = RhoQ4_comp;
457  mi.nc = RhoQ7_comp;
458  mi.nr = RhoQ9_comp;
459  mi.rain_accum = 0;
460  mi.precip_accum = 3;
461  break;
462 
463  case MoistureType::WDM6:
464  // Double-moment warm phase on top of single-moment ice: nn is the
465  // aerosol reservoir and takes the slot Morrison uses for ice number,
466  // and there are no snow or graupel numbers.
467  mi.qv = RhoQ1_comp;
468  mi.qc = RhoQ2_comp;
469  mi.qi = RhoQ3_comp;
470  mi.qr = RhoQ4_comp;
471  mi.qs = RhoQ5_comp;
472  mi.qg = RhoQ6_comp;
473  mi.nc = RhoQ7_comp;
474  mi.nn = RhoQ8_comp;
475  mi.nr = RhoQ9_comp;
476  mi.rain_accum = 0;
477  mi.snow_accum = 1;
478  mi.graupel_accum = 2;
479  mi.precip_accum = 3;
480  break;
481 
482  case MoistureType::SuperDroplets:
483  // Lagrangian microphysics that still carries the six Eulerian mass
484  // species. Its qmoist layout is its own (see the MicVarMap in
485  // SuperDropletsMoist::Init): the graupel accumulation slot exists
486  // but is never filled, so it stays absent.
487  mi.qv = RhoQ1_comp;
488  mi.qc = RhoQ2_comp;
489  mi.qi = RhoQ3_comp;
490  mi.qr = RhoQ4_comp;
491  mi.qs = RhoQ5_comp;
492  mi.qg = RhoQ6_comp;
493  mi.cond_rate = 3;
494  mi.rel_hum = 7;
495  mi.rain_accum = 8;
496  mi.snow_accum = 10;
497  mi.precip_accum = 11;
498  break;
499 
500  default:
501  amrex::Abort("Unknown MoistureType in MoistureComponentIndices::from_moisture_model");
502  }
503 
504  return mi;
505  }
506 
507  /**
508  * @brief An ordered list of conserved-state components forming an aggregate
509  * moisture output variable (qt, qn, qp, moist_density).
510  *
511  * Only the species the active scheme carries are appended, so a consumer can
512  * sum the list without first asking which ones exist.
513  */
514  struct CompList {
515  static constexpr int max_size = 6; //!< qv, qc, qi, qr, qs, qg
517  int size = 0;
518 
519  //! Append a component, ignoring the ones the scheme does not carry.
520  void append (int c) {
521  if ( (c >= 0) && (size < max_size) ) { comp[size++] = c; }
522  }
523  [[nodiscard]] bool empty () const { return (size == 0); }
524  };
525 
526  /**
527  * @brief Components summed for total water, "qt": every mass mixing ratio.
528  *
529  * Number concentrations are excluded -- they are counts, not masses.
530  */
531  [[nodiscard]] CompList total_water_comps () const {
532  CompList list;
533  list.append(qv); list.append(qc); list.append(qi);
534  list.append(qr); list.append(qs); list.append(qg);
535  return list;
536  }
537 
538  /**
539  * @brief Components summed for non-precipitating water, "qn": vapor plus the
540  * suspended condensate. Also the moist part of "moist_density".
541  */
542  [[nodiscard]] CompList nonprecipitating_comps () const {
543  CompList list;
544  list.append(qv); list.append(qc); list.append(qi);
545  return list;
546  }
547 
548  /**
549  * @brief Components summed for precipitating water, "qp": the falling species.
550  */
551  [[nodiscard]] CompList precipitating_comps () const {
552  CompList list;
553  list.append(qr); list.append(qs); list.append(qg);
554  return list;
555  }
556 
557  /**
558  * @brief Test whether a conserved-state component holds one of the moisture
559  * variables this scheme carries.
560  *
561  * Use this rather than enumerating moisture models when deciding whether a
562  * "rhoQn" state component is meaningful for the active scheme.
563  *
564  * @param comp Absolute component index into the conserved state array.
565  */
566  [[nodiscard]] bool has_comp (int comp) const {
567  return ( (comp >= 0) &&
568  ( (comp == qv) || (comp == qc) || (comp == qi) ||
569  (comp == qr) || (comp == qs) || (comp == qg) ||
570  (comp == nc) || (comp == ni) || (comp == nr) ||
571  (comp == ns) || (comp == ng) || (comp == nn) ) );
572  }
573 
574  /**
575  * @brief Test whether the active scheme carries rain, snow and graupel in the
576  * RhoQ4/RhoQ5/RhoQ6 slots assumed by erf_derreflectivity and
577  * erf_dermaxreflectivity.
578  *
579  * Those kernels read the precipitating species by hardcoded component, so this
580  * checks the layout and not just the presence of the species.
581  */
582  [[nodiscard]] bool has_reflectivity_species () const {
583  return ( (qr == RhoQ4_comp) && (qs == RhoQ5_comp) && (qg == RhoQ6_comp) );
584  }
585 
586  /**
587  * @brief Test whether the active scheme carries any moisture at all.
588  *
589  * Every moist scheme allocates water vapor, so this is equivalent to testing
590  * moisture_type != MoistureType::None without needing the enum.
591  */
592  [[nodiscard]] bool has_moisture () const { return (qv >= 0); }
593 
594  /**
595  * @brief The conserved-state component behind a single-species output name,
596  * or `absent` if the name is not a single species or the scheme does
597  * not carry it.
598  *
599  * An output path can use this to copy a species without repeating the
600  * name-to-component mapping.
601  *
602  * @param name Derived or plot variable name.
603  */
604  [[nodiscard]] int comp_for_var (const std::string& name) const {
605  if (name == "qv") { return qv; }
606  if (name == "qc") { return qc; }
607  if (name == "qi") { return qi; }
608  if (name == "qrain") { return qr; }
609  if (name == "qsnow") { return qs; }
610  if (name == "qgraup") { return qg; }
611  if (name == "nc") { return nc; }
612  if (name == "ni") { return ni; }
613  if (name == "nr") { return nr; }
614  if (name == "ns") { return ns; }
615  if (name == "ng") { return ng; }
616  if (name == "nn") { return nn; }
617  return absent;
618  }
619 
620  /**
621  * @brief The qmoist slot behind a moist diagnostic output name.
622  *
623  * Returns `absent` when the scheme does not provide the diagnostic, and
624  * `computed_from_state` when it does provide it but not through a qmoist
625  * array -- in that case the caller must derive it from the conserved state.
626  *
627  * @param name Derived or plot variable name.
628  */
629  [[nodiscard]] int qmoist_index_for_var (const std::string& name) const {
630  if (name == "rain_accum") { return rain_accum; }
631  if (name == "snow_accum") { return snow_accum; }
632  if (name == "graupel_accum") { return graupel_accum; }
633  if (name == "precip_accum") { return precip_accum; }
634  if (name == "rel_humidity") { return rel_hum; }
635  if (name == "condensation_rate") { return cond_rate; }
636  return absent;
637  }
638 
639  /**
640  * @brief Whether this map decides a variable's availability, and if so
641  * whether the variable is available.
642  */
644  bool governed = false; //!< the name is a moisture output variable
645  bool available = false; //!< the active scheme carries the data behind it
646  };
647 
648  /**
649  * @brief Classify an output variable name against this index map.
650  *
651  * This is the one table pairing output names with the data behind them. It
652  * is driven off the scheme's index map rather than an enumerated list of
653  * moisture models, so it stays correct as schemes are added. Names with no
654  * moisture dependence come back not-governed, which lets a caller walk a
655  * mixed list of variable names and only defer to this map where it applies.
656  *
657  * @param name Derived or plot variable name.
658  */
659  [[nodiscard]] VarAvailability query_var (const std::string& name) const {
660 
661  // Single species, taken straight from the conserved state
662  if ( (name == "qv") || (name == "qc") || (name == "qi") ||
663  (name == "qrain") || (name == "qsnow") || (name == "qgraup") ||
664  (name == "nc") || (name == "ni") || (name == "nr") ||
665  (name == "ns") || (name == "ng") || (name == "nn") ) {
666  return {true, comp_for_var(name) >= 0};
667  }
668 
669  // Moist diagnostics, either from a qmoist array or derived at output time
670  if ( (name == "rain_accum") || (name == "snow_accum") ||
671  (name == "graupel_accum") || (name == "precip_accum") ||
672  (name == "rel_humidity") ||
673  (name == "condensation_rate") ) {
674  return {true, qmoist_index_for_var(name) != absent};
675  }
676 
677  // Aggregates over several species
678  if (name == "qt") { return {true, !total_water_comps().empty()}; }
679  if (name == "qp") { return {true, !precipitating_comps().empty()}; }
680 
681  // Non-precipitating water is vapor plus the suspended condensate, and
682  // moist density adds that same sum to the dry density
683  if ( (name == "qn") || (name == "moist_density") ) {
684  return {true, (qv >= 0) && (qc >= 0)};
685  }
686 
687  // Column and thermodynamic diagnostics that read vapor only
688  if ( (name == "qsat") || (name == "precipitable") ) {
689  return {true, has_moisture()};
690  }
691 
692  return {false, false};
693  }
694 
695  /**
696  * @brief Test whether the moist species behind a derived/plot variable name is
697  * carried by the active scheme.
698  *
699  * Names this map does not govern return true, so this can be used directly as
700  * a filter over a mixed list of variable names. Use query_var() instead when
701  * the caller needs to distinguish "not a moisture variable" from "available".
702  *
703  * @param name Derived or plot variable name.
704  */
705  [[nodiscard]] bool has_derived_var (const std::string& name) const {
706  const VarAvailability status = query_var(name);
707  return (!status.governed) || status.available;
708  }
709 };
710 
711 /**
712  * Container holding many of the algorithmic options and parameters
713  */
714 
715 struct SolverChoice {
716  public:
717  /**
718  * @brief Read solver-wide algorithmic options from the input parameter database.
719  * @param max_level Maximum AMR level configured for the run.
720  * @param pp_prefix ParmParse prefix for the ERF input namespace.
721  */
722  void init_params (int max_level, std::string pp_prefix)
723  {
724  amrex::ParmParse pp(pp_prefix);
725 
726  bool bogus_bool;
727  if (pp.query("use_terrain",bogus_bool) > 0) {
728  amrex::Error("The input use_terrain is deprecated. Set terrain_type instead.");
729  }
730 
731  if (pp.query("use_moist_background",bogus_bool) > 0) {
732  amrex::Error("The input use_moist_background is deprecated. Set init_type = MoistBaseState instead.");
733  }
734 
735  // Do we set map scale factors to myhalf instead of 1 for testing?
736  pp.queryAdd("test_mapfactor", test_mapfactor);
737 
738  // For testing: if nonzero, the map scale factors on level 0 vary smoothly in space,
739  // m = 1 + test_mapfactor_amp sin(2 pi x / Lx) cos(2 pi y / Ly), as if read from an init file
740  // there; finer levels take theirs from the coarser level, as a level without a file does
741  pp.queryAdd("test_mapfactor_amp", test_mapfactor_amp);
743  amrex::Abort("erf.test_mapfactor and erf.test_mapfactor_amp cannot be used together");
744  }
745 
746  // Which horizontal pressure gradient formulation to use with terrain fitted coords?
747  // 0: dp/dx with dp/dz correction (default)
748  // 1: gradient of vertically interpolated p, see Klemp 2011
749  pp.queryAdd("gradp_type", gradp_type);
751 
752  // For the lateral pressure gradient to be used in the momentum equation, should we
753  // take the x- and y-derivatives of the perturbational pressure or the full pressure?
754  pp.queryAdd("use_pert_pres_gradient", use_pert_pres_gradient);
755 
756  // What type of moisture model to use?
757  moisture_type = MoistureType::None; // Default
758  if (pp.query("moisture_type",moisture_type) > 0) {
759  amrex::Error("The input moisture_type is deprecated. Set moisture_model instead.");
760  }
761 
762  pp.query_enum_case_insensitive("moisture_model",moisture_type);
763 
764  // Test-only SBM failure injections. They remain disabled unless an
765  // integration test explicitly enables the corresponding option.
766  pp.queryAdd("sbm_test_native_qc_write_fault", sbm_test_native_qc_write_fault);
767  pp.queryAdd("sbm_test_donor_cfl_fault", sbm_test_donor_cfl_fault);
768  if (moisture_type != MoistureType::SBM &&
770  amrex::Error("SBM test fault-injection options require moisture_model=SBM");
771  }
772 
773  // Register the moisture data this scheme carries: conserved-state
774  // components and qmoist diagnostic slots alike. MoistureComponentIndices
775  // is the one place that mapping lives, and every consumer -- source terms,
776  // turbulence closures, and the plotfile/subvolume writers -- queries it
777  // instead of testing moisture_type.
779 
780  if (moisture_type == MoistureType::SBM) {
781  if (pp.contains("sbm_zero_transport_fixture")) {
782  bool legacy_zero_transport = false;
783  pp.get("sbm_zero_transport_fixture", legacy_zero_transport);
784  if (legacy_zero_transport) {
785  amrex::Error("erf.sbm_zero_transport_fixture=true is obsolete; M3 advances the mapped spectral state, so remove this option");
786  }
787  }
788  pp.queryAdd("sbm_nbins", sbm_nbins);
789  pp.queryAdd("sbm_moment_mode", sbm_moment_mode);
790  pp.queryAdd("sbm_cloud_rain_split", sbm_cloud_rain_split);
791  pp.queryarr("sbm_edges", sbm_edges);
792  pp.queryarr("sbm_pivots", sbm_pivots);
793  pp.queryarr("sbm_fixture_initial_state", sbm_fixture_initial_state);
794 
795  if (sbm_nbins < 2 || (sbm_moment_mode != 1 && sbm_moment_mode != 2)) {
796  amrex::Error("SBM requires sbm_nbins >= 2 and sbm_moment_mode equal to 1 or 2");
797  }
798  if (sbm_edges.empty()) {
799  sbm_edges.resize(static_cast<std::size_t>(sbm_nbins + 1));
800  sbm_pivots.resize(static_cast<std::size_t>(sbm_nbins));
801  for (int b = 0; b <= sbm_nbins; ++b) {
802  const amrex::Real fraction = static_cast<amrex::Real>(b) /
803  static_cast<amrex::Real>(sbm_nbins);
804  sbm_edges[static_cast<std::size_t>(b)] =
805  std::pow(amrex::Real(10.0), amrex::Real(-18.0) + amrex::Real(6.0) * fraction);
806  }
807  for (int b = 0; b < sbm_nbins; ++b) {
808  sbm_pivots[static_cast<std::size_t>(b)] = std::sqrt(
809  sbm_edges[static_cast<std::size_t>(b)] * sbm_edges[static_cast<std::size_t>(b + 1)]);
810  }
811  } else {
812  sbm_nbins = static_cast<int>(sbm_edges.size()) - 1;
813  if (sbm_nbins < 2) amrex::Error("sbm_edges must define at least two bins");
814  if (sbm_pivots.empty()) {
815  sbm_pivots.resize(static_cast<std::size_t>(sbm_nbins));
816  for (int b = 0; b < sbm_nbins; ++b) {
817  sbm_pivots[static_cast<std::size_t>(b)] = std::sqrt(
818  sbm_edges[static_cast<std::size_t>(b)] * sbm_edges[static_cast<std::size_t>(b + 1)]);
819  }
820  }
821  }
822  if (sbm_pivots.size() != static_cast<amrex::Long>(sbm_nbins)) {
823  amrex::Error("sbm_pivots must contain one value per SBM bin");
824  }
826  if (sbm_cloud_rain_split <= 0 || sbm_cloud_rain_split >= sbm_nbins) {
827  amrex::Error("sbm_cloud_rain_split must be strictly inside the liquid bin range");
828  }
829  const int expected_sbm_components = sbm_nbins * (sbm_moment_mode == 2 ? 2 : 1);
830  if (!sbm_fixture_initial_state.empty() &&
831  sbm_fixture_initial_state.size() != static_cast<amrex::Long>(expected_sbm_components)) {
832  amrex::Error("sbm_fixture_initial_state must match the full runtime SBM component count");
833  }
834  for (const amrex::Real value : sbm_fixture_initial_state) {
835  if (!std::isfinite(value) || value < amrex::Real(0.0)) {
836  amrex::Error("sbm_fixture_initial_state values must be finite and nonnegative");
837  }
838  }
839  }
840 
841  // Set a default for both dry and moist
842  buoyancy_type.resize(max_level+1);
843  for (int i = 0; i <= max_level; ++i) {
844  buoyancy_type[i] = 1; // uses Rhoprime
845  }
846 
848  pp.queryAdd("moisture_tight_coupling",moisture_tight_coupling);
849  }
850 
851  // Which expression (1,2/3 or 4) to use for buoyancy
852  int default_buoyancy_type = buoyancy_type[0];
853  read_int_string(max_level, "buoyancy_type", buoyancy_type, default_buoyancy_type);
854 
855  // What type of land surface model to use
856  lsm_type = LandSurfaceType::None; // Default
857  pp.query_enum_case_insensitive("land_surface_model",lsm_type);
858 
859  // What type of urban model to use and whether it is active at the given level
861  pp.query_enum_case_insensitive("urban_model", urban_type);
862  read_int_string(max_level, "urban_lev_active", urban_enabled_lev, 1);
863  for (int lev = 0; lev <= max_level; ++lev) {
864  if (urban_enabled_lev[lev] != 0 && urban_enabled_lev[lev] != 1) {
865  amrex::Error("urban_lev_active should be 0 or 1");
866  }
867  }
868 
869  // Will an external ocean coupler supply SST for part or all of the water
870  // cells this run? The surface layer needs to know this it in time to select
871  // ThetaCalcType::SURFACE_TEMPERATURE, which is long before the first
872  // ApplyOceanSurfaceState call.
873  pp.queryAdd("use_coupled_sst", use_coupled_sst);
874 
875  read_int_string(max_level, "is_land", is_land, 1);
876  for (int lev = 0; lev <= max_level; ++lev) {
877  if (is_land[lev] == 1) {
878  amrex::Print() << "Level " << lev << " is land" << std::endl;
879  } else if (is_land[lev] == 0) {
880  amrex::Print() << "Level " << lev << " is water" << std::endl;
881  } else {
882  amrex::Error("is_land should be 0 or 1");
883  }
884  }
885 
886  // What type of radiation model to use
887  rad_type = RadiationType::None; // Default
888  pp.query_enum_case_insensitive("radiation_model", rad_type);
889 
890  // Verify that radiation model cannot be RRTMGP if ERF was not compiled with RRTMGP
891 #ifndef ERF_USE_RRTMGP
892  if (rad_type == RadiationType::RRTMGP)
893  {
894  amrex::Error("ERF was not compiled with RRTMGP enabled!");
895  }
896 #endif
897 
898  // Is the terrain none, static or moving?
899  std::string terrain_type_temp = "";
900  pp.queryAdd("terrain_type", terrain_type_temp);
901  if (terrain_type_temp == "Moving") {
902  amrex::Error("erf.terrain_type = Moving is no longer supported; please replace Moving by MovingFittedMesh");
903  } else if (terrain_type_temp == "Static") {
904  amrex::Error("erf.terrain_type = Static is no longer supported; please replace Static by StaticFittedMesh");
905  } else {
906  pp.query_enum_case_insensitive("terrain_type",terrain_type);
907  }
908 
909  // Whether the terrain is flat - this allows planar averages (lsf, nudging) with StaticFittedMesh
910  pp.queryAdd("flat_terrain", flat_terrain);
911 
912  // Get buildings type
913  std::string buildings_type_temp = "";
914  pp.queryAdd("buildings_type", buildings_type_temp);
915  if (buildings_type_temp == "ImmersedForcing") {
916  buildings_type = BuildingsType::ImmersedForcing;
917  }
918  pp.queryAdd("buildings_fc_factories", buildings_fc_factories);
919 
920  //
921  // Read the init_type here to make sure we correctly set the mesh and terrain types
922  //
923  std::string init_type_temp_string;
924 
925  int found = pp.queryAdd("init_type",init_type_temp_string);
926 
927  if ( (init_type_temp_string == "Real") || (init_type_temp_string == "real") ) {
928  amrex::Error("erf.init_type = Real is deprecated; please replace Real by WRFInput");
929  } else if ( (init_type_temp_string == "Ideal") || (init_type_temp_string == "ideal") ) {
930  amrex::Error("erf.init_type = Ideal is deprecated; please replace Ideal by WRFInput");
931  } else {
932  pp.query_enum_case_insensitive("init_type",init_type);
933  use_real_bcs = ( (init_type == InitType::WRFInput) || (init_type == InitType::Metgrid) );
934  }
935 
936  if ( (init_type == InitType::WRFInput) || (init_type == InitType::Metgrid) ) {
937  if (terrain_type != TerrainType::StaticFittedMesh) {
938  amrex::Error("Only terrain_type = StaticFittedMesh are allowed with init_type = WRFInput or Metgrid");
939  }
940  }
941 
942  if (init_type == InitType::MoistBaseState) {
944  amrex::Error("Makes no sense to have moist base state with no moisture model");
945  }
946  }
947 
948  if (init_type == InitType::WRFInput) {
950  amrex::Error("Can't have moisture_type = None with init_type = WRFInput");
951  }
952 
953  // NetCDF wrfbdy lateral boundary file
954  std::string nc_bdy_file_temp_string;
955  bool has_bdy = pp.queryAdd("nc_bdy_file", nc_bdy_file_temp_string);
956  if (!has_bdy) use_real_bcs = false;
957 
958  bool use_real_bcs_temp = use_real_bcs;
959  pp.queryAdd("use_real_bcs", use_real_bcs_temp);
960  if (use_real_bcs && !use_real_bcs_temp) {
961  use_real_bcs = false;
962  }
963  }
964 
965  if (found == 0 || init_type == InitType::None) {
966  amrex::Print() << "init_type must now be set. The options are " << std::endl;
967  amrex::Print() << " Input_Sounding, NCFile, WRFInput, Metgrid, Uniform, " << std::endl;
968  amrex::Print() << " ConstantDensity, Isentropic, MoistBaseState, or HindCast " << std::endl;
969  amrex::Error("Please add a string for init_type to your inputs file or command line");
970  }
971 
972  // How to interpret input_sounding
973  if (init_type == InitType::Input_Sounding) {
974  pp.query_enum_case_insensitive("sounding_type",sounding_type);
975  }
976 
977  if (terrain_type == TerrainType::StaticFittedMesh ||
978  terrain_type == TerrainType::MovingFittedMesh) {
979  mesh_type = MeshType::VariableDz;
980  }
981 
982  if (flat_terrain && terrain_type != TerrainType::StaticFittedMesh) {
983  amrex::Error("erf.flat_terrain requires terrain_type = StaticFittedMesh");
984  }
985 
986  // Refined levels over moving terrain are not supported: erf_substep_MT does not take the
987  // fluxes through coarse/fine faces from the coarse level (cf_flux_history_active)
988  if (terrain_type == TerrainType::MovingFittedMesh && max_level > 0) {
989  amrex::Error("erf.terrain_type = MovingFittedMesh requires amr.max_level = 0");
990  }
991 
992  // Immersed-forcing buildings need the terrain blanking that init_zphys builds, and
993  // init_zphys returns before building it when the terrain is EB
994  if (terrain_type == TerrainType::EB && buildings_type == BuildingsType::ImmersedForcing) {
995  amrex::Error("erf.buildings_type = ImmersedForcing is not supported with erf.terrain_type = EB");
996  }
997 
998  if ( (moisture_type == MoistureType::SAM ||
999  moisture_type == MoistureType::SAM_NoIce ||
1000  moisture_type == MoistureType::SAM_NoPrecip_NoIce ) &&
1001  (mesh_type == MeshType::VariableDz) )
1002  {
1003  amrex::Abort("SAM is not correct with variable dz -- choose another moisture model");
1004  }
1005 
1006  // queryAdd puts the default in the table, so ask whether the ratio was given first
1007  const bool have_stretching_ratio = pp.contains("grid_stretching_ratio");
1008  pp.queryAdd("grid_stretching_ratio", grid_stretching_ratio);
1009  if (grid_stretching_ratio != 0) {
1011  "The grid stretching ratio must be greater than 1");
1012  }
1013  // erf.initial_dz on its own asks for the stretching ratio to be solved for, so that
1014  // a first layer that thick stretches geometrically to close on prob_hi[2]. An
1015  // explicit erf.grid_stretching_ratio = 0 still means no stretching at all.
1016  const bool solve_stretching_ratio = !have_stretching_ratio && pp.contains("initial_dz");
1017  if (grid_stretching_ratio >= 1 || solve_stretching_ratio) {
1019  terrain_type = TerrainType::StaticFittedMesh;
1020  }
1021  if (mesh_type == MeshType::ConstantDz) {
1022  mesh_type = MeshType::StretchedDz;
1023  }
1024  pp.get("initial_dz", dz0);
1025  }
1026 
1027  // The stretched levels start at geometry.prob_lo[2]; erf.zsurface used to set the
1028  // bottom separately, and a flat surface did not follow it (#4442)
1029  if (pp.contains("zsurface")) {
1030  amrex::Abort("erf.zsurface is no longer supported: a stretched vertical grid now starts "
1031  "at geometry.prob_lo[2], so set the bottom of the domain there instead");
1032  }
1033 
1034  int n_zlevels = pp.countval("terrain_z_levels");
1035  if (n_zlevels > 0)
1036  {
1038  terrain_type = TerrainType::StaticFittedMesh;
1039  }
1040  if (mesh_type == MeshType::ConstantDz) {
1041  mesh_type = MeshType::StretchedDz;
1042  }
1043  }
1044 
1045  // Use lagged_delta_rt in the fast integrator?
1046  pp.queryAdd("use_lagged_delta_rt", use_lagged_delta_rt);
1047 
1048  // Average the input z-face heights onto the nodes, or reconstruct our own
1049  pp.queryAdd("avg_grid_faces_to_nodes",avg_grid_faces_to_nodes);
1050 
1051  // Build the ERF vertical grid from the file's own mean layer-thickness profile,
1052  // or from a single geometric stretch anchored on the file's first layer?
1053  pp.queryAdd("wrfinput_zlevels_from_file",wrfinput_zlevels_from_file);
1054 
1055  // Catch the old name for the flag above so the error says what to do
1056  // rather than silently ignoring the setting.
1057  if (pp.contains("use_wrf_height_grid")) {
1058  amrex::Abort("erf.use_wrf_height_grid has been renamed. "
1059  "Set erf.avg_grid_faces_to_nodes instead; it carries "
1060  "the same meaning (average the input z-face heights "
1061  "onto the nodes rather than reconstructing them).");
1062  }
1063 
1064  // Interpolate atmospheric state from coarse level instead of reading from wrfinput
1065  // when creating a finer level from coarse at a later time?
1066  pp.queryAdd("interp_atmos_from_coarse", interp_atmos_from_coarse);
1067 
1068  // Rebalance wrf state?
1069  pp.queryAdd("rebalance_wrf_input", rebalance_wrf_input);
1070 
1071  // Must rebalance if the nodal heights were reconstructed rather than averaged
1073  amrex::Print() << "Must rebalance when not averaging the input height grid. Setting rebalance_wrf_input to true \n";
1074  rebalance_wrf_input = true;
1075  }
1076 
1077  // These default to true but are used for unit testing
1078  pp.queryAdd("use_gravity", use_gravity);
1079 
1080  // Initializing from Metgrid or WRFInput without gravity makes no sense
1081  if ( !use_gravity && ( (init_type == InitType::WRFInput) || (init_type == InitType::Metgrid) ) ) {
1082  amrex::Print() << "Setting use_gravity to true due to init_type = WRFInput / Metgrid " << std::endl;
1083  use_gravity = true;
1084  }
1085 
1087 
1088  pp.queryAdd("c_p", c_p);
1089  rdOcp = R_d / c_p;
1090 
1091  // *******************************************************************************
1092  // Read anelastic etc and over-ride if necessary
1093  // *******************************************************************************
1094 
1095  read_int_string(max_level, "anelastic", anelastic, 0);
1096  const bool fixed_density_given =
1097  read_int_string(max_level, "fixed_density", fixed_density, 0);
1098  read_int_string(max_level, "project_initial_velocity", project_initial_velocity, 0);
1099 
1100  // The anelastic equations hold the density at its base-state value, so
1101  // fixed_density is forced to 1 at every anelastic level; a user value of 0
1102  // there is ignored, and we say so rather than drop it silently.
1103  bool any_anelastic = false;
1104  bool any_compress = false;
1105  for (int i = 0; i <= max_level; ++i) {
1106  if (anelastic[i] == 1) {
1107  if (fixed_density_given && fixed_density[i] == 0) {
1108  amrex::Print() << "WARNING: erf.fixed_density = 0 is ignored at level " << i
1109  << " because erf.anelastic = 1 there; the anelastic equations"
1110  << " always hold the density fixed in time." << std::endl;
1111  }
1112  project_initial_velocity[i] = 1;
1113  fixed_density[i] = 1; // Always fixed when anelastic
1114  buoyancy_type[i] = 3; // (This isn't actually used when anelastic is set)
1115  any_anelastic = true;
1116  } else {
1117  any_compress = true;
1118  }
1119  }
1120 
1122  amrex::Error("erf.moisture_model = SuperDroplets is not supported with "
1123  "erf.anelastic = 1: the Super-Droplet path currently uses "
1124  "the compressible equation of state. Use compressible "
1125  "dynamics or a supported Eulerian moisture model.");
1126  }
1127 
1128  // Want to have different immersed forcing defaults depending on anelastic or fully compressible.
1129  // We should have different starting values because the stability of the method is dependent on dt.
1130  // dt is coarser for anelastic --> need to loosen stiffness of immersed forcing.
1131  if (any_anelastic) {
1132  immersed_forcing_substep = false;
1133  if_Cd_momentum = amrex::Real(50.0);
1134  if_Cd_scalar = amrex::Real(5.0);
1135  } else {
1136  immersed_forcing_substep = true;
1137  if_Cd_momentum = amrex::Real(500.0);
1138  if_Cd_scalar = amrex::Real(50.0);
1139  }
1140 
1141  // *******************************************************************************
1142  // Read anelastic_type and allow for different values at each level
1143  // *******************************************************************************
1144  anelastic_type.resize(max_level+1);
1145 
1146  for (int i = 0; i <= max_level; i++) {
1147  anelastic_type[i] = AnelasticType::RK2;
1148  }
1149 
1150  for (int i = 0; i <= max_level; i++) {
1152  pp, "anelastic_type", anelastic_type[i], i, max_level);
1153  }
1154 
1155  if (pp.contains("anelastic_type") && !any_anelastic) {
1156  amrex::Print() << "Ignoring erf.anelastic_type because no level is anelastic" << std::endl;
1157  for (int i = 0; i <= max_level; i++) { anelastic_type[i] = AnelasticType::RK2; }
1158  }
1159 
1160  // *******************************************************************************
1161  // Read substepping_type and allow for different values at each level
1162  // *******************************************************************************
1163  substepping_type.resize(max_level+1);
1164 
1165  for (int i = 0; i <= max_level; i++) {
1166  substepping_type[i] = SubsteppingType::Implicit;
1167  }
1168 
1169  for (int i = 0; i <= max_level; i++) {
1171  pp, "substepping_type", substepping_type[i], i, max_level);
1172  }
1173 
1174  pp.queryAdd("substepping_diag", substepping_diag);
1175 
1176  pp.queryAdd("beta_s", beta_s);
1177 
1178  // Divergence damping coefficient for the forward projection of (rho theta)
1179  pp.queryAdd("beta_d", beta_d);
1180  if (!(beta_d >= amrex::Real(0.0) && beta_d <= amrex::Real(1.0))) {
1181  amrex::Abort("erf.beta_d must lie in [0, 1]; got " + std::to_string(beta_d));
1182  }
1183 
1184  // Spatially varying beta_s on the w-faces (implicit substepping only):
1185  // beta_s = max(beta_s_min, min(beta_s_max, h_xi^2 + h_eta^2))
1186  pp.queryAdd("use_variable_beta_s", use_variable_beta_s);
1187  pp.queryAdd("beta_s_min", beta_s_min);
1188  pp.queryAdd("beta_s_max", beta_s_max);
1189  if (use_variable_beta_s) {
1191  "erf.beta_s_min must be less than or equal to erf.beta_s_max");
1193  "erf.beta_s_min and erf.beta_s_max must lie in [-1, 1]");
1194  }
1195 
1196  // *******************************************************************************
1197  // Error check on deprecated input
1198  // *******************************************************************************
1199  int nvals_old = pp.countval("no_substepping");
1200  if (nvals_old > 0) {
1201  amrex::Error("The no_substepping flag is deprecated -- set substepping_type instead");
1202  }
1203 
1204  pp.queryAdd("ncorr", ncorr);
1205  // Wall distance on a terrain-fitted mesh (RANS): "poisson" (Tucker 2003
1206  // differential-equation distance) or "terrain_height" (height above the
1207  // local surface projected on its normal, no linear solve)
1208  pp.queryAdd("wall_dist_type", wall_dist_type);
1209  if (wall_dist_type != "poisson" && wall_dist_type != "terrain_height") {
1210  amrex::Error("erf.wall_dist_type must be poisson or terrain_height");
1211  }
1212  pp.queryAdd("poisson_abstol", poisson_abstol);
1213  pp.queryAdd("poisson_reltol", poisson_reltol);
1214  // Solver for the projection on a terrain-fitted mesh: FFT-preconditioned GMRES (default),
1215  // or amrex::MLTerrainPoisson with multigrid, alone or under GMRES
1216  pp.query_enum_case_insensitive("terrain_poisson_solver", terrain_poisson_solver);
1217  if (pp.contains("terrain_poisson_solver") &&
1218  (mesh_type != MeshType::VariableDz || terrain_type == TerrainType::EB)) {
1219  amrex::Print() << "WARNING: erf.terrain_poisson_solver is ignored: it applies only to the "
1220  << "projection on a terrain-fitted mesh (erf.terrain_type = StaticFittedMesh "
1221  << "or MovingFittedMesh)" << std::endl;
1222  }
1223 #ifdef AMREX_USE_FLOAT
1224  poisson_abstol = amrex::max(poisson_abstol,amrex::Real(1e-6));
1225  poisson_reltol = amrex::max(poisson_reltol,amrex::Real(1e-6));
1226 #endif
1227  for (int lev = 0; lev <= max_level; lev++) {
1228  if (anelastic[lev] != 0)
1229  {
1231  }
1232  }
1233 
1234  pp.queryAdd("force_stage1_single_substep", force_stage1_single_substep);
1235 
1236  // Include Coriolis forcing?
1237  pp.queryAdd("use_coriolis", use_coriolis);
1238  pp.queryAdd("variable_coriolis", variable_coriolis);
1239 
1240  // Include four stream radiation approximation
1241  pp.queryAdd("four_stream_radiation", four_stream_radiation);
1242 
1243  // flags for whether to apply other source terms in substep only
1244  pp.queryAdd("immersed_forcing_substep", immersed_forcing_substep); // apply immersed forcing source terms in substep only
1245  pp.queryAdd("forest_substep", forest_substep); // apply canopy-related source terms in substep only
1246 
1247  // Fixed-leaf-temperature canopy heat exchange
1248  pp.query("forest_biophysics", forest_biophysics);
1249  pp.query("forest_biophysics_heat", forest_biophysics_heat);
1250  pp.query("forest_leaf_theta_fixed", forest_leaf_theta_fixed);
1251 
1254  "forest_biophysics_heat requires forest_biophysics = true");
1258  "forest_biophysics_heat requires a positive forest_leaf_theta_fixed");
1261  "forest_leaf_theta_fixed requires forest_biophysics_heat = true");
1262 
1263  // Canopy terms of the k-eqn RANS model (erf.forest_canopy_*); the
1264  // checks that need the forest and the turbulence model are in
1265  // ERF::ReadParameters, once both are known
1267 
1268  // immersed forcing parameters
1269  pp.queryAdd("if_Cd_momentum", if_Cd_momentum);
1270  pp.queryAdd("if_Cd_scalar", if_Cd_scalar);
1271  pp.queryAdd("if_implicit_drag", if_implicit_drag); // flag for implicit vs. explicit drag formulation
1272  pp.queryAdd("if_z0", if_z0);
1273  pp.queryAdd("if_surf_temp_flux", if_surf_temp_flux);
1274  pp.queryAdd("if_init_surf_temp", if_init_surf_temp);
1275 
1276  pp.queryAdd("if_surf_heating_rate", if_surf_heating_rate);
1277 
1278  // Modify rate to be in units of K / s rather than K / hr
1279  if_surf_heating_rate /= amrex::Real(3600.0); // [K/s]
1280 
1281  pp.queryAdd("if_Olen", if_Olen_in);
1282  pp.queryAdd("if_use_most",if_use_most);
1283  pp.queryAdd("if_snap_partial_cells",if_snap_partial_cells);
1284  // The snapped staircase puts full-strength forcing on every wall face,
1285  // whose explicit relaxation rate (Cd / dx per substep) sits at the
1286  // stability limit and fails where faces stack at rims and corners; the
1287  // point-implicit form holds at any rate, so the snap uses it.
1289  amrex::Print() << "erf.if_snap_partial_cells: using the point-implicit drag (erf.if_implicit_drag = true)\n";
1290  if_implicit_drag = true;
1291  }
1292  pp.queryAdd("if_stability_correction",if_stability_correction);
1293 
1294  // Wall law of the terrain immersed forcing (erf-model/ERF#4134): "legacy" (the default)
1295  // relaxes the top partial cell toward the log law at fixed 0.5/1.5 dz;
1296  // "fraction_stress" applies the wall stress of ERF_ImmersedWallCell.H in the wall cell
1297  // and closes the wall face to diffusion (form 7 of the kynema-sgf immersed terrain)
1298  std::string if_wall_form_in = "legacy";
1299  pp.queryAdd("if_wall_form", if_wall_form_in);
1300  if (if_wall_form_in == "fraction_stress") {
1301  if_fraction_stress = true;
1302  } else if (if_wall_form_in != "legacy") {
1303  amrex::Abort("erf.if_wall_form must be legacy or fraction_stress, not " + if_wall_form_in);
1304  }
1305  // Spacing of the face above the wall cell (fraction_stress only): "log" (default) gives
1306  // that face the relative error of flat ground's first interior face for a log profile,
1307  // "physical" the centroid spacing, "grid" the cell height (ib_wall::face_above_factor)
1308  // (the keys below are accepted at their default values with the legacy law, so one deck
1309  // can run both laws; any other value belongs to fraction_stress)
1310  std::string if_wall_face_spacing_in = "log";
1311  pp.queryAdd("if_wall_face_spacing", if_wall_face_spacing_in);
1312  if (if_wall_face_spacing_in != "log" && !if_fraction_stress) {
1313  amrex::Abort("erf.if_wall_face_spacing belongs to erf.if_wall_form = fraction_stress");
1314  }
1315  if (if_wall_face_spacing_in == "grid") { if_wall_face_spacing = 0; }
1316  else if (if_wall_face_spacing_in == "physical") { if_wall_face_spacing = 1; }
1317  else if (if_wall_face_spacing_in == "log") { if_wall_face_spacing = 2; }
1318  else { amrex::Abort("erf.if_wall_face_spacing must be log, physical or grid"); }
1319  // Wall value of the k-eqn TKE in the fraction-stress wall cell, (u*^3 + kappa B d1)^(2/3)
1320  // / Cmu0^2 (the value erf.dirichlet_k holds over flat ground): with a time factor of 1
1321  // (the default) the wall cell is held at it after every stage and in the implicit TKE
1322  // solve, as flat ground is; a larger factor relaxes toward it as a source over that many
1323  // steps (the kynema-sgf form)
1324  pp.queryAdd("if_wall_tke", if_wall_tke);
1325  pp.queryAdd("if_wall_tke_time_factor", if_wall_tke_time_factor);
1327  amrex::Abort("erf.if_wall_tke = true and erf.if_wall_tke_time_factor != 1 belong to erf.if_wall_form = fraction_stress");
1328  }
1329  if (!(if_wall_tke_time_factor >= one)) {
1330  amrex::Abort("erf.if_wall_tke_time_factor must be >= 1 (steps over which the wall TKE is relaxed)");
1331  }
1332  // Partial wall cells of the fraction-stress law dissipate TKE with the length scale of flat
1333  // ground's first cell (kynema-sgf KLAxell.flat_wall_dissipation); on by default with it.
1334  // query, not queryAdd: the default depends on erf.if_wall_form, so storing it would make a
1335  // later init_params in the same process (the unit tests) see it as given (cf. #4940)
1337  pp.query("if_flat_wall_dissipation", if_flat_wall_dissipation);
1339  amrex::Abort("erf.if_flat_wall_dissipation = true belongs to erf.if_wall_form = fraction_stress");
1340  }
1341  if (if_fraction_stress) {
1342  if (terrain_type != TerrainType::ImmersedForcing) {
1343  amrex::Abort("erf.if_wall_form = fraction_stress needs erf.terrain_type = ImmersedForcing");
1344  }
1345  if (buildings_type == BuildingsType::ImmersedForcing) {
1346  amrex::Abort("erf.if_wall_form = fraction_stress is not available with erf.buildings_type = ImmersedForcing");
1347  }
1348  if (!if_use_most) {
1349  amrex::Abort("erf.if_wall_form = fraction_stress is a wall law: it needs erf.if_use_most = true");
1350  }
1351  if (mesh_type != MeshType::ConstantDz) {
1352  amrex::Abort("erf.if_wall_form = fraction_stress needs a constant-dz mesh");
1353  }
1354  // The wall heat flux and the wall TKE are written by the slow scalar source; the
1355  // compressible fast substep (erf.immersed_forcing_substep) does not carry them
1357  amrex::Abort("erf.if_wall_form = fraction_stress runs with the anelastic model (erf.anelastic = 1 on "
1358  "every level); it does not support erf.immersed_forcing_substep = true");
1359  }
1360  // One surface condition (heat flux, surface temperature - optionally changing at
1361  // erf.if_surf_heating_rate - or Obukhov length; none is neutral, checked below); the
1362  // MOST stability functions always apply, as on the flat surface layer, so
1363  // erf.if_stability_correction (a legacy switch) must not be set
1365  amrex::Abort("erf.if_surf_heating_rate needs erf.if_init_surf_temp");
1366  }
1368  amrex::Abort("erf.if_wall_form = fraction_stress always applies the stability functions; "
1369  "erf.if_stability_correction is for the legacy wall law");
1370  }
1371  }
1372  pp.queryAdd("if_ws_floor",if_ws_floor);
1373  pp.queryAdd("if_damp_alpha",if_damp_alpha);
1374  // Opt-in WRF-style bounds on the wall law (erf-model/ERF#4016): the wind floor behind the
1375  // friction velocity of its stability estimate, and the cap on psi_m in the momentum law as
1376  // a fraction of ln(z/z0); 0 and 1 (the defaults) turn them off
1377  pp.queryAdd("if_stability_wind_floor",if_stability_wind_floor);
1378  pp.queryAdd("if_psi_cap_factor",if_psi_cap_factor);
1379  if (!(if_stability_wind_floor >= zero)) {
1380  amrex::Abort("erf.if_stability_wind_floor must be >= 0 [m/s] (0 leaves the wind as it is)");
1381  }
1382  if (!(if_psi_cap_factor > zero && if_psi_cap_factor <= one)) {
1383  amrex::Abort("erf.if_psi_cap_factor must be in (0, 1] (1 caps psi at ln(z/z0))");
1384  }
1385  // The cell average down over immersed-forcing levels (erf-model/ERF#4918): the plain
1386  // average (the default), or one weighted by the fluid mass of the fine cells
1387  pp.queryAdd("if_average_down", if_average_down);
1388  if (if_average_down != "plain" && if_average_down != "fluid_mass") {
1389  amrex::Abort("erf.if_average_down must be plain or fluid_mass");
1390  }
1391  // (queryAdd puts the default into the table, so the test is on the value, not on contains)
1392  if (if_average_down == "fluid_mass" &&
1393  terrain_type != TerrainType::ImmersedForcing && buildings_type != BuildingsType::ImmersedForcing)
1394  {
1395  amrex::Abort("erf.if_average_down = fluid_mass needs erf.terrain_type = ImmersedForcing "
1396  "or erf.buildings_type = ImmersedForcing");
1397  }
1398  // Warn at start-up where a lateral coarse/fine boundary crosses the immersed surface so
1399  // that the levels disagree: spread of the fine solid fractions under a coarse edge cell
1400  // at least this (0 turns the check off)
1401  pp.queryAdd("if_cf_mismatch_warning", if_cf_mismatch_warning);
1403  amrex::Abort("erf.if_cf_mismatch_warning must be in [0, 1] (a spread of solid fractions; 0 turns it off)");
1404  }
1405  // Log-law share of the wall coarse face's flux among its fine sub-faces at the lateral
1406  // coarse/fine faces (erf-model/ERF#4920): the immersed surface or a surface-layer bottom
1407  // (checked in check_params); constant dz, not EB terrain. The roughness length defaults
1408  // to erf.if_z0 with immersed forcing and to the inputs' erf.most.z0 or erf.zlo.most.z0
1409  // (0.1 m when neither is given) otherwise.
1410  pp.queryAdd("cf_loglaw_fill", cf_loglaw_fill);
1411  const bool z0_given = pp.query("cf_loglaw_z0", cf_loglaw_z0);
1412  if (z0_given && !cf_loglaw_fill) {
1413  amrex::Abort("erf.cf_loglaw_z0 belongs to erf.cf_loglaw_fill = true");
1414  }
1415  if (cf_loglaw_fill) {
1416  if (mesh_type != MeshType::ConstantDz) {
1417  amrex::Abort("erf.cf_loglaw_fill needs a constant-dz mesh");
1418  }
1419  if (terrain_type == TerrainType::EB) {
1420  amrex::Abort("erf.cf_loglaw_fill is not implemented for erf.terrain_type = EB "
1421  "(its wall is not the bottom of the domain)");
1422  }
1423  if (!z0_given) {
1424  if (terrain_type == TerrainType::ImmersedForcing || buildings_type == BuildingsType::ImmersedForcing) {
1425  cf_loglaw_z0 = if_z0;
1426  } else {
1427  // the roughness given in the inputs for the bottom surface layer
1428  // (erf.most.z0, or erf.zlo.most.z0 when the faces are set one by one); a
1429  // roughness a land model supplies later is not seen here
1430  cf_loglaw_z0 = amrex::Real(0.1);
1431  pp.query("most.z0", cf_loglaw_z0);
1432  pp.query("zlo.most.z0", cf_loglaw_z0);
1433  }
1434  }
1435  if (!(cf_loglaw_z0 > zero)) {
1436  amrex::Abort("erf.cf_loglaw_z0 must be > 0 [m] (the roughness length of the log-law coarse/fine fill)");
1437  }
1438  }
1439  // Say so when a bound is set and the wall law's stability estimate is in use
1440  if ((terrain_type == TerrainType::ImmersedForcing || buildings_type == BuildingsType::ImmersedForcing) &&
1441  (if_use_most || if_surf_temp_flux != amrex::Real(1e-8) || if_Olen_in != amrex::Real(1e-8)) &&
1442  (if_psi_cap_factor < one || if_stability_wind_floor > zero))
1443  {
1444  amrex::Print() << "Immersed wall law: erf.if_psi_cap_factor = " << if_psi_cap_factor
1445  << ", erf.if_stability_wind_floor = " << if_stability_wind_floor
1446  << " m/s (WRF-style bounds; the defaults 1 and 0 turn them off)\n";
1447  }
1448 
1449  if ((if_init_surf_temp > zero && if_surf_temp_flux != amrex::Real(1e-8)) ||
1450  (if_init_surf_temp > zero && if_Olen_in != amrex::Real(1e-8)) ||
1451  (if_Olen_in != amrex::Real(1e-8) && if_surf_temp_flux != amrex::Real(1e-8)))
1452  {
1453  amrex::Error("Can only specify one of init_surf_temp, surf_temp_flux, or Olen");
1454  }
1455  if (if_Olen_in == zero &&
1456  (terrain_type == TerrainType::ImmersedForcing || buildings_type == BuildingsType::ImmersedForcing)) {
1457  amrex::Abort("erf.if_Olen = 0 is not an Obukhov length: give a nonzero length [m] (a large |L| is near neutral)");
1458  }
1459 
1460  // The immersed forcing's "almost fluid" threshold (erf-model/ERF#4226). It is recorded
1461  // under eb2.small_volfrac here, in ReadParameters, before ERF::initializeEB runs, so the
1462  // EB build (which covers cells below it), the blanking and the forcing kernels all use
1463  // the same value, and a deck without the key gets 0.005 rather than AMReX's 1e-14.
1464  if (terrain_type == TerrainType::ImmersedForcing || buildings_type == BuildingsType::ImmersedForcing) {
1465  amrex::ParmParse pp_eb2("eb2");
1466  pp_eb2.queryAdd("small_volfrac", if_small_volfrac);
1467  if (!(if_small_volfrac >= zero && if_small_volfrac < one)) {
1468  amrex::Abort("eb2.small_volfrac must be in [0, 1) with immersed forcing");
1469  }
1470  }
1471 
1472  // Implicit drag of the immersed forcing inside the anelastic projection (erf-model/ERF#4919
1473  // for terrain, #4887 for buildings): each face momentum becomes (m* - grad phi) / (1 + dt rate),
1474  // with rate the drag rate of the source term on that face, and the solid is not frozen
1475  // afterwards, so the projected field stays divergence free in the solid too
1476  pp.queryAdd("if_implicit_projection", if_implicit_projection);
1477  if (if_implicit_projection) {
1478  const bool if_terrain = (terrain_type == TerrainType::ImmersedForcing);
1479  const bool if_buildings = (buildings_type == BuildingsType::ImmersedForcing);
1480  if (!if_terrain && !if_buildings) {
1481  amrex::Abort("erf.if_implicit_projection needs erf.terrain_type = ImmersedForcing or "
1482  "erf.buildings_type = ImmersedForcing");
1483  }
1484  if (if_terrain && if_buildings) {
1485  amrex::Abort("erf.if_implicit_projection is not implemented for erf.terrain_type = ImmersedForcing "
1486  "together with erf.buildings_type = ImmersedForcing");
1487  }
1488  if (mesh_type != MeshType::ConstantDz) {
1489  amrex::Abort("erf.if_implicit_projection needs a constant-dz mesh");
1490  }
1491  for (int lev = 0; lev <= max_level; ++lev) {
1492  if (!anelastic[lev]) {
1493  amrex::Abort("erf.if_implicit_projection needs erf.anelastic = 1 on every level");
1494  }
1495  }
1496 #ifndef ERF_USE_FFT
1497  amrex::Abort("erf.if_implicit_projection needs a build with FFT (its GMRES solve is FFT-preconditioned)");
1498 #endif
1499  }
1500 
1501  // Flag to do MOST rotations with terrain
1502  pp.queryAdd("use_rotate_surface_flux",use_rotate_surface_flux);
1504  AMREX_ASSERT_WITH_MESSAGE(terrain_type != TerrainType::None,"MOST stress rotations are only valid with terrain!");
1505  }
1506 
1507  // Which external forcings?
1508  abl_driver_type = ABLDriverType::None; // Default: no ABL driver for simulating classical fluid dynamics problems
1509  pp.query_enum_case_insensitive("abl_driver_type",abl_driver_type);
1510  pp.queryAdd("const_massflux_u", const_massflux_u);
1511  pp.queryAdd("const_massflux_v", const_massflux_v);
1512  pp.queryAdd("const_massflux_tau", const_massflux_tau);
1513  // The control divides by tau; tau <= 0 gives no source or one that drives the flow
1514  // away from the target
1515  if (!(const_massflux_tau > amrex::Real(0))) {
1516  amrex::Abort("erf.const_massflux_tau must be > 0 (got " +
1517  std::to_string(const_massflux_tau) + ")");
1518  }
1519  pp.queryAdd("const_massflux_layer_lo", const_massflux_layer_lo);
1520  pp.queryAdd("const_massflux_layer_hi", const_massflux_layer_hi);
1521 
1522  // Which type of inflow turbulent generation
1523  // Real-data initialization uses parent controls unless explicitly overridden.
1524  perturbation_auto = (init_type == InitType::WRFInput || init_type == InitType::Metgrid);
1525  pp.queryAdd("perturbation_auto", perturbation_auto);
1526  pp.queryAdd("perturbation_auto_period", perturbation_auto_period);
1527  pp.queryAdd("perturbation_auto_pbl_fraction", perturbation_auto_pbl_fraction);
1528  if (perturbation_auto &&
1529  (!(perturbation_auto_period > zero) || !std::isfinite(perturbation_auto_period) ||
1531  amrex::Abort("Automatic perturbations require perturbation_auto_period > 0 and "
1532  "0 < perturbation_auto_pbl_fraction <= 1");
1533  }
1534  pert_type.resize(max_level+1);
1535  for (int lev = 0; lev <= max_level; ++lev) {
1536  pert_type[lev] = PerturbationType::None; // Default
1538  pp, "perturbation_type", pert_type[lev], lev, max_level);
1539  }
1540 
1541  amrex::Vector<amrex::Real> abl_pressure_grad_in = {zero, zero, zero};
1542  pp.queryarr("abl_pressure_grad",abl_pressure_grad_in);
1543  for(int i = 0; i < AMREX_SPACEDIM; ++i) abl_pressure_grad[i] = abl_pressure_grad_in[i];
1544 
1545  amrex::Vector<amrex::Real> abl_geo_forcing_in = {zero, zero, zero};
1546  if(pp.queryarr("abl_geo_forcing",abl_geo_forcing_in)) {
1547  amrex::Print() << "Specified abl_geo_forcing: (";
1548  for (int i = 0; i < AMREX_SPACEDIM; ++i) {
1549  abl_geo_forcing[i] = abl_geo_forcing_in[i];
1550  amrex::Print() << abl_geo_forcing[i] << " ";
1551  }
1552  amrex::Print() << ")" << std::endl;
1553  }
1554 
1555  if (use_coriolis)
1556  {
1558  }
1559 
1560  pp.queryAdd("add_custom_rhotheta_forcing", custom_rhotheta_forcing);
1561  pp.queryAdd("add_custom_moisture_forcing", custom_moisture_forcing);
1562  pp.queryAdd("add_custom_w_subsidence", custom_w_subsidence);
1563  pp.queryAdd("add_do_theta_advection", do_theta_advection); // If true, apply custom subsidence to (rho*theta) when add_custom_w_subsidence is used
1564  pp.queryAdd("add_do_mom_advection", do_mom_advection); // If true, apply custom subsidence to momentum when add_custom_w_subsidence is used
1565  pp.queryAdd("add_custom_geostrophic_profile", custom_geostrophic_profile);
1566  pp.queryAdd("custom_forcing_uses_primitive_vars", custom_forcing_prim_vars);
1567  pp.queryAdd("spatial_rhotheta_forcing", spatial_rhotheta_forcing);
1568  pp.queryAdd("spatial_moisture_forcing", spatial_moisture_forcing);
1569 
1570  pp.queryAdd("nudging_from_input_sounding", nudging_from_input_sounding);
1571 
1572  // Nudging towards observations at stations; the stations and the
1573  // parameters are read by the ObsNudging class, under erf.obs_nudging
1574  pp.queryAdd("nudging_from_observations", nudging_from_observations);
1575  if (nudging_from_observations && terrain_type == TerrainType::EB) {
1576  amrex::Abort("erf.nudging_from_observations does not support erf.terrain_type = EB; "
1577  "use a terrain-fitted mesh or immersed forcing");
1578  }
1579 
1580  int nudging_u_z1_set = pp.queryAdd("nudging_u_z1", nudging_u_z1);
1581  int nudging_u_z2_set = pp.queryAdd("nudging_u_z2", nudging_u_z2);
1582  pp.queryAdd("nudging_t_z1", nudging_t_z1);
1583  pp.queryAdd("nudging_t_z2", nudging_t_z2);
1584  pp.queryAdd("nudging_q_z1", nudging_q_z1);
1585  pp.queryAdd("nudging_q_z2", nudging_q_z2);
1586 
1588  "nudging_u_z1 must be less than or equal to nudging_u_z2");
1590  "nudging_t_z1 must be less than or equal to nudging_t_z2");
1592  "nudging_q_z1 must be less than or equal to nudging_q_z2");
1593 
1594  pp.queryAdd("nudging_u", nudging_u);
1595  pp.queryAdd("nudging_t", nudging_t);
1596  pp.queryAdd("nudging_q", nudging_q);
1597 
1598  pp.queryAdd("large_scale_forcing", large_scale_forcing);
1599 
1601  if (!nudging_u) {
1602  amrex::Print() << "WARNING: erf.nudging_u = false has no effect when erf.large_scale_forcing "
1603  "= true; momentum nudging is then controlled solely by large_scale_forcing.\n";
1604  }
1605  if (nudging_u_z1_set || nudging_u_z2_set) {
1606  amrex::Print() << "WARNING: erf.nudging_u_z1/erf.nudging_u_z2 are ignored when "
1607  "erf.large_scale_forcing = true; momentum nudging is then applied "
1608  "at all heights.\n";
1609  }
1610  }
1611 
1614  "Should not have both abl_geo_wind_table and custom_geostrophic_profile set.");
1615 
1616  pp.queryAdd("Ave_Plane", ave_plane);
1617 
1618  // Use numerical diffusion?
1619  pp.queryAdd("num_diff_coeff",num_diff_coeff);
1621  "Numerical diffusion coefficient must be between 0 & one");
1622  use_num_diff = (num_diff_coeff > 0);
1623  if (use_num_diff) {
1624  amrex::Print() << "6th-order numerical diffusion turned on with coefficient = "
1625  << num_diff_coeff << std::endl;
1626  num_diff_coeff *= amrex::Math::powi<-6>(two);
1627  }
1628 
1629  advChoice.init_params(pp_prefix);
1630  diffChoice.init_params(pp_prefix);
1631  dampingChoice.init_params(pp_prefix);
1632  spongeChoice.init_params(pp_prefix);
1633  ebChoice.init_params(pp_prefix);
1634 
1635  turbChoice.resize(max_level+1);
1636  for (int lev = 0; lev <= max_level; lev++) {
1637  turbChoice[lev].init_params(lev,max_level,pp_prefix);
1638  }
1639 
1640  if (perturbation_auto) {
1641  // Automatic forcing has a parent and a three-dimensional LES closure.
1642  // A horizontal Smagorinsky closure paired with a PBL scheme is mesoscale.
1643  for (int lev = 0; lev <= max_level; ++lev) {
1644  const auto& tc = turbChoice[lev];
1645  if (lev == 0 || tc.les_type == LESType::None || tc.smag2d ||
1646  tc.pbl_type != PBLType::None) {
1648  }
1649  }
1650  }
1651 
1652  // WRF's Smagorinsky-2D slope limiter divides K_h by the terrain slope factor of the
1653  // mesh, which is 1 everywhere without a terrain-fitted mesh; and the EB closure
1654  // (ComputeTurbulentViscosityLES_EB) applies neither limit.
1655  bool smag2d_requested = false;
1656  bool smag2d_somewhere = false;
1657  for (int lev = 0; lev <= max_level; lev++) {
1658  smag2d_requested = smag2d_requested || turbChoice[lev].smag2d_limits_requested;
1659  smag2d_somewhere = smag2d_somewhere || turbChoice[lev].smag2d;
1660  }
1661  if (smag2d_requested && !smag2d_somewhere) {
1662  amrex::Error("erf.smag2d_slope_limiter and erf.smag2d_kh_cap apply only to "
1663  "erf.les_type = Smagorinsky2D, which no level uses");
1664  }
1665  for (int lev = 0; lev <= max_level; lev++) {
1666  const bool fitted = (terrain_type == TerrainType::StaticFittedMesh ||
1667  terrain_type == TerrainType::MovingFittedMesh);
1668  if (turbChoice[lev].smag2d_slope_limiter && !fitted) {
1669  amrex::Error("erf.smag2d_slope_limiter requires erf.terrain_type = StaticFittedMesh "
1670  "or MovingFittedMesh");
1671  }
1672  if (turbChoice[lev].smag2d_kh_cap > zero && terrain_type == TerrainType::EB) {
1673  amrex::Error("erf.smag2d_kh_cap is not supported with erf.terrain_type = EB");
1674  }
1675  }
1676 
1677  // erf.radiation_model = TwoStream selects the two-stream model; its
1678  // erf.radiation.* sub-options are read only then.
1679  radChoice.init_params(rad_type == RadiationType::TwoStream, pp_prefix);
1680  if (rad_type == RadiationType::RRTMGP) {
1681  rrtmgpSolarChoice.read(pp_prefix);
1682  }
1684  if (topographicChoice.active()) {
1685  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(terrain_type == TerrainType::StaticFittedMesh,
1686  "Topographic radiation requires erf.terrain_type = StaticFittedMesh");
1687  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(rad_type == RadiationType::RRTMGP,
1688  "Topographic radiation currently requires erf.radiation_model = RRTMGP");
1689  }
1690 
1691  // Noah-MP integrates on the downwelling shortwave, downwelling longwave and solar
1692  // zenith angle a radiation model writes into it. Only RRTMGP and TwoStream register
1693  // those outputs with the land model (rad_feeds_lsm, ERF::init_stuff), so under any
1694  // other choice they are never written and Noah-MP runs with no radiative forcing at
1695  // all -- the inputs it reads fall back to zero. That is a legitimate thing to want
1696  // in a test, and rarely what anyone running a land model means, so say it at
1697  // start-up.
1698  if (lsm_type == LandSurfaceType::NOAHMP && !rad_feeds_lsm()) {
1699  amrex::Print() << "WARNING: erf.land_surface_model = NOAHMP with erf.radiation_model = "
1700  << amrex::getEnumNameString(rad_type)
1701  << ": only RRTMGP and TwoStream supply the downwelling shortwave, "
1702  "downwelling longwave and solar zenith angle Noah-MP needs. Noah-MP "
1703  "will run with no radiative forcing (zero SWDOWN, GLW and COSZEN).\n";
1704  }
1705 
1706  vert_implicit_fac.resize(max_level+1);
1707  for (int lev = 0; lev <= max_level; lev++) {
1708  vert_implicit_fac[lev].resize(3);
1709  vert_implicit_fac[lev][0] = one;
1710  vert_implicit_fac[lev][1] = one;
1711  vert_implicit_fac[lev][2] = zero;
1712  }
1713 
1714  // YSU PBL: use consistent coriolis frequency
1715  for (int lev = 0; lev <= max_level; lev++) {
1716  if (turbChoice[lev].pbl_ysu_use_consistent_coriolis) {
1717  if (use_coriolis) {
1718  turbChoice[lev].pbl_ysu_coriolis_freq = coriolis_factor * sinphi;
1719  if (lev == 0) {
1720  amrex::Print() << "YSU PBL using ERF coriolis frequency: " << turbChoice[lev].pbl_ysu_coriolis_freq << std::endl;
1721  }
1722  } else {
1723  amrex::Error("YSU cannot use ERF coriolis frequency if not using coriolis");
1724  }
1725  }
1726  }
1727  // MRF
1728  for (int lev = 0; lev <= max_level; lev++) {
1729  if (turbChoice[lev].pbl_ysu_use_consistent_coriolis) {
1730  if (use_coriolis) {
1731  turbChoice[lev].pbl_ysu_coriolis_freq = coriolis_factor * sinphi;
1732  if (lev == 0) {
1733  amrex::Print() << "MRF PBL using ERF coriolis frequency: " << turbChoice[lev].pbl_ysu_coriolis_freq << std::endl;
1734  }
1735  } else {
1736  amrex::Error("MRF cannot use ERF coriolis frequency if not using coriolis");
1737  }
1738  }
1739  }
1740 
1741  // What's the strength of the bdy nudging?
1742  pp.queryAdd("bdy_nudge_factor",bdy_nudge_factor);
1743 
1744  // Optionally use the dry-air density reconstructed from wrfbdy.
1745  pp.queryAdd("use_wrf_bdy_density", use_wrf_bdy_density);
1746 
1747  // Optionally ingest cloud water and cloud ice from wrfinput/wrfbdy.
1748  pp.queryAdd("use_wrf_bdy_qc_qi", use_wrf_bdy_qc_qi);
1749 
1750  if (!use_real_bcs || init_type != InitType::WRFInput) {
1751  use_wrf_bdy_density = false;
1752  }
1753 
1754  pp.queryAdd("bdy_rho_nudge_factor", bdy_rho_nudge_factor);
1755 
1756  // Which approach to nudging the moist variables in the boundary region?
1757  pp.queryAdd("bdy_moist_nudge_type",bdy_moist_nudge_type);
1758  if (bdy_moist_nudge_type < 0 || bdy_moist_nudge_type > 3) {
1759  amrex::Error("erf.bdy_moist_nudge_type must be one of 0, 1, 2, or 3");
1760  }
1761  if (bdy_moist_nudge_type == 3 && !use_wrf_bdy_qc_qi) {
1762  amrex::Error("erf.bdy_moist_nudge_type = 3 requires erf.use_wrf_bdy_qc_qi = true");
1763  }
1764  if (use_wrf_bdy_qc_qi) {
1766  amrex::Error("erf.use_wrf_bdy_qc_qi requires an active moisture model");
1767  }
1768  if (!use_real_bcs || init_type != InitType::WRFInput) {
1769  amrex::Error("erf.use_wrf_bdy_qc_qi requires WRFInput real boundary conditions");
1770  }
1773  amrex::Error("erf.use_wrf_bdy_qc_qi requires active qv and qc component indices");
1774  }
1775  const int optional_moisture_comps[] = {moisture_indices.qi,
1779  for (const int comp : optional_moisture_comps) {
1780  if (comp != -1 && (comp < RhoQ1_comp || comp > RhoQ11_comp)) {
1781  amrex::Error("erf.use_wrf_bdy_qc_qi found an invalid active moisture component index");
1782  }
1783  }
1784  }
1785 
1786  // Are me transporting the scalar component?
1787  pp.queryAdd("transport_scalar",transport_scalar);
1788 
1789  for (int lev = 0; lev <= max_level; ++lev) {
1790  if (turbChoice[lev].uses_eamxx_shoc()) {
1791  use_eamxx_shoc = true;
1792  }
1793  if (turbChoice[lev].uses_native_shoc()) {
1794  use_native_shoc = true;
1796  }
1797  }
1798 
1799  // Set up the vertical implicit diffusion solve
1800  int n_impfac = pp.countval("vert_implicit_fac");
1801  AMREX_ALWAYS_ASSERT(n_impfac == 0 || n_impfac == 1 || n_impfac==3);
1802  if (n_impfac == 1) {
1803  amrex::Real fac_in;
1804  pp.get("vert_implicit_fac", fac_in);
1805  for (int lev = 0; lev <= max_level; lev++) {
1806  for (int i=0; i<3; ++i) {
1807  vert_implicit_fac[lev][i] = fac_in;
1808  }
1809  }
1810  } else if (n_impfac == 3) {
1811  for (int lev = 0; lev <= max_level; lev++) {
1812  pp.getarr("vert_implicit_fac", vert_implicit_fac[lev]);
1813  }
1814  }
1815 
1816  // Record whether the user explicitly requested these
1817  const bool user_set_imp_thermal = pp.contains("implicit_thermal_diffusion");
1818  const bool user_set_imp_moisture = pp.contains("implicit_moisture_diffusion");
1819  const bool user_set_imp_ke = pp.contains("implicit_ke_diffusion");
1820  const bool user_set_imp_momentum = pp.contains("implicit_momentum_diffusion");
1821 
1822  // If true (default), include implicit contributions to vertical thermal diffusion
1823  pp.queryAdd("implicit_thermal_diffusion", implicit_thermal_diffusion);
1824 
1825  // If true (default), include implicit contributions to vertical moisture diffusion
1826  pp.queryAdd("implicit_moisture_diffusion", implicit_moisture_diffusion);
1827 
1828  // If true (default), include implicit contributions to vertical KE diffusion
1829  pp.queryAdd("implicit_ke_diffusion", implicit_ke_diffusion);
1830 
1831  // If true (default), include implicit contributions in tau13, tau23,
1832  pp.queryAdd("implicit_momentum_diffusion", implicit_momentum_diffusion);
1833 
1834  // If true (default false), also solve the compact terrain-metric part of the vertical
1835  // diffusion, K_h M d2/dz2 (ERF_TerrainImplicitMetric.H), implicitly (terrain-fitted
1836  // meshes only)
1837  pp.queryAdd("implicit_terrain_metric", implicit_terrain_metric);
1838 
1839  // The default is true (i.e. that it happens before the acoustic substepping).
1840  pp.queryAdd("implicit_before_substep", implicit_before_substep);
1841  for (int lev = 0; lev <= max_level; lev++) {
1843  amrex::Print() << "implicit_before_substep cannot be false without substepping; setting to true." << "\n";
1844  implicit_before_substep = true;
1845  }
1846  }
1847 
1848  // Handle shoc case -- SHOC owns whatever vertical diffusion the host does not
1849  if (uses_shoc_family()) {
1850  const bool host_owns_scalars = host_owns_vertical_scalar_diffusion();
1851  const bool host_owns_momentum = host_owns_vertical_momentum_diffusion();
1852  if (!host_owns_scalars &&
1853  ( (user_set_imp_thermal && implicit_thermal_diffusion ) ||
1854  (user_set_imp_moisture && implicit_moisture_diffusion) ||
1855  (user_set_imp_ke && implicit_ke_diffusion ) ) ) {
1856  amrex::Print() << "Ignoring erf.implicit_{thermal,moisture,ke}_diffusion = true; "
1857  << "SHOC owns vertical scalar diffusion." << "\n";
1858  }
1859  if (!host_owns_momentum && user_set_imp_momentum && implicit_momentum_diffusion) {
1860  amrex::Print() << "Ignoring erf.implicit_momentum_diffusion = true; "
1861  << "SHOC owns vertical momentum diffusion." << "\n";
1862  }
1863 
1864  implicit_thermal_diffusion = (implicit_thermal_diffusion && host_owns_scalars);
1866  implicit_ke_diffusion = (implicit_ke_diffusion && host_owns_scalars);
1867  implicit_momentum_diffusion = (implicit_momentum_diffusion && host_owns_momentum);
1868 
1869  // Make the resulting configuration visible in the run log
1870  if (!host_owns_scalars && !host_owns_momentum) {
1871  amrex::Print() << "Turning off vertical implicit solve for all components since SHOC owns diffusion." << "\n";
1872  } else {
1873  amrex::Print() << "SHOC hands vertical diffusion of"
1874  << (host_owns_scalars ? " scalars" : "")
1875  << (host_owns_momentum ? " momentum" : "")
1876  << " to the host; keeping the vertical implicit solve on (theta = "
1878  << ", moisture = " << implicit_moisture_diffusion
1879  << ", tke = " << implicit_ke_diffusion
1880  << ", momenta = " << implicit_momentum_diffusion << ")." << "\n";
1881  }
1882  } // uses_shoc_family
1883 
1884  // Do not allow implicit vertical diff if user turns it off
1885  bool do_vert_implicit = true;
1886  pp.queryAdd("vert_implicit", do_vert_implicit);
1887  if (!do_vert_implicit) {
1888  for (int lev = 0; lev <= max_level; lev++) {
1889  amrex::Print() << "Turning off native vertical implicit solve from vert_implicit flag at level " << lev << std::endl;
1890  vert_implicit_fac[lev][0] = zero;
1891  vert_implicit_fac[lev][1] = zero;
1892  vert_implicit_fac[lev][2] = zero;
1893  }
1894  }
1895 
1896  // The implicit vertical diffusion is only second order in an anelastic run when
1897  // the two stages are the midpoint method and the solve is done in the first stage
1898  // alone: S^* = S^n + (dt/2) F(S^*) and S^{n+1} = S^n + dt F(S^*) give
1899  // S^* = (S^n + S^{n+1})/2, i.e. the implicit midpoint rule. Solving again in the
1900  // second stage would reduce the pair to backward Euler, and with the RK2 (Heun)
1901  // stages there is no stage that plays the role of the midpoint at all.
1902  for (int lev = 0; lev <= max_level; lev++) {
1903  if (anelastic[lev]) {
1904  if (anelastic_type[lev] == AnelasticType::RK2) {
1905  amrex::Print() << "Turning off native vertical implicit solve flag because anelastic at level " << lev
1906  << " with RK2 scheme." << std::endl;
1907  vert_implicit_fac[lev][0] = zero;
1908  vert_implicit_fac[lev][1] = zero;
1909  vert_implicit_fac[lev][2] = zero;
1910  } else if (anelastic_type[lev] == AnelasticType::MidPoint) {
1911  if (do_vert_implicit && vert_implicit_fac[lev][0] != one) {
1912  amrex::Print() << "Forcing implicit factor 1 in first stage of MidPoint to ensure second-order accuracy." << std::endl;
1913  vert_implicit_fac[lev][0] = one;
1914  }
1915  if (vert_implicit_fac[lev][1] > zero) {
1916  amrex::Print() << "Turning off the second-stage vertical implicit solve at level " << lev
1917  << " because anelastic with MidPoint solves in the first stage only."
1918  << std::endl;
1919  }
1920  // The anelastic integrator only takes two stages, so [2] is never used
1921  vert_implicit_fac[lev][1] = zero;
1922  vert_implicit_fac[lev][2] = zero;
1923  }
1924  }
1925  }
1926 
1927  // Do not allow implicit vertical diff with EB
1928  if (terrain_type == TerrainType::EB) {
1929  for (int lev = 0; lev <= max_level; lev++) {
1930  if (do_vert_implicit) {
1931  amrex::Print() << "Implicit diffusion is not supported with EB; turning off at level " << lev << "\n";
1932  vert_implicit_fac[lev][0] = zero;
1933  vert_implicit_fac[lev][1] = zero;
1934  vert_implicit_fac[lev][2] = zero;
1935  }
1936  }
1937  }
1938 
1939  // The EB diffusion path adds no buoyancy, shear or dissipation source for
1940  // a prognostic TKE closure
1941  if (terrain_type == TerrainType::EB) {
1942  for (int lev = 0; lev <= max_level; lev++) {
1943  if (turbChoice[lev].use_keqn) {
1944  amrex::Error("erf.terrain_type = EB cannot be used with erf.les_type = Deardorff "
1945  "or erf.rans_type = kEqn: the EB diffusion path has no TKE sources");
1946  }
1947  }
1948  }
1949 
1950  // Do not allow implicit vertical diff when all the components are false
1955  amrex::Print() << "Thermal, moisture, KE, and momentum diffusion are all turned off; turning off vertical implicit solve." << std::endl;
1956  for (int lev = 0; lev <= max_level; lev++) {
1957  vert_implicit_fac[lev][0] = zero;
1958  vert_implicit_fac[lev][1] = zero;
1959  vert_implicit_fac[lev][2] = zero;
1960  }
1961  }
1962 
1963  // Which type of multilevel coupling
1964  coupling_type = CouplingType::TwoWay; // Default
1965  pp.query_enum_case_insensitive("coupling_type",coupling_type);
1966 
1967  // Test for hybrid (compressible + anelastic) -- in this case we must use one-way coupling
1968  if (any_anelastic && any_compress) {
1969  coupling_type = CouplingType::OneWay;
1970  }
1971 
1972  // Which type of windfarm model
1973  windfarm_type = WindFarmType::None; // Default
1974  pp.query_enum_case_insensitive("windfarm_type",windfarm_type);
1975 
1977  pp.query_enum_case_insensitive("windfarm_loc_type",windfarm_loc_type);
1978 
1979  pp.queryAdd("windfarm_loc_table", windfarm_loc_table);
1980  pp.queryAdd("windfarm_spec_table", windfarm_spec_table);
1981  pp.queryAdd("windfarm_blade_table", windfarm_blade_table);
1982  pp.queryAdd("windfarm_airfoil_tables", windfarm_airfoil_tables);
1983  pp.queryAdd("windfarm_spec_table_extra", windfarm_spec_table_extra);
1984 
1985  // Sampling distance upstream of the turbine to find the
1986  // incoming free stream velocity as a factor of the diameter of the
1987  // turbine. ie. the sampling distance will be this number multiplied
1988  // by the diameter of the turbine
1989  pp.queryAdd("sampling_distance_by_D", sampling_distance_by_D);
1990  pp.queryAdd("turb_disk_angle_from_x", turb_disk_angle);
1991 
1992  pp.queryAdd("windfarm_x_shift",windfarm_x_shift);
1993  pp.queryAdd("windfarm_y_shift",windfarm_y_shift);
1994  // Test if time averaged data is to be output
1995  pp.queryAdd("time_avg_vel",time_avg_vel);
1996 
1997  // Interval means and resolved turbulent moments for plotfile output
1998  pp.queryAdd("compute_mean_vars", compute_mean_vars);
1999  pp.queryAdd("mean_vars_reset_mode", mean_vars_reset_mode);
2000  pp.queryAdd("mean_vars_reset_time", mean_vars_reset_time);
2001 
2002  if (compute_mean_vars &&
2003  mean_vars_reset_mode != "plotfile" && mean_vars_reset_mode != "time") {
2004  amrex::Error("erf.mean_vars_reset_mode must be either 'plotfile' or 'time'");
2005  }
2006  if (compute_mean_vars && mean_vars_reset_mode == "time" &&
2008  amrex::Error("erf.mean_vars_reset_time must be non-negative when "
2009  "erf.mean_vars_reset_mode = 'time'");
2010  }
2011 
2012  pp.queryAdd("hindcast_lateral_forcing", hindcast_lateral_forcing);
2013 
2015  pp.queryAdd("hindcast_boundary_data_dir", hindcast_boundary_data_dir);
2016 
2017  if(hindcast_boundary_data_dir.empty()) {
2018  amrex::Error("ERROR: Missing input parameter 'erf.hindcast_boundary_data_dir' for boundary data for lateral forcing");
2019  }
2020  pp.queryAdd("hindcast_data_interval_in_hrs", hindcast_data_interval_in_hrs);
2022  amrex::Error("ERROR: Input parameter 'erf.hindcast_data_interval_in_hrs' which is the time interval between the "
2023  "data files is either missing or set to less than zero");
2024  }
2025  pp.queryAdd("hindcast_lateral_sponge_strength", hindcast_lateral_sponge_strength);
2026  pp.queryAdd("hindcast_lateral_sponge_length", hindcast_lateral_sponge_length);
2027 
2028  pp.queryAdd("hindcast_zhi_sponge_length", hindcast_zhi_sponge_length);
2029  pp.queryAdd("hindcast_zhi_sponge_strength", hindcast_zhi_sponge_strength);
2030 
2031  pp.queryAdd("hindcast_zhi_sponge_damping", hindcast_zhi_sponge_damping);
2032 
2034  amrex::Error("ERROR: Missing input parameter 'erf.hindcast_lateral_sponge_strength' or it is specified to be less than zero");
2035  }
2036 
2038  amrex::Error("ERROR: Missing input parameter 'erf.hindcast_lateral_sponge_length' or it is specified to be less than zero");
2039  }
2040 
2042  amrex::Error("ERROR: Missing input parameter 'erf.hindcast_zhi_sponge_length' or it is specified to be less than zero");
2043  }
2044 
2046  amrex::Error("ERROR: Missing input parameter 'erf.hindcast_zhi_sponge_strength' or it is specified to be less than zero");
2047  }
2048  }
2049 
2050  pp.queryAdd("hindcast_surface_bcs", hindcast_surface_bcs);
2051  if(hindcast_surface_bcs) {
2052  pp.queryAdd("hindcast_surface_data_dir", hindcast_surface_data_dir);
2053  }
2054 
2055  pp.queryAdd("io_hurricane_eye_tracker", io_hurricane_eye_tracker);
2057  pp.queryAdd("hurricane_eye_latitude", hurricane_eye_latitude);
2058  pp.queryAdd("hurricane_eye_longitude", hurricane_eye_longitude);
2060  amrex::Error("ERROR: You are using 'erf.io_hurricane_eye_tracker' to write out the files that track the eye of the hurricane"
2061  " but have not provided the initial location of the eye of the hurricane to be tracked. There has to be two"
2062  " options in the inputs - erf.hurricane_eye_latitude and erf.hurricane_eye_longitude that gives an approximate"
2063  " location of the eye in the initial condition");
2064  }
2065  }
2066 
2067  pp.queryAdd("is_init_for_ensemble", is_init_for_ensemble);
2068  if(is_init_for_ensemble) {
2069  amrex::ParmParse pp_ens("ensemble");
2070  pp_ens.queryAdd("n_members", n_ensemble);
2071  if(n_ensemble < 2) {
2072  amrex::Abort("You are using an ensemble run. There needs to be at least 2 ensemble members. "
2073  "erf.n_ensemble must be >=2.");
2074  }
2075  pp_ens.queryAdd("coarse_bckgnd_data_file", coarse_bckgnd_data_file);
2076  if (coarse_bckgnd_data_file.empty()) {
2077  amrex::Abort("coarse_bckgnd_data_file is empty! For ensmeble simulations, there needs to be a coarse background file which "
2078  "contains the data which will be interpolated onto the fine mesh. There has to a entry ensemble.coarse_bckgnd_data_file "
2079  "which contains the filename in the inputs.");
2080  }
2081  pp_ens.queryAdd("ens_pert_amplitude", ens_pert_amplitude);
2082  if(ens_pert_amplitude <= 0.0) {
2083  amrex::Error("You are using initialization for ensemble simulations using the inputs option "
2084  "ensemble.is_init_for_ensemble=true. In this case, there has to be an option "
2085  "ensemble.ens_pert_amplitude which is the value of the amplitude of the perturbation "
2086  "to be added to the background state and has to be greater than 0.0");
2087  }
2088  pp_ens.queryAdd("ens_pert_correlated_radius", ens_pert_correlated_radius);
2089  if(ens_pert_correlated_radius <= 0.0) {
2090  amrex::Error("You are using initialization with spatially correlated perturbations using the inputs option "
2091  "ensemble.is_init_for_ensemble=true. In this case, there has to be an option "
2092  "ensemble.ens_pert_correlated_radius which is the value of the the spatial correlation radius, "
2093  "and has to be greater than 0.0");
2094  }
2095  }
2096  }
2097 
2098  /**
2099  * @brief Validate solver options after geometry and boundary conditions are known.
2100  * @param max_level Maximum AMR level configured for the run.
2101  * @param geom_vect Geometry for each AMR level.
2102  * @param phys_bc_type Physical boundary-condition types.
2103  */
2104  void check_params (int max_level, const amrex::Vector<amrex::Geometry>& geom_vect, amrex::GpuArray<ERF_BC, AMREX_SPACEDIM*2> phys_bc_type)
2105  {
2106  // The weighted average down acts only where covered coarse cells are averaged down
2107  if (if_average_down == "fluid_mass") {
2108  if (max_level > 0 && coupling_type == CouplingType::TwoWay) {
2109  amrex::Print() << "Immersed forcing: erf.if_average_down = fluid_mass (the cell average down "
2110  << "over refined levels is weighted by the fluid mass of the fine cells)\n";
2111  } else {
2112  amrex::Warning("erf.if_average_down = fluid_mass has no effect without refined levels "
2113  "(amr.max_level > 0) and erf.coupling_type = TwoWay");
2114  }
2115  }
2116  if (if_fraction_stress) {
2117  // The face-above spacing factor is bounded only while z0 < dz/4 (then d2 > d1 for
2118  // every solid fraction and the fluid centroid of a full wall cell sits above 2 z0)
2119  for (int lev = 0; lev <= max_level; ++lev) {
2120  const amrex::Real dz = geom_vect[lev].CellSize(2);
2121  if (!(if_z0 < amrex::Real(0.25) * dz)) {
2122  amrex::Abort("erf.if_wall_form = fraction_stress needs erf.if_z0 < dz/4 on every level (level " +
2123  std::to_string(lev) + ": z0 = " + std::to_string(if_z0) + " m, dz = " +
2124  std::to_string(dz) + " m)");
2125  }
2126  }
2127  }
2129  bool any_keqn = false;
2130  for (int lev = 0; lev <= max_level; ++lev) {
2131  any_keqn = any_keqn || (turbChoice[lev].rans_type == RANSType::kEqn);
2132  }
2133  if (!any_keqn) {
2134  amrex::Abort("erf.if_wall_tke and erf.if_flat_wall_dissipation act on the k-eqn RANS closure: "
2135  "set erf.rans_type = kEqn on a level, or turn them off (erf.if_flat_wall_dissipation = false)");
2136  }
2137  }
2138  if (if_fraction_stress) {
2139  amrex::Print() << "Immersed forcing: erf.if_wall_form = fraction_stress (wall stress in the wall cell, "
2140  << "the wall face closed to diffusion; face spacing "
2141  << ((if_wall_face_spacing == 2) ? "log" : (if_wall_face_spacing == 1) ? "physical" : "grid")
2142  << ", wall TKE " << (if_wall_tke ? "on" : "off")
2143  << ", flat-wall dissipation " << (if_flat_wall_dissipation ? "on" : "off") << ")\n";
2144  }
2145  // The log-law coarse/fine fill acts only on refined levels
2146  if (cf_loglaw_fill) {
2147  const bool immersed = (terrain_type == TerrainType::ImmersedForcing ||
2148  buildings_type == BuildingsType::ImmersedForcing);
2149  if (!immersed && phys_bc_type[amrex::Orientation(amrex::Direction::z, amrex::Orientation::low)] != ERF_BC::surface_layer) {
2150  amrex::Abort("erf.cf_loglaw_fill needs an immersed surface (erf.terrain_type or "
2151  "erf.buildings_type = ImmersedForcing) or zlo.type = surface_layer");
2152  }
2153  if (max_level > 0) {
2154  amrex::Print() << "Coarse/fine faces: erf.cf_loglaw_fill = true (the wall coarse face's flux is shared "
2155  << "among its fine sub-faces by the integral of ln(1 + z/z0), z0 = " << cf_loglaw_z0 << " m)\n";
2156  } else {
2157  amrex::Warning("erf.cf_loglaw_fill has no effect without refined levels (amr.max_level > 0)");
2158  }
2159  }
2161  (terrain_type == TerrainType::EB || terrain_type == TerrainType::ImmersedForcing)) {
2162  amrex::Abort("Automatic perturbations currently support flat, stretched, and fitted-terrain meshes; "
2163  "immersed/embedded terrain requires a ground-relative parent PBL-height conversion");
2164  }
2165 #if 0
2166  // Warn for PBL models and moisture - these may not yet be compatible
2167  for (int lev = 0; lev <= max_level; lev++) {
2168  if ((moisture_type != MoistureType::None) && (turbChoice[lev].pbl_type != PBLType::None)) {
2169  amrex::Warning("\n*** WARNING: Moisture may not yet be compatible with PBL models, \n proceed with caution ***");
2170  }
2171  }
2172 #endif
2173  //
2174  // Buoyancy type check
2175  //
2176  for (int lev = 0; lev <= max_level; lev++) {
2177  if (buoyancy_type[lev] != 1 && buoyancy_type[lev] != 2 && buoyancy_type[lev] != 3 && buoyancy_type[lev] != 4) {
2178  amrex::Error("buoyancy_type must be 1, 2, 3 or 4");
2179  }
2180  }
2181 
2182  if (!use_lagged_delta_rt && !(terrain_type == TerrainType::MovingFittedMesh)) {
2183  amrex::Error("Can't turn off lagged_delta_rt when terrain not moving");
2184  }
2185 
2186  //
2187  // Wind farm checks
2188  //
2189  if (windfarm_type==WindFarmType::SimpleAD and sampling_distance_by_D < zero) {
2190  amrex::Error("To use simplified actuator disks, you need to provide a variable"
2191  " erf.sampling_distance_by_D in the inputs which specifies the upstream"
2192  " distance as a factor of the turbine diameter at which the incoming free stream"
2193  " velocity will be computed at.");
2194  }
2195  if ( (windfarm_type==WindFarmType::SimpleAD ||
2196  windfarm_type==WindFarmType::GeneralAD ) && turb_disk_angle < zero) {
2197  amrex::Error("To use simplified actuator disks, you need to provide a variable"
2198  " erf.turb_disk_angle_from_x in the inputs which is the angle of the face of the"
2199  " turbine disk from the x-axis. A turbine facing an oncoming flow in the x-direction"
2200  " will have turb_disk_angle value of 90 deg.");
2201  }
2202  if (windfarm_loc_type == WindFarmLocType::lat_lon and (windfarm_x_shift < zero or windfarm_y_shift < zero)) {
2203  amrex::Error("You are using windfarms with latitude-logitude option to position the turbines."
2204  " For this you should provide the inputs erf.windfarm_x_shift and"
2205  " erf.windfarm_y_shift which are the values by which the bounding box of the"
2206  " windfarm is shifted from the x and the y axes.");
2207  }
2208 
2209 
2210  if ( (const_massflux_u != 0) && !(geom_vect[0].isPeriodic(0)) ) {
2211  amrex::Error("Constant mass flux (in x) should be used with periodic boundaries");
2212  }
2213 
2214  if ( (const_massflux_v != 0) && !(geom_vect[0].isPeriodic(1)) ) {
2215  amrex::Error("Constant mass flux (in y) should be used with periodic boundaries");
2216  }
2217 
2218  // Mesoscale diffusion -- test if LES appropriate with this dx,dy
2219  for (int lev = 0; lev <= max_level; lev++) {
2220  if ((geom_vect[lev].CellSize(0) > amrex::Real(2000.)) || (geom_vect[lev].CellSize(1) > amrex::Real(2000.)))
2221  {
2222  if ( (turbChoice[lev].les_type == LESType::Smagorinsky) && !turbChoice[lev].smag2d ) {
2223  amrex::Warning("Should use 2-D Smagorinsky for mesoscale resolution");
2224  } else if (turbChoice[lev].les_type == LESType::Deardorff) {
2225  amrex::Warning("Should not use Deardorff LES for mesoscale resolution");
2226  }
2227  }
2228  }
2229 
2230  // Turn off implicit solve if we have no diffusion
2231  bool l_use_kturb = turbChoice[0].use_kturb;
2232  for (int lev = 1; lev <= max_level; lev++) {
2233  l_use_kturb = (l_use_kturb || turbChoice[lev].use_kturb);
2234  }
2235  bool l_use_diff = ( (diffChoice.molec_diff_type != MolecDiffType::None) || l_use_kturb );
2236 
2237  for (int lev = 0; lev <= max_level; lev++) {
2238  bool l_implicit_diff = (vert_implicit_fac[lev][0] > zero ||
2239  vert_implicit_fac[lev][1] > zero ||
2240  vert_implicit_fac[lev][2] > zero);
2241  if (l_implicit_diff && !l_use_diff) {
2242  amrex:: Print() << "No molecular or turbulent diffusion, turning off implicit solve at level " << lev << std::endl;
2243  vert_implicit_fac[lev][0] = zero;
2244  vert_implicit_fac[lev][1] = zero;
2245  vert_implicit_fac[lev][2] = zero;
2246  }
2247  }
2248 
2249  // Two-way coupling refluxes the diffusive fluxes of the slow scalars, but not on an EB mesh
2250  if (coupling_type == CouplingType::TwoWay && terrain_type == TerrainType::EB &&
2251  max_level > 0 && l_use_diff) {
2252  amrex::Warning("erf.coupling_type = TwoWay with erf.terrain_type = EB does not reflux the "
2253  "diffusive fluxes: diffusion does not conserve the scalars across "
2254  "coarse/fine boundaries");
2255  }
2256 
2257  // The implicit terrain metric is a change to the vertical implicit solves on a
2258  // terrain-fitted mesh; refuse it where it could not do anything. This comes after
2259  // every block that can turn the implicit solve off.
2261  if (mesh_type != MeshType::VariableDz) {
2262  amrex::Abort("erf.implicit_terrain_metric = true requires a terrain-fitted mesh with "
2263  "non-flat terrain (erf.terrain_type = StaticFittedMesh or MovingFittedMesh); "
2264  "on a flat or a purely stretched mesh there are no terrain-metric terms");
2265  }
2266  // In a stage without the implicit solve the whole averaged metric term is explicit
2267  // for that stage. On the steep ridge test the option gained little with 1 1 0
2268  // and ran worse than without it with anelastic MidPoint (measured before the exact
2269  // split: 15 s instead of 30 s), while 1 1 1 recovered the full time step. The rule
2270  // is measured, not derived: every stage must solve implicitly.
2271  for (int lev = 0; lev <= max_level; lev++) {
2272  for (int i = 0; i < 3; ++i) {
2273  if (vert_implicit_fac[lev][i] <= zero) {
2274  amrex::Abort("erf.implicit_terrain_metric = true needs the vertical implicit solve "
2275  "in every Runge-Kutta stage at every level, but erf.vert_implicit_fac "
2276  "is not positive in stage " + std::to_string(i+1) + " at level " +
2277  std::to_string(lev) + ". Set erf.vert_implicit_fac = 1 1 1; the "
2278  "option cannot be used where the implicit solve is turned off "
2279  "(erf.vert_implicit = false, every erf.implicit_*_diffusion = false, "
2280  "no molecular or turbulent diffusion, or anelastic, which solves in "
2281  "its first stage only or not at all)");
2282  }
2283  }
2284  }
2285  // The momentum solves carry most of the stiffness; with them explicit the option
2286  // treats only the scalar metric terms implicitly
2288  amrex::Print() << "WARNING: erf.implicit_terrain_metric = true with "
2289  << "erf.implicit_momentum_diffusion = false: the momentum terrain-metric "
2290  << "terms stay explicit; only the scalars are treated implicitly" << std::endl;
2291  }
2292  } else if (mesh_type == MeshType::VariableDz) {
2293  // On a terrain-fitted mesh the projected horizontal stresses carry K_h, and their
2294  // terrain-metric part K_h h^2 d2/dz2 is explicit in any stage without the implicit
2295  // solve. On steep slopes that limits the time step (steep test ridge: 45 s before
2296  // #4214 was fixed, 15 s after, at the default 1 1 0). Say so once at start-up.
2297  bool explicit_stage_with_kh = false;
2298  for (int lev = 0; lev <= max_level; lev++) {
2299  if (turbChoice[lev].les_type == LESType::None) { continue; }
2300  for (int i = 0; i < 3; ++i) {
2301  if (vert_implicit_fac[lev][i] <= zero) { explicit_stage_with_kh = true; }
2302  }
2303  }
2304  if (explicit_stage_with_kh) {
2305  amrex::Print() << "NOTE: terrain-fitted mesh with an LES closure (horizontal eddy "
2306  << "viscosity K_h) and an explicit vertical-diffusion stage "
2307  << "(erf.vert_implicit_fac has a zero): the terrain-metric term "
2308  << "K_h h^2 d2/dz2 stays explicit there and limits the time step on "
2309  << "steep slopes. To relax it, use erf.vert_implicit_fac = 1 1 1 with "
2310  << "erf.implicit_terrain_metric = true, or erf.smag2d_slope_limiter = true "
2311  << "for Smagorinsky2D; erf.diffusive_dt_check reports the limit."
2312  << std::endl;
2313  }
2314  }
2315 
2316  // Countergradient validation: ensure required implicit diffusion is enabled
2317  for (int lev = 0; lev <= max_level; lev++) {
2318  if (turbChoice[lev].enable_mrf_countergradient) {
2320  vert_implicit_fac[lev][0] > zero ||
2321  vert_implicit_fac[lev][1] > zero ||
2322  vert_implicit_fac[lev][2] > zero,
2323  "enable_mrf_countergradient requires vert_implicit_fac > 0");
2326  "enable_mrf_countergradient requires implicit_thermal_diffusion = true");
2330  "enable_mrf_countergradient with moisture requires implicit_moisture_diffusion = true");
2331  }
2332  }
2333  }
2334 
2335  for (int lev = 0; lev <= max_level; lev++) {
2336  turbChoice[lev].check_params(phys_bc_type);
2337  }
2338  }
2339 
2340  /**
2341  * @brief Print the configured solver, physics, and transport options.
2342  * @param max_level Maximum AMR level configured for the run.
2343  * @param pp_prefix ParmParse prefix used to display nested choices.
2344  */
2345  void display (int max_level, std::string pp_prefix)
2346  {
2347  amrex::Print() << "SOLVER CHOICE: " << std::endl;
2348  if (perturbation_auto) {
2349  amrex::Print() << "Automatic LES-nest perturbations: parent update period = "
2350  << perturbation_auto_period << " s, PBL height fraction = "
2351  << perturbation_auto_pbl_fraction << std::endl;
2352  }
2353  for (int lev = 0; lev <= max_level; lev++) {
2354  amrex::Print() << "At level " << lev << " : " << std::endl;
2355  if (anelastic[lev]) {
2356  amrex::Print() << " anelastic with no substepping, using the "
2357  << ((anelastic_type[lev] == AnelasticType::MidPoint) ? "MidPoint" : "RK2")
2358  << " integrator" << std::endl;
2359  } else {
2361  amrex::Print() << " compressible with no substepping" << std::endl;
2362  } else if (substepping_type[lev] == SubsteppingType::Implicit) {
2363  amrex::Print() << " compressible with implicit substepping" << std::endl;
2364  }
2365  }
2366  if (fixed_density[lev]) {
2367  amrex::Print() << " and fixed density" << std::endl;
2368  }
2369  }
2370 
2371  for (int lev = 0; lev <= max_level; lev++) {
2372  amrex::Print() << "vert_implicit_fac at level " << lev << " : "
2373  << vert_implicit_fac[lev][0] << " "
2374  << vert_implicit_fac[lev][1] << " "
2375  << vert_implicit_fac[lev][2];
2376  if (vert_implicit_fac[lev][0] > zero ||
2377  vert_implicit_fac[lev][1] > zero ||
2378  vert_implicit_fac[lev][2] > zero)
2379  {
2380  amrex::Print() << " (theta = " << implicit_thermal_diffusion
2381  << ", moisture = " << implicit_moisture_diffusion
2382  << ", tke = " << implicit_ke_diffusion
2383  << ", momenta = " << implicit_momentum_diffusion;
2385  amrex::Print() << "; terrain metric implicit";
2386  }
2387  amrex::Print() << ")";
2388  }
2389  }
2390  amrex::Print() << std::endl;
2391  amrex::Print() << "use_coriolis : " << use_coriolis << std::endl;
2392  amrex::Print() << "use_gravity : " << use_gravity << std::endl;
2393 
2394  if (moisture_type == MoistureType::SAM) {
2395  amrex::Print() << "Moisture Model: SAM" << std::endl;
2396  } else if (moisture_type == MoistureType::SAM_NoIce) {
2397  amrex::Print() << "Moisture Model: SAM No Ice" << std::endl;
2398  } else if (moisture_type == MoistureType::SAM_NoPrecip_NoIce) {
2399  amrex::Print() << "Moisture Model: SAM No Precip No Ice" << std::endl;
2400  } else if (moisture_type == MoistureType::Morrison) {
2401  amrex::Print() << "Moisture Model: Morrison" << std::endl;
2402  } else if (moisture_type == MoistureType::Morrison_NoIce) {
2403  amrex::Print() << "Moisture Model: Morrison_NoIce" << std::endl;
2404  } else if (moisture_type == MoistureType::WSM6) {
2405  amrex::Print() << "Moisture Model: WSM6" << std::endl;
2406  } else if (moisture_type == MoistureType::WDM6) {
2407  amrex::Print() << "Moisture Model: WDM6" << std::endl;
2408  } else if (moisture_type == MoistureType::Kessler) {
2409  amrex::Print() << "Moisture Model: Kessler" << std::endl;
2410  } else if (moisture_type == MoistureType::Kessler_NoRain) {
2411  amrex::Print() << "Moisture Model: Kessler No Rain" << std::endl;
2412  } else if (moisture_type == MoistureType::SatAdj) {
2413  amrex::Print() << "Moisture Model: Saturation Adjustment" << std::endl;
2414  } else if (moisture_type == MoistureType::SuperDroplets) {
2415  amrex::Print() << "Moisture Model: SuperDroplets" << std::endl;
2416  } else if (moisture_type == MoistureType::MoistNoCondensation) {
2417  amrex::Print() << "Moisture Model: MoistNoCondensation" << std::endl;
2418  } else if (moisture_type == MoistureType::SBM) {
2419  amrex::Print() << "Moisture Model: SBM mapped spectral advection (M3)" << std::endl;
2420  } else {
2421  amrex::Print() << "Moisture Model: None" << std::endl;
2422  }
2423 
2424  if (terrain_type == TerrainType::StaticFittedMesh) {
2425  amrex::Print() << "Terrain Type: StaticFittedMesh" << std::endl;
2426  } else if (terrain_type == TerrainType::MovingFittedMesh) {
2427  amrex::Print() << "Terrain Type: MovingFittedMesh" << std::endl;
2428  } else if (terrain_type == TerrainType::EB) {
2429  amrex::Print() << "Terrain Type: EB" << std::endl;
2430  ebChoice.display();
2431  } else if (terrain_type == TerrainType::ImmersedForcing) {
2432  amrex::Print() << "Terrain Type: ImmersedForcing" << std::endl;
2433  } else {
2434  amrex::Print() << "Terrain Type: None" << std::endl;
2435  }
2436 
2437  amrex::Print() << "Flat fitted terrain: "
2438  << (flat_terrain ? "true" : "false") << std::endl;
2439 
2440  if (buildings_type == BuildingsType::ImmersedForcing) {
2441  amrex::Print() << "Buildings Type: ImmersedForcing" << std::endl;
2442  } else {
2443  amrex::Print() << "Buildings Type: None" << std::endl;
2444  }
2445 
2446  if (mesh_type == MeshType::ConstantDz) {
2447  amrex::Print() << " Mesh Type: ConstantDz" << std::endl;
2448  } else if (mesh_type == MeshType::StretchedDz) {
2449  amrex::Print() << " Mesh Type: StretchedDz" << std::endl;
2450  } else if (mesh_type == MeshType::VariableDz) {
2451  amrex::Print() << " Mesh Type: VariableDz" << std::endl;
2452  } else {
2453  amrex::Error("No mesh_type set!");
2454  }
2455 
2456  amrex::Print() << "ABL Driver Type: " << std::endl;
2458  amrex::Print() << " None" << std::endl;
2459  } else if (abl_driver_type == ABLDriverType::PressureGradient) {
2460  amrex::Print() << " Pressure Gradient "
2461  << amrex::RealVect(abl_pressure_grad[0],abl_pressure_grad[1],abl_pressure_grad[2])
2462  << std::endl;
2463  } else if (abl_driver_type == ABLDriverType::GeostrophicWind) {
2464  amrex::Print() << " Geostrophic Wind "
2465  << amrex::RealVect(abl_geo_forcing[0],abl_geo_forcing[1],abl_geo_forcing[2])
2466  << std::endl;
2467  }
2468 
2469  if (max_level > 0) {
2470  amrex::Print() << "Coupling Type: " << std::endl;
2471  if (coupling_type == CouplingType::TwoWay) {
2472  amrex::Print() << " Two-way" << std::endl;
2473  } else if (coupling_type == CouplingType::OneWay) {
2474  amrex::Print() << " One-way" << std::endl;
2475  }
2476  }
2477 
2478  if (rad_type == RadiationType::RRTMGP) {
2479  amrex::Print() << "Radiation Model: RRTMGP" << std::endl;
2480  } else if (rad_type == RadiationType::Simple) {
2481  amrex::Print() << "Radiation Model: Simple" << std::endl;
2482  } else if (rad_type == RadiationType::TwoStream) {
2483  amrex::Print() << "Radiation Model: TwoStream" << std::endl;
2484  } else {
2485  amrex::Print() << "Radiation Model: None" << std::endl;
2486  }
2487 
2488  amrex::Print() << "Gradp_type : " << gradp_type << std::endl;
2489 
2490  for (int lev = 0; lev <= max_level; lev++) {
2491  amrex::Print() << "Buoyancy_type at level " << lev << " : " << buoyancy_type[lev] << std::endl;
2492  }
2493 
2494  advChoice.display(pp_prefix);
2495  diffChoice.display();
2498 
2499  for (int lev = 0; lev <= max_level; lev++) {
2500  turbChoice[lev].display(lev);
2501  }
2502  }
2503 
2504  /**
2505  * @brief Build constant-latitude Coriolis forcing parameters.
2506  * @param pp_prefix ParmParse prefix for the ERF input namespace.
2507  */
2508  void build_coriolis_forcings_const_lat (std::string pp_prefix)
2509  {
2510  amrex::ParmParse pp(pp_prefix);
2511 
2512  // Read the rotational time period (in seconds)
2513  double rot_time_period = 86400.0;
2514  pp.queryAdd("rotational_time_period", rot_time_period);
2515 
2516  coriolis_factor = static_cast<amrex::Real>(4.0 * PI / rot_time_period);
2517 
2518  pp.queryAdd("coriolis_3d", coriolis_3d);
2519 
2520  // Convert to radians
2521  amrex::Real latitude_for_coriolis = amrex::Real(90.0);
2522  pp.queryAdd("latitude", latitude_for_coriolis);
2523  latitude_for_coriolis *= (PI/amrex::Real(180.));
2524  sinphi = std::sin(latitude_for_coriolis);
2525 
2526  // Note: cosphi defaults to 0
2527  if (coriolis_3d) {
2528  cosphi = std::cos(latitude_for_coriolis);
2529  }
2530 
2531  amrex::Print() << "Coriolis frequency, f = " << coriolis_factor * sinphi << " 1/s" << std::endl;
2532 
2533  if (abl_driver_type == ABLDriverType::GeostrophicWind) {
2534  // Read in the geostrophic wind -- we only use this to construct
2535  // the forcing term so no need to keep it
2536  amrex::Vector<amrex::Real> abl_geo_wind(3);
2537  pp.queryarr("abl_geo_wind",abl_geo_wind);
2538 
2539  if(!pp.queryAdd("abl_geo_wind_table",abl_geo_wind_table)) {
2540  abl_geo_forcing = {
2541  -coriolis_factor * (abl_geo_wind[1]*sinphi - abl_geo_wind[2]*cosphi),
2542  coriolis_factor * abl_geo_wind[0]*sinphi,
2543  -coriolis_factor * abl_geo_wind[0]*cosphi
2544  };
2545  } else {
2546  amrex::Print() << "NOTE: abl_geo_wind_table provided, ignoring input abl_geo_wind" << std::endl;
2547  }
2548  }
2549  }
2550 
2551  /**
2552  * @brief Read one integer value or one value per AMR level.
2553  * @param max_level Maximum AMR level configured for the run.
2554  * @param string_to_read ParmParse key to read.
2555  * @param vec_to_fill Destination vector with one entry per level.
2556  * @param default_int Default value used when the key is absent.
2557  * @return True if the key was present in the inputs.
2558  */
2559  bool read_int_string (int max_level, const char* string_to_read,
2560  amrex::Vector<int>& vec_to_fill, int default_int)
2561  {
2562  amrex::ParmParse pp("erf");
2563  int nvals = pp.countval(string_to_read);
2564  AMREX_ALWAYS_ASSERT(nvals == 0 || nvals == 1 || nvals >= max_level+1);
2565  amrex::Vector<int> temp; temp.resize(nvals);
2566  pp.queryarr(string_to_read,temp);
2567 
2568  if (vec_to_fill.size() < max_level+1) {
2569  vec_to_fill.resize(max_level+1);
2570  }
2571 
2572  if (nvals == 0) {
2573  for (int i = 0; i <= max_level; ++i) vec_to_fill[i] = default_int;
2574  } else if (nvals == 1) {
2575  for (int i = 0; i <= max_level; ++i) vec_to_fill[i] = temp[0];
2576  } else {
2577  for (int i = 0; i <= max_level; ++i) vec_to_fill[i] = temp[i];
2578  }
2579  return (nvals > 0);
2580  }
2581 
2582  inline static
2584 
2585  inline static
2586  SoundingType sounding_type = SoundingType::Ideal;
2587 
2588  inline static
2590 
2591  inline static
2592  bool flat_terrain = false;
2593 
2594  inline static
2596 
2597  inline static
2599 
2600  inline static
2601  bool use_real_bcs = false;
2602 
2603  inline static
2604  MeshType mesh_type = MeshType::ConstantDz;
2605 
2606  /**
2607  * @brief Override the globally selected mesh type.
2608  * @param new_mesh_type Mesh type to store.
2609  */
2610  static
2611  void set_mesh_type (MeshType new_mesh_type)
2612  {
2613  mesh_type = new_mesh_type;
2614  }
2615 
2620  amrex::Vector<TurbChoice> turbChoice;
2625 
2627 
2628  amrex::Vector<SubsteppingType> substepping_type;
2629  amrex::Vector<int> anelastic;
2630  amrex::Vector<AnelasticType> anelastic_type;
2631  amrex::Vector<int> fixed_density;
2632  amrex::Vector<int> project_initial_velocity;
2633  amrex::Vector<int> buoyancy_type;
2634  amrex::Vector<int> is_land;
2635 
2636  // do some extra CFL diagnostics for compressible with substepping
2637  bool substepping_diag = false;
2638 
2639  // time off-centering coefficient, > 0 for forward weighting (i.e., bias
2640  // towards the future time step)
2642 
2643  // if true, beta_s varies on the w-faces as max(beta_s_min, min(beta_s_max, h_xi^2 + h_eta^2))
2644  // and beta_s itself is not used (implicit substepping only)
2645  bool use_variable_beta_s = false;
2648 
2649  // divergence damping coefficient: how far (rho theta) is projected forward
2650  // in the horizontal momentum equations of the acoustic substepping
2652 
2653  // This controls the time-centering of the *vertical* differences in the diffusive term for
2654  // theta, u and v
2655  // 0: fully explicit
2656  // 1: fully implicit
2657  amrex::Vector<amrex::Vector<amrex::Real>> vert_implicit_fac; // one value per RK stage
2658 
2659  // if any vert_implicit_fac > 0, then the following apply:
2665  // On a terrain-fitted mesh, also treat the compact terrain-metric part of the vertical
2666  // diffusion (K_h M d2/dz2, ERF_TerrainImplicitMetric.H) implicitly, in every implicit solve
2668 
2669  int ncorr = 1;
2670  std::string wall_dist_type = "poisson";
2671  TerrainPoissonSolver terrain_poisson_solver = TerrainPoissonSolver::gmres_fft;
2672 #ifdef AMREX_USE_FLOAT
2675 #else
2678 #endif
2679 
2680  bool test_mapfactor = false;
2682 
2683  int gradp_type = 0;
2684 
2686 
2687  // Specify what additional physics/forcing modules we use
2688  bool use_gravity = false;
2689  bool use_coriolis = false;
2690  bool coriolis_3d = true;
2691 
2693 
2694  // Specify whether to apply other various source terms on substep only
2696  bool forest_substep = false;
2697 
2698  // immersed forcing parameters
2701  // Use a point-implicit (linearly-implicit) formulation of the immersed-forcing
2702  // drag. Defaults to false (explicit forward-Euler source).
2703  bool if_implicit_drag = false;
2704  bool if_implicit_projection = false; // erf.if_implicit_projection: solid drag (terrain or buildings) inside the anelastic projection
2705  // immersed forcing MOST parameters.
2711  bool if_use_most = false;
2714  bool if_fraction_stress = false; // erf.if_wall_form = fraction_stress
2715  int if_wall_face_spacing = 2; // erf.if_wall_face_spacing: 0 grid, 1 physical, 2 log (default)
2716  bool if_wall_tke = false; // erf.if_wall_tke: wall value of the k-eqn TKE in the wall cell
2717  amrex::Real if_wall_tke_time_factor = amrex::Real(1.0); // erf.if_wall_tke_time_factor: 1 pins, > 1 relaxes over that many steps
2718  bool if_flat_wall_dissipation = false; // erf.if_flat_wall_dissipation (default: = if_fraction_stress)
2719  amrex::Real if_ws_floor = 0.001; // [m/s]
2723  amrex::Real if_small_volfrac = amrex::Real(0.005); // [-] eb2.small_volfrac
2724  std::string if_average_down = "plain"; // or fluid_mass
2725  amrex::Real if_cf_mismatch_warning = amrex::Real(0.75); // erf.if_cf_mismatch_warning: c/f surface mismatch threshold
2726  bool cf_loglaw_fill = false; // erf.cf_loglaw_fill: log-law share of the wall coarse face's flux
2727  amrex::Real cf_loglaw_z0 = amrex::Real(-1.0); // erf.cf_loglaw_z0 [m]; < 0: resolved from if_z0 / most.z0
2728 
2729  // This defaults to true but can be set to false for moving terrain cases only
2730  bool use_lagged_delta_rt = true;
2731 
2732  // Flag to rebalance states from wrfinput and wrfbdy
2733  bool rebalance_wrf_input = true;
2734 
2735  // Constants
2737  amrex::Real c_p = Cp_d; // specific heat at constant pressure for dry air [J/(kg-K)]
2739 
2740  // Staggered z levels for vertical grid stretching
2743 
2744  // Coriolis forcing
2748 
2749  // User-specified forcings in problem definition
2752  bool custom_w_subsidence = false;
2753  bool do_theta_advection = true; // Apply custom vertical subsidence to (rho*theta) equation when using custom w subsidence
2754  bool do_mom_advection = true; // Apply custom vertical subsidence to momentum equations when using custom w subsidence
2759 
2760  // Do we use source terms to nudge the solution towards
2761  // the time-varying data provided in input sounding files?
2763 
2764  // Do we nudge the solution towards observations at stations (met masts,
2765  // lidars)? See ERF_ObsNudging.H.
2767 
2773  amrex::Real nudging_q_z2 = amrex::Real(10000.0); // TODO: reasonable defaults here?
2774 
2775  bool nudging_u = true; // Nudge u,v when nudging_from_input_sounding is true
2776  bool nudging_t = true; // Nudge potential temperature when nudging_from_input_sounding is true
2777  bool nudging_q = true; // Nudge water vapor when nudging_from_input_sounding is true
2778 
2779  bool large_scale_forcing = false;
2780  std::string lsf_file = "";
2782 
2783  // MOST stress rotations
2785 
2786  // Should we use SHOC?
2787  bool use_eamxx_shoc = false;
2788  bool use_native_shoc = false;
2789 
2790  // Native shoc transport mode
2793 
2794  // Transport the passive scalar?
2795  bool transport_scalar = true;
2796 
2797  // User wishes to output time averaged velocity fields
2798  bool time_avg_vel = false;
2799 
2800  // User wishes to output interval means and resolved turbulent moments
2801  bool compute_mean_vars = false;
2802 
2803  // Reset interval means after each plotfile or once at a requested time
2804  std::string mean_vars_reset_mode = "plotfile";
2806 
2807  // Building forcing and its landmask use the same horizontal resolution cutoff.
2808  [[nodiscard]] bool use_building_immersed_forcing (const amrex::Geometry& geom) const noexcept
2809  {
2810  return buildings_type == BuildingsType::ImmersedForcing &&
2811  std::sqrt(geom.CellSize(0) * geom.CellSize(1)) <=
2812  amrex::Real(50.0) * (one + amrex::Real(1.e-6));
2813  }
2814 
2815  /**
2816  * @brief Query whether any turbulent perturbation mode is enabled on a level.
2817  * @param lev AMR level index.
2818  * @return True if source, direct, CPM, or CPM_W perturbations are enabled.
2819  */
2820  bool use_perturbation (int lev) const
2821  {
2822  return pert_type[lev] == PerturbationType::Source ||
2823  pert_type[lev] == PerturbationType::Direct ||
2824  pert_type[lev] == PerturbationType::CPM ||
2825  pert_type[lev] == PerturbationType::CPM_W;
2826  }
2827 
2828  /**
2829  * @brief Query whether direct turbulent perturbations are enabled on a level.
2830  * @param lev AMR level index.
2831  * @return True if direct, CPM, or CPM_W perturbations are enabled.
2832  */
2833  bool use_direct_perturbation (int lev) const
2834  {
2835  return pert_type[lev] == PerturbationType::Direct ||
2836  pert_type[lev] == PerturbationType::CPM ||
2837  pert_type[lev] == PerturbationType::CPM_W;
2838  }
2839 
2840  /**
2841  * @brief Query whether source-term turbulent perturbations are enabled on a level.
2842  * @param lev AMR level index.
2843  * @return True if source perturbations are enabled.
2844  */
2845  bool use_source_perturbation (int lev) const
2846  {
2847  return pert_type[lev] == PerturbationType::Source;
2848  }
2849 
2850  /**
2851  * @brief Query whether vertical-velocity CPM perturbations are enabled on a level.
2852  * @param lev AMR level index.
2853  * @return True if CPM_W perturbations are enabled.
2854  */
2855  bool use_wvel_perturbation (int lev) const
2856  {
2857  return pert_type[lev] == PerturbationType::CPM_W;
2858  }
2859 
2860  /**
2861  * @brief Query whether any SHOC-family PBL scheme is active.
2862  * @return True if native or EAMxx SHOC is active.
2863  */
2864  bool uses_shoc_family () const noexcept
2865  {
2866  return (use_eamxx_shoc || use_native_shoc);
2867  }
2868 
2869  /**
2870  * @brief Test whether the host owns momentum diffusion.
2871  * @return True if ERF dycore does vertical diffusion of momentum.
2872  */
2874  {
2875  if (use_eamxx_shoc) { return false; }
2876  if (!use_native_shoc) { return true; }
2878  }
2879 
2880  /**
2881  * @brief Test whether the host owns scalar diffusion.
2882  * @return True if ERF dycore does vertical diffusion of scalars.
2883  */
2885  {
2886  if (use_eamxx_shoc) { return false; }
2887  if (!use_native_shoc) { return true; }
2888  return false;
2889  }
2890 
2891  /**
2892  * @brief Query whether the active PBL scheme suppresses microphysics condensation.
2893  * @return True when the active PBL scheme owns condensation handling.
2894  */
2896  {
2897  return uses_shoc_family();
2898  }
2899 
2900  /**
2901  * @brief Query whether any level uses a turbulent perturbation mode.
2902  * @return True if at least one configured level has source, direct, CPM, or CPM_W perturbations.
2903  */
2904  bool any_perturbation () const
2905  {
2906  for (const auto& type : pert_type) {
2907  if (type == PerturbationType::Source ||
2908  type == PerturbationType::Direct ||
2909  type == PerturbationType::CPM ||
2910  type == PerturbationType::CPM_W) {
2911  return true;
2912  }
2913  }
2914  return false;
2915  }
2916 
2917  // Prefer a PBL scheme's height even when the immediate parent is LES.
2918  // An all-LES hierarchy instead uses the nearest LES surface-layer diagnostic.
2919  int perturbation_pblh_level (int lev) const
2920  {
2921  for (int source = lev-1; source >= 0; --source) {
2922  if (turbChoice[source].pbl_type != PBLType::None) { return source; }
2923  }
2924  for (int source = lev-1; source >= 0; --source) {
2925  const auto& tc = turbChoice[source];
2926  if (tc.les_type != LESType::None && !tc.smag2d) { return source; }
2927  }
2928  return -1;
2929  }
2930 
2931  // Type of perturbation
2932  amrex::Vector<PerturbationType> pert_type;
2933  bool perturbation_auto{false};
2934  amrex::Real perturbation_auto_period{amrex::Real(600.0)}; // seconds between parent-based updates
2936 
2937  // Numerical diffusion
2938  bool use_num_diff{false};
2940 
2941  CouplingType coupling_type;
2942  MoistureType moisture_type;
2943  int sbm_nbins{4};
2948  amrex::Vector<amrex::Real> sbm_edges;
2949  amrex::Vector<amrex::Real> sbm_pivots;
2950  amrex::Vector<amrex::Real> sbm_fixture_initial_state;
2951  WindFarmType windfarm_type;
2952  WindFarmLocType windfarm_loc_type;
2953  LandSurfaceType lsm_type;
2954  UrbanType urban_type;
2955  amrex::Vector<int> urban_enabled_lev;
2956  RadiationType rad_type;
2957 
2958  /** @brief True for the models that run through the IRadiation interface
2959  * (RRTMGP, Simple); the two-stream model has its own class. */
2960  [[nodiscard]] bool rad_uses_interface () const {
2961  return (rad_type == RadiationType::RRTMGP) || (rad_type == RadiationType::Simple);
2962  }
2963 
2964  /** @brief True when the radiation model's outputs feed the land model
2965  * (downwelling shortwave, downwelling longwave, solar zenith angle). RRTMGP
2966  * writes them for any land model through lsm_output_ptrs; the two-stream
2967  * model writes the broadband three for Noah-MP only
2968  * (TwoStreamRadiation::write_land_forcing), since SLM wants the visible /
2969  * near-infrared direct / diffuse split it does not compute. Both register
2970  * the outputs in ERF::init_stuff and write them in ERF::advance_radiation;
2971  * every site that encodes that rule must use this predicate so wiring
2972  * another model in (e.g. Simple, which already runs through the interface)
2973  * is one edit. */
2974  [[nodiscard]] bool rad_feeds_lsm () const {
2975  return (rad_type == RadiationType::RRTMGP) ||
2976  (rad_type == RadiationType::TwoStream && lsm_type == LandSurfaceType::NOAHMP);
2977  }
2978 
2979  // Does an external ocean coupler supply SST? See the query in init_params.
2980  bool use_coupled_sst = false;
2981 
2982  ABLDriverType abl_driver_type;
2983  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> abl_pressure_grad;
2984  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> abl_geo_forcing;
2985  std::string abl_geo_wind_table;
2987 
2988  bool variable_coriolis{false};
2989 
2990  int ave_plane {2};
2991 
2992  // Microphysics params
2994 
2996 
3003 
3004  // Nudging factor for bdy sponging : F1 = 1/(nudge_factor * dt)
3006 
3007  // Use density reconstructed from standard WRF lateral boundary data.
3008  bool use_wrf_bdy_density = true;
3009 
3010  // Ingest WRF cloud water and active cloud ice at initialization and lateral boundaries.
3011  bool use_wrf_bdy_qc_qi = false;
3012 
3013  // Density-specific Davies factor; non-positive means use bdy_nudge_factor.
3015 
3016  // Which approach to nudging the moist variables in the boundary region?
3018 
3019  // Average the input z-face heights onto the nodes rather than reconstructing them?
3021 
3022  // When the ERF mesh is reconstructed rather than taken from the file
3023  // (avg_grid_faces_to_nodes = false), build zlevels_stag from the domain-mean
3024  // layer-thickness profile of the wrfinput file. Set false to recover the older
3025  // construction: a single geometric stretch whose first layer is the thickest first
3026  // layer in the file and whose ratio is solved for so the column reaches z_top.
3028 
3029  // Interpolate atmospheric state from coarse when creating finer level at later time?
3031 
3032  // Use forest canopy model?
3033  bool do_forest_drag {false};
3034 
3035  // Fixed-leaf-temperature canopy heat exchange (disabled by default)
3036  bool forest_biophysics {false};
3039  // Canopy terms of the k-eqn RANS model (ERF_CanopyTurbStruct.H)
3041 
3042  // Enforce constant mass flux?
3048  int massflux_klo {0}; // these are updated in ERF.cpp
3049  int massflux_khi {0};
3050 
3054  bool hindcast_surface_bcs = false;
3058 
3061 
3062  bool is_init_for_ensemble = false;
3063  int n_ensemble = -1;
3067 };
3068 
3069 /**
3070  * @var SolverChoice::init_type
3071  * @brief Initial-condition source selected for the run.
3072  * @var SolverChoice::sounding_type
3073  * @brief Interpretation used for input sounding profiles.
3074  * @var SolverChoice::terrain_type
3075  * @brief Terrain or immersed-boundary representation.
3076  * @var SolverChoice::flat_terrain
3077  * @brief Permit fixed-index planar averages after validating a flat fitted mesh.
3078  * @var SolverChoice::buildings_type
3079  * @brief Building representation.
3080  * @var SolverChoice::buildings_fc_factories
3081  * @brief Use native face-centered EB factories for immersed buildings on a static fitted mesh.
3082  * @var SolverChoice::use_real_bcs
3083  * @brief Whether real-data lateral boundary conditions are used.
3084  * @var SolverChoice::mesh_type
3085  * @brief Vertical mesh representation.
3086  * @var SolverChoice::advChoice
3087  * @brief Advection-related options.
3088  * @var SolverChoice::diffChoice
3089  * @brief Diffusion-related options.
3090  * @var SolverChoice::dampingChoice
3091  * @brief Damping-related options.
3092  * @var SolverChoice::spongeChoice
3093  * @brief Sponge-layer options.
3094  * @var SolverChoice::turbChoice
3095  * @brief Turbulence options for each AMR level.
3096  * @var SolverChoice::ebChoice
3097  * @brief Embedded-boundary options.
3098  * @var SolverChoice::force_stage1_single_substep
3099  * @brief Flag forcing a single substep in the first RK stage.
3100  * @var SolverChoice::substepping_type
3101  * @brief Acoustic substepping type for each AMR level.
3102  * @var SolverChoice::anelastic
3103  * @brief Per-level flag selecting anelastic dynamics.
3104  * @var SolverChoice::anelastic_type
3105  * @brief Per-level two-stage scheme used by the anelastic integrator.
3106  * @var SolverChoice::fixed_density
3107  * @brief Per-level flag selecting fixed-density treatment; always 1 at an anelastic level.
3108  * @var SolverChoice::project_initial_velocity
3109  * @brief Per-level flag for projecting the initial velocity.
3110  * @var SolverChoice::buoyancy_type
3111  * @brief Per-level buoyancy formulation selector.
3112  * @var SolverChoice::is_land
3113  * @brief Per-level land/water selector.
3114  * @var SolverChoice::substepping_diag
3115  * @brief Whether to emit extra CFL diagnostics for compressible substepping.
3116  * @var SolverChoice::beta_s
3117  * @brief Time off-centering coefficient for forward weighting.
3118  * @var SolverChoice::use_variable_beta_s
3119  * @brief Whether beta_s varies on the w-faces with the terrain slope.
3120  * @var SolverChoice::beta_s_min
3121  * @brief Lower bound of the spatially varying beta_s.
3122  * @var SolverChoice::beta_s_max
3123  * @brief Upper bound of the spatially varying beta_s.
3124  * @var SolverChoice::beta_d
3125  * @brief Divergence damping coefficient used in the acoustic substepping.
3126  * @var SolverChoice::vert_implicit_fac
3127  * @brief Per-level, per-stage implicit vertical diffusion factors.
3128  * @var SolverChoice::implicit_thermal_diffusion
3129  * @brief Whether implicit vertical thermal diffusion is included.
3130  * @var SolverChoice::implicit_moisture_diffusion
3131  * @brief Whether implicit vertical moisture diffusion is included.
3132  * @var SolverChoice::implicit_ke_diffusion
3133  * @brief Whether implicit vertical TKE diffusion is included.
3134  * @var SolverChoice::implicit_momentum_diffusion
3135  * @brief Whether implicit vertical momentum diffusion is included.
3136  * @var SolverChoice::implicit_before_substep
3137  * @brief Whether implicit diffusion is applied before acoustic substepping.
3138  * @var SolverChoice::implicit_terrain_metric
3139  * @brief Whether the compact terrain-metric part of the vertical diffusion is solved implicitly.
3140  * @var SolverChoice::ncorr
3141  * @brief Number of projection correction iterations.
3142  * @var SolverChoice::terrain_poisson_solver
3143  * @brief Projection solver on a terrain-fitted mesh: gmres_fft (default), mlmg or gmres_mlmg.
3144  * @var SolverChoice::poisson_abstol
3145  * @brief Absolute tolerance for the Poisson solve.
3146  * @var SolverChoice::poisson_reltol
3147  * @brief Relative tolerance for the Poisson solve.
3148  * @var SolverChoice::test_mapfactor
3149  * @brief Whether to use test map scale factors.
3150  * @var SolverChoice::test_mapfactor_amp
3151  * @brief Amplitude of the spatially varying test map scale factors on level 0 (0: not used).
3152  * @var SolverChoice::gradp_type
3153  * @brief Terrain-fitted horizontal pressure-gradient formulation.
3154  * @var SolverChoice::use_pert_pres_gradient
3155  * @brief Whether momentum equations use perturbational pressure gradients.
3156  * @var SolverChoice::use_gravity
3157  * @brief Whether gravitational forcing is enabled.
3158  * @var SolverChoice::use_coriolis
3159  * @brief Whether Coriolis forcing is enabled.
3160  * @var SolverChoice::coriolis_3d
3161  * @brief Whether the three-dimensional Coriolis terms are included.
3162  * @var SolverChoice::four_stream_radiation
3163  * @brief Whether the four-stream radiation approximation is enabled.
3164  * @var SolverChoice::immersed_forcing_substep
3165  * @brief Whether immersed-forcing source terms are applied only during substeps.
3166  * @var SolverChoice::forest_substep
3167  * @brief Whether canopy source terms are applied only during substeps.
3168  * @var SolverChoice::if_Cd_momentum
3169  * @brief Immersed-forcing drag coefficient for momentum.
3170  * @var SolverChoice::if_Cd_scalar
3171  * @brief Immersed-forcing drag coefficient for scalars.
3172  * @var SolverChoice::if_z0
3173  * @brief Immersed-forcing roughness length [m].
3174  * @var SolverChoice::if_surf_temp_flux
3175  * @brief Immersed-forcing surface temperature flux [K m/s].
3176  * @var SolverChoice::if_init_surf_temp
3177  * @brief Initial immersed-forcing surface temperature [K].
3178  * @var SolverChoice::if_fraction_stress
3179  * @brief erf.if_wall_form = fraction_stress: the wall stress of ERF_ImmersedWallCell.H in the wall
3180  * cell of each column, with the wall face closed to diffusion; false is the legacy MOST
3181  * relaxation at fixed 0.5/1.5 dz (the default, unchanged).
3182  * @var SolverChoice::if_wall_face_spacing
3183  * @brief Spacing the face above the fraction-stress wall cell differences over: 0 the cell
3184  * height, 1 the centroid spacing, 2 (default) the log spacing (ib_wall::face_above_factor).
3185  * @var SolverChoice::if_wall_tke
3186  * @brief Hold the k-eqn TKE of the fraction-stress wall cell at its flat-ground wall value
3187  * (ib_wall::wall_tke), as erf.dirichlet_k does over flat ground.
3188  * @var SolverChoice::if_wall_tke_time_factor
3189  * @brief 1 (the default) pins the wall cell after every stage and in the implicit TKE solve;
3190  * a larger value relaxes toward the wall value as a source over that many steps.
3191  * @var SolverChoice::if_flat_wall_dissipation
3192  * @brief Partial fraction-stress wall cells dissipate TKE with the length scale of flat ground's
3193  * first cell; on by default with the fraction-stress law.
3194  * @var SolverChoice::if_surf_heating_rate
3195  * @brief Immersed-forcing surface heating rate [K/hr].
3196  * @var SolverChoice::if_Olen_in
3197  * @brief Input Obukhov length for immersed-forcing MOST [m].
3198  * @var SolverChoice::if_use_most
3199  * @brief Whether immersed-forcing MOST is enabled.
3200  * @var SolverChoice::if_average_down
3201  * @brief Cell average down over immersed-forcing levels: "plain" (the default, the plain cell
3202  * average) or "fluid_mass" (the fine cells weighted by their fluid mass, coarse cells solid
3203  * on their own level kept; see if_average_down in ERF_Utils.H).
3204  * @var SolverChoice::if_cf_mismatch_warning
3205  * @brief Spread of the fine solid fractions under a coarse cell along a lateral coarse/fine face
3206  * at or above which the start-up warning counts the cell (the levels describe the surface
3207  * differently there); 0 turns the check off.
3208  * @var SolverChoice::cf_loglaw_fill
3209  * @brief Share the flux of the first coarse face above a wall among its fine sub-faces at the
3210  * lateral coarse/fine faces by ln(1 + z/z0) at their heights above the wall, instead of the
3211  * linear interpolation; the immersed surface or a surface-layer bottom, constant dz.
3212  * @var SolverChoice::cf_loglaw_z0
3213  * @brief Roughness length [m] of that share; by default erf.if_z0 with immersed forcing, else
3214  * the inputs' erf.most.z0 or erf.zlo.most.z0 (0.1 m when neither is given).
3215  * @var SolverChoice::if_small_volfrac
3216  * @brief Immersed-forcing solid fraction below which a cell or face counts as fluid; read from
3217  * eb2.small_volfrac (default 0.005), the same value the EB build uses.
3218  * @var SolverChoice::if_snap_partial_cells
3219  * When true, the immersed forcing reads the blanking snapped to solid (1) or
3220  * fluid (0) at half, so a building from a height map becomes the same
3221  * staircase of whole cells an exact box is: the wall law (and the thermal
3222  * conditions) sit on the boundary solid cells, roofs included, the drag on
3223  * the interior cells, and nothing on the sliver cells of the reader's
3224  * one-cell ramp (1-20 percent solid at the corners), on which the raw
3225  * fractions make the wall law and the drag disagree and grow a vertical
3226  * checkerboard over hours. The staircase puts full-strength forcing on every
3227  * wall face, so the switch also selects the point-implicit drag form
3228  * (if_implicit_drag), without which the explicit rate at rims and corners
3229  * drives the density negative within seconds. The momentum kernels take the
3230  * blanking of a face from the two cells it joins (solid if either is), so
3231  * the staircase is the same with and without the face-centred blanking of
3232  * the build; a face between a solid and a fluid cell is wall-normal and gets
3233  * the interior drag toward zero (no penetration) whichever row it lies in,
3234  * and a face between two solid cells the roof or wall law of its row (with
3235  * the full log-law target; the partial-cell weight of the target is not
3236  * applied) or the interior drag. Buildings only (erf.buildings_type = ImmersedForcing): the
3237  * terrain kernels keep the raw fractions, whose wall law is weighted by the
3238  * fluid fraction of the face. Default false, the raw fractions, for backward
3239  * compatibility (on an exact box a boundary cell then carries the wall law
3240  * and the interior drag together, and the roof only the drag); set it for
3241  * height-map buildings.
3242  * @var SolverChoice::if_stability_correction
3243  * @brief Whether immersed-forcing stability corrections are enabled.
3244  * @var SolverChoice::if_ws_floor
3245  * @brief Wind-speed floor [m/s] in the per-step change limit of the building wall-law forcing.
3246  * @var SolverChoice::if_damp_alpha
3247  * @brief Immersed-forcing damping coefficient.
3248  * @var SolverChoice::if_stability_wind_floor
3249  * @brief Wind-speed floor [m/s] behind the friction velocity of the wall law's stability estimate (0: none).
3250  * @var SolverChoice::if_psi_cap_factor
3251  * @brief Cap on psi_m in the immersed momentum wall law as a fraction of ln(z/z0) (1: none).
3252  * @var SolverChoice::use_lagged_delta_rt
3253  * @brief Whether to use lagged delta terms for moving terrain.
3254  * @var SolverChoice::rebalance_wrf_input
3255  * @brief Whether to rebalance states from wrfinput and wrfbdy data.
3256  * @var SolverChoice::gravity
3257  * @brief Effective gravitational acceleration.
3258  * @var SolverChoice::c_p
3259  * @brief Specific heat at constant pressure for dry air [J/(kg-K)].
3260  * @var SolverChoice::rdOcp
3261  * @brief Ratio of dry-air gas constant to c_p.
3262  * @var SolverChoice::grid_stretching_ratio
3263  * @brief Vertical grid stretching ratio.
3264  * @var SolverChoice::dz0
3265  * @brief Initial vertical grid spacing for stretched-grid setup.
3266  * @var SolverChoice::coriolis_factor
3267  * @brief Twice the planetary rotation rate used for Coriolis forcing.
3268  * @var SolverChoice::cosphi
3269  * @brief Cosine of the latitude used for Coriolis forcing.
3270  * @var SolverChoice::sinphi
3271  * @brief Sine of the latitude used for Coriolis forcing.
3272  * @var SolverChoice::custom_rhotheta_forcing
3273  * @brief Whether custom rho-theta forcing is enabled.
3274  * @var SolverChoice::custom_moisture_forcing
3275  * @brief Whether custom moisture forcing is enabled.
3276  * @var SolverChoice::custom_w_subsidence
3277  * @brief Whether custom vertical subsidence is enabled.
3278  * @var SolverChoice::do_theta_advection
3279  * @brief Whether custom vertical subsidence is applied to rho-theta.
3280  * @var SolverChoice::do_mom_advection
3281  * @brief Whether custom vertical subsidence is applied to momentum.
3282  * @var SolverChoice::custom_geostrophic_profile
3283  * @brief Whether a custom geostrophic wind profile is used.
3284  * @var SolverChoice::custom_forcing_prim_vars
3285  * @brief Whether custom forcing operates on primitive variables.
3286  * @var SolverChoice::spatial_rhotheta_forcing
3287  * @brief Whether spatially varying rho-theta forcing is enabled.
3288  * @var SolverChoice::spatial_moisture_forcing
3289  * @brief Whether spatially varying moisture forcing is enabled.
3290  * @var SolverChoice::nudging_from_input_sounding
3291  * @brief Whether solution fields are nudged toward input sounding data.
3292  * @var SolverChoice::nudging_from_observations
3293  * @brief Whether solution fields are nudged toward observations at stations.
3294  * @var SolverChoice::use_rotate_surface_flux
3295  * @brief Whether MOST surface fluxes are rotated with terrain.
3296  * @var SolverChoice::use_eamxx_shoc
3297  * @brief Whether any level uses the EAMxx SHOC PBL scheme.
3298  * @var SolverChoice::use_native_shoc
3299  * @brief Whether any level uses the native SHOC PBL scheme.
3300  * @var SolverChoice::transport_scalar
3301  * @brief Whether the passive scalar component is transported.
3302  * @var SolverChoice::time_avg_vel
3303  * @brief Whether time-averaged velocity fields are output.
3304  * @var SolverChoice::pert_type
3305  * @brief Turbulent perturbation type for each AMR level.
3306  * @var SolverChoice::perturbation_auto
3307  * @brief Select LES nests, using parent winds and a PBL ancestor's height,
3308  * or the nearest LES ancestor's diagnostic when no PBL scheme is present.
3309  * Defaults to true for WRFInput and Metgrid initialization, false otherwise.
3310  * @var SolverChoice::perturbation_auto_period
3311  * @brief Seconds between automatic parent-wind and PBL-height updates.
3312  * @var SolverChoice::perturbation_auto_pbl_fraction
3313  * @brief Fraction of the selected ancestor's PBL height occupied by perturbations.
3314  * @var SolverChoice::use_num_diff
3315  * @brief Whether sixth-order numerical diffusion is enabled.
3316  * @var SolverChoice::num_diff_coeff
3317  * @brief Numerical diffusion coefficient after input scaling.
3318  * @var SolverChoice::coupling_type
3319  * @brief Multilevel coupling strategy.
3320  * @var SolverChoice::moisture_type
3321  * @brief Moisture or microphysics model.
3322  * @var SolverChoice::windfarm_type
3323  * @brief Wind-farm model.
3324  * @var SolverChoice::windfarm_loc_type
3325  * @brief Coordinate system for wind-farm turbine locations.
3326  * @var SolverChoice::lsm_type
3327  * @brief Land-surface model.
3328  * @var SolverChoice::rad_type
3329  * @brief Radiation model.
3330  * @var SolverChoice::abl_driver_type
3331  * @brief Atmospheric boundary-layer driver type.
3332  * @var SolverChoice::abl_pressure_grad
3333  * @brief Applied ABL pressure-gradient forcing vector.
3334  * @var SolverChoice::abl_geo_forcing
3335  * @brief Applied geostrophic-wind forcing vector.
3336  * @var SolverChoice::abl_geo_wind_table
3337  * @brief Path to a time-varying geostrophic wind table.
3338  * @var SolverChoice::have_geo_wind_profile
3339  * @brief Whether a geostrophic wind profile has been configured.
3340  * @var SolverChoice::variable_coriolis
3341  * @brief Whether spatially varying Coriolis forcing is enabled.
3342  * @var SolverChoice::ave_plane
3343  * @brief Averaging plane index used by diagnostics.
3344  * @var SolverChoice::moisture_indices
3345  * @brief Index map of the moisture data carried by the active scheme: conserved-state
3346  * components for the species and qmoist slots for the moist diagnostics.
3347  * @var SolverChoice::moisture_tight_coupling
3348  * @brief Whether moisture updates use tight coupling.
3349  * @var SolverChoice::windfarm_loc_table
3350  * @brief Wind-farm location table path.
3351  * @var SolverChoice::windfarm_spec_table
3352  * @brief Wind-farm turbine specification table path.
3353  * @var SolverChoice::windfarm_spec_table_extra
3354  * @brief Additional wind-farm turbine specification table path.
3355  * @var SolverChoice::windfarm_blade_table
3356  * @brief Wind-farm blade table path.
3357  * @var SolverChoice::windfarm_airfoil_tables
3358  * @brief Wind-farm airfoil table paths.
3359  * @var SolverChoice::sampling_distance_by_D
3360  * @brief Turbine inflow sampling distance normalized by rotor diameter.
3361  * @var SolverChoice::turb_disk_angle
3362  * @brief Turbine disk angle from the x axis [deg].
3363  * @var SolverChoice::windfarm_x_shift
3364  * @brief Wind-farm x-coordinate shift.
3365  * @var SolverChoice::windfarm_y_shift
3366  * @brief Wind-farm y-coordinate shift.
3367  * @var SolverChoice::bdy_nudge_factor
3368  * @brief Boundary sponge nudging factor.
3369  * @var SolverChoice::use_wrf_bdy_qc_qi
3370  * @brief Whether WRF cloud water and active cloud ice are ingested at real boundaries.
3371  * @var SolverChoice::bdy_moist_nudge_type
3372  * @brief Moist-variable nudging strategy in boundary regions.
3373  * @var SolverChoice::do_forest_drag
3374  * @brief Whether forest canopy drag is enabled.
3375  * @var SolverChoice::const_massflux_u
3376  * @brief Target constant mass flux in the x direction.
3377  * @var SolverChoice::const_massflux_v
3378  * @brief Target constant mass flux in the y direction.
3379  * @var SolverChoice::const_massflux_tau
3380  * @brief Relaxation time scale for constant-mass-flux forcing.
3381  * @var SolverChoice::const_massflux_layer_lo
3382  * @brief Lower height of the constant-mass-flux forcing layer.
3383  * @var SolverChoice::const_massflux_layer_hi
3384  * @brief Upper height of the constant-mass-flux forcing layer.
3385  * @var SolverChoice::massflux_klo
3386  * @brief Lower vertical index for constant-mass-flux forcing.
3387  * @var SolverChoice::massflux_khi
3388  * @brief Upper vertical index for constant-mass-flux forcing.
3389  * @var SolverChoice::hindcast_boundary_data_dir
3390  * @brief Directory containing hindcast lateral boundary data.
3391  * @var SolverChoice::hindcast_surface_data_dir
3392  * @brief Directory containing hindcast surface boundary data.
3393  * @var SolverChoice::hindcast_data_interval_in_hrs
3394  * @brief Time interval between hindcast data files [hr].
3395  * @var SolverChoice::hindcast_lateral_forcing
3396  * @brief Whether hindcast lateral forcing is enabled.
3397  * @var SolverChoice::hindcast_surface_bcs
3398  * @brief Whether hindcast surface boundary conditions are enabled.
3399  * @var SolverChoice::hindcast_lateral_sponge_strength
3400  * @brief Strength of the hindcast lateral sponge layer.
3401  * @var SolverChoice::hindcast_lateral_sponge_length
3402  * @brief Length of the hindcast lateral sponge layer.
3403  * @var SolverChoice::hindcast_zhi_sponge_strength
3404  * @brief Strength of the upper hindcast sponge layer.
3405  * @var SolverChoice::hindcast_zhi_sponge_length
3406  * @brief Length of the upper hindcast sponge layer.
3407  * @var SolverChoice::hindcast_zhi_sponge_damping
3408  * @brief Whether upper-boundary hindcast sponge damping is enabled.
3409  * @var SolverChoice::io_hurricane_eye_tracker
3410  * @brief Whether hurricane-eye tracking output is enabled.
3411  * @var SolverChoice::hurricane_eye_latitude
3412  * @brief Initial hurricane-eye latitude.
3413  * @var SolverChoice::hurricane_eye_longitude
3414  * @brief Initial hurricane-eye longitude.
3415  * @var SolverChoice::is_init_for_ensemble
3416  * @brief Whether initialization is for an ensemble simulation.
3417  * @var SolverChoice::n_ensemble
3418  * @brief Number of ensemble members.
3419  * @var SolverChoice::ens_pert_correlated_radius
3420  * @brief Spatial correlation radius for ensemble perturbations.
3421  * @var SolverChoice::ens_pert_amplitude
3422  * @brief Amplitude of ensemble initialization perturbations.
3423  * @var SolverChoice::coarse_bckgnd_data_file
3424  * @brief Coarse background data file used for ensemble initialization.
3425  */
3426 #endif
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:51
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:19
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:72
constexpr amrex::Real R_d
Definition: ERF_Constants.H:49
static const std::string BoundaryFaceName[]
Definition: ERF_DataStruct.H:218
TauType
Stress tensor component indices.
Definition: ERF_DataStruct.H:48
@ tau12
Definition: ERF_DataStruct.H:49
@ tau23
Definition: ERF_DataStruct.H:49
@ tau33
Definition: ERF_DataStruct.H:49
@ tau22
Definition: ERF_DataStruct.H:49
@ tau11
Definition: ERF_DataStruct.H:49
@ tau32
Definition: ERF_DataStruct.H:49
@ tau31
Definition: ERF_DataStruct.H:49
@ tau21
Definition: ERF_DataStruct.H:49
@ tau13
Definition: ERF_DataStruct.H:49
bool anelastic_superdroplets_configuration_invalid(const MoistureType moisture_type, const bool any_anelastic) noexcept
Whether an anelastic SuperDroplets configuration is unsupported.
Definition: ERF_DataStruct.H:153
Rayleigh
Rayleigh damping profile component indices.
Definition: ERF_DataStruct.H:206
@ ubar
Definition: ERF_DataStruct.H:207
@ wbar
Definition: ERF_DataStruct.H:207
@ nvars
Definition: ERF_DataStruct.H:207
@ vbar
Definition: ERF_DataStruct.H:207
@ thetabar
Definition: ERF_DataStruct.H:207
Sponge
Sponge reference-state component indices.
Definition: ERF_DataStruct.H:214
@ nvars_sponge
Definition: ERF_DataStruct.H:215
@ vbar_sponge
Definition: ERF_DataStruct.H:215
@ ubar_sponge
Definition: ERF_DataStruct.H:215
MapFacType
Indices for map scale factors on mass and face grids.
Definition: ERF_DataStruct.H:36
@ v_x
Definition: ERF_DataStruct.H:38
@ num
Definition: ERF_DataStruct.H:38
@ u_y
Definition: ERF_DataStruct.H:39
@ v_y
Definition: ERF_DataStruct.H:39
@ m_y
Definition: ERF_DataStruct.H:39
@ u_x
Definition: ERF_DataStruct.H:38
@ m_x
Definition: ERF_DataStruct.H:38
Coord
Coordinate-axis selector.
Definition: ERF_DataStruct.H:198
AMREX_ENUM(PlotFileType, None, Amrex, Netcdf)
#define RhoQ4_comp
Definition: ERF_IndexDefines.H:69
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:67
#define RhoQ3_comp
Definition: ERF_IndexDefines.H:68
#define RhoQ11_comp
Definition: ERF_IndexDefines.H:76
#define RhoQ9_comp
Definition: ERF_IndexDefines.H:74
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:66
#define RhoQ6_comp
Definition: ERF_IndexDefines.H:71
#define RhoQ8_comp
Definition: ERF_IndexDefines.H:73
#define RhoQ5_comp
Definition: ERF_IndexDefines.H:70
#define RhoQ7_comp
Definition: ERF_IndexDefines.H:72
@ surface_layer
#define RhoQ10_comp
Definition: ERF_IndexDefines.H:75
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
ParmParse pp("prob")
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
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 PI
Definition: ERF_NumericalConstants.H:46
std::string name
Definition: ERF_Plotfile2DCatalog.cpp:116
Radiation model type and control parameters.
amrex::Real Real
Definition: ERF_ShocInterface.H:19
ShocMomentumTransport
Definition: ERF_ShocTransportStruct.H:16
bool shoc_disables_momentum_transport(ShocMomentumTransport mode)
Definition: ERF_ShocTransportStruct.H:47
void read_shoc_transport_modes(ShocTransportMode &transport_mode, ShocMomentumTransport &momentum_transport)
Read the native SHOC transport modes from the erf.shoc input namespace.
Definition: ERF_ShocTransportStruct.H:118
ShocTransportMode
Definition: ERF_ShocTransportStruct.H:11
int query_one_or_per_level_enum_case_insensitive(const amrex::ParmParse &pp, const char *query_string, T &query_var, const int lev, const int maxlev)
Query a scalar or per-level enum input value using case-insensitive matching.
Definition: ERF_TurbStruct.H:85
AMREX_ASSERT_WITH_MESSAGE(wbar_cutoff_min > wbar_cutoff_max, "ERROR: wbar_cutoff_min < wbar_cutoff_max")
Definition: ERF_EWP.H:9
Definition: ERF_Fitch.H:9
Definition: ERF_GeneralAD.H:8
Definition: ERF_Kessler.H:78
Definition: ERF_MoistNoCondensation.H:13
Definition: ERF_Morrison.H:124
Definition: ERF_NOAHMP.H:57
Definition: ERF_SAM.H:57
Definition: ERF_SLM.H:185
Definition: ERF_SatAdj.H:47
Definition: ERF_SimpleAD.H:8
Definition: ERF_WDM6.H:115
Definition: ERF_WSM6.H:128
@ None
Definition: ERF_LSM_Types.H:6
@ dz
Definition: ERF_AdvanceWDM6.cpp:273
Definition: ERF_AdvStruct.H:19
void display(std::string &pp_prefix)
Print the configured advection choices.
Definition: ERF_AdvStruct.H:245
void init_params(std::string pp_prefix)
Read advection options from the input parameter database.
Definition: ERF_AdvStruct.H:25
Definition: ERF_CanopyTurbStruct.H:16
void init_params(amrex::ParmParse &pp)
Definition: ERF_CanopyTurbStruct.H:32
Definition: ERF_DampingStruct.H:22
void init_params(std::string pp_prefix)
Read damping options from the input parameter database.
Definition: ERF_DampingStruct.H:28
void display()
Print the configured damping options.
Definition: ERF_DampingStruct.H:71
Definition: ERF_DiffStruct.H:24
MolecDiffType molec_diff_type
Selected molecular transport model.
Definition: ERF_DiffStruct.H:96
void init_params(std::string pp_prefix)
Read diffusion options from the input parameter database.
Definition: ERF_DiffStruct.H:30
void display()
Print the configured diffusion options.
Definition: ERF_DiffStruct.H:79
Definition: ERF_EBStruct.H:36
void init_params(std::string pp_prefix)
Read embedded-boundary options from the input parameter database.
Definition: ERF_EBStruct.H:42
void display()
Print the configured embedded-boundary options.
Definition: ERF_EBStruct.H:63
An ordered list of conserved-state components forming an aggregate moisture output variable (qt,...
Definition: ERF_DataStruct.H:514
int comp[max_size]
Definition: ERF_DataStruct.H:516
int size
Definition: ERF_DataStruct.H:517
bool empty() const
Definition: ERF_DataStruct.H:523
void append(int c)
Append a component, ignoring the ones the scheme does not carry.
Definition: ERF_DataStruct.H:520
static constexpr int max_size
qv, qc, qi, qr, qs, qg
Definition: ERF_DataStruct.H:515
Whether this map decides a variable's availability, and if so whether the variable is available.
Definition: ERF_DataStruct.H:643
bool available
the active scheme carries the data behind it
Definition: ERF_DataStruct.H:645
bool governed
the name is a moisture output variable
Definition: ERF_DataStruct.H:644
The moisture data carried by the active microphysics scheme.
Definition: ERF_DataStruct.H:251
int nc
cloud liquid water number
Definition: ERF_DataStruct.H:272
int qs
snow
Definition: ERF_DataStruct.H:266
int cond_rate
condensation rate
Definition: ERF_DataStruct.H:301
int comp_for_var(const std::string &name) const
The conserved-state component behind a single-species output name, or absent if the name is not a sin...
Definition: ERF_DataStruct.H:604
int nn
CCN / total aerosol number.
Definition: ERF_DataStruct.H:282
int qr
rain
Definition: ERF_DataStruct.H:265
static constexpr int absent
not carried by the active scheme
Definition: ERF_DataStruct.H:254
bool has_comp(int comp) const
Test whether a conserved-state component holds one of the moisture variables this scheme carries.
Definition: ERF_DataStruct.H:566
int precip_accum
accumulated surface precipitation, all species
Definition: ERF_DataStruct.H:299
MoistureComponentIndices()=default
Construct an empty map, i.e. a dry run.
int qi
cloud ice
Definition: ERF_DataStruct.H:264
int rel_hum
relative humidity
Definition: ERF_DataStruct.H:300
int graupel_accum
accumulated surface graupel
Definition: ERF_DataStruct.H:298
static constexpr int computed_from_state
Definition: ERF_DataStruct.H:255
int ni
cloud ice number
Definition: ERF_DataStruct.H:273
int qv
water vapor
Definition: ERF_DataStruct.H:262
int rain_accum
accumulated surface rain
Definition: ERF_DataStruct.H:296
CompList nonprecipitating_comps() const
Components summed for non-precipitating water, "qn": vapor plus the suspended condensate....
Definition: ERF_DataStruct.H:542
bool has_reflectivity_species() const
Test whether the active scheme carries rain, snow and graupel in the RhoQ4/RhoQ5/RhoQ6 slots assumed ...
Definition: ERF_DataStruct.H:582
int snow_accum
accumulated surface snow
Definition: ERF_DataStruct.H:297
CompList total_water_comps() const
Components summed for total water, "qt": every mass mixing ratio.
Definition: ERF_DataStruct.H:531
int nr
rain number
Definition: ERF_DataStruct.H:274
int qc
cloud liquid water
Definition: ERF_DataStruct.H:263
int ng
graupel number
Definition: ERF_DataStruct.H:276
CompList precipitating_comps() const
Components summed for precipitating water, "qp": the falling species.
Definition: ERF_DataStruct.H:551
int qg
graupel
Definition: ERF_DataStruct.H:267
bool has_moisture() const
Test whether the active scheme carries any moisture at all.
Definition: ERF_DataStruct.H:592
int ns
snow number
Definition: ERF_DataStruct.H:275
VarAvailability query_var(const std::string &name) const
Classify an output variable name against this index map.
Definition: ERF_DataStruct.H:659
bool has_derived_var(const std::string &name) const
Test whether the moist species behind a derived/plot variable name is carried by the active scheme.
Definition: ERF_DataStruct.H:705
static MoistureComponentIndices from_moisture_model(const MoistureType moisture_type)
Build the complete index map – conserved-state components and qmoist diagnostic slots – for a moistur...
Definition: ERF_DataStruct.H:357
MoistureComponentIndices(int qv_comp, int qc_comp, int qi_comp=absent, int qr_comp=absent, int qs_comp=absent, int qg_comp=absent, int nc_comp=absent, int ni_comp=absent, int nr_comp=absent, int ns_comp=absent, int ng_comp=absent, int nn_comp=absent)
Construct the conserved-state component map.
Definition: ERF_DataStruct.H:323
int qmoist_index_for_var(const std::string &name) const
The qmoist slot behind a moist diagnostic output name.
Definition: ERF_DataStruct.H:629
Definition: ERF_RadStruct.H:73
void read(const std::string &prefix)
Definition: ERF_RadStruct.H:79
Container holding radiation-related choices and parameters.
Definition: ERF_RadStruct.H:132
void init_params(bool two_stream_selected, std::string pp_prefix)
Definition: ERF_RadStruct.H:904
Definition: ERF_DataStruct.H:715
bool use_coupled_sst
Definition: ERF_DataStruct.H:2980
int sbm_moment_mode
Definition: ERF_DataStruct.H:2944
bool custom_geostrophic_profile
Whether a custom geostrophic wind profile is used.
Definition: ERF_DataStruct.H:2755
int n_ensemble
Number of ensemble members.
Definition: ERF_DataStruct.H:3063
WindFarmLocType windfarm_loc_type
Coordinate system for wind-farm turbine locations.
Definition: ERF_DataStruct.H:2952
bool implicit_before_substep
Whether implicit diffusion is applied before acoustic substepping.
Definition: ERF_DataStruct.H:2664
amrex::Real const_massflux_v
Target constant mass flux in the y direction.
Definition: ERF_DataStruct.H:3044
std::string hindcast_boundary_data_dir
Directory containing hindcast lateral boundary data.
Definition: ERF_DataStruct.H:3051
amrex::Real if_psi_cap_factor
Cap on psi_m in the immersed momentum wall law as a fraction of ln(z/z0) (1: none).
Definition: ERF_DataStruct.H:2722
amrex::Real ens_pert_correlated_radius
Spatial correlation radius for ensemble perturbations.
Definition: ERF_DataStruct.H:3064
amrex::Real forest_leaf_theta_fixed
Definition: ERF_DataStruct.H:3038
static InitType init_type
Initial-condition source selected for the run.
Definition: ERF_DataStruct.H:2583
amrex::Real coriolis_factor
Twice the planetary rotation rate used for Coriolis forcing.
Definition: ERF_DataStruct.H:2745
int perturbation_pblh_level(int lev) const
Definition: ERF_DataStruct.H:2919
bool io_hurricane_eye_tracker
Whether hurricane-eye tracking output is enabled.
Definition: ERF_DataStruct.H:3059
static bool flat_terrain
Permit fixed-index planar averages after validating a flat fitted mesh.
Definition: ERF_DataStruct.H:2592
std::string abl_geo_wind_table
Path to a time-varying geostrophic wind table.
Definition: ERF_DataStruct.H:2985
amrex::Vector< int > fixed_density
Per-level flag selecting fixed-density treatment; always 1 at an anelastic level.
Definition: ERF_DataStruct.H:2631
amrex::Vector< TurbChoice > turbChoice
Turbulence options for each AMR level.
Definition: ERF_DataStruct.H:2620
amrex::Real const_massflux_layer_lo
Lower height of the constant-mass-flux forcing layer.
Definition: ERF_DataStruct.H:3046
amrex::Real hindcast_zhi_sponge_length
Length of the upper hindcast sponge layer.
Definition: ERF_DataStruct.H:3056
TopographicShadingChoice topographicChoice
Definition: ERF_DataStruct.H:2624
bool if_fraction_stress
erf.if_wall_form = fraction_stress: the wall stress of ERF_ImmersedWallCell.H in the wall cell of eac...
Definition: ERF_DataStruct.H:2714
amrex::Real gravity
Effective gravitational acceleration.
Definition: ERF_DataStruct.H:2736
void check_params(int max_level, const amrex::Vector< amrex::Geometry > &geom_vect, amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > phys_bc_type)
Validate solver options after geometry and boundary conditions are known.
Definition: ERF_DataStruct.H:2104
bool pbl_suppresses_microphysics_condensation() const noexcept
Query whether the active PBL scheme suppresses microphysics condensation.
Definition: ERF_DataStruct.H:2895
amrex::Real perturbation_auto_period
Seconds between automatic parent-wind and PBL-height updates.
Definition: ERF_DataStruct.H:2934
bool variable_coriolis
Whether spatially varying Coriolis forcing is enabled.
Definition: ERF_DataStruct.H:2988
bool implicit_moisture_diffusion
Whether implicit vertical moisture diffusion is included.
Definition: ERF_DataStruct.H:2661
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2942
void display(int max_level, std::string pp_prefix)
Print the configured solver, physics, and transport options.
Definition: ERF_DataStruct.H:2345
amrex::Real poisson_abstol
Absolute tolerance for the Poisson solve.
Definition: ERF_DataStruct.H:2676
amrex::Real if_Cd_momentum
Immersed-forcing drag coefficient for momentum.
Definition: ERF_DataStruct.H:2699
bool nudging_t
Definition: ERF_DataStruct.H:2776
amrex::Real if_Olen_in
Input Obukhov length for immersed-forcing MOST [m].
Definition: ERF_DataStruct.H:2710
amrex::Vector< int > buoyancy_type
Per-level buoyancy formulation selector.
Definition: ERF_DataStruct.H:2633
bool forest_biophysics
Definition: ERF_DataStruct.H:3036
void build_coriolis_forcings_const_lat(std::string pp_prefix)
Build constant-latitude Coriolis forcing parameters.
Definition: ERF_DataStruct.H:2508
CouplingType coupling_type
Multilevel coupling strategy.
Definition: ERF_DataStruct.H:2941
bool use_pert_pres_gradient
Whether momentum equations use perturbational pressure gradients.
Definition: ERF_DataStruct.H:2685
bool sbm_test_native_qc_write_fault
Definition: ERF_DataStruct.H:2946
amrex::Vector< int > project_initial_velocity
Per-level flag for projecting the initial velocity.
Definition: ERF_DataStruct.H:2632
int ave_plane
Averaging plane index used by diagnostics.
Definition: ERF_DataStruct.H:2990
amrex::Real const_massflux_u
Target constant mass flux in the x direction.
Definition: ERF_DataStruct.H:3043
bool nudging_q
Definition: ERF_DataStruct.H:2777
int ncorr
Number of projection correction iterations.
Definition: ERF_DataStruct.H:2669
amrex::Real if_small_volfrac
Immersed-forcing solid fraction below which a cell or face counts as fluid; read from eb2....
Definition: ERF_DataStruct.H:2723
static bool buildings_fc_factories
Use native face-centered EB factories for immersed buildings on a static fitted mesh.
Definition: ERF_DataStruct.H:2598
amrex::Real sampling_distance_by_D
Turbine inflow sampling distance normalized by rotor diameter.
Definition: ERF_DataStruct.H:2999
amrex::Real windfarm_y_shift
Wind-farm y-coordinate shift.
Definition: ERF_DataStruct.H:3002
bool use_rotate_surface_flux
Whether MOST surface fluxes are rotated with terrain.
Definition: ERF_DataStruct.H:2784
bool use_building_immersed_forcing(const amrex::Geometry &geom) const noexcept
Definition: ERF_DataStruct.H:2808
bool moisture_tight_coupling
Whether moisture updates use tight coupling.
Definition: ERF_DataStruct.H:2995
EBChoice ebChoice
Embedded-boundary options.
Definition: ERF_DataStruct.H:2621
bool compute_mean_vars
Definition: ERF_DataStruct.H:2801
amrex::Real test_mapfactor_amp
Amplitude of the spatially varying test map scale factors on level 0 (0: not used).
Definition: ERF_DataStruct.H:2681
amrex::Real beta_s
Time off-centering coefficient for forward weighting.
Definition: ERF_DataStruct.H:2641
bool implicit_ke_diffusion
Whether implicit vertical TKE diffusion is included.
Definition: ERF_DataStruct.H:2662
bool interp_atmos_from_coarse
Definition: ERF_DataStruct.H:3030
bool any_perturbation() const
Query whether any level uses a turbulent perturbation mode.
Definition: ERF_DataStruct.H:2904
amrex::Real if_z0
Immersed-forcing roughness length [m].
Definition: ERF_DataStruct.H:2706
bool forest_substep
Whether canopy source terms are applied only during substeps.
Definition: ERF_DataStruct.H:2696
bool use_wrf_bdy_density
Definition: ERF_DataStruct.H:3008
bool implicit_thermal_diffusion
Whether implicit vertical thermal diffusion is included.
Definition: ERF_DataStruct.H:2660
bool if_implicit_projection
Definition: ERF_DataStruct.H:2704
std::string mean_vars_reset_mode
Definition: ERF_DataStruct.H:2804
bool use_gravity
Whether gravitational forcing is enabled.
Definition: ERF_DataStruct.H:2688
amrex::Real beta_s_max
Upper bound of the spatially varying beta_s.
Definition: ERF_DataStruct.H:2647
amrex::Real num_diff_coeff
Numerical diffusion coefficient after input scaling.
Definition: ERF_DataStruct.H:2939
amrex::Real if_wall_tke_time_factor
1 (the default) pins the wall cell after every stage and in the implicit TKE solve; a larger value re...
Definition: ERF_DataStruct.H:2717
amrex::Real bdy_nudge_factor
Boundary sponge nudging factor.
Definition: ERF_DataStruct.H:3005
int sbm_cloud_rain_split
Definition: ERF_DataStruct.H:2945
amrex::Real rdOcp
Ratio of dry-air gas constant to c_p.
Definition: ERF_DataStruct.H:2738
bool use_wvel_perturbation(int lev) const
Query whether vertical-velocity CPM perturbations are enabled on a level.
Definition: ERF_DataStruct.H:2855
bool hindcast_lateral_forcing
Whether hindcast lateral forcing is enabled.
Definition: ERF_DataStruct.H:3053
bool do_theta_advection
Whether custom vertical subsidence is applied to rho-theta.
Definition: ERF_DataStruct.H:2753
bool use_native_shoc
Whether any level uses the native SHOC PBL scheme.
Definition: ERF_DataStruct.H:2788
amrex::Real nudging_q_z2
Definition: ERF_DataStruct.H:2773
static bool use_real_bcs
Whether real-data lateral boundary conditions are used.
Definition: ERF_DataStruct.H:2601
bool spatial_moisture_forcing
Whether spatially varying moisture forcing is enabled.
Definition: ERF_DataStruct.H:2758
bool perturbation_auto
Select LES nests, using parent winds and a PBL ancestor's height, or the nearest LES ancestor's diagn...
Definition: ERF_DataStruct.H:2933
RRTMGPSolarChoice rrtmgpSolarChoice
Definition: ERF_DataStruct.H:2623
amrex::Vector< SubsteppingType > substepping_type
Acoustic substepping type for each AMR level.
Definition: ERF_DataStruct.H:2628
amrex::Real nudging_u_z2
Definition: ERF_DataStruct.H:2769
bool use_perturbation(int lev) const
Query whether any turbulent perturbation mode is enabled on a level.
Definition: ERF_DataStruct.H:2820
static SoundingType sounding_type
Interpretation used for input sounding profiles.
Definition: ERF_DataStruct.H:2586
int bdy_moist_nudge_type
Moist-variable nudging strategy in boundary regions.
Definition: ERF_DataStruct.H:3017
amrex::Real poisson_reltol
Relative tolerance for the Poisson solve.
Definition: ERF_DataStruct.H:2677
bool use_coriolis
Whether Coriolis forcing is enabled.
Definition: ERF_DataStruct.H:2689
amrex::Vector< PerturbationType > pert_type
Turbulent perturbation type for each AMR level.
Definition: ERF_DataStruct.H:2932
static MeshType mesh_type
Vertical mesh representation.
Definition: ERF_DataStruct.H:2604
AdvChoice advChoice
Advection-related options.
Definition: ERF_DataStruct.H:2616
UrbanType urban_type
Definition: ERF_DataStruct.H:2954
SpongeChoice spongeChoice
Sponge-layer options.
Definition: ERF_DataStruct.H:2619
void init_params(int max_level, std::string pp_prefix)
Read solver-wide algorithmic options from the input parameter database.
Definition: ERF_DataStruct.H:722
bool time_avg_vel
Whether time-averaged velocity fields are output.
Definition: ERF_DataStruct.H:2798
bool use_direct_perturbation(int lev) const
Query whether direct turbulent perturbations are enabled on a level.
Definition: ERF_DataStruct.H:2833
bool have_geo_wind_profile
Whether a geostrophic wind profile has been configured.
Definition: ERF_DataStruct.H:2986
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > abl_pressure_grad
Applied ABL pressure-gradient forcing vector.
Definition: ERF_DataStruct.H:2983
RadChoice radChoice
Definition: ERF_DataStruct.H:2622
amrex::Vector< int > anelastic
Per-level flag selecting anelastic dynamics.
Definition: ERF_DataStruct.H:2629
bool four_stream_radiation
Whether the four-stream radiation approximation is enabled.
Definition: ERF_DataStruct.H:2692
amrex::Real perturbation_auto_pbl_fraction
Fraction of the selected ancestor's PBL height occupied by perturbations.
Definition: ERF_DataStruct.H:2935
amrex::Real beta_s_min
Lower bound of the spatially varying beta_s.
Definition: ERF_DataStruct.H:2646
int gradp_type
Terrain-fitted horizontal pressure-gradient formulation.
Definition: ERF_DataStruct.H:2683
DampingChoice dampingChoice
Damping-related options.
Definition: ERF_DataStruct.H:2618
amrex::Real if_damp_alpha
Immersed-forcing damping coefficient.
Definition: ERF_DataStruct.H:2720
bool rebalance_wrf_input
Whether to rebalance states from wrfinput and wrfbdy data.
Definition: ERF_DataStruct.H:2733
bool wrfinput_zlevels_from_file
Definition: ERF_DataStruct.H:3027
amrex::Vector< amrex::Real > sbm_edges
Definition: ERF_DataStruct.H:2948
amrex::Real turb_disk_angle
Turbine disk angle from the x axis [deg].
Definition: ERF_DataStruct.H:3000
amrex::Real hindcast_data_interval_in_hrs
Time interval between hindcast data files [hr].
Definition: ERF_DataStruct.H:3052
amrex::Real const_massflux_layer_hi
Upper height of the constant-mass-flux forcing layer.
Definition: ERF_DataStruct.H:3047
std::string wall_dist_type
Definition: ERF_DataStruct.H:2670
bool avg_grid_faces_to_nodes
Definition: ERF_DataStruct.H:3020
amrex::Real cf_loglaw_z0
Roughness length [m] of that share; by default erf.if_z0 with immersed forcing, else the inputs' erf....
Definition: ERF_DataStruct.H:2727
std::string windfarm_spec_table_extra
Additional wind-farm turbine specification table path.
Definition: ERF_DataStruct.H:2997
amrex::Real if_ws_floor
Wind-speed floor [m/s] in the per-step change limit of the building wall-law forcing.
Definition: ERF_DataStruct.H:2719
amrex::Real lsf_timescale
Definition: ERF_DataStruct.H:2781
bool is_init_for_ensemble
Whether initialization is for an ensemble simulation.
Definition: ERF_DataStruct.H:3062
bool uses_shoc_family() const noexcept
Query whether any SHOC-family PBL scheme is active.
Definition: ERF_DataStruct.H:2864
bool test_mapfactor
Whether to use test map scale factors.
Definition: ERF_DataStruct.H:2680
amrex::Real nudging_u_z1
Definition: ERF_DataStruct.H:2768
bool if_snap_partial_cells
Definition: ERF_DataStruct.H:2712
amrex::Vector< amrex::Real > sbm_pivots
Definition: ERF_DataStruct.H:2949
std::string coarse_bckgnd_data_file
Coarse background data file used for ensemble initialization.
Definition: ERF_DataStruct.H:3066
bool do_forest_drag
Whether forest canopy drag is enabled.
Definition: ERF_DataStruct.H:3033
amrex::Real dz0
Initial vertical grid spacing for stretched-grid setup.
Definition: ERF_DataStruct.H:2742
amrex::Real windfarm_x_shift
Wind-farm x-coordinate shift.
Definition: ERF_DataStruct.H:3001
bool use_lagged_delta_rt
Whether to use lagged delta terms for moving terrain.
Definition: ERF_DataStruct.H:2730
amrex::Real ens_pert_amplitude
Amplitude of ensemble initialization perturbations.
Definition: ERF_DataStruct.H:3065
std::string windfarm_airfoil_tables
Wind-farm airfoil table paths.
Definition: ERF_DataStruct.H:2998
amrex::Vector< AnelasticType > anelastic_type
Per-level two-stage scheme used by the anelastic integrator.
Definition: ERF_DataStruct.H:2630
amrex::Real bdy_rho_nudge_factor
Definition: ERF_DataStruct.H:3014
amrex::Real nudging_q_z1
Definition: ERF_DataStruct.H:2772
amrex::Vector< amrex::Vector< amrex::Real > > vert_implicit_fac
Per-level, per-stage implicit vertical diffusion factors.
Definition: ERF_DataStruct.H:2657
bool transport_scalar
Whether the passive scalar component is transported.
Definition: ERF_DataStruct.H:2795
bool cf_loglaw_fill
Share the flux of the first coarse face above a wall among its fine sub-faces at the lateral coarse/f...
Definition: ERF_DataStruct.H:2726
TerrainPoissonSolver terrain_poisson_solver
Projection solver on a terrain-fitted mesh: gmres_fft (default), mlmg or gmres_mlmg.
Definition: ERF_DataStruct.H:2671
bool hindcast_zhi_sponge_damping
Whether upper-boundary hindcast sponge damping is enabled.
Definition: ERF_DataStruct.H:3057
amrex::Real hurricane_eye_longitude
Initial hurricane-eye longitude.
Definition: ERF_DataStruct.H:3060
bool nudging_from_observations
Whether solution fields are nudged toward observations at stations.
Definition: ERF_DataStruct.H:2766
LandSurfaceType lsm_type
Land-surface model.
Definition: ERF_DataStruct.H:2953
int massflux_khi
Upper vertical index for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:3049
bool large_scale_forcing
Definition: ERF_DataStruct.H:2779
bool implicit_terrain_metric
Whether the compact terrain-metric part of the vertical diffusion is solved implicitly.
Definition: ERF_DataStruct.H:2667
bool if_wall_tke
Hold the k-eqn TKE of the fraction-stress wall cell at its flat-ground wall value (ib_wall::wall_tke)...
Definition: ERF_DataStruct.H:2716
bool substepping_diag
Whether to emit extra CFL diagnostics for compressible substepping.
Definition: ERF_DataStruct.H:2637
static TerrainType terrain_type
Terrain or immersed-boundary representation.
Definition: ERF_DataStruct.H:2589
amrex::Real mean_vars_reset_time
Definition: ERF_DataStruct.H:2805
std::string windfarm_blade_table
Wind-farm blade table path.
Definition: ERF_DataStruct.H:2998
bool if_flat_wall_dissipation
Partial fraction-stress wall cells dissipate TKE with the length scale of flat ground's first cell; o...
Definition: ERF_DataStruct.H:2718
CanopyTurbulence canopyTurb
Definition: ERF_DataStruct.H:3040
amrex::Vector< int > is_land
Per-level land/water selector.
Definition: ERF_DataStruct.H:2634
amrex::Real hindcast_lateral_sponge_strength
Strength of the hindcast lateral sponge layer.
Definition: ERF_DataStruct.H:3055
bool forest_biophysics_heat
Definition: ERF_DataStruct.H:3037
std::string if_average_down
Cell average down over immersed-forcing levels: "plain" (the default, the plain cell average) or "flu...
Definition: ERF_DataStruct.H:2724
ShocTransportMode shoc_transport_mode
Definition: ERF_DataStruct.H:2791
amrex::Real hurricane_eye_latitude
Initial hurricane-eye latitude.
Definition: ERF_DataStruct.H:3060
bool rad_uses_interface() const
True for the models that run through the IRadiation interface (RRTMGP, Simple); the two-stream model ...
Definition: ERF_DataStruct.H:2960
amrex::Real cosphi
Cosine of the latitude used for Coriolis forcing.
Definition: ERF_DataStruct.H:2746
amrex::Real if_Cd_scalar
Immersed-forcing drag coefficient for scalars.
Definition: ERF_DataStruct.H:2700
amrex::Real if_cf_mismatch_warning
Spread of the fine solid fractions under a coarse cell along a lateral coarse/fine face at or above w...
Definition: ERF_DataStruct.H:2725
static BuildingsType buildings_type
Building representation.
Definition: ERF_DataStruct.H:2595
std::string windfarm_loc_table
Wind-farm location table path.
Definition: ERF_DataStruct.H:2997
std::string hindcast_surface_data_dir
Directory containing hindcast surface boundary data.
Definition: ERF_DataStruct.H:3051
int if_wall_face_spacing
Spacing the face above the fraction-stress wall cell differences over: 0 the cell height,...
Definition: ERF_DataStruct.H:2715
ShocMomentumTransport shoc_momentum_transport
Definition: ERF_DataStruct.H:2792
bool custom_rhotheta_forcing
Whether custom rho-theta forcing is enabled.
Definition: ERF_DataStruct.H:2750
amrex::Real beta_d
Divergence damping coefficient used in the acoustic substepping.
Definition: ERF_DataStruct.H:2651
bool sbm_test_donor_cfl_fault
Definition: ERF_DataStruct.H:2947
amrex::Real if_init_surf_temp
Initial immersed-forcing surface temperature [K].
Definition: ERF_DataStruct.H:2708
amrex::Real nudging_t_z2
Definition: ERF_DataStruct.H:2771
int massflux_klo
Lower vertical index for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:3048
std::string lsf_file
Definition: ERF_DataStruct.H:2780
std::string windfarm_spec_table
Wind-farm turbine specification table path.
Definition: ERF_DataStruct.H:2997
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > abl_geo_forcing
Applied geostrophic-wind forcing vector.
Definition: ERF_DataStruct.H:2984
bool read_int_string(int max_level, const char *string_to_read, amrex::Vector< int > &vec_to_fill, int default_int)
Read one integer value or one value per AMR level.
Definition: ERF_DataStruct.H:2559
ABLDriverType abl_driver_type
Atmospheric boundary-layer driver type.
Definition: ERF_DataStruct.H:2982
DiffChoice diffChoice
Diffusion-related options.
Definition: ERF_DataStruct.H:2617
amrex::Real if_surf_temp_flux
Immersed-forcing surface temperature flux [K m/s].
Definition: ERF_DataStruct.H:2707
bool spatial_rhotheta_forcing
Whether spatially varying rho-theta forcing is enabled.
Definition: ERF_DataStruct.H:2757
amrex::Vector< int > urban_enabled_lev
Definition: ERF_DataStruct.H:2955
WindFarmType windfarm_type
Wind-farm model.
Definition: ERF_DataStruct.H:2951
bool use_variable_beta_s
Whether beta_s varies on the w-faces with the terrain slope.
Definition: ERF_DataStruct.H:2645
bool use_source_perturbation(int lev) const
Query whether source-term turbulent perturbations are enabled on a level.
Definition: ERF_DataStruct.H:2845
bool custom_w_subsidence
Whether custom vertical subsidence is enabled.
Definition: ERF_DataStruct.H:2752
bool rad_feeds_lsm() const
True when the radiation model's outputs feed the land model (downwelling shortwave,...
Definition: ERF_DataStruct.H:2974
bool custom_moisture_forcing
Whether custom moisture forcing is enabled.
Definition: ERF_DataStruct.H:2751
amrex::Real if_surf_heating_rate
Immersed-forcing surface heating rate [K/hr].
Definition: ERF_DataStruct.H:2709
int sbm_nbins
Definition: ERF_DataStruct.H:2943
MoistureComponentIndices moisture_indices
Index map of the moisture data carried by the active scheme: conserved-state components for the speci...
Definition: ERF_DataStruct.H:2993
RadiationType rad_type
Radiation model.
Definition: ERF_DataStruct.H:2956
amrex::Real grid_stretching_ratio
Vertical grid stretching ratio.
Definition: ERF_DataStruct.H:2741
bool hindcast_surface_bcs
Whether hindcast surface boundary conditions are enabled.
Definition: ERF_DataStruct.H:3054
bool host_owns_vertical_scalar_diffusion() const noexcept
Test whether the host owns scalar diffusion.
Definition: ERF_DataStruct.H:2884
bool immersed_forcing_substep
Whether immersed-forcing source terms are applied only during substeps.
Definition: ERF_DataStruct.H:2695
bool if_use_most
Whether immersed-forcing MOST is enabled.
Definition: ERF_DataStruct.H:2711
amrex::Real const_massflux_tau
Relaxation time scale for constant-mass-flux forcing.
Definition: ERF_DataStruct.H:3045
amrex::Real hindcast_lateral_sponge_length
Length of the hindcast lateral sponge layer.
Definition: ERF_DataStruct.H:3055
bool use_eamxx_shoc
Whether any level uses the EAMxx SHOC PBL scheme.
Definition: ERF_DataStruct.H:2787
amrex::Real sinphi
Sine of the latitude used for Coriolis forcing.
Definition: ERF_DataStruct.H:2747
bool host_owns_vertical_momentum_diffusion() const noexcept
Test whether the host owns momentum diffusion.
Definition: ERF_DataStruct.H:2873
amrex::Real hindcast_zhi_sponge_strength
Strength of the upper hindcast sponge layer.
Definition: ERF_DataStruct.H:3056
bool coriolis_3d
Whether the three-dimensional Coriolis terms are included.
Definition: ERF_DataStruct.H:2690
int force_stage1_single_substep
Flag forcing a single substep in the first RK stage.
Definition: ERF_DataStruct.H:2626
amrex::Real c_p
Specific heat at constant pressure for dry air [J/(kg-K)].
Definition: ERF_DataStruct.H:2737
bool if_implicit_drag
Definition: ERF_DataStruct.H:2703
bool implicit_momentum_diffusion
Whether implicit vertical momentum diffusion is included.
Definition: ERF_DataStruct.H:2663
bool do_mom_advection
Whether custom vertical subsidence is applied to momentum.
Definition: ERF_DataStruct.H:2754
bool if_stability_correction
Whether immersed-forcing stability corrections are enabled.
Definition: ERF_DataStruct.H:2713
amrex::Real nudging_t_z1
Definition: ERF_DataStruct.H:2770
amrex::Vector< amrex::Real > sbm_fixture_initial_state
Definition: ERF_DataStruct.H:2950
bool custom_forcing_prim_vars
Whether custom forcing operates on primitive variables.
Definition: ERF_DataStruct.H:2756
amrex::Real if_stability_wind_floor
Wind-speed floor [m/s] behind the friction velocity of the wall law's stability estimate (0: none).
Definition: ERF_DataStruct.H:2721
bool nudging_u
Definition: ERF_DataStruct.H:2775
bool nudging_from_input_sounding
Whether solution fields are nudged toward input sounding data.
Definition: ERF_DataStruct.H:2762
static void set_mesh_type(MeshType new_mesh_type)
Override the globally selected mesh type.
Definition: ERF_DataStruct.H:2611
bool use_num_diff
Whether sixth-order numerical diffusion is enabled.
Definition: ERF_DataStruct.H:2938
bool use_wrf_bdy_qc_qi
Whether WRF cloud water and active cloud ice are ingested at real boundaries.
Definition: ERF_DataStruct.H:3011
Definition: ERF_SpongeStruct.H:22
void display()
Print the configured sponge-layer options.
Definition: ERF_SpongeStruct.H:85
void init_params(std::string pp_prefix)
Read sponge-layer options from the input parameter database.
Definition: ERF_SpongeStruct.H:28
Definition: ERF_RadStruct.H:103
void read(const std::string &prefix, const RRTMGPSolarChoice &solar)
Definition: ERF_TopographicShading.cpp:39
bool active() const
Definition: ERF_RadStruct.H:113