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,376 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import os
|
|
3
|
+
import copy
|
|
4
|
+
import numpy as np
|
|
5
|
+
import torch
|
|
6
|
+
|
|
7
|
+
# --- SQM2 ---
|
|
8
|
+
from multioptpy.SQM.sqm2.sqm2_core import SQM2Calculator
|
|
9
|
+
# ---
|
|
10
|
+
|
|
11
|
+
from multioptpy.Utils.calc_tools import Calculationtools
|
|
12
|
+
from multioptpy.Parameters.parameter import UnitValueLib, element_number
|
|
13
|
+
from multioptpy.fileio import xyz2list
|
|
14
|
+
from multioptpy.Visualization.visualization import NEBVisualizer
|
|
15
|
+
|
|
16
|
+
# --- Constants (from SQM2Calculator reference) ---
|
|
17
|
+
ANG2BOHR = 1.8897261246257704
|
|
18
|
+
ANGSTROM_TO_BOHR = ANG2BOHR
|
|
19
|
+
BOHR_TO_ANGSTROM = 1.0 / ANG2BOHR
|
|
20
|
+
# ---
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
Experimental semiempirical electronic structure approach inspired by GFN-xTB (SQM2)
|
|
24
|
+
|
|
25
|
+
This module provides calculator utility helpers wrapping the Python implementation
|
|
26
|
+
of an experimental semiempirical electronic structure approach (SQM2).
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
class Calculation:
|
|
30
|
+
def __init__(self, **kwarg):
|
|
31
|
+
if UnitValueLib is not None:
|
|
32
|
+
UVL = UnitValueLib()
|
|
33
|
+
self.bohr2angstroms = UVL.bohr2angstroms
|
|
34
|
+
else:
|
|
35
|
+
self.bohr2angstroms = BOHR_TO_ANGSTROM
|
|
36
|
+
|
|
37
|
+
# Optional keys
|
|
38
|
+
self.START_FILE = kwarg.get("START_FILE")
|
|
39
|
+
self.N_THREAD = kwarg.get("N_THREAD", 1)
|
|
40
|
+
self.SET_MEMORY = kwarg.get("SET_MEMORY")
|
|
41
|
+
self.FUNCTIONAL = kwarg.get("FUNCTIONAL")
|
|
42
|
+
self.FC_COUNT = kwarg.get("FC_COUNT", -1)
|
|
43
|
+
self.BPA_FOLDER_DIRECTORY = kwarg.get("BPA_FOLDER_DIRECTORY", "./")
|
|
44
|
+
self.Model_hess = kwarg.get("Model_hess")
|
|
45
|
+
self.unrestrict = kwarg.get("unrestrict", False)
|
|
46
|
+
self.dft_grid = kwarg.get("dft_grid")
|
|
47
|
+
self.hessian_flag = False
|
|
48
|
+
|
|
49
|
+
self.device = kwarg.get("device", "cpu")
|
|
50
|
+
self.dtype = kwarg.get("dtype", torch.float64)
|
|
51
|
+
|
|
52
|
+
# The main calculator instance will be created on-the-fly
|
|
53
|
+
# as it requires geometry (xyz) upon initialization.
|
|
54
|
+
self.calculator = None
|
|
55
|
+
|
|
56
|
+
def numerical_hessian(self, geom_num_list, element_list, total_charge):
|
|
57
|
+
"""
|
|
58
|
+
Calculate numerical Hessian using finite differences of gradients (SQM2).
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
geom_num_list: Atomic positions in Angstrom (n_atoms, 3)
|
|
62
|
+
element_list: Atomic numbers (n_atoms,)
|
|
63
|
+
total_charge: Total molecular charge
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Hessian matrix (3*n_atoms, 3*n_atoms) in Hartree/Bohr^2
|
|
67
|
+
"""
|
|
68
|
+
numerical_delivative_delta = 1.0e-4 # in Angstrom
|
|
69
|
+
geom_num_list = np.array(geom_num_list, dtype="float64")
|
|
70
|
+
n_atoms = len(geom_num_list)
|
|
71
|
+
hessian = np.zeros((3*n_atoms, 3*n_atoms))
|
|
72
|
+
count = 0
|
|
73
|
+
|
|
74
|
+
# SQM2Calculator also requires spin (assuming 0 for singlet)
|
|
75
|
+
spin = 0
|
|
76
|
+
|
|
77
|
+
for a in range(n_atoms):
|
|
78
|
+
for i in range(3):
|
|
79
|
+
for b in range(n_atoms):
|
|
80
|
+
for j in range(3):
|
|
81
|
+
if count > 3*b + j:
|
|
82
|
+
continue
|
|
83
|
+
tmp_grad = []
|
|
84
|
+
for direction in [1, -1]:
|
|
85
|
+
shifted = geom_num_list.copy() # Angstrom
|
|
86
|
+
shifted[a, i] += direction * numerical_delivative_delta
|
|
87
|
+
|
|
88
|
+
# --- Get SQM2 gradient ---
|
|
89
|
+
# Re-initialize SQM2Calculator (required as xyz is in __init__)
|
|
90
|
+
calc = SQM2Calculator(
|
|
91
|
+
xyz=shifted,
|
|
92
|
+
element_list=element_list,
|
|
93
|
+
charge=total_charge,
|
|
94
|
+
spin=spin
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# .total_gradient returns (Energy, Gradient)
|
|
98
|
+
# Input: Angstrom, Output: (Hartree, Hartree/Bohr)
|
|
99
|
+
_, grad_np = calc.total_gradient(shifted)
|
|
100
|
+
|
|
101
|
+
tmp_grad.append(grad_np[b, j])
|
|
102
|
+
|
|
103
|
+
# Finite difference
|
|
104
|
+
# val = (Hartree/Bohr) / Angstrom
|
|
105
|
+
val = (tmp_grad[0] - tmp_grad[1]) / (2*numerical_delivative_delta)
|
|
106
|
+
|
|
107
|
+
# Convert units to Hartree/Bohr^2
|
|
108
|
+
hessian[3*a+i, 3*b+j] = val * ANGSTROM_TO_BOHR
|
|
109
|
+
hessian[3*b+j, 3*a+i] = val * ANGSTROM_TO_BOHR
|
|
110
|
+
count += 1
|
|
111
|
+
return hessian
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def exact_hessian(self, element_number_list, total_charge, positions):
|
|
115
|
+
"""
|
|
116
|
+
Calculate exact Hessian using automatic differentiation (SQM2).
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
element_number_list: Atomic numbers (n_atoms,)
|
|
120
|
+
total_charge: Total molecular charge
|
|
121
|
+
positions: Atomic positions in Angstrom (n_atoms, 3)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Projected Hessian matrix (3*n_atoms, 3*n_atoms) in Hartree/Bohr^2
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
# Assume spin=0 (singlet)
|
|
128
|
+
spin = 0
|
|
129
|
+
|
|
130
|
+
# Initialize SQM2Calculator (Input: Angstrom)
|
|
131
|
+
calc = SQM2Calculator(
|
|
132
|
+
xyz=positions,
|
|
133
|
+
element_list=element_number_list,
|
|
134
|
+
charge=total_charge,
|
|
135
|
+
spin=spin
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Calculate Hessian
|
|
139
|
+
# Input: Angstrom, Output: (Hartree/Bohr^2)
|
|
140
|
+
exact_hess_np = calc.total_hessian(positions)
|
|
141
|
+
|
|
142
|
+
# Project out translation and rotation
|
|
143
|
+
if Calculationtools is not None:
|
|
144
|
+
exact_hess_np = Calculationtools().project_out_hess_tr_and_rot_for_coord(
|
|
145
|
+
exact_hess_np, element_number_list.tolist(), positions, display_eigval=False
|
|
146
|
+
)
|
|
147
|
+
self.Model_hess = exact_hess_np
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def single_point(self, file_directory, element_number_list, iter_index, electric_charge_and_multiplicity, method="", geom_num_list=None):
|
|
151
|
+
"""
|
|
152
|
+
Calculate single point energy and gradient (SQM2).
|
|
153
|
+
"""
|
|
154
|
+
print("Warning: SQM2 is an experimental method and does not be suitable for production use.")
|
|
155
|
+
finish_frag = False
|
|
156
|
+
|
|
157
|
+
if isinstance(element_number_list[0], str):
|
|
158
|
+
tmp = copy.copy(element_number_list)
|
|
159
|
+
element_number_list = []
|
|
160
|
+
if element_number is not None:
|
|
161
|
+
for elem in tmp:
|
|
162
|
+
element_number_list.append(element_number(elem))
|
|
163
|
+
element_number_list = np.array(element_number_list)
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
os.mkdir(file_directory)
|
|
167
|
+
except Exception:
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
if file_directory is None:
|
|
171
|
+
file_list = ["dummy"]
|
|
172
|
+
else:
|
|
173
|
+
file_list = glob.glob(file_directory+"/*_[0-9].xyz")
|
|
174
|
+
|
|
175
|
+
total_charge = int(electric_charge_and_multiplicity[0])
|
|
176
|
+
# SQM2 EHTCalculator requires spin (multiplicity - 1)
|
|
177
|
+
spin = int(electric_charge_and_multiplicity[1]) - 1
|
|
178
|
+
|
|
179
|
+
for num, input_file in enumerate(file_list):
|
|
180
|
+
try:
|
|
181
|
+
if geom_num_list is None and xyz2list is not None:
|
|
182
|
+
tmp_positions, _, electric_charge_and_multiplicity = xyz2list(input_file, electric_charge_and_multiplicity)
|
|
183
|
+
else:
|
|
184
|
+
tmp_positions = geom_num_list
|
|
185
|
+
|
|
186
|
+
positions = np.array(tmp_positions, dtype="float64").reshape(-1, 3) # Angstrom
|
|
187
|
+
|
|
188
|
+
# --- SQM2 Energy and Gradient Calculation ---
|
|
189
|
+
# Initialize SQM2Calculator (Input: Angstrom)
|
|
190
|
+
calc = SQM2Calculator(
|
|
191
|
+
xyz=positions,
|
|
192
|
+
element_list=element_number_list,
|
|
193
|
+
charge=total_charge,
|
|
194
|
+
spin=spin
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Get energy and gradient
|
|
198
|
+
# Input: Angstrom, Output: (Hartree, Hartree/Bohr)
|
|
199
|
+
e, g = calc.total_gradient(positions)
|
|
200
|
+
|
|
201
|
+
# Output coordinates are in Bohr
|
|
202
|
+
positions_bohr = positions * ANGSTROM_TO_BOHR
|
|
203
|
+
|
|
204
|
+
S_mat = calc.get_overlap_matrix() # Get overlap matrix S (torch.Tensor)
|
|
205
|
+
natom = len(element_number_list)
|
|
206
|
+
if S_mat is not None and natom <= 10:
|
|
207
|
+
print("Overlap matrix S:")
|
|
208
|
+
|
|
209
|
+
for i in range(len(S_mat)):
|
|
210
|
+
print(" ".join([f"{S_mat[i, j].item():9.6f}" for j in range(len(S_mat))]))
|
|
211
|
+
|
|
212
|
+
# Get EHT MO energies and coefficients
|
|
213
|
+
#mo_energies = calc.get_eht_mo_energy().detach().cpu().numpy()
|
|
214
|
+
#mo_coefficients = calc.get_eht_mo_coeff().detach().cpu().numpy()
|
|
215
|
+
#if natom <= 10:
|
|
216
|
+
# print("Molecular Orbital Energies (Hartree):")
|
|
217
|
+
# for i, ene in enumerate(mo_energies):
|
|
218
|
+
# print(f"MO {i+1:2d}: {ene:12.6f} Hartree")
|
|
219
|
+
# print("Molecular Orbital Coefficients:")
|
|
220
|
+
# for i in range(len(mo_coefficients)):
|
|
221
|
+
# print(f"MO {i+1:2d}:"+" ".join([f"{mo_coefficients[i, j]:9.6f}" for j in range(len(mo_coefficients))]))
|
|
222
|
+
print("Notice: This output is displayed only for small molecules (n_atoms <= 10).")
|
|
223
|
+
# Save results
|
|
224
|
+
self.energy = e
|
|
225
|
+
self.gradient = g
|
|
226
|
+
self.coordinate = positions_bohr # Bohr
|
|
227
|
+
|
|
228
|
+
if self.FC_COUNT == -1 or isinstance(iter_index, str):
|
|
229
|
+
if self.hessian_flag:
|
|
230
|
+
self.exact_hessian(element_number_list, total_charge, positions) # Angstrom
|
|
231
|
+
elif iter_index % self.FC_COUNT == 0 or self.hessian_flag:
|
|
232
|
+
self.exact_hessian(element_number_list, total_charge, positions) # Angstrom
|
|
233
|
+
|
|
234
|
+
except Exception as error:
|
|
235
|
+
print(error)
|
|
236
|
+
|
|
237
|
+
return np.array([0]), np.array([0]), positions, finish_frag
|
|
238
|
+
|
|
239
|
+
# e (Hartree), g (Hartree/Bohr), positions_bohr (Bohr)
|
|
240
|
+
return e, g, positions_bohr, finish_frag
|
|
241
|
+
|
|
242
|
+
def single_point_no_directory(self, positions, element_number_list, electric_charge_and_multiplicity):
|
|
243
|
+
"""
|
|
244
|
+
Calculate single point energy and gradient without file I/O (SQM2).
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
positions: Atomic positions in Angstrom (n_atoms, 3)
|
|
248
|
+
"""
|
|
249
|
+
finish_frag = False
|
|
250
|
+
if isinstance(element_number_list[0], str):
|
|
251
|
+
tmp = copy.copy(element_number_list)
|
|
252
|
+
element_number_list = []
|
|
253
|
+
if element_number is not None:
|
|
254
|
+
for elem in tmp:
|
|
255
|
+
element_number_list.append(element_number(elem))
|
|
256
|
+
element_number_list = np.array(element_number_list)
|
|
257
|
+
try:
|
|
258
|
+
positions = np.array(positions, dtype='float64') # Angstrom
|
|
259
|
+
total_charge = int(electric_charge_and_multiplicity[0])
|
|
260
|
+
spin = int(electric_charge_and_multiplicity[1]) - 1
|
|
261
|
+
|
|
262
|
+
# --- SQM2 Energy and Gradient Calculation ---
|
|
263
|
+
calc = SQM2Calculator(
|
|
264
|
+
xyz=positions,
|
|
265
|
+
element_list=element_number_list,
|
|
266
|
+
charge=total_charge,
|
|
267
|
+
spin=spin
|
|
268
|
+
)
|
|
269
|
+
# Input: Angstrom, Output: (Hartree, Hartree/Bohr)
|
|
270
|
+
e, g = calc.total_gradient(positions)
|
|
271
|
+
|
|
272
|
+
self.energy = e
|
|
273
|
+
self.gradient = g
|
|
274
|
+
except Exception as error:
|
|
275
|
+
print(error)
|
|
276
|
+
print("This molecule could not be optimized.")
|
|
277
|
+
finish_frag = True
|
|
278
|
+
return np.array([0]), np.array([0]), finish_frag
|
|
279
|
+
|
|
280
|
+
# e (Hartree), g (Hartree/Bohr)
|
|
281
|
+
return e, g, finish_frag
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class CalculationEngine:
|
|
285
|
+
def calculate(self, file_directory, optimize_num, pre_total_velocity, config):
|
|
286
|
+
raise NotImplementedError
|
|
287
|
+
|
|
288
|
+
def _get_file_list(self, file_directory):
|
|
289
|
+
return sum([sorted(glob.glob(os.path.join(file_directory, f"*_" + "[0-9]" * i + ".xyz"))) for i in range(1, 7)], [])
|
|
290
|
+
|
|
291
|
+
def _process_visualization(self, energy_list, gradient_list, num_list, optimize_num, config):
|
|
292
|
+
try:
|
|
293
|
+
if getattr(config, 'save_pict', False):
|
|
294
|
+
visualizer = NEBVisualizer(config)
|
|
295
|
+
tmp_ene_list = np.array(energy_list, dtype='float64') * config.hartree2kcalmol
|
|
296
|
+
visualizer.plot_energy(num_list, tmp_ene_list - tmp_ene_list[0], optimize_num)
|
|
297
|
+
print("energy graph plotted.")
|
|
298
|
+
gradient_norm_list = [np.sqrt(np.linalg.norm(g)**2/(len(g)*3)) for g in gradient_list]
|
|
299
|
+
visualizer.plot_gradient(num_list, gradient_norm_list, optimize_num)
|
|
300
|
+
print("gradient graph plotted.")
|
|
301
|
+
except Exception as e:
|
|
302
|
+
print(f"Visualization error: {e}")
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class SQM2Engine(CalculationEngine):
|
|
306
|
+
"""SQM2 calculation engine wrapping SQM2Calculator"""
|
|
307
|
+
def __init__(self, param_file=None, device="cpu", dtype=torch.float64):
|
|
308
|
+
# SQM2Calculator holds parameters internally, not needed here
|
|
309
|
+
self.device = device
|
|
310
|
+
self.dtype = dtype
|
|
311
|
+
# SQM2Calculator is instantiated in the calculate method as it requires xyz
|
|
312
|
+
|
|
313
|
+
def calculate(self, file_directory, optimize_num, pre_total_velocity, config):
|
|
314
|
+
gradient_list = []
|
|
315
|
+
energy_list = []
|
|
316
|
+
geometry_num_list = []
|
|
317
|
+
gradient_norm_list = []
|
|
318
|
+
delete_pre_total_velocity = []
|
|
319
|
+
num_list = []
|
|
320
|
+
|
|
321
|
+
os.makedirs(file_directory, exist_ok=True)
|
|
322
|
+
file_list = self._get_file_list(file_directory)
|
|
323
|
+
|
|
324
|
+
if xyz2list is None:
|
|
325
|
+
raise ImportError("xyz2list is required for this method")
|
|
326
|
+
|
|
327
|
+
geometry_list_tmp, element_list, _ = xyz2list(file_list[0], None)
|
|
328
|
+
element_number_list = []
|
|
329
|
+
if element_number is not None:
|
|
330
|
+
for elem in element_list:
|
|
331
|
+
element_number_list.append(element_number(elem))
|
|
332
|
+
element_number_list = np.array(element_number_list, dtype='int')
|
|
333
|
+
|
|
334
|
+
for num, input_file in enumerate(file_list):
|
|
335
|
+
try:
|
|
336
|
+
print(input_file)
|
|
337
|
+
positions, _, electric_charge_and_multiplicity = xyz2list(input_file, None)
|
|
338
|
+
positions = np.array(positions, dtype='float64').reshape(-1, 3) # Angstrom
|
|
339
|
+
total_charge = int(electric_charge_and_multiplicity[0])
|
|
340
|
+
spin = int(electric_charge_and_multiplicity[1]) - 1
|
|
341
|
+
|
|
342
|
+
# --- SQM2 Energy and Gradient Calculation ---
|
|
343
|
+
calc = SQM2Calculator(
|
|
344
|
+
xyz=positions,
|
|
345
|
+
element_list=element_number_list,
|
|
346
|
+
charge=total_charge,
|
|
347
|
+
spin=spin
|
|
348
|
+
)
|
|
349
|
+
# Input: Angstrom, Output: (Hartree, Hartree/Bohr)
|
|
350
|
+
e, g = calc.total_gradient(positions)
|
|
351
|
+
|
|
352
|
+
print("\n")
|
|
353
|
+
energy_list.append(e) # Hartree
|
|
354
|
+
gradient_list.append(g) # Hartree/Bohr
|
|
355
|
+
gradient_norm_list.append(np.sqrt(np.linalg.norm(g)**2/(len(g)*3)))
|
|
356
|
+
geometry_num_list.append(positions) # Angstrom
|
|
357
|
+
num_list.append(num)
|
|
358
|
+
except Exception as error:
|
|
359
|
+
print(error)
|
|
360
|
+
print("This molecule could not be optimized.")
|
|
361
|
+
if optimize_num != 0:
|
|
362
|
+
delete_pre_total_velocity.append(num)
|
|
363
|
+
|
|
364
|
+
self._process_visualization(energy_list, gradient_list, num_list, optimize_num, config)
|
|
365
|
+
|
|
366
|
+
if optimize_num != 0 and len(pre_total_velocity) != 0:
|
|
367
|
+
pre_total_velocity = np.array(pre_total_velocity, dtype='float64').tolist()
|
|
368
|
+
for i in sorted(delete_pre_total_velocity, reverse=True):
|
|
369
|
+
pre_total_velocity.pop(i)
|
|
370
|
+
pre_total_velocity = np.array(pre_total_velocity, dtype='float64')
|
|
371
|
+
|
|
372
|
+
# (Hartree, Hartree/Bohr, Angstrom, velocity)
|
|
373
|
+
return (np.array(energy_list, dtype='float64'),
|
|
374
|
+
np.array(gradient_list, dtype='float64'),
|
|
375
|
+
np.array(geometry_num_list, dtype='float64'),
|
|
376
|
+
pre_total_velocity)
|