within AixLib.Fluid.MixingVolumes.BaseClasses.Validation;
model MixingVolumeHeatPortAir
  "Validation model for setting the initialization of the pressure for air"
  extends AixLib.Fluid.MixingVolumes.BaseClasses.Validation.MixingVolumeHeatPortWater(
    redeclare package Medium = AixLib.Media.Air);
end MixingVolumeHeatPortAir;


within AixLib.Fluid.MixingVolumes.BaseClasses.Validation;
model MixingVolumeHeatPortWater
  "Validation model for setting the initialization of the pressure for water"
  extends Modelica.Icons.Example;

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

  parameter Integer nEle(min=2)= 3 "Number of volumes"

  replaceable AixLib.Fluid.MixingVolumes.BaseClasses.MixingVolumeHeatPort vol[nEle]
    constrainedby AixLib.Fluid.MixingVolumes.BaseClasses.MixingVolumeHeatPort(
    redeclare each package Medium = Medium,
    each energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    final initialize_p={(i == 1 and not Medium.singleState) for i in 1:nEle},
    each m_flow_nominal=1,
    each V=1,
    each nPorts=2) "Mixing volume"

  AixLib.Fluid.FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    dp_nominal=1000) "Pressure drop"

  AixLib.Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1)
    "Boundary condition"

  AixLib.Fluid.FixedResistances.PressureDrop res1(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    dp_nominal=1000) "Pressure drop"

  AixLib.Fluid.Sources.Boundary_pT bou1(
    redeclare package Medium = Medium,
    nPorts=1)
    "Boundary condition"
equation
  connect(bou.ports[1], res.port_a)
  connect(res.port_b, vol[1].ports[1])
  for i in 1:nEle-1 loop
    connect(vol[i].ports[2], vol[i+1].ports[1]);
  end for;
  connect(res1.port_b, vol[nEle].ports[2])
  connect(bou1.ports[1], res1.port_a)
end MixingVolumeHeatPortWater;


Collection of validation models

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


Medium model for water

within AixLib.Fluid.MixingVolumes.Examples;
model HydraulicSeparator
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Water "Medium model for water";

  AixLib.Fluid.MixingVolumes.HydraulicSeparator hydraulicSeparator(DFlange=
        0.01,
    redeclare package Medium = Medium,
    pumpMaxVolumeFlow=0.03,
    m_flow_nominal=0.1)
  AixLib.Obsolete.Year2021.Fluid.Movers.Pump pump(
    V_flow(start=0.002),
    ControlStrategy=2,
    V_flow_max=12,
    Head_max=10,
    redeclare package Medium = Medium,
    m_flow_small=1e-4)
  Sources.Boundary_pT              boundary_p(
    nPorts=1,
    redeclare package Medium = Medium,
    p=150000)
  MixingVolume heatSource(
    redeclare package Medium = Medium,
    m_flow_small=1e-4,
    nPorts=2,
    m_flow_nominal=0.1,
    V=0.01) "Mixing volume for heat input"
  MixingVolume heatSink(
    redeclare package Medium = Medium,
    m_flow_small=1e-4,
    m_flow_nominal=0.1,
    nPorts=2,
    V=0.01)   "Mixing volume for heat sink"
  FixedResistances.PressureDrop       res1(
    redeclare package Medium = Medium,
    m_flow_nominal=0.1,
    dp_nominal=200) "Hydraulic resistance in primary circuit"
  AixLib.Fluid.Sensors.MassFlowRate  massFlowSensorPrim(redeclare package Medium =
               Medium)
  Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow fixedHeatFlow(
    Q_flow=1.6e3,
    T_ref=343.15,
    alpha=-0.5)
  AixLib.Obsolete.Year2021.Fluid.Movers.Pump pump1(
    ControlStrategy=2,
    V_flow_max=12,
    Head_max=10,
    redeclare package Medium = Medium,
    m_flow_small=1e-4)
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                        temperatureMixedTop(redeclare package Medium =
               Medium, m_flow_nominal=1)
  FixedResistances.PressureDrop       res2(
    redeclare package Medium = Medium,
    m_flow_nominal=0.1,
    dp_nominal=200) "Hydraulic resistance in secondary circuit"
  AixLib.Obsolete.Year2021.Fluid.Actuators.Valves.SimpleValve simpleValve(redeclare
      package                                                                               Medium = Medium, m_flow_small=1e-4)
  AixLib.Fluid.Sensors.MassFlowRate  massFlowSensor1Sec(redeclare package Medium =
               Medium)
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                        temperatureBottom(redeclare package Medium =
               Medium, m_flow_nominal=1)
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature fixedTemperature(
      T=273.15+20)
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor
    thermalConductor(G=1.6e3/8)
  Modelica.Blocks.Sources.BooleanExpression booleanExpression
  Modelica.Blocks.Sources.BooleanExpression booleanExpression1
  Modelica.Blocks.Sources.Step step(
    startTime=12000,
    height=0.5,
    offset=0.2)
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                        temperatureMixedBottom(redeclare
      package                                                                    Medium =
                       Medium, m_flow_nominal=1)
  AixLib.Fluid.Sensors.TemperatureTwoPort
                                        temperatureTop(redeclare package Medium =
        Medium, m_flow_nominal=1)
equation
  connect(pump.port_b, massFlowSensorPrim.port_a)
  connect(temperatureMixedTop.port_b, pump1.port_a)
  connect(pump1.port_b, res2.port_a)
  connect(res2.port_b, simpleValve.port_a)
  connect(massFlowSensor1Sec.port_b, temperatureBottom.port_a)
  connect(thermalConductor.port_a, fixedTemperature.port)
  connect(pump1.IsNight, booleanExpression1.y)
  connect(pump.IsNight, booleanExpression.y)
  connect(step.y, simpleValve.opening)
  connect(res1.port_a, temperatureMixedBottom.port_b)
  connect(res1.port_b, pump.port_a)
  connect(boundary_p.ports[1], pump.port_a)
  connect(temperatureTop.port_b, hydraulicSeparator.port_a_primary)
  connect(temperatureMixedTop.port_a, hydraulicSeparator.port_b_secondary)
  connect(hydraulicSeparator.port_b_primary, temperatureMixedBottom.port_a)
  connect(hydraulicSeparator.port_a_secondary, temperatureBottom.port_b)
  connect(massFlowSensorPrim.port_b, heatSource.ports[1])
  connect(heatSource.ports[2], temperatureTop.port_a)
  connect(fixedHeatFlow.port, heatSource.heatPort)
  connect(thermalConductor.port_b, heatSink.heatPort)
  connect(massFlowSensor1Sec.port_a, heatSink.ports[1])
  connect(simpleValve.port_b, heatSink.ports[2])
end HydraulicSeparator;


Test model for heat transfer to volume

within AixLib.Fluid.MixingVolumes.Examples;
model MixingVolumeHeatConduction "Test model for heat transfer to volume"
  extends Modelica.Icons.Example;
    package Medium = AixLib.Media.Air;
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=10)
    "Thermal conductor"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFlo
    "Heat flow sensor"
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TAmb(T=293.15)
    "Ambient temperature"
  Modelica.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=313.15,
    nPorts=1) "Flow source and sink"
  Modelica.Fluid.Sources.FixedBoundary bou(
    redeclare package Medium = Medium,
    T=303.15,
    nPorts=1) "Boundary condition"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    V=1,
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=true,
    nPorts=2)
  Modelica.Blocks.Sources.Ramp ramp(
    duration=1,
    offset=1,
    height=-2)

  Modelica.Blocks.Math.Gain gain(k=0.01)
equation
  connect(TAmb.port, theCon.port_a)
  connect(heaFlo.port_b, vol.heatPort)
  connect(ramp.y, gain.u)
  connect(gain.y, sou.m_flow_in)
  connect(theCon.port_b, heaFlo.port_a)
  connect(sou.ports[1], vol.ports[1])
  connect(vol.ports[2], bou.ports[1])
end MixingVolumeHeatConduction;


within AixLib.Fluid.MixingVolumes.Examples;
model MixingVolumeMoistAir "Test model for mixing volume with moist air input"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;

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

  AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol1(
    redeclare package Medium = Medium,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=m_flow_nominal,
    nPorts=2)                      "Volume"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TSen
    "Temperature sensor"
  Modelica.Blocks.Sources.Constant XSet(k=0.005)
    "Set point for water mass fraction"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
  Modelica.Blocks.Sources.Constant TSet(k=273.15 + 20)
    "Set point for temperature"
  AixLib.Utilities.Psychrometrics.pW_X humRat(use_p_in=false)
    "Conversion from humidity ratio to partial water vapor pressure"
  AixLib.Utilities.Psychrometrics.TDewPoi_pW dewPoi "Dew point temperature"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heatFlowSensor
  Modelica.Blocks.Continuous.Integrator QSen "Sensible heat transfer"
  Modelica.Blocks.Continuous.Integrator QLat "Enthalpy of extracted water"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    T=293.15)
  AixLib.Fluid.Sources.Boundary_pT sin(        redeclare package Medium =
        Medium,
    T=293.15,
    nPorts=1)
  AixLib.Controls.Continuous.LimPID PI(
    Ni=0.1,
    yMax=1000,
    k=1,
    Ti=1,
    Td=1,
    wd=0,
    yMin=-1000)
  AixLib.Controls.Continuous.LimPID PI1(
    Ni=0.1,
    Ti=1,
    k=10,
    yMax=1,
    yMin=-1,
    Td=1)
  AixLib.Fluid.Sensors.MassFlowRate mIn_flow(redeclare package Medium =
        Medium)
  AixLib.Fluid.Sensors.MassFlowRate mOut_flow(redeclare package Medium =
        Medium)
  Modelica.Blocks.Math.Add dM_flow(k2=-1)
  Modelica.Blocks.Math.Gain gai(k=200)
  Modelica.Blocks.Math.Gain gai1(k=0.1)

  AixLib.Fluid.FixedResistances.PressureDrop res1(
    redeclare package Medium = Medium,
    from_dp=true,
    dp_nominal=2.5,
    m_flow_nominal=m_flow_nominal)
  Sensors.LatentEnthalpyFlowRate senLatEnt(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    tau=0,
    initType=Modelica.Blocks.Types.Init.SteadyState) "Latent enthalpy inflow"
  Sensors.LatentEnthalpyFlowRate senLatEnt1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    tau=0,
    initType=Modelica.Blocks.Types.Init.SteadyState) "Latent enthalpy outflow"
  Modelica.Blocks.Math.Add add
equation
  connect(preHeaFlo.port, heatFlowSensor.port_a)
  connect(heatFlowSensor.Q_flow, QSen.u)
  connect(TSet.y, PI.u_s)
  connect(TSen.T, PI.u_m)
  connect(XSet.y, PI1.u_s)
  connect(mOut_flow.m_flow, dM_flow.u1)
  connect(mIn_flow.m_flow, dM_flow.u2)
  connect(gai.y, preHeaFlo.Q_flow)
  connect(PI1.y, gai1.u)
  connect(gai1.y, vol1.mWat_flow)
  connect(vol1.X_w, PI1.u_m)
  connect(vol1.X_w, humRat.X_w)
  connect(sou.ports[1], mIn_flow.port_a)
  connect(heatFlowSensor.port_b, vol1.heatPort)
  connect(TSen.port, vol1.heatPort)
  connect(PI.y, gai.u)
  connect(humRat.p_w, dewPoi.p_w)
  connect(mOut_flow.port_b, res1.port_a)
  connect(res1.port_b, sin.ports[1])
  connect(mIn_flow.port_b, senLatEnt.port_a)
  connect(senLatEnt.port_b, vol1.ports[1])
  connect(vol1.ports[2], senLatEnt1.port_a)
  connect(senLatEnt1.port_b, mOut_flow.port_a)
  connect(add.y, QLat.u)
  connect(add.u1, senLatEnt.H_flow)
  connect(senLatEnt1.H_flow, add.u2)
end MixingVolumeMoistAir;


Test model for heat transfer to volume

within AixLib.Fluid.MixingVolumes.Examples;
model MixingVolumePrescribedHeatFlowRate
  "Test model for heat transfer to volume"
  extends Modelica.Icons.Example;
    package Medium = AixLib.Media.Air;
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFlo
    "Heat flow sensor"
  Modelica.Fluid.Sources.MassFlowSource_T sou(
    nPorts=1,
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=313.15) "Flow source and sink"
  Modelica.Fluid.Sources.FixedBoundary bou(
    redeclare package Medium = Medium,
    nPorts=1,
    T=303.15) "Boundary condition"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    V=1,
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=true,
    prescribedHeatFlowRate=true)
  Modelica.Blocks.Sources.Ramp ramp(
    duration=1,
    offset=1,
    height=-2)

  Modelica.Blocks.Math.Gain gain(k=0.01)
  Modelica.Blocks.Sources.Constant const(k=0)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow rate"
equation
  connect(sou.ports[1], vol.ports[1])
  connect(ramp.y, gain.u)
  connect(gain.y, sou.m_flow_in)
  connect(vol.ports[2], bou.ports[1])
  connect(preHeaFlo.port, heaFlo.port_a)
  connect(preHeaFlo.Q_flow, const.y)
  connect(heaFlo.port_b, vol.heatPort)
end MixingVolumePrescribedHeatFlowRate;


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


within AixLib.Fluid.MixingVolumes.Validation;
model CoupledVolumesAir
  "Validation model for two coupled volumes with air"
  extends AixLib.Fluid.MixingVolumes.Validation.CoupledVolumesWater(
    redeclare package Medium = AixLib.Media.Air);
end CoupledVolumesAir;


within AixLib.Fluid.MixingVolumes.Validation;
model CoupledVolumesGlycol47
  "Validation model for two coupled volumes with glycol"
  extends AixLib.Fluid.MixingVolumes.Validation.CoupledVolumesWater(
    redeclare package Medium = Modelica.Media.Incompressible.Examples.Glycol47);
end CoupledVolumesGlycol47;


within AixLib.Fluid.MixingVolumes.Validation;
model CoupledVolumesSpecializedAirPerfectGas
  "Validation model for two coupled volumes with air as a perfect gas"
  extends AixLib.Fluid.MixingVolumes.Validation.CoupledVolumesWater(
    redeclare package Medium =
        AixLib.Media.Specialized.Air.PerfectGas);
end CoupledVolumesSpecializedAirPerfectGas;


within AixLib.Fluid.MixingVolumes.Validation;
model CoupledVolumesSpecializedWaterConstantProperties_pT
  "Validation model for two coupled volumes with water with constant properties"
  extends AixLib.Fluid.MixingVolumes.Validation.CoupledVolumesWater(
    redeclare package Medium =
        AixLib.Media.Specialized.Water.ConstantProperties_pT);
end CoupledVolumesSpecializedWaterConstantProperties_pT;


within AixLib.Fluid.MixingVolumes.Validation;
model CoupledVolumesSpecializedWaterTemperatureDependentDensity
  "Validation model for two coupled volumes with water with temperature dependent density"
  extends AixLib.Fluid.MixingVolumes.Validation.CoupledVolumesWater(
    redeclare package Medium =
        AixLib.Media.Specialized.Water.TemperatureDependentDensity);
end CoupledVolumesSpecializedWaterTemperatureDependentDensity;


within AixLib.Fluid.MixingVolumes.Validation;
model CoupledVolumesWater
  "Validation model for two coupled volumes with water"
  extends Modelica.Icons.Example;

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

  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    nPorts=2,
    m_flow_nominal=1,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
  AixLib.Fluid.MixingVolumes.MixingVolume vol1(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=1)
  AixLib.Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1)
    "Pressure boundary condition"
  AixLib.Fluid.FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    dp_nominal=100) "Resistance to decouple initialization of pressure state"
equation
  connect(bou.ports[1], res.port_a)
  connect(res.port_b, vol.ports[1])
  connect(vol.ports[2], vol1.ports[1])
end CoupledVolumesWater;


Test model for mixing volumes

within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolume "Test model for mixing volumes"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model";

    Modelica.Blocks.Sources.Ramp P(
    duration=0.5,
    startTime=0.5,
    height=-10,
    offset=101330)
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    T=293.15,
    use_p_in=true,
    nPorts=3)
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=3,
    use_p_in=false,
    p=101325,
    T=283.15)
  AixLib.Fluid.FixedResistances.PressureDrop res1(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=2,
    dp_nominal=2.5)
  MixingVolumes.MixingVolume vol1(
    redeclare package Medium = Medium,
    V=0.1,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=2)
  AixLib.Fluid.FixedResistances.PressureDrop res2(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=2,
    dp_nominal=2.5)
  AixLib.Fluid.FixedResistances.PressureDrop res11(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=2,
    dp_nominal=2.5)
  AixLib.Fluid.FixedResistances.PressureDrop res12(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=2,
    dp_nominal=2.5)
  Modelica.Fluid.Vessels.ClosedVolume vol(
    redeclare package Medium = Medium,
    massDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=0.1,
    nPorts=2,
    h_start=45300.945,
    use_portsData=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
  AixLib.Utilities.Diagnostics.CheckEquality cheEqu1
    "Check for equality of results"
  AixLib.Fluid.Sensors.EnthalpyFlowRate entFloRat(
    redeclare package Medium = Medium,
    m_flow_nominal=2) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate entFloRat1(
    redeclare package Medium = Medium,
    m_flow_nominal=2) "Enthalpy flow rate"
  AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol2(
    redeclare package Medium = Medium,
    V=0.1,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=2)
  AixLib.Fluid.FixedResistances.PressureDrop res21(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=2,
    dp_nominal=2.5)
  AixLib.Fluid.FixedResistances.PressureDrop res22(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=2,
    dp_nominal=2.5)
  AixLib.Utilities.Diagnostics.CheckEquality cheEqu2
    "Check for equality of results"
  AixLib.Fluid.Sensors.EnthalpyFlowRate entFloRat2(
    redeclare package Medium = Medium,
    m_flow_nominal=2) "Enthalpy flow rate"
    Modelica.Blocks.Sources.Constant zero(k=0)

  inner Modelica.Fluid.System system
equation
  connect(P.y, sou.p_in)
  connect(res2.port_a, entFloRat.port_b)
  connect(entFloRat2.H_flow, cheEqu2.u2)
  connect(zero.y, vol2.mWat_flow)
  connect(sou.ports[1], res1.port_a)
  connect(sou.ports[2], res11.port_a)
  connect(sou.ports[3], res21.port_a)
  connect(sin.ports[1], res2.port_b)
  connect(sin.ports[2], res12.port_b)
  connect(res22.port_b, sin.ports[3])
  connect(res1.port_b, vol.ports[1])
  connect(vol.ports[2], entFloRat.port_a)
  connect(res11.port_b, vol1.ports[1])
  connect(vol1.ports[2], entFloRat1.port_a)
  connect(res21.port_b, vol2.ports[1])
  connect(vol2.ports[2], entFloRat2.port_a)
  connect(entFloRat2.port_b, res22.port_a)
  connect(entFloRat1.port_b, res12.port_a)
  connect(entFloRat.H_flow, cheEqu1.u1)
  connect(entFloRat.H_flow, cheEqu2.u1)
  connect(entFloRat1.H_flow, cheEqu1.u2)
end MixingVolume;


within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeAdiabaticCooling
  "Validation model for mixing volume with adiabatic cooling"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model";
  MixingVolumeMoistAir volSim(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=1,
    V=1,
    final simplify_mWat_flow=true,
    nPorts=1)
    "Mixing volume with water vapor input and simplified water balance"
  MixingVolumeMoistAir volExa(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=1,
    V=1,
    final simplify_mWat_flow=false,
    nPorts=1) "Mixing volume with water vapor input and exact water balance"
  Modelica.Blocks.Sources.Constant mWatFlo(k=0.001) "Water mass flow rate "
  Modelica.Blocks.Sources.RealExpression relErr(
    y=(volSim.heatPort.T - 20)
      /(if abs(volExa.heatPort.T - 20) < 1E-5 then 1 else (volExa.heatPort.T - 20)))
    "Relative error in the temperature difference"
  AixLib.Fluid.Sources.Boundary_pT bou(redeclare package Medium = Medium, nPorts=2)
    "Fixed pressure boundary condition"

equation
  connect(volSim.mWat_flow, mWatFlo.y)
  connect(volExa.mWat_flow, mWatFlo.y)
  connect(bou.ports[1], volSim.ports[1])
  connect(bou.ports[2], volExa.ports[1])
end MixingVolumeAdiabaticCooling;


Validation model for mixing volume with heat input and flow reversal. This model validates the use of the mixing volume with air flowing into and out of the volume
and sensible heat added to the volume.

within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeHeatReverseFlow
  "Validation model for mixing volume with heat input and flow reversal"
  extends
    AixLib.Fluid.MixingVolumes.Validation.BaseClasses.MixingVolumeReverseFlow(
      gain(k=10));

  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaDyn
    "Prescribed heat flow rate"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaSte
    "Prescribed heat flow rate"
equation
  connect(gain.y, preHeaDyn.Q_flow)
  connect(gain.y, preHeaSte.Q_flow)
  connect(preHeaDyn.port, volDyn.heatPort)
  connect(preHeaSte.port, volSte.heatPort)
end MixingVolumeHeatReverseFlow;


Validation model for mixing volume with heat input and flow reversal and prescribed heat flow rate. This model is identical to

AixLib.Fluid.MixingVolumes.Validation.MixingVolumeHeatReverseFlow,
except that the steady state volume volSte
is configured to have a prescribed heat flow rate,
which is in this case zero as the heat port is not connected.
This configures volSte to use the two port
steady state heat and mass balance model

AixLib.Fluid.Interfaces.StaticTwoPortConservationEquation.

within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeHeatReverseFlowPrescribedHeatFlowRate
  "Validation model for mixing volume with heat input and flow reversal and prescribed heat flow rate"
  extends MixingVolumeHeatReverseFlow(
    prescribedHeatFlowRate=true);
end MixingVolumeHeatReverseFlowPrescribedHeatFlowRate;


within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeInitialization "Test model for mixing volume initialization"
  extends Modelica.Icons.Example;
 package Medium = AixLib.Media.Air;

  AixLib.Fluid.Sources.Boundary_pT sou1(redeclare package Medium =
        Medium,
    p=101330,
    nPorts=1,
    T=293.15)
  AixLib.Fluid.Sources.Boundary_pT sin1(redeclare package Medium =
        Medium,
    p=101320,
    nPorts=1,
    T=293.15)
  Modelica.Fluid.Pipes.StaticPipe pipe1(
    redeclare package Medium = Medium,
    length=1,
    diameter=0.25,
    flowModel(m_flow_nominal=2))
  Modelica.Fluid.Pipes.StaticPipe pipe2(
    redeclare package Medium = Medium,
    length=1,
    diameter=0.25,
    flowModel(m_flow_nominal=2))
  MixingVolumes.MixingVolume vol1(
    redeclare package Medium = Medium,
    V=0.1,
    nPorts=2,
    m_flow_nominal=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)

  inner Modelica.Fluid.System system
equation
  connect(sou1.ports[1], pipe1.port_a)
  connect(pipe1.port_b, vol1.ports[1])
  connect(vol1.ports[2], pipe2.port_a)
  connect(pipe2.port_b, sin1.ports[1])
end MixingVolumeInitialization;


within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeMassFlow "Test model for mass flow into and out of volume"
  extends Modelica.Icons.Example;
 package Medium = AixLib.Media.Air "Medium model";

  parameter Modelica.Units.SI.PressureDifference dp_nominal=10
    "Nominal pressure drop";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.5
    "Nominal mass flow rate";

  Modelica.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=false,
    m_flow=m_flow_nominal,
    T=313.15) "Flow source and sink"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    T=303.15,
    nPorts=1) "Boundary condition"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    V=1,
    redeclare package Medium = Medium,
    nPorts=2,
    allowFlowReversal=false,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)

equation
  connect(sou.ports[1], vol.ports[1])
  connect(bou.ports[1], vol.ports[2])
end MixingVolumeMassFlow;


within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeMFactor
  "A check for verifying the implementation of the parameter mSenFac"
  extends AixLib.Fluid.MixingVolumes.Validation.MixingVolumeMassFlow(
  sou(X={0.02,0.98},
      T=Medium.T_default),
  vol(mSenFac=10),
    bou(nPorts=3));
  AixLib.Fluid.MixingVolumes.MixingVolume volMFactor(
    redeclare package Medium = Medium,
    mSenFac=10,
    V=1,
    nPorts=2,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Mixing volume using mSenFac = 10"
  AixLib.Fluid.MixingVolumes.MixingVolume vol1(
    redeclare package Medium = Medium,
    V=10,
    nPorts=2,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "MixingVolume with V = 10 instead of mSenFac = 10"
  AixLib.Fluid.Sources.MassFlowSource_T boundaryMFactor(
    redeclare package Medium = Medium,
    T=300,
    nPorts=1,
    m_flow=1,
    X={0.02,0.98}) "Flow source for mixing volume using mSenFac"
  AixLib.Fluid.Sources.MassFlowSource_T boundary(
    redeclare package Medium = Medium,
    T=300,
    nPorts=1,
    m_flow=1,
    X={0.02,0.98}) "Flow source for mixing volume using larger volume"
equation
  connect(boundaryMFactor.ports[1],volMFactor. ports[1])
  connect(boundary.ports[1], vol1.ports[1])
  connect(bou.ports[2], volMFactor.ports[2])
  connect(bou.ports[3], vol1.ports[2])
end MixingVolumeMFactor;


Validation model for mixing volume with moisture input and flow reversal. This model validates the use of the mixing volume with air flowing into and out of the volume
and moisture added to the volume.

within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeMoistureReverseFlow
  "Validation model for mixing volume with moisture input and flow reversal"
  extends
    AixLib.Fluid.MixingVolumes.Validation.BaseClasses.MixingVolumeReverseFlow(
    gain(k=0.005),
    redeclare MixingVolumeMoistAir volDyn,
    redeclare MixingVolumeMoistAir volSte);

equation
  connect(volDyn.mWat_flow, gain.y)
  connect(gain.y, volSte.mWat_flow)
end MixingVolumeMoistureReverseFlow;


Validation model for mixing volume with moisture input and flow reversal and prescribed heat flow rate. This model is identical to

AixLib.Fluid.MixingVolumes.Validation.MixingVolumeMoistureReverseFlow,
except that the steady state volume volSte
is configured to have a prescribed heat flow rate,
which is in this case zero as the heat port is not connected.
This configures volSte to use the two port
steady state heat and mass balance model

AixLib.Fluid.Interfaces.StaticTwoPortConservationEquation.

within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeMoistureReverseFlowPrescribedHeatFlowRate
  "Validation model for mixing volume with moisture input and flow reversal and prescribed heat flow rate"
  extends MixingVolumeMoistureReverseFlow(
    prescribedHeatFlowRate=true);
end MixingVolumeMoistureReverseFlowPrescribedHeatFlowRate;


within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeSteadyStateMass "Test model for steady state mass dynamics"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1) "Flow source and sink"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Boundary condition"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=1,
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    massDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=true,
    nPorts=2,
    mSenFac=2)
    "Mixing volume with steady state mass dynamics"
  Modelica.Blocks.Sources.Ramp ramp(
    duration=1,
    offset=1,
    height=-2) "Ramp input"

  Modelica.Blocks.Math.Gain gain(k=0.01) "Gain for nominal mass flow rate"
  FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    dp_nominal=1000) "Pressure drop"
equation
  connect(ramp.y, gain.u)
  connect(gain.y, sou.m_flow_in)
  connect(sou.ports[1], vol.ports[1])
  connect(vol.ports[2], res.port_a)
  connect(res.port_b, bou.ports[1])
end MixingVolumeSteadyStateMass;


within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeTraceSubstance
  "Test model for mixing volume with trace substance input"
  extends Modelica.Icons.Example;
 package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"})
    "Medium model";

  parameter Modelica.Units.SI.Pressure dp_nominal=10 "Nominal pressure drop";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.5
    "Nominal mass flow rate";

  Modelica.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=false,
    m_flow=m_flow_nominal,
    T=313.15) "Flow source and sink"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    T=303.15,
    nPorts=2) "Boundary condition"
  AixLib.Fluid.MixingVolumes.MixingVolume volDyn(
    redeclare package Medium = Medium,
    V=1,
    nPorts=2,
    allowFlowReversal=false,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    use_C_flow=true) "Volume with dynamic balance"

  Modelica.Blocks.Sources.Constant C_flow(k=m_flow_nominal/1000)
    "Trace substance mass flow rate"
  Modelica.Fluid.Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=false,
    m_flow=m_flow_nominal,
    T=313.15) "Flow source and sink"
  AixLib.Fluid.MixingVolumes.MixingVolume volSte(
    redeclare package Medium = Medium,
    V=1,
    nPorts=2,
    m_flow_nominal=m_flow_nominal,
    use_C_flow=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false) "Volume with steady-state balance"
equation
  connect(sou.ports[1], volDyn.ports[1])
  connect(C_flow.y, volDyn.C_flow[1])
  connect(sou1.ports[1], volSte.ports[1])
  connect(C_flow.y, volSte.C_flow[1])
  connect(volSte.ports[2], bou.ports[1])
  connect(volDyn.ports[2], bou.ports[2])
end MixingVolumeTraceSubstance;


Validation model for mixing volume with trace substance input and flow reversal. This model validates the use of the mixing volume with air flowing into and out of the volume
and trace substances added to the volume.

within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeTraceSubstanceReverseFlow
  "Validation model for mixing volume with trace substance input and flow reversal"
  extends
    AixLib.Fluid.MixingVolumes.Validation.BaseClasses.MixingVolumeReverseFlow(
    Medium(extraPropertiesNames={"CO2"}),
    volDyn(use_C_flow=true),
    volSte(use_C_flow=true),
    gain(k=1/1000),
    bou(C={0.003}));

equation
  connect(gain.y, volSte.C_flow[1])
  connect(gain.y, volDyn.C_flow[1])
end MixingVolumeTraceSubstanceReverseFlow;


Validation model for mixing volume with trace substance input and flow reversal, and prescribed heat flow rate. This model is identical to

AixLib.Fluid.MixingVolumes.Validation.MixingVolumeTraceSubstanceReverseFlow,
except that the steady state volume volSte
is configured to have a prescribed heat flow rate,
which is in this case zero as the heat port is not connected.
This configures volSte to use the two port
steady state heat and mass balance model

AixLib.Fluid.Interfaces.StaticTwoPortConservationEquation.

within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeTraceSubstanceReverseFlowPrescribedHeatFlowRate
  "Validation model for mixing volume with trace substance input and flow reversal, and prescribed heat flow rate"
  extends MixingVolumeTraceSubstanceReverseFlow(
    prescribedHeatFlowRate=true);
end MixingVolumeTraceSubstanceReverseFlowPrescribedHeatFlowRate;


within AixLib.Fluid.MixingVolumes.Validation;
model MixingVolumeZeroFlow
  "Mixing volume verification around zero flow with heat exchange"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water;
  AixLib.Fluid.MixingVolumes.MixingVolume volNonLinSys(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false)
    "Steady state mixing volume requiring solution of non-linear system"
  AixLib.Fluid.Sources.MassFlowSource_T sou2(
    nPorts=1,
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=true) "Source"
  AixLib.Fluid.Sources.Boundary_pT sin(nPorts=4, redeclare package Medium =
        Medium) "Sink"
  Modelica.Blocks.Sources.Ramp ramp_m_flow(
    height=-1,
    duration=1,
    offset=1) "Mass flow rate ramp input"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea2
    "Prescribed heat flow"
  Modelica.Blocks.Sources.RealExpression reaExp(y=(290 - volNonLinSys.heatPort.T)
        /100*volNonLinSys.heatPort.T^(3/5)*time)
    "Non-linear thermal resistance equation"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature preTem
    "Prescribed temperature"
  Modelica.Blocks.Sources.Cosine cos1(
    f=1,
    offset=283.15,
    amplitude=0.001) "Cosine input"
  AixLib.Fluid.MixingVolumes.MixingVolume volT(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false)
    "Steady state mixing volume with prescribed temperature input"
  AixLib.Fluid.Sources.MassFlowSource_T sou3(
    nPorts=1,
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=true) "Source"
  AixLib.Fluid.MixingVolumes.MixingVolume volLinSys(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false,
    prescribedHeatFlowRate=false)
    "Steady state mixing volume requiring solution of linear system"
  AixLib.Fluid.Sources.MassFlowSource_T sou4(
    nPorts=1,
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=true) "Source"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor theRes(R=0.001)
    "Thermal resistor for creating linear system"
  Modelica.Blocks.Sources.Ramp ramp_T(
    duration=2,
    offset=283.15,
    height=0) "Temperature ramp input"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea1
    "Prescribed heat flow"
  AixLib.Fluid.MixingVolumes.MixingVolume volQflow(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false,
    prescribedHeatFlowRate=true)
    "Steady state mixing volume with fixed heat flow rate input"
  AixLib.Fluid.Sources.MassFlowSource_T sou1(
    nPorts=1,
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=true) "Source"
  Modelica.Blocks.Routing.RealPassThrough reaPasThr
    "Real pass through for unit conversion"
equation
  connect(sou2.ports[1], volNonLinSys.ports[1])
  connect(volNonLinSys.ports[2], sin.ports[1])
  connect(reaExp.y, preHea2.Q_flow)
  connect(preTem.T, cos1.y)
  connect(volT.ports[1],sou3. ports[1])
  connect(volT.ports[2], sin.ports[2])
  connect(volT.heatPort, preTem.port)
  connect(sou4.ports[1], volLinSys.ports[1])
  connect(volLinSys.ports[2], sin.ports[3])
  connect(theRes.port_a, volLinSys.heatPort)
  connect(theRes.port_b, preTem.port)
  connect(volNonLinSys.heatPort, preHea2.port)
  connect(volQflow.heatPort, preHea1.port)
  connect(sou1.ports[1], volQflow.ports[1])
  connect(volQflow.ports[2], sin.ports[4])
  connect(ramp_m_flow.y, sou1.m_flow_in)
  connect(ramp_m_flow.y, sou2.m_flow_in)
  connect(ramp_m_flow.y, sou3.m_flow_in)
  connect(ramp_m_flow.y, sou4.m_flow_in)
  connect(ramp_T.y, sou1.T_in)
  connect(ramp_T.y, sou2.T_in)
  connect(ramp_T.y, sou3.T_in)
  connect(ramp_T.y, sou4.T_in)
  connect(reaPasThr.y, preHea1.Q_flow)
  connect(reaPasThr.u, ramp_m_flow.y)
end MixingVolumeZeroFlow;


This test checks if mass and energy is conserved when mixing fluid streams using dynamic balances. This model verifies if mass and energy are conserved,
using a dynamic balance.
Two air streams with different mass flow rate are humidified
by a mixing volume with two different vapor mass flow rates.
These flows are then mixed.
Boundary integrals are used to verify if air mass,
vapour mass and internal energy are conserved.

within AixLib.Fluid.MixingVolumes.Validation;
model MoistureMixingConservationDynamicBalance
  "This test checks if mass and energy is conserved when mixing fluid streams using dynamic balances"
  extends
    AixLib.Fluid.MixingVolumes.Validation.BaseClasses.MoistureMixingConservation(
    mWatFloSol(k={vol.X_start[1],vol1.X_start[1],vol2.X_start[1]}*m_start),
    vol(
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
    vol1(
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
    vol2(
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
    mFloSol(k=sum(m_start)),
    hSol(k=sum(U_start)),
    mWatFlo3(k=0));

  parameter Modelica.Units.SI.Mass[3] m_start(each fixed=false)
    "Initial mass of the mixing volumes";
  parameter Modelica.Units.SI.InternalEnergy[3] U_start(each fixed=false)
    "Initial energy of the mixing volumes";
  Modelica.Blocks.Continuous.Integrator intMasFloVapIn(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integral of added vapour"
  Modelica.Blocks.Math.Add3 add "Adder for injected water"
  Modelica.Blocks.Sources.RealExpression masVapVol(y=vol.mXi[1] + vol1.mXi[1] +
        vol2.mXi[1]) "Vapour mass stored in mixing volumes"
  Modelica.Blocks.Math.Add3 add3Vap(k3=-1)
    "Sum of vapour mass should be conserved"
  Modelica.Blocks.Sources.RealExpression masVol(y=vol1.m + vol.m + vol2.m)
    "Mass stored in mixing volumes"
  Modelica.Blocks.Continuous.Integrator intMasFloOut(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integral of leaving mass"
  Modelica.Blocks.Math.Add3 add3Mass(k3=-1) "Adding 3 mass streams"
  Modelica.Blocks.Continuous.Integrator intMasFloIn(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integral of added mass"
  Modelica.Blocks.Sources.Constant masFloIn(k=sou1.m_flow + sou2.m_flow)
    "Added air mass flow rate"
  Modelica.Blocks.Continuous.Integrator intEntOut(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integral of leaving enthalpy"
  Modelica.Blocks.Continuous.Integrator intEntIn(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integral of added enthalpy"
  Modelica.Blocks.Sources.RealExpression entVol(y=vol1.U + vol.U + vol2.U)
    "Energy stored in mixing volumes"
  Modelica.Blocks.Math.Add3 add3Ent(k3=-1) "Adding 3 enthalpy streams"
  Modelica.Blocks.Sources.Constant entIn(
    k=sou1.m_flow*sou1.h + sou2.m_flow*sou2.h)
    "Added enthalpy"
  Modelica.Blocks.Math.Product pro "Water vapor flow rate"
  Modelica.Blocks.Continuous.Integrator intMasVapOut(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integral of leaving vapor mass"
initial equation
  m_start = {vol.m, vol1.m,vol2.m};
  U_start = {vol.U, vol1.U,vol2.U};

equation
  connect(add.u1, mWatFlo2.y)

  connect(add.u2, mWatFlo1.y)

  connect(add3Vap.y,cheMasFra.u2)
  connect(masVapVol.y,add3Vap. u2)
  connect(intMasFloVapIn.u, add.y)
  connect(intMasFloVapIn.y,add3Vap. u3)
  connect(senMasFlo.m_flow, intMasFloOut.u)
  connect(add3Mass.y,cheMasFlo.u2)
  connect(masVol.y, add3Mass.u2)
  connect(intMasFloIn.y, add3Mass.u3)
  connect(intMasFloIn.u, masFloIn.y)
  connect(add3Ent.y,cheSpeEnt.u2)
  connect(entVol.y, add3Ent.u2)
  connect(intEntIn.y, add3Ent.u3)
  connect(entIn.y, intEntIn.u)
  connect(add3Ent.u1, intEntOut.y)
  connect(intEntOut.u, senSpeEnt.H_flow)
  connect(add.u3, mWatFlo3.y)
  connect(pro.u1, senMasFra.X)
  connect(pro.u2, senMasFlo.m_flow)
  connect(intMasVapOut.y, add3Vap.u1)
  connect(intMasVapOut.u, pro.y)
  connect(add3Mass.u1, intMasFloOut.y)
end MoistureMixingConservationDynamicBalance;


This test checks if mass and energy is conserved when mixing fluid streams using steady state balances. This test checks if water vapour mass is conserved.
Two air streams with different mass flow rate are humidified
by a mixing volume with two different vapor mass flow rates.
These flows are then mixed.
Afterwards the added water is removed again.
The final water concentration, mass flow rate and enthalpy
flow rate should then be equal to the initial values.

within AixLib.Fluid.MixingVolumes.Validation;
model MoistureMixingConservationSteadyState
  "This test checks if mass and energy is conserved when mixing fluid streams using steady state balances"
  extends
    AixLib.Fluid.MixingVolumes.Validation.BaseClasses.MoistureMixingConservation(
    mWatFloSol(k=0),
    mFloSol(k=sou1.m_flow + sou2.m_flow),
    hSol(k=Medium.h_default*(sou1.m_flow + sou2.m_flow)));

equation
  connect(cheMasFra.u2, senMasFra.X)
  connect(cheMasFlo.u2, senMasFlo.m_flow)
  connect(cheSpeEnt.u2, senSpeEnt.H_flow)
end MoistureMixingConservationSteadyState;


within AixLib.Fluid.MixingVolumes.Validation;
model MSLCombustionAir
  "Test model with medium for combustion air from the Modelica Standard Library"
    extends Modelica.Icons.Example;

  replaceable package Medium =
      Modelica.Media.IdealGases.MixtureGases.CombustionAir;

  parameter Modelica.Units.SI.PressureDifference dp_nominal=10
    "Nominal pressure drop";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=10
    "Nominal mass flow rate";

  AixLib.Fluid.FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal)
    "Pressure drop"
  AixLib.Fluid.Sources.MassFlowSource_T masFloSou(
    redeclare package Medium = Medium,
    m_flow=m_flow_nominal,
    nPorts=1) "Mass flow source"
  AixLib.Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    p=Medium.p_default + dp_nominal,
    nPorts=1)
    "Pressure boundary condition"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=m_flow_nominal,
    V=1,
    nPorts=2)
    "Control volume"
equation
  connect(res.port_b, bou.ports[1])
  connect(masFloSou.ports[1], vol.ports[1])
  connect(vol.ports[2], res.port_a)
end MSLCombustionAir;


within AixLib.Fluid.MixingVolumes.Validation;
model MSLFlueGasSixComponents
  "Test model with medium for flue gas from the Modelica Standard Library"
    extends AixLib.Fluid.MixingVolumes.Validation.MSLCombustionAir(
      redeclare package Medium =
        Modelica.Media.IdealGases.MixtureGases.FlueGasSixComponents);
end MSLFlueGasSixComponents;


Collection of validation models

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


This test checks if trace substance mass flow rates are conserved when a dynamic balance is used. This test checks if the trace substance flow rate is
conserved when adding moisture to a mixing volume that is configured to steady state.<br/>
The trace substance flow rate at the inlet and outlet should be equal
since the trace substance concentration should not
be affected by the independent mass fraction concentration.

within AixLib.Fluid.MixingVolumes.Validation;
model TraceSubstanceConservationDynamicBalance
  "This test checks if trace substance mass flow rates are conserved when a dynamic balance is used"
  extends
    AixLib.Fluid.MixingVolumes.Validation.BaseClasses.TraceSubstanceConservation(
     vol(massDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));

  Modelica.Blocks.Continuous.Integrator intTraSubIn(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integrator for trace substance inlet"
  Modelica.Blocks.Continuous.Integrator intTraSubOut(
    k=1,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0) "Integrator for trace substance outlet"
  Modelica.Blocks.Sources.RealExpression reaExp(y=vol.m*vol.C[1])
    "Mixing volume total species mass"
  Modelica.Blocks.Math.Add cheConMas(k2=-1) "Check for conservation of mass"
  Modelica.Blocks.Math.Add3 add3(
    k1=-1,
    k2=+1,
    k3=-1) "Conservation of mass"
  Modelica.Blocks.Sources.Constant zero(k=0) "Zero input"
equation
  connect(reaExp.y, add3.u3)
  connect(intTraSubIn.y, add3.u2)
  connect(intTraSubOut.y, add3.u1)
  connect(add3.y, cheConMas.u1)
  connect(zero.y, cheConMas.u2)
  connect(intTraSubIn.u, CfloIn.y)
  connect(CfloOut.y, intTraSubOut.u)
end TraceSubstanceConservationDynamicBalance;


This test checks if trace substance mass flow rates are conserved when steady state. This test checks if the trace substance flow rate is conserved
when adding moisture to a mixing volume that is configured to steady state.<br/>
The trace substance flow rate at the inlet and outlet should be equal
since the trace substance concentration should not
be affected by the independent mass fraction concentration.

within AixLib.Fluid.MixingVolumes.Validation;
model TraceSubstanceConservationSteadyState
  "This test checks if trace substance mass flow rates are conserved when steady state"
  extends
    AixLib.Fluid.MixingVolumes.Validation.BaseClasses.TraceSubstanceConservation(
     sou(X={0,1}));
  Modelica.Blocks.Math.Add cheEquTra2(k2=-1)
    "Check for equality of trace substances"
  Modelica.Blocks.Math.Add cheEquTra1(k2=-1)
    "Check for equality of trace substances"
  Modelica.Blocks.Sources.Constant const(k=sou.m_flow*sou.C[1])
    "Set point of trace substance concentration"
equation
  connect(const.y,cheEquTra1. u1)
  connect(cheEquTra1.u2, CfloIn.y)
  connect(cheEquTra2.u2, CfloIn.y)
  connect(cheEquTra2.u1, CfloOut.y)
end TraceSubstanceConservationSteadyState;


within AixLib.Fluid.MixingVolumes.Validation.BaseClasses;
partial model MixingVolumeReverseFlow
  "Test model for mixing volume with flow reversal"
 extends Modelica.Icons.Example;
 package Medium = AixLib.Media.Air "Medium model";

  constant Boolean prescribedHeatFlowRate = false
    "Flag that affects what steady state balance is used in the volume";
  parameter Modelica.Units.SI.Pressure dp_nominal=10 "Nominal pressure drop";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=2.0
    "Nominal mass flow rate";

  Modelica.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=m_flow_nominal,
    use_m_flow_in=true,
    T=313.15) "Flow source and sink"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    T=303.15,
    nPorts=2) "Boundary condition"
  replaceable AixLib.Fluid.MixingVolumes.MixingVolume volDyn
    constrainedby AixLib.Fluid.MixingVolumes.MixingVolume(
        redeclare package Medium = Medium,
        V=1,
        nPorts=2,
        m_flow_nominal=m_flow_nominal,
        energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Volume with dynamic balance"

  replaceable AixLib.Fluid.MixingVolumes.MixingVolume volSte
    constrainedby AixLib.Fluid.MixingVolumes.MixingVolume(
        redeclare package Medium = Medium,
        final prescribedHeatFlowRate = prescribedHeatFlowRate,
        V=1,
        nPorts=2,
        m_flow_nominal=m_flow_nominal,
        energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Volume with steady-state balance"

  Modelica.Blocks.Math.Gain gain
    "Gain to add heat, moisture or trace substance flow rate"
  Modelica.Fluid.Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=m_flow_nominal,
    use_m_flow_in=true,
    T=313.15) "Flow source and sink"
  Modelica.Blocks.Sources.Ramp m_flow(
    height=-2*m_flow_nominal,
    duration=10,
    offset=m_flow_nominal) "Mass flow rate"
equation
  connect(sou.ports[1], volDyn.ports[1])
  connect(sou1.ports[1], volSte.ports[1])
  connect(volSte.ports[2], bou.ports[1])
  connect(volDyn.ports[2], bou.ports[2])
  connect(sou.m_flow_in, m_flow.y)
  connect(m_flow.y, sou1.m_flow_in)
  connect(m_flow.y, gain.u)
end MixingVolumeReverseFlow;


within AixLib.Fluid.MixingVolumes.Validation.BaseClasses;
partial model MoistureMixingConservation
  "Partial for checking conservation of mass for independent mass fraction"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air "Medium model";
  AixLib.Fluid.Sources.MassFlowSource_h sou1(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=2,
    X={0,1}) "Air source"
  AixLib.Fluid.Sources.MassFlowSource_h sou2(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=1,
    X={0,1}) "Air source"
  AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    nPorts=2,
    allowFlowReversal=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Mixing volume for adding water"
  AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol1(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    nPorts=2,
    allowFlowReversal=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Mixing volume for adding water"
  AixLib.Fluid.Sources.Boundary_pT sin(redeclare package Medium = Medium,
      nPorts=1) "Air sink"
  AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol2(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    nPorts=2,
    allowFlowReversal=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Mixing volume for removing water"
  Modelica.Blocks.Sources.Constant mWatFlo1(k=0.001) "Water mass flow rate 1"
  Modelica.Blocks.Sources.Constant mWatFlo3(k=-(mWatFlo1.k + mWatFlo2.k))
    "Withdrawn water rate"
  Modelica.Blocks.Sources.Constant mWatFlo2(k=0.003) "Water mass flow rate 2"
  Modelica.Blocks.Math.Add cheMasFra(k2=-1)
    "Check for water conservation"
  AixLib.Fluid.Sensors.MassFractionTwoPort senMasFra(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    tau=0,
    allowFlowReversal=false) "Sensor for measuring water content"

  Modelica.Blocks.Sources.Constant mWatFloSol "Solution mass fraction water"
  AixLib.Fluid.Sensors.MassFlowRate senMasFlo(redeclare package Medium =
        Medium, allowFlowReversal=false) "Mass flow rate sensor"
  Modelica.Blocks.Math.Add cheMasFlo(k2=-1)
    "Check for conservation of mass"
  Modelica.Blocks.Sources.Constant mFloSol "Solution mass flow rate"
  Sensors.EnthalpyFlowRate senSpeEnt(
    redeclare package Medium = Medium,
    allowFlowReversal=false,
    m_flow_nominal=1,
    tau=0) "Specific enthalpy flow rate sensor"
  Modelica.Blocks.Math.Add cheSpeEnt(k2=-1)
    "Check for conservation of energy"
  Modelica.Blocks.Sources.Constant hSol "Solution mass flow rate"

protected
  Modelica.Fluid.Interfaces.FluidPort_a port_a(
  redeclare package Medium = Medium)
    "Fluid port for using fluid stream mixing implementation"
equation
  connect(sou1.ports[1], vol.ports[1])
  connect(sou2.ports[1], vol1.ports[1])
  connect(mWatFlo1.y, vol.mWat_flow)
  connect(mWatFlo3.y, vol2.mWat_flow)
  connect(mWatFlo2.y, vol1.mWat_flow)
  connect(vol1.ports[2], port_a)
  connect(vol.ports[2], port_a)
  connect(port_a, vol2.ports[1])
  connect(mWatFloSol.y,cheMasFra. u1)
  connect(senMasFlo.port_b, senMasFra.port_a)
  connect(vol2.ports[2], senSpeEnt.port_a)
  connect(senSpeEnt.port_b, senMasFlo.port_a)
  connect(hSol.y,cheSpeEnt. u1)
  connect(mFloSol.y,cheMasFlo. u1)
  connect(senMasFra.port_b, sin.ports[1])
end MoistureMixingConservation;


Baseclasses package

within AixLib.Fluid.MixingVolumes.Validation;
package BaseClasses "Baseclasses package"
extends Modelica.Icons.BasesPackage;
end BaseClasses;


within AixLib.Fluid.MixingVolumes.Validation.BaseClasses;
model TraceSubstanceConservation
  "This test checks if trace substance mass flow rates are conserved"
  extends Modelica.Icons.Example;
  constant String substanceName="CO2";
  package Medium = AixLib.Media.Air(extraPropertiesNames={substanceName});
  AixLib.Fluid.Sources.MassFlowSource_h sou(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=1,
    X={0.01,0.99},
    C=fill(0.001, Medium.nC)) "Air source with moisture and trace substances"

  AixLib.Fluid.MixingVolumes.MixingVolumeMoistAir vol(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    nPorts=2,
    allowFlowReversal=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Mixing volume for adding moisture"
  Modelica.Blocks.Sources.Constant mWatFlo(k=0.001) "Water mass flow rate "
  AixLib.Fluid.Sensors.TraceSubstancesTwoPort senTraSubIn(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    tau=0,
    allowFlowReversal=false,
    substanceName=substanceName) "Measured inlet trace substance concentration"
  AixLib.Fluid.Sensors.TraceSubstancesTwoPort senTraSubOut(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    tau=0,
    allowFlowReversal=false,
    substanceName=substanceName)
    "Measured outlet trace substance concentration"
  AixLib.Fluid.Sources.Boundary_pT sin(redeclare package Medium = Medium,
      nPorts=1) "Air sink"
  Sensors.MassFlowRate senMasFloIn(redeclare package Medium = Medium,
      allowFlowReversal=false) "Fluid mass flow rate at inlet"
  Sensors.MassFlowRate senMasFloOut(redeclare package Medium = Medium,
      allowFlowReversal=false) "Fluid mass flow rate at outlet"
  Modelica.Blocks.Math.Product CfloIn "Trace substance mass flow rate at inlet"
  Modelica.Blocks.Math.Product CfloOut
    "Trace substance mass flow rate at outlet"
equation
  connect(mWatFlo.y, vol.mWat_flow)
  connect(sou.ports[1], senTraSubIn.port_a)
  connect(senTraSubIn.port_b, senMasFloIn.port_a)
  connect(senMasFloIn.port_b, vol.ports[1])
  connect(sin.ports[1], senMasFloOut.port_b)
  connect(senMasFloOut.port_a, senTraSubOut.port_b)
  connect(CfloIn.u2, senTraSubIn.C)
  connect(CfloIn.u1, senMasFloIn.m_flow)
  connect(CfloOut.u1, senMasFloOut.m_flow)
  connect(CfloOut.u2, senTraSubOut.C)
  connect(vol.ports[2], senTraSubOut.port_a)
end TraceSubstanceConservation;


Fan or pump with ideally controlled head dp as input signal. This model describes a fan or pump with prescribed head.
The input connector provides the pressure rise from the inlet to the outlet.

within AixLib.Fluid.Movers;
model FlowControlled_dp
  "Fan or pump with ideally controlled head dp as input signal"
  extends AixLib.Fluid.Movers.BaseClasses.PartialFlowMachine(
    final preVar=AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.PressureDifference,
    final computePowerUsingSimilarityLaws=per.havePressureCurve,
    preSou(dp_start=dp_start, control_dp= not prescribeSystemPressure),
    final stageInputs(each final unit="Pa") = heads,
    final constInput(final unit="Pa") = constantHead,
    final _m_flow_nominal = m_flow_nominal,
    filter(
      final y_start=dp_start,
      u(final unit="Pa"),
      y(final unit="Pa"),
      x(each nominal=dp_nominal),
      u_nominal=dp_nominal),
    eff(
      per(
        final pressure=
          if per.havePressureCurve then
            per.pressure
          else
            AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParameters(
              V_flow = {i/(nOri-1)*2.0*m_flow_nominal/rho_default for i in 0:(nOri-1)},
              dp =     {i/(nOri-1)*2.0*dp_nominal for i in (nOri-1):-1:0}),
        final etaHydMet=
          if (per.etaHydMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate
            or per.etaHydMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber)
            and not per.havePressureCurve then
              AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.NotProvided
          else per.etaHydMet,
        final etaMotMet=
          if (per.etaMotMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
            or per.etaMotMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve)
            and (not per.haveWMot_nominal and not per.havePressureCurve) then
               AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided
          else per.etaMotMet),
      r_N(start=if abs(dp_nominal) > 1E-8 then dp_start/dp_nominal else 0)));

  parameter Modelica.Units.SI.PressureDifference dp_start(
    min=0,
    displayUnit="Pa") = 0 "Initial value of pressure raise"

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal(
    final min=Modelica.Constants.small)
    "Nominal mass flow rate"

  // For air, we set dp_nominal = 600 as default, for water we set 10000
  parameter Modelica.Units.SI.PressureDifference dp_nominal(
    final min=Modelica.Constants.small,
    displayUnit="Pa") = if rho_default < 500 then 500 else 10000 "Nominal pressure raise, used to normalized the filter if use_inputFilter=true,
        to set default values of constantHead and heads, and
        and for default pressure curve if not specified in record per"

  parameter Modelica.Units.SI.PressureDifference constantHead(
    min=0,
    displayUnit="Pa") = dp_nominal
    "Constant pump head, used when inputType=Constant"

  // By default, set heads proportional to sqrt(speed/speed_nominal)
  parameter Modelica.Units.SI.PressureDifference[:] heads(
    each min=0,
    each displayUnit="Pa") = dp_nominal*{(per.speeds[i]/per.speeds[end])^2 for
    i in 1:size(per.speeds, 1)}
    "Vector of head set points, used when inputType=Stages"
  parameter Boolean prescribeSystemPressure = false
    "=true, to control mover such that pressure difference is obtained across two remote points in system"

  Modelica.Blocks.Interfaces.RealInput dpMea(
    final quantity="PressureDifference",
    displayUnit="Pa",
    final unit="Pa")=gain.u if prescribeSystemPressure
    "Measurement of pressure difference between two points where the set point should be obtained"

  Modelica.Blocks.Interfaces.RealInput dp_in(final unit="Pa")
    if inputType == AixLib.Fluid.Types.InputType.Continuous
    "Prescribed pressure rise"

  Modelica.Blocks.Interfaces.RealOutput dp_actual(final unit="Pa")
    "Pressure difference between the mover inlet and outlet"

protected
  Modelica.Blocks.Math.Gain gain(final k=-1)
equation
  assert(inputSwitch.u >= -1E-3,
    "Pressure set point for mover cannot be negative. Obtained dp = " + String(inputSwitch.u));

  if use_inputFilter then
    connect(filter.y, gain.u)
  else
    connect(inputSwitch.y, gain.u)
  end if;

  connect(inputSwitch.u, dp_in)
  connect(preSou.dp_in, gain.y)
  connect(senRelPre.p_rel, dp_actual)
end FlowControlled_dp;


Fan or pump with ideally controlled mass flow rate as input signal. This model describes a fan or pump with prescribed mass flow rate.

within AixLib.Fluid.Movers;
model FlowControlled_m_flow
  "Fan or pump with ideally controlled mass flow rate as input signal"
  extends AixLib.Fluid.Movers.BaseClasses.PartialFlowMachine(
    final preVar=AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.FlowRate,
    final computePowerUsingSimilarityLaws=per.havePressureCurve,
    final stageInputs(each final unit="kg/s")=massFlowRates,
    final constInput(final unit="kg/s")=constantMassFlowRate,
    final _m_flow_nominal = m_flow_nominal,
    filter(
      final y_start=m_flow_start,
      u(final unit="kg/s"),
      y(final unit="kg/s"),
      x(each nominal=m_flow_nominal),
      u_nominal=m_flow_nominal),
    eff(
      per(
        final pressure = if per.havePressureCurve then
          per.pressure
        else
          AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParameters(
            V_flow = {i/(nOri-1)*2.0*m_flow_nominal/rho_default for i in 0:(nOri-1)},
            dp =     {i/(nOri-1)*2.0*dp_nominal for i in (nOri-1):-1:0}),
        final etaHydMet=
          if (per.etaHydMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate
            or per.etaHydMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber)
            and not per.havePressureCurve then
              AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.NotProvided
          else per.etaHydMet,
        final etaMotMet=
          if (per.etaMotMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
            or per.etaMotMet ==
               AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve)
            and (not per.haveWMot_nominal and not per.havePressureCurve) then
               AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided
          else per.etaMotMet),
      r_N(start=if abs(m_flow_nominal) > 1E-8 then m_flow_start/m_flow_nominal else 0)),
    preSou(m_flow_start=m_flow_start));

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal(
    final min=Modelica.Constants.small)
    "Nominal mass flow rate"

  // For air, we set dp_nominal = 600 as default, for water we set 10000
  parameter Modelica.Units.SI.PressureDifference dp_nominal(
    final min=Modelica.Constants.small,
    displayUnit="Pa") = if rho_default < 500 then 500 else 10000
    "Nominal pressure raise, used for default pressure curve if not specified in record per"

  parameter Modelica.Units.SI.MassFlowRate m_flow_start(min=0) = 0
    "Initial value of mass flow rate"

  parameter Modelica.Units.SI.MassFlowRate constantMassFlowRate=m_flow_nominal
    "Constant pump mass flow rate, used when inputType=Constant"

  // By default, set massFlowRates proportional to (speed/speed_nominal)
  parameter Modelica.Units.SI.MassFlowRate[:] massFlowRates=m_flow_nominal*{per.speeds[
      i]/per.speeds[end] for i in 1:size(per.speeds, 1)}
    "Vector of mass flow rate set points, used when inputType=Stage"

  parameter Modelica.Units.SI.Pressure dpMax(
    min=0,
    displayUnit="Pa") = 2*max(eff.per.pressure.dp)
   "Maximum pressure allowed to operate the model, if exceeded, the simulation stops with an error"

  Modelica.Blocks.Interfaces.RealInput m_flow_in(
    final unit="kg/s",
    nominal=m_flow_nominal)
    if inputType == AixLib.Fluid.Types.InputType.Continuous
    "Prescribed mass flow rate"
  Modelica.Blocks.Interfaces.RealOutput m_flow_actual(
    final unit="kg/s",
    nominal=m_flow_nominal) "Actual mass flow rate"

equation
  assert(-dp <= dpMax,
    "In " + getInstanceName() + ": Model operates with head -dp = " + String(-dp) + " Pa,
    exceeding the pressure allowed by the parameter " + getInstanceName() + ".dpMax.
    This can happen if the model forces a high mass flow rate through a closed actuator,
    or if the performance record is unreasonable. Please verify your model, and
    consider using one of the other pump or fan models.");

  if use_inputFilter then
    connect(filter.y, m_flow_actual)
    connect(filter.y, preSou.m_flow_in)
  else
  connect(inputSwitch.y, m_flow_actual)
  connect(inputSwitch.y, preSou.m_flow_in)
  end if;

  connect(inputSwitch.u, m_flow_in)
end FlowControlled_m_flow;


Package with fan and pump models

within AixLib.Fluid;
package Movers "Package with fan and pump models"
  extends Modelica.Icons.Package;
end Movers;


Fan or pump with ideally controlled normalized speed y as input signal. This model describes a fan or pump with prescribed normalized speed.
The input connector provides the normalized rotational speed (between 0 and 1).
The head is computed based on the performance curve that take as an argument
the actual volume flow rate divided by the maximum flow rate and the relative
speed of the fan.

within AixLib.Fluid.Movers;
model SpeedControlled_y
  "Fan or pump with ideally controlled normalized speed y as input signal"
  extends AixLib.Fluid.Movers.BaseClasses.PartialFlowMachine(
    final preVar=AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed,
    final nominalValuesDefineDefaultPressureCurve=false,
    final computePowerUsingSimilarityLaws=true,
    final stageInputs(each final unit="1") = per.speeds,
    final constInput(final unit="1") =       per.constantSpeed,
    filter(
      final y_start=y_start,
      u(final unit="1"),
      y(final unit="1")),
    eff(
      per(final pressure = per.pressure,
          final etaHydMet = per.etaHydMet,
          final etaMotMet = per.etaMotMet),
      r_N(start=y_start)));

  parameter Real y_start(min=0, max=1, unit="1")=0 "Initial value of speed"

  Modelica.Blocks.Interfaces.RealInput y(
    unit="1")
 if inputType == AixLib.Fluid.Types.InputType.Continuous
    "Constant normalized rotational speed"

protected
  Modelica.Blocks.Math.Gain gain(final k=-1) "Pressure gain"
initial equation
  assert(per.havePressureCurve,
   "SpeedControlled_y requires to set the pressure vs. flow rate curve in record 'per'.");

equation
  connect(inputSwitch.u, y)
  connect(eff.dp, gain.u)
  connect(gain.y, preSou.dp_in)

  if use_inputFilter then
    connect(filter.y, eff.y_in)
  else
    connect(inputSwitch.y, eff.y_in)
  end if;
end SpeedControlled_y;


User's Guide

within AixLib.Fluid.Movers;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;

end UsersGuide;


Partial model with performance curves for fans or pumps. This is an interface that implements the functions to compute the head, power draw
and efficiency of fans and pumps.

within AixLib.Fluid.Movers.BaseClasses;
model FlowMachineInterface
  "Partial model with performance curves for fans or pumps"
  extends Modelica.Blocks.Icons.Block;

  import cha = AixLib.Fluid.Movers.BaseClasses.Characteristics;

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

  parameter AixLib.Fluid.Movers.Data.Generic per
    "Record with performance data"

  parameter AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable preVar=
    AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed "Type of prescribed variable";
  parameter Boolean computePowerUsingSimilarityLaws
    "= true, compute power exactly, using similarity laws. Otherwise approximate.";

  final parameter Modelica.Units.SI.VolumeFlowRate V_flow_nominal=per.pressure.V_flow[
      nOri] "Nominal volume flow rate, used for homotopy";

  parameter Modelica.Units.SI.Density rho_default
    "Fluid density at medium default state";

  final parameter Boolean haveVMax = (abs(per.pressure.dp[nOri]) < Modelica.Constants.eps)
    "Flag, true if user specified data that contain V_flow_max";

  final parameter Modelica.Units.SI.VolumeFlowRate V_flow_max=
    if per.V_flow_max>Modelica.Constants.eps
      then per.V_flow_max
    else
      V_flow_nominal
    "Maximum volume flow rate, used for smoothing";

  parameter Integer nOri(min=1) "Number of data points for pressure curve"

 // Normalized speed
  Modelica.Blocks.Interfaces.RealInput y_in(final unit="1") if preSpe
    "Prescribed mover speed"

  Modelica.Blocks.Interfaces.RealOutput y_out(
    final unit="1") "Mover speed (prescribed or computed)"

  Modelica.Blocks.Interfaces.RealInput m_flow(
    final quantity="MassFlowRate",
    final unit="kg/s") "Mass flow rate"

  Modelica.Blocks.Interfaces.RealInput rho(
    final quantity="Density",
    final unit="kg/m3",
    min=0.0) "Medium density"

  Modelica.Blocks.Interfaces.RealOutput V_flow(
    quantity="VolumeFlowRate",
    final unit="m3/s") "Volume flow rate"

  Modelica.Blocks.Interfaces.RealInput dp_in(
    quantity="PressureDifference",
    final unit="Pa") if prePre "Prescribed pressure increase"

  Modelica.Blocks.Interfaces.RealOutput dp(
    quantity="Pressure",
    final unit="Pa") if not prePre "Pressure increase (computed or prescribed)"

  Modelica.Blocks.Interfaces.RealOutput WFlo(
    quantity="Power",
    final unit="W") "Flow work"

  Modelica.Blocks.Interfaces.RealOutput WHyd(
    quantity="Power",
    final unit="W") "Hydraulic work (shaft work, brake horsepower)"

  Modelica.Blocks.Interfaces.RealOutput PEle(
    quantity="Power",
    final unit="W") "Electrical power consumed"

  Modelica.Blocks.Interfaces.RealOutput eta(
    final quantity="Efficiency",
    final unit="1",
    start = 0.49) "Overall efficiency"
  // A start value is given to suppress the following translation warning:
  //   "Some variables are iteration variables of the initialization problem:
  //   but they are not given any explicit start values. Zero will be used."

  Modelica.Blocks.Interfaces.RealOutput etaHyd(
    final quantity="Efficiency",
    final unit="1",
    start = 0.7) "Hydraulic efficiency"

  Modelica.Blocks.Interfaces.RealOutput etaMot(
    final quantity="Efficiency",
    final unit="1",
    start = 0.7) "Motor efficiency"

  // "Shaft rotational speed";
  Modelica.Blocks.Interfaces.RealOutput r_N(unit="1")
    "Ratio N_actual/N_nominal";
  Real r_V(start=1, unit="1") "Ratio V_flow/V_flow_max";

protected
  final parameter Boolean preSpe=
    preVar == AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed
    "True if speed is a prescribed variable of this block";
  final parameter Boolean prePre=
    preVar == AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.PressureDifference or
    preVar == AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.FlowRate
    "True if pressure head is a prescribed variable of this block";

  // Derivatives for cubic spline
  final parameter Real etaDer[size(per.efficiency.V_flow,1)]=
    if not per.etaHydMet==AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate
      then zeros(size(per.efficiency.V_flow,1))
    elseif (size(per.efficiency.V_flow, 1) == 1)
      then {0}
    else
      AixLib.Utilities.Math.Functions.splineDerivatives(
        x=per.efficiency.V_flow,
        y=per.efficiency.eta,
        ensureMonotonicity=AixLib.Utilities.Math.Functions.isMonotonic(
          x=per.efficiency.eta,
          strict=false))
    "Coefficients for cubic spline of total or hydraulic efficiency vs. volume flow rate";
  final parameter Real motDer[size(per.motorEfficiency.V_flow, 1)]=
    if not per.etaMotMet==AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate
      then zeros(size(per.motorEfficiency.V_flow,1))
    elseif (size(per.motorEfficiency.V_flow, 1) == 1)
      then {0}
    else
      AixLib.Utilities.Math.Functions.splineDerivatives(
        x=per.motorEfficiency.V_flow,
        y=per.motorEfficiency.eta,
        ensureMonotonicity=AixLib.Utilities.Math.Functions.isMonotonic(
          x=per.motorEfficiency.eta,
          strict=false))
    "Coefficients for cubic spline of motor efficiency vs. volume flow rate";
  final parameter Real motDer_yMot[size(per.motorEfficiency_yMot.y,1)]=
    if not per.etaMotMet==
      AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
      then zeros(size(per.motorEfficiency_yMot.y,1))
    elseif (size(per.motorEfficiency_yMot.y,1) == 1)
      then {0}
    else
      AixLib.Utilities.Math.Functions.splineDerivatives(
        x=per.motorEfficiency_yMot.y,
        y=per.motorEfficiency_yMot.eta,
        ensureMonotonicity=AixLib.Utilities.Math.Functions.isMonotonic(
          x=per.motorEfficiency_yMot.eta,
          strict=false))
    "Coefficients for cubic spline of motor efficiency vs. motor PLR";
  final parameter Real motDer_yMot_generic[9]=
    if per.etaMotMet==
      AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve
      or  (per.etaMotMet==
      AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided
           and per.haveWMot_nominal)
      then AixLib.Utilities.Math.Functions.splineDerivatives(
             x=per.motorEfficiency_yMot_generic.y,
             y=per.motorEfficiency_yMot_generic.eta,
             ensureMonotonicity=true)
    else zeros(9)
    "Coefficients for cubic spline of motor efficiency vs. motor PLR with generic curves";

  final parameter Modelica.Units.SI.PressureDifference dpMax(
    displayUnit="Pa")=
    per.dpMax "Maximum head";

  parameter Real delta = 0.05
    "Small value used to for regularization and to approximate an internal flow resistance of the fan";

  parameter Real kRes(min=0, unit="kg/(s.m4)") =  dpMax/V_flow_max*delta^2/10
    "Coefficient for internal pressure drop of the fan or pump";

  parameter Integer curve=
     if (haveVMax and haveDPMax) or (nOri == 2) then 1
     elseif haveVMax or haveDPMax then 2
     else 3
    "Flag, used to pick the right representation of the fan or pump's pressure curve";

  final parameter
    AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParametersInternal pCur1(
    final n = nOri,
    final V_flow = if (haveVMax and haveDPMax) or (nOri == 2) then
             {per.pressure.V_flow[i] for i in 1:nOri}
             else
             zeros(nOri),
    final dp = if (haveVMax and haveDPMax) or (nOri == 2) then
             {(per.pressure.dp[i] + per.pressure.V_flow[i] * kRes) for i in 1:nOri}
             else
             zeros(nOri))
    "Volume flow rate vs. total pressure rise with correction for fan or pump's resistance added";

  parameter
    AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParametersInternal pCur2(
    final n = nOri + 1,
    V_flow = if (haveVMax and haveDPMax) or (nOri == 2) then
                zeros(nOri + 1)
             elseif haveVMax then
              cat(1, {0}, {per.pressure.V_flow[i] for i in 1:nOri})
             elseif haveDPMax then
              cat(1, { per.pressure.V_flow[i] for i in 1:nOri}, {V_flow_max})
             else
              zeros(nOri + 1),
    dp = if (haveVMax and haveDPMax) or (nOri == 2) then
                zeros(nOri + 1)
             elseif haveVMax then
              cat(1, {dpMax}, {per.pressure.dp[i] + per.pressure.V_flow[i] * kRes for i in 1:nOri})
             elseif haveDPMax then
              cat(1, {per.pressure.dp[i] + per.pressure.V_flow[i] * kRes for i in 1:nOri}, {0})
             else
               zeros(nOri+1))
    "Volume flow rate vs. total pressure rise with correction for fan or pump's resistance added";
  parameter
    AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParametersInternal pCur3(
    final n = nOri + 2,
    V_flow = if (haveVMax and haveDPMax) or (nOri == 2) then
               zeros(nOri + 2)
             elseif haveVMax or haveDPMax then
               zeros(nOri + 2)
             else
               cat(1, {0}, {per.pressure.V_flow[i] for i in 1:nOri}, {V_flow_max}),
    dp =     if (haveVMax and haveDPMax) or (nOri == 2) then
               zeros(nOri + 2)
             elseif haveVMax or haveDPMax then
               zeros(nOri + 2)
             else
               cat(1, {dpMax}, {per.pressure.dp[i] + per.pressure.V_flow[i] * kRes for i in 1:nOri}, {0}))
    "Volume flow rate vs. total pressure rise with correction for fan or pump's resistance added";

  parameter Real preDer1[nOri](each fixed=false)
    "Derivatives of flow rate vs. pressure at the support points";
  parameter Real preDer2[nOri+1](each fixed=false)
    "Derivatives of flow rate vs. pressure at the support points";
  parameter Real preDer3[nOri+2](each fixed=false)
    "Derivatives of flow rate vs. pressure at the support points";
  parameter Real powDer[size(per.power.V_flow,1)]=
   if per.etaHydMet==
      AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate then
     AixLib.Utilities.Math.Functions.splineDerivatives(
                   x=per.power.V_flow,
                   y=per.power.P,
                   ensureMonotonicity=AixLib.Utilities.Math.Functions.isMonotonic(x=per.power.P,
                                                                                strict=false))
   else
     zeros(size(per.power.V_flow,1))
    "Coefficients for polynomial of power vs. flow rate";

  final parameter AixLib.Fluid.Movers.BaseClasses.Euler.powerWithDerivative powEu_internal=
    if (curve == 1) then
      AixLib.Fluid.Movers.BaseClasses.Euler.power(peak=per.peak,pressure=pCur1)
    elseif (curve == 2) then
      AixLib.Fluid.Movers.BaseClasses.Euler.power(peak=per.peak,pressure=pCur2)
    else
      AixLib.Fluid.Movers.BaseClasses.Euler.power(peak=per.peak,pressure=pCur3)
    "Intermediate parameter";
  final parameter AixLib.Fluid.Movers.BaseClasses.Characteristics.powerParameters powEu(
    V_flow = powEu_internal.V_flow,
    P = powEu_internal.P)
    "Power vs. volumetric flow rate computed from Euler number";
  final parameter Real powEuDer[:] = powEu_internal.d
    "Power derivative with respect to volumetric flow rate computed from Euler number";

  parameter Boolean haveMinimumDecrease=
    if nOri<2 then false
    else
      Modelica.Math.BooleanVectors.allTrue({(per.pressure.dp[i + 1] -
      per.pressure.dp[i])/(per.pressure.V_flow[i + 1] - per.pressure.V_flow[
      i]) < -kRes for i in 1:nOri - 1}) "Flag used for reporting";

  parameter Boolean haveDPMax = (abs(per.pressure.V_flow[1])  < Modelica.Constants.eps)
    "Flag, true if user specified data that contain dpMax";

  Modelica.Blocks.Interfaces.RealOutput dp_internal
    "If dp is prescribed, use dp_in and solve for r_N, otherwise compute dp using r_N";

  Modelica.Units.SI.Efficiency eta_internal
    "Either eta or etaHyd";

  Modelica.Units.SI.Power P_internal
    "Either PEle or WHyd";

  parameter Real deltaP = 1E-4 * V_flow_max * dpMax
    "Small value for regularisation of power";

  Real yMot(final min=0, final start=0.833)=
    if per.haveWMot_nominal
      then WHyd/per.WMot_nominal
    else 1
    "Motor part load ratio";

function getPerformanceDataAsString
  input AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParameters pressure
      "Performance data";
  input Real derivative[:](unit="kg/(s.m4)") "Derivative";
  input Integer minimumLength =  6 "Minimum width of result";
  input Integer significantDigits = 6 "Number of significant digits";
  output String str "String representation";
algorithm
  str :="";
  for i in 1:size(derivative, 1) loop
    str :=str + "  V_flow[" + String(i) + "]=" + String(
        pressure.V_flow[i],
        minimumLength=minimumLength,
        significantDigits=significantDigits) + "\t" + "dp[" + String(i) + "]=" +
        String(
        pressure.dp[i],
        minimumLength=minimumLength,
        significantDigits=significantDigits) + "\tResulting derivative dp/dV_flow = "
         + String(
        derivative[i],
        minimumLength=minimumLength,
        significantDigits=significantDigits) + "\n";
  end for;
end getPerformanceDataAsString;

function getArrayAsString
  input Real array[:] "Array to be printed";
  input String varName "Variable name";
  input Integer minimumLength =  6 "Minimum width of result";
  input Integer significantDigits = 6 "Number of significant digits";
  output String str "String representation";
algorithm
  str :="";
  for i in 1:size(array, 1) loop
    str :=str + "  " + varName + "[" + String(i) + "]=" + String(
        array[i],
        minimumLength=minimumLength,
        significantDigits=significantDigits) + "\n";
  end for;
end getArrayAsString;

initial equation
  // Check validity of data
  assert(nOri > 1, "Must have at least two data points for pressure.V_flow.");
  assert(AixLib.Utilities.Math.Functions.isMonotonic(x=per.pressure.V_flow, strict=true) and
  per.pressure.V_flow[1] > -Modelica.Constants.eps,
  "The fan pressure rise must be a strictly decreasing sequence with respect to the volume flow rate,
  with the first element for the fan pressure raise being non-zero.
The following performance data have been entered:
" + getArrayAsString(per.pressure.V_flow, "pressure.V_flow"));

  if not haveVMax then
    assert(nOri>=2,
      "When the maximum flow is not specified,
      at least two points are needed for the power curve.");
    if nOri>=2 then
      assert((per.pressure.V_flow[nOri]-per.pressure.V_flow[nOri-1])
           /((per.pressure.dp[nOri]-per.pressure.dp[nOri-1]))<0,
    "The last two pressure points for the fan or pump's performance curve must be decreasing.
Received
" + getArrayAsString(per.pressure.dp, "dp"));
    end if;
  end if;

  // Write warning if the volumetric flow rate versus pressure curve does not satisfy
  // the minimum decrease condition
  if (not haveMinimumDecrease) then
    Modelica.Utilities.Streams.print("
Warning:
========
It is recommended that the volume flow rate versus pressure relation
of the fan or pump satisfy the minimum decrease condition

        (per.pressure.dp[i+1]-per.pressure.dp[i])
d[i] = ------------------------------------------------- < " + String(-kRes) + "
       (per.pressure.V_flow[i+1]-per.pressure.V_flow[i])

 is
" + getArrayAsString({(per.pressure.dp[i+1]-per.pressure.dp[i])
        /(per.pressure.V_flow[i+1]-per.pressure.V_flow[i]) for i in 1:nOri-1}, "d") + "
Otherwise, a solution to the equations may not exist if the fan or pump's speed is reduced.
In this situation, the solver will fail due to non-convergence and
the simulation stops.");
  end if;

  // Correction for flow resistance of the fan or pump
  if curve == 1 then  // ----- Curve 1
    // V_flow_max and dpMax are provided by the user, or we only have two data points
    preDer1= AixLib.Utilities.Math.Functions.splineDerivatives(x=pCur1.V_flow,
      y=pCur1.dp);
    preDer2= zeros(nOri + 1);
    preDer3= zeros(nOri + 2);
  elseif curve == 2 then  // ----- Curve 2
    // V_flow_max or dpMax is provided by the user, but not both
    preDer1= zeros(nOri);
    preDer2= AixLib.Utilities.Math.Functions.splineDerivatives(x=pCur2.V_flow,
      y=pCur2.dp);
    preDer3= zeros(nOri + 2);
  else  // ----- Curve 3
    // Neither V_flow_max nor dpMax are provided by the user
    preDer1= zeros(nOri);
    preDer2= zeros(nOri + 1);
    preDer3= AixLib.Utilities.Math.Functions.splineDerivatives(x=pCur3.V_flow,
      y=pCur3.dp);
  end if;

  assert(not ((per.etaMotMet==
           AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio
           or  per.etaMotMet==
           AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve)
         and not per.haveWMot_nominal),
         "In " + getInstanceName() + ": etaMotMet is set to
         .Efficiency_MotorPartLoadRatio or .GenericCurve which requires
         the motor's rated power, but per.WMot_nominal is not assigned or
         cannot be estimated because no power curve is provided.");

  assert(max(per.power.P)<1E-6 or per.WMot_nominal>max(per.power.P)*0.99,
         "In " + getInstanceName() + ": The rated motor power provided in
         per.WMot_nominal is smaller than the maximum power provided in per.power.
         Use a larger value for per.WMot_nominal or leave it blank to allow the
         model to assume a default value.");

  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
  // Assign values of dp and r_N, depending on which variable exists and is prescribed
  connect(dp_internal,dp);
  connect(dp_internal,dp_in);
  connect(r_N, y_in);
  y_out=r_N;

  // Flow rate conversion
  V_flow = m_flow / rho;

  // Hydraulic equations
  r_V = V_flow/V_flow_max;

  // If the speed is not prescribed and we do not require exact power computations, we set r_N = 1.
  // Similarity laws are then not used, meaning the power computation is less accurate.
  // This however has the advantage that no non-linear algebraic loop is formed and
  // it allows an implementation when the pressure curve is unknown.
  if (computePowerUsingSimilarityLaws == false) and preVar <> AixLib.Fluid.Movers.BaseClasses.Types.PrescribedVariable.Speed then
    r_N=1;
  else
  // For the homotopy method, we approximate dp by an equation
  // that is linear in V_flow, and that goes linearly to 0 as r_N goes to 0.
  // The three branches below are identical, except that we pass either
  // pCur1, pCur2 or pCur3, and preDer1, preDer2 or preDer3
  if (curve == 1) then
    if homotopyInitialization then
       V_flow*kRes + dp_internal = homotopy(actual=cha.pressure(
                                                     V_flow=V_flow,
                                                     r_N=r_N,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer1,
                                                     per=pCur1),
                                           simplified=r_N * (cha.pressure(
                                                     V_flow=V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer1,
                                                     per=pCur1)
                               +(V_flow-V_flow_nominal) * (cha.pressure(
                                                     V_flow=(1+delta)*V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer1,
                                                     per=pCur1)
                                       -cha.pressure(V_flow=(1-delta)*V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer1,
                                                     per=pCur1))
                                                    /(2*delta*V_flow_nominal)));

    else
       V_flow*kRes + dp_internal= cha.pressure(V_flow=V_flow,
                                               r_N=r_N,
                                               dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                               d=preDer1,
                                               per=pCur1);
    end if;     // end of computation for this branch
   elseif (curve == 2) then
    if homotopyInitialization then
       V_flow*kRes + dp_internal = homotopy(actual=cha.pressure(
                                                     V_flow=V_flow,
                                                     r_N=r_N,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer2,
                                                     per=pCur2),
                                            simplified=r_N * (cha.pressure(
                                                     V_flow=V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer2,
                                                     per=pCur2)
                               +(V_flow-V_flow_nominal) * (cha.pressure(
                                                     V_flow=(1+delta)*V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer2,
                                                     per=pCur2)
                                       -cha.pressure(V_flow=(1-delta)*V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer2,
                                                     per=pCur2))
                                                    /(2*delta*V_flow_nominal)));

    else
       V_flow*kRes + dp_internal= cha.pressure(V_flow=V_flow,
                                                      r_N=r_N,
                                                      dpMax=dpMax,
                                                      V_flow_max=V_flow_max,
                                                      d=preDer2,
                                                      per=pCur2);
    end if;     // end of computation for this branch
  else
    if homotopyInitialization then
       V_flow*kRes + dp_internal = homotopy(actual=cha.pressure(
                                                     V_flow=V_flow,
                                                     r_N=r_N,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer3,
                                                     per=pCur3),
                          simplified=r_N * (cha.pressure(
                                                     V_flow=V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer3,
                                                     per=pCur3)
                               +(V_flow-V_flow_nominal)*
                                       (cha.pressure(V_flow=(1+delta)*V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer3,
                                                     per=pCur3)
                                       -cha.pressure(V_flow=(1-delta)*V_flow_nominal,
                                                     r_N=1,
                                                     dpMax=dpMax,
                                                     V_flow_max=V_flow_max,
                                                     d=preDer3,
                                                     per=pCur3))
                                                    /(2*delta*V_flow_nominal)));

    else
       V_flow*kRes + dp_internal= cha.pressure(V_flow=V_flow,
                                                      r_N=r_N,
                                                      dpMax=dpMax,
                                                      V_flow_max=V_flow_max,
                                                      d=preDer3,
                                                      per=pCur3);
    end if;
    // end of computation for this branch
  end if;
    // end of if/else choosing between exact/simplified power computation
  end if;

  // Power and efficiency
  WFlo = AixLib.Utilities.Math.Functions.smoothMax(
           x1=dp_internal*V_flow,
           x2=0,
           deltaX=deltaP/2);
  if per.powerOrEfficiencyIsHydraulic then
    eta = etaHyd * etaMot;
  else
    etaHyd = AixLib.Utilities.Math.Functions.smoothMin(
               x1=eta/etaMot, x2=1, deltaX=1E-3);
  end if;

  // Hydraulic efficiency etaHyd and hydraulic work WHyd
  //   or total efficiency eta and total electric power PEle
  //   depending on the information provided
  if per.powerOrEfficiencyIsHydraulic then
    P_internal=WHyd;
    eta_internal=etaHyd;
    PEle = WFlo / AixLib.Utilities.Math.Functions.smoothMax(
                    x1=eta, x2=1E-2, deltaX=1E-3);
  else
    P_internal=PEle;
    eta_internal=eta;
    WHyd = WFlo / AixLib.Utilities.Math.Functions.smoothMax(
                    x1=etaHyd, x2=1E-2, deltaX=1E-3);
  end if;
  if per.etaHydMet==
       AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate then
    if homotopyInitialization then
      P_internal = homotopy(actual=cha.power(per=per.power, V_flow=V_flow, r_N=r_N, d=powDer, delta=delta),
                      simplified=V_flow/V_flow_nominal*
                            cha.power(per=per.power, V_flow=V_flow_nominal, r_N=1, d=powDer, delta=delta));
    else
      P_internal = (rho/rho_default)*cha.power(per=per.power, V_flow=V_flow, r_N=r_N, d=powDer, delta=delta);
    end if;
    eta_internal = WFlo/AixLib.Utilities.Math.Functions.smoothMax(
                     x1=P_internal, x2=deltaP, deltaX=deltaP/2);
  elseif per.etaHydMet==
       AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber then
    if homotopyInitialization then
      P_internal = homotopy(actual=cha.power(per=powEu, V_flow=V_flow, r_N=r_N, d=powEuDer, delta=delta),
                      simplified=V_flow/V_flow_nominal*
                            cha.power(per=powEu, V_flow=V_flow_nominal, r_N=1, d=powEuDer, delta=delta));
    else
      P_internal = (rho/rho_default)*cha.power(per=powEu, V_flow=V_flow, r_N=r_N, d=powEuDer, delta=delta);
    end if;
    eta_internal = WFlo / AixLib.Utilities.Math.Functions.smoothMax(
                            x1=P_internal, x2=deltaP, deltaX=deltaP/2);
  elseif per.etaHydMet == AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate then
    if homotopyInitialization then
      eta_internal = homotopy(actual=cha.efficiency(per=per.efficiency,     V_flow=V_flow, d=etaDer, r_N=r_N, delta=delta),
                        simplified=cha.efficiency(per=per.efficiency, V_flow=V_flow_max,   d=etaDer, r_N=r_N, delta=delta));
    else
      eta_internal = cha.efficiency(per=per.efficiency, V_flow=V_flow, d=etaDer, r_N=r_N, delta=delta);
    end if;
    P_internal=WFlo/AixLib.Utilities.Math.Functions.smoothMax(
                      x1=eta_internal, x2=1E-2, deltaX=1E-3);
  else // Not provided
    if per.powerOrEfficiencyIsHydraulic then
      eta_internal=0.7;
    else
      eta_internal=0.49;
    end if;
    P_internal=WFlo/eta_internal;
  end if;

  // Motor efficiency etaMot
  if per.etaMotMet == AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate then
    if homotopyInitialization then
      etaMot = homotopy(actual=cha.efficiency(per=per.motorEfficiency,     V_flow=V_flow, d=motDer, r_N=r_N, delta=delta),
                        simplified=cha.efficiency(per=per.motorEfficiency, V_flow=V_flow_max,   d=motDer, r_N=r_N, delta=delta));
    else
      etaMot = cha.efficiency(per=per.motorEfficiency,     V_flow=V_flow, d=motDer, r_N=r_N, delta=delta);
    end if;
  elseif per.etaMotMet==
       AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_MotorPartLoadRatio then
    if homotopyInitialization then
      etaMot =homotopy(actual=cha.efficiency_yMot(
        per=per.motorEfficiency_yMot,
        y=yMot,
        d=motDer_yMot), simplified=cha.efficiency_yMot(
        per=per.motorEfficiency_yMot,
        y=1,
        d=motDer_yMot));
    else
      etaMot =cha.efficiency_yMot(
        per=per.motorEfficiency_yMot,
        y=yMot,
        d=motDer_yMot);
    end if;
  elseif per.etaMotMet==
       AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve then
      if homotopyInitialization then
        etaMot =homotopy(actual=cha.efficiency_yMot(
          per=per.motorEfficiency_yMot_generic,
          y=yMot,
          d=motDer_yMot_generic), simplified=cha.efficiency_yMot(
          per=per.motorEfficiency_yMot_generic,
          y=1,
          d=motDer_yMot_generic));
      else
        etaMot =cha.efficiency_yMot(
          per=per.motorEfficiency_yMot_generic,
          y=yMot,
          d=motDer_yMot_generic);
      end if;
  else // Not provided
    etaMot = 0.7;
  end if;
end FlowMachineInterface;


Base class for pressure and mass flow source with optional power input. Model of a fictitious pipe that is used as a base class
for a pressure source or to prescribe a mass flow rate.

within AixLib.Fluid.Movers.BaseClasses;
model IdealSource
  "Base class for pressure and mass flow source with optional power input"
  extends AixLib.Fluid.Interfaces.PartialTwoPortTransport(show_T=false);

  // Quantity to control
  parameter Boolean control_m_flow
    "if true, then the mass flow rate is equal to the value of m_flow_in"
  parameter Boolean control_dp = not control_m_flow
    "if true, then the head is equal to the value of dp_in"

  Modelica.Blocks.Interfaces.RealInput m_flow_in(unit="kg/s") if control_m_flow
    "Prescribed mass flow rate"
  Modelica.Blocks.Interfaces.RealInput dp_in(unit="Pa") if control_dp
    "Prescribed pressure difference port_a.p-port_b.p"

protected
  Modelica.Blocks.Interfaces.RealInput m_flow_internal(unit="kg/s")
    "Needed to connect to conditional connector";
  Modelica.Blocks.Interfaces.RealInput dp_internal(unit="Pa")
    "Needed to connect to conditional connector";

equation
  // Ideal control
  if control_m_flow then
    m_flow = m_flow_internal;
  else
    m_flow_internal = 0;
  end if;
  if control_dp then
    dp = dp_internal;
  else
    dp_internal = 0;
  end if;

  connect(dp_internal, dp_in);
  connect(m_flow_internal, m_flow_in);

  // Energy balance (no storage)
  port_a.h_outflow = if allowFlowReversal then inStream(port_b.h_outflow) else Medium.h_default;
  port_b.h_outflow = inStream(port_a.h_outflow);
end IdealSource;


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