Visualisation: principal-stress glyphs and stress trajectories - #601
Visualisation: principal-stress glyphs and stress trajectories#601lmoresi wants to merge 6 commits into
Conversation
Sample the stress tensor at seed points, the way velocity arrows sample the velocity. principal_stress_glyphs draws one bar per principal axis (blue compressive, red tensile, length = magnitude; a cross in 2-D, three orthogonal bars in 3-D), and direction_trajectories integrates the principal direction field - defined only mod 180 degrees, so the integrator carries orientation continuity and places lines evenly (Jobard-Lehmann occupancy). plot_stress_glyphs is the one-call wrapper beside plot_vector, with default seeding filtered to points inside the mesh (an annulus seeds nothing in its hole). Docs page (docs/advanced/stress-visualisation.md) covers the pressure gauge caveat and why map-view regime colouring is degenerate in 2-D plane strain. Tests cover the glyph geometry, the eigenvector sign-flip case an ordinary streamline integrator gets wrong, and the annulus seeding. Underworld development team with AI support from Claude Code
The grid guess (extent / num_seeds) mis-sizes bars whenever the caller passes section-plane seeds; use the mean nearest-neighbour distance instead, subsampled to keep the pairwise matrix small. Underworld development team with AI support from Claude Code
Adversarial reviewWe attacked the diff along the ways a glyph routine can silently draw the wrong thing. Found and fixed (32b397d): with user-supplied Checked and held:
Known limits, stated in the docs rather than papered over: closed trajectory orbits are traced once per integration sense (cosmetic overdraw); 3-D trajectories are deliberately out of scope (surfaces, not curves); the compressive/tensile split is relative to the pressure gauge, which can flip colours but never rotate axes. |
A scalar MeshVariable's .sym is a 1x1 Matrix; the recipe as written nested matrices. Caught by running the recipe against the fault examples. Underworld development team with AI support from Claude Code
The docstring promised a plotter callers could decorate, but show() ran unconditionally first and a finalized scene ignores later actors - overlays were dropped silently. With show=False the camera is set and the plotter left open; callers add overlays and screenshot. Caught by driving the 3-D sinker example through the wrapper. Underworld development team with AI support from Claude Code
Validation against the fault-interaction examplesWe drove the landed API end-to-end over the checkpointed fault-interaction models (nested listrics, en-echelon, blind thrusts, all at w = 0.01) and the 3-D sinker: The exercise caught two more defects, both fixed:
Tests still 6/6 after both fixes. |
The figures now come from the API demonstration script driving the checkpoints: thrust, listric, and en-echelon each get the two-panel crosses + trajectories treatment, with the strain-rate second invariant behind at low opacity so the quiet wedges and relay lobes read without competing with the glyphs. The sinker figure gains its cube and sphere overlays via plot_stress_glyphs(show=False). Underworld development team with AI support from Claude Code
Development CI is green; this branch hung from 90% to the 2-hour cap (run 32097850915) with one worker lost to the VTK crash genre. The annulus test was the first in CI to drive plot_stress_glyphs through its default show=True, and show() on a headless runner can enter an interactor wait. The test asserts what the plot builds, not what it renders, so it now uses the show=False path and no render happens anywhere in the file. Underworld development team with AI support from Claude Code
CI: first run timed out — diagnosed and fixed (d69839d)The first test run lost a worker at 78% (the intermittent VTK-render crash genre) and then hung from 90% until the 2-hour cap. Development is green on the same configuration, so the cause was in this branch: the annulus test drove The test now uses the |
Adversarial reviewReviewed at the PR head. Three findings, two checks that came back clean, and 1. Degenerate points are the failure mode this integrator has, and nothing That is inherent to the object rather than a defect in the code, and it is worth 2. 3. Checked and cleared.
Link to #602. Underworld development team with AI support from Claude Code |
What this adds
A way to see the stress tensor, in the same spirit as velocity arrows: sampled at seed points, not drawn everywhere.
principal_stress_glyphs(coords, stress, scale)— one bar per principal axis at each seed: length proportional to the principal-value magnitude, with a"tensile"cell array so the two signs colour separately (blue compressive, red tensile, matching the RdBu_r field convention). A cross in 2-D; three orthogonal bars in 3-D.direction_trajectories(direction_at, seeds, inside, step, separation)— stress trajectories. A principal direction is defined only mod 180°, so ordinary streamline tools cannot integrate it; this integrator sign-aligns each evaluated eigenvector with the previous heading, and places lines evenly (Jobard–Lehmann occupancy with separate seed-blocking and line-stopping tests). 2-D only — in 3-D the analogue is a trajectory surface, which we do not attempt.tensor_fn_to_pv_points,trajectories_to_pv_lines— the evaluation and bundling steps, mirroring the existing*_fn_to_pv_pointshelpers.plot_stress_glyphs(mesh, stress, ...)— the one-call wrapper besideplot_vector. Default seeding is a grid over the bounding box filtered to points inside the mesh, so an annulus seeds nothing in its hole.Docs page
docs/advanced/stress-visualisation.mdwith worked figures (a blind-thrust fault network and a 3-D Stokes sinker, both rendered from checkpoints). It records two facts a user needs: the pressure datum is a gauge that can flip bar colours but never rotate axes, and World-Stress-Map-style regime colouring is degenerate in 2-D incompressible plane strain (the out-of-plane stress is always the intermediate principal stress), so it is deferred until there is a 3-D use case.Tests
tests/test_0848_stress_glyphs.py(level_1, tier_a): glyph geometry for uniaxial compression, pure shear, and a 3-D diagonal tensor; the eigenvector sign-flip case that a naive integrator fails by reversing mid-line; trajectory separation; and the annulus default-seeding case end-to-end throughplot_stress_glyphs. All pass in the worktree environment; the style gate is clean.Underworld development team with AI support from Claude Code