# @ferret/flow-solver: API

flow boxes: a solved 2d slice of air or water round bodies (staggered grid, pressure projection) that publishes its wake into the flow field and pushes bodies by surface pressure.

16 exports. Generated from the source (`packages/flow-solver/src`), so it matches the code.

## Classes

### FlowBox

*class* · `packages/flow-solver/src/box.ts:60`

```ts
class FlowBox
```

- `readonly def: FlowBoxDef`
- `readonly name: string`
- `readonly plane: 'xz' | 'xy'`
- `grid: FlowGrid`
- `cell: number` — the cell size actually used (coarser than asked when over `maxCells`)
- `degraded: boolean` — true when the box had to coarsen to fit its budget
- `sleeping: boolean` — a sleeping box adds nothing: the analytic field stays (no body, nobody near it)
- `readonly bodies: Map<string, Placed>`
- `time: number` — the world time of the field in the grid, s
- `steps: number`
- `readonly oa: number`
- `readonly ob: number`
- `readonly level: number`
- `constructor(def: FlowBoxDef, readonly field: FlowField)`
- `get medium(): Medium`
- `get cells()`
- `dispose()`
- `world(a: number, b: number): V3`
- `contains(p: V3): boolean`
- `addBody(d: BoxBodyDef): void`
- `removeBody(id: string)`
- `setBody(id: string, s: { position: [number, number]; angle?: number; velocity?: [number, number]; omega?: number })` — move a body: its position, angle and velocity in the plane
- `maxSpeed(): number` — The largest speed in the grid, m/s.
- `step(dt: number, t: number, maxSub = 4)` — Advance by dt at world time t (the box sub-steps so the Courant number stays near one, at most `maxSub` times).
- `force(id: string): [number, number]` — The force of the flow on a body, N, in the two axes of the plane (per metre of slice times the body's depth).
- `velocityAt(p: V3): V3`
- `pressureAt(p: V3): number` — pressure at a point, Pa gauge (zero at the outflow)
- `dynamicPressure(p: V3): number` — dynamic pressure ½·ρ·v², Pa
- `slice(every = 1): Array<{ at: V3; velocity: V3; speed: number; pressure: number; solid: boolean }>` — A decimated slice for the inspector: points with velocity, speed and pressure (at most 4096).
- `wake()`
- `sleep()`

### FlowGrid

*class* · `packages/flow-solver/src/grid.ts:25`

```ts
class FlowGrid
```

- `readonly nx: number`
- `readonly ny: number`
- `readonly h: number`
- `readonly density: number`
- `nu: number`
- `confinement: number`
- `iterations: number`
- `tolerance: number`
- `u: Float64Array`
- `v: Float64Array`
- `p: Float64Array`
- `readonly owner: Uint16Array` — 0 fluid, otherwise the id of the body that owns the cell (1 = static colliders)
- `readonly sx: Float64Array` — velocity of the solid in a solid cell (the wall's velocity)
- `readonly sy: Float64Array`
- `sides: [Side, Side, Side, Side]` — boundary types: left, right, bottom, top; and the velocity prescribed on inflow faces (per face along that side)
- `readonly inL: Float64Array`
- `readonly inR: Float64Array`
- `readonly inB: Float64Array`
- `readonly inT: Float64Array`
- `divergence: number` — divergence left after the last projection (max over fluid cells, 1/s), iterations used, and the residual
- `used: number`
- `residual: number`
- `constructor(o: GridOptions)`
- `solid(i: number, j: number): boolean`
- `fluid(i: number, j: number): boolean` — the cell is fluid and inside the grid
- `applyBoundary()` — Put the values of the boundaries and of the solids on the faces they own.
- `sampU(x: number, y: number): number` — u and v at a cell-unit position, as numbers (no allocation: the advection calls these a million times)
- `sampV(x: number, y: number): number`
- `velocityCells(x: number, y: number): [number, number]` — velocity at a point in cell units (x, y from the grid origin / h)
- `velocityAt(x: number, y: number): [number, number]` — velocity at a point in metres from the grid origin
- `pressureAt(x: number, y: number): number`
- `centre(i: number, j: number): [number, number]` — velocity at the centre of a cell
- `step(dt: number)`
- `forces(): Map<number, SolidBody>` — The force the flow puts on every body (per unit depth, N/m): the pressure on the faces that touch the fluid plus the wall shear.

## Functions

### applyFlowTier

*function* · `packages/flow-solver/src/box.ts:274`

```ts
function applyFlowTier(field: FlowField, tier: { flowSources: number; flowBoxCells: number }): void
```

Give a field the source budget of a tier.

### flowBoxes

*function* · `packages/flow-solver/src/capability.ts:45`

```ts
function flowBoxes(fieldOf: (host: PhysicsHost) => FlowField | undefined): FlowBoxCapability
```

### flowBoxForms

*function* · `packages/flow-solver/src/capability.ts:135`

```ts
function flowBoxForms(br: FormBridge, cap: FlowBoxCapability): void
```

### flowTierBudget

*function* · `packages/flow-solver/src/box.ts:270`

```ts
function flowTierBudget(tier: { flowSources: number; flowBoxCells: number }, every = 1): { maxSources: number; maxCells: number }
```

The budget of a hardware tier for the flow: the sources a field takes and the cells a box may have when stepped every `every`-th physics step.

### validateFlowBox

*function* · `packages/flow-solver/src/capability.ts:20`

```ts
function validateFlowBox(d: unknown, path = 'flowBoxes'): string[]
```

## Interfaces

### BoxBodyDef

*interface* · `packages/flow-solver/src/box.ts:15`

```ts
interface BoxBodyDef
```

- `id: string`
- `shape: BoxShape`
- `depth?: number` — extent along the slice normal, m (the force is the force per metre times this); default 1
- `position?: [number, number]` — centre in the plane, m (world coordinates of the plane's two axes)
- `angle?: number`
- `velocity?: [number, number]`
- `omega?: number`
- `static?: boolean` — static colliders are voxelised once

### FlowBoxCapability

*interface* · `packages/flow-solver/src/capability.ts:39`

```ts
interface FlowBoxCapability extends Capability
```

- `readonly boxes: Map<string, { box: FlowBox; def: FlowBoxLevelDef; acc: number }>`
- `add(def: FlowBoxLevelDef): FlowBox`
- `remove(name: string): void`

### FlowBoxDef

*interface* · `packages/flow-solver/src/box.ts:29`

```ts
interface FlowBoxDef
```

- `name: string`
- `plane?: 'xz' | 'xy'`
- `origin: V3` — the corner of the box with the smallest coordinates, [x, y, z]; the coordinate off the plane is the slice's `level`
- `size: [number, number]` — extent in the plane, m
- `cell: number` — cell size, m
- `medium?: Medium`
- `density?: number` — kg/m3 and Pa·s; defaults of the medium
- `viscosity?: number`
- `margin?: number` — cells over which the solved field blends into the analytic one at the edge (default 4)
- `maxCells?: number` — cells most the box may have; more are coarsened and `degraded` is set (default 40000)
- `confinement?: number`
- `iterations?: number`
- `bodies?: BoxBodyDef[]`
- `forces?: 'solved' | 'coefficients'` — `solved`: the pressure on the surfaces drives the bodies; `coefficients`: only the field is solved (cheap), the media drag stays
- `reach?: number` — half-thickness of the slice along its normal beyond which the box adds nothing, m (default 5)

### FlowBoxLevelDef

*interface* · `packages/flow-solver/src/capability.ts:14`

```ts
interface FlowBoxLevelDef extends Omit<FlowBoxDef, 'bodies'>
```

The `flowbox` capability: the flow boxes of a level, stepped in the `field` phase, with the rigid bodies in them.

Level block `physics.flowBoxes`: a list of `FlowBoxDef` where each body may have a `node` (a rigid body of the level whose position, angle and velocity
in the plane the box takes every step, and which the force of the flow is applied to when the box's `forces` is `solved`).
`solved`: the pressure and shear on the body's surface drive it and the media drag of a body in an air box is switched off (`exempt: ['media']`),
so nothing is counted twice. `coefficients` (default): the box only solves the field (the wake for the render and for sails); the media drag stays.
A box sleeps while it has no body and nobody `wake`s it; `every` steps it every N-th physics step.

- `bodies?: Array<BoxBodyDef & { node?: string }>`
- `every?: number` — step the solver every N-th physics step with the accumulated time (default 1)

### FormBridge

*interface* · `packages/flow-solver/src/capability.ts:134`

```ts
interface FormBridge
```

Sensors for the language (3.9): `(pressure-at x y z)`, `(dynamic-pressure x y z)` read the box that contains the point.

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

### GridOptions

*interface* · `packages/flow-solver/src/grid.ts:11`

```ts
interface GridOptions
```

- `nx: number`
- `ny: number`
- `h: number`
- `density: number`
- `nu: number` — kinematic viscosity m2/s
- `confinement?: number` — vorticity confinement strength, 0 = off
- `iterations?: number` — most CG iterations per projection (default 80) and the relative residual to stop at (default 1e-5)
- `tolerance?: number`

### SolidBody

*interface* · `packages/flow-solver/src/grid.ts:23`

```ts
interface SolidBody
```

- `id: number`
- `fx: number`
- `fy: number`
- `cells: number`

## Types

### BoxShape

*type* · `packages/flow-solver/src/box.ts:10`

```ts
type BoxShape = | { type: 'circle'; radius: number } | { type: 'box'; half: [number, number] } | { type: 'polygon'; points: Array<[number, number]> }
```

A flow box: a bounded slice of the level where the flow is solved. It takes its inflow from the analytic sources of the level
(`@ferret/flow`), solves the flow round the bodies in it, and publishes itself back into the field as a `solved` source, so the media, the vessels,
the character, the rotors and the render read the wake without a change on their side. `2d` slices: `plane: 'xz'` is horizontal (the streets of a
town, a river seen from above) at height `level`, `plane: 'xy'` vertical (the section of a roof or a wing) at depth `level`.

### Side

*type* · `packages/flow-solver/src/grid.ts:10`

```ts
type Side = 'wall' | 'inflow' | 'outflow'
```

The solver of the flow box: incompressible flow on a staggered (MAC) grid in two dimensions. One step is: boundary
values, advection (MacCormack with a limiter, so a coarse grid keeps its eddies), viscosity, optional vorticity confinement, and a pressure projection
(conjugate gradient with a modified incomplete Cholesky preconditioner, a fixed iteration cap, residual test on the data only). Everything is a
function of the data, never of the clock, so two peers with the same history have the same field.

Units are SI: cell size `h` in m, velocity m/s, density kg/m3, pressure Pa (gauge: zero at the outflow). u lives on the vertical faces
((nx+1) x ny), v on the horizontal faces (nx x (ny+1)), pressure and the solid flags in the cells.

