FERRETGame engine over Babylon.js
Ferret

Links between capabilities

Package @ferret/physics-links, capability name links, phase stretch (order 1000, last).

What it is for#

Each physics capability knows only itself. A link is where one capability's output becomes another's input, so a world closes its loops: a chemical vessel sits in a water bath, a hot wall warms the gas of a tank, a slipping brake heats its drum, steam from a boiler turns a shaft, a reactor's coolant drives a turbine, a screw on a shaft pushes a ship and loads the engine, an arc fault blasts, an overloaded wire lights a fire.

Links read and write the plain fields and methods of the objects they join (a heat part, a chemical vessel, a gas volume, a shaft, a reactor, a thruster, a kernel body), so the package imports none of their packages and you can write your own in a dozen lines.

The model#

A Link is { kind, moved, step(dt, host, contacts) }. step reads one side, writes the other, once per world step after the capabilities have stepped; moved counts the energy that crossed (J). The links capability holds a list of them and keeps the scene energy account: you register terms (stores such as kinetic, heat, chemical, electric; and sinks such as losses and the condenser), and energy() sums them. In a closed scene the total stays where it started.

LinkJoinsDoes
vesselInPart(vessel, part, net)Q → Hthe part is the vessel's surroundings; what the vessel loses goes into the part
gasInPart(gas, part, net, G, at?)gas ↔ Hheat flows through G W/K; a hot wall raises the pressure of a tank
slipHeats(coupling, part, net)M → Hwhat a slipping clutch, belt or brake loses heats the part
plasticHeats(link, part, net, share?)E → H90 % of the plastic work of a stretched link becomes heat
stagnationHeats(part, net, {speed, ambient, G})air → Ha skin warms toward T + r v² / 2cp
reservoirInPart(res, part, net, {G, density, cp})L → Ha tank's liquid warms to its wall; the wall pays what the liquid took
reactorHeatsPart(reactor, part, net, G)N → Hdecay heat that the coolant no longer carries warms the structure
turbineOnReactor(reactor, shaft, {efficiency, maxTorque})N → Ma share of the coolant's heat becomes shaft torque
engineOnBoiler(boiler, shaft, {area, exhaust, work, maxTorque, throttle?})K → Msteam through a throttle turns the shaft; the boiler loses it with its enthalpy
boilerFed(boiler, tank, {setpoint, rate})L → Ka feed pump moves liquid into a boiler; an empty tank starves it
screwOnShaft(thruster, shaft, {diameter, kt, kq})M → Vthrust from shaft speed, the water's torque back on the shaft
armatureOf(coil, body, pole, kernel), coilPulls(...)W → bodiesan electromagnet pulls an armature
arcFlash(world, {circuit, at, voltage, detonate})W → Xfrom a short until the circuit clears, the arc's energy goes into a blast
wireFire(world, {circuit, element, target})W → firean overheated wire ignites its target once
mudOver(terrain, media, {box})T → Dwet ground makes a mud medium; it goes when the ground dries
navFollowsTerrain(world, terrain, {rebuild})T → nava dug trench or heap rebuilds the navigation mesh once digging stops

First use: a brake heats its drum, and the energy adds up#

import { slipHeats, links } from '@ferret/physics-links';

const cap = links();
cap.add(slipHeats(brake, drum, net));                          // the brake's losses become heat in the drum
cap.account({ name: 'wheel', joules: () => train.kinetic() }); // the stores that make up the scene's energy
cap.account({ name: 'drum', joules: () => drum.H });
cap.energy().total;                                            // stays where it started, in a closed scene

When the brake slows the wheel, its rotation goes into the drum: the wheel's term falls, the drum's rises by the same amount, and the total stays put. If the sum of your terms drifts, a link or a capability is losing energy. That is the way to find a bug in a scene.

Recipes#

A hot wall and a tank#

cap.add(gasInPart(gas, wall, net, 2));       // 2 W/K between the wall and the gas

A hot wall warms the gas, and the pressure in a closed tank rises with it. The same pattern with a cooling wall and a vessel (vesselInPart) gives a reactor in a water bath; a vessel that raises chem:runaway in a bath that cannot carry the heat is a thermal-runaway scenario.

A turbine on a reactor#

import { turbineOnReactor } from '@ferret/physics-links';

cap.add(turbineOnReactor(reactor, shaft, { efficiency: 0.33, maxTorque: 1e5 }));

A third of the coolant's heat becomes torque on the shaft. Chain it on: a generator on that shaft (electric bindMachine) lights a lamp whose power comes from the reactor. The steam path in between has its own link, engineOnBoiler.

In a world#

const cap = links();
host.register(cap);
host.enable('links');
cap.add(slipHeats(brake, drum, heat.network));
cap.account({ name: 'wheel', joules: () => train.kinetic() });

The capability steps every link after the other capabilities, once per world step. links.energy (command) returns { total, terms }. Events from the capabilities (circuit:short, circuit:trip, circuit:overheat) are what arcFlash and wireFire listen to; each fires once per fault and has dispose().

A link is any object with kind, moved and step. Read the source, write the target, add the energy that crossed to moved:

const solarHeatsRoof: Link = {
  kind: 'sun→roof', moved: 0,
  step(dt) { const q = irradiance() * area * dt; net.add(roofPart, q); this.moved += q; },
};
cap.add(solarHeatsRoof);

Budget#

A few reads and writes per link per step; hundreds of links are free.

Debugging#

SymptomCause
The energy total driftsa link writes without taking from the other side, or a store is missing from terms
A link does nothingit was not added to the capability, or links is not enabled
Heat appears twicetwo links join the same pair (a part in a vessel and the vessel in the part)
Order effectsa link runs after the capabilities have stepped: what it writes is used next step
arcFlash fires twiceit does not: one blast per fault; a second needs the circuit to clear first

Reference#

links, LinksCapability, Link, EnergyTerm, vesselInPart, gasInPart, slipHeats, turbineOnReactor, engineOnBoiler, screwOnShaft, arcFlash, wireFire, mudOver, boilerFed, @ferret/physics-links.