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.
Files changed (246) hide show
  1. multioptpy/Calculator/__init__.py +0 -0
  2. multioptpy/Calculator/ase_calculation_tools.py +424 -0
  3. multioptpy/Calculator/ase_tools/__init__.py +0 -0
  4. multioptpy/Calculator/ase_tools/fairchem.py +28 -0
  5. multioptpy/Calculator/ase_tools/gamess.py +19 -0
  6. multioptpy/Calculator/ase_tools/gaussian.py +165 -0
  7. multioptpy/Calculator/ase_tools/mace.py +28 -0
  8. multioptpy/Calculator/ase_tools/mopac.py +19 -0
  9. multioptpy/Calculator/ase_tools/nwchem.py +31 -0
  10. multioptpy/Calculator/ase_tools/orca.py +22 -0
  11. multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
  12. multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
  13. multioptpy/Calculator/emt_calculation_tools.py +458 -0
  14. multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
  15. multioptpy/Calculator/lj_calculation_tools.py +314 -0
  16. multioptpy/Calculator/psi4_calculation_tools.py +334 -0
  17. multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
  18. multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
  19. multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
  20. multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
  21. multioptpy/Calculator/tblite_calculation_tools.py +352 -0
  22. multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
  23. multioptpy/Constraint/__init__.py +0 -0
  24. multioptpy/Constraint/constraint_condition.py +834 -0
  25. multioptpy/Coordinate/__init__.py +0 -0
  26. multioptpy/Coordinate/polar_coordinate.py +199 -0
  27. multioptpy/Coordinate/redundant_coordinate.py +638 -0
  28. multioptpy/IRC/__init__.py +0 -0
  29. multioptpy/IRC/converge_criteria.py +28 -0
  30. multioptpy/IRC/dvv.py +544 -0
  31. multioptpy/IRC/euler.py +439 -0
  32. multioptpy/IRC/hpc.py +564 -0
  33. multioptpy/IRC/lqa.py +540 -0
  34. multioptpy/IRC/modekill.py +662 -0
  35. multioptpy/IRC/rk4.py +579 -0
  36. multioptpy/Interpolation/__init__.py +0 -0
  37. multioptpy/Interpolation/adaptive_interpolation.py +283 -0
  38. multioptpy/Interpolation/binomial_interpolation.py +179 -0
  39. multioptpy/Interpolation/geodesic_interpolation.py +785 -0
  40. multioptpy/Interpolation/interpolation.py +156 -0
  41. multioptpy/Interpolation/linear_interpolation.py +473 -0
  42. multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
  43. multioptpy/Interpolation/spline_interpolation.py +353 -0
  44. multioptpy/MD/__init__.py +0 -0
  45. multioptpy/MD/thermostat.py +185 -0
  46. multioptpy/MEP/__init__.py +0 -0
  47. multioptpy/MEP/pathopt_bneb_force.py +443 -0
  48. multioptpy/MEP/pathopt_dmf_force.py +448 -0
  49. multioptpy/MEP/pathopt_dneb_force.py +130 -0
  50. multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
  51. multioptpy/MEP/pathopt_gpneb_force.py +512 -0
  52. multioptpy/MEP/pathopt_lup_force.py +113 -0
  53. multioptpy/MEP/pathopt_neb_force.py +225 -0
  54. multioptpy/MEP/pathopt_nesb_force.py +205 -0
  55. multioptpy/MEP/pathopt_om_force.py +153 -0
  56. multioptpy/MEP/pathopt_qsm_force.py +174 -0
  57. multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
  58. multioptpy/ModelFunction/__init__.py +7 -0
  59. multioptpy/ModelFunction/avoiding_model_function.py +29 -0
  60. multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
  61. multioptpy/ModelFunction/conical_model_function.py +26 -0
  62. multioptpy/ModelFunction/opt_meci.py +50 -0
  63. multioptpy/ModelFunction/opt_mesx.py +47 -0
  64. multioptpy/ModelFunction/opt_mesx_2.py +49 -0
  65. multioptpy/ModelFunction/seam_model_function.py +27 -0
  66. multioptpy/ModelHessian/__init__.py +0 -0
  67. multioptpy/ModelHessian/approx_hessian.py +147 -0
  68. multioptpy/ModelHessian/calc_params.py +227 -0
  69. multioptpy/ModelHessian/fischer.py +236 -0
  70. multioptpy/ModelHessian/fischerd3.py +360 -0
  71. multioptpy/ModelHessian/fischerd4.py +398 -0
  72. multioptpy/ModelHessian/gfn0xtb.py +633 -0
  73. multioptpy/ModelHessian/gfnff.py +709 -0
  74. multioptpy/ModelHessian/lindh.py +165 -0
  75. multioptpy/ModelHessian/lindh2007d2.py +707 -0
  76. multioptpy/ModelHessian/lindh2007d3.py +822 -0
  77. multioptpy/ModelHessian/lindh2007d4.py +1030 -0
  78. multioptpy/ModelHessian/morse.py +106 -0
  79. multioptpy/ModelHessian/schlegel.py +144 -0
  80. multioptpy/ModelHessian/schlegeld3.py +322 -0
  81. multioptpy/ModelHessian/schlegeld4.py +559 -0
  82. multioptpy/ModelHessian/shortrange.py +346 -0
  83. multioptpy/ModelHessian/swartd2.py +496 -0
  84. multioptpy/ModelHessian/swartd3.py +706 -0
  85. multioptpy/ModelHessian/swartd4.py +918 -0
  86. multioptpy/ModelHessian/tshess.py +40 -0
  87. multioptpy/Optimizer/QHAdam.py +61 -0
  88. multioptpy/Optimizer/__init__.py +0 -0
  89. multioptpy/Optimizer/abc_fire.py +83 -0
  90. multioptpy/Optimizer/adabelief.py +58 -0
  91. multioptpy/Optimizer/adabound.py +68 -0
  92. multioptpy/Optimizer/adadelta.py +65 -0
  93. multioptpy/Optimizer/adaderivative.py +56 -0
  94. multioptpy/Optimizer/adadiff.py +68 -0
  95. multioptpy/Optimizer/adafactor.py +70 -0
  96. multioptpy/Optimizer/adam.py +65 -0
  97. multioptpy/Optimizer/adamax.py +62 -0
  98. multioptpy/Optimizer/adamod.py +83 -0
  99. multioptpy/Optimizer/adamw.py +65 -0
  100. multioptpy/Optimizer/adiis.py +523 -0
  101. multioptpy/Optimizer/afire_neb.py +282 -0
  102. multioptpy/Optimizer/block_hessian_update.py +709 -0
  103. multioptpy/Optimizer/c2diis.py +491 -0
  104. multioptpy/Optimizer/component_wise_scaling.py +405 -0
  105. multioptpy/Optimizer/conjugate_gradient.py +82 -0
  106. multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
  107. multioptpy/Optimizer/coordinate_locking.py +405 -0
  108. multioptpy/Optimizer/dic_rsirfo.py +1015 -0
  109. multioptpy/Optimizer/ediis.py +417 -0
  110. multioptpy/Optimizer/eve.py +76 -0
  111. multioptpy/Optimizer/fastadabelief.py +61 -0
  112. multioptpy/Optimizer/fire.py +77 -0
  113. multioptpy/Optimizer/fire2.py +249 -0
  114. multioptpy/Optimizer/fire_neb.py +92 -0
  115. multioptpy/Optimizer/gan_step.py +486 -0
  116. multioptpy/Optimizer/gdiis.py +609 -0
  117. multioptpy/Optimizer/gediis.py +203 -0
  118. multioptpy/Optimizer/geodesic_step.py +433 -0
  119. multioptpy/Optimizer/gpmin.py +633 -0
  120. multioptpy/Optimizer/gpr_step.py +364 -0
  121. multioptpy/Optimizer/gradientdescent.py +78 -0
  122. multioptpy/Optimizer/gradientdescent_neb.py +52 -0
  123. multioptpy/Optimizer/hessian_update.py +433 -0
  124. multioptpy/Optimizer/hybrid_rfo.py +998 -0
  125. multioptpy/Optimizer/kdiis.py +625 -0
  126. multioptpy/Optimizer/lars.py +21 -0
  127. multioptpy/Optimizer/lbfgs.py +253 -0
  128. multioptpy/Optimizer/lbfgs_neb.py +355 -0
  129. multioptpy/Optimizer/linesearch.py +236 -0
  130. multioptpy/Optimizer/lookahead.py +40 -0
  131. multioptpy/Optimizer/nadam.py +64 -0
  132. multioptpy/Optimizer/newton.py +200 -0
  133. multioptpy/Optimizer/prodigy.py +70 -0
  134. multioptpy/Optimizer/purtubation.py +16 -0
  135. multioptpy/Optimizer/quickmin_neb.py +245 -0
  136. multioptpy/Optimizer/radam.py +75 -0
  137. multioptpy/Optimizer/rfo_neb.py +302 -0
  138. multioptpy/Optimizer/ric_rfo.py +842 -0
  139. multioptpy/Optimizer/rl_step.py +627 -0
  140. multioptpy/Optimizer/rmspropgrave.py +65 -0
  141. multioptpy/Optimizer/rsirfo.py +1647 -0
  142. multioptpy/Optimizer/rsprfo.py +1056 -0
  143. multioptpy/Optimizer/sadam.py +60 -0
  144. multioptpy/Optimizer/samsgrad.py +63 -0
  145. multioptpy/Optimizer/tr_lbfgs.py +678 -0
  146. multioptpy/Optimizer/trim.py +273 -0
  147. multioptpy/Optimizer/trust_radius.py +207 -0
  148. multioptpy/Optimizer/trust_radius_neb.py +121 -0
  149. multioptpy/Optimizer/yogi.py +60 -0
  150. multioptpy/OtherMethod/__init__.py +0 -0
  151. multioptpy/OtherMethod/addf.py +1150 -0
  152. multioptpy/OtherMethod/dimer.py +895 -0
  153. multioptpy/OtherMethod/elastic_image_pair.py +629 -0
  154. multioptpy/OtherMethod/modelfunction.py +456 -0
  155. multioptpy/OtherMethod/newton_traj.py +454 -0
  156. multioptpy/OtherMethod/twopshs.py +1095 -0
  157. multioptpy/PESAnalyzer/__init__.py +0 -0
  158. multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
  159. multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
  160. multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
  161. multioptpy/PESAnalyzer/pca_analysis.py +314 -0
  162. multioptpy/Parameters/__init__.py +0 -0
  163. multioptpy/Parameters/atomic_mass.py +20 -0
  164. multioptpy/Parameters/atomic_number.py +22 -0
  165. multioptpy/Parameters/covalent_radii.py +44 -0
  166. multioptpy/Parameters/d2.py +61 -0
  167. multioptpy/Parameters/d3.py +63 -0
  168. multioptpy/Parameters/d4.py +103 -0
  169. multioptpy/Parameters/dreiding.py +34 -0
  170. multioptpy/Parameters/gfn0xtb_param.py +137 -0
  171. multioptpy/Parameters/gfnff_param.py +315 -0
  172. multioptpy/Parameters/gnb.py +104 -0
  173. multioptpy/Parameters/parameter.py +22 -0
  174. multioptpy/Parameters/uff.py +72 -0
  175. multioptpy/Parameters/unit_values.py +20 -0
  176. multioptpy/Potential/AFIR_potential.py +55 -0
  177. multioptpy/Potential/LJ_repulsive_potential.py +345 -0
  178. multioptpy/Potential/__init__.py +0 -0
  179. multioptpy/Potential/anharmonic_keep_potential.py +28 -0
  180. multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
  181. multioptpy/Potential/electrostatic_potential.py +69 -0
  182. multioptpy/Potential/flux_potential.py +30 -0
  183. multioptpy/Potential/gaussian_potential.py +101 -0
  184. multioptpy/Potential/idpp.py +516 -0
  185. multioptpy/Potential/keep_angle_potential.py +146 -0
  186. multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
  187. multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
  188. multioptpy/Potential/keep_potential.py +99 -0
  189. multioptpy/Potential/mechano_force_potential.py +74 -0
  190. multioptpy/Potential/nanoreactor_potential.py +52 -0
  191. multioptpy/Potential/potential.py +896 -0
  192. multioptpy/Potential/spacer_model_potential.py +221 -0
  193. multioptpy/Potential/switching_potential.py +258 -0
  194. multioptpy/Potential/universal_potential.py +34 -0
  195. multioptpy/Potential/value_range_potential.py +36 -0
  196. multioptpy/Potential/void_point_potential.py +25 -0
  197. multioptpy/SQM/__init__.py +0 -0
  198. multioptpy/SQM/sqm1/__init__.py +0 -0
  199. multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
  200. multioptpy/SQM/sqm2/__init__.py +0 -0
  201. multioptpy/SQM/sqm2/calc_tools.py +95 -0
  202. multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
  203. multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
  204. multioptpy/SQM/sqm2/sqm2_core.py +303 -0
  205. multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
  206. multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
  207. multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
  208. multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
  209. multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
  210. multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
  211. multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
  212. multioptpy/Thermo/__init__.py +0 -0
  213. multioptpy/Thermo/normal_mode_analyzer.py +865 -0
  214. multioptpy/Utils/__init__.py +0 -0
  215. multioptpy/Utils/bond_connectivity.py +264 -0
  216. multioptpy/Utils/calc_tools.py +884 -0
  217. multioptpy/Utils/oniom.py +96 -0
  218. multioptpy/Utils/pbc.py +48 -0
  219. multioptpy/Utils/riemann_curvature.py +208 -0
  220. multioptpy/Utils/symmetry_analyzer.py +482 -0
  221. multioptpy/Visualization/__init__.py +0 -0
  222. multioptpy/Visualization/visualization.py +156 -0
  223. multioptpy/WFAnalyzer/MO_analysis.py +104 -0
  224. multioptpy/WFAnalyzer/__init__.py +0 -0
  225. multioptpy/Wrapper/__init__.py +0 -0
  226. multioptpy/Wrapper/autots.py +1239 -0
  227. multioptpy/Wrapper/ieip_wrapper.py +93 -0
  228. multioptpy/Wrapper/md_wrapper.py +92 -0
  229. multioptpy/Wrapper/neb_wrapper.py +94 -0
  230. multioptpy/Wrapper/optimize_wrapper.py +76 -0
  231. multioptpy/__init__.py +5 -0
  232. multioptpy/entrypoints.py +916 -0
  233. multioptpy/fileio.py +660 -0
  234. multioptpy/ieip.py +340 -0
  235. multioptpy/interface.py +1086 -0
  236. multioptpy/irc.py +529 -0
  237. multioptpy/moleculardynamics.py +432 -0
  238. multioptpy/neb.py +1267 -0
  239. multioptpy/optimization.py +1553 -0
  240. multioptpy/optimizer.py +709 -0
  241. multioptpy-1.20.2.dist-info/METADATA +438 -0
  242. multioptpy-1.20.2.dist-info/RECORD +246 -0
  243. multioptpy-1.20.2.dist-info/WHEEL +5 -0
  244. multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
  245. multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
  246. 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")