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 struct WallSpec {
32  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> velocity = {};
33 };
34 
35 struct Config {
36  bool active = false;
37  bool cloudy = false;
43  amrex::Real theta_bottom = amrex::Real(0.0); // legacy_theta_qv only
44  amrex::Real theta_top = amrex::Real(0.0); // legacy_theta_qv only
46  amrex::Real qv_bottom = amrex::Real(0.0); // legacy_theta_qv only
47  amrex::Real qv_top = amrex::Real(0.0); // legacy_theta_qv only
48  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> prob_lo = {};
49  amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> prob_hi = {};
50  amrex::GpuArray<WallSpec, 2 * AMREX_SPACEDIM> walls = {};
51 
53  {
55  for (int n = 0; n < 2 * AMREX_SPACEDIM; ++n) {
56  boundary[n] = walls[n].thermodynamics;
57  }
58  return boundary;
59  }
60 };
61 
62 enum class InitializationMode : unsigned char {
65 };
66 
69  bool cloudy = false;
73 };
74 
75 inline std::string
77 {
78  switch (contract.mode) {
80  if (contract.cloudy) {
81  if (!contract.has_initial_relative_humidity) {
82  return "Cloud Chamber: physical SatAdj initialization requires prob.initial_relative_humidity";
83  }
84  } else if (contract.has_initial_relative_humidity) {
85  return "Cloud Chamber: prob.initial_relative_humidity is only used with erf.moisture_model = SatAdj";
86  }
87  if (contract.has_legacy_profile_keys) {
88  return "Cloud Chamber: physical_temperature_rh cannot be combined with legacy theta/qv profile keys";
89  }
90  break;
92  if (contract.has_physical_profile_keys) {
93  return "Cloud Chamber: legacy_theta_qv cannot be combined with physical temperature/RH profile keys";
94  }
95  break;
96  }
97 
98  return {};
99 }
100 
101 AMREX_FORCE_INLINE
102 AMREX_GPU_HOST_DEVICE
105  amrex::Real coordinate, amrex::Real lower,
106  amrex::Real length) noexcept
107 {
108  return bottom + (top - bottom) * (coordinate - lower) / length;
109 }
110 
111 AMREX_FORCE_INLINE
112 AMREX_GPU_HOST_DEVICE
115  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& lo,
116  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& length,
117  amrex::Real amplitude) noexcept
118 {
119  constexpr amrex::Real pi = amrex::Real(3.1415926535897932384626433832795);
120  const amrex::Real sx = std::sin(amrex::Real(2.0) * pi * (x - lo[0]) / length[0]);
121  const amrex::Real sy = std::sin(amrex::Real(2.0) * pi * (y - lo[1]) / length[1]);
122  const amrex::Real sz = std::sin(pi * (z - lo[2]) / length[2]);
123  return amplitude * sx * sy * sz;
124 }
125 
126 AMREX_FORCE_INLINE
127 AMREX_GPU_HOST_DEVICE
130  amrex::Real z) noexcept
131 {
132  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM> length = {
133  config.prob_hi[0] - config.prob_lo[0],
134  config.prob_hi[1] - config.prob_lo[1],
135  config.prob_hi[2] - config.prob_lo[2]};
136  const amrex::Real bottom = config.physical_initialization ?
137  config.initial_temperature_bottom : config.theta_bottom;
138  const amrex::Real top = config.physical_initialization ?
139  config.initial_temperature_top : config.theta_top;
140  const amrex::Real amplitude = config.physical_initialization ?
141  config.temperature_perturbation_amplitude : config.theta_perturbation_amplitude;
142  return linear_profile(bottom, top, z,
143  config.prob_lo[2], length[2]) +
145  amplitude);
146 }
147 
148 AMREX_FORCE_INLINE
149 AMREX_GPU_HOST_DEVICE
151 qv_at (const Config& config, amrex::Real z) noexcept
152 {
153  return linear_profile(config.qv_bottom, config.qv_top, z,
154  config.prob_lo[2],
155  config.prob_hi[2] - config.prob_lo[2]);
156 }
157 
158 AMREX_FORCE_INLINE
159 AMREX_GPU_HOST_DEVICE
162  amrex::Real pressure_Pa,
163  amrex::Real relative_humidity) noexcept
164 {
165  // erf_esatw returns hPa; convert saturation and vapor pressure to Pa before
166  // applying the dry-air vapor mixing-ratio definition.
167  const amrex::Real es_Pa = erf_esatw(temperature_K) * amrex::Real(100.0);
168  const amrex::Real ev_Pa = relative_humidity * es_Pa;
169  return RdoRv * ev_Pa / (pressure_Pa - ev_Pa);
170 }
171 
172 inline bool finite (amrex::Real value) noexcept
173 {
174  return std::isfinite(static_cast<double>(value));
175 }
176 
177 inline void require_finite (const std::string& key, amrex::Real value)
178 {
179  if (!finite(value)) {
180  amrex::Error("Cloud Chamber: " + key + " must be finite");
181  }
182 }
183 
184 inline std::string face_name (WallFace face)
185 {
186  switch (face) {
187  case WallFace::XLo: return "xlo";
188  case WallFace::XHi: return "xhi";
189  case WallFace::YLo: return "ylo";
190  case WallFace::YHi: return "yhi";
191  case WallFace::ZLo: return "zlo";
192  case WallFace::ZHi: return "zhi";
193  }
194  return "unknown";
195 }
196 
198 {
199  amrex::ParmParse pp_geom("geometry");
200  std::vector<int> periodic;
201  pp_geom.getarr("is_periodic", periodic);
202  if (periodic.size() != AMREX_SPACEDIM) {
203  amrex::Error("Cloud Chamber: geometry.is_periodic must provide three values");
204  }
205  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
206  if (periodic[d] != 0) {
207  amrex::Error("Cloud Chamber: geometry.is_periodic must be 0 0 0");
208  }
209  }
210 }
211 
213 {
214  amrex::ParmParse pp_erf("erf");
215  std::string value;
216  if (pp_erf.query("terrain_type", value) &&
217  amrex::toLower(value) != "none") {
218  amrex::Error("Cloud Chamber: erf.terrain_type must be None; terrain and embedded boundaries are out of scope");
219  }
220  value.clear();
221  if (pp_erf.query("buildings_type", value) &&
222  amrex::toLower(value) != "none") {
223  amrex::Error("Cloud Chamber: erf.buildings_type must be None; immersed buildings are out of scope");
224  }
225  value.clear();
226  if (pp_erf.query("mesh_type", value) &&
227  amrex::toLower(value) != "constantdz") {
228  amrex::Error("Cloud Chamber: erf.mesh_type must be ConstantDz");
229  }
230 }
231 
232 inline void reject_unsupported_face_keys (amrex::ParmParse& pp,
233  const std::string& face)
234 {
235  if (pp.contains("physical_temperature") || pp.contains("relative_humidity") ||
236  pp.contains("rh") || pp.contains("roughness")) {
237  amrex::Error("Cloud Chamber: " + face +
238  " must use temperature and moisture for physical walls; "
239  "roughness and legacy aliases are unsupported");
240  }
241 }
242 
244  const amrex::Real* probhi)
245 {
246  Config config;
247  config.active = true;
248  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
249  config.prob_lo[d] = problo[d];
250  config.prob_hi[d] = probhi[d];
251  require_finite("geometry extent", config.prob_lo[d]);
252  require_finite("geometry extent", config.prob_hi[d]);
253  if (!(config.prob_hi[d] > config.prob_lo[d])) {
254  amrex::Error("Cloud Chamber: geometry.prob_hi must exceed geometry.prob_lo in every direction");
255  }
256  }
257 
258  amrex::ParmParse pp_erf("erf");
259  int anelastic = 0;
260  if (!pp_erf.query("anelastic", anelastic) || anelastic != 1) {
261  amrex::Error("Cloud Chamber: erf.anelastic = 1 is required");
262  }
263  bool use_gravity = false;
264  if (!pp_erf.query("use_gravity", use_gravity) || !use_gravity) {
265  amrex::Error("Cloud Chamber: erf.use_gravity = true is required");
266  }
267 
268  amrex::ParmParse pp_amr("amr");
269  int max_level = 0;
270  pp_amr.query("max_level", max_level);
271  if (max_level != 0) {
272  amrex::Error("Cloud Chamber: amr.max_level must be 0");
273  }
276 
277  amrex::ParmParse pp_prob("prob");
279  if (pp_prob.query("p_inf", p_inf)) {
280  require_finite("prob.p_inf", p_inf);
281  const amrex::Real pressure_tolerance = amrex::Real(1.0e-6) * p_0;
282  if (std::abs(p_inf - p_0) > pressure_tolerance) {
283  amrex::Error("Cloud Chamber: prob.p_inf must match ERF's p_0 = 100000 Pa within 1e-6 relative tolerance");
284  }
285  }
286 
287  std::string moisture_model;
288  const bool moisture_specified = pp_erf.query("moisture_model", moisture_model);
289  if (moisture_specified) {
290  moisture_model = amrex::toLower(moisture_model);
291  if (moisture_model != "satadj") {
292  amrex::Error("Cloud Chamber: moisture_model must be omitted for dry mode or set to SatAdj");
293  }
294  config.cloudy = true;
295  } else if (pp_prob.contains("qv_bottom") || pp_prob.contains("qv_top")) {
296  amrex::Error("Cloud Chamber: qv_bottom/qv_top require erf.moisture_model = SatAdj");
297  }
298 
299  std::string init_mode;
300  pp_prob.get("thermodynamic_initialization", init_mode);
301  init_mode = amrex::toLower(init_mode);
302  if (init_mode == "physical_temperature_rh") {
303  config.physical_initialization = true;
304  std::string init_type;
305  pp_erf.get("init_type", init_type);
306  if (amrex::toLower(init_type) != "constantdensity") {
307  amrex::Error("Cloud Chamber: physical initialization requires the existing HSE-backed erf.init_type = ConstantDensity path");
308  }
309  std::string molec_diff_type;
310  pp_erf.get("molec_diff_type", molec_diff_type);
311  if (amrex::toLower(molec_diff_type) != "constantalpha") {
312  amrex::Error("Cloud Chamber: physical wall transfer requires erf.molec_diff_type = ConstantAlpha");
313  }
314  bool vert_implicit = false;
315  pp_erf.query("vert_implicit", vert_implicit);
316  if (vert_implicit) {
317  amrex::Error("Cloud Chamber: physical wall transfer requires erf.vert_implicit = false");
318  }
319  pp_prob.get("initial_temperature_bottom", config.initial_temperature_bottom);
320  pp_prob.get("initial_temperature_top", config.initial_temperature_top);
321  const bool has_initial_relative_humidity = pp_prob.contains("initial_relative_humidity");
322  auto physical_contract_error = [&](bool has_legacy_profile_keys) {
323  const InitializationContract contract {
325  config.cloudy,
326  has_initial_relative_humidity,
327  has_legacy_profile_keys,
328  false};
329  return initialization_contract_error(contract);
330  };
331  std::string contract_error = physical_contract_error(false);
332  if (!contract_error.empty()) {
333  amrex::Error(contract_error);
334  }
335  if (config.cloudy) {
336  pp_prob.get("initial_relative_humidity", config.initial_relative_humidity);
337  }
338  pp_prob.query("temperature_perturbation_amplitude",
339  config.temperature_perturbation_amplitude);
340  const bool has_legacy_profile_keys = pp_prob.contains("theta_bottom") ||
341  pp_prob.contains("theta_top") ||
342  pp_prob.contains("theta_perturbation_amplitude") ||
343  pp_prob.contains("qv_bottom") ||
344  pp_prob.contains("qv_top");
345  contract_error = physical_contract_error(has_legacy_profile_keys);
346  if (!contract_error.empty()) {
347  amrex::Error(contract_error);
348  }
349  require_finite("prob.initial_temperature_bottom", config.initial_temperature_bottom);
350  require_finite("prob.initial_temperature_top", config.initial_temperature_top);
351  require_finite("prob.temperature_perturbation_amplitude",
352  config.temperature_perturbation_amplitude);
353  if (config.initial_temperature_bottom <= amrex::Real(0.0) ||
354  config.initial_temperature_top <= amrex::Real(0.0)) {
355  amrex::Error("Cloud Chamber: physical initial temperatures must be positive");
356  }
357  if (config.cloudy) {
358  require_finite("prob.initial_relative_humidity", config.initial_relative_humidity);
359  if (config.initial_relative_humidity < amrex::Real(0.0) ||
360  config.initial_relative_humidity > amrex::Real(1.0)) {
361  amrex::Error("Cloud Chamber: initial_relative_humidity must lie in [0,1]");
362  }
363  }
364  } else if (init_mode == "legacy_theta_qv") {
365  config.physical_initialization = false;
366  const InitializationContract contract {
368  false,
369  false,
370  false,
371  pp_prob.contains("initial_temperature_bottom") ||
372  pp_prob.contains("initial_temperature_top") ||
373  pp_prob.contains("initial_relative_humidity") ||
374  pp_prob.contains("temperature_perturbation_amplitude")};
375  const std::string contract_error = initialization_contract_error(contract);
376  if (!contract_error.empty()) {
377  amrex::Error(contract_error);
378  }
379  pp_prob.get("theta_bottom", config.theta_bottom);
380  pp_prob.get("theta_top", config.theta_top);
381  pp_prob.query("theta_perturbation_amplitude",
382  config.theta_perturbation_amplitude);
383  } else {
384  amrex::Error("Cloud Chamber: prob.thermodynamic_initialization must be physical_temperature_rh or legacy_theta_qv");
385  }
386  std::string perturbation_mode = "deterministic_sine";
387  pp_prob.query("perturbation_mode", perturbation_mode);
388  if (amrex::toLower(perturbation_mode) != "deterministic_sine") {
389  amrex::Error("Cloud Chamber: prob.perturbation_mode must be deterministic_sine");
390  }
391  if (!config.physical_initialization) {
392  require_finite("prob.theta_bottom", config.theta_bottom);
393  require_finite("prob.theta_top", config.theta_top);
394  require_finite("prob.theta_perturbation_amplitude",
395  config.theta_perturbation_amplitude);
396  if (config.theta_bottom <= amrex::Real(0.0) ||
397  config.theta_top <= amrex::Real(0.0)) {
398  amrex::Error("Cloud Chamber: theta profile values must be positive");
399  }
400  }
401 
402  if (moisture_specified) {
403  if (!config.physical_initialization) {
404  pp_prob.get("qv_bottom", config.qv_bottom);
405  pp_prob.get("qv_top", config.qv_top);
406  require_finite("prob.qv_bottom", config.qv_bottom);
407  require_finite("prob.qv_top", config.qv_top);
408  if (config.qv_bottom < amrex::Real(0.0) ||
409  config.qv_top < amrex::Real(0.0)) {
410  amrex::Error("Cloud Chamber: qv profile values must be nonnegative");
411  }
412  }
413  }
414 
417  for (int n = 0; n < 2 * AMREX_SPACEDIM; ++n) {
418  const std::string face = face_name(faces[n]);
419  amrex::ParmParse pp_face(face.c_str());
420  reject_unsupported_face_keys(pp_face, face);
421  std::string type;
422  if (!pp_face.query("type", type) ||
423  amrex::toLower(type) != "noslipwall") {
424  amrex::Error("Cloud Chamber: " + face + ".type must be NoSlipWall");
425  }
426  WallSpec& wall = config.walls[n];
427  wall.face = faces[n];
428  if (config.physical_initialization) {
429  if (pp_face.contains("theta") || pp_face.contains("qv") ||
430  pp_face.contains("theta_grad") || pp_face.contains("qv_grad")) {
431  amrex::Error("Cloud Chamber: physical walls cannot combine temperature/moisture with legacy theta/qv wall keys");
432  }
433  if (!pp_face.query("temperature", wall.thermodynamics.physical_temperature_K)) {
434  amrex::Error("Cloud Chamber: missing required physical " + face + ".temperature");
435  }
436  require_finite(face + ".temperature", wall.thermodynamics.physical_temperature_K);
438  amrex::Error("Cloud Chamber: " + face + ".temperature must be positive Kelvin");
439  }
440  std::string moisture;
441  pp_face.get("moisture", moisture);
442  moisture = amrex::toLower(moisture);
443  if (moisture == "dry") {
445  } else if (moisture == "wet") {
447  if (!config.cloudy) {
448  amrex::Error("Cloud Chamber: wet walls require SatAdj moisture");
449  }
450  } else {
451  amrex::Error("Cloud Chamber: " + face + ".moisture must be dry or wet");
452  }
453  std::string transfer = "resolved_molecular";
454  pp_face.query("wall_transfer_model", transfer);
455  transfer = amrex::toLower(transfer);
456  if (transfer != "resolved_molecular") {
457  amrex::Error("Cloud Chamber: only resolved_molecular wall transfer is supported");
458  }
461  } else {
462  if (!pp_face.query("theta", config.walls[n].thermodynamics.physical_temperature_K)) {
463  amrex::Error("Cloud Chamber: missing required " + face + ".theta");
464  }
466  wall.thermodynamics.moisture_mode = config.cloudy ?
469  if (config.cloudy && !pp_face.contains("qv")) {
470  amrex::Error("Cloud Chamber: legacy cloudy walls require " + face + ".qv");
471  }
472  }
473  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
474  if (pp_face.contains("velocity")) {
475  amrex::Error("Cloud Chamber: wall velocity metadata is not active; use NoSlipWall with zero velocity");
476  }
477  wall.velocity[d] = amrex::Real(0.0);
478  }
479  }
480  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
481  const amrex::Real length = config.prob_hi[d] - config.prob_lo[d];
482  if (!(length > amrex::Real(0.0)) || !finite(length)) {
483  amrex::Error("Cloud Chamber: every rectangular dimension must be finite and positive");
484  }
485  }
486  return config;
487 }
488 
489 } // namespace erf_cloud_chamber
490 
491 #endif
constexpr amrex::Real p_0
Definition: ERF_Constants.H:61
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:57
Real value
Definition: ERF_HurricaneDiagnostics.cpp:30
auto probhi
Definition: ERF_InitCustomPertVels_ABL.H:21
auto problo
Definition: ERF_InitCustomPertVels_ABL.H:20
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:197
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
Definition: ERF_CloudChamber.H:114
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real qv_at(const Config &config, amrex::Real z) noexcept
Definition: ERF_CloudChamber.H:151
std::string face_name(WallFace face)
Definition: ERF_CloudChamber.H:184
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
Definition: ERF_CloudChamber.H:161
Config parse_config(const amrex::Real *problo, const amrex::Real *probhi)
Definition: ERF_CloudChamber.H:243
void require_finite(const std::string &key, amrex::Real value)
Definition: ERF_CloudChamber.H:177
InitializationMode
Definition: ERF_CloudChamber.H:62
void reject_unsupported_face_keys(amrex::ParmParse &pp, const std::string &face)
Definition: ERF_CloudChamber.H:232
void require_no_unsupported_geometry()
Definition: ERF_CloudChamber.H:212
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real theta_at(const Config &config, amrex::Real x, amrex::Real y, amrex::Real z) noexcept
Definition: ERF_CloudChamber.H:129
std::string initialization_contract_error(const InitializationContract &contract)
Definition: ERF_CloudChamber.H:76
bool finite(amrex::Real value) noexcept
Definition: ERF_CloudChamber.H:172
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
Definition: ERF_CloudChamber.H:104
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
Definition: ERF_CloudChamber.H:35
amrex::Real qv_top
Definition: ERF_CloudChamber.H:47
amrex::GpuArray< WallSpec, 2 *AMREX_SPACEDIM > walls
Definition: ERF_CloudChamber.H:50
erf_wall_thermodynamics::Boundary wall_boundary() const noexcept
Definition: ERF_CloudChamber.H:52
bool active
Definition: ERF_CloudChamber.H:36
amrex::Real initial_temperature_top
Definition: ERF_CloudChamber.H:40
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > prob_hi
Definition: ERF_CloudChamber.H:49
bool cloudy
Definition: ERF_CloudChamber.H:37
amrex::Real qv_bottom
Definition: ERF_CloudChamber.H:46
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > prob_lo
Definition: ERF_CloudChamber.H:48
amrex::Real initial_temperature_bottom
Definition: ERF_CloudChamber.H:39
amrex::Real temperature_perturbation_amplitude
Definition: ERF_CloudChamber.H:42
amrex::Real theta_perturbation_amplitude
Definition: ERF_CloudChamber.H:45
amrex::Real theta_bottom
Definition: ERF_CloudChamber.H:43
amrex::Real initial_relative_humidity
Definition: ERF_CloudChamber.H:41
amrex::Real theta_top
Definition: ERF_CloudChamber.H:44
bool physical_initialization
Definition: ERF_CloudChamber.H:38
Definition: ERF_CloudChamber.H:67
InitializationMode mode
Definition: ERF_CloudChamber.H:68
bool has_initial_relative_humidity
Definition: ERF_CloudChamber.H:70
bool has_physical_profile_keys
Definition: ERF_CloudChamber.H:72
bool has_legacy_profile_keys
Definition: ERF_CloudChamber.H:71
bool cloudy
Definition: ERF_CloudChamber.H:69
Definition: ERF_CloudChamber.H:29
erf_wall_thermodynamics::Face thermodynamics
Definition: ERF_CloudChamber.H:31
WallFace face
Definition: ERF_CloudChamber.H:30
amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > velocity
Definition: ERF_CloudChamber.H:32
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