Coverage for /builds/alexhroom/ase/ase/calculators/octopus.py: 75.93%
54 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-08-05 14:37 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-08-05 14:37 +0000
1"""ASE-interface to Octopus.
3Ask Hjorth Larsen <asklarsen@gmail.com>
4Carlos de Armas
6http://tddft.org/programs/octopus/
7"""
9import numpy as np
11from ase.calculators.genericfileio import (BaseProfile, CalculatorTemplate,
12 GenericFileIOCalculator)
13from ase.io.octopus.input import generate_input, process_special_kwargs
14from ase.io.octopus.output import read_eigenvalues_file, read_static_info
17class OctopusIOError(IOError):
18 pass
21class OctopusProfile(BaseProfile):
22 def get_calculator_command(self, inputfile):
23 return []
25 def version(self):
26 import re
27 from subprocess import check_output
28 txt = check_output([*self._split_command, '--version'],
29 encoding='ascii')
30 match = re.match(r'octopus\s*(.+)', txt)
31 # With MPI it prints the line for each rank, but we just match
32 # the first line.
33 return match.group(1)
36class OctopusTemplate(CalculatorTemplate):
37 _label = 'octopus'
39 def __init__(self):
40 super().__init__(
41 'octopus',
42 implemented_properties=['energy', 'forces', 'dipole', 'stress'],
43 )
44 self.outputname = f'{self._label}.out'
45 self.errorname = f'{self._label}.err'
47 def read_results(self, directory):
48 """Read octopus output files and extract data."""
49 results = {}
50 with open(directory / 'static/info') as fd:
51 results.update(read_static_info(fd))
53 # If the eigenvalues file exists, we get the eigs/occs from that one.
54 # This probably means someone ran Octopus in 'unocc' mode to
55 # get eigenvalues (e.g. for band structures), and the values in
56 # static/info will be the old (selfconsistent) ones.
57 eigpath = directory / 'static/eigenvalues'
58 if eigpath.is_file():
59 with open(eigpath) as fd:
60 kpts, eigs, occs = read_eigenvalues_file(fd)
61 kpt_weights = np.ones(len(kpts)) # XXX ? Or 1 / len(kpts) ?
62 # XXX New Octopus probably has symmetry reduction !!
63 results.update(eigenvalues=eigs, occupations=occs,
64 ibz_k_points=kpts,
65 k_point_weights=kpt_weights)
66 return results
68 def execute(self, directory, profile):
69 profile.run(directory, None, self.outputname,
70 errorfile=self.errorname)
72 def write_input(self, profile, directory, atoms, parameters, properties):
73 txt = generate_input(atoms, process_special_kwargs(atoms, parameters))
74 inp = directory / 'inp'
75 inp.write_text(txt)
77 def load_profile(self, cfg, **kwargs):
78 return OctopusProfile.from_config(cfg, self.name, **kwargs)
81class Octopus(GenericFileIOCalculator):
82 """Octopus calculator.
84 The label is always assumed to be a directory."""
86 def __init__(self, profile=None, directory='.', **kwargs):
87 """Create Octopus calculator.
89 Label is always taken as a subdirectory.
90 Restart is taken to be a label."""
92 super().__init__(profile=profile,
93 template=OctopusTemplate(),
94 directory=directory,
95 parameters=kwargs)
97 @classmethod
98 def recipe(cls, **kwargs):
99 from ase import Atoms
100 system = Atoms()
101 calc = Octopus(CalculationMode='recipe', **kwargs)
102 system.calc = calc
103 try:
104 system.get_potential_energy()
105 except OctopusIOError:
106 pass
107 else:
108 raise OctopusIOError('Expected recipe, but found '
109 'useful physical output!')