physics_in_parallel 3.7.0

High-performance infrastructure for numerical simulations in physics
Documentation
physics_in_parallel-3.7.0 has been yanked.

Physics in Parallel

Alpha and breaking API notice: PiP is under active design. Version 3.7.0 is the current API contract; every version before 3.7.0 is considered broken and unsupported. Versions still increase normally, but any alpha release may contain breaking changes. Pin the exact version used by a scientific project.

Physics in Parallel (PiP) provides reusable, multicore numerical building blocks for simulations: tensors, matrices, vector batches, spatial sampling, lattices, pair generation, and ready-to-use particle models.

API contract

PiP deliberately has three non-overlapping preludes:

use physics_in_parallel::prelude::basic::*;    // foundational user API
use physics_in_parallel::prelude::models::*;   // ready physical models
use physics_in_parallel::prelude::advanced::*; // backend and extension API

Import only the layers a program uses. No prelude imports another. The old crate-wide and domain-specific preludes no longer exist.

The basic and models symbols listed below are the complete normal user API. The opt-in backend API is catalogued in advanced_api.md. Anything not re-exported by one of these preludes is implementation detail and cannot be imported by downstream crates.

Some foundational container types have inherent methods whose signatures use advanced-only backend types. Those methods are advanced API despite living on the same Rust type; the signature makes the boundary explicit.

PiP itself follows the same dependency rule: compose the basic API first, use the advanced API only when it supplies a necessary capability, and access an internal implementation only when a public route would materially harm performance or cannot express the operation.

Basic API

physics_in_parallel::prelude::basic exports exactly these symbols:

  • Randomness: RngConfig, RngMethod, RngConfigError, IndexedRng.
  • Explicit execution convenience: ComputePool, ComputePoolError, with_threads.
  • Scalars: Scalar, Complex, ScalarCastError.
  • Dense mathematics: Tensor, TensorError, Matrix, DenseMatrix, MatrixError, VectorList.
  • Random filling: RandType, TensorRandFiller, TensorRandError, DEFAULT_RANDOM_MAX_THREADS.
  • Continuous space: BoundaryError, ContinuousBoundary, ClampBox, PeriodicBox, ReflectBox, VectorSamplingMethod, VectorSamplingError, sample_vectors.
  • Square lattices: BoundaryCondition, SquareLatticeConfig, SquareLatticeConfigError, SquareLattice, SquareLatticeInitMethod.
  • Pair generation: PairGeneratorConfig, PairGenerator, PairingMethod, SourceMode, KernelType, KernelError, PairGenerationError.

The core container conventions are consistent:

  • Tensor uses owned contiguous row-major storage and a dynamic shape. as_slice and as_mut_slice provide zero-copy dense access; try_from_vec validates incoming ownership transfers; copy_from, zip_from, and for_each_chunk_mut support allocation-free simulation kernels. Coordinate and flat accessors both wrap periodically, including negative and oversized indices.
  • Matrix uses signed (row, column) and flat access with periodic wrapping. Arithmetic keeps the backend when that is mathematically valid. Scalar multiplication is named scalar_mul; mul_vectors_into applies one matrix to a caller-owned contiguous vector batch.
  • VectorList stores num_vectors rows of dimension dim; use vector, vector_mut, vector_owned, set_vector, axis, and set_axis for row/axis access.
  • SquareLattice exposes coordinate and flat forms of get, get_mut, and set, plus shape, rank, num_sites, and fill, without requiring the advanced Space trait. Every accessor applies its configured boundary. SquareLatticeInitMethod::ShuffledValues accepts one complete flat row-major value list and applies a reproducible unbiased permutation before constructing the shaped lattice. The shuffle machinery remains internal to PiP.

Dense Tensor, Matrix, and VectorList provide constructors, shape/access methods, elementwise arithmetic, scalar_mul, casts, and Serde. Tensor additionally provides mapping and tensor algebra such as transpose, dot, norm, cross, wedge, and matrix multiplication. Matrix additionally provides transpose, Hermitian transpose, trace, matrix multiplication, and abs. VectorList additionally provides parallel row traversal, per-row scaling, norms, normalization, and polar decomposition. Rustdoc is the exact signature reference.

Randomness

RngConfig is the only randomness configuration accepted by normal stochastic APIs. A missing seed uses host entropy; a missing method uses the component's documented default. Long-lived generators expose their resolved configuration. Indexed filling and pair generation are deterministic independently of Rayon scheduling and worker count. TensorRandFiller::max_threads is an instance-local runtime limit: it bounds how many workers one fill may occupy without changing deterministic RNG lanes or generated values.

Internal parallelism

PiP uses the current Rayon pool and does not normally create or own another thread pool. Kernel work planning adapts to the workload and the number of workers available in that pool. There is no process-wide PiP thread or partition setting.

Callers that need a reusable fixed-size execution environment may opt in:

use physics_in_parallel::prelude::basic::*;

let pool = ComputePool::new(4)?;
let _result = pool.install(|| expensive_calculation());
# Ok::<(), Box<dyn std::error::Error>>(())
# fn expensive_calculation() {}

with_threads is the one-operation shortcut; construct ComputePool directly when executing repeatedly so worker creation is amortized. Both are explicit conveniences. Existing Rayon pools, environment configuration, and higher-level workflow concurrency remain owned by the caller.

Elementwise and batched matrix operations select serial execution below a conservative work threshold and bounded Rayon execution above it. This avoids paying task-scheduling costs for small tensors while retaining multicore throughput for spatial fields. Tensor::sum_serial is the stable row-major reduction for calculations whose floating-point order is scientifically observable; Tensor::sum is the parallel throughput-oriented reduction.

Workflow and serialization

PiP containers implement Serde directly. A Tensor<f64> or Option<Tensor<f64>> can therefore be inserted into a heterogeneous workflow state and passed directly to a generic writer without an adapter or conversion allocation. Dense tensors serialize as a versioned flat payload containing kind, version, scalar, shape, and row-major data; deserialization validates all five fields before constructing storage.

PiP is the canonical Rust numerical representation and has no Rust ndarray dependency. Python readers may reconstruct the versioned payload as NumPy arrays at the external analysis boundary.

Pair generation

Construct a PairGeneratorConfig, then a PairGenerator from the lattice shape. Every pairing method produces the same meanings and shapes: one source site and one target site per pair. PairingMethod::IndependentUniform samples source and target independently over the full shape and allows self-pairs. Kernel-based methods instead sample a displacement. Their raw target coordinates can lie outside the declared shape and must be passed to SquareLattice access methods, whose indexing rules resolve the boundary. Users do not add boundary logic to the generator.

Models API

physics_in_parallel::prelude::models exports exactly these symbols:

  • State: PhysObj, AttrsError, create_template, MassiveParticlesError, VelocitySamplingMethod, randomize_r, randomize_v.
  • Canonical attributes: ATTR_R, ATTR_V, ATTR_A, ATTR_M, ATTR_M_INV, ATTR_ALIVE, ATTR_RIGID, ParticleSelection, set_alive, is_alive, set_rigid, is_rigid.
  • Boundaries: ParticleBoundary, ParticleBoundaryError.
  • Integration: Integrator, IntegratorError, ExplicitEuler, SemiImplicitEuler.
  • Thermostats: Thermostat, ThermostatError, LangevinThermostat.
  • Observation: Observer, ObserveError, KineticEnergyObserver, TemperatureObserver.
  • Laws: Spring, SpringCutoff, SpringLawError, PowerLawDecay, PowerLawRange, PowerLawError.
  • Interactions: SpringNetwork, SpringNetworkError, PowerLawNetwork, PowerLawNetworkError, ParticleNeighborList, ParticleNeighborListError.

PhysObj is the canonical system state, so users do not need a wrapper struct. Its normal API exposes metadata, object count, typed attribute access, typed attribute-vector access, mutation, serialization, and JSON persistence. Boolean state is accessed through the bool-facing helpers; its compact numeric encoding is internal.

Serialization

Scientific containers implement Serde directly. Dense data is row-major; sparse data uses canonical sorted flat indices; heterogeneous particle attributes preserve stable IDs and scalar types. Invalid shapes, scalar mismatches, malformed sparse entries, unsupported schema versions, and non-finite JSON numbers are rejected during reconstruction.

PiP owns numerical data formats, not workflow policy. Checkpoint cadence, directories, queues, run metadata, and resumption belong to the consuming workflow crate.

Examples

See EXAMPLES.md. Run an example with:

cargo run --release --example <example_name> -- <arguments>

Development contract

  • Higher layers reuse lower-layer APIs instead of duplicating algorithms.
  • Inferable information is not requested again at downstream call sites.
  • Backend and layout details stay out of normal user code.
  • New public functionality is composed from existing APIs whenever possible.
  • Advanced additions require a concrete use case not covered by the basic or models API.