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,236 @@
1
+
2
+ import numpy as np
3
+
4
+ from multioptpy.Utils.bond_connectivity import BondConnectivity
5
+ from multioptpy.Parameters.parameter import UnitValueLib, covalent_radii_lib
6
+ from multioptpy.Utils.calc_tools import Calculationtools
7
+ from multioptpy.ModelHessian.calc_params import stretch2, bend2, torsion2
8
+
9
+ class FischerApproxHessian:
10
+ def __init__(self):
11
+ """
12
+ Fischer's Model Hessian implementation
13
+ Ref: Fischer and Almlöf, J. Phys. Chem., 1992, 96, 24, 9768–9774
14
+ """
15
+ self.bohr2angstroms = UnitValueLib().bohr2angstroms
16
+ self.hartree2kcalmol = UnitValueLib().hartree2kcalmol
17
+ self.bond_factor = 1.3 # Bond detection threshold factor
18
+
19
+ def calc_bond_force_const(self, r_ab, r_ab_cov):
20
+ """Calculate force constant for bond stretching"""
21
+ return 0.3601 * np.exp(-1.944 * (r_ab - r_ab_cov))
22
+
23
+ def calc_bend_force_const(self, r_ab, r_ac, r_ab_cov, r_ac_cov):
24
+ """Calculate force constant for angle bending"""
25
+ val = r_ab_cov * r_ac_cov
26
+ if abs(val) < 1.0e-10:
27
+ return 0.0 # Avoid division by zero
28
+
29
+ return 0.089 + 0.11 / (val) ** (-0.42) * np.exp(
30
+ -0.44 * (r_ab + r_ac - r_ab_cov - r_ac_cov)
31
+ )
32
+
33
+ def calc_dihedral_force_const(self, r_ab, r_ab_cov, bond_sum):
34
+ """Calculate force constant for dihedral torsion"""
35
+ val = r_ab * r_ab_cov
36
+ if abs(val) < 1.0e-10:
37
+ return 0.0 # Avoid division by zero
38
+ return 0.0015 + 14.0 * max(bond_sum, 0) ** 0.57 / (val) ** 4.0 * np.exp(
39
+ -2.85 * (r_ab - r_ab_cov)
40
+ )
41
+
42
+ def get_bond_connectivity(self, coord, element_list):
43
+ """
44
+ Calculate bond connectivity matrix and related data
45
+ Returns:
46
+ bond_mat: Bond connectivity matrix
47
+ dist_mat: Distance matrix between atoms
48
+ pair_cov_radii_mat: Matrix of covalent radii sums
49
+ """
50
+ n_atoms = len(coord)
51
+ dist_mat = np.zeros((n_atoms, n_atoms))
52
+ pair_cov_radii_mat = np.zeros((n_atoms, n_atoms))
53
+
54
+ for i in range(n_atoms):
55
+ for j in range(i+1, n_atoms):
56
+ dist = np.linalg.norm(coord[i] - coord[j])
57
+ dist_mat[i, j] = dist_mat[j, i] = dist
58
+
59
+ cov_sum = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
60
+ pair_cov_radii_mat[i, j] = pair_cov_radii_mat[j, i] = cov_sum
61
+
62
+ # Bond connectivity matrix (True if bond exists between atoms)
63
+ bond_mat = dist_mat <= (pair_cov_radii_mat * self.bond_factor)
64
+ np.fill_diagonal(bond_mat, False) # No self-bonds
65
+
66
+ return bond_mat, dist_mat, pair_cov_radii_mat
67
+
68
+ def count_bonds_for_dihedral(self, bond_mat, central_atoms):
69
+ """Count bonds connected to the central atoms of a dihedral"""
70
+ a, b = central_atoms
71
+ # Sum bonds for both central atoms and subtract 2 (the bond between them is counted twice)
72
+ bond_sum = bond_mat[a].sum() + bond_mat[b].sum() - 2
73
+ return bond_sum
74
+
75
+ def fischer_bond(self, coord, element_list):
76
+ """Calculate Hessian components for bond stretching"""
77
+ BC = BondConnectivity()
78
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
79
+ bond_indices = BC.bond_connect_table(b_c_mat)
80
+
81
+ for idx in bond_indices:
82
+ i, j = idx
83
+ r_ij = np.linalg.norm(coord[i] - coord[j])
84
+ r_ij_cov = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
85
+
86
+ # Calculate force constant using Fischer's formula
87
+ force_const = self.calc_bond_force_const(r_ij, r_ij_cov)
88
+
89
+ # Convert to Cartesian coordinates
90
+ t_xyz = np.array([coord[i], coord[j]])
91
+ r, b_vec = stretch2(t_xyz)
92
+
93
+ for n in range(3):
94
+ for m in range(3):
95
+ self.cart_hess[3*i+n, 3*i+m] += force_const * b_vec[0][n] * b_vec[0][m]
96
+ self.cart_hess[3*j+n, 3*j+m] += force_const * b_vec[1][n] * b_vec[1][m]
97
+ self.cart_hess[3*i+n, 3*j+m] += force_const * b_vec[0][n] * b_vec[1][m]
98
+ self.cart_hess[3*j+n, 3*i+m] += force_const * b_vec[1][n] * b_vec[0][m]
99
+
100
+ def fischer_angle(self, coord, element_list):
101
+ """Calculate Hessian components for angle bending"""
102
+ BC = BondConnectivity()
103
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
104
+ angle_indices = BC.angle_connect_table(b_c_mat)
105
+
106
+ for idx in angle_indices:
107
+ i, j, k = idx # i-j-k angle
108
+ r_ij = np.linalg.norm(coord[i] - coord[j])
109
+ r_jk = np.linalg.norm(coord[j] - coord[k])
110
+ r_ij_cov = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
111
+ r_jk_cov = covalent_radii_lib(element_list[j]) + covalent_radii_lib(element_list[k])
112
+
113
+ # Calculate force constant using Fischer's formula
114
+ force_const = self.calc_bend_force_const(r_ij, r_jk, r_ij_cov, r_jk_cov)
115
+
116
+ # Convert to Cartesian coordinates
117
+ t_xyz = np.array([coord[i], coord[j], coord[k]])
118
+ theta, b_vec = bend2(t_xyz)
119
+
120
+ for n in range(3):
121
+ for m in range(3):
122
+ self.cart_hess[3*i+n, 3*i+m] += force_const * b_vec[0][n] * b_vec[0][m]
123
+ self.cart_hess[3*j+n, 3*j+m] += force_const * b_vec[1][n] * b_vec[1][m]
124
+ self.cart_hess[3*k+n, 3*k+m] += force_const * b_vec[2][n] * b_vec[2][m]
125
+
126
+ self.cart_hess[3*i+n, 3*j+m] += force_const * b_vec[0][n] * b_vec[1][m]
127
+ self.cart_hess[3*i+n, 3*k+m] += force_const * b_vec[0][n] * b_vec[2][m]
128
+ self.cart_hess[3*j+n, 3*i+m] += force_const * b_vec[1][n] * b_vec[0][m]
129
+ self.cart_hess[3*j+n, 3*k+m] += force_const * b_vec[1][n] * b_vec[2][m]
130
+ self.cart_hess[3*k+n, 3*i+m] += force_const * b_vec[2][n] * b_vec[0][m]
131
+ self.cart_hess[3*k+n, 3*j+m] += force_const * b_vec[2][n] * b_vec[1][m]
132
+
133
+ def fischer_dihedral(self, coord, element_list, bond_mat):
134
+ """Calculate Hessian components for dihedral torsions with linearity check"""
135
+ BC = BondConnectivity()
136
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
137
+ dihedral_indices = BC.dihedral_angle_connect_table(b_c_mat)
138
+
139
+ # Helper to calculate sin^2 of bond angle
140
+ def get_sin_sq_angle(idx1, idx2, idx3):
141
+ v1 = coord[idx1] - coord[idx2]
142
+ v2 = coord[idx3] - coord[idx2]
143
+ # Cross product magnitude squared
144
+ cross_prod = np.cross(v1, v2)
145
+ cross_sq = np.dot(cross_prod, cross_prod)
146
+ # Squared norms
147
+ n1_sq = np.dot(v1, v1)
148
+ n2_sq = np.dot(v2, v2)
149
+ if n1_sq * n2_sq < 1e-12:
150
+ return 0.0
151
+ return cross_sq / (n1_sq * n2_sq)
152
+
153
+ for idx in dihedral_indices:
154
+ # i-j-k-l dihedral
155
+ i, j, k, l = idx
156
+
157
+ # --- Linearity Check ---
158
+ # Check bond angles I-J-K and J-K-L.
159
+ # If atoms are linear, the torsion definition is singular (B-matrix blows up).
160
+ # We use sin^2(theta) because it avoids acos and is efficient.
161
+ sin_sq_ijk = get_sin_sq_angle(i, j, k)
162
+ sin_sq_jkl = get_sin_sq_angle(j, k, l)
163
+
164
+ # Threshold: if sin(theta) < 0.17 (approx 10 degrees from linear), dampen or skip.
165
+ # Using sin^2 < 0.03 as cutoff.
166
+ # The Wilson B-matrix scales as 1/sin(theta).
167
+ # If we are too close to linear, we must skip to avoid numerical explosion.
168
+ if sin_sq_ijk < 1.0e-3 or sin_sq_jkl < 1.0e-3:
169
+ continue
170
+ # -----------------------
171
+
172
+ # Central bond in dihedral (j-k)
173
+ r_jk = np.linalg.norm(coord[j] - coord[k])
174
+ r_jk_cov = covalent_radii_lib(element_list[j]) + covalent_radii_lib(element_list[k])
175
+
176
+ # Count bonds to central atoms
177
+ bond_sum = self.count_bonds_for_dihedral(bond_mat, (j, k))
178
+
179
+ # Calculate force constant
180
+ force_const = self.calc_dihedral_force_const(r_jk, r_jk_cov, bond_sum)
181
+
182
+ # Optional: Additional damping can be applied here if desired,
183
+ # but the skip above is the most robust fix for the singularity.
184
+
185
+ # Convert to Cartesian coordinates
186
+ t_xyz = np.array([coord[i], coord[j], coord[k], coord[l]])
187
+
188
+ # Note: torsion2 might still be unstable if not skipped
189
+ try:
190
+ tau, b_vec = torsion2(t_xyz)
191
+ except Exception:
192
+ # Fallback if torsion2 fails numerically
193
+ continue
194
+
195
+ # Iterate over all pairs of atoms in the dihedral
196
+ atom_indices = [i, j, k, l]
197
+
198
+ for a in range(4):
199
+ for b in range(4):
200
+ atom_a = atom_indices[a]
201
+ atom_b = atom_indices[b]
202
+
203
+ vec_a = b_vec[a]
204
+ vec_b = b_vec[b]
205
+
206
+ # Update the 3x3 Hessian block
207
+ for n in range(3):
208
+ for m in range(3):
209
+ val = force_const * vec_a[n] * vec_b[m]
210
+ self.cart_hess[3*atom_a+n, 3*atom_b+m] += val
211
+
212
+ def main(self, coord, element_list, cart_gradient):
213
+ """Main function to generate Fischer's approximate Hessian"""
214
+ print("Generating Fischer's approximate hessian...")
215
+
216
+ # Initialize Hessian matrix
217
+ n_atoms = len(coord)
218
+ self.cart_hess = np.zeros((n_atoms*3, n_atoms*3), dtype="float64")
219
+
220
+ # Get bond connectivity and distance information
221
+ bond_mat, dist_mat, pair_cov_radii_mat = self.get_bond_connectivity(coord, element_list)
222
+
223
+ # Calculate Hessian components
224
+ self.fischer_bond(coord, element_list)
225
+ self.fischer_angle(coord, element_list)
226
+ self.fischer_dihedral(coord, element_list, bond_mat)
227
+
228
+ # Symmetrize the Hessian
229
+ for i in range(n_atoms*3):
230
+ for j in range(i):
231
+ self.cart_hess[i, j] = self.cart_hess[j, i]
232
+
233
+ # Project out translational and rotational modes
234
+ hess_proj = Calculationtools().project_out_hess_tr_and_rot_for_coord(self.cart_hess, element_list, coord)
235
+
236
+ return hess_proj
@@ -0,0 +1,360 @@
1
+ import numpy as np
2
+ from scipy.spatial.distance import cdist # Highly recommended for vectorized distance calculation
3
+ from multioptpy.Parameters.parameter import UnitValueLib, covalent_radii_lib
4
+ from multioptpy.Utils.calc_tools import Calculationtools
5
+ from multioptpy.Parameters.parameter import D3Parameters, D2_C6_coeff_lib, D2_VDW_radii_lib
6
+ from multioptpy.Utils.bond_connectivity import BondConnectivity
7
+ from multioptpy.ModelHessian.calc_params import torsion2, stretch2, bend2
8
+
9
+
10
+ class FischerD3ApproxHessian:
11
+ def __init__(self):
12
+ """
13
+ Fischer's Model Hessian implementation with D3 dispersion correction
14
+ Ref: Fischer and Almlöf, J. Phys. Chem., 1992, 96, 24, 9768–9774
15
+ Implementation Ref.: pysisyphus.optimizers.guess_hessians
16
+ """
17
+ self.bohr2angstroms = UnitValueLib().bohr2angstroms
18
+ self.hartree2kcalmol = UnitValueLib().hartree2kcalmol
19
+ self.bond_factor = 1.3 # Bond detection threshold factor
20
+
21
+ # D3 dispersion correction parameters (default: PBE0)
22
+ self.d3_params = D3Parameters()
23
+ self.cart_hess = None
24
+
25
+ def calc_bond_force_const(self, r_ab, r_ab_cov):
26
+ """Calculate force constant for bond stretching using Fischer formula"""
27
+ return 0.3601 * np.exp(-1.944 * (r_ab - r_ab_cov))
28
+
29
+ def calc_bend_force_const(self, r_ab, r_ac, r_ab_cov, r_ac_cov):
30
+ """Calculate force constant for angle bending"""
31
+ val = r_ab_cov * r_ac_cov
32
+ if abs(val) < 1.0e-10:
33
+ return 0.0
34
+
35
+ return 0.089 + 0.11 / (val) ** (-0.42) * np.exp(
36
+ -0.44 * (r_ab + r_ac - r_ab_cov - r_ac_cov)
37
+ )
38
+
39
+ def calc_dihedral_force_const(self, r_ab, r_ab_cov, bond_sum):
40
+ """Calculate force constant for dihedral torsion"""
41
+ val = r_ab * r_ab_cov
42
+ if abs(val) < 1.0e-10:
43
+ return 0.0
44
+ return 0.0015 + 14.0 * max(bond_sum, 0) ** 0.57 / (val) ** 4.0 * np.exp(
45
+ -2.85 * (r_ab - r_ab_cov)
46
+ )
47
+
48
+ def get_c6_coefficient(self, element_i, element_j):
49
+ """Get C6 coefficient based on D3 model (simplified)"""
50
+ c6_i = D2_C6_coeff_lib(element_i)
51
+ c6_j = D2_C6_coeff_lib(element_j)
52
+ c6_ij = np.sqrt(c6_i * c6_j)
53
+ return c6_ij
54
+
55
+ def get_c8_coefficient(self, element_i, element_j):
56
+ """Calculate C8 coefficient based on D3 model using reference r4r2 values"""
57
+ c6_ij = self.get_c6_coefficient(element_i, element_j)
58
+ r4r2_i = self.d3_params.get_r4r2(element_i)
59
+ r4r2_j = self.d3_params.get_r4r2(element_j)
60
+ c8_ij = 3.0 * c6_ij * np.sqrt(r4r2_i * r4r2_j)
61
+ return c8_ij
62
+
63
+ def get_r0_value(self, element_i, element_j):
64
+ """Calculate R0 value for D3 model (characteristic distance for atom pair)"""
65
+ try:
66
+ r_i = D2_VDW_radii_lib(element_i)
67
+ r_j = D2_VDW_radii_lib(element_j)
68
+ return r_i + r_j
69
+ except:
70
+ r_i = covalent_radii_lib(element_i) * 1.5
71
+ r_j = covalent_radii_lib(element_j) * 1.5
72
+ return r_i + r_j
73
+
74
+ def d3_damping_function(self, r_ij, r0, order=6):
75
+ """BJ (Becke-Johnson) damping function for D3"""
76
+ if order == 6:
77
+ a1, a2 = self.d3_params.a1, self.d3_params.a2
78
+ else:
79
+ a1, a2 = self.d3_params.a1, self.d3_params.a2 + 2.0
80
+
81
+ denominator = r_ij**order + (a1 * r0 + a2)**order
82
+ return r_ij**order / denominator
83
+
84
+ def d3_hessian_contribution(self, r_vec, r_ij, element_i, element_j):
85
+ """Calculate D3 dispersion contribution to Hessian"""
86
+ if r_ij < 0.1:
87
+ return np.zeros((3, 3))
88
+
89
+ c6_ij = self.get_c6_coefficient(element_i, element_j)
90
+ c8_ij = self.get_c8_coefficient(element_i, element_j)
91
+ r0 = self.get_r0_value(element_i, element_j)
92
+
93
+ f_damp6 = self.d3_damping_function(r_ij, r0, order=6)
94
+ f_damp8 = self.d3_damping_function(r_ij, r0, order=8)
95
+
96
+ # Derivatives of damping functions
97
+ a1, a2 = self.d3_params.a1, self.d3_params.a2
98
+ a1_8, a2_8 = self.d3_params.a1, self.d3_params.a2 + 2.0
99
+
100
+ denom6 = r_ij**6 + (a1 * r0 + a2)**6
101
+ denom8 = r_ij**8 + (a1_8 * r0 + a2_8)**8
102
+
103
+ # df_damp/dr
104
+ df_damp6 = 6 * r_ij**5 / denom6 - 6 * r_ij**12 / denom6**2
105
+ df_damp8 = 8 * r_ij**7 / denom8 - 8 * r_ij**16 / denom8**2
106
+
107
+ # dE/dr (Gradient magnitude)
108
+ g6 = -self.d3_params.s6 * c6_ij * ((-6.0 / r_ij**7) * f_damp6 + (1.0 / r_ij**6) * df_damp6)
109
+ g8 = -self.d3_params.s8 * c8_ij * ((-8.0 / r_ij**9) * f_damp8 + (1.0 / r_ij**8) * df_damp8)
110
+
111
+ # Unit vector and projection operator
112
+ unit_vec = r_vec / r_ij
113
+ proj_op = np.outer(unit_vec, unit_vec) # P = r_hat * r_hat^T
114
+
115
+ # Coefficients for H = (d^2E/dr^2) * P + (1/r * dE/dr) * (I - P)
116
+ # Using the simplified structure from the original code for d^2E/dr^2 approximation:
117
+ h6_proj_coeff = self.d3_params.s6 * c6_ij / r_ij**8 * (42.0 * f_damp6 - r_ij * df_damp6)
118
+ h8_proj_coeff = self.d3_params.s8 * c8_ij / r_ij**10 * (72.0 * f_damp8 - r_ij * df_damp8)
119
+
120
+ h_proj = h6_proj_coeff + h8_proj_coeff # d^2E/dr^2 approximation
121
+ h_perp = (g6 + g8) / r_ij # 1/r * dE/dr (Perpendicular coefficient)
122
+
123
+ # Construct Hessian matrix
124
+ identity = np.eye(3)
125
+ hessian = h_proj * proj_op + h_perp * (identity - proj_op)
126
+
127
+ return hessian
128
+
129
+ # --- Optimized: Vectorized connectivity calculation ---
130
+ def get_bond_connectivity(self, coord, element_list):
131
+ """Calculate bond connectivity matrix and related data (Optimized with vectorization)"""
132
+ n_atoms = len(coord)
133
+
134
+ # 1. Distance matrix (Vectorized)
135
+ try:
136
+ dist_mat = cdist(coord, coord)
137
+ except NameError:
138
+ diff = coord[:, None, :] - coord[None, :, :]
139
+ dist_mat = np.linalg.norm(diff, axis=-1)
140
+
141
+ # 2. Covalent radii sums (Vectorized)
142
+ cov_radii = np.array([covalent_radii_lib(e) for e in element_list])
143
+ pair_cov_radii_mat = cov_radii[:, None] + cov_radii[None, :]
144
+
145
+ # 3. Bond connectivity matrix
146
+ bond_mat = dist_mat <= (pair_cov_radii_mat * self.bond_factor)
147
+ np.fill_diagonal(bond_mat, False)
148
+
149
+ return bond_mat, dist_mat, pair_cov_radii_mat
150
+
151
+ # --- Optimized: Block assignment using slicing ---
152
+ def fischer_bond(self, coord, element_list):
153
+ """Calculate Hessian components for bond stretching (Optimized with slicing)"""
154
+ BC = BondConnectivity()
155
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
156
+ bond_indices = BC.bond_connect_table(b_c_mat)
157
+
158
+ for idx in bond_indices:
159
+ i, j = idx
160
+ r_ij = np.linalg.norm(coord[i] - coord[j])
161
+ r_ij_cov = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
162
+
163
+ force_const = self.calc_bond_force_const(r_ij, r_ij_cov)
164
+
165
+ t_xyz = np.array([coord[i], coord[j]])
166
+ r, b_vec = stretch2(t_xyz)
167
+
168
+ # Optimized: Use NumPy slicing and outer product
169
+ b_vec_i, b_vec_j = b_vec[0], b_vec[1]
170
+
171
+ H_ii_block = force_const * np.outer(b_vec_i, b_vec_i)
172
+ H_jj_block = force_const * np.outer(b_vec_j, b_vec_j)
173
+ H_ij_block = force_const * np.outer(b_vec_i, b_vec_j)
174
+ H_ji_block = force_const * np.outer(b_vec_j, b_vec_i)
175
+
176
+ start_i, end_i = 3 * i, 3 * i + 3
177
+ start_j, end_j = 3 * j, 3 * j + 3
178
+
179
+ self.cart_hess[start_i:end_i, start_i:end_i] += H_ii_block
180
+ self.cart_hess[start_j:end_j, start_j:end_j] += H_jj_block
181
+ self.cart_hess[start_i:end_i, start_j:end_j] += H_ij_block
182
+ self.cart_hess[start_j:end_j, start_i:end_i] += H_ji_block
183
+
184
+
185
+ def fischer_angle(self, coord, element_list):
186
+ """Calculate Hessian components for angle bending (Optimized with slicing)"""
187
+ BC = BondConnectivity()
188
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
189
+ angle_indices = BC.angle_connect_table(b_c_mat)
190
+
191
+ for idx in angle_indices:
192
+ i, j, k = idx # i-j-k angle
193
+ r_ij = np.linalg.norm(coord[i] - coord[j])
194
+ r_jk = np.linalg.norm(coord[j] - coord[k])
195
+ r_ij_cov = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
196
+ r_jk_cov = covalent_radii_lib(element_list[j]) + covalent_radii_lib(element_list[k])
197
+
198
+ force_const = self.calc_bend_force_const(r_ij, r_jk, r_ij_cov, r_jk_cov)
199
+
200
+ t_xyz = np.array([coord[i], coord[j], coord[k]])
201
+ theta, b_vec = bend2(t_xyz)
202
+
203
+ # Optimized: Use NumPy slicing and outer product
204
+ atoms = [i, j, k]
205
+
206
+ for m_idx, m_atom in enumerate(atoms):
207
+ for n_idx, n_atom in enumerate(atoms):
208
+ start_m, end_m = 3 * m_atom, 3 * m_atom + 3
209
+ start_n, end_n = 3 * n_atom, 3 * n_atom + 3
210
+
211
+ H_mn_block = force_const * np.outer(b_vec[m_idx], b_vec[n_idx])
212
+ self.cart_hess[start_m:end_m, start_n:end_n] += H_mn_block
213
+
214
+
215
+ def fischer_dihedral(self, coord, element_list, bond_mat):
216
+ """Calculate Hessian components for dihedral torsion (Optimized with singularity damping)"""
217
+ BC = BondConnectivity()
218
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
219
+ dihedral_indices = BC.dihedral_angle_connect_table(b_c_mat)
220
+
221
+ # Calculate bond count for central atoms in dihedrals
222
+ tors_atom_bonds = {}
223
+ for idx in dihedral_indices:
224
+ i, j, k, l = idx # i-j-k-l dihedral
225
+ bond_sum = bond_mat[j].sum() + bond_mat[k].sum() - 2
226
+ tors_atom_bonds[(j, k)] = bond_sum
227
+
228
+ for idx in dihedral_indices:
229
+ i, j, k, l = idx
230
+
231
+ # Vector calculations
232
+ vec_ji = coord[i] - coord[j]
233
+ vec_jk = coord[k] - coord[j]
234
+ vec_kl = coord[l] - coord[k]
235
+
236
+ r_jk = np.linalg.norm(vec_jk)
237
+ r_jk_cov = covalent_radii_lib(element_list[j]) + covalent_radii_lib(element_list[k])
238
+
239
+ bond_sum = tors_atom_bonds.get((j, k), 0)
240
+
241
+ # Calculate base force constant
242
+ force_const = self.calc_dihedral_force_const(r_jk, r_jk_cov, bond_sum)
243
+
244
+ # --- Singularity Handling (Damping for linear angles) ---
245
+ # Determine linearity of angles i-j-k and j-k-l
246
+
247
+ # Angle 1: i-j-k
248
+ n_ji = np.linalg.norm(vec_ji)
249
+ n_jk = r_jk # already calculated
250
+
251
+ # Avoid division by zero if atoms overlap
252
+ if n_ji < 1e-8 or n_jk < 1e-8: continue
253
+
254
+ cos_theta1 = np.dot(vec_ji, vec_jk) / (n_ji * n_jk)
255
+
256
+ # Angle 2: j-k-l (Note: vec_kj is -vec_jk)
257
+ vec_kj = -vec_jk
258
+ n_kl = np.linalg.norm(vec_kl)
259
+
260
+ if n_kl < 1e-8: continue
261
+
262
+ cos_theta2 = np.dot(vec_kj, vec_kl) / (n_jk * n_kl)
263
+
264
+ # --- Damping Factor Calculation ---
265
+ # The Wilson B-matrix contains 1/sin(theta) terms which diverge at 180 deg.
266
+ # We scale the force constant by sin^2(theta) to cancel this divergence.
267
+ # sin^2(theta) = 1 - cos^2(theta)
268
+
269
+ sin2_theta1 = 1.0 - min(cos_theta1**2, 1.0)
270
+ sin2_theta2 = 1.0 - min(cos_theta2**2, 1.0)
271
+
272
+ # Hard cutoff: If geometry is extremely linear, skip to avoid NaN
273
+ if sin2_theta1 < 1e-4 or sin2_theta2 < 1e-4:
274
+ continue
275
+
276
+ # Apply scaling factor
277
+ # This ensures the force constant goes to 0 as the angle becomes linear
278
+ scaling_factor = sin2_theta1 * sin2_theta2
279
+ force_const *= scaling_factor
280
+
281
+ # --------------------------------------------------------
282
+
283
+ t_xyz = np.array([coord[i], coord[j], coord[k], coord[l]])
284
+
285
+ try:
286
+ tau, b_vec = torsion2(t_xyz)
287
+ except (ValueError, ArithmeticError):
288
+ # Skip if numerical errors occur in torsion calculation
289
+ continue
290
+
291
+ # Optimized: Use NumPy slicing and outer product
292
+ atoms = [i, j, k, l]
293
+
294
+ for m_idx, m_atom in enumerate(atoms):
295
+ for n_idx, n_atom in enumerate(atoms):
296
+ start_m, end_m = 3 * m_atom, 3 * m_atom + 3
297
+ start_n, end_n = 3 * n_atom, 3 * n_atom + 3
298
+
299
+ H_mn_block = force_const * np.outer(b_vec[m_idx], b_vec[n_idx])
300
+ self.cart_hess[start_m:end_m, start_n:end_n] += H_mn_block
301
+
302
+ # --- Optimized: Block assignment using slicing (and fixed logic) ---
303
+ def d3_dispersion_hessian(self, coord, element_list, bond_mat):
304
+ """Calculate Hessian correction based on D3 dispersion forces (Optimized/Corrected)"""
305
+ n_atoms = len(coord)
306
+
307
+ # Calculate D3 dispersion correction for all atom pairs (i > j)
308
+ for i in range(n_atoms):
309
+ for j in range(i):
310
+ # Skip bonded atom pairs
311
+ if bond_mat[i, j]:
312
+ continue
313
+
314
+ r_vec = coord[i] - coord[j]
315
+ r_ij = np.linalg.norm(r_vec)
316
+
317
+ if r_ij < 0.1:
318
+ continue
319
+
320
+ # Calculate D3 Hessian contribution (3x3 block)
321
+ hess_block = self.d3_hessian_contribution(r_vec, r_ij, element_list[i], element_list[j])
322
+
323
+ # Use slicing for efficient block assignment
324
+ # H_ii += H_block, H_jj += H_block, H_ij -= H_block, H_ji -= H_block
325
+ start_i, end_i = 3 * i, 3 * i + 3
326
+ start_j, end_j = 3 * j, 3 * j + 3
327
+
328
+ self.cart_hess[start_i:end_i, start_i:end_i] += hess_block
329
+ self.cart_hess[start_j:end_j, start_j:end_j] += hess_block
330
+ self.cart_hess[start_i:end_i, start_j:end_j] -= hess_block
331
+ self.cart_hess[start_j:end_j, start_i:end_i] -= hess_block
332
+
333
+ # --- Optimized: Main function flow ---
334
+ def main(self, coord, element_list, cart_gradient):
335
+ """
336
+ Calculate Hessian combining Fischer model and D3 dispersion correction
337
+ """
338
+ print("Generating Hessian using Fischer model with D3 dispersion correction...")
339
+
340
+ n_atoms = len(coord)
341
+ self.cart_hess = np.zeros((n_atoms*3, n_atoms*3), dtype="float64")
342
+
343
+ # Calculate bond connectivity matrix ONCE (Optimized internally)
344
+ bond_mat, dist_mat, pair_cov_radii_mat = self.get_bond_connectivity(coord, element_list)
345
+
346
+ # Calculate Hessian components from Fischer model (Optimized internally with slicing)
347
+ self.fischer_bond(coord, element_list)
348
+ self.fischer_angle(coord, element_list)
349
+ self.fischer_dihedral(coord, element_list, bond_mat)
350
+
351
+ # Calculate Hessian components from D3 dispersion correction (Optimized internally with slicing)
352
+ self.d3_dispersion_hessian(coord, element_list, bond_mat)
353
+
354
+ # Optimized: Symmetrize the Hessian matrix
355
+ self.cart_hess = (self.cart_hess + self.cart_hess.T) / 2.0
356
+
357
+ # Project out rotational and translational modes
358
+ hess_proj = Calculationtools().project_out_hess_tr_and_rot_for_coord(self.cart_hess, element_list, coord)
359
+
360
+ return hess_proj