Phase 2.6: MOOSE state-variable wiring + canonical_arguments - #78
Conversation
…ource of truth (#77)
petlenz
left a comment
There was a problem hiding this comment.
Three-lens critical review (architect / cpp-pro / code-reviewer) on Phase 2.6. The canonical_arguments single-source-of-truth is the right architecture and the MOOSE happy-path is correct — but the review found 2 CRITICAL + 5 MAJOR, several convergent across lenses. Two I verified directly against the build:
add_outputhas no name guard (confirmed) — a state varalpha+ outputalphabothdeclareProperty("Model_alpha")→ MOOSE duplicate-property error + duplicate_alphamember.- Non-constant initial values emit bare symbol names (confirmed) —
add_scalar_state_variable("c", K*0.5)generates_c[_qp] = 0.5 * K;ininitQpStatefulProperties, but the MOOSE member is_K—Kis undefined. Silent broken MOOSE.
The recurring theme: the scope boundary (scalar-only, constant-init) is documented in comments but not enforced — tensor state vars, tensor params, and non-constant initials all mis-emit silently instead of failing loudly. Most fixes are small guards.
[CRITICAL] add_output does not check name availability (out-of-diff: recipe.h add_output, unchanged in this PR but exposed by it). Confirmed: add_output skips the assert_symbol_name_available guard that add_scalar_state_variable / the input+param adders call. A recipe with a state variable alpha and an output alpha both emit declareProperty<Real>("Model_alpha") + a _alpha member → MOOSE duplicate-property error + C++ redefinition. Phase 2.6 makes this reachable (state vars now declare properties). Fix: call assert_symbol_name_available(name) at the top of both add_output overloads + add a death test for the state-var/output name clash.
…e guard, -Wswitch, +5 tests
…both declaration orders)
|
Round-2 review (architect + code-reviewer). All 7 round-1 fixups verified held. 1 new CRITICAL found and fixed in [CRITICAL — fixed] Reverse-order output↔state-var name collision bypassed the round-1 guard. Round-1 made [MINOR — deferred → #79] Derived names not reserved. A user output literally named Everything else verified clean:
190/190 tests. Recommend landing once CI is green. |
|
Round-3 review (cpp-pro + code-reviewer). Both lenses: LAND IT. cpp-pro — clean. Verified the round-2 symmetric name-guard is complete + correct: all five add-paths (params/inputs/state-vars) check against both code-reviewer — land, 2 MINOR + 1 NIT. I applied the one with real correctness flavour now (it matches this PR's fail-loud posture): [MINOR — fixed [MINOR — fixed] Docs — workflow.md §5 2.6 row now states the local-newton requirement + constant-init-only limit. [NIT — already covered] Standalone evolution-recipe signature — Verified clean (no new issues): Deferred to fast-follow #79: reserve derived names ( 191/191 tests. Three review rounds converged (3 CRITICAL + 5 MAJOR found & fixed across rounds 1-2; round 3 = 1 MINOR fixed + clean). Ready to land. |
|
Round-4 review (calibrated, convergent-cycle pass). LAND IT — nothing actionable. Checked every fresh angle:
Converged: rounds 1-2 (3 CRITICAL + 5 MAJOR) → round 3 (1 MINOR) → round 4 (nothing). 191/191 tests. Ready to merge. |
…ard non-finite defaults
Round 5 review — security lens (codegen injection surface)Rounds 1–4 converged on correctness (3 CRITICAL + 5 MAJOR + 1 MINOR, all fixed; 191/191 green). Round 5 switched to an adversarial codegen-injection angle: every user-supplied string that flows verbatim into emitted C++ is an injection surface. Three real findings, all fixed in [CRITICAL] Model name was never validatedThe model name flows raw into the generated function name ( Verified pre-fix: Fix: the [MAJOR] Parameter doc strings were emitted unescaped
Fix: doc is routed through the existing [MAJOR] Non-finite parameter defaults streamed as
|
Round 6 review — fresh lenses: adversarial test-quality + standards/generated-code conformanceRounds 1–5 converged on correctness and security. Round 6 deliberately took two angles not yet exercised, run as independent parallel lenses, each told not to re-flag prior rounds. Lens A — C++ standards conformance + generated-code correctness: cleanNo new CRITICAL/MAJOR. Notable verifications:
Lens B — adversarial test-quality: 1 genuine test defect + coverage notes (fixed
|
Summary
Phase 2.6 (issue #77) — wire state variables through the MOOSE backend so an evolution recipe emits a compilable, correct MOOSE
Material. The backend was provably broken for such recipes (3-arg compute call into a 7-param function;<sv>_oldnever declared); this fixes it and flips theKNOWN_GAPguard test to positive checks.The root cause + the fix
The MOOSE call-site rebuilt the argument list (
parameters()→inputs()→outputs()) independently of the generated signature (symbols()→outputs()→newton-out), so it silently omitted state variables. Rather than patch the MOOSE order to match by hand (re-introducing the drift), this PR introducescanonical_arguments(model)— one orderedArgSpeclist that BOTHrender_compute_function(signature) and every backend call-site iterate. One source of truth; the two can't diverge again. (Done as a verified pure refactor first — Step A, generated code byte-identical, all tests green — then the MOOSE wiring built on top.)MOOSE wiring (Step B)
StateVariableCurrent→MaterialProperty<Real> & _<sv>(declareProperty),StateVariableOld→const MaterialProperty<Real> & _<sv>_old(getMaterialPropertyOld of the SAME property name — MOOSE versions it automatically).dt→ MOOSE's framework_dt— excluded fromvalidParams+ the constructor (it's not an input-file param), passed as_dtin the compute call. The old code wrongly emittedgetParam<Real>("dt").initQpStatefulPropertiesseeds each state variable to its initial value (emitted through the scalar pipeline, so non-constant initials work too).computeQpPropertiescall built by iteratingcanonical_arguments→ exact arity match.Example (Newton hardening) — the emitted
.Cnow compiles:Tests
KNOWN_GAPguard → 4 positive Phase-2.6 tests: declares state-var properties, mapsdt→_dt(nogetParam("dt")/addParam("dt")), compute-call arity threads the state args, state initialised ininitQpStatefulProperties.canonical_argumentsextraction.Closes #60
The Category-aware render branch is now complete across both layers:
StateVariableCurrent→ out-param (Newton path) / declared-property (MOOSE);StateVariableOld→ getMaterialPropertyOld. Closes #60.Out of scope
canonical_argumentshelper makes them mechanical — they just need a role→argument mapping like MOOSE's)._Jacobian_multwiring — Phase 3b (Phase 3b — Algorithmic tangent + Kuhn-Tucker lowering #35) / Phase 5 (Phase 5 — MOOSE + Standalone state-variable + algorithmic-tangent wiring #37).Closes #77, closes #60.