physics_in_parallel 3.6.0

High-performance infrastructure for numerical simulations in physics
Documentation
# Physics in Parallel

> **Alpha and breaking API notice:** PiP is under active design. Version 3.6.0
> is the current API contract; every version before 3.6.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:

```rust
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](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`.
- Internal execution policy: `set_parallel_partitions`, `parallel_partitions`,
  `DEFAULT_PARALLEL_PARTITIONS`, `ParallelismError`.
- Scalars: `Scalar`, `Complex`, `ScalarCastError`.
- Dense mathematics: `Tensor`, `TensorError`, `Matrix`, `DenseMatrix`,
  `MatrixError`, `VectorList`.
- Random filling: `RandType`, `TensorRandFiller`, `TensorRandError`.
- 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 a row-major shape and signed coordinates. 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 PiP's execution partition setting.

### Internal parallelism

PiP uses the current Rayon pool; it never creates or owns another thread pool.
Its low-level operations create at most eight partitions by default so a task
can coexist with higher-level parallel tasks. A program may set a different
process-wide limit once, before the first PiP parallel operation:

```rust
use physics_in_parallel::prelude::basic::*;

set_parallel_partitions(4).expect("configure PiP before its first parallel operation");
```

The limit controls PiP work partitioning, not Rayon worker count. Higher-level
workflow concurrency remains the responsibility of the caller. The value is
frozen after first use; later attempts return `ParallelismError`.

### 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](EXAMPLES.md). Run an example with:

```bash
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.