Skip to content

Revert PR #172 (integrals regression) + xfail CellWiseIntegral parity tests#174

Merged
lmoresi merged 2 commits into
developmentfrom
bugfix/integrals-revert
May 7, 2026
Merged

Revert PR #172 (integrals regression) + xfail CellWiseIntegral parity tests#174
lmoresi merged 2 commits into
developmentfrom
bugfix/integrals-revert

Conversation

@lmoresi
Copy link
Copy Markdown
Member

@lmoresi lmoresi commented May 7, 2026

Summary

PR #172 ("Perf: clone DM in Integral.evaluate() (3× speedup; closes #171 narrative)") introduced a regression that broke 4 tests in tests/test_0501_integrals.py. CI on the merged commit (fe05c63) and on development's HEAD shows these failures. This PR reverts #172 to restore correctness, and adds parity test coverage for the sister CellWiseIntegral class — which the original PR claimed worked but never actually has.

What broke

The change in #172 was:

dmc = self.mesh.dm.clone()                # PETSc DMClone does not copy fields
fec = FE.createDefault(dim, 1, False, -1)
dmc.setField(0, fec)                      # clone now has just one P1 field
dmc.createDS()
PetscDSSetObjective(dmc.getDS(), 0, fn)
DMPlexComputeIntegralFEM(dmc, cgvec, ...) # cgvec packed for mesh.dm's multi-field layout

The integrand reads through dmc's single-field DS, but the vector being integrated (cgvec) was packed for mesh.dm's full multi-field layout. The DOFs read are wrong, producing ~2× over-counts on simple cases and NaN/0 on derivative integrands. All four failing tests are explained by this mismatch.

Performance ground truth (#171)

The PR's stated motivation was to fix linear-time growth in t_vol over long convection runs (5 s → 833 s over ~3000 calls). The PR itself notes it could not reproduce the slowdown locally (200 calls × 100-cell mesh stayed flat). The fix was speculative; correctness on the failing tests was lost in exchange for an unverified perf benefit. Issue #171 should be reopened with a real reproducer before any further attempt.

What's in this PR

  1. Revert fe05c63 — restores the pre-Perf: clone DM in Integral.evaluate() (3× speedup; closes #171 narrative) #172 Integral.evaluate() (uses mesh.dm + mesh.dm.getDS() directly).

  2. CellWiseIntegral parity tests (xfail) — the sister class had zero test coverage. While auditing, found two latent bugs:

    The two new tests (test_cellwise_integrate_constants, test_cellwise_integrate_meshvar) are marked xfail with a clear reason. They will start passing when CellWiseIntegral.evaluate() is rewritten to use the same mesh.dm + getDS() pattern that the reverted Integral already uses.

  3. Inline TODO(BUG) in CellWiseIntegral.evaluate() pointing at the same write-up.

Test plan

Underworld development team with AI support from Claude Code

lmoresi added 2 commits May 7, 2026 10:31
…valuate-linear-growth-171"

This reverts commit fe05c63, reversing
changes made to 259958b.
…field-cloning bug

While reverting PR #172 (which broke Integral.evaluate), audited the sister
CellWiseIntegral class — the PR's premise was that CellWiseIntegral "already
takes the right path". It does not:

  1. CellWiseIntegral.evaluate() referenced self.dim (no such attribute),
     so any call to the class crashed with AttributeError before exhibiting
     the deeper bug. This commit fixes it to self.mesh.dim.

  2. With the typo fixed, CellWiseIntegral exhibits the same field-cloning
     bug that broke Integral in PR #172: cloning mesh.dm and attaching a
     fresh single-field discretisation, but then integrating against
     mesh.dm.getGlobalVec() (packed for the multi-field layout), produces
     a ~2x over-count on the unit square.

CellWiseIntegral has zero pre-existing test coverage. This commit adds two
regression tests marked xfail with a clear reason; they will start passing
once CellWiseIntegral is rewritten to integrate against mesh.dm + getDS()
directly (the pre-PR-172 Integral pattern). An inline TODO(BUG) comment in
the source points at the same write-up.

Underworld development team with AI support from Claude Code
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reverts PR #172’s DM-cloning approach in Integral.evaluate() to restore correctness (avoiding DOF layout mismatches), and adds xfail parity tests documenting that CellWiseIntegral.evaluate() currently produces incorrect results due to the same DM/Vec layout mismatch.

Changes:

  • Reverted Integral.evaluate() to compute integrals directly on mesh.dm/mesh.dm.getDS() (instead of cloning and creating a single-field DS per call).
  • Added CellWiseIntegral parity tests (xfail) for constants and a mesh variable integrand.
  • Fixed a CellWiseIntegral dimension typo (self.dimself.mesh.dim) and added an inline TODO describing the known bug.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/underworld3/cython/petsc_maths.pyx Reverts Integral.evaluate() to shared DM/DS path; updates CellWiseIntegral.evaluate() to use self.mesh.dim and adds TODO about known DM/Vec mismatch.
tests/test_0501_integrals.py Adds xfail parity tests for CellWiseIntegral to document current incorrect behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

# Explicit destroy of the clone at the end keeps long runs bounded.
mesh = self.mesh
self.dm = self.mesh.dm # .clone()
mesh=self.mesh
Comment on lines +303 to +311
# TODO(BUG): clone+createDefault+createDS gives dmc a single P1 field,
# but cgvec is packed for mesh.dm's multi-field layout — the integral
# reads the wrong DOFs and over-counts by ~2x on the unit square.
# Same bug as PR #172 (reverted in PR #173-followup). Tests in
# tests/test_0501_integrals.py::test_cellwise_integrate_* are xfail
# until this is rewritten to integrate against mesh.dm + getDS()
# directly (the pre-PR-172 Integral pattern).
cdef DM dmc = self.mesh.dm.clone()
cdef FE fec = FE().createDefault(self.dim, 1, False, -1)
cdef FE fec = FE().createDefault(self.mesh.dim, 1, False, -1)
Comment on lines 310 to 314
cdef DM dmc = self.mesh.dm.clone()
cdef FE fec = FE().createDefault(self.dim, 1, False, -1)
cdef FE fec = FE().createDefault(self.mesh.dim, 1, False, -1)
dmc.setField(0, fec)
dmc.createDS()

@lmoresi lmoresi merged commit 1837cc7 into development May 7, 2026
5 checks passed
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