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,629 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from multioptpy.Potential.potential import BiasPotentialCalculation
|
|
5
|
+
from multioptpy.Utils.calc_tools import Calculationtools
|
|
6
|
+
from multioptpy.Visualization.visualization import Graph
|
|
7
|
+
from multioptpy.Coordinate.polar_coordinate import cart2polar, polar2cart, cart_grad_2_polar_grad
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ElasticImagePair:
|
|
11
|
+
"""
|
|
12
|
+
Implementation of the Improved Elastic Image Pair (iEIP) method
|
|
13
|
+
for finding minimum energy paths and transition states.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, config):
|
|
16
|
+
self.config = config
|
|
17
|
+
|
|
18
|
+
def RMS(self, mat):
|
|
19
|
+
"""Calculate root mean square of a matrix"""
|
|
20
|
+
rms = np.sqrt(np.sum(mat**2))
|
|
21
|
+
return rms
|
|
22
|
+
|
|
23
|
+
def print_info(self, dat):
|
|
24
|
+
"""Print optimization information"""
|
|
25
|
+
print("[[opt information]]")
|
|
26
|
+
print(" image_1 image_2")
|
|
27
|
+
print("energy (normal) : "+str(dat["energy_1"])+" "+str(dat["energy_2"]))
|
|
28
|
+
print("energy (bias) : "+str(dat["bias_energy_1"])+" "+str(dat["bias_energy_2"]))
|
|
29
|
+
print("gradient (normal, RMS) : "+str(self.RMS(dat["gradient_1"]))+" "+str(self.RMS(dat["gradient_2"])))
|
|
30
|
+
print("gradient (bias, RMS) : "+str(self.RMS(dat["bias_gradient_1"]))+" "+str(self.RMS(dat["bias_gradient_2"])))
|
|
31
|
+
print("perpendicular_force (RMS) : "+str(self.RMS(dat["perp_force_1"]))+" "+str(self.RMS(dat["perp_force_2"])))
|
|
32
|
+
print("energy_difference_dependent_force (RMS): "+str(self.RMS(dat["delta_energy_force_1"]))+" "+str(self.RMS(dat["delta_energy_force_2"])))
|
|
33
|
+
print("distance_dependent_force (RMS) : "+str(self.RMS(dat["close_target_force"])))
|
|
34
|
+
print("Total_displacement (RMS) : "+str(self.RMS(dat["total_disp_1"]))+" "+str(self.RMS(dat["total_disp_2"])))
|
|
35
|
+
print("Image_distance : "+str(dat["delta_geometry"]))
|
|
36
|
+
|
|
37
|
+
print("[[threshold]]")
|
|
38
|
+
print("Image_distance : ", self.config.img_distance_convage_criterion)
|
|
39
|
+
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
def lbfgs_update(self, s_list, y_list, grad, m=10):
|
|
43
|
+
"""
|
|
44
|
+
L-BFGS algorithm to compute the search direction.
|
|
45
|
+
|
|
46
|
+
Parameters:
|
|
47
|
+
-----------
|
|
48
|
+
s_list : list of arrays
|
|
49
|
+
List of position differences (x_{k+1} - x_k)
|
|
50
|
+
y_list : list of arrays
|
|
51
|
+
List of gradient differences (g_{k+1} - g_k)
|
|
52
|
+
grad : array
|
|
53
|
+
Current gradient
|
|
54
|
+
m : int
|
|
55
|
+
Number of corrections to store
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
--------
|
|
59
|
+
array
|
|
60
|
+
Search direction
|
|
61
|
+
"""
|
|
62
|
+
k = len(s_list)
|
|
63
|
+
if k == 0:
|
|
64
|
+
return -grad
|
|
65
|
+
|
|
66
|
+
q = grad.copy()
|
|
67
|
+
alphas = np.zeros(k)
|
|
68
|
+
rhos = np.zeros(k)
|
|
69
|
+
|
|
70
|
+
# Compute rho values
|
|
71
|
+
for i in range(k):
|
|
72
|
+
rhos[i] = 1.0 / (np.dot(y_list[i], s_list[i]) + 1e-10)
|
|
73
|
+
|
|
74
|
+
# First loop (backward)
|
|
75
|
+
for i in range(k-1, -1, -1):
|
|
76
|
+
alphas[i] = rhos[i] * np.dot(s_list[i], q)
|
|
77
|
+
q = q - alphas[i] * y_list[i]
|
|
78
|
+
|
|
79
|
+
# Scaling factor
|
|
80
|
+
gamma = np.dot(s_list[-1], y_list[-1]) / (np.dot(y_list[-1], y_list[-1]) + 1e-10)
|
|
81
|
+
r = gamma * q
|
|
82
|
+
|
|
83
|
+
# Second loop (forward)
|
|
84
|
+
for i in range(k):
|
|
85
|
+
beta = rhos[i] * np.dot(y_list[i], r)
|
|
86
|
+
r = r + s_list[i] * (alphas[i] - beta)
|
|
87
|
+
|
|
88
|
+
return -r
|
|
89
|
+
|
|
90
|
+
def microiteration(self, SP1, SP2, FIO1, FIO2, file_directory_1, file_directory_2, element_list, init_electric_charge_and_multiplicity, final_electric_charge_and_multiplicity, prev_geom_num_list_1, prev_geom_num_list_2, iter):
|
|
91
|
+
"""
|
|
92
|
+
Perform microiterations to optimize geometries with adaptive trust radius
|
|
93
|
+
and polar coordinate representation.
|
|
94
|
+
"""
|
|
95
|
+
# Initialize L-BFGS storage
|
|
96
|
+
s_list_1, y_list_1 = [], []
|
|
97
|
+
s_list_2, y_list_2 = [], []
|
|
98
|
+
prev_grad_1, prev_grad_2 = None, None
|
|
99
|
+
prev_pos_1, prev_pos_2 = None, None
|
|
100
|
+
prev_energy_1, prev_energy_2 = None, None
|
|
101
|
+
max_lbfgs_memory = 10 # Store up to 10 previous steps
|
|
102
|
+
|
|
103
|
+
# Initialize trust region parameters with polar coordinate adaptations
|
|
104
|
+
# Base trust radius settings
|
|
105
|
+
trust_radius_1 = 0.015 # Initial trust radius (Bohr)
|
|
106
|
+
trust_radius_2 = 0.015
|
|
107
|
+
# Component weights for polar coordinates
|
|
108
|
+
radial_weight = 1.0 # Weight for radial components
|
|
109
|
+
angular_weight = 0.85 # Weight for angular components (smaller due to different scale)
|
|
110
|
+
# Trust radius limits
|
|
111
|
+
min_trust_radius = 0.01 # Minimum trust radius
|
|
112
|
+
max_trust_radius = 0.1 # Maximum trust radius
|
|
113
|
+
# Adjustment factors
|
|
114
|
+
good_step_factor = 1.25 # Increase factor for good steps
|
|
115
|
+
bad_step_factor = 0.5 # Decrease factor for bad steps
|
|
116
|
+
# Performance thresholds
|
|
117
|
+
excellent_ratio_threshold = 0.85 # Threshold for exceptionally good steps
|
|
118
|
+
very_bad_ratio_threshold = 0.2 # Threshold for very poor steps
|
|
119
|
+
|
|
120
|
+
for i in range(self.config.microiter_num):
|
|
121
|
+
|
|
122
|
+
energy_1, gradient_1, geom_num_list_1, error_flag_1 = SP1.single_point(file_directory_1, element_list, iter, init_electric_charge_and_multiplicity, self.config.force_data["xtb"])
|
|
123
|
+
energy_2, gradient_2, geom_num_list_2, error_flag_2 = SP2.single_point(file_directory_2, element_list, iter, final_electric_charge_and_multiplicity, self.config.force_data["xtb"])
|
|
124
|
+
|
|
125
|
+
BPC_1 = BiasPotentialCalculation(self.config.iEIP_FOLDER_DIRECTORY)
|
|
126
|
+
BPC_2 = BiasPotentialCalculation(self.config.iEIP_FOLDER_DIRECTORY)
|
|
127
|
+
|
|
128
|
+
_, bias_energy_1, bias_gradient_1, _ = BPC_1.main(energy_1, gradient_1, geom_num_list_1, element_list, self.config.force_data)
|
|
129
|
+
_, bias_energy_2, bias_gradient_2, _ = BPC_2.main(energy_2, gradient_2, geom_num_list_2, element_list, self.config.force_data)
|
|
130
|
+
|
|
131
|
+
if error_flag_1 or error_flag_2:
|
|
132
|
+
print("Error in QM calculation.")
|
|
133
|
+
with open(self.config.iEIP_FOLDER_DIRECTORY+"end.txt", "w") as f:
|
|
134
|
+
f.write("Error in QM calculation.")
|
|
135
|
+
break
|
|
136
|
+
|
|
137
|
+
microiter_force_1 = -cart_grad_2_polar_grad(geom_num_list_1.reshape(-1), bias_gradient_1.reshape(-1))
|
|
138
|
+
microiter_force_2 = -cart_grad_2_polar_grad(geom_num_list_2.reshape(-1), bias_gradient_2.reshape(-1))
|
|
139
|
+
microiter_force_1[0] = 0.0
|
|
140
|
+
microiter_force_2[0] = 0.0
|
|
141
|
+
|
|
142
|
+
# Update L-BFGS memory
|
|
143
|
+
if prev_grad_1 is not None and prev_pos_1 is not None:
|
|
144
|
+
s_1 = geom_num_list_1.reshape(-1) - prev_pos_1
|
|
145
|
+
y_1 = microiter_force_1 - prev_grad_1
|
|
146
|
+
|
|
147
|
+
# Only add if step and gradient difference are significant and curvature is positive
|
|
148
|
+
if np.linalg.norm(s_1) > 1e-10 and np.linalg.norm(y_1) > 1e-10 and np.dot(s_1, y_1) > 1e-10:
|
|
149
|
+
s_list_1.append(s_1)
|
|
150
|
+
y_list_1.append(y_1)
|
|
151
|
+
if len(s_list_1) > max_lbfgs_memory:
|
|
152
|
+
s_list_1.pop(0)
|
|
153
|
+
y_list_1.pop(0)
|
|
154
|
+
|
|
155
|
+
if prev_grad_2 is not None and prev_pos_2 is not None:
|
|
156
|
+
s_2 = geom_num_list_2.reshape(-1) - prev_pos_2
|
|
157
|
+
y_2 = microiter_force_2 - prev_grad_2
|
|
158
|
+
|
|
159
|
+
if np.linalg.norm(s_2) > 1e-10 and np.linalg.norm(y_2) > 1e-10 and np.dot(s_2, y_2) > 1e-10:
|
|
160
|
+
s_list_2.append(s_2)
|
|
161
|
+
y_list_2.append(y_2)
|
|
162
|
+
if len(s_list_2) > max_lbfgs_memory:
|
|
163
|
+
s_list_2.pop(0)
|
|
164
|
+
y_list_2.pop(0)
|
|
165
|
+
|
|
166
|
+
# Store current values for next iteration
|
|
167
|
+
prev_grad_1 = microiter_force_1.copy()
|
|
168
|
+
prev_grad_2 = microiter_force_2.copy()
|
|
169
|
+
prev_pos_1 = geom_num_list_1.reshape(-1).copy()
|
|
170
|
+
prev_pos_2 = geom_num_list_2.reshape(-1).copy()
|
|
171
|
+
|
|
172
|
+
if i % 10 == 0:
|
|
173
|
+
print("# Microiteration "+str(i))
|
|
174
|
+
print("Energy 1 :", energy_1)
|
|
175
|
+
print("Energy 2 :", energy_2)
|
|
176
|
+
print("Energy 1 (bias) :", bias_energy_1)
|
|
177
|
+
print("Energy 2 (bias) :", bias_energy_2)
|
|
178
|
+
print("RMS perpendicular force 1:", self.RMS(microiter_force_1[1:]))
|
|
179
|
+
print("RMS perpendicular force 2:", self.RMS(microiter_force_2[1:]))
|
|
180
|
+
print("Trust radius 1 :", trust_radius_1)
|
|
181
|
+
print("Trust radius 2 :", trust_radius_2)
|
|
182
|
+
|
|
183
|
+
polar_coord_1 = cart2polar(geom_num_list_1.reshape(-1), prev_geom_num_list_1.reshape(-1))
|
|
184
|
+
polar_coord_2 = cart2polar(geom_num_list_2.reshape(-1), prev_geom_num_list_2.reshape(-1))
|
|
185
|
+
|
|
186
|
+
# Apply L-BFGS to get initial step directions
|
|
187
|
+
if len(s_list_1) > 0:
|
|
188
|
+
total_disp_1 = self.lbfgs_update(s_list_1, y_list_1, -microiter_force_1)
|
|
189
|
+
else:
|
|
190
|
+
total_disp_1 = microiter_force_1.reshape(-1)
|
|
191
|
+
total_disp_1[0] = 0.0 # Keep the first component fixed
|
|
192
|
+
if len(s_list_2) > 0:
|
|
193
|
+
total_disp_2 = self.lbfgs_update(s_list_2, y_list_2, -microiter_force_2)
|
|
194
|
+
else:
|
|
195
|
+
total_disp_2 = microiter_force_2.reshape(-1)
|
|
196
|
+
total_disp_2[0] = 0.0 # Keep the first component fixed
|
|
197
|
+
# Create component-wise weight masks for polar coordinates
|
|
198
|
+
# Polar coordinate structure: [r, theta, phi, r, theta, phi, ...]
|
|
199
|
+
weights_1 = np.ones_like(total_disp_1)
|
|
200
|
+
weights_2 = np.ones_like(total_disp_2)
|
|
201
|
+
|
|
202
|
+
# Set different weights for radial and angular components
|
|
203
|
+
for j in range(0, len(weights_1), 3):
|
|
204
|
+
weights_1[j] = radial_weight # r component
|
|
205
|
+
weights_1[j+1:j+3] = angular_weight # theta, phi components
|
|
206
|
+
|
|
207
|
+
for j in range(0, len(weights_2), 3):
|
|
208
|
+
weights_2[j] = radial_weight # r component
|
|
209
|
+
weights_2[j+1:j+3] = angular_weight # theta, phi components
|
|
210
|
+
|
|
211
|
+
# Calculate weighted displacement norms for trust radius scaling
|
|
212
|
+
weighted_disp_1 = total_disp_1 * weights_1
|
|
213
|
+
weighted_disp_2 = total_disp_2 * weights_2
|
|
214
|
+
|
|
215
|
+
weighted_norm_1 = np.linalg.norm(weighted_disp_1)
|
|
216
|
+
weighted_norm_2 = np.linalg.norm(weighted_disp_2)
|
|
217
|
+
|
|
218
|
+
# Apply weighted trust region constraints
|
|
219
|
+
if weighted_norm_1 > trust_radius_1:
|
|
220
|
+
scale_factor_1 = trust_radius_1 / weighted_norm_1
|
|
221
|
+
total_disp_1 = total_disp_1 * scale_factor_1
|
|
222
|
+
|
|
223
|
+
if weighted_norm_2 > trust_radius_2:
|
|
224
|
+
scale_factor_2 = trust_radius_2 / weighted_norm_2
|
|
225
|
+
total_disp_2 = total_disp_2 * scale_factor_2
|
|
226
|
+
|
|
227
|
+
# Handle angular periodicities in polar coordinates
|
|
228
|
+
for j in range(1, len(total_disp_1), 3): # theta components
|
|
229
|
+
# Constrain theta (0 to π)
|
|
230
|
+
if polar_coord_1[j] + total_disp_1[j] > np.pi:
|
|
231
|
+
total_disp_1[j] = np.pi - polar_coord_1[j] - 1e-6
|
|
232
|
+
elif polar_coord_1[j] + total_disp_1[j] < 0:
|
|
233
|
+
total_disp_1[j] = -polar_coord_1[j] + 1e-6
|
|
234
|
+
|
|
235
|
+
# Constrain phi (0 to 2Ï€) - ensure shortest path
|
|
236
|
+
if j+1 < len(total_disp_1):
|
|
237
|
+
curr_phi = polar_coord_1[j+1]
|
|
238
|
+
target_phi = curr_phi + total_disp_1[j+1]
|
|
239
|
+
|
|
240
|
+
# Normalize phi to [0, 2Ï€]
|
|
241
|
+
while target_phi > 2*np.pi:
|
|
242
|
+
target_phi -= 2*np.pi
|
|
243
|
+
while target_phi < 0:
|
|
244
|
+
target_phi += 2*np.pi
|
|
245
|
+
|
|
246
|
+
# Find shortest angular path
|
|
247
|
+
phi_diff = target_phi - curr_phi
|
|
248
|
+
if abs(phi_diff) > np.pi:
|
|
249
|
+
if phi_diff > 0:
|
|
250
|
+
phi_diff -= 2*np.pi
|
|
251
|
+
else:
|
|
252
|
+
phi_diff += 2*np.pi
|
|
253
|
+
total_disp_1[j+1] = phi_diff
|
|
254
|
+
|
|
255
|
+
# Apply same periodic boundary handling for image 2
|
|
256
|
+
for j in range(1, len(total_disp_2), 3):
|
|
257
|
+
if polar_coord_2[j] + total_disp_2[j] > np.pi:
|
|
258
|
+
total_disp_2[j] = np.pi - polar_coord_2[j] - 1e-6
|
|
259
|
+
elif polar_coord_2[j] + total_disp_2[j] < 0:
|
|
260
|
+
total_disp_2[j] = -polar_coord_2[j] + 1e-6
|
|
261
|
+
|
|
262
|
+
if j+1 < len(total_disp_2):
|
|
263
|
+
curr_phi = polar_coord_2[j+1]
|
|
264
|
+
target_phi = curr_phi + total_disp_2[j+1]
|
|
265
|
+
|
|
266
|
+
while target_phi > 2*np.pi:
|
|
267
|
+
target_phi -= 2*np.pi
|
|
268
|
+
while target_phi < 0:
|
|
269
|
+
target_phi += 2*np.pi
|
|
270
|
+
|
|
271
|
+
phi_diff = target_phi - curr_phi
|
|
272
|
+
if abs(phi_diff) > np.pi:
|
|
273
|
+
if phi_diff > 0:
|
|
274
|
+
phi_diff -= 2*np.pi
|
|
275
|
+
else:
|
|
276
|
+
phi_diff += 2*np.pi
|
|
277
|
+
total_disp_2[j+1] = phi_diff
|
|
278
|
+
|
|
279
|
+
# Calculate predicted energy reduction
|
|
280
|
+
pred_reduction_1 = np.dot(microiter_force_1, total_disp_1)
|
|
281
|
+
pred_reduction_2 = np.dot(microiter_force_2, total_disp_2)
|
|
282
|
+
|
|
283
|
+
# Apply step
|
|
284
|
+
new_polar_coord_1 = polar_coord_1 + total_disp_1
|
|
285
|
+
new_polar_coord_2 = polar_coord_2 + total_disp_2
|
|
286
|
+
|
|
287
|
+
tmp_geom_1 = polar2cart(new_polar_coord_1, prev_geom_num_list_1.reshape(-1))
|
|
288
|
+
tmp_geom_2 = polar2cart(new_polar_coord_2, prev_geom_num_list_2.reshape(-1))
|
|
289
|
+
|
|
290
|
+
geom_num_list_1 = tmp_geom_1.reshape(len(geom_num_list_1), 3)
|
|
291
|
+
geom_num_list_2 = tmp_geom_2.reshape(len(geom_num_list_2), 3)
|
|
292
|
+
|
|
293
|
+
# Create input files with new geometries
|
|
294
|
+
new_geom_num_list_1_tolist = (geom_num_list_1*self.config.bohr2angstroms).tolist()
|
|
295
|
+
new_geom_num_list_2_tolist = (geom_num_list_2*self.config.bohr2angstroms).tolist()
|
|
296
|
+
for j, elem in enumerate(element_list):
|
|
297
|
+
new_geom_num_list_1_tolist[j].insert(0, elem)
|
|
298
|
+
new_geom_num_list_2_tolist[j].insert(0, elem)
|
|
299
|
+
|
|
300
|
+
new_geom_num_list_1_tolist.insert(0, init_electric_charge_and_multiplicity)
|
|
301
|
+
new_geom_num_list_2_tolist.insert(0, final_electric_charge_and_multiplicity)
|
|
302
|
+
|
|
303
|
+
file_directory_1 = FIO1.make_psi4_input_file([new_geom_num_list_1_tolist], iter)
|
|
304
|
+
file_directory_2 = FIO2.make_psi4_input_file([new_geom_num_list_2_tolist], iter)
|
|
305
|
+
|
|
306
|
+
# Update trust radius based on actual vs. predicted reduction
|
|
307
|
+
if prev_energy_1 is not None and prev_energy_2 is not None:
|
|
308
|
+
actual_reduction_1 = prev_energy_1 - bias_energy_1
|
|
309
|
+
actual_reduction_2 = prev_energy_2 - bias_energy_2
|
|
310
|
+
|
|
311
|
+
# Calculate performance ratio
|
|
312
|
+
ratio_1 = actual_reduction_1 / (pred_reduction_1 + 1e-10)
|
|
313
|
+
ratio_2 = actual_reduction_2 / (pred_reduction_2 + 1e-10)
|
|
314
|
+
|
|
315
|
+
# Update trust radius based on performance metrics
|
|
316
|
+
if ratio_1 < very_bad_ratio_threshold: # Very poor step
|
|
317
|
+
trust_radius_1 = max(trust_radius_1 * bad_step_factor, min_trust_radius)
|
|
318
|
+
elif ratio_1 > excellent_ratio_threshold and weighted_norm_1 >= 0.9 * trust_radius_1: # Excellent step
|
|
319
|
+
trust_radius_1 = min(trust_radius_1 * good_step_factor, max_trust_radius)
|
|
320
|
+
|
|
321
|
+
if ratio_2 < very_bad_ratio_threshold: # Very poor step
|
|
322
|
+
trust_radius_2 = max(trust_radius_2 * bad_step_factor, min_trust_radius)
|
|
323
|
+
elif ratio_2 > excellent_ratio_threshold and weighted_norm_2 >= 0.9 * trust_radius_2: # Excellent step
|
|
324
|
+
trust_radius_2 = min(trust_radius_2 * good_step_factor, max_trust_radius)
|
|
325
|
+
|
|
326
|
+
# Auto-adjust weights if large angular displacements are occurring
|
|
327
|
+
max_angle_disp_1 = max([abs(total_disp_1[j]) for j in range(1, len(total_disp_1), 3) if j < len(total_disp_1)])
|
|
328
|
+
max_angle_disp_2 = max([abs(total_disp_2[j]) for j in range(1, len(total_disp_2), 3) if j < len(total_disp_2)])
|
|
329
|
+
|
|
330
|
+
if max_angle_disp_1 > 0.3: # Threshold for excessive angular displacement
|
|
331
|
+
angular_weight = max(angular_weight * 0.9, 0.1) # Decrease angular weight
|
|
332
|
+
|
|
333
|
+
if max_angle_disp_2 > 0.3:
|
|
334
|
+
angular_weight = max(angular_weight * 0.9, 0.1)
|
|
335
|
+
|
|
336
|
+
# Store current energies for next iteration
|
|
337
|
+
prev_energy_1 = bias_energy_1
|
|
338
|
+
prev_energy_2 = bias_energy_2
|
|
339
|
+
|
|
340
|
+
# Convergence check
|
|
341
|
+
if self.RMS(microiter_force_1) < 0.01 and self.RMS(microiter_force_2) < 0.01:
|
|
342
|
+
print("Optimization converged.")
|
|
343
|
+
break
|
|
344
|
+
|
|
345
|
+
return energy_1, gradient_1, bias_energy_1, bias_gradient_1, geom_num_list_1, energy_2, gradient_2, bias_energy_2, bias_gradient_2, geom_num_list_2
|
|
346
|
+
|
|
347
|
+
def iteration(self, file_directory_1, file_directory_2, SP1, SP2, element_list, init_electric_charge_and_multiplicity, final_electric_charge_and_multiplicity, FIO1, FIO2):
|
|
348
|
+
"""
|
|
349
|
+
Main elastic image pair optimization iteration.
|
|
350
|
+
"""
|
|
351
|
+
G = Graph(self.config.iEIP_FOLDER_DIRECTORY)
|
|
352
|
+
beta_m = 0.9
|
|
353
|
+
beta_v = 0.999
|
|
354
|
+
BIAS_GRAD_LIST_A = []
|
|
355
|
+
BIAS_GRAD_LIST_B = []
|
|
356
|
+
BIAS_ENERGY_LIST_A = []
|
|
357
|
+
BIAS_ENERGY_LIST_B = []
|
|
358
|
+
|
|
359
|
+
GRAD_LIST_A = []
|
|
360
|
+
GRAD_LIST_B = []
|
|
361
|
+
ENERGY_LIST_A = []
|
|
362
|
+
ENERGY_LIST_B = []
|
|
363
|
+
prev_delta_geometry = 0.0
|
|
364
|
+
|
|
365
|
+
for iter in range(0, self.config.microiterlimit):
|
|
366
|
+
if os.path.isfile(self.config.iEIP_FOLDER_DIRECTORY+"end.txt"):
|
|
367
|
+
break
|
|
368
|
+
print("# ITR. "+str(iter))
|
|
369
|
+
|
|
370
|
+
energy_1, gradient_1, geom_num_list_1, error_flag_1 = SP1.single_point(file_directory_1, element_list, iter, init_electric_charge_and_multiplicity, self.config.force_data["xtb"])
|
|
371
|
+
energy_2, gradient_2, geom_num_list_2, error_flag_2 = SP2.single_point(file_directory_2, element_list, iter, final_electric_charge_and_multiplicity, self.config.force_data["xtb"])
|
|
372
|
+
geom_num_list_1, geom_num_list_2 = Calculationtools().kabsch_algorithm(geom_num_list_1, geom_num_list_2)
|
|
373
|
+
|
|
374
|
+
if error_flag_1 or error_flag_2:
|
|
375
|
+
print("Error in QM calculation.")
|
|
376
|
+
with open(self.config.iEIP_FOLDER_DIRECTORY+"end.txt", "w") as f:
|
|
377
|
+
f.write("Error in QM calculation.")
|
|
378
|
+
break
|
|
379
|
+
|
|
380
|
+
if iter == 0:
|
|
381
|
+
m_1 = gradient_1 * 0.0
|
|
382
|
+
m_2 = gradient_1 * 0.0
|
|
383
|
+
v_1 = gradient_1 * 0.0
|
|
384
|
+
v_2 = gradient_1 * 0.0
|
|
385
|
+
ini_geom_1 = geom_num_list_1
|
|
386
|
+
ini_geom_2 = geom_num_list_2
|
|
387
|
+
|
|
388
|
+
BPC_1 = BiasPotentialCalculation(self.config.iEIP_FOLDER_DIRECTORY)
|
|
389
|
+
BPC_2 = BiasPotentialCalculation(self.config.iEIP_FOLDER_DIRECTORY)
|
|
390
|
+
|
|
391
|
+
_, bias_energy_1, bias_gradient_1, _ = BPC_1.main(energy_1, gradient_1, geom_num_list_1, element_list, self.config.force_data)
|
|
392
|
+
_, bias_energy_2, bias_gradient_2, _ = BPC_2.main(energy_2, gradient_2, geom_num_list_2, element_list, self.config.force_data)
|
|
393
|
+
|
|
394
|
+
if self.config.microiter_num > 0 and iter > 0:
|
|
395
|
+
energy_1, gradient_1, bias_energy_1, bias_gradient_1, geom_num_list_1, energy_2, gradient_2, bias_energy_2, bias_gradient_2, geom_num_list_2 = self.microiteration(SP1, SP2, FIO1, FIO2, file_directory_1, file_directory_2, element_list, init_electric_charge_and_multiplicity, final_electric_charge_and_multiplicity, prev_geom_num_list_1, prev_geom_num_list_2, iter)
|
|
396
|
+
if os.path.isfile(self.config.iEIP_FOLDER_DIRECTORY+"end.txt"):
|
|
397
|
+
break
|
|
398
|
+
|
|
399
|
+
# Determine which image is higher in energy for proper force direction
|
|
400
|
+
if energy_2 > energy_1:
|
|
401
|
+
N = self.norm_dist_2imgs(geom_num_list_1, geom_num_list_2)
|
|
402
|
+
L = self.dist_2imgs(geom_num_list_1, geom_num_list_2)
|
|
403
|
+
else:
|
|
404
|
+
N = self.norm_dist_2imgs(geom_num_list_2, geom_num_list_1)
|
|
405
|
+
L = self.dist_2imgs(geom_num_list_2, geom_num_list_1)
|
|
406
|
+
|
|
407
|
+
Lt = self.target_dist_2imgs(L)
|
|
408
|
+
|
|
409
|
+
force_disp_1 = self.displacement(bias_gradient_1)
|
|
410
|
+
force_disp_2 = self.displacement(bias_gradient_2)
|
|
411
|
+
|
|
412
|
+
perp_force_1 = self.perpendicular_force(bias_gradient_1, N)
|
|
413
|
+
perp_force_2 = self.perpendicular_force(bias_gradient_2, N)
|
|
414
|
+
|
|
415
|
+
delta_energy_force_1 = self.delta_energy_force(bias_energy_1, bias_energy_2, N, L)
|
|
416
|
+
delta_energy_force_2 = self.delta_energy_force(bias_energy_1, bias_energy_2, N, L)
|
|
417
|
+
|
|
418
|
+
close_target_force = self.close_target_force(L, Lt, geom_num_list_1, geom_num_list_2)
|
|
419
|
+
|
|
420
|
+
perp_disp_1 = self.displacement(perp_force_1)
|
|
421
|
+
perp_disp_2 = self.displacement(perp_force_2)
|
|
422
|
+
|
|
423
|
+
delta_energy_disp_1 = self.displacement(delta_energy_force_1)
|
|
424
|
+
delta_energy_disp_2 = self.displacement(delta_energy_force_2)
|
|
425
|
+
|
|
426
|
+
close_target_disp = self.displacement(close_target_force)
|
|
427
|
+
|
|
428
|
+
if iter == 0:
|
|
429
|
+
ini_force_1 = perp_force_1 * 0.0
|
|
430
|
+
ini_force_2 = perp_force_2 * 0.0
|
|
431
|
+
ini_disp_1 = ini_force_1
|
|
432
|
+
ini_disp_2 = ini_force_2
|
|
433
|
+
close_target_disp_1 = close_target_disp
|
|
434
|
+
close_target_disp_2 = close_target_disp
|
|
435
|
+
else:
|
|
436
|
+
ini_force_1 = self.initial_structure_dependent_force(geom_num_list_1, ini_geom_1)
|
|
437
|
+
ini_force_2 = self.initial_structure_dependent_force(geom_num_list_2, ini_geom_2)
|
|
438
|
+
ini_disp_1 = self.displacement_prime(ini_force_1)
|
|
439
|
+
ini_disp_2 = self.displacement_prime(ini_force_2)
|
|
440
|
+
|
|
441
|
+
# Based on DS-AFIR method
|
|
442
|
+
Z_1 = np.linalg.norm(geom_num_list_1 - ini_geom_1) / np.linalg.norm(geom_num_list_1 - geom_num_list_2) + (np.sum((geom_num_list_1 - ini_geom_1) * (geom_num_list_1 - geom_num_list_2))) / (np.linalg.norm(geom_num_list_1 - ini_geom_1) * np.linalg.norm(geom_num_list_1 - geom_num_list_2))
|
|
443
|
+
Z_2 = np.linalg.norm(geom_num_list_2 - ini_geom_2) / np.linalg.norm(geom_num_list_2 - geom_num_list_1) + (np.sum((geom_num_list_2 - ini_geom_2) * (geom_num_list_2 - geom_num_list_1))) / (np.linalg.norm(geom_num_list_2 - ini_geom_2) * np.linalg.norm(geom_num_list_2 - geom_num_list_1))
|
|
444
|
+
|
|
445
|
+
if Z_1 > 0.0:
|
|
446
|
+
Y_1 = Z_1 / (Z_1 + 1) + 0.5
|
|
447
|
+
else:
|
|
448
|
+
Y_1 = 0.5
|
|
449
|
+
|
|
450
|
+
if Z_2 > 0.0:
|
|
451
|
+
Y_2 = Z_2 / (Z_2 + 1) + 0.5
|
|
452
|
+
else:
|
|
453
|
+
Y_2 = 0.5
|
|
454
|
+
|
|
455
|
+
u_1 = Y_1 * ((geom_num_list_1 - geom_num_list_2) / np.linalg.norm(geom_num_list_1 - geom_num_list_2)) - (1.0 - Y_1) * ((geom_num_list_1 - ini_geom_1) / np.linalg.norm(geom_num_list_1 - ini_geom_1))
|
|
456
|
+
u_2 = Y_2 * ((geom_num_list_2 - geom_num_list_1) / np.linalg.norm(geom_num_list_2 - geom_num_list_1)) - (1.0 - Y_2) * ((geom_num_list_2 - ini_geom_2) / np.linalg.norm(geom_num_list_2 - ini_geom_2))
|
|
457
|
+
|
|
458
|
+
X_1 = self.config.BETA / np.linalg.norm(u_1) - (np.sum(gradient_1 * u_1) / np.linalg.norm(u_1) ** 2)
|
|
459
|
+
X_2 = self.config.BETA / np.linalg.norm(u_2) - (np.sum(gradient_2 * u_2) / np.linalg.norm(u_2) ** 2)
|
|
460
|
+
|
|
461
|
+
ini_disp_1 *= X_1 * (1.0 - Y_1)
|
|
462
|
+
ini_disp_2 *= X_2 * (1.0 - Y_2)
|
|
463
|
+
|
|
464
|
+
close_target_disp_1 = close_target_disp * X_1 * Y_1
|
|
465
|
+
close_target_disp_2 = close_target_disp * X_2 * Y_2
|
|
466
|
+
|
|
467
|
+
total_disp_1 = -perp_disp_1 + delta_energy_disp_1 + close_target_disp_1 - force_disp_1 + ini_disp_1
|
|
468
|
+
total_disp_2 = -perp_disp_2 - delta_energy_disp_2 - close_target_disp_2 - force_disp_2 + ini_disp_2
|
|
469
|
+
|
|
470
|
+
# AdaBelief optimizer: https://doi.org/10.48550/arXiv.2010.07468
|
|
471
|
+
m_1 = beta_m * m_1 + (1 - beta_m) * total_disp_1
|
|
472
|
+
m_2 = beta_m * m_2 + (1 - beta_m) * total_disp_2
|
|
473
|
+
v_1 = beta_v * v_1 + (1 - beta_v) * (total_disp_1 - m_1)**2
|
|
474
|
+
v_2 = beta_v * v_2 + (1 - beta_v) * (total_disp_2 - m_2)**2
|
|
475
|
+
|
|
476
|
+
adabelief_1 = 0.01 * (m_1 / (np.sqrt(v_1) + 1e-8))
|
|
477
|
+
adabelief_2 = 0.01 * (m_2 / (np.sqrt(v_2) + 1e-8))
|
|
478
|
+
|
|
479
|
+
new_geom_num_list_1 = geom_num_list_1 + adabelief_1
|
|
480
|
+
new_geom_num_list_2 = geom_num_list_2 + adabelief_2
|
|
481
|
+
|
|
482
|
+
new_geom_num_list_1, new_geom_num_list_2 = Calculationtools().kabsch_algorithm(new_geom_num_list_1, new_geom_num_list_2)
|
|
483
|
+
|
|
484
|
+
if iter != 0:
|
|
485
|
+
prev_delta_geometry = delta_geometry
|
|
486
|
+
|
|
487
|
+
delta_geometry = np.linalg.norm(new_geom_num_list_2 - new_geom_num_list_1)
|
|
488
|
+
rms_perp_force = np.linalg.norm(np.sqrt(perp_force_1 ** 2 + perp_force_2 ** 2))
|
|
489
|
+
|
|
490
|
+
info_dat = {
|
|
491
|
+
"perp_force_1": perp_force_1,
|
|
492
|
+
"perp_force_2": perp_force_2,
|
|
493
|
+
"delta_energy_force_1": delta_energy_force_1,
|
|
494
|
+
"delta_energy_force_2": delta_energy_force_2,
|
|
495
|
+
"close_target_force": close_target_force,
|
|
496
|
+
"perp_disp_1": perp_disp_1,
|
|
497
|
+
"perp_disp_2": perp_disp_2,
|
|
498
|
+
"delta_energy_disp_1": delta_energy_disp_1,
|
|
499
|
+
"delta_energy_disp_2": delta_energy_disp_2,
|
|
500
|
+
"close_target_disp": close_target_disp,
|
|
501
|
+
"total_disp_1": total_disp_1,
|
|
502
|
+
"total_disp_2": total_disp_2,
|
|
503
|
+
"bias_energy_1": bias_energy_1,
|
|
504
|
+
"bias_energy_2": bias_energy_2,
|
|
505
|
+
"bias_gradient_1": bias_gradient_1,
|
|
506
|
+
"bias_gradient_2": bias_gradient_2,
|
|
507
|
+
"energy_1": energy_1,
|
|
508
|
+
"energy_2": energy_2,
|
|
509
|
+
"gradient_1": gradient_1,
|
|
510
|
+
"gradient_2": gradient_2,
|
|
511
|
+
"delta_geometry": delta_geometry,
|
|
512
|
+
"rms_perp_force": rms_perp_force
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
self.print_info(info_dat)
|
|
516
|
+
|
|
517
|
+
# Prepare geometries for next iteration
|
|
518
|
+
new_geom_num_list_1_tolist = (new_geom_num_list_1*self.config.bohr2angstroms).tolist()
|
|
519
|
+
new_geom_num_list_2_tolist = (new_geom_num_list_2*self.config.bohr2angstroms).tolist()
|
|
520
|
+
for i, elem in enumerate(element_list):
|
|
521
|
+
new_geom_num_list_1_tolist[i].insert(0, elem)
|
|
522
|
+
new_geom_num_list_2_tolist[i].insert(0, elem)
|
|
523
|
+
|
|
524
|
+
new_geom_num_list_1_tolist.insert(0, init_electric_charge_and_multiplicity)
|
|
525
|
+
new_geom_num_list_2_tolist.insert(0, final_electric_charge_and_multiplicity)
|
|
526
|
+
|
|
527
|
+
file_directory_1 = FIO1.make_psi4_input_file([new_geom_num_list_1_tolist], iter+1)
|
|
528
|
+
file_directory_2 = FIO2.make_psi4_input_file([new_geom_num_list_2_tolist], iter+1)
|
|
529
|
+
|
|
530
|
+
# Record data for plotting
|
|
531
|
+
BIAS_ENERGY_LIST_A.append(bias_energy_1*self.config.hartree2kcalmol)
|
|
532
|
+
BIAS_ENERGY_LIST_B.append(bias_energy_2*self.config.hartree2kcalmol)
|
|
533
|
+
BIAS_GRAD_LIST_A.append(np.sqrt(np.sum(bias_gradient_1**2)))
|
|
534
|
+
BIAS_GRAD_LIST_B.append(np.sqrt(np.sum(bias_gradient_2**2)))
|
|
535
|
+
|
|
536
|
+
ENERGY_LIST_A.append(energy_1*self.config.hartree2kcalmol)
|
|
537
|
+
ENERGY_LIST_B.append(energy_2*self.config.hartree2kcalmol)
|
|
538
|
+
GRAD_LIST_A.append(np.sqrt(np.sum(gradient_1**2)))
|
|
539
|
+
GRAD_LIST_B.append(np.sqrt(np.sum(gradient_2**2)))
|
|
540
|
+
|
|
541
|
+
prev_geom_num_list_1 = geom_num_list_1
|
|
542
|
+
prev_geom_num_list_2 = geom_num_list_2
|
|
543
|
+
|
|
544
|
+
# Check convergence
|
|
545
|
+
if delta_geometry < self.config.img_distance_convage_criterion: # Bohr
|
|
546
|
+
print("Converged!!!")
|
|
547
|
+
break
|
|
548
|
+
|
|
549
|
+
# Adjust beta if images are diverging
|
|
550
|
+
if delta_geometry > prev_delta_geometry:
|
|
551
|
+
self.config.BETA *= 1.02
|
|
552
|
+
else:
|
|
553
|
+
print("Reached maximum number of iterations. This is not converged.")
|
|
554
|
+
with open(self.config.iEIP_FOLDER_DIRECTORY+"not_converged.txt", "w") as f:
|
|
555
|
+
f.write("Reached maximum number of iterations. This is not converged.")
|
|
556
|
+
|
|
557
|
+
# Create energy and gradient profile plots
|
|
558
|
+
bias_ene_list = BIAS_ENERGY_LIST_A + BIAS_ENERGY_LIST_B[::-1]
|
|
559
|
+
bias_grad_list = BIAS_GRAD_LIST_A + BIAS_GRAD_LIST_B[::-1]
|
|
560
|
+
|
|
561
|
+
ene_list = ENERGY_LIST_A + ENERGY_LIST_B[::-1]
|
|
562
|
+
grad_list = GRAD_LIST_A + GRAD_LIST_B[::-1]
|
|
563
|
+
NUM_LIST = [i for i in range(len(ene_list))]
|
|
564
|
+
|
|
565
|
+
G.single_plot(NUM_LIST, ene_list, file_directory_1, "energy", axis_name_2="energy [kcal/mol]", name="energy")
|
|
566
|
+
G.single_plot(NUM_LIST, grad_list, file_directory_1, "gradient", axis_name_2="grad (RMS) [a.u.]", name="gradient")
|
|
567
|
+
G.single_plot(NUM_LIST, bias_ene_list, file_directory_1, "bias_energy", axis_name_2="energy [kcal/mol]", name="energy")
|
|
568
|
+
G.single_plot(NUM_LIST, bias_grad_list, file_directory_1, "bias_gradient", axis_name_2="grad (RMS) [a.u.]", name="gradient")
|
|
569
|
+
FIO1.make_traj_file_for_DM(img_1="A", img_2="B")
|
|
570
|
+
|
|
571
|
+
# Identify critical points
|
|
572
|
+
FIO1.argrelextrema_txt_save(ene_list, "approx_TS", "max")
|
|
573
|
+
FIO1.argrelextrema_txt_save(ene_list, "approx_EQ", "min")
|
|
574
|
+
FIO1.argrelextrema_txt_save(grad_list, "local_min_grad", "min")
|
|
575
|
+
|
|
576
|
+
return
|
|
577
|
+
|
|
578
|
+
def norm_dist_2imgs(self, geom_num_list_1, geom_num_list_2):
|
|
579
|
+
"""Calculate normalized distance vector between two images"""
|
|
580
|
+
L = self.dist_2imgs(geom_num_list_1, geom_num_list_2)
|
|
581
|
+
N = (geom_num_list_2 - geom_num_list_1) / L
|
|
582
|
+
return N
|
|
583
|
+
|
|
584
|
+
def dist_2imgs(self, geom_num_list_1, geom_num_list_2):
|
|
585
|
+
"""Calculate distance between two images"""
|
|
586
|
+
L = np.linalg.norm(geom_num_list_2 - geom_num_list_1) + 1e-10
|
|
587
|
+
return L # Bohr
|
|
588
|
+
|
|
589
|
+
def target_dist_2imgs(self, L):
|
|
590
|
+
"""Calculate target distance between two images"""
|
|
591
|
+
Lt = max(L * 0.9, self.config.L_covergence - 0.01)
|
|
592
|
+
return Lt
|
|
593
|
+
|
|
594
|
+
def force_R(self, L):
|
|
595
|
+
"""Calculate force magnitude based on distance"""
|
|
596
|
+
F_R = min(max(L/self.config.L_covergence, 1)) * self.F_R_convage_criterion
|
|
597
|
+
return F_R
|
|
598
|
+
|
|
599
|
+
def displacement(self, force):
|
|
600
|
+
"""Calculate displacement from force with magnitude limit"""
|
|
601
|
+
n_force = np.linalg.norm(force)
|
|
602
|
+
displacement = (force / (n_force + 1e-10)) * min(n_force, self.config.displacement_limit)
|
|
603
|
+
return displacement
|
|
604
|
+
|
|
605
|
+
def displacement_prime(self, force):
|
|
606
|
+
"""Calculate displacement from force with fixed magnitude"""
|
|
607
|
+
n_force = np.linalg.norm(force)
|
|
608
|
+
displacement = (force / (n_force + 1e-10)) * self.config.displacement_limit
|
|
609
|
+
return displacement
|
|
610
|
+
|
|
611
|
+
def initial_structure_dependent_force(self, geom, ini_geom):
|
|
612
|
+
"""Calculate force toward initial structure"""
|
|
613
|
+
ini_force = geom - ini_geom
|
|
614
|
+
return ini_force
|
|
615
|
+
|
|
616
|
+
def perpendicular_force(self, gradient, N):
|
|
617
|
+
"""Calculate force component perpendicular to path"""
|
|
618
|
+
perp_force = gradient.reshape(len(gradient)*3, 1) - np.dot(gradient.reshape(1, len(gradient)*3), N.reshape(len(gradient)*3, 1)) * N.reshape(len(gradient)*3, 1)
|
|
619
|
+
return perp_force.reshape(len(gradient), 3) # (atomnum×3, ndarray)
|
|
620
|
+
|
|
621
|
+
def delta_energy_force(self, ene_1, ene_2, N, L):
|
|
622
|
+
"""Calculate force component due to energy difference"""
|
|
623
|
+
d_ene_force = N * abs(ene_1 - ene_2) / L
|
|
624
|
+
return d_ene_force
|
|
625
|
+
|
|
626
|
+
def close_target_force(self, L, Lt, geom_num_list_1, geom_num_list_2):
|
|
627
|
+
"""Calculate force component to maintain target distance"""
|
|
628
|
+
ct_force = (geom_num_list_2 - geom_num_list_1) * (L - Lt) / L
|
|
629
|
+
return ct_force
|