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;


Choose setup for this pool

within AixLib.Fluid.Pools;
model IndoorSwimmingPool
  parameter AixLib.DataBase.Pools.IndoorSwimmingPoolBaseDataDefinition
    poolParam "Choose setup for this pool"

   parameter AixLib.DataBase.Walls.WallBaseDataDefinition
    poolWallParam "Choose setup for this pool"

  replaceable package WaterMedium = AixLib.Media.Water

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

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

  // Water transfer coefficients according to VDI 2089 Blatt 1
  parameter Real betaNonUse(unit="m/s")=7/3600 "Water transfer coefficient during non opening hours"
  parameter Real betaCover(unit="m/s")=0.7/3600 "Water transfer coefficient during non opening hours with pool cover"
  parameter Real betaWavePool(unit="m/s")=50/3600 "Water transfer coefficient during wave pool operation"

  // Parameter and variables for evaporation
  final constant Modelica.Units.SI.SpecificHeatCapacity RD=461.52
    "Specific gas constant for steam";                                // Source: Klaus Lucas, Thermodynamik (2008)
  final parameter Modelica.Units.SI.SpecificEnergy hVap=
      AixLib.Media.Air.enthalpyOfCondensingGas(poolParam.TPool)
    "Latent heat of evaporating water";
  Modelica.Units.SI.MassFlowRate m_flow_evap(start=0.0)
    "mass flow rate between pool water and air due to evaporation";
  Modelica.Units.SI.Pressure psat_TPool=
      Modelica.Media.Air.ReferenceMoistAir.Utilities.Water95_Utilities.psat(
      poolWat.T) "Saturation pressure at pool temperature";
  Modelica.Units.SI.Pressure psat_TAir=
      Modelica.Media.Air.ReferenceMoistAir.Utilities.Water95_Utilities.psat(
      TAir) "Saturation pressure at air temperature";
  Real phi "Relative humidity";

  // Pump
  parameter Modelica.Units.SI.Pressure pumpHead=170000
    "Expected average flow resistance of water cycle";    //Source: Saunus 2005

  // Pool circulation flow rate
  final parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=
      poolParam.V_flow_nominal*rhoWater_default
      "Nominal circulation mass flow rate to the pool";
  final parameter Modelica.Units.SI.MassFlowRate m_flow_partial=
      poolParam.V_flow_par*rhoWater_default
    "Partial circulation mass flow rate to pool during non operating hours";
  Modelica.Units.SI.MassFlowRate m_flow_toPool(start=0.0)
     "Actual circulation mass flow rate to the pool";

  // Fresh water and water recycling
  final parameter Modelica.Units.SI.Efficiency eta=if poolParam.use_HRS then
      poolParam.etaHRS else 0;
  Modelica.Units.SI.MassFlowRate m_flow_add(start=0.0)
  "Mass flow of fresh water supplied to pool circulation system";
  parameter Modelica.Media.Interfaces.Types.Temperature TFreWater=283.15
    "Temperature of fresh water";

  // Convection and Radiation at pool water surface
  final parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConvAir=3.5
    "Coefficient of heat transfer between the water surface and the room air";
    // approximated for free and forced convection at velocities between 0,05 to 0,2 m/s  above a plane area
  parameter Real eps = 0.9*0.95
    "Product of expected emission coefficients of water (0.95) and the surrounding wall surfaces (0.95)";

  AixLib.Fluid.MixingVolumes.MixingVolume poolSto(
    redeclare package Medium = WaterMedium,
    energyDynamics=energyDynamics,
    massDynamics=massDynamics,
    T_start=poolParam.TPool,
    m_flow_nominal=m_flow_nominal,
    V=poolParam.VSto,
    nPorts=4) "water storage for pool "

  AixLib.Fluid.Sources.Boundary_pT Sinc(
    redeclare package Medium = WaterMedium, nPorts=1)
  AixLib.Fluid.MixingVolumes.MixingVolume poolWat(
    redeclare package Medium = WaterMedium,
    energyDynamics=energyDynamics,
    massDynamics=massDynamics,
    T_start=poolParam.TPool,
    m_flow_nominal=m_flow_nominal,
    V=poolParam.VPool,
    nPorts=3) "water volume of pool"

  Modelica.Blocks.Sources.RealExpression getSetMasFlo(y=m_flow_toPool)
    "set circulating mass flow of pool water"

  AixLib.Fluid.Movers.BaseClasses.IdealSource setEva(
    redeclare package Medium = WaterMedium,
    m_flow_small=0.00001,
    control_m_flow=true)
  Modelica.Blocks.Sources.RealExpression getEva(y=m_flow_evap)
  AixLib.Fluid.HeatExchangers.ConstantEffectiveness heatExchanger(
    redeclare package Medium1 = WaterMedium,
    redeclare package Medium2 = WaterMedium,
    m1_flow_nominal=poolParam.m_flow_out*1.5,
    m2_flow_nominal=poolParam.m_flow_out,
    dp1_nominal(displayUnit="bar") = 20000,
    dp2_nominal(displayUnit="bar") = 20000,
    eps=eps)
  Modelica.Blocks.Interfaces.RealInput timeOpe
    "Input profile for opening hours (0: closed, 1: open)"
  Modelica.Blocks.Interfaces.RealInput uRelPer
    "relative number of people related to max. value"

  Modelica.Blocks.Interfaces.RealInput TAir(
    final quantity="ThermodynamicTemperature",
    final unit="K",
    displayUnit="degC") "Temperature of the surrounding room air"
  Modelica.Blocks.Interfaces.RealInput X_w "Absolute humidty of the room Air"

  AixLib.ThermalZones.ReducedOrder.Multizone.BaseClasses.AbsToRelHum absToRelHum
    "Calculation of the relative humidity of the room air "

  Modelica.Blocks.Sources.RealExpression getAddWat(y=m_flow_add)
    "requried added fresh warter to the pool circulation"

  AixLib.Fluid.Sources.Boundary_pT sincEva(redeclare package Medium =
        WaterMedium, nPorts=1)

  Modelica.Fluid.Interfaces.FluidPort_b port_b1(redeclare package Medium =
        WaterMedium) if not poolParam.use_ideHeaExc
    "Fluid connector b1 (positive design flow direction is from port_a1 to port_b1)"

  Modelica.Fluid.Interfaces.FluidPort_a port_a1(redeclare package Medium =
        WaterMedium) if not poolParam.use_ideHeaExc
    "Fluid connector a1 (positive design flow direction is from port_a1 to port_b1)"

  AixLib.Fluid.Pools.BaseClasses.waveMachine waveMachine(
    heightWave=poolParam.heiWav,
    widthWave=poolParam.widWav,
    timeWavePul_start=poolParam.timeWavPul_start,
    periodeWavePul=poolParam.perWavPul,
    widthWavePul=poolParam.widWavPul) if poolParam.use_wavPool

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a convPool
    "Air temperature in zone"

  AixLib.Utilities.Interfaces.RadPort radPool
    "Mean Radiation Temperature of surrounding walls "

  Modelica.Thermal.HeatTransfer.Components.BodyRadiation radWaterSurface(
  final Gr=eps*poolParam.APool)
  "Model to depict the heat flow rate due to radiation between the pool surface an the surrounding walls"

  Modelica.Thermal.HeatTransfer.Components.Convection convWaterSurface
  "Convection at the watersurface"

  Modelica.Blocks.Sources.RealExpression getHeatCoefConv(y=hConvAir*poolParam.APool)
    "Coefficient of heat transfer between water surface and room air"

  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeatFlowEvapLoss

  Modelica.Blocks.Math.Gain hEva(final k=hVap)
    "Calculation of heat flow rate due to evaporation"

  Modelica.Blocks.Interfaces.RealOutput m_flow_add_out(
    final quantity="MassFlowRate",
    final unit= "kg/s")
    "Flow rate of added fresh water to the pool and water treatment system"

  AixLib.Fluid.Pools.BaseClasses.HeatTransferConduction heatTraCond(
    AWalInt=poolParam.AWalInt,
    AWalExt=poolParam.AWalExt,
    AFloInt=poolParam.AFloInt,
    AFloExt=poolParam.AFloExt,
    hConWaterHorizontal=poolParam.hConWatHor,
    hConWaterVertical=poolParam.hConWatVer,
    TPool=poolParam.TPool,
    PoolWall=poolWallParam)
    "Model to depict the heat flow rate through the pool walls to the bordering room/soil"

  Modelica.Blocks.Math.Gain minusGain(final k=-1)

  Modelica.Blocks.Interfaces.RealOutput QEva(
    final quantity="HeatFlowRate",
    final unit="W") "Heat due to evaporation"

   Modelica.Blocks.Interfaces.RealOutput QPool(
     final quantity="HeatFlowRate",
     final unit="W")
     if poolParam.use_ideHeaExc
   "Heat flow rate to maintain the pool at the set temperature"

  Modelica.Blocks.Interfaces.RealInput TSoil(
    final quantity="ThermodynamicTemperature",
    final unit="K",
    displayUnit="degC") "Temperature of Soil"

  Modelica.Blocks.Interfaces.RealOutput PPool(
    final quantity="Power",
    final unit="W")
    "Output eletric energy needed for pool operation"

  Modelica.Blocks.Interfaces.RealOutput TPool(
    final quantity="ThermodynamicTemperature",
    final unit="K")
    "current temperature of pool"

  Modelica.Blocks.Math.MultiSum elPower(nu=if poolParam.use_wavPool then 2 else 1)
    "Add electric power of pump and the optional wave pool"
  Sources.MassFlowSource_T bou(
    redeclare package Medium = WaterMedium,
    use_m_flow_in=true,
    T=TFreWater,
    nPorts=1)
  Modelica.Blocks.Sources.RealExpression getMeaTPool(y=poolWat.T)

  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow idealHeater
    if poolParam.use_ideHeaExc
  AixLib.Controls.Continuous.LimPID        PI(
    k=poolParam.KHeat,
    yMax=poolParam.QMaxHeat,
    yMin=poolParam.QMinHeat,
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    Ti=poolParam.timeHea) if poolParam.use_ideHeaExc
  Modelica.Blocks.Sources.RealExpression getSetTPool(y=poolParam.TPool)
    if poolParam.use_ideHeaExc
  AixLib.Fluid.Movers.FlowControlled_m_flow cirPump(
    redeclare package Medium = WaterMedium,
    redeclare Movers.Data.Generic per(
      pressure(V_flow={m_flow_nominal/1000/100,m_flow_nominal/1000,
            m_flow_nominal/1000/0.7}, dp={pumpHead/0.7,pumpHead,0}),
      etaHydMet=AixLib.Fluid.Movers.BaseClasses.Types.HydraulicEfficiencyMethod.Efficiency_VolumeFlowRate,

      etaMotMet=AixLib.Fluid.Movers.BaseClasses.Types.MotorEfficiencyMethod.Efficiency_VolumeFlowRate,

      efficiency(V_flow={m_flow_nominal/1000/100,m_flow_nominal/1000,
            m_flow_nominal/1000/0.7}, eta={0.7,0.8,0.7}),
      motorEfficiency(V_flow={m_flow_nominal/1000/100,m_flow_nominal/1000,
            m_flow_nominal/1000/0.7}, eta={0.9,0.9,0.9})),
    inputType=AixLib.Fluid.Types.InputType.Continuous,
    energyDynamics=energyDynamics,
    T_start=poolParam.TPool,
    allowFlowReversal=false,
    m_flow_nominal=m_flow_nominal,
    addPowerToMedium=false,
    dp_nominal=pumpHead,
    m_flow_start=m_flow_nominal)
    "circulation pump for permanent pool circulation "

  FixedResistances.PressureDrop              res(
    redeclare package Medium = WaterMedium,
    allowFlowReversal=false,
    m_flow_nominal=m_flow_nominal,
    show_T=false,
    from_dp=false,
    dp_nominal=pumpHead - poolParam.dpHeaExcPool,
    homotopyInitialization=true,
    linearized=false,
    deltaM=0.3) "representative resistance for whole system "

  Modelica.Blocks.Interfaces.RealOutput m_flow_eva(final quantity=
        "MassFlowRate", final unit="kg/s") "Evaporation mass flow"
protected
  final parameter Modelica.Units.SI.Density rhoWater_default=
      WaterMedium.density_pTX(
      p=WaterMedium.p_default,
      T=273.15 + 30,
      X=WaterMedium.X_default) "Default medium density";

equation
  // Fresh water and water recycling
  if poolParam.use_watRec then
    m_flow_add=(1-poolParam.x_rec)*(poolParam.m_flow_out + m_flow_evap);
  else
    m_flow_add=poolParam.m_flow_out + m_flow_evap;
  end if;

  // Pool circulation flow rate
  if poolParam.use_parLoa then
    if timeOpe > 0 then
      m_flow_toPool = m_flow_nominal;
    else
      m_flow_toPool = m_flow_partial;
    end if;
  else
    m_flow_toPool = m_flow_nominal;
  end if;

  // Evaporation according to VDI 2089 sheet 1, formula (1)
   phi=absToRelHum.relHum;
   if psat_TPool-phi*psat_TAir<0 then
     m_flow_evap=0.0;
   else
    if timeOpe > 0 then
      if uRelPer > 0 then
         m_flow_evap =uRelPer*(poolParam.betaInUse/(RD*0.5*(poolWat.T + TAir))*(
          psat_TPool - phi*psat_TAir)*poolParam.APool);
       else
         m_flow_evap =betaNonUse/(RD*0.5*(poolWat.T + TAir))*(psat_TPool - phi*
          psat_TAir)*poolParam.APool;
       end if;
     else
       if poolParam.use_poolCov then
         m_flow_evap =betaCover/(RD*0.5*(poolWat.T + TAir))*(psat_TPool - phi*
          psat_TAir)*poolParam.APool;
       else
         m_flow_evap =betaNonUse/(RD*0.5*(poolWat.T + TAir))*(psat_TPool - phi*
          psat_TAir)*poolParam.APool;
       end if;
     end if;
   end if;

  if poolParam.use_ideHeaExc then
    connect(res.port_b, poolWat.ports[1])
  else
    connect(poolWat.ports[1], port_a1)
    connect(port_b1, res.port_b)
  end if;

  connect(poolWat.ports[3], setEva.port_a)

  connect(poolSto.ports[2],heatExchanger. port_b1)
  connect(poolSto.ports[3],heatExchanger. port_a2)
  connect(poolSto.ports[4], cirPump.port_a)
  connect(poolSto.ports[1], poolWat.ports[2])

  connect(absToRelHum.TDryBul, TAir)
  connect(absToRelHum.absHum, X_w)
  connect(heatExchanger.port_b2, Sinc.ports[1])
  connect(setEva.port_b, sincEva.ports[1])
  connect(getEva.y, setEva.m_flow_in)
  connect(radWaterSurface.port_b, radPool)
  connect(convWaterSurface.fluid, convPool)
  connect(convWaterSurface.solid, poolWat.heatPort)
  connect(radWaterSurface.port_a, poolWat.heatPort)
  connect(preHeatFlowEvapLoss.port, poolWat.heatPort)
  connect(getEva.y, hEva.u)
  connect(heatTraCond.heatport_a, poolWat.heatPort)
  connect(hEva.y, minusGain.u)
  connect(hEva.y, QEva)
  connect(heatTraCond.TSoil, TSoil)
  connect(convPool, convPool)
  connect(getHeatCoefConv.y, convWaterSurface.Gc)
  connect(minusGain.y, preHeatFlowEvapLoss.Q_flow)

  connect(bou.ports[1],heatExchanger. port_a1)
  connect(waveMachine.open, timeOpe)
  connect(getMeaTPool.y, TPool)

  connect(PI.y, idealHeater.Q_flow)
  connect(idealHeater.port, poolWat.heatPort)
  connect(getSetTPool.y, PI.u_s)
  connect(QPool, PI.y)
  connect(bou.m_flow_in, getAddWat.y)
  connect(getAddWat.y, m_flow_add_out)
  connect(setEva.port_b, setEva.port_a)

  connect(elPower.u[1], cirPump.P)
  connect(elPower.y, PPool)
  connect(waveMachine.PWaveMachine, elPower.u[2])

  connect(PI.u_m,getMeaTPool. y)
  connect(getEva.y, m_flow_eva)
  connect(getSetMasFlo.y, cirPump.m_flow_in)
  connect(cirPump.port_b, res.port_a)
end IndoorSwimmingPool;


Medium in the component

within AixLib.Fluid.Pools.BaseClasses;
model AirFlowMoistureToROM

    replaceable package AirMedium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

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

  parameter Integer nPools = 1;

    //Parameters to enable addition of evaporation mass flow to ROM
  parameter Modelica.Units.SI.MassFlowRate m_flow_air_nominal=1
    "air flow rate between swimming hall and air layer above swimming pool";
  parameter Modelica.Units.SI.Volume VAirLay = 7500
    "volume of air layer above pool for mass exchange due to evaporation";

  Modelica.Blocks.Interfaces.RealInput QEva[nPools](final quantity=
        "HeatFlowRate", final unit="W") "Heat due to evaporation"
  Modelica.Blocks.Interfaces.RealInput m_flow_eva[nPools]
    "Water mass flow due to evaporation"
  MixingVolumes.MixingVolumeMoistAir AirLay(
    redeclare package Medium = AirMedium,
    T_start=303.15,
    V=VAirLay,
    energyDynamics=energyDynamics,
    m_flow_nominal=m_flow_air_nominal,
    nPorts=2) "Air layer above swimming pool, relevant for evaporation"
  Movers.FlowControlled_m_flow          sou(
    redeclare package Medium = AirMedium,
    energyDynamics=energyDynamics,
    m_flow_nominal=m_flow_air_nominal,
    addPowerToMedium=false,
    nominalValuesDefineDefaultPressureCurve=true)
  Modelica.Fluid.Interfaces.FluidPort_a port_a(redeclare package Medium =
        AirMedium)
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(redeclare package Medium =
        AirMedium)
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Blocks.Math.MultiSum Sum_m_flow_eva(nu=nPools)
  Modelica.Blocks.Math.MultiSum SumQEva(nu=nPools)
  Modelica.Blocks.Sources.RealExpression mAirExc(y=m_flow_air_nominal)
    "Set point for water mass fraction"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow prescribedHeatFlow
equation
  connect(m_flow_eva, Sum_m_flow_eva.u)
  connect(Sum_m_flow_eva.y, AirLay.mWat_flow)
  connect(sou.port_a, AirLay.ports[1])
  connect(sou.port_b, port_b)
  connect(port_a, AirLay.ports[2])
  connect(mAirExc.y, sou.m_flow_in)
  connect(prescribedHeatFlow.port, AirLay.heatPort)
  connect(SumQEva.y, prescribedHeatFlow.Q_flow)
  connect(QEva, SumQEva.u)
end AirFlowMoistureToROM;


Heat transfer due to conduction through pool walls. This model is a base model to calculate the heat transfer through pool walls. The pool walls are  divided in: vertical walls without earth contact, vertical walls with earth contact, pool floor without earth contact, pool floor with earth contact.

within AixLib.Fluid.Pools.BaseClasses;
model HeatTransferConduction
  "Heat transfer due to conduction through pool walls"

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

  parameter Modelica.Units.SI.Area AWalInt "Area of pool walls which is connected to inner rooms (inner pool walls)";
  parameter Modelica.Units.SI.Area AWalExt "Area of pool walls which is connected to the ground (pool wall with earth contact)";
  parameter Modelica.Units.SI.Area AFloInt "Area of pool floors which is connected to inner rooms (inner pool floor)";
  parameter Modelica.Units.SI.Area AFloExt "Area of pool floors which is connected to teh ground (pool floor with earth contact)";
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConWaterHorizontal "Mean value for the heat transfer coefficient of free convection on horizontal pool floors";
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConWaterVertical "Mean value for the heat transfer coefficient of free convection on vertical pool walls";

  parameter Modelica.Units.SI.Temperature TPool "Set temperature for pool";

  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature HeatFlowOuter
    "Generate Heat Flow for earth contact"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatport_a
    "Inlet for heattransfer"

  Modelica.Blocks.Interfaces.RealInput TSoil "Temperature of Soil"

  AixLib.ThermalZones.HighOrder.Components.Walls.BaseClasses.SimpleNLayer
    InnerPoolFloor(
    A=AFloInt,
    wallRec=PoolWall,
    T_start=fill(TPool, PoolWall.n),
    energyDynamics=energyDynamics)
  AixLib.Utilities.HeatTransfer.HeatConvInside HeatConvWaterHorizontalInner(
    hCon_const=hConWaterHorizontal,
    A=AFloInt,
    calcMethod=AixLib.ThermalZones.HighOrder.Components.Types.CalcMethodConvectiveHeatTransferInsideSurface.Custom_hCon,
    surfaceOrientation=AixLib.ThermalZones.HighOrder.Components.Types.InsideSurfaceOrientation.vertical_wall)
  AixLib.Utilities.HeatTransfer.HeatConvInside HeatConvWaterVerticalOuter(
    hCon_const=hConWaterVertical,
    A=AWalExt,
    calcMethod=AixLib.ThermalZones.HighOrder.Components.Types.CalcMethodConvectiveHeatTransferInsideSurface.Custom_hCon,
    surfaceOrientation=AixLib.ThermalZones.HighOrder.Components.Types.InsideSurfaceOrientation.vertical_wall)
  AixLib.ThermalZones.HighOrder.Components.Walls.BaseClasses.SimpleNLayer
    PoolWallWithEarthContact(
    A=AWalExt,
    wallRec=PoolWall,
    T_start=fill(TPool, PoolWall.n),
    energyDynamics=energyDynamics)
  AixLib.Utilities.HeatTransfer.HeatConvInside HeatConvWaterHorizontalOuter(
    hCon_const=hConWaterHorizontal,
    A=AFloExt,
    calcMethod=AixLib.ThermalZones.HighOrder.Components.Types.CalcMethodConvectiveHeatTransferInsideSurface.Custom_hCon,
    surfaceOrientation=AixLib.ThermalZones.HighOrder.Components.Types.InsideSurfaceOrientation.vertical_wall)
  AixLib.ThermalZones.HighOrder.Components.Walls.BaseClasses.SimpleNLayer
    PoolFloorWithEarthContact(
    A=AFloExt,
    wallRec=PoolWall,
    T_start=fill(TPool, PoolWall.n),
    energyDynamics=energyDynamics)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow
    prescribedHeatFlow1 "Generate Heat Flow for earth contact"
  AixLib.ThermalZones.HighOrder.Components.Walls.BaseClasses.SimpleNLayer
    InnerPoolWall(
    A=AWalInt,
    wallRec=PoolWall,
    T_start=fill(TPool, PoolWall.n),
    energyDynamics=energyDynamics)
  Modelica.Blocks.Sources.RealExpression HeatFlowInner(y=0)
    "Inner pool walls are not connected to other zones, only outer pool walls have earth contact"
  AixLib.Utilities.HeatTransfer.HeatConvInside HeatConvWaterVerticalInner(
    hCon_const=hConWaterVertical,
    A=AWalInt,
    calcMethod=AixLib.ThermalZones.HighOrder.Components.Types.CalcMethodConvectiveHeatTransferInsideSurface.Custom_hCon,
    surfaceOrientation=AixLib.ThermalZones.HighOrder.Components.Types.InsideSurfaceOrientation.vertical_wall)
  replaceable parameter AixLib.DataBase.Walls.WallBaseDataDefinition PoolWall
    "Wall type for pool walls, record contains all wall layers"
equation
  connect(HeatFlowOuter.T, TSoil)
  connect(heatport_a, heatport_a)
  connect(HeatConvWaterHorizontalInner.port_a, InnerPoolFloor.port_a)
  connect(PoolFloorWithEarthContact.port_b, HeatFlowOuter.port)
  connect(PoolFloorWithEarthContact.port_a, HeatConvWaterHorizontalOuter.port_a)
  connect(InnerPoolWall.port_b, prescribedHeatFlow1.port)
  connect(HeatConvWaterVerticalOuter.port_b, heatport_a)
  connect(HeatConvWaterHorizontalInner.port_b, heatport_a)
  connect(HeatConvWaterHorizontalOuter.port_b, heatport_a)
  connect(prescribedHeatFlow1.Q_flow, HeatFlowInner.y)
  connect(HeatFlowOuter.port, PoolWallWithEarthContact.port_b)
  connect(InnerPoolFloor.port_b, prescribedHeatFlow1.port)
  connect(HeatConvWaterVerticalOuter.port_a, PoolWallWithEarthContact.port_a)
  connect(InnerPoolWall.port_a, HeatConvWaterVerticalInner.port_a)
  connect(HeatConvWaterVerticalInner.port_b, heatport_a)
end HeatTransferConduction;


Calculate energy demands of a wave machine. Model to calculate the energy demand of a wavemachine. Based on values of:

within AixLib.Fluid.Pools.BaseClasses;
model waveMachine "Calculate energy demands of a wave machine"

  parameter Modelica.Units.SI.Length heightWave "Height of generated wave";
  parameter Modelica.Units.SI.Length widthWave
    "Width of generated wave/ width of wave machineoutlet";
  parameter Modelica.Units.SI.Time timeWavePul_start
    "Start time of first wave cycle";
  parameter Modelica.Units.SI.Time periodeWavePul "Time of cycling period";
  parameter Real widthWavePul "Fraction of time of wave generation within cycling period";

  Modelica.Blocks.Math.RealToBoolean useWavePool(threshold=1)
    "If input = 1, then true, else no waves generated"
  Modelica.Blocks.Tables.CombiTable1Dv tablePWave(
    y(unit="W/m"),
    tableOnFile=false,
    table=[0,0; 0.7,3500; 0.9,6000; 1.3,12000],
    extrapolation=Modelica.Blocks.Types.Extrapolation.LastTwoPoints)
    "Estimate consumed power per width to generate wave of a certain heigth; "
  Modelica.Blocks.Sources.RealExpression get_heightWave(y=heightWave)
    "Get height of generated wave"
  Modelica.Blocks.Interfaces.RealInput open "Input profil of wave machine"
  Modelica.Blocks.Interfaces.RealOutput PWaveMachine( final unit="W", final quantity="Power")
    "Power consumption of wave machine"
  Modelica.Blocks.Math.Gain multiply(k=widthWave) "Multply by width of wave"
  Modelica.Blocks.Sources.Constant zero(k=0)
    "no output if wave machine is off"
  Modelica.Blocks.Logical.Switch switchWaveMachine
  Modelica.Blocks.Logical.And and1
  Modelica.Blocks.Sources.BooleanPulse wavePoolCycle(
    width=widthWavePul,
    period=periodeWavePul,
    startTime=timeWavePul_start)
equation
  connect(get_heightWave.y, tablePWave.u[1])
  connect(multiply.u, tablePWave.y[1])
  connect(open, useWavePool.u)
  connect(switchWaveMachine.y, PWaveMachine)
  connect(multiply.y, switchWaveMachine.u1)
  connect(zero.y, switchWaveMachine.u3)
  connect(PWaveMachine, PWaveMachine)
  connect(useWavePool.y, and1.u1)
  connect(and1.y, switchWaveMachine.u2)
  connect(wavePoolCycle.y, and1.u2)
end waveMachine;


Example of an indoor swimming pool

within AixLib.Fluid.Pools.Examples;
model IndoorSwimmingPool "Example of an indoor swimming pool"
    extends Modelica.Icons.Example;
  .AixLib.Fluid.Pools.IndoorSwimmingPool indoorSwimming(poolParam=
        AixLib.DataBase.Pools.SportPool(),
    poolWallParam=
        AixLib.DataBase.Pools.SwimmingPoolWalls.ConcreteInsulationConstruction(),
                                           redeclare package WaterMedium =
        WaterMedium)

    replaceable package WaterMedium = AixLib.Media.Water

  Modelica.Blocks.Sources.RealExpression TSoil(y=273.15 + 8)
  Modelica.Blocks.Sources.RealExpression X_W(y=14.3/1000)
  Modelica.Blocks.Sources.RealExpression T_Air(y=273.15 + 30)
  Modelica.Blocks.Sources.Pulse timeOpe(
    amplitude=1,
    width=(13/15)*100,
    period=(24 - 7)*3600,
    offset=0,
    startTime=3600*7)
  Modelica.Blocks.Sources.Trapezoid uRelPer(
    amplitude=0.5,
    rising=7*3600,
    width=1*3600,
    falling=7*3600,
    period=17*3600,
    offset=0.3,
    startTime=7*3600)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature
    prescribedTemperature
equation
  connect(TSoil.y, indoorSwimming.TSoil)
  connect(indoorSwimming.X_w, X_W.y)
  connect(indoorSwimming.TAir, T_Air.y)
  connect(timeOpe.y, indoorSwimming.timeOpe)
  connect(uRelPer.y, indoorSwimming.uRelPer)
  connect(prescribedTemperature.T, T_Air.y)
  connect(prescribedTemperature.port, indoorSwimming.convPool)
end IndoorSwimmingPool;


within AixLib.Fluid.Pools.Examples;
model IndoorSwimmingPoolExternalHeatingSystem
    extends Modelica.Icons.Example;
  .AixLib.Fluid.Pools.IndoorSwimmingPool indoorSwimming(poolParam=
        AixLib.DataBase.Pools.SportPool(use_ideHeaExc=false),
    poolWallParam=
        AixLib.DataBase.Pools.SwimmingPoolWalls.ConcreteInsulationConstruction(),
                                                              redeclare package
      WaterMedium = WaterMedium)

    replaceable package WaterMedium = AixLib.Media.Water

  Modelica.Blocks.Sources.RealExpression TSoil(y=273.15 + 8)
  Modelica.Blocks.Sources.RealExpression X_W(y=14.3/1000)
  Modelica.Blocks.Sources.RealExpression T_Air(y=273.15 + 30)
  Modelica.Blocks.Sources.Pulse Opening(
    amplitude=1,
    width=(13/15)*100,
    period=(24 - 7)*3600,
    offset=0,
    startTime=3600*7)
  Modelica.Blocks.Sources.Trapezoid Person(
    amplitude=0.5,
    rising=7*3600,
    width=1*3600,
    falling=7*3600,
    period=17*3600,
    offset=0.3,
    startTime=7*3600)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature
    prescribedTemperature
  Controls.Continuous.LimPID        PI(
    k=1000,
    yMax=10000000,
    yMin=0,
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    Ti=1)
  Modelica.Blocks.Sources.RealExpression SetTemperature(y=273.15 + 28)
  MixingVolumes.MixingVolume vol(
    redeclare package Medium = AixLib.Media.Water,
    energyDynamics=indoorSwimming.energyDynamics,
    p_start=100000,
    m_flow_nominal=indoorSwimming.m_flow_nominal,
    V=2,
    nPorts=2)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow prescribedHeatFlow
equation
  connect(TSoil.y, indoorSwimming.TSoil)
  connect(indoorSwimming.X_w, X_W.y)
  connect(indoorSwimming.TAir, T_Air.y)
  connect(Opening.y, indoorSwimming.timeOpe)
  connect(Person.y, indoorSwimming.uRelPer)
  connect(prescribedTemperature.T, T_Air.y)
  connect(prescribedTemperature.port, indoorSwimming.convPool)
  connect(PI.u_s, SetTemperature.y)
  connect(indoorSwimming.TPool, PI.u_m)
  connect(vol.ports[1], indoorSwimming.port_a1)
  connect(vol.ports[2], indoorSwimming.port_b1)
  connect(prescribedHeatFlow.port, vol.heatPort)
  connect(PI.y, prescribedHeatFlow.Q_flow)
end IndoorSwimmingPoolExternalHeatingSystem;


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

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

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


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

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

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


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

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


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

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


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

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


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

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

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


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

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

equation
m_flow = port_a.m_flow;
end MassFlowRate;


Ideal one port mass fraction sensor

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

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

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


Ideal two port mass fraction sensor

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

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

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

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


within AixLib.Fluid.Sensors;
model ModularSensors
  "Model that contains mass flow, pressure, temperature and quality sensors
  for modular compressors"

  // Definition of parameters
  //
  parameter Modelica.Units.SI.Time tau=1 "Time constant at nominal flow rate"

  parameter Boolean transferHeat=false
    "if true, temperature T converges towards TAmb when no flow"
  parameter Modelica.Units.SI.Temperature TAmb=Medium.T_default
    "Fixed ambient temperature for heat transfer"
  parameter Modelica.Units.SI.Time tauHeaTra=1200
    "Time constant for heat transfer, default 20 minutes"

  parameter Modelica.Blocks.Types.Init initType=
    Modelica.Blocks.Types.Init.InitialState
    "Type of initialization (InitialState and InitialOutput are identical)"
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Initial or guess value of output (= state)"
  parameter Modelica.Units.SI.SpecificEnthalpy h_out_start=
      Medium.specificEnthalpy_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default) "Initial or guess value of output (= state)"

  // Extensions
  //
  extends Interfaces.PartialModularPort_ab;

  // Definition of variables
  //
  Medium.SaturationProperties satPro[nPorts]
    "Saturation states for each fluid port";

  Modelica.Units.SI.SpecificEnthalpy bubEnt[nPorts]
    "Bubble enthalpies for each fluid port";
  Modelica.Units.SI.SpecificEnthalpy dewEnt[nPorts]
    "Dew enthalpies for each fluid port";
  Real pTriCri[nPorts]
    "Trigger to check if medium exceeds critical pressure";

  // Definition of models
  //
  AixLib.Fluid.Sensors.MassFlowRate senMasFlo[nPorts](redeclare each final
      package                                                                      Medium =
                       Medium) "Mass flow sensors"
  AixLib.Fluid.Sensors.Pressure senPre[nPorts](redeclare each final package Medium =
               Medium) "Pressure sensors"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTem[nPorts](
    redeclare each final package Medium = Medium,
    each final m_flow_nominal=m_flow_nominal,
    each final m_flow_small=m_flow_small,
    each final tau=tau,
    each final initType=initType,
    each final T_start=T_start,
    each final transferHeat=transferHeat,
    each final TAmb=TAmb,
    each final tauHeaTra=tauHeaTra) "Temperature sensors"
  AixLib.Fluid.Sensors.SpecificEnthalpyTwoPort senSpeEnt[nPorts](
    redeclare each final package Medium = Medium,
    each final m_flow_nominal=m_flow_nominal,
    each final m_flow_small=m_flow_small,
    each final tau=tau,
    each final initType=initType,
    each final h_out_start=h_out_start) "Specific enthalpy sensors"

  // Definition of connectors
  //
  Modelica.Blocks.Interfaces.RealOutput meaPre[nPorts]
    "Pressures measured by sensors"
  Modelica.Blocks.Interfaces.RealOutput meaTem[nPorts]
    "Temperatures measured by sensors"
  Modelica.Blocks.Interfaces.RealOutput meaMasFlo[nPorts]
    "Mass flow rates measured by sensors"
  Modelica.Blocks.Interfaces.RealOutput meaQua[nPorts]
    "Qualities measured by sensors"

equation
  // Calculating vapour qualities
  //
  for i in 1:nPorts loop
    satPro[i] = Medium.setSat_p(senPre[i].p) "Set saturation properties";
    bubEnt[i] = Medium.bubbleEnthalpy(satPro[i]) "Calculate bubble enthalpies";
    dewEnt[i] = Medium.dewEnthalpy(satPro[i]) "Calculate dew enthalpies";
    pTriCri[i] = senPre[i].p/Medium.fluidConstants[1].criticalPressure
      "Check if medium exceeds critical pressure";
    meaQua
         [i] = noEvent(if (pTriCri[i]<1 and senSpeEnt[i].h_out<bubEnt[i]) then 0
     else if (pTriCri[i]<1 and senSpeEnt[i].h_out>bubEnt[i] and
     senSpeEnt[i].h_out<dewEnt[i]) then (senSpeEnt[i].h_out - bubEnt[i])/
     max(dewEnt[i] - bubEnt[i], 1e-6) else 1.0)
     "Calculate vapour qualities";
  end for;

  // Connection of sensors
  //
  for i in 1:nPorts loop
    connect(ports_a[i],senMasFlo[i].port_a);
    connect(senMasFlo[i].port_b,senPre[i].port);
    connect(senMasFlo[i].port_b,senTem[i].port_a);
    connect(senTem[i].port_b,senSpeEnt[i].port_a);
    connect(senSpeEnt[i].port_b,ports_b[i]);
  end for;

  // Connection of outputs
  //
  connect(meaPre,senPre.p);
  connect(meaTem,senTem.T);
  connect(meaMasFlo,senMasFlo.m_flow);
end ModularSensors;


Package with sensor models

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


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

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

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

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

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

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

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

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

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

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


Ideal two port trace substances sensor outputting in parts per million. This model outputs the trace substance of the passing fluid
in parts per million.
The sensor is ideal, i.e., it does not influence the fluid.
If the parameter tau is non-zero, then its output
is computed using a first order differential equation.
Setting tau=0 is not recommend. See

AixLib.Fluid.Sensors.UsersGuide for an explanation.

within AixLib.Fluid.Sensors;
model PPMTwoPort
  "Ideal two port trace substances sensor outputting in parts per million"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor(
     port_a(C_outflow(each final quantity="MassFraction",
                   each final unit="1",
                   each min=0,
                   each max=1)),
     port_b(C_outflow(each final quantity="MassFraction",
                   each final unit="1",
                   each min=0,
                   each max=1)));
  extends Modelica.Icons.RoundSensor;

  parameter String substanceName = "CO2" "Name of trace substance";
  parameter Real C_start(min=0) = 0
    "Initial or guess value of output (= state)"
  parameter Modelica.Units.SI.MolarMass MM=Modelica.Media.IdealGases.Common.SingleGasesData.CO2.MM
    "Molar mass of the trace substance";
  Modelica.Blocks.Interfaces.RealOutput ppm(min=0)
    "Trace substance in port medium in ppm"

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

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

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

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

  Real CMed(min=0, start=C_start, nominal=sum(Medium.C_nominal))=
    if allowFlowReversal then
              Modelica.Fluid.Utilities.regStep(
                x=port_a.m_flow,
                y1=s*port_b.C_outflow,
                y2=s*port_a.C_outflow,
                x_small=m_flow_small)
              else
                s*port_b.C_outflow
    "Trace substance concentration to which the sensor is exposed";
  Real C(min=0,start=C_start)
    "Trace substance concentration of the passing fluid"

initial equation
  assert(max(s) > 0.9, "Trace substance '" + substanceName + "' is not present in medium '"
         + Medium.mediumName + "'.\n"
         + "Check sensor parameter and medium model.");
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(C) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      C = C_start;
    end if;
  end if;

equation
  ppm = C*coeff;

  // Output signal of sensor
  if dynamic then
    der(C) = (CMed-C)*k*tauInv;
  else
    C = CMed;
  end if;
end PPMTwoPort;


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

within AixLib.Fluid.Sensors;
model Pressure "Ideal pressure sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor(
    final warnAboutOnePortConnection = false);
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput p(final quantity="AbsolutePressure",
                                          final unit="Pa",
                                          min=0) "Pressure at port"
equation
  p = port.p;
end Pressure;


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

within AixLib.Fluid.Sensors;
model RelativeHumidity "Ideal one port relative humidity sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor;
  extends Modelica.Icons.RoundSensor;

  Modelica.Blocks.Interfaces.RealOutput phi(final unit="1", min=0)
    "Relative humidity in port medium"

protected
  Modelica.Units.SI.Temperature T "Temperature of the medium";
  Medium.MassFraction Xi[Medium.nXi](
    quantity=Medium.substanceNames[1:Medium.nXi]) "Mass fraction of the medium";
equation
  Xi = inStream(port.Xi_outflow);
  T=Medium.temperature_phX(
      p=port.p,
      h=inStream(port.h_outflow),
      X=Xi);

  phi = AixLib.Utilities.Psychrometrics.Functions.phi_pTX(
    p=port.p,
    T=T,
    X_w=Xi[1]);
end RelativeHumidity;


Ideal two port relative humidity sensor. This model outputs the relative humidity of the fluid flowing from
port_a to port_b.
The sensor is ideal, i.e., it does not influence the fluid.

within AixLib.Fluid.Sensors;
model RelativeHumidityTwoPort "Ideal two port relative humidity sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput phi(final unit="1",
                                            min=0,
                                            start=phi_start)
    "Relative humidity of the passing fluid"
  parameter Real phi_start(final unit="1", min=0, max=1)=0.5
    "Initial or guess value of output (= state)"

protected
  Real phiMed(final unit="1", min=0, start=phi_start)
    "Relative humidity to which the sensor is exposed to";

protected
  Modelica.Units.SI.Temperature T_a
    "Temperature of the medium flowing from port_a to port_b";
  Medium.MassFraction Xi_a[Medium.nXi](
    quantity=Medium.substanceNames[1:Medium.nXi])
    "Mass fraction of the medium flowing from port_a to port_b";
  Real phi_a(final unit="1")
    "Relative humidity of the medium flowing from port_a to port_b";
  Modelica.Units.SI.Temperature T_b
    "Temperature of the medium flowing from port_b to port_a";
  Medium.MassFraction Xi_b[Medium.nXi](
    quantity=Medium.substanceNames[1:Medium.nXi])
    "Mass fraction of the medium flowing from port_b to port_a";
  Real phi_b(final unit="1")
    "Relative humidity of the medium flowing from port_b to port_a";

initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(phi) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      phi = phi_start;
    end if;
  end if;
equation

  // Since the sensor does not affect the medium, we can use
  // port_b.Xi_outflow = inStream(port_a.Xi_outflow).
  Xi_a = port_b.Xi_outflow;

  T_a=Medium.temperature_phX(
      p=port_a.p,
      h=port_b.h_outflow,
      X=Xi_a);

  phi_a = AixLib.Utilities.Psychrometrics.Functions.phi_pTX(
    p=port_a.p,
    T=T_a,
    X_w=Xi_a[1]);

  if allowFlowReversal then
    phi_b = AixLib.Utilities.Psychrometrics.Functions.phi_pTX(
      p=port_b.p,
      T=T_b,
      X_w=Xi_b[1]);
    T_b=Medium.temperature_phX(
      p=port_b.p,
      h=port_a.h_outflow,
      X=Xi_b);
    Xi_b = port_a.Xi_outflow;
    phiMed = Modelica.Fluid.Utilities.regStep(
               x=port_a.m_flow,
               y1=phi_a,
               y2=phi_b,
               x_small=m_flow_small);
  else
    phi_b = 0;
    T_b = 273.15;
    Xi_b = zeros(Medium.nXi);
    phiMed = phi_a;
  end if;
  // Output signal of sensor
  if dynamic then
    der(phi) = (phiMed-phi)*k*tauInv;
  else
    phi = phiMed;
  end if;
end RelativeHumidityTwoPort;


Ideal relative pressure sensor

within AixLib.Fluid.Sensors;
model RelativePressure "Ideal relative pressure sensor"
  extends Modelica.Icons.RectangularSensor;
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the sensor"

  Modelica.Fluid.Interfaces.FluidPort_a port_a(m_flow(min=0),
                                p(start=Medium.p_default),
                                redeclare package Medium = Medium)
    "Fluid connector of stream a"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(m_flow(min=0),
                                p(start=Medium.p_default),
                                redeclare package Medium = Medium)
    "Fluid connector of stream b"

  Modelica.Blocks.Interfaces.RealOutput p_rel(final quantity="PressureDifference",
                                              final unit="Pa",
                                              displayUnit="Pa")
    "Relative pressure of port_a minus port_b"
equation
  // Zero flow equations for connectors
  port_a.m_flow = 0;
  port_b.m_flow = 0;

  // No contribution of specific quantities
  port_a.h_outflow = 0;
  port_b.h_outflow = 0;
  port_a.Xi_outflow = zeros(Medium.nXi);
  port_b.Xi_outflow = zeros(Medium.nXi);
  port_a.C_outflow  = zeros(Medium.nC);
  port_b.C_outflow  = zeros(Medium.nC);

  // Relative pressure
  p_rel = port_a.p - port_b.p;
end RelativePressure;


Ideal relative temperature sensor

within AixLib.Fluid.Sensors;
model RelativeTemperature "Ideal relative temperature sensor"
  extends Modelica.Icons.RectangularSensor;
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the sensor"
  Modelica.Fluid.Interfaces.FluidPort_a port_a(m_flow(min=0),
                                p(start=Medium.p_default),
                                redeclare package Medium = Medium)
    "Fluid connector of stream a"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(m_flow(min=0),
                                p(start=Medium.p_default),
                                redeclare package Medium = Medium)
    "Fluid connector of stream b"
  Modelica.Blocks.Interfaces.RealOutput T_rel(final unit = "K",
                                              displayUnit = "K")
    "Temperature difference of port_a minus port_b"
equation
  // Zero flow equations for connectors
  port_a.m_flow = 0;
  port_b.m_flow = 0;
  // No contribution of specific quantities
  port_a.h_outflow = 0;
  port_b.h_outflow = 0;
  port_a.Xi_outflow = zeros(Medium.nXi);
  port_b.Xi_outflow = zeros(Medium.nXi);
  port_a.C_outflow  = zeros(Medium.nC);
  port_b.C_outflow  = zeros(Medium.nC);
  // Relative temperature
  T_rel = Medium.temperature(state=Medium.setState_phX(
            p=port_a.p, h=inStream(port_a.h_outflow), X=inStream(port_a.Xi_outflow))) -
          Medium.temperature(state=Medium.setState_phX(
            p=port_b.p, h=inStream(port_b.h_outflow), X=inStream(port_b.Xi_outflow)));
end RelativeTemperature;


within AixLib.Fluid.Sensors;
model SensibleEnthalpyFlowRate
  "Ideal enthalphy flow rate sensor that outputs the sensible enthalpy flow rate only"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor(
    redeclare replaceable package Medium =
        Modelica.Media.Interfaces.PartialCondensingGases,
    tau=0);
  extends AixLib.Fluid.BaseClasses.IndexMassFraction(final substanceName="water");
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput H_flow(final unit="W")
    "Sensible enthalpy flow rate, positive if from port_a to port_b"
  parameter Modelica.Units.SI.SpecificEnthalpy h_out_start=
      Medium.enthalpyOfNonCondensingGas(T=Medium.T_default)
    "Initial or guess value of measured specific sensible enthalpy"
protected
  Modelica.Units.SI.SpecificEnthalpy hMed_out(start=h_out_start)
    "Medium sensible enthalpy to which the sensor is exposed";
  Modelica.Units.SI.SpecificEnthalpy h_out(start=h_out_start)
    "Medium sensible enthalpy that is used to compute the enthalpy flow rate";
  Medium.MassFraction XiActual[Medium.nXi]
    "Medium mass fraction to which sensor is exposed to";
  Medium.SpecificEnthalpy hActual
    "Medium enthalpy to which sensor is exposed to";
initial equation
 // Compute initial state
 if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(h_out) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      h_out = h_out_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     XiActual = Modelica.Fluid.Utilities.regStep(
                  x=port_a.m_flow,
                  y1=port_b.Xi_outflow,
                  y2=port_a.Xi_outflow,
                  x_small=m_flow_small);
     hActual = Modelica.Fluid.Utilities.regStep(
                 x=port_a.m_flow,
                 y1=port_b.h_outflow,
                 y2=port_a.h_outflow,
                 x_small=m_flow_small);
  else
     XiActual = port_b.Xi_outflow;
     hActual = port_b.h_outflow;
  end if;
  // Specific enthalpy measured by sensor
  hMed_out = (1-XiActual[i_x]) * Medium.enthalpyOfNonCondensingGas(
      T=Medium.temperature(state=Medium.setState_phX(p=port_a.p, h=hActual, X=XiActual)));
  if dynamic then
    der(h_out) = (hMed_out-h_out)*k*tauInv;
  else
    h_out = hMed_out;
  end if;
  // Sensor output signal
  H_flow = port_a.m_flow * h_out;
end SensibleEnthalpyFlowRate;


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

within AixLib.Fluid.Sensors;
model SpecificEnthalpy "Ideal one port specific enthalpy sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor;
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput h_out(final quantity="SpecificEnergy",
                                              final unit="J/kg")
    "Specific enthalpy in port medium"
equation
  h_out = inStream(port.h_outflow);
end SpecificEnthalpy;


Ideal two port sensor for the specific enthalpy. This model outputs the specific enthalpy of a passing fluid.
The sensor is ideal, i.e. it does not influence the fluid.
If the parameter tau is non-zero, then its output
is computed using a first order differential equation.
Setting tau=0 is not recommend. See

AixLib.Fluid.Sensors.UsersGuide for an explanation.

within AixLib.Fluid.Sensors;
model SpecificEnthalpyTwoPort "Ideal two port sensor for the specific enthalpy"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  extends Modelica.Icons.RoundSensor;
  parameter Modelica.Units.SI.SpecificEnthalpy h_out_start=
      Medium.specificEnthalpy_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default) "Initial or guess value of output (= state)"
  Modelica.Blocks.Interfaces.RealOutput h_out(final quantity="SpecificEnergy",
                                              final unit="J/kg",
                                              start=h_out_start)
    "Specific enthalpy of the passing fluid"
protected
  Modelica.Units.SI.SpecificEnthalpy hMed_out(start=h_out_start)
    "Medium enthalpy to which the sensor is exposed";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(h_out) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      h_out = h_out_start;
    end if;
  end if;
equation
  if allowFlowReversal then
    hMed_out = Modelica.Fluid.Utilities.regStep(
                 x=port_a.m_flow,
                 y1=port_b.h_outflow,
                 y2=port_a.h_outflow,
                 x_small=m_flow_small);
  else
    hMed_out = port_b.h_outflow;
  end if;
  // Output signal of sensor
  if dynamic then
    der(h_out) = (hMed_out-h_out)*k*tauInv;
  else
    h_out = hMed_out;
  end if;
end SpecificEnthalpyTwoPort;


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

within AixLib.Fluid.Sensors;
model SpecificEntropy "Ideal one port specific entropy sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor;
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput s(final quantity="SpecificEntropy",
                                          final unit="J/(kg.K)")
    "Specific entropy in port medium"

equation
  s = Medium.specificEntropy(state=Medium.setState_phX(
          p=port.p, h=inStream(port.h_outflow), X=inStream(port.Xi_outflow)));
end SpecificEntropy;


Ideal two port sensor for the specific entropy. This model outputs the specific entropy of the passing fluid.
The sensor is ideal, i.e., it does not influence the fluid.
If the parameter tau is non-zero, then its output
is computed using a first order differential equation.
Setting tau=0 is not recommend. See

AixLib.Fluid.Sensors.UsersGuide for an explanation.

within AixLib.Fluid.Sensors;
model SpecificEntropyTwoPort "Ideal two port sensor for the specific entropy"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  extends Modelica.Icons.RoundSensor;
  parameter Modelica.Units.SI.SpecificEntropy s_start=
      Medium.specificEntropy_pTX(
      p=Medium.p_default,
      T=Medium.T_default,
      X=Medium.X_default) "Initial or guess value of output (= state)"
  Modelica.Blocks.Interfaces.RealOutput s(final quantity="SpecificEntropy",
                                          final unit="J/(kg.K)",
                                          start=s_start)
    "Specific entropy of the passing fluid"
protected
  Modelica.Units.SI.SpecificEntropy sMed(start=s_start)
    "Medium entropy to which the sensor is exposed";
  Medium.SpecificEntropy s_a_inflow
    "Specific entropy of inflowing fluid at port_a";
  Medium.SpecificEntropy s_b_inflow
    "Specific entropy of inflowing fluid at port_b, or s_a_inflow if uni-directional flow";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(s) = 0;
    elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      s = s_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     s_a_inflow = Medium.specificEntropy(state=
                    Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     s_b_inflow = Medium.specificEntropy(state=
                    Medium.setState_phX(p=port_a.p, h=port_a.h_outflow, X=port_a.Xi_outflow));
     sMed = Modelica.Fluid.Utilities.regStep(
           x=port_a.m_flow,
           y1=s_a_inflow,
           y2=s_b_inflow,
           x_small=m_flow_small);
  else
     sMed = Medium.specificEntropy(state=
           Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     s_a_inflow = sMed;
     s_b_inflow = sMed;
  end if;
  // Output signal of sensor
  if dynamic then
    der(s) = (sMed-s)*k*tauInv;
  else
    s = sMed;
  end if;
end SpecificEntropyTwoPort;


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

within AixLib.Fluid.Sensors;
model Temperature "Ideal one port temperature sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor;

  Modelica.Blocks.Interfaces.RealOutput T(final quantity="ThermodynamicTemperature",
                                          final unit = "K",
                                          min=0,
                                          displayUnit = "degC")
    "Temperature in port medium"

equation
  T = Medium.temperature(state=Medium.setState_phX(
        p=port.p, h=inStream(port.h_outflow), X=inStream(port.Xi_outflow)));
end Temperature;


Ideal two port temperature sensor. This model outputs the temperature of the medium in the flow
between its fluid ports. The sensor does not influence the fluid.

within AixLib.Fluid.Sensors;
model TemperatureTwoPort "Ideal two port temperature sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  Modelica.Blocks.Interfaces.RealOutput T(final quantity="ThermodynamicTemperature",
                                          final unit="K",
                                          displayUnit = "degC",
                                          min = 0,
                                          start=T_start)
    "Temperature of the passing fluid"
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Initial or guess value of output (= state)"

  parameter Boolean transferHeat = false
    "if true, temperature T converges towards TAmb when no flow"
  parameter Modelica.Units.SI.Temperature TAmb=Medium.T_default
    "Fixed ambient temperature for heat transfer"
  parameter Modelica.Units.SI.Time tauHeaTra(min=1) = 1200
    "Time constant for heat transfer, default 20 minutes"

protected
  parameter Real tauHeaTraInv(final unit = "1/s")=
    if tauHeaTra<1E-10 then 0 else 1/tauHeaTra
    "Dummy parameter to avoid division by tauHeaTra";
  parameter Real ratTau = if dynamic then tauHeaTra/tau else 1
    "Ratio of tau";
  Medium.Temperature TMed(start=T_start)
    "Medium temperature to which the sensor is exposed";
  Medium.Temperature T_a_inflow "Temperature of inflowing fluid at port_a";
  Medium.Temperature T_b_inflow
    "Temperature of inflowing fluid at port_b, or T_a_inflow if uni-directional flow";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(T) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      T = T_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     T_a_inflow = Medium.temperature(state=
                    Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     T_b_inflow = Medium.temperature(state=
                    Medium.setState_phX(p=port_a.p, h=port_a.h_outflow, X=port_a.Xi_outflow));
     TMed = Modelica.Fluid.Utilities.regStep(
              x=port_a.m_flow,
              y1=T_a_inflow,
              y2=T_b_inflow,
              x_small=m_flow_small);
  else
     TMed = Medium.temperature(state=
              Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     T_a_inflow = TMed;
     T_b_inflow = TMed;
  end if;
  // Output signal of sensor
  if dynamic then
    if transferHeat then
      der(T) = (TMed-T)*k*tauInv + (TAmb-T)*tauHeaTraInv/(ratTau*k+1);
    else
      der(T) = (TMed-T)*k*tauInv;
    end if;
  else
    T = TMed;
  end if;
end TemperatureTwoPort;


Ideal wet bulb temperature sensor

within AixLib.Fluid.Sensors;
model TemperatureWetBulbTwoPort "Ideal wet bulb temperature sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  Modelica.Blocks.Interfaces.RealOutput T(
    start=TWetBul_start,
    final quantity="ThermodynamicTemperature",
    final unit="K",
    displayUnit = "degC") "Wet bulb temperature in port medium"
  parameter Modelica.Units.SI.Temperature TWetBul_start=Medium.T_default
    "Initial or guess value of wet bulb temperature (used to compute initial output signal))"

protected
  Medium.Temperature TMedWetBul(start=TWetBul_start)
    "Medium wet bulb temperature to which the sensor is exposed";
  AixLib.Utilities.Psychrometrics.TWetBul_TDryBulXi wetBulMod(
    redeclare package Medium = Medium,
    TWetBul(start=TWetBul_start)) "Block for wet bulb temperature";
  Modelica.Units.SI.SpecificEnthalpy h "Specific enthalpy";
  Medium.MassFraction Xi[Medium.nXi]
    "Species vector, needed because indexed argument for the operator inStream is not supported";
initial equation
  // Initialization of wet bulb temperature
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(T) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
            initType == Modelica.Blocks.Types.Init.InitialOutput then
      T = TWetBul_start;
    end if;
  end if;
equation
  if allowFlowReversal then
    h  = Modelica.Fluid.Utilities.regStep(
           x=port_a.m_flow,
           y1=port_b.h_outflow,
           y2=port_a.h_outflow,
           x_small=m_flow_small);
    Xi = Modelica.Fluid.Utilities.regStep(
           x=port_a.m_flow,
           y1=port_b.Xi_outflow,
           y2=port_a.Xi_outflow,
           x_small=m_flow_small);
  else
    h = port_b.h_outflow;
    Xi = port_b.Xi_outflow;
  end if;
  // Compute wet bulb temperature
  wetBulMod.TDryBul = Medium.temperature_phX(
    p=port_a.p,
    h=h,
    X=if Medium.reducedX then cat(1, Xi, {1-sum(Xi)}) else Xi);
  wetBulMod.Xi = Xi;
  wetBulMod.p  = port_a.p;
  TMedWetBul = wetBulMod.TWetBul;
  // Output signal of sensor
  if dynamic then
    der(T) = (TMedWetBul-T)*k*tauInv;
  else
    T = TMedWetBul;
  end if;
end TemperatureWetBulbTwoPort;


Ideal one port trace substances sensor. This model outputs the trace substances contained in the fluid connected to its port.
The sensor is ideal, i.e., it does not influence the fluid.

within AixLib.Fluid.Sensors;
model TraceSubstances "Ideal one port trace substances sensor"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialAbsoluteSensor;
  extends Modelica.Icons.RoundSensor;

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

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

protected
  parameter Real s[:]= {
    if ( Modelica.Utilities.Strings.isEqual(string1=Medium.extraPropertiesNames[i],
                                            string2=substanceName,
                                            caseSensitive=false))
    then 1 else 0 for i in 1:Medium.nC}
    "Vector with zero everywhere except where species is"
initial equation
  assert(max(s) > 0.9, "Trace substance '" + substanceName + "' is not present in medium '"
         + Medium.mediumName + "'.\n"
         + "Check sensor parameter and medium model.");
equation
  // We obtain the species concentration with a vector multiplication
  // because Dymola 7.3 cannot find the derivative in the model
  // Buildings.Examples.VAVSystemCTControl.mo
  // if we set C = CVec[ind];
  C = s*inStream(port.C_outflow);
end TraceSubstances;


Ideal two port sensor for trace substance. This model outputs the trace substance of the passing fluid.
The sensor is ideal, i.e., it does not influence the fluid.
If the parameter tau is non-zero, then its output
is computed using a first order differential equation.
Setting tau=0 is not recommend. See

AixLib.Fluid.Sensors.UsersGuide for an explanation.

within AixLib.Fluid.Sensors;
model TraceSubstancesTwoPort "Ideal two port sensor for trace substance"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  extends Modelica.Icons.RoundSensor;
  Modelica.Blocks.Interfaces.RealOutput C(min=0,
                                          start=C_start)
    "Trace substance of the passing fluid"

  parameter String substanceName = "CO2" "Name of trace substance";
  parameter Real C_start(min=0) = 0
    "Initial or guess value of output (= state)"

protected
  constant Real sumC_nominal = sum(Medium.C_nominal) "Sum of Medium.C_nominal";
  Real CMed(min=0, start=C_start, nominal=
    if sumC_nominal > Modelica.Constants.eps then sumC_nominal else 1)
    "Medium trace substance to which the sensor is exposed";
  parameter Real s[:]= {
    if ( Modelica.Utilities.Strings.isEqual(string1=Medium.extraPropertiesNames[i],
                                            string2=substanceName,
                                            caseSensitive=false))
    then 1 else 0 for i in 1:Medium.nC}
    "Vector with zero everywhere except where species is";
initial equation
  assert(max(s) > 0.9, "Trace substance '" + substanceName + "' is not present in medium '"
         + Medium.mediumName + "'.\n"
         + "Check sensor parameter and medium model.");

  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(C) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      C = C_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     CMed = Modelica.Fluid.Utilities.regStep(
              x=port_a.m_flow,
              y1=s*port_b.C_outflow,
              y2=s*port_a.C_outflow,
              x_small=m_flow_small);
  else
     CMed = s*port_b.C_outflow;
  end if;
  // Output signal of sensor
  if dynamic then
    der(C) = (CMed-C)*k*tauInv;
  else
    C = CMed;
  end if;
end TraceSubstancesTwoPort;


User's Guide

within AixLib.Fluid.Sensors;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;
end UsersGuide;