dft.py 7.25 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Copyright 2018 Markus Scheidgen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an"AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
DFT specific metadata
"""

from typing import List
import re
from elasticsearch_dsl import Integer
22
import ase.data
23
24
25

from nomad import utils, config

26
from .base import CalcWithMetadata, DomainQuantity, Domain, get_optional_backend_value
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49


xc_treatments = {
    'gga': 'GGA',
    'hf_': 'HF',
    'oep': 'OEP',
    'hyb': 'hybrid',
    'mgg': 'meta-GGA',
    'vdw': 'vdW',
    'lda': 'LDA',
}
""" https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-meta-info/wikis/metainfo/XC-functional """

basis_sets = {
    'gaussians': 'gaussians',
    'realspacegrid': 'real-space grid',
    'planewaves': 'plane waves'
}

version_re = re.compile(r'(\d+(\.\d+(\.\d+)?)?)')


def map_functional_name_to_xc_treatment(name):
Markus Scheidgen's avatar
Markus Scheidgen committed
50
    if name == config.services.unavailable_value:
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
        return name

    return xc_treatments.get(name[:3].lower(), name)


def map_basis_set_to_basis_set_label(name):
    key = name.replace('_', '').replace('-', '').replace(' ', '').lower()
    return basis_sets.get(key, name)


def simplify_version(version):
    match = version_re.search(version)
    if match is None:
        return version
    else:
        return match.group(0)


class DFTCalcWithMetadata(CalcWithMetadata):

    def __init__(self, **kwargs):
        self.formula: str = None
        self.atoms: List[str] = []
        self.n_atoms: int = 0
        self.basis_set: str = None
        self.xc_functional: str = None
        self.system: str = None
        self.crystal_system: str = None
        self.spacegroup: str = None
        self.spacegroup_symbol: str = None
        self.code_name: str = None
        self.code_version: str = None

        self.n_total_energies = 0
        self.n_geometries = 0
        self.quantities = []
        self.geometries = []
        self.group_hash: str = None

        super().__init__(**kwargs)

    def apply_domain_metadata(self, backend):
93
94
        from nomad.normalizing.system import normalized_atom_labels

95
96
97
98
        logger = utils.get_logger(__name__).bind(
            upload_id=self.upload_id, calc_id=self.calc_id, mainfile=self.mainfile)

        self.code_name = backend.get_value('program_name', 0)
99
100
101
102
        try:
            self.code_version = simplify_version(backend.get_value('program_version', 0))
        except KeyError:
            self.code_version = config.services.unavailable_value
103

104
        self.atoms = get_optional_backend_value(backend, 'atom_labels', 'section_system', logger=logger)
105
106
107
        if hasattr(self.atoms, 'tolist'):
            self.atoms = self.atoms.tolist()
        self.n_atoms = len(self.atoms)
108
        self.atoms = list(set(normalized_atom_labels(set(self.atoms))))
109
110
        self.atoms.sort()

111
112
113
114
115
116
        self.crystal_system = get_optional_backend_value(
            backend, 'crystal_system', 'section_symmetry', logger=logger)
        self.spacegroup = get_optional_backend_value(
            backend, 'space_group_number', 'section_symmetry', 0, logger=logger)
        self.spacegroup_symbol = get_optional_backend_value(
            backend, 'international_short_symbol', 'section_symmetry', 0, logger=logger)
117
        self.basis_set = map_basis_set_to_basis_set_label(
118
119
120
121
122
            get_optional_backend_value(backend, 'program_basis_set_type', 'section_run', logger=logger))
        self.system = get_optional_backend_value(
            backend, 'system_type', 'section_system', logger=logger)
        self.formula = get_optional_backend_value(
            backend, 'chemical_composition_bulk_reduced', 'section_system', logger=logger)
123
        self.xc_functional = map_functional_name_to_xc_treatment(
124
            get_optional_backend_value(backend, 'XC_functional_name', 'section_method', logger=logger))
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

        self.group_hash = utils.hash(
            self.formula,
            self.spacegroup,
            self.basis_set,
            self.xc_functional,
            self.code_name,
            self.code_version,
            self.with_embargo,
            self.comment,
            self.references,
            self.uploader,
            self.coauthors)

        quantities = set()
        geometries = set()
        n_total_energies = 0
        n_geometries = 0

        for meta_info, _, value in backend._delegate.results.traverse():
            quantities.add(meta_info)
146
            if meta_info == 'section_single_configuration_calculation':  # 'energy_total'
147
148
149
150
151
152
153
154
155
156
157
158
                n_total_energies += 1
            if meta_info == 'section_system':
                n_geometries += 1
            if meta_info == 'configuration_raw_gid':
                geometries.add(value)

        self.quantities = list(quantities)
        self.geometries = list(geometries)
        self.n_total_energies = n_total_energies
        self.n_geometries = n_geometries


159
160
161
162
163
164
def only_atoms(atoms):
    numbers = [ase.data.atomic_numbers[atom] for atom in atoms]
    only_atoms = [ase.data.chemical_symbols[number] for number in sorted(numbers)]
    return ''.join(only_atoms)


165
Domain('DFT', DFTCalcWithMetadata, quantities=dict(
166
167
168
169
170
    formula=DomainQuantity(
        'The chemical (hill) formula of the simulated system.',
        order_default=True),
    atoms=DomainQuantity(
        'The atom labels of all atoms in the simulated system.',
171
        aggregations=len(ase.data.chemical_symbols), multi=True, zero_aggs=False),
172
173
174
175
    only_atoms=DomainQuantity(
        'The atom labels concatenated in species-number order. Used with keyword search '
        'to facilitate exclusive searches.',
        elastic_value=only_atoms, metadata_field='atoms', multi=True),
176
177
178
179
180
181
182
183
184
    basis_set=DomainQuantity(
        'The used basis set functions.', aggregations=10),
    xc_functional=DomainQuantity(
        'The xc functional type used for the simulation.', aggregations=10),
    system=DomainQuantity(
        'The system type of the simulated system.', aggregations=10),
    crystal_system=DomainQuantity(
        'The crystal system type of the simulated system.', aggregations=10),
    code_name=DomainQuantity(
185
        'The code name.', aggregations=40),
186
187
    spacegroup=DomainQuantity('The spacegroup of the simulated system as number'),
    spacegroup_symbol=DomainQuantity('The spacegroup as international short symbol'),
188
189
190
191
    geometries=DomainQuantity(
        'Hashes that describe unique geometries simulated by this code run.',
        metric=('geometries', 'cardinality')
    ),
192
193
    quantities=DomainQuantity(
        'All quantities that are used by this calculation',
194
        metric=('quantities', 'value_count'), multi=True
195
    ),
196
197
198
199
200
201
202
    n_total_energies=DomainQuantity(
        'Number of total energy calculations',
        metric=('total_energies', 'sum'),
        elastic_mapping=Integer()),
    n_geometries=DomainQuantity(
        'Number of unique geometries',
        elastic_mapping=Integer()),
203
    n_atoms=DomainQuantity('Number of atoms in the simulated system', elastic_mapping=Integer())))