Files
tools/calc_utils/futils/gaussian.py
2026-01-11 11:37:11 +08:00

367 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from ase.calculators import gaussian
from ase.calculators.calculator import FileIOCalculator
from ase.io import read, write
from typing import TYPE_CHECKING, Optional, Literal
from copy import deepcopy
GSUB = "/home/fanhj/calcs/lele/tools/gsub_wait"
gau_src= gaussian.Gaussian
gau_dyn_src = gaussian.GaussianDynamics
methodType = Optional[
str
| Literal[
"HF",
"MP2",
"MP3",
"MP4",
"MP4(DQ)",
"MP4(SDQ)",
"MP5",
"CCSD",
"CCSDT",
"QCISD",
"CID",
"CISD",
"CIS",
"B3LYP",
"B3PW91",
"BLYP",
"PBE",
"PBE0",
"M06",
"M062X",
"M06L",
"M06HF",
"CAM-B3LYP",
"wb97xd",
"wb97xd3",
"LC-wPBE",
"HSE06",
"LSDA",
"SVWN",
"PW91",
"mPW1PW91",
"HCTH",
"HCTH147",
"HCTH407",
"TPSSh",
"TPSS",
"revPBE",
"PBEPBE",
"B2PLYP",
"mPW2PLYP",
"B2PLYPD3",
"PBE0DH",
"PBEQIDH",
]
]
basisType = Optional[
str
| Literal[
"STO-3G",
"3-21G",
"6-31G",
"6-31G(d)",
"6-31G(d,p)",
"6-31+G(d)",
"6-31+G(d,p)",
"6-31++G(d,p)",
"6-311G",
"6-311G(d)",
"6-311G(d,p)",
"6-311+G(d)",
"6-311+G(d,p)",
"6-311++G(d,p)",
"cc-pVDZ",
"cc-pVTZ",
"cc-pVQZ",
"cc-pV5Z",
"cc-pV6Z",
"aug-cc-pVDZ",
"aug-cc-pVTZ",
"aug-cc-pVQZ",
"aug-cc-pV5Z",
"def2SVP",
"def2SVPD",
"def2TZVP",
"def2TZVPD",
"def2QZVP",
"def2QZVPP",
"LANL2DZ",
"LANL2MB",
"SDD",
"CEP-4G",
"CEP-31G",
"CEP-121G",
"DGDZVP",
"DGDZVP2",
"Gen",
"GenECP",
]
]
scrfSolventType = Optional[
str
| Literal['Water', 'Acetone', 'Acetonitrile', 'Aniline', 'Benzene', 'Bromoform', 'Butanol',
'CarbonDisulfide', 'CarbonTetrachloride', 'Chlorobenzene', 'Chloroform', 'Cyclohexane',
'Dichloroethane', 'Dichloromethane', 'Diethylether', 'Dimethylformamide', 'Dimethylsulfoxide',
'Ethanol', 'Ethylacetate', 'Heptane', 'Hexane', 'Methanol', 'Nitromethane', 'Octanol',
'Pyridine', 'Tetrahydrofuran', 'Toluene', 'Xylene'
]
]
class Gaussian(gau_src):
mem:int
nprocshared:Literal[20,32]
charge: int
mult: Optional[int]
def __init__(self,
proc:Literal[20,32]=32,
charge:int=0,
mult:Optional[int]=None,
mem="30GB",
label='Gaussian',
method:methodType=None,
basis:basisType=None,
fitting_basis:Optional[str]=None,
output_type:Literal['N','P']='P',
basisfile:Optional[str]=None,
basis_set:Optional[str]=None,
xc:Optional[str]=None,
extra:Optional[str]=None,
ioplist:Optional[list[str]]=None,
addsec=None,
spinlist=None,
zefflist=None,
qmomlist=None,
nmagmlist=None,
znuclist=None,
radnuclearlist=None,
chk:Optional[str]=None,
oldchk:Optional[str]=None,
nprocshared:Optional[int]=None,
scrfSolvent:scrfSolventType=None,
scrf:Optional[str]=None,
em:Optional[Literal["GD2","GD3","GD4","GD3BJ"]|str]=None,
**kwargs):
'''
Parameters
-----------
proc: int
A short name for nprocshared
method: str
Level of theory to use, e.g. ``hf``, ``ccsd``, ``mp2``, or ``b3lyp``.
Overrides ``xc`` (see below).
xc: str
Level of theory to use. Translates several XC functionals from
their common name (e.g. ``PBE``) to their internal Gaussian name
(e.g. ``PBEPBE``).
basis: str
The basis set to use. If not provided, no basis set will be requested,
which usually results in ``STO-3G``. Maybe omitted if basisfile is set
(see below).
fitting_basis: str
The name of the fitting basis set to use.
output_type: str
Level of output to record in the Gaussian
output file - this may be ``N``- normal or ``P`` -
additional.
basisfile: str
The name of the basis file to use. If a value is provided, basis may
be omitted (it will be automatically set to 'gen')
basis_set: str
The basis set definition to use. This is an alternative
to basisfile, and would be the same as the contents
of such a file.
charge: int
The system charge. If not provided, it will be automatically
determined from the ``Atoms`` objects initial_charges.
mult: int
The system multiplicity (``spin + 1``). If not provided, it will be
automatically determined from the ``Atoms`` objects
``initial_magnetic_moments``.
extra: str
Extra lines to be included in the route section verbatim.
It should not be necessary to use this, but it is included for
backwards compatibility.
ioplist: list
A collection of IOPs definitions to be included in the route line.
addsec: str
Text to be added after the molecular geometry specification, e.g. for
defining masses with ``freq=ReadIso``.
spinlist: list
A list of nuclear spins to be added into the nuclear
propeties section of the molecule specification.
zefflist: list
A list of effective charges to be added into the nuclear
propeties section of the molecule specification.
qmomlist: list
A list of nuclear quadropole moments to be added into
the nuclear propeties section of the molecule
specification.
nmagmlist: list
A list of nuclear magnetic moments to be added into
the nuclear propeties section of the molecule
specification.
znuclist: list
A list of nuclear charges to be added into the nuclear
propeties section of the molecule specification.
radnuclearlist: list
A list of nuclear radii to be added into the nuclear
propeties section of the molecule specification.
params: dict
Contains any extra keywords and values that will be included in either
the link0 section or route section of the gaussian input file.
To be included in the link0 section, the keyword must be one of the
following: ``mem``, ``chk``, ``oldchk``, ``schk``, ``rwf``,
``oldmatrix``, ``oldrawmatrix``, ``int``, ``d2e``, ``save``,
``nosave``, ``errorsave``, ``cpu``, ``nprocshared``, ``gpucpu``,
``lindaworkers``, ``usessh``, ``ssh``, ``debuglinda``.
Any other keywords will be placed (along with their values) in the
route section.
'''
if nprocshared is not None and proc is not None:
if nprocshared == proc:print("Providing both nprocshared and proc is not recomanded")
else:
raise ValueError("both nprocshared and proc provided, and inequal.")
if scrfSolvent is not None and scrf is not None:
raise ValueError("scrfSolvent and scrf both not None")
if scrfSolvent is not None:
scrf = "Solvent="+scrfSolvent
optional_keys = ['chk','oldchk','scrf', 'geom', 'integral', 'density', 'nosymm', 'symmetry', 'units',
'temperature', 'pressure', 'counterpoise', 'gfinput', 'gfprint', 'test',
'output', 'punch', 'prop', 'pseudo', 'restart', 'scale', 'sparse', 'window', 'em']
for key in optional_keys:
val = locals().get(key,None)
if val is not None:
kwargs[key] = val
super().__init__(
nprocshared = proc,
mem=mem,
label=label,
command=GSUB+" "+label,
charge = charge,
mult = mult,
method = method,
basis = basis,
fitting_basis = fitting_basis,
output_type = output_type,
basisfile = basisfile,
basis_set = basis_set,
xc = xc,
extra = extra,
ioplist = ioplist,
addsec = addsec,
spinlist = spinlist,
zefflist = zefflist,
qmomlist = qmomlist,
nmagmlist = nmagmlist,
znuclist = znuclist,
radnuclearlist = radnuclearlist,
**kwargs
)
assert self.fileio_rules
self.fileio_rules.stdin_name = '{prefix}.gin'
def mod(self,charge:int=0, mult:int=1) -> "Gaussian":
new = deepcopy(self)
new.charge = charge
new.mult = mult
return new
def write_input(self, atoms, properties=None, system_changes=None):
FileIOCalculator.write_input(self, atoms, properties, system_changes)
if TYPE_CHECKING:
assert isinstance(self.label, str)
assert self.parameters
write(self.label + '.gin', atoms, properties=properties,
format='gaussian-in', parallel=False, **self.parameters)
def read_results(self):
if TYPE_CHECKING:
assert isinstance(self.label, str)
output = read(self.label + '.out', format='gaussian-out')
assert output
self.calc = output.calc
self.results = output.calc.results
class GaussianDynamics(gau_dyn_src):
def __init__(self, atoms, calc=None):
super().__init__(atoms, calc=calc)
def run(self, **kwargs):
calc_old = self.atoms.calc
params_old = deepcopy(self.calc.parameters)
self.delete_keywords(kwargs)
self.delete_keywords(self.calc.parameters)
self.set_keywords(kwargs)
self.calc.set(**kwargs)
self.atoms.calc = self.calc
try:
self.atoms.get_potential_energy()
except OSError:
converged = False
else:
converged = True
atoms = read(self.calc.label + '.out')
self.atoms.cell = atoms.cell
self.atoms.positions = atoms.positions
self.atoms.calc = atoms.calc
self.calc.parameters = params_old
self.calc.reset()
if calc_old is not None:
self.atoms.calc = calc_old
return converged
class GaussianOptimizer(GaussianDynamics):
keyword = 'opt'
special_keywords = {
'fmax': '{}',
'steps': 'maxcycle={}',
}
class GaussianIRC(GaussianDynamics):
keyword = 'irc'
special_keywords = {
'direction': '{}',
'steps': 'maxpoints={}',
}
gaussian.Gaussian = Gaussian
gaussian.GaussianDynamics = GaussianDynamics
gaussian.GaussianOptimizer = GaussianOptimizer
gaussian.GaussianIRC = GaussianIRC
__all__ = [
'Gaussian',
'GaussianDynamics',
'GaussianOptimizer',
'GaussianIRC',
'GSUB'
]