Skip to content

Add DiracDelta scatter for Points on DGSEM 2D/3D models#125

Merged
fluidnumericsJoe merged 4 commits into
mainfrom
feature/dirac-delta-points
May 20, 2026
Merged

Add DiracDelta scatter for Points on DGSEM 2D/3D models#125
fluidnumericsJoe merged 4 commits into
mainfrom
feature/dirac-delta-points

Conversation

@fluidnumericsJoe
Copy link
Copy Markdown
Member

Summary

  • Add pts%DiracDelta(geometry, scalar) to the Points class. Each stored point owns one variable of the target MappedScalar2D/3D and receives the discrete post-mass-matrix delta f_{ij[k]} = l_i(ξ₀)·l_j(η₀)[·l_k(ζ₀)] / (w_i·w_j[·w_k]·J₀) for iEl = elements(p), zero elsewhere. J₀ is the polynomial interpolation of nodal J at the source location, which makes Σ f·w·w·J = 1 hold algebraically on every element (affine or curved).
  • The form is post-mass-matrix: it can be added directly to dSdt / the source term in a DGSEM model because MappedDGDivergence already divides by w·w·J.
  • HIP kernel + Fortran wrapper mirror the host path for GPU-resident scalars; select type keeps the override compatible with non-GPU subtypes (falls back to the inherited host impl). The kernel reuses the per-point Lagrange basis cache populated by LocatePoints and writes scalar%interior_gpu directly; a pre-launch hipMemset clears the field so unlocated points and non-owning elements remain zero.
  • *.sqsh added to .dockerignore (mirrors the existing *.sif exclusion) so large local archive files don't bloat the Docker build context.

Design notes

  • Strength: unit strength (S = 1). Caller scales after the call if needed.
  • API contract: scalar%nVar == pts%nPoints. Each point owns its own column so contributions never overlap. Points with elements(p) == 0 (not located on this rank) leave their column zero.
  • Face-shared points: receive their entire contribution in the single element LocatePoints chose — no S/2 face split.
  • Cache reuse: when pts%nCached == scalar%interp%N, the cached basis values from LocatePoints are reused. Otherwise the host path recomputes; the GPU path errors out because device basis cache is mandatory there (matches the existing EvaluateScalar GPU contract).

Test plan

  • test/points_dirac_delta_2d.f90 — conservation, other-element isolation, and node-coincident value
  • test/points_dirac_delta_3d.f90 — 3D analogue
  • CI run (existing CPU + GPU jobs should pick up the new tests via the test/CMakeLists.txt registration)
  • Local CPU container build — not run by me: the host /home partition is at 100%, so I couldn't complete a docker/podman build. Asking CI to verify.

🤖 Generated with Claude Code

Implements pts%DiracDelta(geometry, scalar) on the Points class. Each
stored point owns one variable of the target MappedScalar2D/3D and
receives the discrete post-mass-matrix delta

  scalar%interior(i,j[,k],iEl,p) = l_i(xi_p)*l_j(eta_p)[*l_k(zeta_p)]
                                 / ( w_i*w_j[*w_k] * J_0(p) )

for iEl = elements(p) and zero in every other element. J_0(p) is the
polynomial interpolation of the nodal Jacobian determinant at the source
location, so the conservation property
sum_{i,j[,k]} f * w_i*w_j[*w_k]*J_{ij[k]} = 1 holds algebraically
regardless of element curvature.

The form is "post mass matrix" so it can be added directly to dSdt /
the source term in a DGSEM model -- MappedDGDivergence already divides
by w_i*w_j*J_{ij}.

A HIP kernel mirrors the host path for GPU-resident scalars; the
override uses select type so non-GPU subtypes still fall through to
the inherited host implementation. The kernel reuses the per-point
Lagrange basis cache populated by LocatePoints and writes
scalar%interior_gpu directly. A pre-launch hipMemset clears the field
so unlocated points and non-owning elements remain zero.

Two new tests assert (1) discrete conservation = 1, (2) other elements
are exactly zero, and (3) on a node-coincident source the only nonzero
entry equals 1/(w*w*J_node).

Adds *.sqsh to .dockerignore to keep large local archive files out of
the Docker build context.
Fortran treats N and n as the same identifier. The two new DiracDelta
specifics declared `m, n, N` (2D) and `m, n, l, N` (3D) in a single
list, so n/N collided and the entire declaration was rejected under
`implicit none` -- gfortran 9/10/11/12 all reported the loop indices
as having no implicit type.

Rename the J0-interpolation indices to mm/nn(/ll) so they are distinct
from N. Also drop an unused `k` local in the 2D test and tidy a stray
double-space before an inline comment that fprettify rewrote.
On GPU builds DiracDelta writes scalar%interior_gpu; the host-side
scalar%interior buffer that the tests inspect remains the zero-init
state from %Init, producing the spurious "conservation failure
integral=0" reported on amd-mi210-coverage build 121.

Call f%UpdateHost() after pts%DiracDelta in both tests. On the CPU
build the call is a no-op, so behaviour is unchanged there.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

❌ Patch coverage is 93.58974% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/SELF_Points_t.f90 93.58% 5 Missing ⚠️

📢 Thoughts on this report? Let us know!

Codecov flagged three uncovered branches in pts%DiracDelta_{2,3}D:
the on-the-fly Lagrange recompute (useCache=False), the nDim guard,
and the scalar%nVar /= nPoints guard.

Positive tests: extend points_dirac_delta_{2,3}d to run a second
DiracDelta against a scalar at a different polynomial degree so the
cached basis from LocatePoints no longer matches and the host
fallback path fires. Wrap in #ifndef ENABLE_GPU because the GPU
override intentionally stops on a cache-N mismatch (it has no
recompute fallback).

Negative tests: four new programs each set up a deliberate mismatch
(2D/3D x nDim/nVar), call DiracDelta, and let the guard stop 1.
Registered with CTest WILL_FAIL TRUE so a non-zero exit is the pass
criterion. The same programs exercise the GPU guards on GPU builds
(select type + nDim/nVar checks in src/gpu/SELF_Points.f90).
@fluidnumericsJoe fluidnumericsJoe merged commit eccd4d7 into main May 20, 2026
11 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.

1 participant