Skip to content

[train] main train 2026-11-06: C47 Cython 3 binding fix, C7.1 Phase D per-rank restart views, ADR docs - #216

Merged
cekees merged 13 commits into
devfrom
train/main-2026-11-06
Nov 6, 2026
Merged

cekees merged 13 commits into
devfrom
train/main-2026-11-06

Conversation

@cekees

@cekees cekees commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner

Fork CI on the combination before the proteustoolkit pull request merges. Each change below was reviewed, and passed CI, as its own pull request on cekees/proteus. This branch merges them
onto main at c09280c with one merge commit per pull request, in the order listed.

Merge with "Create a merge commit", not squash or rebase, so the commits keep their hashes and the fork's pull requests close themselves.

pull request title commits
#204 C47: Cython 3 binds self on class-stored compiled functions (staticmethod sweep, non-mprans) 5
#213 C7.1 Phase D: per-rank subdomain views as the same-rank restart source 4
#215 docs (main): ADR page for the hybrid CutFEM-IFEM method; document ADR.Coefficients (forward port of #197) 1

Diff against main: 39 files changed, 1208 insertions(+), 52 deletions(-).

Note: #215 alone failed only the coverage floor of proteus/mprans/NCLS3P.py (main 76f3e12 from #199 moved 15 covered statements to Transport); #213 carries the one-line floor change (76.2 -> 75.6), so the train coverage job is expected green.

🤖 Generated with Claude Code

cekees and others added 13 commits November 5, 2026 10:05
…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>
…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>
…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)
…covered lines (same hunk as #209 7c2a5d5, #211 68f5df0)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ns (staticmethod sweep, non-mprans)

From cekees/c47-cython3 at 726eed5 (#204).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…estart source

From cekees/c7-phase-d at ea32584 (#213).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…document ADR.Coefficients (forward port of #197)

From cekees/adr-docs-main at 4da6806 (#215).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cekees
cekees merged commit afcedf6 into dev Nov 6, 2026
7 of 9 checks passed
@cekees
cekees deleted the train/main-2026-11-06 branch November 6, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants