Working with CellArrays
CellArrays are the storage primitive behind JustPIC particle containers. They represent a grid where every logical grid cell stores a small fixed-size payload, for example the particle slots belonging to that cell.
Instantiating a CellArray
cell_array(backend, value, ncells, ni) allocates a CellArray over an ni-shaped grid, giving every cell an ncells-shaped payload initialized to value. The payload is what holds per-particle data — coordinates, temperature, phase labels — for the particles belonging to that grid cell.
julia> using JustPIC
julia> import CellArraysIndexing as CAI
julia> ni = (2, 2) # grid cells
(2, 2)
julia> ncells = (2,) # payload slots per cell
(2,)
julia> CA = cell_array(JustPIC.CPU, 20.0, ncells, ni)
2×2 CellArrays.CPUCellArray{StaticArraysCore.SVector{2, Float64}, 2, 1, Float64}:
[20.0, 20.0] [20.0, 20.0]
[20.0, 20.0] [20.0, 20.0]Indexing a CellArray
Indexing by grid cell returns the whole payload stored in that cell. This is convenient for inspection, but it materializes a StaticArray value:
julia> CA[1, 1]
2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2):
20.0
20.0It is however useful to read and mutate the data of the CellArray object directly, without instantiating a StaticArray. For this purpose, CellArraysIndexing provides @index to directly read and mutate the individual elements of the cell.
For example, to read a single element of CA:
julia> CAI.@index CA[2, 1, 1]
20.0Here the first index selects the payload entry and the remaining indices select the grid cell. Mutation uses the same syntax:
julia> CAI.@index CA[2, 1, 1] = 0.0;
julia> CA
2×2 CellArrays.CPUCellArray{StaticArraysCore.SVector{2, Float64}, 2, 1, Float64}:
[20.0, 0.0] [20.0, 20.0]
[20.0, 20.0] [20.0, 20.0]@cell is the companion macro for reading or writing an entire cell payload:
julia> @cell CA[1, 1]
2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2):
20.0
0.0
julia> @cell CA[1, 1] = @cell(CA[1, 1]) .+ 1;
julia> CA
2×2 CellArrays.CPUCellArray{StaticArraysCore.SVector{2, Float64}, 2, 1, Float64}:
[21.0, 1.0] [20.0, 20.0]
[20.0, 20.0] [20.0, 20.0]Backend and layout notes
cell_array dispatches on the KernelAbstractions backend, so the same call allocates a device-resident CellArray when a vendor extension is loaded. The backing layout differs between them: the CPU path uses a block length of 1, while GPU backends use a structure-of-arrays layout. Always go through CAI.@index, @cell, cellaxes and cellnum rather than indexing CA.data directly, so that code stays portable across backends.
API
JustPIC.cell_array — Function
cell_array(backend, x, ncells::NTuple, ni::NTuple)
cell_array(x, ncells::NTuple, ni::NTuple)Allocate a CellArray on backend (a KernelAbstractions backend type such as CPU), with ncells entries per grid cell over a grid of size ni, and fill every entry with x.
The backend form is the preferred allocation path for particle storage and phase-ratio arrays. The backend-less form allocates on CPU.
Examples
index = cell_array(CPU, false, (24,), (64, 64))
field = cell_array(CPU, 0.0, (3,), (64, 64))JustPIC.CA — Function
CA(backend, dims; eltype = Float64)Allocate an uninitialized CellArray of size dims on backend. Extended for the GPU backends by the package extensions.
JustPIC.cellaxes — Function
cellaxes(A)Return the one-based axes used to iterate over the entries inside each CellArray cell.
This is the preferred helper for loops over particle slots because it works for both scalar and multi-entry cell storage.
JustPIC.cellnum — Function
cellnum(A::CellArray)Return the number of storage slots inside each logical cell of A.
For particle containers this is the number of particle slots reserved per grid cell, including inactive slots.
JustPIC.update_cell_halo! — Function
update_cell_halo!(x::CellArray...)Synchronize the overlapping MPI halo of one or more CellArrays in place.
This is the CellArray companion to ImplicitGlobalGrid.update_halo! and is typically used after particle coordinates or per-particle fields have changed on each rank.
Arguments
x: one or moreCellArrays with the same logical grid layout.
Notes
- Every provided
CellArrayis updated; this is convenient forparticles.coords,particles.index, and particle field arrays returned byinit_cell_arrays. - For MPI particle advection, halo exchange is usually required before
move_particles!so that particles that crossed a rank boundary are visible to the neighboring rank. - With periodic boundary conditions,
update_cell_halo!exchanges the overlap across the periodic domain boundaries as configured ininit_global_grid. - If particles are reinjected with
inject_particles!, refresh the halos again before reconstructing grid fields withparticle2grid!.
Example
advection!(particles, RungeKutta2(), V, dt)
update_cell_halo!(particles.coords...)
update_cell_halo!(particle_args...)
update_cell_halo!(particles.index)
move_particles!(particles, particle_args)
inject_particles!(particles, particle_args)
particle2grid!(T, pT, particles)