Skip to content

Commit e438581

Browse files
sbryngelsonclaude
andcommitted
Add validation constraints and extend doc annotation fallbacks
Validator: add 5 prohibit() statements for previously unchecked params: - igr_pres_lim requires igr - int_comp requires recon_type = 2 (MUSCL) - pre_stress requires hyperelasticity - sigR/sigV require qbmm Docs: extend _format_tag_annotation with reverse dependency lookup, feature tag labels, and prefix-group labels to cover params with no AST rules or schema constraints. Coverage 83% → 97% of general-family. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bd2502f commit e438581

2 files changed

Lines changed: 101 additions & 5 deletions

File tree

toolchain/mfc/case_validator.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ def check_model_eqns_and_num_fluids(self):
145145
def check_igr(self):
146146
"""Checks constraints regarding IGR order"""
147147
igr = self.get('igr', 'F') == 'T'
148+
igr_pres_lim = self.get('igr_pres_lim', 'F') == 'T'
149+
150+
self.prohibit(igr_pres_lim and not igr,
151+
"igr_pres_lim requires igr to be enabled")
148152

149153
if not igr:
150154
return
@@ -153,7 +157,6 @@ def check_igr(self):
153157
m = self.get('m', 0)
154158
n = self.get('n', 0)
155159
p = self.get('p', 0)
156-
157160
self.prohibit(igr_order not in [None, 3, 5],
158161
"igr_order must be 3 or 5")
159162
if igr_order:
@@ -192,6 +195,10 @@ def check_weno(self):
192195
def check_muscl(self):
193196
"""Check constraints regarding MUSCL order"""
194197
recon_type = self.get('recon_type', 1)
198+
int_comp = self.get('int_comp', 'F') == 'T'
199+
200+
self.prohibit(int_comp and recon_type != 2,
201+
"int_comp (THINC interface compression) requires recon_type = 2 (MUSCL)")
195202

196203
# MUSCL_TYPE = 2
197204
if recon_type != 2:
@@ -382,6 +389,13 @@ def check_qbmm_and_polydisperse(self):
382389
self.prohibit(qbmm and nnode is not None and nnode != 4,
383390
"QBMM requires nnode = 4")
384391

392+
sigR = self.get('sigR')
393+
sigV = self.get('sigV')
394+
self.prohibit(sigR is not None and sigR != 0 and not qbmm,
395+
"sigR requires qbmm to be enabled")
396+
self.prohibit(sigV is not None and sigV != 0 and not qbmm,
397+
"sigV requires qbmm to be enabled")
398+
385399
def check_adv_n(self):
386400
"""Checks constraints on adv_n flag"""
387401
adv_n = self.get('adv_n', 'F') == 'T'
@@ -1202,6 +1216,10 @@ def check_probe_integral_output(self):
12021216
def check_hyperelasticity(self):
12031217
"""Checks hyperelasticity constraints"""
12041218
hyperelasticity = self.get('hyperelasticity', 'F') == 'T'
1219+
pre_stress = self.get('pre_stress', 'F') == 'T'
1220+
1221+
self.prohibit(pre_stress and not hyperelasticity,
1222+
"pre_stress requires hyperelasticity to be enabled")
12051223

12061224
if not hyperelasticity:
12071225
return

toolchain/mfc/params/generators/docs_gen.py

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,71 @@ def _get_param_pattern():
185185
return _PARAM_PATTERN
186186

187187

188-
def _format_tag_annotation(_param_name: str, param) -> str:
188+
def _build_reverse_dep_map() -> Dict[str, List[Tuple[str, str]]]:
189+
"""Build map from target param -> [(relation, source_param), ...] from DEPENDENCIES."""
190+
from ..definitions import DEPENDENCIES # pylint: disable=import-outside-toplevel
191+
reverse: Dict[str, List[Tuple[str, str]]] = {}
192+
for param, dep in DEPENDENCIES.items():
193+
if "when_true" in dep:
194+
wt = dep["when_true"]
195+
for r in wt.get("requires", []):
196+
reverse.setdefault(r, []).append(("required by", param))
197+
for r in wt.get("recommends", []):
198+
reverse.setdefault(r, []).append(("recommended for", param))
199+
if "when_value" in dep:
200+
for val, subspec in dep["when_value"].items():
201+
for r in subspec.get("requires", []):
202+
reverse.setdefault(r, []).append(("required by", f"{param}={val}"))
203+
for r in subspec.get("recommends", []):
204+
reverse.setdefault(r, []).append(("recommended for", f"{param}={val}"))
205+
return reverse
206+
207+
208+
_REVERSE_DEPS = None
209+
210+
211+
def _get_reverse_deps():
212+
global _REVERSE_DEPS # noqa: PLW0603 pylint: disable=global-statement
213+
if _REVERSE_DEPS is None:
214+
_REVERSE_DEPS = _build_reverse_dep_map()
215+
return _REVERSE_DEPS
216+
217+
218+
# Feature tag → human-readable label, in priority order
219+
_TAG_LABELS = [
220+
("bubbles", "Bubble model parameter"),
221+
("mhd", "MHD parameter"),
222+
("chemistry", "Chemistry parameter"),
223+
("time", "Time-stepping parameter"),
224+
("grid", "Grid parameter"),
225+
("weno", "WENO parameter"),
226+
("viscosity", "Viscosity parameter"),
227+
("elasticity", "Elasticity parameter"),
228+
("surface_tension", "Surface tension parameter"),
229+
("acoustic", "Acoustic parameter"),
230+
("ib", "Immersed boundary parameter"),
231+
("probes", "Probe/integral parameter"),
232+
("riemann", "Riemann solver parameter"),
233+
("relativity", "Relativity parameter"),
234+
("output", "Output parameter"),
235+
]
236+
237+
# Prefix → label for untagged params
238+
_PREFIX_LABELS = [
239+
("mixlayer_", "Mixing layer parameter"),
240+
("nv_uvm_", "GPU memory management"),
241+
("ic_", "Initial condition parameter"),
242+
]
243+
244+
245+
def _format_tag_annotation(param_name: str, param) -> str: # pylint: disable=too-many-locals
189246
"""
190247
Return a short annotation for params with no schema constraints and no AST rules.
191248
192-
Uses DEPENDENCIES info (highest value) and tag-based output flag labels.
249+
Checks (in order): own DEPENDENCIES, output flag tags, reverse dependencies,
250+
feature tag labels, and prefix-group labels.
193251
"""
194-
# 1. DEPENDENCIES info
252+
# 1. Own DEPENDENCIES info
195253
if param.dependencies:
196254
dep = param.dependencies
197255
if "when_true" in dep:
@@ -208,14 +266,34 @@ def _format_tag_annotation(_param_name: str, param) -> str:
208266
rec = ", ".join(f"`{r}`" for r in wt["recommends"])
209267
return f"Recommends {rec}"
210268

211-
# 2. Tag-based output flag label
269+
# 2. Tag-based output flag label (specific labels for LOG output params)
212270
if "output" in param.tags and param.param_type == ParamType.LOG:
213271
if "bubbles" in param.tags:
214272
return "Lagrangian output flag"
215273
if "chemistry" in param.tags:
216274
return "Chemistry output flag"
217275
return "Post-processing output flag"
218276

277+
# 3. Reverse dependencies (params required/recommended by other features)
278+
reverse = _get_reverse_deps()
279+
if param_name in reverse:
280+
entries = reverse[param_name]
281+
parts = []
282+
for relation, source in entries[:2]:
283+
parts.append(f"Required by `{source}`" if relation == "required by"
284+
else f"Recommended for `{source}`")
285+
return "; ".join(parts)
286+
287+
# 4. Feature tag labels (broader tag-based annotation)
288+
for tag, label in _TAG_LABELS:
289+
if tag in param.tags:
290+
return label
291+
292+
# 5. Prefix group labels (for untagged params with common prefixes)
293+
for prefix, label in _PREFIX_LABELS:
294+
if param_name.startswith(prefix):
295+
return label
296+
219297
return ""
220298

221299

0 commit comments

Comments
 (0)