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,448 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import copy
|
|
3
|
+
from scipy.interpolate import BSpline, interp1d
|
|
4
|
+
from scipy.signal import argrelextrema
|
|
5
|
+
from numpy.polynomial import polynomial as P
|
|
6
|
+
|
|
7
|
+
# ref. : S.-i. Koda and S. Saito, Locating Transition States by Variational Reaction Path Optimization with an Energy-Derivative-Free Objective Function, JCTC, 20, 2798–2811 (2024). doi: 10.1021/acs.jctc.3c01246
|
|
8
|
+
|
|
9
|
+
# Helper functions from the original context, now outside the class
|
|
10
|
+
def extremum_list_index(energy_list):
|
|
11
|
+
"""Finds indices of local maxima and minima in an energy list."""
|
|
12
|
+
energy_list = np.array(energy_list)
|
|
13
|
+
local_max_energy_list_index = argrelextrema(energy_list, np.greater)
|
|
14
|
+
inverse_energy_list = (-1) * energy_list
|
|
15
|
+
local_min_energy_list_index = argrelextrema(inverse_energy_list, np.greater)
|
|
16
|
+
|
|
17
|
+
local_max_energy_list_index = local_max_energy_list_index[0].tolist()
|
|
18
|
+
local_min_energy_list_index = local_min_energy_list_index[0].tolist()
|
|
19
|
+
|
|
20
|
+
# Ensure endpoints are not considered extrema
|
|
21
|
+
if 0 in local_min_energy_list_index: local_min_energy_list_index.remove(0)
|
|
22
|
+
if 0 in local_max_energy_list_index: local_max_energy_list_index.remove(0)
|
|
23
|
+
if len(energy_list)-1 in local_min_energy_list_index: local_min_energy_list_index.remove(len(energy_list)-1)
|
|
24
|
+
if len(energy_list)-1 in local_max_energy_list_index: local_max_energy_list_index.remove(len(energy_list)-1)
|
|
25
|
+
|
|
26
|
+
return local_max_energy_list_index, local_min_energy_list_index
|
|
27
|
+
|
|
28
|
+
class CaluculationDMF():
|
|
29
|
+
"""
|
|
30
|
+
Class to calculate forces for path optimization.
|
|
31
|
+
This version implements the Direct MaxFlux (DMF) algorithm principles.
|
|
32
|
+
"""
|
|
33
|
+
def __init__(self, APPLY_CI_NEB=99999, beta=10.0, nsegs=4, dspl=3):
|
|
34
|
+
"""
|
|
35
|
+
Initializes the calculator.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
APPLY_CI_NEB (int): Legacy parameter, not used by DMF logic.
|
|
39
|
+
beta (float): Reciprocal temperature for the MaxFlux method.
|
|
40
|
+
nsegs (int): Number of segments for the B-spline path.
|
|
41
|
+
dspl (int): Degree of the B-spline functions.
|
|
42
|
+
"""
|
|
43
|
+
self.APPLY_CI_NEB = APPLY_CI_NEB # Retained for structural consistency
|
|
44
|
+
self.beta = beta
|
|
45
|
+
self.nsegs = nsegs
|
|
46
|
+
self.dspl = dspl
|
|
47
|
+
self.nbasis = nsegs + dspl
|
|
48
|
+
|
|
49
|
+
# B-spline basis setup
|
|
50
|
+
_t_knot = np.concatenate([
|
|
51
|
+
np.zeros(dspl),
|
|
52
|
+
np.linspace(0.0, 1.0, nsegs + 1),
|
|
53
|
+
np.ones(dspl)])
|
|
54
|
+
self._t_knot = _t_knot
|
|
55
|
+
|
|
56
|
+
# Create basis functions with error handling
|
|
57
|
+
self._basis = [[], []]
|
|
58
|
+
for i in range(self.nbasis):
|
|
59
|
+
try:
|
|
60
|
+
self._basis[0].append(BSpline(self._t_knot, np.identity(self.nbasis)[i], dspl, extrapolate=False))
|
|
61
|
+
self._basis[1].append(BSpline(self._t_knot, np.identity(self.nbasis)[i], dspl, extrapolate=False).derivative(nu=1))
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"Error creating BSpline basis {i}: {e}")
|
|
64
|
+
# Fall back to simple basis if needed
|
|
65
|
+
self._basis[0].append(None)
|
|
66
|
+
self._basis[1].append(None)
|
|
67
|
+
|
|
68
|
+
def _get_basis_values(self, t_seq, nu=0):
|
|
69
|
+
"""Evaluates B-spline basis functions or their derivatives at given points."""
|
|
70
|
+
if nu not in [0, 1]:
|
|
71
|
+
raise ValueError("Only nu=0 and nu=1 are supported.")
|
|
72
|
+
|
|
73
|
+
# Ensure t_seq is within [0, 1] to avoid extrapolation issues
|
|
74
|
+
t_seq_clipped = np.clip(t_seq, 0.0, 1.0)
|
|
75
|
+
|
|
76
|
+
result = []
|
|
77
|
+
for b in self._basis[nu]:
|
|
78
|
+
if b is None:
|
|
79
|
+
# Handle case where basis function creation failed
|
|
80
|
+
result.append(np.zeros(len(t_seq_clipped)))
|
|
81
|
+
else:
|
|
82
|
+
try:
|
|
83
|
+
values = np.array([b(t) for t in t_seq_clipped])
|
|
84
|
+
# Replace any NaN or inf with zeros
|
|
85
|
+
values = np.nan_to_num(values, nan=0.0, posinf=0.0, neginf=0.0)
|
|
86
|
+
result.append(values)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print(f"Error evaluating basis: {e}")
|
|
89
|
+
result.append(np.zeros(len(t_seq_clipped)))
|
|
90
|
+
|
|
91
|
+
return np.array(result)
|
|
92
|
+
|
|
93
|
+
def _get_coefs_from_images(self, images):
|
|
94
|
+
"""
|
|
95
|
+
Computes B-spline coefficients that best fit the given image geometries.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
images (list of np.ndarray): A list of geometries (images) along the path.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
np.ndarray: The calculated B-spline coefficients.
|
|
102
|
+
"""
|
|
103
|
+
nimages = len(images)
|
|
104
|
+
natoms = images[0].shape[0]
|
|
105
|
+
pos_ref = np.array(images)
|
|
106
|
+
|
|
107
|
+
# Ensure there are no NaN values in the input images
|
|
108
|
+
if not np.all(np.isfinite(pos_ref)):
|
|
109
|
+
print("WARNING: Non-finite values detected in input geometries")
|
|
110
|
+
pos_ref = np.nan_to_num(pos_ref, nan=0.0, posinf=0.0, neginf=0.0)
|
|
111
|
+
|
|
112
|
+
# Estimate path length to create a parameterization t_ref
|
|
113
|
+
diff = pos_ref[1:] - pos_ref[:-1]
|
|
114
|
+
lengths = np.sqrt(np.sum(diff**2, axis=(1, 2)))
|
|
115
|
+
|
|
116
|
+
# Handle potential zeros in lengths
|
|
117
|
+
epsilon = 1e-10
|
|
118
|
+
lengths = np.maximum(lengths, epsilon)
|
|
119
|
+
|
|
120
|
+
t_ref = np.concatenate(([0.0], np.cumsum(lengths)))
|
|
121
|
+
|
|
122
|
+
# STABILITY: Handle case where path length is zero or very small
|
|
123
|
+
if t_ref[-1] > epsilon:
|
|
124
|
+
t_ref /= t_ref[-1]
|
|
125
|
+
else:
|
|
126
|
+
# All images are at nearly the same position
|
|
127
|
+
print("WARNING: Path length is very small, using linear parameterization")
|
|
128
|
+
t_ref = np.linspace(0.0, 1.0, nimages)
|
|
129
|
+
|
|
130
|
+
# Use linear interpolation as a starting point
|
|
131
|
+
try:
|
|
132
|
+
f_interp = interp1d(t_ref, pos_ref, axis=0, fill_value="extrapolate", bounds_error=False)
|
|
133
|
+
t_solve = np.linspace(0.0, 1.0, 4 * self.nsegs + 1)
|
|
134
|
+
pos_solve = f_interp(t_solve)
|
|
135
|
+
except Exception as e:
|
|
136
|
+
print(f"Interpolation error: {e}, falling back to simple interpolation")
|
|
137
|
+
t_solve = np.linspace(0.0, 1.0, 4 * self.nsegs + 1)
|
|
138
|
+
# Fall back to simple linear interpolation
|
|
139
|
+
pos_solve = np.zeros((len(t_solve), natoms, 3))
|
|
140
|
+
for i, t in enumerate(t_solve):
|
|
141
|
+
idx = min(int(t * (nimages-1)), nimages-2)
|
|
142
|
+
frac = (t - idx/(nimages-1)) * (nimages-1)
|
|
143
|
+
pos_solve[i] = pos_ref[idx] * (1 - frac) + pos_ref[idx+1] * frac
|
|
144
|
+
|
|
145
|
+
P_solve = self._get_basis_values(t_solve, nu=0)
|
|
146
|
+
|
|
147
|
+
# Ensure P_solve has no numerical issues
|
|
148
|
+
if not np.all(np.isfinite(P_solve)):
|
|
149
|
+
print("WARNING: Non-finite values in basis evaluation")
|
|
150
|
+
P_solve = np.nan_to_num(P_solve, nan=0.0, posinf=0.0, neginf=0.0)
|
|
151
|
+
|
|
152
|
+
# A * coefs = b, where we solve for coefs
|
|
153
|
+
A = P_solve.T
|
|
154
|
+
b = pos_solve.reshape(len(t_solve), -1)
|
|
155
|
+
|
|
156
|
+
# Check matrix condition before solving
|
|
157
|
+
try:
|
|
158
|
+
# Using a more stable solver with regularization
|
|
159
|
+
rcond = 1e-6 # Increased regularization factor for better stability
|
|
160
|
+
coefs_flat, _, _, _ = np.linalg.lstsq(A, b, rcond=rcond)
|
|
161
|
+
except np.linalg.LinAlgError:
|
|
162
|
+
print("WARNING: Linear algebra error in least squares, using fallback")
|
|
163
|
+
# Fallback: Use simple pseudoinverse with stronger regularization
|
|
164
|
+
ATA = np.dot(A.T, A) + np.eye(A.shape[1]) * 1e-4 # Increased regularization
|
|
165
|
+
ATb = np.dot(A.T, b)
|
|
166
|
+
try:
|
|
167
|
+
coefs_flat = np.linalg.solve(ATA, ATb)
|
|
168
|
+
except:
|
|
169
|
+
print("SEVERE WARNING: Could not solve for coefficients, using direct images")
|
|
170
|
+
# Last resort: Use the original points
|
|
171
|
+
coefs_flat = np.zeros((self.nbasis, natoms * 3))
|
|
172
|
+
coefs_flat[0] = pos_ref[0].reshape(-1)
|
|
173
|
+
coefs_flat[-1] = pos_ref[-1].reshape(-1)
|
|
174
|
+
for i in range(1, self.nbasis-1):
|
|
175
|
+
idx = min(int(i * (nimages-1)/(self.nbasis-1)), nimages-1)
|
|
176
|
+
coefs_flat[i] = pos_ref[idx].reshape(-1)
|
|
177
|
+
|
|
178
|
+
coefs = coefs_flat.reshape(self.nbasis, natoms, 3)
|
|
179
|
+
|
|
180
|
+
# Check for NaN or inf values in coefficients
|
|
181
|
+
if not np.all(np.isfinite(coefs)):
|
|
182
|
+
print("WARNING: Non-finite coefficients detected, replacing with zeros")
|
|
183
|
+
coefs = np.nan_to_num(coefs, nan=0.0, posinf=0.0, neginf=0.0)
|
|
184
|
+
|
|
185
|
+
# Enforce boundary conditions
|
|
186
|
+
coefs[0] = pos_ref[0]
|
|
187
|
+
coefs[-1] = pos_ref[-1]
|
|
188
|
+
|
|
189
|
+
# Ensure coefficients are reasonably smooth - this helps prevent kabsch issues
|
|
190
|
+
# Add a small amount of smoothing/averaging between neighboring coefficients
|
|
191
|
+
smoothed_coefs = coefs.copy()
|
|
192
|
+
for i in range(1, self.nbasis-1):
|
|
193
|
+
smoothed_coefs[i] = 0.9 * coefs[i] + 0.05 * coefs[i-1] + 0.05 * coefs[i+1]
|
|
194
|
+
|
|
195
|
+
# Keep endpoints fixed
|
|
196
|
+
smoothed_coefs[0] = coefs[0]
|
|
197
|
+
smoothed_coefs[-1] = coefs[-1]
|
|
198
|
+
coefs = smoothed_coefs
|
|
199
|
+
|
|
200
|
+
return coefs
|
|
201
|
+
|
|
202
|
+
def _get_path_properties(self, coefs, t_eval):
|
|
203
|
+
"""Calculates positions and velocities along the spline path."""
|
|
204
|
+
P_eval_pos = self._get_basis_values(t_eval, nu=0)
|
|
205
|
+
P_eval_vel = self._get_basis_values(t_eval, nu=1)
|
|
206
|
+
|
|
207
|
+
positions = np.tensordot(P_eval_pos.T, coefs, axes=1)
|
|
208
|
+
velocities = np.tensordot(P_eval_vel.T, coefs, axes=1)
|
|
209
|
+
|
|
210
|
+
# Check for NaN or inf values
|
|
211
|
+
if not np.all(np.isfinite(positions)):
|
|
212
|
+
print("WARNING: Non-finite positions detected")
|
|
213
|
+
positions = np.nan_to_num(positions, nan=0.0, posinf=0.0, neginf=0.0)
|
|
214
|
+
|
|
215
|
+
if not np.all(np.isfinite(velocities)):
|
|
216
|
+
print("WARNING: Non-finite velocities detected")
|
|
217
|
+
velocities = np.nan_to_num(velocities, nan=0.0, posinf=0.0, neginf=0.0)
|
|
218
|
+
|
|
219
|
+
return positions, velocities
|
|
220
|
+
|
|
221
|
+
def _get_func_en(self, energies, e0):
|
|
222
|
+
"""Calculates the energy-dependent part of the action and its derivative."""
|
|
223
|
+
en_shifted = energies - e0
|
|
224
|
+
|
|
225
|
+
# Cap extremely large values to prevent overflow
|
|
226
|
+
max_en = 700.0 / self.beta # Prevent exp overflow
|
|
227
|
+
en_shifted = np.minimum(en_shifted, max_en)
|
|
228
|
+
|
|
229
|
+
exp_beta_en = np.exp(self.beta * en_shifted)
|
|
230
|
+
|
|
231
|
+
# Check for NaN or inf
|
|
232
|
+
if not np.all(np.isfinite(exp_beta_en)):
|
|
233
|
+
print("WARNING: Non-finite energy function values detected")
|
|
234
|
+
exp_beta_en = np.nan_to_num(exp_beta_en, nan=1.0, posinf=1e10, neginf=0.0)
|
|
235
|
+
|
|
236
|
+
return exp_beta_en, self.beta * exp_beta_en
|
|
237
|
+
|
|
238
|
+
def _get_action_and_gradient(self, coefs, t_eval, w_eval, energies, forces, e0):
|
|
239
|
+
"""
|
|
240
|
+
Calculates the MaxFlux action and its gradient with respect to coefficients.
|
|
241
|
+
"""
|
|
242
|
+
positions, velocities = self._get_path_properties(coefs, t_eval)
|
|
243
|
+
|
|
244
|
+
# Calculate norm of velocities with stability checks
|
|
245
|
+
vel_squared = np.sum(velocities**2, axis=(1, 2))
|
|
246
|
+
vel_squared = np.maximum(vel_squared, 1e-16) # Prevent sqrt of negative/zero
|
|
247
|
+
norm_vels = np.sqrt(vel_squared)
|
|
248
|
+
|
|
249
|
+
# STABILITY: Ensure minimum reasonable velocity norm
|
|
250
|
+
epsilon = 1e-8
|
|
251
|
+
norm_vels_safe = np.maximum(norm_vels, epsilon)
|
|
252
|
+
|
|
253
|
+
# Calculate the energy function and its derivative
|
|
254
|
+
fe, dfe = self._get_func_en(energies, e0)
|
|
255
|
+
|
|
256
|
+
# Calculate total action (numerical integration)
|
|
257
|
+
action_terms = w_eval * norm_vels * fe
|
|
258
|
+
action = np.sum(action_terms)
|
|
259
|
+
|
|
260
|
+
# If action is too small, return zero gradient
|
|
261
|
+
if abs(action) < 1e-12:
|
|
262
|
+
print("WARNING: Action is nearly zero, returning zero gradient")
|
|
263
|
+
grad_action = np.zeros_like(coefs)
|
|
264
|
+
return max(action, 1e-12), grad_action # Return small positive action to avoid division by zero
|
|
265
|
+
|
|
266
|
+
# --- Gradient Calculation ---
|
|
267
|
+
P_vel_eval = self._get_basis_values(t_eval, nu=1) # Shape (nbasis, nnode)
|
|
268
|
+
P_pos_eval = self._get_basis_values(t_eval, nu=0) # Shape (nbasis, nnode)
|
|
269
|
+
|
|
270
|
+
# Compute normalized velocities safely
|
|
271
|
+
normalized_velocities = np.zeros_like(velocities)
|
|
272
|
+
for i in range(len(norm_vels_safe)):
|
|
273
|
+
if norm_vels_safe[i] > epsilon:
|
|
274
|
+
normalized_velocities[i] = velocities[i] / norm_vels_safe[i]
|
|
275
|
+
|
|
276
|
+
# Part 1: Gradient from the velocity norm term
|
|
277
|
+
try:
|
|
278
|
+
grad_action_v = np.einsum('bt,tas,t->bas', P_vel_eval, normalized_velocities, w_eval * fe)
|
|
279
|
+
except Exception as e:
|
|
280
|
+
print(f"Error in einsum (velocity part): {e}")
|
|
281
|
+
grad_action_v = np.zeros_like(coefs)
|
|
282
|
+
|
|
283
|
+
# Part 2: Gradient from the energy term (forces)
|
|
284
|
+
term_for_force = w_eval * norm_vels * dfe
|
|
285
|
+
|
|
286
|
+
# Ensure forces have no NaN/inf values
|
|
287
|
+
safe_forces = np.array(forces)
|
|
288
|
+
if not np.all(np.isfinite(safe_forces)):
|
|
289
|
+
print("WARNING: Non-finite forces detected")
|
|
290
|
+
safe_forces = np.nan_to_num(safe_forces, nan=0.0, posinf=0.0, neginf=0.0)
|
|
291
|
+
|
|
292
|
+
# Clip very large force values to prevent numerical instability
|
|
293
|
+
max_force = 1e3
|
|
294
|
+
safe_forces = np.clip(safe_forces, -max_force, max_force)
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
grad_action_f = -np.einsum('bt,tas,t->bas', P_pos_eval, safe_forces, term_for_force)
|
|
298
|
+
except Exception as e:
|
|
299
|
+
print(f"Error in einsum (force part): {e}")
|
|
300
|
+
grad_action_f = np.zeros_like(coefs)
|
|
301
|
+
|
|
302
|
+
# Combine gradients
|
|
303
|
+
grad_action = grad_action_v + grad_action_f
|
|
304
|
+
|
|
305
|
+
# Final check for NaN/inf
|
|
306
|
+
if not np.all(np.isfinite(grad_action)):
|
|
307
|
+
print("WARNING: Non-finite gradient detected")
|
|
308
|
+
grad_action = np.nan_to_num(grad_action, nan=0.0, posinf=0.0, neginf=0.0)
|
|
309
|
+
|
|
310
|
+
# Clip gradient to reasonable values to prevent instability
|
|
311
|
+
max_grad = 1e3
|
|
312
|
+
grad_action = np.clip(grad_action, -max_grad, max_grad)
|
|
313
|
+
|
|
314
|
+
return action, grad_action
|
|
315
|
+
|
|
316
|
+
def calc_force(self, geometry_num_list, energy_list, gradient_list, optimize_num, element_list):
|
|
317
|
+
"""
|
|
318
|
+
Calculates the "forces" to drive the path optimization using the DMF method.
|
|
319
|
+
The returned "force" is the negative gradient of the objective function w.r.t. image positions.
|
|
320
|
+
"""
|
|
321
|
+
print("DMF"*20)
|
|
322
|
+
|
|
323
|
+
nnode = len(energy_list)
|
|
324
|
+
|
|
325
|
+
# Check input data
|
|
326
|
+
try:
|
|
327
|
+
# The "images" are the geometries in Cartesian coordinates
|
|
328
|
+
images = [np.array(g, dtype="float64") for g in geometry_num_list]
|
|
329
|
+
energies = np.array(energy_list, dtype="float64")
|
|
330
|
+
# Gradients are negative forces
|
|
331
|
+
forces = [-np.array(g, dtype="float64") for g in gradient_list]
|
|
332
|
+
|
|
333
|
+
# Check for NaN/inf in input data
|
|
334
|
+
for i, (img, en, f) in enumerate(zip(images, energies, forces)):
|
|
335
|
+
if not np.all(np.isfinite(img)):
|
|
336
|
+
print(f"WARNING: Non-finite values in geometry {i}")
|
|
337
|
+
images[i] = np.nan_to_num(img, nan=0.0, posinf=0.0, neginf=0.0)
|
|
338
|
+
if not np.isfinite(en):
|
|
339
|
+
print(f"WARNING: Non-finite value in energy {i}")
|
|
340
|
+
energies[i] = 0.0
|
|
341
|
+
if not np.all(np.isfinite(f)):
|
|
342
|
+
print(f"WARNING: Non-finite values in force {i}")
|
|
343
|
+
forces[i] = np.nan_to_num(f, nan=0.0, posinf=0.0, neginf=0.0)
|
|
344
|
+
except Exception as e:
|
|
345
|
+
print(f"Error processing input data: {e}")
|
|
346
|
+
# Return zeros if we can't process the input
|
|
347
|
+
return np.zeros((len(geometry_num_list), len(geometry_num_list[0]), 3))
|
|
348
|
+
|
|
349
|
+
# 1. Define the path parameterization and integration weights
|
|
350
|
+
t_eval = np.linspace(0.0, 1.0, nnode)
|
|
351
|
+
w_eval = np.zeros_like(t_eval)
|
|
352
|
+
w_eval[0] = 0.5 * (t_eval[1] - t_eval[0])
|
|
353
|
+
w_eval[-1] = 0.5 * (t_eval[-1] - t_eval[-2])
|
|
354
|
+
w_eval[1:-1] = 0.5 * (t_eval[2:] - t_eval[:-2])
|
|
355
|
+
|
|
356
|
+
# 2. Get B-spline coefficients for the current path
|
|
357
|
+
try:
|
|
358
|
+
coefs = self._get_coefs_from_images(images)
|
|
359
|
+
except Exception as e:
|
|
360
|
+
print(f"Error in coefficient calculation: {e}")
|
|
361
|
+
# Return zero forces if coefficient calculation fails
|
|
362
|
+
return np.zeros((len(geometry_num_list), len(geometry_num_list[0]), 3))
|
|
363
|
+
|
|
364
|
+
# 3. Calculate the objective function (action) and its gradient
|
|
365
|
+
e0 = np.min(energies) # Energy reference
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
action, grad_action_coefs = self._get_action_and_gradient(coefs, t_eval, w_eval, energies, forces, e0)
|
|
369
|
+
# Print the action value as requested
|
|
370
|
+
print(f"DMF Objective Function Value (Action): {action}")
|
|
371
|
+
print(f"Energy Reference e0: {e0}")
|
|
372
|
+
print(f"Beta Value: {self.beta}")
|
|
373
|
+
print(f"Normalized Action: {action * self.beta}")
|
|
374
|
+
except Exception as e:
|
|
375
|
+
print(f"Error in action calculation: {e}")
|
|
376
|
+
# Return zero forces if action calculation fails
|
|
377
|
+
return np.zeros((len(geometry_num_list), len(geometry_num_list[0]), 3))
|
|
378
|
+
|
|
379
|
+
# STABILITY: Check for safe division
|
|
380
|
+
denominator = action * self.beta
|
|
381
|
+
if abs(denominator) < 1e-12:
|
|
382
|
+
print("WARNING: Action denominator is too small")
|
|
383
|
+
objective_grad_coefs = np.zeros_like(grad_action_coefs)
|
|
384
|
+
else:
|
|
385
|
+
objective_grad_coefs = grad_action_coefs / denominator
|
|
386
|
+
|
|
387
|
+
# Clip objective gradient to reasonable values
|
|
388
|
+
max_obj_grad = 1e2
|
|
389
|
+
objective_grad_coefs = np.clip(objective_grad_coefs, -max_obj_grad, max_obj_grad)
|
|
390
|
+
|
|
391
|
+
# 4. Project the gradient on coefficients back to forces on images
|
|
392
|
+
P_pos_eval = self._get_basis_values(t_eval, nu=0) # Shape: [nbasis, nnode]
|
|
393
|
+
|
|
394
|
+
try:
|
|
395
|
+
forces_from_obj = -np.einsum('bt,bas->tas', P_pos_eval, objective_grad_coefs)
|
|
396
|
+
except Exception as e:
|
|
397
|
+
print(f"Error in final einsum: {e}")
|
|
398
|
+
forces_from_obj = np.zeros((len(images), images[0].shape[0], 3))
|
|
399
|
+
|
|
400
|
+
# 5. Handle endpoints: they are fixed, so their forces should be zero.
|
|
401
|
+
forces_from_obj[0, :, :] = 0.0
|
|
402
|
+
forces_from_obj[-1, :, :] = 0.0
|
|
403
|
+
|
|
404
|
+
# STABILITY: Final check to ensure no NaN/inf values are returned
|
|
405
|
+
if not np.all(np.isfinite(forces_from_obj)):
|
|
406
|
+
print("WARNING: Non-finite values detected in DMF forces. Returning zero forces.")
|
|
407
|
+
forces_from_obj = np.nan_to_num(forces_from_obj, nan=0.0, posinf=0.0, neginf=0.0)
|
|
408
|
+
|
|
409
|
+
# Ensure reasonable force magnitudes - critical for trust radius calculations
|
|
410
|
+
# This directly addresses the divide-by-zero errors in trust_radius_neb.py
|
|
411
|
+
for i in range(len(forces_from_obj)):
|
|
412
|
+
force_mag = np.linalg.norm(forces_from_obj[i].reshape(-1))
|
|
413
|
+
if force_mag < 1e-8: # Force too small
|
|
414
|
+
if i > 0 and i < len(forces_from_obj) - 1: # Skip endpoints
|
|
415
|
+
print(f"WARNING: Force magnitude for image {i} is too small ({force_mag}), adding small noise")
|
|
416
|
+
np.random.seed(i+42) # For reproducibility but different for each image
|
|
417
|
+
# Add small noise proportional to the system size
|
|
418
|
+
system_scale = np.mean(np.abs(images[i]))
|
|
419
|
+
if system_scale < 1e-10:
|
|
420
|
+
system_scale = 1.0
|
|
421
|
+
noise_scale = 1e-6 * system_scale
|
|
422
|
+
small_noise = np.random.normal(0, noise_scale, forces_from_obj[i].shape)
|
|
423
|
+
forces_from_obj[i] += small_noise
|
|
424
|
+
elif force_mag > 1e2: # Force too large
|
|
425
|
+
print(f"WARNING: Force magnitude for image {i} is too large ({force_mag}), scaling down")
|
|
426
|
+
scale_factor = 1e2 / force_mag
|
|
427
|
+
forces_from_obj[i] *= scale_factor
|
|
428
|
+
|
|
429
|
+
# Reset endpoints to zero force
|
|
430
|
+
forces_from_obj[0, :, :] = 0.0
|
|
431
|
+
forces_from_obj[-1, :, :] = 0.0
|
|
432
|
+
|
|
433
|
+
# Final sanitization for trust radius calculation safety
|
|
434
|
+
# This ensures total_delta values in trust_radius_neb.py will have reasonable norms
|
|
435
|
+
total_force_list = np.array(forces_from_obj, dtype="float64")
|
|
436
|
+
|
|
437
|
+
# Make sure no image has exactly zero force (except endpoints)
|
|
438
|
+
# This prevents division by zero in trust_radius_neb.py
|
|
439
|
+
for i in range(1, len(total_force_list) - 1):
|
|
440
|
+
norm_i = np.linalg.norm(total_force_list[i].reshape(-1))
|
|
441
|
+
if norm_i < 1e-10:
|
|
442
|
+
total_force_list[i, 0, 0] += 1e-8 # Tiny force in x direction of first atom
|
|
443
|
+
|
|
444
|
+
# Print force statistics to help with debugging
|
|
445
|
+
force_norms = [np.linalg.norm(total_force_list[i].reshape(-1)) for i in range(len(total_force_list))]
|
|
446
|
+
print(f"Force norms: min={min(force_norms):.6e}, max={max(force_norms):.6e}, avg={np.mean(force_norms):.6e}")
|
|
447
|
+
|
|
448
|
+
return total_force_list
|
|
@@ -0,0 +1,130 @@
|
|
|
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 CaluculationDNEB:
|
|
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("DNEBDNEBDNEBDNEBDNEBDNEBDNEBDNEBDNEBDNEBDNEBDNEBDNEBDNEB")
|
|
28
|
+
local_max_energy_list_index, local_min_energy_list_index = extremum_list_index(energy_list)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
total_force_list = [((-1)*np.array(gradient_list[0], dtype = "float64")).tolist()]
|
|
32
|
+
for i in range(1,len(energy_list)-1):
|
|
33
|
+
tau_plus, tau_minus, tau = [], [], []
|
|
34
|
+
|
|
35
|
+
delta_max_energy = np.array(max([(energy_list[i+1]-energy_list[i]),(energy_list[i-1]-energy_list[i])]), dtype = "float64")
|
|
36
|
+
delta_min_energy = np.array(min([(energy_list[i+1]-energy_list[i]),(energy_list[i-1]-energy_list[i])]), dtype = "float64")
|
|
37
|
+
|
|
38
|
+
if (energy_list[i-1] < energy_list[i]) and (energy_list[i] < energy_list[i+1]):
|
|
39
|
+
for t in range(len(geometry_num_list[i])):
|
|
40
|
+
tau_vector = geometry_num_list[i+1][t]-geometry_num_list[i][t]
|
|
41
|
+
tau_norm = np.linalg.norm(geometry_num_list[i+1][t]-geometry_num_list[i][t], ord=2)
|
|
42
|
+
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())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
elif (energy_list[i-1] > energy_list[i]) and (energy_list[i] > energy_list[i+1]):
|
|
46
|
+
for t in range(len(geometry_num_list[i])):
|
|
47
|
+
tau_vector = geometry_num_list[i][t]-geometry_num_list[i-1][t]
|
|
48
|
+
tau_norm = np.linalg.norm(geometry_num_list[i][t]-geometry_num_list[i-1][t], ord=2)
|
|
49
|
+
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())
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
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])):
|
|
54
|
+
for t in range(len(geometry_num_list[i])):
|
|
55
|
+
tau_minus_vector = geometry_num_list[i][t]-geometry_num_list[i-1][t]
|
|
56
|
+
tau_minus_norm = np.linalg.norm(geometry_num_list[i][t]-geometry_num_list[i-1][t], ord=2)
|
|
57
|
+
tau_minus.append(np.divide(tau_minus_vector, tau_minus_norm
|
|
58
|
+
,out=np.zeros_like(geometry_num_list[i][t]),
|
|
59
|
+
where=np.linalg.norm(geometry_num_list[i][t]-geometry_num_list[i-1][t], ord=2)!=0).tolist())
|
|
60
|
+
|
|
61
|
+
for t in range(len(geometry_num_list[i])):
|
|
62
|
+
tau_plus_vector = geometry_num_list[i+1][t]-geometry_num_list[i][t]
|
|
63
|
+
tau_plus_norm = np.linalg.norm(geometry_num_list[i+1][t]-geometry_num_list[i][t], ord=2)
|
|
64
|
+
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())
|
|
65
|
+
|
|
66
|
+
if energy_list[i-1] > energy_list[i+1]:
|
|
67
|
+
for t in range(len(geometry_num_list[i])):
|
|
68
|
+
tau_vector = (tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy)
|
|
69
|
+
tau_norm = np.linalg.norm(tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy, ord=2)
|
|
70
|
+
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())
|
|
71
|
+
else:
|
|
72
|
+
for t in range(len(geometry_num_list[i])):
|
|
73
|
+
tau_vector = (tau_plus[t]*delta_max_energy+tau_minus[t]*delta_min_energy)
|
|
74
|
+
tau_norm = np.linalg.norm(tau_plus[t]*delta_min_energy+tau_minus[t]*delta_max_energy, ord=2)
|
|
75
|
+
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())
|
|
76
|
+
|
|
77
|
+
tau_plus, tau_minus, tau = np.array(tau_plus, dtype = "float64"), np.array(tau_minus, dtype = "float64"), np.array(tau, dtype = "float64")
|
|
78
|
+
#print("tau_minus:\n",tau_minus)
|
|
79
|
+
#print("tau_plus:\n",tau_plus)
|
|
80
|
+
#print("tau:\n",str(tau))
|
|
81
|
+
force_perpendicularity, force_parallelism, force_parallelism_perpendicularity , swiching_double_neb_force = [], [], [], []
|
|
82
|
+
|
|
83
|
+
if energy_list[i] == energy_list[local_max_energy_list_index[0]] and self.APPLY_CI_NEB < optimize_num: #CI-NEB
|
|
84
|
+
for f in range(len(geometry_num_list[i])):
|
|
85
|
+
force_perpendicularity.append(np.array((-1)*self.force_const_for_cineb*(gradient_list[i][f]-2.0*(np.dot(gradient_list[i][f], tau[f]))*tau[f]), dtype = "float64"))
|
|
86
|
+
#print(str(force_perpendicularity))
|
|
87
|
+
total_force = np.array(force_perpendicularity, dtype="float64")
|
|
88
|
+
del local_max_energy_list_index[0]
|
|
89
|
+
#print(str(total_force))
|
|
90
|
+
#elif energy_list[i] == energy_list[local_min_energy_list_index[0]]: #for discovering intermidiate
|
|
91
|
+
# for f in range(len(geometry_num_list[i])):
|
|
92
|
+
# force_perpendicularity.append(np.array(((-1)*(gradient_list[i][f])), dtype = "float64"))
|
|
93
|
+
# #print(str(force_perpendicularity))
|
|
94
|
+
# total_force = np.array(force_perpendicularity, dtype="float64")
|
|
95
|
+
# del local_min_energy_list_index[0]
|
|
96
|
+
else:
|
|
97
|
+
for f in range(len(geometry_num_list[i])):
|
|
98
|
+
grad = 0.0
|
|
99
|
+
|
|
100
|
+
for gg in range(len(gradient_list[i])):
|
|
101
|
+
grad += np.linalg.norm(gradient_list[i][gg], ord=2)
|
|
102
|
+
|
|
103
|
+
grad = grad/len(gradient_list[i])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
#print("spring_constant:",self.spring_constant_k)
|
|
107
|
+
|
|
108
|
+
force_parallelism.append(np.array((self.spring_constant_k*(np.linalg.norm(geometry_num_list[i+1][f]-geometry_num_list[i][f], ord=2))+(-1.0)*self.spring_constant_k*(np.linalg.norm(geometry_num_list[i][f]-geometry_num_list[i-1][f], ord=2)))*tau[f], dtype = "float64"))
|
|
109
|
+
|
|
110
|
+
force_perpendicularity.append(np.array(gradient_list[i][f]-(np.dot(gradient_list[i][f], tau[f]))*tau[f], dtype = "float64"))
|
|
111
|
+
#doubly nudged elastic band method :https://doi.org/10.1063/1.1636455
|
|
112
|
+
|
|
113
|
+
force_parallelism_perpendicularity.append(np.array(np.array((self.spring_constant_k*(np.linalg.norm(geometry_num_list[i+1][f]-geometry_num_list[i][f], ord=2))+(-1.0)*self.spring_constant_k*(np.linalg.norm(geometry_num_list[i][f]-geometry_num_list[i-1][f], ord=2))), dtype = "float64") - (np.dot(np.array((self.spring_constant_k*(np.linalg.norm(geometry_num_list[i+1][f]-geometry_num_list[i][f], ord=2))+(-1.0)*self.spring_constant_k*(np.linalg.norm(geometry_num_list[i][f]-geometry_num_list[i-1][f], ord=2))), dtype = "float64"), tau[f]))*tau[f], dtype = "float64"))
|
|
114
|
+
|
|
115
|
+
swiching_double_neb_force.append((2.0/np.pi)*np.arctan(np.divide(np.linalg.norm(force_parallelism_perpendicularity[f], ord=2)**2, np.linalg.norm(force_parallelism_perpendicularity[f], ord=2)**2 ,out=np.zeros_like(force_parallelism_perpendicularity[f]) ,where=np.linalg.norm(force_parallelism_perpendicularity[f], ord=2)!=0))*(force_parallelism_perpendicularity[f] - np.dot(force_parallelism_perpendicularity[f],force_perpendicularity[f])*force_perpendicularity[f]))
|
|
116
|
+
|
|
117
|
+
force_perpendicularity, force_parallelism, force_parallelism_perpendicularity, swiching_double_neb_force = np.array(force_perpendicularity, dtype = "float64"), np.array(force_parallelism, dtype = "float64"),np.array(force_parallelism_perpendicularity, dtype = "float64"), np.array(swiching_double_neb_force, dtype = "float64")
|
|
118
|
+
total_force = np.array((-1)*force_perpendicularity - force_parallelism - swiching_double_neb_force, dtype = "float64")
|
|
119
|
+
|
|
120
|
+
if np.nanmean(np.nanmean(total_force)) > 10:
|
|
121
|
+
total_force = total_force / np.nanmean(np.nanmean(total_force))
|
|
122
|
+
|
|
123
|
+
total_force_list.append(total_force.tolist())
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
total_force_list.append(((-1)*np.array(gradient_list[-1], dtype = "float64")).tolist())
|
|
129
|
+
|
|
130
|
+
return np.array(total_force_list, dtype = "float64")
|