Skip to content

#108 MVP: derive a hyperelastic material from its energy potential - #117

Open
petlenz wants to merge 5 commits into
mainfrom
feature-material-compiler-mvp
Open

#108 MVP: derive a hyperelastic material from its energy potential#117
petlenz wants to merge 5 commits into
mainfrom
feature-material-compiler-mvp

Conversation

@petlenz

@petlenz petlenz commented Jul 23, 2026

Copy link
Copy Markdown
Member

What — the #108 material-compiler front-end (first slice)

Let the human write the strain-energy potential ψ and have codegen derive
the stress and consistent tangent, instead of writing the stress by hand.

New recipe API:

model.add_hyperelastic_potential("S", psi, E, "E", "dS_dE");
//   S     = ∂ψ/∂E   (registered as the stress output)
//   dS/dE = ∂²ψ/∂E² (registered as the consistent tangent)

Both are cas::diff of the potential — the AceGen "differentiate the potential"
paradigm, one layer above add_output(stress) + add_algorithmic_tangent.

Example + verification

examples/svk_from_energy_recipe.h — St. Venant–Kirchhoff derived from its
energy
ψ = ½λ(tr E)² + μ(E:E). It's the same material the hand-written SVK
example states directly, now generated from ψ alone.

New e2e gate energy_check_driver verifies two ways (per #108's "self-FD is
insufficient" caveat):

  1. the DERIVED stress ∂ψ/∂E equals the independently hand-written closed
    form λ tr(E) I + 2μ E;
  2. the DERIVED tangent ∂²ψ/∂E² matches central FD of that stress in 12
    symmetric directions × 2 states.

Sabotage-checked (corrupting the reference fails the match). Full suite
323/323.

Scope

Deliberately the hyperelastic front-end only. The plastic return-map
front-end (yield f, hardening, flow) and the compile-time radial-return
reduction pass are separate, later slices, per #108's sequencing.

Note

diff(t2s → tensor) (energy → stress) was reachable all along — the earlier
"not linkable" symptom was a missing tensor_to_scalar_diff.h include, now added
to recipe.h alongside the existing tensor_diff.h.

Refs #108.

petlenz added 2 commits July 23, 2026 17:35
…erelastic_potential: S=dpsi/dE, tangent=d2psi/dE2)
…eject strain-independent energy/non-input strain + rollback on throw; non-constant + major-symmetry e2e + negative unit tests
@petlenz

petlenz commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

Folded a 3-lens critical review (architect + cpp-pro + code-reviewer)

All findings verified against the code before folding:

  • HIGH — desyncable strain name. The old signature took the strain both as a holder (used for the stress diff) and a separate strain_name string (used by the tangent pass, pass_bodies.h:377) — they could disagree and silently differentiate the tangent w.r.t. the wrong input. Fix: dropped strain_name; the name is recovered from the holder by node identity via m_tensor_symbols. Rejects a non-input/derived strain loudly.
  • HIGH — no dependence check. A strain-independent energy gave ∂ψ/∂ε ≡ 0 → inert zero material. Fix: LeafCollector check, throws loudly (mirrors the residual-leaf guard).
  • MED — weak tangent test. SVK's tangent is constant (linear stress ⇒ FD-exact), so the second-diff machinery was under-tested. Fix: added NonlinearFromEnergy (quartic ψ → non-constant tangent) with stress-vs-hand-written + tangent-vs-FD.
  • MED — no major-symmetry test. Added DerivedTangentIsMajorSymmetric (C_ijkl=C_klij, the Hessian property that most justifies deriving from a potential).
  • MED — non-atomic compound op. add_output committed before add_algorithmic_tangent could throw. Fix: roll back the stress output on throw (strong exception guarantee); unit-tested.
  • LOW — notation/doc caveats (input-leaf + symmetric-leaf), std::move the derived stress.
  • Added 4 negative unit tests in RecipeTest.cpp (there were none). Suite 330/330.

Sequencing note (architect #6, no code change): this helper eagerly derives-and-discards ψ. The later plastic/reduction slices of #108 need a STORED-potential representation + a lowering pass — they must NOT extend this eager pattern. Recorded in the method doc.

… bug) + test; add minor-symmetry check (the load-bearing property); dial back overstated major-symmetry/dependence-guard claims + fix false 'linearly-independent' comment
@petlenz

petlenz commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

Second-pass (adversarial) review of the review-fixes — it found a real bug + overstatements

Ran the lenses again scoped to the hardening delta. It earned its keep:

  • MED-HIGH (real bug I introduced) — state-variable strain accepted. The reverse-lookup scanned m_tensor_symbols, which also holds state-variable current/_old handles — so add_hyperelastic_potential(..., sv.current, ...) was accepted and silently differentiated a "stress" w.r.t. an internal state variable (empirically confirmed: no throw, output+tangent emitted). Fixed: require Category::Input (cross-check m_inputs_cache); added RejectsStateVariableStrain test. Also fixes the empty-name sentinel misdiagnosis (finding 2) via an is_input flag.
  • MED — my "high-value" major-symmetry test was near-tautological. Major symmetry is automatic for any symbolic Hessian (Clairaut), so it can't catch a value error. The property that actually matters — minor symmetry (conferred by the roles::Strain leaf, and genuinely droppable) — was untested. Fixed: added minor_symmetric() and both checks; corrected the overstated comment.
  • LOW — the dependence guard is leaf-PRESENCE, not a gradient check (ψ=dot(E)-dot(E) passes yet diffs to zero). Comment corrected to say exactly that.
  • LOW — a comment I'd added was false ("12 linearly-independent symmetric directions" in a 6-D space). Corrected.

Verified sound by both lenses: the rollback, move-safety, exception types, the nonlinear math/reference, and that the name-based dependence check is consistent with cas::diff's own hash-based leaf match.

Acknowledged residual gaps (not fixed here): (1) no positive test for a non-symmetric (roles::DeformationGradient) energy-derived material — the minor-symmetry check would catch an asymmetry but there's no example exercising it; (2) AlgorithmicTangentPass::find_tensor_input has the same accepts-state-var scan (pre-existing, broader than this PR); (3) the cancelling-energy degenerate case. Worth follow-ups. Suite 331/331.

petlenz added 2 commits July 24, 2026 22:58
…(psi(F), roles::DeformationGradient) FD-verified + tangent major-but-not-minor-symmetric (also proves minor_symmetric() discriminates)
…rmutations) instead of hand-rolled index loops
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant