Coverage for /builds/kinetik161/ase/ase/calculators/octopus.py: 70.37%
54 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"""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 (CalculatorTemplate,
12 GenericFileIOCalculator,
13 BaseProfile)
14from ase.io.octopus.input import generate_input, process_special_kwargs
15from ase.io.octopus.output import read_eigenvalues_file, read_static_info
18class OctopusIOError(IOError):
19 pass
22class OctopusProfile(BaseProfile):
23 def __init__(self, binary, **kwargs):
24 super().__init__(**kwargs)
25 self.binary = binary
27 def get_calculator_command(self, inputfile):
28 return [self.binary]
30 def version(self):
31 import re
32 from subprocess import check_output
33 txt = check_output(self.argv + ['--version']).decode('ascii')
34 match = re.match(r'octopus\s*(.+)', txt)
35 # With MPI it prints the line for each rank, but we just match
36 # the first line.
37 return match.group(1)
40class OctopusTemplate(CalculatorTemplate):
41 def __init__(self):
42 super().__init__(
43 name='octopus',
44 implemented_properties=['energy', 'forces', 'dipole', 'stress'],
45 )
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, inputfile=None, outputfile='octopus.out')
71 def write_input(self, profile, directory, atoms, parameters, properties):
72 txt = generate_input(atoms, process_special_kwargs(atoms, parameters))
73 inp = directory / 'inp'
74 inp.write_text(txt)
76 def load_profile(self, cfg, **kwargs):
77 return OctopusProfile.from_config(cfg, self.name, **kwargs)
80class Octopus(GenericFileIOCalculator):
81 """Octopus calculator.
83 The label is always assumed to be a directory."""
85 def __init__(self,
86 profile=None,
87 directory='.',
88 parallel_info=None,
89 parallel=True,
90 **kwargs):
91 """Create Octopus calculator.
93 Label is always taken as a subdirectory.
94 Restart is taken to be a label."""
96 super().__init__(profile=profile,
97 template=OctopusTemplate(),
98 directory=directory,
99 parameters=kwargs,
100 parallel_info=parallel_info,
101 parallel=parallel)
103 @classmethod
104 def recipe(cls, **kwargs):
105 from ase import Atoms
106 system = Atoms()
107 calc = Octopus(CalculationMode='recipe', **kwargs)
108 system.calc = calc
109 try:
110 system.get_potential_energy()
111 except OctopusIOError:
112 pass
113 else:
114 raise OctopusIOError('Expected recipe, but found '
115 'useful physical output!')