|
8 | 8 | import re |
9 | 9 | from typing import Any, Dict |
10 | 10 |
|
11 | | -from .registry import REGISTRY |
| 11 | +from .namelist_parser import get_fortran_constants |
| 12 | +from .registry import REGISTRY, IndexedFamily |
12 | 13 | from .schema import ParamDef, ParamType |
13 | 14 |
|
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 |
16 | 39 |
|
17 | 40 |
|
18 | 41 | # Auto-generated Descriptions |
@@ -637,9 +660,9 @@ def get_value_label(param_name: str, value: int) -> str: |
637 | 660 | "R0ref": {"min": 0}, |
638 | 661 | "sigma": {"min": 0}, |
639 | 662 | # 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}, |
643 | 666 | "num_source": {"min": 1}, |
644 | 667 | "num_probes": {"min": 1}, |
645 | 668 | "num_integrals": {"min": 1}, |
@@ -1139,26 +1162,37 @@ def _load(): |
1139 | 1162 | ]: |
1140 | 1163 | _r(f"bub_pp%{a}", REAL, {"bubbles"}, math=sym) |
1141 | 1164 |
|
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"]: |
1159 | 1183 | 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 | + ) |
1162 | 1196 |
|
1163 | 1197 | # acoustic sources (4 sources) |
1164 | 1198 | for i in range(1, NA + 1): |
|
0 commit comments