@@ -16,37 +16,54 @@ def Gibbs_equilibrium_objective(
1616 product , # Product stream (constant T and P)
1717 main_phase , # Initial guess phase
1818 phase_equilibrium , # Name of phase equilibrium. Valid inputs include None, 'vle', 'lle', and 'vlle'
19+ phase_hook ,
1920 ):
2021 mask = mass < 0
2122 penalty = mask .any ()
2223 if penalty : mass [mask ] = 0
2324 product .empty ()
2425 product [main_phase ].imol [IDs ] = mass / MWs
25- if phase_equilibrium is not None :
26- eq = getattr (product , phase_equilibrium )
27- eq (T = eq .T , P = eq .P )
26+ if phase_hook is None :
27+ if phase_equilibrium is not None :
28+ eq = getattr (product , phase_equilibrium )
29+ eq (T = eq .T , P = eq .P )
30+ else :
31+ phase_hook (product )
2832 return product .G
2933
3034def minimize_Gibbs_free_energy (
3135 product ,
3236 IDs = None , # Names of potential products
3337 method = None ,
38+ phase_hook = None ,
3439 ):
3540 if method is None : method = 'differential evolution'
41+
42+ # Normalize to 1 kg/hr
3643 F_mass = product .F_mass
37- mol_norm = product .mol / F_mass # Normalize
44+ mol_norm = product .mol / F_mass
45+
46+ # Get atomic flows
3847 chemicals = product .chemicals
3948 atoms = chemicals .formula_array @ mol_norm
4049 index , = np .where (atoms )
4150 atoms = atoms [index ]
4251 formula_array = chemicals .formula_array [index ]
52+
53+ # Reduce to only possible products (such that all atoms exist in feed)
4354 if IDs is None :
4455 IDs = chemicals .IDs
4556 MWs = chemicals .MW
4657 else :
4758 index = chemicals .get_index (IDs )
4859 formula_array = formula_array [:, index ]
4960 MWs = chemicals .MW [index ]
61+ index , = np .where (formula_array .any (axis = 0 ))
62+ IDs = [IDs [i ] for i in index ]
63+ formula_array = formula_array [:, index ]
64+ MWs = MWs [index ]
65+
66+ # Specify atomic and mass constraints
5067 N = len (IDs )
5168 lb = np .zeros (N )
5269 ub = np .ones (N )
@@ -59,24 +76,41 @@ def minimize_Gibbs_free_energy(
5976 atomic_balance = LinearConstraint (
6077 formula_array / MWs , atoms , atoms
6178 )
62- match product .phases :
63- case ('g' , 'l' ):
64- phase_equilibrium = 'vle'
79+
80+ # Choose main phase and phase equilibrium algorithm
81+ if phase_hook :
82+ phases = product .phases
83+ if 'g' in phases :
6584 main_phase = 'g'
66- case ('L' , 'l' ):
67- phase_equilibrium = 'lle'
68- main_phase = 'l'
69- case ('L' , 'g' , 'l' ):
70- phase_equilibrium = 'vlle'
85+ elif 'l' in phases :
7186 main_phase = 'l'
72- case ('l' , 's' ):
73- phase_equilibrium = 'sle'
74- main_phase = 'l'
75- case [main_phase ]:
76- phase_equilibrium = None
77- case _:
78- raise RuntimeError (f'phase equilibrium for { product .phases !r} not supported' )
79- f_args = (MWs , IDs , product , main_phase , phase_equilibrium )
87+ elif 'L' in phases :
88+ main_phase = 'L'
89+ elif 's' in phases :
90+ main_phase = 's'
91+ else :
92+ raise RuntimeError ('main phase could not be found' )
93+ else :
94+ match product .phases :
95+ case ('g' , 'l' ):
96+ phase_equilibrium = 'vle'
97+ main_phase = 'g'
98+ case ('L' , 'l' ):
99+ phase_equilibrium = 'lle'
100+ main_phase = 'l'
101+ case ('L' , 'g' , 'l' ):
102+ phase_equilibrium = 'vlle'
103+ main_phase = 'l'
104+ case ('l' , 's' ):
105+ phase_equilibrium = 'sle'
106+ main_phase = 'l'
107+ case [main_phase ]:
108+ phase_equilibrium = None
109+ case _:
110+ raise RuntimeError (f'phase equilibrium for { product .phases !r} not supported' )
111+
112+ # Solve
113+ f_args = (MWs , IDs , product , main_phase , phase_equilibrium , phase_hook )
80114 if method == 'differential evolution' :
81115 polish = lambda * args , ** kwargs : minimize (
82116 * args , ** kwargs ,
@@ -89,7 +123,7 @@ def minimize_Gibbs_free_energy(
89123 bounds = bounds ,
90124 constraints = [atomic_balance ],
91125 polish = polish ,
92- tol = 1e-5 ,
126+ tol = 1e-6 ,
93127 seed = 0 ,
94128 )
95129 elif method == 'COBYLA' :
@@ -107,9 +141,13 @@ def minimize_Gibbs_free_energy(
107141 "only 'SHGO' and 'differential evolution' "
108142 "are valid methods"
109143 )
144+
145+ # Set solution
110146 product .empty ()
111147 product [main_phase ].imol [IDs ] = solution .x / MWs
112- if phase_equilibrium is not None :
148+ if phase_hook :
149+ phase_hook (product )
150+ elif phase_equilibrium is not None :
113151 eq = getattr (product , phase_equilibrium )
114152 eq (T = eq .T , P = eq .P )
115153 product .F_mass = F_mass
0 commit comments