within AixLib.Fluid.HeatPumps.Validation;
model ScrollWaterToWater_VariableSpeed
  "Test model for scroll water to water heat pump"
  extends Modelica.Icons.Example;
  package Medium1 = AixLib.Media.Water "Medium model";
  package Medium2 = AixLib.Media.Water "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m1_flow_nominal=0.47
    "Nominal mass flow rate on the condenser side";
  parameter Modelica.Units.SI.MassFlowRate m2_flow_nominal=0.47
    "Nominal mass flow rate on the evaporator side";

  parameter Modelica.Units.SI.MassFlowRate flowSource=0.79
    "Mass flow rate on the condenser side";
  parameter Modelica.Units.SI.MassFlowRate flowLoad=0.47
    "Mass flow rate on the evaporator side";

  AixLib.Fluid.Sources.Boundary_pT sin2(
    redeclare package Medium = Medium2, nPorts=1) "Source side sink"
  AixLib.Fluid.Sources.Boundary_pT sin1(
    redeclare package Medium = Medium1, nPorts=1) "Load side sink"
  Modelica.Fluid.Sources.MassFlowSource_T loa(
    redeclare package Medium = Medium1,
    m_flow=flowLoad,
    use_m_flow_in=true,
    use_T_in=true,
    nPorts=1) "Load side flow source"
  Modelica.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium2,
    m_flow=flowSource,
    use_m_flow_in=true,
    use_T_in=true,
    nPorts=1) "Source side flow source"
  Modelica.Blocks.Sources.RealExpression mLoa(y=flowLoad)
    "Load side mass flow rate"
  Modelica.Blocks.Sources.RealExpression mSou(y=flowSource)
    "Source side mass flwo rate"
  AixLib.Fluid.HeatPumps.ScrollWaterToWater heaPum(
    redeclare package Medium1 = Medium1,
    redeclare package Medium2 = Medium2,
    m1_flow_nominal=m1_flow_nominal,
    m2_flow_nominal=m2_flow_nominal,
    dp1_nominal=1000,
    dp2_nominal=1000,
    redeclare package ref = AixLib.Media.Refrigerants.R410A,
    show_T=true,
    datHeaPum(
      etaEle=0.696,
      PLos=500,
      dTSup=10,
      UACon=4400,
      UAEva=4400,
      volRat=2,
      V_flow_nominal=0.003,
      leaCoe=0.01),
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    enable_temperature_protection=false) "Scroll water to water heat pump"
  Modelica.Blocks.Sources.Ramp N(
    duration=800,
    startTime=100,
    height=1.0,
    offset=0.0) "Heat pump control signal"
  Modelica.Blocks.Sources.Constant TLoa(k=285.15) "Load side fluid temperature"
  Modelica.Blocks.Sources.Constant TSou(k=283.15)
    "Source side fluid temperature"
equation
  connect(mSou.y, sou.m_flow_in)
  connect(mLoa.y, loa.m_flow_in)
  connect(N.y,heaPum.y)
  connect(TSou.y, sou.T_in)
  connect(TLoa.y, loa.T_in)
  connect(sin2.ports[1], heaPum.port_b2)
  connect(loa.ports[1], heaPum.port_a1)
  connect(heaPum.port_b1, sin1.ports[1])
  connect(heaPum.port_a2, sou.ports[1])
end ScrollWaterToWater_VariableSpeed;


within AixLib.Fluid.Humidifiers;
model GenericHumidifier_u
  "Steam or adiabatic humidifier with relative mass flow rate as input"
  extends AixLib.Fluid.Interfaces.TwoPortHeatMassExchanger(
    redeclare replaceable package Medium =
        Modelica.Media.Interfaces.PartialCondensingGases,
    redeclare final AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol(
    final prescribedHeatFlowRate=true));

  parameter Modelica.Units.SI.MassFlowRate mWat_flow_nominal
    "Water mass flow rate at u=1, positive for humidification";

  parameter Modelica.Units.SI.Temperature TLiqWat_in
    "Temperature of liquid water that is vaporized";

  parameter Boolean steamHumidifier=true  "True: steam humidifier, false: adiabatic (water) humidifier";

  parameter Boolean TVapFixed = true "True: fixed vaporization temperature, false: vaporization temperature from pressure"

  parameter Modelica.Units.SI.Temperature TVap=373.15
    "Vaporization temperature of steam"

  Modelica.Blocks.Interfaces.RealInput u(unit="1") "Control input"

  Modelica.Blocks.Interfaces.RealOutput mWat_flow(unit="kg/s")
    "Water added to the fluid"

  Modelica.Blocks.Interfaces.RealOutput powerEva if steamHumidifier
    "Consumed power for evaporization"
  Modelica.Blocks.Sources.RealExpression steamEnthalpyFlow(y=
        Medium.enthalpyOfCondensingGas(T=Tsteam_intern)*mWat_flow) if steamHumidifier
  Modelica.Blocks.Sources.RealExpression waterEnthalpyFlow(y=Medium.enthalpyOfLiquid(T=TLiqWat_in)*mWat_flow)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow prescribedHeatFlow
  Modelica.Blocks.Sources.RealExpression TVapPoint(y=
        AixLib.Utilities.Psychrometrics.Functions.TDewPoi_pW(vol.p)) if not TVapFixed and steamHumidifier
  Modelica.Blocks.Sources.RealExpression TVapPointFix(y=TVap) if TVapFixed and steamHumidifier
  Modelica.Blocks.Routing.RealPassThrough Tsteam if steamHumidifier
  Modelica.Blocks.Sources.RealExpression realExpression(y = mWat_flow_nominal)
  Modelica.Blocks.Math.Product product
protected
  Modelica.Blocks.Interfaces.RealInput Tsteam_intern;
  Modelica.Blocks.Nonlinear.Limiter limiter(uMax=1, uMin=0)

  Modelica.Blocks.Math.Add add(k2=-1) if steamHumidifier
equation
  if not steamHumidifier then
    Tsteam_intern = 273.15;
  end if;

  connect(Tsteam.y, Tsteam_intern);
  connect(product.y, vol.mWat_flow)
  connect(u, limiter.u)
  connect(prescribedHeatFlow.port, vol.heatPort)

  connect(steamEnthalpyFlow.y, add.u1)
  connect(waterEnthalpyFlow.y, add.u2)
  connect(add.y, powerEva)
  if steamHumidifier then
      connect(steamEnthalpyFlow.y, prescribedHeatFlow.Q_flow)
  else
    connect(waterEnthalpyFlow.y, prescribedHeatFlow.Q_flow)
  end if;
  connect(TVapPoint.y, Tsteam.u)
  connect(TVapPointFix.y, Tsteam.u)
  connect(limiter.y, product.u1)
  connect(realExpression.y, product.u2)
  connect(product.y, mWat_flow)
end GenericHumidifier_u;


within AixLib.Fluid.Humidifiers;
model Humidifier_u
  "Ideal humidifier or dehumidifier with prescribed water mass flow rate addition or subtraction"
  extends AixLib.Fluid.Interfaces.TwoPortHeatMassExchanger(
    redeclare replaceable package Medium =
        Modelica.Media.Interfaces.PartialCondensingGases,
    redeclare final AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol(
    final prescribedHeatFlowRate=true));

  parameter Modelica.Units.SI.MassFlowRate mWat_flow_nominal
    "Water mass flow rate at u=1, positive for humidification";

  Modelica.Blocks.Interfaces.RealInput u(unit="1") "Control input"

  Modelica.Blocks.Interfaces.RealOutput mWat_flow(unit="kg/s")
    "Water added to the fluid"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
    "Heat port for total heat exchange with the control volume"

protected
  Modelica.Blocks.Math.Gain gai(final k=mWat_flow_nominal) "Gain"

equation
  connect(u, gai.u)
  connect(gai.y, vol.mWat_flow)
  connect(vol.heatPort, heatPort)

  connect(gai.y, mWat_flow)
end Humidifier_u;


Package with humidifier models

within AixLib.Fluid;
package Humidifiers "Package with humidifier models"
  extends Modelica.Icons.Package;
end Humidifiers;


within AixLib.Fluid.Humidifiers;
model SprayAirWasher_X
  "Spray air washer with leaving water mass fraction as input"
  extends AixLib.Fluid.HeatExchangers.BaseClasses.PartialPrescribedOutlet(
    redeclare replaceable package Medium =
        Modelica.Media.Interfaces.PartialCondensingGases,
    outCon(
      final T_start=293.15,
      final X_start=X_start,
      final use_TSet = true,
      final use_X_wSet = true,
      final QMax_flow = Modelica.Constants.inf,
      final QMin_flow = -Modelica.Constants.inf,
      final mWatMax_flow = mWatMax_flow,
      final mWatMin_flow = 0));

  parameter Modelica.Units.SI.MassFlowRate mWatMax_flow(min=0) = Modelica.Constants.inf
    "Maximum water mass flow rate addition (positive)"

  parameter Modelica.Units.SI.MassFraction X_start[Medium.nX]=Medium.X_default
    "Start value of mass fractions m_i/m"

  // Set maximum to a high value to avoid users mistakenly entering relative humidity.
  Modelica.Blocks.Interfaces.RealInput X_w(
    unit="1",
    min=0,
    max=0.03)
    "Set point for water vapor mass fraction in kg/kg total air of the fluid that leaves port_b"

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

protected
  constant Modelica.Units.SI.SpecificEnthalpy hSte=Medium.enthalpyOfLiquid(T=
      283.15) "Enthalpy of water at 10 degree Celsius";

  Modelica.Units.SI.SpecificEnthalpy hLea=inStream(port_a.h_outflow) + {hSte}*(
      port_b.Xi_outflow - inStream(port_a.Xi_outflow))
    "Approximation of leaving enthalpy, based on dh/dx=h_fg";

  Modelica.Blocks.Sources.RealExpression TLea(y=
    Medium.temperature_phX(p = port_b.p,
                           h = hLea,
                           X = port_b.Xi_outflow)) "Leaving air temperature"
equation
  connect(X_w, outCon.X_wSet)
  connect(outCon.mWat_flow, mWat_flow)

  connect(TLea.y, outCon.TSet)
end SprayAirWasher_X;


Steam humidifier with leaving water mass fraction as input. Model for a steam humidifier with a prescribed outlet water vapor mass fraction
in kg/kg total air.

within AixLib.Fluid.Humidifiers;
model SteamHumidifier_X
  "Steam humidifier with leaving water mass fraction as input"
  extends AixLib.Fluid.HeatExchangers.BaseClasses.PartialPrescribedOutlet(
    outCon(
      final T_start=293.15,
      final X_start=X_start,
      final use_TSet = true,
      final use_X_wSet = true,
      final QMax_flow = Modelica.Constants.inf,
      final QMin_flow = -Modelica.Constants.inf,
      final mWatMax_flow = mWatMax_flow,
      final mWatMin_flow = 0));

  parameter Modelica.Units.SI.MassFlowRate mWatMax_flow(min=0) = Modelica.Constants.inf
    "Maximum water mass flow rate addition (positive)"

  parameter Modelica.Units.SI.MassFraction X_start[Medium.nX]=Medium.X_default
    "Start value of mass fractions m_i/m"

  // Set maximum to a high value to avoid users mistakenly entering relative humidity.
  Modelica.Blocks.Interfaces.RealInput X_w(
    unit="1",
    min=0,
    max=0.03)
    "Set point for water vapor mass fraction in kg/kg total air of the fluid that leaves port_b"

  Modelica.Blocks.Interfaces.RealOutput mWat_flow(unit="kg/s")
    "Water added to the fluid (if flow is from port_a to 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)"
protected
  constant Modelica.Units.SI.SpecificEnthalpy hSte=
      Medium.enthalpyOfCondensingGas(T=373.15)
    "Enthalpy of steam at 100 degree Celsius";

  Modelica.Units.SI.SpecificEnthalpy hLea=inStream(port_a.h_outflow) + {hSte}*(
      port_b.Xi_outflow - inStream(port_a.Xi_outflow))
    "Approximation of leaving enthalpy, based on dh/dx=h_fg";

  Modelica.Blocks.Sources.RealExpression TLea(y=
    Medium.temperature_phX(p = port_b.p,
                           h = hLea,
                           X = port_b.Xi_outflow)) "Leaving air temperature"
equation
  connect(X_w, outCon.X_wSet)
  connect(outCon.mWat_flow, mWat_flow)

  connect(Q_flow, outCon.Q_flow)
  connect(TLea.y, outCon.TSet)
end SteamHumidifier_X;


within AixLib.Fluid.Humidifiers.Examples;
model GenericHumidifier_u
  "Model that demonstrates the steam and adiabtic humidifier"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=3000/1000/20
    "Nominal mass flow rate";

  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_T_in=false,
    nPorts=3,
    m_flow=3*m_flow_nominal,
    T=303.15) "Source"
  AixLib.Fluid.Humidifiers.GenericHumidifier_u steamHum(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=6000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial,
    mWat_flow_nominal=m_flow_nominal*0.003,
    TLiqWat_in=293.15) "Steam humidifier"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTem(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Humidifiers.GenericHumidifier_u adiabHum(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=6000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial,
    mWat_flow_nominal=m_flow_nominal*0.003,
    TLiqWat_in=293.15,
    steamHumidifier=false) "Adiabatic humidifier"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTem2(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_T_in=false,
    p(displayUnit="Pa") = Medium.p_default + 10000,
    T=303.15,
    nPorts=3) "Sink"
  Modelica.Blocks.Sources.Ramp ramp(duration=1200, startTime=100)
  Sensors.RelativeHumidityTwoPort senRelHum(redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal)
  Sensors.RelativeHumidityTwoPort senRelHum2(redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal)
  AixLib.Fluid.Humidifiers.GenericHumidifier_u steamHum1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=6000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial,
    mWat_flow_nominal=m_flow_nominal*0.003,
    TLiqWat_in=293.15,
    TVapFixed=false) "Steam humidifier"
  Sensors.TemperatureTwoPort              senTem1(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Temperature sensor"
  Sensors.RelativeHumidityTwoPort senRelHum1(redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal)
equation
  connect(steamHum.port_b, senTem.port_a)
  connect(adiabHum.port_b, senTem2.port_a)

  connect(sou.ports[1], steamHum.port_a)
  connect(ramp.y, steamHum.u)
  connect(ramp.y, adiabHum.u)
  connect(senTem.port_b, senRelHum.port_a)
  connect(senTem2.port_b, senRelHum2.port_a)
  connect(steamHum1.port_b, senTem1.port_a)
  connect(ramp.y, steamHum1.u)
  connect(senTem1.port_b, senRelHum1.port_a)
  connect(steamHum1.port_a, sou.ports[2])
  connect(adiabHum.port_a, sou.ports[3])
  connect(senRelHum.port_b, sin.ports[1])
  connect(senRelHum1.port_b, sin.ports[2])
  connect(senRelHum2.port_b, sin.ports[3])
end GenericHumidifier_u;


within AixLib.Fluid.Humidifiers.Examples;
model Humidifier_u "Model that demonstrates the ideal humidifier model"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=3000/1000/20
    "Nominal mass flow rate";

  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_T_in=false,
    nPorts=2,
    m_flow=2*m_flow_nominal,
    T=303.15) "Source"
  AixLib.Fluid.Humidifiers.Humidifier_u humSte(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=6000,
    tau=0,
    mWat_flow_nominal=m_flow_nominal*0.005,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Steady-state model of the humidifier"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTem1(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Temperature sensor"
  Modelica.Blocks.Sources.TimeTable TSet(table=[0, 273.15 + 30; 120, 273.15 +
        30; 120, 273.15 + 25; 1200, 273.15 + 25]) "Setpoint"
  AixLib.Controls.Continuous.LimPID con1(
    Td=1,
    k=1,
    Ti=10,
    reverseActing=false) "Controller"
  AixLib.Fluid.Humidifiers.Humidifier_u humDyn(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=6000,
    mWat_flow_nominal=m_flow_nominal*0.005,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=303.15) "Dynamic model of the humidifier"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTem2(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Temperature sensor"
  AixLib.Controls.Continuous.LimPID con2(
    Td=1,
    Ti=10,
    k=0.1,
    reverseActing=false) "Controller"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_T_in=false,
    p(displayUnit="Pa"),
    T=303.15,
    nPorts=2) "Sink"
equation
  connect(senTem1.T, con1.u_m)
  connect(TSet.y, con1.u_s)
  connect(con1.y, humSte.u)
  connect(humSte.port_b, senTem1.port_a)
  connect(senTem2.T, con2.u_m)
  connect(TSet.y, con2.u_s)
  connect(con2.y, humDyn.u)
  connect(humDyn.port_b, senTem2.port_a)

  connect(sou.ports[1], humSte.port_a)
  connect(sou.ports[2], humDyn.port_a)
  connect(senTem1.port_b, sin.ports[1])
  connect(senTem2.port_b, sin.ports[2])
end Humidifier_u;


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


Collection of validation models

within AixLib.Fluid.Humidifiers;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


within AixLib.Fluid.Humidifiers.Validation;
model SprayAirWasher_X
  "Model that demonstrates the spray air washer model, configured as steady-state"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;

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

  parameter Modelica.Units.SI.MassFlowRate mWat_flow_nominal=m_flow_nominal*
      0.004 "Maximum humidification water mass flow rate";

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_T_in=false,
    p(displayUnit="Pa"),
    T=293.15,
    nPorts=1) "Sink"

  replaceable AixLib.Fluid.Humidifiers.SprayAirWasher_X hum(
    mWatMax_flow=mWat_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
      constrainedby AixLib.Fluid.Humidifiers.SprayAirWasher_X(
        redeclare package Medium = Medium,
        m_flow_nominal=m_flow_nominal,
        dp_nominal=6000,
        show_T=true) "Humidifier with capacity limitation"

  Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=m_flow_nominal,
    T=293.15) "Flow source"

  Modelica.Blocks.Sources.CombiTimeTable XSet(
    tableOnFile=false,
    table=[
      0.0,   0.01;
      1*180, 0.012;
      2*180, 0.015;
      3*180, 0.01;
      4*180, 0.008;
      5*180, 0.004;
      6*180, 0.01],
    smoothness=Modelica.Blocks.Types.Smoothness.ConstantSegments,
    extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic)
    "Set point for humidity"

  AixLib.Fluid.Sensors.MassFractionTwoPort senHum(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    tau=0) "Mass fraction sensor"

equation
  connect(sou1.ports[1], hum.port_a)
  connect(XSet.y[1], hum.X_w)
  connect(hum.port_b, senHum.port_a)
  connect(senHum.port_b, sin.ports[1])
end SprayAirWasher_X;


Model that demonstrates the spray air washer model, configured as dynamic model. Model that validates the use of a spray air washer
configured as a dynamic model with limits on the maximum water mass flow rate
that is added to the air stream.

within AixLib.Fluid.Humidifiers.Validation;
model SprayAirWasher_X_dynamic
  "Model that demonstrates the spray air washer model, configured as dynamic model"
  extends AixLib.Fluid.Humidifiers.Validation.SprayAirWasher_X(
    hum(
     mWatMax_flow=mWat_flow_nominal,
     energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));
end SprayAirWasher_X_dynamic;


Model that demonstrates the steam humidifier model, configured as steady-state model. Model that validates the use of a spray air washer
configured as a steady-state model with limits on the maximum water mass flow rate
that is added to the air stream.

within AixLib.Fluid.Humidifiers.Validation;
model SteamHumidifier_X
  "Model that demonstrates the steam humidifier model, configured as steady-state model"
  extends AixLib.Fluid.Humidifiers.Validation.SprayAirWasher_X(
    redeclare AixLib.Fluid.Humidifiers.SteamHumidifier_X hum(
      mWatMax_flow=mWat_flow_nominal));
end SteamHumidifier_X;


Model that demonstrates the steam humidifier model, configured as dynamic model. Model that validates the use of a spray air washer
configured as a dynamic model with limits on the maximum water mass flow rate
that is added to the air stream.

within AixLib.Fluid.Humidifiers.Validation;
model SteamHumidifier_X_dynamic
  "Model that demonstrates the steam humidifier model, configured as dynamic model"
  extends AixLib.Fluid.Humidifiers.Validation.SprayAirWasher_X(
    redeclare AixLib.Fluid.Humidifiers.SteamHumidifier_X hum(
      mWatMax_flow=mWat_flow_nominal,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));
end SteamHumidifier_X_dynamic;


within AixLib.Fluid.Interfaces;
model ConservationEquation "Lumped volume with mass and energy balance"

  extends AixLib.Fluid.Interfaces.LumpedVolumeDeclarations;

  // Constants
  parameter Boolean initialize_p = not Medium.singleState
    "= true to set up initial equations for pressure"

  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. Used only if Medium.nX > 1";

  // Port definitions
  parameter Integer nPorts=0 "Number of ports"

  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 use this model
  Modelica.Blocks.Interfaces.RealOutput hOut(unit="J/kg",
                                             start=hStart,
                                             nominal=Medium.h_default)
    "Leaving specific enthalpy 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"
  Modelica.Blocks.Interfaces.RealOutput UOut(unit="J")
    "Internal energy of the component"
  Modelica.Blocks.Interfaces.RealOutput mXiOut[Medium.nXi](each min=0, each unit="kg")
    "Species mass of the component"
  Modelica.Blocks.Interfaces.RealOutput mOut(min=0, unit="kg")
    "Mass of the component"
  Modelica.Blocks.Interfaces.RealOutput mCOut[Medium.nC](each min=0, each unit="kg")
    "Trace substance mass of the component"

  Modelica.Fluid.Vessels.BaseClasses.VesselFluidPorts_b ports[nPorts](
      redeclare each final package Medium = Medium,
      each h_outflow(nominal=Medium.h_default),
      each Xi_outflow(each nominal=0.01)) "Fluid inlets and outlets"

  // Set nominal attributes where literal values can be used.
  Medium.BaseProperties medium(
    p(start=p_start),
    h(start=hStart),
    T(start=T_start),
    Xi(
      each stateSelect=if medium.preferredMediumStates then StateSelect.prefer else StateSelect.default,
      start=X_start[1:Medium.nXi]),
    X(start=X_start),
    d(start=rho_start)) "Medium properties";

  Modelica.Units.SI.Energy U(
    start=fluidVolume*rho_start*
        Medium.specificInternalEnergy(Medium.setState_pTX(
        T=T_start,
        p=p_start,
        X=X_start[1:Medium.nXi])) + (T_start - Medium.reference_T)*CSen,
      nominal=1E5) "Internal energy of fluid";

  Modelica.Units.SI.Mass m(start=fluidVolume*rho_start, stateSelect=if
        massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
        StateSelect.default else StateSelect.prefer) "Mass of fluid";

  Modelica.Units.SI.Mass[Medium.nXi] mXi(
    each stateSelect=StateSelect.never,
    start=fluidVolume*rho_start*X_start[1:Medium.nXi])
    "Masses of independent components in the fluid";
  Modelica.Units.SI.Mass[Medium.nC] mC(start=fluidVolume*rho_start*C_start)
    "Masses of trace substances in the fluid";
  // C need to be added here because unlike for Xi, which has medium.Xi,
  // there is no variable medium.C
  Medium.ExtraProperty C[Medium.nC](nominal=C_nominal)
    "Trace substance mixture content";

  Modelica.Units.SI.MassFlowRate mb_flow "Mass flows across boundaries";
  Modelica.Units.SI.MassFlowRate[Medium.nXi] mbXi_flow
    "Substance mass flows across boundaries";
  Medium.ExtraPropertyFlowRate[Medium.nC] mbC_flow
    "Trace substance mass flows across boundaries";
  Modelica.Units.SI.EnthalpyFlowRate Hb_flow
    "Enthalpy flow across boundaries or energy source/sink";

  // Parameters that need to be defined by an extending class
  parameter Modelica.Units.SI.Volume fluidVolume "Volume";
  final parameter Modelica.Units.SI.HeatCapacity CSen=(mSenFac - 1)*rho_default
      *cp_default*fluidVolume
    "Aditional heat capacity for implementing mFactor";
protected
  Medium.EnthalpyFlowRate ports_H_flow[nPorts];
  Modelica.Units.SI.MassFlowRate ports_mXi_flow[nPorts,Medium.nXi];
  Medium.ExtraPropertyFlowRate ports_mC_flow[nPorts,Medium.nC];
  parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(state=state_default)
    "Heat capacity, to compute additional dry mass";
  parameter Modelica.Units.SI.Density rho_start=Medium.density(
      Medium.setState_pTX(
      T=T_start,
      p=p_start,
      X=X_start[1:Medium.nXi])) "Density, used to compute fluid mass";

  // Parameter for avoiding extra overhead calculations when CSen==0
  final parameter Boolean computeCSen = abs(mSenFac-1) > Modelica.Constants.eps
  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.Density rho_default=Medium.density(state=
      state_default) "Density, used to compute fluid mass";
  // Parameter that is used to construct the vector mXi_flow
  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";
  parameter Modelica.Units.SI.SpecificEnthalpy hStart=
      Medium.specificEnthalpy_pTX(
      p_start,
      T_start,
      X_start) "Start value for specific enthalpy";

  // Set _simplify_mWat_flow == false for Glycol47; otherwise Dymola 2018FD01
  // cannot differentiate the equations.
  constant Boolean _simplify_mWat_flow = simplify_mWat_flow and Medium.nX > 1
   "If true, then port_a.m_flow + port_b.m_flow = 0 even if mWat_flow is non-zero, and equations are simplified";

  // 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, "In " + getInstanceName()
       + ":
         If Medium.nXi > 1, then substance 'water' must be present for one component of '"
       + Medium.mediumName + "'.
         Check medium model.");
  end if;

  // Make sure that if energyDynamics is SteadyState, then
  // massDynamics is also SteadyState.
  // Otherwise, the system of ordinary differential equations may be inconsistent.
  if energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
    assert(massDynamics == energyDynamics, "In " + getInstanceName() + ":
         If 'massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState', then it is
         required that 'energyDynamics==Modelica.Fluid.Types.Dynamics.SteadyState'.
         Otherwise, the system of equations may not be consistent.
         You need to select other parameter values.");
  end if;

  // initialization of balances
  if energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then
      medium.T = T_start;
  else
    if energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then
        der(medium.T) = 0;
    end if;
  end if;

  if massDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then
    if initialize_p then
      medium.p = p_start;
    end if;
  else
    if massDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then
      if initialize_p then
        der(medium.p) = 0;
      end if;
    end if;
  end if;

  if substanceDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then
    medium.Xi = X_start[1:Medium.nXi];
  else
    if substanceDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then
      der(medium.Xi) = zeros(Medium.nXi);
    end if;
  end if;

  if traceDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then
    C = C_start[1:Medium.nC];
  else
    if traceDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then
      der(C) = zeros(Medium.nC);
    end if;
  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;

  // Total quantities
  if massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
    m = fluidVolume*rho_start;
  else
    if _simplify_mWat_flow then
      // If moisture is neglected in mass balance, assume for computation
      // of the mass of air that the air is at Medium.X_default.
      m = fluidVolume*Medium.density(Medium.setState_phX(
        p = medium.p,
        h = hOut,
        X = Medium.X_default));
    else
      // Use actual density
      m = fluidVolume*medium.d;
    end if;
  end if;
  mXi = m*medium.Xi;
  if computeCSen then
    U = m*medium.u + CSen*(medium.T-Medium.reference_T);
  else
    U = m*medium.u;
  end if;
  mC = m*C;

  hOut = medium.h;
  XiOut = medium.Xi;
  COut = C;

  for i in 1:nPorts loop
    //The semiLinear function should be used for the equations below
    //for allowing min/max simplifications.
    //See https://github.com/ibpsa/modelica-ibpsa/issues/216 for a discussion and motivation
    ports_H_flow[i]     = semiLinear(ports[i].m_flow, inStream(ports[i].h_outflow), ports[i].h_outflow)
      "Enthalpy flow";
    for j in 1:Medium.nXi loop
      ports_mXi_flow[i,j] = semiLinear(ports[i].m_flow, inStream(ports[i].Xi_outflow[j]), ports[i].Xi_outflow[j])
        "Component mass flow";
    end for;
    for j in 1:Medium.nC loop
      ports_mC_flow[i,j]  = semiLinear(ports[i].m_flow, inStream(ports[i].C_outflow[j]),  ports[i].C_outflow[j])
        "Trace substance mass flow";
    end for;
  end for;

  for i in 1:Medium.nXi loop
    mbXi_flow[i] = sum(ports_mXi_flow[:,i]);
  end for;

  for i in 1:Medium.nC loop
    mbC_flow[i]  = sum(ports_mC_flow[:,i]);
  end for;

  mb_flow = sum(ports.m_flow);
  Hb_flow = sum(ports_H_flow);

  // Energy and mass balances
  if energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
    0 = Hb_flow + Q_flow;
  else
    der(U) = Hb_flow + Q_flow;
  end if;

  if massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
    0 = mb_flow + (if simplify_mWat_flow then 0 else mWat_flow_internal);
  else
    der(m) = mb_flow + (if simplify_mWat_flow then 0 else mWat_flow_internal);
  end if;

  if substanceDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
    zeros(Medium.nXi) = mbXi_flow + mWat_flow_internal * s;
  else
    der(medium.Xi) = (mbXi_flow + mWat_flow_internal * s)/m;
  end if;

  if traceDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
    zeros(Medium.nC)  = mbC_flow + C_flow_internal;
  else
    der(mC)  = mbC_flow + C_flow_internal;
  end if;

  // Properties of outgoing flows
  for i in 1:nPorts loop
      ports[i].p          = medium.p;
      ports[i].h_outflow  = medium.h;
      ports[i].Xi_outflow = medium.Xi;
      ports[i].C_outflow  = C;
  end for;
  UOut=U;
  mXiOut=mXi;
  mOut=m;
  mCOut=mC;
end ConservationEquation;


Partial model with eight ports

within AixLib.Fluid.Interfaces;
partial model EightPort "Partial model with eight 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"
  replaceable package Medium3 =
    Modelica.Media.Interfaces.PartialMedium "Medium 3 in the component"
  replaceable package Medium4 =
    Modelica.Media.Interfaces.PartialMedium "Medium 4 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)"
  parameter Boolean allowFlowReversal3 = true
    "= true to allow flow reversal in medium 3, false restricts to design direction (port_a -> port_b)"
  parameter Boolean allowFlowReversal4 = true
    "= true to allow flow reversal in medium 4, false restricts to design direction (port_a -> port_b)"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_a1_start=Medium1.h_default
    "Start value for enthalpy flowing out of port a1"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_b1_start=Medium1.h_default
    "Start value for enthalpy flowing out of port b1"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_a2_start=Medium2.h_default
    "Start value for enthalpy flowing out of port a2"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_b2_start=Medium2.h_default
    "Start value for enthalpy flowing out of port b2"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_a3_start=Medium3.h_default
    "Start value for enthalpy flowing out of port a1"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_b3_start=Medium3.h_default
    "Start value for enthalpy flowing out of port b1"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_a4_start=Medium4.h_default
    "Start value for enthalpy flowing out of port a1"

  parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_b4_start=Medium4.h_default
    "Start value for enthalpy flowing out of port b1"

  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=h_outflow_a1_start))
    "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=h_outflow_b1_start))
    "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=h_outflow_a2_start))
    "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=h_outflow_b2_start))
    "Fluid connector b2 (positive design flow direction is from port_a2 to port_b2)"

  Modelica.Fluid.Interfaces.FluidPort_a port_a3(
                     redeclare final package Medium = Medium3,
                     m_flow(min=if allowFlowReversal3 then -Modelica.Constants.inf else 0),
                     h_outflow(start=h_outflow_a3_start))
    "Fluid connector a1 (positive design flow direction is from port_a3 to port_b3)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b3(
                     redeclare final package Medium = Medium3,
                     m_flow(max=if allowFlowReversal3 then +Modelica.Constants.inf else 0),
                     h_outflow(start=h_outflow_b3_start))
    "Fluid connector b2 (positive design flow direction is from port_a3 to port_b3)"
  Modelica.Fluid.Interfaces.FluidPort_a port_a4(
                     redeclare final package Medium = Medium4,
                     m_flow(min=if allowFlowReversal4 then -Modelica.Constants.inf else 0),
                     h_outflow(start=h_outflow_a4_start))
    "Fluid connector a1 (positive design flow direction is from port_a4 to port_b4)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b4(
                     redeclare final package Medium = Medium4,
                     m_flow(max=if allowFlowReversal4 then +Modelica.Constants.inf else 0),
                     h_outflow(start=h_outflow_b4_start))
    "Fluid connector b2 (positive design flow direction is from port_a4 to port_b4)"
end EightPort;


within AixLib.Fluid.Interfaces;
model EightPortHeatMassExchanger
  "Model transporting four fluid streams between eight ports with storing mass or energy"
  extends AixLib.Fluid.Interfaces.PartialEightPortInterface(
    final h_outflow_a1_start = h1_outflow_start,
    final h_outflow_b1_start = h1_outflow_start,
    final h_outflow_a2_start = h2_outflow_start,
    final h_outflow_b2_start = h2_outflow_start,
    final h_outflow_a3_start = h3_outflow_start,
    final h_outflow_b3_start = h3_outflow_start,
    final h_outflow_a4_start = h4_outflow_start,
    final h_outflow_b4_start = h4_outflow_start);

  extends AixLib.Fluid.Interfaces.EightPortFlowResistanceParameters(
     final computeFlowResistance1=true, final computeFlowResistance2=true,final computeFlowResistance3=true, final computeFlowResistance4=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"
  parameter Modelica.Units.SI.Time tau3=30 "Time constant at nominal flow"
  parameter Modelica.Units.SI.Time tau4=30 "Time constant at nominal flow"

  // Assumptions
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Formulation of energy balance"

  // 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](
       quantity=Medium1.extraPropertiesNames)=fill(0, Medium1.nC)
    "Start value of trace substances"
  parameter Medium1.ExtraProperty C1_nominal[Medium1.nC](
       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](
       quantity=Medium2.extraPropertiesNames)=fill(0, Medium2.nC)
    "Start value of trace substances"
  parameter Medium2.ExtraProperty C2_nominal[Medium2.nC](
       quantity=Medium2.extraPropertiesNames) = fill(1E-2, Medium2.nC)
    "Nominal value of trace substances. (Set to typical order of magnitude.)"

  parameter Medium3.AbsolutePressure p3_start = Medium3.p_default
    "Start value of pressure"
  parameter Medium3.Temperature T3_start = Medium3.T_default
    "Start value of temperature"
  parameter Medium3.MassFraction X3_start[Medium3.nX] = Medium3.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium3.ExtraProperty C3_start[Medium3.nC](
       quantity=Medium3.extraPropertiesNames)=fill(0, Medium3.nC)
    "Start value of trace substances"
  parameter Medium3.ExtraProperty C3_nominal[Medium3.nC](
       quantity=Medium3.extraPropertiesNames) = fill(1E-2, Medium3.nC)
    "Nominal value of trace substances. (Set to typical order of magnitude.)"

  parameter Medium4.AbsolutePressure p4_start = Medium4.p_default
    "Start value of pressure"
  parameter Medium4.Temperature T4_start = Medium4.T_default
    "Start value of temperature"
  parameter Medium4.MassFraction X4_start[Medium4.nX] = Medium4.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium4.ExtraProperty C4_start[Medium4.nC](
       quantity=Medium4.extraPropertiesNames)=fill(0, Medium4.nC)
    "Start value of trace substances"
  parameter Medium4.ExtraProperty C4_nominal[Medium4.nC](
       quantity=Medium4.extraPropertiesNames) = fill(1E-2, Medium4.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";
  Modelica.Units.SI.HeatFlowRate Q3_flow=vol3.heatPort.Q_flow
    "Heat flow rate into medium 1";
  Modelica.Units.SI.HeatFlowRate Q4_flow=vol4.heatPort.Q_flow
    "Heat flow rate into medium 2";

  AixLib.Fluid.MixingVolumes.MixingVolume vol1(
    redeclare final package Medium = Medium1,
    nPorts = 2,
    V=m1_flow_nominal*tau1/rho1_nominal,
    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) "Volume for fluid 1"

  AixLib.Fluid.MixingVolumes.MixingVolume vol2(
    redeclare final package Medium = Medium2,
    nPorts=2,
    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,
    final m_flow_nominal=m2_flow_nominal,
    V=m2_flow_nominal*tau2/rho2_nominal) "Volume for fluid 2"

  AixLib.Fluid.MixingVolumes.MixingVolume vol3(
    redeclare final package Medium = Medium3,
    energyDynamics=if tau3 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
    massDynamics=if tau3 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
    final p_start=p3_start,
    final T_start=T3_start,
    final X_start=X3_start,
    final C_start=C3_start,
    final C_nominal=C3_nominal,
    final m_flow_nominal=m3_flow_nominal,
    V=m3_flow_nominal*tau3/rho3_nominal,
    nPorts=2) "Volume for fluid 3"
  AixLib.Fluid.MixingVolumes.MixingVolume vol4(
    redeclare final package Medium = Medium4,
    nPorts=2,
    energyDynamics=if tau4 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
    massDynamics=if tau4 > Modelica.Constants.eps
                         then energyDynamics else
                         Modelica.Fluid.Types.Dynamics.SteadyState,
    final p_start=p4_start,
    final T_start=T4_start,
    final X_start=X4_start,
    final C_start=C4_start,
    final C_nominal=C4_nominal,
    final m_flow_nominal=m4_flow_nominal,
    V=m4_flow_nominal*tau4/rho4_nominal) "Volume for fluid 4"

  AixLib.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) "Pressure drop model for fluid 1"

  AixLib.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) "Pressure drop model for fluid 2"
  AixLib.Fluid.FixedResistances.PressureDrop preDro3(
    redeclare final package Medium = Medium3,
    final m_flow_nominal=m3_flow_nominal,
    final deltaM=deltaM3,
    final allowFlowReversal=allowFlowReversal3,
    final show_T=false,
    final from_dp=from_dp3,
    final linearized=linearizeFlowResistance3,
    final homotopyInitialization=homotopyInitialization,
    final dp_nominal=dp3_nominal) "Pressure drop model for fluid 3"

  AixLib.Fluid.FixedResistances.PressureDrop preDro4(
    redeclare final package Medium = Medium4,
    final m_flow_nominal=m4_flow_nominal,
    final deltaM=deltaM4,
    final allowFlowReversal=allowFlowReversal4,
    final show_T=false,
    final from_dp=from_dp4,
    final linearized=linearizeFlowResistance4,
    final homotopyInitialization=homotopyInitialization,
    final dp_nominal=dp4_nominal) "Pressure drop model for fluid 4"
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 sta3_nominal=Medium3.setState_pTX(
      T=Medium3.T_default, p=Medium3.p_default, X=Medium3.X_default);
  parameter Modelica.Units.SI.Density rho3_nominal=Medium3.density(sta3_nominal)
    "Density, used to compute fluid volume";
  parameter Medium4.ThermodynamicState sta4_nominal=Medium4.setState_pTX(
      T=Medium4.T_default, p=Medium4.p_default, X=Medium4.X_default);
  parameter Modelica.Units.SI.Density rho4_nominal=Medium4.density(sta4_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";
  parameter Medium3.ThermodynamicState sta3_start=Medium3.setState_pTX(
      T=T3_start, p=p3_start, X=X3_start);
  parameter Modelica.Units.SI.SpecificEnthalpy h3_outflow_start=
      Medium3.specificEnthalpy(sta3_start)
    "Start value for outflowing enthalpy";
  parameter Medium4.ThermodynamicState sta4_start=Medium4.setState_pTX(
      T=T4_start, p=p4_start, X=X4_start);
  parameter Modelica.Units.SI.SpecificEnthalpy h4_outflow_start=
      Medium4.specificEnthalpy(sta4_start)
    "Start value for outflowing enthalpy";

initial algorithm
  // 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");

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

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


within AixLib.Fluid.Interfaces;
model FourPortHeatExchanger
  "Model transporting two fluid streams between four ports with storing mass or energy in n volumes"
  extends AixLib.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 AixLib.Fluid.Interfaces.FourPortFlowResistanceParameters(
     final computeFlowResistance1=true, final computeFlowResistance2=true);

  parameter Integer nNodes(min=1) = 2 "Spatial segmentation";

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

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

  // 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"

  // 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=sum(vol1.heatPort.Q_flow)
    "Heat flow rate into medium 1";
  Modelica.Units.SI.HeatFlowRate Q2_flow=sum(vol2.heatPort.Q_flow)
    "Heat flow rate into medium 2";

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

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

  AixLib.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"

  AixLib.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");
  assert((massDynamics == 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 massDynamics == 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((massDynamics == 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 massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState to model steady-state.
 Received tau2 = " + String(tau2) + "\n");

equation

  connect(port_a1, preDro1.port_a)
  connect(preDro1.port_b, vol1[1].ports[1])
  connect(vol1[nNodes].ports[2], port_b1)
  connect(port_a2, preDro2.port_a)
  connect(preDro2.port_b, vol2[1].ports[1])
  connect(vol2[nNodes].ports[2], port_b2)

  for i in 1:nNodes-1 loop
    connect(vol1[i].ports[2],vol1[i+1].ports[1]);
    connect(vol2[i].ports[2],vol2[i+1].ports[1]);
  end for;
end FourPortHeatExchanger;


within AixLib.Fluid.Interfaces;
model FourPortHeatMassExchanger
  "Model transporting two fluid streams between four ports with storing mass or energy"
  extends AixLib.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 AixLib.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 AixLib.Fluid.MixingVolumes.BaseClasses.MixingVolumeHeatPort vol1
    constrainedby
    AixLib.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 AixLib.Fluid.MixingVolumes.MixingVolume vol2
    constrainedby
    AixLib.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"

  AixLib.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"

  AixLib.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 AixLib.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 AixLib.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 AixLib.Fluid.Interfaces;
partial model PartialEightPortInterface
  "Partial model with eight ports and declaration of quantities that are used by many models"
  extends AixLib.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 AixLib.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 AixLib.Fluid.Interfaces;
partial model PartialFourPortInterface
  "Partial model with four ports and declaration of quantities that are used by many models"
  extends AixLib.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 AixLib.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;


within AixLib.Fluid.Interfaces;
partial model PartialModularPort_a
  "Base model for all modular models with multiple inlet ports and one outlet port"

  // Definition of the medium model
  //
  replaceable package Medium =
    Modelica.Media.R134a.R134a_ph
    constrainedby Modelica.Media.Interfaces.PartialTwoPhaseMedium
    "Medium in the component"

  // Definition of parameters describing modular approach
  //
  parameter Integer nPorts = 1
    "Number of outlet ports"

  // Definition of parameters describing assumptions
  //
  parameter Boolean allowFlowReversal = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"

  // Definition of parameters describing initialisation and numeric limits
  //
  parameter Modelica.Units.SI.PressureDifference dp_start=1e5
    "Guess value of pressure difference for subcomponents"
  parameter Medium.MassFlowRate m_flow_start = 0.5*m_flow_nominal
    "Guess value of m_flow = sum(ports_a.m_flow)"
  parameter Modelica.Units.SI.PressureDifference dp_nominal=7.5e5
    "Pressure drop at nominal conditions"
  parameter Medium.MassFlowRate m_flow_nominal = 0.1
    "Nominal mass flow rate"
  parameter Medium.MassFlowRate m_flow_small = 1e-6*m_flow_nominal
    "Small mass flow rate for regularization of zero flow"

  // Definition of connectors
  //
  Modelica.Fluid.Interfaces.FluidPorts_a ports_a[nPorts](
    redeclare final package Medium = Medium,
     m_flow(each min=if allowFlowReversal then -Modelica.Constants.inf else 0),
     h_outflow(each start = Medium.h_default))
    "Fluid connectors a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare each final package Medium = Medium,
    m_flow(max=if allowFlowReversal then +Modelica.Constants.inf else 0),
    h_outflow(start = Medium.h_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"
end PartialModularPort_a;


within AixLib.Fluid.Interfaces;
partial model PartialModularPort_ab
  "Base model for all modular models with multiple inlet and outlet ports"

  // Definition of the medium model
  //
  replaceable package Medium =
    Modelica.Media.R134a.R134a_ph
    constrainedby Modelica.Media.Interfaces.PartialTwoPhaseMedium
    "Medium in the component"

  // Definition of parameters describing modular approach
  //
  parameter Integer nPorts = 1
    "Number of inlet and outlet ports"

  // Definition of parameters describing assumptions
  //
  parameter Boolean allowFlowReversal = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"

  // Definition of parameters describing initialisation and numeric limits
  //
  parameter Medium.MassFlowRate m_flow_nominal = 0.1
    "Nominal mass flow rate"
  parameter Medium.MassFlowRate m_flow_small = 1e-6*m_flow_nominal
    "Small mass flow rate for regularization of zero flow"

  // Definition of connectors
  //
  Modelica.Fluid.Interfaces.FluidPorts_a ports_a[nPorts](
    redeclare final package Medium = Medium,
     m_flow(each min=if allowFlowReversal then -Modelica.Constants.inf else 0),
     h_outflow(each start = Medium.h_default))
    "Fluid connectors a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPorts_b ports_b[nPorts](
    redeclare each final package Medium = Medium,
    m_flow(each max=if allowFlowReversal then +Modelica.Constants.inf else 0),
    h_outflow(each start = Medium.h_default))
    "Fluid connectors b (positive design flow direction is from port_a to port_b)"
end PartialModularPort_ab;


within AixLib.Fluid.Interfaces;
partial model PartialModularPort_b
  "Base model for all modular models with one inlet port and multiple outlet ports"

  // Definition of the medium model
  //
  replaceable package Medium =
    Modelica.Media.R134a.R134a_ph
    constrainedby Modelica.Media.Interfaces.PartialTwoPhaseMedium
    "Medium in the component"

  // Definition of parameters describing modular approach
  //
  parameter Integer nPorts = 1
    "Number of outlet ports"

  // Definition of parameters describing assumptions
  //
  parameter Boolean allowFlowReversal = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"

  // Definition of parameters describing initialisation and numeric limits
  //
  parameter Modelica.Units.SI.PressureDifference dp_start=1e5
    "Guess value of pressure difference for subcomponents"
  parameter Medium.MassFlowRate m_flow_start = 0.5*m_flow_nominal
    "Guess value of m_flow = port_a.m_flow"
  parameter Modelica.Units.SI.PressureDifference dp_nominal=7.5e5
    "Pressure drop at nominal conditions"
  parameter Medium.MassFlowRate m_flow_nominal = 0.1
    "Nominal mass flow rate"
  parameter Medium.MassFlowRate m_flow_small = 1e-6*m_flow_nominal
    "Small mass flow rate for regularization of zero flow"

  // Definition of connectors
  //
  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))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPorts_b ports_b[nPorts](
    redeclare each final package Medium = Medium,
    m_flow(each max=if allowFlowReversal then +Modelica.Constants.inf else 0),
    h_outflow(each start = Medium.h_default))
    "Fluid connectors b (positive design flow direction is from port_a to port_b)"
end PartialModularPort_b;


Partial component with two ports

within AixLib.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 AixLib.Fluid.Interfaces;
partial model PartialTwoPortInterface
  "Partial model with two ports and declaration of quantities that are used by many models"
  extends AixLib.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

AixLib.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

AixLib.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

AixLib.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.

"),
   __Dymola_LockedEditing="Model from IBPSA");
end PartialTwoPortInterface;