within Buildings.Fluid.Movers.BaseClasses.Validation;
model NegativePressureOrFlow
  "A validation model that tests the mover behaviour when the pressure rise or flow is negative"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Air;

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=10000
    "Nominal pressure difference";
  parameter Modelica.Units.SI.Density rho_default=1.2
    "Fluid density at medium default state";

  Buildings.Fluid.Movers.BaseClasses.FlowMachineInterface eff(
    per(pressure(V_flow=m_flow_nominal/rho_default*{0,2},
                 dp=dp_nominal*{2,0}),
        etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.NotProvided,
        etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided),
    preVar=Buildings.Fluid.Movers.BaseClasses.Types.PrescribedVariable.PressureDifference,
    computePowerUsingSimilarityLaws=true,
    rho_default=rho_default,
    nOri=2)
    "FlowMachineInterface model with prescribed pressure"
  Modelica.Blocks.Sources.Constant rho(k=rho_default) "Density"
  Modelica.Blocks.Sources.Ramp m_flow(
    height=2*m_flow_nominal,
    duration=3600,
    offset=-0.5*m_flow_nominal) "Mass flow rate"
  Modelica.Blocks.Sources.Ramp dp(
    height=-2*dp_nominal,
    duration=3600,
    offset=1.5*dp_nominal) "Pressure rise"
equation

  connect(eff.m_flow, m_flow.y)
  connect(rho.y, eff.rho)
  connect(dp.y, eff.dp_in)
end NegativePressureOrFlow;


Collection of validation models

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


Simple model to validate PowerInterface. Simple validation model for the power interface model.

within Buildings.Fluid.Movers.BaseClasses.Validation;
model PowerInterface "Simple model to validate PowerInterface"
  extends Modelica.Icons.Example;
  Buildings.Fluid.Movers.BaseClasses.PowerInterface powCoo(
    motorCooledByFluid=true,
    delta_V_flow=0.05) "Power interface model"
  Modelica.Blocks.Sources.Constant WFlo(k=1*1000) "Flow work"
  Modelica.Blocks.Sources.Ramp V_flow(
    duration=1,
    height=1) "Volume flow rate"
  Modelica.Blocks.Sources.Constant WHyd(k=1250) "Hydraulic work"
  Modelica.Blocks.Sources.Constant PEle(k=1*1000*4) "Electrical work"
  Buildings.Fluid.Movers.BaseClasses.PowerInterface powNonCoo(
    motorCooledByFluid=false,
    delta_V_flow=0.05) "Power interface model"
equation
  connect(V_flow.y, powCoo.V_flow)
  connect(powCoo.WFlo, WFlo.y)
  connect(PEle.y, powCoo.PEle)
  connect(V_flow.y, powNonCoo.V_flow)
  connect(powNonCoo.WFlo, WFlo.y)
  connect(PEle.y, powNonCoo.PEle)
  connect(WHyd.y, powCoo.WHyd)
  connect(WHyd.y, powNonCoo.WHyd)
end PowerInterface;


Validation model for total efficiency specified via hydraulic efficiency options. This model is the same as

Buildings.Fluid.Movers.BaseClasses.Validation.HydraulicEfficiencyMethods
except that the enumeration is used for total efficiency η
via the parameter per.powerOrEfficiencyIsHydraulic.

within Buildings.Fluid.Movers.BaseClasses.Validation;
model TotalEfficiencyMethods
  "Validation model for total efficiency specified via hydraulic efficiency options"
  extends HydraulicEfficiencyMethods(
    per(powerOrEfficiencyIsHydraulic=false),
    eff2(per(efficiency(eta={0,0.35,0.49,0.35,0}))),
    eff3(per(power(P={680,771,730,780,1010}))),
    eff4(per(peak(eta=0.49))));
end TotalEfficiencyMethods;


Generic data record for movers

within Buildings.Fluid.Movers.Data;
record Generic "Generic data record for movers"
  extends Modelica.Icons.Record;

  // Pressure requires default values to avoid in Dymola the message
  // Failed to expand the variable pressure.V_flow.
  parameter Buildings.Fluid.Movers.BaseClasses.Characteristics.flowParameters pressure(
    V_flow = {0, 0},
    dp =     {0, 0}) "Volume flow rate vs. total pressure rise"
  final parameter Modelica.Units.SI.VolumeFlowRate V_flow_max=
    if havePressureCurve
      then (pressure.V_flow[end]
                -(pressure.V_flow[end] - pressure.V_flow[end - 1])
                /(pressure.dp[end] - pressure.dp[end - 1])
                * pressure.dp[end])
    else 0
      "Volume flow rate on the curve when pressure rise is zero";
  final parameter Modelica.Units.SI.PressureDifference dpMax(
    displayUnit="Pa")=
    if havePressureCurve
      then (pressure.dp[1]
                -(pressure.dp[1] - pressure.dp[2])
                /(pressure.V_flow[1] - pressure.V_flow[2])
                * pressure.V_flow[1])
    else 0
      "Pressure rise on the curve when flow rate is zero";

  // Efficiency computation choices
  parameter Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod etaHydMet=
    if havePressureCurve
    then Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber
    else Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.NotProvided
    "Efficiency computation method for the hydraulic efficiency etaHyd"
  parameter Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod etaMotMet=
    if powerOrEfficiencyIsHydraulic and havePressureCurve
    then Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve
    else Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided
    "Efficiency computation method for the motor efficiency etaMot"

  parameter Boolean powerOrEfficiencyIsHydraulic=true
    "=true if hydraulic power or efficiency is provided, instead of total"

  // Arrays for efficiency values
  parameter
    Buildings.Fluid.Movers.BaseClasses.Characteristics.efficiencyParameters
    efficiency(
      V_flow={0},
      eta={0.7}) "Total or hydraulic efficiency vs. volumetric flow rate"
  parameter
    Buildings.Fluid.Movers.BaseClasses.Characteristics.efficiencyParameters
    motorEfficiency(
      V_flow={0},
      eta={0.7})
    "Motor efficiency vs. volumetric flow rate"
  parameter
    Buildings.Fluid.Movers.BaseClasses.Characteristics.efficiencyParameters_yMot
    motorEfficiency_yMot(y={0}, eta={0.7})
    "Motor efficiency vs. part load ratio yMot, where yMot = WHyd/WMot_nominal"

  // Power curve
  //   It requires default values to suppress Dymola message
  //   "Failed to expand the variable Power.V_flow"
  parameter Buildings.Fluid.Movers.BaseClasses.Characteristics.powerParameters power(
    V_flow={0},
    P={0})
    "Power (either consumed or hydraulic) vs. volumetric flow rate"

  // Peak condition
  parameter Buildings.Fluid.Movers.BaseClasses.Euler.peak peak(
    V_flow=peak_internal.V_flow,
    dp=peak_internal.dp,
    eta=peak_internal.eta)
    "Volume flow rate, pressure rise, and efficiency (either total or hydraulic) at peak condition"
  final parameter Buildings.Fluid.Movers.BaseClasses.Euler.peak peak_internal=
    if etaHydMet == Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber
    then Buildings.Fluid.Movers.BaseClasses.Euler.getPeak(pressure=pressure,power=power)
    else Buildings.Fluid.Movers.BaseClasses.Euler.peak(V_flow=V_flow_max/2,
                                                   dp=dpMax/2,
                                                   eta=max(efficiency.eta))
    "Internal peak variable";
  // The getPeak() function automatically handles the estimation of peak point
  //   when insufficient information is provided from the pressure curve.

  // Motor
  parameter Boolean motorCooledByFluid=true
    "If true, then motor heat is added to fluid stream"
  parameter Modelica.Units.SI.Power WMot_nominal=
    if max(power.P)>Modelica.Constants.eps
    then
      if powerOrEfficiencyIsHydraulic
        then max(power.P)*1.2
        else max(power.P)
    else
      if havePressureCurve
        then if powerOrEfficiencyIsHydraulic
          then V_flow_max/2 * dpMax/2 /peak.eta*1.2
          else V_flow_max/2 * dpMax/2 /0.7*1.2
        else 0
    "Rated motor power"
  parameter Modelica.Units.SI.Efficiency etaMot_max(max=1)= 0.7
    "Maximum motor efficiency"
  final parameter
    Buildings.Fluid.Movers.BaseClasses.Characteristics.efficiencyParameters_yMot
      motorEfficiency_yMot_generic=
        Buildings.Fluid.Movers.BaseClasses.Characteristics.motorEfficiencyCurve(
          P_nominal=WMot_nominal,
          eta_max=etaMot_max)
    "Motor efficiency  vs. part load ratio"
  final parameter Boolean haveWMot_nominal=WMot_nominal > Modelica.Constants.eps
    "= true, if the rated motor power is provided";

  // Speed
  parameter Real speed_nominal(
    final min=0,
    final unit="1") = 1 "Nominal rotational speed for flow characteristic"

  parameter Real constantSpeed(final min=0, final unit="1") = 1
    "Normalized speed set point, used if inputType = Buildings.Fluid.Types.InputType.Constant"

  parameter Real[:] speeds(each final min = 0, each final unit="1") = {1}
    "Vector of normalized speed set points, used if inputType = Buildings.Fluid.Types.InputType.Stages"

  // Set a parameter in order for
  // (a) FlowControlled_m_flow and FlowControlled_dp to be able to set a reasonable
  //     default pressure curve if it is not specified here, and
  // (b) SpeedControlled_y to be able to issue an assert
  //     if no pressure curve is specified.
  final parameter Boolean havePressureCurve=
    sum(pressure.V_flow) > Modelica.Constants.eps and
    sum(pressure.dp) > Modelica.Constants.eps
    "= true, if default record values are being used";
end Generic;


within Buildings.Fluid.Movers;
package Data "Package containing data for real pumps/fans"
extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Package of fan performance data

within Buildings.Fluid.Movers.Data;
package Fans "Package of fan performance data"
  extends Modelica.Icons.Package;
end Fans;


within Buildings.Fluid.Movers.Data.Fans;
package Greenheck "Package with performance data for fans of Greenheck"
  extends Modelica.Icons.Package;
end Greenheck;


Package containing data for pumps

within Buildings.Fluid.Movers.Data;
package Pumps "Package containing data for pumps"
  extends Modelica.Icons.Package;
end Pumps;


within Buildings.Fluid.Movers.Data.Pumps;
package Wilo "Package with performance data for Wilo pumps"
  extends Modelica.Icons.Package;
end Wilo;


Flow machine with feedback control

within Buildings.Fluid.Movers.Examples;
model ClosedLoop_y "Flow machine with feedback control"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Air;

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=500
    "Nominal pressure difference";

  Modelica.Blocks.Sources.Pulse y(
    offset=0.25,
    startTime=0,
    amplitude=0.5,
    period=15*60) "Input signal"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=2)
  Buildings.Fluid.FixedResistances.PressureDrop dp1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop dp2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
  Buildings.Fluid.Movers.SpeedControlled_y fan(
      redeclare package Medium = Medium,
      per(pressure(V_flow={0,m_flow_nominal,2*m_flow_nominal}/1.2,
                   dp={2*dp_nominal,dp_nominal,0})),
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial) "Fan"
  Buildings.Fluid.Sensors.MassFlowRate senMasFlo(redeclare package Medium =
        Medium)
  Buildings.Controls.Continuous.LimPID conPID(
    Td=1,
    k=0.5,
    Ti=15)
  Modelica.Blocks.Math.Gain gain1(k=1/m_flow_nominal)
equation
  connect(sou.ports[1], senMasFlo.port_a)
  connect(senMasFlo.port_b, dp1.port_a)
  connect(dp1.port_b, fan.port_a)
  connect(fan.port_b, dp2.port_a)
  connect(dp2.port_b, sou.ports[2])
  connect(senMasFlo.m_flow, gain1.u)
  connect(gain1.y, conPID.u_m)
  connect(y.y, conPID.u_s)
  connect(conPID.y, fan.y)
end ClosedLoop_y;


Example model of movers using a real input for setting the stage. This example demonstrates the use of a RealInput
connector for a mover model.

within Buildings.Fluid.Movers.Examples;
model MoverContinuous
  "Example model of movers using a real input for setting the stage"
  extends MoverParameter(
    pump_m_flow(inputType=Buildings.Fluid.Types.InputType.Continuous),
    pump_y(inputType=Buildings.Fluid.Types.InputType.Continuous),
    pump_dp(inputType=Buildings.Fluid.Types.InputType.Continuous));
  Modelica.Blocks.Sources.Ramp ramp(duration=1) "Ramp input for all movers"
  Modelica.Blocks.Math.Gain gai_m_flow(k=m_flow_nominal)
    "Nominal mass flow rate"
  Modelica.Blocks.Math.Gain gai_dp(k=dp_nominal) "Nominal pressure drop"
equation
  connect(gai_dp.y, pump_dp.dp_in)
  connect(gai_m_flow.y, pump_m_flow.m_flow_in)
  connect(gai_m_flow.u, ramp.y)
  connect(gai_dp.u, ramp.y)
  connect(pump_y.y, ramp.y)
end MoverContinuous;


within Buildings.Fluid.Movers.Examples;
model MoverParameter
  "Example model of movers using a parameter for setting the stage"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;

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

  Buildings.Fluid.Movers.FlowControlled_m_flow pump_m_flow(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=m_flow_nominal,
    use_riseTime=false,
    massFlowRates={0,0.5,1}*m_flow_nominal,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump with m_flow input"

  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    nPorts=3) "Fluid source"

  Buildings.Fluid.Sources.Boundary_pT sin(redeclare package Medium = Medium,
      nPorts=3) "Fluid sink"
  Buildings.Fluid.Movers.SpeedControlled_y pump_y(
    redeclare package Medium = Medium,
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos25slash1to4 per(speeds=
          {0,0.5,1}, constantSpeed=1),
    use_riseTime=false,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump with normalised speed input"
  Buildings.Fluid.Movers.FlowControlled_dp pump_dp(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos25slash1to4 per,
    use_riseTime=false,
    heads={0,0.5,1}*dp_nominal,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    dp_nominal=dp_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump with pressure head input"
  Buildings.Fluid.FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal)
    "Pressure drop component for avoiding singular system"
  parameter Modelica.Units.SI.PressureDifference dp_nominal=10000
    "Nominal pressure raise";
equation
  connect(sou.ports[1], pump_m_flow.port_a)
  connect(pump_m_flow.port_b, sin.ports[1])
  connect(res.port_a, pump_dp.port_b)
  connect(pump_y.port_b, sin.ports[2])
  connect(res.port_b, sin.ports[3])
  connect(pump_dp.port_a, sou.ports[2])
  connect(pump_y.port_a, sou.ports[3])
end MoverParameter;


within Buildings.Fluid.Movers.Examples;
model MoverStages
  "Example model of movers using an integer input for setting the stage"
  extends Buildings.Fluid.Movers.Examples.MoverParameter(
    pump_m_flow(inputType=Buildings.Fluid.Types.InputType.Stages),
    pump_y(inputType=Buildings.Fluid.Types.InputType.Stages),
    pump_dp(inputType=Buildings.Fluid.Types.InputType.Stages));
  package Medium = Buildings.Media.Water;

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

  Modelica.Blocks.Sources.IntegerTable integerTable(table=[0,1; 0.3,2; 0.6,3])
    "Integer step input, 1 is off, 2 is on"
  parameter Modelica.Units.SI.PressureDifference dp_nominal=10000
    "Nominal pressure raise";
equation
  connect(integerTable.y, pump_m_flow.stage)
  connect(integerTable.y, pump_y.stage)
  connect(integerTable.y, pump_dp.stage)
end MoverStages;


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


Two flow machines in parallel

within Buildings.Fluid.Movers.Examples;
model PumpsParallel "Two flow machines in parallel"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Water "Medium model";

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

  parameter Modelica.Units.SI.Density rho_nominal=1000
    "Density, used to compute fluid mass";

  Buildings.Fluid.FixedResistances.PressureDrop dpIn1(
    redeclare package Medium = Medium,
    dp_nominal=1000,
    m_flow_nominal=0.5*m_flow_nominal) "Pressure drop"
  Buildings.Fluid.Movers.SpeedControlled_y floMac1(
    redeclare package Medium = Medium,
    per(pressure(V_flow={0, m_flow_nominal/rho_nominal}, dp={2*4*1000, 0})),
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Model of a flow machine"

  Buildings.Fluid.FixedResistances.PressureDrop dpOut1(
    redeclare package Medium = Medium,
    dp_nominal=1000,
    m_flow_nominal=0.5*m_flow_nominal) "Pressure drop"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    nPorts=2,
    T=293.15)

  Buildings.Fluid.FixedResistances.PressureDrop dpIn(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=1000) "Pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop dpOut3(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=1000) "Pressure drop"

  Buildings.Fluid.FixedResistances.PressureDrop dpIn2(
    redeclare package Medium = Medium,
    dp_nominal=1000,
    m_flow_nominal=0.5*m_flow_nominal) "Pressure drop"
  Buildings.Fluid.Movers.SpeedControlled_y floMac2(
    redeclare package Medium = Medium,
    per(pressure(V_flow={0, m_flow_nominal/rho_nominal}, dp={2*4*1000, 0})),
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    inputType=Buildings.Fluid.Types.InputType.Constant) "Model of a flow machine"
  Buildings.Fluid.FixedResistances.PressureDrop dpOut2(
    redeclare package Medium = Medium,
    dp_nominal=1000,
    m_flow_nominal=0.5*m_flow_nominal) "Pressure drop"
  Modelica.Blocks.Sources.Step const1(
    height=-1,
    offset=1,
    startTime=150)
equation
  connect(dpIn1.port_b, floMac1.port_a)
  connect(floMac1.port_b, dpOut1.port_a)
  connect(sou.ports[1], dpIn.port_a)
  connect(dpIn.port_b, dpIn1.port_a)
  connect(dpOut1.port_b, dpOut3.port_a)
  connect(dpOut3.port_b, sou.ports[2])
  connect(dpIn2.port_b,floMac2. port_a)
  connect(floMac2.port_b,dpOut2. port_a)
  connect(dpIn.port_b, dpIn2.port_a)
  connect(dpOut2.port_b, dpOut3.port_a)
  connect(const1.y, floMac1.y)
end PumpsParallel;


Two flow machines in series

within Buildings.Fluid.Movers.Examples;
model PumpsSeries "Two flow machines in series"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;

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

  Buildings.Fluid.Movers.SpeedControlled_y floMac1(
    redeclare package Medium = Medium,
    per(pressure(V_flow={0, m_flow_nominal/1000}, dp={2*4*1000, 0})),
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Model of a flow machine"

  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p(displayUnit="Pa") = 300000,
    T=293.15,
    nPorts=1)

  parameter Medium.ThermodynamicState state_start = Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default) "Start state";
  parameter Modelica.Units.SI.Density rho_nominal=Medium.density(state_start)
    "Density, used to compute fluid mass";

  Buildings.Fluid.Movers.SpeedControlled_y floMac2(
    redeclare package Medium = Medium,
    per(pressure(V_flow={0, m_flow_nominal/1000}, dp={2*4*1000, 0})),
    inputType=Buildings.Fluid.Types.InputType.Constant,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Model of a flow machine"
  Modelica.Blocks.Sources.Step const1(
    height=-1,
    offset=1,
    startTime=150)
  Buildings.Fluid.Sources.Boundary_pT sou1(
    redeclare package Medium = Medium,
    use_p_in=false,
    p(displayUnit="Pa") = 300000 + 4000,
    T=293.15,
    nPorts=1)
equation
  connect(const1.y, floMac1.y)
  connect(floMac1.port_b, floMac2.port_a)
  connect(sou.ports[1], floMac1.port_a)
  connect(floMac2.port_b, sou1.ports[1])
end PumpsSeries;


within Buildings.Fluid.Movers.Examples;
model SpeedControlled_y_pumpCurves
  "Pumps that illustrates the use of the pump curves"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water "Medium model";

  // For OpenModelica, changed m_flow_nominal to a constant. Otherwise
  // the translation fails with "Error: Cyclically dependent parameters found"
  constant Modelica.Units.SI.MassFlowRate m_flow_nominal=0.5
    "Nominal mass flow rate";
  // For OpenModelica, changed dp_nominal to a constant. Otherwise
  // the compilation fails.
  constant Modelica.Units.SI.PressureDifference dp_nominal=10000
    "Nominal pressure";

   model pumpModel = Buildings.Fluid.Movers.SpeedControlled_y (
      redeclare package Medium = Medium,
      use_riseTime=false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      per(pressure(V_flow=2/1000*m_flow_nominal*{0.2,0.4,0.6,0.8}, dp=
              dp_nominal*{0.9,0.85,0.6,0.2})))
    "Declaration of pump model";

  pumpModel pum(
    inputType=Buildings.Fluid.Types.InputType.Constant,
    per(constantSpeed=1)) "Pump"
  pumpModel pum1(
    inputType=Buildings.Fluid.Types.InputType.Constant,
    per(constantSpeed=0.5)) "Pump"
  pumpModel pum2(
    inputType=Buildings.Fluid.Types.InputType.Constant,
    per(constantSpeed=0.05)) "Pump"
  pumpModel pum3(
    inputType=Buildings.Fluid.Types.InputType.Constant,
    per(constantSpeed=0.01)) "Pump"

  Modelica.Blocks.Sources.Ramp y(
    offset=1,
    duration=0.5,
    startTime=0.25,
    height=-0.999)
               "Input signal"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=300000,
    T=293.15,
    nPorts=4)

  Buildings.Fluid.Sources.Boundary_pT sou1(
    redeclare package Medium = Medium,
    use_p_in=false,
    nPorts=4,
    p(displayUnit="Pa") = 300000,
    T=293.15)
  Buildings.Fluid.Actuators.Valves.TwoWayLinear dp1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=0.01*dp_nominal,
    use_strokeTime=false) "Pressure drop"

  Buildings.Fluid.Actuators.Valves.TwoWayLinear dp2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=0.01*dp_nominal,
    use_strokeTime=false) "Pressure drop"

  Buildings.Fluid.Actuators.Valves.TwoWayLinear dp3(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=0.01*dp_nominal,
    use_strokeTime=false) "Pressure drop"
  Buildings.Fluid.Actuators.Valves.TwoWayLinear dp4(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=0.01*dp_nominal,
    use_strokeTime=false) "Pressure drop"
equation
  connect(dp1.port_b, pum.port_a)
  connect(dp1.port_a, sou.ports[1])
  connect(y.y, dp1.y)
  connect(dp2.port_b, pum1.port_a)
  connect(y.y,dp2. y)
  connect(sou.ports[2], dp2.port_a)
  connect(dp3.port_b, pum2.port_a)
  connect(y.y,dp3. y)
  connect(dp3.port_a, sou.ports[3])
  connect(dp4.port_b, pum3.port_a)
  connect(y.y,dp4. y)
  connect(dp4.port_a, sou.ports[4])
  connect(pum3.port_b, sou1.ports[1])
  connect(pum2.port_b, sou1.ports[2])

  connect(pum1.port_b, sou1.ports[3])
  connect(pum.port_b, sou1.ports[4])
end SpeedControlled_y_pumpCurves;


within Buildings.Fluid.Movers.Examples;
model StaticReset
  "Comparing different computation paths with a static pressure reset"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Air;

  // Fans and their performance records
  Buildings.Fluid.Movers.SpeedControlled_y fan1(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare Buildings.Fluid.Movers.Examples.Data.EnglanderNorford1992 per(
      etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate,
      etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided))
    "Fan using power curves"
  Buildings.Fluid.Movers.SpeedControlled_y fan2(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    per(
      powerOrEfficiencyIsHydraulic=fan1.per.powerOrEfficiencyIsHydraulic,
      pressure=fan1.per.pressure,
      etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
      etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided,
      peak(
        V_flow=23.022856,
        dp=1211.1604,
        eta=0.74030644)))
    "Fan using the Euler number"
  Buildings.Fluid.Movers.SpeedControlled_y fan3(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    per(
      powerOrEfficiencyIsHydraulic=fan1.per.powerOrEfficiencyIsHydraulic,
      pressure=fan1.per.pressure,
      etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate,
      etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided,
      efficiency(eta={0.7}))) "Fan using constant efficiency"

  // Define the system curve
  parameter Modelica.Units.SI.VolumeFlowRate V_flow_nominal=21.8
    "Nominal volumetric flow rate of the system";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=V_flow_nominal/1.2
    "Nominal mass flow rate of the system";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=1244.2
    "Nominal pressure rise of the system";

  // Boundaries
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=6) "Boundary"
  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=3) "Boundary"

  // Duct pressure drops
  Buildings.Fluid.FixedResistances.PressureDrop dp11(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/3)
    "Duct pressure drop before the static pressure measurement point"
  Buildings.Fluid.FixedResistances.PressureDrop dp12(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/3)
    "Duct pressure drop after the static pressure measurement point"
  Buildings.Fluid.FixedResistances.PressureDrop dp21(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/3)
    "Duct pressure drop before the static pressure measurement point"
  Buildings.Fluid.FixedResistances.PressureDrop dp22(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/3)
    "Duct pressure drop after the static pressure measurement point"
  Buildings.Fluid.FixedResistances.PressureDrop dp31(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/3)
    "Duct pressure drop before the static pressure measurement point"
  Buildings.Fluid.FixedResistances.PressureDrop dp32(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/3)
    "Duct pressure drop after the static pressure measurement point"

  // PI controllers and components
  Buildings.Fluid.Sensors.RelativePressure pDucSta1(
    redeclare package Medium = Medium)
    "Duct static pressure"
  Buildings.Fluid.Sensors.RelativePressure pDucSta2(
    redeclare package Medium = Medium)
    "Duct static pressure"
  Buildings.Fluid.Sensors.RelativePressure pDucSta3(
    redeclare package Medium = Medium)
    "Duct static pressure"
  Buildings.Controls.Continuous.LimPID conPID1(
    Td=1,
    k=0.5,
    Ti=15) "PI controller"
  Buildings.Controls.Continuous.LimPID conPID2(
    Td=1,
    k=0.5,
    Ti=15) "PI controller"
  Buildings.Controls.Continuous.LimPID conPID3(
    Td=1,
    k=0.5,
    Ti=15) "PI controller"
  Modelica.Blocks.Math.Gain gai1(k=2/dp_nominal) "Gain"
  Modelica.Blocks.Math.Gain gai2(k=2/dp_nominal) "Gain"
  Modelica.Blocks.Math.Gain gai3(k=2/dp_nominal) "Gain"
  Modelica.Blocks.Sources.Constant y(k=1)
    "Duct static pressure setpoint (normalised)"

  // VAV boxes
  Actuators.Dampers.Exponential damExp1(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpDamper_nominal=dp_nominal/3,
    y_start=0) "Damper representing a VAV box"
  Actuators.Dampers.Exponential damExp2(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpDamper_nominal=dp_nominal/3,
    y_start=0) "Damper representing a VAV box"
  Actuators.Dampers.Exponential damExp3(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpDamper_nominal=dp_nominal/3,
    y_start=0) "Damper representing a VAV box"
  Modelica.Blocks.Sources.Ramp yDam(
    height=1,
    duration=3600,
    offset=0) "Ramp input for damper position"

equation
  connect(y.y, conPID3.u_s)
  connect(pDucSta3.p_rel, gai3.u)
  connect(gai3.y, conPID3.u_m)
  connect(conPID3.y, fan3.y)
  connect(fan3.port_b, dp31.port_a)
  connect(dp31.port_b, dp32.port_a)
  connect(fan2.port_b, dp21.port_a)
  connect(dp21.port_b, dp22.port_a)
  connect(pDucSta2.p_rel, gai2.u)
  connect(gai2.y, conPID2.u_m)
  connect(pDucSta2.port_a, dp22.port_a)
  connect(y.y, conPID2.u_s)
  connect(conPID2.y, fan2.y)
  connect(fan1.port_b, dp11.port_a)
  connect(dp11.port_b, dp12.port_a)
  connect(pDucSta1.p_rel,gai1. u)
  connect(gai1.y,conPID1. u_m)
  connect(y.y, conPID1.u_s)
  connect(pDucSta1.port_a, dp12.port_a)
  connect(conPID1.y, fan1.y)
  connect(pDucSta3.port_a, dp32.port_a)
  connect(fan3.port_a, sou.ports[1])
  connect(fan2.port_a, sou.ports[2])
  connect(fan1.port_a, sou.ports[3])
  connect(pDucSta3.port_b, sou.ports[4])
  connect(pDucSta2.port_b, sou.ports[5])
  connect(pDucSta1.port_b, sou.ports[6])
  connect(dp12.port_b, damExp1.port_a)
  connect(damExp1.port_b, sin.ports[1])
  connect(yDam.y, damExp1.y)
  connect(dp22.port_b,damExp2. port_a)
  connect(damExp2.port_b, sin.ports[2])
  connect(dp32.port_b,damExp3. port_a)
  connect(damExp3.port_b, sin.ports[3])
  connect(yDam.y, damExp2.y)
  connect(yDam.y, damExp3.y)
end StaticReset;


Package containing data for examples

within Buildings.Fluid.Movers.Examples;
package Data "Package containing data for examples"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Fan or pump with ideally controlled head dp as input signal and pre-configured parameters.

within Buildings.Fluid.Movers.Preconfigured;
model FlowControlled_dp "Fan or pump with ideally controlled head dp as input signal and pre-configured parameters"
  extends Buildings.Fluid.Movers.FlowControlled_dp(
    final per(
            pressure(
              V_flow=m_flow_nominal/rho_default*{0, 1, 2},
              dp=if rho_default < 500
                   then dp_nominal*{1.12, 1, 0}
                   else dp_nominal*{1.14, 1, 0.42}),
            powerOrEfficiencyIsHydraulic=true,
            etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
            etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
    final constantHead,
    final heads,
    final nominalValuesDefineDefaultPressureCurve=true,
    final inputType=Buildings.Fluid.Types.InputType.Continuous,
    final prescribeSystemPressure=false,
    final init=Modelica.Blocks.Types.Init.InitialOutput,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial);
end FlowControlled_dp;


Fan or pump with ideally controlled mass flow rate as input signal and pre-configured parameters.

within Buildings.Fluid.Movers.Preconfigured;
model FlowControlled_m_flow "Fan or pump with ideally controlled mass flow rate as input signal and pre-configured parameters"
  extends Buildings.Fluid.Movers.FlowControlled_m_flow(
    final per(
            pressure(
              V_flow=m_flow_nominal/rho_default*{0, 1, 2},
              dp=if rho_default < 500
                   then dp_nominal*{1.12, 1, 0}
                   else dp_nominal*{1.14, 1, 0.42}),
            powerOrEfficiencyIsHydraulic=true,
            etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
            etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
    final constantMassFlowRate,
    final massFlowRates,
    final nominalValuesDefineDefaultPressureCurve=true,
    final inputType=Buildings.Fluid.Types.InputType.Continuous,
    final init=Modelica.Blocks.Types.Init.InitialOutput,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial);
end FlowControlled_m_flow;


Preconfigured mover models

within Buildings.Fluid.Movers;
package Preconfigured "Preconfigured mover models"
  extends Modelica.Icons.Package;
end Preconfigured;


Fan or pump with ideally controlled normalized speed y as input signal and pre-configured parameters.

within Buildings.Fluid.Movers.Preconfigured;
model SpeedControlled_y "Fan or pump with ideally controlled normalized speed y as input signal and pre-configured parameters"
  extends Buildings.Fluid.Movers.SpeedControlled_y(
    final per(
            pressure(
              V_flow=m_flow_nominal/rho_default*{0, 1, 2},
              dp=if rho_default < 500
                   then dp_nominal*{1.12, 1, 0}
                   else dp_nominal*{1.14, 1, 0.42}),
            powerOrEfficiencyIsHydraulic=true,
            etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
            etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
    final inputType=Buildings.Fluid.Types.InputType.Continuous,
    final init=Modelica.Blocks.Types.Init.InitialOutput,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial);

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal(
    final min=Modelica.Constants.small)
    "Nominal mass flow rate for configuration of pressure curve"
  parameter Modelica.Units.SI.PressureDifference dp_nominal(
    final min=Modelica.Constants.small)
    "Nominal pressure head for configuration of pressure curve"
end SpeedControlled_y;


User's Guide

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

end UsersGuide;


within Buildings.Fluid.Movers.Preconfigured.Validation;
model ControlledFlowMachinePreconfigured
  "Preconfigured fans with different control signals"
  extends Modelica.Icons.Example;
  extends Buildings.Fluid.Movers.Validation.BaseClasses.ControlledFlowMachine(
    redeclare Buildings.Fluid.Movers.Preconfigured.SpeedControlled_y fan1(
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal),
    redeclare Buildings.Fluid.Movers.Preconfigured.FlowControlled_m_flow fan2(
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal),
    redeclare Buildings.Fluid.Movers.Preconfigured.FlowControlled_dp fan3(
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal),
    dp1(m_flow_nominal=m_flow_nominal, dp_nominal=dp_nominal),
    dp2(m_flow_nominal=m_flow_nominal, dp_nominal=dp_nominal),
    dp3(m_flow_nominal=m_flow_nominal, dp_nominal=dp_nominal));

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=2
    "Nominal mass flow rate for each fan";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=500
    "Nominal pressure head for each fan";
end ControlledFlowMachinePreconfigured;


Package for validation models

within Buildings.Fluid.Movers.Preconfigured;
package Validation "Package for validation models"
extends Modelica.Icons.ExamplesPackage;
end Validation;


within Buildings.Fluid.Movers.Validation;
model ComparePowerHydraulic
  "Compare power estimation with hydraulic power curve"
  extends Modelica.Icons.Example;
  extends Buildings.Fluid.Movers.Validation.BaseClasses.ComparePower(
    redeclare replaceable Buildings.Fluid.Movers.Data.Fans.Greenheck.BIDW15 per(
      etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate,
      motorEfficiency(V_flow={0}, eta={0.7}))
      constrainedby Buildings.Fluid.Movers.Data.Generic,
    redeclare Buildings.Fluid.Movers.SpeedControlled_y mov1(
      redeclare final package Medium = Medium,
      per=per,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      addPowerToMedium=false,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.SpeedControlled_y mov2(
      redeclare final package Medium = Medium,
      per(
        powerOrEfficiencyIsHydraulic=per.powerOrEfficiencyIsHydraulic,
        pressure=per.pressure,
        etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
        peak=peak,
        etaMotMet=per.etaMotMet),
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      addPowerToMedium=false,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.SpeedControlled_y mov3(
      redeclare final package Medium = Medium,
      per(
        powerOrEfficiencyIsHydraulic=per.powerOrEfficiencyIsHydraulic,
        pressure=per.pressure,
        etaHydMet=Buildings.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate,
        efficiency(V_flow={peak.V_flow}, eta={peak.eta}),
        etaMotMet=per.etaMotMet),
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      addPowerToMedium=false,
      use_riseTime=false));

  final parameter Buildings.Fluid.Movers.BaseClasses.Euler.peak peak =
    Buildings.Fluid.Movers.BaseClasses.Euler.getPeak(
      pressure=per.pressure,
      power=per.power)
    "Peak operating point";

equation
  connect(ramSpe.y, mov2.y)
  connect(ramSpe.y, mov3.y)
  connect(ramSpe.y, mov1.y)
end ComparePowerHydraulic;


within Buildings.Fluid.Movers.Validation;
model ComparePowerInput
  "Compare power estimation with different input signal"
  extends Modelica.Icons.Example;
  extends Buildings.Fluid.Movers.Validation.BaseClasses.ComparePower(
    redeclare Buildings.Fluid.Movers.Data.Fans.Greenheck.BIDW15 per(
      etaMotMet=Buildings.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate,
      motorEfficiency(V_flow={0}, eta={0.7})),
    redeclare Buildings.Fluid.Movers.SpeedControlled_y mov1(
      redeclare final package Medium = Medium,
      per=per,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      addPowerToMedium=false,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.FlowControlled_dp mov2(
      redeclare final package Medium = Medium,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      per=per,
      addPowerToMedium=false,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.FlowControlled_m_flow mov3(
      redeclare final package Medium = Medium,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      per=per,
      addPowerToMedium=false,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal,
      use_riseTime=false),
    ramDam(height=-0.5));
  Modelica.Blocks.Sources.RealExpression exp_dp(y=mov1.dpMachine)
    "Expression to impose the same pressure rise"
  Modelica.Blocks.Sources.RealExpression exp_m_flow(y=mov1.m_flow)
    "Expression to impose the same mass flow rate"
equation
  connect(exp_dp.y, mov2.dp_in)
  connect(exp_m_flow.y, mov3.m_flow_in)
  connect(ramSpe.y, mov1.y)
end ComparePowerInput;


Compare power estimation with total power curve. This validation model is similar to

Buildings.Fluid.Movers.Validation.ComparePowerHydraulic,
with the difference being the fan components are replaced with pump components
with per.powerOrEfficiencyIsHydraulic=false.

within Buildings.Fluid.Movers.Validation;
model ComparePowerTotal
  "Compare power estimation with total power curve"
  extends Buildings.Fluid.Movers.Validation.ComparePowerHydraulic(
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos40slash1to12 per);
end ComparePowerTotal;


Fans with different control signals as input.

within Buildings.Fluid.Movers.Validation;
model ControlledFlowMachine "Fans with different control signals as input"
  extends Modelica.Icons.Example;
  extends Buildings.Fluid.Movers.Validation.BaseClasses.ControlledFlowMachine(
    fan1(addPowerToMedium=false, energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
    fan2(
      addPowerToMedium=false,
      use_riseTime=false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
    fan3(
      addPowerToMedium=false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      use_riseTime=false));
end ControlledFlowMachine;


Fans with different control signals as input and a dynamic speed signal.

within Buildings.Fluid.Movers.Validation;
model ControlledFlowMachineDynamic
  "Fans with different control signals as input and a dynamic speed signal"
  extends Modelica.Icons.Example;
  extends Buildings.Fluid.Movers.Validation.BaseClasses.ControlledFlowMachine(
    fan1(energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
    fan2(energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
    fan3(energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));

end ControlledFlowMachineDynamic;


within Buildings.Fluid.Movers.Validation;
model FlowControlled_dp "Fan with zero mass flow rate and head as input"
  extends Modelica.Icons.Example;
 extends Buildings.Fluid.Movers.Validation.BaseClasses.FlowMachine_ZeroFlow(
    gain(k=dp_nominal),
    redeclare Buildings.Fluid.Movers.FlowControlled_dp floMacSta(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.FlowControlled_dp floMacDyn(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      use_riseTime=false));

equation
  connect(gain.y, floMacSta.dp_in)
  connect(gain.y, floMacDyn.dp_in)
end FlowControlled_dp;


within Buildings.Fluid.Movers.Validation;
model FlowControlled_dpSystem
  "Demonstration of the use of prescribedPressure"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Air;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=100
    "Nominal pressure difference";
  Modelica.Blocks.Sources.Ramp y(
    duration=0.5,
    startTime=0.25,
    height=-dp_nominal,
    offset=dp_nominal)
               "Input signal"
  Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    nPorts=2)
    "Source"
  Buildings.Fluid.Movers.FlowControlled_dp floConDp(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=1,
    use_riseTime=false) "Regular dp controlled fan"
  Buildings.Fluid.Movers.FlowControlled_dp floConDpSystem(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=1,
    use_riseTime=false,
    prescribeSystemPressure=true)
    "Dp controlled fan that sets pressure difference at remote point in the system"
  Buildings.Fluid.FixedResistances.PressureDrop heaCoi2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Heating coil pressure drop"
  Sensors.RelativePressure senRelPre(redeclare package Medium = Medium)
    "Pressure difference across air system"

  Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=4) "Sink"
  MixingVolumes.MixingVolume zone2(
    redeclare package Medium = Medium,
    V=50,
    m_flow_nominal=m_flow_nominal,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Mixing volume"
  Buildings.Fluid.FixedResistances.PressureDrop heaCoi1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Heating coil pressure drop"
  Actuators.Dampers.Exponential dam2(
    redeclare package Medium = Medium,
    from_dp=true,
    use_strokeTime=false,
    dpDamper_nominal=10,
    m_flow_nominal=m_flow_nominal/2) "Damper"
  MixingVolumes.MixingVolume zone1(
    redeclare package Medium = Medium,
    V=50,
    m_flow_nominal=m_flow_nominal,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Mixing volume"
  Actuators.Dampers.Exponential dam1(
    redeclare package Medium = Medium,
    from_dp=true,
    use_strokeTime=false,
    dpDamper_nominal=10,
    m_flow_nominal=m_flow_nominal/2) "Damper"
  Actuators.Dampers.Exponential dam3(
    redeclare package Medium = Medium,
    from_dp=true,
    use_strokeTime=false,
    dpDamper_nominal=10,
    m_flow_nominal=m_flow_nominal/2) "Damper"
  Actuators.Dampers.Exponential dam4(
    redeclare package Medium = Medium,
    from_dp=true,
    use_strokeTime=false,
    dpDamper_nominal=10,
    m_flow_nominal=m_flow_nominal/2) "Damper"
  MixingVolumes.MixingVolume zone3(
    redeclare package Medium = Medium,
    V=50,
    m_flow_nominal=m_flow_nominal,
    nPorts=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Mixing volume"
  MixingVolumes.MixingVolume zone4(
    redeclare package Medium = Medium,
    V=50,
    m_flow_nominal=m_flow_nominal,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Mixing volume"
  Modelica.Blocks.Sources.Ramp y1(
    duration=0.5,
    height=1,
    offset=0,
    startTime=0)
    "Input signal"
  Buildings.Fluid.FixedResistances.PressureDrop duct3(
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal/2,
    m_flow_nominal=m_flow_nominal/2) "Duct pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop duct4(
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal/2,
    m_flow_nominal=m_flow_nominal/2) "Duct pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop duct1(
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal/2,
    m_flow_nominal=m_flow_nominal/2) "Duct pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop duct2(
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal/2,
    m_flow_nominal=m_flow_nominal/2) "Duct pressure drop"
equation
  connect(y.y, floConDp.dp_in)
  connect(y.y, floConDpSystem.dp_in)
  connect(zone2.ports[1], sin.ports[1])
  connect(senRelPre.p_rel, floConDpSystem.dpMea)
  connect(floConDp.port_a, sou.ports[1])
  connect(floConDpSystem.port_a, sou.ports[2])
  connect(zone1.ports[1], sin.ports[2])
  connect(heaCoi1.port_a, floConDp.port_b)
  connect(heaCoi2.port_a, floConDpSystem.port_b)
  connect(dam2.port_b, zone1.ports[2])
  connect(dam1.port_b, zone2.ports[2])
  connect(zone3.ports[1], sin.ports[3])
  connect(zone4.ports[1], sin.ports[4])
  connect(dam3.port_b, zone3.ports[2])
  connect(zone4.ports[2], dam4.port_b)
  connect(y1.y, dam2.y)
  connect(senRelPre.port_b, zone3.ports[3])
  connect(senRelPre.port_a, heaCoi2.port_b)
  connect(duct3.port_b, dam3.port_a)
  connect(duct4.port_b, dam4.port_a)
  connect(duct2.port_a, heaCoi1.port_b)
  connect(heaCoi1.port_b, duct1.port_a)
  connect(duct1.port_b, dam2.port_a)
  connect(duct2.port_b, dam1.port_a)
  connect(duct3.port_a, heaCoi2.port_b)
  connect(duct4.port_a, heaCoi2.port_b)
  connect(y1.y, dam1.y)
  connect(y1.y, dam3.y)
  connect(y1.y, dam4.y)
end FlowControlled_dpSystem;


within Buildings.Fluid.Movers.Validation;
model FlowControlled_m_flow
  "Fan with zero mass flow rate and mass flow rate as input"
  extends Modelica.Icons.Example;
 extends Buildings.Fluid.Movers.Validation.BaseClasses.FlowMachine_ZeroFlow(
    gain(k=m_flow_nominal),
    redeclare Buildings.Fluid.Movers.FlowControlled_m_flow floMacSta(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.FlowControlled_m_flow floMacDyn(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      use_riseTime=false));

equation
  connect(gain.y, floMacSta.m_flow_in)
  connect(gain.y, floMacDyn.m_flow_in)
end FlowControlled_m_flow;


Collection of validation models

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


within Buildings.Fluid.Movers.Validation;
model PressureCurve "Displays the pressure curve of the mover"
  extends Modelica.Icons.Example;

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

  parameter Modelica.Units.SI.Density rho_default=
    Medium.density_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default) "Default medium density";
  parameter Buildings.Fluid.Movers.Data.Generic per(
    pressure(V_flow={0.945419103313839, 2.83300844704353, 4.71734892787522},
                 dp={ 3010.50788091068, 2632.22416812609, 830.122591943958}))
    "Performance data"

  Buildings.Fluid.Movers.SpeedControlled_y mov(
    redeclare final package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per,
    addPowerToMedium=false,
    y_start=1) "Mover"
  Modelica.Blocks.Sources.Constant one(final k=1) "Constant one"
  Buildings.Fluid.Sources.MassFlowSource_T bou1(
    redeclare final package Medium = Medium,
    final use_m_flow_in=true,
    nPorts=1) "Boundary that forces a mass flow rate"
  Modelica.Blocks.Sources.Ramp ram(
    height=per.V_flow_max*rho_default,
    duration=1,
    offset=0) "Ramp signal"
  Buildings.Fluid.Sources.Boundary_pT bou2(
    redeclare final package Medium = Medium,
    nPorts=1) "Boundary"

equation
  connect(one.y, mov.y)
  connect(ram.y, bou1.m_flow_in)
  connect(bou1.ports[1], mov.port_a)
  connect(mov.port_b, bou2.ports[1])
end PressureCurve;


within Buildings.Fluid.Movers.Validation;
model PumpCurveConstruction
  "Validation model that tests that the pump curve is properly extrapolated to V=0 and dp=0"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Water "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate at zero pump head";
  parameter Modelica.Units.SI.VolumeFlowRate V_flow_nominal=m_flow_nominal/1000
    "Nominal mass flow rate at zero pump head";

  parameter Modelica.Units.SI.PressureDifference dp_nominal=10000
    "Nominal pump head at zero mass flow rate";

  Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    nPorts=8,
    p=101325,
    T=293.15) "Pressure boundary condition"

  Buildings.Fluid.Movers.SpeedControlled_y pum(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_riseTime=false,
    per(pressure(V_flow={0,0.5*V_flow_nominal,V_flow_nominal}, dp={dp_nominal,
            0.5*dp_nominal,0})),
    inputType=Buildings.Fluid.Types.InputType.Constant)
    "Pump with 3 data points for the pressure flow relation"

  Buildings.Fluid.Movers.SpeedControlled_y pum_dp(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_riseTime=false,
    per(pressure(V_flow={0.5*V_flow_nominal,0.75*V_flow_nominal,V_flow_nominal},
          dp={0.5*dp_nominal,0.25*dp_nominal,0})),
    inputType=Buildings.Fluid.Types.InputType.Constant)
    "Pump with 2 data points for the pressure flow relation, with data at dp=0"

  Buildings.Fluid.Movers.SpeedControlled_y pum_m_flow(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_riseTime=false,
    per(pressure(V_flow={0,0.25*V_flow_nominal,0.5*V_flow_nominal}, dp={
            dp_nominal,0.75*dp_nominal,0.5*dp_nominal})),
    inputType=Buildings.Fluid.Types.InputType.Constant)
    "Pump with 2 data points for the pressure flow relation, with data at m_flow=0"

  Buildings.Fluid.Movers.SpeedControlled_y pum_no(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_riseTime=false,
    per(pressure(V_flow={0.25*V_flow_nominal,0.5*V_flow_nominal,0.75*
            V_flow_nominal}, dp={0.75*dp_nominal,0.5*dp_nominal,0.25*dp_nominal})),
    inputType=Buildings.Fluid.Types.InputType.Constant)
    "Pump with 2 data points for the pressure flow relation, with no data at m_flow=0 and dp=0"

  Modelica.Blocks.Sources.Ramp yVal(
    duration=1,
    offset=1,
    height=-0.99) "Input signal for valve"

  Actuators.Valves.TwoWayLinear val1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_strokeTime=false,
    dpValve_nominal=dp_nominal/1000,
    from_dp=false) "Valve with very small pressure drop if fully open"

  Actuators.Valves.TwoWayLinear val2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_strokeTime=false,
    dpValve_nominal=dp_nominal/1000,
    from_dp=false) "Valve with very small pressure drop if fully open"

  Actuators.Valves.TwoWayLinear val3(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_strokeTime=false,
    dpValve_nominal=dp_nominal/1000,
    from_dp=false) "Valve with very small pressure drop if fully open"

  Actuators.Valves.TwoWayLinear val4(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_strokeTime=false,
    dpValve_nominal=dp_nominal/1000,
    from_dp=false) "Valve with very small pressure drop if fully open"

equation
  connect(pum.port_a, val1.port_b)
  connect(val1.port_a, sou.ports[1])
  connect(yVal.y, val1.y)
  connect(pum_dp.port_a, val2.port_b)
  connect(pum_m_flow.port_a, val3.port_b)
  connect(sou.ports[2], val2.port_a)
  connect(val3.port_a, sou.ports[3])
  connect(yVal.y, val2.y)
  connect(yVal.y, val3.y)

  connect(val4.port_a, sou.ports[4])
  connect(val4.port_b, pum_no.port_a)
  connect(yVal.y, val4.y)
  connect(pum_no.port_b, sou.ports[5])
  connect(pum_m_flow.port_b, sou.ports[6])
  connect(pum_dp.port_b, sou.ports[7])
  connect(pum.port_b, sou.ports[8])
end PumpCurveConstruction;


within Buildings.Fluid.Movers.Validation;
model PumpCurveDerivatives
  "Check for monotoneously increasing pump curve relations between y, dp and m_flow"
  extends Modelica.Icons.Example;
  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;

  parameter Data.Pumps.Wilo.Stratos80slash1to12 per "Pump performance data"

  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    nPorts=2) "Boundary condition with fixed pressure"

  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium =Medium,
    nPorts=2) "Boundary condition with fixed pressure"

  Modelica.Blocks.Sources.Ramp m_flow(
    height=60/3.6,
    offset=0,
    duration=1,
    startTime=0) "Ramp signal for forced mass flow rate"

  Buildings.Fluid.Movers.SpeedControlled_y pump1(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per,
    use_riseTime=false) "Wilo Stratos pump"
  Buildings.Fluid.Movers.SpeedControlled_y pump2(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per,
    use_riseTime=false) "Wilo Stratos pump"

  Buildings.Fluid.Movers.FlowControlled_m_flow forcedPump1(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_riseTime=false) "Pump for forcing a certain mass flow rate"

  Modelica.Blocks.Sources.Constant y1(k=1000/2610) "Pump speed control signal"

  Modelica.Blocks.Math.Min min1
    "Minimum for not going outside of the figure range (see documentation)"

  Modelica.Blocks.Sources.Constant mMax_flow(k=40/3.6)
    "Maximum flow rate of the pump at given speed"

  FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=40/3.6,
    dp_nominal=7e4) "Pressure drop component"
  Modelica.Blocks.Sources.Ramp y2(
    duration=1,
    startTime=0,
    height=100/2610,
    offset=0) "Ramp signal for speed"
  Sensors.RelativePressure relPre(redeclare package Medium =
        Medium)
  Modelica.Blocks.Continuous.Derivative ddp_dm_flow(
    initType=Modelica.Blocks.Types.Init.InitialState,
    x_start=-11502.5)
    "Derivative of dp for changing m_flow"
  Buildings.Fluid.Sensors.RelativePressure relPre1(
    redeclare package Medium = Medium)
  Modelica.Blocks.Continuous.Derivative ddp_dy(initType=Modelica.Blocks.Types.Init.InitialState)
    "Derivative of dp for changing speed"
  Sensors.MassFlowRate senMasFlo(redeclare package Medium = Medium)
    "Mass flow rate sensor"
  Modelica.Blocks.Continuous.Derivative dm_flow_dy(initType=Modelica.Blocks.Types.Init.InitialState)
    "Derivative of m_flow for changing speed"
equation
  connect(sou.ports[1], pump1.port_a)
  connect(forcedPump1.port_a, pump1.port_b)
  connect(pump1.y, y1.y)
  connect(pump2.port_a, sou.ports[2])

  connect(forcedPump1.m_flow_in, min1.y)
  connect(min1.u1, m_flow.y)
  connect(mMax_flow.y, min1.u2)
  connect(forcedPump1.port_b, sin.ports[1])

  connect(res.port_b, sin.ports[2])
  connect(y2.y, pump2.y)
  connect(ddp_dm_flow.u, relPre.p_rel)
  connect(relPre1.port_b, pump2.port_a)
  connect(relPre1.port_a, pump2.port_b)
  connect(ddp_dy.u, relPre1.p_rel)
  connect(senMasFlo.port_a, pump2.port_b)
  connect(senMasFlo.port_b, res.port_a)
  connect(senMasFlo.m_flow, dm_flow_dy.u)
  connect(relPre.port_b, pump1.port_b)
  connect(relPre.port_a, pump1.port_a)
end PumpCurveDerivatives;


Stratos pumps with speed as input

within Buildings.Fluid.Movers.Validation;
model Pump_stratos "Stratos pumps with speed as input"
  extends Modelica.Icons.Example;
 extends Buildings.Fluid.Movers.Validation.BaseClasses.FlowMachine_ZeroFlow(
    redeclare package Medium = Buildings.Media.Water,
    gain(k=1),
    m_flow_nominal=floMacSta.per.pressure.V_flow[3]*1000,
    dp_nominal=floMacSta.per.pressure.dp[3]/2,
    redeclare Buildings.Fluid.Movers.SpeedControlled_y floMacSta(
      redeclare package Medium = Medium,
      per=per,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.SpeedControlled_y floMacDyn(
      redeclare package Medium = Medium,
      per=per,
      use_riseTime=false));
  parameter Data.Pumps.Wilo.Stratos25slash1to6 per
equation
  connect(gain.y, floMacSta.y)
  connect(gain.y, floMacDyn.y)
end Pump_stratos;


within Buildings.Fluid.Movers.Validation;
model Pump_y_stratos "Model validation using a Wilo Stratos 80/1-12 pump"
  extends Modelica.Icons.Example;
  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;

  parameter Data.Pumps.Wilo.Stratos80slash1to12 per "Pump performance data"

  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    nPorts=5) "Boundary condition with fixed pressure"

  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium =Medium,
    nPorts=5) "Boundary condition with fixed pressure"

  Modelica.Blocks.Sources.Ramp m_flow(
    startTime=100,
    duration=800,
    height=60/3.6,
    offset=0) "Ramp signal for forced mass flow rate"

  Buildings.Fluid.Movers.SpeedControlled_y pump1(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  Buildings.Fluid.Movers.SpeedControlled_y pump2(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  Buildings.Fluid.Movers.SpeedControlled_y pump3(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  Buildings.Fluid.Movers.SpeedControlled_y pump4(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  Buildings.Fluid.Movers.SpeedControlled_y pump5(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"

  Buildings.Fluid.Movers.FlowControlled_m_flow forcedPump1(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"
  Buildings.Fluid.Movers.FlowControlled_m_flow forcedPump2(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"
  Buildings.Fluid.Movers.FlowControlled_m_flow forcedPump3(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"
  Buildings.Fluid.Movers.FlowControlled_m_flow forcedPump4(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"
  Buildings.Fluid.Movers.FlowControlled_m_flow forcedPump5(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"

  Modelica.Blocks.Sources.Constant y1(k=2960/2610) "Pump speed control signal"
  Modelica.Blocks.Sources.Constant y2(k=2610/2610) "Pump speed control signal"
  Modelica.Blocks.Sources.Constant y3(k=1930/2610) "Pump speed control signal"
  Modelica.Blocks.Sources.Constant y4(k=3300/2610) "Pump speed control signal"
  Modelica.Blocks.Sources.Constant y5(k=900/2610) "Pump speed control signal"

  Modelica.Blocks.Math.Min min1
    "Minimum for not going outside of the figure range (see documentation)"
  Modelica.Blocks.Math.Min min2
    "Minimum for not going outside of the figure range (see documentation)"
  Modelica.Blocks.Math.Min min3
    "Minimum for not going outside of the figure range (see documentation)"
  Modelica.Blocks.Math.Min min4
    "Minimum for not going outside of the figure range (see documentation)"
  Modelica.Blocks.Math.Min min5
    "Minimum for not going outside of the figure range (see documentation)"

  Modelica.Blocks.Sources.Constant mMax_flow1(k=40/3.6)
    "Maximum flow rate of the pump at given speed"
  Modelica.Blocks.Sources.Constant mMax_flow2(k=55/3.6)
    "Maximum flow rate of the pump at given speed"
  Modelica.Blocks.Sources.Constant mMax_flow3(k=40/3.6)
    "Maximum flow rate of the pump at given speed"
  Modelica.Blocks.Sources.Constant mMax_flow4(k=22/3.6)
    "Maximum flow rate of the pump at given speed"
  Modelica.Blocks.Sources.Constant mMax_flow5(k=16/3.6)
    "Maximum flow rate of the pump at given speed"

equation
  connect(sou.ports[1], pump1.port_a)
  connect(forcedPump1.port_a, pump1.port_b)
  connect(pump1.y, y1.y)
  connect(pump2.port_a, sou.ports[2])
  connect(pump3.port_a, sou.ports[3])
  connect(pump4.port_a, sou.ports[4])

  connect(pump5.port_a, sou.ports[5])
  connect(pump2.port_b, forcedPump2.port_a)
  connect(pump3.port_b, forcedPump3.port_a)
  connect(pump4.port_b, forcedPump4.port_a)
  connect(pump5.port_b, forcedPump5.port_a)
  connect(y2.y, pump2.y)
  connect(y3.y, pump3.y)
  connect(y4.y, pump4.y)
  connect(y5.y, pump5.y)
  connect(forcedPump1.m_flow_in, min1.y)
  connect(min1.u1, m_flow.y)
  connect(min2.y, forcedPump2.m_flow_in)
  connect(min2.u1, m_flow.y)
  connect(min3.y, forcedPump3.m_flow_in)
  connect(min5.y, forcedPump5.m_flow_in)
  connect(min4.y, forcedPump4.m_flow_in)
  connect(min3.u1, m_flow.y)
  connect(min4.u1, m_flow.y)
  connect(min5.u1, m_flow.y)
  connect(mMax_flow1.y, min1.u2)
  connect(mMax_flow2.y, min2.u2)
  connect(min3.u2, mMax_flow3.y)
  connect(mMax_flow4.y, min4.u2)
  connect(mMax_flow5.y, min5.u2)
  connect(forcedPump1.port_b, sin.ports[1])
  connect(forcedPump2.port_b, sin.ports[2])
  connect(forcedPump3.port_b, sin.ports[3])
  connect(forcedPump4.port_b, sin.ports[4])
  connect(forcedPump5.port_b, sin.ports[5])
end Pump_y_stratos;


within Buildings.Fluid.Movers.Validation;
model SpeedControlled_y
  "Fan with zero mass flow rate and control signal y as input"
  extends Modelica.Icons.Example;
 extends Buildings.Fluid.Movers.Validation.BaseClasses.FlowMachine_ZeroFlow(
    gain(k=1),
    redeclare Buildings.Fluid.Movers.SpeedControlled_y floMacSta(
      redeclare package Medium = Medium,
      per(pressure(V_flow={0,m_flow_nominal,2*m_flow_nominal}/1.2,
                    dp={2*dp_nominal,dp_nominal,0})),
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      use_riseTime=false),
    redeclare Buildings.Fluid.Movers.SpeedControlled_y floMacDyn(
      redeclare package Medium = Medium,
      per(pressure(V_flow={0,m_flow_nominal,2*m_flow_nominal}/1.2,
                    dp={2*dp_nominal,dp_nominal,0})),
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      use_riseTime=false));

equation
  connect(gain.y, floMacDyn.y)
  connect(gain.y, floMacSta.y)
end SpeedControlled_y;


within Buildings.Fluid.Movers.Validation;
model SpeedControlled_y_linear
  "Pump with linear characteristic for pressure vs. flow rate"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water "Medium model";

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

  Modelica.Blocks.Sources.Ramp y(
    offset=1,
    duration=0.5,
    startTime=0.25,
    height=-1) "Input signal"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=300000,
    T=293.15,
    nPorts=1)
  Buildings.Fluid.Movers.SpeedControlled_y pumFixDp(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per(pressure(V_flow=2/1000*{0,m_flow_nominal}, dp={2*dp_nominal,0})),
    use_riseTime=false) "Pump with fixed pressure raise"

  Buildings.Fluid.Sources.Boundary_pT sou1(
    redeclare package Medium = Medium,
    use_p_in=false,
    p(displayUnit="Pa") = 300000 + 0.01*dp_nominal,
    T=293.15,
    nPorts=1)
  Buildings.Fluid.FixedResistances.PressureDrop dp1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=0.01*dp_nominal) "Pressure drop"
  Buildings.Fluid.Sources.MassFlowSource_T sou2(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=m_flow_nominal*0.01,
    T=293.15)
  Buildings.Fluid.Movers.SpeedControlled_y pumFixM_flow(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per(pressure(V_flow=2/1000*{0,m_flow_nominal}, dp={2*dp_nominal,0})),
    use_riseTime=false) "Pump with fixed mass flow rate"
  Buildings.Fluid.Sources.Boundary_pT sou3(
    redeclare package Medium = Medium,
    use_p_in=false,
    p(displayUnit="Pa") = 300000 + 0.01*dp_nominal,
    T=293.15,
    nPorts=1)
equation
  connect(pumFixDp.port_b, sou1.ports[1])
  connect(dp1.port_b, pumFixDp.port_a)
  connect(dp1.port_a, sou.ports[1])
  connect(pumFixM_flow.port_b, sou3.ports[1])
  connect(sou2.ports[1], pumFixM_flow.port_a)
  connect(y.y, pumFixDp.y)
  connect(y.y, pumFixM_flow.y)
end SpeedControlled_y_linear;


within Buildings.Fluid.Movers.Validation.BaseClasses;
model ComparePower
  "Base class for validation models that compare power estimation methods"

  package Medium = Buildings.Media.Water "Medium model";

  replaceable parameter Buildings.Fluid.Movers.Data.Generic per
    constrainedby Buildings.Fluid.Movers.Data.Generic
    "Performance records"

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal =
    per.peak.V_flow * rho_default
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal =
    per.peak.dp
    "Nominal pressure drop";
  final parameter Modelica.Units.SI.Density rho_default=
    Medium.density_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default) "Default medium density";

  replaceable Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine mov1
    constrainedby Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine
    "Mover (fan or pump)"
  replaceable Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine mov2
    constrainedby Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine
    "Mover (fan or pump)"
  replaceable Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine mov3
    constrainedby Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine
    "Mover (fan or pump)"

  Buildings.Fluid.Actuators.Dampers.Exponential damExp1(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    use_strokeTime=false,
    final dpDamper_nominal=dp_nominal/2,
    y_start=1,
    final dpFixed_nominal=dp_nominal/2) "Damper"
  Buildings.Fluid.Actuators.Dampers.Exponential damExp2(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    use_strokeTime=false,
    final dpDamper_nominal=dp_nominal/2,
    y_start=1,
    final dpFixed_nominal=dp_nominal/2) "Damper"
  Buildings.Fluid.Actuators.Dampers.Exponential damExp3(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    use_strokeTime=false,
    final dpDamper_nominal=dp_nominal/2,
    y_start=1,
    final dpFixed_nominal=dp_nominal/2) "Damper"

  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare final package Medium = Medium,
    nPorts=3)
    "Source"
  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare final package Medium = Medium,
    nPorts=3)
    "Sink"
  Modelica.Blocks.Sources.Ramp ramSpe(
    height=1,
    duration=60,
    startTime=20) "Ramp signal for mover speed"
  Modelica.Blocks.Sources.Ramp ramDam(
    height=-1,
    duration=60,
    offset=1,
    startTime=120) "Ramp signal for damper position"

equation
  connect(sou.ports[1], mov1.port_a)
  connect(sou.ports[2], mov2.port_a)
  connect(sou.ports[3], mov3.port_a)
  connect(damExp1.port_b, sin.ports[1])
  connect(ramDam.y, damExp1.y)
  connect(mov2.port_b, damExp2.port_a)
  connect(damExp2.port_b, sin.ports[2])
  connect(mov3.port_b, damExp3.port_a)
  connect(damExp3.port_b, sin.ports[3])
  connect(ramDam.y, damExp2.y)
  connect(ramDam.y, damExp3.y)
  connect(mov1.port_b, damExp1.port_a)
end ComparePower;


Medium model

within Buildings.Fluid.Movers.Validation.BaseClasses;
model ControlledFlowMachine

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

  Modelica.Blocks.Sources.Pulse y(
    startTime=0,
    offset=0,
    amplitude=1,
    period=120,
    width=50)
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=3)

  Buildings.Fluid.Sensors.MassFlowRate masFloRat1(redeclare package Medium =
        Medium)
  Buildings.Fluid.Sensors.RelativePressure relPre(redeclare package Medium =
        Medium)
  replaceable
  Buildings.Fluid.Movers.SpeedControlled_y fan1(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos32slash1to12 per)
    constrainedby Buildings.Fluid.Movers.SpeedControlled_y
    "Fan with normalized control input between 0 and 1"
  Buildings.Fluid.FixedResistances.PressureDrop dp1(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=0.006,
    dp_nominal=50000) "Pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop dp2(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=0.006,
    dp_nominal=50000) "Pressure drop"
  Buildings.Fluid.Sensors.MassFlowRate masFloRat2(
    redeclare package Medium = Medium)
  Buildings.Fluid.FixedResistances.PressureDrop dp3(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=0.006,
    dp_nominal=50000) "Pressure drop"
  Buildings.Fluid.Sensors.MassFlowRate masFloRat3(
    redeclare package Medium = Medium)
  replaceable
  Buildings.Fluid.Movers.FlowControlled_dp fan3(
    redeclare package Medium = Medium,
    m_flow_nominal=6000/3600*1.2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos32slash1to12 per)
    constrainedby Buildings.Fluid.Movers.FlowControlled_dp
    "Fan with head as input"
  replaceable
  Buildings.Fluid.Movers.FlowControlled_m_flow fan2(
    redeclare package Medium = Medium,
    m_flow_nominal=6000/3600*1.2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    redeclare Buildings.Fluid.Movers.Data.Pumps.Wilo.Stratos32slash1to12 per)
    constrainedby Buildings.Fluid.Movers.FlowControlled_m_flow
    "Fan with mass flow rate as input"
  Buildings.Fluid.FixedResistances.PressureDrop dp5(
    m_flow_nominal=6000/3600*1.2,
    redeclare package Medium = Medium,
    dp_nominal=300,
    from_dp=true) "Pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop dp6(
    m_flow_nominal=6000/3600*1.2,
    redeclare package Medium = Medium,
    dp_nominal=300,
    from_dp=true) "Pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop dp7(
    m_flow_nominal=6000/3600*1.2,
    redeclare package Medium = Medium,
    dp_nominal=300,
    from_dp=true) "Pressure drop"
  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=3)
equation

  connect(fan1.port_a, relPre.port_b)
  connect(fan1.port_b, relPre.port_a)
  connect(fan1.port_b, dp1.port_a)
  connect(fan2.port_b, dp2.port_a)
  connect(fan3.port_b, dp3.port_a)
  connect(masFloRat1.m_flow, fan2.m_flow_in)
  connect(relPre.p_rel, fan3.dp_in)
  connect(dp5.port_b, fan1.port_a)
  connect(dp6.port_b, fan2.port_a)
  connect(dp7.port_b, fan3.port_a)
  connect(y.y, fan1.y)
  connect(dp5.port_a, sou.ports[1])
  connect(dp6.port_a, sou.ports[2])
  connect(dp7.port_a, sou.ports[3])
  connect(dp1.port_b, masFloRat1.port_a)
  connect(dp2.port_b, masFloRat2.port_a)
  connect(dp3.port_b, masFloRat3.port_a)
  connect(masFloRat1.port_b, sin.ports[1])
  connect(masFloRat2.port_b, sin.ports[2])
  connect(masFloRat3.port_b, sin.ports[3])
end ControlledFlowMachine;


within Buildings.Fluid.Movers.Validation.BaseClasses;
partial model FlowMachine_ZeroFlow
  "Base class to test flow machines with zero flow rate"

  replaceable package Medium = Buildings.Media.Air
      constrainedby Modelica.Media.Interfaces.PartialMedium "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=500
    "Nominal pressure difference";

  Modelica.Blocks.Sources.Ramp y(
    offset=1,
    duration=0.5,
    startTime=0.25,
    height=-1) "Input signal"
  Buildings.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=4)
  Buildings.Fluid.FixedResistances.PressureDrop dpSta(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
  replaceable Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine floMacSta
    constrainedby Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine(
      redeclare package Medium = Medium,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Static model of a flow machine"
  replaceable Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine floMacDyn
    constrainedby Buildings.Fluid.Movers.BaseClasses.PartialFlowMachine(
      redeclare package Medium = Medium,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Dynamic model of a flow machine"
  Buildings.Fluid.FixedResistances.PressureDrop dpDyn(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
  Modelica.Blocks.Math.Gain gain "Gain for input signal"
  Buildings.Fluid.FixedResistances.PressureDrop dpSta1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
  Buildings.Fluid.FixedResistances.PressureDrop dpDyn1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
equation
  connect(floMacSta.port_b, dpSta.port_a)
  connect(y.y, gain.u)
  connect(floMacDyn.port_b, dpDyn.port_a)
  connect(dpSta1.port_b, floMacSta.port_a)
  connect(dpDyn1.port_b, floMacDyn.port_a)
  connect(dpSta1.port_a, sou.ports[1])
  connect(dpDyn1.port_a, sou.ports[2])
  connect(dpDyn.port_b, sou.ports[3])
  connect(dpSta.port_b, sou.ports[4])
end FlowMachine_ZeroFlow;


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


Ideal one port density sensor. This model outputs the density of the fluid connected to its port.
The sensor is ideal, i.e. it does not influence the fluid.

within Buildings.Fluid.Sensors;
model Density "Ideal one port density sensor"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor;
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput d(final quantity="Density",
                                          final unit="kg/m3",
                                          min=0) "Density in port medium"

equation
  d = Medium.density(
       state=Medium.setState_phX(
         p=port.p,
         h=inStream(port.h_outflow),
         X=inStream(port.Xi_outflow)));
end Density;


Ideal two port density sensor. This model outputs the density of the fluid flowing from
port_a to port_b.

within Buildings.Fluid.Sensors;
model DensityTwoPort "Ideal two port density sensor"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput d(final quantity="Density",
                                          final unit="kg/m3",
                                          min=0) "Density of the passing fluid"
  parameter Medium.Density d_start=
     Medium.density(state=Medium.setState_pTX(
       p=p_start, T=T_start, X=X_start))
    "Initial or guess value of output (=state)"
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Temperature used to compute d_start"
  parameter Modelica.Units.SI.Pressure p_start=Medium.p_default
    "Pressure used to compute d_start"
  parameter Modelica.Units.SI.MassFraction X_start[Medium.nX]=Medium.X_default
    "Mass fraction used to compute d_start"
protected
  Medium.Density dMed(start=d_start)
    "Medium density to which the sensor is exposed";

  Medium.Density d_a_inflow "Density of inflowing fluid at port_a";
  Medium.Density d_b_inflow
    "Density of inflowing fluid at port_b, or rho_a_inflow if uni-directional flow";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(d) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      d = d_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     d_a_inflow = Medium.density(
       state=Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     d_b_inflow = Medium.density(
       state=Medium.setState_phX(p=port_a.p, h=port_a.h_outflow, X=port_a.Xi_outflow));
     dMed = Modelica.Fluid.Utilities.regStep(
       x=port_a.m_flow, y1=d_a_inflow, y2=d_b_inflow, x_small=m_flow_small);
  else
     dMed = Medium.density(
       state=Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     d_a_inflow = dMed;
     d_b_inflow = dMed;
  end if;
  // Output signal of sensor
  if dynamic then
    der(d) = (dMed-d)*k*tauInv;
  else
    d = dMed;
  end if;
end DensityTwoPort;


Ideal enthalphy flow rate sensor. This model outputs the enthalphy flow rate of the medium in the flow
between fluid ports. The sensor is ideal, i.e., it does not influence the fluid.

within Buildings.Fluid.Sensors;
model EnthalpyFlowRate "Ideal enthalphy flow rate sensor"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor(
    tau=0);
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput H_flow(final unit="W")
    "Enthalpy flow rate, positive if from port_a to port_b"
  parameter Modelica.Units.SI.SpecificEnthalpy h_out_start=
      Medium.specificEnthalpy_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default)
    "Initial or guess value of measured specific enthalpy"
protected
  Modelica.Units.SI.SpecificEnthalpy hMed_out(start=h_out_start)
    "Medium enthalpy to which the sensor is exposed";
  Modelica.Units.SI.SpecificEnthalpy h_out(start=h_out_start)
    "Medium enthalpy that is used to compute the enthalpy flow rate";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(h_out) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      h_out = h_out_start;
    end if;
  end if;
equation
  if allowFlowReversal then
    hMed_out = Modelica.Fluid.Utilities.regStep(
                 x=port_a.m_flow,
                 y1=port_b.h_outflow,
                 y2=port_a.h_outflow,
                 x_small=m_flow_small);
  else
    hMed_out = port_b.h_outflow;
  end if;
  // Specific enthalpy measured by sensor
  if dynamic then
    der(h_out) = (hMed_out-h_out)*k*tauInv;
  else
    h_out = hMed_out;
  end if;
  // Sensor output signal
  H_flow = port_a.m_flow * h_out;
end EnthalpyFlowRate;


Ideal entropy flow rate sensor. This model outputs the entropy flow rate of the medium in the flow
between fluid ports. The sensor is ideal, i.e., it does not influence the fluid.

within Buildings.Fluid.Sensors;
model EntropyFlowRate "Ideal entropy flow rate sensor"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor(
    tau=0);
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput S_flow(final unit="W/K")
    "Entropy flow rate, positive if from port_a to port_b"
  parameter Modelica.Units.SI.SpecificEntropy s_out_start=
      Medium.specificEntropy_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default) "Initial or guess value of measured specific entropy"
protected
  Modelica.Units.SI.SpecificEntropy sMed_out(start=s_out_start)
    "Medium entropy to which the sensor is exposed";
  Modelica.Units.SI.SpecificEntropy s_out(start=s_out_start)
    "Medium entropy that is used to compute the entropy flow rate";
  Modelica.Units.SI.SpecificEntropy port_b_s_outflow(start=s_out_start)
    "Medium entropy outflowing at port_b if mass flow were from port_a to port_b";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(s_out) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      s_out = s_out_start;
    end if;
  end if;
equation
  port_b_s_outflow = Medium.specificEntropy(
                       Medium.setState_phX(
                         p=port_b.p,
                         h=port_b.h_outflow,
                         X=port_b.Xi_outflow));
  if allowFlowReversal then
    sMed_out = Modelica.Fluid.Utilities.regStep(
                 x=port_a.m_flow,
                 y1=port_b_s_outflow,
                 y2=Medium.specificEntropy(
                      Medium.setState_phX(
                         p=port_a.p,
                         h=port_a.h_outflow,
                         X=port_a.Xi_outflow)),
                 x_small=m_flow_small);
  else
    sMed_out = port_b_s_outflow;
  end if;
  // Specific entropy measured by sensor
  if dynamic then
    der(s_out) = (sMed_out-s_out)*k*tauInv;
  else
    s_out = sMed_out;
  end if;
  // Sensor output signal
  S_flow = port_a.m_flow * s_out;
end EntropyFlowRate;


Measures thermal energy provided between supply and return pipes. This model measures thermal energy provided between a supply and return pipe.
It measures the capacity flow rate of the heat transfer fluid and the change in its
temperature compared to an external temperature measurement that is input into the port
TExt. The sensor does not influence the fluid.

within Buildings.Fluid.Sensors;
model HeatMeter "Measures thermal energy provided between supply and return pipes"
  extends BaseClasses.PartialDynamicFlowSensor;
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealInput TExt(
    final unit="K",
    displayUnit="degC") "External temperature measurement to calculate temperature difference"
  Modelica.Blocks.Interfaces.RealOutput Q_flow(
    final quantity="HeatFlowRate",
    final unit="W") "Heat flow rate"
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Initial or guess value of temperature"
  Medium.Temperature T(start=T_start) "Temperature of the passing fluid";
protected
  Medium.Temperature TMed(start=T_start) "Medium temperature to which the sensor is exposed";
  Medium.Temperature T_a_inflow "Temperature of inflowing fluid at port_a";
  Medium.Temperature T_b_inflow "Temperature of inflowing fluid at port_b, or T_a_inflow if uni-directional flow";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(T) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      T = T_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     T_a_inflow = Medium.temperature(state=
                    Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     T_b_inflow = Medium.temperature(state=
                    Medium.setState_phX(p=port_a.p, h=port_a.h_outflow, X=port_a.Xi_outflow));
     TMed = Modelica.Fluid.Utilities.regStep(
              x=port_a.m_flow,
              y1=T_a_inflow,
              y2=T_b_inflow,
              x_small=m_flow_small);
  else
     TMed = Medium.temperature(state=
              Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     T_a_inflow = TMed;
     T_b_inflow = TMed;
  end if;
  if dynamic then
    der(T) = (TMed-T)*k*tauInv;
  else
    T = TMed;
  end if;
  Q_flow = port_a.m_flow * Medium.cp_const * (T - TExt);
end HeatMeter;


within Buildings.Fluid.Sensors;
model LatentEnthalpyFlowRate
  "Ideal enthalphy flow rate sensor that outputs the latent enthalpy flow rate only"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor(
    redeclare replaceable package Medium =
        Modelica.Media.Interfaces.PartialCondensingGases,
    tau=0);
  extends Buildings.Fluid.BaseClasses.IndexMassFraction(final substanceName="water");
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput H_flow(final unit="W")
    "Latent enthalpy flow rate, positive if from port_a to port_b"
  parameter Modelica.Units.SI.SpecificEnthalpy h_out_start=
      Medium.specificEnthalpy_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default) - Medium.enthalpyOfNonCondensingGas(T=Medium.T_default)
    "Initial or guess value of measured specific latent enthalpy"

protected
  Modelica.Units.SI.SpecificEnthalpy hMed_out(start=h_out_start)
    "Medium latent enthalpy to which the sensor is exposed";
  Modelica.Units.SI.SpecificEnthalpy h_out(start=h_out_start)
    "Medium latent enthalpy that is used to compute the enthalpy flow rate";

  Medium.MassFraction XiActual[Medium.nXi]
    "Medium mass fraction to which sensor is exposed to";
  Medium.SpecificEnthalpy hActual
    "Medium enthalpy to which sensor is exposed to";
initial equation
 // Compute initial state
 if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(h_out) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      h_out = h_out_start;
    end if;
 end if;
equation
  if allowFlowReversal then
     XiActual = Modelica.Fluid.Utilities.regStep(
                 x=port_a.m_flow,
                 y1=port_b.Xi_outflow,
                 y2=port_a.Xi_outflow,
                 x_small=m_flow_small);
     hActual = Modelica.Fluid.Utilities.regStep(
                 x=port_a.m_flow,
                 y1=port_b.h_outflow,
                 y2=port_a.h_outflow,
                 x_small=m_flow_small);
  else
     XiActual = port_b.Xi_outflow;
     hActual = port_b.h_outflow;
  end if;
  // Specific enthalpy measured by sensor.
  // Compute H_flow as difference between total enthalpy and enthalpy on non-condensing gas.
  // This is needed to compute the liquid vs. gas fraction of water, using the equations
  // provided by the medium model
  hMed_out = (hActual -
     (1-XiActual[i_x]) * Medium.enthalpyOfNonCondensingGas(
       T=Medium.temperature(Medium.setState_phX(p=port_a.p, h=hActual, X=XiActual))));
  if dynamic then
    der(h_out) = (hMed_out-h_out)*k*tauInv;
  else
    h_out = hMed_out;
  end if;
  // Sensor output signal
  H_flow = port_a.m_flow * h_out;
end LatentEnthalpyFlowRate;


Ideal sensor for mass flow rate. This model outputs the mass flow rate flowing from
port_a to port_b.
The sensor is ideal, i.e., it does not influence the fluid.

within Buildings.Fluid.Sensors;
model MassFlowRate "Ideal sensor for mass flow rate"
extends Buildings.Fluid.Sensors.BaseClasses.PartialFlowSensor(
  final m_flow_nominal = 0,
  final m_flow_small = 0);
extends Modelica.Icons.RoundSensor;
Modelica.Blocks.Interfaces.RealOutput m_flow(quantity="MassFlowRate",
                                             final unit="kg/s")
  "Mass flow rate from port_a to port_b"

equation
m_flow = port_a.m_flow;
end MassFlowRate;


Ideal one port mass fraction sensor

within Buildings.Fluid.Sensors;
model MassFraction "Ideal one port mass fraction sensor"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor(
    redeclare replaceable package Medium =
        Modelica.Media.Interfaces.PartialCondensingGases);
  extends Buildings.Fluid.BaseClasses.IndexMassFraction(substanceName = "water");
  extends Modelica.Icons.RoundSensor;

  Modelica.Blocks.Interfaces.RealOutput X(min=-1e-3,
                                          max=1.001,
                                          final unit="kg/kg") "Mass fraction in port"
protected
  Medium.MassFraction XiVec[Medium.nXi](
    final quantity=Medium.substanceNames[1:Medium.nXi])
    "Mass fraction vector, needed because indexed argument for the operator inStream is not supported";

equation
  XiVec = inStream(port.Xi_outflow);
  X = if i_x > Medium.nXi then (1-sum(XiVec)) else XiVec[i_x];
end MassFraction;


Ideal two port mass fraction sensor

within Buildings.Fluid.Sensors;
model MassFractionTwoPort "Ideal two port mass fraction sensor"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor(
    redeclare replaceable package Medium =
        Modelica.Media.Interfaces.PartialCondensingGases);
  extends Buildings.Fluid.BaseClasses.IndexMassFraction(substanceName = "water");
  extends Modelica.Icons.RoundSensor;

  parameter Medium.MassFraction X_start=Medium.X_default[i_x]
    "Initial or guess value of output (= state)"

  Modelica.Blocks.Interfaces.RealOutput X(min=-1e-3,
                                          max=1.001,
                                          start=X_start,
                                          final unit="kg/kg")
    "Mass fraction of the passing fluid"

protected
  Medium.MassFraction XMed(start=X_start)
    "Mass fraction to which the sensor is exposed";
  Medium.MassFraction XiVec[Medium.nXi](
    final quantity=Medium.substanceNames[1:Medium.nXi])
    "Mass fraction vector, needed because indexed argument for the operator inStream is not supported";
initial equation
  // Assign initial conditions
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(X) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      X = X_start;
    end if;
  end if;
equation
  if allowFlowReversal then
    XiVec = Modelica.Fluid.Utilities.regStep(
              x=port_a.m_flow,
              y1=port_b.Xi_outflow,
              y2=port_a.Xi_outflow,
              x_small=m_flow_small);
  else
    XiVec = port_b.Xi_outflow;
  end if;
  XMed = if i_x > Medium.nXi then (1-sum(XiVec)) else XiVec[i_x];
  // Output signal of sensor
  if dynamic then
    der(X)  = (XMed-X)*k*tauInv;
  else
    X = XMed;
  end if;
end MassFractionTwoPort;


Package with sensor models

within Buildings.Fluid;
package Sensors "Package with sensor models"
  extends Modelica.Icons.SensorsPackage;
end Sensors;


Ideal one port trace substances sensor outputting in parts per million. This model outputs the trace substance concentration in ppm contained in the fluid connected to its port.
The sensor is ideal, i.e., it does not influence the fluid.

within Buildings.Fluid.Sensors;
model PPM
  "Ideal one port trace substances sensor outputting in parts per million"
  extends Buildings.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor(
    port(C_outflow(each final quantity="MassFraction",
                   each final unit="1",
                   each min=0,
                   each max=1)));
  extends Modelica.Icons.RoundSensor;

  parameter String substanceName = "CO2" "Name of trace substance";

  parameter Modelica.Units.SI.MolarMass MM=Modelica.Media.IdealGases.Common.SingleGasesData.CO2.MM
    "Molar mass of the trace substance";

  Modelica.Blocks.Interfaces.RealOutput ppm(min=0)
    "Trace substance in port medium in ppm"

protected
  parameter Real s[:]= {
    if ( Modelica.Utilities.Strings.isEqual(string1=Medium.extraPropertiesNames[i],
                                            string2=substanceName,
                                            caseSensitive=false))
    then 1 else 0 for i in 1:Medium.nC}
    "Vector with zero everywhere except where species is";

  final parameter Modelica.Units.SI.MolarMass MMBul=Medium.molarMass(
      Medium.setState_phX(
      p=Medium.p_default,
      h=Medium.h_default,
      X=Medium.X_default)) "Molar mass of bulk medium";

  final parameter Real MMFraction(
    min=0,
    max=1,
    final unit="1",
    final quantity="MassFraction")=MMBul/MM
    "Molar mass of the medium divided by the molar mass of the trace substance";

  final parameter Real coeff = MMFraction*1e6
    "Conversion from mass fraction to ppm";

initial equation
  assert(max(s) > 0.9, "Trace substance '" + substanceName + "' is not present in medium '"
         + Medium.mediumName + "'.\n"
         + "Check sensor parameter and medium model.");
equation
  // We obtain the species concentration with a vector multiplication
  // because Dymola 7.3 cannot find the derivative in the model
  // Buildings.Examples.VAVSystemCTControl.mo
  // if we set C = CVec[ind];
  ppm = s*inStream(port.C_outflow)*coeff;
end PPM;