ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TurbStruct.H
Go to the documentation of this file.
1 #ifndef ERF_TURB_STRUCT_H_
2 #define ERF_TURB_STRUCT_H_
3 
4 #include <AMReX_ParallelDescriptor.H>
5 #include <ERF_MYNNStruct.H>
6 
7 /**
8  * @brief Large-eddy simulation closure type.
9  */
10 AMREX_ENUM(LESType, None, Smagorinsky, Smagorinsky2D, Deardorff);
11 
12 /**
13  * @brief Reynolds-averaged turbulence closure type.
14  */
15 AMREX_ENUM(RANSType, None, kEqn);
16 
17 /**
18  * @brief Planetary boundary-layer closure type.
19  */
20 AMREX_ENUM(PBLType, None, MYJ, MYNN25, MYNNEDMF, YSU, YSUNew, MRF, SHOC, EAMXX_SHOC, NATIVE_SHOC);
21 
22 /**
23  * @brief Thermodynamic variable used for stability stratification.
24  */
25 AMREX_ENUM(StratType, theta, thetav, thetal);
26 
27 /**
28  * @brief Query a scalar or per-level input value.
29  * @tparam T Value type accepted by ParmParse::query.
30  * @param pp ParmParse object to query.
31  * @param query_string Input key to query.
32  * @param query_var Destination value for the requested level.
33  * @param lev AMR level index.
34  * @param maxlev Maximum AMR level configured for the run.
35  * @return ParmParse query result, or zero if the key is absent.
36  */
37 template <typename T>
38 int
40  const amrex::ParmParse& pp,
41  const char* query_string,
42  T& query_var,
43  const int lev,
44  const int maxlev)
45 {
46  int count = pp.countval(query_string);
47  if (count == 0) {
48  return 0; // nothing to do
49  } else if (count == 1) {
50  // In this case, we assume the same value is used at every level
51  return pp.query(query_string, query_var);
52  } else if (count >= maxlev + 1) {
53  // In this case, there may be more values than levels so we
54  // will just read the number of values we need and ignore the
55  // extra levels
56  return pp.query(query_string, query_var, lev);
57  } else {
58  // In this case, there is more than one value AND there
59  // are more levels than values; as an example, if max_level = 2
60  // but count = 2 so we have three levels but only two values --
61  // it is not clear how to interpret this so we abort
62  amrex::Error(
63  "For parmparse variable " + pp.prefixedName(query_string) +
64  ": if specified, specify once total or at least once for each level");
65  return 0; // avoid compiler warning
66  }
67 }
68 
69 /**
70  * @brief Query a scalar or per-level enum input value using case-insensitive matching.
71  * @tparam T Enum type accepted by ParmParse::query_enum_case_insensitive.
72  * @param pp ParmParse object to query.
73  * @param query_string Input key to query.
74  * @param query_var Destination enum value for the requested level.
75  * @param lev AMR level index.
76  * @param maxlev Maximum AMR level configured for the run.
77  * @return ParmParse query result, or zero if the key is absent.
78  */
79 template <typename T>
80 int
82  const amrex::ParmParse& pp,
83  const char* query_string,
84  T& query_var,
85  const int lev,
86  const int maxlev)
87 {
88  int count = pp.countval(query_string);
89  if (count == 0) {
90  return 0; // nothing to do
91  } else if (count == 1) {
92  // In this case, we assume the same value is used at every level
93  return pp.query_enum_case_insensitive(query_string, query_var);
94  } else if (count >= maxlev + 1) {
95  // In this case, there may be more values than levels so we
96  // will just read the number of values we need and ignore the
97  // extra levels
98  return pp.query_enum_case_insensitive(query_string, query_var, lev);
99  } else {
100  // In this case, there is more than one value AND there
101  // are more levels than values; as an example, if max_level = 2
102  // but count = 2 so we have three levels but only two values --
103  // it is not clear how to interpret this so we abort
104  amrex::Error(
105  "For parmparse variable " + pp.prefixedName(query_string) +
106  ": if specified, specify once total or at least once for each level");
107  return 0; // avoid compiler warning
108  }
109 }
110 
111 /**
112  * Container holding quantities related to turbulence parametrizations
113  */
115 {
116 public:
117  /**
118  * @brief Read turbulence options for one AMR level from the input parameter database.
119  * @param lev AMR level index.
120  * @param max_level Maximum AMR level configured for the run.
121  * @param pp_prefix ParmParse prefix for the ERF input namespace.
122  */
123  void init_params (int lev, int max_level, std::string pp_prefix)
124  {
125  amrex::ParmParse pp(pp_prefix);
126 
127  // Which LES closure?
128  query_one_or_per_level(pp, "les_type", les_type, lev, max_level);
129  // Immersed-boundary awareness of the MRF and YSUNew schemes (off by default):
130  // the surface of a column with immersed solid cells is the first fluid cell
131  // above them, heights and the boundary-layer depth are measured from it,
132  // the eddy diffusivities are zero inside the solid, and the surface
133  // scales over such a column come from a neutral log law at its top with
134  // the roughness pbl_ib_z0. Only MRF and YSUNew honour it.
135  query_one_or_per_level(pp, "pbl_ib_aware", pbl_ib_aware, lev, max_level);
136  query_one_or_per_level(pp, "pbl_ib_z0", pbl_ib_z0, lev, max_level);
137 
138  // Handle 2-D Smag
139  if (les_type == LESType::Smagorinsky2D) {
140  les_type = LESType::Smagorinsky;
141  smag2d = true;
142  }
143 
144  // Which RANS closure?
145  query_one_or_per_level(pp, "rans_type", rans_type, lev, max_level);
146 
147  if ((rans_type != RANSType::None) && (les_type != LESType::None)) {
148  amrex::Error("Hybrid RANS-LES not implemented");
149  }
150 
151  // Which PBL Closure
152  query_one_or_per_level_enum_case_insensitive(pp, "pbl_type", pbl_type, lev, max_level);
153  if (pbl_type == PBLType::SHOC) {
154  static bool warned_legacy_shoc = false;
155  if (!warned_legacy_shoc) {
156  amrex::Warning("erf.pbl_type = SHOC is deprecated; use erf.pbl_type = EAMXX_SHOC");
157  warned_legacy_shoc = true;
158  }
159  pbl_type = PBLType::EAMXX_SHOC;
160  }
161 
162  // Do some more stuff for PBL Modeling
163  if (pbl_type != PBLType::None) {
164  // Check for compatibility between PBL, LES, Molec Transport
165  if (les_type != LESType::None) {
166  amrex::Print() << "Selected a PBL model and an LES model: "
167  << "Using PBL for vertical transport, LES for horizontal"
168  << std::endl;
169  }
170  if (les_type == LESType::Smagorinsky) {
171  if (!smag2d)
172  amrex::Error("If using Smagorinsky with a PBL model, the 2-D "
173  "formulation should be used");
174  } else if (les_type == LESType::Deardorff) {
175  amrex::Error(
176  "It is not recommended to use Deardorff LES and a PBL model");
177  }
178  // Both the k-eqn RANS closure and these PBL schemes transport RhoKE
179  if ((rans_type == RANSType::kEqn) &&
180  ((pbl_type == PBLType::MYJ) || (pbl_type == PBLType::MYNN25) ||
181  (pbl_type == PBLType::MYNNEDMF) || uses_shoc_family())) {
182  amrex::Error("erf.rans_type = kEqn cannot be combined with erf.pbl_type = " +
183  std::string(amrex::getEnumNameString(pbl_type)) +
184  ": both transport the turbulent kinetic energy");
185  }
186 
187  if (pbl_type == PBLType::MYNN25 || pbl_type == PBLType::MYNNEDMF) {
188  query_one_or_per_level(pp, "pbl_mynn_A1", pbl_mynn.A1, lev, max_level);
189  query_one_or_per_level(pp, "pbl_mynn_A2", pbl_mynn.A2, lev, max_level);
190  query_one_or_per_level(pp, "pbl_mynn_B1", pbl_mynn.B1, lev, max_level);
191  query_one_or_per_level(pp, "pbl_mynn_B2", pbl_mynn.B2, lev, max_level);
192  query_one_or_per_level(pp, "pbl_mynn_C1", pbl_mynn.C1, lev, max_level);
193  query_one_or_per_level(pp, "pbl_mynn_C2", pbl_mynn.C2, lev, max_level);
194  query_one_or_per_level(pp, "pbl_mynn_C3", pbl_mynn.C3, lev, max_level);
195  query_one_or_per_level(pp, "pbl_mynn_C4", pbl_mynn.C4, lev, max_level);
196  query_one_or_per_level(pp, "pbl_mynn_C5", pbl_mynn.C5, lev, max_level);
201  pp, "pbl_mynn_diffuse_moistvars", pbl_mynn.diffuse_moistvars, lev,
202  max_level);
204  pp, "pbl_mynn_SMmin", pbl_mynn.SMmin, lev, max_level);
206  pp, "pbl_mynn_SMmax", pbl_mynn.SMmax, lev, max_level);
208  pp, "pbl_mynn_SHmin", pbl_mynn.SHmin, lev, max_level);
210  pp, "pbl_mynn_SHmax", pbl_mynn.SHmax, lev, max_level);
212  pp, "pbl_mynn_SQfactor", pbl_mynn.SQfac, lev, max_level);
213 
214  // SQ = SQfac*SM, so its bound is tied to SHmax and SQfac; the default member
215  // initializer was evaluated before either could be overridden here.
217 
218  } else if (pbl_type == PBLType::YSU || pbl_type == PBLType::YSUNew) {
220  pp, "pbl_ysu_coriolis_freq", pbl_ysu_coriolis_freq, lev, max_level);
222  pp, "pbl_ysu_use_consistent_coriolis",
223  pbl_ysu_use_consistent_coriolis, lev, max_level);
225  pp, "pbl_ysu_force_over_water", pbl_ysu_force_over_water, lev,
226  max_level);
228  pp, "pbl_ysu_land_Ribcr", pbl_ysu_land_Ribcr, lev, max_level);
230  pp, "pbl_ysu_unst_Ribcr", pbl_ysu_unst_Ribcr, lev, max_level);
232  pp, "enable_ysu_liquid_theta", enable_ysu_liquid_theta, lev, max_level);
234  pp, "enable_ysu_countergradient", enable_ysu_countergradient, lev, max_level);
236  pp, "enable_ysu_terrain_pblh_floor", enable_ysu_terrain_pblh_floor, lev, max_level);
238  pp, "enable_ysu_sat_limiter", enable_ysu_sat_limiter, lev, max_level);
240  pp, "enable_ysu_topdown", enable_ysu_topdown, lev, max_level);
242  pp, "enable_ysu_entrainment", enable_ysu_entrainment, lev, max_level);
244  pp, "enable_ysu_cloud_pblh", enable_ysu_cloud_pblh, lev, max_level);
246  pp, "enable_mrf_unbounded_vpert", enable_mrf_unbounded_vpert, lev, max_level);
248  pp, "ysu_qcloud_threshold", ysu_qcloud_threshold, lev, max_level);
250  pp, "ysu_moistvars", ysu_moistvars, lev, max_level);
252  pp, "pbl_ysunew_highres_bounds", pbl_ysunew_highres_bounds, lev, max_level);
253  // Radiative tendency limiter (YSUNew-only)
255  pp, "enable_ysu_rad_tend_limiter", enable_ysu_rad_tend_limiter, lev, max_level);
257  pp, "ysu_rad_tend_limiter_magnitude", ysu_rad_tend_limiter_magnitude, lev, max_level);
258  if (!(ysu_rad_tend_limiter_magnitude > 0.0)) {
259  if (amrex::ParallelDescriptor::IOProcessor()) {
260  amrex::Print() << "WARNING: erf.ysu_rad_tend_limiter_magnitude = "
262  << " is not positive; using the default of 1.0 K/s\n";
263  }
265  }
266  // Vogelezang & Holtslag (1996) shear correction
268  pp, "enable_vh96_shear_correction", enable_vh96_shear_correction, lev, max_level);
270  pp, "vh96_shear_const_b", vh96_shear_const_b, lev, max_level);
271  // PBLH spatial smoothing
273  pp, "enable_pblh_smoothing", enable_pblh_smoothing, lev, max_level);
275  pp, "pblh_smoothing_passes", pblh_smoothing_passes, lev, max_level);
277  pp, "pblh_smoothing_weight", pblh_smoothing_weight, lev, max_level);
278  // Validate pblh_smoothing_weight is in [0,1]
279  if (enable_pblh_smoothing && (pblh_smoothing_weight < 0 || pblh_smoothing_weight > 1)) {
280  amrex::Error("pblh_smoothing_weight must be in [0, 1]");
281  }
282  // QNSE stable stability functions (Sukoriansky et al. 2005)
284  pp, "enable_qnse_stable_functions", enable_qnse_stable_functions, lev, max_level);
286  pp, "qnse_am", qnse_am, lev, max_level);
288  pp, "qnse_bm", qnse_bm, lev, max_level);
290  pp, "qnse_ah", qnse_ah, lev, max_level);
292  pp, "qnse_bh", qnse_bh, lev, max_level);
293  } else if (pbl_type == PBLType::MRF) {
295  pp, "pbl_mrf_coriolis_freq", pbl_mrf_coriolis_freq, lev, max_level);
297  pp, "pbl_mrf_Ribcr", pbl_mrf_Ribcr, lev, max_level);
299  pp, "pbl_mrf_const_b", pbl_mrf_const_b, lev, max_level);
300  query_one_or_per_level(pp, "pbl_mrf_sf", pbl_mrf_sf, lev, max_level);
302  pp, "enable_mrf_countergradient", enable_mrf_countergradient, lev, max_level);
304  pp, "enable_mrf_cloud_adjustment", enable_mrf_cloud_adjustment, lev, max_level);
306  pp, "pbl_mrf_highres_bounds", pbl_mrf_highres_bounds, lev, max_level);
308  pp, "enable_mrf_unbounded_vpert", enable_mrf_unbounded_vpert, lev, max_level);
310  pp, "pbl_mrf_use_zero_ri_extent", pbl_mrf_use_zero_ri_extent, lev, max_level);
311  // QNSE stable stability functions (Sukoriansky et al. 2005)
313  pp, "enable_qnse_stable_functions", enable_qnse_stable_functions, lev, max_level);
315  pp, "qnse_am", qnse_am, lev, max_level);
317  pp, "qnse_bm", qnse_bm, lev, max_level);
319  pp, "qnse_ah", qnse_ah, lev, max_level);
321  pp, "qnse_bh", qnse_bh, lev, max_level);
322  // Scale-aware PBL-LES blending parameters.
324  pp, "pbl_blend_length", pbl_blend_length, lev, max_level);
326  pp, "pbl_blend_cs", pbl_blend_cs, lev, max_level);
328  pp, "pbl_blend_c_max", pbl_blend_c_max, lev, max_level);
330  pp, "pbl_blend_use_smag", pbl_blend_use_smag, lev, max_level);
331  // Vogelezang & Holtslag (1996) shear correction
333  pp, "enable_vh96_shear_correction", enable_vh96_shear_correction, lev, max_level);
335  pp, "vh96_shear_const_b", vh96_shear_const_b, lev, max_level);
336  // PBLH spatial smoothing
338  pp, "enable_pblh_smoothing", enable_pblh_smoothing, lev, max_level);
340  pp, "pblh_smoothing_passes", pblh_smoothing_passes, lev, max_level);
342  pp, "pblh_smoothing_weight", pblh_smoothing_weight, lev, max_level);
343  // Validate pblh_smoothing_weight is in [0,1]
344  if (enable_pblh_smoothing && (pblh_smoothing_weight < 0 || pblh_smoothing_weight > 1)) {
345  amrex::Error("pblh_smoothing_weight must be in [0, 1]");
346  }
347  } else if (pbl_type == PBLType::SHOC) {
348 #ifndef ERF_USE_SHOC
349  amrex::Abort("You set use_shoc to true but didn't build with SHOC; you must rebuild the executable");
350  }
351 #endif
352  if (uses_eamxx_shoc()) {
353 #ifndef ERF_USE_EAMXX_SHOC
354  amrex::Abort("PBLType::EAMXX_SHOC requested, but ERF was not built with ERF_ENABLE_EAMXX_SHOC=ON");
355 #endif
356  }
357 
359  std::string zlo_bc = "none";
360  amrex::ParmParse pp_bc("zlo");
361  pp_bc.get("type",zlo_bc);
362  if (amrex::toLower(zlo_bc) != "surface_layer") {
363  amrex::Abort("You must use the surface_layer BC at zlo with the selected PBL.");
364  }
365  }
366  }
367 
368  // Flags for QKE/TKE equation
369  if (pbl_type == PBLType::MYJ || pbl_type == PBLType::MYNN25 || pbl_type == PBLType::MYNNEDMF) {
370  // Add sources/sinks to QKE/TKE? (MYJ does this inline)
371  if (pbl_type == PBLType::MYNN25 || pbl_type == PBLType::MYNNEDMF) {
372  use_pbl_tke = true;
373  }
374  // Advect QKE/TKE?
375  query_one_or_per_level(pp, "advect_tke" , advect_tke , lev, max_level);
376  // Apply numerical diffusion to QKE/TKE?
377  query_one_or_per_level(pp, "diffuse_tke_3D", diffuse_tke_3D, lev, max_level);
378  }
379 
380  // There is a default value of 1e-6 but the user can override the value here,
381  // and in addition can add perturbational values. and in addition can add perturbational values.
382  pp.queryAdd("tke_min",tke_min);
383 
384  // LES constants...
385  query_one_or_per_level(pp, "Cs", Cs, lev, max_level);
386 
387  query_one_or_per_level(pp, "Pr_t", Pr_t, lev, max_level);
388  query_one_or_per_level(pp, "Sc_t", Sc_t, lev, max_level);
389 
390  // Compute relevant forms of diffusion parameters
391  Pr_t_inv = one / Pr_t;
392  Sc_t_inv = one / Sc_t;
393 
394  if (les_type == LESType::Deardorff) {
395  query_one_or_per_level(pp, "Ck", Ck, lev, max_level);
396  query_one_or_per_level(pp, "Ce", Ce, lev, max_level);
397  query_one_or_per_level(pp, "Ce_wall", Ce_wall, lev, max_level);
398  }
399 
400  // To quantify atmospheric stability for subgrid modeling
401  query_one_or_per_level(pp, "thermal_stratification", strat_type, lev, max_level);
402  if (strat_type == StratType::theta) {
403  amrex::Print() << "Thermal stratification based on gradient of potential temperature" << std::endl;
404  } else if (strat_type == StratType::thetav) {
405  amrex::Print() << "Thermal stratification based on gradient of virtual potential temperature" << std::endl;
406  } else if (strat_type == StratType::thetal) {
407  amrex::Print() << "Thermal stratification based on gradient of linearized liquid-water potential temperature" << std::endl;
408  }
409 
410  // k-eqn constants
411  query_one_or_per_level(pp, "Cmu0", Cmu0, lev, max_level);
412  query_one_or_per_level(pp, "Cb", Cb, lev, max_level);
413  query_one_or_per_level(pp, "Rt_crit", Rt_crit, lev, max_level);
414  query_one_or_per_level(pp, "Rt_min", Rt_min, lev, max_level);
415  query_one_or_per_level(pp, "max_geom_lscale", l_g_max, lev, max_level);
416  query_one_or_per_level(pp, "dirichlet_k", dirichlet_k, lev, max_level);
417  query_one_or_per_level(pp, "tke_floor", tke_floor, lev, max_level);
418  query_one_or_per_level(pp, "implicit_tke_dissipation", implicit_tke_dissipation, lev, max_level);
419  query_one_or_per_level(pp, "rans_consistent_diffusivities", rans_consistent_diffusivities, lev, max_level);
420  query_one_or_per_level(pp, "rans_lscale_from_pblh", rans_lscale_from_pblh, lev, max_level);
421  query_one_or_per_level(pp, "rans_lscale_min", rans_lscale_min, lev, max_level);
422  if (rans_lscale_from_pblh && !(rans_lscale_min > 0)) {
423  amrex::Error("erf.rans_lscale_min must be > 0 with erf.rans_lscale_from_pblh");
424  }
425 
426  if (rans_type == RANSType::kEqn) {
427  // The stability functions (AL01 Eqs. 31, 32) have poles near Rt = -3.6;
428  // the Burchard & Petersen smoothing maps Rt < Rt_crit onto
429  // (Rt_min, Rt_crit) and needs Rt_min < Rt_crit <= 0.
430  if (!(Cmu0 > 0)) { amrex::Error("erf.Cmu0 must be > 0 for the k-eqn RANS"); }
431  if (!(Cb > 0)) { amrex::Error("erf.Cb must be > 0 for the k-eqn RANS"); }
432  if (!(l_g_max > 0)) { amrex::Error("erf.max_geom_lscale must be > 0 for the k-eqn RANS"); }
433  if (!(Rt_crit <= 0)) { amrex::Error("erf.Rt_crit must be <= 0 for the k-eqn RANS"); }
434  if (!(Rt_min < Rt_crit)) { amrex::Error("erf.Rt_min must be < erf.Rt_crit for the k-eqn RANS"); }
435  if (!(Rt_min > amrex::Real(-3.6))) {
436  amrex::Error("erf.Rt_min must be > -3.6 for the k-eqn RANS (stability functions have poles near -3.61)");
437  }
438  }
439  if (tke_floor < 0) { amrex::Error("erf.tke_floor must be >= 0"); }
440 
441  // Common inputs (LES or RANS)
442  if (!query_one_or_per_level(pp, "sigma_k", sigma_k, lev, max_level) && rans_type == RANSType::kEqn) {
443  amrex::Print() << "Overriding default sigma_k for k-eqn RANS" << std::endl;
444  sigma_k = one;
445  };
446  query_one_or_per_level(pp, "theta_ref", theta_ref, lev, max_level);
447 
448  query_one_or_per_level(pp, "mix_isotropic", mix_isotropic, lev, max_level);
449  query_one_or_per_level(pp, "use_Ri_correction", use_Ri_correction, lev, max_level);
450  query_one_or_per_level(pp, "Ri_crit", Ri_crit, lev, max_level);
451 
452  // Set common flags
453  use_kturb =
454  ((les_type != LESType::None) || (rans_type != RANSType::None) ||
455  (pbl_type != PBLType::None));
456  use_keqn =
457  ((les_type == LESType::Deardorff) || (rans_type == RANSType::kEqn));
458  use_tke =
459  ((les_type == LESType::Deardorff) || (rans_type == RANSType::kEqn) ||
460  (pbl_type == PBLType::MYJ) || (pbl_type == PBLType::MYNN25) ||
461  (pbl_type == PBLType::MYNNEDMF) || uses_shoc_family());
462 
463  if (use_tke) {
464  query_one_or_per_level(pp, "init_tke_from_ustar", init_tke_from_ustar, lev, max_level);
465  }
466 
467  // Validate inputs
468  if (les_type == LESType::Smagorinsky) {
469  if (Cs == 0) {
470  amrex::Error("Need to specify Cs for Smagorsinky LES");
471  }
472  if (smag2d && mix_isotropic) {
473  amrex::Print() << "Turning off mix_isotropic for 2-D Smagorinsky" << std::endl;
474  mix_isotropic = false;
475  }
476  }
477  }
478 
479  /**
480  * @brief Validate turbulence options against physical boundary conditions.
481  * @param phys_bc_type Physical boundary-condition types.
482  */
483  void check_params (amrex::GpuArray<ERF_BC, AMREX_SPACEDIM*2>& phys_bc_type)
484  {
485  // BC compatibility
487  phys_bc_type[amrex::Orientation(amrex::Direction::z,amrex::Orientation::low)] != ERF_BC::surface_layer ) {
488  amrex::Abort("The selected PBL model requires MOST at lower boundary");
489  }
490  if ( (les_type == LESType::Deardorff) && (Ce_wall > 0) &&
491  (phys_bc_type[amrex::Orientation(amrex::Direction::z,amrex::Orientation::low)] != ERF_BC::surface_layer) &&
492  (phys_bc_type[amrex::Orientation(amrex::Direction::z,amrex::Orientation::low)] != ERF_BC::slip_wall) &&
493  (phys_bc_type[amrex::Orientation(amrex::Direction::z,amrex::Orientation::low)] != ERF_BC::no_slip_wall) )
494  {
495  amrex::Warning("Deardorff LES assumes wall at zlo when applying Ce_wall");
496  }
497  }
498 
499  /**
500  * @brief Print turbulence settings for one AMR level.
501  * @param lev AMR level index.
502  */
503  void display (int lev)
504  {
505  amrex::Print() << "Turbulence Settings at level " << lev << std::endl;
506 
507  if (
508  les_type == LESType::None && rans_type == RANSType::None &&
509  pbl_type == PBLType::None) {
510  amrex::Print() << " Using DNS model at level " << lev << std::endl;
511  } else if (les_type == LESType::Smagorinsky) {
512  if (smag2d) {
513  amrex::Print() << " Using 2D Smagorinsky LES model at level " << lev << std::endl;
514  } else {
515  amrex::Print() << " Using Smagorinsky LES model at level " << lev << std::endl;
516  }
517  if (use_Ri_correction) {
518  amrex::Print() << " Smagorinsky uses Richardson number correction with Ri_crit = "
519  << Ri_crit << std::endl;
520  }
521  } else if (les_type == LESType::Deardorff) {
522  amrex::Print() << " Using Deardorff LES model at level " << lev << std::endl;
523  } else if (rans_type == RANSType::kEqn) {
524  amrex::Print()
525  << " Using Axell & Liungman one-equation RANS k model at level " << lev
526  << std::endl;
527  } else if (pbl_type == PBLType::MYJ) {
528  amrex::Print() << " Using MYJ PBL model at level " << lev << std::endl;
529  } else if (pbl_type == PBLType::MYNN25) {
530  amrex::Print() << " Using MYNN2.5 PBL model at level " << lev << std::endl;
531  } else if (pbl_type == PBLType::MYNNEDMF) {
532  amrex::Print() << " Using MYNNEDMF PBL model at level " << lev << std::endl;
533  } else if (pbl_type == PBLType::YSU) {
534  amrex::Print() << " Using YSU PBL model at level " << lev << std::endl;
535  } else if (pbl_type == PBLType::YSUNew) {
536  amrex::Print() << " Using YSU PBL model at level " << lev << std::endl;
537  } else if (pbl_type == PBLType::MRF) {
538  amrex::Print() << " Using MRF PBL model at level " << lev << std::endl;
539  } else if (pbl_type == PBLType::EAMXX_SHOC) {
540  amrex::Print() << " Using EAMxx SHOC PBL model at level " << lev << std::endl;
541  } else if (pbl_type == PBLType::NATIVE_SHOC) {
542  amrex::Print() << " Using native SHOC PBL model at level " << lev << std::endl;
543  } else {
544  amrex::Error("Unknown turbulence model");
545  }
546 
547  if (les_type != LESType::None) {
548  if (les_type == LESType::Smagorinsky) {
549  amrex::Print() << " Cs : " << Cs << std::endl;
550  }
551  if (les_type == LESType::Deardorff) {
552  amrex::Print() << " Ce : " << Ce << std::endl;
553  amrex::Print() << " Ce at wall : " << Ce_wall << std::endl;
554  amrex::Print() << " Ck : " << Ck << std::endl;
555  amrex::Print() << " sigma_k : " << sigma_k << std::endl;
556 
557  // Sullivan et al 1994, Eqn 14
558  amrex::Real Cs_equiv = std::sqrt(Ck * std::sqrt(Ck / Ce));
559  amrex::Print() << " equivalent Cs : " << Cs_equiv
560  << std::endl;
561  }
562  amrex::Print() << " isotropic mixing : " << mix_isotropic
563  << std::endl;
564  }
565 
566  if (rans_type != RANSType::None) {
567  if (rans_type == RANSType::kEqn) {
568  amrex::Print() << "Cmu0 : " << Cmu0 << std::endl;
569  amrex::Print() << "sigma_k : " << sigma_k << std::endl;
570  amrex::Print() << "Cb : " << Cb << std::endl;
571  amrex::Print() << "Rt_crit : " << Rt_crit << std::endl;
572  amrex::Print() << "Rt_min : " << Rt_min << std::endl;
573  amrex::Print() << "max_geom_lscale : " << l_g_max << std::endl;
574  amrex::Print() << "dirichlet_k : " << dirichlet_k << std::endl;
575  amrex::Print() << "tke_floor : " << tke_floor << std::endl;
576  amrex::Print() << "implicit_tke_dissipation : " << implicit_tke_dissipation << std::endl;
577  amrex::Print() << "consistent diffusivities : " << rans_consistent_diffusivities << std::endl;
578  amrex::Print() << "lscale cap from PBL height : " << rans_lscale_from_pblh
579  << " (floor " << rans_lscale_min << " m)" << std::endl;
580  }
581  }
582 
583  if ((les_type == LESType::Deardorff) ||
584  (rans_type == RANSType::kEqn)) {
585  if (theta_ref > 0) {
586  amrex::Print() << " reference theta : " << theta_ref << std::endl;
587  } else {
588  amrex::Print() << " reference theta : n/a" << std::endl;
589  }
590  }
591 
592  if ((les_type != LESType::None) || (rans_type != RANSType::None)) {
593  amrex::Print() << " Pr_t : " << Pr_t << std::endl;
594  amrex::Print() << " Sc_t : " << Sc_t << std::endl;
595  }
596 
597  if (pbl_type == PBLType::MYNN25 || pbl_type == PBLType::MYNNEDMF) {
598  amrex::Print() << " pbl_mynn_A1 : " << pbl_mynn.A1 << std::endl;
599  amrex::Print() << " pbl_mynn_A2 : " << pbl_mynn.A2 << std::endl;
600  amrex::Print() << " pbl_mynn_B1 : " << pbl_mynn.B1 << std::endl;
601  amrex::Print() << " pbl_mynn_B2 : " << pbl_mynn.B2 << std::endl;
602  amrex::Print() << " pbl_mynn_C1 : " << pbl_mynn.C1 << std::endl;
603  amrex::Print() << " pbl_mynn_C2 : " << pbl_mynn.C2 << std::endl;
604  amrex::Print() << " pbl_mynn_C3 : " << pbl_mynn.C3 << std::endl;
605  amrex::Print() << " pbl_mynn_C4 : " << pbl_mynn.C4 << std::endl;
606  amrex::Print() << " pbl_mynn_C5 : " << pbl_mynn.C5 << std::endl;
607  } else if (pbl_type == PBLType::YSU || pbl_type == PBLType::YSUNew) {
608  amrex::Print() << " pbl_ysu_coriolis_freq : "
609  << pbl_ysu_coriolis_freq << std::endl;
610  amrex::Print() << " pbl_ysu_use_consistent_coriolis : "
611  << pbl_ysu_use_consistent_coriolis << std::endl;
612  amrex::Print() << " pbl_ysu_force_over_water : "
613  << pbl_ysu_force_over_water << std::endl;
614  amrex::Print() << " pbl_ysu_land_Ribcr : "
615  << pbl_ysu_land_Ribcr << std::endl;
616  amrex::Print() << " pbl_ysu_unst_Ribcr : "
617  << pbl_ysu_unst_Ribcr << std::endl;
618  amrex::Print() << " enable_ysu_liquid_theta : "
619  << enable_ysu_liquid_theta << std::endl;
620  amrex::Print() << " enable_ysu_countergradient : "
621  << enable_ysu_countergradient << std::endl;
622  amrex::Print() << " enable_ysu_terrain_pblh_floor : "
623  << enable_ysu_terrain_pblh_floor << std::endl;
624  amrex::Print() << " enable_ysu_sat_limiter : "
625  << enable_ysu_sat_limiter << std::endl;
626  amrex::Print() << " enable_ysu_topdown : "
627  << enable_ysu_topdown << std::endl;
628  amrex::Print() << " enable_ysu_entrainment : "
629  << enable_ysu_entrainment << std::endl;
630  amrex::Print() << " enable_ysu_cloud_pblh : "
631  << enable_ysu_cloud_pblh << std::endl;
632  amrex::Print() << " ysu_qcloud_threshold : "
633  << ysu_qcloud_threshold << std::endl;
634  amrex::Print() << " ysu_moistvars : "
635  << ysu_moistvars << std::endl;
636  amrex::Print() << " pbl_ysunew_highres_bounds : "
637  << pbl_ysunew_highres_bounds << std::endl;
638  // Radiative tendency limiter/smoothing (YSUNew-only)
639  amrex::Print() << " enable_ysu_rad_tend_limiter : "
640  << enable_ysu_rad_tend_limiter << std::endl;
641  amrex::Print() << " ysu_rad_tend_limiter_magnitude : "
642  << ysu_rad_tend_limiter_magnitude << std::endl;
643  amrex::Print() << " enable_vh96_shear_correction : "
644  << enable_vh96_shear_correction << std::endl;
645  amrex::Print() << " vh96_shear_const_b : "
646  << vh96_shear_const_b << std::endl;
647  amrex::Print() << " enable_pblh_smoothing : "
648  << enable_pblh_smoothing << std::endl;
649  amrex::Print() << " pblh_smoothing_passes : "
650  << pblh_smoothing_passes << std::endl;
651  amrex::Print() << " pblh_smoothing_weight : "
652  << pblh_smoothing_weight << std::endl;
653  amrex::Print() << " enable_mrf_unbounded_vpert : "
654  << enable_mrf_unbounded_vpert << std::endl;
655  amrex::Print() << " enable_qnse_stable_functions : "
656  << enable_qnse_stable_functions << std::endl;
658  amrex::Print() << " qnse_am (momentum coeff) : " << qnse_am << std::endl;
659  amrex::Print() << " qnse_bm (momentum coeff) : " << qnse_bm << std::endl;
660  amrex::Print() << " qnse_ah (heat coeff) : " << qnse_ah << std::endl;
661  amrex::Print() << " qnse_bh (heat coeff) : " << qnse_bh << std::endl;
662  }
663  } else if (pbl_type == PBLType::MRF) {
664  amrex::Print() << " pbl_mrf_coriolis_freq : " << pbl_mrf_coriolis_freq
665  << std::endl;
666  amrex::Print() << " pbl_mrf_Ribcr : " << pbl_mrf_Ribcr
667  << std::endl;
668  amrex::Print() << " pbl_mrf_const_b : " << pbl_mrf_const_b
669  << std::endl;
670  amrex::Print() << " pbl_mrf_sf : " << pbl_mrf_sf
671  << std::endl;
672  amrex::Print() << " enable_mrf_countergradient : " << enable_mrf_countergradient
673  << std::endl;
674  amrex::Print() << " enable_mrf_cloud_adjustment : " << enable_mrf_cloud_adjustment
675  << std::endl;
676  amrex::Print() << " pbl_mrf_highres_bounds : " << pbl_mrf_highres_bounds
677  << std::endl;
678  amrex::Print() << " enable_mrf_unbounded_vpert : " << enable_mrf_unbounded_vpert
679  << std::endl;
680  amrex::Print() << " pbl_mrf_use_zero_ri_extent : " << pbl_mrf_use_zero_ri_extent
681  << std::endl;
682  amrex::Print() << " pbl_blend_length : " << pbl_blend_length
683  << std::endl;
684  amrex::Print() << " pbl_blend_cs : " << pbl_blend_cs
685  << std::endl;
686  amrex::Print() << " pbl_blend_c_max : " << pbl_blend_c_max
687  << std::endl;
688  amrex::Print() << " pbl_blend_use_smag : " << pbl_blend_use_smag
689  << std::endl;
690  amrex::Print() << " enable_vh96_shear_correction : " << enable_vh96_shear_correction
691  << std::endl;
692  amrex::Print() << " vh96_shear_const_b : " << vh96_shear_const_b
693  << std::endl;
694  amrex::Print() << " enable_pblh_smoothing : " << enable_pblh_smoothing
695  << std::endl;
696  amrex::Print() << " pblh_smoothing_passes : " << pblh_smoothing_passes
697  << std::endl;
698  amrex::Print() << " pblh_smoothing_weight : " << pblh_smoothing_weight
699  << std::endl;
700  amrex::Print() << " enable_qnse_stable_functions : "
701  << enable_qnse_stable_functions << std::endl;
703  amrex::Print() << " qnse_am (momentum coeff) : " << qnse_am << std::endl;
704  amrex::Print() << " qnse_bm (momentum coeff) : " << qnse_bm << std::endl;
705  amrex::Print() << " qnse_ah (heat coeff) : " << qnse_ah << std::endl;
706  amrex::Print() << " qnse_bh (heat coeff) : " << qnse_bh << std::endl;
707  }
708  }
709  }
710 
711  // LES model
712  LESType les_type = LESType::None;
713 
714  // Turbulent Prandtl number
717 
718  // Turbulent Schmidt number
721 
722  // Smagorinsky
724  bool smag2d = false;
725 
726  // Deardorff
728  amrex::Real Ce_wall = zero; // if > 0, then set Ce to this at k=0
730 
731  // k-eqn RANS coefficients (Axell & Liungman 2001)
736  amrex::Real l_g_max = amrex::Real(30.0); // ~ kappa * (amrex::Real(0.1) * zi)
737 
738  // Deardorff or k-eqn RANS
739  // - diffusivity of tke is 1/sigma_k times the eddy viscosity
741  // - reference potential temperature used to quantify the stratification in
742  // a stable region
744  // - how to quantify stratification effects
746 
747  // Anisotropic length scales
748  bool mix_isotropic = true;
749 
750  bool use_Ri_correction = true;
752 
753  // RANS type
754  RANSType rans_type = RANSType::None;
755 
756  bool dirichlet_k = false;
757 
758  // Runtime floor on the turbulent kinetic energy [m2/s2] in the k-equation
759  // update and the RANS closure; 0 means machine epsilon on rho*k (the
760  // historical floor). Distinct from tke_min, the initial TKE value.
762 
763  // Treat the TKE dissipation implicitly: eps = (Cmu0^3 sqrt(k_old)/L) * k_new
764  // folded into the update (Deardorff and k-eqn RANS)
766 
767  // k-eqn RANS: horizontal heat, scalar and moisture diffusivities follow the
768  // AL01 scalar stability function (cmu') like the vertical heat diffusivity,
769  // instead of Pr_t and Sc_t times the eddy viscosity
771 
772  // k-eqn RANS: cap the geometric length at kappa * 0.1 * PBL height (from
773  // erf.most.pblh_calc), clamped to [rans_lscale_min, max_geom_lscale],
774  // instead of the fixed max_geom_lscale
775  bool rans_lscale_from_pblh = false;
777 
778  // PBL model
779  PBLType pbl_type = PBLType::None;
780 
781  /**
782  * @brief Query whether this level uses the EAMxx SHOC PBL scheme.
783  * @return True if PBLType::EAMXX_SHOC is selected.
784  */
785  bool uses_eamxx_shoc () const noexcept
786  {
787  return pbl_type == PBLType::EAMXX_SHOC;
788  }
789 
790  /**
791  * @brief Query whether this level uses the native SHOC PBL scheme.
792  * @return True if PBLType::NATIVE_SHOC is selected.
793  */
794  bool uses_native_shoc () const noexcept
795  {
796  return pbl_type == PBLType::NATIVE_SHOC;
797  }
798 
799  /**
800  * @brief Query whether this level uses any SHOC-family PBL scheme.
801  * @return True if native or EAMxx SHOC is selected.
802  */
803  bool uses_shoc_family () const noexcept
804  {
805  return uses_eamxx_shoc() || uses_native_shoc();
806  }
807 
808  /**
809  * @brief Query whether the selected PBL scheme requires surface-layer boundary conditions.
810  * @return True if the PBL scheme requires a lower surface-layer boundary.
811  */
812  bool pbl_requires_surface_layer () const noexcept
813  {
814  return (pbl_type == PBLType::MYNN25) ||
815  (pbl_type == PBLType::MYNNEDMF) ||
816  (pbl_type == PBLType::YSU) ||
817  (pbl_type == PBLType::YSUNew) ||
818  (pbl_type == PBLType::MRF) ||
820  }
821 
822  /**
823  * @brief Query whether the selected PBL scheme suppresses microphysics condensation.
824  * @return True when the PBL scheme owns condensation handling.
825  */
827  {
828  return uses_shoc_family();
829  }
830 
832  MYNNLevel2 pbl_mynn_level2; // limiting in the decaying turbulence regime
833 
834  // Common Flags
835  bool use_kturb = false; // Any turbulence modeling?
836  bool use_keqn =
837  false; // Any microscale turbulence modeling (LES, RANS) with TKE closure?
838  // Then need to populate SmnSmn_lev for production term.
839  bool use_pbl_tke =
840  false; // Any mesoscale turbulence modeling (PBL) with TKE closure?
841  bool use_tke = false; // Any TKE closure (meso or microscale)?
842 
843  // Initialize TKE/QKE with linear profiles whose surface values are a
844  // function of the friction velocity calculated by the surface layer scheme
845  // (e.g., as done in MYNN-EDMF)
846  bool init_tke_from_ustar = false;
847 
848  // This is the value of tke_min in WRF 4.5
850 
851  // Model coefficients - YSU
852  // TODO: Add parmparse for all of these above
854  amrex::Real(1.0e-4); // 1e-4 is hardcoded in WRF, we let the user specify or take the
855  // value from ERF coriolis forcing
857  false; // ignore input pbl_ysu_coriolis_freq, take value from ERF coriolis
858  // forcing instead
860  false; // Force YSU to act as if it is over water regardless of other inputs
861  // (for testing)
863  fourth; // Critical Bulk Richardson number of Land for stable conditions
865  zero; // Critical Bulk Richardson number for unstable conditions
867  true; // if true, use liquid-water virtual potential temperature in YSU stability
869  true; // if true, apply YSU-style HGAMT/HGAMQ/HGAMU/HGAMV countergradient correction
871  true; // if true, apply terrain-following floor on PBL height
873  false; // if true, apply saturation limiter to moisture countergradient
875  true; // if true, enable top-down mixing for cloud-topped boundary layers
877  true; // if true, enable entrainment layer parameterization
879  true; // if true, enable cloud-based PBL height detection
881  amrex::Real(1.0e-4); // Cloud liquid water threshold for YSUNew (kg/kg)
882  bool ysu_moistvars = false; // if true, adds turbulence to moisture
883  bool pbl_ysunew_highres_bounds = false; // if true, apply high-resolution grid-dependent diffusivity bounds
884  // Radiative tendency limiter (YSUNew-only)
885  bool enable_ysu_rad_tend_limiter = false; // if true, guard and bound the qheating_rates top-down forcing
886  amrex::Real ysu_rad_tend_limiter_magnitude = amrex::Real(1.0); // Bound on the limited radiative tendency (K/s)
887  // Vogelezang & Holtslag (1996) shear correction and PBLH smoothing for YSU/YSUNew
888  bool enable_vh96_shear_correction = false; // if true, use VH96 shear correction term instead of ad-hoc floor
889  amrex::Real vh96_shear_const_b = amrex::Real(100.0); // VH96 shear correction constant b
890  bool enable_pblh_smoothing = false; // if true, apply spatial smoothing to diagnosed PBLH
891  int pblh_smoothing_passes = 1; // number of smoothing iterations
892  amrex::Real pblh_smoothing_weight = amrex::Real(0.5); // center-cell weight in smoothing stencil
893  // Model coefficients - MRF
895  amrex::Real pbl_mrf_Ribcr = amrex::Real(0.5); // Critical Bulk Richardson number for MRF PBL model
896  amrex::Real pbl_mrf_const_b = amrex::Real(7.8); // Constant b in MRF PBL model, used to compute the PBL height
897  amrex::Real pbl_mrf_sf = amrex::Real(0.1); // MRF surface flux, used to compute the PBL height
898  // Scale-aware PBL-LES blending parameters.
899  // Boutle et al. (2014): https://doi.org/10.1175/MWR-D-13-00229.1
900  // Set pbl_blend_length = 0 to disable (default). Recovers original behaviour.
901  amrex::Real pbl_blend_length = amrex::Real(0.0); ///< Boutle blending length L [m]. 0 = off.
902  amrex::Real pbl_blend_cs = amrex::Real(0.17); ///< Smagorinsky coeff for K_h ceiling.
903  amrex::Real pbl_blend_c_max = amrex::Real(0.1); ///< Power-law ceiling coeff [m^(2/3)/s].
904  bool pbl_blend_use_smag = true; ///< Use Smagorinsky ceiling (else power-law).
905  bool enable_mrf_countergradient = false; // if true, apply WRF-style HGAMT/HGAMQ correction to PBL height
906  bool pbl_ib_aware = false; // MRF / YSUNew: measure heights from the top of immersed solid columns
907  amrex::Real pbl_ib_z0 = amrex::Real(0.01); // roughness [m] of the neutral log law at the top of an immersed column
908  bool enable_mrf_cloud_adjustment = false; // if true, apply cloud-aware stability function adjustments in MRF scheme
909  bool pbl_mrf_highres_bounds = false; // if true, apply high-resolution grid-dependent diffusivity bounds instead of global forecast bounds
910  bool enable_mrf_unbounded_vpert = false; // if true, does not limit VPERT to GAMCRT
911  bool pbl_mrf_use_zero_ri_extent = false; // if true, use Ri=0 (pbli_zero_arr) for K-profile extent; default false uses Ri=0.5 corrector (pbli_arr) for WRF compatibility
912  // QNSE (Quasi-Normal Scale Elimination) spectral stable stability functions
913  // Sukoriansky, S., B. Galperin, and V. Perov, 2005: Application of a New Spectral
914  // Theory of Stably Stratified Turbulence to the Atmospheric Boundary Layer over
915  // Sea Ice. Boundary-Layer Meteorology, 117, 231–257.
916  // https://doi.org/10.1007/s10546-004-6848-4
917  bool enable_qnse_stable_functions = false; // if true, use QNSE spectral stability functions
918  // instead of linear Businger-Dyer form for stable conditions
919  amrex::Real qnse_am = amrex::Real(2.5); // QNSE momentum stability function coefficient (dimensionless)
920  amrex::Real qnse_bm = amrex::Real(0.2); // QNSE momentum stability function coefficient (dimensionless)
921  amrex::Real qnse_ah = amrex::Real(2.5); // QNSE heat stability function coefficient (dimensionless)
922  amrex::Real qnse_bh = amrex::Real(0.2); // QNSE heat stability function coefficient (dimensionless)
923  // MYNN2.5 PBL model
924  // TKE/QKE stuff
925  bool advect_tke = true; // if MYNN2.5 PBL is used default is turb transport in
926  // Z-direction only
927  bool diffuse_tke_3D = true; // if numerical diffusion is turned on
928 };
929 
930 /**
931  * @var TurbChoice::les_type
932  * @brief Selected LES closure.
933  * @var TurbChoice::Pr_t
934  * @brief Turbulent Prandtl number.
935  * @var TurbChoice::Pr_t_inv
936  * @brief Inverse turbulent Prandtl number.
937  * @var TurbChoice::Sc_t
938  * @brief Turbulent Schmidt number.
939  * @var TurbChoice::Sc_t_inv
940  * @brief Inverse turbulent Schmidt number.
941  * @var TurbChoice::Cs
942  * @brief Smagorinsky model coefficient.
943  * @var TurbChoice::smag2d
944  * @brief Whether the 2-D Smagorinsky formulation is used.
945  * @var TurbChoice::Ce
946  * @brief Deardorff dissipation coefficient.
947  * @var TurbChoice::Ce_wall
948  * @brief Wall value for the Deardorff dissipation coefficient.
949  * @var TurbChoice::Ck
950  * @brief Deardorff eddy-viscosity coefficient.
951  * @var TurbChoice::Cmu0
952  * @brief One-equation RANS Cmu0 coefficient.
953  * @var TurbChoice::Cb
954  * @brief One-equation RANS buoyancy coefficient.
955  * @var TurbChoice::Rt_crit
956  * @brief Critical turbulent Reynolds number.
957  * @var TurbChoice::Rt_min
958  * @brief Minimum turbulent Reynolds number.
959  * @var TurbChoice::l_g_max
960  * @brief Maximum geometric length scale.
961  * @var TurbChoice::sigma_k
962  * @brief TKE diffusivity coefficient denominator.
963  * @var TurbChoice::theta_ref
964  * @brief Reference potential temperature for stable stratification.
965  * @var TurbChoice::strat_type
966  * @brief Thermodynamic variable used for stability stratification.
967  * @var TurbChoice::mix_isotropic
968  * @brief Whether subgrid mixing uses isotropic length scales.
969  * @var TurbChoice::use_Ri_correction
970  * @brief Whether Richardson-number correction is applied.
971  * @var TurbChoice::Ri_crit
972  * @brief Critical Richardson number for stability correction.
973  * @var TurbChoice::rans_type
974  * @brief Selected RANS closure.
975  * @var TurbChoice::dirichlet_k
976  * @brief Whether TKE uses Dirichlet boundary treatment.
977  * @var TurbChoice::pbl_type
978  * @brief Selected PBL closure.
979  * @var TurbChoice::pbl_mynn
980  * @brief MYNN level-2.5 closure coefficients.
981  * @var TurbChoice::pbl_mynn_level2
982  * @brief MYNN level-2 closure coefficients for limiting.
983  * @var TurbChoice::use_kturb
984  * @brief Whether any turbulence model is active.
985  * @var TurbChoice::use_keqn
986  * @brief Whether a microscale TKE closure is active.
987  * @var TurbChoice::use_pbl_tke
988  * @brief Whether a mesoscale PBL TKE closure is active.
989  * @var TurbChoice::use_tke
990  * @brief Whether any TKE or QKE closure is active.
991  * @var TurbChoice::init_tke_from_ustar
992  * @brief Whether initial TKE/QKE profiles are based on surface friction velocity.
993  * @var TurbChoice::tke_min
994  * @brief Minimum TKE/QKE value.
995  * @var TurbChoice::pbl_ysu_coriolis_freq
996  * @brief Coriolis frequency used by YSU-family PBL schemes.
997  * @var TurbChoice::pbl_ysu_use_consistent_coriolis
998  * @brief Whether YSU uses the ERF Coriolis frequency.
999  * @var TurbChoice::pbl_ysu_force_over_water
1000  * @brief Whether YSU is forced to use over-water behavior.
1001  * @var TurbChoice::pbl_ysu_land_Ribcr
1002  * @brief Critical bulk Richardson number over land for stable YSU conditions.
1003  * @var TurbChoice::pbl_ysu_unst_Ribcr
1004  * @brief Critical bulk Richardson number for unstable YSU conditions.
1005  * @var TurbChoice::enable_ysu_liquid_theta
1006  * @brief Whether YSU uses liquid-water virtual potential temperature for stability.
1007  * @var TurbChoice::enable_ysu_countergradient
1008  * @brief Whether YSU countergradient corrections are enabled.
1009  * @var TurbChoice::enable_ysu_terrain_pblh_floor
1010  * @brief Whether YSU applies a terrain-following PBL-height floor.
1011  * @var TurbChoice::enable_ysu_sat_limiter
1012  * @brief Whether YSU applies a saturation limiter to moisture countergradient terms.
1013  * @var TurbChoice::enable_ysu_topdown
1014  * @brief Whether YSU top-down mixing is enabled.
1015  * @var TurbChoice::enable_ysu_entrainment
1016  * @brief Whether YSU entrainment-layer parameterization is enabled.
1017  * @var TurbChoice::enable_ysu_cloud_pblh
1018  * @brief Whether YSU cloud-based PBL-height detection is enabled.
1019  * @var TurbChoice::ysu_qcloud_threshold
1020  * @brief Cloud liquid water threshold for YSUNew [kg/kg].
1021  * @var TurbChoice::ysu_moistvars
1022  * @brief Whether YSU applies turbulence to moisture variables.
1023  * @var TurbChoice::pbl_ysunew_highres_bounds
1024  * @brief Whether YSUNew applies high-resolution grid-dependent diffusivity bounds.
1025  * @var TurbChoice::enable_vh96_shear_correction
1026  * @brief Whether Vogelezang & Holtslag (1996) shear-correction term is enabled.
1027  * @var TurbChoice::vh96_shear_const_b
1028  * @brief Vogelezang & Holtslag (1996) shear-correction constant b.
1029  * @var TurbChoice::enable_pblh_smoothing
1030  * @brief Whether spatial smoothing of diagnosed PBLH is enabled.
1031  * @var TurbChoice::pblh_smoothing_passes
1032  * @brief Number of PBLH smoothing iterations to apply.
1033  * @var TurbChoice::pblh_smoothing_weight
1034  * @brief Center-cell weight in PBLH smoothing stencil (must be in [0,1]).
1035  * @var TurbChoice::pbl_mrf_coriolis_freq
1036  * @brief Coriolis frequency used by the MRF PBL scheme.
1037  * @var TurbChoice::pbl_mrf_Ribcr
1038  * @brief Critical bulk Richardson number for the MRF PBL scheme.
1039  * @var TurbChoice::pbl_mrf_const_b
1040  * @brief MRF constant used to compute PBL height.
1041  * @var TurbChoice::pbl_mrf_sf
1042  * @brief MRF surface flux value used to compute PBL height.
1043  * @var TurbChoice::enable_mrf_countergradient
1044  * @brief Whether MRF countergradient corrections are enabled.
1045  * @var TurbChoice::pbl_ib_aware
1046  * When true, the MRF and YSUNew schemes treat the first fluid cell above the
1047  * immersed solid of a column (buildings or terrain by immersed forcing) as
1048  * that column's surface: the bulk Richardson heights, the boundary-layer
1049  * depth and the K profile are measured from it, the diffusivities vanish
1050  * inside the solid, and the surface scales of such a column are a neutral
1051  * log law at its top (u* from the wind there and pbl_ib_z0, theta* = 0). The
1052  * boundary-layer height both schemes store (pblh, Lturb) stays the absolute
1053  * height above the domain bottom; with enable_pblh_smoothing the stencil runs
1054  * on that absolute height, and the smoothed value is kept at or above each
1055  * column's own floor (half a cell above its surface, at least 10 m). Off by
1056  * default; without immersed cells the results are identical. Not supported
1057  * with terrain-fitted coordinates.
1058  * @var TurbChoice::pbl_ib_z0
1059  * Roughness length [m] of that log law.
1060  * @var TurbChoice::enable_mrf_cloud_adjustment
1061  * @brief Whether MRF cloud-aware stability adjustments are enabled.
1062  * @var TurbChoice::pbl_mrf_highres_bounds
1063  * @brief Whether MRF applies high-resolution grid-dependent diffusivity bounds.
1064  * @var TurbChoice::enable_mrf_unbounded_vpert
1065  * @brief Whether MRF leaves VPERT unlimited by GAMCRT.
1066  * @var TurbChoice::pbl_mrf_use_zero_ri_extent
1067  * @brief Whether MRF uses the Ri=0 K-profile extent.
1068  * @var TurbChoice::advect_tke
1069  * @brief Whether TKE/QKE is advected.
1070  * @var TurbChoice::diffuse_tke_3D
1071  * @brief Whether three-dimensional numerical diffusion is applied to TKE/QKE.
1072  */
1073 #endif
@ no_slip_wall
@ surface_layer
ParmParse pp("prob")
constexpr amrex::Real three
Definition: ERF_NumericalConstants.H:32
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:35
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_ENUM(LESType, None, Smagorinsky, Smagorinsky2D, Deardorff)
Large-eddy simulation closure type.
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
int query_one_or_per_level(const amrex::ParmParse &pp, const char *query_string, T &query_var, const int lev, const int maxlev)
Query a scalar or per-level input value.
Definition: ERF_TurbStruct.H:39
@ theta
Definition: ERF_SLM.H:19
@ T
Definition: ERF_IndexDefines.H:128
Coefficients and stability functions for the Level 2.5 MYNN closure.
Definition: ERF_MYNNStruct.H:14
amrex::Real SMmax
Definition: ERF_MYNNStruct.H:61
amrex::Real SHmax
Definition: ERF_MYNNStruct.H:63
amrex::Real SQfac
Definition: ERF_MYNNStruct.H:57
amrex::Real C4
Definition: ERF_MYNNStruct.H:53
amrex::Real C1
Definition: ERF_MYNNStruct.H:50
amrex::Real C3
Definition: ERF_MYNNStruct.H:52
amrex::Real C2
Definition: ERF_MYNNStruct.H:51
amrex::Real A2
Definition: ERF_MYNNStruct.H:47
amrex::Real SHmin
Definition: ERF_MYNNStruct.H:62
amrex::Real B1
Definition: ERF_MYNNStruct.H:48
amrex::Real B2
Definition: ERF_MYNNStruct.H:49
amrex::Real C5
Definition: ERF_MYNNStruct.H:54
amrex::Real SMmin
Definition: ERF_MYNNStruct.H:60
amrex::Real A1
Definition: ERF_MYNNStruct.H:46
bool diffuse_moistvars
Definition: ERF_MYNNStruct.H:70
amrex::Real SQmax
Definition: ERF_MYNNStruct.H:65
Coefficients and stability functions for the Level 2 MYNN closure.
Definition: ERF_MYNNStruct.H:76
void init_coeffs(amrex::Real A1_lvl25, amrex::Real A2_lvl25, amrex::Real B1, amrex::Real B2, amrex::Real C1, amrex::Real C2, amrex::Real C3, amrex::Real, amrex::Real C5)
Initialize Level 2 closure coefficients from Level 2.5 coefficients.
Definition: ERF_MYNNStruct.H:88
Definition: ERF_TurbStruct.H:115
bool advect_tke
Whether TKE/QKE is advected.
Definition: ERF_TurbStruct.H:925
bool pbl_ysu_force_over_water
Whether YSU is forced to use over-water behavior.
Definition: ERF_TurbStruct.H:859
bool implicit_tke_dissipation
Definition: ERF_TurbStruct.H:765
StratType strat_type
Thermodynamic variable used for stability stratification.
Definition: ERF_TurbStruct.H:745
bool rans_consistent_diffusivities
Definition: ERF_TurbStruct.H:770
amrex::Real Ce
Deardorff dissipation coefficient.
Definition: ERF_TurbStruct.H:727
bool enable_mrf_unbounded_vpert
Whether MRF leaves VPERT unlimited by GAMCRT.
Definition: ERF_TurbStruct.H:910
amrex::Real Sc_t
Turbulent Schmidt number.
Definition: ERF_TurbStruct.H:719
MYNNLevel2 pbl_mynn_level2
MYNN level-2 closure coefficients for limiting.
Definition: ERF_TurbStruct.H:832
bool diffuse_tke_3D
Whether three-dimensional numerical diffusion is applied to TKE/QKE.
Definition: ERF_TurbStruct.H:927
bool use_tke
Whether any TKE or QKE closure is active.
Definition: ERF_TurbStruct.H:841
amrex::Real sigma_k
TKE diffusivity coefficient denominator.
Definition: ERF_TurbStruct.H:740
bool use_Ri_correction
Whether Richardson-number correction is applied.
Definition: ERF_TurbStruct.H:750
bool pbl_ysu_use_consistent_coriolis
Whether YSU uses the ERF Coriolis frequency.
Definition: ERF_TurbStruct.H:856
amrex::Real pbl_blend_length
Boutle blending length L [m]. 0 = off.
Definition: ERF_TurbStruct.H:901
amrex::Real Ck
Deardorff eddy-viscosity coefficient.
Definition: ERF_TurbStruct.H:729
bool pbl_mrf_use_zero_ri_extent
Whether MRF uses the Ri=0 K-profile extent.
Definition: ERF_TurbStruct.H:911
amrex::Real Ce_wall
Wall value for the Deardorff dissipation coefficient.
Definition: ERF_TurbStruct.H:728
amrex::Real qnse_ah
Definition: ERF_TurbStruct.H:921
RANSType rans_type
Selected RANS closure.
Definition: ERF_TurbStruct.H:754
void check_params(amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type)
Validate turbulence options against physical boundary conditions.
Definition: ERF_TurbStruct.H:483
bool enable_vh96_shear_correction
Whether Vogelezang & Holtslag (1996) shear-correction term is enabled.
Definition: ERF_TurbStruct.H:888
amrex::Real theta_ref
Reference potential temperature for stable stratification.
Definition: ERF_TurbStruct.H:743
amrex::Real Sc_t_inv
Inverse turbulent Schmidt number.
Definition: ERF_TurbStruct.H:720
amrex::Real l_g_max
Maximum geometric length scale.
Definition: ERF_TurbStruct.H:736
bool enable_mrf_cloud_adjustment
Whether MRF cloud-aware stability adjustments are enabled.
Definition: ERF_TurbStruct.H:908
bool use_keqn
Whether a microscale TKE closure is active.
Definition: ERF_TurbStruct.H:836
bool uses_eamxx_shoc() const noexcept
Query whether this level uses the EAMxx SHOC PBL scheme.
Definition: ERF_TurbStruct.H:785
bool enable_ysu_entrainment
Whether YSU entrainment-layer parameterization is enabled.
Definition: ERF_TurbStruct.H:876
bool enable_ysu_sat_limiter
Whether YSU applies a saturation limiter to moisture countergradient terms.
Definition: ERF_TurbStruct.H:872
bool enable_ysu_cloud_pblh
Whether YSU cloud-based PBL-height detection is enabled.
Definition: ERF_TurbStruct.H:878
amrex::Real pbl_mrf_const_b
MRF constant used to compute PBL height.
Definition: ERF_TurbStruct.H:896
amrex::Real pblh_smoothing_weight
Center-cell weight in PBLH smoothing stencil (must be in [0,1]).
Definition: ERF_TurbStruct.H:892
amrex::Real pbl_blend_cs
Smagorinsky coeff for K_h ceiling.
Definition: ERF_TurbStruct.H:902
amrex::Real tke_floor
Definition: ERF_TurbStruct.H:761
bool uses_shoc_family() const noexcept
Query whether this level uses any SHOC-family PBL scheme.
Definition: ERF_TurbStruct.H:803
amrex::Real ysu_qcloud_threshold
Cloud liquid water threshold for YSUNew [kg/kg].
Definition: ERF_TurbStruct.H:880
amrex::Real qnse_bm
Definition: ERF_TurbStruct.H:920
amrex::Real pbl_blend_c_max
Power-law ceiling coeff [m^(2/3)/s].
Definition: ERF_TurbStruct.H:903
bool init_tke_from_ustar
Whether initial TKE/QKE profiles are based on surface friction velocity.
Definition: ERF_TurbStruct.H:846
amrex::Real qnse_bh
Definition: ERF_TurbStruct.H:922
bool enable_qnse_stable_functions
Definition: ERF_TurbStruct.H:917
bool enable_mrf_countergradient
Whether MRF countergradient corrections are enabled.
Definition: ERF_TurbStruct.H:905
bool enable_ysu_countergradient
Whether YSU countergradient corrections are enabled.
Definition: ERF_TurbStruct.H:868
bool mix_isotropic
Whether subgrid mixing uses isotropic length scales.
Definition: ERF_TurbStruct.H:748
bool enable_pblh_smoothing
Whether spatial smoothing of diagnosed PBLH is enabled.
Definition: ERF_TurbStruct.H:890
bool uses_native_shoc() const noexcept
Query whether this level uses the native SHOC PBL scheme.
Definition: ERF_TurbStruct.H:794
bool dirichlet_k
Whether TKE uses Dirichlet boundary treatment.
Definition: ERF_TurbStruct.H:756
int pblh_smoothing_passes
Number of PBLH smoothing iterations to apply.
Definition: ERF_TurbStruct.H:891
LESType les_type
Selected LES closure.
Definition: ERF_TurbStruct.H:712
void init_params(int lev, int max_level, std::string pp_prefix)
Read turbulence options for one AMR level from the input parameter database.
Definition: ERF_TurbStruct.H:123
amrex::Real pbl_ib_z0
Definition: ERF_TurbStruct.H:907
bool enable_ysu_topdown
Whether YSU top-down mixing is enabled.
Definition: ERF_TurbStruct.H:874
bool rans_lscale_from_pblh
Definition: ERF_TurbStruct.H:775
bool pbl_ib_aware
Definition: ERF_TurbStruct.H:906
amrex::Real ysu_rad_tend_limiter_magnitude
Definition: ERF_TurbStruct.H:886
amrex::Real rans_lscale_min
Definition: ERF_TurbStruct.H:776
bool pbl_mrf_highres_bounds
Whether MRF applies high-resolution grid-dependent diffusivity bounds.
Definition: ERF_TurbStruct.H:909
amrex::Real Rt_min
Minimum turbulent Reynolds number.
Definition: ERF_TurbStruct.H:735
bool pbl_blend_use_smag
Use Smagorinsky ceiling (else power-law).
Definition: ERF_TurbStruct.H:904
MYNNLevel25 pbl_mynn
MYNN level-2.5 closure coefficients.
Definition: ERF_TurbStruct.H:831
amrex::Real pbl_ysu_land_Ribcr
Critical bulk Richardson number over land for stable YSU conditions.
Definition: ERF_TurbStruct.H:862
amrex::Real Cb
One-equation RANS buoyancy coefficient.
Definition: ERF_TurbStruct.H:733
amrex::Real Ri_crit
Critical Richardson number for stability correction.
Definition: ERF_TurbStruct.H:751
amrex::Real pbl_ysu_unst_Ribcr
Critical bulk Richardson number for unstable YSU conditions.
Definition: ERF_TurbStruct.H:864
amrex::Real Rt_crit
Critical turbulent Reynolds number.
Definition: ERF_TurbStruct.H:734
amrex::Real vh96_shear_const_b
Vogelezang & Holtslag (1996) shear-correction constant b.
Definition: ERF_TurbStruct.H:889
bool pbl_suppresses_microphysics_condensation() const noexcept
Query whether the selected PBL scheme suppresses microphysics condensation.
Definition: ERF_TurbStruct.H:826
amrex::Real pbl_mrf_Ribcr
Critical bulk Richardson number for the MRF PBL scheme.
Definition: ERF_TurbStruct.H:895
amrex::Real qnse_am
Definition: ERF_TurbStruct.H:919
amrex::Real pbl_mrf_sf
MRF surface flux value used to compute PBL height.
Definition: ERF_TurbStruct.H:897
amrex::Real Cmu0
One-equation RANS Cmu0 coefficient.
Definition: ERF_TurbStruct.H:732
amrex::Real Cs
Smagorinsky model coefficient.
Definition: ERF_TurbStruct.H:723
amrex::Real Pr_t
Turbulent Prandtl number.
Definition: ERF_TurbStruct.H:715
amrex::Real pbl_mrf_coriolis_freq
Coriolis frequency used by the MRF PBL scheme.
Definition: ERF_TurbStruct.H:894
bool pbl_ysunew_highres_bounds
Whether YSUNew applies high-resolution grid-dependent diffusivity bounds.
Definition: ERF_TurbStruct.H:883
amrex::Real tke_min
Minimum TKE/QKE value.
Definition: ERF_TurbStruct.H:849
amrex::Real pbl_ysu_coriolis_freq
Coriolis frequency used by YSU-family PBL schemes.
Definition: ERF_TurbStruct.H:853
void display(int lev)
Print turbulence settings for one AMR level.
Definition: ERF_TurbStruct.H:503
bool use_kturb
Whether any turbulence model is active.
Definition: ERF_TurbStruct.H:835
bool ysu_moistvars
Whether YSU applies turbulence to moisture variables.
Definition: ERF_TurbStruct.H:882
PBLType pbl_type
Selected PBL closure.
Definition: ERF_TurbStruct.H:779
amrex::Real Pr_t_inv
Inverse turbulent Prandtl number.
Definition: ERF_TurbStruct.H:716
bool enable_ysu_terrain_pblh_floor
Whether YSU applies a terrain-following PBL-height floor.
Definition: ERF_TurbStruct.H:870
bool enable_ysu_liquid_theta
Whether YSU uses liquid-water virtual potential temperature for stability.
Definition: ERF_TurbStruct.H:866
bool smag2d
Whether the 2-D Smagorinsky formulation is used.
Definition: ERF_TurbStruct.H:724
bool enable_ysu_rad_tend_limiter
Definition: ERF_TurbStruct.H:885
bool pbl_requires_surface_layer() const noexcept
Query whether the selected PBL scheme requires surface-layer boundary conditions.
Definition: ERF_TurbStruct.H:812
bool use_pbl_tke
Whether a mesoscale PBL TKE closure is active.
Definition: ERF_TurbStruct.H:839