Partial refrigerant cycle model. Partial modular refrigerant cycle models for data and
  equation based approaches used in the model

  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.PartialReversibleRefrigerantMachine.

within AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses;
partial model PartialModularRefrigerantCycle
  "Partial refrigerant cycle model"

  parameter Boolean use_rev=true
    "True if the refrigerant machine is reversible";

  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus sigBus
    "Signal bus with data for refrigerant models"
  Modelica.Blocks.Interfaces.RealOutput QCon_flow(
    final unit="W")
    "Heat flow rate from the refrigerant to the condenser medium"
  Modelica.Blocks.Interfaces.RealOutput QEva_flow(
    final unit="W")
    "Heat flow rate from the evaporator medium to the refrigerant"
  Modelica.Blocks.Logical.Switch swiQEva(
    u1(final unit="W"),
    u3(final unit="W"),
    y(final unit="W"))
    "Routing block that picks the component acting as evaporator"
  Modelica.Blocks.Logical.Switch swiQCon(
    y(final unit="W"),
    u1(final unit="W"),
    u3(final unit="W"))
    "Routing block that picks the component acting as condenser"
  Modelica.Blocks.Interfaces.RealOutput PEle(final unit="W")
    "Routing block that picks the component for electric power consumption"

  Modelica.Blocks.Logical.Switch swiPEle(
    u1(final unit="W"),
    u3(final unit="W"),
    y(final unit="W"))
    "Whether to use cooling or heating power output"
protected
  Modelica.Blocks.Routing.BooleanPassThrough pasTrhModSet
    "Pass through to enable assertion for non-reversible device";
equation
  assert(
    use_rev or (use_rev == false and pasTrhModSet.y == true),
    "In " + getInstanceName() + ": Can't turn to reversible operation mode on
    irreversible refrigerant machine.",
    level=AssertionLevel.error);

  connect(swiQEva.y, QEva_flow)
  connect(swiPEle.y, PEle)
  connect(swiQCon.y, QCon_flow)
end PartialModularRefrigerantCycle;


within AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses;
partial model PartialReversibleRefrigerantMachine
  "Model for reversible heat pumps and chillers with a refrigerant cycle"
  extends AixLib.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = MediumCon,
    redeclare final package Medium2 = MediumEva,
    final m1_flow_nominal=mCon_flow_nominal,
    final m2_flow_nominal=mEva_flow_nominal,
    final allowFlowReversal1=allowFlowReversalCon,
    final allowFlowReversal2=allowFlowReversalEva,
    final m1_flow_small=1E-4*abs(mCon_flow_nominal),
    final m2_flow_small=1E-4*abs(mEva_flow_nominal));

  //General
  replaceable package MediumCon =
    Modelica.Media.Interfaces.PartialMedium "Medium on condenser side"
  replaceable package MediumEva =
    Modelica.Media.Interfaces.PartialMedium "Medium on evaporator side"
  replaceable PartialModularRefrigerantCycle refCyc constrainedby
    PartialModularRefrigerantCycle(final use_rev=use_rev)
    "Model of the refrigerant cycle"
  parameter Modelica.Units.SI.HeatFlowRate PEle_nominal
    "Nominal electrical power consumption"

  replaceable model RefrigerantCycleInertia =
     AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Inertias.NoInertia
    constrainedby
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Inertias.BaseClasses.PartialInertia
      "Inertia between the refrigerant cycle outputs and the heat exchangers."
  parameter Boolean use_rev=true
    "=true if the chiller or heat pump is reversible"
  parameter Boolean allowDifferentDeviceIdentifiers=false
    "if use_rev=true, device data for cooling and heating need to entered. Set allowDifferentDeviceIdentifiers=true to allow different device identifiers devIde"

  //Condenser
  parameter Modelica.Units.SI.Time tauCon=30
    "Condenser heat transfer time constant at nominal flow"
  parameter Modelica.Units.SI.TemperatureDifference dTCon_nominal
    "Nominal temperature difference in condenser medium, used to calculate mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate mCon_flow_nominal
    "Nominal mass flow rate of the condenser medium"

  parameter Modelica.Units.SI.PressureDifference dpCon_nominal(displayUnit="Pa")
    "Pressure drop at nominal mass flow rate"
  parameter Real deltaMCon=0.1
    "Fraction of nominal mass flow rate where transition to turbulent occurs"
  parameter Boolean use_conCap=true
    "=true if using capacitor model for condenser heat loss estimation"
  parameter Modelica.Units.SI.HeatCapacity CCon=0
    "Heat capacity of the condenser"
  parameter Modelica.Units.SI.ThermalConductance GConOut=0
    "Outer thermal conductance for condenser heat loss calculations"
  parameter Modelica.Units.SI.ThermalConductance GConIns=0
    "Inner thermal conductance for condenser heat loss calculations"

  final parameter Modelica.Units.SI.Density rhoCon=MediumCon.density(staCon_nominal)
    "Condenser medium density";
  final parameter Modelica.Units.SI.SpecificHeatCapacity cpCon=
      MediumCon.specificHeatCapacityCp(staCon_nominal)
    "Condenser medium specific heat capacity";

  //Evaporator
  parameter Modelica.Units.SI.Time tauEva=30
    "Evaporator heat transfer time constant at nominal flow"
  parameter Modelica.Units.SI.TemperatureDifference dTEva_nominal
    "Nominal temperature difference in evaporator medium, used to calculate mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate mEva_flow_nominal
    "Nominal mass flow rate of the evaporator medium"

  parameter Modelica.Units.SI.PressureDifference dpEva_nominal(displayUnit="Pa")
    "Pressure drop at nominal mass flow rate"
  parameter Real deltaMEva=0.1
    "Fraction of nominal mass flow rate where transition to turbulent occurs"
  parameter Boolean use_evaCap=true
    "=true if using capacitor model for evaporator heat loss estimation"
  parameter Modelica.Units.SI.HeatCapacity CEva=0
    "Heat capacity of the evaporator"
  parameter Modelica.Units.SI.ThermalConductance GEvaOut=0
    "Outer thermal conductance for evaporator heat loss calculations"
  parameter Modelica.Units.SI.ThermalConductance GEvaIns=0
    "Inner thermal conductance for evaporator heat loss calculations"
  final parameter Modelica.Units.SI.Density rhoEva=MediumEva.density(staEva_nominal)
    "Evaporator medium density";
  final parameter Modelica.Units.SI.SpecificHeatCapacity cpEva=
      MediumEva.specificHeatCapacityCp(staEva_nominal)
    "Evaporator medium specific heat capacity";

  // Safety control
  parameter Boolean use_intSafCtr=true
    "=true to enable internal safety control"
  replaceable parameter
    AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021 safCtrPar
    constrainedby
    AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Generic
    "Safety control parameters"

//Assumptions
  parameter Boolean allowFlowReversalEva=true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"
  parameter Boolean allowFlowReversalCon=true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"

//Initialization
  parameter Modelica.Blocks.Types.Init initType=
    Modelica.Blocks.Types.Init.InitialState
    "Type of initialization for refrigerant cycle dynamics (InitialState and InitialOutput are identical)"
  parameter Modelica.Media.Interfaces.Types.AbsolutePressure pCon_start=
      MediumCon.p_default "Start value of pressure"
  parameter Modelica.Media.Interfaces.Types.Temperature TCon_start=
    MediumCon.T_default
    "Start value of temperature"
  parameter Modelica.Units.SI.Temperature TConCap_start=MediumCon.T_default
    "Initial temperature of heat capacity of condenser"
  parameter Modelica.Media.Interfaces.Types.MassFraction XCon_start[MediumCon.nX]=
     MediumCon.X_default "Start value of mass fractions m_i/m"
  parameter Modelica.Media.Interfaces.Types.AbsolutePressure pEva_start=
      MediumEva.p_default "Start value of pressure"
  parameter Modelica.Media.Interfaces.Types.Temperature TEva_start=
    MediumEva.T_default
    "Start value of temperature"
  parameter Modelica.Units.SI.Temperature TEvaCap_start=MediumEva.T_default
    "Initial temperature of heat capacity at evaporator"
  parameter Modelica.Media.Interfaces.Types.MassFraction XEva_start[MediumEva.nX]=
     MediumEva.X_default "Start value of mass fractions m_i/m"

//Dynamics
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=
    Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options)
    or steady state (only affects fluid-models)"
//Advanced
  parameter Boolean from_dp=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"
  parameter Boolean linearized=false
    "= true, use linear relation between m_flow and dp for any flow rate"
  parameter Real ySet_small(min=0.002)=0.01
    "Threshold for relative speed for the device to be considered on"
  parameter Boolean calEff=true
    "=false to disable efficiency calculation, may speed up the simulation"
  parameter Real limWarSca(final unit="1") = 0.05
    "Allowed difference in scaling '|scaFacHea - scaFacCoo| / scaFacHea', if exceeded, a warning will be issued"

  Modelica.Units.SI.HeatFlowRate Q1_flow = QCon_flow
    "Heat transferred into the medium 1";
  Modelica.Units.SI.HeatFlowRate Q2_flow = QEva_flow
    "Heat transferred into the medium 2";

  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.EvaporatorCondenserWithCapacity con(
    redeclare final package Medium = MediumCon,
    final allowFlowReversal=allowFlowReversalCon,
    final m_flow_small=1E-4*abs(mCon_flow_nominal),
    final show_T=show_T,
    final deltaM=deltaMCon,
    final tau=tauCon,
    final T_start=TCon_start,
    final p_start=pCon_start,
    final use_cap=use_conCap,
    final X_start=XCon_start,
    final from_dp=from_dp,
    final energyDynamics=energyDynamics,
    final isCon=true,
    final C=CCon,
    final TCap_start=TConCap_start,
    final GOut=GConOut,
    final m_flow_nominal=mCon_flow_nominal,
    final dp_nominal=dpCon_nominal,
    final GInn=GConIns) "Heat exchanger model for the condenser"
  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.EvaporatorCondenserWithCapacity eva(
    redeclare final package Medium = MediumEva,
    final deltaM=deltaMEva,
    final tau=tauEva,
    final use_cap=use_evaCap,
    final allowFlowReversal=allowFlowReversalEva,
    final m_flow_small=1E-4*abs(mEva_flow_nominal),
    final show_T=show_T,
    final T_start=TEva_start,
    final p_start=pEva_start,
    final X_start=XEva_start,
    final from_dp=from_dp,
    final energyDynamics=energyDynamics,
    final isCon=false,
    final C=CEva,
    final m_flow_nominal=mEva_flow_nominal,
    final dp_nominal=dpEva_nominal,
    final TCap_start=TEvaCap_start,
    final GOut=GEvaOut,
    final GInn=GEvaIns) "Heat exchanger model for the evaporator"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature varTOutEva
    if use_evaCap "Forces heat losses according to ambient temperature"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature varTOutCon
    if use_conCap "Forces heat losses according to ambient temperature"
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Safety safCtr(
    final mEva_flow_nominal=mEva_flow_nominal,
    final mCon_flow_nominal=mCon_flow_nominal,
    safCtrPar=safCtrPar,
    final ySet_small=ySet_small) if use_intSafCtr "Safety control models"
  Modelica.Blocks.Interfaces.RealInput ySet if not use_busConOnl
    "Relative compressor speed between 0 and 1"

  Modelica.Blocks.Interfaces.RealInput TEvaAmb(final unit="K", displayUnit="degC")
    if use_evaCap and not use_busConOnl
    "Ambient temperature on the evaporator side"
  Modelica.Blocks.Interfaces.RealInput TConAmb(final unit="K", displayUnit="degC")
    if use_conCap and not use_busConOnl
    "Ambient temperature on the condenser side"

  AixLib.Fluid.Sensors.MassFlowRate mEva_flow(redeclare final package Medium =
        MediumEva, final allowFlowReversal=allowFlowReversalEva)
    "Mass flow sensor at the evaporator"
  AixLib.Fluid.Sensors.MassFlowRate mCon_flow(final allowFlowReversal=
        allowFlowReversalEva, redeclare final package Medium = MediumCon)
    "Mass flow sensor at the evaporator"

  Modelica.Blocks.Logical.Hysteresis hys(
    final uLow=0.001,
    final uHigh=ySet_small,
    final pre_y_start=false) "Use default ySet value"

  RefrigerantCycleInertia refCycIneCon "Inertia model for condenser side"
  RefrigerantCycleInertia refCycIneEva "Inertia model for evaporator side"

  Modelica.Blocks.Sources.RealExpression senTConIn(
    final y(
      final unit="K",
      displayUnit="degC")=MediumCon.temperature(MediumCon.setState_phX(
        port_a1.p,
        inStream(port_a1.h_outflow),
        inStream(port_a1.Xi_outflow))))
    "Real expression for condenser inlet temperature"
  Modelica.Blocks.Sources.RealExpression senTEvaIn(
    final y(
      final unit="K",
      displayUnit="degC")=MediumEva.temperature(MediumEva.setState_phX(
        port_a2.p,
        inStream(port_a2.h_outflow),
        inStream(port_a2.Xi_outflow))))
    "Real expression for evaporator inlet temperature"
  Modelica.Blocks.Interfaces.RealOutput QCon_flow(final quantity="HeatFlowRate",
      final unit="W") "Actual heating heat flow rate added to fluid 1"
  Modelica.Blocks.Interfaces.RealOutput P(final quantity="Power", final unit="W")
    "Electric power consumed by compressor"
  Modelica.Blocks.Interfaces.RealOutput QEva_flow(final quantity="HeatFlowRate",
      final unit="W") "Actual cooling heat flow rate removed from fluid 2"
  Modelica.Blocks.Interfaces.RealOutput EER(unit="1") if use_EER
    "Energy efficieny ratio"
  Modelica.Blocks.Interfaces.RealOutput COP(unit="1") if use_COP
    "Coefficient of performance"

  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.CalculateEfficiency
    eff(PEleMin=PEle_nominal*0.1) if calEff "Calculate efficiencies of device"
// To avoid using the bus, set the section below to protected
// <!-- @include_Buildings @include_IDEAS @include_BuildingSystems
// -->
  RefrigerantMachineControlBus sigBus
    "Bus with model outputs and possibly inputs"

  parameter Boolean use_busConOnl=false
    "=true to allow input to bus connector,
    not applicable with internal safety control"

protected
  parameter Boolean use_COP "=true to enable COP output";
  parameter Boolean use_EER "=true to enable EER output";
  parameter MediumCon.ThermodynamicState staCon_nominal=MediumCon.setState_pTX(
      T=MediumCon.T_default, p=MediumCon.p_default, X=MediumCon.X_default)
      "Nominal state of condenser medium";

  parameter MediumEva.ThermodynamicState staEva_nominal=MediumEva.setState_pTX(
      T=MediumEva.T_default, p=MediumEva.p_default, X=MediumEva.X_default)
      "Nominal state of evaporator medium";

equation

  // Non bus connections
  connect(safCtr.sigBus, sigBus)
  connect(safCtr.yOut, sigBus.yMea)
  connect(ySet, safCtr.ySet)
  connect(TConAmb, varTOutCon.T)
  connect(varTOutCon.port, con.port_out)
  connect(TEvaAmb, varTOutEva.T)
  connect(eva.port_out, varTOutEva.port)
  connect(mEva_flow.port_a, port_a2)
  connect(port_a1,mCon_flow. port_a)
  connect(mEva_flow.port_b, eva.port_a)
  connect(eva.port_b, port_b2)
  connect(refCyc.QEva_flow, refCycIneEva.u)
  connect(eva.Q_flow,refCycIneEva. y)
  connect(refCycIneCon.y, con.Q_flow)
  connect(refCycIneCon.u, refCyc.QCon_flow)
  connect(mCon_flow.port_b, con.port_a)
  connect(con.port_b, port_b1)
  // External bus connections
  connect(mEva_flow.m_flow, sigBus.mEvaMea_flow)
  connect(mCon_flow.m_flow, sigBus.mConMea_flow)
  connect(refCyc.sigBus, sigBus)
  connect(refCyc.PEle, sigBus.PEleMea)
  connect(hys.y, sigBus.onOffMea)
  connect(TConAmb, sigBus.TConAmbMea)
  connect(TEvaAmb, sigBus.TEvaAmbMea)
  connect(hys.u, sigBus.yMea)
  connect(con.T, sigBus.TConOutMea)
  connect(senTConIn.y, sigBus.TConInMea)
  connect(eva.T, sigBus.TEvaOutMea)
  connect(senTEvaIn.y, sigBus.TEvaInMea)
  if not use_intSafCtr then
    connect(ySet, sigBus.yMea)
  end if;
  connect(ySet, sigBus.ySet)
  connect(refCyc.PEle, P)
  connect(refCycIneEva.y, QEva_flow)
  connect(refCycIneCon.y, QCon_flow)
  connect(eff.PEle, refCyc.PEle)
  connect(eff.COP, COP)
  connect(eff.EER, EER)
end PartialReversibleRefrigerantMachine;


Refrigerant cycle model of a heat pump. Modular refrigerant cycle model for heat pump applications used in
  the model
  AixLib.Fluid.HeatPumps.ModularReversible.Modular and extending models
  of the modular approach.

within AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses;
model RefrigerantCycle
  "Refrigerant cycle model of a heat pump"
  extends AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.PartialModularRefrigerantCycle;

  parameter Boolean allowDifferentDeviceIdentifiers=false
    "if use_rev=true, device data for cooling and heating need to entered. Set allowDifferentDeviceIdentifiers=true to allow different device identifiers devIde"

  replaceable model RefrigerantCycleHeatPumpHeating =
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.NoHeating(
      useInHeaPum=true)
      constrainedby
        AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle
    "Replaceable model for refrigerant cycle of a heat pump in main operation mode"

  replaceable model RefrigerantCycleHeatPumpCooling =
      AixLib.Fluid.Chillers.ModularReversible.RefrigerantCycle.BaseClasses.NoCooling(
        useInChi=true)
      constrainedby
        AixLib.Fluid.Chillers.ModularReversible.RefrigerantCycle.BaseClasses.PartialChillerCycle
    "Replaceable model for refrigerant cycle of a heat pump in reversed operation mode"

  RefrigerantCycleHeatPumpHeating refCycHeaPumHea
    "Refrigerant cycle instance for heating"
  RefrigerantCycleHeatPumpCooling refCycHeaPumCoo
    "Refrigerant cycle instance for cooling"

protected
  parameter String devIde =
    if use_rev then refCycHeaPumCoo.devIde else refCycHeaPumHea.devIde
    "Data source for refrigerant cycle";

initial algorithm
  if not allowDifferentDeviceIdentifiers then
    assert(
      devIde == refCycHeaPumHea.devIde,
      "In " + getInstanceName() + ": Device identifiers devIde for reversible operation are not equal.
      Heating device identifier is '" + refCycHeaPumHea.devIde + "' but cooling is '"
      + devIde + "'. To allow this, set 'allowDifferentDeviceIdentifiers=true'.",
      AssertionLevel.error);
  end if;

equation
  connect(pasTrhModSet.u, sigBus.hea);

  connect(sigBus,refCycHeaPumCoo.sigBus)
  connect(sigBus,refCycHeaPumHea.sigBus)
  connect(swiQCon.u2, sigBus.hea)
  connect(swiPEle.u2, sigBus.hea)
  connect(swiQEva.u2, sigBus.hea)
  connect(refCycHeaPumHea.QCon_flow, swiQCon.u1)
  connect(refCycHeaPumHea.QEva_flow, swiQEva.u1)
  connect(refCycHeaPumCoo.QEva_flow, swiQCon.u3)
  connect(refCycHeaPumCoo.QCon_flow, swiQEva.u3)
  connect(refCycHeaPumCoo.PEle, swiPEle.u3)
  connect(refCycHeaPumHea.PEle, swiPEle.u1)
end RefrigerantCycle;


Package of control sequences

within AixLib.Fluid.HeatPumps.ModularReversible;
package Controls "Package of control sequences"
  extends Modelica.Icons.Package;
end Controls;


Model to prevent source from freezing. This model is used to prevent freezing of
  the condenser or evaporator side.
  A real device would shut off as well.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
model AntiFreeze "Model to prevent source from freezing"
  extends BaseClasses.PartialSafetyWithCounter;

  parameter Modelica.Units.SI.ThermodynamicTemperature TAntFre=276.15
    "Limit temperature for anti freeze control";
  parameter Real dTHys=2
    "Hysteresis interval width";
  Modelica.Blocks.Logical.Hysteresis hys(
    final uLow=TAntFre,
    final pre_y_start=true,
    final uHigh=TAntFre + dTHys) "Hysteresis to indicate if freezing occurs"

  Modelica.Blocks.Math.Min min
    "Minimum of evaporator outlet and condenser inlet temperatures"

equation
  connect(ySet,swiErr.u1)
  connect(min.y, hys.u)
  connect(sigBus.TConInMea, min.u1)

  connect(sigBus.TEvaOutMea, min.u2)

  connect(hys.y, booPasThr.u)
end AntiFreeze;


Safety control for minimum mass flow rate. Safety control to prevent the device from turning on
  if the mass flow rate is too low in either
  condenser or evaporator.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
model MinimalFlowRate "Safety control for minimum mass flow rate"
  extends BaseClasses.PartialSafetyWithCounter;

  parameter Modelica.Units.SI.MassFlowRate mEvaMin_flow
    "Minimal mass flow rate in evaporator required to operate the device";
  parameter Modelica.Units.SI.MassFlowRate mConMin_flow
    "Minimal mass flow rate in condenser required to operate the device";
  Modelica.Blocks.Logical.Hysteresis hysCon(
    final uLow=mConMin_flow,
    final uHigh=max(mConMin_flow*1.1, Modelica.Constants.eps),
    final pre_y_start=false)
    "Check if condenser mass flow rate is high enough"
  Modelica.Blocks.Logical.Hysteresis hysEva(
    final uLow=mEvaMin_flow,
    final uHigh=max(mEvaMin_flow*1.1, Modelica.Constants.eps),
    final pre_y_start=false)
    "Check if evaporator mass flow rate is high enough"
  Modelica.Blocks.Logical.And and1
    "Both condenser and evaporator have sufficient flow"

equation
  connect(hysCon.y, and1.u1)
  connect(hysEva.y, and1.u2)
  connect(and1.y, booPasThr.u)
  connect(hysEva.u, sigBus.mEvaMea_flow)
  connect(hysCon.u, sigBus.mConMea_flow)
  connect(ySet, swiErr.u1)
end MinimalFlowRate;


Controlls if the Safety constraints for on-time, off-time, and cycle rate. Checks if the ySet value is legal by checking if
  the device can either be turned on or off,
  depending on which state it was in.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
model OnOff
  "Controlls if the Safety constraints for on-time, off-time, and cycle rate"
  extends BaseClasses.PartialSafety;
  parameter Boolean use_minOnTime=true
    "=false to ignore minimum on-time constraint"
  parameter Modelica.Units.SI.Time minOnTime(displayUnit="min")
    "Minimum on-time"
  parameter Boolean use_minOffTime=true
    "=false to ignore minimum off time constraint"
  parameter Modelica.Units.SI.Time minOffTime(displayUnit="min")
    "Minimum off time"
  parameter Boolean use_maxCycRat=true
    "=false to ignore maximal cycle rate constraint"
  parameter Integer maxCycRat "Maximum cycle rate"
  parameter Boolean onOffMea_start=true
    "Start value for the on-off signal of the device, true for on";
  parameter Real ySet_small
    "Threshold for relative speed for the device to be considered on";
  parameter Real ySetRed=ySet_small
    "Reduced relative compressor speed to allow longer on-time";
  Modelica.Blocks.Logical.Hysteresis ySetOn(
    final pre_y_start=onOffMea_start,
    final uHigh=ySet_small,
    final uLow=ySet_small/2) "=true if device is set on"
  Modelica.Blocks.Routing.BooleanPassThrough isAblToTurOff(
    y(start=true, fixed=true))
    "=true if the device is allowed to turn off, else false"
  Modelica.Blocks.Logical.Pre preOnOff(final pre_u_start=onOffMea_start)
    "On off signal of previous time step"
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses.CycleRateBoundary
    cycRatBou(final maxCycRat=maxCycRat, final delTim=3600) if use_maxCycRat
    "Check cycle rate violations"
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses.OnPastThreshold locTimCtr(
   final minOnTime=minOffTime) if use_minOffTime
    "Check if device should be locked"
  Modelica.Blocks.Logical.Not notIsOn "=true if device is off"
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses.OnPastThreshold runTimCtr(
      final minOnTime=minOnTime) if use_minOnTime "Check if device needs to run"
  Modelica.Blocks.Logical.And andIsAblToTurOn(
    y(start=true, fixed=true))
    "=false to lock the device off"

  Modelica.Blocks.Sources.BooleanConstant booConstCycRat(final k=true)
    if not use_maxCycRat "Constant value for disabled option"
  Modelica.Blocks.Sources.BooleanConstant booConstLocTim(final k=true)
    if not use_minOffTime "Constant value for disabled option"
  Modelica.Blocks.Sources.BooleanConstant booConstRunTim(final k=true)
    if not use_minOnTime "Constant value for disabled option"
  Modelica.Blocks.Logical.Not notSetOn "Device is not set to turn on"
  Modelica.Blocks.Logical.And andTurOff
    "Check if device is on and is set to be turned off"
  Modelica.Blocks.Logical.And andTurOn
    "Check if device is off and is set to be turned on"

  Modelica.Blocks.Logical.And andStaOn
    "=true if the device is on and wants to stay on"
  Modelica.Blocks.Logical.And andStaOff
    "=true if the device is off and wants to stay off"

  BaseClasses.OnOffFuzzyLogic onOffFuzLog(ySetRed=ySetRed)
    "Fuzzy logic to device for output"
equation
  connect(preOnOff.y, cycRatBou.u)
  connect(preOnOff.y, notIsOn.u)
  connect(notIsOn.y, locTimCtr.u)
  connect(runTimCtr.u, preOnOff.y)
  connect(locTimCtr.y, andIsAblToTurOn.u1)
  connect(cycRatBou.y, andIsAblToTurOn.u2)
  connect(booConstCycRat.y, andIsAblToTurOn.u2)

  connect(ySetOn.y, notSetOn.u)
  connect(notSetOn.y, andTurOff.u2)
  connect(preOnOff.y, andTurOff.u1)
  connect(ySetOn.y, andTurOn.u2)
  connect(notIsOn.y, andTurOn.u1)
  connect(booConstLocTim.y, andIsAblToTurOn.u1)

  connect(preOnOff.u, sigBus.onOffMea)
  connect(ySetOn.u, ySet)
  connect(andStaOn.u1, ySetOn.y)
  connect(andStaOn.u2, preOnOff.y)
  connect(andStaOff.u1, notIsOn.y)
  connect(andStaOff.u2, notSetOn.y)
  connect(isAblToTurOff.u, runTimCtr.y)
  connect(booConstRunTim.y, isAblToTurOff.u)
  connect(onOffFuzLog.yOut, yOut)
  connect(onOffFuzLog.ySet, ySet)
  connect(onOffFuzLog.staOff, andStaOff.y)
  connect(onOffFuzLog.staOn, andStaOn.y)
  connect(onOffFuzLog.isAblToTurOff, isAblToTurOff.y)
  connect(andIsAblToTurOn.y, onOffFuzLog.isAblToTurOn)
  connect(onOffFuzLog.turOff, andTurOff.y)
  connect(onOffFuzLog.turOn, andTurOn.y)
end OnOff;


Indicates if the heat pump operation is within a defined envelope. Model to check if the operating conditions of a heat pump are inside
  the given boundaries. If not, the heat pump or chiller will switch off.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
model OperationalEnvelope
  "Indicates if the heat pump operation is within a defined envelope"
  extends BaseClasses.PartialOperationalEnvelope;
equation
  if use_TConOutHea then
    connect(bouMapHea.TUseSid, sigBus.TConOutMea)
  else
    connect(bouMapHea.TUseSid, sigBus.TConInMea)
  end if;
  if use_TEvaOutCoo then
    connect(bouMapCoo.TAmbSid, sigBus.TEvaOutMea)
  else
    connect(bouMapCoo.TAmbSid, sigBus.TEvaInMea)
  end if;
  if use_TEvaOutHea then
    connect(bouMapHea.TAmbSid, sigBus.TEvaOutMea)
  else
    connect(bouMapHea.TAmbSid, sigBus.TEvaInMea)
  end if;
  if use_TConOutCoo then
    connect(bouMapCoo.TUseSid, sigBus.TConOutMea)
  else
    connect(bouMapCoo.TUseSid, sigBus.TConInMea)
  end if;
  connect(swiHeaCoo.u2, sigBus.hea)
end OperationalEnvelope;


within AixLib.Fluid.HeatPumps.ModularReversible.Controls;
package Safety "Contains typical safety controllers for heat pumps"
  extends Modelica.Icons.Package;
end Safety;


Model including all safety levels. Aggregation of the four main safety controls
  of a refrigerant machine (heat pump or chiller).

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
model Safety "Model including all safety levels"
  extends BaseClasses.PartialSafety;
  parameter Modelica.Units.SI.MassFlowRate mEva_flow_nominal
    "Nominal mass flow rate in evaporator medium"
  parameter Modelica.Units.SI.MassFlowRate mCon_flow_nominal
    "Nominal mass flow rate in condenser medium"
  parameter Real ySet_small
    "Threshold for relative speed for the device to be considered on";

  replaceable parameter AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Generic
    safCtrPar constrainedby
    AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Generic
    "Safety control parameters"
  replaceable
    AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.OperationalEnvelope
    opeEnv if safCtrPar.use_opeEnv constrainedby
    AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses.PartialOperationalEnvelope(
      final use_TConOutHea=safCtrPar.use_TConOutHea,
      final use_TEvaOutHea=safCtrPar.use_TEvaOutHea,
      final use_TConOutCoo=safCtrPar.use_TConOutCoo,
      final use_TEvaOutCoo=safCtrPar.use_TEvaOutCoo,
      final tabUppHea=safCtrPar.tabUppHea,
      final tabLowCoo=safCtrPar.tabLowCoo,
      final dTHys=safCtrPar.dTHysOpeEnv) "Block for operational envelope"
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.OnOff onOffCtr(
    final minOnTime=safCtrPar.minOnTime,
    final minOffTime=safCtrPar.minOffTime,
    final use_minOnTime=safCtrPar.use_minOnTime,
    final use_minOffTime=safCtrPar.use_minOffTime,
    final use_maxCycRat=safCtrPar.use_maxCycRat,
    final maxCycRat=safCtrPar.maxCycRat,
    final onOffMea_start=safCtrPar.onOffMea_start,
    final ySet_small=ySet_small,
    final ySetRed=safCtrPar.ySetRed) if safCtrPar.use_minOnTime or safCtrPar.use_minOffTime
     or safCtrPar.use_maxCycRat "On off control block"

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.AntiFreeze antFre(final TAntFre=
        safCtrPar.TAntFre, final dTHys=safCtrPar.dTHysAntFre)
    if safCtrPar.use_antFre "Antifreeze control"
  Modelica.Blocks.Interfaces.IntegerOutput opeEnvErr if safCtrPar.use_opeEnv
    "Number of errors from violating the operational envelope"
  Modelica.Blocks.Interfaces.IntegerOutput antFreErr if safCtrPar.use_antFre
    "Number of errors from antifreeze control"

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.MinimalFlowRate
    minVolFloRatSaf(final mEvaMin_flow=safCtrPar.r_mEvaMinPer_flow*
        mEva_flow_nominal, final mConMin_flow=safCtrPar.r_mConMinPer_flow*
        mCon_flow_nominal) if safCtrPar.use_minFlowCtr
    "Block to ensure minimal flow rates"

  Modelica.Blocks.Interfaces.IntegerOutput minFlowErr
    if safCtrPar.use_minFlowCtr
    "Number of errors from violating minimum flow rates"
  Modelica.Blocks.Routing.RealPassThrough reaPasThrOnOff if not (safCtrPar.use_minOnTime
     or safCtrPar.use_minOffTime or safCtrPar.use_maxCycRat)
    "No on off control"
  Modelica.Blocks.Routing.RealPassThrough reaPasThrOpeEnv
    if not safCtrPar.use_opeEnv "No operational envelope control"
  Modelica.Blocks.Routing.RealPassThrough reaPasThrAntFre
    if not safCtrPar.use_antFre "No antifreeze control"
  Modelica.Blocks.Routing.RealPassThrough reaPasThrMinVolRat
    if not safCtrPar.use_minFlowCtr  "No minimum flow rate control"
equation

  connect(sigBus, onOffCtr.sigBus)

  connect(sigBus, opeEnv.sigBus)

  connect(sigBus, antFre.sigBus)

  connect(antFre.err, antFreErr)
  connect(opeEnv.err, opeEnvErr)
  connect(minVolFloRatSaf.yOut, yOut)
  connect(sigBus, minVolFloRatSaf.sigBus)

  connect(minVolFloRatSaf.err, minFlowErr)
  connect(reaPasThrOnOff.y, reaPasThrOpeEnv.u)
  connect(reaPasThrOpeEnv.y, reaPasThrAntFre.u)
  connect(reaPasThrAntFre.y, reaPasThrMinVolRat.u)
  connect(reaPasThrMinVolRat.y, yOut)
  connect(onOffCtr.yOut, opeEnv.ySet)
  connect(opeEnv.yOut, antFre.ySet)
  connect(antFre.yOut, minVolFloRatSaf.ySet)
  connect(antFre.yOut, reaPasThrMinVolRat.u)
  connect(reaPasThrAntFre.y, minVolFloRatSaf.ySet)
  connect(opeEnv.yOut, reaPasThrAntFre.u)
  connect(reaPasThrOpeEnv.y, antFre.ySet)
  connect(onOffCtr.yOut, reaPasThrOpeEnv.u)
  connect(reaPasThrOnOff.y, opeEnv.ySet)

  connect(ySet, onOffCtr.ySet)
  connect(ySet, reaPasThrOnOff.u)
end Safety;


Block that checks if the inputs are within the characteristic map. Given an input of TCon and TEva,
  the model returns false if the given
  point is outside of the given operational envelope.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses;
model BoundaryMap
  "Block that checks if the inputs are within the characteristic map"
  parameter Real tab[:,2]
    "Table for boundary with second column as useful temperature side";
  parameter Modelica.Units.SI.TemperatureDifference dT
    "Delta value used to avoid state events when used as a safety control";
  parameter Boolean isUppBou "=true if it is an upper boundary, false for lower";
  Modelica.Blocks.Interfaces.BooleanOutput noErr
    "=false when an error occurs"
  Modelica.Blocks.Interfaces.RealInput TAmbSid(unit="K", displayUnit="degC")
    "Temperature at ambient side"

  Modelica.Blocks.Tables.CombiTable1Ds tabBou(
    final table=tab,
    final smoothness=Modelica.Blocks.Types.Smoothness.LinearSegments,
    final tableOnFile=false) "Table with envelope data"
  Modelica.Blocks.MathBoolean.Nor nor(nu=3)
    "=true if the operational envelope is not violated"
  Modelica.Blocks.Logical.Hysteresis hysLef(
    final uLow=-0.05,
    final uHigh=0,
    pre_y_start=false) "Hysteresis for left side of envelope"
  Modelica.Blocks.Logical.Hysteresis hysRig(
    final uLow=-0.05,
    final uHigh=0,
    pre_y_start=false) "Hysteresis for right side of envelope"
  Modelica.Blocks.Sources.Constant conTAmbSidMin(k=TAmbSidMin)
    "Constant minimal temperature of ambient temperature side"
  Modelica.Blocks.Sources.Constant conTAmbSidMax(k=TAmbSidMax)
    "Constant maximal temperature of ambient temperature side"

  Modelica.Blocks.Math.Add subMax(final k1=+1, final k2=-1)
    "Actual minus maximal ambient side temperature"
  Modelica.Blocks.Math.Add sub(final k1=-1, final k2=+1)
    "Minimal minus current ambient side temperature"
  Modelica.Blocks.Interfaces.RealInput TUseSid(unit="K", displayUnit="degC")
    "Useful temperature side "
  Modelica.Blocks.Math.Add subBou(final k1=if isUppBou then 1 else -1, final k2=
       if isUppBou then -1 else 1)
    "Subtract boundary from current value depending on lower or upper boundary"
  Modelica.Blocks.Logical.Hysteresis hysBou(
    final uLow=-dT,
    final uHigh=0,
    pre_y_start=false) "Hysteresis for temperature limit"
protected
  parameter Real icoMin=-70
    "Used to set the frame where the icon should appear";
  parameter Real icoMax=70 "Used to set the frame where the icon should appear";
  final parameter Modelica.Units.SI.Temperature TAmbSidMax=tab[end, 1]
    "Maximal value of ambient side";
  final parameter Modelica.Units.SI.Temperature TAmbSidMin=tab[1, 1]
    "Minimal temperature at ambient side";
  final parameter Modelica.Units.SI.Temperature TUseSidMax=max(tab[:, 2])
    "Maximal temperature of useful side";
  final parameter Modelica.Units.SI.Temperature TUseSidMin=0
    "Minimal value of useful side";
  final parameter Real poi[size(scaTAmbSid, 1),2]=transpose({scaTAmbSidToPoi,
      scaTUseSidToPoi}) "Points for dynamic annotation"
  final parameter Modelica.Units.SI.Temperature scaTAmbSid[:]=tab[:, 1]
    "Helper array with only not ambient side temperature values";
  final parameter Modelica.Units.SI.Temperature scaTUseSid[:]=tab[:, 2]
    "Helper array with only useful side temperature values";
  final parameter Real scaTAmbSidToPoi[size(scaTAmbSid, 1)](
    each min=-100,
    each max=100) = (scaTAmbSid - fill(TAmbSidMin, size(scaTAmbSid, 1)))*(icoMax -
    icoMin)/(TAmbSidMax - TAmbSidMin) + fill(icoMin, size(scaTAmbSid, 1))
    "Scale ambient side to icon size";
  final parameter Real scaTUseSidToPoi[size(scaTAmbSid, 1)](
    each min=-100,
    each max=100) = (scaTUseSid - fill(TUseSidMin, size(scaTUseSid, 1)))*(icoMax -
    icoMin)/(TUseSidMax - TUseSidMin) + fill(icoMin, size(scaTUseSid, 1))
    "Scale useful side to icon size";

equation
  connect(nor.y, noErr)
  connect(hysLef.y, nor.u[1])
  connect(hysRig.y, nor.u[2])
  connect(subMax.u2,conTAmbSidMax.y)
  connect(sub.u2,conTAmbSidMin.y)
  connect(subMax.y, hysRig.u)
  connect(sub.y, hysLef.u)
  connect(hysBou.u, subBou.y)
  connect(hysBou.y, nor.u[3])
  connect(tabBou.y[1], subBou.u2)
  connect(subBou.u1, TUseSid)
  connect(TAmbSid, tabBou.u)
  connect(TAmbSid, sub.u1)
  connect(TAmbSid, subMax.u1)
end BoundaryMap;


Checks if the cycle rate is in boundary. The block counts the number of times the device is turned on
  in a given time interval by tracking the rising edge of the boolean
  input signal. If this number is higher than a set value, the block
  indicates an error.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses;
block CycleRateBoundary "Checks if the cycle rate is in boundary"
  extends Modelica.Blocks.Interfaces.BooleanSISO;
  parameter Integer maxCycRat "Maximal cycle rate";
  parameter Modelica.Units.SI.Time delTim(displayUnit="h") = 3600
    "Delay time of output with respect to input signal";
  Modelica.Blocks.Logical.LessThreshold runCouLesMax(threshold=maxCycRat)
    "Checks if the count of total runs is lower than the maximal value"
  Modelica.Blocks.MathInteger.TriggeredAdd triAdd "Count number of starts"
  Modelica.Blocks.Sources.IntegerConstant intConPluOne(final k=1)
    "Value for counting"
  Modelica.Blocks.Math.IntegerToReal intToRea
    "Convert to real in order to compare and delay"
  Modelica.Blocks.Math.Add sub(k2=-1)
    "Difference of current and delayed starts"
  Modelica.Blocks.Nonlinear.FixedDelay fixDel(final delayTime=delTim)
    "Apply delay to enable starts per delay time"
equation
  connect(intConPluOne.y, triAdd.u)
  connect(intToRea.u, triAdd.y)
  connect(intToRea.y, sub.u1)
  connect(intToRea.y, fixDel.u)
  connect(fixDel.y, sub.u2)
  connect(runCouLesMax.y, y)
  connect(u, triAdd.trigger)
  connect(sub.y, runCouLesMax.u)
end CycleRateBoundary;


Fuzzy logic approach for on-off control. The model uses a fuzzy logic approach to avoid the need for a state-machine.
The device either has to turn off, run at the desired operating speed, or run
at the minimal speed.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses;
model OnOffFuzzyLogic
  "Fuzzy logic approach for on-off control"
  parameter Real ySetRed
    "Reduced relative compressor speed to allow longer on-time";
  Modelica.Blocks.Interfaces.BooleanInput turOn(start=false, fixed=true)
    "Indicates if device should turn on"
  Modelica.Blocks.Interfaces.BooleanInput isAblToTurOn
    "Indicates if the device can turn on"
  Modelica.Blocks.Interfaces.BooleanInput turOff(start=false, fixed=true)
    "Indicates if the device should turn off"
  Modelica.Blocks.Interfaces.BooleanInput isAblToTurOff
    "Indicates if the device can turn off"
  Modelica.Blocks.Interfaces.BooleanInput staOff
    "Indicates if the device has to stay off"
  Modelica.Blocks.Interfaces.BooleanInput staOn
    "Indicates if the device has to stay on"
  Modelica.Blocks.Interfaces.RealOutput yOut
    "Output for relative compressor speed from 0 to 1"
  Modelica.Blocks.Interfaces.RealInput ySet
    "Input for relative compressor speed from 0 to 1"
protected
  Integer devRunMin(start=0, fixed=true)
    "Indicates if device needs to run at minimal limit";
  Integer devTurOff(start=0, fixed=true)
    "Indicates if device needs to turn off";
  Integer devNorOpe(start=1, fixed=true)
    "Indicates if device is at normal operation";
equation
  // The part "+ 0 * devTurOff" is implicitly included
  yOut = ySet * devNorOpe + ySetRed  * devRunMin;
  when edge(turOn) then
    if isAblToTurOn then
      devTurOff = 0;
      devRunMin = 0;
      devNorOpe = 1;
   else
      devTurOff = 1;
      devRunMin = 0;
      devNorOpe = 0;
    end if;
  elsewhen edge(turOff) then
    if isAblToTurOff then
      devTurOff = 0;
      devRunMin = 0;
      devNorOpe = 1;
    else
      devTurOff = 0;
      devRunMin = 1;
      devNorOpe = 0;
    end if;
  elsewhen isAblToTurOn and turOn then
    devTurOff = 0;
    devRunMin = 0;
    devNorOpe = 1;
  elsewhen isAblToTurOff and turOff then
    devTurOff = 0;
    devRunMin = 0;
    devNorOpe = 1;
  elsewhen staOff then
    devTurOff = 0;
    devRunMin = 0;
    devNorOpe = 1;
  elsewhen staOn then
    devTurOff = 0;
    devRunMin = 0;
    devNorOpe = 1;
  end when;
end OnOffFuzzyLogic;


Returns true if the device stays on for longer than a threshold time. This block delays a true signal and only returns true
  if the device stays on (true) for longer than a threshold time.
  If the device is switched off before the threshold time,
  this block continues returning false.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses;
block OnPastThreshold
  "Returns true if the device stays on for longer than a threshold time"
  extends Modelica.Blocks.Interfaces.BooleanSISO;

  parameter Modelica.Units.SI.Time minOnTime
    "Minimal time the device is turned on or off";
  Modelica.Blocks.Logical.Timer runTim
    "Counts the seconds the heat pump is locked still"
  Modelica.Blocks.Logical.GreaterEqualThreshold runTimGreMin(final threshold=
        minOnTime) "Checks if the on-time is greater than the minimal on-time"
equation
  connect(runTimGreMin.y, y)
  connect(u,runTim. u)
  connect(runTim.y, runTimGreMin.u)
end OnPastThreshold;


within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
package BaseClasses "Package with base classes for AixLib.Fluid.HeatPump.Controls.Safety"
end BaseClasses;


Indicates if the device operation is within a defined envelope. Model to check if the operating conditions are inside
  the given boundaries. If not, the heat pump or chiller will switch off.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses;
partial model PartialOperationalEnvelope
  "Indicates if the device operation is within a defined envelope"
  extends BaseClasses.PartialSafetyWithCounter;
  parameter Modelica.Units.SI.Temperature tabUppHea[:,2]
    "Upper boundary for heating with second column as useful temperature side";
  parameter Modelica.Units.SI.Temperature tabLowCoo[:,2]
    "Lower boundary for cooling with second column as useful temperature side";
  parameter Boolean use_TConOutHea=true
    "=true to use condenser outlet temperature for envelope in heating mode, false for inlet"
  parameter Boolean use_TEvaOutHea=false
    "=true to use evaporator outlet temperature for envelope in heating mode, false for inlet"
  parameter Boolean use_TConOutCoo=false
    "=true to use useful side outlet temperature for envelope in cooling mode, false for inlet"
  parameter Boolean use_TEvaOutCoo=true
    "=true to use evaporator outlet temperature for envelope in cooling mode, false for inlet"

  parameter Modelica.Units.SI.TemperatureDifference dTHys=5
    "Temperature deadband in the operational envelope";

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses.BoundaryMap bouMapHea(
    final tab=tabUppHea,
    final dT=dTHys,
    final isUppBou=true) "Operational boundary map for heating operation"

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses.BoundaryMap bouMapCoo(
    final tab=tabLowCoo,
    final dT=dTHys,
    final isUppBou=false) "Operational boundary map for cooling operation"
  Modelica.Blocks.Logical.LogicalSwitch swiHeaCoo
    "Switch between heating and cooling envelope"
equation
  connect(ySet,swiErr.u1)
  connect(swiHeaCoo.y, booPasThr.u)
  connect(bouMapCoo.noErr, swiHeaCoo.u3)
  connect(bouMapHea.noErr, swiHeaCoo.u1)
end PartialOperationalEnvelope;


Safety control with I/O. Partial block for a safety control. Based on the signals in the
  sigBus either the input signals are equal to the output
  signals or, if an error occurs, set to 0.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses;
partial model PartialSafety "Safety control with I/O"
  Modelica.Blocks.Interfaces.RealInput ySet
    "Input for relative compressor speed from 0 to 1"
  Modelica.Blocks.Interfaces.RealOutput yOut
    "Output for relative compressor speed from 0 to 1"
  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus sigBus
    "Bus-connector for the heat pump"
end PartialSafety;


Safety control which adds an error counter to the I/O. This block counts the number of errors occurred in a specific safety block.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses;
partial model PartialSafetyWithCounter
   "Safety control which adds an error counter to the I/O"
  extends PartialSafety;

  Modelica.Blocks.Logical.Switch swiErr
    "Switches to zero when an error occurs"
  Modelica.Blocks.Sources.Constant conZer(final k=0)
    "Constant zero"
  Modelica.Blocks.MathInteger.TriggeredAdd disErr(
    y_start=0,
    use_reset=false,
    use_set=false) "Used to show if the error was triggered"
  Modelica.Blocks.Interfaces.IntegerOutput err
    "Integer for displaying number of errors during simulation"
  Modelica.Blocks.Logical.Not notVal "=true indicates a error"
  Modelica.Blocks.Sources.IntegerConstant intConOne(final k=1)
    "Used for display of current error"
  Modelica.Blocks.Routing.BooleanPassThrough booPasThr
    "Used to keep the connection to the counter"
equation
  connect(conZer.y,swiErr. u3)
  connect(disErr.y,err)
  connect(notVal.y, disErr.trigger)
  connect(intConOne.y, disErr.u)
  connect(booPasThr.y, swiErr.u2)
  connect(booPasThr.y, notVal.u)
  connect(swiErr.y, yOut)
end PartialSafetyWithCounter;


within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
package Data "Package with parameters for safety control blocks"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Example for usage of antifreeze model. This example shows the usage of the model

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.AntiFreeze.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Examples;
model AntiFreeze "Example for usage of antifreeze model"
  extends BaseClasses.PartialSafety;
  extends Modelica.Icons.Example;
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.AntiFreeze antFre
    "Safety control for antifreeze"
  Modelica.Blocks.Sources.Pulse ySetPul(amplitude=1, period=50)
    "Pulse signal for ySet"
  Modelica.Blocks.Sources.Pulse TConInEmu(
    amplitude=-10,
    period=20,
    offset=283.15,
    y(unit="K", displayUnit="K"))   "Emulator for condenser inlet temperature"
  Modelica.Blocks.Sources.Pulse TEvaOutEmu(
    amplitude=-10,
    period=15,
    offset=283.15,
    y(unit="K", displayUnit="K"))
                   "Emulator for evaporator outlet temperature"
equation
  connect(antFre.sigBus, sigBus)
  connect(ySetPul.y, antFre.ySet)
  connect(TEvaOutEmu.y, sigBus.TEvaOutMea)
  connect(TConInEmu.y, sigBus.TConInMea)
  connect(hys.u, antFre.yOut)
  connect(antFre.yOut, yOut)
  connect(ySetPul.y, ySet)
end AntiFreeze;


Example for usage of minimal flow rate safety control. This example shows the usage of the model

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.MinimalFlowRate.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Examples;
model MinimalFlowRate
  "Example for usage of minimal flow rate safety control"
  extends BaseClasses.PartialSafety;
  extends Modelica.Icons.Example;
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.MinimalFlowRate minFloRatSaf(
      mEvaMin_flow=0.1, mConMin_flow=0.1)
    "Safety block for minimal flow rate safety"
  Modelica.Blocks.Sources.Pulse ySetPul(amplitude=1, period=50)
    "Pulse signal for ySet"
  Modelica.Blocks.Sources.Pulse mConEmu_flow(amplitude=1, period=20)
    "Emulator for condenser mass flow rate"
  Modelica.Blocks.Sources.Pulse mEvaEmu_flow(amplitude=1, period=15)
    "Emulator for evaporator mass flow rate"
equation
  connect(sigBus, minFloRatSaf.sigBus)
  connect(minFloRatSaf.yOut, hys.u)
  connect(ySetPul.y, minFloRatSaf.ySet)
  connect(mConEmu_flow.y, sigBus.mConMea_flow)
  connect(mEvaEmu_flow.y, sigBus.mEvaMea_flow)
  connect(minFloRatSaf.yOut, yOut)
  connect(ySetPul.y, ySet)
end MinimalFlowRate;


Example for on off controller. This example shows the usage of the model

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.OnOff.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Examples;
model OnOff "Example for on off controller"
  extends BaseClasses.PartialSafety;
  extends Modelica.Icons.Example;

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.OnOff onOffCtr(
    maxCycRat=2,
    minOffTime(displayUnit="s") = 200,
    minOnTime(displayUnit="s") = 300,
    onOffMea_start=false,
    use_minOffTime=true,
    use_minOnTime=true,
    use_maxCycRat=true,
    ySet_small=hys.uHigh,
    ySetRed=0.5) "Example case for on off control"

  Modelica.Blocks.Sources.Sine ySetSin(
    amplitude=0.5,
    f=1/180,
    offset=0.5) "Sine signal for ySet"
equation
  connect(onOffCtr.yOut, hys.u)
  connect(onOffCtr.sigBus, sigBus)
  connect(ySetSin.y, onOffCtr.ySet)
  connect(ySetSin.y, ySet)
  connect(onOffCtr.yOut, yOut)
end OnOff;


Example for usage of operational envelope model. This example shows the usage of the model

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.OperationalEnvelope.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Examples;
model OperationalEnvelope "Example for usage of operational envelope model"
  extends BaseClasses.PartialSafety;
  extends Modelica.Icons.Example;
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.OperationalEnvelope opeEnv(
    tabUppHea=[233.15,333.15; 313.15,333.15],
    tabLowCoo=[233.15,288.15; 313.15,288.15]) "Safety control for operational envelope"
  Modelica.Blocks.Sources.Constant
                                ySetPul(k=1) "Always on"
  Modelica.Blocks.Sources.Trapezoid
                                TConOutEmu(
    amplitude=60,
    rising=5,
    width=20,
    falling=5,
    period=50,
    offset=283.15,
    y(unit="K", displayUnit="degC"))
                   "Emulator for condenser outlet temperature"
  Modelica.Blocks.Sources.Pulse TEvaInEmu(
    amplitude=-10,
    period=15,
    offset=283.15,
    y(unit="K", displayUnit="degC"))
                   "Emulator for evaporator inlet temperature"
  Modelica.Blocks.Sources.BooleanStep hea(startTime=50, startValue=true)
    "Heating mode"
equation
  connect(opeEnv.sigBus, sigBus)
  connect(ySetPul.y, opeEnv.ySet)
  connect(hys.u, opeEnv.yOut)
  connect(opeEnv.yOut, yOut)
  connect(ySetPul.y, ySet)
  connect(TConOutEmu.y, sigBus.TConOutMea)
  connect(TEvaInEmu.y, sigBus.TEvaInMea)
  connect(hea.y, sigBus.hea)
  connect(TConOutEmu.y, sigBus.TConInMea)
  connect(TEvaInEmu.y, sigBus.TEvaOutMea)
end OperationalEnvelope;


Examples package

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety;
package Examples "Examples package"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Example for usage of all safety controls. This example shows the usage and effect of
  all safety controls aggregates into the model

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Safety.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Examples;
model Safety "Example for usage of all safety controls"
  extends BaseClasses.PartialSafety;
  extends Modelica.Icons.Example;
  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Safety safCtr(
    mEva_flow_nominal=0.01,
    mCon_flow_nominal=0.01,
    ySet_small=0.01,
    redeclare AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021
      safCtrPar(
      minOnTime=5,
      minOffTime=5,
      use_antFre=true,
      TAntFre=276.15)) "Safety control"
  Modelica.Blocks.Sources.Pulse ySetPul(amplitude=1, period=50)
    "Pulse signal for ySet"
  Modelica.Blocks.Sources.Pulse TConInEmu(
    amplitude=10,
    period=20,
    offset=303.15,
    y(unit="K", displayUnit="degC"))
                   "Emulator for condenser inlet temperature"
  Modelica.Blocks.Sources.Pulse TEvaOutEmu(
    amplitude=-10,
    period=15,
    offset=287.15,
    y(unit="K", displayUnit="degC"))
                   "Emulator for evaporator outlet temperature"
  Modelica.Blocks.Sources.Pulse TConOutEmu(
    amplitude=40,
    period=20,
    offset=313.15,
    y(unit="K", displayUnit="degC"))
                   "Emulator for condenser outlet temperature"
  Modelica.Blocks.Sources.Pulse TEvaInEmu(
    amplitude=-10,
    period=15,
    offset=288.15,
    y(unit="K", displayUnit="degC"))
                   "Emulator for evaporator inlet temperature"
  Modelica.Blocks.Sources.Pulse mConEmu_flow(
    amplitude=1,
    width=80,
    period=100) "Emulator for condenser mass flow rate"
  Modelica.Blocks.Sources.Pulse mEvaEmu_flow(amplitude=1, period=100)
    "Emulator for evaporator mass flow rate"
  Modelica.Blocks.Sources.BooleanConstant conHea(final k=true)
    "Constant heating mode"
equation
  connect(safCtr.sigBus, sigBus)
  connect(ySetPul.y, safCtr.ySet)
  connect(TEvaOutEmu.y, sigBus.TEvaOutMea)
  connect(TConInEmu.y, sigBus.TConInMea)
  connect(hys.u, safCtr.yOut)
  connect(safCtr.yOut, yOut)
  connect(ySetPul.y, ySet)
  connect(TConOutEmu.y, sigBus.TConOutMea)
  connect(TEvaInEmu.y, sigBus.TEvaInMea)
  connect(mEvaEmu_flow.y, sigBus.mEvaMea_flow)
  connect(mConEmu_flow.y, sigBus.mConMea_flow)
  connect(conHea.y, sigBus.hea)
end Safety;


within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Examples;
package BaseClasses "Package with partial models for examples"
extends Modelica.Icons.BasesPackage;
end BaseClasses;


Bus-connector for the heat pump. Partial example providing interfaces to
  show usage of safety control models.

within AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Examples.BaseClasses;
partial model PartialSafety

  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus sigBus
    "Bus-connector for the heat pump"
  Modelica.Blocks.Logical.Hysteresis hys(
    pre_y_start=false,
    uHigh=0.01,
    uLow=0.01/2) "Check if onOffMea in heat pump model would be true"
  Modelica.Blocks.Interfaces.RealOutput ySet
    "Relative speed of compressor from 0 to 1"
  Modelica.Blocks.Interfaces.RealOutput yOut
    "Relative speed of compressor applied after safety control"
equation
  connect(hys.y, sigBus.onOffMea)
end PartialSafety;


within AixLib.Fluid.HeatPumps.ModularReversible;
package Data "Package with data for modular reversible heat pump models"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Package with two-dimensional data

within AixLib.Fluid.HeatPumps.ModularReversible.Data;
package TableData2D "Package with two-dimensional data"
  extends Modelica.Icons.MaterialPropertiesPackage;
end TableData2D;


Package for EN15411 data records

within AixLib.Fluid.HeatPumps.ModularReversible.Data.TableData2D;
package EN14511 "Package for EN15411 data records"
end EN14511;


A2W Vitocal 251 by Viessmann. Data from the planning book (Planungshandbuch) from Viessmann.


Package for older EN255 data records

within AixLib.Fluid.HeatPumps.ModularReversible.Data.TableData2D;
package EN255 "Package for older EN255 data records"

end EN255;


within AixLib.Fluid.HeatPumps.ModularReversible.Data;
package TableDataSDF "Package with n-dimensional data based on SDF"
    extends Modelica.Icons.MaterialPropertiesPackage;
end TableDataSDF;


Use table based on three dimensions

within AixLib.Fluid.HeatPumps.ModularReversible.Data.TableDataSDF;
package TableData3D "Use table based on three dimensions"
    extends Modelica.Icons.MaterialPropertiesPackage;

end TableData3D;


within AixLib.Fluid.HeatPumps.ModularReversible.Data.TableDataSDF.TableData3D;
package VCLibPy "Package containing links to 3D-SDF tables generated by VCLibPy"
  extends Modelica.Icons.MaterialPropertiesPackage;

end VCLibPy;


within AixLib.Fluid.HeatPumps.ModularReversible.Examples;
model AirToWater2D_OneRoomRadiator
  "Reversible heat pump with EN 2D data connected to a simple room model with radiator"
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.Examples.BaseClasses.PartialOneRoomRadiator(
    mEva_flow_nominal=heaPum.mEva_flow_nominal,
    mCon_flow_nominal=heaPum.mCon_flow_nominal,
    sin(nPorts=1, redeclare package Medium = MediumAir),
    booToReaPumEva(realTrue=heaPum.mEva_flow_nominal),
    pumHeaPumSou(
      dp_nominal=heaPum.dpEva_nominal,
      redeclare package Medium = MediumAir),
    sou(use_T_in=true,
      redeclare package Medium = MediumAir),
    pumHeaPum(dp_nominal=heaPum.dpCon_nominal));

  AixLib.Fluid.HeatPumps.ModularReversible.AirToWaterTableData2D
    heaPum(
    redeclare package MediumCon = MediumWat,
    redeclare package MediumEva = MediumAir,
    TCon_start=TRadSup_nominal,
    QHea_flow_nominal=Q_flow_nominal,
    use_intSafCtr=true,
    dpCon_nominal(displayUnit="Pa") = 2000,
    dpEva_nominal(displayUnit="Pa") = 200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TConHea_nominal=rad.TRad_nominal,
    TEvaHea_nominal=283.15,
    TConCoo_nominal=oneRooRadHeaPumCtr.TRadMinSup,
    TEvaCoo_nominal=303.15,
    redeclare
      AixLib.Fluid.HeatPumps.ModularReversible.Data.TableData2D.EN14511.Vitocal251A08
      datTabHea,
    redeclare
      AixLib.Fluid.Chillers.ModularReversible.Data.TableData2D.EN14511.Vitocal251A08
      datTabCoo,
    redeclare
      AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021
      safCtrPar(
      use_minOnTime=true,
      minOnTime=300,
      use_minOffTime=true,
      minOffTime=300,
      use_maxCycRat=true)) "Reversible heat pump based on 2D table data"
  extends Modelica.Icons.Example;

  Modelica.Blocks.Sources.Pulse TAirSouSte(
    amplitude=20,
    width=10,
    period=86400,
    offset=283.15,
    startTime=86400/2) if witCoo "Air source temperature step for cooling phase"
equation
  connect(heaPum.port_b2, sin.ports[1])
  connect(heaPum.port_a2, pumHeaPumSou.port_b)
  connect(heaPum.port_b1, pumHeaPum.port_a)
  connect(heaPum.port_a1, temRet.port_b)
  connect(oneRooRadHeaPumCtr.ySet, heaPum.ySet)
  connect(heaPum.hea, oneRooRadHeaPumCtr.hea)
  connect(TAirSouSte.y, sou.T_in)
end AirToWater2D_OneRoomRadiator;


within AixLib.Fluid.HeatPumps.ModularReversible.Examples;
model CarnotWithLosses_OneRoomRadiator
  "Reversible heat pump with Carnot approach connected to a simple room model with radiator"
  extends Examples.BaseClasses.PartialOneRoomRadiator(
    mEva_flow_nominal=heaPum.mEva_flow_nominal,
    mCon_flow_nominal=heaPum.mCon_flow_nominal,
    sin(nPorts=1),
    booToReaPumEva(realTrue=heaPum.mEva_flow_nominal),
    pumHeaPumSou(dp_nominal=heaPum.dpEva_nominal),
    pumHeaPum(dp_nominal=heaPum.dpCon_nominal));
  extends Modelica.Icons.Example;

  parameter Real perHeaLos=0.01
    "Percentage of heat losses in the heat exchangers to the nominal heating output";
  AixLib.Fluid.HeatPumps.ModularReversible.CarnotWithLosses heaPum(
    redeclare package MediumCon = MediumWat,
    redeclare package MediumEva = MediumWat,
    QHea_flow_nominal=Q_flow_nominal,
    use_rev=true,
    use_intSafCtr=true,
    TConHea_nominal=TRadSup_nominal,
    dTCon_nominal=TRadSup_nominal - TRadRet_nominal,
    dpCon_nominal(displayUnit="Pa") = 2000,
    CCon=5000,
    GConOut=perHeaLos*Q_flow_nominal/(TRadSup_nominal - temAmbBas.k),
    GConIns=20000,
    TEvaHea_nominal=sou.T,
    dTEva_nominal=5,
    dpEva_nominal(displayUnit="Pa") = 2000,
    CEva=5000,
    GEvaOut=perHeaLos*Q_flow_nominal/(temAmbBas.k - sou.T),
    GEvaIns=20000,
    TConCoo_nominal=oneRooRadHeaPumCtr.TRadMinSup,
    TEvaCoo_nominal=sou.T + 10,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021 safCtrPar,
    etaCarnot_nominal=0.4)
      "Reversible heat pump with losses and Carnot approach"
  Modelica.Blocks.Sources.Constant temAmbBas(final k(
      final unit="K",
      displayUnit="degC") = 291.15)
    "Ambient temperature in basement of building"
equation
  connect(heaPum.port_b2, sin.ports[1])
  connect(heaPum.port_a2, pumHeaPumSou.port_b)
  connect(heaPum.port_b1, pumHeaPum.port_a)
  connect(heaPum.port_a1, temRet.port_b)
  connect(temAmbBas.y, heaPum.TEvaAmb)
  connect(temAmbBas.y, heaPum.TConAmb)

  connect(oneRooRadHeaPumCtr.hea, heaPum.hea)
  connect(oneRooRadHeaPumCtr.ySet, heaPum.ySet)
end CarnotWithLosses_OneRoomRadiator;


Large scale water to water heat pump connected to a simple room model with radiator. This example demonstrates how to use the

  AixLib.Fluid.HeatPumps.ModularReversible.LargeScaleWaterToWater
  heat pump model. Please check the associated documentation for
  further information.

within AixLib.Fluid.HeatPumps.ModularReversible.Examples;
model LargeScaleWaterToWater_OneRoomRadiator
  "Large scale water to water heat pump connected to a simple room model with radiator"
  extends Modelica.Icons.Example;
  extends BaseClasses.PartialOneRoomRadiator(
    mEva_flow_nominal=heaPum.mEva_flow_nominal,
    mCon_flow_nominal=heaPum.mCon_flow_nominal,
    V=6*100*3,
    witCoo=true,
    mAirRoo_flow_nominal=V*1.2*6/3600*10,
    Q_flow_nominal=200000,
    sin(nPorts=1),
    booToReaPumEva(realTrue=heaPum.mEva_flow_nominal),
    pumHeaPumSou(dp_nominal=150000),
    pumHeaPum(dp_nominal=150000),
    sou(use_T_in=true));

  AixLib.Fluid.HeatPumps.ModularReversible.LargeScaleWaterToWater heaPum(
    allowDifferentDeviceIdentifiers=true,
    QHea_flow_nominal=Q_flow_nominal,
    use_intSafCtr=true,
    QCoo_flow_nominal=-Q_flow_nominal/2,
    TConHea_nominal=TRadSup_nominal,
    dpCon_nominal(displayUnit="Pa"),
    TEvaHea_nominal=sou.T,
    dpEva_nominal(displayUnit="Pa"),
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021
      safCtrPar,
    TConCoo_nominal=oneRooRadHeaPumCtr.TRadMinSup,
    TEvaCoo_nominal=sou.T + 30,
    redeclare
      AixLib.Fluid.HeatPumps.ModularReversible.Data.TableData2D.EN14511.WAMAK_WaterToWater_220kW
      datTabHea,
    redeclare
      AixLib.Fluid.Chillers.ModularReversible.Data.TableData2D.EN14511.Carrier30XWP1012_1MW
      datTabCoo)
    "Large scale water to water heat pump"
  Modelica.Blocks.Sources.Pulse TAirSouSte(
    amplitude=20,
    width=10,
    period=86400,
    offset=283.15,
    startTime=86400/2) if witCoo "Air source temperature step for cooling phase"
equation
  connect(heaPum.port_b2, sin.ports[1])
  connect(heaPum.port_a2, pumHeaPumSou.port_b)
  connect(heaPum.port_b1, pumHeaPum.port_a)
  connect(heaPum.port_a1, temRet.port_b)
  connect(oneRooRadHeaPumCtr.ySet, heaPum.ySet)
  connect(oneRooRadHeaPumCtr.hea, heaPum.hea)
  connect(TAirSouSte.y, sou.T_in)
end LargeScaleWaterToWater_OneRoomRadiator;


within AixLib.Fluid.HeatPumps.ModularReversible.Examples;
model Modular_OneRoomRadiator
  "Modular reversible heat pump connected to a simple room model with radiator"
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.Examples.BaseClasses.PartialOneRoomRadiator(
    mEva_flow_nominal=heaPum.mEva_flow_nominal,
    sin(nPorts=1, redeclare package Medium = MediumAir),
    pumHeaPumSou(
      dp_nominal=heaPum.dpEva_nominal,
      redeclare package Medium = MediumAir),
    sou(redeclare package Medium = MediumAir),
    booToReaPumEva(realTrue=heaPum.mEva_flow_nominal),
    pumHeaPum(dp_nominal=heaPum.dpCon_nominal));
  extends Modelica.Icons.Example;

  AixLib.Fluid.HeatPumps.ModularReversible.Modular heaPum(
    redeclare package MediumCon = MediumWat,
    redeclare package MediumEva = MediumAir,
    use_rev=true,
    allowDifferentDeviceIdentifiers=true,
    QHea_flow_nominal=Q_flow_nominal,
    redeclare model RefrigerantCycleInertia =
        AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Inertias.NoInertia,
    use_intSafCtr=true,
    TConHea_nominal=TRadSup_nominal,
    TConCoo_nominal=oneRooRadHeaPumCtr.TRadMinSup,
    dTCon_nominal=TRadSup_nominal - TRadRet_nominal,
    dpCon_nominal(displayUnit="Pa") = 2000,
    use_conCap=true,
    CCon=3000,
    GConOut=100,
    GConIns=1000,
    TEvaHea_nominal=sou.T,
    TEvaCoo_nominal=sou.T,
    dTEva_nominal=2,
    dpEva_nominal(displayUnit="Pa") = 200,
    use_evaCap=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare model RefrigerantCycleHeatPumpHeating =
      AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.ConstantCarnotEffectiveness(
        redeclare
          AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.NoFrosting
          iceFacCal,
        TAppCon_nominal=0,
        TAppEva_nominal=0),
    redeclare model RefrigerantCycleHeatPumpCooling =
      AixLib.Fluid.Chillers.ModularReversible.RefrigerantCycle.TableData2D(
        redeclare
          AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.NoFrosting
          iceFacCal,
        mCon_flow_nominal=heaPum.mCon_flow_nominal,
        mEva_flow_nominal=heaPum.mEva_flow_nominal,
        datTab=AixLib.Fluid.Chillers.ModularReversible.Data.TableData2D.EN14511.Vitocal251A08()),
    redeclare AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021 safCtrPar(
      use_TConOutHea=true,
      use_TEvaOutHea=false,
      use_antFre=true,
      TAntFre=275.15),
    QCoo_flow_nominal=-Q_flow_nominal*0.5)
      "Modular reversible heat pump"

  Modelica.Blocks.Sources.Constant temAmbBas(
    final k(
      final unit="K",
      displayUnit="degC") = 291.15)
    "Ambient temperature in basement of building"
equation
  connect(heaPum.port_b2, sin.ports[1])
  connect(heaPum.port_a2, pumHeaPumSou.port_b)
  connect(heaPum.port_b1, pumHeaPum.port_a)
  connect(heaPum.port_a1, temRet.port_b)
  connect(temAmbBas.y, heaPum.TConAmb)
  connect(heaPum.hea, oneRooRadHeaPumCtr.hea)
  connect(oneRooRadHeaPumCtr.ySet, heaPum.ySet)
end Modular_OneRoomRadiator;


within AixLib.Fluid.HeatPumps.ModularReversible;
package Examples "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


within AixLib.Fluid.HeatPumps.ModularReversible.Examples;
model TableData3D_OneRoomRadiator
  "Large scale water to water heat pump with 3D data"
  extends Modelica.Icons.Example;
  extends BaseClasses.PartialOneRoomRadiator(
    mEva_flow_nominal=heaPum.mEva_flow_nominal,
    mCon_flow_nominal=heaPum.mCon_flow_nominal,
    V=6*100*3,
    witCoo=true,
    mAirRoo_flow_nominal=V*1.2*6/3600*10,
    Q_flow_nominal=200000,
    sin(nPorts=1),
    booToReaPumEva(realTrue=heaPum.mEva_flow_nominal),
    pumHeaPumSou(dp_nominal=150000),
    pumHeaPum(dp_nominal=150000),
    sou(use_T_in=true),
    cooLoa(amplitude=0));

  Modular                                                         heaPum(
    redeclare package MediumCon = MediumWat,
    redeclare package MediumEva = MediumWat,
    use_rev=false,
    allowDifferentDeviceIdentifiers=true,
    dTCon_nominal=8,
    use_conCap=false,
    dTEva_nominal=5,
    use_evaCap=false,
    QHea_flow_nominal=Q_flow_nominal,
    use_intSafCtr=true,
    redeclare model RefrigerantCycleHeatPumpHeating =
        AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.TableData3D (
          extrapMethod=SDF.Types.ExtrapolationMethod.Hold, redeclare
          AixLib.Fluid.HeatPumps.ModularReversible.Data.TableDataSDF.TableData3D.VZH088AG
          datTab),
    TConHea_nominal=TRadSup_nominal,
    dpCon_nominal(displayUnit="kPa") = 40000,
    TEvaHea_nominal=sou.T,
    dpEva_nominal(displayUnit="kPa") = 40000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021
      safCtrPar,
    TConCoo_nominal=273.15,
    TEvaCoo_nominal=273.15)
    "Large scale water to water heat pump"
  Modelica.Blocks.Sources.Pulse TAirSouSte(
    amplitude=20,
    width=10,
    period=86400,
    offset=283.15,
    startTime=86400/2) if witCoo "Air source temperature step for cooling phase"
equation
  connect(heaPum.port_b2, sin.ports[1])
  connect(heaPum.port_a2, pumHeaPumSou.port_b)
  connect(heaPum.port_b1, pumHeaPum.port_a)
  connect(heaPum.port_a1, temRet.port_b)
  connect(oneRooRadHeaPumCtr.ySet, heaPum.ySet)
  connect(TAirSouSte.y, sou.T_in)
end TableData3D_OneRoomRadiator;


within AixLib.Fluid.HeatPumps.ModularReversible.Examples;
model VCLibAirToWater_OneRoomRadiator
  "Air to water heat pump with 3D data based on VCLib"
  extends Modelica.Icons.Example;
  extends BaseClasses.PartialOneRoomRadiator(
    mEva_flow_nominal=heaPum.mEva_flow_nominal,
    mCon_flow_nominal=heaPum.mCon_flow_nominal,
    witCoo=false,
    sin(nPorts=1, redeclare package Medium = MediumAir),
    pumHeaPumSou(
    dp_nominal=heaPum.dpEva_nominal,
      redeclare package Medium = MediumAir),
    sou(use_T_in=true,
      redeclare package Medium = MediumAir),
    pumHeaPum(dp_nominal=heaPum.dpCon_nominal),
    booToReaPumEva(realTrue=heaPum.mEva_flow_nominal),
    oneRooRadHeaPumCtr(PIDHea(k=0.08)));

  Modular                                                         heaPum(
    redeclare package MediumCon = MediumWat,
    redeclare package MediumEva = MediumAir,
    use_rev=false,
    allowDifferentDeviceIdentifiers=true,
    dTCon_nominal=8,
    use_conCap=false,
    dTEva_nominal=3,
    use_evaCap=false,
    QHea_flow_nominal=Q_flow_nominal,
    use_intSafCtr=true,
    redeclare model RefrigerantCycleHeatPumpHeating =
        AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.TableData3D (
          redeclare
          AixLib.Fluid.HeatPumps.ModularReversible.Data.TableDataSDF.TableData3D.VCLibPy.VCLibVaporInjectionPhaseSeparatorR410A
          datTab),
    TConHea_nominal=TRadSup_nominal,
    dpCon_nominal(displayUnit="Pa") = 2000,
    TEvaHea_nominal=sou.T,
    dpEva_nominal(displayUnit="Pa") = 200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.Data.Wuellhorst2021
      safCtrPar,
    TConCoo_nominal=273.15,
    TEvaCoo_nominal=273.15)
    "Large scale water to water heat pump"
  Modelica.Blocks.Sources.Pulse TAirSouSte(
    amplitude=20,
    width=10,
    period=86400,
    offset=283.15,
    startTime=86400/2)           "Air source temperature step for cooling phase"
equation
  connect(heaPum.port_b2, sin.ports[1])
  connect(heaPum.port_a2, pumHeaPumSou.port_b)
  connect(heaPum.port_b1, pumHeaPum.port_a)
  connect(heaPum.port_a1, temRet.port_b)
  connect(oneRooRadHeaPumCtr.ySet, heaPum.ySet)
  connect(TAirSouSte.y, sou.T_in)
end VCLibAirToWater_OneRoomRadiator;


Helper model for the control of the system. Helper control model for the example

  AixLib.Fluid.HeatPumps.ModularReversible.Examples.BaseClasses.PartialOneRoomRadiator
  The control enables a PI control of the heat pumps compressor
  speed for both heating and cooling. Depending on a hysteresis,
  the heating or cooling mode is selected.
  If the radiator supply temperature drops below a critical
  value, the heat pump will turn to heating mode. This prohibits
  possible freezing or water condensation.

within AixLib.Fluid.HeatPumps.ModularReversible.Examples.BaseClasses;
model OneRoomRadiatorHeatPumpControl
  "Helper model for the control of the system"
  parameter Boolean witCoo=true
    "=true to simulate cooling behaviour";
  parameter Modelica.Units.SI.Temperature TRooSetHea=293.15
    "Room set temperature for heating";
  parameter Modelica.Units.SI.Temperature TRooSetCoo=296.15
    "Room set temperature for cooling";
  parameter Modelica.Units.SI.Temperature TRadMinSup=290.15
    "Minimal radiator supply temperature to avoid condensation effects";
  parameter Modelica.Units.SI.TemperatureDifference dTHysRoo=2
    "Temperature hysteresis for room control";
  Modelica.Blocks.Logical.Hysteresis hysHea(
    final uLow=TRooSetHea - dTHysRoo,
    final uHigh=TRooSetHea + dTHysRoo,
    final pre_y_start=false)
    "Hysteresis controller for heating"
  Modelica.Blocks.Logical.Hysteresis hysCoo(
    final uLow=TRooSetCoo - dTHysRoo,
    final uHigh=TRooSetCoo + dTHysRoo,
    final pre_y_start=false)
    if witCoo
    "Hysteresis controller for cooling"
  Modelica.Blocks.Sources.BooleanConstant conFal(final k=false)
                                                          if not witCoo
    "No cooling if witCoo=false"
  Modelica.Blocks.Logical.Not heaIsOn
    "If lower than hysteresis, heating demand"
  Modelica.Blocks.Logical.Switch swiHeaCooYSet if witCoo
    "Switch ySet for heating and cooling"
  Modelica.Blocks.Continuous.LimPID PIDCoo(
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    k=0.01,
    Ti=800,
    yMax=1,
    yMin=0.3,
    I(use_reset=true, reset=booPasThrCoo.y)) if witCoo
              "PI control for cooling, inverse"
  Modelica.Blocks.Continuous.LimPID PIDHea(
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    k=0.01,
    Ti=800,
    yMax=1,
    yMin=0.3,
    I(use_reset=true, reset=hysHea.y))
              "PI control for heating"
  Modelica.Blocks.Sources.Constant constTSetRooHea(final k=TRooSetHea)
    "Room set point temperature for heating"
  Modelica.Blocks.Sources.Constant constTSetRooCoo(final k=TRooSetCoo) if witCoo
    "Room set point temperature for cooling"
  Modelica.Blocks.Sources.Constant constYSetZer(final k=0) "ySet equals zero"
  Modelica.Blocks.Logical.Switch swiYSet "If no demand, switch heat pump off"
  Modelica.Blocks.Logical.Switch swiSecForCoo
    "If in cooling mode, heat pump can't operate below condensing temperature of 15 °C"
  Modelica.Blocks.Logical.Hysteresis hysSecCoo(
    final uLow=TRadMinSup,
    final uHigh=TRadMinSup + 4,
    final pre_y_start=false)
            if witCoo
    "Hysteresis for avoiding temperatures below 15 °C when cooling"
  Modelica.Blocks.Sources.BooleanConstant conTru(final k=true)
                                                         if not witCoo
    "No cooling if witCoo=false, then no safety is necessary"
  Modelica.Blocks.Interfaces.RealInput TRooMea(unit="K", displayUnit="degC")
                                               "Room measurement temperature"
  Modelica.Blocks.Interfaces.RealInput TRadSup(displayUnit="degC", unit="K")
                                               "Radiator supply temperature"
  Modelica.Blocks.Interfaces.RealOutput ySet "Heat pump set speed"
  Modelica.Blocks.Interfaces.BooleanOutput hea if witCoo
    "Heat pump in normal mode"
  Modelica.Blocks.Logical.Or heaOrCooIsOn "Heating or cooling is on"

  Modelica.Blocks.Logical.Or cooValOrHea
    "Cooling safety control is used only when the device is not heating"
  Modelica.Blocks.Routing.BooleanPassThrough booPasThrCoo "Used for reset of PID"
equation
  connect(hysHea.y, heaIsOn.u)
  connect(swiHeaCooYSet.u2, hysCoo.y)
  connect(swiYSet.u2, heaOrCooIsOn.y)
  connect(heaIsOn.y, heaOrCooIsOn.u1)
  connect(PIDHea.u_s,constTSetRooHea. y)
  connect(PIDHea.y, swiHeaCooYSet.u3)
  connect(constTSetRooCoo.y,PIDCoo. u_m)
  connect(swiSecForCoo.u3, constYSetZer.y)
  connect(constYSetZer.y, swiYSet.u3)
  connect(hysCoo.u, TRooMea)
  connect(TRooMea, hysHea.u)
  connect(swiSecForCoo.y, ySet)
  connect(hysSecCoo.u, TRadSup)
  connect(swiYSet.y, swiSecForCoo.u1)
  connect(PIDCoo.u_s, TRooMea)
  connect(PIDHea.u_m, TRooMea)
  connect(PIDCoo.y, swiHeaCooYSet.u1)
  connect(swiHeaCooYSet.y, swiYSet.u1)
  connect(heaOrCooIsOn.u2, conFal.y)
  connect(hysCoo.y, heaOrCooIsOn.u2)
  connect(heaIsOn.y, hea)
  connect(cooValOrHea.y, swiSecForCoo.u2)
  connect(cooValOrHea.u2, conTru.y)
  connect(cooValOrHea.u2, hysSecCoo.y)
  connect(heaIsOn.y, cooValOrHea.u1)
  if not witCoo then
    connect(PIDHea.y, swiYSet.u1)
  end if;
  connect(hysCoo.y, booPasThrCoo.u)
  connect(conFal.y, booPasThrCoo.u)
end OneRoomRadiatorHeatPumpControl;


Bases package for examples

within AixLib.Fluid.HeatPumps.ModularReversible.Examples;
package BaseClasses "Bases package for examples"
  extends Modelica.Icons.BasesPackage;

end BaseClasses;


within AixLib.Fluid.HeatPumps.ModularReversible.Examples.BaseClasses;
partial model PartialOneRoomRadiator
  "Simple room model with radiator, without a heat pump"
  replaceable package MediumAir =
      AixLib.Media.Air "Medium model for air";
  replaceable package MediumWat =
      AixLib.Media.Water "Medium model for water";

  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=20000
    "Nominal heat flow rate of radiator";
  parameter Modelica.Units.SI.Temperature TRadSup_nominal=273.15 + 50
    "Radiator nominal supply water temperature";
  parameter Modelica.Units.SI.Temperature TRadRet_nominal=273.15 + 45
    "Radiator nominal return water temperature";
  parameter Modelica.Units.SI.MassFlowRate mCon_flow_nominal=Q_flow_nominal/4200
      /5 "Heat pump nominal mass flow rate in condenser";
  parameter Modelica.Units.SI.MassFlowRate mEva_flow_nominal(min=Modelica.Constants.eps)
    "Heat pump nominal mass flow rate in evaporator";
  parameter Modelica.Units.SI.Volume V=6*10*3 "Room volume";
  parameter Modelica.Units.SI.MassFlowRate mAirRoo_flow_nominal=V*1.2*6/3600
    "Nominal mass flow rate of room air";
  parameter Modelica.Units.SI.HeatFlowRate QRooInt_flow=Q_flow_nominal/5
    "Internal heat gains of the room";
  parameter Boolean witCoo=true "=true to simulate cooling behaviour";
//------------------------------------------------------------------------------//

  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    redeclare package Medium = MediumAir,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=mAirRoo_flow_nominal,
    V=V)
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=
        Q_flow_nominal/40)
    "Thermal conductance with the ambient"
  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,
      smoothness=Modelica.Blocks.Types.Smoothness.ConstantSegments,
      table=[-6*3600, 0;
              8*3600, QRooInt_flow;
             18*3600, 0]) "Time table for internal heat gain"
  AixLib.Fluid.HeatExchangers.Radiators.RadiatorEN442_2 rad(
    redeclare package Medium = MediumWat,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    Q_flow_nominal=Q_flow_nominal,
    T_a_nominal=TRadSup_nominal,
    T_b_nominal=TRadRet_nominal,
    m_flow_nominal=mCon_flow_nominal,
    T_start=TRadSup_nominal) "Radiator"
  AixLib.Fluid.Sensors.TemperatureTwoPort temSup(
    redeclare package Medium = MediumWat,
    m_flow_nominal=mCon_flow_nominal,
    T_start=TRadSup_nominal) "Supply water temperature"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temRoo
    "Room temperature"

//----------------------------------------------------------------------------//

  replaceable AixLib.Fluid.Movers.Preconfigured.FlowControlled_m_flow pumHeaPum(
    redeclare package Medium = MediumWat,
    m_flow_nominal=mCon_flow_nominal,
    m_flow_start=mCon_flow_nominal,
    T_start=TRadSup_nominal,
    use_inputFilter=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for radiator side"
//----------------------------------------------------------------------------//

  AixLib.Fluid.Sensors.TemperatureTwoPort temRet(
    redeclare package Medium = MediumWat,
    m_flow_nominal=mCon_flow_nominal,
    T_start=TRadSup_nominal) "Return water temperature"

//------------------------------------------------------------------------------------//

  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(filNam=
    Modelica.Utilities.Files.loadResource(
      "modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"))
    "Weather data reader"
  AixLib.BoundaryConditions.WeatherData.Bus weaBus "Weather data bus"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TOut
    "Outside temperature"

//--------------------------------------------------------------------------------------//

  AixLib.Fluid.Movers.Preconfigured.FlowControlled_m_flow pumHeaPumSou(
    redeclare package Medium = MediumWat,
    m_flow_start=mEva_flow_nominal,
    m_flow_nominal=mEva_flow_nominal,
    use_inputFilter=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for heat pump source side"
  Modelica.Blocks.Math.BooleanToReal booToReaPumCon(
    realTrue=mCon_flow_nominal,
    y(start=0)) "Pump signal"
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = MediumWat,
    T=281.15,
    nPorts=1) "Fluid source on source side"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = MediumWat,
    T=283.15) "Fluid sink on source side"
  AixLib.Fluid.Sources.Boundary_pT preSou(
    redeclare package Medium = MediumWat,
    T=TRadSup_nominal,
    nPorts=1)
    "Source for pressure and to account for thermal expansion of water"

  Modelica.Blocks.Math.BooleanToReal booToReaPumEva(realTrue=1, y(start=0))
    "Pump signal"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaCoo if witCoo
    "Prescribed heat flow to trigger cooling"
  Modelica.Blocks.Sources.Pulse cooLoa(
    amplitude=Q_flow_nominal/2,
    width=10,
    period=86400,
    startTime=86400/2) if witCoo "Cooling load with step"
  AixLib.Fluid.HeatPumps.ModularReversible.Examples.BaseClasses.OneRoomRadiatorHeatPumpControl oneRooRadHeaPumCtr(
    final witCoo=witCoo)
    "Control block for single room heat pump control"
  Modelica.Blocks.Sources.BooleanConstant conPumAlwOn(final k=true)
    "Let the pumps always run, due to inertia of the heat pump"
equation
  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(weaDat.weaBus, weaBus)
  connect(weaBus.TDryBul, TOut.T)
  connect(TOut.port, theCon.port_a)
  connect(pumHeaPum.port_b, temSup.port_a)
  connect(temRet.port_a, rad.port_b)
  connect(booToReaPumCon.y, pumHeaPum.m_flow_in)
  connect(sou.ports[1], pumHeaPumSou.port_a)
  connect(preSou.ports[1], temRet.port_b)
  connect(booToReaPumEva.y, pumHeaPumSou.m_flow_in)

  connect(cooLoa.y, preHeaCoo.Q_flow)
  connect(preHeaCoo.port, heaCap.port)
  connect(conPumAlwOn.y, booToReaPumEva.u)
  connect(conPumAlwOn.y, booToReaPumCon.u)
  connect(oneRooRadHeaPumCtr.TRadSup, temSup.T)
  connect(oneRooRadHeaPumCtr.TRooMea, temRoo.T)
end PartialOneRoomRadiator;


Carnot COP with a constant Carnot effectiveness. This model uses a constant Carnot effectiveness
  to compute the efficiency of the heat pump.

within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle;
model ConstantCarnotEffectiveness "Carnot COP with a constant Carnot effectiveness"
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle(
      useInHeaPum=true,
      PEle_nominal=QHea_flow_nominal / COP_nominal,
      devIde="ConstantCarnotEffectiveness");
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialCarnot(
     TAppCon_nominal=if cpCon < 1500 then 5 else 2,
     TAppEva_nominal=if cpEva < 1500 then 5 else 2,
     final useForChi=false,
     final QEva_flow_nominal=PEle_nominal-QHea_flow_nominal,
     final QCon_flow_nominal=QHea_flow_nominal,
     constPEle(final k=PEle_nominal));
  parameter Real COP_nominal(
    min=0,
    final unit="1") = etaCarnot_nominal*(TCon_nominal + TAppCon_nominal)/(
    TCon_nominal + TAppCon_nominal - (TEva_nominal - TAppEva_nominal))
    "Nominal coefficient of performance";
equation

  connect(pasThrYMea.u, sigBus.yMea)
  if useInHeaPum then
    connect(pasThrTCon.u, sigBus.TConOutMea)
    connect(pasThrTEva.u, sigBus.TEvaOutMea)
  else
    connect(pasThrTCon.u, sigBus.TEvaOutMea)
    connect(pasThrTEva.u, sigBus.TConOutMea)
  end if;
  connect(swiPEle.y, redQCon.u2)
  connect(swiPEle.y, PEle)
  connect(swiQUse.u2, sigBus.onOffMea)
  connect(swiPEle.u2, sigBus.onOffMea)
  connect(swiPEle.y, feeHeaFloEva.u1)
  connect(feeHeaFloEva.u2, swiQUse.y)
end ConstantCarnotEffectiveness;


within AixLib.Fluid.HeatPumps.ModularReversible;
package RefrigerantCycle "Package for heat pupmp refrigerant cycle modules"
   extends Modelica.Icons.MaterialPropertiesPackage;
end RefrigerantCycle;


Performance data based on condenser outlet and evaporator inlet temperature. This model uses two-dimensional table data typically given
  by manufacturers as required by e.g. European Norm 14511
  or ASHRAE 205 to calculate
  QCon_flow and PEle.

within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle;
model TableData2D "Performance data based on condenser outlet and evaporator inlet temperature"
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle(
    final devIde=datTab.devIde,
    PEle_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2(
        tabIdePEle,
        TCon_nominal,
        TEva_nominal) * scaFac);
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialTableData2D(
    final useInRevDev=not useInHeaPum,
    scaFac=QHea_flow_nominal/QHeaNoSca_flow_nominal,
    final valTabQEva_flow = valTabQCon_flow .- valTabPEle,
    final valTabQCon_flow = {{tabQUse_flow.table[j, i] for i in 2:numCol} for j in 2:numRow},
    final mCon_flow_max=max(valTabQCon_flow) * scaFac / cpCon / dTMin,
    final mCon_flow_min=min(valTabQCon_flow) * scaFac / cpCon / dTMax,
    final mEva_flow_min=min(valTabQEva_flow) * scaFac / cpEva / dTMax,
    final mEva_flow_max=max(valTabQEva_flow) * scaFac / cpEva / dTMin,
    mEva_flow_nominal=datTab.mEva_flow_nominal*scaFac,
    mCon_flow_nominal=datTab.mCon_flow_nominal*scaFac,
    final use_TConOutForTab=datTab.use_TConOutForTab,
    final use_TEvaOutForTab=datTab.use_TEvaOutForTab,
    tabQUse_flow(final table=datTab.tabQCon_flow),
    tabPEle(final table=datTab.tabPEle));
  parameter Modelica.Units.SI.HeatFlowRate QHeaNoSca_flow_nominal=Modelica.Blocks.Tables.Internal.getTable2DValueNoDer2(
        tabIdeQUse_flow,
        TCon_nominal,
        TEva_nominal)
    "Unscaled nominal heating capacity "

  replaceable parameter AixLib.Fluid.HeatPumps.ModularReversible.Data.TableData2D.GenericHeatPump datTab
    "Data Table of HP"

equation

  connect(scaFacTimPel.y, PEle)
  connect(scaFacTimPel.y, redQCon.u2)
  connect(yMeaTimScaFac.u1, sigBus.yMea)
  connect(reaPasThrTEvaOut.u, sigBus.TEvaOutMea)
  connect(reaPasThrTEvaIn.u, sigBus.TEvaInMea)
  connect(reaPasThrTConIn.u, sigBus.TConInMea)
  connect(reaPasThrTConOut.u, sigBus.TConOutMea)
  if useInHeaPum then
    connect(reaPasThrTConOut.y, tabPEle.u1)
    connect(reaPasThrTConIn.y, tabPEle.u1)
    connect(reaPasThrTConIn.y, tabQUse_flow.u1)
    connect(reaPasThrTConOut.y, tabQUse_flow.u1)
    connect(reaPasThrTEvaOut.y, tabPEle.u2)
    connect(reaPasThrTEvaOut.y, tabQUse_flow.u2)
    connect(reaPasThrTEvaIn.y, tabPEle.u2)
    connect(reaPasThrTEvaIn.y, tabQUse_flow.u2)
  else
    connect(reaPasThrTEvaOut.y, tabPEle.u1)
    connect(reaPasThrTEvaOut.y, tabQUse_flow.u1)
    connect(reaPasThrTEvaIn.y, tabPEle.u1)
    connect(reaPasThrTEvaIn.y, tabQUse_flow.u1)
    connect(reaPasThrTConOut.y, tabPEle.u2)
    connect(reaPasThrTConOut.y, tabQUse_flow.u2)
    connect(reaPasThrTConIn.y, tabPEle.u2)
    connect(reaPasThrTConIn.y, tabQUse_flow.u2)
  end if;
  connect(scaFacTimPel.y, feeHeaFloEva.u1)
  connect(scaFacTimQUse_flow.y, feeHeaFloEva.u2)
end TableData2D;


3D data: condenser temperature, evaporator temperature, compressor speed. This model uses three-dimensional table data possibly given
  by manufacturers or estimated using other tools, such as VCLibPy, to calculate
  QCon_flow and PEle.

within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle;
model TableData3D
  "3D data: condenser temperature, evaporator temperature, compressor speed"
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle(
    final devIde=datTab.devIde,
    PEle_nominal=evaluate(extTabPEle, uOrdSca_nominal, interpMethod, extrapMethod)
      * scaFac);
  extends
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialTableData3D(
    facGain(final k=datTab.facGai),
    final u_nominal={TCon_nominal,TEva_nominal,y_nominal},
    final useInRevDev=not useInHeaPum,
    scaFac=QHea_flow_nominal/QHeaNoSca_flow_nominal,
    final use_TConOutForTab=datTab.use_TConOutForTab,
    final use_TEvaOutForTab=datTab.use_TEvaOutForTab,
    nDTabPEle(
      final filename=datTab.filename,
      final dataset=datTab.datasetPEle,
      final dataUnit=datTab.dataUnitPEle,
      final scaleUnits=datTab.scaleUnitsPEle),
    nDTabQUse_flow(
      final filename=datTab.filename,
      final dataset=datTab.datasetQCon_flow,
      final dataUnit=datTab.dataUnitQCon_flow,
      final scaleUnits=datTab.scaleUnitsQCon_flow),
    ordInp(final outOrd=datTab.outOrd));
  parameter Modelica.Units.SI.HeatFlowRate QHeaNoSca_flow_nominal=evaluate(
        extTabQUse_flow, uOrdSca_nominal, interpMethod, extrapMethod)
    "Unscaled nominal heating capacity "

  replaceable parameter
    AixLib.Fluid.HeatPumps.ModularReversible.Data.TableDataSDF.TableData3D.Generic
    datTab constrainedby
    AixLib.Fluid.HeatPumps.ModularReversible.Data.TableDataSDF.TableData3D.Generic
    "Data Table of HP"

initial algorithm
  assert(datTab.nDim == 3, "In " + getInstanceName() + ":
    The provided SDF record contains more than three dimensions", AssertionLevel.error);

equation

  connect(scaFacTimPel.y, PEle)
  connect(scaFacTimPel.y, redQCon.u2)
  connect(reaPasThrTEvaOut.u, sigBus.TEvaOutMea)
  connect(reaPasThrTEvaIn.u, sigBus.TEvaInMea)
  connect(reaPasThrTConIn.u, sigBus.TConInMea)
  connect(reaPasThrTConOut.u, sigBus.TConOutMea)
  if useInHeaPum then
    connect(reaPasThrTConIn.y, mux.u[1])
    connect(reaPasThrTConOut.y, mux.u[1])
    connect(reaPasThrTEvaOut.y, mux.u[2])
    connect(reaPasThrTEvaIn.y, mux.u[2])
  else
    connect(reaPasThrTEvaOut.y, mux.u[1])
    connect(reaPasThrTEvaIn.y, mux.u[1])
    connect(reaPasThrTConOut.y, mux.u[2])
    connect(reaPasThrTConIn.y, mux.u[2])
  end if;
  connect(scaFacTimPel.y, feeHeaFloEva.u1)
  connect(scaFacTimQUse_flow.y, feeHeaFloEva.u2)
  connect(mux.u[3], sigBus.yMea)

  connect(booToRea.u, sigBus.onOffMea)
end TableData3D;


Placeholder to disable heating. 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.

within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses;
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
      AixLib.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;


within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle;
package BaseClasses "Package with partial classes of Performance Data"
end BaseClasses;


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

within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses;
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/
        AixLib.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;


Partial model to allow selection of only heat pump options. Partial refrigerant cycle model for heat pumps.
  It adds the specification for frosting calculation
  and restricts to the intended choices under
  choicesAllMatching.

within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses;
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;


Partial model of refrigerant cycle. 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.

within AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses;
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 AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.NoFrosting iceFacCal
  constrainedby
    AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.BaseClasses.PartialIcingFactor
    "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"
  AixLib.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;