🔥 Thermal Integration#
EMHASS can plan heat together with electricity. A domestic-hot-water (DHW) tank, a space-heating buffer, a pool or the thermal mass of the house is modelled as a temperature that the optimizer steers through the horizon, inside a comfort band you set. It then decides when to make heat: on cheap power or surplus PV, and not during a price peak, the same way it plans a battery’s state of charge.
Do I need this?#
Only if you want EMHASS to schedule something that makes heat or cold: a heat
pump, an electric water heater, a gas boiler, an air conditioner. If you only
have PV, a battery and ordinary deferrable loads, skip this section. Nothing in
it is active by default: heat_topology is null and no deferrable load has a
thermal configuration, so the optimization is exactly the electrical one.
Which model to use#
Your system |
Use |
Start with |
|---|---|---|
One device heating or cooling one room, no tank |
Deferrable load thermal model ( |
the example on that page |
One heat pump charging one tank or a floor slab, with a temperature-dependent COP |
Thermal battery ( |
|
Several sources or several stores: a heat pump plus a gas boiler or electric booster, a DHW tank plus a buffer, a buffer feeding the house, sources on different tariffs |
Heat topology graph model ( |
For a short, step-by-step start, the cookbook has two recipes on the heat topology: a DHW tank with a heat pump and an electric booster and pre-heating the house with a heat pump.
The first two are single-load models configured under def_load_config. The
heat topology describes the system as a small graph of sources, stores and
flows, which EMHASS compiles into deferrable loads and shared thermal tanks. It
can express most single-load setups as one-source stores; the exceptions are
listed below.
Moving from thermal_battery to heat_topology#
Systems tend to grow: a second tank, a booster, a boiler. The fields map directly:
|
In |
|---|---|
|
a |
|
an |
|
a storage entry. The defaults differ: |
|
the same storage entry |
|
the storage’s |
|
a |
|
a |
|
a |
|
the |
|
no direct equivalent: a storage’s |
the load’s |
the source’s |
the load’s |
the source’s |
the load’s |
the source’s |
the load’s |
no equivalent: a topology load may run over the whole horizon |
Then remove the thermal_battery entry from def_load_config, lower
number_of_deferrable_loads by one, and remove that load’s entry from every
per-load array (nominal_power_of_deferrable_loads,
operating_hours_of_each_deferrable_load, treat_deferrable_load_as_semi_cont
and the others), so the remaining loads keep matching entries. The compiler
creates one deferrable load per source-to-storage flow, numbered in the order of
flows, so the load index (and sensor.p_deferrable{k}) of the heat pump may
change: update custom_deferrable_forecast_id and
custom_predicted_temperature_id accordingly. If you also have ordinary
deferrable loads, set extend_deferrable_loads (see
Combining with other deferrable loads);
without it the topology replaces them, and the log warns which loads are not
optimized.
Temperature-dependent COP#
A heat pump’s COP falls as it heats the store hotter. The optimizer plans against
a COP at an assumed temperature; with cop_solver: auto it checks the plan
against the true temperature-dependent COP afterwards and refines it only when
the two disagree (default static: no refinement). This applies to heat pumps
with a heating_curve (or cooling_curve) on a heat_topology storage; a
thermal_battery and a fixed-supply heat pump keep their COP. cop_solver is a
setting in the configuration, not a topology field. See
heat_topology for when to turn it on and
the mathematical model for how it works.