Pneumatics: a network of gas volumes
@ferret/physics-pneumatics (PLAN-DYNAMICS.md stage A12). PneumaticCircuit joins closed volumes of gas with pipes, valves, regulators, compressors and cylinders.
The gas#
A node (tank(id, { volume, pressure?, temperature?, mix?, wall? })) holds moles per species and an internal energy U. Air is { N2: .79, O2: .21 }; Any gas of the species library of chemistry can be named by formula (CO2, Ar, He, H2, CH4, NH3, SO2, steam for H2O, ...): the molar mass comes from the formula and the heat capacity from cp(T) of the species, so gamma follows the temperature of the node (a gas heated by a fast compression has a different gamma) and the energy U, the enthalpy that flows carry and the compressor work are all on cp(T). SPECIES is the old table as a view of the library; gasOf(formula) gives cp, cv, u, h, gamma, s; useGasLibrary(lib) swaps the library. An unknown formula is refused with the list. The laws are tested by name:
| law | what holds |
|---|---|
| Boyle–Mariotte | p·V stays within 1.5 % when compressed slowly against a wall (wall.hA large) |
| Gay-Lussac / Charles | heating at constant pressure keeps V/T, at constant volume p/T |
| Poisson | compressed fast and insulated, p·V^γ stays within 0.5 % |
| Avogadro | equal moles, volume and temperature give equal pressure, whatever the gas |
| Dalton | p is the sum of partial(species); a node's fraction(sp) is the mole share |
The wall (hA J/(s·K), T) exchanges heat exactly, so a slowly filled tank stays near the room temperature and a fast one warms (tested: +30 K fast, within 5 K slow).
Flow#
gasMassFlow(p0, T0, p1, area, { gamma, R, Cd }) in @ferret/physics-media is the one orifice law (subsonic isentropic, choked below the critical ratio); combustion chambers use the same function. A donor loses moles and cools isentropically; the receiver gets the donor's enthalpy, so the energy closes. Sub-steps are limited to a share of the donor and half way to equal pressure, so a stiff small volume does not overshoot.
| link | call | |
|---|---|---|
orifice, valve (open 0..1), pipe (pipe: { length, diameter, f? } turns friction into area) | orifice, `link(id, { from, to, cdA | pipe }), setOpen` |
| check valve | check(id, from, to, cdA) | |
regulator (holds the downstream set, 5 % band) | regulator(id, from, to, cdA, set) | |
| relief valve | relief(id, from, to, cdA, crack) | |
compressor (rate m³/s at inlet, maxPressure, efficiency; heats adiabatically, link.work counts its work) | compressor(id, from, to, { … }), setOn |
air is the atmosphere and never changes.
Cylinders#
cylinder(id, { bore, rod, stroke, mass, dead?, x?, pressure?, vented?, coulomb?, viscous?, load? }) has chambers id.cap and id.rod (or vented: true: the rod side is open to the air, an air spring or a gun barrel). The piston is a mass; the gas does p dV on it adiabatically (or isothermally if the wall conducts): an air spring is softer isothermal than adiabatic and stiffer at small volume (tested); an air gun's best kinetic energy matches the adiabatic work less the work against the atmosphere within 5 %. With attach: { a, b, axis } in a level it pushes two bodies apart like the hydraulic one.
Henry#
liquid(nodeId, { liquidVolume, henry: { CO2: 3.3e-4 }, rate, equilibrium? }) puts a liquid under the gas head: it dissolves k·partial pressure·V_liquid at rate both ways. Without henry, liquid(nodeId, { liquidVolume, gases: ['CO2'] }) reads the constants of the aqueous set (henryConstant(gas, T), with their temperature dependence, for CO2, N2, O2, Ar, He, Ne, H2, CH4, CO, NH3, SO2, H2S, Cl2, NO); EXAMPLE_LIQUIDS.water.henry is the same table at 298 K. With vessel: <a Vessel of physics-chemistry> the dissolved gas is that vessel's CO2(aq): one amount for both solvers, the vessel reacts (carbonic acid, bicarbonate) and its pH falls as the gas goes in; inventory counts the carbon of the carbonates too. A bottle opened for a moment loses CO2, and closed again its head rises as the gas comes out (tested); circuit.inventory(sp) counts gas plus dissolved and does not lose moles.
In a level and in the language#
physics.pneumatics: a list of circuits { name, tanks, links, cylinders }; errors name the node or gas. Commands: pneumatics.state, .open, .compressor, .regulator, edit.physics.pneumatics.set/remove. Forms: (gas-pressure "c" "node"), (gas-temperature …), (gas-valve "c" "link" x), (compressor "c" "link" #t), (regulator "c" "link" pa). (pressure stays the boiler word of the engines.)