within Buildings.Fluid.Interfaces;
model FourPortHeatMassExchanger
  "Model transporting two fluid streams between four ports with storing mass or energy"
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface(
    port_a1(h_outflow(start=h1_outflow_start)),
    port_b1(h_outflow(start=h1_outflow_start)),
    port_a2(h_outflow(start=h2_outflow_start)),
    port_b2(h_outflow(start=h2_outflow_start)));
  extends Buildings.Fluid.Interfaces.FourPortFlowResistanceParameters(
     final computeFlowResistance1=true, final computeFlowResistance2=true);

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

  parameter Modelica.Units.SI.Time tau1=30 "Time constant at nominal flow"
  parameter Modelica.Units.SI.Time tau2=30 "Time constant at nominal flow"

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

  // Initialization
  parameter Medium1.AbsolutePressure p1_start = Medium1.p_default
    "Start value of pressure"
  parameter Medium1.Temperature T1_start = Medium1.T_default
    "Start value of temperature"
  parameter Medium1.MassFraction X1_start[Medium1.nX] = Medium1.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium1.ExtraProperty C1_start[Medium1.nC](
    final quantity=Medium1.extraPropertiesNames)=fill(0, Medium1.nC)
    "Start value of trace substances"
  parameter Medium1.ExtraProperty C1_nominal[Medium1.nC](
    final quantity=Medium1.extraPropertiesNames) = fill(1E-2, Medium1.nC)
    "Nominal value of trace substances. (Set to typical order of magnitude.)"

  parameter Medium2.AbsolutePressure p2_start = Medium2.p_default
    "Start value of pressure"
  parameter Medium2.Temperature T2_start = Medium2.T_default
    "Start value of temperature"
  parameter Medium2.MassFraction X2_start[Medium2.nX] = Medium2.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium2.ExtraProperty C2_start[Medium2.nC](
    final quantity=Medium2.extraPropertiesNames)=fill(0, Medium2.nC)
    "Start value of trace substances"
  parameter Medium2.ExtraProperty C2_nominal[Medium2.nC](
    final quantity=Medium2.extraPropertiesNames) = fill(1E-2, Medium2.nC)
    "Nominal value of trace substances. (Set to typical order of magnitude.)"

  Modelica.Units.SI.HeatFlowRate Q1_flow=vol1.heatPort.Q_flow
    "Heat flow rate into medium 1";
  Modelica.Units.SI.HeatFlowRate Q2_flow=vol2.heatPort.Q_flow
    "Heat flow rate into medium 2";

  replaceable Buildings.Fluid.MixingVolumes.BaseClasses.MixingVolumeHeatPort vol1
    constrainedby
    Buildings.Fluid.MixingVolumes.BaseClasses.MixingVolumeHeatPort(
        redeclare final package Medium = Medium1,
        nPorts = 2,
        V=m1_flow_nominal*tau1/rho1_nominal,
        final allowFlowReversal=allowFlowReversal1,
        final m_flow_nominal=m1_flow_nominal,
        energyDynamics=if tau1 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
        massDynamics=if tau1 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
        final p_start=p1_start,
        final T_start=T1_start,
        final X_start=X1_start,
        final C_start=C1_start,
        final C_nominal=C1_nominal,
        mSenFac=1) "Volume for fluid 1"

  replaceable Buildings.Fluid.MixingVolumes.MixingVolume vol2
    constrainedby
    Buildings.Fluid.MixingVolumes.BaseClasses.MixingVolumeHeatPort(
        redeclare final package Medium = Medium2,
        nPorts = 2,
        V=m2_flow_nominal*tau2/rho2_nominal,
        final allowFlowReversal=allowFlowReversal2,
        mSenFac=1,
        final m_flow_nominal = m2_flow_nominal,
        energyDynamics=if tau2 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
        massDynamics=if tau2 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
        final p_start=p2_start,
        final T_start=T2_start,
        final X_start=X2_start,
        final C_start=C2_start,
        final C_nominal=C2_nominal) "Volume for fluid 2"

  Buildings.Fluid.FixedResistances.PressureDrop preDro1(
    redeclare final package Medium = Medium1,
    final m_flow_nominal=m1_flow_nominal,
    final deltaM=deltaM1,
    final allowFlowReversal=allowFlowReversal1,
    final show_T=false,
    final from_dp=from_dp1,
    final linearized=linearizeFlowResistance1,
    final homotopyInitialization=homotopyInitialization,
    final dp_nominal=dp1_nominal) "Flow resistance of fluid 1"

  Buildings.Fluid.FixedResistances.PressureDrop preDro2(
    redeclare final package Medium = Medium2,
    final m_flow_nominal=m2_flow_nominal,
    final deltaM=deltaM2,
    final allowFlowReversal=allowFlowReversal2,
    final show_T=false,
    final from_dp=from_dp2,
    final linearized=linearizeFlowResistance2,
    final homotopyInitialization=homotopyInitialization,
    final dp_nominal=dp2_nominal) "Flow resistance of fluid 2"

protected
  parameter Medium1.ThermodynamicState sta1_nominal=Medium1.setState_pTX(
      T=Medium1.T_default, p=Medium1.p_default, X=Medium1.X_default);
  parameter Modelica.Units.SI.Density rho1_nominal=Medium1.density(sta1_nominal)
    "Density, used to compute fluid volume";
  parameter Medium2.ThermodynamicState sta2_nominal=Medium2.setState_pTX(
      T=Medium2.T_default, p=Medium2.p_default, X=Medium2.X_default);
  parameter Modelica.Units.SI.Density rho2_nominal=Medium2.density(sta2_nominal)
    "Density, used to compute fluid volume";

  parameter Medium1.ThermodynamicState sta1_start=Medium1.setState_pTX(
      T=T1_start, p=p1_start, X=X1_start);
  parameter Modelica.Units.SI.SpecificEnthalpy h1_outflow_start=
      Medium1.specificEnthalpy(sta1_start)
    "Start value for outflowing enthalpy";
  parameter Medium2.ThermodynamicState sta2_start=Medium2.setState_pTX(
      T=T2_start, p=p2_start, X=X2_start);
  parameter Modelica.Units.SI.SpecificEnthalpy h2_outflow_start=
      Medium2.specificEnthalpy(sta2_start)
    "Start value for outflowing enthalpy";

initial equation
  // Check for tau1
  assert((energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState) or
          tau1 > Modelica.Constants.eps,
"The parameter tau1, 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 tau1 = " + String(tau1) + "\n");

 // Check for tau2
  assert((energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState) or
          tau2 > Modelica.Constants.eps,
"The parameter tau2, 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 tau2 = " + String(tau2) + "\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(vol1.ports[2], port_b1)
  connect(vol2.ports[2], port_b2)
  connect(port_a1, preDro1.port_a)
  connect(preDro1.port_b, vol1.ports[1])
  connect(port_a2, preDro2.port_a)
  connect(preDro2.port_b, vol2.ports[1])
end FourPortHeatMassExchanger;


Declarations for lumped volumes

within Buildings.Fluid.Interfaces;
block LumpedVolumeDeclarations "Declarations for lumped volumes"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  // Assumptions
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"
  parameter Modelica.Fluid.Types.Dynamics massDynamics=energyDynamics
    "Type of mass balance: dynamic (3 initialization options) or steady state, must be steady state if energyDynamics is steady state"
  final parameter Modelica.Fluid.Types.Dynamics substanceDynamics=energyDynamics
    "Type of independent mass fraction balance: dynamic (3 initialization options) or steady state"
  final parameter Modelica.Fluid.Types.Dynamics traceDynamics=energyDynamics
    "Type of trace substance 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](
       quantity=Medium.substanceNames) = Medium.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium.ExtraProperty C_start[Medium.nC](
       quantity=Medium.extraPropertiesNames)=fill(0, Medium.nC)
    "Start value of trace substances"
  parameter Medium.ExtraProperty C_nominal[Medium.nC](
       quantity=Medium.extraPropertiesNames) = fill(1E-2, Medium.nC)
    "Nominal value of trace substances. (Set to typical order of magnitude.)"
  parameter Real mSenFac(min=1)=1
    "Factor for scaling the sensible thermal mass of the volume"

protected
  // The parameter below is evaluated by OCT during compilation, and
  // if false, the assert statement won't be optimized away during
  // code generation.
  final parameter Boolean wrongEnergyMassBalanceConfiguration=
    not (energyDynamics <> Modelica.Fluid.Types.Dynamics.SteadyState or
         massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState)
    "True if configuration of energy and mass balance is wrong."
initial equation
  if wrongEnergyMassBalanceConfiguration then
  assert(not wrongEnergyMassBalanceConfiguration,
         "In " + getInstanceName() +
         ": energyDynamics is selected as steady state, and therefore massDynamics must also be steady-state.");
  end if;
end LumpedVolumeDeclarations;


within Buildings.Fluid;
package Interfaces "Package with interfaces for fluid models"
  extends Modelica.Icons.InterfacesPackage;

package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;

end UsersGuide;
end Interfaces;


within Buildings.Fluid.Interfaces;
partial model PartialEightPortInterface
  "Partial model with eight ports and declaration of quantities that are used by many models"
  extends Buildings.Fluid.Interfaces.EightPort;
  parameter Modelica.Units.SI.MassFlowRate m1_flow_nominal(min=0)
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate m2_flow_nominal(min=0)
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate m3_flow_nominal(min=0)
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate m4_flow_nominal(min=0)
    "Nominal mass flow rate"

  parameter Medium1.MassFlowRate m1_flow_small(min=0) = 1E-4*abs(m1_flow_nominal)
    "Small mass flow rate for regularization of zero flow"
  parameter Medium2.MassFlowRate m2_flow_small(min=0) = 1E-4*abs(m2_flow_nominal)
    "Small mass flow rate for regularization of zero flow"
  parameter Medium3.MassFlowRate m3_flow_small(min=0) = 1E-4*abs(m3_flow_nominal)
    "Small mass flow rate for regularization of zero flow"
  parameter Medium4.MassFlowRate m4_flow_small(min=0) = 1E-4*abs(m4_flow_nominal)
    "Small mass flow rate for regularization of zero flow"

  // Diagnostics
  parameter Boolean show_T = false
    "= true, if actual temperature at port is computed"
  Medium1.MassFlowRate m1_flow = port_a1.m_flow
    "Mass flow rate from port_a1 to port_b1 (m1_flow > 0 is design flow direction)";
  Modelica.Units.SI.Pressure dp1(displayUnit="Pa") = port_a1.p - port_b1.p
    "Pressure difference between port_a1 and port_b1";
  Medium2.MassFlowRate m2_flow = port_a2.m_flow
    "Mass flow rate from port_a2 to port_b2 (m2_flow > 0 is design flow direction)";
  Modelica.Units.SI.Pressure dp2(displayUnit="Pa") = port_a2.p - port_b2.p
    "Pressure difference between port_a2 and port_b2";

  Medium3.MassFlowRate m3_flow = port_a3.m_flow
    "Mass flow rate from port_a3 to port_b3 (m3_flow > 0 is design flow direction)";
  Modelica.Units.SI.Pressure dp3(displayUnit="Pa") = port_a3.p - port_b3.p
    "Pressure difference between port_a3 and port_b3";
  Medium4.MassFlowRate m4_flow = port_a4.m_flow
    "Mass flow rate from port_a4 to port_b4 (m4_flow > 0 is design flow direction)";
  Modelica.Units.SI.Pressure dp4(displayUnit="Pa") = port_a4.p - port_b4.p
    "Pressure difference between port_a4 and port_b4";

  Medium1.ThermodynamicState sta_a1=
    if allowFlowReversal1 then
      Medium1.setState_phX(port_a1.p,
                          noEvent(actualStream(port_a1.h_outflow)),
                          noEvent(actualStream(port_a1.Xi_outflow)))
    else
      Medium1.setState_phX(port_a1.p,
                          inStream(port_a1.h_outflow),
                          inStream(port_a1.Xi_outflow))
      if show_T "Medium properties in port_a1";
  Medium1.ThermodynamicState sta_b1=
    if allowFlowReversal1 then
      Medium1.setState_phX(port_b1.p,
                          noEvent(actualStream(port_b1.h_outflow)),
                          noEvent(actualStream(port_b1.Xi_outflow)))
    else
      Medium1.setState_phX(port_b1.p,
                          port_b1.h_outflow,
                          port_b1.Xi_outflow)
       if show_T "Medium properties in port_b1";

  Medium2.ThermodynamicState sta_a2=
    if allowFlowReversal2 then
      Medium2.setState_phX(port_a2.p,
                          noEvent(actualStream(port_a2.h_outflow)),
                          noEvent(actualStream(port_a2.Xi_outflow)))
    else
      Medium2.setState_phX(port_a2.p,
                          inStream(port_a2.h_outflow),
                          inStream(port_a2.Xi_outflow))
      if show_T "Medium properties in port_a2";
  Medium2.ThermodynamicState sta_b2=
    if allowFlowReversal2 then
      Medium2.setState_phX(port_b2.p,
                          noEvent(actualStream(port_b2.h_outflow)),
                          noEvent(actualStream(port_b2.Xi_outflow)))
    else
      Medium2.setState_phX(port_b2.p,
                          port_b2.h_outflow,
                          port_b2.Xi_outflow)
       if show_T "Medium properties in port_b2";

  Medium3.ThermodynamicState sta_a3=
    if allowFlowReversal3 then
      Medium3.setState_phX(port_a3.p,
                          noEvent(actualStream(port_a3.h_outflow)),
                          noEvent(actualStream(port_a3.Xi_outflow)))
    else
      Medium3.setState_phX(port_a3.p,
                          inStream(port_a3.h_outflow),
                          inStream(port_a3.Xi_outflow))
      if show_T "Medium properties in port_a3";
  Medium3.ThermodynamicState sta_b3=
    if allowFlowReversal3 then
      Medium3.setState_phX(port_b3.p,
                          noEvent(actualStream(port_b3.h_outflow)),
                          noEvent(actualStream(port_b3.Xi_outflow)))
    else
      Medium3.setState_phX(port_b3.p,
                          port_b3.h_outflow,
                          port_b3.Xi_outflow)
       if show_T "Medium properties in port_b3";

  Medium4.ThermodynamicState sta_a4=
    if allowFlowReversal4 then
      Medium4.setState_phX(port_a4.p,
                          noEvent(actualStream(port_a4.h_outflow)),
                          noEvent(actualStream(port_a4.Xi_outflow)))
    else
      Medium4.setState_phX(port_a4.p,
                          inStream(port_a4.h_outflow),
                          inStream(port_a4.Xi_outflow))
      if show_T "Medium properties in port_a4";
  Medium4.ThermodynamicState sta_b4=
    if allowFlowReversal4 then
      Medium4.setState_phX(port_b4.p,
                          noEvent(actualStream(port_b4.h_outflow)),
                          noEvent(actualStream(port_b4.Xi_outflow)))
    else
      Medium4.setState_phX(port_b4.p,
                          port_b4.h_outflow,
                          port_b4.Xi_outflow)
       if show_T "Medium properties in port_b4";

protected
  Medium1.ThermodynamicState state_a1_inflow=
    Medium1.setState_phX(port_a1.p, inStream(port_a1.h_outflow), inStream(port_a1.Xi_outflow))
    "state for medium inflowing through port_a1";
  Medium1.ThermodynamicState state_b1_inflow=
    Medium1.setState_phX(port_b1.p, inStream(port_b1.h_outflow), inStream(port_b1.Xi_outflow))
    "state for medium inflowing through port_b1";
  Medium2.ThermodynamicState state_a2_inflow=
    Medium2.setState_phX(port_a2.p, inStream(port_a2.h_outflow), inStream(port_a2.Xi_outflow))
    "state for medium inflowing through port_a2";
  Medium2.ThermodynamicState state_b2_inflow=
    Medium2.setState_phX(port_b2.p, inStream(port_b2.h_outflow), inStream(port_b2.Xi_outflow))
    "state for medium inflowing through port_b2";
  Medium3.ThermodynamicState state_a3_inflow=
    Medium3.setState_phX(port_a3.p, inStream(port_a3.h_outflow), inStream(port_a3.Xi_outflow))
    "state for medium inflowing through port_a3";
  Medium3.ThermodynamicState state_b3_inflow=
    Medium3.setState_phX(port_b3.p, inStream(port_b3.h_outflow), inStream(port_b3.Xi_outflow))
    "state for medium inflowing through port_b3";
  Medium4.ThermodynamicState state_a4_inflow=
    Medium4.setState_phX(port_a4.p, inStream(port_a4.h_outflow), inStream(port_a4.Xi_outflow))
    "state for medium inflowing through port_a4";
  Medium4.ThermodynamicState state_b4_inflow=
    Medium4.setState_phX(port_b4.p, inStream(port_b4.h_outflow), inStream(port_b4.Xi_outflow))
    "state for medium inflowing through port_b4";
end PartialEightPortInterface;


Partial model with four ports

within Buildings.Fluid.Interfaces;
partial model PartialFourPort "Partial model with four ports"

  replaceable package Medium1 =
    Modelica.Media.Interfaces.PartialMedium "Medium 1 in the component"
  replaceable package Medium2 =
    Modelica.Media.Interfaces.PartialMedium "Medium 2 in the component"

  parameter Boolean allowFlowReversal1 = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal for medium 1"
  parameter Boolean allowFlowReversal2 = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal for medium 2"

  Modelica.Fluid.Interfaces.FluidPort_a port_a1(
                     redeclare final package Medium = Medium1,
                     m_flow(min=if allowFlowReversal1 then -Modelica.Constants.inf else 0),
                     h_outflow(start = Medium1.h_default, nominal = Medium1.h_default))
    "Fluid connector a1 (positive design flow direction is from port_a1 to port_b1)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b1(
                     redeclare final package Medium = Medium1,
                     m_flow(max=if allowFlowReversal1 then +Modelica.Constants.inf else 0),
                     h_outflow(start = Medium1.h_default, nominal = Medium1.h_default))
    "Fluid connector b1 (positive design flow direction is from port_a1 to port_b1)"

  Modelica.Fluid.Interfaces.FluidPort_a port_a2(
                     redeclare final package Medium = Medium2,
                     m_flow(min=if allowFlowReversal2 then -Modelica.Constants.inf else 0),
                     h_outflow(start = Medium2.h_default, nominal = Medium2.h_default))
    "Fluid connector a2 (positive design flow direction is from port_a2 to port_b2)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b2(
                     redeclare final package Medium = Medium2,
                     m_flow(max=if allowFlowReversal2 then +Modelica.Constants.inf else 0),
                     h_outflow(start = Medium2.h_default, nominal = Medium2.h_default))
    "Fluid connector b2 (positive design flow direction is from port_a2 to port_b2)"
end PartialFourPort;


within Buildings.Fluid.Interfaces;
partial model PartialFourPortInterface
  "Partial model with four ports and declaration of quantities that are used by many models"
  extends Buildings.Fluid.Interfaces.PartialFourPort;
  parameter Modelica.Units.SI.MassFlowRate m1_flow_nominal(min=0)
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate m2_flow_nominal(min=0)
    "Nominal mass flow rate"
  parameter Medium1.MassFlowRate m1_flow_small(min=0) = 1E-4*abs(m1_flow_nominal)
    "Small mass flow rate for regularization of zero flow"
  parameter Medium2.MassFlowRate m2_flow_small(min=0) = 1E-4*abs(m2_flow_nominal)
    "Small mass flow rate for regularization of zero flow"
  // Diagnostics
  parameter Boolean show_T = false
    "= true, if actual temperature at port is computed"

  Medium1.MassFlowRate m1_flow = port_a1.m_flow
    "Mass flow rate from port_a1 to port_b1 (m1_flow > 0 is design flow direction)";
  Modelica.Units.SI.PressureDifference dp1(displayUnit="Pa") = port_a1.p - port_b1.p
    "Pressure difference between port_a1 and port_b1";

  Medium2.MassFlowRate m2_flow = port_a2.m_flow
    "Mass flow rate from port_a2 to port_b2 (m2_flow > 0 is design flow direction)";
  Modelica.Units.SI.PressureDifference dp2(displayUnit="Pa") = port_a2.p - port_b2.p
    "Pressure difference between port_a2 and port_b2";

  Medium1.ThermodynamicState sta_a1=
    if allowFlowReversal1 then
      Medium1.setState_phX(port_a1.p,
                          noEvent(actualStream(port_a1.h_outflow)),
                          noEvent(actualStream(port_a1.Xi_outflow)))
    else
      Medium1.setState_phX(port_a1.p,
                          inStream(port_a1.h_outflow),
                          inStream(port_a1.Xi_outflow))
      if show_T "Medium properties in port_a1";
  Medium1.ThermodynamicState sta_b1=
    if allowFlowReversal1 then
      Medium1.setState_phX(port_b1.p,
                          noEvent(actualStream(port_b1.h_outflow)),
                          noEvent(actualStream(port_b1.Xi_outflow)))
    else
      Medium1.setState_phX(port_b1.p,
                          port_b1.h_outflow,
                          port_b1.Xi_outflow)
       if show_T "Medium properties in port_b1";

  Medium2.ThermodynamicState sta_a2=
    if allowFlowReversal2 then
      Medium2.setState_phX(port_a2.p,
                          noEvent(actualStream(port_a2.h_outflow)),
                          noEvent(actualStream(port_a2.Xi_outflow)))
    else
      Medium2.setState_phX(port_a2.p,
                          inStream(port_a2.h_outflow),
                          inStream(port_a2.Xi_outflow))
      if show_T "Medium properties in port_a2";
  Medium2.ThermodynamicState sta_b2=
    if allowFlowReversal2 then
      Medium2.setState_phX(port_b2.p,
                          noEvent(actualStream(port_b2.h_outflow)),
                          noEvent(actualStream(port_b2.Xi_outflow)))
    else
      Medium2.setState_phX(port_b2.p,
                          port_b2.h_outflow,
                          port_b2.Xi_outflow)
       if show_T "Medium properties in port_b2";

protected
  Medium1.ThermodynamicState state_a1_inflow=
    Medium1.setState_phX(port_a1.p, inStream(port_a1.h_outflow), inStream(port_a1.Xi_outflow))
    "state for medium inflowing through port_a1";
  Medium1.ThermodynamicState state_b1_inflow=
    Medium1.setState_phX(port_b1.p, inStream(port_b1.h_outflow), inStream(port_b1.Xi_outflow))
    "state for medium inflowing through port_b1";
  Medium2.ThermodynamicState state_a2_inflow=
    Medium2.setState_phX(port_a2.p, inStream(port_a2.h_outflow), inStream(port_a2.Xi_outflow))
    "state for medium inflowing through port_a2";
  Medium2.ThermodynamicState state_b2_inflow=
    Medium2.setState_phX(port_b2.p, inStream(port_b2.h_outflow), inStream(port_b2.Xi_outflow))
    "state for medium inflowing through port_b2";
end PartialFourPortInterface;


within Buildings.Fluid.Interfaces;
partial model PartialFourPortParallel
  "Partial model with four ports for components with parallel flow"

  replaceable package Medium1 =
    Modelica.Media.Interfaces.PartialMedium "Medium 1 in the component"
  replaceable package Medium2 =
    Modelica.Media.Interfaces.PartialMedium "Medium 2 in the component"

  parameter Boolean allowFlowReversal1 = true
    "= true to allow flow reversal in medium 1, false restricts to design direction (port_a -> port_b)"
  parameter Boolean allowFlowReversal2 = true
    "= true to allow flow reversal in medium 2, false restricts to design direction (port_a -> port_b)"

  Modelica.Fluid.Interfaces.FluidPort_a port_a1(
                     redeclare final package Medium = Medium1,
                     m_flow(min=if allowFlowReversal1 then -Modelica.Constants.inf else 0),
                     h_outflow(start = Medium1.h_default, nominal = Medium1.h_default))
    "Fluid connector a1 (positive design flow direction is from port_a1 to port_b1)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b1(
                     redeclare final package Medium = Medium1,
                     m_flow(max=if allowFlowReversal1 then +Modelica.Constants.inf else 0),
                     h_outflow(start = Medium1.h_default, nominal = Medium1.h_default))
    "Fluid connector b1 (positive design flow direction is from port_a1 to port_b1)"

  Modelica.Fluid.Interfaces.FluidPort_a port_a2(
                     redeclare final package Medium = Medium2,
                     m_flow(min=if allowFlowReversal2 then -Modelica.Constants.inf else 0),
                     h_outflow(start = Medium2.h_default, nominal = Medium2.h_default))
    "Fluid connector a2 (positive design flow direction is from port_a2 to port_b2)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b2(
                     redeclare final package Medium = Medium2,
                     m_flow(max=if allowFlowReversal2 then +Modelica.Constants.inf else 0),
                     h_outflow(start = Medium2.h_default, nominal = Medium2.h_default))
    "Fluid connector b2 (positive design flow direction is from port_a2 to port_b2)"
end PartialFourPortParallel;


Partial component with two ports

within Buildings.Fluid.Interfaces;
partial model PartialTwoPort "Partial component with two ports"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Boolean allowFlowReversal = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"

  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),
     Xi_outflow(each nominal=0.01))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare final package Medium = Medium,
    m_flow(max=if allowFlowReversal then +Modelica.Constants.inf else 0),
     h_outflow(
       start = Medium.h_default,
       nominal = Medium.h_default),
     Xi_outflow(each nominal=0.01))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"
end PartialTwoPort;


within Buildings.Fluid.Interfaces;
partial model PartialTwoPortInterface
  "Partial model with two ports and declaration of quantities that are used by many models"
  extends Buildings.Fluid.Interfaces.PartialTwoPort(
    port_a(p(start=Medium.p_default)),
    port_b(p(start=Medium.p_default)));

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate m_flow_small(min=0) = 1E-4*abs(
    m_flow_nominal) "Small mass flow rate for regularization of zero flow"
  // Diagnostics
   parameter Boolean show_T = false
    "= true, if actual temperature at port is computed"

  Modelica.Units.SI.MassFlowRate m_flow(start=_m_flow_start) = port_a.m_flow
    "Mass flow rate from port_a to port_b (m_flow > 0 is design flow direction)";

  Modelica.Units.SI.PressureDifference dp(
    start=_dp_start,
    displayUnit="Pa") = port_a.p - port_b.p
    "Pressure difference between port_a and port_b";

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

  Medium.ThermodynamicState sta_b=
    if allowFlowReversal then
      Medium.setState_phX(port_b.p,
                          noEvent(actualStream(port_b.h_outflow)),
                          noEvent(actualStream(port_b.Xi_outflow)))
    else
      Medium.setState_phX(port_b.p,
                          noEvent(port_b.h_outflow),
                          noEvent(port_b.Xi_outflow))
       if show_T "Medium properties in port_b";

protected
  final parameter Modelica.Units.SI.MassFlowRate _m_flow_start=0
    "Start value for m_flow, used to avoid a warning if not set in m_flow, and to avoid m_flow.start in parameter window";
  final parameter Modelica.Units.SI.PressureDifference _dp_start(displayUnit=
        "Pa") = 0
    "Start value for dp, used to avoid a warning if not set in dp, and to avoid dp.start in parameter window";

Thus, it can be used as a base class for a heat and mass transfer component

The partial model extends

Buildings.Fluid.Interfaces.PartialTwoPort
and adds quantities that are used by many models such as
m_flow_nominal, m_flow and dp.

The model is used by other models in this package that add heat transfer,
mass transfer and pressure drop equations. See for example

Buildings.Fluid.Interfaces.StaticTwoPortHeatMassExchanger.

", revisions="

September 22, 2023, by Michael Wetter:

Improved documentation.

This is for
IBPSA, #1796.

February 2, 2022, by Hongxiang Fu:

If allowFlowReversal==false, replaced actualStream()
with inStream() for sta_a and
removed actualStream() for sta_b.

This is for
IBPSA, #1578.

March 30, 2021, by Michael Wetter:

Added annotation HideResult=true.

This is for
IBPSA, #1459.

November 3, 2016, by Michael Wetter:

Renamed protected parameter m_flow_start to avoid
a name clash with

Buildings.Fluid.Movers.FlowControlled_m_flow
which leads to an error as the definition were different,
and also renamed protected parameter dp_start.
This is for
#552

Moved computation of pressure drop to variable assignment so that
the model won't mix graphical with textual modeling if used as a base
class for a graphically implemented model.

November 3, 2016, by Michael Wetter:

Removed start values for mass flow rate and pressure difference
to simplify the parameter window.

This is for
#552.

January 22, 2016, by Michael Wetter:

Corrected type declaration of pressure difference.
This is
for #404.

October 3, 2014, by Michael Wetter:

Changed assignment of nominal value to avoid in OpenModelica the warning
alias set with different nominal values.

November 12, 2013 by Michael Wetter:

Removed import Modelica.Constants; statement.

November 11, 2013 by Michael Wetter:

Removed the parameter homotopyInitialization
as it is no longer used in this model.

November 10, 2013 by Michael Wetter:

In the computation of sta_a and sta_b,
removed the branch that uses the homotopy operator.
The rational is that these variables are conditionally enables (because
of ... if show_T). Therefore, the Modelica Language Specification
does not allow for these variables to be used in any equation. Hence,
the use of the homotopy operator is not needed here.

October 10, 2013 by Michael Wetter:

Added noEvent to the computation of the states at the port.
This is correct, because the states are only used for reporting, but not
to compute any other variable.
Use of the states to compute other variables would violate the Modelica
language, as conditionally removed variables must not be used in any equation.

October 8, 2013 by Michael Wetter:

Removed the computation of V_flow and removed the parameter
show_V_flow.
The reason is that the computation of V_flow required
the use of sta_a (to compute the density),
but sta_a is also a variable that is conditionally
enabled. However, this was not correct Modelica syntax as conditional variables
can only be used in a connect
statement, not in an assignment. Dymola 2014 FD01 beta3 is checking
for this incorrect syntax. Hence, V_flow was removed as its
conditional implementation would require a rather cumbersome implementation
that uses a new connector that carries the state of the medium.

April 26, 2013 by Marco Bonvini:

Moved the definition of dp because it causes some problem with PyFMI.

March 27, 2012 by Michael Wetter:

Changed condition to remove sta_a to also
compute the state at the inlet port if show_V_flow=true.
The previous implementation resulted in a translation error
if show_V_flow=true, but worked correctly otherwise
because the erroneous function call is removed if  show_V_flow=false.

March 27, 2011 by Michael Wetter:

Added homotopy operator.

March 21, 2010 by Michael Wetter:

Changed pressure start value from system.p_start
to Medium.p_default since HVAC models may have water and
air, which are typically at different pressures.

September 19, 2008 by Michael Wetter:

Added equations for the mass balance of extra species flow,
i.e., C and mC_flow.

March 11, 2008, by Michael Wetter:

First implementation.

"));
end PartialTwoPortInterface;


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 Buildings.Fluid.Interfaces;
partial model PartialTwoPortTransport
  "Partial element transporting fluid between two ports without storage of mass or energy"
  extends Buildings.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 Buildings.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;


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 Buildings.Fluid.Interfaces;
model PrescribedOutlet
  "Component that assigns the outlet fluid property at port_a based on an input signal"
  extends Buildings.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 Buildings.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 = Buildings.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 = Buildings.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 =Buildings.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 =Buildings.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 =Buildings.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 =Buildings.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 =Buildings.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 =Buildings.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 Buildings.Fluid.Interfaces;
model StaticFourPortHeatMassExchanger
  "Partial model transporting two fluid streams between four ports without storing mass or energy"
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface;
  extends Buildings.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
  Buildings.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";
  Buildings.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 Buildings.Fluid.Interfaces;
model StaticTwoPortConservationEquation
  "Partial model for static energy and mass conservation equations"
  extends Buildings.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 = Buildings.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 = Buildings.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 = Buildings.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 = Buildings.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 Buildings.Fluid.Interfaces;
model StaticTwoPortHeatMassExchanger
  "Partial model transporting fluid between two ports without storing mass or energy"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface;
  extends Buildings.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
  Buildings.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"

  Buildings.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 Buildings.Fluid.Interfaces;
model TwoPortHeatMassExchanger
  "Partial model transporting one fluid stream with storing mass or energy"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    port_a(h_outflow(start=h_outflow_start)),
    port_b(h_outflow(start=h_outflow_start)));
  extends Buildings.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 Buildings.Fluid.MixingVolumes.MixingVolume vol
  constrainedby Buildings.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"

  Buildings.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 Buildings.Fluid.Interfaces.Examples;
model ConservationEquation "Model that tests the conservation equation"
extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Water "Medium model";
  Buildings.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"

  Buildings.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"
  Buildings.Fluid.Sources.Boundary_pT sin(
    nPorts=2,
    use_p_in=false,
    redeclare package Medium = Medium,
    p=101325,
    T=283.15)
  Buildings.Fluid.FixedResistances.PressureDrop res1(
    from_dp=true,
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    dp_nominal=100) "Flow resistance"
  Buildings.Fluid.Sources.MassFlowSource_T bou(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow=0.01) "Boundary condition for mass flow rate"
  Buildings.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 Buildings.Fluid.Interfaces.Examples;
model ConservationEquationSensibleLatent
  "Model that tests the conservation equation for sensible and latent heat input"
extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Air "Medium model";

  constant Modelica.Units.SI.SpecificEnergy h_fg=
      Buildings.Media.Air.enthalpyOfCondensingGas(273.15 + 37)
    "Latent heat of water vapor";

  Buildings.Fluid.Interfaces.ConservationEquation dynBal(
    simplify_mWat_flow=true,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    fluidVolume=1,
    use_mWat_flow=true,
    nPorts=2) "Dynamic conservation equation"

  Buildings.Fluid.Sources.Boundary_pT sin(
    use_p_in=false,
    redeclare package Medium = Medium,
    p=101325,
    T=283.15,
    nPorts=1)
  Buildings.Fluid.Sources.MassFlowSource_T bou(
    redeclare package Medium = Medium,
    m_flow=0.1,
    nPorts=1)    "Boundary condition for mass flow rate"

  Modelica.Blocks.Sources.Step QSen_flow(height=1000,startTime=900)
    "Sensible heat flow rate"
  Modelica.Blocks.Sources.Step QLat_flow(height=1000, startTime=1800)
    "Latent heat flow rate"
  Modelica.Blocks.Math.Add QTot_flow "Total heat flow rate"
protected
  Modelica.Blocks.Math.Gain mWat_flow(
    final k(unit="kg/J") = 1/h_fg,
    u(final unit="W"),
    y(final unit="kg/s")) "Water flow rate due to latent heat gain"
equation

  connect(mWat_flow.y, dynBal.mWat_flow)
  connect(dynBal.Q_flow, QTot_flow.y)
  connect(QTot_flow.u1, QSen_flow.y)
  connect(QTot_flow.u2, QLat_flow.y)
  connect(QLat_flow.y, mWat_flow.u)
  connect(bou.ports[1], dynBal.ports[1])
  connect(dynBal.ports[2], sin.ports[1])
end ConservationEquationSensibleLatent;


within Buildings.Fluid.Interfaces.Examples;
model EightPortHeatMassExchanger
  "EightPortHeatMassExchanger example model"
  extends Modelica.Icons.Example;
  package Medium = Buildings.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"
  Buildings.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 Buildings.Fluid.Interfaces.Examples;
model FourPortHeatMassExchanger
  "FourPortHeatMassExchanger example model"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Buildings.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 Buildings.Fluid.Interfaces.Examples;
model HeaterCooler_u
  "Model that tests a heat exchanger model with reverse flow"
  extends Modelica.Icons.Example;

 package Medium = Buildings.Media.Water;

  Buildings.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"
  Buildings.Fluid.Sources.Boundary_pT sou_1(
    redeclare package Medium = Medium,
    use_T_in=true,
    nPorts=4,
    p(displayUnit="Pa") = 101735,
    T=293.15)
  Buildings.Fluid.FixedResistances.PressureDrop res_11(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.Fluid.FixedResistances.PressureDrop res_12(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.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"
  Buildings.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)
  Buildings.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"
  Buildings.Fluid.FixedResistances.PressureDrop res_2(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.Fluid.FixedResistances.PressureDrop res_3(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.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"
  Buildings.Fluid.FixedResistances.PressureDrop res_4(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.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)
  Buildings.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"
  Buildings.Fluid.FixedResistances.PressureDrop res_1(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.Fluid.FixedResistances.PressureDrop res_5(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.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"
  Buildings.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"
  Buildings.Fluid.FixedResistances.PressureDrop res_6(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.Fluid.FixedResistances.PressureDrop res_7(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.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"
  Buildings.Fluid.FixedResistances.PressureDrop res_8(
    redeclare package Medium = Medium,
    dp_nominal=5,
    m_flow_nominal=0.5)
  Buildings.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)

  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem2a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem2b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem1a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem1b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem3a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem3b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem4a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.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)
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem6b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem6a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem5b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.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)
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem8b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem8a(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.Fluid.Sensors.TemperatureTwoPort
                                      senTem7b(redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    tau=0) "Temperature sensor"
  Buildings.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;