FERRETGame engine over Babylon.js
Ferret

@ferret/physics-nuclear

decay, activation, radiation dose and shielding, reactor kinetics, fallout.

46 exports · source in packages/physics-nuclear/src

Classes

Assembly class

class Assembly
spec: AssemblySpec
F: numberfission rate now, 1/s
fissions: numberfissions so far
time: number
critical: boolean
disassembled: boolean
onEvent: ((e: AssemblyEvent) => void) | null
constructor(spec: AssemblySpec)
get energy()J released so far
get fissioned()kg of nuclei that fissioned
get expansion()compression that the heating has undone: the assembly expands as the energy per kg approaches its disassembly energy
get criticalMassNow()the effective critical mass now, kg
get k()multiplication
step(dt: number)

packages/physics-nuclear/src/assembly.ts:34

Fallout class

class Fallout

What a release leaves on the ground: a grid of cells, each with its own inventory that decays exactly like any other, so a cell that holds Cs-137 loses its activity by the half-life and one that holds Ba-137m next to it keeps the daughter in step. Washing (rain, a hose) takes a share of every isotope off a cell, to a sink that is not tracked here.

readonly cell: number
readonly library: IsotopeLibrary
constructor(cell = 1, library: IsotopeLibrary = new IsotopeLibrary())
deposit(x: number, z: number, isotope: string, atoms: number)Put atoms of an isotope on the cell holding (x, z).
deposit_disc(x: number, z: number, radius: number, isotope: string, bq: number)Spread an activity (Bq) of an isotope evenly over a disc of the given radius.
surfaceActivity(x: number, z: number, isotope?: string): numberBq per m² on the cell holding (x, z): all isotopes, or one (a daughter grows in beside its parent, so the sum can rise as the parent falls).
get total(): numberBq on the whole ground.
decay(dt: number)Let every cell decay.
wash(fraction: number, at?: [number, number])Take the share fraction of every isotope off the cell holding (x, z) or, with no position, off all of them.
doseRate(x: number, z: number, height = 1, o: { mu?: number; coefficient?: number; reach?: number } = {}): numberThe gamma dose rate (Sv/s) at (x, z) and height m above the ground: every cell is a point source of its gamma photons per second (activity x the gamma yield of each isotope), seen through the air as e^(-mu r) / (4 pi r^2), summed over the cells within reach m. mu is the attenuation of air for ~0.7 MeV photons (0.0035 /m); the dose coefficient is the library's fluence-to-dose for gammas.

packages/physics-nuclear/src/fallout.ts:9

Inventory class

class Inventory

Atoms of each isotope in a body, a vessel or a patch of soil.

readonly library: IsotopeLibrary
readonly atoms: Map<string, number>
stream: (() => number) | nullwhen set, the number of decays in a step is drawn from the Poisson law instead of taken at its expectation
constructor(library: IsotopeLibrary = new IsotopeLibrary())
add(isotope: string, atoms: number)
addActivity(isotope: string, bq: number)Add an amount by its activity in Bq.
amount(isotope: string)
activity(isotope?: string): numberBq
get mass(): numberkg
get power(): numberHeat power now, W
decay(dt: number): DecayResultDecay for dt seconds. Exact for the expectation (matrix exponential of the network, with the decays integrated alongside).

packages/physics-nuclear/src/decay.ts:108

IsotopeLibrary class

class IsotopeLibrary
constructor(initial: Record<string, IsotopeDef> = EXAMPLE_ISOTOPES)
add(name: string, d: IsotopeDef)
get(name: string): IsotopeDef
has(name: string)
names()
lambda(name: string): number1/s

packages/physics-nuclear/src/decay.ts:49

Reactor class

class Reactor
readonly spec: Required<Pick<ReactorSpec, 'power' | 'generation' | 'rodWorth' | 'rodsCritical' | 'fuelHeat' | 'coolantHeat' | 'exchange' | 'inlet' | 'scramSpeed'>> & ReactorSpec
readonly beta: number[]
readonly lambda: number[]
P: numberfission power now, W
C: number[]delayed precursor groups in power units, W
Tf: number
Tc: number
sink: numberheat taken from the fuel by whatever it is linked to (a structure, a pool), W; set by a link
readonly Tf0: number
readonly Tc0: number
rods: number
target: numbera rod position the controller is driving to (the scram sets it to 1)
external: numberextra reactivity from outside (a game's own cause)
iodine: numberiodine-135 and xenon-135 as a share of their equilibrium at nominal power (1 = nominal equilibrium)
xe: number
flow: number
scrammed: boolean
melted: boolean
time: number
fissions: number
energy: numberJ of fission energy released
maxSubstep: numberthe longest sub-step, s; raise it for a long coast where nothing changes quickly
onEvent: ((e: ReactorEvent) => void) | null
constructor(spec: ReactorSpec)
get betaTotal()
get void(): number
get rho(): numberreactivity (Δk/k)
get decayHeat(): numberdecay heat power now, W
get thermal(): numberheat into the fuel, W
get fissionRate()fissions per second
get period(): numbere-folding time of the power, s (Infinity when steady)
scram()
setRods(position: number)
step(dt: number)Advance dt seconds. Sub-steps follow the fastest rate of the kinetics.

packages/physics-nuclear/src/reactor.ts:84

ShieldLibrary class

class ShieldLibrary
constructor(initial: Record<string, ShieldMaterial> = EXAMPLE_SHIELDS)
add(name: string, s: ShieldMaterial)
get(name: string): ShieldMaterial | undefined
names()
transmission(kind: RadiationKind, layers: Layer[]): numberThe share of a beam of this kind that gets through the layers.

packages/physics-nuclear/src/radiation.ts:35

Functions

activate function

function activate(inv: Inventory, captures: readonly Capture[], flux: number, dt: number): number

Neutron activation. A flux Φ (neutrons per m² and s) turns the nuclei of a target into its radioactive product at the rate σ Φ per nucleus: N_to gains N_from (1 - e^(-σΦt)) over t and the target loses the same, so what is made is never more than there was. The product then decays with the rest of the inventory (call inv.decay(dt) after this, which makes the activity rise to the saturation value N_from σ Φ and no further). Returns the atoms made.

packages/physics-nuclear/src/activation.ts:15

chord function

function chord(b: Body, origin: Vec3, dir: Vec3, maxDistance: number): { enter: number; length: number } | null

Length of the chord a ray cuts through a body, from the point it enters, m (planes have no thickness here).

packages/physics-nuclear/src/radiation.ts:62

doseRate function

function doseRate( inventory: Inventory, r: number, layers: Layer[], shields: ShieldLibrary, coefficients: Record<RadiationKind, number> = EXAMPLE_DOSE_COEFFICIENTS, ): Record<RadiationKind, number>

Dose rate (Sv/s) of a source at a point through the given layers, by kind.

packages/physics-nuclear/src/radiation.ts:121

expm function

function expm(A: number[][]): number[][]

exp of a small dense matrix by scaling and squaring

packages/physics-nuclear/src/decay.ts:84

inhourRoot function

function inhourRoot(rho: number, generation: number, groups = DELAYED_GROUPS): number

The stable period of the point-kinetics model for a reactivity step: the largest root ω of the inhour equation ρ = Λω + Σ βᵢω/(ω+λᵢ).

packages/physics-nuclear/src/reactor.ts:236

layersBetween function

function layersBetween(host: PhysicsHost, from: Vec3, to: Vec3, shieldOf: (b: Body) => string | undefined, except: Body[] = []): Layer[]

The layers a ray crosses between two points: every body in the way, by the shield material its physics material names, plus the air between. except bodies (the source and the receiver themselves) are not counted.

packages/physics-nuclear/src/radiation.ts:91

nuclear function

function nuclear(isotopes = new IsotopeLibrary(), shields = new ShieldLibrary()): NuclearCapability

packages/physics-nuclear/src/index.ts:69

poisson function

function poisson(mean: number, rand: () => number): number

A count drawn from the Poisson law (Knuth for small means, the normal approximation above 50).

packages/physics-nuclear/src/decay.ts:71

rng function

function rng(seed: number)

Deterministic seeded stream (mulberry32).

packages/physics-nuclear/src/decay.ts:65

solve function

function solve(A: number[][], b: number[]): number[]

Solve A·x = b by Gaussian elimination with partial pivoting.

packages/physics-nuclear/src/reactor.ts:222

totalOf function

function totalOf: (r: Record<RadiationKind, number>) => number

packages/physics-nuclear/src/radiation.ts:137

wayWigner function

function wayWigner(tau: number, top = Infinity): number

Way–Wigner: fraction of the operating power left as decay heat τ seconds after shutdown from top seconds of operation.

packages/physics-nuclear/src/reactor.ts:16

Interfaces

AssemblyEntry interface

interface AssemblyEntry extends Placed
assembly: Assembly
spent: boolean

packages/physics-nuclear/src/index.ts:46

AssemblyEvent interface

interface AssemblyEvent
type: 'critical' | 'disassembled'
energy: number
fissioned: number
time: number

packages/physics-nuclear/src/assembly.ts:32

AssemblySpec interface

interface AssemblySpec
mass: numberkg of fissile material
criticalMass: numberthe bare critical mass of the material in this shape, kg (data)
reflector?: numbercritical mass is multiplied by this (a reflector < 1)
compression?: numberdensity relative to the data's (compression > 1 lowers the critical mass as 1/c²)
exponent?: numberexponent of k against m / m_c (1 = k equals the ratio)
generation?: numbergeneration time, s
source?: numberstray neutrons per second that start a chain
disassemblyEnergy: numberJ/kg of the assembly that blows it apart (data)
nucleusMass?: numberkg per nucleus

packages/physics-nuclear/src/assembly.ts:11

Capture interface

interface Capture

A capture that makes a radioactive nucleus: the stable from takes a neutron and becomes to, with a cross-section in barns (thermal Co-59: 37, Na-23: 0.53).

from: string
to: string
sigma: number

packages/physics-nuclear/src/activation.ts:7

DecayResult interface

interface DecayResult
heat: numberJ left in the matter by the decays of the step
decays: Map<string, number>decays of each isotope in the step
emitted: Record<RadiationKind, number>particles of each kind that left

packages/physics-nuclear/src/decay.ts:98

IsotopeDef interface

interface IsotopeDef
halfLife: numberseconds; Infinity for a stable nucleus
daughter?: stringwhat it becomes (a name in the library); missing = leaves the books
energy: numbertotal energy per decay, J
retained?: numbershare of it that stays in the matter and becomes heat (1 when the radiation is absorbed where it is born)
emits?: Partial<Record<RadiationKind, number>>particles of each kind that leave the source per decay
atomicMass?: numberkg per atom (for the mass of a quantity)

packages/physics-nuclear/src/decay.ts:10

Layer interface

interface Layer
material: string
thickness: number

packages/physics-nuclear/src/radiation.ts:33

NuclearCapability interface

interface NuclearCapability extends Capability
readonly isotopes: IsotopeLibrary
readonly shields: ShieldLibrary
readonly sources: Map<string, SourceEntry>
readonly receivers: Map<string, ReceiverEntry>
readonly reactors: Map<string, ReactorEntry>
readonly assemblies: Map<string, AssemblyEntry>
coefficients: Record<RadiationKind, number>
addSource(name: string, o?: Placed & { body?: Body; inventory?: Inventory }): Inventory
addReceiver(name: string, o: Placed & { sensitivity?: number; seed?: number; body?: Body }): ReceiverEntry
addReactor(name: string, spec: ReactorSpec, o?: Placed): Reactor
addAssembly(name: string, spec: AssemblySpec, o?: Placed): Assembly
doseAt(receiver: string): Record<RadiationKind, number>Dose rate by kind at a receiver now, Sv/s (through the scene's shielding).

packages/physics-nuclear/src/index.ts:48

ReactorEntry interface

interface ReactorEntry extends Placed
reactor: Reactor

packages/physics-nuclear/src/index.ts:45

ReactorEvent interface

interface ReactorEvent
type: 'scram' | 'prompt' | 'melt'
time: number

packages/physics-nuclear/src/reactor.ts:82

ReactorSpec interface

interface ReactorSpec
power: numbernominal fission power, W
generation?: numberprompt neutron generation time, s
groups?: { beta: number[]; lambda: number[] }delayed groups (defaults to thermal fission)
rodWorth: numberreactivity worth of the whole rod travel, Δρ, and the rod position (0 out .. 1 in) at which the core is critical at nominal
rodsCritical: number
rods?: number
fuelCoefficient?: numbertemperature coefficients, Δρ per K (negative = self-stabilising)
coolantCoefficient?: number
voidCoefficient?: numberΔρ for a fully voided core and the coolant temperature span over which it boils out
boil?: [number, number]
fuelHeat: numberJ/K of the fuel and of the coolant with its structure
coolantHeat: number
exchange: numberW/K from fuel to coolant, and the W/K the coolant loses to the heat sink (flow·cp) at inlet temperature
flow: number
inlet?: number
xenon?: { worth: number; burnout?: number; iodine?: number }Xenon-135 poisoning. worth is the reactivity the equilibrium xenon takes at nominal power (about 0.025 for a thermal core; the rods are critical with it, so a core at nominal power has no xenon term). burnout is the burn-out rate at nominal flux over the decay rate of xenon (about 3 to 4); iodine the share of the xenon yield that comes by iodine-135 (0.95).
burnup?: { worth: number; energy: number }Burn-up: the fuel loses reactivity as it burns, worth (Δρ) for each energy joules of fission energy released (a core of 1 GW(th) for 500 days is 4e16 J; ~30 000 MWd/t is about 0.1 of the fissile loss). The rods make up for it, so a core whose rods are at the end of their travel is a spent core. Both are linear: the model has no fission-product poisoning but xenon.
melt?: numberthe fuel melts here, K
scramSpeed?: numberrod speed in a scram, 1/s of the full travel
trip?: { power?: number; fuel?: number }trips: scram automatically above this power (× nominal) or this fuel temperature

packages/physics-nuclear/src/reactor.ts:38

ReceiverEntry interface

interface ReceiverEntry extends Placed
dose: numberSv so far and Sv/s now
rate: number
sensitivity?: numbercounts per Sv for a counter (a Geiger tube is about 6·10⁹)
counts: number
body?: Body
next: number
stream: () => number

packages/physics-nuclear/src/index.ts:34

ShieldMaterial interface

interface ShieldMaterial

Radiation transport, lumped: a point source gives a fluence that falls as 1/(4πr²); each material along the ray takes a part of it away by its half-value layer; a dose coefficient turns the fluence into a dose. Alphas stop by range, not by a half-value layer.

halfValue?: Partial<Record<RadiationKind, number>>half-value layer by kind, m: the thickness that halves the beam
alphaRange?: numberhow far an alpha gets in it, m
buildup?: numberbuild-up of scattered radiation behind the shield: B = 1 + buildup·μx (0 = none)

packages/physics-nuclear/src/radiation.ts:9

SourceDef interface

interface SourceDef
position: Vec3
inventory: Inventory
body?: Bodythe body that carries it (not counted as a shield)

packages/physics-nuclear/src/radiation.ts:113

SourceEntry interface

interface SourceEntry extends Placed
inventory: Inventory
body?: Body

packages/physics-nuclear/src/index.ts:33

Types

RadiationKind type

type RadiationKind = 'alpha' | 'beta' | 'gamma' | 'neutron'

packages/physics-nuclear/src/decay.ts:8

Constants

BARN const

const BARN: 1e-28

1 barn in m².

packages/physics-nuclear/src/activation.ts:4

DECAY_FRACTION const

const DECAY_FRACTION: number

the steady share of the fission energy that comes out later as decay heat

packages/physics-nuclear/src/reactor.ts:36

DELAYED_GROUPS const

const DELAYED_GROUPS: { beta: number[]; lambda: number[]; }

Standard thermal-fission delayed groups: fractions and decay constants (1/s).

packages/physics-nuclear/src/reactor.ts:10

E_FISSION const

const E_FISSION: 3.2e-11

A lumped power reactor: point kinetics with six delayed-neutron groups, reactivity from the rods and from the temperatures of the fuel and the coolant, a two-lump thermal model, and the decay heat of the fission products. Everything is data; there is no geometry.

packages/physics-nuclear/src/reactor.ts:7

EXAMPLE_DOSE_COEFFICIENTS const

const EXAMPLE_DOSE_COEFFICIENTS: Record<RadiationKind, number>

Sv·m² per particle that crosses a square metre (fluence-to-dose, order of magnitude for ~1 MeV).

packages/physics-nuclear/src/radiation.ts:31

EXAMPLE_ISOTOPES const

const EXAMPLE_ISOTOPES: Record<string, IsotopeDef>

A few real isotopes with their well-known half-lives, as sample data; a game writes its own.

packages/physics-nuclear/src/decay.ts:29

EXAMPLE_SHIELDS const

const EXAMPLE_SHIELDS: Record<string, ShieldMaterial>

Textbook values for roughly 1 MeV photons and fast neutrons (as sample data).

packages/physics-nuclear/src/radiation.ts:19

FISSION_ENERGY_PER_KG const

const FISSION_ENERGY_PER_KG: number

A runaway assembly in the one-group, lumped sense: the multiplication k follows from the mass against a critical mass the game supplies (with a reflector factor and a compression), the fission rate grows on the generation time as dF/dt = (k − 1)/Λ · F + S, and the assembly comes apart as it heats, which ends the reaction. This is a model for a game's accident or device record, with the numbers in the data; it has no geometry and is too coarse to be a design tool.

packages/physics-nuclear/src/assembly.ts:9

MEV const

const MEV: 1.602176634e-13

Radioactive decay: isotopes with half-lives and daughters, an inventory that decays exactly (the matrix exponential of the decay network, so a chain of any length closes), the heat the decays leave in the matter, and a seeded Poisson stream for counters.

packages/physics-nuclear/src/decay.ts:5

NUCLEAR_DEFAULTS const

const NUCLEAR_DEFAULTS: { doseStep: number; }

nuclear processes. Inventories of isotopes decay with the world (the heat goes to H when the carrier has a temperature), dose accumulates in receivers through the shielding the scene puts in the way, reactors run on point kinetics with feedback and decay heat, and a runaway assembly ends in an energy that is handed to X as a blast.

values (each at world / level / zone / entity scope):

  doseStep    dose between two `radiation:dose` events, Sv   (1e-3)

events: decay { source, decays }, radiation:dose { receiver, dose }, radiation:counts { receiver, n },

        `reactor:scram` / `reactor:prompt` / `reactor:melt` { name }, `assembly:critical` { name }, `assembly:disassembled` { name, energy, tnt }.

packages/physics-nuclear/src/index.ts:30

Sv_PER_MSV const

const Sv_PER_MSV: 0.001

packages/physics-nuclear/src/radiation.ts:138

YEAR const

const YEAR: number

packages/physics-nuclear/src/decay.ts:6