Example 1 model with mixing volume

within AixLib.Fluid.Examples.Performance;
model Example1v2 "Example 1 model with mixing volume"
  extends AixLib.Fluid.Examples.Performance.BaseClasses.Example1;
  parameter Modelica.Units.SI.Time tau=10 "Time constant at nominal flow";

  Fluid.Delays.DelayFirstOrder[nRes.k] vol(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each allowFlowReversal=allowFlowReversal.k,
    each nPorts=2,
    each tau=tau,
    each energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Mixing volumes for adding states in enthalpy circuit"

equation
  for i in 1:nRes.k loop
    connect(vol[i].ports[1], res[i].port_b)
    connect(vol[i].ports[2], val.port_3)

  end for;
end Example1v2;


within AixLib.Fluid.Examples.Performance;
model Example2 "Example 2 model with series pressure components"
  extends Modelica.Icons.Example;

  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  Fluid.Movers.FlowControlled_dp pump_dp(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_inputFilter=false,
    allowFlowReversal=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nominalValuesDefineDefaultPressureCurve=true)
    "Pump model with unidirectional flow"
  Fluid.Sources.Boundary_pT bou(redeclare package Medium = Medium, nPorts=1)
    "Boundary for absolute pressure boundary condition"
  Modelica.Blocks.Sources.Pulse pulse(period=1) "Pulse input"

  FixedResistances.PressureDrop[nRes.k] res(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each from_dp=from_dp.k,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) for i in 1:nRes.k})
  Modelica.Blocks.Sources.BooleanConstant from_dp(k=true)
    "Block for easily changing parameter from_dp.k"
  Modelica.Blocks.Sources.IntegerConstant nRes(k=6)
    "Number of parallel branches"
equation
  connect(pump_dp.port_a, bou.ports[1])

  connect(pump_dp.dp_in, pulse.y)
  connect(res[1].port_a, pump_dp.port_b)
  for i in 1:nRes.k-1 loop
    connect(res[i].port_b, res[i+1].port_a)
  end for;

  connect(res[nRes.k].port_b, pump_dp.port_a)

simulateModel(\"AixLib.Fluid.Examples.Performance.Example2(from_dp.k=true)\", stopTime=10000, numberOfIntervals=10, method=\"dassl\", resultFile=\"Example2\");

createPlot(id=1, position={15, 10, 592, 421}, range={0.0, 10000.0, -0.01, 25}, autoscale=false, grid=true);

plotExpression(apply(Example2[end-1].CPUtime), false, \"from_dp=false\", 1);

plotExpression(apply(Example2[end].CPUtime), false, \"from_dp=true\", 1);

OutputCPUtime=cpuOld;

Evaluate=evaluateOld;

See Jorissen et al. (2015) for a discussion.

References

Filip Jorissen, Michael Wetter and Lieve Helsen.

Simulation speed analysis and improvements of Modelica
models for building energy simulation.

Submitted: 11th Modelica Conference. Paris, France. Sep. 2015.

", revisions="

January 22, 2016, by Michael Wetter:

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

July 14, 2015, by Michael Wetter:

Revised documentation.

May 20, 2015, by Filip Jorissen:

First implementation.

"),
    __Dymola_Commands(file="modelica://AixLib/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example2.mos"
        "Simulate and plot"),
   __Dymola_LockedEditing="Model from IBPSA");
end Example2;


within AixLib.Fluid.Examples.Performance;
model Example3
  "Example 3 model with mixed series/parallel pressure drop components"
  extends Modelica.Icons.Example;

  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  Fluid.Movers.FlowControlled_m_flow pump(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_inputFilter=false,
    allowFlowReversal=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nominalValuesDefineDefaultPressureCurve=true)
    "Pump model with unidirectional flow"
  Fluid.Sources.Boundary_pT bou(redeclare package Medium = Medium, nPorts=1)
    "Boundary for absolute pressure boundary condition"
  Modelica.Blocks.Sources.Pulse pulse(period=1) "Pulse input"

  FixedResistances.PressureDrop[nRes.k] res(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each from_dp=from_dp.k,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) + (if mergeDp.k then 0 else
        dp_nominal) for i in 1:nRes.k}) "First pressure drop component"
  Modelica.Blocks.Sources.BooleanConstant mergeDp(k=true)
    "Block for easily changing parameter mergeDp.k"
  Modelica.Blocks.Sources.BooleanConstant from_dp(k=true)
    "Block for easily changing parameter from_dp.k"
  FixedResistances.PressureDrop[nRes.k] res1(
    redeclare package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each from_dp=from_dp.k,
    each allowFlowReversal=false,
    each dp_nominal=if mergeDp.k then 0 else dp_nominal)
    "Second pressure drop component"
  Modelica.Blocks.Sources.IntegerConstant nRes(k=6)
    "Block for easily changing parameter nRes.k"
equation
  connect(pump.port_a, bou.ports[1])
  connect(pulse.y, pump.m_flow_in)
  connect(res.port_b, res1.port_a)
  for i in 1:nRes.k loop
    connect(res[i].port_a, pump.port_b)
    connect(res1[i].port_b, pump.port_a)
  end for;

simulateModel(\"AixLib.Fluid.Examples.Performance.Example3(from_dp.k=false, mergeDp.k=true, nRes.k=10)\", stopTime=1000, numberOfIntervals=10, method=\"dassl\", resultFile=\"Example3\");

simulateModel(\"AixLib.Fluid.Examples.Performance.Example3(from_dp.k=true, mergeDp.k=false, nRes.k=10)\", stopTime=1000, numberOfIntervals=10, method=\"dassl\", resultFile=\"Example3\");

simulateModel(\"AixLib.Fluid.Examples.Performance.Example3(from_dp.k=true, mergeDp.k=true, nRes.k=10)\", stopTime=1000, numberOfIntervals=10, method=\"dassl\", resultFile=\"Example3\");

createPlot(id=1, position={15, 10, 592, 421}, range={0.0, 1000.0, -0.01, 8}, autoscale=false, grid=true);

plotExpression(apply(Example3[end-3].CPUtime), false, \"from_dp=false, mergeDp=false\", 1);

plotExpression(apply(Example3[end-2].CPUtime), false, \"from_dp=false, mergeDp=true\", 1);

plotExpression(apply(Example3[end-1].CPUtime), false, \"from_dp=true, mergeDp=false\", 1);

plotExpression(apply(Example3[end].CPUtime), false, \"from_dp=true, mergeDp=true\", 1);

OutputCPUtime=cpuOld;

Evaluate=evaluateOld;

See Jorissen et al. (2015) for a discussion.

References

Filip Jorissen, Michael Wetter and Lieve Helsen.

Simulation speed analysis and improvements of Modelica
models for building energy simulation.

Submitted: 11th Modelica Conference. Paris, France. Sep. 2015.

"),
    __Dymola_Commands(file="modelica://AixLib/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example3.mos"
        "Simulate and plot"),
   __Dymola_LockedEditing="Model from IBPSA");
end Example3;


within AixLib.Fluid.Examples.Performance;
model Example4 "Example 4 model of simple condensing heat exchanger"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;
  parameter Boolean allowFlowReversal=false
    "= false to simplify equations, assuming, but not enforcing, no flow reversal";

  Modelica.Units.SI.MassFlowRate m_condens=min(0, -vol.ports[1].m_flow*(bou.X[1]
       - xSat.X[1])) "Water vapor mass flow rate";
  Fluid.MixingVolumes.MixingVolumeMoistAir vol(
    nPorts=2,
    ports(m_flow(min={0,-Modelica.Constants.inf})),
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    V=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=allowFlowReversal,
    prescribedHeatFlowRate=true) "Mixing volume for extracting moisture"
  Fluid.HeatExchangers.ConstantEffectiveness hex(
    redeclare package Medium1 = Medium,
    redeclare package Medium2 = Medium,
    m1_flow_nominal=1,
    m2_flow_nominal=1,
    dp1_nominal=0,
    dp2_nominal=0,
    allowFlowReversal1=allowFlowReversal,
    allowFlowReversal2=allowFlowReversal) "Heat exchanger"
  Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    p=Medium.p_default + 1000,
    use_p_in=false,
    use_T_in=true,
    X={0.02,0.98},
    nPorts=2) "Sink and source"
  Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    m_flow=1,
    T=273.15,
    nPorts=1) "Air source"
  Fluid.Sources.Boundary_pT sin(redeclare package Medium = Medium, nPorts=1)
    "Air sink"
  Modelica.Blocks.Sources.RealExpression mCond(y=m_condens)
  AixLib.Utilities.Psychrometrics.X_pTphi xSat(use_p_in=false)
    "Block for converting relative humidity into absolute humidity"
  Modelica.Blocks.Sources.Constant phiSat(k=1) "Humidity of 100%"
  Modelica.Blocks.Sources.Ramp Tin(
    duration=1,
    height=20,
    offset=293.15) "Inlet temperature ramp"
  FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    dp_nominal=100,
    allowFlowReversal=allowFlowReversal,
    from_dp=true) "Pressure drop component"

  Fluid.Sensors.TemperatureTwoPort senTem(
    redeclare package Medium = Medium,
    allowFlowReversal=false,
    m_flow_nominal=1,
    tau=0) "Temperature sensor"
equation
  connect(phiSat.y, xSat.phi)
  connect(mCond.y, vol.mWat_flow)
  connect(Tin.y, bou.T_in)
  connect(res.port_b, sin.ports[1])
  connect(senTem.port_b, vol.ports[1])
  connect(senTem.T, xSat.T)
  connect(vol.ports[2], res.port_a)
  connect(hex.port_a1, sou.ports[1])
  connect(hex.port_a2, bou.ports[1])
  connect(hex.port_b1, bou.ports[2])
  connect(hex.port_b2, senTem.port_a)
end Example4;


Example 5 model of Modelica code that is inefficiently compiled into C-code. This example illustrates the impact of
Modelica code formulations on the C-code.

within AixLib.Fluid.Examples.Performance;
model Example5
  "Example 5 model of Modelica code that is inefficiently compiled into C-code"
  extends Modelica.Icons.Example;
  parameter Boolean efficient = false

  parameter Real[3] a = 1:3;
  parameter Real b=sum(a);

  Real c;
protected
  constant Real k(final unit="1/s") = 1
    "Unit conversion to satisfy unit check";
initial equation
  c=0;

equation
  der(c) = sin(k*time)*(if efficient then b else sum(a));
F_[0] = helpvar[0]*(IF DP_[0] THEN W_[0] ELSE DP_[1]+DP_[2]+DP_[3]);

Efficient = false and Evaluate = true

helpvar[0] = sin(Time);
F_[0] = helpvar[0]*(DP_[0]+DP_[1]+DP_[2]);

Efficient = true and Evaluate = true

helpvar[0] = sin(Time);
F_[0] = helpvar[0]*W_[1];

The last option requires much less operations to be performed and is therefore more efficient.

See Jorissen et al. (2015) for a discussion.

References

Filip Jorissen, Michael Wetter and Lieve Helsen.

Simulation speed analysis and improvements of Modelica
models for building energy simulation.

Submitted: 11th Modelica Conference. Paris, France. Sep. 2015.

"), __Dymola_Commands(file="modelica://AixLib/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example5.mos"
        "Simulate and plot"),
   __Dymola_LockedEditing="Model from IBPSA");
end Example5;


Example 6 model of Modelica code that is inefficiently compiled into C-code. This example, together with

AixLib.Fluid.Examples.Performance.Example7,
illustrates the overhead
generated by divisions by parameters. See Jorissen et al. (2015) for a complementary discussion.

within AixLib.Fluid.Examples.Performance;
model Example6
  "Example 6 model of Modelica code that is inefficiently compiled into C-code"
  extends Modelica.Icons.Example;
  parameter Integer nCapacitors = 500;
  parameter Real R = 0.001;
   //
  parameter Real C = 1000;
   //

  Real[nCapacitors] T;
  Real[nCapacitors+1] Q_flow;

protected
  constant Real k(final unit="1/s") = 1
    "Unit conversion to satisfy unit check";
initial equation
  T = fill(273.15,nCapacitors);
equation
  Q_flow[1]=((273.15+sin(k*time))-T[1])/R;
  der(T[1])=(Q_flow[1]-Q_flow[2])/C;
  for i in 2:nCapacitors loop
    Q_flow[i] = (T[i-1] - T[i])/R;
    der(T[i])=(Q_flow[i]-Q_flow[i+1])/C;
  end for;
  Q_flow[nCapacitors+1]=0; //adiabatic
simulateModel(\"AixLib.Fluid.Examples.PerformanceExamples.Example7\", stopTime=100, numberOfIntervals=1, method=\"Rkfix4\", fixedstepsize=0.001, resultFile=\"Example7\");

Comparing the CPU times indicates a speed improvement of 56%.
This difference almost disappears when adding
F_[0] = divmacro(W_[1]-W_[2],\"Q_flow[1]-Q_flow[2]\",DP_[1],\"C\");

This suggests that the parameter division needs to be handled during
each function evaluation, probably causing the increased overhead.

The following command allows comparing the CPU times objectively.

simulateModel(\"AixLib.Fluid.Examples.Performance.Example6\", stopTime=100, numberOfIntervals=1, method=\"Euler\", fixedstepsize=0.001, resultFile=\"Example6\");

See Jorissen et al. (2015) for a discussion.

References

Filip Jorissen, Michael Wetter and Lieve Helsen.

Simulation speed analysis and improvements of Modelica
models for building energy simulation.

Submitted: 11th Modelica Conference. Paris, France. Sep. 2015.

"),
    __Dymola_Commands(file="modelica://AixLib/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example6.mos"
        "Simulate and plot"),
   __Dymola_LockedEditing="Model from IBPSA");
end Example6;


Example 7 model of Modelica code that is more efficiently compiled into C-code. See

AixLib.Fluid.Examples.Performance.Example6 for the documentation.

within AixLib.Fluid.Examples.Performance;
model Example7
  "Example 7 model of Modelica code that is more efficiently compiled into C-code"
  extends Modelica.Icons.Example;
  parameter Integer nTem = 500;
  parameter Real R = 0.001;
  parameter Real C = 1000;
  parameter Real tauInv = 1/(R*C);

  Real[nTem] T;

protected
  constant Real k(final unit="1/s") = 1
    "Unit conversion to satisfy unit check";

initial equation
  T = fill(273.15, nTem);
equation
  der(T[1])= ((273.15+sin(k*time))-2*T[1] + T[2])*tauInv;
  for i in 2:nTem-1 loop
    der(T[i])=(T[i+1]+T[i-1]-2*T[i])*tauInv;
  end for;
  der(T[nTem])= (T[nTem-1]-T[nTem])*tauInv;
end Example7;


Common subexpression elimination example. This is a very simple example demonstrating common subexpression elimination.
The Dymola generated C-code of this model is:

within AixLib.Fluid.Examples.Performance;
model Example8 "Common subexpression elimination example"
  extends Modelica.Icons.Example;

  Real a = sin(k*time+1);
  Real b = sin(k*time+1);

protected
  constant Real k(final unit="1/s") = 1
    "Unit conversion to satisfy unit check";
W_[1] = W_[0];
</pre>
<p>
Hence, the sine and addition are evaluated once only, which is more efficient.
</p>
</html>"),
    __Dymola_Commands(file="modelica://AixLib/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example8.mos"
        "Simulate and plot"),
   __Dymola_LockedEditing="Model from IBPSA");
end Example8;


within AixLib.Fluid.Examples;
package Performance "Package of examples that demonstrate computation speed performance"
extends Modelica.Icons.ExamplesPackage;
end Performance;


Example 1 partial model

within AixLib.Fluid.Examples.Performance.BaseClasses;
partial model Example1 "Example 1 partial model"
  extends Modelica.Icons.Example;

  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;
  parameter Real m_flow_nominal=0.1 "Gain value multiplied with input signal";

  AixLib.Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Boundary for pressure boundary condition"
  Fluid.Movers.FlowControlled_m_flow pump(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_inputFilter=false,
    allowFlowReversal=allowFlowReversal.k,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nominalValuesDefineDefaultPressureCurve=true)
    "Pump model with unidirectional flow"
  AixLib.Fluid.HeatExchangers.Heater_T hea(
    redeclare package Medium = Medium,
    dp_nominal=1000,
    QMax_flow=1000,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    allowFlowReversal=allowFlowReversal.k) "Heater"
  Modelica.Blocks.Sources.Pulse pulse(period=1000) "Pulse input"
  Modelica.Blocks.Math.Gain gain(k=m_flow_nominal) "Gain for m_flow_nominal"
  AixLib.Fluid.Actuators.Valves.ThreeWayLinear val(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=1000,
    l={0.002,0.002},
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_inputFilter=false,
    portFlowDirection_1=if allowFlowReversal.k then Modelica.Fluid.Types.PortFlowDirection.Bidirectional
         else Modelica.Fluid.Types.PortFlowDirection.Entering,
    portFlowDirection_2=if allowFlowReversal.k then Modelica.Fluid.Types.PortFlowDirection.Bidirectional
         else Modelica.Fluid.Types.PortFlowDirection.Leaving,
    portFlowDirection_3=if allowFlowReversal.k then Modelica.Fluid.Types.PortFlowDirection.Bidirectional
         else Modelica.Fluid.Types.PortFlowDirection.Entering)
    "Three way valve with constant input"
  Modelica.Blocks.Sources.Constant const(k=0.5) "Constant valve set point"
  Modelica.Blocks.Sources.BooleanConstant allowFlowReversal(k=true)
    "Block for setting allowFlowReversal in components"
  AixLib.Fluid.FixedResistances.PressureDrop[nRes.k] res(
    redeclare package Medium = Medium,
    each allowFlowReversal=allowFlowReversal.k,
    each m_flow_nominal=m_flow_nominal,
    each dp_nominal=1000,
    each from_dp=from_dp.k) "Fluid resistance for splitting flow"
  Modelica.Blocks.Sources.IntegerConstant nRes(k=20)
    "Number of parallel branches"
  Modelica.Blocks.Sources.BooleanConstant from_dp(k=false)
    "Block for setting from_dp in res"
equation
  connect(bou.ports[1],hea. port_a)
  connect(pulse.y,hea. TSet)
  connect(pump.m_flow_in, gain.y)
  connect(gain.u,pulse. y)
  connect(hea.port_b,val. port_1)
  connect(val.port_2, pump.port_a)
  connect(const.y,val. y)
  connect(val.port_3,hea. port_a)
  for i in 1:nRes.k loop
    connect(pump.port_b, res[i].port_a)
  end for;
end Example1;


within AixLib.Fluid.Examples.Performance;
package PressureDrop "Package with various configurations of pressure drop models to analyze symbolic processing"
  extends Modelica.Icons.ExamplesPackage;
end PressureDrop;


within AixLib.Fluid.Examples.Performance.PressureDrop;
model ParallelDp
  "Parallel connection with prescribed pressure difference and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Integer nRes(min=2) = 10 "Number of resistances";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_p_in=true)
      "Pressure boundary condition"
  Modelica.Blocks.Sources.Pulse pulse_p(
    period=1,
    offset=Medium.p_default,
    amplitude=dp_nominal) "Pulse input for pressure"
  FixedResistances.PressureDrop[nRes] resParallel(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) for i in 1:nRes},
    each from_dp=false) "Parallel pressure drop components"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary condition"
equation
  connect(pulse_p.y,sou. p_in)
  for i in 1:nRes-1 loop
    connect(resParallel[i].port_a, resParallel[i + 1].port_a)
    connect(resParallel[i].port_b, resParallel[i + 1].port_b)
  end for;
  connect(resParallel[nRes].port_b, sin.ports[1])
  connect(resParallel[1].port_a, sou.ports[1])
end ParallelDp;


Parallel connection with prescribed pressure difference and optimised parameters. Example model that demonstrates how translation statistics
depend on the type of boundary conditions,
the parallel or series configuration of the components
and the value of parameter from_dp.

within AixLib.Fluid.Examples.Performance.PressureDrop;
model ParallelDpOptimised
  "Parallel connection with prescribed pressure difference and optimised parameters"
  extends ParallelDp(resParallel(each from_dp=true));
end ParallelDpOptimised;


within AixLib.Fluid.Examples.Performance.PressureDrop;
model ParallelFlow
  "Parallel connection with prescribed flow rate and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Integer nRes(min=2) = 10 "Number of resistances";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=true) "Flow rate boundary condition"
  Modelica.Blocks.Sources.Pulse pulse_m_flow(
    amplitude=1,
    period=1,
    offset=0) "Pulse input for flow rate"
  FixedResistances.PressureDrop[nRes] resParallel(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) for i in 1:nRes},
    each from_dp=false) "Parallel pressure drop components"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary condition"
equation
  for i in 1:nRes-1 loop
    connect(resParallel[i].port_a, resParallel[i + 1].port_a)
    connect(resParallel[i].port_b, resParallel[i + 1].port_b)
  end for;
  connect(resParallel[nRes].port_b, sin.ports[1])
  connect(resParallel[1].port_a, sou.ports[1])

  connect(pulse_m_flow.y, sou.m_flow_in)
end ParallelFlow;


Parallel connection with prescribed flow rate and optimised parameters. Example model that demonstrates how translation statistics
depend on the type of boundary conditions,
the parallel or series configuration of the components
and the value of parameter from_dp.

within AixLib.Fluid.Examples.Performance.PressureDrop;
model ParallelFlowOptimised
  "Parallel connection with prescribed flow rate and optimised parameters"
  extends ParallelFlow(
    resParallel(each from_dp=true));
end ParallelFlowOptimised;


within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesDp
  "Series connection with prescribed pressure difference and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Integer nRes(min=2) = 10 "Number of resistances";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_p_in=true)
      "Pressure boundary condition"
  Modelica.Blocks.Sources.Pulse pulse_p(
    period=1,
    offset=Medium.p_default,
    amplitude=dp_nominal) "Pulse input for pressure"
  FixedResistances.PressureDrop[nRes] resSeries(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) for i in 1:nRes},
    each from_dp=true) "Series pressure drop components"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary condition"
equation
  connect(pulse_p.y,sou. p_in)
  for i in 1:nRes-1 loop
    connect(resSeries[i].port_b, resSeries[i + 1].port_a)
  end for;
  connect(resSeries[nRes].port_b, sin.ports[1])
  connect(resSeries[1].port_a, sou.ports[1])
end SeriesDp;


Series connection with prescribed pressure difference and optimised parameters. Example model that demonstrates how translation statistics
depend on the type of boundary conditions,
the parallel or series configuration of the components
and the value of parameter from_dp.

within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesDpOptimised
  "Series connection with prescribed pressure difference and optimised parameters"
  extends SeriesDp(
    resSeries(each from_dp=false));
end SeriesDpOptimised;


within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesFlow
  "Series connection with prescribed flow rate and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Integer nRes(min=2) = 10 "Number of resistances";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=true) "Flow rate boundary condition"
  Modelica.Blocks.Sources.Pulse pulse_p(
    amplitude=1,
    period=1,
    offset=0) "Pulse input for flow rate"
  FixedResistances.PressureDrop[nRes] resSeries(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) for i in 1:nRes},
    each from_dp=true) "Series pressure drop components"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary condition"
equation
  for i in 1:nRes-1 loop
    connect(resSeries[i].port_b, resSeries[i + 1].port_a)
  end for;
  connect(resSeries[nRes].port_b, sin.ports[1])
  connect(resSeries[1].port_a, sou.ports[1])

  connect(sou.m_flow_in, pulse_p.y)
end SeriesFlow;


Series connection with prescribed flow and optimised parameters. Example model that demonstrates how translation statistics
depend on the type of boundary conditions,
the parallel or series configuration of the components
and the value of parameter from_dp.

within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesFlowOptimised
  "Series connection with prescribed flow and optimised parameters"
  extends SeriesFlow(resSeries(each from_dp=false));
end SeriesFlowOptimised;


within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesParallelDp
  "Combined series and parallel connection with prescribed pressure difference and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Integer nRes(min=2) = 10 "Number of resistances";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_p_in=true) "Pressure boundary condition"
  Modelica.Blocks.Sources.Pulse pulse_m_flow(
    period=1,
    offset=Medium.p_default,
    amplitude=dp_nominal)
              "Pulse input for flow rate"
  FixedResistances.PressureDrop[nRes] resParallel(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) for i in 1:nRes},
    each from_dp=false) "Parallel pressure drop components"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium, nPorts=1)
      "Pressure boundary condition"
  FixedResistances.PressureDrop res2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    from_dp=false)
                  "Series pressure drop component"
equation
  for i in 1:nRes-1 loop
    connect(resParallel[i].port_a, resParallel[i + 1].port_a)
    connect(resParallel[i].port_b, resParallel[i + 1].port_b)
  end for;
  connect(resParallel[1].port_a, sou.ports[1])

  connect(resParallel[nRes].port_b, res2.port_a)
  connect(res2.port_b, sin.ports[1])
  connect(pulse_m_flow.y, sou.p_in)
end SeriesParallelDp;


Combined series and parallel connection with prescribed pressure difference and non-optimised parameters. Example model that demonstrates how translation statistics
depend on the type of boundary conditions,
the parallel or series configuration of the components
and the value of parameter from_dp.

within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesParallelDpOptimised
  "Combined series and parallel connection with prescribed pressure difference and non-optimised parameters"
  extends SeriesParallelDp(
    res2(from_dp=true),
    resParallel(each from_dp=true));
end SeriesParallelDpOptimised;


within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesParallelFlow
  "Combined series and parallel connection with prescribed flow rate and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Integer nRes(min=2) = 10 "Number of resistances";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1
    "Pressure drop at nominal mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";

  Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=true) "Flow rate boundary condition"
  Modelica.Blocks.Sources.Pulse pulse_m_flow(
    amplitude=1,
    period=1,
    offset=0) "Pulse input for flow rate"
  FixedResistances.PressureDrop[nRes] resParallel(
    redeclare each package Medium = Medium,
    each m_flow_nominal=m_flow_nominal,
    each allowFlowReversal=false,
    dp_nominal={dp_nominal*(1 + mod(i, 3)) for i in 1:nRes},
    each from_dp=false) "Parallel pressure drop components"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium, nPorts=1)
      "Pressure boundary condition"
  FixedResistances.PressureDrop res2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    from_dp=true) "Series pressure drop component"
equation
  for i in 1:nRes-1 loop
    connect(resParallel[i].port_a, resParallel[i + 1].port_a)
    connect(resParallel[i].port_b, resParallel[i + 1].port_b)
  end for;
  connect(resParallel[1].port_a, sou.ports[1])

  connect(pulse_m_flow.y, sou.m_flow_in)
  connect(resParallel[nRes].port_b, res2.port_a)
  connect(res2.port_b, sin.ports[1])
end SeriesParallelFlow;


Combined series and parallel connection with prescribed flow rate and non-optimised parameters. Example model that demonstrates how translation statistics
depend on the type of boundary conditions,
the parallel or series configuration of the components
and the value of parameter from_dp.

within AixLib.Fluid.Examples.Performance.PressureDrop;
model SeriesParallelFlowOptimised
  "Combined series and parallel connection with prescribed flow rate and non-optimised parameters"
  extends SeriesParallelFlow(
    res2(from_dp=false),
    resParallel(each from_dp=true));
end SeriesParallelFlowOptimised;


Check valve that avoids flow reversal. Implementation of a hydraulic check valve.
Note that the small reverse flows can still occur with this model.

within AixLib.Fluid.FixedResistances;
model CheckValve "Check valve that avoids flow reversal"
  extends AixLib.Fluid.BaseClasses.PartialResistance(
    dp(nominal=2000),
    final dp_nominal=dpValve_nominal + dpFixed_nominal,
    final m_flow_turbulent=deltaM*abs(m_flow_nominal),
    final from_dp=true,
    final linearized=false,
    allowFlowReversal=true);
  extends AixLib.Fluid.Actuators.BaseClasses.ValveParameters(
    rhoStd=Medium.density_pTX(101325, 273.15 + 4, Medium.X_default));

  parameter Modelica.Units.SI.PressureDifference dpFixed_nominal(
    displayUnit="Pa",
    min=0) = 0 "Pressure drop of pipe and other resistances that are in series"

  parameter Modelica.Units.SI.PressureDifference dpValve_closing=
      dpValve_nominal/2 "Pressure drop when the check valve starts to close"

  parameter Real l(min=1e-10, max=1)=0.001 "Valve leakage, l=Kv(y=0)/Kv(y=1)";

  parameter Real kFixed(unit="", min=0)=
    if dpFixed_nominal > Modelica.Constants.eps then
      m_flow_nominal/sqrt(dpFixed_nominal)
    else 0
    "Flow coefficient of fixed resistance that may be in series with valve,
    k=m_flow/sqrt(dp), with unit=(kg.m)^(1/2).";

  Real k(min=Modelica.Constants.small)
    "Flow coefficient of valve and pipe in series in allowed/forward direction,
    k=m_flow/sqrt(dp), with unit=(kg.m)^(1/2).";

protected
  Real a
    "Scaled pressure variable";
  Real cv
    "Twice differentiable Heaviside check valve characteristic";
  Real kCv
    "Smoothed restriction characteristic";

initial equation
  assert(dpFixed_nominal > -Modelica.Constants.eps,
    "In " + getInstanceName() + ": We require dpFixed_nominal >= 0.
    Received dpFixed_nominal = " + String(dpFixed_nominal) + " Pa.");
  assert(l > -Modelica.Constants.eps,
    "In " + getInstanceName() + ": We require l >= 0. Received l = " + String(l));
equation
  a = dp/dpValve_closing;
  cv = smooth(2, max(0, min(1, a^3*(10+a*(-15+6*a)))));
  kCv = Kv_SI*(cv*(1-l) + l);

  if (dpFixed_nominal > Modelica.Constants.eps) then
    k = sqrt(1/(1/kFixed^2 + 1/kCv^2));
  else
    k = kCv;
  end if;

  if homotopyInitialization then
    m_flow = homotopy(
      actual=AixLib.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
            dp = dp,
            k = k,
            m_flow_turbulent = m_flow_turbulent),
      simplified = m_flow_nominal_pos*dp/dp_nominal_pos);
  else
    m_flow = AixLib.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
        dp = dp,
        k = k,
        m_flow_turbulent = m_flow_turbulent);
  end if;
end CheckValve;


within AixLib.Fluid.FixedResistances;
model GenericPipe
  "Pipe model that includes several selectable pipe models"

  extends AixLib.Fluid.Interfaces.PartialTwoPort;

  parameter String pipeModel="SimplePipe"

  parameter AixLib.DataBase.Pipes.PipeBaseDataDefinition parameterPipe=
      AixLib.DataBase.Pipes.Copper.Copper_6x1() "Pipe type"

  parameter Modelica.Units.SI.Length length(min=0) "Pipe length";

  parameter Boolean withInsulation=true "Pipe with or without insulation"

  parameter AixLib.DataBase.Pipes.InsulationBaseDataDefinition parameterIso=
      AixLib.DataBase.Pipes.Insulation.Iso50pc() "Insulation Type"

  parameter Boolean withConvection=true "convectional heat transfer"

  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hCon=4
    "Convection heat transfer coeffient"

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

  // Advanced
  parameter Real fac=1
    "Factor to take into account flow resistance of bends etc., fac=dp_nominal/dpStraightPipe_nominal"
  parameter Real ReC=2300
    "Reynolds number where transition to turbulent starts"
  parameter Modelica.Units.SI.Height roughness=2.5e-5
    "Average height of surface asperities (default: smooth steel pipe)"
  parameter Integer nNodes=3 "Spatial segmentation for SimplePipe"

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

  // Initialization
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Initialization temperature at pipe inlet"

  PlugFlowPipe plugFlowPipe(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final dh=parameterPipe.d_i,
    final ReC=ReC,
    final roughness=roughness,
    final length=length,
    final m_flow_nominal=m_flow_nominal,
    final dIns=(parameterPipe.d_o - parameterPipe.d_i)/2,
    final kIns=parameterPipe.lambda,
    final cPip=parameterPipe.c,
    final rhoPip=parameterPipe.d,
    final thickness=(parameterPipe.d_o - parameterPipe.d_i)/2,
    final T_start_in=T_start,
    final T_start_out=T_start,
    final fac=fac) if pipeModel == "PlugFlowPipe"
  SimplePipe simplePipe(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final nNodes=nNodes,
    final dh=parameterPipe.d_i,
    final withHeattransfer=true,
    final length=length,
    final fac=fac,
    final ReC=ReC,
    final roughness=roughness,
    final lambda=parameterPipe.lambda,
    final c=parameterPipe.c,
    final rho=parameterPipe.d,
    final thickness=(parameterPipe.d_o - parameterPipe.d_i)/2,
    final T_start=T_start,
    final m_flow_nominal=m_flow_nominal,
    final energyDynamics=energyDynamics,
    final massDynamics=massDynamics) if pipeModel == "SimplePipe"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector thermalCollector(
    final m=nNodes)
    if pipeModel == "SimplePipe"

    Utilities.HeatTransfer.CylindricHeatTransfer Insulation(
    final energyDynamics=energyDynamics,
    final c=parameterIso.c,
    final d_out=parameterPipe.d_o*parameterIso.factor*2 + parameterPipe.d_o,
    final d_in=parameterPipe.d_o,
    final length=length/nNodes,
    final lambda=parameterIso.lambda,
    final T0=T_start,
    final rho=parameterIso.d,
    final nParallel=1) if withInsulation

  Utilities.HeatTransfer.HeatConv heatConv(
    final hCon=hCon,
    final A=Modelica.Constants.pi*parameterPipe.d_o*length) if withConvection
    "Convection from insulation"

  Modelica.Thermal.HeatTransfer.Components.ThermalCollector thermalPassthroughInsulation(final m=1)
    if not withInsulation
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector thermalPassthroughConvection(final m=1)
     if not withConvection

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort "Heat port at outside surface of pipe"

equation
  connect(simplePipe.heatPorts, thermalCollector.port_a)
  connect(thermalCollector.port_b, Insulation.port_a)
  connect(plugFlowPipe.heatPort, Insulation.port_a)
  connect(plugFlowPipe.port_a, port_a)
  connect(plugFlowPipe.port_b, port_b)
  connect(simplePipe.port_a, port_a)
  connect(simplePipe.port_b, port_b)
  connect(heatConv.port_a, heatPort)
  connect(Insulation.port_b, heatConv.port_b)
  connect(plugFlowPipe.heatPort, thermalPassthroughInsulation.port_a[1])
  connect(thermalCollector.port_b, thermalPassthroughInsulation.port_a[1])
  connect(thermalPassthroughInsulation.port_b, thermalPassthroughConvection.port_a[1])
  connect(thermalPassthroughConvection.port_b, heatPort)
  connect(thermalPassthroughInsulation.port_b, heatConv.port_b)
  connect(Insulation.port_b, thermalPassthroughConvection.port_a[1])
end GenericPipe;


within AixLib.Fluid.FixedResistances;
model HydraulicDiameter "Fixed flow resistance with hydraulic diameter and m_flow as parameter"
  extends AixLib.Fluid.FixedResistances.PressureDrop(
    final deltaM =  eta_default*dh/4*Modelica.Constants.pi*ReC/m_flow_nominal_pos,
    final dp_nominal=fac*dpStraightPipe_nominal);

  parameter Modelica.Units.SI.Length dh=sqrt(4*m_flow_nominal/rho_default/
      v_nominal/Modelica.Constants.pi)
    "Hydraulic diameter (assuming a round cross section area)";

  parameter Modelica.Units.SI.Length length "Length of the pipe";

  parameter Real ReC(min=0)=4000
    "Reynolds number where transition to turbulence starts";

  parameter Modelica.Units.SI.Velocity v_nominal=if rho_default < 500 then 1.5
       else 0.15
    "Velocity at m_flow_nominal (used to compute default value for hydraulic diameter dh)"

  parameter Modelica.Units.SI.Length roughness(min=0) = 2.5e-5
    "Absolute roughness of pipe, with a default for a smooth steel pipe (dummy if use_roughness = false)";

  parameter Real fac(min=1) = 2
    "Factor to take into account resistance of bends etc., fac=dp_nominal/dpStraightPipe_nominal";

  final parameter Modelica.Units.SI.PressureDifference dpStraightPipe_nominal(
      displayUnit="Pa") =
    Modelica.Fluid.Pipes.BaseClasses.WallFriction.Detailed.pressureLoss_m_flow(
    m_flow=m_flow_nominal,
    rho_a=rho_default,
    rho_b=rho_default,
    mu_a=mu_default,
    mu_b=mu_default,
    length=length,
    diameter=dh,
    roughness=roughness,
    m_flow_small=m_flow_small)
    "Pressure loss of a straight pipe at m_flow_nominal";

  Modelica.Units.SI.Velocity v=m_flow/(rho_default*ARound)
    "Flow velocity (assuming a round cross section area)";

protected
  parameter Modelica.Units.SI.Area ARound=dh^2*Modelica.Constants.pi/4
    "Cross sectional area (assuming a round cross section area)";

  parameter Medium.ThermodynamicState state_default=
    Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default[1:Medium.nXi]) "Default state";

  parameter Modelica.Units.SI.Density rho_default=Medium.density(state_default)
    "Density at nominal condition";

  parameter Modelica.Units.SI.DynamicViscosity mu_default=
      Medium.dynamicViscosity(state_default)
    "Dynamic viscosity at nominal condition";
end HydraulicDiameter;


Simple model for a hydraulic resistance using a pressure loss factor. Overview

within AixLib.Fluid.FixedResistances;
model HydraulicResistance
  "Simple model for a hydraulic resistance using a pressure loss factor"
  extends AixLib.Fluid.BaseClasses.PartialResistance(
    final m_flow(start=m_flow_start),
    final dp(start=dp_start),
    final dp_nominal=8*zeta*m_flow_nominal*m_flow_nominal/
      (rho_default*Modelica.Constants.pi*Modelica.Constants.pi*
      diameter*diameter*diameter*diameter),
    final m_flow_turbulent=100*m_flow_nominal);
      /* No usage of m_flow_turbulent since zeta approach does not distinguish
         between laminar and turbulent flow. */

  parameter Real zeta(min=0, unit="")
    "Pressure loss factor for flow of port_a -> port_b";
  parameter Modelica.Units.SI.Diameter diameter "Diameter of component";
  parameter Modelica.Units.SI.PressureDifference dp_start(displayUnit="Pa") = 0
    "Guess value of dp = port_a.p - port_b.p"
  parameter Medium.MassFlowRate m_flow_start=0
    "Guess value of m_flow = port_a.m_flow"

protected
  final parameter Real k(min=0, unit="")=
    Modelica.Fluid.Fittings.BaseClasses.lossConstant_D_zeta(
      D=diameter,
      zeta=zeta)
    "Calculate loss coefficient based on diameter and zeta";
  parameter Modelica.Units.SI.PressureDifference dp_small=1E-4*abs(dp_nominal)
    "Small pressure difference for regularization of zero pressure difference";
  parameter Modelica.Units.SI.Density rho_default=Medium.density_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default[1:Medium.nXi]) "Density at nominal condition";

  Modelica.Units.SI.Density rho_a "Density of the fluid at port_a";
  Modelica.Units.SI.Density rho_b "Density of the fluid at port_b";

initial equation
  assert(m_flow_nominal_pos > 0,
    "m_flow_nominal_pos must be non-zero. Check parameters.");

equation

  rho_a = Medium.density(Medium.setState_phX(
    p=port_a.p,
    h=inStream(port_a.h_outflow),
    X=inStream(port_a.Xi_outflow)));

  rho_b = Medium.density(Medium.setState_phX(
    p=port_b.p,
    h=inStream(port_b.h_outflow),
    X=inStream(port_b.Xi_outflow)));

  // Pressure drop calculation
  if linearized then
    m_flow*m_flow_nominal_pos = k*k*dp;
  else
    if homotopyInitialization then
      if from_dp then
        m_flow = homotopy(actual=Modelica.Fluid.Utilities.regRoot2(
          dp,
          dp_small,
          rho_a/k,
          rho_b/k), simplified=m_flow_nominal_pos*dp/dp_nominal_pos);
      else
        dp = homotopy(actual=Modelica.Fluid.Utilities.regSquare2(
          m_flow,
          m_flow_small,
          k/rho_a,
          k/rho_b), simplified=dp_nominal_pos*m_flow/m_flow_nominal_pos);
      end if; // from_dp
    else // do not use homotopy
      if from_dp then
        m_flow = Modelica.Fluid.Utilities.regRoot2(
          dp,
          dp_small,
          rho_a/k,
          rho_b/k);
      else
        dp = Modelica.Fluid.Utilities.regSquare2(
          m_flow,
          m_flow_small,
          k/rho_a,
          k/rho_b);
      end if; // from_dp
    end if; // homotopyInitialization
  end if; // linearized
end HydraulicResistance;


Flow splitter with fixed resistance at each port. Model of a flow junction with an optional fixed resistance in each flow leg
and an optional mixing volume at the junction.

within AixLib.Fluid.FixedResistances;
model Junction
  "Flow splitter with fixed resistance at each port"
    extends AixLib.Fluid.BaseClasses.PartialThreeWayResistance(
    m_flow_small=mDyn_flow_nominal*1e-4,
    mDyn_flow_nominal = sum(abs(m_flow_nominal[:])/3),
    redeclare AixLib.Fluid.FixedResistances.PressureDrop res1(
      from_dp=from_dp,
      final m_flow_nominal=m_flow_nominal[1],
      final dp_nominal=dp_nominal[1],
      linearized=linearized,
      homotopyInitialization=homotopyInitialization,
      deltaM=deltaM),
    redeclare AixLib.Fluid.FixedResistances.PressureDrop res2(
      from_dp=from_dp,
      final m_flow_nominal=m_flow_nominal[2],
      final dp_nominal=dp_nominal[2],
      linearized=linearized,
      homotopyInitialization=homotopyInitialization,
      deltaM=deltaM),
    redeclare AixLib.Fluid.FixedResistances.PressureDrop res3(
      from_dp=from_dp,
      final m_flow_nominal=m_flow_nominal[3],
      final dp_nominal=dp_nominal[3],
      linearized=linearized,
      homotopyInitialization=homotopyInitialization,
      deltaM=deltaM));

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

  parameter Modelica.Units.SI.MassFlowRate[3] m_flow_nominal
    "Mass flow rate. Set negative at outflowing ports."

  parameter Modelica.Units.SI.Pressure[3] dp_nominal(each displayUnit="Pa")
    "Pressure drop at nominal mass flow rate, set to zero or negative number at outflowing ports."

  parameter Real deltaM(min=0) = 0.3
    "Fraction of nominal mass flow rate where transition to turbulent occurs"

  parameter Boolean linearized = false
    "= true, use linear relation between m_flow and dp for any flow rate"

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


Pipe with no flow friction and no heat transfer. Model of a pipe with no flow resistance, no heat loss and no transport delay.
This model can be used to replace a replaceable pipe model
in flow legs in which no friction should be modeled.
This is for example done in the outlet port of the
base class for three way valves,

AixLib.Fluid.Actuators.BaseClasses.PartialThreeWayValve.

within AixLib.Fluid.FixedResistances;
model LosslessPipe "Pipe with no flow friction and no heat transfer"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface;
  final parameter Boolean from_dp=true "Used to satisfy replaceable models";
equation
  dp=0;
  // Isenthalpic state transformation (no storage and no loss of energy)
  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);

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

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

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


within AixLib.Fluid;
package FixedResistances "Package with models for fixed flow resistances"
  extends Modelica.Icons.VariantsPackage;
end FixedResistances;


within AixLib.Fluid.FixedResistances;
model Pipe "Discretized DynamicPipe with heat loss to ambient"
  extends Interfaces.PartialTwoPort;
  import Modelica.Fluid.Types.ModelStructure;

  outer Modelica.Fluid.System system "System wide properties";

   parameter Integer nParallel = 1 "Number of identical parallel pipes"
  parameter Modelica.Units.SI.Length length=1 "Length"
   parameter Boolean isCircular = true
    "=true if cross sectional area is circular"
  parameter Modelica.Units.SI.Diameter diameter=parameterPipe.d_i
    "Diameter of circular pipe"
  parameter Modelica.Units.SI.Area crossArea=Modelica.Constants.pi*diameter*
      diameter/4 "Inner cross section area"
  parameter Modelica.Units.SI.Length perimeter=Modelica.Constants.pi*diameter
    "Inner perimeter"
  parameter Modelica.Units.SI.Height roughness=2.5e-5
    "Average height of surface asperities (default: smooth steel pipe)"

  parameter Modelica.Units.SI.Length height_ab=0
    "Height(port_b)-Height(port_a)"

   replaceable model FlowModel =
    Modelica.Fluid.Pipes.BaseClasses.FlowModels.DetailedPipeFlow
    constrainedby Modelica.Fluid.Pipes.BaseClasses.FlowModels.PartialStaggeredFlowModel
    "Wall friction, gravity, momentum flow"

    parameter Modelica.Fluid.Types.Dynamics energyDynamics = system.energyDynamics
    "Formulation of energy balances"
    parameter Modelica.Fluid.Types.Dynamics massDynamics = system.massDynamics
    "Formulation of mass balances"
    parameter Modelica.Fluid.Types.Dynamics momentumDynamics = system.momentumDynamics
    "Formulation of momentum balances"

    //Parameter Tab "HeatTransfer"
    parameter Boolean Heat_Loss_To_Ambient = false
    "= true to internally simulate heat loss to ambient by convection and radiation"
    parameter Boolean isEmbedded = false
    "= true if pipe is embedded in a solid material, for example walls "
    parameter Boolean withInsulation = false
    "= true to use a pipe with insulation"

    parameter Boolean use_HeatTransferConvective = true
    "= true to use the convective HeatTransfer model"
    replaceable model HeatTransferConvective =
      Modelica.Fluid.Pipes.BaseClasses.HeatTransfer.ConstantFlowHeatTransfer (alpha0 = alpha_i)
    constrainedby Modelica.Fluid.Pipes.BaseClasses.HeatTransfer.PartialFlowHeatTransfer
    "Wall heat transfer"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hCon_i=1000
    "Heat tranfer coefficient from fluid to pipe wall";
    parameter AixLib.DataBase.Pipes.PipeBaseDataDefinition parameterPipe=
      AixLib.DataBase.Pipes.Copper.Copper_6x1() "Pipe type"
  parameter AixLib.DataBase.Pipes.InsulationBaseDataDefinition parameterIso=
      AixLib.DataBase.Pipes.Insulation.Iso50pc() "Insulation Type"

  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hCon=8
    "Heat transfer coefficient to ambient"
    Utilities.HeatTransfer.CylindricHeatTransfer                       PipeWall[nNodes](
    rho=fill(parameterPipe.d, nNodes),
    c=fill(parameterPipe.c, nNodes),
    d_out=fill(parameterPipe.d_o, nNodes),
    d_in=fill(parameterPipe.d_i, nNodes),
    length=fill(length/nNodes, nNodes),
    lambda=fill(parameterPipe.lambda, nNodes),
    T0=fill(T_start, nNodes),
    each nParallel=nParallel)

    Utilities.HeatTransfer.CylindricHeatTransfer                       Insulation[nNodes](
    c=fill(parameterIso.c, nNodes),
    d_out=fill(parameterPipe.d_o*parameterIso.factor*2 + parameterPipe.d_o,
        nNodes),
    d_in=fill(parameterPipe.d_o, nNodes),
    length=fill(length/nNodes, nNodes),
    lambda=fill(parameterIso.lambda, nNodes),
    T0=fill(T_start, nNodes),
    rho=fill(parameterIso.d, nNodes),
    each nParallel=nParallel)              if withInsulation

  Modelica.Fluid.Pipes.DynamicPipe pipe(
    redeclare package Medium = Medium,
    nParallel=nParallel,
    length=length,
    isCircular=isCircular,
    diameter=diameter,
    crossArea=crossArea,
    perimeter=perimeter,
    roughness=roughness,
    height_ab=height_ab,
    redeclare model FlowModel = FlowModel,
    use_HeatTransfer=Heat_Loss_To_Ambient,
    allowFlowReversal=allowFlowReversal,
    energyDynamics=energyDynamics,
    massDynamics=massDynamics,
    p_a_start=p_a_start,
    p_b_start=p_b_start,
    use_T_start=use_T_start,
    T_start=T_start,
    h_start=h_start,
    X_start=X_start,
    C_start=C_start,
    momentumDynamics=momentumDynamics,
    m_flow_start=m_flow_start,
    nNodes=nNodes,
    modelStructure=modelStructure,
    useLumpedPressure=useLumpedPressure,
    useInnerPortProperties=useInnerPortProperties,
    redeclare model HeatTransfer =
        Modelica.Fluid.Pipes.BaseClasses.HeatTransfer.ConstantFlowHeatTransfer
        (                                                                                alpha0=hCon_i))

    // Parameter Tab "Initialisation"
   parameter Medium.AbsolutePressure p_a_start=system.p_start
    "Start value of pressure at port a"
  parameter Medium.AbsolutePressure p_b_start=p_a_start
    "Start value of pressure at port b"
  parameter Boolean use_T_start=true "Use T_start if true, otherwise h_start"

  parameter Medium.Temperature T_start=if use_T_start then system.T_start else
              Medium.temperature_phX(
        (p_a_start + p_b_start)/2,
        h_start,
        X_start) "Start value of temperature"
  parameter Medium.SpecificEnthalpy h_start=if use_T_start then
        Medium.specificEnthalpy_pTX(
        (p_a_start + p_b_start)/2,
        T_start,
        X_start) else Medium.h_default "Start value of specific enthalpy"
  parameter Medium.MassFraction X_start[Medium.nX]=Medium.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium.ExtraProperty C_start[Medium.nC](
       quantity=Medium.extraPropertiesNames)=fill(0, Medium.nC)
    "Start value of trace substances"
  parameter Medium.MassFlowRate m_flow_start = system.m_flow_start
    "Start value for mass flow rate"

    // Parameter Tab "Advanced"
    parameter Integer nNodes(min=2)=2 "Number of discrete flow volumes"

   parameter Modelica.Fluid.Types.ModelStructure modelStructure=Modelica.Fluid.Types.ModelStructure.av_vb
    "Determines whether flow or volume models are present at the ports"

  parameter Boolean useLumpedPressure=false
    "=true to lump pressure states together"
  parameter Boolean useInnerPortProperties=false
    "=true to take port properties for flow models from internal control volumes"

protected
  Modelica.Fluid.Interfaces.HeatPorts_a heatPorts[nNodes]

public
  AixLib.Utilities.HeatTransfer.HeatConv heatConv[nNodes](hCon=fill(hCon, nNodes), A=
        Modelica.Constants.pi*PipeWall.d_out*length/nNodes*nParallel)
    if Heat_Loss_To_Ambient and not withInsulation and not isEmbedded "Convection from pipe wall"
  AixLib.Utilities.HeatTransfer.HeatConv heatConv_withInsulation[nNodes](hCon=fill(hCon, nNodes), A=
        Modelica.Constants.pi*Insulation.d_out*length/nNodes*nParallel)
                       if (Heat_Loss_To_Ambient and withInsulation and not isEmbedded) "Convection from insulation"
  Utilities.HeatTransfer.HeatToRad twoStar_RadEx[nNodes](eps=fill(eps, nNodes), A=Modelica.Constants.pi*PipeWall.d_out*length/nNodes*nParallel)
                                  if Heat_Loss_To_Ambient and not isEmbedded "Radiation"
  parameter Modelica.Units.SI.Emissivity eps=0.8 "Emissivity"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort_outside

  Modelica.Thermal.HeatTransfer.Components.ThermalCollector thermalCollector(m=nNodes)

  AixLib.Utilities.Interfaces.RadPort Star if Heat_Loss_To_Ambient and not isEmbedded
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector thermalCollector_Star(m=nNodes)
                                  if Heat_Loss_To_Ambient and not isEmbedded
protected
  Modelica.Fluid.Interfaces.HeatPorts_a heatPorts_Star[nNodes] if Heat_Loss_To_Ambient and not isEmbedded
  /*Modelica.Blocks.Math.Sum sum1(nin=n)
 Modelica.Blocks.Interfaces.RealOutput StoredEnergy
 Modelica.Blocks.Interfaces.RealOutput Temperature
        */
equation

  connect(pipe.heatPorts,PipeWall.port_a);
  connect(heatPorts, thermalCollector.port_a);
  connect(heatPort_outside, thermalCollector.port_b);

        //Connect pipe wall or insulation to the outside.

        //Pipe: embedded, with heat losses, without insulation
        if (isEmbedded and Heat_Loss_To_Ambient and not withInsulation) then
        connect(PipeWall.port_b,heatPorts);

        //Pipe: embedded, with heat losses, with insulation
        elseif (isEmbedded and Heat_Loss_To_Ambient and withInsulation) then
        connect(PipeWall.port_b,Insulation.port_a);
        connect(Insulation.port_b,  heatPorts);

        //Pipe: not embedded, with heat losses, with insulation
        elseif (withInsulation and Heat_Loss_To_Ambient and not isEmbedded) then
        connect(PipeWall.port_b,Insulation.port_a);
        connect(Insulation.port_b,  heatConv_withInsulation.port_b);
        connect(heatConv_withInsulation.port_a, heatPorts);
        connect(heatPorts,thermalCollector.port_a);
        connect(thermalCollector.port_b,heatPort_outside);
    connect(Insulation.port_b, twoStar_RadEx.convPort);
    connect(twoStar_RadEx.radPort, heatPorts_Star);
        connect(heatPorts_Star, thermalCollector_Star.port_a);
        connect(thermalCollector_Star.port_b, Star);

        //Pipe: not embedded, with heat losses, without insulation
        elseif
              (Heat_Loss_To_Ambient and not withInsulation and not isEmbedded) then
        connect(PipeWall.port_b,heatConv.port_b);
        connect(heatConv.port_a, heatPorts);
        connect(heatPorts,thermalCollector.port_a);
        connect(thermalCollector.port_b,heatPort_outside);
    connect(PipeWall.port_b, twoStar_RadEx.convPort);
    connect(twoStar_RadEx.radPort, heatPorts_Star);
        connect(heatPorts_Star, thermalCollector_Star.port_a);
        connect(thermalCollector_Star.port_b, Star);

        else
        connect(PipeWall.port_b,  heatPorts);
        end if;

  /*connect(sum1.y,StoredEnergy)
      */
  connect(pipe.port_a, port_a)
  connect(pipe.port_b, port_b)
end Pipe;


Pipe model using spatialDistribution for temperature delay. Pipe with heat loss using the time delay based heat losses and transport
of the fluid using a plug flow model, applicable for simulation of long
pipes such as in district heating and cooling systems.

within AixLib.Fluid.FixedResistances;
model PlugFlowPipe
  "Pipe model using spatialDistribution for temperature delay"
  extends AixLib.Fluid.FixedResistances.BaseClasses.PlugFlowPipe(
    redeclare final AixLib.Fluid.FixedResistances.HydraulicDiameter res(
      final dh=dh,
      final from_dp=from_dp,
      final length=length,
      final roughness=roughness,
      final fac=fac,
      final ReC=ReC,
      final v_nominal=v_nominal,
      final homotopyInitialization=homotopyInitialization,
      final linearized=linearized,
    dp(nominal=fac*200*length)));
end PlugFlowPipe;


within AixLib.Fluid.FixedResistances;
model PlugFlowPipeDiscretized
  "Discretized pipe model using spatialDistribution for temperature delay"
  extends AixLib.Fluid.Interfaces.PartialTwoPort;

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

  parameter Integer nSeg(min=1) = 1 "Number of axial segment";

  parameter Boolean from_dp=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"

  parameter Boolean have_pipCap=true
    "= true, a mixing volume is added to each segment that corresponds
    to the heat capacity of the pipe segment wall"
  parameter Boolean have_symmetry=true
    "= false, the mixing volume is only on port_b of each segment,
    which improve performances, but reduces dynamic accuracy"

  parameter Modelica.Units.SI.Length dh "Hydraulic diameter"

  parameter Real ReC=4000
    "Reynolds number where transition to turbulence starts";

  parameter Modelica.Units.SI.Height roughness=2.5e-5
    "Average height of surface asperities (default: smooth steel pipe)"
  parameter Modelica.Units.SI.Length totLen=sum(segLen)
    "Total pipe length (used to compute segment length)"
  parameter Modelica.Units.SI.Length segLen[nSeg]=fill(totLen/nSeg, nSeg)
    "Pipe segment length"

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

  parameter Modelica.Units.SI.Length dIns
    "Thickness of pipe insulation, used to compute R"
  parameter Modelica.Units.SI.ThermalConductivity kIns
    "Heat conductivity of pipe insulation, used to compute R"

  parameter Modelica.Units.SI.Length thickness=0.0035 "Pipe wall thickness"
  parameter Modelica.Units.SI.SpecificHeatCapacity cPip=2300
    "Specific heat of pipe wall material. 2300 for PE, 500 for steel"
  parameter Modelica.Units.SI.Density rhoPip(displayUnit="kg/m3") = 930
    "Density of pipe wall material. 930 for PE, 8000 for steel"

  parameter Modelica.Units.SI.Temperature T_start_in[nSeg]=fill(Medium.T_default,
      nSeg) "Initialization temperature at pipe inlet"
  parameter Modelica.Units.SI.Temperature T_start_out[nSeg]=T_start_in
    "Initialization temperature at pipe outlet"
  parameter Boolean initDelay = false
    "Initialize delay for a constant mass flow rate if true, otherwise start from 0"
  parameter Modelica.Units.SI.MassFlowRate m_flow_start=0
    "Initial value of mass flow rate through pipe"

  parameter Real fac=1
    "Factor to take into account flow resistance of bends etc., fac=dp_nominal/dpStraightPipe_nominal";

  parameter Modelica.Units.SI.MassFlowRate m_flow_small=1E-4*abs(m_flow_nominal)
    "Small mass flow rate for regularization of zero flow"
  parameter Boolean linearized = false
    "= true, use linear relation between m_flow and dp for any flow rate"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPorts[nSeg]
    "Heat transfer to or from surrounding for each pipe segment (positive if pipe is colder than surrounding)"

  Modelica.Units.SI.HeatFlowRate QEnv_flow=sum(heatPorts.Q_flow)
    "Heat transfer to or from surroundings of total pipe length (positive if pipe is colder than surrounding)";

  Modelica.Units.SI.PressureDifference dp(displayUnit="Pa") = res.dp
    "Pressure difference between port_a and port_b";

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

  final parameter Modelica.Units.SI.Velocity v_nominal=m_flow_nominal/(APip*
      rho_default) "Velocity at m_flow_nominal";

  Modelica.Units.SI.Velocity v=pipSeg[1].v "Flow velocity of medium in pipe";

  FixedResistances.HydraulicDiameter res(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final dh=dh,
    final from_dp=from_dp,
    final length=totLen,
    final roughness=roughness,
    final fac=fac,
    final ReC=ReC,
    final v_nominal=v_nominal,
    final allowFlowReversal=allowFlowReversal,
    final show_T=false,
    final homotopyInitialization=homotopyInitialization,
    final linearized=linearized,
    dp(nominal= if rho_default > 500 then totLen * fac * 200 else totLen * fac * 2))
    "Pressure drop calculation for this pipe"

protected
  parameter Modelica.Units.SI.Length rInt=dh/2 "Pipe interior radius";

  parameter Modelica.Units.SI.Area APip=Modelica.Constants.pi*rInt^2
    "Pipe hydraulic cross-sectional area";

  parameter Modelica.Units.SI.Density rho_default=Medium.density_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default)
    "Default density (e.g., rho_liquidWater = 995, rho_air = 1.2)";

  AixLib.Fluid.FixedResistances.BaseClasses.PlugFlowPipe pipSeg[nSeg](
    redeclare each final package Medium = Medium,
    redeclare each final FixedResistances.LosslessPipe res,
    final length=segLen,
    final T_start_in=T_start_in,
    final T_start_out=T_start_out,
    each final dIns=dIns,
    each final kIns=kIns,
    each final cPip=cPip,
    each final rhoPip=rhoPip,
    each final dh=dh,
    each final v_nominal=v_nominal,
    each final allowFlowReversal=allowFlowReversal,
    each final m_flow_nominal=m_flow_nominal,
    each final thickness=thickness,
    each final m_flow_small=m_flow_small,
    each final initDelay=initDelay,
    each final have_pipCap=have_pipCap,
    each final have_symmetry=have_symmetry)
    "Pipe segments"

equation
  connect(res.port_b, port_b)

  for i in 2:nSeg loop
    connect(pipSeg[i-1].port_b, pipSeg[i].port_a);
  end for;

  connect(pipSeg.heatPort, heatPorts)
  connect(pipSeg[nSeg].port_b, res.port_a)
  connect(port_a, pipSeg[1].port_a)
end PlugFlowPipeDiscretized;


within AixLib.Fluid.FixedResistances;
model PressureDrop
  "Fixed flow resistance with dp and m_flow as parameter"
  extends AixLib.Fluid.BaseClasses.PartialResistance(
    final m_flow_turbulent = if computeFlowResistance then deltaM * m_flow_nominal_pos else 0);

  parameter Real deltaM(min=1E-6) = 0.3
    "Fraction of nominal mass flow rate where transition to turbulent occurs"

  final parameter Real k = if computeFlowResistance then
        m_flow_nominal_pos / sqrt(dp_nominal_pos) else 0
    "Flow coefficient, k=m_flow/sqrt(dp), with unit=(kg.m)^(1/2)";
protected
  final parameter Boolean computeFlowResistance=(dp_nominal_pos > Modelica.Constants.eps)
    "Flag to enable/disable computation of flow resistance"
  final parameter Real coeff=
    if linearized and computeFlowResistance
    then if from_dp then k^2/m_flow_nominal_pos else m_flow_nominal_pos/k^2
    else 0
    "Precomputed coefficient to avoid division by parameter";
initial equation
 if computeFlowResistance then
   assert(m_flow_turbulent > 0, "m_flow_turbulent must be bigger than zero.");
 end if;

 assert(m_flow_nominal_pos > 0, "m_flow_nominal_pos must be non-zero. Check parameters.");
equation
  // Pressure drop calculation
  if computeFlowResistance then
    if linearized then
      if from_dp then
        m_flow = dp*coeff;
      else
        dp = m_flow*coeff;
      end if;
    else
      if homotopyInitialization then
        if from_dp then
          m_flow=homotopy(
            actual=AixLib.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
              dp=dp,
              k=k,
              m_flow_turbulent=m_flow_turbulent),
            simplified=m_flow_nominal_pos*dp/dp_nominal_pos);
        else
          dp=homotopy(
            actual=AixLib.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow(
              m_flow=m_flow,
              k=k,
              m_flow_turbulent=m_flow_turbulent),
            simplified=dp_nominal_pos*m_flow/m_flow_nominal_pos);
         end if;  // from_dp
      else // do not use homotopy
        if from_dp then
          m_flow=AixLib.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
            dp=dp,
            k=k,
            m_flow_turbulent=m_flow_turbulent);
        else
          dp=AixLib.Fluid.BaseClasses.FlowModels.basicFlowFunction_m_flow(
            m_flow=m_flow,
            k=k,
            m_flow_turbulent=m_flow_turbulent);
        end if;  // from_dp
      end if; // homotopyInitialization
    end if; // linearized
  else // do not compute flow resistance
    dp = 0;
  end if;  // computeFlowResistance
end PressureDrop;


within AixLib.Fluid.FixedResistances;
model SimplePipe "Simple pipe model with n discrete elements"

  extends AixLib.Fluid.Interfaces.PartialTwoPort;

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

  parameter Modelica.Units.SI.Length dh
    "Inner/hydraulic diameter (assuming a round cross section area)";

  parameter Boolean withHeattransfer=true "True, if heat transfer to ambient"

  parameter Modelica.Units.SI.Length length(min=0) "Pipe length";

  parameter Modelica.Units.SI.MassFlowRate m_flow_small(min=0) = 1E-4*abs(
    m_flow_nominal) "Small mass flow rate for regularization of zero flow"

  parameter Real fac=1
    "Factor to take into account flow resistance of bends etc., fac=dp_nominal/dpStraightPipe_nominal";

  // Material properties for resistance
  parameter Real ReC=2300
    "Reynolds number where transition to turbulent starts"

  parameter Modelica.Units.SI.Height roughness=2.5e-5
    "Average height of surface asperities (default: smooth steel pipe)"

  // Material for heattransfer
  parameter Modelica.Units.SI.ThermalConductivity lambda=370
    "Heat conductivity of pipe material"
  parameter Modelica.Units.SI.SpecificHeatCapacity c=1600
    "Specific heat capacity of pipe material"
  parameter Modelica.Units.SI.Density rho=1000 "Density of pipe material"

  parameter Boolean from_dp=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"
  parameter Modelica.Units.SI.Length thickness(min=0) = 0.001
    "Pipe wall thickness"

  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Initialization temperature at pipe inlet"

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

  constant Boolean homotopyInitialization=true "= true, use homotopy method"
  parameter Boolean linearized=false
    "= true, use linear relation between m_flow and dp for any flow rate"
  parameter Medium.AbsolutePressure p_start=Medium.p_default
    "Start value of pressure"

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

  HydraulicDiameter res(
    redeclare final package Medium = Medium,
    final dh=dh,
    final m_flow_nominal=m_flow_nominal,
    final from_dp=from_dp,
    final length=length,
    final roughness=roughness,
    final fac=fac,
    final ReC=ReC,
    final allowFlowReversal=allowFlowReversal,
    final show_T=false,
    final homotopyInitialization=homotopyInitialization,
    final linearized=linearized,
    dp(nominal=fac*200*length)) "Pressure drop calculation for this pipe"
  MixingVolumes.MixingVolume vol[nNodes](
    redeclare each final package Medium = Medium,
    each final energyDynamics=energyDynamics,
    each final massDynamics=massDynamics,
    each final p_start=p_start,
    each final T_start=T_start,
    each final m_flow_nominal=m_flow_nominal,
    each final allowFlowReversal=allowFlowReversal,
    each final V=dh^2*length/nNodes*Modelica.Constants.pi/4,
    each nPorts=2)
  Utilities.HeatTransfer.CylindricHeatTransfer PipeWall[nNodes](
    each final energyDynamics=energyDynamics,
    each final rho=rho,
    each final c=c,
    each final d_out=dh + 2*thickness,
    each final d_in=dh,
    each final length=length/nNodes,
    each final lambda=lambda,
    each final T0=T_start,
    each final nParallel=1) if withHeattransfer

  Modelica.Fluid.Interfaces.HeatPorts_a heatPorts[nNodes] if withHeattransfer
equation
  connect(res.port_a, port_a)
  connect(vol[1].ports[1], res.port_b)
  connect(vol[nNodes].ports[2], port_b)

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

  connect(PipeWall.port_a, vol.heatPort)
  connect(PipeWall.port_b, heatPorts)
end SimplePipe;


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


Lossless pipe model with spatialDistribution plug flow implementation. Model that computes the temperature propagation of
a fluid flow through a pipe, idealized as a plug flow.

within AixLib.Fluid.FixedResistances.BaseClasses;
model PlugFlow
  "Lossless pipe model with spatialDistribution plug flow implementation"
  extends AixLib.Fluid.Interfaces.PartialTwoPort;

  parameter Modelica.Units.SI.Length dh
    "Hydraulic diameter (assuming a round cross section area)";
  parameter Modelica.Units.SI.Length length(min=0) "Pipe length";
  final parameter Modelica.Units.SI.Area A=Modelica.Constants.pi*(dh/2)^2
    "Cross-sectional area of pipe";

  parameter Medium.MassFlowRate m_flow_small
    "Small mass flow rate for regularization of zero flow"
  parameter Modelica.Units.SI.Temperature T_start_in=Medium.T_default
    "Initial temperature in pipe at inlet"
  parameter Modelica.Units.SI.Temperature T_start_out=Medium.T_default
    "Initial temperature in pipe at outlet"

  Modelica.Units.SI.Length x
    "Spatial coordinate for spatialDistribution operator";
  Modelica.Units.SI.Velocity v "Flow velocity of medium in pipe";

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

protected
  parameter Modelica.Units.SI.SpecificEnthalpy h_ini_in=Medium.specificEnthalpy(
      Medium.setState_pTX(
      T=T_start_in,
      p=Medium.p_default,
      X=Medium.X_default)) "For initialization of spatialDistribution inlet";

  parameter Modelica.Units.SI.SpecificEnthalpy h_ini_out=
      Medium.specificEnthalpy(Medium.setState_pTX(
      T=T_start_out,
      p=Medium.p_default,
      X=Medium.X_default)) "For initialization of spatialDistribution outlet";

  Medium.MassFraction Xi_inflow_a[Medium.nXi] = inStream(port_a.Xi_outflow)
    "Independent mixture mass fractions m_i/m close to the connection point flow into the component";
  Medium.MassFraction Xi_inflow_b[Medium.nXi] = inStream(port_b.Xi_outflow)
    "Independent mixture mass fractions m_i/m close to the connection point flow into the component";
  Medium.ExtraProperty C_inflow_a[Medium.nC] = inStream(port_a.C_outflow)
    "Properties c_i/m close to the connection point if flow into the component";
  Medium.ExtraProperty C_inflow_b[Medium.nC] = inStream(port_b.C_outflow)
    "Properties c_i/m close to the connection point if flow into the component";
initial equation
  x = 0;
equation
  // No pressure drop
  port_a.p = port_b.p;

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

  der(x) = v;
  v = V_flow/A;

  (port_a.h_outflow, port_b.h_outflow) = spatialDistribution(
    inStream(port_a.h_outflow),
    inStream(port_b.h_outflow),
    x/length,
    v >= 0,
    {0.0, 1.0},
    {h_ini_in, h_ini_out});

  // Transport of substances
  for i in 1:Medium.nXi loop
  (port_a.Xi_outflow[i], port_b.Xi_outflow[i]) = spatialDistribution(
    Xi_inflow_a[i],
    Xi_inflow_b[i],
    x/length,
    v >= 0,
    {0.0, 1.0},
    {0.01, 0.01});
  end for;

  for i in 1:Medium.nC loop
  (port_a.C_outflow[i], port_b.C_outflow[i]) = spatialDistribution(
    C_inflow_a[i],
    C_inflow_b[i],
    x/length,
    v >= 0,
    {0.0, 1.0},
    {0, 0});
  end for;
end PlugFlow;


within AixLib.Fluid.FixedResistances.BaseClasses;
model PlugFlowCore
  "Pipe model using spatialDistribution for temperature delay with modified delay tracker"
  extends AixLib.Fluid.Interfaces.PartialTwoPort;

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

  parameter Modelica.Units.SI.Length dh
    "Hydraulic diameter (assuming a round cross section area)";

  parameter Modelica.Units.SI.Velocity v_nominal
    "Velocity at m_flow_nominal (used to compute default value for hydraulic diameter dh)"

  parameter Modelica.Units.SI.Length length(min=0) "Pipe length";

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

  parameter Modelica.Units.SI.MassFlowRate m_flow_small(min=0) = 1E-4*abs(
    m_flow_nominal) "Small mass flow rate for regularization of zero flow"

  parameter Modelica.Units.SI.Height roughness=2.5e-5
    "Average height of surface asperities (default: smooth steel pipe)"

  parameter Real R(unit="(m.K)/W")
    "Thermal resistance per unit length from fluid to boundary temperature";

  parameter Real C(unit="J/(K.m)")
    "Thermal capacity per unit length of pipe";

  parameter Real fac=1
    "Factor to take into account flow resistance of bends etc., fac=dp_nominal/dpStraightPipe_nominal";

  parameter Boolean from_dp=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"
  parameter Modelica.Units.SI.Length thickness(min=0) "Pipe wall thickness";

  parameter Modelica.Units.SI.Temperature T_start_in=Medium.T_default
    "Initialization temperature at pipe inlet"
  parameter Modelica.Units.SI.Temperature T_start_out=Medium.T_default
    "Initialization temperature at pipe outlet"
  parameter Boolean initDelay=false
    "Initialize delay for a constant mass flow rate if true, otherwise start from 0"
  parameter Modelica.Units.SI.MassFlowRate m_flow_start=0

  parameter Real ReC=4000
    "Reynolds number where transition to turbulent starts";

  parameter Boolean linearized = false
    "= true, use linear relation between m_flow and dp for any flow rate"

  AixLib.Fluid.FixedResistances.HydraulicDiameter res(
    redeclare final package Medium = Medium,
    final dh=dh,
    final m_flow_nominal=m_flow_nominal,
    final from_dp=from_dp,
    final length=length,
    final roughness=roughness,
    final fac=fac,
    final ReC=ReC,
    final v_nominal=v_nominal,
    final allowFlowReversal=allowFlowReversal,
    final show_T=false,
    final homotopyInitialization=homotopyInitialization,
    final linearized=linearized,
    dp(nominal=fac*200*length))
                 "Pressure drop calculation for this pipe"

  AixLib.Fluid.FixedResistances.BaseClasses.PlugFlow del(
    redeclare final package Medium = Medium,
    final m_flow_small=m_flow_small,
    final dh=dh,
    final length=length,
    final allowFlowReversal=allowFlowReversal,
    final T_start_in=T_start_in,
    final T_start_out=T_start_out)
    "Model for temperature wave propagation"

  AixLib.Fluid.FixedResistances.BaseClasses.PlugFlowHeatLoss heaLos_a(
    redeclare final package Medium = Medium,
    final C=C,
    final R=R,
    final m_flow_small=m_flow_small,
    final T_start=T_start_in,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_start=m_flow_start,
    final show_T=false,
    final show_V_flow=false) "Heat loss for flow from port_b to port_a"

  AixLib.Fluid.FixedResistances.BaseClasses.PlugFlowHeatLoss heaLos_b(
    redeclare final package Medium = Medium,
    final C=C,
    final R=R,
    final m_flow_small=m_flow_small,
    final T_start=T_start_out,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_start=m_flow_start,
    final show_T=false,
    final show_V_flow=false) "Heat loss for flow from port_a to port_b"
  AixLib.Fluid.Sensors.MassFlowRate senMasFlo(
    redeclare final package Medium = Medium) "Mass flow sensor"
  AixLib.Fluid.FixedResistances.BaseClasses.PlugFlowTransportDelay timDel(
    final length=length,
    final dh=dh,
    final rho=rho_default,
    final initDelay=initDelay,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_start=m_flow_start) "Time delay"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
    "Heat port to connect environment (positive heat flow for heat loss to surroundings)"

protected
  parameter Modelica.Units.SI.Density rho_default=Medium.density_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default)
    "Default density (e.g., rho_liquidWater = 995, rho_air = 1.2)"

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

equation
  connect(senMasFlo.m_flow, timDel.m_flow)
  connect(heaLos_a.heatPort, heatPort)
  connect(heaLos_b.heatPort, heatPort)

  connect(timDel.tauRev, heaLos_a.tau)
  connect(timDel.tau, heaLos_b.tau)

  connect(port_a, heaLos_a.port_b)
  connect(heaLos_a.port_a, senMasFlo.port_a)
  connect(heaLos_b.port_b, port_b)
  connect(del.port_a, res.port_b)
  connect(senMasFlo.port_b, res.port_a)
  connect(heaLos_b.port_a, del.port_b)
end PlugFlowCore;


Heat loss model for pipe with delay as an input variable. Component that calculates the heat losses at the end of a plug flow pipe
when the flow goes in the design direction.

within AixLib.Fluid.FixedResistances.BaseClasses;
model PlugFlowHeatLoss
  "Heat loss model for pipe with delay as an input variable"
  extends Fluid.Interfaces.PartialTwoPortTransport(
    final allowFlowReversal=true,
    final dp_start=0);
    // allowFlowReversal set to true because this model is used for inlet and outlets

  parameter Real C(unit="J/(K.m)")
    "Thermal capacity per unit length of pipe";
  parameter Real R(unit="(m.K)/W")
    "Thermal resistance per unit length from fluid to boundary temperature";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.Temperature T_start "Initial output temperature";

  final parameter Modelica.Units.SI.Time tau_char=R*C
    "Characteristic delay time";

  Modelica.Blocks.Interfaces.RealInput tau(unit="s") "Time delay at pipe level"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
    "Heat port to connect environment (negative if heat is lost to ambient)"

  Modelica.Units.SI.Temperature T_a_inflow(start=T_start)
    "Temperature at port_a for inflowing fluid";
  Modelica.Units.SI.Temperature T_b_outflow(start=T_start)
    "Temperature at port_b for outflowing fluid";
  Modelica.Units.SI.Temperature TAmb=heatPort.T "Environment temperature";

protected
  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default) "Default medium state";
  parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(state=sta_default)
    "Heat capacity of medium";

equation
  dp = 0;

  port_a.h_outflow = inStream(port_b.h_outflow);

  port_b.h_outflow =Medium.specificEnthalpy(
    Medium.setState_pTX(
      port_a.p,
      T_b_outflow,
      port_b.Xi_outflow)) "Calculate enthalpy of output state";

    T_a_inflow = Medium.temperature(
      Medium.setState_phX(
        port_a.p,
        inStream(port_a.h_outflow),
        port_b.Xi_outflow));

  // Heat losses
  T_b_outflow = TAmb + (T_a_inflow - TAmb)*Modelica.Math.exp(-tau/tau_char);

  heatPort.Q_flow = -AixLib.Utilities.Math.Functions.spliceFunction(
    pos=(T_a_inflow - T_b_outflow)*cp_default,
    neg=0,
    x=port_a.m_flow,
    deltax=m_flow_nominal/1000)*port_a.m_flow;
end PlugFlowHeatLoss;


within AixLib.Fluid.FixedResistances.BaseClasses;
model PlugFlowPipe
  "Pipe model using spatialDistribution for temperature delay"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface(
    m_flow(start=m_flow_start));

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

  parameter Boolean from_dp=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"

  parameter Boolean have_pipCap=true
    "= true, a mixing volume is added that corresponds
    to the heat capacity of the pipe wall"
  parameter Boolean have_symmetry=true
    "= false, the mixing volume is only on port_b,
    which improve performances, but reduces dynamic accuracy."

  parameter Modelica.Units.SI.Length dh=sqrt(4*m_flow_nominal/rho_default/
      v_nominal/Modelica.Constants.pi)
    "Hydraulic diameter (assuming a round cross section area)"

  parameter Modelica.Units.SI.Velocity v_nominal=1.5
    "Velocity at m_flow_nominal (used to compute default value for hydraulic diameter dh)"

  parameter Real ReC=4000
    "Reynolds number where transition to turbulence starts";

  parameter Modelica.Units.SI.Height roughness=2.5e-5
    "Average height of surface asperities (default: smooth steel pipe)"

  parameter Modelica.Units.SI.Length length "Pipe length"

  parameter Modelica.Units.SI.Length dIns
    "Thickness of pipe insulation, used to compute R"

  parameter Modelica.Units.SI.ThermalConductivity kIns
    "Heat conductivity of pipe insulation, used to compute R"

  parameter Modelica.Units.SI.SpecificHeatCapacity cPip=2300
    "Specific heat of pipe wall material. 2300 for PE, 500 for steel"

  parameter Modelica.Units.SI.Density rhoPip(displayUnit="kg/m3") = 930
    "Density of pipe wall material. 930 for PE, 8000 for steel"

  parameter Modelica.Units.SI.Length thickness=0.0035 "Pipe wall thickness"

  parameter Modelica.Units.SI.Temperature T_start_in(start=Medium.T_default) =
    Medium.T_default "Initialization temperature at pipe inlet"
  parameter Modelica.Units.SI.Temperature T_start_out(start=Medium.T_default)
     = T_start_in "Initialization temperature at pipe outlet"
  parameter Boolean initDelay = false
    "Initialize delay for a constant mass flow rate if true, otherwise start from 0"
  parameter Modelica.Units.SI.MassFlowRate m_flow_start=0
    "Initial value of mass flow rate through pipe"

  parameter Real R(unit="(m.K)/W")=1/(kIns*2*Modelica.Constants.pi/
    Modelica.Math.log((dh/2 + thickness + dIns)/(dh/2 + thickness)))
    "Thermal resistance per unit length from fluid to boundary temperature"

  parameter Real fac=1
    "Factor to take into account flow resistance of bends etc.,
    fac=dp_nominal/dpStraightPipe_nominal";

  parameter Boolean linearized = false
    "= true, use linear relation between m_flow and dp for any flow rate"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
    "Heat transfer to or from surroundings (positive if pipe is colder than surrounding)"

  // QEnv_flow is introduced because in discretized pipes, heatPort.Q_flow must be summed over all ports.
  // By introducing this variable, both models have the same variable.
  Modelica.Units.SI.HeatFlowRate QEnv_flow=heatPort.Q_flow
    "Heat transfer to or from surroundings (positive if pipe is colder than surrounding)";

  Modelica.Units.SI.Velocity v=del.v "Flow velocity of medium in pipe";

  replaceable AixLib.Fluid.FixedResistances.HydraulicDiameter res(
    final dh=dh,
    final from_dp=from_dp,
    final length=length,
    final roughness=roughness,
    final fac=fac,
    final ReC=ReC,
    final v_nominal=v_nominal,
    final homotopyInitialization=homotopyInitialization,
    final linearized=linearized,
    dp(nominal=fac*200*length)) constrainedby
    AixLib.Fluid.Interfaces.PartialTwoPortInterface(
      redeclare final package Medium = Medium,
      final m_flow_nominal=m_flow_nominal,
      final allowFlowReversal=allowFlowReversal,
      final show_T=false)
    "Pressure drop calculation for this pipe"

protected
  parameter Modelica.Units.SI.HeatCapacity CPip=length*((dh + 2*thickness)^2 -
      dh^2)*Modelica.Constants.pi/4*cPip*rhoPip "Heat capacity of pipe wall";

  final parameter Modelica.Units.SI.Volume VEqu=if have_symmetry then CPip/(
      rho_default*cp_default)/2 else CPip/(rho_default*cp_default)
    "Equivalent water volume to represent pipe wall thermal inertia";

  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default) "Default medium state";

  parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(state=sta_default)
    "Heat capacity of medium";

  parameter Real C(unit="J/(K.m)")=
    rho_default*Modelica.Constants.pi*(dh/2)^2*cp_default
    "Thermal capacity per unit length of water in pipe";

  parameter Modelica.Units.SI.Density rho_default=Medium.density_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default)
    "Default density (e.g., rho_liquidWater = 995, rho_air = 1.2)"

  parameter Boolean have_pipCap_a = have_pipCap and have_symmetry;

  BaseClasses.PlugFlow del(
    redeclare final package Medium = Medium,
    final m_flow_small=m_flow_small,
    final dh=dh,
    final length=length,
    final allowFlowReversal=allowFlowReversal,
    final T_start_in=T_start_in,
    final T_start_out=T_start_out)
    "Model for temperature wave propagation"
  BaseClasses.PlugFlowHeatLoss heaLos_a(
    redeclare final package Medium = Medium,
    final C=C,
    final R=R,
    final m_flow_small=m_flow_small,
    final T_start=T_start_in,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_start=m_flow_start,
    final show_T=false,
    final show_V_flow=false) "Heat loss for flow from port_b to port_a"
  BaseClasses.PlugFlowHeatLoss heaLos_b(
    redeclare final package Medium = Medium,
    final C=C,
    final R=R,
    final m_flow_small=m_flow_small,
    final T_start=T_start_out,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_start=m_flow_start,
    final show_T=false,
    final show_V_flow=false) "Heat loss for flow from port_a to port_b"
  Sensors.MassFlowRate senMasFlo(
    redeclare final package Medium = Medium)
     "Mass flow sensor"
  BaseClasses.PlugFlowTransportDelay timDel(
    final length=length,
    final dh=dh,
    final rho=rho_default,
    final initDelay=initDelay,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_start=m_flow_start) "Time delay"

  // In the volume, below, we scale down V and use
  // mSenFac. Otherwise, for air, we would get very large volumes
  // which affect the delay of water vapor and contaminants.
  // See also AixLib.Fluid.FixedResistances.Validation.PlugFlowPipes.TransportWaterAir
  // for why mSenFac is 10 and not 1000, as this gives more reasonable
  // temperature step response
  Fluid.MixingVolumes.MixingVolume vol_b(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final V=if rho_default > 500 then VEqu else VEqu/1000,
    final nPorts=2,
    final T_start=T_start_out,
    final energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    final mSenFac=if rho_default > 500 then 1 else 10) if have_pipCap
    "Control volume connected to port_b.
    Represents equivalent pipe wall thermal capacity."
  LosslessPipe noMixPip_b(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_small=m_flow_small,
    final allowFlowReversal=allowFlowReversal) if not have_pipCap
    "Lossless pipe for connecting the outlet port when have_pipCap=false"

  MixingVolumes.MixingVolume vol_a(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final V=if rho_default > 500 then VEqu else VEqu/1000,
    final nPorts=2,
    final T_start=T_start_out,
    final energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    final mSenFac=if rho_default > 500 then 1 else 10) if have_pipCap_a
    "Control volume connected to port_a.
    Represents equivalent pipe wall thermal capacity."
  LosslessPipe noMixPip_a(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_small=m_flow_small,
    final allowFlowReversal=allowFlowReversal) if not have_pipCap_a
    "Lossless pipe for connecting the outlet port when have_pipCap=false
    or have_symmetry=false"

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

equation

  connect(senMasFlo.m_flow,timDel. m_flow)
  connect(timDel.tauRev,heaLos_a. tau)
  connect(timDel.tau,heaLos_b. tau)
  connect(heaLos_a.port_a,senMasFlo. port_a)
  connect(del.port_a,res. port_b)
  connect(senMasFlo.port_b,res. port_a)
  connect(heaLos_b.port_a,del. port_b)
  connect(heaLos_b.heatPort, heatPort)
  connect(heaLos_a.heatPort, heatPort)

  connect(heaLos_b.port_b, vol_b.ports[1])
  connect(vol_b.ports[2], port_b)
  connect(heaLos_b.port_b, noMixPip_b.port_a)
  connect(noMixPip_b.port_b, port_b)

  connect(port_a, vol_a.ports[1])
  connect(vol_a.ports[2], heaLos_a.port_b)
  connect(port_a, noMixPip_a.port_a)
  connect(noMixPip_a.port_b, heaLos_a.port_b)
end PlugFlowPipe;


Delay time for given normalized velocity. Calculates time delay at both sides of the pipe as the difference between the
current simulation time and the inlet time of the fluid at both ends of the pipe.

within AixLib.Fluid.FixedResistances.BaseClasses;
model PlugFlowTransportDelay "Delay time for given normalized velocity"

  parameter Modelica.Units.SI.Length length "Pipe length";
  parameter Modelica.Units.SI.Length dh
    "Hydraulic diameter (assuming a round cross section area)";
  parameter Modelica.Units.SI.Density rho "Standard density of fluid";
  parameter Boolean initDelay=false
    "Initialize delay for a constant m_flow_start if true, otherwise start from 0"
  parameter Modelica.Units.SI.MassFlowRate m_flow_start=0
    "Initialization of mass flow rate to calculate initial time delay"

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

  final parameter Modelica.Units.SI.Time t_in_start=if initDelay and (abs(
      m_flow_start) > 1E-10*m_flow_nominal) then min(length/m_flow_start*(rho*
      dh^2/4*Modelica.Constants.pi), 0) else 0
    "Initial value of input time at inlet";
  final parameter Modelica.Units.SI.Time t_out_start=if initDelay and (abs(
      m_flow_start) > 1E-10*m_flow_nominal) then min(-length/m_flow_start*(rho*
      dh^2/4*Modelica.Constants.pi), 0) else 0
    "Initial value of input time at outlet";
  final parameter Real conUM(unit="1/kg") = 4/rho/dh/dh/Modelica.Constants.pi/length
    "Constant to convert mass flow rate into velocity normalized by the pipe length";
  Modelica.Units.SI.Time time_out_rev "Reverse flow direction output time";
  Modelica.Units.SI.Time time_out_des "Design flow direction output time";

  Real x(start=0) "Spatial coordinate for spatialDistribution operator";
  Real u(unit="1/s") "Normalized fluid velocity (1/s)";

  Modelica.Blocks.Interfaces.RealInput m_flow "Mass flow of fluid"

  Modelica.Blocks.Interfaces.RealOutput tau
    "Time delay for design flow direction"
  Modelica.Blocks.Interfaces.RealOutput tauRev "Time delay for reverse flow"

protected
  parameter Modelica.Units.SI.Time t0(fixed=false)
    "Start time of the simulation";

initial equation
  x = 0;
  t0 = time;

equation
  u = m_flow * conUM;
  der(x) = u;
  (time_out_rev, time_out_des) = spatialDistribution(
    time,
    time,
    x,
    u >= 0,
    initialPoints = {0.0, 1.0},
    initialValues = {t0 + t_in_start, t0 + t_out_start});

  tau    = max(0, time - time_out_des);
  tauRev = max(0, time - time_out_rev);
end PlugFlowTransportDelay;


Example model for check valve

within AixLib.Fluid.FixedResistances.Examples;
model CheckValve "Example model for check valve"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water "Medium model";
  Modelica.Blocks.Sources.Ramp P_dp(
    duration=1,
    height=4e5,
    offset=3e5,
    startTime=0)
    "Ramp pressure signal"
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    T=273.15 + 20,
    use_p_in=true,
    nPorts=2)
    "Pressure boundary condition"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=273.15 + 10,
    p(displayUnit="bar") = 500000,
    nPorts=3)
    "Pressure boundary condition"
  AixLib.Fluid.FixedResistances.CheckValve checkValve(
    redeclare package Medium = AixLib.Media.Water,
    m_flow_nominal=2,
    dpValve_nominal=3600)
    "Check valve"
  AixLib.Fluid.FixedResistances.CheckValve checkValveDpFix(
    redeclare package Medium = Media.Water,
    m_flow_nominal=2,
    dpValve_nominal=3600,
    dpFixed_nominal=1e4)
    "Check valve with series resistance"
  AixLib.Fluid.FixedResistances.CheckValve checkValve_m_flow(
    redeclare package Medium = Media.Water,
    m_flow_nominal=2,
    dpValve_nominal=3600)
    "Check valve where the flow rate is prescribed"
  Sources.MassFlowSource_T bou(
    redeclare package Medium = Media.Water,
    use_m_flow_in=true,
    nPorts=1)
    "Mass flow source"
  Modelica.Blocks.Sources.Ramp P_m_flow(
    duration=1,
    height=5,
    offset=0,
    startTime=0)
    "Ramp flow rate signal"
equation
  connect(P_dp.y, sou.p_in)
  connect(sou.ports[1], checkValve.port_a)
  connect(checkValve.port_b, sin.ports[1])
  connect(sou.ports[2], checkValveDpFix.port_a)
  connect(checkValveDpFix.port_b, sin.ports[2])
  connect(bou.ports[1], checkValve_m_flow.port_a)
  connect(P_m_flow.y, bou.m_flow_in)
  connect(checkValve_m_flow.port_b, sin.ports[3])
end CheckValve;


within AixLib.Fluid.FixedResistances.Examples;
model CompareFixedResistances
  "Compare models FixedResistancesDpM and HydraulicResistance"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Water;
  Modelica.Blocks.Sources.Constant PAtm(k=101325)
  Modelica.Blocks.Sources.Trapezoid P(
    rising=1,
    width=0.5,
    falling=1,
    period=3,
    amplitude=-12000,
    offset=101325 + 6000)
  AixLib.Fluid.FixedResistances.PressureDrop FRdp(
    redeclare package Medium = Medium,
    deltaM=0.3,
    linearized=false,
    m_flow_nominal=1,
    dp(start=P.offset - PAtm.k),
    from_dp=true,
    dp_nominal=10000)
  AixLib.Fluid.Sources.Boundary_pT sou1(
    redeclare package Medium = Medium,
    T=293.15,
    nPorts=2,
    use_p_in=true)
  AixLib.Fluid.Sources.Boundary_pT sin1(
    redeclare package Medium = Medium,
    T=283.15,
    nPorts=2,
    use_p_in=true)
  HydraulicResistance HR(
    redeclare package Medium = Medium,
    dp_start=P.offset - PAtm.k,
    m_flow_start=0,
    diameter=sqrt(1/(sqrt(995.586)*Modelica.Constants.pi)),
    m_flow_nominal=1,
    from_dp=true,
    zeta=10000/8)
  AixLib.Fluid.Sensors.MassFlowRate masFlo2(redeclare package Medium = Medium)
    "Mass flow rate sensor"
  AixLib.Utilities.Diagnostics.AssertEquality assEqu(message=
        "Inputs differ, check that lossless pipe is correctly implemented.",
      threShold=0.1) "Assert equality of the two mass flow rates"
  AixLib.Fluid.Sensors.MassFlowRate masFlo1(redeclare package Medium = Medium)
    "Mass flow rate sensor"

equation
  connect(PAtm.y, sin1.p_in)
  connect(P.y, sou1.p_in)
  connect(HR.port_b, masFlo2.port_a)
  connect(FRdp.port_b, masFlo1.port_a)
  connect(sou1.ports[1], FRdp.port_a)
  connect(sou1.ports[2], HR.port_a)
  connect(sin1.ports[1], masFlo1.port_b)
  connect(sin1.ports[2], masFlo2.port_b)
  connect(masFlo2.m_flow, assEqu.u1)
  connect(masFlo1.m_flow, assEqu.u2)
end CompareFixedResistances;