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.
- multioptpy/Calculator/__init__.py +0 -0
- multioptpy/Calculator/ase_calculation_tools.py +424 -0
- multioptpy/Calculator/ase_tools/__init__.py +0 -0
- multioptpy/Calculator/ase_tools/fairchem.py +28 -0
- multioptpy/Calculator/ase_tools/gamess.py +19 -0
- multioptpy/Calculator/ase_tools/gaussian.py +165 -0
- multioptpy/Calculator/ase_tools/mace.py +28 -0
- multioptpy/Calculator/ase_tools/mopac.py +19 -0
- multioptpy/Calculator/ase_tools/nwchem.py +31 -0
- multioptpy/Calculator/ase_tools/orca.py +22 -0
- multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
- multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
- multioptpy/Calculator/emt_calculation_tools.py +458 -0
- multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
- multioptpy/Calculator/lj_calculation_tools.py +314 -0
- multioptpy/Calculator/psi4_calculation_tools.py +334 -0
- multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
- multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
- multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
- multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
- multioptpy/Calculator/tblite_calculation_tools.py +352 -0
- multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
- multioptpy/Constraint/__init__.py +0 -0
- multioptpy/Constraint/constraint_condition.py +834 -0
- multioptpy/Coordinate/__init__.py +0 -0
- multioptpy/Coordinate/polar_coordinate.py +199 -0
- multioptpy/Coordinate/redundant_coordinate.py +638 -0
- multioptpy/IRC/__init__.py +0 -0
- multioptpy/IRC/converge_criteria.py +28 -0
- multioptpy/IRC/dvv.py +544 -0
- multioptpy/IRC/euler.py +439 -0
- multioptpy/IRC/hpc.py +564 -0
- multioptpy/IRC/lqa.py +540 -0
- multioptpy/IRC/modekill.py +662 -0
- multioptpy/IRC/rk4.py +579 -0
- multioptpy/Interpolation/__init__.py +0 -0
- multioptpy/Interpolation/adaptive_interpolation.py +283 -0
- multioptpy/Interpolation/binomial_interpolation.py +179 -0
- multioptpy/Interpolation/geodesic_interpolation.py +785 -0
- multioptpy/Interpolation/interpolation.py +156 -0
- multioptpy/Interpolation/linear_interpolation.py +473 -0
- multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
- multioptpy/Interpolation/spline_interpolation.py +353 -0
- multioptpy/MD/__init__.py +0 -0
- multioptpy/MD/thermostat.py +185 -0
- multioptpy/MEP/__init__.py +0 -0
- multioptpy/MEP/pathopt_bneb_force.py +443 -0
- multioptpy/MEP/pathopt_dmf_force.py +448 -0
- multioptpy/MEP/pathopt_dneb_force.py +130 -0
- multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
- multioptpy/MEP/pathopt_gpneb_force.py +512 -0
- multioptpy/MEP/pathopt_lup_force.py +113 -0
- multioptpy/MEP/pathopt_neb_force.py +225 -0
- multioptpy/MEP/pathopt_nesb_force.py +205 -0
- multioptpy/MEP/pathopt_om_force.py +153 -0
- multioptpy/MEP/pathopt_qsm_force.py +174 -0
- multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
- multioptpy/ModelFunction/__init__.py +7 -0
- multioptpy/ModelFunction/avoiding_model_function.py +29 -0
- multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
- multioptpy/ModelFunction/conical_model_function.py +26 -0
- multioptpy/ModelFunction/opt_meci.py +50 -0
- multioptpy/ModelFunction/opt_mesx.py +47 -0
- multioptpy/ModelFunction/opt_mesx_2.py +49 -0
- multioptpy/ModelFunction/seam_model_function.py +27 -0
- multioptpy/ModelHessian/__init__.py +0 -0
- multioptpy/ModelHessian/approx_hessian.py +147 -0
- multioptpy/ModelHessian/calc_params.py +227 -0
- multioptpy/ModelHessian/fischer.py +236 -0
- multioptpy/ModelHessian/fischerd3.py +360 -0
- multioptpy/ModelHessian/fischerd4.py +398 -0
- multioptpy/ModelHessian/gfn0xtb.py +633 -0
- multioptpy/ModelHessian/gfnff.py +709 -0
- multioptpy/ModelHessian/lindh.py +165 -0
- multioptpy/ModelHessian/lindh2007d2.py +707 -0
- multioptpy/ModelHessian/lindh2007d3.py +822 -0
- multioptpy/ModelHessian/lindh2007d4.py +1030 -0
- multioptpy/ModelHessian/morse.py +106 -0
- multioptpy/ModelHessian/schlegel.py +144 -0
- multioptpy/ModelHessian/schlegeld3.py +322 -0
- multioptpy/ModelHessian/schlegeld4.py +559 -0
- multioptpy/ModelHessian/shortrange.py +346 -0
- multioptpy/ModelHessian/swartd2.py +496 -0
- multioptpy/ModelHessian/swartd3.py +706 -0
- multioptpy/ModelHessian/swartd4.py +918 -0
- multioptpy/ModelHessian/tshess.py +40 -0
- multioptpy/Optimizer/QHAdam.py +61 -0
- multioptpy/Optimizer/__init__.py +0 -0
- multioptpy/Optimizer/abc_fire.py +83 -0
- multioptpy/Optimizer/adabelief.py +58 -0
- multioptpy/Optimizer/adabound.py +68 -0
- multioptpy/Optimizer/adadelta.py +65 -0
- multioptpy/Optimizer/adaderivative.py +56 -0
- multioptpy/Optimizer/adadiff.py +68 -0
- multioptpy/Optimizer/adafactor.py +70 -0
- multioptpy/Optimizer/adam.py +65 -0
- multioptpy/Optimizer/adamax.py +62 -0
- multioptpy/Optimizer/adamod.py +83 -0
- multioptpy/Optimizer/adamw.py +65 -0
- multioptpy/Optimizer/adiis.py +523 -0
- multioptpy/Optimizer/afire_neb.py +282 -0
- multioptpy/Optimizer/block_hessian_update.py +709 -0
- multioptpy/Optimizer/c2diis.py +491 -0
- multioptpy/Optimizer/component_wise_scaling.py +405 -0
- multioptpy/Optimizer/conjugate_gradient.py +82 -0
- multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
- multioptpy/Optimizer/coordinate_locking.py +405 -0
- multioptpy/Optimizer/dic_rsirfo.py +1015 -0
- multioptpy/Optimizer/ediis.py +417 -0
- multioptpy/Optimizer/eve.py +76 -0
- multioptpy/Optimizer/fastadabelief.py +61 -0
- multioptpy/Optimizer/fire.py +77 -0
- multioptpy/Optimizer/fire2.py +249 -0
- multioptpy/Optimizer/fire_neb.py +92 -0
- multioptpy/Optimizer/gan_step.py +486 -0
- multioptpy/Optimizer/gdiis.py +609 -0
- multioptpy/Optimizer/gediis.py +203 -0
- multioptpy/Optimizer/geodesic_step.py +433 -0
- multioptpy/Optimizer/gpmin.py +633 -0
- multioptpy/Optimizer/gpr_step.py +364 -0
- multioptpy/Optimizer/gradientdescent.py +78 -0
- multioptpy/Optimizer/gradientdescent_neb.py +52 -0
- multioptpy/Optimizer/hessian_update.py +433 -0
- multioptpy/Optimizer/hybrid_rfo.py +998 -0
- multioptpy/Optimizer/kdiis.py +625 -0
- multioptpy/Optimizer/lars.py +21 -0
- multioptpy/Optimizer/lbfgs.py +253 -0
- multioptpy/Optimizer/lbfgs_neb.py +355 -0
- multioptpy/Optimizer/linesearch.py +236 -0
- multioptpy/Optimizer/lookahead.py +40 -0
- multioptpy/Optimizer/nadam.py +64 -0
- multioptpy/Optimizer/newton.py +200 -0
- multioptpy/Optimizer/prodigy.py +70 -0
- multioptpy/Optimizer/purtubation.py +16 -0
- multioptpy/Optimizer/quickmin_neb.py +245 -0
- multioptpy/Optimizer/radam.py +75 -0
- multioptpy/Optimizer/rfo_neb.py +302 -0
- multioptpy/Optimizer/ric_rfo.py +842 -0
- multioptpy/Optimizer/rl_step.py +627 -0
- multioptpy/Optimizer/rmspropgrave.py +65 -0
- multioptpy/Optimizer/rsirfo.py +1647 -0
- multioptpy/Optimizer/rsprfo.py +1056 -0
- multioptpy/Optimizer/sadam.py +60 -0
- multioptpy/Optimizer/samsgrad.py +63 -0
- multioptpy/Optimizer/tr_lbfgs.py +678 -0
- multioptpy/Optimizer/trim.py +273 -0
- multioptpy/Optimizer/trust_radius.py +207 -0
- multioptpy/Optimizer/trust_radius_neb.py +121 -0
- multioptpy/Optimizer/yogi.py +60 -0
- multioptpy/OtherMethod/__init__.py +0 -0
- multioptpy/OtherMethod/addf.py +1150 -0
- multioptpy/OtherMethod/dimer.py +895 -0
- multioptpy/OtherMethod/elastic_image_pair.py +629 -0
- multioptpy/OtherMethod/modelfunction.py +456 -0
- multioptpy/OtherMethod/newton_traj.py +454 -0
- multioptpy/OtherMethod/twopshs.py +1095 -0
- multioptpy/PESAnalyzer/__init__.py +0 -0
- multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
- multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
- multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
- multioptpy/PESAnalyzer/pca_analysis.py +314 -0
- multioptpy/Parameters/__init__.py +0 -0
- multioptpy/Parameters/atomic_mass.py +20 -0
- multioptpy/Parameters/atomic_number.py +22 -0
- multioptpy/Parameters/covalent_radii.py +44 -0
- multioptpy/Parameters/d2.py +61 -0
- multioptpy/Parameters/d3.py +63 -0
- multioptpy/Parameters/d4.py +103 -0
- multioptpy/Parameters/dreiding.py +34 -0
- multioptpy/Parameters/gfn0xtb_param.py +137 -0
- multioptpy/Parameters/gfnff_param.py +315 -0
- multioptpy/Parameters/gnb.py +104 -0
- multioptpy/Parameters/parameter.py +22 -0
- multioptpy/Parameters/uff.py +72 -0
- multioptpy/Parameters/unit_values.py +20 -0
- multioptpy/Potential/AFIR_potential.py +55 -0
- multioptpy/Potential/LJ_repulsive_potential.py +345 -0
- multioptpy/Potential/__init__.py +0 -0
- multioptpy/Potential/anharmonic_keep_potential.py +28 -0
- multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
- multioptpy/Potential/electrostatic_potential.py +69 -0
- multioptpy/Potential/flux_potential.py +30 -0
- multioptpy/Potential/gaussian_potential.py +101 -0
- multioptpy/Potential/idpp.py +516 -0
- multioptpy/Potential/keep_angle_potential.py +146 -0
- multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
- multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
- multioptpy/Potential/keep_potential.py +99 -0
- multioptpy/Potential/mechano_force_potential.py +74 -0
- multioptpy/Potential/nanoreactor_potential.py +52 -0
- multioptpy/Potential/potential.py +896 -0
- multioptpy/Potential/spacer_model_potential.py +221 -0
- multioptpy/Potential/switching_potential.py +258 -0
- multioptpy/Potential/universal_potential.py +34 -0
- multioptpy/Potential/value_range_potential.py +36 -0
- multioptpy/Potential/void_point_potential.py +25 -0
- multioptpy/SQM/__init__.py +0 -0
- multioptpy/SQM/sqm1/__init__.py +0 -0
- multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
- multioptpy/SQM/sqm2/__init__.py +0 -0
- multioptpy/SQM/sqm2/calc_tools.py +95 -0
- multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
- multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
- multioptpy/SQM/sqm2/sqm2_core.py +303 -0
- multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
- multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
- multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
- multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
- multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
- multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
- multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
- multioptpy/Thermo/__init__.py +0 -0
- multioptpy/Thermo/normal_mode_analyzer.py +865 -0
- multioptpy/Utils/__init__.py +0 -0
- multioptpy/Utils/bond_connectivity.py +264 -0
- multioptpy/Utils/calc_tools.py +884 -0
- multioptpy/Utils/oniom.py +96 -0
- multioptpy/Utils/pbc.py +48 -0
- multioptpy/Utils/riemann_curvature.py +208 -0
- multioptpy/Utils/symmetry_analyzer.py +482 -0
- multioptpy/Visualization/__init__.py +0 -0
- multioptpy/Visualization/visualization.py +156 -0
- multioptpy/WFAnalyzer/MO_analysis.py +104 -0
- multioptpy/WFAnalyzer/__init__.py +0 -0
- multioptpy/Wrapper/__init__.py +0 -0
- multioptpy/Wrapper/autots.py +1239 -0
- multioptpy/Wrapper/ieip_wrapper.py +93 -0
- multioptpy/Wrapper/md_wrapper.py +92 -0
- multioptpy/Wrapper/neb_wrapper.py +94 -0
- multioptpy/Wrapper/optimize_wrapper.py +76 -0
- multioptpy/__init__.py +5 -0
- multioptpy/entrypoints.py +916 -0
- multioptpy/fileio.py +660 -0
- multioptpy/ieip.py +340 -0
- multioptpy/interface.py +1086 -0
- multioptpy/irc.py +529 -0
- multioptpy/moleculardynamics.py +432 -0
- multioptpy/neb.py +1267 -0
- multioptpy/optimization.py +1553 -0
- multioptpy/optimizer.py +709 -0
- multioptpy-1.20.2.dist-info/METADATA +438 -0
- multioptpy-1.20.2.dist-info/RECORD +246 -0
- multioptpy-1.20.2.dist-info/WHEEL +5 -0
- multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
- multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
- 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
|