Partial element transporting fluid between two ports without storage of mass or energy. This component transports fluid between its two ports, without storing mass or energy.
Energy may be exchanged with the environment though, e.g., in the form of work.
PartialTwoPortTransport is intended as base class for devices like orifices, valves and simple fluid machines.

within AixLib.Fluid.Interfaces;
partial model PartialTwoPortTransport
  "Partial element transporting fluid between two ports without storage of mass or energy"
  extends AixLib.Fluid.Interfaces.PartialTwoPort;

  // Advanced
  // Note: value of dp_start shall be refined by derived model,
  // based on local dp_nominal
  parameter Modelica.Units.SI.PressureDifference dp_start(displayUnit="Pa") = 0
    "Guess value of dp = port_a.p - port_b.p"
  parameter Medium.MassFlowRate m_flow_start = 0
    "Guess value of m_flow = port_a.m_flow"
  // Note: value of m_flow_small shall be refined by derived model,
  // based on local m_flow_nominal
  parameter Medium.MassFlowRate m_flow_small
    "Small mass flow rate for regularization of zero flow"

  // Diagnostics
  parameter Boolean show_T = true
    "= true, if temperatures at port_a and port_b are computed"
  parameter Boolean show_V_flow = true
    "= true, if volume flow rate at inflowing port is computed"

  // Variables
  Medium.MassFlowRate m_flow(
     min=if allowFlowReversal then -Modelica.Constants.inf else 0,
     start = m_flow_start) "Mass flow rate in design flow direction";
  Modelica.Units.SI.PressureDifference dp(start=dp_start, displayUnit="Pa")
    "Pressure difference between port_a and port_b (= port_a.p - port_b.p)";

  Modelica.Units.SI.VolumeFlowRate V_flow=m_flow/
      Modelica.Fluid.Utilities.regStep(
      m_flow,
      Medium.density(Medium.setState_phX(
        p=port_a.p,
        h=inStream(port_a.h_outflow),
        X=inStream(port_a.Xi_outflow))),
      Medium.density(Medium.setState_phX(
        p=port_b.p,
        h=inStream(port_b.h_outflow),
        X=inStream(port_b.Xi_outflow))),
      m_flow_small) if show_V_flow
    "Volume flow rate at inflowing port (positive when flow from port_a to port_b)";

  Medium.Temperature port_a_T=
      Modelica.Fluid.Utilities.regStep(port_a.m_flow,
                  Medium.temperature(
                    Medium.setState_phX(
                      p = port_a.p,
                      h = inStream(port_a.h_outflow),
                      X = inStream(port_a.Xi_outflow))),
                  Medium.temperature(Medium.setState_phX(port_a.p, port_a.h_outflow, port_a.Xi_outflow)),
                  m_flow_small) if show_T
    "Temperature close to port_a, if show_T = true";
  Medium.Temperature port_b_T=
      Modelica.Fluid.Utilities.regStep(port_b.m_flow,
                  Medium.temperature(
                    Medium.setState_phX(
                      p = port_b.p,
                      h = inStream(port_b.h_outflow),
                      X = inStream(port_b.Xi_outflow))),
                  Medium.temperature(Medium.setState_phX(port_b.p, port_b.h_outflow, port_b.Xi_outflow)),
                  m_flow_small) if show_T
    "Temperature close to port_b, if show_T = true";
equation
  // Pressure drop in design flow direction
  dp = port_a.p - port_b.p;

  // Design direction of mass flow rate
  m_flow = port_a.m_flow;
  assert(m_flow > -m_flow_small or allowFlowReversal,
      "Reverting flow occurs even though allowFlowReversal is false");

  // Mass balance (no storage)
  port_a.m_flow + port_b.m_flow = 0;

  // Transport of substances
  port_a.Xi_outflow = if allowFlowReversal then inStream(port_b.Xi_outflow) else Medium.X_default[1:Medium.nXi];
  port_b.Xi_outflow = inStream(port_a.Xi_outflow);

  port_a.C_outflow = if allowFlowReversal then inStream(port_b.C_outflow) else zeros(Medium.nC);
  port_b.C_outflow = inStream(port_a.C_outflow);
end PartialTwoPortTransport;


within AixLib.Fluid.Interfaces;
partial model PartialTwoPortVector "Partial component with two ports, one of which being vectorized"

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"
  parameter Integer nPorts "Number of ports"
  parameter Boolean allowFlowReversal=true
    "= true to allow flow reversal, false restricts to design direction (port_a -> port_b)"

  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare final package Medium = Medium,
    m_flow(min=if allowFlowReversal then -Modelica.Constants.inf else 0),
    h_outflow(start=Medium.h_default, nominal=Medium.h_default))
    "Fluid connector a (positive design flow direction is from port_a to ports_b)"

  Modelica.Fluid.Interfaces.FluidPorts_b ports_b[nPorts](
    redeclare each package Medium = Medium,
    each m_flow(max=if allowFlowReversal then +Modelica.Constants.inf else 0),
    each h_outflow(start=Medium.h_default, nominal=Medium.h_default))
    "Fluid connectors b (positive design flow direction is from port_a to ports_b)"

  // Diagnostics
   parameter Boolean show_T = false
    "= true, if actual temperature at port is computed"

  Medium.ThermodynamicState sta_a=
      Medium.setState_phX(port_a.p,
                          noEvent(actualStream(port_a.h_outflow)),
                          noEvent(actualStream(port_a.Xi_outflow)))
      if show_T "Medium properties in port_a";

  Medium.ThermodynamicState sta_b[nPorts]=
      Medium.setState_phX(ports_b.p,
                          noEvent(actualStream(ports_b.h_outflow)),
                          noEvent(actualStream(ports_b.Xi_outflow)))
      if show_T "Medium properties in ports_b";
end PartialTwoPortVector;


To make medium connectors conditional. Model to make models extending the TwoPort-Model optional.

within AixLib.Fluid.Interfaces;
model PassThroughMedium "To make medium connectors conditional"
  extends AixLib.Fluid.Interfaces.PartialTwoPort;
equation
  connect(port_a, port_b)
end PassThroughMedium;


Model to connect single port port_a to fluid port bus ports_b. This component transports fluid between its signle inlet and multiple
  outlet ports, without storing mass or energy. Therefore, all inlet
  ports ports_a are simply connected with the outlet port
  port_b[i].

within AixLib.Fluid.Interfaces;
model PortAThroughPortsB
  "Model to connect single port port_a to fluid port bus ports_b"

  // Definition of parameters describing the modular approach in general
  //
  parameter Integer nVal = 1
    "Number of valves - each valve will be connected to an individual port_b"

  // Extends base port model and set base parameters
  //
  extends PartialModularPort_b(
    final nPorts=nVal,
    final allowFlowReversal=true,
    final dp_start,
    final m_flow_start,
    final dp_nominal,
    final m_flow_nominal,
    final m_flow_small);

equation
  // Connect port_a with inlet ports of expansion valves
  //
  for i in 1:nVal loop
    connect(port_a,ports_b[i]);
  end for;
end PortAThroughPortsB;


Model to connect fluid port bus ports_a to single port port_b. This component transports fluid between its multiple inlet and single
  outlet ports, without storing mass or energy. Therefore, all inlet
  ports ports_a[i] are simply connected with the outlet
  port port_b.

within AixLib.Fluid.Interfaces;
model PortsAThroughPortB
  "Model to connect fluid port bus ports_a to single port port_b"

  // Definition of parameters describing the modular approach in general
  //
  parameter Integer nVal = 1
    "Number of valves - each valve will be connected to an individual port_b"

  // Extends base port model and set base parameters
  //
  extends PartialModularPort_a(
    final nPorts=nVal,
    final allowFlowReversal=true,
    final dp_start,
    final m_flow_start,
    final dp_nominal,
    final m_flow_nominal,
    final m_flow_small);

equation
  // Connect port_a with inlet ports of expansion valves
  //
  for i in 1:nVal loop
    connect(ports_a[i],port_b);
  end for;
end PortsAThroughPortB;


Component that assigns the outlet fluid property at port_a based on an input signal. This model sets the temperature or the water vapor mass fraction
of the medium that leaves port_a
to the value given by the input TSet or X_wSet,
subject to optional limitations on the capacity
for heating and cooling, or limitations on the humidification or dehumidification
moisture mass flow rate.
Also, optionally the model allows to take into account first order dynamics.

within AixLib.Fluid.Interfaces;
model PrescribedOutlet
  "Component that assigns the outlet fluid property at port_a based on an input signal"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface;

  parameter Modelica.Units.SI.HeatFlowRate QMax_flow(min=0) = Modelica.Constants.inf
    "Maximum heat flow rate for heating (positive)"
  parameter Modelica.Units.SI.HeatFlowRate QMin_flow(max=0) = -Modelica.Constants.inf
    "Maximum heat flow rate for cooling (negative)"
  parameter Modelica.Units.SI.MassFlowRate mWatMax_flow(min=0) = Modelica.Constants.inf
    "Maximum water mass flow rate addition (positive)"

  parameter Modelica.Units.SI.MassFlowRate mWatMin_flow(max=0) = -Modelica.Constants.inf
    "Maximum water mass flow rate removal (negative)"

  parameter Modelica.Units.SI.Time tau(min=0) = 10
    "Time constant at nominal flow rate (used if energyDynamics not equal Modelica.Fluid.Types.Dynamics.SteadyState)"
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Start value of temperature"
  parameter Modelica.Units.SI.MassFraction X_start[Medium.nX]=Medium.X_default
    "Start value of mass fractions m_i/m"

  // Dynamics
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState
    "Type of energy balance: dynamic (3 initialization options) or steady state"

  parameter Boolean use_TSet = true
    "Set to false to disable temperature set point"

  parameter Boolean use_X_wSet = true
    "Set to false to disable water vapor set point"

  Modelica.Blocks.Interfaces.RealInput TSet(unit="K", displayUnit="degC") if use_TSet
    "Set point temperature of the fluid that leaves port_b"

  Modelica.Blocks.Interfaces.RealInput X_wSet(unit="1") if use_X_wSet
    "Set point for water vapor mass fraction of the fluid that leaves port_b"

  Modelica.Blocks.Interfaces.RealOutput Q_flow(unit="W")
    "Heat flow rate added to the fluid (if flow is from port_a to port_b)"

  Modelica.Blocks.Interfaces.RealOutput mWat_flow(unit="kg/s")
    "Water vapor mass flow rate added to the fluid (if flow is from port_a to port_b)"

protected
  parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(Medium.setState_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default)) "Specific heat capacity at default medium state";

  parameter Boolean restrictHeat = QMax_flow < Modelica.Constants.inf/10.0
    "Flag, true if maximum heating power is restricted"
  parameter Boolean restrictCool = QMin_flow > -Modelica.Constants.inf/10.0
    "Flag, true if maximum cooling power is restricted"

  parameter Boolean restrictHumi = mWatMax_flow < Modelica.Constants.inf/10.0
    "Flag, true if maximum humidification is restricted"
  parameter Boolean restrictDehu = mWatMin_flow > -Modelica.Constants.inf/10.0
    "Flag, true if maximum dehumidification is restricted"

  parameter Modelica.Units.SI.SpecificEnthalpy deltaH=cp_default*1E-6
    "Small value for deltaH used for regularization";

  parameter Modelica.Units.SI.MassFraction deltaXi=1E-6
    "Small mass fraction used for regularization";

  Modelica.Units.SI.MassFlowRate m_flow_pos
    "Mass flow rate, or zero if reverse flow";

  Modelica.Units.SI.MassFlowRate m_flow_non_zero
    "Mass flow rate bounded away from zero";

  Modelica.Units.SI.SpecificEnthalpy hSet
    "Set point for enthalpy leaving port_b";

  Modelica.Units.SI.Temperature T
    "Temperature of outlet state assuming unlimited capacity and taking dynamics into account";

  Modelica.Units.SI.MassFraction Xi
    "Water vapor mass fraction of outlet state assuming unlimited capacity and taking dynamics into account";

  Modelica.Units.SI.MassFraction Xi_instream[Medium.nXi]
    "Instreaming water vapor mass fraction at port_a";

  Modelica.Units.SI.MassFraction Xi_outflow
    "Outstreaming water vapor mass fraction at port_a";

  Modelica.Units.SI.SpecificEnthalpy dhAct
    "Actual enthalpy difference from port_a to port_b";

  Real dXiAct(final unit="1")
    "Actual mass fraction difference from port_a to port_b";

  Real k(start=1)
    "Gain to take flow rate into account for sensor time constant";

  Real mNor_flow "Normalized mass flow rate";

  Modelica.Blocks.Interfaces.RealInput TSet_internal(unit="K", displayUnit="degC")
    "Internal connector for set point temperature of the fluid that leaves port_b";

  Modelica.Blocks.Interfaces.RealInput X_wSet_internal(unit="1")
    "Internal connector for set point for water vapor mass fraction of the fluid that leaves port_b";

initial equation
  // Set initial conditions, unless use_{T,Xi}Set = false in which case
  // it is not a state.
  if use_TSet then
    if energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then
      der(T) = 0;
    elseif energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then
      T = T_start;
    end if;
  end if;

  if use_X_wSet then
    if energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then
      der(Xi) = 0;
    elseif energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then
      Xi = X_start[1];
    end if;
  end if;

  assert((energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState) or
          tau > Modelica.Constants.eps,
"The parameter tau, or the volume of the model from which tau may be derived, is unreasonably small.
 You need to set energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState to model steady-state.
 Received tau = " + String(tau) + "\n");

 if use_X_wSet then
  assert(Medium.nX > 1, "If use_X_wSet = true, require a medium with water vapor, such as AixLib.Media.Air");
 end if;

equation
  // Conditional connectors
  if not use_TSet then
    TSet_internal = 293.15;
  end if;
  connect(TSet, TSet_internal);
  if not use_X_wSet then
    X_wSet_internal = 0.01;
  end if;
  connect(X_wSet, X_wSet_internal);

  if ((use_TSet or use_X_wSet) and energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState) then
    mNor_flow = port_a.m_flow/m_flow_nominal;
    k = Modelica.Fluid.Utilities.regStep(x=port_a.m_flow,
                                         y1= mNor_flow,
                                         y2=-mNor_flow,
                                         x_small=m_flow_small);
  else
    mNor_flow = 1;
    k = 1;
  end if;

  if use_TSet and energyDynamics <> Modelica.Fluid.Types.Dynamics.SteadyState then
    der(T) = (TSet_internal-T)*k/tau;
  else
    T = TSet_internal;
  end if;

  if use_X_wSet and energyDynamics <> Modelica.Fluid.Types.Dynamics.SteadyState then
    der(Xi) = (X_wSet_internal-Xi)*k/tau;
  else
    Xi = X_wSet_internal;
  end if;

  Xi_instream = inStream(port_a.Xi_outflow);

  // Set point without any capacity limitation
  hSet = if use_TSet then Medium.specificEnthalpy(
    Medium.setState_pTX(
      p = port_a.p,
      T = T,
      X = Xi_instream + fill(dXiAct, Medium.nXi)))
        else Medium.h_default;

  m_flow_pos = AixLib.Utilities.Math.Functions.smoothMax(
    x1=m_flow,
    x2=0,
    deltaX=m_flow_small);

   if not restrictHeat and not restrictCool and
      not restrictHumi and not restrictDehu then
     m_flow_non_zero = Modelica.Constants.eps;
   else
     m_flow_non_zero = AixLib.Utilities.Math.Functions.smoothMax(
       x1 = port_a.m_flow,
       x2 = m_flow_small,
       deltaX=m_flow_small/2);
   end if;

  // Compute mass fraction leaving the component.
  // Below, we use sum(Xi_instream) as Xi anyway has only one element.
  // However, scalar(Xi_instream) would not work as dim(Xi_instream) = 0
  // if the medium is not a mixture.
  // Note that the computations for the mass fraction and for the
  // enthalpy change are identical. In a development version, they were
  // put in a function, but this led to large nonlinear systems of equations.
  if use_X_wSet then
    if not restrictHumi and not restrictDehu then
      // No capacity limit
      dXiAct = Xi_outflow-sum(Xi_instream);
      Xi_outflow  = Xi;
      mWat_flow   = m_flow_pos*(Xi - sum(Xi_instream));
    else
      if restrictHumi and restrictDehu then
        // Capacity limits for heating and cooling
        dXiAct =AixLib.Utilities.Math.Functions.smoothLimit(
              x=Xi - sum(Xi_instream),
              l=mWatMin_flow/m_flow_non_zero,
              u=mWatMax_flow/m_flow_non_zero,
              deltaX=deltaXi);
      elseif restrictHumi then
        // Capacity limit for heating only
        dXiAct =AixLib.Utilities.Math.Functions.smoothMin(
              x1=Xi - sum(Xi_instream),
              x2=mWatMax_flow/m_flow_non_zero,
              deltaX=deltaXi);
      else
        // Capacity limit for cooling only
        dXiAct =AixLib.Utilities.Math.Functions.smoothMax(
              x1=Xi - sum(Xi_instream),
              x2=mWatMin_flow/m_flow_non_zero,
              deltaX=deltaXi);
      end if;
      Xi_outflow = sum(Xi_instream) + dXiAct;
      mWat_flow  = m_flow_pos*dXiAct;
    end if;
    port_b.Xi_outflow = fill(Xi_outflow, Medium.nXi);
  else
    Xi_outflow = sum(Xi_instream);
    mWat_flow = 0;
    dXiAct = 0;
    port_b.Xi_outflow = Xi_instream;
  end if;

  // Compute enthalpy leaving the component.
  if use_TSet then
    if not restrictHeat and not restrictCool then
      // No capacity limit
      dhAct = 0;
      port_b.h_outflow  = hSet;
      Q_flow  =    m_flow_pos*(hSet - inStream(port_a.h_outflow));
    else
      if restrictHeat and restrictCool then
        // Capacity limits for heating and cooling
        dhAct =AixLib.Utilities.Math.Functions.smoothLimit(
              x=hSet - inStream(port_a.h_outflow),
              l=QMin_flow/m_flow_non_zero,
              u=QMax_flow/m_flow_non_zero,
              deltaX=deltaH);
      elseif restrictHeat then
        // Capacity limit for heating only
        dhAct =AixLib.Utilities.Math.Functions.smoothMin(
              x1=hSet - inStream(port_a.h_outflow),
              x2=QMax_flow/m_flow_non_zero,
              deltaX=deltaH);
      else
        // Capacity limit for cooling only
        dhAct =AixLib.Utilities.Math.Functions.smoothMax(
              x1=hSet - inStream(port_a.h_outflow),
              x2=QMin_flow/m_flow_non_zero,
              deltaX=deltaH);
      end if;
      port_b.h_outflow = inStream(port_a.h_outflow) + dhAct;
      Q_flow = m_flow_pos*dhAct;
    end if;
  else
    port_b.h_outflow = inStream(port_a.h_outflow);
    Q_flow = 0;
    dhAct = 0;
  end if;

  // Outflowing property at port_a is unaffected by this model.
  if allowFlowReversal then
    port_a.h_outflow =  inStream(port_b.h_outflow);
    port_a.Xi_outflow = inStream(port_b.Xi_outflow);
    port_a.C_outflow =  inStream(port_b.C_outflow);
  else
    port_a.h_outflow =  Medium.h_default;
    port_a.Xi_outflow = Medium.X_default[1:Medium.nXi];
    port_a.C_outflow =  zeros(Medium.nC);
  end if;
  // No pressure drop
  dp = 0;

  // Mass balance (no storage)
  port_a.m_flow + port_b.m_flow = 0;

  // Transport of substances
  port_b.C_outflow = inStream(port_a.C_outflow);

  if not allowFlowReversal then
    assert(m_flow > -m_flow_small,
      "Reverting flow occurs even though allowFlowReversal is false");
  end if;
end PrescribedOutlet;


within AixLib.Fluid.Interfaces;
model StaticFourPortHeatMassExchanger
  "Partial model transporting two fluid streams between four ports without storing mass or energy"
  extends AixLib.Fluid.Interfaces.PartialFourPortInterface;
  extends AixLib.Fluid.Interfaces.FourPortFlowResistanceParameters(
   final computeFlowResistance1=(dp1_nominal > Modelica.Constants.eps),
   final computeFlowResistance2=(dp2_nominal > Modelica.Constants.eps));

  constant Boolean prescribedHeatFlowRate1 = false
    "Set to true if the heat flow rate into fluid 1 is not a function of the component temperature";
  constant Boolean prescribedHeatFlowRate2 = false
    "Set to true if the heat flow rate into fluid 2 is not a function of the component temperature";

  constant Boolean homotopyInitialization = true "= true, use homotopy method"

  // Q1_flow is sensible plus latent heat flow rate
  input Modelica.Units.SI.HeatFlowRate Q1_flow
    "Heat transferred into the medium 1";
  input Medium1.MassFlowRate mWat1_flow
    "Moisture mass flow rate added to the medium 1";
  // Q2_flow is sensible plus latent heat flow rate
  input Modelica.Units.SI.HeatFlowRate Q2_flow
    "Heat transferred into the medium 2";
  input Medium2.MassFlowRate mWat2_flow
    "Moisture mass flow rate added to the medium 2";
  constant Boolean sensibleOnly1
    "Set to true if sensible exchange only for medium 1";
  constant Boolean sensibleOnly2
    "Set to true if sensible exchange only for medium 2";

protected
  AixLib.Fluid.Interfaces.StaticTwoPortHeatMassExchanger bal1(
    redeclare final package Medium=Medium1,
    final sensibleOnly = sensibleOnly1,
    final prescribedHeatFlowRate=prescribedHeatFlowRate1,
    final m_flow_nominal = m1_flow_nominal,
    final dp_nominal = dp1_nominal,
    final allowFlowReversal = allowFlowReversal1,
    final m_flow_small = m1_flow_small,
    final homotopyInitialization = homotopyInitialization,
    final from_dp = from_dp1,
    final linearizeFlowResistance = linearizeFlowResistance1,
    final deltaM = deltaM1,
    final Q_flow = Q1_flow,
    final mWat_flow = mWat1_flow)
    "Model for heat, mass, species, trace substance and pressure balance of stream 1";
  AixLib.Fluid.Interfaces.StaticTwoPortHeatMassExchanger bal2(
    redeclare final package Medium=Medium2,
    final sensibleOnly = sensibleOnly2,
    final prescribedHeatFlowRate=prescribedHeatFlowRate2,
    final m_flow_nominal = m2_flow_nominal,
    final dp_nominal = dp2_nominal,
    final allowFlowReversal = allowFlowReversal2,
    final m_flow_small = m2_flow_small,
    final homotopyInitialization = homotopyInitialization,
    final from_dp = from_dp2,
    final linearizeFlowResistance = linearizeFlowResistance2,
    final deltaM = deltaM2,
    final Q_flow = Q2_flow,
    final mWat_flow = mWat2_flow)
    "Model for heat, mass, species, trace substance and pressure balance of stream 2";

initial equation
  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

equation
  connect(bal1.port_a, port_a1);
  connect(bal1.port_b, port_b1);
  connect(bal2.port_a, port_a2);
  connect(bal2.port_b, port_b2);
end StaticFourPortHeatMassExchanger;


Partial model for static energy and mass conservation equations. This model transports fluid between its two ports, without storing mass or energy.
It implements a steady-state conservation equation for energy and mass fractions.
The model has zero pressure drop between its ports.

within AixLib.Fluid.Interfaces;
model StaticTwoPortConservationEquation
  "Partial model for static energy and mass conservation equations"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface;

  constant Boolean simplify_mWat_flow = true
    "Set to true to cause port_a.m_flow + port_b.m_flow = 0 even if mWat_flow is non-zero";

  constant Boolean prescribedHeatFlowRate = false
    "Set to true if the heat flow rate is not a function of a temperature difference to the fluid temperature";

  parameter Boolean use_mWat_flow = false
    "Set to true to enable input connector for moisture mass flow rate"

  parameter Boolean use_C_flow = false
    "Set to true to enable input connector for trace substance"

  Modelica.Blocks.Interfaces.RealInput Q_flow(unit="W")
    "Sensible plus latent heat flow rate transferred into the medium"
  Modelica.Blocks.Interfaces.RealInput mWat_flow(final quantity="MassFlowRate",
                                                 unit="kg/s")
    if use_mWat_flow "Moisture mass flow rate added to the medium"
  Modelica.Blocks.Interfaces.RealInput[Medium.nC] C_flow
    if use_C_flow "Trace substance mass flow rate added to the medium"

  // Outputs that are needed in models that extend this model
  Modelica.Blocks.Interfaces.RealOutput hOut(final unit="J/kg")
    "Leaving specific enthalpy of the component"

  Modelica.Blocks.Interfaces.RealOutput XiOut[Medium.nXi](each unit="1",
                                                          each min=0,
                                                          each max=1,
                                                          nominal=0.01*ones(Medium.nXi))
    "Leaving species concentration of the component"
  Modelica.Blocks.Interfaces.RealOutput COut[Medium.nC](each min=0)
    "Leaving trace substances of the component"

protected
  final parameter Boolean use_m_flowInv=
    (prescribedHeatFlowRate or use_mWat_flow or use_C_flow)
    "Flag, true if m_flowInv is used in the model"
  final parameter Real s[Medium.nXi] = {if Modelica.Utilities.Strings.isEqual(string1=Medium.substanceNames[i],
                                            string2="Water",
                                            caseSensitive=false)
                                            then 1 else 0 for i in 1:Medium.nXi}
    "Vector with zero everywhere except where species is";

  Real m_flowInv(unit="s/kg") "Regularization of 1/m_flow of port_a";

  Modelica.Units.SI.MassFlowRate mXi_flow[Medium.nXi]
    "Mass flow rates of independent substances added to the medium";

  // Parameters for inverseXRegularized.
  // These are assigned here for efficiency reason.
  // Otherwise, they would need to be computed each time
  // the function is invocated.
  final parameter Real deltaReg = m_flow_small/1E3
    "Smoothing region for inverseXRegularized";

  final parameter Real deltaInvReg = 1/deltaReg
    "Inverse value of delta for inverseXRegularized";

  final parameter Real aReg = -15*deltaInvReg
    "Polynomial coefficient for inverseXRegularized";
  final parameter Real bReg = 119*deltaInvReg^2
    "Polynomial coefficient for inverseXRegularized";
  final parameter Real cReg = -361*deltaInvReg^3
    "Polynomial coefficient for inverseXRegularized";
  final parameter Real dReg = 534*deltaInvReg^4
    "Polynomial coefficient for inverseXRegularized";
  final parameter Real eReg = -380*deltaInvReg^5
    "Polynomial coefficient for inverseXRegularized";
  final parameter Real fReg = 104*deltaInvReg^6
    "Polynomial coefficient for inverseXRegularized";

  final parameter Medium.ThermodynamicState state_default = Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default[1:Medium.nXi]) "Medium state at default values";
  // Density at medium default values, used to compute the size of control volumes
  final parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(state=state_default)
    "Specific heat capacity, used to verify energy conservation";
  constant Modelica.Units.SI.TemperatureDifference dTMax(min=1) = 200
    "Maximum temperature difference across the StaticTwoPortConservationEquation";
  // Conditional connectors
  Modelica.Blocks.Interfaces.RealInput mWat_flow_internal(unit="kg/s")
    "Needed to connect to conditional connector";
  Modelica.Blocks.Interfaces.RealInput C_flow_internal[Medium.nC]
    "Needed to connect to conditional connector";
initial equation
  // Assert that the substance with name 'water' has been found.
  if use_mWat_flow then
    assert(Medium.nXi == 0 or abs(sum(s)-1) < 1e-5,
      "If Medium.nXi > 1, then substance 'water' must be present for one component.'"
         + Medium.mediumName + "'.\n"
         + "Check medium model.");
  end if;
equation
  // Conditional connectors
  connect(mWat_flow, mWat_flow_internal);
  if not use_mWat_flow then
    mWat_flow_internal = 0;
  end if;

  connect(C_flow, C_flow_internal);
  if not use_C_flow then
    C_flow_internal = zeros(Medium.nC);
  end if;

  // Species flow rate from connector mWat_flow
  mXi_flow = mWat_flow_internal * s;

  // Regularization of m_flow around the origin to avoid a division by zero
  // m_flowInv is only used if prescribedHeatFlowRate == true, or
  // if the input connectors mWat_flow or C_flow are enabled.
  if use_m_flowInv then
    m_flowInv = AixLib.Utilities.Math.Functions.inverseXRegularized(
                       x=port_a.m_flow,
                       delta=deltaReg, deltaInv=deltaInvReg,
                       a=aReg, b=bReg, c=cReg, d=dReg, e=eReg, f=fReg);
  else
    // m_flowInv is not used.
    m_flowInv = 0;
  end if;

  if prescribedHeatFlowRate then
    assert(noEvent( abs(Q_flow) < dTMax*cp_default*max(m_flow_small/1E3, abs(m_flow))),
   "In " + getInstanceName() + ":
   The heat flow rate equals " + String(Q_flow) +
   " W and the mass flow rate equals " + String(m_flow) + " kg/s,
   which results in a temperature difference " +
   String(abs(Q_flow)/ (cp_default*max(m_flow_small/1E3, abs(m_flow)))) +
   " K > dTMax=" +String(dTMax) + " K.
   This may indicate that energy is not conserved for small mass flow rates.
   The implementation may require prescribedHeatFlowRate = false.");
  end if;

  if allowFlowReversal then
    // Formulate hOut using spliceFunction. This avoids an event iteration.
    // The introduced error is small because deltax=m_flow_small/1e3
    hOut = AixLib.Utilities.Math.Functions.regStep(y1=port_b.h_outflow,
                                                    y2=port_a.h_outflow,
                                                    x=port_a.m_flow,
                                                    x_small=m_flow_small/1E3);
    XiOut = AixLib.Utilities.Math.Functions.regStep(y1=port_b.Xi_outflow,
                                                     y2=port_a.Xi_outflow,
                                                     x=port_a.m_flow,
                                                     x_small=m_flow_small/1E3);
    COut = AixLib.Utilities.Math.Functions.regStep(y1=port_b.C_outflow,
                                                    y2=port_a.C_outflow,
                                                    x=port_a.m_flow,
                                                    x_small=m_flow_small/1E3);
  else
    hOut =  port_b.h_outflow;
    XiOut = port_b.Xi_outflow;
    COut =  port_b.C_outflow;
  end if;

  //////////////////////////////////////////////////////////////////////////////////////////
  // Energy balance and mass balance

    // Mass balance (no storage)
    port_a.m_flow + port_b.m_flow = if simplify_mWat_flow then 0 else -mWat_flow_internal;

    // Substance balance
    // a) forward flow
    if use_m_flowInv then
      port_b.Xi_outflow = inStream(port_a.Xi_outflow) + mXi_flow * m_flowInv;
    else // no water is added
      assert(use_mWat_flow == false, "In " + getInstanceName() + ": Wrong implementation for forward flow.");
      port_b.Xi_outflow = inStream(port_a.Xi_outflow);
    end if;

    // b) backward flow
    if allowFlowReversal then
      if use_m_flowInv then
        port_a.Xi_outflow = inStream(port_b.Xi_outflow) - mXi_flow * m_flowInv;
      else // no water added
        assert(use_mWat_flow == false, "In " + getInstanceName() + ": Wrong implementation for reverse flow.");
        port_a.Xi_outflow = inStream(port_b.Xi_outflow);
      end if;
    else // no  flow reversal
      port_a.Xi_outflow = Medium.X_default[1:Medium.nXi];
    end if;

    // Energy balance.
    // This equation is approximate since m_flow = port_a.m_flow is used for the mass flow rate
    // at both ports. Since mWat_flow_internal << m_flow, the error is small.
    if prescribedHeatFlowRate then
      port_b.h_outflow = inStream(port_a.h_outflow) + Q_flow * m_flowInv;
      if allowFlowReversal then
        port_a.h_outflow = inStream(port_b.h_outflow) - Q_flow * m_flowInv;
      else
        port_a.h_outflow = Medium.h_default;
      end if;
    else
      // Case with prescribedHeatFlowRate == false.
      // port_b.h_outflow is known and the equation needs to be solved for Q_flow.
      // Hence, we cannot use m_flowInv as for m_flow=0, any Q_flow would satisfiy
      // Q_flow * m_flowInv = 0.
      // The same applies for port_b.Xi_outflow and mXi_flow.
      port_a.m_flow * (inStream(port_a.h_outflow) - port_b.h_outflow)     = -Q_flow;
      if allowFlowReversal then
        port_a.m_flow * (inStream(port_b.h_outflow)  - port_a.h_outflow)  = +Q_flow;
      else
        // When allowFlowReversal = false, the downstream enthalpy does not matter.
        // Therefore a dummy value is used to avoid algebraic loops
        port_a.h_outflow = Medium.h_default;
      end if;
    end if;

  // Transport of trace substances
  if use_m_flowInv and use_C_flow then
    port_b.C_outflow =  inStream(port_a.C_outflow) + C_flow_internal * m_flowInv;
  else // no trace substance added.
    assert(not use_C_flow, "In " + getInstanceName() + ": Wrong implementation of trace substance balance for forward flow.");
    port_b.C_outflow =  inStream(port_a.C_outflow);
  end if;

  if allowFlowReversal then
    if use_C_flow then
      port_a.C_outflow = inStream(port_b.C_outflow) - C_flow_internal * m_flowInv;
    else
      port_a.C_outflow = inStream(port_b.C_outflow);
    end if;
  else
    port_a.C_outflow = zeros(Medium.nC);
  end if;

  ////////////////////////////////////////////////////////////////////////////
  // No pressure drop in this model
  port_a.p = port_b.p;
end StaticTwoPortConservationEquation;


within AixLib.Fluid.Interfaces;
model StaticTwoPortHeatMassExchanger
  "Partial model transporting fluid between two ports without storing mass or energy"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface;
  extends AixLib.Fluid.Interfaces.TwoPortFlowResistanceParameters(
    final computeFlowResistance=(abs(dp_nominal) > Modelica.Constants.eps));

  constant Boolean sensibleOnly "Set to true if sensible exchange only";
  constant Boolean prescribedHeatFlowRate
    "Set to true if the heat flow rate is not a function of the component temperature";

  constant Boolean homotopyInitialization = true "= true, use homotopy method"

  // Model inputs
  // Q_flow is the sensible plus latent heat flow rate
  input Modelica.Units.SI.HeatFlowRate Q_flow
    "Heat transferred into the medium";
  input Modelica.Units.SI.MassFlowRate mWat_flow
    "Moisture mass flow rate added to the medium";

  // Models for conservation equations and pressure drop
  AixLib.Fluid.Interfaces.StaticTwoPortConservationEquation vol(
    redeclare final package Medium = Medium,
    final use_mWat_flow = not sensibleOnly,
    final prescribedHeatFlowRate = prescribedHeatFlowRate,
    final m_flow_nominal = m_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_small=m_flow_small)
    "Control volume for steady-state energy and mass balance"

  AixLib.Fluid.FixedResistances.PressureDrop preDro(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final deltaM=deltaM,
    final allowFlowReversal=allowFlowReversal,
    final show_T=false,
    final from_dp=from_dp,
    final linearized=linearizeFlowResistance,
    final homotopyInitialization=homotopyInitialization,
    final dp_nominal=dp_nominal) "Flow resistance"

  // Outputs that are needed in models that extend this model
  Modelica.Blocks.Interfaces.RealOutput hOut(unit="J/kg")
    "Leaving temperature of the component";

  Modelica.Blocks.Interfaces.RealOutput XiOut[Medium.nXi](each unit="1",
                                                          each min=0,
                                                          each max=1)
    "Leaving species concentration of the component";
  Modelica.Blocks.Interfaces.RealOutput COut[Medium.nC](each min=0)
    "Leaving trace substances of the component";

protected
  Modelica.Blocks.Sources.RealExpression heaInp(y=Q_flow)
    "Block to set heat input into volume"
  Modelica.Blocks.Sources.RealExpression
    masExc(final y=mWat_flow) "Block to set moisture exchange in volume"

initial equation
  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

equation
  connect(vol.hOut, hOut);
  connect(vol.XiOut, XiOut);
  connect(vol.COut, COut);
  connect(port_a,preDro. port_a)
  connect(preDro.port_b, vol.port_a)

  connect(vol.port_b, port_b)

  connect(heaInp.y, vol.Q_flow)
  connect(masExc.y, vol.mWat_flow)
end StaticTwoPortHeatMassExchanger;


within AixLib.Fluid.Interfaces;
model TwoPortHeatMassExchanger
  "Partial model transporting one fluid stream with storing mass or energy"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface(
    port_a(h_outflow(start=h_outflow_start)),
    port_b(h_outflow(start=h_outflow_start)));
  extends AixLib.Fluid.Interfaces.TwoPortFlowResistanceParameters(
    final computeFlowResistance=true);

  constant Boolean homotopyInitialization = true "= true, use homotopy method"

  parameter Modelica.Units.SI.Time tau=30
    "Time constant at nominal flow (if energyDynamics <> SteadyState)"

  // Dynamics
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"

  // Initialization
  parameter Medium.AbsolutePressure p_start = Medium.p_default
    "Start value of pressure"
  parameter Medium.Temperature T_start = Medium.T_default
    "Start value of temperature"
  parameter Medium.MassFraction X_start[Medium.nX](
    final quantity=Medium.substanceNames) = Medium.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium.ExtraProperty C_start[Medium.nC](
    final quantity=Medium.extraPropertiesNames)=fill(0, Medium.nC)
    "Start value of trace substances"

  replaceable AixLib.Fluid.MixingVolumes.MixingVolume vol
  constrainedby AixLib.Fluid.MixingVolumes.BaseClasses.PartialMixingVolume(
    redeclare final package Medium = Medium,
    nPorts = 2,
    V=m_flow_nominal*tau/rho_default,
    final allowFlowReversal=allowFlowReversal,
    final mSenFac=1,
    final m_flow_nominal = m_flow_nominal,
    final energyDynamics=energyDynamics,
    final massDynamics=energyDynamics,
    final p_start=p_start,
    final T_start=T_start,
    final X_start=X_start,
    final C_start=C_start) "Volume for fluid stream"

  AixLib.Fluid.FixedResistances.PressureDrop preDro(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final deltaM=deltaM,
    final allowFlowReversal=allowFlowReversal,
    final show_T=false,
    final from_dp=from_dp,
    final linearized=linearizeFlowResistance,
    final homotopyInitialization=homotopyInitialization,
    final dp_nominal=dp_nominal) "Flow resistance"

protected
  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default, p=Medium.p_default, X=Medium.X_default);
  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density, used to compute fluid volume";
  parameter Medium.ThermodynamicState sta_start=Medium.setState_pTX(
      T=T_start, p=p_start, X=X_start);
  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_start=
      Medium.specificEnthalpy(sta_start) "Start value for outflowing enthalpy";

initial algorithm
  assert((energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState) or
          tau > Modelica.Constants.eps,
"The parameter tau, or the volume of the model from which tau may be derived, is unreasonably small.
 You need to set energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState to model steady-state.
 Received tau = " + String(tau) + "\n");

  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

equation
  connect(vol.ports[2], port_b)
  connect(port_a, preDro.port_a)
  connect(preDro.port_b, vol.ports[1])
end TwoPortHeatMassExchanger;


within AixLib.Fluid.Interfaces.Examples;
model ConservationEquation "Model that tests the conservation equation"
extends Modelica.Icons.Example;
 package Medium = AixLib.Media.Water "Medium model";
  AixLib.Fluid.Interfaces.ConservationEquation dyn(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=2,
    fluidVolume=0.01,
    use_mWat_flow=true) "Dynamic conservation equation"

  AixLib.Fluid.Interfaces.StaticTwoPortConservationEquation ste(
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    show_T=true,
    prescribedHeatFlowRate=true,
    use_mWat_flow=true) "Steady-state conservation equation"

  Modelica.Blocks.Sources.Constant mWat_flow(k=0)
    "Water mass flow rate added to the control volume"
  Modelica.Blocks.Sources.TimeTable QSen_flow(startTime=0, table=[
                                0,-100;
                                900,-100;
                                900,0;
                                1800,0;
                                1800,100])
    "Sensible heat flow rate added to the control volume"
  AixLib.Fluid.Sources.Boundary_pT sin(
    nPorts=2,
    use_p_in=false,
    redeclare package Medium = Medium,
    p=101325,
    T=283.15)
  AixLib.Fluid.FixedResistances.PressureDrop res1(
    from_dp=true,
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    dp_nominal=100) "Flow resistance"
  AixLib.Fluid.Sources.MassFlowSource_T bou(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow=0.01) "Boundary condition for mass flow rate"
  AixLib.Fluid.FixedResistances.PressureDrop res2(
    from_dp=true,
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    dp_nominal=100) "Flow resistance"

equation
  connect(QSen_flow.y, dyn.Q_flow)
  connect(mWat_flow.y, dyn.mWat_flow)

  connect(bou.ports[1], dyn.ports[1])
  connect(res1.port_b, sin.ports[1])
  connect(QSen_flow.y, ste.Q_flow)
  connect(mWat_flow.y, ste.mWat_flow)
  connect(res2.port_b, sin.ports[2])
  connect(ste.port_a, bou.ports[2])
  connect(ste.port_b, res2.port_a)
  connect(dyn.ports[2], res1.port_a)
end ConservationEquation;


within AixLib.Fluid.Interfaces.Examples;
model EightPortHeatMassExchanger
  "EightPortHeatMassExchanger example model"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Sources.Boundary_pT sin(          redeclare package Medium = Medium, nPorts=4)
    "Mass flow sink"
  Sources.MassFlowSource_T sou2(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=true,
    nPorts=1) "Mass flow source"
  Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    m_flow=1,
    nPorts=1,
    T=283.15) "Mass flow source"
  Modelica.Blocks.Sources.Ramp ram_T2(
    height=10,
    offset=273.15,
    duration=5) "Temperature ramp"
  Modelica.Blocks.Sources.Ramp ram_m2_flow(
    height=-2,
    offset=1,
    duration=5) "Mass flow rate ramp"
  AixLib.Fluid.Interfaces.EightPortHeatMassExchanger eigPor(
    redeclare package Medium1 = Medium,
    redeclare package Medium2 = Medium,
    redeclare package Medium3 = Medium,
    redeclare package Medium4 = Medium,
    m1_flow_nominal=m_flow_nominal,
    m2_flow_nominal=m_flow_nominal,
    m3_flow_nominal=m_flow_nominal,
    m4_flow_nominal=m_flow_nominal,
    dp1_nominal=0,
    dp2_nominal=0,
    dp3_nominal=0,
    dp4_nominal=0,
    tau1=1,
    tau2=1,
    tau3=1,
    tau4=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Eight port heat and mass exchanger"
  Sources.MassFlowSource_T sou4(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=true,
    nPorts=1) "Mass flow source"
  Modelica.Blocks.Sources.Ramp ram_T4(
    duration=5,
    height=-10,
    offset=283.15) "Temperature ramp"
  Modelica.Blocks.Sources.Ramp ram_m4_flow(
    duration=5,
    height=2,
    offset=-1) "Mass flow rate ramp"
  Sources.MassFlowSource_T sou3(
    redeclare package Medium = Medium,
    m_flow=1,
    nPorts=1,
    T=303.15) "Mass flow source"
equation
  connect(ram_m2_flow.y, sou2.m_flow_in)
  connect(ram_T2.y, sou2.T_in)
  connect(ram_m4_flow.y, sou4.m_flow_in)
  connect(ram_T4.y, sou4.T_in)
  connect(sou2.ports[1], eigPor.port_b2)
  connect(sou4.ports[1], eigPor.port_b4)
  connect(sou3.ports[1], eigPor.port_b3)
  connect(sou1.ports[1], eigPor.port_b1)
  connect(eigPor.port_a1, sin.ports[1])
  connect(eigPor.port_a3, sin.ports[2])
  connect(eigPor.port_a2, sin.ports[3])
  connect(eigPor.port_a4, sin.ports[4])
end EightPortHeatMassExchanger;


FourPortHeatMassExchanger example model

within AixLib.Fluid.Interfaces.Examples;
model FourPortHeatMassExchanger
  "FourPortHeatMassExchanger example model"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  AixLib.Fluid.Interfaces.FourPortHeatMassExchanger fouPor(
    redeclare package Medium1 = Medium,
    redeclare package Medium2 = Medium,
    m1_flow_nominal=m_flow_nominal,
    m2_flow_nominal=m_flow_nominal,
    dp1_nominal=0,
    dp2_nominal=0,
    tau1=1,
    tau2=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
                   "Four port heat mass exchanger"

  Sources.Boundary_pT sin(nPorts=2, redeclare package Medium = Medium)
    "Mass flow sink"
  Sources.MassFlowSource_T sou1(
    nPorts=1,
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=true) "Mass flow source"
  Sources.MassFlowSource_T sou2(
    nPorts=1,
    redeclare package Medium = Medium,
    m_flow=1,
    T=283.15) "Mass flow source"
  Modelica.Blocks.Sources.Ramp ram_T(
    height=10,
    offset=273.15,
    duration=5) "Temperature ramp"
  Modelica.Blocks.Sources.Ramp ram_m_flow(
    height=-2,
    offset=1,
    duration=5) "Mass flow rate ramp"
equation
  connect(sin.ports[1], fouPor.port_b2)
  connect(fouPor.port_b1, sin.ports[2])
  connect(sou1.ports[1], fouPor.port_a1)
  connect(sou2.ports[1], fouPor.port_a2)
  connect(ram_m_flow.y, sou1.m_flow_in)
  connect(ram_T.y, sou1.T_in)
end FourPortHeatMassExchanger;


within AixLib.Fluid.Interfaces.Examples;
model HeaterCooler_u
  "Model that tests a heat exchanger model with reverse flow"
  extends Modelica.Icons.Example;

 package Medium = AixLib.Media.Water;

  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea1(
    redeclare package Medium = Medium,
    Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  Modelica.Blocks.Sources.Constant TDb(k=293.15) "Drybulb temperature"
  AixLib.Fluid.Sources.Boundary_pT sou_1(
    redeclare package Medium = Medium,
    use_T_in=true,
    nPorts=4,
    p(displayUnit="Pa") = 101735,
    T=293.15)
  AixLib.Fluid.FixedResistances.PressureDrop res_11(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_12(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.Sources.Boundary_pT sin_1(                       redeclare
      package Medium = Medium,
    use_p_in=true,
    T=288.15,
    nPorts=4)
    Modelica.Blocks.Sources.Constant POut(k=101325)
    Modelica.Blocks.Sources.Ramp u(
    height=2,
    duration=3600,
    offset=-1,
    startTime=0) "Control signal"
  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea2(
    redeclare package Medium = Medium,
    Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  Modelica.Blocks.Math.Gain gain(k=-1)
  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea3(
    redeclare package Medium = Medium,
    Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_2(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_3(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea4(
    redeclare package Medium = Medium, Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_4(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.MixingVolumes.MixingVolume mix1(
                                           redeclare package Medium = Medium, V=
       0.000001,
    nPorts=2,
    m_flow_nominal=0.5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
  Modelica.Blocks.Math.Add che1(k2=-1)
  Modelica.Blocks.Math.Add che2(k2=-1)
  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea5(
    redeclare package Medium = Medium,
    Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_1(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_5(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea6(
    redeclare package Medium =
        Medium, Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea7(
    redeclare package Medium = Medium,
    Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_6(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_7(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.HeatExchangers.HeaterCooler_u hea8(
    redeclare package Medium = Medium, Q_flow_nominal=5000,
    m_flow_nominal=0.5,
    dp_nominal=200,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_8(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.MixingVolumes.MixingVolume mix2(
    redeclare package Medium = Medium,
    V=0.000001,
    nPorts=2,
    m_flow_nominal=0.5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
  Modelica.Blocks.Math.Add che9(k2=-1)
  Modelica.Blocks.Math.Add che10(k2=-1)

  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem2a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem2b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem1a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem1b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem3a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem3b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem4a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem4b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Modelica.Blocks.Math.Add che3(k2=-1)
  Modelica.Blocks.Math.Add che4(k2=-1)
  Modelica.Blocks.Math.Add che5(k2=-1)
  Modelica.Blocks.Math.Add che6(k2=-1)
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem6b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem6a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem5b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem5a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Modelica.Blocks.Math.Add che7(k2=-1)
  Modelica.Blocks.Math.Add che8(k2=-1)
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem8b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem8a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem7b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                      senTem7a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
equation
  connect(POut.y,sin_1. p_in)
  connect(TDb.y,sou_1. T_in)
  connect(u.y, hea1.u)
  connect(gain.y, hea2.u)
  connect(u.y, gain.u)
  connect(u.y, hea3.u)
  connect(gain.y, hea4.u)
  connect(u.y, hea6.u)
  connect(u.y, hea8.u)
  connect(gain.y, hea5.u)
  connect(gain.y, hea7.u)
  connect(sin_1.ports[1], res_12.port_a)
  connect(sin_1.ports[2], res_3.port_a)
  connect(sou_1.ports[1], res_11.port_a)
  connect(sou_1.ports[2], res_2.port_a)
  connect(sin_1.ports[3], res_1.port_a)
  connect(sin_1.ports[4], res_6.port_a)
  connect(sou_1.ports[3], res_5.port_a)
  connect(sou_1.ports[4], res_7.port_a)
  connect(mix1.ports[1], res_4.port_a)
  connect(mix2.ports[1], res_8.port_a)
  connect(senTem1a.T,che1. u1)
  connect(senTem2a.T,che1. u2)
  connect(senTem1b.T,che2. u2)
  connect(senTem2b.T,che2. u1)
  connect(senTem4a.T,che4. u2)
  connect(senTem3a.T,che4. u1)
  connect(senTem4b.T,che3. u1)
  connect(senTem3b.T,che3. u2)
  connect(senTem6a.T,che6. u2)
  connect(senTem5a.T,che6. u1)
  connect(senTem6b.T,che5. u1)
  connect(senTem5b.T,che5. u2)
  connect(senTem8a.T,che8. u2)
  connect(senTem7a.T,che8. u1)
  connect(senTem8b.T,che7. u1)
  connect(senTem7b.T,che7. u2)
  connect(senTem3a.T,che10. u1)
  connect(senTem7a.T,che10. u2)
  connect(senTem2b.T,che9. u1)
  connect(senTem5b.T,che9. u2)
  connect(res_12.port_b, senTem2a.port_a)
  connect(senTem2a.port_b, hea2.port_a)
  connect(hea2.port_b, senTem2b.port_a)
  connect(senTem2b.port_b, senTem1b.port_b)
  connect(hea1.port_b, senTem1b.port_a)
  connect(res_11.port_b, senTem1a.port_a)
  connect(senTem1a.port_b, hea1.port_a)
  connect(res_2.port_b, senTem3a.port_a)
  connect(senTem3a.port_b, hea3.port_a)
  connect(hea3.port_b, senTem3b.port_a)
  connect(senTem3b.port_b, mix1.ports[2])
  connect(hea4.port_b, senTem4b.port_a)
  connect(res_3.port_b, senTem4a.port_a)
  connect(senTem4a.port_b, hea4.port_a)
  connect(senTem4b.port_b, res_4.port_b)
  connect(res_5.port_b, senTem6a.port_a)
  connect(senTem6a.port_b, hea6.port_a)
  connect(hea6.port_b, senTem6b.port_a)
  connect(senTem6b.port_b, senTem5b.port_b)
  connect(senTem5b.port_a, hea5.port_b)
  connect(hea5.port_a, senTem5a.port_b)
  connect(senTem5a.port_a, res_1.port_b)
  connect(res_7.port_b, senTem8a.port_a)
  connect(senTem8a.port_b, hea8.port_a)
  connect(hea8.port_b, senTem8b.port_a)
  connect(senTem8b.port_b, res_8.port_b)
  connect(mix2.ports[2], senTem7b.port_b)
  connect(senTem7b.port_a, hea7.port_b)
  connect(hea7.port_a, senTem7a.port_b)
  connect(senTem7a.port_a, res_6.port_b)
end HeaterCooler_u;


Nominal water mass flow rate

within AixLib.Fluid.Interfaces.Examples;
model Humidifier_u
  extends Modelica.Icons.Example;

 package Medium = AixLib.Media.Air;
  parameter Modelica.Units.SI.MassFlowRate mWat_flow_nominal=0.001
    "Nominal water mass flow rate";
  Humidifier hea1(redeclare package Medium =
        Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  Modelica.Blocks.Sources.Constant TDb(k=293.15) "Drybulb temperature"
  AixLib.Fluid.Sources.Boundary_pT sou_1(
    redeclare package Medium = Medium,
    use_T_in=true,
    nPorts=4,
    p(displayUnit="Pa") = 101435,
    T=293.15)
  AixLib.Fluid.FixedResistances.PressureDrop res_11(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_12(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.Sources.Boundary_pT sin_1(
    redeclare package Medium = Medium,
    use_p_in=true,
    T=288.15,
    nPorts=4)
    Modelica.Blocks.Sources.Constant POut(k=101325)
    Modelica.Blocks.Sources.Ramp u(
    duration=3600,
    startTime=0,
    height=1,
    offset=0) "Control signal"
  Humidifier hea2(
    redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  Modelica.Blocks.Math.Gain gain(k=-1)
  Humidifier hea3(
    redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_2(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_3(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Humidifier hea4(
    redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_4(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.MixingVolumes.MixingVolume mix1(
    redeclare package Medium = Medium,
    V=0.000001,
    nPorts=2,
    m_flow_nominal=0.5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
  Modelica.Blocks.Math.Add che1(k2=-1)
  Modelica.Blocks.Sources.RealExpression y1(y=hea2.staB.T)
  Modelica.Blocks.Sources.RealExpression y2(y=hea1.staB.T)
  Modelica.Blocks.Math.Add che2(k2=-1)
  Modelica.Blocks.Sources.RealExpression y3(y=hea2.staA.T)
  Modelica.Blocks.Sources.RealExpression y4(y=hea1.staA.T)
  Modelica.Blocks.Math.Add che3(k2=-1)
  Modelica.Blocks.Sources.RealExpression y5(y=hea4.staB.T)
  Modelica.Blocks.Sources.RealExpression y6(y=hea3.staB.T)
  Modelica.Blocks.Math.Add che4(k2=-1)
  Modelica.Blocks.Sources.RealExpression y7(y=hea4.staA.T)
  Modelica.Blocks.Sources.RealExpression y8(y=hea3.staA.T)
  Humidifier hea5(redeclare package Medium =
        Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_1(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_5(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Humidifier hea6(                               redeclare package Medium =
        Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  Humidifier hea7(redeclare package Medium =
        Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_6(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.FixedResistances.PressureDrop res_7(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Humidifier hea8(                               redeclare package Medium =
        Medium,
    m_flow_nominal=0.5,
    mWat_flow_nominal=mWat_flow_nominal,
    dp_nominal=50,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Heater and cooler"
  AixLib.Fluid.FixedResistances.PressureDrop res_8(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  AixLib.Fluid.MixingVolumes.MixingVolume mix2(
                                           redeclare package Medium = Medium, V=
       0.000001,
    nPorts=2,
    m_flow_nominal=0.5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
  Modelica.Blocks.Math.Add che5(k2=-1)
  Modelica.Blocks.Sources.RealExpression y9(y=hea6.staB.T)
  Modelica.Blocks.Sources.RealExpression y10(
                                            y=hea5.staB.T)
  Modelica.Blocks.Math.Add che6(k2=-1)
  Modelica.Blocks.Sources.RealExpression y11(
                                            y=hea6.staA.T)
  Modelica.Blocks.Sources.RealExpression y12(
                                            y=hea5.staA.T)
  Modelica.Blocks.Math.Add che7(k2=-1)
  Modelica.Blocks.Sources.RealExpression y13(
                                            y=hea8.staB.T)
  Modelica.Blocks.Sources.RealExpression y14(
                                            y=hea7.staB.T)
  Modelica.Blocks.Math.Add che8(k2=-1)
  Modelica.Blocks.Sources.RealExpression y15(
                                            y=hea8.staA.T)
  Modelica.Blocks.Sources.RealExpression y16(
                                            y=hea7.staA.T)
  Modelica.Blocks.Math.Add che9(k2=-1)
  Modelica.Blocks.Sources.RealExpression y17(y=hea2.staB.T)
  Modelica.Blocks.Sources.RealExpression y18(y=hea5.staB.T)
  Modelica.Blocks.Math.Add che10(k2=-1)
  Modelica.Blocks.Sources.RealExpression y19(y=hea4.staA.T)
  Modelica.Blocks.Sources.RealExpression y20(y=hea7.staA.T)
  Modelica.Blocks.Math.Add che11(k2=-1)
  Modelica.Blocks.Sources.RealExpression y21(y=hea2.staB.X[1])
  Modelica.Blocks.Sources.RealExpression y22(y=hea1.staB.X[1])
  Modelica.Blocks.Math.Add che12(k2=-1)
  Modelica.Blocks.Sources.RealExpression y23(
                                            y=hea2.staA.X[1])
  Modelica.Blocks.Sources.RealExpression y24(
                                            y=hea1.staA.X[1])
  Modelica.Blocks.Math.Add che13(k2=-1)
  Modelica.Blocks.Sources.RealExpression y25(
                                            y=hea4.staB.X[1])
  Modelica.Blocks.Sources.RealExpression y26(
                                            y=hea3.staB.X[1])
  Modelica.Blocks.Math.Add che14(k2=-1)
  Modelica.Blocks.Sources.RealExpression y27(
                                            y=hea4.staA.X[1])
  Modelica.Blocks.Sources.RealExpression y28(
                                            y=hea3.staA.X[1])
  Modelica.Blocks.Math.Add che15(k2=-1)
  Modelica.Blocks.Sources.RealExpression y29(
                                            y=hea6.staB.X[1])
  Modelica.Blocks.Sources.RealExpression y30(
                                            y=hea5.staB.X[1])
  Modelica.Blocks.Math.Add che16(k2=-1)
  Modelica.Blocks.Sources.RealExpression y31(
                                            y=hea6.staA.X[1])
  Modelica.Blocks.Sources.RealExpression y32(
                                            y=hea5.staA.X[1])
  Modelica.Blocks.Math.Add che17(k2=-1)
  Modelica.Blocks.Sources.RealExpression y33(
                                            y=hea8.staB.X[1])
  Modelica.Blocks.Sources.RealExpression y34(
                                            y=hea7.staB.X[1])
  Modelica.Blocks.Math.Add che18(k2=-1)
  Modelica.Blocks.Sources.RealExpression y35(
                                            y=hea8.staA.X[1])
  Modelica.Blocks.Sources.RealExpression y36(
                                            y=hea7.staA.X[1])
  Modelica.Blocks.Math.Add che19(k2=-1)
  Modelica.Blocks.Sources.RealExpression y37(y=hea2.staB.X[1])
  Modelica.Blocks.Sources.RealExpression y38(y=hea5.staB.X[1])
  Modelica.Blocks.Math.Add che20(k2=-1)
  Modelica.Blocks.Sources.RealExpression y39(y=hea4.staA.X[1])
  Modelica.Blocks.Sources.RealExpression y40(y=hea7.staA.X[1])

protected
  model Humidifier
    "Model for humidifier that adds a variable for the thermodynamic states at its ports"
    extends AixLib.Fluid.Humidifiers.Humidifier_u;
   Medium.ThermodynamicState staA=
      Medium.setState_phX(port_a.p,
                          actualStream(port_a.h_outflow),
                          actualStream(port_a.Xi_outflow))
      "Thermodynamic state in port a";
   Medium.ThermodynamicState staB=
      Medium.setState_phX(port_b.p,
                          actualStream(port_b.h_outflow),
                          actualStream(port_b.Xi_outflow))
      "Thermodynamic state in port b";
  end Humidifier;
equation
  connect(POut.y,sin_1. p_in)
  connect(TDb.y,sou_1. T_in)
  connect(res_11.port_b, hea1.port_a)
  connect(u.y, hea1.u)
  connect(gain.y, hea2.u)
  connect(u.y, gain.u)
  connect(res_12.port_b, hea2.port_a)
  connect(res_2.port_b, hea3.port_a)
  connect(u.y, hea3.u)
  connect(gain.y, hea4.u)
  connect(res_3.port_b, hea4.port_a)
  connect(hea4.port_b, res_4.port_b)
  connect(hea1.port_b, hea2.port_b)
  connect(y1.y,che1. u1)
  connect(y2.y,che1. u2)
  connect(y3.y,che2. u1)
  connect(y4.y,che2. u2)
  connect(y5.y,che3. u1)
  connect(y6.y,che3. u2)
  connect(y7.y,che4. u1)
  connect(y8.y,che4. u2)
  connect(res_1.port_b, hea5.port_a)
  connect(res_5.port_b, hea6.port_a)
  connect(res_6.port_b,hea7. port_a)
  connect(res_7.port_b,hea8. port_a)
  connect(hea8.port_b,res_8. port_b)
  connect(hea5.port_b,hea6. port_b)
  connect(y9.y,che5. u1)
  connect(y10.y,che5. u2)
  connect(y11.y,che6. u1)
  connect(y12.y,che6. u2)
  connect(y13.y,che7. u1)
  connect(y14.y,che7. u2)
  connect(y15.y,che8. u1)
  connect(y16.y,che8. u2)
  connect(y17.y,che9. u1)
  connect(y18.y,che9. u2)
  connect(y19.y,che10. u1)
  connect(y20.y,che10. u2)
  connect(u.y, hea6.u)
  connect(u.y, hea8.u)
  connect(gain.y, hea5.u)
  connect(gain.y, hea7.u)
  connect(y21.y,che11. u1)
  connect(y22.y,che11. u2)
  connect(y23.y,che12. u1)
  connect(y24.y,che12. u2)
  connect(y25.y,che13. u1)
  connect(y26.y,che13. u2)
  connect(y27.y,che14. u1)
  connect(y28.y,che14. u2)
  connect(y29.y,che15. u1)
  connect(y30.y,che15. u2)
  connect(y31.y,che16. u1)
  connect(y32.y,che16. u2)
  connect(y33.y,che17. u1)
  connect(y34.y,che17. u2)
  connect(y35.y,che18. u1)
  connect(y36.y,che18. u2)
  connect(y37.y,che19. u1)
  connect(y38.y,che19. u2)
  connect(y39.y,che20. u1)
  connect(y40.y,che20. u2)
  connect(sin_1.ports[1], res_12.port_a)
  connect(sin_1.ports[2], res_3.port_a)
  connect(sou_1.ports[1], res_11.port_a)
  connect(sou_1.ports[2], res_2.port_a)
  connect(sin_1.ports[3], res_1.port_a)
  connect(sin_1.ports[4], res_6.port_a)
  connect(sou_1.ports[3], res_5.port_a)
  connect(sou_1.ports[4], res_7.port_a)
  connect(hea3.port_b, mix1.ports[1])
  connect(mix1.ports[2], res_4.port_a)
  connect(hea7.port_b, mix2.ports[1])
  connect(mix2.ports[2], res_8.port_a)
end Humidifier_u;


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


Test model for prescribed outlet state. Model that demonstrates

AixLib.Fluid.Interfaces.PrescribedOutlet
with mass fraction setpoint.

within AixLib.Fluid.Interfaces.Examples;
model PrescribedOutlet "Test model for prescribed outlet state"
  extends Modelica.Icons.Example;
  extends AixLib.Fluid.Interfaces.Examples.BaseClasses.PrescribedOutletState;

equation
  connect(setHeaHigPow.y[1], heaHigPow.TSet)
  connect(setHeaHigPow.y[2], heaHigPow.X_wSet)
  connect(setCooLimPow.y[1], cooLimPow.TSet)
  connect(setCooLimPow.y[2], cooLimPow.X_wSet)
  connect(setHeaCooUnl.y[1], heaCooUnl.TSet)
  connect(setHeaCooUnl.y[2], heaCooUnl.X_wSet)
  connect(setHeaCooUnl.y[1], steSta.TSet)
  connect(setHeaCooUnl.y[2], steSta.X_wSet)
end PrescribedOutlet;


Test model for prescribed outlet state. Model that demonstrates

AixLib.Fluid.Interfaces.PrescribedOutlet
with temperature setpoint.

within AixLib.Fluid.Interfaces.Examples;
model PrescribedOutlet_TSetOnly "Test model for prescribed outlet state"
  extends Modelica.Icons.Example;
  extends AixLib.Fluid.Interfaces.Examples.BaseClasses.PrescribedOutletState(
    steSta(use_X_wSet=false),
    heaHigPow(use_X_wSet=false),
    cooLimPow(use_X_wSet=false),
    heaCooUnl(use_X_wSet=false));

equation
  connect(setHeaHigPow.y[1], heaHigPow.TSet)
  connect(setCooLimPow.y[1], cooLimPow.TSet)
  connect(setHeaCooUnl.y[1], heaCooUnl.TSet)
  connect(setHeaCooUnl.y[1], steSta.TSet)
end PrescribedOutlet_TSetOnly;


Test model for prescribed outlet state

within AixLib.Fluid.Interfaces.Examples;
model PrescribedOutlet_Water
  "Test model for prescribed outlet state"
  extends Modelica.Icons.Example;
  extends AixLib.Fluid.Interfaces.Examples.BaseClasses.PrescribedOutletState(
    redeclare package Medium = AixLib.Media.Water,
    steSta(use_X_wSet=false),
    heaHigPow(use_X_wSet=false),
    cooLimPow(use_X_wSet=false),
    heaCooUnl(use_X_wSet=false));

equation
  connect(setHeaHigPow.y[1], heaHigPow.TSet)
  connect(setCooLimPow.y[1], cooLimPow.TSet)
  connect(setHeaCooUnl.y[1], heaCooUnl.TSet)
  connect(setHeaCooUnl.y[1], steSta.TSet)
end PrescribedOutlet_Water;


within AixLib.Fluid.Interfaces.Examples;
model PrescribedOutlet_X_wSetOnly
  "Test model for prescribed outlet state"
  extends Modelica.Icons.Example;
  extends AixLib.Fluid.Interfaces.Examples.BaseClasses.PrescribedOutletState(
    heaHigPow(use_TSet=false),
    cooLimPow(use_TSet=false),
    heaCooUnl(use_TSet=false),
    steSta(use_TSet=false));

equation
  connect(setHeaHigPow.y[2], heaHigPow.X_wSet)
  connect(setCooLimPow.y[2], cooLimPow.X_wSet)
  connect(setHeaCooUnl.y[2], heaCooUnl.X_wSet)
  connect(setHeaCooUnl.y[2], steSta.X_wSet)
end PrescribedOutlet_X_wSetOnly;


within AixLib.Fluid.Interfaces.Examples;
model ReverseFlowHumidifier
  "Model that tests the reverse flow for a humidifier"
  extends Modelica.Icons.Example;
package Medium = AixLib.Media.Air;
  Modelica.Blocks.Math.Add cheTem(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Blocks.Math.Add cheEnt(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Blocks.Math.Add cheMas(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  AixLib.Fluid.Humidifiers.Humidifier_u humBac(
    redeclare package Medium = Medium,
    dp_nominal=0,
    m_flow(start=1),
    m_flow_nominal=1,
    mWat_flow_nominal=0.1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Humidifier with backward flow"
  AixLib.Fluid.Humidifiers.Humidifier_u humFor(
    redeclare package Medium = Medium,
    dp_nominal=0,
    m_flow(start=1),
    m_flow_nominal=1,
    mWat_flow_nominal=0.1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Humidifier with forward flow"
  Modelica.Blocks.Sources.Constant u2(k=0.01) "Control input"
  Modelica.Fluid.Sources.MassFlowSource_T source1(
    redeclare package Medium = Medium,
    use_m_flow_in=false,
    use_T_in=false,
    use_X_in=false,
    T(displayUnit="K") = 323.15,
    X={0.01,0.99},
    nPorts=1,
    m_flow=0.5) "Fluid source"
  AixLib.Fluid.Sources.Boundary_pT  sink1(
    redeclare package Medium = Medium,
    nPorts=2) "Fluid sink"
  Sensors.SpecificEnthalpy senEnt1(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "Specific enthalpy sensor"
  AixLib.Fluid.Sensors.Temperature senTem1(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "Temperature sensor"
  Sensors.MassFraction senMas1(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "Mass fraction sensor"
  AixLib.Fluid.Sensors.Temperature senTem2(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "Temperature sensor"
  Sensors.SpecificEnthalpy senEnt2(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "Specific enthalpy sensor"
  Sensors.MassFraction senMas2(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "Mass fraction sensor"
  FixedResistances.PressureDrop res1(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    from_dp=true,
    linearized=false,
    dp_nominal=1000) "Fixed resistance"
  FixedResistances.PressureDrop res2(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    from_dp=true,
    linearized=false,
    dp_nominal=1000) "Fixed resistance"
  Modelica.Fluid.Sources.MassFlowSource_T source2(
    redeclare package Medium = Medium,
    use_m_flow_in=false,
    use_T_in=false,
    use_X_in=false,
    T(displayUnit="K") = 323.15,
    X={0.01,0.99},
    nPorts=1,
    m_flow=0.5) "Fluid source"

equation
  connect(u2.y, humFor.u)
  connect(u2.y, humBac.u)
  connect(humFor.port_b, senTem1.port)
  connect(humFor.port_b, senEnt1.port)
  connect(humFor.port_b, senMas1.port)
  connect(humBac.port_a, senTem2.port)
  connect(humBac.port_a, senEnt2.port)
  connect(humBac.port_a, senMas2.port)
  connect(humFor.port_b, res1.port_a)
  connect(res1.port_b, sink1.ports[1])
  connect(humBac.port_a, res2.port_a)
  connect(res2.port_b, sink1.ports[2])
  connect(senTem1.T,cheTem. u1)
  connect(senEnt1.h_out,cheEnt. u1)
  connect(senMas1.X,cheMas. u1)
  connect(senTem2.T,cheTem. u2)
  connect(senEnt2.h_out,cheEnt. u2)
  connect(senMas2.X,cheMas. u2)
  connect(humFor.port_a, source1.ports[1])
  connect(source2.ports[1], humBac.port_b)
end ReverseFlowHumidifier;


within AixLib.Fluid.Interfaces.Examples;
model ReverseFlowMassExchanger
  "Model that tests the reverse flow for a mass exchanger"
  extends Modelica.Icons.Example;
package Medium = AixLib.Media.Air;
  Modelica.Blocks.Math.Add cheTem(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Blocks.Math.Add cheEnt(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Blocks.Math.Add cheMas(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Fluid.Sources.MassFlowSource_T source2(
    m_flow=1,
    redeclare package Medium = Medium,
    use_m_flow_in=false,
    use_T_in=false,
    use_X_in=false,
    nPorts=1,
    T(displayUnit="degC") = 303.15,
    X={0.02,0.98})
  AixLib.Fluid.MassExchangers.ConstantEffectiveness masExcFor(
    redeclare package Medium1 = Medium,
    redeclare package Medium2 = Medium,
    m1_flow_nominal=1,
    m2_flow_nominal=1,
    dp1_nominal=0,
    dp2_nominal=0,
    epsL=0) "Mass exchanger with forward flow"
  AixLib.Fluid.MassExchangers.ConstantEffectiveness masExcRev(
    redeclare package Medium1 = Medium,
    redeclare package Medium2 = Medium,
    m1_flow_nominal=1,
    m2_flow_nominal=1,
    dp1_nominal=0,
    dp2_nominal=0,
    epsL=0) "Mass exchanger with reverse flow"
  AixLib.Fluid.Sources.Boundary_pT sink2(
     redeclare package Medium = Medium,
     nPorts=2) "Fluid sink"
  FixedResistances.PressureDrop res3(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    from_dp=true,
    linearized=false,
    dp_nominal=1000) "Fixed resistance"
  FixedResistances.PressureDrop res4(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    from_dp=true,
    linearized=false,
    dp_nominal=1000) "Fixed resistance"
  Sensors.SpecificEnthalpy senEnt3(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  AixLib.Fluid.Sensors.Temperature senTem3(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  Sensors.MassFraction senMas3(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  Sensors.SpecificEnthalpy senEnt4(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  AixLib.Fluid.Sensors.Temperature senTem4(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  Sensors.MassFraction senMas4(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  Modelica.Blocks.Math.Add cheTem1(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Blocks.Math.Add cheEnt1(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Blocks.Math.Add cheMas1(k2=-1)
    "Check whether the outputs of the forward flow and reverse flow model are identical"
  Modelica.Fluid.Sources.MassFlowSource_T source3(
    m_flow=1,
    redeclare package Medium = Medium,
    use_m_flow_in=false,
    use_T_in=false,
    use_X_in=false,
    X={0.01,0.99},
    nPorts=1,
    T(displayUnit="degC") = 293.15)
  Modelica.Fluid.Sources.MassFlowSource_T source4(
    m_flow=1,
    redeclare package Medium = Medium,
    use_m_flow_in=false,
    use_T_in=false,
    use_X_in=false,
    nPorts=1,
    T(displayUnit="degC") = 303.15,
    X={0.02,0.98})

  Modelica.Fluid.Sources.MassFlowSource_T source1(
    m_flow=1,
    redeclare package Medium = Medium,
    use_m_flow_in=false,
    use_T_in=false,
    use_X_in=false,
    X={0.01,0.99},
    nPorts=1,
    T(displayUnit="degC") = 293.15)
  Sources.Boundary_pT sink1(
    redeclare package Medium = Medium,
    nPorts=2) "Fluid sink"
  Sensors.SpecificEnthalpy senEnt1(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  AixLib.Fluid.Sensors.Temperature senTem1(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  Sensors.MassFraction senMas1(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  AixLib.Fluid.Sensors.Temperature senTem2(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  Sensors.SpecificEnthalpy senEnt2(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  Sensors.MassFraction senMas2(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
  FixedResistances.PressureDrop res1(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    from_dp=true,
    linearized=false,
    dp_nominal=1000) "Fixed resistance"
  FixedResistances.PressureDrop res2(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    from_dp=true,
    linearized=false,
    dp_nominal=1000) "Fixed resistance"

equation
  connect(res1.port_b, sink1.ports[1])
  connect(res2.port_b, sink1.ports[2])
  connect(senTem1.T,cheTem. u1)
  connect(senEnt1.h_out,cheEnt. u1)
  connect(senMas1.X,cheMas. u1)
  connect(senTem2.T,cheTem. u2)
  connect(senEnt2.h_out,cheEnt. u2)
  connect(senMas2.X,cheMas. u2)
  connect(masExcFor.port_b1, res1.port_a)
  connect(masExcFor.port_a1, source1.ports[1])
  connect(masExcRev.port_a1, res2.port_a)
  connect(source2.ports[1], masExcFor.port_a2)
  connect(masExcRev.port_a1, senTem2.port)
  connect(masExcRev.port_a1, senEnt2.port)
  connect(masExcRev.port_a1, senMas2.port)
  connect(masExcFor.port_b1, senTem1.port)
  connect(masExcFor.port_b1, senEnt1.port)
  connect(masExcFor.port_b1, senMas1.port)
  connect(masExcFor.port_b2, res3.port_a)
  connect(masExcRev.port_a2, res4.port_a)
  connect(res3.port_b, sink2.ports[1])
  connect(res4.port_b, sink2.ports[2])
  connect(senTem3.T,cheTem1. u1)
  connect(senTem4.T,cheTem1. u2)
  connect(senEnt3.h_out,cheEnt1. u1)
  connect(senEnt4.h_out,cheEnt1. u2)
  connect(senMas3.X,cheMas1. u1)
  connect(senMas4.X,cheMas1. u2)
  connect(masExcFor.port_b2, senTem3.port)
  connect(masExcFor.port_b2, senEnt3.port)
  connect(masExcFor.port_b2, senMas3.port)
  connect(masExcRev.port_a2, senTem4.port)
  connect(masExcRev.port_a2, senEnt4.port)
  connect(masExcRev.port_a2, senMas4.port)
  connect(source3.ports[1], masExcRev.port_b1)
  connect(source4.ports[1], masExcRev.port_b2)
end ReverseFlowMassExchanger;


within AixLib.Fluid.Interfaces.Examples;
model StaticTwoPortConservationEquation
  "Model that tests the static two port conservation equation"
extends Modelica.Icons.Example;
 package MediumW = AixLib.Media.Water "Medium model";
 package MediumA = AixLib.Media.Air "Medium model";

  Modelica.Blocks.Sources.Constant mWat_flow(k=0)
    "Water mass flow rate added to the control volume"
  Modelica.Blocks.Sources.TimeTable QSen_flow(startTime=0, table=[
                                0,-100;
                                900,-100;
                                900,0;
                                1800,0;
                                1800,100])
    "Sensible heat flow rate added to the control volume"

  SubModel modWatRev(
    redeclare package Medium = MediumW,
    allowFlowReversal=true,
    use_mWat_flow = false) "Submodel for energy and mass balance"
  SubModel modWatNoRev(
    redeclare package Medium = MediumW,
    allowFlowReversal=false,
    use_mWat_flow = false) "Submodel for energy and mass balance"
  SubModel modAirRev(
    redeclare package Medium = MediumA,
    allowFlowReversal=true,
    use_mWat_flow = true) "Submodel for energy and mass balance"
  SubModel modAirNoRev(
    redeclare package Medium = MediumA,
    allowFlowReversal=false,
    use_mWat_flow = true) "Submodel for energy and mass balance"
equation

  connect(QSen_flow.y, modWatRev.Q_flow)
  connect(mWat_flow.y, modWatRev.mWat_flow)
protected
  model SubModel
    replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
      "Medium model";
    Sources.MassFlowSource_T bou(
      nPorts=1,
      redeclare package Medium = Medium,
      m_flow=0.01) "Boundary condition for mass flow rate"
    AixLib.Fluid.Interfaces.StaticTwoPortConservationEquation conEqn(
      redeclare package Medium = Medium,
      m_flow_nominal=0.01,
      show_T=true,
      allowFlowReversal=allowFlowReversal,
      use_mWat_flow=use_mWat_flow,
      prescribedHeatFlowRate=true) "Steady-state conservation equation"
    AixLib.Fluid.Sources.Boundary_pT sin(
      use_p_in=false,
      redeclare package Medium = Medium,
      nPorts=1,
      p=101325,
      T=283.15) "Sink"

    Modelica.Blocks.Interfaces.RealInput Q_flow(unit="W")
    Modelica.Blocks.Interfaces.RealInput mWat_flow(unit="kg/s")
    parameter Boolean use_mWat_flow
      "Set to true to enable exchange of moisture";
    parameter Boolean allowFlowReversal=true
      "= false to simplify equations, assuming, but not enforcing, no flow reversal";
  equation
    connect(conEqn.port_b, sin.ports[1])
    connect(conEqn.port_a, bou.ports[1])
    connect(Q_flow, conEqn.Q_flow)
    connect(mWat_flow, conEqn.mWat_flow)
  end SubModel;
equation
  connect(QSen_flow.y, modWatNoRev.Q_flow)
  connect(mWat_flow.y, modWatNoRev.mWat_flow)
  connect(QSen_flow.y, modAirRev.Q_flow)
  connect(mWat_flow.y, modAirRev.mWat_flow)
  connect(QSen_flow.y, modAirNoRev.Q_flow)
  connect(mWat_flow.y, modAirNoRev.mWat_flow)
end StaticTwoPortConservationEquation;


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


Test model for prescribed outlet state

within AixLib.Fluid.Interfaces.Examples.BaseClasses;
partial model PrescribedOutletState "Test model for prescribed outlet state"

  replaceable package Medium = AixLib.Media.Air
     constrainedby Modelica.Media.Interfaces.PartialMedium  "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1
    "Nominal mass flow rate";

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_T_in=false,
    p(displayUnit="Pa"),
    T=293.15,
    nPorts=4) "Sink"
  AixLib.Fluid.Interfaces.PrescribedOutlet heaHigPow(
    redeclare package Medium = Medium,
    QMax_flow=1.0e10,
    mWatMax_flow = 0.001,
    m_flow_small=1E-4*abs(m_flow_nominal),
    show_T=true,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Steady-state model of the heater with high capacity"
  Modelica.Blocks.Sources.CombiTimeTable setHeaHigPow(table=[
    0,    20.0, 0.012;
    120,  20.0, 0.012;
    500,  60.0, 0.020;
    800,  10.0, 0.005;
    1200, 30.0, 0.015],
    offset={273.15,0},
    smoothness = Modelica.Blocks.Types.Smoothness.ConstantSegments)
    "Setpoint heating and humidification"
  AixLib.Fluid.Interfaces.PrescribedOutlet cooLimPow(
    redeclare package Medium = Medium,
    QMin_flow=-1000,
    mWatMin_flow = -0.001,
    m_flow_small=1E-4*abs(m_flow_nominal),
    show_T=true,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Steady-state model of the cooler with limited capacity"
  Modelica.Blocks.Sources.CombiTimeTable setCooLimPow(table=[
    0,    20.0, 0.008;
    120,  20.0, 0.008;
    500,  15.0, 0.004;
    800,  60.0, 0.015;
    1200, 10.0, 0.002],
    offset={273.15,0},
    smoothness = Modelica.Blocks.Types.Smoothness.ConstantSegments)
    "Setpoint cooling and dehumidification"
  AixLib.Fluid.Interfaces.PrescribedOutlet heaCooUnl(
    redeclare package Medium = Medium,
    m_flow_small=1E-4*abs(m_flow_nominal),
    show_T=true,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Steady-state model of the heater or cooler with unlimited capacity"
  AixLib.Fluid.Interfaces.PrescribedOutlet steSta(
    redeclare package Medium = Medium,
    m_flow_small=1E-4*abs(m_flow_nominal),
    show_T=true,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Steady-state model of the heater or cooler with unlimited capacity"
  Modelica.Blocks.Sources.CombiTimeTable setHeaCooUnl(table=[
    0,    20.0, 0.012;
    120,  20.0, 0.012;
    500,  15.0, 0.008;
    800,  10.0, 0.005;
    1200, 30.0, 0.015],
    offset={273.15,0},
    smoothness = Modelica.Blocks.Types.Smoothness.ConstantSegments)
    "Setpoint cooling, heating, dehumidification and humidification"
  Modelica.Blocks.Sources.Ramp m_flow(
    height=-2*m_flow_nominal,
    duration=100,
    offset=m_flow_nominal,
    startTime=1000) "Mass flow rate"
  AixLib.Fluid.Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=293.15,
    nPorts=1) "Flow source"
  AixLib.Fluid.Sources.MassFlowSource_T sou2(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=293.15,
    nPorts=1) "Flow source"
  AixLib.Fluid.Sources.MassFlowSource_T sou3(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=293.15,
    nPorts=1) "Flow source"
  AixLib.Fluid.Sources.MassFlowSource_T sou4(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=293.15,
    nPorts=1) "Flow source"
equation

  connect(m_flow.y, sou1.m_flow_in)
  connect(m_flow.y, sou2.m_flow_in)
  connect(m_flow.y, sou3.m_flow_in)
  connect(sou1.ports[1], heaHigPow.port_a)
  connect(sou2.ports[1], cooLimPow.port_a)
  connect(sou3.ports[1], heaCooUnl.port_a)
  connect(heaCooUnl.port_b, sin.ports[1])
  connect(cooLimPow.port_b, sin.ports[2])
  connect(heaHigPow.port_b, sin.ports[3])
  connect(m_flow.y, sou4.m_flow_in)
  connect(sou4.ports[1], steSta.port_a)
  connect(steSta.port_b, sin.ports[4])
end PrescribedOutletState;


within AixLib.Fluid.MassExchangers;
model ConstantEffectiveness
  "Heat and moisture exchanger with constant effectiveness"
  extends AixLib.Fluid.HeatExchangers.BaseClasses.PartialEffectiveness(
  redeclare replaceable package Medium1 =
        Modelica.Media.Interfaces.PartialCondensingGases,
  redeclare replaceable package Medium2 =
        Modelica.Media.Interfaces.PartialCondensingGases,
  sensibleOnly1=false,
  sensibleOnly2=false,
  final prescribedHeatFlowRate1=true,
  final prescribedHeatFlowRate2=true,
  Q1_flow = epsS * QMax_flow + QLat_flow,
  Q2_flow = -Q1_flow,
  mWat1_flow = +mWat_flow,
  mWat2_flow = -mWat_flow);

  parameter Modelica.Units.SI.Efficiency epsS(max=1) = 0.8
    "Sensible heat exchanger effectiveness";
  parameter Modelica.Units.SI.Efficiency epsL(max=1) = 0.8
    "Latent heat exchanger effectiveness";

  Modelica.Units.SI.HeatFlowRate QLat_flow
    "Latent heat exchange from medium 2 to medium 1";

  Medium1.MassFraction X_w_in1 "Inlet water mass fraction of medium 1";
  Medium2.MassFraction X_w_in2 "Inlet water mass fraction of medium 2";

  Modelica.Units.SI.MassFlowRate mWat_flow
    "Water flow rate from medium 2 to medium 1";
  Modelica.Units.SI.MassFlowRate mMax_flow
    "Maximum water flow rate from medium 2 to medium 1";

protected
  parameter Integer i1_w(min=1, fixed=false) "Index for water substance";
  parameter Integer i2_w(min=1, fixed=false) "Index for water substance";
  Real gai1(min=0, max=1) "Auxiliary variable for smoothing at zero flow";
  Real gai2(min=0, max=1) "Auxiliary variable for smoothing at zero flow";
initial algorithm
  i1_w:= -1;
  i2_w:= -1;
  for i in 1:Medium1.nXi loop
      if Modelica.Utilities.Strings.isEqual(string1=Medium1.substanceNames[i],
                                            string2="Water",
                                            caseSensitive=false) then
      i1_w := i;
    end if;
   end for;
  for i in 1:Medium2.nXi loop
      if Modelica.Utilities.Strings.isEqual(string1=Medium2.substanceNames[i],
                                            string2="Water",
                                            caseSensitive=false) then
      i2_w := i;
    end if;
   end for;
    assert(i1_w > 0, "Substance 'water' is not present in Medium1 '"
         + Medium1.mediumName + "'.\n"
         + "Check medium model.");
    assert(i2_w > 0, "Substance 'water' is not present in Medium2 '"
         + Medium2.mediumName + "'.\n"
         + "Check medium model.");
equation
  // Definitions for effectiveness model
  X_w_in1 = Modelica.Fluid.Utilities.regStep(m1_flow,
                  state_a1_inflow.X[i1_w],
                  state_b1_inflow.X[i1_w], m1_flow_small);
  X_w_in2 = Modelica.Fluid.Utilities.regStep(m2_flow,
                  state_a2_inflow.X[i2_w],
                  state_b2_inflow.X[i2_w], m2_flow_small);

  // mass exchange
  // Compute a gain that goes to zero near zero flow rate.
  // This is required to smoothen the heat transfer at very small flow rates.
  // Note that gaiK = 1 for abs(mK_flow) > mK_flow_small
  gai1 = Modelica.Fluid.Utilities.regStep(abs(m1_flow) - 0.75*m1_flow_small,
              1, 0, 0.25*m1_flow_small);
  gai2 = Modelica.Fluid.Utilities.regStep(abs(m2_flow) - 0.75*m2_flow_small,
              1, 0, 0.25*m2_flow_small);

  mMax_flow = smooth(1, min(smooth(1, gai1 * abs(m1_flow)),
                            smooth(1, gai2 * abs(m2_flow)))) * (X_w_in2 - X_w_in1);
  mWat_flow = epsL * mMax_flow;
  // As enthalpyOfCondensingGas is dominated by the latent heat of phase change,
  // we simplify and use Medium1.enthalpyOfVaporization for the
  // latent heat that is exchanged among the fluid streams.
  // This is simply added to QSen_flow, while mass is conserved because
  // of the assignment of mWat1_flow and mWat2_flow.
  QLat_flow = mWat_flow * Medium1.enthalpyOfVaporization(Medium1.T_default);
end ConstantEffectiveness;


Package with mass exchanger models

within AixLib.Fluid;
package MassExchangers "Package with mass exchanger models"
  extends Modelica.Icons.VariantsPackage;
end MassExchangers;


Water temperature

within AixLib.Fluid.MassExchangers.Examples;
model ConstantEffectiveness
  extends Modelica.Icons.Example;

 package Medium1 = AixLib.Media.Air;
 package Medium2 = AixLib.Media.Air;

  AixLib.Fluid.Sources.Boundary_pT sin_2(
    redeclare package Medium = Medium2, T=273.15 + 10,
    use_p_in=true,
    nPorts=1)
    Modelica.Blocks.Sources.Ramp PIn(
    height=200,
    duration=60,
    offset=101330)
  AixLib.Fluid.Sources.Boundary_pT sou_2(
    redeclare package Medium = Medium2, T=273.15 + 5,
    use_p_in=true,
    use_T_in=true,
    nPorts=1)
    Modelica.Blocks.Sources.Ramp TWat(
    height=10,
    duration=60,
    offset=273.15 + 30,
    startTime=60) "Water temperature"
  Modelica.Blocks.Sources.Constant TDb(k=293.15) "Drybulb temperature"
    Modelica.Blocks.Sources.Constant POut(k=101325)
  AixLib.Fluid.Sources.Boundary_pT sin_1(
    redeclare package Medium = Medium1,
    T=273.15 + 30,
    X={0.012,1 - 0.012},
    use_p_in=true,
    p=300000,
    nPorts=1)
  AixLib.Fluid.Sources.Boundary_pT sou_1(
    redeclare package Medium = Medium1,
    T=273.15 + 50,
    X={0.012,1 - 0.012},
    use_T_in=true,
    p=100000,
    nPorts=1)
    Modelica.Blocks.Sources.Ramp PSin_1(
    duration=60,
    startTime=240,
    height=100,
    offset=1E5 - 110)
  AixLib.Fluid.MassExchangers.ConstantEffectiveness hex(
    redeclare package Medium1 = Medium1,
    redeclare package Medium2 = Medium2,
    m1_flow(start=5),
    m2_flow(start=5),
    m1_flow_nominal=5,
    m2_flow_nominal=5,
    dp1_nominal=100,
    dp2_nominal=100,
    show_T=true)

equation
  connect(PIn.y,sou_2. p_in)
  connect(TDb.y, sou_2.T_in)
  connect(TWat.y, sou_1.T_in)
  connect(PSin_1.y, sin_1.p_in)
  connect(sou_1.ports[1], hex.port_a1)
  connect(hex.port_a2, sou_2.ports[1])
  connect(POut.y, sin_2.p_in)
  connect(hex.port_b1, sin_1.ports[1])
  connect(hex.port_b2, sin_2.ports[1])
end ConstantEffectiveness;


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