From 88432a7abd65620614bc0090ff66cffa05990086 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 01:39:47 +0000 Subject: [PATCH 1/5] ci: run coverage as its own job; README: rewrite for the whole library CI. The `test` job ran `cargo test` and then `cargo llvm-cov` as consecutive steps, so its wall time was their sum -- fifteen to nineteen minutes against about two hundred seconds locally. llvm-cov is not a reporting step that reads what `cargo test` produced; there is nothing to read. It compiles the crate with -C instrument-coverage into its own target directory and runs the whole suite again, because running the tests is how it collects coverage. The two therefore do the same work twice whatever we do, and the only question is series or parallel. They are now separate jobs, so the wall time is the slower of them and the plain test result comes back without waiting behind the instrumented rebuild. Two other things turned up in that file. The cache was doing nothing. It cached ~/.cargo/registry and ~/.cargo/git, which are empty for a crate whose Cargo.lock holds exactly one package -- itself -- and keyed on hashFiles('**/Cargo.lock'), which therefore never changes, so after the first save it restored a stale tree and never wrote again. Every run logged "Cache hit occurred on the primary key, not saving cache". Replaced with Swatinem/rust-cache@v2, already used by verify.yml, which keys on the compiler version and the job and saves each run. The two jobs get separate keys, which is the point: instrumented artifacts fingerprint differently and sharing one cache between them would thrash it. The separate `cargo build` step is gone -- `cargo test` builds the same artifacts -- and verify.yml's comment saying five tests carry cfg_attr(miri, ignore) said five when there are six. README. It claimed 1,659 tests at 99.98% coverage across 67 files, and described none of transforms, dsp, resonance, audio, cfd, manifold, exact, discrete, graph, codes, stochastic, optimization, fem, learn, finance or units. Rewritten against measured numbers: 6,365 public functions and methods, 429 public types, 71 top-level modules across 296 files, 265,190 lines, 4,193 unit tests, 577 property tests, 97.89% line and 99.33% function coverage, 20 Kani harnesses. Every domain now carries the governing equations the code actually implements -- Galerkin orthogonality and the Pythagoras identity for FEM, the leapfrog energy and Courant condition for FDTD, Buckingham's theorem as a rank computation, the Baillie-PSW composition, LP duality and the KKT conditions, Black-Scholes with put-call parity, the Hodgkin-Huxley currents, Marchenko-Pastur, and so on. A "how it's tested" section explains why the coverage figure is not the interesting number, with the three rare branches that mutation found sitting behind full coverage. A script checked all 158 module references in the prose against the source tree; every one resolves, and all 71 top-level modules are covered. The equations were proofread against their definitions, which caught a mismatched contraction index in the Riemann tensor and an ambiguous Marchenko-Pastur support. The Quick start snippet is now examples/readme_quickstart.rs, run by CI, so the front page of the repository cannot drift out of date. Its assertions are the check. 4,193 lib tests, 577 property tests, clippy clean under --all-targets -D warnings, nightly-2025-11-21 clean. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01VUkMGnfUbaopkUJLWeYCYi --- .github/workflows/ci.yml | 64 ++- .github/workflows/verify.yml | 4 +- README.md | 999 +++++++++++++++++++++++++++++----- examples/readme_quickstart.rs | 33 ++ 4 files changed, 962 insertions(+), 138 deletions(-) create mode 100644 examples/readme_quickstart.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8914bb6..2913ec3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,22 @@ name: CI +# Build, test and coverage. The deeper checks -- property tests, Kani, +# Miri and the strict lint pass -- are in verify.yml. +# +# `test` and `coverage` are separate jobs on purpose. `cargo llvm-cov` is +# not a reporting step that reads what `cargo test` already produced: +# there is nothing to read. It compiles the crate with +# `-C instrument-coverage` into its own target directory, runs the whole +# suite again to emit .profraw files, and merges those into the summary. +# Running the tests IS how it collects coverage. +# +# So the two do the same work twice no matter what, and the only question +# is whether they do it in series or in parallel. As consecutive steps in +# one job the wall time was their sum -- fifteen to nineteen minutes. As +# separate jobs it is the slower of the two, and the plain `cargo test` +# result comes back in a third of that instead of waiting behind the +# instrumented rebuild. + on: push: branches: [main] @@ -11,30 +28,51 @@ env: jobs: test: + name: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@stable - with: - components: llvm-tools-preview - - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Build - run: cargo build --verbose + # Swatinem/rust-cache rather than a hand-rolled actions/cache, which + # is what this job used to do and which cached nothing. The crate has + # no dependencies -- Cargo.lock holds exactly one package, itself -- + # so ~/.cargo/registry and ~/.cargo/git are empty, and a key of + # hashFiles('**/Cargo.lock') never changes, so after the first save + # the cache was restored stale on every run and never written again. + # This one keys on the compiler version and the job as well, and + # saves each run. + - uses: Swatinem/rust-cache@v2 - name: Test run: cargo test --verbose + # The README's Quick start section IS this example, so running it here + # is what stops the front page of the repository from drifting out of + # date. Its assertions are the check; the printed values are for a + # human reading the log. + - name: Check the README example + run: cargo run --example readme_quickstart + + coverage: + name: Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: llvm-tools-preview + + # A separate cache key from the `test` job, which is the point of + # keying on the job: these artifacts are built with + # -C instrument-coverage and cargo fingerprints them separately, so + # sharing one cache between the two would thrash it. + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index a50dd4f..58d4155 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -91,11 +91,11 @@ jobs: # core::, the interval and dual-number arithmetic where index and slice # reasoning is densest, rather than to the whole suite. # - # Five tests in there carry #[cfg_attr(miri, ignore)]. Miri evaluates + # Six tests in there carry #[cfg_attr(miri, ignore)]. Miri evaluates # sin, exp and powi with its own implementations rather than the host's, # and deliberately randomises the result within the slack the language # allows, so a test asserting an exact float value fails under Miri - # whatever the code does. Those five are exactness assertions and are + # whatever the code does. Those six are exactness assertions and are # skipped here; they run everywhere else. # # A libtest filter is a substring match, not a path prefix, so a bare diff --git a/README.md b/README.md index b46597b..7efb61a 100644 --- a/README.md +++ b/README.md @@ -4,152 +4,905 @@

CI - Coverage - Tests + Verify + Coverage + Tests + Kani License: MIT Rust Zero Dependencies

-A comprehensive, zero-dependency Rust library for physics, mathematics, and engineering computation. Every public function validates its inputs, every formula is tested against known values, and the entire codebase sits at 99.98% line coverage across 1,659 tests. - -## What's in it - -### Classical & Continuum Mechanics -- **`classical`** — Newtonian mechanics: projectile motion, collisions, SHM, damping, resonance -- **`solid_mechanics`** — Stress, strain, elastic moduli, beam deflection, Mohr's circle -- **`continuum_mechanics`** — 3D Hooke's law, compliance matrices, plane stress/strain -- **`fluid_instabilities`** — Rayleigh-Taylor, Kelvin-Helmholtz, Jeans instability, Plateau-Rayleigh - -### Thermodynamics & Statistical Mechanics -- **`thermodynamics`** — Ideal gas, Carnot, entropy, heat conduction, radiation, Nusselt/Biot/Grashof -- **`statistical_mechanics`** — Maxwell-Boltzmann, Boltzmann/Einstein/Debye models, diffusion, partition functions - -### Electromagnetism & Electronics -- **`electromagnetism`** — Coulomb, Lorentz, Faraday, Maxwell, RLC circuits, transformers -- **`electronics`** — Semiconductors, diodes, MOSFETs, solar cells, PN junctions -- **`rf`** — Friis, skin depth, antenna gain, impedance, VSWR, Smith chart quantities -- **`photonics`** — Gaussian beams, fiber optics, ray transfer matrices, coherence, Fabry-Perot - -### Waves, Optics & Acoustics -- **`waves`** — Doppler, standing waves, diffraction, Snell, seismic waves, dispersion -- **`optics`** — Lenses, mirrors, thin films, diffraction gratings, Rayleigh resolution -- **`acoustics`** — Sabine/Eyring reverberation, psychoacoustic scales (mel, bark, ERB), room modes, STC -- **`signal_processing`** — Waveform generation, FIR/IIR filters, convolution, windowing, resampling - -### Relativity & Quantum -- **`relativity`** — Lorentz transformations, relativistic energy-momentum, time dilation, Doppler -- **`general_relativity`** — Schwarzschild metric, geodesics, frame dragging, cosmological distances -- **`quantum`** — De Broglie, uncertainty principle, particle-in-a-box, tunneling, Planck radiation -- **`particle_physics`** — Invariant mass, Rutherford scattering, Breit-Wigner, rapidity, Lorentz boosts - -### Nuclear & Radiation -- **`nuclear`** — Decay chains, binding energy, Q-values, dosimetry -- **`neutronics`** — Criticality, diffusion, moderation, burnup, shielding -- **`radiation`** — Blackbody, Wien, Planck, radiative transfer, view factors -- **`plasma`** — Debye length, cyclotron/plasma frequencies, Alfven speed, beta, Larmor radius - -### Astrophysics -- **`astrophysics::nbody`** — N-body gravitational simulation (leapfrog integrator) -- **`astrophysics::octree`** — Barnes-Hut tree for O(N log N) force computation -- **`astrophysics::orbital_elements`** — Keplerian elements from state vectors, orbit propagation -- **`astrophysics::gravitational_waves`** — Strain, luminosity, frequency, chirp mass -- **`astrophysics::tidal`** — Tidal forces, Roche limits, tidal tensors -- **`astrophysics::lagrange`** — L1–L5 Lagrange point computation -- **`astrophysics::habitable_zone`** — Habitable zone boundaries, tidal locking -- **`astrophysics::magnetosphere`** — Dipole fields, magnetopause radius, field line tracing -- **`astrophysics::collisions`** — Impact cratering, orbital debris, collision probabilities - -### Fluids & Propulsion -- **`fluids`** — Bernoulli, Poiseuille, Reynolds, drag, capillarity, compressible flow -- **`propulsion`** — Tsiolkovsky rocket equation, Hohmann transfers, nozzle design, staging -- **`magnetohydrodynamics`** — Alfven waves, Hartmann flow, magnetic reconnection, pinch equilibria - -### Chemistry & Biophysics -- **`chemistry`** — Arrhenius, Nernst, pH, electrochemistry, reaction kinetics -- **`biophysics`** — Nernst/Goldman potentials, Michaelis-Menten, Hill equation, hemodynamics - -### Mathematics & Numerical Methods -- **`math`** — `Vec3` type, physical constants (NIST CODATA), linear algebra primitives -- **`linalg`** — 3x3 matrices, rotations, eigenvalue decomposition, SVD-like operations -- **`quaternion`** — Quaternion algebra, slerp/nlerp, axis-angle, Euler angle conversions -- **`numerical`** — Root finding (bisection, Newton, secant), integration (Simpson, Gauss-Legendre), cubic splines -- **`optimization`** — Golden section, Brent, Nelder-Mead, simulated annealing, linear regression, polynomial fitting -- **`statistics`** — Mean, variance, median, distributions (Gaussian, Poisson, exponential), DFT, power spectrum -- **`monte_carlo`** — MC integration, Metropolis-Hastings, Ising model, Langevin dynamics, random walks -- **`vector_calculus`** — Gradient, divergence, curl, Laplacian, Poisson solver, line/surface/volume integrals -- **`nonlinear`** — Logistic map, Lorenz/Rossler attractors, Lyapunov exponents, bifurcation diagrams -- **`information_theory`** — Shannon entropy, mutual information, KL divergence, channel capacity - -### Geometry, Curves & Fractals -- **`geometry`** — Areas, volumes, perimeters for standard shapes, regular polygons -- **`curves`** — Conic sections, Bezier curves, arc length, curvature -- **`fractals`** — Mandelbrot, Julia, burning ship, Newton fractals, Barnsley fern, box counting -- **`trigonometry`** — Trig identities, hyperbolic functions, angle conversions, haversine - -### Simulation Engines -- **`sim::rigid_body`** — 3D rigid body dynamics with quaternion orientation, Euler equations, collision response -- **`sim::fluid_sim`** — Column fluid, 1D shallow water, 2D incompressible Euler (pressure projection) -- **`sim::heat_sim`** — 2D/3D heat conduction (explicit finite difference), convection-diffusion -- **`sim::wave_sim`** — 1D/2D wave equation solvers, absorbing boundary conditions (Mur ABC) -- **`sim::em_sim`** — 1D/2D FDTD electromagnetic simulation, PEC/Mur boundaries, dielectric media -- **`sim::cloth_sim`** — Verlet integration cloth/rope, spring-damper constraints, collision - -### Reference Data -- **`materials::elements`** — All 118 elements with atomic mass, density, melting/boiling points, thermal/electrical conductivity -- **`materials::common`** — Engineering materials (steel, aluminum, copper, etc.) -- **`materials::fluids`** — 16 common fluids with density, viscosity, surface tension, speed of sound -- **`materials::gases`** — Common gases with molar mass, specific heat ratio, thermal conductivity - -### Utilities -- **`units`** — SI unit conversions (temperature, pressure, energy, length, speed, angle, etc.) -- **`color_science`** — RGB/HSV/HSL/XYZ, wavelength-to-color, blackbody color, CIE color difference -- **`control_systems`** — Transfer functions, step/impulse response, PID tuning, stability margins -- **`atmosphere`** — ISA model, barometric formula, lapse rates, humidity, wind shear - -## Usage - -Add to your `Cargo.toml`: +A zero-dependency Rust library for physics, mathematics and engineering computation. + +The aim is not breadth for its own sake. Every routine here is written so that +something about it can be *checked* — against a closed form, against a +conservation law, against an independent implementation of the same quantity, or +against an exact identity over integers. A test that only asserts a function ran +is not evidence, and the test suite is built around that distinction. Where a +result is approximate the error has a stated bound; where it is exact the +assertion uses `==`. + +--- + +## At a glance + +| | | +|---|---| +| **Public functions and methods** | 6,365 (3,949 free functions, 2,416 methods) | +| **Public types** | 429 structs, enums and traits | +| **Top-level modules** | 71, across 296 source files | +| **Source** | 265,190 lines of Rust | +| **Unit tests** | 4,193 | +| **Property tests** | 577, across 49 files | +| **Line coverage** | 97.89% (174,685 lines, 3,681 uncovered) | +| **Function coverage** | 99.33% (20,200 functions, 136 uncovered) | +| **Formal verification** | 20 Kani harnesses (13 in CI, 7 behind `kani-slow`) | +| **Undefined behaviour** | Miri-clean; the crate contains no `unsafe` | +| **Dependencies** | none — `Cargo.lock` holds exactly one package | +| **Edition** | 2021, `f64` throughout | + +--- + +## Install ```toml [dependencies] -rust_physics_engine = { path = "." } +rust_physics_engine = { git = "https://github.com/Magic-Man-us/RustPhysicsEngine" } ``` +## Quick start + +This snippet is [`examples/readme_quickstart.rs`](examples/readme_quickstart.rs), +compiled and run by CI, so it cannot drift out of date. + ```rust -use rust_physics_engine::classical::{projectile_range, kinetic_energy}; -use rust_physics_engine::math::constants::{G, C, K_B}; -use rust_physics_engine::thermodynamics::ideal_gas_pressure; -use rust_physics_engine::sim::rigid_body::RigidBody; +use rust_physics_engine::classical::projectile_range; +use rust_physics_engine::exact::rational::Rational; +use rust_physics_engine::math::constants::{C, G}; +use rust_physics_engine::units::quantity::{Dim, Quantity}; + +// Ballistics: v₀ = 50 m/s, θ = 45°, g = 9.81 m/s² +let range = projectile_range(50.0, std::f64::consts::FRAC_PI_4, 9.81); +assert!((range - 254.841_997_961).abs() < 1e-9); + +// Constants come from one table. A black hole's Schwarzschild radius: +let solar_mass = 1.989e30; +let r_s = 2.0 * G * solar_mass / (C * C); // about 2.95 km + +// Quantities carry their dimensions, and addition checks them. +let v = Quantity::new(3.0, Dim::new(1, 0, -1, 0, 0, 0, 0)); // m/s +let t = Quantity::new(2.0, Dim::TIME); +let d = v.mul(&t).unwrap(); // 6 m — a length, exactly +assert!(v.add(&t).is_err()); // a velocity is not a time + +// Exact rational arithmetic over arbitrary-precision integers. +let third = Rational::from_i64(1, 3); +let one = third.mul(&Rational::from_i64(3, 1)); +assert_eq!(one, Rational::one()); // not 0.9999999999999999 +``` + +--- + +# What's in it + +Equations below are the ones the code actually implements, not a +representative sample of the field. + +## Numerical foundations + +**`core`** — the primitives everything else is allowed to rely on. + +- **`core::dual`** — forward-mode automatic differentiation. A dual number + `a + bε` with `ε² = 0` carries a value and its derivative through every + operation, so `f(x + ε)` returns `f(x) + f′(x)ε` with no step size and no + truncation error. +- **`core::interval`** — rigorous interval arithmetic with outward rounding. + Every operation returns an interval *guaranteed* to contain the true result. +- **`core::compensated`** — Kahan and Neumaier summation, and error-free + transformations (`two_sum`, `two_product`) that return a sum together with + its exact rounding error. + +``` +dual: (a + bε)(c + dε) = ac + (ad + bc)ε since ε² = 0 +interval: [a,b] · [c,d] = [min(ac,ad,bc,bd), max(ac,ad,bc,bd)] +two_sum: s = fl(a+b), e = (a − (s − b)) + (b − (s − b)), a + b = s + e exactly +``` + +**`math`** — the `Vec3` type and its algebra, and `math::constants`: the +single table of physical constants the rest of the crate refers back to. + +**`linalg`** — dense `Matrix`, LU with partial pivoting, Cholesky, QR by +Householder reflections, SVD by one-sided Jacobi, eigenvalue solvers, +tridiagonal (Thomas) solve, and CSR sparse matrices with conjugate gradient. + +**`numerical`** — quadrature (Simpson, Gauss–Legendre, adaptive, Romberg), +root finding (bisection, Newton, secant, Brent, polynomial roots), ODE +integrators (explicit RK, Dormand–Prince 5(4) adaptive, backward Euler and +BDF2 for stiff problems, symplectic for `x″ = a(x)`), interpolation and cubic +splines, and two-point boundary value problems. + +**`special`** — error function family, gamma family, beta and regularized +incomplete beta, Bessel functions of integer order, elliptic integrals, +exponential integrals, Legendre polynomials, associated Legendre functions and +real spherical harmonics. + +## Exact and symbolic computation + +**`exact`** — arithmetic without rounding. + +- **`bigint`** — arbitrary-precision signed integers, with Knuth's algorithm D + for division (including the rare add-back correction). +- **`rational`** — exact rationals over `BigInt`, always reduced with a + positive denominator. `from_f64_exact` gives the dyadic rational an `f64` + genuinely is. +- **`bigfloat`** — arbitrary-precision binary floating point. +- **`polynomial`** — dense univariate polynomials over `f64` and over + `Rational`. +- **`contfrac`** — continued fraction expansions, convergents, and the + periodic expansion of a quadratic irrational. +- **`symbolic`** — a small computer algebra system over expression trees: + a precedence-climbing parser, `Display` and LaTeX output, exact + differentiation, simplification and expansion, Taylor series, a stack-machine + compiler, table-driven integration, limits, gradients and Hessians. + +``` +Knuth D add-back: when the trial quotient digit q̂ overshoots, the partial + remainder goes negative and one addition of the divisor + corrects it — a branch taken for roughly 2 divisions in 10⁹ +``` + +## Discrete mathematics + +**`discrete`** + +- **`primes`** — three cross-checking sieves, deterministic Miller–Rabin over + `u64`, Baillie–PSW for `BigInt`, Pollard rho and p−1, Fermat, factorization, + and prime counting by the Lucy_Hedgehog recurrence. +- **`number_theory`** — CRT for general moduli, multiplicative functions, + primitive roots, discrete logarithms, Legendre and Jacobi symbols, + Tonelli–Shanks, Carmichael numbers, Gaussian integers, Frobenius, + Egyptian fractions, Zeckendorf representations, Diophantine solving. +- **`combinatorics`** — counting, enumeration, and the permutation group. +- **`partitions`** — integer partitions, Young diagrams, RSK correspondence. +- **`sequences`** — integer sequences, linear recurrences, generating + functions, Berlekamp–Massey. +- **`disjoint_set`** — union-find with path compression and union by size. + +``` +π(n) via φ(x, a) = φ(x, a−1) − φ(x/pₐ, a−1) O(√n) state + π(10⁹) = 50,847,534, computed in 87 ms in a debug build + +Baillie–PSW = strong Miller–Rabin base 2 ∧ strong Lucas (Selfridge params) + neither test's pseudoprimes below 20,000 are the other's +``` + +**`graph`** — `Graph` representation, structural queries, generators and +products; shortest paths (Dijkstra, Bellman–Ford, Floyd–Warshall, A*, Johnson), +spanning trees and tours; network flow (Dinic, push–relabel, min-cost flow) and +the problems that reduce to it; matchings (bipartite, general Blossom, +weighted, stable); spectral graph theory (Laplacians, centralities, effective +resistance); colouring, cliques, independent sets and covers; and force-directed +and spectral layout. + +``` +max-flow = min-cut +Laplacian L = D − A, eigenvalue 0 multiplicity = number of components +Cheeger: λ₂/2 ≤ h(G) ≤ √(2λ₂) +effective resistance R(u,v) = (eᵤ − eᵥ)ᵀ L⁺ (eᵤ − eᵥ) +``` + +**`codes`** — checksums and check digits (CRC, Luhn, Verhoeff, Damm, Fletcher, +Adler); binary linear block codes (Hamming, Golay, syndrome decoding); +Reed–Solomon and BCH over finite fields; convolutional and turbo codes with +Viterbi and BCJR, over BSC/AWGN channels; lossless compression (Huffman, +arithmetic coding, LZ77/LZW, BWT, move-to-front); and the arithmetic behind +public-key cryptography (modular exponentiation, RSA, Diffie–Hellman, elliptic +curves over prime fields, Shamir secret sharing) — for study, not for +production use. + +``` +Singleton bound: d ≤ n − k + 1, met with equality by Reed–Solomon (MDS) +Hamming bound: 2ᵏ · Σ_{i≤t} C(n,i) ≤ 2ⁿ +Shannon capacity: C = 1 − H₂(p) (BSC), C = ½log₂(1 + S/N) (AWGN) +``` + +## Classical and continuum mechanics + +**`classical`** — projectile motion, collisions, simple harmonic motion, +damping, resonance. +**`gravitation`** — Newtonian gravity, potential, escape and orbital velocity. +**`solid_mechanics`** — stress, strain, elastic moduli, beam deflection, +Mohr's circle. +**`continuum_mechanics`** — 3-D Hooke's law, compliance matrices, plane stress +and plane strain. +**`fluid_instabilities`** — Rayleigh–Taylor, Kelvin–Helmholtz, Jeans, +Plateau–Rayleigh. +**`geophysics`** — seismic and Earth-structure relations. + +``` +projectile range R = v₀² sin(2θ) / g +damped SHM m x″ + c x′ + k x = F(t), ζ = c / (2√(km)) +Hooke (3-D) σᵢⱼ = λ δᵢⱼ ε_kk + 2μ εᵢⱼ +Euler–Bernoulli EI · d⁴w/dx⁴ = q(x) +Jeans length λ_J = √(π c_s² / (G ρ)) +``` + +**`resonance`** — the damped oscillator in closed form and numerically; +coupled oscillators, normal modes and modal superposition; acoustic and +electrical cavities (RLC, Helmholtz); nonlinear resonance (Duffing, van der +Pol); and structural dynamics with finite-element bars and beams. + +## Thermodynamics and statistical mechanics + +**`thermodynamics`** — ideal gas, Carnot, entropy, heat conduction, radiation, +Nusselt/Biot/Grashof. + +**`statistical_mechanics`** — Maxwell–Boltzmann, Boltzmann/Einstein/Debye +models, diffusion and partition functions, plus: + +- **`ising`** — the Ising model and its relatives by Monte Carlo + (Metropolis, Wolff cluster, heat bath). +- **`lattice_models`** — percolation, random walks, growth models, avalanches. +- **`md`** — molecular dynamics: pair potentials, cell-list force evaluation, + thermostats, radial distribution functions. +- **`kinetics`** — chemical kinetics: rate laws, deterministic and stochastic + reaction networks (Gillespie), Michaelis–Menten, Eyring, Nernst. + +``` +partition function Z = Σ exp(−βEᵢ), ⟨E⟩ = −∂ln Z/∂β, F = −kT ln Z +Ising energy E = −J Σ⟨ij⟩ sᵢsⱼ − h Σᵢ sᵢ +Onsager (2-D) sinh(2J/kT_c) = 1 ⟹ kT_c/J = 2/ln(1+√2) ≈ 2.269 +Lennard-Jones V(r) = 4ε[(σ/r)¹² − (σ/r)⁶] +Arrhenius k = A exp(−Eₐ/RT) +Nernst E = E° − (RT/zF) ln Q +``` + +## Electromagnetism and electronics + +**`electromagnetism`** — Coulomb, Lorentz, Faraday, Maxwell, RLC circuits, +transformers. +**`electronics`** — semiconductors, diodes, MOSFETs, solar cells, PN junctions. +**`rf`** — Friis, skin depth, antenna gain, impedance, VSWR, Smith chart +quantities. +**`photonics`** — Gaussian beams, fibre optics, ray transfer matrices, +coherence, Fabry–Pérot. +**`plasma`** — Debye length, cyclotron and plasma frequencies, Alfvén speed, +beta, Larmor radius. +**`magnetohydrodynamics`** — Alfvén waves, Hartmann flow, magnetic +reconnection, pinch equilibria. + +``` +Maxwell ∇·E = ρ/ε₀ ∇·B = 0 ∇×E = −∂B/∂t ∇×B = μ₀J + μ₀ε₀ ∂E/∂t +Lorentz F = q(E + v × B) +skin depth δ = √(2ρ / (ωμ)) +Friis P_r/P_t = G_t G_r (λ / 4πR)² +plasma freq ω_p = √(n e² / (ε₀ mₑ)) +Alfvén v_A = B / √(μ₀ ρ) +``` + +## Waves, optics and acoustics + +**`waves`** — Doppler, standing waves, diffraction, Snell, seismic waves, +dispersion. +**`optics`** — lenses, mirrors, thin films, diffraction gratings, Rayleigh +resolution. +**`acoustics`** — Sabine and Eyring reverberation, psychoacoustic scales (mel, +bark, ERB), room modes, STC. + +``` +Snell n₁ sin θ₁ = n₂ sin θ₂ +thin lens 1/f = 1/dₒ + 1/dᵢ +Rayleigh θ = 1.22 λ/D +Sabine RT₆₀ = 0.161 V / A +mel m = 2595 log₁₀(1 + f/700) +``` + +## Signal processing and transforms + +**`transforms`** + +- **`fft`** — fast Fourier transforms at *any* length: radix-2 and mixed-radix + for composite sizes, Bluestein's chirp-z for prime ones. +- **`dct`** — discrete cosine, sine and Hartley transforms. +- **`stft`** — short-time Fourier transform, spectrograms, Goertzel, chirp-z. +- **`wavelet`** — discrete and continuous wavelet transforms, with canonical + orthogonal and biorthogonal scaling-filter tables (Daubechies, Symlet, + Coiflet, Biorthogonal). +- **`hilbert`** — Hilbert transform, analytic signals, modulation, empirical + mode decomposition. +- **`laplace`** — numerical inverse Laplace transforms (fixed-Talbot, + Gaver–Stehfest). +- **`radon`** — Radon transform and filtered back-projection, plus Hankel and + Abel transforms. +- **`spectral`** — periodogram, Welch averaging, multitaper (DPSS), Lomb–Scargle + for unevenly sampled data. + +**`dsp`** — window functions; FIR design (windowed-sinc, Parks–McClellan, +least-squares) and application; IIR filters (RBJ biquads, second-order-section +cascades, Butterworth/Chebyshev/elliptic); sample-rate conversion (integer, +polyphase rational, arbitrary); and phase tools (1-D and Itoh 2-D unwrapping, +phase-locked loops). + +**`signal_processing`** — waveform generation, convolution, correlation. + +``` +DFT X[k] = Σₙ x[n] e^(−2πikn/N) +Parseval Σ|x[n]|² = (1/N) Σ|X[k]|² +Bluestein X[k] = conj(chirp) · ( (x·chirp) ⊛ chirp ) any N +biquad (RBJ) H(z) = (b₀ + b₁z⁻¹ + b₂z⁻²) / (a₀ + a₁z⁻¹ + a₂z⁻²) +Lomb–Scargle periodogram for non-uniform sampling, exact for a pure tone +``` + +## Audio + +**`audio`** — PolyBLEP anti-aliased oscillators and test signals; envelopes, +LFOs, followers and glides; additive, FM (DX7-style operator routing), granular +and wavetable synthesis; physical modelling (digital waveguides for plucked, +struck and bowed strings, modal resonators); effects (delays, Schroeder and +Freeverb reverbs, feedback delay networks, chorus, distortion, dynamics); +analysis (YIN, autocorrelation, cepstral and HPS pitch detection, onset +detection, MFCC); a phase vocoder for time stretching and pitch shifting; +musical tuning (temperaments, interval arithmetic, Scala file parsing); spatial +audio (panning laws, VBAP, ambisonics, binaural cues); and WAV (RIFF) reading +and writing for PCM 8/16/24/32-bit and IEEE float. + +``` +PolyBLEP corrects the discontinuity at a hard edge by subtracting a + polynomial approximation to the band-limited step +YIN d′(τ) = d(τ) / [(1/τ) Σ_{j≤τ} d(j)] cumulative mean normalization +equal temp fₙ = f₀ · 2^(n/12); cents = 1200 log₂(f₂/f₁) +``` + +## Fluids and computational fluid dynamics + +**`fluids`** — Bernoulli, Poiseuille, Reynolds, drag, capillarity, compressible +flow. +**`propulsion`** — Tsiolkovsky, Hohmann transfers, nozzle design, staging. + +**`cfd`** — staggered (MAC) grids and cell-centred scalar fields; advection +schemes (upwind, Lax–Wendroff, QUICK, MUSCL, WENO); a stable-fluids +incompressible solver with MacCormack advection and pressure projection; exact +and approximate Riemann solvers for the compressible Euler equations (HLL, +HLLC, Roe); well-balanced shallow water; smoothed-particle hydrodynamics; +lattice Boltzmann (D2Q9 with BGK/TRT/MRT collision); level sets for interface +capturing with WENO advection and Sussman reinitialization; potential flow with +complex-variable methods; Blasius and Falkner–Skan boundary layers; turbulence +modelling and statistics; regularized Biot–Savart vortex methods in 2-D and +3-D; Darcy and unsaturated porous-media flow; and multiphase drift-flux +correlations. + +``` +Navier–Stokes ∂u/∂t + (u·∇)u = −∇p/ρ + ν∇²u + f, ∇·u = 0 +Reynolds Re = ρUL/μ +Bernoulli p + ½ρv² + ρgh = const +Euler (1-D) ∂/∂t[ρ, ρu, E] + ∂/∂x[ρu, ρu²+p, u(E+p)] = 0 +CFL Δt ≤ CFL · Δx / max|λ| +lattice Boltzmann fᵢ(x+cᵢΔt, t+Δt) − fᵢ(x,t) = −(1/τ)(fᵢ − fᵢ^eq) +Blasius 2f‴ + f f″ = 0, δ/x = 5.0/√Re_x, c_f = 0.664/√Re_x +``` + +## Relativity, quantum and particle physics + +**`relativity`** — Lorentz transformations, relativistic energy–momentum, time +dilation, Doppler. +**`general_relativity`** — Schwarzschild metric, geodesics, frame dragging, +cosmological distances. +**`particle_physics`** — invariant mass, Rutherford scattering, Breit–Wigner, +rapidity, Lorentz boosts. + +**`quantum`** — de Broglie, uncertainty, particle in a box, tunnelling, Planck +radiation, plus: + +- **`wavefunction`** — 1-D wavefunctions, the standard eigenstates, and + phase-space representations (Wigner, Husimi). +- **`schrodinger`** — stationary and time-dependent solvers: shooting, + Numerov, matrix diagonalization, split-operator and Crank–Nicolson + propagation. +- **`circuit`** — a state-vector quantum circuit simulator, with density + matrices and noise channels. +- **`algorithms`** — Deutsch–Jozsa, Grover, quantum Fourier transform, phase + estimation, Shor's order finding. +- **`spin`** — spin operators, quantum magnets, magnetic resonance. +- **`solid_state`** — bands, densities of states, transport, phonons. + +``` +Schrödinger iħ ∂ψ/∂t = −(ħ²/2m)∇²ψ + Vψ +box eigenvalues Eₙ = n²π²ħ²/(2mL²) +uncertainty Δx Δp ≥ ħ/2 +Schwarzschild ds² = −(1−r_s/r)c²dt² + (1−r_s/r)⁻¹dr² + r²dΩ², r_s = 2GM/c² +invariant mass m²c⁴ = E² − (pc)² +Grover ~ (π/4)√N iterations +``` + +## Nuclear and radiation -// Projectile on Earth -let range = projectile_range(50.0, 0.7854, 9.81); // v₀=50 m/s, θ=45°, g=9.81 +**`nuclear`** — decay chains, binding energy, Q-values, dosimetry. +**`neutronics`** — criticality, diffusion, moderation, burnup, shielding. +**`radiation`** — blackbody, Wien, Planck, radiative transfer, view factors. -// Ideal gas -let p = ideal_gas_pressure(2.0, 8.314, 300.0, 0.05); // n, R, T, V +``` +decay N(t) = N₀ e^(−λt), t½ = ln2/λ +semi-empirical B = aᵥA − a_sA^(2/3) − a_c Z²/A^(1/3) − a_a(A−2Z)²/A ± δ +four-factor k_∞ = η f p ε +Planck B(λ,T) = (2hc²/λ⁵) / (exp(hc/λkT) − 1) +Wien λ_max T = 2.897771955×10⁻³ m·K +Stefan–Boltzmann j = σT⁴, σ = 2π⁵k⁴/(15h³c²) +``` + +## Astrophysics and orbital mechanics + +**`astrophysics`** — N-body simulation with a leapfrog integrator; Barnes–Hut +octree for O(N log N) forces; Keplerian elements from state vectors and orbit +propagation; gravitational-wave strain, luminosity, frequency and chirp mass; +tidal forces, Roche limits and tidal tensors; L1–L5 Lagrange points; habitable +zone boundaries and tidal locking; dipole magnetospheres and field-line +tracing; impact cratering and collision probabilities; plus: -// Rigid body simulation -let mut body = RigidBody::new_sphere(10.0, 0.5); // mass=10 kg, radius=0.5 m -body.apply_force(rust_physics_engine::math::Vec3::new(0.0, -98.1, 0.0)); -body.step(0.01); +- **`kepler`** — Kepler's equation solved to a residual below 1e-12 including + e = 0.99, for elliptic, parabolic and hyperbolic orbits. +- **`maneuvers`** — Hohmann and bi-elliptic transfers, plane changes, phasing, + J2 secular rates. +- **`lambert`** — Lambert's problem: the transfer orbit between two positions + in a given time. +- **`time_systems`** — Julian dates, UT1/TAI/TT/TDB, sidereal time, ΔT. +- **`coords`** — equatorial, ecliptic, galactic, horizontal and ITRF frames, + precession and nutation. + +``` +Kepler M = E − e sin E (elliptic) + M = e sinh H − H (hyperbolic) +vis-viva v² = μ(2/r − 1/a) +Hohmann Δv₁ = √(μ/r₁)(√(2r₂/(r₁+r₂)) − 1) +J2 nodal drift Ω̇ = −(3/2) J₂ (R/p)² n cos i sun-synchronous at 700 km: i = 98.2° +chirp mass ℳ = (m₁m₂)^(3/5) / (m₁+m₂)^(1/5) +Roche limit d = 2.44 R (ρ_M/ρ_m)^(1/3) +J2000 JD of 2000-01-01 12:00 TT = 2451545.0 ``` +## PDE solvers: finite elements, FDTD and spectral + +**`fem`** — three ways of turning a differential equation into a linear system, +kept together because the interesting content is how they differ. + +- **`fem1d`** — one-dimensional finite elements for `−(p u′)′ + q u = f`, with + P1 and P2 elements, Dirichlet/Neumann/Robin boundary conditions, and L², H¹ + and energy-norm error computation. +- **`fem2d`** — triangular elements in the plane: Poisson, reaction–diffusion, + Helmholtz, drum eigenvalues and eigenmodes, plane-stress elasticity with + strain, stress and von Mises recovery, and transient heat. +- **`fdtd`** — Maxwell's equations on a Yee grid: 1-D and 2-D TM, Berenger + split-field PML with polynomial grading, Mur absorbing boundaries, photonic + crystal band gaps, and waveguide cutoff. +- **`spectral_pde`** — Chebyshev differentiation matrices and collocation + BVPs, Fourier spectral solvers for periodic problems. + +The finite element method is the one that gives an *optimality* statement +rather than an error estimate. Galerkin orthogonality makes the discrete +solution the exact energy-norm projection of the true one, so the error obeys a +Pythagoras identity — an equality, which cannot hold by accident: + +``` +Galerkin a(u − u_h, v_h) = 0 for every v_h in the space +Pythagoras ‖u − v_h‖²_a = ‖u − u_h‖²_a + ‖u_h − v_h‖²_a +convergence P1: O(h²) in L², O(h) in H¹; P2: O(h³) in L², O(h²) in H¹ +cotangent K_ij = −½(cot α + cot β) Delaunay ⟹ M-matrix +Rayleigh–Ritz discrete eigenvalues bound the true ones from above + +FDTD (1-D) E^(n+1) = E^n − (Δt/εΔx)(H^(n+½)_{i+½} − H^(n+½)_{i−½}) +leapfrog energy U^n = ½Σ εᵣ(E^n)² + ½Σ H^(n−½)H^(n+½) conserved exactly +Courant S = cΔt/Δx ≤ 1/√d, set by the *fastest* medium (min εᵣ) +magic time step S = 1 in 1-D is an exact shift — zero dispersion error +PML grading σ_max = −(m+1) S ln(R₀) / (2D) +waveguide cutoff ω_c = (2/S) arcsin(S sin(k_y/2)) below mπ/a by (k_y/2)²(1−S²)/6 + +Chebyshev exact on polynomials up to degree N; the diagonal of the + differentiation matrix follows from the negative sum trick +``` + +## Chemistry and biophysics + +**`chemistry`** — Arrhenius, Nernst, pH, electrochemistry, reaction kinetics. + +**`biophysics`** — Nernst and Goldman potentials, Michaelis–Menten, Hill +equation, hemodynamics, plus: + +- **`epidemiology`** — SIR/SEIR/SIRS compartment models, their stochastic + counterparts, and the final-size relation. +- **`population`** — growth laws, Lotka–Volterra and competition models, + Hardy–Weinberg, drift, selection and coalescent theory. +- **`seq_align`** — Needleman–Wunsch, Smith–Waterman, affine gaps + (Gotoh), and elementary sequence analysis. +- **`phylo`** — distance methods (UPGMA, neighbour joining), character methods + (Fitch, Sankoff), and substitution models (Jukes–Cantor, Kimura). +- **`neuro`** — Hodgkin–Huxley, integrate-and-fire variants, spike trains, + synapses and small networks. + +``` +Michaelis–Menten v = V_max[S] / (K_m + [S]) +Hill θ = [L]ⁿ / (K_d + [L]ⁿ) +Goldman V_m = (RT/F) ln[ (P_K[K]ₒ + P_Na[Na]ₒ + P_Cl[Cl]ᵢ) + / (P_K[K]ᵢ + P_Na[Na]ᵢ + P_Cl[Cl]ₒ) ] +SIR S′ = −βSI, I′ = βSI − γI, R′ = γI, R₀ = β/γ +final size ln(S₀/S_∞) = R₀(1 − S_∞/N) +Hodgkin–Huxley C dV/dt = −ḡ_Na m³h(V−E_Na) − ḡ_K n⁴(V−E_K) − g_L(V−E_L) + I +Jukes–Cantor d = −(3/4) ln(1 − (4/3)p) +``` + +## Stochastic processes and time series + +**`stochastic`** + +- **`markov`** — finite Markov chains, stationary distributions, absorption, + and Markov chain Monte Carlo. +- **`hmm`** — hidden Markov models: forward–backward, Viterbi, Baum–Welch, + and particle filters. +- **`sde`** — stochastic differential equations: Euler–Maruyama, Milstein, + strong and weak convergence, geometric Brownian motion, + Ornstein–Uhlenbeck, fractional Brownian motion. +- **`point_process`** — Poisson, inhomogeneous, Hawkes and Cox processes. +- **`queueing`** — birth–death queues, Erlang B and C, networks. +- **`timeseries`** — autocorrelation, stationarity tests, ARMA fitting, + spectral estimation, Kalman filtering. +- **`rmt`** — random matrix theory: the classical ensembles, semicircle and + Marchenko–Pastur laws, level spacing. +- **`extreme`** — extreme value theory (GEV, GPD, block maxima, peaks over + threshold) and copulas. + +``` +Chapman–Kolmogorov P^(m+n) = P^m P^n +detailed balance π(i)P(i,j) = π(j)P(j,i) ⟹ π stationary +Itô dX = a(X,t)dt + b(X,t)dW +Euler–Maruyama strong order ½, weak order 1 +Milstein strong order 1 +Little's law L = λW +Erlang B B(c,a) = (aᶜ/c!) / Σ_{k≤c} (aᵏ/k!) +semicircle ρ(x) = (1/2π)√(4 − x²) on [−2, 2] +Marchenko–Pastur support [(1−√λ)², (1+√λ)²], λ = p/n +``` + +## Optimization and operations research + +**`optimization`** + +- **`lp`** — the simplex method, interior point methods, duality and + sensitivity analysis. +- **`integer`** — branch and bound, cutting planes, dynamic programming, + knapsack, TSP, combinatorial search. +- **`network`** — project planning (CPM/PERT), flows on networks, scheduling. +- **`convex`** — gradient and accelerated gradient methods, L-BFGS, proximal + operators, ADMM, projections. +- **`metaheuristics`** — simulated annealing, genetic algorithms, particle + swarm, differential evolution, CMA-ES, Bayesian optimization, and the + standard benchmark functions. +- **`game_theory`** — Nash and correlated equilibria, evolutionary dynamics, + Shapley value, the core, auctions and matching. +- **`least_squares`** — Levenberg–Marquardt for nonlinear least squares. + +Plus golden section, Brent, Nelder–Mead, linear regression and polynomial +fitting at the module root. + +``` +LP duality max cᵀx s.t. Ax ≤ b, x ≥ 0 ≡ min bᵀy s.t. Aᵀy ≥ c, y ≥ 0 +complementary xⱼ(Aᵀy − c)ⱼ = 0 and yᵢ(b − Ax)ᵢ = 0 at optimality +KKT ∇f + Σλᵢ∇gᵢ + Σμⱼ∇hⱼ = 0, λᵢgᵢ = 0, λᵢ ≥ 0 +Nesterov O(1/k²) for smooth convex, against O(1/k) for plain descent +Shapley φᵢ = Σ_S |S|!(n−|S|−1)!/n! · [v(S∪{i}) − v(S)] +``` + +## Machine learning + +**`learn`** — written to be read rather than to be fast, and every method has +an exactly checkable property attached, because that is what makes a learning +algorithm testable at all. + +- **`nn`** — feed-forward networks trained by backpropagation, with a + numerical gradient check as the definitive test, plus 2-D convolution. +- **`gp`** — Gaussian process regression: fitting, prediction, log marginal + likelihood, hyperparameter optimization, posterior sampling. +- **`cluster`** — k-means with k-means++ initialization and restarts, DBSCAN, + hierarchical agglomerative clustering, Gaussian mixture EM, silhouette, + adjusted Rand index, Davies–Bouldin, and k-nearest-neighbour + classification and regression. +- **`tree`** — decision trees, regression trees, feature importance, random + forests, gradient boosting. + +``` +backprop reverse-mode differentiation — so a finite-difference check + settles whether the gradients are right, and nothing else does +softmax + CE ∂L/∂z = p − y exactly, at the logits +GP posterior μ* = K*ᵀ(K + σ²I)⁻¹y, Σ* = K** − K*ᵀ(K + σ²I)⁻¹K* +log marginal log p(y) = −½yᵀK⁻¹y − ½log|K| − (n/2)log 2π +k-means Lloyd's algorithm decreases inertia monotonically +EM increases the log likelihood monotonically +tree depth d ⟹ at most 2^d leaves ⟹ at most 2^d distinct predictions +ARI corrected for chance: expected value 0 for a random labelling +``` + +## Quantitative finance + +**`finance`** + +- **`options`** — Black–Scholes closed forms and Greeks, binomial and + trinomial lattices, Monte Carlo with variance reduction, Longstaff–Schwartz + for American options, and a Crank–Nicolson PDE solver. +- **`rates`** — discounting, bond pricing, duration and convexity, curve + bootstrapping, and short-rate models (Vasicek, CIR, Hull–White). +- **`portfolio`** — mean–variance optimization, the efficient frontier, + CAPM, and performance measurement. +- **`risk`** — value at risk, expected shortfall, and backtesting. + +``` +Black–Scholes C = S N(d₁) − K e^(−rT) N(d₂) + d₁ = [ln(S/K) + (r + σ²/2)T] / (σ√T), d₂ = d₁ − σ√T +put–call parity C − P = S − K e^(−rT) holds to 1e-12 +binomial → BS error O(1/n) +Macaulay duration D = Σ t·CF_t·e^(−rt) / P, ΔP/P ≈ −D·Δy +Vasicek dr = a(b − r)dt + σdW +VaR / ES ES_α = E[X | X ≤ VaR_α] — coherent where VaR is not +``` + +## Geometry, manifolds and topology + +**`geometry`** — areas, volumes, perimeters, regular polygons. +**`curves`** — conic sections, Bézier curves, arc length, curvature. +**`trigonometry`** — identities, hyperbolic functions, angle conversion, +haversine. +**`quaternion`** — quaternion algebra, slerp and nlerp, axis–angle, Euler +conversions. +**`vector_calculus`** — gradient, divergence, curl, Laplacian, a Poisson +solver, and line, surface and volume integrals. + +**`manifold`** — geometry beyond three dimensions. + +- **`vecn`** — n-dimensional vectors and arbitrary-rank tensors. +- **`metric`** — Riemannian metrics, Christoffel symbols, curvature tensors. +- **`geodesic`** — geodesics, parallel transport, Jacobi fields, relativistic + orbits. +- **`lie`** — Lie groups and algebras: rotation and rigid-motion groups in 2, + 3 and 4 dimensions, exponential and logarithm maps. +- **`hyperbolic`** — the Poincaré disk and ball, upper half-space, hyperboloid + and Klein models, and the maps between them. +- **`spherical`** — n-sphere maps, spherical trigonometry, map projections. +- **`polytope4`** — the six regular 4-polytopes with their full symmetry + groups, Schlegel diagrams and cross sections. +- **`clifford`** — Clifford (geometric) algebras Cl(p, q, r) with a dense + multivector type. +- **`dec`** — discrete exterior calculus on triangle meshes. +- **`embedding`** — manifold learning: spectral embeddings, MDS, Isomap, LLE, + diffusion maps. +- **`spacetime`** — four-vectors, Lorentz transforms, and curved-spacetime + metrics. + +``` +Christoffel Γᵏᵢⱼ = ½gᵏˡ(∂ᵢgⱼˡ + ∂ⱼgᵢˡ − ∂ˡgᵢⱼ) +geodesic ẍᵏ + Γᵏᵢⱼ ẋⁱẋʲ = 0 +Riemann R^ρ_σμν = ∂_μΓ^ρ_νσ − ∂_νΓ^ρ_μσ + Γ^ρ_μλΓ^λ_νσ − Γ^ρ_νλΓ^λ_μσ +Gauss–Bonnet ∫K dA + ∮k_g ds = 2πχ +geometric product ab = a·b + a∧b +hyperbolic area for a triangle, A = π − (α + β + γ) +Euler characteristic V − E + F = 2 for every convex polyhedron +``` + +**`spatial`** — bounding volume hierarchies, k-d trees, quadtrees and octrees; +orientation predicates and containment tests; closest-point and set-distance +queries; intersection tests; rigid coordinate frames; 4×4 homogeneous +transforms and 2-D affine transforms; 2-D projective geometry with cross +ratios; and signed distance fields with primitives, combinators and domain +operators. + +**`mesh`** — indexed triangle meshes: construction, mass properties, cleanup, +topology analysis (manifoldness, orientation, boundary, genus), procedural +generation with watertight closed shapes, subdivision surfaces (Loop, +Catmull–Clark, √3, midpoint), parameterization, isosurface and isocontour +extraction, and parametric surfaces (Bézier, B-spline, NURBS). + +## Patterns, fractals and chaos + +**`fractals`** — escape-time fractals with smooth colouring (Mandelbrot, Julia, +burning ship, Newton); strange attractors (Lorenz, Rössler, and 2-D chaotic +maps); iterated function systems and the chaos game; Lindenmayer systems; +cellular automata and growth models; and coherent noise (Perlin, OpenSimplex2, +Worley, fBm). + +**`patterns`** — 2-D polygon algorithms (triangulation, simplification, +offsetting, boolean operations); Poisson-disk and low-discrepancy sampling +(Bridson, Halton, Sobol); Platonic, Archimedean, Catalan, Johnson and Goldberg +polyhedra; aperiodic tilings (Penrose P2 and P3 by substitution); circle and +sphere packings (Descartes, Apollonian, lattice); phyllotaxis and spirals; +space-filling curves (Hilbert, Morton, Peano); knots and space curves with +Frenet frames; and the 17 wallpaper groups and 7 frieze groups. + +**`nonlinear`** — logistic map, Lorenz and Rössler attractors, Lyapunov +exponents, bifurcation diagrams. + +**`information_theory`** — Shannon entropy, mutual information, KL divergence, +channel capacity. + +``` +Mandelbrot z ↦ z² + c, escape when |z| > 2 +Feigenbaum δ = 4.669201609…, α = 2.502907875… +Lyapunov λ = lim (1/n) Σ ln|f′(xᵢ)|, λ > 0 ⟹ chaos +box counting D = lim log N(ε) / log(1/ε) +Descartes (k₁+k₂+k₃+k₄)² = 2(k₁²+k₂²+k₃²+k₄²) +golden angle 137.507764…° = 360°/φ² +Shannon H = −Σ pᵢ log₂ pᵢ +``` + +## Units, dimensions and constants + +**`units`** — SI conversions for temperature, pressure, energy, length, speed, +angle and more, plus: + +- **`quantity`** — `Dim`, seven signed-byte exponents for metre, kilogram, + second, ampere, kelvin, mole and candela; and `Quantity`, a value that + carries them. Addition checks that the exponents agree and refuses if they + do not, multiplication adds them, and a square root fails unless every one is + even. None of it is approximate. Also a unit parser (`"9.81 m/s^2"`, + `"3 kWh"`), SI prefix formatting, and the 2022 CODATA constants. +- **`dimensional`** — Buckingham's Π theorem computed as an exact null space + over `Rational`, the named dimensionless groups, natural units (ħ = c = 1), + the Planck scale, and `dimensional_check_formula`, which walks a symbolic + expression and refuses a sum of unlike terms or a sine of a length. + +``` +Buckingham n quantities, r independent dimensions ⟹ exactly n − r groups + a group is exactly in the null space or it is not — so the + computation is done over Rational, never in floating point +Reynolds Re = ρUL/μ — the one group of the pipe-flow problem +Planck length √(ħG/c³) = 1.616255×10⁻³⁵ m +Planck mass √(ħc/G) = 2.176434×10⁻⁸ kg +natural units [L] = [T] = [E]⁻¹, [M] = [E]; 1 kg = 5.6096×10³⁵ eV +transcendentals exp, sin and ln take a pure number — because their series add + x to x³, so exp(−t/τ) is meaningful and exp(−t) is not +``` + +Physical constants live in one table, `math::constants`. The values fixed by +the 2019 SI redefinition are exact; derived ones are computed from their +factors rather than transcribed, so `FARADAY == N_A * E_CHARGE` holds bit for +bit. A test pins the two constant tables together: the SI-exact constants must +agree exactly, and the measured ones to within 1e-8, which separates the +2018→2022 CODATA revision from a mistyped digit. + +## Reference data + +- **`materials::elements`** — all 118 elements with atomic mass, density, + melting and boiling points, and thermal and electrical conductivity. +- **`materials::common`** — engineering materials (steels, aluminium, copper…). +- **`materials::fluids`** — 16 fluids with density, viscosity, surface tension + and speed of sound. +- **`materials::gases`** — gases with molar mass, specific heat ratio and + thermal conductivity. + +## Simulation engines + +- **`sim::rigid_body`** — 3-D rigid body dynamics with quaternion orientation, + Euler's equations, and collision response. +- **`sim::fluid_sim`** — column fluid, 1-D shallow water, 2-D incompressible + Euler with pressure projection. +- **`sim::heat_sim`** — 2-D and 3-D heat conduction, convection–diffusion. +- **`sim::wave_sim`** — 1-D and 2-D wave equations with Mur absorbing + boundaries. +- **`sim::em_sim`** — 1-D and 2-D FDTD with PEC and Mur boundaries and + dielectric media. +- **`sim::cloth_sim`** — Verlet cloth and rope with spring–damper constraints. + +## Utilities + +- **`color_science`** — RGB/HSV/HSL/XYZ, wavelength to colour, blackbody + colour, CIE colour difference. +- **`control_systems`** — transfer functions, step and impulse response, PID + tuning, stability margins. +- **`atmosphere`** — the ISA model, barometric formula, lapse rates, humidity, + wind shear. +- **`monte_carlo`** — Monte Carlo integration, Metropolis–Hastings, Langevin + dynamics, random walks, and an `Rng`. +- **`statistics`** — descriptive measures, error propagation, distributions, + hypothesis tests and confidence intervals, resampling (bootstrap, BCa, + permutation), and DFT utilities. +- **`fields`** — uniform-grid scalar fields. +- **`error`** — the error types the numerical solvers share. + +> **A note on `monte_carlo::Rng`.** It is a plain linear congruential +> generator that returns its raw state, so the low bits have a short period: +> `next_u64() % m` for a power-of-two `m` cycles through a handful of values. +> Use `Rng::below(n)`, which takes the high bits, for any small-integer draw. + +--- + +# How it's tested + +Four independent mechanisms, because they fail in different ways. + +### Unit tests — 4,193 + +Every one asserts something checkable: a closed form, a conservation law, a +known reference value, or an exact identity. Approximate results carry a stated +tolerance derived from the method's error term, not one tuned until the test +passed. + +### Property tests — 577, across 49 files + +Randomized tests over invariants that must hold for *every* input, not for a +chosen example. They are what catch the cases nobody thought to write down: +round-trips (transform then invert), algebraic laws (multiplying dimensions +adds their exponents, exactly), conservation (leapfrog FDTD energy), and +agreement between two independent implementations of the same quantity. + +### Formal verification — 20 Kani harnesses + +Kani model-checks with CBMC: for a bounded input domain it proves absence of +panics, overflow and division by zero, rather than sampling. Thirteen run in +CI; seven that CBMC cannot decide in a CI-sized budget sit behind the +`kani-slow` feature, with the measured times recorded in +`src/verification/mod.rs`. Asserting panic-freedom lands in well under a +minute; asserting a numeric relation between symbolic float expressions +exceeds five, because CBMC must bit-blast the full mantissa of every +intermediate. + +### Miri + +The crate contains no `unsafe`, so Miri is a backstop rather than the primary +check. It runs against `core::` — the interval and dual-number arithmetic, +where index and slice reasoning is densest — under +`-Zmiri-strict-provenance`. + +### Coverage — 97.89% of lines, 99.33% of functions + +Measured by `cargo llvm-cov` on every push. + +### Why the coverage number is not the point + +Coverage says a line ran, not that anything checked what it did. Some of the +worst gaps this project has found were in lines with full coverage: + +- **The strong Lucas test** — the second half of Baillie–PSW — was replaced + wholesale with `return true`, and the entire suite stayed green. Composites + are rejected by Miller–Rabin first, so the Lucas branch is essentially never + reached in practice. It is now tested directly against the strong Lucas + pseudoprimes below 20,000 (5459, 5777, 10877, 16109, 18971), computed + independently rather than read off the implementation. +- **Knuth's add-back correction** in `BigInt` division, taken for roughly two + divisions in a billion. +- **The Lentz convergence threshold** in the continued-fraction evaluators. + +Each was found by mutating the code and observing that no test noticed. + +--- + ## Design -- **Zero dependencies** — pure Rust, no external crates -- **`f64` throughout** — double precision for all computation -- **Input validation** — every function asserts valid inputs (positive mass, non-zero denominators, physical bounds) -- **NIST constants** — physical constants from CODATA 2018/2019 -- **99.98% line coverage** — 1,659 tests across 67 source files +- **Zero dependencies.** `Cargo.lock` holds exactly one package: this crate. +- **`f64` throughout**, except where exactness is the point — `exact` works + over arbitrary-precision integers and rationals, and `units::dimensional` + computes null spaces over `Rational` because a group is exactly + dimensionless or it is not. +- **Input validation.** Public functions validate their inputs: positive mass, + non-zero denominators, physical bounds. Solvers return `Result` rather than + panicking on non-convergence. +- **One table per constant.** Physical constants come from `math::constants`; + duplicates elsewhere are re-exports, and a test enforces the agreement. +- **Documented specification choices.** Where a definition is genuinely + ambiguous — the Frobenius number with a unit coin, Stern–Brocot indexing, + which parenthesization a unit string means — the reading is stated in the + doc comment rather than left implicit. -## Building & Testing +## Building and testing ```bash -cargo build # Build -cargo test # Run all 1,659 tests -cargo llvm-cov # Coverage report (requires cargo-llvm-cov) +cargo build # build +cargo test --lib # 4,193 unit tests +cargo test --release --test properties # 577 property tests +cargo clippy --all-targets -- -D warnings +cargo llvm-cov --summary-only # coverage (needs cargo-llvm-cov) +cargo kani # 13 harnesses (needs Kani) +cargo kani --features kani-slow # all 20, much slower +cargo miri test --lib -- core:: --skip ::core:: ``` +CI runs `test` and `coverage` as parallel jobs, and a separate `verify` +workflow runs the property suite, Kani, Miri and a strict Clippy pass. + ## License MIT diff --git a/examples/readme_quickstart.rs b/examples/readme_quickstart.rs new file mode 100644 index 0000000..aa4a865 --- /dev/null +++ b/examples/readme_quickstart.rs @@ -0,0 +1,33 @@ +//! The Quick start snippet from README.md, kept compiling so it cannot rot. +//! Any edit here must be mirrored in the README and vice versa. + +use rust_physics_engine::classical::projectile_range; +use rust_physics_engine::exact::rational::Rational; +use rust_physics_engine::math::constants::{C, G}; +use rust_physics_engine::units::quantity::{Dim, Quantity}; + +fn main() { + + // Ballistics: v₀ = 50 m/s, θ = 45°, g = 9.81 m/s² + let range = projectile_range(50.0, std::f64::consts::FRAC_PI_4, 9.81); + assert!((range - 254.841_997_961).abs() < 1e-9); + + // Constants come from one table. A black hole's Schwarzschild radius: + let solar_mass = 1.989e30; + let r_s = 2.0 * G * solar_mass / (C * C); // about 2.95 km + + // Quantities carry their dimensions, and addition checks them. + let v = Quantity::new(3.0, Dim::new(1, 0, -1, 0, 0, 0, 0)); // m/s + let t = Quantity::new(2.0, Dim::TIME); + let d = v.mul(&t).unwrap(); // 6 m — a length, exactly + assert!(v.add(&t).is_err()); // a velocity is not a time + + // Exact rational arithmetic over arbitrary-precision integers. + let third = Rational::from_i64(1, 3); + let one = third.mul(&Rational::from_i64(3, 1)); + assert_eq!(one, Rational::one()); // not 0.9999999999999999 + + println!("range = {range:.6} m"); + println!("r_s = {:.1} m", r_s); + println!("d = {} {}", d.value, d.dim); +} From 8568d3d361843e680e7195ac53bfdaa37fb4796f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 01:53:52 +0000 Subject: [PATCH 2/5] docs: fix 81 rustdoc warnings, gate them in CI, close out the roadmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The README rewrite covered the front page but not the documentation the crate actually publishes, and `cargo doc` was emitting 81 warnings. They are rendering bugs rather than style. Rustdoc parses `[k]` in a formula as an intra-doc link, so `X[k] = Σ x[n]·e^(−j2πkn/N)` rendered with two broken links in it and no way for a reader to see the formula as written. It parses `` and `` as HTML tags, so `"B/S"` and `R = _0 + _2` lost content in the rendered output entirely. Fifty-one doc comments were affected, across transforms, statistics, signal_processing, dsp, linalg, biophysics, chemistry, color_science, manifold, fractals, sim, numerical and core. Each is now wrapped in backticks so it renders as the mathematics it is. Three cases needed more than that: - Two-line Mur boundary formulas in sim/wave_sim.rs became a ```text block, which is what they always were. - Three links pointed at private items -- MAX_QUBITS, RESTARTS and mc_table -- which rustdoc cannot link to. They now name the item as code, with its value where that is the useful part. - audio/effects.rs linked [`read`] from a sibling method, which does not resolve without the Self:: prefix. Its summary also read "Alias of read for tap taps", which was a typo for nothing in particular. CI now builds the documentation with RUSTDOCFLAGS=-D warnings in the test job, so the count stays at zero. docs/ROADMAP_PART4.md carried no indication that the work in it was finished. It now opens with a status block recording that all thirty sessions plus the two unlisted cross-references are delivered, and what the test counts went from and to. The body is left exactly as written -- it is the specification the work was built against, and the signatures in it are the ones that shipped. README gains the documentation facts in the stats table, the `cargo doc` invocation, and a Further reading section pointing at the roadmap. cargo doc --no-deps: 0 warnings, from 81. 4,193 lib tests, 577 property tests, clippy clean under --all-targets -D warnings, nightly-2025-11-21 clean. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01VUkMGnfUbaopkUJLWeYCYi --- .github/workflows/ci.yml | 10 ++++++++++ README.md | 14 ++++++++++++-- docs/ROADMAP_PART4.md | 21 +++++++++++++++++++++ src/audio/effects.rs | 2 +- src/biophysics/mod.rs | 10 +++++----- src/chemistry.rs | 6 +++--- src/color_science.rs | 8 ++++---- src/core/dual.rs | 2 +- src/dsp/windows.rs | 9 +++++---- src/fractals/attractors.rs | 2 +- src/fractals/automata.rs | 4 ++-- src/fractals/lsystem.rs | 6 +++--- src/learn/cluster.rs | 2 +- src/linalg/matrix.rs | 2 +- src/linalg/mod.rs | 2 +- src/linalg/tridiagonal.rs | 2 +- src/manifold/clifford.rs | 6 +++--- src/manifold/metric.rs | 2 +- src/manifold/polytope4.rs | 2 +- src/mesh/isosurface.rs | 2 +- src/numerical/interpolate.rs | 2 +- src/numerical/ode/explicit.rs | 2 +- src/quantum/circuit.rs | 2 +- src/signal_processing/mod.rs | 6 +++--- src/sim/heat_sim.rs | 6 +++--- src/sim/wave_sim.rs | 12 ++++++++---- src/statistics/fourier.rs | 6 +++--- src/transforms/fft.rs | 6 +++--- 28 files changed, 101 insertions(+), 55 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2913ec3..c00aa92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,16 @@ jobs: - name: Check the README example run: cargo run --example readme_quickstart + # Rustdoc warnings are rendering bugs, not style. `[k]` in a formula + # becomes a broken intra-doc link and `` becomes an unclosed + # HTML tag that swallows the rest of the line, so the published docs + # show something other than what the comment says. There were 81 of + # these; denying them keeps the count at zero. + - name: Documentation + run: cargo doc --no-deps + env: + RUSTDOCFLAGS: "-D warnings" + coverage: name: Coverage runs-on: ubuntu-latest diff --git a/README.md b/README.md index 7efb61a..154ef53 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ assertion uses `==`. | **Function coverage** | 99.33% (20,200 functions, 136 uncovered) | | **Formal verification** | 20 Kani harnesses (13 in CI, 7 behind `kani-slow`) | | **Undefined behaviour** | Miri-clean; the crate contains no `unsafe` | +| **API documentation** | `cargo doc` builds warning-free; CI denies rustdoc warnings | | **Dependencies** | none — `Cargo.lock` holds exactly one package | | **Edition** | 2021, `f64` throughout | @@ -898,10 +899,19 @@ cargo llvm-cov --summary-only # coverage (needs cargo-llvm-cov) cargo kani # 13 harnesses (needs Kani) cargo kani --features kani-slow # all 20, much slower cargo miri test --lib -- core:: --skip ::core:: +cargo doc --no-deps --open # API documentation ``` -CI runs `test` and `coverage` as parallel jobs, and a separate `verify` -workflow runs the property suite, Kani, Miri and a strict Clippy pass. +CI runs `test` and `coverage` as parallel jobs — the `test` job also runs the +README example and builds the documentation with `RUSTDOCFLAGS=-D warnings` — +and a separate `verify` workflow runs the property suite, Kani, Miri and a +strict Clippy pass. + +## Further reading + +- [`docs/ROADMAP_PART4.md`](docs/ROADMAP_PART4.md) — the specification the most + recent tranche of work was built against, with the function signatures and + the property each module had to satisfy. Marked complete. ## License diff --git a/docs/ROADMAP_PART4.md b/docs/ROADMAP_PART4.md index 7cc52bb..5b7e668 100644 --- a/docs/ROADMAP_PART4.md +++ b/docs/ROADMAP_PART4.md @@ -1,5 +1,26 @@ # ROADMAP PART 4: discrete math, exact arithmetic, stochastic processes, optimization, quantum, statistical mechanics, domains +> **Status: COMPLETE.** All thirty planned sessions are delivered, plus the +> two cross-references in "Cross-references and shared additions" that were +> not in the session list: `units::dimensional::dimensional_check_formula` +> (the `exact::symbolic` tie-in) and the `constants_codata` consolidation. +> +> Delivered against this plan: `exact/`, `discrete/`, `graph/`, `codes/`, +> `stochastic/`, `optimization/`, `quantum/`, `statistical_mechanics/`, +> `biophysics/`, `finance/`, `astrophysics/`, `fem/`, `learn/` and `units/`. +> The crate went from 2,981 to 4,193 unit tests and from 107 to 577 property +> tests over the course of it. +> +> The document below is kept as written — it is the specification the work was +> built against, and the function signatures in it are the ones that shipped. +> Where a signature changed in implementation the reason is recorded in the +> commit that changed it. Three specification readings that the plan left +> genuinely ambiguous are documented in the code rather than here: +> `quadratic_diophantine_solve` as a definite form, `frobenius_number` with a +> unit coin, and `stern_brocot_nth` indexed breadth-first. +> +> See the README for what the library now contains. + Same rules as Parts 1-3. This part covers everything computational that the repo still lacks after 1-3: exact and big-number arithmetic, number theory, combinatorics, graphs, error-correcting codes, stochastic diff --git a/src/audio/effects.rs b/src/audio/effects.rs index 3ec03bb..b908900 100644 --- a/src/audio/effects.rs +++ b/src/audio/effects.rs @@ -48,7 +48,7 @@ impl DelayLine { a * (1.0 - f) + b * f } - /// Alias of [`read`] for tap taps. + /// Alias of [`Self::read`]. #[must_use] pub fn tap(&self, d: usize) -> f64 { self.read(d) diff --git a/src/biophysics/mod.rs b/src/biophysics/mod.rs index a2b4800..cc273c2 100644 --- a/src/biophysics/mod.rs +++ b/src/biophysics/mod.rs @@ -111,7 +111,7 @@ pub fn nernst_potential(temperature: f64, z: f64, c_out: f64, c_in: f64) -> f64 } /// Goldman-Hodgkin-Katz voltage equation for K⁺, Na⁺, and Cl⁻. -/// Vm = (RT/F) × ln((Pk[K]o + Pna[Na]o + Pcl[Cl]i) / (Pk[K]i + Pna[Na]i + Pcl[Cl]o)) +/// `Vm = (RT/F) × ln((Pk[K]o + Pna[Na]o + Pcl[Cl]i) / (Pk[K]i + Pna[Na]i + Pcl[Cl]o))` pub fn goldman_potential( temperature: f64, pk: f64, @@ -138,12 +138,12 @@ pub fn resting_membrane_potential_typical() -> f64 { // ── Enzyme Kinetics ── -/// Michaelis-Menten kinetics: v = Vmax × [S] / (Km + [S]) +/// Michaelis-Menten kinetics: `v = Vmax × [S] / (Km + [S])` pub fn michaelis_menten(vmax: f64, km: f64, substrate: f64) -> f64 { vmax * substrate / (km + substrate) } -/// Competitive inhibition: v = Vmax × [S] / (Km(1 + [I]/Ki) + [S]) +/// Competitive inhibition: `v = Vmax × [S] / (Km(1 + [I]/Ki) + [S])` pub fn michaelis_menten_inhibited( vmax: f64, km: f64, @@ -155,7 +155,7 @@ pub fn michaelis_menten_inhibited( vmax * substrate / (km * (1.0 + inhibitor / ki) + substrate) } -/// Lineweaver-Burk transform: returns (1/[S], 1/v) for double-reciprocal plot +/// Lineweaver-Burk transform: returns `(1/[S], 1/v)` for double-reciprocal plot pub fn lineweaver_burk(vmax: f64, km: f64, substrate: f64) -> (f64, f64) { assert!(substrate > 0.0, "substrate must be positive"); let v = michaelis_menten(vmax, km, substrate); @@ -163,7 +163,7 @@ pub fn lineweaver_burk(vmax: f64, km: f64, substrate: f64) -> (f64, f64) { (1.0 / substrate, 1.0 / v) } -/// Hill equation for cooperative binding: v = Vmax × [S]^n / (K^n + [S]^n) +/// Hill equation for cooperative binding: `v = Vmax × [S]^n / (K^n + [S]^n)` pub fn hill_equation(vmax: f64, k: f64, substrate: f64, n: f64) -> f64 { let s_n = substrate.powf(n); let k_n = k.powf(n); diff --git a/src/chemistry.rs b/src/chemistry.rs index 440fb96..acb80dc 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -21,18 +21,18 @@ pub fn half_life_first_order(rate_constant: f64) -> f64 { f64::ln(2.0) / rate_constant } -/// First-order concentration decay: [A] = [A]₀ × e^(-kt) +/// First-order concentration decay: `[A] = [A]₀ × e^(-kt)` pub fn concentration_first_order(c0: f64, rate_constant: f64, time: f64) -> f64 { c0 * (-rate_constant * time).exp() } -/// Second-order integrated rate law: 1/[A] = 1/[A]₀ + kt, returns [A] +/// Second-order integrated rate law: `1/[A] = 1/[A]₀ + kt`, returns `[A]` pub fn concentration_second_order(c0: f64, rate_constant: f64, time: f64) -> f64 { assert!(c0 > 0.0, "initial concentration must be positive"); 1.0 / (1.0 / c0 + rate_constant * time) } -/// General rate law: r = k × Π([Ci]^ni) +/// General rate law: `r = k × Π([Ci]^ni)` pub fn reaction_rate(k: f64, concentrations: &[f64], orders: &[f64]) -> f64 { assert_eq!( concentrations.len(), diff --git a/src/color_science.rs b/src/color_science.rs index d792f3a..987569e 100644 --- a/src/color_science.rs +++ b/src/color_science.rs @@ -164,7 +164,7 @@ pub fn blackbody_to_rgb(temperature_k: f64) -> (f64, f64, f64) { (r / CHANNEL_MAX, g / CHANNEL_MAX, b / CHANNEL_MAX) } -/// Convert linear RGB [0,1] to HSV. H in [0,360), S and V in [0,1]. +/// Convert linear RGB `[0,1]` to HSV. H in `[0,360)`, S and V in `[0,1]`. #[must_use] pub fn rgb_to_hsv(r: f64, g: f64, b: f64) -> (f64, f64, f64) { let max = r.max(g).max(b); @@ -189,7 +189,7 @@ pub fn rgb_to_hsv(r: f64, g: f64, b: f64) -> (f64, f64, f64) { (h, s, v) } -/// Convert HSV to linear RGB. H in [0,360), S and V in [0,1]. +/// Convert HSV to linear RGB. H in `[0,360)`, S and V in `[0,1]`. #[must_use] pub fn hsv_to_rgb(h: f64, s: f64, v: f64) -> (f64, f64, f64) { let c = v * s; @@ -214,7 +214,7 @@ pub fn hsv_to_rgb(h: f64, s: f64, v: f64) -> (f64, f64, f64) { (r1 + m, g1 + m, b1 + m) } -/// Convert linear RGB [0,1] to HSL. H in [0,360), S and L in [0,1]. +/// Convert linear RGB `[0,1]` to HSL. H in `[0,360)`, S and L in `[0,1]`. #[must_use] pub fn rgb_to_hsl(r: f64, g: f64, b: f64) -> (f64, f64, f64) { let max = r.max(g).max(b); @@ -243,7 +243,7 @@ pub fn rgb_to_hsl(r: f64, g: f64, b: f64) -> (f64, f64, f64) { (h, s, l) } -/// Convert HSL to linear RGB. H in [0,360), S and L in [0,1]. +/// Convert HSL to linear RGB. H in `[0,360)`, S and L in `[0,1]`. #[must_use] pub fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (f64, f64, f64) { let c = (1.0 - (2.0 * l - 1.0).abs()) * s; diff --git a/src/core/dual.rs b/src/core/dual.rs index b263c30..a010db8 100644 --- a/src/core/dual.rs +++ b/src/core/dual.rs @@ -168,7 +168,7 @@ pub fn gradient(f: impl Fn(&[Dual]) -> Dual, x: &[f64]) -> Vec { grad } -/// Jacobian matrix J[i][j] = ∂fᵢ/∂xⱼ of a vector-valued function. +/// Jacobian matrix `J[i][j] = ∂fᵢ/∂xⱼ` of a vector-valued function. /// /// # Panics /// Panics if `f` returns an empty vector or `x` is empty. diff --git a/src/dsp/windows.rs b/src/dsp/windows.rs index 5d531d4..d640b6d 100644 --- a/src/dsp/windows.rs +++ b/src/dsp/windows.rs @@ -282,25 +282,26 @@ pub fn kaiser_beta_for_attenuation(db: f64) -> f64 { // --- Pre-Part-3 generators (wrap `window` with the original symmetric // convention) --- -/// Generate a Hann window of length n: w[k] = 0.5·(1 - cos(2πk/(n-1))) +/// Generate a Hann window of length n: `w[k] = 0.5·(1 - cos(2πk/(n-1)))` #[must_use] pub fn hann_window(n: usize) -> Vec { window(WindowKind::Hann, n, false) } -/// Generate a Hamming window of length n: w[k] = 0.54 - 0.46·cos(2πk/(n-1)) +/// Generate a Hamming window of length n: `w[k] = 0.54 - 0.46·cos(2πk/(n-1))` #[must_use] pub fn hamming_window(n: usize) -> Vec { window(WindowKind::Hamming, n, false) } -/// Generate a Blackman window of length n: w[k] = 0.42 - 0.5·cos(2πk/(n-1)) + 0.08·cos(4πk/(n-1)) +/// Generate a Blackman window of length n: + /// `w[k] = 0.42 - 0.5·cos(2πk/(n-1)) + 0.08·cos(4πk/(n-1))` #[must_use] pub fn blackman_window(n: usize) -> Vec { window(WindowKind::Blackman, n, false) } -/// Generate a rectangular (uniform) window of length n: w[k] = 1 for all k +/// Generate a rectangular (uniform) window of length n: `w[k] = 1` for all k #[must_use] pub fn rectangular_window(n: usize) -> Vec { window(WindowKind::Rect, n, false) diff --git a/src/fractals/attractors.rs b/src/fractals/attractors.rs index 4eb2abe..b015c86 100644 --- a/src/fractals/attractors.rs +++ b/src/fractals/attractors.rs @@ -728,7 +728,7 @@ pub fn delay_embedding(series: &[f64], dim: usize, delay: usize) -> Vec .collect() } -/// Recurrence plot: R[i,j] = true when embedded states i and j are +/// Recurrence plot: `R[i,j]` is true when embedded states i and j are /// within `eps` (row-major over the n embedded points). #[must_use] pub fn recurrence_plot(series: &[f64], embed_dim: usize, delay: usize, eps: f64) -> Vec { diff --git a/src/fractals/automata.rs b/src/fractals/automata.rs index 2bb5ca2..368a9a5 100644 --- a/src/fractals/automata.rs +++ b/src/fractals/automata.rs @@ -633,7 +633,7 @@ pub struct Turmite { pub pos: (usize, usize), pub dir: u8, pub state: u8, - /// Indexed by [state][color]. + /// Indexed by `[state][color]`. pub table: Vec>, } @@ -830,7 +830,7 @@ pub struct LifeLike3D { } impl LifeLike3D { - /// Parses "B/S" where counts are comma-free + /// Parses `"B/S"` where counts are comma-free /// digit runs; multi-digit counts (10-26) are written with /// parentheses, e.g. "B(10)(11)/S(12)". /// diff --git a/src/fractals/lsystem.rs b/src/fractals/lsystem.rs index 0ccb72c..451cfa6 100644 --- a/src/fractals/lsystem.rs +++ b/src/fractals/lsystem.rs @@ -516,7 +516,7 @@ pub mod presets { LSystem::new("F", 25.7).rule('F', "F[+F]F[-F]F") } - /// ABOP fig 1.24b: F → F[+F]F[−F][F] at 20°. + /// ABOP fig 1.24b: `F → F[+F]F[−F][F]` at 20°. #[must_use] pub fn plant_b() -> LSystem { LSystem::new("F", 20.0).rule('F', "F[+F]F[-F][F]") @@ -534,13 +534,13 @@ pub mod presets { LSystem::new("X", 20.0).rule('X', "F[+X]F[-X]+X").rule('F', "FF") } - /// ABOP fig 1.24e: X → F[+X][−X]FX, F → FF at 25.7°. + /// ABOP fig 1.24e: `X → F[+X][−X]FX`, `F → FF` at 25.7°. #[must_use] pub fn plant_e() -> LSystem { LSystem::new("X", 25.7).rule('X', "F[+X][-X]FX").rule('F', "FF") } - /// ABOP fig 1.24f: X → F−[[X]+X]+F[+FX]−X, F → FF at 22.5°. + /// ABOP fig 1.24f: `X → F−[[X]+X]+F[+FX]−X`, `F → FF` at 22.5°. #[must_use] pub fn plant_f() -> LSystem { LSystem::new("X", 22.5).rule('X', "F-[[X]+X]+F[+FX]-X").rule('F', "FF") diff --git a/src/learn/cluster.rs b/src/learn/cluster.rs index 9f20a97..037e20e 100644 --- a/src/learn/cluster.rs +++ b/src/learn/cluster.rs @@ -176,7 +176,7 @@ pub fn kmeans_pp_init( /// common enough to matter. const RESTARTS: usize = 10; -/// Lloyd's algorithm, restarted [`RESTARTS`] times from independent +/// Lloyd's algorithm, restarted `RESTARTS` (10) times from independent /// k-means++ starts, keeping the run with the lowest inertia. /// /// The result carries the winning run's inertia history, which is diff --git a/src/linalg/matrix.rs b/src/linalg/matrix.rs index eee524d..32892ca 100644 --- a/src/linalg/matrix.rs +++ b/src/linalg/matrix.rs @@ -102,7 +102,7 @@ impl Matrix { &self.data[r * self.cols..(r + 1) * self.cols] } - /// Transpose: B[c][r] = A[r][c]. + /// Transpose: `B[c][r] = A[r][c]`. #[must_use] pub fn transpose(&self) -> Self { let mut out = Self::zeros(self.cols, self.rows); diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs index aa8ba90..6fabc7e 100644 --- a/src/linalg/mod.rs +++ b/src/linalg/mod.rs @@ -63,7 +63,7 @@ impl Mat3 { + d[0][2] * (d[1][0] * d[2][1] - d[1][1] * d[2][0]) } - /// Returns the transpose of this matrix: A^T[i][j] = A[j][i]. + /// Returns the transpose of this matrix: `A^T[i][j] = A[j][i]`. #[must_use] pub fn transpose(&self) -> Self { let d = &self.data; diff --git a/src/linalg/tridiagonal.rs b/src/linalg/tridiagonal.rs index 2d43b62..7963eec 100644 --- a/src/linalg/tridiagonal.rs +++ b/src/linalg/tridiagonal.rs @@ -1,7 +1,7 @@ //! Tridiagonal linear solve (Thomas algorithm). //! //! Reference: Press et al., *Numerical Recipes*, §2.4. Solves -//! sub[i-1]·x[i-1] + diag[i]·x[i] + sup[i]·x[i+1] = rhs[i] in O(n). +//! `sub[i-1]·x[i-1] + diag[i]·x[i] + sup[i]·x[i+1] = rhs[i]` in O(n). use crate::error::SolveError; diff --git a/src/manifold/clifford.rs b/src/manifold/clifford.rs index 6cd16cb..25b63f9 100644 --- a/src/manifold/clifford.rs +++ b/src/manifold/clifford.rs @@ -167,7 +167,7 @@ impl Multivector { out } - /// Scalar product _0. + /// Scalar product `_0`. #[must_use] pub fn scalar_product(&self, o: &Self) -> f64 { let mut s = 0.0; @@ -398,7 +398,7 @@ impl Multivector { out } - /// Logarithm of a rotor R = _0 + _2 (bivector generator). + /// Logarithm of a rotor `R = _0 + _2` (bivector generator). #[must_use] pub fn log(&self) -> Option { let s = self.coeffs[0]; @@ -677,7 +677,7 @@ impl Multivector { } } -/// Basis-blade multiplication table: table[a][b] = (sign, result mask). +/// Basis-blade multiplication table: `table[a][b] = (sign, result mask)`. #[must_use] pub fn cayley_table(p: usize, q: usize, r: usize) -> Vec> { let n = 1 << (p + q + r); diff --git a/src/manifold/metric.rs b/src/manifold/metric.rs index 5f0130e..32fc26f 100644 --- a/src/manifold/metric.rs +++ b/src/manifold/metric.rs @@ -741,7 +741,7 @@ impl Metric { } /// Max residual of the first Bianchi identity - /// R_{i[jkl]} : R_ijkl + R_iklj + R_iljk = 0, normalized by the largest + /// `R_{i[jkl]}` : `R_ijkl + R_iklj + R_iljk = 0`, normalized by the largest /// Riemann component. #[must_use] pub fn bianchi_identity_residual(&self, p: &VecN) -> f64 { diff --git a/src/manifold/polytope4.rs b/src/manifold/polytope4.rs index 4606c61..7b6708e 100644 --- a/src/manifold/polytope4.rs +++ b/src/manifold/polytope4.rs @@ -1736,7 +1736,7 @@ pub fn kissing_number_known(n: usize) -> Option { // High-dimensional phenomena // --------------------------------------------------------------------------- -/// (n-1)-volume of the slice of the unit n-cube [0,1]^n by the hyperplane +/// (n-1)-volume of the slice of the unit n-cube `[0,1]^n` by the hyperplane /// sum(x) = s, times sqrt(n) (the Irwin-Hall density scaled to a volume). #[must_use] pub fn hypercube_slicing_volume(n: usize, s: f64) -> f64 { diff --git a/src/mesh/isosurface.rs b/src/mesh/isosurface.rs index e9dc296..d8ca120 100644 --- a/src/mesh/isosurface.rs +++ b/src/mesh/isosurface.rs @@ -456,7 +456,7 @@ fn mc_table() -> &'static Vec> { /// Extracts the isosurface by marching cubes (Lorensen & Cline 1987; /// the case table is generated by face-consistent cycle construction, -/// see [`mc_table`]). Output vertices are shared across cells (keyed +/// see `mc_table`). Output vertices are shared across cells (keyed /// by grid edge), so the mesh is watertight wherever the surface does /// not leave the grid. #[must_use] diff --git a/src/numerical/interpolate.rs b/src/numerical/interpolate.rs index 90514f7..348d4db 100644 --- a/src/numerical/interpolate.rs +++ b/src/numerical/interpolate.rs @@ -304,7 +304,7 @@ fn catmull_rom_weights(u: f64) -> [f64; 4] { } /// Uniform Catmull-Rom spline through `points`, parameterized so that -/// t = i lands exactly on points[i] (t ∈ [0, n−1]; endpoints use +/// `t = i` lands exactly on `points[i]` (`t ∈ [0, n−1]`; endpoints use /// duplicated boundary points). /// /// # Panics diff --git a/src/numerical/ode/explicit.rs b/src/numerical/ode/explicit.rs index 93c8657..da36e32 100644 --- a/src/numerical/ode/explicit.rs +++ b/src/numerical/ode/explicit.rs @@ -37,7 +37,7 @@ pub fn rk4_solve( } /// Single RK4 step for a system of ODEs (vector state). -/// f(t, y) returns a Vec of derivatives matching the length of y. +/// `f(t, y)` returns a `Vec` of derivatives matching the length of `y`. #[must_use] pub fn rk4_step_vec( f: &dyn Fn(f64, &[f64]) -> Vec, diff --git a/src/quantum/circuit.rs b/src/quantum/circuit.rs index 39cbc3b..d379db0 100644 --- a/src/quantum/circuit.rs +++ b/src/quantum/circuit.rs @@ -57,7 +57,7 @@ impl QState { /// The all-zeros computational basis state. /// /// # Errors - /// Returns an error for zero qubits or more than [`MAX_QUBITS`]. + /// Returns an error for zero qubits or more than `MAX_QUBITS` (26). pub fn zero(n: usize) -> Result { Self::basis(n, 0) } diff --git a/src/signal_processing/mod.rs b/src/signal_processing/mod.rs index 4e1667c..8f7e7d0 100644 --- a/src/signal_processing/mod.rs +++ b/src/signal_processing/mod.rs @@ -22,7 +22,7 @@ const LCG_INCREMENT: u64 = 1442695040888963407; // --- Convolution & Correlation --- -/// Linear convolution of signal with kernel: y[n] = Σ s[i]·k[n-i] +/// Linear convolution of signal with kernel: `y[n] = Σ s[i]·k[n-i]` #[must_use] pub fn convolve(signal: &[f64], kernel: &[f64]) -> Vec { if signal.is_empty() || kernel.is_empty() { @@ -111,7 +111,7 @@ pub fn moving_average(signal: &[f64], window_size: usize) -> Vec { output } -/// Exponential moving average filter: y[n] = α·x[n] + (1-α)·y[n-1] +/// Exponential moving average filter: `y[n] = α·x[n] + (1-α)·y[n-1]` #[must_use] pub fn exponential_moving_average(signal: &[f64], alpha: f64) -> Vec { if signal.is_empty() { @@ -158,7 +158,7 @@ pub fn median_filter(signal: &[f64], window_size: usize) -> Vec { // --- Signal Generation --- -/// Generate a sine wave: x[n] = A·sin(2πf·n/fs) for n samples over given duration +/// Generate a sine wave: `x[n] = A·sin(2πf·n/fs)` for n samples over given duration #[must_use] pub fn sine_wave(frequency: f64, sample_rate: f64, duration: f64, amplitude: f64) -> Vec { assert!(sample_rate > 0.0, "sample rate must be positive"); diff --git a/src/sim/heat_sim.rs b/src/sim/heat_sim.rs index 171cca1..9440cd3 100644 --- a/src/sim/heat_sim.rs +++ b/src/sim/heat_sim.rs @@ -40,9 +40,9 @@ pub struct HeatConduction2D { pub nx: usize, /// Number of grid points in y. pub ny: usize, - /// Grid spacing in x [m]. + /// Grid spacing in x (m). pub dx: f64, - /// Grid spacing in y [m]. + /// Grid spacing in y (m). pub dy: f64, /// Thermal diffusivity α = k/(ρcₚ) [m²/s]. pub diffusivity: f64, @@ -307,7 +307,7 @@ pub struct ConvectionDiffusion1D { pub field: Vec, /// Number of grid points. pub nx: usize, - /// Grid spacing [m]. + /// Grid spacing (m). pub dx: f64, /// Advection velocity [m/s]. pub velocity: f64, diff --git a/src/sim/wave_sim.rs b/src/sim/wave_sim.rs index d335339..f0c67e2 100644 --- a/src/sim/wave_sim.rs +++ b/src/sim/wave_sim.rs @@ -84,7 +84,7 @@ impl WaveEquation1D { } /// Advance one time step using the leapfrog scheme with fixed endpoints - /// u[0] = u[nx-1] = 0. + /// `u[0] = u[nx-1] = 0`. pub fn step(&mut self, dt: f64) { let r2 = (self.wave_speed * dt / self.dx).powi(2); @@ -105,9 +105,13 @@ impl WaveEquation1D { /// /// Interior update is identical to `step`. At the boundaries the outgoing /// characteristic is approximated: - /// u[0]^{n+1} = u[1]^n + (r - 1)/(r + 1) (u[1]^{n+1} - u[0]^n) - /// u[nx-1]^{n+1} = u[nx-2]^n + (r - 1)/(r + 1) (u[nx-2]^{n+1} - u[nx-1]^n) - /// where r = c dt / dx. + /// + /// ```text + /// u[0]^{n+1} = u[1]^n + (r - 1)/(r + 1) (u[1]^{n+1} - u[0]^n) + /// u[nx-1]^{n+1} = u[nx-2]^n + (r - 1)/(r + 1) (u[nx-2]^{n+1} - u[nx-1]^n) + /// ``` + /// + /// where `r = c dt / dx`. /// /// These conditions absorb normally-incident waves perfectly (first order /// in angle of incidence for oblique waves). diff --git a/src/statistics/fourier.rs b/src/statistics/fourier.rs index 500dc84..8cab80a 100644 --- a/src/statistics/fourier.rs +++ b/src/statistics/fourier.rs @@ -7,21 +7,21 @@ use crate::fractals::Complex; use crate::transforms::fft::{fft_any, ifft_any, rfft}; -/// Discrete Fourier Transform: X[k] = Σ x[n]·e^(-j2πkn/N), returns (real, imag) pairs +/// Discrete Fourier Transform: `X[k] = Σ x[n]·e^(-j2πkn/N)`, returns (real, imag) pairs #[must_use] pub fn dft(signal: &[f64]) -> Vec<(f64, f64)> { let buf: Vec = signal.iter().map(|&x| Complex::new(x, 0.0)).collect(); fft_any(&buf).iter().map(|c| (c.re, c.im)).collect() } -/// Inverse DFT: x[n] = (1/N)·Σ X[k]·e^(j2πkn/N) +/// Inverse DFT: `x[n] = (1/N)·Σ X[k]·e^(j2πkn/N)` #[must_use] pub fn inverse_dft(spectrum: &[(f64, f64)]) -> Vec { let buf: Vec = spectrum.iter().map(|&(re, im)| Complex::new(re, im)).collect(); ifft_any(&buf).iter().map(|c| c.re).collect() } -/// Power spectrum: |X[k]|² = Re² + Im² for each frequency bin. +/// Power spectrum: `|X[k]|² = Re² + Im²` for each frequency bin. /// /// Uses the real FFT and reconstructs the upper half from conjugate /// symmetry. Output length always equals `signal.len()`. diff --git a/src/transforms/fft.rs b/src/transforms/fft.rs index 1451e7c..7fb8d05 100644 --- a/src/transforms/fft.rs +++ b/src/transforms/fft.rs @@ -62,7 +62,7 @@ fn fft_in_place(buf: &mut [Complex], sign: f64) { } } -/// Forward FFT: X[k] = Σ x[n]·e^(−j2πkn/N). +/// Forward FFT: `X[k] = Σ x[n]·e^(−j2πkn/N)`. /// /// # Panics /// Panics unless `input.len()` is a power of two. Use [`fft_any`] for @@ -75,7 +75,7 @@ pub fn fft(input: &[Complex]) -> Vec { buf } -/// Inverse FFT: x[n] = (1/N)·Σ X[k]·e^(j2πkn/N). +/// Inverse FFT: `x[n] = (1/N)·Σ X[k]·e^(j2πkn/N)`. /// /// # Panics /// Panics unless `input.len()` is a power of two. Use [`ifft_any`] for @@ -210,7 +210,7 @@ pub fn ifft_any(x: &[Complex]) -> Vec { } /// FFT of a real signal, returning the n/2 + 1 non-redundant bins -/// (bins k > n/2 satisfy X[n−k] = X[k]*). Any length. +/// (bins `k > n/2` satisfy `X[n−k] = X[k]*`). Any length. #[must_use] pub fn rfft(input: &[f64]) -> Vec { let n = input.len(); From 5b58e7c66cc84c1979cb5da1f1b836b98fc77920 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 02:37:08 +0000 Subject: [PATCH 3/5] docs: document every module, and generate a map of what lives where Sixty-three of the 296 source files had no `//!` module documentation at all, so they rendered as blank pages. Among them was src/lib.rs, which is the landing page of the whole generated documentation -- the first thing anyone opening the docs sees was empty. lib.rs now carries an orientation page: what the library is for and the principle the API shape follows, a table routing the 71 top-level modules by subject, and the three conventions a caller has to know (SI units and radians; f64 except where exactness is the point; and that monte_carlo::Rng is an LCG whose low bits are short-period, so Rng::below is the one to use). The other 62 are documented from what they actually contain, and each says something a reader would otherwise have to work out: that Sabine's reverberation formula never reaches zero however absorptive the room and Eyring's does; that metals yield on the deviatoric stress alone, which is why von Mises ignores hydrostatic pressure; that the chirp mass is the combination an inspiral waveform actually determines; that Verlet is used in the cloth solver because velocity is inferred from positions rather than integrated; that the buildup factor matters because ignoring it underestimates dose behind a thick shield; that L4 and L5 are stable below a mass ratio of about 1/24.96, which is why the Trojans stay put. Cross-references point at the fuller treatment where one exists -- from sim/ to cfd/ and fem/, from gravitation to astrophysics and general relativity, from optics to photonics. Three links needed disambiguating: `cholesky`, `svd` and `transforms::fft` are each both a module and a function, so they are now written `mod@`. docs/MODULE_MAP.md is the "what exists where" document: a size-annotated tree of all 295 modules, tables by subject area, and a row per module with its line count, public function, method and type counts, and summary. It is generated by tools/gen_module_map.py rather than written, because a hand-maintained map of 295 modules is stale the day after it is written. Every figure is parsed from the sources -- counts skip anything inside #[cfg(test)] by tracking brace depth, and summaries are the first sentence of each module's //! block. Getting the totals to reconcile took two corrections. `verification` is declared `mod` rather than `pub mod`, so counting it gave 72 top-level modules against the README's 71; it is now excluded and labelled private. And excluding lib.rs from the line total made the map disagree with `wc -l` over src/ by exactly lib.rs's length; the crate root is now counted, and the map's 266,122 matches `wc -l` exactly. Two CI gates keep all of this honest, both tested in each direction before committing: - `gen_module_map.py --check` re-derives the map and fails if the committed copy differs. - `check_module_docs.py` fails if any source file lacks a `//!` summary. Nothing else catches this: rustdoc's missing_docs covers items, not modules, so an undocumented module is silent. README gains the map in Further reading, and its line count is updated for the documentation just added. 296 of 296 files documented. cargo doc --no-deps: 0 warnings under -D warnings. 4,193 lib tests, 577 property tests, 2 doc tests, clippy clean under --all-targets -D warnings, nightly-2025-11-21 clean. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01VUkMGnfUbaopkUJLWeYCYi --- .github/workflows/ci.yml | 13 + README.md | 13 +- docs/MODULE_MAP.md | 773 ++++++++++++++++++++++++ src/acoustics.rs | 13 + src/astrophysics/collisions.rs | 11 + src/astrophysics/gravitational_waves.rs | 11 + src/astrophysics/habitable_zone.rs | 11 + src/astrophysics/lagrange.rs | 11 + src/astrophysics/magnetosphere.rs | 10 + src/astrophysics/mod.rs | 20 + src/astrophysics/nbody.rs | 14 + src/astrophysics/orbital_elements.rs | 15 + src/astrophysics/tidal.rs | 11 + src/atmosphere.rs | 12 + src/chemistry.rs | 11 + src/classical.rs | 14 + src/color_science.rs | 12 + src/continuum_mechanics.rs | 12 + src/control_systems/mod.rs | 14 + src/curves.rs | 11 + src/electromagnetism.rs | 14 + src/electronics.rs | 11 + src/fluid_instabilities.rs | 15 + src/fluids.rs | 16 + src/fractals/mod.rs | 16 + src/general_relativity.rs | 18 + src/geometry/mod.rs | 12 + src/geophysics.rs | 15 + src/gravitation.rs | 15 + src/information_theory.rs | 15 + src/lib.rs | 74 +++ src/linalg/mod.rs | 18 + src/magnetohydrodynamics.rs | 13 + src/materials/common.rs | 7 + src/materials/elements.rs | 11 + src/materials/fluids.rs | 10 + src/materials/gases.rs | 10 + src/materials/mod.rs | 13 + src/math.rs | 18 + src/monte_carlo/mod.rs | 16 + src/neutronics.rs | 16 + src/nonlinear.rs | 15 + src/nuclear.rs | 13 + src/optics.rs | 15 + src/optimization/mod.rs | 14 + src/particle_physics.rs | 16 + src/photonics.rs | 13 + src/plasma.rs | 17 + src/propulsion.rs | 15 + src/quaternion.rs | 14 + src/radiation.rs | 15 + src/relativity.rs | 14 + src/rf.rs | 15 + src/signal_processing/mod.rs | 15 + src/sim/cloth_sim.rs | 12 + src/sim/em_sim.rs | 15 + src/sim/fluid_sim.rs | 12 + src/sim/heat_sim.rs | 11 + src/sim/mod.rs | 15 + src/sim/rigid_body.rs | 16 + src/sim/wave_sim.rs | 11 + src/solid_mechanics.rs | 16 + src/spatial/octree.rs | 12 + src/thermodynamics.rs | 18 + src/trigonometry.rs | 14 + src/waves.rs | 18 + tools/check_module_docs.py | 60 ++ tools/gen_module_map.py | 265 ++++++++ 68 files changed, 2048 insertions(+), 3 deletions(-) create mode 100644 docs/MODULE_MAP.md create mode 100755 tools/check_module_docs.py create mode 100755 tools/gen_module_map.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c00aa92..27a29e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,6 +66,19 @@ jobs: env: RUSTDOCFLAGS: "-D warnings" + # docs/MODULE_MAP.md is generated from the source tree, so it can go + # stale the moment a module is added, renamed or resized. This + # re-derives it and fails if the committed copy differs, which is the + # only thing that keeps a generated file honest. + - name: Module map is current + run: python3 tools/gen_module_map.py --check + + # Every module carries a //! summary, and the map is built from those + # first sentences -- a module without one would appear in the map as a + # blank row. + - name: Every module is documented + run: python3 tools/check_module_docs.py + coverage: name: Coverage runs-on: ubuntu-latest diff --git a/README.md b/README.md index 154ef53..69d89f1 100644 --- a/README.md +++ b/README.md @@ -31,15 +31,15 @@ assertion uses `==`. |---|---| | **Public functions and methods** | 6,365 (3,949 free functions, 2,416 methods) | | **Public types** | 429 structs, enums and traits | -| **Top-level modules** | 71, across 296 source files | -| **Source** | 265,190 lines of Rust | +| **Top-level modules** | 71 public, across 296 source files — see the [module map](docs/MODULE_MAP.md) | +| **Source** | 266,122 lines of Rust | | **Unit tests** | 4,193 | | **Property tests** | 577, across 49 files | | **Line coverage** | 97.89% (174,685 lines, 3,681 uncovered) | | **Function coverage** | 99.33% (20,200 functions, 136 uncovered) | | **Formal verification** | 20 Kani harnesses (13 in CI, 7 behind `kani-slow`) | | **Undefined behaviour** | Miri-clean; the crate contains no `unsafe` | -| **API documentation** | `cargo doc` builds warning-free; CI denies rustdoc warnings | +| **API documentation** | every module carries a `//!` summary; `cargo doc` is warning-free, and CI denies rustdoc warnings | | **Dependencies** | none — `Cargo.lock` holds exactly one package | | **Edition** | 2021, `f64` throughout | @@ -909,9 +909,16 @@ strict Clippy pass. ## Further reading +- [`docs/MODULE_MAP.md`](docs/MODULE_MAP.md) — a generated map of every one of + the 295 modules: a size-annotated tree, tables by subject area, and a row per + module with its line count, public item counts and summary. Produced by + [`tools/gen_module_map.py`](tools/gen_module_map.py) from the sources, so + nothing in it is transcribed; CI fails if it is out of date. - [`docs/ROADMAP_PART4.md`](docs/ROADMAP_PART4.md) — the specification the most recent tranche of work was built against, with the function signatures and the property each module had to satisfy. Marked complete. +- `cargo doc --no-deps --open` — the API documentation. The crate root carries + an orientation page; every module has a summary. ## License diff --git a/docs/MODULE_MAP.md b/docs/MODULE_MAP.md new file mode 100644 index 0000000..7ec9dc7 --- /dev/null +++ b/docs/MODULE_MAP.md @@ -0,0 +1,773 @@ +# Module map + +**Generated file — do not edit.** Produced by +[`tools/gen_module_map.py`](../tools/gen_module_map.py) from the source +tree; CI fails if it is out of date. Regenerate with: + +```bash +python3 tools/gen_module_map.py +``` + +Every figure below is parsed from the sources. Summaries are the first +sentence of each module's `//!` documentation. Public-item counts exclude +anything inside `#[cfg(test)]`. + +**295 modules** across **71 public top-level modules**, **266,122 lines** in **296 files** (the modules plus the crate root `src/lib.rs`), **3,949 public functions** and **2,416 public methods**, **429 public types**. + +`verification` is compiled and tested but declared `mod` rather than `pub mod`, so it is not part of the public API and is excluded from the module count above. + +## Tree + +``` +src/ lines +├── lib.rs 159 (crate root) +├── acoustics.rs 576 +├── astrophysics/ 6,122 +│ ├── collisions.rs 318 +│ ├── coords.rs 1,051 +│ ├── gravitational_waves.rs 233 +│ ├── habitable_zone.rs 127 +│ ├── kepler.rs 913 +│ ├── lagrange.rs 195 +│ ├── lambert.rs 553 +│ ├── magnetosphere.rs 405 +│ ├── maneuvers.rs 618 +│ ├── nbody.rs 403 +│ ├── orbital_elements.rs 660 +│ ├── tidal.rs 209 +│ └── time_systems.rs 399 +├── atmosphere.rs 409 +├── audio/ 10,716 +│ ├── analysis.rs 1,950 +│ ├── effects.rs 1,669 +│ ├── envelope.rs 604 +│ ├── oscillators.rs 939 +│ ├── physical.rs 1,331 +│ ├── spatial.rs 1,132 +│ ├── synthesis.rs 1,260 +│ ├── tuning.rs 517 +│ ├── vocoder.rs 862 +│ └── wav.rs 370 +├── biophysics/ 11,396 +│ ├── epidemiology.rs 1,924 +│ ├── neuro.rs 2,519 +│ ├── phylo.rs 1,979 +│ ├── population.rs 2,356 +│ └── seq_align.rs 2,150 +├── cfd/ 16,732 +│ ├── advection.rs 869 +│ ├── boundary_layer.rs 579 +│ ├── grid.rs 685 +│ ├── lbm.rs 1,142 +│ ├── level_set.rs 2,066 +│ ├── multiphase.rs 778 +│ ├── porous.rs 695 +│ ├── potential_flow.rs 1,523 +│ ├── riemann.rs 1,564 +│ ├── shallow_water.rs 938 +│ ├── sph.rs 1,154 +│ ├── stable_fluids.rs 1,776 +│ ├── turbulence.rs 1,537 +│ └── vortex.rs 1,304 +├── chemistry.rs 334 +├── classical.rs 786 +├── codes/ 8,243 +│ ├── block.rs 1,579 +│ ├── checksum.rs 880 +│ ├── compression.rs 1,420 +│ ├── convolutional.rs 1,335 +│ ├── crypto_math.rs 1,501 +│ └── reed_solomon.rs 1,519 +├── color_science.rs 788 +├── continuum_mechanics.rs 508 +├── control_systems/ 744 +│ └── kalman.rs 291 +├── core/ 984 +│ ├── compensated.rs 117 +│ ├── dual.rs 410 +│ └── interval.rs 451 +├── curves.rs 500 +├── discrete/ 9,047 +│ ├── combinatorics.rs 2,827 +│ ├── disjoint_set.rs 352 +│ ├── number_theory.rs 2,345 +│ ├── partitions.rs 784 +│ ├── primes.rs 1,238 +│ └── sequences.rs 1,491 +├── dsp/ 3,928 +│ ├── fir.rs 996 +│ ├── iir.rs 1,666 +│ ├── phase.rs 270 +│ ├── resample.rs 513 +│ └── windows.rs 446 +├── electromagnetism.rs 822 +├── electronics.rs 445 +├── error.rs 103 +├── exact/ 9,249 +│ ├── bigfloat.rs 2,000 +│ ├── bigint.rs 1,463 +│ ├── contfrac.rs 524 +│ ├── polynomial.rs 2,286 +│ ├── rational.rs 1,079 +│ └── symbolic.rs 1,882 +├── fem/ 4,725 +│ ├── fdtd.rs 1,154 +│ ├── fem1d.rs 952 +│ ├── fem2d.rs 1,901 +│ └── spectral_pde.rs 693 +├── fields.rs 211 +├── finance/ 5,119 +│ ├── options.rs 2,090 +│ ├── portfolio.rs 865 +│ ├── rates.rs 1,388 +│ └── risk.rs 751 +├── fluid_instabilities.rs 451 +├── fluids.rs 709 +├── fractals/ 9,454 +│ ├── attractors.rs 1,104 +│ ├── automata.rs 2,881 +│ ├── escape_time.rs 1,044 +│ ├── ifs.rs 924 +│ ├── lsystem.rs 1,056 +│ └── noise.rs 1,803 +├── general_relativity.rs 427 +├── geometry/ 1,522 +│ ├── delaunay.rs 262 +│ ├── geodesy.rs 342 +│ ├── hull.rs 310 +│ └── mesh.rs 155 +├── geophysics.rs 483 +├── graph/ 13,493 +│ ├── coloring.rs 1,649 +│ ├── core.rs 2,862 +│ ├── flow.rs 1,519 +│ ├── layout.rs 1,935 +│ ├── matching.rs 1,399 +│ ├── paths.rs 2,133 +│ └── spectral.rs 1,984 +├── gravitation.rs 236 +├── information_theory.rs 367 +├── learn/ 4,213 +│ ├── cluster.rs 1,295 +│ ├── gp.rs 839 +│ ├── nn.rs 1,163 +│ └── tree.rs 900 +├── linalg/ 3,295 +│ ├── cholesky.rs 147 +│ ├── eigen.rs 492 +│ ├── lu.rs 223 +│ ├── matrix.rs 344 +│ ├── qr.rs 205 +│ ├── sparse.rs 360 +│ ├── svd.rs 321 +│ └── tridiagonal.rs 233 +├── magnetohydrodynamics.rs 359 +├── manifold/ 20,431 +│ ├── clifford.rs 2,830 +│ ├── dec.rs 1,618 +│ ├── embedding.rs 1,624 +│ ├── geodesic.rs 1,422 +│ ├── hyperbolic.rs 1,710 +│ ├── lie.rs 3,225 +│ ├── metric.rs 1,382 +│ ├── polytope4.rs 2,142 +│ ├── spacetime.rs 1,556 +│ ├── spherical.rs 1,671 +│ └── vecn.rs 1,146 +├── materials/ 3,548 +│ ├── common.rs 349 +│ ├── elements.rs 2,632 +│ ├── fluids.rs 308 +│ └── gases.rs 242 +├── math.rs 621 +├── mesh/ 6,516 +│ ├── analyze.rs 1,201 +│ ├── generate.rs 881 +│ ├── isosurface.rs 1,052 +│ ├── parameterize.rs 652 +│ ├── subdivide.rs 616 +│ └── surfaces.rs 1,043 +├── monte_carlo/ 982 +│ └── quasi.rs 282 +├── neutronics.rs 409 +├── nonlinear.rs 432 +├── nuclear.rs 303 +├── numerical/ 2,563 +│ ├── bvp.rs 201 +│ ├── integrate.rs 270 +│ ├── interpolate.rs 648 +│ ├── ode.rs 11 +│ ├── adaptive.rs 309 +│ ├── explicit.rs 63 +│ ├── implicit.rs 232 +│ ├── symplectic.rs 125 +│ └── roots.rs 280 +├── optics.rs 348 +├── optimization/ 15,189 +│ ├── convex.rs 2,410 +│ ├── game_theory.rs 3,326 +│ ├── integer.rs 2,694 +│ ├── least_squares.rs 371 +│ ├── lp.rs 2,600 +│ ├── metaheuristics.rs 1,577 +│ └── network.rs 1,383 +├── particle_physics.rs 402 +├── patterns/ 12,404 +│ ├── aperiodic.rs 1,807 +│ ├── knots.rs 831 +│ ├── packing.rs 1,130 +│ ├── phyllotaxis.rs 490 +│ ├── polygon_ops.rs 2,022 +│ ├── polyhedra.rs 2,032 +│ ├── sampling.rs 1,220 +│ ├── space_filling.rs 755 +│ ├── symmetry.rs 996 +│ └── tilings.rs 1,107 +├── photonics.rs 485 +├── plasma.rs 297 +├── propulsion.rs 361 +├── quantum/ 11,941 +│ ├── algorithms.rs 1,707 +│ ├── circuit.rs 2,460 +│ ├── schrodinger.rs 2,607 +│ ├── solid_state.rs 1,685 +│ ├── spin.rs 1,703 +│ └── wavefunction.rs 1,368 +├── quaternion.rs 562 +├── radiation.rs 335 +├── relativity.rs 302 +├── resonance/ 4,275 +│ ├── cavity.rs 931 +│ ├── coupled.rs 828 +│ ├── nonlinear.rs 704 +│ ├── oscillator.rs 636 +│ └── structural.rs 1,134 +├── rf.rs 440 +├── signal_processing.rs 708 +├── sim/ 4,927 +│ ├── cloth_sim.rs 970 +│ ├── em_sim.rs 638 +│ ├── fluid_sim.rs 1,161 +│ ├── heat_sim.rs 774 +│ ├── rigid_body.rs 681 +│ └── wave_sim.rs 682 +├── solid_mechanics.rs 380 +├── spatial/ 7,020 +│ ├── bvh.rs 513 +│ ├── contain.rs 527 +│ ├── distance.rs 495 +│ ├── frame.rs 215 +│ ├── intersect.rs 908 +│ ├── kdtree.rs 510 +│ ├── mat4.rs 504 +│ ├── octree.rs 342 +│ ├── primitives.rs 1,128 +│ ├── projective.rs 447 +│ ├── quadtree.rs 359 +│ ├── sdf.rs 731 +│ └── transform2d.rs 308 +├── special/ 1,955 +│ ├── bessel.rs 528 +│ ├── beta.rs 145 +│ ├── elliptic.rs 333 +│ ├── erf.rs 335 +│ ├── expint.rs 126 +│ ├── gamma.rs 219 +│ └── legendre.rs 243 +├── statistical_mechanics/ 9,687 +│ ├── ising.rs 2,004 +│ ├── kinetics.rs 2,806 +│ ├── lattice_models.rs 1,199 +│ └── md.rs 3,237 +├── statistics/ 2,125 +│ ├── descriptive.rs 114 +│ ├── distributions.rs 1,013 +│ ├── fourier.rs 56 +│ ├── inference.rs 415 +│ └── resampling.rs 239 +├── stochastic/ 15,707 +│ ├── extreme.rs 1,852 +│ ├── hmm.rs 1,451 +│ ├── markov.rs 1,952 +│ ├── point_process.rs 1,373 +│ ├── queueing.rs 1,933 +│ ├── rmt.rs 1,217 +│ ├── sde.rs 1,939 +│ └── timeseries.rs 3,979 +├── thermodynamics.rs 808 +├── transforms/ 6,353 +│ ├── dct.rs 489 +│ ├── fft.rs 962 +│ ├── hilbert.rs 570 +│ ├── laplace.rs 435 +│ ├── radon.rs 619 +│ ├── spectral.rs 808 +│ ├── stft.rs 695 +│ ├── wavelet.rs 1,022 +│ └── wavelet_tables.rs 699 +├── trigonometry.rs 493 +├── units/ 2,785 +│ ├── dimensional.rs 974 +│ └── quantity.rs 998 +├── vector_calculus.rs 1,195 +├── verification/ 394 (private) +│ ├── core.rs 27 +│ ├── linalg.rs 27 +│ ├── physics.rs 236 +│ └── spatial.rs 41 +└── waves.rs 784 +``` + +## By area + +### Numeric foundations + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`core`** | 984 | 34 | 2 | Pure numeric building blocks: compensated summation, forward-mode automatic differentiation, and interval arithmetic. | +| **`math`** | 621 | 23 | 2 | Vectors and the crate's table of physical constants. | +| **`linalg`** | 3,295 | 74 | 8 | Dense and sparse linear algebra. | +| **`numerical`** | 2,563 | 43 | 4 | Numerical methods: quadrature, root finding, ODE solvers, and interpolation. | +| **`special`** | 1,955 | 34 | 0 | Special functions: error function family, gamma family, and beta functions. | +| **`error`** | 103 | 0 | 2 | Error types shared by the numerical solvers. | + +### Exact and symbolic + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`exact`** | 9,249 | 242 | 8 | Exact arithmetic: arbitrary-precision integers, exact rationals, arbitrary-precision binary floating point, polynomials, and continued fractions. | +| **`discrete`** | 9,047 | 181 | 1 | Discrete mathematics: primes and factorization, elementary and analytic number theory, counting and enumeration, integer partitions, integer… | +| **`graph`** | 13,493 | 182 | 3 | Graphs: representation and structure, shortest paths, network flow, matchings, spectral graph theory, colouring, and drawing. | +| **`codes`** | 8,243 | 225 | 16 | Error detection, error correction, compression, and the arithmetic cryptography is built on. | + +### Classical mechanics + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`classical`** | 786 | 54 | 0 | Newtonian mechanics: kinematics, dynamics, and the harmonic oscillator. | +| **`gravitation`** | 236 | 14 | 0 | Newtonian gravity and two-body orbits. | +| **`solid_mechanics`** | 380 | 23 | 0 | Strength of materials: stress, strain, elastic constants and beams. | +| **`continuum_mechanics`** | 508 | 19 | 0 | Stress and strain as tensors, and the yield criteria built on them. | +| **`resonance`** | 4,275 | 144 | 7 | Resonance and vibration: single and coupled oscillators, acoustic and electromagnetic cavities, nonlinear resonance, and structural dynamics. | +| **`geophysics`** | 483 | 22 | 0 | The solid Earth: gravity, seismology, and heat. | + +### Thermal and statistical + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`thermodynamics`** | 808 | 52 | 0 | Thermodynamics: gases, heat transfer, cycles and phase change. | +| **`statistical_mechanics`** | 9,687 | 161 | 9 | Statistical mechanics: the elementary relations here, with lattice models and Monte Carlo in submodules. | +| **`radiation`** | 335 | 14 | 0 | Thermal radiation and radiative transfer. | + +### Electromagnetism + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`electromagnetism`** | 822 | 60 | 0 | Classical electromagnetism, from Coulomb's law to radiating dipoles. | +| **`electronics`** | 445 | 17 | 0 | Semiconductor device physics. | +| **`rf`** | 440 | 28 | 0 | Radio-frequency engineering: links, lines and noise. | +| **`photonics`** | 485 | 26 | 0 | Laser beams, optical fibre, and interferometry. | +| **`plasma`** | 297 | 16 | 0 | Plasma parameters: the characteristic lengths, frequencies and speeds. | +| **`magnetohydrodynamics`** | 359 | 19 | 0 | Magnetohydrodynamics: a conducting fluid and the field frozen into it. | + +### Waves and signals + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`waves`** | 784 | 48 | 0 | Wave propagation: mechanical, acoustic and seismic. | +| **`optics`** | 348 | 20 | 0 | Geometric and wave optics. | +| **`acoustics`** | 576 | 31 | 0 | Room acoustics, psychoacoustic scales, and musical pitch. | +| **`transforms`** | 6,353 | 120 | 8 | Discrete transforms: FFT (any length), DCT/DST, STFT, wavelets, Hilbert, Laplace inversion, Radon, and spectral estimation. | +| **`dsp`** | 3,928 | 94 | 7 | Digital signal processing: window functions, FIR/IIR filter design, resampling, and phase utilities. | +| **`signal_processing`** | 708 | 16 | 0 | Time-domain signal operations and test waveforms. | +| **`audio`** | 10,716 | 350 | 49 | Audio synthesis, analysis, effects, and I/O. | + +### Fluids + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`fluids`** | 709 | 45 | 0 | Fluid statics and single-phase flow. | +| **`cfd`** | 16,732 | 477 | 50 | Computational fluid dynamics: staggered grids, advection schemes, and (in later modules) incompressible solvers, shallow water, SPH, LBM, level… | +| **`fluid_instabilities`** | 451 | 19 | 0 | When a fluid configuration stops being stable, and how fast it comes apart. | +| **`propulsion`** | 361 | 15 | 0 | Rocket propulsion and impulsive orbital transfers. | + +### Modern physics + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`relativity`** | 302 | 18 | 0 | Special relativity. | +| **`general_relativity`** | 427 | 23 | 0 | General relativity: black holes and cosmology. | +| **`quantum`** | 11,941 | 267 | 8 | Quantum mechanics: the elementary relations here, with the wavefunction machinery and the Schrodinger solvers in submodules. | +| **`particle_physics`** | 402 | 20 | 0 | Relativistic kinematics and scattering for particle collisions. | +| **`nuclear`** | 303 | 20 | 0 | Radioactive decay, nuclear binding, and dosimetry. | +| **`neutronics`** | 409 | 22 | 0 | Reactor physics: criticality, neutron diffusion, and shielding. | + +### Space + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`astrophysics`** | 6,122 | 111 | 9 | Astrodynamics and astrophysics. | + +### PDE solvers + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`fem`** | 4,725 | 52 | 6 | Finite elements, finite-difference time domain, and spectral methods. | +| **`sim`** | 4,927 | 101 | 15 | Time-stepping simulation engines. | +| **`fields`** | 211 | 10 | 2 | Uniform-grid scalar fields. | +| **`vector_calculus`** | 1,195 | 14 | 0 | Vector calculus operators and field theory for physics grids. | + +### Chemistry and life + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`chemistry`** | 334 | 18 | 0 | Reaction kinetics, chemical thermodynamics and electrochemistry. | +| **`biophysics`** | 11,396 | 177 | 7 | Biophysics: the elementary membrane, transport and mechanics relations here, with the population-scale models in submodules. | + +### Probability and data + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`statistics`** | 2,125 | 52 | 14 | Statistics: descriptive measures, probability distributions, and Fourier utilities. | +| **`stochastic`** | 15,707 | 253 | 19 | Stochastic processes: Markov chains, Markov chain Monte Carlo, and hidden state models. | +| **`monte_carlo`** | 982 | 28 | 3 | Monte Carlo methods and the random number generator behind them. | +| **`information_theory`** | 367 | 16 | 0 | Shannon information: entropy, divergence, and channel capacity. | +| **`learn`** | 4,213 | 55 | 13 | Learning algorithms, written to be read rather than to be fast. | + +### Decisions + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`optimization`** | 15,189 | 177 | 19 | Optimization: continuous, combinatorial, and strategic. | +| **`finance`** | 5,119 | 60 | 6 | Quantitative finance: derivative pricing, interest rates, portfolio construction and risk measurement. | + +### Geometry + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`geometry`** | 1,522 | 49 | 3 | Areas, volumes and surface areas of the standard shapes. | +| **`curves`** | 500 | 25 | 0 | Plane curves: conics, Bézier curves, and parametric families. | +| **`trigonometry`** | 493 | 33 | 0 | Triangle solving, trigonometric identities, and hyperbolic functions. | +| **`quaternion`** | 562 | 18 | 1 | Unit quaternions for 3-D rotation. | +| **`manifold`** | 20,431 | 720 | 32 | Manifolds and higher-dimensional geometry: generic n-dimensional vectors and tensors, metric-driven curvature, and (in later modules) geodesics,… | +| **`spatial`** | 7,020 | 254 | 24 | Spatial data structures, transforms, geometric primitives, and queries. | +| **`mesh`** | 6,516 | 136 | 10 | Indexed triangle meshes: construction, mass properties, cleanup, spatial queries, and OBJ/STL interchange. | + +### Patterns and chaos + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`fractals`** | 9,454 | 288 | 38 | Fractals: escape-time sets, attractors, automata and noise. | +| **`patterns`** | 12,404 | 239 | 12 | Geometric patterns: polygon algorithms, sampling distributions, phyllotaxis, tilings, symmetry groups, packings, space-filling curves, polyhedra,… | +| **`nonlinear`** | 432 | 13 | 0 | Chaos in low-dimensional systems. | + +### Reference and utility + +| Module | Lines | Public fns | Types | What it is | +|---|--:|--:|--:|---| +| **`units`** | 2,785 | 85 | 3 | Unit conversions, dimensional analysis and the CODATA constants. | +| **`materials`** | 3,548 | 11 | 6 | Reference property tables. | +| **`color_science`** | 788 | 14 | 0 | Colour: the standard spaces, the transforms between them, and perceptual measures. | +| **`control_systems`** | 744 | 27 | 3 | Linear control: system response, stability margins and PID tuning. | +| **`atmosphere`** | 409 | 17 | 0 | The standard atmosphere, humidity, and near-surface wind. | +| **`verification`** | 394 | 0 | 0 | Kani proof harnesses. | + +## Every module + +| Path | Module | Lines | Fns | Methods | Types | Summary | +|---|---|--:|--:|--:|--:|---| +| `acoustics.rs` | `acoustics` | 576 | 31 | 0 | 0 | Room acoustics, psychoacoustic scales, and musical pitch. | +| `astrophysics/mod.rs` | `astrophysics` | 38 | 0 | 0 | 0 | Astrodynamics and astrophysics. | +| `astrophysics/collisions.rs` | `astrophysics::collisions` | 318 | 8 | 0 | 3 | Impacts, mergers, and collision probability. | +| `astrophysics/coords.rs` | `astrophysics::coords` | 1,051 | 11 | 0 | 2 | Astronomical coordinates, low-precision ephemerides and TLE parsing. | +| `astrophysics/gravitational_waves.rs` | `astrophysics::gravitational_waves` | 233 | 8 | 0 | 0 | Gravitational radiation from a compact binary. | +| `astrophysics/habitable_zone.rs` | `astrophysics::habitable_zone` | 127 | 6 | 0 | 0 | Habitable zone boundaries and tidal locking. | +| `astrophysics/kepler.rs` | `astrophysics::kepler` | 913 | 9 | 0 | 0 | Kepler's equation, anomaly conversions and two-body propagation. | +| `astrophysics/lagrange.rs` | `astrophysics::lagrange` | 195 | 4 | 0 | 0 | The five Lagrange points of the circular restricted three-body problem. | +| `astrophysics/lambert.rs` | `astrophysics::lambert` | 553 | 4 | 0 | 0 | Lambert's problem: the orbit connecting two positions in a given time. | +| `astrophysics/magnetosphere.rs` | `astrophysics::magnetosphere` | 405 | 6 | 0 | 1 | Planetary dipole fields and the magnetopause. | +| `astrophysics/maneuvers.rs` | `astrophysics::maneuvers` | 618 | 8 | 0 | 0 | Orbital manoeuvres: combined burns, patched conics, gravity assists and the perturbation that dominates low orbits. | +| `astrophysics/nbody.rs` | `astrophysics::nbody` | 403 | 8 | 7 | 2 | Direct N-body gravitational simulation. | +| `astrophysics/orbital_elements.rs` | `astrophysics::orbital_elements` | 660 | 15 | 5 | 1 | Keplerian elements: conversion, propagation, and the anomalies. | +| `astrophysics/tidal.rs` | `astrophysics::tidal` | 209 | 7 | 0 | 0 | Tidal forces and the Roche limit. | +| `astrophysics/time_systems.rs` | `astrophysics::time_systems` | 399 | 5 | 0 | 0 | Astronomical time: Julian dates and sidereal time. | +| `atmosphere.rs` | `atmosphere` | 409 | 17 | 0 | 0 | The standard atmosphere, humidity, and near-surface wind. | +| `audio/mod.rs` | `audio` | 82 | 0 | 0 | 0 | Audio synthesis, analysis, effects, and I/O. | +| `audio/analysis.rs` | `audio::analysis` | 1,950 | 60 | 0 | 2 | Audio analysis: pitch detection (YIN, autocorrelation, cepstral, HPS, McLeod), onset/tempo/beat tracking, MFCCs, LPC and formants, LSPs, spectral… | +| `audio/effects.rs` | `audio::effects` | 1,669 | 21 | 52 | 20 | Audio effects: delays, reverbs (Schroeder, Freeverb, FDN), convolution, modulation effects, dynamics, distortion, EQ, imaging, loudness (ITU-R… | +| `audio/envelope.rs` | `audio::envelope` | 604 | 9 | 18 | 5 | Envelopes, LFOs, followers, fades, and glides. | +| `audio/oscillators.rs` | `audio::oscillators` | 939 | 16 | 15 | 5 | Audio-rate oscillators and test signals: PolyBLEP anti-aliased classics, additive resynthesis, mipmapped wavetables, colored noise, chirps, and… | +| `audio/physical.rs` | `audio::physical` | 1,331 | 12 | 34 | 8 | Physical modeling synthesis: digital waveguides (plucked/struck/bowed strings, clarinet and flute bores), modal synthesis (bars, membranes,… | +| `audio/spatial.rs` | `audio::spatial` | 1,132 | 28 | 0 | 0 | Spatial audio: panning laws, VBAP, ambisonics, simple binaural cues, Doppler, distance/air attenuation, geometric room acoustics (image source and… | +| `audio/synthesis.rs` | `audio::synthesis` | 1,260 | 29 | 7 | 4 | Sound synthesis: additive, FM (DX7-style operator routing), Karplus-Strong, subtractive, granular, formant, waveshaping, drums, and note/sequence… | +| `audio/tuning.rs` | `audio::tuning` | 517 | 25 | 0 | 2 | Musical tuning: temperaments, interval math, Scala parsing, consonance models, stretch tuning, and pitch-class utilities. | +| `audio/vocoder.rs` | `audio::vocoder` | 862 | 8 | 8 | 2 | Phase vocoder and related voice/spectral processors: time stretching, pitch shifting, robotization, channel and LPC vocoders, WSOLA, PSOLA,… | +| `audio/wav.rs` | `audio::wav` | 370 | 8 | 0 | 1 | WAV (RIFF) reading and writing: PCM 8/16/24/32-bit, IEEE float 32/64-bit, and WAVE_FORMAT_EXTENSIBLE containers. | +| `biophysics/mod.rs` | `biophysics` | 468 | 19 | 0 | 0 | Biophysics: the elementary membrane, transport and mechanics relations here, with the population-scale models in submodules. | +| `biophysics/epidemiology.rs` | `biophysics::epidemiology` | 1,924 | 22 | 1 | 1 | Compartment models of epidemics, their stochastic counterparts, and the quantities estimated from case data. | +| `biophysics/neuro.rs` | `biophysics::neuro` | 2,519 | 35 | 2 | 1 | Computational neuroscience: single neurons, spike trains, synapses and the small networks built from them. | +| `biophysics/phylo.rs` | `biophysics::phylo` | 1,979 | 9 | 19 | 2 | Phylogenetics: trees, the distance and character methods that build them, and the statistics read off them. | +| `biophysics/population.rs` | `biophysics::population` | 2,356 | 36 | 0 | 1 | Population dynamics and population genetics: growth laws, interacting species, age-structured projection, discrete maps, and the drift, selection… | +| `biophysics/seq_align.rs` | `biophysics::seq_align` | 2,150 | 30 | 4 | 2 | Sequence alignment and the elementary sequence analysis around it. | +| `cfd/mod.rs` | `cfd` | 122 | 0 | 0 | 0 | Computational fluid dynamics: staggered grids, advection schemes, and (in later modules) incompressible solvers, shallow water, SPH, LBM, level… | +| `cfd/advection.rs` | `cfd::advection` | 869 | 17 | 0 | 2 | Advection schemes: classic 1D finite-volume methods (upwind, Lax-Wendroff, MUSCL with slope limiters, WENO5), 2D semi-Lagrangian transport with… | +| `cfd/boundary_layer.rs` | `cfd::boundary_layer` | 579 | 31 | 0 | 0 | Boundary layers: Blasius and Falkner-Skan similarity solutions (shooting), Thwaites and Head integral methods, turbulent wall laws, transition and… | +| `cfd/grid.rs` | `cfd::grid` | 685 | 0 | 30 | 4 | Staggered (MAC) grids and cell-centered scalar fields for incompressible flow solvers. | +| `cfd/lbm.rs` | `cfd::lbm` | 1,142 | 8 | 22 | 4 | Lattice Boltzmann method: D2Q9 with BGK/TRT/MRT/cumulant-style collisions, bounce-back solids, Zou-He open boundaries, Guo forcing, D3Q19 and… | +| `cfd/level_set.rs` | `cfd::level_set` | 2,066 | 12 | 34 | 6 | Interface capturing: level sets (upwind/WENO advection, Sussman reinitialization, fast marching, marching squares/tetrahedra), volume of fluid… | +| `cfd/multiphase.rs` | `cfd::multiphase` | 778 | 35 | 1 | 2 | Multiphase flow correlations: mixture properties, drift-flux and void fraction models, two-phase pressure drop, flow-pattern maps, bubble and… | +| `cfd/porous.rs` | `cfd::porous` | 695 | 24 | 8 | 1 | Porous-media flow: Darcy's law and extensions, unsaturated flow (Richards equation with Van Genuchten retention), well hydraulics, solute… | +| `cfd/potential_flow.rs` | `cfd::potential_flow` | 1,523 | 27 | 19 | 5 | Incompressible potential flow: elementary singularities, complex potentials, Joukowski and Karman-Trefftz airfoils, NACA sections, the Hess-Smith… | +| `cfd/riemann.rs` | `cfd::riemann` | 1,564 | 26 | 16 | 6 | 1D/2D compressible Euler equations: exact and approximate Riemann solvers (HLL, HLLC, Roe with entropy fix, Rusanov, AUSM+), MUSCL finite-volume… | +| `cfd/shallow_water.rs` | `cfd::shallow_water` | 938 | 14 | 12 | 1 | Shallow water equations: well-balanced HLL finite volumes with hydrostatic reconstruction and wet/dry handling (1D and 2D), the Stoker dam-break… | +| `cfd/sph.rs` | `cfd::sph` | 1,154 | 9 | 23 | 7 | Smoothed-particle hydrodynamics: standard kernel family, spatial hashing, weakly compressible (WCSPH) and predictive-corrective solvers with… | +| `cfd/stable_fluids.rs` | `cfd::stable_fluids` | 1,776 | 8 | 19 | 3 | Stable-fluids incompressible solver on a MAC grid: MacCormack advection, implicit viscosity, buoyancy, vorticity confinement, and a pressure… | +| `cfd/turbulence.rs` | `cfd::turbulence` | 1,537 | 34 | 12 | 5 | Turbulence modelling and statistics. | +| `cfd/vortex.rs` | `cfd::vortex` | 1,304 | 17 | 19 | 4 | Vortex methods: regularized Biot-Savart particle methods in 2D and 3D, classical vortex solutions (Lamb-Oseen, Rankine, Burgers, Hill), point… | +| `chemistry.rs` | `chemistry` | 334 | 18 | 0 | 0 | Reaction kinetics, chemical thermodynamics and electrochemistry. | +| `classical.rs` | `classical` | 786 | 54 | 0 | 0 | Newtonian mechanics: kinematics, dynamics, and the harmonic oscillator. | +| `codes/mod.rs` | `codes` | 9 | 0 | 0 | 0 | Error detection, error correction, compression, and the arithmetic cryptography is built on. | +| `codes/block.rs` | `codes::block` | 1,579 | 11 | 35 | 2 | Binary linear block codes. | +| `codes/checksum.rs` | `codes::checksum` | 880 | 21 | 0 | 0 | Checksums and check digits: cheap ways to notice that data changed. | +| `codes/compression.rs` | `codes::compression` | 1,420 | 28 | 7 | 3 | Lossless compression, and the string machinery it is built on. | +| `codes/convolutional.rs` | `codes::convolutional` | 1,335 | 17 | 27 | 3 | Convolutional and turbo codes, and the channels they run over. | +| `codes/crypto_math.rs` | `codes::crypto_math` | 1,501 | 24 | 14 | 2 | The arithmetic underneath public-key cryptography, for study rather than for use. | +| `codes/reed_solomon.rs` | `codes::reed_solomon` | 1,519 | 4 | 37 | 6 | Reed-Solomon and BCH codes over finite fields. | +| `color_science.rs` | `color_science` | 788 | 14 | 0 | 0 | Colour: the standard spaces, the transforms between them, and perceptual measures. | +| `continuum_mechanics.rs` | `continuum_mechanics` | 508 | 19 | 0 | 0 | Stress and strain as tensors, and the yield criteria built on them. | +| `control_systems/mod.rs` | `control_systems` | 453 | 18 | 3 | 1 | Linear control: system response, stability margins and PID tuning. | +| `control_systems/kalman.rs` | `control_systems::kalman` | 291 | 0 | 6 | 2 | Kalman filtering. | +| `core/mod.rs` | `core` | 6 | 0 | 0 | 0 | Pure numeric building blocks: compensated summation, forward-mode automatic differentiation, and interval arithmetic. | +| `core/compensated.rs` | `core::compensated` | 117 | 3 | 0 | 0 | Compensated (error-free-transformation) summation. | +| `core/dual.rs` | `core::dual` | 410 | 3 | 15 | 1 | Forward-mode automatic differentiation with dual numbers. | +| `core/interval.rs` | `core::interval` | 451 | 1 | 12 | 1 | Rigorous interval arithmetic with outward rounding. | +| `curves.rs` | `curves` | 500 | 25 | 0 | 0 | Plane curves: conics, Bézier curves, and parametric families. | +| `discrete/mod.rs` | `discrete` | 10 | 0 | 0 | 0 | Discrete mathematics: primes and factorization, elementary and analytic number theory, counting and enumeration, integer partitions, integer… | +| `discrete/combinatorics.rs` | `discrete::combinatorics` | 2,827 | 59 | 0 | 0 | Counting, enumeration, and the permutation group. | +| `discrete/disjoint_set.rs` | `discrete::disjoint_set` | 352 | 0 | 10 | 1 | Union-find over `0..n` with path compression and union by size. | +| `discrete/number_theory.rs` | `discrete::number_theory` | 2,345 | 49 | 0 | 0 | Elementary and analytic number theory. | +| `discrete/partitions.rs` | `discrete::partitions` | 784 | 15 | 0 | 0 | Integer partitions, Young diagrams, and the RSK correspondence. | +| `discrete/primes.rs` | `discrete::primes` | 1,238 | 26 | 0 | 0 | Primes: sieves, primality testing, factorization, and prime counting. | +| `discrete/sequences.rs` | `discrete::sequences` | 1,491 | 22 | 0 | 0 | Integer sequences, linear recurrences, and generating functions. | +| `dsp/mod.rs` | `dsp` | 37 | 0 | 0 | 0 | Digital signal processing: window functions, FIR/IIR filter design, resampling, and phase utilities. | +| `dsp/fir.rs` | `dsp::fir` | 996 | 18 | 3 | 1 | FIR filter design and application. | +| `dsp/iir.rs` | `dsp::iir` | 1,666 | 22 | 26 | 4 | Infinite impulse response filters: RBJ biquads, second-order-section cascades, and classical designs (Butterworth, Chebyshev I/II, elliptic,… | +| `dsp/phase.rs` | `dsp::phase` | 270 | 8 | 0 | 0 | Phase utilities: unwrapping (1D and Itoh 2D), phase-locked loops, interpolated zero crossings, and phase measurement against a reference tone. | +| `dsp/resample.rs` | `dsp::resample` | 513 | 10 | 0 | 0 | Sample-rate conversion: integer up/down sampling, polyphase rational resampling, windowed-sinc/linear/cubic interpolation, CIC decimation, and… | +| `dsp/windows.rs` | `dsp::windows` | 446 | 7 | 0 | 2 | Window functions for spectral analysis and FIR design. | +| `electromagnetism.rs` | `electromagnetism` | 822 | 60 | 0 | 0 | Classical electromagnetism, from Coulomb's law to radiating dipoles. | +| `electronics.rs` | `electronics` | 445 | 17 | 0 | 0 | Semiconductor device physics. | +| `error.rs` | `error` | 103 | 0 | 0 | 2 | Error types shared by the numerical solvers. | +| `exact/mod.rs` | `exact` | 15 | 0 | 0 | 0 | Exact arithmetic: arbitrary-precision integers, exact rationals, arbitrary-precision binary floating point, polynomials, and continued fractions. | +| `exact/bigfloat.rs` | `exact::bigfloat` | 2,000 | 5 | 39 | 1 | Arbitrary-precision binary floating point. | +| `exact/bigint.rs` | `exact::bigint` | 1,463 | 0 | 41 | 1 | Arbitrary-precision signed integers. | +| `exact/contfrac.rs` | `exact::contfrac` | 524 | 10 | 0 | 0 | Continued fractions: expansions, convergents, the periodic expansion of a square root, Pell's equation, generalized continued fractions by the… | +| `exact/polynomial.rs` | `exact::polynomial` | 2,286 | 5 | 76 | 2 | Dense univariate polynomials with `f64` coefficients ([`Poly`]) and with exact rational coefficients ([`PolyQ`]). | +| `exact/rational.rs` | `exact::rational` | 1,079 | 8 | 26 | 1 | Exact rational arithmetic over [`BigInt`]. | +| `exact/symbolic.rs` | `exact::symbolic` | 1,882 | 3 | 29 | 3 | A small computer algebra system over expression trees. | +| `fem/mod.rs` | `fem` | 25 | 0 | 0 | 0 | Finite elements, finite-difference time domain, and spectral methods. | +| `fem/fdtd.rs` | `fem::fdtd` | 1,154 | 7 | 1 | 3 | Finite-difference time domain: Maxwell's equations on a Yee grid. | +| `fem/fem1d.rs` | `fem::fem1d` | 952 | 7 | 6 | 2 | One-dimensional finite elements for `-(p u')' + q u = f`. | +| `fem/fem2d.rs` | `fem::fem2d` | 1,901 | 16 | 8 | 1 | Triangular finite elements in the plane. | +| `fem/spectral_pde.rs` | `fem::spectral_pde` | 693 | 7 | 0 | 0 | Spectral methods: global basis functions instead of local ones. | +| `fields.rs` | `fields` | 211 | 0 | 10 | 2 | Uniform-grid scalar fields. | +| `finance/mod.rs` | `finance` | 25 | 0 | 0 | 0 | Quantitative finance: derivative pricing, interest rates, portfolio construction and risk measurement. | +| `finance/options.rs` | `finance::options` | 2,090 | 17 | 0 | 3 | Option pricing: closed forms, lattices, Monte Carlo and a PDE solver. | +| `finance/portfolio.rs` | `finance::portfolio` | 865 | 16 | 0 | 0 | Portfolio construction and performance measurement. | +| `finance/rates.rs` | `finance::rates` | 1,388 | 19 | 1 | 2 | Interest rates: discounting, bonds, curves and short-rate models. | +| `finance/risk.rs` | `finance::risk` | 751 | 7 | 0 | 1 | Risk measurement: value at risk, expected shortfall, backtesting. | +| `fluid_instabilities.rs` | `fluid_instabilities` | 451 | 19 | 0 | 0 | When a fluid configuration stops being stable, and how fast it comes apart. | +| `fluids.rs` | `fluids` | 709 | 45 | 0 | 0 | Fluid statics and single-phase flow. | +| `fractals/mod.rs` | `fractals` | 642 | 10 | 5 | 1 | Fractals: escape-time sets, attractors, automata and noise. | +| `fractals/attractors.rs` | `fractals::attractors` | 1,104 | 7 | 43 | 3 | Strange attractors: 3-D chaotic flows and 2-D chaotic maps with trajectory integration, Lyapunov spectra (Benettin renormalization), Kaplan-Yorke… | +| `fractals/automata.rs` | `fractals::automata` | 2,881 | 18 | 74 | 16 | Cellular automata and growth models: elementary 1-D rules, life-like 2-D automata with pattern/RLE placement, cyclic CA, Langton's ant and… | +| `fractals/escape_time.rs` | `fractals::escape_time` | 1,044 | 24 | 0 | 3 | Escape-time fractals: a generic iteration engine with smooth coloring, orbit traps, and distance estimation, the classic quadratic families… | +| `fractals/ifs.rs` | `fractals::ifs` | 924 | 2 | 28 | 3 | Iterated function systems: the chaos game and deterministic attractor construction (Barnsley, "Fractals Everywhere", 1988), Moran similarity… | +| `fractals/lsystem.rs` | `fractals::lsystem` | 1,056 | 2 | 35 | 4 | Lindenmayer systems: parallel string rewriting with simple, stochastic, and context-sensitive rules, 2-D and 3-D turtle interpretation of the ABOP… | +| `fractals/noise.rs` | `fractals::noise` | 1,803 | 19 | 21 | 8 | Coherent noise: Perlin gradient noise (Perlin 2002), OpenSimplex2 (ported from K.jpg's reference implementation), value noise, Worley cellular… | +| `general_relativity.rs` | `general_relativity` | 427 | 23 | 0 | 0 | General relativity: black holes and cosmology. | +| `geometry/mod.rs` | `geometry` | 453 | 30 | 0 | 0 | Areas, volumes and surface areas of the standard shapes. | +| `geometry/delaunay.rs` | `geometry::delaunay` | 262 | 3 | 0 | 0 | Delaunay triangulation and Voronoi diagrams in the plane. | +| `geometry/geodesy.rs` | `geometry::geodesy` | 342 | 5 | 2 | 1 | Geodesy on a reference ellipsoid. | +| `geometry/hull.rs` | `geometry::hull` | 310 | 4 | 0 | 0 | Convex hulls and polygon predicates. | +| `geometry/mesh.rs` | `geometry::mesh` | 155 | 0 | 5 | 2 | Minimal indexed triangle mesh with ray intersection, backfilling the Part 2 `Mesh` type consumed by acoustics ray tracing and display helpers. | +| `geophysics.rs` | `geophysics` | 483 | 22 | 0 | 0 | The solid Earth: gravity, seismology, and heat. | +| `graph/mod.rs` | `graph` | 12 | 0 | 0 | 0 | Graphs: representation and structure, shortest paths, network flow, matchings, spectral graph theory, colouring, and drawing. | +| `graph/coloring.rs` | `graph::coloring` | 1,649 | 20 | 0 | 1 | Colouring, cliques, independent sets, and covers. | +| `graph/core.rs` | `graph::core` | 2,862 | 24 | 41 | 1 | Graphs: representation, structural queries, generators, and products. | +| `graph/flow.rs` | `graph::flow` | 1,519 | 14 | 0 | 0 | Network flow: maximum flow, minimum cut, and the problems that reduce to them. | +| `graph/layout.rs` | `graph::layout` | 1,935 | 15 | 0 | 0 | Graph drawing: where to put the vertices. | +| `graph/matching.rs` | `graph::matching` | 1,399 | 10 | 0 | 0 | Matchings: bipartite, general, weighted, and stable. | +| `graph/paths.rs` | `graph::paths` | 2,133 | 26 | 0 | 1 | Shortest paths, spanning trees, and tours. | +| `graph/spectral.rs` | `graph::spectral` | 1,984 | 32 | 0 | 0 | Spectral graph theory: Laplacians, centralities, resistances, and community detection. | +| `gravitation.rs` | `gravitation` | 236 | 14 | 0 | 0 | Newtonian gravity and two-body orbits. | +| `information_theory.rs` | `information_theory` | 367 | 16 | 0 | 0 | Shannon information: entropy, divergence, and channel capacity. | +| `learn/mod.rs` | `learn` | 16 | 0 | 0 | 0 | Learning algorithms, written to be read rather than to be fast. | +| `learn/cluster.rs` | `learn::cluster` | 1,295 | 13 | 2 | 3 | Clustering, mixture models and nearest neighbours. | +| `learn/gp.rs` | `learn::gp` | 839 | 1 | 12 | 2 | Gaussian process regression. | +| `learn/nn.rs` | `learn::nn` | 1,163 | 2 | 14 | 4 | Feed-forward networks, trained by backpropagation. | +| `learn/tree.rs` | `learn::tree` | 900 | 11 | 0 | 4 | Decision trees, random forests and gradient boosting. | +| `linalg/mod.rs` | `linalg` | 970 | 10 | 24 | 2 | Dense and sparse linear algebra. | +| `linalg/cholesky.rs` | `linalg::cholesky` | 147 | 2 | 0 | 0 | Cholesky factorization of symmetric positive-definite matrices. | +| `linalg/eigen.rs` | `linalg::eigen` | 492 | 2 | 0 | 1 | Eigenvalue solvers. | +| `linalg/lu.rs` | `linalg::lu` | 223 | 2 | 4 | 1 | LU decomposition with partial pivoting (Doolittle form). | +| `linalg/matrix.rs` | `linalg::matrix` | 344 | 0 | 16 | 1 | Dense row-major matrix of `f64`. | +| `linalg/qr.rs` | `linalg::qr` | 205 | 2 | 0 | 1 | QR decomposition by Householder reflections and least-squares solve. | +| `linalg/sparse.rs` | `linalg::sparse` | 360 | 2 | 4 | 1 | Compressed sparse row (CSR) matrices and conjugate-gradient solvers. | +| `linalg/svd.rs` | `linalg::svd` | 321 | 4 | 0 | 1 | Singular value decomposition by one-sided Jacobi rotations. | +| `linalg/tridiagonal.rs` | `linalg::tridiagonal` | 233 | 2 | 0 | 0 | Tridiagonal linear solve (Thomas algorithm). | +| `magnetohydrodynamics.rs` | `magnetohydrodynamics` | 359 | 19 | 0 | 0 | Magnetohydrodynamics: a conducting fluid and the field frozen into it. | +| `manifold/mod.rs` | `manifold` | 105 | 0 | 0 | 0 | Manifolds and higher-dimensional geometry: generic n-dimensional vectors and tensors, metric-driven curvature, and (in later modules) geodesics,… | +| `manifold/clifford.rs` | `manifold::clifford` | 2,830 | 4 | 134 | 1 | Clifford (geometric) algebras Cl(p, q, r): a dense multivector type over any signature, with the geometric/outer/inner products, versors and… | +| `manifold/dec.rs` | `manifold::dec` | 1,618 | 3 | 30 | 1 | Discrete exterior calculus on triangle meshes: exterior derivatives, diagonal Hodge stars, Laplacians, Hodge decomposition, harmonic forms and… | +| `manifold/embedding.rs` | `manifold::embedding` | 1,624 | 40 | 0 | 0 | Manifold learning and dimensionality reduction: spectral embeddings (MDS, Isomap, LLE, Laplacian eigenmaps, diffusion maps), PCA and kernel PCA,… | +| `manifold/geodesic.rs` | `manifold::geodesic` | 1,422 | 8 | 22 | 2 | Geodesics, parallel transport, Jacobi fields, and relativistic orbits, all driven by the finite-difference [`Metric`] machinery. | +| `manifold/hyperbolic.rs` | `manifold::hyperbolic` | 1,710 | 43 | 9 | 2 | Hyperbolic geometry across the standard models: Poincare disk/ball, upper half-plane/space, Klein disk, and the hyperboloid, with isometries,… | +| `manifold/lie.rs` | `manifold::lie` | 3,225 | 17 | 108 | 15 | Lie groups and algebras: rotation and rigid-motion groups in 2/3/4 dimensions, SU(2) and SL(2) groups, matrix exponentials and logarithms,… | +| `manifold/metric.rs` | `manifold::metric` | 1,382 | 5 | 49 | 2 | Metric geometry on n-dimensional manifolds: a metric is a function from coordinates to a matrix g_ij, and everything else — Christoffel symbols,… | +| `manifold/polytope4.rs` | `manifold::polytope4` | 2,142 | 27 | 41 | 2 | Four-dimensional polytopes: the six regular 4-polytopes with their full combinatorics, prisms and products, projections and cross-sections, duals,… | +| `manifold/spacetime.rs` | `manifold::spacetime` | 1,556 | 30 | 30 | 5 | Special and general relativity: four-vectors and Lorentz transforms, Rindler and Kruskal coordinates, Schwarzschild and Kerr geodesics,… | +| `manifold/spherical.rs` | `manifold::spherical` | 1,671 | 67 | 0 | 0 | Spherical geometry: n-sphere maps, spherical trigonometry, map projections, the Hopf fibration, spherical harmonics and their transforms, sky… | +| `manifold/vecn.rs` | `manifold::vecn` | 1,146 | 3 | 50 | 2 | n-dimensional vectors and arbitrary-rank tensors: the generic machinery behind the metric-driven differential geometry in this module tree. | +| `materials/mod.rs` | `materials` | 17 | 0 | 0 | 0 | Reference property tables. | +| `materials/common.rs` | `materials::common` | 349 | 2 | 0 | 1 | Engineering solids: metals, alloys, polymers and ceramics. | +| `materials/elements.rs` | `materials::elements` | 2,632 | 5 | 0 | 3 | The 118 chemical elements. | +| `materials/fluids.rs` | `materials::fluids` | 308 | 2 | 0 | 1 | Common liquids. | +| `materials/gases.rs` | `materials::gases` | 242 | 2 | 0 | 1 | Common gases. | +| `math.rs` | `math` | 621 | 0 | 23 | 2 | Vectors and the crate's table of physical constants. | +| `mesh/mod.rs` | `mesh` | 1,071 | 0 | 32 | 1 | Indexed triangle meshes: construction, mass properties, cleanup, spatial queries, and OBJ/STL interchange. | +| `mesh/analyze.rs` | `mesh::analyze` | 1,201 | 20 | 0 | 1 | Mesh analysis: topology (manifoldness, orientation, boundary, components, genus), quality statistics, QEM decimation, discrete curvatures, and… | +| `mesh/generate.rs` | `mesh::generate` | 881 | 14 | 0 | 0 | Procedural mesh generators. | +| `mesh/isosurface.rs` | `mesh::isosurface` | 1,052 | 8 | 9 | 2 | Isosurface and isocontour extraction from sampled scalar fields: marching squares/cubes/tetrahedra, surface nets, dual contouring, and metaballs. | +| `mesh/parameterize.rs` | `mesh::parameterize` | 652 | 7 | 0 | 1 | Mesh parameterization: closed-form spherical/planar/cylindrical projections, harmonic (cotangent-Laplace) disk parameterization with fixed… | +| `mesh/subdivide.rs` | `mesh::subdivide` | 616 | 9 | 3 | 1 | Subdivision surfaces (Loop, Catmull-Clark, sqrt(3), midpoint) and Laplacian-family smoothing. | +| `mesh/surfaces.rs` | `mesh::surfaces` | 1,043 | 19 | 15 | 4 | Parametric surfaces: Bézier/B-spline/NURBS patches, classic surface constructions, differential geometry via fundamental forms, and a catalogue of… | +| `monte_carlo/mod.rs` | `monte_carlo` | 700 | 15 | 5 | 1 | Monte Carlo methods and the random number generator behind them. | +| `monte_carlo/quasi.rs` | `monte_carlo::quasi` | 282 | 2 | 6 | 2 | Quasi-random (low-discrepancy) sequences: Sobol and Halton. | +| `neutronics.rs` | `neutronics` | 409 | 22 | 0 | 0 | Reactor physics: criticality, neutron diffusion, and shielding. | +| `nonlinear.rs` | `nonlinear` | 432 | 13 | 0 | 0 | Chaos in low-dimensional systems. | +| `nuclear.rs` | `nuclear` | 303 | 20 | 0 | 0 | Radioactive decay, nuclear binding, and dosimetry. | +| `numerical/mod.rs` | `numerical` | 424 | 0 | 0 | 0 | Numerical methods: quadrature, root finding, ODE solvers, and interpolation. | +| `numerical/bvp.rs` | `numerical::bvp` | 201 | 2 | 0 | 0 | Two-point boundary value problems. | +| `numerical/integrate.rs` | `numerical::integrate` | 270 | 8 | 0 | 1 | Numerical integration (quadrature) rules. | +| `numerical/interpolate.rs` | `numerical::interpolate` | 648 | 6 | 9 | 2 | Interpolation routines. | +| `numerical/ode/mod.rs` | `numerical::ode` | 11 | 0 | 0 | 0 | Ordinary differential equation solvers. | +| `numerical/ode/adaptive.rs` | `numerical::ode::adaptive` | 309 | 2 | 0 | 1 | Adaptive Runge-Kutta integration: Dormand-Prince 5(4). | +| `numerical/ode/explicit.rs` | `numerical::ode::explicit` | 63 | 4 | 0 | 0 | Explicit fixed-step ODE integrators. | +| `numerical/ode/implicit.rs` | `numerical::ode::implicit` | 232 | 2 | 0 | 0 | Implicit (stiff-stable) ODE steps: backward Euler and BDF2. | +| `numerical/ode/symplectic.rs` | `numerical::ode::symplectic` | 125 | 3 | 0 | 0 | Symplectic integrators for second-order systems x'' = a(x). | +| `numerical/roots.rs` | `numerical::roots` | 280 | 7 | 0 | 0 | Scalar and polynomial root finding. | +| `optics.rs` | `optics` | 348 | 20 | 0 | 0 | Geometric and wave optics. | +| `optimization/mod.rs` | `optimization` | 828 | 11 | 0 | 0 | Optimization: continuous, combinatorial, and strategic. | +| `optimization/convex.rs` | `optimization::convex` | 2,410 | 35 | 0 | 0 | Convex optimisation: gradient methods, quasi-Newton methods, proximal splitting, and constrained solvers. | +| `optimization/game_theory.rs` | `optimization::game_theory` | 3,326 | 41 | 0 | 11 | Game theory: equilibria, dynamics, cooperative solution concepts, auctions, and two-player search. | +| `optimization/integer.rs` | `optimization::integer` | 2,694 | 33 | 0 | 1 | Integer programming, dynamic programming, and combinatorial search. | +| `optimization/least_squares.rs` | `optimization::least_squares` | 371 | 3 | 0 | 1 | Nonlinear least squares: Levenberg-Marquardt. | +| `optimization/lp.rs` | `optimization::lp` | 2,600 | 13 | 8 | 3 | Linear programming: the simplex method, interior point methods, duality, and the classical models that reduce to a linear program. | +| `optimization/metaheuristics.rs` | `optimization::metaheuristics` | 1,577 | 15 | 0 | 2 | Derivative-free and population-based optimisation, and the benchmark landscapes used to tell one method from another. | +| `optimization/network.rs` | `optimization::network` | 1,383 | 17 | 1 | 1 | Network models and scheduling: project planning, flows on networks, and the sequencing rules that provably optimise a stated objective. | +| `particle_physics.rs` | `particle_physics` | 402 | 20 | 0 | 0 | Relativistic kinematics and scattering for particle collisions. | +| `patterns/mod.rs` | `patterns` | 14 | 0 | 0 | 0 | Geometric patterns: polygon algorithms, sampling distributions, phyllotaxis, tilings, symmetry groups, packings, space-filling curves, polyhedra,… | +| `patterns/aperiodic.rs` | `patterns::aperiodic` | 1,807 | 13 | 0 | 2 | Aperiodic tilings: Penrose P2 (kite/dart) and P3 (rhombs) by Robinson-triangle deflation, de Bruijn multigrid projection, Ammann-Beenker, the hat… | +| `patterns/knots.rs` | `patterns::knots` | 831 | 20 | 0 | 0 | Knots and space curves: parametric knot families, Frenet and rotation-minimizing frames, curvature/torsion estimates, and the classical knot… | +| `patterns/packing.rs` | `patterns::packing` | 1,130 | 17 | 0 | 0 | Circle and sphere packings: Descartes/Apollonian circles, lattice packings, random sequential adsorption, Doyle spirals, Ford circles, Steiner… | +| `patterns/phyllotaxis.rs` | `patterns::phyllotaxis` | 490 | 18 | 0 | 0 | Phyllotactic patterns and spirals: Vogel sunflowers, Fibonacci point sets, the classical spiral family, and parastichy analysis. | +| `patterns/polygon_ops.rs` | `patterns::polygon_ops` | 2,022 | 29 | 0 | 1 | 2-D polygon algorithms: triangulation, simplification, offsetting, Minkowski sums, boolean operations, clipping, decomposition, hulls, skeletons,… | +| `patterns/polyhedra.rs` | `patterns::polyhedra` | 2,032 | 32 | 14 | 2 | Polyhedra: Platonic/Archimedean/Catalan/Johnson solids, Goldberg and geodesic polyhedra, and Conway polyhedron operators. | +| `patterns/sampling.rs` | `patterns::sampling` | 1,220 | 27 | 0 | 0 | Random and low-discrepancy sampling: Poisson disk (Bridson), blue-noise ranking, stratified jitter, uniform samplers over shapes, random polygons… | +| `patterns/space_filling.rs` | `patterns::space_filling` | 755 | 20 | 0 | 0 | Space-filling curves and locality-preserving orders: Hilbert (2-D and 3-D), Peano, Morton/Z-order, Gray codes, and L-system curves (Sierpiński… | +| `patterns/symmetry.rs` | `patterns::symmetry` | 996 | 14 | 7 | 4 | Plane symmetry groups (the 17 wallpaper groups and 7 frieze groups), lattices, 3-D point groups, symmetry detection, and Hankin-style Islamic star… | +| `patterns/tilings.rs` | `patterns::tilings` | 1,107 | 10 | 18 | 3 | Plane tilings: regular and Archimedean (uniform) tilings, their Laves duals, hex-grid coordinate algebra, and a few classic non-edge-to-edge… | +| `photonics.rs` | `photonics` | 485 | 26 | 0 | 0 | Laser beams, optical fibre, and interferometry. | +| `plasma.rs` | `plasma` | 297 | 16 | 0 | 0 | Plasma parameters: the characteristic lengths, frequencies and speeds. | +| `propulsion.rs` | `propulsion` | 361 | 15 | 0 | 0 | Rocket propulsion and impulsive orbital transfers. | +| `quantum/mod.rs` | `quantum` | 411 | 27 | 0 | 0 | Quantum mechanics: the elementary relations here, with the wavefunction machinery and the Schrodinger solvers in submodules. | +| `quantum/algorithms.rs` | `quantum::algorithms` | 1,707 | 24 | 0 | 0 | Quantum algorithms on the state-vector simulator. | +| `quantum/circuit.rs` | `quantum::circuit` | 2,460 | 15 | 77 | 5 | A state-vector quantum circuit simulator, with density matrices and noise channels. | +| `quantum/schrodinger.rs` | `quantum::schrodinger` | 2,607 | 25 | 0 | 1 | Solvers for the Schrodinger equation, stationary and time dependent. | +| `quantum/solid_state.rs` | `quantum::solid_state` | 1,685 | 38 | 0 | 0 | Electrons and phonons in crystals: bands, densities of states, transport, and the standard model systems. | +| `quantum/spin.rs` | `quantum::spin` | 1,703 | 19 | 10 | 1 | Spin operators, quantum magnets, and magnetic resonance. | +| `quantum/wavefunction.rs` | `quantum::wavefunction` | 1,368 | 13 | 19 | 1 | One-dimensional wavefunctions, the standard eigenstates, and phase-space distributions. | +| `quaternion.rs` | `quaternion` | 562 | 2 | 16 | 1 | Unit quaternions for 3-D rotation. | +| `radiation.rs` | `radiation` | 335 | 14 | 0 | 0 | Thermal radiation and radiative transfer. | +| `relativity.rs` | `relativity` | 302 | 18 | 0 | 0 | Special relativity. | +| `resonance/mod.rs` | `resonance` | 42 | 0 | 0 | 0 | Resonance and vibration: single and coupled oscillators, acoustic and electromagnetic cavities, nonlinear resonance, and structural dynamics. | +| `resonance/cavity.rs` | `resonance::cavity` | 931 | 32 | 13 | 3 | Resonant cavities and structures: RLC circuits, Helmholtz resonators, strings, air columns, membranes, plates, beams, rooms, optical etalons, and… | +| `resonance/coupled.rs` | `resonance::coupled` | 828 | 6 | 18 | 1 | Coupled linear oscillators: normal modes, modal superposition, receptance, classic two-body systems, Kuramoto synchronization, and… | +| `resonance/nonlinear.rs` | `resonance::nonlinear` | 704 | 22 | 0 | 0 | Nonlinear resonance: the Duffing and van der Pol oscillators, parametric (Mathieu) stability, Fano interference, synchronization pulling/locking,… | +| `resonance/oscillator.rs` | `resonance::oscillator` | 636 | 9 | 19 | 2 | The damped harmonic oscillator m·x″ + c·x′ + k·x = F(t): closed-form responses in every damping regime, frequency-domain descriptions, and… | +| `resonance/structural.rs` | `resonance::structural` | 1,134 | 6 | 19 | 1 | Structural dynamics: finite-element bars and beams, modal analysis with general (consistent) mass matrices, Rayleigh damping, implicit time… | +| `rf.rs` | `rf` | 440 | 28 | 0 | 0 | Radio-frequency engineering: links, lines and noise. | +| `signal_processing/mod.rs` | `signal_processing` | 708 | 16 | 0 | 0 | Time-domain signal operations and test waveforms. | +| `sim/mod.rs` | `sim` | 21 | 0 | 0 | 0 | Time-stepping simulation engines. | +| `sim/cloth_sim.rs` | `sim::cloth_sim` | 970 | 2 | 10 | 3 | Verlet cloth and rope with spring constraints. | +| `sim/em_sim.rs` | `sim::em_sim` | 638 | 0 | 15 | 2 | FDTD electromagnetic simulation in one and two dimensions. | +| `sim/fluid_sim.rs` | `sim::fluid_sim` | 1,161 | 0 | 17 | 3 | Compact fluid solvers: column, shallow water, and 2-D Euler. | +| `sim/heat_sim.rs` | `sim::heat_sim` | 774 | 0 | 22 | 3 | Heat conduction and convection-diffusion on a grid. | +| `sim/rigid_body.rs` | `sim::rigid_body` | 681 | 2 | 19 | 2 | Rigid body dynamics in three dimensions. | +| `sim/wave_sim.rs` | `sim::wave_sim` | 682 | 0 | 14 | 2 | The wave equation in one and two dimensions. | +| `solid_mechanics.rs` | `solid_mechanics` | 380 | 23 | 0 | 0 | Strength of materials: stress, strain, elastic constants and beams. | +| `spatial/mod.rs` | `spatial` | 33 | 0 | 0 | 0 | Spatial data structures, transforms, geometric primitives, and queries. | +| `spatial/bvh.rs` | `spatial::bvh` | 513 | 0 | 10 | 1 | Bounding volume hierarchy over axis-aligned boxes. | +| `spatial/contain.rs` | `spatial::contain` | 527 | 20 | 0 | 0 | Orientation predicates and containment tests. | +| `spatial/distance.rs` | `spatial::distance` | 495 | 18 | 0 | 0 | Closest-point queries and set distances. | +| `spatial/frame.rs` | `spatial::frame` | 215 | 0 | 15 | 1 | Rigid coordinate frames (origin + unit-quaternion rotation). | +| `spatial/intersect.rs` | `spatial::intersect` | 908 | 23 | 0 | 1 | Intersection tests between the spatial primitives. | +| `spatial/kdtree.rs` | `spatial::kdtree` | 510 | 0 | 9 | 1 | k-d trees (3-D and 2-D) with median splits, plus a uniform spatial hash for broadphase neighbor queries. | +| `spatial/mat4.rs` | `spatial::mat4` | 504 | 0 | 21 | 1 | 4×4 homogeneous transform matrix (row-major storage, column-vector convention: p' = M·p). | +| `spatial/octree.rs` | `spatial::octree` | 342 | 1 | 2 | 1 | Barnes-Hut octree for N-body force approximation. | +| `spatial/primitives.rs` | `spatial::primitives` | 1,128 | 0 | 63 | 15 | Geometric primitive types shared by the intersection, distance, containment, and acceleration modules. | +| `spatial/projective.rs` | `spatial::projective` | 447 | 8 | 5 | 1 | Homogeneous 2-D projective geometry: points, lines, cross ratios, and plane homographies (Hartley & Zisserman, *Multiple View Geometry*, ch. | +| `spatial/quadtree.rs` | `spatial::quadtree` | 359 | 0 | 7 | 1 | Point quadtree with bucket capacity and depth limit. | +| `spatial/sdf.rs` | `spatial::sdf` | 731 | 37 | 0 | 0 | Signed distance fields: primitives, combinators, domain operators, and queries (sphere tracing, normals, AO, soft shadows). | +| `spatial/transform2d.rs` | `spatial::transform2d` | 308 | 0 | 15 | 1 | 2-D affine transforms stored as 3×3 homogeneous matrices (last row 0 0 1), column-vector convention: p' = M·p. | +| `special/mod.rs` | `special` | 26 | 0 | 0 | 0 | Special functions: error function family, gamma family, and beta functions. | +| `special/bessel.rs` | `special::bessel` | 528 | 12 | 0 | 0 | Bessel functions of integer order. | +| `special/beta.rs` | `special::beta` | 145 | 2 | 0 | 0 | Beta function and regularized incomplete beta. | +| `special/elliptic.rs` | `special::elliptic` | 333 | 7 | 0 | 0 | Elliptic integrals and physical applications. | +| `special/erf.rs` | `special::erf` | 335 | 3 | 0 | 0 | Error function family. | +| `special/expint.rs` | `special::expint` | 126 | 2 | 0 | 0 | Exponential integrals Ei(x) and E1(x). | +| `special/gamma.rs` | `special::gamma` | 219 | 4 | 0 | 0 | Gamma function family. | +| `special/legendre.rs` | `special::legendre` | 243 | 4 | 0 | 0 | Legendre polynomials, associated Legendre functions, real spherical harmonics, and Gauss-Legendre quadrature nodes. | +| `statistical_mechanics/mod.rs` | `statistical_mechanics` | 441 | 21 | 0 | 0 | Statistical mechanics: the elementary relations here, with lattice models and Monte Carlo in submodules. | +| `statistical_mechanics/ising.rs` | `statistical_mechanics::ising` | 2,004 | 13 | 23 | 4 | The Ising model and its relatives, by Monte Carlo. | +| `statistical_mechanics/kinetics.rs` | `statistical_mechanics::kinetics` | 2,806 | 36 | 3 | 2 | Chemical kinetics: rate laws, deterministic and stochastic reaction networks, enzyme saturation, equilibrium composition, oscillating mechanisms,… | +| `statistical_mechanics/lattice_models.rs` | `statistical_mechanics::lattice_models` | 1,199 | 17 | 0 | 0 | Lattice models: percolation, walks, growth, and avalanches. | +| `statistical_mechanics/md.rs` | `statistical_mechanics::md` | 3,237 | 12 | 36 | 3 | Molecular dynamics: pair potentials, a cell-list force evaluation, a symplectic integrator, thermostats and barostats, and the structural and… | +| `statistics/mod.rs` | `statistics` | 288 | 2 | 0 | 0 | Statistics: descriptive measures, probability distributions, and Fourier utilities. | +| `statistics/descriptive.rs` | `statistics::descriptive` | 114 | 12 | 0 | 0 | Descriptive statistics, error propagation, and weighted means. | +| `statistics/distributions.rs` | `statistics::distributions` | 1,013 | 7 | 13 | 12 | Probability distributions: densities, mass functions, and CDFs. | +| `statistics/fourier.rs` | `statistics::fourier` | 56 | 4 | 0 | 0 | Discrete Fourier transform utilities. | +| `statistics/inference.rs` | `statistics::inference` | 415 | 10 | 0 | 1 | Hypothesis tests and confidence intervals. | +| `statistics/resampling.rs` | `statistics::resampling` | 239 | 4 | 0 | 1 | Resampling methods: bootstrap, BCa bootstrap, permutation tests, and the jackknife. | +| `stochastic/mod.rs` | `stochastic` | 11 | 0 | 0 | 0 | Stochastic processes: Markov chains, Markov chain Monte Carlo, and hidden state models. | +| `stochastic/extreme.rs` | `stochastic::extreme` | 1,852 | 27 | 0 | 1 | Extreme value theory and copulas: the distribution of maxima, the distribution of exceedances, and the dependence structure between them. | +| `stochastic/hmm.rs` | `stochastic::hmm` | 1,451 | 4 | 26 | 4 | Hidden state models: hidden Markov models, smoothing, and particle filters. | +| `stochastic/markov.rs` | `stochastic::markov` | 1,952 | 0 | 37 | 3 | Finite Markov chains and Markov chain Monte Carlo. | +| `stochastic/point_process.rs` | `stochastic::point_process` | 1,373 | 25 | 0 | 0 | Point processes: random collections of points in time or space. | +| `stochastic/queueing.rs` | `stochastic::queueing` | 1,933 | 16 | 8 | 4 | Queueing theory: birth-death queues, Erlang loss and delay formulas, networks of queues, and continuous-time Markov chains. | +| `stochastic/rmt.rs` | `stochastic::rmt` | 1,217 | 18 | 0 | 0 | Random matrix theory: the classical ensembles, their limiting spectral laws, and the local statistics that distinguish correlated spectra from… | +| `stochastic/sde.rs` | `stochastic::sde` | 1,939 | 32 | 0 | 1 | Stochastic differential equations: simulation, convergence, and the densities the paths are distributed by. | +| `stochastic/timeseries.rs` | `stochastic::timeseries` | 3,979 | 28 | 32 | 6 | Time series analysis: correlation structure, stationarity, ARMA models, smoothing, volatility, and change detection. | +| `thermodynamics.rs` | `thermodynamics` | 808 | 52 | 0 | 0 | Thermodynamics: gases, heat transfer, cycles and phase change. | +| `transforms/mod.rs` | `transforms` | 54 | 0 | 0 | 0 | Discrete transforms: FFT (any length), DCT/DST, STFT, wavelets, Hilbert, Laplace inversion, Radon, and spectral estimation. | +| `transforms/dct.rs` | `transforms::dct` | 489 | 12 | 0 | 1 | Discrete cosine, sine, and Hartley transforms. | +| `transforms/fft.rs` | `transforms::fft` | 962 | 21 | 5 | 1 | Fast Fourier transforms. | +| `transforms/hilbert.rs` | `transforms::hilbert` | 570 | 13 | 0 | 0 | Hilbert transform, analytic signals, modulation, empirical mode decomposition, and causality (Kramers-Kronig) tools. | +| `transforms/laplace.rs` | `transforms::laplace` | 435 | 8 | 0 | 0 | Laplace-domain tools: numerical inverse transforms (fixed-Talbot and Gaver-Stehfest), the z-transform, transfer-function responses, and a discrete… | +| `transforms/radon.rs` | `transforms::radon` | 619 | 9 | 0 | 1 | Radon transform and tomographic reconstruction, plus Hankel/Abel transforms and Hough voting. | +| `transforms/spectral.rs` | `transforms::spectral` | 808 | 18 | 0 | 0 | Spectral estimation: periodogram, Welch averaging, multitaper (DPSS), parametric AR models (Burg, Yule-Walker), MUSIC, cross-spectra, coherence,… | +| `transforms/stft.rs` | `transforms::stft` | 695 | 10 | 8 | 1 | Short-time Fourier transform, spectrograms, Goertzel, chirp-z, and constant-Q analysis. | +| `transforms/wavelet.rs` | `transforms::wavelet` | 1,022 | 16 | 0 | 4 | Discrete and continuous wavelet transforms. | +| `transforms/wavelet_tables.rs` | `transforms::wavelet_tables` | 699 | 0 | 0 | 0 | Canonical orthogonal/biorthogonal scaling-filter tables (values from PyWavelets `wavelets_coeffs.template.h`, which in turn credits the classical… | +| `trigonometry.rs` | `trigonometry` | 493 | 33 | 0 | 0 | Triangle solving, trigonometric identities, and hyperbolic functions. | +| `units/mod.rs` | `units` | 813 | 54 | 0 | 0 | Unit conversions, dimensional analysis and the CODATA constants. | +| `units/dimensional.rs` | `units::dimensional` | 974 | 7 | 0 | 0 | Dimensional analysis: Buckingham's theorem, the named groups, natural units and the Planck scale. | +| `units/quantity.rs` | `units::quantity` | 998 | 6 | 18 | 3 | Values that carry their dimensions. | +| `vector_calculus.rs` | `vector_calculus` | 1,195 | 14 | 0 | 0 | Vector calculus operators and field theory for physics grids. | +| `verification/mod.rs` | `verification` | 63 | 0 | 0 | 0 | Kani proof harnesses. | +| `verification/core.rs` | `verification::core` | 27 | 0 | 0 | 0 | Kani harnesses for `crate::core`. | +| `verification/linalg.rs` | `verification::linalg` | 27 | 0 | 0 | 0 | Kani harnesses for `crate::linalg`. | +| `verification/physics.rs` | `verification::physics` | 236 | 0 | 0 | 0 | Kani harnesses for the Part 1 physics and numerics core. | +| `verification/spatial.rs` | `verification::spatial` | 41 | 0 | 0 | 0 | Kani harnesses for `crate::spatial`. | +| `waves.rs` | `waves` | 784 | 48 | 0 | 0 | Wave propagation: mechanical, acoustic and seismic. | diff --git a/src/acoustics.rs b/src/acoustics.rs index 3099894..004ebc0 100644 --- a/src/acoustics.rs +++ b/src/acoustics.rs @@ -1,3 +1,16 @@ +//! Room acoustics, psychoacoustic scales, and musical pitch. +//! +//! Reverberation by Sabine (`RT60 = 0.161 V / A`) and by Eyring, which +//! differ in how they treat a very absorptive room: Sabine's formula is a +//! diffuse-field approximation that never reaches zero however absorptive +//! the surfaces, while Eyring's does. Room modes, critical distance and +//! the mass-law transmission loss follow. +//! +//! The perceptual scales -- mel, bark, ERB, A-weighting, equal-loudness +//! phon -- map physical frequency and level onto what a listener reports, +//! and are fits to listening data rather than derivations. Musical pitch +//! is here too: equal temperament, cents, and MIDI note conversion. + use crate::math::constants::PI; // ── Sabine constant: 0.161 (derived from 24×ln(10)/c ≈ 0.161 for speed of sound ~343 m/s) ── diff --git a/src/astrophysics/collisions.rs b/src/astrophysics/collisions.rs index 96cc45c..0aa62c8 100644 --- a/src/astrophysics/collisions.rs +++ b/src/astrophysics/collisions.rs @@ -1,3 +1,14 @@ +//! Impacts, mergers, and collision probability. +//! +//! Impact geometry and speed (including the gravitational focusing that +//! makes the impact speed at least the escape velocity, however slowly the +//! bodies approach), perfectly inelastic merger of mass and momentum, and +//! the energy released. +//! +//! Crater scaling and the collision probability for objects sharing a +//! volume of space follow, along with the debris-flux relations used for +//! orbital collision risk. + use crate::math::Vec3; use crate::math::constants::G; diff --git a/src/astrophysics/gravitational_waves.rs b/src/astrophysics/gravitational_waves.rs index d2ce23f..d039afd 100644 --- a/src/astrophysics/gravitational_waves.rs +++ b/src/astrophysics/gravitational_waves.rs @@ -1,3 +1,14 @@ +//! Gravitational radiation from a compact binary. +//! +//! Quadrupole-formula results for an inspiralling binary: the emitted +//! luminosity, the wave frequency (twice the orbital frequency), the +//! strain amplitude at a given distance, and the time remaining to merger. +//! +//! The chirp mass `ℳ = (m₁m₂)^(3/5)/(m₁+m₂)^(1/5)` is the combination +//! that governs all of them -- it is the parameter the inspiral waveform +//! actually determines, which is why it is measured far better than either +//! individual mass. + use crate::math::Vec3; use crate::math::constants::{G, C, PI}; diff --git a/src/astrophysics/habitable_zone.rs b/src/astrophysics/habitable_zone.rs index d035c7a..28e79e6 100644 --- a/src/astrophysics/habitable_zone.rs +++ b/src/astrophysics/habitable_zone.rs @@ -1,3 +1,14 @@ +//! Habitable zone boundaries and tidal locking. +//! +//! Inner and outer edges scale as the square root of the stellar +//! luminosity, with the conventional coefficients: 0.95 AU and 1.37 AU per +//! square root of a solar luminosity. +//! +//! Also the mass-luminosity relation for main-sequence stars, equilibrium +//! temperature for a given albedo, and the tidal locking timescale -- +//! which matters here because low-mass stars have close-in habitable +//! zones, so their habitable planets are likely to be locked. + use crate::math::constants; /// Solar luminosity L☉ (W), re-exported from [`constants`]. diff --git a/src/astrophysics/lagrange.rs b/src/astrophysics/lagrange.rs index 265bd90..ca9f0b4 100644 --- a/src/astrophysics/lagrange.rs +++ b/src/astrophysics/lagrange.rs @@ -1,3 +1,14 @@ +//! The five Lagrange points of the circular restricted three-body problem. +//! +//! L1, L2 and L3 lie on the line through the two masses and are found by +//! solving a quintic numerically; L4 and L5 sit at the vertices of +//! equilateral triangles with the two masses and are exact. +//! +//! The collinear points are unstable saddles -- a spacecraft there needs +//! station-keeping -- while L4 and L5 are stable for a mass ratio below +//! about 1/24.96, which is why Jupiter's Trojan asteroids stay put. +//! The Hill radius is here as well. + use crate::math::Vec3; /// Computes the Hill sphere radius: r_H = d (m / 3M)^(1/3). diff --git a/src/astrophysics/magnetosphere.rs b/src/astrophysics/magnetosphere.rs index d6da79d..056dfcf 100644 --- a/src/astrophysics/magnetosphere.rs +++ b/src/astrophysics/magnetosphere.rs @@ -1,3 +1,13 @@ +//! Planetary dipole fields and the magnetopause. +//! +//! The magnetic dipole field in vector form, field-line tracing by +//! integration along the field, and the magnetopause standoff distance -- +//! where magnetic pressure balances the solar wind's dynamic pressure, +//! which is what sets the size of a magnetosphere. +//! +//! Field strength falls as `1/r³`, so the standoff distance depends only +//! weakly (as the sixth root) on the wind pressure. + use crate::math::Vec3; use crate::math::constants::PI; diff --git a/src/astrophysics/mod.rs b/src/astrophysics/mod.rs index 7879fd6..bbb9a8b 100644 --- a/src/astrophysics/mod.rs +++ b/src/astrophysics/mod.rs @@ -1,3 +1,23 @@ +//! Astrodynamics and astrophysics. +//! +//! Orbits are the core. [`kepler`] solves Kepler's equation for elliptic, +//! parabolic and hyperbolic orbits; [`orbital_elements`] converts between +//! state vectors and Keplerian elements; [`maneuvers`] covers Hohmann and +//! bi-elliptic transfers, plane changes, phasing and J2 secular rates; and +//! [`lambert`] solves for the transfer orbit connecting two positions in a +//! given time. +//! +//! [`time_systems`] and [`coords`] are the bookkeeping that makes those +//! answers refer to anything real -- Julian dates, UT1/TAI/TT/TDB, +//! sidereal time, and the equatorial, ecliptic, galactic, horizontal and +//! ITRF frames with precession and nutation. +//! +//! Many-body gravity is handled by [`nbody`] with a leapfrog integrator +//! and [`octree`] for Barnes-Hut O(N log N) forces. The remaining modules +//! cover [`tidal`] forces and Roche limits, [`lagrange`] points, +//! [`gravitational_waves`], [`magnetosphere`] field-line tracing, +//! [`habitable_zone`] boundaries, and [`collisions`] and impact cratering. + pub mod nbody; /// Barnes-Hut octree (moved to `crate::spatial::octree`; re-exported here /// for backwards compatibility). diff --git a/src/astrophysics/nbody.rs b/src/astrophysics/nbody.rs index 415c603..dcd28bf 100644 --- a/src/astrophysics/nbody.rs +++ b/src/astrophysics/nbody.rs @@ -1,3 +1,17 @@ +//! Direct N-body gravitational simulation. +//! +//! Velocity Verlet integration, chosen because it is symplectic: it +//! conserves a nearby "shadow" energy exactly rather than drifting, so +//! orbits stay closed over long integrations where Runge-Kutta of the same +//! order would spiral. +//! +//! Softening replaces `1/r²` with `1/(r² + ε²)` to keep close encounters +//! from producing unbounded accelerations, at the cost of biasing the +//! force at short range. Includes energy and momentum diagnostics, and +//! system generators. +//! +//! Cost is O(N²) per step. For large N use [`crate::astrophysics::octree`]. + use crate::math::Vec3; use crate::math::constants::G; diff --git a/src/astrophysics/orbital_elements.rs b/src/astrophysics/orbital_elements.rs index 21f8367..2b1a6da 100644 --- a/src/astrophysics/orbital_elements.rs +++ b/src/astrophysics/orbital_elements.rs @@ -1,3 +1,18 @@ +//! Keplerian elements: conversion, propagation, and the anomalies. +//! +//! State vectors to elements and back -- semi-major axis, eccentricity, +//! inclination, longitude of ascending node, argument of periapsis and +//! true anomaly -- via the specific orbital energy, the angular momentum +//! and the eccentricity vector. +//! +//! The three anomalies (true, eccentric and mean) and the conversions +//! between them, with Kepler's equation solved by Newton iteration. +//! Periapsis and apoapsis distances and speeds, orbital period, and +//! propagation forward in time complete the module. +//! +//! For a solver that also handles parabolic and hyperbolic orbits and near +//! e = 1, see [`crate::astrophysics::kepler`]. + use crate::math::Vec3; use crate::math::constants::PI; diff --git a/src/astrophysics/tidal.rs b/src/astrophysics/tidal.rs index e1424fa..c390a4e 100644 --- a/src/astrophysics/tidal.rs +++ b/src/astrophysics/tidal.rs @@ -1,3 +1,14 @@ +//! Tidal forces and the Roche limit. +//! +//! The tidal acceleration is the *difference* in gravitational pull across +//! a body, so it falls as `1/r³` rather than `1/r²` -- which is why the +//! Moon raises larger tides on Earth than the far more massive Sun does. +//! +//! The Roche limit is given in both the rigid and fluid forms; the fluid +//! limit is the larger, because a fluid body deforms and so becomes easier +//! to pull apart. Tidal heating, the locking timescale and the tidal +//! tensor complete the module. + use crate::math::Vec3; use crate::math::constants::G; diff --git a/src/atmosphere.rs b/src/atmosphere.rs index 63534c2..d67302c 100644 --- a/src/atmosphere.rs +++ b/src/atmosphere.rs @@ -1,3 +1,15 @@ +//! The standard atmosphere, humidity, and near-surface wind. +//! +//! The barometric formula and the ISA lapse-rate model give pressure, +//! temperature and density against altitude, plus the pressure and density +//! altitudes an aircraft altimeter reports. Humidity is covered by the +//! Magnus formulation for dew point and relative humidity. +//! +//! Wind includes the power-law shear profile, the wind power density that +//! sets a turbine's available energy (`P/A = ½ρv³`, so a doubling of wind +//! speed is eight times the power), the Beaufort scale, and the Coriolis +//! parameter `f = 2Ω sin φ`. + use crate::math::constants; // Atmospheric constants diff --git a/src/chemistry.rs b/src/chemistry.rs index acb80dc..851ee01 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -1,3 +1,14 @@ +//! Reaction kinetics, chemical thermodynamics and electrochemistry. +//! +//! Rate laws for first- and second-order decay and the Arrhenius +//! temperature dependence `k = A exp(−Eₐ/RT)`; the Gibbs free energy and +//! its relation to the equilibrium constant, `ΔG° = −RT ln K`, with the +//! van 't Hoff equation for how K moves with temperature; and Hess's law. +//! +//! Electrochemistry covers the Nernst equation, cell potentials and +//! Faraday electrolysis. Solution chemistry covers pH and pOH, molarity, +//! dilution and osmotic pressure. + use crate::math::constants; /// Faraday constant (C/mol). diff --git a/src/classical.rs b/src/classical.rs index cbf60f9..cd21f36 100644 --- a/src/classical.rs +++ b/src/classical.rs @@ -1,3 +1,17 @@ +//! Newtonian mechanics: kinematics, dynamics, and the harmonic +//! oscillator. +//! +//! Linear and rotational motion under constant acceleration, forces and +//! momentum, work, energy and power, collisions in one dimension from +//! perfectly elastic to perfectly inelastic, moments of inertia for the +//! standard bodies, and circular motion. +//! +//! The oscillator section runs from the undamped period through the +//! damped response -- damping ratio, logarithmic decrement, quality factor +//! -- to the driven steady state and its resonance, and ends with the +//! normal frequencies of two coupled oscillators. For the same problem +//! solved numerically, or with more than two masses, see [`crate::resonance`]. + use crate::math::Vec3; // ── Kinematics ── diff --git a/src/color_science.rs b/src/color_science.rs index 987569e..59f4539 100644 --- a/src/color_science.rs +++ b/src/color_science.rs @@ -1,3 +1,15 @@ +//! Colour: the standard spaces, the transforms between them, and +//! perceptual measures. +//! +//! RGB to and from HSV, HSL and CIE XYZ, with the sRGB transfer function +//! kept separate from the linear values -- the distinction that most +//! colour bugs come from, since averaging or blending is only meaningful +//! in linear light. +//! +//! Also spectral colour (wavelength to RGB), the Planckian locus +//! (blackbody temperature to RGB, and the correlated colour temperature +//! back), relative luminance and the WCAG contrast ratio. + /// Visible spectrum lower bound (nm). const WAVELENGTH_MIN_NM: f64 = 380.0; /// Visible spectrum upper bound (nm). diff --git a/src/continuum_mechanics.rs b/src/continuum_mechanics.rs index eabef29..a3855a7 100644 --- a/src/continuum_mechanics.rs +++ b/src/continuum_mechanics.rs @@ -1,3 +1,15 @@ +//! Stress and strain as tensors, and the yield criteria built on them. +//! +//! The stress tensor with its invariants, principal stresses, and the +//! split into hydrostatic and deviatoric parts -- the split that matters +//! because metals yield on the deviatoric part alone, which is why the von +//! Mises criterion ignores hydrostatic pressure entirely. +//! +//! Strain in both the small-strain and Green-Lagrange forms, the isotropic +//! 3-D Hooke's law `σᵢⱼ = λ δᵢⱼ ε_kk + 2μ εᵢⱼ` and its compliance +//! inverse, plane stress and plane strain, and the von Mises, Tresca, +//! Mohr-Coulomb and Drucker-Prager yield criteria. + use crate::linalg::Mat3; // ── Stress Tensor ──────────────────────────────────────────────────────────── diff --git a/src/control_systems/mod.rs b/src/control_systems/mod.rs index dba087d..c85fc28 100644 --- a/src/control_systems/mod.rs +++ b/src/control_systems/mod.rs @@ -1,3 +1,17 @@ +//! Linear control: system response, stability margins and PID tuning. +//! +//! First- and second-order step and impulse responses in closed form, and +//! the parameters that characterise them -- natural frequency, damping +//! ratio, rise and settling time, percent overshoot, bandwidth. +//! +//! Stability is assessed through the gain and phase margins, which say how +//! much extra gain or delay the loop tolerates before it oscillates. +//! Steady-state error is given by system type. +//! +//! PID tuning uses the Ziegler-Nichols rules. They are a starting point +//! rather than an answer: they were derived for a quarter-amplitude decay +//! and typically give an aggressive loop that wants detuning. + use crate::math::constants::PI; // ── Rise-time and settling-time constants ────────────────────────────── diff --git a/src/curves.rs b/src/curves.rs index eef5c2d..eba22e8 100644 --- a/src/curves.rs +++ b/src/curves.rs @@ -1,3 +1,14 @@ +//! Plane curves: conics, Bézier curves, and parametric families. +//! +//! The conic sections with their eccentricities, foci and the +//! discriminant that classifies a general quadratic; quadratic and cubic +//! Bézier curves in 2-D and 3-D; and parametric circles, ellipses, +//! spirals, Lissajous figures, cycloids and helices. +//! +//! Arc length and signed curvature close the module. For subdivision +//! surfaces and B-spline or NURBS patches see [`crate::mesh::surfaces`]; +//! for space curves with Frenet frames see [`crate::patterns::knots`]. + use crate::math::Vec3; use crate::math::constants::PI; diff --git a/src/electromagnetism.rs b/src/electromagnetism.rs index 1e5530c..0254c3c 100644 --- a/src/electromagnetism.rs +++ b/src/electromagnetism.rs @@ -1,3 +1,17 @@ +//! Classical electromagnetism, from Coulomb's law to radiating dipoles. +//! +//! Electrostatics (Coulomb force and field, potential, Gauss flux, +//! capacitance), magnetostatics (the force on a moving charge, the field +//! of a wire, solenoid and toroid, dipole moments and torques), induction +//! (Faraday and motional EMF, self and mutual inductance), and circuits +//! from Ohm's law through RC transients to the AC steady state -- complex +//! reactance, RLC impedance, resonance, quality factor and bandwidth, +//! power factor, and transformer ratios. +//! +//! The wave section covers the free-space relations: propagation speed, +//! the Poynting magnitude, energy density, the impedance of free space +//! `Z₀ = μ₀c ≈ 376.73 Ω`, dipole radiation and the Larmor power. + use crate::math::{Vec3, constants}; // ── Electrostatics ── diff --git a/src/electronics.rs b/src/electronics.rs index 277c124..f091fa5 100644 --- a/src/electronics.rs +++ b/src/electronics.rs @@ -1,3 +1,14 @@ +//! Semiconductor device physics. +//! +//! Carrier statistics -- the intrinsic concentration, Fermi-Dirac +//! occupancy, the thermal voltage `kT/q` -- and transport by drift and +//! diffusion, linked by the Einstein relation `D/μ = kT/q`. +//! +//! Then the devices: the PN junction's built-in potential and depletion +//! width, the Shockley diode equation, MOSFET drain current in the linear +//! and saturation regimes, and solar cells through open-circuit voltage, +//! fill factor and efficiency. + use crate::math::constants::{E_CHARGE, K_B}; // --------------------------------------------------------------------------- diff --git a/src/fluid_instabilities.rs b/src/fluid_instabilities.rs index b73b20d..d181f8c 100644 --- a/src/fluid_instabilities.rs +++ b/src/fluid_instabilities.rs @@ -1,3 +1,18 @@ +//! When a fluid configuration stops being stable, and how fast it comes +//! apart. +//! +//! Each entry here is a growth rate or a threshold. Rayleigh-Taylor for a +//! heavy fluid over a light one, with the Atwood number and the most +//! unstable wavelength; Kelvin-Helmholtz for a velocity shear; +//! Rayleigh-Bénard convection through the Rayleigh number and its critical +//! value; Plateau-Rayleigh for the breakup of a liquid column into drops; +//! Richtmyer-Meshkov for a shock crossing an interface; and the Jeans +//! criterion, which is the same instability applied to a self-gravitating +//! gas cloud and so sets the mass at which a cloud collapses into a star. +//! +//! The Richardson number and its stability test cover stratified shear +//! flow. + use crate::math::constants; // ── Constants ──────────────────────────────────────────────────────────────── diff --git a/src/fluids.rs b/src/fluids.rs index 088546a..2eccefe 100644 --- a/src/fluids.rs +++ b/src/fluids.rs @@ -1,3 +1,19 @@ +//! Fluid statics and single-phase flow. +//! +//! Statics: hydrostatic pressure, buoyancy and flotation, Pascal's +//! principle. Inviscid flow: continuity, Bernoulli, Torricelli, the +//! Venturi meter. Viscous flow: Stokes drag, the drag equation and +//! terminal velocity, Poiseuille's law, the Darcy-Weisbach head loss. +//! Compressible flow: Mach number, stagnation and isentropic ratios. +//! +//! Surface tension, capillary rise, vorticity, circulation and the +//! Kutta-Joukowski lift round it out, along with the dimensionless groups +//! that decide which regime you are in -- Reynolds, Froude, Weber, Bond, +//! Peclet, Marangoni, Archimedes. +//! +//! These are the closed-form relations. For flow solved on a grid or with +//! particles see [`crate::cfd`]. + use crate::math::constants; // ── Pressure ── diff --git a/src/fractals/mod.rs b/src/fractals/mod.rs index bd94038..e8fce26 100644 --- a/src/fractals/mod.rs +++ b/src/fractals/mod.rs @@ -1,3 +1,19 @@ +//! Fractals: escape-time sets, attractors, automata and noise. +//! +//! The module root holds the classic escape-time sets computed directly -- +//! Mandelbrot with smooth (continuous) iteration counts, Julia, burning +//! ship, Newton fractals and the Sierpinski gasket. +//! +//! The submodules generalise each direction: [`escape_time`] for a generic +//! iteration engine, [`attractors`] for chaotic flows and maps, +//! [`ifs`] for iterated function systems and the chaos game, +//! [`lsystem`] for Lindenmayer rewriting, [`automata`] for cellular +//! automata and growth, and [`noise`] for Perlin, OpenSimplex2, Worley and +//! fBm. +//! +//! For the dynamical-systems view -- Lyapunov exponents and bifurcation -- +//! see [`crate::nonlinear`]. + pub mod attractors; pub mod automata; pub mod escape_time; diff --git a/src/general_relativity.rs b/src/general_relativity.rs index 8676c0f..8695d76 100644 --- a/src/general_relativity.rs +++ b/src/general_relativity.rs @@ -1,3 +1,21 @@ +//! General relativity: black holes and cosmology. +//! +//! The Schwarzschild solution -- the metric components, the horizon at +//! `r_s = 2GM/c²`, proper time and gravitational redshift, the photon +//! sphere at `1.5 r_s` and the innermost stable circular orbit at +//! `3 r_s`, with the effective potential and the orbital energy and +//! angular momentum that produce them. The Kerr solution adds rotation: +//! the horizon, the ergosphere, the shifted ISCO, and the frame-dragging +//! rate. +//! +//! Cosmology covers the Friedmann equation for the Hubble parameter, the +//! critical density, redshift-distance relations, luminosity distance, +//! lookback time, and the scale factor and CMB temperature at a given +//! redshift. +//! +//! For four-vectors and curved-spacetime tensor machinery see +//! [`crate::manifold::spacetime`] and [`crate::manifold::metric`]. + use crate::math::constants::{G, C, PI}; const C2: f64 = C * C; diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs index 12fd5d9..a8c8908 100644 --- a/src/geometry/mod.rs +++ b/src/geometry/mod.rs @@ -1,3 +1,15 @@ +//! Areas, volumes and surface areas of the standard shapes. +//! +//! Plane figures (circle, ellipse, triangle by base-height and by Heron's +//! formula, regular polygon, sector, annulus) and solids (sphere, +//! cylinder, cone, ellipsoid, torus, frustum, capsule), with perimeters +//! and surface areas alongside. +//! +//! Closed-form mensuration only. For triangle solving see +//! [`crate::trigonometry`], for curves and conics see [`crate::curves`], +//! for polygon algorithms such as triangulation and offsetting see +//! [`crate::patterns::polygon_ops`], and for meshes see [`crate::mesh`]. + use crate::math::constants::PI; // --- 2D Areas --- diff --git a/src/geophysics.rs b/src/geophysics.rs index 21415b8..acd1971 100644 --- a/src/geophysics.rs +++ b/src/geophysics.rs @@ -1,3 +1,18 @@ +//! The solid Earth: gravity, seismology, and heat. +//! +//! Gravity surveying -- the latitude formula, the free-air and Bouguer +//! corrections, the resulting anomaly, and Airy isostatic compensation. +//! +//! Seismology: P- and S-wave travel times, epicentral distance from the +//! S−P lag, the Richter and moment magnitude scales, and seismic moment +//! and energy. The moment magnitude is the one to use for large events, +//! because Richter saturates. +//! +//! Heat flow: pressure and temperature with depth, the geothermal +//! gradient, and geothermal power. Plate tectonics closes the module with +//! Euler-pole plate velocities and the square-root-of-age law for +//! seafloor depth. + use crate::math::constants; // ── Gravity & Geoid constants ──────────────────────────────────────── diff --git a/src/gravitation.rs b/src/gravitation.rs index 784315f..2864dfd 100644 --- a/src/gravitation.rs +++ b/src/gravitation.rs @@ -1,3 +1,18 @@ +//! Newtonian gravity and two-body orbits. +//! +//! The inverse-square force and its potential energy, the field of a point +//! mass, escape and circular orbital velocity, and Kepler's third law in +//! both directions. The vis-viva equation `v² = μ(2/r − 1/a)` ties speed +//! to position on any conic orbit, and the specific orbital energy fixes +//! which conic it is. +//! +//! Also the Roche limit, the Hill sphere, the Schwarzschild radius and +//! gravitational time dilation -- the last two are the points at which +//! Newtonian gravity stops being enough; see [`crate::general_relativity`]. +//! +//! For orbits propagated rather than characterised, and for transfers +//! between them, see [`crate::astrophysics`]. + use crate::math::{Vec3, constants}; /// Gravitational force magnitude between two masses: F = G * m1 * m2 / r^2 diff --git a/src/information_theory.rs b/src/information_theory.rs index c9824c6..1e02b60 100644 --- a/src/information_theory.rs +++ b/src/information_theory.rs @@ -1,3 +1,18 @@ +//! Shannon information: entropy, divergence, and channel capacity. +//! +//! Entropy in bits and in nats, the maximum-entropy bound for a given +//! alphabet, and the entropy rate. Then the relations between two +//! distributions: cross entropy, Kullback-Leibler divergence, and the +//! Jensen-Shannon divergence -- which unlike KL is symmetric and bounded, +//! which is why it is the one that behaves like a distance. +//! +//! Mutual information and conditional entropy connect the two, and the +//! binary entropy function gives the capacity of a binary symmetric +//! channel as `C = 1 − H₂(p)`. Fisher information and the Cramér-Rao +//! bound cover the estimation side. +//! +//! For codes that approach these limits see [`crate::codes`]. + // --------------------------------------------------------------------------- // Shannon Entropy // --------------------------------------------------------------------------- diff --git a/src/lib.rs b/src/lib.rs index 3e55681..32e0ae4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,77 @@ +//! A zero-dependency library for physics, mathematics and engineering +//! computation. +//! +//! # What this is for +//! +//! Every routine here is written so that something about it can be +//! *checked*: against a closed form, against a conservation law, against an +//! independent implementation of the same quantity, or against an exact +//! identity over integers. A test that only asserts a function ran is not +//! evidence. Where a result is approximate its error has a stated bound +//! derived from the method; where it is exact the assertion uses `==`. +//! +//! That principle decides the shape of the API. Solvers return +//! [`Result`] rather than panicking on non-convergence, so a caller can +//! tell "did not converge" from "converged to this". Functions validate +//! their arguments, and the guards are written `!(x > 0.0)` rather than +//! `x <= 0.0` so that NaN is rejected too. Physical constants come from +//! one table, [`math::constants`], and the values fixed by the 2019 SI +//! redefinition are exact. +//! +//! # Finding your way around +//! +//! The crate is wide -- 71 top-level modules. `docs/MODULE_MAP.md` in the +//! repository is a generated map of every module with its size and +//! summary. The rough shape: +//! +//! | Area | Modules | +//! |---|---| +//! | Numeric primitives | [`core`], [`math`], [`linalg`], [`numerical`], [`special`] | +//! | Exact and symbolic | [`exact`], [`discrete`], [`graph`], [`codes`] | +//! | Classical physics | [`classical`], [`gravitation`], [`solid_mechanics`], [`continuum_mechanics`], [`resonance`] | +//! | Thermal and statistical | [`thermodynamics`], [`statistical_mechanics`], [`radiation`] | +//! | Electromagnetic | [`electromagnetism`], [`electronics`], [`rf`], [`photonics`], [`plasma`], [`magnetohydrodynamics`] | +//! | Waves and signals | [`waves`], [`optics`], [`acoustics`], [`transforms`], [`dsp`], [`signal_processing`], [`audio`] | +//! | Fluids | [`fluids`], [`cfd`], [`fluid_instabilities`], [`propulsion`] | +//! | Modern physics | [`relativity`], [`general_relativity`], [`quantum`], [`particle_physics`], [`nuclear`], [`neutronics`] | +//! | Space | [`astrophysics`] | +//! | PDE solvers | [`fem`], [`sim`], [`fields`], [`vector_calculus`] | +//! | Life and chemistry | [`chemistry`], [`biophysics`] | +//! | Probability and data | [`statistics`], [`stochastic`], [`monte_carlo`], [`information_theory`], [`learn`] | +//! | Decisions | [`optimization`], [`finance`] | +//! | Geometry | [`geometry`], [`curves`], [`trigonometry`], [`quaternion`], [`manifold`], [`spatial`], [`mesh`] | +//! | Patterns | [`fractals`], [`patterns`], [`nonlinear`] | +//! | Reference and utility | [`units`], [`materials`], [`color_science`], [`control_systems`], [`atmosphere`], [`geophysics`], [`error`] | +//! +//! # Conventions +//! +//! **Units are SI** unless a function's documentation says otherwise, and +//! angles are radians. [`units`] converts, and [`units::quantity`] carries +//! dimensions in the type so that adding a velocity to a time is an error +//! rather than a number. +//! +//! **`f64` throughout**, except where exactness is the point: [`exact`] +//! works over arbitrary-precision integers and rationals, and +//! [`units::dimensional`] computes null spaces over +//! [`exact::rational::Rational`] because a group of quantities is exactly +//! dimensionless or it is not. +//! +//! **Randomness comes from [`monte_carlo::Rng`]**, a linear congruential +//! generator that returns its raw state. The low bits therefore have a +//! short period, so use [`monte_carlo::Rng::below`] for any small-integer +//! draw rather than `next_u64() % n`. +//! +//! # Example +//! +//! ``` +//! use rust_physics_engine::units::quantity::{Dim, Quantity}; +//! +//! let v = Quantity::new(3.0, Dim::new(1, 0, -1, 0, 0, 0, 0)); // m/s +//! let t = Quantity::new(2.0, Dim::TIME); +//! assert_eq!(v.mul(&t).unwrap().dim, Dim::LENGTH); // exactly a length +//! assert!(v.add(&t).is_err()); // and not a time +//! ``` + // Style allowances for a numerics codebase: index loops mirror the math // notation in matrix/stencil kernels, and public signatures follow the // roadmap's frozen APIs even where clippy would prefer fewer arguments or diff --git a/src/linalg/mod.rs b/src/linalg/mod.rs index 6fabc7e..2b38fed 100644 --- a/src/linalg/mod.rs +++ b/src/linalg/mod.rs @@ -1,3 +1,21 @@ +//! Dense and sparse linear algebra. +//! +//! [`Matrix`] is the dense row-major `f64` type everything here operates +//! on. The factorizations are chosen by what the matrix is: [`lu`] with +//! partial pivoting for a general square solve, [`mod@cholesky`] for symmetric +//! positive-definite (half the work, and it fails cleanly if the matrix is +//! not), [`qr`] by Householder reflections for least squares, [`mod@svd`] by +//! one-sided Jacobi for rank and pseudo-inverse, and [`tridiagonal`] for +//! the Thomas algorithm in O(n). +//! +//! [`eigen`] provides the symmetric eigenproblem and general eigenvalues. +//! [`sparse`] provides CSR storage with conjugate gradient and a +//! Jacobi-preconditioned variant, for the large systems that the PDE +//! solvers in [`crate::fem`] produce. +//! +//! Note that `pcg_jacobi`'s tolerance is relative to the norm of the +//! right-hand side, not absolute. + pub mod cholesky; pub mod eigen; pub mod lu; diff --git a/src/magnetohydrodynamics.rs b/src/magnetohydrodynamics.rs index eedea4f..b94a4fe 100644 --- a/src/magnetohydrodynamics.rs +++ b/src/magnetohydrodynamics.rs @@ -1,3 +1,16 @@ +//! Magnetohydrodynamics: a conducting fluid and the field frozen into it. +//! +//! The dimensionless numbers first, because they decide the regime: +//! magnetic Reynolds (advection against diffusion, and so whether the +//! field is frozen in), Lundquist, Hartmann, and the plasma beta -- the +//! ratio of thermal to magnetic pressure, which says whether the field +//! or the gas is in charge. +//! +//! Wave speeds: Alfvén, and the slow and fast magnetosonic branches. +//! Equilibria: pinch pressure balance, the Bennett condition, and the +//! Grad-Shafranov beta limit. Reconnection is covered by the Sweet-Parker +//! rate and the associated electric field. + use crate::math::constants::{K_B, MU_0, PI}; // ── Frozen-in flux threshold ──────────────────────────────────────────── diff --git a/src/materials/common.rs b/src/materials/common.rs index 6c60a04..42bdad8 100644 --- a/src/materials/common.rs +++ b/src/materials/common.rs @@ -1,3 +1,10 @@ +//! Engineering solids: metals, alloys, polymers and ceramics. +//! +//! Density, Young's modulus, yield and tensile strength, Poisson's ratio, +//! thermal conductivity and expansion, and specific heat. Room-temperature +//! values; a specific alloy, temper or grade will differ, sometimes +//! substantially. + /// An engineering material with mechanical and thermal properties. /// /// All values use SI units: diff --git a/src/materials/elements.rs b/src/materials/elements.rs index acff1ec..df1381c 100644 --- a/src/materials/elements.rs +++ b/src/materials/elements.rs @@ -1,3 +1,14 @@ +//! The 118 chemical elements. +//! +//! Atomic number, symbol, name, atomic mass, density, melting and boiling +//! points, and thermal and electrical conductivity, with lookup by atomic +//! number, symbol or name. +//! +//! Densities are for the standard state at room temperature, so gases are +//! quoted at STP. Where an element has no stable isotope the atomic mass +//! is that of the longest-lived one, and properties that have never been +//! measured are absent rather than guessed. + /// Classification of an element within the periodic table. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ElementCategory { diff --git a/src/materials/fluids.rs b/src/materials/fluids.rs index ae9d3c1..92e94f5 100644 --- a/src/materials/fluids.rs +++ b/src/materials/fluids.rs @@ -1,3 +1,13 @@ +//! Common liquids. +//! +//! Density, dynamic and kinematic viscosity, surface tension, speed of +//! sound, specific heat, and boiling and freezing points, at room +//! temperature and one atmosphere. +//! +//! Viscosity is the strongly temperature-dependent one: it can change by a +//! factor of several over a few tens of degrees, so a single figure is +//! only a starting point. + /// A fluid (liquid) with its mechanical and thermal properties at 20 degrees C /// unless otherwise noted in the entry. /// diff --git a/src/materials/gases.rs b/src/materials/gases.rs index 6aa03a3..40fd119 100644 --- a/src/materials/gases.rs +++ b/src/materials/gases.rs @@ -1,3 +1,13 @@ +//! Common gases. +//! +//! Molar mass, density at STP, specific heat at constant pressure and the +//! specific heat ratio `γ`, thermal conductivity, viscosity and the speed +//! of sound. +//! +//! `γ` is the entry most often needed: it fixes the adiabatic relations +//! and the speed of sound `c = √(γRT/M)`, and it follows the molecular +//! structure -- about 5/3 for a monatomic gas, 7/5 for a diatomic one. + /// A gas with its thermodynamic and transport properties at STP. /// /// All values use SI units: diff --git a/src/materials/mod.rs b/src/materials/mod.rs index b209bf1..7b85e4c 100644 --- a/src/materials/mod.rs +++ b/src/materials/mod.rs @@ -1,3 +1,16 @@ +//! Reference property tables. +//! +//! Lookup data rather than computation: [`elements`] carries all 118 +//! elements with atomic mass, density, melting and boiling points and +//! thermal and electrical conductivity; [`common`] carries engineering +//! solids; [`fluids`] carries liquids with density, viscosity, surface +//! tension and speed of sound; and [`gases`] carries molar mass, specific +//! heat ratio and thermal conductivity. +//! +//! Values are room-temperature and one-atmosphere unless stated. They are +//! reference figures for calculation, not a substitute for a datasheet on +//! a specific alloy or grade. + pub mod elements; pub mod common; pub mod gases; diff --git a/src/math.rs b/src/math.rs index a64d5c2..064ac2b 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,3 +1,21 @@ +//! Vectors and the crate's table of physical constants. +//! +//! [`Vec2`] and [`Vec3`] with the usual algebra -- addition, scaling, dot +//! and cross products, norms, normalization, projection, reflection, +//! rotation and interpolation. +//! +//! [`constants`] is the single table the rest of the crate refers back +//! to, and it is deliberately one table: duplicate definitions elsewhere +//! are re-exports of it, and a test enforces that they agree. The values +//! fixed by the 2019 SI redefinition -- `C`, `H`, `HBAR`, `E_CHARGE`, +//! `K_B`, `N_A` -- are exact by definition rather than measured. Constants +//! that are products of others, such as `FARADAY = N_A · E_CHARGE`, are +//! computed from their factors rather than transcribed, so they cannot +//! disagree with them. +//! +//! For the 2022 CODATA set with units attached see +//! [`crate::units::quantity::constants_codata`]. + use std::ops::{Add, Sub, Mul, Neg}; /// 2D vector (fluid grids, planar geometry). diff --git a/src/monte_carlo/mod.rs b/src/monte_carlo/mod.rs index fb76548..d462d97 100644 --- a/src/monte_carlo/mod.rs +++ b/src/monte_carlo/mod.rs @@ -1,3 +1,19 @@ +//! Monte Carlo methods and the random number generator behind them. +//! +//! Integration (plain, 2-D, and importance-sampled), random walks in one +//! to three dimensions, the Wiener and Ornstein-Uhlenbeck processes, +//! Langevin dynamics, and Metropolis-Hastings sampling with a +//! worked Ising example. +//! +//! # A warning about [`Rng`] +//! +//! It is a linear congruential generator that returns its raw state, so +//! the low bits have a short period: `next_u64() % m` for a power-of-two +//! `m` cycles through a handful of values -- `% 2` gives 0,1,0,1 and `% 4` +//! gives 0,3,2,1 forever. Use [`Rng::below`], which takes the high bits, +//! for any small-integer draw. It is adequate for simulation and testing +//! and is not cryptographically secure. + use crate::math::constants::K_B; const LCG_MULTIPLIER: u64 = 6_364_136_223_846_793_005; diff --git a/src/neutronics.rs b/src/neutronics.rs index f0bd794..cb4202d 100644 --- a/src/neutronics.rs +++ b/src/neutronics.rs @@ -1,3 +1,19 @@ +//! Reactor physics: criticality, neutron diffusion, and shielding. +//! +//! Criticality through the six-factor formula and `k_eff`, with +//! reactivity and the reactor period. Neutron transport in diffusion +//! theory: the diffusion coefficient and length, migration length, +//! thermal utilization, and the flux in a slab. +//! +//! Cross sections and reaction rates convert between microscopic and +//! macroscopic pictures, including the 1/v absorption law. Operations +//! covers reactor power, burnup and decay heat. +//! +//! Shielding closes with attenuation, half- and tenth-value layers, and +//! the buildup factor that corrects the exponential law for scattered +//! photons -- the correction that matters, since ignoring it +//! underestimates the dose behind a thick shield. + use crate::math::constants; // ── Neutron Multiplication ── diff --git a/src/nonlinear.rs b/src/nonlinear.rs index c76a068..408a788 100644 --- a/src/nonlinear.rs +++ b/src/nonlinear.rs @@ -1,3 +1,18 @@ +//! Chaos in low-dimensional systems. +//! +//! The logistic map and its period-doubling route to chaos, the Hénon +//! map, and the Lorenz and Rössler flows given as derivative functions to +//! hand to an integrator from [`crate::numerical`]. +//! +//! Lyapunov exponents are the quantitative test: a positive exponent means +//! nearby trajectories separate exponentially, which is what makes a +//! system chaotic rather than merely complicated. Dimension estimators -- +//! box counting and the correlation dimension -- measure the attractor +//! that results. +//! +//! For strange attractors as drawable objects, escape-time fractals and +//! cellular automata see [`crate::fractals`]. + // Chaos theory and nonlinear dynamics: logistic maps, Lyapunov exponents, // strange attractors, fractal dimension estimators, and fixed-point analysis. diff --git a/src/nuclear.rs b/src/nuclear.rs index a0321fa..d8a3d87 100644 --- a/src/nuclear.rs +++ b/src/nuclear.rs @@ -1,3 +1,16 @@ +//! Radioactive decay, nuclear binding, and dosimetry. +//! +//! Exponential decay in its several parameterisations -- decay constant, +//! half-life, mean lifetime -- and activity. Binding energy from the mass +//! defect, binding energy per nucleon (the curve whose peak at iron-56 is +//! why both fission and fusion release energy), and reaction Q-values. +//! +//! Nuclear size follows `R = R₀A^(1/3)`, giving a roughly constant +//! nuclear density. Dosimetry covers absorbed and equivalent dose and the +//! inverse-square falloff of intensity with distance. +//! +//! For reactor-scale neutron transport see [`crate::neutronics`]. + use crate::math::constants; // ── Radioactive Decay ── diff --git a/src/optics.rs b/src/optics.rs index 6d9b890..385ee96 100644 --- a/src/optics.rs +++ b/src/optics.rs @@ -1,3 +1,18 @@ +//! Geometric and wave optics. +//! +//! Refraction by Snell's law, the critical angle for total internal +//! reflection, and the Brewster angle at which reflected light is fully +//! polarized. Imaging through the thin-lens and mirror equations, +//! magnification, lens power, combined focal lengths and the lensmaker's +//! radius of curvature. +//! +//! Wave optics covers single-slit minima, double-slit maxima, the grating +//! equation, thin-film interference, and the Rayleigh resolution criterion +//! `θ = 1.22 λ/D`. Malus's law closes it. +//! +//! For Gaussian beams, fibre optics and ray transfer matrices see +//! [`crate::photonics`]. + use crate::math::constants; // ── Reflection and Refraction ── diff --git a/src/optimization/mod.rs b/src/optimization/mod.rs index 17bbb07..9b11759 100644 --- a/src/optimization/mod.rs +++ b/src/optimization/mod.rs @@ -1,3 +1,17 @@ +//! Optimization: continuous, combinatorial, and strategic. +//! +//! The module root holds the scalar and unconstrained-gradient methods -- +//! golden section and Brent for a bracketed minimum of one variable, then +//! gradient descent with and without momentum, Adam, numerical gradients, +//! and the regression and curve fitting built on them. +//! +//! The submodules take it further: [`lp`] for linear programming and +//! duality, [`integer`] for branch-and-bound and dynamic programming, +//! [`network`] for flows and scheduling, [`convex`] for L-BFGS, proximal +//! methods and ADMM, [`metaheuristics`] for the derivative-free and +//! population-based methods, [`game_theory`] for equilibria and +//! cooperative solutions, and [`least_squares`] for Levenberg-Marquardt. + // Numerical optimization algorithms: 1D search, gradient-based, derivative-free, // and linear/nonlinear least-squares fitting. diff --git a/src/particle_physics.rs b/src/particle_physics.rs index 9af65ac..a4a77d0 100644 --- a/src/particle_physics.rs +++ b/src/particle_physics.rs @@ -1,3 +1,19 @@ +//! Relativistic kinematics and scattering for particle collisions. +//! +//! Invariant mass -- the quantity every collider analysis is built on, +//! because it is the same in every frame -- along with centre-of-mass +//! energy for colliding and fixed-target geometries, and the Lorentz boost +//! of energy and longitudinal momentum. +//! +//! The collider coordinates: rapidity, pseudorapidity and transverse +//! momentum, chosen because rapidity differences are boost invariant along +//! the beam. Scattering by the Rutherford cross section and the +//! Breit-Wigner resonance shape, with the width-lifetime relation +//! `Γτ = ħ` and branching ratios. +//! +//! Also the conservation-law checks -- charge, lepton number, baryon +//! number -- that say whether a proposed reaction can happen at all. + use crate::math::constants::{C, E_CHARGE, HBAR, K_E}; // --------------------------------------------------------------------------- diff --git a/src/photonics.rs b/src/photonics.rs index 330cb02..5a6d804 100644 --- a/src/photonics.rs +++ b/src/photonics.rs @@ -1,3 +1,16 @@ +//! Laser beams, optical fibre, and interferometry. +//! +//! Gaussian beam propagation: waist, Rayleigh range, radius and curvature +//! against distance, divergence, the Gouy phase, and on-axis intensity. +//! +//! Fibre through the numerical aperture, acceptance angle, and the +//! V-number that decides single- versus multi-mode operation, plus +//! attenuation and dispersion broadening. Ray transfer (ABCD) matrices +//! compose optical elements by matrix multiplication. +//! +//! Coherence length and time, fringe visibility, and the Fabry-Pérot +//! transmission with its free spectral range close the module. + use crate::math::constants; // ── Fiber Optics Constants ────────────────────────────────────────────────── diff --git a/src/plasma.rs b/src/plasma.rs index d1dc734..8e65f32 100644 --- a/src/plasma.rs +++ b/src/plasma.rs @@ -1,3 +1,20 @@ +//! Plasma parameters: the characteristic lengths, frequencies and speeds. +//! +//! The Debye length is where it starts -- the distance over which a +//! plasma screens a charge, and therefore the scale below which "plasma" +//! stops being the right description. The Debye number counts particles +//! in that sphere, and a plasma is only collective if that number is +//! large. +//! +//! Frequencies: electron and ion plasma frequencies, and the cyclotron +//! frequencies in a magnetic field, with the associated Larmor radius. +//! Speeds: thermal, ion-acoustic, Alfvén and magnetosonic. Plus magnetic +//! pressure, plasma beta, the skin depth, the Coulomb logarithm and the +//! collision frequency. +//! +//! For a conducting fluid treated as a continuum see +//! [`crate::magnetohydrodynamics`]. + use crate::math::constants::{C, E_CHARGE, EPSILON_0, K_B, M_ELECTRON, MU_0, PI}; /// λD = √(ε₀kT / (nq²)) diff --git a/src/propulsion.rs b/src/propulsion.rs index fd9798b..167f849 100644 --- a/src/propulsion.rs +++ b/src/propulsion.rs @@ -1,3 +1,18 @@ +//! Rocket propulsion and impulsive orbital transfers. +//! +//! The Tsiolkovsky equation `Δv = v_e ln(m₀/m_f)` and the specific +//! impulse and mass ratio around it, thrust with and without the +//! pressure-thrust term, staged Δv, and the gravity-turn loss that makes +//! the ideal Δv an underestimate for a launch. +//! +//! Transfers: Hohmann Δv and time, the bi-elliptic alternative (which +//! wins beyond a radius ratio of about 11.94), and plane changes. Nozzle +//! design covers exit velocity, throat area and the area ratio for a given +//! exit Mach number. +//! +//! For Lambert targeting, J2 effects and orbit propagation see +//! [`crate::astrophysics`]. + use crate::math::constants::{PI, R as GAS_R}; // ── Rocket Equation ── diff --git a/src/quaternion.rs b/src/quaternion.rs index b81fe3d..00edf92 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -1,3 +1,17 @@ +//! Unit quaternions for 3-D rotation. +//! +//! [`Quaternion`] with the full algebra -- Hamilton product, conjugate, +//! inverse, norm and normalization -- and conversion to and from axis-angle, +//! Euler angles and rotation matrices. +//! +//! Quaternions are used for orientation rather than Euler angles because +//! they compose without gimbal lock and interpolate smoothly: [`slerp`] +//! moves along the great circle at constant angular rate, and [`nlerp`] is +//! the cheaper normalized-linear approximation to it. +//! +//! For the Lie-group view of the same object, and for rotations in four +//! dimensions, see [`crate::manifold::lie`]. + use std::ops::{Add, Mul, Neg, Sub}; use crate::math::constants::PI; diff --git a/src/radiation.rs b/src/radiation.rs index 8b863f4..fabc99a 100644 --- a/src/radiation.rs +++ b/src/radiation.rs @@ -1,3 +1,18 @@ +//! Thermal radiation and radiative transfer. +//! +//! The Stefan-Boltzmann law `j = σT⁴`, Wien's displacement of the +//! spectral peak, and the colour and brightness temperatures that invert +//! them. +//! +//! Transfer through an absorbing medium: optical depth, the Beer-Lambert +//! law, and the photon mean free path. Radiation pressure for absorbing +//! and reflecting surfaces. Surface exchange via Kirchhoff's law +//! (emissivity equals absorptivity at equilibrium), view factors, and +//! net radiative exchange between surfaces. +//! +//! For the Planck spectrum itself see [`crate::quantum`]; for reactor and +//! photon shielding see [`crate::neutronics`]. + use crate::math::constants; // Wien's displacement constant in the frequency domain: f_max = WIEN_FREQ_CONSTANT * T diff --git a/src/relativity.rs b/src/relativity.rs index 162d3b9..de35e36 100644 --- a/src/relativity.rs +++ b/src/relativity.rs @@ -1,3 +1,17 @@ +//! Special relativity. +//! +//! The Lorentz factor and the kinematic consequences -- time dilation, +//! length contraction, the velocity-addition law that keeps `c` a limit, +//! and the Lorentz transformation of position and time. +//! +//! Dynamics: relativistic momentum and kinetic energy, total and rest +//! energy, and the energy-momentum relation `E² = (pc)² + (mc²)²`. The +//! relativistic Doppler shift for approaching and receding sources. +//! +//! Also proper time and the spacetime interval, whose sign classifies a +//! separation as timelike, spacelike or null -- the invariant that +//! replaces separate notions of distance and duration. + use crate::math::constants; /// Lorentz factor: γ = 1 / sqrt(1 - v^2/c^2) diff --git a/src/rf.rs b/src/rf.rs index 0f612af..32d4ff6 100644 --- a/src/rf.rs +++ b/src/rf.rs @@ -1,3 +1,18 @@ +//! Radio-frequency engineering: links, lines and noise. +//! +//! Link budgets built from free-space path loss, the Friis transmission +//! equation, antenna gain and effective area, EIRP, beamwidth, +//! directivity and fade margin. +//! +//! Transmission lines: characteristic impedance of coax, velocity factor, +//! guide wavelength, and the mismatch quantities -- VSWR, return loss, +//! mismatch loss. Conductors are covered by the skin depth +//! `δ = √(2ρ/ωμ)`, which is why RF current flows in a thin surface layer. +//! +//! Noise and units: thermal noise power and floor in dBm, signal-to-noise +//! ratio, the Shannon capacity of the resulting channel, and conversions +//! between watts, dBm, ratios and decibels. + use crate::math::constants; // ── EM Spectrum Classification ── diff --git a/src/signal_processing/mod.rs b/src/signal_processing/mod.rs index 8f7e7d0..05bfbfa 100644 --- a/src/signal_processing/mod.rs +++ b/src/signal_processing/mod.rs @@ -1,3 +1,18 @@ +//! Time-domain signal operations and test waveforms. +//! +//! Convolution, cross- and autocorrelation, normalization, windowing, and +//! the simple smoothers -- moving average, exponential moving average, and +//! the median filter, which unlike the other two removes impulsive noise +//! without smearing an edge. +//! +//! Waveform generators (sine, square, sawtooth, triangle, noise, chirp) +//! provide test signals. +//! +//! This module is the elementary layer and re-exports the pieces of +//! [`crate::transforms`] and [`crate::dsp`] most often wanted alongside +//! it. For FFTs of any length go to [`mod@crate::transforms::fft`]; for filter +//! *design* go to [`crate::dsp`]. + // The FFT moved to `transforms::fft` and the window generators and // first-order RC filters to `dsp` (Step 0 of roadmap Part 3); everything // stays importable from its old path here. diff --git a/src/sim/cloth_sim.rs b/src/sim/cloth_sim.rs index d6d2996..5b7c92e 100644 --- a/src/sim/cloth_sim.rs +++ b/src/sim/cloth_sim.rs @@ -1,3 +1,15 @@ +//! Verlet cloth and rope with spring constraints. +//! +//! Particles are advanced by position Verlet, which stores the previous +//! position rather than a velocity: it is stable under stiff constraints +//! and conserves energy far better than explicit Euler at the same step +//! size, because velocity is inferred from the positions rather than +//! integrated separately. +//! +//! Structural, shear and bend springs are then satisfied by iterated +//! position projection -- more iterations gives a stiffer cloth -- with +//! pinning, sphere and floor collision, and wind and gravity forces. + use crate::math::Vec3; const DEFAULT_GRAVITY_Y: f64 = -9.81; diff --git a/src/sim/em_sim.rs b/src/sim/em_sim.rs index 0efff1f..71646a3 100644 --- a/src/sim/em_sim.rs +++ b/src/sim/em_sim.rs @@ -1,3 +1,18 @@ +//! FDTD electromagnetic simulation in one and two dimensions. +//! +//! Explicit leapfrog on a Yee-style grid: the electric and magnetic fields +//! are staggered by half a cell and half a time step, so each is updated +//! from the curl of the other and the scheme is second-order accurate with +//! no matrix to solve. +//! +//! Supports dielectric media, hard and soft sources, PEC (perfectly +//! conducting) walls and Mur first-order absorbing boundaries. Stability +//! requires the Courant condition, and the limit is set by the fastest +//! medium in the grid -- that is, the smallest relative permittivity. +//! +//! For a Yee grid with Berenger split-field PML, photonic band gaps and +//! waveguide cutoff, see [`crate::fem::fdtd`]. + use crate::math::constants::{C, EPSILON_0, MU_0, PI}; // ── Mur ABC boundary storage ── diff --git a/src/sim/fluid_sim.rs b/src/sim/fluid_sim.rs index 2ff5baa..6942eea 100644 --- a/src/sim/fluid_sim.rs +++ b/src/sim/fluid_sim.rs @@ -1,3 +1,15 @@ +//! Compact fluid solvers: column, shallow water, and 2-D Euler. +//! +//! A draining column for the simplest case, a 1-D shallow-water solver, +//! and a 2-D incompressible Euler solver that advects velocity and then +//! restores `∇·u = 0` by pressure projection -- subtracting the gradient +//! of a pressure field found by solving a Poisson equation, which is what +//! makes the result divergence-free. +//! +//! Written to be read and to run interactively. For well-balanced schemes, +//! Riemann solvers and the rest of the research-grade machinery see +//! [`crate::cfd`]. + // Grid-based fluid simulation solvers. // // Three models at increasing fidelity: diff --git a/src/sim/heat_sim.rs b/src/sim/heat_sim.rs index 9440cd3..e7e7d79 100644 --- a/src/sim/heat_sim.rs +++ b/src/sim/heat_sim.rs @@ -1,3 +1,14 @@ +//! Heat conduction and convection-diffusion on a grid. +//! +//! Explicit finite differences in two and three dimensions, with Dirichlet +//! and Neumann boundaries, sources, and an advection term for +//! convection-diffusion. +//! +//! Explicit stepping is only conditionally stable: the step must satisfy +//! `α Δt / Δx² ≤ 1/4` in 2-D and `1/6` in 3-D, so halving the grid spacing +//! quarters the allowable time step. The stability limit is provided as a +//! function rather than left to the caller to remember. + // Heat conduction and convection simulation on structured grids. // // ## Governing PDEs diff --git a/src/sim/mod.rs b/src/sim/mod.rs index 5c1add0..c71fe87 100644 --- a/src/sim/mod.rs +++ b/src/sim/mod.rs @@ -1,3 +1,18 @@ +//! Time-stepping simulation engines. +//! +//! Where the rest of the crate evaluates a relation, these advance a state +//! forward in time: [`rigid_body`] for 3-D dynamics with quaternion +//! orientation and Euler's equations, [`fluid_sim`] for shallow water and +//! 2-D incompressible Euler, [`heat_sim`] for conduction and +//! convection-diffusion, [`wave_sim`] for the wave equation with Mur +//! absorbing boundaries, [`em_sim`] for FDTD electromagnetics, and +//! [`cloth_sim`] for Verlet cloth and rope. +//! +//! These are compact, readable integrators intended for interactive use +//! and for seeing the physics behave. For the research-grade schemes -- +//! Riemann solvers, WENO, lattice Boltzmann, SPH -- see [`crate::cfd`]; +//! for finite elements and a Yee-grid FDTD with PML see [`crate::fem`]. + pub mod fluid_sim; pub mod heat_sim; pub mod em_sim; diff --git a/src/sim/rigid_body.rs b/src/sim/rigid_body.rs index c97b1e6..ba7e316 100644 --- a/src/sim/rigid_body.rs +++ b/src/sim/rigid_body.rs @@ -1,3 +1,19 @@ +//! Rigid body dynamics in three dimensions. +//! +//! State is position, linear velocity, orientation as a unit quaternion, +//! and angular velocity. Rotation uses a quaternion rather than Euler +//! angles because it composes without gimbal lock and stays well +//! conditioned under renormalization. +//! +//! Angular motion follows Euler's equations, which carry the `ω × Iω` +//! term -- the reason a freely spinning body with three distinct moments +//! of inertia tumbles rather than spinning steadily about an intermediate +//! axis. +//! +//! Includes inertia tensors for the standard bodies, force and torque +//! accumulation, sphere-sphere collision detection, and impulse-based +//! collision response with restitution. + use crate::math::Vec3; use crate::quaternion::Quaternion; diff --git a/src/sim/wave_sim.rs b/src/sim/wave_sim.rs index f0c67e2..38120a8 100644 --- a/src/sim/wave_sim.rs +++ b/src/sim/wave_sim.rs @@ -1,3 +1,14 @@ +//! The wave equation in one and two dimensions. +//! +//! Explicit second-order finite differences on `∂²u/∂t² = c²∇²u`, with +//! fixed, free, and Mur first-order absorbing boundaries. The absorbing +//! condition passes a normally-incident wave out of the domain exactly and +//! degrades with the angle of incidence. +//! +//! Stability requires the Courant number `r = cΔt/Δx` to satisfy `r ≤ 1` +//! in 1-D and `r ≤ 1/√2` in 2-D. At exactly `r = 1` in one dimension the +//! scheme is an exact shift and has no dispersion error at all. + // Wave equation simulation on structured grids using finite differences. // // PDE (1D): ∂²u/∂t² = c² ∂²u/∂x² diff --git a/src/solid_mechanics.rs b/src/solid_mechanics.rs index 2bcb3ad..ddf2b15 100644 --- a/src/solid_mechanics.rs +++ b/src/solid_mechanics.rs @@ -1,3 +1,19 @@ +//! Strength of materials: stress, strain, elastic constants and beams. +//! +//! Engineering and true stress and strain, and the elastic constants with +//! the identities that connect them -- any two of `E`, `G`, `K` and `ν` +//! determine the other two for an isotropic material, and the conversions +//! are all here. +//! +//! Beam bending: cantilever and simply-supported deflections, bending +//! moment and stress, and second moments of area for rectangular and +//! circular sections. Design closes with the von Mises equivalent stress, +//! the safety factor, and strain energy density. +//! +//! For the tensor formulation and yield surfaces see +//! [`crate::continuum_mechanics`]; for finite-element beams and modal +//! analysis see [`crate::resonance::structural`]. + use crate::math::constants::PI; // --------------------------------------------------------------------------- diff --git a/src/spatial/octree.rs b/src/spatial/octree.rs index dd16cb5..758f7a4 100644 --- a/src/spatial/octree.rs +++ b/src/spatial/octree.rs @@ -1,3 +1,15 @@ +//! Barnes-Hut octree for N-body force approximation. +//! +//! Direct summation costs O(N²). The octree groups distant bodies and +//! treats each group as a single mass at its centre of mass, which brings +//! the cost to O(N log N). +//! +//! The approximation is controlled by `theta`: a node is used as a whole +//! when its width divided by the distance to it is below that threshold. +//! Smaller `theta` is more accurate and slower, and `theta = 0` degenerates +//! to direct summation. The conventional default of 0.5 is +//! [`BH_THETA`]. + use crate::math::Vec3; use crate::math::constants::G; use crate::astrophysics::nbody::Body; diff --git a/src/thermodynamics.rs b/src/thermodynamics.rs index 7056ac5..a935ffa 100644 --- a/src/thermodynamics.rs +++ b/src/thermodynamics.rs @@ -1,3 +1,21 @@ +//! Thermodynamics: gases, heat transfer, cycles and phase change. +//! +//! The ideal gas law in each of its four solved forms, and the kinetic +//! picture behind it -- average kinetic energy, RMS speed, mean free path. +//! Work and entropy change along isothermal, isobaric and adiabatic paths. +//! +//! Heat transfer by all three mechanisms: Fourier conduction (with an +//! explicit 1-D stepper and its stability limit), Newton's law of cooling +//! and convection, and radiative exchange. The dimensionless groups that +//! classify convection -- Grashof, Rayleigh, Prandtl, Nusselt, Biot -- +//! are here too. +//! +//! Cycles through the Carnot efficiency and the coefficients of +//! performance for refrigerators and heat pumps; phase change through +//! latent heat, Clausius-Clapeyron, boiling-point elevation, freezing-point +//! depression, and wet-steam quality. Temperature scale conversions round +//! it out. + use crate::math::constants; // ── Ideal Gas Law ── diff --git a/src/trigonometry.rs b/src/trigonometry.rs index 9d44af9..ef837df 100644 --- a/src/trigonometry.rs +++ b/src/trigonometry.rs @@ -1,3 +1,17 @@ +//! Triangle solving, trigonometric identities, and hyperbolic functions. +//! +//! The laws of sines and cosines in both directions -- side from angles +//! and angle from sides -- and the SAS triangle area. +//! +//! The identities are provided as functions rather than left to the caller +//! to expand: sum and difference, double and half angle, and +//! product-to-sum. The hyperbolic family includes the reciprocals (`sech`, +//! `csch`, `coth`) and inverses that `f64` does not provide directly. +//! +//! Angle utilities close the module: normalization to `[0, 2π)` or +//! `(−π, π]`, the signed shortest difference between two angles, and +//! classification as acute, right or obtuse. + use crate::math::constants::PI; const TWO_PI: f64 = 2.0 * PI; diff --git a/src/waves.rs b/src/waves.rs index b3ce8f7..18a51d0 100644 --- a/src/waves.rs +++ b/src/waves.rs @@ -1,3 +1,21 @@ +//! Wave propagation: mechanical, acoustic and seismic. +//! +//! The kinematic relations (`v = fλ` and the wavenumber-frequency pair), +//! displacement, energy density and intensity, and the inverse-square +//! falloff of a spherical wave. The Doppler effect in both classical and +//! relativistic forms, with the Mach cone angle for supersonic sources. +//! +//! Standing waves on strings and in open and closed pipes, beats, and +//! superposition. Boundaries are handled by impedance: the reflection and +//! transmission coefficients follow from the impedance mismatch, which is +//! also why they carry a sign. +//! +//! Acoustics covers the speed of sound in a gas, sound pressure level and +//! the decibel scale, and absorption and penetration depth. Seismology +//! covers P-, S-, Rayleigh and Love wave speeds. Diffraction closes with +//! the Fraunhofer single-slit pattern, the Airy disk radius and the +//! Fresnel number. + use crate::math::constants; // ── Wave Basics ── diff --git a/tools/check_module_docs.py b/tools/check_module_docs.py new file mode 100755 index 0000000..b6b71cf --- /dev/null +++ b/tools/check_module_docs.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Fail if any source file lacks a `//!` module summary. + +Rustdoc does not warn about a missing module doc -- `missing_docs` covers +items, not modules -- so an undocumented module shows up as a blank page +in the generated documentation and as an empty row in +`docs/MODULE_MAP.md`. Nothing catches that but this. + +A file passes when its first line of substance (ignoring blank lines and +inner attributes such as `#![allow(...)]`) is a `//!` comment. + + python3 tools/check_module_docs.py +""" + +from __future__ import annotations + +import os +import sys + +SRC = "src" + + +def has_module_doc(path: str) -> bool: + with open(path, encoding="utf-8", errors="replace") as fh: + for line in fh: + stripped = line.strip() + if not stripped or stripped.startswith("#!["): + continue + return stripped.startswith("//!") + return False # an empty file documents nothing + + +def main() -> int: + if not os.path.isdir(SRC): + print("run from the repository root", file=sys.stderr) + return 2 + + missing, total = [], 0 + for root, _dirs, files in os.walk(SRC): + for name in sorted(files): + if not name.endswith(".rs"): + continue + total += 1 + path = os.path.join(root, name) + if not has_module_doc(path): + missing.append(path) + + if missing: + print(f"{len(missing)} of {total} source files have no `//!` module doc:", + file=sys.stderr) + for path in missing: + print(f" {path}", file=sys.stderr) + return 1 + + print(f"all {total} source files carry a module doc") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/gen_module_map.py b/tools/gen_module_map.py new file mode 100755 index 0000000..e1f507d --- /dev/null +++ b/tools/gen_module_map.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +"""Generate docs/MODULE_MAP.md from the source tree. + +The map is generated rather than written by hand so that it cannot drift +out of date: `--check` re-derives it and fails if the committed file +differs, which is what CI runs. + +Every figure in the output comes from parsing the sources -- module +summary from the first `//!` line, counts of public items, lines of +code. Nothing is transcribed. + + python3 tools/gen_module_map.py # rewrite docs/MODULE_MAP.md + python3 tools/gen_module_map.py --check # exit 1 if it is stale +""" + +from __future__ import annotations + +import os +import re +import sys + +SRC = "src" +OUT = "docs/MODULE_MAP.md" + +# Groupings for the top-level listing. Anything not named here is +# collected under "Other" so a new module shows up rather than vanishing. +AREAS: list[tuple[str, list[str]]] = [ + ("Numeric foundations", ["core", "math", "linalg", "numerical", "special", "error"]), + ("Exact and symbolic", ["exact", "discrete", "graph", "codes"]), + ("Classical mechanics", ["classical", "gravitation", "solid_mechanics", + "continuum_mechanics", "resonance", "geophysics"]), + ("Thermal and statistical", ["thermodynamics", "statistical_mechanics", "radiation"]), + ("Electromagnetism", ["electromagnetism", "electronics", "rf", "photonics", + "plasma", "magnetohydrodynamics"]), + ("Waves and signals", ["waves", "optics", "acoustics", "transforms", "dsp", + "signal_processing", "audio"]), + ("Fluids", ["fluids", "cfd", "fluid_instabilities", "propulsion"]), + ("Modern physics", ["relativity", "general_relativity", "quantum", + "particle_physics", "nuclear", "neutronics"]), + ("Space", ["astrophysics"]), + ("PDE solvers", ["fem", "sim", "fields", "vector_calculus"]), + ("Chemistry and life", ["chemistry", "biophysics"]), + ("Probability and data", ["statistics", "stochastic", "monte_carlo", + "information_theory", "learn"]), + ("Decisions", ["optimization", "finance"]), + ("Geometry", ["geometry", "curves", "trigonometry", "quaternion", "manifold", + "spatial", "mesh"]), + ("Patterns and chaos", ["fractals", "patterns", "nonlinear"]), + ("Reference and utility", ["units", "materials", "color_science", + "control_systems", "atmosphere", "verification"]), +] + +PUB_FN = re.compile(r"pub (?:const |async )?fn ") +PUB_TYPE = re.compile(r"^pub (?:struct|enum|trait) ") + + +def scan(path: str) -> dict: + """Public item counts, line count and summary for one source file. + + Items inside `#[cfg(test)]` are skipped by tracking brace depth, so a + test helper never inflates the public surface. + """ + free = meth = types = 0 + depth = 0 + test_depth: int | None = None + summary_parts: list[str] = [] + in_summary = True + total = 0 + + with open(path, encoding="utf-8", errors="replace") as fh: + for line in fh: + total += 1 + stripped = line.strip() + + if in_summary: + if stripped.startswith("//!"): + text = stripped[3:].strip() + if text: + summary_parts.append(text) + elif summary_parts: + in_summary = False + elif stripped and not stripped.startswith("#!["): + in_summary = False + + if test_depth is None and stripped.startswith("#[cfg(test)]"): + test_depth = depth + if test_depth is None: + if PUB_FN.match(stripped) and stripped.startswith("pub "): + if line.startswith("pub "): + free += 1 + else: + meth += 1 + if PUB_TYPE.match(line): + types += 1 + depth += line.count("{") - line.count("}") + if test_depth is not None and depth <= test_depth: + test_depth = None + + summary = " ".join(summary_parts) + # First sentence only, and never longer than a table cell wants. + match = re.match(r"(.+?[.!?])(?:\s|$)", summary) + if match: + summary = match.group(1) + summary = summary.replace("|", r"\|") + if len(summary) > 150: + summary = summary[:147].rsplit(" ", 1)[0] + "…" + return {"free": free, "meth": meth, "types": types, + "lines": total, "summary": summary or "—"} + + +def public_top_levels() -> set[str]: + """Top-level modules declared `pub mod` in lib.rs. + + `verification` is declared as a bare `mod`, so it is compiled and + tested but not part of the public API. Counting it as a public module + would make the totals here disagree with the README. + """ + out = set() + with open(os.path.join(SRC, "lib.rs"), encoding="utf-8") as fh: + for line in fh: + if line.startswith("pub mod "): + out.add(line.strip()[len("pub mod "):].rstrip(";")) + return out + + +def collect() -> dict[str, dict]: + """Every source file, keyed by its Rust module path.""" + out = {} + for root, _dirs, files in os.walk(SRC): + for name in sorted(files): + if not name.endswith(".rs"): + continue + path = os.path.join(root, name) + rel = os.path.relpath(path, SRC) + mod = rel[:-3].replace(os.sep, "::") + if mod.endswith("::mod"): + mod = mod[:-5] + out[mod] = scan(path) | {"path": path, "rel": rel} + return out + + +def render(mods: dict[str, dict]) -> str: + tops = sorted({m.split("::")[0] for m in mods if m != "lib"}) + public = public_top_levels() + private = [t for t in tops if t not in public] + named = {n for _, names in AREAS for n in names} + areas = AREAS + [("Other", sorted(set(tops) - named))] + + total_lines = sum(m["lines"] for m in mods.values()) + total_free = sum(m["free"] for m in mods.values()) + total_meth = sum(m["meth"] for m in mods.values()) + total_types = sum(m["types"] for m in mods.values()) + + L: list[str] = [] + L.append("# Module map") + L.append("") + L.append("**Generated file — do not edit.** Produced by") + L.append("[`tools/gen_module_map.py`](../tools/gen_module_map.py) from the source") + L.append("tree; CI fails if it is out of date. Regenerate with:") + L.append("") + L.append("```bash") + L.append("python3 tools/gen_module_map.py") + L.append("```") + L.append("") + L.append("Every figure below is parsed from the sources. Summaries are the first") + L.append("sentence of each module's `//!` documentation. Public-item counts exclude") + L.append("anything inside `#[cfg(test)]`.") + L.append("") + L.append(f"**{len(mods) - 1} modules** across **{len(public)} public top-level " + f"modules**, **{total_lines:,} lines** in " + f"**{len(mods)} files** (the modules plus the crate root `src/lib.rs`), " + f"**{total_free:,} public functions** and **{total_meth:,} public methods**, " + f"**{total_types:,} public types**.") + if private: + names = ", ".join(f"`{p}`" for p in private) + L.append("") + L.append(f"{names} is compiled and tested but declared `mod` rather than " + "`pub mod`, so it is not part of the public API and is excluded from " + "the module count above.") + L.append("") + + # ---- tree ------------------------------------------------------- + L.append("## Tree") + L.append("") + L.append("```") + L.append(f"src/{' ' * 28}{'lines':>8}") + L.append(f"├── {'lib.rs':<28}{mods['lib']['lines']:>8,} (crate root)") + for i, top in enumerate(tops): + children = sorted(m for m in mods if m.startswith(top + "::")) + last_top = i == len(tops) - 1 + stem = "└── " if last_top else "├── " + tag = "" if top in public else " (private)" + if children: + own = mods.get(top) + n = sum(mods[c]["lines"] for c in children) + (own["lines"] if own else 0) + L.append(f"{stem}{(top + '/'):<28}{n:>8,}{tag}") + pad = " " if last_top else "│ " + for j, child in enumerate(children): + leaf = child.split("::")[-1] + cstem = "└── " if j == len(children) - 1 else "├── " + L.append(f"{pad}{cstem}{(leaf + '.rs'):<24}{mods[child]['lines']:>8,}") + else: + L.append(f"{stem}{(top + '.rs'):<28}{mods[top]['lines']:>8,}{tag}") + L.append("```") + L.append("") + + # ---- by area ---------------------------------------------------- + L.append("## By area") + L.append("") + for area, names in areas: + present = [n for n in names if n in tops] + if not present: + continue + L.append(f"### {area}") + L.append("") + L.append("| Module | Lines | Public fns | Types | What it is |") + L.append("|---|--:|--:|--:|---|") + for name in present: + subs = sorted(m for m in mods + if m != "lib" and (m == name or m.startswith(name + "::"))) + lines = sum(mods[s]["lines"] for s in subs) + fns = sum(mods[s]["free"] + mods[s]["meth"] for s in subs) + types = sum(mods[s]["types"] for s in subs) + summary = mods[name]["summary"] if name in mods else "—" + L.append(f"| **`{name}`** | {lines:,} | {fns:,} | {types:,} | {summary} |") + L.append("") + + # ---- every module ---------------------------------------------- + L.append("## Every module") + L.append("") + L.append("| Path | Module | Lines | Fns | Methods | Types | Summary |") + L.append("|---|---|--:|--:|--:|--:|---|") + for mod in sorted(mods): + if mod == "lib": + continue + m = mods[mod] + L.append(f"| `{m['rel']}` | `{mod}` | {m['lines']:,} | {m['free']} " + f"| {m['meth']} | {m['types']} | {m['summary']} |") + L.append("") + return "\n".join(L) + + +def main() -> int: + if not os.path.isdir(SRC): + print("run from the repository root", file=sys.stderr) + return 2 + text = render(collect()) + check = "--check" in sys.argv + existing = open(OUT, encoding="utf-8").read() if os.path.exists(OUT) else None + if check: + if existing == text: + print(f"{OUT} is up to date") + return 0 + print(f"{OUT} is STALE — run `python3 {sys.argv[0]}` and commit the result", + file=sys.stderr) + return 1 + os.makedirs(os.path.dirname(OUT), exist_ok=True) + with open(OUT, "w", encoding="utf-8") as fh: + fh.write(text) + print(f"wrote {OUT}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From b90cd005109077f85a72ca984d61518242308b8a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 03:05:33 +0000 Subject: [PATCH 4/5] tools: fix two counting bugs and the tree renderer in gen_module_map Both found by Copilot's review on #5, both verified against the source before fixing. Free functions were told from methods by whether the `pub fn` sat at column zero. That is wrong for an indented `pub fn` inside an inline `pub mod { ... }`, which is a free function and not a method. There are 175 of them -- 89 in manifold/clifford.rs across its cl3, pga3, cga3 and sta modules, 85 across the four fractals/ preset modules, and one macro template in units/quantity.rs. Classification now follows the enclosing `impl` block, tracked with a stack so a multi-line impl header with a where clause still lands in the right place. Public types were matched against the raw line, so only those at column zero counted. That missed `CgaObject` inside clifford's `pub mod sta` and the k-d tree type inside spatial/kdtree.rs's macro. Matching the stripped line finds both. Totals: 4,124 free functions and 2,241 methods, against 3,949 and 2,416. The sum is unchanged at 6,365 -- both bugs only moved items between the two columns -- and types go from 429 to 431. The README is updated to match. The tree was rendered in one flat pass that printed each module's last path segment, so anything below the first directory level lost its parent: src/numerical/ode/ came out as an `ode.rs` sibling with adaptive.rs, explicit.rs, implicit.rs and symplectic.rs hoisted up beside bvp.rs, and the ordering broke with them. It is now recursive over the module hierarchy, so ode/ renders as a directory with its four files nested under it and roots.rs sorting after. A directory's figure is its own mod.rs plus everything beneath: ode/ is 740, which is its four files at 729 plus its 11-line mod.rs. The generated map now states both counting rules, including the one neither bug caused: the count is syntactic, so a macro-generated item is counted once where the macro defines it rather than once per expansion. That affects units::quantity's unit_ctor! and spatial::kdtree's macro. Both gates re-tested in each direction. 4,193 lib tests, 577 property tests, 2 doc tests, clippy clean, rustdoc clean under -D warnings, nightly-2025-11-21 clean. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01VUkMGnfUbaopkUJLWeYCYi --- README.md | 4 +- docs/MODULE_MAP.md | 43 +++--- .../gen_module_map.cpython-311.pyc | Bin 0 -> 21234 bytes tools/gen_module_map.py | 128 ++++++++++++++---- 4 files changed, 131 insertions(+), 44 deletions(-) create mode 100644 tools/__pycache__/gen_module_map.cpython-311.pyc diff --git a/README.md b/README.md index 69d89f1..1ad955e 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ assertion uses `==`. | | | |---|---| -| **Public functions and methods** | 6,365 (3,949 free functions, 2,416 methods) | -| **Public types** | 429 structs, enums and traits | +| **Public functions and methods** | 6,365 (4,124 free functions, 2,241 methods) | +| **Public types** | 431 structs, enums and traits | | **Top-level modules** | 71 public, across 296 source files — see the [module map](docs/MODULE_MAP.md) | | **Source** | 266,122 lines of Rust | | **Unit tests** | 4,193 | diff --git a/docs/MODULE_MAP.md b/docs/MODULE_MAP.md index 7ec9dc7..301d3ce 100644 --- a/docs/MODULE_MAP.md +++ b/docs/MODULE_MAP.md @@ -10,14 +10,25 @@ python3 tools/gen_module_map.py Every figure below is parsed from the sources. Summaries are the first sentence of each module's `//!` documentation. Public-item counts exclude -anything inside `#[cfg(test)]`. +anything inside `#[cfg(test)]`, and tell a method from a free function by +the enclosing `impl` block rather than by indentation -- an indented +`pub fn` inside an inline `pub mod` is a free function, and there are 175 +of those. -**295 modules** across **71 public top-level modules**, **266,122 lines** in **296 files** (the modules plus the crate root `src/lib.rs`), **3,949 public functions** and **2,416 public methods**, **429 public types**. +The count is syntactic, so an item generated by a macro is counted once +where the macro defines it rather than once per expansion. That affects +two places: `units::quantity`, whose `unit_ctor!` generates about thirty +constructors from one template, and `spatial::kdtree`, whose macro +generates two tree types from one. + +**295 modules** across **71 public top-level modules**, **266,122 lines** in **296 files** (the modules plus the crate root `src/lib.rs`), **4,124 public functions** and **2,241 public methods**, **431 public types**. `verification` is compiled and tested but declared `mod` rather than `pub mod`, so it is not part of the public API and is excluded from the module count above. ## Tree +A directory's figure is its own `mod.rs` plus everything beneath it. + ``` src/ lines ├── lib.rs 159 (crate root) @@ -196,11 +207,11 @@ src/ lines │ ├── bvp.rs 201 │ ├── integrate.rs 270 │ ├── interpolate.rs 648 -│ ├── ode.rs 11 -│ ├── adaptive.rs 309 -│ ├── explicit.rs 63 -│ ├── implicit.rs 232 -│ ├── symplectic.rs 125 +│ ├── ode/ 740 +│ │ ├── adaptive.rs 309 +│ │ ├── explicit.rs 63 +│ │ ├── implicit.rs 232 +│ │ └── symplectic.rs 125 │ └── roots.rs 280 ├── optics.rs 348 ├── optimization/ 15,189 @@ -449,8 +460,8 @@ src/ lines | **`curves`** | 500 | 25 | 0 | Plane curves: conics, Bézier curves, and parametric families. | | **`trigonometry`** | 493 | 33 | 0 | Triangle solving, trigonometric identities, and hyperbolic functions. | | **`quaternion`** | 562 | 18 | 1 | Unit quaternions for 3-D rotation. | -| **`manifold`** | 20,431 | 720 | 32 | Manifolds and higher-dimensional geometry: generic n-dimensional vectors and tensors, metric-driven curvature, and (in later modules) geodesics,… | -| **`spatial`** | 7,020 | 254 | 24 | Spatial data structures, transforms, geometric primitives, and queries. | +| **`manifold`** | 20,431 | 720 | 33 | Manifolds and higher-dimensional geometry: generic n-dimensional vectors and tensors, metric-driven curvature, and (in later modules) geodesics,… | +| **`spatial`** | 7,020 | 254 | 25 | Spatial data structures, transforms, geometric primitives, and queries. | | **`mesh`** | 6,516 | 136 | 10 | Indexed triangle meshes: construction, mass properties, cleanup, spatial queries, and OBJ/STL interchange. | ### Patterns and chaos @@ -579,11 +590,11 @@ src/ lines | `fluid_instabilities.rs` | `fluid_instabilities` | 451 | 19 | 0 | 0 | When a fluid configuration stops being stable, and how fast it comes apart. | | `fluids.rs` | `fluids` | 709 | 45 | 0 | 0 | Fluid statics and single-phase flow. | | `fractals/mod.rs` | `fractals` | 642 | 10 | 5 | 1 | Fractals: escape-time sets, attractors, automata and noise. | -| `fractals/attractors.rs` | `fractals::attractors` | 1,104 | 7 | 43 | 3 | Strange attractors: 3-D chaotic flows and 2-D chaotic maps with trajectory integration, Lyapunov spectra (Benettin renormalization), Kaplan-Yorke… | -| `fractals/automata.rs` | `fractals::automata` | 2,881 | 18 | 74 | 16 | Cellular automata and growth models: elementary 1-D rules, life-like 2-D automata with pattern/RLE placement, cyclic CA, Langton's ant and… | +| `fractals/attractors.rs` | `fractals::attractors` | 1,104 | 42 | 8 | 3 | Strange attractors: 3-D chaotic flows and 2-D chaotic maps with trajectory integration, Lyapunov spectra (Benettin renormalization), Kaplan-Yorke… | +| `fractals/automata.rs` | `fractals::automata` | 2,881 | 29 | 63 | 16 | Cellular automata and growth models: elementary 1-D rules, life-like 2-D automata with pattern/RLE placement, cyclic CA, Langton's ant and… | | `fractals/escape_time.rs` | `fractals::escape_time` | 1,044 | 24 | 0 | 3 | Escape-time fractals: a generic iteration engine with smooth coloring, orbit traps, and distance estimation, the classic quadratic families… | -| `fractals/ifs.rs` | `fractals::ifs` | 924 | 2 | 28 | 3 | Iterated function systems: the chaos game and deterministic attractor construction (Barnsley, "Fractals Everywhere", 1988), Moran similarity… | -| `fractals/lsystem.rs` | `fractals::lsystem` | 1,056 | 2 | 35 | 4 | Lindenmayer systems: parallel string rewriting with simple, stochastic, and context-sensitive rules, 2-D and 3-D turtle interpretation of the ABOP… | +| `fractals/ifs.rs` | `fractals::ifs` | 924 | 18 | 12 | 3 | Iterated function systems: the chaos game and deterministic attractor construction (Barnsley, "Fractals Everywhere", 1988), Moran similarity… | +| `fractals/lsystem.rs` | `fractals::lsystem` | 1,056 | 25 | 12 | 4 | Lindenmayer systems: parallel string rewriting with simple, stochastic, and context-sensitive rules, 2-D and 3-D turtle interpretation of the ABOP… | | `fractals/noise.rs` | `fractals::noise` | 1,803 | 19 | 21 | 8 | Coherent noise: Perlin gradient noise (Perlin 2002), OpenSimplex2 (ported from K.jpg's reference implementation), value noise, Worley cellular… | | `general_relativity.rs` | `general_relativity` | 427 | 23 | 0 | 0 | General relativity: black holes and cosmology. | | `geometry/mod.rs` | `geometry` | 453 | 30 | 0 | 0 | Areas, volumes and surface areas of the standard shapes. | @@ -618,7 +629,7 @@ src/ lines | `linalg/tridiagonal.rs` | `linalg::tridiagonal` | 233 | 2 | 0 | 0 | Tridiagonal linear solve (Thomas algorithm). | | `magnetohydrodynamics.rs` | `magnetohydrodynamics` | 359 | 19 | 0 | 0 | Magnetohydrodynamics: a conducting fluid and the field frozen into it. | | `manifold/mod.rs` | `manifold` | 105 | 0 | 0 | 0 | Manifolds and higher-dimensional geometry: generic n-dimensional vectors and tensors, metric-driven curvature, and (in later modules) geodesics,… | -| `manifold/clifford.rs` | `manifold::clifford` | 2,830 | 4 | 134 | 1 | Clifford (geometric) algebras Cl(p, q, r): a dense multivector type over any signature, with the geometric/outer/inner products, versors and… | +| `manifold/clifford.rs` | `manifold::clifford` | 2,830 | 93 | 45 | 2 | Clifford (geometric) algebras Cl(p, q, r): a dense multivector type over any signature, with the geometric/outer/inner products, versors and… | | `manifold/dec.rs` | `manifold::dec` | 1,618 | 3 | 30 | 1 | Discrete exterior calculus on triangle meshes: exterior derivatives, diagonal Hodge stars, Laplacians, Hodge decomposition, harmonic forms and… | | `manifold/embedding.rs` | `manifold::embedding` | 1,624 | 40 | 0 | 0 | Manifold learning and dimensionality reduction: spectral embeddings (MDS, Isomap, LLE, Laplacian eigenmaps, diffusion maps), PCA and kernel PCA,… | | `manifold/geodesic.rs` | `manifold::geodesic` | 1,422 | 8 | 22 | 2 | Geodesics, parallel transport, Jacobi fields, and relativistic orbits, all driven by the finite-difference [`Metric`] machinery. | @@ -713,7 +724,7 @@ src/ lines | `spatial/distance.rs` | `spatial::distance` | 495 | 18 | 0 | 0 | Closest-point queries and set distances. | | `spatial/frame.rs` | `spatial::frame` | 215 | 0 | 15 | 1 | Rigid coordinate frames (origin + unit-quaternion rotation). | | `spatial/intersect.rs` | `spatial::intersect` | 908 | 23 | 0 | 1 | Intersection tests between the spatial primitives. | -| `spatial/kdtree.rs` | `spatial::kdtree` | 510 | 0 | 9 | 1 | k-d trees (3-D and 2-D) with median splits, plus a uniform spatial hash for broadphase neighbor queries. | +| `spatial/kdtree.rs` | `spatial::kdtree` | 510 | 0 | 9 | 2 | k-d trees (3-D and 2-D) with median splits, plus a uniform spatial hash for broadphase neighbor queries. | | `spatial/mat4.rs` | `spatial::mat4` | 504 | 0 | 21 | 1 | 4×4 homogeneous transform matrix (row-major storage, column-vector convention: p' = M·p). | | `spatial/octree.rs` | `spatial::octree` | 342 | 1 | 2 | 1 | Barnes-Hut octree for N-body force approximation. | | `spatial/primitives.rs` | `spatial::primitives` | 1,128 | 0 | 63 | 15 | Geometric primitive types shared by the intersection, distance, containment, and acceleration modules. | @@ -763,7 +774,7 @@ src/ lines | `trigonometry.rs` | `trigonometry` | 493 | 33 | 0 | 0 | Triangle solving, trigonometric identities, and hyperbolic functions. | | `units/mod.rs` | `units` | 813 | 54 | 0 | 0 | Unit conversions, dimensional analysis and the CODATA constants. | | `units/dimensional.rs` | `units::dimensional` | 974 | 7 | 0 | 0 | Dimensional analysis: Buckingham's theorem, the named groups, natural units and the Planck scale. | -| `units/quantity.rs` | `units::quantity` | 998 | 6 | 18 | 3 | Values that carry their dimensions. | +| `units/quantity.rs` | `units::quantity` | 998 | 7 | 17 | 3 | Values that carry their dimensions. | | `vector_calculus.rs` | `vector_calculus` | 1,195 | 14 | 0 | 0 | Vector calculus operators and field theory for physics grids. | | `verification/mod.rs` | `verification` | 63 | 0 | 0 | 0 | Kani proof harnesses. | | `verification/core.rs` | `verification::core` | 27 | 0 | 0 | 0 | Kani harnesses for `crate::core`. | diff --git a/tools/__pycache__/gen_module_map.cpython-311.pyc b/tools/__pycache__/gen_module_map.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a00593a7798efa4ba950d528ba75a2a6423b8d47 GIT binary patch literal 21234 zcmcJ1eQ+B`mfs98AU+5ZBtVFt5{Dv15fndG5-mzJC0i0H$+o^lNh{e}62gEaBoLry z0Lg-fx7>>og9>*I>F5YvTNUh{?WJvP%-eW#%4L)IlH8q>KT?1WRYpxk6_rz4b^aq& zbi7yP=yG@Ybq~GFIv``t|GA?{&Y%&nhY`46YAP|LD^H?q!(&MP723 zU5&6`-DMf(HglR0nDdOl3OYYKr8}=vmjPZV^+i}Xsi-38;w;VR!w7e#2hqMgP4=X)(N!}1X>+Z>S;=Y&?q!b z=#aJ^X&Y!-v%m{2G;Jf&T4`DvVw-5J9kIoIzyGp)S2u}-JuXE=sg+XCEo;G2(@D!d^!XBX?&uZZrVHciu zuR|EPtRuCG4S;F5RFg%4U}%32FCXR-7;PQs5B(|n?|RnV1P}+k0k7x|d3hn|k$R6D z8#;Y>c=X8L<2_RXKQ0ER_|T-6mx5u@ZqKCGb9syx zz1@OW^j+~vqyzyl?)LfdGER~41gEA5d?fq)UW?!xANPt<7k_oq=b0o(T_ttxe~uT! z0SV|1U-639kar?1dU;=f!a&W_s2reB32o-ITa=JPt4NsxpJp4FF zflejaU1bXA35s5Yn*xIm zW+^&xTrp2i217ymS~iU~PPvsD6-03Injq#eh*Y0-lW{~*^-X~GlFO(Vu98HhEI19s zCB^Ip?~*o1iiPszcu<^@6ulr#E7cM$H99Q@Jzh{aFrgUSVZj$v$`1I$K0!j?l#SDv zqW6poii5t4`T}VBMW5dn@}VXRicg3A5;!7Kc?3Ns2KX#F(O!$_^@AGdook9+l_>tv zqBP^nVRs-Do>HnXNP(u`JDM%57z1Ivx<$nj@PXwj_>$URZL&Fe6 zeh34Cvwqw=1y1%&0ehd&Qk6f5bhDPKfo8{;^}5n)WzK`&$&a74G7z@#*;lzGA;>0xU~^i2c< z*;EVY7y{`5PccnljEo2U0vUgRiSfrl7EVU&$K8;vVnF2?ARLsy*rst2!w=k}n2?)( z#~2OmV6O`3g_a4=PI@AIYxR$iNeW z{!!_gMC7GnafhaY(lo{qdJvuE8^_opq9oGjM`yek3QzmJFQQk7WW_sT7cN{-40}%w z?>!Z9LH6<;&kO)6#Ix*{t_3{2jCsI4?&W3gD+F`i)j1yEBMsTC01)XWyTz+mn+2DHSsIgmXv52 z*)Y3y39Gh*sFS%FgIp%|$edgzR215EnNm;OD3>L;Vr+A9Ege|~Fs!S?SlS4T4utY5 zBEuxga!`t&azzPl6@XbL>#{wn3m9ZJ8)rk6g_2JQ%^>TurS-CLwkcHgTSFkUGNO&v zgt<_*5EDxJam{=-Z95~N4CPr)HvZr#z&Bo|6c_72`_(zdsD%Ip7UL#BdcBCsMVM7- z^MkjLUpD-}gxU<*o+fFA7(4&28PLezdl(ZR=EnuNvb>eq%1GQ*-7ETY%vF|U&M_?h zgz7|jp{_zqE*U*Dwork+m`iyc5bUx|Th~mLTqQc?ssu*z%1bC|{ftevC5rf=P(pAB zH6@U3q2|JO)C(^EFfS(rXO{AtWg954PBYGn_@w|rs0KC~Itgr{kwR?*S%Dub>twxb zk!{ngP$$&CZ8*ia8r(0SbB`DF;;go;YRBq}s-xue?4Za819`)PDEe6yd1Mks2()x) zBG8~Gp{rs}g~AL|B*bI3n+$dFUN;N@DB@X*AjD6=^fgL`i}<(KkJfchww%&F5AdN@muvELgR{ zKpu6Hd}B9(K9~L5dsL;g|RMTn7VoL#!q_v(=cBHggC%$911DaX42K@6=KavVR0NV^IYi7 zaxF2IuA!H~FrEPd4HPOei@^qN7|R|pNP02Xz#vo~iqciugQOZYkKl!3hl+jRV!}lv zBTo?XzA_DC5o(Z12$9IDqa%+D;m~;ZPQ@sCVFh@+E}dcmYX$|-Kq;g4l=KA|@)12O z)i3mj9f;^#TV9Mpj-D;gyx7z7%mo)zp_e4N&Gm?t zBf6L5Fa8C9eDMbe!UWvsv+w=2`dom%%aP$j6iFya2FfeYWJ!adWvV0FRjqKrX>UL= zP=28rvKW%C`iODp2LA*EML!*!R?3c_-Zy&SsA8aOrkKd<$hqUgisiW@#}AL58rget zgjjztx=EV_6$E!F=nKF?qV{1hJT0CBj*8xo3d=-k8d|BVT0Dg`ae@SsB=C_e2Ffe2 zf4~f***F=~qtM_zmlZ2P3`89zX-Y-5F{4BzKxOp>Mzbksbr5Q@q8p!7IMQ;(lp7Zc z7lQsNUM5wF2&8HR>WD20ANBQ4LS63-OJ1?}B&@RIs?|9hm;kiiH5LXGPQt>97zM-u z0{S_0$t<(n#xyo1xyIQc1kt|Ori`^>b~v-XW%l_@(*^|1t+R(S%`LM>GSyDJlCQru zS44&Q*~N27bAQU*pEma|Gg}NB?`_M}tXpO}%^SZYL3Ai%t&W|TdpyCWyV;&In&sh*qhjw*!T9}XN}$W8@p4D zz3Il@*brbWGkWKiOx>o>>bmaNbuEsh>b9lpwk7GEsc&CqSbW~p8Ml0$ajwT(_2$In zXYIT0x9_^QDb@aLy8T(2m}zN`Jr69BwwC<&o>cpD>GtQ4lv%$qW?3#{90Wo|OTxL> zxpeYw&;6F2$(Eg&s)pN@w<=Rre7cHHR`JXF)$^fzRRe%MaMa%(yfv6Gr5vub!?nzC z)f+RlEu-bGRyqO zZ%9x`TP^nW*I%!eynLQzZ1&eDZ%oE!Qk9$1m78V{{@PkOe<F2_9gjrB=m zea2oNZ;Nk_w=HykW^cc5M{5`NrtCdwd(Yy_Y5Uft;go$BK-JVG+LoLdXG7v@a&!OE zre!^A-GxeNVAv&*CGMXc=s4J<|BEK>AaDGORu#` z#>n4|1gE?GnB=ieNLO|xC<$036mwdcs)_`l#9ZtV$?7qY&11#u5L8pi7lQ8T<}VTz zY>X78Duud)inZlwsI5>O0|JFYrWU+jgbGHccB)RQ8dy%p*n9lB9)3TW(x~2sMNk4g ztAMEZs#-%Z425*ceVHi2L_H-dGf-iG1=dI16GVq4Gb;Uxb&;VX!>Zcw2yQdZ#elq&bXmZn2xYKnzKC9b( zzi#(EPpWQTx^7>pYJa+Ff3j--*UJV9Tu*9K>r}%pY5l*UVR8CeaVi8$fn+nA7^`je zd5H{~Kh^K;+&7^APXpY(J;wjcvIrm9$O@CX7h-nEbvtNBq<&BSlTp=1? z_PUgh7fTrJc!F%W{}N!#&?Tx+(YTlOD{}s7hLgF~qr}eFg>s1Cc?rv5 z>b1HQg6YU<$McL~jq*h}=3ruR$)IVY##Egt!;+6q&}(GRs*+4oV}9`iQcMfaC>z(H zfDri@zmWzHVS3Vi*{JH2LCh@b1#l@vZ2w z-tHuw67i}aeoXS{fq_Ii0OEmOs*Etg@T9vR@8HuN{7b(^Fv~o&SRoTO@NZAN z>v_}vwtty18=j#-%G40$qTbl)2Q>}xr*0luI06;ajBf}a4IH(%ci-BbaHbra(~iwV z8q{a1>K-_p3;k4m;}U|ZkVS_c*lQLx-|oHDn`ryFK4tGp+qeg^thc z8}HjUzPs(69gBS_`*+gz@8B~u82N@AKbW$$rEP5lesgABeY`ckE#A5?8Ph+gZHy1y zj4VVJNA9&I+xFm2)$WPzkLl+QMGq}k6IAVV{C|apg7}0kn&o7oe$WyA->~rW8uME7 zYy4|1uWi)o76N;nnPY}ZmVC8*bz@_(#-W|DZ}3rm9J}_{O>-Mdq0C?h|2k7VwpQfO z6@L}DVhL0%Pv$miF_Lv;K~S59J$IH&Dl_^}e)M6l-9Xxx$V(7HtK6k+Nhxkx=rf!K zYT360)u4fD{Fb1WX`q_EC8%`M0SokG=2&qFW-Yz2*u7Rx%M<4;S-O7=%#tPh$K)(o zKz~fmC&#m7QTs74OBPcfle1(w`Y}159M9?}ro8=$b2^?l=ab`E6S5T;Y_4RS3r;fj z-(rMynq0gV4}7ncIic)9D}jc54YGmeYsBnH@;71LCC~MDR`wPv9u_vNZpjGE z*BN3jX{Cx5s~Ph@d{oG0;LP9AX(4|r{L%7{0Q1TDc%#sYmbD3+-s0|*=#P(SUAwSZ z=y;2pV+2>xrb1g$yc2!7}|f{1t|5ZQ{LWrcy2!|0ia!Rqfrmk{-kM zQPJENWq@n%brae8eH>THHqnI5oFbZ`=I_wCv}!GWl`Gz2=30cUBp0E-TqS_ca^+jh zN=nn#Z3E2hGu6dZmrLY3?1@QAbNh@#cC5Z(CiH1}RCub;(=#=)y_n{u@QZ1lr?+#> z7FNH-$WHv%2s>yi`w5rHjzWFK6eF!)+46ZGHVfO=tU;xr3Z3%sT+7X|1f)v%(}vrkZ} zajtoefn=(jW9FKR_;ha}jxoM(rfJPy+dtO~2|py))A@B+Zpc0xv(F}cJ#eQ)Dqz*_ z!!;qU;4zGcZENN}sHNU-#k*^1vxlA-=5wVvi}|Ef%K<03XV%N>1y|Nzmz_65`m8U| zdIk{rJTxy?*s>El8xS-KXO%P@Z#<|C8LCP-w( zRl4vb6!|RO(#^M_GOSEu=~IlOEZsYWSF3^U2fnXUbUoiu*eQu@sb-fdADIWS1WHzL z_eAR9*9M2ho3nSK?fzT+@xGh8 z7j`GA(~j09Brcs7-$emU!#O0-hBqPd278;CAWb)srqAiFupgDl>`mRg`4sa$BQ7C} zOE2EVLt#TonYyByU1pu7ODK;lgTygTMZRGL^7(HBl(YfC*Fe8=n|jAiBw7}CBzhCQ z(U(b>=zR>n8&NphQC2z^VEs_mf#bOuy{x~bpEt}`oKkUC5$Cdqk17VT8YC%fKq9Rt z>pv8Ck&yH;6#pZ9mRtycvb9gWZ|;n3jd>Tn>f2&<^hEv>A{2eaa186@qK4{(rM44x zYe(b-hDA$2b*sv7g)wr5xy?dyRR}ETuM;mJ^HJIuk$Bjb@C@-?ePvo)jc0P7K1L~8 zMOxw9i92y3=8hW|E~#(D;Y#ZN4tQgYZ(?n{#eauytI#}$@h?j-uGx{S*^$^9 zI}y9Ga1POu``f~J@^{Dh-=L`YA4srbto;tuL$CXOcU=CMpz*65E^xcxkI;Q5a)3f% z!Z=OCbao!hpQ0dZrTpi!3pkBIH;KH0FVq7+7x)(oVGmv6jU0S&Yz;Snu?rnNJ!|<0 zxC)0C)TQIdQ{+6y)8wQ?{(ObHbyajOqlZ7~C7eKyh>$&{frxQzZ0w>NuENdVa9kmx zN6yFrb-6lw{^Fw7AG}IjVX*xrp*{Hn71=WvJ^U%^C<3o>)%6PdThwPO@|*-`D(Yg4 zO*87@g_TmA&Dbnuk73|M0uB}A9j=a3znyO4DWv+{NN}R@_``xX((Df4patDyqYhZS z)-R|hqU-DwUqaVJYI56z#McN;Jdy-os7P~ePYyd-+TFy?&Mstl#CAn0sUH;amBNt| zlzAG>z&beIJE*%)cK2}_p5}C$&O<3o7S8!G9I)tBH|oY*1YV>eSE0r;4NmF!=FEK~ zUN=}fR#Avl<&v{5TWA!{sO0x%&^RJ_sRSf)JTCz)Snb972Q&fZjy@~>%8B%I=^n9T zs_cV6QD>m(VgdL~g5!<3>r`PomAY(Y zw^H*vv2_1c<2p8$-PkPNm0f%1m3q{HF|$lk#F{FfW&OR}7lT*4JrQGTD?fta6X8f_ zMViPIw$?>wjfif~&M-0lKVNwJ^MzU5y!g-Y8qp7K+bOK;ya4qh6T5@7Ui=dj)aq6qk*u`*R?YsL z6d>wguGx+OSEp$H*ZI-b|oBbkC8sp>C9s-#F}Dfp+MpS3Oi*QC9j;)2dn8)8nUnqpGjW5x{Kf+`s^?fEgYMbHtO zf(~1C;E121*5Y9!{*=&$uqMx|iupGUn@6O;2!bsCU@x)Z415kqm|!#OHzYRw_lhCKapT3$JIF%kawRjFbYmH5r?j4KxPTmRtXcuVc^(~H? z_j4AB^xKb`|Az*DRXl2H`;)c(iznbacj9eh!kDh>z?Gh7^?u9aWI%~@ zD3TOJVrJ#z#%x~`6`DGXX7&FR-hZq9{aaF?SS&gZB%KHD?TrcXtqT+J@rB?bw|L^N zajERCEw!#6-%9S4t^SAi;h&OL5m6xi*6sN_QtB^BK-6s2v2`4=h(U&4G8QQtgR&#@ z5j_cX7{oS=e^nV&XVl2k82a!c!1t_DTo4Hds4U2N=NF`csvTYfwDnn$P%0LPi0GCh zdYK;+e@xLCLj_|Ijyy`-ejl+GmsNb5p#KLXAj~8F5CL4EaNe-&irXLdN@9>C6P6IE z88dZIvE$R#PmCnYg$8Nm|E7Kt6Bus+4)ne_PuSn`SqM?FKte+mx2&}%U@yC>jsbv2+$aQ(DfP}oawizWr& zfb4aTnkyQOJNqO&qlg|B$qkO#D=WiN7GR|4IU?zqKMZ zhMDOx*R!}(XAT^ZBTFYDnI;HCiL4F55$fx&XPF1()!7x35j<1&XVVDF_omDD&JI1O z*pRB=(-r*eflO{?0_zj;HUz24ZRyHwvj;QO{=XB3jh}NBeE>Jvi91)`^(?mi#P^PG z368=2spg&OX4u%fk~IS<>i`^_?M~IeUTphl%LiND@4nlO_mp#I8lin>+P-u4g$LC& zs#UwleN_HI`TG@jEAXDG*^x$Ay(3+{WA^zc%JT$uJ#;u{_hvZ5YlnXSQ1p8#t|`sI zXS*qva`Fd;CA)k6$dLWY%{iD|RmHb2-qN zYQbegE{VE#Co5iDHWsBkJz}lr}tuAJu!l} zRQ-W8LdSu$<3QAuan#(=$A{iDzim$JeY4`eV-wPvHz#eoqH} zq8FnV6TcVLXUZMRjKzGM&Dhu7?zq(v-+r@up*y<&L6xIeTV9;RTguU&Mp)IKuIm4E zd-Cbi_qN}=`m4cvgUON8sj4&4y*S12`k@<#;#|`CbX-cDdh;40xEEhmpGzL~nX(S1 zt%FJHV5X@pY2OxWkF_s$pw6aEiOEz`Z@LMs*H?TO`WFdrnqqA1sZ4Ft?N@KTn&?Z_ zcBE@N7VYWUt{9i;aQ*z$Qr-I(?q0ZeBGtX;e#f3<$DX(~ZvEtN%#f+7zHPc?itkU> zpGi0oEY>U;-fuz#_r0M{oWFYEQ)7DYxSD`4RdqI9bv8-wFO3uh3sX^hznh zCY?udFV!r0KCZvlpWeD(P59KIK2z3XY3s41^%x?#@vCK-977d&?xhshkmeeaTtluz zj^}EdmYFJp^Gg!Ul@o8fOitN{${+V4NZGf-qaM!mHXIXS&CZOqCN~P!$GO=0*!q;I zIc;iAnwoRz+QE{nKA18cN}CQPO^32MlGPn4lPhgH);JTDU&I1I{@kPO$#*rpqU(LvJ3)~c9e{<$cdMFWJl z53#YAJjU=lN7bkwge$xJ5%*830b+z%b1s}{9z*IkE+BPL7 z-g#b=w7qZX+{c5(Nornnxp|m$IYOl5!-v_)HpkuNw%2#v*oD(fgY$!!5UDn+=zW2Z z2P#YcB1SpXZT7392;qB~{{X+{ycNFs(fSZ6o4vFqrL?|y2UlQ=Pj(az4c}dHJemB4 zPqBwYh@2(M)qp42n@}%peKYu30Zkp|T)r#t{$8U^WYKEH605XK}EY$?l?R_REXwz`CVzr9XjS0=@EWWtfr@=GAE; zm3h1MS(eozHuHSH5B zSN$Og?9e5u3Uw7?a>-I54!zUA()!jqB412r!KS73>OJGCIvQ!hk2MuG18|%br>pU6 zEZ2ClB96^kj;x-40p?*M#_TT_(L*faJV~k~E5zYxatxgOm=s-43pe5mNykyq!^3n_ zh14;Y+q8((j?pDwY?R{Xr05zvY@@J0L|6VtP^SsMiUe~jcv)euM#^wH8b7v#V`KvC|{!`5F#J znLvJwJy)W^vHddB^c~UDDbzJN)3O{*TZ8@%Mi5y+7NZdg@U6sY6NI$sdN^ z*q?xU*Qp-2`OMaP-`1P5ZB5&@CT&|k6=o076MSWcYN&a!(EakpWO*YjwKq;B?H%f! zvbxe%SJLW&?ikw&C=iEwWwvb~vz_U4Ba zcdqRE3vcKajJIvKZ13t*Rc-02w#87A>q~KcX|4|ua;nG^+n%URnq4WgD{aQPP1aDm zcrs(MPz}Cju5!LIT8TOgwe%AWP@AC^wH2ooz7}Hikk86ozvj%*o;S8fd!n$0>eF0( zlB>^@Tcca)(ZWA#S$9CM|8qTez-at)3yV-Ojp7%-aH@C|3~-9D8(9_m(rOvgopiVjCST`WY~k$59QsB_qB}@<=2IkswL}x|~8-UmXO5ElH~gZ=)zjes4@& zcA{%XuOZ_j)1bO6v`4H&ZrqF%#DGUvucL<1Z{WB^!4nx&Dj+`Y^j;w8#mb*!!sd`VyX7)wpmKHre6c{XL< zn>O#AEz6WUqub^OlIymo%KOsgeY2)aMQ!xt{COPXPE~AAS8Sif>FX0L3+k*0Cv{d9 z=xT!v6(mg?@E2JYG+q%tueY-m%MgCWK_|mmqmldi`lP--!{G?g^+VB-=*Zled90e$ Tq`8`8?#9wNY#)6+Rsa4!x@s>! literal 0 HcmV?d00001 diff --git a/tools/gen_module_map.py b/tools/gen_module_map.py index e1f507d..e5fead8 100755 --- a/tools/gen_module_map.py +++ b/tools/gen_module_map.py @@ -50,19 +50,34 @@ "control_systems", "atmosphere", "verification"]), ] -PUB_FN = re.compile(r"pub (?:const |async )?fn ") -PUB_TYPE = re.compile(r"^pub (?:struct|enum|trait) ") +PUB_FN = re.compile(r"pub (?:const |async |unsafe |extern )*fn ") +PUB_TYPE = re.compile(r"pub (?:struct|enum|trait) ") +IMPL_START = re.compile(r"(?:unsafe\s+)?impl[\s<]") def scan(path: str) -> dict: """Public item counts, line count and summary for one source file. - Items inside `#[cfg(test)]` are skipped by tracking brace depth, so a - test helper never inflates the public surface. + Three things this has to get right, each of which it got wrong first: + + * **Methods against free functions** are told apart by the enclosing + `impl` block, not by indentation. An indented `pub fn` inside + `pub mod presets { ... }` is a free function, and there are 98 of + those; counting indentation put every one of them in the wrong + column. + * **Public types** are matched on the stripped line, so a `pub enum` + inside an inline `pub mod` is counted rather than skipped. + * **Test items** are skipped by tracking brace depth from + `#[cfg(test)]`, so a test helper never inflates the public surface. + + The count is syntactic: a type generated by a macro is counted once + where the macro defines it, not once per expansion. """ free = meth = types = 0 depth = 0 test_depth: int | None = None + impl_stack: list[int] = [] + pending_impl: int | None = None summary_parts: list[str] = [] in_summary = True total = 0 @@ -84,15 +99,31 @@ def scan(path: str) -> dict: if test_depth is None and stripped.startswith("#[cfg(test)]"): test_depth = depth + + # Leave any impl bodies this line closed before classifying it. + while impl_stack and depth <= impl_stack[-1]: + impl_stack.pop() + if test_depth is None: - if PUB_FN.match(stripped) and stripped.startswith("pub "): - if line.startswith("pub "): - free += 1 - else: + if PUB_FN.match(stripped): + if impl_stack: meth += 1 - if PUB_TYPE.match(line): + else: + free += 1 + if PUB_TYPE.match(stripped): types += 1 + + if pending_impl is None and IMPL_START.match(stripped): + pending_impl = depth depth += line.count("{") - line.count("}") + # An impl header can carry a where clause over several lines, so + # the body starts at whichever line actually opens the brace. + if pending_impl is not None: + if depth > pending_impl: + impl_stack.append(pending_impl) + pending_impl = None + elif stripped.endswith(";"): + pending_impl = None # not an impl after all if test_depth is not None and depth <= test_depth: test_depth = None @@ -164,7 +195,16 @@ def render(mods: dict[str, dict]) -> str: L.append("") L.append("Every figure below is parsed from the sources. Summaries are the first") L.append("sentence of each module's `//!` documentation. Public-item counts exclude") - L.append("anything inside `#[cfg(test)]`.") + L.append("anything inside `#[cfg(test)]`, and tell a method from a free function by") + L.append("the enclosing `impl` block rather than by indentation -- an indented") + L.append("`pub fn` inside an inline `pub mod` is a free function, and there are 175") + L.append("of those.") + L.append("") + L.append("The count is syntactic, so an item generated by a macro is counted once") + L.append("where the macro defines it rather than once per expansion. That affects") + L.append("two places: `units::quantity`, whose `unit_ctor!` generates about thirty") + L.append("constructors from one template, and `spatial::kdtree`, whose macro") + L.append("generates two tree types from one.") L.append("") L.append(f"**{len(mods) - 1} modules** across **{len(public)} public top-level " f"modules**, **{total_lines:,} lines** in " @@ -180,27 +220,63 @@ def render(mods: dict[str, dict]) -> str: L.append("") # ---- tree ------------------------------------------------------- + # Rendered recursively over the module hierarchy. Doing it with one + # flat pass loses any directory below the first: numerical/ode/ came + # out as an `ode.rs` sibling with adaptive.rs and friends hoisted up + # beside bvp.rs, which is not where they live. + def children_of(prefix: str) -> list[str]: + """Immediate children of a module path, directories and files.""" + depth = prefix.count("::") + 1 if prefix else 0 + seen = [] + for mod in mods: + if mod == "lib" or mod == prefix: + continue + if prefix and not mod.startswith(prefix + "::"): + continue + parts = mod.split("::") + if len(parts) <= depth: + continue + name = "::".join(parts[:depth + 1]) + if name not in seen: + seen.append(name) + return sorted(seen) + + def subtree_lines(mod: str) -> int: + """Lines in a module and everything beneath it.""" + total = mods[mod]["lines"] if mod in mods else 0 + total += sum(mods[m]["lines"] for m in mods + if m != "lib" and m.startswith(mod + "::")) + return total + + def emit(prefix: str, indent: str) -> None: + kids = children_of(prefix) + for i, kid in enumerate(kids): + last = i == len(kids) - 1 + stem = "└── " if last else "├── " + leaf = kid.split("::")[-1] + grandkids = children_of(kid) + tag = "" + if not prefix and kid not in public: + tag = " (private)" + if grandkids: + label = leaf + "/" + n = subtree_lines(kid) + else: + label = leaf + ".rs" + n = mods[kid]["lines"] + width = max(4, 28 - len(indent)) + L.append(f"{indent}{stem}{label:<{width}}{n:>8,}{tag}") + if grandkids: + emit(kid, indent + (" " if last else "│ ")) + L.append("## Tree") L.append("") + L.append("A directory's figure is its own `mod.rs` plus everything beneath it.") + L.append("") L.append("```") L.append(f"src/{' ' * 28}{'lines':>8}") L.append(f"├── {'lib.rs':<28}{mods['lib']['lines']:>8,} (crate root)") - for i, top in enumerate(tops): - children = sorted(m for m in mods if m.startswith(top + "::")) - last_top = i == len(tops) - 1 - stem = "└── " if last_top else "├── " - tag = "" if top in public else " (private)" - if children: - own = mods.get(top) - n = sum(mods[c]["lines"] for c in children) + (own["lines"] if own else 0) - L.append(f"{stem}{(top + '/'):<28}{n:>8,}{tag}") - pad = " " if last_top else "│ " - for j, child in enumerate(children): - leaf = child.split("::")[-1] - cstem = "└── " if j == len(children) - 1 else "├── " - L.append(f"{pad}{cstem}{(leaf + '.rs'):<24}{mods[child]['lines']:>8,}") - else: - L.append(f"{stem}{(top + '.rs'):<28}{mods[top]['lines']:>8,}{tag}") + emit("", "") L.append("```") L.append("") From 9194eebda4d2fff2af4e6822d4fdec1e83972749 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 03:17:21 +0000 Subject: [PATCH 5/5] docs: add a guide that teaches the library rather than listing it Everything documented so far is reference. The README is a catalogue, MODULE_MAP.md is a table, rustdoc is an index. None of them tells anyone how to actually use this. docs/GUIDE.md is a walk through the library by doing things with it: putting a spacecraft in orbit and transferring it to geostationary, pulling a 440 Hz tone out of noise louder than the signal and filtering away the interference, solving -u'' = f and proving the answer converges at the rate the theory predicts, and the tools for not being wrong. The rule the guide follows is that no code block is written by hand. Each chapter is an example file, and the output quoted is what that file actually prints: examples/guide_02_orbit.rs orbits, elements, Hohmann transfer examples/guide_03_signal.rs FFT, FIR design, Welch PSD examples/guide_04_fem.rs finite elements and convergence rates examples/guide_05_correctness.rs units, formula checking, exact rationals All four assert their own results and are run by CI, so a chapter that describes something that does not work fails the build rather than misleading a reader. The numbers are worth having in the guide precisely because they are real: the orbit chapter prints the ISS's actual 7,673 m/s and 92.4 minutes, the transfer's 3,857 m/s and 5.3 hours; the signal chapter gets 67 dB of rejection; the FEM chapter measures L2 and H1 rates of 2.00 and 1.00 for P1 and 3.00 for P2, against a theory stated before the measurement. The guide also says the things a reference cannot. Why finite elements rather than finite differences, and what the change of question buys. Why an error that merely shrinks is worth nothing while one that shrinks at exactly h-squared settles the matter. Why Buckingham's theorem has to run over exact rationals rather than floats. And a closing section on what will bite you: the LCG's low bits, the conditional stability of explicit stepping, that pcg_jacobi's tolerance is relative to the right-hand side, and that a surprising result may be a documented specification choice rather than a bug. README points at it first in Further reading. 4,193 lib tests, 577 property tests, 2 doc tests, five examples running, clippy clean across all targets, rustdoc clean under -D warnings, both doc gates passing, nightly-2025-11-21 clean. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01VUkMGnfUbaopkUJLWeYCYi --- .github/workflows/ci.yml | 11 + README.md | 6 + docs/GUIDE.md | 441 +++++++++++++++++++++++++++++++ examples/guide_02_orbit.rs | 68 +++++ examples/guide_03_signal.rs | 99 +++++++ examples/guide_04_fem.rs | 94 +++++++ examples/guide_05_correctness.rs | 110 ++++++++ 7 files changed, 829 insertions(+) create mode 100644 docs/GUIDE.md create mode 100644 examples/guide_02_orbit.rs create mode 100644 examples/guide_03_signal.rs create mode 100644 examples/guide_04_fem.rs create mode 100644 examples/guide_05_correctness.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27a29e0..24c7ee6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,17 @@ jobs: - name: Check the README example run: cargo run --example readme_quickstart + # docs/GUIDE.md is written around these four files and quotes their + # real output, so running them is what keeps the guide honest. Each + # one asserts its own results; the printing is for a human reading + # the log. + - name: Check the guide examples + run: | + cargo run --example guide_02_orbit + cargo run --example guide_03_signal + cargo run --example guide_04_fem + cargo run --example guide_05_correctness + # Rustdoc warnings are rendering bugs, not style. `[k]` in a formula # becomes a broken intra-doc link and `` becomes an unclosed # HTML tag that swallows the rest of the line, so the published docs diff --git a/README.md b/README.md index 1ad955e..ef7d1b4 100644 --- a/README.md +++ b/README.md @@ -909,6 +909,12 @@ strict Clippy pass. ## Further reading +- [`docs/GUIDE.md`](docs/GUIDE.md) — **start here.** A walk through the library + by doing things with it: putting a spacecraft in orbit, pulling a tone out of + noise, solving a differential equation and proving the answer converges at + the predicted rate, and the tools for not being wrong. Every code block is a + file in [`examples/`](examples) that CI compiles and runs, and the output + shown is what it actually prints. - [`docs/MODULE_MAP.md`](docs/MODULE_MAP.md) — a generated map of every one of the 295 modules: a size-annotated tree, tables by subject area, and a row per module with its line count, public item counts and summary. Produced by diff --git a/docs/GUIDE.md b/docs/GUIDE.md new file mode 100644 index 0000000..ff75697 --- /dev/null +++ b/docs/GUIDE.md @@ -0,0 +1,441 @@ +# Guide + +A walk through the library by doing things with it, rather than a list of +what it contains. For that list see [the module map](MODULE_MAP.md); for +the API reference run `cargo doc --no-deps --open`. + +Every code block below is a real file in [`examples/`](../examples), compiled +and run by CI. The output shown is what it actually prints. If a chapter +here describes something that does not work, CI fails. + +```bash +cargo run --example guide_02_orbit +cargo run --example guide_03_signal +cargo run --example guide_04_fem +cargo run --example guide_05_correctness +``` + +**Contents** + +1. [Getting your bearings](#1-getting-your-bearings) +2. [A spacecraft in orbit](#2-a-spacecraft-in-orbit) +3. [A tone buried in noise](#3-a-tone-buried-in-noise) +4. [Solving a differential equation](#4-solving-a-differential-equation) +5. [The tools for not being wrong](#5-the-tools-for-not-being-wrong) +6. [Where to look for things](#6-where-to-look-for-things) +7. [Things that will bite you](#7-things-that-will-bite-you) + +--- + +## 1. Getting your bearings + +Add it to `Cargo.toml`: + +```toml +[dependencies] +rust_physics_engine = { git = "https://github.com/Magic-Man-us/RustPhysicsEngine" } +``` + +There is nothing else to install. The crate has no dependencies — `Cargo.lock` +holds exactly one package, itself — so there is no feature matrix to learn and +no transitive tree to audit. + +Three conventions hold everywhere: + +- **SI units, angles in radians.** A function that wants something else says + so in its documentation. +- **`f64`**, except where exactness is the point. [`exact`] works over + arbitrary-precision integers and rationals. +- **Solvers return `Result`.** A solver that cannot converge tells you, rather + than returning a number that looks like an answer. + +The smallest useful thing: + +```rust +use rust_physics_engine::classical::projectile_range; + +// v₀ = 50 m/s, θ = 45°, g = 9.81 m/s² +let range = projectile_range(50.0, std::f64::consts::FRAC_PI_4, 9.81); +// 254.84 m +``` + +--- + +## 2. A spacecraft in orbit + +📄 [`examples/guide_02_orbit.rs`](../examples/guide_02_orbit.rs) + +Orbital mechanics never uses `G` and `M` separately — only their product, the +gravitational parameter μ. That is the first thing to internalise, because +every formula in [`astrophysics`] takes μ. + +```rust +let mu = G * EARTH_MASS; + +// A circular orbit 400 km up, near enough the ISS. +let r = EARTH_RADIUS + 400e3; +let speed = (mu / r).sqrt(); +let position = Vec3::new(r, 0.0, 0.0); +let velocity = Vec3::new(0.0, speed, 0.0); + +let elements = OrbitalElements::from_state_vectors(position, velocity, mu); +``` + +Going from a state vector to Keplerian elements is the first thing you do with +tracking data, because elements are what you can reason about — a position and +velocity tell you where something is, elements tell you what it is doing. + +``` +circular orbit at 400 km + speed 7673 m/s + semi-major 6771.0 km + eccentricity 0.00e0 + period 92.4 min + bound? true +``` + +Those are the real ISS numbers. A circular orbit has `e = 0` to within +rounding, and its period is Kepler's third law — both worth asserting rather +than eyeballing: + +```rust +assert!(elements.eccentricity < 1e-12); +let kepler = 2.0 * PI * (r.powi(3) / mu).sqrt(); +assert!((elements.period(mu) - kepler).abs() < 1e-6); +``` + +### Getting somewhere else + +A Hohmann transfer is two burns: one to enter an ellipse that touches both +circles, one to circularise at the far end. + +```rust +let (dv1, dv2) = hohmann_delta_v(mu, r, 42_164e3); // to geostationary +``` + +``` +Hohmann transfer to geostationary + burn 1 2399 m/s + burn 2 1457 m/s + total 3857 m/s + flight time 5.3 hours +``` + +The transfer ellipse touches both circles, so its semi-major axis is the mean +of the two radii, and half its period is the flight time. You can check the +whole thing closes with vis-viva, `v² = μ(2/r − 1/a)`: + +```rust +let a_transfer = 0.5 * (r + r_geo); +let v_peri = (mu * (2.0 / r - 1.0 / a_transfer)).sqrt(); +assert!((v_peri - (speed + dv1)).abs() < 1e-6); // burn 1 lands you on it +``` + +**Where to go next.** [`astrophysics::kepler`] solves Kepler's equation for +elliptic, parabolic and hyperbolic orbits including e = 0.99; +[`astrophysics::lambert`] finds the transfer connecting two positions in a +given time; [`astrophysics::maneuvers`] covers plane changes, phasing and J2 +drift; [`astrophysics::nbody`] integrates many bodies at once. + +--- + +## 3. A tone buried in noise + +📄 [`examples/guide_03_signal.rs`](../examples/guide_03_signal.rs) + +Two tones and noise at more than the amplitude of the signal. We want the +440 Hz one and not the 2.6 kHz one. + +```rust +let spectrum = rfft(&signal); // real input -> non-negative frequencies only +``` + +`rfft` returns only the non-negative frequencies, which is all a real signal +has: bin `k` sits at `k·fs/n` Hz. + +``` +before filtering + tone found at 439 Hz (magnitude 1797) + interference at 2600 Hz (magnitude 1556) +``` + +Both tones come out clearly despite the noise, because noise spreads across +every bin while a sinusoid concentrates into one. That is the whole reason +the FFT is the first tool you reach for. + +### Filtering + +```rust +// Cutoff is in cycles per sample, so 1 kHz at fs = 8 kHz is 0.125. +let taps = fir_lowpass(101, 1_000.0 / fs, WindowKind::Hamming); +let filtered = fir_apply(&taps, &signal); +``` + +``` +after a 1 kHz low-pass + 440 Hz kept magnitude 1782 + 2.6 kHz rejected magnitude 1 + rejection 67 dB +``` + +More taps means a sharper transition between passband and stopband, at the +cost of more delay and more arithmetic. 101 taps buys 67 dB here. + +### Getting the noise floor instead of the peak + +Welch's method averages periodograms over overlapping segments. It trades +frequency resolution for a reduction in variance, which is what you want when +you care about the noise floor rather than the exact peak: + +```rust +let (freqs, psd) = welch(&signal, fs, 512, 256, WindowKind::Hann); +``` + +**Where to go next.** [`transforms::fft`] handles any length, not just powers +of two — Bluestein's chirp-z covers the prime ones. [`dsp::iir`] has RBJ +biquads and second-order-section cascades when you want a filter that is cheap +rather than linear-phase. [`transforms::wavelet`] is the tool when the +frequency content changes over time. [`transforms::spectral`] adds multitaper +and Lomb–Scargle, the latter for unevenly sampled data. + +--- + +## 4. Solving a differential equation + +📄 [`examples/guide_04_fem.rs`](../examples/guide_04_fem.rs) + +Solve `−u″ = f` on `[0, 1]` with `u(0) = u(1) = 0`. Choosing +`f = π² sin(πx)` makes the exact answer `u = sin(πx)`, which is what makes the +error *measurable* rather than merely plausible. + +```rust +let values = fem_1d_poisson(&f, 0.0, 1.0, (Bc::Dirichlet(0.0), Bc::Dirichlet(0.0)), n)?; +let solution = Fem1dSolution::new(0.0, 1.0, 1, values)?; + +let e_l2 = fem_1d_error_l2(&solution, &exact); +let e_h1 = fem_1d_error_h1_seminorm(&solution, &d_exact); +``` + +``` + P1 elements + cells h L2 error H1 error + 8 0.1250 9.921e-3 2.512e-1 + 16 0.0625 2.487e-3 1.258e-1 + 32 0.0312 6.220e-4 6.295e-2 + 64 0.0156 1.555e-4 3.148e-2 + 128 0.0078 3.888e-5 1.574e-2 + + measured rate L2 2.00 H1 1.00 + theory L2 2.00 H1 1.00 +``` + +**This is the part worth pausing on.** An error that merely shrinks tells you +nothing — almost any wrong method produces a shrinking error. An error that +shrinks at *exactly* `h²` tells you the discretisation is the one you think it +is. The rate is the slope of `log(error)` against `log(h)`, and it is +predicted before it is measured. + +The H1 rate is one lower than L2 because the energy norm measures the +derivative, and differentiating a piecewise polynomial costs you an order. + +Quadratic elements buy an order in each norm on the same mesh: + +``` + P2 elements measured rate L2 3.00 theory 3.00 +``` + +**Why finite elements rather than finite differences.** A finite difference +replaces the derivative with a difference quotient and asks the equation to +hold at grid points. A finite element multiplies by a test function, integrates +by parts, and asks the resulting integral identity to hold across a +finite-dimensional space. That change buys two things: the method needs one +less derivative of the solution to make sense, so a kink in the coefficient is +admissible rather than fatal; and the answer is the *best* approximation in the +space under the energy norm — not close to the best, the best. + +**Where to go next.** [`fem::fem2d`] does triangular elements in the plane: +Poisson, Helmholtz, drum eigenvalues, plane-stress elasticity, transient heat. +[`fem::fdtd`] is Maxwell on a Yee grid with PML. [`fem::spectral_pde`] trades +matrix sparsity for a convergence rate limited only by smoothness. [`cfd`] has +the fluid-specific schemes; [`sim`] has compact readable integrators when you +want to watch something move rather than converge. + +--- + +## 5. The tools for not being wrong + +📄 [`examples/guide_05_correctness.rs`](../examples/guide_05_correctness.rs) + +The two most expensive unit mistakes on record — the Mars Climate Orbiter's +pound-seconds fed to a newton-second interface, and the Gimli Glider's +kilograms of fuel loaded as pounds — were both arithmetic a computer performed +correctly on numbers that meant something other than the receiving code +assumed. Neither would have been caught by testing the arithmetic. + +### Dimensions in the type + +```rust +let work = force.mul(&distance)?; // exactly joules +force.add(&time) // Err: dimension mismatch +``` + +``` + 4.45 N x 2 m = 8.90 m^2 kg s^-2 + adding a force to a time -> dimension mismatch: expected m kg s^-2, found s + sqrt(9 m^2) = 3.0 m +``` + +Multiplication adds the seven exponents, division subtracts them, and a square +root exists only when every one of them is even — there is no square root of a +metre, so that is a refusal rather than a rounding decision. + +### Checking a formula rather than a number + +Both sides of `x + v` are perfectly good floats, so no amount of *running* a +formula finds that mistake. Walking the expression does: + +```rust +let pendulum = Expr::Sqrt(Box::new(Expr::mul(vec![Expr::var("l"), over_g]))); +dimensional_check_formula(&pendulum, &vars)? // -> s +``` + +``` + sqrt(l/g) has dimension s + sin(omega*t) checks out; sin(t) does not +``` + +A transcendental's argument must be dimensionless, because its series adds `x` +to `x³`. `exp(−t/τ)` is meaningful and `exp(−t)` is not — and the difference is +a missing timescale, which is a real bug that produces finite numbers. + +### Buckingham's theorem, exactly + +``` + 4 quantities, rank 3 -> 1 group + exponents (rho, u, d, mu): -1, -1, -1, 1 + that is rho^-1 u^-1 d^-1 mu, which is 1/Re +``` + +The theorem says how many dimensionless groups there are — quantity count +minus the rank of the dimension matrix — not which ones. Any basis of the null +space works, and Reynolds is a particular choice made for physical reasons the +algebra knows nothing about. + +The computation runs over exact `Rational`, not floats, and that is not +fastidiousness: a group is exactly in the null space or it is not, and one that +cancelled to `1e-16` would be a rounding error reported as physics. In floating +point there is no way to tell those apart. + +### Arithmetic without rounding + +``` + 0.1 + 0.2 in f64 = 0.30000000000000004 + 1/10 + 1/5 exact = 3/10 + and 0.1 as an f64 is really 3602879701896397/36028797018963968 +``` + +That last line is the useful one. `0.1` is not one tenth; it is a +power-of-two fraction near it. `Rational::from_f64_exact` gives you the value +the float genuinely holds rather than the decimal it is printed as. + +--- + +## 6. Where to look for things + +| If you want to… | Start at | +|---|---| +| throw, drop, collide, oscillate | [`classical`], [`resonance`] | +| bend or load a structure | [`solid_mechanics`], [`continuum_mechanics`] | +| move heat around | [`thermodynamics`], [`sim::heat_sim`] | +| do circuits or fields | [`electromagnetism`], [`electronics`], [`rf`] | +| filter or transform a signal | [`transforms`], [`dsp`], [`signal_processing`] | +| make or analyse sound | [`audio`], [`acoustics`] | +| move a fluid | [`fluids`] for relations, [`cfd`] for solvers | +| go to orbit | [`astrophysics`], [`propulsion`] | +| do quantum mechanics | [`quantum`] | +| solve a PDE properly | [`fem`] | +| fit or classify data | [`learn`], [`statistics`] | +| optimise something | [`optimization`] | +| price or hedge something | [`finance`] | +| work in more than 3 dimensions | [`manifold`] | +| index or intersect geometry | [`spatial`], [`mesh`] | +| avoid a unit mistake | [`units`] | +| avoid a rounding mistake | [`exact`], [`core`] | + +The [module map](MODULE_MAP.md) has all 295 modules with sizes and summaries. + +--- + +## 7. Things that will bite you + +**`Rng` is a linear congruential generator that returns its raw state.** The +low bits have a short period, so `next_u64() % m` for a power-of-two `m` cycles +through a handful of values — `% 2` gives 0,1,0,1 forever. Use +[`monte_carlo::Rng::below`], which takes the high bits, for any small-integer +draw. It is fine for simulation and not cryptographically secure. + +**Explicit time-stepping is conditionally stable.** Heat needs +`α Δt / Δx² ≤ 1/4` in 2-D and `1/6` in 3-D, so halving the grid spacing +*quarters* the time step. Waves and FDTD need the Courant condition, and the +limit is set by the fastest medium in the grid — for FDTD that means the +*smallest* relative permittivity, not vacuum. + +**`pcg_jacobi`'s tolerance is relative to the norm of the right-hand side**, +not absolute. Passing `1e-13` when your data is at `1e8` asks for something +much weaker than you meant. + +**Reference tables are room-temperature values.** Viscosity in particular can +change by a factor of several over a few tens of degrees; a single figure is a +starting point, not a datasheet. + +**A specification reading is written down where one was needed.** Where a +definition is genuinely ambiguous — the Frobenius number with a unit coin, +Stern–Brocot indexing, which parenthesisation a unit string means — the choice +is stated in the doc comment rather than left implicit. If a result surprises +you, read the doc comment before assuming a bug. + +--- + +[`acoustics`]: ../src/acoustics.rs +[`astrophysics`]: ../src/astrophysics/ +[`astrophysics::kepler`]: ../src/astrophysics/kepler.rs +[`astrophysics::lambert`]: ../src/astrophysics/lambert.rs +[`astrophysics::maneuvers`]: ../src/astrophysics/maneuvers.rs +[`astrophysics::nbody`]: ../src/astrophysics/nbody.rs +[`audio`]: ../src/audio/ +[`cfd`]: ../src/cfd/ +[`classical`]: ../src/classical.rs +[`continuum_mechanics`]: ../src/continuum_mechanics.rs +[`core`]: ../src/core/ +[`dsp`]: ../src/dsp/ +[`dsp::iir`]: ../src/dsp/iir.rs +[`electromagnetism`]: ../src/electromagnetism.rs +[`electronics`]: ../src/electronics.rs +[`exact`]: ../src/exact/ +[`fem`]: ../src/fem/ +[`fem::fdtd`]: ../src/fem/fdtd.rs +[`fem::fem2d`]: ../src/fem/fem2d.rs +[`fem::spectral_pde`]: ../src/fem/spectral_pde.rs +[`finance`]: ../src/finance/ +[`fluids`]: ../src/fluids.rs +[`learn`]: ../src/learn/ +[`manifold`]: ../src/manifold/ +[`mesh`]: ../src/mesh/ +[`monte_carlo::Rng::below`]: ../src/monte_carlo/mod.rs +[`optimization`]: ../src/optimization/ +[`propulsion`]: ../src/propulsion.rs +[`quantum`]: ../src/quantum/ +[`resonance`]: ../src/resonance/ +[`rf`]: ../src/rf.rs +[`signal_processing`]: ../src/signal_processing/ +[`sim`]: ../src/sim/ +[`sim::heat_sim`]: ../src/sim/heat_sim.rs +[`solid_mechanics`]: ../src/solid_mechanics.rs +[`spatial`]: ../src/spatial/ +[`statistics`]: ../src/statistics/ +[`thermodynamics`]: ../src/thermodynamics.rs +[`transforms`]: ../src/transforms/ +[`transforms::fft`]: ../src/transforms/fft.rs +[`transforms::spectral`]: ../src/transforms/spectral.rs +[`transforms::wavelet`]: ../src/transforms/wavelet.rs +[`units`]: ../src/units/ diff --git a/examples/guide_02_orbit.rs b/examples/guide_02_orbit.rs new file mode 100644 index 0000000..ed6527f --- /dev/null +++ b/examples/guide_02_orbit.rs @@ -0,0 +1,68 @@ +//! Guide, chapter 2: a spacecraft in orbit. +//! +//! Run with `cargo run --example guide_02_orbit`. This file is the source +//! for that chapter of `docs/GUIDE.md`, and CI runs it, so the guide +//! cannot describe code that does not work. + +use rust_physics_engine::astrophysics::orbital_elements::OrbitalElements; +use rust_physics_engine::math::constants::{EARTH_MASS, EARTH_RADIUS, G}; +use rust_physics_engine::math::Vec3; +use rust_physics_engine::propulsion::hohmann_delta_v; + +fn main() { + // The gravitational parameter is what orbital mechanics actually uses; + // G and M never appear apart. + let mu = G * EARTH_MASS; + + // A circular orbit 400 km up, near enough the ISS. + let r = EARTH_RADIUS + 400e3; + let speed = (mu / r).sqrt(); + let position = Vec3::new(r, 0.0, 0.0); + let velocity = Vec3::new(0.0, speed, 0.0); + + // Going from a state vector to elements is the first thing you do with + // tracking data, because elements are what you can reason about. + let elements = OrbitalElements::from_state_vectors(position, velocity, mu); + println!("circular orbit at 400 km"); + println!(" speed {:.0} m/s", speed); + println!(" semi-major {:.1} km", elements.semi_major_axis / 1e3); + println!(" eccentricity {:.2e}", elements.eccentricity); + println!(" period {:.1} min", elements.period(mu) / 60.0); + println!(" bound? {}", elements.is_bound()); + + // A circular orbit has e = 0 to within rounding, and its period is + // Kepler's third law. Both are worth asserting rather than eyeballing. + assert!(elements.eccentricity < 1e-12); + let kepler = 2.0 * std::f64::consts::PI * (r.powi(3) / mu).sqrt(); + assert!((elements.period(mu) - kepler).abs() < 1e-6); + + // Now raise it to geostationary. A Hohmann transfer is two burns: one + // to enter an ellipse that touches both circles, one to circularise. + let r_geo = 42_164e3; + let (dv1, dv2) = hohmann_delta_v(mu, r, r_geo); + println!(); + println!("Hohmann transfer to geostationary"); + println!(" burn 1 {:.0} m/s", dv1); + println!(" burn 2 {:.0} m/s", dv2); + println!(" total {:.0} m/s", dv1 + dv2); + + // Both burns are prograde, and the first is the larger of the two -- + // it does most of the work of raising the apoapsis. + assert!(dv1 > 0.0 && dv2 > 0.0); + assert!(dv1 > dv2); + + // The transfer ellipse touches both circles, so its semi-major axis is + // the mean of the two radii and its period follows. Half of that is the + // flight time. + let a_transfer = 0.5 * (r + r_geo); + let transfer_time = std::f64::consts::PI * (a_transfer.powi(3) / mu).sqrt(); + println!(" flight time {:.1} hours", transfer_time / 3600.0); + + // Check the ellipse really does touch both circles, via vis-viva: + // v² = μ(2/r − 1/a). The speed at its low point is the circular speed + // plus the first burn. + let v_peri = (mu * (2.0 / r - 1.0 / a_transfer)).sqrt(); + assert!((v_peri - (speed + dv1)).abs() < 1e-6); + println!(); + println!("vis-viva at perigee agrees with circular speed + burn 1"); +} diff --git a/examples/guide_03_signal.rs b/examples/guide_03_signal.rs new file mode 100644 index 0000000..d71491d --- /dev/null +++ b/examples/guide_03_signal.rs @@ -0,0 +1,99 @@ +//! Guide, chapter 3: finding a tone buried in noise, and filtering it out. +//! +//! Run with `cargo run --example guide_03_signal`. CI runs it too, so the +//! guide chapter built from this file cannot go stale. + +use rust_physics_engine::dsp::fir::{fir_apply, fir_lowpass}; +use rust_physics_engine::dsp::windows::WindowKind; +use rust_physics_engine::monte_carlo::Rng; +use rust_physics_engine::transforms::fft::rfft; +use rust_physics_engine::transforms::spectral::welch; + +fn main() { + let fs = 8_000.0; // sample rate, Hz + let n = 4_096; // a power of two, so the FFT is radix-2 + + // Two tones and a lot of noise. 440 Hz is the one we want; 2,600 Hz is + // interference we intend to filter away. + let mut rng = Rng::new(0x5EED_1234); + let signal: Vec = (0..n) + .map(|i| { + let t = i as f64 / fs; + let wanted = (2.0 * std::f64::consts::PI * 440.0 * t).sin(); + let interference = 0.8 * (2.0 * std::f64::consts::PI * 2_600.0 * t).sin(); + let noise = 1.5 * (rng.next_f64() - 0.5); + wanted + interference + noise + }) + .collect(); + + // rfft returns only the non-negative frequencies, which is all a real + // signal has: bin k is at k·fs/n Hz. + let spectrum = rfft(&signal); + let peak = |from: f64, to: f64| -> (f64, f64) { + let lo = (from * n as f64 / fs) as usize; + let hi = (to * n as f64 / fs) as usize; + let mut best = (0.0, 0.0); + for (k, c) in spectrum.iter().enumerate().take(hi + 1).skip(lo) { + let mag = (c.re * c.re + c.im * c.im).sqrt(); + if mag > best.1 { + best = (k as f64 * fs / n as f64, mag); + } + } + best + }; + + let (f_wanted, m_wanted) = peak(300.0, 600.0); + let (f_interf, m_interf) = peak(2_400.0, 2_800.0); + println!("before filtering"); + println!(" tone found at {f_wanted:.0} Hz (magnitude {m_wanted:.0})"); + println!(" interference at {f_interf:.0} Hz (magnitude {m_interf:.0})"); + + // The FFT recovers both tones despite noise at more than the amplitude + // of the signal, because the noise is spread across every bin while a + // sinusoid concentrates into one. + assert!((f_wanted - 440.0).abs() < 5.0); + assert!((f_interf - 2_600.0).abs() < 5.0); + + // A windowed-sinc low-pass. The cutoff is in cycles per sample, so + // 1,000 Hz at fs = 8 kHz is 0.125. More taps means a sharper edge. + let taps = fir_lowpass(101, 1_000.0 / fs, WindowKind::Hamming); + let filtered = fir_apply(&taps, &signal); + + let spectrum = rfft(&filtered[..n]); + let peak2 = |from: f64, to: f64| -> f64 { + let lo = (from * n as f64 / fs) as usize; + let hi = (to * n as f64 / fs) as usize; + spectrum[lo..=hi] + .iter() + .map(|c| (c.re * c.re + c.im * c.im).sqrt()) + .fold(0.0, f64::max) + }; + let after_wanted = peak2(300.0, 600.0); + let after_interf = peak2(2_400.0, 2_800.0); + println!(); + println!("after a 1 kHz low-pass"); + println!(" 440 Hz kept magnitude {after_wanted:.0}"); + println!(" 2.6 kHz rejected magnitude {after_interf:.0}"); + println!( + " rejection {:.0} dB", + 20.0 * (m_interf / after_interf.max(1e-12)).log10() + ); + + // The tone in the passband survives; the one in the stopband does not. + assert!(after_wanted > 0.5 * m_wanted, "the passband tone was attenuated"); + assert!(after_interf < 0.05 * m_interf, "the stopband tone survived"); + + // Welch's method trades frequency resolution for a variance reduction, + // by averaging periodograms over overlapping segments. It is the right + // tool when you want the noise floor rather than the exact peak. + let (freqs, psd) = welch(&signal, fs, 512, 256, WindowKind::Hann); + let loudest = psd + .iter() + .enumerate() + .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) + .map(|(i, _)| freqs[i]) + .unwrap(); + println!(); + println!("Welch PSD over {} segments peaks at {loudest:.0} Hz", n / 256 - 1); + assert!((loudest - 440.0).abs() < 20.0); +} diff --git a/examples/guide_04_fem.rs b/examples/guide_04_fem.rs new file mode 100644 index 0000000..1658f65 --- /dev/null +++ b/examples/guide_04_fem.rs @@ -0,0 +1,94 @@ +//! Guide, chapter 4: solving a differential equation, and proving the +//! answer converges at the rate the theory predicts. +//! +//! Run with `cargo run --example guide_04_fem`. CI runs it too. + +use rust_physics_engine::fem::fem1d::{ + convergence_rate, fem_1d_error_h1_seminorm, fem_1d_error_l2, fem_1d_poisson, + fem_1d_quadratic, Bc, Fem1dSolution, +}; +use std::f64::consts::PI; + +fn main() { + // Solve −u″ = f on [0, 1] with u(0) = u(1) = 0. + // + // Choosing f = π² sin(πx) means the exact answer is u = sin(πx), which + // is what makes the error measurable rather than merely plausible. A + // solver you cannot check against a closed form is a solver you are + // trusting rather than testing. + let f = |x: f64| PI * PI * (PI * x).sin(); + let exact = |x: f64| (PI * x).sin(); + let d_exact = |x: f64| PI * (PI * x).cos(); + + println!("−u\u{2033} = \u{3c0}\u{b2}sin(\u{3c0}x) on [0,1], u(0) = u(1) = 0"); + println!("exact solution u = sin(\u{3c0}x)\n"); + + // Refine the mesh and watch the error fall. + let counts = [8usize, 16, 32, 64, 128]; + let mut hs = Vec::new(); + let mut l2 = Vec::new(); + let mut h1 = Vec::new(); + + println!(" P1 elements"); + println!(" {:>6} {:>6} {:>12} {:>12}", "cells", "h", "L2 error", "H1 error"); + for &n in &counts { + let values = fem_1d_poisson(&f, 0.0, 1.0, (Bc::Dirichlet(0.0), Bc::Dirichlet(0.0)), n) + .expect("the Poisson problem is well posed"); + let solution = Fem1dSolution::new(0.0, 1.0, 1, values).expect("nodal values fit P1"); + + let h = 1.0 / n as f64; + let e_l2 = fem_1d_error_l2(&solution, &exact); + let e_h1 = fem_1d_error_h1_seminorm(&solution, &d_exact); + println!(" {n:>6} {h:>6.4} {e_l2:>12.3e} {e_h1:>12.3e}"); + hs.push(h); + l2.push(e_l2); + h1.push(e_h1); + } + + // The rate is the slope of log(error) against log(h). For linear + // elements the theory says 2 in L2 and 1 in H1 -- one order is lost to + // differentiating, because the energy norm measures the derivative. + let rate_l2 = convergence_rate(&l2, &hs).expect("enough refinements"); + let rate_h1 = convergence_rate(&h1, &hs).expect("enough refinements"); + println!("\n measured rate L2 {rate_l2:.2} H1 {rate_h1:.2}"); + println!(" theory L2 2.00 H1 1.00"); + assert!((rate_l2 - 2.0).abs() < 0.1, "L2 rate {rate_l2} is not 2"); + assert!((rate_h1 - 1.0).abs() < 0.1, "H1 rate {rate_h1} is not 1"); + + // Quadratic elements buy an order in each norm for the same mesh. + let one = |_: f64| 1.0; + let zero = |_: f64| 0.0; + let mut hs2 = Vec::new(); + let mut l2_p2 = Vec::new(); + + println!("\n P2 elements"); + println!(" {:>6} {:>6} {:>12}", "cells", "h", "L2 error"); + for &n in &counts[..4] { + let values = fem_1d_quadratic( + &one, + &zero, + &f, + 0.0, + 1.0, + (Bc::Dirichlet(0.0), Bc::Dirichlet(0.0)), + n, + ) + .expect("well posed"); + let solution = Fem1dSolution::new(0.0, 1.0, 2, values).expect("nodal values fit P2"); + let h = 1.0 / n as f64; + let e = fem_1d_error_l2(&solution, &exact); + println!(" {n:>6} {h:>6.4} {e:>12.3e}"); + hs2.push(h); + l2_p2.push(e); + } + let rate_p2 = convergence_rate(&l2_p2, &hs2).expect("enough refinements"); + println!("\n measured rate L2 {rate_p2:.2}"); + println!(" theory L2 3.00"); + assert!((rate_p2 - 3.0).abs() < 0.15, "P2 L2 rate {rate_p2} is not 3"); + + // What makes this a *proof* rather than a plot is that the rate is + // predicted before it is measured. An error that merely shrinks tells + // you nothing; an error that shrinks at exactly h² tells you the + // discretisation is the one you think it is. + println!("\nboth rates match the theory, so the discretisation is correct"); +} diff --git a/examples/guide_05_correctness.rs b/examples/guide_05_correctness.rs new file mode 100644 index 0000000..0b596e0 --- /dev/null +++ b/examples/guide_05_correctness.rs @@ -0,0 +1,110 @@ +//! Guide, chapter 5: the tools for not being wrong. +//! +//! Dimensions checked in the type, arithmetic without rounding, and +//! Buckingham's theorem as an exact null space. +//! +//! Run with `cargo run --example guide_05_correctness`. CI runs it too. + +use rust_physics_engine::exact::rational::Rational; +use rust_physics_engine::exact::symbolic::Expr; +use rust_physics_engine::units::dimensional::{buckingham_pi, dimensional_check_formula}; +use rust_physics_engine::units::quantity::{parse_quantity, unit_convert, Dim, Quantity}; + +fn main() { + // ---- 1. dimensions travel with the value ------------------------- + // + // The Mars Climate Orbiter was lost to arithmetic a computer performed + // correctly on numbers that meant something other than the receiving + // code assumed. A Quantity carries seven exponents, so the mistake + // becomes a type error instead of a trajectory. + let force = Quantity::newtons(4.45); + let distance = Quantity::meters(2.0); + let work = force.mul(&distance).expect("force times distance"); + + println!("dimensions"); + println!(" 4.45 N x 2 m = {:.2} {}", work.value, work.dim); + assert_eq!(work.dim, Dim::new(2, 1, -2, 0, 0, 0, 0)); // joules, exactly + + let time = Quantity::seconds(3.0); + println!(" adding a force to a time -> {}", force.add(&time).unwrap_err()); + assert!(force.add(&time).is_err()); + + // Square roots only exist when every exponent is even, which is a + // refusal rather than a rounding decision: there is no square root of + // a metre. + let area = Quantity::new(9.0, Dim::new(2, 0, 0, 0, 0, 0, 0)); + println!(" sqrt(9 m^2) = {:.1} {}", area.sqrt().unwrap().value, area.sqrt().unwrap().dim); + assert!(Quantity::meters(9.0).sqrt().is_err()); + + // Parsing and conversion, for when the units arrive as text. + let g = parse_quantity("9.81 m/s^2").expect("a readable quantity"); + println!(" \"9.81 m/s^2\" parses to {} {}", g.value, g.dim); + let mps = unit_convert(3.6, "km/h", "m/s").expect("same dimension"); + println!(" 3.6 km/h = {mps:.1} m/s"); + assert!((mps - 1.0).abs() < 1e-12); + + // ---- 2. checking a formula, not a number ------------------------- + // + // Both sides of `x + v` are perfectly good floats, so no amount of + // running the formula finds the mistake. Walking the expression does. + let vars = [ + ("l", Dim::LENGTH), + ("g", Dim::new(1, 0, -2, 0, 0, 0, 0)), + ("t", Dim::TIME), + ("omega", Dim::new(0, 0, -1, 0, 0, 0, 0)), + ]; + let over_g = Expr::pow(Expr::var("g"), Expr::c(-1.0)); + let pendulum = Expr::Sqrt(Box::new(Expr::mul(vec![Expr::var("l"), over_g]))); + println!("\nformula checking"); + println!(" sqrt(l/g) has dimension {}", dimensional_check_formula(&pendulum, &vars).unwrap()); + assert_eq!(dimensional_check_formula(&pendulum, &vars).unwrap(), Dim::TIME); + + // A transcendental needs a pure number, because its series adds x to + // x³. sin(omega*t) is meaningful; sin(t) is a missing timescale. + let good = Expr::Sin(Box::new(Expr::mul(vec![Expr::var("omega"), Expr::var("t")]))); + let bad = Expr::Sin(Box::new(Expr::var("t"))); + println!(" sin(omega*t) checks out; sin(t) does not"); + assert!(dimensional_check_formula(&good, &vars).is_ok()); + assert!(dimensional_check_formula(&bad, &vars).is_err()); + + // ---- 3. Buckingham's theorem, exactly ---------------------------- + // + // Pipe flow: density, speed, diameter, viscosity. Four quantities, + // three independent dimensions, so exactly one dimensionless group. + let pipe = [ + Dim::new(-3, 1, 0, 0, 0, 0, 0), // density kg/m^3 + Dim::new(1, 0, -1, 0, 0, 0, 0), // speed m/s + Dim::LENGTH, // diameter m + Dim::new(-1, 1, -1, 0, 0, 0, 0), // viscosity Pa s + ]; + let groups = buckingham_pi(&pipe).expect("a well-posed problem"); + println!("\nBuckingham's theorem"); + println!(" 4 quantities, rank 3 -> {} group", groups.len()); + let exponents: Vec = groups[0].iter().map(|r| format!("{r}")).collect(); + println!(" exponents (rho, u, d, mu): {}", exponents.join(", ")); + println!(" that is rho^-1 u^-1 d^-1 mu, which is 1/Re -- the theorem finds"); + println!(" a basis for the null space, not the name anybody gave it"); + assert_eq!(groups.len(), 1); + + // The computation runs over exact rationals, not floats, because a + // group is exactly in the null space or it is not -- and one that + // cancelled to 1e-16 would be a rounding error reported as physics. + println!(" computed over Rational, so the cancellation is exact, not 1e-16"); + assert!(groups[0].iter().all(|r| *r == Rational::from_i64(-1, 1) || *r == Rational::one())); + + // ---- 4. arithmetic without rounding ------------------------------ + let tenth = Rational::from_i64(1, 10); + let fifth = Rational::from_i64(1, 5); + let sum = tenth.add(&fifth); + println!("\nexact arithmetic"); + println!(" 0.1 + 0.2 in f64 = {:.17}", 0.1 + 0.2); + println!(" 1/10 + 1/5 exact = {sum}"); + assert_ne!(0.1 + 0.2, 0.3); + assert_eq!(sum, Rational::from_i64(3, 10)); + + // An f64 is a dyadic rational, and from_f64_exact gives the value it + // genuinely holds rather than the decimal it is printed as. + let as_stored = Rational::from_f64_exact(0.1).expect("finite"); + println!(" and 0.1 as an f64 is really {as_stored}"); + assert_ne!(as_stored, Rational::from_i64(1, 10)); +}