@@ -921,7 +921,7 @@ def _run(self):
921921
922922 def _update_separation_factors (self , f = None ):
923923 if self .B == inf :
924- self .S = np .ones (len (self .partition .IDs )) * np . inf
924+ self .S = np .ones (len (self .partition .IDs )) * inf
925925 elif self .B == 0 :
926926 self .S = np .zeros (len (self .partition .IDs ))
927927 else :
@@ -1481,7 +1481,7 @@ def initialize_energy_balance_node(self):
14811481 def K_node (self ):
14821482 if hasattr (self , '_K_node' ): return self ._K_node
14831483 partition_data = self .partition .partition_data
1484- if (self .specified_variable == 'B' ) and (self .B == 0 or self .B == np . inf ):
1484+ if (self .specified_variable == 'B' ) and (self .B == 0 or self .B == inf ):
14851485 var = None
14861486 elif (partition_data and 'K' in partition_data ):
14871487 var = None
@@ -1525,7 +1525,7 @@ class PhasePartition(Unit):
15251525 _N_ins = 1
15261526 _N_outs = 2
15271527 strict_infeasibility_check = False
1528- conversion_relaxation_factor = 0
1528+ conversion_relaxation_factor = 0.5
15291529 S_relaxation_factor = 0
15301530 B_relaxation_factor = 0
15311531 K_relaxation_factor = 0
@@ -1805,7 +1805,7 @@ def _run_lle(self, P=None, update=True, top_chemical=None, single_loop=False):
18051805 self .strict_infeasibility_check , 1
18061806 )
18071807 if phi == 1 :
1808- self .B = np . inf
1808+ self .B = inf
18091809 else :
18101810 self .B = phi / (1 - phi )
18111811 self .K = K
@@ -1818,7 +1818,7 @@ def _run_lle(self, P=None, update=True, top_chemical=None, single_loop=False):
18181818 else :
18191819 lle_chemicals , K_new , gamma_y , phi = eq (T = ms .T , P = P or self .P , top_chemical = top_chemical , update = update , use_cache = self .use_cache )
18201820 if phi == 1 or phi is None :
1821- self .B = np . inf
1821+ self .B = inf
18221822 self .T = ms .T
18231823 return
18241824 else :
@@ -2088,8 +2088,9 @@ class MultiStageEquilibrium(Unit):
20882088 iteration_memory = 10 # Length of recorded iterations.
20892089 preconditioning_tolerance = 1e-3
20902090 preconditioning_relative_tolerance = 1e-3
2091+ homotopy_continuation_steps = 5
20912092 inside_maxiter = 100
2092- default_max_attempts = 5
2093+ default_max_attempts = 2
20932094 default_maxiter = 100
20942095 default_optimize_result = False
20952096 default_tolerance = 1e-6
@@ -2404,6 +2405,7 @@ def _init(self,
24042405 else :
24052406 self .methods = methods
24062407
2408+ self .homotopy_continuation = bool (stage_reactions )
24072409 self .vle_decomposition = vle_decomposition
24082410
24092411
@@ -2438,7 +2440,7 @@ def _residuals(self, x):
24382440 H_model = self ._eq_thermo .mixture .H
24392441 if self .stage_reactions :
24402442 feed_flows = self .feed_flows + self .conversion
2441- feed_and_invariable_enthalpies = self ._feed_and_invariable_enthalpies + (self .conversion * self ._Hf_eq ).sum (axis = 1 )
2443+ feed_and_invariable_enthalpies = self ._feed_and_invariable_enthalpies - (self .conversion * self ._Hf_eq ).sum (axis = 1 )
24422444 else :
24432445 feed_flows = self .feed_flows
24442446 feed_and_invariable_enthalpies = self ._feed_and_invariable_enthalpies
@@ -2534,6 +2536,15 @@ def material_errors(self):
25342536 )
25352537 return pd .DataFrame (errors , columns = IDs )
25362538
2539+ # %% Homotopy for reactive stages
2540+
2541+ @property
2542+ def conversion (self ):
2543+ if self .homotopy_continuation :
2544+ return self .conversion_homotopy * self ._conversion
2545+ else :
2546+ return self ._conversion
2547+
25372548 # %% Simulation
25382549
25392550 def default_vle_decomposition (self ):
@@ -2575,14 +2586,39 @@ def _run(self):
25752586 solver = flx .wegstein
25762587 else :
25772588 raise ValueError (f'invalid method { method !r} ' )
2578- if self ._has_lle and algorithm == 'inside out' : continue
2589+ if self ._has_lle and algorithm == 'inside out' :
2590+ raise RuntimeError ('inside out method does not support liquid extraction yet' )
25792591 if analysis_mode :
25802592 self ._tracked_algorithms .append (
25812593 (self .iter + 1 , algorithm )
25822594 )
2583- try : x = solver (f , x , maxiter = maxiter , xtol = xtol , rtol = rtol , args = (algorithm ,))
2595+ try :
2596+ if self .homotopy_continuation :
2597+ if self .conversion_homotopy == 1 :
2598+ try :
2599+ x = solver (f , x , maxiter = maxiter , xtol = xtol , rtol = rtol , args = (algorithm ,))
2600+ except :
2601+ x = self ._best_result .x
2602+ self ._mean_residual = inf
2603+ self ._iteration_record [0 ] = IterationResult (None , inf )
2604+ self ._best_result = IterationResult (x , inf )
2605+ else :
2606+ break
2607+ steps = self .homotopy_continuation_steps
2608+ for t in np .linspace (0 , 1 , steps ):
2609+ self .conversion_homotopy = t
2610+ try :
2611+ x = solver (f , x , maxiter = maxiter , xtol = xtol , rtol = rtol , args = (algorithm ,))
2612+ except :
2613+ x = self ._best_result .x
2614+ self ._mean_residual = inf
2615+ self ._iteration_record [0 ] = IterationResult (None , inf )
2616+ self ._best_result = IterationResult (x , inf )
2617+ else :
2618+ x = solver (f , x , maxiter = maxiter , xtol = xtol , rtol = rtol , args = (algorithm ,))
25842619 except :
2585- self ._mean_residual = np .inf
2620+ self ._mean_residual = inf
2621+ self ._iteration_record [0 ] = IterationResult (None , inf )
25862622 if self ._best_result .x is not None : x = self ._best_result .x
25872623 maxiter = self .maxiter - self .iter
25882624 if maxiter <= 0 : break
@@ -2696,7 +2732,7 @@ def _run_inside_out(self):
26962732 feed_flows = self .feed_flows + self .conversion
26972733 total_feed_flows = feed_flows .sum (axis = 1 )
26982734 bulk_feed = total_feed_flows .sum ()
2699- feed_and_invariable_enthalpies = self ._feed_and_invariable_enthalpies + (self .conversion * self ._Hf_eq ).sum (axis = 1 )
2735+ feed_and_invariable_enthalpies = self ._feed_and_invariable_enthalpies - (self .conversion * self ._Hf_eq ).sum (axis = 1 )
27002736 else :
27012737 total_feed_flows = self ._total_feed_flows
27022738 bulk_feed = self ._bulk_feed
@@ -2835,7 +2871,7 @@ def f(x):
28352871 self ._result = result = least_squares (f ,
28362872 x0 = x .flatten (),
28372873 jac = lambda x : MESH .create_block_tridiagonal_matrix (* self ._jacobian (x .reshape (shape ))),
2838- bounds = (0 , np . inf ),
2874+ bounds = (0 , inf ),
28392875 method = 'trf' ,
28402876 max_nfev = self .maxiter ,
28412877 xtol = self .tolerance ,
@@ -2973,7 +3009,7 @@ def hot_start(self):
29733009 i ._N_chemicals = N_chemicals
29743010 self .feed_flows = feed_flows = np .zeros ([N_stages , N_chemicals ])
29753011 if self .stage_reactions :
2976- self .conversion = conversion = feed_flows .copy ()
3012+ self ._conversion = conversion = feed_flows .copy ()
29773013 self ._Hf_eq = self .chemicals .Hf [index ]
29783014 for i , j in zip (partitions , conversion ): i .conversion = j
29793015 self .feed_enthalpies = feed_enthalpies = np .zeros (N_stages )
@@ -3136,6 +3172,7 @@ def hot_start(self):
31363172 for s in partition .outs : s .T = T
31373173 xs = np .array ([i .x for i in partitions ])
31383174 ys = np .array ([i .y for i in partitions ])
3175+ self .conversion_homotopy = 0
31393176 Vs , Ls = self .estimate_bulk_vapor_and_liquid_flow_rates (xs , ys , Ts )
31403177 phase_ratios = Vs / Ls
31413178 for partition , B in zip (partitions , phase_ratios ):
@@ -3172,8 +3209,8 @@ def hot_start(self):
31723209 self ._phi = self ._eq_thermo .Phi (self ._eq_thermo .chemicals )
31733210 self ._H_magnitude = 100 * sum ([i .mixture .Cn ('l' , i .mol , i .T , i .P ) for i in self .ins ])
31743211 self .attempt = 0
3175- self ._mean_residual = np . inf
3176- self ._best_result = empty = IterationResult (None , np . inf )
3212+ self ._mean_residual = inf
3213+ self ._best_result = empty = IterationResult (None , inf )
31773214 self ._point_shape = (N_stages , 2 * N_chemicals + 1 )
31783215 record = self .iteration_memory * [empty ]
31793216 x = self ._get_point ()
@@ -3328,7 +3365,7 @@ def estimate_bulk_vapor_and_liquid_flow_rates(self, xs, ys, Ts):
33283365 feed_flows = self .feed_flows + self .conversion
33293366 total_feed_flows = feed_flows .sum (axis = 1 )
33303367 bulk_feed = total_feed_flows .sum ()
3331- feed_and_invariable_enthalpies = self ._feed_and_invariable_enthalpies + (self .conversion * self ._Hf_eq ).sum (axis = 1 )
3368+ feed_and_invariable_enthalpies = self ._feed_and_invariable_enthalpies - (self .conversion * self ._Hf_eq ).sum (axis = 1 )
33323369 else :
33333370 total_feed_flows = self ._total_feed_flows
33343371 bulk_feed = self ._bulk_feed
@@ -3669,7 +3706,7 @@ def f(x):
36693706 least_squares (f ,
36703707 x0 = x0 .flatten (),
36713708 jac = lambda x : MESH .create_block_tridiagonal_matrix (* self ._jacobian (x .reshape (shape ))),
3672- bounds = (0 , np . inf ),
3709+ bounds = (0 , inf ),
36733710 method = 'trf' ,
36743711 max_nfev = iterations ,
36753712 x_scale = 'jac' ,
0 commit comments