#NAME? error in Excel for VBA Function

rize1159

Board Regular
Joined
Jan 8, 2011
Messages
51
I have following function in excel sheet. But when Icopy code to a new sheet, I get #NAME? error. New excel dont recognizes the defined functions and give #Name error.

VBA Code:
' PsychroLib (version 2.3.0) ([URL='https://github.com/psychrometrics/psychrolib']psychrometrics/psychrolib[/URL])
' Copyright (c) 2018 D. Thevenard and D. Meyer for the current library implementation
' Copyright (c) 2017 ASHRAE Handbook — Fundamentals for ASHRAE equations and coefficients
' Licensed under the MIT License.
'
' psychrolib.vba
'
' Contains functions for calculating thermodynamic properties of gas-vapor mixtures
' and standard atmosphere suitable for most engineering, physical and meteorological
' applications.
'
' Most of the functions are an implementation of the formulae found in the
' 2017 ASHRAE Handbook - Fundamentals, in both International System (SI),
' and Imperial (IP) units. Please refer to the information included in
' each function for their respective reference.
'
' Example
'     ' Set the unit system, for example to SI (can be either ' SI'  or ' IP' )
'     ' by uncommenting the following line in the psychrolib module
'     Const PSYCHROLIB_UNITS = UnitSystem.SI
'
'     ' Calculate the dew point temperature for a dry bulb temperature of 25 C and a relative humidity of 80%
'     TDewPoint = GetTDewPointFromRelHum(25.0, 0.80)
'     Debug.Print(TDewPoint)
'     21.309397163661785
'
' Copyright
'     - For the current library implementation
'         Copyright (c) 2018 D. Thevenard and D. Meyer.
'     - For equations and coefficients published ASHRAE Handbook — Fundamentals, Chapter 1
'         Copyright (c) 2017 ASHRAE Handbook — Fundamentals ([URL='https://www.ashrae.org']Home | ashrae.org[/URL])
'
' License
'     MIT ([URL]https://github.com/psychrometrics/psychrolib/LICENSE.txt[/URL])
'
' Note from the Authors
'     We have made every effort to ensure that the code is adequate, however, we make no
'     representation with respect to its accuracy. Use at your own risk. Should you notice
'     an error, or if you have a suggestion, please notify us through GitHub at
'     [URL='https://github.com/psychrometrics/psychrolib/issues']psychrometrics/psychrolib[/URL].
'

Option Explicit


'******************************************************************************************************
' IMPORTANT: Manually uncomment the system of units to use
'******************************************************************************************************

'Enumeration to define systems of units
Enum UnitSystem
  IP = 1
  SI = 2
End Enum

' Uncomment one of these two lines to define the system of units ("IP" or "SI")
'Const PSYCHROLIB_UNITS = UnitSystem.IP
Const PSYCHROLIB_UNITS = UnitSystem.SI


'******************************************************************************************************
' Global constants
'******************************************************************************************************

Private Const ZERO_FAHRENHEIT_AS_RANKINE = 459.67   ' Zero degree Fahrenheit (°F) expressed as degree Rankine (°R).
                                                    'Reference: ASHRAE Handbook - Fundamentals (2017) ch. 39.

Private Const ZERO_CELSIUS_AS_KELVIN = 273.15       ' Zero degree Celsius (°C) expressed as Kelvin (K).
                                                    ' Reference: ASHRAE Handbook - Fundamentals (2017) ch. 39.

Private Const R_DA_IP = 53.35                 ' Universal gas constant for dry air (IP version) in ft lbf/lb_DryAir/R.

Private Const R_DA_SI = 287.042               ' Universal gas constant for dry air (SI version) in J/kg_DryAir/K.

Private Const MAX_ITER_COUNT = 100            ' Maximum number of iterations before exiting while loops.

Private Const MIN_HUM_RATIO = 0.0000001       ' Minimum acceptable humidity ratio used/returned by any functions.
                                              ' Any value above 0 or below the MIN_HUM_RATIO will be reset to this value.

Private Const FREEZING_POINT_WATER_IP = 32#   ' Freezing point of water, in °F

Private Const FREEZING_POINT_WATER_SI = 0#    ' Freezing point of water, in °C

Private Const TRIPLE_POINT_WATER_IP = 32.018  ' Triple point of water, in °F

Private Const TRIPLE_POINT_WATER_SI = 0.01    ' Triple point of water, in °C

'******************************************************************************************************
' Helper functions
'******************************************************************************************************

Function GetUnitSystem() As UnitSystem
'
' This function returns the system of units currently in use (SI or IP).
'
' Args:
'        none
'
' Returns:
'        The system of units currently in use ('SI' or 'IP')
'
' Note:
'
'        If you get an error here, it's because you have not uncommented one of the two lines
'        defining PSYCHROLIB_UNITS (see Global Constants section)
'
    GetUnitSystem = PSYCHROLIB_UNITS

End Function

Private Function isIP() As Variant
'
' This function checks whether the system of units currently in use is IP or SI.
'
' Args:
'         none
'
' Returns:
'         True if IP, False if SI, and raises error if undefined
'
  If (PSYCHROLIB_UNITS = UnitSystem.IP) Then
    isIP = True
  ElseIf (PSYCHROLIB_UNITS = UnitSystem.SI) Then
    isIP = False
  Else
    MsgBox ("The system of units has not been defined.")
    isIP = CVErr(xlErrNA)
  End If

End Function

Private Function GetTol() As Variant
'
' This function returns the tolerance on temperatures used for iterative solving.
' The value is physically the same in IP or SI.
'
' Args:
'         none
'
' Returns:
'         Tolerance on temperatures
'
  If (PSYCHROLIB_UNITS = UnitSystem.IP) Then
    GetTol = 0.001 * 9 / 5
  Else
    GetTol = 0.001
  End If
End Function

Private Sub MyMsgBox(ByVal ErrMsg As String)
'
' Error message output
' Override this function with your own if needed, or comment its code out if you don't want to see the messages
'
' Message disabled by default
'  MsgBox (ErrMsg)

End Sub

Private Function Min(ByVal Num1 As Variant, ByVal Num2 As Variant) As Variant
'
' Min function to return minimum of two numbers
'
  If (Num1 <= Num2) Then
    Min = Num1
  Else
    Min = Num2
  End If

End Function

Private Function Max(ByVal Num1 As Variant, ByVal Num2 As Variant) As Variant
'
' Max function to return maximum of two numbers
'
  If (Num1 >= Num2) Then
    Max = Num1
  Else
    Max = Num2
  End If

End Function


'*****************************************************************************
' Conversions between temperature units
'*****************************************************************************

Function GetTRankineFromTFahrenheit(ByVal T_Fahrenheit As Variant) As Variant
'
' Utility function to convert temperature to degree Rankine (°R)
' given temperature in degree Fahrenheit (°F).
'
'Args:
'        T_Fahrenheit: Temperature in degree Fahrenheit (°F)
'
'Returns:
'        Temperature in degree Rankine (°R)
'
'Reference:
'        Reference: ASHRAE Handbook - Fundamentals (2017) ch. 1 section 3
'
'Notes:
'        Exact conversion.
'
  On Error GoTo ErrHandler

  GetTRankineFromTFahrenheit = (T_Fahrenheit + ZERO_FAHRENHEIT_AS_RANKINE)
  Exit Function

ErrHandler:
  GetTRankineFromTFahrenheit = CVErr(xlErrNA)

End Function

Function GetTFahrenheitFromTRankine(ByVal T_Rankine As Variant) As Variant
'
' Utility function to convert temperature to degree Fahrenheit (°F)
' given temperature in degree Rankine (°R).
'
'Args:
'        TRankine: Temperature in degree Rankine (°R)
'
'Returns:
'        Temperature in degree Fahrenheit (°F)
'
'Reference:
'        Reference: ASHRAE Handbook - Fundamentals (2017) ch. 1 section 3
'
'Notes:
'        Exact conversion.
'
  On Error GoTo ErrHandler

  GetTFahrenheitFromTRankine = (T_Rankine - ZERO_FAHRENHEIT_AS_RANKINE)
  Exit Function

ErrHandler:
  GetTFahrenheitFromTRankine = CVErr(xlErrNA)

End Function

Function GetTKelvinFromTCelsius(ByVal T_Celsius As Variant) As Variant
'
' Utility function to convert temperature to Kelvin (K)
' given temperature in degree Celsius (°C).
'
'Args:
'        TCelsius: Temperature in degree Celsius (°C)
'
'Returns:
'        Temperature in Kelvin (K)
'
'Reference:
'        Reference: ASHRAE Handbook - Fundamentals (2017) ch. 1 section 3
'
'Notes:
'        Exact conversion.
'
  On Error GoTo ErrHandler

  GetTKelvinFromTCelsius = (T_Celsius + ZERO_CELSIUS_AS_KELVIN)
  Exit Function

ErrHandler:
  GetTKelvinFromTCelsius = CVErr(xlErrNA)

End Function

Function GetTCelsiusFromTKelvin(ByVal T_Kelvin As Variant) As Variant
'
' Utility function to convert temperature to degree Celsius (°C)
' given temperature in Kelvin (K).
'
'Args:
'        TKelvin: Temperature in Kelvin (K)
'
'Returns:
'        Temperature in degree Celsius (°C)
'
'Reference:
'        Reference: ASHRAE Handbook - Fundamentals (2017) ch. 1 section 3
'
'Notes:
'        Exact conversion.
'
  On Error GoTo ErrHandler

  GetTCelsiusFromTKelvin = (T_Kelvin - ZERO_CELSIUS_AS_KELVIN)
  Exit Function

ErrHandler:
  GetTCelsiusFromTKelvin = CVErr(xlErrNA)

End Function


'******************************************************************************************************
' Conversions between dew point, wet bulb, and relative humidity
'******************************************************************************************************

Function GetTWetBulbFromTDewPoint(ByVal TDryBulb As Variant, ByVal TDewPoint As Variant, ByVal Pressure As Variant) As Variant
'
' Return wet-bulb temperature given dry-bulb temperature, dew-point temperature, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        TDewPoint : Dew-point temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Wet-bulb temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  Dim HumRatio As Variant

  On Error GoTo ErrHandler

  If TDewPoint > TDryBulb Then
    MyMsgBox ("Dew point temperature is above dry bulb temperature")
    GoTo ErrHandler
  End If

  HumRatio = GetHumRatioFromTDewPoint(TDewPoint, Pressure)
  GetTWetBulbFromTDewPoint = GetTWetBulbFromHumRatio(TDryBulb, HumRatio, Pressure)
  Exit Function

ErrHandler:
  GetTWetBulbFromTDewPoint = CVErr(xlErrNA)

End Function

Function GetTWetBulbFromRelHum(ByVal TDryBulb As Variant, ByVal RelHum As Variant, ByVal Pressure As Variant) As Variant
'
' Return wet-bulb temperature given dry-bulb temperature, relative humidity, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        RelHum : Relative humidity in range [0, 1]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Wet-bulb temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  Dim HumRatio As Variant

  On Error GoTo ErrHandler

  If (RelHum < 0 Or RelHum > 1) Then
    MyMsgBox ("Relative humidity is outside range [0,1]")
    GoTo ErrHandler
  End If

  HumRatio = GetHumRatioFromRelHum(TDryBulb, RelHum, Pressure)
  GetTWetBulbFromRelHum = GetTWetBulbFromHumRatio(TDryBulb, HumRatio, Pressure)
  Exit Function

ErrHandler:
  GetTWetBulbFromRelHum = CVErr(xlErrNA)

End Function

Function GetRelHumFromTDewPoint(ByVal TDryBulb As Variant, ByVal TDewPoint As Variant) As Variant
'
' Return relative humidity given dry-bulb temperature and dew-point temperature.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        TDewPoint : Dew-point temperature in °F [IP] or °C [SI]
'
' Returns:
'        Relative humidity in range [0, 1]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 22
'
  Dim VapPres As Variant
  Dim SatVapPres As Variant

  On Error GoTo ErrHandler

  If (TDewPoint > TDryBulb) Then
    MyMsgBox ("Dew point temperature is above dry bulb temperature")
    GoTo ErrHandler
  End If

  VapPres = GetSatVapPres(TDewPoint)
  SatVapPres = GetSatVapPres(TDryBulb)
  GetRelHumFromTDewPoint = VapPres / SatVapPres
  Exit Function

ErrHandler:
  GetRelHumFromTDewPoint = CVErr(xlErrNA)

End Function

Function GetRelHumFromTWetBulb(ByVal TDryBulb As Variant, ByVal TWetBulb As Variant, ByVal Pressure As Variant) As Variant
'
' Return relative humidity given dry-bulb temperature, wet bulb temperature and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        TWetBulb : Wet-bulb temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Relative humidity in range [0, 1]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  Dim HumRatio As Variant

  On Error GoTo ErrHandler

  If TWetBulb > TDryBulb Then
    MyMsgBox ("Wet bulb temperature is above dry bulb temperature")
    GoTo ErrHandler
  End If

  HumRatio = GetHumRatioFromTWetBulb(TDryBulb, TWetBulb, Pressure)
  GetRelHumFromTWetBulb = GetRelHumFromHumRatio(TDryBulb, HumRatio, Pressure)
  Exit Function

ErrHandler:
  GetRelHumFromTWetBulb = CVErr(xlErrNA)

End Function

Function GetTDewPointFromRelHum(ByVal TDryBulb As Variant, ByVal RelHum As Variant) As Variant
'
' Return dew-point temperature given dry-bulb temperature and relative humidity.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        RelHum: Relative humidity in range [0, 1]
'
' Returns:
'        Dew-point temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'

  Dim VapPres As Variant

  On Error GoTo ErrHandler

  If RelHum < 0 Or RelHum > 1 Then
    MyMsgBox ("Relative humidity is outside range [0, 1]")
    GoTo ErrHandler
  End If

  VapPres = GetVapPresFromRelHum(TDryBulb, RelHum)
  GetTDewPointFromRelHum = GetTDewPointFromVapPres(TDryBulb, VapPres)
  Exit Function

ErrHandler:
  GetTDewPointFromRelHum = CVErr(xlErrNA)

End Function

Function GetTDewPointFromTWetBulb(ByVal TDryBulb As Variant, ByVal TWetBulb As Variant, ByVal Pressure As Variant) As Variant
'
' Return dew-point temperature given dry-bulb temperature, wet-bulb temperature, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        TWetBulb : Wet-bulb temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Dew-point temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  Dim HumRatio As Variant

  On Error GoTo ErrHandler

  If TWetBulb > TDryBulb Then
    MyMsgBox ("Wet bulb temperature is above dry bulb temperature")
    GoTo ErrHandler
  End If

  HumRatio = GetHumRatioFromTWetBulb(TDryBulb, TWetBulb, Pressure)
  GetTDewPointFromTWetBulb = GetTDewPointFromHumRatio(TDryBulb, HumRatio, Pressure)
  Exit Function

ErrHandler:
  GetTDewPointFromTWetBulb = CVErr(xlErrNA)

End Function


'******************************************************************************************************
'  Conversions between dew point, or relative humidity and vapor pressure
'******************************************************************************************************

Function GetVapPresFromRelHum(ByVal TDryBulb As Variant, ByVal RelHum As Variant) As Variant
'
' Return partial pressure of water vapor as a function of relative humidity and temperature.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        RelHum : Relative humidity in range [0, 1]
'
' Returns:
'        Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 12, 22
'
  On Error GoTo ErrHandler

  If RelHum < 0 Or RelHum > 1 Then
    MyMsgBox ("Relative humidity is outside range [0, 1]")
    GoTo ErrHandler
  End If

  GetVapPresFromRelHum = RelHum * GetSatVapPres(TDryBulb)
  Exit Function

ErrHandler:
  GetVapPresFromRelHum = CVErr(xlErrNA)

End Function

Function GetRelHumFromVapPres(ByVal TDryBulb As Variant, ByVal VapPres As Variant) As Variant
' Return relative humidity given dry-bulb temperature and vapor pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        VapPres: Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'
' Returns:
'        Relative humidity in range [0, 1]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 12, 22
'
  On Error GoTo ErrHandler

  If (VapPres < 0) Then
    MyMsgBox ("Partial pressure of water vapor in moist air is negative")
    GoTo ErrHandler
  End If

  GetRelHumFromVapPres = VapPres / GetSatVapPres(TDryBulb)
  Exit Function

ErrHandler:
  GetRelHumFromVapPres = CVErr(xlErrNA)

End Function


Private Function dLnPws_(TDryBulb As Variant) As Variant
'
'    Helper function returning the derivative of the natural log of the saturation vapor pressure
'    as a function of dry-bulb temperature.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'
' Returns:
'        Derivative of natural log of vapor pressure of saturated air in Psi [IP] or Pa [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1  eqn 5 & 6
'
  Dim T As Variant
  If (isIP()) Then
    T = GetTRankineFromTFahrenheit(TDryBulb)
    If (TDryBulb <= TRIPLE_POINT_WATER_IP) Then
      dLnPws_ = 10214.165 / T ^ 2 - 0.0053765794 + 2 * 0.00000019202377 * T _
             + 3 * 3.5575832E-10 * T ^ 2 - 4 * 9.0344688E-14 * T ^ 3 + 4.1635019 / T
    Else
      dLnPws_ = 10440.397 / T ^ 2 - 0.027022355 + 2 * 0.00001289036 * T _
             - 3 * 2.4780681E-09 * T ^ 2 + 6.5459673 / T
    End If
  Else
    T = GetTKelvinFromTCelsius(TDryBulb)
    If (TDryBulb <= TRIPLE_POINT_WATER_SI) Then
      dLnPws_ = 5674.5359 / T ^ 2 - 0.009677843 + 2 * 0.00000062215701 * T _
             + 3 * 2.0747825E-09 * T ^ 2 - 4 * 9.484024E-13 * T ^ 3 + 4.1635019 / T
    Else
      dLnPws_ = 5800.2206 / T ^ 2 - 0.048640239 + 2 * 0.000041764768 * T _
             - 3 * 0.000000014452093 * T ^ 2 + 6.5459673 / T
    End If
  End If
End Function

Function GetTDewPointFromVapPres(ByVal TDryBulb As Variant, ByVal VapPres As Variant) As Variant
'
' Return dew-point temperature given dry-bulb temperature and vapor pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        VapPres: Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'
' Returns:
'        Dew-point temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn. 5 and 6
'
' Notes:
'        The dew point temperature is solved by inverting the equation giving water vapor pressure
'        at saturation from temperature rather than using the regressions provided
'        by ASHRAE (eqn. 37 and 38) which are much less accurate and have a
'        narrower range of validity.
'        The Newton-Raphson (NR) method is used on the logarithm of water vapour
'        pressure as a function of temperature, which is a very smooth function
'        Convergence is usually achieved in 3 to 5 iterations.
'        TDryBulb is not really needed here, just used for convenience.
'
  Dim BOUNDS(2) As Variant
  Dim PSYCHROLIB_TOLERANCE As Variant

  If (isIP()) Then
    BOUNDS(1) = -148#
    BOUNDS(2) = 392#
  Else
    BOUNDS(1) = -100#
    BOUNDS(2) = 200#
  End If

  On Error GoTo ErrHandler

  If ((VapPres < GetSatVapPres(BOUNDS(1))) Or (VapPres > GetSatVapPres(BOUNDS(2)))) Then
    MyMsgBox ("Partial pressure of water vapor is outside range of validity of equations")
    GoTo ErrHandler
  End If

  PSYCHROLIB_TOLERANCE = GetTol()

  Dim TDewPoint As Variant
  Dim lnVP As Variant
  Dim d_lnVP As Variant
  Dim TDewPoint_iter As Variant
  Dim lnVP_iter
  Dim index As Variant
  index = 1

  ' We use NR to approximate the solution.
  ' First guess
  TDewPoint = TDryBulb        ' Calculated value of dew point temperatures, solved for iteratively
  lnVP = Log(VapPres)         ' Partial pressure of water vapor in moist air

  ' Iteration
  Do
    TDewPoint_iter = TDewPoint   ' Value of Tdp used in NR calculation
    lnVP_iter = Log(GetSatVapPres(TDewPoint_iter))

    ' Derivative of function, calculated analytically
    d_lnVP = dLnPws_(TDewPoint_iter)

    ' New estimate, bounded by domain of validity of eqn. 5 and 6 and by the freezing point
    TDewPoint = TDewPoint_iter - (lnVP_iter - lnVP) / d_lnVP
    TDewPoint = Max(TDewPoint, BOUNDS(1))
    TDewPoint = Min(TDewPoint, BOUNDS(2))

    If (index > MAX_ITER_COUNT) Then
      GoTo ErrHandler
    End If

    index = index + 1

  Loop While (Abs(TDewPoint - TDewPoint_iter) > PSYCHROLIB_TOLERANCE)

  TDewPoint = Min(TDewPoint, TDryBulb)
  GetTDewPointFromVapPres = TDewPoint
  Exit Function

ErrHandler:
  GetTDewPointFromVapPres = CVErr(xlErrNA)

End Function

Function GetVapPresFromTDewPoint(ByVal TDewPoint As Variant) As Variant
'
' Return vapor pressure given dew point temperature.
'
' Args:
'        TDewPoint : Dew-point temperature in °F [IP] or °C [SI]
'
' Returns:
'        Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 36
'
  On Error GoTo ErrHandler
  GetVapPresFromTDewPoint = GetSatVapPres(TDewPoint)
  Exit Function

ErrHandler:
  GetVapPresFromTDewPoint = CVErr(xlErrNA)

End Function


'******************************************************************************************************
'  Conversions from wet-bulb temperature, dew-point temperature, or relative humidity to humidity ratio
'******************************************************************************************************

Function GetTWetBulbFromHumRatio(ByVal TDryBulb As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return wet-bulb temperature given dry-bulb temperature, humidity ratio, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Wet-bulb temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 33 and 35 solved for Tstar
'

  ' Declarations
  Dim Wstar As Variant
  Dim TDewPoint As Variant, TWetBulb As Variant, TWetBulbSup As Variant, TWetBulbInf As Variant
  Dim Tol As Variant, BoundedHumRatio As Variant, index As Variant

  On Error GoTo ErrHandler

  If HumRatio < 0 Then
    MyMsgBox ("Humidity ratio cannot be negative")
    GoTo ErrHandler
  End If
  BoundedHumRatio = Max(HumRatio, MIN_HUM_RATIO)

  TDewPoint = GetTDewPointFromHumRatio(TDryBulb, BoundedHumRatio, Pressure)

  ' Initial guesses
  TWetBulbSup = TDryBulb
  TWetBulbInf = TDewPoint
  TWetBulb = (TWetBulbInf + TWetBulbSup) / 2

  ' Bisection loop
  Tol = GetTol()
  index = 0
  While ((TWetBulbSup - TWetBulbInf) > Tol)

    ' Compute humidity ratio at temperature Tstar
    Wstar = GetHumRatioFromTWetBulb(TDryBulb, TWetBulb, Pressure)

    ' Get new bounds
    If (Wstar > BoundedHumRatio) Then
      TWetBulbSup = TWetBulb
    Else
      TWetBulbInf = TWetBulb
    End If

    ' New guess of wet bulb temperature
    TWetBulb = (TWetBulbSup + TWetBulbInf) / 2

    If (index > MAX_ITER_COUNT) Then
      GoTo ErrHandler
    End If

    index = index + 1
  Wend

  GetTWetBulbFromHumRatio = TWetBulb
  Exit Function

ErrHandler:
  GetTWetBulbFromHumRatio = CVErr(xlErrNA)

End Function

Function GetHumRatioFromTWetBulb(ByVal TDryBulb As Variant, ByVal TWetBulb As Variant, ByVal Pressure As Variant) As Variant
'
' Return humidity ratio given dry-bulb temperature, wet-bulb temperature, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        TWetBulb : Wet-bulb temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 33 and 35

  Dim Wsstar As Variant, HumRatio As Variant
  Wsstar = GetSatHumRatio(TWetBulb, Pressure)

  On Error GoTo ErrHandler

  If TWetBulb > TDryBulb Then
    MyMsgBox ("Wet bulb temperature is above dry bulb temperature")
    GoTo ErrHandler
  End If

  If isIP() Then
    If (TWetBulb >= FREEZING_POINT_WATER_IP) Then
      HumRatio = ((1093 - 0.556 * TWetBulb) * Wsstar - 0.24 * (TDryBulb - TWetBulb)) / (1093 + 0.444 * TDryBulb - TWetBulb)
    Else
      HumRatio = ((1220 - 0.04 * TWetBulb) * Wsstar - 0.24 * (TDryBulb - TWetBulb)) / (1220 + 0.444 * TDryBulb - 0.48 * TWetBulb)
    End If
  Else
    If (TWetBulb >= FREEZING_POINT_WATER_SI) Then
      HumRatio = ((2501 - 2.326 * TWetBulb) * Wsstar - 1.006 * (TDryBulb - TWetBulb)) / (2501 + 1.86 * TDryBulb - 4.186 * TWetBulb)
    Else
      HumRatio = ((2830 - 0.24 * TWetBulb) * Wsstar - 1.006 * (TDryBulb - TWetBulb)) / (2830 + 1.86 * TDryBulb - 2.1 * TWetBulb)
    End If
  End If
  ' Validity check.
  GetHumRatioFromTWetBulb = Max(HumRatio, MIN_HUM_RATIO)
  Exit Function

ErrHandler:
  GetHumRatioFromTWetBulb = CVErr(xlErrNA)

End Function

Function GetHumRatioFromRelHum(ByVal TDryBulb As Variant, ByVal RelHum As Variant, ByVal Pressure As Variant) As Variant
'
' Return humidity ratio given dry-bulb temperature, relative humidity, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        RelHum : Relative humidity in range [0, 1]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  Dim VapPres As Variant

  On Error GoTo ErrHandler

  If RelHum < 0 Or RelHum > 1 Then
    MyMsgBox ("Relative humidity is outside range [0, 1]")
    GoTo ErrHandler
  End If

  VapPres = GetVapPresFromRelHum(TDryBulb, RelHum)
  GetHumRatioFromRelHum = GetHumRatioFromVapPres(VapPres, Pressure)
  Exit Function

ErrHandler:
  GetHumRatioFromRelHum = CVErr(xlErrNA)

End Function

Function GetRelHumFromHumRatio(ByVal TDryBulb As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
'    Return relative humidity given dry-bulb temperature, humidity ratio, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Relative humidity in range [0, 1]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  Dim VapPres As Variant

  On Error GoTo ErrHandler

  If HumRatio < 0 Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If

  VapPres = GetVapPresFromHumRatio(HumRatio, Pressure)
  GetRelHumFromHumRatio = GetRelHumFromVapPres(TDryBulb, VapPres)
  Exit Function

ErrHandler:
  GetRelHumFromHumRatio = CVErr(xlErrNA)

End Function


Function GetHumRatioFromTDewPoint(ByVal TDewPoint As Variant, ByVal Pressure As Variant) As Variant
'
' Return humidity ratio given dew-point temperature and pressure.
'
' Args:
'        TDewPoint : Dew-point temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 13
'
  Dim VapPres As Variant

  On Error GoTo ErrHandler

  VapPres = GetSatVapPres(TDewPoint)
  GetHumRatioFromTDewPoint = GetHumRatioFromVapPres(VapPres, Pressure)
  Exit Function

ErrHandler:
  GetHumRatioFromTDewPoint = CVErr(xlErrNA)

End Function

Function GetTDewPointFromHumRatio(ByVal TDryBulb As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return dew-point temperature given dry-bulb temperature, humidity ratio, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Dew-point temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  Dim VapPres As Variant

  On Error GoTo ErrHandler

  If HumRatio < 0 Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If

  VapPres = GetVapPresFromHumRatio(HumRatio, Pressure)
  GetTDewPointFromHumRatio = GetTDewPointFromVapPres(TDryBulb, VapPres)
  Exit Function

ErrHandler:
  GetTDewPointFromHumRatio = CVErr(xlErrNA)
End Function


'******************************************************************************************************
'       Conversions between humidity ratio and vapor pressure
'******************************************************************************************************

Function GetHumRatioFromVapPres(ByVal VapPres As Variant, ByVal Pressure As Variant) As Variant
'
' Return humidity ratio given water vapor pressure and atmospheric pressure.
'
' Args:
'        VapPres : Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 20
'
  Dim HumRatio As Variant

  On Error GoTo ErrHandler

  If VapPres < 0 Then
    MyMsgBox ("Partial pressure of water vapor in moist air is negative")
    GoTo ErrHandler
  End If

  HumRatio = 0.621945 * VapPres / (Pressure - VapPres)
  ' Validity check.
  GetHumRatioFromVapPres = Max(HumRatio, MIN_HUM_RATIO)
  Exit Function

ErrHandler:
  GetHumRatioFromVapPres = CVErr(xlErrNA)

End Function

Function GetVapPresFromHumRatio(ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return vapor pressure given humidity ratio and pressure.
'
' Args:
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 20 solved for pw
'

  Dim VapPres As Variant, BoundedHumRatio As Variant

  On Error GoTo ErrHandler

  If HumRatio < 0 Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If
  BoundedHumRatio = Max(HumRatio, MIN_HUM_RATIO)

  VapPres = Pressure * BoundedHumRatio / (0.621945 + BoundedHumRatio)
  GetVapPresFromHumRatio = VapPres
  Exit Function

ErrHandler:
  GetVapPresFromHumRatio = CVErr(xlErrNA)

End Function


'******************************************************************************************************
'       Conversions between humidity ratio and specific humidity
'******************************************************************************************************

Function GetSpecificHumFromHumRatio(ByVal HumRatio As Variant) As Variant
'
' Return the specific humidity from humidity ratio (aka mixing ratio).
'
' Args:
'     HumRatio : Humidity ratio in lb_H2O lb_Dry_Air?¹ [IP] or kg_H2O kg_Dry_Air?¹ [SI]
'
' Returns:
'     Specific humidity in lb_H2O lb_Air?¹ [IP] or kg_H2O kg_Air?¹ [SI]
'
' Reference:
'     ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 9b
'
'
  Dim SpecificHum As Variant

  On Error GoTo ErrHandler

  If (HumRatio < 0) Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If

  SpecificHum = HumRatio / (1# + HumRatio)
  GetSpecificHumFromHumRatio = SpecificHum
  Exit Function

ErrHandler:
  GetSpecificHumFromHumRatio = CVErr(xlErrNA)

End Function

Function GetHumRatioFromSpecificHum(ByVal SpecificHum As Variant) As Variant
'
' Return the humidity ratio (aka mixing ratio) from specific humidity.
'
' Args:
'     SpecificHum : Specific Humidity in lb_H2O lb_Air?¹ [IP] or kg_H2O kg_Air?¹ [SI]
'
' Returns:
'     Humidity ratio in lb_H2O lb_Dry_Air?¹ [IP] or kg_H2O kg_Dry_Air?¹ [SI]
'
' Reference:
'     ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 9b (solved for humidity ratio)
'
'
  Dim HumRatio As Variant

  On Error GoTo ErrHandler

  If (SpecificHum < 0 Or SpecificHum >= 1) Then
    MyMsgBox ("Specific humidity is outside range [0, 1[")
    GoTo ErrHandler
  End If

    HumRatio = SpecificHum / (1# - SpecificHum)
    GetHumRatioFromSpecificHum = Max(HumRatio, MIN_HUM_RATIO)
  Exit Function

ErrHandler:
  GetHumRatioFromSpecificHum = CVErr(xlErrNA)

End Function


'******************************************************************************************************
' Dry Air Calculations
'******************************************************************************************************

Function GetDryAirEnthalpy(ByVal TDryBulb As Variant) As Variant
'
' Return dry-air enthalpy given dry-bulb temperature.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'
' Returns:
'        Dry air enthalpy in Btu/lb [IP] or J/kg [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 28
'
  On Error GoTo ErrHandler

  If (isIP()) Then
    GetDryAirEnthalpy = 0.24 * TDryBulb
  Else
    GetDryAirEnthalpy = 1006 * TDryBulb
  End If
  Exit Function

ErrHandler:
  GetDryAirEnthalpy = CVErr(xlErrNA)

End Function

Function GetDryAirDensity(ByVal TDryBulb As Variant, ByVal Pressure As Variant) As Variant
'
' Return dry-air density given dry-bulb temperature and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Dry air density in lb/ft³ [IP] or kg/m³ [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
' Notes:
'        Eqn 14 for the perfect gas relationship for dry air.
'        Eqn 1 for the universal gas constant.
'        The factor 144 in IP is for the conversion of Psi = lb/in² to lb/ft².
'
  On Error GoTo ErrHandler

  If (isIP()) Then
    GetDryAirDensity = (144 * Pressure) / R_DA_IP / GetTRankineFromTFahrenheit(TDryBulb)
  Else
    GetDryAirDensity = Pressure / R_DA_SI / GetTKelvinFromTCelsius(TDryBulb)
  End If
  Exit Function

ErrHandler:
  GetDryAirDensity = CVErr(xlErrNA)

End Function

Function GetDryAirVolume(ByVal TDryBulb As Variant, ByVal Pressure As Variant) As Variant
'
' Return dry-air volume given dry-bulb temperature and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Dry air volume in ft³/lb [IP] or in m³/kg [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
' Notes:
'        Eqn 14 for the perfect gas relationship for dry air.
'        Eqn 1 for the universal gas constant.
'        The factor 144 in IP is for the conversion of Psi = lb/in² to lb/ft².
'
  On Error GoTo ErrHandler

  If (isIP()) Then
    GetDryAirVolume = GetTRankineFromTFahrenheit(TDryBulb) * R_DA_IP / (144 * Pressure)
  Else:
    GetDryAirVolume = GetTKelvinFromTCelsius(TDryBulb) * R_DA_SI / Pressure
  End If
  Exit Function

ErrHandler:
  GetDryAirVolume = CVErr(xlErrNA)

End Function

Function GetTDryBulbFromEnthalpyAndHumRatio(ByVal MoistAirEnthalpy As Variant, ByVal HumRatio As Variant) As Variant
'
' Return dry bulb temperature from enthalpy and humidity ratio.
'
'
' Args:
'     MoistAirEnthalpy : Moist air enthalpy in Btu lb?¹ [IP] or J kg?¹
'     HumRatio : Humidity ratio in lb_H2O lb_Air?¹ [IP] or kg_H2O kg_Air?¹ [SI]
'
' Returns:
'     Dry-bulb temperature in °F [IP] or °C [SI]
'
' Reference:
'     ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 30
'
' Notes:
'     Based on the `GetMoistAirEnthalpy` function, rearranged for temperature.
'

  On Error GoTo ErrHandler

  If HumRatio < 0 Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If

  If (isIP()) Then
    GetTDryBulbFromEnthalpyAndHumRatio = (MoistAirEnthalpy - 1061# * HumRatio) / (0.24 + 0.444 * HumRatio)
  Else:
    GetTDryBulbFromEnthalpyAndHumRatio = (MoistAirEnthalpy / 1000# - 2501# * HumRatio) / (1.006 + 1.86 * HumRatio)
  End If
  Exit Function

ErrHandler:
  GetTDryBulbFromEnthalpyAndHumRatio = CVErr(xlErrNA)

End Function

Function GetHumRatioFromEnthalpyAndTDryBulb(ByVal MoistAirEnthalpy As Variant, ByVal TDryBulb As Variant) As Variant
'
' Return humidity ratio from enthalpy and dry-bulb temperature.
'
'
' Args:
'     MoistAirEnthalpy : Moist air enthalpy in Btu lb?¹ [IP] or J kg?¹
'     TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'
' Returns:
'     Humidity ratio in lb_H2O lb_Air?¹ [IP] or kg_H2O kg_Air?¹ [SI]
'
' Reference:
'     ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 30
'
' Notes:
'     Based on the `GetMoistAirEnthalpy` function, rearranged for humidity ratio.
'

  On Error GoTo ErrHandler

  If (isIP()) Then
    GetHumRatioFromEnthalpyAndTDryBulb = (MoistAirEnthalpy - 0.24 * TDryBulb) / (1061# + 0.444 * TDryBulb)
  Else:
    GetHumRatioFromEnthalpyAndTDryBulb = (MoistAirEnthalpy / 1000# - 1.006 * TDryBulb) / (2501# + 1.86 * TDryBulb)
  End If
  Exit Function

ErrHandler:
  GetHumRatioFromEnthalpyAndTDryBulb = CVErr(xlErrNA)

End Function


'******************************************************************************************************
' Saturated Air Calculations
'******************************************************************************************************

Function GetSatVapPres(ByVal TDryBulb As Variant) As Variant
'
' Return saturation vapor pressure given dry-bulb temperature.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'
' Returns:
'        Vapor pressure of saturated air in Psi [IP] or Pa [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1  eqn 5 & 6
'        Important note: the ASHRAE formulae are defined above and below the freezing point but have
'        a discontinuity at the freezing point. This is a small inaccuracy on ASHRAE's part: the formulae
'        should be defined above and below the triple point of water (not the feezing point) in which case
'        the discontinuity vanishes. It is essential to use the triple point of water otherwise function
'        GetTDewPointFromVapPres, which inverts the present function, does not converge properly around
'        the freezing point.
'
  Dim LnPws As Variant, T As Variant

  On Error GoTo ErrHandler

  If (isIP()) Then
    If (TDryBulb < -148 Or TDryBulb > 392) Then
      MyMsgBox ("Dry bulb temperature is outside range [-148, 392] °F")
      GoTo ErrHandler
    End If

    T = GetTRankineFromTFahrenheit(TDryBulb)

    If (TDryBulb <= TRIPLE_POINT_WATER_IP) Then
      LnPws = (-10214.165 / T - 4.8932428 - 0.0053765794 * T + 0.00000019202377 * T ^ 2 _
            + 3.5575832E-10 * T ^ 3 - 9.0344688E-14 * T ^ 4 + 4.1635019 * Log(T))
    Else
      LnPws = -10440.397 / T - 11.29465 - 0.027022355 * T + 0.00001289036 * T ^ 2 _
            - 2.4780681E-09 * T ^ 3 + 6.5459673 * Log(T)
    End If

  Else
    If (TDryBulb < -100 Or TDryBulb > 200) Then
      MyMsgBox ("Dry bulb temperature is outside range [-100, 200] °C")
      GoTo ErrHandler
    End If

    T = GetTKelvinFromTCelsius(TDryBulb)

    If (TDryBulb <= TRIPLE_POINT_WATER_SI) Then
        LnPws = -5674.5359 / T + 6.3925247 - 0.009677843 * T + 0.00000062215701 * T ^ 2 _
              + 2.0747825E-09 * T ^ 3 - 9.484024E-13 * T ^ 4 + 4.1635019 * Log(T)
    Else
        LnPws = -5800.2206 / T + 1.3914993 - 0.048640239 * T + 0.000041764768 * T ^ 2 _
              - 0.000000014452093 * T ^ 3 + 6.5459673 * Log(T)
    End If
  End If

  GetSatVapPres = Exp(LnPws)
  Exit Function

ErrHandler:
  GetSatVapPres = CVErr(xlErrNA)

End Function

Function GetSatHumRatio(ByVal TDryBulb As Variant, ByVal Pressure As Variant) As Variant
'
' Return humidity ratio of saturated air given dry-bulb temperature and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio of saturated air in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 36, solved for W
'
  Dim SatVaporPres As Variant, SatHumRatio As Variant

  On Error GoTo ErrHandler

  SatVaporPres = GetSatVapPres(TDryBulb)
  SatHumRatio = 0.621945 * SatVaporPres / (Pressure - SatVaporPres)
  GetSatHumRatio = Max(SatHumRatio, MIN_HUM_RATIO)
  Exit Function

ErrHandler:
  GetSatHumRatio = CVErr(xlErrNA)

End Function

Function GetSatAirEnthalpy(ByVal TDryBulb As Variant, ByVal Pressure As Variant) As Variant
'
' Return saturated air enthalpy given dry-bulb temperature and pressure.
'
' Args:
'        TDryBulb: Dry-bulb temperature in °F [IP] or °C [SI]
'        Pressure: Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Saturated air enthalpy in Btu/lb [IP] or J/kg [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1
'
  On Error GoTo ErrHandler

  GetSatAirEnthalpy = GetMoistAirEnthalpy(TDryBulb, GetSatHumRatio(TDryBulb, Pressure))
  Exit Function

ErrHandler:
  GetSatAirEnthalpy = CVErr(xlErrNA)

End Function


'******************************************************************************************************
' Moist Air Calculations
'******************************************************************************************************


Function GetVaporPressureDeficit(ByVal TDryBulb As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return Vapor pressure deficit given dry-bulb temperature, humidity ratio, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Vapor pressure deficit in Psi [IP] or Pa [SI]
'
' Reference:
'        Oke (1987) eqn 2.13a
'
  Dim RelHum As Variant

  On Error GoTo ErrHandler

  If HumRatio < 0 Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If

  RelHum = GetRelHumFromHumRatio(TDryBulb, HumRatio, Pressure)
  GetVaporPressureDeficit = GetSatVapPres(TDryBulb) * (1 - RelHum)
  Exit Function

ErrHandler:
  GetVaporPressureDeficit = CVErr(xlErrNA)

End Function

Function GetDegreeOfSaturation(ByVal TDryBulb As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return the degree of saturation (i.e humidity ratio of the air / humidity ratio of the air at saturation
' at the same temperature and pressure) given dry-bulb temperature, humidity ratio, and atmospheric pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Degree of saturation in arbitrary unit
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2009) ch. 1 eqn 12
'
' Notes:
'        This definition is absent from the 2017 Handbook. Using 2009 version instead.
'
  Dim BoundedHumRatio As Variant

  On Error GoTo ErrHandler

  If HumRatio < 0 Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If
  BoundedHumRatio = Max(HumRatio, MIN_HUM_RATIO)

  GetDegreeOfSaturation = BoundedHumRatio / GetSatHumRatio(TDryBulb, Pressure)
  Exit Function

ErrHandler:
  GetDegreeOfSaturation = CVErr(xlErrNA)

End Function

Function GetMoistAirEnthalpy(ByVal TDryBulb As Variant, ByVal HumRatio As Variant) As Variant
'
' Return moist air enthalpy given dry-bulb temperature and humidity ratio.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'
' Returns:
'        Moist air enthalpy in Btu/lb [IP] or J/kg
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 30
'
  Dim BoundedHumRatio As Variant

  On Error GoTo ErrHandler

  If (HumRatio < 0) Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If
  BoundedHumRatio = Max(HumRatio, MIN_HUM_RATIO)

  If (isIP()) Then
    GetMoistAirEnthalpy = 0.24 * TDryBulb + BoundedHumRatio * (1061 + 0.444 * TDryBulb)
  Else
    GetMoistAirEnthalpy = (1.006 * TDryBulb + BoundedHumRatio * (2501 + 1.86 * TDryBulb)) * 1000
  End If
  Exit Function

ErrHandler:
  GetMoistAirEnthalpy = CVErr(xlErrNA)

End Function

Function GetMoistAirVolume(ByVal TDryBulb As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return moist air specific volume given dry-bulb temperature, humidity ratio, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Specific volume of moist air in ft³/lb of dry air [IP] or in m³/kg of dry air [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 26
'
' Notes:
'        In IP units, R_DA_IP / 144 equals 0.370486 which is the coefficient appearing in eqn 26
'        The factor 144 is for the conversion of Psi = lb/in² to lb/ft².
'
  Dim BoundedHumRatio As Variant

  On Error GoTo ErrHandler

  If (HumRatio < 0) Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If
  BoundedHumRatio = Max(HumRatio, MIN_HUM_RATIO)

  If (isIP()) Then
    GetMoistAirVolume = R_DA_IP * GetTRankineFromTFahrenheit(TDryBulb) * (1 + 1.607858 * BoundedHumRatio) / (144 * Pressure)
  Else
    GetMoistAirVolume = R_DA_SI * GetTKelvinFromTCelsius(TDryBulb) * (1 + 1.607858 * BoundedHumRatio) / Pressure
  End If
  Exit Function

ErrHandler:
  GetMoistAirVolume = CVErr(xlErrNA)

End Function

Function GetTDryBulbFromMoistAirVolumeAndHumRatio(ByVal MoistAirVolume As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return dry-bulb temperature given moist air specific volume, humidity ratio, and pressure.
'
' Args:
'        MoistAirVolume: Specific volume of moist air in ft³ lb?¹ of dry air [IP] or in m³ kg?¹ of dry air [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Specific volume of moist air in ft³/lb of dry air [IP] or in m³/kg of dry air [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 26
'
' Notes:
'        In IP units, R_DA_IP / 144 equals 0.370486 which is the coefficient appearing in eqn 26
'        The factor 144 is for the conversion of Psi = lb/in² to lb/ft².
'        Based on the `GetMoistAirVolume` function, rearranged for dry-bulb temperature.
'
  Dim BoundedHumRatio As Variant

  On Error GoTo ErrHandler

  If (HumRatio < 0) Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If
  BoundedHumRatio = Max(HumRatio, MIN_HUM_RATIO)

  If (isIP()) Then
    GetTDryBulbFromMoistAirVolumeAndHumRatio = GetTFahrenheitFromTRankine(MoistAirVolume * (144 * Pressure) / (R_DA_IP * (1 + 1.607858 * BoundedHumRatio)))
  Else
    GetTDryBulbFromMoistAirVolumeAndHumRatio = GetTCelsiusFromTKelvin(MoistAirVolume * Pressure / (R_DA_SI * (1 + 1.607858 * BoundedHumRatio)))
  End If
  Exit Function

ErrHandler:
  GetTDryBulbFromMoistAirVolumeAndHumRatio = CVErr(xlErrNA)

End Function

Function GetMoistAirDensity(ByVal TDryBulb As Variant, ByVal HumRatio As Variant, ByVal Pressure As Variant) As Variant
'
' Return moist air density given humidity ratio, dry bulb temperature, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        HumRatio : Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        MoistAirDensity: Moist air density in lb/ft³ [IP] or kg/m³ [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 11
'
  Dim MoistAirVolume As Variant, BoundedHumRatio As Variant

  On Error GoTo ErrHandler

  If (HumRatio < 0) Then
    MyMsgBox ("Humidity ratio is negative")
    GoTo ErrHandler
  End If
  BoundedHumRatio = Max(HumRatio, MIN_HUM_RATIO)

  MoistAirVolume = GetMoistAirVolume(TDryBulb, BoundedHumRatio, Pressure)
  GetMoistAirDensity = (1 + BoundedHumRatio) / MoistAirVolume
  Exit Function

ErrHandler:
  GetMoistAirDensity = CVErr(xlErrNA)

End Function


'******************************************************************************************************
' Standard atmosphere
'******************************************************************************************************

Function GetStandardAtmPressure(ByVal Altitude As Variant) As Variant
'
' Return standard atmosphere barometric pressure, given the elevation (altitude).
'
' Args:
'        Altitude: Altitude in ft [IP] or m [SI]
'
' Returns:
'        Standard atmosphere barometric pressure in Psi [IP] or Pa [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 3
'
  On Error GoTo ErrHandler

  If (isIP()) Then
    GetStandardAtmPressure = 14.696 * (1 - 0.0000068754 * Altitude) ^ 5.2559
  Else
    GetStandardAtmPressure = 101325 * (1 - 0.0000225577 * Altitude) ^ 5.2559
  End If
  Exit Function

ErrHandler:
  GetStandardAtmPressure = CVErr(xlErrNA)

End Function

Function GetStandardAtmTemperature(ByVal Altitude As Variant) As Variant
'
' Return standard atmosphere temperature, given the elevation (altitude).
'
' Args:
'        Altitude: Altitude in ft
'
' Returns:
'        Standard atmosphere dry-bulb temperature in °F [IP] or °C [SI]
'
' Reference:
'        ASHRAE Handbook - Fundamentals (2017) ch. 1 eqn 4
'
  On Error GoTo ErrHandler

  If (isIP()) Then
    GetStandardAtmTemperature = 59 - 0.0035662 * Altitude
  Else
    GetStandardAtmTemperature = 15 - 0.0065 * Altitude
  End If
  Exit Function

ErrHandler:
  GetStandardAtmTemperature = CVErr(xlErrNA)

End Function

Function GetSeaLevelPressure(ByVal StationPressure As Variant, ByVal Altitude As Variant, ByVal TDryBulb As Variant) As Variant
'
' Return sea level pressure given dry-bulb temperature, altitude above sea level and pressure.
'
' Args:
'        StationPressure : Observed station pressure in Psi [IP] or Pa [SI]
'        Altitude: Altitude in ft [IP] or m [SI]
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'
' Returns:
'        Sea level barometric pressure in Psi [IP] or Pa [SI]
'
' Reference:
'        Hess SL, Introduction to theoretical meteorology, Holt Rinehart and Winston, NY 1959,
'        ch. 6.5; Stull RB, Meteorology for scientists and engineers, 2nd edition,
'        Brooks/Cole 2000, ch. 1.
'
' Notes:
'        The standard procedure for the US is to use for TDryBulb the average
'        of the current station temperature and the station temperature from 12 hours ago.
'

  ' Calculate average temperature in column of air, assuming a lapse rate
  ' of 6.5 °C/km
  Dim TColumn As Variant
  Dim H As Variant

  On Error GoTo ErrHandler

  If (isIP()) Then
    ' Calculate average temperature in column of air, assuming a lapse rate
    ' of 3.6 °F/1000ft
    TColumn = TDryBulb + 0.0036 * Altitude / 2

    ' Determine the scale height
    H = 53.351 * GetTRankineFromTFahrenheit(TColumn)
  Else
    ' Calculate average temperature in column of air, assuming a lapse rate
    ' of 6.5 °C/km
    TColumn = TDryBulb + 0.0065 * Altitude / 2

    ' Determine the scale height
    H = 287.055 * GetTKelvinFromTCelsius(TColumn) / 9.807
  End If

  ' Calculate the sea level pressure
  GetSeaLevelPressure = StationPressure * Exp(Altitude / H)
  Exit Function

ErrHandler:
  GetSeaLevelPressure = CVErr(xlErrNA)

End Function

Function GetStationPressure(ByVal SeaLevelPressure As Variant, ByVal Altitude As Variant, ByVal TDryBulb As Variant) As Variant
'
' Args:
'        SeaLevelPressure : Sea level barometric pressure in Psi [IP] or Pa [SI]
'        Altitude: Altitude in ft [IP] or m [SI]
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'
' Returns:
'        Station pressure in Psi [IP] or Pa [SI]
'
' Reference:
'        See 'GetSeaLevelPressure'
'
' Notes:
'        This function is just the inverse of 'GetSeaLevelPressure'.
'
  On Error GoTo ErrHandler

  GetStationPressure = SeaLevelPressure / GetSeaLevelPressure(1, Altitude, TDryBulb)
  Exit Function

ErrHandler:
  GetStationPressure = CVErr(xlErrNA)

End Function

'******************************************************************************************************
' Functions to set all psychrometric values
'******************************************************************************************************

Sub CalcPsychrometricsFromTWetBulb(ByVal TDryBulb As Variant, ByVal TWetBulb As Variant, ByVal Pressure As Variant, _
    ByRef HumRatio As Variant, ByRef TDewPoint As Variant, ByRef RelHum As Variant, ByRef VapPres As Variant, _
    ByRef MoistAirEnthalpy As Variant, ByRef MoistAirVolume As Variant, ByRef DegreeOfSaturation As Variant)
'
' Utility function to calculate humidity ratio, dew-point temperature, relative humidity,
' vapour pressure, moist air enthalpy, moist air volume, and degree of saturation of air given
' dry-bulb temperature, wet-bulb temperature, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        TWetBulb : Wet-bulb temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Dew-point temperature in °F [IP] or °C [SI]
'        Relative humidity in range [0, 1]
'        Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'        Moist air enthalpy in Btu/lb [IP] or J/kg [SI]
'        Specific volume of moist air in ft³/lb [IP] or in m³/kg [SI]
'        Degree of saturation [unitless]
'
  HumRatio = GetHumRatioFromTWetBulb(TDryBulb, TWetBulb, Pressure)
  TDewPoint = GetTDewPointFromHumRatio(TDryBulb, HumRatio, Pressure)
  RelHum = GetRelHumFromHumRatio(TDryBulb, HumRatio, Pressure)
  VapPres = GetVapPresFromHumRatio(HumRatio, Pressure)
  MoistAirEnthalpy = GetMoistAirEnthalpy(TDryBulb, HumRatio)
  MoistAirVolume = GetMoistAirVolume(TDryBulb, HumRatio, Pressure)
  DegreeOfSaturation = GetDegreeOfSaturation(TDryBulb, HumRatio, Pressure)

End Sub

Sub CalcPsychrometricsFromTDewPoint(ByVal TDryBulb As Variant, ByVal TDewPoint As Variant, ByVal Pressure As Variant, _
    ByRef HumRatio As Variant, ByRef TWetBulb As Variant, ByRef RelHum As Variant, ByRef VapPres As Variant, _
    ByRef MoistAirEnthalpy As Variant, ByRef MoistAirVolume As Variant, ByRef DegreeOfSaturation As Variant)
'
' Utility function to calculate humidity ratio, wet-bulb temperature, relative humidity,
' vapour pressure, moist air enthalpy, moist air volume, and degree of saturation of air given
' dry-bulb temperature, dew-point temperature, and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        TDewPoint : Dew-point temperature in °F [IP] or °C [SI]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Wet-bulb temperature in °F [IP] or °C [SI]
'        Relative humidity in range [0, 1]
'        Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'        Moist air enthalpy in Btu/lb [IP] or J/kg [SI]
'        Specific volume of moist air in ft³/lb [IP] or in m³/kg [SI]
'        Degree of saturation [unitless]
'
  HumRatio = GetHumRatioFromTDewPoint(TDewPoint, Pressure)
  TWetBulb = GetTWetBulbFromHumRatio(TDryBulb, HumRatio, Pressure)
  RelHum = GetRelHumFromHumRatio(TDryBulb, HumRatio, Pressure)
  VapPres = GetVapPresFromHumRatio(HumRatio, Pressure)
  MoistAirEnthalpy = GetMoistAirEnthalpy(TDryBulb, HumRatio)
  MoistAirVolume = GetMoistAirVolume(TDryBulb, HumRatio, Pressure)
  DegreeOfSaturation = GetDegreeOfSaturation(TDryBulb, HumRatio, Pressure)

End Sub

Sub CalcPsychrometricsFromRelHum(ByVal TDryBulb As Variant, ByVal RelHum As Variant, ByVal Pressure As Variant, _
    ByRef HumRatio As Variant, ByRef TWetBulb As Variant, ByRef TDewPoint As Variant, ByRef VapPres As Variant, _
    ByRef MoistAirEnthalpy As Variant, ByRef MoistAirVolume As Variant, ByRef DegreeOfSaturation As Variant)
'
' Utility function to calculate humidity ratio, wet-bulb temperature, dew-point temperature,
' vapour pressure, moist air enthalpy, moist air volume, and degree of saturation of air given
' dry-bulb temperature, relative humidity and pressure.
'
' Args:
'        TDryBulb : Dry-bulb temperature in °F [IP] or °C [SI]
'        RelHum : Relative humidity in range [0, 1]
'        Pressure : Atmospheric pressure in Psi [IP] or Pa [SI]
'
' Returns:
'        Humidity ratio in lb_H2O/lb_Air [IP] or kg_H2O/kg_Air [SI]
'        Wet-bulb temperature in °F [IP] or °C [SI]
'        Dew-point temperature in °F [IP] or °C [SI].
'        Partial pressure of water vapor in moist air in Psi [IP] or Pa [SI]
'        Moist air enthalpy in Btu/lb [IP] or J/kg [SI]
'        Specific volume of moist air in ft³/lb [IP] or in m³/kg [SI]
'        Degree of saturation [unitless]
'
  HumRatio = GetHumRatioFromRelHum(TDryBulb, RelHum, Pressure)
  TWetBulb = GetTWetBulbFromHumRatio(TDryBulb, HumRatio, Pressure)
  TDewPoint = GetTDewPointFromHumRatio(TDryBulb, HumRatio, Pressure)
  VapPres = GetVapPresFromHumRatio(HumRatio, Pressure)
  MoistAirEnthalpy = GetMoistAirEnthalpy(TDryBulb, HumRatio)
  MoistAirVolume = GetMoistAirVolume(TDryBulb, HumRatio, Pressure)
  DegreeOfSaturation = GetDegreeOfSaturation(TDryBulb, HumRatio, Pressure)

End Sub
 
Last edited by a moderator:

Excel Facts

Waterfall charts in Excel?
Office 365 customers have access to Waterfall charts since late 2016. They were added to Excel 2019.

Forum statistics

Threads
1,224,819
Messages
6,181,153
Members
453,021
Latest member
Justyna P

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top