within AixLib.Fluid.Movers.DpControlledMovers;
model DpControlled_dp "Pump or fan including pressure control (constant or variable)"
  extends AixLib.Fluid.Interfaces.LumpedVolumeDeclarations(
    final mSenFac=1);
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface(
    m_flow_nominal(final min=Modelica.Constants.small),
    port_a(
      h_outflow(start=h_outflow_start)),
    port_b(
      h_outflow(start=h_outflow_start),
      p(start=p_start),
      final m_flow(max = if allowFlowReversal then +Modelica.Constants.inf else 0)));

  replaceable parameter AixLib.Fluid.Movers.Data.Generic per(pressure=pressureCurve_default) "Record with performance data"

  parameter Modelica.Units.SI.PressureDifference dp_nominal(
    min=0,
    displayUnit="Pa") = if rho_default < 500 then 500 else 10000 "Nominal pressure raise, used to normalized the filter if use_inputFilter=true,
        to set default values of constantHead and heads, and
        and for default pressure curve if not specified in record per"
  parameter AixLib.Fluid.Movers.DpControlledMovers.Types.CtrlType ctrlType=AixLib.Fluid.Movers.DpControlledMovers.Types.CtrlType.dpTotal "Type of mover control"
  parameter AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParameters pressureCurve_default(
      V_flow=m_flow_nominal/rho_default*{0,1,1.5,2},
      dp=dp_nominal*{1.3,1,0.75,0})
    "General mover curve: volume flow rate vs. total pressure head"
  parameter AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParameters pressureCurve_dpConst(
      V_flow=m_flow_nominal/rho_default*{0,1,1.5,2},
      dp=dp_nominal*{1,1,0.75,0})
    "dpConst control: volume flow rate vs. total pressure head"
  parameter AixLib.Fluid.Movers.BaseClasses.Characteristics.flowParameters pressureCurve_dpVar(
      V_flow=m_flow_nominal/rho_default*{0,1,1.5,2},
      dp=dp_nominal*{0.5,1,0.75,0})
    "dpVar control: volume flow rate vs. total pressure head"

  parameter Boolean addPowerToMedium=true
    "Set to false to avoid any power (=heat and flow work) being added to medium (may give simpler equations)";
  parameter Boolean nominalValuesDefineDefaultPressureCurve = false
    "Set to true to avoid warning if m_flow_nominal and dp_nominal are used to construct the default pressure curve";
  parameter Boolean prescribeSystemPressure=false
    "=true, to control mover such that pressure difference is obtained across two remote points in system"
  parameter Modelica.Units.SI.Time tauMov=1
    "Time constant in mover (pump or fan) of fluid volume for nominal flow, used if energy or mass balance is dynamic"

  // Classes used to implement the filtered speed
  parameter Boolean use_inputFilter=true
    "= true, if speed is filtered with a 2nd order CriticalDamping filter"
  parameter Modelica.Units.SI.Time riseTime=30
    "Rise time of the filter (time to reach 99.6 % of the speed)"
  parameter Modelica.Blocks.Types.Init init=Modelica.Blocks.Types.Init.InitialOutput
    "Type of initialization (no init/steady state/initial state/initial output)"
  parameter Real y_start(min=0, max=1, unit="1")=0
    "Initial value of speed"

  // Sensor parameters
  parameter Modelica.Units.SI.PressureDifference dp_start=0
    "Initial value of pressure raise"
  parameter Modelica.Units.SI.Time tauSen=0
    "Time constant at nominal flow rate (use tau=0 for steady-state sensor, but see user guide for potential problems)"
  parameter Modelica.Blocks.Types.Init initType=Modelica.Blocks.Types.Init.InitialState
    "Type of initialization (InitialState and InitialOutput are identical)"
  parameter Modelica.Media.Interfaces.Types.Density d_start=Medium.density(Medium.setState_pTX(
      senVolFlo.p_start,
      senVolFlo.T_start,
      senVolFlo.X_start)) "Initial or guess value of density"

  // Table parameters
  parameter Modelica.Blocks.Types.Smoothness smoothness=Modelica.Blocks.Types.Smoothness.LinearSegments
    "Smoothness of table interpolation"
  parameter Boolean verboseExtrapolation=false
    "= true, if warning messages are to be printed if table input is outside the definition range"

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

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
    "Heat dissipation to environment"

  AixLib.Fluid.Sensors.VolumeFlowRate senVolFlo(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_small=m_flow_small,
    final tau=tauSen,
    final initType=initType,
    final d_start=d_start,
    final T_start=T_start,
    final p_start=p_start,
    final X_start=X_start)
    "Sensor to measure volume flow rate"

  Modelica.Blocks.Tables.CombiTable1Dv pressureCurveSelected(
    final tableOnFile=false,
    final table=if (ctrlType == AixLib.Fluid.Movers.DpControlledMovers.Types.CtrlType.dpConst)
         then [cat(1, pressureCurve_dpConst.V_flow),cat(1,
        pressureCurve_dpConst.dp)] elseif (ctrlType == AixLib.Fluid.Movers.DpControlledMovers.Types.CtrlType.dpVar)
         then [cat(1, pressureCurve_dpVar.V_flow),cat(1, pressureCurve_dpVar.dp)]
         else [cat(1, per.pressure.V_flow),cat(1, per.pressure.dp)],
    final tableName="NoName",
    final fileName="NoName",
    final verboseRead=true,
    final columns=2:size(pressureCurveSelected.table, 2),
    final smoothness=smoothness,
    final extrapolation=Modelica.Blocks.Types.Extrapolation.HoldLastPoint,
    u(each final unit="m3/s"),
    y(each final unit="Pa"),
    final verboseExtrapolation=verboseExtrapolation)
    "Table with points of selected curve to calculate dp from V_flow"

  AixLib.Fluid.Movers.FlowControlled_dp mov(
    redeclare final package Medium = Medium,
    final energyDynamics=energyDynamics,
    final p_start=p_start,
    final T_start=T_start,
    final X_start=X_start,
    final C_start=C_start,
    final C_nominal=C_nominal,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_nominal=m_flow_nominal,
    final m_flow_small=m_flow_small,
    final show_T=false,
    final per=per,
    final inputType=AixLib.Fluid.Types.InputType.Continuous,
    final addPowerToMedium=addPowerToMedium,
    final nominalValuesDefineDefaultPressureCurve=nominalValuesDefineDefaultPressureCurve,
    final tau=tauMov,
    final use_inputFilter=use_inputFilter,
    final riseTime=riseTime,
    final init=init,
    final dp_start=dp_start,
    final dp_nominal=dp_nominal,
    final constantHead=dp_nominal,
    final heads=dp_nominal*{(per.speeds[i]/per.speeds[end])^2 for i in 1:size(per.speeds, 1)},
    final prescribeSystemPressure=prescribeSystemPressure)
    "Mover: pump or fan"

  Modelica.Blocks.Interfaces.RealOutput P(final quantity="Power", final unit="W")
    "Electrical power consumed"
  Modelica.Blocks.Interfaces.RealOutput y_actual(final unit="1")
    "Actual normalised pump speed that is used for computations"
  Modelica.Blocks.Interfaces.RealOutput dp_actual(final unit="Pa")
    "Pressure difference between the mover inlet and outlet"

protected
  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";
  final parameter Medium.ThermodynamicState sta_start=Medium.setState_pTX(
      T=T_start,
      p=p_start,
      X=X_start)
    "Medium state at start values";

  final parameter Modelica.Units.SI.SpecificEnthalpy h_outflow_start=
      Medium.specificEnthalpy(sta_start) "Start value for outflowing enthalpy";

  final parameter Modelica.Units.SI.VolumeFlowRate V_flow_min=min(min(
      pressureCurve_default.V_flow), min(min(pressureCurve_dpConst.V_flow), min(
      pressureCurve_dpVar.V_flow))) "Get minimum of three vectors";
  final parameter Modelica.Units.SI.VolumeFlowRate V_flow_max=max(max(
      pressureCurve_default.V_flow), max(max(pressureCurve_dpConst.V_flow), max(
      pressureCurve_dpVar.V_flow))) "Get maximum of three vectors";

  constant Integer n_sup_pts = 100
    "Supporting points";
  final parameter Modelica.Units.SI.PressureDifference dps_default[:]=array(
      Modelica.Math.Vectors.interpolate(
      pressureCurve_default.V_flow,
      pressureCurve_default.dp,
      Vi) for Vi in V_flow_min:(V_flow_max - V_flow_min)/n_sup_pts:V_flow_max)
    "Vector (equally spaced) with pressure heads (default curve) on the basis of n_sup_pts supporting points";
  final parameter Modelica.Units.SI.PressureDifference dps_dpConst[:]=array(
      Modelica.Math.Vectors.interpolate(
      pressureCurve_dpConst.V_flow,
      pressureCurve_dpConst.dp,
      Vi) for Vi in V_flow_min:(V_flow_max - V_flow_min)/n_sup_pts:V_flow_max)
    "Vector (equally spaced) with pressure heads (dpConst curve) on the basis of n_sup_pts supporting points";
  final parameter Modelica.Units.SI.PressureDifference dps_dpVar[:]=array(
      Modelica.Math.Vectors.interpolate(
      pressureCurve_dpVar.V_flow,
      pressureCurve_dpVar.dp,
      Vi) for Vi in V_flow_min:(V_flow_max - V_flow_min)/n_sup_pts:V_flow_max)
    "Vector (equally spaced) with pressure heads (dpVar curve) on the basis of n_sup_pts supporting points";

  function checkDpCurves
    input Modelica.Units.SI.PressureDifference dps_default[:]
      "Reference vector containing pressure head points";
    input Modelica.Units.SI.PressureDifference dps_other[:]
      "Comparing vector containing pressure head points";
    input Integer n_sup_pts(min=2) "Supporting points";
    output Boolean dps_error "Use a boolean to avoid multiple assertion errors to be printed";
  algorithm
    dps_error := false;
    for i in 1:1:n_sup_pts loop
     if dps_error == false and dps_other[i]<=dps_default[i] then
       dps_error :=false;
     else
       dps_error :=true;
     end if;
    end for;
  end checkDpCurves;

initial equation
  assert(pressureCurveSelected.n==1,
    "\n+++++++++++++++++++++++++++++++++++++++++++\nNumber of outputs of table component "+getInstanceName()+".pressureCurveSelected must equal 1, but they are "+String(pressureCurveSelected.n)+".\n+++++++++++++++++++++++++++++++++++++++++++",
    AssertionLevel.error);
  assert(pressureCurveSelected.table[1, 1] == 0.0,
    "\n+++++++++++++++++++++++++++++++++++++++++++\nParameterization error in component ("+getInstanceName()+".pressureCurveSelected):\nThe mover's (pump or fan) curve must have first point at V_flow = 0.0 m3/s.\n+++++++++++++++++++++++++++++++++++++++++++",
    AssertionLevel.error);
  assert(pressureCurveSelected.table[size(pressureCurveSelected.table, 1), size(pressureCurveSelected.table, 2)] == 0.0,
    "\n+++++++++++++++++++++++++++++++++++++++++++\nParameterization error in component ("+getInstanceName()+".pressureCurveSelected):\nThe mover's (pump or fan) curve must have last point at dp = 0.0 Pa.\n+++++++++++++++++++++++++++++++++++++++++++",
    AssertionLevel.error);
  // Check on the basis of n supporting points if both controlled curves are correct parametrized so that they lie below the default curve.
  assert(checkDpCurves(dps_default, dps_dpConst, n_sup_pts)==false,
    "\n+++++++++++++++++++++++++++++++++++++++++++\nParameterization error in component ("+getInstanceName()+".pressureCurve_dpConst):\nThe dp values of the curve must be less or equal the default/total mover's curve.\n+++++++++++++++++++++++++++++++++++++++++++",
     AssertionLevel.error);
  assert(checkDpCurves(dps_default, dps_dpVar, n_sup_pts)==false,
    "\n+++++++++++++++++++++++++++++++++++++++++++\nParameterization error in component ("+getInstanceName()+".pressureCurve_dpVar):\nThe dp values of the curve must be less or equal the default/total mover's curve.\n+++++++++++++++++++++++++++++++++++++++++++",
    AssertionLevel.error);

equation
  connect(mov.port_b, port_b)
  connect(port_a, senVolFlo.port_a)
  connect(senVolFlo.port_b, mov.port_a)
  connect(senVolFlo.V_flow, pressureCurveSelected.u[1])
  connect(dpMea, mov.dpMea)
  connect(mov.heatPort, heatPort)
  connect(mov.P, P)
  connect(mov.y_actual, y_actual)
  connect(mov.dp_actual, dp_actual)
  connect(pressureCurveSelected.y[1], mov.dp_in)
end DpControlled_dp;


Medium model

within AixLib.Fluid.Movers.DpControlledMovers.Examples;
model DpConst
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1.0
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal_max=2*m_flow_nominal
    "To describe max point of mover's characteristic curve.";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=100000 "Nominal pressure raise, used to normalized the filter if use_inputFilter=true,
        to set default values of constantHead and heads, and
        and for default pressure curve if not specified in record per";

  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=1)

  AixLib.Fluid.Actuators.Valves.TwoWayLinear val(
    redeclare package Medium = Medium,
    from_dp=true,
    m_flow_nominal=m_flow_nominal_max,
    dpValve_nominal=0.5*dp_nominal,
    y_start=rampValve.offset)
                      "Pressure drop"
  AixLib.Fluid.Sensors.MassFlowRate masFloRat(redeclare package Medium = Medium)
  AixLib.Fluid.Movers.DpControlledMovers.DpControlled_dp dpControlled_dp(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    redeclare Data.Generic per(pressure=dpControlled_dp.pressureCurve_default, motorCooledByFluid=false),
    dp_nominal=dp_nominal,
    ctrlType=AixLib.Fluid.Movers.DpControlledMovers.Types.CtrlType.dpConst)
  AixLib.Fluid.FixedResistances.PressureDrop dp(
    m_flow_nominal=m_flow_nominal_max,
    redeclare package Medium = Medium,
    dp_nominal=0.5*dp_nominal,
    from_dp=true) "Pressure drop"

  Modelica.Blocks.Sources.Ramp rampValve(
    height=1,
    duration=1800,
    offset=0,
    startTime=900)
  AixLib.Fluid.Delays.DelayFirstOrder vol(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal_max,
    nPorts=2)
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature fixedTemperature(T=293.15)

  Modelica.Blocks.Continuous.Integrator integrator(u(final quantity="Power", final unit="W"), y(final quantity="Energy", final unit="J", final displayUnit="kW.h"))
  Modelica.Blocks.Interfaces.RealOutput elEnergy(final quantity="Energy", final unit="J", final displayUnit="kW.h") "Cumulated electrical power"
equation

  connect(dpControlled_dp.port_b,val. port_a)
  connect(dp.port_b, dpControlled_dp.port_a)
  connect(val.port_b, masFloRat.port_a)
  connect(sou.ports[1], dp.port_a)
  connect(rampValve.y,val. y)
  connect(masFloRat.port_b, vol.ports[1])
  connect(vol.ports[2], dp.port_a)
  connect(fixedTemperature.port, vol.heatPort)
  connect(dpControlled_dp.P, integrator.u)
  connect(integrator.y, elEnergy)
end DpConst;


within AixLib.Fluid.Movers.DpControlledMovers.Examples;
model DpVar
  extends DpConst(dpControlled_dp(ctrlType=AixLib.Fluid.Movers.DpControlledMovers.Types.CtrlType.dpVar));
end DpVar;


Flow machine with feedback control

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

  package Medium = AixLib.Media.Air;

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.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"
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=2)
  AixLib.Fluid.FixedResistances.PressureDrop dp1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
  AixLib.Fluid.FixedResistances.PressureDrop dp2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal/2) "Pressure drop"
  AixLib.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"
  AixLib.Fluid.Sensors.MassFlowRate senMasFlo(redeclare package Medium =
        Medium)
  AixLib.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 AixLib.Fluid.Movers.Examples;
model MoverContinuous
  "Example model of movers using a real input for setting the stage"
  extends MoverParameter(
    pump_m_flow(inputType=AixLib.Fluid.Types.InputType.Continuous),
    pump_y(inputType=AixLib.Fluid.Types.InputType.Continuous),
    pump_dp(inputType=AixLib.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 AixLib.Fluid.Movers.Examples;
model MoverParameter
  "Example model of movers using a parameter for setting the stage"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water;

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

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

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

  AixLib.Fluid.Sources.Boundary_pT sin(redeclare package Medium = Medium,
      nPorts=3) "Fluid sink"
  AixLib.Fluid.Movers.SpeedControlled_y pump_y(
    redeclare package Medium = Medium,
    redeclare AixLib.Fluid.Movers.Data.Pumps.Wilo.Stratos25slash1to4 per(
        speeds={0,0.5,1}, constantSpeed=1),
    use_inputFilter=false,
    inputType=AixLib.Fluid.Types.InputType.Constant,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump with normalised speed input"
  AixLib.Fluid.Movers.FlowControlled_dp pump_dp(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    redeclare AixLib.Fluid.Movers.Data.Pumps.Wilo.Stratos25slash1to4 per,
    use_inputFilter=false,
    heads={0,0.5,1}*dp_nominal,
    inputType=AixLib.Fluid.Types.InputType.Constant,
    dp_nominal=dp_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump with pressure head input"
  AixLib.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 AixLib.Fluid.Movers.Examples;
model MoverStages
  "Example model of movers using an integer input for setting the stage"
  extends AixLib.Fluid.Movers.Examples.MoverParameter(
    pump_m_flow(inputType=AixLib.Fluid.Types.InputType.Stages),
    pump_y(inputType=AixLib.Fluid.Types.InputType.Stages),
    pump_dp(inputType=AixLib.Fluid.Types.InputType.Stages));
  package Medium = AixLib.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 AixLib.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 AixLib.Fluid.Movers.Examples;
model PumpsParallel "Two flow machines in parallel"
  extends Modelica.Icons.Example;

  package Medium = AixLib.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";

  AixLib.Fluid.FixedResistances.PressureDrop dpIn1(
    redeclare package Medium = Medium,
    dp_nominal=1000,
    m_flow_nominal=0.5*m_flow_nominal) "Pressure drop"
  AixLib.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"

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

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

  AixLib.Fluid.FixedResistances.PressureDrop dpIn2(
    redeclare package Medium = Medium,
    dp_nominal=1000,
    m_flow_nominal=0.5*m_flow_nominal) "Pressure drop"
  AixLib.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=AixLib.Fluid.Types.InputType.Constant) "Model of a flow machine"
  AixLib.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 AixLib.Fluid.Movers.Examples;
model PumpsSeries "Two flow machines in series"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water;

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

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

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

  AixLib.Fluid.Movers.SpeedControlled_y floMac2(
    redeclare package Medium = Medium,
    per(pressure(V_flow={0, m_flow_nominal/1000}, dp={2*4*1000, 0})),
    inputType=AixLib.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)
  AixLib.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 AixLib.Fluid.Movers.Examples;
model SpeedControlled_y_pumpCurves
  "Pumps that illustrates the use of the pump curves"
  extends Modelica.Icons.Example;
  package Medium = AixLib.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 = AixLib.Fluid.Movers.SpeedControlled_y (
    redeclare package Medium = Medium,
    use_inputFilter=
                  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=AixLib.Fluid.Types.InputType.Constant,
    per(constantSpeed=1)) "Pump"
  pumpModel pum1(
    inputType=AixLib.Fluid.Types.InputType.Constant,
    per(constantSpeed=0.5)) "Pump"
  pumpModel pum2(
    inputType=AixLib.Fluid.Types.InputType.Constant,
    per(constantSpeed=0.05)) "Pump"
  pumpModel pum3(
    inputType=AixLib.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"
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=300000,
    T=293.15,
    nPorts=4)

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

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

  AixLib.Fluid.Actuators.Valves.TwoWayLinear dp3(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=0.01*dp_nominal,
    use_inputFilter=false) "Pressure drop"
  AixLib.Fluid.Actuators.Valves.TwoWayLinear dp4(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=0.01*dp_nominal,
    use_inputFilter=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 AixLib.Fluid.Movers.Examples;
model StaticReset
  "Comparing different computation paths with a static pressure reset"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;

  // Fans and their performance records
  AixLib.Fluid.Movers.SpeedControlled_y fan1(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare AixLib.Fluid.Movers.Examples.Data.EnglanderNorford1992 per(
      etaHydMet=AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Power_VolumeFlowRate,
      etaMotMet=AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided))
    "Fan using power curves"
  AixLib.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=AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
      etaMotMet=AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.NotProvided,
      peak(
        V_flow=23.022856,
        dp=1211.1604,
        eta=0.74030644)))
    "Fan using the Euler number"
  AixLib.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=AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate,
      etaMotMet=AixLib.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
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=6) "Boundary"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    p=101325,
    T=293.15,
    nPorts=3) "Boundary"

  // Duct pressure drops
  AixLib.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"
  AixLib.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"
  AixLib.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"
  AixLib.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"
  AixLib.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"
  AixLib.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
  AixLib.Fluid.Sensors.RelativePressure pDucSta1(
    redeclare package Medium = Medium)
    "Duct static pressure"
  AixLib.Fluid.Sensors.RelativePressure pDucSta2(
    redeclare package Medium = Medium)
    "Duct static pressure"
  AixLib.Fluid.Sensors.RelativePressure pDucSta3(
    redeclare package Medium = Medium)
    "Duct static pressure"
  AixLib.Controls.Continuous.LimPID conPID1(
    Td=1,
    k=0.5,
    Ti=15) "PI controller"
  AixLib.Controls.Continuous.LimPID conPID2(
    Td=1,
    k=0.5,
    Ti=15) "PI controller"
  AixLib.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 AixLib.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 AixLib.Fluid.Movers.Preconfigured;
model FlowControlled_dp "Fan or pump with ideally controlled head dp as input signal and pre-configured parameters"
  extends AixLib.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=AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
            etaMotMet=AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
    final constantHead,
    final heads,
    final nominalValuesDefineDefaultPressureCurve=true,
    final inputType=AixLib.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 AixLib.Fluid.Movers.Preconfigured;
model FlowControlled_m_flow "Fan or pump with ideally controlled mass flow rate as input signal and pre-configured parameters"
  extends AixLib.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=AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
            etaMotMet=AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
    final constantMassFlowRate,
    final massFlowRates,
    final nominalValuesDefineDefaultPressureCurve=true,
    final inputType=AixLib.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 AixLib.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 AixLib.Fluid.Movers.Preconfigured;
model SpeedControlled_y "Fan or pump with ideally controlled normalized speed y as input signal and pre-configured parameters"
  extends AixLib.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=AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.EulerNumber,
            etaMotMet=AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.GenericCurve),
    final inputType=AixLib.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 AixLib.Fluid.Movers.Preconfigured;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;

end UsersGuide;


within AixLib.Fluid.Movers.Preconfigured.Validation;
model ControlledFlowMachinePreconfigured
  "Preconfigured fans with different control signals"
  extends Modelica.Icons.Example;
  extends AixLib.Fluid.Movers.Validation.BaseClasses.ControlledFlowMachine(
    redeclare AixLib.Fluid.Movers.Preconfigured.SpeedControlled_y fan1(
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal),
    redeclare AixLib.Fluid.Movers.Preconfigured.FlowControlled_m_flow fan2(
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal),
    redeclare AixLib.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 AixLib.Fluid.Movers.Preconfigured;
package Validation "Package for validation models"
extends Modelica.Icons.ExamplesPackage;
end Validation;


within AixLib.Fluid.Movers;
package PumpsPolynomialBased "Pump models with control block and new configuration approach"
extends Modelica.Icons.Package;
end PumpsPolynomialBased;


within AixLib.Fluid.Movers.PumpsPolynomialBased;
model PumpHeadControlled
  "Pump model with pump head control, an onOff-Switch and limitation of pump head."
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface;

  parameter AixLib.DataBase.Pumps.PumpPolynomialBased.PumpBaseRecord pumpParam=
      AixLib.DataBase.Pumps.PumpPolynomialBased.PumpBaseRecord() "pump parameter record"

  parameter Real Qnom(
    quantity="VolumeFlowRate",
    unit="m3/h",
    displayUnit="m3/h") = 0.67*max(pumpParam.maxMinSpeedCurves[:, 1])
    "Nominal volume flow rate in m³/h (~0.67*Qmax).
    Qmax is taken from pumpParam.maxMinSpeedCurves."
  parameter Modelica.Units.NonSI.AngularVelocity_rpm Nnom=
      Modelica.Math.Vectors.interpolate(
      x=pumpParam.maxMinSpeedCurves[:, 1],
      y=pumpParam.maxMinSpeedCurves[:, 2],
      xi=Qnom) "Pump speed in design point (Qnom,Hnom).
    Default is maximum speed at Qnom from pumpParam.maxMinSpeedCurves.
    Note that N is defined only on [nMin, nMax]. Due to power limitation
    N might be smaller than nMax for higher Q."
  parameter Modelica.Units.SI.Height Hnom=
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
      pumpParam.cHQN,
      Qnom,
      Nnom) "Nominal pump head in m (water).
      Will by default be calculated automatically from Qnom and Nnom.
      If you change the value make sure to also set a feasible Qnom."

// Parameters
  // Initialization
  parameter Modelica.Units.SI.Height Hstart=Hnom "
      Start value of pump head. Will be used to initialize criticalDamping."
  parameter Modelica.Media.Interfaces.Types.AbsolutePressure p_start=Medium.p_default
    "Start value of pressure"
  parameter Modelica.Media.Interfaces.Types.Temperature T_start=Medium.T_default
    "Start value of temperature"

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

// Assumptions
  parameter Modelica.Units.SI.Volume V=0 "Volume inside the pump"

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

// Power and Efficiency
  parameter Boolean calculatePower=true "calc. power consumption?"
  parameter Boolean calculateEfficiency=false
    "calc. efficency? (eta = f(H, Q, P))"
  replaceable function efficiencyCharacteristic =
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.efficiencyCharacteristic.Wilo_Formula_efficiency
    constrainedby
    AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.efficiencyCharacteristic.baseEfficiency
    "eta = f(H, Q, P)"

// Variables
  Modelica.Units.SI.Pressure dp_pump "Pressure increase";
  Modelica.Blocks.Interfaces.RealOutput head(
    quantity="Length",
    unit="m") "Pump head";
  Modelica.Units.SI.AngularVelocity n
    "pump speed calculated from volume flow and head n=f(Q,H)";
  Modelica.Blocks.Interfaces.RealOutput power(quantity="Power", unit="W")
    "electical power";
  Modelica.Blocks.Interfaces.RealOutput eta(
    quantity="Efficiency",
    unit="1",
    min=0) "efficiency";

  Modelica.Blocks.Sources.RealExpression Vflow_m3h(y=m_flow/rho_default*3600)
    "conversion of mass flow rate to volume flow rate"

  Modelica.Blocks.Tables.CombiTable1Dv maxMinTable(
    columns={2,3},
    extrapolation=Modelica.Blocks.Types.Extrapolation.HoldLastPoint,
    table=pumpParam.maxMinHeight,
    tableName="NoName",
    tableOnFile=false)
    "Outputs static head (H). Maximum, minimum and freely selectable pump curve"

  Modelica.Blocks.Routing.DeMultiplex2 deMultiplex3_1
    "Seperate max/min pump head curve signal"
  BaseClasses.VariableLimiter variableLimiter(strict=false)
    "Limit pump head to max/min curve"

  BaseClasses.PumpBus pumpBus

protected
  Modelica.Blocks.Sources.RealExpression pumpPower(y=power) if calculatePower
    "implements a connectable object that can be cuppled with pumpBus."
  Modelica.Blocks.Sources.RealExpression pumpHead(y=head)
    "implements a connectable object that can be cuppled with pumpBus."
  Modelica.Blocks.Sources.RealExpression pumpEfficiency(y=eta)
 if calculateEfficiency
    "implements a connectable object that can be cuppled with pumpBus."
  Modelica.Blocks.Sources.Constant off(k=0)
  Modelica.Blocks.Sources.RealExpression pressure_difference(y=-dp_pump)
    "implements a connectable object that can be cuppled with pumpBus."
  Modelica.Blocks.Continuous.CriticalDamping
    criticalDamping(
      f=1/5,
      initType=Modelica.Blocks.Types.Init.InitialOutput,
      n=1,
      y_start=Hstart)

public
  Modelica.Blocks.Logical.Switch onOff

  Modelica.Blocks.Sources.RealExpression realExpression(y=n)

  AixLib.Fluid.Movers.BaseClasses.IdealSource idealSource(
    redeclare package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_small=1E-4*abs(m_flow_nominal),
    final control_m_flow=false)
  MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    energyDynamics=energyDynamics,
    massDynamics=massDynamics,
    p_start=p_start,
    T_start=T_start,
    m_flow_nominal=m_flow_nominal,
    m_flow_small=1E-4*abs(m_flow_nominal),
    allowFlowReversal=allowFlowReversal,
    final V=V,
    nPorts=2)
equation

  connect(maxMinTable.u[1], Vflow_m3h.y)
    "for the interpolation of the max curve"
  connect(maxMinTable.u[2], Vflow_m3h.y)
    "for the interpolation of the min curve"

  head = criticalDamping.y "safe head after limiting and other checks.";
  dp_pump = head * rho_default * Modelica.Constants.g_n;
//Calculate power and Efficiency
  if pumpBus.onSet and head >
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
      pumpParam.cHQN,
      Modelica.Constants.small,
      pumpParam.nMin*0.9) then
    if calculatePower then
      n = max(0,
        AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomialABCinverse(
        {pumpParam.cHQN[3, 1],pumpParam.cHQN[2, 2],pumpParam.cHQN[1, 3]},
        Vflow_m3h.y,
        head)) "n can be calculated with inverse ABC formula or with a solver
        when using the full information in pumpParam.cHQN.";
      power = AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
        pumpParam.cPQN,
        Vflow_m3h.y,
        n) "will not check for negative values";
    else
      n = 0;
      power = 0;
    end if;

    eta = if (calculateEfficiency) and power >
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
      pumpParam.cPQN,
      Modelica.Constants.small,
      pumpParam.nMin*0.9) then efficiencyCharacteristic(
      Vflow_m3h.y,
      head,
      power,
      rho_default) else 0;
  else
    n = 0;
    power = 0;
    eta = 0;
  end if;

  connect(maxMinTable.y, deMultiplex3_1.u)
  connect(deMultiplex3_1.y2[1], variableLimiter.limit2)

  connect(pumpPower.y, pumpBus.PelMea)
  connect(pumpHead.y, pumpBus.dpMea)
  connect(pumpEfficiency.y, pumpBus.efficiencyMea)
  connect(onOff.u2, pumpBus.onSet)
  connect(off.y, onOff.u3)
  connect(deMultiplex3_1.y1[1], variableLimiter.limit1)
  connect(variableLimiter.y, onOff.u1)
  connect(Vflow_m3h.y, pumpBus.vFRcur_m3h)

  connect(variableLimiter.u, pumpBus.dpSet)
  connect(realExpression.y, pumpBus.rpmMea)
  connect(pressure_difference.y,idealSource. dp_in)
  connect(idealSource.port_a, port_a)
  connect(idealSource.port_b,vol. ports[1])
  connect(vol.ports[2], port_b)
  connect(onOff.y, criticalDamping.u)
end PumpHeadControlled;


within AixLib.Fluid.Movers.PumpsPolynomialBased;
model PumpSpeedControlled
  "Pump model with speed control, onOff-Switch and bounding of speed instead of pump delivery head."

  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface;
    // Energy and mass balance (defines Medium start properties)

  parameter AixLib.DataBase.Pumps.PumpPolynomialBased.PumpBaseRecord pumpParam= AixLib.DataBase.Pumps.PumpPolynomialBased.PumpBaseRecord() "pump parameter record"

  parameter Real Qnom(
    quantity="VolumeFlowRate",
    unit="m3/h",
    displayUnit="m3/h") = 0.67*max(pumpParam.maxMinSpeedCurves[:, 1])
    "Nominal volume flow rate in m³/h (~0.67*Qmax).
    Qmax is taken from pumpParam.maxMinSpeedCurves."
  parameter Modelica.Units.NonSI.AngularVelocity_rpm Nnom=
      Modelica.Math.Vectors.interpolate(
      x=pumpParam.maxMinSpeedCurves[:, 1],
      y=pumpParam.maxMinSpeedCurves[:, 2],
      xi=Qnom) "Pump speed in design point (Qnom,Hnom).
    Default is maximum speed at Qnom from pumpParam.maxMinSpeedCurves.
    Note that N is defined only on [nMin, nMax]. Due to power limitation
    N might be smaller than nMax for higher Q."
  parameter Modelica.Units.SI.Height Hnom=
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
      pumpParam.cHQN,
      Qnom,
      Nnom) "Nominal pump head in m (water).
      Will by default be calculated automatically from Qnom and Nnom.
      If you change the value make sure to also set a feasible Qnom."

  // Parameters
  // Initialization
  parameter Modelica.Units.SI.Height Hstart=Hnom "
      Start value of pump head. Will be used to initialize criticalDamping."
  parameter Modelica.Media.Interfaces.Types.AbsolutePressure p_start=Medium.p_default
    "Start value of pressure"
  parameter Modelica.Media.Interfaces.Types.Temperature T_start=Medium.T_default
    "Start value of temperature"

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

  // Assumptions
  parameter Modelica.Units.SI.Volume V=0 "Volume inside the pump"

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

  // Power and Efficiency
  parameter Boolean calculatePower=true "calc. power consumption?"
  parameter Boolean calculateEfficiency=false
    "calc. efficency? (eta = f(H, Q, P))"
  replaceable function efficiencyCharacteristic =
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.efficiencyCharacteristic.Wilo_Formula_efficiency
    constrainedby AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.efficiencyCharacteristic.baseEfficiency
    "eta = f(H, Q, P)"
  /*
  Defining constants that will be used as min, max, nominal attributes for variables
  */
protected
  constant Modelica.Units.SI.Length constHmax=20;
  constant Modelica.Units.SI.Length constHnom=0.5*constHmax;
  constant Modelica.Units.SI.PressureDifference constDpMax=1.1*constHmax*995*
      Modelica.Constants.g_n;
  constant Modelica.Units.SI.PressureDifference constDpNom=constDpMax*0.5;
  constant Modelica.Units.SI.MassFlowRate constMflowMax=60*995/3600;
  constant Modelica.Units.SI.MassFlowRate constMflowNom=2*995/3600;

  // Variables
public
  Modelica.Units.SI.PressureDifference dp_pump(
    min=0,
    max=constDpMax,
    nominal=constDpNom) "Pressure increase";
  Modelica.Blocks.Interfaces.RealOutput head(
    quantity="Length",
    unit="m",
    min=0,
    max=constHmax,
    nominal=constHnom) "Pump head";
  Modelica.Blocks.Interfaces.RealOutput n(
    nominal=Nnom,
    min=0,
    max=pumpParam.nMax,
    quantity="AngularVelocity",
    unit="rev/min")
    "limited pump speed calculated from volume flow and head n=f(Q,H)";
  Modelica.Blocks.Interfaces.RealOutput power(
    quantity="Power",
    unit="W",
    min=0) "electical power";
  Modelica.Blocks.Interfaces.RealOutput eta(
    quantity="Efficiency",
    unit="1",
    min=0) "efficiency";
  Modelica.Blocks.Sources.RealExpression Vflow_m3h(y(
      min=0,
      max=max(pumpParam.maxMinSpeedCurves[:, 1]),
      nominal=Qnom)= m_flow/rho_default*3600)
    "conversion of mass flow rate to volume flow rate"
  Modelica.Blocks.Tables.CombiTable1Dv maxMinTable(
    columns={2,3},
    tableName="NoName",
    tableOnFile=false,
    table=pumpParam.maxMinSpeedCurves)
    "Outputs max [1] and min [2] pump speed for the current value of volume flow rate. "

  Modelica.Blocks.Routing.DeMultiplex2 deMultiplex2(n1=1, n2=1)
    "Seperate max/min pump head curve signal"
  BaseClasses.VariableLimiter variableLimiter(strict=false)
    "Limit pump speed to max/min curve"

  BaseClasses.PumpBus pumpBus

protected
  Modelica.Blocks.Sources.RealExpression pumpPower(y=power) if calculatePower
    "implements a connectable object that can be cuppled with pumpBus."
  Modelica.Blocks.Sources.RealExpression pumpHead(y=head)
    "implements a connectable object that can be cuppled with pumpBus."
  Modelica.Blocks.Sources.RealExpression pumpEfficiency(y=eta)
 if calculateEfficiency
    "implements a connectable object that can be cuppled with pumpBus."
  Modelica.Blocks.Sources.Constant off(k=Modelica.Constants.small)
  Modelica.Blocks.Continuous.CriticalDamping
                                    criticalDamping(
    y_start=Hstart,
    f=1/5,
    initType=Modelica.Blocks.Types.Init.InitialOutput)
  Modelica.Blocks.Sources.RealExpression pressure_difference(y=-dp_pump)
    "implements a connectable object that can be cuppled with pumpBus."
public
  Modelica.Blocks.Logical.Switch onOffHead "turns head variable off or on."
  Modelica.Blocks.Logical.Switch onOffn "turns speed variable n off or on."

  Modelica.Blocks.Sources.RealExpression headUnbound(y=max(0,
        AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
        pumpParam.cHQN,
        Vflow_m3h.y,
        n)))

  AixLib.Fluid.Movers.BaseClasses.IdealSource idealSource(
    redeclare package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_small=1E-4*abs(m_flow_nominal),
    final control_m_flow=false)
  MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    energyDynamics=energyDynamics,
    massDynamics=massDynamics,
    final p_start=p_start,
    final T_start=T_start,
    m_flow_nominal=m_flow_nominal,
    m_flow_small=1E-4*abs(m_flow_nominal),
    allowFlowReversal=allowFlowReversal,
    final V=V,
    nPorts=2)

equation

  // Physical limitations of the pump
  connect(maxMinTable.u[1], Vflow_m3h.y)
    "for the interpolation of the max curve"
  connect(maxMinTable.u[2], Vflow_m3h.y)
    "for the interpolation of the min curve"

  // without damping: onOff.y
  head = criticalDamping.y "safe head after limiting and other checks.";
  dp_pump = head  * rho_default * Modelica.Constants.g_n;
  //Calculate power and Efficiency
  if calculatePower then
    power =if n >= pumpParam.nMin*0.9 and pumpBus.onSet and Vflow_m3h.y >
      Modelica.Constants.small then max(0,
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
      pumpParam.cPQN,
      Vflow_m3h.y,
      n)) else 0 "computing power as long as n > 90% nMin";
    if calculateEfficiency then
      eta =if n >= pumpParam.nMin*0.9 and pumpBus.onSet and power >
        AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
        pumpParam.cPQN,
        Modelica.Constants.small,
        pumpParam.nMin*0.9) then max(0, efficiencyCharacteristic(
        Vflow_m3h.y,
        head,
        power,
        rho_default)) else 0 "computing eta as long as n > 90% nMin";
    else
      eta = 0;
    end if;
  else
    power = 0;
    eta = 0;
  end if;

  connect(maxMinTable.y, deMultiplex2.u)
  connect(deMultiplex2.y2[1], variableLimiter.limit2)

  connect(pumpPower.y, pumpBus.PelMea)
  connect(pumpEfficiency.y, pumpBus.efficiencyMea)
  connect(onOffHead.u2, pumpBus.onSet)
  connect(off.y, onOffHead.u3)
  connect(deMultiplex2.y1[1], variableLimiter.limit1)
  connect(variableLimiter.u, pumpBus.rpmSet)
  connect(onOffHead.y, criticalDamping.u)
  connect(pumpHead.y, pumpBus.dpMea)

  connect(variableLimiter.y, onOffn.u1)
  connect(onOffn.u2, pumpBus.onSet)
  connect(onOffn.y, n)

  connect(n, pumpBus.rpmMea)
  connect(Vflow_m3h.y, pumpBus.vFRcur_m3h)
  connect(off.y, onOffn.u3)
  connect(onOffHead.u1, headUnbound.y)
  connect(idealSource.port_a, port_a)
  connect(pressure_difference.y, idealSource.dp_in)
  connect(idealSource.port_b, vol.ports[1])
  connect(vol.ports[2], port_b)
end PumpSpeedControlled;


info

within AixLib.Fluid.Movers.PumpsPolynomialBased;
package BaseClasses
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


computes the polynomial in two variables a and b: sum_ij( c[i, j] * a^i * b^j ) where c is the coefficient matrix..

within AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses;
function polynomial2D
  "computes the polynomial in two variables a and b: sum_ij( c[i, j] * a^i * b^j ) where c is the coefficient matrix."
  extends Modelica.Icons.Function;
  import pow = AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.powerInt;
  input Real c[:,:] "coefficient matrix";
  input Real a "variable 1";
  input Real b "variable 2";
  output Real y "result";

algorithm
  y := 0;
  for i in 1:size(c, 1) loop
    for j in 1:size(c, 2) loop
      if c[i,j] <> 0 then
        y := y + c[i,j] * pow(a,i-1) * pow(b,j-1);
      end if;
    end for;
  end for;
end polynomial2D;


Limit the range of a signal with variable limits. The original variable Limiter
  Modelica.Blocks.Nonlinear.VariableLimiter showed problems with the
  pump model. The output variable y would often stay at the minimum
  (limit2) although u was larger than this value. The homotropy and
  smooth functions were not the problem as can be shown. Even removing
  both of them did not solve the problem that occured in special (?)
  hydraulic setups. See for example
  Zugabe.Fluid.Movers.Examples.test_nset.

within AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses;
block VariableLimiter
  "Limit the range of a signal with variable limits"
  extends Modelica.Blocks.Interfaces.SISO;
  parameter Boolean strict=false "= true, if strict limits with noEvent(..)"
  parameter Boolean limitsAtInit=true
    "Has no longer an effect and is only kept for backwards compatibility (the implementation uses now the homotopy operator)"
  Modelica.Blocks.Interfaces.RealInput limit1
    "Connector of Real input signal used as maximum of input u"
  Modelica.Blocks.Interfaces.RealInput limit2
    "Connector of Real input signal used as minimum of input u"
algorithm
  assert(limit1 >= limit2, "Input signals are not consistent: limit1 < limit2");

  if strict then
    y := smooth(0, noEvent(if u > limit1 then limit1 else if u < limit2 then
      limit2 else u));
  else
    y := smooth(0, if u > limit1 then limit1 else if u < limit2 then limit2
       else u);
  end if;
end VariableLimiter;


within AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses;
package efficiencyCharacteristic "eta = f(Q,H,P) - efficiency characteristic (not pump specific!)"
extends Modelica.Icons.Package;

end efficiencyCharacteristic;


'dp variable' for PumpHeadControlled. This controller implements the conventional variable dp control
  strategy. The pump's operating points fall on an ascending line with
  the point p0(Q0, H0) and p1(Qnom, Hnom) where Q0 and H0 are volume
  flow rate and pump head at zero mass flow. Hence p0 = (0, H0). p1 is
  the pump's design point. Normally, H0 = 0.5 * Hnom.

within AixLib.Fluid.Movers.PumpsPolynomialBased.Controls;
model CtrlDpVarH "'dp variable' for PumpHeadControlled"
  extends BaseClasses.PumpController;

  parameter Modelica.Units.NonSI.AngularVelocity_rpm Nstart=pumpParam.nMin + (
      pumpParam.nMax - pumpParam.nMin)*0.8 "pump speed at start of simulation";
  parameter Real Qnom(
    quantity="VolumeFlowRate",
    unit="m3/h",
    displayUnit="m3/h") = 0.5*max(pumpParam.maxMinSpeedCurves[:,1]) "Nominal volume flow rate in m³/h";
  parameter Modelica.Units.SI.Height Hnom=
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
      pumpParam.cHQN,
      Qnom,
      Nstart) "Nominal pump head in m (water)";
  parameter Modelica.Units.SI.Height H0=0.5*Hnom "pump head when Q == 0 m3/h";

  parameter Real k(unit="1") = 50 "Gain of controller";
  parameter Modelica.Units.SI.Time Ti(min=Modelica.Constants.small) = 0.01
    "Time constant of Integrator block";
  parameter Modelica.Units.SI.Time Td(min=0) = 0.001
    "Time constant of Derivative block";

  Modelica.Blocks.Sources.RealExpression headControl(y=(Hnom - H0)/Qnom*Q.y +
        H0)    "The given pump head according to controll strategy"
protected
  Modelica.Blocks.Routing.RealPassThrough powerPassThrough
  Modelica.Blocks.Routing.RealPassThrough efficiencyPassThrough
  Modelica.Blocks.Routing.RealPassThrough Q
    "Dummy for volume flow rate signal."
public
  parameter Modelica.Blocks.Types.SimpleController controllerTypePID=.Modelica.Blocks.Types.SimpleController.PID
    "Type of controller";
  Modelica.Blocks.Routing.BooleanPassThrough onOffPassThrough1
equation
  connect(pumpBus.PelMea, powerPassThrough.u)
  connect(powerPassThrough.y, pumpControllerBus.PelMea)
  connect(pumpBus.efficiencyMea, efficiencyPassThrough.u)
  connect(efficiencyPassThrough.y, pumpControllerBus.efficiencyMea)
  connect(pumpBus.vFRcur_m3h, Q.u)
  connect(headControl.y, pumpBus.dpSet)
  connect(pumpControllerBus.onSet, onOffPassThrough1.u)
  connect(onOffPassThrough1.y, pumpBus.onSet)
end CtrlDpVarH;


'dp variable' for PumpSpeedControlled. This controller implements the conventional variable dp control
  strategy. The pump's operating points fall on an ascending line with
  the point p0(Q0, H0) and p1(Qnom, Hnom) where Q0 and H0 are volume
  flow rate and pump head at zero mass flow. Hence p0 = (0, H0). p1 is
  the pump's design point. Normally, H0 = 0.5 * Hnom.

within AixLib.Fluid.Movers.PumpsPolynomialBased.Controls;
model CtrlDpVarN "'dp variable' for PumpSpeedControlled"
  extends BaseClasses.PumpController;

  parameter Modelica.Units.NonSI.AngularVelocity_rpm Nstart=pumpParam.nMin + (
      pumpParam.nMax - pumpParam.nMin)*0.8 "pump speed at start of simulation";
  parameter Real Qnom(
    quantity="VolumeFlowRate",
    unit="m3/h",
    displayUnit="m3/h") = 0.5*max(pumpParam.maxMinSpeedCurves[:,1])  "Nominal volume flow rate in m³/h";
  parameter Modelica.Units.SI.Height Hnom=
      AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.polynomial2D(
      pumpParam.cHQN,
      Qnom,
      Nstart) "Nominal pump head in m (water)";
  parameter Modelica.Units.SI.Height H0=0.5*Hnom "pump head when Q == 0 m3/h";

  parameter Real k(unit="1") = 10 "Gain of controller";
  parameter Modelica.Units.SI.Time Ti(min=Modelica.Constants.small) = 5
    "Time constant of Integrator block";
  parameter Modelica.Units.SI.Time Td(min=0) = 0.001
    "Time constant of Derivative block";

  Modelica.Blocks.Continuous.LimPID PID(
    k=k,
    Ti=Ti,
    Td=Td,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    y_start=Nstart,
    yMax=pumpParam.nMax,
    yMin=pumpParam.nMin,
    controllerType=controllerTypePID)
  Modelica.Blocks.Sources.RealExpression headControl(y=(Hnom - H0)/Qnom*Q.y +
        H0)    "The given pump head according to controll strategy"
  parameter Modelica.Blocks.Types.SimpleController controllerTypePID=.Modelica.Blocks.Types.SimpleController.PID
    "Type of controller";
  Modelica.Blocks.Logical.Switch pumpSpeedSwitch "If false: rpm_Input = 0"
  Modelica.Blocks.Sources.Constant zeroSpeed(k=0)
  Modelica.Blocks.Routing.RealPassThrough nActPassThrough
    "pass through of pump speed signal"
  Modelica.Blocks.Routing.BooleanPassThrough onOffPassThrough1
protected
  Modelica.Blocks.Routing.RealPassThrough powerPassThrough
  Modelica.Blocks.Routing.RealPassThrough efficiencyPassThrough
  Modelica.Blocks.Routing.RealPassThrough Q
    "Dummy for volume flow rate signal."
equation
  connect(pumpBus.PelMea, powerPassThrough.u)
  connect(powerPassThrough.y, pumpControllerBus.PelMea)
  connect(pumpBus.efficiencyMea, efficiencyPassThrough.u)
  connect(efficiencyPassThrough.y, pumpControllerBus.efficiencyMea)
  connect(PID.y, pumpSpeedSwitch.u1)
  connect(pumpBus.vFRcur_m3h, Q.u)
  connect(pumpBus.dpMea, PID.u_m)
  connect(headControl.y, PID.u_s)
  connect(pumpSpeedSwitch.y, pumpBus.rpmSet)
  connect(zeroSpeed.y, pumpSpeedSwitch.u3)
  connect(pumpBus.rpmMea, nActPassThrough.u)
  connect(nActPassThrough.y, pumpControllerBus.rpmMea)
  connect(pumpControllerBus.onSet, onOffPassThrough1.u)
  connect(onOffPassThrough1.y, pumpBus.onSet)
  connect(pumpControllerBus.onSet, pumpSpeedSwitch.u2)
end CtrlDpVarN;


'n_set' passhrough for PumpSpeedControlled. Simple Controller that passes the pump speed signal on the
  pumpControllerBus (rpm_input) to the pumpBus. This way the pump
  speed, computed somewhere else, can be applied directly to the
  PumpPhysics model.

within AixLib.Fluid.Movers.PumpsPolynomialBased.Controls;
model CtrlPassThroughN "'n_set' passhrough for PumpSpeedControlled"
  extends BaseClasses.PumpController;
equation
  connect(pumpBus, pumpControllerBus)
end CtrlPassThroughN;


Pump Controller

within AixLib.Fluid.Movers.PumpsPolynomialBased;
package Controls "Pump Controller"
  extends Modelica.Icons.VariantsPackage;

end Controls;


Base Classes for Controller Models

within AixLib.Fluid.Movers.PumpsPolynomialBased.Controls;
package BaseClasses "Base Classes for Controller Models"
  extends Modelica.Icons.BasesPackage;

end BaseClasses;


Pump controller base class. Partial pump controller with states. Currently the states only
  provide on and off mode. This can as well be replaced by the bolean
  onOff signal from the pumpControllerBus.

within AixLib.Fluid.Movers.PumpsPolynomialBased.Controls.BaseClasses;
partial model PumpController "Pump controller base class"

  parameter AixLib.DataBase.Pumps.PumpPolynomialBased.PumpBaseRecord pumpParam "pump parameter record"
  AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.PumpBus pumpControllerBus
  AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.PumpBus pumpBus
end PumpController;


info

within AixLib.Fluid.Movers.PumpsPolynomialBased;
package Examples
extends Modelica.Icons.ExamplesPackage;
end Examples;


testing the head controlled pump model.

within AixLib.Fluid.Movers.PumpsPolynomialBased.Examples;
model PumpHeadControlled "testing the head controlled pump model."
  extends Modelica.Icons.Example;

  replaceable package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;

  .AixLib.Fluid.Movers.PumpsPolynomialBased.PumpHeadControlled pump(
    m_flow_nominal=m_flow_nominal,
    calculatePower=true,
    calculateEfficiency=true,
    redeclare package Medium = Medium,
    pumpParam=DataBase.Pumps.PumpPolynomialBased.Pump_DN25_H1_8_V9())

  Modelica.Blocks.Sources.Ramp rampValvePosition(
    offset=0.5,
    height=0.5,
    duration(displayUnit="s") = 120,
    startTime(displayUnit="min") = 120)
  AixLib.Fluid.Sources.Boundary_pT vessle(
    redeclare package Medium = Medium, nPorts=2)
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1 "Nominal mass flow rate";
  Modelica.Blocks.Sources.BooleanPulse    PumpOn1(period=600, width=500/600*100,
    startTime=0)
  Modelica.Blocks.Sources.Ramp rampPumpDp(
    height=2,
    offset=2,
    duration(displayUnit="s") = 100,
    startTime(displayUnit="s") = 100)
  BaseClasses.PumpBus                                          pumpBus
  Actuators.Valves.TwoWayLinear             simpleValve(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    CvData=AixLib.Fluid.Types.CvTypes.Kv,
    Kv=6.3,
    dpFixed_nominal=1000)
equation
  connect(PumpOn1.y, pumpBus.onSet)
  connect(pumpBus, pump.pumpBus)
  connect(rampPumpDp.y, pumpBus.dpSet)
  connect(pump.port_b, simpleValve.port_a)
  connect(vessle.ports[1], pump.port_a)
  connect(simpleValve.port_b, vessle.ports[2])
  connect(rampValvePosition.y, simpleValve.y)
end PumpHeadControlled;


within AixLib.Fluid.Movers.PumpsPolynomialBased.Examples;
model PumpSpeedControlled "Testing the pump speed controlled model."
  extends Modelica.Icons.Example;

  replaceable package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;

  .AixLib.Fluid.Movers.PumpsPolynomialBased.PumpSpeedControlled pump(
    m_flow_nominal=m_flow_nominal,
    pumpParam=DataBase.Pumps.PumpPolynomialBased.Pump_DN25_H1_6_V4(),
    calculatePower=true,
    calculateEfficiency=true,
    redeclare function efficiencyCharacteristic =
        AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.efficiencyCharacteristic.Wilo_Formula_efficiency,
    redeclare package Medium = Medium)

  AixLib.Fluid.Movers.PumpsPolynomialBased.BaseClasses.PumpBus pumpBus
  Modelica.Blocks.Sources.BooleanPulse    PumpOn(period=600, width=500/600*100)
  Modelica.Blocks.Sources.Ramp rampPumpSpeed(
    height=pump.pumpParam.nMax - pump.pumpParam.nMin + 200,
    offset=pump.pumpParam.nMin - 100,
    duration(displayUnit="s") = 100,
    startTime(displayUnit="s") = 100)

  Modelica.Blocks.Sources.Ramp rampValvePosition(
    offset=0.5,
    height=0.48,
    duration(displayUnit="s") = 100,
    startTime(displayUnit="s") = 300)
  AixLib.Fluid.Sources.Boundary_pT vessle(
    redeclare package Medium = Medium, nPorts=2)
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";
  Actuators.Valves.TwoWayLinear             simpleValve(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=1000)
equation
  connect(pumpBus, pump.pumpBus)
  connect(PumpOn.y, pumpBus.onSet)
  connect(rampPumpSpeed.y, pumpBus.rpmSet)
  connect(simpleValve.y, rampValvePosition.y)
  connect(pump.port_b, simpleValve.port_a)
  connect(simpleValve.port_b, vessle.ports[1])
  connect(vessle.ports[2], pump.port_a)
end PumpSpeedControlled;


within AixLib.Fluid.Movers.PumpsPolynomialBased.Examples;
model PumpSpeedControlledDpV
  "Testing the pump dp-var algorithm with the speed controlled pump model."
  extends Modelica.Icons.Example;

  replaceable package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;

  .AixLib.Fluid.Movers.PumpsPolynomialBased.PumpSpeedControlled pump(
    m_flow_nominal=m_flow_nominal,
    calculatePower=true,
    calculateEfficiency=true,
    redeclare package Medium = Medium,
    pumpParam=DataBase.Pumps.PumpPolynomialBased.Pump_DN25_H1_6_V4())

  Modelica.Blocks.Sources.BooleanPulse PumpOn(period=600, width=500/600*100)

  Modelica.Blocks.Sources.Ramp rampValvePosition(
    offset=0.5,
    height=0.48,
    duration(displayUnit="s") = 120,
    startTime(displayUnit="min") = 120)
  AixLib.Fluid.Sources.Boundary_pT vessle(
    redeclare package Medium = Medium,
    nPorts=2)
  Controls.CtrlDpVarN ctrlDpVarN(pumpParam=
        DataBase.Pumps.PumpPolynomialBased.Pump_DN25_H1_6_V4())
  BaseClasses.PumpBus pumpBus
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1
    "Nominal mass flow rate";
  Actuators.Valves.TwoWayLinear             simpleValve(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=1000)
equation
  connect(vessle.ports[1], pump.port_a)
  connect(pump.pumpBus, ctrlDpVarN.pumpBus)
  connect(ctrlDpVarN.pumpControllerBus, pumpBus)
  connect(PumpOn.y, pumpBus.onSet)
  connect(simpleValve.port_b, vessle.ports[2])
  connect(pump.port_b, simpleValve.port_a)
  connect(simpleValve.y, rampValvePosition.y)
end PumpSpeedControlledDpV;


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

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


within AixLib.Fluid.Movers.Validation;
model ComparePowerInput
  "Compare power estimation with different input signal"
  extends Modelica.Icons.Example;
  extends AixLib.Fluid.Movers.Validation.BaseClasses.ComparePower(
    redeclare AixLib.Fluid.Movers.Data.Fans.Greenheck.BIDW15 per(
      etaMotMet=AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate,
      motorEfficiency(V_flow={0}, eta={0.7})),
    redeclare AixLib.Fluid.Movers.SpeedControlled_y mov1(
      redeclare final package Medium = Medium,
      per=per,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      addPowerToMedium=false,
      use_inputFilter=false),
    redeclare AixLib.Fluid.Movers.FlowControlled_dp mov2(
      redeclare final package Medium = Medium,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      per=per,
      addPowerToMedium=false,
      use_inputFilter=false,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal),
    redeclare AixLib.Fluid.Movers.FlowControlled_m_flow mov3(
      redeclare final package Medium = Medium,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      per=per,
      addPowerToMedium=false,
      use_inputFilter=false,
      m_flow_nominal=m_flow_nominal,
      dp_nominal=dp_nominal),
    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

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

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


Fans with different control signals as input.

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


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

within AixLib.Fluid.Movers.Validation;
model ControlledFlowMachineDynamic
  "Fans with different control signals as input and a dynamic speed signal"
  extends Modelica.Icons.Example;
  extends AixLib.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 AixLib.Fluid.Movers.Validation;
model FlowControlled_dp "Fan with zero mass flow rate and head as input"
  extends Modelica.Icons.Example;
 extends AixLib.Fluid.Movers.Validation.BaseClasses.FlowMachine_ZeroFlow(
    gain(k=dp_nominal),
    redeclare AixLib.Fluid.Movers.FlowControlled_dp floMacSta(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      use_inputFilter=false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    redeclare AixLib.Fluid.Movers.FlowControlled_dp floMacDyn(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      use_inputFilter=false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));

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


within AixLib.Fluid.Movers.Validation;
model FlowControlled_dpSystem
  "Demonstration of the use of prescribedPressure"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.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"
  AixLib.Fluid.Movers.FlowControlled_dp floConDp(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=1,
    use_inputFilter=false) "Regular dp controlled fan"
  AixLib.Fluid.Movers.FlowControlled_dp floConDpSystem(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=1,
    use_inputFilter=false,
    prescribeSystemPressure=true)
    "Dp controlled fan that sets pressure difference at remote point in the system"
  AixLib.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"
  AixLib.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_inputFilter=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_inputFilter=false,
    dpDamper_nominal=10,
    m_flow_nominal=m_flow_nominal/2)
             "Damper"
  Actuators.Dampers.Exponential dam3(
    redeclare package Medium = Medium,
    from_dp=true,
    use_inputFilter=false,
    dpDamper_nominal=10,
    m_flow_nominal=m_flow_nominal/2)
             "Damper"
  Actuators.Dampers.Exponential dam4(
    redeclare package Medium = Medium,
    from_dp=true,
    use_inputFilter=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"
  AixLib.Fluid.FixedResistances.PressureDrop duct3(
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal/2,
    m_flow_nominal=m_flow_nominal/2) "Duct pressure drop"
  AixLib.Fluid.FixedResistances.PressureDrop duct4(
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal/2,
    m_flow_nominal=m_flow_nominal/2) "Duct pressure drop"
  AixLib.Fluid.FixedResistances.PressureDrop duct1(
    redeclare package Medium = Medium,
    dp_nominal=dp_nominal/2,
    m_flow_nominal=m_flow_nominal/2) "Duct pressure drop"
  AixLib.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 AixLib.Fluid.Movers.Validation;
model FlowControlled_m_flow
  "Fan with zero mass flow rate and mass flow rate as input"
  extends Modelica.Icons.Example;
 extends AixLib.Fluid.Movers.Validation.BaseClasses.FlowMachine_ZeroFlow(
    gain(k=m_flow_nominal),
    redeclare AixLib.Fluid.Movers.FlowControlled_m_flow floMacSta(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      use_inputFilter=false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState),
    redeclare AixLib.Fluid.Movers.FlowControlled_m_flow floMacDyn(
      nominalValuesDefineDefaultPressureCurve=true,
      redeclare package Medium = Medium,
      m_flow_nominal=m_flow_nominal,
      use_inputFilter=false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));

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


Collection of validation models

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


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

  package Medium = AixLib.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 AixLib.Fluid.Movers.Data.Generic per(
    pressure(V_flow={0.945419103313839, 2.83300844704353, 4.71734892787522},
                 dp={ 3010.50788091068, 2632.22416812609, 830.122591943958}))
    "Performance data"

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

  Actuators.Valves.TwoWayLinear val1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_inputFilter=false,
    dpValve_nominal=dp_nominal/1000,
    from_dp=false) "Valve with very small pressure drop if fully open"
  Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    nPorts=8,
    p=101325,
    T=293.15) "Pressure boundary condition"

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

  AixLib.Fluid.Movers.SpeedControlled_y pum_dp(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_inputFilter=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=AixLib.Fluid.Types.InputType.Constant)
    "Pump with 2 data points for the pressure flow relation, with data at dp=0"

  AixLib.Fluid.Movers.SpeedControlled_y pum_m_flow(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_inputFilter=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=AixLib.Fluid.Types.InputType.Constant)
    "Pump with 2 data points for the pressure flow relation, with data at m_flow=0"

  AixLib.Fluid.Movers.SpeedControlled_y pum_no(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_inputFilter=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=AixLib.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 val2(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    use_inputFilter=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_inputFilter=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_inputFilter=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 AixLib.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"

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

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

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

  AixLib.Fluid.Movers.FlowControlled_m_flow forcedPump1(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_inputFilter=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"
  AixLib.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 AixLib.Fluid.Movers.Validation;
model Pump_stratos "Stratos pumps with speed as input"
  extends Modelica.Icons.Example;
 extends AixLib.Fluid.Movers.Validation.BaseClasses.FlowMachine_ZeroFlow(
    redeclare package Medium = AixLib.Media.Water,
    gain(k=1),
    m_flow_nominal=floMacSta.per.pressure.V_flow[3]*1000,
    dp_nominal=floMacSta.per.pressure.dp[3]/2,
    redeclare AixLib.Fluid.Movers.SpeedControlled_y floMacSta(
      redeclare package Medium = Medium,
      use_inputFilter=false,
      per=per),
    redeclare AixLib.Fluid.Movers.SpeedControlled_y floMacDyn(
      redeclare package Medium = Medium,
      use_inputFilter=false,
      per=per));
  parameter Data.Pumps.Wilo.Stratos25slash1to6 per
equation
  connect(gain.y, floMacSta.y)
  connect(gain.y, floMacDyn.y)
end Pump_stratos;