boiler.cir · 51 lines
* Espresso machine boiler -- lumped thermal model as an electrical analogue
* ---------------------------------------------------------------------------
* Analogy between the thermal and electrical domains:
* voltage [V] = temperature [degC]
* current [A] = heat flow [W]
* capacitance [F] = thermal mass [J/K]
* resistance [ohm] = thermal resistance [K/W]
* Node b is the boiler temperature; node amb is the room-temperature reference.
* ---------------------------------------------------------------------------
.param Tamb = 20 $ ambient / room temperature (degC)
.param Cth = 5000 $ boiler thermal mass (J/K)
.param Rth = 0.7 $ boiler-to-ambient thermal resistance (K/W)
.param Pheater = 1300 $ heater power when energised (W)
.param Rsense = 1 $ sense resistor that turns the heater into a current source
.param Tlow = 92 $ thermostat lower set point (degC)
.param Thigh = 96 $ thermostat upper set point (degC)
* --- Thermal mass: capacitor from boiler node to ground ---------------------
Cth b 0 {Cth} IC={Tamb}
* --- Ambient reference and heat loss: resistor to the ambient source --------
Vamb amb 0 dc {Tamb}
Rloss b amb {Rth}
* --- Heater: forces an exact current Pheater into the boiler node b ---------
* The E source lifts the top of the sense resistor to V(b)+P*Rsense, so while
* the thermostat switch is closed the current through it is exactly Pheater
* (1 A of current = 1 W of heat in this analogy).
Eheat hsup 0 VALUE = { V(b) + Pheater*Rsense }
Rs hsup hmid {Rsense}
* --- Thermostat: voltage-controlled switch with hysteresis ------------------
* Control voltage = 94 - T. The switch closes when T < 92 and opens when
* T > 96, which is the 4 degC dead band of a real espresso thermostat.
Ssw hmid b ctrl 0 swmod
Ectrl ctrl 0 VALUE = { (Tlow+Thigh)/2 - V(b) }
.model swmod sw(vt=0 vh={(Thigh-Tlow)/2} ron=1m roff=1G)
* --- Simulation: first 20 minutes from cold, then save for plotting ---------
.control
tran 0.2 1200 uic
* boiler temperature
wrdata boiler_temp.txt v(b)
* flat traces that mark the 92 degC and 96 degC thermostat limits
let lowset = 92 + 0*time
let highset = 96 + 0*time
wrdata thermostat_band_92_96.txt lowset highset
.endc
.endround 1 of 2

5000 J/K, a big boiler. 4.8 minutes to temperature.
modeldeepseek/deepseek-v4.1-flashtaskspice-boiler-thermostat@v1date2026-09-16rounds2 of 8 allowedcost$0.0281thinkingmediumtokens588,118 in, 27,747 out, of which 21,897 reasoningtime3 min 27 s
Complete analoguedecided by a number, from the filepass
Real hysteresisdecided by a number, from the filepass
Starts colddecided by a number, from the filepass
Holds 92–96 °Cdecided by a number, from the filepass
Writes the .txtdecided by a number, from the filepass
same prompt, other modelsall 2 runs on Espresso boiler thermostat →
Model an espresso machine's boiler in ngspice as an electrical analogue — heater, thermal mass, heat loss — with a thermostat that holds it between 92 and 96 °C, and simulate the first 20 minutes from cold. In a .control block, save the boiler temperature with wrdata to a .txt file so it can be plotted.Frozen at v1. This model got this and nothing else. The prompt has since moved to v2, on 2026-09-20, so this run is not directly comparable with one made after that date. Both wordings are on the task page.
The netlist itself: re-run in ngspice offline, and the temperature it writes out read back. A thermostat with a dead band is a stateful thing, so a bare comparator on one threshold is wrong however plausible the plot looks. How it is scored, and this run's files on GitHub.