Skip to content

Commit 52cff8d

Browse files
authored
Merge branch 'master' into feature/non-newtonian-viscosity
2 parents 38948f3 + 119a8fa commit 52cff8d

14 files changed

Lines changed: 721 additions & 111 deletions

File tree

src/simulation/m_ibm.fpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ contains
193193
type(ghost_point) :: gp
194194
type(ghost_point) :: innerp
195195

196-
! set the Moving IBM interior Pressure Values
196+
! set the Moving IBM interior conservative variables
197197
$:GPU_PARALLEL_LOOP(private='[i,j,k,patch_id,rho]', copyin='[E_idx,momxb]', collapse=3)
198198
do l = 0, p
199199
do k = 0, n

toolchain/mfc/case.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def get_inp(self, _target) -> str:
6262
cons.print(f"Generating [magenta]{target.name}.inp[/magenta]:")
6363
cons.indent()
6464

65-
MASTER_KEYS: list = case_dicts.get_input_dict_keys(target.name)
65+
MASTER_KEYS = case_dicts.get_input_dict_keys(target.name)
6666

6767
ignored = []
6868

toolchain/mfc/case_validator.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from .common import MFCException
2121
from .params.definitions import CONSTRAINTS
22+
from .params.namelist_parser import get_fortran_constants
2223
from .state import CFG
2324

2425
# Physics documentation for check methods.
@@ -572,7 +573,12 @@ def check_ibm(self):
572573
num_ibs = self.get("num_ibs", 0)
573574

574575
self.prohibit(ib and n <= 0, "Immersed Boundaries do not work in 1D (requires n > 0)")
575-
self.prohibit(ib and (num_ibs <= 0 or num_ibs > 1000), "num_ibs must be between 1 and num_patches_max (1000)")
576+
self.prohibit(ib and num_ibs <= 0, "num_ibs must be >= 1 when ib is enabled")
577+
num_patches_max = get_fortran_constants().get("num_patches_max", 1000)
578+
self.prohibit(
579+
ib and num_ibs > num_patches_max,
580+
f"num_ibs must be <= {num_patches_max} (num_patches_max in m_constants.fpp)",
581+
)
576582
self.prohibit(not ib and num_ibs > 0, "num_ibs is set, but ib is not enabled")
577583

578584
def check_stiffened_eos(self):
@@ -1211,6 +1217,11 @@ def check_restart(self):
12111217
self.prohibit(old_grid and t_step_old is None, "old_grid requires t_step_old to be set")
12121218
self.prohibit(num_patches < 0, "num_patches must be non-negative")
12131219
self.prohibit(num_patches == 0 and t_step_old is None, "num_patches must be positive for the non-restart case")
1220+
num_patches_max = get_fortran_constants().get("num_patches_max", 1000)
1221+
self.prohibit(
1222+
num_patches > num_patches_max,
1223+
f"num_patches must be <= {num_patches_max} (num_patches_max in m_constants.fpp)",
1224+
)
12141225

12151226
def check_qbmm_pre_process(self):
12161227
"""Checks QBMM constraints for pre-process"""
@@ -1422,7 +1433,7 @@ def check_patch_physics(self):
14221433
def check_bc_patches(self):
14231434
"""Checks boundary condition patch geometry (pre-process)"""
14241435
num_bc_patches = self.get("num_bc_patches", 0)
1425-
num_bc_patches_max = self.get("num_bc_patches_max", 10)
1436+
num_bc_patches_max = get_fortran_constants().get("num_bc_patches_max", 10)
14261437

14271438
if num_bc_patches <= 0:
14281439
return

toolchain/mfc/params/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@
2323
# and freezes it. It must come after REGISTRY is imported and must not be removed.
2424
from . import definitions # noqa: F401
2525
from .definitions import CONSTRAINTS, DEPENDENCIES, get_value_label
26-
from .registry import REGISTRY, RegistryFrozenError
26+
from .registry import REGISTRY, IndexedFamily, RegistryFrozenError
2727
from .schema import ParamDef, ParamType
2828

2929
__all__ = [
3030
"REGISTRY",
31+
"IndexedFamily",
3132
"RegistryFrozenError",
3233
"ParamDef",
3334
"ParamType",

toolchain/mfc/params/definitions.py

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,34 @@
88
import re
99
from typing import Any, Dict
1010

11-
from .registry import REGISTRY
11+
from .namelist_parser import get_fortran_constants
12+
from .registry import REGISTRY, IndexedFamily
1213
from .schema import ParamDef, ParamType
1314

14-
# Index limits
15-
NP, NF, NI, NA, NPR, NB = 10, 10, 1000, 4, 10, 10 # patches, fluids, ibs, acoustic, probes, bc_patches
15+
# Index limits — sourced from Fortran compile-time constants (m_constants.fpp).
16+
# These must stay in sync with Fortran; we error if the source can't be parsed.
17+
_FC = get_fortran_constants()
18+
19+
20+
def _fc(name: str) -> int:
21+
"""Get a required Fortran constant, raising if unavailable."""
22+
if name not in _FC:
23+
raise RuntimeError(
24+
f"Fortran constant '{name}' not found in m_constants.fpp. "
25+
f"Toolchain is out of sync with Fortran source."
26+
)
27+
return _FC[name]
28+
29+
30+
NF = _fc("num_fluids_max") # fluid_pp
31+
NPR = _fc("num_probes_max") # probe, acoustic, integral
32+
NB = _fc("num_bc_patches_max") # patch_bc
33+
NUM_PATCHES_MAX = _fc("num_patches_max") # patch_icpp, patch_ib (Fortran array bound)
34+
# Enumeration limits for families not yet converted to IndexedFamily.
35+
# These are smaller than the Fortran array bounds to keep the registry compact.
36+
# The CONSTRAINTS dict below uses the Fortran constants for validation.
37+
NP = 10 # patch_icpp: has per-index variations, can't easily be IndexedFamily
38+
NA = 4 # acoustic sources: enumerated individually
1639

1740

1841
# Auto-generated Descriptions
@@ -637,9 +660,9 @@ def get_value_label(param_name: str, value: int) -> str:
637660
"R0ref": {"min": 0},
638661
"sigma": {"min": 0},
639662
# Counts (must be positive)
640-
"num_fluids": {"min": 1, "max": 10},
641-
"num_patches": {"min": 0, "max": 10},
642-
"num_ibs": {"min": 0, "max": 1000},
663+
"num_fluids": {"min": 1, "max": NF},
664+
"num_patches": {"min": 0, "max": NUM_PATCHES_MAX},
665+
"num_ibs": {"min": 0},
643666
"num_source": {"min": 1},
644667
"num_probes": {"min": 1},
645668
"num_integrals": {"min": 1},
@@ -1139,26 +1162,37 @@ def _load():
11391162
]:
11401163
_r(f"bub_pp%{a}", REAL, {"bubbles"}, math=sym)
11411164

1142-
# patch_ib (10 immersed boundaries)
1143-
for i in range(1, NI + 1):
1144-
px = f"patch_ib({i})%"
1145-
for a in ["geometry", "moving_ibm"]:
1146-
_r(f"{px}{a}", INT, {"ib"})
1147-
for a, pt in [("radius", REAL), ("theta", REAL), ("slip", LOG), ("c", REAL), ("p", REAL), ("t", REAL), ("m", REAL), ("mass", REAL)]:
1148-
_r(f"{px}{a}", pt, {"ib"})
1149-
for j in range(1, 4):
1150-
_r(f"{px}angles({j})", REAL, {"ib"})
1151-
for d in ["x", "y", "z"]:
1152-
_r(f"{px}{d}_centroid", REAL, {"ib"})
1153-
_r(f"{px}length_{d}", REAL, {"ib"})
1154-
for a, pt in [("model_filepath", STR), ("model_spc", INT), ("model_threshold", REAL)]:
1155-
_r(f"{px}{a}", pt, {"ib"})
1156-
for t in ["translate", "scale", "rotate"]:
1157-
for j in range(1, 4):
1158-
_r(f"{px}model_{t}({j})", REAL, {"ib"})
1165+
# patch_ib (immersed boundaries) — registered as indexed family for O(1) lookup.
1166+
# max_index is None so the parameter registry stays compact (no enumeration).
1167+
# The Fortran-side upper bound (num_patches_max in m_constants.fpp) is parsed
1168+
# and enforced by the case_validator, not by max_index here.
1169+
_ib_tags = {"ib"}
1170+
_ib_attrs: Dict[str, tuple] = {}
1171+
for a in ["geometry", "moving_ibm"]:
1172+
_ib_attrs[a] = (INT, _ib_tags)
1173+
for a, pt in [("radius", REAL), ("theta", REAL), ("slip", LOG), ("c", REAL), ("p", REAL), ("t", REAL), ("m", REAL), ("mass", REAL)]:
1174+
_ib_attrs[a] = (pt, _ib_tags)
1175+
for j in range(1, 4):
1176+
_ib_attrs[f"angles({j})"] = (REAL, _ib_tags)
1177+
for d in ["x", "y", "z"]:
1178+
_ib_attrs[f"{d}_centroid"] = (REAL, _ib_tags)
1179+
_ib_attrs[f"length_{d}"] = (REAL, _ib_tags)
1180+
for a, pt in [("model_filepath", STR), ("model_spc", INT), ("model_threshold", REAL)]:
1181+
_ib_attrs[a] = (pt, _ib_tags)
1182+
for t in ["translate", "scale", "rotate"]:
11591183
for j in range(1, 4):
1160-
_r(f"{px}vel({j})", A_REAL, {"ib"})
1161-
_r(f"{px}angular_vel({j})", A_REAL, {"ib"})
1184+
_ib_attrs[f"model_{t}({j})"] = (REAL, _ib_tags)
1185+
for j in range(1, 4):
1186+
_ib_attrs[f"vel({j})"] = (A_REAL, _ib_tags)
1187+
_ib_attrs[f"angular_vel({j})"] = (A_REAL, _ib_tags)
1188+
REGISTRY.register_family(
1189+
IndexedFamily(
1190+
base_name="patch_ib",
1191+
attrs=_ib_attrs,
1192+
tags=_ib_tags,
1193+
max_index=NUM_PATCHES_MAX,
1194+
)
1195+
)
11621196

11631197
# acoustic sources (4 sources)
11641198
for i in range(1, NA + 1):

toolchain/mfc/params/namelist_parser.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import re
1313
from pathlib import Path
14-
from typing import Dict, Set
14+
from typing import Dict, Optional, Set
1515

1616
# Fallback parameters for when Fortran source files are not available.
1717
# Generated from the namelist definitions in src/*/m_start_up.fpp.
@@ -464,6 +464,44 @@ def parse_all_namelists(mfc_root: Path) -> Dict[str, Set[str]]:
464464
return result
465465

466466

467+
def parse_fortran_constants(filepath: Path) -> Dict[str, int]:
468+
"""
469+
Parse integer parameter constants from a Fortran source file.
470+
471+
Extracts lines like ``integer, parameter :: name = 123`` and returns
472+
a dict mapping constant names to their integer values.
473+
"""
474+
constants: Dict[str, int] = {}
475+
pattern = re.compile(
476+
r"integer\s*,\s*parameter\s*::\s*(\w+)\s*=\s*(\d+)", re.IGNORECASE
477+
)
478+
try:
479+
text = filepath.read_text()
480+
except FileNotFoundError:
481+
return constants
482+
for m in pattern.finditer(text):
483+
constants[m.group(1)] = int(m.group(2))
484+
return constants
485+
486+
487+
# Module-level cache for Fortran constants (None = not yet loaded)
488+
_FORTRAN_CONSTANTS_CACHE: Optional[Dict[str, int]] = None
489+
490+
491+
def get_fortran_constants() -> Dict[str, int]:
492+
"""
493+
Get Fortran compile-time constants from m_constants.fpp.
494+
495+
Cached after first call. Returns empty dict if source unavailable.
496+
"""
497+
global _FORTRAN_CONSTANTS_CACHE # noqa: PLW0603
498+
if _FORTRAN_CONSTANTS_CACHE is None:
499+
root = get_mfc_root()
500+
path = root / "src" / "common" / "m_constants.fpp"
501+
_FORTRAN_CONSTANTS_CACHE = parse_fortran_constants(path)
502+
return _FORTRAN_CONSTANTS_CACHE
503+
504+
467505
def get_mfc_root() -> Path:
468506
"""Get the MFC root directory from this file's location."""
469507
# This file is at toolchain/mfc/params/namelist_parser.py

0 commit comments

Comments
 (0)