Skip to content

JIT: a manifested constants[] UWexpression can be baked into the C source (not live-rampable) #302

Description

@lmoresi

Summary

A constant UWexpression that is collected into solver.constants_manifest can nonetheless have its value baked into the compiled C source, so changing its .sym between solves has no effect — the parameter is not live-rampable even though it is listed as a live constant. A full solver rebuild (is_setup = False + re-set the constitutive parameter) is required to pick up the new value.

This breaks the documented contract that a manifested constant can be updated via .sym and repacked through constants[] without recompilation. It is the mechanism the δ-continuation / homotopy work relies on, and it works for some parameters (δ) but silently fails for others (a rate-strengthening viscosity floor ξ).

Observed behaviour (Stokes, ViscousFlowModel, Drucker–Prager + Perzyna floor)

Viscosity contains two rampable constants defined identically as uw.expression(...):

  • δ (soft-min smoothing): appears as δ² inside sqrt((f-1)² + δ²) where f depends on the strain-rate field. Live-ramps correctly — setting δ.sym and solving picks up the new value (the δ-march converges as expected).
  • ξ (Perzyna floor): appears as ξ·η_bg in η_p = σ_y/(2·ε̇) + ξ·η_bg. Does NOT live-ramp — setting ξ.sym between solves leaves the solution unchanged; only a rebuild propagates the new ξ.

Both appear in constants_manifest (verified by printing [e.name for i,e in solver.constants_manifest]\xi is present at a valid index). So ξ is listed as a live constant but its value is baked in the generated C — updating constants[] for its slot does not change the residual.

Verification was via full solves (a warm ξ-descent): without a rebuild between ξ steps the converged solution (and a band-thickness diagnostic) is frozen across all ξ; with a rebuild per ξ it varies correctly. (Note: a quick snes_max_it=0 + getFunctionNorm() residual probe is not a reliable test here — it returns a lagged norm — so please validate any fix with a real solve.)

Likely mechanism (hypothesis for the maintainers)

constants_manifest is built by _extract_constants / _collect_constant_atoms on the pre-unwrap callbacks (so ξ is found and listed). The C source is generated from the post-unwrap expression. The nondimensionalisation/unwrap appears to fold pure-constant subexpressions to numbers: ξ·η_bg is a product of two constants and collapses to a literal, so ξ never reaches the substitution that would route it through constants[]. δ survives only because its sole occurrence is inside a field-dependent subexpression ((f-1)² + δ²) that cannot fold. Net: a manifested constant whose every occurrence is inside an otherwise fully-constant subexpression gets baked, and the manifest/​C-source disagree.

If correct, reformulating ξ so it always multiplies a field (e.g. the natural Perzyna numerator ξ·η_bg·2·ε̇ before dividing by 2·ε̇) might keep it live — but this is untested (the fold may still occur after sympy simplification), and the deeper fix is to make _extract_constants and the C-source generation agree (either don't fold a subexpression that contains a manifested constant, or substitute manifested constants before the fold).

Impact

  • Silent: no error; the parameter simply doesn't update, which can invalidate continuation/homotopy studies that ramp such a parameter in place.
  • Same family as the previously-noted "constant in an exponent doesn't ramp" JIT gotcha.

Suggested checks

  1. Make constants_manifest and the emitted C source consistent — a listed constant must be read from its constants[] slot, never baked.
  2. Add a guard/warning when a manifested constant is detected to have been folded out of the C source (manifest lists it but the code never references its slot).
  3. A minimal repro: two uw.expression constants in a viscosity, one multiplying a field, one multiplying only other constants; ramp each .sym and confirm via a real solve which propagates.

Reported from the yield-homotopy investigation (Spiegelman notch, harness convergence.py); happy to provide the full harness or help build a minimal repro.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions