diff --git a/examples/2D_shockdroplet_muscl/case.py b/examples/2D_shockdroplet_muscl/case.py index 61b904ec85..4367cd079a 100755 --- a/examples/2D_shockdroplet_muscl/case.py +++ b/examples/2D_shockdroplet_muscl/case.py @@ -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 diff --git a/examples/3D_shockdroplet_muscl/case.py b/examples/3D_shockdroplet_muscl/case.py index 8213b85105..67bd4954e5 100644 --- a/examples/3D_shockdroplet_muscl/case.py +++ b/examples/3D_shockdroplet_muscl/case.py @@ -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, diff --git a/toolchain/mfc/case_validator.py b/toolchain/mfc/case_validator.py index 2167c3f749..600b0ba9f0 100644 --- a/toolchain/mfc/case_validator.py +++ b/toolchain/mfc/case_validator.py @@ -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. @@ -1398,6 +1403,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 @@ -1689,13 +1698,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)""" diff --git a/toolchain/mfc/test_case_validator.py b/toolchain/mfc/test_case_validator.py index 3f4a8aa728..3b3281a571 100644 --- a/toolchain/mfc/test_case_validator.py +++ b/toolchain/mfc/test_case_validator.py @@ -714,3 +714,18 @@ 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})