within AixLib.Fluid.HeatPumps.ModularReversible;
package UsersGuide
  "User's Guide for modular reversible heat pump and chiller models"
  extends Modelica.Icons.Information;

  They may be on/off and inverter driven;

  They are able reverse the operation between heating to cooling;

  They operate in a limited characteristic range (operational envelope);

  The design depends on various nominal boundary conditions;

  Safety features are part of their control sequence.

  To what extend all these effects need to be modeled depends
  on the simulation aim. Sometimes a simple Carnot approach is
  sufficient, sometimes a more detailed performance data
  and a realistic control behaviour is required.

  The modular approach allows to disable any irrelevant features,
  select readily made functional modules, and
  easily add new model modules.
  Relevant components are declared as replaceable.
  Replaceable models are constrainedby partial models
  which one is free to extend. Thus, new model approaches can be inserted
  into the framework of the modular reversible model approach.
  For users not familiar with replaceable models,
  there are readily assembled models as well.

Package and model structure

  This section explains the inheritance and model package structure
  to help navigate through all options and to check out the
  detailed documentation of each model for further information.

  All modular heat pump or chiller models base on the model

  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle.
  This partial model declares all common interfaces, parameters, etc.

Heat pump models

For heat pumps, the model
AixLib.Fluid.HeatPumps.ModularReversible.Modular extends the partial model and adds
the connector hea to choose
between the operation modes of the heat pump:

hea = true: Main operation mode (heating)

hea = false: Reversed operation mode (cooling)

Furthermore, the refrigerant cycle is redeclared to use the one for
the heat pump

AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantCycle.
This refrigerant cycle in turn contains replaceable models for the
heating and cooling operation of the heat pump.
Available modules can be found in the package:

AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle
For more information on the refrigerant cycle models, refer
to the section Refrigerant cycle models.

  There are a number of preconfigured models provided in the package.
  Please check out the documentation of each approach
  to check if this approach is suitable.

  AixLib.Fluid.HeatPumps.ModularReversible.LargeScaleWaterToWater

  AixLib.Fluid.HeatPumps.ModularReversible.AirToWaterTableData2D

  AixLib.Fluid.HeatPumps.ModularReversible.CarnotWithLosses

Chiller models

For chillers, the model
AixLib.Fluid.Chillers.ModularReversible.Modular extends the partial model and adds
the connector coo to choose
between the operation mode of the chiller:

coo = true: Main operation mode (cooling)

coo = false: Reversed operation mode (heating)

Furthermore, the refrigerant cycle is redeclared to use the one for
the chiller

AixLib.Fluid.Chillers.ModularReversible.BaseClasses.RefrigerantCycle.
This refrigerant cycle in turn contains replaceable models for the
cooling and heating operation of the chiller.
Available modules can be found in the package:

AixLib.Fluid.Chillers.ModularReversible.RefrigerantCycle.
For more information on the refrigerant cycle models, refer
to the section Refrigerant cycle models.

  There are a number of preconfigured models provided in the package.
  Please check out the documentation of each approach
  to check if this approach is suitable.

  AixLib.Fluid.Chillers.ModularReversible.LargeScaleWaterToWater

  AixLib.Fluid.Chillers.ModularReversible.CarnotWithLosses

Naming and reversible operation

Simulating reversible heat pumps and chillers can get confusing, as the same heat
exchanger model has to be used for both condensation and evaporation.

In most cases, heat pumps and chillers extract/exhaust heat from/to
an ambient source (air, soil, ground-water, etc.),
and, consuming electrical energy, provide heating or cooling
on the useful or load side.
For building applications, this useful / load side is inside the building.
The ambient heat is outside of the building, on the ambient / source side.
However, vapor compression machines can also be used to provide heating
and cooling simultaneously.
In this case, both sides provide utility to the energy system.
Hence, both sides are useful / load sides.

What always stays the same is that there is one heated fluid, and
one cooled fluid. Thus, specifying the nominal temperatures, users have
to specify the temperatures of both heated and cooled fluids, for both cooling
and heating operation. Note, that the heated fluid will be the cooled fluid in
reversed operation, and vice versa.

Because of this, we decided on a naming scheme which is based on the main operation
of the heat pump or chiller. The main operation of a heat pump is heating, for
a chiller, it is cooling. When reversed, the condenser becomes the evaporator and
vice versa. As renaming instances after translation is not possible, users always
have to think about the names con and eva in terms of
the main operation of the device.
This applies to the instance and variable names,
such as the heat exchangers con and eva,
as well as sensors such as TConOutMea and TEvaInMea,
or nominal conditions TConHea_nominal and TConCoo_nominal.

As the nominal temperatures may be used table-based performance data
and the operational envelope model,
users also have to think about the useful / load
and ambient / source side and how they translate to
heat pumps and chillers in both main and reversed operation
when adding new datasheets.
Thinking about the heated and cooled fluid helps.

The following tables summarizes the possible options.

      Heat pump main
      Heat pump reversed
      Chiller main
      Chiller reversed

      Heating / cooling?
      heating
      cooling
      cooling
      heating

      Table-data: column 1: useful / load side
      con
      con 1
      eva
      eva 2

      Table-data - row 1: ambient / source side
      eva
      eva 2
      con
      con 1

      Operational envelope - column 1: ambient / source side
      eva
      eva 2
      con
      con 1

      Operational envelope - column 2: useful / load side
      con
      con 1
      eva
      eva 2

Footnotes:

1 In reality, the condenser, e.g. a plate heat exchanger, of the main operation is used for evaporation.
2 In reality, the evaporator, e.g. fin-tube heat exchanger, of the main operation is used for condensation.

Connectors

  Aside from the aforementioned Boolean signals
  hea and coo, the following
  connectors are relevant to understanding how the model works:
  Compressor speed and the expandable bus connector.

  The ambient temperatures TEvaAmb and TConAmb are
  only relevant for the heat losses.
  The fluid ports are explained in more detail here:
AixLib.Fluid.UsersGuide.

Compressor speed

  The input ySet represents the relative compressor speed.
  To model both on/off and inverter controlled refrigerant machines,
  the compressor speed is normalised to a relative value between 0 and 1.
  To model heat pumps other than inverter driven,
  other signal limits can be used.

  If data is in Hz or similar, consider converting
  the input according to the maximum allowed value.

  We use the notation Set to indicate a set value.
  It may be modified by the safety control blocks which produces a signal
  with the Mea notation. For example, the compressor
  speed ySet is modified by the safety
  control block to yMea. If no safety violations
  occur, ySet equals yMea.

Expandable bus connector

  As the modular approach may require different information to model
  the refrigerant cycle depending on configuration, all available states and outputs
  are aggregated in the expandable bus connector sigBus.
  For instance, in order to control both chillers and heat pumps,
  both flow and return temperature are relevant.

Refrigerant cycle models

  A replaceable refrigerant cycle model for heating or
  cooling calculates the electrical power consumption
  PEle, condenser heat flow rate QCon_flow
  and evaporator heat flow rate QEva_flow based on the
  values in the sigBus.
  Heat pumps models extend

  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.BaseClasses.PartialHeatPumpCycle
  and chiller models extend

  AixLib.Fluid.Chillers.ModularReversible.RefrigerantCycle.BaseClasses.PartialChillerCycle.

  Currently, two modules for refrigerant cycle are implemented.
  First, the
  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.ConstantCarnotEffectiveness model
  uses the same equations as the Carnot models, i.e.
  AixLib.Fluid.HeatPumps.Carnot_y.
  Second, the
  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.TableData2D provides performance data
  based on different condenser outlet and evaporator inlet temperatures using 2D-tables.

  Two additional modules exist in the AixLib library.
  These approaches require the use of the SDF-library, as
  the compressor speed influences the model output as a third
  dimension. Currently, tables with more than two dimensions are not supported in
  the Modelica Standard Library.
  The first approach is similar to
  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.TableData2D
  approach buts adds the 3rd dimension of compressor speed.
  The second approach is based on white-box stationary Python models
  for closed-loop refrigerant cycles. The model has been empirically
  validated and can take up to n-dimensions.
  If your simulation aim requires more detailed data, be sure
  to check out the models in the AixLib.

Parameterization and naming

  The refrigerant cycle models will output
  varying heat flow rates and electrical power consumptions.
  This is based on the fact that refrigerant cycle performance
  depend heavily on the boundary conditions.

  Still, the user needs to size the device or the system
  according to some reference points. Accordingly, we follow
  the basic AixLib concept for nominal conditions, explained in
  detail here:
  AixLib.Fluid.UsersGuide

  The nominal heat flow rate of the device is distinct for
  heat pumps and chillers.
  For heat pumps, it is the nominal
  condenser heat flow rate QHea_flow_nominal.
  For chillers, it is the nominal
  evaporator heat flow rate QCoo_flow_nominal (negative).
  This nominal heat flow rate is only valid at the
  nominal conditions. Whether parameters influence the nominal heat flow rates
  depends on the model approach used to estimate the heat flow rate and efficiencies.
  Typically, at least nominal source and sink temperatures will influence the
  nominal conditions:

  condenser temperature TCon_nominal,

  evaporator temperature TEva_nominal,

  Depending on the model in use, this may be inlet or outlet.

  Another example would be inverter driven devices.
  Here, the compressor speed influences the
  refrigerant mass flow rate and compressor efficiencies.
  If the performance data is dependent on the compressor speed,
  y_nominal influences the nominal efficiencies.
  In such cases, specifying additional nominal parameters will
  be nessary.

  Using the nominal conditions and the specified heat flow rate,
  the nominal electrical power consumption PEle_nominal is calculated.
  As reversible devices have typically a four-way-valve and a single
  compressor, you have to make sure that the values for PEle_nominal
  are similar between heating and cooling. The pre-configured models
  warn about deviations if they are too large.

  To change the capacity of the model, users should change
  QHea_flow_nominal for heating operation and
  QCoo_flow_nominal for cooling operation. This will then also
  update the electricity use PEle_nominal.

  For models with table-based performance curves, changing these values will also scale
  the design mass flow rates and pressure drops.
  The documentation of

  AixLib.Fluid.HeatPumps.ModularReversible.TableData2D
  further explains the scaling.

Safety controls

  Refrigerant machines contain internal safety controls,
  prohibiting operations in possibly unsafe points.
  All ModularReversible models account for those.
  All options can be disabled as described in the model description

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

  An important safety control with regard to
  system interaction is the operation envelope. Read the documentation of

  AixLib.Fluid.HeatPumps.ModularReversible.Controls.Safety.BaseClasses.PartialOperationalEnvelope
  for more information on this model and how it affects device operation.

Refrigerant cycle inertia

  The refrigerant cycle models
  are based on stationary data points, any inertia
  (mass and thermal) of components inside the refrigerant cycle
  (compressor, pipes, expansion valve, fluid, etc.) is neglected.
  To overcome this issue, replaceable SISO blocks that are connected to the
  output of the refrigerant cycle (instance refCyc)
  can approximate the refrigerant cycle inertia.

  The package

  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Inertias
  contains implemented inertia models. In the
  contribution, an empirical
  validation showed that a third-order critical damping element
  fits the inertia most closely. At the same time, models in literature
  often use first-order delay blocks.
  Additionally, higher-order elements require more computation time.
  At the end, the requirements on the analysis will define the required level
  of detail of the model.

  The effect of the inertia can be removed by setting NoInertia.

  If a user finds in real data that another approach might be better suited
  (e.g. a deadband), then the model

  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Inertias.BaseClasses.PartialInertia

  can be extended to implement a costum model.

Frost

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

  QEva_flow = iceFac * (QConNoIce_flow - PEle)

  With iceFac as a relative value between 0 and 1:

iceFac = kA/kA_noIce

Finally, the energy balance must still hold:

QCon_flow = PEle + QEva_flow

  Different options can be selected for the modeling of the icing factor, or
  a custom model can be implemented by extending

  AixLib.Fluid.HeatPumps.ModularReversible.RefrigerantCycle.Frosting.BaseClasses.PartialIcingFactor

  Note however, that this only simulates the efficiency reduction
  due to frost. If a user-provided frost module enables the simulation
  of a defrost cycle, the user needs to implement the corresponding external controls.
  The iceFac approach was already used by Vering et al., 2021,
  to account for reverse cycle defrost based on validated literature-data.
  However, as no empirical validation was performed, the model was not
  added to the AixLib library.

Heat losses

  Most refrigerant cycle models that calculate
  PEle, QCon_flow, and QEva_flow
  assume the device to be adiabatic, following the equation:

  QCon_flow = PEle + QEva_flow

  Depending on the application, one may need to model
  the heat losses to the ambient, as those may
  impact the overall efficiency of the heat pump or chiller.
  Thus, the heat exchangers in the models adds
  thermal capacities to the adiabatic heat exchanger.
  The parameterization may be challenging, as datasheets
  do not contain parameters for the required values.
  Besides empirical calibration, simplified
  assumptions (e.g. 2% heat loss) may be used to
  parameterize the required values.
  For more information, see the description of

  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.EvaporatorCondenserWithCapacity

References

  Fabian Wuellhorst, David Jansen, Philipp Mehrfeld and Dirk Müller.

  A Modular Model of Reversible Heat Pumps and Chillers for System Applications.

  Proceedings of 14th Modelica Conference 2021. Linköping, Sweden, September, 2021.

  doi:10.3384/ecp21181561.

Christian Vering, Fabian Wüllhorst, Philipp Mehrfeld and Dirk Müller.

Towards an integrated design of heat pump systems: Application of process intensification using two-stage optimization.

Energy Conversion and Management, Volume 250, 2021.

doi:10.1016/j.enconman.2021.114888.

"));

end UsersGuide;


Calculate the COP or EER of a device. This model is used to calculate the COP or the EER of a device. As
  the electrical power can get zero, a lower boundary is used to
  avoid division by zero.

within AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses;
model CalculateEfficiency
  "Calculate the COP or EER of a device"
  extends Modelica.Blocks.Icons.Block;
  parameter Modelica.Units.SI.Power PEleMin(min=Modelica.Constants.eps)
    "If eletrical power consumption falls below this value, COP will be set to zero";

  Modelica.Blocks.Interfaces.RealInput PEle(final unit="W", displayUnit="W")
    "Electrical power consumed by the system"
  Modelica.Blocks.Interfaces.RealInput QUse_flow(final unit="W", displayUnit="W")
    "Useful heat flow"
  Modelica.Blocks.Interfaces.RealOutput COP(min=0, final unit="1")
    "Output for calculated COP value"
  Modelica.Blocks.Logical.Hysteresis hys(
    uLow=PEleMin,
    uHigh=PEleMin*1.1)
    "Hysteresis to switch between calculation and no calculation"
  Modelica.Blocks.Interfaces.BooleanInput hea
    "=true for heating, false for cooling"
  Modelica.Blocks.Interfaces.RealOutput EER(min=0, final unit="1")
    "Output for calculated EER value"
  AixLib.Utilities.Math.InverseXRegularized invXReg(delta=PEleMin)
    "Inverse of electrical power"
protected
  Modelica.Blocks.Math.Product  copCom "Computes COP"
  Modelica.Blocks.Logical.Switch swi
    "Outputs COP or zero depending on electricity use"
  Modelica.Blocks.Sources.Constant zer(final k=0) "Outputs zero"
  Modelica.Blocks.Logical.Switch swiCoo "Switch for cooling and EER calculation"
  Modelica.Blocks.Logical.Switch swiHea "Switch for heating and COP calculation"
protected
  Modelica.Blocks.Math.Abs absQEva_flow
    "Negates possibly negative usefule flow rates"
initial equation
  assert(PEleMin > 0,
    "In " + getInstanceName() + ": PEleMin must be greater than zero. Disable efficiency calculation using
    calEff=false to debug why PEle_nominal is lower than zero.",
    AssertionLevel.error);

equation

  connect(hys.u, PEle)
  connect(hys.y, swi.u2)
  connect(copCom.y, swi.u1)
  connect(zer.y, swi.u3)
  connect(swiCoo.y, EER)
  connect(swiHea.y, COP)
  connect(hea, swiHea.u2)
  connect(swi.y, swiHea.u1)
  connect(swiHea.u3, zer.y)
  connect(hea, swiCoo.u2)
  connect(swi.y, swiCoo.u3)
  connect(zer.y, swiCoo.u1)
  connect(QUse_flow, absQEva_flow.u)
  connect(absQEva_flow.y, copCom.u1)
  connect(copCom.u2, invXReg.y)
  connect(invXReg.u, PEle)
end CalculateEfficiency;


Evaporaotr or condenser model with added capacity for heat losses to the ambient. Model for an evaporator or condenser with the use of a capacity to
  simulate heat losses.

within AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses;
model EvaporatorCondenserWithCapacity
  "Evaporaotr or condenser model with added capacity for heat losses to the ambient"
  extends AixLib.Fluid.Interfaces.TwoPortHeatMassExchanger(
    redeclare final AixLib.Fluid.MixingVolumes.MixingVolume vol(final V=
          m_flow_nominal*tau/rho_default,
    final prescribedHeatFlowRate=true));

  parameter Boolean isCon "=true for condenser, false for evaporator"
  parameter Boolean use_cap=true
    "False if capacity and heat losses are neglected"
  parameter Modelica.Units.SI.HeatCapacity C
    "Capacity of heat exchanger, set to zero to ignore its dry mass"
  parameter Modelica.Units.SI.Temperature TCap_start=Medium.T_default
    "Initial temperature of heat capacity"
  Modelica.Units.SI.ThermalConductance GOut
    "Exterior heat transfer coefficient,
    set to zero to ignore external heat loss but keep the dry mass"
  Modelica.Blocks.Interfaces.RealOutput GInn
    "Interior heat transfer coefficient"
  Modelica.Thermal.HeatTransfer.Components.Convection conIns if use_cap
    "Convection between the wall and the working fluid"
  Modelica.Thermal.HeatTransfer.Components.Convection conOut if use_cap
    "Convection and conduction between the wall and ambient air"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCap(
    final C=C,
    final T(final fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial),
        final start=TCap_start),
    final der_T(final fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial),
        start=0)) if use_cap "Heat Capacity"
  Modelica.Blocks.Sources.RealExpression heaLosInt(final y=GInn) if use_cap
    "Nominal heat loss coefficient to the interior"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_out if use_cap
    "Temperature and heat flow to the ambient"
  Modelica.Blocks.Sources.RealExpression heaLosExt(final y=GOut) if use_cap
    "Nominal heat loss coefficient to the exterior"

  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea(final alpha=0,
      final T_ref=293.15) "Heat flow rate of the condenser"
  Modelica.Blocks.Interfaces.RealInput Q_flow
    "Heat flow rate from the refrigerant to the medium"

  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senT
    "Temperature sensor for the condenser volume"
  Modelica.Blocks.Interfaces.RealOutput T(
    final unit="K",
    displayUnit="degC")
    "Temperature of the condenser volume"
equation
  connect(conIns.fluid, heaCap.port)
  connect(heaCap.port, conOut.solid)
  connect(conIns.Gc, heaLosInt.y)
  connect(conOut.fluid, port_out)
  connect(conOut.Gc, heaLosExt.y)
  connect(vol.heatPort, conIns.solid)
  connect(vol.heatPort, preHea.port)
  connect(preHea.Q_flow, Q_flow)
  connect(senT.port, vol.heatPort)
  connect(senT.T, T)
end EvaporatorCondenserWithCapacity;


Model with parameters for large scale water-to-water heat pump. This model provides declarations that auto-populate nominal mass flow rates
  and time constants (i.e. volumes) of the heat exchange based on
  the nominal electric power consumption of the chiller or heat pump.
  It is based on more than 20 datasets of water-to-water heat pumps
  from multiple manufacturers ranging from about 25 kW
  to 1 MW in nominal electric power consumption. The linear regressions with coefficients
  of determination above 91% give a good approximation of these
  parameters. Nevertheless, estimates for machines outside
  the given range should be checked for plausibility during simulation.

within AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses;
partial model LargeScaleWaterToWaterDeclarations
  "Model with parameters for large scale water-to-water heat pump"

  //Automatic calculation of mass flow rates and volumes of the evaporator
  // and condenser using linear regressions from data sheets of
  // heat pumps and chillers (water to water)

protected
  parameter Modelica.Units.SI.MassFlowRate autCalMMin_flow=0.3
    "Realistic mass flow minimum for simulation plausibility";
  parameter Modelica.Units.SI.Volume autCalVMin=0.003
    "Realistic volume minimum for simulation plausibility";

  parameter Modelica.Units.SI.MassFlowRate autCalMasEva_flow
    "Automatically calculated evaporator mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate autCalMasCon_flow
    "Automatically calculated condenser mass flow rate";
  parameter Modelica.Units.SI.Volume autCalVEva
    "Automatically calculated evaporator volume";
  parameter Modelica.Units.SI.Volume autCalVCon
    "Automatically calculated condenser volume";

initial equation
  //Control and feedback for the auto-calculation of condenser and evaporator data
  assert(
    autCalMasEva_flow > autCalMMin_flow and autCalMasEva_flow < 90,
    "In " + getInstanceName() + ": Given nominal heat output for auto-calculation of
    evaporator and condenser data is outside the range of data sheets
    considered. Verify the auto-calculated mass flows.",
    level=AssertionLevel.warning);
  assert(
    autCalVEva > autCalVMin and autCalVEva < 0.43,
  "In " + getInstanceName() + ": Given nominal heat output for auto-calculation of evaporator
  and condenser data is outside the range of data sheets considered.
  Verify the auto-calculated volumes.",
    level=AssertionLevel.warning);
end LargeScaleWaterToWaterDeclarations;


within AixLib.Fluid.HeatPumps.ModularReversible;
package BaseClasses "Package with base classes for AixLib.Fluid.HeatPumps.ModularReversible"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


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;