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,709 @@
1
+ import numpy as np
2
+ from collections import defaultdict
3
+
4
+ from multioptpy.Parameters.parameter import UnitValueLib
5
+ from multioptpy.Utils.calc_tools import Calculationtools
6
+ from multioptpy.Parameters.parameter import GFNFFParameters
7
+
8
+
9
+ class GFNFFApproxHessian:
10
+ def __init__(self):
11
+ """
12
+ GFNFF-based model Hessian implementation
13
+ Based on the GFN-FF method by Grimme et al., JCTC 2017, 13, 1989-2009
14
+ """
15
+ self.bohr2angstroms = UnitValueLib().bohr2angstroms
16
+ self.hartree2kcalmol = UnitValueLib().hartree2kcalmol
17
+ self.params = GFNFFParameters()
18
+ self.bond_factor = 1.3 # Bond detection threshold factor
19
+
20
+ def estimate_atomic_charges(self, coord, element_list, bond_mat):
21
+ """
22
+ Estimate atomic partial charges using a simplified EEQ model based on GFNFF approach
23
+
24
+ Parameters:
25
+ coord: atomic coordinates (Bohr)
26
+ element_list: list of element symbols
27
+ bond_mat: bond connectivity matrix
28
+
29
+ Returns:
30
+ charges: array of estimated atomic charges
31
+ """
32
+ n_atoms = len(coord)
33
+ charges = np.zeros(n_atoms)
34
+
35
+ # Calculate coordination numbers for charge dependence
36
+ cn = self.calculate_coordination_numbers(coord, element_list)
37
+
38
+ # Calculate electronegativity-based charge transfer
39
+ for i in range(n_atoms):
40
+ for j in range(n_atoms):
41
+ if i == j or not bond_mat[i, j]:
42
+ continue
43
+
44
+ # Get electronegativity values
45
+ en_i = self.params.get_electronegativity(element_list[i])
46
+ en_j = self.params.get_electronegativity(element_list[j])
47
+
48
+ # Calculate distance in Angstroms
49
+ r_ij = np.linalg.norm(coord[i] - coord[j]) * self.bohr2angstroms
50
+
51
+ # CN-dependent electronegativity adjustment (simplified from GFNFF)
52
+ cn_ref_i = self.params.ref_cn.get(element_list[i], 1.0)
53
+ cn_ref_j = self.params.ref_cn.get(element_list[j], 1.0)
54
+
55
+ cn_factor_i = np.exp(-0.1 * (cn[i] - cn_ref_i)**2)
56
+ cn_factor_j = np.exp(-0.1 * (cn[j] - cn_ref_j)**2)
57
+
58
+ en_eff_i = en_i * cn_factor_i
59
+ en_eff_j = en_j * cn_factor_j
60
+
61
+ # Simple electronegativity-based charge transfer
62
+ charge_transfer = 0.1 * (en_eff_j - en_eff_i) / (r_ij * (en_eff_i + en_eff_j))
63
+ charges[i] += charge_transfer
64
+ charges[j] -= charge_transfer
65
+
66
+ # Normalize charges to ensure neutrality
67
+ charges -= np.mean(charges)
68
+
69
+ return charges
70
+
71
+ def calculate_coordination_numbers(self, coord, element_list):
72
+ """
73
+ Calculate atomic coordination numbers using the counting function from GFNFF
74
+
75
+ Parameters:
76
+ coord: atomic coordinates (Bohr)
77
+ element_list: list of element symbols
78
+
79
+ Returns:
80
+ cn: array of coordination numbers for each atom
81
+ """
82
+ n_atoms = len(coord)
83
+ cn = np.zeros(n_atoms)
84
+
85
+ for i in range(n_atoms):
86
+ for j in range(n_atoms):
87
+ if i == j:
88
+ continue
89
+
90
+ # Get covalent radii
91
+ r_cov_i = self.params.get_cov_radius(element_list[i])
92
+ r_cov_j = self.params.get_cov_radius(element_list[j])
93
+
94
+ # Calculate distance in Angstroms
95
+ dist_ij = np.linalg.norm(coord[i] - coord[j]) * self.bohr2angstroms
96
+
97
+ # Calculate coordination number contribution (GFNFF counting function)
98
+ r_cov = r_cov_i + r_cov_j
99
+ k_cn = 16.0 # Steepness of the counting function
100
+ cn_contrib = 1.0 / (1.0 + np.exp(-k_cn * (r_cov * 1.2 / dist_ij - 1.0)))
101
+ cn[i] += cn_contrib
102
+
103
+ return cn
104
+
105
+ def get_bond_connectivity(self, coord, element_list):
106
+ """Calculate bond connectivity matrix and related data"""
107
+ n_atoms = len(coord)
108
+ dist_mat = np.zeros((n_atoms, n_atoms))
109
+ pair_cov_radii_mat = np.zeros((n_atoms, n_atoms))
110
+
111
+ for i in range(n_atoms):
112
+ for j in range(i+1, n_atoms):
113
+ dist = np.linalg.norm(coord[i] - coord[j])
114
+ dist_mat[i, j] = dist_mat[j, i] = dist
115
+
116
+ r_cov_i = self.params.get_cov_radius(element_list[i])
117
+ r_cov_j = self.params.get_cov_radius(element_list[j])
118
+ cov_sum = (r_cov_i + r_cov_j) / self.bohr2angstroms # Convert to bohr
119
+
120
+ pair_cov_radii_mat[i, j] = pair_cov_radii_mat[j, i] = cov_sum
121
+
122
+ # Bond connectivity matrix (True if bond exists between atoms)
123
+ bond_mat = dist_mat <= (pair_cov_radii_mat * self.bond_factor)
124
+ np.fill_diagonal(bond_mat, False) # No self-bonds
125
+
126
+ return bond_mat, dist_mat, pair_cov_radii_mat
127
+
128
+ def detect_hydrogen_bonds(self, coord, element_list, bond_mat, charges):
129
+ """
130
+ Detect hydrogen bonds in the structure based on GFNFF criteria
131
+
132
+ Parameters:
133
+ coord: atomic coordinates (Bohr)
134
+ element_list: list of element symbols
135
+ bond_mat: bond connectivity matrix
136
+ charges: atomic partial charges
137
+
138
+ Returns:
139
+ hbonds: list of hydrogen bonds as (donor, H, acceptor) triplets
140
+ """
141
+ n_atoms = len(coord)
142
+ hbonds = []
143
+
144
+ # Define potential donors (electronegative elements that can have H attached)
145
+ donors = ['O', 'N', 'F', 'Cl', 'Br', 'I', 'S']
146
+ acceptors = ['O', 'N', 'F', 'Cl', 'Br', 'I', 'S']
147
+
148
+ # Find all hydrogen bonds
149
+ for i in range(n_atoms):
150
+ if element_list[i] != 'H':
151
+ continue
152
+
153
+ # Find potential donor (atom bonded to H)
154
+ donor_idx = -1
155
+ for j in range(n_atoms):
156
+ if bond_mat[i, j] and element_list[j] in donors:
157
+ donor_idx = j
158
+ break
159
+
160
+ if donor_idx == -1:
161
+ continue # No suitable donor
162
+
163
+ donor_element = element_list[donor_idx]
164
+
165
+ # Look for acceptors
166
+ for acceptor_idx in range(n_atoms):
167
+ if acceptor_idx == donor_idx or bond_mat[i, acceptor_idx]:
168
+ continue # Skip donor and directly bonded atoms
169
+
170
+ acceptor_element = element_list[acceptor_idx]
171
+ if acceptor_element not in acceptors:
172
+ continue
173
+
174
+ # Check if this acceptor-H-donor combination is valid
175
+ if (acceptor_element, 'H', donor_element) not in self.params.hbond_params and \
176
+ (donor_element, 'H', acceptor_element) not in self.params.hbond_params:
177
+ continue
178
+
179
+ # Calculate H...acceptor distance
180
+ h_acc_dist = np.linalg.norm(coord[i] - coord[acceptor_idx]) * self.bohr2angstroms
181
+
182
+ # Calculate donor-H...acceptor angle
183
+ d_h_vec = coord[i] - coord[donor_idx]
184
+ h_acc_vec = coord[acceptor_idx] - coord[i]
185
+ d_h_len = np.linalg.norm(d_h_vec)
186
+ h_acc_len = np.linalg.norm(h_acc_vec)
187
+
188
+ if d_h_len > 0 and h_acc_len > 0:
189
+ cos_angle = np.dot(d_h_vec, h_acc_vec) / (d_h_len * h_acc_len)
190
+ cos_angle = np.clip(cos_angle, -1.0, 1.0)
191
+ angle = np.arccos(cos_angle) * 180.0 / np.pi
192
+
193
+ # Get parameters for this H-bond
194
+ h_params = self.params.get_hbond_params(donor_element, 'H', acceptor_element)
195
+ r0_hb = h_params[0] * self.bohr2angstroms # Convert to Angstroms
196
+
197
+ # Check hydrogen bond criteria: distance and angle
198
+ # Distance criterion: within 30% of optimal distance
199
+ # Angle criterion: > 120 degrees (approximately linear)
200
+ if h_acc_dist < 1.3 * r0_hb and angle > 120.0:
201
+ # Verify with partial charges (acceptor should be negative)
202
+ if charges[acceptor_idx] < -0.05:
203
+ hbonds.append((donor_idx, i, acceptor_idx))
204
+
205
+ return hbonds
206
+
207
+ def build_topology(self, coord, element_list):
208
+ """
209
+ Build molecular topology including bonds, angles, dihedrals, and non-bonded interactions
210
+
211
+ Parameters:
212
+ coord: atomic coordinates (Bohr)
213
+ element_list: list of element symbols
214
+
215
+ Returns:
216
+ topology: dictionary with molecular topology information
217
+ """
218
+ # Get bond connectivity
219
+ bond_mat, dist_mat, cov_radii_mat = self.get_bond_connectivity(coord, element_list)
220
+
221
+ # Estimate atomic charges
222
+ charges = self.estimate_atomic_charges(coord, element_list, bond_mat)
223
+
224
+ # Detect hydrogen bonds
225
+ hbonds = self.detect_hydrogen_bonds(coord, element_list, bond_mat, charges)
226
+
227
+ # Calculate coordination numbers
228
+ cn = self.calculate_coordination_numbers(coord, element_list)
229
+
230
+ # Build lists of bonds, angles, and dihedrals
231
+ n_atoms = len(coord)
232
+ bonds = []
233
+ angles = []
234
+ dihedrals = []
235
+
236
+ # Extract bonds
237
+ for i in range(n_atoms):
238
+ for j in range(i+1, n_atoms):
239
+ if bond_mat[i, j]:
240
+ bonds.append((i, j))
241
+
242
+ # Extract angles
243
+ for j in range(n_atoms):
244
+ bonded_to_j = [i for i in range(n_atoms) if bond_mat[i, j] and i != j]
245
+ for i in bonded_to_j:
246
+ for k in bonded_to_j:
247
+ if i < k: # Avoid duplicates
248
+ angles.append((i, j, k))
249
+
250
+ # Extract dihedrals
251
+ for j, k in bonds:
252
+ bonded_to_j = [i for i in range(n_atoms) if bond_mat[i, j] and i != j and i != k]
253
+ bonded_to_k = [l for l in range(n_atoms) if bond_mat[k, l] and l != k and l != j]
254
+
255
+ for i in bonded_to_j:
256
+ for l in bonded_to_k:
257
+ if i != l: # Avoid improper dihedrals here
258
+ dihedrals.append((i, j, k, l))
259
+
260
+ # Build non-bonded pairs (atoms separated by at least 3 bonds)
261
+ nb_pairs = []
262
+
263
+ # Calculate shortest path lengths between atoms
264
+ bond_graph = defaultdict(list)
265
+ for i, j in bonds:
266
+ bond_graph[i].append(j)
267
+ bond_graph[j].append(i)
268
+
269
+ # For each atom pair, determine if they're separated by at least 3 bonds
270
+ for i in range(n_atoms):
271
+ for j in range(i+1, n_atoms):
272
+ if not bond_mat[i, j]: # Not directly bonded
273
+ # Check if they share a bonded neighbor (1-3 interaction)
274
+ common_neighbors = set(bond_graph[i]).intersection(bond_graph[j])
275
+ if not common_neighbors: # No common neighbors
276
+ nb_pairs.append((i, j))
277
+ else:
278
+ # Check if they're involved in a 1-4 interaction (part of a dihedral)
279
+ is_14 = False
280
+ for k in common_neighbors:
281
+ for l in bond_graph[j]:
282
+ if l != k and l in bond_graph[k] and l != i:
283
+ is_14 = True
284
+ break
285
+ if is_14:
286
+ break
287
+
288
+ if not is_14:
289
+ nb_pairs.append((i, j))
290
+
291
+ # Collect topology information
292
+ topology = {
293
+ 'bonds': bonds,
294
+ 'angles': angles,
295
+ 'dihedrals': dihedrals,
296
+ 'nb_pairs': nb_pairs,
297
+ 'hbonds': hbonds,
298
+ 'charges': charges,
299
+ 'cn': cn,
300
+ 'bond_mat': bond_mat,
301
+ 'dist_mat': dist_mat
302
+ }
303
+
304
+ return topology
305
+
306
+ def gfnff_bond_hessian(self, coord, element_list, topology):
307
+ """
308
+ Calculate bond stretching contributions to the Hessian
309
+
310
+ Parameters:
311
+ coord: atomic coordinates (Bohr)
312
+ element_list: list of element symbols
313
+ topology: molecular topology information
314
+ """
315
+ bonds = topology['bonds']
316
+ cn = topology['cn']
317
+ n_atoms = len(coord)
318
+
319
+ for i, j in bonds:
320
+ r_vec = coord[j] - coord[i]
321
+ r_ij = np.linalg.norm(r_vec)
322
+
323
+ # Get base bond parameters (r0 in bohr, k in atomic units)
324
+ r0, k_bond = self.params.get_bond_params(element_list[i], element_list[j])
325
+
326
+ # Apply CN-dependent scaling (as in GFNFF)
327
+ cn_ref_i = self.params.ref_cn.get(element_list[i], 1.0)
328
+ cn_ref_j = self.params.ref_cn.get(element_list[j], 1.0)
329
+
330
+ cn_factor = np.exp(-self.params.bond_decay * ((cn[i] - cn_ref_i)**2 + (cn[j] - cn_ref_j)**2))
331
+ force_const = k_bond * cn_factor * self.params.bond_scaling
332
+
333
+ # Calculate unit vector and projection operator
334
+ if r_ij > 1e-10:
335
+ u_ij = r_vec / r_ij
336
+ proj_op = np.outer(u_ij, u_ij)
337
+ else:
338
+ # Avoid division by zero
339
+ proj_op = np.eye(3) / 3.0
340
+
341
+ # Calculate force constant with exponential term for deviation from equilibrium
342
+ # (GFNFF uses a Morse-like function for bonds, but here we use a simpler approach)
343
+ exp_factor = np.exp(-2.0 * (r_ij - r0)**2)
344
+ force_const *= exp_factor
345
+
346
+ # Calculate Hessian blocks
347
+ h_diag = force_const * proj_op
348
+
349
+ # Add to Cartesian Hessian
350
+ for n in range(3):
351
+ for m in range(3):
352
+ self.cart_hess[3*i+n, 3*i+m] += h_diag[n, m]
353
+ self.cart_hess[3*j+n, 3*j+m] += h_diag[n, m]
354
+ self.cart_hess[3*i+n, 3*j+m] -= h_diag[n, m]
355
+ self.cart_hess[3*j+n, 3*i+m] -= h_diag[n, m]
356
+
357
+ def gfnff_angle_hessian(self, coord, element_list, topology):
358
+ """
359
+ Calculate angle bending contributions to the Hessian
360
+
361
+ Parameters:
362
+ coord: atomic coordinates (Bohr)
363
+ element_list: list of element symbols
364
+ topology: molecular topology information
365
+ """
366
+ angles = topology['angles']
367
+ cn = topology['cn']
368
+ n_atoms = len(coord)
369
+
370
+ for i, j, k in angles:
371
+ # Calculate vectors and distances
372
+ r_ji = coord[i] - coord[j]
373
+ r_jk = coord[k] - coord[j]
374
+ r_ji_len = np.linalg.norm(r_ji)
375
+ r_jk_len = np.linalg.norm(r_jk)
376
+
377
+ # Skip if atoms are too close
378
+ if r_ji_len < 1e-10 or r_jk_len < 1e-10:
379
+ continue
380
+
381
+ # Calculate angle
382
+ cos_theta = np.dot(r_ji, r_jk) / (r_ji_len * r_jk_len)
383
+ cos_theta = np.clip(cos_theta, -0.999999, 0.999999) # Avoid numerical issues
384
+ theta = np.arccos(cos_theta)
385
+
386
+ # Get angle parameters (theta0 in degrees, k in atomic units)
387
+ theta0, k_angle = self.params.get_angle_params(element_list[i], element_list[j], element_list[k])
388
+ theta0 = theta0 * np.pi / 180.0 # Convert to radians
389
+
390
+ # Apply coordination number scaling
391
+ cn_ref_j = self.params.ref_cn.get(element_list[j], 1.0)
392
+ cn_factor = np.exp(-0.1 * (cn[j] - cn_ref_j)**2)
393
+ force_const = k_angle * cn_factor
394
+
395
+ # Calculate unit vectors
396
+ u_ji = r_ji / r_ji_len
397
+ u_jk = r_jk / r_jk_len
398
+
399
+ # Calculate derivatives of the angle w.r.t. Cartesian coordinates
400
+ # This is a simplified approach for the Hessian
401
+ p_i = (u_ji - cos_theta * u_jk) / (r_ji_len * np.sin(theta))
402
+ p_k = (u_jk - cos_theta * u_ji) / (r_jk_len * np.sin(theta))
403
+ p_j = -p_i - p_k
404
+
405
+ # Build derivative vectors
406
+ deriv_i = p_i
407
+ deriv_j = p_j
408
+ deriv_k = p_k
409
+
410
+ # Calculate Hessian blocks (simplified approach)
411
+ for a in range(3):
412
+ for b in range(3):
413
+ # i-i block
414
+ self.cart_hess[3*i+a, 3*i+b] += force_const * deriv_i[a] * deriv_i[b]
415
+
416
+ # j-j block
417
+ self.cart_hess[3*j+a, 3*j+b] += force_const * deriv_j[a] * deriv_j[b]
418
+
419
+ # k-k block
420
+ self.cart_hess[3*k+a, 3*k+b] += force_const * deriv_k[a] * deriv_k[b]
421
+
422
+ # Cross terms
423
+ self.cart_hess[3*i+a, 3*j+b] += force_const * deriv_i[a] * deriv_j[b]
424
+ self.cart_hess[3*i+a, 3*k+b] += force_const * deriv_i[a] * deriv_k[b]
425
+ self.cart_hess[3*j+a, 3*i+b] += force_const * deriv_j[a] * deriv_i[b]
426
+ self.cart_hess[3*j+a, 3*k+b] += force_const * deriv_j[a] * deriv_k[b]
427
+ self.cart_hess[3*k+a, 3*i+b] += force_const * deriv_k[a] * deriv_i[b]
428
+ self.cart_hess[3*k+a, 3*j+b] += force_const * deriv_k[a] * deriv_j[b]
429
+
430
+ def gfnff_torsion_hessian(self, coord, element_list, topology):
431
+ """
432
+ Calculate torsion (dihedral) contributions to the Hessian
433
+
434
+ Parameters:
435
+ coord: atomic coordinates (Bohr)
436
+ element_list: list of element symbols
437
+ topology: molecular topology information
438
+ """
439
+ dihedrals = topology['dihedrals']
440
+ cn = topology['cn']
441
+ n_atoms = len(coord)
442
+
443
+ for i, j, k, l in dihedrals:
444
+ # Calculate vectors along the bonds
445
+ r_ij = coord[j] - coord[i]
446
+ r_jk = coord[k] - coord[j]
447
+ r_kl = coord[l] - coord[k]
448
+
449
+ # Calculate cross products for the dihedral
450
+ n1 = np.cross(r_ij, r_jk)
451
+ n2 = np.cross(r_jk, r_kl)
452
+
453
+ # Skip if any of the cross products are too small
454
+ n1_norm = np.linalg.norm(n1)
455
+ n2_norm = np.linalg.norm(n2)
456
+ r_jk_norm = np.linalg.norm(r_jk)
457
+
458
+ if n1_norm < 1e-10 or n2_norm < 1e-10 or r_jk_norm < 1e-10:
459
+ continue
460
+
461
+ # Calculate the dihedral angle
462
+ cos_phi = np.dot(n1, n2) / (n1_norm * n2_norm)
463
+ cos_phi = np.clip(cos_phi, -0.999999, 0.999999) # Avoid numerical issues
464
+
465
+ sin_phi = np.dot(np.cross(n1, n2), r_jk) / (n1_norm * n2_norm * r_jk_norm)
466
+ phi = np.arctan2(sin_phi, cos_phi)
467
+
468
+ # Get torsion parameters (V1, V2, V3 in hartree)
469
+ v1, v2, v3 = self.params.get_torsion_params(
470
+ element_list[i], element_list[j], element_list[k], element_list[l]
471
+ )
472
+
473
+ # Apply coordination number scaling (simplified)
474
+ cn_ref_j = self.params.ref_cn.get(element_list[j], 1.0)
475
+ cn_ref_k = self.params.ref_cn.get(element_list[k], 1.0)
476
+ cn_factor = np.exp(-0.05 * ((cn[j] - cn_ref_j)**2 + (cn[k] - cn_ref_k)**2))
477
+
478
+ v1 *= cn_factor
479
+ v2 *= cn_factor
480
+ v3 *= cn_factor
481
+
482
+ # Calculate forces based on derivatives of the potential
483
+ # V = v1*(1+cos(phi)) + v2*(1+cos(2*phi)) + v3*(1+cos(3*phi-pi))
484
+ f1 = -v1 * np.sin(phi)
485
+ f2 = -2 * v2 * np.sin(2*phi)
486
+ f3 = -3 * v3 * np.sin(3*phi)
487
+
488
+ force = f1 + f2 + f3
489
+
490
+ # Calculate second derivatives (simplified for Hessian)
491
+ # This is a simplified approach; a complete implementation would
492
+ # include all second derivatives of the torsion angles
493
+ k2 = v1 * np.cos(phi) + 4 * v2 * np.cos(2*phi) + 9 * v3 * np.cos(3*phi)
494
+
495
+ # Calculate derivatives of phi w.r.t. Cartesian coordinates
496
+ # (Simplified approach - for a full treatment, see the properly derived formulas)
497
+ # This approximation might not be accurate for all geometries
498
+
499
+ # Normalize bond vectors
500
+ e_ij = r_ij / np.linalg.norm(r_ij)
501
+ e_jk = r_jk / r_jk_norm
502
+ e_kl = r_kl / np.linalg.norm(r_kl)
503
+
504
+ # Normalized cross products
505
+ n1 = n1 / n1_norm
506
+ n2 = n2 / n2_norm
507
+
508
+ # Calculate derivatives (simplified)
509
+ # For a complete treatment, derive the derivatives of the dihedral angle
510
+ # with respect to all atomic positions
511
+
512
+ # Apply a simple projection approach for the derivatives
513
+ # These are simplified derivatives and not analytically correct for all geometries
514
+ deriv_i = np.cross(e_ij, n1) / np.linalg.norm(r_ij)
515
+ deriv_l = np.cross(n2, e_kl) / np.linalg.norm(r_kl)
516
+ deriv_j = -deriv_i - np.cross(e_jk, n1) / np.linalg.norm(r_ij)
517
+ deriv_k = -deriv_l - np.cross(n2, e_jk) / np.linalg.norm(r_kl)
518
+
519
+ # Scale derivatives by force constant
520
+ deriv_i *= force
521
+ deriv_j *= force
522
+ deriv_k *= force
523
+ deriv_l *= force
524
+
525
+ # Add to Cartesian Hessian
526
+ # This is a very simplified Hessian contribution
527
+ atoms = [i, j, k, l]
528
+ derivs = [deriv_i, deriv_j, deriv_k, deriv_l]
529
+
530
+ for m in range(4):
531
+ for n in range(4):
532
+ if m <= n: # Only calculate upper triangular part
533
+ for a in range(3):
534
+ for b in range(3):
535
+ self.cart_hess[3*atoms[m]+a, 3*atoms[n]+b] += k2 * derivs[m][a] * derivs[n][b]
536
+
537
+ # Make Hessian symmetric
538
+ for m in range(3*n_atoms):
539
+ for n in range(m):
540
+ self.cart_hess[m, n] = self.cart_hess[n, m]
541
+
542
+ def gfnff_hbond_hessian(self, coord, element_list, topology):
543
+ """
544
+ Calculate hydrogen bond contributions to the Hessian
545
+
546
+ Parameters:
547
+ coord: atomic coordinates (Bohr)
548
+ element_list: list of element symbols
549
+ topology: molecular topology information
550
+ """
551
+ hbonds = topology['hbonds']
552
+
553
+ for donor_idx, h_idx, acceptor_idx in hbonds:
554
+ # Get atom elements
555
+ donor_element = element_list[donor_idx]
556
+ acceptor_element = element_list[acceptor_idx]
557
+
558
+ # Get H-bond parameters
559
+ r0_hb, k_hb = self.params.get_hbond_params(donor_element, 'H', acceptor_element)
560
+
561
+ # Calculate vectors and distances
562
+ r_dh = coord[h_idx] - coord[donor_idx]
563
+ r_ha = coord[acceptor_idx] - coord[h_idx]
564
+ r_dh_len = np.linalg.norm(r_dh)
565
+ r_ha_len = np.linalg.norm(r_ha)
566
+
567
+ # Skip if atoms are too close
568
+ if r_dh_len < 1e-10 or r_ha_len < 1e-10:
569
+ continue
570
+
571
+ # Calculate angle
572
+ cos_angle = np.dot(r_dh, r_ha) / (r_dh_len * r_ha_len)
573
+ cos_angle = np.clip(cos_angle, -0.999999, 0.999999) # Avoid numerical issues
574
+ angle = np.arccos(cos_angle)
575
+
576
+ # H-bond force constant depends on distance and angle
577
+ # Optimal H-bond is linear (180 degrees) and at equilibrium distance
578
+
579
+ # Distance-dependent term
580
+ dist_factor = np.exp(-(r_ha_len - r0_hb)**2 / (2.0 * 0.3**2)) # Gaussian shape
581
+
582
+ # Angle-dependent term (preference for linear H-bonds)
583
+ angle_factor = (1.0 + np.cos(angle - np.pi))**2 / 4.0 # Peaks at 180 degrees
584
+
585
+ # Combined force constant
586
+ force_const = k_hb * dist_factor * angle_factor
587
+
588
+ # Calculate unit vectors for H-A bond
589
+ u_ha = r_ha / r_ha_len
590
+ proj_op = np.outer(u_ha, u_ha)
591
+
592
+ # Calculate Hessian blocks for H-bond stretching
593
+ # This is a simplified approach focusing on the H-A distance
594
+ for n in range(3):
595
+ for m in range(3):
596
+ self.cart_hess[3*h_idx+n, 3*h_idx+m] += force_const * proj_op[n, m]
597
+ self.cart_hess[3*acceptor_idx+n, 3*acceptor_idx+m] += force_const * proj_op[n, m]
598
+ self.cart_hess[3*h_idx+n, 3*acceptor_idx+m] -= force_const * proj_op[n, m]
599
+ self.cart_hess[3*acceptor_idx+n, 3*h_idx+m] -= force_const * proj_op[n, m]
600
+
601
+ # Angle component is more complex and not included in this simplified model
602
+
603
+ def gfnff_nonbonded_hessian(self, coord, element_list, topology):
604
+ """
605
+ Calculate non-bonded interactions (dispersion and repulsion) to the Hessian
606
+
607
+ Parameters:
608
+ coord: atomic coordinates (Bohr)
609
+ element_list: list of element symbols
610
+ topology: molecular topology information
611
+ """
612
+ n_atoms = len(coord)
613
+ nb_pairs = topology['nb_pairs']
614
+ charges = topology['charges']
615
+
616
+ for i, j in nb_pairs:
617
+ # Calculate distance vector and magnitude
618
+ r_vec = coord[i] - coord[j]
619
+ r_ij = np.linalg.norm(r_vec)
620
+
621
+ # Skip if atoms are too close
622
+ if r_ij < 0.1:
623
+ continue
624
+
625
+ # Get element properties
626
+ alpha_i = self.params.get_polarizability(element_list[i])
627
+ alpha_j = self.params.get_polarizability(element_list[j])
628
+
629
+ # Calculate simple dispersion C6 coefficient (simplified DFT-D3 style)
630
+ c6_ij = 2.0 * alpha_i * alpha_j / (alpha_i/alpha_j + alpha_j/alpha_i) * 0.05
631
+
632
+ # Calculate van der Waals radii
633
+ vdw_i = self.params.get_vdw_radius(element_list[i]) / self.bohr2angstroms
634
+ vdw_j = self.params.get_vdw_radius(element_list[j]) / self.bohr2angstroms
635
+ vdw_sum = vdw_i + vdw_j
636
+
637
+ # Calculate dispersion energy and derivatives
638
+ # Repulsive term: simple exponential
639
+ rep_const = 0.3 # Repulsion strength
640
+ rep_energy = rep_const * np.exp(-(r_ij/vdw_sum - 0.6) * 12.0)
641
+ rep_deriv = -12.0 * rep_const * np.exp(-(r_ij/vdw_sum - 0.6) * 12.0) * (1.0/vdw_sum) / r_ij
642
+
643
+ # Attractive dispersion term (D3-like with BJ-damping)
644
+ r0_ij = 0.5 * vdw_sum
645
+ a1, a2 = 0.4, 3.0 # BJ-damping parameters
646
+ damp_factor = r_ij**6 / (r_ij**6 + (a1*r0_ij + a2)**6)
647
+ disp_energy = -self.params.d4_s6 * c6_ij * damp_factor / r_ij**6
648
+
649
+ # Compute the derivative of the damping factor
650
+ damp_deriv = 6 * r_ij**5 * (a1*r0_ij + a2)**6 / (r_ij**6 + (a1*r0_ij + a2)**6)**2
651
+ disp_deriv = self.params.d4_s6 * c6_ij * (6 * damp_factor / r_ij**7 - damp_deriv / r_ij**6)
652
+
653
+ # Total force
654
+ force = rep_deriv + disp_deriv
655
+
656
+ # Calculate unit vector and projection operator
657
+ u_ij = r_vec / r_ij
658
+ proj_op = np.outer(u_ij, u_ij)
659
+
660
+ # Simplified second derivatives for Hessian
661
+ # This is not analytically correct but provides an approximation
662
+ # For correct treatment, one would need the full second derivatives
663
+ hess_factor = (force / r_ij) + 0.2 # Adding a small positive constant for stability
664
+
665
+ # Add to Cartesian Hessian
666
+ for n in range(3):
667
+ for m in range(3):
668
+ self.cart_hess[3*i+n, 3*i+m] += hess_factor * proj_op[n, m]
669
+ self.cart_hess[3*j+n, 3*j+m] += hess_factor * proj_op[n, m]
670
+ self.cart_hess[3*i+n, 3*j+m] -= hess_factor * proj_op[n, m]
671
+ self.cart_hess[3*j+n, 3*i+m] -= hess_factor * proj_op[n, m]
672
+
673
+ def main(self, coord, element_list, cart_gradient):
674
+ """
675
+ Calculate Hessian using GFNFF-based model
676
+
677
+ Parameters:
678
+ coord: Atomic coordinates (N×3 array, Bohr)
679
+ element_list: List of element symbols
680
+ cart_gradient: Gradient in Cartesian coordinates
681
+
682
+ Returns:
683
+ hess_proj: Hessian with rotational and translational modes projected out
684
+ """
685
+ print("Generating Hessian using GFNFF model...")
686
+
687
+ # Initialize Hessian matrix
688
+ n_atoms = len(coord)
689
+ self.cart_hess = np.zeros((n_atoms*3, n_atoms*3), dtype="float64")
690
+
691
+ # Build molecular topology
692
+ topology = self.build_topology(coord, element_list)
693
+
694
+ # Calculate different Hessian components
695
+ self.gfnff_bond_hessian(coord, element_list, topology)
696
+ self.gfnff_angle_hessian(coord, element_list, topology)
697
+ self.gfnff_torsion_hessian(coord, element_list, topology)
698
+ self.gfnff_hbond_hessian(coord, element_list, topology)
699
+ self.gfnff_nonbonded_hessian(coord, element_list, topology)
700
+
701
+ # Symmetrize the Hessian matrix
702
+ for i in range(n_atoms*3):
703
+ for j in range(i):
704
+ self.cart_hess[j, i] = self.cart_hess[i, j]
705
+
706
+ # Project out rotational and translational modes
707
+ hess_proj = Calculationtools().project_out_hess_tr_and_rot_for_coord(self.cart_hess, element_list, coord)
708
+
709
+ return hess_proj