Coverage for /builds/debichem-team/python-ase/ase/calculators/onetep.py: 41.86%
43 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-03-06 04:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-03-06 04:00 +0000
1"""ONETEP interface for the Atomic Simulation Environment (ASE) package
3T. Demeyere, T.Demeyere@soton.ac.uk (2023)
5https://onetep.org"""
7from copy import deepcopy
9from ase.calculators.genericfileio import (
10 BaseProfile,
11 CalculatorTemplate,
12 GenericFileIOCalculator,
13 read_stdout,
14)
15from ase.io import read, write
18class OnetepProfile(BaseProfile):
19 """
20 ONETEP profile class.
21 """
23 configvars = {'pseudo_path'}
25 def __init__(self, command, pseudo_path, **kwargs):
26 """
27 Parameters
28 ----------
29 command: str
30 The onetep command (not including inputfile).
31 **kwargs: dict
32 Additional kwargs are passed to the BaseProfile
33 class.
34 """
35 super().__init__(command, **kwargs)
36 self.pseudo_path = pseudo_path
38 def version(self):
39 lines = read_stdout(self._split_command)
40 return self.parse_version(lines)
42 def parse_version(lines):
43 return '1.0.0'
45 def get_calculator_command(self, inputfile):
46 return [str(inputfile)]
49class OnetepTemplate(CalculatorTemplate):
50 _label = 'onetep'
52 def __init__(self, append):
53 super().__init__(
54 'ONETEP',
55 implemented_properties=[
56 'energy',
57 'free_energy',
58 'forces',
59 'stress'])
60 self.inputname = f'{self._label}.dat'
61 self.outputname = f'{self._label}.out'
62 self.errorname = f'{self._label}.err'
63 self.append = append
65 def execute(self, directory, profile):
66 profile.run(directory, self.inputname, self.outputname,
67 self.errorname, append=self.append)
69 def read_results(self, directory):
70 output_path = directory / self.outputname
71 atoms = read(output_path, format='onetep-out')
72 return dict(atoms.calc.properties())
74 def write_input(self, profile, directory, atoms, parameters, properties):
75 input_path = directory / self.inputname
77 parameters = deepcopy(parameters)
79 keywords = parameters.get('keywords', {})
80 keywords.setdefault('pseudo_path', profile.pseudo_path)
81 parameters['keywords'] = keywords
83 write(input_path, atoms, format='onetep-in',
84 properties=properties, **parameters)
86 def load_profile(self, cfg, **kwargs):
87 return OnetepProfile.from_config(cfg, self.name, **kwargs)
90class Onetep(GenericFileIOCalculator):
91 """
92 Class for the ONETEP calculator, uses ase/io/onetep.py.
93 Need the env variable "ASE_ONETEP_COMMAND" defined to
94 properly work. All other options are passed in kwargs.
96 Parameters
97 ----------
98 autorestart : Bool
99 When activated, manages restart keywords automatically.
100 append: Bool
101 Append to output instead of overwriting.
102 directory: str
103 Directory where to run the calculation(s).
104 keywords: dict
105 Dictionary with ONETEP keywords to write,
106 keywords with lists as values will be
107 treated like blocks, with each element
108 of list being a different line.
109 xc: str
110 DFT xc to use e.g (PBE, RPBE, ...).
111 ngwfs_count: int|list|dict
112 Behaviour depends on the type:
113 int: every species will have this amount
114 of ngwfs.
115 list: list of int, will be attributed
116 alphabetically to species:
117 dict: keys are species name(s),
118 value are their number:
119 ngwfs_radius: int|list|dict
120 Behaviour depends on the type:
121 float: every species will have this radius.
122 list: list of float, will be attributed
123 alphabetically to species:
124 [10.0, 9.0]
125 dict: keys are species name(s),
126 value are their radius:
127 {'Na': 9.0, 'Cl': 10.0}
128 pseudopotentials: list|dict
129 Behaviour depends on the type:
130 list: list of string(s), will be attributed
131 alphabetically to specie(s):
132 ['Cl.usp', 'Na.usp']
133 dict: keys are species name(s) their
134 value are the pseudopotential file to use:
135 {'Na': 'Na.usp', 'Cl': 'Cl.usp'}
136 pseudo_path: str
137 Where to look for pseudopotential, correspond
138 to the pseudo_path keyword of ONETEP.
140 .. note::
141 write_forces is always turned on by default
142 when using this interface.
144 .. note::
145 Little to no check is performed on the keywords provided by the user
146 via the keyword dictionary, it is the user responsibility that they
147 are valid ONETEP keywords.
148 """
150 def __init__(
151 self,
152 *,
153 profile=None,
154 directory='.',
155 **kwargs):
157 self.keywords = kwargs.get('keywords', None)
158 self.template = OnetepTemplate(
159 append=kwargs.pop('append', False)
160 )
162 super().__init__(profile=profile, template=self.template,
163 directory=directory,
164 parameters=kwargs)