Coverage for /builds/kinetik161/ase/ase/calculators/onetep.py: 32.65%
49 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
1"""ONETEP interface for the Atomic Simulation Environment (ASE) package
3T. Demeyere, T.Demeyere@soton.ac.uk (2023)
5https://onetep.org"""
7from pathlib import Path
9from ase.calculators.genericfileio import (CalculatorTemplate,
10 GenericFileIOCalculator,
11 read_stdout,
12 BaseProfile)
13from ase.io import read, write
15# TARP: New setup exc should be the onetep binary itself
16# def find_onetep_command(cmd):
17# exploded_cmd = cmd.split()
18# n_cmd = len(exploded_cmd)
19# if n_cmd == 1:
20# return cmd
21# for substring in exploded_cmd:
22# if 'onetep' in substring:
23# return substring
24# return None
27class OnetepProfile(BaseProfile):
28 def __init__(self, binary, **kwargs):
29 super().__init__(**kwargs)
30 self.binary = binary
32 def version(self):
33 # onetep_exec = find_onetep_command(self.argv)
34 lines = read_stdout(self.binary)
35 return self.parse_version(lines)
37 def parse_version(lines):
38 return '1.0.0'
40 def get_calculator_command(self, inputfile):
41 return [self.binary, str(inputfile)]
44class OnetepTemplate(CalculatorTemplate):
45 def __init__(self, label, append):
46 super().__init__(
47 name='ONETEP',
48 implemented_properties=[
49 'energy',
50 'free_energy',
51 'forces',
52 'stress'])
53 self.label = label
54 self.input = label + '.dat'
55 self.output = label + '.out'
56 self.error = label + '.err'
57 self.append = append
59 def execute(self, directory, profile):
60 profile.run(directory, self.input, self.output, self.error,
61 self.append)
63 def read_results(self, directory):
64 output_path = directory / self.output
65 atoms = read(output_path, format='onetep-out')
66 return dict(atoms.calc.properties())
68 def write_input(self, directory, atoms, parameters, properties):
69 input_path = directory / self.input
70 write(input_path, atoms, format='onetep-in',
71 properties=properties, **parameters)
73 def load_profile(self, cfg, **kwargs):
74 return OnetepProfile.from_config(cfg, self.name, **kwargs)
77class Onetep(GenericFileIOCalculator):
78 """
79 Class for the ONETEP calculator, uses ase/io/onetep.py.
80 Need the env variable "ASE_ONETEP_COMMAND" defined to
81 properly work. All other options are passed in kwargs.
83 Parameters
84 ----------
85 autorestart : Bool
86 When activated, manages restart keywords automatically.
87 append: Bool
88 Append to output instead of overwriting.
89 directory: str
90 Directory where to run the calculation(s).
91 keywords: dict
92 Dictionary with ONETEP keywords to write,
93 keywords with lists as values will be
94 treated like blocks, with each element
95 of list being a different line.
96 label: str
97 Name used for the ONETEP prefix.
98 xc: str
99 DFT xc to use e.g (PBE, RPBE, ...).
100 ngwfs_count: int|list|dict
101 Behaviour depends on the type:
102 int: every species will have this amount
103 of ngwfs.
104 list: list of int, will be attributed
105 alphabetically to species:
106 dict: keys are species name(s),
107 value are their number:
108 ngwfs_radius: int|list|dict
109 Behaviour depends on the type:
110 float: every species will have this radius.
111 list: list of float, will be attributed
112 alphabetically to species:
113 [10.0, 9.0]
114 dict: keys are species name(s),
115 value are their radius:
116 {'Na': 9.0, 'Cl': 10.0}
117 pseudopotentials: list|dict
118 Behaviour depends on the type:
119 list: list of string(s), will be attributed
120 alphabetically to specie(s):
121 ['Cl.usp', 'Na.usp']
122 dict: keys are species name(s) their
123 value are the pseudopotential file to use:
124 {'Na': 'Na.usp', 'Cl': 'Cl.usp'}
125 pseudo_path: str
126 Where to look for pseudopotential, correspond
127 to the pseudo_path keyword of ONETEP.
129 .. note::
130 write_forces is always turned on by default
131 when using this interface.
133 .. note::
134 Little to no check is performed on the keywords provided by the user
135 via the keyword dictionary, it is the user responsibility that they
136 are valid ONETEP keywords.
137 """
138 # TARP: I thought GenericFileIO calculators no longer had atoms attached
139 # to them
140 def __init__(
141 self,
142 label='onetep',
143 directory='.',
144 profile=None,
145 append=False,
146 autorestart=True,
147 atoms=None,
148 parallel_info=None,
149 parallel=True,
150 **kwargs):
152 self.directory = Path(directory)
153 self.autorestart = autorestart
154 self.label = label
155 self.keywords = kwargs.get('keywords', None)
156 self.append = append
157 self.template = OnetepTemplate(label, append=self.append)
159 kwargs['autorestart'] = self.autorestart
160 kwargs['directory'] = self.directory
161 kwargs['label'] = self.label
162 super().__init__(profile=profile, template=self.template,
163 directory=directory,
164 parameters=kwargs,
165 parallel=parallel,
166 parallel_info=parallel_info)
167 # Copy is probably not needed, but just in case
168 if atoms is not None:
169 self.atoms = atoms.copy()
170 if atoms.calc is not None:
171 # TARP: Does this make sense? Why append the previous
172 # calculators results to a new calculator. Especially
173 # without checking if the parameters are the same as well.
174 self.results = atoms.calc.results.copy()