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,174 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import copy
|
|
3
|
+
from scipy.signal import argrelextrema
|
|
4
|
+
from multioptpy.Coordinate.redundant_coordinate import calc_int_grad_from_pBmat, calc_cart_grad_from_pBmat
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def extremum_list_index(energy_list):
|
|
8
|
+
local_max_energy_list_index = argrelextrema(energy_list, np.greater)
|
|
9
|
+
inverse_energy_list = (-1)*energy_list
|
|
10
|
+
local_min_energy_list_index = argrelextrema(inverse_energy_list, np.greater)
|
|
11
|
+
|
|
12
|
+
local_max_energy_list_index = local_max_energy_list_index[0].tolist()
|
|
13
|
+
local_min_energy_list_index = local_min_energy_list_index[0].tolist()
|
|
14
|
+
local_max_energy_list_index.append(0)
|
|
15
|
+
local_min_energy_list_index.append(0)
|
|
16
|
+
local_max_energy_list_index.append(0)
|
|
17
|
+
local_min_energy_list_index.append(0)
|
|
18
|
+
return local_max_energy_list_index, local_min_energy_list_index
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CaluculationQSM:
|
|
24
|
+
def __init__(self, APPLY_CI_NEB=99999):
|
|
25
|
+
self.spring_constant_k = 0.01
|
|
26
|
+
self.APPLY_CI_NEB = APPLY_CI_NEB
|
|
27
|
+
self.force_const_for_cineb = 0.01
|
|
28
|
+
|
|
29
|
+
def calc_force(self, geometry_num_list, energy_list, gradient_list, optimize_num, element_list):
|
|
30
|
+
print("QSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSMQSM")
|
|
31
|
+
#ref. J. Chem. Phys. 124, 054109 (2006)
|
|
32
|
+
nnode = len(energy_list)
|
|
33
|
+
local_max_energy_list_index, local_min_energy_list_index = extremum_list_index(energy_list)
|
|
34
|
+
total_force_list = []
|
|
35
|
+
self.tau_list = []
|
|
36
|
+
for i in range(nnode):
|
|
37
|
+
if i == 0:
|
|
38
|
+
total_force_list.append(-1*np.array(gradient_list[0], dtype = "float64"))
|
|
39
|
+
self.tau_list.append(np.zeros_like(geometry_num_list[0], dtype = "float64"))
|
|
40
|
+
continue
|
|
41
|
+
elif i == nnode-1:
|
|
42
|
+
total_force_list.append(-1*np.array(gradient_list[nnode-1], dtype = "float64"))
|
|
43
|
+
self.tau_list.append(np.zeros_like(geometry_num_list[nnode-1], dtype = "float64"))
|
|
44
|
+
continue
|
|
45
|
+
tmp_grad = copy.copy(gradient_list[i]).reshape(-1, 1)
|
|
46
|
+
force, tangent_grad = self.calc_project_out_grad(geometry_num_list[i-1], geometry_num_list[i], geometry_num_list[i+1], tmp_grad, energy_list[i-1:i+2])
|
|
47
|
+
if optimize_num > self.APPLY_CI_NEB and (i + 1 in local_max_energy_list_index or i - 1 in local_max_energy_list_index) and (i != 1 and i != nnode-2):
|
|
48
|
+
force *= 0.001
|
|
49
|
+
print("Restrect step of # NODE", i, " for CI-NEB")
|
|
50
|
+
elif optimize_num > self.APPLY_CI_NEB and (i in local_max_energy_list_index) and (i != 1 or i != nnode-2):
|
|
51
|
+
force = self.calc_ci_neb_force(tmp_grad, tangent_grad)
|
|
52
|
+
print("CI-NEB was applied to # NODE", i)
|
|
53
|
+
else:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
total_force_list.append(-1*force.reshape(-1, 3))
|
|
59
|
+
self.tau_list.append(tangent_grad.reshape(-1, 3) / (np.linalg.norm(tangent_grad) + 1e-15))
|
|
60
|
+
|
|
61
|
+
total_force_list = np.array(total_force_list, dtype = "float64")
|
|
62
|
+
total_force_list = projection(total_force_list, geometry_num_list)
|
|
63
|
+
|
|
64
|
+
return total_force_list
|
|
65
|
+
|
|
66
|
+
def calc_project_out_grad(self, coord_1, coord_2, coord_3, grad_2, energy_list):# grad: (3N, 1), geom_num_list: (N, 3)
|
|
67
|
+
natom = len(coord_2)
|
|
68
|
+
tmp_grad = copy.copy(grad_2)
|
|
69
|
+
if energy_list[0] < energy_list[1] and energy_list[1] < energy_list[2]:
|
|
70
|
+
B_mat = self.calc_B_matrix_for_NEB_tangent(coord_2, coord_3)
|
|
71
|
+
int_grad = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat)
|
|
72
|
+
projection_grad = calc_cart_grad_from_pBmat(-1*int_grad, B_mat)
|
|
73
|
+
proj_grad = tmp_grad.reshape(3*natom, 1) + projection_grad
|
|
74
|
+
tangent_grad = projection_grad
|
|
75
|
+
elif energy_list[0] > energy_list[1] and energy_list[1] > energy_list[2]:
|
|
76
|
+
B_mat = self.calc_B_matrix_for_NEB_tangent(coord_1, coord_2)
|
|
77
|
+
int_grad = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat)
|
|
78
|
+
projection_grad = calc_cart_grad_from_pBmat(-1*int_grad, B_mat)
|
|
79
|
+
proj_grad = tmp_grad.reshape(3*natom, 1) + projection_grad
|
|
80
|
+
tangent_grad = projection_grad
|
|
81
|
+
else:
|
|
82
|
+
B_mat_plus = self.calc_B_matrix_for_NEB_tangent(coord_2, coord_3)
|
|
83
|
+
B_mat_minus = self.calc_B_matrix_for_NEB_tangent(coord_1, coord_2)
|
|
84
|
+
int_grad_plus = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat_plus)
|
|
85
|
+
int_grad_minus = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat_minus)
|
|
86
|
+
max_ene = max(abs(energy_list[2] - energy_list[1]), abs(energy_list[1] - energy_list[0]))
|
|
87
|
+
min_ene = min(abs(energy_list[2] - energy_list[1]), abs(energy_list[1] - energy_list[0]))
|
|
88
|
+
a = (max_ene) / (max_ene + min_ene + 1e-8)
|
|
89
|
+
b = (min_ene) / (max_ene + min_ene + 1e-8)
|
|
90
|
+
|
|
91
|
+
if energy_list[0] < energy_list[2]:
|
|
92
|
+
projection_grad_plus = calc_cart_grad_from_pBmat(-a*int_grad_plus, B_mat_plus)
|
|
93
|
+
projection_grad_minus = calc_cart_grad_from_pBmat(-b*int_grad_minus, B_mat_minus)
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
projection_grad_plus = calc_cart_grad_from_pBmat(-b*int_grad_plus, B_mat_plus)
|
|
97
|
+
projection_grad_minus = calc_cart_grad_from_pBmat(-a*int_grad_minus, B_mat_minus)
|
|
98
|
+
proj_grad = tmp_grad.reshape(3*natom, 1) + projection_grad_plus + projection_grad_minus
|
|
99
|
+
tangent_grad = projection_grad_plus + projection_grad_minus
|
|
100
|
+
return proj_grad, tangent_grad
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def calc_B_matrix_for_NEB_tangent(self, coord_1, coord_2):
|
|
104
|
+
natom = len(coord_2)
|
|
105
|
+
B_mat = np.zeros((natom, 3*natom))
|
|
106
|
+
|
|
107
|
+
for i in range(natom):
|
|
108
|
+
norm_12 = np.linalg.norm(coord_1[i] - coord_2[i]) + 1e-15
|
|
109
|
+
dr12_dx2 = (coord_2[i][0] - coord_1[i][0]) / norm_12
|
|
110
|
+
dr12_dy2 = (coord_2[i][1] - coord_1[i][1]) / norm_12
|
|
111
|
+
dr12_dz2 = (coord_2[i][2] - coord_1[i][2]) / norm_12
|
|
112
|
+
B_mat[i][3*i] = dr12_dx2
|
|
113
|
+
B_mat[i][3*i+1] = dr12_dy2
|
|
114
|
+
B_mat[i][3*i+2] = dr12_dz2
|
|
115
|
+
|
|
116
|
+
return B_mat
|
|
117
|
+
|
|
118
|
+
def calc_proj_hess(self, hess, node_num, geometry_num_list):
|
|
119
|
+
if node_num == 0 or node_num == len(geometry_num_list)-1:
|
|
120
|
+
return hess
|
|
121
|
+
proj_hess = projection_hess(hess, geometry_num_list, node_num)
|
|
122
|
+
# Make sure the Hessian is symmetric
|
|
123
|
+
proj_hess = 0.5 * (proj_hess + proj_hess.T)
|
|
124
|
+
return proj_hess
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def projection(move_vector_list, geometry_list):
|
|
129
|
+
print("Applying projection to move vectors")
|
|
130
|
+
for i in range(1, len(geometry_list)-1):
|
|
131
|
+
vec_1 = geometry_list[i] - geometry_list[i-1]
|
|
132
|
+
vec_2 = geometry_list[i+1] - geometry_list[i]
|
|
133
|
+
vec_1_norm = np.linalg.norm(vec_1)
|
|
134
|
+
vec_2_norm = np.linalg.norm(vec_2)
|
|
135
|
+
if vec_1_norm < 1e-8 or vec_2_norm < 1e-8:
|
|
136
|
+
continue
|
|
137
|
+
vec_1 = vec_1 / vec_1_norm
|
|
138
|
+
vec_2 = vec_2 / vec_2_norm
|
|
139
|
+
vec_1 = vec_1.reshape(-1, 1)
|
|
140
|
+
vec_2 = vec_2.reshape(-1, 1)
|
|
141
|
+
# Gram-Schmidt process
|
|
142
|
+
vec_2 -= np.dot(vec_2.T, vec_1) * vec_1
|
|
143
|
+
if np.linalg.norm(vec_2) < 1e-8:
|
|
144
|
+
continue
|
|
145
|
+
vec_2 /= np.linalg.norm(vec_2)
|
|
146
|
+
|
|
147
|
+
P = np.eye(len(vec_1)) - np.outer(vec_1, vec_1) - np.outer(vec_2, vec_2)
|
|
148
|
+
tmp_proj_move_vec = np.dot(P, move_vector_list[i].reshape(-1, 1))
|
|
149
|
+
move_vector_list[i] = tmp_proj_move_vec.reshape(-1, 3)
|
|
150
|
+
return move_vector_list
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def projection_hess(hessian, geometry_list, node_num):
|
|
154
|
+
print("Applying projection to Hessian")
|
|
155
|
+
|
|
156
|
+
vec_1 = geometry_list[node_num] - geometry_list[node_num-1]
|
|
157
|
+
vec_2 = geometry_list[node_num+1] - geometry_list[node_num]
|
|
158
|
+
vec_1_norm = np.linalg.norm(vec_1)
|
|
159
|
+
vec_2_norm = np.linalg.norm(vec_2)
|
|
160
|
+
if vec_1_norm < 1e-8 or vec_2_norm < 1e-8:
|
|
161
|
+
return hessian
|
|
162
|
+
vec_1 = vec_1 / vec_1_norm
|
|
163
|
+
vec_2 = vec_2 / vec_2_norm
|
|
164
|
+
vec_1 = vec_1.reshape(-1, 1)
|
|
165
|
+
vec_2 = vec_2.reshape(-1, 1)
|
|
166
|
+
# Gram-Schmidt process
|
|
167
|
+
vec_2 -= np.dot(vec_2.T, vec_1) * vec_1
|
|
168
|
+
if np.linalg.norm(vec_2) < 1e-8:
|
|
169
|
+
return hessian
|
|
170
|
+
vec_2 /= np.linalg.norm(vec_2)
|
|
171
|
+
|
|
172
|
+
P = np.eye(len(vec_1)) - np.outer(vec_1, vec_1) - np.outer(vec_2, vec_2)
|
|
173
|
+
tmp_proj_hess = np.dot(np.dot(P, hessian), P.T)
|
|
174
|
+
return tmp_proj_hess
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import copy
|
|
3
|
+
from scipy.signal import argrelextrema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def extremum_list_index(energy_list):
|
|
7
|
+
local_max_energy_list_index = argrelextrema(energy_list, np.greater)
|
|
8
|
+
inverse_energy_list = (-1)*energy_list
|
|
9
|
+
local_min_energy_list_index = argrelextrema(inverse_energy_list, np.greater)
|
|
10
|
+
|
|
11
|
+
local_max_energy_list_index = local_max_energy_list_index[0].tolist()
|
|
12
|
+
local_min_energy_list_index = local_min_energy_list_index[0].tolist()
|
|
13
|
+
local_max_energy_list_index.append(0)
|
|
14
|
+
local_min_energy_list_index.append(0)
|
|
15
|
+
local_max_energy_list_index.append(0)
|
|
16
|
+
local_min_energy_list_index.append(0)
|
|
17
|
+
return local_max_energy_list_index, local_min_energy_list_index
|
|
18
|
+
|
|
19
|
+
class CaluculationQSMv2:
|
|
20
|
+
"""
|
|
21
|
+
Implementation of Tangent-based Projection using Ayala & Schlegel (1997) method.
|
|
22
|
+
Replaces the B-matrix logic of QSM with geometric tangent propagation from the TS.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, APPLY_CI_NEB=99999):
|
|
25
|
+
self.spring_constant_k = 0.01
|
|
26
|
+
self.APPLY_CI_NEB = APPLY_CI_NEB
|
|
27
|
+
self.force_const_for_cineb = 0.01
|
|
28
|
+
self.tau_list = [] # Cache tangents for Hessian projection
|
|
29
|
+
|
|
30
|
+
def _normalize(self, v):
|
|
31
|
+
"""Helper function to normalize a vector."""
|
|
32
|
+
norm = np.linalg.norm(v)
|
|
33
|
+
return v / norm if norm > 1e-10 else v
|
|
34
|
+
|
|
35
|
+
def _calc_arc_tangent(self, q_cur, q_uphill, t_uphill):
|
|
36
|
+
"""Ayala & Schlegel Eq. 3c: Arc approximation"""
|
|
37
|
+
chord = q_cur - q_uphill
|
|
38
|
+
diff_norm_sq = np.dot(chord, chord)
|
|
39
|
+
denom = 2 * np.dot(t_uphill, chord)
|
|
40
|
+
|
|
41
|
+
if abs(denom) < 1e-10:
|
|
42
|
+
return self._normalize(chord)
|
|
43
|
+
|
|
44
|
+
r = diff_norm_sq / denom
|
|
45
|
+
t_new = (chord - r * t_uphill) / r
|
|
46
|
+
return self._normalize(t_new)
|
|
47
|
+
|
|
48
|
+
def _calc_parabola_tangent(self, q_cur, q_uphill, t_uphill):
|
|
49
|
+
"""Ayala & Schlegel Eq. 3d: Parabola approximation"""
|
|
50
|
+
chord = q_cur - q_uphill
|
|
51
|
+
chord_len = np.linalg.norm(chord)
|
|
52
|
+
if chord_len < 1e-10:
|
|
53
|
+
return t_uphill
|
|
54
|
+
|
|
55
|
+
cos_theta = np.dot(chord, t_uphill) / chord_len
|
|
56
|
+
theta = np.arccos(np.clip(cos_theta, -1.0, 1.0))
|
|
57
|
+
|
|
58
|
+
proj = np.dot(chord, t_uphill)
|
|
59
|
+
n_vec = chord - proj * t_uphill
|
|
60
|
+
n_vec = self._normalize(n_vec)
|
|
61
|
+
|
|
62
|
+
tan_val = np.tan(theta - (np.pi / 4.0))
|
|
63
|
+
t_new = n_vec - tan_val * (t_uphill - n_vec)
|
|
64
|
+
return self._normalize(t_new)
|
|
65
|
+
|
|
66
|
+
def _calculate_all_tangents(self, geometry_num_list, energy_list):
|
|
67
|
+
"""
|
|
68
|
+
Calculate tangents for all nodes using Ayala & Schlegel logic.
|
|
69
|
+
Propagates from the highest energy point (TS) downwards.
|
|
70
|
+
"""
|
|
71
|
+
geoms = np.array(geometry_num_list, dtype=np.float64)
|
|
72
|
+
energies = np.array(energy_list, dtype=np.float64)
|
|
73
|
+
n_images = len(geoms)
|
|
74
|
+
|
|
75
|
+
# Flatten geometries to treat system as a single vector (3N dim)
|
|
76
|
+
flat_geoms = geoms.reshape(n_images, -1)
|
|
77
|
+
tangents = np.zeros_like(flat_geoms)
|
|
78
|
+
|
|
79
|
+
# 1. Find TS (Highest Energy)
|
|
80
|
+
# Exclude fixed endpoints (0 and -1) from being the TS source if possible
|
|
81
|
+
search_energies = energies.copy()
|
|
82
|
+
|
|
83
|
+
ts_idx = np.argmax(search_energies)
|
|
84
|
+
|
|
85
|
+
# Safety for edge cases
|
|
86
|
+
if ts_idx == 0:
|
|
87
|
+
ts_idx = 1
|
|
88
|
+
if ts_idx == n_images - 1:
|
|
89
|
+
ts_idx = n_images - 2
|
|
90
|
+
|
|
91
|
+
# 2. Tangent at TS (Eq. 3a)
|
|
92
|
+
q_ts = flat_geoms[ts_idx]
|
|
93
|
+
q_prev = flat_geoms[ts_idx - 1]
|
|
94
|
+
q_next = flat_geoms[ts_idx + 1]
|
|
95
|
+
|
|
96
|
+
v_prev = q_prev - q_ts
|
|
97
|
+
v_next = q_next - q_ts
|
|
98
|
+
|
|
99
|
+
dist_prev_sq = max(np.dot(v_prev, v_prev), 1e-10)
|
|
100
|
+
dist_next_sq = max(np.dot(v_next, v_next), 1e-10)
|
|
101
|
+
|
|
102
|
+
ts_tangent = (v_next / dist_next_sq) - (v_prev / dist_prev_sq)
|
|
103
|
+
tangents[ts_idx] = self._normalize(ts_tangent)
|
|
104
|
+
|
|
105
|
+
# 3. Propagate Downhill (Reactant side: ts-1 -> 1)
|
|
106
|
+
for i in range(ts_idx - 1, 0, -1):
|
|
107
|
+
q_cur = flat_geoms[i]
|
|
108
|
+
q_uphill = flat_geoms[i+1]
|
|
109
|
+
t_uphill = tangents[i+1]
|
|
110
|
+
|
|
111
|
+
chord = q_cur - q_uphill
|
|
112
|
+
chord_u = self._normalize(chord)
|
|
113
|
+
angle = np.arccos(np.clip(np.dot(chord_u, t_uphill), -1.0, 1.0))
|
|
114
|
+
|
|
115
|
+
if angle <= (np.pi / 4.0):
|
|
116
|
+
tangents[i] = self._calc_arc_tangent(q_cur, q_uphill, t_uphill)
|
|
117
|
+
else:
|
|
118
|
+
tangents[i] = self._calc_parabola_tangent(q_cur, q_uphill, t_uphill)
|
|
119
|
+
|
|
120
|
+
# 4. Propagate Downhill (Product side: ts+1 -> N-2)
|
|
121
|
+
for i in range(ts_idx + 1, n_images - 1):
|
|
122
|
+
q_cur = flat_geoms[i]
|
|
123
|
+
q_uphill = flat_geoms[i-1]
|
|
124
|
+
t_uphill = tangents[i-1]
|
|
125
|
+
|
|
126
|
+
chord = q_cur - q_uphill
|
|
127
|
+
chord_u = self._normalize(chord)
|
|
128
|
+
angle = np.arccos(np.clip(np.dot(chord_u, t_uphill), -1.0, 1.0))
|
|
129
|
+
|
|
130
|
+
if angle <= (np.pi / 4.0):
|
|
131
|
+
tangents[i] = self._calc_arc_tangent(q_cur, q_uphill, t_uphill)
|
|
132
|
+
else:
|
|
133
|
+
tangents[i] = self._calc_parabola_tangent(q_cur, q_uphill, t_uphill)
|
|
134
|
+
|
|
135
|
+
# Reshape tangents back to (N, Atoms, 3)
|
|
136
|
+
return -1*tangents.reshape(geoms.shape)
|
|
137
|
+
|
|
138
|
+
def calc_force(self, geometry_num_list, energy_list, gradient_list, optimize_num, element_list):
|
|
139
|
+
print("AyalaMethodV2_AyalaMethodV2_AyalaMethodV2_AyalaMethodV2")
|
|
140
|
+
|
|
141
|
+
nnode = len(energy_list)
|
|
142
|
+
local_max_energy_list_index, local_min_energy_list_index = extremum_list_index(energy_list)
|
|
143
|
+
|
|
144
|
+
# Calculate Ayala Tangents for all nodes
|
|
145
|
+
ayala_tangents = self._calculate_all_tangents(geometry_num_list, energy_list)
|
|
146
|
+
|
|
147
|
+
# Store for calc_proj_hess use
|
|
148
|
+
self.tau_list = ayala_tangents
|
|
149
|
+
|
|
150
|
+
total_force_list = []
|
|
151
|
+
|
|
152
|
+
for i in range(nnode):
|
|
153
|
+
# Fixed Endpoints
|
|
154
|
+
if i == 0 or i == nnode - 1:
|
|
155
|
+
total_force_list.append(-1 * np.array(gradient_list[i], dtype="float64"))
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
# Current Gradient and Tangent
|
|
159
|
+
grad = np.array(gradient_list[i], dtype="float64").flatten()
|
|
160
|
+
tangent = ayala_tangents[i].flatten()
|
|
161
|
+
|
|
162
|
+
# Project Gradient
|
|
163
|
+
# g_parallel = (g . tau) * tau
|
|
164
|
+
g_parallel = np.dot(grad, tangent) * tangent
|
|
165
|
+
g_perp = grad - g_parallel
|
|
166
|
+
|
|
167
|
+
# Force Calculation
|
|
168
|
+
# Basic NEB Force (Projected Gradient): F = -g_perp
|
|
169
|
+
force = -1.0 * g_perp
|
|
170
|
+
|
|
171
|
+
# CI-NEB Logic
|
|
172
|
+
# If CI-NEB is active and this node is a local max (TS candidate)
|
|
173
|
+
if optimize_num > self.APPLY_CI_NEB and (i in local_max_energy_list_index) and (i != 1 and i != nnode-2):
|
|
174
|
+
print(f"CI-NEB was applied to # NODE {i} (Ayala Tangent)")
|
|
175
|
+
# Invert the parallel component to climb up: F_ci = -g_perp + g_parallel
|
|
176
|
+
# (Usually F = -Grad_perp - Grad_parallel_spring + ..., here we simulate climbing)
|
|
177
|
+
# Removing spring force along path and adding potential force UP the path.
|
|
178
|
+
# Since standard force here is just -g_perp, adding climbing force means adding component along gradient?
|
|
179
|
+
# Standard CI-NEB: F = -Gradient + 2*(Gradient.Tangent)*Tangent
|
|
180
|
+
# F = - (g_perp + g_para) + 2 * g_para = -g_perp + g_para
|
|
181
|
+
force = -1.0 * g_perp + g_parallel
|
|
182
|
+
|
|
183
|
+
elif optimize_num > self.APPLY_CI_NEB and (i + 1 in local_max_energy_list_index or i - 1 in local_max_energy_list_index) and (i != 1 and i != nnode-2):
|
|
184
|
+
# Restrict step for neighbors of TS (as in original code)
|
|
185
|
+
print(f"Restrict step of # NODE {i} for CI-NEB")
|
|
186
|
+
force *= 0.001
|
|
187
|
+
|
|
188
|
+
total_force_list.append(force.reshape(geometry_num_list[i].shape))
|
|
189
|
+
|
|
190
|
+
total_force_list = np.array(total_force_list, dtype="float64")
|
|
191
|
+
|
|
192
|
+
# Note: Original code applied a global 'projection' here.
|
|
193
|
+
# Since we explicitly projected using Ayala tangents inside the loop,
|
|
194
|
+
# applying another Gram-Schmidt based on simple neighbor differences might be redundant or conflicting.
|
|
195
|
+
# However, if the original 'projection' function handles constraints other than the path tangent
|
|
196
|
+
# (like removing rotation/translation), it might still be needed.
|
|
197
|
+
# Assuming 'projection' in the original snippet was purely for path orthogonality:
|
|
198
|
+
# We DO NOT call the global 'projection' function here to strictly follow the Ayala tangent method.
|
|
199
|
+
|
|
200
|
+
return total_force_list
|
|
201
|
+
|
|
202
|
+
def calc_proj_hess(self, hess, node_num, geometry_num_list):
|
|
203
|
+
"""
|
|
204
|
+
Project the Hessian using the cached Ayala tangent.
|
|
205
|
+
P = I - |tau><tau|
|
|
206
|
+
H_proj = P * H * P
|
|
207
|
+
"""
|
|
208
|
+
# Ensure tangents are available (calc_force should be called before this)
|
|
209
|
+
if len(self.tau_list) == 0:
|
|
210
|
+
# Fallback or error handling if calc_force wasn't called.
|
|
211
|
+
# For safety, we return original hess, or one needs to calculate tangents here using stored energies.
|
|
212
|
+
# Assuming usage pattern: calc_force -> opt step -> (optional) calc_hess
|
|
213
|
+
print("Warning: Tangent list empty in calc_proj_hess. Returning original Hessian.")
|
|
214
|
+
return hess
|
|
215
|
+
|
|
216
|
+
if node_num == 0 or node_num == len(geometry_num_list)-1:
|
|
217
|
+
return hess
|
|
218
|
+
|
|
219
|
+
print(f"Applying Ayala tangent projection to Hessian at node {node_num}")
|
|
220
|
+
|
|
221
|
+
tangent = self.tau_list[node_num].flatten()
|
|
222
|
+
|
|
223
|
+
# Make Projector P = I - t * t.T
|
|
224
|
+
dim = len(tangent)
|
|
225
|
+
identity = np.eye(dim)
|
|
226
|
+
projector = identity - np.outer(tangent, tangent)
|
|
227
|
+
|
|
228
|
+
# H_proj = P . H . P
|
|
229
|
+
# Note: hess is likely (3N, 3N)
|
|
230
|
+
tmp_proj_hess = np.dot(np.dot(projector, hess), projector.T)
|
|
231
|
+
|
|
232
|
+
# Ensure symmetry
|
|
233
|
+
tmp_proj_hess = 0.5 * (tmp_proj_hess + tmp_proj_hess.T)
|
|
234
|
+
|
|
235
|
+
return tmp_proj_hess
|
|
236
|
+
|
|
237
|
+
def get_tau(self, node_num):
|
|
238
|
+
"""Returns the flattened tangent vector at the specified node."""
|
|
239
|
+
if len(self.tau_list) == 0:
|
|
240
|
+
raise ValueError("Tangent list is empty. Calculate forces first.")
|
|
241
|
+
return self.tau_list[node_num]
|
|
242
|
+
|
|
243
|
+
def calculate_gamma(self, q_triplet, E_triplet, g_triplet, tangent):
|
|
244
|
+
"""
|
|
245
|
+
Calculates the curvature gamma along the path using quintic polynomial fitting.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
q_triplet: List of [q_prev, q_curr, q_next] coordinates
|
|
249
|
+
E_triplet: List of [E_prev, E_curr, E_next] energies
|
|
250
|
+
g_triplet: List of [g_prev, g_curr, g_next] gradients
|
|
251
|
+
tangent: Normalized tangent vector at the current node
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
gamma: Curvature (2nd derivative) along the path at the current node
|
|
255
|
+
"""
|
|
256
|
+
q_prev, q_curr, q_next = q_triplet
|
|
257
|
+
E_prev, E_curr, E_next = E_triplet
|
|
258
|
+
g_prev, g_curr, g_next = g_triplet
|
|
259
|
+
|
|
260
|
+
# 1. Distances along the path
|
|
261
|
+
dist_prev = np.linalg.norm(q_curr - q_prev)
|
|
262
|
+
dist_next = np.linalg.norm(q_next - q_curr)
|
|
263
|
+
|
|
264
|
+
if dist_prev < 1e-6 or dist_next < 1e-6:
|
|
265
|
+
return 0.0
|
|
266
|
+
|
|
267
|
+
# s coordinates: prev at -dist_prev, curr at 0, next at +dist_next
|
|
268
|
+
s_p = -dist_prev
|
|
269
|
+
s_c = 0.0
|
|
270
|
+
s_n = dist_next
|
|
271
|
+
|
|
272
|
+
# 2. Project gradients onto path
|
|
273
|
+
# Tangent at i-1: Approximated by direction from i-1 to i
|
|
274
|
+
t_prev = (q_curr - q_prev) / dist_prev
|
|
275
|
+
gp_proj = np.dot(g_prev.flatten(), t_prev.flatten())
|
|
276
|
+
|
|
277
|
+
# Tangent at i: Given tangent
|
|
278
|
+
gc_proj = np.dot(g_curr.flatten(), tangent.flatten())
|
|
279
|
+
|
|
280
|
+
# Tangent at i+1: Approximated by direction from i to i+1
|
|
281
|
+
t_next = (q_next - q_curr) / dist_next
|
|
282
|
+
gn_proj = np.dot(g_next.flatten(), t_next.flatten())
|
|
283
|
+
|
|
284
|
+
# 3. Solve Quintic Polynomial Coefficients
|
|
285
|
+
# E(s) = c0 + c1*s + c2*s^2 + c3*s^3 + c4*s^4 + c5*s^5
|
|
286
|
+
A = np.array([
|
|
287
|
+
[1, s_p, s_p**2, s_p**3, s_p**4, s_p**5],
|
|
288
|
+
[1, s_c, s_c**2, s_c**3, s_c**4, s_c**5],
|
|
289
|
+
[1, s_n, s_n**2, s_n**3, s_n**4, s_n**5],
|
|
290
|
+
[0, 1, 2*s_p, 3*s_p**2, 4*s_p**3, 5*s_p**4],
|
|
291
|
+
[0, 1, 2*s_c, 3*s_c**2, 4*s_c**3, 5*s_c**4],
|
|
292
|
+
[0, 1, 2*s_n, 3*s_n**2, 4*s_n**3, 5*s_n**4]
|
|
293
|
+
])
|
|
294
|
+
|
|
295
|
+
b = np.array([E_prev, E_curr, E_next, gp_proj, gc_proj, gn_proj])
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
coeffs = np.linalg.solve(A, b)
|
|
299
|
+
# Curvature gamma = E''(0) = 2 * c2
|
|
300
|
+
gamma = 2.0 * coeffs[2]
|
|
301
|
+
return gamma
|
|
302
|
+
except np.linalg.LinAlgError:
|
|
303
|
+
# Fallback for singular matrix
|
|
304
|
+
return 0.0
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from .seam_model_function import SeamModelFunction
|
|
2
|
+
from .avoiding_model_function import AvoidingModelFunction
|
|
3
|
+
from .binary_image_ts_search_model_function import BITSSModelFunction
|
|
4
|
+
from .conical_model_function import ConicalModelFunction
|
|
5
|
+
from .opt_mesx import OptMESX
|
|
6
|
+
from .opt_mesx_2 import OptMESX2
|
|
7
|
+
from .opt_meci import OptMECI
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
class AvoidingModelFunction:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
#ref.: Advances in Physical Chemistry 2012, 2012, 1-13.
|
|
6
|
+
self.alpha = 0.01
|
|
7
|
+
return
|
|
8
|
+
|
|
9
|
+
def calc_energy(self, energy_1, energy_2):
|
|
10
|
+
U = self.alpha / 2.0 * np.exp(-1 * (energy_1 - energy_2) ** 2 / self.alpha)
|
|
11
|
+
tot_energy = 0.5 * (energy_1 + energy_2) + 0.5 * np.sqrt((energy_1 - energy_2) ** 2 + 4.0 * U)
|
|
12
|
+
return tot_energy
|
|
13
|
+
|
|
14
|
+
def calc_grad(self, energy_1, energy_2, grad_1, grad_2):
|
|
15
|
+
b = np.exp(-1 * (energy_1 - energy_2) ** 2 / self.alpha)
|
|
16
|
+
U = self.alpha / 2.0 * b
|
|
17
|
+
a = np.sqrt((energy_1 - energy_2) ** 2 + 4.0 * U)
|
|
18
|
+
dU_dq1 = -1 * (energy_1 - energy_2) * grad_1 * b
|
|
19
|
+
dU_dq2 = (energy_1 - energy_2) * grad_2 * b
|
|
20
|
+
|
|
21
|
+
smf_grad_1 = 0.5 * (grad_1 + grad_2) + 0.5 * (1.0 /a) * ( 2.0 * (energy_1 - energy_2) * (grad_1) + 8.0 * U * dU_dq1) + 0.5 * (1.0 /a) * (-2.0 * (energy_1 - energy_2) * (grad_2) + 8.0 * U * dU_dq2)
|
|
22
|
+
smf_grad_2 = 0.5 * (grad_1 + grad_2) + 0.5 * (1.0 /a) * ( 2.0 * (energy_1 - energy_2) * (grad_1) + 8.0 * U * dU_dq1) + 0.5 * (1.0 /a) * (-2.0 * (energy_1 - energy_2) * (grad_2) + 8.0 * U * dU_dq2)
|
|
23
|
+
|
|
24
|
+
return smf_grad_1, smf_grad_2
|
|
25
|
+
|
|
26
|
+
def calc_hess(self, energy_1, energy_2, grad_1, grad_2, hess_1, hess_2):
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
return
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
#J. Chem. Phys. 157, 124107 (2022)
|
|
4
|
+
#https://doi.org/10.1063/5.0102145
|
|
5
|
+
|
|
6
|
+
class BITSSModelFunction:
|
|
7
|
+
def __init__(self, geom_num_list_1, geom_num_list_2):
|
|
8
|
+
self.f = 0.5
|
|
9
|
+
self.alpha = 10.0
|
|
10
|
+
self.beta = 0.1
|
|
11
|
+
self.d = np.linalg.norm(geom_num_list_1 - geom_num_list_2)
|
|
12
|
+
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
def calc_energy(self, energy_1, energy_2, geom_num_list_1, geom_num_list_2, gradient_1, gradient_2, iter):
|
|
16
|
+
current_distance = np.linalg.norm(geom_num_list_1 - geom_num_list_2)
|
|
17
|
+
if iter % 500 == 0:
|
|
18
|
+
|
|
19
|
+
self.E_B = abs(energy_1 - energy_2)
|
|
20
|
+
self.kappa_e = self.alpha / (2.0 * self.E_B + 1e-10)
|
|
21
|
+
|
|
22
|
+
unit_vec = (geom_num_list_1 - geom_num_list_2) / current_distance
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
proj_grad_1 = gradient_1 * unit_vec * (-1)
|
|
27
|
+
proj_grad_2 = gradient_2 * unit_vec
|
|
28
|
+
|
|
29
|
+
a = np.sqrt(np.linalg.norm(proj_grad_1) + np.linalg.norm(proj_grad_2)) / (2 ** 1.5 * self.beta * self.d + 1e-10)
|
|
30
|
+
b = self.E_B / (self.beta * self.d ** 2 + 1e-10)
|
|
31
|
+
self.kappa_d = max(a, b)
|
|
32
|
+
self.d = np.linalg.norm(geom_num_list_1 - geom_num_list_2)
|
|
33
|
+
|
|
34
|
+
self.d = max((1.0 - self.f) * self.d, 1e-10)
|
|
35
|
+
energy = energy_1 + energy_2 + self.kappa_e * (energy_1 + energy_2) ** 2 + self.kappa_d * (current_distance - self.d) ** 2
|
|
36
|
+
|
|
37
|
+
return energy
|
|
38
|
+
|
|
39
|
+
def calc_grad(self, energy_1, energy_2, geom_num_list_1, geom_num_list_2, gradient_1, gradient_2):
|
|
40
|
+
current_vec = geom_num_list_1 - geom_num_list_2
|
|
41
|
+
current_dist = np.linalg.norm(current_vec) + 1e-10
|
|
42
|
+
|
|
43
|
+
bitss_grad_1 = gradient_1 + gradient_2 + 2.0 * self.kappa_e * (energy_1 - energy_2) * (gradient_1 - gradient_2) + current_vec * 2.0 * self.kappa_d * (current_dist - self.d) / current_dist
|
|
44
|
+
|
|
45
|
+
bitss_grad_2 = gradient_1 + gradient_2 + 2.0 * self.kappa_e * (energy_1 - energy_2) * (gradient_1 - gradient_2) - current_vec * 2.0 * self.kappa_d * (current_dist - self.d) / current_dist
|
|
46
|
+
|
|
47
|
+
return bitss_grad_1, bitss_grad_2
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConicalModelFunction:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
#ref.: J. Phys. Chem. B 2008, 112, 2, 405–413
|
|
7
|
+
self.alpha = 0.025
|
|
8
|
+
self.sigma = 3.5
|
|
9
|
+
return
|
|
10
|
+
|
|
11
|
+
def calc_energy(self, energy_1, energy_2):
|
|
12
|
+
delta_ene = energy_1 - energy_2
|
|
13
|
+
|
|
14
|
+
tot_energy = 0.5 * (energy_1 + energy_2) + self.sigma * delta_ene ** 2 / (delta_ene + self.alpha)
|
|
15
|
+
return tot_energy
|
|
16
|
+
|
|
17
|
+
def calc_grad(self, energy_1, energy_2, grad_1, grad_2):
|
|
18
|
+
delta_ene = energy_1 - energy_2
|
|
19
|
+
mf_grad_1 = 0.5 * (grad_1) + 0.5 * (grad_2) + self.sigma * (delta_ene ** 2.0 + 2.0 * self.alpha * delta_ene) / (delta_ene + self.alpha) ** 2.0 * (grad_1 - grad_2)
|
|
20
|
+
mf_grad_2 = 0.5 * (grad_1) + 0.5 * (grad_2) + self.sigma * (delta_ene ** 2.0 + 2.0 * self.alpha * delta_ene) / (delta_ene + self.alpha) ** 2.0 * (grad_1 - grad_2)
|
|
21
|
+
return mf_grad_1, mf_grad_2
|
|
22
|
+
|
|
23
|
+
def calc_hess(self, energy_1, energy_2, grad_1, grad_2, hess_1, hess_2):
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
return
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
class OptMECI:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
# ref.:https://doi.org/10.1021/ct1000268
|
|
6
|
+
|
|
7
|
+
self.switch_threshold = 5e-4
|
|
8
|
+
self.alpha = 1e-3
|
|
9
|
+
self.approx_cdv_vec = None
|
|
10
|
+
self.prev_dgv_vec = None
|
|
11
|
+
self.dgv_vec = None
|
|
12
|
+
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
def calc_energy(self, energy_1, energy_2):
|
|
16
|
+
tot_energy = (energy_1 + energy_2) / 2.0
|
|
17
|
+
print("energy_1:", energy_1, "hartree")
|
|
18
|
+
print("energy_2:", energy_2, "hartree")
|
|
19
|
+
print("energy_1 - energy_2:", abs(energy_1 - energy_2), "hartree")
|
|
20
|
+
return tot_energy
|
|
21
|
+
|
|
22
|
+
def calc_grad(self, energy_1, energy_2, grad_1, grad_2):
|
|
23
|
+
if self.approx_cdv_vec is None:
|
|
24
|
+
self.approx_cdv_vec = np.ones((len(grad_1)*3, 1))
|
|
25
|
+
|
|
26
|
+
delta_grad = grad_1 - grad_2
|
|
27
|
+
dgv_vec = delta_grad / np.linalg.norm(delta_grad)
|
|
28
|
+
dgv_vec = dgv_vec.reshape(-1, 1)
|
|
29
|
+
|
|
30
|
+
if self.prev_dgv_vec is None:
|
|
31
|
+
self.prev_dgv_vec = dgv_vec
|
|
32
|
+
|
|
33
|
+
self.approx_cdv_vec = (np.dot(self.approx_cdv_vec.T, dgv_vec) * self.prev_dgv_vec -1 * np.dot(self.prev_dgv_vec.T, dgv_vec) * self.approx_cdv_vec) / np.sqrt(np.dot(self.approx_cdv_vec.T, dgv_vec) ** 2 + np.dot(self.prev_dgv_vec.T, dgv_vec) ** 2)
|
|
34
|
+
|
|
35
|
+
P_matrix = np.eye((len(dgv_vec))) -1 * np.dot(dgv_vec, dgv_vec.T) -1 * np.dot(self.approx_cdv_vec, self.approx_cdv_vec.T)
|
|
36
|
+
P_matrix = 0.5 * (P_matrix + P_matrix.T)
|
|
37
|
+
gp_grad = 2 * (energy_1 - energy_2) * dgv_vec + np.dot(P_matrix, 0.5 * (grad_1.reshape(-1, 1) + grad_2.reshape(-1, 1)))
|
|
38
|
+
|
|
39
|
+
self.prev_dgv_vec = dgv_vec
|
|
40
|
+
gp_grad = gp_grad.reshape(len(grad_1), 3)
|
|
41
|
+
return gp_grad
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def calc_hess(self, hess_1, hess_2):
|
|
45
|
+
mean_hess = 0.5 * (hess_1 + hess_2)
|
|
46
|
+
P_matrix = np.eye((len(self.prev_dgv_vec))) -1 * np.dot(self.prev_dgv_vec, self.prev_dgv_vec.T) -1 * np.dot(self.approx_cdv_vec, self.approx_cdv_vec.T)
|
|
47
|
+
P_matrix = 0.5 * (P_matrix + P_matrix.T)
|
|
48
|
+
|
|
49
|
+
gp_hess = np.dot(P_matrix, np.dot(mean_hess, P_matrix))
|
|
50
|
+
return gp_hess
|