Source code for lib.EoS.BWRSoave

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

'''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 scipy.optimize import fsolve
from scipy import constants as k

from lib import unidades
from lib.eos import EoS
from lib.bip import Kij


[docs] class BWRSoave(EoS): r"""Soave modification of Benedict-Webb-Rubin equation of state as give in [1]_ .. math:: Z = 1 + B\rho + D\rho^4 + E\rho^2\left(1+F\rho^2\right) \exp(-F\rho^2) .. math:: \begin{array}[t]{l} \beta = B \left(\frac{P_c}{RT_c}\right) = \beta_c + 0.422\left(1-\frac{1}{T_r^{1.6}}\right) + 0.234 \omega \left(1-\frac{1}{T_r^3}\right) \\ \delta = D \left(\frac{P_c}{RT_c}\right)^4 = \delta_c \left[ 1 + d_1\left(\frac{1}{T_r}-1\right) + d_2\left(\frac{1}{T_r}-1\right)^2\right] \\ \epsilon = E \left(\frac{P_c}{RT_c}\right)^2 = \epsilon_c + e_1\left(\frac{1}{T_r}-1\right) + e_2\left(\frac{1}{T_r}-1\right)^2 + e_3\left(\frac{1}{T_r}-1\right)^3 \\ \phi = F \left(\frac{P_c}{RT_c}\right)^2 = f Z_c^2 \end{array} where: .. math:: \begin{array}[t]{l} d_1 = 0.4912 + 0.6478\omega\\ d_2 = 0.3 + 0.3619\omega\\ e_1 = 0.0841 + 0.1318\omega + 0.0018\omega^2\\ e_2 = 0.075 + 0.2408\omega + 0.014\omega^2\\ e_3 = -0.0065 + 0.1798\omega + 0.0078\omega^2\\ f = 0.77\\ \beta_c = bZ_c\\ \delta_c = dZ_c^4\\ \epsilon_c= eZ_c^2\\ e = \frac{2-5Z_c}{\left(1+f+3f^2-2f^3\right)\exp(-f)}\\ d = \frac{1}{3} \left[1-2Z_c-e\left(1+f-2f^2\right)\exp(-f)\right]\\ b = Z_c - 1 - d - e\left(1+f\right)\exp(-f) \end{array} This equation use critical parameters as parameters including Zc and acentric factor. The mixture are treated as fictitious pure compounds with the critical parameters calculated by this mixing rules: .. math:: \begin{array}[t]{l} T_{cm} = \frac{S_1}{\left(\sqrt{S_2}+\sqrt{S_3}\right)^2} \\ P_{cm} = \frac{T_{cm}}{S_3} \\ m_m = \sqrt{\frac{S_2}{S_3}} \\ Z_{cm} = \frac{\sum_ix_iZ_{ci}T{_ci}/P_{ci}}{\sum_ix_i T_{ci}/P_{ci}}\\ S_1 = \sum_i \sum_j x_ix_j\left(1-k_{ij}\right)\frac{T_{ci}T_{cj}} {\sqrt{P_{ci}P_{cj}}} \left(1+m_i\right)\left(1+m_j\right) \\ S_2 = \sum_i \sum_j x_ix_j\left(1-k_{ij}\right)\frac{T_{ci}T_{cj}} {\sqrt{P_{ci}P_{cj}}} m_i m_j \\ S_3 = \sum_i x_i \frac{T_{ci}}{P_{ci}} \\ \end{array} """ __title__ = "Benedict-Webb-Rubin-Soave (1999)" __status__ = "BWRSoave" __doi__ = ( { "autor": "Soave, G.S.", "title": "An Effective Modification of the Benedict-Webb-Rubin " "Equation of State", "ref": "Fluid Phase Equilibria 164(2) (1999) 157-172", "doi": "10.1016/s0378-3812(99)00252-6"}, { "autor": "Soave, G.S.", "title": "A Noncubic Equation of State for the Tretament of " "Hydrocarbon Fluids at Rerservoir Conditions", "ref": "Ind. Eng. Chem. Res. 34(11) (1995) 3981-3994", "doi": "10.1021/ie00038a039"}, { "autor": "", "title": "", "ref": "", "doi": ""}, )
[docs] def __init__(self, T, P, mezcla): EoS.__init__(self, T, P, mezcla) self.kij = Kij(mezcla.ids, "BWRSoave") # print(self.mezcla.Tc, T) # if self.mezcla.Tc < T: # self.x = 1 # self.xi = self.zi # self.yi = self.zi # self.Zg = self._Z() # self.Zl = None # else: self.x, self.Zl, self.Zg, self.xi, self.yi, self.Ki = self._Flash() # # print("q = ", self.x) # # print("x = ", self.xi) # # print("y = ", self.yi) # # print("K = ", self.Ki) if self.Zl: self.Vl = unidades.MolarVolume(self.Zl*k.R*T/P, "m3mol") # l/mol self.rhoL = unidades.MolarDensity(1/self.Vl) else: self.Vl = None self.rhoL = None if self.Zg: self.Vg = unidades.MolarVolume(self.Zg*k.R*T/P, "m3mol") # l/mol self.rhoG = unidades.MolarDensity(1/self.Vg) else: self.Vg = None self.rhoG = None
[docs] def _lib(self, T, **kw): """Library for compound specified parameters""" Tc = kw["Tc"] w = kw["w"] Zc = kw["Zc"] Tr = T/Tc tau = 1/Tr-1 f = 0.77 # Eq 12 e = (2 - 5*Zc)/((1 + f + 3*f**2 - 2*f**3)*exp(-f)) # Eq 14 d = (1 - 2*Zc - e*(1 + f - 2*f**2)*exp(-f))/3 b = Zc - 1 - d - e*(1+f)*exp(-f) beta_c = b*Zc # Eq 13 delta_c = d*Zc**4 epsilon_c = e*Zc**2 d1 = 0.4912 + 0.6478*w # Eq 10 d2 = 0.3 + 0.3619*w e1 = 0.0841 + 0.1318*w + 0.0018*w**2 # Eq 11 e2 = 0.0750 + 0.2408*w - 0.0140*w**2 e3 = -0.0065 + 0.1798*w - 0.0078*w**2 # Eq 6 beta = beta_c + 0.422*(1-1/Tr**1.6) + 0.234*w*(1-1/Tr**3) # Eq 7 delta = delta_c*(1 + d1*tau + d2*tau**2) # Eq 8 epsilon = epsilon_c + e1*tau + e2*tau**2 + e3*tau**3 # Eq 9 phi = f*Zc**2 kw = {} kw["beta"] = beta kw["delta"] = delta kw["epsilon"] = epsilon kw["phi"] = phi return kw
[docs] def _mixture(self, zi): """Mixing rules""" # Linear correlation for acentric factor m = [1.25*cmp.f_acent for cmp in self.componente] S1 = S2 = S3 = 0 for ci, kiji, mi, xi in zip(self.componente, self.kij, m, zi): for cj, ki, mj, xj in zip(self.componente, kiji, m, zi): S1 += xi*xj*(1-ki)*ci.Tc*cj.Tc/(ci.Pc*cj.Pc)**0.5*(1+mi)*(1+mj) S2 += xi*xj*(1-ki)*(ci.Tc*cj.Tc/ci.Pc/cj.Pc)**0.5*mi*mj S3 += xi*ci.Tc/ci.Pc Tcm = S1/(S2**0.5+S3**0.5)**2 Pcm = Tcm/S3 mm = (S2/S3)**0.5 # Eq 23 num = 0 den = 0 for xi, cmp in zip(zi, self.componente): num += xi*cmp.Zc*cmp.Tc/cmp.Pc den += xi*cmp.Tc/cmp.Pc Zcm = num/den kw = {} kw["Tc"] = Tcm kw["Pc"] = Pcm kw["w"] = mm/1.25 kw["Zc"] = Zcm kw["S1"] = S1 kw["S2"] = S2 kw["S3"] = S3 return kw
[docs] def _Z(self, zi=None, T=None, P=None, crit=None, phase=0): if not zi: zi = self.zi if not T: T = self.T if not P: P = self.P if not crit: crit = self._mixture(zi) kw = self._lib(T, **crit) b = kw["beta"] d = kw["delta"] e = kw["epsilon"] ph = kw["phi"] Tr = T/crit["Tc"] Pr = P/crit["Pc"] def func(ps): rlh = ps*(1+b*ps+d*ps**4+e*ps**2*(1+ph*ps**2)*exp(-ph*ps**2)) return rlh-Pr/Tr if Tr > 1: y0 = Pr/Tr/(1+b*Pr/Tr) elif phase == 0: y0 = Pr/Tr/(1+b*Pr/Tr) else: y0 = 1/crit["Zc"]**(1+(1-Tr)**(2/7)) rinput = fsolve(func, y0, full_output=True) y = rinput[0] Z = Pr/y/Tr # print(Z, rinput) return Z
[docs] def _fug(self, xi, yi, T, P): """Fugacities of component in mixture calculation Parameters ---------- xi : list Molar fraction of component in liquid phase, [-] yi : list Molar fraction of component in vapor phase, [-] Returns ------- tital : list List with liquid phase component fugacities titav : list List with vapour phase component fugacities """ critL = self._mixture(xi) Zl = self._Z(xi, T, P, crit=critL, phase=1)[0] kwL = self._lib(T, **critL) kwL.update(critL) tital = self._fugacity(Zl, xi, **kwL) critG = self._mixture(yi) Zv = self._Z(yi, T, P, crit=critG, phase=0)[0] kwG = self._lib(T, **critG) kwG.update(critG) titav = self._fugacity(Zv, yi, **kwG) return tital, titav
[docs] def _fugacity(self, Z, x, **kw): # Fugacity coefficient in a mixture, Appendix A print(Z, x) f = 0.77 Tc = kw["Tc"] Pc = kw["Pc"] Zc = kw["Zc"] w = kw["w"] beta = kw["beta"] delta = kw["delta"] epsilon = kw["epsilon"] phi = kw["phi"] S1 = kw["S1"] S2 = kw["S2"] S3 = kw["S3"] Tr = self.T/Tc Pr = self.P/Pc psi = 1/Z*Pr/Tr f = 0.77 e = (2 - 5*Zc)/((1 + f + 3*f**2 - 2*f**3)*exp(-f)) d = (1 - 2*Zc - e*(1+f-2*f**2)*exp(-f))/3 b = Zc - 1 - d - e*(1+f)*exp(-f) # betac = b*Zc deltac = d*Zc**4 # epsilonc = e*Zc**2 d1 = 0.4912 + 0.6478*w d2 = 0.3 + 0.3619*w e1 = 0.0841 + 0.1318*w + 0.0018*w**2 e2 = 0.0750 + 0.2408*w - 0.0140*w**2 e3 = -0.0065 + 0.1798*w - 0.0078*w**2 dZcdn = [] for cmp in self.componente: dZcdn.append(cmp.Tc/cmp.Pc/Tc*Pc*(cmp.Zc-Zc)) # Appendix B S1i = [] S2i = [] for cmp in self.componente: S1i.append(cmp.Tc**2/cmp.Pc*(1+1.4*cmp.f_acent)**2) S2i.append(cmp.Tc/cmp.Pc*(1.4*cmp.f_acent)**2) S1ij = [] S2ij = [] for s1i, s2i, kiji in zip(S1i, S2i, self.kij): S1iji = [] S2iji = [] for s1j, s2j, kij in zip(S1i, S2i, kiji): S1iji.append((1-kij)*(s1i*s1j)**0.5) S2iji.append((1-kij)*(s2i*s2j)**0.5) S1ij.append(S1iji) S2ij.append(S2iji) from pprint import pprint pprint(S1ij) pprint(S1i) sum1 = [xi*s1ij[i] for i, (xi, s1ij) in enumerate(zip(x, S1ij))] sum2 = [xi*s2ij[i] for i, (xi, s2ij) in enumerate(zip(x, S2ij))] print(sum1) S1m = sum([x * s1 for x, s1 in zip(self.zi, sum1)]) S2m = sum([x * s2 for x, s2 in zip(self.zi, sum2)]) # Appendix C, section B1, from [2]_ ds1dn = [2*s1i - 2*S1m for s1i in sum1] ds2dn = [2*s2i - 2*S2m for s2i in sum2] ds3dn = [cmp.Tc/cmp.Pc-Tc/Pc for cmp in self.componente] # Appendix C, section B3, from [2]_ dwdn = [] for ds2, ds3 in zip(ds2dn, ds3dn): dwdn.append(w/2*(ds2/S2-ds3/S3)) # Appendix C, section B2, from [2]_ dTcdn = [] for ds1, ds2, ds3 in zip(ds1dn, ds2dn, ds3dn): dTcdn.append(ds1/S1-(ds2/S2**0.5+ds3/S3**0.5)/(S2**0.5+S3**0.5)) dedn = [-5*dZ/(1+f+3*f**2-2*f**3)/exp(-f) for dZ in dZcdn] dddn = [(1-Z-e*(1+f-2*f**2)*exp(-f))/3 for Z, e in zip(dZcdn, dedn)] dbdn = [Z-d-e*(1+4)*exp(-f) for Z, d, e in zip(dZcdn, dddn, dedn)] dbetacdn = [Zc*db + b*dZ for db, dZ in zip(dbdn, dZcdn)] ddeltacdn = [Zc**4*dd + 4*d*Zc**3*dZ for dd, dZ in zip(dddn, dZcdn)] depscdn = [Zc**2*de + 2*e*Zc*dZ for de, dZ in zip(dedn, dZcdn)] dbbcdTc = -1.6*0.422/Tr**1.6 - w*3*0.234/Tr**3 dbbcdw = 0.422/Tr**0.234 dbetadn = [] for dbc, dtc, dwn in zip(dbetacdn, dTcdn, dwdn): dbetadn.append(dbc + dbbcdTc*dtc + dbbcdw*dwn) dddcdTc = (d1+d2*(1/Tr-1))/Tr dddcdw = 0.6478*(1/Tr-1) + 0.3619*(1/Tr-1)**2 ddeltadn = [] for dd, dt, dw in zip(ddeltacdn, dTcdn, dwdn): ddeltadn.append(delta/deltac*dd + deltac*(dddcdTc*dt + dddcdw*dw)) deTcterm = (e1 + 2*e2*(1/Tr-1) + 3*e3*(1/Tr-1)**2)/Tr depsdw = (0.1318+2*0.0018*w)*(1/Tr-1) + \ (0.2408-2*0.014*w)*(1/Tr-1)**2 + (0.1798-2*0.0078*w)*(1/Tr-1)**3 depsilondn = [] for dec, dtc, dwn in zip(depscdn, dTcdn, dwdn): depsilondn.append(dec + deTcterm*dtc + depsdw*dwn) dphidn = [2*f*Zc*dZn for dZn in dZcdn] dBn2 = [] for dbet, cmp in zip(dbetadn, self.componente): dBn2.append(1 + dbet/beta + cmp.Tc/cmp.Pc/Tc*Pc) dDn5 = [] for ddel, cmp in zip(ddeltadn, self.componente): dDn5.append(1 + ddel/delta + 4*cmp.Tc/cmp.Pc/Tc*Pc) dEn3 = [] for dep, cmp in zip(depsilondn, self.componente): dEn3.append(dep/epsilon + 2*cmp.Tc/cmp.Pc/Tc*Pc) dFn2 = [] for dp, cmp in zip(dphidn, self.componente): dFn2.append(dp/phi + 2*cmp.Tc/cmp.Pc/Tc*Pc) tita = [] for db, dd, de, df in zip(dBn2, dDn5, dEn3, dFn2): fug = -log(Z) + db*beta*psi + dd*delta*psi**4/4 + \ de*epsilon/phi*(1-(1+phi*psi**2/2)*exp(-phi*psi**2)) + \ df*epsilon/phi*( (1+phi*psi**2+phi**2*psi**4/2) * exp(-phi*psi**2)-1) tita.append(fug) return tita
_all = [BWRSoave] if __name__ == "__main__": from lib.mezcla import Mezcla from lib.EoS.BWRS import BWRS # from lib.mEoS import O2 # mezcla = Mezcla(3, ids=[47], caudalMasico=1, fraccionMolar=[1]) # eq = BWRSoave(160, 1e7, mezcla) # # print(eq.Zg) # eq = BWRS(160, 1e7, mezcla) # print("BWRS", eq.Zg, eq.rhoG) # eq = O2(T=160, P=1e7) # print("MEoS", eq.Z, eq.rhoM) # from matplotlib.pyplot import * # from numpy import logspace # P = logspace(1, 3.5, 200) # Z1 = [] # Z2 = [] # for p in P: # eq = BWRSoave(160, p*1e5, mezcla) # if eq.x == 1: # Z1.append(eq.Zg) # else: # Z1.append(eq.Zl) # eq = BWRS(160, p*1e5, mezcla) # # eq = O2(T=160, P=p*1e5) # # Z2.appendg(eq.Z) # if eq.x == 1: # Z2.append(eq.Zg) # else: # Z2.append(eq.Zl) # plot(P, Z1) # plot(P, Z2) # yscale("log") # xscale("log") # xlim(10, 5000) # ylim(0.1, 10) # show() mezcla = Mezcla(3, ids=[2, 3, 14], caudalMasico=1, fraccionMolar=[0.698, 0.297, 0.005]) eq = BWRSoave(230, 4e6, mezcla) print(eq.rhoL.str)