# @ferret/physics-liquids: API

containers and reservoirs, a height-field water surface, particle liquids, foils, hydraulic circuits (cylinders, valves, pumps, accumulators).

80 exports. Generated from the source (`packages/physics-liquids/src`), so it matches the code.

## Classes

### Capillary

*class* · `packages/physics-liquids/src/index.ts:55`

```ts
class Capillary
```

- `rise: number` — height of the liquid in the tube above the reservoir surface (negative: pushed down, mercury in glass)
- `constructor(readonly def: CapillaryDef)`
- `equilibrium(l: LiquidMaterial, angle: number, g: number)`
- `step(dt: number, net: LiquidNetwork, angle: (liquid: string, solid?: string) => number)`

### Fluid

*class* · `packages/physics-liquids/src/particles.ts:65`

```ts
class Fluid
```

- `n: number`
- `x: Float64Array`
- `v: Float64Array`
- `readonly colliders: Collider[]`
- `readonly s: number`
- `readonly H: number`
- `readonly mass: number`
- `readonly rho0: number`
- `readonly density: number`
- `readonly viscosity: number`
- `readonly tension: number`
- `adhesion: number`
- `readonly gravity: Vec3`
- `iterations: number`
- `substeps: number`
- `readonly budget: number`
- `stats: FluidStats`
- `time: number`
- `constructor(d: FluidDef)`
- `add(points: Iterable<Vec3>, velocity: Vec3 = [0, 0, 0])` — Add particles at the points.
- `fillBox(min: Vec3, max: Vec3, velocity: Vec3 = [0, 0, 0])` — Fill a box with particles on the lattice.
- `fillSphere(c: Vec3, r: number, velocity: Vec3 = [0, 0, 0])` — Fill a sphere (a drop).
- `remove(i: number)`
- `position(i: number): Vec3`
- `get volume()` — Total liquid volume, m³ (particles × the volume each stands for).
- `densityOf(i: number)` — Density of the particle i at its last solve, kg/m³ (reported in units of the true density, not the lattice's).
- `step(dt: number)`

### HydraulicCircuit

*class* · `packages/physics-liquids/src/hydraulics.ts:82`

```ts
class HydraulicCircuit
```

- `readonly nodes: Map<string, Node>`
- `readonly links: Map<string, HydraulicLink>`
- `readonly cylinders: Map<string, Cylinder>`
- `time: number`
- `readonly rho: number`
- `readonly bulk: number`
- `substeps: number` — sub-steps used by the last step
- `residual: number` — the worst scaled residual and the Newton iterations of the last sub-step (a check on the solve)
- `iterations: number`
- `constructor(readonly liquid: LiquidMaterial, o: { bulk?: number } = {})`
- `tank(id: string): this` — an open tank at the atmosphere (a node `tank` exists from the start)
- `line(id: string, volume: number, pressure = P_AMB): this` — a line or junction holding `volume` m3 of liquid, at `pressure` Pa
- `accumulator(id: string, o: { precharge: number; gas: number; gamma?: number; line?: number; pressure?: number }): this` — a gas-charged accumulator on a line: precharge Pa, gas volume at precharge m3, polytropic exponent (1.4 adiabatic, 1 isothermal), plus the line volume
- `stored(id: string): number` — liquid held in an accumulator at its pressure, m3
- `pump(id: string, node: string, rate: number | (() => number)): this` — a constant or time-varying flow into a node (a pump), m3/s
- `pressure(id: string): number`
- `link(id: string, spec: LinkSpec): HydraulicLink`
- `relief(id: string, line: string, o: { crack: number; cdA: number; range?: number; to?: string }): HydraulicLink` — a relief valve from `line` to the tank, set to `crack` Pa
- `setOpen(id: string, v: number)` — set the opening of a valve link (a number replaces a function)
- `cylinder(id: string, def: CylinderDef): Cylinder`
- `handPump(id: string, o: { bore: number; stroke: number; to: string; cdA?: number; from?: string; dead?: number }): HandPump` — a hand pump: a small cylinder driven by a lever (0..1), with an inlet check from the tank and an outlet check to `to`. `lever` is the position of the piston as a share of the stroke.
- `directional(id: string, o: { supply: string; a: string; b: string; cdA: number; tank?: string }): DirectionalValve` — a four-way, three-position valve: the spool in -1..1; positive sends the pump to `a` and `b` to the tank, negative the other way, zero closes all (the load holds)
- `step(dt: number): void`
- `liquidVolume(): number` — Total liquid in the circuit (compressed and stored), m3: for checks that nothing is made or lost.
- `fromTank(): number` — Volume that has gone into the tank and come out of it, m3, through the links that touch it.

### Link

*class* · `packages/physics-liquids/src/network.ts:90`

```ts
class Link
```

- `readonly def: LinkDef`
- `flow: number` — m³/s, positive from → to
- `velocity: number`
- `reynolds: number`
- `regime: 'none' | 'laminar' | 'turbulent' | 'orifice' | 'pump'`
- `dp: number` — the driving pressure (Pa) and the liquid that flows
- `liquid: string`
- `constructor(def: LinkDef)`
- `get id()`
- `get area()`
- `set valve(v: number)`
- `get valve()`
- `pressureAt(s: number, pIn: number, density: number): number` — The pressure at a distance s along a pipe with a `profile`, by Bernoulli from the inlet pressure `pIn`: the speed follows the bore (continuity), and the pressure drops by ½·ρ·(v² − v_in²). Friction is not subtracted: it is the Venturi, not the whole line.

### LiquidLibrary

*class* · `packages/physics-liquids/src/liquid.ts:26`

```ts
class LiquidLibrary
```

- `constructor(initial: Record<string, LiquidMaterial> = EXAMPLE_LIQUIDS)`
- `add(name: string, l: LiquidMaterial)`
- `get(name: string): LiquidMaterial`
- `has(name: string)`
- `names()`
- `contactAngle(liquid: string, solid?: string): number` — Contact angle in degrees against a solid (90° when the liquid has no entry for it and no default).

### LiquidNetwork

*class* · `packages/physics-liquids/src/network.ts:135`

```ts
class LiquidNetwork
```

Reservoirs joined by links: levels, pressures and flows. No particles; exact where the physics is exact (hydrostatics, Bernoulli, Poiseuille).

- `readonly reservoirs: Map<string, Reservoir>`
- `readonly links: Map<string, Link>`
- `readonly discharged: Map<string, number>` — liquid that has left the network through open ends, by liquid, m³
- `constructor(readonly liquids: LiquidLibrary, public g = 9.80665)`
- `addReservoir(def: ReservoirDef): Reservoir`
- `addLink(def: LinkDef): Link`
- `removeReservoir(id: string)`
- `removeLink(id: string)`
- `pressureAt(id: string, y: number): number` — Pressure at a world y inside a reservoir's liquid, p = p0 + ρ·g·depth.
- `gateForce(id: string, width: number, yBottom: number, yTop: number): { force: number; centreY: number }` — Force (N) of the liquid on a vertical rectangular gate of the given width between two world heights, and the depth of its centre of pressure.
- `step(dt: number, o: StepOptions = {})`

### Reservoir

*class* · `packages/physics-liquids/src/network.ts:22`

```ts
class Reservoir
```

- `readonly id: string`
- `liquid: string`
- `readonly container: Container`
- `readonly position: [number, number, number]`
- `volume: number`
- `pressure: number` — pressure over the surface when the tank is open
- `gas?: GasVolume`
- `level: number` — surface height above the container's bottom (m), with what bodies displace
- `displaced: number` — the volume that bodies in the tank displace at the current level
- `full: boolean`
- `empty: boolean`
- `discharged: number`
- `received: number`
- `constructor(def: ReservoirDef)`
- `get surfaceY()`
- `get headPressure()` — pressure of whatever is over the liquid
- `get bounds(): { min: [number, number, number]; max: [number, number, number] }` — the box the liquid fills, for a medium of the physics step

### WaterSurface

*class* · `packages/physics-liquids/src/surface.ts:39`

```ts
class WaterSurface
```

- `readonly nx: number`
- `readonly nz: number`
- `readonly dx: number`
- `readonly origin: [number, number]`
- `readonly level: number`
- `readonly g: number`
- `readonly eta: Float64Array` — elevation of the surface above the rest level, per cell
- `readonly depth: Float64Array` — rest depth per cell
- `readonly qx: Float64Array` — flow per unit width (m²/s) through the x faces, (nx+1)·nz, and the z faces, nx·(nz+1)
- `readonly qz: Float64Array`
- `readonly u0: Float64Array` — steady current per cell
- `readonly v0: Float64Array`
- `friction: number`
- `readonly nonlinear: boolean`
- `time: number`
- `lastMs: number` — wall-clock cost of the last step, ms (for the inspector)
- `constructor(d: SurfaceDef)`
- `get maxSpeed(): number` — The fastest wave speed on the grid, m/s.
- `get maxStep(): number` — The largest stable step, s.
- `height(x: number, z: number): number` — Height of the water surface at a point, m (the rest level where the point is off the grid's cells it clamps to the nearest).
- `depthAt(x: number, z: number): number` — Depth of the water at a point, m.
- `normal(x: number, z: number): Vec3` — Surface normal at a point.
- `velocity(x: number, z: number): [number, number]` — Velocity of the water at a point: the current plus the flow of the waves, m/s ([x, z]).
- `disturb(x: number, z: number, volume: number, radius = 2 * this.dx)` — Add (or take away, when negative) a volume of water around a point, spread over a smooth disc of the radius; at the next step. A body that pushes into the water adds the volume it displaces.
- `get volume(): number` — Total volume above the rest level, m³.
- `energy(rho = 1000): number` — Energy of the waves, J per unit density of water ρ = 1000 kg/m³.
- `field()` — The heights and normals handed to a surface shader of `@ferret/fx`: nothing is drawn here.
- `step(dt: number)` — Advance by dt, split into steps below the stability limit.

### Wick

*class* · `packages/physics-liquids/src/index.ts:73`

```ts
class Wick
```

- `reach: number` — how far the liquid has crept into the wick, m
- `constructor(readonly def: WickDef)`
- `step(dt: number, net: LiquidNetwork, angle: (liquid: string, solid?: string) => number)`

## Functions

### adhesionFor

*function* · `packages/physics-liquids/src/particles.ts:479`

```ts
function adhesionFor: (contactAngleDeg: number) => number
```

### bodySdf

*function* · `packages/physics-liquids/src/fluid.ts:51`

```ts
function bodySdf(b: Body): Collider['sdf']
```

The SDF of a body's shape where it is now (it turns with the body). A hull stands in as its bounding box; a mesh is refused.

### boxContainer

*function* · `packages/physics-liquids/src/container.ts:28`

```ts
function boxContainer(width: number, depth: number, height: number): Container
```

### boxSdf

*function* · `packages/physics-liquids/src/particles.ts:26`

```ts
function boxSdf: (c: Vec3, half: Vec3) => (x: number, y: number, z: number) => number
```

A solid box (negative inside it).

### buildCircuit

*function* · `packages/physics-liquids/src/hydraulics-host.ts:63`

```ts
function buildCircuit(d: CircuitDef, liquids: Record<string, LiquidMaterial> = EXAMPLE_LIQUIDS): BuiltCircuit
```

### capVolume

*function* · `packages/physics-liquids/src/water.ts:40`

```ts
function capVolume(r: number, s: number): number
```

Volume of the part of a sphere of radius r below a height s above its bottom.

### containerOf

*function* · `packages/physics-liquids/src/index.ts:96`

```ts
function containerOf(s: ContainerSpec): Container
```

### creep

*function* · `packages/physics-liquids/src/index.ts:48`

```ts
function creep(u: number, drive: number, gTerm: number, radius: number, viscosity: number, dt: number): number
```

How far a liquid has crept after dt in a thin channel of radius R: with the length u, u·du/dt = R²/(8μ)·(drive − g_term·u), which is
singular at u = 0 in u but smooth in M = u²: dM/dt = R²/(4μ)·(drive − g_term·√M). `drive` is the capillary pressure 2γ|cosθ|/R (Pa)
and `gTerm` the weight of the column per metre (ρ·g, or ρ·g·sin α for a tilted wick).

### cylinderContainer

*function* · `packages/physics-liquids/src/container.ts:33`

```ts
function cylinderContainer(radius: number, height: number): Container
```

### foilForce

*function* · `packages/physics-liquids/src/foil.ts:57`

```ts
function foilForce(f: FoilDef, v: Vec3, flow: Vec3, rho: number, chordDir: Vec3 = f.chord ?? [1, 0, 0], spanDir: Vec3 = f.span ?? [0, 0, 1]): FoilForce
```

Force on a foil moving with velocity `v` through a fluid moving with velocity `flow` (both in the frame of `chord` and `span`, which
the caller has rotated into place), in a fluid of density `rho`. The lift is perpendicular to the relative flow and to the span; the
drag is along the relative flow.

### hydraulicForms

*function* · `packages/physics-liquids/src/hydraulics-host.ts:181`

```ts
function hydraulicForms(br: FormBridge, cap: HydraulicsCapability): void
```

### hydraulics

*function* · `packages/physics-liquids/src/hydraulics-host.ts:87`

```ts
function hydraulics(): HydraulicsCapability
```

### jurinHeight

*function* · `packages/physics-liquids/src/liquid.ts:47`

```ts
function jurinHeight(l: LiquidMaterial, angleDeg: number, radius: number, g = 9.80665): number
```

Jurin's law: the rise of a liquid in a tube of radius r, h = 2·γ·cosθ / (ρ·g·r); negative (a depression) when θ is over 90°.

### laplacePressure

*function* · `packages/physics-liquids/src/liquid.ts:57`

```ts
function laplacePressure(l: LiquidMaterial, radius: number, surfaces = 1): number
```

Laplace pressure inside a drop (one surface) or a bubble in the liquid; a soap bubble has two surfaces: pass surfaces = 2.

### liquids

*function* · `packages/physics-liquids/src/index.ts:117`

```ts
function liquids(): LiquidsCapability
```

### magnus

*function* · `packages/physics-liquids/src/foil.ts:73`

```ts
function magnus(v: Vec3, omega: Vec3, radius: number, rho: number): Vec3
```

Magnus force on a spinning ball: F = ½ρ·A·Cl·|v|²·(ω̂ × v̂), with Cl rising with the spin ratio r|ω|/|v| and saturating at 0.4.
`v` is the velocity of the ball relative to the fluid.

### particleLiquid

*function* · `packages/physics-liquids/src/fluid.ts:73`

```ts
function particleLiquid(): FluidCapability
```

### planeSdf

*function* · `packages/physics-liquids/src/particles.ts:23`

```ts
function planeSdf: (normal: Vec3, offset: number) => (x: number, y: number, z: number) => number
```

### profileContainer

*function* · `packages/physics-liquids/src/container.ts:53`

```ts
function profileContainer(heights: number[], areas: number[], half?: [number, number]): Container
```

Any container from its profile: the cross-section area at a list of heights (increasing from 0), linear between them. A funnel, a
bottle, a hull's hold; the table can be built once from a closed mesh by slicing it.

### roomSdf

*function* · `packages/physics-liquids/src/particles.ts:31`

```ts
function roomSdf: (c: Vec3, half: Vec3) => (x: number, y: number, z: number) => number
```

A box-shaped room: the fluid is kept inside it.

### sphereContainer

*function* · `packages/physics-liquids/src/container.ts:39`

```ts
function sphereContainer(radius: number): Container
```

A spherical tank, or a round flask.

### sphereSdf

*function* · `packages/physics-liquids/src/particles.ts:24`

```ts
function sphereSdf: (c: Vec3, r: number) => (x: number, y: number, z: number) => number
```

### thinFoil

*function* · `packages/physics-liquids/src/foil.ts:18`

```ts
function thinFoil(o: { aspect?: number; Cd0?: number; stall?: number; efficiency?: number } = {}): Polar
```

A flat-plate polar with a stall: thin-airfoil lift 2π·α up to the stall angle, then the post-stall flat plate (Cl = sin 2α), drag from
the zero-lift drag plus the induced term Cl²/(π·e·AR).

### tubeSdf

*function* · `packages/physics-liquids/src/particles.ts:33`

```ts
function tubeSdf: (x0: number, z0: number, radius: number, y0: number, y1: number) => (x: number, y: number, z: number) => number
```

A vertical tube: a wall of the radius around the axis (x0, z0), the fluid inside; open at both ends between y0 and y1 (the wall ends there).

### validateCircuit

*function* · `packages/physics-liquids/src/hydraulics-host.ts:31`

```ts
function validateCircuit(d: unknown, path = 'hydraulics'): string[]
```

### venturiDrop

*function* · `packages/physics-liquids/src/liquid.ts:62`

```ts
function venturiDrop(density: number, v1: number, a1: number, a2: number): number
```

Pressure drop across a contraction from Bernoulli, Δp = ½·ρ·(v₂² − v₁²), the Venturi effect.

### washburnLength

*function* · `packages/physics-liquids/src/liquid.ts:52`

```ts
function washburnLength(l: LiquidMaterial, angleDeg: number, radius: number, t: number): number
```

Washburn: the distance a liquid has crept into a horizontal pore of radius r after t seconds, L² = γ·r·cosθ·t / (2·μ).

### waterSurface

*function* · `packages/physics-liquids/src/water.ts:45`

```ts
function waterSurface(): SurfaceCapability
```

## Interfaces

### AttachDef

*interface* · `packages/physics-liquids/src/hydraulics-host.ts:15`

```ts
interface AttachDef
```

The `hydraulics` capability: circuits of the level, stepped every fixed step in the `field` phase, cylinders that push
two bodies apart along an axis, and the commands and forms to drive the valves.

Level block `physics.hydraulics`: a list of circuits
  { name, liquid?, bulk?, lines: [{ id, volume, pressure? }], accumulators: [{ id, precharge, gas, gamma?, line? }], pumps: [{ node, rate }],
    cylinders: [{ id, bore, rod, stroke, mass?, ..., attach?: { a, b, axis } }], links: [{ id, from, to, cdA | laminar, open?, check?, relief? }],
    valves: [{ id, supply, a, b, cdA }], handPumps: [{ id, bore, stroke, to }], reliefs: [{ id, line, crack, cdA }] }
where `a` and `b` of an attached cylinder are the nodes of two bodies (the barrel on `a`, the rod on `b`) and `axis` the extension direction in the frame of `a`.

- `a: string`
- `b: string`
- `axis: Vec3`
- `offset?: number`

### BuiltCircuit

*interface* · `packages/physics-liquids/src/hydraulics-host.ts:55`

```ts
interface BuiltCircuit
```

What the capability keeps of a circuit: the circuit, its valves and pumps by name.

- `name: string`
- `circuit: HydraulicCircuit`
- `valves: Map<string, { id: string; spool: number }>`
- `handPumps: Map<string, { id: string; lever: number; handleForce: number }>`
- `attached: Array<{ cyl: Cylinder; def: AttachDef }>`

### CapillaryDef

*interface* · `packages/physics-liquids/src/index.ts:24`

```ts
interface CapillaryDef
```

Capability L1: liquids as reservoirs and pipes. Liquid in a container has a level that follows from its volume; reservoirs are
joined by links (pipes, orifices, valves, pumps, check valves) whose flow comes from the pressure difference, with Hagen–Poiseuille
in laminar flow and Darcy–Weisbach in turbulent flow, so levels equalise with the right time constant, two liquids in a U-tube
balance `ρ₁h₁ = ρ₂h₂`, a tank drains by Torricelli. A body put in raises the level by what it displaces. A reservoir is also a
medium of the capability D (when it is on), so a body in it floats, sinks and drags by the same law as in a lake. Capillary
tubes (Jurin) and wicks (Washburn) take liquid from a reservoir.

values: `liquids` ({name: LiquidMaterial}) over the examples.

- `id: string`
- `reservoir: string`
- `radius: number`
- `solid?: string` — the solid the tube is made of, for the contact angle
- `length?: number` — the tube's length above the reservoir surface, m (the liquid stops at the top)

### CircuitCylinder

*interface* · `packages/physics-liquids/src/hydraulics-host.ts:16`

```ts
interface CircuitCylinder extends Omit<CylinderDef, 'load'>
```

- `id: string`
- `attach?: AttachDef`

### CircuitDef

*interface* · `packages/physics-liquids/src/hydraulics-host.ts:17`

```ts
interface CircuitDef
```

- `name: string`
- `liquid?: string | LiquidMaterial`
- `bulk?: number`
- `lines?: Array<{ id: string; volume: number; pressure?: number }>`
- `accumulators?: Array<{ id: string; precharge: number; gas: number; gamma?: number; line?: number; pressure?: number }>`
- `pumps?: Array<{ node: string; rate: number }>`
- `cylinders?: CircuitCylinder[]`
- `links?: Array<{ id: string } & LinkSpec>`
- `valves?: Array<{ id: string; supply: string; a: string; b: string; cdA: number; tank?: string }>`
- `handPumps?: Array<{ id: string; bore: number; stroke: number; to: string; cdA?: number }>`
- `reliefs?: Array<{ id: string; line: string; crack: number; cdA: number; range?: number }>`

### Collider

*interface* · `packages/physics-liquids/src/particles.ts:10`

```ts
interface Collider
```

L3: liquid as particles, by position-based fluids: a density constraint per particle solved with a fixed number of iterations over a
neighbour grid (incompressible, so a still column holds ρ·g·h), XSPH viscosity mapped from the material's viscosity (water to honey),
cohesion between particles (Akinci) that rounds drops up, and adhesion to solids that sets the contact angle. Solids are signed distance
functions, so static meshes, boxes, spheres and tubes all work the same way, and moving bodies push back through the same call.
Fixed step, fixed iteration count and no randomness: a replay is identical.

- `sdf(x: number, y: number, z: number): number` — signed distance to the solid: negative inside it (where a particle may not be), positive outside
- `friction?: number` — friction against the wall, 0..1 (velocity along the wall lost per contact)
- `adhesion?: number` — adhesion of this wall as a share of the liquid's cohesion (see `adhesionFor`); the fluid's default when missing
- `onPush?(point: Vec3, impulse: Vec3): void` — a moving solid reports what the fluid did to it: the impulse of one correction, N·s (sum over a step / dt is the force)
- `velocity?(x: number, y: number, z: number): Vec3` — velocity of the wall at a point (a moving body drags the fluid along); still when missing

### Container

*interface* · `packages/physics-liquids/src/container.ts:5`

```ts
interface Container
```

A container shape for a reservoir: how much liquid fits up to a height. The surface level follows from the volume by
inverting `volume(h)`; the cross-section `area(h)` is what a level rises against.

- `readonly height: number` — inner height, m
- `readonly half: [number, number]` — horizontal half extents of the footprint (x, z), m: where bodies count as inside, and the box of the water
- `area(h: number): number` — cross-section at height h, m²
- `volume(h: number): number` — liquid volume up to height h, m³
- `heightOf(v: number): number` — height reached by volume v (clamped to 0..height)
- `readonly capacity: number`

### Cylinder

*interface* · `packages/physics-liquids/src/hydraulics.ts:65`

```ts
interface Cylinder
```

- `readonly id: string`
- `readonly def: Required<Pick<CylinderDef, 'bore' | 'rod' | 'stroke' | 'dead' | 'mass' | 'coulomb' | 'viscous' | 'leak' | 'damping'>> & CylinderDef`
- `x: number`
- `v: number`
- `cap: string` — nodes of the two chambers: `id.cap` and `id.rod`
- `rod: string`
- `force: number` — force of the liquid on the piston along the extension, N, and the force the piston gives the load
- `atStop: 0 | 1 | -1` — the extension is held at an end stop
- `area: number`
- `rodSide: number`
- `attach?: { read(): { x: number; v: number }; push(f: number): void }` — external extension and speed from bodies, when attached
- `setLoad(f: ((x: number, v: number, t: number) => number) | undefined): void` — the load given by bodies is the one that moves the piston: set `mass: 0` and `attach`

### CylinderDef

*interface* · `packages/physics-liquids/src/hydraulics.ts:14`

```ts
interface CylinderDef
```

- `bore: number` — piston bore and rod diameter, m
- `rod: number`
- `stroke: number` — stroke, m
- `dead?: number` — dead volume in each chamber, m3 (default a tenth of the swept volume)
- `mass?: number` — moving mass (piston and rod), kg; 0 with `attach`: the bodies carry the mass
- `coulomb?: number` — seal friction: Coulomb N and viscous N·s/m
- `viscous?: number`
- `damping?: number` — damping of the liquid column as a share of critical (default 0.15): the bulk viscosity of the oil and the give of the hoses take the ringing out of the load on its oil spring
- `leak?: number` — leak across the piston from the cap side to the rod side, m3/s per Pa
- `x?: number` — starting extension, m
- `load?: (x: number, v: number, t: number) => number` — a force on the rod along the extension (a load, gravity, a spring), N, from the extension and speed

### DirectionalValve

*interface* · `packages/physics-liquids/src/hydraulics.ts:359`

```ts
interface DirectionalValve
```

- `id: string`
- `spool: number`

### Drain

*interface* · `packages/physics-liquids/src/fluid.ts:35`

```ts
interface Drain extends DrainDef
```

- `taken: number`

### DrainDef

*interface* · `packages/physics-liquids/src/fluid.ts:29`

```ts
interface DrainDef
```

- `into?: Reservoir` — the reservoir that takes the liquid back; without one the liquid leaves the scene
- `sdf: (x: number, y: number, z: number) => number` — the region that swallows particles: positive outside it, negative inside (an SDF)

### Floater

*interface* · `packages/physics-liquids/src/water.ts:26`

```ts
interface Floater extends FloaterDef
```

- `volume: number`

### FloaterDef

*interface* · `packages/physics-liquids/src/water.ts:17`

```ts
interface FloaterDef
```

- `body: Body`
- `radius: number` — radius of the sphere that stands for the body's displacement, m
- `Cd?: number` — drag coefficient against the water's velocity, 1 for a rough sphere
- `push?: boolean` — false: the body does not push the water

### FluidCapability

*interface* · `packages/physics-liquids/src/fluid.ts:40`

```ts
interface FluidCapability extends Capability
```

- `readonly fluids: Map<string, Fluid>`
- `add(name: string, def: FluidDef): Fluid`
- `couple(name: string, body: Body, opts?: { friction?: number; adhesion?: number }): Collider` — A kernel body becomes a moving solid of the liquid; a dynamic one is pushed back.
- `pour(name: string, def: PourDef): Pour`
- `drain(name: string, def: DrainDef): Drain`
- `view(name: string): FluidView`

### FluidDef

*interface* · `packages/physics-liquids/src/particles.ts:40`

```ts
interface FluidDef
```

- `spacing: number` — particle spacing at rest, m
- `density?: number` — kg/m³
- `viscosity?: number` — dynamic viscosity, Pa·s
- `surfaceTension?: number` — N/m: drives the cohesion between particles
- `adhesion?: number` — adhesion to walls as a share κ of the liquid's own cohesion (0 beads up, 1 spreads; see `adhesionFor` for a contact angle)
- `iterations?: number`
- `substeps?: number`
- `gravity?: Vec3`
- `budget?: number` — most particles the solver will step; beyond it, the solver reduces its iterations and says so

### FluidStats

*interface* · `packages/physics-liquids/src/particles.ts:58`

```ts
interface FluidStats
```

- `particles: number`
- `iterations: number`
- `substeps: number`
- `degraded: boolean`
- `neighbours: number`
- `ms: number`

### FluidView

*interface* · `packages/physics-liquids/src/fluid.ts:38`

```ts
interface FluidView
```

What a renderer of `@ferret/fx` draws: positions and speeds of the particles, and the spacing (a particle's size).

- `count: number`
- `spacing: number`
- `positions: Float32Array`
- `speeds: Float32Array`

### FoilDef

*interface* · `packages/physics-liquids/src/foil.ts:34`

```ts
interface FoilDef
```

- `polar: Polar`
- `area: number` — planform area, m²
- `chord?: Vec3` — unit vectors in the body frame: along the chord (leading edge forward) and along the span
- `span?: Vec3`

### FoilForce

*interface* · `packages/physics-liquids/src/foil.ts:43`

```ts
interface FoilForce
```

- `force: Vec3` — force on the foil, N, in the same frame as the inputs
- `lift: number`
- `drag: number`
- `alpha: number` — angle of attack, rad

### FormBridge

*interface* · `packages/physics-liquids/src/hydraulics-host.ts:180`

```ts
interface FormBridge
```

The words of the language for hydraulics: `(spool "circuit" "valve" :extend|:hold|:retract|number)`, `(valve "circuit" "link" x)`,
`(cylinder-position "circuit" "cyl")`, `(cylinder-force "circuit" "cyl")`. `define` is the bridge's `define`.

- `define(name: string, sig: { args: string[]; returns?: string }, fn: (ctx: unknown, ...args: any[]) => unknown): unknown`

### HandPump

*interface* · `packages/physics-liquids/src/hydraulics.ts:358`

```ts
interface HandPump
```

- `id: string`
- `lever: number`
- `last: number`
- `area: number`
- `node: string`
- `handleForce: number`
- `delivered: number`

### HydraulicLink

*interface* · `packages/physics-liquids/src/hydraulics.ts:63`

```ts
interface HydraulicLink
```

- `id: string`
- `spec: LinkSpec`
- `a: Node`
- `b: Node`
- `flow: number`
- `moved: number`
- `open: () => number`

### HydraulicNodeInfo

*interface* · `packages/physics-liquids/src/hydraulics.ts:37`

```ts
interface HydraulicNodeInfo
```

- `id: string`
- `pressure: number`
- `kind: string`

### HydraulicsCapability

*interface* · `packages/physics-liquids/src/hydraulics-host.ts:81`

```ts
interface HydraulicsCapability extends Capability
```

- `readonly circuits: Map<string, BuiltCircuit>`
- `add(def: CircuitDef): BuiltCircuit`
- `remove(name: string): void`

### LinkDef

*interface* · `packages/physics-liquids/src/network.ts:62`

```ts
interface LinkDef
```

- `id: string`
- `from: string | 'air'`
- `to: string | 'air'`
- `fromElevation: number` — world y of each end of the pipe
- `toElevation: number`
- `diameter: number` — pipe bore (m) and length (m); an orifice ignores the length
- `length?: number`
- `roughness?: number` — absolute roughness of the wall (m)
- `loss?: number` — sum of minor-loss coefficients (bends, entrance)
- `valve?: number` — 0..1 open fraction of a valve
- `pump?: { pressure?: number; rate?: number }` — pump: adds pressure (Pa) to the flow from → to, or forces a rate (m³/s)
- `check?: boolean` — a check valve lets liquid pass from → to only
- `orifice?: { cd?: number; area?: number }` — a sharp opening instead of a pipe: Torricelli with a discharge coefficient
- `main?: { liquid: string; pressure: number }` — a main (a supply at pressure) in place of `from`
- `profile?: Array<{ s: number; diameter: number }>` — pipe diameter along the length, for pressure taps: [{s, diameter}], s from 0 to length

### LinkEnd

*interface* · `packages/physics-liquids/src/network.ts:60`

```ts
interface LinkEnd
```

- `reservoir: string | 'air'`
- `elevation: number`

### LinkSpec

*interface* · `packages/physics-liquids/src/hydraulics.ts:50`

```ts
interface LinkSpec
```

- `from: string`
- `to: string`
- `cdA?: number` — discharge coefficient times the flow area, m2 (the orifice law `Q = cdA·sqrt(2·dp/rho)`)
- `laminar?: number` — a laminar leak instead of an orifice: m3/s per Pa
- `open?: number | (() => number)` — 0..1 now (a number, or a function of time for a valve driven from outside)
- `check?: boolean` — passes only from `from` to `to`
- `relief?: { crack: number; range?: number }` — a relief valve: opens from `crack` Pa over `range` Pa (of the pressure at `from`)

### LiquidMaterial

*interface* · `packages/physics-liquids/src/liquid.ts:3`

```ts
interface LiquidMaterial
```

A liquid as data: the same record for a tank, a pipe, a wick and a splash.

- `density: number` — kg/m³
- `viscosity: number` — dynamic viscosity, Pa·s
- `surfaceTension: number` — N/m
- `compressibility?: number` — 1/Pa; 0 = incompressible
- `contactAngle?: Record<string, number>` — degrees against a solid material by name; `default` for the rest (water on glass is near 0°, mercury on glass near 140°)
- `henry?: Record<string, number>` — Henry's constants for dissolved gases by species, mol/(m3·Pa) at 298 K: the dissolved amount at equilibrium is k·(partial pressure) per m3 of liquid (water's come from the aqueous set of physics-chemistry, which also holds the temperature dependence)

### LiquidsCapability

*interface* · `packages/physics-liquids/src/index.ts:105`

```ts
interface LiquidsCapability extends Capability
```

- `net: LiquidNetwork`
- `liquids: LiquidLibrary`
- `tubes: Map<string, Capillary>`
- `wicks: Map<string, Wick>`
- `addReservoir(host: PhysicsHost | null, def: ReservoirDef, asMedium?: boolean): Reservoir`
- `addTube(def: CapillaryDef): Capillary`
- `addWick(def: WickDef): Wick`
- `advance(dt: number, g: number, host?: PhysicsHost | null): void` — Advance by dt with a given gravity (also what the capability's step does).

### Polar

*interface* · `packages/physics-liquids/src/foil.ts:7`

```ts
interface Polar
```

Lift and drag of a wing, sail, keel, fin or rudder from coefficients of the angle of attack, against the local flow. This is the
Bernoulli effect as a game uses it, not a solved flow around the shape.

- `Cl(alpha: number): number` — lift coefficient at an angle of attack α, rad (odd in α for a symmetric foil)
- `Cd(alpha: number): number` — drag coefficient (even in α)

### Pour

*interface* · `packages/physics-liquids/src/fluid.ts:27`

```ts
interface Pour extends PourDef
```

- `owed: number`
- `poured: number`

### PourDef

*interface* · `packages/physics-liquids/src/fluid.ts:16`

```ts
interface PourDef
```

- `from?: Reservoir` — the L1 reservoir the liquid comes from; without one the pour makes liquid (a tap on the mains)
- `at: Vec3` — centre of the nozzle and the velocity the liquid leaves it with
- `velocity: Vec3`
- `radius: number` — nozzle radius, m
- `rate: number` — m³/s

### ReservoirDef

*interface* · `packages/physics-liquids/src/network.ts:7`

```ts
interface ReservoirDef
```

- `id: string`
- `liquid: string`
- `container: Container`
- `position?: [number, number, number]` — the middle of the container's bottom, world metres
- `volume?: number` — initial liquid volume (m³), or initial level (m)
- `level?: number`
- `pressure?: number` — pressure over the surface (Pa); the atmosphere unless the tank is closed
- `closed?: boolean` — a closed tank has a gas head whose pressure follows the volume it has left (isothermal, p·V = const)

### StepOptions

*interface* · `packages/physics-liquids/src/network.ts:125`

```ts
interface StepOptions
```

- `g?: number` — gravity (m/s²)
- `displacement?: (r: Reservoir, surfaceY: number) => number` — volume that bodies displace in a reservoir whose liquid surface is at y
- `emit?: (name: string, data: Record<string, unknown>) => void` — called for events: 'level', 'empty', 'full', 'overflow'

### SurfaceCapability

*interface* · `packages/physics-liquids/src/water.ts:28`

```ts
interface SurfaceCapability extends Capability
```

- `readonly surfaces: Map<string, WaterSurface>`
- `readonly floaters: Map<Body, Floater>`
- `add(name: string, def: SurfaceDef): WaterSurface`
- `float(def: FloaterDef): Floater`
- `height(x: number, z: number): number | null` — Height of the water at a point (the first surface that covers it), or null off every surface.
- `cost_ms(): number` — Wall-clock cost of the surfaces' last updates, ms.

### SurfaceDef

*interface* · `packages/physics-liquids/src/surface.ts:10`

```ts
interface SurfaceDef
```

L2: the surface of a lake, a harbour or a river as a height field on a grid, by the linearised shallow-water equations on a
staggered grid (heights at cell centres, flows through the faces), stepped forward-backward so it is stable up to a Courant number
of about 0.7. Waves travel at √(g·h) in the local depth, reflect from the banks and the walls, shoal in shallow water and are
eaten by a sponge at the open edges. A steady current carries the waves and anything floating on them. The grid is not a flow solver
around bodies: bodies push it with `disturb` and read it with `height` and `velocity`.

- `nx: number` — cells along x and z
- `nz: number`
- `dx: number` — cell size, m
- `origin?: [number, number]` — world position of the corner (min x, min z) of the grid
- `level?: number` — height of the rest surface, m
- `depth?: number | ((x: number, z: number) => number)` — depth of the water at rest, m; a number or a function of the world x, z (a beach is a depth that reaches 0)
- `current?: [number, number] | ((x: number, z: number) => [number, number])` — steady current, m/s; a vector or a function of x, z
- `friction?: number` — linear friction on the flow, 1/s
- `open?: Array<'minX' | 'maxX' | 'minZ' | 'maxZ'>` — cells of sponge at the edges named here (open water); the other edges are walls
- `spongeCells?: number`
- `gravity?: number`
- `nonlinear?: boolean` — keep the momentum advection of the shallow-water equations (the flow carries its own momentum: u ∂q/∂x...): a wave of finite height then travels at about √(g·h)·(1 + 3a/2h), so crests catch the troughs and a front steepens. Off: the linear waves, cheaper and unconditionally gentle.

### WickDef

*interface* · `packages/physics-liquids/src/index.ts:31`

```ts
interface WickDef
```

- `id: string`
- `reservoir: string`
- `poreRadius: number` — pore radius, m
- `solid?: string`
- `area?: number` — cross-section of the wick, m², and the fraction of it that is pore
- `porosity?: number`
- `length?: number` — length of the wick, m
- `angle?: number` — angle from horizontal in degrees: 0 creeps along a level wick, 90 straight up against gravity

## Types

### ContainerSpec

*type* · `packages/physics-liquids/src/index.ts:91`

```ts
type ContainerSpec = | { kind: 'box'; size: [number, number, number] } | { kind: 'cylinder'; radius: number; height: number } | { kind: 'sphere'; radius: number } | { kind: 'profile'; heights: number[]; areas: number[]; half?: [number, number] }
```

## Constants

### EXAMPLE_LIQUIDS

*const* · `packages/physics-liquids/src/liquid.ts:18`

```ts
const EXAMPLE_LIQUIDS: Record<string, LiquidMaterial>
```

### FLUID_DEFAULTS

*const* · `packages/physics-liquids/src/fluid.ts:14`

```ts
const FLUID_DEFAULTS: { budget: number; }
```

Capability L3: particle liquids stepped with the world. A kernel body joins a liquid as a solid that moves (the liquid takes its
velocity at the wall) and is pushed back (the pressure on it is an impulse on the body, so a ball floats, a paddle splashes). An L1
reservoir stays the truth of the volume (section 8, decision 7): a pour takes its volume out as particles, a drain puts what reaches it
back, and nothing is made or lost on the way.

values (each at world / level / zone / entity scope):
  budget    most particles stepped at full quality per liquid   (6000)

### P_ATM

*const* · `packages/physics-liquids/src/network.ts:5`

```ts
const P_ATM: 101325
```

### SURFACE_DEFAULTS

*const* · `packages/physics-liquids/src/water.ts:15`

```ts
const SURFACE_DEFAULTS: { rate: number; waterDensity: number; }
```

Capability L2: water surfaces stepped with the world at a lower rate, and the bodies that float on them. A floater gets buoyancy from
the volume of its sphere below the surface at its own position (waves lift and tilt it), drag against the water's velocity (the
current and the flow of the waves carry it) and pushes the water by the volume it displaces.

values (each at world / level / zone / entity scope):
  rate           surface updates per second   (30)
  waterDensity   kg/m³                        (1000)

The surface of a harbour is read by others through `height(x, z)`; `vehicles().surface = water.height` makes ships ride the waves.

