Skip to content

Viscoelastic stress past the conformation limit: a positive-definite convected step and a log-conformation history - #795

Open
lmoresi wants to merge 1 commit into
bugfix/stress-history-unitsfrom
feature/log-conformation
Open

lmoresi wants to merge 1 commit into
bugfix/stress-history-unitsfrom
feature/log-conformation

Conversation

@lmoresi

@lmoresi lmoresi commented Sep 26, 2026

Copy link
Copy Markdown
Member

Stacked on #789. Lets the viscoelastic Stokes solve reach the high-Weissenberg regime of the benchmark flows: with it the creeping UCM cross-slot shows its purely elastic pitchfork, where before every run from De 0.5 stalled or hung.

What lost the conformation, measured on the cross-slot at De 0.8:

  • The step. The upper-convected BDF-1 step is F c* Fᵀ (F = I + dt L) less its second-order term dt² L c* Lᵀ, so it goes indefinite once dt|L| is of order one. A smaller timestep did not help: at a corner |L| grows with resolution.
  • The store. Interpolation, projection and per-cell fits undershoot near a corner singularity, and the forward store's linear fit extrapolates to the cell edges; the stress read back was indefinite although every stored value was positive.

Two options on ViscoElasticPlasticFlowModel (upper-convected, first order):

  • convected_step="deformation" keeps the dropped term: positive-definite for any step, still first order. The exponential step completes the relaxation target's stretch to a product the same way.
  • stress_history="log_conformation" (2-D) stores ψ = log c and reads σ* = G(exp ψ* − I); it implies the deformation step. The flavours are untouched: each transports its stored tensor by pure advection, and the step stays on c, where relaxation is linear, so neither integrator changes.

The model owns the encoding. The default NavierStokes decodes its history flux; SNES_NavierStokes and the multi-material model refuse the log store. stokes.tau projects the decoded stress; the health check reports where the logarithm's floor acted.

Result, cross-slot, full resolution, forward history, seeded: growth rate of the asymmetry −0.77, −0.24, +0.06, +0.27 at De 0.5, 0.65, 0.72, 0.8 (De = λU/H, H the half-width), onset near 0.71, against 0.62 expected from Poole et al. (full-width 0.31). The conformation stays above 0.33 everywhere.

Tests (test_1065, and log cases in test_1064): the one-step extension closed form for both integrators and both representations, plus the linear step's indefinite −0.818; the shear start-up across four flavours and both integrators against the discrete recurrence in σ_xy and N1; the units model. The stress-history, swarm and advection files pass (316); the style gate is clean.

Limits: first order; the log history 2-D only; not for stresses that are not conformations (geodynamic VEP with no objective rate).

Underworld development team with AI support from Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_017kSkAq7oJ5J3XisuvLBovo

…convected step and a log-conformation history

Two things lose the conformation c = sigma/G + I at a re-entrant corner or a
stagnation point at high Weissenberg number, measured on the cross-slot:

- The step. The upper-convected BDF-1 step is F c* F^T (F = I + dt L) less its
  second-order term dt^2 L c* L^T, so it goes indefinite once dt |L| ~ 1.
  convected_step="deformation" keeps the term (source += dt(L s* L^T + G L L^T)):
  positive-definite for any step, still first order. The exponential step
  completes the relaxation target's stretch to a product the same way, written
  in x = dt/lam rather than the integrator's alpha, which is clamped to one in
  the elastic limit. max_elastic_timestep reports no limit for this step.
- The store. Interpolation, projection and per-cell fits undershoot near a
  corner singularity, and a fit extrapolates to the cell edges.
  stress_history="log_conformation" stores psi = log c; the model reads
  sigma* = G (exp psi* - I), a conformation whatever was done to psi. It implies
  the deformation step. Every flavour transports its stored tensor by pure
  advection, so none changes; the step stays on c, where relaxation is linear.

The model owns the encoding (encode_history; the decode is its carried-stress
read); the solver stores the model's record (one helper for the flux and the
commit), builds a log store dimensionless, and hands the history the encoding
for the inflow datum. The default NavierStokes decodes its history flux;
SNES_NavierStokes, which reads the history as a flux, and the multi-material
model refuse the log store. stokes.tau projects the decoded stress. The health
check reports where the logarithm's floor (1e-12, round-off only) acted.

Tests (test_1065): the one-step extension closed form for both integrators and
both representations, and the linear step's indefinite -0.818; the shear
start-up across four flavours and both integrators against the discrete
recurrence in sigma_xy and N1 (every deformation term lives in sigma_xx);
units-model cases in test_1064.

Underworld development team with AI support from Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kSkAq7oJ5J3XisuvLBovo
@lmoresi

lmoresi commented Sep 26, 2026

Copy link
Copy Markdown
Member Author

Adversarial review before the push (two reviewers: the units change and this branch, briefed for over-complexity as well as defects). Applied here:

  • One option, not a matrix. The log history implies the deformation step and refuses the linear one; the linear step could hand it an indefinite conformation, which the floor then hid.
  • One owner of the encoding. The model's encode_history and its carried-stress read; one solver helper stores the record for both the flux and the commit (the fallback chain was written three ways); the history gets the encoding once, for the inflow datum, read at one place. Removed: the private maps pushed into the history, the _inflow_value property and its __dict__ access, three of the five encode/decode methods.
  • Readers that took ψ as a stress. The default NavierStokes flux (decodes now), stokes.tau (projects the decoded stress, not the next step's predictor), the multi-material model (refuses).
  • The exponential term at the elastic limit. Written in x = dt/λ; the integrator's α is clamped to one there and the old form divided by zero.
  • The floor. 1e-12 (round-off only after a positive-definite step), not 1e-3, which clipped legitimate conformations such as 1/(1+2Wi) in extension; the health check now reports where it acted, since a decoded conformation is positive by construction.
  • max_elastic_timestep reports no limit for the deformation step; the order guard sits where the order is set.
  • Tests. The shear box checked only σ_xy, where every new term vanishes; it now follows the discrete recurrence in N1 too, and the exponential deformation term has its closed form.

Recorded: set_initial_history takes stored values (documented), while the inflow datum is given as a stress; a checkpoint does not record which representation it holds.

lmoresi added a commit that referenced this pull request Sep 27, 2026
…Stokes velocity history offers every semi-Lagrangian scheme

global_evaluate applied monotone="clamp" on the rank that asked for a point,
bounding it by that rank's nearest nodes; for a departure point evaluated on
another rank those are the wrong neighbourhood. The bound now runs on the
evaluating rank (first pass and containment round), on non-dimensional values
like the nodal data it compares with. Serial results are unchanged. #682's
level-set reproduction (LeVeque swirl, 128^2 quad, P2, SLCN with clamp):
volume at np 1/2/4/8 now 0.0706771442/415/438/412 (np 8 was 1.6% off).

NavierStokesSLCN(velocity_transport=...) chooses the velocity history among
the four semi-Lagrangian schemes, through one _value_history helper shared
with AdvDiffusionSLCN(transport=...). Forward schemes need order=1; the
forward integration-point fit is linear and refuses a P2 velocity.

tests: test_1103 (lid-driven cavity, Re 100, three schemes against recorded
values and each other); test_1066 adds the cavity (equal to serial at np
3/4/6). tests/parallel at np 4: 168 passed, 1 xfailed, 1 failed
(test_1063_constrained_freeslip_parallel[ti], fails identically on #795's
05d5248). Serial set: 393 passed.

Underworld development team with AI support from Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017kSkAq7oJ5J3XisuvLBovo

This branch has not been deployed

No deployments
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