diff --git a/.gitignore b/.gitignore index b471695f7752bb0638df650edaf203729fa82e80..a364e9ceda327e5ba081c95bde21b4ffc95896d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,14 @@ # use glob syntax. syntax: glob -*.ser -*.class *~ *.bak -#*.off +*.off *.old *.pyc *.bk *.swp .DS_Store +__pycache__/ # logging files detailed.log @@ -54,4 +53,4 @@ lib/ env/ # Setuptools install folder -parser/parser-vasp/vaspparser.egg-info/ +vaspparser.egg-info/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index df4d3bbef8a070d96ddc07696e9b7954401965f8..7cc4f9fa006a7470840bd621bed02a141bce197f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,4 @@ +# TODO this is still the nomad-coe ci config stages: - test diff --git a/README.md b/README.md index fa53e7e4fab62aa28d75fc55befc197aa8621a37..f6a19433437019b323c64877c76f1ac2d5996609 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ This is the main repository of the [NOMAD](https://www.nomad-coe.eu/) parser for # Example ```python - from vaspparser import VASPParser + from vaspparser import VASPRunParserInterface import matplotlib.pyplot as mpl # 1. Initialize a parser with a set of default units. default_units = ["eV"] - parser = VASPParser(default_units=default_units) + parser = VASPRunParserInterface(default_units=default_units) # 2. Parse a file path = "path/to/main.file" @@ -21,7 +21,7 @@ This is the main repository of the [NOMAD](https://www.nomad-coe.eu/) parser for ``` # Installation -The code is python 2 and python 3 compatible. First download and install +The code is python 3 compatible. First download and install the nomadcore package: ```sh diff --git a/parser/parser-vasp/vaspparser/__init__.py b/parser/parser-vasp/vaspparser/__init__.py deleted file mode 100644 index b7722573128ced8a50b6ca434563b5de84b54643..0000000000000000000000000000000000000000 --- a/parser/parser-vasp/vaspparser/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe -# -# 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. - -from vaspparser.parser import VASPParser diff --git a/parser/parser-vasp/vaspparser/parser_vasp.py b/parser/parser-vasp/vaspparser/parser_vasp.py deleted file mode 100644 index 128764e89e5f6ed5bbb24f2add346ad4656acde4..0000000000000000000000000000000000000000 --- a/parser/parser-vasp/vaspparser/parser_vasp.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe -# -# 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. - -from builtins import object -import vaspparser.setup_paths -from nomadcore.simple_parser import mainFunction, SimpleMatcher as SM -from nomadcore.local_meta_info import loadJsonFile, InfoKindEl -from nomadcore.caching_backend import CachingLevel -import os, sys, json, logging -import numpy as np - -# description of the input -mainFileDescription = SM( - name = 'root', - weak = True, - startReStr = "", - subMatchers = [ - SM(name = 'newRun', - startReStr = r"^\s*vasp.(?:[0-9.]+)\s+(?:[0-9]+[A-Za-z]+[0-9]+)\s+\(build (:[^)]+)\)\s+complex\s*", - repeats = True, - required = True, - forwardMatch = True, - sections = ['section_run','section_method'], - subMatchers = [ - SM(name = 'header', - startReStr = r"^\s*vasp.(?P<program_version>[0-9.]+)\s+(?P<vasp_src_date>[0-9]+[A-Za-z]+[0-9]+)\s+\(build (?P<vasp_build_date>[^)]+)\)\s+complex\s*", - subMatchers = [ - ] - ), - ]) -]) - -# loading metadata from nomad-meta-info/meta_info/nomad_meta_info/gaussian.nomadmetainfo.json -metaInfoPath = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../../../../../nomad-meta-info/meta_info/nomad_meta_info/vasp.nomadmetainfo.json")) -metaInfoEnv, warnings = loadJsonFile(filePath = metaInfoPath, dependencyLoader = None, extraArgsHandling = InfoKindEl.ADD_EXTRA_ARGS, uri = None) - -parserInfo = { - "name": "parser_vasp", - "version": "1.0" -} - -class VaspParserContext(object): - """main place to keep the parser status, open ancillary files,...""" - def __init__(self): - pass - - def startedParsing(self, path, parser): - self.parser = parser - -# which values to cache or forward (mapping meta name -> CachingLevel) -cachingLevelForMetaName = { -} - -if __name__ == "__main__": - mainFunction(mainFileDescription, metaInfoEnv, parserInfo, - cachingLevelForMetaName = cachingLevelForMetaName, - superContext = VaspParserContext()) diff --git a/parser/parser-vasp/vaspparser/scalainterface.py b/parser/parser-vasp/vaspparser/scalainterface.py deleted file mode 100644 index 19143cfaea535955e0c8327fdea7e2c3ceacf917..0000000000000000000000000000000000000000 --- a/parser/parser-vasp/vaspparser/scalainterface.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe -# -# 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. - -""" -This is the access point to the parser for the scala layer in the -NOMAD project. -""" -from __future__ import absolute_import -import sys -import setup_paths -from nomadcore.parser_backend import JsonParseEventsWriterBackend -from vaspparser import VASPParser - - -if __name__ == "__main__": - - # Initialise the parser with the main filename and a JSON backend - main_file = sys.argv[1] - parser = VASPParser(backend=JsonParseEventsWriterBackend) - parser.parse(main_file) diff --git a/parser/parser-vasp/vaspparser/setup_paths.py b/parser/parser-vasp/vaspparser/setup_paths.py deleted file mode 100644 index 68d9828b27e64123df754e5ae475a2c487c6edf5..0000000000000000000000000000000000000000 --- a/parser/parser-vasp/vaspparser/setup_paths.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe -# -# 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. - -""" -Setups the python-common library in the PYTHONPATH system variable. -""" -import sys -import os -import os.path - -baseDir = os.path.dirname(os.path.abspath(__file__)) -commonDir = os.path.normpath(os.path.join(baseDir, "../../../../../python-common/common/python")) -parserDir = os.path.normpath(os.path.join(baseDir, "..")) - -# Using sys.path.insert(1, ...) instead of sys.path.insert(0, ...) based on -# this discusssion: -# http://stackoverflow.com/questions/10095037/why-use-sys-path-appendpath-instead-of-sys-path-insert1-path -if commonDir not in sys.path: - sys.path.insert(1, commonDir) -if parserDir not in sys.path: - sys.path.insert(1, parserDir) diff --git a/parser/parser-vasp/vaspparser/vaspmainparser.py b/parser/parser-vasp/vaspparser/vaspmainparser.py deleted file mode 100644 index e15c34ff5c56b9fe568699566fbc2287add39bfc..0000000000000000000000000000000000000000 --- a/parser/parser-vasp/vaspparser/vaspmainparser.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe -# -# 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. - -from __future__ import absolute_import -import logging -import os -from vaspparser.parser_vasprun import VasprunContext, XmlParser, parserInfo -LOGGER = logging.getLogger("nomad") - - -class VASPMainParser(object): - """The main parser class that is called for all run types. Parses the VASP - XML output files. - """ - def __init__(self, parser_context): - """ - """ - self.parser_context = parser_context - - def parse(self, filepath): - superContext = VasprunContext() - parser = XmlParser(parserInfo, superContext) - backend = self.parser_context.super_backend - parser.parse(os.path.abspath(filepath), open(filepath), backend) diff --git a/setup.py b/setup.py index c73c3944d3908e23a0937e9889e8f4526f7f5217..d5193d8e61fe7511146386dabcfc7ec2226a5d1f 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from setuptools import setup, find_packages +from setuptools import setup def main(): @@ -20,10 +20,9 @@ def main(): name='vaspparser', version='0.1', description='NOMAD parser implementation for VASP.', - author='Fawzi Mohamed', + author='Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe', license='APACHE 2.0', - package_dir={'': 'parser/parser-vasp'}, - packages=find_packages('parser/parser-vasp'), + package='vaspparser', install_requires=[ 'nomadcore' ], diff --git a/src/main/scala/eu/nomad_lab/parsers/VaspParser.scala b/src/main/scala/eu/nomad_lab/parsers/VaspParser.scala deleted file mode 100644 index 17564fdca8ecb3545d20f64d6fad09bd474e9bcf..0000000000000000000000000000000000000000 --- a/src/main/scala/eu/nomad_lab/parsers/VaspParser.scala +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe - * - * 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. - */ - -package eu.nomad_lab.parsers -import eu.nomad_lab.DefaultPythonInterpreter -import org.{ json4s => jn } -import eu.{ nomad_lab => lab } -import scala.collection.breakOut - -object VaspParser extends SimpleExternalParserGenerator( - name = "VaspParser", - parserInfo = jn.JObject( - ("name" -> jn.JString("VaspParser")) :: - ("parserId" -> jn.JString("VaspParser" + lab.VaspVersionInfo.version)) :: - ("versionInfo" -> jn.JObject( - ("nomadCoreVersion" -> jn.JObject(lab.NomadCoreVersionInfo.toMap.map { - case (k, v) => k -> jn.JString(v.toString) - }(breakOut): List[(String, jn.JString)])) :: - (lab.VaspVersionInfo.toMap.map { - case (key, value) => - (key -> jn.JString(value.toString)) - }(breakOut): List[(String, jn.JString)]) - )) :: Nil - ), - mainFileTypes = Seq("text/.*"), - mainFileRe = """^\s*vasp.(?<version>[0-9.]+)\s+(?<srcDate>[0-9]+[A-Za-z]+[0-9]+)\s+\(build (?<buildDate>[^)]+)\)\s+complex\s* -""".r, - cmd = Seq(DefaultPythonInterpreter.pythonExe(), "${envDir}/parsers/vasp/parser/parser-vasp/vaspparser/parser_vasp.py", - "--uri", "${mainFileUri}", "${mainFilePath}"), - cmdCwd = "${mainFilePath}/..", - resList = Seq( - "parser-vasp/vaspparser/parser_vasp.py", - "parser-vasp/vaspparser/setup_paths.py", - "nomad_meta_info/public.nomadmetainfo.json", - "nomad_meta_info/common.nomadmetainfo.json", - "nomad_meta_info/meta_types.nomadmetainfo.json", - "nomad_meta_info/vasp.nomadmetainfo.json" - ) ++ DefaultPythonInterpreter.commonFiles(), - dirMap = Map( - "parser-vasp" -> "parsers/vasp/parser/parser-vasp", - "nomad_meta_info" -> "nomad-meta-info/meta_info/nomad_meta_info", - "python" -> "python-common/common/python/nomadcore" - ) ++ DefaultPythonInterpreter.commonDirMapping() -) - -object VaspRunParser extends SimpleExternalParserGenerator( - name = "VaspRunParser", - parserInfo = jn.JObject( - ("name" -> jn.JString("VaspRunParser")) :: - ("parserId" -> jn.JString("VaspRunParser" + lab.VaspVersionInfo.version)) :: - ("versionInfo" -> jn.JObject( - ("nomadCoreVersion" -> jn.JObject(lab.NomadCoreVersionInfo.toMap.map { - case (k, v) => k -> jn.JString(v.toString) - }(breakOut): List[(String, jn.JString)])) :: - (lab.VaspVersionInfo.toMap.map { - case (key, value) => - (key -> jn.JString(value.toString)) - }(breakOut): List[(String, jn.JString)]) - )) :: Nil - ), - mainFileTypes = Seq("application/xml"), - mainFileRe = """\s*<\?xml version="1\.0" encoding="ISO-8859-1"\?>\s* -?\s*<modeling> -?\s*<generator> -?\s*<i name="program" type="string">\s*vasp\s*</i> -?""".r, - cmd = Seq(DefaultPythonInterpreter.pythonExe(), "${envDir}/parsers/vasp/parser/parser-vasp/vaspparser/scalainterface.py", - "${mainFilePath}", "${mainFileUri}"), - cmdCwd = "${mainFilePath}/..", - resList = Seq( - "parser-vasp/vaspparser/__init__.py", - "parser-vasp/vaspparser/parser.py", - "parser-vasp/vaspparser/parser_vasprun.py", - "parser-vasp/vaspparser/scalainterface.py", - "parser-vasp/vaspparser/setup_paths.py", - "parser-vasp/vaspparser/vaspmainparser.py", - "nomad_meta_info/public.nomadmetainfo.json", - "nomad_meta_info/common.nomadmetainfo.json", - "nomad_meta_info/meta_types.nomadmetainfo.json", - "nomad_meta_info/vasp.nomadmetainfo.json" - ) ++ DefaultPythonInterpreter.commonFiles(), - dirMap = Map( - "parser-vasp" -> "parsers/vasp/parser/parser-vasp", - "nomad_meta_info" -> "nomad-meta-info/meta_info/nomad_meta_info", - "python" -> "python-common/common/python/nomadcore" - ) ++ DefaultPythonInterpreter.commonDirMapping() -) diff --git a/src/test/scala/eu/nomad_lab/parsers/VaspParserSpec.scala b/src/test/scala/eu/nomad_lab/parsers/VaspParserSpec.scala deleted file mode 100644 index 945efb3bfe80018897963cbfa91e0dc400f836d3..0000000000000000000000000000000000000000 --- a/src/test/scala/eu/nomad_lab/parsers/VaspParserSpec.scala +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe - * - * 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. - */ - -package eu.nomad_lab.parsers - -import eu.nomad_lab.{ parsers, DefaultPythonInterpreter } -import org.scalacheck.Properties -import org.specs2.mutable.Specification -import org.{ json4s => jn } - -object VaspParserSpec extends Specification { - "VaspParserTest" >> { - "test with Al.out" >> { - "test with json-events" >> { - ParserRun.parse(VaspParser, "test/examples/oqmd/relaxation/OUTCAR", "json-events") must_== ParseResult.ParseSuccess - } - "test with json" >> { - ParserRun.parse(VaspParser, "test/examples/oqmd/relaxation/OUTCAR", "json") must_== ParseResult.ParseSuccess - } - } - } -} - -object VaspRunParserSpec extends Specification { - "VaspRunParserTest" >> { - "test with Al.out" >> { - "test with json-events" >> { - ParserRun.parse(VaspRunParser, "test/examples/oqmd/relaxation/vasprun.xml", "json-events") must_== ParseResult.ParseSuccess - } - "test with json" >> { - ParserRun.parse(VaspRunParser, "test/examples/oqmd/relaxation/vasprun.xml", "json") must_== ParseResult.ParseSuccess - } - } - } -} diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/regtests/vasp/dos/vasprun.xml.static b/test/examples/dos/vasprun.xml.static similarity index 100% rename from regtests/vasp/dos/vasprun.xml.static rename to test/examples/dos/vasprun.xml.static diff --git a/regtests/regtests.py b/test/regtests.py similarity index 91% rename from regtests/regtests.py rename to test/regtests.py index 68fcbb679e1e3859c075c7d98e95099c998e1986..5cbe7f21e2bf6dfe274919ad0f3354b5c1e6a4e6 100644 --- a/regtests/regtests.py +++ b/test/regtests.py @@ -1,11 +1,11 @@ # Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe -# +# # 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. @@ -15,7 +15,8 @@ import os import unittest import logging -from vaspparser import VASPParser + +from vaspparser import VASPRunParserInterface def get_result(folder, metaname=None): @@ -28,8 +29,8 @@ def get_result(folder, metaname=None): the full dictionary of results. """ dirname = os.path.dirname(__file__) - filename = os.path.join("vasp", dirname, folder, "vasprun.xml.static") - parser = VASPParser(None, debug=True, log_level=logging.CRITICAL) + filename = os.path.join(dirname, folder, "vasprun.xml.static") + parser = VASPRunParserInterface(None, debug=True, log_level=logging.CRITICAL) results = parser.parse(filename) if metaname is None: @@ -43,7 +44,7 @@ class TestDOS(unittest.TestCase): """ @classmethod def setUpClass(cls): - cls.results = get_result("dos") + cls.results = get_result(os.path.join("examples", "dos")) def test_program_name(self): result = self.results["program_name"] diff --git a/parser/parser-vasp/vaspparser/parser.py b/vaspparser/__init__.py similarity index 55% rename from parser/parser-vasp/vaspparser/parser.py rename to vaspparser/__init__.py index 87bdf7a4d96c19e663c65814a855fe1d04211b0c..897c695426541f12e7c7dcf1c7f9ebb60c355b99 100644 --- a/parser/parser-vasp/vaspparser/parser.py +++ b/vaspparser/__init__.py @@ -14,25 +14,50 @@ import os import logging + from nomadcore.baseclasses import ParserInterface + from vaspparser.parser_vasprun import parserInfo -from vaspparser.vaspmainparser import VASPMainParser -logger = logging.getLogger("nomad") +from vaspparser.parser_vasprun import VasprunContext, XmlParser, parserInfo + +logger = logging.getLogger(__name__) -class VASPParser(ParserInterface): - """This class handles the initial setup before any parsing can happen. It +class VASPRunParser: + """ + The main parser class that is called for all run types. Parses the VASP + .out output files. + """ + def __init__(self, parser_context): + self.parser_context = parser_context + + def parse(self, filepath): + superContext = VasprunContext(logger=logger) + parser = XmlParser(parserInfo, superContext) + backend = self.parser_context.super_backend + parser.parse(os.path.abspath(filepath), open(filepath), backend) + + +class VASPRunParserInterface(ParserInterface): + """ + This class handles the initial setup before any parsing can happen. It determines which version of BigDFT was used to generate the output and then sets up a correct main parser. After the implementation has been setup, you can parse the files with parse(). """ - def __init__(self, metainfo_to_keep=None, backend=None, default_units=None, metainfo_units=None, debug=True, log_level=logging.ERROR, store=True): - super(VASPParser, self).__init__(metainfo_to_keep, backend, default_units, metainfo_units, debug, log_level, store) + def __init__( + self, + metainfo_to_keep=None, backend=None, default_units=None, + metainfo_units=None, debug=True, log_level=logging.ERROR, store=True): + + super(VASPRunParserInterface, self).__init__( + metainfo_to_keep, backend, default_units, metainfo_units, debug, log_level, store) def setup_version(self): - """Setups the version by looking at the output file and the version + """ + Setups the version by looking at the output file and the version specified in it. """ # Setup the root folder to the fileservice that is used to access files @@ -40,7 +65,7 @@ class VASPParser(ParserInterface): dirpath = os.path.abspath(dirpath) self.parser_context.file_service.setup_root_folder(dirpath) self.parser_context.file_service.set_file_id(filename, "output") - self.main_parser = VASPMainParser(self.parser_context) + self.main_parser = VASPRunParser(self.parser_context) def get_metainfo_filename(self): return "vasp.nomadmetainfo.json" diff --git a/parser/parser-vasp/vaspparser/parser_vasprun.py b/vaspparser/parser_vasprun.py similarity index 68% rename from parser/parser-vasp/vaspparser/parser_vasprun.py rename to vaspparser/parser_vasprun.py index b3df9e77f48cd59651db2484a2b09ea6fca8ce15..ca2bff2f01a584b94e9a7e77c960643b1aafcf76 100644 --- a/parser/parser-vasp/vaspparser/parser_vasprun.py +++ b/vaspparser/parser_vasprun.py @@ -12,28 +12,32 @@ # See the License for the specific language governing permissions and # limitations under the License. -# coding=utf-8 from __future__ import division -from builtins import map from builtins import range from builtins import object import xml.etree.ElementTree -import logging, sys, bisect -import vaspparser.setup_paths +import sys +import bisect from datetime import datetime -import os, logging, re, traceback -from nomadcore.parser_backend import JsonParseEventsWriterBackend -from nomadcore.local_meta_info import loadJsonFile, InfoKindEl +import os +import re +import traceback import numpy as np -from nomadcore.unit_conversion.unit_conversion import convert_unit_function -from nomadcore.unit_conversion.unit_conversion import convert_unit import ase.geometry import ase.data from math import pi +import xml.etree.ElementTree as ET +import logging + +from nomadcore.parser_backend import JsonParseEventsWriterBackend +from nomadcore.local_meta_info import loadJsonFile, InfoKindEl +from nomadcore.unit_conversion.unit_conversion import convert_unit_function +from nomadcore.unit_conversion.unit_conversion import convert_unit eV2J = convert_unit_function("eV", "J") eV2JV = np.vectorize(eV2J) + def crystal_structure_from_cell(cell, eps=1e-4): """Return the crystal structure as a string calculated from the cell. """ @@ -60,7 +64,7 @@ def crystal_structure_from_cell(cell, eps=1e-4): abs(angles[1:] - pi / 2).max() < eps): return 'monoclinic' else: - raise ValueError('Cannot find crystal structure') + raise ValueError('Cannot find crystal structure') special_points = { @@ -177,19 +181,26 @@ def get_special_points(cell, eps=1e-4): 'Y1': [0, 0, -1 / 2], 'Z': [1 / 2, 0, 0]} + def findLabel(labels, value): for k, v in labels.items(): if np.all(np.abs(v-value) < 1.e-5): return k return "?" + def secondsFromEpoch(date): - epoch = datetime(1970,1,1) - ts=date-epoch + epoch = datetime(1970, 1, 1) + ts = date-epoch return ts.seconds + ts.microseconds/1000.0 -trueRe = re.compile(r"\s*(?:\.?[Tt](?:[Rr][Uu][Ee])?\.?|1|[Yy](?:[Ee][Ss])?|[Jj][Aa]?)\s*$") -falseRe = re.compile(r"\s*(?:\.?[fF](?:[Aa][Ll][Ss][Ee])?\.?|0|[Nn](?:[Oo]|[Ee][Ii][Nn])?)\s*$") + +trueRe = re.compile( + r"\s*(?:\.?[Tt](?:[Rr][Uu][Ee])?\.?|1|[Yy](?:[Ee][Ss])?|[Jj][Aa]?)\s*$") +falseRe = re.compile( + r"\s*(?:\.?[fF](?:[Aa][Ll][Ss][Ee])?\.?|0|[Nn](?:[Oo]|[Ee][Ii][Nn])?)\s*$") + + def toBool(value): if falseRe.match(value): return False @@ -199,6 +210,7 @@ def toBool(value): backend.pwarn("Unexpected value for boolean field: %s" % (value)) return None + metaTypeTransformers = { 'C': lambda x: x.strip(), 'i': lambda x: int(x.strip()), @@ -207,13 +219,11 @@ metaTypeTransformers = { } -import xml.etree.ElementTree as ET - class MyXMLParser(ET.XMLParser): rx = re.compile("&#([0-9]+);|&#x([0-9a-fA-F]+);") - def feed(self,data): + def feed(self, data): m = self.rx.search(data) if m is not None: target = m.group(1) @@ -228,12 +238,17 @@ class MyXMLParser(ET.XMLParser): mydata = data[:mstart] + data[mend:] else: mydata = data - super(MyXMLParser,self).feed(mydata) + super(MyXMLParser, self).feed(mydata) + + def transform2(y): - if '**' in y: return float('nan') - else: return y + if '**' in y: + return float('nan') + else: + return y + -def getVector(el, transform = float, field = "v"): +def getVector(el, transform=float, field="v"): """ returns the vasp style vector contained in the element el (using field v). single elements are converted using the function convert""" # @@ -241,8 +256,10 @@ def getVector(el, transform = float, field = "v"): # for y in re.split(r"\s+", x.text.strip()): return [[transform(transform2(y)) for y in re.split(r"\s+", x.text.strip())] for x in el.findall(field)] + class VasprunContext(object): - def __init__(self): + def __init__(self, logger=None): + self.logger = logger self.parser = None self.bands = None self.kpoints = None @@ -258,6 +275,9 @@ class VasprunContext(object): self.cell = None self.angstrom_cell = None + if self.logger is None: + logger = logging.getLogger(__name__) + sectionMap = { "modeling": ["section_run", "section_method"], "structure": ["section_system"], @@ -286,12 +306,15 @@ class VasprunContext(object): if date: pdate = datetime.strptime(date.strip(), "%Y %m %d") if pdate and time: - pdate = datetime.combine(pdate.date(), datetime.strptime(time.strip(), "%H:%M:%S").timetz()) + pdate = datetime.combine(pdate.date(), datetime.strptime( + time.strip(), "%H:%M:%S").timetz()) if pdate: - backend.addValue("program_compilation_datetime", secondsFromEpoch(pdate)) + backend.addValue("program_compilation_datetime", + secondsFromEpoch(pdate)) for i in element: if i.tag != "i" or not i.attrib.get("name") in set(["program", "version", "subversion", "platform", "program_version", "date", "time"]): - backend.pwarn("unexpected tag %s %s %r in generator" % (i.tag, i.attrib, i.text)) + backend.pwarn("unexpected tag %s %s %r in generator" % + (i.tag, i.attrib, i.text)) def onEnd_incar(self, parser, event, element, pathStr): backend = parser.backend @@ -301,32 +324,38 @@ class VasprunContext(object): nsw = 0 for el in element: if (el.tag != "i"): - backend.pwarn("unexpected tag %s %s %r in incar" % (el.tag, el.attrib, el.text)) + backend.pwarn("unexpected tag %s %s %r in incar" % + (el.tag, el.attrib, el.text)) else: name = el.attrib.get("name", None) meta = metaEnv['x_vasp_incar_' + name] valType = el.attrib.get("type") if not meta: - backend.pwarn("Unknown INCAR parameter (not registered in the meta data): %s %s %r" % (el.tag, el.attrib, el.text)) + backend.pwarn("Unknown INCAR parameter (not registered in the meta data): %s %s %r" % ( + el.tag, el.attrib, el.text)) elif valType: expectedMetaType = { 'string': ['C'], 'int': ['i'], - 'logical': ['b','C'] + 'logical': ['b', 'C'] }.get(valType) if not expectedMetaType: - backend.pwarn("Unknown value type %s encountered in INCAR: %s %s %r" % (valType, el.tag, el.attrib, el.text)) + backend.pwarn("Unknown value type %s encountered in INCAR: %s %s %r" % ( + valType, el.tag, el.attrib, el.text)) elif not meta.get('dtypeStr') in expectedMetaType: - backend.pwarn("type mismatch between meta data %s and INCAR type %s for %s %s %r" % ( meta.get('dtypeStr'), valType, el.tag, el.attrib, el.text)) + backend.pwarn("type mismatch between meta data %s and INCAR type %s for %s %s %r" % ( + meta.get('dtypeStr'), valType, el.tag, el.attrib, el.text)) else: shape = meta.get("shape", None) dtypeStr = meta.get("dtypeStr", None) converter = metaTypeTransformers.get(dtypeStr) if not converter: - backend.pwarn("could not find converter for dtypeStr %s when handling meta info %s" % (dtypeStr, )) + backend.pwarn( + "could not find converter for dtypeStr %s when handling meta info %s" % (dtypeStr, )) elif shape: vals = re.split("\s+", el.text.strip()) - backend.addValue(meta["name"], [converter(x) for x in vals]) + backend.addValue( + meta["name"], [converter(x) for x in vals]) else: backend.addValue(meta["name"], converter(el.text)) if name == 'GGA': @@ -338,12 +367,15 @@ class VasprunContext(object): } functs = fMap.get(el.text.strip(), None) if not functs: - backend.pwarn("Unknown XC functional %s" % el.text.strip()) + backend.pwarn("Unknown XC functional %s" % + el.text.strip()) else: for f in functs: - backend.openNonOverlappingSection("section_XC_functionals") + backend.openNonOverlappingSection( + "section_XC_functionals") backend.addValue("XC_functional_name", f) - backend.closeNonOverlappingSection("section_XC_functionals") + backend.closeNonOverlappingSection( + "section_XC_functionals") elif name == "ISPIN": self.ispin = int(el.text.strip()) elif name == "LDAU": @@ -372,10 +404,11 @@ class VasprunContext(object): if el.tag == "generation": param = el.attrib.get("param", None) if param: - backend.addValue("x_vasp_k_points_generation_method", param) + backend.addValue( + "x_vasp_k_points_generation_method", param) if param == "listgenerated": self.bands = { - "divisions": g(el,"i/[@name='divisions']", None), + "divisions": g(el, "i/[@name='divisions']", None), "points": getVector(el) } elif param == "Monkhorst-Pack": @@ -389,7 +422,8 @@ class VasprunContext(object): backend.addArrayValues("k_mesh_points", self.kpoints) elif name == "weights": self.weights = np.asarray(getVector(el)) - backend.addArrayValues("k_mesh_weights", self.weights.flatten()) + backend.addArrayValues( + "k_mesh_weights", self.weights.flatten()) else: backend.pwarn("Unknown array %s in kpoints" % name) else: @@ -407,28 +441,37 @@ class VasprunContext(object): name = cellEl.attrib.get("name", None) if name == "basis": conv = convert_unit_function("angstrom", "m") - self.cell = getVector(cellEl, lambda x: conv(float(x))) + self.cell = getVector( + cellEl, lambda x: conv(float(x))) self.angstrom_cell = np.array(getVector(cellEl)) - backend.addArrayValues("simulation_cell", np.asarray(self.cell)) - backend.addArrayValues("configuration_periodic_dimensions", np.ones(3, dtype=bool)) + backend.addArrayValues( + "simulation_cell", np.asarray(self.cell)) + backend.addArrayValues( + "configuration_periodic_dimensions", np.ones(3, dtype=bool)) elif name == "rec_basis": pass else: - backend.pwarn("Unexpected varray %s in crystal" % name) + backend.pwarn( + "Unexpected varray %s in crystal" % name) elif cellEl.tag == "i": if cellEl.attrib.get("name") != "volume": - backend.pwarn("Unexpected i value %s in crystal" % cellEl.attrib) + backend.pwarn( + "Unexpected i value %s in crystal" % cellEl.attrib) else: - backend.pwarn("Unexpected tag %s %s %r in crystal" % (cellEl.tag, cellEl.attrib, cellEl.text)) + backend.pwarn("Unexpected tag %s %s %r in crystal" % ( + cellEl.tag, cellEl.attrib, cellEl.text)) elif el.tag == "varray": name = el.attrib.get("name", None) if name == "positions": pos = getVector(el) - backend.addArrayValues("atom_positions", np.dot(np.asarray(pos), self.cell)) + backend.addArrayValues( + "atom_positions", np.dot(np.asarray(pos), self.cell)) else: - backend.pwarn("Unexpected varray in structure %s" % el.attrib) + backend.pwarn( + "Unexpected varray in structure %s" % el.attrib) else: - backend.pwarn("Unexpected tag in structure %s %s %r" % el.tag, el.attrib, el.text) + backend.pwarn("Unexpected tag in structure %s %s %r" % + el.tag, el.attrib, el.text) if self.labels is not None: backend.addArrayValues("atom_labels", self.labels) @@ -454,18 +497,24 @@ class VasprunContext(object): for kEl in spinEl: if kEl.tag == "set": ik += 1 - bands = np.asarray(getVector(kEl, field = "r")) + bands = np.asarray( + getVector(kEl, field="r")) if eigenvalues is None: - eigenvalues = np.zeros((self.ispin, self.kpoints.shape[0], bands.shape[0]), dtype = float) - occupation = np.zeros((self.ispin, self.kpoints.shape[0], bands.shape[0]), dtype = float) - eigenvalues[isp, ik] = bands[:,0] - occupation[isp, ik] = bands[:,1] + eigenvalues = np.zeros( + (self.ispin, self.kpoints.shape[0], bands.shape[0]), dtype=float) + occupation = np.zeros( + (self.ispin, self.kpoints.shape[0], bands.shape[0]), dtype=float) + eigenvalues[isp, ik] = bands[:, 0] + occupation[isp, ik] = bands[:, 1] else: - backend.pwarn("unexpected tag %s in k array of the eigenvalues" % kEl.tag) + backend.pwarn( + "unexpected tag %s in k array of the eigenvalues" % kEl.tag) else: - backend.pwarn("unexpected tag %s in spin array of the eigenvalues" % spinEl.tag) + backend.pwarn( + "unexpected tag %s in spin array of the eigenvalues" % spinEl.tag) else: - backend.pwarn("unexpected tag %s in array of the eigenvalues" % arrEl.tag) + backend.pwarn( + "unexpected tag %s in array of the eigenvalues" % arrEl.tag) if eigenvalues is not None: ev = eV2JV(eigenvalues) @@ -475,8 +524,9 @@ class VasprunContext(object): vbTopE.append(float('-inf')) ebMinE.append(float('inf')) for ik in range(occupation.shape[1]): - ebIndex = bisect.bisect_right(-occupation[ispin, ik, :], -0.5) - 1 - vbTopIndex = ebIndex -1 + ebIndex = bisect.bisect_right( + -occupation[ispin, ik, :], -0.5) - 1 + vbTopIndex = ebIndex - 1 if vbTopIndex >= 0: vbTopK = ev[ispin, ik, vbTopIndex] if vbTopK > vbTopE[ispin]: @@ -487,47 +537,70 @@ class VasprunContext(object): ebMinE[ispin] = ebMinK self.vbTopE = vbTopE self.ebMinE = ebMinE - backend.addArrayValues("energy_reference_highest_occupied", np.array(vbTopE)) - backend.addArrayValues("energy_reference_lowest_unoccupied", np.array(ebMinE)) + backend.addArrayValues( + "energy_reference_highest_occupied", np.array(vbTopE)) + backend.addArrayValues( + "energy_reference_lowest_unoccupied", np.array(ebMinE)) if self.bands: divisions = int(self.bands['divisions']) backend.openNonOverlappingSection("section_k_band") nsegments = self.kpoints.shape[0] // divisions - kpt = np.reshape(self.kpoints, (nsegments, divisions, 3)) - energies = np.reshape(ev, (self.ispin, nsegments, divisions , bands.shape[0])) - occ = np.reshape(occupation, (self.ispin, nsegments, divisions, bands.shape[0])) + kpt = np.reshape( + self.kpoints, (nsegments, divisions, 3)) + energies = np.reshape( + ev, (self.ispin, nsegments, divisions, bands.shape[0])) + occ = np.reshape( + occupation, (self.ispin, nsegments, divisions, bands.shape[0])) for isegment in range(nsegments): - backend.openNonOverlappingSection("section_k_band_segment") - backend.addArrayValues("band_energies", energies[:, isegment, :, :]) - backend.addArrayValues("band_occupations", occ[:, isegment, :, :]) - backend.addArrayValues("band_k_points", kpt[isegment]) + backend.openNonOverlappingSection( + "section_k_band_segment") + backend.addArrayValues( + "band_energies", energies[:, isegment, :, :]) + backend.addArrayValues( + "band_occupations", occ[:, isegment, :, :]) + backend.addArrayValues( + "band_k_points", kpt[isegment]) # "band_segm_labels" - backend.addArrayValues("band_segm_start_end", np.asarray([kpt[isegment, 0], kpt[isegment, divisions - 1]])) - backend.closeNonOverlappingSection("section_k_band_segment") + backend.addArrayValues("band_segm_start_end", np.asarray( + [kpt[isegment, 0], kpt[isegment, divisions - 1]])) + backend.closeNonOverlappingSection( + "section_k_band_segment") backend.closeNonOverlappingSection("section_k_band") - backend.openNonOverlappingSection("section_k_band_normalized") + backend.openNonOverlappingSection( + "section_k_band_normalized") specialPoints = {} try: - specialPoints = get_special_points(convert_unit_function("m","angstrom")(self.cell)) - except: - logging.exception("failed to get special points") + specialPoints = get_special_points( + convert_unit_function("m", "angstrom")(self.cell)) + except Exception as e: + self.logger.error("failed to get special points", exc_info=e) for isegment in range(nsegments): - backend.openNonOverlappingSection("section_k_band_segment_normalized") - backend.addArrayValues("band_energies_normalized", energies[:, isegment, :, :] - max(self.vbTopE)) - backend.addArrayValues("band_occupations_normalized", occ[:, isegment, :, :]) - backend.addArrayValues("band_k_points_normalized", kpt[isegment]) - backend.addArrayValues("band_segm_start_end_normalized", np.asarray([kpt[isegment, 0], kpt[isegment, divisions - 1]])) + backend.openNonOverlappingSection( + "section_k_band_segment_normalized") + backend.addArrayValues( + "band_energies_normalized", energies[:, isegment, :, :] - max(self.vbTopE)) + backend.addArrayValues( + "band_occupations_normalized", occ[:, isegment, :, :]) + backend.addArrayValues( + "band_k_points_normalized", kpt[isegment]) + backend.addArrayValues("band_segm_start_end_normalized", np.asarray( + [kpt[isegment, 0], kpt[isegment, divisions - 1]])) backend.addValue("band_segm_labels_normalized", [findLabel(specialPoints, kpt[isegment, 0]), findLabel(specialPoints, kpt[isegment, divisions - 1])]) - backend.closeNonOverlappingSection("section_k_band_segment_normalized") + backend.closeNonOverlappingSection( + "section_k_band_segment_normalized") - backend.closeNonOverlappingSection("section_k_band_normalized") + backend.closeNonOverlappingSection( + "section_k_band_normalized") else: - backend.openNonOverlappingSection("section_eigenvalues") + backend.openNonOverlappingSection( + "section_eigenvalues") backend.addArrayValues("eigenvalues_values", ev) - backend.addArrayValues("eigenvalues_occupation", occupation) - backend.closeNonOverlappingSection("section_eigenvalues") + backend.addArrayValues( + "eigenvalues_occupation", occupation) + backend.closeNonOverlappingSection( + "section_eigenvalues") else: backend.pwarn("unexpected tag %s in the eigenvalues" % el.tag) @@ -536,10 +609,12 @@ class VasprunContext(object): def onStart_calculation(self, parser, event, element, pathStr): gIndexes = parser.tagSections[pathStr] - self.singleConfCalcs.append(gIndexes["section_single_configuration_calculation"]) + self.singleConfCalcs.append( + gIndexes["section_single_configuration_calculation"]) if self.waveCut: backend.openNonOverlappingSection("section_basis_set") - backend.addValue("mapping_section_basis_set_cell_dependent", self.waveCut) + backend.addValue( + "mapping_section_basis_set_cell_dependent", self.waveCut) backend.closeNonOverlappingSection("section_basis_set") def onEnd_modeling(self, parser, event, element, pathStr): @@ -555,7 +630,8 @@ class VasprunContext(object): backend.closeSection("section_sampling_method", samplingGIndex) frameSequenceGIndex = backend.openSection("section_frame_sequence") backend.addValue("frame_sequence_to_sampling_ref", samplingGIndex) - backend.addArrayValues("frame_sequence_local_frames_ref", np.asarray(self.singleConfCalcs)) + backend.addArrayValues( + "frame_sequence_local_frames_ref", np.asarray(self.singleConfCalcs)) backend.closeSection("section_frame_sequence", frameSequenceGIndex) def onEnd_calculation(self, parser, event, element, pathStr): @@ -563,9 +639,11 @@ class VasprunContext(object): fConv = convert_unit_function("eV/angstrom", "N") pConv = convert_unit_function("eV/angstrom^3", "Pa") backend = parser.backend - backend.addValue("single_configuration_calculation_to_system_ref", self.lastSystemDescription) + backend.addValue( + "single_configuration_calculation_to_system_ref", self.lastSystemDescription) gIndexes = parser.tagSections["/modeling"] - backend.addValue("single_configuration_to_calculation_method_ref", gIndexes["section_method"]) + backend.addValue( + "single_configuration_to_calculation_method_ref", gIndexes["section_method"]) for el in element: if el.tag == "energy": for enEl in el: @@ -581,7 +659,8 @@ class VasprunContext(object): value = eConv(float(enEl.text.strip())) backend.addValue("energy_total_T0", value) else: - backend.pwarn("Unexpected i tag with name %s in energy section" % name) + backend.pwarn( + "Unexpected i tag with name %s in energy section" % name) elif enEl.tag == "varray": name = enEl.attrib.get("name", None) if name == "forces": @@ -615,13 +694,15 @@ class VasprunContext(object): elif atomsEl.tag == "set": for atomsLine in atomsEl: if atomsLine.tag != "rc": - backend.pwarn("unexpected tag %s in atoms array in atominfo" % atomsLine.tag) + backend.pwarn( + "unexpected tag %s in atoms array in atominfo" % atomsLine.tag) else: line = atomsLine.findall("c") labels.append(line[0].text.strip()) atomTypes.append(int(line[1].text.strip())) else: - backend.pwarn("unexpected tag %s in atoms array in atominfo" % atomsEl.tag) + backend.pwarn( + "unexpected tag %s in atoms array in atominfo" % atomsEl.tag) elif name == "atomtypes": keys = [] fieldTypes = [] @@ -630,14 +711,18 @@ class VasprunContext(object): pass elif atomsEl.tag == "field": keys.append(atomsEl.text.strip()) - fieldTypes.append(atomsEl.attrib.get("type", "float")) + fieldTypes.append( + atomsEl.attrib.get("type", "float")) elif atomsEl.tag == "set": - expectedKeys = ["atomspertype", "element", "mass", "valence", "pseudopotential"] + expectedKeys = ["atomspertype", "element", + "mass", "valence", "pseudopotential"] if keys != expectedKeys: - backend.pwarn("unexpected fields in atomtype: %s vs %s" % (keys, expectedKeys)) + backend.pwarn( + "unexpected fields in atomtype: %s vs %s" % (keys, expectedKeys)) for atomsLine in atomsEl: if atomsLine.tag != "rc": - backend.pwarn("unexpected tag %s in atoms array in atominfo" % atomsLine.tag) + backend.pwarn( + "unexpected tag %s in atoms array in atominfo" % atomsLine.tag) else: line = atomsLine.findall("c") typeDesc = {} @@ -653,36 +738,48 @@ class VasprunContext(object): typeDesc[k] = value atomTypesDesc.append(typeDesc) else: - backend.pwarn("unexpected tag %s in atomtypes array in atominfo" % atomsEl.tag) + backend.pwarn( + "unexpected tag %s in atomtypes array in atominfo" % atomsEl.tag) kindIds = [] - nEl={} + nEl = {} kindLabels = [] for atomDesc in atomTypesDesc: - kindId = backend.openSection("section_method_atom_kind") + kindId = backend.openSection( + "section_method_atom_kind") if 'element' in atomDesc: elName = atomDesc['element'].strip() try: elNr = ase.data.chemical_symbols.index(elName) - backend.addValue("method_atom_kind_atom_number", elNr) - except: - logging.exception("error finding element number for %r",atomDesc['element'].strip()) - nElNow = 1 + nEl.get(elName,0) + backend.addValue( + "method_atom_kind_atom_number", elNr) + except Exception as e: + self.logger.error( + "error finding element number for %r" % atomDesc['element'].strip(), + exc_info=e) + nElNow = 1 + nEl.get(elName, 0) nEl[elName] = nElNow - elLabel = elName + (str(nElNow) if nElNow > 1 else "") + elLabel = elName + \ + (str(nElNow) if nElNow > 1 else "") kindLabels.append(elLabel) backend.addValue("method_atom_kind_label", elLabel) if "mass" in atomDesc: - backend.addValue("method_atom_kind_mass", atomDesc["mass"]) + backend.addValue( + "method_atom_kind_mass", atomDesc["mass"]) if "valence" in atomDesc: - backend.addValue("method_atom_kind_explicit_electrons", atomDesc["valence"]) + backend.addValue( + "method_atom_kind_explicit_electrons", atomDesc["valence"]) if "pseudopotential" in atomDesc: - backend.addValue("method_atom_kind_pseudopotential_name", atomDesc["pseudopotential"]) + backend.addValue( + "method_atom_kind_pseudopotential_name", atomDesc["pseudopotential"]) kindIds.append(kindId) - backend.closeSection("section_method_atom_kind", kindId) - backend.addArrayValues("x_vasp_atom_kind_refs", np.asarray([kindIds[i-1] for i in atomTypes])) + backend.closeSection( + "section_method_atom_kind", kindId) + backend.addArrayValues("x_vasp_atom_kind_refs", np.asarray( + [kindIds[i-1] for i in atomTypes])) labels2 = [kindLabels[i-1] for i in atomTypes] else: - backend.pwarn("unexpected array named %s in atominfo" % name) + backend.pwarn( + "unexpected array named %s in atominfo" % name) else: backend.pwarn("unexpected tag %s in atominfo" % el.tag) self.labels = np.asarray(labels2) if labels2 else np.asarray(labels) @@ -691,64 +788,75 @@ class VasprunContext(object): backend = self.parser.backend metaEnv = self.parser.backend.metaInfoEnv() if (el.tag != "i"): - backend.pwarn("unexpected tag %s %s %r in incar" % (el.tag, el.attrib, el.text)) + backend.pwarn("unexpected tag %s %s %r in incar" % + (el.tag, el.attrib, el.text)) else: name = el.attrib.get("name", None) meta = metaEnv['x_vasp_incarOut_' + name] valType = el.attrib.get("type") if not meta: - backend.pwarn("Unknown INCAR out parameter (not registered in the meta data): %s %s %r" % (el.tag, el.attrib, el.text)) + backend.pwarn("Unknown INCAR out parameter (not registered in the meta data): %s %s %r" % ( + el.tag, el.attrib, el.text)) else: if valType: expectedMetaType = { 'string': ['C'], 'int': ['i'], - 'logical': ['b','C'] + 'logical': ['b', 'C'] }.get(valType) if not expectedMetaType: - backend.pwarn("Unknown value type %s encountered in INCAR out: %s %s %r" % (valType, el.tag, el.attrib, el.text)) + backend.pwarn("Unknown value type %s encountered in INCAR out: %s %s %r" % ( + valType, el.tag, el.attrib, el.text)) elif not meta.get('dtypeStr') in expectedMetaType: - backend.pwarn("type mismatch between meta data %s and INCAR type %s for %s %s %r" % ( meta.get('dtypeStr'), valType, el.tag, el.attrib, el.text)) + backend.pwarn("type mismatch between meta data %s and INCAR type %s for %s %s %r" % ( + meta.get('dtypeStr'), valType, el.tag, el.attrib, el.text)) try: shape = meta.get("shape", None) dtypeStr = meta.get("dtypeStr", None) converter = metaTypeTransformers.get(dtypeStr) if not converter: - backend.pwarn("could not find converter for dtypeStr %s when handling meta info %s" % (dtypeStr, )) + backend.pwarn( + "could not find converter for dtypeStr %s when handling meta info %s" % (dtypeStr, )) elif shape: vals = re.split("\s+", el.text.strip()) - backend.addValue(meta["name"], [converter(x) for x in vals]) + backend.addValue( + meta["name"], [converter(x) for x in vals]) else: backend.addValue(meta["name"], converter(el.text)) except: - backend.pwarn("Exception trying to handle incarOut %s: %s" % (name, traceback.format_exc())) + backend.pwarn("Exception trying to handle incarOut %s: %s" % ( + name, traceback.format_exc())) if name == 'ENMAX' or name == 'PREC': - if name =='ENMAX': self.enmax=converter(el.text) - if name =='PREC' : - if 'acc' in converter(el.text): - self.prec=1.3 - else: - self.prec=1.0 + if name == 'ENMAX': + self.enmax = converter(el.text) + if name == 'PREC': + if 'acc' in converter(el.text): + self.prec = 1.3 + else: + self.prec = 1.0 if name == 'GGA': fMap = { '91': ['GGA_X_PW91', 'GGA_C_PW91'], 'PE': ['GGA_X_PBE', 'GGA_C_PBE'], 'RP': ['GGA_X_RPBE', 'GGA_C_PBE'], 'PS': ['GGA_C_PBE_SOL', 'GGA_X_PBE_SOL'], - '--': ['GGA_X_PBE', 'GGA_C_PBE'] # should check potcar + '--': ['GGA_X_PBE', 'GGA_C_PBE'] # should check potcar } functs = fMap.get(el.text.strip(), None) if not functs: - backend.pwarn("Unknown XC functional %s" % el.text.strip()) + backend.pwarn("Unknown XC functional %s" % + el.text.strip()) else: for f in functs: - backend.openNonOverlappingSection("section_XC_functionals") + backend.openNonOverlappingSection( + "section_XC_functionals") backend.addValue("XC_functional_name", f) - backend.closeNonOverlappingSection("section_XC_functionals") + backend.closeNonOverlappingSection( + "section_XC_functionals") elif name == "ISPIN": self.ispin = int(el.text.strip()) - def separatorScan(self, element, backend, depth = 0): + def separatorScan(self, element, backend, depth=0): for separators in element: if separators.tag == "separator": separatorName = separators.attrib.get("name") @@ -758,29 +866,37 @@ class VasprunContext(object): elif el.tag == "separator": self.separatorScan(el, backend, depth + 1) else: - backend.pwarn("unexpected tag %s %s in parameters separator %s at depth %d" % (el.tag, el.attrib, separatorName, depth)) + backend.pwarn("unexpected tag %s %s in parameters separator %s at depth %d" % ( + el.tag, el.attrib, separatorName, depth)) elif separators.tag == "i": self.incarOutTag(separators) else: - backend.pwarn("unexpected tag %s %s in parameters at depth %d" % (separators.tag, separators.attrib, depth)) + backend.pwarn("unexpected tag %s %s in parameters at depth %d" % ( + separators.tag, separators.attrib, depth)) def onEnd_parameters(self, parser, event, element, pathStr): self.separatorScan(element, parser.backend) backend = parser.backend try: - self.prec - try: - self.enmax - self.waveCut = backend.openNonOverlappingSection("section_basis_set_cell_dependent") - backend.addValue("basis_set_planewave_cutoff", eV2J(self.enmax*self.prec)) - backend.closeNonOverlappingSection("section_basis_set_cell_dependent") - backend.openNonOverlappingSection("section_method_basis_set") - backend.addValue("mapping_section_method_basis_set_cell_associated", self.waveCut) - backend.closeNonOverlappingSection("section_method_basis_set") - except AttributeError: - backend.pwarn("Missing ENMAX for calculating plane wave basis cut off ") + self.prec + try: + self.enmax + self.waveCut = backend.openNonOverlappingSection( + "section_basis_set_cell_dependent") + backend.addValue("basis_set_planewave_cutoff", + eV2J(self.enmax*self.prec)) + backend.closeNonOverlappingSection( + "section_basis_set_cell_dependent") + backend.openNonOverlappingSection("section_method_basis_set") + backend.addValue( + "mapping_section_method_basis_set_cell_associated", self.waveCut) + backend.closeNonOverlappingSection("section_method_basis_set") + except AttributeError: + backend.pwarn( + "Missing ENMAX for calculating plane wave basis cut off ") except AttributeError: - backend.pwarn("Missing PREC for calculating plane wave basis cut off ") + backend.pwarn( + "Missing PREC for calculating plane wave basis cut off ") def onEnd_dos(self, parser, event, element, pathStr): "density of states" @@ -791,9 +907,11 @@ class VasprunContext(object): if el.attrib.get("name") == "efermi": self.eFermi = eV2J(float(el.text.strip())) backend.addValue("dos_fermi_energy", self.eFermi) - backend.addArrayValues("energy_reference_fermi", np.array([self.eFermi]*self.ispin)) + backend.addArrayValues( + "energy_reference_fermi", np.array([self.eFermi]*self.ispin)) else: - backend.pwarn("unexpected tag %s %s in dos" % (el.tag, el.attrib)) + backend.pwarn("unexpected tag %s %s in dos" % + (el.tag, el.attrib)) elif el.tag == "total": for el1 in el: if el1.tag == "array": @@ -804,12 +922,15 @@ class VasprunContext(object): dosL = [] for spinComponent in el2: if spinComponent.tag == "set": - dosL.append(getVector(spinComponent, field = "r")) + dosL.append( + getVector(spinComponent, field="r")) else: - backend.pwarn("unexpected tag %s %s in dos total array set" % (spinComponent.tag, spinComponent.attrib)) + backend.pwarn("unexpected tag %s %s in dos total array set" % ( + spinComponent.tag, spinComponent.attrib)) dosA = np.asarray(dosL) if len(dosA.shape) != 3: - raise Exception("unexpected shape %s (%s) for total dos (ragged arrays?)" % (dosA.shape), dosA.dtype) + raise Exception("unexpected shape %s (%s) for total dos (ragged arrays?)" % ( + dosA.shape), dosA.dtype) dosE = eV2JV(dosA[0, :, 0]) dosI = dosA[:, :, 2] dosV = dosA[:, :, 1] @@ -829,17 +950,21 @@ class VasprunContext(object): else: eRef = self.eFermi backend.addArrayValues("dos_energies", dosE) - backend.addArrayValues("dos_energies_normalized", dosE - eRef) + backend.addArrayValues( + "dos_energies_normalized", dosE - eRef) backend.addArrayValues("dos_values", dosV) - backend.addArrayValues("dos_integrated_values", dosI) + backend.addArrayValues( + "dos_integrated_values", dosI) else: - backend.pwarn("unexpected tag %s %s in dos total array" % (el2.tag, el2.attrib)) + backend.pwarn("unexpected tag %s %s in dos total array" % ( + el2.tag, el2.attrib)) else: - backend.pwarn("unexpected tag %s %s in dos total" % (el2.tag, el2.attrib)) + backend.pwarn("unexpected tag %s %s in dos total" % ( + el2.tag, el2.attrib)) elif el.tag == "partial": for el1 in el: if el1.tag == "array": - lm=[] + lm = [] for el2 in el1: if el2.tag == "dimension": pass @@ -848,20 +973,21 @@ class VasprunContext(object): pass else: strLm = { - "s": [0,0], - "p": [1,-1], - "px":[1,0], - "py":[1,1], - "pz":[1,2], - "d": [2,-1], - "dx2":[2,0], - "dxy":[2,1], - "dxz":[2,2], - "dy2":[2,3], - "dyz":[2,4], - "dz2":[2,5] + "s": [0, 0], + "p": [1, -1], + "px": [1, 0], + "py": [1, 1], + "pz": [1, 2], + "d": [2, -1], + "dx2": [2, 0], + "dxy": [2, 1], + "dxz": [2, 2], + "dy2": [2, 3], + "dyz": [2, 4], + "dz2": [2, 5] } - lm.append(strLm.get(el2.text.strip(), [-1,-1])) + lm.append( + strLm.get(el2.text.strip(), [-1, -1])) elif el2.tag == "set": dosL = [] for atom in el2: @@ -870,28 +996,37 @@ class VasprunContext(object): dosL.append(atomL) for spinComponent in atom: if spinComponent.tag == "set": - atomL.append(getVector(spinComponent, field = "r")) + atomL.append( + getVector(spinComponent, field="r")) else: - backend.pwarn("unexpected tag %s %s in dos partial array set set" % (spinComponent.tag, spinComponent.attrib)) + backend.pwarn("unexpected tag %s %s in dos partial array set set" % ( + spinComponent.tag, spinComponent.attrib)) else: - backend.pwarn("unexpected tag %s %s in dos partial array set" % (spinComponent.tag, spinComponent.attrib)) + backend.pwarn("unexpected tag %s %s in dos partial array set" % ( + spinComponent.tag, spinComponent.attrib)) dosLM = np.asarray(dosL) - assert len(dosLM.shape) == 4, "invalid shape dimension in projected dos (ragged arrays?)" - backend.addArrayValues("dos_values_lm", dosLM[:,:,:,1:]) + assert len( + dosLM.shape) == 4, "invalid shape dimension in projected dos (ragged arrays?)" + backend.addArrayValues( + "dos_values_lm", dosLM[:, :, :, 1:]) else: - backend.pwarn("unexpected tag %s %s in dos total array" % (el2.tag, el2.attrib)) + backend.pwarn("unexpected tag %s %s in dos total array" % ( + el2.tag, el2.attrib)) backend.addArrayValues("dos_lm", np.asarray(lm)) backend.addValue("dos_m_kind", "polynomial") else: - backend.pwarn("unexpected tag %s %s in dos total" % (el2.tag, el2.attrib)) + backend.pwarn("unexpected tag %s %s in dos total" % ( + el2.tag, el2.attrib)) else: - backend.pwarn("unexpected tag %s %s in dos" % (el2.tag, el2.attrib)) + backend.pwarn("unexpected tag %s %s in dos" % + (el2.tag, el2.attrib)) backend.closeNonOverlappingSection("section_dos") def onEnd_projected(self, parser, event, element, pathStr): "projected eigenvalues" return None + class XmlParser(object): @staticmethod def extractCallbacks(obj): @@ -909,38 +1044,41 @@ class XmlParser(object): return triggers @staticmethod - def maybeGet(el, path, default = None): + def maybeGet(el, path, default=None): i = el.findall(path) if i: return i.pop().text else: return default - def __init__(self, parserInfo, superContext, callbacks = None, sectionMap = None): + def __init__(self, parserInfo, superContext, callbacks=None, sectionMap=None): self.fIn = None self.parserInfo = parserInfo self.superContext = superContext - self.callbacks = callbacks if callbacks is not None else XmlParser.extractCallbacks(superContext) + self.callbacks = callbacks if callbacks is not None else XmlParser.extractCallbacks( + superContext) self.sectionMap = sectionMap if sectionMap is not None else superContext.sectionMap self.path = [] self.tagSections = {} def parse(self, mainFileUri, fIn, backend): if self.path: - raise Exception("Parse of %s called with non empty path, parse already in progress?" % mainFileUri) + raise Exception( + "Parse of %s called with non empty path, parse already in progress?" % mainFileUri) self.mainFileUri = mainFileUri self.fIn = fIn self.backend = backend backend.startedParsingSession( - mainFileUri = mainFileUri, - parserInfo = self.parserInfo) + mainFileUri=mainFileUri, + parserInfo=self.parserInfo) self.superContext.startedParsing(self) # there are invalid characters like esc in the files, we do not want to crash on them xmlParser = MyXMLParser() try: - for event, el in xml.etree.ElementTree.iterparse(self.fIn, events=["start","end"], parser = xmlParser): + for event, el in xml.etree.ElementTree.iterparse(self.fIn, events=["start", "end"], parser=xmlParser): if event == 'start': - pathStr = "/".join([x.tag for x in self.path]) + "/" + el.tag + pathStr = "/".join([x.tag for x in self.path] + ) + "/" + el.tag sectionsToOpen = self.sectionMap.get(el.tag, None) if sectionsToOpen: gIndexes = {} @@ -954,7 +1092,8 @@ class XmlParser(object): elif event == 'end': lastEl = self.path.pop() if lastEl != el: - raise Exception("mismatched path at end, got %s expected %s" % (lastEl, el)) + raise Exception( + "mismatched path at end, got %s expected %s" % (lastEl, el)) tag = el.tag pathStr = "/".join([x.tag for x in self.path]) + "/" + tag callback = self.callbacks.get("onEnd_" + tag, None) @@ -979,28 +1118,30 @@ class XmlParser(object): else: raise Exception("Unexpected event %s" % event) except: - logging.exception("failure when parsing %s", self.mainFileUri) backend.finishedParsingSession( - parserStatus = "ParseFailure", - parserErrors = ["exception: %s" % sys.exc_info()[1]] + parserStatus="ParseFailure", + parserErrors=["exception: %s" % sys.exc_info()[1]] ) else: backend.finishedParsingSession( - parserStatus = "ParseSuccess", - parserErrors = None + parserStatus="ParseSuccess", + parserErrors=None ) + g = XmlParser.maybeGet parserInfo = { - "name": "parser_vasprun", - "version": "1.0" + "name": "parser_vasprun", + "version": "1.0" } if __name__ == "__main__": - metaInfoPath = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../../../../../nomad-meta-info/meta_info/nomad_meta_info/vasp.nomadmetainfo.json")) - metaInfoEnv, warnings = loadJsonFile(filePath = metaInfoPath, dependencyLoader = None, extraArgsHandling = InfoKindEl.ADD_EXTRA_ARGS, uri = None) - superContext = VasprunContext() + metaInfoPath = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath( + __file__)), "../../../../../nomad-meta-info/meta_info/nomad_meta_info/vasp.nomadmetainfo.json")) + metaInfoEnv, warnings = loadJsonFile( + filePath=metaInfoPath, dependencyLoader=None, extraArgsHandling=InfoKindEl.ADD_EXTRA_ARGS, uri=None) + superContext = VasprunContext() parser = XmlParser(parserInfo, superContext) backend = JsonParseEventsWriterBackend(metaInfoEnv, sys.stdout) parser.parse(sys.argv[1], open(sys.argv[2]), backend)