FERRETGame engine over Babylon.js
Ferret

Heat

Package @ferret/physics-heat, capability name heat, phase stretch (order 1000: it runs at the very end of the step).

What it is for#

Things that get hot and cold: a block of ice that melts, a pot of water that boils, a steel beam that sags in a fire, a bar held between two walls that buckles when heated, a brake disc that warms by the work of braking, a wall that insulates. Heat is the bookkeeper of energy: work lost in contacts (friction, inelastic hits) turns into heat in the parts that touched, so the energy of a scene closes. Other capabilities hand heat to it: a fire heats a pot (see links), a reaction heats a vessel.

The model#

A HeatNetwork is lumped parts joined by conductances. A part (a body, a wall segment, a gas, a reservoir) has mass, a material, a temperature T, and an enthalpy H (J, counted from 0 K as a solid). The state is the enthalpy, so a phase change holds the temperature while its latent heat is paid, and the energy balance closes exactly. The network is stepped by Crank–Nicolson (second order, stable at any step); parts run in insertion order, so a replay gives the same temperatures.

Heat moves by:

PathLawDials
conduction between joined partsQ = G (T₁ − T₂)conductance G (W/K); a wall of layers in series: wallConductance = A / Σ(Lᵢ/kᵢ)
convection to the surroundingsQ = h A (T − T_amb)h (W/m²K) and area; EXAMPLE_CONVECTION has still-air and flowing values
radiationStefan–Boltzmann ε σ A (T⁴ − T_amb⁴)radiates: true, emissivity
sourcespower (W) or add(part, joules)continuous heating or a pulse
mechanical lossshare frictionToHeat of the energy lost in a contactfriction, rolling, inelastic hits

A material (HeatMaterial): density, c (specific heat J/kgK), k (conductivity), emissivity, melt and boil ({T, latent, c}: the temperature, the latent heat J/kg and the specific heat of what comes after), alpha (linear expansion 1/K), E (Young's modulus), sigmaMax and strengthCurve (stress held, and its fall with temperature as [K, factor] points), ignition (K). EXAMPLE_HEAT_MATERIALS: steel, copper, aluminium, concrete, brick, wood, glass, insulation, water, ice.

First use: ice melts, water warms#

import { HeatNetwork, EXAMPLE_HEAT_MATERIALS as M } from '@ferret/physics-heat';

const net = new HeatNetwork();
net.ambient = 293.15;
const ice = net.addPart({ name: 'ice', mass: 1, material: M.ice, T: 263.15, power: 1000 });        // 1 kg at -10 C, heated with 1 kW
const phases: string[] = [];
net.onEvent = e => { if (e.kind === 'phase') phases.push(e.to); };
let reached = -1, liquid = -1, t = 0;
while (t < 2000 && liquid < 0) {
  net.step(0.25); t += 0.25;
  if (reached < 0 && ice.T >= 273.15 - 1e-9) reached = t;
  if (ice.phase === 'liquid') liquid = t;
}
const plateau = (liquid - reached) * 1000;                                                          // J paid at the melting point

The temperature sits at 273.15 K until the latent heat (334 kJ for 1 kg) has been paid, to within 1 %, then rises as water at 4186 J/kgK. Boiling does the same at 373.15 K with 2.26 MJ/kg. Events: heat:phase { part, to, from }, heat:over { part, limit, value } (a limit of temperature or 'stress'), heat:ignite { part } (a part with an ignition temperature passes it).

Recipes#

Cooling to the surroundings#

A part with area and h cools as T_env + (T₀ − T_env) e^(−t/τ) with τ = m c / (h A). Give ambient per part when one object sits in a different environment (a freezer), or set the world's ambient (293.15 K by default).

Two things in contact#

net.join(a, b, G) makes them exchange heat; they reach the heat-capacity-weighted mean and the stored energy stays constant within 0.1 %. In a world, contactConductance (W/K, 0 = none) makes parts that touch exchange heat.

A wall with layers, a rod with a transient#

net.grid({ name, material, nx, dx, thickness, edge: { left: T, right: T } }) builds a rod of cells between held ends; steady conduction is k A ΔT / L, a multi-layer wall is the series resistance, and the transient follows the error-function solution within 3 %. wallConductance is the quick way to get G for join.

A bar that cannot grow#

A free bar grows by α L ΔT (expansion); a held bar (held: true, length) carries a thermal stress E α ΔT (compression when heated), reported by net.stress(part) and thermalStress. Past sigmaMax (times strengthFactor(material, T) when the material has a strengthCurve) the part raises heat:over with limit: 'stress', and with strength on it breaks the body. A steel beam therefore fails sooner in a fire than on a hot day: its curve takes strength away (strengthFactor(steel, 873 K) = 0.4).

import { HeatNetwork, EXAMPLE_HEAT_MATERIALS as M, expansion, thermalStress, type HeatMaterial } from '@ferret/physics-heat';

const flat: HeatMaterial = { ...M.steel, strengthCurve: undefined };
const net = new HeatNetwork(); net.ambient = 293.15;
const bar = net.addPart({ name: 'bar', mass: 1, material: flat, T: 293.15, length: 1, held: true, power: 490 });   // 1 K per second
let breaksAt = -1;
net.onEvent = e => { if (e.kind === 'over' && e.limit === 'stress') breaksAt = e.part.T; };
for (let t = 0; t < 400; t += 0.5) net.step(0.5);
const expected = 293.15 + 400e6 / (200e9 * 12e-6);                       // sigmaMax / (E alpha)

Friction turns into heat#

With the capability on, a body given a temperature (cap.attach(entity, { material: 'steel', T: 293.15 })) warms by exactly the mechanical energy lost: a 2 kg block sliding at 5 m/s to rest warms by ½ m v² / (m c) and the energy of the scene closes. frictionToHeat (default 1) is the share that becomes heat; lower it if you want some to be sound.

In a world#

heat() is a capability with its own network and material library; cap.attach(entity, def) gives a rigid body's entity a part (and cap.partOf(entity) reads it). Commands: heat.part.add {name, material, mass|volume, T?, area?, h?, radiates?, fixed?, power?}, heat.set {part, T}, heat.add {part, joules?|watts?}, heat.join {a, b, conductance}, heat.state (temperatures, phase, stored energy), heat.info, heat.step {dt}.

Parameters#

Capability valueDefaultEffect
ambient293.15 Ktemperature of the surroundings
frictionToHeat1share of lost mechanical energy that becomes heat
contactConductance0W/K between parts that touch
PartDefMeaning
mass or volumekg, or volume with the material's density
T, ambientstart temperature, K; its own surroundings
area, h, radiatesexposure to the surroundings
fixeda heat bath held at its temperature
powercontinuous heating, W
length, held, referencebar for thermal stress; stress-free temperature
limittemperature that raises over once

Interplay#

  • links join heat to other capabilities: vesselInPart (a chemical vessel sits in a part), gasInPart (a hot wall warms a tank's gas and raises its pressure), slipHeats (a slipping clutch heats its housing).
  • strength: a part over its stress limit breaks the body (when strength is on).
  • combustion and chemistry produce heat for parts; nuclear produces decay heat.
  • A material ignition temperature raises heat:ignite: what burns is the job of combustion.

Budget#

Linear in parts and links: hundreds of parts per step are cheap; a grid of 1000 cells is still well below a millisecond. Crank–Nicolson is stable at any step, so you can step the network at a coarser rate than physics when only slow heating matters.

Debugging#

SymptomCause
Temperature never changesno source, no link, no convection area: heat has nowhere to flow; check area and h
Phase never changesthe material has no melt or boil, or the part has not stored the latent heat yet (the plateau is correct)
Energy does not closeyou changed a part's T by hand (setTemperature) or a held/fixed part exchanged heat with the boundary: see the boundary count
A held bar never breakssigmaMax missing, or the temperature rise is below σ/(E α) (about 170 K for steel)
Heat from friction is absentthe body has no part (attach) or the heat capability is off
Stress is positivecooled and held: tension

Reference#

heat, HeatCapability, HeatNetwork, PartDef, HeatMaterial, HeatLibrary, EXAMPLE_HEAT_MATERIALS, expansion, thermalStress, strengthFactor, wallConductance, HEAT_DEFAULTS, @ferret/physics-heat.