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,822 @@
1
+ import numpy as np
2
+
3
+ from multioptpy.Parameters.parameter import UnitValueLib, covalent_radii_lib, element_number
4
+ from multioptpy.Utils.calc_tools import Calculationtools
5
+ from multioptpy.ModelHessian.calc_params import torsion2, outofplane2
6
+ from multioptpy.Parameters.parameter import D2_C6_coeff_lib, UFF_VDW_distance_lib, D3Parameters
7
+
8
+
9
+ class Lindh2007D3ApproxHessian:
10
+ """
11
+ Lindh's Model Hessian (2007) augmented with D3 dispersion correction.
12
+
13
+ This class implements Lindh's 2007 approximate Hessian model with D3 dispersion
14
+ corrections for improved accuracy in describing non-covalent interactions.
15
+
16
+ References:
17
+ - Lindh et al., Chem. Phys. Lett. 2007, 241, 423.
18
+ - Grimme et al., J. Chem. Phys. 2010, 132, 154104 (DFT-D3).
19
+ - https://github.com/grimme-lab/xtb/blob/main/src/model_hessian.f90
20
+ """
21
+ def __init__(self):
22
+ # Unit conversion constants
23
+ self.bohr2angstroms = UnitValueLib().bohr2angstroms
24
+ self.hartree2kcalmol = UnitValueLib().hartree2kcalmol
25
+
26
+ # Force constant parameters
27
+ self.bond_threshold_scale = 1.0
28
+ self.kr = 0.45 # Bond stretching force constant
29
+ self.kf = 0.10 # Angle bend force constant
30
+ self.kt = 0.0025 # Torsion force constant
31
+ self.ko = 0.16 # Out-of-plane force constant
32
+ self.kd = 0.05 # Dispersion force constant
33
+
34
+ # Numerical parameters
35
+ self.cutoff = 50.0 # Cutoff for long-range interactions (Bohr)
36
+ self.eps = 1.0e-12 # Numerical threshold for avoiding division by zero
37
+
38
+ # Reference parameters (element type matrices)
39
+ self.rAv = np.array([
40
+ [1.3500, 2.1000, 2.5300],
41
+ [2.1000, 2.8700, 3.8000],
42
+ [2.5300, 3.8000, 4.5000]
43
+ ])
44
+
45
+ self.aAv = np.array([
46
+ [1.0000, 0.3949, 0.3949],
47
+ [0.3949, 0.2800, 0.1200],
48
+ [0.3949, 0.1200, 0.0600]
49
+ ])
50
+
51
+ self.dAv = np.array([
52
+ [0.0000, 3.6000, 3.6000],
53
+ [3.6000, 5.3000, 5.3000],
54
+ [3.6000, 5.3000, 5.3000]
55
+ ])
56
+
57
+ # D3 dispersion parameters
58
+ self.d3params = D3Parameters()
59
+
60
+ def select_idx(self, elem_num):
61
+ """
62
+ Determine element group index for parameter selection.
63
+
64
+ Args:
65
+ elem_num (str or int): Element symbol or atomic number
66
+
67
+ Returns:
68
+ int: Group index (0-2) for parameter lookup
69
+ """
70
+ if isinstance(elem_num, str):
71
+ elem_num = element_number(elem_num)
72
+
73
+ # Group 1: H
74
+ if elem_num > 0 and elem_num < 2:
75
+ return 0
76
+ # Group 2: First row elements (Li-Ne)
77
+ elif elem_num >= 2 and elem_num < 10:
78
+ return 1
79
+ # Group 3: All others
80
+ else:
81
+ return 2
82
+
83
+ def calc_force_const(self, alpha, r_0, distance_2):
84
+ """
85
+ Calculate bond stretching force constant based on Lindh's model.
86
+
87
+ Args:
88
+ alpha: Exponential parameter
89
+ r_0: Reference bond length
90
+ distance_2: Squared distance between atoms
91
+
92
+ Returns:
93
+ float: Force constant
94
+ """
95
+ return np.exp(alpha * (r_0**2 - distance_2))
96
+
97
+ def get_c6_coefficient(self, element):
98
+ """
99
+ Get C6 dispersion coefficient for an element.
100
+
101
+ Args:
102
+ element: Element symbol
103
+
104
+ Returns:
105
+ float: C6 coefficient in atomic units
106
+ """
107
+ return D2_C6_coeff_lib(element)
108
+
109
+ def calc_d3_force_const(self, r_ij, c6_param, c8_param, r0_param):
110
+ """
111
+ Calculate D3 dispersion force constant with Becke-Johnson damping.
112
+
113
+ Args:
114
+ r_ij: Distance between atoms
115
+ c6_param: C6 dispersion coefficient
116
+ c8_param: C8 dispersion coefficient
117
+ r0_param: van der Waals radius sum parameter
118
+
119
+ Returns:
120
+ float: D3 dispersion force constant
121
+ """
122
+ # Becke-Johnson damping function for C6 term
123
+ r0_plus_a1 = r0_param + self.d3params.a1
124
+ f_damp_6 = r_ij**6 / (r_ij**6 + (r0_plus_a1 * self.d3params.a2)**6)
125
+
126
+ # Becke-Johnson damping function for C8 term
127
+ f_damp_8 = r_ij**8 / (r_ij**8 + (r0_plus_a1 * self.d3params.a2)**8)
128
+
129
+ # D3 dispersion energy contributions
130
+ e6 = -self.d3params.s6 * c6_param * f_damp_6 / r_ij**6
131
+ e8 = -self.d3params.s8 * c8_param * f_damp_8 / r_ij**8
132
+
133
+ # Combined force constant (negative of energy for attractive contribution)
134
+ return -(e6 + e8)
135
+
136
+ def get_d3_parameters(self, elem1, elem2):
137
+ """
138
+ Get D3 parameters for a pair of elements.
139
+
140
+ Args:
141
+ elem1: First element symbol
142
+ elem2: Second element symbol
143
+
144
+ Returns:
145
+ tuple: (c6_param, c8_param, r0_param) for the element pair
146
+ """
147
+ # Get C6 coefficients
148
+ c6_1 = self.get_c6_coefficient(elem1)
149
+ c6_2 = self.get_c6_coefficient(elem2)
150
+
151
+ # Combine C6 coefficients
152
+ c6_param = np.sqrt(c6_1 * c6_2)
153
+
154
+ # Get r4r2 values for C8 coefficient calculation
155
+ r4r2_1 = self.d3params.get_r4r2(elem1)
156
+ r4r2_2 = self.d3params.get_r4r2(elem2)
157
+
158
+ # Calculate C8 coefficient (3.0 is the conversion factor in Grimme's D3 formulation)
159
+ c8_param = 3.0 * c6_param * np.sqrt(r4r2_1 * r4r2_2)
160
+
161
+ # Calculate R0 parameter (vdW radii sum)
162
+ r0_1 = UFF_VDW_distance_lib(elem1) / self.bohr2angstroms
163
+ r0_2 = UFF_VDW_distance_lib(elem2) / self.bohr2angstroms
164
+ r0_param = r0_1 + r0_2
165
+
166
+ return c6_param, c8_param, r0_param
167
+
168
+ def calc_d3_gradient_components(self, x_ij, y_ij, z_ij, c6_param, c8_param, r0_param):
169
+ """
170
+ Calculate D3 dispersion gradient components.
171
+
172
+ Args:
173
+ x_ij, y_ij, z_ij: Distance components
174
+ c6_param: C6 dispersion coefficient
175
+ c8_param: C8 dispersion coefficient
176
+ r0_param: van der Waals radius sum parameter
177
+
178
+ Returns:
179
+ tuple: (xx, xy, xz, yy, yz, zz) gradient components
180
+ """
181
+ r_ij_2 = x_ij**2 + y_ij**2 + z_ij**2
182
+ r_ij = np.sqrt(r_ij_2)
183
+
184
+ if r_ij < 0.1: # Avoid numerical issues
185
+ return 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
186
+
187
+ # BJ damping parameters
188
+ r0_plus_a1 = r0_param + self.d3params.a1
189
+ a2_term = self.d3params.a2
190
+ bj_term_6 = (r0_plus_a1 * a2_term)**6
191
+ bj_term_8 = (r0_plus_a1 * a2_term)**8
192
+
193
+ # Calculate damping functions and their derivatives
194
+ r_ij_6 = r_ij**6
195
+ r_ij_8 = r_ij**8
196
+
197
+ # C6 term: damping and derivatives
198
+ damp_6 = r_ij_6 / (r_ij_6 + bj_term_6)
199
+ d_damp_6_dr = 6.0 * r_ij_6 * bj_term_6 / ((r_ij_6 + bj_term_6)**2 * r_ij)
200
+
201
+ # C8 term: damping and derivatives
202
+ damp_8 = r_ij_8 / (r_ij_8 + bj_term_8)
203
+ d_damp_8_dr = 8.0 * r_ij_8 * bj_term_8 / ((r_ij_8 + bj_term_8)**2 * r_ij)
204
+
205
+ # Force (negative derivative of energy)
206
+ f6 = self.d3params.s6 * c6_param * (6.0 * damp_6 / r_ij**7 + d_damp_6_dr / r_ij**6)
207
+ f8 = self.d3params.s8 * c8_param * (8.0 * damp_8 / r_ij**9 + d_damp_8_dr / r_ij**8)
208
+
209
+ # Total force
210
+ force = f6 + f8
211
+
212
+ # Calculate derivative components
213
+ deriv_scale = force / r_ij
214
+
215
+ # Calculate gradient components
216
+ xx = deriv_scale * x_ij**2 / r_ij_2
217
+ xy = deriv_scale * x_ij * y_ij / r_ij_2
218
+ xz = deriv_scale * x_ij * z_ij / r_ij_2
219
+ yy = deriv_scale * y_ij**2 / r_ij_2
220
+ yz = deriv_scale * y_ij * z_ij / r_ij_2
221
+ zz = deriv_scale * z_ij**2 / r_ij_2
222
+
223
+ return xx, xy, xz, yy, yz, zz
224
+
225
+ def lindh2007_bond(self, coord, element_list):
226
+ """
227
+ Calculate bond stretching contributions to the Hessian.
228
+
229
+ Args:
230
+ coord: Atomic coordinates (Bohr)
231
+ element_list: List of element symbols
232
+ """
233
+ n_atoms = len(coord)
234
+
235
+ for i in range(n_atoms):
236
+ i_idx = self.select_idx(element_list[i])
237
+
238
+ for j in range(i):
239
+ j_idx = self.select_idx(element_list[j])
240
+
241
+ # Calculate distance components and magnitude
242
+ x_ij = coord[i][0] - coord[j][0]
243
+ y_ij = coord[i][1] - coord[j][1]
244
+ z_ij = coord[i][2] - coord[j][2]
245
+ r_ij_2 = x_ij**2 + y_ij**2 + z_ij**2
246
+ r_ij = np.sqrt(r_ij_2)
247
+
248
+ # Get Lindh parameters
249
+ r_0 = self.rAv[i_idx][j_idx]
250
+ d_0 = self.dAv[i_idx][j_idx]
251
+ alpha = self.aAv[i_idx][j_idx]
252
+
253
+ # Determine appropriate bond length based on bond type
254
+ single_bond = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
255
+ covalent_length = single_bond # Default to single bond
256
+
257
+ # Get D3 parameters and calculate dispersion contribution
258
+ c6_param, c8_param, r0_param = self.get_d3_parameters(element_list[i], element_list[j])
259
+
260
+ # Calculate force constants
261
+ lindh_force = self.kr * self.calc_force_const(alpha, covalent_length, r_ij_2)
262
+
263
+ # Add D3 dispersion if atoms are far apart
264
+ d3_factor = 0.0
265
+ if r_ij > 2.0 * covalent_length:
266
+ d3_factor = self.kd * self.calc_d3_force_const(r_ij, c6_param, c8_param, r0_param)
267
+
268
+ # Combined force constant
269
+ g_mm = lindh_force + d3_factor
270
+
271
+ # Calculate D3 gradient components
272
+ d3_xx, d3_xy, d3_xz, d3_yy, d3_yz, d3_zz = self.calc_d3_gradient_components(
273
+ x_ij, y_ij, z_ij, c6_param, c8_param, r0_param)
274
+
275
+ # Calculate Hessian elements
276
+ hess_xx = g_mm * x_ij**2 / r_ij_2 - d3_xx
277
+ hess_xy = g_mm * x_ij * y_ij / r_ij_2 - d3_xy
278
+ hess_xz = g_mm * x_ij * z_ij / r_ij_2 - d3_xz
279
+ hess_yy = g_mm * y_ij**2 / r_ij_2 - d3_yy
280
+ hess_yz = g_mm * y_ij * z_ij / r_ij_2 - d3_yz
281
+ hess_zz = g_mm * z_ij**2 / r_ij_2 - d3_zz
282
+
283
+ # Update diagonal blocks
284
+ i_offset = i * 3
285
+ j_offset = j * 3
286
+
287
+ # i-i block
288
+ self.cart_hess[i_offset, i_offset] += hess_xx
289
+ self.cart_hess[i_offset + 1, i_offset] += hess_xy
290
+ self.cart_hess[i_offset + 1, i_offset + 1] += hess_yy
291
+ self.cart_hess[i_offset + 2, i_offset] += hess_xz
292
+ self.cart_hess[i_offset + 2, i_offset + 1] += hess_yz
293
+ self.cart_hess[i_offset + 2, i_offset + 2] += hess_zz
294
+
295
+ # j-j block
296
+ self.cart_hess[j_offset, j_offset] += hess_xx
297
+ self.cart_hess[j_offset + 1, j_offset] += hess_xy
298
+ self.cart_hess[j_offset + 1, j_offset + 1] += hess_yy
299
+ self.cart_hess[j_offset + 2, j_offset] += hess_xz
300
+ self.cart_hess[j_offset + 2, j_offset + 1] += hess_yz
301
+ self.cart_hess[j_offset + 2, j_offset + 2] += hess_zz
302
+
303
+ # i-j block
304
+ self.cart_hess[i_offset, j_offset] -= hess_xx
305
+ self.cart_hess[i_offset, j_offset + 1] -= hess_xy
306
+ self.cart_hess[i_offset, j_offset + 2] -= hess_xz
307
+ self.cart_hess[i_offset + 1, j_offset] -= hess_xy
308
+ self.cart_hess[i_offset + 1, j_offset + 1] -= hess_yy
309
+ self.cart_hess[i_offset + 1, j_offset + 2] -= hess_yz
310
+ self.cart_hess[i_offset + 2, j_offset] -= hess_xz
311
+ self.cart_hess[i_offset + 2, j_offset + 1] -= hess_yz
312
+ self.cart_hess[i_offset + 2, j_offset + 2] -= hess_zz
313
+
314
+ def lindh2007_angle(self, coord, element_list):
315
+ """
316
+ Calculate angle bending contributions to the Hessian.
317
+
318
+ Args:
319
+ coord: Atomic coordinates (Bohr)
320
+ element_list: List of element symbols
321
+ """
322
+ n_atoms = len(coord)
323
+
324
+ for i in range(n_atoms):
325
+ i_idx = self.select_idx(element_list[i])
326
+
327
+ for j in range(n_atoms):
328
+ if i == j:
329
+ continue
330
+
331
+ j_idx = self.select_idx(element_list[j])
332
+
333
+ # Vector from j to i
334
+ x_ij = coord[i][0] - coord[j][0]
335
+ y_ij = coord[i][1] - coord[j][1]
336
+ z_ij = coord[i][2] - coord[j][2]
337
+ r_ij_2 = x_ij**2 + y_ij**2 + z_ij**2
338
+ r_ij = np.sqrt(r_ij_2)
339
+
340
+ # Get bond parameters
341
+ covalent_length_ij = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
342
+
343
+ # Get Lindh parameters
344
+ r_ij_0 = self.rAv[i_idx][j_idx]
345
+ d_ij_0 = self.dAv[i_idx][j_idx]
346
+ alpha_ij = self.aAv[i_idx][j_idx]
347
+
348
+ # Loop through potential third atoms to form an angle
349
+ for k in range(j):
350
+ if i == k:
351
+ continue
352
+
353
+ k_idx = self.select_idx(element_list[k])
354
+
355
+ # Get parameters for i-k interaction
356
+ r_ik_0 = self.rAv[i_idx][k_idx]
357
+ d_ik_0 = self.dAv[i_idx][k_idx]
358
+ alpha_ik = self.aAv[i_idx][k_idx]
359
+
360
+ # Vector from k to i
361
+ x_ik = coord[i][0] - coord[k][0]
362
+ y_ik = coord[i][1] - coord[k][1]
363
+ z_ik = coord[i][2] - coord[k][2]
364
+ r_ik_2 = x_ik**2 + y_ik**2 + z_ik**2
365
+ r_ik = np.sqrt(r_ik_2)
366
+
367
+ # Get bond parameters
368
+ covalent_length_ik = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[k])
369
+
370
+ # Check if angle is well-defined (not linear)
371
+ cos_angle = (x_ij * x_ik + y_ij * y_ik + z_ij * z_ik) / (r_ij * r_ik)
372
+
373
+ if abs(cos_angle - 1.0) < self.eps:
374
+ continue # Skip near-linear angles
375
+
376
+ # Vector from k to j
377
+ x_jk = coord[j][0] - coord[k][0]
378
+ y_jk = coord[j][1] - coord[k][1]
379
+ z_jk = coord[j][2] - coord[k][2]
380
+ r_jk_2 = x_jk**2 + y_jk**2 + z_jk**2
381
+ r_jk = np.sqrt(r_jk_2)
382
+
383
+ # Calculate force constants with D3 contributions
384
+ c6_ij, c8_ij, r0_ij = self.get_d3_parameters(element_list[i], element_list[j])
385
+ c6_ik, c8_ik, r0_ik = self.get_d3_parameters(element_list[i], element_list[k])
386
+
387
+ g_ij = self.calc_force_const(alpha_ij, covalent_length_ij, r_ij_2)
388
+ if r_ij > 2.0 * covalent_length_ij:
389
+ g_ij += 0.5 * self.kd * self.calc_d3_force_const(r_ij, c6_ij, c8_ij, r0_ij)
390
+
391
+ g_ik = self.calc_force_const(alpha_ik, covalent_length_ik, r_ik_2)
392
+ if r_ik > 2.0 * covalent_length_ik:
393
+ g_ik += 0.5 * self.kd * self.calc_d3_force_const(r_ik, c6_ik, c8_ik, r0_ik)
394
+
395
+ # Angular force constant
396
+ g_jk = self.kf * (g_ij + 0.5 * self.kd / self.kr * d_ij_0) * (g_ik + 0.5 * self.kd / self.kr * d_ik_0)
397
+
398
+ # Cross product magnitude for sin(theta)
399
+ r_cross_2 = (y_ij * z_ik - z_ij * y_ik)**2 + (z_ij * x_ik - x_ij * z_ik)**2 + (x_ij * y_ik - y_ij * x_ik)**2
400
+ r_cross = np.sqrt(r_cross_2) if r_cross_2 > 1.0e-12 else 0.0
401
+
402
+ # Skip if distances are too small
403
+ if r_ik <= self.eps or r_ij <= self.eps or r_jk <= self.eps:
404
+ continue
405
+
406
+ # Calculate angle and its derivatives
407
+ dot_product = x_ij * x_ik + y_ij * y_ik + z_ij * z_ik
408
+ cos_theta = dot_product / (r_ij * r_ik)
409
+ sin_theta = r_cross / (r_ij * r_ik)
410
+
411
+ if sin_theta > self.eps: # Non-linear case
412
+ # Calculate derivatives
413
+ s_xj = (x_ij / r_ij * cos_theta - x_ik / r_ik) / (r_ij * sin_theta)
414
+ s_yj = (y_ij / r_ij * cos_theta - y_ik / r_ik) / (r_ij * sin_theta)
415
+ s_zj = (z_ij / r_ij * cos_theta - z_ik / r_ik) / (r_ij * sin_theta)
416
+
417
+ s_xk = (x_ik / r_ik * cos_theta - x_ij / r_ij) / (r_ik * sin_theta)
418
+ s_yk = (y_ik / r_ik * cos_theta - y_ij / r_ij) / (r_ik * sin_theta)
419
+ s_zk = (z_ik / r_ik * cos_theta - z_ij / r_ij) / (r_ik * sin_theta)
420
+
421
+ s_xi = -s_xj - s_xk
422
+ s_yi = -s_yj - s_yk
423
+ s_zi = -s_zj - s_zk
424
+
425
+ s_i = [s_xi, s_yi, s_zi]
426
+ s_j = [s_xj, s_yj, s_zj]
427
+ s_k = [s_xk, s_yk, s_zk]
428
+
429
+ # Update Hessian elements
430
+ for l in range(3):
431
+ for m in range(3):
432
+ # i-j block
433
+ if i > j:
434
+ self.cart_hess[i*3+l, j*3+m] += g_jk * s_i[l] * s_j[m]
435
+ else:
436
+ self.cart_hess[j*3+l, i*3+m] += g_jk * s_j[l] * s_i[m]
437
+
438
+ # i-k block
439
+ if i > k:
440
+ self.cart_hess[i*3+l, k*3+m] += g_jk * s_i[l] * s_k[m]
441
+ else:
442
+ self.cart_hess[k*3+l, i*3+m] += g_jk * s_k[l] * s_i[m]
443
+
444
+ # j-k block
445
+ if j > k:
446
+ self.cart_hess[j*3+l, k*3+m] += g_jk * s_j[l] * s_k[m]
447
+ else:
448
+ self.cart_hess[k*3+l, j*3+m] += g_jk * s_k[l] * s_j[m]
449
+
450
+ # Diagonal blocks
451
+ for l in range(3):
452
+ for m in range(l):
453
+ self.cart_hess[j*3+l, j*3+m] += g_jk * s_j[l] * s_j[m]
454
+ self.cart_hess[i*3+l, i*3+m] += g_jk * s_i[l] * s_i[m]
455
+ self.cart_hess[k*3+l, k*3+m] += g_jk * s_k[l] * s_k[m]
456
+
457
+ else: # Linear case
458
+ # Handle linear angles using arbitrary perpendicular vectors
459
+ if abs(y_ij) < self.eps and abs(z_ij) < self.eps:
460
+ x_1, y_1, z_1 = -y_ij, x_ij, 0.0
461
+ x_2, y_2, z_2 = -x_ij * z_ij, -y_ij * z_ij, x_ij**2 + y_ij**2
462
+ else:
463
+ x_1, y_1, z_1 = 1.0, 0.0, 0.0
464
+ x_2, y_2, z_2 = 0.0, 1.0, 0.0
465
+
466
+ x = [x_1, x_2]
467
+ y = [y_1, y_2]
468
+ z = [z_1, z_2]
469
+
470
+ # Calculate derivatives for two perpendicular directions
471
+ for ii in range(2):
472
+ r_1 = np.sqrt(x[ii]**2 + y[ii]**2 + z[ii]**2)
473
+ cos_theta_x = x[ii] / r_1
474
+ cos_theta_y = y[ii] / r_1
475
+ cos_theta_z = z[ii] / r_1
476
+
477
+ # Derivatives
478
+ s_xj = -cos_theta_x / r_ij
479
+ s_yj = -cos_theta_y / r_ij
480
+ s_zj = -cos_theta_z / r_ij
481
+ s_xk = -cos_theta_x / r_ik
482
+ s_yk = -cos_theta_y / r_ik
483
+ s_zk = -cos_theta_z / r_ik
484
+
485
+ s_xi = -s_xj - s_xk
486
+ s_yi = -s_yj - s_yk
487
+ s_zi = -s_zj - s_zk
488
+
489
+ s_i = [s_xi, s_yi, s_zi]
490
+ s_j = [s_xj, s_yj, s_zj]
491
+ s_k = [s_xk, s_yk, s_zk]
492
+
493
+ # Update Hessian elements
494
+ for l in range(3):
495
+ for m in range(3):
496
+ # i-j block
497
+ if i > j:
498
+ self.cart_hess[i*3+l, j*3+m] += g_jk * s_i[l] * s_j[m]
499
+ else:
500
+ self.cart_hess[j*3+l, i*3+m] += g_jk * s_j[l] * s_i[m]
501
+
502
+ # i-k block
503
+ if i > k:
504
+ self.cart_hess[i*3+l, k*3+m] += g_jk * s_i[l] * s_k[m]
505
+ else:
506
+ self.cart_hess[k*3+l, i*3+m] += g_jk * s_k[l] * s_i[m]
507
+
508
+ # j-k block
509
+ if j > k:
510
+ self.cart_hess[j*3+l, k*3+m] += g_jk * s_j[l] * s_k[m]
511
+ else:
512
+ self.cart_hess[k*3+l, j*3+m] += g_jk * s_k[l] * s_j[m]
513
+
514
+ # Diagonal blocks
515
+ for l in range(3):
516
+ for m in range(l):
517
+ self.cart_hess[j*3+l, j*3+m] += g_jk * s_j[l] * s_j[m]
518
+ self.cart_hess[i*3+l, i*3+m] += g_jk * s_i[l] * s_i[m]
519
+ self.cart_hess[k*3+l, k*3+m] += g_jk * s_k[l] * s_k[m]
520
+
521
+ def lindh2007_dihedral_angle(self, coord, element_list):
522
+ """
523
+ Calculate dihedral angle (torsion) contributions to the Hessian.
524
+
525
+ Args:
526
+ coord: Atomic coordinates (Bohr)
527
+ element_list: List of element symbols
528
+ """
529
+ n_atoms = len(coord)
530
+
531
+ for j in range(n_atoms):
532
+ t_xyz_2 = coord[j]
533
+
534
+ for k in range(j+1, n_atoms):
535
+ t_xyz_3 = coord[k]
536
+
537
+ for i in range(j):
538
+ if i == k:
539
+ continue
540
+
541
+ t_xyz_1 = coord[i]
542
+
543
+ for l in range(k+1, n_atoms):
544
+ if l == i or l == j:
545
+ continue
546
+
547
+ t_xyz_4 = coord[l]
548
+
549
+ # Get element indices for parameter lookup
550
+ i_idx = self.select_idx(element_list[i])
551
+ j_idx = self.select_idx(element_list[j])
552
+ k_idx = self.select_idx(element_list[k])
553
+ l_idx = self.select_idx(element_list[l])
554
+
555
+ # Get Lindh parameters
556
+ r_ij_0 = self.rAv[i_idx][j_idx]
557
+ d_ij_0 = self.dAv[i_idx][j_idx]
558
+ alpha_ij = self.aAv[i_idx][j_idx]
559
+
560
+ r_jk_0 = self.rAv[j_idx][k_idx]
561
+ d_jk_0 = self.dAv[j_idx][k_idx]
562
+ alpha_jk = self.aAv[j_idx][k_idx]
563
+
564
+ r_kl_0 = self.rAv[k_idx][l_idx]
565
+ d_kl_0 = self.dAv[k_idx][l_idx]
566
+ alpha_kl = self.aAv[k_idx][l_idx]
567
+
568
+ # Calculate bond vectors and lengths
569
+ r_ij = coord[i] - coord[j]
570
+ r_jk = coord[j] - coord[k]
571
+ r_kl = coord[k] - coord[l]
572
+
573
+ # Get bond parameters
574
+ covalent_length_ij = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
575
+ covalent_length_jk = covalent_radii_lib(element_list[j]) + covalent_radii_lib(element_list[k])
576
+ covalent_length_kl = covalent_radii_lib(element_list[k]) + covalent_radii_lib(element_list[l])
577
+
578
+ # Calculate squared distances
579
+ r_ij_2 = np.sum(r_ij**2)
580
+ r_jk_2 = np.sum(r_jk**2)
581
+ r_kl_2 = np.sum(r_kl**2)
582
+
583
+ # Calculate norms
584
+ norm_r_ij = np.sqrt(r_ij_2)
585
+ norm_r_jk = np.sqrt(r_jk_2)
586
+ norm_r_kl = np.sqrt(r_kl_2)
587
+
588
+ # Check if near-linear angles would cause numerical issues
589
+ a35 = (35.0/180) * np.pi
590
+ cosfi_max = np.cos(a35)
591
+
592
+ cosfi2 = np.dot(r_ij, r_jk) / np.sqrt(r_ij_2 * r_jk_2)
593
+ if abs(cosfi2) > cosfi_max:
594
+ continue
595
+
596
+ cosfi3 = np.dot(r_kl, r_jk) / np.sqrt(r_kl_2 * r_jk_2)
597
+ if abs(cosfi3) > cosfi_max:
598
+ continue
599
+
600
+ # Get D3 parameters for bond pairs
601
+ c6_ij, c8_ij, r0_ij = self.get_d3_parameters(element_list[i], element_list[j])
602
+ c6_jk, c8_jk, r0_jk = self.get_d3_parameters(element_list[j], element_list[k])
603
+ c6_kl, c8_kl, r0_kl = self.get_d3_parameters(element_list[k], element_list[l])
604
+
605
+ # Calculate force constants with D3 contributions
606
+ g_ij = self.calc_force_const(alpha_ij, covalent_length_ij, r_ij_2)
607
+ if norm_r_ij > 2.0 * covalent_length_ij:
608
+ g_ij += 0.5 * self.kd * self.calc_d3_force_const(norm_r_ij, c6_ij, c8_ij, r0_ij)
609
+
610
+ g_jk = self.calc_force_const(alpha_jk, covalent_length_jk, r_jk_2)
611
+ if norm_r_jk > 2.0 * covalent_length_jk:
612
+ g_jk += 0.5 * self.kd * self.calc_d3_force_const(norm_r_jk, c6_jk, c8_jk, r0_jk)
613
+
614
+ g_kl = self.calc_force_const(alpha_kl, covalent_length_kl, r_kl_2)
615
+ if norm_r_kl > 2.0 * covalent_length_kl:
616
+ g_kl += 0.5 * self.kd * self.calc_d3_force_const(norm_r_kl, c6_kl, c8_kl, r0_kl)
617
+
618
+ # Calculate torsion force constant
619
+ t_ij = self.kt * (g_ij * 0.5 * self.kd / self.kr * d_ij_0) * \
620
+ (g_jk * 0.5 * self.kd / self.kr * d_jk_0) * \
621
+ (g_kl * 0.5 * self.kd / self.kr * d_kl_0)
622
+
623
+ # Calculate torsion angle and derivatives
624
+ t_xyz = np.array([t_xyz_1, t_xyz_2, t_xyz_3, t_xyz_4])
625
+ tau, c = torsion2(t_xyz)
626
+
627
+ # Extract derivatives
628
+ s_i = c[0]
629
+ s_j = c[1]
630
+ s_k = c[2]
631
+ s_l = c[3]
632
+
633
+ # Update off-diagonal blocks
634
+ for n in range(3):
635
+ for m in range(3):
636
+ self.cart_hess[3*i+n, 3*j+m] += t_ij * s_i[n] * s_j[m]
637
+ self.cart_hess[3*i+n, 3*k+m] += t_ij * s_i[n] * s_k[m]
638
+ self.cart_hess[3*i+n, 3*l+m] += t_ij * s_i[n] * s_l[m]
639
+ self.cart_hess[3*j+n, 3*k+m] += t_ij * s_j[n] * s_k[m]
640
+ self.cart_hess[3*j+n, 3*l+m] += t_ij * s_j[n] * s_l[m]
641
+ self.cart_hess[3*k+n, 3*l+m] += t_ij * s_k[n] * s_l[m]
642
+
643
+ # Update diagonal blocks (lower triangle)
644
+ for n in range(3):
645
+ for m in range(n):
646
+ self.cart_hess[3*i+n, 3*i+m] += t_ij * s_i[n] * s_i[m]
647
+ self.cart_hess[3*j+n, 3*j+m] += t_ij * s_j[n] * s_j[m]
648
+ self.cart_hess[3*k+n, 3*k+m] += t_ij * s_k[n] * s_k[m]
649
+ self.cart_hess[3*l+n, 3*l+m] += t_ij * s_l[n] * s_l[m]
650
+
651
+ def lindh2007_out_of_plane(self, coord, element_list):
652
+ """
653
+ Calculate out-of-plane bending contributions to the Hessian.
654
+
655
+ Args:
656
+ coord: Atomic coordinates (Bohr)
657
+ element_list: List of element symbols
658
+ """
659
+ n_atoms = len(coord)
660
+
661
+ for i in range(n_atoms):
662
+ t_xyz_4 = coord[i]
663
+
664
+ for j in range(i+1, n_atoms):
665
+ t_xyz_1 = coord[j]
666
+
667
+ for k in range(j+1, n_atoms):
668
+ t_xyz_2 = coord[k]
669
+
670
+ for l in range(k+1, n_atoms):
671
+ t_xyz_3 = coord[l]
672
+
673
+ # Calculate bond vectors
674
+ r_ij = coord[i] - coord[j]
675
+ r_ik = coord[i] - coord[k]
676
+ r_il = coord[i] - coord[l]
677
+
678
+ # Get bond parameters
679
+ covalent_length_ij = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
680
+ covalent_length_ik = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[k])
681
+ covalent_length_il = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[l])
682
+
683
+ # Get element indices for parameter lookup
684
+ idx_i = self.select_idx(element_list[i])
685
+ idx_j = self.select_idx(element_list[j])
686
+ idx_k = self.select_idx(element_list[k])
687
+ idx_l = self.select_idx(element_list[l])
688
+
689
+ # Get Lindh parameters
690
+ d_ij_0 = self.dAv[idx_i][idx_j]
691
+ r_ij_0 = self.rAv[idx_i][idx_j]
692
+ alpha_ij = self.aAv[idx_i][idx_j]
693
+
694
+ d_ik_0 = self.dAv[idx_i][idx_k]
695
+ r_ik_0 = self.rAv[idx_i][idx_k]
696
+ alpha_ik = self.aAv[idx_i][idx_k]
697
+
698
+ d_il_0 = self.dAv[idx_i][idx_l]
699
+ r_il_0 = self.rAv[idx_i][idx_l]
700
+ alpha_il = self.aAv[idx_i][idx_l]
701
+
702
+ # Calculate squared distances
703
+ r_ij_2 = np.sum(r_ij**2)
704
+ r_ik_2 = np.sum(r_ik**2)
705
+ r_il_2 = np.sum(r_il**2)
706
+
707
+ # Calculate norms
708
+ norm_r_ij = np.sqrt(r_ij_2)
709
+ norm_r_ik = np.sqrt(r_ik_2)
710
+ norm_r_il = np.sqrt(r_il_2)
711
+
712
+ # Check for near-linear angles that would cause numerical issues
713
+ cosfi2 = np.dot(r_ij, r_ik) / (norm_r_ij * norm_r_ik)
714
+ if abs(abs(cosfi2) - 1.0) < 1.0e-1:
715
+ continue
716
+
717
+ cosfi3 = np.dot(r_ij, r_il) / (norm_r_ij * norm_r_il)
718
+ if abs(abs(cosfi3) - 1.0) < 1.0e-1:
719
+ continue
720
+
721
+ cosfi4 = np.dot(r_ik, r_il) / (norm_r_ik * norm_r_il)
722
+ if abs(abs(cosfi4) - 1.0) < 1.0e-1:
723
+ continue
724
+
725
+ # Get D3 parameters for each pair
726
+ c6_ij, c8_ij, r0_ij = self.get_d3_parameters(element_list[i], element_list[j])
727
+ c6_ik, c8_ik, r0_ik = self.get_d3_parameters(element_list[i], element_list[k])
728
+ c6_il, c8_il, r0_il = self.get_d3_parameters(element_list[i], element_list[l])
729
+
730
+ # Disable direct D3 contributions to out-of-plane terms
731
+ kd = 0.0
732
+
733
+ # Calculate force constants for each bond
734
+ g_ij = self.calc_force_const(alpha_ij, covalent_length_ij, r_ij_2)
735
+ if norm_r_ij > 2.0 * covalent_length_ij:
736
+ g_ij += 0.5 * kd * self.calc_d3_force_const(norm_r_ij, c6_ij, c8_ij, r0_ij)
737
+
738
+ g_ik = self.calc_force_const(alpha_ik, covalent_length_ik, r_ik_2)
739
+ if norm_r_ik > 2.0 * covalent_length_ik:
740
+ g_ik += 0.5 * kd * self.calc_d3_force_const(norm_r_ik, c6_ik, c8_ik, r0_ik)
741
+
742
+ g_il = self.calc_force_const(alpha_il, covalent_length_il, r_il_2)
743
+ if norm_r_il > 2.0 * covalent_length_il:
744
+ g_il += 0.5 * kd * self.calc_d3_force_const(norm_r_il, c6_il, c8_il, r0_il)
745
+
746
+ # Combined force constant for out-of-plane motion
747
+ t_ij = self.ko * g_ij * g_ik * g_il
748
+
749
+ # Calculate out-of-plane angle and derivatives
750
+ t_xyz = np.array([t_xyz_1, t_xyz_2, t_xyz_3, t_xyz_4])
751
+ theta, c = outofplane2(t_xyz)
752
+
753
+ # Extract derivatives
754
+ s_i = c[0]
755
+ s_j = c[1]
756
+ s_k = c[2]
757
+ s_l = c[3]
758
+
759
+ # Update off-diagonal blocks
760
+ for n in range(3):
761
+ for m in range(3):
762
+ self.cart_hess[i*3+n, j*3+m] += t_ij * s_i[n] * s_j[m]
763
+ self.cart_hess[i*3+n, k*3+m] += t_ij * s_i[n] * s_k[m]
764
+ self.cart_hess[i*3+n, l*3+m] += t_ij * s_i[n] * s_l[m]
765
+ self.cart_hess[j*3+n, k*3+m] += t_ij * s_j[n] * s_k[m]
766
+ self.cart_hess[j*3+n, l*3+m] += t_ij * s_j[n] * s_l[m]
767
+ self.cart_hess[k*3+n, l*3+m] += t_ij * s_k[n] * s_l[m]
768
+
769
+ # Update diagonal blocks (lower triangle)
770
+ for n in range(3):
771
+ for m in range(n):
772
+ self.cart_hess[i*3+n, i*3+m] += t_ij * s_i[n] * s_i[m]
773
+ self.cart_hess[j*3+n, j*3+m] += t_ij * s_j[n] * s_j[m]
774
+ self.cart_hess[k*3+n, k*3+m] += t_ij * s_k[n] * s_k[m]
775
+ self.cart_hess[l*3+n, l*3+m] += t_ij * s_l[n] * s_l[m]
776
+
777
+ def main(self, coord, element_list, cart_gradient):
778
+ """
779
+ Calculate approximate Hessian using Lindh's 2007 model with D3 dispersion.
780
+
781
+ Args:
782
+ coord: Atomic coordinates (Bohr)
783
+ element_list: List of element symbols
784
+ cart_gradient: Cartesian gradient vector
785
+
786
+ Returns:
787
+ hess_proj: Projected approximate Hessian matrix
788
+ """
789
+ print("Generating Lindh's (2007) approximate Hessian with D3 dispersion...")
790
+
791
+ # Scale eigenvalues based on gradient norm (smaller scale for larger gradients)
792
+ norm_grad = np.linalg.norm(cart_gradient)
793
+ scale = 0.1
794
+ eigval_scale = scale * np.exp(-1 * norm_grad**2.0)
795
+
796
+ # Initialize Hessian matrix
797
+ n_atoms = len(coord)
798
+ self.cart_hess = np.zeros((n_atoms*3, n_atoms*3), dtype="float64")
799
+
800
+ # Calculate individual contributions
801
+ self.lindh2007_bond(coord, element_list)
802
+ self.lindh2007_angle(coord, element_list)
803
+ self.lindh2007_dihedral_angle(coord, element_list)
804
+ self.lindh2007_out_of_plane(coord, element_list)
805
+
806
+ # Symmetrize the Hessian matrix
807
+ for i in range(n_atoms*3):
808
+ for j in range(i):
809
+ if abs(self.cart_hess[i, j]) < 1.0e-10:
810
+ self.cart_hess[i, j] = self.cart_hess[j, i]
811
+ else:
812
+ self.cart_hess[j, i] = self.cart_hess[i, j]
813
+
814
+ # Project out translational and rotational degrees of freedom
815
+ hess_proj = Calculationtools().project_out_hess_tr_and_rot_for_coord(self.cart_hess, element_list, coord)
816
+
817
+ # Adjust eigenvalues for stability based on gradient magnitude
818
+ eigenvalues, eigenvectors = np.linalg.eigh(hess_proj)
819
+ hess_proj = np.dot(np.dot(eigenvectors, np.diag(np.abs(eigenvalues) * eigval_scale)),
820
+ np.transpose(eigenvectors))
821
+
822
+ return hess_proj