Physics Primitives#

prophys.physics holds generic building blocks for transport, dispersion, thermal, reliability and fatigue calculations. None of them is a model of a particular system: a Gaussian plume, a Theis drawdown or a wake deficit is a composition of a primitive with symbolic arithmetic. Every primitive returns an Expr, accepts constants, parameters, random variables or other expressions as arguments, and is backed by a native kernel with the same guarantees as the rest of prophys:

  • Exact outputs. Closed forms, or finite algorithms whose step count is fixed or bounded by the size of the input — never a loop that runs “until converged”. Where a quantity is prone to cancellation or under/overflow (tail probabilities, large Poisson means, far polygon edges), the kernel evaluates it in a form that keeps its relative accuracy.

  • Exact gradients with respect to every continuous argument.

  • The same results on CPU and GPU: forward values bit for bit on the tested GPUs, gradients within a tight tolerance (see Kernel correctness).

Primitive

What it computes

erf(), erfcx(), log_ndtr(), exp1(), i0e() … owens_t()

Special functions for closed-form solutions (error functions far into the tails, the exponential integral, scaled Bessel functions, Owen’s T).

superpose()

Sums of anisotropic pair Green’s functions between sources and receptors: steady advected plumes, moving puffs, advancing fronts and radial E1 kernels, with power-law spreading, first-order decay and reflecting ground/lid images.

gaussian_polygon_probability()

Probability that a bivariate normal position falls in a polygon.

ray_integral()

Exact line integrals, maxima and threshold lengths of raster fields along segments.

propagate_linear()

The state of dx/dt = Q x + u after a time t (matrix exponential).

state_space_loglik()

The exact Kalman-filter log-likelihood of noisy observations.

overlap_area()

Intersection areas of polygons with polygons or disks.

piecewise_polynomial()

Published piecewise (log-)polynomial correlations from their coefficient tables.

network_potentials()

Potentials of grounded linear networks (DC power flow, resistor, heat and linearized pipe networks).

compound_distribution()

The exact pmf of aggregate losses or counts (Panjer’s recursion).

spectral_random_field()

Differentiable samples of stationary Gaussian random fields.

directional_sweep()

Node outputs under directional shadowing (wakes, sheltering).

fatigue_damage()

Rainflow-counted Palmgren–Miner damage of load histories.

Composing a model#

A Gaussian plume concentration is the steady kernel scaled by the emission rate over the wind speed; the wind direction and speed may be random variables:

import numpy as np
import prophys as prp
from prophys import physics

receptors = np.array([[250.0, 10.0, 1.5], [400.0, -30.0, 1.5]])
source = np.array([[0.0, 0.0, 20.0]])
concentration = physics.superpose(
    receptors,
    source,
    [emission_rate / wind_speed],
    direction=wind_direction,
    spread_across=(0.22, 0.9),
    spread_vertical=(0.2, 0.85),
    vertical="reflect",
    lid=800.0,
    images=3,
)

The Theis drawdown around a pumping well uses the radial kernel with radial_scale = 4 T t / S:

drawdown = physics.superpose(
    wells, pump, [q / (4 * np.pi * transmissivity)],
    kind="radial_exp1", radial_scale=4 * transmissivity * t / storativity,
)

Choosing between a primitive and symbolic code#

A formula that is a handful of elementary operations per element (an Arrhenius rate, a friction factor from an explicit correlation, a probit) belongs in symbolic code. The primitives exist where a native kernel adds something the symbolic layer cannot: loops over pairs, samples and time steps, special functions and tail-accurate forms, discrete structure (traversals, recursions, cycle counting), or the size gate.