within AixLib.Fluid.Storage.Examples;
model StorageBoiler
  extends Modelica.Icons.Example;

  replaceable package Medium =
     Modelica.Media.Water.ConstantPropertyLiquidWater
     constrainedby Modelica.Media.Interfaces.PartialMedium;

  AixLib.Fluid.Storage.StorageDetailed bufferStorage(
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare package MediumHC1 = Medium,
    redeclare package MediumHC2 = Medium,
    m1_flow_nominal=pipe1.m_flow_nominal,
    m2_flow_nominal=pipe1.m_flow_nominal,
    mHC1_flow_nominal=pipe1.m_flow_nominal,
    useHeatingCoil2=false,
    useHeatingRod=false,
    TStart=fill(343.15, 10),
    redeclare AixLib.DataBase.Storage.Generic_New_2000l data(
      hTank=2,
      hUpperPortDemand=1.95,
      hUpperPortSupply=1.95,
      hHC1Up=1.95,
      dTank=1,
      hTS2=1.95),
    n=10,
    hConIn=1500,
    hConOut=15,
    redeclare package Medium = Medium,
    hConHC1=300)
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature fixedTemperature(T = 283.15)
  AixLib.Fluid.Sources.Boundary_pT boundary_p(redeclare package Medium = Medium,nPorts=1)
  Modelica.Blocks.Sources.Constant SetTemp(k=273.15 + 80)
  AixLib.Fluid.FixedResistances.PressureDrop pipe(
    redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    dp_nominal=200)
  AixLib.Fluid.FixedResistances.HydraulicResistance hydraulicResistance(zeta = 1000,
    redeclare package Medium = Medium,
    diameter=0.05,
    m_flow_nominal=0.001)
  AixLib.Fluid.Sources.Boundary_ph boundary_ph1(use_p_in = true,
	h = 0.8e5,
    nPorts=1,
    redeclare package Medium = Medium)
  Modelica.Blocks.Sources.Ramp ramp(duration = 1000,
	height = 0.00001e5,
    offset=101325)
  AixLib.Fluid.Sources.Boundary_pT boundary_ph2(nPorts=1,
					  redeclare package Medium = Medium)
  AixLib.Fluid.FixedResistances.PressureDrop pipe1(
    redeclare package Medium = Medium,
    m_flow_nominal=0.5,
    dp_nominal=200)
  AixLib.Fluid.HeatExchangers.Heater_T       hea(
    redeclare package Medium = Medium,
    m_flow_nominal=0.01,
    dp_nominal=0)
  AixLib.Fluid.Movers.FlowControlled_dp pump(redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    T_start=298.15,
      m_flow_nominal=pipe1.m_flow_nominal,
    per(pressure(V_flow={0,pipe.m_flow_nominal/1000,pipe.m_flow_nominal/(1000*
            0.8)}, dp={dpSet.k/0.8,dpSet.k,0}), motorCooledByFluid=false))
  Modelica.Blocks.Sources.Constant dpSet(k=40000)
    "Constant set pressure difference for pump"
equation
  connect(pipe.port_b, hydraulicResistance.port_a)
  connect(ramp.y, boundary_ph1.p_in)
  connect(boundary_ph1.ports[1], pipe1.port_a)
  connect(hydraulicResistance.port_b, hea.port_a)
  connect(SetTemp.y, hea.TSet)
  connect(boundary_ph2.ports[1], bufferStorage.fluidportTop2)
  connect(bufferStorage.fluidportBottom2, pipe1.port_b)
  connect(pipe.port_a, bufferStorage.portHC1Out)
  connect(fixedTemperature.port, bufferStorage.heatportOutside)
  connect(pump.port_b, bufferStorage.portHC1In)
  connect(pump.port_a, hea.port_b)
  connect(boundary_p.ports[1], pump.port_a)
  connect(dpSet.y, pump.dp_in)
end StorageBoiler;


within AixLib.Fluid.Storage.Examples;
model StorageDetailedExample "Simple usage example for StorageDetailed"

  extends Modelica.Icons.Example;
  replaceable package Medium =
     Modelica.Media.Water.ConstantPropertyLiquidWater
     constrainedby Modelica.Media.Interfaces.PartialMedium;
  AixLib.Fluid.Storage.StorageDetailed bufferStorage(
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    massDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial,
    m1_flow_nominal=const.k,
    m2_flow_nominal=boundary1.m_flow,
    mHC1_flow_nominal=boundary3.m_flow,
    n=10,
    redeclare package Medium = Medium,
    data=AixLib.DataBase.Storage.Generic_New_2000l(),
    useHeatingCoil1=true,
    useHeatingCoil2=false,
    upToDownHC1=false,
    upToDownHC2=false,
    useHeatingRod=false,
    redeclare model HeatTransfer =
        AixLib.Fluid.Storage.BaseClasses.HeatTransferBuoyancyWetter,
    redeclare package MediumHC1 = Medium,
    redeclare package MediumHC2 = Medium,
    TStart={333.15,328.15,323.15,318.15,313.15,308.15,303.15,298.15,293.15,293.15})
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature fixedTemperature(T=283.15)
  AixLib.Fluid.Sources.MassFlowSource_T boundary(
    m_flow=0.2,
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=343.15,
    nPorts=1)
  AixLib.Fluid.Sources.Boundary_pT
                      boundary_ph5(redeclare package Medium = Medium,
      nPorts=1)
  AixLib.Fluid.Sources.MassFlowSource_T boundary1(
    m_flow=0,
    redeclare package Medium = Medium,
    T=343.15,
    nPorts=1)
  AixLib.Fluid.Sources.MassFlowSource_T boundary2(
    m_flow=0,
    redeclare package Medium = Medium,
    T=343.15,
    nPorts=1)
  Modelica.Blocks.Sources.Constant const(k=0.2)
  AixLib.Fluid.Sources.MassFlowSource_T boundary3(
    m_flow=0.5*const.k,
    redeclare package Medium = Medium,
    T=298.15,
    nPorts=1)
  AixLib.Fluid.Sources.Boundary_pT
                      boundary_ph1(redeclare package Medium = Medium, nPorts=1)
equation
  connect(boundary.m_flow_in, const.y)
  connect(boundary1.ports[1], bufferStorage.fluidportTop2)
  connect(bufferStorage.fluidportTop1, boundary.ports[1])
  connect(bufferStorage.fluidportBottom1, boundary_ph5.ports[1])
  connect(boundary2.ports[1], bufferStorage.fluidportBottom2)
  connect(fixedTemperature.port, bufferStorage.heatportOutside)
  connect(boundary3.ports[1], bufferStorage.portHC1In)
  connect(boundary_ph1.ports[1], bufferStorage.portHC1Out)
end StorageDetailedExample;


Example model with simple storage

within AixLib.Fluid.Storage.Examples;
model StorageSimpleExample "Example model with simple storage"
  extends Modelica.Icons.Example;
  StorageSimple storageSimple(
    redeclare package Medium = Medium,
    use_TOut=true,
    n=3,
    d=0.7,
    h=0.78,
    lambda_ins=0.032,
    s_ins=0.01,
    hConIn=1500,
    hConOut=15,
    V_HE=0.04,
    k_HE=1500,
    A_HE=10,
    m_flow_nominal_layer=m_flow_nominal_gen,
    m_flow_nominal_HE=m_flow_nominal_gen,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=fill(308.15, 3))
    "300 l storage"
  HeatExchangers.HeatingRod heatingRod(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal_gen,
    dp_nominal=dp_nominal_hr,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    Q_flow_nominal=8000,
    V=0.01,
    eta=0.98,
    use_countNumSwi=false)
  replaceable package Medium = AixLib.Media.Water
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal_gen=
  heatingRod.Q_flow_nominal/(dT_gen*Medium.cp_const)
  "Nominal mass flow rate in generation cycle";

  parameter Modelica.Units.SI.PressureDifference dp_nominal_hr = 1000
    "Pressure difference in heating rod";

  parameter Modelica.Units.SI.TemperatureDifference dT_gen = 8
  "Temperature difference in generation cycle";

  parameter Real bandwidth=5 "Bandwidth around reference signal";

  AixLib.Systems.HydraulicModules.SimpleConsumer simpleConsumer(
    redeclare package Medium = Medium,
    kA=heatingRod.Q_flow_nominal/dT_gen,
    V=0.2,
    m_flow_nominal=m_flow_nominal_gen,
    functionality="Q_flow_input")
  AixLib.Fluid.Sources.Boundary_pT pressureBoundary(
    redeclare package Medium = Medium,
    p=200000,
    nPorts=1)
  AixLib.Fluid.Movers.SpeedControlled_y pumpGen(redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      redeclare
      AixLib.Fluid.Movers.Data.Pumps.Wilo.CronolineIL80slash220dash4slash4 per)
  AixLib.Fluid.Movers.SpeedControlled_y pumpCon(redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      redeclare
      AixLib.Fluid.Movers.Data.Pumps.Wilo.CronolineIL80slash220dash4slash4 per)
    "Consumer pump"
  AixLib.Fluid.Sources.Boundary_pT pressureBoundary1(
    redeclare package Medium = Medium,
    p=200000,
    nPorts=1)
  Modelica.Blocks.Logical.OnOffController storageHysteresis(bandwidth=bandwidth,
      pre_y_start=true)
  AixLib.Controls.Continuous.LimPID PIDHR(
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    Ti=10,
    yMax=1,
    yMin=0,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    xi_start=1) "PID controller for heating rod"
  Modelica.Blocks.Logical.Switch HROffOrPI
  Modelica.Blocks.Sources.Constant TUpperHysteresis(k=273.15 + 40)
  Modelica.Blocks.Interfaces.RealOutput PelHR
    "Electrical power used to provide current heat flow"
  Modelica.Blocks.Sources.Cosine dailyHeatDemand(
    amplitude=-2000,
    f=1/86400,
    phase=3.1415926535898,
    offset=-4000)
  Modelica.Blocks.Sources.Constant Off(k=0)
  Modelica.Thermal.HeatTransfer.Celsius.FixedTemperature fixedTemperature(T=18)
  Modelica.Blocks.Logical.LessThreshold    greaterZero
  AixLib.Fluid.Sensors.MassFlowRate senMasFlo_gen(redeclare package Medium = Medium)
    "Mass flow rate sensor at generation side"
  Controls.Continuous.LimPID PIDPumpGen(
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    k=0.5,
    Ti=10,
    yMax=1,
    yMin=0,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    xi_start=1) "PID controller for generation system pump"
  AixLib.Fluid.Sensors.MassFlowRate senMasFlo_con(redeclare package Medium = Medium)
    "Mass fow rate sensor at consumer side"
  Modelica.Blocks.Sources.Constant m_flow_set(k=m_flow_nominal_gen)
    "Set mass flow rate"
  Controls.Continuous.LimPID PIDPumpCon(
    controllerType=Modelica.Blocks.Types.SimpleController.PI,
    yMax=1,
    yMin=0,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    xi_start=1) "PID controller for consumer pump"
  Modelica.Blocks.Logical.Switch PumpOnOffCon
  Modelica.Blocks.Sources.Constant Offpump(k=0)
  Modelica.Blocks.Logical.Switch PumpOnOffGen
  Modelica.Blocks.Math.Add add
  Modelica.Blocks.Sources.Constant addToSetTemp(k=bandwidth/2)

  FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal_gen,
    dp_nominal=20000)
  FixedResistances.PressureDrop res1(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal_gen,
    dp_nominal=20000)
equation
  connect(heatingRod.port_b, storageSimple.port_a_heatGenerator)
  connect(pumpGen.port_a, storageSimple.port_b_heatGenerator)
  connect(pressureBoundary.ports[1], pumpGen.port_a)
  connect(pumpCon.port_b, storageSimple.port_a_consumer)
  connect(pressureBoundary1.ports[1], pumpCon.port_a)
  connect(simpleConsumer.port_b, pumpCon.port_a)
  connect(storageHysteresis.y, HROffOrPI.u2)
  connect(HROffOrPI.y, heatingRod.u)
  connect(PIDHR.y, HROffOrPI.u1)
  connect(TUpperHysteresis.y, storageHysteresis.reference)
  connect(heatingRod.Pel, PelHR)
  connect(dailyHeatDemand.y, simpleConsumer.Q_flow)
  connect(Off.y, HROffOrPI.u3)
  connect(fixedTemperature.port, storageSimple.heatPort)
  connect(dailyHeatDemand.y, greaterZero.u)
  connect(pumpGen.port_b, senMasFlo_gen.port_a)
  connect(senMasFlo_con.port_b, simpleConsumer.port_a)
  connect(m_flow_set.y, PIDPumpGen.u_s)
  connect(senMasFlo_gen.m_flow, PIDPumpGen.u_m)
  connect(m_flow_set.y, PIDPumpCon.u_s)
  connect(PIDPumpCon.y, PumpOnOffCon.u1)
  connect(greaterZero.y, PumpOnOffCon.u2)
  connect(Offpump.y, PumpOnOffCon.u3)
  connect(PumpOnOffCon.y, pumpCon.y)
  connect(senMasFlo_con.m_flow, PIDPumpCon.u_m)
  connect(PIDPumpGen.y, PumpOnOffGen.u1)
  connect(storageHysteresis.y, PumpOnOffGen.u2)
  connect(PumpOnOffGen.y, pumpGen.y)
  connect(Offpump.y, PumpOnOffGen.u3)
  connect(add.y, PIDHR.u_s)
  connect(TUpperHysteresis.y, add.u2)
  connect(addToSetTemp.y, add.u1)
  connect(senMasFlo_gen.port_b, res.port_a)
  connect(res.port_b, heatingRod.port_a)
  connect(storageSimple.port_b_consumer, res1.port_a)
  connect(res1.port_b, senMasFlo_con.port_a)
  connect(storageSimple.TLayer[3], PIDHR.u_m)
  connect(storageSimple.TLayer[3], storageHysteresis.u)
end StorageSimpleExample;


within AixLib.Fluid.Storage.Examples;
model StorageSolarCollector
  extends Modelica.Icons.Example;

  replaceable package Medium = AixLib.Media.Water;

  AixLib.Fluid.Storage.StorageDetailed bufferStorage(
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    redeclare package MediumHC1 = Medium,
    redeclare package MediumHC2 = Medium,
    m1_flow_nominal=solarThermal.m_flow_nominal,
    m2_flow_nominal=solarThermal.m_flow_nominal,
    mHC1_flow_nominal=solarThermal.m_flow_nominal,
    useHeatingCoil2=false,
    useHeatingRod=false,
    TStart=fill(333.15, 10),
    redeclare AixLib.DataBase.Storage.Generic_New_2000l data(hHC1Up=2.1),
    n=10,
    hConIn=1500,
    hConOut=15,
    redeclare package Medium = Medium,
    hConHC1=1500)
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature fixedTemperature(T = 283.15)
  AixLib.Fluid.Sources.Boundary_pT boundary_p(redeclare package Medium = Medium,nPorts=1)
  AixLib.Fluid.FixedResistances.PressureDrop
                   res1(
    redeclare package Medium = Medium,
    m_flow_nominal=solarThermal.m_flow_nominal,
    dp_nominal=2000)
  AixLib.Fluid.Sources.Boundary_pT boundary_ph2(nPorts=1, redeclare package Medium = Medium)
  AixLib.Fluid.FixedResistances.PressureDrop
                   res(
    redeclare package Medium = Medium,
    allowFlowReversal=true,
    m_flow_nominal=solarThermal.m_flow_nominal,
    dp_nominal=2000)
  AixLib.Fluid.Solar.Thermal.SolarThermal solarThermal(
    vol(T(start=298.15, fixed=true)),
    Collector=AixLib.DataBase.SolarThermal.FlatCollector(),
    A=20,
    redeclare package Medium = Medium,
    m_flow_nominal=0.04,
    volPip=1)
  Modelica.Blocks.Sources.Pulse pulse(period = 3600,
    width=10,
    amplitude=0.05,
    offset=0)
  AixLib.Fluid.Actuators.Valves.TwoWayEqualPercentage valve(
    redeclare package Medium = Medium,
    m_flow_nominal=solarThermal.m_flow_nominal,
    dpValve_nominal=2000)
  AixLib.Fluid.Sensors.TemperatureTwoPort temperatureSensor(redeclare package Medium = Medium,
      m_flow_nominal=0.01)
  Modelica.Blocks.Continuous.LimPID PI(controllerType = Modelica.Blocks.Types.SimpleController.PI,
    k=0.005,
    Ti=60,
	yMax = 0.999,
	yMin = 0)
  Modelica.Blocks.Sources.Constant const(k = 273.15 + 70)
  Modelica.Blocks.Math.Add add(k2 = -1)
  Modelica.Blocks.Sources.Constant const1(k = 1)
  Modelica.Blocks.Sources.CombiTimeTable hotSummerDay(
    extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic,
    table=[0,21,0; 3600,20.6,0; 7200,20.5,0; 10800,20.4,0; 14400,20,6; 18000,20.5,
        106; 21600,22.4,251; 25200,24.1,402; 28800,26.3,540; 32400,28.4,657; 36000,
        30,739; 39600,31.5,777; 43200,31.5,778; 46800,32.5,737; 50400,32.5,657;
        54000,32.5,544; 57600,32.5,407; 61200,32.5,257; 64800,31.6,60; 68400,30.8,
        5; 72000,22.9,0; 75600,21.2,0; 79200,20.6,0; 82800,20.3,0],
    offset={273.15,0.01})
  AixLib.Fluid.Movers.FlowControlled_dp pump(redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
      m_flow_nominal=solarThermal.m_flow_nominal,
    per(pressure(V_flow={0,solarThermal.m_flow_nominal/1000,solarThermal.m_flow_nominal
            /(1000*0.8)}, dp={dpPumpInput.k/0.8,dpPumpInput.k,0})))
  Modelica.Blocks.Sources.Constant dpPumpInput(k=55000)
  AixLib.Fluid.Sources.MassFlowSource_T boundary(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_T_in=false,
    T=283.15,
    nPorts=1)
equation
  connect(hotSummerDay.y[2], solarThermal.Irradiation)
  connect(hotSummerDay.y[1], solarThermal.T_air)
  connect(solarThermal.port_b, temperatureSensor.port_a)
  connect(solarThermal.port_a,res1. port_b)
  connect(temperatureSensor.port_b, valve.port_a)
  connect(const.y, PI.u_s)
  connect(PI.y, add.u2)
  connect(temperatureSensor.T, PI.u_m)
  connect(const1.y, add.u1)
  connect(boundary_ph2.ports[1], bufferStorage.fluidportTop2)
  connect(res.port_b, bufferStorage.fluidportBottom2)
  connect(fixedTemperature.port, bufferStorage.heatportOutside)
  connect(res1.port_a, bufferStorage.portHC1Out)
  connect(valve.port_b, pump.port_a)
  connect(dpPumpInput.y, pump.dp_in)
  connect(pump.port_a, boundary_p.ports[1])
  connect(add.y, valve.y)
  connect(pump.port_b, bufferStorage.portHC1In)
  connect(boundary.ports[1], res.port_a)
  connect(pulse.y, boundary.m_flow_in)
end StorageSolarCollector;


Test model for stratified tank

within AixLib.Fluid.Storage.Examples;
model Stratified "Test model for stratified tank"
  extends Modelica.Icons.Example;

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

  AixLib.Fluid.Storage.Stratified tanSim(
    redeclare package Medium = Medium,
    hTan=3,
    dIns=0.3,
    nSeg=10,
    m_flow_nominal=0.1,
    VTan=3) "Tank"
    Modelica.Blocks.Sources.TimeTable TWat(table=[0,273.15 + 40; 3600,273.15 +
        40; 3600,273.15 + 20; 7200,273.15 + 20]) "Water temperature"
  AixLib.Fluid.Sources.Boundary_pT sou_1(
    p=300000 + 5000,
    T=273.15 + 50,
    redeclare package Medium = Medium,
    use_T_in=true,
    nPorts=2)
  AixLib.Fluid.Sources.Boundary_pT sin_1(
    redeclare package Medium = Medium,
    T=273.15 + 20,
    use_p_in=true,
    p=300000,
    nPorts=2)
  AixLib.Fluid.FixedResistances.PressureDrop res_1(
    from_dp=true,
    redeclare package Medium = Medium,
    dp_nominal=5000,
    m_flow_nominal=0.1)
  AixLib.Fluid.Storage.StratifiedEnhanced tanEnh(
    redeclare package Medium = Medium,
    hTan=3,
    dIns=0.3,
    nSeg=10,
    m_flow_nominal=0.1,
    VTan=3) "Tank"
  AixLib.Fluid.FixedResistances.PressureDrop res_2(
    from_dp=true,
    redeclare package Medium = Medium,
    dp_nominal=5000,
    m_flow_nominal=0.1)
  AixLib.Fluid.Sensors.EnthalpyFlowRate HOut_flow(redeclare package Medium =
        Medium, m_flow_nominal=0.1) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate HOut_flow1(redeclare package Medium =
        Medium, m_flow_nominal=0.1) "Enthalpy flow rate"
  Modelica.Blocks.Continuous.Integrator dH
    "Differenz in enthalpy (should be zero at steady-state)"
  Modelica.Blocks.Math.Add add(k2=-1)
    Modelica.Blocks.Sources.TimeTable P(table=[0,300000; 4200,300000; 4200,
        305000; 7200,305000; 7200,310000; 10800,310000; 10800,305000])
    "Pressure boundary condition"
  Modelica.Blocks.Sources.Sine sine(
    f=1/86400,
    amplitude=10,
    offset=273.15 + 20)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TBCSid2
    "Boundary condition for tank"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TBCSid1
    "Boundary condition for tank"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TBCTop1
    "Boundary condition for tank"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TBCTop2
    "Boundary condition for tank"
equation
  connect(TWat.y, sou_1.T_in)
  connect(tanSim.port_b, HOut_flow.port_a)
  connect(HOut_flow.port_b, res_1.port_a)
  connect(tanEnh.port_b, HOut_flow1.port_a)
  connect(HOut_flow1.port_b, res_2.port_a)
  connect(add.y, dH.u)
  connect(HOut_flow.H_flow, add.u1)
  connect(HOut_flow1.H_flow, add.u2)
  connect(P.y, sin_1.p_in)
  connect(sine.y, TBCSid1.T)
  connect(sine.y, TBCTop1.T)
  connect(sine.y, TBCSid2.T)
  connect(sine.y, TBCTop2.T)
  connect(TBCSid2.port, tanEnh.heaPorSid)
  connect(TBCTop2.port, tanEnh.heaPorTop)
  connect(sin_1.ports[1], res_1.port_b)
  connect(sin_1.ports[2], res_2.port_b)
  connect(sou_1.ports[1], tanSim.port_a)
  connect(sou_1.ports[2], tanEnh.port_a)
  connect(TBCSid1.port, tanSim.heaPorSid)
  connect(TBCTop1.port, tanSim.heaPorTop)
end Stratified;


within AixLib.Fluid.Storage.Examples;
model StratifiedEnhancedInternalHex
  "Example showing the use of StratifiedEnhancedInternalHex"
  extends Modelica.Icons.Example;

  package MediumTan = AixLib.Media.Water "Medium in the tank";
  package MediumHex = AixLib.Media.Water "Medium in the heat exchanger";

  parameter Modelica.Units.SI.PressureDifference dpHex_nominal=2500
    "Pressure drop across the heat exchanger at nominal conditions";

  parameter Modelica.Units.SI.MassFlowRate mHex_flow_nominal=0.278
    "Mass flow rate of heat exchanger";

  AixLib.Fluid.Sources.Boundary_pT bouWat(redeclare package Medium =
        MediumTan, nPorts=3)
    "Boundary condition for water (used to set pressure)"
  AixLib.Fluid.Sources.Boundary_pT solColSup(
    redeclare package Medium = MediumHex,
    nPorts=3,
    use_p_in=true,
    T=353.15) "Water from solar collector"
  AixLib.Fluid.Sources.Boundary_pT toSolCol(
    redeclare package Medium = MediumHex,
    nPorts=3,
    p(displayUnit="Pa") = 3E5,
    T=283.15) "Water to solar collector"
  AixLib.Fluid.Storage.StratifiedEnhancedInternalHex tanSte(
    redeclare package Medium = MediumTan,
    m_flow_nominal=0.001,
    VTan=0.151416,
    dIns=0.0762,
    redeclare package MediumHex = MediumHex,
    CHex=40,
    Q_flow_nominal=0.278*4200*20,
    hTan=1.746,
    hHex_a=0.995,
    hHex_b=0.1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    energyDynamicsHex=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=false,
    allowFlowReversalHex=false,
    mHex_flow_nominal=mHex_flow_nominal,
    TTan_nominal=293.15,
    THex_nominal=323.15,
    dpHex_nominal=dpHex_nominal)
    "Tank with heat exchanger configured as steady state"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTemSte(
    redeclare package Medium = MediumHex,
    allowFlowReversal=false,
    m_flow_nominal=mHex_flow_nominal,
    tau=0) "Temperature sensor for outlet of steady-state heat exchanger"
  Modelica.Blocks.Sources.Step step(
    height=dpHex_nominal,
    offset=3E5,
    startTime=300) "Step input for mass flow rate"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTemDyn(
    redeclare package Medium = MediumHex,
    allowFlowReversal=false,
    m_flow_nominal=mHex_flow_nominal,
    tau=0) "Temperature sensor for outlet of dynamic heat exchanger"
  AixLib.Fluid.Storage.StratifiedEnhancedInternalHex tanDyn(
    redeclare package Medium = MediumTan,
    m_flow_nominal=0.001,
    VTan=0.151416,
    dIns=0.0762,
    redeclare package MediumHex = MediumHex,
    CHex=40,
    Q_flow_nominal=0.278*4200*20,
    hTan=1.746,
    hHex_a=0.995,
    hHex_b=0.1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    allowFlowReversal=false,
    allowFlowReversalHex=false,
    mHex_flow_nominal=mHex_flow_nominal,
    energyDynamicsHex=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TTan_nominal=293.15,
    THex_nominal=323.15) "Tank with heat exchanger configured as dynamic"
  AixLib.Fluid.Storage.StratifiedEnhancedInternalHex tanDynSol(
    redeclare package Medium = MediumTan,
    m_flow_nominal=0.001,
    VTan=0.151416,
    dIns=0.0762,
    redeclare package MediumHex = MediumHex,
    CHex=40,
    Q_flow_nominal=0.278*4200*20,
    hTan=1.746,
    hHex_a=0.995,
    hHex_b=0.1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    allowFlowReversal=false,
    allowFlowReversalHex=false,
    mHex_flow_nominal=mHex_flow_nominal,
    energyDynamicsHex=Modelica.Fluid.Types.Dynamics.SteadyState,
    energyDynamicsHexSolid=Modelica.Fluid.Types.Dynamics.SteadyStateInitial,
    TTan_nominal=293.15,
    THex_nominal=323.15)
    "Tank with heat exchanger configured as steady-state except for metal which is dynamic"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTemDynSol(
    redeclare package Medium = MediumHex,
    allowFlowReversal=false,
    m_flow_nominal=mHex_flow_nominal,
    tau=0)
    "Temperature sensor for outlet of steady state heat exchanger with solid configured dynamic"
equation
  connect(solColSup.ports[1], tanSte.portHex_a)
  connect(senTemSte.port_b, toSolCol.ports[1])
  connect(senTemSte.port_a, tanSte.portHex_b)
  connect(senTemDyn.port_a, tanDyn.portHex_b)
  connect(senTemDyn.port_b, toSolCol.ports[2])
  connect(tanDynSol.portHex_b, senTemDynSol.port_a)
  connect(senTemDynSol.port_b, toSolCol.ports[3])
  connect(solColSup.ports[2], tanDyn.portHex_a)
  connect(solColSup.ports[3], tanDynSol.portHex_a)
  connect(bouWat.ports[1], tanSte.port_b)
  connect(bouWat.ports[2], tanDyn.port_b)
  connect(bouWat.ports[3], tanDynSol.port_b)
  connect(step.y, solColSup.p_in)
end StratifiedEnhancedInternalHex;


within AixLib.Fluid.Storage.Examples;
model StratifiedUnloadAtMinimumTemperature
  "Example that demonstrates how to draw from a hot water tank at the minimum temperature"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water "Medium model";

  parameter Modelica.Units.SI.Volume VTan=3 "Tank volume";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=3*1000/3600
    "Nominal mass flow rate";

  constant Integer nSeg=5 "Number of volume segments";

  AixLib.Fluid.Storage.Stratified tan(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    VTan=VTan,
    hTan=2,
    dIns=0.2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nSeg=nSeg,
    T_start=353.15) "Hot water storage tank"
  AixLib.Fluid.Sources.Boundary_pT loa(redeclare package Medium = Medium,
      nPorts=1)
    "Load (imposed by a constant pressure boundary condition and the flow of masSou)"
  AixLib.Fluid.Sources.MassFlowSource_T masSou(
    nPorts=1,
    redeclare package Medium = Medium,
    m_flow=m_flow_nominal) "Mass flow rate into the tank"

  AixLib.Fluid.Actuators.Valves.TwoWayLinear valTop(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=3000,
    use_inputFilter=false) "Control valve at top"

  AixLib.Fluid.Actuators.Valves.TwoWayLinear valMid(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=3000,
    use_inputFilter=false) "Control valve at middle"

  AixLib.Fluid.Actuators.Valves.TwoWayLinear valBot(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dpValve_nominal=3000,
    use_inputFilter=false) "Control valve at bottom"

  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TMid
    "Temperature tank middle"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TBot
    "Temperature tank bottom"
  Modelica.Blocks.Logical.Hysteresis onOffBot(uLow=273.15 + 40 - 0.05, uHigh=
        273.15 + 40 + 0.05)
    "Controller for valve at bottom"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTem(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    tau=0) "Outflowing temperature"
  Modelica.Blocks.Logical.Hysteresis onOffMid(uLow=273.15 + 40 - 0.05, uHigh=
        273.15 + 40 + 0.05)
    "Controller for valve at middle of tank"
  Modelica.Blocks.Logical.And and2
    "And block to compute control action for middle valve"
  Modelica.Blocks.Math.BooleanToReal yMid
    "Boolean to real conversion for valve at middle"
  Modelica.Blocks.Math.BooleanToReal yTop
    "Boolean to real conversion for valve at top"
  Modelica.Blocks.Logical.Nor nor
    "Nor block for top-most control valve"
  Modelica.Blocks.Logical.Not not1
    "Not block to negate control action of upper level control"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow hea
    "Heat input at the bottom of the tank"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TTop
    "Temperature tank top"
  Modelica.Blocks.Logical.Hysteresis onOffHea(uLow=273.15 + 50 - 0.05, uHigh=
        273.15 + 50 + 0.05)
    "Controller for heater at bottom"
  Modelica.Blocks.Math.BooleanToReal yHea(realFalse=150000)
    "Boolean to real for valve at bottom"
  Modelica.Blocks.Math.BooleanToReal yBot
    "Boolean to real conversion for valve at bottom"
equation
  connect(masSou.ports[1], tan.port_b)
  connect(TMid.port, tan.heaPorVol[3])
  connect(TBot.port, tan.heaPorVol[5])
  connect(valTop.port_b, senTem.port_a)
  connect(valMid.port_b, senTem.port_a)
  connect(valBot.port_b, senTem.port_a)
  connect(senTem.port_b,loa. ports[1])
  connect(valTop.port_a, tan.fluPorVol[1])
  connect(valMid.port_a, tan.fluPorVol[3])
  connect(valBot.port_a, tan.fluPorVol[5])
  connect(onOffMid.y, and2.u1)
  connect(yMid.u, and2.y)
  connect(yTop.u, nor.y)
  connect(and2.y, nor.u1)
  connect(onOffBot.y, nor.u2)
  connect(yTop.y, valTop.y)
  connect(yMid.y, valMid.y)
  connect(not1.u, onOffBot.y)
  connect(not1.y, and2.u2)
  connect(hea.port, tan.heaPorVol[5])
  connect(TTop.port, tan.heaPorVol[1])
  connect(onOffHea.u, TTop.T)
  connect(onOffHea.y, yHea.u)
  connect(hea.Q_flow, yHea.y)
  connect(onOffBot.y, yBot.u)
  connect(yBot.y, valBot.y)
  connect(TBot.T, onOffBot.u)
  connect(onOffMid.u, TMid.T)
end StratifiedUnloadAtMinimumTemperature;


within AixLib.Fluid.Storage.Examples;
model TwoPhaseSeparator
  "Test model to show the functionality of the two-phase seperator"
  extends Modelica.Icons.Example;

  // Definition of medium and parameters
  //
  package Medium =
   Modelica.Media.R134a.R134a_ph
   "Actual medium of the compressor";

  parameter Modelica.Units.SI.AbsolutePressure pInl=Medium.pressure(
      Medium.setBubbleState(Medium.setSat_T(TInl + 5)))
    "Actual pressure at inlet conditions";
  parameter Modelica.Units.SI.Temperature TInl=303.15
    "Actual temperature at inlet conditions";
  parameter Modelica.Units.SI.AbsolutePressure pOut=Medium.pressure(
      Medium.setDewState(Medium.setSat_T(TOut)))
    "Actual set point of the compressor's outlet pressure";
  parameter Modelica.Units.SI.Temperature TOut=293.15
    "Actual temperature at outlet conditions";

  // Definition of models
  //
  Modelica.Blocks.Sources.Ramp rampTInl(
    duration=1,
    height=0,
    offset=TOut)
    "Ramp to provide temperature at tank's inlet"
  Sources.MassFlowSource_T source(
    redeclare package Medium = Medium,
    T=TInl,
    m_flow=0.5,
    use_T_in=true,
    nPorts=1)
    "Source of constant mass flow and variable temperature"
  AixLib.Fluid.Storage.TwoPhaseSeparator twoPhaseSeperator(
    redeclare package Medium = Medium,
    show_T=false,
    show_V_flow=false,
    useHeatLoss=false,
    show_tankPropertiesDetailed=true,
    steSta=false,
    show_heatLosses=true,
    VTanInn=10e-3)
    "Model of a two-phase tank loacted after condenser"
  AixLib.Fluid.Sources.Boundary_pT sink(
    redeclare package Medium = Medium,
    use_p_in=true,
    T=TOut,
    nPorts=1)
    "Sink of constant temperature and variable pressure"
  Modelica.Blocks.Sources.Ramp rampPOut(
    duration=1,
    height=pOut - pInl,
    offset=pInl)
    "Ramp to provide pressure at tank's outlet"
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature
    fixedTemperature(T=258.15)
    "Fixed ambient temperature to simulate heat losses to ambient"

equation
  // Connections of the models
  //
  connect(source.ports[1], twoPhaseSeperator.port_a)
  connect(twoPhaseSeperator.port_b,sink. ports[1])
  connect(rampTInl.y, source.T_in)
  connect(rampPOut.y,sink. p_in)
  connect(fixedTemperature.port, twoPhaseSeperator.heatPort)
end TwoPhaseSeparator;


within AixLib.Fluid.Storage.Validation;
model HeatExchangerDynamics
  "Test model for stratified tank with steady-state and dynamic heat exchanger balance"
  extends Modelica.Icons.Example;

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

  constant Integer nSeg = 7 "Number of segments in tank";

  parameter Modelica.Units.SI.HeatFlowRate QHex_flow_nominal=2000
    "Design heat flow rate of heat exchanger";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=QHex_flow_nominal/
      4200/4;

  AixLib.Fluid.Sources.Boundary_pT watInTan(
    redeclare package Medium = Medium,
    use_T_in=false,
    nPorts=2,
    T=273.15 + 30,
    p(displayUnit="Pa")) "Boundary condition for water in the tank"
  AixLib.Fluid.Sources.MassFlowSource_T mHex_flow1(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=273.15 + 60,
    nPorts=1) "Mass flow rate through the heat exchanger"
  model Tank = StratifiedEnhancedInternalHex (
    redeclare package Medium = Medium,
    redeclare package MediumHex = Medium,
    hTan=2,
    dIns=0.3,
    VTan=0.3,
    nSeg=nSeg,
    hHex_a=1,
    hHex_b=0.2,
    Q_flow_nominal=QHex_flow_nominal,
    TTan_nominal=313.15,
    THex_nominal=333.15,
    mHex_flow_nominal=m_flow_nominal,
    show_T=true,
    m_flow_nominal=m_flow_nominal,
    energyDynamicsHex=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Tank with dynamic heat exchanger balance";

  Tank tanDyn "Tank with dynamic heat exchanger balance"

  Tank tanSte(energyDynamicsHex=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Tank with steady-state heat exchanger balance"

  Modelica.Blocks.Sources.Trapezoid mHex_flow_in(
    period=7200,
    amplitude=m_flow_nominal,
    offset=0,
    rising=1800,
    width=1800,
    falling=1800,
    startTime=900) "Control signal for mass flow rate in heat exchanger"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=273.15 + 30,
    p(displayUnit="Pa"),
    nPorts=2) "Sink boundary condition"

  AixLib.Fluid.Sources.MassFlowSource_T mHex_flow2(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=273.15 + 60,
    nPorts=1) "Mass flow rate through the heat exchanger"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTanDyn(
    redeclare package Medium = Medium,
    allowFlowReversal=false,
    m_flow_nominal=m_flow_nominal,
    tau=0) "Temperature sensor at tank outlet"
  AixLib.Fluid.Sensors.TemperatureTwoPort senTanSte(
    redeclare package Medium = Medium,
    allowFlowReversal=false,
    m_flow_nominal=m_flow_nominal,
    tau=0) "Temperature sensor at tank outlet"
  AixLib.Fluid.Sources.MassFlowSource_T mWatTanDyn_flow(
    redeclare package Medium = Medium,
    nPorts=1) "Mass flow rate through the tank"
  AixLib.Fluid.Sources.MassFlowSource_T mWatTanSte_flow(
    redeclare package Medium = Medium,
    nPorts=1) "Mass flow rate through the tank"
equation
  connect(mHex_flow_in.y, mHex_flow1.m_flow_in)
  connect(mHex_flow2.m_flow_in, mHex_flow_in.y)
  connect(mHex_flow2.ports[1], tanSte.portHex_a)
  connect(watInTan.ports[1], tanSte.port_a)
  connect(mHex_flow1.ports[1], tanDyn.portHex_a)
  connect(watInTan.ports[2], tanDyn.port_a)
  connect(senTanDyn.port_a, tanDyn.portHex_b)
  connect(senTanSte.port_a, tanSte.portHex_b)
  connect(senTanDyn.port_b, sin.ports[1])
  connect(senTanSte.port_b, sin.ports[2])
  connect(mWatTanDyn_flow.ports[1], tanDyn.port_b)
  connect(mWatTanSte_flow.ports[1], tanSte.port_b)
end HeatExchangerDynamics;


within AixLib.Fluid.Storage.Validation;
model HeatExchangerLocation
  "Test model for heat exchanger with hHex_a and hHex_b interchanged"
  extends Modelica.Icons.Example;

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

  parameter Modelica.Units.SI.HeatFlowRate QHex_flow_nominal=6000
    "Design heat flow rate of heat exchanger";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=QHex_flow_nominal/
      4200/4;

  AixLib.Fluid.Sources.Boundary_pT watInTan(
    redeclare package Medium = Medium,
    use_T_in=false,
    nPorts=2,
    T=273.15 + 30,
    p(displayUnit="Pa")) "Boundary condition for water in the tank"
  AixLib.Fluid.Sources.MassFlowSource_T mHex_flow1(
    redeclare package Medium = Medium,
    nPorts=2,
    m_flow=m_flow_nominal/100,
    T=273.15 + 60) "Mass flow rate through the heat exchanger"
  model Tank = StratifiedEnhancedInternalHex (
    redeclare final package Medium = Medium,
    redeclare final package MediumHex = Medium,
    final VTan=0.3,
    final kIns=0.034,
    final dIns=0.04,
    final hTan=1.2,
    final m_flow_nominal=m_flow_nominal,
    final mHex_flow_nominal=m_flow_nominal,
    final nSeg=12,
    final hexSegMult=2,
    final Q_flow_nominal=QHex_flow_nominal,
    final TTan_nominal=293.15,
    final THex_nominal=273.15+60,
    final computeFlowResistance=false,
    final energyDynamicsHex=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Tank";

  Tank tan_aTop(
    hHex_a=0.3,
    hHex_b=0.1)
    "Tank with heat exchanger inlet above its outlet"

  Tank tan_bTop(
    hHex_a=0.1,
    hHex_b=0.3)
    "Tank with heat exchanger outlet above its inlet"

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=273.15 + 30,
    p(displayUnit="Pa"),
    nPorts=2) "Sink boundary condition"

  AixLib.Fluid.Sensors.TemperatureTwoPort senTan_aTop(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    tau=0) "Temperature sensor at tank outlet"

  AixLib.Fluid.Sensors.TemperatureTwoPort senTan_bTop(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    tau=0) "Temperature sensor at tank outlet"
  AixLib.Fluid.Sources.MassFlowSource_T mWatTanSte_flow(
    redeclare package Medium = Medium,
    nPorts=1) "Mass flow rate through the tank"
  AixLib.Fluid.Sources.MassFlowSource_T mWatTanDyn_flow(
    redeclare package Medium = Medium,
    nPorts=1) "Mass flow rate through the tank"
equation
  connect(mHex_flow1.ports[1], tan_aTop.portHex_a)
  connect(senTan_aTop.port_a, tan_aTop.portHex_b)
  connect(senTan_aTop.port_b, sin.ports[1])
  connect(senTan_bTop.port_b, sin.ports[2])
  connect(senTan_bTop.port_a, tan_bTop.portHex_b)
  connect(tan_bTop.portHex_a, mHex_flow1.ports[2])
  connect(tan_aTop.port_a, watInTan.ports[1])
  connect(tan_bTop.port_a, watInTan.ports[2])
  connect(mWatTanDyn_flow.ports[1], tan_aTop.port_b)
  connect(tan_bTop.port_b, mWatTanSte_flow.ports[1])
end HeatExchangerLocation;


within AixLib.Fluid.Storage;
package Validation "Collection of models that validate the storage models"
extends Modelica.Icons.ExamplesPackage;
end Validation;


Test model for stratified tank

within AixLib.Fluid.Storage.Validation;
model StratifiedLoadingUnloading "Test model for stratified tank"
  extends Modelica.Icons.Example;

 package Medium = AixLib.Media.Water "Medium model";
 constant Integer nSeg = 7 "Number of segments in tank";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1*1000/3600/4;

  AixLib.Fluid.Sources.Boundary_pT sou_1(
    p=300000 + 5000,
    T=273.15 + 40,
    redeclare package Medium = Medium,
    use_T_in=false,
    nPorts=2)
  AixLib.Fluid.Sources.MassFlowSource_T sin_1(
    redeclare package Medium = Medium,
    T=273.15 + 20,
    m_flow=-0.028,
    use_m_flow_in=true,
    nPorts=1)
  AixLib.Fluid.Storage.StratifiedEnhanced tanEnh(
    redeclare package Medium = Medium,
    hTan=3,
    dIns=0.3,
    VTan=0.1,
    nSeg=nSeg,
    show_T=true,
    m_flow_nominal=m_flow_nominal) "Tank"

  AixLib.Fluid.Sources.MassFlowSource_T sin_2(
    redeclare package Medium = Medium,
    T=273.15 + 20,
    m_flow=-0.028,
    use_m_flow_in=true,
    nPorts=1)
  AixLib.Fluid.Storage.Stratified tan(
    redeclare package Medium = Medium,
    hTan=3,
    dIns=0.3,
    VTan=0.1,
    nSeg=nSeg,
    show_T=true,
    m_flow_nominal=m_flow_nominal) "Tank"

  Modelica.Blocks.Sources.Pulse pulse(
    amplitude=2*m_flow_nominal,
    offset=-m_flow_nominal,
    period=7200)
  AixLib.Fluid.Sensors.EnthalpyFlowRate HIn_flow(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate HOut_flow(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate HInEnh_flow(redeclare package Medium =
        Medium, m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate HOutEnh_flow(redeclare package
      Medium = Medium, m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  Modelica.Blocks.Math.Add add(k2=-1)
  Modelica.Blocks.Continuous.Integrator dHTanEnh
    "Difference in enthalpy (should be zero at steady-state)"
  Modelica.Blocks.Math.Add add1(
                               k2=-1)
  Modelica.Blocks.Continuous.Integrator dHTan
    "Difference in enthalpy (should be zero at steady-state)"
equation

  connect(sou_1.ports[1], HIn_flow.port_a)
  connect(HIn_flow.port_b, tan.port_a)
  connect(tan.port_b, HOut_flow.port_a)
  connect(HOut_flow.port_b, sin_2.ports[1])
  connect(sou_1.ports[2], HInEnh_flow.port_a)
  connect(HInEnh_flow.port_b, tanEnh.port_a)
  connect(tanEnh.port_b, HOutEnh_flow.port_a)
  connect(HOutEnh_flow.port_b, sin_1.ports[1])
  connect(HInEnh_flow.H_flow, add.u1)
  connect(HOutEnh_flow.H_flow, add.u2)
  connect(add.y, dHTanEnh.u)
  connect(HIn_flow.H_flow, add1.u1)
  connect(HOut_flow.H_flow, add1.u2)
  connect(add1.y, dHTan.u)
  connect(pulse.y, sin_1.m_flow_in)
  connect(pulse.y, sin_2.m_flow_in)
end StratifiedLoadingUnloading;


within AixLib.Fluid.Storage.Validation;
model StratifiedNonUniformInitial
  "Test model for stratified tank with non-uniform initial temperature"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Water "Medium model";
  constant Integer nSeg = 7 "Number of segments in tank";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1*1000/3600/4;

  AixLib.Fluid.Sources.Boundary_pT sou_1(
    p=300000 + 5000,
    T=273.15 + 40,
    redeclare package Medium = Medium,
    use_T_in=false,
    nPorts=2)
  AixLib.Fluid.Sources.MassFlowSource_T sin_1(
    redeclare package Medium = Medium,
    T=273.15 + 20,
    m_flow=-m_flow_nominal,
    nPorts=1)
    "Mass flow source"
  AixLib.Fluid.Storage.Stratified heaTan(
    redeclare package Medium = Medium,
    hTan=3,
    dIns=0.3,
    VTan=0.1,
    nSeg=nSeg,
    show_T=true,
    m_flow_nominal=m_flow_nominal,
    TFlu_start={313.15,312.15,311.15,310.15,309.15,308.15,307.15})
    "Tank that will be heated up"
  AixLib.Fluid.Sources.MassFlowSource_T sin_2(
    redeclare package Medium = Medium,
    T=273.15 + 20,
    m_flow=m_flow_nominal,
    nPorts=1)
    "Mass flow source"
  AixLib.Fluid.Storage.Stratified cooTan(
    redeclare package Medium = Medium,
    hTan=3,
    dIns=0.3,
    VTan=0.1,
    nSeg=nSeg,
    show_T=true,
    m_flow_nominal=m_flow_nominal,
    TFlu_start={313.15,312.15,311.15,310.15,309.15,308.15,307.15})
    "Tank that will be cooled down"

  AixLib.Fluid.Sensors.EnthalpyFlowRate HIn_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate HOut_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate HInEnh_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  AixLib.Fluid.Sensors.EnthalpyFlowRate HOutEnh_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal) "Enthalpy flow rate"
  Modelica.Blocks.Math.Add add(k2=-1) "Adder for enthalpy difference"
  Modelica.Blocks.Continuous.Integrator dHTanEnh
    "Difference in enthalpy (should be zero at steady-state)"
  Modelica.Blocks.Math.Add add1(k2=-1) "Adder for enthalpy difference"
  Modelica.Blocks.Continuous.Integrator dHTan
    "Difference in enthalpy (should be zero at steady-state)"

equation
  connect(HIn_flow.port_b, cooTan.port_a)
  connect(cooTan.port_b, HOut_flow.port_a)
  connect(HOut_flow.port_b, sin_2.ports[1])
  connect(HOutEnh_flow.port_b, sin_1.ports[1])
  connect(HInEnh_flow.H_flow, add.u1)
  connect(HOutEnh_flow.H_flow, add.u2)
  connect(add.y, dHTanEnh.u)
  connect(HIn_flow.H_flow, add1.u1)
  connect(HOut_flow.H_flow, add1.u2)
  connect(add1.y, dHTan.u)
  connect(HInEnh_flow.port_b, heaTan.port_a)
  connect(heaTan.port_b, HOutEnh_flow.port_a)
  connect(sou_1.ports[1], HInEnh_flow.port_a)
  connect(sou_1.ports[2], HIn_flow.port_a)
end StratifiedNonUniformInitial;


within AixLib.Media;
package Air
  "Package with moist air model that decouples pressure and temperature"
  extends Modelica.Media.Interfaces.PartialCondensingGases(
     mediumName="Air",
     final substanceNames={"water", "air"},
     final reducedX=true,
     final singleState = false,
     reference_X={0.01,0.99},
     final fluidConstants = {Modelica.Media.IdealGases.Common.FluidData.H2O,
                             Modelica.Media.IdealGases.Common.FluidData.N2},
     reference_T=273.15,
     reference_p=101325,
     AbsolutePressure(start=p_default),
     Temperature(start=T_default));
  extends Modelica.Icons.Package;

  constant Integer Water=1
    "Index of water (in substanceNames, massFractions X, etc.)";
  constant Integer Air=2
    "Index of air (in substanceNames, massFractions X, etc.)";

  // In the assignments below, we compute cv as OpenModelica
  // cannot evaluate cv=cp-R as defined in GasProperties.
  constant GasProperties dryair(
    R=Modelica.Media.IdealGases.Common.SingleGasesData.Air.R_s,
    MM=Modelica.Media.IdealGases.Common.SingleGasesData.Air.MM,
    cp=AixLib.Utilities.Psychrometrics.Constants.cpAir,
    cv=AixLib.Utilities.Psychrometrics.Constants.cpAir - Modelica.Media.IdealGases.Common.SingleGasesData.Air.R_s)
    "Dry air properties";
  constant GasProperties steam(
    R=Modelica.Media.IdealGases.Common.SingleGasesData.H2O.R_s,
    MM=Modelica.Media.IdealGases.Common.SingleGasesData.H2O.MM,
    cp=AixLib.Utilities.Psychrometrics.Constants.cpSte,
    cv=AixLib.Utilities.Psychrometrics.Constants.cpSte - Modelica.Media.IdealGases.Common.SingleGasesData.H2O.R_s)
    "Steam properties";

  constant Real k_mair =  steam.MM/dryair.MM "Ratio of molar weights";

  constant Modelica.Units.SI.MolarMass[2] MMX={steam.MM,dryair.MM}
    "Molar masses of components";

  constant AbsolutePressure pStp = reference_p
    "Pressure for which fluid density is defined";
  constant Density dStp = 1.2 "Fluid density at pressure pStp";

  // Redeclare ThermodynamicState to avoid the warning
  // "Base class ThermodynamicState is replaceable"
  // during model check
  redeclare record extends ThermodynamicState
    "ThermodynamicState record for moist air"
  end ThermodynamicState;
  // There must not be any stateSelect=StateSelect.prefer for
  // the pressure.
  // Otherwise, translateModel("AixLib.Fluid.FMI.ExportContainers.Examples.FMUs.ResistanceVolume")
  // will fail as Dymola does an index reduction and outputs
  //   Differentiated the equation
  //   vol.dynBal.medium.p+res.dp-inlet.p = 0.0;
  //   giving
  //   der(vol.dynBal.medium.p)+der(res.dp) = der(inlet.p);
  //
  //   The model requires derivatives of some inputs as listed below:
  //   1 inlet.m_flow
  //   1 inlet.p
  // Therefore, the statement
  //   p(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default)
  // has been removed.
  redeclare replaceable model BaseProperties "Base properties (p, d, T, h, u, R, MM and X and Xi) of a medium"

  parameter Boolean preferredMediumStates=false
    "= true if StateSelect.prefer shall be used for the independent property variables of the medium"
  final parameter Boolean standardOrderComponents=true
    "If true, and reducedX = true, the last element of X will be computed from the other ones";

  InputAbsolutePressure p "Absolute pressure of medium";
  InputMassFraction[1] Xi(
    start=reference_X[1:1],
    nominal={0.01},
    each stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default)
    "Structurally independent mass fractions";
  InputSpecificEnthalpy h "Specific enthalpy of medium";
  Modelica.Media.Interfaces.Types.Density d "Density of medium";
  Modelica.Media.Interfaces.Types.Temperature T(
   stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default)
   "Temperature of medium";
  Modelica.Media.Interfaces.Types.MassFraction[2] X(
    start=reference_X,
    nominal={0.01, 1})
    "Mass fractions (= (component mass)/total mass  m_i/m)";
  Modelica.Media.Interfaces.Types.SpecificInternalEnergy u
    "Specific internal energy of medium";
  Modelica.Media.Interfaces.Types.SpecificHeatCapacity R_s
    "Gas constant (of mixture if applicable)";
  Modelica.Media.Interfaces.Types.MolarMass MM
    "Molar mass (of mixture or single fluid)";
  ThermodynamicState state(
    X(nominal={0.01, 1}))
    "Thermodynamic state record for optional functions";

    Modelica.Units.NonSI.Temperature_degC T_degC=
        Modelica.Units.Conversions.to_degC(T) "Temperature of medium in [degC]";
    Modelica.Units.NonSI.Pressure_bar p_bar=Modelica.Units.Conversions.to_bar(p)
      "Absolute pressure of medium in [bar]";

  // Local connector definition, used for equation balancing check
  connector InputAbsolutePressure = input Modelica.Units.SI.AbsolutePressure
    "Pressure as input signal connector";
  connector InputSpecificEnthalpy = input Modelica.Units.SI.SpecificEnthalpy
    "Specific enthalpy as input signal connector";
  connector InputMassFraction = input Modelica.Units.SI.MassFraction
    "Mass fraction as input signal connector";

    // Declarations for Air only
  protected
    Modelica.Units.SI.TemperatureDifference dT(start=T_default - reference_T)
      "Temperature difference used to compute enthalpy";

  equation
    MM = 1/(X[1]/steam.MM+(X[2])/dryair.MM);

    dT = T - reference_T;
    h = dT*dryair.cp * X[2] +
       (dT * steam.cp + h_fg) * X[1];
    R_s = dryair.R*X[2] + steam.R*X[1];

    // Equation for ideal gas, from h=u+p*v and R*T=p*v, from which follows that  u = h-R*T.
    // u = h-R*T;
    // However, in this medium, the gas law is d/dStp=p/pStp, from which follows using h=u+pv that
    // u= h-p*v = h-p/d = h-pStp/dStp
    u = h-pStp/dStp;

    // In this medium model, the density depends only
    // on temperature, but not on pressure.
    //  d = p/(R*T);
    d/dStp = p/pStp;

    state.p = p;
    state.T = T;
    state.X = X;

    X[1] = Xi[1];
    X[2] = 1 - X[1];

    // Assertions to test for bounds
    assert(noEvent(X[1] >= -1.e-5) and noEvent(X[1] <= 1 + 1.e-5), "Mass fraction X[1] = " + String(X[1]) + " of substance water"
      + "\nof medium \"AixLib.Media.Air\" is not in the range 0..1");

    assert(noEvent(T >= 200.0), "In "   + getInstanceName() + ": Temperature T exceeded its minimum allowed value of -73.15 degC (200 Kelvin)
as required from medium model \"AixLib.Media.Air\".");
    assert(noEvent(T <= 423.15), "In "   + getInstanceName() + ": Temperature T exceeded its maximum allowed value of 150 degC (423.15 Kelvin)
as required from medium model \"AixLib.Media.Air\".");

  assert(noEvent(p >= 0.0), "Pressure (= " + String(p) + " Pa) of medium \"AixLib.Media.Air\" is negative\n(Temperature = " + String(T) + " K)");
  end BaseProperties;

redeclare function density "Gas density"
  extends Modelica.Icons.Function;
  input ThermodynamicState state;
  output Density d "Density";
algorithm
  d :=state.p*dStp/pStp;
end density;

redeclare function extends dynamicViscosity
    "Return the dynamic viscosity of dry air"
algorithm
  eta := 4.89493640395e-08 * state.T + 3.88335940547e-06;
end dynamicViscosity;

redeclare function enthalpyOfCondensingGas
    "Enthalpy of steam per unit mass of steam"
  extends Modelica.Icons.Function;

  input Temperature T "temperature";
  output SpecificEnthalpy h "steam enthalpy";
algorithm
  h := (T-reference_T) * steam.cp + h_fg;
end enthalpyOfCondensingGas;

redeclare replaceable function extends enthalpyOfGas
    "Enthalpy of gas mixture per unit mass of gas mixture"
algorithm
  h := enthalpyOfCondensingGas(T)*X[Water]
       + enthalpyOfDryAir(T)*(1.0-X[Water]);
end enthalpyOfGas;

redeclare replaceable function extends enthalpyOfLiquid
    "Enthalpy of liquid (per unit mass of liquid) which is linear in the temperature"
algorithm
  h := (T - reference_T)*cpWatLiq;
end enthalpyOfLiquid;

redeclare function enthalpyOfNonCondensingGas
    "Enthalpy of non-condensing gas per unit mass of steam"
  extends Modelica.Icons.Function;

  input Temperature T "temperature";
  output SpecificEnthalpy h "enthalpy";
algorithm
  h := enthalpyOfDryAir(T);
end enthalpyOfNonCondensingGas;

redeclare function extends enthalpyOfVaporization
    "Enthalpy of vaporization of water"
algorithm
  r0 := h_fg;
end enthalpyOfVaporization;

redeclare function extends gasConstant
    "Return ideal gas constant as a function from thermodynamic state, only valid for phi<1"

algorithm
    R_s := dryair.R*(1 - state.X[Water]) + steam.R*state.X[Water];
end gasConstant;

redeclare function extends pressure
    "Returns pressure of ideal gas as a function of the thermodynamic state record"

algorithm
  p := state.p;
end pressure;

redeclare function extends isobaricExpansionCoefficient
    "Isobaric expansion coefficient beta"
algorithm
  beta := 0;
end isobaricExpansionCoefficient;

redeclare function extends isothermalCompressibility
    "Isothermal compressibility factor"
algorithm
  kappa := -1/state.p;
end isothermalCompressibility;

redeclare function extends saturationPressure
    "Saturation curve valid for 223.16 <= T <= 373.16 (and slightly outside with less accuracy)"

algorithm
  psat := AixLib.Utilities.Psychrometrics.Functions.saturationPressure(Tsat);
end saturationPressure;

redeclare function extends specificEntropy
    "Return the specific entropy, only valid for phi<1"

  protected
    Modelica.Units.SI.MoleFraction[2] Y "Molar fraction";
algorithm
    Y := massToMoleFractions(
         state.X, {steam.MM,dryair.MM});
    s := specificHeatCapacityCp(state) * Modelica.Math.log(state.T/reference_T)
         - Modelica.Constants.R *
         sum(state.X[i]/MMX[i]*
             Modelica.Math.log(max(Y[i], Modelica.Constants.eps)*state.p/reference_p) for i in 1:2);
end specificEntropy;

redeclare function extends density_derp_T
    "Return the partial derivative of density with respect to pressure at constant temperature"
algorithm
  ddpT := dStp/pStp;
end density_derp_T;

redeclare function extends density_derT_p
    "Return the partial derivative of density with respect to temperature at constant pressure"
algorithm
  ddTp := 0;
end density_derT_p;

redeclare function extends density_derX
    "Return the partial derivative of density with respect to mass fractions at constant pressure and temperature"
algorithm
  dddX := fill(0, nX);
end density_derX;

redeclare replaceable function extends specificHeatCapacityCp
    "Specific heat capacity of gas mixture at constant pressure"
algorithm
  cp := dryair.cp*(1-state.X[Water]) +steam.cp*state.X[Water];
end specificHeatCapacityCp;

redeclare replaceable function extends specificHeatCapacityCv
    "Specific heat capacity of gas mixture at constant volume"
algorithm
  cv:= dryair.cv*(1-state.X[Water]) +steam.cv*state.X[Water];
end specificHeatCapacityCv;

redeclare function setState_dTX
    "Return thermodynamic state as function of density d, temperature T and composition X"
  extends Modelica.Icons.Function;
  input Density d "Density";
  input Temperature T "Temperature";
  input MassFraction X[:]=reference_X "Mass fractions";
  output ThermodynamicState state "Thermodynamic state";

algorithm
    // Note that d/dStp = p/pStp, hence p = d*pStp/dStp
    state := if size(X, 1) == nX then
               ThermodynamicState(p=d*pStp/dStp, T=T, X=X)
             else
               ThermodynamicState(p=d*pStp/dStp,
                                  T=T,
                                  X=cat(1, X, {1 - sum(X)}));
end setState_dTX;

redeclare function extends setState_phX
    "Return thermodynamic state as function of pressure p, specific enthalpy h and composition X"
algorithm
  state := if size(X, 1) == nX then
    ThermodynamicState(p=p, T=temperature_phX(p, h, X), X=X)
 else
    ThermodynamicState(p=p, T=temperature_phX(p, h, X), X=cat(1, X, {1 - sum(X)}));
end setState_phX;

redeclare function extends setState_pTX
    "Return thermodynamic state as function of p, T and composition X or Xi"
algorithm
    state := if size(X, 1) == nX then
                ThermodynamicState(p=p, T=T, X=X)
             else
                ThermodynamicState(p=p, T=T, X=cat(1, X, {1 - sum(X)}));
end setState_pTX;

redeclare function extends setState_psX
    "Return the thermodynamic state as function of p, s and composition X or Xi"
  protected
    Modelica.Units.SI.MassFraction[2] X_int "Mass fraction";
    Modelica.Units.SI.MoleFraction[2] Y "Molar fraction";
    Modelica.Units.SI.Temperature T "Temperature";
algorithm
    if size(X, 1) == nX then
      X_int:=X;
    else
      X_int :=cat(
        1,
        X,
        {1 - sum(X)});
    end if;
   Y := massToMoleFractions(
         X_int, {steam.MM,dryair.MM});
    // The next line is obtained from symbolic solving the
    // specificEntropy function for T.
    // In this formulation, we can set T to any value when calling
    // specificHeatCapacityCp as cp does not depend on T.
    T := 273.15 * Modelica.Math.exp((s + Modelica.Constants.R *
           sum(X_int[i]/MMX[i]*
             Modelica.Math.log(max(Y[i], Modelica.Constants.eps)) for i in 1:2))
             / specificHeatCapacityCp(setState_pTX(p=p,
                                                   T=273.15,
                                                   X=X_int)));

    state := ThermodynamicState(p=p,
                                T=T,
                                X=X_int);
end setState_psX;

redeclare replaceable function extends specificEnthalpy
    "Compute specific enthalpy from pressure, temperature and mass fraction"
algorithm
  h := (state.T - reference_T)*dryair.cp * (1 - state.X[Water]) +
       ((state.T-reference_T) * steam.cp + h_fg) * state.X[Water];
end specificEnthalpy;

redeclare replaceable function specificEnthalpy_pTX "Specific enthalpy"
  extends Modelica.Icons.Function;
    input Modelica.Units.SI.Pressure p "Pressure";
    input Modelica.Units.SI.Temperature T "Temperature";
    input Modelica.Units.SI.MassFraction X[:] "Mass fractions of moist air";
    output Modelica.Units.SI.SpecificEnthalpy h "Specific enthalpy at p, T, X";

algorithm
  h := specificEnthalpy(setState_pTX(p, T, X));
end specificEnthalpy_pTX;

redeclare replaceable function extends specificGibbsEnergy
    "Specific Gibbs energy"
algorithm
  g := specificEnthalpy(state) - state.T*specificEntropy(state);
end specificGibbsEnergy;

redeclare replaceable function extends specificHelmholtzEnergy
    "Specific Helmholtz energy"
algorithm
  f := specificEnthalpy(state) - gasConstant(state)*state.T - state.T*specificEntropy(state);
end specificHelmholtzEnergy;

redeclare function extends isentropicEnthalpy "Return the isentropic enthalpy"
algorithm
  h_is := specificEnthalpy(setState_psX(
            p=p_downstream,
            s=specificEntropy(refState),
            X=refState.X));
end isentropicEnthalpy;

redeclare function extends specificInternalEnergy "Specific internal energy"
  extends Modelica.Icons.Function;
algorithm
  u := specificEnthalpy(state) - pStp/dStp;
end specificInternalEnergy;

redeclare function extends temperature
    "Return temperature of ideal gas as a function of the thermodynamic state record"
algorithm
  T := state.T;
end temperature;

redeclare function extends molarMass "Return the molar mass"
algorithm
    MM := 1/(state.X[Water]/MMX[Water]+(1.0-state.X[Water])/MMX[Air]);
end molarMass;

redeclare replaceable function temperature_phX
    "Compute temperature from specific enthalpy and mass fraction"
    extends Modelica.Icons.Function;
  input AbsolutePressure p "Pressure";
  input SpecificEnthalpy h "specific enthalpy";
  input MassFraction[:] X "mass fractions of composition";
  output Temperature T "temperature";
algorithm
  T := reference_T + (h - h_fg * X[Water])
       /((1 - X[Water])*dryair.cp + X[Water] * steam.cp);
end temperature_phX;

redeclare function extends thermalConductivity
    "Thermal conductivity of dry air as a polynomial in the temperature"
algorithm
  lambda :=Modelica.Math.Polynomials.evaluate({(-4.8737307422969E-008),
      7.67803133753502E-005,0.0241814385504202},
      Modelica.Units.Conversions.to_degC(state.T));
end thermalConductivity;
//////////////////////////////////////////////////////////////////////
// Protected classes.
// These classes are only of use within this medium model.
// Models generally have no need to access them.
// Therefore, they are made protected. This also allows to redeclare the
// medium model with another medium model that does not provide an
// implementation of these classes.
protected
  record GasProperties
    "Coefficient data record for properties of perfect gases"
    extends Modelica.Icons.Record;

    Modelica.Units.SI.MolarMass MM "Molar mass";
    Modelica.Units.SI.SpecificHeatCapacity R "Gas constant";
    Modelica.Units.SI.SpecificHeatCapacity cp
      "Specific heat capacity at constant pressure";
    Modelica.Units.SI.SpecificHeatCapacity cv=cp - R
      "Specific heat capacity at constant volume";
  end GasProperties;
  constant Modelica.Units.SI.SpecificEnergy h_fg=AixLib.Utilities.Psychrometrics.Constants.h_fg
    "Latent heat of evaporation of water";
  constant Modelica.Units.SI.SpecificHeatCapacity cpWatLiq=AixLib.Utilities.Psychrometrics.Constants.cpWatLiq
    "Specific heat capacity of liquid water";

replaceable function der_enthalpyOfLiquid
    "Temperature derivative of enthalpy of liquid per unit mass of liquid"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of liquid enthalpy";
algorithm
  der_h := cpWatLiq*der_T;
end der_enthalpyOfLiquid;

function der_enthalpyOfCondensingGas
    "Derivative of enthalpy of steam per unit mass of steam"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of steam enthalpy";
algorithm
  der_h := steam.cp*der_T;
end der_enthalpyOfCondensingGas;

replaceable function enthalpyOfDryAir
    "Enthalpy of dry air per unit mass of dry air"
  extends Modelica.Icons.Function;

  input Temperature T "Temperature";
  output SpecificEnthalpy h "Dry air enthalpy";
algorithm
  h := (T - reference_T)*dryair.cp;
end enthalpyOfDryAir;

replaceable function der_enthalpyOfDryAir
    "Derivative of enthalpy of dry air per unit mass of dry air"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of dry air enthalpy";
algorithm
  der_h := dryair.cp*der_T;
end der_enthalpyOfDryAir;

replaceable function der_enthalpyOfNonCondensingGas
    "Derivative of enthalpy of non-condensing gas per unit mass of steam"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of steam enthalpy";
algorithm
  der_h := der_enthalpyOfDryAir(T, der_T);
end der_enthalpyOfNonCondensingGas;

replaceable function der_specificHeatCapacityCp
    "Derivative of specific heat capacity of gas mixture at constant pressure"
  extends Modelica.Icons.Function;
    input ThermodynamicState state "Thermodynamic state";
    input ThermodynamicState der_state "Derivative of thermodynamic state";
    output Real der_cp(unit="J/(kg.K.s)")
      "Derivative of specific heat capacity";
algorithm
  der_cp := (steam.cp-dryair.cp)*der_state.X[Water];
end der_specificHeatCapacityCp;

replaceable function der_specificHeatCapacityCv
    "Derivative of specific heat capacity of gas mixture at constant volume"
  extends Modelica.Icons.Function;
    input ThermodynamicState state "Thermodynamic state";
    input ThermodynamicState der_state "Derivative of thermodynamic state";
    output Real der_cv(unit="J/(kg.K.s)")
      "Derivative of specific heat capacity";
algorithm
  der_cv := (steam.cv-dryair.cv)*der_state.X[Water];
end der_specificHeatCapacityCv;
end Air;


within AixLib.Media;
package AirIncompressible
  "Package with incopressible moist air model for air duct simulations"
  extends Modelica.Media.Interfaces.PartialCondensingGases(
     mediumName="Air",
     final substanceNames={"water", "air"},
     final reducedX=true,
     final singleState = false,
     reference_X={0.01,0.99},
     final fluidConstants = {Modelica.Media.IdealGases.Common.FluidData.H2O,
                             Modelica.Media.IdealGases.Common.FluidData.N2},
     reference_T=273.15,
     reference_p=101325,
     AbsolutePressure(start=p_default),
     Temperature(start=T_default));
  extends Modelica.Icons.Package;

  constant Integer Water=1
    "Index of water (in substanceNames, massFractions X, etc.)";
  constant Integer Air=2
    "Index of air (in substanceNames, massFractions X, etc.)";

  constant AbsolutePressure pStp = reference_p
    "Pressure for which fluid density is defined";
  constant Density dStp = 1.2 "Fluid density at pressure pStp";

  // Redeclare ThermodynamicState to avoid the warning
  // "Base class ThermodynamicState is replaceable"
  // during model check
  redeclare record extends ThermodynamicState
    "ThermodynamicState record for moist air"
  end ThermodynamicState;
  // There must not be any stateSelect=StateSelect.prefer for
  // the pressure.
  // Otherwise, translateModel("Buildings.Fluid.FMI.ExportContainers.Examples.FMUs.ResistanceVolume")
  // will fail as Dymola does an index reduction and outputs
  //   Differentiated the equation
  //   vol.dynBal.medium.p+res.dp-inlet.p = 0.0;
  //   giving
  //   der(vol.dynBal.medium.p)+der(res.dp) = der(inlet.p);
  //
  //   The model requires derivatives of some inputs as listed below:
  //   1 inlet.m_flow
  //   1 inlet.p
  // Therefore, the statement
  //   p(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default)
  // has been removed.
  redeclare replaceable model extends BaseProperties(
    Xi(each stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
    T(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
    final standardOrderComponents=true) "Base properties"

  protected
    constant Modelica.Units.SI.MolarMass[2] MMX={steam.MM,dryair.MM}
      "Molar masses of components";

    MassFraction X_steam "Mass fraction of steam water";
    MassFraction X_air "Mass fraction of air";
    Modelica.Units.SI.TemperatureDifference dT(start=T_default - reference_T)
      "Temperature difference used to compute enthalpy";
  equation
    assert(T >= 200.0, "
In "   + getInstanceName() + ": Temperature T exceeded its minimum allowed value of -73.15 degC (200 Kelvin)
as required from medium model \"" + mediumName + "\".");
    assert(T <= 423.15, "
In "   + getInstanceName() + ": Temperature T exceeded its maximum allowed value of 150 degC (423.15 Kelvin)
as required from medium model \"" + mediumName + "\".");

    MM = 1/(Xi[Water]/MMX[Water]+(1.0-Xi[Water])/MMX[Air]);

    X_steam  = Xi[Water]; // There is no liquid in this medium model
    X_air    = 1-Xi[Water];

    dT = T - reference_T;
    h = dT*dryair.cp * X_air +
       (dT * steam.cp + h_fg) * X_steam;
    R_s = dryair.R*X_air + steam.R*X_steam;

    // Equation for ideal gas, from h=u+p*v and R*T=p*v, from which follows that  u = h-R*T.
    // u = h-R*T;
    // However, in this medium, the gas law is d/dStp=p/pStp, from which follows using h=u+pv that
    // u= h-p*v = h-p/d = h-pStp/dStp
    u = h-pStp/dStp;

    // In this medium model, the density depends only
    // on temperature, but not on pressure.
    //  d = p/(R*T);
    d/dStp = p/pStp;

    state.p = p;
    state.T = T;
    state.X = X;
  end BaseProperties;

redeclare function density "Gas density"
  extends Modelica.Icons.Function;
  input ThermodynamicState state;
  output Density d "Density";
algorithm
  d :=dStp;
end density;

redeclare function extends dynamicViscosity
    "Return the dynamic viscosity of dry air"
algorithm
  eta := 4.89493640395e-08 * state.T + 3.88335940547e-06;
end dynamicViscosity;

redeclare function enthalpyOfCondensingGas
    "Enthalpy of steam per unit mass of steam"
  extends Modelica.Icons.Function;

  input Temperature T "temperature";
  output SpecificEnthalpy h "steam enthalpy";
algorithm
  h := (T-reference_T) * steam.cp + h_fg;
end enthalpyOfCondensingGas;

redeclare replaceable function extends enthalpyOfGas
    "Enthalpy of gas mixture per unit mass of gas mixture"
algorithm
  h := enthalpyOfCondensingGas(T)*X[Water]
       + enthalpyOfDryAir(T)*(1.0-X[Water]);
end enthalpyOfGas;

redeclare replaceable function extends enthalpyOfLiquid
    "Enthalpy of liquid (per unit mass of liquid) which is linear in the temperature"
algorithm
  h := (T - reference_T)*cpWatLiq;
end enthalpyOfLiquid;

redeclare function enthalpyOfNonCondensingGas
    "Enthalpy of non-condensing gas per unit mass of steam"
  extends Modelica.Icons.Function;

  input Temperature T "temperature";
  output SpecificEnthalpy h "enthalpy";
algorithm
  h := enthalpyOfDryAir(T);
end enthalpyOfNonCondensingGas;

redeclare function extends enthalpyOfVaporization
    "Enthalpy of vaporization of water"
algorithm
  r0 := h_fg;
end enthalpyOfVaporization;

redeclare function extends gasConstant
    "Return ideal gas constant as a function from thermodynamic state, only valid for phi<1"

algorithm
    R_s := dryair.R*(1 - state.X[Water]) + steam.R*state.X[Water];
end gasConstant;

redeclare function extends pressure
    "Returns pressure of ideal gas as a function of the thermodynamic state record"

algorithm
  p := state.p;
end pressure;

redeclare function extends isobaricExpansionCoefficient
    "Isobaric expansion coefficient beta"
algorithm
  beta := 0;
end isobaricExpansionCoefficient;

redeclare function extends isothermalCompressibility
    "Isothermal compressibility factor"
algorithm
  kappa := -1/state.p;
end isothermalCompressibility;

redeclare function extends saturationPressure
    "Saturation curve valid for 223.16 <= T <= 373.16 (and slightly outside with less accuracy)"

algorithm
  psat := AixLib.Utilities.Psychrometrics.Functions.saturationPressure(Tsat);
end saturationPressure;

redeclare function extends specificEntropy
    "Return the specific entropy, only valid for phi<1"

  protected
    Modelica.Units.SI.MoleFraction[2] Y "Molar fraction";
algorithm
    Y := massToMoleFractions(
         state.X, {steam.MM,dryair.MM});
    s := specificHeatCapacityCp(state) * Modelica.Math.log(state.T/reference_T)
         - Modelica.Constants.R *
         sum(state.X[i]/MMX[i]*
             Modelica.Math.log(max(Y[i], Modelica.Constants.eps)*state.p/reference_p) for i in 1:2);
end specificEntropy;

redeclare function extends density_derp_T
    "Return the partial derivative of density with respect to pressure at constant temperature"
algorithm
  ddpT := 0;
end density_derp_T;

redeclare function extends density_derT_p
    "Return the partial derivative of density with respect to temperature at constant pressure"
algorithm
  ddTp := 0;
end density_derT_p;

redeclare function extends density_derX
    "Return the partial derivative of density with respect to mass fractions at constant pressure and temperature"
algorithm
  dddX := fill(0, nX);
end density_derX;

redeclare replaceable function extends specificHeatCapacityCp
    "Specific heat capacity of gas mixture at constant pressure"
algorithm
  cp := dryair.cp*(1-state.X[Water]) +steam.cp*state.X[Water];
end specificHeatCapacityCp;

redeclare replaceable function extends specificHeatCapacityCv
    "Specific heat capacity of gas mixture at constant volume"
algorithm
  cv:= dryair.cv*(1-state.X[Water]) +steam.cv*state.X[Water];
end specificHeatCapacityCv;

redeclare function setState_dTX
    "Return thermodynamic state as function of density d, temperature T and composition X"
  extends Modelica.Icons.Function;
  input Density d "Density";
  input Temperature T "Temperature";
  input MassFraction X[:]=reference_X "Mass fractions";
  output ThermodynamicState state "Thermodynamic state";

algorithm
    // Note that d/dStp = p/pStp, hence p = d*pStp/dStp
    state := if size(X, 1) == nX then
               ThermodynamicState(p=d*pStp/dStp, T=T, X=X)
             else
               ThermodynamicState(p=d*pStp/dStp,
                                  T=T,
                                  X=cat(1, X, {1 - sum(X)}));
end setState_dTX;

redeclare function extends setState_phX
    "Return thermodynamic state as function of pressure p, specific enthalpy h and composition X"
algorithm
  state := if size(X, 1) == nX then
    ThermodynamicState(p=p, T=temperature_phX(p, h, X), X=X)
 else
    ThermodynamicState(p=p, T=temperature_phX(p, h, X), X=cat(1, X, {1 - sum(X)}));
end setState_phX;

redeclare function extends setState_pTX
    "Return thermodynamic state as function of p, T and composition X or Xi"
algorithm
    state := if size(X, 1) == nX then
                ThermodynamicState(p=p, T=T, X=X)
             else
                ThermodynamicState(p=p, T=T, X=cat(1, X, {1 - sum(X)}));
end setState_pTX;

redeclare function extends setState_psX
    "Return the thermodynamic state as function of p, s and composition X or Xi"
  protected
    Modelica.Units.SI.MassFraction[2] X_int "Mass fraction";
    Modelica.Units.SI.MoleFraction[2] Y "Molar fraction";
    Modelica.Units.SI.Temperature T "Temperature";
algorithm
    if size(X, 1) == nX then
      X_int:=X;
    else
      X_int :=cat(
        1,
        X,
        {1 - sum(X)});
    end if;
   Y := massToMoleFractions(
         X_int, {steam.MM,dryair.MM});
    // The next line is obtained from symbolic solving the
    // specificEntropy function for T.
    // In this formulation, we can set T to any value when calling
    // specificHeatCapacityCp as cp does not depend on T.
    T := 273.15 * Modelica.Math.exp((s + Modelica.Constants.R *
           sum(X_int[i]/MMX[i]*
             Modelica.Math.log(max(Y[i], Modelica.Constants.eps)) for i in 1:2))
             / specificHeatCapacityCp(setState_pTX(p=p,
                                                   T=273.15,
                                                   X=X_int)));

    state := ThermodynamicState(p=p,
                                T=T,
                                X=X_int);
end setState_psX;

redeclare replaceable function extends specificEnthalpy
    "Compute specific enthalpy from pressure, temperature and mass fraction"
algorithm
  h := (state.T - reference_T)*dryair.cp * (1 - state.X[Water]) +
       ((state.T-reference_T) * steam.cp + h_fg) * state.X[Water];
end specificEnthalpy;

redeclare replaceable function specificEnthalpy_pTX "Specific enthalpy"
  extends Modelica.Icons.Function;
    input Modelica.Units.SI.Pressure p "Pressure";
    input Modelica.Units.SI.Temperature T "Temperature";
    input Modelica.Units.SI.MassFraction X[:] "Mass fractions of moist air";
    output Modelica.Units.SI.SpecificEnthalpy h "Specific enthalpy at p, T, X";

algorithm
  h := specificEnthalpy(setState_pTX(p, T, X));
end specificEnthalpy_pTX;

redeclare replaceable function extends specificGibbsEnergy
    "Specific Gibbs energy"
algorithm
  g := specificEnthalpy(state) - state.T*specificEntropy(state);
end specificGibbsEnergy;

redeclare replaceable function extends specificHelmholtzEnergy
    "Specific Helmholtz energy"
algorithm
  f := specificEnthalpy(state) - gasConstant(state)*state.T - state.T*specificEntropy(state);
end specificHelmholtzEnergy;

redeclare function extends isentropicEnthalpy "Return the isentropic enthalpy"
algorithm
  h_is := specificEnthalpy(setState_psX(
            p=p_downstream,
            s=specificEntropy(refState),
            X=refState.X));
end isentropicEnthalpy;

redeclare function extends specificInternalEnergy "Specific internal energy"
  extends Modelica.Icons.Function;
algorithm
  u := specificEnthalpy(state) - pStp/dStp;
end specificInternalEnergy;

redeclare function extends temperature
    "Return temperature of ideal gas as a function of the thermodynamic state record"
algorithm
  T := state.T;
end temperature;

redeclare function extends molarMass "Return the molar mass"
algorithm
    MM := 1/(state.X[Water]/MMX[Water]+(1.0-state.X[Water])/MMX[Air]);
end molarMass;

redeclare replaceable function temperature_phX
    "Compute temperature from specific enthalpy and mass fraction"
    extends Modelica.Icons.Function;
  input AbsolutePressure p "Pressure";
  input SpecificEnthalpy h "specific enthalpy";
  input MassFraction[:] X "mass fractions of composition";
  output Temperature T "temperature";
algorithm
  T := reference_T + (h - h_fg * X[Water])
       /((1 - X[Water])*dryair.cp + X[Water] * steam.cp);
end temperature_phX;

redeclare function extends thermalConductivity
    "Thermal conductivity of dry air as a polynomial in the temperature"
algorithm
  lambda :=Modelica.Math.Polynomials.evaluate({(-4.8737307422969E-008),
      7.67803133753502E-005,0.0241814385504202},
      Modelica.Units.Conversions.to_degC(state.T));
end thermalConductivity;
//////////////////////////////////////////////////////////////////////
// Protected classes.
// These classes are only of use within this medium model.
// Models generally have no need to access them.
// Therefore, they are made protected. This also allows to redeclare the
// medium model with another medium model that does not provide an
// implementation of these classes.
protected
  record GasProperties
    "Coefficient data record for properties of perfect gases"
    extends Modelica.Icons.Record;

    Modelica.Units.SI.MolarMass MM "Molar mass";
    Modelica.Units.SI.SpecificHeatCapacity R "Gas constant";
    Modelica.Units.SI.SpecificHeatCapacity cp
      "Specific heat capacity at constant pressure";
    Modelica.Units.SI.SpecificHeatCapacity cv=cp - R
      "Specific heat capacity at constant volume";
  end GasProperties;
  // In the assignments below, we compute cv as OpenModelica
  // cannot evaluate cv=cp-R as defined in GasProperties.
  constant GasProperties dryair(
    R=Modelica.Media.IdealGases.Common.SingleGasesData.Air.R_s,
    MM=Modelica.Media.IdealGases.Common.SingleGasesData.Air.MM,
    cp=AixLib.Utilities.Psychrometrics.Constants.cpAir,
    cv=AixLib.Utilities.Psychrometrics.Constants.cpAir - Modelica.Media.IdealGases.Common.SingleGasesData.Air.R_s)
    "Dry air properties";
  constant GasProperties steam(
    R=Modelica.Media.IdealGases.Common.SingleGasesData.H2O.R_s,
    MM=Modelica.Media.IdealGases.Common.SingleGasesData.H2O.MM,
    cp=AixLib.Utilities.Psychrometrics.Constants.cpSte,
    cv=AixLib.Utilities.Psychrometrics.Constants.cpSte - Modelica.Media.IdealGases.Common.SingleGasesData.H2O.R_s)
    "Steam properties";

  constant Real k_mair =  steam.MM/dryair.MM "Ratio of molar weights";

  constant Modelica.Units.SI.MolarMass[2] MMX={steam.MM,dryair.MM}
    "Molar masses of components";

  constant Modelica.Units.SI.SpecificEnergy h_fg=AixLib.Utilities.Psychrometrics.Constants.h_fg
    "Latent heat of evaporation of water";
  constant Modelica.Units.SI.SpecificHeatCapacity cpWatLiq=AixLib.Utilities.Psychrometrics.Constants.cpWatLiq
    "Specific heat capacity of liquid water";

replaceable function der_enthalpyOfLiquid
    "Temperature derivative of enthalpy of liquid per unit mass of liquid"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of liquid enthalpy";
algorithm
  der_h := cpWatLiq*der_T;
end der_enthalpyOfLiquid;

function der_enthalpyOfCondensingGas
    "Derivative of enthalpy of steam per unit mass of steam"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of steam enthalpy";
algorithm
  der_h := steam.cp*der_T;
end der_enthalpyOfCondensingGas;

replaceable function enthalpyOfDryAir
    "Enthalpy of dry air per unit mass of dry air"
  extends Modelica.Icons.Function;

  input Temperature T "Temperature";
  output SpecificEnthalpy h "Dry air enthalpy";
algorithm
  h := (T - reference_T)*dryair.cp;
end enthalpyOfDryAir;

replaceable function der_enthalpyOfDryAir
    "Derivative of enthalpy of dry air per unit mass of dry air"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of dry air enthalpy";
algorithm
  der_h := dryair.cp*der_T;
end der_enthalpyOfDryAir;

replaceable function der_enthalpyOfNonCondensingGas
    "Derivative of enthalpy of non-condensing gas per unit mass of steam"
  extends Modelica.Icons.Function;
  input Temperature T "Temperature";
  input Real der_T "Temperature derivative";
  output Real der_h "Derivative of steam enthalpy";
algorithm
  der_h := der_enthalpyOfDryAir(T, der_T);
end der_enthalpyOfNonCondensingGas;

replaceable function der_specificHeatCapacityCp
    "Derivative of specific heat capacity of gas mixture at constant pressure"
  extends Modelica.Icons.Function;
    input ThermodynamicState state "Thermodynamic state";
    input ThermodynamicState der_state "Derivative of thermodynamic state";
    output Real der_cp(unit="J/(kg.K.s)")
      "Derivative of specific heat capacity";
algorithm
  der_cp := (steam.cp-dryair.cp)*der_state.X[Water];
end der_specificHeatCapacityCp;

replaceable function der_specificHeatCapacityCv
    "Derivative of specific heat capacity of gas mixture at constant volume"
  extends Modelica.Icons.Function;
    input ThermodynamicState state "Thermodynamic state";
    input ThermodynamicState der_state "Derivative of thermodynamic state";
    output Real der_cv(unit="J/(kg.K.s)")
      "Derivative of specific heat capacity";
algorithm
  der_cv := (steam.cv-dryair.cv)*der_state.X[Water];
end der_specificHeatCapacityCv;
end AirIncompressible;


Package with medium models

within AixLib;
package Media "Package with medium models"
  extends Modelica.Icons.Package;
end Media;