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,611 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import os
|
|
3
|
+
import copy
|
|
4
|
+
import numpy as np
|
|
5
|
+
import torch
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from multioptpy.SQM.sqm1.sqm1_core import SQM1Calculator, SQM1Parameters, ANGSTROM_TO_BOHR, BOHR_TO_ANGSTROM, is_covalently_bonded
|
|
9
|
+
from multioptpy.Utils.calc_tools import Calculationtools
|
|
10
|
+
from multioptpy.Parameters.parameter import UnitValueLib, element_number
|
|
11
|
+
from multioptpy.fileio import xyz2list
|
|
12
|
+
from multioptpy.Visualization.visualization import NEBVisualizer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Experimental semiempirical electronic structure approach inspired by GFN0-xTB (SQM1)
|
|
17
|
+
|
|
18
|
+
This module provides calculator utility helpers wrapping the Python implementation
|
|
19
|
+
of an experimental semiempirical electronic structure approach inspired by GFN0-xTB.
|
|
20
|
+
It mirrors the interface style of tblite_calculation_tools for convenience.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
class Calculation:
|
|
24
|
+
def __init__(self, **kwarg):
|
|
25
|
+
if UnitValueLib is not None:
|
|
26
|
+
UVL = UnitValueLib()
|
|
27
|
+
self.bohr2angstroms = UVL.bohr2angstroms
|
|
28
|
+
else:
|
|
29
|
+
self.bohr2angstroms = BOHR_TO_ANGSTROM
|
|
30
|
+
# Optional keys kept for interface parity; use .get to avoid KeyError
|
|
31
|
+
self.START_FILE = kwarg.get("START_FILE")
|
|
32
|
+
self.N_THREAD = kwarg.get("N_THREAD", 1)
|
|
33
|
+
self.SET_MEMORY = kwarg.get("SET_MEMORY")
|
|
34
|
+
self.FUNCTIONAL = kwarg.get("FUNCTIONAL")
|
|
35
|
+
self.FC_COUNT = kwarg.get("FC_COUNT", -1)
|
|
36
|
+
self.BPA_FOLDER_DIRECTORY = kwarg.get("BPA_FOLDER_DIRECTORY", "./")
|
|
37
|
+
self.Model_hess = kwarg.get("Model_hess")
|
|
38
|
+
self.unrestrict = kwarg.get("unrestrict", False)
|
|
39
|
+
self.dft_grid = kwarg.get("dft_grid")
|
|
40
|
+
self.hessian_flag = False
|
|
41
|
+
# Load SQM1 parameters (now embedded, no file needed)
|
|
42
|
+
self.params = SQM1Parameters()
|
|
43
|
+
self.device = kwarg.get("device", "cpu")
|
|
44
|
+
self.dtype = kwarg.get("dtype", torch.float64)
|
|
45
|
+
# Calculator instance will be created per calculation with appropriate geometry
|
|
46
|
+
self.calculator = None
|
|
47
|
+
# Distance constraint parameters
|
|
48
|
+
self.use_distance_constraints = kwarg.get("use_distance_constraints", True)
|
|
49
|
+
self.max_distance_deviation = kwarg.get("max_distance_deviation", 0.10) # 10% default
|
|
50
|
+
self.constraint_penalty_strength = kwarg.get("constraint_penalty_strength", 1000.0)
|
|
51
|
+
self.initial_distances = {} # Will store bonded pair distances on first calculation
|
|
52
|
+
self.bonded_pairs = set()
|
|
53
|
+
self.constraints_initialized = False
|
|
54
|
+
|
|
55
|
+
def _update_distance_constraints(self, positions, element_number_list):
|
|
56
|
+
"""
|
|
57
|
+
Update distance constraints for covalently bonded atom pairs.
|
|
58
|
+
This is called every time energy is calculated to track dynamic bond formation/breaking.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
positions: Atomic positions in Angstrom (n_atoms, 3)
|
|
62
|
+
element_number_list: Atomic numbers
|
|
63
|
+
"""
|
|
64
|
+
if not self.use_distance_constraints:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
positions = np.array(positions, dtype='float64').reshape(-1, 3)
|
|
68
|
+
n_atoms = len(positions)
|
|
69
|
+
|
|
70
|
+
# Update bonded pairs based on current geometry
|
|
71
|
+
current_bonded_pairs = set()
|
|
72
|
+
|
|
73
|
+
# Identify covalently bonded pairs at current geometry
|
|
74
|
+
for i in range(n_atoms):
|
|
75
|
+
for j in range(i + 1, n_atoms):
|
|
76
|
+
# Calculate distance in Angstrom
|
|
77
|
+
d_angstrom = np.linalg.norm(positions[i] - positions[j])
|
|
78
|
+
|
|
79
|
+
# Check if atoms are covalently bonded at current geometry
|
|
80
|
+
if is_covalently_bonded(element_number_list[i], element_number_list[j], d_angstrom):
|
|
81
|
+
current_bonded_pairs.add((i, j))
|
|
82
|
+
|
|
83
|
+
# If this is a new bond, record its initial distance
|
|
84
|
+
if (i, j) not in self.initial_distances:
|
|
85
|
+
d_bohr = d_angstrom * ANGSTROM_TO_BOHR
|
|
86
|
+
self.initial_distances[(i, j)] = d_bohr
|
|
87
|
+
|
|
88
|
+
# Update the bonded pairs to reflect current state
|
|
89
|
+
self.bonded_pairs = current_bonded_pairs
|
|
90
|
+
self.constraints_initialized = True
|
|
91
|
+
|
|
92
|
+
def _calculate_distance_constraint_penalty(self, positions):
|
|
93
|
+
"""
|
|
94
|
+
Calculate penalty energy and gradient for distance constraint violations.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
positions: Current positions in Angstrom (n_atoms, 3)
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Tuple of (penalty_energy, penalty_gradient)
|
|
101
|
+
- penalty_energy: Penalty energy in Hartree
|
|
102
|
+
- penalty_gradient: Gradient of penalty in Hartree/Bohr (n_atoms, 3)
|
|
103
|
+
"""
|
|
104
|
+
if not self.use_distance_constraints or not self.constraints_initialized:
|
|
105
|
+
return 0.0, None
|
|
106
|
+
|
|
107
|
+
positions = np.array(positions, dtype='float64').reshape(-1, 3)
|
|
108
|
+
n_atoms = len(positions)
|
|
109
|
+
penalty = 0.0
|
|
110
|
+
penalty_gradient = np.zeros((n_atoms, 3), dtype='float64')
|
|
111
|
+
|
|
112
|
+
# Only apply penalty to CURRENTLY bonded pairs
|
|
113
|
+
for (i, j) in self.bonded_pairs:
|
|
114
|
+
if (i, j) not in self.initial_distances:
|
|
115
|
+
continue # Skip if we don't have a reference distance
|
|
116
|
+
|
|
117
|
+
d_init_bohr = self.initial_distances[(i, j)]
|
|
118
|
+
|
|
119
|
+
# Calculate distance vector in Angstrom
|
|
120
|
+
r_ij_angstrom = positions[i] - positions[j]
|
|
121
|
+
d_current_angstrom = np.linalg.norm(r_ij_angstrom)
|
|
122
|
+
|
|
123
|
+
# Convert to Bohr
|
|
124
|
+
r_ij_bohr = r_ij_angstrom * ANGSTROM_TO_BOHR
|
|
125
|
+
d_current_bohr = d_current_angstrom * ANGSTROM_TO_BOHR
|
|
126
|
+
|
|
127
|
+
# Calculate relative deviation (dimensionless)
|
|
128
|
+
deviation = abs(d_current_bohr - d_init_bohr) / d_init_bohr
|
|
129
|
+
|
|
130
|
+
# Apply penalty if deviation exceeds threshold
|
|
131
|
+
if deviation > self.max_distance_deviation:
|
|
132
|
+
# Energy penalty (in Hartree)
|
|
133
|
+
penalty += self.constraint_penalty_strength * (deviation - self.max_distance_deviation)**2
|
|
134
|
+
|
|
135
|
+
# Gradient penalty calculation:
|
|
136
|
+
# E = k * (dev - thresh)²
|
|
137
|
+
# dev = |d_curr - d_init| / d_init
|
|
138
|
+
#
|
|
139
|
+
# Using chain rule:
|
|
140
|
+
# ∂E/∂r_bohr = ∂E/∂dev * ∂dev/∂d_curr * ∂d_curr/∂r_bohr
|
|
141
|
+
#
|
|
142
|
+
# ∂E/∂dev = 2*k*(dev - thresh)
|
|
143
|
+
# ∂dev/∂d_curr = sign(d_curr - d_init) / d_init
|
|
144
|
+
# ∂d_curr/∂r_bohr = r_ij_bohr / d_curr_bohr (unit vector in Bohr)
|
|
145
|
+
#
|
|
146
|
+
# Combined:
|
|
147
|
+
# ∂E/∂r_bohr = 2*k*(dev - thresh) * sign(d_curr - d_init)/d_init * r_ij_bohr/d_curr_bohr
|
|
148
|
+
|
|
149
|
+
# Sign of deviation
|
|
150
|
+
sign = 1.0 if d_current_bohr > d_init_bohr else -1.0
|
|
151
|
+
|
|
152
|
+
# Gradient prefactor (in Hartree/Bohr)
|
|
153
|
+
grad_prefactor = 2.0 * self.constraint_penalty_strength * (deviation - self.max_distance_deviation) * sign / d_init_bohr
|
|
154
|
+
|
|
155
|
+
# Direction vector (unit vector in Bohr space)
|
|
156
|
+
direction_bohr = r_ij_bohr / d_current_bohr
|
|
157
|
+
|
|
158
|
+
# Gradient in Hartree/Bohr
|
|
159
|
+
grad_contribution = grad_prefactor * direction_bohr
|
|
160
|
+
|
|
161
|
+
# Apply to both atoms (i and j)
|
|
162
|
+
penalty_gradient[i] += grad_contribution
|
|
163
|
+
penalty_gradient[j] -= grad_contribution
|
|
164
|
+
|
|
165
|
+
return penalty, penalty_gradient
|
|
166
|
+
|
|
167
|
+
def _calculate_distance_constraint_penalty_hessian(self, positions):
|
|
168
|
+
"""
|
|
169
|
+
Calculate Hessian of distance constraint penalty.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
positions: Current positions in Angstrom (n_atoms, 3)
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Hessian of penalty in Hartree/Bohr^2 (3*n_atoms, 3*n_atoms)
|
|
176
|
+
"""
|
|
177
|
+
if not self.use_distance_constraints or not self.constraints_initialized:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
positions = np.array(positions, dtype='float64').reshape(-1, 3)
|
|
181
|
+
n_atoms = len(positions)
|
|
182
|
+
penalty_hessian = np.zeros((3*n_atoms, 3*n_atoms), dtype='float64')
|
|
183
|
+
|
|
184
|
+
# Only apply penalty to CURRENTLY bonded pairs
|
|
185
|
+
for (i, j) in self.bonded_pairs:
|
|
186
|
+
if (i, j) not in self.initial_distances:
|
|
187
|
+
continue # Skip if we don't have a reference distance
|
|
188
|
+
|
|
189
|
+
d_init_bohr = self.initial_distances[(i, j)]
|
|
190
|
+
# Calculate distance vector in Angstrom
|
|
191
|
+
r_ij_angstrom = positions[i] - positions[j]
|
|
192
|
+
d_current_angstrom = np.linalg.norm(r_ij_angstrom)
|
|
193
|
+
|
|
194
|
+
# Convert to Bohr
|
|
195
|
+
r_ij_bohr = r_ij_angstrom * ANGSTROM_TO_BOHR
|
|
196
|
+
d_current_bohr = d_current_angstrom * ANGSTROM_TO_BOHR
|
|
197
|
+
|
|
198
|
+
# Calculate relative deviation (dimensionless)
|
|
199
|
+
deviation = abs(d_current_bohr - d_init_bohr) / d_init_bohr
|
|
200
|
+
|
|
201
|
+
# Only calculate if deviation exceeds threshold
|
|
202
|
+
if deviation > self.max_distance_deviation:
|
|
203
|
+
# Sign of deviation
|
|
204
|
+
sign = 1.0 if d_current_bohr > d_init_bohr else -1.0
|
|
205
|
+
|
|
206
|
+
# Unit vector along bond (in Bohr)
|
|
207
|
+
u = r_ij_bohr / d_current_bohr
|
|
208
|
+
|
|
209
|
+
# For harmonic penalty E = k * (dev - thresh)²
|
|
210
|
+
# where dev = sign * (d - d0) / d0
|
|
211
|
+
#
|
|
212
|
+
# The Hessian has two terms:
|
|
213
|
+
# H = ∂²E/∂r_i∂r_j
|
|
214
|
+
#
|
|
215
|
+
# For a pair of atoms (atom_a, atom_b):
|
|
216
|
+
# H_aa = projection along bond + projection perpendicular
|
|
217
|
+
# H_ab = -H_aa (Newton's 3rd law)
|
|
218
|
+
# H_bb = H_aa
|
|
219
|
+
#
|
|
220
|
+
# The full formula for harmonic penalty Hessian:
|
|
221
|
+
# H_αβ = (2k/d0²) * [u_α u_β + (dev-thresh)/sign * (δ_αβ - u_α u_β) / d]
|
|
222
|
+
#
|
|
223
|
+
# Simplified for the case where dev > thresh:
|
|
224
|
+
# Second derivative along bond direction:
|
|
225
|
+
k = self.constraint_penalty_strength
|
|
226
|
+
d0 = d_init_bohr
|
|
227
|
+
d = d_current_bohr
|
|
228
|
+
|
|
229
|
+
# Prefactor for second derivative (in Hartree/Bohr²)
|
|
230
|
+
# ∂²E/∂d² = 2k/d0²
|
|
231
|
+
prefactor = 2.0 * k / (d0 * d0)
|
|
232
|
+
|
|
233
|
+
# Additional term from directional derivative
|
|
234
|
+
# When dev > thresh, we have an active constraint
|
|
235
|
+
# The Hessian includes both the force constant and geometric terms
|
|
236
|
+
if abs(d - d0) > 1e-10: # Avoid division by zero
|
|
237
|
+
# Coefficient for the projection onto bond direction
|
|
238
|
+
coeff_parallel = prefactor
|
|
239
|
+
# Coefficient for the projection perpendicular to bond
|
|
240
|
+
# This comes from the d/dr term in the gradient
|
|
241
|
+
coeff_perp = prefactor * (deviation - self.max_distance_deviation) * sign / d
|
|
242
|
+
else:
|
|
243
|
+
coeff_parallel = prefactor
|
|
244
|
+
coeff_perp = 0.0
|
|
245
|
+
|
|
246
|
+
# Build 3x3 block for this atom pair
|
|
247
|
+
# H_block = coeff_parallel * u⊗u + coeff_perp * (I - u⊗u)
|
|
248
|
+
identity = np.eye(3, dtype='float64')
|
|
249
|
+
u_outer = np.outer(u, u)
|
|
250
|
+
H_block = coeff_parallel * u_outer + coeff_perp * (identity - u_outer)
|
|
251
|
+
|
|
252
|
+
# Add to Hessian (symmetric contributions)
|
|
253
|
+
# H_ii += H_block
|
|
254
|
+
penalty_hessian[3*i:3*i+3, 3*i:3*i+3] += H_block
|
|
255
|
+
# H_jj += H_block (symmetric)
|
|
256
|
+
penalty_hessian[3*j:3*j+3, 3*j:3*j+3] += H_block
|
|
257
|
+
# H_ij -= H_block (off-diagonal)
|
|
258
|
+
penalty_hessian[3*i:3*i+3, 3*j:3*j+3] -= H_block
|
|
259
|
+
# H_ji -= H_block (symmetric off-diagonal)
|
|
260
|
+
penalty_hessian[3*j:3*j+3, 3*i:3*i+3] -= H_block
|
|
261
|
+
|
|
262
|
+
return penalty_hessian
|
|
263
|
+
|
|
264
|
+
def numerical_hessian(self, geom_num_list, element_list, total_charge):
|
|
265
|
+
"""
|
|
266
|
+
Calculate numerical Hessian using finite differences of gradients.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
geom_num_list: Atomic positions in Angstrom (n_atoms, 3)
|
|
270
|
+
element_list: Atomic numbers (n_atoms,)
|
|
271
|
+
total_charge: Total molecular charge
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Hessian matrix (3*n_atoms, 3*n_atoms) in Hartree/Bohr^2
|
|
275
|
+
"""
|
|
276
|
+
numerical_delivative_delta = 1.0e-4 # in Angstrom
|
|
277
|
+
geom_num_list = np.array(geom_num_list, dtype="float64")
|
|
278
|
+
n_atoms = len(geom_num_list)
|
|
279
|
+
hessian = np.zeros((3*n_atoms, 3*n_atoms))
|
|
280
|
+
count = 0
|
|
281
|
+
|
|
282
|
+
# Update distance constraints based on current geometry
|
|
283
|
+
self._update_distance_constraints(geom_num_list, element_list)
|
|
284
|
+
|
|
285
|
+
for a in range(n_atoms):
|
|
286
|
+
for i in range(3):
|
|
287
|
+
for b in range(n_atoms):
|
|
288
|
+
for j in range(3):
|
|
289
|
+
if count > 3*b + j:
|
|
290
|
+
continue
|
|
291
|
+
tmp_grad = []
|
|
292
|
+
for direction in [1, -1]:
|
|
293
|
+
shifted = geom_num_list.copy()
|
|
294
|
+
shifted[a, i] += direction * numerical_delivative_delta
|
|
295
|
+
# Create calculator for this geometry
|
|
296
|
+
calc = SQM1Calculator(
|
|
297
|
+
atomic_numbers=element_list,
|
|
298
|
+
positions=shifted,
|
|
299
|
+
charge=total_charge,
|
|
300
|
+
uhf=0,
|
|
301
|
+
params=self.params,
|
|
302
|
+
device=self.device,
|
|
303
|
+
dtype=self.dtype
|
|
304
|
+
)
|
|
305
|
+
# Get gradient in Hartree/Bohr
|
|
306
|
+
_, grad = calc.calculate_energy_and_gradient()
|
|
307
|
+
grad_np = grad.cpu().detach().numpy()
|
|
308
|
+
|
|
309
|
+
# Add penalty gradient
|
|
310
|
+
_, penalty_grad = self._calculate_distance_constraint_penalty(shifted)
|
|
311
|
+
if penalty_grad is not None:
|
|
312
|
+
grad_np += penalty_grad
|
|
313
|
+
|
|
314
|
+
tmp_grad.append(grad_np[b, j])
|
|
315
|
+
# Finite difference in Angstrom, convert to Bohr
|
|
316
|
+
val = (tmp_grad[0] - tmp_grad[1]) / (2*numerical_delivative_delta)
|
|
317
|
+
hessian[3*a+i, 3*b+j] = val
|
|
318
|
+
hessian[3*b+j, 3*a+i] = val
|
|
319
|
+
count += 1
|
|
320
|
+
return hessian
|
|
321
|
+
|
|
322
|
+
def exact_hessian(self, element_number_list, total_charge, positions):
|
|
323
|
+
"""
|
|
324
|
+
Calculate exact Hessian using automatic differentiation.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
element_number_list: Atomic numbers (n_atoms,)
|
|
328
|
+
total_charge: Total molecular charge
|
|
329
|
+
positions: Atomic positions in Angstrom (n_atoms, 3)
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
Projected Hessian matrix (3*n_atoms, 3*n_atoms) in Hartree/Bohr^2
|
|
333
|
+
"""
|
|
334
|
+
# Update distance constraints based on current geometry
|
|
335
|
+
self._update_distance_constraints(positions, element_number_list)
|
|
336
|
+
|
|
337
|
+
# Create calculator for this geometry
|
|
338
|
+
calc = SQM1Calculator(
|
|
339
|
+
atomic_numbers=element_number_list,
|
|
340
|
+
positions=positions,
|
|
341
|
+
charge=total_charge,
|
|
342
|
+
uhf=0,
|
|
343
|
+
params=self.params,
|
|
344
|
+
device=self.device,
|
|
345
|
+
dtype=self.dtype
|
|
346
|
+
)
|
|
347
|
+
# Calculate Hessian using automatic differentiation
|
|
348
|
+
exact_hess = calc.calculate_hessian(method='analytical')
|
|
349
|
+
exact_hess_np = exact_hess.cpu().detach().numpy()
|
|
350
|
+
|
|
351
|
+
# Add penalty Hessian
|
|
352
|
+
penalty_hess = self._calculate_distance_constraint_penalty_hessian(positions)
|
|
353
|
+
if penalty_hess is not None:
|
|
354
|
+
exact_hess_np += penalty_hess
|
|
355
|
+
|
|
356
|
+
# Project out translation and rotation if Calculationtools is available
|
|
357
|
+
if Calculationtools is not None:
|
|
358
|
+
exact_hess_np = Calculationtools().project_out_hess_tr_and_rot_for_coord(
|
|
359
|
+
exact_hess_np, element_number_list.tolist(), positions, display_eigval=False
|
|
360
|
+
)
|
|
361
|
+
self.Model_hess = exact_hess_np
|
|
362
|
+
|
|
363
|
+
def single_point(self, file_directory, element_number_list, iter_index, electric_charge_and_multiplicity, method="", geom_num_list=None):
|
|
364
|
+
"""
|
|
365
|
+
Calculate single point energy and gradient.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
file_directory: Directory containing xyz files
|
|
369
|
+
element_number_list: Atomic numbers
|
|
370
|
+
iter_index: Iteration index
|
|
371
|
+
electric_charge_and_multiplicity: [charge, multiplicity]
|
|
372
|
+
geom_num_list: Optional geometry (n_atoms, 3) in Angstrom
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
Tuple of (energy, gradient, positions, finish_flag)
|
|
376
|
+
"""
|
|
377
|
+
gradient_list = []
|
|
378
|
+
energy_list = []
|
|
379
|
+
geometry_num_list = []
|
|
380
|
+
finish_frag = False
|
|
381
|
+
|
|
382
|
+
if isinstance(element_number_list[0], str):
|
|
383
|
+
tmp = copy.copy(element_number_list)
|
|
384
|
+
element_number_list = []
|
|
385
|
+
if element_number is not None:
|
|
386
|
+
for elem in tmp:
|
|
387
|
+
element_number_list.append(element_number(elem))
|
|
388
|
+
element_number_list = np.array(element_number_list)
|
|
389
|
+
|
|
390
|
+
try:
|
|
391
|
+
os.mkdir(file_directory)
|
|
392
|
+
except Exception:
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
if file_directory is None:
|
|
396
|
+
file_list = ["dummy"]
|
|
397
|
+
else:
|
|
398
|
+
file_list = glob.glob(file_directory+"/*_[0-9].xyz")
|
|
399
|
+
|
|
400
|
+
total_charge = int(electric_charge_and_multiplicity[0])
|
|
401
|
+
|
|
402
|
+
for num, input_file in enumerate(file_list):
|
|
403
|
+
if True:
|
|
404
|
+
if geom_num_list is None and xyz2list is not None:
|
|
405
|
+
tmp_positions, _, electric_charge_and_multiplicity = xyz2list(input_file, electric_charge_and_multiplicity)
|
|
406
|
+
else:
|
|
407
|
+
tmp_positions = geom_num_list
|
|
408
|
+
|
|
409
|
+
positions = np.array(tmp_positions, dtype="float64").reshape(-1, 3) # Angstrom
|
|
410
|
+
|
|
411
|
+
# Update distance constraints on every calculation to track dynamic bonding
|
|
412
|
+
self._update_distance_constraints(positions, element_number_list)
|
|
413
|
+
|
|
414
|
+
# Create calculator for this geometry
|
|
415
|
+
calc = SQM1Calculator(
|
|
416
|
+
atomic_numbers=element_number_list,
|
|
417
|
+
positions=positions,
|
|
418
|
+
charge=total_charge,
|
|
419
|
+
uhf=0,
|
|
420
|
+
params=self.params,
|
|
421
|
+
device=self.device,
|
|
422
|
+
dtype=self.dtype
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
# Calculate energy and gradient
|
|
426
|
+
e, g = calc.calculate_energy_and_gradient()
|
|
427
|
+
e = e.cpu().detach().numpy().item() # Hartree
|
|
428
|
+
g = g.cpu().detach().numpy() # Hartree/Bohr
|
|
429
|
+
|
|
430
|
+
# Apply distance constraint penalty
|
|
431
|
+
penalty, penalty_grad = self._calculate_distance_constraint_penalty(positions)
|
|
432
|
+
e += penalty
|
|
433
|
+
if penalty_grad is not None:
|
|
434
|
+
g += penalty_grad
|
|
435
|
+
positions /= BOHR_TO_ANGSTROM # Convert back to Angstrom for output
|
|
436
|
+
# Save results
|
|
437
|
+
self.energy = e
|
|
438
|
+
self.gradient = g
|
|
439
|
+
self.coordinate = positions
|
|
440
|
+
|
|
441
|
+
if self.FC_COUNT == -1 or isinstance(iter_index, str):
|
|
442
|
+
if self.hessian_flag:
|
|
443
|
+
self.exact_hessian(element_number_list, total_charge, positions)
|
|
444
|
+
elif iter_index % self.FC_COUNT == 0 or self.hessian_flag:
|
|
445
|
+
self.exact_hessian(element_number_list, total_charge, positions)
|
|
446
|
+
|
|
447
|
+
#except Exception as error:
|
|
448
|
+
# print(error)
|
|
449
|
+
# print("This molecule could not be optimized.")
|
|
450
|
+
# print("Input file: ", file_list, "\n")
|
|
451
|
+
# finish_frag = True
|
|
452
|
+
# return np.array([0]), np.array([0]), positions, finish_frag
|
|
453
|
+
|
|
454
|
+
return e, g, positions, finish_frag
|
|
455
|
+
|
|
456
|
+
def single_point_no_directory(self, positions, element_number_list, electric_charge_and_multiplicity):
|
|
457
|
+
"""
|
|
458
|
+
Calculate single point energy and gradient without file I/O.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
positions: Atomic positions in Angstrom (n_atoms, 3)
|
|
462
|
+
element_number_list: Atomic numbers
|
|
463
|
+
electric_charge_and_multiplicity: [charge, multiplicity]
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
Tuple of (energy, gradient, finish_flag)
|
|
467
|
+
"""
|
|
468
|
+
finish_frag = False
|
|
469
|
+
if isinstance(element_number_list[0], str):
|
|
470
|
+
tmp = copy.copy(element_number_list)
|
|
471
|
+
element_number_list = []
|
|
472
|
+
if element_number is not None:
|
|
473
|
+
for elem in tmp:
|
|
474
|
+
element_number_list.append(element_number(elem))
|
|
475
|
+
element_number_list = np.array(element_number_list)
|
|
476
|
+
try:
|
|
477
|
+
positions = np.array(positions, dtype='float64')
|
|
478
|
+
total_charge = int(electric_charge_and_multiplicity[0])
|
|
479
|
+
|
|
480
|
+
# Update distance constraints on every calculation to track dynamic bonding
|
|
481
|
+
self._update_distance_constraints(positions, element_number_list)
|
|
482
|
+
|
|
483
|
+
# Create calculator for this geometry
|
|
484
|
+
calc = SQM1Calculator(
|
|
485
|
+
atomic_numbers=element_number_list,
|
|
486
|
+
positions=positions,
|
|
487
|
+
charge=total_charge,
|
|
488
|
+
uhf=0,
|
|
489
|
+
params=self.params,
|
|
490
|
+
device=self.device,
|
|
491
|
+
dtype=self.dtype
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Calculate energy and gradient
|
|
495
|
+
e, g = calc.calculate_energy_and_gradient()
|
|
496
|
+
e = e.cpu().detach().numpy().item() # Hartree
|
|
497
|
+
g = g.cpu().detach().numpy() # Hartree/Bohr
|
|
498
|
+
|
|
499
|
+
# Apply distance constraint penalty
|
|
500
|
+
penalty, penalty_grad = self._calculate_distance_constraint_penalty(positions)
|
|
501
|
+
e += penalty
|
|
502
|
+
if penalty_grad is not None:
|
|
503
|
+
g += penalty_grad
|
|
504
|
+
|
|
505
|
+
self.energy = e
|
|
506
|
+
self.gradient = g
|
|
507
|
+
except Exception as error:
|
|
508
|
+
print(error)
|
|
509
|
+
print("This molecule could not be optimized.")
|
|
510
|
+
finish_frag = True
|
|
511
|
+
return np.array([0]), np.array([0]), finish_frag
|
|
512
|
+
return e, g, finish_frag
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
class CalculationEngine:
|
|
516
|
+
def calculate(self, file_directory, optimize_num, pre_total_velocity, config):
|
|
517
|
+
raise NotImplementedError
|
|
518
|
+
|
|
519
|
+
def _get_file_list(self, file_directory):
|
|
520
|
+
return sum([sorted(glob.glob(os.path.join(file_directory, f"*_" + "[0-9]" * i + ".xyz"))) for i in range(1, 7)], [])
|
|
521
|
+
|
|
522
|
+
def _process_visualization(self, energy_list, gradient_list, num_list, optimize_num, config):
|
|
523
|
+
try:
|
|
524
|
+
if getattr(config, 'save_pict', False):
|
|
525
|
+
visualizer = NEBVisualizer(config)
|
|
526
|
+
tmp_ene_list = np.array(energy_list, dtype='float64') * config.hartree2kcalmol
|
|
527
|
+
visualizer.plot_energy(num_list, tmp_ene_list - tmp_ene_list[0], optimize_num)
|
|
528
|
+
print("energy graph plotted.")
|
|
529
|
+
gradient_norm_list = [np.sqrt(np.linalg.norm(g)**2/(len(g)*3)) for g in gradient_list]
|
|
530
|
+
visualizer.plot_gradient(num_list, gradient_norm_list, optimize_num)
|
|
531
|
+
print("gradient graph plotted.")
|
|
532
|
+
except Exception as e:
|
|
533
|
+
print(f"Visualization error: {e}")
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
class SQM1Engine(CalculationEngine):
|
|
537
|
+
"""SQM1 calculation engine wrapping SQM1Calculator"""
|
|
538
|
+
def __init__(self, param_file=None, device="cpu", dtype=torch.float64):
|
|
539
|
+
# Parameters are now embedded, param_file is ignored (kept for backward compatibility)
|
|
540
|
+
self.params = SQM1Parameters()
|
|
541
|
+
self.device = device
|
|
542
|
+
self.dtype = dtype
|
|
543
|
+
|
|
544
|
+
def calculate(self, file_directory, optimize_num, pre_total_velocity, config):
|
|
545
|
+
gradient_list = []
|
|
546
|
+
energy_list = []
|
|
547
|
+
geometry_num_list = []
|
|
548
|
+
gradient_norm_list = []
|
|
549
|
+
delete_pre_total_velocity = []
|
|
550
|
+
num_list = []
|
|
551
|
+
|
|
552
|
+
os.makedirs(file_directory, exist_ok=True)
|
|
553
|
+
file_list = self._get_file_list(file_directory)
|
|
554
|
+
|
|
555
|
+
if xyz2list is None:
|
|
556
|
+
raise ImportError("xyz2list is required for this method")
|
|
557
|
+
|
|
558
|
+
geometry_list_tmp, element_list, _ = xyz2list(file_list[0], None)
|
|
559
|
+
element_number_list = []
|
|
560
|
+
if element_number is not None:
|
|
561
|
+
for elem in element_list:
|
|
562
|
+
element_number_list.append(element_number(elem))
|
|
563
|
+
element_number_list = np.array(element_number_list, dtype='int')
|
|
564
|
+
|
|
565
|
+
for num, input_file in enumerate(file_list):
|
|
566
|
+
try:
|
|
567
|
+
print(input_file)
|
|
568
|
+
positions, _, electric_charge_and_multiplicity = xyz2list(input_file, None)
|
|
569
|
+
positions = np.array(positions, dtype='float64').reshape(-1, 3) # Angstrom
|
|
570
|
+
total_charge = int(electric_charge_and_multiplicity[0])
|
|
571
|
+
|
|
572
|
+
# Create calculator for this geometry
|
|
573
|
+
calc = SQM1Calculator(
|
|
574
|
+
atomic_numbers=element_number_list,
|
|
575
|
+
positions=positions,
|
|
576
|
+
charge=total_charge,
|
|
577
|
+
uhf=0,
|
|
578
|
+
params=self.params,
|
|
579
|
+
device=self.device,
|
|
580
|
+
dtype=self.dtype
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
# Calculate energy and gradient
|
|
584
|
+
e, g = calc.calculate_energy_and_gradient()
|
|
585
|
+
e = e.cpu().detach().numpy().item() # Hartree
|
|
586
|
+
g = g.cpu().detach().numpy() # Hartree/Bohr
|
|
587
|
+
|
|
588
|
+
print("\n")
|
|
589
|
+
energy_list.append(e)
|
|
590
|
+
gradient_list.append(g)
|
|
591
|
+
gradient_norm_list.append(np.sqrt(np.linalg.norm(g)**2/(len(g)*3)))
|
|
592
|
+
geometry_num_list.append(positions)
|
|
593
|
+
num_list.append(num)
|
|
594
|
+
except Exception as error:
|
|
595
|
+
print(error)
|
|
596
|
+
print("This molecule could not be optimized.")
|
|
597
|
+
if optimize_num != 0:
|
|
598
|
+
delete_pre_total_velocity.append(num)
|
|
599
|
+
|
|
600
|
+
self._process_visualization(energy_list, gradient_list, num_list, optimize_num, config)
|
|
601
|
+
|
|
602
|
+
if optimize_num != 0 and len(pre_total_velocity) != 0:
|
|
603
|
+
pre_total_velocity = np.array(pre_total_velocity, dtype='float64').tolist()
|
|
604
|
+
for i in sorted(delete_pre_total_velocity, reverse=True):
|
|
605
|
+
pre_total_velocity.pop(i)
|
|
606
|
+
pre_total_velocity = np.array(pre_total_velocity, dtype='float64')
|
|
607
|
+
|
|
608
|
+
return (np.array(energy_list, dtype='float64'),
|
|
609
|
+
np.array(gradient_list, dtype='float64'),
|
|
610
|
+
np.array(geometry_num_list, dtype='float64'),
|
|
611
|
+
pre_total_velocity)
|