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
|
File without changes
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import os
|
|
3
|
+
import numpy as np
|
|
4
|
+
import datetime
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from ase import Atoms
|
|
9
|
+
from ase.vibrations import Vibrations
|
|
10
|
+
except ImportError:
|
|
11
|
+
print("ASE is not installed. Please install ASE to use this module.")
|
|
12
|
+
|
|
13
|
+
from multioptpy.Utils.calc_tools import Calculationtools
|
|
14
|
+
from multioptpy.Parameters.parameter import UnitValueLib, number_element
|
|
15
|
+
from multioptpy.fileio import read_software_path, xyz2list
|
|
16
|
+
from multioptpy.Visualization.visualization import NEBVisualizer
|
|
17
|
+
from multioptpy.Calculator.ase_tools.gamess import ASE_GAMESSUS
|
|
18
|
+
from multioptpy.Calculator.ase_tools.nwchem import ASE_NWCHEM
|
|
19
|
+
from multioptpy.Calculator.ase_tools.gaussian import ASE_GAUSSIAN
|
|
20
|
+
from multioptpy.Calculator.ase_tools.orca import ASE_ORCA
|
|
21
|
+
from multioptpy.Calculator.ase_tools.fairchem import ASE_FAIRCHEM
|
|
22
|
+
from multioptpy.Calculator.ase_tools.mace import ASE_MACE
|
|
23
|
+
from multioptpy.Calculator.ase_tools.mopac import ASE_MOPAC
|
|
24
|
+
from multioptpy.Calculator.ase_tools.pygfn0 import ASE_GFN0
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
referrence:
|
|
28
|
+
|
|
29
|
+
@inproceedings{Batatia2022mace,
|
|
30
|
+
title={{MACE}: Higher Order Equivariant Message Passing Neural Networks for Fast and Accurate Force Fields},
|
|
31
|
+
author={Ilyes Batatia and David Peter Kovacs and Gregor N. C. Simm and Christoph Ortner and Gabor Csanyi},
|
|
32
|
+
booktitle={Advances in Neural Information Processing Systems},
|
|
33
|
+
editor={Alice H. Oh and Alekh Agarwal and Danielle Belgrave and Kyunghyun Cho},
|
|
34
|
+
year={2022},
|
|
35
|
+
url={https://openreview.net/forum?id=YPpSngE-ZU}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@misc{Batatia2022Design,
|
|
39
|
+
title = {The Design Space of E(3)-Equivariant Atom-Centered Interatomic Potentials},
|
|
40
|
+
author = {Batatia, Ilyes and Batzner, Simon and Kov{\'a}cs, D{\'a}vid P{\'e}ter and Musaelian, Albert and Simm, Gregor N. C. and Drautz, Ralf and Ortner, Christoph and Kozinsky, Boris and Cs{\'a}nyi, G{\'a}bor},
|
|
41
|
+
year = {2022},
|
|
42
|
+
number = {arXiv:2205.06643},
|
|
43
|
+
eprint = {2205.06643},
|
|
44
|
+
eprinttype = {arxiv},
|
|
45
|
+
doi = {10.48550/arXiv.2205.06643},
|
|
46
|
+
archiveprefix = {arXiv}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ASE
|
|
50
|
+
Ask Hjorth Larsen, Jens Jørgen Mortensen, Jakob Blomqvist,
|
|
51
|
+
Ivano E. Castelli, Rune Christensen, Marcin Dułak, Jesper Friis,
|
|
52
|
+
Michael N. Groves, Bjørk Hammer, Cory Hargus, Eric D. Hermes,
|
|
53
|
+
Paul C. Jennings, Peter Bjerre Jensen, James Kermode, John R. Kitchin,
|
|
54
|
+
Esben Leonhard Kolsbjerg, Joseph Kubal, Kristen Kaasbjerg,
|
|
55
|
+
Steen Lysgaard, Jón Bergmann Maronsson, Tristan Maxson, Thomas Olsen,
|
|
56
|
+
Lars Pastewka, Andrew Peterson, Carsten Rostgaard, Jakob Schiøtz,
|
|
57
|
+
Ole Schütt, Mikkel Strange, Kristian S. Thygesen, Tejs Vegge,
|
|
58
|
+
Lasse Vilhelmsen, Michael Walter, Zhenhua Zeng, Karsten Wedel Jacobsen
|
|
59
|
+
The Atomic Simulation Environment—A Python library for working with atoms
|
|
60
|
+
J. Phys.: Condens. Matter Vol. 29 273002, 2017
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
class Calculation:
|
|
65
|
+
def __init__(self, **kwarg):
|
|
66
|
+
UVL = UnitValueLib()
|
|
67
|
+
|
|
68
|
+
self.bohr2angstroms = UVL.bohr2angstroms
|
|
69
|
+
self.hartree2eV = UVL.hartree2eV
|
|
70
|
+
|
|
71
|
+
self.START_FILE = kwarg.get("START_FILE", None)
|
|
72
|
+
self.N_THREAD = kwarg.get("N_THREAD", 1)
|
|
73
|
+
self.SET_MEMORY = kwarg.get("SET_MEMORY", "2GB")
|
|
74
|
+
self.FUNCTIONAL = kwarg.get("FUNCTIONAL", "PBE")
|
|
75
|
+
self.BASIS_SET = kwarg.get("BASIS_SET", "6-31G(d)")
|
|
76
|
+
self.FC_COUNT = kwarg.get("FC_COUNT", 1)
|
|
77
|
+
self.BPA_FOLDER_DIRECTORY = kwarg.get("BPA_FOLDER_DIRECTORY", None)
|
|
78
|
+
self.Model_hess = kwarg.get("Model_hess", None)
|
|
79
|
+
self.unrestrict = kwarg.get("unrestrict", None)
|
|
80
|
+
self.software_type = kwarg.get("software_type", None)
|
|
81
|
+
self.hessian_flag = False
|
|
82
|
+
self.software_path_dict = read_software_path(kwarg.get("software_path_file", "./software_path.conf"))
|
|
83
|
+
|
|
84
|
+
def calc_exact_hess(self, calc_obj, positions, element_list):
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
timestamp = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S_%f")[:-2]
|
|
88
|
+
|
|
89
|
+
if self.software_type == "gaussian":
|
|
90
|
+
print("Calculating exact Hessian using Gaussian...")
|
|
91
|
+
exact_hess = calc_obj.calc_analytic_hessian() # in hartree/Bohr^2
|
|
92
|
+
|
|
93
|
+
else:
|
|
94
|
+
vib = Vibrations(atoms=calc_obj.atom_obj, delta=0.001, name="z_hess_"+timestamp)
|
|
95
|
+
vib.run()
|
|
96
|
+
result_vib = vib.get_vibrations()
|
|
97
|
+
exact_hess = result_vib.get_hessian_2d() # eV/Ų
|
|
98
|
+
vib.clean()
|
|
99
|
+
exact_hess = exact_hess / self.hartree2eV * (self.bohr2angstroms ** 2)
|
|
100
|
+
|
|
101
|
+
if type(element_list[0]) is str:
|
|
102
|
+
exact_hess = Calculationtools().project_out_hess_tr_and_rot_for_coord(exact_hess, element_list, positions)
|
|
103
|
+
else:
|
|
104
|
+
exact_hess = Calculationtools().project_out_hess_tr_and_rot_for_coord(exact_hess, [number_element(elem_num) for elem_num in element_list], positions)
|
|
105
|
+
exact_hess = (exact_hess + exact_hess.T) / 2 # make sure it's symmetric
|
|
106
|
+
|
|
107
|
+
self.Model_hess = exact_hess
|
|
108
|
+
|
|
109
|
+
return exact_hess
|
|
110
|
+
|
|
111
|
+
def single_point(self, file_directory, element_list, iter, electric_charge_and_multiplicity, method, geom_num_list=None):
|
|
112
|
+
"""execute extended tight binding method calculation."""
|
|
113
|
+
|
|
114
|
+
finish_frag = False
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
os.mkdir(file_directory)
|
|
118
|
+
except:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
if file_directory is None:
|
|
122
|
+
file_list = ["dummy"]
|
|
123
|
+
else:
|
|
124
|
+
file_list = glob.glob(file_directory+"/*_[0-9].xyz")
|
|
125
|
+
|
|
126
|
+
for num, input_file in enumerate(file_list):
|
|
127
|
+
try:
|
|
128
|
+
if geom_num_list is None:
|
|
129
|
+
positions, _, electric_charge_and_multiplicity = xyz2list(input_file, electric_charge_and_multiplicity)
|
|
130
|
+
else:
|
|
131
|
+
positions = geom_num_list
|
|
132
|
+
|
|
133
|
+
positions = np.array(positions, dtype="float64") # ang.
|
|
134
|
+
atom_obj = Atoms(element_list, positions)
|
|
135
|
+
calc_obj = setup_calculator(
|
|
136
|
+
atom_obj,
|
|
137
|
+
self.software_type,
|
|
138
|
+
electric_charge_and_multiplicity,
|
|
139
|
+
input_file=input_file,
|
|
140
|
+
software_path_dict=self.software_path_dict,
|
|
141
|
+
functional=self.FUNCTIONAL,
|
|
142
|
+
basis_set=self.BASIS_SET,
|
|
143
|
+
set_memory=self.SET_MEMORY,
|
|
144
|
+
)
|
|
145
|
+
calc_obj.run()
|
|
146
|
+
g = -1*calc_obj.atom_obj.get_forces(apply_constraint=False) * self.bohr2angstroms / self.hartree2eV # eV/ang. to a.u.
|
|
147
|
+
e = calc_obj.atom_obj.get_potential_energy(apply_constraint=False) / self.hartree2eV # eV to hartree
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
if self.FC_COUNT == -1 or type(iter) is str:
|
|
151
|
+
if self.hessian_flag:
|
|
152
|
+
_ = self.calc_exact_hess(calc_obj, positions, element_list)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
elif iter % self.FC_COUNT == 0 or self.hessian_flag:
|
|
156
|
+
# exact numerical hessian
|
|
157
|
+
_ = self.calc_exact_hess(calc_obj, positions, element_list)
|
|
158
|
+
except Exception as error:
|
|
159
|
+
print(error)
|
|
160
|
+
print("This molecule could not be optimized.")
|
|
161
|
+
finish_frag = True
|
|
162
|
+
return np.array([0]), np.array([0]), np.array([0]), finish_frag
|
|
163
|
+
|
|
164
|
+
positions /= self.bohr2angstroms
|
|
165
|
+
self.energy = e
|
|
166
|
+
self.gradient = g
|
|
167
|
+
self.coordinate = positions
|
|
168
|
+
|
|
169
|
+
return e, g, positions, finish_frag
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class CalculationEngine(ABC):
|
|
174
|
+
#Base class for calculation engines
|
|
175
|
+
|
|
176
|
+
@abstractmethod
|
|
177
|
+
def calculate(self, file_directory, optimize_num, pre_total_velocity, config):
|
|
178
|
+
#Calculate energy and gradients
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
def _get_file_list(self, file_directory):
|
|
182
|
+
#Get list of input files
|
|
183
|
+
return sum([sorted(glob.glob(os.path.join(file_directory, f"*_" + "[0-9]" * i + ".xyz")))
|
|
184
|
+
for i in range(1, 7)], [])
|
|
185
|
+
|
|
186
|
+
def _process_visualization(self, energy_list, gradient_list, num_list, optimize_num, config):
|
|
187
|
+
#Process common visualization tasks
|
|
188
|
+
try:
|
|
189
|
+
if config.save_pict:
|
|
190
|
+
visualizer = NEBVisualizer(config)
|
|
191
|
+
tmp_ene_list = np.array(energy_list, dtype="float64") * config.hartree2kcalmol
|
|
192
|
+
visualizer.plot_energy(num_list, tmp_ene_list - tmp_ene_list[0], optimize_num)
|
|
193
|
+
print("energy graph plotted.")
|
|
194
|
+
|
|
195
|
+
gradient_norm_list = [np.sqrt(np.linalg.norm(g)**2/(len(g)*3)) for g in gradient_list]
|
|
196
|
+
visualizer.plot_gradient(num_list, gradient_norm_list, optimize_num)
|
|
197
|
+
print("gradient graph plotted.")
|
|
198
|
+
except Exception as e:
|
|
199
|
+
print(f"Visualization error: {e}")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class ASEEngine(CalculationEngine):
|
|
204
|
+
"""ASE-based calculation engine supporting multiple quantum chemistry software packages.
|
|
205
|
+
|
|
206
|
+
This engine uses the Atomic Simulation Environment (ASE) to interface with various
|
|
207
|
+
quantum chemistry software packages like GAMESSUS, NWChem, Gaussian, ORCA, MACE, and MOPAC.
|
|
208
|
+
"""
|
|
209
|
+
def __init__(self, **kwargs):
|
|
210
|
+
UVL = UnitValueLib()
|
|
211
|
+
self.software_path_file = kwargs.get("software_path_file", "./software_path.conf")
|
|
212
|
+
self.software_path_dict = read_software_path(self.software_path_file)
|
|
213
|
+
|
|
214
|
+
self.bohr2angstroms = UVL.bohr2angstroms
|
|
215
|
+
self.hartree2eV = UVL.hartree2eV
|
|
216
|
+
|
|
217
|
+
def calculate(self, file_directory, optimize_num, pre_total_velocity, config):
|
|
218
|
+
"""Calculate energy and gradients using ASE and the configured software.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
file_directory (str): Directory containing input files
|
|
222
|
+
optimize_num (int): Optimization iteration number
|
|
223
|
+
pre_total_velocity (np.ndarray): Previous velocities for dynamics
|
|
224
|
+
config (object): Configuration object with calculation parameters
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
tuple: (energy_list, gradient_list, geometry_num_list, pre_total_velocity)
|
|
228
|
+
"""
|
|
229
|
+
gradient_list = []
|
|
230
|
+
energy_list = []
|
|
231
|
+
geometry_num_list = []
|
|
232
|
+
gradient_norm_list = []
|
|
233
|
+
delete_pre_total_velocity = []
|
|
234
|
+
num_list = []
|
|
235
|
+
|
|
236
|
+
os.makedirs(file_directory, exist_ok=True)
|
|
237
|
+
file_list = self._get_file_list(file_directory)
|
|
238
|
+
|
|
239
|
+
if not file_list:
|
|
240
|
+
print("No input files found in directory.")
|
|
241
|
+
return (np.array([], dtype="float64"),
|
|
242
|
+
np.array([], dtype="float64"),
|
|
243
|
+
np.array([], dtype="float64"),
|
|
244
|
+
pre_total_velocity)
|
|
245
|
+
|
|
246
|
+
# Get element list from the first file
|
|
247
|
+
geometry_list_tmp, element_list, _ = xyz2list(file_list[0], None)
|
|
248
|
+
|
|
249
|
+
hess_count = 0
|
|
250
|
+
software_type = config.othersoft
|
|
251
|
+
|
|
252
|
+
for num, input_file in enumerate(file_list):
|
|
253
|
+
try:
|
|
254
|
+
print(f"Processing file: {input_file}")
|
|
255
|
+
positions, _, electric_charge_and_multiplicity = xyz2list(input_file, None)
|
|
256
|
+
|
|
257
|
+
positions = np.array(positions, dtype="float64") # in angstroms
|
|
258
|
+
atom_obj = Atoms(element_list, positions)
|
|
259
|
+
calc_obj = setup_calculator(
|
|
260
|
+
atom_obj,
|
|
261
|
+
software_type,
|
|
262
|
+
electric_charge_and_multiplicity,
|
|
263
|
+
input_file=input_file,
|
|
264
|
+
software_path_dict=self.software_path_dict,
|
|
265
|
+
functional=config.FUNCTIONAL,
|
|
266
|
+
basis_set=config.basisset,
|
|
267
|
+
set_memory=config.SET_MEMORY,
|
|
268
|
+
)
|
|
269
|
+
calc_obj.run()
|
|
270
|
+
# Calculate energy and forces
|
|
271
|
+
g = -1 * calc_obj.atom_obj.get_forces(apply_constraint=False) * self.bohr2angstroms / self.hartree2eV # eV/ang. to a.u.
|
|
272
|
+
e = calc_obj.atom_obj.get_potential_energy(apply_constraint=False) / self.hartree2eV # eV to hartree
|
|
273
|
+
|
|
274
|
+
# Store results
|
|
275
|
+
energy_list.append(e)
|
|
276
|
+
gradient_list.append(g)
|
|
277
|
+
gradient_norm_list.append(np.sqrt(np.linalg.norm(g)**2/(len(g)*3))) # RMS
|
|
278
|
+
geometry_num_list.append(positions / self.bohr2angstroms) # Convert to Bohr
|
|
279
|
+
num_list.append(num)
|
|
280
|
+
|
|
281
|
+
# Handle hessian calculation if needed
|
|
282
|
+
if config.FC_COUNT == -1 or isinstance(optimize_num, str):
|
|
283
|
+
pass
|
|
284
|
+
elif optimize_num % config.FC_COUNT == 0:
|
|
285
|
+
# Calculate exact numerical hessian
|
|
286
|
+
timestamp = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S_%f")[:-2]
|
|
287
|
+
if software_type == "gaussian":
|
|
288
|
+
exact_hess = calc_obj.calc_analytic_hessian() # in hartree/Bohr^2
|
|
289
|
+
exact_hess = exact_hess
|
|
290
|
+
else:
|
|
291
|
+
vib = Vibrations(atoms=calc_obj.atom_obj, delta=0.001, name="z_hess_"+timestamp)
|
|
292
|
+
vib.run()
|
|
293
|
+
result_vib = vib.get_vibrations()
|
|
294
|
+
exact_hess = result_vib.get_hessian_2d() # eV/Ų
|
|
295
|
+
vib.clean()
|
|
296
|
+
|
|
297
|
+
# Convert hessian units
|
|
298
|
+
exact_hess = exact_hess / self.hartree2eV * (self.bohr2angstroms ** 2)
|
|
299
|
+
|
|
300
|
+
# Project out translational and rotational modes
|
|
301
|
+
calc_tools = Calculationtools()
|
|
302
|
+
exact_hess = calc_tools.project_out_hess_tr_and_rot_for_coord(exact_hess, element_list, positions)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# Save hessian
|
|
306
|
+
np.save(os.path.join(config.NEB_FOLDER_DIRECTORY, f"tmp_hessian_{hess_count}.npy"), exact_hess)
|
|
307
|
+
hess_count += 1
|
|
308
|
+
|
|
309
|
+
except Exception as error:
|
|
310
|
+
print(f"Error: {error}")
|
|
311
|
+
print("This molecule could not be optimized.")
|
|
312
|
+
if optimize_num != 0:
|
|
313
|
+
delete_pre_total_velocity.append(num)
|
|
314
|
+
|
|
315
|
+
# Process visualization
|
|
316
|
+
self._process_visualization(energy_list, gradient_list, num_list, optimize_num, config)
|
|
317
|
+
|
|
318
|
+
# Update velocities if needed
|
|
319
|
+
if optimize_num != 0 and len(pre_total_velocity) != 0:
|
|
320
|
+
pre_total_velocity = np.array(pre_total_velocity, dtype="float64")
|
|
321
|
+
pre_total_velocity = pre_total_velocity.tolist()
|
|
322
|
+
for i in sorted(delete_pre_total_velocity, reverse=True):
|
|
323
|
+
pre_total_velocity.pop(i)
|
|
324
|
+
pre_total_velocity = np.array(pre_total_velocity, dtype="float64")
|
|
325
|
+
|
|
326
|
+
return (np.array(energy_list, dtype="float64"),
|
|
327
|
+
np.array(gradient_list, dtype="float64"),
|
|
328
|
+
np.array(geometry_num_list, dtype="float64"),
|
|
329
|
+
pre_total_velocity)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def setup_calculator(atom_obj, software_type, electric_charge_and_multiplicity, input_file=None, software_path_dict=None, functional=None, basis_set=None, set_memory=None):
|
|
334
|
+
"""Module-level helper to attach the appropriate calculator to an ASE Atoms object.
|
|
335
|
+
|
|
336
|
+
Parameters mirror the previous inline branches. Returns the atoms object with
|
|
337
|
+
.calc set.
|
|
338
|
+
"""
|
|
339
|
+
software_path_dict = software_path_dict or {}
|
|
340
|
+
|
|
341
|
+
if software_type == "gamessus":
|
|
342
|
+
calc_obj = ASE_GAMESSUS(atom_obj=atom_obj,
|
|
343
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
344
|
+
gamessus_path=software_path_dict.get("gamessus"),
|
|
345
|
+
functional=functional,
|
|
346
|
+
basis_set=basis_set)
|
|
347
|
+
|
|
348
|
+
return calc_obj
|
|
349
|
+
if software_type == "nwchem":
|
|
350
|
+
calc_obj = ASE_NWCHEM(atom_obj=atom_obj,
|
|
351
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
352
|
+
input_file=input_file,
|
|
353
|
+
functional=functional,
|
|
354
|
+
basis_set=basis_set,
|
|
355
|
+
memory=set_memory)
|
|
356
|
+
|
|
357
|
+
return calc_obj
|
|
358
|
+
if software_type == "gaussian":
|
|
359
|
+
calc_obj = ASE_GAUSSIAN(atom_obj=atom_obj,
|
|
360
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
361
|
+
functional=functional,
|
|
362
|
+
basis_set=basis_set,
|
|
363
|
+
memory=set_memory,
|
|
364
|
+
software_path_dict=software_path_dict)
|
|
365
|
+
return calc_obj
|
|
366
|
+
|
|
367
|
+
if software_type == "orca":
|
|
368
|
+
calc_obj = ASE_ORCA(atom_obj=atom_obj,
|
|
369
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
370
|
+
input_file=input_file,
|
|
371
|
+
orca_path=software_path_dict.get("orca"),
|
|
372
|
+
functional=functional,
|
|
373
|
+
basis_set=basis_set)
|
|
374
|
+
|
|
375
|
+
return calc_obj
|
|
376
|
+
|
|
377
|
+
if software_type == "uma-s-1":
|
|
378
|
+
calc_obj = ASE_FAIRCHEM(atom_obj=atom_obj,
|
|
379
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
380
|
+
software_path=software_path_dict.get("uma-s-1"),
|
|
381
|
+
software_type=software_type)
|
|
382
|
+
return calc_obj
|
|
383
|
+
if software_type == "uma-s-1p1":
|
|
384
|
+
calc_obj = ASE_FAIRCHEM(atom_obj=atom_obj,
|
|
385
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
386
|
+
software_path=software_path_dict.get("uma-s-1p1"),
|
|
387
|
+
software_type=software_type)
|
|
388
|
+
return calc_obj
|
|
389
|
+
if software_type == "uma-m-1p1":
|
|
390
|
+
calc_obj = ASE_FAIRCHEM(atom_obj=atom_obj,
|
|
391
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
392
|
+
software_path=software_path_dict.get("uma-m-1p1"),
|
|
393
|
+
software_type=software_type)
|
|
394
|
+
|
|
395
|
+
return calc_obj
|
|
396
|
+
if software_type == "mace_mp":
|
|
397
|
+
calc_obj = ASE_MACE(atom_obj=atom_obj,
|
|
398
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
399
|
+
software_path=software_path_dict.get("mace_mp"),
|
|
400
|
+
software_type=software_type)
|
|
401
|
+
return calc_obj
|
|
402
|
+
if software_type == "mace_off":
|
|
403
|
+
calc_obj = ASE_MACE(atom_obj=atom_obj,
|
|
404
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
405
|
+
software_path=software_path_dict.get("mace_off"),
|
|
406
|
+
software_type=software_type)
|
|
407
|
+
|
|
408
|
+
return calc_obj
|
|
409
|
+
if software_type == "GFN0-xTB":
|
|
410
|
+
calc_obj = ASE_GFN0(atom_obj=atom_obj,
|
|
411
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
412
|
+
software_type=software_type)
|
|
413
|
+
|
|
414
|
+
return calc_obj
|
|
415
|
+
if software_type == "mopac":
|
|
416
|
+
calc_obj = ASE_MOPAC(atom_obj=atom_obj,
|
|
417
|
+
electric_charge_and_multiplicity=electric_charge_and_multiplicity,
|
|
418
|
+
input_file=input_file)
|
|
419
|
+
return calc_obj
|
|
420
|
+
|
|
421
|
+
# Unknown software type
|
|
422
|
+
raise ValueError(f"Unsupported software type: {software_type}")
|
|
423
|
+
|
|
424
|
+
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class ASE_FAIRCHEM:
|
|
4
|
+
def __init__(self, **kwargs):
|
|
5
|
+
self.atom_obj = kwargs.get('atom_obj', None)
|
|
6
|
+
self.electric_charge_and_multiplicity = kwargs.get('electric_charge_and_multiplicity', None)
|
|
7
|
+
self.software_path = kwargs.get('software_path', None)
|
|
8
|
+
self.task_name = "omol"
|
|
9
|
+
self.software_type = kwargs.get('software_type', None)
|
|
10
|
+
print(f"ASE_FAIRCHEM: software_type = {self.software_type}")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run(self): # fairchem.core: version 2.x.x
|
|
14
|
+
try:
|
|
15
|
+
from fairchem.core import FAIRChemCalculator
|
|
16
|
+
from fairchem.core.units.mlip_unit import load_predict_unit
|
|
17
|
+
except ImportError:
|
|
18
|
+
raise ImportError("FAIRChem.core modules not found")
|
|
19
|
+
# Load the prediction unit
|
|
20
|
+
predict_unit = load_predict_unit(path=self.software_path, device="cpu")
|
|
21
|
+
|
|
22
|
+
# Set up the FAIRChem calculator
|
|
23
|
+
fairchem_calc = FAIRChemCalculator(predict_unit=predict_unit, task_name=self.task_name)
|
|
24
|
+
self.atom_obj.info = {"charge": int(self.electric_charge_and_multiplicity[0]),
|
|
25
|
+
"spin": int(self.electric_charge_and_multiplicity[1])}
|
|
26
|
+
self.atom_obj.calc = fairchem_calc
|
|
27
|
+
|
|
28
|
+
return self.atom_obj
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class ASE_GAMESSUS:
|
|
4
|
+
def __init__(self, **kwargs):
|
|
5
|
+
self.atom_obj = kwargs.get('atom_obj', None)
|
|
6
|
+
self.electric_charge_and_multiplicity = kwargs.get('electric_charge_and_multiplicity', None)
|
|
7
|
+
self.gamessus_path = kwargs.get('gamessus_path', None)
|
|
8
|
+
self.functional = kwargs.get('functional', None)
|
|
9
|
+
self.basis_set = kwargs.get('basis_set', None)
|
|
10
|
+
self.memory = kwargs.get('memory', None)
|
|
11
|
+
|
|
12
|
+
def run(self):
|
|
13
|
+
from ase.calculators.gamess_us import GAMESSUS
|
|
14
|
+
self.atom_obj.calc = GAMESSUS(userscr=self.gamessus_path,
|
|
15
|
+
contrl=dict(dfttyp=self.functional),
|
|
16
|
+
charge=self.electric_charge_and_multiplicity[0],
|
|
17
|
+
mult=self.electric_charge_and_multiplicity[1],
|
|
18
|
+
basis=self.basis_set)
|
|
19
|
+
return self.atom_obj
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# If you want to use Gaussian with ASE, you need to have ASE installed and Gaussian 16.
|
|
6
|
+
# ASE can be installed via pip:
|
|
7
|
+
# pip install ase
|
|
8
|
+
# Gaussian 16 must be installed separately and is not free software.
|
|
9
|
+
# Make sure to set the GAUSS_EXEDIR environment variable to point to the directory containing the Gaussian executables.
|
|
10
|
+
# You must write the directory of the Gaussian executables in the software_path.conf file.
|
|
11
|
+
# (e.x., C:\g16\bin for Windows or /usr/local/g16/g16 for Linux)
|
|
12
|
+
|
|
13
|
+
class ASE_GAUSSIAN:
|
|
14
|
+
def __init__(self, **kwargs):
|
|
15
|
+
self.atom_obj = kwargs.get('atom_obj', None)
|
|
16
|
+
self.electric_charge_and_multiplicity = kwargs.get('electric_charge_and_multiplicity', None)
|
|
17
|
+
self.functional = kwargs.get('functional', None)
|
|
18
|
+
self.basis_set = kwargs.get('basis_set', None)
|
|
19
|
+
self.memory = kwargs.get('memory', None)
|
|
20
|
+
self.software_path_dict = kwargs.get('software_path_dict', None)
|
|
21
|
+
self.gau_prog = "g16" # Default to Gaussian 16
|
|
22
|
+
from ase.calculators.gaussian import Gaussian
|
|
23
|
+
self.Gaussian = Gaussian
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run(self):
|
|
27
|
+
if os.name == "nt": # windows (for Gaussian16W)
|
|
28
|
+
os.environ['GAUSS_EXEDIR'] = self.software_path_dict.get("gaussian", "")
|
|
29
|
+
input_file = self.atom_obj.info.get('input_file', 'unknown')
|
|
30
|
+
abs_path = os.path.abspath(input_file)
|
|
31
|
+
#input_file_name = os.path.basename(abs_path)
|
|
32
|
+
input_dir = os.path.dirname(abs_path)
|
|
33
|
+
self.atom_obj.calc = self.Gaussian(xc=self.functional,
|
|
34
|
+
basis=self.basis_set,
|
|
35
|
+
scf='xqc,maxcon=128,maxcyc=32,conver=8',
|
|
36
|
+
mem=self.memory,
|
|
37
|
+
command=f'{self.gau_prog} {input_dir}\\Gaussian.com {input_dir}\\Gaussian.log',
|
|
38
|
+
charge=int(self.electric_charge_and_multiplicity[0]),
|
|
39
|
+
mult=int(self.electric_charge_and_multiplicity[1])
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
elif os.name == "posix": # Linux (for Gaussian16)
|
|
43
|
+
os.environ['GAUSS_EXEDIR'] = self.software_path_dict.get("gaussian", "")
|
|
44
|
+
input_file = self.atom_obj.info.get('input_file', 'unknown')
|
|
45
|
+
abs_path = os.path.abspath(input_file)
|
|
46
|
+
#input_file_name = os.path.basename(abs_path)
|
|
47
|
+
input_dir = os.path.dirname(abs_path)
|
|
48
|
+
atom_obj.calc = self.Gaussian(xc=functional,
|
|
49
|
+
basis=basis_set,
|
|
50
|
+
scf='xqc,maxcon=128,maxcyc=32,conver=8',
|
|
51
|
+
mem=memory,
|
|
52
|
+
command=f'{self.gau_prog} < {input_dir}/Gaussian.com > {input_dir}/Gaussian.log',
|
|
53
|
+
charge=int(electric_charge_and_multiplicity[0]),
|
|
54
|
+
mult=int(electric_charge_and_multiplicity[1])
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
raise EnvironmentError("Unsupported operating system")
|
|
58
|
+
|
|
59
|
+
return self.atom_obj
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def calc_analytic_hessian(self):
|
|
63
|
+
"""Calculate and return the analytic Hessian matrix."""
|
|
64
|
+
|
|
65
|
+
if self.atom_obj.calc is None:
|
|
66
|
+
raise ValueError("Calculator not set. Please run the 'run' method first.")
|
|
67
|
+
|
|
68
|
+
if os.name == "nt": # windows (for Gaussian16W)
|
|
69
|
+
os.environ['GAUSS_EXEDIR'] = self.software_path_dict.get("gaussian", "")
|
|
70
|
+
input_file = self.atom_obj.info.get('input_file', 'unknown')
|
|
71
|
+
abs_path = os.path.abspath(input_file)
|
|
72
|
+
input_dir = os.path.dirname(abs_path)
|
|
73
|
+
self.atom_obj.calc = self.Gaussian(xc=self.functional,
|
|
74
|
+
basis=self.basis_set,
|
|
75
|
+
scf='xqc,maxcon=128,maxcyc=32,conver=8',
|
|
76
|
+
extra='freq=noraman',
|
|
77
|
+
mem=self.memory,
|
|
78
|
+
command=f'{self.gau_prog} {input_dir}\\Gaussian.com {input_dir}\\Gaussian.log',
|
|
79
|
+
charge=int(self.electric_charge_and_multiplicity[0]),
|
|
80
|
+
mult=int(self.electric_charge_and_multiplicity[1]),
|
|
81
|
+
|
|
82
|
+
)
|
|
83
|
+
self.atom_obj.calc.calculate(self.atom_obj)
|
|
84
|
+
|
|
85
|
+
elif os.name == "posix": # Linux (for Gaussian16)
|
|
86
|
+
os.environ['GAUSS_EXEDIR'] = self.software_path_dict.get("gaussian", "")
|
|
87
|
+
input_file = self.atom_obj.info.get('input_file', 'unknown')
|
|
88
|
+
abs_path = os.path.abspath(input_file)
|
|
89
|
+
input_dir = os.path.dirname(abs_path)
|
|
90
|
+
self.atom_obj.calc = self.Gaussian(xc=self.functional,
|
|
91
|
+
basis=self.basis_set,
|
|
92
|
+
scf='xqc,maxcon=128,maxcyc=32,conver=8',
|
|
93
|
+
extra='freq=noraman',
|
|
94
|
+
mem=self.memory,
|
|
95
|
+
command=f'{self.gau_prog} < {input_dir}/Gaussian.com > {input_dir}/Gaussian.log',
|
|
96
|
+
charge=int(self.electric_charge_and_multiplicity[0]),
|
|
97
|
+
mult=int(self.electric_charge_and_multiplicity[1]),
|
|
98
|
+
|
|
99
|
+
)
|
|
100
|
+
self.atom_obj.calc.calculate(self.atom_obj)
|
|
101
|
+
else: # Unsupported OS
|
|
102
|
+
raise EnvironmentError("Unsupported operating system")
|
|
103
|
+
|
|
104
|
+
hessian = self._extract_hessian_from_output(input_dir + "/Gaussian")
|
|
105
|
+
|
|
106
|
+
return hessian # in hartree/Bohr^2
|
|
107
|
+
|
|
108
|
+
def _extract_hessian_from_output(self, label):
|
|
109
|
+
log_file = f"{label}.log"
|
|
110
|
+
|
|
111
|
+
if not os.path.exists(log_file):
|
|
112
|
+
raise FileNotFoundError(f"Gaussian output file {log_file} not found")
|
|
113
|
+
|
|
114
|
+
with open(log_file, 'r') as f:
|
|
115
|
+
lines = f.readlines()
|
|
116
|
+
|
|
117
|
+
# Find Hessian in output
|
|
118
|
+
reading_hessian = False
|
|
119
|
+
|
|
120
|
+
counter_1 = 0
|
|
121
|
+
counter_2 = 0
|
|
122
|
+
|
|
123
|
+
for i, line in enumerate(lines):
|
|
124
|
+
# Determine number of atoms
|
|
125
|
+
if 'NAtoms=' in line:
|
|
126
|
+
n_atoms = int(line.split('NAtoms=')[1].split()[0])
|
|
127
|
+
n_coords = n_atoms * 3
|
|
128
|
+
hessian = np.zeros((n_coords, n_coords))
|
|
129
|
+
|
|
130
|
+
# Locate Hessian matrix section
|
|
131
|
+
if 'Force constants in Cartesian coordinates:' in line:
|
|
132
|
+
reading_hessian = True
|
|
133
|
+
tmp_i = i
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
if reading_hessian and tmp_i + 1 < i:
|
|
137
|
+
# Parse Hessian data
|
|
138
|
+
if line.strip() == '':
|
|
139
|
+
reading_hessian = False
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
parts = line.split()
|
|
143
|
+
atom_counter_i = 0 + 5 * counter_1
|
|
144
|
+
|
|
145
|
+
if len(parts) > 1:
|
|
146
|
+
|
|
147
|
+
if "D" in parts[1]:
|
|
148
|
+
values = [float(x.replace('D', 'E')) for x in parts[1:]]
|
|
149
|
+
values = np.array(values, dtype=np.float64)
|
|
150
|
+
|
|
151
|
+
values_len = len(values)
|
|
152
|
+
|
|
153
|
+
hessian[(atom_counter_i + counter_2), atom_counter_i:(atom_counter_i + values_len)] = values
|
|
154
|
+
hessian[atom_counter_i:(atom_counter_i + values_len), (atom_counter_i + counter_2)] = values
|
|
155
|
+
counter_2 += 1
|
|
156
|
+
else:
|
|
157
|
+
counter_1 += 1
|
|
158
|
+
counter_2 = 0
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
hessian = (hessian + hessian.T) / 2 # Symmetrize Hessian
|
|
162
|
+
|
|
163
|
+
return hessian #hartree/Bohr^2
|
|
164
|
+
|
|
165
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ASE_MACE:
|
|
5
|
+
def __init__(self, **kwargs):
|
|
6
|
+
|
|
7
|
+
self.atom_obj = kwargs.get('atom_obj', None)
|
|
8
|
+
self.electric_charge_and_multiplicity = kwargs.get('electric_charge_and_multiplicity', None)
|
|
9
|
+
self.software_path = kwargs.get('software_path', None)
|
|
10
|
+
self.software_type = kwargs.get('software_type', None)
|
|
11
|
+
|
|
12
|
+
def set_nnp(self):
|
|
13
|
+
if self.software_type == "MACE_MP":
|
|
14
|
+
from mace.calculators import mace_mp
|
|
15
|
+
mace_mp = mace_mp()
|
|
16
|
+
return mace_mp
|
|
17
|
+
elif self.software_type == "MACE_OFF":
|
|
18
|
+
from mace.calculators import mace_off
|
|
19
|
+
mace_off = mace_off()
|
|
20
|
+
return mace_off
|
|
21
|
+
else:
|
|
22
|
+
raise ValueError(f"Unsupported software type: {self.software_type}")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run(self):
|
|
26
|
+
nnp_obj = self.set_nnp()
|
|
27
|
+
self.atom_obj.calc = nnp_obj
|
|
28
|
+
return self.atom_obj
|