parser_wien2k.py 19.2 KB
Newer Older
1
from builtins import object
2
# import setup_paths
3
import numpy as np
4
import ase.io
5
6
7
import os
import sys

Daria Tomecka's avatar
Daria Tomecka committed
8
from nomadcore.simple_parser import mainFunction, AncillaryParser, CachingLevel
9
10
from nomadcore.simple_parser import SimpleMatcher as SM
from nomadcore.local_meta_info import loadJsonFile, InfoKindEl
11
from nomadcore.unit_conversion import unit_conversion
12
13
14
15
import wien2kparser.wien2k_parser_struct as wien2k_parser_struct
import wien2kparser.wien2k_parser_in0 as wien2k_parser_in0
import wien2kparser.wien2k_parser_in1 as wien2k_parser_in1
import wien2kparser.wien2k_parser_in2 as wien2k_parser_in2
Lauri Himanen's avatar
Lauri Himanen committed
16
import logging
17

Markus Scheidgen's avatar
Markus Scheidgen committed
18
from nomad.parsing.legacy import CoESimpleMatcherParser
Daria Tomecka's avatar
Daria Tomecka committed
19
20

################################################################
Daria Tomecka's avatar
Daria Tomecka committed
21
# This is the parser for the main output file (.scf) of WIEN2k.
Daria Tomecka's avatar
Daria Tomecka committed
22
23
################################################################

Daria M. Tomecka's avatar
Daria M. Tomecka committed
24
# Copyright 2016-2018 Daria M. Tomecka, Fawzi Mohamed
Daria Tomecka's avatar
Daria Tomecka committed
25
26
27
28
29
30
31
32
33
34
35
36
37
#
# 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.

Daria M. Tomecka's avatar
Daria M. Tomecka committed
38
39
40
41
__author__ = "Daria M. Tomecka"
__maintainer__ = "Daria M. Tomecka"
__email__ = "tomeckadm@gmail.com;"
__date__ = "15/05/2017"
Daria Tomecka's avatar
Daria Tomecka committed
42

Daria Tomecka's avatar
Daria Tomecka committed
43

44
45
46
47
48
49
50
51
class Wien2kContext(object):
    """context for wien2k parser"""

    def __init__(self):
        self.parser = None

    def initialize_values(self):
        """allows to reset values if the same superContext is used to parse different files"""
Daria Tomecka's avatar
Daria Tomecka committed
52
        self.metaInfoEnv = self.parser.parserBuilder.metaInfoEnv
53
54
55
56
        self.rootSecMethodIndex = None
        self.secMethodIndex = None
        self.secSystemIndex = None
        self.scfIterNr = 0
Pavel Ondračka's avatar
Pavel Ondračka committed
57
        self.spinPol = None
58
        self.eTot = None
59
60
61
62
63
64
65
66

    def startedParsing(self, path, parser):
        """called when parsing starts"""
        self.parser = parser
        # allows to reset values if the same superContext is used to parse different files
        self.initialize_values()

    def onClose_x_wien2k_header(self, backend, gIndex, section):
67
68
69
70
71
72
73
74
75
        version_check = section["x_wien2k_version"]
#        riprova = section["x_wien2k_release_date"][0]
#        print("prova=",prova," riprova=",riprova)
        if version_check:
            backend.addValue("program_version",
                             section["x_wien2k_version"][0] + " " +
                             section["x_wien2k_release_date"][0])
        else:
            backend.addValue("program_version", "Before_wien2k11")
76

Daria Tomecka's avatar
Daria Tomecka committed
77
    def onOpen_section_system(self, backend, gIndex, section):
Daria Tomecka's avatar
Daria Tomecka committed
78
        self.secSystemIndex = gIndex
Daria Tomecka's avatar
Daria Tomecka committed
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93
    def onOpen_section_method(self, backend, gIndex, section):

        mainFile = self.parser.fIn.fIn.name
        fName = mainFile[:-4] + ".in0"
        if os.path.exists(fName):
            subSuperContext = wien2k_parser_in0.Wien2kIn0Context()
            subParser = AncillaryParser(
                fileDescription = wien2k_parser_in0.buildIn0Matchers(),
                parser = self.parser,
                cachingLevelForMetaName = wien2k_parser_in0.get_cachingLevelForMetaName(self.metaInfoEnv, CachingLevel.PreOpenedIgnore),
                superContext = subSuperContext)
            with open(fName) as fIn:
                subParser.parseFile(fIn)

94

95
96
        mainFile = self.parser.fIn.fIn.name
        fName = mainFile[:-4] + ".in1"
97
98
        if not os.path.exists(fName):
            fName = mainFile[:-4] + ".in1c"
99
100
101
102
103
104
105
106
107
108
109
110
111
        if os.path.exists(fName):
            subSuperContext = wien2k_parser_in1.Wien2kIn1Context()
            subParser = AncillaryParser(
                fileDescription = wien2k_parser_in1.buildIn1Matchers(),
                parser = self.parser,
                cachingLevelForMetaName = wien2k_parser_in1.get_cachingLevelForMetaName(self.metaInfoEnv, CachingLevel.PreOpenedIgnore),
                superContext = subSuperContext)
            with open(fName) as fIn:
                subParser.parseFile(fIn)


        mainFile = self.parser.fIn.fIn.name
        fName = mainFile[:-4] + ".in2"
112
113
        if not os.path.exists(fName):
            fName = mainFile[:-4] + ".in2c"
114
115
116
117
118
119
120
121
122
        if os.path.exists(fName):
            subSuperContext = wien2k_parser_in2.Wien2kIn2Context()
            subParser = AncillaryParser(
                fileDescription = wien2k_parser_in2.buildIn2Matchers(),
                parser = self.parser,
                cachingLevelForMetaName = wien2k_parser_in2.get_cachingLevelForMetaName(self.metaInfoEnv, CachingLevel.PreOpenedIgnore),
                superContext = subSuperContext)
            with open(fName) as fIn:
                subParser.parseFile(fIn)
Daria Tomecka's avatar
Daria Tomecka committed
123

124
125
126
127
128
129
130
131
        #if self.secMethodIndex is None:
        if self.rootSecMethodIndex is None:
            self.rootSecMethodIndex = gIndex
        self.secMethodIndex = gIndex
#        self.secMethodIndex["single_configuration_to_calculation_method_ref"] = gIndex


    def onClose_section_single_configuration_calculation(self, backend, gIndex, section):
132

133
134
135
136
137
138
       # write number of SCF iterations
        backend.addValue('number_of_scf_iterations', self.scfIterNr)
        # write the references to section_method and section_system
        backend.addValue('single_configuration_to_calculation_method_ref', self.secMethodIndex)
        backend.addValue('single_configuration_calculation_to_system_ref', self.secSystemIndex)

139
140
141
        if self.eTot is not None:
            backend.addValue("energy_total", self.eTot)

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
        mainFile = self.parser.fIn.fIn.name

        eigvalKpoint=[]
        eigvalKpointMult=[]
        eigvalVal=[[]]

        # Read eigenvalues from the energy files
        # this can be either case.energy for non spinpolarized serial calculation
        # case.energyup and case.energydn for spinpolarized serial calculation
        # case.energy_x files for non spinpolarized parallel calculation
        # case.energyup_x and case.energydn_x for spinpolarized parallel calculation
        suffixes = ["", "up", "dn"]
        spin = 0

        for suf in suffixes:
            for i in range(1000):
                paraIdx = ""
                if i != 0:
                    paraIdx = "_" + str(i)
                fName = mainFile[:-4] + ".energy" + suf + paraIdx
                if os.path.exists(fName) and os.stat(fName).st_size > 0:
                    with open(fName) as g:
                        if suf == "dn":
                            spin = 1
                            if len(eigvalVal) == 1:
                                eigvalVal.append([])
                        else:
                            spin = 0
                        fromR = unit_conversion.convert_unit_function("rydberg", "J")
                        eigvalToRead=0
                        for l in g:
                            if len(l) > 90:
                                continue
                            #there are no spaces sometime between the numbers but the format is fixed
                            if eigvalToRead == 0:
                                kx = float(l[0:19])
                                ky = float(l[19:38])
                                kz = float(l[38:57])
                                eigvalKpointInd = int(l[57:67])
                                if len(eigvalKpoint) == eigvalKpointInd - 1:
                                    eigvalKpoint.append([kx, ky, kz])
                                    eigvalKpointMult.append([int(float(l[79:84]))])
                                if len(eigvalVal[spin]) != eigvalKpointInd -1:
                                    break
                                    #raise Exception("Found old eigenvalues for the current k-point while reading from %s,\
                                    #        possible mixing of serial nad parallel runs?" % fName)
                                eigvalToRead = int(l[73:79])
                                eigvalVal[spin].append([])
                            else:
                                eigvalVal[spin][-1].append(fromR(float(l[12:31])))
                                eigvalToRead -= 1
                elif i > 0:
                    break
        if eigvalVal[0]: 
            eigvalGIndex = backend.openSection("section_eigenvalues")
            backend.addArrayValues("eigenvalues_values", np.asarray(eigvalVal))
            backend.addArrayValues("eigenvalues_kpoints", np.asarray(eigvalKpoint))
            backend.addArrayValues("eigenvalues_kpoints_multiplicity", np.asarray(eigvalKpointMult))
            backend.closeSection("section_eigenvalues",eigvalGIndex)
201
202
203
204
205
206
207

    def onClose_section_system(self, backend, gIndex, section):

        #   atom labels
        atom_labels = section['x_wien2k_atom_name']
        if atom_labels is not None:
           backend.addArrayValues('atom_labels', np.asarray(atom_labels))
208

209
210
211
212
213
214
215
216
217
218
        # atom force
        atom_force = []
        for i in ['x', 'y', 'z']:
            api = section['x_wien2k_for_' + i]
            if api is not None:
               atom_force.append(api)
        if atom_force:
            # need to transpose array since its shape is [number_of_atoms,3] in\the metadata
           backend.addArrayValues('atom_forces', np.transpose(np.asarray(atom_force)))

219
        # Parse the structure file
220
221
222
        mainFile = self.parser.fIn.fIn.name
        fName = mainFile[:-4] + ".struct"
        if os.path.exists(fName):
223

224
            # ASE does not support reading file object for WIEN2k structure files.
Lauri Himanen's avatar
Lauri Himanen committed
225
226
227
228
229
230
231
232
233
234
235
236
237
            try:
                atoms = ase.io.read(fName, format="struct")
            except Exception:
                logging.error("Could not read/parse the WIEN2k structure file.")
            else:
                pos = atoms.get_positions() * 1E-10
                symbols = atoms.get_chemical_symbols()
                cell = atoms.get_cell() * 1E-10
                pbc = atoms.get_pbc()
                backend.addArrayValues('lattice_vectors', cell)
                backend.addArrayValues("configuration_periodic_dimensions", pbc)
                backend.addValue("atom_labels", symbols)
                backend.addArrayValues('atom_positions', pos)
238
239
240
241
242
243
244
245
246

            with open(fName, "r") as fin:
                structSuperContext = wien2k_parser_struct.Wien2kStructContext()
                structParser = AncillaryParser(
                    fileDescription = wien2k_parser_struct.buildStructureMatchers(),
                    parser = self.parser,
                    cachingLevelForMetaName = wien2k_parser_struct.get_cachingLevelForMetaName(self.metaInfoEnv, CachingLevel.PreOpenedIgnore),
                    superContext = structSuperContext)
                structParser.parseFile(fin)
247

Pavel Ondračka's avatar
Pavel Ondračka committed
248
249
250
251
252
253
254
255
256
    def onClose_section_method(self, backend, gIndex, section):
        #Trigger called when section_method is closed.

        if self.spinPol is not None:
            if self.spinPol:
                backend.addValue("number_of_spin_channels", 2)
            else:
                backend.addValue("number_of_spin_channels", 1)

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
        mainFile = self.parser.fIn.fIn.name

        #read kpoints from the klist file
        klistFile = mainFile[:-4] + ".klist"
        if os.path.exists(klistFile):
            with open(klistFile) as f:
                kMeshWeights=[]
                weightsSum=0.0
                kMeshPoints=[]
                for l in f:
                    tmp = l.split()
                    if len(tmp) >= 6:
                        kMeshWeights.append(float(tmp[5]))
                        weightsSum += kMeshWeights[-1]
                        kMeshPoints.append([float(tmp[1])/float(tmp[4]), float(tmp[2])/float(tmp[4]), float(tmp[3])/float(tmp[4])])
                    elif tmp[0] == 'END': break
                kMeshWeights = [w/weightsSum for w in kMeshWeights]
                backend.addArrayValues("k_mesh_points", np.asarray(kMeshPoints))
                backend.addArrayValues("k_mesh_weights", np.asarray(kMeshWeights))

277
278
    def onClose_section_scf_iteration(self, backend, gIndex, section):
        #Trigger called when section_scf_iteration is closed.
279

280
281
282
        # count number of SCF iterations
        self.scfIterNr += 1

Pavel Ondračka's avatar
Pavel Ondračka committed
283
284
285
286
287
288
        sp = section["x_wien2k_spinpolarization"]
        if sp is not None:
            if "NON" in sp[0]:
                self.spinPol = False
            else:
                self.spinPol = True
289
290
291
292
        
        eTot = section["energy_total_scf_iteration"]
        if eTot is not None:
            self.eTot = eTot[0]
Pavel Ondračka's avatar
Pavel Ondračka committed
293

294
295
296
297
298
# description of the input
mainFileDescription = SM(
    name = 'root',
    weak = True,
    startReStr = "",
299
    sections   = ['section_run','x_wien2k_header'],
300
    subMatchers = [
301
        SM(r"\s*:LABEL[0-9]+: using WIEN2k_(?P<x_wien2k_version>[0-9.]+) \(Release (?P<x_wien2k_release_date>[0-9/.]+)\) in "),
302
        SM(name = 'newRun',
303
304
305
306
#           subMatchers=[
#           SM(r"\s*:LABEL[0-9]+: using WIEN2k_(?P<x_wien2k_version>[0-9.]+) \(Release (?P<x_wien2k_release_date>[0-9/.]+)\) in ")
#           ],
           startReStr = r"\s*:ITE[0-9]+:\s*[0-9]+.\s*ITERATION",
307
308
309
           repeats = True,
           required = True,
           forwardMatch = True,
310
311
           sections   = ['section_method', 'section_system', 'section_single_configuration_calculation'],
           fixedStartValues={'program_name': 'WIEN2k', 'program_basis_set_type': '(L)APW+lo' }, #, 'program_version': 'Before WIEN2k_11'},
312
           subMatchers = [
313
314
315
316
               SM(
                  name = "scf iteration",
                  startReStr = r"\s*:ITE(?P<x_wien2k_iteration_number>[0-9]+):\s*[0-9]*. ITERATION",
                  sections=["section_scf_iteration"],
Daria Tomecka's avatar
Daria Tomecka committed
317
                  repeats = True,
318
                  subMatchers=[
319
                      SM(r":NATO\s*:\s*(?P<x_wien2k_nr_of_independent_atoms>[0-9]+)\s*INDEPENDENT AND\s*(?P<x_wien2k_total_atoms>[0-9]+)\s*TOTAL ATOMS IN UNITCELL"),
Daria Tomecka's avatar
Daria Tomecka committed
320
                      SM(r"\s*SUBSTANCE: (?P<x_wien2k_system_name>.*)"),
321
322
323
324
                      # older Wien2k versions used numerical identification for the potential
                      # newer Wien2k versions allow to specificy separatelly the functional for calculation of both exchange and correlation potential and energy separatelly
                      # the output can look like this, ":POT  : POTENTIAL OPTION EX_LDA EC_LDA VX_MBJ VC_LDA"
                      SM(r":POT\s*:\s*POTENTIAL OPTION\s*(?P<x_wien2k_potential_option>[0-9 \w]+)"),
325
                      SM(r":LAT\s*:\s*LATTICE CONSTANTS=\s*(?P<x_wien2k_lattice_const_a>[0-9.]+)\s*(?P<x_wien2k_lattice_const_b>[0-9.]+)\s*(?P<x_wien2k_lattice_const_c>[0-9.]+)"),
326
                      SM(r":VOL\s*:\s*UNIT CELL VOLUME\s*=\s*(?P<x_wien2k_unit_cell_volume_bohr3>[0-9.]+)"),
Pavel Ondračka's avatar
Pavel Ondračka committed
327
                      SM(r"\s*(?P<x_wien2k_spinpolarization>(NON-)?SPINPOLARIZED) CALCULATION\s*"),
328
                      SM(r":RKM  : MATRIX SIZE\s*(?P<x_wien2k_matrix_size>[0-9]+)\s*LOs:\s*(?P<x_wien2k_LOs>[0-9.]+)\s*RKM=\s*(?P<x_wien2k_rkm>[0-9.]+)\s*WEIGHT=\s*[0-9.]*\s*\w*:"),
329
                      SM(r":KPT\s*:\s*NUMBER\s*OF\s*K-POINTS:\s*(?P<x_wien2k_nr_kpts>[-+0-9.]+)"),
Daria Tomecka's avatar
Daria Tomecka committed
330
                      SM(r":GAP\s*:\s*(?P<x_wien2k_ene_gap__rydberg>[-+0-9.]+)\s*Ry\s*=\s*(?P<x_wien2k_ene_gap_eV>[-+0-9.]+)\s*eV\s*.*"),
331
                      SM(r":NOE\s*:\s*NUMBER\sOF\sELECTRONS\s*=\s*(?P<x_wien2k_noe>[0-9.]+)"),
332
333
                      SM(r":FER\s*:\sF E R M I - ENERGY\W\w*\W\w*M\W*=\s*(?P<energy_reference_fermi_iteration__rydberg>[-+0-9.]+)"),
                      SM(r":GMA\s*:\s*POTENTIAL\sAND\sCHARGE\sCUT-OFF\s*(?P<x_wien2k_cutoff>[0-9.]+)\s*Ry\W\W[0-9.]+"),
334
335
                      SM(r":CHA(?P<x_wien2k_atom_nr>[-+0-9]+):\s*TOTAL\s*\w*\s*CHARGE INSIDE SPHERE\s*(?P<x_wien2k_sphere_nr>[-+0-9]+)\s*=\s*(?P<x_wien2k_tot_val_charge_sphere>[0-9.]+)",repeats = True),
                      SM(r":CHA\s*:\s*TOTAL\s*\w*\s*CHARGE INSIDE\s*\w*\s*CELL\s=\s*(?P<x_wien2k_tot_val_charge_cell>[-+0-9.]+)"),
336
                      SM(r":SUM\s*:\s*SUM OF EIGENVALUES\s*=\s*(?P<energy_sum_eigenvalues_scf_iteration__rydberg>[-+0-9.]+)"),
337
                      SM(r":RTO(?P<x_wien2k_atom_nr>[-+0-9]+)\s*:\s*[0-9]+\s*(?P<x_wien2k_density_at_nucleus_valence>[-+0-9.]+)\s*(?P<x_wien2k_density_at_nucleus_semicore>[-+0-9.]+)\s*(?P<x_wien2k_density_at_nucleus_core>[-+0-9.]+)\s*(?P<x_wien2k_density_at_nucleus_tot>[0-9.]+)",repeats = True),
338
                      #FIXME: followig matchers work just for cases without spin polarization
339
340
                      SM(r":NTO\s*:\s*\sTOTAL\s*INTERSTITIAL\s*CHARGE=\s*(?P<x_wien2k_tot_int_charge_nm>[-+0-9.]+)"),
                      SM(r":NTO(?P<x_wien2k_atom_nr>[-+0-9]+)[0-9]*:\s*\sTOTAL\s*CHARGE\s*IN\s*SPHERE\s*(?P<x_wien2k_sphere_nr>[-+0-9]+)\s*=\s*(?P<x_wien2k_tot_charge_in_sphere_nm>[-+0-9.]+)",repeats = True),
341
                      SM(r":DTO(?P<x_wien2k_atom_nr>[-+0-9]+)[0-9]*:\sTOTAL\s*DIFFERENCE\s*CHARGE\W*\w*\s*IN\s*SPHERE\s*(?P<x_wien2k_sphere_nr>[-+0-9]+)\s*=\s*(?P<x_wien2k_tot_diff_charge>[-+0-9.]+)", repeats = True),
Daria Tomecka's avatar
Daria Tomecka committed
342
                      SM(r":DIS\s*:\s*CHARGE\sDISTANCE\s*\W*[0-9.]+\sfor\satom\s*[0-9]*\sspin\s[0-9]*\W\s*(?P<x_wien2k_charge_distance>[0-9.]+)"),
343
344
                      SM(r":CTO\s*:\s*\sTOTAL\s*INTERSTITIAL\s*CHARGE=\s*(?P<x_wien2k_tot_int_charge>[-+0-9.]+)"),
                      SM(r":CTO(?P<x_wien2k_atom_nr>[-+0-9]+)[0-9]*:\s*\sTOTAL\s*CHARGE\s*IN\s*SPHERE\s*(?P<x_wien2k_sphere_nr>[-+0-9]+)\s*=\s*(?P<x_wien2k_tot_charge_in_sphere>[-+0-9.]+)",repeats = True),
345
#                      SM(r":NEC(?P<x_wien2k_necnr>[-+0-9]+)\s*:\s*NUCLEAR AND ELECTRONIC CHARGE\s*(?P<x_wien2k_nuclear_charge>[-+0-9.]+)\s*(?P<x_wien2k_electronic_charge>[0-9.]+)",repeats = True),
346
347
348
349
350
                      SM(r":MMINT:\s*MAGNETIC MOMENT IN INTERSTITIAL\s*=\s*(?P<x_wien2k_mmint>[-+0-9.]+)"),
                      #FIXME: read for all spheres
                      SM(r":MMI001:\s*MAGNETIC MOMENT IN SPHERE\s*1\s*=\s*(?P<x_wien2k_mmi001>[-+0-9.]+)"),
                      # its not clear if the old and new MMTOT (total magnetic moment in cell vs spin magnetic moment in cell) are the same thing?
                      SM(r":MMTOT:\s*(TOTAL|SPIN) MAGNETIC MOMENT IN CELL\s*=\s*(?P<x_wien2k_mmtot>[-+0-9.]+)"),
351
                      SM(r":ENE\s*:\s*\W*\w*\W*\s*TOTAL\s*ENERGY\s*IN\s*Ry\s*=\s*(?P<energy_total_scf_iteration__rydberg>[-+0-9.]+)"),
352
                      SM(r":FOR[0-9]*:\s*(?P<x_wien2k_atom_nr>[0-9]+).ATOM\s*(?P<x_wien2k_for_abs>[0-9.]+)\s*(?P<x_wien2k_for_x>[-++0-9.]+)\s*(?P<x_wien2k_for_y>[-+0-9.]+)\s*(?P<x_wien2k_for_z>[-+0-9.]+)\s*partial\sforces", repeats = True),
Daria Tomecka's avatar
Daria Tomecka committed
353
                      SM(r":FGL[0-9]*:\s*(?P<x_wien2k_atom_nr>[0-9]+).ATOM\s*(?P<x_wien2k_for_x_gl>[-+0-9.]+)\s*(?P<x_wien2k_for_y_gl>[-+0-9.]+)\s*(?P<x_wien2k_for_z_gl>[-+0-9.]+)\s*partial\sforces", repeats = True)
354
                  ]
355
              )
356
357
           ]
       )
358
359
    ])

360

Markus Scheidgen's avatar
Markus Scheidgen committed
361
class Wien2kParser(CoESimpleMatcherParser):
362

Markus Scheidgen's avatar
Markus Scheidgen committed
363
364
365
    def metainfo_env(self):
        from .metainfo import m_env
        return m_env
366

Markus Scheidgen's avatar
Markus Scheidgen committed
367
368
    def create_super_context(self):
        return Wien2kContext()
369

Markus Scheidgen's avatar
Markus Scheidgen committed
370
371
    def create_simple_matcher(self):
        return mainFileDescription
372

Markus Scheidgen's avatar
Markus Scheidgen committed
373
374
375
376
377
    def create_parser_description(self):
        return {
            "name": "Wien2k",
            "version": "1.0"
        }
378

Markus Scheidgen's avatar
Markus Scheidgen committed
379
380
381
382
383
    def create_caching_levels(self):
        return {
            "XC_functional_name": CachingLevel.ForwardAndCache,
            "energy_total": CachingLevel.ForwardAndCache
        }