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,398 @@
1
+ import numpy as np
2
+ import itertools
3
+ from scipy.spatial.distance import cdist
4
+
5
+ from multioptpy.Parameters.parameter import UnitValueLib, covalent_radii_lib
6
+ from multioptpy.Utils.calc_tools import Calculationtools
7
+ from multioptpy.Parameters.parameter import D4Parameters
8
+ from multioptpy.Utils.bond_connectivity import BondConnectivity
9
+ from multioptpy.ModelHessian.calc_params import torsion2, stretch2, bend2
10
+
11
+ # Optimized Helper: JIT-friendly structure
12
+ def _fischer_block_assignment(cart_hess, indices, force_const, b_vecs):
13
+ """Helper function to assign force constant to Hessian blocks using slicing."""
14
+ n_indices = len(indices)
15
+ for a in range(n_indices):
16
+ m_atom = indices[a]
17
+ vec_a = b_vecs[a]
18
+ start_m, end_m = 3 * m_atom, 3 * m_atom + 3
19
+
20
+ for b in range(n_indices):
21
+ n_atom = indices[b]
22
+ vec_b = b_vecs[b]
23
+ start_n, end_n = 3 * n_atom, 3 * n_atom + 3
24
+
25
+ # Use outer product for speed
26
+ cart_hess[start_m:end_m, start_n:end_n] += force_const * np.outer(vec_a, vec_b)
27
+
28
+
29
+ class FischerD4ApproxHessian:
30
+ def __init__(self):
31
+ """
32
+ Fischer's Model Hessian implementation with D4 dispersion correction
33
+ Optimized for performance.
34
+ """
35
+ self.bohr2angstroms = UnitValueLib().bohr2angstroms
36
+ self.hartree2kcalmol = UnitValueLib().hartree2kcalmol
37
+ self.bond_factor = 1.3
38
+ self.d4_params = D4Parameters()
39
+ self.cart_hess = None
40
+
41
+ def calc_bond_force_const(self, r_ab, r_ab_cov):
42
+ return 0.3601 * np.exp(-1.944 * (r_ab - r_ab_cov))
43
+
44
+ def calc_bend_force_const(self, r_ab, r_ac, r_ab_cov, r_ac_cov):
45
+ val = r_ab_cov * r_ac_cov
46
+ if abs(val) < 1.0e-10: return 0.0
47
+ return 0.089 + 0.11 / (val) ** (-0.42) * np.exp(
48
+ -0.44 * (r_ab + r_ac - r_ab_cov - r_ac_cov)
49
+ )
50
+
51
+ def calc_dihedral_force_const(self, r_ab, r_ab_cov, bond_sum):
52
+ val = r_ab * r_ab_cov
53
+ if abs(val) < 1.0e-10: return 0.0
54
+ return 0.0015 + 14.0 * max(bond_sum, 0) ** 0.57 / (val) ** 4.0 * np.exp(
55
+ -2.85 * (r_ab - r_ab_cov)
56
+ )
57
+
58
+ # --- D4 Utility Methods ---
59
+ def get_bond_connectivity(self, coord, element_list):
60
+ """Calculate bond connectivity matrix (Vectorized)"""
61
+ dist_mat = cdist(coord, coord)
62
+
63
+ cov_radii = np.array([covalent_radii_lib(e) for e in element_list])
64
+ pair_cov_radii_mat = cov_radii[:, None] + cov_radii[None, :]
65
+
66
+ bond_mat = dist_mat <= (pair_cov_radii_mat * self.bond_factor)
67
+ np.fill_diagonal(bond_mat, False)
68
+
69
+ return bond_mat, dist_mat, pair_cov_radii_mat
70
+
71
+ def estimate_atomic_charges(self, element_list, bond_mat):
72
+ """Estimate atomic partial charges (Fully Vectorized)"""
73
+ n_atoms = len(element_list)
74
+ charges = np.zeros(n_atoms)
75
+ en_list = np.array([self.d4_params.get_electronegativity(e) for e in element_list])
76
+
77
+ i_indices, j_indices = np.where(np.triu(bond_mat, k=1))
78
+
79
+ if len(i_indices) > 0:
80
+ en_i = en_list[i_indices]
81
+ en_j = en_list[j_indices]
82
+
83
+ charge_transfer = 0.2 * (en_j - en_i) / (en_i + en_j)
84
+
85
+ np.add.at(charges, i_indices, charge_transfer)
86
+ np.add.at(charges, j_indices, -charge_transfer)
87
+
88
+ return charges
89
+
90
+ def get_charge_scaling_factor(self, element, charge):
91
+ return np.exp(-self.d4_params.ga * charge * charge)
92
+
93
+ def get_c6_coefficient(self, element_i, element_j, q_i=0.0, q_j=0.0):
94
+ alpha_i = self.d4_params.get_polarizability(element_i)
95
+ alpha_j = self.d4_params.get_polarizability(element_j)
96
+ scale_i = self.get_charge_scaling_factor(element_i, q_i)
97
+ scale_j = self.get_charge_scaling_factor(element_j, q_j)
98
+ denom = (alpha_i / scale_i) + (alpha_j / scale_j)
99
+ return 2.0 * alpha_i * alpha_j / denom * 0.75
100
+
101
+ def get_c8_coefficient(self, element_i, element_j, q_i=0.0, q_j=0.0):
102
+ c6_ij = self.get_c6_coefficient(element_i, element_j, q_i, q_j)
103
+ r4r2_i = self.d4_params.get_r4r2(element_i)
104
+ r4r2_j = self.d4_params.get_r4r2(element_j)
105
+ return 3.0 * c6_ij * np.sqrt(r4r2_i * r4r2_j)
106
+
107
+ def get_r0_value(self, element_i, element_j):
108
+ try:
109
+ return (covalent_radii_lib(element_i) + covalent_radii_lib(element_j)) * 4.0/3.0
110
+ except:
111
+ return (covalent_radii_lib(element_i) + covalent_radii_lib(element_j)) * 2.0
112
+
113
+ def d4_damping_function(self, r_ij, r0, order=6):
114
+ if order == 6:
115
+ a1, a2 = self.d4_params.a1, self.d4_params.a2
116
+ else:
117
+ a1, a2 = self.d4_params.a1, self.d4_params.a2 + 2.0
118
+
119
+ r_pow = r_ij**order
120
+ denominator = r_pow + (a1 * r0 + a2)**order
121
+ return r_pow / denominator
122
+
123
+ def three_body_damping(self, r_ij, r_jk, r_ki, r0_ij, r0_jk, r0_ki):
124
+ f_ij = self.d4_damping_function(r_ij, r0_ij, order=6)
125
+ f_jk = self.d4_damping_function(r_jk, r0_jk, order=6)
126
+ f_ki = self.d4_damping_function(r_ki, r0_ki, order=6)
127
+ return f_ij * f_jk * f_ki
128
+
129
+ # --- Fischer Term Calculations ---
130
+ def fischer_bond(self, coord, element_list):
131
+ BC = BondConnectivity()
132
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
133
+ bond_indices = BC.bond_connect_table(b_c_mat)
134
+
135
+ for idx in bond_indices:
136
+ i, j = idx
137
+ r_ij = np.linalg.norm(coord[i] - coord[j])
138
+ r_ij_cov = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
139
+ force_const = self.calc_bond_force_const(r_ij, r_ij_cov)
140
+ t_xyz = np.array([coord[i], coord[j]])
141
+ _, b_vec = stretch2(t_xyz)
142
+ _fischer_block_assignment(self.cart_hess, (i, j), force_const, b_vec)
143
+
144
+ def fischer_angle(self, coord, element_list):
145
+ BC = BondConnectivity()
146
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
147
+ angle_indices = BC.angle_connect_table(b_c_mat)
148
+
149
+ for idx in angle_indices:
150
+ i, j, k = idx
151
+ r_ij = np.linalg.norm(coord[i] - coord[j])
152
+ r_jk = np.linalg.norm(coord[j] - coord[k])
153
+ r_ij_cov = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
154
+ r_jk_cov = covalent_radii_lib(element_list[j]) + covalent_radii_lib(element_list[k])
155
+ force_const = self.calc_bend_force_const(r_ij, r_jk, r_ij_cov, r_jk_cov)
156
+ t_xyz = np.array([coord[i], coord[j], coord[k]])
157
+ _, b_vec = bend2(t_xyz)
158
+ _fischer_block_assignment(self.cart_hess, (i, j, k), force_const, b_vec)
159
+
160
+ def fischer_dihedral(self, coord, element_list, bond_mat):
161
+ """Optimized dihedral calculation with linearity check."""
162
+ BC = BondConnectivity()
163
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
164
+ dihedral_indices = BC.dihedral_angle_connect_table(b_c_mat)
165
+
166
+ bond_counts = bond_mat.sum(axis=1)
167
+
168
+ def get_sin_sq_angle(idx1, idx2, idx3):
169
+ v1 = coord[idx1] - coord[idx2]
170
+ v2 = coord[idx3] - coord[idx2]
171
+ cross_prod = np.cross(v1, v2)
172
+ cross_sq = np.dot(cross_prod, cross_prod)
173
+ n1_sq = np.dot(v1, v1)
174
+ n2_sq = np.dot(v2, v2)
175
+ if n1_sq * n2_sq < 1e-12: return 0.0
176
+ return cross_sq / (n1_sq * n2_sq)
177
+
178
+ for idx in dihedral_indices:
179
+ i, j, k, l = idx
180
+
181
+ if get_sin_sq_angle(i, j, k) < 1.0e-3 or get_sin_sq_angle(j, k, l) < 1.0e-3:
182
+ continue
183
+
184
+ r_jk = np.linalg.norm(coord[j] - coord[k])
185
+ r_jk_cov = covalent_radii_lib(element_list[j]) + covalent_radii_lib(element_list[k])
186
+ bond_sum = bond_counts[j] + bond_counts[k] - 2
187
+
188
+ force_const = self.calc_dihedral_force_const(r_jk, r_jk_cov, bond_sum)
189
+ t_xyz = np.array([coord[i], coord[j], coord[k], coord[l]])
190
+
191
+ try:
192
+ _, b_vec = torsion2(t_xyz)
193
+ _fischer_block_assignment(self.cart_hess, (i, j, k, l), force_const, b_vec)
194
+ except Exception:
195
+ continue
196
+
197
+ # --- Optimized D4 Methods ---
198
+
199
+ def _precompute_active_triplets(self, coord, cutoff=20.0):
200
+ """Identify triplets within cutoff distance ONCE."""
201
+ n_atoms = len(coord)
202
+ cutoff_sq = cutoff**2
203
+ active_triplets = []
204
+
205
+ neighbors = {i: [] for i in range(n_atoms)}
206
+ for i in range(n_atoms):
207
+ for j in range(i + 1, n_atoms):
208
+ d2 = np.sum((coord[i] - coord[j])**2)
209
+ if d2 < cutoff_sq:
210
+ neighbors[i].append(j)
211
+ neighbors[j].append(i)
212
+
213
+ for i in range(n_atoms):
214
+ i_neighbors = set(neighbors[i])
215
+ for j in neighbors[i]:
216
+ if j <= i: continue
217
+
218
+ j_neighbors = neighbors[j]
219
+ for k in j_neighbors:
220
+ if k <= j: continue
221
+ if k in i_neighbors:
222
+ active_triplets.append((i, j, k))
223
+
224
+ return active_triplets
225
+
226
+ def d4_three_body_gradient_optimized(self, coord, element_list, charges, active_triplets):
227
+ """Calculate gradient using pre-computed active triplets."""
228
+ n_atoms = len(coord)
229
+ gradients = np.zeros((n_atoms, 3))
230
+
231
+ for i, j, k in active_triplets:
232
+ r_vec_ij = coord[j] - coord[i]
233
+ r_vec_jk = coord[k] - coord[j]
234
+ r_vec_ki = coord[i] - coord[k]
235
+
236
+ d2_ij = np.dot(r_vec_ij, r_vec_ij)
237
+ d2_jk = np.dot(r_vec_jk, r_vec_jk)
238
+ d2_ki = np.dot(r_vec_ki, r_vec_ki)
239
+
240
+ r_ij = np.sqrt(d2_ij)
241
+ r_jk = np.sqrt(d2_jk)
242
+ r_ki = np.sqrt(d2_ki)
243
+
244
+ if min(r_ij, r_jk, r_ki) < 0.1: continue
245
+
246
+ u_ij = r_vec_ij / r_ij
247
+ u_jk = r_vec_jk / r_jk
248
+ u_ki = r_vec_ki / r_ki
249
+
250
+ c6_ij = self.get_c6_coefficient(element_list[i], element_list[j], charges[i], charges[j])
251
+ c6_jk = self.get_c6_coefficient(element_list[j], element_list[k], charges[j], charges[k])
252
+ c6_ki = self.get_c6_coefficient(element_list[k], element_list[i], charges[k], charges[i])
253
+ c9_ijk = np.sqrt(c6_ij * c6_jk * c6_ki)
254
+
255
+ cos_i = np.dot(-u_ki, u_ij)
256
+ cos_j = np.dot(-u_ij, u_jk)
257
+ cos_k = np.dot(-u_jk, u_ki)
258
+
259
+ angle_factor = 1.0 + 3.0 * cos_i * cos_j * cos_k
260
+
261
+ r0_ij = self.get_r0_value(element_list[i], element_list[j])
262
+ r0_jk = self.get_r0_value(element_list[j], element_list[k])
263
+ r0_ki = self.get_r0_value(element_list[k], element_list[i])
264
+ damp_factor = self.three_body_damping(r_ij, r_jk, r_ki, r0_ij, r0_jk, r0_ki)
265
+
266
+ pre_factor = self.d4_params.s9 * c9_ijk * damp_factor * angle_factor
267
+
268
+ denom = (r_ij * r_jk * r_ki)**3
269
+ g_base = -3.0 * pre_factor / denom
270
+
271
+ gradients[i] += g_base * (u_ij / r_ij - u_ki / r_ki)
272
+ gradients[j] += g_base * (u_jk / r_jk - u_ij / r_ij)
273
+ gradients[k] += g_base * (u_ki / r_ki - u_jk / r_jk)
274
+
275
+ g_ang_coeff = 3.0 * pre_factor / denom
276
+
277
+ g_ang_i = g_ang_coeff * cos_j * cos_k * (-u_ij - u_ki)
278
+ g_ang_j = g_ang_coeff * cos_i * cos_k * (-u_jk - u_ij)
279
+ g_ang_k = g_ang_coeff * cos_i * cos_j * (-u_ki - u_jk)
280
+
281
+ gradients[i] += g_ang_i
282
+ gradients[j] += g_ang_j
283
+ gradients[k] += g_ang_k
284
+
285
+ return gradients
286
+
287
+ def d4_three_body_hessian(self, coord, element_list, charges):
288
+ """Calculate Hessian from three-body term using optimized finite difference."""
289
+ n_atoms = len(coord)
290
+ hessian = np.zeros((3*n_atoms, 3*n_atoms))
291
+
292
+ active_triplets = self._precompute_active_triplets(coord, cutoff=20.0)
293
+
294
+ if not active_triplets:
295
+ return hessian
296
+
297
+ base_gradients = self.d4_three_body_gradient_optimized(coord, element_list, charges, active_triplets)
298
+
299
+ delta = 1e-5
300
+
301
+ for i in range(n_atoms):
302
+ for j in range(3):
303
+ coord_plus = np.copy(coord)
304
+ coord_plus[i, j] += delta
305
+
306
+ grad_plus = self.d4_three_body_gradient_optimized(coord_plus, element_list, charges, active_triplets)
307
+
308
+ row_start = 3 * i + j
309
+ row_block = (grad_plus - base_gradients).flatten() / delta
310
+
311
+ hessian[row_start, :] = row_block
312
+
313
+ return (hessian + hessian.T) / 2.0
314
+
315
+ # --- Re-added Missing Method ---
316
+ def d4_hessian_contribution(self, r_vec, r_ij, element_i, element_j, q_i, q_j):
317
+ """Calculate D4 dispersion contribution to Hessian for a pair of atoms."""
318
+ c6_ij = self.get_c6_coefficient(element_i, element_j, q_i, q_j)
319
+ c8_ij = self.get_c8_coefficient(element_i, element_j, q_i, q_j)
320
+ r0 = self.get_r0_value(element_i, element_j)
321
+
322
+ f_damp6 = self.d4_damping_function(r_ij, r0, order=6)
323
+ f_damp8 = self.d4_damping_function(r_ij, r0, order=8)
324
+
325
+ a1, a2 = self.d4_params.a1, self.d4_params.a2
326
+ a1_8, a2_8 = self.d4_params.a1, self.d4_params.a2 + 2.0
327
+
328
+ denom6 = r_ij**6 + (a1 * r0 + a2)**6
329
+ denom8 = r_ij**8 + (a1_8 * r0 + a2_8)**8
330
+
331
+ df_damp6 = 6 * r_ij**5 / denom6 - 6 * r_ij**12 / denom6**2
332
+ df_damp8 = 8 * r_ij**7 / denom8 - 8 * r_ij**16 / denom8**2
333
+
334
+ g6 = -self.d4_params.s6 * c6_ij * ((-6.0 / r_ij**7) * f_damp6 + (1.0 / r_ij**6) * df_damp6)
335
+ g8 = -self.d4_params.s8 * c8_ij * ((-8.0 / r_ij**9) * f_damp8 + (1.0 / r_ij**8) * df_damp8)
336
+
337
+ h6_proj_approx = self.d4_params.s6 * c6_ij / r_ij**8 * (42.0 * f_damp6 - r_ij * df_damp6)
338
+ h8_proj_approx = self.d4_params.s8 * c8_ij / r_ij**10 * (72.0 * f_damp8 - r_ij * df_damp8)
339
+
340
+ h_proj = h6_proj_approx + h8_proj_approx
341
+ h_perp = (g6 + g8) / r_ij
342
+
343
+ unit_vec = r_vec / r_ij
344
+ proj_op = np.outer(unit_vec, unit_vec)
345
+
346
+ identity = np.eye(3)
347
+ return h_proj * proj_op + h_perp * (identity - proj_op)
348
+
349
+ def d4_dispersion_hessian(self, coord, element_list, bond_mat, dist_mat):
350
+ """Optimized Pairwise D4 Hessian"""
351
+ n_atoms = len(coord)
352
+ charges = self.estimate_atomic_charges(element_list, bond_mat)
353
+
354
+ for i in range(n_atoms):
355
+ el_i = element_list[i]
356
+ q_i = charges[i]
357
+
358
+ for j in range(i):
359
+ if bond_mat[i, j]: continue
360
+ r_ij = dist_mat[i, j]
361
+ if r_ij < 0.1: continue
362
+
363
+ el_j = element_list[j]
364
+ q_j = charges[j]
365
+ r_vec = coord[i] - coord[j]
366
+
367
+ hess_block = self.d4_hessian_contribution(r_vec, r_ij, el_i, el_j, q_i, q_j)
368
+
369
+ si, ei = 3 * i, 3 * i + 3
370
+ sj, ej = 3 * j, 3 * j + 3
371
+
372
+ self.cart_hess[si:ei, si:ei] += hess_block
373
+ self.cart_hess[sj:ej, sj:ej] += hess_block
374
+ self.cart_hess[si:ei, sj:ej] -= hess_block
375
+ self.cart_hess[sj:ej, si:ei] -= hess_block
376
+
377
+ three_body_hess = self.d4_three_body_hessian(coord, element_list, charges)
378
+ self.cart_hess += three_body_hess
379
+
380
+ def main(self, coord, element_list, cart_gradient):
381
+ print("Generating Hessian using Optimized Fischer + D4 model...")
382
+
383
+ n_atoms = len(coord)
384
+ self.cart_hess = np.zeros((n_atoms*3, n_atoms*3), dtype="float64")
385
+
386
+ bond_mat, dist_mat, pair_cov_radii_mat = self.get_bond_connectivity(coord, element_list)
387
+
388
+ self.fischer_bond(coord, element_list)
389
+ self.fischer_angle(coord, element_list)
390
+ self.fischer_dihedral(coord, element_list, bond_mat)
391
+
392
+ self.d4_dispersion_hessian(coord, element_list, bond_mat, dist_mat)
393
+
394
+ self.cart_hess = (self.cart_hess + self.cart_hess.T) / 2.0
395
+
396
+ hess_proj = Calculationtools().project_out_hess_tr_and_rot_for_coord(self.cart_hess, element_list, coord)
397
+
398
+ return hess_proj