Repository navigation
Conversation
SNES_Vector built its finite element with mesh.dim components while the unknown MeshVariable, the residual and the Jacobian blocks were already sized by the embedding dimension, so on a SphericalManifold (dim 2, cdim 3) the field and the FE disagreed and the solve failed on a Vec-size mismatch. The FE, the PC block size and the default unknown now use cdim. The same component count applies in maths/vector_calculus (a tangential field on a manifold is still a cdim-component row) and in the JIT's truncation of sympy.vector objects. On a volume mesh cdim == dim and nothing changes. Measured: a thin-sheet manufactured solution (toroidal l=2) on a unit SphericalManifold at h=0.2, P2, relative nodal L2 error 4e-3. Underworld development team with AI support from Claude Code
…cous) uw.systems.ThinSheet solves -div_s(L sigma) + c u = f for the sheet velocity with the plane-stress closure sigma = tau + tr(tau) P, which eliminates the pressure: a vector problem on SNES_Vector, not a saddle point. Any deviatoric constitutive law applies; the solver's inner _Unknowns hands the model the in-plane strain rate P sym(grad u) P through Unknowns.E (P = I on a flat mesh), and ViscousFlowModel.grad_u now reads Unknowns.E as the VEP model already did (identical on a volume mesh). Tests (closed forms): uniaxial stretch with free edges gives the Trouton stress 3 eta edot and lateral contraction edot/2; a rigid motion carries no stress; a uniform load against basal drag gives u = f/c; a manufactured solution converges at third order with P2. Design: docs/developer/design/THIN_SHEET_VISCOELASTIC_DESIGN.md. Underworld development team with AI support from Claude Code
…d with Stokes The viscoelastic stress history (its transport choice, the three per-step hooks, the history factory and the `tau` read) moves from SNES_Stokes into _StressHistoryMixin, a pure move apart from sizing the stored tensor by the embedding dimension (identical on a volume mesh) and a no-op DEVSS hook that Stokes overrides. ThinSheet.solve(timestep=) runs the same sequence around its momentum solve, so any constitutive model with a stress history works in the sheet unchanged. Tests (closed forms): the exponential integrator reproduces the Maxwell uniaxial response 3 eta edot (1 - exp(-t mu/eta)) at every step; backward Euler reproduces its own recursion; Elsasser stress diffusion into an elastic sheet over a viscous substrate (D = mu L / c) approaches the erfc front at first order in the timestep. Stokes regression (1050/1051/1052/ 0070) unchanged. scripts/sessions/thin_sheet_elsasser.py draws the profiles. Underworld development team with AI support from Claude Code
The rotated strong-constraint machinery (add_rotated_freeslip_bc, add_fault_bc, the constraint-mechanism guard, the reaction recoveries and the solve dispatch) lived on the saddle-point class. It moves to SolverBaseClass as _solve_rotated_if_registered, called by the Stokes solve and now by SNES_Vector.solve, so a thin sheet takes a split-node fault the way Stokes does. SNES_Vector registers its one field under the name the rotated path addresses it by; rotated_bc gates its pressure sites on the presence of a pressure field and solves the rotated operator of a vector solver with one KSP (custom-prolongation FMG when a hierarchy exists, else the shared GAMG bundle at the solver's block size). Tests (thin viscous sheet, plane-stress elasticity with nu = 1/2): a frictionless fault along or across a uniaxial stretch carries no shear traction and leaves the exact solution untouched (nodal 1e-9); a viscous interface approaches the welded sheet at first order in 1/eta_f; a frictionless fault under remote shear slips as the Griffith mode-II crack, (4 tau / E') sqrt(a^2 - x^2) with E' = 3 eta, within 8% (2 x 2 box, a = 0.2, h = 1/25). Stokes regression on the moved code: 22 rotated/fault tests and 17 viscoelastic tests unchanged. Underworld development team with AI support from Claude Code
…rojector A sheet on a SphericalManifold carries its velocity in the embedding space. Tangency u.n = 0 is a rotated strong constraint at every node (rotated_bc's new SHEET_TANGENCY spec, enumerated over all velocity nodes with the analytic surface normal), so the solve takes the same path as rotated free-slip and the split-node fault pairs. rotated_bc's component and coordinate strides are the embedding dimension (identical on a volume mesh) and the rigid-rotation gauge offers the three Killing fields when the embedding dimension is three. The in-plane projector uses the cell's facet normal, not the analytic one: the assembler differentiates along the flat facets, and with the analytic normal a rigid rotation kept a spurious strain of order h (0.7% of the velocity at h = 0.25). With the facet normal the rotation is a discrete solution (residual 2e-14) and is returned to 2e-10. SphericalManifold cleared nothing after setting dm_plex_gmsh_spacedim, so every later gmsh mesh in the process loaded with three coordinates (a 2-D box after a sphere failed on a 3:2 vector-length mismatch); the option is now cleared once consumed. Tests: rigid rotation exact against basal drag (Killing field); toroidal manufactured solution at second order with tangency at machine precision. Underworld development team with AI support from Claude Code
A fault polyline given with three coordinates is read as great-circle arcs about the origin: line_cut's chord primitives become segment objects (a chord in the plane, an arc on the sphere) with the same cut algorithm on top; the containing-cell test, the tip placement and the signed cell area take their surface-mesh forms (the area signed against the outward direction, the mesh's winding convention read off the majority). reconnect orients a surface cell by its normal against the centroid, and the chain orientation lives in the embedding dimension. The contact frame of a coincident pair on a surface mesh is the in-surface conormal of the fault edge (PETSc's FVM normal is undefined for an edge in three dimensions), the trace tangent, and the surface normal — orthonormal- ised, because the conormal is perpendicular to the facet while the surface normal at the node is the analytic one: an O(h) misfit made the pair rotation non-orthogonal and released a normal mode the sheet has no stiffness against (a Killing field came back 5e-2 off, the same from LU). The pair block pins the jump along the conormal and both sides' tangency (the jump and the mean along the surface normal); the slip row stays free; a paired node leaves the sheet-tangency wall set. Strides in fault_contact are the embedding dimension and the interface assembler has one slip component on a surface. Tests: a rigid rotation (zero surface strain, so no traction on the fault) is reproduced exactly across a frictionless arc with zero slip; a short arc under the shear of the toroidal flow slips as the plane-stress Griffith crack within 12% at a/R = 0.25. The 2-D fault suite (line cut, split, contact, API, surfaces, placement, sheet faults) is unchanged: 110 passed. Underworld development team with AI support from Claude Code
…g dimension A stress history on a surface mesh is a 3x3 tensor. The symmetric-tensor data layout and Voigt list of a MeshVariable, the history projection's component count, the Eulerian history's velocity check, the velocity-trace variables and the objective-rate term were all sized by the topological dimension; they now use the embedding dimension (identical on a volume mesh). ThinSheet carries its history on the grid on a manifold (the Eulerian streamline-upwind transport): the semi-Lagrangian trace-back leaves the faceted surface and is not validated there. Test: with the grid history a rigid rotation stays the exact solution of the Maxwell sheet on the sphere step after step (3e-10), with the carried stress at round-off and tangency at machine precision. Flat viscoelastic sheet and Stokes shear-box tests unchanged. Underworld development team with AI support from Claude Code
…the facet topology The interface assembler chose its quadrature by the component count, which became the embedding dimension on a surface mesh and so picked the triangle rule for edge facets (six nodes against three). It now follows the facet topology; the component count still sizes the strides. Test: a viscous interface law on a great-circle arc under the toroidal shear reduces the slip at first order in 1/eta_f. Underworld development team with AI support from Claude Code
ThinSheet.stress_transport refuses anything but the Eulerian (streamline- upwind) history on a manifold mesh, where the semi-Lagrangian trace-back leaves the faceted surface; the default is already the grid history. Test: the refusal, and that the default reads "eulerian". Underworld development team with AI support from Claude Code
…ts, histories A subsystem page for uw.systems.ThinSheet — the plane-stress closure, the closed forms the tests pin, the facet-normal projector against the analytic tangency normal on a sphere, great-circle faults and their pair frame, the grid-carried history on a manifold, and the known limits — linked from the developer index. The design record notes the facet-normal finding. Underworld development team with AI support from Claude Code
…ng dimension write_timestep(petsc_reload=True) reshaped the coordinate vector by the topological dimension and failed on a surface mesh (a reshape ValueError, seen in the sphere fault-cycle run); it now uses the embedding dimension (identical on a volume mesh). Test: a vector field on a SphericalManifold round-trips exactly through the in-place reload. Two further manifold defects found while testing, marked TODO(BUG) and not fixed here: the default DMPlex-migration reload segfaults inside PETSc on a manifold, and the XDMF writer labels triangles in three-space as hexahedra. Underworld development team with AI support from Claude Code
…erence, uplift rate scripts/sessions/thin_sheet_fault_cycle.py runs a Coulomb fault in a Maxwell sheet dragged by a shearing substrate, flat or on the sphere. Snapshots show the Coulomb failure change on fault-parallel receivers against a reference sheet with the fault welded, on the same split mesh and solved alongside (a reference on the unsplit mesh differed by interpolation at the cut's moved vertices), and the implied isostatic uplift rate from the thinning rate. The fault traction and slip rate are recorded every step; checkpoints are reloadable. Underworld development team with AI support from Claude Code
The TODO(BUG) on read_checkpoint now records what was observed against the current reload design (#674, #640): the reconstructed-mesh path segfaults on a manifold when called on the mesh that wrote the file, where a volume mesh round-trips exactly through the same call; the in-place path is exact on the manifold; and the path's intended use, a mesh rebuilt from its checkpoint, cannot yet be tried on a manifold because the h5 mesh round-trip drops the coordinate dimension first. Underworld development team with AI support from Claude Code
…e-back reads across a fault With a sliding Coulomb fault in a Maxwell sheet, the semi-Lagrangian history produced a period-2-step oscillation of the near-tip slip rate (sign reversal at 77% of steps, amplitude doubling at half the timestep, unchanged by a 1e-11 solver tolerance) and patchy stress along the trace. Its trace-back samples the history by point evaluation, which within about h/2 of a split-node slit can read the other side of the fault. The grid history (Eulerian SUPG) removes both: reversals at 2% of steps, no amplitude, the same yield time and mean slip rate, and a sixth of the Newton work. ThinSheet now uses it by default on every mesh; a flat mesh may still choose another. TODO(BUG) on SemiLagrangian.update_pre_solve: a Stokes viscoelastic model with a split fault uses that history by default. Thin-sheet tests 1075-1079: 19 passed with the new default. Underworld development team with AI support from Claude Code
…rt and order options -uw_tolerance, -uw_eta_v (a viscous term eta_v V added to the Coulomb law, registered through fault_contact's private _register_law with a TODO(FEATURE) for a public route), -uw_transport and -uw_order / -uw_integrator: the options the sawtooth diagnosis used. Underworld development team with AI support from Claude Code
The Eulerian SUPG history (its advection term, its stabilisation scale and its transport-step residual) and the Eulerian advection-diffusion solver (its velocity check and its diffusive flux) summed or sized over the topological dimension. On a sphere surface mesh the velocity and the surface gradient have three components, so the z term of the advection was dropped; they now run over the embedding dimension (identical on a volume mesh). Tests on a SphericalManifold, a rigid rotation about the x axis carrying z to -y in a quarter turn: through the grid stress history (relative error 1.126 before the fix, 2.27e-3 after) and through uw.systems.AdvDiffusion (a shape error before the fix, 2.27e-3 after). The stress-transport, SUPG advection API and thin-sheet suites pass (61). The Eulerian Navier-Stokes solver has the same topological sizing in several places; it is not used on a surface mesh here and is left as it is. Underworld development team with AI support from Claude Code
TODO(BUG): its velocity-shaped fields, SUPG terms and speed estimates are sized by mesh.dim, which on a surface mesh drops a velocity component — the defect just fixed in EulerianSUPG and the Eulerian AdvDiffusion. Left unfixed; the solver is not used on a surface mesh yet. Underworld development team with AI support from Claude Code
…lane The history is transported component by component in the embedding space, so a tensor carried along the curved surface keeps its Cartesian components while the tangent plane under it turns. It acquired normal components (31% of the stress by t = 3, measured on an unfaulted sheet driven by a divergence-free toroidal flow), which the plane-stress closure reads as a trace and so as a pressure. On a manifold the sheet now projects every stored level onto the tangent plane, P sigma* P with the analytic normal at each node, after the transport and after the commit: tangent to round-off. That sheet still thickens at both poles (uplift rate 0.15). This is the advection of the stress, a large-strain effect quadratic in the flow at a Weissenberg number of about two: with the transport switched off it falls to 6e-6, and a Jaumann rate does not change it. The test records this and asserts the tangency. Underworld development team with AI support from Claude Code
…ing) Work in progress, set aside to merge development: the objective step as a truncated expansion of exp(dt K) sigma exp(dt K^T) or the Cayley rotation (objective_order), the spin of a sheet element on a curved surface, the carried stress frozen in the Picard tangent, the history projected only after the commit (placement under measurement), and the design note on one model for thin and thick sheets. Tests and measurements follow. Underworld development team with AI support from Claude Code
The graph route converted sympy.vector Vector and Dyadic objects with the topological dimension, which drops the third component on a surface mesh in three-space. The expanded route was corrected the same way earlier (aeb0293); the graph route, merged since, carried the old slice. Underworld development team with AI support from Claude Code
…linearised in Newton Completes 978b540. A viscoelastic model's carried stress is taken through the step at objective_order: q truncates exp(dt K) sigma* exp(dt K^T) at total order q in dt (K = W Jaumann, L upper-convected; q = 1 is the existing explicit source and stays the default for upper-convected), or "cayley" rotates it orthogonally (the Jaumann default). The rotation reads the solver's spin, which on a curved sheet includes the turning of the normal. The carried stress is frozen in the Picard tangent: differentiating the Cayley rotation symbolically took tens of minutes per setup. A model now declares tangent_surrogates, differentiable stand-ins for its frozen atoms (here the first-order step); in Newton mode the solver swaps them into its own flux before differentiating, so a solver's closure stays in the tangent. Measured on the sphere, 4 steps: Picard 9 -> 14 iterations a step, rising; Newton with the stand-in 5, steady, 31 s against 40-51 s. Measured rigid quarter-turn on the sphere (h = 0.15, 40 steps): the history projected onto the tangent plane only after the commit, Cayley 8.4e-3, independent of dt (8.5e-3 at 80 steps) and 2.5e-3 at h = 0.075 (order 1.77 in space); projecting before the solve as well cost 4.0e-2. Tests (test_1080): the order's defaults and refusals; the Cayley step is the orthogonal rotation of the stress; a rigid quarter-turn of a flat sheet carries the stress to 2.9e-3; the Newton tangent converges to the frozen tangent's stress. Regression across stress transport, log-conformation, VE stability, Newton cold start, yield, the JIT, rotated constraints, faults and the thin-sheet suites: 157 passed. Underworld development team with AI support from Claude Code
TODO(BUG): a model's flux_jacobian replaces the whole F1 in the Newton tangent, so with one set the Stokes uu tangent loses the augmented-Lagrangian penalty and the DEVSS term, which both depend on the velocity gradient; a thin sheet's closure would be lost the same way. To be unified with the thin/thick alignment (solvers apply their own closure to the model's flux_jacobian; tangent_surrogates then retires). Underworld development team with AI support from Claude Code
…ly (slow test) The objective step on a curved sheet end to end: grid transport of the Cartesian components, the spin with the turning of the normal, the Cayley rotation, and the tangent-plane projection after the rotation. A quarter turn about x in 20 steps at h = 0.15 carries the stress to 9.4e-3 (measured; 8.4e-3 at 40 steps, 8.5e-3 at 80, 2.5e-3 at h = 0.075), tangent to round-off. level_3: 13 minutes. Underworld development team with AI support from Claude Code
… forcing The Newton tangent flux. A constitutive model's flux_jacobian is a deviatoric stress, like its flux; each solver now builds its momentum flux through one function (_flux_closure: Stokes and Navier-Stokes add the mechanical pressure with the penalty and DEVSS, the thin sheet the plane-stress trace term times the thickness) and applies it to the tangent flux too. Before, the tangent flux replaced the whole F1 (a Stokes Newton solve lost its pressure, penalty and DEVSS terms and diverged), and on vector and scalar solvers it was silently ignored: a 2-D Array was flattened with list(), which yields rows, and the error swallowed by a bare except. tangent_surrogates becomes the model's private _tangent_surrogates. Invariants. Every second invariant a constitutive model forms goes through Unknowns.second_invariant (the plain contraction by default, the same expression as before); the thin sheet adds the out-of-plane component M_nn = -tr M, so a plastic sheet in uniaxial stretch yields at the von Mises sqrt(3) tau_y instead of sqrt(18/5) tau_y. The sheet's API follows Stokes: ThinSheet(mesh, v, p, degree, p_continuous), with p = -tr(tau) as a diagnostic refreshed after each solve; bodyforce per unit volume (the sheet integrates it through its thickness); add_basal_drag(c, boundary, reference_velocity) and add_basal_traction on both solvers (natural conditions on Stokes). uw.maths.tensor gains resolved_traction and coulomb_failure_stress on any solver's stress. SNES_Vector copies its fields to the coarse levels before the field decomposition, which recurses into them (PETSc error 62 on any refined mesh since the decomposition was added for the rotated solves). Tests: test_1082 (tangent flux keeps the closure, and is live), test_1075 (forcing per unit volume with drag and traction, the pressure diagnostic, the von Mises yield, a refined mesh); test_108* batched in scripts/test.sh. Underworld development team with AI support from Claude Code
… second order
An unfaulted Maxwell sheet on the unit sphere dragged by the toroidal force
4A rhat x grad_s(z^2): the first-order flow is divergence-free, and the sheet
thins only through the Jaumann rate turning shear stress into normal stress
differences (the Weissenberg effect). The steady second-order perturbation,
derived in sympy (scripts/sessions/thin_sheet_quadratic_closed_form.py):
-div_s u / A^2 = -3 (65 z^4 - 126 z^2 + 29) / 533
= (24/91) P2(z) - (24/287) P4(z),
with no P0 and no odd degrees. ETD-1 relaxes the corotational term with the
effective time lambda' = alpha dt / (1 - alpha) in steady state, and the
thinning is linear in lambda, so the discrete steady state is the closed form
times lambda'/lambda. Measured at A = 0.1: P2 within 0.65 % (h 0.2, dt 0.2),
0.18 % (h 0.12, dt 0.1), 0.09 % (h 0.08); P4 converges at second order in h
(14 %, 5.6 %, 2.75 %); the dt dependence is entirely lambda'.
test_1084 (level 3, 7 min): h 0.2, dt 0.2, 20 steps; P2 to 2 %, P4 to 25 %,
P0 and the odd degrees below 2e-3 of P2.
Underworld development team with AI support from Claude Code
uw.meshing.extrude_sheet / Mesh.extrude(L, layers) build a layer of tetrahedra centred on a sheet mesh (flat, or radially from a spherical manifold). The sheet's boundaries become the layer's walls under the same names, its faces are Base and Surface, and a split-node fault of the sheet becomes a vertical fault through the whole layer: the slit is duplicated at every level, so the fault is open at the surface and the base, its tips are shared vertical edges, and the side labels and minus-to-plus pairing are rebuilt by joining mapped vertex tuples. (The 3-D splitter cannot make this fault: it keeps a patch rim welded and needs about nine cells of depth.) Each prism is cut into three tetrahedra by the vertex order, with a fault's minus copy taking its plus copy's place, so both sides triangulate alike. Cells are oriented as PETSc expects; with the other sign every volume integral flipped against the facet ones (the volume came out negative and a basal drag pushed). An unfaulted layer gets a geometric-multigrid tail that coarsens through the thickness first and then laterally through the sheet's own levels. A faulted layer gets none yet: custom multigrid on the 3-D rotated pair operator fails on its first application (marked TODO(BUG)); algebraic multigrid converges. So one model function runs thin or thick by changing the mesh and the solver class. test_1083: the layer's geometry and pairing; a dragged plate, thin against thick, mid-plane velocity within 0.72 %; the same plate with a frictionless fault, 0.53 % away from the slit and the slip within 1.6 % (L = 0.04, c L / eta = 0.04). Serial only for now. Underworld development team with AI support from Claude Code
…heet The same model, a sheet on the unit-sphere manifold and the shell extruded radially from it, dragged by a toroidal base: the difference is first order in L, as the thin-sheet limit predicts (the base has 1 - L/R of the mid-surface's area, and the drag-driven shear c L / eta scales with L too): 3.32 % at L = 0.04, 1.35 % at L = 0.02. In test_1083. extrude_sheet carries over only the sheet's labels that mark facets (a manifold labels the whole surface "Surface", which the layer's own surface face then collided with), and refuses a real wall whose name the base or the surface would take. Underworld development team with AI support from Claude Code
…lit sheet The layer's multigrid tail was extruded from the split sheet itself when the split mesh owned no tail (a plain refined base hands none down), so the one-layer coarse level carried the slit: coincident nodes, which the barycentric and RBF transfer builders cannot place, and the first cycle returned NaN (DIVERGED_PC_FAILED). It was first read as a failure of geometric multigrid on the rotated 3-D pair operator and the faulted layer was left on algebraic multigrid. The coarse levels now come from the split sheet's ancestors: the conforming cut it was split from (same nodes, no slit) for the through-thickness levels, the first ancestor with a refinement hierarchy for the lateral ones. Measured: the faulted layer's velocity block takes 14 iterations per solve against about 180 under algebraic multigrid. test_1083's faulted case asserts the layer runs custom FMG and converges. Underworld development team with AI support from Claude Code
build_rotation takes the operator's row layout from the solver's Jacobian. _build() leaves the SNES set up only on some paths: with essential conditions registered it is, without them (a drag-only layer) the matrix is never created, and build_rotation dereferenced a null Mat - a segmentation fault. The solver's own rotated solve sets the SNES up first, so production solves were unaffected; a direct call on a built solver was not. Now it sets the SNES up when the Jacobian does not exist yet. test_1085 (crashed with SIGSEGV before the guard). Underworld development team with AI support from Claude Code
…of the parallel path Underworld development team with AI support from Claude Code
mesh.add_fault carries a split-node fault through every add-on level of the inherited multigrid tail (the background is never cut), so the coarse spaces contain the discontinuity and the velocity iterations fall with refinement: 6, 5, 4 at k = 2, 3, 4 add-on levels against 13, 18, 23 with the welded tail, the slip identical to six figures. split_levels=False keeps the welded tail. utilities/fault_mg.py: the side-aware cell-located Lagrange transfer (fine cell centroid located in the coarse mesh, dual-basis weights, barycentric clamp; batched; serial CSR and an MPIAIJ builder that locates rank-locally and asks the other ranks for the nodes it cannot), the per-level pair rotation built from the level's own pairing, and the composition that removes each level's opening rows and drops unreached coarse unknowns instead of repairing them by injection (which duplicated columns and made the Galerkin operators singular in parallel). Every pair touching a split level takes this transfer, so the welded tail under a split finest now runs at np > 1 too (it returned NaN before). fault_split: 3-D sheet faults through add_fault (placed and split, placement options threaded through for bisection-refined meshes), the fault recipe recorded for replay on the levels, and a pairing that rides redistributions as a pair of DM labels: the migration star forest PETSc returns for an initial distribution does not index the stratified numbering. meshing/extrude: the layer's tail extrudes the split sheet and its split levels, and the extrusion runs in parallel (built on one rank, distributed, fault stars made rank-interior, pairing collected): 261 of 261 pairs coincident at np = 2, the faulted plate solving with its slip unchanged. Tests: test_1087 (tail structure, iterations against the welded tail, viscous fault, moving fault, the transfer's side awareness and exactness) and tests/parallel/test_1089 (np = 2 and 4; the thick twin extruded with its pairing). Subsystem page docs/developer/subsystems/fault-split-multigrid.md. Underworld development team with AI support from Claude Code
#756) Conflicts resolved with both sides kept where they meet: - fault_split: development's region-based redistribution (groups) carries the pairing through as labels when asked; add_fault takes cut / exclude / blind / across_seams and the 3-D placement options; the seam-normal override lives in the mesh-level pair helper, so coarse levels see it. - Mesh.add_fault: development's options plus split_levels; the level replay applies to cut faults only (a placed band's spines have no counterpart on the levels), runs finest-down, and stops with a warning at a level the faults cannot be carried onto (a one-cell junction ligament closes at the coarser spacing), leaving that level and those beneath welded. - custom_mg: the cell-located transfer and the orphan drop apply only when a COARSE level is split; a hierarchy whose only split level is the finest keeps development's builders, orphan repair and FAC patch. - rotated_bc: the same zeroRows-by-global-row fix on both sides, one copy. - Thin-sheet conflicts against development (solver Cython warm-up, the checkpoint read, field projection): development's code with the thin-sheet additions. Merged build: tests 1086, 1087, 1089 (np 2), 1083, 0845-0847, 1017, 1022, 0865 (np 2) and 0866 pass. Underworld development team with AI support from Claude Code
Repeated work has come from investigating without looking at what is in flight: the fault multigrid session of 2026-10-13 re-diagnosed parallel fault defects that PR #672, open for weeks, had already fixed, and then paid a seven-file merge. The bootstrap now requires fetching development and reading the open and recently merged PRs on the subsystem before an investigation, a branch or a design decision, naming them in the opening report, basing the work on current development, and raising a related PR that has sat open for more than about a week as a finding at the start. The branching guide says the same for feature branches. Underworld development team with AI support from Claude Code
…oad measurement Each level's split gathers its own fault star onto the rank holding most of it, which differs from level to level, so a transfer between stars on different ranks located most of the fault band remotely (measured: 48 % of the finest nodes at np = 2, 58 % at np = 8). After the replay every level's star is moved onto the rank holding the finest's star, the pairing carried as labels; the misses fall to the band's rim (2 to 6 %). The parallel transfer records its hit rate per level on the hierarchy. scripts/sessions/fault_mg: parallel_load.py (owned cells and star per rank on every level, misses, stage times), and the figure scripts for the wiki page and the technical note (strategy schematics, the transfer rule, the pipeline, the rendered hierarchy, iterations against refinement, the moving-fault cost, the parallel load). Underworld development team with AI support from Claude Code
… load case Underworld development team with AI support from Claude Code
…ir is retired The nearest-node injection that repaired a coarse unknown with no fine image put a weight-1 entry on a fine row that already carried one: two identical columns, a singular Galerkin coarse operator. Measured on the composed ladder rig (test_1022's stack with two mid ribbons): four orphans on the welded-to-finest transfer, NaN on the first cycle, with a split finest and with an unsplit TI finest alike; and 512 duplicated columns at np = 2 on the welded tail under a split fault. Every custom-P hierarchy now drops such unknowns at install (restrict_unreached), on the standard and the rotated path; the coarse levels' FAC patch rows are cleared by the renumbering, the finest's kept. The repair and its zero-column asserts are removed. scripts/sessions/fault_mg/ladder_c.py: the ladder measured — the mid ribbons split, welded or absent at two depths, and the TI realisation on the same nested ladders (recorded on the wiki, Fault multigrid). Serial: 77 tests pass across the custom-P, adapt, ribbon and fault files; parallel: the fault, rotated-row, seam-split and single-field FMG tests at np = 2. Underworld development team with AI support from Claude Code
…ch, the real ladder levels, and the parallel imbalance decomposed by level Underworld development team with AI support from Claude Code
… contact and TI, and the finer base Underworld development team with AI support from Claude Code
…e wall rotation
Placement on a curved wall (utilities/place_surface.py):
- the domain tool winds every boundary loop counter-clockwise; a hole loop wound
against the exterior was added as area and the ribbon met nothing (annulus at
cell sizes 0.04 and 0.08, "meshed to no triangles");
- the 2-D outcrop frame groups the wall's chords into smooth walls (the 3-D
rule) and the splice keeps the curve's own vertices, so a dipping band's
clearance cavity is not refused as contact away from the outcrop and the cap
does not chord across the sagitta the area guard refuses;
- the structured ladder outcrops: rungs inside the domain to one rung short of
the wall, closed by a small unstructured cap clipped against the domain. The
spine keeps its rung vertices for the split; the cap is the TI bridge's home.
Custom multigrid under rotated free-slip (utilities/fault_mg.py, rotated_bc.py):
- every coarse level takes the wall rotation on its own nodes (level_wall_rotation,
composed with a split level's pair rotation by level_rotation) and loses its
normal rows. Cartesian coarse levels under a rotated finest kept wall-normal
unknowns the fine operator reaches only through vanishing sines; the inode SOR
smoother's nodal block inversion met a zero pivot and the velocity solve failed
before its first iteration;
- the orphan rule's reach test is relative (UNREACHED_REL) and runs after the
finest's constrained rows are zeroed;
- the finest's FAC patch rows survive the per-level restriction.
tests/test_1091_place_outcrop_curved_wall.py covers the placer on the annulus.
scripts/sessions/fault_mg/zg95_{probe,annulus,profile}.py: the Zhong & Gurnis
(1995) analogue (annulus, temperature-dependent lid, dipping trench that moves
with the overriding plate, topography and surface velocity), its probe and its
profile (meshing 5 s against a 60 s solve, the solve being Schur Krylov work).
Underworld development team with AI support from Claude Code
Underworld development team with AI support from Claude Code
On the developed temperature field of the annulus run at four ranks the inode SOR's 2x2 block inversion met a zero pivot on the base's two finest levels with every scalar diagonal healthy; without the grouping the same solve converged in nine iterations to the two-rank answer. A rotated wall node keeps one tangential row beside the two-row nodes. The finest operator is untouched. The driver gains a level inspection (ZG95_INSPECT) and the parallel reductions. Underworld development team with AI support from Claude Code
…river; the jigsaw renderer Underworld development team with AI support from Claude Code
Underworld development team with AI support from Claude Code
Underworld development team with AI support from Claude Code
…ited) Underworld development team with AI support from Claude Code
Underworld development team with AI support from Claude Code
Underworld development team with AI support from Claude Code
Underworld development team with AI support from Claude Code
…rench, hand-over note Driver: the yield indicator is evaluated on the solve mesh into a P1 variable and transferred to the base collectively (a rank-local "is this the base" test hung the 8-rank fault phase); failures in the diagnostic are decided on every rank; the figure's temperature panel is a collective polar-grid evaluation instead of a serial-only branch; the add-on build line reports global counts; each rebuilt add-on warm-starts from the previous velocity and pressure; the trench follows the median overriding-plate velocity over a window beyond the fault; --verbose, --newton_switch, --trench_off/--trench_span options; the surface and trace are saved at checkpoints too. Renderer: the trace falls back to the nearest earlier surface file; the unrolled section has a vertical exaggeration of 2. CAMPAIGN.md records the runs, results, solver state and open items for the session taking the work over. Underworld development team with AI support from Claude Code
One conflict, in systems/solvers.py: this branch moves Stokes' four stress-history methods (_stress_history_prepare, _advance, _post_solve, _create_stress_history_ddt) into _StressHistoryMixin; development changed two comments inside them since f414da6 and nothing else. The methods stay in the mixin, with development's two comments. Underworld development team with AI support from Claude Code
…reach Library: - split_tail_levels exchanges each level's verdict, so every rank stops the replay at the same level; a rank-local failure of the split hung the ranks that went on (new test in tests/parallel/test_1089: hangs before, passes after). - build_rotation's manifold pairing pre-pass raises collectively, like the pair-block assembly after it; an empty pair list no longer recomputes. - custom_mg.install keeps the FAC patch rows of every coarse level that restrict_unreached left whole; only renumbered levels lose theirs. - ThinSheet: L and c_b come from the solvers' unique-name factory (a user's "L" was reset to 1 by building a sheet, and two sheets shared a thickness); the plane-stress pressure is read from the committed stress after a viscoelastic solve, not from the shifted history, and p is filled on first access after a solve; a transversely isotropic model is refused on a manifold (its strain rate is not projected). - objective_order defaults to 1 for every solver again (Louis, 2026-10-28); ThinSheet selects "cayley" for a first-order Jaumann history when no order was given. Stokes VE results and order=2 + jaumann are as on development. - add_basal_drag / add_basal_traction take their value as conds (Charter section 6), and Stokes' versions non-dimensionalise the reference velocity and traction as the sheet's do (_nondimensional_row moved to solvers.py). - extrude_sheet is in meshing.__all__. Tests: test_1091 is reached (test_109* glob; check_test_coverage failed); the welded-beats-split iteration assertion in test_1087 is gone (Charter section 8), the slip agreement stays; test_1081 and test_1084 (13 and 7 min) are tier C, reported not gated; test_1083's thin-against-thick agreements are tier C; test_1086 runs both tails at np >= 2 and no longer asserts a property true by construction. Docs: thin-sheet.md's limits paragraph (parallel extrusion, split tails) and the co-location and ghost-cell descriptions corrected. Underworld development team with AI support from Claude Code
…itudes in test_1075 - test_1080: the model's default objective_order is 1 for every rate (Louis, 2026-10-28); the default test now also checks that a ThinSheet selects the Cayley step for a first-order Jaumann history given no order, and keeps a given order; the Cayley test asks for the step by name. - test_1075: the von Mises sheet test compares magnitudes. On development a viscoplastic stress evaluated in a model without units comes back tagged pascal (issue #881), and the comparison with a plain number raised. - extrude: the boundary-value sentinels (666, 1001) are named where the base and surface values are chosen. Underworld development team with AI support from Claude Code
…ted; sheet fixes - The merge with development took #846's removal of the base refusal of a rotated constraint; only the Stokes saddle point routes a rotated adjoint (solve_rotated_adjoint), and the vector solvers' adjoint_solve transposes the unrotated operator. A manifold ThinSheet (its tangency is a rotated constraint) recorded "supported" and adjoint_solve ignored the constraint. Solvers declare _adjoint_routes_rotated_constraints (True on the saddle point); the base refuses otherwise. test_0018 gains the manifold sheet. - ThinSheet refuses a transversely isotropic model on a manifold before it is installed (a caught refusal left the model in place), including a composite's constituents. - The sheet's plane-stress pressure adds the solvent's Newtonian stress (the history carries the Maxwell element only). - test_1089's replay test injects its rank-local failure after the split's last collective, the case the verdict exchange handles; injected before, the split itself hangs (issue #874), which no exchange after it can rescue. Passes with the exchange; with each rank deciding alone it hangs at np 2. Underworld development team with AI support from Claude Code
…epository Louis, 2026-10-28: library, tests and docs in underworld3; campaign drivers outside it. scripts/sessions/fault_mg/ (28 files: the ZG95 annulus drivers, the exploration stages, UWTN 2026-022's figure scripts) and the three scripts/sessions/thin_sheet_*.py go; they are kept with the campaign. The design note, the subsystem doc and test_1084 no longer point at them. Underworld development team with AI support from Claude Code
Adversarial reviewThree independent read-only passes on f414da6..488004d (2026-10-28): parallel rank asymmetry in the multigrid machinery; the manifold solves and the solver contracts; the tests. Then the branch was merged with development (bfd07b7), the findings were fixed, and a fourth pass reviewed the merge and the fixes. Every item below is fixed in this branch unless it names an issue. FixedParallel:
Solver behaviour for users without faults:
ThinSheet:
Tests and CI:
Docs and code hygiene: Left open (filed)#869-#872 (the markers), #873 (an empty coarse level on a rank; not reproduced at np 4 and 6 on a long fault across a small add-on), #874 (rank-local raises in the cell-located transfer and inside the split; the line cut's orientation vote), #875 (L and c_b inlined in the residual), #876 ( Not changed (nits): direct mpi4py on the DM's communicator in Attacks that failed
Merge conditions
|
Split-level fault multigrid, a thin-sheet (plane-stress) solver on flat and spherical meshes, and the extrusion of a sheet into a thick layer with its faults. This is the method UWTN 2026-022 describes; it is the base the parallel add-on work (the next PR, stacked on this one) builds on.
What it adds
utilities/fault_mg.py,docs/developer/subsystems/fault-split-multigrid.md):mesh.add_faultsplits the finest level and, by default, every add-on level of the hierarchy (split_levels=Falsekeeps the welded tail). Transfers are located by cell, side-aware across the slit; each level takes its own rotation frame (pairs and walls); coarse unknowns no fine unknown reaches are dropped rather than repaired by injection.systems/thin_sheet.py,docs/developer/subsystems/thin-sheet.md): the vertically averaged sheet, viscous, Maxwell viscoelastic or yielding, with split-node faults, on a flat mesh or aSphericalManifold; objective (Jaumann) stress transport with an exactly orthogonal Cayley step on the sheet.uw.meshing.extrude_sheet: the sheet as a thin 3-D layer, faults carried through, so the same model runs thin or thick.Measured
UWTN 2026-022's fixture (box in simple shear, straight fault,
library_c.py), velocity iterations, split / welded: 5 / 8, 5 / 13, 4 / 18 at k = 2, 3, 4 (serial); 6 / 8, 5 / 13, 5 / 18 at np 4; peak slip equal to six figures between the two hierarchies and between np 1 and np 4.Rotated free-slip without a fault (the coarse-level wall rotation reaches every custom-FMG rotated solve), velocity iterations:
Same answers: the annulus to nine digits, the shell to 2.4e-7 (solve tolerance 1e-6). Adapted toward the outer wall with
base.adapt, rotated free-slip on both walls, buoyancy forcing. A plainAnnulus(refinement=...)orSphericalShell(refinement=...)solves on GAMG on both builds and is unaffected (identical iterations and answer; the plain shell's velocity solve sits at its 200-iteration cap on both).Review
Three independent read-only passes on the branch (parallel rank asymmetry; the manifold solves and solver contracts; the tests), then a re-review of the fixes and of the merge with development. The findings and what was done are in the first comment. Fixed in this branch, among them: a rank-local failure in the replay of the fault onto the add-on levels hung the other ranks (new parallel test, hangs before);
ThinSheet'sLoverwrote a user's same-named expression; the sheet's pressure was read from the shifted history;test_1091never ran andcheck_test_coveragefailed; a welded-beats-split assertion; two 13- and 7-minute tests gated CI.objective_orderdefaults to 1 for every solver as on development (Louis, 2026-10-28); the sheet selects the Cayley step itself.Suites (laptop, macOS): tier A level 1 serial 1497 passed, 44 skipped, 1 xfailed, 0 failed;
tests/parallelat np 2 168 passed, 11 skipped, 1 failed; at np 4 176 passed, 3 skipped, 1 failed; no hangs. The one failure istest_1063_constrained_freeslip_parallel[ti](#826), which fails identically on development. The fault and sheet tests by name (test_1075-1080, 1082, 1083, 1085, 1087, 1091, test_0016, 0018, 0022, the parallel 1086 and 1089 at np 2 and 4) pass; test_1081 and test_1084 are tier C.Issues
Left open by this PR, found in the review: #869-#872 (the four TODO(BUG) markers it adds), #873 (an empty coarse level on a rank), #874 (rank-local raises in the cell-located transfer; the line cut's orientation vote), #875 (the sheet's L and c_b inlined in its residual), #876 (
ThinSheet.solvetakes nopicard), #877 (units-model rotated solve with cdim 3, present on development), #878 (no parallel test of the sheet or ofextrude_sheet), #879 (the sheet's history projection is not in the transcript; the facet-normal field after a deform), #880 (test hardening).Underworld development team with AI support from Claude Code