# Heat

# 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 (skill `ferret-physics-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:

| Path | Law | Dials |
|---|---|---|
| **conduction** between joined parts | `Q = G (T₁ − T₂)` | conductance `G` (W/K); a wall of layers in series: `wallConductance` = `A / Σ(Lᵢ/kᵢ)` |
| **convection** to the surroundings | `Q = h A (T − T_amb)` | `h` (W/m²K) and `area`; `EXAMPLE_CONVECTION` has still-air and flowing values |
| **radiation** | Stefan–Boltzmann `ε σ A (T⁴ − T_amb⁴)` | `radiates: true`, `emissivity` |
| **sources** | `power` (W) or `add(part, joules)` | continuous heating or a pulse |
| **mechanical loss** | share `frictionToHeat` of the energy lost in a contact | friction, 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

```ts
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` (skill `ferret-physics-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).

```ts
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 value | Default | Effect |
|---|---|---|
| `ambient` | 293.15 K | temperature of the surroundings |
| `frictionToHeat` | 1 | share of lost mechanical energy that becomes heat |
| `contactConductance` | 0 | W/K between parts that touch |

| `PartDef` | Meaning |
|---|---|
| `mass` or `volume` | kg, or volume with the material's density |
| `T`, `ambient` | start temperature, K; its own surroundings |
| `area`, `h`, `radiates` | exposure to the surroundings |
| `fixed` | a heat bath held at its temperature |
| `power` | continuous heating, W |
| `length`, `held`, `reference` | bar for thermal stress; stress-free temperature |
| `limit` | temperature that raises `over` once |

## Interplay

- `links` (skill `ferret-physics-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` (skill `ferret-physics-strength`): a part over its stress limit breaks the body (when strength is on).
- `combustion` (skill `ferret`) and `chemistry` (skill `ferret-physics-chemistry-nuclear`) produce heat for parts; `nuclear` (skill `ferret-physics-chemistry-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

| Symptom | Cause |
|---|---|
| Temperature never changes | no source, no link, no convection area: heat has nowhere to flow; check `area` and `h` |
| Phase never changes | the material has no `melt` or `boil`, or the part has not stored the latent heat yet (the plateau is correct) |
| Energy does not close | you 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 breaks | `sigmaMax` missing, or the temperature rise is below `σ/(E α)` (about 170 K for steel) |
| Heat from friction is absent | the body has no part (`attach`) or the `heat` capability is off |
| Stress is positive | cooled and held: tension |

## Reference

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