Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/documentation/case.md
Original file line number Diff line number Diff line change
Expand Up @@ -1428,8 +1428,8 @@ This boundary condition can be used for subsonic inflow (`bc_[x,y,z]%[beg,end]`
| 15 | N/A | N/A | N/A | No longer exists. Empty. |
| 16 | 1D bubble pulse | 1 | N | Requires `x_centroid`, `length_x` |
| 17 | Spiral | 2 | N | Requires `[x,y]_centroid` |
| 18 | 2D Varcircle | 2 | Y | Requires `[x,y]_centroid`, `radius`, and `thickness` |
| 19 | 3D Varcircle | 3 | Y | Requires `[x,y,z]_centroid`, `length_z`, `radius`, and `thickness` |
| 18 | 2D Varcircle | 2 | Y | Requires `[x,y]_centroid`, `radius`, and `epsilon` (thickness) |
| 19 | 3D Varcircle | 3 | Y | Requires `[x,y,z]_centroid`, `length_z`, `radius`, and `epsilon` (thickness) |
| 20 | 2D Taylor-Green Vortex | 2 | N | Requires `[x,y]_centroid`, `length_x`, `length_y`, `vel(1)`, and `vel(2)` |
| 21 | Model | 2 & 3 | Y | Imports a Model (STL/OBJ). Requires `model_id`. |

Expand Down
4 changes: 2 additions & 2 deletions examples/2D_shockdroplet_muscl/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
"riemann_solver": "hllc",
"wave_speeds": "direct",
"avg_state": "arithmetic",
"bc_x%beg": -6, # 11,
"bc_x%end": -6, # 12
"bc_x%beg": -3,
"bc_x%end": -3,
"bc_y%beg": -2,
"bc_y%end": -3,
# Formatted Database Files Structure Parameters
Expand Down
4 changes: 2 additions & 2 deletions examples/3D_shockdroplet_muscl/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@
"riemann_solver": "hllc",
"wave_speeds": "direct",
"avg_state": "arithmetic",
"bc_x%beg": -6,
"bc_x%end": -6,
"bc_x%beg": -3,
"bc_x%end": -3,
"bc_y%beg": -2,
"bc_y%end": -3,
"bc_z%beg": -2,
Expand Down
4 changes: 2 additions & 2 deletions src/pre_process/m_check_ib_patches.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ contains

integer :: i

do i = 1, num_patches_max
do i = 1, num_ib_patches_max_namelist
call s_int_to_str(i, iStr)
if (i <= num_ibs) then
call s_int_to_str(i, iStr)
@:PROHIBIT(patch_ib(i)%geometry == dflt_int, "IB patch undefined. patch_ib("//trim(iStr)//")%geometry must be set.")

! Constraints on the geometric initial condition patch parameters
Expand Down
77 changes: 75 additions & 2 deletions src/pre_process/m_check_patches.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ contains
call s_check_2d_modal_patch_geometry(i)
else if (patch_icpp(i)%geometry == 14) then
call s_check_3d_spherical_harmonic_patch_geometry(i)
else if (patch_icpp(i)%geometry == 16) then
call s_check_1d_bubble_pulse_patch_geometry(i)
else if (patch_icpp(i)%geometry == 17) then
call s_check_spiral_patch_geometry(i)
else if (patch_icpp(i)%geometry == 18) then
call s_check_varcircle_patch_geometry(i)
else if (patch_icpp(i)%geometry == 19) then
call s_check_3d_varcircle_patch_geometry(i)
else if (patch_icpp(i)%geometry == 20) then
call s_check_2D_TaylorGreen_vortex_patch_geometry(i)
else if (patch_icpp(i)%geometry == 21) then
Expand Down Expand Up @@ -148,8 +156,8 @@ contains

call s_int_to_str(patch_id, iStr)

@:PROHIBIT(n == 0, "Circle patch "//trim(iStr)//": n must be zero")
@:PROHIBIT(p > 0, "Circle patch "//trim(iStr)//": p must be greater than zero")
@:PROHIBIT(n == 0, "Circle patch "//trim(iStr)//": n must be greater than zero")
@:PROHIBIT(p > 0, "Circle patch "//trim(iStr)//": p must be zero")
@:PROHIBIT(patch_icpp(patch_id)%radius <= 0._wp, "Circle patch "//trim(iStr)//": radius must be greater than zero")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%x_centroid), "Circle patch "//trim(iStr)//": x_centroid must be set")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%y_centroid), "Circle patch "//trim(iStr)//": y_centroid must be set")
Expand Down Expand Up @@ -229,6 +237,71 @@ contains

end subroutine s_check_2D_TaylorGreen_vortex_patch_geometry

!> Check the 1D bubble pulse patch input
impure subroutine s_check_1d_bubble_pulse_patch_geometry(patch_id)

integer, intent(in) :: patch_id

call s_int_to_str(patch_id, iStr)

@:PROHIBIT(n > 0, "1D bubble pulse patch "//trim(iStr)//": n must be zero")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%x_centroid), &
& "1D bubble pulse patch " // trim(iStr) // ": x_centroid must be set")
@:PROHIBIT(patch_icpp(patch_id)%length_x <= 0._wp, &
& "1D bubble pulse patch " // trim(iStr) // ": length_x must be greater than zero")

end subroutine s_check_1d_bubble_pulse_patch_geometry

!> Check the spiral patch input
impure subroutine s_check_spiral_patch_geometry(patch_id)

integer, intent(in) :: patch_id

call s_int_to_str(patch_id, iStr)

@:PROHIBIT(n == 0, "Spiral patch "//trim(iStr)//": n must be greater than zero")
@:PROHIBIT(p > 0, "Spiral patch "//trim(iStr)//": p must be zero")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%x_centroid), "Spiral patch "//trim(iStr)//": x_centroid must be set")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%y_centroid), "Spiral patch "//trim(iStr)//": y_centroid must be set")

end subroutine s_check_spiral_patch_geometry

!> Check the 2D varcircle patch input; epsilon is the ring thickness
impure subroutine s_check_varcircle_patch_geometry(patch_id)

integer, intent(in) :: patch_id

call s_int_to_str(patch_id, iStr)

@:PROHIBIT(n == 0, "Varcircle patch "//trim(iStr)//": n must be greater than zero")
@:PROHIBIT(p > 0, "Varcircle patch "//trim(iStr)//": p must be zero")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%x_centroid), "Varcircle patch "//trim(iStr)//": x_centroid must be set")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%y_centroid), "Varcircle patch "//trim(iStr)//": y_centroid must be set")
@:PROHIBIT(patch_icpp(patch_id)%radius <= 0._wp, "Varcircle patch "//trim(iStr)//": radius must be greater than zero")
@:PROHIBIT(patch_icpp(patch_id)%epsilon <= 0._wp, "Varcircle patch "//trim(iStr)//": epsilon must be greater than zero")

end subroutine s_check_varcircle_patch_geometry

!> Check the 3D varcircle patch input; epsilon is the ring thickness
impure subroutine s_check_3d_varcircle_patch_geometry(patch_id)

integer, intent(in) :: patch_id

call s_int_to_str(patch_id, iStr)

@:PROHIBIT(p == 0, "3D varcircle patch "//trim(iStr)//": p must be greater than zero")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%x_centroid), "3D varcircle patch "//trim(iStr)//": x_centroid must be set")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%y_centroid), "3D varcircle patch "//trim(iStr)//": y_centroid must be set")
@:PROHIBIT(f_is_default(patch_icpp(patch_id)%z_centroid), "3D varcircle patch "//trim(iStr)//": z_centroid must be set")
@:PROHIBIT(patch_icpp(patch_id)%length_z <= 0._wp, &
& "3D varcircle patch " // trim(iStr) // ": length_z must be greater than zero")
@:PROHIBIT(patch_icpp(patch_id)%radius <= 0._wp, &
& "3D varcircle patch " // trim(iStr) // ": radius must be greater than zero")
@:PROHIBIT(patch_icpp(patch_id)%epsilon <= 0._wp, &
& "3D varcircle patch " // trim(iStr) // ": epsilon must be greater than zero")

end subroutine s_check_3d_varcircle_patch_geometry

!> Check the model patch input
impure subroutine s_check_sphere_patch_geometry(patch_id)

Expand Down
29 changes: 22 additions & 7 deletions toolchain/mfc/case_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,11 @@ def _check_order_fits_grid(self, order: int, param_name: str) -> None:
def _get_recon_type(self) -> int:
return self.get("recon_type", 1)

def _characteristic_bcs(self) -> list:
"""Names of the boundaries set to a characteristic BC, BC_CHAR_SLIP_WALL (-5) to BC_CHAR_SUP_OUTFLOW (-12)"""
names = [f"bc_{d}%{b}" for d in ["x", "y", "z"] for b in ["beg", "end"]]
return [name for name in names if self.get(name) is not None and -12 <= self.get(name) <= -5]

def check_parameter_types(self):
"""Validate parameter types before other checks.

Expand Down Expand Up @@ -1390,6 +1395,10 @@ def check_muscl_simulation(self):
muscl_lim = self.get("muscl_lim")
muscl_eps = self.get("muscl_eps")

# s_cbc computes characteristic fluxes for WENO only, so MUSCL would silently ignore a CBC
for name in self._characteristic_bcs():
self.prohibit(True, f"Characteristic boundary condition {name} is not compatible with recon_type = 2 (MUSCL)")

if muscl_order is None:
return

Expand Down Expand Up @@ -1681,13 +1690,8 @@ def check_igr_simulation(self):
self.prohibit(probe_wrt, "IGR does not support probe writes")
self.prohibit(int_comp > 0, "IGR does not support int_comp > 0")

# Check BCs - IGR does not support characteristic BCs
# Characteristic BCs are BC_CHAR_SLIP_WALL (-5) through BC_CHAR_SUP_OUTFLOW (-12)
for dir in ["x", "y", "z"]:
for bound in ["beg", "end"]:
bc = self.get(f"bc_{dir}%{bound}")
if bc is not None:
self.prohibit(-12 <= bc <= -5, f"Characteristic boundary condition bc_{dir}%{bound} is not compatible with IGR")
for name in self._characteristic_bcs():
self.prohibit(True, f"Characteristic boundary condition {name} is not compatible with IGR")

def check_acoustic_source(self):
"""Checks acoustic source parameters (simulation)"""
Expand Down Expand Up @@ -2669,6 +2673,16 @@ def check_surface_tension_post(self):

self.prohibit(cf_wrt and not surface_tension, "cf_wrt can only be enabled if surface_tension is enabled")

def check_sim_data(self):
"""Checks interface/energy data output constraints (post-process)"""
if self.get("sim_data", "F") != "T":
return

# s_write_energy_data_file uses dz and s_write_intf_data_file reads alpha_2 at eqn_idx%E + 2
self.prohibit(self.get("p", 0) == 0, "sim_data requires p > 0 (3D)")
self.prohibit(self.get("num_fluids") != 2, "sim_data requires num_fluids = 2")
self.prohibit(self.get("model_eqns") not in [2, 3], "sim_data requires model_eqns = 2 or 3")

def check_no_flow_variables(self):
"""Checks that at least one flow variable is selected (post-process)"""
rho_wrt = self.get("rho_wrt", "F") == "T"
Expand Down Expand Up @@ -3106,6 +3120,7 @@ def validate_post_process(self):
self.check_liutex_post()
self.check_schlieren()
self.check_surface_tension_post()
self.check_sim_data()
self.check_no_flow_variables()

def validate(self, stage: str = "simulation"):
Expand Down
1 change: 1 addition & 0 deletions toolchain/mfc/lint_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ def check_physics_docs_coverage(repo_root: Path) -> list[str]:
"check_partial_domain", # output format settings
"check_perturb_density", # parameter pairing validation
"check_qm", # output dimension requirements
"check_sim_data", # output dimension requirements
"check_chemistry", # runtime Cantera validation only
# Awaiting proper physics documentation (math, references, explanation)
"check_adaptive_time_stepping",
Expand Down
62 changes: 62 additions & 0 deletions toolchain/mfc/test_case_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,3 +704,65 @@ def test_rejects_isothermal_wall_without_any_heat_path(self):
{**BASE, "bc_x%beg": -16, "bc_x%end": -16, "bc_x%isothermal_in": "T", "bc_x%Twall_in": 300.0},
"requires a heat-conduction path",
)


class TestMusclCharacteristicBcs(ConstraintTestCase):
"""s_cbc computes characteristic fluxes for WENO only, so MUSCL silently ignored a CBC."""

MUSCL = {**{k: v for k, v in BASE.items() if not k.startswith("weno")}, "recon_type": 2, "muscl_order": 2, "muscl_lim": 1}

def test_rejects_cbc_with_muscl(self):
self.assertRejects({**self.MUSCL, "bc_x%end": -6}, "bc_x%end is not compatible with recon_type = 2 (MUSCL)")

def test_accepts_non_characteristic_bc_with_muscl(self):
self.assertAccepts({**self.MUSCL, "bc_x%beg": -3, "bc_x%end": -3})

def test_accepts_cbc_with_weno(self):
self.assertAccepts({**BASE, "bc_x%beg": -6, "bc_x%end": -6})


class TestSimData(ConstraintTestCase):
"""sim_data's interface and energy writers assume a 3D two-fluid 5- or 6-equation case."""

GOOD = {
**BASE_2D,
**{k: v for k, v in TWO_FLUID.items() if k not in BASE},
"num_fluids": 2,
"p": 50,
"bc_z%beg": -1,
"bc_z%end": -1,
"z_domain%beg": 0.0,
"z_domain%end": 1.0,
"patch_icpp(1)%z_centroid": 0.5,
"patch_icpp(1)%length_z": 1.0,
"patch_icpp(1)%vel(3)": 0.0,
"rho_wrt": "T",
"sim_data": "T",
}

def errors_for(self, params) -> str:
"""Validate at the post_process stage, where sim_data is read."""
validator = CaseValidator(dict(params))
try:
validator.validate("post_process")
except CaseConstraintError as exc:
return str(exc)
return ""

def test_accepts_3d_two_fluid(self):
self.assertAccepts(self.GOOD)

def test_accepts_six_equation(self):
self.assertAccepts({**self.GOOD, "model_eqns": 3})

def test_rejects_2d(self):
self.assertRejects({**self.GOOD, "p": 0}, "sim_data requires p > 0 (3D)")

def test_rejects_one_fluid(self):
self.assertRejects({**self.GOOD, "num_fluids": 1}, "sim_data requires num_fluids = 2")

def test_rejects_gamma_law(self):
self.assertRejects({**self.GOOD, "model_eqns": 1}, "sim_data requires model_eqns = 2 or 3")

def test_not_tripped_when_disabled(self):
self.assertAccepts({**BASE_2D, "rho_wrt": "T", "sim_data": "F"})
Loading