QuantumEspressoCommon.py 12.1 KB
Newer Older
1
import quantumespressoparser.setup_paths as setup_paths
2
3
4
import calendar
import json
import os
5
import sys
6
7
8
9
10
import re
import numpy as np
import logging
from nomadcore.local_meta_info import loadJsonFile, InfoKindEl
from nomadcore.unit_conversion.unit_conversion import convert_unit
11
from nomadcore.simple_parser import mainFunction, SimpleMatcher as SM, CachingLevel
12
from nomadcore.baseclasses import ParserInterface
13
14
15
16
17
18
19

############################################################
# This file contains functions and constants that are needed
# by more than one parser.
############################################################


20
LOGGER = logging.getLogger(__name__)
21

22
23
# fortran float, alternate too-long-for-field fortran marker
RE_f = r"(?:[+-]?\d+(?:\.\d+)?(?:[eEdD][+-]?\d+)?|\*+)"
24
cRE_f = re.compile(RE_f)
25
# fortran int, alternate too-long-for-field fortran marker
Henning Glawe's avatar
Henning Glawe committed
26
RE_i = r"(?:[+-]?\d+|\*+)"
27
cRE_i = re.compile(RE_i)
Henning Glawe's avatar
Henning Glawe committed
28
NAN = float('nan')
29

30
def re_vec(name, units='', split="\s+"):
31
32
33
34
    """generator for 3-component vector regex"""
    if units:
        units = '__' + units
    res = (
Henning Glawe's avatar
Henning Glawe committed
35
36
37
        r'(?P<' + name + r'_x' + units + r'>' + RE_f + r')' + split +
        r'(?P<' + name + r'_y' + units + r'>' + RE_f + r')' + split +
        r'(?P<' + name + r'_z' + units + r'>' + RE_f + r')'
38
39
40
41
        )
    return res


42
43
# loading metadata from
# nomad-meta-info/meta_info/nomad_meta_info/quantum_espresso.nomadmetainfo.json
44
45
46
47
48
49
50
51
52
53
54
55
# META_INFO = loadJsonFile(
#     filePath=os.path.normpath(os.path.join(
#         os.path.dirname(os.path.abspath(__file__)),
#         "../../../../nomad-meta-info/meta_info/nomad_meta_info/quantum_espresso.nomadmetainfo.json")),
#     dependencyLoader=None,
#     extraArgsHandling=InfoKindEl.ADD_EXTRA_ARGS,
#     uri=None)[0]

import nomad_meta_info
metaInfoPath = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(nomad_meta_info.__file__)), "quantum_espresso.nomadmetainfo.json"))
metaInfoEnv, warnings = loadJsonFile(filePath = metaInfoPath, dependencyLoader = None, extraArgsHandling = InfoKindEl.ADD_EXTRA_ARGS, uri = None)
META_INFO = metaInfoEnv
56
57
58
59
60
61

PARSER_INFO_DEFAULT = {
  "name": "parser_quantum_espresso",
  "version": "0.0.1"
}

62
63
64
# constants for date conversion
MONTHS = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
           'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
Henning Glawe's avatar
Henning Glawe committed
65
MONTH_NUMBER = { MONTHS[num]: num+1 for num in range(0,12) }
66

67
68
69
70
71
72

QE_SMEARING_KIND = {
    '-99': 'fermi',
     '-1': 'marzari-vanderbilt',
      '0': 'gaussian',
      '1': 'methfessel-paxton',
73
74
75
76
    'Marzari-Vanderbilt smearing': 'marzari-vanderbilt',
     'Methfessel-Paxton smearing': 'methfessel-paxton',
              'gaussian smearing': 'gaussian',
           'Fermi-Dirac smearing': 'fermi',
77
             'tetrahedron method': 'tetrahedra',
78
79
80
}


81
class ParserQuantumEspresso():
82
    """Base class for all Quantum Espresso parsers"""
83
84
85
86
87
    def __init__(
        self, cachingLevelForMetaName=None, coverageIgnoreList=None,
        re_program_name=None, metainfo_to_keep=None, backend=None, default_units=None,
        metainfo_units=None, debug=True, log_level=logging.ERROR, store=True):

88
        self.re_program_name = re_program_name
89
90
        self.parserInfo = PARSER_INFO_DEFAULT.copy()
        self.cachingLevelForMetaName = {}
91
        self.backend = backend
92
93
94
95
96
97
        for name in META_INFO.infoKinds:
            # set all temporaries to caching-only
            if name.startswith('x_qe_t_'):
                self.cachingLevelForMetaName[name] = CachingLevel.Cache
        # common prosa in espresso output
        self.coverageIgnoreList = [
98
            # ignore empty lines
99
            r"\s*",
100
101
            # table separators
            r"^\s*[=%-]+\s*$",
102
            r"^\s*%\s*%\s*$",
103
104
105
        ]
        self.coverageIgnore = None

106
107
108
109
110
111
112
113
    # Old parser definition pre-Nomad-Fair (dts edit 11/02/2019)
    # def parse(self):
    #     self.coverageIgnore = re.compile(r"^(?:" + r"|".join(self.coverageIgnoreList) + r")$")
    #     mainFunction(self.mainFileDescription(), META_INFO, self.parserInfo,
    #                 cachingLevelForMetaName=self.cachingLevelForMetaName,
    #                 superContext=self)

    def parse(self, mainfile):
Henning Glawe's avatar
Henning Glawe committed
114
        self.coverageIgnore = re.compile(r"^(?:" + r"|".join(self.coverageIgnoreList) + r")$")
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
        logging.info('quantum espresso parser started')
        logging.getLogger('nomadcore').setLevel(logging.WARNING)
        backend = self.backend(META_INFO)
        # print("Main File Description in parse()")
        # print(self.mainFileDescription())
        # with patch.object(sys, 'argv', ['<exe>', '--uri', 'nmd://uri', mainfile]):
        mainFunction(
            self.mainFileDescription(),
            META_INFO,
            self.parserInfo,
            cachingLevelForMetaName=self.cachingLevelForMetaName,
            superContext=self,
            superBackend=backend,
            mainFile=mainfile)
        return backend
Henning Glawe's avatar
Henning Glawe committed
130

131
132
133
134
135
136
137
138
139
    def adHoc_suicide_qe_program_name(self, parser):
        if self.re_program_name is not None:
            if not self.re_program_name.match(
                    parser.lastMatch['x_qe_program_name']):
                raise Exception(
                    "mainFile program name was: %s, unsuited for %s" % (
                        parser.lastMatch['x_qe_program_name'],
                        type(self).__name__))

140
141
142
143
144
145
146
    def mainFileDescription(self):
        # assemble matchers and submatchers
        result = SM(
            name='root',
            weak=True,
            startReStr="",
            subMatchers=[
147
148
149
150
                SM(name='ktab_cIgn', coverageIgnore=True,
                   # early output seen in benchmark.out.v5.3.0.inp\=vdw1.in.1452257026
                   startReStr=r"\s*Generating kernel table - May take several minutes.*$",
                ),
151
152
153
154
155
156
157
158
                SM(name='newRun', repeats=True, required=True,
                   startReStr=(
                       # program name, e.g. PWSCF, DOS
                       r"\s*Program\s+(?P<x_qe_program_name>\S+)\s+v\." +
                       # version
                       r"(?P<program_version>\S+(?:\s+\(svn\s+rev\.\s+\d+\s*\))?)" +
                       r"\s+starts" +
                       # newer espresso: "on $date"
159
                       r"(?:(?:\s+on\s+(?P<time_run_date_start__strQeDate>.+?)?)\s*$|" +
160
161
162
                       # older espresso has just "..." and date on new line
                       r"(?:\s*\.\.\.)\s*$)"
                   ),
163
                   adHoc = self.adHoc_suicide_qe_program_name,
164
165
166
167
168
169
                   fixedStartValues={'program_name': 'Quantum Espresso',
                                     'program_basis_set_type': 'plane waves'},
                   sections=['section_run'],
                   subMatchers=([
                       # older espresso versions have start date on separate line
                       SM(name='run_date',
170
                          startReStr=r"\s*Today is\s*(?P<time_run_date_start__strQeDate>.+?)\s*$"
171
                       ),
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
                       SM(name='copyright_msg', coverageIgnore=True,
                          # ignore copyright/citation msg
                          startReStr=r"\s*This program is part of the open-source Quantum ESPRESSO suite",
                          subMatchers=[
                              SM(name='copyright_msg010', coverageIgnore=True,
                                 startReStr=r"\s*for quantum simulation of materials; please (?:cite|acknowledge)",
                              ),
                              SM(name='copyright_msg020', coverageIgnore=True,
                                 startReStr=r"\s*\"P. Giannozzi et al., J. Phys.:Condens. Matter 21 395502 \(2009\);\s*",
                              ),
                              SM(name='copyright_msg030', coverageIgnore=True,
                                 startReStr=r"\s*URL http://www.quantum-espresso.org\",\s*",
                              ),
                              SM(name='copyright_msg040', coverageIgnore=True,
                                 startReStr=r"\s*in publications or presentations arising from this work. More details at",
                              ),
188
                              # Changed CI to False
189
190
191
192
193
194
195
196
                              SM(name='copyright_msg050', coverageIgnore=True,
                                 startReStr=r"\s*http://www.quantum-espresso.org/quote(?:\.php)?",
                              ),
                              SM(name='copyright_msg055', coverageIgnore=True,
                                 startReStr=r"\s*http://www.quantum-espresso.org/wiki/index.php/Citing_Quantum-ESPRESSO\s*",
                              ),
                          ],
                       ),
Henning Glawe's avatar
Henning Glawe committed
197
198
199
200
201
202
203
204
205
                   ] + self.run_submatchers() + [
                       SM(name='end_date',
                          startReStr=r"\s*This run was terminated on:\s*(?P<time_run_date_end__strQeDate>.+?)\s*$",
                       ),
                       SM(name='job_done',
                          startReStr=r"\s*JOB DONE\.\s*",
                          adHoc=lambda p: p.backend.addValue('run_clean_end', True),
                       ),
                   ]),
206
207
208
209
210
211
212
213
                )
            ]
        )
        return result

    def run_submatchers(self):
        return []

214
    def strValueTransform_strQeDate(self, espresso_date):
215
216
        if espresso_date is None:
            return None
Henning Glawe's avatar
Henning Glawe committed
217
        epoch = 0
218
219
220
221
222
223
224
225
226
        match = re.match(
            r"(\d+)\s*([A-Za-z]+)\s*(\d+)\s+at\s+(\d+):\s*(\d+):\s*(\d+)",
            espresso_date)
        if match:
            month = MONTH_NUMBER[match.group(2)]
            epoch = calendar.timegm(
                (int(match.group(3)), int(month), int(match.group(1)),
                 int(match.group(4)), int(match.group(5)), int(match.group(6))))
        else:
Henning Glawe's avatar
Henning Glawe committed
227
228
229
230
231
232
233
234
235
236
237
            match = re.match(
                r"\s*(\d+):\s*(\d+):\s*(\d+)\s+(\d+)\s*([A-Za-z]+)\s*(\d+)\s*",
                espresso_date)
            if match:
                month = MONTH_NUMBER[match.group(5)]
                epoch = calendar.timegm(
                    (int(match.group(6)), int(month), int(match.group(4)),
                     int(match.group(1)), int(match.group(2)), int(match.group(3))))
            else:
                raise RuntimeError("unparsable date: %s", espresso_date)
        return(epoch)
238
    strValueTransform_strQeDate.units = 's'
Henning Glawe's avatar
Henning Glawe committed
239

240
241
242
    def strValueTransform_strQeTimespan(self, espresso_timespan):
        if espresso_timespan is None:
            return None
Henning Glawe's avatar
Henning Glawe committed
243
        match = ParserQuantumEspresso.strValueTransform_strQeTimespan.re.match(
244
            espresso_timespan)
245
246
247
248
        if not match:
            raise RuntimeError(
                "unparsable timespan (regex match failed): %s",
                espresso_timespan)
Henning Glawe's avatar
Henning Glawe committed
249
250
        had_time_components = 0
        timespan_seconds = 0.0
251
252
        if match.group('seconds') is not None:
            timespan_seconds += float(match.group('seconds'))
Henning Glawe's avatar
Henning Glawe committed
253
            had_time_components += 1
254
255
        if match.group('minutes') is not None:
            timespan_seconds += float(match.group('minutes'))*60
Henning Glawe's avatar
Henning Glawe committed
256
            had_time_components += 1
257
258
259
260
261
262
        if match.group('hours') is not None:
            timespan_seconds += float(match.group('hours'))*60*60
            had_time_components += 1
        if match.group('days') is not None:
            timespan_seconds += float(match.group('days'))*60*60*24
            had_time_components += 1
Henning Glawe's avatar
Henning Glawe committed
263
264
265
266
        if had_time_components == 0:
            raise RuntimeError(
                "unparsable timespan (no time components extracted): %s",
                espresso_timespan)
267
        return(timespan_seconds)
Henning Glawe's avatar
Henning Glawe committed
268
269
270
271
272
273
274
    strValueTransform_strQeTimespan.re = re.compile(
            r"(?:\s*(?P<days>\d+)\s*d)?" +
            r"(?:\s*(?P<hours>\d+)\s*h)?" +
            r"(?:\s*(?P<minutes>\d+)\s*m)?" +
            r"(?:\s*(?P<seconds>" + RE_f + ")\s*s)?" +
            r"\s*$" # all groups are optional, end-anchor with rubber-space
    )
275
276
    strValueTransform_strQeTimespan.units = 's'

277
278
279
280
    def addDict(self, backend, this_dict):
        for key, value in sorted(this_dict.items()):
            backend.addValue(key, value)

Henning Glawe's avatar
Henning Glawe committed
281
282
    def addSectionDict(self, backend, section_name, section_dict):
        gIndex = backend.openSection(section_name)
283
        self.addDict(backend, section_dict)
284
        backend.closeSection(section_name, gIndex)