Example implementation of flow system

within Buildings.Fluid.Examples.FlowSystem;
model Basic "Example implementation of flow system"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water "Medium model";
  Buildings.Fluid.Movers.FlowControlled_dp pmpSouth(
    redeclare package Medium = Medium,
    redeclare replaceable
      Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos80slash1to12 per,
    dp_nominal=12*9810,
    m_flow_nominal=27570/3600,
    inputType=Buildings.Fluid.Types.InputType.Stages,
    heads=pmpSouth.dp_nominal*{0,1},
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Pump to south of building"
  Buildings.Fluid.FixedResistances.PressureDrop pipSouth1(
    redeclare package Medium = Medium,
    dp_nominal=50000,
    m_flow_nominal=4) "Pipe 1 to south of building"
  Buildings.Fluid.FixedResistances.PressureDrop pipSouth2(
    redeclare package Medium = Medium,
    dp_nominal=50000,
    m_flow_nominal=3) "Pipe 2 to south of building"
  Buildings.Fluid.Movers.FlowControlled_dp pmpNorth(
    redeclare package Medium = Medium,
    redeclare replaceable
      Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos80slash1to12 per,
    dp_nominal=12*9810,
    m_flow_nominal=24660/3600,
    inputType=Buildings.Fluid.Types.InputType.Stages,
    heads=pmpNorth.dp_nominal*{0,1},
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
     "Pump to north of building"
  Buildings.Fluid.FixedResistances.PressureDrop pipNorth2(
    redeclare package Medium = Medium,
    dp_nominal=50000,
    m_flow_nominal=5) "Pipe 2 to north of building"
  Buildings.Fluid.FixedResistances.PressureDrop pipNorth1(
    redeclare package Medium = Medium,
    dp_nominal=50000,
    m_flow_nominal=3) "Pipe 1 to north of building"
  Buildings.Fluid.Actuators.Valves.TwoWayLinear[4] valSouth1(
    each dpFixed_nominal=0,
    each CvData=Buildings.Fluid.Types.CvTypes.OpPoint,
    each dpValve_nominal=8e4,
    redeclare each package Medium = Medium,
    each m_flow_nominal=1) "Valves on souther tabs section 1"
  Buildings.Fluid.Actuators.Valves.TwoWayLinear[8] valSouth2(
    each dpFixed_nominal=0,
    each CvData=Buildings.Fluid.Types.CvTypes.OpPoint,
    each dpValve_nominal=8e4,
    redeclare each package Medium = Medium,
    each m_flow_nominal=1) "Valves on souther tabs section 2"
  Buildings.Fluid.Actuators.Valves.TwoWayLinear[4] valNorth1(
    each dpFixed_nominal=0,
    each CvData=Buildings.Fluid.Types.CvTypes.OpPoint,
    each dpValve_nominal=8e4,
    redeclare each package Medium = Medium,
    each m_flow_nominal=1) "Valves on northern tabs section 1"
  Buildings.Fluid.Actuators.Valves.TwoWayLinear[8] valNorth2(
    each dpFixed_nominal=0,
    each CvData=Buildings.Fluid.Types.CvTypes.OpPoint,
    each dpValve_nominal=8e4,
    redeclare each package Medium = Medium,
    each m_flow_nominal=1) "Valves on northern tabs section 2"
  Modelica.Blocks.Math.BooleanToInteger booleanToInteger(integerTrue=2,
      integerFalse=1) "Boolean to integer conversion block"
  Modelica.Blocks.Sources.BooleanPulse stepPump(startTime=5, period=1000)
    "Step control"
  Modelica.Blocks.Sources.Sine sine(
    amplitude=0.5,
    offset=0.5,
    f=0.001) "Valve control signal"
  Buildings.Fluid.Actuators.Valves.ThreeWayLinear valSouth(
    redeclare package Medium = Medium,
    m_flow_nominal=5,
    dpFixed_nominal={20000,0},
    from_dp=true,
    dpValve_nominal=10000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Three way valve for controlling supply temperature to south of building"
  Buildings.Fluid.Actuators.Valves.ThreeWayLinear valNorth(
    redeclare package Medium = Medium,
    m_flow_nominal=5,
    dpFixed_nominal={20000,0},
    from_dp=true,
    dpValve_nominal=10000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Three way valve for controlling supply temperature to north of building"
  Buildings.Fluid.HeatExchangers.PrescribedOutlet heater(
    m_flow_nominal=10,
    dp_nominal=100,
    redeclare package Medium = Medium,
    use_X_wSet=false)                  "Heating device"
  Buildings.Fluid.Sources.Boundary_pT bou(nPorts=1,
  redeclare package Medium = Medium) "Boundary for setting absolute temperature"
  Buildings.Fluid.FixedResistances.Junction spl(
    m_flow_nominal={10,10,10},
    dp_nominal={1000,10,10},
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Splitter"
  Buildings.Fluid.FixedResistances.Junction spl1(
    m_flow_nominal={10,10,10},
    dp_nominal={10,10,10},
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Splitter"
  Buildings.Fluid.Movers.FlowControlled_m_flow pumpHea(
    m_flow_nominal=10,
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.VeroLine80slash115dash2comma2slash2 per,
    redeclare package Medium = Medium,
    inputType=Buildings.Fluid.Types.InputType.Stages,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Pump for circulating hot water"
  Modelica.Blocks.Sources.Constant Thot(k=273.15 + 50) "Hot water temperature"
  Buildings.Fluid.Actuators.Valves.TwoWayLinear valHea(
    dpFixed_nominal=0,
    CvData=Buildings.Fluid.Types.CvTypes.OpPoint,
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    dpValve_nominal=1e4)
    "Valve for allowing water to be drawn from hot water circuit"
  Modelica.Blocks.Sources.Pulse stepValve(startTime=10, period=500)
    "Set point for valves"
  Buildings.Fluid.Actuators.Valves.TwoWayLinear valCoo(
    dpFixed_nominal=0,
    CvData=Buildings.Fluid.Types.CvTypes.OpPoint,
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    dpValve_nominal=1e4)
    "Valve for allowing water to be drawn from cold water circuit"
  Modelica.Blocks.Sources.RealExpression valCooExp(y=1 - stepValve.y)
    "Cooling valve opens when heating valve is closed"
  Buildings.Fluid.FixedResistances.Junction spl2(
    m_flow_nominal={10,10,10},
    dp_nominal={1000,10,10},
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Splitter"
  Buildings.Fluid.FixedResistances.Junction spl3(
    m_flow_nominal={10,10,10},
    dp_nominal={10,10,10},
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Splitter"
  Buildings.Fluid.Movers.FlowControlled_m_flow pumpCoo(
    m_flow_nominal=10,
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.VeroLine80slash115dash2comma2slash2 per,
    redeclare package Medium = Medium,
    inputType=Buildings.Fluid.Types.InputType.Stages,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Pump for circulating cold water"
  Buildings.Fluid.MixingVolumes.MixingVolume vol(
    nPorts=2,
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    V=1) "Simplified model of cold water source"
  Buildings.HeatTransfer.Sources.FixedTemperature Tcold(T=273.15 + 10)
    "Cold water temperature"
  Buildings.Fluid.FixedResistances.PressureDrop tabsSouth1[4](
    redeclare each package Medium = Medium,
    each dp_nominal=50000,
    m_flow_nominal=valSouth1.m_flow_nominal)
    "Pressure drop of southern tabs sections on collector 1"
  Buildings.Fluid.FixedResistances.PressureDrop tabsSouth2[8](
    redeclare each package Medium = Medium,
    each dp_nominal=50000,
    m_flow_nominal=valSouth2.m_flow_nominal)
    "Pressure drop of southern tabs sections on collector 2"
  Buildings.Fluid.FixedResistances.PressureDrop tabsNorth1[4](
    redeclare each package Medium = Medium,
    each dp_nominal=50000,
    m_flow_nominal=valNorth1.m_flow_nominal)
    "Pressure drop of northern tabs sections on collector 1"
  Buildings.Fluid.FixedResistances.PressureDrop tabsNorth2[8](
    redeclare each package Medium = Medium,
    each dp_nominal=50000,
    m_flow_nominal=valNorth2.m_flow_nominal)
    "Pressure drop of northern tabs sections on collector 2"
equation
  connect(pipSouth2.port_a, pmpSouth.port_b)
  connect(pmpSouth.port_b, pipSouth1.port_a)
  connect(pipNorth1.port_a, pmpNorth.port_b)
  connect(pmpNorth.port_b, pipNorth2.port_a)
  for i in 1:4 loop
      connect(tabsNorth1[i].port_b,valNorth. port_3)
    connect(tabsSouth1[i].port_b, valSouth.port_3)

    connect(valSouth1[i].y, sine.y)

    connect(pipSouth1.port_b, valSouth1[i].port_a)
    connect(pipNorth1.port_b, valNorth1[i].port_a)
   end for;
    for i in 1:8 loop
  connect(tabsNorth2[i].port_b,valNorth. port_3)
    connect(tabsSouth2[i].port_b, valSouth.port_3)

    connect(valSouth2[i].y, sine.y)
    connect(pipSouth2.port_b, valSouth2[i].port_a)
    connect(pipNorth2.port_b, valNorth2[i].port_a)
  end for;

  connect(pmpSouth.stage, pmpNorth.stage)
  connect(booleanToInteger.y, pmpSouth.stage)
  connect(stepPump.y, booleanToInteger.u)
  connect(valSouth1.y, valNorth1.y)
  connect(valSouth2.y,valNorth2. y)

  connect(valSouth.port_2, pmpSouth.port_a)
  connect(valNorth.port_2, pmpNorth.port_a)
  connect(valNorth.y, valSouth.y)
  connect(bou.ports[1], heater.port_a)
  connect(spl1.port_3, spl.port_3)
  connect(heater.port_b, spl.port_1)
  connect(pumpHea.port_a, spl1.port_2)
  connect(pumpHea.port_b, heater.port_a)
  connect(Thot.y, heater.TSet)
  connect(spl1.port_1, valSouth.port_3)
  connect(spl.port_2, valHea.port_a)
  connect(valHea.port_b, valSouth.port_1)
  connect(stepValve.y, valSouth.y)
  connect(stepValve.y, valHea.y)
  connect(valCooExp.y, valCoo.y)
  connect(valCoo.port_b, valSouth.port_1)
  connect(valCoo.port_b,valNorth. port_1)
  connect(spl2.port_2, valCoo.port_a)
  connect(spl3.port_1, spl1.port_1)
  connect(spl3.port_3, spl2.port_3)
  connect(spl3.port_2, pumpCoo.port_a)
  connect(vol.ports[1], pumpCoo.port_b)
  connect(vol.ports[2], spl2.port_1)
  connect(Tcold.port, vol.heatPort)
  connect(pumpHea.stage, pumpCoo.stage)
  connect(pumpHea.stage, booleanToInteger.y)
  connect(valNorth.port_3, spl3.port_1)
  connect(valSouth1.port_b, tabsSouth1.port_a)
  connect(valSouth2.port_b, tabsSouth2.port_a)
  connect(valNorth1.port_b, tabsNorth1.port_a)
  connect(valNorth2.port_b, tabsNorth2.port_a)
end Basic;


<html>
<p>
This package contains fluid flow models that demonstrate
how such models can be simplified to reduce the dimension
of systems of nonlinear equations in order
to reduce computing time.
</p>
</html>

within Buildings.Fluid.Examples;
package FlowSystem
  extends Modelica.Icons.ExamplesPackage;
end FlowSystem;


Aggregated pressure drops. The model is simplified: series pressure drop components are aggregated into the valve model.

within Buildings.Fluid.Examples.FlowSystem;
model Simplified1 "Aggregated pressure drops"
  extends Basic(
    tabsSouth1(each dp_nominal=0),
    tabsSouth2(each dp_nominal=0),
    tabsNorth1(each dp_nominal=0),
    tabsNorth2(each dp_nominal=0),
    valSouth1(each dpFixed_nominal=50000),
    valSouth2(each dpFixed_nominal=50000),
    valNorth1(each dpFixed_nominal=50000),
    valNorth2(each dpFixed_nominal=50000));
end Simplified1;


Using from_dp. The model is simplified: using from_dp to find more efficient tearing variables.

within Buildings.Fluid.Examples.FlowSystem;
model Simplified2 "Using from_dp"
  extends Simplified1(
    valSouth1(each from_dp=true),
    valSouth2(each from_dp=true),
    valNorth1(each from_dp=true),
    valNorth2(each from_dp=true));
end Simplified2;


Neglecting pressure drop in splitters. The model is further simplified by removing some small pressure drops in the bypass.
This allows the solver to identify sub-circuits.

within Buildings.Fluid.Examples.FlowSystem;
model Simplified3 "Neglecting pressure drop in splitters"
  extends Simplified2(
    spl(dp_nominal={1000,0,0}),
    spl1(dp_nominal={0,0,0}),
    spl3(dp_nominal={0,0,0}),
    spl2(dp_nominal={1000,0,0}));
end Simplified3;


Removed valve dynamics. The model is further simplified by removing the valve and pump control dynamics.

within Buildings.Fluid.Examples.FlowSystem;
model Simplified4 "Removed valve dynamics"
  extends Simplified3(
    valNorth(use_strokeTime =false),
    valSouth(use_strokeTime =false),
    pmpNorth(
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      use_riseTime=false),
    pmpSouth(
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      use_riseTime=false),
    valSouth1(each use_strokeTime=false),
    valSouth2(each use_strokeTime=false),
    valNorth1(each use_strokeTime=false),
    valNorth2(each use_strokeTime=false),
    pumpHea(use_riseTime=false),
    pumpCoo(use_riseTime=false),
    valCoo(use_strokeTime=false),
    valHea(use_strokeTime=false));
end Simplified4;


Removed most mass/energy dynamics. The model is further simplified by setting the mass dynamics and energy dynamics
of most models to be steady state.
Note that by default, the mass dynamics is set to the same configuration
as the energy dynamics.

within Buildings.Fluid.Examples.FlowSystem;
model Simplified5 "Removed most mass/energy dynamics"
  extends Simplified4(
    spl1(energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    spl(energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    spl2(energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    spl3(energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    pumpCoo(energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    pumpHea(energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    heater(energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));
end Simplified5;


Set allowFlowReversal=false. The model is further simplified by setting allowFlowReversal=false.

within Buildings.Fluid.Examples.FlowSystem;
model Simplified6 "Set allowFlowReversal=false"
  extends Simplified5(
    pmpNorth(allowFlowReversal=false),
    pmpSouth(allowFlowReversal=false),
    tabsSouth1(each allowFlowReversal=false),
    tabsSouth2(each allowFlowReversal=false),
    pipSouth1(allowFlowReversal=false),
    pipSouth2(allowFlowReversal=false),
    valSouth2(each allowFlowReversal=false),
    valSouth1(each allowFlowReversal=false),
    tabsNorth1(each allowFlowReversal=false),
    valNorth1(each allowFlowReversal=false),
    pipNorth1(allowFlowReversal=false),
    pipNorth2(allowFlowReversal=false),
    valNorth2(each allowFlowReversal=false),
    tabsNorth2(each allowFlowReversal=false),
    pumpHea(allowFlowReversal=false),
    vol(allowFlowReversal=false),
    heater(allowFlowReversal=false),
    valCoo(allowFlowReversal=false),
    valHea(allowFlowReversal=false));
end Simplified6;


Example 1 model without mixing volume. This model demonstrates the impact of the allowFlowReversal
and from_dp parameters on the sizes of nonlinear algebraic loops.
The user can change the parameter value in the respective
BooleanConstant blocks and rerun the simulation to compare the performance.
The results are also demonstrated below for nRes.k = 20,
the number of parallel branches, which contain one pressure drop element each.

within Buildings.Fluid.Examples.Performance;
model Example1v1 "Example 1 model without mixing volume"
  extends Buildings.Fluid.Examples.Performance.BaseClasses.Example1(
      allowFlowReversal(k=false), from_dp(k=true));

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

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

simulateModel(\"Buildings.Fluid.Examples.Performance.Example1v1(allowFlowReversal.k=false, from_dp.k=false)\", stopTime=10000, numberOfIntervals=10, method=\"dassl\", resultFile=\"Example1v1\");

simulateModel(\"Buildings.Fluid.Examples.Performance.Example1v1(allowFlowReversal.k=false, from_dp.k=true)\", stopTime=10000, numberOfIntervals=10, method=\"dassl\", resultFile=\"Example1v1\");

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

plotExpression(apply(Example1v1[end-2].CPUtime), false, \"Default case\", 1);

plotExpression(apply(Example1v2[end].CPUtime), false, \"Adding dummy states\", 1);

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

plotExpression(apply(Example1v1[end].CPUtime), false, \"allowFlowReversal=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="

July 14, 2015, by Michael Wetter:

Revised documentation.

April 17, 2015, by Filip Jorissen:

First implementation.

"),
    __Dymola_Commands(file="modelica://Buildings/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example1v1.mos"
        "Simulate and plot"));
end Example1v1;


Example 1 model with mixing volume

within Buildings.Fluid.Examples.Performance;
model Example1v2 "Example 1 model with mixing volume"
  extends Buildings.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 Buildings.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_riseTime=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(\"Buildings.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://Buildings/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example2.mos"
        "Simulate and plot"));
end Example2;


within Buildings.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_riseTime=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(\"Buildings.Fluid.Examples.Performance.Example3(from_dp.k=false, mergeDp.k=true, nRes.k=10)\", stopTime=1000, numberOfIntervals=10, method=\"dassl\", resultFile=\"Example3\");

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

simulateModel(\"Buildings.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://Buildings/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example3.mos"
        "Simulate and plot"));
end Example3;


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

  package Medium = Buildings.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)
  Buildings.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 Buildings.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://Buildings/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example5.mos"
        "Simulate and plot"));
end Example5;


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

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

within Buildings.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(\"Buildings.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(\"Buildings.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://Buildings/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example6.mos"
        "Simulate and plot"));
end Example6;


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

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

within Buildings.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 Buildings.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://Buildings/Resources/Scripts/Dymola/Fluid/Examples/Performance/Example8.mos"
        "Simulate and plot"));
end Example8;


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


Example 1 partial model

within Buildings.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";

  Buildings.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_riseTime=false,
    allowFlowReversal=allowFlowReversal.k,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nominalValuesDefineDefaultPressureCurve=true)
    "Pump model with unidirectional flow"
  Buildings.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"
  Buildings.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_strokeTime=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"
  Buildings.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 Buildings.Fluid.Examples.Performance;
package PressureDrop "Package with various configurations of pressure drop models to analyze symbolic processing"
  extends Modelica.Icons.ExamplesPackage;
end PressureDrop;


within Buildings.Fluid.Examples.Performance.PressureDrop;
model ParallelDp
  "Parallel connection with prescribed pressure difference and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = Buildings.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 Buildings.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 Buildings.Fluid.Examples.Performance.PressureDrop;
model ParallelFlow
  "Parallel connection with prescribed flow rate and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = Buildings.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 Buildings.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 Buildings.Fluid.Examples.Performance.PressureDrop;
model SeriesDp
  "Series connection with prescribed pressure difference and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = Buildings.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 Buildings.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 Buildings.Fluid.Examples.Performance.PressureDrop;
model SeriesFlow
  "Series connection with prescribed flow rate and non-optimised parameters"
  extends Modelica.Icons.Example;
  package Medium = Buildings.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 Buildings.Fluid.Examples.Performance.PressureDrop;
model SeriesFlowOptimised
  "Series connection with prescribed flow and optimised parameters"
  extends SeriesFlow(resSeries(each from_dp=false));
end SeriesFlowOptimised;


within Buildings.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 = Buildings.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 Buildings.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 Buildings.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 = Buildings.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 Buildings.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 Buildings.Fluid.FixedResistances;
model CheckValve "Check valve that avoids flow reversal"
  extends Buildings.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 Buildings.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=Buildings.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 = Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
        dp = dp,
        k = k,
        m_flow_turbulent = m_flow_turbulent);
  end if;
end CheckValve;


within Buildings.Fluid.FixedResistances;
model HydraulicDiameter "Fixed flow resistance with hydraulic diameter and m_flow as parameter"
  extends Buildings.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;


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 Buildings.Fluid.FixedResistances;
model Junction
  "Flow splitter with fixed resistance at each port"
    extends Buildings.Fluid.BaseClasses.PartialThreeWayResistance(
    m_flow_small=mDyn_flow_nominal*1e-4,
    mDyn_flow_nominal = sum(abs(m_flow_nominal[:])/3),
    redeclare Buildings.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 Buildings.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 Buildings.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,

Buildings.Fluid.Actuators.BaseClasses.PartialThreeWayValve.

within Buildings.Fluid.FixedResistances;
model LosslessPipe "Pipe with no flow friction and no heat transfer"
  extends Buildings.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 Buildings.Fluid;
package FixedResistances "Package with models for fixed flow resistances"
  extends Modelica.Icons.VariantsPackage;
end FixedResistances;


within Buildings.Fluid.FixedResistances;
model Pipe "Pipe with finite volume discretization along flow path"
  extends Buildings.Fluid.FixedResistances.BaseClasses.Pipe(
   diameter=sqrt(4*m_flow_nominal/rho_default/v_nominal/Modelica.Constants.pi),
   dp_nominal=2*dpStraightPipe_nominal,
   preDro(dp(nominal=length*10)));
  // Because dp_nominal is a non-literal value, we set
  // dp.nominal=100 instead of the default dp.nominal=dp_nominal,
  // because the latter is ignored by Dymola 2012 FD 01.

  parameter Modelica.Units.SI.Velocity v_nominal=0.15
    "Velocity at m_flow_nominal (used to compute default diameter)";
  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)";
  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=diameter,
    roughness=roughness,
    m_flow_small=m_flow_small)
    "Pressure loss of a straight pipe at m_flow_nominal";

  parameter Boolean useMultipleHeatPorts=false
    "= true to use one heat port for each segment of the pipe, false to use a single heat port for the entire pipe";

  Modelica.Thermal.HeatTransfer.Components.ThermalConductor conPipWal[nSeg](
      each G=2*Modelica.Constants.pi*lambdaIns*length/nSeg/Modelica.Math.log((
        diameter/2.0 + thicknessIns)/(diameter/2.0)))
    "Thermal conductance through pipe wall"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector colAllToOne(m=nSeg)
    if not useMultipleHeatPorts
    "Connector to assign multiple heat ports to one heat port"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort if not
    useMultipleHeatPorts
    "Single heat port that connects to outside of pipe wall (default, enabled when useMultipleHeatPorts=false)"
  Modelica.Fluid.Interfaces.HeatPorts_a heatPorts[nSeg]
    if useMultipleHeatPorts
    "Multiple heat ports that connect to outside of pipe wall (enabled if useMultipleHeatPorts=true)"
equation
  connect(conPipWal.port_b, vol.heatPort)
  if useMultipleHeatPorts then
    connect(heatPorts, conPipWal.port_a)
  else
    connect(colAllToOne.port_a, conPipWal.port_a)
    connect(colAllToOne.port_b, heatPort)

  end if;
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 Buildings.Fluid.FixedResistances;
model PlugFlowPipe
  "Pipe model using spatialDistribution for temperature delay"
  extends Buildings.Fluid.FixedResistances.BaseClasses.PlugFlowPipe(
    redeclare final Buildings.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 Buildings.Fluid.FixedResistances;
model PlugFlowPipeDiscretized
  "Discretized pipe model using spatialDistribution for temperature delay"
  extends Buildings.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)";

  Buildings.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 Buildings.Fluid.FixedResistances;
model PressureDrop
  "Fixed flow resistance with dp and m_flow as parameter"
  extends Buildings.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=Buildings.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=Buildings.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=Buildings.Fluid.BaseClasses.FlowModels.basicFlowFunction_dp(
            dp=dp,
            k=k,
            m_flow_turbulent=m_flow_turbulent);
        else
          dp=Buildings.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 Buildings.Fluid.FixedResistances;
package BaseClasses "Package with base classes for Buildings.Fluid.FixedResistances"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


within Buildings.Fluid.FixedResistances.BaseClasses;
model Pipe
  "Model of a pipe with finite volume discretization along the flow path"
  extends Buildings.Fluid.Interfaces.LumpedVolumeDeclarations;
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
  final show_T=true);
  extends Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters(
    final computeFlowResistance=(abs(dp_nominal) > Modelica.Constants.eps));

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

  parameter Integer nSeg(min=1) = 10 "Number of volume segments";
  parameter Modelica.Units.SI.Length thicknessIns "Thickness of insulation";
  parameter Modelica.Units.SI.ThermalConductivity lambdaIns
    "Heat conductivity of insulation";
  parameter Modelica.Units.SI.Length diameter
    "Pipe diameter (without insulation)";

  parameter Modelica.Units.SI.Length length "Length of the pipe";
  parameter Real ReC=4000
    "Reynolds number where transition to turbulence starts"

  Buildings.Fluid.FixedResistances.PressureDrop preDro(
    redeclare final package Medium = Medium,
    final from_dp=from_dp,
    final show_T=show_T,
    final m_flow_nominal=m_flow_nominal,
    final dp_nominal=dp_nominal,
    final allowFlowReversal=allowFlowReversal,
    final linearized=linearizeFlowResistance,
    final homotopyInitialization=homotopyInitialization) "Flow resistance"
  Buildings.Fluid.MixingVolumes.BaseClasses.MixingVolumeHeatPort[nSeg] vol(
    redeclare each final package Medium = Medium,
    each energyDynamics=energyDynamics,
    each massDynamics=massDynamics,
    final initialize_p = {(i == 1 and (not Medium.singleState)) for i in 1:nSeg},
    each final V=VPipe/nSeg,
    each nPorts=2,
    each final m_flow_nominal=m_flow_nominal,
    each prescribedHeatFlowRate=true,
    each p_start=p_start,
    each T_start=T_start,
    each X_start=X_start,
    each C_start=C_start,
    each C_nominal=C_nominal,
    each final m_flow_small=m_flow_small,
    each final allowFlowReversal=allowFlowReversal) "Volume for pipe fluid"

protected
  parameter Modelica.Units.SI.Volume VPipe=Modelica.Constants.pi*(diameter/2.0)
      ^2*length "Pipe volume";
  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);
  parameter Modelica.Units.SI.DynamicViscosity mu_default=
      Medium.dynamicViscosity(state_default)
    "Dynamic viscosity at nominal condition";

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(port_a, preDro.port_a)
  connect(preDro.port_b, vol[1].ports[1])
  if nSeg > 1 then
    for i in 1:(nSeg - 1) loop
      connect(vol[i].ports[2], vol[i + 1].ports[1]);
    end for;
  end if;
  connect(vol[nSeg].ports[2], port_b)
end Pipe;


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 Buildings.Fluid.FixedResistances.BaseClasses;
model PlugFlow
  "Lossless pipe model with spatialDistribution plug flow implementation"
  extends Buildings.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;


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 Buildings.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 = -Buildings.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 Buildings.Fluid.FixedResistances.BaseClasses;
model PlugFlowPipe
  "Pipe model using spatialDistribution for temperature delay"
  extends Buildings.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 Buildings.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
    Buildings.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 Buildings.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 Buildings.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;


Thermal coupling between buried pipes and ground. This model simulates the heat transfer between multiple buried pipes and the
ground, using climate and soil information and the geometry of the pipes
network.

within Buildings.Fluid.FixedResistances.BuriedPipes;
model GroundCoupling "Thermal coupling between buried pipes and ground"
  parameter Integer nPip(min=1) "Number of buried pipes";
  parameter Integer nSeg(min=1) = 1 "Number of axial segments";

  replaceable parameter Buildings.HeatTransfer.Data.Soil.Generic soiDat
    "Soil thermal properties";
  replaceable parameter
    Buildings.BoundaryConditions.GroundTemperature.ClimaticConstants.Generic
    cliCon "Surface temperature climatic conditions";

  parameter Modelica.Units.SI.Length len[nSeg] "Pipes length";

  parameter Modelica.Units.SI.Length dep[nPip] "Pipes buried depth";
  parameter Modelica.Units.SI.Length pos[nPip]
    "Pipes horizontal coordinate (to an arbitrary reference point)";
  parameter Modelica.Units.SI.Length rad[nPip] "Pipes external radius";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a ports[nPip, nSeg]
    "Buried pipes heatports"
  Buildings.BoundaryConditions.GroundTemperature.UndisturbedSoilTemperature
    soi(cliCon=cliCon, soiDat=soiDat, dep=depMea) "Soil temperature";

protected
  parameter Modelica.Units.SI.Length depMea=sum(dep)/nPip "Average depth";
  parameter Real P[nPip,nPip]=BaseClasses.groundCouplingFactors(
      nPip,
      dep,
      pos,
      rad) "Thermal coupling geometric factors";

equation
  for seg in 1:nSeg loop
    ports[:,seg].T .- soi.T = P * ports[:,seg].Q_flow /
        (2 * Modelica.Constants.pi * soiDat.k * len[seg]);
  end for;
end GroundCoupling;


Package with models for buried pipes

within Buildings.Fluid.FixedResistances;
package BuriedPipes "Package with models for buried pipes"
  extends Modelica.Icons.VariantsPackage;
end BuriedPipes;


Pipe length. This model considers the conduction heat transfer between the buried pipe
wall and the undisturbed ground temperature by adding a dynamic ground layer.
The dynamic ground layer is modeled using the radial heat conduction model
through a hollow cylinder
 Buildings.HeatTransfer.Conduction.SingleLayerCylinder.
The undisturbed ground temperature is calculated
by the model
Buildings.BoundaryConditions.GroundTemperature.UndisturbedSoilTemperature.
This model can consider an axial discretization of the pipe.
A figure highlighting the main variables and parameters is shown below:

within Buildings.Fluid.FixedResistances.BuriedPipes;
model PipeGroundCoupling
  parameter Modelica.Units.SI.Height lPip = 100 "Pipe length";
  parameter Modelica.Units.SI.Radius rPip = 0.5 "Pipe external radius";
  parameter Modelica.Units.SI.Radius thiGroLay = 1.0 "Dynamic ground layer thickness";
  final parameter Modelica.Units.SI.Radius rGroLay = rPip + thiGroLay "Pipe radius plus dynamic ground layer";
  parameter Integer nSeg(min=1) = 10 "Number of pipe discretization segments axial direction";
  parameter Integer nSta=1 "Number of state variables for dynamic ground layer";
  parameter Modelica.Units.SI.Temperature TpipSta = 280.15
    "Initial temperature for the pipe, used if steadyStateInitial = false"
  parameter Modelica.Units.SI.Temperature TGrouBouSta = 283.15
    "Initial temperature at ground final layer, used if steadyStateInitial = false"
  parameter Boolean steadyStateInitial = false
    "true initializes dT(0)/dt=0, false initializes T(0) at fixed temperature using TpipSta and TGrouBouSta"
  parameter Modelica.Units.SI.Length dep = 10 "Depth of buried pipe to calculate ground temperature";
  replaceable parameter Buildings.DHC.Examples.Combined.BaseClasses.Zurich cliCon
    "Surface temperature climatic conditions";
  replaceable parameter Buildings.HeatTransfer.Data.Soil.Generic soiDat(
    k=1.58,c=1150,d=1600) "Soil thermal properties"
  Buildings.HeatTransfer.Conduction.SingleLayerCylinder groActLay[nSeg](
    each material = soiDat,
    each h = lPip,
    each r_a = rPip,
    each r_b = rGroLay,
    each nSta = nSta,
    each TInt_start = TpipSta,
    each TExt_start = TGrouBouSta,
    each steadyStateInitial=steadyStateInitial)

  BoundaryConditions.GroundTemperature.UndisturbedSoilTemperature soiTem(
    dep=dep,
    soiDat=soiDat,
    cliCon=cliCon)

  Modelica.Blocks.Sources.RealExpression soiTemExp(y=soiTem.T)
    "Soil undisturbed temperature"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature groTem[nSeg]
    "Prescribed ground temperature"
  Modelica.Fluid.Interfaces.HeatPorts_a heatPorts[nSeg]
    "Multiple heat ports that connect to outside of pipe wall"
  Modelica.Blocks.Routing.Replicator groTemDup(nout=nSeg)
    "Ground temperature replicator"

equation
  connect(groTem.T, groTemDup.y)
  connect(groTemDup.u, soiTemExp.y)
  connect(heatPorts, groActLay.port_a)
  connect(groActLay.port_b, groTem.port)
end PipeGroundCoupling;


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


within Buildings.Fluid.FixedResistances.BuriedPipes.Examples;
model DiscretizedBuriedPipe
  "Example model of a buried pipe with multiple segments"
  extends Modelica.Icons.Example;

  replaceable package Medium = Buildings.Media.Water "Medium in the pipe"

  parameter Modelica.Units.SI.Length totLen=10000 "Total pipe length";

  parameter Integer nSeg=2 "Number of pipe segments";
  parameter Modelica.Units.SI.Length segLen[nSeg]=fill(totLen/nSeg, nSeg)
    "Pipe segments length";

  parameter Integer nSegRev=10 "Number of pipe segments";
  parameter Modelica.Units.SI.Length segLenRev[nSegRev]=fill(totLen/nSegRev,
      nSegRev) "Pipe segments length";

  parameter Modelica.Units.SI.Length dIns=0.2
    "Virtual insulation layer thickness";

  replaceable parameter
    Buildings.BoundaryConditions.GroundTemperature.ClimaticConstants.Boston
    cliCon "Surface temperature climatic conditions";

  replaceable parameter Buildings.HeatTransfer.Data.Soil.Generic
    soiDat(k=1.58,c=1150,d=1600) "Soil thermal properties"

  FixedResistances.PlugFlowPipeDiscretized pip(
    redeclare package Medium = Medium,
    nSeg=nSeg,
    dh=0.1,
    segLen=segLen,
    m_flow_nominal=1,
    dIns=dIns,
    kIns=soiDat.k,
    thickness=0.0032) "Buried pipe"

  Buildings.Fluid.FixedResistances.BuriedPipes.GroundCoupling gro(
    nPip=1,
    cliCon=cliCon,
    soiDat=soiDat,
    nSeg=pip.nSeg,
    len=pip.segLen,
    dep={1.5},
    pos={0},
    rad={pip.dh/2 + pip.thickness + pip.dIns}) "Ground coupling"

  Modelica.Blocks.Sources.Sine Tin(
    amplitude=5,
    f=1/180/24/60/60,
    offset=273.15 + 20) "Pipe inlet temperature signal"
  Sources.MassFlowSource_T sou(
    nPorts=1,
    redeclare package Medium = Medium,
    use_T_in=true,
    m_flow=3) "Flow source"
  Sensors.TemperatureTwoPort senTemInl(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    T_start=293.15) "Pipe inlet temperature sensor"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=273.15 + 10,
    nPorts=1,
    p(displayUnit="Pa") = 101325) "Boundary condition"
  Sensors.TemperatureTwoPort senTemOut(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    T_start=293.15) "Pipe outlet temperature sensor"

  FixedResistances.PlugFlowPipeDiscretized pipRev(
    redeclare package Medium = Medium,
    nSeg=nSegRev,
    dh=0.1,
    segLen=segLenRev,
    m_flow_nominal=1,
    dIns=dIns,
    kIns=soiDat.k,
    thickness=0.0032) "Buried pipe"
  GroundCoupling groRev(
    nPip=1,
    cliCon=cliCon,
    soiDat=soiDat,
    nSeg=pipRev.nSeg,
    len=pipRev.segLen,
    dep={1.5},
    pos={0},
    rad={pip.dh / 2 + pip.thickness + pip.dIns})
    "Ground coupling"
  Sensors.TemperatureTwoPort senTemOutRev(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    T_start=293.15) "Pipe outlet temperature sensor"
  Sensors.TemperatureTwoPort senTemInlRev(
    redeclare package Medium = Medium,
    m_flow_nominal=1,
    T_start=293.15) "Pipe outlet temperature sensor"
  Sources.MassFlowSource_T souRev(
    nPorts=1,
    redeclare package Medium = Medium,
    use_T_in=true,
    m_flow=3) "Flow source"
  Sources.Boundary_pT sinRev(
    redeclare package Medium = Medium,
    T=273.15 + 10,
    nPorts=1,
    p(displayUnit="Pa") = 101325) "Boundary condition"
equation
  connect(Tin.y,sou. T_in)
  connect(sou.ports[1], senTemInl.port_a)
  connect(senTemOut.port_b, sin.ports[1])
  connect(senTemInl.port_b, pip.port_a)
  connect(pip.port_b, senTemOut.port_a)
  connect(senTemOutRev.port_b, pipRev.port_a)
  connect(pipRev.port_b, senTemInlRev.port_a)
  connect(pip.heatPorts, gro.ports[1, :])
  connect(pipRev.heatPorts, groRev.ports[1, :])
  connect(souRev.ports[1], senTemInlRev.port_b)
  connect(senTemOutRev.port_a, sinRev.ports[1])
  connect(Tin.y, souRev.T_in)
end DiscretizedBuriedPipe;


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