This package contains examples with step-by-step instructions for how to build a system model for a boiler with a heat load as shown in the figure below. The pressure drops of the individual flow branches and the temperatures correspond to design conditions. The heating system shall be designed to provide 20 kW, which is the load needed at -10°C outdoor temperature. This load already takes into account the heat required for air infiltration and ventilation. Using this load and the temperatures shown in the schematic drawing, the nominal mass flow rates of the individual flow branches should be computed. From 8:00 to 18:00, there is an internal heat gain of 4kW, which should not be accounted for when sizing the system.
The room volume is 180m3. To approximate the thermal storage effect of furniture and building constructions, the heat capacity of the room should be increased by a factor of three. (Modeling a detailed room heat transfer as implemented in Buildings.Rooms is out of scope for this tutorial.)
The space heating shall be switched on if the outdoor temperature is below 16°C and the room temperature is below 20°C. It shall be switched off if either the outdoor temperature is above 17°C or the room temperature is above 21°C.
The model consists of
To explain the implementation of this model, the model has been created in the following stages:
Name | Description |
---|---|
System1 | 1st part of the system model, consisting of the room with heat transfer |
System2 | 2nd part of the system model, consisting of the room with heat transfer and a radiator |
System3 | 3rd part of the system model, which adds the boiler loop with open loop control |
System4 | 4th part of the system model, which adds closed-loop control for the pumps and the boiler |
System5 | 5th part of the system model, which adds closed-loop control for the valves |
System6 | 6th part of the system model, which adds weather data and changes to PI control |
System7 | 7th part of the system model, which implements the on/off control using a state machine |
This part of the system model implements the room with a heat gain. The room is simplified as a volume of air, a prescribed heat source for the internal convective heat gain, and a heat conductor for steady-state heat conduction to the outside. To increase the heat capacity of the room, such as due to heat stored in furniture and in building constructions, the heat capacity of the room air was increased by a factor of three. The convective heat transfer coefficient is lumped into the heat conductor model.
This section describes step by step how we implemented the model.
First, we dragged
Modelica.Fluid.System into the model and keep its name at
its default setting, which is system
.
This model is required for all fluid flow models to set
global properties.
Next, to define the medium properties, we added the declaration
replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated;
This will allow the propagation of the medium model to all models that contain air. In this example, there is only one model with air.
We called the medium MediumA
to distinguish it from
MediumW
that we will use in later versions of the model for components that
have water as a medium. Because we do not anticipate saturated air, we used
the medium model
Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated
instead of
Buildings.Media.GasesPTDecoupled.MoistAir
as the latter is computationally more expensive.
Because in this model, we are not interested in air humidification or
dehumidification, we could have as well used the medium model
Buildings.Media.GasesPTDecoupled.SimpleAir.
We also defined the system-level parameters
parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";
to declare that the room volume is 180 m3, that the room has a nominal mass flow rate of 6 air changes per hour and that the internal heat gains of the room are 4000 Watts. These parameters have been declared at the top-level of the model as they will be used in several other models. Declaring them at the top-level allows to propagate them to other models, and to easily change them at one location should this be required when revising the model.
To model the room air, approximated as a completely mixed volume of air,
an instance of
Buildings.Fluid.MixingVolumes.MixingVolume
has been used, as this model can be used with dry air or moist air.
The medium model has been set to MediumA
, and the nominal mass
flow rate is set to mA_flow_nominal
.
The nominal mass flow rate is used for numerical reasons and should be set
to the approximate order of magnitude. It only has an effect if the mass flow
rate is near zero and what "near zero" means depends on the magnitude of
m_flow_nominal
, as it is used for the default value of the parameter
m_flow_small
on the Assumptions
tag of the model.
See also
Buildings.Fluid.UsersGuide
for an explanation of the purpose of m_flow_small
.
Since we need to increase the heat capacity of the room air to approximate
energy storage in furniture and building constructions, we connected the instance heaCap
of
Modelica.Thermal.HeatTransfer.Components.HeatCapacitor
to the heat port of the room air.
The model heaCap
models energy storage. We set its capacity to
C=2*V*1.2*1006 J/K. This will increase the total heat capacity
of the room air by a factor of three.
We used the instance theCon
of
Modelica.Thermal.HeatTransfer.Components.ThermalConductor
to model the thermal conductance to the ambient.
Since our room should have a heat loss of 20 kW at a temperature difference
of 30 Kelvin, we set the conductance to
G=20000 ⁄ 30 W/K.
We used the instance preHea
of
Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow
to model a prescribed heat gain,
such as due to internal heat source.
This model outputs the heat gain which is equal to the value of its
input signal, which is obtained from a time table.
To define a time-dependent heat gain, we instantiated the block
Modelica.Blocks.Sources.CombiTimeTable
and set its name to timTab
.
We set the table parameters to
Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain";
Note that we set that the output is a periodic signal by configuring
extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic.
The documentation of
Modelica.Blocks.Sources.CombiTimeTable
explains why we added two values for 8am and 6pm.
Next, we connected its output to the input of the instance preHea
.
This completes the initial version of the model. When simulating the model for 2 days, or 172800 seconds, the response shown below should be seen.
To verify the correctness of the model, we can compare the simulated results to the following analytical solutions:
When the internal heat gain is zero, the room temperature should be equal to the outside temperature.
At steady-state when the internal heat gain is 4000 Watts, the temperature difference to the outside should be Δ T = Q̇ ⁄ UA = 4000/(20000/30) = 6 Kelvin, which corresponds to a room temperature of -4°C.
Both analytical values agree with the simulation results shown in the above figure.
An alternative validation can be done by fixing the temperature of the volume to 20°C and plotting the heat flow rate that is needed to maintain this temperature. This can be implemented by connecting an instance of Modelica.Thermal.HeatTransfer.Sources.FixedTemperature as shown below.
When plotting the heat flow rate fixTemp.port.Q_flow
, one can see
that the required heat flow rate to keep the temperature at
20°C is
20 kW during night, and
16 kW during day when the heat gain is active.
For a more realistic model of a room, the model Buildings.Rooms.MixedAir could have been used. For transient heat conduction, models from the package Buildings.HeatTransfer.Conduction could have been used.
Extends from Modelica.Icons.Example (Icon for runnable examples).
Type | Name | Default | Description |
---|---|---|---|
replaceable package MediumA | Buildings.Media.GasesPTDecou... | ||
Volume | V | 6*10*3 | Room volume [m3] |
MassFlowRate | mA_flow_nominal | V*6/3600 | Nominal mass flow rate [kg/s] |
HeatFlowRate | QRooInt_flow | 4000 | Internal heat gains of the room [W] |
Type | Name | Description |
---|---|---|
replaceable package MediumA |
model System1 "1st part of the system model, consisting of the room with heat transfer" extends Modelica.Icons.Example; replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated;inner Modelica.Fluid.System system; Fluid.MixingVolumes.MixingVolume vol( redeclare package Medium = MediumA, m_flow_nominal=mA_flow_nominal, V=V); Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=20000/30) "Thermal conductance with the ambient"; parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TOut(T=263.15) "Outside temperature"; Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea "Prescribed heat flow"; Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(C=2*V*1.2*1006) "Heat capacity for furniture and walls"; Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain"; equationconnect(TOut.port, theCon.port_a); connect(theCon.port_b, vol.heatPort); connect(preHea.port, vol.heatPort); connect(heaCap.port, vol.heatPort); connect(timTab.y[1], preHea.Q_flow); end System1;
This part of the system model adds a radiator with a prescribed mass flow rate to the system that is implemented in Buildings.Examples.Tutorial.Boiler.System1.
This model was built as follows:
First, we copied the model
Buildings.Examples.Tutorial.Boiler.System1
and called it
Buildings.Examples.Tutorial.Boiler.System2
.
Since this model uses water as the medium, we declared the water medium model at the top-level of the model by adding the lines
replaceable package MediumW = Buildings.Media.ConstantPropertyLiquidWater "Medium model";
To model the pump, a temperature sensor which we will need later
for the control, and a flow sink, we made instances of the models
Buildings.Fluid.Movers.FlowMachine_m_flow
(instance pumRad
for the pump that serves the radiators),
Buildings.Fluid.Sensors.TemperatureTwoPort
(instance temSup
),
Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2
(instance rad
), and
Buildings.Fluid.Sources.FixedBoundary
(instance sou
and sin
for the sink and source
reservoirs, which will later be replace by the boiler loop).
In all of these instances, we set the medium model to MediumW
.
We also made an instance of the model
Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor
(instance temRoo
) to measure the room temperature.
We connected the model as shown in the figure below.
Note that there are two connections from the radiator to the room volume: One connection is for the convective heat flow rate, and the other is for the radiative heat flow rate. For simplicity, we assumed that the air and radiative temperature of the room are equal. Furthermore, we simplified the model by using only one radiator instead of multiple radiators, although this radiator will be quite large as it needs to provide a heat flow rate of 20 kW.
Next, we computed the design mass flow rate for the radiator. According to the schematic drawing, the radiator should have at the design conditions a supply water temperature of 50°C and a return water temperature of 40°C. Thus, we define the radiator mass flow rate as
parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal = 20000 "Nominal heat flow rate of radiator"; parameter Modelica.SIunits.Temperature TRadSup_nominal = 273.15+50 "Radiator nominal supply water temperature"; parameter Modelica.SIunits.Temperature TRadRet_nominal = 273.15+40 "Radiator nominal return water temperature"; parameter Modelica.SIunits.MassFlowRate mRad_flow_nominal = Q_flow_nominal/4200/(TRadSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate";
Now, we set the mass flow rate of pumRad
and temSup
to
mRad_flow_nominal
.
We also set the temperature of the fluid that flows out of sou
to TRadSup_nominal
.
We configured the parameters of the radiator model as
Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad( redeclare package Medium = MediumW, Q_flow_nominal=Q_flow_nominal, T_a_nominal=TRadSup_nominal, T_b_nominal=TRadRet_nominal) "Radiator";
We configured the parameters of the pump model as
Buildings.Fluid.Movers.FlowMachine_m_flow pumRad( m_flow_nominal=mRad_flow_nominal) "Pump for radiator";
To enable the pump when the room temperature is below 19°C and to switch it off when the room temperature is below 21°C, we implemented the control blocks as shown in the figure below.
In this control sequence, the first block is a hysteresis element, which is modeled by Modelica.Blocks.Logical.Hysteresis. It is configured as
Modelica.Blocks.Logical.Hysteresis hysPum( uLow=273.15 + 19, uHigh=273.15 + 21) "Pump hysteresis";
to output false
when the input signal falls below 19°C, and
true
when the input signal raises above 21°C.
Next, we send the output to the instance not1
, which outputs
y= not u
to negate the signal. The output of this signal is a boolean value, but the pump input signal is the required mass flow rate. Thus, we used the block Modelica.Blocks.Math.BooleanToReal to convert the signal. We set the parameters of the boolean to real converter as
Modelica.Blocks.Math.BooleanToReal booToReaRad( realTrue=mRad_flow_nominal, realFalse=0) "Radiator pump signal";
For numerical reasons, in particular in large system models, it is recommended to
continuously change the mass flow rate, as opposed to having a step change.
Therefore,
in the instance pumRad
, we leave the parameter
filteredSpeed
at its default value true
.
This will approximate a continuous change in mass flow rate when the
pump is switched on or off.
Finally, we closed the control loop between the room temperature sensor and the
pump input signal.
This completes the initial version of the model. When simulating the model for 2 days, or 172800 seconds, the response shown below should be seen.
The figure shows that the room temperature is maintained at 20°C when the internal heat gain is zero, and controlled around 19°C to 21°C when there is an internal heat gain. The temperature is slightly outside this temperature range because of the time lag that is caused by the thermal capacity of the radiator.
For a more realistic model of a room, the model Buildings.Rooms.MixedAir could have been used. For transient heat conduction, models from the package Buildings.HeatTransfer.Conduction could have been used.
Extends from Modelica.Icons.Example (Icon for runnable examples).
Type | Name | Default | Description |
---|---|---|---|
replaceable package MediumA | Buildings.Media.GasesPTDecou... | ||
replaceable package MediumW | Buildings.Media.ConstantProp... | Medium model | |
HeatFlowRate | Q_flow_nominal | 20000 | Nominal heat flow rate of radiator [W] |
Temperature | TRadSup_nominal | 273.15 + 50 | Radiator nominal supply water temperature [K] |
Temperature | TRadRet_nominal | 273.15 + 40 | Radiator nominal return water temperature [K] |
MassFlowRate | mRad_flow_nominal | Q_flow_nominal/4200/(TRadSup... | Radiator nominal mass flow rate [kg/s] |
Volume | V | 6*10*3 | Room volume [m3] |
MassFlowRate | mA_flow_nominal | V*6/3600 | Nominal mass flow rate [kg/s] |
HeatFlowRate | QRooInt_flow | 4000 | Internal heat gains of the room [W] |
Type | Name | Description |
---|---|---|
replaceable package MediumA | ||
replaceable package MediumW | Medium model |
model System2 "2nd part of the system model, consisting of the room with heat transfer and a radiator" extends Modelica.Icons.Example; replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated; //-------------------------Step 2: Water as medium-------------------------// replaceable package MediumW = Buildings.Media.ConstantPropertyLiquidWater "Medium model"; //-------------------------------------------------------------------------// //------------------------Step 4: Design conditions------------------------// parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal = 20000 "Nominal heat flow rate of radiator"; parameter Modelica.SIunits.Temperature TRadSup_nominal = 273.15+50 "Radiator nominal supply water temperature"; parameter Modelica.SIunits.Temperature TRadRet_nominal = 273.15+40 "Radiator nominal return water temperature"; parameter Modelica.SIunits.MassFlowRate mRad_flow_nominal= Q_flow_nominal/4200/(TRadSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate"; //------------------------------------------------------------------------//inner Modelica.Fluid.System system; Fluid.MixingVolumes.MixingVolume vol( redeclare package Medium = MediumA, m_flow_nominal=mA_flow_nominal, V=V); Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=20000/30) "Thermal conductance with the ambient"; parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TOut(T=263.15) "Outside temperature"; Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea "Prescribed heat flow"; Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(C=2*V*1.2*1006) "Heat capacity for furniture and walls"; Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain"; //-------------------------Step 5: Radiator Model-------------------------//Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad( redeclare package Medium = MediumW, Q_flow_nominal=Q_flow_nominal, T_a_nominal=TRadSup_nominal, T_b_nominal=TRadRet_nominal) "Radiator"; //------------------------------------------------------------------------//Fluid.Sources.FixedBoundary sin(nPorts=1, redeclare package Medium = MediumW) "Sink for mass flow rate"; Fluid.Sensors.TemperatureTwoPort temSup(redeclare package Medium = MediumW, m_flow_nominal=mRad_flow_nominal) "Supply water temperature"; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temRoo "Room temperature"; //------------------------Step 5: Pump for radiator-----------------------//Buildings.Fluid.Movers.FlowMachine_m_flow pumRad(m_flow_nominal=mRad_flow_nominal, redeclare package Medium = MediumW) "Pump for radiator"; //------------------------------------------------------------------------//Fluid.Sources.FixedBoundary sou( nPorts=1, redeclare package Medium = MediumW, T=TRadSup_nominal) "Sink for mass flow rate"; //--------------------------Step 6: Pump control--------------------------//Modelica.Blocks.Logical.Hysteresis hysPum(uLow=273.15 + 19, uHigh=273.15 + 21) "Pump hysteresis"; Modelica.Blocks.Math.BooleanToReal booToReaRad(realTrue=mRad_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Logical.Not not1 "Negate output of hysteresis"; //------------------------------------------------------------------------// equationconnect(TOut.port, theCon.port_a); connect(theCon.port_b, vol.heatPort); connect(preHea.port, vol.heatPort); connect(heaCap.port, vol.heatPort); connect(timTab.y[1], preHea.Q_flow); connect(temSup.port_b, rad.port_a); connect(rad.port_b, sin.ports[1]); connect(temRoo.port, vol.heatPort); connect(rad.heatPortCon, vol.heatPort); connect(rad.heatPortRad, vol.heatPort); connect(sou.ports[1], pumRad.port_a); connect(pumRad.port_b, temSup.port_a); connect(temRoo.T, hysPum.u); connect(hysPum.y, not1.u); connect(not1.y, booToReaRad.u); connect(booToReaRad.y, pumRad.m_flow_in); end System2;
This part of the system model adds the boiler loop with open loop control to the model that is implemented in Buildings.Examples.Tutorial.Boiler.System2.
This model was built as follows:
First, we copied the model
Buildings.Examples.Tutorial.Boiler.System2
and called it
Buildings.Examples.Tutorial.Boiler.System3
.
Next, we deleted the sink and source models sin
and sou
,
as these will be replaced by the boiler loop.
We made various instances of Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM to model flow splitter and mixers. We also made an instance of Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear to model the three-way valves, and an instance of Buildings.Fluid.Boilers.BoilerPolynomial to model the boiler.
Note that we also made an instance of
Buildings.Fluid.Sources.FixedBoundary,
which we called preSou
.
This is required to set a pressure reference in the water loop, which is a closed system.
For medium models that expand with increasing temperature,
this is also required to accumulate, and to feed back into the system,
the difference in mass due to the thermal expansion of water.
Next, we parameterized the medium of all these components by
setting it to MediumW
.
Since the nominal mass flow rate for the
boiler loop is required by several models, we introduced
the following system-level parameters, where
TBoiRet_min
will be used to avoid
condensation in the boiler:
parameter Modelica.SIunits.Temperature TBoiSup_nominal = 273.15+80 "Boiler nominal supply water temperature"; parameter Modelica.SIunits.Temperature TBoiRet_min = 273.15+50 "Boiler minimum return water temperature"; parameter Modelica.SIunits.MassFlowRate mBoi_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TBoiRet_min) "Boiler nominal mass flow rate";
Next, we set the parameters of the individual component models.
We lumped the pressure drop of the boiler loop into the boiler model and hence set
Buildings.Fluid.Boilers.BoilerPolynomial boi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, dp_nominal=2000, Q_flow_nominal=Q_flow_nominal, fue=Buildings.Fluid.Data.Fuels.HeatingOilLowerHeatingValue()) "Boiler";
For the three-way valve in the radiator loop, we used the default pressure drop of 6000 Pa. For its mass flow rate, we introduced the parameter
parameter Modelica.SIunits.MassFlowRate mRadVal_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate";
This allowed us to configure the valve as
Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valRad( redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve for radiator loop"
where l={0.01,0.01}
is a valve leakage of 1%
and dpValve_nominal=6000
is the pressure drop of the valve
if it is fully open
and if the mass flow rate is equal to m_flow_nominal
.
It is recommended to set the valve leakage to a non-zero value to avoid numerical problems.
A non-zero value also represent a more realistic situation, as most valves have some leakage.
For the bypass between the valve and the radiator, we note that the
mass flow rate is equal to mRad_flow_nomial - mRadVal_flow_nominal
.
This is a fixed bypass that is used to allow the valve to work across
its full range.
We decided to lump the pressure drop of this bypass, and the pressure drop of the
radiator loop, into the instance mix
.
Hence, its parameters are
Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix( redeclare package Medium =MediumW, m_flow_nominal={mRadVal_flow_nominal, -mRad_flow_nominal, mRad_flow_nominal-mRadVal_flow_nominal}, dp_nominal={100,-8000,6750}) "Mixer between valve and radiators";
Note the negative values, which are used for the ports where the medium is outflowing at nominal conditions. See also the documentation of the model Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM.
We also set the pressure drop of
port 3 of mix
to the same value as the pressure drop of the valve in order
to balance the flows. In practice, this can be done by using a
balance valve, whose pressure drop we lumped into the component
mix
.
Next, we set the pressure drops and flow rates of all flow splitters and mixers. For the flow splitters and mixers that are connected to another flow splitter or mixer which already computes the pressure drop of the connecting flow leg, we set the nominal pressure drop to zero. This will remove the equation for the pressure drop calculation. However, we chose to still use the mixer and splitters to make sure that we properly defined the mixing points in the system.
Next, we connected all fluid ports, and we set open-loop control signals
for the valves, pumps and boilers.
This is implemented using the block
Modelica.Blocks.Sources.Constant.
Using open-loop signals allows testing the model prior to
adding the complexity of closed loop control.
To avoid that the boiler overheats, we set its control input to
0.5
. Otherwise, the boiler temperature would raise quickly
when the radiator pump is off, and the simulation would stop
with an error message that says that its medium temperature is
outside the allowed range.
This completes the initial version of the model. When simulating the model for 2 days, or 172800 seconds, the response shown below should be seen.
The figure shows that the room temperature is around 5°C when the internal heat gain is zero, i.e., it is at the average of the outside temperature and the room design temperature. Since the boiler control signal is 0.5, this indicates that the model is correct.
Extends from Modelica.Icons.Example (Icon for runnable examples).
Type | Name | Default | Description |
---|---|---|---|
replaceable package MediumA | Buildings.Media.GasesPTDecou... | ||
replaceable package MediumW | Buildings.Media.ConstantProp... | Medium model | |
HeatFlowRate | Q_flow_nominal | 20000 | Nominal heat flow rate of radiator [W] |
Temperature | TRadSup_nominal | 273.15 + 50 | Radiator nominal supply water temperature [K] |
Temperature | TRadRet_nominal | 273.15 + 40 | Radiator nominal return water temperature [K] |
MassFlowRate | mRad_flow_nominal | Q_flow_nominal/4200/(TRadSup... | Radiator nominal mass flow rate [kg/s] |
Temperature | TBoiSup_nominal | 273.15 + 70 | Boiler nominal supply water temperature [K] |
Temperature | TBoiRet_min | 273.15 + 60 | Boiler minimum return water temperature [K] |
MassFlowRate | mBoi_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
MassFlowRate | mRadVal_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Radiator nominal mass flow rate [kg/s] |
Volume | V | 6*10*3 | Room volume [m3] |
MassFlowRate | mA_flow_nominal | V*6/3600 | Nominal mass flow rate [kg/s] |
HeatFlowRate | QRooInt_flow | 4000 | Internal heat gains of the room [W] |
Type | Name | Description |
---|---|---|
replaceable package MediumA | ||
replaceable package MediumW | Medium model |
model System3 "3rd part of the system model, which adds the boiler loop with open loop control" extends Modelica.Icons.Example; replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated; replaceable package MediumW = Buildings.Media.ConstantPropertyLiquidWater "Medium model"; parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal = 20000 "Nominal heat flow rate of radiator"; parameter Modelica.SIunits.Temperature TRadSup_nominal = 273.15+50 "Radiator nominal supply water temperature"; parameter Modelica.SIunits.Temperature TRadRet_nominal = 273.15+40 "Radiator nominal return water temperature"; parameter Modelica.SIunits.MassFlowRate mRad_flow_nominal= Q_flow_nominal/4200/(TRadSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate"; //-------------------------Step 4: Boiler design values-------------------------// parameter Modelica.SIunits.Temperature TBoiSup_nominal = 273.15+70 "Boiler nominal supply water temperature"; parameter Modelica.SIunits.Temperature TBoiRet_min = 273.15+60 "Boiler minimum return water temperature"; parameter Modelica.SIunits.MassFlowRate mBoi_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TBoiRet_min) "Boiler nominal mass flow rate"; //------------------------------------------------------------------------------// //----------------Radiator loop: Three-way valve: mass flow rate----------------// parameter Modelica.SIunits.MassFlowRate mRadVal_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate"; //------------------------------------------------------------------------------//inner Modelica.Fluid.System system; Buildings.Fluid.MixingVolumes.MixingVolume vol( redeclare package Medium = MediumA, m_flow_nominal=mA_flow_nominal, V=V); Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=20000/30) "Thermal conductance with the ambient"; parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TOut(T=263.15) "Outside temperature"; Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea "Prescribed heat flow"; Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(C=2*V*1.2*1006) "Heat capacity for furniture and walls"; Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain"; Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad( redeclare package Medium = MediumW, Q_flow_nominal=Q_flow_nominal, T_a_nominal=TRadSup_nominal, T_b_nominal=TRadRet_nominal) "Radiator"; Buildings.Fluid.Sensors.TemperatureTwoPort temSup(redeclare package Medium = MediumW, m_flow_nominal=mRad_flow_nominal) "Supply water temperature"; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temRoo "Room temperature"; Buildings.Fluid.Movers.FlowMachine_m_flow pumRad(m_flow_nominal=mRad_flow_nominal, redeclare package Medium = MediumW) "Pump for radiator"; //-------------------------Step 3: Splitter and mixers------------------------//Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix( redeclare package Medium =MediumW, m_flow_nominal={mRadVal_flow_nominal, -mRad_flow_nominal, mRad_flow_nominal-mRadVal_flow_nominal}, dp_nominal={100,-8000,6750}) "Mixer between valve and radiators"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl( redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal, -mRadVal_flow_nominal, -mBoi_flow_nominal}, dp_nominal={200,-200,-50}) "Splitter of boiler loop bypass"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl2(redeclare package Medium = MediumW, dp_nominal={0,0,0}, m_flow_nominal={mRad_flow_nominal,-mRadVal_flow_nominal,-mRad_flow_nominal + mRadVal_flow_nominal}); Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix2(redeclare package Medium = MediumW, dp_nominal={0,-200,0}, m_flow_nominal={mRadVal_flow_nominal,-mBoi_flow_nominal,mBoi_flow_nominal}) "Mixer"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl4(redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal*{1,-1,-1}, dp_nominal=200*{1,-1,-1}) "Splitter for radiator loop valve bypass"; //----------------------------------------------------------------------------//Buildings.Fluid.Movers.FlowMachine_m_flow pumBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Pump for boiler"; //-------------------------------Step 3: Boiler-------------------------------//Buildings.Fluid.Boilers.BoilerPolynomial boi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, dp_nominal=2000, Q_flow_nominal=Q_flow_nominal, fue=Buildings.Fluid.Data.Fuels.HeatingOilLowerHeatingValue()) "Boiler"; //----------------------------------------------------------------------------// //--------------------------Step 3: Three-way Valve---------------------------//Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valRad( redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve for radiator loop"; //----------------------------------------------------------------------------//Buildings.Fluid.Sources.FixedBoundary preSou(redeclare package Medium = MediumW, nPorts=1) "Source for pressure and to account for thermal expansion of water"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve for boiler"; Buildings.Fluid.Sensors.TemperatureTwoPort temRet(redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Return water temperature"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl1(redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal,-mBoi_flow_nominal,-mBoi_flow_nominal}, dp_nominal={0,0,-200}) "Splitter"; Modelica.Blocks.Sources.Constant const(k=1) "Constant control signal for valves"; Modelica.Blocks.Sources.Constant conBoi(k=mBoi_flow_nominal) "Constant mass flow rate for boiler pump"; Modelica.Blocks.Sources.Constant const1(k=0.5) "Constant control signal for valves"; Modelica.Blocks.Logical.Hysteresis hysPum(uLow=273.15 + 19, uHigh=273.15 + 21) "Pump hysteresis"; Modelica.Blocks.Math.BooleanToReal booToReaRad(realTrue=mRad_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Logical.Not not1 "Negate output of hysteresis"; equationconnect(TOut.port, theCon.port_a); connect(theCon.port_b, vol.heatPort); connect(preHea.port, vol.heatPort); connect(heaCap.port, vol.heatPort); connect(timTab.y[1], preHea.Q_flow); connect(temSup.port_b, rad.port_a); connect(temRoo.port, vol.heatPort); connect(rad.heatPortCon, vol.heatPort); connect(rad.heatPortRad, vol.heatPort); connect(pumRad.port_b, temSup.port_a); connect(boi.port_b, pumBoi.port_a); connect(pumBoi.port_b, spl1.port_1); connect(spl1.port_2, spl.port_1); connect(spl.port_2, valRad.port_1); connect(valRad.port_2, mix.port_1); connect(spl1.port_3, valBoi.port_3); connect(valBoi.port_2, temRet.port_a); connect(temRet.port_b, boi.port_a); connect(boi.port_a, preSou.ports[1]); connect(mix2.port_2, valBoi.port_1); connect(spl4.port_2, mix2.port_1); connect(spl2.port_2, spl4.port_1); connect(valRad.port_3, spl4.port_3); connect(spl.port_3, mix2.port_3); connect(mix.port_3, spl2.port_3); connect(mix.port_2, pumRad.port_a); connect(rad.port_b, spl2.port_1); connect(const.y, valRad.y); connect(conBoi.y, pumBoi.m_flow_in); connect(const.y, valBoi.y); connect(const1.y, boi.y); connect(temRoo.T,hysPum. u); connect(hysPum.y,not1. u); connect(not1.y,booToReaRad. u); connect(booToReaRad.y, pumRad.m_flow_in); end System3;
This part of the system model adds to the model that is implemented in Buildings.Examples.Tutorial.Boiler.System3 closed loop control for the pumps and the boiler. The control valves are still open loop control.
This model was built as follows:
First, we copied the model
Buildings.Examples.Tutorial.Boiler.System3
and called it
Buildings.Examples.Tutorial.Boiler.System4
.
Next, we added the outdoor temperature sensor senTOut
,
which we will use to disable the plant when the outdoor temperature
is warmer than 17°C.
This is implemented by the instances hysTOut
and
not2
, whose output signal is connected
to the block and1
as shown in the figure below.
Similar to the control of the radiator pump, we used a boolean to real converter, followed by a first order filter, to set the mass flow rate of the boiler pump.
Next, for the boiler on/off control, we use again a hysteresis block
(instance hysTBoi
), which we configured as
Modelica.Blocks.Logical.Hysteresis hysTBoi(uLow=273.15 + 70, uHigh=273.15 + 90) "Hysteresis for on/off of boiler";
The output of the hysteresis block is sent to the instance
not3
, which negates its input signal.
The output signal of the not3
instance is then sent to the
and2
block, which ensures that the boiler is only
on when the pumps are on and the temperature is below
70°C, and that the boiler is off if its temperature reaches 90°C.
Therefore, the boiler control sequence is as shown below.
This completes the closed loop control of the boiler and the pumps. When simulating the model for 2 days, or 172800 seconds, the response shown below should be seen.
The figure shows that the return water temperature
temRet.T
is below
50°C for quite some time when the system heats up.
Furthermore, the supply water temperature
temSup.T
is oscillating with the boiler temperature.
We will fix these issues by adding closed loop control for the valves in model
Buildings.Examples.Tutorial.Boiler.System5.
Type | Name | Default | Description |
---|---|---|---|
replaceable package MediumA | Buildings.Media.GasesPTDecou... | ||
replaceable package MediumW | Buildings.Media.ConstantProp... | Medium model | |
HeatFlowRate | Q_flow_nominal | 20000 | Nominal heat flow rate of radiator [W] |
Temperature | TRadSup_nominal | 273.15 + 50 | Radiator nominal supply water temperature [K] |
Temperature | TRadRet_nominal | 273.15 + 40 | Radiator nominal return water temperature [K] |
MassFlowRate | mRad_flow_nominal | Q_flow_nominal/4200/(TRadSup... | Radiator nominal mass flow rate [kg/s] |
Temperature | TBoiSup_nominal | 273.15 + 70 | Boiler nominal supply water temperature [K] |
Temperature | TBoiRet_min | 273.15 + 60 | Boiler minimum return water temperature [K] |
MassFlowRate | mBoi_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
MassFlowRate | mRadVal_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
Volume | V | 6*10*3 | Room volume [m3] |
MassFlowRate | mA_flow_nominal | V*6/3600 | Nominal mass flow rate [kg/s] |
HeatFlowRate | QRooInt_flow | 4000 | Internal heat gains of the room [W] |
Type | Name | Description |
---|---|---|
replaceable package MediumA | ||
replaceable package MediumW | Medium model |
model System4 "4th part of the system model, which adds closed-loop control for the pumps and the boiler" extends Modelica.Icons.Example; replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated; replaceable package MediumW = Buildings.Media.ConstantPropertyLiquidWater "Medium model"; parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal = 20000 "Nominal heat flow rate of radiator"; parameter Modelica.SIunits.Temperature TRadSup_nominal = 273.15+50 "Radiator nominal supply water temperature"; parameter Modelica.SIunits.Temperature TRadRet_nominal = 273.15+40 "Radiator nominal return water temperature"; parameter Modelica.SIunits.MassFlowRate mRad_flow_nominal= Q_flow_nominal/4200/(TRadSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate"; parameter Modelica.SIunits.Temperature TBoiSup_nominal = 273.15+70 "Boiler nominal supply water temperature"; parameter Modelica.SIunits.Temperature TBoiRet_min = 273.15+60 "Boiler minimum return water temperature"; parameter Modelica.SIunits.MassFlowRate mBoi_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TBoiRet_min) "Boiler nominal mass flow rate"; parameter Modelica.SIunits.MassFlowRate mRadVal_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TRadRet_nominal) "Boiler nominal mass flow rate";inner Modelica.Fluid.System system; Buildings.Fluid.MixingVolumes.MixingVolume vol( redeclare package Medium = MediumA, m_flow_nominal=mA_flow_nominal, V=V); Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=20000/30) "Thermal conductance with the ambient"; parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TOut(T=263.15) "Outside temperature"; Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea "Prescribed heat flow"; Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(C=2*V*1.2*1006) "Heat capacity for furniture and walls"; Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain"; Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad( redeclare package Medium = MediumW, Q_flow_nominal=Q_flow_nominal, T_a_nominal=TRadSup_nominal, T_b_nominal=TRadRet_nominal) "Radiator"; Buildings.Fluid.Sensors.TemperatureTwoPort temSup(redeclare package Medium = MediumW, m_flow_nominal=mRad_flow_nominal) "Supply water temperature"; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temRoo "Room temperature"; Buildings.Fluid.Movers.FlowMachine_m_flow pumRad(m_flow_nominal=mRad_flow_nominal, redeclare package Medium = MediumW) "Pump for radiator"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix( redeclare package Medium =MediumW, m_flow_nominal={mRadVal_flow_nominal, -mRad_flow_nominal, mRad_flow_nominal-mRadVal_flow_nominal}, dp_nominal={100,-8000,6750}) "Mixer between valve and radiators"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl( redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal, -mRadVal_flow_nominal, -mBoi_flow_nominal}, dp_nominal={200,-200,-50}) "Splitter of boiler loop bypass"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl2(redeclare package Medium = MediumW, dp_nominal={0,0,0}, m_flow_nominal={mRad_flow_nominal,-mRadVal_flow_nominal,-mRad_flow_nominal + mRadVal_flow_nominal}); Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix2(redeclare package Medium = MediumW, dp_nominal={0,-200,0}, m_flow_nominal={mRadVal_flow_nominal,-mBoi_flow_nominal,mBoi_flow_nominal}) "Mixer"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl4(redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal*{1,-1,-1}, dp_nominal=200*{1,-1,-1}) "Splitter for radiator loop valve bypass"; Buildings.Fluid.Movers.FlowMachine_m_flow pumBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Pump for boiler"; Buildings.Fluid.Boilers.BoilerPolynomial boi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, dp_nominal=2000, Q_flow_nominal=Q_flow_nominal, fue=Buildings.Fluid.Data.Fuels.HeatingOilLowerHeatingValue()) "Boiler"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valRad( redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve"; Buildings.Fluid.Sources.FixedBoundary preSou(redeclare package Medium = MediumW, nPorts=1) "Source for pressure and to account for thermal expansion of water"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve for boiler"; Buildings.Fluid.Sensors.TemperatureTwoPort temRet(redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Return water temperature"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl1(redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal,-mBoi_flow_nominal,-mBoi_flow_nominal}, dp_nominal={0,0,-200}) "Splitter"; Modelica.Blocks.Sources.Constant const(k=1) "Constant control signal for valves"; //---------------------Step 2: Outdoor temperature sensor and control------------------//Modelica.Blocks.Logical.Hysteresis hysTOut(uLow=273.15 + 16, uHigh=273.15 + 17) "Hysteresis for on/off based on outside temperature"; Modelica.Blocks.Logical.Not not2; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTOut "Outdoor temperature sensor"; //------------------------------------------------------------------------------------// //-------------------------------Step 4: Boiler hysteresis----------------------------//Modelica.Blocks.Logical.Hysteresis hysTBoi(uHigh=273.15 + 90, uLow=273.15 + 70) "Hysteresis for on/off of boiler"; Modelica.Blocks.Logical.Not not3; Modelica.Blocks.Logical.And and1; //------------------------------------------------------------------------------------// //-------------------------Step 3: Boolean to real: Boiler Pump-----------------------//Modelica.Blocks.Math.BooleanToReal booToReaRad1(realTrue=mBoi_flow_nominal) "Boiler pump signal"; //------------------------------------------------------------------------------------//Modelica.Blocks.Logical.And and2; //---------------------------------Step 4: Boiler signal------------------------------//Modelica.Blocks.Math.BooleanToReal booToReaRad2(realTrue=1) "Boiler signal"; //------------------------------------------------------------------------------------//Modelica.Blocks.Logical.Hysteresis hysPum(uLow=273.15 + 19, uHigh=273.15 + 21) "Pump hysteresis"; Modelica.Blocks.Math.BooleanToReal booToReaRad(realTrue=mRad_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Logical.Not not1 "Negate output of hysteresis"; equationconnect(TOut.port, theCon.port_a); connect(theCon.port_b, vol.heatPort); connect(preHea.port, vol.heatPort); connect(heaCap.port, vol.heatPort); connect(timTab.y[1], preHea.Q_flow); connect(temSup.port_b, rad.port_a); connect(temRoo.port, vol.heatPort); connect(rad.heatPortCon, vol.heatPort); connect(rad.heatPortRad, vol.heatPort); connect(pumRad.port_b, temSup.port_a); connect(boi.port_b, pumBoi.port_a); connect(pumBoi.port_b, spl1.port_1); connect(spl1.port_2, spl.port_1); connect(spl.port_2, valRad.port_1); connect(valRad.port_2, mix.port_1); connect(spl1.port_3, valBoi.port_3); connect(valBoi.port_2, temRet.port_a); connect(temRet.port_b, boi.port_a); connect(boi.port_a, preSou.ports[1]); connect(mix2.port_2, valBoi.port_1); connect(spl4.port_2, mix2.port_1); connect(spl2.port_2, spl4.port_1); connect(valRad.port_3, spl4.port_3); connect(spl.port_3, mix2.port_3); connect(mix.port_3, spl2.port_3); connect(mix.port_2, pumRad.port_a); connect(rad.port_b, spl2.port_1); connect(const.y, valRad.y); connect(const.y, valBoi.y); connect(hysTOut.y, not2.u); connect(senTOut.port, TOut.port); connect(hysTBoi.y, not3.u); connect(not2.y, and1.u2); connect(boi.T, hysTBoi.u); connect(senTOut.T, hysTOut.u); connect(and1.y, booToReaRad1.u); connect(and1.y, and2.u1); connect(not3.y, and2.u2); connect(and2.y, booToReaRad2.u); connect(booToReaRad2.y, boi.y); connect(temRoo.T,hysPum. u); connect(hysPum.y,not1. u); connect(not1.y, and1.u1); connect(and1.y, booToReaRad.u); connect(booToReaRad.y, pumRad.m_flow_in); connect(booToReaRad1.y, pumBoi.m_flow_in); end System4;
This part of the system model adds to the model that is implemented in Buildings.Examples.Tutorial.Boiler.System4 closed loop control for the valves.
This model was built as follows:
First, we copied the model
Buildings.Examples.Tutorial.Boiler.System4
and called it
Buildings.Examples.Tutorial.Boiler.System5
.
Next, we added closed loop control for the boiler valve as shown in the figure below.
This is implemented using the constant block Modelica.Blocks.Sources.Constant for the set point, the PID controller with output limitation Buildings.Controls.Continuous.LimPID. We configured the controller as
Buildings.Controls.Continuous.LimPID conPIDBoi( controllerType=Modelica.Blocks.Types.SimpleController.P, k=0.1, Ti=120, Td=1) "Controller for valve in boiler loop";
We set the proportional band to 10 Kelvin, hence k=0.1
.
We set the integral time constant to 120 seconds, which is
the same time as is required to open or close the valve.
These settings turn out to give satisfactory closed loop control performance.
Otherwise, we would need to retune the controller, which is
usually easiest by configuring the controller as a P-controller, then tuning the
proportional gain, and finally changing it to a PI-controller and tuning the
integral time constant.
The valve control for the radiator loop is implemented similar to
the boiler loop, with the exception that the setpoint is computed
using the model
Buildings.Controls.SetPoints.Table to implement
a set point that shifts as a function of the room temperature.
This instance is called TSetSup
in the
control sequence shown in the figure below.
Its configuration is
Controls.SetPoints.Table TSetSup(table=[273.15 + 19, 273.15 + 50; 273.15 + 21, 273.15 + 21]) "Setpoint for supply water temperature";
This completes the closed loop control. When simulating the model for 2 days, or 172800 seconds, the response shown below should be seen.
The figure shows that the return water temperature
temRet.T
quickly raises to 50°C and the supply water temperature
temSup.T
has smaller oscillations compared to
Buildings.Examples.Tutorial.Boiler.System4.
Type | Name | Default | Description |
---|---|---|---|
replaceable package MediumA | Buildings.Media.GasesPTDecou... | ||
replaceable package MediumW | Buildings.Media.ConstantProp... | Medium model | |
HeatFlowRate | Q_flow_nominal | 20000 | Nominal heat flow rate of radiator [W] |
Temperature | TRadSup_nominal | 273.15 + 50 | Radiator nominal supply water temperature [K] |
Temperature | TRadRet_nominal | 273.15 + 40 | Radiator nominal return water temperature [K] |
MassFlowRate | mRad_flow_nominal | Q_flow_nominal/4200/(TRadSup... | Radiator nominal mass flow rate [kg/s] |
Temperature | TBoiSup_nominal | 273.15 + 70 | Boiler nominal supply water temperature [K] |
Temperature | TBoiRet_min | 273.15 + 60 | Boiler minimum return water temperature [K] |
MassFlowRate | mBoi_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
MassFlowRate | mRadVal_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
Volume | V | 6*10*3 | Room volume [m3] |
MassFlowRate | mA_flow_nominal | V*6/3600 | Nominal mass flow rate [kg/s] |
HeatFlowRate | QRooInt_flow | 4000 | Internal heat gains of the room [W] |
Type | Name | Description |
---|---|---|
replaceable package MediumA | ||
replaceable package MediumW | Medium model |
model System5 "5th part of the system model, which adds closed-loop control for the valves" extends Modelica.Icons.Example; replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated; replaceable package MediumW = Buildings.Media.ConstantPropertyLiquidWater "Medium model"; parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal = 20000 "Nominal heat flow rate of radiator"; parameter Modelica.SIunits.Temperature TRadSup_nominal = 273.15+50 "Radiator nominal supply water temperature"; parameter Modelica.SIunits.Temperature TRadRet_nominal = 273.15+40 "Radiator nominal return water temperature"; parameter Modelica.SIunits.MassFlowRate mRad_flow_nominal= Q_flow_nominal/4200/(TRadSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate"; parameter Modelica.SIunits.Temperature TBoiSup_nominal = 273.15+70 "Boiler nominal supply water temperature"; parameter Modelica.SIunits.Temperature TBoiRet_min = 273.15+60 "Boiler minimum return water temperature"; parameter Modelica.SIunits.MassFlowRate mBoi_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TBoiRet_min) "Boiler nominal mass flow rate"; parameter Modelica.SIunits.MassFlowRate mRadVal_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TRadRet_nominal) "Boiler nominal mass flow rate";inner Modelica.Fluid.System system; Buildings.Fluid.MixingVolumes.MixingVolume vol( redeclare package Medium = MediumA, m_flow_nominal=mA_flow_nominal, V=V); Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=20000/30) "Thermal conductance with the ambient"; parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TOut(T=263.15) "Outside temperature"; Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea "Prescribed heat flow"; Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(C=2*V*1.2*1006) "Heat capacity for furniture and walls"; Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain"; Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad( redeclare package Medium = MediumW, Q_flow_nominal=Q_flow_nominal, T_a_nominal=TRadSup_nominal, T_b_nominal=TRadRet_nominal) "Radiator"; Buildings.Fluid.Sensors.TemperatureTwoPort temSup(redeclare package Medium = MediumW, m_flow_nominal=mRad_flow_nominal) "Supply water temperature"; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temRoo "Room temperature"; Buildings.Fluid.Movers.FlowMachine_m_flow pumRad(m_flow_nominal=mRad_flow_nominal, redeclare package Medium = MediumW) "Pump for radiator"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix( redeclare package Medium =MediumW, m_flow_nominal={mRadVal_flow_nominal, -mRad_flow_nominal, mRad_flow_nominal-mRadVal_flow_nominal}, dp_nominal={100,-8000,6750}) "Mixer between valve and radiators"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl( redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal, -mRadVal_flow_nominal, -mBoi_flow_nominal}, dp_nominal={200,-200,-50}) "Splitter of boiler loop bypass"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl2(redeclare package Medium = MediumW, dp_nominal={0,0,0}, m_flow_nominal={mRad_flow_nominal,-mRadVal_flow_nominal,-mRad_flow_nominal + mRadVal_flow_nominal}); Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix2(redeclare package Medium = MediumW, dp_nominal={0,-200,0}, m_flow_nominal={mRadVal_flow_nominal,-mBoi_flow_nominal,mBoi_flow_nominal}) "Mixer"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl4(redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal*{1,-1,-1}, dp_nominal=200*{1,-1,-1}) "Splitter for radiator loop valve bypass"; Buildings.Fluid.Movers.FlowMachine_m_flow pumBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Pump for boiler"; Buildings.Fluid.Boilers.BoilerPolynomial boi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, dp_nominal=2000, Q_flow_nominal=Q_flow_nominal, fue=Buildings.Fluid.Data.Fuels.HeatingOilLowerHeatingValue()) "Boiler"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valRad( redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve"; Buildings.Fluid.Sources.FixedBoundary preSou(redeclare package Medium = MediumW, nPorts=1) "Source for pressure and to account for thermal expansion of water"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve for boiler"; Buildings.Fluid.Sensors.TemperatureTwoPort temRet(redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Return water temperature"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl1(redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal,-mBoi_flow_nominal,-mBoi_flow_nominal}, dp_nominal={0,0,-200}) "Splitter"; Modelica.Blocks.Logical.Hysteresis hysTOut(uLow=273.15 + 16, uHigh=273.15 + 17) "Hysteresis for on/off based on outside temperature"; Modelica.Blocks.Logical.Not not2; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTOut "Outdoor temperature sensor"; Modelica.Blocks.Logical.Hysteresis hysTBoi(uLow=273.15 + 70, uHigh=273.15 + 90) "Hysteresis for on/off of boiler"; Modelica.Blocks.Logical.Not not3; Modelica.Blocks.Logical.And and1; Modelica.Blocks.Math.BooleanToReal booToReaRad1(realTrue=mBoi_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Logical.And and2; Modelica.Blocks.Math.BooleanToReal booToReaRad2(realTrue=1) "Radiator pump signal"; Modelica.Blocks.Logical.Hysteresis hysPum(uLow=273.15 + 19, uHigh=273.15 + 21) "Pump hysteresis"; Modelica.Blocks.Math.BooleanToReal booToReaRad(realTrue=mRad_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Logical.Not not1 "Negate output of hysteresis"; //------------------------Step 2: Boiler loop valve control-----------------------//Modelica.Blocks.Sources.Constant TSetBoiRet(k=TBoiRet_min) "Temperature setpoint for boiler return"; Buildings.Controls.Continuous.LimPID conPIDBoi( Td=1, Ti=120, controllerType=Modelica.Blocks.Types.SimpleController.PI, k=0.1) "Controller for valve in boiler loop"; //--------------------------------------------------------------------------------// //----------------------Step 3: Radiator loop valve control-----------------------//Controls.SetPoints.Table TSetSup(table=[273.15 + 19, 273.15 + 50; 273.15 + 21, 273.15 + 21]) "Setpoint for supply water temperature"; Buildings.Controls.Continuous.LimPID conPIDRad( Td=1, Ti=120, controllerType=Modelica.Blocks.Types.SimpleController.PI, k=0.1) "Controller for valve in radiator loop"; //--------------------------------------------------------------------------------// equationconnect(TOut.port, theCon.port_a); connect(theCon.port_b, vol.heatPort); connect(preHea.port, vol.heatPort); connect(heaCap.port, vol.heatPort); connect(timTab.y[1], preHea.Q_flow); connect(temSup.port_b, rad.port_a); connect(temRoo.port, vol.heatPort); connect(rad.heatPortCon, vol.heatPort); connect(rad.heatPortRad, vol.heatPort); connect(pumRad.port_b, temSup.port_a); connect(boi.port_b, pumBoi.port_a); connect(pumBoi.port_b, spl1.port_1); connect(spl1.port_2, spl.port_1); connect(spl.port_2, valRad.port_1); connect(valRad.port_2, mix.port_1); connect(spl1.port_3, valBoi.port_3); connect(valBoi.port_2, temRet.port_a); connect(temRet.port_b, boi.port_a); connect(boi.port_a, preSou.ports[1]); connect(mix2.port_2, valBoi.port_1); connect(spl4.port_2, mix2.port_1); connect(spl2.port_2, spl4.port_1); connect(valRad.port_3, spl4.port_3); connect(spl.port_3, mix2.port_3); connect(mix.port_3, spl2.port_3); connect(mix.port_2, pumRad.port_a); connect(rad.port_b, spl2.port_1); connect(hysTOut.y, not2.u); connect(senTOut.port, TOut.port); connect(hysTBoi.y, not3.u); connect(not2.y, and1.u2); connect(boi.T, hysTBoi.u); connect(senTOut.T, hysTOut.u); connect(and1.y, booToReaRad1.u); connect(and1.y, and2.u1); connect(not3.y, and2.u2); connect(and2.y, booToReaRad2.u); connect(booToReaRad2.y, boi.y); connect(temRoo.T,hysPum. u); connect(hysPum.y,not1. u); connect(not1.y, and1.u1); connect(and1.y, booToReaRad.u); connect(temRet.T,conPIDBoi. u_s); connect(TSetBoiRet.y,conPIDBoi. u_m); connect(temRoo.T, TSetSup.u); connect(TSetSup.y, conPIDRad.u_s); connect(temSup.T, conPIDRad.u_m); connect(conPIDRad.y, valRad.y); connect(booToReaRad.y, pumRad.m_flow_in); connect(booToReaRad1.y, pumBoi.m_flow_in); connect(conPIDBoi.y, valBoi.y); end System5;
This part of the system model adds to the model that is implemented in Buildings.Examples.Tutorial.Boiler.System5 weather data, and it changes the control to PI control.
This model was built as follows:
First, we copied the model
Buildings.Examples.Tutorial.Boiler.System5
and called it
Buildings.Examples.Tutorial.Boiler.System6
.
Next, we added the weather data as shown in the figure below.
The weather data reader is implemented using
BoundaryConditions.WeatherData.ReaderTMY3 weaDat( filNam="modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos") "Weather data reader";
The yellow icon in the middle of the figure is an instance of Buildings.BoundaryConditions.WeatherData.Bus. This is required to extract the dry bulb temperature from the weather data bus.
Note that we changed the instance TOut
from
Modelica.Thermal.HeatTransfer.Sources.FixedTemperature
to
Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature
in order to use the dry-bulb temperature as an input signal.
This completes the closed loop control. When simulating the model for 2 days, or 172800 seconds, the response shown below should be seen.
The figure shows that the boiler temperature is regulated between 70°C and 90°C, that the boiler inlet temperature is above 60°C, and that the room temperature and the supply water temperature are maintained at their set point.
Extends from Modelica.Icons.Example (Icon for runnable examples).
Type | Name | Default | Description |
---|---|---|---|
replaceable package MediumA | Buildings.Media.GasesPTDecou... | ||
replaceable package MediumW | Buildings.Media.ConstantProp... | Medium model | |
HeatFlowRate | Q_flow_nominal | 20000 | Nominal heat flow rate of radiator [W] |
Temperature | TRadSup_nominal | 273.15 + 50 | Radiator nominal supply water temperature [K] |
Temperature | TRadRet_nominal | 273.15 + 40 | Radiator nominal return water temperature [K] |
MassFlowRate | mRad_flow_nominal | Q_flow_nominal/4200/(TRadSup... | Radiator nominal mass flow rate [kg/s] |
Temperature | TBoiSup_nominal | 273.15 + 70 | Boiler nominal supply water temperature [K] |
Temperature | TBoiRet_min | 273.15 + 60 | Boiler minimum return water temperature [K] |
MassFlowRate | mBoi_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
MassFlowRate | mRadVal_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
Volume | V | 6*10*3 | Room volume [m3] |
MassFlowRate | mA_flow_nominal | V*6/3600 | Nominal mass flow rate [kg/s] |
HeatFlowRate | QRooInt_flow | 4000 | Internal heat gains of the room [W] |
Type | Name | Description |
---|---|---|
replaceable package MediumA | ||
replaceable package MediumW | Medium model | |
Bus | weaBus |
model System6 "6th part of the system model, which adds weather data and changes to PI control" extends Modelica.Icons.Example; replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated; replaceable package MediumW = Buildings.Media.ConstantPropertyLiquidWater "Medium model"; parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal = 20000 "Nominal heat flow rate of radiator"; parameter Modelica.SIunits.Temperature TRadSup_nominal = 273.15+50 "Radiator nominal supply water temperature"; parameter Modelica.SIunits.Temperature TRadRet_nominal = 273.15+40 "Radiator nominal return water temperature"; parameter Modelica.SIunits.MassFlowRate mRad_flow_nominal= Q_flow_nominal/4200/(TRadSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate"; parameter Modelica.SIunits.Temperature TBoiSup_nominal = 273.15+70 "Boiler nominal supply water temperature"; parameter Modelica.SIunits.Temperature TBoiRet_min = 273.15+60 "Boiler minimum return water temperature"; parameter Modelica.SIunits.MassFlowRate mBoi_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TBoiRet_min) "Boiler nominal mass flow rate"; parameter Modelica.SIunits.MassFlowRate mRadVal_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TRadRet_nominal) "Boiler nominal mass flow rate";inner Modelica.Fluid.System system; Buildings.Fluid.MixingVolumes.MixingVolume vol( redeclare package Medium = MediumA, m_flow_nominal=mA_flow_nominal, V=V); Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=20000/30) "Thermal conductance with the ambient"; parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea "Prescribed heat flow"; Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(C=2*V*1.2*1006) "Heat capacity for furniture and walls"; Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain"; Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad( redeclare package Medium = MediumW, Q_flow_nominal=Q_flow_nominal, T_a_nominal=TRadSup_nominal, T_b_nominal=TRadRet_nominal) "Radiator"; Buildings.Fluid.Sensors.TemperatureTwoPort temSup(redeclare package Medium = MediumW, m_flow_nominal=mRad_flow_nominal) "Supply water temperature"; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temRoo "Room temperature"; Buildings.Fluid.Movers.FlowMachine_m_flow pumRad(m_flow_nominal=mRad_flow_nominal, redeclare package Medium = MediumW) "Pump for radiator"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix( redeclare package Medium =MediumW, m_flow_nominal={mRadVal_flow_nominal, -mRad_flow_nominal, mRad_flow_nominal-mRadVal_flow_nominal}, dp_nominal={100,-8000,6750}) "Mixer between valve and radiators"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl( redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal, -mRadVal_flow_nominal, -mBoi_flow_nominal}, dp_nominal={200,-200,-50}) "Splitter of boiler loop bypass"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl2(redeclare package Medium = MediumW, dp_nominal={0,0,0}, m_flow_nominal={mRad_flow_nominal,-mRadVal_flow_nominal,-mRad_flow_nominal + mRadVal_flow_nominal}); Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix2(redeclare package Medium = MediumW, m_flow_nominal={mRadVal_flow_nominal,-mBoi_flow_nominal,mBoi_flow_nominal}, dp_nominal={0,-200,0}) "Mixer"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl4(redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal*{1,-1,-1}, dp_nominal=200*{1,-1,-1}) "Splitter for radiator loop valve bypass"; Buildings.Fluid.Movers.FlowMachine_m_flow pumBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Pump for boiler"; Buildings.Fluid.Boilers.BoilerPolynomial boi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, dp_nominal=2000, Q_flow_nominal=Q_flow_nominal, fue=Buildings.Fluid.Data.Fuels.HeatingOilLowerHeatingValue()) "Boiler"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valRad( redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve"; Buildings.Fluid.Sources.FixedBoundary preSou(redeclare package Medium = MediumW, nPorts=1) "Source for pressure and to account for thermal expansion of water"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve for boiler"; Buildings.Fluid.Sensors.TemperatureTwoPort temRet(redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Return water temperature"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl1(redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal,-mBoi_flow_nominal,-mBoi_flow_nominal}, dp_nominal={0,0,-200}) "Splitter"; Modelica.Blocks.Logical.Hysteresis hysTOut(uLow=273.15 + 16, uHigh=273.15 + 17) "Hysteresis for on/off based on outside temperature"; Modelica.Blocks.Logical.Not not2; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTOut "Outdoor temperature sensor"; Modelica.Blocks.Logical.Hysteresis hysTBoi(uLow=273.15 + 70, uHigh=273.15 + 90) "Hysteresis for on/off of boiler"; Modelica.Blocks.Logical.Not not3; Modelica.Blocks.Logical.And and1; Modelica.Blocks.Math.BooleanToReal booToReaRad1(realTrue=mBoi_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Logical.And and2; Modelica.Blocks.Math.BooleanToReal booToReaRad2(realTrue=1) "Radiator pump signal"; Modelica.Blocks.Logical.Hysteresis hysPum(uLow=273.15 + 19, uHigh=273.15 + 21) "Pump hysteresis"; Modelica.Blocks.Math.BooleanToReal booToReaRad(realTrue=mRad_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Logical.Not not1 "Negate output of hysteresis"; Modelica.Blocks.Sources.Constant TSetBoiRet(k=TBoiRet_min) "Temperature setpoint for boiler return"; //-----------------------Step 3: Change in controller type-----------------------//Buildings.Controls.Continuous.LimPID conPIDBoi( Td=1, controllerType=Modelica.Blocks.Types.SimpleController.PI, k=0.1, Ti=120) "Controller for valve in boiler loop"; //------------------------------------------------------------------------------//Controls.SetPoints.Table TSetSup(table=[273.15 + 19, 273.15 + 50; 273.15 + 21, 273.15 + 21]) "Setpoint for supply water temperature"; //-----------------------Step 3: Change in controller type-----------------------//Buildings.Controls.Continuous.LimPID conPIDRad( Td=1, controllerType=Modelica.Blocks.Types.SimpleController.PI, k=0.1, Ti=120) "Controller for valve in radiator loop"; //------------------------------------------------------------------------------// //-----------------------------Step 2: Weather data------------------------------//BoundaryConditions.WeatherData.ReaderTMY3 weaDat( filNam="modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos") "Weather data reader"; BoundaryConditions.WeatherData.Bus weaBus; Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TOut "Outside temperature"; //------------------------------------------------------------------------------// equationconnect(theCon.port_b, vol.heatPort); connect(preHea.port, vol.heatPort); connect(heaCap.port, vol.heatPort); connect(timTab.y[1], preHea.Q_flow); connect(temSup.port_b, rad.port_a); connect(temRoo.port, vol.heatPort); connect(rad.heatPortCon, vol.heatPort); connect(rad.heatPortRad, vol.heatPort); connect(pumRad.port_b, temSup.port_a); connect(boi.port_b, pumBoi.port_a); connect(pumBoi.port_b, spl1.port_1); connect(spl1.port_2, spl.port_1); connect(spl.port_2, valRad.port_1); connect(valRad.port_2, mix.port_1); connect(spl1.port_3, valBoi.port_3); connect(valBoi.port_2, temRet.port_a); connect(temRet.port_b, boi.port_a); connect(boi.port_a, preSou.ports[1]); connect(mix2.port_2, valBoi.port_1); connect(spl4.port_2, mix2.port_1); connect(spl2.port_2, spl4.port_1); connect(valRad.port_3, spl4.port_3); connect(spl.port_3, mix2.port_3); connect(mix.port_3, spl2.port_3); connect(mix.port_2, pumRad.port_a); connect(rad.port_b, spl2.port_1); connect(hysTOut.y, not2.u); connect(hysTBoi.y, not3.u); connect(not2.y, and1.u2); connect(boi.T, hysTBoi.u); connect(senTOut.T, hysTOut.u); connect(and1.y, booToReaRad1.u); connect(and1.y, and2.u1); connect(not3.y, and2.u2); connect(and2.y, booToReaRad2.u); connect(booToReaRad2.y, boi.y); connect(temRoo.T,hysPum. u); connect(hysPum.y,not1. u); connect(not1.y, and1.u1); connect(and1.y, booToReaRad.u); connect(temRet.T,conPIDBoi. u_s); connect(TSetBoiRet.y,conPIDBoi. u_m); connect(temRoo.T, TSetSup.u); connect(TSetSup.y, conPIDRad.u_s); connect(temSup.T, conPIDRad.u_m); connect(weaDat.weaBus, weaBus); connect(weaBus.TDryBul, TOut.T); connect(TOut.port, theCon.port_a); connect(TOut.port, senTOut.port); connect(booToReaRad.y, pumRad.m_flow_in); connect(conPIDRad.y, valRad.y); connect(booToReaRad1.y, pumBoi.m_flow_in); connect(conPIDBoi.y, valBoi.y); end System6;
This part of the system model changes the implementation of the control in Buildings.Examples.Tutorial.Boiler.System6 to use a state machine to switch the pumps and the boiler on and off. State machines provide an alternate way to implement discrete event, reactive and hybrid systems. The state machine that is implemented in this model is shown in the figure below.
In the figure above, the ovals depict states, and the arrows are transitions between the states. The transitions fire when the conditions are true.
This model was built as follows:
First, we copied the model
Buildings.Examples.Tutorial.Boiler.System6
and called it
Buildings.Examples.Tutorial.Boiler.System7
.
We implemented the state machine using blocks from the library Modelica_StateGraph2. How to use these blocks is explained in the user's guide of the Modelica_StateGraph2 library.
The figure below shows the state machine. The oval icons are states, and the black bars are transitions. The transitions are enabled when their input signal is true. The red numbers to the right of the transition indicate the delay in seconds. If a delay is present, the input needs to be true during the entire duration of the delay for a transition to fire.
In our implementation, the state allOff
is the initial state,
indicated by its black arrow.
The transition T1
is used to switch the pumps on.
Once the pumps are on, transition T2
may fire, which
would switch the boiler on.
Hence, the boiler can only be on when the pumps are on.
From the state boilerOn
, the only next step can be to
transition to the state pumpsOn
.
Once this state has been reached (and hence the boiler is off),
the pumps can be switched off when transition T4
fires.
Note that the transitions T3
and T4
only fire when their input is true for the entire duration of 10 seconds.
Hence, the pumps and the boiler must run for at least 10 seconds
before they can be switched off.
When simulating the model for 2 days, or 172800 seconds, the response shown below should be seen.
Extends from Modelica.Icons.Example (Icon for runnable examples).
Type | Name | Default | Description |
---|---|---|---|
replaceable package MediumA | Buildings.Media.GasesPTDecou... | ||
replaceable package MediumW | Buildings.Media.ConstantProp... | Medium model | |
HeatFlowRate | Q_flow_nominal | 20000 | Nominal heat flow rate of radiator [W] |
Temperature | TRadSup_nominal | 273.15 + 50 | Radiator nominal supply water temperature [K] |
Temperature | TRadRet_nominal | 273.15 + 40 | Radiator nominal return water temperature [K] |
MassFlowRate | mRad_flow_nominal | Q_flow_nominal/4200/(TRadSup... | Radiator nominal mass flow rate [kg/s] |
Temperature | TBoiSup_nominal | 273.15 + 70 | Boiler nominal supply water temperature [K] |
Temperature | TBoiRet_min | 273.15 + 60 | Boiler minimum return water temperature [K] |
MassFlowRate | mBoi_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
MassFlowRate | mRadVal_flow_nominal | Q_flow_nominal/4200/(TBoiSup... | Boiler nominal mass flow rate [kg/s] |
Volume | V | 6*10*3 | Room volume [m3] |
MassFlowRate | mA_flow_nominal | V*6/3600 | Nominal mass flow rate [kg/s] |
HeatFlowRate | QRooInt_flow | 4000 | Internal heat gains of the room [W] |
Type | Name | Description |
---|---|---|
replaceable package MediumA | ||
replaceable package MediumW | Medium model | |
Bus | weaBus |
model System7 "7th part of the system model, which implements the on/off control using a state machine" extends Modelica.Icons.Example; replaceable package MediumA = Buildings.Media.GasesPTDecoupled.MoistAirUnsaturated; replaceable package MediumW = Buildings.Media.ConstantPropertyLiquidWater "Medium model"; parameter Modelica.SIunits.HeatFlowRate Q_flow_nominal = 20000 "Nominal heat flow rate of radiator"; parameter Modelica.SIunits.Temperature TRadSup_nominal = 273.15+50 "Radiator nominal supply water temperature"; parameter Modelica.SIunits.Temperature TRadRet_nominal = 273.15+40 "Radiator nominal return water temperature"; parameter Modelica.SIunits.MassFlowRate mRad_flow_nominal= Q_flow_nominal/4200/(TRadSup_nominal-TRadRet_nominal) "Radiator nominal mass flow rate"; parameter Modelica.SIunits.Temperature TBoiSup_nominal = 273.15+70 "Boiler nominal supply water temperature"; parameter Modelica.SIunits.Temperature TBoiRet_min = 273.15+60 "Boiler minimum return water temperature"; parameter Modelica.SIunits.MassFlowRate mBoi_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TBoiRet_min) "Boiler nominal mass flow rate"; parameter Modelica.SIunits.MassFlowRate mRadVal_flow_nominal= Q_flow_nominal/4200/(TBoiSup_nominal-TRadRet_nominal) "Boiler nominal mass flow rate";inner Modelica.Fluid.System system; Buildings.Fluid.MixingVolumes.MixingVolume vol( redeclare package Medium = MediumA, m_flow_nominal=mA_flow_nominal, V=V); Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=20000/30) "Thermal conductance with the ambient"; parameter Modelica.SIunits.Volume V=6*10*3 "Room volume"; parameter Modelica.SIunits.MassFlowRate mA_flow_nominal = V*6/3600 "Nominal mass flow rate"; parameter Modelica.SIunits.HeatFlowRate QRooInt_flow = 4000 "Internal heat gains of the room";Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea "Prescribed heat flow"; Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(C=2*V*1.2*1006) "Heat capacity for furniture and walls"; Modelica.Blocks.Sources.CombiTimeTable timTab( extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic, table=[ 0, 0; 8*3600, 0; 8*3600, QRooInt_flow; 18*3600, QRooInt_flow; 18*3600, 0; 24*3600, 0]) "Time table for internal heat gain"; Buildings.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad( redeclare package Medium = MediumW, Q_flow_nominal=Q_flow_nominal, T_a_nominal=TRadSup_nominal, T_b_nominal=TRadRet_nominal) "Radiator"; Buildings.Fluid.Sensors.TemperatureTwoPort temSup(redeclare package Medium = MediumW, m_flow_nominal=mRad_flow_nominal) "Supply water temperature"; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temRoo "Room temperature"; Buildings.Fluid.Movers.FlowMachine_m_flow pumRad(m_flow_nominal=mRad_flow_nominal, redeclare package Medium = MediumW) "Pump for radiator"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix( redeclare package Medium =MediumW, m_flow_nominal={mRadVal_flow_nominal, -mRad_flow_nominal, mRad_flow_nominal-mRadVal_flow_nominal}, dp_nominal={100,-8000,6750}) "Mixer between valve and radiators"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl( redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal, -mRadVal_flow_nominal, -mBoi_flow_nominal}, dp_nominal={200,-200,-50}) "Splitter of boiler loop bypass"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl2(redeclare package Medium = MediumW, dp_nominal={0,0,0}, m_flow_nominal={mRad_flow_nominal,-mRadVal_flow_nominal,-mRad_flow_nominal + mRadVal_flow_nominal}); Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM mix2(redeclare package Medium = MediumW, m_flow_nominal={mRadVal_flow_nominal,-mBoi_flow_nominal,mBoi_flow_nominal}, dp_nominal={0,-200,0}) "Mixer"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl4(redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal*{1,-1,-1}, dp_nominal=200*{1,-1,-1}) "Splitter for radiator loop valve bypass"; Buildings.Fluid.Movers.FlowMachine_m_flow pumBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Pump for boiler"; Buildings.Fluid.Boilers.BoilerPolynomial boi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, dp_nominal=2000, Q_flow_nominal=Q_flow_nominal, fue=Buildings.Fluid.Data.Fuels.HeatingOilLowerHeatingValue()) "Boiler"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valRad( redeclare package Medium = MediumW, m_flow_nominal=mRadVal_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve"; Buildings.Fluid.Sources.FixedBoundary preSou(redeclare package Medium = MediumW, nPorts=1) "Source for pressure and to account for thermal expansion of water"; Buildings.Fluid.Actuators.Valves.ThreeWayEqualPercentageLinear valBoi( redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal, l={0.01,0.01}, dpValve_nominal=6000) "Three-way valve for boiler"; Buildings.Fluid.Sensors.TemperatureTwoPort temRet(redeclare package Medium = MediumW, m_flow_nominal=mBoi_flow_nominal) "Return water temperature"; Buildings.Fluid.FixedResistances.SplitterFixedResistanceDpM spl1(redeclare package Medium = MediumW, m_flow_nominal={mBoi_flow_nominal,-mBoi_flow_nominal,-mBoi_flow_nominal}, dp_nominal={0,0,-200}) "Splitter"; Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTOut "Outdoor temperature sensor"; Modelica.Blocks.Math.BooleanToReal booToReaRad1(realTrue=mBoi_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Math.BooleanToReal booToReaRad2(realTrue=1) "Radiator pump signal"; Modelica.Blocks.Math.BooleanToReal booToReaRad(realTrue=mRad_flow_nominal) "Radiator pump signal"; Modelica.Blocks.Sources.Constant TSetBoiRet(k=TBoiRet_min) "Temperature setpoint for boiler return"; Buildings.Controls.Continuous.LimPID conPIDBoi( Td=1, controllerType=Modelica.Blocks.Types.SimpleController.PI, k=0.1, Ti=120) "Controller for valve in boiler loop"; Controls.SetPoints.Table TSetSup(table=[273.15 + 19, 273.15 + 50; 273.15 + 21, 273.15 + 21]) "Setpoint for supply water temperature"; Buildings.Controls.Continuous.LimPID conPIDRad( Td=1, controllerType=Modelica.Blocks.Types.SimpleController.PI, k=0.1, Ti=120) "Controller for valve in radiator loop"; BoundaryConditions.WeatherData.ReaderTMY3 weaDat( filNam="modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos") "Weather data reader"; BoundaryConditions.WeatherData.Bus weaBus; Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TOut "Outside temperature"; //-------------------------Step 2: State machine implementation-------------------------//Modelica_StateGraph2.Step allOff( nOut=1, initialStep=true, use_activePort=true, nIn=1) "System off"; Modelica_StateGraph2.Transition T1(use_conditionPort=true, delayedTransition= false); Modelica.Blocks.Logical.LessThreshold lessThreshold(threshold=273.15 + 19); Modelica.Blocks.Logical.LessThreshold lessThreshold1(threshold=273.15 + 16); Modelica.Blocks.Logical.And and3; Modelica.Blocks.Logical.LessThreshold lessThreshold2(threshold=273.15 + 70) "Threshold for boiler control"; Modelica_StateGraph2.Step boilerOn( nIn=1, use_activePort=true, nOut=1) "Boiler is on"; Modelica_StateGraph2.Transition T2( use_conditionPort=true, delayedTransition=false); Modelica.Blocks.Logical.GreaterThreshold greThrBoi(threshold=273.15 + 90) "Threshold for boiler control"; Modelica_StateGraph2.Transition T3(use_conditionPort=true, delayedTransition=true, waitTime=10); Modelica.Blocks.Logical.GreaterThreshold greThrTRoo(threshold=273.15 + 21) "Threshold for room temperature"; Modelica.Blocks.Logical.GreaterThreshold greThrTROut(threshold=273.15 + 17) "Threshold for room temperature"; Modelica.Blocks.Logical.And and1; Modelica_StateGraph2.Transition T4(use_conditionPort=true, delayedTransition=true, waitTime=10); Modelica_StateGraph2.Step pumpsOn( nIn=2, use_activePort=false, nOut=2) "Pumps are on"; Modelica.Blocks.Logical.Not not1 "Negate output of hysteresis"; //--------------------------------------------------------------------------------------// equationconnect(theCon.port_b, vol.heatPort); connect(preHea.port, vol.heatPort); connect(heaCap.port, vol.heatPort); connect(timTab.y[1], preHea.Q_flow); connect(temSup.port_b, rad.port_a); connect(temRoo.port, vol.heatPort); connect(rad.heatPortCon, vol.heatPort); connect(rad.heatPortRad, vol.heatPort); connect(pumRad.port_b, temSup.port_a); connect(boi.port_b, pumBoi.port_a); connect(pumBoi.port_b, spl1.port_1); connect(spl1.port_2, spl.port_1); connect(spl.port_2, valRad.port_1); connect(valRad.port_2, mix.port_1); connect(spl1.port_3, valBoi.port_3); connect(valBoi.port_2, temRet.port_a); connect(temRet.port_b, boi.port_a); connect(boi.port_a, preSou.ports[1]); connect(mix2.port_2, valBoi.port_1); connect(spl4.port_2, mix2.port_1); connect(spl2.port_2, spl4.port_1); connect(valRad.port_3, spl4.port_3); connect(spl.port_3, mix2.port_3); connect(mix.port_3, spl2.port_3); connect(mix.port_2, pumRad.port_a); connect(rad.port_b, spl2.port_1); connect(booToReaRad2.y, boi.y); connect(temRet.T,conPIDBoi. u_s); connect(TSetBoiRet.y,conPIDBoi. u_m); connect(temRoo.T, TSetSup.u); connect(TSetSup.y, conPIDRad.u_s); connect(temSup.T, conPIDRad.u_m); connect(weaDat.weaBus, weaBus); connect(weaBus.TDryBul, TOut.T); connect(TOut.port, theCon.port_a); connect(TOut.port, senTOut.port); connect(allOff.outPort[1], T1.inPort); connect(temRoo.T, lessThreshold.u); connect(lessThreshold1.u, senTOut.T); connect(lessThreshold.y, and3.u1); connect(lessThreshold1.y, and3.u2); connect(T1.conditionPort, and3.y); connect(boi.T, lessThreshold2.u); connect(lessThreshold2.y, T2.conditionPort); connect(T2.outPort, boilerOn.inPort[1]); connect(boi.T, greThrBoi.u); connect(boilerOn.outPort[1], T3.inPort); connect(greThrBoi.y, T3.conditionPort); connect(boilerOn.activePort, booToReaRad2.u); connect(temRoo.T, greThrTRoo.u); connect(senTOut.T, greThrTROut.u); connect(greThrTRoo.y, and1.u1); connect(greThrTROut.y, and1.u2); connect(and1.y, T4.conditionPort); connect(T1.outPort, pumpsOn.inPort[1]); connect(allOff.activePort, not1.u); connect(not1.y, booToReaRad.u); connect(not1.y, booToReaRad1.u); connect(T3.outPort, pumpsOn.inPort[2]); connect(T4.outPort, allOff.inPort[1]); connect(pumpsOn.outPort[1], T4.inPort); connect(pumpsOn.outPort[2], T2.inPort); connect(conPIDRad.y, valRad.y); connect(booToReaRad.y, pumRad.m_flow_in); connect(conPIDBoi.y, valBoi.y); connect(booToReaRad1.y, pumBoi.m_flow_in); end System7;