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,884 @@
1
+ import itertools
2
+ import numpy as np
3
+ import copy
4
+ from scipy.spatial import cKDTree
5
+ from scipy.interpolate import make_interp_spline
6
+
7
+ from multioptpy.Interpolation.interpolation import spline_interpolation
8
+ from multioptpy.Potential.idpp import IDPP
9
+ from multioptpy.Parameters.parameter import covalent_radii_lib, atomic_mass, UnitValueLib
10
+
11
+ try:
12
+ import torch
13
+ except:
14
+ print("You cannot import pyTorch.")
15
+
16
+
17
+ class CalculationStructInfo:
18
+ def __init__(self):
19
+ return
20
+
21
+ def calculate_cos(self, bg, g):
22
+ if np.linalg.norm(bg) == 0.0 or np.linalg.norm(g) == 0.0:
23
+ cos = 2.0
24
+ else:
25
+ cos = np.sum(bg * g) / (np.linalg.norm(g) * np.linalg.norm(bg))
26
+ return cos
27
+
28
+
29
+ def calculate_distance(self, atom1, atom2):
30
+ atom1, atom2 = np.array(atom1, dtype="float64"), np.array(atom2, dtype="float64")
31
+ distance = np.linalg.norm(atom2 - atom1)
32
+ return distance
33
+
34
+
35
+ def calculate_bond_angle(self, atom1, atom2, atom3):
36
+ atom1, atom2, atom3 = np.array(atom1, dtype="float64"), np.array(atom2, dtype="float64"), np.array(atom3, dtype="float64")
37
+ vector1 = atom1 - atom2
38
+ vector2 = atom3 - atom2
39
+
40
+ cos_angle = np.dot(vector1, vector2) / (np.linalg.norm(vector1) * np.linalg.norm(vector2))
41
+ angle = np.arccos(cos_angle)
42
+ angle_deg = np.degrees(angle)
43
+
44
+ return angle_deg
45
+
46
+ def calculate_dihedral_angle(self, atom1, atom2, atom3, atom4):
47
+ atom1, atom2, atom3, atom4 = np.array(atom1, dtype="float64"), np.array(atom2, dtype="float64"), np.array(atom3, dtype="float64"), np.array(atom4, dtype="float64")
48
+
49
+ a1 = atom2 - atom1
50
+ a2 = atom3 - atom2
51
+ a3 = atom4 - atom3
52
+
53
+ v1 = np.cross(a1, a2)
54
+ v1 = v1 / np.linalg.norm(v1, ord=2)
55
+ v2 = np.cross(a2, a3)
56
+ v2 = v2 / np.linalg.norm(v2, ord=2)
57
+ porm = np.sign((v1 * a3).sum(-1))
58
+ angle = np.arccos((v1*v2).sum(-1) / ((v1**2).sum(-1) * (v2**2).sum(-1))**0.5)
59
+ if not porm == 0:
60
+ angle = angle * porm
61
+
62
+ dihedral_angle_deg = np.degrees(angle)
63
+
64
+ return dihedral_angle_deg
65
+
66
+
67
+ def read_xyz_file(self, file_name):
68
+ with open(file_name,"r") as f:
69
+ words = f.readlines()
70
+ mole_struct_list = []
71
+
72
+ for word in words[1:]:
73
+ mole_struct_list.append(word.split())
74
+ return mole_struct_list
75
+
76
+ def Data_extract(self, file, atom_numbers):
77
+ data_list = []
78
+ data_name_list = []
79
+
80
+
81
+
82
+ mole_struct_list = self.read_xyz_file(file)
83
+ DBD_list = []
84
+ DBD_name_list = []
85
+ #print(file, atom_numbers)
86
+ if len(atom_numbers) > 1:
87
+ for a1, a2 in list(itertools.combinations(atom_numbers,2)):
88
+ #print(a1, a2)
89
+ try:
90
+ distance = self.calculate_distance(mole_struct_list[int(a1) - 1][1:4], mole_struct_list[int(a2) - 1][1:4])
91
+ DBD_name_list.append("Distance ("+str(a1)+"-"+str(a2)+") [ang.]")
92
+ DBD_list.append(distance)
93
+
94
+ except Exception as e:
95
+ print(e)
96
+ DBD_name_list.append("Distance ("+str(a1)+"-"+str(a2)+") [ang.]")
97
+ DBD_list.append("nan")
98
+
99
+ if len(atom_numbers) > 2:
100
+ for a1, a2, a3 in list(itertools.permutations(atom_numbers,3)):
101
+ try:
102
+ bond_angle = self.calculate_bond_angle(mole_struct_list[int(a1)-1][1:4], mole_struct_list[int(a2)-1][1:4], mole_struct_list[int(a3)-1][1:4])
103
+ DBD_name_list.append("Bond_angle ("+str(a1)+"-"+str(a2)+"-"+str(a3)+") [deg.]")
104
+ DBD_list.append(bond_angle)
105
+ except Exception as e:
106
+ print(e)
107
+ DBD_name_list.append("Bond_angle ("+str(a1)+"-"+str(a2)+"-"+str(a3)+") [deg.]")
108
+ DBD_list.append("nan")
109
+
110
+ if len(atom_numbers) > 3:
111
+ for a1, a2, a3, a4 in list(itertools.permutations(atom_numbers,4)):
112
+ try:
113
+ dihedral_angle = self.calculate_dihedral_angle(mole_struct_list[int(a1)-1][1:4], mole_struct_list[int(a2)-1][1:4],mole_struct_list[int(a3)-1][1:4], mole_struct_list[int(a4)-1][1:4])
114
+ DBD_name_list.append("Dihedral_angle ("+str(a1)+"-"+str(a2)+"-"+str(a3)+"-"+str(a4)+") [deg.]")
115
+ DBD_list.append(dihedral_angle)
116
+ except Exception as e:
117
+ print(e)
118
+ DBD_name_list.append("Dihedral_angle ("+str(a1)+"-"+str(a2)+"-"+str(a3)+"-"+str(a4)+") [deg.]")
119
+ DBD_list.append("nan")
120
+
121
+ data_list = DBD_list
122
+
123
+ data_name_list = DBD_name_list
124
+ return data_list, data_name_list
125
+
126
+ def hess2mwhess(hessian, element_list):
127
+ elem_mass = np.array([atomic_mass(elem) for elem in element_list], dtype="float64")
128
+ M = np.diag(np.repeat(elem_mass, 3))
129
+ M_minus_sqrt = np.diag(np.repeat(elem_mass, 3) ** (-0.5))
130
+ mw_hessian = np.dot(np.dot(M_minus_sqrt, hessian), M_minus_sqrt)#mw = mass weighted
131
+ return mw_hessian
132
+
133
+
134
+ class Calculationtools:
135
+ def __init__(self):
136
+ return
137
+
138
+ def calc_center(self, geomerty, element_list=[]):#geomerty:Bohr
139
+ center = np.array([0.0, 0.0, 0.0], dtype="float64")
140
+ for i in range(len(geomerty)):
141
+
142
+ center += geomerty[i]
143
+ center /= len(geomerty)
144
+
145
+ return center
146
+
147
+
148
+ def calc_center_of_mass(self, geomerty, element_list):#geomerty:Bohr
149
+ center_of_mass = np.array([0.0, 0.0, 0.0], dtype="float64")
150
+ elem_mass = np.array([atomic_mass(elem) for elem in element_list], dtype="float64")
151
+
152
+ for i in range(len(elem_mass)):
153
+
154
+ center_of_mass += geomerty[i] * elem_mass[i]
155
+
156
+ center_of_mass /= np.sum(elem_mass)
157
+
158
+ return center_of_mass
159
+
160
+ def coord2massweightedcoord(self, geomerty, element_list):
161
+ #output: Mass-weighted coordinates adjusted to the origin of the mass-weighted point
162
+ center_of_mass = self.calc_center_of_mass(geomerty, element_list)
163
+ geomerty -= center_of_mass
164
+ elem_mass = np.array([atomic_mass(elem) for elem in element_list], dtype="float64")
165
+ mass_weighted_coord = geomerty * 0.0
166
+ for i in range(len(geomerty)):
167
+ mass_weighted_coord[i] = copy.copy(geomerty[i] * np.sqrt(elem_mass[i]))
168
+ return mass_weighted_coord
169
+
170
+ def project_out_hess_tr_and_rot(self, hessian, element_list, geometry, display_eigval=True):#covert coordination to mass-weighted coordination
171
+ natoms = len(element_list)
172
+
173
+ # Move to center of mass
174
+ geometry = geometry - self.calc_center_of_mass(geometry, element_list)
175
+
176
+ # Calculate atomic masses
177
+ elem_mass = np.array([atomic_mass(elem) for elem in element_list], dtype="float64")
178
+
179
+ # Mass-weighting matrices
180
+ m_sqrt = np.repeat(elem_mass, 3) ** 0.5
181
+ M_minus_sqrt = np.diag(np.repeat(elem_mass, 3) ** (-0.5))
182
+
183
+ # Convert to mass-weighted Hessian
184
+ mw_hessian = np.dot(np.dot(M_minus_sqrt, hessian), M_minus_sqrt)
185
+
186
+ # Initialize arrays for translation and rotation vectors
187
+ tr_vectors = np.zeros((3, 3 * natoms))
188
+ rot_vectors = np.zeros((3, 3 * natoms))
189
+
190
+ # Create mass-weighted translation vectors
191
+ for i in range(3):
192
+ tr_vectors[i, i::3] = m_sqrt[i::3]
193
+
194
+ # Create mass-weighted rotation vectors
195
+ for atom in range(natoms):
196
+ x, y, z = geometry[atom]
197
+ mass_sqrt = m_sqrt[3*atom]
198
+
199
+ # Rotation around x-axis: (0, -z, y)
200
+ rot_vectors[0, 3*atom:3*atom+3] = np.array([0.0, -z, y]) * mass_sqrt
201
+
202
+ # Rotation around y-axis: (z, 0, -x)
203
+ rot_vectors[1, 3*atom:3*atom+3] = np.array([z, 0.0, -x]) * mass_sqrt
204
+
205
+ # Rotation around z-axis: (-y, x, 0)
206
+ rot_vectors[2, 3*atom:3*atom+3] = np.array([-y, x, 0.0]) * mass_sqrt
207
+
208
+ # Combine translation and rotation vectors
209
+ TR_vectors = np.vstack([tr_vectors, rot_vectors])
210
+
211
+ # Gram-Schmidt orthonormalization with improved numerical stability
212
+ def gram_schmidt(vectors):
213
+ basis = []
214
+ for v in vectors:
215
+ w = v.copy()
216
+ for b in basis:
217
+ w -= np.dot(v, b) * b
218
+ norm = np.linalg.norm(w)
219
+ if norm > 1e-10: # Threshold for linear independence
220
+ basis.append(w / norm)
221
+ return np.array(basis)
222
+
223
+ # Orthonormalize the translation and rotation vectors
224
+ TR_vectors = gram_schmidt(TR_vectors)
225
+
226
+ # Calculate projection matrix
227
+ P = np.eye(3 * natoms)
228
+ for vector in TR_vectors:
229
+ P -= np.outer(vector, vector)
230
+
231
+ # Project the mass-weighted Hessian
232
+ mw_hess_proj = np.dot(np.dot(P.T, mw_hessian), P)
233
+
234
+ # Ensure symmetry (numerical stability)
235
+ mw_hess_proj = (mw_hess_proj + mw_hess_proj.T) / 2
236
+
237
+ if display_eigval:
238
+ eigenvalues, _ = np.linalg.eigh(mw_hess_proj)
239
+ eigenvalues = np.sort(eigenvalues)
240
+ # Stricter threshold for eigenvalue filtering
241
+ idx_eigenvalues = np.where(np.abs(eigenvalues) > 1e-7)[0]
242
+ print(f"EIGENVALUES (MASS-WEIGHTED COORDINATE, NUMBER OF VALUES: {len(idx_eigenvalues)}):")
243
+ for i in range(0, len(idx_eigenvalues), 6):
244
+ tmp_arr = eigenvalues[idx_eigenvalues[i:i+6]]
245
+ print(" ".join(f"{val:12.8f}" for val in tmp_arr))
246
+
247
+ return mw_hess_proj
248
+
249
+ def project_out_hess_tr_and_rot_for_coord(self, hessian, element_list, geometry, display_eigval=True):#do not consider atomic mass
250
+ def gram_schmidt(vectors):
251
+ basis = []
252
+ for v in vectors:
253
+ w = v.copy()
254
+ for b in basis:
255
+ w -= np.dot(v, b) * b
256
+ norm = np.linalg.norm(w)
257
+ if norm > 1e-10:
258
+ basis.append(w / norm)
259
+ return np.array(basis)
260
+
261
+ natoms = len(element_list)
262
+ # Center the geometry
263
+ geometry = geometry - self.calc_center(geometry, element_list)
264
+
265
+ # Initialize arrays for translation and rotation vectors
266
+ tr_vectors = np.zeros((3, 3 * natoms))
267
+ rot_vectors = np.zeros((3, 3 * natoms))
268
+
269
+ # Create translation vectors (mass-weighted normalization is not used as specified)
270
+ for i in range(3):
271
+ tr_vectors[i, i::3] = 1.0
272
+
273
+ # Create rotation vectors
274
+ for atom in range(natoms):
275
+ # Get atom coordinates
276
+ x, y, z = geometry[atom]
277
+
278
+ # Rotation around x-axis: (0, -z, y)
279
+ rot_vectors[0, 3*atom:3*atom+3] = np.array([0.0, -z, y])
280
+
281
+ # Rotation around y-axis: (z, 0, -x)
282
+ rot_vectors[1, 3*atom:3*atom+3] = np.array([z, 0.0, -x])
283
+
284
+ # Rotation around z-axis: (-y, x, 0)
285
+ rot_vectors[2, 3*atom:3*atom+3] = np.array([-y, x, 0.0])
286
+
287
+ # Combine translation and rotation vectors
288
+ TR_vectors = np.vstack([tr_vectors, rot_vectors])
289
+
290
+
291
+
292
+ # Orthonormalize the translation and rotation vectors
293
+ TR_vectors = gram_schmidt(TR_vectors)
294
+
295
+ # Calculate projection matrix
296
+ P = np.eye(3 * natoms)
297
+ for vector in TR_vectors:
298
+ P -= np.outer(vector, vector)
299
+
300
+ # Project the Hessian
301
+ hess_proj = np.dot(np.dot(P.T, hessian), P)
302
+
303
+ # Make the projected Hessian symmetric (numerical stability)
304
+ hess_proj = (hess_proj + hess_proj.T) / 2
305
+
306
+ if display_eigval:
307
+ eigenvalues, _ = np.linalg.eigh(hess_proj)
308
+ eigenvalues = np.sort(eigenvalues)
309
+ # Filter out near-zero eigenvalues
310
+ idx_eigenvalues = np.where(np.abs(eigenvalues) > 1e-10)[0]
311
+ print(f"EIGENVALUES (NORMAL COORDINATE, NUMBER OF VALUES: {len(idx_eigenvalues)}):")
312
+ for i in range(0, len(idx_eigenvalues), 6):
313
+ tmp_arr = eigenvalues[idx_eigenvalues[i:i+6]]
314
+ print(" ".join(f"{val:12.8f}" for val in tmp_arr))
315
+
316
+ return hess_proj
317
+
318
+ def check_atom_connectivity(self, mol_list, element_list, atom_num, covalent_radii_threshold_scale=1.2):#mol_list:ang.
319
+ # Convert molecular coordinates to numpy array
320
+ coords = np.array(mol_list, dtype=np.float64)
321
+
322
+ # Build KD-Tree for efficient nearest neighbor searches
323
+ kdtree = cKDTree(coords)
324
+
325
+ # Initialize arrays for tracking
326
+ n_atoms = len(mol_list)
327
+ connected_atoms = [atom_num]
328
+ searched_atoms = []
329
+
330
+ # Pre-calculate covalent radii for each element to avoid repeated lookups
331
+ cov_radii = [covalent_radii_lib(element) * UnitValueLib().bohr2angstroms for element in element_list]
332
+
333
+ while True:
334
+ search_progress = False
335
+ for i in connected_atoms:
336
+ if i in searched_atoms:
337
+ continue
338
+
339
+ # Calculate max possible bond distance for this atom
340
+ # This is a conservative estimate to limit initial search radius
341
+ max_cov_radius = max([cov_radii[i] + cov_radii[j] for j in range(n_atoms)]) * covalent_radii_threshold_scale
342
+
343
+ # Query the KD-Tree for potential neighbors within the max bond distance
344
+ potential_neighbors = kdtree.query_ball_point(coords[i], max_cov_radius)
345
+
346
+ # Check each potential neighbor more precisely
347
+ for j in potential_neighbors:
348
+ if j == i or j in connected_atoms:
349
+ continue
350
+
351
+ # Calculate exact threshold for this specific pair
352
+ covalent_dist_threshold = covalent_radii_threshold_scale * (cov_radii[i] + cov_radii[j])
353
+
354
+ # Calculate distance
355
+ dist = np.linalg.norm(coords[i] - coords[j])
356
+
357
+ if dist < covalent_dist_threshold:
358
+ connected_atoms.append(j)
359
+ search_progress = True
360
+
361
+ searched_atoms.append(i)
362
+ search_progress = True
363
+
364
+ if not search_progress or len(connected_atoms) == len(searched_atoms):
365
+ break
366
+
367
+ return sorted(connected_atoms)
368
+
369
+ def calc_fragm_distance_matrix(self, fragm_coord_list):
370
+ distance_matrix = np.zeros((len(fragm_coord_list), len(fragm_coord_list)))
371
+ for i in range(len(fragm_coord_list)):
372
+ for j in range(len(fragm_coord_list)):
373
+ if i < j:
374
+ continue
375
+ dist = np.linalg.norm(self.calc_center(fragm_coord_list[i], []) - self.calc_center(fragm_coord_list[j], []))
376
+ distance_matrix[i][j] = dist
377
+ distance_matrix[j][i] = dist
378
+
379
+ return distance_matrix
380
+
381
+
382
+ def calc_fragm_distance(self, geom_num_list, fragm_1_num, fragm_2_num):
383
+ fragm_1_coord = np.array([0.0, 0.0, 0.0], dtype="float64")
384
+ fragm_2_coord = np.array([0.0, 0.0, 0.0], dtype="float64")
385
+
386
+ for num in fragm_1_num:
387
+ fragm_1_coord += geom_num_list[num]
388
+
389
+ fragm_1_coord /= len(fragm_1_num)
390
+
391
+ for num in fragm_2_num:
392
+ fragm_2_coord += geom_num_list[num]
393
+
394
+ fragm_2_coord /= len(fragm_2_num)
395
+
396
+ dist = np.linalg.norm(fragm_1_coord - fragm_2_coord)
397
+
398
+ return dist
399
+
400
+ def calc_geodesic_distance(self, geom_num_list_1, geom_num_list_2):
401
+ #doi:10.1002/jcc.27030
402
+ geodesic_dist_mat = np.ones((len(geom_num_list_1), 3))
403
+ dist = np.linalg.norm(geom_num_list_2 - geom_num_list_1)
404
+ geodesic_dist_mat *= dist / np.sqrt(3 * len(geom_num_list_1))
405
+ return geodesic_dist_mat
406
+
407
+ def calc_euclidean_distance(self, geom_num_list_1, geom_num_list_2):
408
+ #doi:10.1002/jcc.27030
409
+ euclidean_dist_mat = geom_num_list_2 - geom_num_list_1
410
+ return euclidean_dist_mat
411
+
412
+ def kabsch_algorithm(self, P, Q):
413
+ #scipy.spatial.transform.Rotation.align_vectors
414
+ centroid_P = np.array([np.mean(P.T[0]), np.mean(P.T[1]), np.mean(P.T[2])], dtype="float64")
415
+ centroid_Q = np.array([np.mean(Q.T[0]), np.mean(Q.T[1]), np.mean(Q.T[2])], dtype="float64")
416
+ P -= centroid_P
417
+ Q -= centroid_Q
418
+ H = np.dot(P.T, Q)
419
+ U, S, Vt = np.linalg.svd(H)
420
+ R = np.dot(Vt.T, U.T)
421
+ if np.linalg.det(R) < 0:
422
+ Vt[-1,:] *= -1
423
+ R = np.dot(Vt.T, U.T)
424
+ P = np.dot(R, P.T).T
425
+ return P, Q
426
+
427
+ def gen_n_dinensional_rot_matrix(self, vector_1, vector_2):
428
+ #Zhelezov NRMG algorithm (doi:10.5923/j.ajcam.20170702.04) This implementation may be not correct.
429
+ dimension_1 = len(vector_1)
430
+ dimension_2 = len(vector_2)
431
+ assert dimension_1 == dimension_2
432
+ R_1 = np.eye((dimension_1))
433
+ R_2 = np.eye((dimension_2))
434
+
435
+
436
+
437
+ step = 1
438
+
439
+ while step < dimension_1:
440
+ A_1 = np.eye((dimension_1))
441
+ n = 1
442
+ #print(step)
443
+ while n <= dimension_1 - step:
444
+ #print(n)
445
+ #print(vector_1[n + step - 1])
446
+ r2 = vector_1[n - 1] ** 2 + vector_1[n + step - 1] ** 2
447
+ if r2 > 0:
448
+ r = r2 ** 0.5
449
+ p_cos = vector_1[n - 1] / r
450
+
451
+ p_sin = -1 * vector_1[n + step - 1] / r
452
+ A_1[n - 1][n - 1] = p_cos.item()
453
+ A_1[n - 1][n + step - 1] = -1 * p_sin.item()
454
+ A_1[n + step - 1][n - 1] = p_sin.item()
455
+ A_1[n + step - 1][n + step - 1] = p_cos.item()
456
+ n += 2 * step
457
+ step *= 2
458
+ vector_1 = np.dot(A_1, vector_1)
459
+ R_1 = np.dot(A_1, R_1)
460
+
461
+ step = 1
462
+
463
+ while step < dimension_2:
464
+ A_2 = np.eye((dimension_2))
465
+ n = 1
466
+ while n <= dimension_2 - step:
467
+ r2 = vector_2[n - 1] ** 2 + vector_2[n + step - 1] ** 2
468
+ if r2 > 0:
469
+ r = r2 ** 0.5
470
+ p_cos = vector_2[n - 1] / r
471
+ p_sin = -1 * vector_2[n + step - 1] / r
472
+ A_2[n - 1][n - 1] = p_cos.item()
473
+ A_2[n - 1][n + step - 1] = -1 * p_sin.item()
474
+ A_2[n + step - 1][n - 1] = p_sin.item()
475
+ A_2[n + step - 1][n + step - 1] = p_cos.item()
476
+ n += 2 * step
477
+ step *= 2
478
+ vector_2 = np.dot(A_2, vector_2)
479
+ R_2 = np.dot(A_2, R_2)
480
+ #print(R_1, R_2)
481
+ R_12 = np.dot(R_2.T, R_1)
482
+ #vector_1 -> vector_2's direction
483
+ return R_12
484
+
485
+ def calc_multi_dim_vec_angle(self, vec_1, vec_2):
486
+
487
+ angle = np.arccos(np.sum(vec_1 * vec_2) / (np.linalg.norm(vec_1) * np.linalg.norm(vec_2)) + 1e-8)
488
+
489
+ return angle
490
+
491
+ def calc_normalized_distance_list(geom_num_list, element_list, tgt_atoms=None):
492
+ if tgt_atoms is not None:
493
+ atom_list = [i for i in tgt_atoms]
494
+ else:
495
+ atom_list = [i for i in range(len(geom_num_list))]
496
+
497
+ norm_distance_list = []
498
+
499
+ for i, j in itertools.combinations(atom_list, 2):#(0, 1) (0, 2) ... (natoms-2, natoms-1)
500
+ elem_i = element_list[i]
501
+ elem_j = element_list[j]
502
+ covalent_length = covalent_radii_lib(elem_i) + covalent_radii_lib(elem_j)
503
+ norm_distance = np.linalg.norm(geom_num_list[i] - geom_num_list[j]) / covalent_length
504
+ norm_distance_list.append(norm_distance)
505
+ norm_distance_list = np.array(norm_distance_list)
506
+ return norm_distance_list
507
+
508
+ def return_pair_idx(i, j):
509
+ ii = max(i, j) + 1
510
+ jj = min(i, j) + 1
511
+ pair_idx = int(ii * (ii - 1) / 2 - (ii - jj)) -1
512
+ return pair_idx
513
+
514
+ def calc_bond_length_from_vec(vector1, vector2):
515
+ distance = np.linalg.norm(vector1 - vector2)
516
+ return distance
517
+
518
+ def torch_calc_angle_from_vec(vector1, vector2):
519
+ magnitude1 = torch.linalg.norm(vector1)
520
+ if torch.abs(magnitude1) < 1e-15:
521
+ magnitude1 = magnitude1 + 1e-15
522
+ magnitude2 = torch.linalg.norm(vector2)
523
+ if torch.abs(magnitude2) < 1e-15:
524
+ magnitude2 = magnitude2 + 1e-15
525
+
526
+ dot_product = torch.matmul(vector1, vector2)
527
+ cos_theta = dot_product / (magnitude1 * magnitude2)
528
+ theta = torch.arccos(cos_theta)
529
+ return theta
530
+
531
+ def calc_angle_from_vec(vector1, vector2):
532
+ magnitude1 = np.linalg.norm(vector1)
533
+ if np.abs(magnitude1) < 1e-15:
534
+ magnitude1 += 1e-15
535
+ magnitude2 = np.linalg.norm(vector2)
536
+ if np.abs(magnitude2) < 1e-15:
537
+ magnitude2 += 1e-15
538
+ dot_product = np.matmul(vector1, vector2)
539
+ cos_theta = dot_product / (magnitude1 * magnitude2)
540
+ theta = np.arccos(cos_theta)
541
+ return theta
542
+
543
+ def torch_calc_dihedral_angle_from_vec(vector1, vector2, vector3):
544
+ v1 = torch.linalg.cross(vector1, vector2)
545
+ v2 = torch.linalg.cross(vector2, vector3)
546
+ norm_v1 = torch.linalg.norm(v1)
547
+ if torch.abs(norm_v1) < 1e-15:
548
+ norm_v1 = norm_v1 + 1e-15
549
+ norm_v2 = torch.linalg.norm(v2)
550
+ if torch.abs(norm_v2) < 1e-15:
551
+ norm_v2 = norm_v2 + 1e-15
552
+
553
+ cos_theta = torch.sum(v1*v2) / (norm_v1 * norm_v2)
554
+ angle = torch.arccos(cos_theta)
555
+ sign = torch.sign(torch.sum(torch.linalg.cross(v1 / norm_v1, v2 / norm_v2) * vector2))
556
+ if sign != 0:
557
+ angle = -1 * angle * sign
558
+ return angle
559
+
560
+
561
+ def change_torsion_angle_both_side(coordinates, atom_idx1, atom_idx2, atom_idx3, atom_idx4, target_torsion):#rad:target_torsion
562
+ A = coordinates[atom_idx1]
563
+ B = coordinates[atom_idx2]
564
+ C = coordinates[atom_idx3]
565
+ D = coordinates[atom_idx4]
566
+ current_torsion = calc_dihedral_angle_from_vec(A - B, B - C, C - D)
567
+
568
+ torsion_diff = target_torsion - current_torsion
569
+
570
+ BC = C - B
571
+ new_D = rotate_atom(D, C, BC, torsion_diff * 0.5)
572
+ new_A = rotate_atom(A, B, BC, -1*torsion_diff * 0.5)
573
+ coordinates[atom_idx4] = new_D
574
+ coordinates[atom_idx1] = new_A
575
+
576
+ return coordinates
577
+
578
+ def change_bond_angle_both_side(coordinates, atom_idx1, atom_idx2, atom_idx3, target_angle):#rad:target_angle
579
+ A = coordinates[atom_idx1]
580
+ B = coordinates[atom_idx2]
581
+ C = coordinates[atom_idx3]
582
+ BA = A - B
583
+ BC = C - B
584
+ current_angle_rad = calc_angle_from_vec(BA, BC)
585
+ rotation_axis = np.cross(BA, BC)
586
+ rotation_axis = rotation_axis / np.linalg.norm(rotation_axis)
587
+
588
+ angle_diff = target_angle - current_angle_rad
589
+
590
+ new_A = rotate_atom(A, B, rotation_axis, -angle_diff / 2.0)
591
+ new_C = rotate_atom(C, B, rotation_axis, angle_diff / 2.0)
592
+ coordinates[atom_idx1] = new_A
593
+ coordinates[atom_idx3] = new_C
594
+ return coordinates
595
+
596
+ def calc_dihedral_angle_from_vec(vector1, vector2, vector3):
597
+ v1 = np.cross(vector1, vector2)
598
+ v2 = np.cross(vector2, vector3)
599
+ norm_v1 = np.linalg.norm(v1)
600
+ if np.abs(norm_v1) < 1e-15:
601
+ norm_v1 += 1e-15
602
+ if np.abs(norm_v2) < 1e-15:
603
+ norm_v2 += 1e-15
604
+
605
+ cos_theta = np.sum(v1*v2) / (norm_v1 * norm_v2)
606
+ angle = np.abs(np.arccos(cos_theta))
607
+ sign = np.sign(np.dot(np.cross(v1 / norm_v1, v2 / norm_v2), vector2))
608
+ if sign != 0:
609
+ angle = -1 * angle * sign
610
+ return angle
611
+
612
+
613
+
614
+ def rotate_atom(coord, axis_point, axis_direction, angle):
615
+ axis_unit = axis_direction / np.linalg.norm(axis_direction)
616
+ translated_coord = coord - axis_point
617
+ cos_angle = np.cos(angle)
618
+ sin_angle = np.sin(angle)
619
+ rotation_matrix = np.array([
620
+ [cos_angle + axis_unit[0]**2 * (1 - cos_angle), axis_unit[0]*axis_unit[1]*(1 - cos_angle) - axis_unit[2]*sin_angle, axis_unit[0]*axis_unit[2]*(1 - cos_angle) + axis_unit[1]*sin_angle],
621
+ [axis_unit[1]*axis_unit[0]*(1 - cos_angle) + axis_unit[2]*sin_angle, cos_angle + axis_unit[1]**2 * (1 - cos_angle), axis_unit[1]*axis_unit[2]*(1 - cos_angle) - axis_unit[0]*sin_angle],
622
+ [axis_unit[2]*axis_unit[0]*(1 - cos_angle) - axis_unit[1]*sin_angle, axis_unit[2]*axis_unit[1]*(1 - cos_angle) + axis_unit[0]*sin_angle, cos_angle + axis_unit[2]**2 * (1 - cos_angle)]
623
+ ])
624
+ rotated_coord = np.dot(rotation_matrix, translated_coord)
625
+ return rotated_coord + axis_point
626
+
627
+ def torch_calc_outofplain_angle_from_vec(vector1, vector2, vector3):
628
+ v1 = torch.linalg.cross(vector1, vector2)
629
+ magnitude1 = torch.linalg.norm(v1)
630
+ if torch.abs(magnitude1) < 1e-15:
631
+ magnitude1 = magnitude1 + 1e-15
632
+ magnitude2 = torch.linalg.norm(vector3)
633
+ if torch.abs(magnitude2) < 1e-15:
634
+ magnitude2 = magnitude2 + 1e-15
635
+
636
+ dot_product = torch.matmul(v1, vector3)
637
+ cos_theta = dot_product / (magnitude1 * magnitude2)
638
+ angle = torch.arccos(cos_theta)
639
+ return angle
640
+
641
+ def calc_outofplain_angle_from_vec(vector1, vector2, vector3):
642
+ v1 = np.cross(vector1, vector2)
643
+
644
+ magnitude1 = np.linalg.norm(v1)
645
+ if np.abs(magnitude1) < 1e-15:
646
+ magnitude1 += 1e-15
647
+ magnitude2 = np.linalg.norm(vector3)
648
+ if np.abs(magnitude2) < 1e-15:
649
+ magnitude2 += 1e-15
650
+
651
+ dot_product = np.matmul(v1, vector3)
652
+ cos_theta = dot_product / (magnitude1 * magnitude2)
653
+ angle = np.arccos(cos_theta)
654
+ return angle
655
+
656
+
657
+ def output_partial_hess(hessian, atom_num_list, element_list, geometry):#hessian: ndarray 3N*3N, atom_num_list: list
658
+ partial_hess = np.zeros((3*len(atom_num_list), 3*len(atom_num_list)))
659
+ partial_geom = np.zeros((len(atom_num_list), 3))
660
+ partial_element_list = []
661
+
662
+ # Copy the relevant parts of the geometry and element list
663
+ for i in range(len(atom_num_list)):
664
+ partial_geom[i] = copy.copy(geometry[atom_num_list[i]-1])
665
+ partial_element_list.append(element_list[atom_num_list[i]-1])
666
+
667
+ # Copy the relevant parts of the Hessian matrix
668
+ for i, j in itertools.product(range(len(atom_num_list)), repeat=2):
669
+ for k in range(3):
670
+ for l in range(3):
671
+ partial_hess[3*i+k][3*j+l] = copy.copy(hessian[3*(atom_num_list[i]-1)+k][3*(atom_num_list[j]-1)+l])
672
+
673
+ return partial_hess, partial_geom, partial_element_list
674
+
675
+ def fragment_check(new_geometry, element_list):
676
+ atom_label_list = [i for i in range(len(new_geometry))]
677
+ fragm_atom_num_list = []
678
+ while len(atom_label_list) > 0:
679
+ tmp_fragm_list = Calculationtools().check_atom_connectivity(new_geometry, element_list, atom_label_list[0], covalent_radii_threshold_scale=1.2)
680
+
681
+ for j in tmp_fragm_list:
682
+ atom_label_list.remove(j)
683
+ fragm_atom_num_list.append(tmp_fragm_list)
684
+
685
+ print("\nfragment_list:", fragm_atom_num_list)
686
+
687
+ return fragm_atom_num_list
688
+
689
+ def rotate_molecule(geom, axis, angle):
690
+ #geom: ndarray, axis: str, angle: float (radian)
691
+ #axis: "x", "y", "z"
692
+ if axis == "x":
693
+ rot_matrix = np.array([[1.0, 0.0, 0.0],
694
+ [0.0, np.cos(angle), -np.sin(angle)],
695
+ [0.0, np.sin(angle), np.cos(angle)]], dtype="float64")
696
+ elif axis == "y":
697
+ rot_matrix = np.array([[np.cos(angle), 0.0, np.sin(angle)],
698
+ [0.0, 1.0, 0.0],
699
+ [-np.sin(angle), 0.0, np.cos(angle)]], dtype="float64")
700
+ elif axis == "z":
701
+ rot_matrix = np.array([[np.cos(angle), -np.sin(angle), 0.0],
702
+ [np.sin(angle), np.cos(angle), 0.0],
703
+ [0.0, 0.0, 1.0]], dtype="float64")
704
+ else:
705
+ print("Invalid axis.")
706
+ return
707
+
708
+ new_geom = np.dot(geom, rot_matrix)
709
+ return new_geom
710
+
711
+ def calc_partial_center(geometry, atom_num_list):
712
+ partial_center = np.array([0.0, 0.0, 0.0], dtype="float64")
713
+
714
+ for i in atom_num_list:
715
+ partial_center += geometry[i-1]
716
+ partial_center /= len(atom_num_list)
717
+
718
+ return partial_center
719
+
720
+ def torch_calc_partial_center(geometry, atom_num_list):
721
+ partial_center = torch.tensor([0.0, 0.0, 0.0], dtype=torch.float64, requires_grad=True)
722
+ for i in atom_num_list:
723
+ partial_center = partial_center + geometry[i-1]
724
+ partial_center /= len(atom_num_list)
725
+ return partial_center
726
+
727
+ def project_optional_vector_for_grad(gradient, vector):#gradient:ndarray (3*natoms, 1), vector:ndarray (3*natoms, 1)
728
+ unit_vec = vector / np.linalg.norm(vector)
729
+ P_matrix = np.eye((len(gradient))) -1 * np.dot(unit_vec, unit_vec.T)
730
+ gradient_proj = np.dot(P_matrix, gradient).reshape(len(vector), 1)
731
+ return gradient_proj #gradient_proj:ndarray (3*natoms, 1)
732
+
733
+ def project_optional_vector_for_hess(hessian, vector):#hessian:ndarray (3*natoms, 3*natoms), vector:ndarray (3*natoms, 1)
734
+ hess_length = len(vector)
735
+ identity_matrix = np.eye(hess_length)
736
+ LL = np.dot(vector, vector.T)
737
+ E_LL = identity_matrix - LL
738
+ hessian_proj = np.dot(np.dot(E_LL, hessian), E_LL)
739
+ return hessian_proj #hessian:ndarray (3*natoms, 3*natoms)
740
+
741
+
742
+ def move_atom_distance_one_side(geom_num_list, atom1, atom2, distance):
743
+ vec = geom_num_list[atom2] - geom_num_list[atom1]
744
+ norm_vec = np.linalg.norm(vec)
745
+ unit_vec = vec / norm_vec
746
+ geom_num_list[atom2] = geom_num_list[atom2] + distance * unit_vec
747
+ return geom_num_list
748
+
749
+ def change_atom_distance_both_side(geom_num_list, atom1, atom2, distance):
750
+ vec = geom_num_list[atom2] - geom_num_list[atom1]
751
+ norm_vec = np.linalg.norm(vec)
752
+ unit_vec = vec / norm_vec
753
+ dist_diff = distance - norm_vec
754
+
755
+ geom_num_list[atom1] = geom_num_list[atom1] - dist_diff * unit_vec * 0.5
756
+ geom_num_list[atom2] = geom_num_list[atom2] + dist_diff * unit_vec * 0.5
757
+ return geom_num_list
758
+
759
+ def change_fragm_distance_both_side(geom_num_list, fragm1, fragm2, distance):
760
+ center_1 = np.array([0.0, 0.0, 0.0], dtype="float64")
761
+ for i in fragm1:
762
+ center_1 += geom_num_list[i]
763
+ center_1 /= len(fragm1)
764
+
765
+ center_2 = np.array([0.0, 0.0, 0.0], dtype="float64")
766
+ for i in fragm2:
767
+ center_2 += geom_num_list[i]
768
+ center_2 /= len(fragm2)
769
+
770
+ vec = center_2 - center_1
771
+ norm_vec = np.linalg.norm(vec)
772
+ unit_vec = vec / norm_vec
773
+ dist_diff = distance - norm_vec
774
+
775
+ for i in fragm1:
776
+ geom_num_list[i] = geom_num_list[i] - dist_diff * unit_vec * 0.5
777
+
778
+ for i in fragm2:
779
+ geom_num_list[i] = geom_num_list[i] + dist_diff * unit_vec * 0.5
780
+
781
+ return geom_num_list
782
+
783
+
784
+ def calc_bond_matrix(geom_num_list, element_list, threshold=1.2):
785
+ bond_matrix = np.zeros((len(geom_num_list), len(geom_num_list)))
786
+
787
+ for i in range(len(element_list)):
788
+ for j in range(i+1, len(element_list)):
789
+ r = np.linalg.norm(geom_num_list[i], geom_num_list[j])
790
+ r_cov = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
791
+ if r < threshold * r_cov:
792
+ bond_matrix[i][j] = 1
793
+ bond_matrix[j][i] = 1
794
+
795
+ return bond_matrix
796
+
797
+
798
+ def calc_RMS(data):
799
+ return np.sqrt(np.mean(data ** 2))
800
+
801
+
802
+ def torch_rotate_around_axis(theta, axis='z'):
803
+ cos_theta = torch.cos(theta).reshape(1)
804
+ sin_theta = torch.sin(theta).reshape(1)
805
+ tensor_zero = torch.tensor([0.0], dtype=torch.float64, requires_grad=True)
806
+ tensor_one = torch.tensor([1.0], dtype=torch.float64, requires_grad=True)
807
+
808
+ if axis == 'x':
809
+
810
+ R = torch.stack([torch.cat([tensor_one, tensor_zero, tensor_zero]),
811
+ torch.cat([tensor_zero, cos_theta, -sin_theta]),
812
+ torch.cat([tensor_zero, sin_theta, cos_theta])])
813
+ elif axis == 'y':
814
+
815
+ R = torch.stack([torch.cat([cos_theta, tensor_zero, sin_theta]),
816
+ torch.cat([tensor_zero, tensor_one, tensor_zero]),
817
+ torch.cat([-sin_theta, tensor_zero, cos_theta])])
818
+ elif axis == 'z':
819
+
820
+ R = torch.stack([torch.cat([cos_theta, -sin_theta, tensor_zero]),
821
+ torch.cat([sin_theta, cos_theta, tensor_zero]),
822
+ torch.cat([tensor_zero, tensor_zero, tensor_one])])
823
+ else:
824
+ raise ValueError
825
+
826
+ return R
827
+
828
+
829
+ def torch_align_vector_with_z(v):
830
+ v = v / torch.linalg.norm(v)
831
+ z = torch.tensor([0.0, 0.0, 1.0], dtype=v.dtype, requires_grad=True)
832
+ tensor_zero = torch.tensor([0.0], dtype=v.dtype, requires_grad=True)
833
+ axis = torch.linalg.cross(v, z)
834
+ axis_len = torch.linalg.norm(axis)
835
+
836
+ cos_theta = torch.dot(v, z)
837
+ sin_theta = axis_len
838
+
839
+ axis = axis / axis_len
840
+ axis = axis.reshape(3, 1)
841
+ K = torch.stack([
842
+ torch.cat([tensor_zero, -axis[2], axis[1]]),
843
+ torch.cat([axis[2], tensor_zero, -axis[0]]),
844
+ torch.cat([-axis[1], axis[0], tensor_zero])
845
+ ])
846
+
847
+ R = torch.eye(3, dtype=v.dtype, requires_grad=True) + sin_theta * K + (1 - cos_theta) * torch.matmul(K, K)
848
+ return R
849
+
850
+
851
+
852
+ def calc_path_length_list(geometry_list):
853
+ """Calculate path length list for geometry distribution"""
854
+ path_length_list = [0.0]
855
+ for i in range(len(geometry_list)-1):
856
+ tmp_geometry_list_j = geometry_list[i+1] - np.mean(geometry_list[i+1], axis=0)
857
+ tmp_geometry_list_i = geometry_list[i] - np.mean(geometry_list[i], axis=0)
858
+
859
+ path_length = path_length_list[-1] + np.linalg.norm(tmp_geometry_list_j - tmp_geometry_list_i)
860
+ path_length_list.append(path_length)
861
+ return path_length_list
862
+
863
+
864
+ def apply_climbing_image(geometry_list, energy_list, element_list):
865
+ """Apply climbing image method to locate transition states"""
866
+ path_length_list = calc_path_length_list(geometry_list)
867
+ total_length = path_length_list[-1]
868
+ local_maxima, local_minima = spline_interpolation(path_length_list, energy_list)
869
+ print(local_maxima)
870
+
871
+ for distance, energy in local_maxima:
872
+ print("Local maximum at distance: ", distance)
873
+ for i in range(2, len(path_length_list)-2):
874
+ if path_length_list[i] >= distance or distance >= path_length_list[i+1]:
875
+ continue
876
+ delta_t = (distance - path_length_list[i]) / (path_length_list[i+1] - path_length_list[i])
877
+ tmp_geometry = geometry_list[i] + (geometry_list[i+1] - geometry_list[i]) * delta_t
878
+ tmp_geom_list = [geometry_list[i], tmp_geometry, geometry_list[i+1]]
879
+ idpp_instance = IDPP()
880
+ tmp_geom_list = idpp_instance.opt_path(tmp_geom_list, element_list)
881
+ geometry_list[i] = tmp_geom_list[1]
882
+ return geometry_list
883
+
884
+