1111import flexsolve as flx
1212from .fugacity_coefficients import IdealFugacityCoefficients
1313from .domain import vle_domain
14+ from .lle import solve_lle_mol
1415from ..exceptions import InfeasibleRegion
1516from .. import functional as fn
1617from .._settings import settings
@@ -156,6 +157,18 @@ def _T_error(self, T, P, z_over_P, z_norm, y):
156157 y [:] = solve_y (y_phi , self .phi , T , P , y )
157158 return 1. - y .sum ()
158159
160+ def _T_error_lle (self , T , P , z , y , x ):
161+ if T <= 0 : raise InfeasibleRegion ('negative temperature' )
162+ Psats = np .array ([i (T ) for i in self .Psats ], dtype = float )
163+ mol = solve_lle_mol (self .gamma , z , T , P , sample = x )
164+ x [:] = mol / mol .sum () if mol .any () else z
165+ y_phi = (x / P
166+ * Psats
167+ * self .gamma (x , T , P )
168+ * self .pcf (T , P , Psats ))
169+ y [:] = solve_y (y_phi , self .phi , T , P , y )
170+ return 1. - y .sum ()
171+
159172 def _P_error (self , P , T , z_Psat_gamma , Psats , y ):
160173 if P <= 0 : raise InfeasibleRegion ('negative pressure' )
161174 y_phi = z_Psat_gamma * self .pcf (T , P , Psats ) / P
@@ -168,6 +181,14 @@ def _P_error_dep(self, P, T, z, Psats, z_Psats, y):
168181 y [:] = solve_y (y_phi , self .phi , T , P , y )
169182 return 1. - y .sum ()
170183
184+ def _P_error_lle (self , P , T , z , Psats , y , x ):
185+ if P <= 0 : raise InfeasibleRegion ('negative pressure' )
186+ mol = solve_lle_mol (self .gamma , z , T , P , sample = x )
187+ x [:] = mol / mol .sum () if mol .any () else z
188+ y_phi = Psats * x * self .gamma (x , T , P ) * self .pcf (T , P , Psats ) / P
189+ y [:] = solve_y (y_phi , self .phi , T , P , y )
190+ return 1. - y .sum ()
191+
171192 def _T_error_reactive (self , T , P , z , dz , y , x , liquid_conversion ):
172193 if T <= 0 : raise InfeasibleRegion ('negative temperature' )
173194 dz [:] = liquid_conversion (z , T , P , 'l' )
@@ -217,21 +238,21 @@ def _Py_ideal(self, z_Psat_gamma_pcf):
217238 y = z_Psat_gamma_pcf / P
218239 return P , y
219240
220- def __call__ (self , z , * , T = None , P = None , liquid_conversion = None ):
241+ def __call__ (self , z , * , T = None , P = None , liquid_conversion = None , lle = False ):
221242 z = np .asarray (z , float )
222243 if T :
223244 if P : raise ValueError ("may specify either T or P, not both" )
224- P , * args = self .solve_Py (z , T , liquid_conversion )
245+ P , * args = self .solve_Py (z , T , liquid_conversion , lle )
225246 elif P :
226- T , * args = self .solve_Ty (z , P , liquid_conversion )
247+ T , * args = self .solve_Ty (z , P , liquid_conversion , lle )
227248 else :
228249 raise ValueError ("must specify either T or P" )
229250 if liquid_conversion :
230251 return ReactiveBubblePointValues (T , P , self .IDs , z , * args )
231252 else :
232253 return BubblePointValues (T , P , self .IDs , z , * args )
233254
234- def solve_Ty (self , z , P , liquid_conversion = None , guess = None ):
255+ def solve_Ty (self , z , P , liquid_conversion = None , lle = False ):
235256 """
236257 Bubble point at given composition and pressure.
237258
@@ -270,11 +291,15 @@ def solve_Ty(self, z, P, liquid_conversion=None, guess=None):
270291 y = z .copy ()
271292 return T , fn .normalize (y )
272293 elif liquid_conversion is None :
273- f = self ._T_error
274- z_norm = z / z .sum ()
275- z_over_P = z / P
294+ z = z / z .sum ()
295+ z_over_P = z / P
276296 T_guess , y = self ._Ty_ideal (z_over_P )
277- args = (P , z_over_P , z_norm , y )
297+ if lle :
298+ f = self ._T_error_lle
299+ args = (P , z , y , y .copy ())
300+ else :
301+ f = self ._T_error
302+ args = (P , z_over_P , z , y )
278303 try :
279304 T = flx .aitken_secant (f , T_guess , T_guess + 1e-3 ,
280305 self .T_tol , 5e-12 , args ,
@@ -311,7 +336,7 @@ def solve_Ty(self, z, P, liquid_conversion=None, guess=None):
311336 maxiter = self .maxiter )
312337 return T , dz , fn .normalize (y ), x
313338
314- def solve_Py (self , z , T , liquid_conversion = None ):
339+ def solve_Py (self , z , T , liquid_conversion = None , lle = False ):
315340 """
316341 Bubble point at given composition and temperature.
317342
@@ -353,17 +378,23 @@ def solve_Py(self, z, T, liquid_conversion=None):
353378 if T > self .Tmax : T = self .Tmax
354379 elif T < self .Tmin : T = self .Tmin
355380 Psats = np .array ([i (T ) for i in self .Psats ])
356- z_norm = z / z .sum ()
381+ z = z / z .sum ()
357382 if self .gamma .P_dependent :
358383 f = self ._P_error_dep
359- z_Psats = z_norm * Psats
384+ z_Psats = z * Psats
360385 P_guess , y = self ._Py_ideal (z_Psats )
361- args = (T , z_norm , Psats , z_Psats , y )
386+ args = (T , z , Psats , z_Psats , y )
362387 else :
363- z_Psat_gamma = z_norm * Psats * self .gamma (z_norm , T , 101325 )
364- P_guess , y = self ._Py_ideal (z_Psat_gamma )
365- f = self ._P_error
366- args = (T , z_Psat_gamma , Psats , y )
388+ if lle :
389+ z_Psat_gamma = z * Psats * self .gamma (z , T , 101325 )
390+ P_guess , y = self ._Py_ideal (z_Psat_gamma )
391+ f = self ._P_error_lle
392+ args = (P , T , z , Psats , y , y .copy ())
393+ else :
394+ z_Psat_gamma = z * Psats * self .gamma (z , T , 101325 )
395+ P_guess , y = self ._Py_ideal (z_Psat_gamma )
396+ f = self ._P_error
397+ args = (T , z_Psat_gamma , Psats , y )
367398 try :
368399 P = flx .aitken_secant (f , P_guess , P_guess - 1 , self .P_tol , 1e-9 ,
369400 args , checkiter = False , maxiter = self .maxiter )
0 commit comments