Grid
The MSM grid hierarchy is built from UniformGrid instances — one per level. Each grid is described by its per-axis spacing, extent, origin, and per-axis boundary conditions.
MultilevelSummation.UniformGrid — Type
UniformGrid{D,T,Per,Sz}(spacing, origin)
UniformGrid(spacing, size, origin, periodic::NTuple{D,Bool})A uniform D-dimensional grid with per-axis spacing h_α, integer extent n_α, an origin offset in particle coordinates, and per-axis boundary conditions.
Static type parameters (compile-time constants):
D::Int— spatial dimensionT<:AbstractFloat— coordinate floating-point typePer::NTuple{D,Bool}—true= periodic axis,false= openSz::NTuple{D,Int}— per-axis grid extentn_α
Lifting both Per and Sz into the type lets the inner loop of every grid operator dispatch on them and produce fully unrolled, branch-free, constant-bound code (no if g.periodic[α] and no runtime mod(_, n) with n from a struct field).
Grid point m ∈ 0:n_α-1 along axis α sits at origin_α + m·spacing_α in particle coordinates. Periodic axes wrap indices mod n_α; open axes drop contributions whose indices fall outside 0:n_α-1.
getproperty shims keep g.periodic and g.size working as before, so no call site has to change.
MultilevelSummation.grid_zeros — Function
grid_zeros(T, g)Allocate a Array{T, D} of zeros sized to match g.
MultilevelSummation.npoints — Function
Total number of grid points.
MultilevelSummation.wrap_index — Function
wrap_index(idx, g) -> (idx_wrapped::NTuple{D,Int}, in_bounds::Bool)Map a raw integer index idx (0-based, may be negative or out of range) to a 1-based index suitable for Array access. Returns in_bounds=false if the index falls outside an open axis. Periodic axes always wrap.
Implemented as a @generated function dispatching on (Per, Sz): each periodicity-and-size combination compiles to fully unrolled, branch-free per-axis code with the grid extents inlined as integer literals. Power-of-two periodic axes skip the mod entirely and use a bitmask (((idx + n) & (n - 1)) + 1), valid because the convolution callers bound idx ≥ -smax and n ≥ smax, so idx + n ≥ 0. For general periodic sizes we fall back to mod(idx, n) + 1, which the compiler lowers to a multiply-high sequence (n is still a compile-time literal).
MultilevelSummation.particle_to_grid — Function
particle_to_grid(r, g) -> SVector{D,T}Convert particle-coordinate position r to dimensionless grid coordinates ξ_α = (r_α − origin_α) / spacing_α. The integer part is the nearest grid point below; the fractional part determines basis weights.
MultilevelSummation.coarser_grid — Function
coarser_grid(g, factor=2) -> UniformGridBuild the "next-coarser" grid in the MSM hierarchy: same origin, same periodicity, each axis spacing scaled by factor, each axis extent divided by factor. For periodic axes the original extent must be divisible by factor. Open axes round down.
MultilevelSummation.build_grid_hierarchy — Function
build_grid_hierarchy(cell, periodic, positions, c) -> Vector{UniformGrid{D,T,Per}}Build the finest-to-coarsest MSM grid hierarchy. For periodic axes, the level-1 extent is L_α / h (must be an integer multiple of 2^{L-1}). For open axes, the level-1 extent is set from the particle bounding box plus a basis-support pad, rounded up to a multiple of 2^{L-1}.
Mixed BC: any combination is allowed; each axis is treated independently.