ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_CloudChamber.H
Go to the documentation of this file.
1 #ifndef ERF_CLOUD_CHAMBER_H_
2 #define ERF_CLOUD_CHAMBER_H_
3 
4 #include <AMReX_Array.H>
5 #include <AMReX_ParmParse.H>
6 #include <AMReX_REAL.H>
7 
8 #include "ERF_Constants.H"
9 #include "ERF_EOS.H"
10 #include "ERF_MicrophysicsUtils.H"
12 
13 #include <cmath>
14 #include <limits>
15 #include <string>
16 #include <vector>
17 
18 namespace erf_cloud_chamber {
19 
20 /**
21  * Stage 1 wall metadata. This is deliberately a small, trivially copyable
22  * description: it can be captured by an AMReX device kernel without virtual
23  * dispatch. PrescribedState is the only active treatment. A future wall
24  * factory can consume the face identity and return momentum, thermal-scalar,
25  * and vapor fluxes without changing the chamber initializer.
26  */
27 enum class WallFace : unsigned char { XLo, XHi, YLo, YHi, ZLo, ZHi };
28 
29 /**
30  * @brief Metadata describing a chamber wall.
31  */
32 struct WallSpec {
35  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> velocity = {};
36 };
37 
38 /**
39  * @brief Configuration parameters for cloud chamber initialization.
40  */
41 struct Config {
42  bool active = false;
43  bool cloudy = false;
49  amrex::Real theta_bottom = amrex::Real(0.0); // legacy_theta_qv only
50  amrex::Real theta_top = amrex::Real(0.0); // legacy_theta_qv only
52  amrex::Real qv_bottom = amrex::Real(0.0); // legacy_theta_qv only
53  amrex::Real qv_top = amrex::Real(0.0); // legacy_theta_qv only
54  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> prob_lo = {};
55  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> prob_hi = {};
56  amrex::GpuArray<WallSpec, 2 * AMREX_SPACEDIM> walls = {};
57 
58  /**
59  * @brief Collect thermodynamic boundary conditions for all chamber walls.
60  * @return Boundary array containing thermodynamics for each face.
61  */
63  {
65  for (int n = 0; n < 2 * AMREX_SPACEDIM; ++n) {
66  boundary[n] = walls[n].thermodynamics;
67  }
68  return boundary;
69  }
70 };
71 
72 enum class InitializationMode : unsigned char {
75 };
76 
77 /**
78  * @brief Contract used to validate initialization parameter consistency.
79  */
82  bool cloudy = false;
86 };
87 
88 /**
89  * @brief Validate the initialization contract and return an error message if violated.
90  * @param[in] contract The contract to validate.
91  * @return Error message string, empty if valid.
92  */
93 inline std::string
95 {
96  switch (contract.mode) {
98  if (contract.cloudy) {
99  if (!contract.has_initial_relative_humidity) {
100  return "Cloud Chamber: physical SatAdj initialization requires prob.initial_relative_humidity";
101  }
102  } else if (contract.has_initial_relative_humidity) {
103  return "Cloud Chamber: prob.initial_relative_humidity is only used with erf.moisture_model = SatAdj";
104  }
105  if (contract.has_legacy_profile_keys) {
106  return "Cloud Chamber: physical_temperature_rh cannot be combined with legacy theta/qv profile keys";
107  }
108  break;
110  if (contract.has_physical_profile_keys) {
111  return "Cloud Chamber: legacy_theta_qv cannot be combined with physical temperature/RH profile keys";
112  }
113  break;
114  }
115 
116  return {};
117 }
118 
119 /**
120  * @brief Compute a linear profile between two values.
121  * @param[in] bottom Value at the lower boundary.
122  * @param[in] top Value at the upper boundary.
123  * @param[in] coordinate Current coordinate.
124  * @param[in] lower Lower boundary coordinate.
125  * @param[in] length Length of the domain.
126  * @return Interpolated value.
127  */
128 AMREX_FORCE_INLINE
129 AMREX_GPU_HOST_DEVICE
132  amrex::Real coordinate, amrex::Real lower,
133  amrex::Real length) noexcept
134 {
135  return bottom + (top - bottom) * (coordinate - lower) / length;
136 }
137 
138 /**
139  * @brief Compute a deterministic 3D sine perturbation.
140  * @param[in] x x-coordinate.
141  * @param[in] y y-coordinate.
142  * @param[in] z z-coordinate.
143  * @param[in] lo Lower boundary coordinates.
144  * @param[in] length Domain lengths.
145  * @param[in] amplitude Perturbation amplitude.
146  * @return Perturbation value at the given coordinates.
147  */
148 AMREX_FORCE_INLINE
149 AMREX_GPU_HOST_DEVICE
152  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& lo,
153  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& length,
154  amrex::Real amplitude) noexcept
155 {
156  constexpr amrex::Real pi = amrex::Real(3.1415926535897932384626433832795);
157  const amrex::Real sx = std::sin(amrex::Real(2.0) * pi * (x - lo[0]) / length[0]);
158  const amrex::Real sy = std::sin(amrex::Real(2.0) * pi * (y - lo[1]) / length[1]);
159  const amrex::Real sz = std::sin(pi * (z - lo[2]) / length[2]);
160  return amplitude * sx * sy * sz;
161 }
162 
163 /**
164  * @brief Compute the potential temperature at a given location.
165  * @param[in] config Configuration parameters.
166  * @param[in] x x-coordinate.
167  * @param[in] y y-coordinate.
168  * @param[in] z z-coordinate.
169  * @return Potential temperature at the specified location.
170  */
171 AMREX_FORCE_INLINE
172 AMREX_GPU_HOST_DEVICE
175  amrex::Real z) noexcept
176 {
177  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> length = {
178  config.prob_hi[0] - config.prob_lo[0],
179  config.prob_hi[1] - config.prob_lo[1],
180  config.prob_hi[2] - config.prob_lo[2]};
181  const amrex::Real bottom = config.physical_initialization ?
182  config.initial_temperature_bottom : config.theta_bottom;
183  const amrex::Real top = config.physical_initialization ?
184  config.initial_temperature_top : config.theta_top;
185  const amrex::Real amplitude = config.physical_initialization ?
186  config.temperature_perturbation_amplitude : config.theta_perturbation_amplitude;
187  return linear_profile(bottom, top, z,
188  config.prob_lo[2], length[2]) +
190  amplitude);
191 }
192 
193 /**
194  * @brief Compute the water vapor mixing ratio at a given height.
195  * @param[in] config Configuration parameters.
196  * @param[in] z Vertical coordinate.
197  * @return Vapor mixing ratio at the specified height.
198  */
199 AMREX_FORCE_INLINE
200 AMREX_GPU_HOST_DEVICE
202 qv_at (const Config& config, amrex::Real z) noexcept
203 {
204  return linear_profile(config.qv_bottom, config.qv_top, z,
205  config.prob_lo[2],
206  config.prob_hi[2] - config.prob_lo[2]);
207 }
208 
209 /**
210  * @brief Convert relative humidity to water vapor mixing ratio.
211  * @param[in] temperature_K Temperature in Kelvin.
212  * @param[in] pressure_Pa Pressure in Pascals.
213  * @param[in] relative_humidity Relative humidity [0,1].
214  * @return Water vapor mixing ratio.
215  */
216 AMREX_FORCE_INLINE
217 AMREX_GPU_HOST_DEVICE
220  amrex::Real pressure_Pa,
221  amrex::Real relative_humidity) noexcept
222 {
223  // erf_esatw returns hPa; convert saturation and vapor pressure to Pa before
224  // applying the dry-air vapor mixing-ratio definition.
225  const amrex::Real es_Pa = erf_esatw(temperature_K) * amrex::Real(100.0);
226  const amrex::Real ev_Pa = relative_humidity * es_Pa;
227  return RdoRv * ev_Pa / (pressure_Pa - ev_Pa);
228 }
229 
230 inline bool finite (amrex::Real value) noexcept
231 {
232  return std::isfinite(static_cast<double>(value));
233 }
234 
235 inline void require_finite (const std::string& key, amrex::Real value)
236 {
237  if (!finite(value)) {
238  amrex::Error("Cloud Chamber: " + key + " must be finite");
239  }
240 }
241 
242 inline std::string face_name (WallFace face)
243 {
244  switch (face) {
245  case WallFace::XLo: return "xlo";
246  case WallFace::XHi: return "xhi";
247  case WallFace::YLo: return "ylo";
248  case WallFace::YHi: return "yhi";
249  case WallFace::ZLo: return "zlo";
250  case WallFace::ZHi: return "zhi";
251  }
252  return "unknown";
253 }
254 
256 {
257  amrex::ParmParse pp_geom("geometry");
258  std::vector<int> periodic;
259  pp_geom.getarr("is_periodic", periodic);
260  if (periodic.size() != AMREX_SPACEDIM) {
261  amrex::Error("Cloud Chamber: geometry.is_periodic must provide three values");
262  }
263  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
264  if (periodic[d] != 0) {
265  amrex::Error("Cloud Chamber: geometry.is_periodic must be 0 0 0");
266  }
267  }
268 }
269 
271 {
272  amrex::ParmParse pp_erf("erf");
273  std::string value;
274  if (pp_erf.query("terrain_type", value) &&
275  amrex::toLower(value) != "none") {
276  amrex::Error("Cloud Chamber: erf.terrain_type must be None; terrain and embedded boundaries are out of scope");
277  }
278  value.clear();
279  if (pp_erf.query("buildings_type", value) &&
280  amrex::toLower(value) != "none") {
281  amrex::Error("Cloud Chamber: erf.buildings_type must be None; immersed buildings are out of scope");
282  }
283  value.clear();
284  if (pp_erf.query("mesh_type", value) &&
285  amrex::toLower(value) != "constantdz") {
286  amrex::Error("Cloud Chamber: erf.mesh_type must be ConstantDz");
287  }
288 }
289 
290 inline void reject_unsupported_face_keys (amrex::ParmParse& pp,
291  const std::string& face)
292 {
293  if (pp.contains("physical_temperature") || pp.contains("relative_humidity") ||
294  pp.contains("rh") || pp.contains("roughness")) {
295  amrex::Error("Cloud Chamber: " + face +
296  " must use temperature and moisture for physical walls; "
297  "roughness and legacy aliases are unsupported");
298  }
299 }
300 
302  const amrex::Real* probhi)
303 {
304  Config config;
305  config.active = true;
306  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
307  config.prob_lo[d] = problo[d];
308  config.prob_hi[d] = probhi[d];
309  require_finite("geometry extent", config.prob_lo[d]);
310  require_finite("geometry extent", config.prob_hi[d]);
311  if (!(config.prob_hi[d] > config.prob_lo[d])) {
312  amrex::Error("Cloud Chamber: geometry.prob_hi must exceed geometry.prob_lo in every direction");
313  }
314  }
315 
316  amrex::ParmParse pp_erf("erf");
317  int anelastic = 0;
318  if (!pp_erf.query("anelastic", anelastic) || anelastic != 1) {
319  amrex::Error("Cloud Chamber: erf.anelastic = 1 is required");
320  }
321  bool use_gravity = false;
322  if (!pp_erf.query("use_gravity", use_gravity) || !use_gravity) {
323  amrex::Error("Cloud Chamber: erf.use_gravity = true is required");
324  }
325 
326  amrex::ParmParse pp_amr("amr");
327  int max_level = 0;
328  pp_amr.query("max_level", max_level);
329  if (max_level != 0) {
330  amrex::Error("Cloud Chamber: amr.max_level must be 0");
331  }
334 
335  amrex::ParmParse pp_prob("prob");
337  if (pp_prob.query("p_inf", p_inf)) {
338  require_finite("prob.p_inf", p_inf);
339  const amrex::Real pressure_tolerance = amrex::Real(1.0e-6) * p_0;
340  if (std::abs(p_inf - p_0) > pressure_tolerance) {
341  amrex::Error("Cloud Chamber: prob.p_inf must match ERF's p_0 = 100000 Pa within 1e-6 relative tolerance");
342  }
343  }
344 
345  std::string moisture_model;
346  const bool moisture_specified = pp_erf.query("moisture_model", moisture_model);
347  if (moisture_specified) {
348  moisture_model = amrex::toLower(moisture_model);
349  if (moisture_model != "satadj") {
350  amrex::Error("Cloud Chamber: moisture_model must be omitted for dry mode or set to SatAdj");
351  }
352  config.cloudy = true;
353  } else if (pp_prob.contains("qv_bottom") || pp_prob.contains("qv_top")) {
354  amrex::Error("Cloud Chamber: qv_bottom/qv_top require erf.moisture_model = SatAdj");
355  }
356 
357  std::string init_mode;
358  pp_prob.get("thermodynamic_initialization", init_mode);
359  init_mode = amrex::toLower(init_mode);
360  if (init_mode == "physical_temperature_rh") {
361  config.physical_initialization = true;
362  std::string init_type;
363  pp_erf.get("init_type", init_type);
364  if (amrex::toLower(init_type) != "constantdensity") {
365  amrex::Error("Cloud Chamber: physical initialization requires the existing HSE-backed erf.init_type = ConstantDensity path");
366  }
367  std::string molec_diff_type;
368  pp_erf.get("molec_diff_type", molec_diff_type);
369  if (amrex::toLower(molec_diff_type) != "constantalpha") {
370  amrex::Error("Cloud Chamber: physical wall transfer requires erf.molec_diff_type = ConstantAlpha");
371  }
372  bool vert_implicit = false;
373  pp_erf.query("vert_implicit", vert_implicit);
374  if (vert_implicit) {
375  amrex::Error("Cloud Chamber: physical wall transfer requires erf.vert_implicit = false");
376  }
377  pp_prob.get("initial_temperature_bottom", config.initial_temperature_bottom);
378  pp_prob.get("initial_temperature_top", config.initial_temperature_top);
379  const bool has_initial_relative_humidity = pp_prob.contains("initial_relative_humidity");
380  auto physical_contract_error = [&](bool has_legacy_profile_keys) {
381  const InitializationContract contract {
383  config.cloudy,
384  has_initial_relative_humidity,
385  has_legacy_profile_keys,
386  false};
387  return initialization_contract_error(contract);
388  };
389  std::string contract_error = physical_contract_error(false);
390  if (!contract_error.empty()) {
391  amrex::Error(contract_error);
392  }
393  if (config.cloudy) {
394  pp_prob.get("initial_relative_humidity", config.initial_relative_humidity);
395  }
396  pp_prob.query("temperature_perturbation_amplitude",
397  config.temperature_perturbation_amplitude);
398  const bool has_legacy_profile_keys = pp_prob.contains("theta_bottom") ||
399  pp_prob.contains("theta_top") ||
400  pp_prob.contains("theta_perturbation_amplitude") ||
401  pp_prob.contains("qv_bottom") ||
402  pp_prob.contains("qv_top");
403  contract_error = physical_contract_error(has_legacy_profile_keys);
404  if (!contract_error.empty()) {
405  amrex::Error(contract_error);
406  }
407  require_finite("prob.initial_temperature_bottom", config.initial_temperature_bottom);
408  require_finite("prob.initial_temperature_top", config.initial_temperature_top);
409  require_finite("prob.temperature_perturbation_amplitude",
410  config.temperature_perturbation_amplitude);
411  if (config.initial_temperature_bottom <= amrex::Real(0.0) ||
412  config.initial_temperature_top <= amrex::Real(0.0)) {
413  amrex::Error("Cloud Chamber: physical initial temperatures must be positive");
414  }
415  if (config.cloudy) {
416  require_finite("prob.initial_relative_humidity", config.initial_relative_humidity);
417  if (config.initial_relative_humidity < amrex::Real(0.0) ||
418  config.initial_relative_humidity > amrex::Real(1.0)) {
419  amrex::Error("Cloud Chamber: initial_relative_humidity must lie in [0,1]");
420  }
421  }
422  } else if (init_mode == "legacy_theta_qv") {
423  config.physical_initialization = false;
424  const InitializationContract contract {
426  false,
427  false,
428  false,
429  pp_prob.contains("initial_temperature_bottom") ||
430  pp_prob.contains("initial_temperature_top") ||
431  pp_prob.contains("initial_relative_humidity") ||
432  pp_prob.contains("temperature_perturbation_amplitude")};
433  const std::string contract_error = initialization_contract_error(contract);
434  if (!contract_error.empty()) {
435  amrex::Error(contract_error);
436  }
437  pp_prob.get("theta_bottom", config.theta_bottom);
438  pp_prob.get("theta_top", config.theta_top);
439  pp_prob.query("theta_perturbation_amplitude",
440  config.theta_perturbation_amplitude);
441  } else {
442  amrex::Error("Cloud Chamber: prob.thermodynamic_initialization must be physical_temperature_rh or legacy_theta_qv");
443  }
444  std::string perturbation_mode = "deterministic_sine";
445  pp_prob.query("perturbation_mode", perturbation_mode);
446  if (amrex::toLower(perturbation_mode) != "deterministic_sine") {
447  amrex::Error("Cloud Chamber: prob.perturbation_mode must be deterministic_sine");
448  }
449  if (!config.physical_initialization) {
450  require_finite("prob.theta_bottom", config.theta_bottom);
451  require_finite("prob.theta_top", config.theta_top);
452  require_finite("prob.theta_perturbation_amplitude",
453  config.theta_perturbation_amplitude);
454  if (config.theta_bottom <= amrex::Real(0.0) ||
455  config.theta_top <= amrex::Real(0.0)) {
456  amrex::Error("Cloud Chamber: theta profile values must be positive");
457  }
458  }
459 
460  if (moisture_specified) {
461  if (!config.physical_initialization) {
462  pp_prob.get("qv_bottom", config.qv_bottom);
463  pp_prob.get("qv_top", config.qv_top);
464  require_finite("prob.qv_bottom", config.qv_bottom);
465  require_finite("prob.qv_top", config.qv_top);
466  if (config.qv_bottom < amrex::Real(0.0) ||
467  config.qv_top < amrex::Real(0.0)) {
468  amrex::Error("Cloud Chamber: qv profile values must be nonnegative");
469  }
470  }
471  }
472 
475  for (int n = 0; n < 2 * AMREX_SPACEDIM; ++n) {
476  const std::string face = face_name(faces[n]);
477  amrex::ParmParse pp_face(face.c_str());
478  reject_unsupported_face_keys(pp_face, face);
479  std::string type;
480  if (!pp_face.query("type", type) ||
481  amrex::toLower(type) != "noslipwall") {
482  amrex::Error("Cloud Chamber: " + face + ".type must be NoSlipWall");
483  }
484  WallSpec& wall = config.walls[n];
485  wall.face = faces[n];
486  if (config.physical_initialization) {
487  if (pp_face.contains("theta") || pp_face.contains("qv") ||
488  pp_face.contains("theta_grad") || pp_face.contains("qv_grad")) {
489  amrex::Error("Cloud Chamber: physical walls cannot combine temperature/moisture with legacy theta/qv wall keys");
490  }
491  if (!pp_face.query("temperature", wall.thermodynamics.physical_temperature_K)) {
492  amrex::Error("Cloud Chamber: missing required physical " + face + ".temperature");
493  }
494  require_finite(face + ".temperature", wall.thermodynamics.physical_temperature_K);
496  amrex::Error("Cloud Chamber: " + face + ".temperature must be positive Kelvin");
497  }
498  std::string moisture;
499  pp_face.get("moisture", moisture);
500  moisture = amrex::toLower(moisture);
501  if (moisture == "dry") {
503  } else if (moisture == "wet") {
505  if (!config.cloudy) {
506  amrex::Error("Cloud Chamber: wet walls require SatAdj moisture");
507  }
508  } else {
509  amrex::Error("Cloud Chamber: " + face + ".moisture must be dry or wet");
510  }
511  std::string transfer = "resolved_molecular";
512  pp_face.query("wall_transfer_model", transfer);
513  transfer = amrex::toLower(transfer);
514  if (transfer != "resolved_molecular") {
515  amrex::Error("Cloud Chamber: only resolved_molecular wall transfer is supported");
516  }
519  } else {
520  if (!pp_face.query("theta", config.walls[n].thermodynamics.physical_temperature_K)) {
521  amrex::Error("Cloud Chamber: missing required " + face + ".theta");
522  }
524  wall.thermodynamics.moisture_mode = config.cloudy ?
527  if (config.cloudy && !pp_face.contains("qv")) {
528  amrex::Error("Cloud Chamber: legacy cloudy walls require " + face + ".qv");
529  }
530  }
531  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
532  if (pp_face.contains("velocity")) {
533  amrex::Error("Cloud Chamber: wall velocity metadata is not active; use NoSlipWall with zero velocity");
534  }
535  wall.velocity[d] = amrex::Real(0.0);
536  }
537  }
538  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
539  const amrex::Real length = config.prob_hi[d] - config.prob_lo[d];
540  if (!(length > amrex::Real(0.0)) || !finite(length)) {
541  amrex::Error("Cloud Chamber: every rectangular dimension must be finite and positive");
542  }
543  }
544  return config;
545 }
546 
547 } // namespace erf_cloud_chamber
548 
549 #endif
constexpr amrex::Real p_0
Definition: ERF_Constants.H:61
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:57
auto probhi
Definition: ERF_InitCustomPertVels_ABL.H:37
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:36
InitType init_type
Definition: ERF_InitCustomPertVels_EkmanSpiral.H:3
ParmParse pp("prob")
ParmParse pp_prob("prob")
const Real length
Definition: ERF_InitCustomPert_AnelasticWallDiffusion.H:14
const auto config
Definition: ERF_InitCustomPert_CloudChamber.H:37
amrex::Real p_inf
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:5
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:123
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_CloudChamber.H:18
void require_zero_periodicity()
Definition: ERF_CloudChamber.H:255
WallFace
Definition: ERF_CloudChamber.H:27
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real deterministic_perturbation(amrex::Real x, amrex::Real y, amrex::Real z, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &lo, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &length, amrex::Real amplitude) noexcept
Compute a deterministic 3D sine perturbation.
Definition: ERF_CloudChamber.H:151
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real qv_at(const Config &config, amrex::Real z) noexcept
Compute the water vapor mixing ratio at a given height.
Definition: ERF_CloudChamber.H:202
std::string face_name(WallFace face)
Definition: ERF_CloudChamber.H:242
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real vapor_mixing_ratio_from_relative_humidity(amrex::Real temperature_K, amrex::Real pressure_Pa, amrex::Real relative_humidity) noexcept
Convert relative humidity to water vapor mixing ratio.
Definition: ERF_CloudChamber.H:219
Config parse_config(const amrex::Real *problo, const amrex::Real *probhi)
Definition: ERF_CloudChamber.H:301
void require_finite(const std::string &key, amrex::Real value)
Definition: ERF_CloudChamber.H:235
InitializationMode
Definition: ERF_CloudChamber.H:72
void reject_unsupported_face_keys(amrex::ParmParse &pp, const std::string &face)
Definition: ERF_CloudChamber.H:290
void require_no_unsupported_geometry()
Definition: ERF_CloudChamber.H:270
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real theta_at(const Config &config, amrex::Real x, amrex::Real y, amrex::Real z) noexcept
Compute the potential temperature at a given location.
Definition: ERF_CloudChamber.H:174
std::string initialization_contract_error(const InitializationContract &contract)
Validate the initialization contract and return an error message if violated.
Definition: ERF_CloudChamber.H:94
bool finite(amrex::Real value) noexcept
Definition: ERF_CloudChamber.H:230
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real linear_profile(amrex::Real bottom, amrex::Real top, amrex::Real coordinate, amrex::Real lower, amrex::Real length) noexcept
Compute a linear profile between two values.
Definition: ERF_CloudChamber.H:131
amrex::GpuArray< Face, 2 *AMREX_SPACEDIM > Boundary
Definition: ERF_WallThermodynamics.H:34
real(c_double), parameter, private pi
Definition: ERF_module_mp_morr_two_moment.F90:100
Configuration parameters for cloud chamber initialization.
Definition: ERF_CloudChamber.H:41
amrex::Real qv_top
Definition: ERF_CloudChamber.H:53
amrex::GpuArray< WallSpec, 2 *AMREX_SPACEDIM > walls
Definition: ERF_CloudChamber.H:56
erf_wall_thermodynamics::Boundary wall_boundary() const noexcept
Collect thermodynamic boundary conditions for all chamber walls.
Definition: ERF_CloudChamber.H:62
bool active
Definition: ERF_CloudChamber.H:42
amrex::Real initial_temperature_top
Definition: ERF_CloudChamber.H:46
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > prob_hi
Definition: ERF_CloudChamber.H:55
bool cloudy
Definition: ERF_CloudChamber.H:43
amrex::Real qv_bottom
Definition: ERF_CloudChamber.H:52
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > prob_lo
Definition: ERF_CloudChamber.H:54
amrex::Real initial_temperature_bottom
Definition: ERF_CloudChamber.H:45
amrex::Real temperature_perturbation_amplitude
Definition: ERF_CloudChamber.H:48
amrex::Real theta_perturbation_amplitude
Definition: ERF_CloudChamber.H:51
amrex::Real theta_bottom
Definition: ERF_CloudChamber.H:49
amrex::Real initial_relative_humidity
Definition: ERF_CloudChamber.H:47
amrex::Real theta_top
Definition: ERF_CloudChamber.H:50
bool physical_initialization
Definition: ERF_CloudChamber.H:44
Contract used to validate initialization parameter consistency.
Definition: ERF_CloudChamber.H:80
InitializationMode mode
Definition: ERF_CloudChamber.H:81
bool has_initial_relative_humidity
Definition: ERF_CloudChamber.H:83
bool has_physical_profile_keys
Definition: ERF_CloudChamber.H:85
bool has_legacy_profile_keys
Definition: ERF_CloudChamber.H:84
bool cloudy
Definition: ERF_CloudChamber.H:82
Metadata describing a chamber wall.
Definition: ERF_CloudChamber.H:32
erf_wall_thermodynamics::Face thermodynamics
Definition: ERF_CloudChamber.H:34
WallFace face
Definition: ERF_CloudChamber.H:33
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > velocity
Definition: ERF_CloudChamber.H:35
Definition: ERF_WallThermodynamics.H:27
MoistureMode moisture_mode
Definition: ERF_WallThermodynamics.H:29
ThermalMode thermal_mode
Definition: ERF_WallThermodynamics.H:28
TransferModel transfer_model
Definition: ERF_WallThermodynamics.H:30
amrex::Real physical_temperature_K
Definition: ERF_WallThermodynamics.H:31