Physics in Parallel
Physics in Parallel is a Rust crate for building physics-oriented numerical simulations from reusable layers. The design goal is to let domain users work with familiar concepts such as scalars, tensors, spaces, particles, boundaries, laws, and interactions while hiding backend memory layout and parallel traversal details.
The crate is organized from lower-level infrastructure to higher-level model code:
math -> space -> engines -> models
mathdefines scalar algebra, rank-N tensors, matrices, vector batches, random fillers, and math IO.spacedefines continuous-space utilities and discrete square-lattice spaces.enginesdefines model-agnostic runtime storage, reducers, interaction topology, and neighbor-list infrastructure.modelsdefines validated physical laws and canonical massive-particle model pieces.
Common import:
use *;
Narrower imports are also available:
use *;
use *;
use *;
use *;
Examples
The runnable examples are documented in EXAMPLES.md. That guide introduces each example, explains what it does, shows the command-line arguments, and describes how to interpret benchmark or demonstration output.
Serialization and Persistence Interop
PiP's scientific containers implement Serde directly. They can therefore be
encoded with serde_json, embedded in an application's own serializable
records, or passed to any independent storage or workflow library that accepts
Serialize. Typed JSON reconstruction uses the corresponding Deserialize
implementation:
use ;
let tensor = from_vec;
let json = to_string?;
let restored: = from_str?;
assert_eq!;
# Ok::
Direct Serde calls are the canonical path. They serialize dense storage from
borrowed slices and stream heterogeneous attribute columns without cloning the
scientific payload first. Encoding must still allocate or write the resulting
JSON bytes. Convenience methods that return a String, serde_json::Value, or
owned payload representation necessarily allocate and are intended for
inspection and small standalone operations.
The wire format is explicit and self-describing:
- every current document carries
version: 1; - homogeneous numeric payloads carry a stable
scalaridentifier; - dense containers use row-major
shapeanddata; - sparse tensors and matrices use sorted flat
indicesand matchingvalues; AttrsCoreandPhysObjpreserve heterogeneous column types and stable attribute IDs; and- malformed shapes, scalar mismatches, non-canonical sparse entries, and non-finite JSON numbers are rejected during reconstruction.
PiP deliberately does not own chunking, queues, checkpoint cadence, directory
layouts, or run metadata. Those policies belong to the consuming application
or workflow layer. The dependency direction is one-way: such systems may
depend on PiP's Serde types, while PiP has no dependency on
scientific-workflow or any other workflow framework.
Design Rules
Core consistency rules used across the crate:
- Lower modules should be reused by higher modules. For example, particle boundaries call
space::continuousboundary logic, particle randomization callsspace::continuous::sampling, and particle interactions useengines::soa::Interaction. - Add a public type or trait only when no existing abstraction can express the behavior. Extend the closest existing concept before creating a parallel API.
- Optional behavior belongs in one configurable interface with documented
defaults. Every end-user-facing stochastic API accepts only
RngConfig; components reject unsupported methods instead of introducing parallel RNG APIs. - Public APIs should expose physical or mathematical concepts, not backend layout. Internal storage helpers are kept behind
pub(crate)where possible. - Type-preserving math operations keep the same scalar type and backend when the operation is mathematically backend-preserving.
- Explicit conversion APIs have explicit names such as
try_cast_to,cast_to,to_dense,to_sparse, andto_ndarray. - Boolean particle masks use compact numeric storage internally, while users normally call bool-facing helpers such as
set_alive,is_alive,set_rigid, andis_rigid.
Supported public API
This is PiP's exhaustive supported API allowlist. Users may rely on the items
below, their public enum variants, and their documented public methods. The
crate-wide physics_in_parallel::prelude::* exports the union of the four
domain preludes plus the RNG types. Compiler-visible implementation paths not
listed here are not compatibility promises.
- Randomness:
IndexedRng,RngConfig,RngConfigError, andRngMethod. - Math scalars and conversion:
Complex,Scalar,ScalarCastError,ScalarSerde, andNdarrayConvert. - Tensors and random filling:
Backend,DenseBackend,SparseBackend,Tensor,TensorError,TensorResult,TensorTrait,RandType,TensorRandError,TensorRandFiller,TensorRandElement,NUM_RNGS, and thedense,dense_rand,sparse, andtensor_traitmodules. Their low-level surface also includesdense::Tensor,sparse::Tensor, and theerrorshelpersvalidate_shape,checked_num_elements,ensure_same_shape,ensure_index_rank, andensure_rank. - Tensor operation functions:
abs,conj,cross,dot,elem_div,elem_mul,hermitian_dot,hermitian_transpose,map,matmul,norm,norm_sqr,norm_sqr_real,scalar_mul,size,sqrt,transpose,wedge,zip_with, andassert_same_shapeinmath::tensor::rank_n::ops. - Rank-two math:
Matrix,DenseMatrix,SparseMatrix,MatrixBackend,MatrixError,DiagonalMatrix,SymmetricMatrix,AntiSymmetricMatrix,UpperTriangularMatrix,StrictUpperTriangularMatrix,LowerTriangularMatrix,StrictLowerTriangularMatrix,RankNDense,RankNSparse,Diagonal,Symmetric,AntiSymmetric,Triangular,VectorList,DynVectorList,VectorListRand,HaarVectors, andNNVectors. - Math interchange schema:
JSON_SCHEMA_VERSION,FlatPayload,SparsePayload,SparsePayloadParts,ToJsonPayload,FromJsonPayload,TensorStringConvert, and the JSON helperschecked_num_elementsandscalar_kind. - Continuous space:
BoundaryError,ClampBox,ContinuousBoundary,PeriodicBox,ReflectBox,VectorSamplingError,VectorSamplingMethod, andsample_vectors. - General and lattice space:
Space,BoundaryCondition,Kernel,KernelError,KernelType,NearestNeighborKernel,PairGenerationError,PowerLawKernel,RandPairGenerator,SourceMode,SquareLattice,SquareLatticeConfig,SquareLatticeConfigError,SquareLatticeInitMethod,UniformKernel,create_kernel,try_create_kernel, andsave_square_lattice. - Generic engines:
MeanReducer,Reducer,AttrId,AttrsCore,AttrsError,AttrsMeta,PhysObj,ObjId,InteractionId,Interaction,InteractionError,InteractionNodes,InteractionOrder,InteractionTopology,NeighborList, andNeighborListError. - Physical laws:
PowerLawDecay,PowerLawError,PowerLawRange,Spring,SpringCutoff, andSpringLawError. - Particle attributes:
ParticleSelection,ALIVE_FALSE,ALIVE_TRUE,RIGID_FALSE,RIGID_TRUE,ATTR_A,ATTR_ALIVE,ATTR_M,ATTR_M_INV,ATTR_R,ATTR_RIGID,ATTR_V,alive_value,is_alive,is_alive_value,is_rigid,is_rigid_value,rigid_value,set_alive, andset_rigid. - Particle construction and dynamics:
MassiveParticlesError,VelocitySamplingMethod,create_template,randomize_r,randomize_v,ParticleBoundary,ParticleBoundaryError,ExplicitEuler,Integrator,IntegratorError,SemiImplicitEuler,LangevinThermostat,Thermostat, andThermostatError. - Particle observation and interactions:
KineticEnergyObserver,ObserveError,Observer,TemperatureObserver,ParticleNeighborList,ParticleNeighborListError,PowerLawNetwork,PowerLawNetworkError,SpringNetwork, andSpringNetworkError.
The Math, Space, Engines, and Models sections below exhaustively catalogue the supported constructors and operations for these items. Generated crate documentation is the exact signature reference.
Unified RNG Configuration
RngConfig is the only randomness input used by PiP's public stochastic APIs.
Its seed, method, and parallel stream count are independently optional. Missing
values select the receiving component's documented defaults; a missing seed is
resolved from host entropy.
use NonZeroUsize;
use *;
let rng = new;
Stateful tensor sampling supports SmallRng, PCG, and ChaCha methods plus an
optional parallel stream count. Indexed lattice algorithms use
IndexedSplitMix64, are independent of worker scheduling, and do not accept a
stream count. RngConfig::default() lets each component select all defaults.
Construction resolves all missing values. Long-lived stochastic objects expose
the resulting rng_config(); one-shot sampling functions return it. Use
RngMethod::name(), version(), and seed_encoding() together with
RngConfig::encode_seed() when writing workflow provenance.
Math
Purpose:
math is the numeric foundation. It provides scalar traits, generic rank-N tensors, rank-2 matrix wrappers, vector-list batches, random fillers, and IO conversion traits.
Scalar
Purpose:
Scalar gives integers, real floats, and complex values one common number-like interface. It separates operations that preserve the scalar type from projection/construction/casting operations that intentionally cross type boundaries.
Core API:
zero
one
from_real
real
imag
conj
abs
sqrt
exp
ln
sin
cos
pow
norm_sqr
norm_sqr_real
Core types:
ScalarScalarSerdeScalarCastErrorComplex
Rank-N Tensor
Purpose:
Tensor<T, Backend> is the general tensor facade. It lets users address tensors by shape and coordinate while dense/sparse storage and parallel element traversal stay behind the API.
Core creation API:
empty
zeros
from_vec
from_fn
empty
from_triplets
Core access API:
tensor.shape
tensor.rank
tensor.size
tensor.get
tensor.get_mut
tensor.set
tensor.fill
tensor.print
Type-preserving math API:
&tensor + &rhs
&tensor - &rhs
tensor.elem_mul
tensor.elem_div
tensor.scalar_mul
tensor.conj
tensor.abs
tensor.sqrt
tensor.transpose
tensor.hermitian_transpose
Explicit conversion and linear algebra API:
tensor.
tensor.
tensor.to_dense
tensor.to_sparse
tensor.dot
tensor.hermitian_dot
tensor.matmul
tensor.cross
tensor.wedge
tensor.norm_sqr
tensor.norm_sqr_real
Core types:
Tensor<T, DenseBackend>Tensor<T, SparseBackend>TensorTraitTensorErrorTensorResult
Tensor Random Fillers
Purpose:
TensorRandFiller fills dense tensor storage in parallel. It is the shared random infrastructure used directly by tensors and indirectly by vector-list random generators and continuous-space sampling.
Core API:
new
try_new?
filler.refresh
filler.try_refresh
filler.try_fill_slice
filler.rng_config
Use RngConfig::default() for component defaults. The resolved seed, method,
and stream count remain available through rng_config() for provenance.
Core types:
TensorRandFillerRandTypeRngConfigRngMethodTensorRandError
Matrix
Purpose:
Matrix<T, Backend> is a rank-2 wrapper over rank-N tensor infrastructure. Dense and sparse matrices use rank-N tensor backends; structured matrices such as diagonal, symmetric, antisymmetric, and triangular matrices store only the independent entries and infer the rest.
Core creation API:
empty
from_vec
try_from_vec?
empty
from_triplets
empty
empty
empty
empty
empty
empty
empty
Core API:
matrix.shape
matrix.rows
matrix.cols
matrix.size
matrix.get
matrix.set
matrix.fill
matrix.print
matrix.add
matrix.sub
matrix.elem_mul
matrix.elem_div
matrix.scalar_mul
matrix.transpose
matrix.hermitian_transpose
matrix.trace
matrix.matmul
matrix.mul_vector_into?
matrix.
matrix.
matrix.to_dense
matrix.to_sparse
matrix.to_dense_matrix
Core types:
DenseMatrix<T>SparseMatrix<T>DiagonalMatrix<T>SymmetricMatrix<T>AntiSymmetricMatrix<T>UpperTriangularMatrix<T>LowerTriangularMatrix<T>StrictUpperTriangularMatrix<T>StrictLowerTriangularMatrix<T>MatrixError
VectorList
Purpose:
VectorList<T> stores many fixed-length vectors as dense rank-N storage with logical shape [num_vectors, dim]. It is used when the natural unit of manipulation is a vector row, not an individual scalar.
Core API:
empty
zeros
from_vec
from_fn
vectors.dim
vectors.num_vectors
vectors.shape
vectors.get
vectors.set
vectors.get_vec
vectors.get_vec_mut
vectors.get_vec_owned
vectors.set_vec
vectors.axis
vectors.fill
vectors.print
vectors.par_for_each_vec
vectors.par_for_each_vec_mut
vectors.scale_vectors_by_list
vectors.normalize
vectors.norms_real
vectors.
vectors.
Random vector batches:
new?
haar.refresh
haar.rng_config
new?
nn.refresh
nn.rng_config
Core types:
VectorList<T>VectorListRandHaarVectorsNNVectors
Math IO
Purpose:
math::io handles external-format interop for math containers. Every current
JSON payload carries version: 1. Dense containers borrow their row-major data
directly during Serde encoding:
Sparse tensors and matrices encode only explicit nonzeros through strictly increasing row-major flat indices:
Sparse encoding is proportional to nnz, never materializes implicit zeros,
and is deterministic regardless of hash-map order. Decoding validates version,
shape, lengths, bounds, ordering, duplicates, and explicit zeros before direct
sparse construction. PiP rejects non-finite numeric values because ordinary
JSON numbers cannot represent NaN or infinity faithfully. Finite f32 and
f64 values use exact round-trip parsing.
Prefer serde_json::to_writer when writing to a file or buffered stream, and
serde_json::to_string only when an owned JSON string is actually needed.
ToJsonPayload and FromJsonPayload expose owned schema representations for
specialized manipulation; they are not required for ordinary serialization.
Core API:
to_string_pretty
value.to_ndarray
from_ndarray
value.to_tensor_string
from_tensor_string
Core types:
NdarrayConvertToJsonPayloadFromJsonPayloadFlatPayload<T>SparsePayload<T>TensorStringConvert
Space
Purpose:
space adds physical coordinate semantics on top of math data structures. It contains continuous-space tools and discrete square-lattice spaces.
Continuous Boundary
Purpose:
Continuous boundaries define how real-valued coordinate vectors are returned to an axis-aligned domain. The pure boundary code is independent of particles and can operate on one vector or a flat list of vectors.
Core API:
new
new
new
boundary.dim
boundary.min
boundary.max
boundary.apply_position
boundary.apply_position_velocity
boundary.apply_positions
boundary.apply_positions_velocities
Core types:
ContinuousBoundaryPeriodicBoxClampBoxReflectBoxBoundaryError
Continuous Sampling
Purpose:
Continuous sampling fills VectorList<f64> values with common coordinate or velocity initialization patterns. Particle state construction delegates to this module for generic continuous-vector randomization.
Core API:
let resolved_rng = sample_vectors?;
Core types:
VectorSamplingMethodVectorSamplingError
Space Trait
Purpose:
Space<T> gives higher-level code a common interface for spatial containers without exposing each container's storage or boundary implementation.
Core API:
space.data
space.dims
space.linear_size
space.get
space.get_mut
space.set
space.set_all
space.save
SquareLattice
Purpose:
SquareLattice<T> represents square, cubic, or hypercubic lattice sites over a tensor-style shape such as [128], [64, 64], or [32, 64, 16].
Core API:
try_new?
new
periodic
reflective
cfg.shape
cfg.boundary
cfg.rank
cfg.num_sites
cfg.tensor_shape
cfg.spacing
cfg.strides
cfg.coordinate
cfg.neighbor
cfg.laplacian?
new?
lattice.config
lattice.initialization_rng_config
lattice.data
lattice.downsample
lattice.rescale
Core types:
SquareLattice<T>SquareLatticeConfigSquareLatticeConfigErrorSquareLatticeInitMethod<T>BoundaryCondition
SquareLatticeConfig is the complete serializable spatial configuration. Its
JSON form is {"shape":[64,64],"boundary":"periodic","spacing":[1.0,1.0]}.
Deserialization and
try_new reject empty shapes, zero-length axes, unknown fields, and site-count
overflow, so downstream crates do not need mirror shape, boundary, spacing,
stride, coordinate, neighbor, or finite-difference layout types. Neumann
provides zero-normal-gradient edge handling. PiP does not assign a universal
vacancy sentinel: zero remains a valid scientific value unless a model says otherwise.
Square-Lattice Kernels And Pair Generation
Purpose:
Kernels define reproducible displacement rules for square-lattice workflows.
RandPairGenerator creates source coordinates, raw displacements, and raw
targets from one resolved RngConfig and scientific sweep. Every value is
indexed by seed, sweep, domain, pair, and component, so generated batches are
identical across Rayon worker counts. Boundary interpretation is intentionally
left to SquareLattice access methods.
Core API:
try_create_kernel
kernel.sample_indexed?
kernel.sample_batch_indexed?
kernel.kind
new?
gen.refresh_at
gen.rng_config
gen.sources
gen.displacements
gen.targets
gen.source
gen.displacement
gen.target
Core types:
KernelKernelTypeNearestNeighborKernelUniformKernelPowerLawKernelRandPairGeneratorRngConfigRngMethodPairGenerationErrorSourceMode
Indexed algorithms resolve to RngMethod::IndexedSplitMix64. Record the
resolved configuration plus the method's name, version, and seed encoding. PiP
owns the random mapping; a workflow or application remains responsible for
persisting these facts with the simulation record.
Space IO
Purpose:
space::io contains IO behavior for space types. Current ready support is square-lattice JSON and ndarray conversion.
Core API:
save_square_lattice
to_string_pretty
from_ndarray
lattice.to_ndarray
lattice.serialize
Engines
Purpose:
engines provides model-agnostic runtime infrastructure. The ready backend is structure-of-arrays storage, where each attribute is stored as a typed vector-list column.
Reducers
Purpose:
Reducers combine batches of observed values without knowing which model produced the values.
Core API:
MeanReducer.reduce
Core types:
Reducer<T>MeanReducer
PhysObj And Attribute Storage
Purpose:
PhysObj stores many simulation objects as named typed attribute columns. Each column is a VectorList<T> with shape [n_objects, dim]. Attribute labels are the normal user path; generated attribute IDs are available for repeated expert lookups.
AttrsCore and PhysObj implement direct streaming Serde serialization and
typed deserialization. The versioned representation stores each column's stable
attribute ID and scalar kind, so mixed built-in PiP scalar columns reconstruct
without an intermediate serde_json::Value tree. Consequently, PhysObj can
be used as a typed payload by any independent application or storage framework
that accepts Serde values.
Core API:
empty
new
empty
core.
core.insert
core.remove
core.rename
core.contains
core.labels
core.n_objects
core.id_of
core.label_of
core.
core.
core.
core.
core.
core.
core.
core.dim_of
core.type_name_of
empty
new
obj.serialize
obj.save_to_json
Core types:
AttrsMetaAttrsCoreAttrsErrorAttrIdPhysObj
Interaction Storage
Purpose:
InteractionTopology maps participating object IDs to stable interaction IDs. Interaction<T> combines topology with payload storage, so topology edits and payload edits stay synchronized.
Core API:
new
with_order
topology.set_order
topology.set_n_objects
topology.prune_n_objects
topology.add
topology.remove
topology.id_of
topology.nodes_of
topology.add_pair
topology.remove_pair
new
with_topology
interaction.set
interaction.get
interaction.get_mut
interaction.remove
interaction.set_pair
interaction.get_pair
interaction.remove_pair
interaction.par_for_each
interaction.par_for_each_payload_mut
Core types:
InteractionTopologyInteraction<T>InteractionNodesInteractionOrderInteractionErrorObjIdInteractionId
NeighborList
Purpose:
NeighborList is a cell-linked candidate-pair generator. It emits unique unordered candidate pairs from same/adjacent cells but does not apply a final physical cutoff distance.
Core API:
new
neighbor_list.rebuild
neighbor_list.for_each_pair_candidate
neighbor_list.collect_pair_candidates
neighbor_list.clear
neighbor_list.dim
neighbor_list.num_objects
neighbor_list.cells_per_axis
Core types:
NeighborListNeighborListError
Models
Purpose:
models contains concrete physical model pieces built on the lower layers. Current ready modules cover validated law payloads and canonical massive-particle simulation components.
Laws
Purpose:
models::laws stores small validated parameter payloads. These payloads do not know how objects are stored; model adapters decide how to apply them to particle state, lattice sites, or future model objects.
Core API:
new
spring.validate
new
power_law.validate
Core types:
SpringSpringCutoffSpringLawErrorPowerLawDecayPowerLawRangePowerLawError
Particle Attributes And State Construction
Purpose:
Particle modules use a canonical PhysObj layout. Vector attributes r, v, and a have shape [num_particles, dim]. Scalar attributes m, m_inv, alive, and rigid have shape [num_particles, 1].
Core attribute API:
set_alive
is_alive
set_rigid
is_rigid
alive_value
rigid_value
AliveOnly
All
Core construction/randomization API:
create_template
let resolved_rng = randomize_r?;
let resolved_rng = randomize_v?;
Core types:
ParticleSelectionMassiveParticlesErrorVelocitySamplingMethod
Particle Boundary
Purpose:
Particle boundary adapters apply space::continuous boundary objects to canonical particle state. The continuous boundary owns the geometric rule; the particle adapter owns traversal over ATTR_R, velocity updates in ATTR_V, and alive/rigid mask handling.
Core API:
new?.apply_to_particles
new?.apply_to_particles
new?.apply_to_particles
Core types:
ParticleBoundaryParticleBoundaryError
Particle Integrators
Purpose:
Integrators advance canonical particle r and v from a. They skip dead particles and rigid particles; they do not clear acceleration after stepping.
Core API:
ExplicitEuler.apply
SemiImplicitEuler.apply
Core types:
IntegratorExplicitEulerSemiImplicitEulerIntegratorError
Particle Thermostat
Purpose:
LangevinThermostat applies an exact Ornstein-Uhlenbeck velocity update to canonical particle velocities. It honors ParticleSelection for alive/dead behavior and always skips rigid particles.
Core API:
new?
thermostat.apply
thermostat.tau_target
thermostat.gamma
thermostat.rng_config
thermostat.step_counter
thermostat.selection
Core types:
ThermostatLangevinThermostatThermostatError
Particle Observers
Purpose:
Particle observers compute read-only scalar summaries from canonical particle state. AliveOnly skips dead particles, while All intentionally includes every allocated slot for diagnostics.
Core API:
default.observe
new.observe
default.observe
new.observe
Core types:
ObserverKineticEnergyObserverTemperatureObserverObserveError
Particle Interactions
Purpose:
Particle interaction modules wrap engine-level interaction storage with particle-specific validation and application rules.
Core API:
from_bounds
from_box
neighbor_list.rebuild
neighbor_list.collect_pairs
empty
springs.add_spring
springs.add_spring_payload
springs.get_spring
springs.remove_spring
springs.apply_hooke_acceleration
empty
network.add_power_law
network.add_payload
network.get_power_law
network.remove_power_law
Core types:
ParticleNeighborListParticleNeighborListErrorSpringNetworkSpringNetworkErrorPowerLawNetworkPowerLawNetworkError
Examples
Runnable examples and benchmark usage are documented in EXAMPLES.md. Common entry points:
Verification
Standard checks: