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;


within AixLib.Media;
package Steam
  "Package with model for pure steam water vapor"
  extends Modelica.Media.Interfaces.PartialMedium(
    mediumName="steam",
    final substanceNames={"water"},
    singleState=true,
    reducedX=true,
    fixedX=true,
    FluidConstants={Modelica.Media.IdealGases.Common.FluidData.H2O},
    ThermoStates=Modelica.Media.Interfaces.Choices.IndependentVariables.pTX,
    reference_T=273.15,
    reference_p=101325,
    reference_X={1},
    AbsolutePressure(start=p_default),
    Temperature(start=T_default),
    SpecificEnthalpy(start=2.7e6, nominal=2.7e6),
    Density(start=0.6, nominal=1),
    T_default=Modelica.Units.Conversions.from_degC(100),
    p_default=100000);
  extends Modelica.Icons.Package;

  redeclare record ThermodynamicState
    "Thermodynamic state variables"
    AbsolutePressure p "Absolute pressure of medium";
    Temperature T "Temperature of medium";
  end ThermodynamicState;

redeclare replaceable model extends BaseProperties(
    preferredMediumStates = true,
    final standardOrderComponents=true)
    "Base properties (p, d, T, h, u, R, MM) of water"
equation
    MM = steam.MM;
    h = specificEnthalpy(state);
    d = density(state);
    u = h - p/d;
    R_s = steam.R;
    state.p = p;
    state.T = T;
end BaseProperties;

redeclare replaceable function extends density
  "Returns density"
algorithm
  d := rho_pT(state.p, state.T);
end density;

redeclare replaceable function extends dynamicViscosity
    "Return dynamic viscosity"
algorithm
    eta := Modelica.Media.Water.IF97_Utilities.dynamicViscosity(
          d=density(state),
          T=state.T,
          p=state.p);
end dynamicViscosity;

redeclare replaceable function extends molarMass
  "Return the molar mass of the medium"
algorithm
  MM := steam.MM;
end molarMass;

redeclare function extends pressure
  "Return pressure"
algorithm
  p := state.p;
end pressure;

replaceable function saturationPressure
  "Return saturation pressure of condensing fluid"
    extends Modelica.Icons.Function;
    input Temperature Tsat "Saturation temperature";
    output AbsolutePressure psat "Saturation pressure";
algorithm
   psat := Modelica.Media.Water.IF97_Utilities.BaseIF97.Basic.psat(Tsat);
end saturationPressure;

replaceable function saturationTemperature
    "Return saturation temperature"
  input AbsolutePressure psat "Saturation pressure";
  output Temperature Tsat "Saturation temperature";
algorithm
    Tsat := Modelica.Media.Water.IF97_Utilities.BaseIF97.Basic.tsat(psat);
end saturationTemperature;

redeclare replaceable function extends specificEnthalpy
  "Returns specific enthalpy"
  protected
    constant Real a[:] = {2.749e+06,-9118,2.752e+04} "Regression coefficients";
    constant AbsolutePressure pMean = 2.50427896656637E+05 "Mean pressure";
    constant Temperature TMean =  4.15555698340926E+02 "Mean temperature";
    constant Real pSD = 1.13236055019318E+05 "Normalization value";
    constant Real TSD = 1.32971013463839E+01 "Normalization value";
    Modelica.Units.SI.PressureDifference pHat;
    Modelica.Units.SI.TemperatureDifference THat;
algorithm
  pHat := (state.p - pMean)/pSD;
  THat := (state.T - TMean)/TSD;
  h := a[1] + a[2]*pHat + a[3]*THat;
end specificEnthalpy;

redeclare replaceable function extends specificEntropy
  "Return specific entropy"
  protected
    constant Real a[:] = {7135,-252.4,70.03,40.6,4.953} "Regression coefficients";
    constant AbsolutePressure pMean = 2.50427896656637E+05 "Mean pressure";
    constant Temperature TMean =  4.15555698340926E+02 "Mean temperature";
    constant Real pSD = 1.13236055019318E+05 "Normalization value";
    constant Real TSD = 1.32971013463839E+01 "Normalization value";
    Modelica.Units.SI.PressureDifference pHat;
    Modelica.Units.SI.TemperatureDifference THat;
algorithm
  pHat := (state.p - pMean)/pSD;
  THat := (state.T - TMean)/TSD;
  s := a[1] + a[2]*pHat + a[3]*THat + pHat*(a[4]*pHat + a[5]*THat);
end specificEntropy;

redeclare replaceable function extends specificInternalEnergy
  "Return specific internal energy"
algorithm
  u := specificEnthalpy(state) - state.p/density(state);
end specificInternalEnergy;

redeclare replaceable function extends specificHeatCapacityCp
  "Specific heat capacity at constant pressure"

  protected
  Modelica.Media.Common.GibbsDerivs g
    "Dimensionless Gibbs function and derivatives w.r.t. pi and tau";
  SpecificHeatCapacity R "Specific gas constant of water vapor";
algorithm
  R := Modelica.Media.Water.IF97_Utilities.BaseIF97.data.RH2O;
  // Region 2 properties
  g := g2(state.p, state.T);
  cp := -R*g.tau*g.tau*g.gtautau;
end specificHeatCapacityCp;

redeclare replaceable function extends specificHeatCapacityCv
  "Specific heat capacity at constant volume"

  protected
  Modelica.Media.Common.GibbsDerivs g
    "Dimensionless Gibbs function and derivatives w.r.t. pi and tau";
  SpecificHeatCapacity R "Specific gas constant of water vapor";
algorithm
  R := Modelica.Media.Water.IF97_Utilities.BaseIF97.data.RH2O;
  // Region 2 properties
  g := g2(state.p, state.T);
  cv := R*(-g.tau*g.tau*g.gtautau + ((g.gpi - g.tau*g.gtaupi)*(g.gpi
     - g.tau*g.gtaupi)/g.gpipi));
end specificHeatCapacityCv;

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) - steam.R*state.T - state.T*specificEntropy(state);
end specificHelmholtzEnergy;

redeclare replaceable function extends setState_dTX
    "Return the thermodynamic state as function of d and T"
algorithm
  state := ThermodynamicState(p=pressure_dT(d,T), T=T);
end setState_dTX;

redeclare replaceable function extends setState_pTX
    "Return the thermodynamic state as function of p and T"
algorithm
  state := ThermodynamicState(p=p, T=T);
end setState_pTX;

redeclare replaceable function extends setState_phX
    "Return the thermodynamic state as function of p and h"
algorithm
  state := ThermodynamicState(p=p, T=temperature_ph(p,h));
end setState_phX;

redeclare replaceable function extends setState_psX
    "Return the thermodynamic state as function of p and s"
algorithm
  state := ThermodynamicState(p=p, T=temperature_ps(p,s));
end setState_psX;

redeclare function extends temperature
    "Return temperature"
algorithm
  T := state.T;
end temperature;

redeclare replaceable function extends thermalConductivity
  "Return thermal conductivity"
algorithm
  lambda := Modelica.Media.Water.IF97_Utilities.thermalConductivity(
        density(state),
        state.T,
        state.p);
end thermalConductivity;

redeclare function extends density_derh_p
  "Density derivative by specific enthalpy"
algorithm
  ddhp := Modelica.Media.Water.IF97_Utilities.ddhp(
        p = state.p,
        h = specificEnthalpy(state),
        phase = 1,
        region = 2);
end density_derh_p;

redeclare function extends density_derp_h
  "Density derivative by pressure"
algorithm
  ddph := Modelica.Media.Water.IF97_Utilities.ddph(
        p = state.p,
        h = specificEnthalpy(state),
        phase = 1,
        region = 2);
end density_derp_h;

redeclare replaceable function extends isentropicExponent
  "Return isentropic exponent"
algorithm
  gamma := Modelica.Media.Water.IF97_Utilities.isentropicExponent_pT(
        p = state.p,
        T = state.T,
        region = 2);
end isentropicExponent;

redeclare replaceable function extends isothermalCompressibility
  "Isothermal compressibility of water"
algorithm
  kappa := Modelica.Media.Water.IF97_Utilities.kappa_pT(
        p = state.p,
        T = state.T,
        region = 2);
end isothermalCompressibility;

redeclare replaceable function extends isobaricExpansionCoefficient
  "Isobaric expansion coefficient of water"
algorithm
  beta := Modelica.Media.Water.IF97_Utilities.beta_pT(
        p = state.p,
        T = state.T,
        region = 2);
end isobaricExpansionCoefficient;

redeclare replaceable function extends isentropicEnthalpy
  "Isentropic enthalpy"
algorithm
  h_is := Modelica.Media.Water.IF97_Utilities.isentropicEnthalpy(
        p = p_downstream,
        s = specificEntropy(refState),
        phase = 0); // phase 0 means unknown
end isentropicEnthalpy;
//////////////////////////////////////////////////////////////////////
// Protected 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";
end GasProperties;
  constant GasProperties steam(R=Modelica.Media.IdealGases.Common.SingleGasesData.H2O.R_s,
      MM=Modelica.Media.IdealGases.Common.SingleGasesData.H2O.MM)
    "Steam properties";

function g2 "Gibbs function for region 2: g(p,T)"
  extends Modelica.Icons.Function;
    input Modelica.Units.SI.Pressure p "Pressure";
    input Modelica.Units.SI.Temperature T "Temperature (K)";
  output Modelica.Media.Common.GibbsDerivs g
    "Dimensionless Gibbs function and derivatives w.r.t. pi and tau";
  protected
  Real tau2 "Dimensionless temperature";
  Real[55] o "Vector of auxiliary variables";
algorithm
  g.p := p;
  g.T := T;
    g.R_s := Modelica.Media.Water.IF97_Utilities.BaseIF97.data.RH2O;
//  assert(p > 0.0,
//    "IF97 medium function g2 called with too low pressure\n" + "p = " +
//    String(p) + " Pa <=  0.0 Pa");
//  assert(p <= 100.0e6, "IF97 medium function g2: the input pressure (= "
//     + String(p) + " Pa) is higher than 100 Mpa");
//  assert(T >= 273.15, "IF97 medium function g2: the temperature (= " +
//    String(T) + " K) is lower than 273.15 K!");
//  assert(T <= 1073.15,
//    "IF97 medium function g2: the input temperature (= " + String(T) +
//    " K) is higher than the limit of 1073.15 K");
  g.pi := p/Modelica.Media.Water.IF97_Utilities.BaseIF97.data.PSTAR2;
  g.tau := Modelica.Media.Water.IF97_Utilities.BaseIF97.data.TSTAR2/T;
  tau2 := -0.5 + g.tau;
  o[1] := tau2*tau2;
  o[2] := o[1]*tau2;
  o[3] := -0.050325278727930*o[2];
  o[4] := -0.057581259083432 + o[3];
  o[5] := o[4]*tau2;
  o[6] := -0.045996013696365 + o[5];
  o[7] := o[6]*tau2;
  o[8] := -0.0178348622923580 + o[7];
  o[9] := o[8]*tau2;
  o[10] := o[1]*o[1];
  o[11] := o[10]*o[10];
  o[12] := o[11]*o[11];
  o[13] := o[10]*o[11]*o[12]*tau2;
  o[14] := o[1]*o[10]*tau2;
  o[15] := o[10]*o[11]*tau2;
  o[16] := o[1]*o[12]*tau2;
  o[17] := o[1]*o[11]*tau2;
  o[18] := o[1]*o[10]*o[11];
  o[19] := o[10]*o[11]*o[12];
  o[20] := o[1]*o[10];
  o[21] := g.pi*g.pi;
  o[22] := o[21]*o[21];
  o[23] := o[21]*o[22];
  o[24] := o[10]*o[12]*tau2;
  o[25] := o[12]*o[12];
  o[26] := o[11]*o[12]*o[25]*tau2;
  o[27] := o[10]*o[12];
  o[28] := o[1]*o[10]*o[11]*tau2;
  o[29] := o[10]*o[12]*o[25]*tau2;
  o[30] := o[1]*o[10]*o[25]*tau2;
  o[31] := o[1]*o[11]*o[12];
  o[32] := o[1]*o[12];
  o[33] := g.tau*g.tau;
  o[34] := o[33]*o[33];
  o[35] := -0.000053349095828174*o[13];
  o[36] := -0.087594591301146 + o[35];
  o[37] := o[2]*o[36];
  o[38] := -0.0078785554486710 + o[37];
  o[39] := o[1]*o[38];
  o[40] := -0.00037897975032630 + o[39];
  o[41] := o[40]*tau2;
  o[42] := -0.000066065283340406 + o[41];
  o[43] := o[42]*tau2;
  o[44] := 5.7870447262208e-6*tau2;
  o[45] := -0.301951672367580*o[2];
  o[46] := -0.172743777250296 + o[45];
  o[47] := o[46]*tau2;
  o[48] := -0.091992027392730 + o[47];
  o[49] := o[48]*tau2;
  o[50] := o[1]*o[11];
  o[51] := o[10]*o[11];
  o[52] := o[11]*o[12]*o[25];
  o[53] := o[10]*o[12]*o[25];
  o[54] := o[1]*o[10]*o[25];
  o[55] := o[11]*o[12]*tau2;

  g.g := g.pi*(-0.00177317424732130 + o[9] + g.pi*(tau2*(-0.000033032641670203
     + (-0.000189489875163150 + o[1]*(-0.0039392777243355 + (-0.043797295650573
     - 0.0000266745479140870*o[13])*o[2]))*tau2) + g.pi*(
    2.04817376923090e-8 + (4.3870667284435e-7 + o[1]*(-0.000032277677238570
     + (-0.00150339245421480 - 0.040668253562649*o[13])*o[2]))*tau2 + g.pi
    *(g.pi*(2.29220763376610e-6*o[14] + g.pi*((-1.67147664510610e-11 + o[
    15]*(-0.00211714723213550 - 23.8957419341040*o[16]))*o[2] + g.pi*(-5.9059564324270e-18
     + o[17]*(-1.26218088991010e-6 - 0.038946842435739*o[18]) + g.pi*(o[
    11]*(1.12562113604590e-11 - 8.2311340897998*o[19]) + g.pi*(
    1.98097128020880e-8*o[15] + g.pi*(o[10]*(1.04069652101740e-19 + (-1.02347470959290e-13
     - 1.00181793795110e-9*o[10])*o[20]) + o[23]*(o[13]*(-8.0882908646985e-11
     + 0.106930318794090*o[24]) + o[21]*(-0.33662250574171*o[26] + o[21]*
    (o[27]*(8.9185845355421e-25 + (3.06293168762320e-13 -
    4.2002467698208e-6*o[15])*o[28]) + g.pi*(-5.9056029685639e-26*o[24]
     + g.pi*(3.7826947613457e-6*o[29] + g.pi*(-1.27686089346810e-15*o[30]
     + o[31]*(7.3087610595061e-29 + o[18]*(5.5414715350778e-17 -
    9.4369707241210e-7*o[32]))*g.pi)))))))))))) + tau2*(-7.8847309559367e-10
     + (1.27907178522850e-8 + 4.8225372718507e-7*tau2)*tau2))))) + (-0.0056087911830200
     + g.tau*(0.071452738814550 + g.tau*(-0.40710498239280 + g.tau*(
    1.42408197144400 + g.tau*(-4.3839511194500 + g.tau*(-9.6927686002170
     + g.tau*(10.0866556801800 + (-0.284086326077200 + 0.0212684635330700
    *g.tau)*g.tau) + Modelica.Math.log(g.pi)))))))/(o[34]*g.tau);

  g.gpi := (1.00000000000000 + g.pi*(-0.00177317424732130 + o[9] + g.pi*(
    o[43] + g.pi*(6.1445213076927e-8 + (1.31612001853305e-6 + o[1]*(-0.000096833031715710
     + (-0.0045101773626444 - 0.122004760687947*o[13])*o[2]))*tau2 + g.pi
    *(g.pi*(0.0000114610381688305*o[14] + g.pi*((-1.00288598706366e-10 +
    o[15]*(-0.0127028833928130 - 143.374451604624*o[16]))*o[2] + g.pi*(-4.1341695026989e-17
     + o[17]*(-8.8352662293707e-6 - 0.272627897050173*o[18]) + g.pi*(o[11]
    *(9.0049690883672e-11 - 65.849072718398*o[19]) + g.pi*(
    1.78287415218792e-7*o[15] + g.pi*(o[10]*(1.04069652101740e-18 + (-1.02347470959290e-12
     - 1.00181793795110e-8*o[10])*o[20]) + o[23]*(o[13]*(-1.29412653835176e-9
     + 1.71088510070544*o[24]) + o[21]*(-6.0592051033508*o[26] + o[21]*(o[
    27]*(1.78371690710842e-23 + (6.1258633752464e-12 -
    0.000084004935396416*o[15])*o[28]) + g.pi*(-1.24017662339842e-24*o[24]
     + g.pi*(0.000083219284749605*o[29] + g.pi*(-2.93678005497663e-14*o[
    30] + o[31]*(1.75410265428146e-27 + o[18]*(1.32995316841867e-15 -
    0.0000226487297378904*o[32]))*g.pi)))))))))))) + tau2*(-3.15389238237468e-9
     + (5.1162871409140e-8 + 1.92901490874028e-6*tau2)*tau2))))))/g.pi;

  g.gpipi := (-1.00000000000000 + o[21]*(o[43] + g.pi*(
    1.22890426153854e-7 + (2.63224003706610e-6 + o[1]*(-0.000193666063431420
     + (-0.0090203547252888 - 0.244009521375894*o[13])*o[2]))*tau2 + g.pi
    *(g.pi*(0.000045844152675322*o[14] + g.pi*((-5.0144299353183e-10 + o[
    15]*(-0.063514416964065 - 716.87225802312*o[16]))*o[2] + g.pi*(-2.48050170161934e-16
     + o[17]*(-0.000053011597376224 - 1.63576738230104*o[18]) + g.pi*(o[
    11]*(6.3034783618570e-10 - 460.94350902879*o[19]) + g.pi*(
    1.42629932175034e-6*o[15] + g.pi*(o[10]*(9.3662686891566e-18 + (-9.2112723863361e-12
     - 9.0163614415599e-8*o[10])*o[20]) + o[23]*(o[13]*(-1.94118980752764e-8
     + 25.6632765105816*o[24]) + o[21]*(-103.006486756963*o[26] + o[21]*(
    o[27]*(3.3890621235060e-22 + (1.16391404129682e-10 -
    0.00159609377253190*o[15])*o[28]) + g.pi*(-2.48035324679684e-23*o[24]
     + g.pi*(0.00174760497974171*o[29] + g.pi*(-6.4609161209486e-13*o[30]
     + o[31]*(4.0344361048474e-26 + o[18]*(3.05889228736295e-14 -
    0.00052092078397148*o[32]))*g.pi)))))))))))) + tau2*(-9.4616771471240e-9
     + (1.53488614227420e-7 + o[44])*tau2)))))/o[21];

  g.gtau := (0.0280439559151000 + g.tau*(-0.285810955258200 + g.tau*(
    1.22131494717840 + g.tau*(-2.84816394288800 + g.tau*(4.3839511194500
     + o[33]*(10.0866556801800 + (-0.56817265215440 + 0.063805390599210*g.tau)
    *g.tau))))))/(o[33]*o[34]) + g.pi*(-0.0178348622923580 + o[49] + g.pi
    *(-0.000033032641670203 + (-0.00037897975032630 + o[1]*(-0.0157571108973420
     + (-0.306581069554011 - 0.00096028372490713*o[13])*o[2]))*tau2 + g.pi
    *(4.3870667284435e-7 + o[1]*(-0.000096833031715710 + (-0.0090203547252888
     - 1.42338887469272*o[13])*o[2]) + g.pi*(-7.8847309559367e-10 + g.pi*
    (0.0000160454534363627*o[20] + g.pi*(o[1]*(-5.0144299353183e-11 + o[
    15]*(-0.033874355714168 - 836.35096769364*o[16])) + g.pi*((-0.0000138839897890111
     - 0.97367106089347*o[18])*o[50] + g.pi*(o[14]*(9.0049690883672e-11
     - 296.320827232793*o[19]) + g.pi*(2.57526266427144e-7*o[51] + g.pi*(
    o[2]*(4.1627860840696e-19 + (-1.02347470959290e-12 -
    1.40254511313154e-8*o[10])*o[20]) + o[23]*(o[19]*(-2.34560435076256e-9
     + 5.3465159397045*o[24]) + o[21]*(-19.1874828272775*o[52] + o[21]*(o[
    16]*(1.78371690710842e-23 + (1.07202609066812e-11 -
    0.000201611844951398*o[15])*o[28]) + g.pi*(-1.24017662339842e-24*o[27]
     + g.pi*(0.000200482822351322*o[53] + g.pi*(-4.9797574845256e-14*o[54]
     + (1.90027787547159e-27 + o[18]*(2.21658861403112e-15 -
    0.000054734430199902*o[32]))*o[55]*g.pi)))))))))))) + (
    2.55814357045700e-8 + 1.44676118155521e-6*tau2)*tau2))));

  g.gtautau := (-0.168263735490600 + g.tau*(1.42905477629100 + g.tau*(-4.8852597887136
     + g.tau*(8.5444918286640 + g.tau*(-8.7679022389000 + o[33]*(-0.56817265215440
     + 0.127610781198420*g.tau)*g.tau)))))/(o[33]*o[34]*g.tau) + g.pi*(-0.091992027392730
     + (-0.34548755450059 - 1.50975836183790*o[2])*tau2 + g.pi*(-0.00037897975032630
     + o[1]*(-0.047271332692026 + (-1.83948641732407 - 0.033609930371750*
    o[13])*o[2]) + g.pi*((-0.000193666063431420 + (-0.045101773626444 -
    48.395221739552*o[13])*o[2])*tau2 + g.pi*(2.55814357045700e-8 +
    2.89352236311042e-6*tau2 + g.pi*(0.000096272720618176*o[10]*tau2 + g.pi
    *((-1.00288598706366e-10 + o[15]*(-0.50811533571252 -
    28435.9329015838*o[16]))*tau2 + g.pi*(o[11]*(-0.000138839897890111 -
    23.3681054614434*o[18])*tau2 + g.pi*((6.3034783618570e-10 -
    10371.2289531477*o[19])*o[20] + g.pi*(3.09031519712573e-6*o[17] + g.pi
    *(o[1]*(1.24883582522088e-18 + (-9.2112723863361e-12 -
    1.82330864707100e-7*o[10])*o[20]) + o[23]*(o[1]*o[11]*o[12]*(-6.5676921821352e-8
     + 261.979281045521*o[24])*tau2 + o[21]*(-1074.49903832754*o[1]*o[10]
    *o[12]*o[25]*tau2 + o[21]*((3.3890621235060e-22 + (
    3.6448887082716e-10 - 0.0094757567127157*o[15])*o[28])*o[32] + g.pi*(
    -2.48035324679684e-23*o[16] + g.pi*(0.0104251067622687*o[1]*o[12]*o[
    25]*tau2 + g.pi*(o[11]*o[12]*(4.7506946886790e-26 + o[18]*(
    8.6446955947214e-14 - 0.00311986252139440*o[32]))*g.pi -
    1.89230784411972e-12*o[10]*o[25]*tau2))))))))))))))));

  g.gtaupi := -0.0178348622923580 + o[49] + g.pi*(-0.000066065283340406
     + (-0.00075795950065260 + o[1]*(-0.0315142217946840 + (-0.61316213910802
     - 0.00192056744981426*o[13])*o[2]))*tau2 + g.pi*(1.31612001853305e-6
     + o[1]*(-0.000290499095147130 + (-0.0270610641758664 -
    4.2701666240781*o[13])*o[2]) + g.pi*(-3.15389238237468e-9 + g.pi*(
    0.000080227267181813*o[20] + g.pi*(o[1]*(-3.00865796119098e-10 + o[15]
    *(-0.203246134285008 - 5018.1058061618*o[16])) + g.pi*((-0.000097187928523078
     - 6.8156974262543*o[18])*o[50] + g.pi*(o[14]*(7.2039752706938e-10 -
    2370.56661786234*o[19]) + g.pi*(2.31773639784430e-6*o[51] + g.pi*(o[2]
    *(4.1627860840696e-18 + (-1.02347470959290e-11 - 1.40254511313154e-7*
    o[10])*o[20]) + o[23]*(o[19]*(-3.7529669612201e-8 + 85.544255035272*o[
    24]) + o[21]*(-345.37469089099*o[52] + o[21]*(o[16]*(
    3.5674338142168e-22 + (2.14405218133624e-10 - 0.0040322368990280*o[15])
    *o[28]) + g.pi*(-2.60437090913668e-23*o[27] + g.pi*(
    0.0044106220917291*o[53] + g.pi*(-1.14534422144089e-12*o[54] + (
    4.5606669011318e-26 + o[18]*(5.3198126736747e-14 -
    0.00131362632479764*o[32]))*o[55]*g.pi)))))))))))) + (
    1.02325742818280e-7 + o[44])*tau2)));
end g2;

function temperature_ph
  "Return temperature from p and h, inverse function of h(p,T)"
    input AbsolutePressure p "Pressure";
    input SpecificEnthalpy h "Specific Enthalpy";
    output Temperature T "Temperature";
  protected
    constant Real a[:] = {2.749e+06,-9118,2.752e+04} "Coefficients from forward function h(p,T)";
    constant Real b[:] = {-a[1]*TSD/a[3]+TMean, -a[2]*TSD/a[3], TSD/a[3]} "Regression coefficients";
    constant AbsolutePressure pMean = 2.50427896656637E+05 "Mean pressure";
    constant Temperature TMean =  4.15555698340926E+02 "Mean temperature";
    constant Real pSD = 1.13236055019318E+05 "Normalization value";
    constant Real TSD = 1.32971013463839E+01 "Normalization value";
    Modelica.Units.SI.PressureDifference pHat;
algorithm
  pHat := (p - pMean)/pSD;
  T := b[1] + b[2]*pHat + b[3]*h;
end temperature_ph;

function temperature_ps
  "Return temperature from p and s, inverse function of s(p,T)"
    input AbsolutePressure p "Pressure";
    input SpecificEntropy s "Specific Entropy";
    output Temperature T "Temperature";
  protected
    constant Real a[:] = {7135,-252.4,70.03,40.6,4.953} "Coefficients from forward function s(p,T)";
    constant AbsolutePressure pMean = 2.50427896656637E+05 "Mean pressure";
    constant Temperature TMean =  4.15555698340926E+02 "Mean temperature";
    constant Real pSD = 1.13236055019318E+05 "Normalization value";
    constant Real TSD = 1.32971013463839E+01 "Normalization value";
    Modelica.Units.SI.PressureDifference pHat;
    Modelica.Units.SI.TemperatureDifference THat;
algorithm
  pHat := (p - pMean)/pSD;
  THat := (s - a[1] - pHat*(a[2] + a[4]*pHat))/(a[3] + a[5]*pHat);
  T := THat*TSD + TMean;
end temperature_ps;

function rho_pT "Density as function of temperature and pressure"
  extends Modelica.Icons.Function;
  input AbsolutePressure p "Pressure";
  input Temperature T "Temperature";
  output Density rho "Density";
  protected
  Modelica.Media.Common.GibbsDerivs g
    "Dimensionless Gibbs function and derivatives w.r.t. pi and tau";
  SpecificHeatCapacity R "Specific gas constant of water vapor";
  function g2_smooth
    extends Modelica.Media.Water.IF97_Utilities.BaseIF97.Basic.g2;
  end g2_smooth;
algorithm
  R := Modelica.Media.Water.IF97_Utilities.BaseIF97.data.RH2O;
  // Region 2 properties
  g := g2_smooth(p, T);
  rho := p/(R*T*g.pi*g.gpi);
end rho_pT;

function pressure_dT
  "Computes pressure as a function of density and temperature"
  input Density d "Density";
  input Temperature T "Temperature";
  output AbsolutePressure p "Pressure";
algorithm
  p := Modelica.Media.Water.IF97_Utilities.BaseIF97.Inverses.pofdt125(
        d=d,
        T=T,
        reldd=1.0e-8,
        region=2);
end pressure_dT;
end Steam;


within AixLib.Media;
package Water "Package with model for liquid water with constant density"
   extends Modelica.Media.Water.ConstantPropertyLiquidWater(
     p_default=300000,
     reference_p=300000,
     reference_T=273.15,
     reference_X={1},
     AbsolutePressure(start=p_default),
     Temperature(start=T_default),
     Density(start=d_const),
     final cv_const=cp_const);
  // cp_const and cv_const have been made final because the model sets u=h.
  extends Modelica.Icons.Package;

  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";
    Modelica.Units.SI.Density d=d_const "Density of medium";
    Temperature T(
      start=293.15,
      stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default)
      "Temperature of medium";
    InputAbsolutePressure p "Absolute pressure of medium";
    InputMassFraction[nXi] Xi=fill(0, 0)
      "Structurally independent mass fractions";
    InputSpecificEnthalpy h "Specific enthalpy of medium";
    Modelica.Units.SI.SpecificInternalEnergy u
      "Specific internal energy of medium";

    Modelica.Units.SI.MassFraction[nX] X={1}
      "Mass fractions (= (component mass)/total mass  m_i/m)";
    final Modelica.Units.SI.SpecificHeatCapacity R_s=0
      "Gas constant (of mixture if applicable)";
    final Modelica.Units.SI.MolarMass MM=MM_const
      "Molar mass (of mixture or single fluid)";
    ThermodynamicState state
      "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";

  equation
    h = cp_const*(T-reference_T);
    u = h;
    state.T = T;
    state.p = p;

    // Assertions to test for bounds
    assert(noEvent(T >= T_min), "In " + getInstanceName() + ": Temperature T = " + String(T) + " K exceeded its minimum allowed value of " +
  String(T_min-273.15) + " degC (" + String(T_min) + " Kelvin) as required from medium model \"AixLib.Media.Water\".");

    assert(noEvent(T <= T_max), "In " + getInstanceName() + ": Temperature T = " + String(T) + " K exceeded its maximum allowed value of " +
  String(T_max-273.15) + " degC (" + String(T_max) + " Kelvin) as required from medium model \"AixLib.Media.Water\".");

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

function enthalpyOfLiquid "Return the specific enthalpy of liquid"
  extends Modelica.Icons.Function;
    input Modelica.Units.SI.Temperature T "Temperature";
    output Modelica.Units.SI.SpecificEnthalpy h "Specific enthalpy";
algorithm
  h := cp_const*(T-reference_T);
end enthalpyOfLiquid;
end Water;


within AixLib.Media.Antifreeze;
package EthyleneGlycolWater
  "Package with model for ethylene glycol - water with constant properties"
  extends Modelica.Media.Interfaces.PartialSimpleMedium(
    mediumName="EthyleneGlycolWater(X_a = " + String(X_a) + ", property_T = "
         + String(property_T) + ")",
    final cp_const=specificHeatCapacityCp_TX_a(T=property_T, X_a=X_a),
    final cv_const=cp_const,
    final d_const=density_TX_a(T=property_T, X_a=X_a),
    final eta_const=dynamicViscosity_TX_a(T=property_T, X_a=X_a),
    final lambda_const=thermalConductivity_TX_a(T=property_T, X_a=X_a),
    a_const=1484,
    final T_min=fusionTemperature_TX_a(T=property_T, X_a=X_a),
    T_max=Modelica.Units.Conversions.from_degC(100),
    T0=273.15,
    MM_const=(X_a/simpleEthyleneGlycolWaterConstants[1].molarMass + (1 - X_a)/
        0.018015268)^(-1),
    fluidConstants=simpleEthyleneGlycolWaterConstants,
    p_default=300000,
    reference_p=300000,
    reference_T=273.15,
    reference_X={1},
    AbsolutePressure(start=p_default),
    Temperature(start=T_default),
    Density(start=d_const));

  constant Modelica.Units.SI.Temperature property_T
    "Temperature for evaluation of constant fluid properties";
  constant Modelica.Units.SI.MassFraction X_a
    "Mass fraction of propylene glycol in water";

  redeclare model BaseProperties "Base properties"
    Temperature T(stateSelect=
      if preferredMediumStates then StateSelect.prefer else StateSelect.default)
      "Temperature of medium";

    InputAbsolutePressure p "Absolute pressure of medium";
    InputMassFraction[nXi] Xi=fill(0, 0)
      "Structurally independent mass fractions";
    InputSpecificEnthalpy h "Specific enthalpy of medium";
    Modelica.Units.SI.SpecificInternalEnergy u
      "Specific internal energy of medium";
    Modelica.Units.SI.Density d=d_const "Density of medium";
    Modelica.Units.SI.MassFraction[nX] X={1}
      "Mass fractions (= (component mass)/total mass  m_i/m)";
    final Modelica.Units.SI.SpecificHeatCapacity R_s=0
      "Gas constant (of mixture if applicable)";
    final Modelica.Units.SI.MolarMass MM=MM_const
      "Molar mass (of mixture or single fluid)";
    ThermodynamicState state
      "Thermodynamic state record for optional functions";
    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";
    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";

  equation
  assert(T >= T_min, "
In "   + getInstanceName() + ": Temperature T exceeded its minimum allowed value of " + String(T_min-273.15)
    + " degC (" + String(T_min) + " Kelvin)
as required from medium model \"" + mediumName + "\".");
  assert(T <= T_max, "
In "   + getInstanceName() + ": Temperature T exceeded its maximum allowed value of " + String(T_max-273.15)
    + " degC (" + String(T_max) + " Kelvin)
as required from medium model \"" + mediumName + "\".");

  assert(X_a >= X_a_min, "
In "   + getInstanceName() + ": Mass fraction x_a exceeded its minimum allowed value of " + String(X_a_min) + "
as required from medium model \"" + mediumName + "\".");
  assert(X_a <= X_a_max, "
In "   + getInstanceName() + ": Mass fraction x_a exceeded its maximum allowed value of " + String(X_a_max) + "
as required from medium model \"" + mediumName + "\".");

    h = cp_const*(T-reference_T);
    u = h;
    state.T = T;
    state.p = p;
  end BaseProperties;
protected
  constant Modelica.Units.SI.MassFraction X_a_min=0.
    "Minimum allowed mass fraction of ethylene glycol in water";
  constant Modelica.Units.SI.MassFraction X_a_max=0.6
    "Maximum allowed mass fraction of propylene glycol in water";

  // Fluid constants based on pure Ethylene Glycol
  constant Modelica.Media.Interfaces.Types.Basic.FluidConstants[1]
    simpleEthyleneGlycolWaterConstants(
    each chemicalFormula="C2H6O2",
    each structureFormula="HOCH2CH2OH",
    each casRegistryNumber="107-21-1",
    each iupacName="Ethane-1,2-diol",
    each molarMass=0.062068);

  // Coefficients for evaluation of physical properties
  constant AixLib.Media.Antifreeze.BaseClasses.PropertyCoefficients proCoe(
    X_a_ref=0.308462,
    T_ref=Modelica.Units.Conversions.from_degC(31.728),
    nX_a=6,
    nT={4,4,4,3,2,1},
    nTot=18,
    a_d={1.034e3,-4.781e-1,-2.692e-3,4.725e-6,1.311e0,-6.876e-3,4.805e-5,
        1.690e-8,7.490e-5,7.855e-5,-3.995e-7,4.982e-9,-1.062e-4,1.229e-6,-1.153e-8,
        -9.623e-7,-7.221e-8,4.891e-8},
    a_eta={4.705e-1,-2.550e-2,1.782e-4,-7.669e-7,2.471e-2,-1.171e-4,1.052e-6,-1.634e-8,
        3.328e-6,-1.086e-6,-1.051e-8,-6.475e-10,1.695e-6,3.157e-9,4.063e-10,
        3.089e-8,1.831e-10,-1.865e-9},
    a_Tf={-1.525e1,-1.566e-6,-2.278e-7,2.169e-9,-8.080e-1,-1.339e-6,2.047e-08,-2.717e-11,
        -1.334e-2,6.332e-8,2.373e-10,-2.183e-12,-7.293e-5,-1.764e-9,-2.442e-11,
        1.006e-6,-7.662e-11,1.140e-9},
    a_cp={3.737e3,2.930e0,-4.675e-3,-1.389e-5,-1.799e1,1.046e-1,-4.147e-4,
        1.847e-7,-9.933e-2,3.516e-4,5.109e-6,-7.138e-8,2.610e-3,-1.189e-6,-1.643e-7,
        1.537e-5,-4.272e-7,-1.618e-6},
    a_lambda={4.720e-1,8.903e-4,-1.058e-6,-2.789e-9,-4.286e-3,-1.473e-5,
        1.059e-7,-1.142e-10,1.747e-5,6.814e-8,-3.612e-9,2.365e-12,3.017e-8,-2.412e-9,
        4.004e-11,-1.322e-09,2.555e-11,2.678e-11})
    "Coefficients for evaluation of thermo-physical properties";

  replaceable function density_TX_a
    "Evaluate density of antifreeze-water mixture"
    extends Modelica.Icons.Function;
    input Modelica.Units.SI.Temperature T
      "Temperature of antifreeze-water mixture";
    input Modelica.Units.SI.MassFraction X_a "Mass fraction of antifreeze";
    output Modelica.Units.SI.Density d "Density of antifreeze-water mixture";
  algorithm
    d :=polynomialProperty(
        X_a,
        T,
        proCoe.a_d)

  end density_TX_a;

  replaceable function dynamicViscosity_TX_a
    "Evaluate dynamic viscosity of antifreeze-water mixture"
      extends Modelica.Icons.Function;
    input Modelica.Units.SI.Temperature T
      "Temperature of antifreeze-water mixture";
    input Modelica.Units.SI.MassFraction X_a "Mass fraction of antifreeze";
    output Modelica.Units.SI.DynamicViscosity eta
      "Dynamic Viscosity of antifreeze-water mixture";
  algorithm
    eta :=1e-3*exp(polynomialProperty(
        X_a,
        T,
        proCoe.a_eta));
  end dynamicViscosity_TX_a;

  replaceable function fusionTemperature_TX_a
    "Evaluate temperature of fusion of antifreeze-water mixture"
      extends Modelica.Icons.Function;
    input Modelica.Units.SI.Temperature T
      "Temperature of antifreeze-water mixture";
    input Modelica.Units.SI.MassFraction X_a "Mass fraction of antifreeze";
    output Modelica.Units.SI.Temperature Tf
      "Temperature of fusion of antifreeze-water mixture";
  algorithm
    Tf :=Modelica.Units.Conversions.from_degC(polynomialProperty(
        X_a,
        T,
        proCoe.a_Tf));
  end fusionTemperature_TX_a;

  replaceable function polynomialProperty
    "Evaluates thermophysical property from 2-variable polynomial"
    extends Modelica.Icons.Function;

    input Real x "First independent variable";
    input Real y "Second independent variable";
    input Real a[sum(proCoe.nT)] "Polynomial coefficients";

    output Real f "Value of thermophysical property";

  protected
    Real dx;
    Real dy;
    Integer n;
  algorithm
    dx := 100*(x - proCoe.X_a_ref);
    dy := y - proCoe.T_ref;

    f := 0;
    n := 0;
    for i in 0:proCoe.nX_a - 1 loop
      for j in 0:proCoe.nT[i+1] - 1 loop
        n := n + 1;
        f := f + a[n]*dx^i*dy^j;
      end for;
    end for;
  end polynomialProperty;

  replaceable function specificHeatCapacityCp_TX_a
    "Evaluate specific heat capacity of antifreeze-water mixture"
      extends Modelica.Icons.Function;
    input Modelica.Units.SI.Temperature T
      "Temperature of antifreeze-water mixture";
    input Modelica.Units.SI.MassFraction X_a "Mass fraction of antifreeze";
    output Modelica.Units.SI.SpecificHeatCapacity cp
      "Specific heat capacity of antifreeze-water mixture";
  algorithm
    cp :=polynomialProperty(
        X_a,
        T,
        proCoe.a_cp);
  end specificHeatCapacityCp_TX_a;

  replaceable function thermalConductivity_TX_a
    "Evaluate thermal conductivity of antifreeze-water mixture"
      extends Modelica.Icons.Function;
    input Modelica.Units.SI.Temperature T
      "Temperature of antifreeze-water mixture";
    input Modelica.Units.SI.MassFraction X_a "Mass fraction of antifreeze";
    output Modelica.Units.SI.ThermalConductivity lambda
      "Thermal conductivity of antifreeze-water mixture";
  algorithm
    lambda :=polynomialProperty(
        X_a,
        T,
        proCoe.a_lambda);
  end thermalConductivity_TX_a;
end EthyleneGlycolWater;


within AixLib.Media;
package Antifreeze "Medium packages of secondary working fluids"
  extends Modelica.Icons.Package;
end Antifreeze;