MultiOptPy 1.20.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multioptpy/Calculator/__init__.py +0 -0
- multioptpy/Calculator/ase_calculation_tools.py +424 -0
- multioptpy/Calculator/ase_tools/__init__.py +0 -0
- multioptpy/Calculator/ase_tools/fairchem.py +28 -0
- multioptpy/Calculator/ase_tools/gamess.py +19 -0
- multioptpy/Calculator/ase_tools/gaussian.py +165 -0
- multioptpy/Calculator/ase_tools/mace.py +28 -0
- multioptpy/Calculator/ase_tools/mopac.py +19 -0
- multioptpy/Calculator/ase_tools/nwchem.py +31 -0
- multioptpy/Calculator/ase_tools/orca.py +22 -0
- multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
- multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
- multioptpy/Calculator/emt_calculation_tools.py +458 -0
- multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
- multioptpy/Calculator/lj_calculation_tools.py +314 -0
- multioptpy/Calculator/psi4_calculation_tools.py +334 -0
- multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
- multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
- multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
- multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
- multioptpy/Calculator/tblite_calculation_tools.py +352 -0
- multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
- multioptpy/Constraint/__init__.py +0 -0
- multioptpy/Constraint/constraint_condition.py +834 -0
- multioptpy/Coordinate/__init__.py +0 -0
- multioptpy/Coordinate/polar_coordinate.py +199 -0
- multioptpy/Coordinate/redundant_coordinate.py +638 -0
- multioptpy/IRC/__init__.py +0 -0
- multioptpy/IRC/converge_criteria.py +28 -0
- multioptpy/IRC/dvv.py +544 -0
- multioptpy/IRC/euler.py +439 -0
- multioptpy/IRC/hpc.py +564 -0
- multioptpy/IRC/lqa.py +540 -0
- multioptpy/IRC/modekill.py +662 -0
- multioptpy/IRC/rk4.py +579 -0
- multioptpy/Interpolation/__init__.py +0 -0
- multioptpy/Interpolation/adaptive_interpolation.py +283 -0
- multioptpy/Interpolation/binomial_interpolation.py +179 -0
- multioptpy/Interpolation/geodesic_interpolation.py +785 -0
- multioptpy/Interpolation/interpolation.py +156 -0
- multioptpy/Interpolation/linear_interpolation.py +473 -0
- multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
- multioptpy/Interpolation/spline_interpolation.py +353 -0
- multioptpy/MD/__init__.py +0 -0
- multioptpy/MD/thermostat.py +185 -0
- multioptpy/MEP/__init__.py +0 -0
- multioptpy/MEP/pathopt_bneb_force.py +443 -0
- multioptpy/MEP/pathopt_dmf_force.py +448 -0
- multioptpy/MEP/pathopt_dneb_force.py +130 -0
- multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
- multioptpy/MEP/pathopt_gpneb_force.py +512 -0
- multioptpy/MEP/pathopt_lup_force.py +113 -0
- multioptpy/MEP/pathopt_neb_force.py +225 -0
- multioptpy/MEP/pathopt_nesb_force.py +205 -0
- multioptpy/MEP/pathopt_om_force.py +153 -0
- multioptpy/MEP/pathopt_qsm_force.py +174 -0
- multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
- multioptpy/ModelFunction/__init__.py +7 -0
- multioptpy/ModelFunction/avoiding_model_function.py +29 -0
- multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
- multioptpy/ModelFunction/conical_model_function.py +26 -0
- multioptpy/ModelFunction/opt_meci.py +50 -0
- multioptpy/ModelFunction/opt_mesx.py +47 -0
- multioptpy/ModelFunction/opt_mesx_2.py +49 -0
- multioptpy/ModelFunction/seam_model_function.py +27 -0
- multioptpy/ModelHessian/__init__.py +0 -0
- multioptpy/ModelHessian/approx_hessian.py +147 -0
- multioptpy/ModelHessian/calc_params.py +227 -0
- multioptpy/ModelHessian/fischer.py +236 -0
- multioptpy/ModelHessian/fischerd3.py +360 -0
- multioptpy/ModelHessian/fischerd4.py +398 -0
- multioptpy/ModelHessian/gfn0xtb.py +633 -0
- multioptpy/ModelHessian/gfnff.py +709 -0
- multioptpy/ModelHessian/lindh.py +165 -0
- multioptpy/ModelHessian/lindh2007d2.py +707 -0
- multioptpy/ModelHessian/lindh2007d3.py +822 -0
- multioptpy/ModelHessian/lindh2007d4.py +1030 -0
- multioptpy/ModelHessian/morse.py +106 -0
- multioptpy/ModelHessian/schlegel.py +144 -0
- multioptpy/ModelHessian/schlegeld3.py +322 -0
- multioptpy/ModelHessian/schlegeld4.py +559 -0
- multioptpy/ModelHessian/shortrange.py +346 -0
- multioptpy/ModelHessian/swartd2.py +496 -0
- multioptpy/ModelHessian/swartd3.py +706 -0
- multioptpy/ModelHessian/swartd4.py +918 -0
- multioptpy/ModelHessian/tshess.py +40 -0
- multioptpy/Optimizer/QHAdam.py +61 -0
- multioptpy/Optimizer/__init__.py +0 -0
- multioptpy/Optimizer/abc_fire.py +83 -0
- multioptpy/Optimizer/adabelief.py +58 -0
- multioptpy/Optimizer/adabound.py +68 -0
- multioptpy/Optimizer/adadelta.py +65 -0
- multioptpy/Optimizer/adaderivative.py +56 -0
- multioptpy/Optimizer/adadiff.py +68 -0
- multioptpy/Optimizer/adafactor.py +70 -0
- multioptpy/Optimizer/adam.py +65 -0
- multioptpy/Optimizer/adamax.py +62 -0
- multioptpy/Optimizer/adamod.py +83 -0
- multioptpy/Optimizer/adamw.py +65 -0
- multioptpy/Optimizer/adiis.py +523 -0
- multioptpy/Optimizer/afire_neb.py +282 -0
- multioptpy/Optimizer/block_hessian_update.py +709 -0
- multioptpy/Optimizer/c2diis.py +491 -0
- multioptpy/Optimizer/component_wise_scaling.py +405 -0
- multioptpy/Optimizer/conjugate_gradient.py +82 -0
- multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
- multioptpy/Optimizer/coordinate_locking.py +405 -0
- multioptpy/Optimizer/dic_rsirfo.py +1015 -0
- multioptpy/Optimizer/ediis.py +417 -0
- multioptpy/Optimizer/eve.py +76 -0
- multioptpy/Optimizer/fastadabelief.py +61 -0
- multioptpy/Optimizer/fire.py +77 -0
- multioptpy/Optimizer/fire2.py +249 -0
- multioptpy/Optimizer/fire_neb.py +92 -0
- multioptpy/Optimizer/gan_step.py +486 -0
- multioptpy/Optimizer/gdiis.py +609 -0
- multioptpy/Optimizer/gediis.py +203 -0
- multioptpy/Optimizer/geodesic_step.py +433 -0
- multioptpy/Optimizer/gpmin.py +633 -0
- multioptpy/Optimizer/gpr_step.py +364 -0
- multioptpy/Optimizer/gradientdescent.py +78 -0
- multioptpy/Optimizer/gradientdescent_neb.py +52 -0
- multioptpy/Optimizer/hessian_update.py +433 -0
- multioptpy/Optimizer/hybrid_rfo.py +998 -0
- multioptpy/Optimizer/kdiis.py +625 -0
- multioptpy/Optimizer/lars.py +21 -0
- multioptpy/Optimizer/lbfgs.py +253 -0
- multioptpy/Optimizer/lbfgs_neb.py +355 -0
- multioptpy/Optimizer/linesearch.py +236 -0
- multioptpy/Optimizer/lookahead.py +40 -0
- multioptpy/Optimizer/nadam.py +64 -0
- multioptpy/Optimizer/newton.py +200 -0
- multioptpy/Optimizer/prodigy.py +70 -0
- multioptpy/Optimizer/purtubation.py +16 -0
- multioptpy/Optimizer/quickmin_neb.py +245 -0
- multioptpy/Optimizer/radam.py +75 -0
- multioptpy/Optimizer/rfo_neb.py +302 -0
- multioptpy/Optimizer/ric_rfo.py +842 -0
- multioptpy/Optimizer/rl_step.py +627 -0
- multioptpy/Optimizer/rmspropgrave.py +65 -0
- multioptpy/Optimizer/rsirfo.py +1647 -0
- multioptpy/Optimizer/rsprfo.py +1056 -0
- multioptpy/Optimizer/sadam.py +60 -0
- multioptpy/Optimizer/samsgrad.py +63 -0
- multioptpy/Optimizer/tr_lbfgs.py +678 -0
- multioptpy/Optimizer/trim.py +273 -0
- multioptpy/Optimizer/trust_radius.py +207 -0
- multioptpy/Optimizer/trust_radius_neb.py +121 -0
- multioptpy/Optimizer/yogi.py +60 -0
- multioptpy/OtherMethod/__init__.py +0 -0
- multioptpy/OtherMethod/addf.py +1150 -0
- multioptpy/OtherMethod/dimer.py +895 -0
- multioptpy/OtherMethod/elastic_image_pair.py +629 -0
- multioptpy/OtherMethod/modelfunction.py +456 -0
- multioptpy/OtherMethod/newton_traj.py +454 -0
- multioptpy/OtherMethod/twopshs.py +1095 -0
- multioptpy/PESAnalyzer/__init__.py +0 -0
- multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
- multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
- multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
- multioptpy/PESAnalyzer/pca_analysis.py +314 -0
- multioptpy/Parameters/__init__.py +0 -0
- multioptpy/Parameters/atomic_mass.py +20 -0
- multioptpy/Parameters/atomic_number.py +22 -0
- multioptpy/Parameters/covalent_radii.py +44 -0
- multioptpy/Parameters/d2.py +61 -0
- multioptpy/Parameters/d3.py +63 -0
- multioptpy/Parameters/d4.py +103 -0
- multioptpy/Parameters/dreiding.py +34 -0
- multioptpy/Parameters/gfn0xtb_param.py +137 -0
- multioptpy/Parameters/gfnff_param.py +315 -0
- multioptpy/Parameters/gnb.py +104 -0
- multioptpy/Parameters/parameter.py +22 -0
- multioptpy/Parameters/uff.py +72 -0
- multioptpy/Parameters/unit_values.py +20 -0
- multioptpy/Potential/AFIR_potential.py +55 -0
- multioptpy/Potential/LJ_repulsive_potential.py +345 -0
- multioptpy/Potential/__init__.py +0 -0
- multioptpy/Potential/anharmonic_keep_potential.py +28 -0
- multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
- multioptpy/Potential/electrostatic_potential.py +69 -0
- multioptpy/Potential/flux_potential.py +30 -0
- multioptpy/Potential/gaussian_potential.py +101 -0
- multioptpy/Potential/idpp.py +516 -0
- multioptpy/Potential/keep_angle_potential.py +146 -0
- multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
- multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
- multioptpy/Potential/keep_potential.py +99 -0
- multioptpy/Potential/mechano_force_potential.py +74 -0
- multioptpy/Potential/nanoreactor_potential.py +52 -0
- multioptpy/Potential/potential.py +896 -0
- multioptpy/Potential/spacer_model_potential.py +221 -0
- multioptpy/Potential/switching_potential.py +258 -0
- multioptpy/Potential/universal_potential.py +34 -0
- multioptpy/Potential/value_range_potential.py +36 -0
- multioptpy/Potential/void_point_potential.py +25 -0
- multioptpy/SQM/__init__.py +0 -0
- multioptpy/SQM/sqm1/__init__.py +0 -0
- multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
- multioptpy/SQM/sqm2/__init__.py +0 -0
- multioptpy/SQM/sqm2/calc_tools.py +95 -0
- multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
- multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
- multioptpy/SQM/sqm2/sqm2_core.py +303 -0
- multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
- multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
- multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
- multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
- multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
- multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
- multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
- multioptpy/Thermo/__init__.py +0 -0
- multioptpy/Thermo/normal_mode_analyzer.py +865 -0
- multioptpy/Utils/__init__.py +0 -0
- multioptpy/Utils/bond_connectivity.py +264 -0
- multioptpy/Utils/calc_tools.py +884 -0
- multioptpy/Utils/oniom.py +96 -0
- multioptpy/Utils/pbc.py +48 -0
- multioptpy/Utils/riemann_curvature.py +208 -0
- multioptpy/Utils/symmetry_analyzer.py +482 -0
- multioptpy/Visualization/__init__.py +0 -0
- multioptpy/Visualization/visualization.py +156 -0
- multioptpy/WFAnalyzer/MO_analysis.py +104 -0
- multioptpy/WFAnalyzer/__init__.py +0 -0
- multioptpy/Wrapper/__init__.py +0 -0
- multioptpy/Wrapper/autots.py +1239 -0
- multioptpy/Wrapper/ieip_wrapper.py +93 -0
- multioptpy/Wrapper/md_wrapper.py +92 -0
- multioptpy/Wrapper/neb_wrapper.py +94 -0
- multioptpy/Wrapper/optimize_wrapper.py +76 -0
- multioptpy/__init__.py +5 -0
- multioptpy/entrypoints.py +916 -0
- multioptpy/fileio.py +660 -0
- multioptpy/ieip.py +340 -0
- multioptpy/interface.py +1086 -0
- multioptpy/irc.py +529 -0
- multioptpy/moleculardynamics.py +432 -0
- multioptpy/neb.py +1267 -0
- multioptpy/optimization.py +1553 -0
- multioptpy/optimizer.py +709 -0
- multioptpy-1.20.2.dist-info/METADATA +438 -0
- multioptpy-1.20.2.dist-info/RECORD +246 -0
- multioptpy-1.20.2.dist-info/WHEEL +5 -0
- multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
- multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
- multioptpy-1.20.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.optimize import newton
|
|
3
|
+
|
|
4
|
+
from multioptpy.Utils.calc_tools import Calculationtools
|
|
5
|
+
|
|
6
|
+
class TRIM:
|
|
7
|
+
def __init__(self, saddle_order=0):
|
|
8
|
+
"""
|
|
9
|
+
Trust Region Image Minimization (TRIM) for transition state optimization.
|
|
10
|
+
|
|
11
|
+
ref.: https://doi.org/10.1016/0009-2614(91)90115-P
|
|
12
|
+
Helgaker, 1991
|
|
13
|
+
"""
|
|
14
|
+
# Trust region parameters
|
|
15
|
+
self.trust_radius = 0.3 # Default trust radius
|
|
16
|
+
self.trust_radius_min = 0.01 # Minimum trust radius
|
|
17
|
+
self.trust_radius_max = 0.5 # Maximum trust radius
|
|
18
|
+
|
|
19
|
+
# Transition state mode parameters
|
|
20
|
+
self.roots = [0] # Indices of eigenvalues to follow uphill (default: lowest mode)
|
|
21
|
+
self.ts_mode_history = [] # History of transition state modes
|
|
22
|
+
self.mode_following_threshold = 0.7 # Dot product threshold for mode following
|
|
23
|
+
|
|
24
|
+
# Step control parameters
|
|
25
|
+
self.max_step_norm = 0.5 # Maximum allowed step size
|
|
26
|
+
self.min_eigenvalue_shift = 1e-8 # Minimum eigenvalue shift for numerical stability
|
|
27
|
+
|
|
28
|
+
# Convergence tracking
|
|
29
|
+
self.predicted_energy_changes = []
|
|
30
|
+
self.iter = 0
|
|
31
|
+
self.H = None # Store Hessian for energy prediction
|
|
32
|
+
self.saddle_order = saddle_order # Desired saddle order
|
|
33
|
+
# Logging and diagnostics
|
|
34
|
+
self.debug = False
|
|
35
|
+
|
|
36
|
+
def log(self, message):
|
|
37
|
+
"""Print log message if debug is enabled."""
|
|
38
|
+
if self.debug:
|
|
39
|
+
print(f"TRIM: {message}")
|
|
40
|
+
else:
|
|
41
|
+
# Always print important information even when debug is disabled
|
|
42
|
+
if "μ=" in message or "norm(step)" in message:
|
|
43
|
+
print(f"TRIM: {message}")
|
|
44
|
+
|
|
45
|
+
def update_ts_mode(self, eigvals, eigvecs):
|
|
46
|
+
"""
|
|
47
|
+
Update the transition state mode to follow based on eigenvalues and eigenvectors.
|
|
48
|
+
|
|
49
|
+
Parameters:
|
|
50
|
+
-----------
|
|
51
|
+
eigvals : numpy.ndarray
|
|
52
|
+
Eigenvalues of the Hessian matrix
|
|
53
|
+
eigvecs : numpy.ndarray
|
|
54
|
+
Eigenvectors of the Hessian matrix
|
|
55
|
+
"""
|
|
56
|
+
# Sort eigenvalues to find the lowest (potentially negative) ones
|
|
57
|
+
idx = np.argsort(eigvals)
|
|
58
|
+
|
|
59
|
+
# Default to following the lowest mode (idx[0])
|
|
60
|
+
if not self.ts_mode_history:
|
|
61
|
+
self.roots = [idx[0:self.saddle_order]]
|
|
62
|
+
self.ts_mode_history.append(eigvecs[:, idx[0:self.saddle_order]])
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
# If we have history, ensure we follow the consistent mode
|
|
66
|
+
prev_mode = self.ts_mode_history[-1]
|
|
67
|
+
overlaps = np.abs([np.dot(prev_mode.flatten(), eigvecs[:, i].flatten()) for i in idx[:3]])
|
|
68
|
+
|
|
69
|
+
# Find the mode with highest overlap with previous mode
|
|
70
|
+
max_overlap_idx = np.argmax(overlaps)
|
|
71
|
+
if overlaps[max_overlap_idx] > self.mode_following_threshold:
|
|
72
|
+
# Use the mode with highest overlap
|
|
73
|
+
self.roots = [idx[max_overlap_idx]]
|
|
74
|
+
else:
|
|
75
|
+
# Default to lowest eigenvalue if no good overlap
|
|
76
|
+
self.roots = [idx[0:self.saddle_order]]
|
|
77
|
+
|
|
78
|
+
self.ts_mode_history.append(eigvecs[:, self.roots[0:self.saddle_order]])
|
|
79
|
+
self.log(f"Following mode with eigenvalue {eigvals[self.roots[0]]:.6f}")
|
|
80
|
+
|
|
81
|
+
def quadratic_model(self, gradient, hessian, step):
|
|
82
|
+
"""
|
|
83
|
+
Predict energy change using quadratic model.
|
|
84
|
+
|
|
85
|
+
Parameters:
|
|
86
|
+
-----------
|
|
87
|
+
gradient : numpy.ndarray
|
|
88
|
+
Current gradient
|
|
89
|
+
hessian : numpy.ndarray
|
|
90
|
+
Current Hessian matrix
|
|
91
|
+
step : numpy.ndarray
|
|
92
|
+
Step vector
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
--------
|
|
96
|
+
float
|
|
97
|
+
Predicted energy change
|
|
98
|
+
"""
|
|
99
|
+
step_flat = step.flatten()
|
|
100
|
+
grad_flat = gradient.flatten()
|
|
101
|
+
|
|
102
|
+
linear_term = np.dot(grad_flat, step_flat)
|
|
103
|
+
quadratic_term = 0.5 * np.dot(step_flat, np.dot(hessian, step_flat))
|
|
104
|
+
|
|
105
|
+
return linear_term + quadratic_term
|
|
106
|
+
|
|
107
|
+
def get_step(self, forces, hessian, eigvals, eigvecs):
|
|
108
|
+
"""
|
|
109
|
+
Calculate the TRIM step.
|
|
110
|
+
|
|
111
|
+
Parameters:
|
|
112
|
+
-----------
|
|
113
|
+
|
|
114
|
+
forces : numpy.ndarray
|
|
115
|
+
Current forces (-gradient)
|
|
116
|
+
hessian : numpy.ndarray
|
|
117
|
+
Current Hessian matrix
|
|
118
|
+
eigvals : numpy.ndarray
|
|
119
|
+
Eigenvalues of the Hessian
|
|
120
|
+
eigvecs : numpy.ndarray
|
|
121
|
+
Eigenvectors of the Hessian
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
--------
|
|
125
|
+
numpy.ndarray
|
|
126
|
+
Calculated step vector
|
|
127
|
+
"""
|
|
128
|
+
gradient = -forces
|
|
129
|
+
self.H = hessian # Store for energy prediction
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# Update transition state mode
|
|
133
|
+
if self.saddle_order > 0:
|
|
134
|
+
self.update_ts_mode(eigvals, eigvecs)
|
|
135
|
+
print(f"Signs of eigenvalue and -vector of root(s) {self.roots} will be reversed!")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# Transform gradient to basis of eigenvectors
|
|
139
|
+
gradient_ = eigvecs.T.dot(gradient.flatten())
|
|
140
|
+
|
|
141
|
+
if self.saddle_order > 0:
|
|
142
|
+
# Construct image function by inverting the signs of the eigenvalue and
|
|
143
|
+
# -vector of the mode to follow uphill.
|
|
144
|
+
eigvals_ = eigvals.copy()
|
|
145
|
+
eigvals_[self.roots] *= -1
|
|
146
|
+
gradient_ = gradient_.copy()
|
|
147
|
+
gradient_[self.roots] *= -1
|
|
148
|
+
else:
|
|
149
|
+
eigvals_ = eigvals.copy()
|
|
150
|
+
gradient_ = gradient_.copy()
|
|
151
|
+
|
|
152
|
+
def get_step(mu):
|
|
153
|
+
"""Calculate step with level shift parameter mu."""
|
|
154
|
+
zetas = -gradient_ / (eigvals_ - mu)
|
|
155
|
+
# Replace nan with 0.
|
|
156
|
+
zetas = np.nan_to_num(zetas)
|
|
157
|
+
# Transform to original basis
|
|
158
|
+
step = np.dot(eigvecs, zetas[:, np.newaxis])
|
|
159
|
+
return step
|
|
160
|
+
|
|
161
|
+
def get_step_norm(mu):
|
|
162
|
+
"""Calculate norm of step with level shift parameter mu."""
|
|
163
|
+
return np.linalg.norm(get_step(mu))
|
|
164
|
+
|
|
165
|
+
def func(mu):
|
|
166
|
+
"""Function to find mu such that step norm equals trust radius."""
|
|
167
|
+
return get_step_norm(mu) - self.trust_radius
|
|
168
|
+
|
|
169
|
+
# Initialize level shift parameter
|
|
170
|
+
mu = 0
|
|
171
|
+
norm0 = get_step_norm(mu)
|
|
172
|
+
|
|
173
|
+
# Apply trust radius constraint if needed
|
|
174
|
+
if norm0 > self.trust_radius:
|
|
175
|
+
try:
|
|
176
|
+
mu, res = newton(func, x0=mu, full_output=True)
|
|
177
|
+
if res.converged:
|
|
178
|
+
self.log(f"Using levelshift of μ={mu:.4f}")
|
|
179
|
+
else:
|
|
180
|
+
self.log("Newton method for levelshift did not converge, using simple scaling")
|
|
181
|
+
mu = 0
|
|
182
|
+
step = get_step(mu)
|
|
183
|
+
step = step * (self.trust_radius / norm0)
|
|
184
|
+
return step
|
|
185
|
+
except:
|
|
186
|
+
self.log("Error in levelshift calculation, using simple scaling")
|
|
187
|
+
mu = 0
|
|
188
|
+
step = get_step(mu)
|
|
189
|
+
step = step * (self.trust_radius / norm0)
|
|
190
|
+
return step
|
|
191
|
+
else:
|
|
192
|
+
self.log("Took pure newton step without levelshift")
|
|
193
|
+
|
|
194
|
+
# Calculate final step
|
|
195
|
+
step = get_step(mu)
|
|
196
|
+
step_norm = np.linalg.norm(step)
|
|
197
|
+
self.log(f"norm(step)={step_norm:.6f}")
|
|
198
|
+
|
|
199
|
+
# Store predicted energy change
|
|
200
|
+
predicted_change = self.quadratic_model(gradient, self.H, step)
|
|
201
|
+
self.predicted_energy_changes.append(predicted_change)
|
|
202
|
+
|
|
203
|
+
return step
|
|
204
|
+
|
|
205
|
+
def run(self, geom_num_list, B_g, hessian, trust_radius, original_move_vector):
|
|
206
|
+
"""
|
|
207
|
+
Run TRIM optimization step
|
|
208
|
+
|
|
209
|
+
Parameters:
|
|
210
|
+
-----------
|
|
211
|
+
geom_num_list : numpy.ndarray
|
|
212
|
+
Current geometry
|
|
213
|
+
energy : float
|
|
214
|
+
Current energy value
|
|
215
|
+
B_g : numpy.ndarray
|
|
216
|
+
Current gradient (forces with sign flipped)
|
|
217
|
+
original_move_vector : numpy.ndarray
|
|
218
|
+
Step calculated by the original method (ignored in TRIM)
|
|
219
|
+
hessian : numpy.ndarray, optional
|
|
220
|
+
Current Hessian matrix (required for TRIM)
|
|
221
|
+
trust_radius : float
|
|
222
|
+
Trust radius for the optimization step
|
|
223
|
+
original_move_vector : numpy.ndarray
|
|
224
|
+
Step calculated by the original method (ignored in TRIM)
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
--------
|
|
228
|
+
numpy.ndarray
|
|
229
|
+
Optimized step vector
|
|
230
|
+
"""
|
|
231
|
+
print("TRIM method")
|
|
232
|
+
n_coords = len(geom_num_list)
|
|
233
|
+
step_norm = np.linalg.norm(original_move_vector)
|
|
234
|
+
print(f"Original step norm: {step_norm:.6f}, Trust radius: {trust_radius:.6f}")
|
|
235
|
+
self.trust_radius = trust_radius
|
|
236
|
+
if step_norm < self.trust_radius:
|
|
237
|
+
print("Step is within trust radius, using original move vector")
|
|
238
|
+
return original_move_vector
|
|
239
|
+
|
|
240
|
+
# Check if Hessian is provided
|
|
241
|
+
if hessian is None:
|
|
242
|
+
print("Error: Hessian matrix is required for TRIM method")
|
|
243
|
+
return original_move_vector # Fallback to steepest descent
|
|
244
|
+
|
|
245
|
+
hessian = Calculationtools().project_out_hess_tr_and_rot_for_coord(hessian, geom_num_list.reshape(-1, 3), geom_num_list.reshape(-1, 3), display_eigval=False)
|
|
246
|
+
|
|
247
|
+
# Compute eigenvalues and eigenvectors of the Hessian
|
|
248
|
+
try:
|
|
249
|
+
eigvals, eigvecs = np.linalg.eigh(hessian)
|
|
250
|
+
except np.linalg.LinAlgError:
|
|
251
|
+
print("Warning: Eigenvalue computation failed, using diagonal approximation")
|
|
252
|
+
# Use diagonal approximation if eigenvalue computation fails
|
|
253
|
+
H_diag = np.diag(np.diag(hessian))
|
|
254
|
+
eigvals = np.diag(H_diag)
|
|
255
|
+
eigvecs = np.eye(len(eigvals))
|
|
256
|
+
|
|
257
|
+
# Reshape forces for compatibility
|
|
258
|
+
forces = -B_g.reshape(-1) # Convert gradient to forces (sign flip)
|
|
259
|
+
|
|
260
|
+
# Calculate TRIM step
|
|
261
|
+
move_vector = self.get_step(forces, hessian, eigvals, eigvecs)
|
|
262
|
+
|
|
263
|
+
# Ensure step has correct shape
|
|
264
|
+
move_vector = -1*move_vector.reshape(n_coords, 1)
|
|
265
|
+
|
|
266
|
+
# Final safety checks
|
|
267
|
+
move_norm = np.linalg.norm(move_vector)
|
|
268
|
+
if move_norm < 1e-10 or np.any(np.isnan(move_vector)) or np.any(np.isinf(move_vector)):
|
|
269
|
+
print("Warning: Step issue detected, using scaled gradient instead")
|
|
270
|
+
move_vector = original_move_vector
|
|
271
|
+
|
|
272
|
+
self.iter += 1
|
|
273
|
+
return move_vector
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
class TrustRadius:
|
|
4
|
+
def __init__(self,
|
|
5
|
+
initial_trust_radius=0.3,
|
|
6
|
+
min_trust_radius=0.01,
|
|
7
|
+
max_trust_radius=0.5,
|
|
8
|
+
history_size=5,
|
|
9
|
+
adaptive_factor_scale=0.8,
|
|
10
|
+
energy_precision_threshold=1e-8,
|
|
11
|
+
quality='Normal'):
|
|
12
|
+
"""
|
|
13
|
+
Initialize the composite-step trust radius method
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
initial_trust_radius: Initial trust radius value
|
|
17
|
+
min_trust_radius: Minimum allowed trust radius
|
|
18
|
+
max_trust_radius: Maximum allowed trust radius
|
|
19
|
+
history_size: Number of previous steps to consider in history
|
|
20
|
+
adaptive_factor_scale: Scale factor for adaptive adjustment (0-1)
|
|
21
|
+
energy_precision_threshold: Threshold for energy precision
|
|
22
|
+
quality: Convergence quality setting ('VeryBasic', 'Basic', 'Normal', 'Good', 'VeryGood')
|
|
23
|
+
"""
|
|
24
|
+
# Basic parameters
|
|
25
|
+
self.trust_radius = initial_trust_radius
|
|
26
|
+
self.min_trust_radius = min_trust_radius
|
|
27
|
+
self.max_trust_radius = max_trust_radius
|
|
28
|
+
|
|
29
|
+
# Adaptive parameters
|
|
30
|
+
self.history_size = history_size
|
|
31
|
+
self.adaptive_factor_scale = adaptive_factor_scale
|
|
32
|
+
self.energy_precision_threshold = energy_precision_threshold
|
|
33
|
+
|
|
34
|
+
# History data
|
|
35
|
+
self.energy_ratios = [] # History of actual/predicted energy change ratios
|
|
36
|
+
self.step_sizes = [] # History of step sizes
|
|
37
|
+
self.energy_changes = [] # History of energy changes
|
|
38
|
+
|
|
39
|
+
# Optimization state
|
|
40
|
+
self.iteration_count = 0
|
|
41
|
+
self.last_energy = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# AMS convergence criteria based on quality setting
|
|
45
|
+
self.set_convergence_quality(quality)
|
|
46
|
+
|
|
47
|
+
def set_min_trust_radius(self, min_trust_radius):
|
|
48
|
+
"""Set minimum trust radius"""
|
|
49
|
+
self.min_trust_radius = min_trust_radius
|
|
50
|
+
|
|
51
|
+
def set_max_trust_radius(self, max_trust_radius):
|
|
52
|
+
"""Set maximum trust radius"""
|
|
53
|
+
self.max_trust_radius = max_trust_radius
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def set_convergence_quality(self, quality):
|
|
58
|
+
"""
|
|
59
|
+
Set convergence criteria based on quality setting
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
quality: Quality setting ('VeryBasic', 'Basic', 'Normal', 'Good', 'VeryGood')
|
|
63
|
+
"""
|
|
64
|
+
# Default to Normal if invalid quality is provided
|
|
65
|
+
if quality not in ['VeryBasic', 'Basic', 'Normal', 'Good', 'VeryGood']:
|
|
66
|
+
quality = 'Normal'
|
|
67
|
+
|
|
68
|
+
quality_map = {
|
|
69
|
+
'VeryBasic': {'energy': 1e-3, 'gradients': 1e-1, 'step': 1.0, 'stress': 5e-2},
|
|
70
|
+
'Basic': {'energy': 1e-4, 'gradients': 1e-2, 'step': 0.1, 'stress': 5e-3},
|
|
71
|
+
'Normal': {'energy': 1e-5, 'gradients': 1e-3, 'step': 0.01, 'stress': 5e-4},
|
|
72
|
+
'Good': {'energy': 1e-6, 'gradients': 1e-4, 'step': 0.001, 'stress': 5e-5},
|
|
73
|
+
'VeryGood': {'energy': 1e-7, 'gradients': 1e-5, 'step': 0.0001, 'stress': 5e-6}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
self.convergence_criteria = quality_map[quality]
|
|
77
|
+
self.quality = quality
|
|
78
|
+
|
|
79
|
+
def calculate_adaptive_factor(self):
|
|
80
|
+
"""
|
|
81
|
+
Calculate adaptive adjustment factor based on optimization history
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
float: Adaptive factor determining adjustment strength
|
|
85
|
+
"""
|
|
86
|
+
# Return default value if no history is available
|
|
87
|
+
if len(self.energy_ratios) == 0:
|
|
88
|
+
return 2.0
|
|
89
|
+
|
|
90
|
+
# Calculate based on recent prediction accuracy
|
|
91
|
+
recent_ratios = self.energy_ratios[-min(self.history_size, len(self.energy_ratios)):]
|
|
92
|
+
ratio_variance = np.var(recent_ratios) if len(recent_ratios) > 1 else 0.0
|
|
93
|
+
|
|
94
|
+
# More cautious adjustment when prediction variance is high
|
|
95
|
+
base_factor = 2.0 * np.exp(-ratio_variance)
|
|
96
|
+
|
|
97
|
+
# More cautious when approaching convergence
|
|
98
|
+
if self.is_approaching_convergence():
|
|
99
|
+
base_factor *= self.adaptive_factor_scale
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
return max(1.1, min(base_factor, 3.0)) # Limit to range 1.1-3.0
|
|
104
|
+
|
|
105
|
+
def is_approaching_convergence(self):
|
|
106
|
+
"""
|
|
107
|
+
Determine if optimization is approaching convergence
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
bool: True if approaching convergence
|
|
111
|
+
"""
|
|
112
|
+
if len(self.energy_changes) < 2:
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
# Check if recent energy changes are small and decreasing
|
|
116
|
+
recent_changes = np.abs(self.energy_changes[-min(3, len(self.energy_changes)):])
|
|
117
|
+
return np.all(recent_changes < 0.01) and np.mean(recent_changes) < 0.005
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def update_trust_radii(self,
|
|
121
|
+
B_e,
|
|
122
|
+
pre_B_e,
|
|
123
|
+
pre_B_g,
|
|
124
|
+
pre_move_vector,
|
|
125
|
+
model_hess,
|
|
126
|
+
geom_num_list,
|
|
127
|
+
trust_radii,
|
|
128
|
+
atom_types=None,
|
|
129
|
+
constraints=None):
|
|
130
|
+
"""
|
|
131
|
+
Update trust radius using composite-step approach
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
B_e: Current energy
|
|
135
|
+
pre_B_e: Previous energy
|
|
136
|
+
pre_B_g: Previous gradient
|
|
137
|
+
pre_move_vector: Previous movement vector
|
|
138
|
+
model_hess: Model Hessian
|
|
139
|
+
geom_num_list: Geometry number list
|
|
140
|
+
trust_radii: Current trust radius
|
|
141
|
+
atom_types: Optional list of atom types
|
|
142
|
+
constraints: Optional constraint information
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
float: Updated trust radius
|
|
146
|
+
"""
|
|
147
|
+
if self.iteration_count == 0:
|
|
148
|
+
self.iteration_count += 1
|
|
149
|
+
return trust_radii
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Calculate predicted energy change
|
|
153
|
+
Ce = (np.dot(pre_B_g.reshape(1, len(geom_num_list)),
|
|
154
|
+
pre_move_vector.reshape(len(geom_num_list), 1)) +
|
|
155
|
+
0.5 * np.dot(np.dot(pre_move_vector.reshape(1, len(geom_num_list)),
|
|
156
|
+
model_hess),
|
|
157
|
+
pre_move_vector.reshape(len(geom_num_list), 1)))
|
|
158
|
+
|
|
159
|
+
# Handle numerical stability
|
|
160
|
+
if abs(Ce) < self.energy_precision_threshold:
|
|
161
|
+
Ce += np.sign(Ce) * self.energy_precision_threshold
|
|
162
|
+
if abs(Ce) < self.energy_precision_threshold: # If sign is unknown
|
|
163
|
+
Ce = self.energy_precision_threshold
|
|
164
|
+
|
|
165
|
+
# Ratio of actual to predicted energy change
|
|
166
|
+
r = (pre_B_e - B_e) / Ce
|
|
167
|
+
|
|
168
|
+
# Update history
|
|
169
|
+
self.energy_ratios.append(float(r))
|
|
170
|
+
self.step_sizes.append(float(np.linalg.norm(pre_move_vector)))
|
|
171
|
+
self.energy_changes.append(float(pre_B_e - B_e))
|
|
172
|
+
|
|
173
|
+
# Calculate adaptive factor
|
|
174
|
+
adaptive_factor = self.calculate_adaptive_factor()
|
|
175
|
+
|
|
176
|
+
# Debug information
|
|
177
|
+
print(f"Iteration: {self.iteration_count}")
|
|
178
|
+
print(f"Energy ratio (actual/predicted): {r}")
|
|
179
|
+
print(f"Adaptive factor: {adaptive_factor}")
|
|
180
|
+
print(f"Current trust radius: {trust_radii}")
|
|
181
|
+
|
|
182
|
+
# Trust radius adjustment logic (improved version)
|
|
183
|
+
r_min = 0.25
|
|
184
|
+
r_good = 0.75
|
|
185
|
+
|
|
186
|
+
if r <= r_min or r >= (2.0 - r_min):
|
|
187
|
+
# Decrease trust radius for poor prediction
|
|
188
|
+
trust_radii /= adaptive_factor
|
|
189
|
+
print("Decrease trust radius")
|
|
190
|
+
elif r >= r_good and r <= (2.0 - r_good):
|
|
191
|
+
# For good prediction
|
|
192
|
+
if abs(np.linalg.norm(pre_move_vector) - trust_radii) < self.energy_precision_threshold:
|
|
193
|
+
# Increase trust radius if previous step was limited by trust radius
|
|
194
|
+
trust_radii *= adaptive_factor ** 0.5
|
|
195
|
+
print("Increase trust radius")
|
|
196
|
+
else:
|
|
197
|
+
# Otherwise maintain trust radius
|
|
198
|
+
print("Keep trust radius (good prediction)")
|
|
199
|
+
else:
|
|
200
|
+
# Maintain trust radius for moderate prediction
|
|
201
|
+
print("Keep trust radius (moderate prediction)")
|
|
202
|
+
|
|
203
|
+
self.iteration_count += 1
|
|
204
|
+
|
|
205
|
+
# Clip final trust radius between min and max values
|
|
206
|
+
return np.clip(trust_radii, self.min_trust_radius, self.max_trust_radius)
|
|
207
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TR_NEB:
|
|
5
|
+
def __init__(self, **config):
|
|
6
|
+
self.NEB_FOLDER_DIRECTORY = config.get("NEB_FOLDER_DIRECTORY", None)
|
|
7
|
+
self.fix_init_edge = config.get("fix_init_edge", False)
|
|
8
|
+
self.fix_end_edge = config.get("fix_end_edge", False)
|
|
9
|
+
self.apply_convergence_criteria = config.get("apply_convergence_criteria", False)
|
|
10
|
+
self.threshold_max_force = 0.00045
|
|
11
|
+
self.threshold_rms_force = 0.00030
|
|
12
|
+
self.threshold_max_displacement = 0.0018
|
|
13
|
+
self.threshold_rms_displacement = 0.0012
|
|
14
|
+
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
def TR_calc(self, geometry_num_list, total_force_list, total_delta, biased_energy_list, pre_biased_energy_list, pre_geom):
|
|
18
|
+
if self.fix_init_edge:
|
|
19
|
+
move_vector = [total_delta[0]*0.0]
|
|
20
|
+
else:
|
|
21
|
+
init_norm_move_vector = np.linalg.norm(total_delta[0])
|
|
22
|
+
init_tr = min(0.5, init_norm_move_vector)
|
|
23
|
+
if init_norm_move_vector < 1e-15:
|
|
24
|
+
move_vector = [total_delta[0]*0.0]
|
|
25
|
+
else:
|
|
26
|
+
move_vector = [init_tr * total_delta[0] / init_norm_move_vector]
|
|
27
|
+
|
|
28
|
+
trust_radii_1_list = []
|
|
29
|
+
trust_radii_2_list = []
|
|
30
|
+
|
|
31
|
+
for i in range(1, len(total_delta)-1):
|
|
32
|
+
#if biased_energy_list[i] > pre_biased_energy_list[i] and pre_geom is not None:
|
|
33
|
+
# total_delta[i] = pre_geom[i] - geometry_num_list[i]
|
|
34
|
+
# print("Energy increased... ")
|
|
35
|
+
|
|
36
|
+
trust_radii_1 = np.linalg.norm(geometry_num_list[i] - geometry_num_list[i-1]) / 2.0
|
|
37
|
+
trust_radii_2 = np.linalg.norm(geometry_num_list[i] - geometry_num_list[i+1]) / 2.0
|
|
38
|
+
|
|
39
|
+
trust_radii_1_list.append(str(trust_radii_1*2))
|
|
40
|
+
trust_radii_2_list.append(str(trust_radii_2*2))
|
|
41
|
+
|
|
42
|
+
normalized_vec_1 = (geometry_num_list[i-1] - geometry_num_list[i])/(np.linalg.norm(geometry_num_list[i-1] - geometry_num_list[i]) + 1e-15)
|
|
43
|
+
normalized_vec_2 = (geometry_num_list[i+1] - geometry_num_list[i])/(np.linalg.norm(geometry_num_list[i+1] - geometry_num_list[i]) + 1e-15)
|
|
44
|
+
normalized_delta = total_delta[i] / np.linalg.norm(total_delta[i])
|
|
45
|
+
|
|
46
|
+
cos_1 = np.sum(normalized_vec_1 * normalized_delta)
|
|
47
|
+
cos_2 = np.sum(normalized_vec_2 * normalized_delta)
|
|
48
|
+
|
|
49
|
+
force_move_vec_cos = np.sum(total_force_list[i] * total_delta[i]) / (np.linalg.norm(total_force_list[i]) * np.linalg.norm(total_delta[i]))
|
|
50
|
+
|
|
51
|
+
if force_move_vec_cos >= 0: #Projected velocity-verlet algorithm
|
|
52
|
+
if (cos_1 > 0 and cos_2 < 0) or (cos_1 < 0 and cos_2 > 0):
|
|
53
|
+
if np.linalg.norm(total_delta[i]) > trust_radii_1 and cos_1 > 0:
|
|
54
|
+
move_vector.append(total_delta[i]*trust_radii_1/np.linalg.norm(total_delta[i]))
|
|
55
|
+
|
|
56
|
+
elif np.linalg.norm(total_delta[i]) > trust_radii_2 and cos_2 > 0:
|
|
57
|
+
move_vector.append(total_delta[i]*trust_radii_2/np.linalg.norm(total_delta[i]))
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
move_vector.append(total_delta[i])
|
|
61
|
+
|
|
62
|
+
elif (cos_1 < 0 and cos_2 < 0):
|
|
63
|
+
move_vector.append(total_delta[i])
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
if np.linalg.norm(total_delta[i]) > trust_radii_1:
|
|
67
|
+
move_vector.append(total_delta[i]*trust_radii_1/np.linalg.norm(total_delta[i]))
|
|
68
|
+
|
|
69
|
+
elif np.linalg.norm(total_delta[i]) > trust_radii_2:
|
|
70
|
+
move_vector.append(total_delta[i]*trust_radii_2/np.linalg.norm(total_delta[i]))
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
move_vector.append(total_delta[i])
|
|
74
|
+
else:
|
|
75
|
+
print("no displacements (Projected velocity-verlet algorithm): # NODE "+str(i))
|
|
76
|
+
move_vector.append(total_delta[i] * 0.0)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
with open(self.NEB_FOLDER_DIRECTORY+"procrustes_distance_1.csv", "a") as f:
|
|
80
|
+
f.write(",".join(trust_radii_1_list)+"\n")
|
|
81
|
+
|
|
82
|
+
with open(self.NEB_FOLDER_DIRECTORY+"procrustes_distance_2.csv", "a") as f:
|
|
83
|
+
f.write(",".join(trust_radii_2_list)+"\n")
|
|
84
|
+
|
|
85
|
+
if self.fix_end_edge:
|
|
86
|
+
move_vector.append(total_delta[-1]*0.0)
|
|
87
|
+
else:
|
|
88
|
+
end_norm_move_vector = np.linalg.norm(total_delta[-1])
|
|
89
|
+
end_tr = min(0.5, end_norm_move_vector)
|
|
90
|
+
if end_norm_move_vector < 1e-15:
|
|
91
|
+
move_vector.append(total_delta[-1]*0.0)
|
|
92
|
+
else:
|
|
93
|
+
move_vector.append(end_tr * total_delta[-1] / end_norm_move_vector)
|
|
94
|
+
|
|
95
|
+
if self.apply_convergence_criteria:
|
|
96
|
+
move_vector = self.check_convergence(total_force_list, move_vector)
|
|
97
|
+
|
|
98
|
+
return move_vector
|
|
99
|
+
|
|
100
|
+
def check_convergence(self, total_force_list, move_vec_list):
|
|
101
|
+
|
|
102
|
+
for i in range(1, len(total_force_list)-1):
|
|
103
|
+
max_grad = np.max(total_force_list[i])
|
|
104
|
+
rms_grad = np.sqrt(np.sum(total_force_list[i]**2)/(len(total_force_list[i])*3))
|
|
105
|
+
max_move = np.max(move_vec_list[i])
|
|
106
|
+
rms_move = np.sqrt(np.sum(move_vec_list[i]**2)/(len(move_vec_list[i])*3))
|
|
107
|
+
print("--------------------")
|
|
108
|
+
print("NODE #"+str(i))
|
|
109
|
+
print(f"MAXIMUM NEB FORCE : {float(max_grad):12.8f}")
|
|
110
|
+
print(f"RMS NEB FORCE : {float(rms_grad):12.8f}")
|
|
111
|
+
print(f"MAXIMUM DISPLACEMENT : {float(max_move):12.8f}")
|
|
112
|
+
print(f"RMS DISPLACEMENT : {float(rms_move):12.8f}")
|
|
113
|
+
|
|
114
|
+
if max_grad < self.threshold_max_force and rms_grad < self.threshold_rms_force and max_move < self.threshold_max_displacement and rms_move < self.threshold_rms_displacement:
|
|
115
|
+
print("Converged?: YES")
|
|
116
|
+
move_vec_list[i] = move_vec_list[i]*0.0
|
|
117
|
+
else:
|
|
118
|
+
print("Converged?: NO")
|
|
119
|
+
print("--------------------")
|
|
120
|
+
return move_vec_list
|
|
121
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import copy
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class YOGI:
|
|
6
|
+
def __init__(self, **config):
|
|
7
|
+
#https://doi.org/10.1145/3441501.3441507
|
|
8
|
+
self.adam_count = 1
|
|
9
|
+
self.DELTA = 0.05
|
|
10
|
+
self.beta_m = 0.9
|
|
11
|
+
self.beta_v = 0.999
|
|
12
|
+
self.Epsilon = 1e-8
|
|
13
|
+
self.Initialization = True
|
|
14
|
+
self.config = config
|
|
15
|
+
self.hessian = None
|
|
16
|
+
self.bias_hessian = None
|
|
17
|
+
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
def run(self, geom_num_list, B_g, pre_B_g=[], pre_geom=[], B_e=0.0, pre_B_e=0.0, pre_move_vector=[], initial_geom_num_list=[], g=[], pre_g=[]):
|
|
21
|
+
print("YOGI")
|
|
22
|
+
if self.Initialization:
|
|
23
|
+
self.adam_m = geom_num_list * 0.0
|
|
24
|
+
self.adam_v = geom_num_list * 0.0
|
|
25
|
+
self.Initialization = False
|
|
26
|
+
|
|
27
|
+
adam_count = self.adam_count
|
|
28
|
+
adam_m = self.adam_m
|
|
29
|
+
adam_v = self.adam_v
|
|
30
|
+
new_adam_m = adam_m*0.0
|
|
31
|
+
new_adam_v = adam_v*0.0
|
|
32
|
+
|
|
33
|
+
for i in range(len(geom_num_list)):
|
|
34
|
+
new_adam_m[i] = copy.copy(self.beta_m*adam_m[i] + (1.0-self.beta_m)*(B_g[i]))
|
|
35
|
+
new_adam_v[i] = copy.copy(adam_v[i] - (1.0 - self.beta_v) * np.sign(adam_v[i] - B_g[i] ** 2) * (B_g[i])**2)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
move_vector = []
|
|
39
|
+
|
|
40
|
+
for i in range(len(geom_num_list)):
|
|
41
|
+
move_vector.append(self.DELTA*(new_adam_m[i])/np.sqrt((new_adam_v[i])+self.Epsilon))
|
|
42
|
+
|
|
43
|
+
self.adam_m = new_adam_m
|
|
44
|
+
self.adam_v = new_adam_v
|
|
45
|
+
self.adam_count += 1
|
|
46
|
+
|
|
47
|
+
return move_vector#Bohr
|
|
48
|
+
def set_hessian(self, hessian):
|
|
49
|
+
self.hessian = hessian
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
def set_bias_hessian(self, bias_hessian):
|
|
53
|
+
self.bias_hessian = bias_hessian
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
def get_hessian(self):
|
|
57
|
+
return self.hessian
|
|
58
|
+
|
|
59
|
+
def get_bias_hessian(self):
|
|
60
|
+
return self.bias_hessian
|
|
File without changes
|