Skip to content

fix(ddt): Lagrangian history writes use the modern data layout (BF-04)#310

Open
lmoresi wants to merge 1 commit into
developmentfrom
bugfix/track0-ddt-history
Open

fix(ddt): Lagrangian history writes use the modern data layout (BF-04)#310
lmoresi wants to merge 1 commit into
developmentfrom
bugfix/track0-ddt-history

Conversation

@lmoresi

@lmoresi lmoresi commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Fixes audit finding SWARM-06 (Track 0 item BF-04, docs/reviews/2026-07/REMEDIATION-WORKLIST.md / SWARM-SUBSYSTEM-REVIEW.md).

The Lagrangian history-update blocks in systems/ddt.py (Lagrangian and Lagrangian_Swarm) wrote per-component particle history as psi_star_0[i, j].data[:] = .... Indexing a SwarmVariable goes through MathematicalMixin.__getitem__ and returns a symbolic component (an UnderworldAppliedFunction) with no .data attribute, so the first update_pre_solve of any user-configured Lagrangian history raised AttributeError and the advertised feature (solver_template.py:92-93, solvers.py:196-197) was unusable. The only prior test (test_0007) set _history_initialised manually and never advected, so the crash was uncovered.

Reproduction

Confirmed on this branch's base (development@6451896a) by swapping the baseline ddt.py into site-packages and running the new regression tests:

AttributeError: '{\left<u^{ * }\right>}' object has no attribute 'data'
  at underworld3/systems/ddt.py:3197 (Lagrangian.initialise_history)

All three new tests fail with this error against the old write path, and pass with the fix.

Fix

At the four sites (previously ddt.py:3197, 3281, 3549, 3638-3639):

  • Component writes go through the canonical (N, components) storage: psi_star_0.data[:, var._data_layout(i, j)].
  • Particle coordinates are read from swarm._particle_coordinates.data instead of the deprecated swarm.data.
  • The deprecated swarm.access() context wrappers around these blocks are removed.

These blocks now sit directly on the modern data-access pattern, so the Wave B migration (WB-02) has nothing left to do at these lines. No solver numerics are touched.

Tests

New: tests/test_0857_lagrangian_ddt_advecting_history.py (level_1, tier_a)

  • Lagrangian scalar: auto-initialise, history shift, swarm advection; asserts history values equal psi at each particle's pre-advection position (coordinate-based, ordering-robust).
  • Lagrangian vector: asserts each component lands in its own _data_layout column.
  • Lagrangian_Swarm: step-averaged (phi = 1/2) blending across two updates with a user-driven swarm advection in between; asserts blend and chain-copy values.

Gate results

  • Serial level_1 and tier_a, pre-fix baseline (new file excluded): 295 passed, 7 skipped, 4 xfailed, 1 xpassed — green.
  • Serial level_1 and tier_a, post-fix (new file included): 298 passed, 7 skipped, 4 xfailed, 1 xpassed — green.
  • MPI np2: test_0857 3 passed; test_0855_oldframe_sl_traceback 5 passed; test_1054_advdiff_monotone_mode_kwarg 6 passed, 1 failed — the failure is TestMonotoneSolverIntegration::test_pick_runs_end_to_end, which hits the intentional NotImplementedError: monotone='pick' is serial-only gate; it fails identically at the baseline (pre-existing, unrelated: the test lacks an MPI skip guard).

Underworld development team with AI support from Claude Code

The history-update blocks in Lagrangian and Lagrangian_Swarm wrote
per-component particle history as psi_star_0[i, j].data[:] = ...
Indexing a SwarmVariable returns a symbolic component (via
MathematicalMixin.__getitem__, an UnderworldAppliedFunction) which has
no .data attribute, so the first update_pre_solve of any user-
configured Lagrangian history raised AttributeError at ddt.py:3197 and
the advertised feature was unusable (audit finding SWARM-06,
runtime-confirmed).

Component writes now go through the canonical (N, components) storage
via psi_star_0.data[:, var._data_layout(i, j)], particle coordinates
are read from swarm._particle_coordinates.data instead of the
deprecated swarm.data, and the deprecated swarm.access() context
wrappers around these blocks are removed. The four sites (previously
ddt.py:3197, 3281, 3549, 3638-3639) now sit directly on the modern
data-access pattern, so Wave B migration has nothing left to do there.

Adds advecting-history regression tests for both classes
(tests/test_0857_lagrangian_ddt_advecting_history.py): scalar and
vector Lagrangian histories through initialise/advect/shift, and
Lagrangian_Swarm step-averaged blending across a user-driven
advection. All three fail with the original AttributeError against
the old write path.

Underworld development team with AI support from Claude Code
Copilot AI review requested due to automatic review settings July 3, 2026 22:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes the Lagrangian DDt history update path in ddt.py so per-component history writes use the canonical SwarmVariable storage layout (.data[:, _data_layout(i,j)]) rather than indexing into symbolic components (which previously raised AttributeError on first update_pre_solve). Adds a regression test module that actually advects particles and validates both history shifting and component layout.

Changes:

  • Fix Lagrangian / Lagrangian_Swarm history initialisation and post-solve updates to write via canonical (N, components) storage and to read coordinates from swarm._particle_coordinates.data.
  • Remove deprecated swarm.access() wrappers around these history update blocks.
  • Add regression tests covering scalar and vector Lagrangian histories under swarm advection and step-averaging.

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 Corrects history component writes to use modern SwarmVariable canonical data layout and modern coordinate access; removes deprecated access wrappers.
tests/test_0857_lagrangian_ddt_advecting_history.py Adds regression coverage for advecting-history behavior and verifies component-to-column layout via _data_layout.

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

Comment on lines +21 to +24
import numpy as np
import pytest
import sympy

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