|
10 | 10 | from collections import namedtuple |
11 | 11 | import logging |
12 | 12 |
|
13 | | -from ..optimize.nonlinear_solvers import damped_newton_solve |
| 13 | +from ..constants import logish_eps |
| 14 | +from ..optimize.nonlinear_solvers import damped_newton_solve, TerminationCode |
14 | 15 | from ..classes.solution import Solution |
15 | 16 |
|
16 | 17 | P_scaling = 1.0e9 # Pa |
@@ -1073,32 +1074,94 @@ def equilibrate( |
1073 | 1074 |
|
1074 | 1075 | F_tol = default_F_tolerances(assemblage, equality_constraints, n_atoms) |
1075 | 1076 |
|
1076 | | - # Set the initial fractions and compositions |
1077 | | - # of the phases in the assemblage: |
1078 | | - sol = damped_newton_solve( |
1079 | | - F=lambda x: F( |
| 1077 | + def F_fn(x): |
| 1078 | + return F( |
1080 | 1079 | x, |
1081 | 1080 | assemblage, |
1082 | 1081 | equality_constraints, |
1083 | 1082 | prm.reduced_composition_vector, |
1084 | 1083 | prm.reduced_free_composition_vectors, |
1085 | | - ), |
1086 | | - J=lambda x: jacobian( |
| 1084 | + ) |
| 1085 | + |
| 1086 | + def J_fn(x): |
| 1087 | + return jacobian( |
1087 | 1088 | x, |
1088 | 1089 | assemblage, |
1089 | 1090 | equality_constraints, |
1090 | 1091 | prm.reduced_free_composition_vectors, |
1091 | | - ), |
1092 | | - lambda_bounds=lambda dx, x: lambda_bounds( |
1093 | | - dx, x, assemblage.endmembers_per_phase |
1094 | | - ), |
| 1092 | + ) |
| 1093 | + |
| 1094 | + def lambda_bounds_fn(dx, x): |
| 1095 | + return lambda_bounds(dx, x, assemblage.endmembers_per_phase) |
| 1096 | + |
| 1097 | + linear_constraints = (prm.constraint_matrix, prm.constraint_vector) |
| 1098 | + |
| 1099 | + sol = damped_newton_solve( |
| 1100 | + F=F_fn, |
| 1101 | + J=J_fn, |
| 1102 | + lambda_bounds=lambda_bounds_fn, |
1095 | 1103 | guess=parameters, |
1096 | | - linear_constraints=(prm.constraint_matrix, prm.constraint_vector), |
| 1104 | + linear_constraints=linear_constraints, |
1097 | 1105 | tol=tol, |
1098 | 1106 | F_tol=F_tol, |
1099 | 1107 | store_iterates=store_iterates, |
1100 | 1108 | max_iterations=max_iterations, |
1101 | 1109 | ) |
| 1110 | + # If we hit max iterations, we try relaxing the logish_eps |
| 1111 | + # and solving again. This helps convergence in hard problems because |
| 1112 | + # it allows larger steps when phase compositions are close to the |
| 1113 | + # boundaries of the solution (where the gradient in excess entropy is large). |
| 1114 | + if sol.code == TerminationCode.MAX_ITERATIONS: |
| 1115 | + old_logish_eps = logish_eps[0] |
| 1116 | + logish_eps[0] = 1.0e-5 |
| 1117 | + c_shifts = sol.x[-n_free_compositional_vectors:] |
| 1118 | + new_parameters = get_parameters(assemblage, n_free_compositional_vectors) |
| 1119 | + new_parameters[-n_free_compositional_vectors:] = c_shifts |
| 1120 | + set_compositions_and_state_from_parameters(assemblage, parameters) # reset |
| 1121 | + |
| 1122 | + sol2 = damped_newton_solve( |
| 1123 | + F=F_fn, |
| 1124 | + J=J_fn, |
| 1125 | + lambda_bounds=lambda_bounds_fn, |
| 1126 | + guess=new_parameters, |
| 1127 | + linear_constraints=linear_constraints, |
| 1128 | + tol=tol, |
| 1129 | + F_tol=F_tol, |
| 1130 | + store_iterates=store_iterates, |
| 1131 | + max_iterations=max_iterations, |
| 1132 | + ) |
| 1133 | + |
| 1134 | + logish_eps[0] = old_logish_eps |
| 1135 | + c_shifts = sol2.x[-n_free_compositional_vectors:] |
| 1136 | + new_parameters = get_parameters(assemblage, n_free_compositional_vectors) |
| 1137 | + new_parameters[-n_free_compositional_vectors:] = c_shifts |
| 1138 | + set_compositions_and_state_from_parameters(assemblage, parameters) # reset |
| 1139 | + sol3 = damped_newton_solve( |
| 1140 | + F=F_fn, |
| 1141 | + J=J_fn, |
| 1142 | + lambda_bounds=lambda_bounds_fn, |
| 1143 | + guess=new_parameters, |
| 1144 | + linear_constraints=linear_constraints, |
| 1145 | + tol=tol, |
| 1146 | + F_tol=F_tol, |
| 1147 | + store_iterates=store_iterates, |
| 1148 | + max_iterations=max_iterations, |
| 1149 | + ) |
| 1150 | + |
| 1151 | + # combine iterates from all three solves |
| 1152 | + sol3.n_it = sol.n_it + sol2.n_it + sol3.n_it |
| 1153 | + if store_iterates: |
| 1154 | + sol3.iterates.x = np.concatenate( |
| 1155 | + (sol.iterates.x, sol2.iterates.x, sol3.iterates.x), axis=0 |
| 1156 | + ) |
| 1157 | + sol3.iterates.F = np.concatenate( |
| 1158 | + (sol.iterates.F, sol2.iterates.F, sol3.iterates.F), axis=0 |
| 1159 | + ) |
| 1160 | + sol3.iterates.lmda = np.concatenate( |
| 1161 | + (sol.iterates.lmda, sol2.iterates.lmda, sol3.iterates.lmda), |
| 1162 | + axis=0, |
| 1163 | + ) |
| 1164 | + sol = sol3 |
1102 | 1165 |
|
1103 | 1166 | if sol.success and len(assemblage.reaction_affinities) > 0.0: |
1104 | 1167 | maxres = np.max(np.abs(assemblage.reaction_affinities)) + 1.0e-5 |
|
0 commit comments