# Boiler Plant v0.1 — sequence and model contract

Implemented from the user-approved primary / low-loss-header / secondary design
on 2026-09-22. Educational model; no physical validation or manufacturer parity.
`boiler-simulator.html` works with classic scripts directly from a file URL.

## Module boundaries

`js/boiler/config.js` centralizes all settings, bounds, units and constants.
`pi.js` is conventional PI; `controller.js` owns sequencing, timers, commands and
alarms. `model.js` owns hydraulics, LLH mixing, heat response and energy storage.
`engine.js` owns fixed simulated time, atomic live edits, history and events.
`charts.js` and `ui.js` render; the existing shared trend cursor inspects samples.
Existing simulator engineering modules are unchanged.

Canonical units: °C, L/s, kPa, kW, kJ/K, seconds and percent. UI °C/°F, L/s/US gpm
and kPa/psi switches preserve canonical settings. Integral gains explicitly remain
percentage points per °C (or kPa) per minute. Ki=0 provides biased P control.

## Topology and model

Two identical boiler branches run in parallel on the primary side. Each operating
dedicated pump supplies its configured constant branch flow unless flow is faulted.
Ideal check valves isolate stopped branches. A four-port LLH separates hydraulic
pressure effects, while allowing water mixing. The secondary VFD pump circulates
through a load valve, aggregate heat load and HWS/HWR sensors. HWS after the LLH
is the temperature-loop feedback; local boiler outlet readings provide high limits.

Secondary pump head is Pr[2n² − (q/Qr)²]. The load resistance is Pr(q/Qr)²/a²,
where n=speed fraction and a=valve opening fraction. The intersection gives flow
and DP across the load circuit. A closed valve has zero flow and shutoff pressure;
the injected secondary flow failure instead sets both to zero. Speed ramps with
the configured full-travel time. Primary speed is not controlled by secondary DP.

For primary/secondary flows Gp/Gs, flow-weighted boiler outlet Tp and return Tr:

- Gp≥Gs>0: HWS=Tp, primary return=[Gs Tr+(Gp−Gs)Tp]/Gp.
- Gs>Gp>0: HWS=[Gp Tp+(Gs−Gp)Tr]/Gs, primary return=Tr.
- Gp=0: secondary recirculation at Tr, no primary heat transfer.
- Gs=0: primary recirculation at Tp, no load delivery. Both-zero means no transfer.

Positive bypass flows down; negative flows up. At zero primary flow its displayed
common supply is the last flowing value, not a valid flow-weighted measurement.
Unflowed secondary HWS is unavailable to the controller and plotted as a gap.

Each boiler has lumped heat storage Cb and the secondary return loop has Cs:
Cb dTb/dt = boiler heat + 4.18 Gbranch(TprimaryReturn − Tb).
Cs dTr/dt = 4.18 Gs(HWS − Tr) − delivered heat.
Heat units are kW with 4.18 kJ/(L·K) as the constant water volumetric heat capacity.
Delivered heat=min(requested kW, max(0, 0.8 × 4.18 Gs(Tr − sink temperature))).
The load is flow/temperature limited; unmet demand remains visible. It is not a
full room/coil model. Load kW and valve opening are independent disturbances.

Actual boiler heat approaches commanded heat exponentially using heatLag. Start
transients may be below minimum steady firing rate. Turning firing off removes
burner heat immediately; stored water/metal heat remains. Physical integration
uses bounded substeps determined by storage/flow stability. Each step checks total
stored-energy change against heat in minus load heat; physical state is not clamped
to fix invalid results. No pipe losses, pump heat, fuel/efficiency or LLH buffer
storage are added. Storage settings lock after Start to preserve the energy basis.

## Control and sequence

HWS SP is manual or a linear two-point outdoor reset bounded by its endpoints.
Warm-weather shutdown has a separate restart differential and applies to manual
boiler commands too. A heating request comes from positive load or positive manual
boiler modulation, gated by plant On, weather and the HWS sensor alarm.

The secondary pump circulates for demand or post-run. Auto DP PI owns speed;
Manual owns its explicitly entered speed. With no valid circulating HWS or an
injected secondary flow failure, firing is inhibited and HWS PI integration holds.
When plant demand ends or HWS sensor failure occurs, the secondary pump post-runs.

Both PI loops use P=100 error/PB and I increment=error × Ki × dt/60, with conditional
anti-windup. Bias seeds I; editing Bias replaces I. Disabled loops retain I without
integration. HWS output 0–100% maps to 0–2×rated boiler heat. Actual eligible Auto
boilers share remaining heat after manual commanded heat is accounted for. Output
limits can leave unmet demand. An incoming proven boiler joins allocation in the
same control evaluation so staging does not double requested heat. Minimum firing
constraints can still deliver more than a low plant request.

Lead is explicitly selected or initially the eligible boiler with least accumulated
runtime. Faulted/unavailable Auto units are excluded; an available replacement
still respects minimum off. Add lag after lead output and HWS deficit continuously
meet thresholds for onDelay. Remove it after total automatic heat request is below
the configured fraction of one boiler capacity and HWS deficit is small enough
for offDelay, with minimum firing time satisfied if the lag is still firing.
An already stopped lag can be released without restarting it to satisfy a run timer.
Broken qualifications reset timers.

Timed/requested lead handover retains the old lead while proving the incoming
boiler, reallocates demand when it fires, then swaps roles. When both were already
needed, both remain staged. Manual boilers are outside automatic staging/rotation.
Pending explicit lead requests wait for eligibility/timers rather than bypassing them.

Pump enable precedes branch flow proof. After proofDelay with sufficient flow,
startDelay represents startup/run proof. Missing flow or injected missing run proof
times out; missing run proof withholds heat in this simplified startup model.
Normal burner starts respect minimum off even in Manual. Each enabled Auto burner
fires at least minFire. If requested modulation is at/below minFire and HWS is above
SP + half cycleBand, it stops once minimum run is satisfied; restart requires HWS
below SP − half band and minimum off readiness. Above minimum modulation the PI
can reduce output continuously; the cycling differential does not interrupt normal
modulation or prematurely prevent sustained lag-stop qualification.
Auto PI cannot command a fictitious steady firing rate below minFire.

Trip, flow loss while firing, proof timeout and local outlet high limit latch the
affected boiler. Remove the injected fault, Apply, then Reset alarms. High-limit
reset requires outlet below highLimit−highReset. HWS signal failure latches a plant
heat inhibit, freezes HWS integration, preserves true water temperatures and triggers
pump post-run. Restore Normal, Apply, then Reset alarms to recover. Alarm reset
does not bypass active faults. Off/fault/no-flow inhibits override manual modulation
and normal minimum-run holding. Stored heat remains in the physical model.

## Playback, edits and diagnostics

Start/Pause/Resume/Reset use fixed 1 s steps. Thermal substeps do not change control
time. Paused changes and reset requests commit at the next simulated step. Initial
temperatures/storage/runtimes/duration lock after Start. Reset restores run-start
settings, clears history/alarms/scenario clock and initializes all state explicitly.
Hidden-page or long browser interruptions pause playback; no hidden elapsed time
is silently simulated. History samples every 5 s plus edit boundaries, event log
retains the latest 100 events. A 24-hour run remains within the declared duration.

Scenario mode supplies load0 until step1, load1 until step2, then load2. Times are
relative to simulation start, not mode selection. Switching to Manual demand is an
explicit override recorded in the event log. Scenario changes do not move the valve.
The UI displays command vs actual heat, flow proof/start phases, minimum-off and
post-run countdowns, lead/lag qualifying timers, starts, runtimes and alarm reasons.

Unified colored trends share time with separate quantity axes, nullable sensor
readings, navigation, and actual-recorded-sample cursor inspection. Viewing and
unit changes do not alter controller or physical state.

## Verification and boundaries

Run `node tests/boiler-tests.cjs` for the 54 passing LLH/energy, control, faults,
lifecycle and actual-page DOM tests, including two 24-hour runs.
Run `node tests/run-node.cjs` for site and FCU regressions.
Actual browser rendering/native controls/console inspection remain pending under
the existing browser file-URL policy restriction. DOM tests are not browser tests.

Source context and original scope: [design proposal](boiler-plant-proposal.md).
This simulator does not model ignition/flame safeguards, DHW, mechanical expansion,
pressure safety, frost protection or manufacturer-specific burner control.
