Skip to content

Add transient subcell (TAS) collision-partner selection method (CPU + KOKKOS) - #31

Open
stanmoore1 wants to merge 50 commits into
masterfrom
claude/subcell-method-evaluation-yk2nuh
Open

Add transient subcell (TAS) collision-partner selection method (CPU + KOKKOS)#31
stanmoore1 wants to merge 50 commits into
masterfrom
claude/subcell-method-evaluation-yk2nuh

Conversation

@stanmoore1

Copy link
Copy Markdown
Owner

Purpose

Adds Bird's transient adaptive subcell (TAS) collision-partner selection method as an alternative to the existing nearcp near-neighbor option, enabled with a new collide_modify subcell yes keyword. Each timestep the particles in a collision cell are binned into a transient grid of subcells (~1 particle/subcell), and a collision partner is drawn from the same subcell, or from expanding shells of neighbor subcells if the home subcell is empty. Like nearcp, this keeps the mean collision separation (MCS) small even when cells are much larger than the local mean free path, which allows accurate simulations on coarser grids with fewer particles.

The method was prototyped on the long-lived subcell branch. This PR brings that branch up to date with master, rewrites the CPU implementation on top of the current Collide structure (fixing a number of bugs found in the prototype), documents and exercises it with examples, and adds a validated KOKKOS port so it runs on CPU threads and GPUs.

Author(s)

Stan Moore, Sandia National Laboratories, with an original CPU prototype from the subcell branch. Implementation completed, corrected, KOKKOS-ported, and verified with assistance from Claude Code.

Backward Compatibility

Fully backward compatible. The feature is opt-in via the new collide_modify subcell keyword, which defaults to no. With subcell no, all code paths (standard random, nearcp, ambipolar, both CPU and KOKKOS) are byte-for-byte unchanged — verified against the existing blessed example logs and against master behavior. No input syntax changes for existing decks.

subcell is mutually exclusive with nearcp, and (for now) errors out cleanly if combined with the ambipolar approximation or with multiple collision groups.

Implementation Notes

Algorithm (src/collide.cpp, Collide::collisions_one_subcell). Templated on dimension. Per cell: Nsub = floor(N^(1/dim)) subcells per direction so the subcell count does not exceed the particle count; particles are binned into a head-insertion linked list (subcell_first/subcell_next/subcell_count, flattened subcell_id); the first particle is random over the cell, the partner is random within its subcell, else random over an expanding, grid-clipped shell of subcells (subcell_ring). A previous-collision-partner exclusion (reusing the existing nn_last_partner machinery) prevents an unphysical immediate re-collision of the same pair, choosing a different/next-nearest partner instead, per Bird. The transient binning is discarded after each cell, so memory overhead is negligible, and it is rebuilt after any reaction that adds/removes a particle so the subcell lists stay consistent with plist.

KOKKOS port (src/KOKKOS/collide_vss_kokkos.cpp). Mirrors the existing nearcp Kokkos path: the parallel_for/parallel_reduce runs one grid cell per thread, and the subcell scratch is kept thread-private by indexing it as (icell, *) view rows — exactly like d_nn_last_partner. Five 2D device views hold the per-cell subcell state, sized (nglocal, maxcellcount) and reallocated alongside d_plist including in the react-retry path. Partner selection (find_nn_subcell) and rebinning (rebin_subcell) are DIM-templated KOKKOS_INLINE_FUNCTIONs that reproduce the CPU shell search and exclusion exactly. The existing react retry/backup machinery is reused unchanged; the subcell scratch needs no backup since it is rebuilt per cell and after each reaction.

Bugs fixed relative to the prototype: infinite loop on an unknown collide_modify keyword (missing final else error); a destructor memory leak (recomb_ijflag); a heap overflow in the particle-creation growth path (grew the wrong arrays); stale subcell lists after reactions could select deleted or uninitialized particles; pow(N,1/3) integer-truncation of the subcell count (now cbrt/sqrt with a tolerance); an unused npmax*npmax allocation (hundreds of MB for large cells) and an unused vmax array; and out-of-range subcell indices for particles on a cell's upper face (now clamped). Development scratch files and stray debug code from the prototype were removed.

Verification.

  • Regression: with subcell no, examples/collide/in.collideInterspecies reproduces the repository's blessed logs column-for-column; the standard and nearcp KOKKOS paths still match the CPU.
  • KOKKOS correctness: built with -DSPARTA_KOKKOS_EXACT=ON, the subcell method reproduces the non-KOKKOS results bit-for-bit (Natt, Ncoll, temperature identical every stats step) for the 2d and 3d subcell examples and for a dissociating-gas case that exercises particle creation and the retry path (Np grows 10000→10751, Nreact and T all match). Multithread and production-RNG runs are physically consistent.
  • Physics: equilibrium Ar box (273 K), collision rate matches VHS theory to within statistics — standard 0.9999, nearcp 0.9991, subcell 0.9984 of the analytic rate (the small deficits are the expected effect of suppressing repeat collisions); equilibrium temperature exactly conserved.
  • Memory safety: dissociation (particle creation), high-rate recombination (deletion), 3000-particle cells (array growth), and 2d all run clean under AddressSanitizer.
  • Mean collision separation: for random selection MCS = 0.662·Δx exactly as theory predicts and scales with cell size; subcell MCS is grid-independent (2.355e-6 m on both 1e-5 and 2e-5 m cells) where nearcp's MCS grows with the cell.

Performance vs near-neighbor.

Selection cost — collide-section seconds for 300 steps, 80k-particle 3d box, serial:

grid particles/cell std nearcp 10 nearcp 30 subcell
20³ 10 1.05 1.45 1.35 1.53
10³ 80 1.07 1.52 1.78 1.69
640 1.00 1.37 1.71 1.63
2963 1.02 1.47 1.69 1.59

Subcell costs about the same as nearcp 30 and, unlike nearcp, is O(1) per selection independent of cell population (no Nlimit to tune) while its partner quality does not degrade as cells coarsen.

Coarse-grid accuracy — Fourier heat conduction between 223/323 K diffuse walls (Kn≈0.024), wall heat-flux error vs a fine-grid (cell≈λ/3) reference (±2–3% statistical noise):

cell size std nearcp 30 subcell
λ/3 (ref) −0.3% +0.5% −0.2%
λ +0.3% −4.0% −2.5%
4.2 λ +26.7% +1.0% +2.2%

On cells ~13× coarser than the λ/3 rule, standard selection is off by 27% while subcell stays within noise of the reference. Because it holds accuracy on coarse grids, subcell lets the grid and particle count shrink together: a coarse-grid subcell run with 12.8× fewer particles matched the fine reference to +2.6%, ran ~15× faster per step, and used half the particle memory — consistent with the ~2× overall efficiency reported by Gallis & Torczynski for the sophisticated-DSMC scheme.

Post Submission Checklist

  • The feature or features in this pull request is complete
  • Suitable new documentation files and/or updates to the existing docs are included (doc/collide_modify.txt)
  • One or more example input decks are included (examples/collide/in.collide_{2D,3D}{,_nn,_sub} with reference logs for 1 and 4 MPI ranks)
  • The source code follows the SPARTA formatting guidelines

Further Information, Files, and Links

  • The KOKKOS subcell path currently supports single-group, non-ambipolar collisions (same scope as its nearcp counterpart); ambipolar and multi-group support is left as follow-up.
  • src/MAKE/Makefile.asan is included as a convenience AddressSanitizer build config used during verification (analogous to the existing Makefile.serial_debug); reviewers who prefer not to ship it can drop that one file with no effect on the feature.
  • References for the method: G. A. Bird, M. A. Gallis, J. R. Torczynski, D. J. Rader, "Accuracy and efficiency of the sophisticated direct simulation Monte Carlo algorithm for simulating noncontinuum gas flows," Phys. Fluids 21, 017103 (2009), DOI:10.1063/1.3067865; M. A. Gallis, J. R. Torczynski, "Effect of Collision-Partner Selection Schemes on the Accuracy and Efficiency of the DSMC Method," Int. J. Numer. Meth. Fluids 67(8):1057-1072 (2011), DOI:10.1002/fld.2409.

🤖 Generated with Claude Code


Generated by Claude Code

jfabell and others added 14 commits August 11, 2022 16:21
…w Collide structure

Merge brings the subcell branch up to date with master (~763 commits).
Conflict resolution and porting decisions:

- src/collide.cpp/h: collisions_one_subcell() rewritten on top of master's
  refactored Collide class (NEARCP/GASTALLY templating, setup(), gas
  collision tally hooks). The port also fixes several defects in the
  original prototype:
  * restored 'else error' in modify_params (unknown collide_modify
    keyword previously hung in an infinite loop)
  * restored memory->destroy(recomb_ijflag) in destructor (leak)
  * removed unused npmax x npmax subcell_list allocation and unused
    vmax array
  * subcell grid size now uses sqrt/cbrt with tolerance so exact
    squares/cubes are not rounded down (pow(27,1/3) -> 2 bug)
  * subcell indices clamped to grid bounds to guard round-off for
    particles on upper cell faces
  * subcell binning rebuilt after reactions delete/create particles
    (previously stale linked lists could select deleted particles or
    read uninitialized subcell IDs; grow block also grew the wrong
    arrays -> heap overflow)
  * added rejection of immediate re-collision of same pair (Bird's
    previous-collision-partner exclusion), falling back to expanding
    shell search, consistent with nearcp's nn_last_partner scheme
  * simplified data structs: per-particle subcell IDs derived from a
    single flattened ID; head-insertion linked list removes
    subcell_mostrecent and all per-cell memsets of particle-length
    arrays; shell enumeration uses clamped loop bounds
  * subcell + nearcp, subcell + ambipolar, subcell + multi-group all
    error out cleanly
- src/KOKKOS/*: taken from master wholesale; the unreviewed Kokkos
  subcell prototype (debug printf scatter) is dropped for now and
  CollideVSSKokkos::init() errors out if subcell is enabled
- removed scratch files: JAB_readme.txt, src/jab_bird_partnerselection.txt,
  src/jab_testproblem/, src/KOKKOS/jab_collide_vss_kokkos.*,
  examples/collide/in.collide_3D_sub_test

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
Make the 2d/3d example trios (default, nearcp, subcell) directly
comparable and physically self-consistent:
- 2d examples previously had inconsistent nrho/fnum vs the created
  particle count, yielding a nearly collisionless gas (~10 collision
  attempts per step); now nrho*V/fnum matches the created particle count
- gas state documented in headers: Ar at 273.15 K, lambda ~ 2.4e-6 m,
  grid cells ~ 4x lambda (the coarse-cell regime where near-neighbor
  partner selection matters), timestep ~ tau/3
- variants differ only in the collide_modify line
- add reference logs for 1 and 4 MPI ranks (results identical)

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
Implements Collide::collisions_one_subcell in CollideVSSKokkos so the
subcell partner-selection method runs on the Kokkos backends (CPU
threads and GPU), previously it errored out under the KOKKOS package.

Design mirrors the existing nearcp Kokkos path: the parallel_for/reduce
runs one grid cell per thread, and the transient subcell binning is kept
thread-private by indexing per-cell scratch as (icell,*) view rows,
just like d_nn_last_partner. Five 2D device views hold the per-cell
subcell state (id, count, first, next, ring), each sized (nglocal,
maxcellcount) and reallocated alongside d_plist including in the
react-retry path. Partner selection (find_nn_subcell) and rebinning
(rebin_subcell) are DIM-templated KOKKOS_INLINE_FUNCTIONs that
reproduce the CPU shell search and previous-partner exclusion exactly.
The react retry/backup machinery is reused unchanged; subcell scratch
needs no backup since it is rebuilt per cell and after each reaction.

Validation (SPARTA_KOKKOS_EXACT, 1 thread, 1 MPI rank) reproduces the
non-Kokkos results bit-for-bit for the 2d and 3d subcell examples and
for a dissociating-gas case that exercises particle creation and the
retry path; multithread and production-RNG runs are physically
consistent (equilibrium temperature and collision rate preserved). The
standard and nearcp Kokkos paths are unchanged and still match the CPU.

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
@stanmoore1
stanmoore1 force-pushed the claude/subcell-method-evaluation-yk2nuh branch from 427f7cf to 9529c04 Compare July 6, 2026 21:24
claude and others added 5 commits July 8, 2026 13:38
The collide_{2D,3D}{,_nn,_sub} reference logs were regenerated on a
sandbox whose libm/compiler produce slightly different floating-point
results than the ubuntu-22.04 CI image, so the committed gold-standard
logs did not match fresh CI runs and the regression tests failed.

This is a cross-platform FP difference, not a bug:
 - no crashes; all cases run to completion
 - kinetic energy is exactly conserved (equilibrium temperature is
   bit-identical every step; elastic VSS collisions conserve energy)
 - the deviation hits the standard, nearcp, and subcell examples
   identically, so it is not specific to the new subcell method
 - the pre-existing collideInterspecies example reproduces its
   committed log exactly on the CI machine, confirming the CI platform
   is deterministic and the sandbox was the outlier

Replace the 12 reference logs with the logs produced by the CI machine
itself (openmpi, 1 and 4 ranks) so future CI runs match.

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
Resolves two conflicts against master's new collide_modify {scheme}
keyword (NTC vs majorant collision frequency):
- src/collide.cpp modify_params: keep both the {subcell} and {scheme}
  keyword clauses
- doc/collide_modify.txt: merge both keywords into the syntax list and
  defaults, and keep both the Bird94 (MCF/NTC) and Bird09 (subcell)
  references

The subcell method composes with the MCF scheme automatically, since
the attempt count is computed by attempt_collision(), which subcell
already calls; verified subcell + 'scheme mcf' runs correctly.

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
The transient subcell method as published (Almgren-Bell, Moore & Gallis,
CSRI Summer Proceedings 2022) selects the partner purely at random from
the minimal-radius subcell set, with no previous-partner test. This
implementation adds the test, matching Bird's nearest-neighbor scheme
and the nearcp option; correct the comment, which wrongly attributed the
test to the published subcell method, and record why it is kept.

Measured on an equilibrium Ar box and a Fourier heat-flow problem
(cell ~ lambda/3, ~60 particles/cell, 2 seeds x 90k averaging steps):

  same-pair repeat fraction, test disabled:
    random selection 0.2%, nearcp 3.2%, subcell 7.3%

  thermal conductivity vs random selection:
    subcell with test     +1.00% +- 0.95%  (1.1 sigma)
    subcell without test  +7.40% +- 0.93%  (8.0 sigma)
    difference            -5.96% +- 0.94%  (6.3 sigma)

Repeat collisions of a pair are statistically equivalent to a single
collision, so a 7% repeat fraction lowers the effective collision rate
by ~7% and inflates the conductivity by a comparable amount, which is
what is observed. With ~1 particle per subcell the partner comes from a
small stable neighborhood, so subcell repeats are more frequent than
nearcp repeats and the test matters more, not less.

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
Review feedback on the upstream PR (sjplimp) noted the subcell
paragraph never said what happens when the keyword is set to {no},
which is the default. Add that, matching how the nearcp paragraph is
written, and rewrap the following text.

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
Review feedback on the upstream PR (sjplimp) asked whether the
partner-selection keywords could be folded into one keyword. They are
mutually exclusive choices of the same thing, so combine them:

  collide_modify partners random
  collide_modify partners nearcp Nlimit
  collide_modify partners subcell

The scheme keyword is deliberately left alone. It sets how many
collisions are attempted in a cell, not which particles are paired, and
it composes with any partners style, so folding it in would make valid
combinations such as mcf + subcell inexpressible.

The subcell keyword is removed outright since it has never appeared in
a release. nearcp is retained as a deprecated alias, since it is used
by examples/cylinder and by the LANL ATS-5 SPARTA benchmark input;
"nearcp yes Nlimit" maps to "partners nearcp Nlimit" and "nearcp no
Nlimit" to "partners random". The new form also drops the old wart of
having to supply an ignored Nlimit when near-neighbor selection is off.

Since a single knob now sets both internal flags, the two can no longer
both be set from an input script; the init() check is kept as a guard
against a derived class setting them directly.

Verified with the same binary that "partners nearcp 30" and "nearcp yes
30" produce bit-for-bit identical results, as do "partners random" and
"nearcp no 30". KOKKOS matches non-KOKKOS bit-for-bit under
SPARTA_KOKKOS_EXACT for the nearcp and subcell examples.

The examples/collide inputs are updated to the new syntax, so their
reference logs no longer match the commands they echo and need to be
reblessed on the CI reference platform. Note that those logs were last
generated on Jul 8, before the master merge that brought in the MCF
scheme, so they are already stale independent of this change.

Co-Authored-By: Stan Moore <stanmoore1@gmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
@stanmoore1
stanmoore1 force-pushed the claude/subcell-method-evaluation-yk2nuh branch from ef7db3f to 17d30cb Compare July 24, 2026 20:23
Brings in 15 commits through 912c9e1 (PR sparta#663). No conflicts; the
merge is a clean fast-forward of unrelated subsystems (fix ablate,
read_isurf validation, fix emit face/surf, KOKKOS fix ave/grid sync).

Verified the collide_modify partners parser still behaves after the
merge: partners random/subcell/nearcp accepted, partners nearcp 0 and
partners bogus rejected, the deprecated nearcp alias still accepted,
and the removed subcell keyword still rejected.

Co-authored-by: Stan Moore <stanmoore1@gmail.com>
Claude-Session: https://claude.ai/code/session_01EibVA6xVmY79EpNi1XeqiB
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.

3 participants