This is a medium model that is similar to Modelica.Media.Air.MoistAir but it has a constant specific heat capacity.
In particular, the medium is thermally perfect, i.e.,
| Name | Description | 
|---|---|
| Water=1 | Index of water (in substanceNames, massFractions X, etc.) | 
| Air=2 | Index of air (in substanceNames, massFractions X, etc.) | 
| k_mair=steam.MM/dryair.MM | ratio of molar weights | 
| dryair=Buildings.Media.PerfectGases.Common.SingleGasData.Air | |
| steam=Buildings.Media.PerfectGases.Common.SingleGasData.H2O | |
| saturation curve valid for 223.16 <= T <= 373.16 (and slightly outside with less accuracy) | |
| enthalpy of vaporization of water | |
| specific heat capacity of water (liquid only) | |
| temperature derivative of enthalpy of liquid per unit mass of steam | |
| enthalpy of steam per unit mass of steam | |
| derivative of enthalpy of steam per unit mass of steam | |
| Return specific heat capacity at constant pressure | |
| Return specific heat capacity at constant volume | |
| Compute specific enthalpy from pressure, temperature and mass fraction | |
| specific enthalpy | |
| Return specific internal energy | |
| Compute temperature from specific enthalpy and mass fraction | |
| utility functions | 
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 Real k_mair = steam.MM/dryair.MM "ratio of molar weights";
  constant Buildings.Media.PerfectGases.Common.DataRecord dryair=
        Buildings.Media.PerfectGases.Common.SingleGasData.Air;
  constant Buildings.Media.PerfectGases.Common.DataRecord steam=
        Buildings.Media.PerfectGases.Common.SingleGasData.H2O;
Buildings.Media.PerfectGases.MoistAir.BaseProperties
| Type | Name | Default | Description | 
|---|---|---|---|
| Advanced | |||
| Boolean | preferredMediumStates | false | = true if StateSelect.prefer shall be used for the independent property variables of the medium | 
redeclare replaceable model extends BaseProperties(
  T(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
  p(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default),
  Xi(stateSelect=if preferredMediumStates then StateSelect.prefer else StateSelect.default)) 
  
  /* p, T, X = X[Water] are used as preferred states, since only then all
     other quantities can be computed in a recursive sequence. 
     If other variables are selected as states, static state selection
     is no longer possible and non-linear algebraic equations occur.
      */
  MassFraction x_water "mass of total water/mass of dry air";
  Real phi "relative humidity";
  
protected 
  constant SI.MolarMass[2] MMX = {steam.MM,dryair.MM} 
    "molar masses of components";
  
  MassFraction X_liquid "mass fraction of liquid water";
  MassFraction X_steam "mass fraction of steam water";
  MassFraction X_air "mass fraction of air";
  MassFraction X_sat 
    "steam water mass fraction of saturation boundary in kg_water/kg_moistair";
  MassFraction x_sat 
    "steam water mass content of saturation boundary in kg_water/kg_dryair";
  AbsolutePressure p_steam_sat "Partial saturation pressure of steam";
equation 
  assert(T >= 200.0 and T <= 423.15, "
Temperature T is not in the allowed range
200.0 K <= (T ="
             + String(T) + " K) <= 423.15 K
required from medium model \""   + mediumName + "\".");
  MM = 1/(Xi[Water]/MMX[Water]+(1.0-Xi[Water])/MMX[Air]);
  
  p_steam_sat = min(saturationPressure(T),0.999*p);
  X_sat = min(p_steam_sat * k_mair/max(100*Modelica.Constants.eps, p - p_steam_sat)*(1 - Xi[Water]), 1.0) 
    "Water content at saturation with respect to actual water content";
  X_liquid = max(Xi[Water] - X_sat, 0.0);
  X_steam  = Xi[Water]-X_liquid;
  X_air    = 1-Xi[Water];
  
  h = specificEnthalpy_pTX(p,T,Xi);
  R = dryair.R*(1 - X_steam/(1 - X_liquid)) + steam.R*X_steam/(1 - X_liquid);
  //                
  u = h - R*T;
  d = p/(R*T);
  /* Note, u and d are computed under the assumption that the volume of the liquid
         water is neglible with respect to the volume of air and of steam
      */
  state.p = p;
  state.T = T;
  state.X = X;
  
  // this x_steam is water load / dry air!!!!!!!!!!!
  x_sat    = k_mair*p_steam_sat/max(100*Modelica.Constants.eps,p - p_steam_sat);
  x_water = Xi[Water]/max(X_air,100*Modelica.Constants.eps);
  phi = p/p_steam_sat*Xi[Water]/(Xi[Water] + k_mair*X_air);
end BaseProperties;
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | shermodynamic state | 
| Type | Name | Description | 
|---|---|---|
| MassFraction | X_sat | steam mass fraction of sat. boundary [kg/kg] | 
function Xsaturation = Modelica.Media.Air.MoistAir.Xsaturation;
Buildings.Media.PerfectGases.MoistAir.setState_pTX
| Type | Name | Default | Description | 
|---|---|---|---|
| AbsolutePressure | p | Pressure [Pa] | |
| Temperature | T | Temperature [K] | |
| MassFraction | X[:] | Mass fractions [kg/kg] | 
| Type | Name | Description | 
|---|---|---|
| ThermodynamicState | state | 
redeclare function setState_pTX 
    extends Modelica.Media.Air.MoistAir.setState_pTX;
end setState_pTX;
Buildings.Media.PerfectGases.MoistAir.setState_phX
| Type | Name | Default | Description | 
|---|---|---|---|
| AbsolutePressure | p | Pressure [Pa] | |
| SpecificEnthalpy | h | Specific enthalpy [J/kg] | |
| MassFraction | X[:] | Mass fractions [kg/kg] | 
| Type | Name | Description | 
|---|---|---|
| ThermodynamicState | state | 
redeclare function setState_phX extends Modelica.Media.Air.MoistAir.setState_phX; end setState_phX;
Buildings.Media.PerfectGases.MoistAir.setState_dTX
| Type | Name | Default | Description | 
|---|---|---|---|
| Density | d | density [kg/m3] | |
| Temperature | T | Temperature [K] | |
| MassFraction | X[:] | Mass fractions [kg/kg] | 
| Type | Name | Description | 
|---|---|---|
| ThermodynamicState | state | 
redeclare function setState_dTX extends Modelica.Media.Air.MoistAir.setState_dTX; end setState_dTX;
Buildings.Media.PerfectGases.MoistAir.gasConstant
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | thermodynamic state | 
| Type | Name | Description | 
|---|---|---|
| SpecificHeatCapacity | R | mixture gas constant [J/(kg.K)] | 
redeclare function gasConstant extends Modelica.Media.Air.MoistAir.gasConstant; end gasConstant;
Buildings.Media.PerfectGases.MoistAir.saturationPressureLiquid
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | Tsat | saturation temperature [K] | 
| Type | Name | Description | 
|---|---|---|
| AbsolutePressure | psat | saturation pressure [Pa] | 
function saturationPressureLiquid = 
    Modelica.Media.Air.MoistAir.saturationPressureLiquid;
Buildings.Media.PerfectGases.MoistAir.sublimationPressureIce
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | Tsat | sublimation temperature [K] | 
| Type | Name | Description | 
|---|---|---|
| AbsolutePressure | psat | sublimation pressure [Pa] | 
function sublimationPressureIce = 
    Modelica.Media.Air.MoistAir.sublimationPressureIce;
Buildings.Media.PerfectGases.MoistAir.saturationPressure
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | Tsat | saturation temperature [K] | 
| Type | Name | Description | 
|---|---|---|
| AbsolutePressure | psat | saturation pressure [Pa] | 
redeclare function extends saturationPressure "saturation curve valid for 223.16 <= T <= 373.16 (and slightly outside with less accuracy)" algorithm psat := Utilities.spliceFunction(saturationPressureLiquid(Tsat),sublimationPressureIce(Tsat),Tsat-273.16,1.0); end saturationPressure;
Buildings.Media.PerfectGases.MoistAir.pressure
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| AbsolutePressure | p | Pressure [Pa] | 
redeclare function pressure extends Modelica.Media.Air.MoistAir.pressure; end pressure;
Buildings.Media.PerfectGases.MoistAir.temperature
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| Temperature | T | Temperature [K] | 
redeclare function temperature extends Modelica.Media.Air.MoistAir.temperature; end temperature;
Buildings.Media.PerfectGases.MoistAir.density
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| Density | d | Density [kg/m3] | 
redeclare function density extends Modelica.Media.Air.MoistAir.density; end density;
Buildings.Media.PerfectGases.MoistAir.specificEntropy
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| SpecificEntropy | s | Specific entropy [J/(kg.K)] | 
redeclare function specificEntropy extends Modelica.Media.Air.MoistAir.specificEntropy; end specificEntropy;
Buildings.Media.PerfectGases.MoistAir.enthalpyOfVaporization
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnthalpy | r0 | vaporization enthalpy [J/kg] | 
redeclare function extends enthalpyOfVaporization "enthalpy of vaporization of water" algorithm r0 := 2501014.5; end enthalpyOfVaporization;
Buildings.Media.PerfectGases.MoistAir.HeatCapacityOfWater
constant specific heat capacity of water
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificHeatCapacity | cp_fl | [J/(kg.K)] | 
function HeatCapacityOfWater "specific heat capacity of water (liquid only)" extends Modelica.Icons.Function; input Temperature T; output SpecificHeatCapacity cp_fl; algorithm cp_fl := 4186; end HeatCapacityOfWater;
Buildings.Media.PerfectGases.MoistAir.enthalpyOfLiquid
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnthalpy | h | liquid enthalpy [J/kg] | 
redeclare replaceable function extends enthalpyOfLiquid annotation(derivative=der_enthalpyOfLiquid); algorithm h := (T - 273.15)*4186; end enthalpyOfLiquid;
Buildings.Media.PerfectGases.MoistAir.der_enthalpyOfLiquid
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | |
| Temperature | der_T | temperature derivative [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificHeatCapacity | der_h | derivative of liquid enthalpy [J/(kg.K)] | 
replaceable function der_enthalpyOfLiquid "temperature derivative of enthalpy of liquid per unit mass of steam" extends Modelica.Icons.Function; input Temperature T "temperature"; input Temperature der_T "temperature derivative"; output SpecificHeatCapacity der_h "derivative of liquid enthalpy"; algorithm der_h := 4186; end der_enthalpyOfLiquid;
Buildings.Media.PerfectGases.MoistAir.enthalpyOfCondensingGas
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnthalpy | h | steam enthalpy [J/kg] | 
redeclare function enthalpyOfCondensingGas "enthalpy of steam per unit mass of steam" annotation(derivative=der_enthalpyOfCondensingGas); extends Modelica.Icons.Function; input Temperature T "temperature"; output SpecificEnthalpy h "steam enthalpy"; algorithm h := (T-273.15) * steam.cp + enthalpyOfVaporization(T); end enthalpyOfCondensingGas;
Buildings.Media.PerfectGases.MoistAir.der_enthalpyOfCondensingGas
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | |
| Temperature | der_T | temperature derivative [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificHeatCapacity | der_h | derivative of steam enthalpy [J/(kg.K)] | 
replaceable function der_enthalpyOfCondensingGas "derivative of enthalpy of steam per unit mass of steam" extends Modelica.Icons.Function; input Temperature T "temperature"; input Temperature der_T "temperature derivative"; output SpecificHeatCapacity der_h "derivative of steam enthalpy"; algorithm der_h := steam.cp; end der_enthalpyOfCondensingGas;
Buildings.Media.PerfectGases.MoistAir.enthalpyOfGas
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | |
| MassFraction | X[:] | vector of mass fractions [kg/kg] | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnthalpy | h | liquid enthalpy [J/kg] | 
redeclare replaceable function extends enthalpyOfGas 
algorithm 
  h := enthalpyOfCondensingGas(T)*X[Water]
       + enthalpyOfDryAir(T)*(1.0-X[Water]);
end enthalpyOfGas;
Buildings.Media.PerfectGases.MoistAir.enthalpyOfDryAir
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnthalpy | h | dry air enthalpy [J/kg] | 
replaceable function enthalpyOfDryAir annotation(derivative=der_enthalpyOfDryAir); extends Modelica.Icons.Function; input Temperature T "temperature"; output SpecificEnthalpy h "dry air enthalpy"; algorithm h := (T - 273.15)*dryair.cp; end enthalpyOfDryAir;
Buildings.Media.PerfectGases.MoistAir.der_enthalpyOfDryAir
| Type | Name | Default | Description | 
|---|---|---|---|
| Temperature | T | temperature [K] | |
| Temperature | der_T | temperature derivative [K] | 
| Type | Name | Description | 
|---|---|---|
| SpecificHeatCapacity | der_h | derivative of dry air enthalpy [J/(kg.K)] | 
replaceable function der_enthalpyOfDryAir extends Modelica.Icons.Function; input Temperature T "temperature"; input Temperature der_T "temperature derivative"; output SpecificHeatCapacity der_h "derivative of dry air enthalpy"; algorithm der_h := dryair.cp; end der_enthalpyOfDryAir;
Buildings.Media.PerfectGases.MoistAir.specificHeatCapacityCp
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| SpecificHeatCapacity | cp | Specific heat capacity at constant pressure [J/(kg.K)] | 
redeclare replaceable function extends specificHeatCapacityCp "Return specific heat capacity at constant pressure" algorithm cp := dryair.cp*(1-state.X[Water]) +steam.cp*state.X[Water]; end specificHeatCapacityCp;
Buildings.Media.PerfectGases.MoistAir.specificHeatCapacityCv
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| SpecificHeatCapacity | cv | Specific heat capacity at constant volume [J/(kg.K)] | 
redeclare replaceable function extends specificHeatCapacityCv "Return specific heat capacity at constant volume" algorithm cv:= dryair.cv*(1-state.X[Water]) +steam.cv*state.X[Water]; end specificHeatCapacityCv;
Buildings.Media.PerfectGases.MoistAir.dynamicViscosity
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| DynamicViscosity | eta | Dynamic viscosity [Pa.s] | 
redeclare function extends dynamicViscosity algorithm eta := 1.85E-5; end dynamicViscosity;
Buildings.Media.PerfectGases.MoistAir.thermalConductivity
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| ThermalConductivity | lambda | Thermal conductivity [W/(m.K)] | 
redeclare function extends thermalConductivity 
algorithm 
  lambda := Polynomials_Temp.evaluate({(-4.8737307422969E-008), 7.67803133753502E-005, 0.0241814385504202},
   Cv.to_degC(state.T));
end thermalConductivity;
Buildings.Media.PerfectGases.MoistAir.h_pTX
| Type | Name | Default | Description | 
|---|---|---|---|
| Pressure | p | Pressure [Pa] | |
| Temperature | T | Temperature [K] | |
| MassFraction | X[nX] | Mass fractions of moist air [1] | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnthalpy | h | Specific enthalpy at p, T, X [J/kg] | 
function h_pTX 
  "Compute specific enthalpy from pressure, temperature and mass fraction" 
  extends Modelica.Icons.Function;
  input SI.Pressure p "Pressure";
  input SI.Temperature T "Temperature";
  input SI.MassFraction X[nX] "Mass fractions of moist air";
  output SI.SpecificEnthalpy h "Specific enthalpy at p, T, X";
  
protected 
  SI.AbsolutePressure p_steam_sat "Partial saturation pressure of steam";
  SI.MassFraction x_sat "steam water mass fraction of saturation boundary";
  SI.MassFraction X_liquid "mass fraction of liquid water";
  SI.MassFraction X_steam "mass fraction of steam water";
  SI.MassFraction X_air "mass fraction of air";
  SI.SpecificEnthalpy hDryAir "Enthalpy of dry air";
algorithm 
  p_steam_sat :=saturationPressure(T);
  x_sat    :=k_mair*p_steam_sat/(p - p_steam_sat);
  X_liquid :=max(X[Water] - x_sat/(1 + x_sat), 0.0);
  X_steam  :=X[Water] - X_liquid;
  X_air    :=1 - X[Water];
  
/* THIS DOES NOT WORK --------------------------    
  h := enthalpyOfDryAir(T) * X_air + 
       Modelica.Media.Air.MoistAir.enthalpyOfCondensingGas(T) * X_steam + enthalpyOfLiquid(T)*X_liquid;
--------------------------------- */
  
/* THIS WORKS!!!! +++++++++++++++++++++
  h := (T - 273.15)*dryair.cp * X_air + 
       Modelica.Media.Air.MoistAir.enthalpyOfCondensingGas(T) * X_steam + enthalpyOfLiquid(T)*X_liquid;
 +++++++++++++++++++++*/
  
  hDryAir := (T - 273.15)*dryair.cp;
  h := hDryAir * X_air +
       ((T-273.15) * steam.cp + 2501014.5) * X_steam +
       (T - 273.15)*4186*X_liquid;
end h_pTX;
Buildings.Media.PerfectGases.MoistAir.specificEnthalpy
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnthalpy | h | Specific enthalpy [J/kg] | 
redeclare function extends specificEnthalpy "specific enthalpy" algorithm h := h_pTX(state.p, state.T, state.X); end specificEnthalpy;
Buildings.Media.PerfectGases.MoistAir.specificInternalEnergy
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnergy | u | Specific internal energy [J/kg] | 
redeclare function extends specificInternalEnergy "Return specific internal energy" extends Modelica.Icons.Function; algorithm u := h_pTX(state.p,state.T,state.X) - gasConstant(state)*state.T; end specificInternalEnergy;
Buildings.Media.PerfectGases.MoistAir.specificGibbsEnergy
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnergy | g | Specific Gibbs energy [J/kg] | 
redeclare function extends specificGibbsEnergy extends Modelica.Icons.Function; algorithm g := h_pTX(state.p,state.T,state.X) - state.T*specificEntropy(state); end specificGibbsEnergy;
Buildings.Media.PerfectGases.MoistAir.specificHelmholtzEnergy
| Type | Name | Default | Description | 
|---|---|---|---|
| ThermodynamicState | state | 
| Type | Name | Description | 
|---|---|---|
| SpecificEnergy | f | Specific Helmholtz energy [J/kg] | 
redeclare function extends specificHelmholtzEnergy extends Modelica.Icons.Function; algorithm f := h_pTX(state.p,state.T,state.X) - gasConstant(state)*state.T - state.T*specificEntropy(state); end specificHelmholtzEnergy;
| Type | Name | Default | Description | 
|---|---|---|---|
| AbsolutePressure | p | Pressure [Pa] | |
| SpecificEnthalpy | h | specific enthalpy [J/kg] | |
| MassFraction | X[:] | mass fractions of composition [kg/kg] | 
| Type | Name | Description | 
|---|---|---|
| Temperature | T | temperature [K] | 
function T_phX 
  "Compute temperature from specific enthalpy and mass fraction" 
  input AbsolutePressure p "Pressure";
  input SpecificEnthalpy h "specific enthalpy";
  input MassFraction[:] X "mass fractions of composition";
  output Temperature T "temperature";
protected 
package Internal 
    "Solve h(data,T) for T with given h (use only indirectly via temperature_phX)" 
  extends Modelica.Media.Common.OneNonLinearEquation;
  redeclare record extends f_nonlinear_Data 
      "Data to be passed to non-linear function" 
    extends Buildings.Media.PerfectGases.Common.DataRecord;
  end f_nonlinear_Data;
    
  redeclare function extends f_nonlinear 
  algorithm 
      y := h_pTX(p,x,X);
  end f_nonlinear;
    
  // Dummy definition has to be added for current Dymola
  redeclare function extends solve 
  end solve;
end Internal;
  
algorithm 
 /* The function call below has been changed from 
      Internal.solve(h, 200, 6000, p, X[1:nXi], steam);
    to  
      Internal.solve(h, 200, 6000, p, X, steam);
    The reason is that when running the problem
       Buildings.Media.PerfectGases.Examples.MoistAirComparison
    then an assertion is triggered because the vector X had the wrong
    dimension. The above example verifies that T(h(T)) = 0.
 */
  T := Internal.solve(h, 200, 6000, p, X, steam);
end T_phX;