248 for (
int d = 0; d < AMREX_SPACEDIM; ++d) {
254 amrex::Error(
"Cloud Chamber: geometry.prob_hi must exceed geometry.prob_lo in every direction");
258 amrex::ParmParse pp_erf(
"erf");
260 if (!pp_erf.query(
"anelastic", anelastic) || anelastic != 1) {
261 amrex::Error(
"Cloud Chamber: erf.anelastic = 1 is required");
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");
268 amrex::ParmParse pp_amr(
"amr");
270 pp_amr.query(
"max_level", max_level);
271 if (max_level != 0) {
272 amrex::Error(
"Cloud Chamber: amr.max_level must be 0");
277 amrex::ParmParse
pp_prob(
"prob");
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");
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");
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");
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;
306 if (amrex::toLower(
init_type) !=
"constantdensity") {
307 amrex::Error(
"Cloud Chamber: physical initialization requires the existing HSE-backed erf.init_type = ConstantDensity path");
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");
314 bool vert_implicit =
false;
315 pp_erf.query(
"vert_implicit", vert_implicit);
317 amrex::Error(
"Cloud Chamber: physical wall transfer requires erf.vert_implicit = false");
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 {
324 InitializationMode::PhysicalTemperatureRH,
326 has_initial_relative_humidity,
327 has_legacy_profile_keys,
331 std::string contract_error = physical_contract_error(
false);
332 if (!contract_error.empty()) {
333 amrex::Error(contract_error);
336 pp_prob.get(
"initial_relative_humidity",
config.initial_relative_humidity);
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") ||
345 contract_error = physical_contract_error(has_legacy_profile_keys);
346 if (!contract_error.empty()) {
347 amrex::Error(contract_error);
352 config.temperature_perturbation_amplitude);
355 amrex::Error(
"Cloud Chamber: physical initial temperatures must be positive");
361 amrex::Error(
"Cloud Chamber: initial_relative_humidity must lie in [0,1]");
364 }
else if (init_mode ==
"legacy_theta_qv") {
365 config.physical_initialization =
false;
366 const InitializationContract contract {
367 InitializationMode::LegacyThetaQv,
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")};
376 if (!contract_error.empty()) {
377 amrex::Error(contract_error);
381 pp_prob.query(
"theta_perturbation_amplitude",
382 config.theta_perturbation_amplitude);
384 amrex::Error(
"Cloud Chamber: prob.thermodynamic_initialization must be physical_temperature_rh or legacy_theta_qv");
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");
391 if (!
config.physical_initialization) {
395 config.theta_perturbation_amplitude);
398 amrex::Error(
"Cloud Chamber: theta profile values must be positive");
402 if (moisture_specified) {
403 if (!
config.physical_initialization) {
410 amrex::Error(
"Cloud Chamber: qv profile values must be nonnegative");
415 const WallFace faces[] = {WallFace::XLo, WallFace::XHi, WallFace::YLo,
416 WallFace::YHi, WallFace::ZLo, WallFace::ZHi};
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());
422 if (!pp_face.query(
"type", type) ||
423 amrex::toLower(type) !=
"noslipwall") {
424 amrex::Error(
"Cloud Chamber: " + face +
".type must be NoSlipWall");
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");
433 if (!pp_face.query(
"temperature", wall.thermodynamics.physical_temperature_K)) {
434 amrex::Error(
"Cloud Chamber: missing required physical " + face +
".temperature");
436 require_finite(face +
".temperature", wall.thermodynamics.physical_temperature_K);
437 if (wall.thermodynamics.physical_temperature_K <=
amrex::Real(0.0)) {
438 amrex::Error(
"Cloud Chamber: " + face +
".temperature must be positive Kelvin");
440 std::string moisture;
441 pp_face.get(
"moisture", moisture);
442 moisture = amrex::toLower(moisture);
443 if (moisture ==
"dry") {
445 }
else if (moisture ==
"wet") {
448 amrex::Error(
"Cloud Chamber: wet walls require SatAdj moisture");
451 amrex::Error(
"Cloud Chamber: " + face +
".moisture must be dry or wet");
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");
462 if (!pp_face.query(
"theta",
config.walls[n].thermodynamics.physical_temperature_K)) {
463 amrex::Error(
"Cloud Chamber: missing required " + face +
".theta");
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");
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");
480 for (
int d = 0; d < AMREX_SPACEDIM; ++d) {
483 amrex::Error(
"Cloud Chamber: every rectangular dimension must be finite and positive");
constexpr amrex::Real p_0
Definition: ERF_Constants.H:61
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("prob")
const auto config
Definition: ERF_InitCustomPert_CloudChamber.H:37
amrex::Real p_inf
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:5
void require_zero_periodicity()
Definition: ERF_CloudChamber.H:197
WallFace
Definition: ERF_CloudChamber.H:27
std::string face_name(WallFace face)
Definition: ERF_CloudChamber.H:184
void require_finite(const std::string &key, amrex::Real value)
Definition: ERF_CloudChamber.H:177
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
std::string initialization_contract_error(const InitializationContract &contract)
Definition: ERF_CloudChamber.H:76
bool finite(amrex::Real value) noexcept
Definition: ERF_CloudChamber.H:172
@ FixedPhysicalTemperature