Repository navigation
Conversation
A solver that assembles its own weighted sum of history terms (an Eulerian scheme applying a multistep rule to a spatial operator) needs the constants-routed coefficient expressions, not just their current values. Read-only accessors; no behaviour change. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…vars A MeshVariable that is dropped and garbage-collected (the default Model holds the only strong reference; uw.reset_default_model() releases it, and the statistics helpers delete temporaries deliberately) leaves its PETSc field in the DM. Mesh.update_lvec zipped mesh.vars.values() against the field decomposition by position, and the JIT's petsc_a[] offsets were a running count over the live variables, so every later variable was packed into, and read from, the wrong slots. Measured: a P0 cell-size field landing in a P2 slot as garbage, NaN residuals in one run and a subtly wrong answer in the next, depending on when the collector ran. update_lvec now packs by field name and zeroes an orphaned field; the JIT reads component offsets from the DM's own field list and patches each variable from its field_id. Regression test: 2 of its 3 checks fail without the fix. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…r for scalar variables A Gaussian carried round the origin by rigid rotation while diffusing is exact at every time (rotation commutes with the Laplacian), so a transport scheme's error can be measured directly and the round trip after one revolution is an absolute check. AnalyticSolution.error(norm='integral') added a 1x1 Matrix symbol to a scalar expression and had never been exercised on a scalar variable. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…fusion solvers The cell-crossing / diffusion-time reduction (isotropic or direction-aware, minimum or percentile) becomes a module-level helper so the Eulerian solver can call it rather than carrying a copy. SLCN behaviour unchanged. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…nditioner A solver with no managed option block (_pc_option_prefix is None) sets its own PC; installing the adapt child's PCMG hierarchy on it segfaulted inside PETSc (additive-Schwarz PC, PCMG calls). The gate now treats that state as the explicit choice it is, alongside preconditioner='gamg' and the user override latch. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…from the symbolic history uw.systems.AdvDiffusionSUPG(mesh, T, V_fn, order=N, integrator='bdf'|'am') assembles the implicit weak form from the Eulerian DDt history: the BDF stencil or the Adams-Moulton weights on the advective and diffusive terms at every stored time level, plus the SUPG flux tau R u with the strong residual of the same scheme. Timestep, multistep coefficients and the tau weights are runtime constants of the compiled kernels, so a change of dt costs nothing (the issue underworldcode#657 prototype recompiled on every change). Diffusivity comes from the constitutive model like every scalar solver. Measured on the rotating Gaussian: stable at any cell Courant number, error set by u dt against the feature width (dt^2 for the second-order schemes), unchanged to three digits by a band refined to h/9 at local Courant 13; Crank-Nicolson reproduces the prototype's numbers to four digits. Tests: API and no-recompile contract, temporal convergence (slopes 0.8/0.9 for BDF1, 1.9 for BDF2), band invariance, round trip, np=2 = serial. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…om the integrator study Rotating-Gaussian study at res 32, Courant 0.25 to 8, pure advection and kappa 1e-3: Adams-Moulton above order 1 blows up from Courant 1 (bounded stability region), BDF3 fails from Courant 4, Crank-Nicolson is three to four times more accurate than BDF2 at the same timestep but rings once the feature is under-resolved in time, backward Euler carries 20-40% error at any practical timestep. Cost per step is the same for every scheme. BDF2 is the robust default; the note records the alternatives and when to pick them. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…Courant number BDF2 and Crank-Nicolson track their res-32 errors at the same u dt. BDF3's stability region misses the imaginary axis near the origin, so the low-frequency modes of a finer mesh grow: 31x the exact field after 590 steps at Courant 1. Safe only with diffusion, below Courant 2. Note and docstring updated; the BDF2 default stands. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…-in replacement The constructor, order, theta, f, V_fn, constitutive_model, delta_t, estimate_dt and solve keep the meaning they have for AdvDiffusionSLCN, so a script changes the class name and nothing else. order=1 with theta=0.5 is Crank-Nicolson and the default, as for SLCN; order=2 takes theta=1 (BDF2) unless 0.5 is asked for explicitly, which is refused for the reason the SLCN documentation gives. The trace-back-only arguments (restore_points_func, monotone_mode, old_frame_traceback, DFDt) are accepted and ignored with a warning. integrator is inferred and only needs setting to reach the higher Adams-Moulton rules. delta_t is settable and solve() reuses it; the notebook viewer reports the scheme. User page docs/advanced/eulerian-advection-diffusion.md with the swap table and the when-to-use-which guidance. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…e scheme The only schemes the argument added were Adams-Moulton at orders 2 and 3, which the integrator study shows blowing up on advection from Courant 1. The multistep family now follows the order (the theta rule at order 1, BDF above); the higher Adams-Moulton assembly stays in the code, reachable only by switching the family on the instance, which is how the study measured it. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…the module and note estimate_dt now returns the step at which the field changes by a fraction (0.02) of its range: from the advective rate |u . grad phi| at the vertices before the first solve, and from the rate the last step actually produced after it. The cell-crossing time the semi-Lagrangian solver reports is not a stability limit for this scheme and says nothing about its accuracy; it stays available as basis='resolution'. The estimate is mesh-independent, which the band test now checks (the resolution estimate collapses 3x on the refined child, the accuracy estimate moves under 25%), and at the default fraction Crank-Nicolson completes the rotating-Gaussian round trip under one per cent. The advective rate uses the vertex Clement gradient rather than a point evaluation of a derivative expression, which fails on a mesh carrying many variables. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…oner="fmg" a real switch on the SUPG solver The Eulerian SUPG step took two Newton iterations on a linear operator: the Krylov default (rtol 1e-5) does not reach the SNES tolerance (1e-8), and the second Jacobian assembly cost more than every linear solve of the step. The Krylov tolerance is now 1e-9 and a step is one Newton iteration: 1.54 s to 0.91 s per step at 256^2 in serial. Measured against geometric multigrid at matched tolerances (design note, "Preconditioner"), GMRES with additive-Schwarz ILU is the cheaper linear solve at every Courant number from 1/2 to 32 and its iteration count is the same on one and eight ranks; the multigrid's cycle count grows with the Courant number nearly as fast, and a cycle costs about three Schwarz iterations. Schwarz stays the default on every mesh. preconditioner = "fmg" now hands the block to the managed multigrid route (custom-P transfers over the refinement hierarchy or an adapt child's coarse tail, flexible GMRES outside) for the rank count where a one-level method runs out of coarse space. The solver's solve() builds through the base _build, where a preconditioner choice is resolved; the pre-run of the three setup stages marked the solver set up first, so the request was silently inert. The semi-Lagrangian solvers share that pattern and the defect (underworldcode#683). Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
…lows The shipped convection examples set adv_diff.theta = 0.5 after building the solver; the Eulerian drop-in refused it. The blend is a runtime constant refreshed from the history manager before every solve, so the setter updates it without a recompile (order 1 only, the constructor's rule). Vector and tensor unknowns join the design note's deferred list: the solver is scalar, where the semi-Lagrangian trace-back carries them. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
Use one public AdvDiffusionSUPG class and one F0/F1 assembly for implicit CN/BE/BDF and explicit CitcomS updates. Preserve the P1 positive lumped mass, gamma=0.5 two-correction update, directional tau and conservative timestep estimate, cached PETSc vectors and geometry. Replace the former implementation with compatibility imports; no duplicate solver remains. Register timestep-estimator and integrator snapshot state, omit unused CitcomS DDt history, retain explicit BDF selection and custom tau, and validate incompatible settings. Add frozen-source triangle/tetrahedron equivalence and in-memory/disk restart tests. Replace the MPI Gaussian cross-host golden value with a same-host serial reference without loosening the threshold. Validation: rebuilt Mac worktree; 25 unified/API tests passed, 13 residual tests passed, seven MPI tests passed per rank including the Gaussian regression, and nine expanded two-rank migration/snapshot tests passed per rank. Style gate and diff whitespace checks passed. Gadi coupled benchmarks and memory gate are still pending; this is not a production-acceptance claim. Underworld development team with AI support from OpenAI Codex.
Select backward Euler explicitly for quasi-steady manufactured and high-Peclet tests. Check the public generic tau expression instead of the removed P0 implementation detail. Compare spherical serial/MPI values on the same host and cached triangulation; keep manufactured convergence as the absolute accuracy gate. Apply the same near-machine-precision implicit replay tolerance as the unified snapshot tests. Syntax and whitespace checks pass; Gadi numerical validation remains pending.
The Gadi eight-rank migration gate failed before any timestep because the tiny triangle fixture leaves unsupported local simplex layouts on some ranks. Propagate layout errors to every rank before entering timestep reductions. Increase the equivalence fixture resolution and add a separate empty-partition rejection regression. Preserve numerical algorithms and tolerances. Rebuilt locally; Gadi validation pending.
Gadi preserved snapshot fields exactly but a rebuilt CN solve differed by 3.45e-10 with the default 1e-9 Krylov tolerance. Tighten only the restart-test KSP/SNES tolerances and retain the near-machine-precision replay assertion. Production solver defaults are unchanged.
Eight-rank PC2 migration and field replay now pass. The CN rate differed by 2.04e-12 because taking a timestep derivative divides field roundoff by dt=0.003. Propagate the unchanged 2e-14 field comparison bound through max(abs(delta T))/dt rather than using an unrelated fixed rate tolerance. Snapshot restoration remains exact and production settings are unchanged.
Exercise the explicit P1 gamma=0.5 two-correction path against smooth published pulse solutions on triangles/tetrahedra, the existing rotating-Gaussian oracle, and exact radial diffusion in a spherical shell. Keep SUPG active for advection and prescribe velocity without Stokes. Bound finite-domain tails, use common fixed timesteps for spatial refinement, check absolute errors and convergence, and optionally retain compact HDF5 diagnostics for same-host serial/MPI comparison. Verify the spherical equation symbolically. Solver implementation unchanged. Syntax checks pass; numerical Gadi validation is pending.
Refine the spherical diffusion pair from 1/4-1/8 to 1/8-1/16 without relaxing the 8 percent absolute L2 criterion. Add fixed-1/8 dt, dt/2 and dt/4 trajectories, restoring identical T/Tdot/startup state and comparing full FE fields. Record input Gmsh SHA256 and short mesh filenames for strict same-mesh serial/MPI comparison. Reuse a focused advance/measurement helper instead of duplicating the diagnostic code. Solver implementation unchanged. Syntax checks pass; bounded Gadi numerical follow-up pending.
|
@lmoresi This PR is ready for a focused review once the current CI run completes. Two decisions remain intentionally open:
The former 200-step RSS soak is now opt-in ( |
Keep test 1077's default cellsize 1/8, numerical setup, 5% analytical bound and 1e-8 serial/MPI comparison unchanged. Expose only the internal helper's mesh size for the requested eight-rank 1/4, 1/8 and 1/16 Mac investigation after an empty-partition mesh. All three resolutions completed after mesh fix 7e17bec. Same-host serial/MPI errors agreed within 8.47e-9 at 1/8 and 2.46e-9 at 1/16. Preserve and document the independent 1/4 discrepancy of 7.00e-7; it persists with tighter solves on identical mesh hashes. Do not relax the original gate.
Add tiny 2D triangle and 3D tetrahedron tests with independent analytical P1 element matrices and exact semidiscrete eigenmode references. Compare every UW3 T/Tdot update against a closed-form two-correction map without using a fine numerical solution, Stokes, or A1. Demonstrate that consistent residual mass with lumped correction mass and two fixed corrections approaches (2I-D^-1 M)D^-1 K, with first-order timestep differences. Separate the startup-rate error using exactly solved CN and genuinely lumped-residual controls, which establish why uniform scalar decay is insufficient. Document the limitation without changing CitcomS semantics, solver defaults, or prior tolerances. Four tests pass on Mac serial (17.46 s) and eight MPI ranks (32.91 s); matching mesh hashes and update discrepancies below 4e-14. Style gate and whitespace checks pass.
Exercise the actual shared Eulerian CN solver on the same tiny triangle/tetrahedron meshes and timestep sequence as the PC2 diagnosis. Check the exact CN amplification map and second-order error against independently assembled generalized eigenmodes. Two tests pass in serial (26.61 s) and on eight MPI ranks (13.69 s), with order 2.00 in both dimensions. Finest relative errors are approximately 5.4e-9 and 5.7e-9. No Stokes/A1 run, numerical-method changes, or relaxed error tolerances.
A tiny three-process restart regression exposed a missing _h_cell field: implicit SUPG previously created its generic-tau geometry field only during residual construction, while snapshots require the same fields registered before loading. Materialize this existing dependency at solver construction, only for automatic generic tau; do not relax snapshot schema checks or change the time integration. Add independent full/write/resume worker tests for PC2, CN and BDF2 on a 370-tetrahedron mesh with varying timesteps and velocity. Require exact restored fields/state, then compare step-12 continuation with uninterrupted runs. The CN case demonstrably failed before the four-line constructor fix. After rebuild: serial 3 passed in 48.95 s; eight-rank workers 3 passed in 58.98 s. PC2 continuation is exact; CN/BDF2 fields agree within 8e-16. Update the user guide with the fresh-interpreter workflow. No Stokes/A1 run or checkpoint tolerance relaxation.
b739d68 to
048ee09
Compare
|
Follow-up: the two generic mesh fixes have now been removed from this PR and submitted independently:
The #689 history was rewritten and force-pushed without either commit. A clean rebuild succeeded. Its broad serial SUPG suite then produced 73 passed, 9 expected skips, and one expected prerequisite failure: without #692, the existing high-Peclet SUPG error is The earlier request to decide whether the generic fixes should stay in #689 is therefore resolved. The remaining review question is only the intended public contract for fixed two-correction PC2 and its documented temporal-order limitation. |
Preserve the fixed two-correction CitcomS compatibility mode while adding an explicitly selected pc_converged integrator. Iterate the full Petrov-Galerkin rate residual at startup and each timestep, using the lumped mass only as a reusable correction preconditioner and failing clearly when configured tolerances are not reached. Persist correction controls in solver snapshots, expose convergence diagnostics, and extend workspace and fresh-process restart coverage. Add independent finite-element diffusion tests proving second-order convergence and exact trapezoidal-map agreement in 2D/3D serial and MPI without relying on the Zhong A1 model. Document the mathematical distinction from fixed PC2 and the current repeated-residual performance cost.
|
Marked ready for review without changing the requested target: The head is 17 commits ahead of that target and contains no unrelated target divergence. Generic mesh fixes remain in independent PRs #691 and #692 rather than being duplicated in this SUPG integration diff. The current red CI result is the known partition-dependent |
DMPlexIsSimplex returns false on an empty partition. Using that local value for coordinate FE construction mixed simplex and tensor bases across the same communicator, consuming different PETSc message tags. A subsequent mesh HDF5 labelsLoad then blocked in PetscSFSetUp_Basic/MPI_Waitall. Gather cell-family decisions from populated ranks before FE setup and use the same classification for element metadata. Preserve the constructor hint for an entirely empty mesh; do not change SUPG algorithms, solver tolerances or MPI providers. Add single-cell triangle/tet/quad/hex regressions that require empty ranks and subsequently load another mesh, checking P2 volume and boundary integrals. The triangle regression failed before the fix. The original SUPG migration/partition sequence plus all four regressions passed on eight Mac ranks: 15 tests in 26.05 s, 32.30 s including launcher, 2.74 GiB peak process-tree RSS. The pre-fix sequence hung and the mesh-only reproducer also hung. Gadi rerun remains pending.
…#687) Adapt only the mesh-size correction from lmoresi's 68e545f on feature/navier-stokes-supg; do not import Navier-Stokes or other branch changes. Cache _radii_own from current DM vertex coordinates and use it for mesh.cell_size(). Preserve the legacy kd-tree radius arrays and global timestep/mesh-motion consumers. Use coordinate-section offsets and the full vertex stratum so the own-cell RMS definition also handles hexahedra, which have eight vertices but six faces. Correct the field documentation and Nitsche mechanism tests for the new definition; retain physical solve tolerances and use the exact nearest-centroid <= own-centroid ordering instead of an arbitrary approximate-equality tolerance. Add a first-failing independent geometry/deformation regression for triangles, tetrahedra, quadrilaterals and hexahedra plus a regular-square analytical control. Before: four failures in serial and on eight ranks. After rebuild: 21 passed/one expected skip serial (22.90 s), 22 passed on eight ranks (40.45 s), covering Nitsche solves, radius accessors, frozen PC2 migration and memory/disk snapshots. Own-cell geometry error is zero in these tests; style and whitespace gates pass.
Rename the new per-cell geometric radius cache from _radii_own to _cell_radii so the name describes cell geometry rather than rank ownership. Update the focused Nitsche and deformation checks accordingly.\n\nAdd an enumerated parallel regression that gathers owned-cell centroid/radius pairs and compares the complete sorted table with a fresh single-rank run on the same cached Gmsh mesh. This directly guards the rank-count-independence claim at np=2, np=4 and np=8 instead of relying only on within-rank geometric identities.\n\nValidated locally with 9 focused serial tests and the new MPI test at 2, 4 and 8 ranks.
Remove the local_h=False workaround from the boundary-normal MPI regression now that Mesh.cell_size() is partition independent. The test again exercises the public local_h=True default and compares its Nitsche solve with a fresh serial process.\n\nRecord the user-visible consequence in the development changelog: the rank-local centroid kd-tree moved the default Nitsche velocity answer by 6.6e-3, while the cell-geometry replacement is identical cell by cell from one through eight ranks.\n\nValidated the focused Nitsche regression at 2, 4 and 8 Open MPI ranks (10.99 s, 7.31 s and 9.60 s respectively).
|
PR #689 now includes the complete commit stacks from #691 and #692 as explicit cherry-picks, without retargeting the PR or importing unrelated
Validation after rebuilding the combined branch:
The 8-rank batch includes |
|
@lmoresi The remaining current-development integration gate is now complete. I created a temporary merge of current
This exercises the exact same-layout field reload from #674 and MPI descendant supervision from #678 without duplicating either implementation in #689. The PR description and restart table now record this result. Upstream CI for the exact PR head is still running. The remaining maintainer decision is the public API contract: preserve fixed two-correction |
|
Final author-side gate update: upstream CI passed for the exact PR head. The PR is now clean and mergeable, and every author-action checklist item is complete. The requested maintainer decision on the public PC2 API remains pending review. |
|
#688 is merged to development (1d5e8c1), so this PR is retargeted there and now shows conflicts: it was built on the closed #673 branch and its diff carries 65 uses of names that no longer exist ( 🤖 Generated with Claude Code |
…manager Resolve the PR underworldcode#688 architecture transition on existing PR underworldcode#689. Retain the upstream composing AdvDiffusion solver and EulerianSUPG implicit manager; move fixed and residual-converged predictor-corrector rate history, tau, stability estimate, corrections and reusable state into EulerianSUPGPC. Keep PDE assembly in the composing solver through small manager execution hooks. Preserve matching-graph snapshots for manager history and solver timestep/change estimates. Reject cross-solver manager reuse; keep theta validation transactional and rewire changed velocity expressions. Retain explicit-tau anisotropic behavior. Exclude unrelated mantle Stokes, geoid and dependency changes. Validate frozen-source equivalence, independent analytical recurrence and temporal order, live manager mutations, workspace reuse, and serial/eight-rank disk/fresh-process restarts. The focused MPI group passes 49 tests plus five formerly skipped disk replays; all four fresh-process MPI methods pass. CN and converged PC are order 2; fixed CitcomS remains approximately order 1. Full repository Level 1 run remains in progress and is reported separately.
|
@lmoresi implemented your DDt-manager recommendation in transport = uw.systems.ddt.EulerianSUPGPC(
mesh, T, U.sym, method="citcoms", temperature_rate_field=Tdot,
)
thermal = uw.systems.AdvDiffusion(mesh, T, U.sym, DuDt=transport)
All disk-replay cases now run: an obsolete capability check was inspecting The PR description includes the extended analytical results, timing details |
|
Measured the manager on the rotating-Gaussian round trip we use for the transport schemes (one revolution, sigma 0.12, 32 cells across, serial; branch cd155fc on development 1d5e8c1). At its own stability step (dt 0.0130, Courant 0.60, 485 steps): 🤖 Generated with Claude Code |
|
Ruling from Louis on the comparison above: the predictor-corrector manager stays as a CitcomS-compatibility choice, not a general-purpose scheme, and we are not asking for P2 support (P2 halves its explicit step and the two-pass error comes from not converging the implicit system, not from the element order). For the PR that means: keep 🤖 Generated with Claude Code |
Design note: the predictor-corrector manager (#689) measured against the implicit scheme
…sured against the implicit scheme on the rotating Gaussian Same case as the Eulerian-vs-semi-Lagrangian table: the two-pass CitcomS preset costs what the implicit P1 step costs (0.030 s) and is eight times less accurate; the converged mode is the same implicit system at twenty times the cost per step and three times the error (its steady tau). Runs in ~/+Simulations/supg_vs_slcn_657/rotation_pc/. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018T2VHUGaZiQVJ95qQ4DiSL
Two conflicts, both in ddt.py and both placement collisions: this branch adds EulerianSUPGPC where development adds CharacteristicTrace, and development adds IntegrationPointSemiLagrangian and ForwardSemiLagrangian where this branch adds nothing. Both sides are kept in full; no behaviour was chosen between them. Note for whichever of this branch and underworldcode#744 merges second: underworldcode#744 adds _adjoint_support to _DDtBase and a test that every history scheme declares a verdict. That method does not exist on development yet, so nothing is owed here today, but EulerianSUPGPC will need a verdict once underworldcode#744 is in. check_test_coverage.py passes. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E87Q7KrpapxeQiLD1RiNXv
Eight of this branch's ten new test files matched no glob in scripts/test.sh, so CI collected none of them: test_1113, test_1114, test_1115, test_1116, test_1117, test_1118, test_1119 and test_1121. The batch named test_1110 and test_1120 literally, which caught the two files that existed when it was written and nothing added since. The batch is now test_111*/test_112*, and check_test_coverage.py sees all 346 test files. This is the failure mode of underworldcode#721, where the whole integration-point suite ran nowhere for two months. Underworld development team with AI support from Claude Code Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E87Q7KrpapxeQiLD1RiNXv
Merged
|
# Conflicts: # docs/developer/CHANGELOG.md
Two CI failures on this branch, both reproduced locally and bisected
against development.
test_0011 `test_replaying_a_rewound_step_reproduces_it`
------------------------------------------------------
`state` was defined TWICE on SNES_AdvectionDiffusion_Composed: the
existing AdvectionDiffusionStepState property, and a new AdvDiffusionState
one further down the class. Python keeps the last definition, so the first
became dead code -- and with it went `has_solution` and the
`SNES_Scalar.state.fset` delegation, neither of which AdvDiffusionState
carried.
`has_solution` decides whether the next solve starts from the current
iterate or from zero. After `model.rewind()` it stayed True instead of
returning to False, so the replayed step began from a different initial
guess and converged to a different iterate within snes_rtol (1e-8). The
fields came back 2.04e-10 apart and `np.array_equal` refused them.
Measured with the same probe in two worktrees, everything else equal --
T, psi_star, dt_history, theta, delta_t and the SNES iteration count all
identical:
has_solution before the replay replay bit-identical
development False yes
this branch True no (2.04e-10)
AdvDiffusionState now extends AdvectionDiffusionStepState rather than
SnapshottableState, so it keeps has_solution; there is one `state`
property, and its setter delegates to the base before applying the extra
fields. The probe now reports False and 0.0, matching development.
Ruled out first, each rebuilt and re-run: the eager `mesh.cell_size()` in
EulerianSUPG.__init__, the `delta_t` setter change, and the
`_register_state_bearer` call. Reverting the whole file to development
fixed it, which is what localised the cause to this file.
test_0018 `test_every_history_scheme_declares_a_verdict`
--------------------------------------------------------
EulerianSUPGPC derives from _DDtBase directly, so it inherited the base
refusal and read as undeclared. It now declares its own verdict, grounded
in what the corrector does: `_apply_pc_correction` advances the rate and
temperature by hand on PETSc vectors outside the owning solver's
residual, so the step is never materialised as an operator to transpose,
and under `pc_converged` `_converge_pc_residual` chooses the number of
corrections from a residual test, which is piecewise constant in the data.
A refusal is a verdict, not a gap; it should be revisited if the
corrections are ever expressed as a residual the solver assembles.
test_0011 and test_0018: 28 passed. test_00[0-4]* and test_107* under
xdist: 445 passed, 1 failed -- that one being test_1079, which fails on
development too and is fixed separately in underworldcode#848.
Underworld development team with AI support from Claude Code
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E87Q7KrpapxeQiLD1RiNXv
Summary
Add the Zhong/CitcomS predictor-corrector as a transport manager for the
composing
uw.systems.AdvDiffusionsolver introduced in #688.The solver assembles diffusion, source, and the manager's transport
contributions. The manager owns the persistent rate, steady directional
SUPG parameter, correction loop, stability estimate, and restart metadata.
The scalar solver supplies residual assembly through a small execution hook.
Implicit CN/BDF continue to use the existing
EulerianSUPGmanager.Numerical Behavior
method="citcoms"method="pc_converged"adv_gamma=0.5; raises on non-convergenceBoth PC modes require continuous scalar P1 fields on triangles or tetrahedra.
Their timestep is
0.9 * min(dt_adv, dt_diff). They reuse PETSc vectors andgeometry arrays and allocate no unused implicit history.
Restart and Integration
correction diagnostics, and timestep; mesh snapshots carry the rate field.
the old standalone SUPG solver are not a migration format.
Validation
Validated locally on 8 September 2026 against the merged development
architecture. Times are elapsed suite times, not CPU-hours. Independent
suites sometimes ran concurrently; rows are not an additive wall-time total.
f41bcd2fsource versus manager, both PC modes in 2-D/3-DThe initial MPI group had five obsolete checkpoint-capability skips, caused
by inspecting the enhanced-variable wrapper signature. That guard is now
removed and all five disk cases pass in the separate row above. Six
200-update RSS soak cases remain opt-in Level 3 tests; the default workspace
checks run eight updates. Optional frozen-baseline cases need the old source
file and were run separately in serial and included in the MPI group.
Fixed PC2 retains order approximately 1.01; composed CN and converged PC
measure order 2.00 on triangles and tetrahedra. Converged-PC agreement with
the independent trapezoidal map is below
5.2e-14. MPI fresh-process PCrestarts are exact; CN/BDF2 maximum field differences are below
9e-16.The full Level 1 invocation (
pytest tests -m level_1) also deselected 1362other-level cases. Its 27 min 13.89 s duration is the repository-wide suite,
not a focused SUPG subtotal. GitHub CI on this new revision is pending;
previous-revision CI is not counted as validation.
Scope
Targets
developmentfollowing #688. Generic mesh fixes #691/#692 are alreadyupstream. No Stokes, geoid, Navier-Stokes, or dependency changes are included.
The implementation and small tests do not require a long coupled A1 run.
Checklist
Maintainer review of the manager API remains requested.