Commit 003edfdc authored by Daniel Speckhard's avatar Daniel Speckhard
Browse files

Restructured files to remove redundant folders.

parent 1d8d8e17
# Copyright 2016-2018 Lauri Himanen, Fawzi Mohamed
#
# 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 bigdftparser.parser import BigDFTParser
# Copyright 2016-2018 Lauri Himanen, Fawzi Mohamed
#
# 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.
LIB_XC_MAPPING = {
"001": "LDA_X",
"002": "LDA_C_WIGNER",
"003": "LDA_C_RPA",
"004": "LDA_C_HL",
"005": "LDA_C_GL",
"006": "LDA_C_XALPHA",
"007": "LDA_C_VWN",
"008": "LDA_C_VWN_RPA",
"009": "LDA_C_PZ",
"010": "LDA_C_PZ_MOD",
"011": "LDA_C_OB_PZ",
"012": "LDA_C_PW",
"013": "LDA_C_PW_MOD",
"014": "LDA_C_OB_PW",
"015": "LDA_C_2D_AMGB",
"016": "LDA_C_2D_PRM",
"017": "LDA_C_vBH",
"018": "LDA_C_1D_CSC",
"019": "LDA_X_2D",
"020": "LDA_XC_TETER93",
"021": "LDA_X_1D",
"101": "GGA_X_PBE",
"102": "GGA_X_PBE_R",
"103": "GGA_X_B86",
"104": "GGA_X_B86_R",
"105": "GGA_X_B86_MGC",
"106": "GGA_X_B88",
"107": "GGA_X_G96",
"108": "GGA_X_PW86",
"109": "GGA_X_PW91",
"110": "GGA_X_OPTX",
"111": "GGA_X_DK87_R1",
"112": "GGA_X_DK87_R2",
"113": "GGA_X_LG93",
"114": "GGA_X_FT97_A",
"115": "GGA_X_FT97_B",
"116": "GGA_X_PBE_SOL",
"117": "GGA_X_RPBE",
"118": "GGA_X_WC",
"119": "GGA_X_mPW91",
"120": "GGA_X_AM05",
"121": "GGA_X_PBEA",
"122": "GGA_X_MPBE",
"123": "GGA_X_XPBE",
"124": "GGA_X_2D_B86_MGC",
"125": "GGA_X_BAYESIAN",
"126": "GGA_X_PBE_JSJR",
"127": "GGA_X_2D_B88",
"128": "GGA_X_2D_B86",
"129": "GGA_X_2D_PBE",
"130": "GGA_C_PBE",
"131": "GGA_C_LYP",
"132": "GGA_C_P86",
"133": "GGA_C_PBE_SOL",
"134": "GGA_C_PW91",
"135": "GGA_C_AM05",
"136": "GGA_C_XPBE",
"137": "GGA_C_LM",
"138": "GGA_C_PBE_JRGX",
"139": "GGA_X_OPTB88_VDW",
"140": "GGA_X_PBEK1_VDW",
"141": "GGA_X_OPTPBE_VDW",
"160": "GGA_XC_LB",
"161": "GGA_XC_HCTH_93",
"162": "GGA_XC_HCTH_120",
"163": "GGA_XC_HCTH_147",
"164": "GGA_XC_HCTH_407",
"165": "GGA_XC_EDF1",
"166": "GGA_XC_XLYP",
"167": "GGA_XC_B97",
"168": "GGA_XC_B97_1",
"169": "GGA_XC_B97_2",
"170": "GGA_XC_B97_D",
"171": "GGA_XC_B97_K",
"172": "GGA_XC_B97_3",
"173": "GGA_XC_PBE1W",
"174": "GGA_XC_MPWLYP1W",
"175": "GGA_XC_PBELYP1W",
"176": "GGA_XC_SB98_1a",
"177": "GGA_XC_SB98_1b",
"178": "GGA_XC_SB98_1c",
"179": "GGA_XC_SB98_2a",
"180": "GGA_XC_SB98_2b",
"181": "GGA_XC_SB98_2c",
"401": "HYB_GGA_XC_B3PW91",
"402": "HYB_GGA_XC_B3LYP",
"403": "HYB_GGA_XC_B3P86",
"404": "HYB_GGA_XC_O3LYP",
"405": "HYB_GGA_XC_mPW1K",
"406": "HYB_GGA_XC_PBEH",
"407": "HYB_GGA_XC_B97",
"408": "HYB_GGA_XC_B97_1",
"410": "HYB_GGA_XC_B97_2",
"411": "HYB_GGA_XC_X3LYP",
"412": "HYB_GGA_XC_B1WC",
"413": "HYB_GGA_XC_B97_K",
"414": "HYB_GGA_XC_B97_3",
"415": "HYB_GGA_XC_mPW3PW",
"416": "HYB_GGA_XC_B1LYP",
"417": "HYB_GGA_XC_B1PW91",
"418": "HYB_GGA_XC_mPW1PW",
"419": "HYB_GGA_XC_mPW3LYP",
"420": "HYB_GGA_XC_SB98_1a",
"421": "HYB_GGA_XC_SB98_1b",
"422": "HYB_GGA_XC_SB98_1c",
"423": "HYB_GGA_XC_SB98_2a",
"424": "HYB_GGA_XC_SB98_2b",
"425": "HYB_GGA_XC_SB98_2c",
"201": "MGGA_X_LTA",
"202": "MGGA_X_TPSS",
"203": "MGGA_X_M06L",
"204": "MGGA_X_GVT4",
"205": "MGGA_X_TAU_HCTH",
"206": "MGGA_X_BR89",
"207": "MGGA_X_BJ06",
"208": "MGGA_X_TB09",
"209": "MGGA_X_RPP09",
"231": "MGGA_C_TPSS",
"232": "MGGA_C_VSXC",
"301": "LCA_OMC",
"302": "LCA_LCH",
}
# Copyright 2016-2018 Lauri Himanen, Fawzi Mohamed
#
# 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.
import os
import re
import logging
import importlib
from nomadcore.baseclasses import ParserInterface
class BigDFTParser(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, logger=None, log_level=logging.ERROR,
store=True
):
super(BigDFTParser, self).__init__(
metainfo_to_keep, backend, default_units,
metainfo_units, debug, log_level, store
)
if logger is not None:
self.logger = logger
self.logger.debug('received logger')
else:
self.logger = logging.getLogger(__name__)
def setup_version(self):
"""Setups the version by looking at the output file and the version
specified in it.
"""
# Search for the BigDFT version specification. The correct parser is
# initialized based on this information.
regex_version = re.compile(" Version Number\s+: (\d\.\d)")
version_id = None
with open(self.parser_context.main_file, 'r') as outputfile:
header = outputfile.read(50*80)
for line in header.split("\n"):
# Look for version definition
result_version = regex_version.match(line)
if result_version:
version_id = result_version.group(1).replace('.', '')
if version_id is None:
msg = "Could not find a version specification from the given main file."
self.logger.exception(msg)
raise RuntimeError(msg)
# Setup the root folder to the fileservice that is used to access files
dirpath, filename = os.path.split(self.parser_context.main_file)
dirpath = os.path.abspath(dirpath)
self.parser_context.file_service.setup_root_folder(dirpath)
self.parser_context.file_service.set_file_id(filename, "output")
# Setup the correct main parser based on the version id. If no match
# for the version is found, use the main parser for NWChem 6.6
self.setup_main_parser(version_id)
def get_metainfo_filename(self):
return "big_dft.nomadmetainfo.json"
def get_parser_info(self):
return {'name': 'big-dft-parser', 'version': '1.0'}
def setup_main_parser(self, version_id):
# Currently the version id is a pure integer, so it can directly be mapped
# into a package name.
base = "bigdftparser.versions.bigdft{}.mainparser".format(version_id)
parser_module = None
parser_class = None
try:
parser_module = importlib.import_module(base)
except ImportError:
self.logger.warning("Could not find a parser for version '{}'. Trying to default to the base implementation for BigDFT 1.8.0".format(version_id))
base = "bigdftparser.versions.bigdft18.mainparser"
try:
parser_module = importlib.import_module(base)
except ImportError:
self.logger.exception("Could not find the module '{}'".format(base))
raise
try:
class_name = "BigDFTMainParser"
parser_class = getattr(parser_module, class_name)
except AttributeError:
self.logger.exception("A parser class '{}' could not be found in the module '[]'.".format(class_name, parser_module))
raise
self.main_parser = parser_class(self.parser_context)
# Copyright 2016-2018 Lauri Himanen, Fawzi Mohamed
#
# 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 bigdftparser import BigDFTParser
if __name__ == "__main__":
# Initialise the parser with the main filename and a JSON backend
main_file = sys.argv[1]
parser = BigDFTParser(backend=JsonParseEventsWriterBackend)
parser.parse(main_file)
# Copyright 2016-2018 Lauri Himanen, Fawzi Mohamed
#
# 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, "../../parser-big-dft"))
# 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)
sys.path.insert(1, parserDir)
# Copyright 2016-2018 Lauri Himanen, Fawzi Mohamed
#
# 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.
import logging
import numpy as np
from yaml import Loader, YAMLError
from yaml import ScalarNode, SequenceNode, MappingNode, MappingEndEvent
from nomadcore.baseclasses import AbstractBaseParser
from bigdftparser.generic.libxc_codes import LIB_XC_MAPPING
LOGGER = logging.getLogger("nomad")
class BigDFTMainParser(AbstractBaseParser):
"""The main parser class that is called for all run types. Parses the NWChem
output file.
"""
def __init__(self, parser_context):
"""
"""
super(BigDFTMainParser, self).__init__(parser_context)
# Map keys in the output to funtions that handle the values
self.key_to_funct_map = {
"Version Number": lambda x: self.backend.addValue("program_version", x),
"Atomic structure": self.atomic_structure,
"Sizes of the simulation domain": self.simulation_domain,
"Atomic System Properties": self.atomic_system_properties,
"dft": self.dft,
"DFT parameters": self.dft_parameters,
"Ground State Optimization": self.ground_state_optimization,
"Atomic Forces (Ha/Bohr)": self.atomic_forces,
"Energy (Hartree)": lambda x: self.backend.addRealValue("energy_total", float(x), unit="hartree"),
}
def parse(self, filepath):
"""The output file of a BigDFT run is a YAML document. Here we directly
parse this document with an existing YAML library, and push its
contents into the backend. This function will read the document in
smaller pieces, thus preventing the parser from opening too large files
directly into memory.
"""
self.prepare()
self.print_json_header()
with open(filepath, "r") as fin:
try:
# Open default sections and output default information
section_run_id = self.backend.openSection("section_run")
section_system_id = self.backend.openSection("section_system")
section_method_id = self.backend.openSection("section_method")
section_scc_id = self.backend.openSection("section_single_configuration_calculation")
self.backend.addValue("program_name", "BigDFT")
self.backend.addValue("electronic_structure_method", "DFT")
self.backend.addValue("program_basis_set_type", "real-space grid")
self.backend.addValue("single_configuration_calculation_to_system_ref", section_system_id)
self.backend.addValue("single_configuration_to_calculation_method_ref", section_method_id)
loader = Loader(fin)
generator = self.generate_root_nodes(loader)
# Go through all the keys in the mapping, and call an appropriate
# function on the value.
for key, value in generator:
function = self.key_to_funct_map.get(key)
if function is not None:
function(value)
# Close default sections
self.backend.closeSection("section_single_configuration_calculation", section_scc_id)
self.backend.closeSection("section_method", section_method_id)
self.backend.closeSection("section_system", section_system_id)
self.backend.closeSection("section_run", section_run_id)
except YAMLError:
raise Exception("There was a syntax error in the BigDFT YAML output file.")
self.print_json_footer()
def generate_root_nodes(self, loader):
# Ignore the first two events
loader.get_event() # StreamStarEvetn
loader.get_event() # DocumentStartEvent
start_event = loader.get_event() # MappingStartEvent
tag = start_event.tag
# This is the root mapping that contains everything
node = MappingNode(tag, [],
start_event.start_mark, None,
flow_style=start_event.flow_style)
while not loader.check_event(MappingEndEvent):
key = loader.construct_scalar(loader.compose_node(node, None))
value = loader.compose_node(node, key)
if isinstance(value, MappingNode):
value = loader.construct_mapping(value, deep=True)
elif isinstance(value, SequenceNode):
value = loader.construct_sequence(value, deep=True)
elif isinstance(value, ScalarNode):
value = loader.construct_scalar(value)
yield (key, value)
#===========================================================================
# The following functions handle the different sections in the output
def ground_state_optimization(self, value):
subspace_optimization = value[0]["Hamiltonian Optimization"]
subspace_optimization = subspace_optimization[0]["Subspace Optimization"]
wavefunction_iterations = subspace_optimization["Wavefunctions Iterations"]
n_iterations = len(wavefunction_iterations)
self.backend.addValue("number_of_scf_iterations", n_iterations)
for iteration in wavefunction_iterations:
scf_id = self.backend.openSection("section_scf_iteration")
energies = iteration["Energies"]
# ekin = energies["Ekin"]
# epot = energies["Epot"]
# enl = energies["Enl"]
# eh = energies["EH"]
# gradient = iteration["gnrm"]
exc = energies.get("EXC") # Use get instead, because this value is not always present
evxc = energies.get("EvXC") # Use get instead, because this value is not always present
etotal = iteration["EKS"]
energy_change = iteration["D"]
self.backend.addRealValue("energy_total_scf_iteration", etotal, unit="hartree")
if exc is not None:
self.backend.addRealValue("energy_XC_scf_iteration", exc, unit="hartree")
if evxc is not None:
self.backend.addRealValue("energy_XC_potential_scf_iteration", evxc, unit="hartree")
self.backend.addRealValue("energy_change_scf_iteration", energy_change, unit="hartree")
self.backend.closeSection("section_scf_iteration", scf_id)
def atomic_structure(self, value):
np_positions = []
np_labels = []
positions = value["Positions"]
for position in positions:
np_positions.append(*position.values())
np_labels.append(*position.keys())
np_positions = np.array(np_positions)
np_labels = np.array(np_labels)
self.backend.addArrayValues("atom_positions", np_positions, unit="angstrom")
self.backend.addArrayValues("atom_labels", np_labels)
def simulation_domain(self, value):
simulation_cell = np.diag(value["Angstroem"])
self.backend.addArrayValues("simulation_cell", simulation_cell, unit="angstrom")
def atomic_system_properties(self, value):
# Number of atoms
n_atoms = value["Number of atoms"]
self.backend.addValue("number_of_atoms", n_atoms)
# Periodicity
boundary = value["Boundary Conditions"]
if boundary == "Free":
periodic_dimensions = np.array([False, False, False])
elif boundary == "Periodic":
periodic_dimensions = np.array([True, True, True])
elif boundary == "Surface":
periodic_dimensions = np.array([True, False, True])
else:
raise Exception("Unknown boundary condtions.")
self.backend.addArrayValues("configuration_periodic_dimensions", periodic_dimensions)
def dft(self, value):
# Total_charge
charge = value["qcharge"]
self.backend.addValue("total_charge", charge)
# SCF options
max_iter = value["itermax"]
self.backend.addValue("scf_max_iteration", max_iter)
# Spin channels
n_spin = value["nspin"]
self.backend.addValue("number_of_spin_channels", n_spin)
def atomic_forces(self, value):
forces = []
for force in value:
forces.append(*force.values())
forces = np.array(forces)
self.backend.addArrayValues("atom_forces", forces, unit="hartree/bohr")
def dft_parameters(self, value):
# XC functional
exchange_settings = value["eXchange Correlation"]
xc_id = exchange_settings["XC ID"]
# LibXC codes, see http://bigdft.org/Wiki/index.php?title=XC_codes
if xc_id < 0:
xc_parts = []
xc_id_str = str(xc_id)[1:]
xc_id_str_len = len(xc_id_str)
if xc_id_str_len <= 3:
xc_id_str = "0"*(3-xc_id_str_len)+xc_id_str
xc_parts.append(xc_id_str)
elif xc_id_str_len == 6:
xc_parts.append(xc_id_str[:3])
xc_parts.append(xc_id_str[3:])
xc = []
for part in xc_parts:
xc1 = LIB_XC_MAPPING.get(part)
if xc1 is not None:
xc.append(xc1)
else:
raise Exception("Unknown LibXC functional number: '{}'".format(part))
# ABINIT codes, see
# http://www.tddft.org/programs/octopus/wiki/index.php/Developers_Manual:ABINIT
# and
# http://bigdft.org/Wiki/index.php?title=XC_codes
else:
mapping = {
1: ["LDA_XC_TETER93"],
# 2: ["LDA_C_PZ"], # Not really sure...
# 3: Unknown
# 4: ["LDA_C_WIGNER"], # Not really sure...
# 5: ["LDA_C_HL"], # Not really sure...
# 6: ["LDA_C_XALPHA"], # Not really sure...
# 7: ["LDA_XC_PW"], # Not really sure...
# 8: ["LDA_X_PW"], # Not really sure...
# 9: ["LDA_X_PW", "LDA_C_RPA"], # Not really sure...
# 10: Internal
11: ["GGA_C_PBE", "GGA_X_PBE"],
12: ["GGA_X_PBE"],
# 13: ["GGA_C_PBE","GGA_X_LB"], # Not really sure...
# 14: ["GGA_C_PBE","GGA_X_PBE_R"], # Not really sure...
15: ["GGA_C_PBE", "GGA_X_RPBE"],
16: ["GGA_XC_HCTH_93"],
17: ["GGA_XC_HCTH_120"],
# 20: Unknown
# 21: Unknown
# 22: Unknown
# 23: ["GGA_X_WC"], # Not really sure...
# 24: ["GGA_X_C09X"], # Not really sure...
# 25: Internal
26: ["GGA_XC_HCTH_147"],
27: ["GGA_XC_HCTH_407"],
# 28: Internal
100: ["HF_X"],
}
xc = mapping.get(xc_id)
# Create the XC sections and a summary
if xc is None:
raise Exception("Unknown functional number: '{}'".format(xc_id))
sorted_xc = sorted(xc)
summary = ""
n_names = len(sorted_xc)
for i_name, name in enumerate(sorted_xc):
weight = 1.0
xc_id = self.backend.openSection("section_XC_functionals")
self.backend.addValue("XC_functional_name", name)
self.backend.addValue("XC_functional_weight", weight)
self.backend.closeSection("section_XC_functionals", xc_id)
summary += "{}*{}".format(weight, name)
if i_name+1 != n_names:
summary += "_"
self.backend.addValue("XC_functional", summary)