Source code for lib.EoS.Cubic.TB

#!/usr/bin/python3
# -*- coding: utf-8 -*-

r"""Pychemqt, Chemical Engineering Process simulator
Copyright (C) 2009-2025, Juan José Gómez Romera <jjgomera@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>."""


from math import exp, log

from numpy import roots
from scipy.constants import R

from lib.EoS.cubic import Cubic


# Table 2 from [2]_
dat = {
    98: (0.3536, 0.1054),
    105: (0.4058, 0.1803),
    # : (0.3593, 0.1441),  # phosphine
    208: (0.4303, 0.1196),
    951: (0.4714, 0.1407),
    104: (0.4257, 0.1694),
    1: (0.1015, 0),
    62: (0.6042, 0.1832),
    50: (0.3731, 0.3096),
    63: (0.4907, 0.3435),
    46: (0.3844, 0.1683),
    110: (0.4892, 0.2851),
    107: (0.4478, 0),
    47: (0.3770, 0.1049),
    51: (0.5516, 0.3468),
    111: (0.6180, 1.2403),
    215: (0.5058, 0.1767),
    216: (0.4468, 0.1919),
    101: (0.5311, 0.1746),
    217: (0.4913, 0.2128),
    100: (0.4733, 0.2386),
    218: (0.5037, 0.1866),
    48: (0.3790, 0.2281),
    49: (0.5663, 0.2308),
    220: (0.5155, 0.2268),
    642: (0.5267, 0.2223),
    115: (0.4456, 0.1881),
    2: (0.3322, 0.1363),
    117: (0.7840, 0.1380),
    # : (0.4621, 0.2130),  # C1Mercaptan
    65: (0.5229, 0.2262),
    125: (0.2703, 0.4115),
    22: (0.4029, 0.1705),
    130: (0.5733, 0.1836),
    131: (0.5080, 0.2809),
    132: (0.4443, 0.2221),
    3: (0.4060, 0.1493),
    133: (0.4587, 0.2315),
    134: (0.9385, 0.2262),
    137: (0.4619, 0.2930),
    136: (0.4587, 0.2315),
    66: (0.5010, 0.1896),
    258: (0.5445, 0.1375),
    23: (0.4577, 0.1849),
    140: (0.4988, 0.2844),
    141: (0.5283, 0.4757),
    142: (0.5010, 0.1896),
    4: (0.4529, 0.1701),
    146: (0.9354, 0.3127),
    24: (0.4862, 0.2060),
    155: (0.5931, 0.3423),
    156: (0.5919, 0.3268),
    157: (0.5699, 0.5655),
    6: (0.5036, 0.1963),
    5: (0.4824, 0.1851),
    162: (0.5640, 0.2772),
    290: (0.4834, 0.7262),
    294: (0.5439, 0.3124),
    166: (0.6147, 0.3639),
    309: (0.6179, 0.3521),
    310: (0.6211, 0.3113),
    311: (0.6048, 0.3160),
    8: (0.5517, 0.1961),
    7: (0.5346, 0.2093),
    318: (0.6387, 0.1969),
    171: (0.4996, 0.2522),
    172: (0.5080, 0.2571),
    319: (0.5247, 0.2829),
    173: (0.5101, 0.2821),
    40: (0.4763, 0.2796),
    38: (0.4994, 0.3004),
    10: (0.5619, 0.2437),
    11: (0.5954, 0.2617),
    12: (0.6290, 0.2874),
    13: (0.6810, 0.3291),
    14: (0.7252, 0.3411),
    15: (0.7618, 0.3312),
    16: (0.7970, 0.3552),
    17: (0.8359, 0.3645),
    18: (0.8006, 0.5691),
    19: (0.9180, 0.3409),
    20: (0.9087, 0.3700),
    21: (0.7926, 0.5491),
    90: (0.7297, 0.6887),
    91: (0.9768, 0.5428),
    92: (1.0644, 0.4451)}


[docs] class TB(Cubic): r"""Trebble-Bishnoi cubic equation of state .. math:: \begin{array}[t]{l} P = \frac{RT}{V-b}-\frac{a}{V^2+\left(b+c\right)V+bc+d^2}\\ a = a_c\exp{q_1\left(1-T_r\right)}\\ b = b_c\left(1+q_2\left(1-T_r+\ln{T_r}\right)\right)\\ c = \frac{RT_c}{P_c}\left(1-3\zeta\right)\\ q_1 = f\left(\omega, Z_c\right)\\ q_2 = f\left(\omega\right)\\ \zeta = 1.075Z_c\\ d(cc/mol) = 0.241V_c(cc/mol)-5\\ \end{array} This four parameter cubic equation include a b parameter with temperature dependence. The model need two compound specific parameters but include too a generalized correlation for both parameters defined in [1]_. The parameters are the updated values given in [2]_ Examples -------- Example 4.3 from [3]_, Propane saturated at 300K >>> from lib.mezcla import Mezcla >>> mix = Mezcla(5, ids=[4], caudalMolar=1, fraccionMolar=[1]) >>> eq = TB(300, 9.9742e5, mix) >>> '%0.1f' % (eq.Vl.ccmol) '90.8' >>> eq = TB(300, 42.477e5, mix) >>> '%0.1f' % (eq.Vg.ccmol) '89.1' There are a tiny desviation, 2025 89.4 for two phases state and 88.3 for single phase state """ __title__ = "Trebble-Bishnoi (1987)" __status__ = "TB" __doi__ = ( {"autor": "Trebble, M.A., Bishnoi, P.R.", "title": "Development of a New Four-Parameter Cubic Equation of State", "ref": "Fluid Phase Equilibria 35 (1987) 1-8", "doi": "10.1016/0378-3812(87)80001-8"}, {"autor": "Trebble, M.A.", "title": "Calculation of Constants in the Trebble-Bishnoi Equation of " "State with an Extended Corresponding States Approach", "ref": "Fluid Phase Equilibria 45 (1989) 165-172", "doi": "10.1016/0378-3812(89)80255-9"}, {"autor": "Poling, B.E, Prausnitz, J.M, O'Connell, J.P", "title": "The Properties of Gases and Liquids 5th Edition", "ref": "McGraw-Hill, New York, 2001", "doi": ""})
[docs] def _cubicDefinition(self, T): """Definition of individual components coefficients""" ai = [] bi = [] ci = [] di = [] for cmp in self.componente: a, b, c, d = self.__lib(cmp, T) ai.append(a) bi.append(b) ci.append(c) di.append(d) self.ai = ai self.bi = bi self.ci = ci self.di = di
[docs] def _GEOS(self, xi): coef = [self.ai, self.bi, self.ci, self.di] am, bm, cm, dm = self._mixture(None, xi, coef) delta = bm+cm epsilon = -bm*cm - dm**2 return am, bm, delta, epsilon
def __lib(self, cmp, T): Tr = T/cmp.Tc TcPci = cmp.Tc*cmp.Pc.MPa if cmp.f_acent < 0.225: # Eq 36 TcPcH = 775.9 + 12003*cmp.f_acent - 57335*cmp.f_acent**2 + \ 91393*cmp.f_acent**3 elif cmp.f_acent <= 1: TcPcH = 1876 - 1160*cmp.f_acent # Eq 37 else: TcPcH = TcPci if cmp.f_acent >= -0.14: # Eq 34 Zc = 0.29 - 0.0885*cmp.f_acent - 0.0005/(TcPci**0.5-TcPcH**0.5) else: Zc = 0.3024 Xc = 1.075*Zc # Eq 28 d = 0.341*cmp.Vc.ccg*cmp.M - 0.005 # Eq 29 # Convert d from cc/mol to m3/mol d /= 1e6 Dc = d*cmp.Pc/R/cmp.Tc # Eq 12 Cc = 1-3*Xc # Eq 6 # Bc calculated ad the smallest positive root of Eq 8 B = roots([1, 2-3*Xc, 3*Xc**2, -Dc**2-Xc**3]) Bpositivos = [] for Bi in B: if Bi > 0: Bpositivos.append(Bi) Bc = min(Bpositivos) Ac = 3*Xc**2 + 2*Bc*Cc + Bc + Cc + Bc**2 + Dc**2 # Eq 7 if cmp.id in dat: q1, q2 = dat[cmp.id] else: if cmp.id == 212 and Tr <= 1: q1 = -0.31913 elif cmp.f_acent < -0.1: # Eq 30 q1 = 0.66208 + 4.63961*cmp.f_acent + 7.45183*cmp.f_acent**2 elif cmp.f_acent <= 0.4: # Eq 32 q1 = 0.35 + 0.7924*cmp.f_acent + 0.1875*cmp.f_acent**2 - \ 28.93*(0.3-Zc)**2 elif cmp.f_acent > 0.4: # Eq 33 q1 = 0.32 + 0.9424*cmp.f_acent - 28.93*(0.3-Zc)**2 if cmp.f_acent < -0.0423: q2 = 0 elif cmp.f_acent <= 0.3: # Eq 26 q2 = 0.05246 + 1.15058*cmp.f_acent - \ 1.99348*cmp.f_acent**2 + 1.5949*cmp.f_acent**3 - \ 1.39267*cmp.f_acent**4 else: q2 = 0.17959 + 0.23471*cmp.f_acent # Eq 27 alfa = exp(q1*(1-Tr)) ac = Ac*R**2*cmp.Tc**2/cmp.Pc a = ac*alfa # Eq 23 if T <= cmp.Tc: beta = 1 + q2*(1 - Tr + log(Tr)) # Eq 19 else: beta = 1 bc = Bc*R*cmp.Tc/cmp.Pc b = bc*beta # Eq 18 c = Cc*R*cmp.Tc/cmp.Pc return a, b, c, d
[docs] def TB_Fugacidad(self, T, P): """Método de cálculo de la fugacidad mediante la ecuación de estado de Trebble-Bishnoi""" a, b, c, d, q1, q2=self.TB_lib(T, P) z=self.TB_Z(T, P) A=a*P/R_atml**2/T**2 B=b*P/R_atml/T u=1+c/b t=1+6*c/b+c**2/b**2+4*d**2/b**2 tita=abs(t)**0.5 if t>=0: lamda=log((2*z+B*(u-tita))/(2*z+B*(u+tita))) else: lamda=2*arctan((2*z+u*B)/B/tita)-pi fi=z-1-log(z-B)+A/B/tita*lamda return unidades.Pressure(P*exp(fi), "atm")
[docs] def TB_U_exceso(self, T, P): """Método de cálculo de la energía interna de exceso mediante la ecuación de estado de Trebble-Bishnoi""" a, b, c, d, q1, q2=self.TB_lib(T, P) v=self.TB_V(T, P) z=P*v/R_atml/T A=a*P/R_atml**2/T**2 B=b*P/R_atml/T u=1+c/b t=1+6*c/b+c**2/b**2+4*d**2/b**2 tita=abs(t)**0.5 if t>=0: lamda=log((2*z+B*(u-tita))/(2*z+B*(u+tita))) else: lamda=2*arctan((2*z+u*B)/B/tita)-pi delta=v**2+(b+c)*v-b*c-d**2 beta=1.+q2*(1-self.tr(T)+log(self.tr(T))) da=-q1*a/self.Tc if self.tr(T)<=1.0: db=b/beta*(1/T-1/self.Tc) else: db=0 U=lamda/b/tita*(a-da*T)+db*T*(-R_atml*T/(v-b)+a/b**2/t*((v*(3*c+b)-b*c+c**2-2*d**2)/delta+(3*c+b)*lamda/b/tita)) #atm*l/mol return unidades.Enthalpy(U*101325/1000/self.peso_molecular, "Jkg")
[docs] def TB_H_exceso(self, T, P): """Método de cálculo de la entalpía de exceso mediante la ecuación de estado de Trebble-Bishnoi""" p=unidades.Pressure(P, "atm") U=self.TB_U_exceso(T, P) a, b, c, d, q1, q2=self.TB_lib(T, P) t=1+6*c/b+c**2/b**2+4*d**2/b**2 if t>=0: v=self.TB_V(T, P).m3g*self.peso_molecular return unidades.Enthalpy(p*v-R/T/self.peso_molecular+U.Jg, "Jg") else: Z=self.TB_Z(T, P) return unidades.Enthalpy(R/self.peso_molecular*T*(Z-1)+U.Jg, "Jg")
[docs] def TB_Entalpia(self, T, P): """Método de cálculo de la entalpía mediante la ecuación de estado de Trebble-Bishnoi""" Ho=self._Ho(T) Delta=self.TB_H_exceso(T, P) return unidades.Enthalpy(Delta+Ho)
[docs] def TB_S_exceso(self, T, P): """Método de cálculo de la entropía de exceso mediante la ecuación de estado de Trebble-Bishnoi""" H=self.TB_H_exceso(T, P) f=self.TB_Fugacidad(T, P) return unidades.SpecificHeat(H.Jg/T-R/self.peso_molecular*log(f.atm/P), "JgK")
[docs] def TB_Entropia(self, T, P): """Método de cálculo de la entropía mediante la ecuación de estado de Trebble-Bishnoi""" So=self._so(T) Delta=self.TB_S_exceso(T, P) return unidades.SpecificHeat(Delta+So)
[docs] def TB_Cv_exceso(self, T, P): """Método de cálculo de la capacidad calorífica a volumen constante de exceso mediante la ecuación de estado de Trebble-Bishnoi""" a, b, c, d, q1, q2=self.TB_lib(T, P) v=self.TB_V(T, P) z=P*v/R_atml/T t=1+6*c/b+c**2/b**2+4*d**2/b**2 tita=abs(t)**0.5 A=a*P/R_atml**2/T**2 B=b*P/R_atml/T u=1+c/b delta=v**2+(b+c)*v-b*c-d**2 beta=1.+q2*(1-self.tr(T)+log(self.tr(T))) da=-q1*a/self.Tc dda=q1**2*a/self.Tc**2 if self.tr(T)<=1.0: db=b/beta*(1/T-1/self.Tc) ddb=-q2*b/beta/T**2 else: db=0 ddb=0 dt=-db/b**2*(6*c+2*c**2/b+8*d**2/b) dtita=abs(dt)/20 if t>=0: lamda=log((2*z+B*(u-tita))/(2*z+B*(u+tita))) dlamda=(db-db*tita-b*dtita)/(2*v+b+c-b*tita)-(db+db*tita+b*dtita)/((2*v+b+c+b*tita)) else: lamda=2*arctan((2*z+u*B)/B/tita)-pi dlamda=2/(1+((2*v+b+c)/b/tita)**2)*(db/b/tita-(2*v+b+c)*(db/b**2/tita+dtita/b/tita**2)) Cv=1/b/tita*(dlamda*(a-da*T)-lamda*dda*T-lamda*(a-da*T)*(db/b+dtita/tita))+(ddb*T+db)*(-R_atml*T/(v-b)+a/b**2/t*((v*(3*c+b)-b*c+c**2-2*d**2)/delta+(3*c+b)*lamda/b/tita))+db*T*(-R_atml/(v-b)-R_atml*T*db/(v-b)**2+1/b**2/t*(da-2*a*db/b-a*dt/t)*((v*(3*c+b)-b*c+c**2-2*d**2)/delta+(3*c+b)*lamda/b/tita)+a/b**2/t*(db*(v-c)*(v**2-2*c*v-c**2+d**2)/delta**2+db*lamda/b/tita+(3*c+b)/b/tita*(dlamda-lamda*(db/b+dtita/tita)))) return unidades.SpecificHeat(Cv*101325/1000/self.peso_molecular, "JkgK")
[docs] def TB_Cv(self, T, P): """Método de cálculo de la capacidad calorifica a volumen constante mediante la ecuación de estado de Trebble-Bishnoi""" Cvo=self.Cv_ideal(T) Delta=self.TB_Cv_exceso(T, P) return unidades.SpecificHeat(Delta+Cvo)
[docs] def TB_Cp_exceso(self, T, P): """Método de cálculo de la capacidad calorífica a presión constante de exceso mediante la ecuación de estado de Trebble-Bishnoi""" a, b, c, d, q1, q2=self.TB_lib(T, P) v=self.TB_V(T, P) Cv=self.TB_Cv_exceso(T, P) beta=1.+q2*(1-self.tr(T)+log(self.tr(T))) delta=v**2+(b+c)*v-b*c-d**2 da=-q1*a/self.Tc if self.tr(T)<=1.0: db=b/beta*(1/T-1/self.Tc) else: db=0 dpdt=R_atml/(v-b)+R*T*db/(v-b)**2-da/delta+a*(v-c)*db/delta**2 dpdv=-R_atml*T/(v-b)**2+a*(2*v+b+c)/delta**2 Cp=-R-T*dpdt**2/dpdv*101325/1000+Cv.JgK*self.peso_molecular return unidades.SpecificHeat(Cp/self.peso_molecular, "JgK")
[docs] def TB_Cp(self, T, P): """Método de cálculo de la capacidad calorifica a presión constante mediante la ecuación de estado de Trebble-Bishnoi""" Cpo=self.Cp_ideal(T) Delta=self.TB_Cp_exceso(T, P) return unidades.SpecificHeat(Delta+Cpo)
[docs] def TB_Joule_Thomson(self, T, P): """Método de cálculo del coeficiente de Joule-Thomson mediante la ecuación de estado de Trebble-Bishnoi""" v=self.TB_V(T, P) Cp=self.TB_Cp(T, P) a, b, c, d, q1, q2=self.TB_lib(T, P) beta=1.+q2*(1-self.tr(T)+log(self.tr(T))) delta=v**2+(b+c)*v-b*c-d**2 da=-q1*a/self.Tc if self.tr(T)<=1.0: db=b/beta*(1/T-1/self.Tc) else: db=0 dpdt=R_atml/(v-b)+R*T*db/(v-b)**2-da/delta+a*(v-c)*db/delta**2 dpdv=-R_atml*T/(v-b)**2+a*(2*v+b+c)/delta**2 return -(T*dpdt+v*dpdv)/Cp/dpdv
if __name__ == "__main__": from lib.mezcla import Mezcla mix = Mezcla(5, ids=[4], caudalMolar=1, fraccionMolar=[1]) eq = TB(300, 9.9742e5, mix) print('%0.0f %0.1f' % (eq.Vg.ccmol, eq.Vl.ccmol)) eq = TB(300, 42.477e5, mix) print('%0.1f' % (eq.Vl.ccmol))