Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses

Package with partial classes of Performance Data

Information

This package contains base classes for the package Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.

Package Content

Name Description
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.NoHeating NoHeating Placeholder to disable heating
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialCarnot PartialCarnot Model with components for Carnot efficiency calculation
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle PartialHeatPumpCycle Partial model to allow selection of only heat pump options
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialRefrigerantCycle PartialRefrigerantCycle Partial model of refrigerant cycle
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialTableData2D PartialTableData2D Partial model with components for TableData2D approach for heat pumps and chillers
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep TableData2DLoadDep Calculation of capacity, heat flow rate and power based on load-dependent 2D table data
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDepSHC TableData2DLoadDepSHC Modeling block for simultaneous heating and cooling systems based on load-dependent 2D table data
Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.Validation Validation Collection of validation models

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.NoHeating Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.NoHeating

Placeholder to disable heating

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.NoHeating

Information

Using this model, the heat pump will always be off. This option is mainly used to avoid warnings about partial model which must be replaced.

Extends from PartialHeatPumpCycle (Partial model to allow selection of only heat pump options).

Parameters

TypeNameDefaultDescription
StringdevIde"NoHeating"Indicates the data source, used to warn users about different vapor compression devices in reversible models
BooleanuseInHeaPum =false to indicate that this model is used in a chiller
Nominal condition
PowerPEle_nominal0Nominal electrical power consumption [W]
TemperatureTCon_nominal273.15Nominal temperature at secondary condenser side [K]
TemperatureTEva_nominal273.15Nominal temperature at secondary evaporator side [K]
HeatFlowRateQHea_flow_nominal0Nominal heating capacity [W]
Advanced
Medium properties
SpecificHeatCapacitycpCon4184Evaporator medium specific heat capacity [J/(kg.K)]
SpecificHeatCapacitycpEva4184Evaporator medium specific heat capacity [J/(kg.K)]

Connectors

TypeNameDescription
output RealOutputPEleElectrical Power consumed by the device [W]
output RealOutputQCon_flowHeat flow rate through condenser [W]
RefrigerantMachineControlBussigBusBus-connector
output RealOutputQEva_flowHeat flow rate through evaporator [W]

Modelica definition

model NoHeating "Placeholder to disable heating" extends PartialHeatPumpCycle( TEva_nominal=273.15, TCon_nominal=273.15, cpEva=4184, cpCon=4184, PEle_nominal=0, redeclare final Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.NoFrosting iceFacCal, devIde="NoHeating", QHea_flow_nominal=0); Modelica.Blocks.Sources.Constant constZer(final k=0) "No heating, hence, zero"; equation connect(constZer.y, feeHeaFloEva.u1); connect(constZer.y, feeHeaFloEva.u2); connect(constZer.y, redQCon.u2); connect(constZer.y, PEle); end NoHeating;

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialCarnot

Model with components for Carnot efficiency calculation

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialCarnot

Information

Partial model for equations and componenents used in both heat pump and chiller with the Carnot approach.

Parameters

TypeNameDefaultDescription
BooleanuseForChi =false to use in heat pump models
Nominal condition
RealetaCarnot_nominal0.3Constant Carnot effectiveness
Efficiency
Booleanuse_constAppTemfalse=true to fix approach temperatures at nominal values. This can improve simulation speed
TemperatureDifferenceTAppCon_nominal Temperature difference between refrigerant and working fluid outlet in condenser [K]
TemperatureDifferenceTAppEva_nominal Temperature difference between refrigerant and working fluid outlet in evaporator [K]
Advanced
TemperatureDifferencedTCarMin5Minimal temperature difference, used to avoid division errors [K]

Modelica definition

partial model PartialCarnot "Model with components for Carnot efficiency calculation" parameter Boolean useForChi "=false to use in heat pump models"; parameter Real etaCarnot_nominal=0.3 "Constant Carnot effectiveness"; parameter Boolean use_constAppTem=false "=true to fix approach temperatures at nominal values. This can improve simulation speed"; parameter Modelica.Units.SI.TemperatureDifference TAppCon_nominal(min=0) "Temperature difference between refrigerant and working fluid outlet in condenser"; parameter Modelica.Units.SI.TemperatureDifference TAppEva_nominal(min=0) "Temperature difference between refrigerant and working fluid outlet in evaporator"; parameter Modelica.Units.SI.TemperatureDifference dTCarMin=5 "Minimal temperature difference, used to avoid division errors"; Modelica.Blocks.Sources.RealExpression reaCarnotCOP(final y=TUseSidAct/ Buildings.Utilities.Math.Functions.smoothMax( x1=dTCarMin, x2=(TConAct - TEvaAct), deltaX=0.25)) "Internal calculation of Carnot COP"; Modelica.Blocks.Math.MultiProduct proQUse_flow(nu=3) "Calculate QUse_flow"; Modelica.Blocks.Math.Product proPEle "Calculate electrical power consumption"; Modelica.Blocks.Sources.Constant constPEle "Constant electrical power consumption"; Modelica.Blocks.Routing.RealPassThrough pasThrYMea "From signal bus"; Modelica.Units.SI.Temperature TUseSidAct = if useForChi then TEvaAct else TConAct "Useful side refrigerant temperature"; Modelica.Units.SI.Temperature TConAct = pasThrTCon.y + TAppCon "Refrigerant condensation temperature"; Modelica.Units.SI.Temperature TEvaAct = pasThrTEva.y - TAppEva "Refrigerant evaporation temperature"; Modelica.Units.SI.TemperatureDifference TAppCon = if use_constAppTem then TAppCon_nominal else TAppCon_nominal * QCon_flow_internal/QCon_flow_nominal "Condenser approach temperature"; Modelica.Units.SI.TemperatureDifference TAppEva = if use_constAppTem then TAppEva_nominal else TAppEva_nominal * QEva_flow_internal/QEva_flow_nominal "Evaporator approach temperature "; Modelica.Blocks.Sources.Constant constZer(final k=0) "Constant zero value if off"; Modelica.Blocks.Logical.Switch swiPEle "If device is off, no heat exchange occurs"; Modelica.Blocks.Logical.Switch swiQUse "If device is off, no heat exchange occurs"; Modelica.Blocks.Routing.RealPassThrough pasThrTEva "Evaporator outlet pass through"; Modelica.Blocks.Routing.RealPassThrough pasThrTCon "Condenser outlet pass through"; Modelica.Blocks.Sources.RealExpression reaCarnotEff(y=etaCarnot_nominal) "Internal calculation of Carnot effectiveness"; protected parameter Modelica.Units.SI.HeatFlowRate QCon_flow_nominal "Nominal condenser heat flow rate"; parameter Modelica.Units.SI.HeatFlowRate QEva_flow_nominal "Nominal evaporator heat flow rate"; Modelica.Units.SI.HeatFlowRate QCon_flow_internal = if useForChi then swiPEle.y - swiQUse.y else swiQUse.y "Condenser heat flow rate"; Modelica.Units.SI.HeatFlowRate QEva_flow_internal = if useForChi then swiQUse.y else swiPEle.y - swiQUse.y "Evaporator heat flow rate"; equation connect(proPEle.u1, constPEle.y); connect(pasThrYMea.y, proPEle.u2); connect(constZer.y, swiPEle.u3); connect(proPEle.y, swiPEle.u1); connect(proQUse_flow.y, swiQUse.u1); connect(swiQUse.u3, constZer.y); connect(reaCarnotCOP.y, proQUse_flow.u[1]); connect(proPEle.y, proQUse_flow.u[2]); connect(reaCarnotEff.y, proQUse_flow.u[3]); end PartialCarnot;

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle

Partial model to allow selection of only heat pump options

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle

Information

Partial refrigerant cycle model for heat pumps. It adds the specification for frosting calculation and restricts to the intended choices under choicesAllMatching.

Extends from PartialRefrigerantCycle (Partial model of refrigerant cycle).

Parameters

TypeNameDefaultDescription
StringdevIde""Indicates the data source, used to warn users about different vapor compression devices in reversible models
BooleanuseInHeaPum =false to indicate that this model is used in a chiller
Nominal condition
PowerPEle_nominal Nominal electrical power consumption [W]
TemperatureTCon_nominal Nominal temperature at secondary condenser side [K]
TemperatureTEva_nominal Nominal temperature at secondary evaporator side [K]
HeatFlowRateQHea_flow_nominal Nominal heating capacity [W]
Frosting supression
NoFrostingiceFacCalredeclare Buildings.Fluid.He...Replaceable model to calculate the icing factor
Advanced
Medium properties
SpecificHeatCapacitycpCon Evaporator medium specific heat capacity [J/(kg.K)]
SpecificHeatCapacitycpEva Evaporator medium specific heat capacity [J/(kg.K)]

Connectors

TypeNameDescription
output RealOutputPEleElectrical Power consumed by the device [W]
output RealOutputQCon_flowHeat flow rate through condenser [W]
RefrigerantMachineControlBussigBusBus-connector
output RealOutputQEva_flowHeat flow rate through evaporator [W]

Modelica definition

partial model PartialHeatPumpCycle "Partial model to allow selection of only heat pump options" extends PartialRefrigerantCycle; parameter Modelica.Units.SI.HeatFlowRate QHea_flow_nominal "Nominal heating capacity"; parameter Boolean useInHeaPum "=false to indicate that this model is used in a chiller"; Modelica.Blocks.Math.Feedback feeHeaFloEva "Calculates evaporator heat flow with total energy balance"; equation connect(iceFacCal.iceFac, sigBus.iceFacHPMea); connect(feeHeaFloEva.y, proRedQEva.u2); end PartialHeatPumpCycle;

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialRefrigerantCycle Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialRefrigerantCycle

Partial model of refrigerant cycle

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialRefrigerantCycle

Information

Partial model for calculation of electrical power consumption PEle, condenser heat flow QCon_flow and evaporator heat flow QEva_flow based on the values in the sigBus for a refrigerant machine.

Frosting performance

To simulate possible icing of the evaporator on air-source devices, the icing factor iceFac is used to influence the outputs. The factor models the reduction of heat transfer between refrigerant and source. Thus, the factor is implemented as follows:

QEva_flow = iceFac * (QConNoIce_flow - PEle)

With iceFac as a relative value between 0 and 1:

iceFac = kA/kA_noIce

Finally, the energy balance must still hold:

QCon_flow = PEle + QEva_flow

You can select different options for the modeling of the icing factor or implement your own approach.

Parameters

TypeNameDefaultDescription
StringdevIde""Indicates the data source, used to warn users about different vapor compression devices in reversible models
Nominal condition
PowerPEle_nominal Nominal electrical power consumption [W]
TemperatureTCon_nominal Nominal temperature at secondary condenser side [K]
TemperatureTEva_nominal Nominal temperature at secondary evaporator side [K]
Frosting supression
NoFrostingiceFacCalredeclare Buildings.Fluid.He...Replaceable model to calculate the icing factor
Advanced
Medium properties
SpecificHeatCapacitycpCon Evaporator medium specific heat capacity [J/(kg.K)]
SpecificHeatCapacitycpEva Evaporator medium specific heat capacity [J/(kg.K)]

Connectors

TypeNameDescription
output RealOutputPEleElectrical Power consumed by the device [W]
output RealOutputQCon_flowHeat flow rate through condenser [W]
RefrigerantMachineControlBussigBusBus-connector
output RealOutputQEva_flowHeat flow rate through evaporator [W]

Modelica definition

partial model PartialRefrigerantCycle "Partial model of refrigerant cycle" parameter Modelica.Units.SI.Power PEle_nominal "Nominal electrical power consumption"; parameter Modelica.Units.SI.Temperature TCon_nominal "Nominal temperature at secondary condenser side"; parameter Modelica.Units.SI.Temperature TEva_nominal "Nominal temperature at secondary evaporator side"; parameter String devIde="" "Indicates the data source, used to warn users about different vapor compression devices in reversible models"; replaceable Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.NoFrosting iceFacCal constrainedby Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.BaseClasses.PartialIcingFactor(final cpEva=cpEva) "Replaceable model to calculate the icing factor"; parameter Modelica.Units.SI.SpecificHeatCapacity cpCon "Evaporator medium specific heat capacity"; parameter Modelica.Units.SI.SpecificHeatCapacity cpEva "Evaporator medium specific heat capacity"; Modelica.Blocks.Interfaces.RealOutput PEle(final unit="W") "Electrical Power consumed by the device"; Modelica.Blocks.Interfaces.RealOutput QCon_flow(final unit="W") "Heat flow rate through condenser"; Buildings.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus sigBus "Bus-connector"; Modelica.Blocks.Interfaces.RealOutput QEva_flow(final unit="W") "Heat flow rate through evaporator"; Modelica.Blocks.Math.Add redQCon(final k1=-1, final k2=+1) "Reduce heat flow to the condenser based on the reduction to the evaporator"; Modelica.Blocks.Math.Product proRedQEva "Reduce heat flow to the evaporator based on the icing factor"; equation connect(proRedQEva.y, QEva_flow); connect(proRedQEva.y, redQCon.u1); connect(redQCon.y, QCon_flow); connect(iceFacCal.iceFac, proRedQEva.u1); connect(iceFacCal.sigBus, sigBus); end PartialRefrigerantCycle;

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialTableData2D

Partial model with components for TableData2D approach for heat pumps and chillers

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialTableData2D

Information

Partial model for equations and componenents used in both heat pump and chiller models using two-dimensional data.

Parameters

TypeNameDefaultDescription
RealscaFac Scaling factor
Booleanuse_TEvaOutForTabtrueif true, use evaporator outlet temperature, otherwise use inlet
Booleanuse_TConOutForTabtrueif true, use condenser outlet temperature, otherwise use inlet
Nominal condition
MassFlowRatemCon_flow_nominal Nominal mass flow rate in secondary condenser side [kg/s]
MassFlowRatemEva_flow_nominal Nominal mass flow rate in secondary evaporator side [kg/s]
Data handling
SmoothnesssmoothnessModelica.Blocks.Types.Smooth...Smoothness of table interpolation
ExtrapolationextrapolationModelica.Blocks.Types.Extrap...Extrapolation of data outside the definition range

Modelica definition

partial model PartialTableData2D "Partial model with components for TableData2D approach for heat pumps and chillers" parameter Real scaFac "Scaling factor"; parameter Modelica.Units.SI.MassFlowRate mCon_flow_nominal "Nominal mass flow rate in secondary condenser side"; parameter Modelica.Units.SI.MassFlowRate mEva_flow_nominal "Nominal mass flow rate in secondary evaporator side"; parameter Modelica.Blocks.Types.Smoothness smoothness= Modelica.Blocks.Types.Smoothness.LinearSegments "Smoothness of table interpolation"; parameter Modelica.Blocks.Types.Extrapolation extrapolation= Modelica.Blocks.Types.Extrapolation.LastTwoPoints "Extrapolation of data outside the definition range"; parameter Boolean use_TEvaOutForTab=true "if true, use evaporator outlet temperature, otherwise use inlet"; parameter Boolean use_TConOutForTab=true "if true, use condenser outlet temperature, otherwise use inlet"; Modelica.Blocks.Tables.CombiTable2Ds tabPEle( final tableOnFile=false, final tableName="NoName", final fileName="NoName", final verboseRead=true, final smoothness=smoothness, final u1(unit="K", displayUnit="degC"), final u2(unit="K", displayUnit="degC"), final y(unit="W"), final extrapolation=extrapolation) "Electrical power consumption table"; Modelica.Blocks.Tables.CombiTable2Ds tabQUse_flow( final tableOnFile=false, final tableName="NoName", final fileName="NoName", final verboseRead=true, final smoothness=smoothness, final u1(unit="K", displayUnit="degC"), final u2(unit="K", displayUnit="degC"), final y(unit="W"), final extrapolation=extrapolation) "Table for useful heat flow rate"; Modelica.Blocks.Math.Product yMeaTimScaFac "Create the product of the scaling factor and relative compressor speed"; Modelica.Blocks.Math.Product scaFacTimPel "Scale electrical power consumption"; Modelica.Blocks.Math.Product scaFacTimQUse_flow "Scale useful heat flow rate"; Modelica.Blocks.Sources.Constant constScaFac(final k=scaFac) "Calculates correction of table output based on scaling factor"; Modelica.Blocks.Routing.RealPassThrough reaPasThrTEvaIn if (not useInRevDev and not use_TEvaOutForTab) or (useInRevDev and not use_TConOutForTab) "Used to enable conditional bus connection"; Modelica.Blocks.Routing.RealPassThrough reaPasThrTConIn if (not useInRevDev and not use_TConOutForTab) or (useInRevDev and not use_TEvaOutForTab) "Used to enable conditional bus connection"; Modelica.Blocks.Routing.RealPassThrough reaPasThrTEvaOut if (not useInRevDev and use_TEvaOutForTab) or (useInRevDev and use_TConOutForTab) "Used to enable conditional bus connection"; Modelica.Blocks.Routing.RealPassThrough reaPasThrTConOut if (not useInRevDev and use_TConOutForTab) or (useInRevDev and use_TEvaOutForTab) "Used to enable conditional bus connection"; protected parameter Boolean useInRevDev "=true to indicate usage in reversed operation"; parameter Integer numRow=size(tabPEle.table, 1) "Number of rows in table"; parameter Integer numCol=size(tabPEle.table, 2) "Number of columns in table"; parameter Modelica.Units.SI.TemperatureDifference dTMin=3 "Minimal temperature spread according to EN 14511"; parameter Modelica.Units.SI.TemperatureDifference dTMax=10 "Maximal temperature spread according to EN 14511"; parameter Modelica.Units.SI.Power valTabPEle[numRow-1, numCol - 1]= {{tabPEle.table[j, i] for i in 2:numCol} for j in 2:numRow} "Table with electrical power values only"; parameter Modelica.Units.SI.HeatFlowRate valTabQCon_flow[numRow-1, numCol - 1] "Table with condenser heat flow values only"; parameter Modelica.Units.SI.HeatFlowRate valTabQEva_flow[numRow-1, numCol - 1] "Table with evaporator heat flow values only"; parameter Modelica.Blocks.Types.ExternalCombiTable2D tabIdeQUse_flow= Modelica.Blocks.Types.ExternalCombiTable2D( "NoName", "NoName", tabQUse_flow.table, smoothness, extrapolation, false) "External table object for nominal useful side conditions"; parameter Modelica.Blocks.Types.ExternalCombiTable2D tabIdePEle= Modelica.Blocks.Types.ExternalCombiTable2D( "NoName", "NoName", tabPEle.table, smoothness, extrapolation, false) "External table object for nominal electrical power consumption"; parameter Modelica.Units.SI.MassFlowRate mEva_flow_min "Minimal evaporator mass flow rate"; parameter Modelica.Units.SI.MassFlowRate mEva_flow_max "Maximal evaporator mass flow rate"; parameter Modelica.Units.SI.MassFlowRate mCon_flow_min "Minimal evaporator mass flow rate"; parameter Modelica.Units.SI.MassFlowRate mCon_flow_max "Maximal evaporator mass flow rate"; initial algorithm assert(mCon_flow_nominal >= mCon_flow_min, "In " + getInstanceName() + ": The nominal condenser mass flow rate (" + String(mCon_flow_nominal) + " kg/s) is smaller than the minimal value (" + String(mCon_flow_min) + " kg/s) for the table data when assuming a temperature spread between 3 and 10 K, as in EN 14511.", AssertionLevel.warning); assert(mCon_flow_nominal <= mCon_flow_max, "In " + getInstanceName() + ": The nominal condenser mass flow rate (" + String(mCon_flow_nominal) + " kg/s) is bigger than the maximal value (" + String(mCon_flow_max) + " kg/s) for the table data when assuming a temperature spread between 3 and 10 K, as in EN 14511.", AssertionLevel.warning); assert(mEva_flow_nominal >= mEva_flow_min, "In " + getInstanceName() + ": The nominal evaporator mass flow rate (" + String(mEva_flow_nominal) + " kg/s) is smaller than the minimal value (" + String(mEva_flow_min) + " kg/s) for the table data when assuming a temperature spread between 3 and 10 K, as in EN 14511.", AssertionLevel.warning); assert(mEva_flow_nominal <= mEva_flow_max, "In " + getInstanceName() + ": The nominal evaporator mass flow rate (" + String(mEva_flow_nominal) + " kg/s) is bigger than the maximal value (" + String(mEva_flow_max) + " kg/s) for the table data when assuming a temperature spread between 3 and 10 K, as in EN 14511.", AssertionLevel.warning); equation connect(constScaFac.y,yMeaTimScaFac. u2); connect(scaFacTimPel.u2,yMeaTimScaFac. y); connect(tabQUse_flow.y, scaFacTimQUse_flow.u1); connect(scaFacTimQUse_flow.u2,yMeaTimScaFac. y); connect(tabPEle.y, scaFacTimPel.u1); end PartialTableData2D;

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep

Calculation of capacity, heat flow rate and power based on load-dependent 2D table data

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep

Information

This block implements two core features for some chiller and heat pump models within Buildings.Fluid.HeatPumps.ModularReversible and Buildings.Fluid.Chillers.ModularReversible.

1 The part load ratio is defined as the ratio of the actual heating (or cooling) heat flow rate to the maximum capacity of the heat pump (or chiller) at the given load-side and ambient-side fluid temperatures. It is dimensionless and bounded by 0 and max(PLRSup), where the upper bound is typically equal to 1 (unless there are some capacity margins at design conditions that need to be accounted for). In this block, the part load ratio is used as a proxy variable for the actual capacity modulation observable. For systems with VFDs, this is the compressor speed. For systems with on/off compressors, this is the capacity of the enabled compressors divided by the total capacity. When meeting the load by cycling on and off a single compressor, this is the time fraction the compressor is enabled. In all cases, the algorithm assumes continuous operation and only approximates the performance on a time average. Finally, note that while the part load ratio is used for generalization purposes, either the part load ratio or the actual capacity modulation observable (e.g., the normalized compressor speed) may be used to map the performance data. The only requirement is that this variable be normalized, as the algorithm assumes it equals 1 at design (selection) conditions.

2 The reason why the part load ratio is both calculated (PLR) and exposed as an input (yMea) is to allow for modeling internal safeties that can limit operation. If no safeties are modeled, a direct feedback of PLR to yMea can be used.

Capacity and power calculation

When the machine is enabled (input signal on is true) the capacity and power are calculated by partitioning the PLR values into three domains, as illustrated in Figure 1.

  1. Normal capacity modulation
    This domain corresponds to the capacity range where the machine adapts to the load without false loading or cycling on and off the last operating compressor. Depending on the technology, this is achieved for example by modulating the compressor speed, throttling the inlet guide vanes or staging a varying number of compressors. In this domain, both the machine PLR and the compressor PLR vary. The capacity and power are linearly interpolated based on the performance data provided in an external file, which syntax is specified in the following section. The interpolation is carried out along three variables: the load-side fluid temperature, the ambient-side fluid temperature and the part load ratio. Note that no extrapolation is performed. The capacity and power are limited by the minimum or maximum values provided in the performance data file.
  2. Compressor false loading
    This domain corresponds to the capacity range where the machine adapts to the load by false loading the compressor. For a chiller, this is achieved by bypassing hot gas directly to the evaporator. In this domain, the machine PLR varies while the compressor PLR stays roughly the same. The input power is considered equal to the interpolated value at TLoa, TAmb, min(PLRSup). This domain may not exist if the parameter PLRCyc_min is equal to min(PLRSup), which is the default setting.
  3. Last operating compressor cycling
    This domain corresponds to the capacity range where the last operating compressor cycles on and off. In this domain, the capacity is linearly interpolated between 0 and the value at TLoa, TAmb, min(PLRSup), while the power is linearly interpolated between P_min and the value at TLoa, TAmb, min(PLRSup), where P_min corresponds to the remaining power when the machine is enabled and all compressors are disabled.

Input power as a function of the part load ratio.

Figure 1. Input power as a function of the part load ratio.

Performance data file

The performance data are read from an external ASCII file that must meet the requirements specified in the documentation of Modelica.Blocks.Tables.CombiTable2Ds.

In addition, this file must contain at least two 2D-tables that provide the maximum heating (resp. minimum cooling) heat flow rate and the input power of the heat pump (resp. chiller) at 100 % PLR. Each row of these tables corresponds to a value of the load-side fluid temperature, each column corresponds to a value of the ambient-side fluid temperature. This could be either the leaving temperature if use_T*OutForTab is true, or the entering temperature if use_T*OutForTab is false. The load and ambient temperatures must cover the whole operating domain, knowing that the model only performs interpolation and no extrapolation of the capacity and power along these variables.

The table providing the capacity values must be named q@X.XX where X.XX is the PLR value formatted with exactly 2 decimal places ("%.2f"). Similarly, the table providing the power values must be named p@X.XX.

Here is an example of chiller data ("-----" is not part of the file content):

-----------------------------------------------------
#1
double q@1.00(5,5)                    # Cooling heat flow rate at 100 % PLR
0 292.0 297.4 302.8 308.2             # CW temperatures as column headers
280.4 -493241 -555900 -495611 -312372 # Each row provides the capacity at a given CHW temperature
282.2 -470560 -578165 -562822 -424529
284.1 -418413 -573462 -605561 -514711
285.9 -342290 -542284 -619329 -573426
double p@1.00(5, 5)                   # Input power at 100 % PLR
0 292.0 297.4 302.8 308.2             # CW temperatures as column headers
280.4 60430 80413 80830 55530         # Each row provides the input power at a given CHW temperature
282.2 54399 80278 89151 73950
284.1 45251 76017 92822 87633
285.9 34546 68567 91833 95401
-----------------------------------------------------

In addition, for machines that have capacity modulation other than cycling on and off a single compressor, the whole range of normal capacity modulation must be covered by providing similar 2D-tables at different PLR values. The lowest PLR value will be considered as the minimum PLR value before false loading the compressor. If the machine has no hot gas bypass (PLRCyc_min = min(PLRSup)) this will correspond to the minimum PLR value before cycling the last operating compressor.

All the PLR values used in the performance data file must be specified in the array parameter PLRSup[:].

Compressor cycling

Compressor cycling is not explicitly modeled. Instead, the model assumes continuous operation from 0 to max(PLRSup). The only effect of cycling taken into account is the impact of the remaining power P_min when the machine is enabled and the last operating compressor is cycled off. Studies on chillers and heat pumps show that this is the main driver of efficiency loss due to cycling (Rivière, 2004). When a compressor is staged on, energy losses occur due to the overcoming of the refrigerant pressure equalization and the heat exchanger temperature conditioning. However, a large part of these losses is recovered when staging off the compressor, unless the machine is disconnected from the load when compressors are disabled. This disconnection does not happen when staging multiple compressors, and the research shows no significant performance degradation when a chiller cycles between different stages without completely shutting down. And even when disabling the last operating compressor, most plant controls require continuous operation of the primary pumps when the chillers or heat pumps are enabled. The European Standard for performance rating of chillers and heat pumps at part load conditions (CEN, 2022) states that the performance degradation due to the pressure equalization effect when the unit restarts can be considered as negligible for hydronic systems. The only effect that will impact the coefficient of performance when cycling is the remaining power input when the compressor is switching off. If this remaining power is not measured, the Standard prescribes a default value of 10 % of the effective power input measured during continuous operation at part load.

Heat recovery chillers

Heat recovery chillers can be modeled with this block. In this case, the same chiller performance data file is used for both cooling and heating operation. The model assumes that all dissipated heat from the compressor is recovered by the refrigerant. This assumption enables computing the heating capacity as the sum of the cooling capacity and the input power.

When configured to represent a heat recovery chiller, this block uses an additional input connector coo which must be true when cooling mode is enabled, and false when heating mode is enabled. The load side input variables must externally be connected to the evaporator side variables in cooling mode, and to the condenser side variables in heating mode. The output connector Q_flow is always the cooling heat flow rate, whatever the operating mode. The heating heat flow rate in heating mode can be computed externally as P-Q_flow.

Ideal controls

The block implements ideal controls by solving for the part load ratio required to meet the load (more precisely the minimum between the load and the actual capacity for the current load and ambient temperatures). This is done by interpolating the PLR values along the heat flow rate values for a given load.

The load is calculated based on the load side variables and the temperature setpoint provided as inputs. The setpoint either represents a leaving (supply) temperature setpoint if use_TLoaLvgForCtl is true (default setting) or the entering (return) temperature if use_TLoaLvgForCtl is false.

The required PLR value is returned as an output while the actual heat flow rate and power are calculated using the PLR value yMea provided as input, which allows limiting the required PLR to account for equipment internal safeties.

References

Extends from Modelica.Blocks.Icons.Block (Basic graphical layout of input/output block).

Parameters

TypeNameDefaultDescription
Integertyp  
Booleanuse_TLoaLvgForCtltrueSet to true for leaving temperature control, false for entering temperature control
Booleanuse_TEvaOutForTab =true to use evaporator outlet temperature for table data, false for inlet
Booleanuse_TConOutForTab =true to use condenser outlet temperature for table data, false for inlet
RealscaFac1Scaling factor for interpolated heat flow rate and power [1]
DimensionlessRatioPLRSup[:] PLR values at which heat flow rate and power data are provided [1]
DimensionlessRatioPLRCyc_minmin(PLRSup)Minimum PLR before cycling off the last compressor [1]
PowerP_min0Minimum power when system is enabled with compressor cycled off [W]
StringfileName File where performance data are stored
Nominal condition
TemperatureTLoa_nominal Load side fluid temperature — Entering or leaving depending on use_T*OutForTab [K]
TemperatureTAmb_nominal Ambient side fluid temperature — Entering or leaving depending on use_T*OutForTab [K]
Advanced
StringtabNamQ[nPLR]{"q@" + String(p, format=".2...Table names with heat flow rate data
StringtabNamP[nPLR]{"p@" + String(p, format=".2...Table names with power data

Connectors

TypeNameDescription
input BooleanInputonSet to true to enable compressor, or false to disable compressor
input BooleanInputcooSwitchover signal: true for cooling, false for heating
input RealInputTLoaEntEntering fluid temperature on load side [K]
input RealInputTAmbEntEntering fluid temperature on ambient side [K]
input RealInputTSetTemperature setpoint [K]
input RealInputTLoaLvgLeaving fluid temperature on load side [K]
input RealInputmLoa_flowFluid mass flow rate on load side [kg/s]
output RealOutputPInput power [W]
output RealOutputPLRCompressor part load ratio to meet the load (within capacity) [1]
output RealOutputQ_flowHeat flow rate [J/s]
input RealInputTAmbLvgLeaving fluid temperature on ambient side [K]
input RealInputyMeaCapacity limiting signal yielded by internal safety logic [1]
input RealInputcpLoaSpecific heat capacity of fluid on load side [J/(kg.K)]

Modelica definition

block TableData2DLoadDep "Calculation of capacity, heat flow rate and power based on load-dependent 2D table data" extends Modelica.Blocks.Icons.Block; type TypeOfSystem = Integer(final min = 1, final max = 3); parameter TypeOfSystem typ; parameter Boolean use_TLoaLvgForCtl=true "Set to true for leaving temperature control, false for entering temperature control"; parameter Boolean use_TEvaOutForTab "=true to use evaporator outlet temperature for table data, false for inlet"; parameter Boolean use_TConOutForTab "=true to use condenser outlet temperature for table data, false for inlet"; parameter Real scaFac(unit="1")=1 "Scaling factor for interpolated heat flow rate and power"; parameter Modelica.Units.SI.DimensionlessRatio PLRSup[:](each final min=0) "PLR values at which heat flow rate and power data are provided"; final parameter Modelica.Units.SI.DimensionlessRatio PLRUnl_min=min(PLRSup) "Minimum PLR before false loading the compressor"; parameter Modelica.Units.SI.DimensionlessRatio PLRCyc_min( final max=PLRUnl_min, final min=0)=min(PLRSup) "Minimum PLR before cycling off the last compressor"; parameter Modelica.Units.SI.Power P_min(final min=0)=0 "Minimum power when system is enabled with compressor cycled off"; final parameter Integer nPLR=size(PLRSup, 1) "Number of PLR support points"; parameter String fileName "File where performance data are stored"; final parameter Modelica.Blocks.Types.Smoothness smoothness= Modelica.Blocks.Types.Smoothness.LinearSegments "Smoothness of table interpolation"; final parameter Modelica.Blocks.Types.Extrapolation extrapolation= Modelica.Blocks.Types.Extrapolation.HoldLastPoint "Extrapolation of data outside the definition range"; parameter String tabNamQ[nPLR]={"q@" + String(p, format=".2f") for p in PLRSor} "Table names with heat flow rate data"; parameter String tabNamP[nPLR]={"p@" + String(p, format=".2f") for p in PLRSor} "Table names with power data"; parameter Modelica.Units.SI.Temperature TLoa_nominal "Load side fluid temperature — Entering or leaving depending on use_T*OutForTab"; parameter Modelica.Units.SI.Temperature TAmb_nominal "Ambient side fluid temperature — Entering or leaving depending on use_T*OutForTab"; // OMC and OCT require getTable2DValueNoDer2() to be called in initial equation section. // Binding equations yield incorrect results but no error! final parameter Modelica.Units.SI.Power PInt_nominal[nPLR](each fixed=false) "Power interpolated at nominal conditions, at each PLR – Unscaled"; final parameter Modelica.Units.SI.HeatFlowRate QInt_flow_nominal[nPLR](each fixed=false) "Heat flow rate interpolated at nominal conditions, at each PLR – Unscaled"; final parameter Modelica.Units.SI.Power P_nominal= Modelica.Math.Vectors.interpolate(PLRSor, PInt_nominal, 1) "Power interpolated at nominal conditions, at PLR=1 – Unscaled"; final parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal= Modelica.Math.Vectors.interpolate(PLRSor, QInt_flow_nominal, 1) "Heat flow rate interpolated at nominal conditions, at PLR=1 – Unscaled"; Buildings.Controls.OBC.CDL.Interfaces.BooleanInput on "Set to true to enable compressor, or false to disable compressor"; Buildings.Controls.OBC.CDL.Interfaces.BooleanInput coo if typ==2 "Switchover signal: true for cooling, false for heating"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TLoaEnt( final unit="K", displayUnit="degC") "Entering fluid temperature on load side"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TAmbEnt( final unit="K", displayUnit="degC") "Entering fluid temperature on ambient side"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TSet( final unit="K", displayUnit="degC") "Temperature setpoint"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TLoaLvg( final unit="K", displayUnit="degC") "Leaving fluid temperature on load side"; Buildings.Controls.OBC.CDL.Interfaces.RealInput mLoa_flow( final unit="kg/s") "Fluid mass flow rate on load side"; Buildings.Controls.OBC.CDL.Interfaces.RealOutput P( final unit="W") "Input power"; Buildings.Controls.OBC.CDL.Interfaces.RealOutput PLR( final unit="1") "Compressor part load ratio to meet the load (within capacity)"; Buildings.Controls.OBC.CDL.Interfaces.RealOutput Q_flow( final unit="J/s") "Heat flow rate"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TAmbLvg( final unit="K", displayUnit="degC") "Leaving fluid temperature on ambient side"; Buildings.Controls.OBC.CDL.Interfaces.RealInput yMea( final unit="1") "Capacity limiting signal yielded by internal safety logic"; Buildings.Controls.OBC.CDL.Interfaces.RealInput cpLoa( final unit="J/(kg.K)") "Specific heat capacity of fluid on load side"; protected final parameter Real PLRSor[:]=Modelica.Math.Vectors.sort(PLRSup) "PLR values in increasing order"; final parameter Real PLRSorWith0[:]=cat(1, {0}, PLRSor) "PLR values in increasing order"; final parameter Real PLR_max=PLRSor[nPLR] "Maximum PLR"; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabQ[nPLR]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamQ, fileName=fill(fileName, nPLR), table=fill(fill(0.0, 1, 2), nPLR), smoothness=fill(smoothness, nPLR), extrapolation=fill(extrapolation, nPLR), verboseRead=fill(false, nPLR)) "External table objects for heat flow interpolation"; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabP[nPLR]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamP, fileName=fill(fileName, nPLR), table=fill(fill(0.0, 1, 2), nPLR), smoothness=fill(smoothness, nPLR), extrapolation=fill(extrapolation, nPLR), verboseRead=fill(false, nPLR)) "External table objects for power interpolation"; Modelica.Units.SI.HeatFlowRate QSet_flow "Cooling or heating load"; Modelica.Units.SI.HeatFlowRate QSwiSet_flow( start=0, final min=0) "Heating load – Used for HRC in heating mode"; Modelica.Units.SI.HeatFlowRate QInt_flow[nPLR] "Capacity at PLR support points"; Modelica.Units.SI.Power PInt[nPLR] "Input power at PLR support points"; Real PLR1( start=0, final min=0, final max=PLR_max) "Machine part load ratio"; // For HRC the same data table is used for both cooling and heating, // and the block is necessarily used with chiller data. // So the first interpolation variable TLoaTab is always the evaporator temperature, // which is either TLoa(Ent|Lvg) or TAmb(Ent|Lvg) depending on the operating mode. Modelica.Units.SI.Temperature TLoaTab=if typ==2 then (if coo_internal then (if use_TEvaOutForTab then TLoaLvg else TLoaEnt) else (if use_TEvaOutForTab then TAmbLvg else TAmbEnt)) elseif typ==1 then (if use_TEvaOutForTab then TLoaLvg else TLoaEnt) else (if use_TConOutForTab then TLoaLvg else TLoaEnt) "Fluid temperature on load side used for table data interpolation"; Modelica.Units.SI.Temperature TAmbTab=if typ == 2 then (if coo_internal then ( if use_TConOutForTab then TAmbLvg else TAmbEnt) else (if use_TConOutForTab then TLoaLvg else TLoaEnt)) elseif typ == 1 then (if use_TConOutForTab then TAmbLvg else TAmbEnt) else (if use_TEvaOutForTab then TAmbLvg else TAmbEnt) "Fluid temperature on ambient side used for table data interpolation"; Modelica.Units.SI.Temperature TLoaCtl=if use_TLoaLvgForCtl then TLoaEnt else TLoaLvg "Fluid temperature used for load calculation (Delta-T with setpoint)"; Real sigLoa=if use_TLoaLvgForCtl then 1 else - 1 "Sign of Delta-T used for load calculation"; Buildings.Controls.OBC.CDL.Interfaces.BooleanInput coo_internal; initial equation PInt_nominal = Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabP, fill(TLoa_nominal, nPLR), fill(TAmb_nominal, nPLR)); QInt_flow_nominal = Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQ, fill(TLoa_nominal, nPLR), fill(TAmb_nominal, nPLR)); equation if typ==2 then connect(coo, coo_internal); else if typ==1 then coo_internal=true; else coo_internal=false; end if; end if; if on then QSet_flow=if typ==2 and (not coo_internal) then P - QSwiSet_flow elseif typ==1 or typ==2 and coo_internal then min(0, sigLoa *(TSet - TLoaCtl) * cpLoa * mLoa_flow) else max(0, sigLoa *(TSet - TLoaCtl) * cpLoa * mLoa_flow); QSwiSet_flow=if typ==2 and (not coo_internal) then max(0, sigLoa *(TSet - TLoaCtl) * cpLoa * mLoa_flow) else 0; QInt_flow=scaFac * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQ, fill(TLoaTab, nPLR), fill(TAmbTab, nPLR)); PLR1=min(PLR_max, Modelica.Math.Vectors.interpolate( abs(cat(1, {0}, QInt_flow)), cat(1, {0}, PLRSor), abs(QSet_flow))); PLR=if PLR1 < PLRUnl_min and PLR1 > PLRCyc_min then PLRUnl_min else PLR1; // Actual input and output accounting for equipement internal safeties Q_flow=Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRSor), cat(1, {0}, QInt_flow), yMea); PInt=scaFac * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabP, fill(TLoaTab, nPLR), fill(TAmbTab, nPLR)); P=if PLRCyc_min < PLRSor[1] then Modelica.Math.Vectors.interpolate( cat(1, {0, PLRCyc_min}, PLRSor), cat(1, {P_min, PInt[1]}, PInt), yMea) else Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRSor), cat(1, {P_min}, PInt), yMea); else QSet_flow=0; QSwiSet_flow=0; QInt_flow=fill(0, nPLR); PLR1=0; PLR=0; Q_flow=0; PInt=fill(0, nPLR); P=0; end if; end TableData2DLoadDep;

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDepSHC Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDepSHC

Modeling block for simultaneous heating and cooling systems based on load-dependent 2D table data

Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDepSHC

Information

This block provides the core implementation to model simultaneous heating and cooling (SHC) systems, also referred to as multipipe polyvalent units or "Type A" in Eurovent (2025). Since the most recent versions of these systems are composed of multiple modules, the implementation includes the staging logic for an arbitrary number of modules nUni. Nevertheless, single-module systems can also be appropriately represented by setting nUni = 1.

All kinds of capacity-modulation processes are supported, such as VFD-driven compressors, multiple on-off compressors, and single compressor cycling. The method used to interpolate capacity and power based on user-provided data is taken from Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep. Users should be familiar with this latter block before continuing with this documentation.

The block implements the following functionalities:

System and module operating mode

The block inputs onHea and onCoo allow switching between three system operating modes.

  1. Heating-only: In this mode, all modules operate as heat pumps, tracking the HW temperature setpoint and sourcing heat from the ambient-side fluid.
  2. Cooling-only: In this mode, all modules operate as chillers, tracking the CHW temperature setpoint and rejecting heat to the ambient-side fluid.
  3. Simultaneous heating and cooling: In this mode, some modules operate as heat recovery chillers, sourcing heat from the CHW circuit and rejecting heat to the HW circuit. The system load balancing logic (see Section "Load balancing between the HW and CHW side") ensures that, on average, both the HW temperature setpoint and the CHW temperature setpoint are met by these modules. Additional modules may concurrently run in heating-only or cooling-only mode to match the residual load. In the extreme case where the system is only exposed to heating (resp. cooling) loads, all modules will run in heating-only (resp. cooling-only) mode.

Ideal controls

For each module operating mode, the block implements ideal controls by solving for the part load ratio required to meet the load (more precisely the minimum between the load and the actual capacity for the current source and sink temperatures). This is done by interpolating the PLR values along the heat flow rate values for a given load. As described in Section "Load balancing between the HW and CHW side", the part load ratio for modules in SHC mode is the maximum between the PLR values for the heating load and the cooling load.

The load is calculated based on the HW and CHW-side side variables and the temperature setpoint provided as inputs. The setpoint either represents a leaving (supply) temperature setpoint if use_TLoaLvgForCtl is true (default setting) or the entering (return) temperature if use_TLoaLvgForCtl is false.

In contrast to the implementation in Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep the current block does not expose the PLR value, and therefore does not support external modeling of equipment safeties.

Capacity and power calculation

The capacity and power calculation follows the same logic as the one described in Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep except that the current block does not include compressor false loading, i.e., both the capacity and power are linearly interpolated along PLR between 0 and min(PLR<Shc|Hea|Coo>Sup).

Performance data file and scaling

The performance data are read from external ASCII files that must meet the requirements specified in the documentation of Buildings.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.TableData2DLoadDep.

A performance data file must be provided for each module operating mode: heating-only, cooling-only and simultaneous heating and cooling. It is expected that performance data be provided for a single module. This is however a loose requirement as the scaling logic anyway ensures that the nominal heat flow rates Q*_flow_nominal provided as parameters match the values interpolated from the performance data, times the number of modules.

Note that for single-mode performance data, the ambient-side fluid temperature must correspond to the entering temperature, while the model supports choosing between entering or leaving temperature for the CHW and HW via the parameters use_TEvaOutForTab and use_TConOutForTab, respectively.

Module staging

First, the heating and cooling loads QHeaSet_flow and QCooSet_flow are calculated from the block inputs (see Section "Ideal controls"). The staging logic uses time-averaged heating and cooling loads QHeaSetMea_flow and QCooSetMea_flow. An exponential moving average (dtMea * der(Q<Hea|Coo>SetMea_flow) = Q<Hea|Coo>Set_flow - Q<Hea|Coo>SetMea_flow) is used for computational efficiency.

Then the model evaluates the capacity of a single module in each mode, based on the current source and sink fluid temperature (see Section "Capacity and power calculation"). The number of modules required to run to meet the load is then calculated for each mode as follows.

Discrete-event logic is then used to calculate the actual number of modules in each mode (nUniShc, nUniHea and nUniCoo) considering the following requirements.

Load balancing between the HW and CHW side

A fundamental assumption for load balancing is that the system is composed of equally sized modules that are hydronically balanced, connected in parallel arrangement and controlled at the same setpoint. This implies that each module connected to the HW (resp. CHW) loop handles an equal fraction of the total heating (resp. cooling) load, irrespective of whether the module operates in SHC or single mode. This assumption is strictly true on the dominant side. However, as explained below, it is only partially true on the non-dominant side where setpoint deviations occur in the modules with excess thermal output and in the compensating module.

Based on this assumption, on the dominant side, the heating or cooling load of each module in SHC mode can be calculated as

Q<Hea|Coo>SetUniShc_flow = Q<Hea|Coo>Set_flow / (nUniShc + nUni<Hea|Coo>).

In order to achieve load balancing between the CHW and HW sides for the subset of modules in SHC mode, the model assumes that these modules are loaded for the most demanding side, and that a single module can cycle between SHC and the corresponding single-mode operation. For example, in case of 90 % cooling load and 60 % heating load, the module will cycle between SHC at 90 % PLR during 2/3 of the time and cooling-only at 90 % PLR during 1/3 of the time. This logic is inspired from the sequence of operation of a multipipe heat pump system (Johnson Controls, 2024) where the last enabled circuit cycles between SHC and single-mode operation to balance the heating and cooling loads.

The part load ratio of each module in SHC mode to satisfy the most demanding side is

PLRShcLoa = min(PLRShcSup, max(fHeaShc-1(QHeaSetUniShc_flow), fCooShc-1(QCooSetUniShc_flow))),

where f<Hea|Coo>Shc-1 is the linear interpolation of the part load ratio along the module heating or cooling capacity at the actual source and sink temperature, based on the performance data provided for SHC operation.

A demand limiting logic is implemented to prevent overcooling or overheating due to the stage minimum runtime requirement and the possible flow variations resulting from modulating the primary pump speed and/or the minimum flow bypass valve. This logic uses a temperature deviation from setpoint dTSaf, which is converted to limiting heat flow rates Q<Hea|Coo>Saf_flow. The limiting part load ratio is calculated as

PLRShcSaf = min(fHeaShc-1(QHeaSaf_flow / (nUniShc + nUniHea)), fCooShc-1(QCooSaf_flow / (nUniShc + nUniCoo))).

The effective part load ratio of each module in SHC mode is then the minimum of the load-based and safety-limited values

PLRShc = min(PLRShcLoa, PLRShcSaf).

The excess heating or cooling heat flow rate (non-dominant side) of the modules in SHC mode is then calculated as

Q<Hea|Coo>ShcExc_flow = nUniShc * (f<Hea|Coo>Shc(PLRShc) - Q<Hea|Coo>SetUniShc_flow),

where f<Hea|Coo>Shc is the linear interpolation of the module heating or cooling capacity along the part load ratio at the actual source and sink temperature, based on the performance data provided for SHC operation.

This excess heat flow rate is compensated by cycling a single module, which gives theg expression of the cycling ratio for this module as

ratCycShc = 1 - min(1, max(QHeaShcExc_flow / fHeaShc(PLRShc), QCooShcExc_flow / fCooShc(PLRShc))),

where ratCycShc = 1 means perfect balance, i.e., the module does not cycle and continuously runs in SHC mode, and ratCycShc = 0 means that the module continuously runs in single mode.

The actual heating or cooling heat flow rate of the modules in SHC mode is

Q<Hea|Coo>Shc_flow = (nUniShc - 1 + ratCycShc) * f<Hea|Coo>Shc(PLRShc).

The residual load that the module which cycles between SHC and single-mode must handle is

Q<Hea|Coo>SetUniShc_flow * nUniShc - Q<Hea|Coo>Shc_flow,

which gives the part load ratio PLR<Hea|Coo>ShcCyc of this module while it runs in single mode. The corresponding heating or cooling heat flow rate is then

Q<Hea|Coo>ShcCyc_flow = (1 - ratCycShc) * f<Hea|Coo>(PLR<Hea|Coo>ShcCyc),

where f<Hea|Coo> is the linear interpolation of the module capacity along the part load ratio at the actual source and sink temperature, based on the performance data provided for single-mode operation.

The residual heating or cooling loads that the modules running in single mode must meet can now be calculated as

Q<Hea|Coo>SetRes_flow = Q<Hea|Coo>Set_flow - (Q<Hea|Coo>Shc_flow + Q<Hea|Coo>ShcCyc_flow),

which ultimately allows calculating the PLR value of these modules and their contribution to the total heating and cooling output of the bank.

Implementation limitations

The load balancing logic relies on a subset of modules producing excess heat flow rate while another module compensates for it. The fundamental assumption of even load between modules therefore breaks down on the non-dominant side. In a real system where the modules are hydronically balanced, this load imbalance yields varying leaving temperatures across modules. In the worst case, the deviation from setpoint is ΔT⋅SPLR / 2, where ΔT is the design temperature difference.

These temperature discrepancies are neglected in the model which "numerically absorbs" them by simply adjusting the load that each module must handle. This creates a modeling uncertainty that is deemed acceptable given the error magnitude and partial cancellation of opposing errors from modules that exhibit setpoint overshoot and modules that exhibit setpoint undershoot.

References

Parameters

TypeNameDefaultDescription
IntegernUni1Number of modules
Booleanuse_TLoaLvgForCtltrueSet to true for leaving temperature control, false for entering temperature control
Booleanuse_TEvaOutForTab =true to use CHW temperature at outlet for table data, false for inlet
Booleanuse_TConOutForTab =true to use HW temperature at outlet for table data, false for inlet
DimensionlessRatioPLRHeaSup[:] PLR values at which heat flow rate and power data are provided - Heating [1]
DimensionlessRatioPLRCooSup[:] PLR values at which heat flow rate and power data are provided - Cooling [1]
DimensionlessRatioPLRShcSup[:] PLR values at which heat flow rate and power data are provided - SHC [1]
PowerP_min0Remaining power when system is enabled with all compressors cycled off [W]
StringfileNameHea File where performance data are stored - Heating (single module)
StringfileNameCoo File where performance data are stored - Cooling (single module)
StringfileNameShc File where performance data are stored - SHC (single module)
Nominal condition
TemperatureTHw_nominal HW temperature — Entering or leaving depending on use_TConOutForTab [K]
TemperatureTChw_nominal CHW temperature — Entering or leaving depending on use_TEvaOutForTab [K]
Nominal condition - Heating
TemperatureTAmbHea_nominal Ambient-side fluid temperature — Entering [K]
HeatFlowRateQHea_flow_nominal Heating heat flow rate - All modules [W]
Nominal condition - Cooling
TemperatureTAmbCoo_nominal Ambient-side fluid temperature — Entering [K]
HeatFlowRateQCoo_flow_nominal Cooling heat flow rate - All modules [W]
Nominal condition - SHC
HeatFlowRateQHeaShc_flow_nominal Heating heat flow rate - All modules [W]
HeatFlowRateQCooShc_flow_nominal Cooling heat flow rate - All modules [W]
Advanced
StringtabNamQHea[nPLRHea]{"q@" + String(p, format=".2...Table names with heat flow rate data - Heating
StringtabNamPHea[nPLRHea]{"p@" + String(p, format=".2...Table names with power data - Heating
StringtabNamQCoo[nPLRCoo]{"q@" + String(p, format=".2...Table names with heat flow rate data - Cooling
StringtabNamPCoo[nPLRCoo]{"p@" + String(p, format=".2...Table names with power data - Cooling
StringtabNamQShc[nPLRShc]{"q@" + String(p, format=".2...Table names with cooling heat flow rate data - SHC
StringtabNamPShc[nPLRShc]{"p@" + String(p, format=".2...Table names with power data - SHC
Staging logic
RealdtRun300Minimum stage runtime [s]
RealdtMea120Load averaging time window [s]
RealSPLR0.9Staging part load ratio
Safeties
TemperatureDifferencedTSaf2Maximum temperature deviation from setpoint before limiting demand for safety (>0) [K]

Connectors

TypeNameDescription
input BooleanInputonHeaHeating on/off command
input BooleanInputonCooCooling on/off command
input RealInputTHwEntCondenser entering HW temperature [K]
input RealInputTHwLvgCondenser leaving HW temperature [K]
input RealInputTChwEntEvaporator entering CHW temperature [K]
input RealInputTChwLvgEvaporator leaving CHW temperature [K]
input RealInputTAmbEntEntering fluid temperature on ambient side [K]
input RealInputcpHwHW specific heat capacity [J/(kg.K)]
input RealInputcpChwCHW specific heat capacity [J/(kg.K)]
input RealInputTChwSetCHW temperature setpoint [K]
input RealInputTHwSetHW temperature setpoint [K]
input RealInputmHw_flowHW mass flow rate [kg/s]
input RealInputmChw_flowCHW mass flow rate [kg/s]
output RealOutputPInput power [W]
output RealOutputQHea_flowHeating heat flow rate [J/s]
output RealOutputQCoo_flowCooling heat flow rate [J/s]
output IntegerOutputnUniHeaNumber of modules in heating mode
output IntegerOutputnUniCooNumber of modules in cooling mode
output IntegerOutputnUniShcNumber of modules in SHC mode (may be cycling into single mode)

Modelica definition

block TableData2DLoadDepSHC "Modeling block for simultaneous heating and cooling systems based on load-dependent 2D table data" parameter Integer nUni(final min=1)=1 "Number of modules"; parameter Boolean use_TLoaLvgForCtl=true "Set to true for leaving temperature control, false for entering temperature control"; parameter Boolean use_TEvaOutForTab "=true to use CHW temperature at outlet for table data, false for inlet"; parameter Boolean use_TConOutForTab "=true to use HW temperature at outlet for table data, false for inlet"; parameter Modelica.Units.SI.DimensionlessRatio PLRHeaSup[:](each final min=0) "PLR values at which heat flow rate and power data are provided - Heating"; parameter Modelica.Units.SI.DimensionlessRatio PLRCooSup[:](each final min=0) "PLR values at which heat flow rate and power data are provided - Cooling"; parameter Modelica.Units.SI.DimensionlessRatio PLRShcSup[:](each final min=0) "PLR values at which heat flow rate and power data are provided - SHC"; final parameter Modelica.Units.SI.DimensionlessRatio PLRHeaCyc_min=min(PLRHeaSup) "Minimum PLR before cycling off the last compressor - Heating"; final parameter Modelica.Units.SI.DimensionlessRatio PLRCooCyc_min=min(PLRCooSup) "Minimum PLR before cycling off the last compressor - Cooling"; final parameter Modelica.Units.SI.DimensionlessRatio PLRShcCyc_min=min(PLRShcSup) "Minimum PLR before cycling off the last compressor - SHC"; parameter Modelica.Units.SI.Power P_min(final min=0)=0 "Remaining power when system is enabled with all compressors cycled off"; final parameter Integer nPLRHea=size(PLRHeaSup, 1) "Number of PLR support points - Heating"; final parameter Integer nPLRCoo=size(PLRCooSup, 1) "Number of PLR support points - Cooling"; final parameter Integer nPLRShc=size(PLRShcSup, 1) "Number of PLR support points - SHC"; parameter String fileNameHea "File where performance data are stored - Heating (single module)"; parameter String fileNameCoo "File where performance data are stored - Cooling (single module)"; parameter String fileNameShc "File where performance data are stored - SHC (single module)"; final parameter Modelica.Blocks.Types.Smoothness smoothness= Modelica.Blocks.Types.Smoothness.LinearSegments "Smoothness of table interpolation"; final parameter Modelica.Blocks.Types.Extrapolation extrapolation= Modelica.Blocks.Types.Extrapolation.HoldLastPoint "Extrapolation of data outside the definition range"; parameter String tabNamQHea[nPLRHea]={"q@" + String(p, format=".2f") for p in PLRHeaSor} "Table names with heat flow rate data - Heating"; parameter String tabNamPHea[nPLRHea]={"p@" + String(p, format=".2f") for p in PLRHeaSor} "Table names with power data - Heating"; parameter String tabNamQCoo[nPLRCoo]={"q@" + String(p, format=".2f") for p in PLRCooSor} "Table names with heat flow rate data - Cooling"; parameter String tabNamPCoo[nPLRCoo]={"p@" + String(p, format=".2f") for p in PLRCooSor} "Table names with power data - Cooling"; parameter String tabNamQShc[nPLRShc]={"q@" + String(p, format=".2f") for p in PLRShcSor} "Table names with cooling heat flow rate data - SHC"; parameter String tabNamPShc[nPLRShc]={"p@" + String(p, format=".2f") for p in PLRShcSor} "Table names with power data - SHC"; parameter Modelica.Units.SI.Temperature THw_nominal "HW temperature — Entering or leaving depending on use_TConOutForTab"; parameter Modelica.Units.SI.Temperature TChw_nominal "CHW temperature — Entering or leaving depending on use_TEvaOutForTab"; parameter Modelica.Units.SI.Temperature TAmbHea_nominal "Ambient-side fluid temperature — Entering"; parameter Modelica.Units.SI.HeatFlowRate QHea_flow_nominal "Heating heat flow rate - All modules"; parameter Modelica.Units.SI.Temperature TAmbCoo_nominal "Ambient-side fluid temperature — Entering"; parameter Modelica.Units.SI.HeatFlowRate QCoo_flow_nominal "Cooling heat flow rate - All modules"; parameter Modelica.Units.SI.HeatFlowRate QHeaShc_flow_nominal "Heating heat flow rate - All modules"; parameter Modelica.Units.SI.HeatFlowRate QCooShc_flow_nominal "Cooling heat flow rate - All modules"; parameter Real dtRun( final min=0, final unit="s") = 300 "Minimum stage runtime"; parameter Real dtMea( final min=0, final unit="s") = 120 "Load averaging time window"; parameter Real SPLR( final max=1, final min=0) = 0.9 "Staging part load ratio"; parameter Modelica.Units.SI.TemperatureDifference dTSaf( final min=0) = 2 "Maximum temperature deviation from setpoint before limiting demand for safety (>0)"; // OMC and OCT require getTable2DValueNoDer2() to be called in initial equation section. // Binding equations yield incorrect results but no error! final parameter Modelica.Units.SI.Power PHeaInt_nominal[nPLRHea]( each fixed=false) "Power interpolated at nominal conditions, at each PLR - Heating, single module"; final parameter Modelica.Units.SI.HeatFlowRate QHeaInt_flow_nominal[nPLRHea]( each fixed=false) "Heat flow rate interpolated at nominal conditions, at each PLR - Heating, single module"; final parameter Modelica.Units.SI.Power PHeaInt1_nominal=Modelica.Math.Vectors.interpolate( PLRHeaSor, PHeaInt_nominal, 1) "Power interpolated at nominal conditions, at PLR=1 - Heating, single module"; final parameter Modelica.Units.SI.HeatFlowRate QHeaInt1_flow_nominal=Modelica.Math.Vectors.interpolate( PLRHeaSor, QHeaInt_flow_nominal, 1) "Heat flow rate interpolated at nominal conditions, at PLR=1 - Heating, single module"; final parameter Modelica.Units.SI.Power PCooInt_nominal[nPLRCoo]( each fixed=false) "Power interpolated at nominal conditions, at each PLR - Cooling, single module"; final parameter Modelica.Units.SI.HeatFlowRate QCooInt_flow_nominal[nPLRCoo]( each fixed=false) "Heat flow rate interpolated at nominal conditions, at each PLR - Cooling, single module"; final parameter Modelica.Units.SI.Power PCooInt1_nominal=Modelica.Math.Vectors.interpolate( PLRCooSor, PCooInt_nominal, 1) "Power interpolated at nominal conditions, at PLR=1 - Cooling, single module"; final parameter Modelica.Units.SI.HeatFlowRate QCooInt1_flow_nominal=Modelica.Math.Vectors.interpolate( PLRCooSor, QCooInt_flow_nominal, 1) "Heat flow rate interpolated at nominal conditions, at PLR=1 - Cooling, single module"; final parameter Modelica.Units.SI.Power PShcInt_nominal[nPLRShc]( each fixed=false) "Power interpolated at nominal conditions, at each PLR - SHC, single module"; final parameter Modelica.Units.SI.HeatFlowRate QCooShcInt_flow_nominal[nPLRShc]( each fixed=false) "Cooling heat flow rate interpolated at nominal conditions, at each PLR - SHC, single module"; final parameter Modelica.Units.SI.Power PShcInt1_nominal=Modelica.Math.Vectors.interpolate( PLRShcSor, PShcInt_nominal, 1) "Power interpolated at nominal conditions, at PLR=1 - SHC, single module"; final parameter Modelica.Units.SI.HeatFlowRate QCooShcInt1_flow_nominal= Modelica.Math.Vectors.interpolate(PLRShcSor, QCooShcInt_flow_nominal, 1) "Cooling heat flow rate interpolated at nominal conditions, at PLR=1 - SHC, single module"; final parameter Modelica.Units.SI.HeatFlowRate QHeaShcInt1_flow_nominal= PShcInt1_nominal - QCooShcInt1_flow_nominal "Heating heat flow rate at nominal conditions, at PLR=1 - SHC, single module"; final parameter Real scaFacHea( unit="1")=QHea_flow_nominal / (nUni * QHeaInt1_flow_nominal) "Scaling factor for interpolated heat flow rate and power - Heating"; final parameter Real scaFacCoo( unit="1")=QCoo_flow_nominal / (nUni * QCooInt1_flow_nominal) "Scaling factor for interpolated heat flow rate and power - Cooling"; final parameter Real scaFacCooShc( unit="1")=QCooShc_flow_nominal / (nUni * QCooShcInt1_flow_nominal) "Scaling factor for interpolated cooling heat flow rate and power - SHC"; final parameter Real scaFacHeaShc( unit="1")=QHeaShc_flow_nominal / (nUni * QHeaShcInt1_flow_nominal) "Scaling factor for interpolated heating heat flow rate - SHC"; final parameter Modelica.Units.SI.Power P_nominal=max({scaFacHea * PHeaInt1_nominal, scaFacCoo * PCooInt1_nominal, scaFacCooShc * PShcInt1_nominal}) "Maximum power at nominal conditions (external use) - All modes"; Buildings.Controls.OBC.CDL.Interfaces.BooleanInput onHea "Heating on/off command"; Buildings.Controls.OBC.CDL.Interfaces.BooleanInput onCoo "Cooling on/off command"; Buildings.Controls.OBC.CDL.Interfaces.RealInput THwEnt( final unit="K", displayUnit="degC") "Condenser entering HW temperature"; Buildings.Controls.OBC.CDL.Interfaces.RealInput THwLvg( final unit="K", displayUnit="degC") "Condenser leaving HW temperature"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TChwEnt( final unit="K", displayUnit="degC") "Evaporator entering CHW temperature"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TChwLvg( final unit="K", displayUnit="degC") "Evaporator leaving CHW temperature"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TAmbEnt( final unit="K", displayUnit="degC") "Entering fluid temperature on ambient side"; Buildings.Controls.OBC.CDL.Interfaces.RealInput cpHw( final unit="J/(kg.K)") "HW specific heat capacity"; Buildings.Controls.OBC.CDL.Interfaces.RealInput cpChw( final unit="J/(kg.K)") "CHW specific heat capacity"; Buildings.Controls.OBC.CDL.Interfaces.RealInput TChwSet( final unit="K", displayUnit="degC") "CHW temperature setpoint"; Buildings.Controls.OBC.CDL.Interfaces.RealInput THwSet( final unit="K", displayUnit="degC") "HW temperature setpoint"; Buildings.Controls.OBC.CDL.Interfaces.RealInput mHw_flow( final unit="kg/s") "HW mass flow rate"; Buildings.Controls.OBC.CDL.Interfaces.RealInput mChw_flow( final unit="kg/s") "CHW mass flow rate"; Buildings.Controls.OBC.CDL.Interfaces.RealOutput P( final unit="W") "Input power"; Buildings.Controls.OBC.CDL.Interfaces.RealOutput QHea_flow( final unit="J/s") "Heating heat flow rate"; Buildings.Controls.OBC.CDL.Interfaces.RealOutput QCoo_flow( final unit="J/s") "Cooling heat flow rate"; Buildings.Controls.OBC.CDL.Interfaces.IntegerOutput nUniHea(start=0) "Number of modules in heating mode"; Buildings.Controls.OBC.CDL.Interfaces.IntegerOutput nUniCoo(start=0) "Number of modules in cooling mode"; Buildings.Controls.OBC.CDL.Interfaces.IntegerOutput nUniShc(start=0) "Number of modules in SHC mode (may be cycling into single mode)"; protected final parameter Real PLRHeaSor[nPLRHea]=Modelica.Math.Vectors.sort(PLRHeaSup) "PLR values in increasing order - Heating"; final parameter Real PLRHea_max=PLRHeaSor[nPLRHea] "Maximum PLR"; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabQHea[nPLRHea]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamQHea, fileName=fill(fileNameHea, nPLRHea), table=fill(fill(0.0, 1, 2), nPLRHea), smoothness=fill(smoothness, nPLRHea), extrapolation=fill(extrapolation, nPLRHea), verboseRead=fill(false, nPLRHea)) "External table objects for heat flow interpolation "; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabPHea[nPLRHea]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamPHea, fileName=fill(fileNameHea, nPLRHea), table=fill(fill(0.0, 1, 2), nPLRHea), smoothness=fill(smoothness, nPLRHea), extrapolation=fill(extrapolation, nPLRHea), verboseRead=fill(false, nPLRHea)) "External table objects for power interpolation"; final parameter Real PLRCooSor[nPLRCoo]=Modelica.Math.Vectors.sort(PLRCooSup) "PLR values in increasing order - Cooling"; final parameter Real PLRCoo_max=PLRCooSor[nPLRCoo] "Maximum PLR - Cooling"; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabQCoo[nPLRCoo]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamQCoo, fileName=fill(fileNameCoo, nPLRCoo), table=fill(fill(0.0, 1, 2), nPLRCoo), smoothness=fill(smoothness, nPLRCoo), extrapolation=fill(extrapolation, nPLRCoo), verboseRead=fill(false, nPLRCoo)) "External table objects for heat flow interpolation - Cooling"; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabPCoo[nPLRCoo]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamPCoo, fileName=fill(fileNameCoo, nPLRCoo), table=fill(fill(0.0, 1, 2), nPLRCoo), smoothness=fill(smoothness, nPLRCoo), extrapolation=fill(extrapolation, nPLRCoo), verboseRead=fill(false, nPLRCoo)) "External table objects for power interpolation - Cooling"; final parameter Real PLRShcSor[nPLRShc]=Modelica.Math.Vectors.sort(PLRShcSup) "PLR values in increasing order - SHC"; final parameter Real PLRShc_max=PLRShcSor[nPLRShc] "Maximum PLR - SHC"; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabQShc[nPLRShc]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamQShc, fileName=fill(fileNameShc, nPLRShc), table=fill(fill(0.0, 1, 2), nPLRShc), smoothness=fill(smoothness, nPLRShc), extrapolation=fill(extrapolation, nPLRShc), verboseRead=fill(false, nPLRShc)) "External table objects for heat flow interpolation - SHC"; final parameter Modelica.Blocks.Types.ExternalCombiTable2D tabPShc[nPLRShc]= Modelica.Blocks.Types.ExternalCombiTable2D( tableName=tabNamPShc, fileName=fill(fileNameShc, nPLRShc), table=fill(fill(0.0, 1, 2), nPLRShc), smoothness=fill(smoothness, nPLRShc), extrapolation=fill(extrapolation, nPLRShc), verboseRead=fill(false, nPLRShc)) "External table objects for power interpolation - SHC"; constant Real deltaX = 1E-3 "Small number used for smoothing"; Modelica.Units.SI.HeatFlowRate QHeaSet_flow "Heating load - All modules"; Modelica.Units.SI.HeatFlowRate QCooSet_flow "Cooling load - All modules"; Modelica.Units.SI.HeatFlowRate QHeaSetRes_flow "Residual heating load - All modules except those in SHC mode"; Modelica.Units.SI.HeatFlowRate QCooSetRes_flow "Residual cooling load - All modules except those in SHC mode"; Modelica.Units.SI.HeatFlowRate QHeaSetMea_flow "Time-averaged heating load - All modules"; Modelica.Units.SI.HeatFlowRate QCooSetMea_flow "Time-averaged cooling load - All modules"; Modelica.Units.SI.HeatFlowRate QHeaSetResMea_flow "Time-averaged residual heating load - All modules except those in SHC mode"; Modelica.Units.SI.HeatFlowRate QCooSetResMea_flow "Time-averaged residual cooling load - All modules except those in SHC mode"; Modelica.Units.SI.HeatFlowRate QHeaSaf_flow "Demand limit for safe heating operation"; Modelica.Units.SI.HeatFlowRate QCooSaf_flow "Demand limit for safe cooling operation"; Modelica.Units.SI.HeatFlowRate QHeaShc_flow "Heating heat flow rate - All modules in SHC mode"; Modelica.Units.SI.HeatFlowRate QCooShc_flow "Cooling heat flow rate - All modules in SHC mode"; Modelica.Units.SI.HeatFlowRate QHeaShcNoCyc_flow "Heating heat flow rate without cycling - All modules in SHC mode"; Modelica.Units.SI.HeatFlowRate QCooShcNoCyc_flow "Cooling heat flow rate without cycling - All modules in SHC mode"; Modelica.Units.SI.HeatFlowRate QHeaShcCyc_flow "Heating heat flow rate when cycling in heating only mode - All modules cycling from SHC mode"; Modelica.Units.SI.HeatFlowRate QCooShcCyc_flow "Cooling heat flow rate when cycling in cooling only mode - All modules cycling from SHC mode"; Modelica.Units.SI.HeatFlowRate QHeaInt_flow[nPLRHea] "Capacity at PLR support points - Heating, single module"; Modelica.Units.SI.Power PHeaInt[nPLRHea] "Input power at PLR support points - Heating, single module"; Modelica.Units.SI.HeatFlowRate QCooInt_flow[nPLRCoo] "Capacity at PLR support points - Cooling, single module"; Modelica.Units.SI.Power PCooInt[nPLRCoo] "Input power at PLR support points - Cooling, single module"; Modelica.Units.SI.HeatFlowRate QCooShcInt_flow[nPLRShc] "Cooling capacity at PLR support points - SHC, single module"; Modelica.Units.SI.HeatFlowRate QHeaShcInt_flow[nPLRShc] "Heating capacity at PLR support points - SHC, single module"; Modelica.Units.SI.HeatFlowRate QHeaShcExc_flow "Excess heating heat flow rate - All modules in SHC mode"; Modelica.Units.SI.HeatFlowRate QCooShcExc_flow "Excess cooling heat flow rate - All modules in SHC mode"; Modelica.Units.SI.Power PShcInt[nPLRShc] "Input power at PLR support points - SHC, single module"; Real PLRHea(start=1) "Part load ratio - Modules in heating mode"; Real PLRCoo(start=1) "Part load ratio - Modules in cooling mode"; Real PLRShc(start=1) "Part load ratio - Modules in SHC mode"; Real PLRShcSaf "Limiting part load ratio for safety - Modules in SHC mode"; Real PLRShcLoa "Part load ratio satisfying the most demanding side - Modules in SHC mode"; Real PLRHeaShcCyc(start=1) "Part load ratio - Modules in SHC mode that cycle in heating mode"; Real PLRCooShcCyc(start=1) "Part load ratio - Modules in SHC mode that cycle in cooling mode"; Modelica.Units.SI.Temperature THwTab=if use_TConOutForTab then THwLvg else THwEnt "HW temperature used for table data interpolation"; Modelica.Units.SI.Temperature TChwTab=if use_TEvaOutForTab then TChwLvg else TChwEnt "CHW temperature used for table data interpolation"; Modelica.Units.SI.Temperature TAmbTab=TAmbEnt "Fluid temperature on ambient side used for table data interpolation"; Modelica.Units.SI.Temperature THwCtl=if use_TLoaLvgForCtl then THwEnt else THwLvg "HW temperature used for load calculation (Delta-T with setpoint)"; Modelica.Units.SI.Temperature TChwCtl=if use_TLoaLvgForCtl then TChwEnt else TChwLvg "CHW temperature used for load calculation (Delta-T with setpoint)"; Real sigLoa=if use_TLoaLvgForCtl then 1 else - 1 "Sign of Delta-T used for load calculation"; Real ratCycShc(start=1) "Cycling ratio between SHC and single mode for load balancing"; Integer nUniHeaShcRaw(start=0, fixed=true) "Number of modules required to meet heating load based on SHC capacity"; Integer nUniCooShcRaw(start=0, fixed=true) "Number of modules required to meet cooling load based on SHC capacity"; discrete Real entryTime(final quantity="Time", final unit="s") "Time instant when stage started"; Integer nUniHeaRaw(start=0, fixed=true) "Number of modules in heating mode - Without runtime requirement"; Integer nUniCooRaw(start=0, fixed=true) "Number of modules in cooling mode - Without runtime requirement"; Integer nUniShcRaw(start=0, fixed=true) "Number of modules in SHC mode (may be cycling into cooling or heating mode) - Without runtime requirement"; Integer pre_nUniShcRaw(start=0, fixed=true)=pre(nUniShcRaw) "Left limit of nUniShcRaw"; Integer pre_nUniHeaRaw(start=0, fixed=true)=pre(nUniHeaRaw) "Left limit of nUniHeaRaw"; Integer pre_nUniCooRaw(start=0, fixed=true)=pre(nUniCooRaw) "Left limit of nUniCooRaw"; Integer useHeaShc(start=0, fixed=true) "Calculation variable to zero out nUniHeaRaw when the bank is cooling dominated and SHC activated"; Integer useCooShc(start=0, fixed=true) "Calculation variable to zero out nUniCooRaw when the bank is heating dominated and SHC activated"; Integer useHea "Calculation variable to compute nUniHeaRaw in heating only mode"; Integer useCoo "Calculation variable to compute nUniCooRaw in cooling only mode"; initial equation PHeaInt_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2(tabPHea, fill( THw_nominal, nPLRHea), fill(TAmbHea_nominal, nPLRHea)); QHeaInt_flow_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQHea, fill(THw_nominal, nPLRHea), fill(TAmbHea_nominal, nPLRHea)); PCooInt_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2(tabPCoo, fill( TChw_nominal, nPLRCoo), fill(TAmbCoo_nominal, nPLRCoo)); QCooInt_flow_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQCoo, fill(TChw_nominal, nPLRCoo), fill(TAmbCoo_nominal, nPLRCoo)); PShcInt_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2(tabPShc, fill( TChw_nominal, nPLRShc), fill(THw_nominal, nPLRShc)); QCooShcInt_flow_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQShc, fill(TChw_nominal, nPLRShc), fill(THw_nominal, nPLRShc)); pre(nUniShc) = nUniShcRaw; pre(nUniHea) = nUniHeaRaw; pre(nUniCoo) = nUniCooRaw; pre(entryTime) = -Modelica.Constants.inf; QHeaSetMea_flow = 0; QCooSetMea_flow = 0; equation // Update number of modules in each mode with respect to // - minimum stage runtime // - step-by-step staging // - maximum number of modules in bank // - priority order: SHC > heating > cooling when {initial(), change(pre_nUniShcRaw), change(pre_nUniHeaRaw), change(pre_nUniCooRaw), time >= pre(entryTime) + dtRun} then if time >= pre(entryTime) + dtRun then nUniShc = pre(nUniShc) + ( if nUniShcRaw > pre(nUniShc) and pre(nUniShc) < nUni then 1 elseif pre(nUniShc) > 0 and nUniShcRaw < pre(nUniShc) then -1 else 0); nUniHea = pre(nUniHea) + ( if nUniHeaRaw > pre(nUniHea) and nUniShc <= pre(nUniShc) and nUniShc + pre(nUniHea) < nUni then 1 elseif pre(nUniHea) > 0 and nUniShc >= pre(nUniShc) and ( nUniHeaRaw < pre(nUniHea) or nUniShc + pre(nUniHea) + pre(nUniCoo) > nUni) then -1 else 0); nUniCoo = pre(nUniCoo) + ( if nUniCooRaw > pre(nUniCoo) and nUniShc <= pre(nUniShc) and nUniShc + nUniHea + pre(nUniCoo) < nUni then 1 elseif pre(nUniCoo) > 0 and nUniShc >= pre(nUniShc) and ( nUniCooRaw < pre(nUniCoo) or nUniShc + nUniHea + pre(nUniCoo) > nUni) then -1 else 0); else nUniShc = pre(nUniShc); nUniHea = pre(nUniHea); nUniCoo = pre(nUniCoo); end if; entryTime=if change(nUniShc) or change(nUniHea) or change(nUniCoo) then time else pre(entryTime); end when; // Update calculation variables for number of modules in single-mode depending on dominant load when {change(nUniShcRaw),change(nUniHeaShcRaw),change(nUniCooShcRaw)} then useHeaShc=if nUniShcRaw < nUni and nUniHeaShcRaw > nUniShcRaw then 1 else 0; useCooShc=if nUniShcRaw < nUni and nUniCooShcRaw > nUniShcRaw then 1 else 0; end when; if onHea and not onCoo then useHea=1; useCoo=0; elseif not onHea and onCoo then useHea=0; useCoo=1; else useHea=0; useCoo=0; end if; // Calculate total heating and cooling loads QHeaSet_flow = max(0, sigLoa * (THwSet - THwCtl) * cpHw * mHw_flow); QCooSet_flow = min(0, sigLoa * (TChwSet - TChwCtl) * cpChw * mChw_flow); QHeaSaf_flow = QHeaSet_flow + dTSaf * cpHw * mHw_flow; QCooSaf_flow = QCooSet_flow - dTSaf * cpChw * mChw_flow; dtMea * der(QHeaSetMea_flow) = QHeaSet_flow - QHeaSetMea_flow; dtMea * der(QCooSetMea_flow) = QCooSet_flow - QCooSetMea_flow; // Calculate capacity in each mode given actual condenser and evaporator-side temperature QHeaInt_flow = scaFacHea * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQHea, fill(THwTab, nPLRHea), fill(TAmbTab, nPLRHea)); QCooInt_flow = scaFacCoo * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQCoo, fill(TChwTab, nPLRCoo), fill(TAmbTab, nPLRCoo)); QCooShcInt_flow=scaFacCooShc * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabQShc, fill(TChwTab, nPLRShc), fill(THwTab, nPLRShc)); QHeaShcInt_flow=scaFacHeaShc *(PShcInt .- QCooShcInt_flow) / scaFacCooShc; // Calculate number of modules in SHC mode and PLR for these modules // (deltaX guards against numerical residuals influencing stage transitions near zero load) if onHea and onCoo then nUniHeaShcRaw = integer(ceil((QHeaSetMea_flow - 10 * deltaX * QHeaShc_flow_nominal) / SPLR / max( cat( 1, QHeaShcInt_flow, {deltaX*QHeaShc_flow_nominal})))); nUniCooShcRaw = integer(ceil((QCooSetMea_flow - 10 * deltaX * QCooShc_flow_nominal) / SPLR / min( cat( 1, QCooShcInt_flow, {deltaX*QCooShc_flow_nominal})))); else nUniHeaShcRaw = 0; nUniCooShcRaw = 0; end if; nUniShcRaw =min({nUni,nUniHeaShcRaw,nUniCooShcRaw}); if nUniShc > 0 then // Calculate PLR for modules in SHC mode // Using smoothLimit() instead of smoothMin(smoothMax()) below triggers chattering with OCT PLRShcSaf = min( Modelica.Math.Vectors.interpolate( cat(1, {0}, QHeaShcInt_flow), cat(1, {0}, PLRShcSor), QHeaSaf_flow / max(1, nUniShc + nUniHea)), Modelica.Math.Vectors.interpolate( abs(cat(1, {0}, QCooShcInt_flow)), cat(1, {0}, PLRShcSor), abs(QCooSaf_flow / max(1, nUniShc + nUniCoo)))); PLRShcLoa = Buildings.Utilities.Math.Functions.smoothMin( Buildings.Utilities.Math.Functions.smoothMax( Modelica.Math.Vectors.interpolate( cat(1, {0}, QHeaShcInt_flow), cat(1, {0}, PLRShcSor), QHeaSet_flow / max(1, nUniShc + nUniHea)), Modelica.Math.Vectors.interpolate( abs(cat(1, {0}, QCooShcInt_flow)), cat(1, {0}, PLRShcSor), abs(QCooSet_flow / max(1, nUniShc + nUniCoo))), deltaX), PLRShc_max, deltaX); PLRShc = min(PLRShcSaf, PLRShcLoa); // Calculate thermal output of module in SHC mode without single-mode cycling QHeaShcNoCyc_flow = Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRShcSor), cat(1, {0}, QHeaShcInt_flow), PLRShc); QCooShcNoCyc_flow = Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRShcSor), cat(1, {0}, QCooShcInt_flow), PLRShc); // Calculate excess heat flow rate and SHC cycling ratio to balance heating and cooling loads // ratCycShc=1 means perfect balance (no cycling): module continuously runs in SHC. QHeaShcExc_flow = max(0, nUniShc * (QHeaShcNoCyc_flow - QHeaSet_flow / max(1, nUniShc + nUniHea))); QCooShcExc_flow = min(0, nUniShc * (QCooShcNoCyc_flow - QCooSet_flow / max(1, nUniShc + nUniCoo))); ratCycShc = 1 - Buildings.Utilities.Math.Functions.smoothMin( 1, Buildings.Utilities.Math.Functions.smoothMax( QHeaShcExc_flow * Buildings.Utilities.Math.Functions.inverseXRegularized( QHeaShcNoCyc_flow, deltaX * QHeaShc_flow_nominal / nUni), QCooShcExc_flow * Buildings.Utilities.Math.Functions.inverseXRegularized( QCooShcNoCyc_flow, deltaX * abs(QCooShc_flow_nominal) / nUni), deltaX), deltaX); QHeaShc_flow = (nUniShc - 1 + ratCycShc) * QHeaShcNoCyc_flow; QCooShc_flow = (nUniShc - 1 + ratCycShc) * QCooShcNoCyc_flow; // Calculate PLR and thermal output of SHC module cycling into single-mode // Using smoothLimit() instead of smoothMin(smoothMax()) below makes // OCT fail to update the events when simulating Validation.TableData2DLoadDepSHC PLRHeaShcCyc = Buildings.Utilities.Math.Functions.smoothMin( Buildings.Utilities.Math.Functions.smoothMax( Modelica.Math.Vectors.interpolate( cat(1, {0}, QHeaInt_flow), cat(1, {0}, PLRHeaSor), (QHeaSet_flow / max(1, nUniShc + nUniHea) * nUniShc - QHeaShc_flow) * Buildings.Utilities.Math.Functions.inverseXRegularized( 1 - ratCycShc, deltaX)), 0, deltaX), PLRHea_max, deltaX); PLRCooShcCyc = Buildings.Utilities.Math.Functions.smoothMin( Buildings.Utilities.Math.Functions.smoothMax( Modelica.Math.Vectors.interpolate( abs(cat(1, {0}, QCooInt_flow)), cat(1, {0}, PLRCooSor), abs(QCooSet_flow / max(1, nUniShc + nUniCoo) * nUniShc - QCooShc_flow) * Buildings.Utilities.Math.Functions.inverseXRegularized( 1 - ratCycShc, deltaX)), 0, deltaX), PLRCoo_max, deltaX); QHeaShcCyc_flow = (1 - ratCycShc) * Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRHeaSor), cat(1, {0}, QHeaInt_flow), PLRHeaShcCyc); QCooShcCyc_flow = (1 - ratCycShc) * Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRCooSor), cat(1, {0}, QCooInt_flow), PLRCooShcCyc); else PLRShcSaf=0; PLRShcLoa=0; PLRShc=0; QHeaShcNoCyc_flow=0; QCooShcNoCyc_flow=0; ratCycShc=0; QHeaShc_flow=0; QCooShc_flow=0; QHeaShcExc_flow=0; QCooShcExc_flow=0; PLRHeaShcCyc=0; PLRCooShcCyc=0; QHeaShcCyc_flow=0; QCooShcCyc_flow=0; end if; // Calculate residual load, number of modules in single mode and PLR for these modules // (deltaX guards against numerical residuals influencing stage transitions near zero load) QHeaSetRes_flow = QHeaSet_flow -(QHeaShc_flow + QHeaShcCyc_flow); QCooSetRes_flow = QCooSet_flow -(QCooShc_flow + QCooShcCyc_flow); QHeaSetResMea_flow = QHeaSetMea_flow - (QHeaShc_flow + QHeaShcCyc_flow); QCooSetResMea_flow = QCooSetMea_flow - (QCooShc_flow + QCooShcCyc_flow); nUniHeaRaw=max(useHeaShc, useHea) * integer(ceil( (QHeaSetResMea_flow - 10 * deltaX * QHea_flow_nominal) / SPLR / max(cat(1, QHeaInt_flow, {deltaX * QHea_flow_nominal})))); nUniCooRaw=max(useCooShc, useCoo) * integer(ceil( (QCooSetResMea_flow - 10 * deltaX * QCoo_flow_nominal) / SPLR / min(cat(1, QCooInt_flow, {deltaX * QCoo_flow_nominal})))); if nUniHea > 0 then PLRHea=max(0, min(PLRHea_max, Modelica.Math.Vectors.interpolate( cat(1, {0}, QHeaInt_flow), cat(1, {0}, PLRHeaSor), QHeaSetRes_flow / nUniHea))); PHeaInt=scaFacHea * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabPHea, fill(THwTab, nPLRHea), fill(TAmbTab, nPLRHea)); else PLRHea=0; PHeaInt=fill(0, nPLRHea); end if; if nUniCoo > 0 then PLRCoo=max(0, min(PLRCoo_max, Modelica.Math.Vectors.interpolate( abs(cat(1, {0}, QCooInt_flow)), cat(1, {0}, PLRCooSor), abs(QCooSetRes_flow / nUniCoo)))); PCooInt=scaFacCoo * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabPCoo, fill(TChwTab, nPLRCoo), fill(TAmbTab, nPLRCoo)); else PLRCoo = 0; PCooInt = fill(0, nPLRCoo); end if; // Calculate total heating and cooling flow rate and power PShcInt = scaFacCooShc * Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2( tabPShc, fill(TChwTab, nPLRShc), fill(THwTab, nPLRShc)); QHea_flow = nUniHea * Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRHeaSor), cat(1, {0}, QHeaInt_flow), PLRHea) + QHeaShc_flow + QHeaShcCyc_flow; QCoo_flow = nUniCoo * Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRCooSor), cat(1, {0}, QCooInt_flow), PLRCoo) + QCooShc_flow + QCooShcCyc_flow; P = nUniHea * Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRHeaSor), cat(1, {0}, PHeaInt), PLRHea) + nUniCoo * Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRCooSor), cat(1, {0}, PCooInt), PLRCoo) + nUniShc * (ratCycShc * Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRShcSor), cat(1, {0}, PShcInt), PLRShc) + (1 - ratCycShc) * ( Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRHeaSor), cat(1, {0}, PHeaInt), PLRHeaShcCyc) + Modelica.Math.Vectors.interpolate( cat(1, {0}, PLRCooSor), cat(1, {0}, PCooInt), PLRCooShcCyc))); end TableData2DLoadDepSHC;