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,559 @@
1
+ import numpy as np
2
+ import itertools
3
+
4
+ from multioptpy.Parameters.parameter import UnitValueLib, covalent_radii_lib, UFF_VDW_distance_lib, D4Parameters, triple_covalent_radii_lib
5
+ from multioptpy.Utils.calc_tools import Calculationtools
6
+ from multioptpy.Parameters.parameter import UFF_VDW_distance_lib, number_element
7
+ from multioptpy.Coordinate.redundant_coordinate import RedundantInternalCoordinates
8
+ from multioptpy.Utils.bond_connectivity import BondConnectivity
9
+
10
+ class SchlegelD4ApproxHessian:
11
+ def __init__(self):
12
+ """
13
+ Schlegel's approximate Hessian with D4 dispersion corrections and special handling for cyano groups
14
+ References:
15
+ - Schlegel: Journal of Molecular Structure: THEOCHEM Volumes 398–399, 30 June 1997, Pages 55-61
16
+ - D4: E. Caldeweyher, C. Bannwarth, S. Grimme, J. Chem. Phys., 2017, 147, 034112
17
+ """
18
+ self.bohr2angstroms = UnitValueLib().bohr2angstroms
19
+ self.hartree2kcalmol = UnitValueLib().hartree2kcalmol
20
+
21
+ # D4 dispersion parameters
22
+ self.d4_params = D4Parameters()
23
+
24
+ # Cyano group parameters - enhanced force constants
25
+ self.cn_stretch_factor = 2.0 # Enhance stretch force constants for C≡N triple bond
26
+ self.cn_angle_factor = 1.5 # Enhance angle force constants involving C≡N
27
+ self.cn_torsion_factor = 0.5 # Reduce torsion force constants involving C≡N (more flexible)
28
+
29
+ def detect_cyano_groups(self, coord, element_list):
30
+ """Detect C≡N triple bonds in the structure"""
31
+ cyano_atoms = [] # List of (C_idx, N_idx) tuples
32
+
33
+ for i in range(len(coord)):
34
+ if element_list[i] != 'C':
35
+ continue
36
+
37
+ for j in range(len(coord)):
38
+ if i == j or element_list[j] != 'N':
39
+ continue
40
+
41
+ # Calculate distance between C and N
42
+ r_ij = np.linalg.norm(coord[i] - coord[j])
43
+
44
+ # Check if distance is close to a triple bond length
45
+ cn_triple_bond = triple_covalent_radii_lib('C') + triple_covalent_radii_lib('N')
46
+
47
+ if abs(r_ij - cn_triple_bond) < 0.3: # Within 0.3 bohr of ideal length
48
+ # Check if C is connected to only one other atom (besides N)
49
+ connections_to_c = 0
50
+ for k in range(len(coord)):
51
+ if k == i or k == j:
52
+ continue
53
+
54
+ r_ik = np.linalg.norm(coord[i] - coord[k])
55
+ cov_dist = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[k])
56
+
57
+ if r_ik < 1.3 * cov_dist: # Using 1.3 as a factor to account for bond length variations
58
+ connections_to_c += 1
59
+
60
+ # If C has only one other connection, it's likely a terminal cyano group
61
+ if connections_to_c <= 1:
62
+ cyano_atoms.append((i, j))
63
+
64
+ return cyano_atoms
65
+
66
+ def calculate_coordination_numbers(self, coord, element_list):
67
+ """Calculate atomic coordination numbers for D4 scaling"""
68
+ n_atoms = len(coord)
69
+ cn = np.zeros(n_atoms)
70
+
71
+ for i in range(n_atoms):
72
+ for j in range(n_atoms):
73
+ if i == j:
74
+ continue
75
+
76
+ # Calculate distance
77
+ r_ij = np.linalg.norm(coord[i] - coord[j])
78
+
79
+ # Get covalent radii
80
+ r_cov_i = covalent_radii_lib(element_list[i])
81
+ r_cov_j = covalent_radii_lib(element_list[j])
82
+
83
+ # Coordination number contribution using counting function
84
+ # k1 = 16.0, k2 = 4.0/3.0 (standard values from DFT-D4)
85
+ k1 = 16.0
86
+ k2 = 4.0/3.0
87
+ r0 = r_cov_i + r_cov_j
88
+
89
+ # Avoid overflow in exp
90
+ if k1 * (r_ij / r0 - 1.0) > 25.0:
91
+ continue
92
+
93
+ cn_contrib = 1.0 / (1.0 + np.exp(-k1 * (k2 * r0 / r_ij - 1.0)))
94
+ cn[i] += cn_contrib
95
+
96
+ return cn
97
+
98
+ def estimate_atomic_charges(self, coord, element_list):
99
+ """
100
+ Estimate atomic charges using electronegativity equalization
101
+ Simplified version for Hessian generation
102
+ """
103
+ n_atoms = len(coord)
104
+ charges = np.zeros(n_atoms)
105
+
106
+ # Calculate reference electronegativities
107
+ en_list = [self.d4_params.get_electronegativity(elem) for elem in element_list]
108
+
109
+ # Simple charge estimation based on electronegativity differences
110
+ # This is a very simplified model
111
+ for i in range(n_atoms):
112
+ for j in range(i+1, n_atoms):
113
+ r_ij = np.linalg.norm(coord[i] - coord[j])
114
+ r_cov_sum = covalent_radii_lib(element_list[i]) + covalent_radii_lib(element_list[j])
115
+
116
+ # Only consider bonded atoms
117
+ if r_ij < 1.3 * r_cov_sum:
118
+ en_diff = en_list[j] - en_list[i]
119
+ charge_transfer = 0.1 * en_diff # Simple approximation
120
+ charges[i] += charge_transfer
121
+ charges[j] -= charge_transfer
122
+
123
+ return charges
124
+
125
+ def return_schlegel_const(self, element_1, element_2):
126
+ """Return Schlegel's constant for a given element pair"""
127
+ if type(element_1) is int:
128
+ element_1 = number_element(element_1)
129
+ if type(element_2) is int:
130
+ element_2 = number_element(element_2)
131
+
132
+ parameter_B_matrix = [
133
+ [0.2573, 0.3401, 0.6937, 0.7126, 0.8335, 0.9491, 0.9491],
134
+ [0.3401, 0.9652, 1.2843, 1.4725, 1.6549, 1.7190, 1.7190],
135
+ [0.6937, 1.2843, 1.6925, 1.8238, 2.1164, 2.3185, 2.3185],
136
+ [0.7126, 1.4725, 1.8238, 2.0203, 2.2137, 2.5206, 2.5206],
137
+ [0.8335, 1.6549, 2.1164, 2.2137, 2.3718, 2.5110, 2.5110],
138
+ [0.9491, 1.7190, 2.3185, 2.5206, 2.5110, 2.5110, 2.5110],
139
+ [0.9491, 1.7190, 2.3185, 2.5206, 2.5110, 2.5110, 2.5110]
140
+ ] # Bohr
141
+
142
+ first_period_table = ["H", "He"]
143
+ second_period_table = ["Li", "Be", "B", "C", "N", "O", "F", "Ne"]
144
+ third_period_table = ["Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar"]
145
+ fourth_period_table = ["K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br","Kr"]
146
+ fifth_period_table = ["Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc","Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te","I", "Xe"]
147
+ sixth_period_table = ["Cs", "Ba", "La","Ce","Pr","Nd","Pm","Sm", "Eu", "Gd", "Tb", "Dy" ,"Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn"]
148
+
149
+ if element_1 in first_period_table:
150
+ idx_1 = 0
151
+ elif element_1 in second_period_table:
152
+ idx_1 = 1
153
+ elif element_1 in third_period_table:
154
+ idx_1 = 2
155
+ elif element_1 in fourth_period_table:
156
+ idx_1 = 3
157
+ elif element_1 in fifth_period_table:
158
+ idx_1 = 4
159
+ elif element_1 in sixth_period_table:
160
+ idx_1 = 5
161
+ else:
162
+ idx_1 = 6
163
+
164
+ if element_2 in first_period_table:
165
+ idx_2 = 0
166
+ elif element_2 in second_period_table:
167
+ idx_2 = 1
168
+ elif element_2 in third_period_table:
169
+ idx_2 = 2
170
+ elif element_2 in fourth_period_table:
171
+ idx_2 = 3
172
+ elif element_2 in fifth_period_table:
173
+ idx_2 = 4
174
+ elif element_2 in sixth_period_table:
175
+ idx_2 = 5
176
+ else:
177
+ idx_2 = 6
178
+
179
+ const_b = parameter_B_matrix[idx_1][idx_2]
180
+ return const_b
181
+
182
+ def d4_damping_function(self, r_ij, r0, order=6):
183
+ """D4 rational damping function"""
184
+ a1 = self.d4_params.a1
185
+ a2 = self.d4_params.a2
186
+
187
+ if order == 6:
188
+ return 1.0 / (1.0 + 6.0 * (r_ij / (a1 * r0)) ** a2)
189
+ elif order == 8:
190
+ return 1.0 / (1.0 + 6.0 * (r_ij / (a2 * r0)) ** a1)
191
+ return 0.0
192
+
193
+ def charge_scale_factor(self, charge, element):
194
+ """Calculate charge scaling factor for D4"""
195
+ ga = self.d4_params.ga # D4 charge scaling parameter (default=3.0)
196
+ q_ref = self.d4_params.get_electronegativity(element)
197
+
198
+ # Prevent numerical issues with large exponents
199
+ exp_arg = -ga * abs(charge)
200
+ if exp_arg < -50.0: # Avoid underflow
201
+ return 0.0
202
+
203
+ return np.exp(exp_arg)
204
+
205
+ def get_d4_parameters(self, elem1, elem2, q1=0.0, q2=0.0, cn1=None, cn2=None):
206
+ """Get D4 parameters for a pair of elements with charge scaling"""
207
+ # Get polarizabilities
208
+ alpha1 = self.d4_params.get_polarizability(elem1)
209
+ alpha2 = self.d4_params.get_polarizability(elem2)
210
+
211
+ # Charge scaling
212
+ qscale1 = self.charge_scale_factor(q1, elem1)
213
+ qscale2 = self.charge_scale_factor(q2, elem2)
214
+
215
+ # Get R4/R2 values
216
+ r4r2_1 = self.d4_params.get_r4r2(elem1)
217
+ r4r2_2 = self.d4_params.get_r4r2(elem2)
218
+
219
+ # C6 coefficients with charge scaling
220
+ c6_1 = alpha1 * r4r2_1 * qscale1
221
+ c6_2 = alpha2 * r4r2_2 * qscale2
222
+ c6_param = 2.0 * c6_1 * c6_2 / (c6_1 + c6_2) # Effective C6 using harmonic mean
223
+
224
+ # C8 coefficients
225
+ c8_param = 3.0 * c6_param * np.sqrt(r4r2_1 * r4r2_2)
226
+
227
+ # r0 parameter (combined vdW radii)
228
+ r0_param = np.sqrt(UFF_VDW_distance_lib(elem1) * UFF_VDW_distance_lib(elem2))
229
+
230
+ return c6_param, c8_param, r0_param
231
+
232
+ def calc_d4_correction(self, r_ij, elem1, elem2, q1=0.0, q2=0.0, cn1=None, cn2=None):
233
+ """Calculate D4 dispersion correction to the force constant"""
234
+ # Get D4 parameters with charge scaling
235
+ c6_param, c8_param, r0_param = self.get_d4_parameters(
236
+ elem1, elem2, q1=q1, q2=q2, cn1=cn1, cn2=cn2
237
+ )
238
+
239
+ # Damping functions
240
+ damp6 = self.d4_damping_function(r_ij, r0_param, order=6)
241
+ damp8 = self.d4_damping_function(r_ij, r0_param, order=8)
242
+
243
+ # D4 energy contribution
244
+ s6 = self.d4_params.s6
245
+ s8 = self.d4_params.s8
246
+ e_disp = -s6 * c6_param / r_ij**6 * damp6 - s8 * c8_param / r_ij**8 * damp8
247
+
248
+ # Approximate second derivative (force constant)
249
+ fc_disp = s6 * c6_param * (42.0 / r_ij**8) * damp6 + s8 * c8_param * (72.0 / r_ij**10) * damp8
250
+
251
+ return fc_disp * 0.01 # Scale factor to match overall Hessian scale
252
+
253
+ def calculate_three_body_term(self, coord, element_list, charges, cn):
254
+ """Calculate three-body dispersion contribution"""
255
+ n_atoms = len(coord)
256
+ s9 = self.d4_params.s9
257
+
258
+ # Skip if three-body term is turned off
259
+ if abs(s9) < 1e-12:
260
+ return np.zeros((3 * n_atoms, 3 * n_atoms))
261
+
262
+ # Initialize three-body Hessian contribution
263
+ three_body_hess = np.zeros((3 * n_atoms, 3 * n_atoms))
264
+
265
+ # Loop over all atom triplets
266
+ for i in range(n_atoms):
267
+ for j in range(i+1, n_atoms):
268
+ for k in range(j+1, n_atoms):
269
+ # Get positions
270
+ r_i = coord[i]
271
+ r_j = coord[j]
272
+ r_k = coord[k]
273
+
274
+ # Calculate interatomic distances
275
+ r_ij = np.linalg.norm(r_i - r_j)
276
+ r_jk = np.linalg.norm(r_j - r_k)
277
+ r_ki = np.linalg.norm(r_k - r_i)
278
+
279
+ # Get coordination-number scaled C6 coefficients
280
+ c6_ij, _, r0_ij = self.get_d4_parameters(
281
+ element_list[i], element_list[j],
282
+ q1=charges[i], q2=charges[j],
283
+ cn1=cn[i], cn2=cn[j]
284
+ )
285
+
286
+ c6_jk, _, r0_jk = self.get_d4_parameters(
287
+ element_list[j], element_list[k],
288
+ q1=charges[j], q2=charges[k],
289
+ cn1=cn[j], cn2=cn[k]
290
+ )
291
+
292
+ c6_ki, _, r0_ki = self.get_d4_parameters(
293
+ element_list[k], element_list[i],
294
+ q1=charges[k], q2=charges[i],
295
+ cn1=cn[k], cn2=cn[i]
296
+ )
297
+
298
+ # Calculate geometric mean of C6 coefficients
299
+ c9 = np.cbrt(c6_ij * c6_jk * c6_ki)
300
+
301
+ # Calculate damping
302
+ damp_ij = self.d4_damping_function(r_ij, r0_ij)
303
+ damp_jk = self.d4_damping_function(r_jk, r0_jk)
304
+ damp_ki = self.d4_damping_function(r_ki, r0_ki)
305
+ damp = damp_ij * damp_jk * damp_ki
306
+
307
+ # Skip if damping is too small
308
+ if damp < 1e-8:
309
+ continue
310
+
311
+ # Calculate angle factor
312
+ r_ij_vec = r_j - r_i
313
+ r_jk_vec = r_k - r_j
314
+ r_ki_vec = r_i - r_k
315
+
316
+ cos_ijk = np.dot(r_ij_vec, r_jk_vec) / (r_ij * r_jk)
317
+ cos_jki = np.dot(r_jk_vec, -r_ki_vec) / (r_jk * r_ki)
318
+ cos_kij = np.dot(-r_ki_vec, r_ij_vec) / (r_ki * r_ij)
319
+
320
+ angle_factor = 1.0 + 3.0 * cos_ijk * cos_jki * cos_kij
321
+
322
+ # Calculate three-body energy term
323
+ e_3 = -s9 * angle_factor * c9 * damp / (r_ij * r_jk * r_ki) ** 3
324
+
325
+ # Approximate Hessian contribution (simplified)
326
+ # We use a small scaling factor to avoid dominating the Hessian
327
+ fc_scale = 0.002 * s9 * angle_factor * c9 * damp
328
+
329
+ # Add approximate three-body contributions to Hessian
330
+ for n in range(3):
331
+ for m in range(3):
332
+ # Properly define all indices
333
+ idx_i_n = i * 3 + n
334
+ idx_j_n = j * 3 + n
335
+ idx_k_n = k * 3 + n
336
+
337
+ idx_i_m = i * 3 + m
338
+ idx_j_m = j * 3 + m
339
+ idx_k_m = k * 3 + m
340
+
341
+ # Diagonal blocks (diagonal atoms)
342
+ if n == m:
343
+ three_body_hess[idx_i_n, idx_i_n] += fc_scale / (r_ij**6) + fc_scale / (r_ki**6)
344
+ three_body_hess[idx_j_n, idx_j_n] += fc_scale / (r_ij**6) + fc_scale / (r_jk**6)
345
+ three_body_hess[idx_k_n, idx_k_n] += fc_scale / (r_jk**6) + fc_scale / (r_ki**6)
346
+
347
+ # Off-diagonal blocks (between atoms)
348
+ three_body_hess[idx_i_n, idx_j_m] -= fc_scale / (r_ij**6)
349
+ three_body_hess[idx_j_m, idx_i_n] -= fc_scale / (r_ij**6)
350
+
351
+ three_body_hess[idx_j_n, idx_k_m] -= fc_scale / (r_jk**6)
352
+ three_body_hess[idx_k_m, idx_j_n] -= fc_scale / (r_jk**6)
353
+
354
+ three_body_hess[idx_k_n, idx_i_m] -= fc_scale / (r_ki**6)
355
+ three_body_hess[idx_i_m, idx_k_n] -= fc_scale / (r_ki**6)
356
+
357
+ return three_body_hess
358
+
359
+ def guess_schlegel_hessian(self, coord, element_list, charges, cn):
360
+ """
361
+ Calculate approximate Hessian using Schlegel's approach augmented with D4 dispersion
362
+ and special handling for cyano groups
363
+ """
364
+ # Detect cyano groups
365
+ cyano_atoms = self.detect_cyano_groups(coord, element_list)
366
+ cyano_set = set()
367
+ for c_idx, n_idx in cyano_atoms:
368
+ cyano_set.add(c_idx)
369
+ cyano_set.add(n_idx)
370
+
371
+ # Setup connectivity tables using BondConnectivity utility
372
+ BC = BondConnectivity()
373
+ b_c_mat = BC.bond_connect_matrix(element_list, coord)
374
+ connectivity_table = [BC.bond_connect_table(b_c_mat),
375
+ BC.angle_connect_table(b_c_mat),
376
+ BC.dihedral_angle_connect_table(b_c_mat)]
377
+
378
+ # Initialize RIC index list for all atom pairs
379
+ RIC_idx_list = [[i[0], i[1]] for i in itertools.combinations(range(len(coord)), 2)]
380
+ self.RIC_variable_num = len(RIC_idx_list)
381
+ RIC_approx_diag_hessian = [0.0] * self.RIC_variable_num
382
+
383
+ # Process connectivity table to build Hessian
384
+ for idx_list in connectivity_table:
385
+ for idx in idx_list:
386
+ # Bond stretching terms
387
+ if len(idx) == 2:
388
+ tmp_idx = sorted([idx[0], idx[1]])
389
+ distance = np.linalg.norm(coord[idx[0]] - coord[idx[1]])
390
+
391
+ elem_1 = element_list[idx[0]]
392
+ elem_2 = element_list[idx[1]]
393
+ const_b = self.return_schlegel_const(elem_1, elem_2)
394
+ tmpnum = RIC_idx_list.index(tmp_idx)
395
+
396
+ # Base Schlegel force constant
397
+ F = 1.734 / (distance - const_b) ** 3
398
+
399
+ # Check if this is a cyano bond
400
+ is_cyano_bond = False
401
+ for c_idx, n_idx in cyano_atoms:
402
+ if (idx[0] == c_idx and idx[1] == n_idx) or (idx[0] == n_idx and idx[1] == c_idx):
403
+ is_cyano_bond = True
404
+ break
405
+
406
+ # Add D4 dispersion contribution with charge scaling
407
+ d4_correction = self.calc_d4_correction(
408
+ distance, elem_1, elem_2,
409
+ q1=charges[idx[0]], q2=charges[idx[1]],
410
+ cn1=cn[idx[0]], cn2=cn[idx[1]]
411
+ )
412
+
413
+ if is_cyano_bond:
414
+ # Enhanced force constant for C≡N triple bond
415
+ RIC_approx_diag_hessian[tmpnum] += self.cn_stretch_factor * F + d4_correction
416
+ else:
417
+ RIC_approx_diag_hessian[tmpnum] += F + d4_correction
418
+
419
+ # Angle bending terms
420
+ elif len(idx) == 3:
421
+ tmp_idx_1 = sorted([idx[0], idx[1]])
422
+ tmp_idx_2 = sorted([idx[1], idx[2]])
423
+ elem_1 = element_list[idx[0]]
424
+ elem_2 = element_list[idx[1]]
425
+ elem_3 = element_list[idx[2]]
426
+ tmpnum_1 = RIC_idx_list.index(tmp_idx_1)
427
+ tmpnum_2 = RIC_idx_list.index(tmp_idx_2)
428
+
429
+ # Check if angle involves cyano group
430
+ is_cyano_angle = (idx[0] in cyano_set or idx[1] in cyano_set or idx[2] in cyano_set)
431
+
432
+ # Base Schlegel force constant
433
+ if elem_1 == "H" or elem_3 == "H":
434
+ F_angle = 0.160
435
+ else:
436
+ F_angle = 0.250
437
+
438
+ # Add D4 dispersion contribution with charge scaling
439
+ d3_r1 = np.linalg.norm(coord[idx[0]] - coord[idx[1]])
440
+ d3_r2 = np.linalg.norm(coord[idx[1]] - coord[idx[2]])
441
+
442
+ d4_correction_1 = self.calc_d4_correction(
443
+ d3_r1, elem_1, elem_2,
444
+ q1=charges[idx[0]], q2=charges[idx[1]],
445
+ cn1=cn[idx[0]], cn2=cn[idx[1]]
446
+ ) * 0.2
447
+
448
+ d4_correction_2 = self.calc_d4_correction(
449
+ d3_r2, elem_2, elem_3,
450
+ q1=charges[idx[1]], q2=charges[idx[2]],
451
+ cn1=cn[idx[1]], cn2=cn[idx[2]]
452
+ ) * 0.2
453
+
454
+ if is_cyano_angle:
455
+ # Enhanced angle force constants for angles involving C≡N
456
+ RIC_approx_diag_hessian[tmpnum_1] += self.cn_angle_factor * F_angle + d4_correction_1
457
+ RIC_approx_diag_hessian[tmpnum_2] += self.cn_angle_factor * F_angle + d4_correction_2
458
+ else:
459
+ RIC_approx_diag_hessian[tmpnum_1] += F_angle + d4_correction_1
460
+ RIC_approx_diag_hessian[tmpnum_2] += F_angle + d4_correction_2
461
+
462
+ # Torsion (dihedral) terms
463
+ elif len(idx) == 4:
464
+ tmp_idx_1 = sorted([idx[0], idx[1]])
465
+ tmp_idx_2 = sorted([idx[1], idx[2]])
466
+ tmp_idx_3 = sorted([idx[2], idx[3]])
467
+ elem_1 = element_list[idx[0]]
468
+ elem_2 = element_list[idx[1]]
469
+ elem_3 = element_list[idx[2]]
470
+ elem_4 = element_list[idx[3]]
471
+ distance = np.linalg.norm(coord[idx[1]] - coord[idx[2]])
472
+ bond_length = covalent_radii_lib(elem_2) + covalent_radii_lib(elem_3)
473
+ tmpnum_1 = RIC_idx_list.index(tmp_idx_1)
474
+ tmpnum_2 = RIC_idx_list.index(tmp_idx_2)
475
+ tmpnum_3 = RIC_idx_list.index(tmp_idx_3)
476
+
477
+ # Base Schlegel torsion force constant
478
+ F_torsion = 0.0023 - 0.07 * (distance - bond_length)
479
+
480
+ # Check if torsion involves cyano group
481
+ is_cyano_torsion = (idx[0] in cyano_set or idx[1] in cyano_set or
482
+ idx[2] in cyano_set or idx[3] in cyano_set)
483
+
484
+ # Add D4 dispersion contribution with charge scaling
485
+ d3_r1 = np.linalg.norm(coord[idx[0]] - coord[idx[1]])
486
+ d3_r2 = np.linalg.norm(coord[idx[1]] - coord[idx[2]])
487
+ d3_r3 = np.linalg.norm(coord[idx[2]] - coord[idx[3]])
488
+
489
+ d4_correction_1 = self.calc_d4_correction(
490
+ d3_r1, elem_1, elem_2,
491
+ q1=charges[idx[0]], q2=charges[idx[1]],
492
+ cn1=cn[idx[0]], cn2=cn[idx[1]]
493
+ ) * 0.05
494
+
495
+ d4_correction_2 = self.calc_d4_correction(
496
+ d3_r2, elem_2, elem_3,
497
+ q1=charges[idx[1]], q2=charges[idx[2]],
498
+ cn1=cn[idx[1]], cn2=cn[idx[2]]
499
+ ) * 0.05
500
+
501
+ d4_correction_3 = self.calc_d4_correction(
502
+ d3_r3, elem_3, elem_4,
503
+ q1=charges[idx[2]], q2=charges[idx[3]],
504
+ cn1=cn[idx[2]], cn2=cn[idx[3]]
505
+ ) * 0.05
506
+
507
+ if is_cyano_torsion:
508
+ # Reduced torsion force constants for torsions involving C≡N
509
+ RIC_approx_diag_hessian[tmpnum_1] += self.cn_torsion_factor * F_torsion + d4_correction_1
510
+ RIC_approx_diag_hessian[tmpnum_2] += self.cn_torsion_factor * F_torsion + d4_correction_2
511
+ RIC_approx_diag_hessian[tmpnum_3] += self.cn_torsion_factor * F_torsion + d4_correction_3
512
+ else:
513
+ RIC_approx_diag_hessian[tmpnum_1] += F_torsion + d4_correction_1
514
+ RIC_approx_diag_hessian[tmpnum_2] += F_torsion + d4_correction_2
515
+ RIC_approx_diag_hessian[tmpnum_3] += F_torsion + d4_correction_3
516
+
517
+ # Convert to numpy array
518
+ RIC_approx_hessian = np.diag(RIC_approx_diag_hessian).astype("float64")
519
+ return RIC_approx_hessian
520
+
521
+ def main(self, coord, element_list, cart_gradient):
522
+ """Main method to calculate the approximate Hessian"""
523
+ print("Generating Schlegel's approximate Hessian with D4 dispersion correction...")
524
+
525
+ # Calculate coordination numbers and atomic charges for D4
526
+ cn = self.calculate_coordination_numbers(coord, element_list)
527
+ charges = self.estimate_atomic_charges(coord, element_list)
528
+
529
+ # Calculate B matrix for redundant internal coordinates
530
+ b_mat = RedundantInternalCoordinates().B_matrix(coord)
531
+ self.RIC_variable_num = len(b_mat)
532
+
533
+ # Calculate approximate Hessian in internal coordinates
534
+ int_approx_hess = self.guess_schlegel_hessian(coord, element_list, charges, cn)
535
+
536
+ # Convert to Cartesian coordinates
537
+ cart_hess = np.dot(b_mat.T, np.dot(int_approx_hess, b_mat))
538
+
539
+ # Add three-body contribution (specific to D4)
540
+ three_body_hess = self.calculate_three_body_term(coord, element_list, charges, cn)
541
+ cart_hess += three_body_hess
542
+
543
+ # Handle NaN values
544
+ cart_hess = np.nan_to_num(cart_hess, nan=0.0)
545
+
546
+ # Ensure Hessian is symmetric
547
+ n = len(coord) * 3
548
+ for i in range(n):
549
+ for j in range(i):
550
+ avg = (cart_hess[i, j] + cart_hess[j, i]) / 2
551
+ cart_hess[i, j] = avg
552
+ cart_hess[j, i] = avg
553
+
554
+ # Project out translational and rotational modes
555
+ hess_proj = Calculationtools().project_out_hess_tr_and_rot_for_coord(cart_hess, element_list, coord)
556
+
557
+ print("D4 dispersion correction applied successfully")
558
+ return hess_proj
559
+