Fix NavierStokesSLCN DDt projection source shape mismatch (#180)#183
Fix NavierStokesSLCN DDt projection source shape mismatch (#180)#183lmoresi wants to merge 2 commits into
Conversation
When SNES_MultiComponent_Projection was wired into the SemiLagrangian DDt
path, the psi_fn setter (line 1402) and _setup_projections (lines 1373,
863) were updated to flatten the source tensor to a (1, Nc) row matrix
via _build_projection_source. The fallback inside update_pre_solve at
line 1769 — taken when uw.function.evaluate() raises on expressions
containing derivatives (the NS viscous flux every step) — missed the
migration and assigned self.psi_fn directly, producing:
sympy.matrices.exceptions.ShapeError:
Matrix size mismatch: (1, 3) + (2, 2).
Stokes never hits this because its projection source has no derivatives.
NavierStokes hits it every solve.
Fix: route the fallback through _build_projection_source like the setter
already does. Adds tests/test_0610_navier_stokes_slcn_projection.py which
reproduces the user's failure (one ns.solve() on a tiny mesh) — reliably
raises ShapeError without the fix, passes with it.
Reported by @Chawgai. Diagnosed with @ss2098's investigation pointing at
the SLCN projection/history-field path.
Underworld development team with AI support from Claude Code
There was a problem hiding this comment.
Pull request overview
Fixes a NavierStokes SemiLagrangian (SLCN) DDt projection fallback path where tensor-valued projection sources were not flattened to the (1, Nc) row-matrix shape required by SNES_MultiComponent_Projection, which previously caused SymPy ShapeError during solves.
Changes:
- Route the
update_pre_solve()projection fallback through_build_projection_source()so tensor sources are flattened consistently. - Add a regression test that runs a minimal
NavierStokes.solve()and asserts it no longer raises the(1, 3) + (2, 2)shape mismatch error.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/underworld3/systems/ddt.py |
Updates the SemiLagrangian DDt fallback projection source wiring to apply tensor flattening via _build_projection_source(). |
tests/test_0610_navier_stokes_slcn_projection.py |
Adds a regression test covering the NavierStokes SLCN DDt projection fallback shape mismatch. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) | ||
| self._psi_star_projection_solver.smoothing = 0.0 | ||
| self._psi_star_projection_solver.solve(verbose=verbose) | ||
|
|
Copilot flagged that after the shape fix, the multicomponent projector writes into _psi_star_flat_var but update_pre_solve() never unpacks the solved flat (1, Nc) field back into psi_star[0]. Mirrors the fan-out already in initialise_history() (~ddt.py:1540): walk _psi_star_indep_indices, copy flat var k-th component into psi_star[0][:, i, j], and symmetric-fill [:, j, i] for SYM_TENSOR. In practice the SemiLagrangian advection step at line 1016 then modifies psi_star[0] in place, so the missing fan-out wasn't producing an immediate symptom in this short test — but the projected flux value was being silently discarded. Fix restores the intended semantics: psi_star[0] reflects the current psi_fn (projected flux) before SL advection updates it for the upstream sample. Reverted the over-eager second test (asserting psi_star[0] non-zero after one solve) since the SL advection masks the missing fan-out in that observation window. Kept the ShapeError regression test which remains a definitive guard for the original visible failure. Underworld development team with AI support from Claude Code
Review pass — ready to mergeScope: the DDt-fallback path was assigning Verification:
Copilot note on stale state: Copilot's earlier comment about the missing fan-out is now stale — the current diff includes exactly the fan-out it asked for (lines 1779–1785). Worth marking resolved. Suggested follow-up: the fallback exists at all because LGTM modulo standard maintainer eyeball. Underworld development team with AI support from Claude Code |
Summary
One-line outdated-API fix. When
SNES_MultiComponent_Projectionwas wired into theSemiLagrangianDDt path, thepsi_fnsetter and_setup_projectionswere updated to flatten the source tensor to(1, Nc)via_build_projection_source— but the fallback insideupdate_pre_solve(taken wheneveruw.function.evaluate()raises on expressions containing derivatives) was missed and kept assigningself.psi_fndirectly.Stokes never hits this fallback because its projection source has no derivatives. NavierStokes hits it every solve, producing:
—
(1, 3)is the projector'sNc=3symmetric-tensor solution variable in 2D;(2, 2)is the raw stress flux tensor that should have been flattened.Files
src/underworld3/systems/ddt.py— route the fallback through_build_projection_source.tests/test_0610_navier_stokes_slcn_projection.py— regression test: onens.solve()on a tiny mesh. Reliably raisesShapeErrorwithout the fix, passes with it.Test plan
pytest tests/test_0610_navier_stokes_slcn_projection.py— passes(1, 3) + (2, 2)nstt.pyreproducerReported by @Chawgai. Diagnosed with @ss2098's investigation pointing at the SLCN projection/history-field path as the root cause.
Underworld development team with AI support from Claude Code