# Combustion

# Combustion

Package `@ferret/physics-combustion`, capability `combustion`.

## What it is for

Everything that burns something in a closed space and uses the pressure: a gun, a rocket motor, a petrol or diesel engine, a jet engine, a steam boiler, a gas cylinder that gets hot and bursts.

## How it works

The model is a **lumped** one. A chamber is one volume of ideal gas with a mass, a temperature and a pressure. Fuel burns by a law, heat leaves through the wall, and gas leaves through an opening. Because the state is mass and energy, every process closes its energy balance: nothing appears from nowhere.

A `Chamber` holds gas (`AIR`, `COMBUSTION_GAS`, a `GasSpec`, or any gas or mixture of the species library of chemistry (skill `ferret-physics-chemistry-nuclear`): `gas: 'CO2'`, `gas: { N2: 0.5, CO2: 0.5 }`, with the molar mass from the formula and `gamma` from `cp(T)` at the chamber's temperature; `gasSpec(gas, T)` gives the same numbers) and can hold:

- a **premixed fuel**, with a heating value, an efficiency (`premixFuel('CH4')`, `'C3H8'`, `'H2'`, `'C2H5OH(l)'`, `'C8H18(l)'` take the heating value and the oxygen per kilogram from the reaction enthalpy of the library: the lower heating values of the tables), the oxygen it needs per kilogram, and flammability limits. `ignite()` only lights a mixture inside the limits and with oxygen. A lit chamber that runs out of oxygen goes out (`combustion:out`);
- a **solid propellant** that burns on its surface at a rate that rises with pressure, which is why a rocket grain finds its own pressure where burning meets the flow through the throat;
- a **wall** that takes heat, and a `burstPressure` above which it ruptures.

Gas leaves through an `Orifice { area, Cd }`, with choked flow at high pressure; the pressure outside is the capability's `ambient` (101325 Pa).

When the chamber burns its fuel, the pressure rises by `(γ − 1)·m·Q / V`: the energy released, spread over the volume. A mixture outside its limits does not light, and with no oxygen nothing burns, which is how a sealed room smothers a fire.

## What you can build from it

| Piece | What it gives |
|---|---|
| `fireGun` | a shot from a barrel: muzzle velocity, peak pressure, recoil, wall loss |
| `nozzle`, `exitMach` | rocket thrust, with the correction for ambient pressure |
| `PistonEngine` | four-stroke, two-stroke and diesel engines on a crank |
| `turbojet`, `turbofan` | jet engines: thrust, fuel use, efficiencies |
| `Boiler` | a drum of water and steam with a safety valve and steam taken out |

## A gun

Give it the barrel, the projectile and the charge, and get the shot.

```ts
import { fireGun } from '@ferret/physics-combustion';

const charge = { density: 1600, a: 6e-7, n: 0.8, Q: 3e6, surface: () => 0.06, mass: 0.015 };
const shot = fireGun({ bore: 0.02, length: 0.6, projectile: 0.1, chamberVolume: 4e-5, charge,
                       efficiency: 0.9, startForce: 3000, friction: 500 });
shot.muzzleVelocity;   // m/s
```

The result has `muzzleVelocity`, `peakPressure`, `muzzlePressure`, the projectile's `kinetic` energy against the `chemical` energy of the charge (well under 40 %: a gun is not efficient), the momentum of the projectile and of the gas, the `recoilImpulse`, the `wallLoss` and a `trace` of time, position, velocity and pressure. A longer barrel gives a higher velocity; a `burstPressure` under the peak makes the barrel burst. `covolume` adds the room molecules take at high density, which raises the peak pressure; `wallHeat` lets the bore take heat.

In a world, `cap.fire(name, spec, { body, direction })` applies the recoil to the body, opposite to `direction`, and spawns a projectile with the muzzle velocity (`spawn: false` skips that). Event `gun:fired { name, shot }`; command `gun.fire`.

## A rocket

```ts
import { nozzle } from '@ferret/physics-combustion';

const gas = { molarMass: 0.024, gamma: 1.2 };
const geometry = { throatArea: 1e-3, exitArea: 8e-3 };
const vacuum = nozzle(gas, 7e6, 3200, geometry, 0);
const seaLevel = nozzle(gas, 7e6, 3200, geometry, 101325);
```

Thrust is the mass flow times the exit speed, plus the pressure difference at the exit times the exit area. So thrust rises with altitude by exactly what the ambient pressure took away. The speed a burn gives follows the rocket equation, `c·ln(m₀/m₁)`.

## A piston engine

```ts
const engine = new PistonEngine({ bore: 0.08, crank: 0.04, rod: 0.14, compressionRatio: 9,
                                  fuel: PETROL, airFuelRatio: 14.7 });
drivetrain.drive(crank, (w, th) => engine.torque(w, th));     // see Machines
```

Everything else comes from the cycle: idle, the torque curve, the stall. An engine that breathes no oxygen dies (`air: () => ({ pressure, temperature, oxygen: 0 })`). Plug `air` into media (skill `ferret`) (`standardAtmosphere.at(h)`) and the engine loses power with altitude by itself.

| Option | Meaning |
|---|---|
| `cycle` | `'four'` or `'two'` |
| `ignition` | `'spark'` or `'compression'` |
| `advance`, `burnDuration` | when the burn starts and how long it lasts |
| `throttle` | 0 to 1; with a diesel it is the quantity of fuel injected |

A diesel draws air alone, injects fuel at the advance, and lights only if the compressed air is hot enough (about 750 K); `misfires` counts cycles that did not light. Commands: `engine.start`, `engine.stop`, `engine.throttle { value }`.

## A jet

```ts
const real = turbojet({ pressureRatio: 12, T4: 1500, etaCompressor: 0.85, etaTurbine: 0.9, etaNozzle: 0.97 }, 0);
real.specificThrust;      // per kg/s of air
real.tsfc;                // fuel per unit thrust
```

The second argument is the flight speed. Losses cost thrust and fuel; propulsive efficiency rises with speed. Values are per kilogram per second of air; multiply by the mass flow of your engine. `turbofan` adds a bypass ratio.

## A boiler

```ts
import { Boiler } from '@ferret/physics-combustion';

const boiler = new Boiler({ volume: 0.2, water: 100, T: 450, safety: { pressure: 1e6, area: 2e-4 } });
boiler.step(0.5, 80e3);       // half a second at 80 kW
```

The drum sits on the saturation line, so heating it raises pressure and temperature together. The safety valve holds the pressure and vents steam. `take(kg)` draws steam out with its enthalpy (to feed a turbine, `steamWork(p1, p2, efficiency)` gives the work per kilogram of an expansion); `feed(kg, T)` adds water, and cold feed cools the drum; `flowThrough(area, outside)` is the choked steam flow. The steam tables behind it match the real ones to a fraction of a percent up to a few percent.

## In a world

`cap.addChamber(name, chamber, { vent?, entity?, casing? })` puts a chamber under the world step: the premix burns, the propellant burns, heat goes into the wall, gas leaves through the vent.

Past its burst pressure the chamber turns into the blast of explosions (skill `ferret-physics-combustion-explosions`), when that capability is on and the chamber has an `entity`. The blast carries the energy of the gas at the burst, and `casing` is the mass that becomes fragments.

Events: `chamber:burst { name, pressure, energy, volume, mass, T }`, `combustion:out { name }`. Commands: `chamber.state`, `chamber.ignite`, `chamber.vent { name, area?, Cd? }`, `combustion.info`.

## Parameters

| Value | Default | Effect |
|---|---|---|
| `ambient` | 101325 Pa | pressure outside the chambers; set another on a zone for a vacuum or a deep level |

## Works with

explosions (skill `ferret-physics-combustion-explosions`): a burst chamber is an air blast with fragments. heat (skill `ferret-physics-heat`) and links (skill `ferret-physics-links`): `gasInPart` connects a heat part to a chamber's gas, so a hot wall raises a closed tank's pressure. [mechanisms](mechanisms.md): an engine drives a drive train, and a turbine takes `steamWork`. media (skill `ferret`): the atmosphere supplies the air an engine breathes and the ambient pressure of a nozzle.

## Cost

A few operations per chamber per step. A gun is computed whole, at the call (a few milliseconds of simulated time), so do not call it every frame for show.

## When it does not behave

| Symptom | Cause |
|---|---|
| `ignite()` returns false | outside the flammability limits (check the equivalence ratio), no oxygen, or no fuel |
| A lit chamber goes out | the oxygen was used up; leftover fuel is correct |
| The gun bursts | `burstPressure` is below the peak (read `peakPressure`), or the charge is too big for the chamber |
| The muzzle velocity is too high | `efficiency` 1 means no wall loss; use 0.8 to 0.9, or set `wallHeat` |
| A chamber never bursts in a world | no `entity`, or the explosion capability is off (the event still comes) |
| An engine stalls on start | give the shaft a starter torque for the first fraction of a second |

Worked numbers (pressure rise of a closed burn, gun momentum, rocket thrust at sea level, jet losses, boiler mass and energy balance) are in `tests/unit/examples/physics-combustion.test.ts`.

## Reference

`combustion`, `CombustionCapability`, `Chamber`, `Propellant`, `Orifice`, `fireGun`, `PistonEngine`, `turbojet`, `Boiler`.
