Repository navigation
Conversation
…ticmethods; MiscibleDisplacement runs
Cython 3 compiles def functions with binding=True: a compiled function stored
on a class (class-body `from .ctransportCoefficients import f`) and called as
self.f(...) receives self as an extra first argument, like a Python function.
test/MiscibleDisplacement stopped at the first transport evaluate with
TypeError: groundwaterTransportCoefficientsEvaluate_hetMat() takes exactly
12 positional arguments (13 given)
Each of the 17 class-stored compiled functions in this file (24 call lines
through self, including the two-phase Darcy classes TwophaseDarcyCoefficients
subclasses) is now wrapped with staticmethod right after its import. The call
sites are unchanged; staticmethod returns the wrapped callable whether or not
it binds, so the same code is correct on Cython 0.29 and 3.x.
test/MiscibleDisplacement had no test. The deck gets context options
(unstructured_mesh, refinement_level, ndays, nDTout; defaults unchanged) so a
test can run it on a regular triangular mesh, which needs material type 0;
test_miscible_displacement.py runs flow (p1-nc) + transport on an 8x8 mesh to
500 d and pins the tracer mass.
Measured (Cython 3.0.11, macOS arm64, env proteus-bc-261025):
test_miscible_displacement.py before: TypeError after: pass, 0.9 s
parun md_so.py (default deck: triangle, 5000 d, 50 outputs)
before: exit 1, 3.5 s after: exit 0, 9.4 s
tracer mass at 500 d, 8x8: 26191.0447689 (identical in 2 runs)
min c (SUPG + shock capturing undershoot): -0.155 (8x8), -0.29 (16x16),
-3e-5 (32x32); the test's lower bound is -0.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and TimeIntegration; invariant test The same wrap as the previous commit, in every other file outside the mprans models (C0, after C0 round 5 lands) and TimeIntegration (C17/C14): file stored live self.f( calls TwophaseDarcyCoefficients 3 3 TransportCoefficients 1 0 (call commented out) richards/Richards 2 1 m_comp_co2/m_comp_co2 2 1 ADR, AuxiliaryVariables 1+1 0 PostProcessingTools VPP_P1nc_RT0 9 0 (calls use cpostprocessing.f) test/dg/la_gauss_2d_p.py 1 1 PostProcessingTools.VPP_POINT_EVAL_GWVD is left alone: #203 deletes the class. test/test_cython_class_attributes.py checks the premise (a compiled function stored on a class binds self; staticmethod returns it unwrapped) and, for each of the 23 modules that store compiled functions on classes, that the set of binding class attributes equals a DEFERRED list (TimeIntegration 16, mprans 39, PostProcessingTools 1). The set is exact in both directions, so fixing a deferred site fails the test until its entry goes. Inventory: a runtime scan of every importable proteus module found 89 class attributes holding a binding compiled function in 23 modules; an AST scan of test/, scripts/, experimental/ found one more (test/dg). This commit and the previous one fix 34; 56 remain, listed in DEFERRED. Measured (Cython 3.0.11, macOS arm64, env proteus-bc-261025): test_cython_class_attributes.py without the wraps: 8 of 24 module checks fail; with them: 25/25 parun la_gauss_2d_p.py la_gauss_2d_dgp2_n.py (test/dg) main: TypeError in TimeIntegration, computeElementNeighborShapeGradients() 5 for 6 TimeIntegration's 16 wrapped at run time (file untouched), deck unfixed: TypeError: unitSquareRotationEvaluate() takes exactly 6 positional arguments (7 given) both: exit 0, 66 s Existing tests on this branch (richards, griffiths_lane_6, single_phase_gw, post_processing, poisson_2d, levelset, LS_with_edgeBased_EV/MCorr, test_adr_problem, test_analyticalSolutions, multilevel): 821 passed, 32 failed with -n 8. None comes from this change: the 8 multilevel failures fail identically on unchanged main (ce43b73); 23 were xdist workers sharing reference_triangle.* in one cwd and pass serially (93 passed); 1 (test_ymfrun_knows_which_domains_fix_the_mesh) fails on main too, because the env's ymf has no discretizations.mesh_from_domain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…icmethods TimeIntegration is C17/C14 territory. cekees decided on 2026-11-05 that C47 applies this change on #204, because the change is mechanical and no C17/C14 session is running this week. C17/C14: the only change is one `f = staticmethod(f)` line after each class-body `from .ctimeIntegration import f`, as in the two previous commits. 16 class attributes in 14 classes (DGlimiterP1Lagrange1d/2d, P2, PkMonomial, Durlofsky, the _Sw variants, UnstructuredLimiter_base, CockburnNotesLimiter2d_base, PsiTCtte, PsiTCtte_new) and 16 live self.f(...) call lines. Each raised TypeError (N+1 arguments) under Cython 3 when reached. DEFERRED in test_cython_class_attributes.py loses its TimeIntegration entry. Measured (Cython 3.0.11, macOS arm64, env proteus-bc-261025): parun la_gauss_2d_p.py la_gauss_2d_dgp2_n.py (test/dg, DGlimiterP2Lagrange2d) before: TypeError: computeElementNeighborShapeGradients() takes exactly 5 positional arguments (6 given) after: exit 0, 38.6 s test_cython_class_attributes.py: 24/24 test/shallow_water/dambreak_1d (DGlimiterP1Lagrange1d) still does not run, for an unrelated reason: its flux subclass calls RusanovNumericalFlux_Diagonal without getPeriodicConditions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hing deferred After C0 round 5 (#199) and #203 reached cekees/main (c09280c, merged into this branch), the last sites: mprans Dissipation 2, Kappa 2, MCorr 2, MCorr3P 2, NCLS 1, NCLS3P 1, RANS2P 4, RANS3PF 4, RANS3PSed 4, RDLS 1, TADR 3, VOF 3, VOF3P 3, VOS3P 3 (35) Their 12 live self.f(...) call lines are all in Coefficients.evaluate, which the mprans models do not call (the kernels evaluate the coefficients), so no mprans result changes. VPP_POINT_EVAL_GWVD went with #203. DEFERRED in test_cython_class_attributes.py is empty: a runtime scan of every importable proteus module finds no class attribute holding a binding compiled function (89 at the start of C47: 88 in proteus/ plus the test/dg deck). Measured (Cython 3.0.11, macOS arm64, env proteus-bc-261025, serial): test_cython_class_attributes.py, MiscibleDisplacement, LS_with_edgeBased_EV, levelset, TwoPhaseFlow, test_scatter_dof_array, test_two_stage_newton, test_{vof,tadr,twophaseflow}_partition_independence, test_numerical_flux_arguments: 103 passed (7 min 20 s). test_damBreak_hotstart needed this tree's scripts/parun on PATH (the env's installed parun predates --hotStartMode); with the env's parun it fails on that option, not on C47. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A replay (test/step_retry_mpi.py REPLAY=, the C0 retry tests' oracle) made the
system step land on the accepted times of the forced-failure run by wrapping
choose_dt_system, so it could not take the first step: its dt comes from
initialize_dt_system. A failure in step 1 could not be tested.
SO_base.setStepSchedule(times) / so.systemStepSchedule: every system step
that starts before the last scheduled time ends on the next one, the first
step included. initialize_dt_system and choose_dt_system of every adaptive
controller (SO_base, MinModelStep, MinFLCBDF, MinAdaptive, ISO, _SS and the
NotInOrder variants) take scheduled_dt(...) in place of their choice, before
they hand it to the models, so the models' time histories are initialized with
the scheduled step. Past the last time the controller chooses as usual. The
harness now sets so.systemStepSchedule instead of patching two controllers.
With the first step replayable, a CLSVOF failure in step 1 showed a second
defect: CLSVOFNewton's spin-up for a discontinuous initial condition runs in
the first solve and clears coefficients.disc_ICs, which was not in CLSVOF's
state(). The retry skipped the spin-up, started from the raw characteristic
function and diverged (three cuts instead of one). disc_ICs is now in state();
the same flag would have re-run the spin-up on a hot start's smooth field.
Largest relative difference over every model, forced failure vs replay
(test/test_step_retry.py, macOS arm64, serial):
damBreakLevelSet VOF fails in step 1 before 5.0e-2 after 0.0
(replay's first step was 1e-3, the forced run's 5e-4)
fillingTank CLSVOF fails in step 1 before 1.03 after 3.4e-14
(schedule alone 3.0e-1; 3 retries before, 1 after)
The test also checks that the replay's accepted times equal the forced run's.
test_step_retry.py 17 passed (54 s, -n 6); test_state_contract.py and the
test_hotstart_* files 24 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
damBreakLevelSet (RANS2P + VOF + NCLS + RDLS + MCorr, he=0.1) hot-started at 0.04 drifted from the uninterrupted run (forces ~1% two steps later). Found by dumping every array and scalar of every model, its integrator, shock capturing, subgrid error, step controller and solver at each model's first solve after the restart, uninterrupted vs hot. What a restart rebuilt differently, and is now archived in solver_state and put back after the spin-up: - the fields other models read at the quadrature points and faces (COUPLED_FIELD_NAMES, the set the retry restore keeps): they hold each model's last residual, not its dofs, since MCorr and RDLS change VOF's and NCLS's dofs after their solves. NumericalSolution owner coupledFields. - BackwardEuler.m_last, the mass the next step differences against (VOF's is its uncorrected mass), and BackwardEuler_cfl.dtLast (caps the next step's growth): state()/set_state() on both (inherited by SSP, the RKEVs, PsiTCtte and TADR's CompatibleBackwardEuler; inventory updated). - RANS2P's shock capturing (DelayedLagState: lag machine and numDiff_last) and subgrid error (nSteps, v_last alias or copy). - the solver's unknowns uList[-1], the next Newton's initial guess and the source of the dof history shift (owner unknowns). - the next system step: initialize_dt_system chose afresh from the restart state (restarted at 0.005 the first step went to 0.01, not 0.0075). Owner systemStep, kept with the first model, via the new SO_base.setNextStep. Largest relative difference over every model's dofs at the final time, hot start vs uninterrupted run (macOS arm64, serial; each piece left out alone): before (all left out) 5.1e-1 (MCorr; velocity 7.3e-2, VOF 1.4e-2) coupled fields left out 6.6e-2 BackwardEuler state 5.0e-1 (MCorr) RANS2P shock capturing 3.2e-3 RANS2P subgrid error 6.7e-4 solver unknowns 5.4e-14 next system step (t=0.005) 5.4e-1 after 0.0 (dofs and forceHistory_p), both restarts The CFL controller's dt_model_last made no difference once dtLast is back (the same cap), so it is left as it was. test/test_hotstart_levelset.py: restarts at 0.04 (after the switch to lagged shock capturing) and 0.005 (capped step) are exact, same accepted times and force rows; --hotStartMode restart differs (> 1e-3). The harness (step_retry_mpi.py) gains DTOUT, HOTSTART, HOTSTARTMODE and runs the source tree's scripts/parun when there is one. 76 passed: test_step_retry, test_hotstart_*, test_state_contract, test_mpi_launch_retry, test/TwoPhaseFlow, test/HotStart_3P (-n 8, 78 s). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-H -t 0.06 with archives every 0.02 restarted from 0.04: the archived step is 0.06000000000000001 (a sum of steps), and "the latest archived step at or before t" compared it with 0.06 exactly. NumericalSolution.hot_start_step() takes an archived time as at t within a millionth of the smallest interval between archived times (2e-8 here; a few ulps of accumulated rounding is far below it, and a real step is far above it), and is the one place the rule lives. damBreakLevelSet archived every 0.02 to 0.1, hot-started again with -t 0.06: before continued from 0.04 (first step ended at 0.06000000000000001) after continued from 0.06 (first step ends at 0.08) test/test_hotstart_levelset.py: the end-to-end case (fails on the parent commit) and a unit test of hot_start_step (times off by an ulp, between steps, inf, before the first, a single step). 5 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ailing The proteus-bc-261025 env's parun has no --hotStartMode because it is a copy of scripts/parun made at install time (Oct 25): setup.py installs parun with scripts=, which copies the file, editable install or not. scripts/parun has the option; nothing is missing on the proteus side there. What was: NS_base validated getattr(opts, 'hotStartMode', 'continue') in __init__ but restoreSolverState read opts.hotStartMode directly, so any options object older than the option (an installed parun from before it, another driver) crashed in the hot start with AttributeError. NS_base now resolves it once (self.hotStartMode) and uses that. test_a_driver_without_hot_start_mode_continues (harness NOHOTSTARTMODE=1 deletes the attribute): before AttributeError, after a bit-identical continue (0.0). test_hotstart_levelset.py 6 passed. The harness already runs the source tree's scripts/parun when there is one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ain with its own numbering
A per-rank view must give the solver back the subdomain partitionMesh built: the partition's maps
number its element boundaries and edges, so those numbers must come back too. generateFromArrays
numbers boundaries and edges its own way; generateFromNumberedArrays takes each element's boundary
numbers and the edge list and calls what partitioning.cpp calls on a subdomain:
constructElementBoundaryElementsArrayWithGivenElementBoundaryAndEdgeNumbers_{triangle,tetrahedron},
then allocate/computeGeometricInfo, then the element boundary flags. mesh.cpp and partitioning.cpp
are not edited. The arrays are allocated with new[] (the Mesh frees them with delete[]); the node and
element fill is shared with generateFromArrays (_fillNodesAndElements).
Measured (macOS arm64, MPICH, env proteus-bc-261025): every rank's subdomain of square4x4_tagged,
square4x4 and cube_3x3x3, owners scattered and reversed, on 2 and 3 ranks (30 rank subdomains):
- generateFromArrays from the same nodes and elements: 7 arrays differ in every case
(elementBoundariesArray, elementBoundaryNodesArray, elementBoundaryElementsArray,
elementBoundaryLocalElementBoundariesArray, interior/exteriorElementBoundariesArray, edgeNodesArray);
- generateFromNumberedArrays: all 33 arrays equal (nodeDiametersArray aside, which partitionMesh
finishes across the ranks afterwards).
test_numbered_arrays (subdomains from partitionNodesLocal in process): 7 passed, serial and 2 ranks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tart that reads them Decided by cekees 2026-11-05 (decision 3, after C32 round 2): per-rank views written into per-rank archives (archive-modes layouts B and C) as the same-rank restart source. - Write: on a per-rank archive's first mesh write (Mesh.writeMeshXdmf, driver per-rank), each rank writes subdomain_view/<name> into its own <archive>_p<rank>.h5: the step-H ymf grid (its arrays as datasets beside it, the grid in the `ymf` attribute), and what rebuilding the solver's subdomain needs: each element's boundary numbers, the edges, every element boundary's flag (-1 included), the partition's eight maps, the global mesh's counts and scalars, its fingerprint and whether the partition renumbered. - Read: subdomainViewMesh(archive) -- an open archive or its path -- builds on every rank the mesh that stands for the global one (counts, scalars, maps, no arrays; as cartesianSubdomain's) with the subdomain rebuilt by cmeshTools.generateFromNumberedArrays, then _finishPartitioning. Collective: every rank reads its view or every rank raises SubdomainViewUnavailable with the first reason (no per-rank file, no view, another number of ranks, another view format). - generateMesh(..., restartArchive=None): with an archive whose views fit, a one-level hierarchy from them (multilevelMeshFromSubdomainViews), no global mesh and no partitioner; otherwise, or with more than one level, the old path, logged with the reason. NumericalSolution does not pass it yet (C17/C14's file: the hand-off line is agreed separately); the tests' driver passes the run's open archive. - The input maps and fingerprint carry over a views-built mesh (_localInputNumbering, _inputFingerprint), so the restart's archive records them as the global path's does. Measured (macOS arm64, MPICH, env proteus-bc-261025, ymf c2e646f, PETSC_OPTIONS -mat_partitioning_type current): - write then read, square4x4_tagged and cube_3x3x3, 2 and 3 ranks, layouts B and C: on every rank all 33 subdomain arrays, the 8 maps, the 17 global scalars and the -1 marking equal partitionMesh's (cut faces per rank 6/0, 8/0/8, ...). - same-rank restart, vortex2D (ls + rdls + vof + mcorr, 4 models), stopped at step 3, hot started to step 7 from the same archive with the mesh from the views and with the mesh generated and partitioned: on 2 ranks, partitioned (regularGrid global) and per-rank generated meshes, layouts B and C, every dataset of both ranks' files equal: 138/138 (fields, lagged solver state, mesh, maps). On 3 ranks the run is not reproducible against itself on either path: two global restarts differ by up to 1.7e-12 (18 datasets per rank), two view restarts by up to 2.1e-12 once in four, and views vs global by at most 1.0e-12, inside that spread. Not this change's (both paths); for C3/C17. - unpinned (the graph partitioner), the global restart's partition differed from the run it restarted (rank 0: 420 elements against 416), so its per-rank lagged state was another partition's; the views restart keeps the run's partition by construction. - test_subdomain_view_restart 8 passed (35 s); test_archive_*, test_archiver_write_field, test_hotstart_*, test_gauges*, mesh_tests, ci, poisson_2d, test_adr_problem, test_domain_ymf, multilevel, rank-interface, periodic: 1629 passed, 3 xfailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eed with C17/C14) The hand-off point agreed with the C17/C14 session (2026-11-05): NumericalSolution's mesh setup passes restartArchive=self.ar[0] -- the run's open archive, not its path: a hot start opens each rank's file for append, and HDF5 refuses a second, read-only open in the same process -- unless opts.hotStartMesh is 'global' (an options attribute for now; a parun flag waits for C15). Nothing else in NumericalSolution changes. The vortex2D driver now sets opts.hotStartMesh instead of wrapping generateMesh, and the bit-identity test fails if either hot start logs "no solver state to restore" (a mesh numbered otherwise than the archive's makes a hot start a silent restart; C17/C14's request). Measured: test_subdomain_view_restart + test_hotstart_parallel_state + test_hotstart_state: 16 passed. That log line, counted in the earlier runs: unpinned global restart 4 (one per model: the graph partitioner gave another partition, so every model restarted without its lagged state); views restart 0; pinned global restart 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C7.1 Phase D (#213) found that a hot start through the global mesh path, with the graph partitioner unpinned, gets another partition than the run it restarts: every model's solver state is refused (its fingerprint differs) and the run quietly becomes a restart, said only per model at log level 0. The recorded decision (2026-10-29) is that a refused restore continues as a restart with a message, so this keeps the behaviour and makes the message loud: one RuntimeWarning on the first rank (and one WARNING log line) naming the models and the reason, and saying how to continue exactly (the archived partition: same ranks, the per-rank mesh views or a pinned partitioner) or to ask for a restart (--hotStartMode restart). The reason now tells a different partition (same ranks and mesh, other pieces) apart from another rank count or no state at all; the existing texts are unchanged. Whether a refused continue should instead be an error is left to cekees. test_a_continue_on_another_partition_warns_once (harness OTHERPARTITION=1 changes only the partition hashes of the fingerprint): before, no warning; after, exactly one, naming rans2p..mcorr and "different partition" (the run differs from the uninterrupted one by 5.2e-1, as a restart does). test_hotstart_*, test_state_contract, test_step_retry: 48 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…chived step
cekees decided (2026-11-05) that a hot start on another partition is
legitimate, not an error: with the solution archived through MPI-IO and a
global mesh, it restarts from the archived step's solution without the
solver state (integrator history, lagged values). That is what the continue
fallback does; this pins it.
damBreakLevelSet archived on 2 ranks at 0.04, hot-started on 3 ranks to 0.1
(pinned partition):
continue (falls back) vs --hotStartMode restart, every rank 0.0 and
9.9e-14 in two runs (3 ranks is not deterministic run to run; C7.1 saw
2.1e-12 on vortex2D), same accepted steps, one RuntimeWarning
3-rank fallback vs a serial restart from 0.04, gathered 8.2e-9
uninterrupted run vs that serial restart 3.6e-1
so the fallback restarts from the archived 0.04 solution, read through the
global mesh on the new partition, not from the initial state. The test
asserts < 1e-10. The harness writes u<rank>.csv in parallel (all ranks wrote
u.csv before). test_hotstart_levelset + test_step_retry: 25 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cients - docs/source/models/adr.rst (new, in the Models toctree): what the ADR model solves, the embedded boundary (CutFEM: Nitsche or Robin, ghost penalty) and the immersed interface (IFEM: two-branch P1/P2 basis, SCIFEM edge terms, interior penalty, prescribed jumps), triply-cut cells, the drivers that reproduce the paper (paper=True, test/option map, an example command), the two-layer overlap needed in parallel, the test/IFEM tests, and the reference. - proteus/ADR.py: NumPy-style docstring for ADR.Coefficients (rendered by napoleon) describing all 42 arguments: defaults, sign conventions, the values the paper uses, that aOfX must agree with mua/mub when the immersed interface is on, and which arguments are accepted but unused (immersedBoundary_penalty, immersedBoundary_u, test). Docstring only; no code change. - docs/source/index.rst: Running path test/ci (was tests/ci); the ADR capability bullet mentions unfitted interfaces and links the new page; the paper added to the references. - docs/source/models/navier_stokes.rst: RANS2P_IB described consistently as two-phase flow with immersed solid boundaries (was "three-phase" in one place), and distinguished from the immersed finite elements of ADR. Built locally with Sphinx 8.2.3 and graphviz 14.1.2 (no doxygen): no warnings from these files; the new page, the Coefficients parameter list, the cross-references and the inheritance diagrams render. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEhZZbGLmd57vYntPKW6am (cherry picked from commit e8664b4)
…tements to Transport) The floor was recorded on main ce43b73 (33f30b1): 522 of 685 statements, 76.2%. #199 (C0 round 5, 76f3e12) replaced NCLS3P's own scatterDOFArray (15 statements, all covered) by the one on OneLevelTransport, so main c09280c has 507 of 670, 75.67%: the same lines untested, a smaller file. This branch touches no NCLS3P line; its coverage job (run 42119354796) reports NCLS3P 670 statements, 75.7%, the only module below a floor. Same hunk as 7c2a5d5 on c0-r6; the new floor is the measured value rounded down to a tenth. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test/TwoPhaseFlow damBreakLevelSet with interface=1 (RANS2P + CLSVOF, he=0.1), hot start vs the uninterrupted run, largest relative difference over every model's dofs at the final time (macOS arm64, serial): restart at 0.04 (archived every 0.02), run to 0.1 main c09280c 2.3e-3 (CLSVOF), this branch 0.0 restart at 0.005 (archived every 0.005), run to 0.03 main c09280c 1.2e-2 (CLSVOF), this branch 0.0 Same accepted times and forceHistory_p rows after the restart. --hotStartMode restart at 0.04: 5.5e-3 (so the check can fail). Nothing changed for it here: the generic owners of 605b1e5 and 9f55ebe (coupled fields, BackwardEuler state, solver unknowns, next system step, CLSVOF's disc_ICs in state()) carry it. test_hot_start_continues_the_uninterrupted_run is parametrized over the level-set family and CLSVOF: 4 passed (-n 4, 5 s). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lag state VOF3P.ShockCapturing and NCLS3P.ShockCapturing run the same machine as VOF's and NCLS's (start unlagged with numDiff_last the alias of numDiff, switch to a lagged copy after nStepsToDelay history updates) without DelayedLagState, so a hot start rebuilt it unlagged: the first step after the restart used the current numDiff where the uninterrupted run used the lagged one. They now take the mixin (lag, nSteps, alias or numDiff_last copy). test/surface_tension/rising_bubble_rans3p (VOF3P, NCLS3P, RDLS, MCorr3P, RANS3PF, PresInc, Pres, PresInit), hot-started at 0.02 and run to 0.05, largest relative difference over each model's dofs vs the uninterrupted run (macOS arm64, serial; found with the dump-and-transplant tools of this round: dump every array and scalar of every model at its first solve after the restart, then put the uninterrupted run's values into the hot run by group): main c09280c 8.1e-1 (MCorr3P), VOF3P 2.5e-2 this branch before this commit 2.2e-3 (MCorr3P), VOF3P 2.4e-5, velocity 1.2e-4; pressureInitial 1.4e-2 this commit 1.9e-4 (MCorr3P), VOF3P 6.1e-8, velocity 5.1e-7; pressureInitial 1.4e-2 each left out, with the next commit (which brings the rest to 0.0): VOF3P shock capturing 1.3e-4 NCLS3P shock capturing 1.9e-3 RANS3PF.SubgridError also switches to lagging on this deck (lag/nSteps differ after the restart), but a state() for it changed nothing (0.0 with and without, next commit applied), so it stays "rebuilt" in the inventory. Inventory: SC mprans.NCLS3P and mprans.VOF3P ShockCapturing unmeasured -> state. test_state_contract: 8 passed. The pinning test comes with the next commit, which closes the remaining gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Between reading the archive and the first step, a hot start evaluates every model's residual once (to compute the
coefficients) and runs the spin-up; either may change a model's dofs, which the uninterrupted run never does at that
instant. Two causes, on test/surface_tension/rising_bubble_rans3p:
- RDLS's residual freezes its interface dofs to the level set's current dofs (freeze_interface_within_elliptic_redist).
At the restart those are NCLS3P's mass-corrected dofs; the uninterrupted run still holds RDLS's own last solution
(the archive's phid, exactly), from which RDLS's next preStep builds the normal reconstruction (lumped_qx/qy,
ELLIPTIC_REDISTANCING=2). Found by tracing RDLS's dofs through the hot start: they left phid by 1.2e-5 in the
coefficient evaluation (NumericalSolution line 653), nowhere else.
- the spin-up (modelSpinUpList = [pressureInitial]) solves pressureInitial again at the restart time; the
uninterrupted run solved it at t=0 only. Nothing else read it after the spin-up on this deck (every other model
0.0 with the RDLS cause alone fixed), but its dofs and archive differed.
NS_base.restoreArchivedDofs keeps each model's dofs as read and puts them back after the solver state, for every
model whose state was restored (a continue hot start). The solver's unknowns are the unknowns owner's, as before. A
restart (--hotStartMode restart, or a continue that falls back to one) is unchanged: it rebuilds from the restart
state, spin-up included.
rising_bubble_rans3p hot-started at 0.02, run to 0.05, largest relative difference over each model's dofs vs the
uninterrupted run (macOS arm64, serial):
before this commit 1.9e-4 (MCorr3P), VOF3P 6.1e-8, velocity 5.1e-7; pressureInitial 1.4e-2
after 0.0 on every model, pressureInitial included; same accepted times; forceHistory_p,
forceHistory_v and momentHistory rows identical
each cause left out RDLS 1.9e-4 (MCorr3P); pressureInitial 1.4e-2 (its own dofs only)
--hotStartMode restart from the same archive: 8.4 (pressure increment), 8.4e-1 (MCorr3P)
damBreakLevelSet (both restarts, level set and CLSVOF) stays 0.0.
The deck gains a context option T (the automated test's final time, default 0.01 as before) so the harness can run it
further. test_hotstart_levelset.py: test_rans3p_hot_start_continues_the_uninterrupted_run (continue exact; restart
> 1e-3); 12 passed (-n 4, 12 s). Also run (-n 4): test_step_retry, test_hotstart_*, test_state_contract,
test_mpi_launch_retry, test/TwoPhaseFlow, test/HotStart_3P, test/surface_tension/rising_bubble_rans3p: 89 passed
(TwoPhaseFlow with the tree's scripts/parun first on PATH).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main train 2026-11-06: C47 Cython 3 binding fix, C7.1 Phase D per-rank restart views, ADR docs
… on every rank
archiveSolverState compares the ranks' lists of dataset names before its collective creates and writes
nothing for a model whose lists differ. The lists follow the state() dicts' insertion order, and RANS2P's
numericalFlux.ebqe lists ('u', 0)..('u', 3) in an order set by each process's string hash seed, so on two
ranks the coupled-fields owner's names came out in different orders about one run in two: "the ranks have
different solver state to write ... model rans2p" (level-0 log line only), no solver_state/rans2p in the
archive, and the next same-rank continue fell back to a restart with the RuntimeWarning. The earlier
2-rank "0.0" for damBreakLevelSet was a run that happened to hash alike.
Measured (damBreakLevelSet, 2 ranks, -mat_partitioning_type current, archive every 0.02 to 0.04):
archives missing solver_state/rans2p, random seeds pre-merge c0bed9e 3/6, after the merge of
cekees/main 5/6 (not #213's: same before it)
per-rank seeds equal (1/1, 2/2, 7/7, 5/5) never; different seeds 5/6: always
continue at 0.04 to 0.1, seeds 5 and 6, vs the uninterrupted run (same seeds)
before 3.7e-3 on both ranks, one RuntimeWarning (rans2p)
after 0.0 on both ranks, same accepted times, no warning
The fingerprint stays first and the step stamp last (written last, as the docstring requires).
The order inside an owner's state does not matter on read (by name).
test_a_two_rank_continue_is_exact_whatever_each_rank_hashes: an MPMD launch with PYTHONHASHSEED 5 and 6;
fails without this change, passes with it. With cekees/main afcedf6 merged (#204, #213, #215), -n 4:
test_step_retry, test_hotstart_* (test_hotstart_levelset 13), test_state_contract, test_mpi_launch_retry,
test/TwoPhaseFlow, test/HotStart_3P, test/surface_tension/rising_bubble_rans3p,
test/mesh_tests/test_subdomain_view_restart (skipped: this env's ymf has no ymf.mesh),
test_cython_class_attributes: 114 passed, 1 skipped. The one-warning tests (perturbed partition
fingerprint; 2 ranks archived, 3 continued) pass with #213's per-rank-view restart source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
C17/C14 round 1 (owner of StepControl/SplitOperator/TimeIntegration/ShockCapturing this round). One commit per item, each with a test. macOS arm64, serial, env proteus-bc-261025; "largest relative difference" is over every model's dofs at the final time.
so.systemStepSchedule,SO_base.setStepSchedule): a replay takes the first step too. CLSVOFdisc_ICsin state() (a retried first step skipped the disc-IC spin-up)BackwardEuler(_cfl)state (m_last,dtLast); RANS2P SC/SG lag machines-H -t: an archived time within 1e-6 of the smallest archived interval counts as at t (hot_start_step)-t 0.06, archive holds 0.06000000000000001: continued from 0.04hotStartModedefault to continue (an installedparunis an install-time copy; the env's is from Oct 25)proteus/mprans/NCLS3P.py76.2 -> 75.6 (#199 moved 15 covered statements to Transport; same hunk as 7c2a5d5 on c0-r6)interface=1) is bit-identical; nothing changed for it, the owners above carry itDelayedLagState(rebuilt unlagged by a hot start)NS_base.restoreArchivedDofs): RDLS's first residual froze its interface dofs to NCLS3P's corrected ones; the spin-up re-solved pressureInitial at the restartnumericalFlux.ebqekey order follows each process's hash seed, the writer's rank-by-rank name check failed and skipped all of rans2p's state (pre-existing, not #213)Ablation for 9f55ebe (each piece left out alone): coupled fields 6.6e-2, BackwardEuler state 5.0e-1, RANS2P SC 3.2e-3, SG 6.7e-4, unknowns 5.4e-14. The CFL controller's
dt_model_lastmade no difference oncedtLastis back, so it is unchanged.Ablation for c790729 + c0bed9e on rising_bubble_rans3p (each left out alone, the rest applied): VOF3P SC 1.3e-4, NCLS3P SC 1.9e-3, RDLS archived dofs 1.9e-4, pressureInitial archived dofs 1.4e-2 (its own dofs only). RANS3PF's subgrid error lags on this deck too, but a
state()for it changed nothing (0.0), so it is not added (inventory stays "rebuilt").Measured, no change (decision for cekees, in the handoff): an archive hot start shifts the dof history twice before its first step (once before the stepping loop, once at the step), so
dof_last_lastequalsdof_lastthere: on rising_bubble_rans3p it differs from the uninterrupted run's by up to 0.77 relative (MCorr3P), 0.18 (velocity), 0.14 (pressure increment), while every dof, step and force row is 0.0. Nothing in the tree readsdof_last_lastfor a computation (only the PUMI adapt transfers it, as*_old_oldfields nobody reads);dof_lastis exact. The PUMI hot start reloads the history from its checkpoint after the first shift, so it is right there.Tests run locally: test_step_retry (17), test_hotstart_* incl. the new test_hotstart_levelset (6), test_state_contract, test_mpi_launch_retry, test/TwoPhaseFlow, test/HotStart_3P: 76 + 6 passed. At c0bed9e: the same set plus test/surface_tension/rising_bubble_rans3p, 89 passed (-n 4; test_hotstart_levelset now 12: level set and CLSVOF x two restarts, 3P continue and restart). After the merge (3e1d643), -n 4: test_step_retry, test_hotstart_* (test_hotstart_levelset 13), test_state_contract, test_mpi_launch_retry, test/TwoPhaseFlow, test/HotStart_3P, test/surface_tension/rising_bubble_rans3p, test/mesh_tests/test_subdomain_view_restart (skipped: env's ymf lacks ymf.mesh), test_cython_class_attributes: 114 passed, 1 skipped. #213's per-rank-view restart source: same-rank 2-rank continue reads the views, same fingerprint, exact; the one-warning tests (perturbed fingerprint; 2 ranks archived, 3 continued) pass. CI is the oracle.
Also measured: the damBreakLevelSet hot start at 0.04 on 2 ranks (
-mat_partitioning_type current): both ranks' dofs 0.0 at t=0.1 (not in a test). Not done:parunas a console entry point (so it cannot go stale) left for C15.🤖 Generated with Claude Code