from tblite.ase import TBLite as XTB
from pharmaforge.interfaces import AbstractIO
[docs]
class XTBInterface(AbstractIO):
""" This class is used to calculate the energies and forces for a single point calculation using XTB and the
atomic simulation environment (ASE). For more information on the XTB calculator, see the ASE documentation.
.. code-block:: python
self.xtb_options = {
"method": "GFN2-xTB",
"accuracy": 1.0,
"electronic_temperature": 300.0,
}
Parameters
----------
default : bool
If True, use the default options for the XTB calculator. Default is False.
options : dict
A dictionary of options to pass to the XTB calculator. Default is an empty dictionary.
Attributes
----------
calculator : object
The XTB calculator object.
allow_parallel : bool
Whether to allow parallelization. Default is False.
level_of_theory : str
The level of theory used for the calculations. Default is "XTB".
options_dict : dict
A dictionary of options for the XTB calculator, that can include things not sent to the calculator.
xtb_options : dict
A dictionary of options for the XTB calculator, that are passed to the calculator.
See Also
--------
pharmaforge.interfaces.abstractio.AbstractIO : The abstract interface class for the Psi4Interface.
xtb.ase.calculator.XTB : The XTB calculator object.
Example
-------
>>> from pharmaforge.interfaces.xtbio import XTBInterface
>>> from ase import Atoms
>>> h2 = Atoms('HH', positions=[(0, 0, 0), (0, 0, 1)])
>>> xtb = XTBInterface(default=True)
>>> xtb.single_point(h2)
(-26.300431519257398, array([[-0. , -0. , 3.24309609],
... [-0. , -0. , -3.24309609]]))
"""
def __init__(self, default=False, **options):
"""Initialize the XTBInterface class."""
self.calculator = XTB
self.allow_parallel = False
self.options_dict = {}
if default:
self.xtb_options = {
"method": "GFN2-xTB",
"accuracy": 1.0,
"electronic_temperature": 300.0,
"verbosity":0,
"max-iter": 1000
}
else:
self.xtb_options = {}
for option, value in options.items():
self.xtb_options[option] = value
for key, value in options.items():
self.options_dict[key] = value
hartree_per_kelvin =(3.166808578545117e-6)
if "electronic_temperature" in self.xtb_options:
self.xtb_options["electronic_temperature"] = float(self.xtb_options["electronic_temperature"])*hartree_per_kelvin
self.level_of_theory = f"{self.xtb_options['method']}"
return
def _calculate(self, structure, charge=0):
"""Calculate the system data.
Parameters
----------
structure : ase.Atoms
The ASE atoms object containing the system data.
Returns
-------
calc : XTB
The XTB calculator object.
"""
# tblite is annoyingly verbose, so if verbosity is set to 0, we need to catch the output
if self.xtb_options.get("verbosity", 1) == 0:
import os
import sys
from contextlib import redirect_stdout, redirect_stderr
with open(os.devnull, 'w') as fnull:
with redirect_stdout(fnull), redirect_stderr(fnull):
structure.calc = self.calculator(charge=charge, **self.xtb_options)
else:
structure.calc = self.calculator(charge=charge, **self.xtb_options)
return structure
[docs]
def single_point(self, structure, charge=0):
"""Perform a single point calculation on the system.
Parameters
----------
structure : ase.Atoms
The ASE atoms object containing the system data.
Returns
-------
e : float
The energy of the system.
f : list
The forces acting on the atoms in the system.
"""
calc = self._calculate(structure)
e, f = self._report_data(calc)
return e, f