settingsbasisset.py 6.05 KB
Newer Older
1
from abc import ABC, abstractmethod
2
3
4
5
from collections import OrderedDict
import numpy as np
from typing import Tuple, List

6
from nomad.parsing.backend import Section
7
from nomad.utils import RestrictedDict
8

9
10
11
12
13
14
15
16
17
18
19
20

def get_basis_set_settings(context, backend, logger):
    """Decide which type of basis set settings are applicable to the entry
    and return a corresponding SettingsBasisSet class.
    """
    settings = None
    program_name = backend.get('program_name')
    if program_name == "exciting":
        settings = SettingsBasisSetExciting(context, backend, logger).settings
    elif program_name == "FHI-aims":
        settings = SettingsBasisSetFHIAims(context, backend, logger).settings
    return settings
21
22
23
24
25
26
27
28
29
30
31


class SettingsBasisSet(ABC):
    """Abstract base class for basis set settings

    Provides a factory() static method for delegating to the concrete
    implementation applicable for a given calculation.
    """
    def __init__(self, context, backend, logger):
        """
        """
32
33
34
35
36
37
        self._ctx = context
        self._backend = backend
        self._logger = logger
        mandatory, optional = self.setup()
        self.settings = RestrictedDict(mandatory, optional)
        self.fill()
38

39
40
41
    @abstractmethod
    def fill(self):
        """Used to fill the settings.
42
        """
43
        pass
44
45

    @abstractmethod
46
47
48
    def setup(self) -> Tuple:
        """Used to define a list of mandatory and optional settings for a
        subclass in the form of a RestrictedDict object.
49

50
51
52
        Returns:
            Should return a tuple of two lists: the first one defining
            mandatory keys and the second one defining optional keys.
53
        """
54
55
56
        mandatory: List = []
        optional: List = []
        return mandatory, optional
57
58


59
class SettingsBasisSetFHIAims(SettingsBasisSet):
60
    """Basis set settings for 'FHI-Aims' (code-dependent).
61
    """
62
63
64
65
    def setup(self) -> Tuple:
        mandatory, optional = super().setup()
        mandatory += ["FhiAims_basis"]
        return mandatory, optional
66

67
    def fill(self):
68
69
        """Special case of basis set settings for FHI-Aims code.
        """
70
        # Get basis set settings for each species
71
72
73
74
75
76
77
78
79
80
81
82
83
84
        aims_bs = self._backend.get('x_fhi_aims_section_controlIn_basis_set')
        if aims_bs is not None:
            bs_by_species = {}
            for this_aims_bs in aims_bs:
                this_bs_dict = self._values_to_dict(this_aims_bs, level=2)
                this_species = this_aims_bs['x_fhi_aims_controlIn_species_name'][0]
                bs_by_species[this_species] = this_bs_dict

            # Sort alphabetically by species label
            if bs_by_species:
                basis = OrderedDict()
                for k in sorted(bs_by_species.keys()):
                    basis[k] = bs_by_species[k]
                self.settings["FhiAims_basis"] = basis
85
86
87
88
89
90

    @classmethod
    def _values_to_dict(cls, data, level=0):
        result = None
        if data is None:
            return None
91
        elif isinstance(data, (Section, dict)):
92
93
94
95
96
97
98
99
100
            result = OrderedDict()
            for k in sorted(cls._filtered_section_keys(data)):
                v = data.get(k, None)
                result[k] = cls._values_to_dict(v, level=level + 1)
        elif isinstance(data, (list)):
            result = []
            for k in range(len(data)):
                v = data[k]
                result.append(cls._values_to_dict(v, level=level + 1))
101
        elif isinstance(data, (np.ndarray)):
102
103
104
105
106
            result = data.tolist()
        else:
            result = data
        return result

107
108
109
110
111
112
113
114
115
116
117
118
    @classmethod
    def _filtered_section_keys(cls, section):
        for k in section.keys():
            # skip JSON-specific keys
            if k == '_gIndex':
                continue
            if k == '_name':
                continue
            else:
                # json values and subsections
                yield k

119

120
class SettingsBasisSetExciting(SettingsBasisSet):
121
    """Basis set settings for 'Exciting' (code-dependent).
122
    """
123
124
125
126
127
128
129
130
131
132
133
134
    def setup(self) -> Tuple:
        mandatory, optional = super().setup()
        mandatory += [
            "muffin_tin_settings",
            "rgkmax",
            "gkmax",
            "lo",
            "lmaxapw",
        ]
        return mandatory, optional

    def fill(self):
135
136
137
138
        """Special case of basis set settings for Exciting code. See list at:
        https://gitlab.mpcdf.mpg.de/nomad-lab/encyclopedia-general/wikis/FHI-visit-preparation
        """

139
        # Add the muffin-tin settings for each species ordered alphabetically by atom label
140
        try:
141
            groups = self._backend["x_exciting_section_atoms_group"]
142
            groups = sorted(groups, key=lambda group: group["x_exciting_geometry_atom_labels"])
143
            muffin_tin_settings = OrderedDict()
144
145
146
            for group in groups:
                label = group["x_exciting_geometry_atom_labels"]
                try:
147
                    muffin_tin_settings["{}_muffin_tin_radius".format(label)] = "%.6f" % (1e+10 * group['x_exciting_muffin_tin_radius'])
148
                except KeyError:
149
                    muffin_tin_settings["{}_muffin_tin_radius".format(label)] = None
150
                try:
151
                    muffin_tin_settings["{}_muffin_tin_points".format(label)] = "%d" % group['x_exciting_muffin_tin_points']
152
                except KeyError:
153
154
                    muffin_tin_settings["{}_muffin_tin_points".format(label)] = None
            self.settings["muffin_tin_settings"] = muffin_tin_settings
155
156
        except KeyError:
            pass
157

158
        # Other important method settings
159
        system = self._ctx.representative_system
160
        try:
161
162
163
            self.settings['rgkmax'] = "%.6f" % (system['x_exciting_rgkmax'])
        except KeyError:
            pass
164
        try:
165
166
167
            self.settings['gkmax'] = "%.6f" % (1e-10 * system['x_exciting_gkmax'])
        except KeyError:
            pass
168
        try:
169
170
171
            self.settings['lo'] = "%d" % (system['x_exciting_lo'])
        except KeyError:
            pass
172
        try:
173
174
175
            self.settings['lmaxapw'] = "%d" % (system['x_exciting_lmaxapw'])
        except KeyError:
            pass