"""ONETEP interface for the Atomic Simulation Environment (ASE) package
T. Demeyere, T.Demeyere@soton.ac.uk (2023)
https://onetep.org"""
from os import environ
from ase.calculators.genericfileio import (BaseProfile, CalculatorTemplate,
GenericFileIOCalculator,
read_stdout)
from ase.io import read, write
class OnetepProfile(BaseProfile):
"""
ONETEP profile class, additional "old" parameter
is automatically passed for now if the user uses the
now deprecated "ASE_ONETEP_COMMAND".
"""
def __init__(self, binary, old=False, **kwargs):
"""
Parameters
----------
binary: str
Path to the ONETEP binary.
old: bool
If True, will use the old ASE_ONETEP_COMMAND
interface.
**kwargs: dict
Additional kwargs are passed to the BaseProfile
class.
"""
super().__init__(**kwargs)
self.binary = binary
self.old = old
def version(self):
lines = read_stdout(self.binary)
return self.parse_version(lines)
def parse_version(lines):
return '1.0.0'
def get_calculator_command(self, inputfile):
if self.old:
return self.binary.split() + [str(inputfile)]
else:
return [self.binary, str(inputfile)]
class OnetepTemplate(CalculatorTemplate):
_label = 'onetep'
def __init__(self, append):
super().__init__(
'ONETEP',
implemented_properties=[
'energy',
'free_energy',
'forces',
'stress'])
self.inputname = f'{self._label}.dat'
self.outputname = f'{self._label}.out'
self.errorname = f'{self._label}.err'
self.append = append
def execute(self, directory, profile):
profile.run(directory, self.inputname, self.outputname,
self.errorname, append=self.append)
def read_results(self, directory):
output_path = directory / self.outputname
atoms = read(output_path, format='onetep-out')
return dict(atoms.calc.properties())
def write_input(self, profile, directory, atoms, parameters, properties):
input_path = directory / self.inputname
write(input_path, atoms, format='onetep-in',
properties=properties, **parameters)
def load_profile(self, cfg, **kwargs):
return OnetepProfile.from_config(cfg, self.name, **kwargs)
[docs]class Onetep(GenericFileIOCalculator):
"""
Class for the ONETEP calculator, uses ase/io/onetep.py.
Need the env variable "ASE_ONETEP_COMMAND" defined to
properly work. All other options are passed in kwargs.
Parameters
----------
autorestart : Bool
When activated, manages restart keywords automatically.
append: Bool
Append to output instead of overwriting.
directory: str
Directory where to run the calculation(s).
keywords: dict
Dictionary with ONETEP keywords to write,
keywords with lists as values will be
treated like blocks, with each element
of list being a different line.
xc: str
DFT xc to use e.g (PBE, RPBE, ...).
ngwfs_count: int|list|dict
Behaviour depends on the type:
int: every species will have this amount
of ngwfs.
list: list of int, will be attributed
alphabetically to species:
dict: keys are species name(s),
value are their number:
ngwfs_radius: int|list|dict
Behaviour depends on the type:
float: every species will have this radius.
list: list of float, will be attributed
alphabetically to species:
[10.0, 9.0]
dict: keys are species name(s),
value are their radius:
{'Na': 9.0, 'Cl': 10.0}
pseudopotentials: list|dict
Behaviour depends on the type:
list: list of string(s), will be attributed
alphabetically to specie(s):
['Cl.usp', 'Na.usp']
dict: keys are species name(s) their
value are the pseudopotential file to use:
{'Na': 'Na.usp', 'Cl': 'Cl.usp'}
pseudo_path: str
Where to look for pseudopotential, correspond
to the pseudo_path keyword of ONETEP.
.. note::
write_forces is always turned on by default
when using this interface.
.. note::
Little to no check is performed on the keywords provided by the user
via the keyword dictionary, it is the user responsibility that they
are valid ONETEP keywords.
"""
def __init__(
self,
*,
profile=None,
directory='.',
parallel_info=None,
parallel=True,
**kwargs):
self.keywords = kwargs.get('keywords', None)
self.template = OnetepTemplate(
append=kwargs.pop('append', False)
)
if 'ASE_ONETEP_COMMAND' in environ and profile is None:
import warnings
warnings.warn("using ASE_ONETEP_COMMAND env is \
deprecated, please use OnetepProfile",
FutureWarning)
profile = OnetepProfile(environ['ASE_ONETEP_COMMAND'], old=True)
super().__init__(profile=profile, template=self.template,
directory=directory,
parameters=kwargs,
parallel=parallel,
parallel_info=parallel_info)