FastModel: models close to Buildings, in closed loop and for MPC
The RC models of the mpc library are small and fast, but they lump a zone
into one to three capacities and leave the heating system out. The FastModel library instead turns the same YAML
file into a model close to the Buildings library that still translates into a CasADi ODE (with
rumoca) for simulation, state estimation and model predictive control
with IPOPT:
- every zone has the physics of
Buildings.ThermalZones.Detailed.MixedAir: layered walls, floors and roofs, windows with the angular optics of their glazing, interior and exterior convection, long-wave exchange with the sky and between the room surfaces, the transmitted solar radiation distributed on the room surfaces, infiltration, walls and floors shared between zones; - the hydronic circuits of the YAML are modelled: radiators (EN 442), valves, mixing three-way valves, split valves, pumps, sensors, gas boilers and air-to-water heat pumps with their buffer;
- balanced ventilation units with heat recovery (trano's
system_d) and PV arrays.
The library has two configurations, built from the same components and templates:
FastModelsimulates the building in closed loop, as the Buildings and IDEAS models do: the controllers of the YAML are part of the model (written without events) and the water flows follow the pressures of the circuits;FastModelMPCis the model for optimal control: the controllers are left out, the controls of the systems are inputs that the optimisation decides, and each valve sets the flow of its branch.
trano create-model house.yaml FastModel # closed loop: writes house.mo
trano simulate-model house.yaml FastModel # runnable, like the other libraries
trano create-model house.yaml FastModelMPC # the controls as inputs, for optimal control
A library like Buildings and IDEAS
FastModel is used exactly as trano uses Buildings and IDEAS: a Modelica library and one template per element of
the YAML, without code of its own.
trano/library/FastModel.moholds the components (the physics), embedded in every generated package;trano/elements/library/models/fast_model/*.yamlhold the templates that declare a component for each element of the YAML, with its ports, as the templates of the other libraries;- the
FastModelentry oflibrary.jsonwrites the constructions and glazings as records of aDatapackage, takes the parameter names of Buildings, and the package templatetemplates/fast_model.jinja2; theFastModelMPCentry extends it under another name, and the templates render each element for the library they are given (library == 'FastModel': controllers and pressure-driven circuits;'FastModelMPC': inputs).
The walls, windows and floors are elements of their own connected to the bus of their zone, as in IDEAS: each
OuterWall, Window, BoundaryWall (floor on ground or over outdoor air), AdiabaticWall or InternalWall
connects its propsBus_a to a propsBus[i] of its Zone. The bus carries the convective and radiative
exchanges with the room, the properties of the surface that the zone needs (area, emissivity, solar absorptance
and transmittance), the solar radiation a window transmits, and the weather.
| Package | Components |
|---|---|
BoundaryConditions |
Weather (solar position and Perez sky of the site), TiltedSurface (irradiance on a surface) |
Envelope |
OuterWall, Window, BoundaryWall, AdiabaticWall, InternalWall |
Zones |
Zone, VentilatedZone |
Hydronics |
Radiator (EN 442), Valve, ThreeWayValve, Junction, Pump, TemperatureSensor, Boiler, HeatPump |
Ventilation |
HeatRecoveryUnit, Duct |
Electrical |
Photovoltaic |
HVAC |
IdealHeatingCooling (heat as an input), IdealHeater, IdealHeatingCoolingPI (closed loop) |
Controls |
EmissionP, EmissionPI, EmissionOnOff, EmissionMonitored, CollectorControl, ThreeWayValveControl, BoilerDemand, BoilerTemperatureControl, HeatPumpControl (closed loop, without events) |
Data |
Construction, Glazing (records written by trano) |
The generated package holds two models:
building_mpc, the building: an explicit ODE whose inputs are the weather data, the internal gains and, withFastModelMPC, the controls of the systems. It contains no table, no event and no component of another library;building, the runnable model:building_mpcfed with the weather file (the Buildings reader, which also givesbuilding_mpcthe location of the site), the occupancy schedules, and the external data of the network: the controls replayed from it withFastModelMPC(each held at 1 without data), the set points of theMonitoredSetpointemission controls withFastModel.
Rules for an explicit ODE
Every component keeps a few rules, so that rumoca translates the assembled model into xdot = f(t, x, u, p):
- every node joined by connections has a heat capacity, or is solved in closed form inside its component (the radiant star of a room, the mixing of water flows);
- the equations are smooth: power laws and table interpolations are regularised, conditions are inside functions; the controllers have no event (a hysteresis is a fast bistable state, a PI controller has anti-windup);
- with
FastModelMPC, flows are set: each valve sets the flow of its branch, a mixing valve the share of supply water, mass balances the rest; the production (boiler, heat pump) or the ventilation unit closes the balance of its circuit, one per circuit. WithFastModel, they are pressure-driven: each component has a pressure node of small compliance at its inlet, every flow follows from the pressures, the pumps run on their curves; - a port with several connections is an array, one element per connection, sized by the template from the connections trano made: the inlets of a component mix, its outlets leave at its outlet temperature;
- parameters computed by functions (the conductance of a radiator solved as Buildings does at initialisation, the polynomial fits of the window optics, the scaling of the heat pump tables) take values settled at translation.
Inputs of building_mpc
The closed-loop FastModel takes the weather, the internal gains and, for a MonitoredSetpoint emission control,
its set point <control>_TSet; FastModelMPC takes the controls too:
| Input | Role |
|---|---|
weather_inputs.TDryBul, .TBlaSky, .winSpe, .winDir, .HDirNor, .HDifHor, .HGloHor |
weather data, as in a weather file or a forecast (disturbances) |
<occupancy>.gai.QCon, .QRad |
convective and radiant internal gains per floor area [W/m2] (disturbances) |
<valve>_y.y |
flow of a radiator branch relative to its nominal flow (control, 0 to 1) |
<three-way valve>_y.y |
share of supply water (control) |
<boiler>_y.y |
boiler part load ratio, or heat pump compressor speed (control) |
<ventilation unit>_y.y |
air flow relative to the design flow (control held at 1 by its bounds, as Buildings runs it) |
<emitter>_Q.Q_flow |
heat of an ideal emitter [W] (control, between its bounds) |
The solar position and the irradiance on each surface are computed inside the model from the time (the clock time
of the year) and the site, so that a forecast only needs the weather channels. The meters of the systems are
states in kWh: EFue (boiler fuel), EEle (heat pump electricity), EFan (fans), EPro (PV production).
Simulation, estimation and optimal control: trano.optimization
The CasADi side lives in trano.optimization, a package that depends on rumoca, CasADi, NumPy and pydantic only
(no other module of trano), so that it can be moved to a package of its own. It reads everything from the Modelica
model: the names of the states, inputs and parameters in the order of the CasADi vectors, their units, bounds and
descriptions. The roles of the variables follow simple conventions (Roles.infer): an input with a min and a
max is a control, any other input a disturbance; the states named TAir are the comfort temperatures; the
states in kWh are meters.
from pathlib import Path
import numpy as np
from trano.data_models.conversion import convert_network
from trano.elements.library.library import Library
from trano.optimization import KELVIN, CasadiModel, OptimalControl, OptimalControlSettings
network = convert_network("house", Path("house.yaml"), library=Library.from_configuration("FastModelMPC"))
model = CasadiModel.from_modelica(network.model(), "house.building_mpc")
ocp = OptimalControl(model, settings=OptimalControlSettings(time_step=3600))
hours = np.arange(24)
comfort = np.where((hours >= 7) & (hours < 22), KELVIN + 20, KELVIN + 16)
disturbances = ... # (len(ocp.roles.disturbances), 24): forecasts of the weather and gains
result = ocp.solve(model.description.initial_state, disturbances, comfort, price=np.full(24, 0.10),
start_time=t0) # the model time of the first step: the solar position depends on it
result.controls # branch flows, valve positions, boiler part load ratio, hour by hour
OptimalControl collocates the ODE on Radau points (stable for the stiff water nodes whatever the step), solves
with IPOPT (SPRAL when CasADi ships it, MUMPS otherwise), warm-starts from a simulation of the model (CVODES), and
costs the energy read from the meters: the PV production first covers the electricity of the step, the rest is sold
at feed_in_price; the boiler fuel has its own fuel_price. The same discretization gives the moving horizon
estimation and the closed loop:
from trano.optimization import MovingHorizonEstimation, receding_horizon
estimator = MovingHorizonEstimation(model)
estimate = estimator.estimate(prior_state, applied_controls, past_disturbances, measured_air_temperatures)
loop = receding_horizon(ocp, estimate.current, disturbances, comfort, price, horizon=12, start_time=t0)
The parameters computed from others (a conductance from a thickness and a conductivity) are parameters of their own in the CasADi function, at their values: an identification adjusts the effective ones (conductances, capacities).
How close to Buildings
python -m validation.mpc simulates the Buildings model of a YAML file and compares it with FastModel in two
ways: it replays what the systems of Buildings did (the heat of ideal emitters, or the branch flows, valve
positions and boiler or heat pump controls) through the runnable FastModelMPC model (--models FastModel), or it
simulates the closed-loop FastModel with its own controllers (--models FastModelClosedLoop); then it compares
the zones, the water temperatures and the meters. See the validation page for the
results.
Limits
- One production (boiler or heat pump) per hydraulic circuit, and mixing three-way valves (the bypass from the return): diverting valves are not modelled yet.
- Ventilation units are balanced (supply = extract) and of the
system_dkind. - Boilers with a storage tank, water-to-water heat pumps, chillers, domestic hot water, batteries, EV chargers and VAV systems are not modelled: generating a model with them stops with a message naming them.
- With
FastModelMPC, flows are set by the valves; the pressure-driven sharing of flow between branches is modelled in the closed loop only, with the resistances linearised at their nominal flows. - The closed-loop controllers have no event: a hysteresis switches within 10 s, and the minimum on and off times of the heat pump are not modelled.