Source code for thermosteam.properties.heat_capacity

# -*- coding: utf-8 -*-
"""
Data and methods for calculating the heat capacity of a chemical.

References
----------
.. [1] Lastovka, Vaclav, and John M. Shaw. "Predictive Correlations for
       Ideal Gas Heat Capacities of Pure Hydrocarbons and Petroleum Fractions."
       Fluid Phase Equilibria 356 (October 25, 2013): 338-370.
       doi:10.1016/j.fluid.2013.07.023.
.. [2] Kabo, G. J., and G. N. Roganov. Thermodynamics of Organic Compounds
       in the Gas State, Volume II: V. 2. College Station, Tex: CRC Press, 1994.
.. [3] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
       New York: McGraw-Hill Professional, 2000.
.. [4] Gesellschaft, V. D. I., ed. VDI Heat Atlas. 2nd edition.
       Berlin; New York:: Springer, 2010.
.. [5] J.S. Rowlinson, Liquids and Liquid Mixtures, 2nd Ed.,
       Butterworth, London (1969).
.. [6] Dadgostar, Nafiseh, and John M. Shaw. "A Predictive Correlation for
       the Constant-Pressure Specific Heat Capacity of Pure and Ill-Defined
       Liquid Hydrocarbons." Fluid Phase Equilibria 313 (January 15, 2012):
       211-226. doi:10.1016/j.fluid.2011.09.015.
.. [7] Zabransky, M., V. Ruzicka Jr, V. Majer, and Eugene S. Domalski.
       Heat Capacity of Liquids: Critical Review and Recommended Values.
       2 Volume Set. Washington, D.C.: Amer Inst of Physics, 1996.
.. [8] Laštovka, Václav, Michal Fulem, Mildred Becerra, and John M. Shaw.
       "A Similarity Variable for Estimating the Heat Capacity of Solid Organic
       Compounds: Part II. Application: Heat Capacity Calculation for
       Ill-Defined Organic Solids." Fluid Phase Equilibria 268, no. 1-2
       (June 25, 2008): 134-41. doi:10.1016/j.fluid.2008.03.018.
.. [9] Green, Don, and Robert Perry.
       Perry's Chemical Engineers' Handbook,
       Eighth Edition. McGraw-Hill Professional, 2007.

"""
import os
from io import open
from math import log, exp
from cmath import log as clog, exp as cexp
import numpy as np
from ..base import InterpolatedTDependentModel, thermo_model, TDependentHandleBuilder, \
                   PhaseTHandleBuilder, S, H, Cn
from .utils import to_nums, CASDataReader
from .._constants import R, calorie
from ..functional import polylog2
from .miscdata import VDI_saturation_dict, VDI_tabular_data
# from .electrochem import (LaliberteHeatCapacityModel,
#                           _Laliberte_Heat_Capacity_ParametersDict)
# from .coolprop import has_CoolProp, coolprop_dict, CoolProp_T_dependent_property,\
#                       coolprop_fluids, PropsSI

__all__ = ('heat_capacity_handle',

           'Lastovka_Shaw',
           'Lastovka_Shaw_Integral',
           'Lastovka_Shaw_Integral_Over_T',

           'TRCCn', 
           'TRCCn_Integral', 
           'TRCCn_Integral_Over_T',

           'Poling',
           'Poling_Integral', 
           'Poling_Integral_Over_T',
           
           'Rowlinson_Poling', 'Rowlinson_Bondi',

           'Zabransky_Cubic', 
           'Zabransky_Cubic_Integral', 
           'Zabransky_Cubic_Over_T_Integral',

           'Zabransky_Quasi_Polynomial', 
           'Zabransky_Quasi_Polynomial_Integral', 
           'Zabransky_Quasi_Polynomial_Over_T_Integral',

           'Dadgostar_Shaw', 
           'Dadgostar_Shaw_Integral', 
           'Dadgostar_Shaw_Over_T_Integral',

           'Lastovka_Solid', 
           'Lastovka_Solid_Integral', 
           'Lastovka_Solid_Over_T_Integral',
           
           'Perry_151', 
           'Perry_151_Integral', 
           'Perry_151_Over_T_Integral'
)


# %% Utilities

def CnHS(FCn, FH, FS, data):
    fCn = FCn.from_args(data)
    return {'evaluate': fCn,
            'integrate_by_T': FH.from_other(fCn),
            'integrate_by_T_over_T': FS.from_other(fCn)}

def CnHSModel(FCn, FH, FS, data=None, Tmin=None, Tmax=None, name=None):
    funcs = CnHS(FCn, FH, FS, data)
    return thermo_model(Tmin=Tmin, Tmax=Tmax, name=name, **funcs)

class ZabranskyModelBuilder:
    __slots__ = ('name', 'casdata', 'funcs', 'many')
    
    def __init__(self, name, casdata, funcs, many=False):
        self.name = name
        self.casdata = casdata
        self.funcs = funcs
        self.many = many
    
    def add_model(self, CAS, models):
        if CAS in self.casdata:
            if self.many:
                models.extend([self.build_model(i) for i in self.casdata[CAS]])
            else:
                models.append(self.build_model(self.casdata[CAS]))
    
    def build_model(self, data):
        *args, Tmin, Tmax = data
        return CnHSModel(*self.funcs, args, Tmin, Tmax, self.name)


# %% Data

read = CASDataReader(__file__, 'Heat Capacity')
_Poling = read('PolingDatabank.tsv')
_TRC_gas = read('TRC Thermodynamics of Organic Compounds in the Gas State.tsv')
_CRC_standard = read('CRC Standard Thermodynamic Properties of Chemical Substances.tsv')
_PerryI = {}

with open(os.path.join(read.folder, 'Perrys Table 2-151.tsv'), encoding='utf-8') as f:
    '''Read in a dict of heat capacities of irnorganic and elemental solids.
    These are in section 2, table 151 in:
    Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook,
    Eighth Edition. McGraw-Hill Professional, 2007.

    Formula:
    Cn(Cal/mol/K) = Const + Lin*T + Quadinv/T^2 + Quadinv*T^2

    Phases: c, gls, l, g.
    '''
    next(f)
    for line in f:
        values = to_nums(line.strip('\n').split('\t'))
        (CASRN, _formula, _phase, _subphase, Const, Lin, Quadinv, Quad, Tmin,
         Tmax, err) = values
        if Lin is None:
            Lin = 0
        if Quadinv is None:
            Quadinv = 0
        if Quad is None:
            Quad = 0
        if CASRN in _PerryI and CASRN:
            a = _PerryI[CASRN]
            a.update({_phase: {"Formula": _formula, "Phase": _phase,
                               "Subphase": _subphase, "Const": Const,
                               "Lin": Lin, "Quadinv": Quadinv, "Quad": Quad,
                               "Tmin": Tmin, "Tmax": Tmax, "Error": err}})
            _PerryI[CASRN] = a
        else:
            _PerryI[CASRN] = {_phase: {"Formula": _formula, "Phase": _phase,
                                       "Subphase": _subphase, "Const": Const,
                                       "Lin": Lin, "Quadinv": Quadinv,
                                       "Quad": Quad, "Tmin": Tmin,
                                       "Tmax": Tmax, "Error": err}}



# %% Heat Capacities Gas
 
def calculate_Lastovka_Shaw_term(iscyclic_aliphatic, similary_variable):
    if iscyclic_aliphatic:
        A1 = -0.1793547
        A2 = 3.86944439
        term = A1 + A2 * similary_variable
    else:
        A1 = 0.58
        A2 = 1.25
        A3 = 0.17338003 # 803 instead of 8003 in another paper
        A4 = 0.014
        term = A2 + (A1-A2)/(1. + exp((similary_variable - A3)/A4))
    return term

[docs]@Cn.g(ref="[1]_") def Lastovka_Shaw(T, MW, similarity_variable, iscyclic_aliphatic=False): r''' Notes ----- The ideal-gas heat capacity (Cn.g; in J/mol/K) is given by: .. math:: C_n^0 = MW \left(A_2 + \frac{A_1 - A_2}{1 + \exp(\frac{\alpha-A_3}{A_4})}\right) + (B_{11} + B_{12}\alpha)\left(-\frac{(C_{11} + C_{12}\alpha)}{T}\right)^2 \frac{\exp(-(C_{11} + C_{12}\alpha)/T)}{[1-\exp(-(C_{11}+C_{12}\alpha)/T)]^2}\\ + (B_{21} + B_{22}\alpha)\left(-\frac{(C_{21} + C_{22}\alpha)}{T}\right)^2 \frac{\exp(-(C_{21} + C_{22}\alpha)/T)}{[1-\exp(-(C_{21}+C_{22}\alpha)/T)]^2} Original model is in terms of J/g/K, but is translated to molar heat capacity in J/mol/K. A1 = 0.58, A2 = 1.25, A3 = 0.17338003, A4 = 0.014, B11 = 0.73917383, B12 = 8.88308889, C11 = 1188.28051, C12 = 1813.04613, B21 = 0.0483019, B22 = 4.35656721, C21 = 2897.01927, C22 = 5987.80407. Examples -------- Compute the heat capacity of Methane at 300 K: >>> f_Cng = Lastovka_Shaw(MW=16.043, ... similarity_variable=0.31166, ... iscyclic_aliphatic=False) >>> f_Cng Functor: Lastovka_Shaw(T, P=None) -> Cn.g [J/mol/K] MW: 16.043 g/mol similarity_variable: 0.31166 iscyclic_aliphatic: 0 >>> f_Cng(T=400.0) 33.91574477458602 ''' a = similarity_variable term = calculate_Lastovka_Shaw_term(iscyclic_aliphatic, a) B11 = 0.73917383 B12 = 8.88308889 C11 = 1188.28051 C12 = 1813.04613 B21 = 0.0483019 B22 = 4.35656721 C21 = 2897.01927 C22 = 5987.80407 return MW * (term + (B11 + B12*a)*((C11+C12*a)/T)**2*exp(-(C11 + C12*a)/T)/(1.-exp(-(C11+C12*a)/T))**2 + (B21 + B22*a)*((C21+C22*a)/T)**2*exp(-(C21 + C22*a)/T)/(1.-exp(-(C21+C22*a)/T))**2)
@H.g def Lastovka_Shaw_Integral(Ta, Tb, MW, similarity_variable, iscyclic_aliphatic=False): term = calculate_Lastovka_Shaw_term(iscyclic_aliphatic, a) return (Lastovka_Shat_Indefinite_Integral(Tb, MW, similarity_variable, term) - Lastovka_Shat_Indefinite_Integral(Ta, MW, similarity_variable, term)) def Lastovka_Shat_Indefinite_Integral(T, MW, similarity_variable, term=None): a = similarity_variable B11 = 0.73917383 B12 = 8.88308889 C11 = 1188.28051 C12 = 1813.04613 B21 = 0.0483019 B22 = 4.35656721 C21 = 2897.01927 C22 = 5987.80407 return MW * (T*term - (B11 + B12*a)*(-C11 - C12*a)**2/(-C11 - C12*a + (C11 + C12*a)*exp((-C11 - C12*a)/T)) - (B21 + B22*a)*(-C21 - C22*a)**2/(-C21 - C22*a + (C21 + C22*a)*exp((-C21 - C22*a)/T))) @S.g def Lastovka_Shaw_Integral_Over_T(Ta, Tb, MW, similarity_variable, iscyclic_aliphatic=False): term = calculate_Lastovka_Shaw_term(iscyclic_aliphatic, a) return (Lastovka_Shaw_Indefinite_Integral_Over_T(Tb, MW, similarity_variable, term) - Lastovka_Shaw_Indefinite_Integral_Over_T(Ta, MW, similarity_variable, term)) def Lastovka_Shaw_Indefinite_Integral_Over_T(T, MW, similarity_variable, term): a = similarity_variable a2 = a*a B11 = 0.73917383 B12 = 8.88308889 C11 = 1188.28051 C12 = 1813.04613 B21 = 0.0483019 B22 = 4.35656721 C21 = 2897.01927 C22 = 5987.80407 S = (term*clog(T) + (-B11 - B12*a)*clog(cexp((-C11 - C12*a)/T) - 1.) + (-B11*C11 - B11*C12*a - B12*C11*a - B12*C12*a2)/(T*cexp((-C11 - C12*a)/T) - T) - (B11*C11 + B11*C12*a + B12*C11*a + B12*C12*a2)/T) S += ((-B21 - B22*a)*clog(cexp((-C21 - C22*a)/T) - 1.) + (-B21*C21 - B21*C22*a - B22*C21*a - B22*C22*a2)/(T*cexp((-C21 - C22*a)/T) - T) - (B21*C21 + B21*C22*a + B22*C21*a + B22*C22*a**2)/T) # There is a non-real component, but it is only a function of similariy # variable and so will always cancel out. return MW * S.real
[docs]@Cn.g(ref="[2]_") def TRCCn(T, a0, a1, a2, a3, a4, a5, a6, a7): r''' Notes ----- The ideal gas heat capacity (Cn.g; J/mol/K) is given by: .. math:: C_n = R\left(a_0 + (a_1/T^2) \exp(-a_2/T) + a_3 y^2 + (a_4 - a_5/(T-a_7)^2 )y^j \right) y = \frac{T-a_7}{T+a_6} \text{ for } T > a_7 \text{ otherwise } 0 j is set to 8. Analytical integrals are available for this expression. Examples -------- Calculate the heat capacity of Methane at 300 K: >>> f_Cng = TRCCn(4.0, 22350000, 2018, 32.767, -31.098, 1346090000, 1229, 473) >>> f_Cng Functor: TRCCn(T, P=None) -> Cn.g [J/mol/K] a0: 4 a1: 2.235e+07 a2: 2018 a3: 32.767 a4: -31.098 a5: 1.3461e+09 a6: 1229 a7: 473 >>> f_Cng(300) 35.73249522521754 ''' if T <= a7: y = 0. else: y = (T - a7)/(T + a6) return R*(a0 + (a1/T**2)*exp(-a2/T) + a3*y**2 + (a4 - a5/(T-a7)**2 )*y**8.)
def TRCCn_Indefinite_Integral(T, a0, a1, a2, a3, a4, a5, a6, a7): first = a6 + a7 y = 0. if T <= a7 else (T - a7)/(T + a6) y2 = y*y y4 = y2*y2 if T <= a7: h = 0.0 else: second = (2.*a3 + 8.*a4)*log(1. - y) third = (a3*(1. + 1./(1. - y)) + a4*(7. + 1./(1. - y)))*y fourth = a4*(3.*y2 + 5./3.*y*y2 + y4 + 0.6*y4*y + 1/3.*y4*y2) fifth = 1/7.*(a4 - a5/((a6 + a7)**2))*y4*y2*y h = first*(second + third + fourth + fifth) return (a0 + a1*exp(-a2/T)/(a2*T) + h/T)*R*T @H.g def TRCCn_Integral(Ta, Tb, a0, a1, a2, a3, a4, a5, a6, a7): return (TRCCn_Indefinite_Integral(Tb, a0, a1, a2, a3, a4, a5, a6, a7) - TRCCn_Indefinite_Integral(Ta, a0, a1, a2, a3, a4, a5, a6, a7)) def TRCCn_Over_T_Indefinite_Integral(T, a0, a1, a2, a3, a4, a5, a6, a7): # Possible optimizations: pre-cache as much as possible. # If this were replaced by a cache, much of this would not need to be computed. y = 0. if T <= a7 else (T - a7)/(T + a6) z = T/(T + a6)*(a7 + a6)/a7 if T <= a7: s = 0. else: a72 = a7*a7 a62 = a6*a6 a7_a6 = a7/a6 # a7/a6 a7_a6_2 = a7_a6*a7_a6 a7_a6_4 = a7_a6_2*a7_a6_2 x1 = (a4*a72 - a5)/a62 # part of third, sum first = (a3 + ((a4*a72 - a5)/a62)*a7_a6_4)*a7_a6_2*log(z) second = (a3 + a4)*log((T + a6)/(a6 + a7)) fourth = -(a3/a6*(a6 + a7) + a5*y**6/(7.*a7*(a6 + a7)))*y third = np.array([(x1*(-a7_a6)**(6-i) - a4)*y**i/i for i in np.arange(1, 8)]).sum() s = first + second + third + fourth return a0*log(T) + a1/(a2*a2)*(1. + a2/T)*exp(-a2/T) + s @S.g def TRCCn_Integral_Over_T(Ta, Tb, a0, a1, a2, a3, a4, a5, a6, a7): return R*(TRCCn_Over_T_Indefinite_Integral(Tb, a0, a1, a2, a3, a4, a5, a6, a7) - TRCCn_Over_T_Indefinite_Integral(Ta, a0, a1, a2, a3, a4, a5, a6, a7))
[docs]@Cn.g(ref="[3]_") def Poling(T, a, b, c, d, e): r""" Notes ----- The ideal gas heat capacity (Cn.g; J/mol/K) is given by: .. math:: C_n = R*(a + bT + cT^2 + dT^3 + eT^4) The data is based on the Poling data bank. Examples -------- Compute the gas heat capacity of Methane at 300 K: >>> f_Cng = Poling(a=4.568, b=-0.008975, c=3.631e-05, d=-3.407e-08, e=1.091e-11) >>> f_Cng Functor: Poling(T, P=None) -> Cn.g [J/mol/K] a: 4.568 b: -0.008975 c: 3.631e-05 d: -3.407e-08 e: 1.091e-11 >>> f_Cng(300) 35.85096123688382 """ return R*(a + b*T + c*T**2 + d*T**3 + e*T**4)
def Poling_Indefinite_Integral(T, a, b, c, d, e): return (((((0.2*e)*T + 0.25*d)*T + c/3.)*T + 0.5*b)*T + a)*T @H.g def Poling_Integral(Ta, Tb, a, b, c, d, e): return R*(Poling_Indefinite_Integral(Tb, a, b, c, d, e) - Poling_Indefinite_Integral(Ta, a, b, c, d, e)) def Poling_Over_T_Indefinite_Integral(T, a, b, c, d, e): return ((((0.25*e)*T + d/3.)*T + 0.5*c)*T + b)*T @S.g def Poling_Integral_Over_T(Ta, Tb, a, b, c, d, e): return R*(Poling_Over_T_Indefinite_Integral(Tb, a, b, c, d, e) - Poling_Over_T_Indefinite_Integral(Ta, a, b, c, d, e) + a*log(Tb/Ta)) # Heat capacity gas methods TRCIG = 'TRC Thermodynamics of Organic Compounds in the Gas State (1994)' POLING = 'Poling et al. (2001)' POLING_CONST = 'Poling et al. (2001) constant' CRCSTD = 'CRC Standard Thermodynamic Properties of Chemical Substances' VDI_TABULAR = 'VDI Heat Atlas' LASTOVKA_SHAW = 'Lastovka and Shaw (2013)' TRCCn_Functors = (TRCCn, TRCCn_Integral, TRCCn_Integral_Over_T) Poling_Functors = (Poling, Poling_Integral, Poling_Integral_Over_T) @TDependentHandleBuilder('Cn.g') def heat_capacity_gas_handle(handle, CAS, MW, similarity_variable, iscyclic_aliphatic): add_model = handle.add_model if CAS in _TRC_gas: _, Tmin, Tmax, a0, a1, a2, a3, a4, a5, a6, a7, _, _, _ = _TRC_gas[CAS] funcs = CnHS(*TRCCn_Functors, (a0, a1, a2, a3, a4, a5, a6, a7)) add_model(Tmin=Tmin, Tmax=Tmax, name=TRCIG, **funcs) if CAS in _Poling: _, Tmin, Tmax, a, b, c, d, e, Cn_g, _ = _Poling[CAS] if not np.isnan(a): funcs = CnHS(*Poling_Functors, (a, b, c, d, e)) add_model(Tmin=Tmin, Tmax=Tmax, **funcs, name=POLING) if not np.isnan(Cn_g): add_model(Cn_g, Tmin, Tmax, name=POLING_CONST) if CAS in _CRC_standard: Cn_g = _CRC_standard[CAS][-1] if not np.isnan(Cn_g): add_model(Cn_g, name=CRCSTD) if MW and similarity_variable: data = (MW, similarity_variable, iscyclic_aliphatic) add_model(Lastovka_Shaw.from_args(data), name=LASTOVKA_SHAW) if CAS in VDI_saturation_dict: # NOTE: VDI data is for the saturation curve, i.e. at increasing # pressure; it is normally substantially higher than the ideal gas # value Ts, Cn_gs = VDI_tabular_data(CAS, 'Cp (g)') add_model(InterpolatedTDependentModel(Ts, Cn_gs, Tmin=Ts[0], Tmax=Ts[-1], name=VDI_TABULAR)) ### Heat capacities of liquids
[docs]@Cn.l(ref="[3]_") def Rowlinson_Poling(T, Tc, ω, Cn_g): r''' Notes ----- The heat capacity of a liquid is given by: .. math:: \frac{Cn^{L} - Cn^{g}}{R} = 1.586 + \frac{0.49}{1-T_r} + \omega\left[ 4.2775 + \frac{6.3(1-T_r)^{1/3}}{T_r} + \frac{0.4355}{1-T_r}\right] This equation is not too accurate. Poling compared 212 substances, and found error at 298K larger than 10% for 18 of them, mostly associating. Of the other 194 compounds, AARD is 2.5%. Examples -------- Compute the liquid heat capacity of methane at 100 K: >>> Cn_g = Poling(a=4.568, b=-0.008975, c=3.631e-05, ... d=-3.407e-08, e=1.091e-11) >>> f_Cnl = Rowlinson_Poling(Tc=190.564, ω=0.008, Cn_g=Cn_g) Functor: Rowlinson_Poling(T, P=None) -> Cn.l [J/mol/K] Tc: 190.56 K ω: 0.008 Cn_g: Poling(T, P=None) -> J/mol/K >>> f_Cnl(100) 55.99104104081243 ''' Tr = T/Tc return Cn_g(T) + R*(1.586 + 0.49/(1.-Tr) + ω*(4.2775 + 6.3*(1-Tr)**(1/3.)/Tr + 0.4355/(1.-Tr)))
[docs]@Cn.l(ref="[3]_ [4]_ [5]_") def Rowlinson_Bondi(T, Tc, ω, Cn_g): r''' Notes ----- The heat capacity of a liquid is given by: .. math:: \frac{Cn^L - Cn^{ig}}{R} = 1.45 + 0.45(1-T_r)^{-1} + 0.25\omega [17.11 + 25.2(1-T_r)^{1/3}T_r^{-1} + 1.742(1-T_r)^{-1}] Less accurate than `Rowlinson_Poling`. Examples -------- >>> Cn_g = Poling(a=4.568, b=-0.008975, c=3.631e-05, ... d=-3.407e-08, e=1.091e-11) >>> f_Cnl = Rowlinson_Poling(Tc=190.564, ω=0.008, ... Cn_g=Cn_g) >>> f_Cnl Functor: Rowlinson_Poling(T, P=None) -> Cn.l [J/mol/K] Tc: 190.56 K ω: 0.008 Cn_g: Poling(T, P=None) -> J/mol/K >>> f_Cnl(100) 55.986088530998174 ''' Tr = T/Tc return Cn_g(T) + R*(1.45 + 0.45/(1.-Tr) + 0.25*ω*(17.11 + 25.2*(1-Tr)**(1/3.)/Tr + 1.742/(1.-Tr)))
[docs]@Cn.l(ref="[6]_") def Dadgostar_Shaw(T, MW, similarity_variable): r''' Notes ----- The liquid heat capacity is given by: .. math:: C_{p} = 24.5(a_{11}\alpha + a_{12}\alpha^2)+ (a_{21}\alpha + a_{22}\alpha^2)T +(a_{31}\alpha + a_{32}\alpha^2)T^2 C_n = MW \cdot Cp Where :math:`\alpha` is the similarity variable. There are many restrictions on its use. Original model is in terms of J/g/K, but it has been modified to give molar heat capacity in J/mol/K. a11 = -0.3416; a12 = 2.2671; a21 = 0.1064; a22 = -0.3874l; a31 = -9.8231E-05; a32 = 4.182E-04 ''' first, second, third = calculate_Dadgostar_Shaw_terms(MW, similarity_variable) first *= 3*R*(151.8675/T)**2*exp(151.8675/T)/(exp(151.8675/T)-1)**2 return (first + second*T + third*T**2) * MW
def calculate_Dadgostar_Shaw_terms(MW, similarity_variable): a = similarity_variable a2 = a*a a11 = -0.3416 a12 = 2.2671 a21 = 0.1064 a22 = -0.3874 a31 = -9.8231E-05 a32 = 4.182E-04 # Didn't seem to improve the comparison; sum of errors on some # points included went from 65.5 to 286. # Author probably used more precision in their calculation. return (a11*a + a12*a2, a21*a + a22*a2, a31*a + a32*a2) def Dadgostar_Shaw_Indefinte_Integral(T, first, second, third): T2 = T*T first *= 3*R*(151.8675/T)**2*exp(151.8675/T)/(exp(151.8675/T)-1)**2 return T2*T/3.*third + T2*0.5*second + T*first @H.l def Dadgostar_Shaw_Integral(Ta, Tb, MW, similarity_variable): terms = calculate_Dadgostar_Shaw_terms(MW, similarity_variable) return MW*(Dadgostar_Shaw_Indefinte_Integral(Tb, *terms) - Dadgostar_Shaw_Indefinte_Integral(Ta, *terms)) def Dadgostar_Shaw_Over_T_Indefinte_Integral(T, first, second, third): first *= 3*R*(151.8675/T)**2*exp(151.8675/T)/(exp(151.8675/T)-1)**2 return T*T*0.5*third + T*second + first*log(T) @H.l def Dadgostar_Shaw_Over_T_Integral(Ta, Tb, MW, similarity_variable): terms = calculate_Dadgostar_Shaw_terms(MW, similarity_variable) return MW*(Dadgostar_Shaw_Over_T_Indefinte_Integral(Tb, *terms) - Dadgostar_Shaw_Over_T_Indefinte_Integral(Ta, *terms)) zabransky_dict_sat_s = {} zabransky_dict_sat_p = {} zabransky_dict_const_s = {} zabransky_dict_const_p = {} zabransky_dict_iso_s = {} zabransky_dict_iso_p = {} # C means average heat capacity values, from less rigorous experiments # sat means heat capacity along the saturation line # p means constant-pressure values, # second argument is whether or not it has a spline type_to_zabransky_dict = {('C', True): zabransky_dict_const_s, ('C', False): zabransky_dict_const_p, ('sat', True): zabransky_dict_sat_s, ('sat', False): zabransky_dict_sat_p, ('p', True): zabransky_dict_iso_s, ('p', False): zabransky_dict_iso_p} with open(os.path.join(read.folder, 'Zabransky.tsv'), encoding='utf-8') as f: next(f) for line in f: values = to_nums(line.strip('\n').split('\t')) (CAS, name, Type, uncertainty, Tmin, Tmax, a1s, a2s, a3s, a4s, a1p, a2p, a3p, a4p, a5p, a6p, Tc) = values spline = bool(a1s) # False if Quasypolynomial, True if spline d = type_to_zabransky_dict[(Type, spline)] if spline: if CAS not in d: d[CAS] = [(a1s, a2s, a3s, a4s, Tmin, Tmax)] else: d[CAS].append((a1s, a2s, a3s, a4s, Tmin, Tmax)) else: # No duplicates for quasipolynomials d[CAS] = (Tc, a1p, a2p, a3p, a4p, a5p, a6p, Tmin, Tmax)
[docs]@Cn.l(ref="[7]_") def Zabransky_Quasi_Polynomial(T, Tc, a1, a2, a3, a4, a5, a6): r""" Notes ----- The liquid heat capacity is given by: .. math:: \frac{C_n}{R}=A_1\ln(1-T_r) + \frac{A_2}{1-T_r} + \sum_{j=0}^m A_{j+3} T_r^j Used only for isobaric heat capacities, not saturation heat capacities. Designed for reasonable extrapolation behavior caused by using the reduced critical temperature. Used by the authors of [1]_ when critical temperature was available for the fluid. """ Tr = T/Tc return R*(a1*log(1-Tr) + a2/(1-Tr) + a3 + a4*Tr + a5*Tr**2 + a6*Tr**3)
def Zabransky_Quasi_Polynomial_Indefinte_Integral(T, Tc, a1, a2, a3, a4, a5, a6): Tc2 = Tc*Tc Tc3 = Tc2*Tc term = (T - Tc)**2 return R*(T*(T*(T*(T*a6/(4.*Tc3) + a5/(3.*Tc2)) + a4/(2.*Tc)) - a1 + a3) + T*a1*log(1. - T/Tc) - 0.5*Tc*(a1 + a2)*log(term)) @H.l def Zabransky_Quasi_Polynomial_Integral(Ta, Tb, Tc, a1, a2, a3, a4, a5, a6): return (Zabransky_Quasi_Polynomial_Indefinte_Integral(Tb, Tc, a1, a2, a3, a4, a5, a6) - Zabransky_Quasi_Polynomial_Indefinte_Integral(Ta, Tc, a1, a2, a3, a4, a5, a6)) def Zabransky_Quasi_Polynomial_Over_T_Indefinte_Integral(T, Tc, a1, a2, a3, a4, a5, a6): Tc2 = Tc*Tc Tc3 = Tc2*Tc term = (T - Tc)**2 term = T - Tc logT = log(T) return R*(a3*logT -a1*polylog2(T/Tc) - a2*(-logT + 0.5*log(term*term)) + T*(T*(T*a6/(3.*Tc3) + a5/(2.*Tc2)) + a4/Tc)) @S.l def Zabransky_Quasi_Polynomial_Over_T_Integral(Ta, Tb, Tc, a1, a2, a3, a4, a5, a6): return (Zabransky_Quasi_Polynomial_Over_T_Indefinte_Integral(Tb, Tc, a1, a2, a3, a4, a5, a6) - Zabransky_Quasi_Polynomial_Over_T_Indefinte_Integral(Ta, Tc, a1, a2, a3, a4, a5, a6))
[docs]@Cn.l(ref='[7]_') def Zabransky_Cubic(T, a1, a2, a3, a4): r""" Notes ----- The liquid heat capacity is given by: .. math:: \frac{C}{R}=\sum_{j=0}^3 A_{j+1} \left(\frac{T}{100}\right)^j """ T = T/100. return R*(((a4*T + a3)*T + a2)*T + a1)
def Zabransky_Cubic_Indefinite_Integral(T, a1, a2, a3, a4): T = T/100. return 100.*R*T*(T*(T*(T*a4*0.25 + a3/3.) + a2*0.5) + a1) @H.l def Zabransky_Cubic_Integral(Ta, Tb, a1, a2, a3, a4): return (Zabransky_Cubic_Indefinite_Integral(Tb, a1, a2, a3, a4) - Zabransky_Cubic_Indefinite_Integral(Ta, a1, a2, a3, a4)) def Zabransky_Cubic_Over_T_Indefinite_Integral(T, a1, a2, a3, a4): T = T/100. return R*(T*(T*(T*a4/3. + a3/2.) + a2) + a1*log(T)) @S.l def Zabransky_Cubic_Over_T_Integral(Ta, Tb, a1, a2, a3, a4): return (Zabransky_Cubic_Over_T_Indefinite_Integral(Tb, a1, a2, a3, a4) - Zabransky_Cubic_Over_T_Indefinite_Integral(Ta, a1, a2, a3, a4)) # Heat capacity liquid methods: ZABRANSKY_SPLINE = 'Zabransky spline, averaged heat capacity' ZABRANSKY_QUASIPOLYNOMIAL = 'Zabransky quasipolynomial, averaged heat capacity' ZABRANSKY_SPLINE_C = 'Zabransky spline, constant-pressure' ZABRANSKY_QUASIPOLYNOMIAL_C = 'Zabransky quasipolynomial, constant-pressure' ZABRANSKY_SPLINE_SAT = 'Zabransky spline, saturation' ZABRANSKY_QUASIPOLYNOMIAL_SAT = 'Zabransky quasipolynomial, saturation' ROWLINSON_POLING = 'Rowlinson and Poling (2001)' ROWLINSON_BONDI = 'Rowlinson and Bondi (1969)' DADGOSTAR_SHAW = 'Dadgostar and Shaw (2011)' Zabransky_Cubic_Functors = (Zabransky_Cubic, Zabransky_Cubic_Integral, Zabransky_Cubic_Over_T_Integral) Zabransky_Quasi_Polynomial_Functors = (Zabransky_Quasi_Polynomial, Zabransky_Quasi_Polynomial_Integral, Zabransky_Quasi_Polynomial_Over_T_Integral) Dadgostar_Shaw_Functors = (Dadgostar_Shaw, Dadgostar_Shaw_Integral, Dadgostar_Shaw_Over_T_Integral) zabransky_model_data = ((ZABRANSKY_SPLINE, zabransky_dict_const_s, Zabransky_Cubic_Functors), (ZABRANSKY_QUASIPOLYNOMIAL, zabransky_dict_const_p, Zabransky_Quasi_Polynomial_Functors), (ZABRANSKY_SPLINE_C, zabransky_dict_iso_s, Zabransky_Cubic_Functors), (ZABRANSKY_QUASIPOLYNOMIAL_C, zabransky_dict_iso_p, Zabransky_Quasi_Polynomial_Functors), (ZABRANSKY_SPLINE_SAT, zabransky_dict_sat_s, Zabransky_Cubic_Functors), (ZABRANSKY_QUASIPOLYNOMIAL_SAT, zabransky_dict_sat_p, Zabransky_Quasi_Polynomial_Functors)) zabransky_model_builders = [ZabranskyModelBuilder(*i) for i in zabransky_model_data] zabransky_model_builders[0].many = True zabransky_model_builders[2].many = True zabransky_model_builders[4].many = True @TDependentHandleBuilder('Cn.l') def heat_capacity_liquid_handle(handle, CAS, Tb, Tc, omega, MW, similarity_variable, Cn): Cn_g = Cn.g for i in zabransky_model_builders: i.add_model(CAS, handle.models) add_model = handle.add_model if CAS in VDI_saturation_dict: # NOTE: VDI data is for the saturation curve, i.e. at increasing # pressure; it is normally substantially higher than the ideal gas # value Ts, Cn_ls = VDI_tabular_data(CAS, 'Cp (l)') add_model(InterpolatedTDependentModel(Ts, Cn_ls, Ts[0], Ts[-1], name=VDI_TABULAR)) if Tc and omega and Cn_g: args = (Tc, omega, Cn_g, 200, Tc) add_model(Rowlinson_Bondi.from_args(args), Tmin=0, Tmax=Tc, name=ROWLINSON_BONDI) add_model(Rowlinson_Poling.from_args(args),Tmin=0, Tmax=Tc, name=ROWLINSON_POLING) # Other if MW and similarity_variable: add_model(CnHSModel(*Dadgostar_Shaw_Functors, data=(MW, similarity_variable), name=DADGOSTAR_SHAW)) # Constant models if CAS in _Poling: _, Tmin, Tmax, a, b, c, d, e, Cn_g, Cn_l = _Poling[CAS] if not np.isnan(Cn_g): add_model(Cn_l, Tmin, Tmax, name=POLING_CONST) if CAS in _CRC_standard: Cn_l = _CRC_standard[CAS][-5] if not np.isnan(Cn_l): add_model(Cn_l, 0, Tc, name=CRCSTD) # %% Heat Capacity Solid
[docs]@Cn.s(ref="[8]_") def Lastovka_Solid(T, similarity_variable, MW): r''' Notes ----- The solid heat capacity is given by: .. math:: C_p = 3(A_1\alpha + A_2\alpha^2)R\left(\frac{\theta}{T}\right)^2 \frac{\exp(\theta/T)}{[\exp(\theta/T)-1]^2} + (C_1\alpha + C_2\alpha^2)T + (D_1\alpha + D_2\alpha^2)T^2 Many restrictions on its use. Trained on data with MW from 12.24 g/mol to 402.4 g/mol, C mass fractions from 61.3% to 95.2%, H mass fractions from 3.73% to 15.2%, N mass fractions from 0 to 15.4%, O mass fractions from 0 to 18.8%, and S mass fractions from 0 to 29.6%. Recommended for organic compounds with low mass fractions of hetero-atoms and especially when molar mass exceeds 200 g/mol. This model does not show and effects of phase transition but should not be used passed the triple point. Original model is in terms of J/g/K, but it has been translated to J/mol/K. A1 = 0.013183; A2 = 0.249381; theta = 151.8675; C1 = 0.026526; C2 = -0.024942; D1 = 0.000025; D2 = -0.000123. Examples -------- Calculate the solid heat capacity of Ethanol at 150 K: >>> f = Lastovka_Solid(similarity_variable=0.19536150996213458, ... MW=46.06844) >>> f Functor: Lastovka_Solid(T, P=None) -> Cn.s [J/mol/K] similarity_variable: 0.19536 MW: 46.068 g/mol >>> f(150) 42.196379547807 ''' A1 = 0.013183 A2 = 0.249381 theta = 151.8675 C1 = 0.026526 C2 = -0.024942 D1 = 0.000025 D2 = -0.000123 sim2 = similarity_variable*similarity_variable return MW * (3*(A1*similarity_variable + A2*sim2)*R*(theta/T)**2 * exp(theta/T)/(exp(theta/T)-1)**2 + (C1*similarity_variable + C2*sim2)*T + (D1*similarity_variable + D2*sim2)*T**2)
def Lastovka_Solid_Indefinite_Integral(T, similarity_variable, MW): A1 = 0.013183 A2 = 0.249381 theta = 151.8675 C1 = 0.026526 C2 = -0.024942 D1 = 0.000025 D2 = -0.000123 sim2 = similarity_variable*similarity_variable return MW * (T**3*(D1*similarity_variable/3. + D2*sim2/3.) + T*T*(C1*similarity_variable + C2*sim2)/2. + 3. * (A1*R*similarity_variable*theta + A2*R*sim2*theta)/(exp(theta/T) - 1.)) @H.s def Lastovka_Solid_Integral(Ta, Tb, similarity_variable, MW): return (Lastovka_Solid_Indefinite_Integral(Tb, similarity_variable, MW) - Lastovka_Solid_Indefinite_Integral(Ta, similarity_variable, MW)) def Lastovka_Solid_Over_T_Indefinite_Integral(T, similarity_variable, MW): A1 = 0.013183 A2 = 0.249381 theta = 151.8675 C1 = 0.026526 C2 = -0.024942 D1 = 0.000025 D2 = -0.000123 sim2 = similarity_variable*similarity_variable exp_theta_T = exp(theta/T) return MW * (-3.*R*similarity_variable*(A1 + A2*similarity_variable)*log(exp_theta_T - 1.) + T**2*(D1*similarity_variable + D2*sim2) / 2. + T*(C1*similarity_variable + C2*sim2) + (3.*A1*R*similarity_variable*theta + 3.*A2*R*sim2*theta)/(T*exp_theta_T - T) + (3.*A1*R*similarity_variable*theta + 3.*A2*R*sim2*theta)/T) @S.s def Lastovka_Solid_Over_T_Integral(Ta, Tb, similarity_variable, MW): return (Lastovka_Solid_Over_T_Indefinite_Integral(Tb, similarity_variable, MW) - Lastovka_Solid_Over_T_Indefinite_Integral(Ta, similarity_variable, MW))
[docs]@Cn.s(ref='[9]_') def Perry_151(T, a, b, c, d): r""" Notes ----- The solid heat capacity is given by: .. math:: C_n = 4.184 (a + bT + \frac{c}{T^2} + dT^2) Coefficients are listed in section 2, table 151 of [9]_. Note that the original model was in a Calorie basis, but has been translated to Joules. """ return (a + b*T + c/T**2 + d*T**2)*calorie
@H.s def Perry_151_Integral(Ta, Tb, a, b, c, d): H1 = (a*Ta + 0.5*b*Ta**2 - d/Ta + c*Ta**3/3.) H2 = (a*Tb + 0.5*b*Tb**2 - d/Tb + c*Tb**3/3.) return (H2 - H1)*calorie @S.s def Perry_151_Over_T_Integral(Ta, Tb, a, b, c, d): S1 = a*log(Ta) + b*Ta - d/(2.*Ta**2) + 0.5*c*Ta**2 S2 = a*log(Tb) + b*Tb - d/(2.*Tb**2) + 0.5*c*Tb**2 return (S2 - S1)*calorie Lastovka_Solid_Functors = (Lastovka_Solid, Lastovka_Solid_Integral, Lastovka_Solid_Over_T_Integral) Perry_151_Functors = (Perry_151, Perry_151_Integral, Perry_151_Over_T_Integral) # Heat capacity solid methods LASTOVKA_S = 'Lastovka, Fulem, Becerra and Shaw (2008)' PERRY151 = "Perry's Table 2-151" @TDependentHandleBuilder('Cn.s') def heat_capacity_solid_handle(handle, CAS, similarity_variable, MW): Tmin = 0 Tmax = 2000 add_model = handle.add_model if CAS in _PerryI: vals = _PerryI[CAS] if 'c' in vals: c = vals['c'] Tmin = c['Tmin'] Tmax = c['Tmax'] data = (c['Const'], c['Lin'], c['Quad'], c['Quadinv']) add_model(CnHSModel(*Perry_151_Functors, data), Tmin, Tmax, name=PERRY151) if CAS in _CRC_standard: Cnc = _CRC_standard[CAS][3] if not np.isnan(Cnc): add_model(float(Cnc), 200, 350) if similarity_variable and MW: data = (similarity_variable, MW) add_model(CnHSModel(*Lastovka_Solid_Functors, data), Tmin, Tmax, name=LASTOVKA_S) heat_capacity_handle = PhaseTHandleBuilder('Cn', heat_capacity_solid_handle, heat_capacity_liquid_handle, heat_capacity_gas_handle)