Commit 75773e82 authored by Lauri Himanen's avatar Lauri Himanen

Added parsing for self-interaction correction, multiplicity and total charge.

parent 0641893c
......@@ -28,8 +28,8 @@ overridden only where necesssary.
The parser is designed to be usable also outside the NoMaD project as a
separate python package. This standalone python-only mode is primarily for
people who want to easily access the parser without the need to setup the whole
"NOMAD Stack". It is also used when running unit tests. Here is an example of
the call syntax:
"NOMAD Stack". It is also used when running custom unit tests found in the
folder "cp2k/test/unittests". Here is an example of the call syntax:
```python
from cp2kparser import CP2KParser
......@@ -37,7 +37,7 @@ the call syntax:
# 1. Initialize a parser by giving a path to the CP2K output file and a list of
# default units
path = "/home/lauri/Dropbox/nomad-dev/nomad-lab-base/parsers/cp2k/test/unittests/cp2k_2.6.2/energy_force/unittest.out"
path = "path/to/main.file"
default_units = ["eV"]
parser = CP2KParser(path, default_units=default_units)
......
......@@ -3,6 +3,8 @@ from nomadcore.simple_parser import SimpleMatcher as SM
from nomadcore.caching_backend import CachingLevel
from cp2kparser.utils.baseclasses import MainParser
import numpy as np
import logging
logger = logging.getLogger("nomad.CP2KParser")
#===============================================================================
......@@ -13,8 +15,8 @@ class CP2KMainParser(MainParser):
"""Initialize an output parser.
"""
super(CP2KMainParser, self).__init__(file_path, parser_context)
self.f_regex = "-?\d+\.\d+(?:E(?:\+|-)\d+)?" # Regex for a floating point value
self.i_regex = "-?\d+" # Regex for an integer
self.regex_f = "-?\d+\.\d+(?:E(?:\+|-)\d+)?" # Regex for a floating point value
self.regex_i = "-?\d+" # Regex for an integer
# Define the output parsing tree for this version
self.root_matcher = SM("",
......@@ -49,11 +51,14 @@ class CP2KMainParser(MainParser):
adHoc=self.adHoc_cp2k_section_cell(),
otherMetaInfo=["simulation_cell"]
),
SM( " FUNCTIONAL\|",
SM( " DFT\|",
sections=["section_method"],
otherMetaInfo=["XC_functional"],
otherMetaInfo=["XC_functional", "self_interaction_correction_method"],
forwardMatch=True,
subMatchers=[
SM( " DFT\| Multiplicity\s+(?P<target_multiplicity>{})".format(self.regex_i)),
SM( " DFT\| Charge\s+(?P<total_charge>{})".format(self.regex_i)),
SM( " DFT\| Self-interaction correction \(SIC\)\s+(?P<self_interaction_correction_method>[^\n]+)"),
SM( " FUNCTIONAL\| ([\w\d\W\s]+):",
forwardMatch=True,
repeats=True,
......@@ -73,15 +78,14 @@ class CP2KMainParser(MainParser):
adHoc=self.adHoc_cp2k_section_quickstep_atom_information(),
otherMetaInfo=["atom_label", "atom_position"]
),
# Single Configuration Calculation
SM( " SCF WAVEFUNCTION OPTIMIZATION",
sections=["section_single_configuration_calculation"],
subMatchers=[
SM( r"\s+\d+\s+\S+\s+{0}\s+{0}\s+{0}\s+(?P<energy_total_scf_iteration__hartree>{0})\s+{0}".format(self.f_regex),
SM( r"\s+\d+\s+\S+\s+{0}\s+{0}\s+{0}\s+(?P<energy_total_scf_iteration__hartree>{0})\s+{0}".format(self.regex_f),
sections=["section_scf_iteration"],
repeats=True,
),
SM( r" ENERGY\| Total FORCE_EVAL \( \w+ \) energy \(a\.u\.\):\s+(?P<energy_total__hartree>{0})".format(self.f_regex)),
SM( r" ENERGY\| Total FORCE_EVAL \( \w+ \) energy \(a\.u\.\):\s+(?P<energy_total__hartree>{0})".format(self.regex_f)),
SM( r" ATOMIC FORCES in \[a\.u\.\]"),
SM( r" # Atom Kind Element X Y Z",
adHoc=self.adHoc_atom_forces()
......@@ -99,7 +103,7 @@ class CP2KMainParser(MainParser):
SM( " ATOMIC FORCES in \[a\.u\.\]",
sections=["cp2k_section_md_forces"],
subMatchers=[
SM( "\s+\d+\s+\d+\s+[\w\W\d]+\s+(?P<cp2k_md_force_atom_string>{0}\s+{0}\s+{0})".format(self.f_regex),
SM( "\s+\d+\s+\d+\s+[\w\W\d]+\s+(?P<cp2k_md_force_atom_string>{0}\s+{0}\s+{0})".format(self.regex_f),
sections=["cp2k_section_md_force_atom"],
repeats=True,
)
......@@ -107,13 +111,13 @@ class CP2KMainParser(MainParser):
),
SM( " STEP NUMBER\s+=\s+(?P<cp2k_md_step_number>\d+)"),
SM( " TIME \[fs\]\s+=\s+(?P<cp2k_md_step_time>\d+\.\d+)"),
SM( " TEMPERATURE \[K\]\s+=\s+(?P<cp2k_md_temperature_instantaneous>{0})\s+(?P<cp2k_md_temperature_average>{0})".format(self.f_regex)),
SM( " TEMPERATURE \[K\]\s+=\s+(?P<cp2k_md_temperature_instantaneous>{0})\s+(?P<cp2k_md_temperature_average>{0})".format(self.regex_f)),
SM( " i =",
sections=["cp2k_section_md_coordinates"],
otherMetaInfo=["cp2k_md_coordinates"],
dependencies={"cp2k_md_coordinates": ["cp2k_md_coordinate_atom_string"]},
subMatchers=[
SM( " \w+\s+(?P<cp2k_md_coordinate_atom_string>{0}\s+{0}\s+{0})".format(self.f_regex),
SM( " \w+\s+(?P<cp2k_md_coordinate_atom_string>{0}\s+{0}\s+{0})".format(self.regex_f),
endReStr="\n",
sections=["cp2k_section_md_coordinate_atom"],
repeats=True,
......@@ -130,6 +134,7 @@ class CP2KMainParser(MainParser):
# The cache settings
self.caching_level_for_metaname = {
'section_XC_functionals': CachingLevel.ForwardAndCache,
'self_interaction_correction_method': CachingLevel.Cache,
'cp2k_section_md_coordinates': CachingLevel.Cache,
'cp2k_section_md_coordinate_atom': CachingLevel.Cache,
'cp2k_md_coordinate_atom_string': CachingLevel.Cache,
......@@ -148,18 +153,34 @@ class CP2KMainParser(MainParser):
with the nomad correspondents and combines into one single string which
is put into the backend.
"""
# Combine the functional names into a one big string that is placed
# into XC_functional
functional_names = []
section_XC_functionals = section["section_XC_functionals"]
for functional in section_XC_functionals:
functional_name = functional["XC_functional_name"][0]
functional_names.append(functional_name)
# Sort and concatenate the functional names
functionals = "_".join(sorted(functional_names))
# Push the functional string into the backend
backend.addValue('XC_functional', functionals)
# Transform the CP2K self-interaction correction string to the NOMAD
# correspondent, and push directly to the superBackend to avoid caching
sic_cp2k = section["self_interaction_correction_method"][0]
sic_map = {
"NO": "",
"AD SIC": "SIC_AD",
"Explicit Orbital SIC": "SIC_EXPLICIT_ORBITALS",
"SPZ/MAURI SIC": "SIC_MAURI_SPZ",
"US/MAURI SIC": "SIC_MAURI_US",
}
sic_nomad = sic_map.get(sic_cp2k)
if sic_nomad is not None:
backend.superBackend.addValue('self_interaction_correction_method', sic_nomad)
else:
logger.warning("Unknown self-interaction correction method used.")
def onClose_cp2k_section_md_coordinate_atom(self, backend, gIndex, section):
"""Given the string with the coordinate components for one atom, make it
into a numpy array of coordinate components and store for later
......@@ -240,7 +261,7 @@ class CP2KMainParser(MainParser):
c_line = parser.fIn.readline()
# Define the regex that extracts the components and apply it to the lines
regex_string = r" CELL\| Vector \w \[angstrom\]:\s+({0})\s+({0})\s+({0})".format(self.f_regex)
regex_string = r" CELL\| Vector \w \[angstrom\]:\s+({0})\s+({0})\s+({0})".format(self.regex_f)
regex_compiled = re.compile(regex_string)
a_result = regex_compiled.match(a_line)
b_result = regex_compiled.match(b_line)
......@@ -264,7 +285,7 @@ class CP2KMainParser(MainParser):
def wrapper(parser):
# Define the regex that extracts the information
regex_string = r"\s+\d+\s+\d+\s+(\w+)\s+\d+\s+({0})\s+({0})\s+({0})".format(self.f_regex)
regex_string = r"\s+\d+\s+\d+\s+(\w+)\s+\d+\s+({0})\s+({0})\s+({0})".format(self.regex_f)
regex_compiled = re.compile(regex_string)
match = True
......
......@@ -2,13 +2,19 @@
This is a module for unit testing the CP2K parser. The unit tests are run with
a custom backend that outputs the results directly into native python object for
easier and faster analysis.
Each property that has an enumerable list of different possible options is
assigned a new test class, that should ideally test through all the options.
The properties that can have any value imaginable will be tested only for one
specific case inside a test class that is designed for a certain type of run
(MD, optimization, QM/MM, etc.)
"""
import os
import unittest
import logging
import numpy as np
from cp2kparser import CP2KParser
from nomadtoolkit import Analyzer
from nomadcore.unit_conversion.unit_conversion import convert_unit
......@@ -26,8 +32,7 @@ def get_results(folder, metainfo_to_keep=None):
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, folder, "unittest.out")
parser = CP2KParser(filename, metainfo_to_keep)
analyzer = Analyzer(parser)
results = analyzer.parse()
results = parser.parse()
return results
......@@ -60,6 +65,32 @@ class TestXCFunctional(unittest.TestCase):
self.assertEqual(xc, "GGA_C_LYP_GGA_X_B88")
#===============================================================================
class TestSelfInteractionCorrectionMethod(unittest.TestCase):
"""Tests that the self-interaction correction can be properly parsed.
"""
def test_no(self):
sic = get_result("sic/no", "self_interaction_correction_method")
self.assertEqual(sic, "")
def test_ad(self):
sic = get_result("sic/ad", "self_interaction_correction_method")
self.assertEqual(sic, "SIC_AD")
def test_explicit_orbitals(self):
sic = get_result("sic/explicit_orbitals", "self_interaction_correction_method")
self.assertEqual(sic, "SIC_EXPLICIT_ORBITALS")
def test_mauri_spz(self):
sic = get_result("sic/mauri_spz", "self_interaction_correction_method")
self.assertEqual(sic, "SIC_MAURI_SPZ")
def test_mauri_us(self):
sic = get_result("sic/mauri_us", "self_interaction_correction_method")
self.assertEqual(sic, "SIC_MAURI_US")
#===============================================================================
class TestEnergyForce(unittest.TestCase):
"""Tests for a CP2K calculation with RUN_TYPE ENERGY_FORCE.
......@@ -143,6 +174,14 @@ class TestEnergyForce(unittest.TestCase):
expected_coordinate = "__STD_INPUT__"
self.assertEqual(coordinate_filename, expected_coordinate)
def test_target_multiplicity(self):
multiplicity = self.results["target_multiplicity"]
self.assertEqual(multiplicity, 1)
def test_total_charge(self):
charge = self.results["total_charge"]
self.assertEqual(charge, 0)
#===============================================================================
if __name__ == '__main__':
pass
......@@ -152,5 +191,6 @@ if __name__ == '__main__':
suites = []
suites.append(unittest.TestLoader().loadTestsFromTestCase(TestXCFunctional))
suites.append(unittest.TestLoader().loadTestsFromTestCase(TestEnergyForce))
suites.append(unittest.TestLoader().loadTestsFromTestCase(TestSelfInteractionCorrectionMethod))
alltests = unittest.TestSuite(suites)
unittest.TextTestRunner(verbosity=0).run(alltests)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment