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;


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

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

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

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

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

  AixLib.Fluid.Movers.SpeedControlled_y pump1(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  AixLib.Fluid.Movers.SpeedControlled_y pump2(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  AixLib.Fluid.Movers.SpeedControlled_y pump3(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  AixLib.Fluid.Movers.SpeedControlled_y pump4(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "Wilo Stratos pump"
  AixLib.Fluid.Movers.SpeedControlled_y pump5(
    y_start=1,
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per=per) "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)
    "Pump for forcing a certain mass flow rate"
  AixLib.Fluid.Movers.FlowControlled_m_flow forcedPump2(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"
  AixLib.Fluid.Movers.FlowControlled_m_flow forcedPump3(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"
  AixLib.Fluid.Movers.FlowControlled_m_flow forcedPump4(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"
  AixLib.Fluid.Movers.FlowControlled_m_flow forcedPump5(
    redeclare package Medium = Medium,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Pump for forcing a certain mass flow rate"

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

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

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

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

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


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

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


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

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

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

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


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

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

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

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

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

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

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

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


Medium model

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

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

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

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

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


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

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

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

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


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