|
| template<typename M > |
| | H5Part::Scalar (const char *, M) -> Scalar< M > |
| | Deduction guide so that Scalar{"name", &P::m} deduces the member-pointer type.
|
| |
| template<typename... Ms> |
| | H5Part::Vector (const char *, Ms...) -> Vector< Ms... > |
| | Deduction guide so that Vector{"name", D_DECL(&P::x, &P::y, &P::z)} deduces the member-pointer types.
|
| |
| template<typename A , typename B > |
| constexpr bool | detail::eqMember (A, B) noexcept |
| | Type-mismatched overload of the pointer-to-member equality (always false).
|
| |
| template<typename A > |
| constexpr bool | detail::eqMember (A a_lhs, A a_rhs) noexcept |
| | Same-type overload of the pointer-to-member equality.
|
| |
| template<typename Tup , typename M > |
| constexpr std::size_t | detail::indexOfImpl (const Tup &, M, std::index_sequence<>) noexcept |
| | Base case of the compile-time member-pointer index search (empty sequence).
|
| |
| template<typename Tup , typename M , std::size_t I, std::size_t... Rest> |
| constexpr std::size_t | detail::indexOfImpl (const Tup &a_t, M a_target, std::index_sequence< I, Rest... >) noexcept |
| | Recursive step of the compile-time member-pointer index search.
|
| |
| template<typename Tup , typename M > |
| constexpr std::size_t | detail::indexOf (const Tup &a_t, M a_target) noexcept |
| | Compile-time index of a member pointer within a tuple (tuple size if absent).
|
| |
| constexpr std::size_t | detail::sumSizes () noexcept |
| | Base case of the variadic constexpr size sum (no arguments).
|
| |
| template<typename... Rest> |
| constexpr std::size_t | detail::sumSizes (std::size_t a_first, Rest... a_rest) noexcept |
| | Variadic constexpr sum of std::size_t values.
|
| |
| template<typename Traits , std::size_t... I> |
| constexpr std::index_sequence< indexOf(Traits::columns, std::get< I >(Traits::h5Columns))... > | detail::deriveH5Seq (std::index_sequence< I... > a_seq) noexcept |
| | Turn a tuple of HDF5 payload member pointers into a std::index_sequence of DERIVED payload-column indices (each via indexOf against the payload columns).
|
| |
| template<typename T > |
| void | detail::pushBytes (unsigned char *&a_buf, const T &a_value) noexcept |
| | memcpy one trivially-copyable value onto a byte buffer and advance the buffer pointer.
|
| |
| template<typename T > |
| void | detail::pullBytes (const unsigned char *&a_buf, T &a_value) noexcept |
| | memcpy one trivially-copyable value off a byte buffer and advance the buffer pointer.
|
| |
Declaration of ParticleSoA, an arena-backed Struct-of-Arrays particle container.
- Author
- Robert Marskar
ParticleSoA is the per-patch particle storage that replaces Chombo's List
. It is the single source of truth for one grid patch worth of particles; the AMR machinery (the ParticleContainer equivalent) owns a LayoutData<ParticleSoA<P>> per level.
The data model
The container stores particles column-major (Struct-of-Arrays): one contiguous array per field. ALL columns live in ONE aligned allocation (the "arena"), laid out as offset slices. Position and weight are stored in double precision – they index the grid and are summed/conserved across the whole population, so they must not lose precision to float roundoff (and must stay double even in a CH_USE_FLOAT build, hence double, not Real). Only the user payload columns may use the narrower ParticleReal.
* [ x | pad | y | pad | z | pad | weight | pad | id | pad | rank | pad | ghost | pad | payload... ]
*
Column base pointers are cached, so element access is as cheap as a plain array. Bulk operations over a compact (size == capacity) container are then a SINGLE contiguous span: one allocation to build, one memcpy (or a zero-copy MPI_Send of data()) to transfer.
Mandatory columns are container-owned; the user supplies only a payload
Every particle in chombo-discharge has a position, a weight, and metadata (a global id + the owning rank). ParticleSoA OWNS those columns and the user does NOT declare them:
- position :
SpaceDim raw scalar double columns (NOT a RealVect column). Per-component storage is what lets the field-update kernels vectorize and is GPU-optimal; double precision is mandatory because a float global position loses sub-cell resolution on fine grids (at ~10^6 cells/dim the float ULP is ~0.1 cell, so small advective steps round to zero – particles freeze).
- weight : one
double column, because per-cell Sum(weight) and merge/split center-of-mass conservation accumulate float roundoff over the whole population, and superparticle weights span a huge dynamic range.
- particleID / rankID : container-owned bookkeeping columns of fixed-width integer type (std::int64_t / std::int32_t – portable across the byte-blit I/O paths). They travel in MPI linearization but are NOT written to HDF5 (regenerated on restart).
The user defines ONLY the extra payload fields (velocity, mobility, energy, ...) as a plain struct, plus a ParticleTraits specialization listing the payload columns. The payload may be empty, so ParticleSoA<> is a ready-made point/tracer particle (position + weight + metadata, nothing else).
Defining a particle type (the complete recipe)
Step 1 – write a plain struct of the EXTRA (payload) members only. Each member becomes one SoA column and must be trivially copyable. Do NOT add position/weight/id/rank; the container already owns those. Vector fields are stored PER-COMPONENT as scalar columns – there is no RealVect column type; declare x/y/z members and promote to RealVect in downstream code if needed. Use ParticleReal for local per-particle physics (velocity, mobility, energy) where float is safe; use double for anything position-like or globally reduced (e.g. an oldPosition, which subtracts from the double position).
struct MyPayload
{
};
CD_PARTICLE_REAL ParticleReal
Floating-point type a user may use for payload columns.
Definition CD_ParticleSoA.H:156
Step 2 – specialize ParticleTraits<MyPayload> with the columns tuple (one pointer-to-member per payload column; the single source of truth for the layout):
template <>
{
static constexpr auto columns = std::make_tuple(D_DECL(&MyPayload::vx,
&MyPayload::vy,
&MyPayload::vz),
&MyPayload::mobility,
&MyPayload::energy);
};
Traits class that a user specializes to describe the PAYLOAD columns of a particle type....
Definition CD_ParticleSoA.H:192
Step 3 (optional) – restrict which PAYLOAD columns go to HDF5 by declaring an h5Columns tuple of member pointers. Position and weight are ALWAYS checkpointed; id/rank NEVER are. If h5Columns is omitted, every payload column is checkpointed.
static constexpr auto h5Columns = std::make_tuple(&MyPayload::energy);
That is all – ParticleSoA<MyPayload> now works. Payload fields are selected by member pointer, e.g. soa.column<&MyPayload::energy>(); the column index is DERIVED from the pointer at compile time, so reordering columns keeps name-based access correct.
Precision
Position and weight are always double; see the data-model note above for why. PAYLOAD columns use whatever type the user declares – ParticleReal is a compile-time alias that defaults to double but may be set to float (define CD_PARTICLE_REAL) for half-memory, 8-wide-SIMD payload against a double-precision grid. position(i) returns a RealVect for cell lookup / Chombo interop (the one geometry type at the accessor boundary).
- Note
- ParticleSoA is move-only: copy construction and copy assignment are deleted so an O(N) per-column copy can never happen implicitly (matching ParticleContainer). When an independent copy is genuinely wanted, use the explicit deepCopy() (returns a new container) or deepCopyTo() (reuses an existing destination's arena).
- Template Parameters
-
| P | User payload struct (extra columns only); defaults to NoPayload. |
| Traits | Column descriptor for the payload; defaults to ParticleTraits |
.