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