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,512 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import copy
|
|
3
|
+
from scipy.optimize import minimize
|
|
4
|
+
from scipy.signal import argrelextrema
|
|
5
|
+
from multioptpy.Coordinate.redundant_coordinate import calc_int_grad_from_pBmat, calc_cart_grad_from_pBmat
|
|
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
|
+
# Chunked RBF Kernel Utilities
|
|
22
|
+
#########################
|
|
23
|
+
def rbf_kernel_chunked(X1, X2, sigma_f, length_scale, chunk_size=1024):
|
|
24
|
+
"""
|
|
25
|
+
Compute the RBF kernel matrix between X1 (N1, d) and X2 (N2, d) in chunks
|
|
26
|
+
to reduce memory usage.
|
|
27
|
+
Args:
|
|
28
|
+
X1: (N1, d) array
|
|
29
|
+
X2: (N2, d) array
|
|
30
|
+
sigma_f: scalar, kernel amplitude
|
|
31
|
+
length_scale: scalar, kernel length scale
|
|
32
|
+
chunk_size: int, number of X1 rows to process in each chunk
|
|
33
|
+
Returns:
|
|
34
|
+
(N1, N2) RBF kernel matrix
|
|
35
|
+
"""
|
|
36
|
+
N1, d = X1.shape
|
|
37
|
+
N2 = X2.shape[0]
|
|
38
|
+
K = np.zeros((N1, N2), dtype=np.float64)
|
|
39
|
+
|
|
40
|
+
# Precompute norms for X2 to help with chunk-based distance calculations
|
|
41
|
+
X2_sq = np.sum(X2**2, axis=1).reshape(1, -1)
|
|
42
|
+
|
|
43
|
+
start = 0
|
|
44
|
+
while start < N1:
|
|
45
|
+
end = min(start + chunk_size, N1)
|
|
46
|
+
X1_chunk = X1[start:end]
|
|
47
|
+
|
|
48
|
+
# Compute squared distances for chunk
|
|
49
|
+
X1_sq = np.sum(X1_chunk**2, axis=1).reshape(-1, 1)
|
|
50
|
+
dist_sq = X1_sq + X2_sq - 2.0 * np.dot(X1_chunk, X2.T)
|
|
51
|
+
|
|
52
|
+
exponent = -0.5 * dist_sq / (length_scale**2)
|
|
53
|
+
# Clip the exponent to avoid overflow in exp. The upper bound 700 is conservative.
|
|
54
|
+
exponent_clipped = np.clip(exponent, -1000, 1000)
|
|
55
|
+
K[start:end, :] = (sigma_f**2) * np.exp(exponent_clipped)
|
|
56
|
+
start = end
|
|
57
|
+
|
|
58
|
+
return K
|
|
59
|
+
|
|
60
|
+
def rbf_kernel_grad_x_chunked(X1, X2, sigma_f, length_scale, chunk_size=1024):
|
|
61
|
+
"""
|
|
62
|
+
Compute gradient of the RBF kernel with respect to X1 in a chunked manner.
|
|
63
|
+
Output shape: (N1, N2, d).
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
X1: (N1, d)
|
|
67
|
+
X2: (N2, d)
|
|
68
|
+
sigma_f: float
|
|
69
|
+
length_scale: float
|
|
70
|
+
chunk_size: int
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
grad: (N1, N2, d) array
|
|
74
|
+
"""
|
|
75
|
+
N1, d = X1.shape
|
|
76
|
+
N2 = X2.shape[0]
|
|
77
|
+
grad = np.zeros((N1, N2, d), dtype=np.float64)
|
|
78
|
+
|
|
79
|
+
start = 0
|
|
80
|
+
while start < N1:
|
|
81
|
+
end = min(start + chunk_size, N1)
|
|
82
|
+
X1_chunk = X1[start:end] # (chunk_section, d)
|
|
83
|
+
|
|
84
|
+
# Kernel block for chunk
|
|
85
|
+
K_chunk = rbf_kernel_chunked(X1_chunk, X2, sigma_f, length_scale, chunk_size=chunk_size)
|
|
86
|
+
|
|
87
|
+
# diff array for chunk => shape (chunk_section, N2, d)
|
|
88
|
+
diff_chunk = X1_chunk[:, np.newaxis, :] - X2[np.newaxis, :, :]
|
|
89
|
+
|
|
90
|
+
# Gradient formula: (∂k/∂x1) = -diff / l^2 * k
|
|
91
|
+
K_chunk_3d = K_chunk[..., np.newaxis] # shape becomes (chunk_section, N2, 1)
|
|
92
|
+
grad_chunk = -diff_chunk / (length_scale**2) * K_chunk_3d
|
|
93
|
+
|
|
94
|
+
grad[start:end, :, :] = grad_chunk
|
|
95
|
+
start = end
|
|
96
|
+
|
|
97
|
+
return grad
|
|
98
|
+
|
|
99
|
+
def rbf_kernel_hessian_chunked(X1, X2, sigma_f, length_scale, chunk_size=512):
|
|
100
|
+
"""
|
|
101
|
+
Compute Hessian of the RBF kernel with respect to x1, x2:
|
|
102
|
+
Each entry H[a,b] = ∂^2 k(x1, x2) / (∂x1_a ∂x2_b),
|
|
103
|
+
for all pairs in X1, X2. Returns array of shape (N1, N2, d, d).
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
X1: (N1, d)
|
|
107
|
+
X2: (N2, d)
|
|
108
|
+
sigma_f: float
|
|
109
|
+
length_scale: float
|
|
110
|
+
chunk_size: int
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
hessian: (N1, N2, d, d)
|
|
114
|
+
"""
|
|
115
|
+
N1, d = X1.shape
|
|
116
|
+
N2 = X2.shape[0]
|
|
117
|
+
hessian = np.zeros((N1, N2, d, d), dtype=np.float64)
|
|
118
|
+
|
|
119
|
+
# We'll compute the kernel block and then apply the known Hessian formula:
|
|
120
|
+
# H[a,b] = k * [ δ(a,b) / l^2 - (x1[a] - x2[a])*(x1[b] - x2[b]) / l^4 ]
|
|
121
|
+
start = 0
|
|
122
|
+
while start < N1:
|
|
123
|
+
end = min(start + chunk_size, N1)
|
|
124
|
+
X1_chunk = X1[start:end] # shape (chunk_section, d)
|
|
125
|
+
|
|
126
|
+
# k values (chunk_section, N2)
|
|
127
|
+
K_chunk = rbf_kernel_chunked(X1_chunk, X2, sigma_f, length_scale, chunk_size=chunk_size)
|
|
128
|
+
diff_chunk = X1_chunk[:, np.newaxis, :] - X2[np.newaxis, :, :] # (chunk_section, N2, d)
|
|
129
|
+
|
|
130
|
+
# Expand for shape (chunk_section, N2, 1, 1)
|
|
131
|
+
K_4d = K_chunk[:, :, np.newaxis, np.newaxis]
|
|
132
|
+
|
|
133
|
+
# diff outer product => (chunk_section, N2, d, d)
|
|
134
|
+
diff_outer = np.einsum('...i,...j->...ij', diff_chunk, diff_chunk)
|
|
135
|
+
|
|
136
|
+
# Identity for each (d, d)
|
|
137
|
+
eye_d = np.eye(d, dtype=np.float64).reshape(1, 1, d, d)
|
|
138
|
+
|
|
139
|
+
# Hessian formula
|
|
140
|
+
# H = K * [ (I / l^2) - (diff_outer / l^4) ]
|
|
141
|
+
factor1 = eye_d / (length_scale**2)
|
|
142
|
+
factor2 = diff_outer / (length_scale**4)
|
|
143
|
+
|
|
144
|
+
hess_block = K_4d * (factor1 - factor2)
|
|
145
|
+
hessian[start:end] = hess_block
|
|
146
|
+
start = end
|
|
147
|
+
|
|
148
|
+
return hessian
|
|
149
|
+
|
|
150
|
+
##################################
|
|
151
|
+
# Full Gaussian Process Regressor for Energy and Force
|
|
152
|
+
##################################
|
|
153
|
+
|
|
154
|
+
class GaussianProcessRegressor:
|
|
155
|
+
"""
|
|
156
|
+
Gaussian Process Regressor for energies and forces with full kernel
|
|
157
|
+
computations. The training block matrix is built as:
|
|
158
|
+
K = [ K_EE K_EF ]
|
|
159
|
+
[ K_FE K_FF ]
|
|
160
|
+
where:
|
|
161
|
+
- K_EE[i,j] = k(x_i, x_j)
|
|
162
|
+
- K_EF[i, j] = -∂k(x_i, x_j)/∂x_j (each block is 1 x d)
|
|
163
|
+
- K_FE[i, j] = -∂k(x_i, x_j)/∂x_i (each block is d x 1)
|
|
164
|
+
- K_FF[i,j] = ∂^2k(x_i, x_j)/(∂x_i ∂x_j) (each block is d x d)
|
|
165
|
+
The target vector is: Y = [E; F] where energies E are (N,) and forces F are (N,d).
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
def __init__(self):
|
|
169
|
+
"""
|
|
170
|
+
Args:
|
|
171
|
+
sigma_f: RBF kernel amplitude.
|
|
172
|
+
length_scale: RBF kernel length scale.
|
|
173
|
+
noise_e: Noise standard deviation for energies.
|
|
174
|
+
noise_f: Noise standard deviation for forces.
|
|
175
|
+
chunk_size: Chunk size for kernel computations.
|
|
176
|
+
"""
|
|
177
|
+
# Training data
|
|
178
|
+
self.X = None # shape (N, d)
|
|
179
|
+
self.E = None # shape (N,)
|
|
180
|
+
self.F = None # shape (N, d)
|
|
181
|
+
|
|
182
|
+
# Optimized hyperparameters and alpha for predictions
|
|
183
|
+
self.theta_opt = None
|
|
184
|
+
self.alpha = None
|
|
185
|
+
|
|
186
|
+
def _build_block_matrix_chunked(self, X, E, F, sigma_f, length_scale, noise_e, noise_f):
|
|
187
|
+
"""
|
|
188
|
+
Build the full block kernel matrix in a chunked manner.
|
|
189
|
+
Returns:
|
|
190
|
+
K_full: (N + N*d, N + N*d) kernel matrix.
|
|
191
|
+
Y: (N + N*d,) target vector.
|
|
192
|
+
"""
|
|
193
|
+
N, d = X.shape
|
|
194
|
+
|
|
195
|
+
# K_EE: Energy vs Energy, shape (N, N)
|
|
196
|
+
K_EE = rbf_kernel_chunked(X, X, sigma_f, length_scale, chunk_size=self.chunk_size)
|
|
197
|
+
|
|
198
|
+
# K_EF: Energy vs Force, shape (N, N*d)
|
|
199
|
+
# For each training pair (i,j), block = -∂k(x_i, x_j)/∂x_j.
|
|
200
|
+
# Compute gradient with respect to first argument for (X, X) then use symmetry.
|
|
201
|
+
grad_X = rbf_kernel_grad_x_chunked(X, X, sigma_f, length_scale, chunk_size=self.chunk_size) # shape (N, N, d)
|
|
202
|
+
# For derivative w.r.t second argument, we have: ∂k(x_i, x_j)/∂x_j = -∂k(x_j, x_i)/∂x_j.
|
|
203
|
+
# Therefore, K_EF[i, j] = -∂k(x_i, x_j)/∂x_j = grad_X[j, i]
|
|
204
|
+
K_EF = np.zeros((N, N * d), dtype=np.float64)
|
|
205
|
+
for i in range(N):
|
|
206
|
+
for j in range(N):
|
|
207
|
+
K_EF[i, j * d:(j + 1) * d] = grad_X[j, i]
|
|
208
|
+
|
|
209
|
+
# K_FE: Force vs Energy, shape (N*d, N)
|
|
210
|
+
# For each training pair (i,j), block = -∂k(x_i, x_j)/∂x_i.
|
|
211
|
+
# That is simply: K_FE[i, j] = -grad_X[i, j]
|
|
212
|
+
K_FE = np.zeros((N * d, N), dtype=np.float64)
|
|
213
|
+
for i in range(N):
|
|
214
|
+
for j in range(N):
|
|
215
|
+
K_FE[i * d:(i + 1) * d, j] = -grad_X[i, j]
|
|
216
|
+
|
|
217
|
+
# K_FF: Force vs Force, shape (N*d, N*d)
|
|
218
|
+
# For each pair (i,j), block = ∂^2 k(x_i, x_j)/(∂x_i ∂x_j)
|
|
219
|
+
H = rbf_kernel_hessian_chunked(X, X, sigma_f, length_scale, chunk_size=self.chunk_size) # shape (N, N, d, d)
|
|
220
|
+
K_FF = np.zeros((N * d, N * d), dtype=np.float64)
|
|
221
|
+
for i in range(N):
|
|
222
|
+
for j in range(N):
|
|
223
|
+
K_FF[i * d:(i + 1) * d, j * d:(j + 1) * d] = H[i, j]
|
|
224
|
+
|
|
225
|
+
# Add noise to diagonal blocks
|
|
226
|
+
K_EE += (noise_e**2) * np.eye(N, dtype=np.float64)
|
|
227
|
+
K_FF += (noise_f**2) * np.eye(N * d, dtype=np.float64)
|
|
228
|
+
|
|
229
|
+
# Combine blocks into full matrix:
|
|
230
|
+
# K_full = [ K_EE K_EF ]
|
|
231
|
+
# [ K_FE K_FF ]
|
|
232
|
+
top = np.hstack((K_EE, K_EF))
|
|
233
|
+
bottom = np.hstack((K_FE, K_FF))
|
|
234
|
+
K_full = np.vstack((top, bottom))
|
|
235
|
+
|
|
236
|
+
# Construct target vector Y = [E; F_vectorized]
|
|
237
|
+
Y = np.concatenate([E, F.reshape(-1)], axis=0)
|
|
238
|
+
return K_full, Y
|
|
239
|
+
|
|
240
|
+
def _neg_log_marginal_likelihood(self, params):
|
|
241
|
+
"""
|
|
242
|
+
Negative log marginal likelihood function.
|
|
243
|
+
params: [sigma_f, length_scale, noise_e, noise_f]
|
|
244
|
+
"""
|
|
245
|
+
sigma_f, length_scale, noise_e, noise_f = params
|
|
246
|
+
K, Y = self._build_block_matrix_chunked(self.X, self.E, self.F,
|
|
247
|
+
sigma_f, length_scale, noise_e, noise_f)
|
|
248
|
+
N, d = self.X.shape
|
|
249
|
+
n_data = N + N * d
|
|
250
|
+
try:
|
|
251
|
+
L = np.linalg.cholesky(K)
|
|
252
|
+
alpha = np.linalg.solve(L.T, np.linalg.solve(L, Y))
|
|
253
|
+
logdetK = 2.0 * np.sum(np.log(np.diag(L)))
|
|
254
|
+
except np.linalg.LinAlgError:
|
|
255
|
+
return 1e20
|
|
256
|
+
|
|
257
|
+
nll = 0.5 * np.dot(Y, alpha) + 0.5 * logdetK + 0.5 * n_data * np.log(2.0 * np.pi)
|
|
258
|
+
print("NLML func:", nll)
|
|
259
|
+
return nll
|
|
260
|
+
|
|
261
|
+
def fit(self, X, E, F, initial_params=(1.0, 1.0, 1e-3, 1e-3)):
|
|
262
|
+
"""
|
|
263
|
+
Fit the GPR model to energy and force data.
|
|
264
|
+
Args:
|
|
265
|
+
X: (N, d) array of positions.
|
|
266
|
+
E: (N,) array of energy values.
|
|
267
|
+
F: (N, d) array of force values.
|
|
268
|
+
initial_params: Tuple of initial hyperparameters (sigma_f, length_scale, noise_e, noise_f).
|
|
269
|
+
"""
|
|
270
|
+
self.X = X
|
|
271
|
+
self.E = E
|
|
272
|
+
self.F = F
|
|
273
|
+
|
|
274
|
+
bounds = [(1e-9, None), (1e-9, None), (1e-9, None), (1e-9, None)]
|
|
275
|
+
res = minimize(self._neg_log_marginal_likelihood, x0=initial_params, bounds=bounds, method='L-BFGS-B', tol=1e-10)
|
|
276
|
+
self.theta_opt = res.x
|
|
277
|
+
self._compute_alpha()
|
|
278
|
+
|
|
279
|
+
self.sigma_f, self.length_scale, self.noise_e, self.noise_f = self.theta_opt
|
|
280
|
+
|
|
281
|
+
def _compute_alpha(self):
|
|
282
|
+
"""
|
|
283
|
+
Compute alpha = K^{-1} Y with the optimized hyperparameters.
|
|
284
|
+
"""
|
|
285
|
+
sigma_f, length_scale, noise_e, noise_f = self.theta_opt
|
|
286
|
+
K, Y = self._build_block_matrix_chunked(self.X, self.E, self.F,
|
|
287
|
+
sigma_f, length_scale, noise_e, noise_f)
|
|
288
|
+
L = np.linalg.cholesky(K)
|
|
289
|
+
self.alpha = np.linalg.solve(L.T, np.linalg.solve(L, Y))
|
|
290
|
+
|
|
291
|
+
def predict_energy_and_forces(self, X_star):
|
|
292
|
+
"""
|
|
293
|
+
Predict the energy and forces at new positions X_star.
|
|
294
|
+
For each test point s:
|
|
295
|
+
E(s) = k(s, X) * alpha_E + [-∂k(s, X)/∂x] * alpha_F,
|
|
296
|
+
F(s) = [-∂k(s, X)/∂s] * alpha_E + [∂^2k(s, X)/(∂s ∂x)] * alpha_F,
|
|
297
|
+
where alpha_E = self.alpha[:N] and alpha_F = self.alpha[N:].reshape(N, d).
|
|
298
|
+
Args:
|
|
299
|
+
X_star: (M, d) array of positions.
|
|
300
|
+
Returns:
|
|
301
|
+
E_pred: (M,) predicted energies.
|
|
302
|
+
F_pred: (M, d) predicted forces.
|
|
303
|
+
"""
|
|
304
|
+
sigma_f, length_scale, noise_e, noise_f = self.theta_opt
|
|
305
|
+
X_train = self.X
|
|
306
|
+
N, d = X_train.shape
|
|
307
|
+
M = X_star.shape[0]
|
|
308
|
+
|
|
309
|
+
# Compute K_star_EE: k(X_star, X_train), shape (M, N)
|
|
310
|
+
K_star_EE = rbf_kernel_chunked(X_star, X_train, sigma_f, length_scale, chunk_size=self.chunk_size)
|
|
311
|
+
|
|
312
|
+
# Compute derivative of k w.r.t test input: ∇_{s} k(s, x)
|
|
313
|
+
# This is used for K_star_FE: -∇_s k(s, x)
|
|
314
|
+
grad_test = rbf_kernel_grad_x_chunked(X_star, X_train, sigma_f, length_scale, chunk_size=self.chunk_size)
|
|
315
|
+
# K_star_FE = -∇_{s} k(s,x), shape (M, N, d)
|
|
316
|
+
K_star_FE = -grad_test
|
|
317
|
+
|
|
318
|
+
# Compute derivative of k w.r.t training input: ∇_{x} k(x, s)
|
|
319
|
+
# Using symmetry, ∇_{x} k(s, x) = -∇_{x} k(x, s)
|
|
320
|
+
grad_train = rbf_kernel_grad_x_chunked(X_train, X_star, sigma_f, length_scale, chunk_size=self.chunk_size)
|
|
321
|
+
# Transpose grad_train to shape (M, N, d)
|
|
322
|
+
grad_train_T = np.transpose(grad_train, (1, 0, 2))
|
|
323
|
+
# K_star_EF = -∂k(s,x)/∂x. But note: ∂k(s,x)/∂x = -∇_{x} k(x,s), so:
|
|
324
|
+
K_star_EF = grad_train_T # shape (M, N, d)
|
|
325
|
+
|
|
326
|
+
# Compute Hessian cross covariance: ∂^2k(s, x)/(∂s ∂x), shape (M, N, d, d)
|
|
327
|
+
K_star_FF = rbf_kernel_hessian_chunked(X_star, X_train, sigma_f, length_scale, chunk_size=self.chunk_size)
|
|
328
|
+
|
|
329
|
+
# Reshape alpha into alpha_E and alpha_F
|
|
330
|
+
alpha_E = self.alpha[:N] # shape (N,)
|
|
331
|
+
alpha_F = self.alpha[N:].reshape(N, d) # shape (N, d)
|
|
332
|
+
|
|
333
|
+
# Predicted energy: E_pred = K_star_EE * alpha_E + sum_{j,l} K_star_EF[:, j, l] * alpha_F[j,l]
|
|
334
|
+
E_part1 = np.dot(K_star_EE, alpha_E) # shape (M,)
|
|
335
|
+
E_part2 = np.einsum('mjd,jd->m', K_star_EF, alpha_F) # shape (M,)
|
|
336
|
+
E_pred = E_part1 + E_part2
|
|
337
|
+
|
|
338
|
+
# Predicted force: F_pred = sum_j [K_star_FE[:,j,:]*alpha_E[j]] + sum_j [K_star_FF[:,j,:,:] dot alpha_F[j]]
|
|
339
|
+
F_part1 = np.einsum('mnd,n->md', K_star_FE, alpha_E) # shape (M, d)
|
|
340
|
+
F_part2 = np.einsum('mnij,nj->mi', K_star_FF, alpha_F) # shape (M, d)
|
|
341
|
+
F_pred = F_part1 + F_part2
|
|
342
|
+
|
|
343
|
+
return E_pred, F_pred
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class CaluculationGPNEB:
|
|
347
|
+
def __init__(self, directory, APPLY_CI_NEB=99999):
|
|
348
|
+
self.APPLY_CI_NEB = APPLY_CI_NEB
|
|
349
|
+
self.base_dir = directory
|
|
350
|
+
self.init_param = None
|
|
351
|
+
self.spes_iter = 50
|
|
352
|
+
self.chunk_size = 64
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
def calc_quickmin_step(self, positions, velocities, forces, mass=1.0, dt=0.01, alpha=0.0):
|
|
356
|
+
dot_vf = np.sum(velocities * forces, axis=(1, 2), keepdims=True)
|
|
357
|
+
mask = dot_vf < 0
|
|
358
|
+
velocities[mask] = 0.0
|
|
359
|
+
velocities = (1 - alpha) * velocities + (dt / mass) * forces
|
|
360
|
+
positions = positions + dt * velocities
|
|
361
|
+
return positions, velocities
|
|
362
|
+
|
|
363
|
+
def judge_early_stopping(self, spes_energy_list, prev_spes_energy_list, spes_force_list, prev_spes_force_list):
|
|
364
|
+
boolean_list = []
|
|
365
|
+
nnode = len(spes_energy_list)
|
|
366
|
+
for i in range(nnode):
|
|
367
|
+
pass
|
|
368
|
+
### implement early stopping conditions
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
return boolean_list
|
|
372
|
+
|
|
373
|
+
def calc_force(self, geometry_num_list, energy_list, gradient_list, optimize_num, element_list):
|
|
374
|
+
nnode = len(energy_list)
|
|
375
|
+
prev_geometry_num_list = copy.copy(geometry_num_list)
|
|
376
|
+
print("Start GPR fitting.")
|
|
377
|
+
if optimize_num == 0:
|
|
378
|
+
GPR = GaussianProcessRegressor()
|
|
379
|
+
init_param = {"sigma_f": 1.0, "length_scale": 1.0, "noise_e": 1e-3, "noise_f": 1e-3, "chunk_size": self.chunk_size}
|
|
380
|
+
train_geom_list = []
|
|
381
|
+
train_force_list = []
|
|
382
|
+
train_energy_list = []
|
|
383
|
+
|
|
384
|
+
for i in range(nnode):
|
|
385
|
+
train_geom_list.append(geometry_num_list[i].reshape(-1))
|
|
386
|
+
train_energy_list.append(energy_list[i])
|
|
387
|
+
train_force_list.append(gradient_list[i].reshape(-1))
|
|
388
|
+
train_geom_list = np.array(train_geom_list, dtype = "float64")
|
|
389
|
+
train_energy_list = np.array(train_energy_list, dtype = "float64")
|
|
390
|
+
train_force_list = np.array(train_force_list, dtype = "float64")
|
|
391
|
+
GPR.fit(train_geom_list, train_energy_list, train_force_list, init_param)
|
|
392
|
+
|
|
393
|
+
else:
|
|
394
|
+
GPR = GaussianProcessRegressor()
|
|
395
|
+
train_geom_list = np.load(self.base_dir + "/train_geometry_num_list.npy")
|
|
396
|
+
train_energy_list = np.load(self.base_dir + "/train_energy_list.npy")
|
|
397
|
+
train_force_list = np.load(self.base_dir + "/train_force_list.npy")
|
|
398
|
+
for i in range(nnode):
|
|
399
|
+
np.vstack(train_geom_list, geometry_num_list[i].reshape(-1))
|
|
400
|
+
np.hstack(train_energy_list, energy_list[i])
|
|
401
|
+
np.vstack(train_force_list, gradient_list[i].reshape(-1))
|
|
402
|
+
|
|
403
|
+
GPR.fit(train_geom_list, train_energy_list, train_force_list, self.init_param)
|
|
404
|
+
|
|
405
|
+
print("GPR fitting was done.")
|
|
406
|
+
print("Start optimization path on SPES (Surrogate Potential Energy Surface).")
|
|
407
|
+
|
|
408
|
+
velocities_list = np.zeros_like(gradient_list)
|
|
409
|
+
for l in range(self.spes_iter):
|
|
410
|
+
print("SPES Opt ITR.:", l)
|
|
411
|
+
input_pos = geometry_num_list
|
|
412
|
+
### reshape positions (3N, nnode) input_pos
|
|
413
|
+
spes_energy_list, spes_force_list = GPR.predict_energy_and_forces(input_pos)
|
|
414
|
+
### reshape positions (nnode, N, 3) input_pos
|
|
415
|
+
total_neb_spes_force_list = self.calc_force_for_gpr(input_pos, spes_energy_list, spes_force_list)
|
|
416
|
+
### reshape total_neb_spes_force_list (nnode, N, 3)
|
|
417
|
+
for j in range(nnode):
|
|
418
|
+
input_pos[j], velocities_list[j] = self.calc_quickmin_step(input_pos[j], velocities_list[j], total_neb_spes_force_list[j], mass=1.0, dt=0.01, alpha=0.0)
|
|
419
|
+
if l > 0:
|
|
420
|
+
### implement conditions of early stopping. is_early_stopping_list = self.judge_early_stopping(spes_energy_list, prev_spes_energy_list, spes_force_list, prev_spes_force_list)
|
|
421
|
+
pass
|
|
422
|
+
else:
|
|
423
|
+
pass
|
|
424
|
+
|
|
425
|
+
for j in range(nnode):
|
|
426
|
+
if is_early_stopping_list[j]:
|
|
427
|
+
print("Detect abnormal force and energy. Stop updating geometry.")
|
|
428
|
+
|
|
429
|
+
else:
|
|
430
|
+
geometry_num_list[j] = input_pos[j]
|
|
431
|
+
prev_spes_energy_list = spes_energy_list
|
|
432
|
+
prev_spes_force_list = spes_force_list
|
|
433
|
+
|
|
434
|
+
print("Optimization on SPES was done.")
|
|
435
|
+
np.save(self.base_dir + "/train_geometry_num_list.npy", train_geom_list)
|
|
436
|
+
np.save(self.base_dir + "/train_energy_list.npy", train_energy_list)
|
|
437
|
+
np.save(self.base_dir + "/train_force_list.npy", train_force_list)
|
|
438
|
+
self.init_param = {"sigma_f": GPR.sigma_f, "length_scale": GPR.length_scale, "noise_e": GPR.noise_e, "noise_f": GPR.noise_f, "chunk_size": self.chunk_size}
|
|
439
|
+
total_force_list = geometry_num_list - prev_geometry_num_list
|
|
440
|
+
return np.array(total_force_list, dtype = "float64")
|
|
441
|
+
|
|
442
|
+
def calc_force_for_gpr(self, geometry_num_list, energy_list, gradient_list):
|
|
443
|
+
print("GPNEBGPNEBGPNEBGPNEBGPNEBGPNEBGPNEBGPNEBGPNEBGPNEBGPNEB")
|
|
444
|
+
nnode = len(energy_list)
|
|
445
|
+
local_max_energy_list_index, local_min_energy_list_index = extremum_list_index(energy_list)
|
|
446
|
+
total_force_list = []
|
|
447
|
+
for i in range(nnode):
|
|
448
|
+
if i == 0:
|
|
449
|
+
total_force_list.append(-1*np.array(gradient_list[0], dtype = "float64"))
|
|
450
|
+
continue
|
|
451
|
+
elif i == nnode-1:
|
|
452
|
+
total_force_list.append(-1*np.array(gradient_list[nnode-1], dtype = "float64"))
|
|
453
|
+
continue
|
|
454
|
+
tmp_grad = copy.copy(gradient_list[i]).reshape(-1, 1)
|
|
455
|
+
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])
|
|
456
|
+
total_force_list.append(-1*force.reshape(-1, 3))
|
|
457
|
+
return np.array(total_force_list, dtype = "float64")
|
|
458
|
+
|
|
459
|
+
def calc_project_out_grad(self, coord_1, coord_2, coord_3, grad_2, energy_list):# grad: (3N, 1), geom_num_list: (N, 3)
|
|
460
|
+
natom = len(coord_2)
|
|
461
|
+
tmp_grad = copy.copy(grad_2)
|
|
462
|
+
if energy_list[0] < energy_list[1] and energy_list[1] < energy_list[2]:
|
|
463
|
+
B_mat = self.calc_B_matrix_for_NEB_tangent(coord_2, coord_3)
|
|
464
|
+
int_grad = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat)
|
|
465
|
+
projection_grad = calc_cart_grad_from_pBmat(-1*int_grad, B_mat)
|
|
466
|
+
proj_grad = tmp_grad.reshape(3*natom, 1) + projection_grad
|
|
467
|
+
tangent_grad = projection_grad
|
|
468
|
+
elif energy_list[0] > energy_list[1] and energy_list[1] > energy_list[2]:
|
|
469
|
+
B_mat = self.calc_B_matrix_for_NEB_tangent(coord_1, coord_2)
|
|
470
|
+
int_grad = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat)
|
|
471
|
+
projection_grad = calc_cart_grad_from_pBmat(-1*int_grad, B_mat)
|
|
472
|
+
proj_grad = tmp_grad.reshape(3*natom, 1) + projection_grad
|
|
473
|
+
tangent_grad = projection_grad
|
|
474
|
+
else:
|
|
475
|
+
B_mat_plus = self.calc_B_matrix_for_NEB_tangent(coord_2, coord_3)
|
|
476
|
+
B_mat_minus = self.calc_B_matrix_for_NEB_tangent(coord_1, coord_2)
|
|
477
|
+
int_grad_plus = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat_plus)
|
|
478
|
+
int_grad_minus = calc_int_grad_from_pBmat(tmp_grad.reshape(3*natom, 1), B_mat_minus)
|
|
479
|
+
max_ene = max(abs(energy_list[2] - energy_list[1]), abs(energy_list[1] - energy_list[0]))
|
|
480
|
+
min_ene = min(abs(energy_list[2] - energy_list[1]), abs(energy_list[1] - energy_list[0]))
|
|
481
|
+
a = (max_ene + 1e-15) / (max_ene + min_ene + 1e-15)
|
|
482
|
+
b = (min_ene + 1e-15) / (max_ene + min_ene + 1e-15)
|
|
483
|
+
|
|
484
|
+
if energy_list[0] < energy_list[2]:
|
|
485
|
+
projection_grad_plus = calc_cart_grad_from_pBmat(-a*int_grad_plus, B_mat_plus)
|
|
486
|
+
projection_grad_minus = calc_cart_grad_from_pBmat(-b*int_grad_minus, B_mat_minus)
|
|
487
|
+
|
|
488
|
+
else:
|
|
489
|
+
projection_grad_plus = calc_cart_grad_from_pBmat(-b*int_grad_plus, B_mat_plus)
|
|
490
|
+
projection_grad_minus = calc_cart_grad_from_pBmat(-a*int_grad_minus, B_mat_minus)
|
|
491
|
+
proj_grad = tmp_grad.reshape(3*natom, 1) + projection_grad_plus + projection_grad_minus
|
|
492
|
+
tangent_grad = projection_grad_plus + projection_grad_minus
|
|
493
|
+
return proj_grad, tangent_grad
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def calc_B_matrix_for_NEB_tangent(self, coord_1, coord_2):
|
|
497
|
+
natom = len(coord_2)
|
|
498
|
+
B_mat = np.zeros((natom, 3*natom))
|
|
499
|
+
|
|
500
|
+
for i in range(natom):
|
|
501
|
+
norm_12 = np.linalg.norm(coord_1[i] - coord_2[i]) + 1e-15
|
|
502
|
+
dr12_dx2 = (coord_2[i][0] - coord_1[i][0]) / norm_12
|
|
503
|
+
dr12_dy2 = (coord_2[i][1] - coord_1[i][1]) / norm_12
|
|
504
|
+
dr12_dz2 = (coord_2[i][2] - coord_1[i][2]) / norm_12
|
|
505
|
+
B_mat[i][3*i] = dr12_dx2
|
|
506
|
+
B_mat[i][3*i+1] = dr12_dy2
|
|
507
|
+
B_mat[i][3*i+2] = dr12_dz2
|
|
508
|
+
|
|
509
|
+
return B_mat
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.signal import argrelextrema
|
|
3
|
+
|
|
4
|
+
def extremum_list_index(energy_list):
|
|
5
|
+
local_max_energy_list_index = argrelextrema(energy_list, np.greater)
|
|
6
|
+
inverse_energy_list = (-1)*energy_list
|
|
7
|
+
local_min_energy_list_index = argrelextrema(inverse_energy_list, np.greater)
|
|
8
|
+
|
|
9
|
+
local_max_energy_list_index = local_max_energy_list_index[0].tolist()
|
|
10
|
+
local_min_energy_list_index = local_min_energy_list_index[0].tolist()
|
|
11
|
+
local_max_energy_list_index.append(0)
|
|
12
|
+
local_min_energy_list_index.append(0)
|
|
13
|
+
local_max_energy_list_index.append(0)
|
|
14
|
+
local_min_energy_list_index.append(0)
|
|
15
|
+
return local_max_energy_list_index, local_min_energy_list_index
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CaluculationLUP:
|
|
21
|
+
def __init__(self, APPLY_CI_NEB=99999):
|
|
22
|
+
self.spring_constant_k = 0.01
|
|
23
|
+
self.APPLY_CI_NEB = APPLY_CI_NEB
|
|
24
|
+
self.force_const_for_cineb = 0.01
|
|
25
|
+
|
|
26
|
+
def calc_force(self, geometry_num_list, energy_list, gradient_list, optimize_num, element_list):
|
|
27
|
+
print("LUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUPLUP")
|
|
28
|
+
#ref. Ulitsky, A., & Elber, R. (1990). A new technique to calculate steepest descent paths in flexible polyatomic systems. The Journal of Chemical Physics, 92(2), 1510.
|
|
29
|
+
#https://doi.org/10.1063/1.458112
|
|
30
|
+
local_max_energy_list_index, local_min_energy_list_index = extremum_list_index(energy_list)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
total_force_list = [((-1)*np.array(gradient_list[0], dtype = "float64")).tolist()]
|
|
34
|
+
for i in range(1,len(energy_list)-1):
|
|
35
|
+
tau_plus, tau_minus, tau = [], [], []
|
|
36
|
+
|
|
37
|
+
delta_max_energy = np.array(max([(energy_list[i+1]-energy_list[i]),(energy_list[i-1]-energy_list[i])]), dtype = "float64")
|
|
38
|
+
delta_min_energy = np.array(min([(energy_list[i+1]-energy_list[i]),(energy_list[i-1]-energy_list[i])]), dtype = "float64")
|
|
39
|
+
|
|
40
|
+
if (energy_list[i-1] < energy_list[i]) and (energy_list[i] < energy_list[i+1]):
|
|
41
|
+
for t in range(len(geometry_num_list[i])):
|
|
42
|
+
tau_vector = geometry_num_list[i+1][t]-geometry_num_list[i][t]
|
|
43
|
+
tau_norm = np.linalg.norm(geometry_num_list[i+1][t]-geometry_num_list[i][t], ord=2)
|
|
44
|
+
tau.append(np.divide(tau_vector, tau_norm, out=np.zeros_like(geometry_num_list[i][t]) ,where=np.linalg.norm(geometry_num_list[i+1][t]-geometry_num_list[i][t], ord=2)!=0).tolist())
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
elif (energy_list[i-1] > energy_list[i]) and (energy_list[i] > energy_list[i+1]):
|
|
48
|
+
for t in range(len(geometry_num_list[i])):
|
|
49
|
+
tau_vector = geometry_num_list[i][t]-geometry_num_list[i-1][t]
|
|
50
|
+
tau_norm = np.linalg.norm(geometry_num_list[i][t]-geometry_num_list[i-1][t], ord=2)
|
|
51
|
+
tau.append(np.divide(tau_vector, tau_norm, out=np.zeros_like(geometry_num_list[i][t]), where=np.linalg.norm(geometry_num_list[i][t]-geometry_num_list[i-1][t], ord=2)!=0).tolist())
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
else: #((energy_list[i-1] >= energy_list[i]) and (energy_list[i] <= energy_list[i+1])) or ((energy_list[i-1] <= energy_list[i]) and (energy_list[i] >= energy_list[i+1])):
|
|
56
|
+
for t in range(len(geometry_num_list[i])):
|
|
57
|
+
tau_minus_vector = geometry_num_list[i][t]-geometry_num_list[i-1][t]
|
|
58
|
+
tau_minus_norm = np.linalg.norm(geometry_num_list[i][t]-geometry_num_list[i-1][t], ord=2)
|
|
59
|
+
tau_minus.append(np.divide(tau_minus_vector, tau_minus_norm
|
|
60
|
+
,out=np.zeros_like(geometry_num_list[i][t]),
|
|
61
|
+
where=np.linalg.norm(geometry_num_list[i][t]-geometry_num_list[i-1][t], ord=2)!=0).tolist())
|
|
62
|
+
|
|
63
|
+
for t in range(len(geometry_num_list[i])):
|
|
64
|
+
tau_plus_vector = geometry_num_list[i+1][t]-geometry_num_list[i][t]
|
|
65
|
+
tau_plus_norm = np.linalg.norm(geometry_num_list[i+1][t]-geometry_num_list[i][t], ord=2)
|
|
66
|
+
tau_plus.append(np.divide(tau_plus_vector, tau_plus_norm, out=np.zeros_like(geometry_num_list[i][t]), where=np.linalg.norm(geometry_num_list[i+1][t]-geometry_num_list[i][t], ord=2)!=0).tolist())
|
|
67
|
+
|
|
68
|
+
if energy_list[i-1] > energy_list[i+1]:
|
|
69
|
+
for t in range(len(geometry_num_list[i])):
|
|
70
|
+
tau_vector = (tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy)
|
|
71
|
+
tau_norm = np.linalg.norm(tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy, ord=2)
|
|
72
|
+
tau.append(np.divide(tau_vector,tau_norm, out=np.zeros_like(tau_plus[0]) ,where=np.linalg.norm(tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy!=0)).tolist())
|
|
73
|
+
else:
|
|
74
|
+
for t in range(len(geometry_num_list[i])):
|
|
75
|
+
tau_vector = (tau_plus[t]*delta_max_energy+tau_minus[t]*delta_min_energy)
|
|
76
|
+
tau_norm = np.linalg.norm(tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy, ord=2)
|
|
77
|
+
tau.append(np.divide(tau_vector, tau_norm,out=np.zeros_like(tau_minus[0]) ,where=np.linalg.norm(tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy, ord=2)!=0 ).tolist())
|
|
78
|
+
|
|
79
|
+
tau_plus, tau_minus, tau = np.array(tau_plus, dtype = "float64"), np.array(tau_minus, dtype = "float64"), np.array(tau, dtype = "float64")
|
|
80
|
+
#print("tau_minus:\n",tau_minus)
|
|
81
|
+
#print("tau_plus:\n",tau_plus)
|
|
82
|
+
#print("tau:\n",str(tau))
|
|
83
|
+
force_perpendicularity = []
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
for f in range(len(geometry_num_list[i])):
|
|
87
|
+
grad = 0.0
|
|
88
|
+
|
|
89
|
+
for gg in range(len(gradient_list[i])):
|
|
90
|
+
grad += np.linalg.norm(gradient_list[i][gg], ord=2)
|
|
91
|
+
|
|
92
|
+
grad = grad/len(gradient_list[i])
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
force_perpendicularity.append(np.array(gradient_list[i][f]-(np.dot(gradient_list[i][f], tau[f]))*tau[f], dtype = "float64"))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
force_perpendicularity = np.array(force_perpendicularity, dtype = "float64")
|
|
101
|
+
total_force = np.array((-1)*force_perpendicularity, dtype = "float64")
|
|
102
|
+
|
|
103
|
+
if np.nanmean(np.nanmean(total_force)) > 10:
|
|
104
|
+
total_force = total_force / np.nanmean(np.nanmean(total_force))
|
|
105
|
+
|
|
106
|
+
total_force_list.append(total_force.tolist())
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
total_force_list.append(((-1)*np.array(gradient_list[-1], dtype = "float64")).tolist())
|
|
112
|
+
|
|
113
|
+
return np.array(total_force_list, dtype = "float64")
|