Skip to content

Dissipation positivity check - #1771

Open
IvarStefansson wants to merge 5 commits into
dissipation-driven-historyfrom
dissipation-positivity-check
Open

Dissipation positivity check#1771
IvarStefansson wants to merge 5 commits into
dissipation-driven-historyfrom
dissipation-positivity-check

Conversation

@IvarStefansson

@IvarStefansson IvarStefansson commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Assert dissipation positivity

Stacked on #1768 — the base branch is dissipation-driven-history, not develop.

Adds two parameter guards and a runtime check that the frictional dissipation
D = (μ* − tanψ)σ_n is non-negative at every converged solve.

What it is for. μ* − tanψ = μ_b(1 + tan²ψ)/(1 − μ_b tanψ) + μ_p is an algebraic identity, so with admissible parameters the dissipation is non-negative at every reachable state: the clips hold tanψ ≤ tanψ_0, and the guards hold μ_b tanψ_0 < 1, μ_b ≥ 0 and μ_p⁰ ≥ 0. The new runtime check therefore cannot fire on parameters. It catches an implementation error, and specifically one that loses or negates the μ_b term, that being the only term large enough to carry the sign. It also guards a future law in which μ_b or ψ_0 becomes state dependent, where a pole crossed mid-run would pass a check made once at construction.

The check is opt-in, via its own mixin. constitutive_laws.py today contains zero lifecycle-hook overrides — constitutive laws are pure operator providers, and the file's only validation idiom is "evaluate a sub-operator inside the operator-returning method and raise" (_validate_sliding_composition, BartonBandis, SecondOrderTensorUtils). Putting after_nonlinear_convergence on AsperityStressPartition would make a law class join the solve lifecycle and would charge every user of the partition for the check. A separate mixin keeps the laws clean and puts "this model asserts its own admissibility" in the base list where a reader sees it.

Changes

  1. AsperityStressPartition.frictional_dissipation(μ* − tanψ)σ_n. Both factors are read through self, so it is the composed coefficient and the damaged, partitioned dilation angle the model actually uses. Distinct from the total frictional work μ*σ_n that drives the wear, which includes the dilational part deliberately.
  2. μ_p⁰ ≥ 0, validated in ploughing_friction_coefficient where the value is produced, so that what is checked is unambiguously the intact coefficient rather than the damage-scaled one.
  3. μ_b ≥ 0, alongside the existing pole check in _validate_sliding_composition. Both are parameter-level preconditions of the same identity.
  4. DissipationPositivityCheck — overrides after_nonlinear_convergence and raises, naming the offending cell count, the minimum and its index. Mixed into FractureDamageMomentumBalance. Zero is admissible and not reported: it is the frictionless limit.

Tests

test_dissipation_is_positive tightened from the cell mean to the smallest cell value — identical on the uniform states it prescribes, but a positive mean can hide negative cells. New tests cover the dissipation against a closed form in Pa, the check firing when the basic friction term is removed, the check silent on a correct composition, the frictionless limit accepted, and both parameter guards.

Mutation-tested, since a check nobody has made fire is untested scaffolding: dropping the characteristic_contact_traction factor fails the closed-form cases, and the firing test asserts D < 0 before expecting the raise so it cannot pass vacuously.

The check adds no operator to any equation, so results are unchanged.

The states the test prescribes are spatially uniform, so the two agree
numerically. The difference is in what is guaranteed: a mean stays positive
while individual cells go negative, and the property being checked is
pointwise.
Asperities resist being sheared through; they do not assist. The sign
matters beyond its own term: mu_p >= 0 is one of the two preconditions
under which mu* - tan psi is positive term by term, the other being the
pole the sliding composition already guards.

Checked where the value is produced rather than where it is used, so the
quantity validated is unambiguously the intact coefficient rather than the
damage-scaled one.
D = (mu* - tan psi) sigma_n, the shear resistance less the part recovered
as dilation. Distinct from the total frictional work mu* sigma_n that
drives the wear, which includes the dilational part deliberately.

Positivity is an algebraic identity given the two conditions the sliding
and ploughing guards check, so a non-positive value means the composition
was assembled wrongly rather than that the parameters are marginal.
The frictional dissipation is non-negative by an algebraic identity, so a
negative value means the law is composed wrongly rather than that the
parameters are extreme. Check it once per converged nonlinear solve and
raise, since a fracture that produces energy by sliding invalidates every
number in the run.

The identity's basic-friction term exceeds tan psi for any realistic mu_b,
so it is the term that carries the sign; a mis-ordering costing only the
ploughing term stays admissible and is left to the closed-form tests.
Friction resists sliding. The sign matters beyond its own term: mu_b enters
the one term of mu* - tan psi large enough to carry the sign, so it is the
only parameter that makes a correctly composed law dissipate negatively.

Checked alongside the pole, both being parameter-level preconditions of the
same sliding composition. This leaves the standing dissipation check
guarding the composition and any future state dependence of mu_b or psi_0,
rather than the parameters.
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