Commit 2560d3cb authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Broke the parsing of different CP2K run types into separate modules. The...

Broke the parsing of different CP2K run types into separate modules. The common functionality is now found in 'commonmatcher.py'. I think that this will make the code maintenance much easier than having everything in one huge file.
parent c45e41aa
......@@ -3,7 +3,7 @@ import re
import logging
from nomadcore.baseclasses import ParserInterface
from cp2kparser.versions.versionsetup import get_main_parser
logger = logging.getLogger(__name__)
logger = logging.getLogger("nomad")
#===============================================================================
......@@ -22,19 +22,31 @@ class CP2KParser(ParserInterface):
"""Setups the version by looking at the output file and the version
specified in it.
"""
# Search for the version specification and initialize a correct
# main parser for this version.
regex = re.compile(r" CP2K\| version string:\s+CP2K version ([\d\.]+)")
n_lines = 30
# Search for the CP2K version specification and the RUN_TYPE for the
# calculation. The correct and optimized parser is initialized based on
# this information.
regex_version = re.compile(r" CP2K\| version string:\s+CP2K version ([\d\.]+)")
regex_run_type = re.compile(r"\s+GLOBAL\| Run type\s+(.+)")
n_lines = 50
version_id = None
run_type = None
with open(self.parser_context.main_file, 'r') as outputfile:
for i_line in xrange(n_lines):
line = next(outputfile)
result = regex.match(line)
if result:
version_id = result.group(1).replace('.', '')
break
if not result:
logger.error("Could not find a version specification from the given main file.")
result_version = regex_version.match(line)
result_run_type = regex_run_type.match(line)
if result_version:
version_id = result_version.group(1).replace('.', '')
if result_run_type:
run_type = result_run_type.group(1)
if version_id is None:
msg = "Could not find a version specification from the given main file."
logger.exception(msg)
raise RuntimeError(msg)
if run_type is None:
msg = "Could not find a version specification from the given main file."
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)
......@@ -44,7 +56,7 @@ class CP2KParser(ParserInterface):
# Setup the correct main parser based on the version id. If no match
# for the version is found, use the main parser for CP2K 2.6.2
self.main_parser = get_main_parser(version_id)(self.parser_context.main_file, self.parser_context)
self.main_parser = get_main_parser(version_id, run_type)(self.parser_context.main_file, self.parser_context)
def get_metainfo_filename(self):
return "cp2k.nomadmetainfo.json"
......
import re
import numpy as np
from nomadcore.simple_parser import SimpleMatcher as SM
from inputparser import CP2KInputParser
import logging
from nomadcore.simple_parser import extractOnCloseTriggers
logger = logging.getLogger("nomad")
#===============================================================================
class CommonMatcher(object):
"""
This class is used to store and instantiate common parts of the
hierarchical SimpleMatcher structure used in the parsing of a CP2K
output file.
"""
def __init__(self, parser_context):
# Repeating regex definitions
self.parser_context = parser_context
self.file_service = parser_context.file_service
self.regex_f = "-?\d+\.\d+(?:E(?:\+|-)\d+)?" # Regex for a floating point value
self.regex_i = "-?\d+" # Regex for an integer
def adHoc_cp2k_section_cell(self):
"""Used to extract the cell information.
"""
def wrapper(parser):
# Read the lines containing the cell vectors
a_line = parser.fIn.readline()
b_line = parser.fIn.readline()
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.regex_f)
regex_compiled = re.compile(regex_string)
a_result = regex_compiled.match(a_line)
b_result = regex_compiled.match(b_line)
c_result = regex_compiled.match(c_line)
# Convert the string results into a 3x3 numpy array
cell = np.zeros((3, 3))
cell[0, :] = [float(x) for x in a_result.groups()]
cell[1, :] = [float(x) for x in b_result.groups()]
cell[2, :] = [float(x) for x in c_result.groups()]
# Push the results to the correct section
parser.backend.addArrayValues("simulation_cell", cell, unit="angstrom")
return wrapper
# SimpleMatcher for the header that is common to all run types
def header(self):
return SM(r" DBCSR\| Multiplication driver",
forwardMatch=True,
subMatchers=[
SM( r" DBCSR\| Multiplication driver",
sections=['cp2k_section_dbcsr'],
),
SM( r" \*\*\*\* \*\*\*\* \*\*\*\*\*\* \*\* PROGRAM STARTED AT\s+(?P<cp2k_run_start_date>\d{4}-\d{2}-\d{2}) (?P<cp2k_run_start_time>\d{2}:\d{2}:\d{2}.\d{3})",
sections=['cp2k_section_startinformation'],
),
SM( r" CP2K\|",
sections=['cp2k_section_programinformation'],
forwardMatch=True,
subMatchers=[
SM( r" CP2K\| version string:\s+(?P<program_version>[\w\d\W\s]+)"),
SM( r" CP2K\| source code revision number:\s+svn:(?P<cp2k_svn_revision>\d+)"),
]
),
SM( r" CP2K\| Input file name\s+(?P<cp2k_input_filename>.+$)",
sections=['cp2k_section_filenames'],
subMatchers=[
SM( r" GLOBAL\| Basis set file name\s+(?P<cp2k_basis_set_filename>.+$)"),
SM( r" GLOBAL\| Geminal file name\s+(?P<cp2k_geminal_filename>.+$)"),
SM( r" GLOBAL\| Potential file name\s+(?P<cp2k_potential_filename>.+$)"),
SM( r" GLOBAL\| MM Potential file name\s+(?P<cp2k_mm_potential_filename>.+$)"),
SM( r" GLOBAL\| Coordinate file name\s+(?P<cp2k_coordinate_filename>.+$)"),
]
),
SM( " CELL\|",
adHoc=self.adHoc_cp2k_section_cell(),
otherMetaInfo=["simulation_cell"]
),
SM( " DFT\|",
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( " TOTAL NUMBERS AND MAXIMUM NUMBERS",
sections=["cp2k_section_total_numbers"],
subMatchers=[
SM( "\s+- Atoms:\s+(?P<number_of_atoms>\d+)"),
SM( "\s+- Shell sets:\s+(?P<cp2k_shell_sets>\d+)")
]
)
]
)
def onClose_section_method(self, backend, gIndex, section):
"""When all the functional definitions have been gathered, matches them
with the nomad correspondents and combines into one single string which
is put into the backend.
"""
# 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_filenames(self, backend, gIndex, section):
"""
"""
# If the input file is available, parse it
input_file = section["cp2k_input_filename"][0]
filepath = self.file_service.get_absolute_path_to_file(input_file)
if filepath is not None:
input_parser = CP2KInputParser(filepath, self.parser_context)
input_parser.parse()
else:
logger.warning("The input file of the calculation could not be found.")
def getOnCloseTriggers(self):
"""
Returns:
A dictionary containing a section name as a key, and a list of
trigger functions associated with closing that section.
"""
onClose = {}
for attr, callback in extractOnCloseTriggers(self).items():
onClose[attr] = [callback]
return onClose
......@@ -2,97 +2,29 @@ import re
from nomadcore.simple_parser import SimpleMatcher as SM
from nomadcore.caching_backend import CachingLevel
from nomadcore.baseclasses import MainHierarchicalParser
from inputparser import CP2KInputParser
from singlepointforceparser import CP2KSinglePointForceParser
from commonmatcher import CommonMatcher
import numpy as np
import logging
logger = logging.getLogger("nomad")
#===============================================================================
class CP2KMainParser(MainHierarchicalParser):
"""The main parser class. Used to parse the CP2K output file and to
instantiate all the other subparsers for each other file.
class CP2KSinglePointParser(MainHierarchicalParser):
"""The main parser class. Used to parse the CP2K calculation with run types:
-ENERGY
-ENERGY_FORCE
"""
def __init__(self, file_path, parser_context):
"""Initialize an output parser.
"""
super(CP2KMainParser, self).__init__(file_path, parser_context)
self.regex_f = "-?\d+\.\d+(?:E(?:\+|-)\d+)?" # Regex for a floating point value
self.regex_i = "-?\d+" # Regex for an integer
# Find out the run type for this calculation. The run type can be used
# to optimize the parsing, because not all parsing functionality will
# be necessary for all run types.
run_type = None
with open(file_path) as f:
counter = 0
for line in f:
counter += 1
# Define the regex that searches for the
regex_string = r"\s+GLOBAL\| Run type\s+(.+)"
regex_compiled = re.compile(regex_string)
match = regex_compiled.match(line)
if match is not None:
run_type = match.groups()[0]
break
if counter > 50:
break
if run_type is None:
logger.error("Could not determine the CP2K run type from the output file.")
# SimpleMatcher for the header that is common to all run types
self.header = SM(r" DBCSR\| Multiplication driver",
forwardMatch=True,
subMatchers=[
SM( r" DBCSR\| Multiplication driver",
sections=['cp2k_section_dbcsr'],
),
SM( r" \*\*\*\* \*\*\*\* \*\*\*\*\*\* \*\* PROGRAM STARTED AT\s+(?P<cp2k_run_start_date>\d{4}-\d{2}-\d{2}) (?P<cp2k_run_start_time>\d{2}:\d{2}:\d{2}.\d{3})",
sections=['cp2k_section_startinformation'],
),
SM( r" CP2K\|",
sections=['cp2k_section_programinformation'],
forwardMatch=True,
subMatchers=[
SM( r" CP2K\| version string:\s+(?P<program_version>[\w\d\W\s]+)"),
SM( r" CP2K\| source code revision number:\s+svn:(?P<cp2k_svn_revision>\d+)"),
]
),
SM( r" CP2K\| Input file name\s+(?P<cp2k_input_filename>.+$)",
sections=['cp2k_section_filenames'],
subMatchers=[
SM( r" GLOBAL\| Basis set file name\s+(?P<cp2k_basis_set_filename>.+$)"),
SM( r" GLOBAL\| Geminal file name\s+(?P<cp2k_geminal_filename>.+$)"),
SM( r" GLOBAL\| Potential file name\s+(?P<cp2k_potential_filename>.+$)"),
SM( r" GLOBAL\| MM Potential file name\s+(?P<cp2k_mm_potential_filename>.+$)"),
SM( r" GLOBAL\| Coordinate file name\s+(?P<cp2k_coordinate_filename>.+$)"),
]
),
SM( " CELL\|",
adHoc=self.adHoc_cp2k_section_cell(),
otherMetaInfo=["simulation_cell"]
),
SM( " DFT\|",
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( " TOTAL NUMBERS AND MAXIMUM NUMBERS",
sections=["cp2k_section_total_numbers"],
subMatchers=[
SM( "\s+- Atoms:\s+(?P<number_of_atoms>\d+)"),
SM( "\s+- Shell sets:\s+(?P<cp2k_shell_sets>\d+)")
]
)
]
)
"""
super(CP2KSinglePointParser, self).__init__(file_path, parser_context)
self.scf_iterations = 0
self.cm = CommonMatcher(parser_context)
self.section_method_index = None
self.section_system_description_index = None
# Simple matcher for run type ENERGY_FORCE, ENERGY
# Simple matcher for run type ENERGY_FORCE, ENERGY with QUICKSTEP
self.energy_force = SM(
" MODULE QUICKSTEP: ATOMIC COORDINATES IN angstrom",
forwardMatch=True,
......@@ -102,18 +34,23 @@ class CP2KMainParser(MainHierarchicalParser):
otherMetaInfo=["atom_label", "atom_position"]
),
SM( " SCF WAVEFUNCTION OPTIMIZATION",
sections=["section_single_configuration_calculation"],
subMatchers=[
SM( r" Trace\(PS\):",
sections=["section_scf_iteration"],
repeats=True,
subMatchers=[
SM( r" Exchange-correlation energy:\s+(?P<energy_XC_scf_iteration__hartree>{})".format(self.regex_f)),
SM( r"\s+\d+\s+\S+\s+{0}\s+{0}\s+{0}\s+(?P<energy_total_scf_iteration__hartree>{0})\s+(?P<energy_change_scf_iteration__hartree>{0})".format(self.regex_f)),
SM( r" Exchange-correlation energy:\s+(?P<energy_XC_scf_iteration__hartree>{})".format(self.cm.regex_f)),
SM( r"\s+\d+\s+\S+\s+{0}\s+{0}\s+{0}\s+(?P<energy_total_scf_iteration__hartree>{0})\s+(?P<energy_change_scf_iteration__hartree>{0})".format(self.cm.regex_f)),
]
),
SM( r" Electronic kinetic energy:\s+(?P<electronic_kinetic_energy__hartree>{})".format(self.regex_f)),
SM( r" ENERGY\| Total FORCE_EVAL \( \w+ \) energy \(a\.u\.\):\s+(?P<energy_total__hartree>{0})".format(self.regex_f)),
SM( r" \*\*\* SCF run converged in\s+(\d+) steps \*\*\*",
adHoc=self.adHoc_single_point_converged()
),
SM( r" \*\*\* SCF run NOT converged \*\*\*",
adHoc=self.adHoc_single_point_not_converged()
),
SM( r" Electronic kinetic energy:\s+(?P<electronic_kinetic_energy__hartree>{})".format(self.cm.regex_f)),
SM( r" ENERGY\| Total FORCE_EVAL \( \w+ \) energy \(a\.u\.\):\s+(?P<energy_total__hartree>{0})".format(self.cm.regex_f)),
SM( r" ATOMIC FORCES in \[a\.u\.\]"),
SM( r" # Atom Kind Element X Y Z",
adHoc=self.adHoc_atom_forces()
......@@ -123,75 +60,17 @@ class CP2KMainParser(MainHierarchicalParser):
]
)
# Simple matcher for run type MD, MOLECULAR_DYNAMICS
self.md = SM(
" MD| Molecular Dynamics Protocol",
sections=["cp2k_section_md"],
# Compose root matcher according to the run type. This way the
# unnecessary regex parsers will not be compiled and searched. Saves
# computational time.
self.root_matcher = SM("",
forwardMatch=True,
sections=['section_run', "section_single_configuration_calculation", "section_system_description", "section_method"],
subMatchers=[
SM( " ENERGY\| Total FORCE_EVAL",
repeats=True,
sections=["cp2k_section_md_step"],
subMatchers=[
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.regex_f),
sections=["cp2k_section_md_force_atom"],
repeats=True,
)
]
),
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.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.regex_f),
endReStr="\n",
sections=["cp2k_section_md_coordinate_atom"],
repeats=True,
)
]
)
]
)
self.cm.header(),
self.energy_force
]
)
# Compose root matcher according to the run type. This way the
# unnecessary regex parsers will not be compiled and searched. Saves
# computational time.
if run_type in ("ENERGY_FORCE", "FORCE", "WAVEFUNCTION_OPTIMIZATION", "WFN_OPT"):
self.root_matcher = SM("",
forwardMatch=True,
sections=['section_run', "section_system_description", "section_method"],
subMatchers=[
self.header,
self.energy_force
]
)
elif run_type in ("GEO_OPT", "GEOMETRY_OPTIMIZATION"):
self.root_matcher = SM("",
forwardMatch=True,
sections=['section_run', "section_system_description", "section_method"],
subMatchers=[
self.header,
self.geo_opt
]
)
elif run_type in ("MD", "MOLECULAR_DYNAMICS"):
self.root_matcher = SM("",
forwardMatch=True,
sections=['section_run', "section_system_description", "section_method"],
subMatchers=[
self.header,
self.md
]
)
#=======================================================================
# The cache settings
self.caching_level_for_metaname = {
......@@ -208,28 +87,28 @@ class CP2KMainParser(MainHierarchicalParser):
'cp2k_md_force_atom_float': CachingLevel.Cache,
}
#=======================================================================
# The additional onClose trigger functions
self.onClose = self.cm.getOnCloseTriggers()
#===========================================================================
# The functions that trigger when sections are closed
# onClose triggers. These are specific to this main parser, common
# triggers are imprted from commonmatchers module.
def onClose_section_scf_iteration(self, backend, gIndex, section):
"""Keep track of how many SCF iteration are made."""
self.scf_iterations += 1
def onClose_section_system_description(self, backend, gIndex, section):
"""Stores the index of the section method. Should always be 0, but
let's get it dynamically just in case there's something wrong.
"""
self.section_system_description_index = gIndex
def onClose_section_method(self, backend, gIndex, section):
"""When all the functional definitions have been gathered, matches them
with the nomad correspondents and combines into one single string which
is put into the backend.
"""Stores the index of the section method. Should always be 0, but
let's get it dynamically just in case there's something wrong.
"""
# 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.")
self.section_method_index = gIndex
def onClose_section_single_configuration_calculation(self, backend, gIndex, section):
"""
......@@ -245,54 +124,15 @@ class CP2KMainParser(MainHierarchicalParser):
else:
logger.warning("The file containing the forces printed by ENERGY_FORCE calculation could not be found.")
def onClose_cp2k_section_filenames(self, backend, gIndex, section):
"""
"""
# If the input file is available, parse it
input_file = section["cp2k_input_filename"][0]
filepath = self.file_service.get_absolute_path_to_file(input_file)
if filepath is not None:
input_parser = CP2KInputParser(filepath, self.parser_context)
input_parser.parse()
else:
logger.warning("The input file of the calculation could not be found.")
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
concatenation.
"""
force_string = section["cp2k_md_coordinate_atom_string"][0]
components = np.array([float(x) for x in force_string.split()])
backend.addArrayValues("cp2k_md_coordinate_atom_float", components)
# Output the number of SCF iterations made
backend.addValue("scf_dft_number_of_iterations", self.scf_iterations)
def onClose_cp2k_section_md_coordinates(self, backend, gIndex, section):
"""When all the coordinates for individual atoms have been gathered,
concatenate them into one big array and forward to the backend.
"""
forces = section["cp2k_md_coordinate_atom_float"]
forces = np.array(forces)
backend.addArrayValues("cp2k_md_coordinates", forces)
def onClose_cp2k_section_md_force_atom(self, backend, gIndex, section):
"""Given the string with the force components for one atom, make it
into a numpy array of force components and store for later
concatenation.
"""
force_string = section["cp2k_md_force_atom_string"][0]
components = np.array([float(x) for x in force_string.split()])
backend.addArrayValues("cp2k_md_force_atom_float", components)
def onClose_cp2k_section_md_forces(self, backend, gIndex, section):
"""When all the forces for individual atoms have been gathered,
concatenate them into one big array and forward to the backend.
"""
forces = section["cp2k_md_force_atom_float"]
forces = np.array(forces)
backend.addArrayValues("cp2k_md_forces", forces, unit="forceAu")
# Write the references to section_method and section_system_description
backend.addValue('single_configuration_to_calculation_method_ref', self.section_method_index)
backend.addValue('single_configuration_calculation_to_system_description_ref', self.section_system_description_index)
#===========================================================================
# adHoc functions that are used to do custom parsing. Primarily these
# adHoc functions. Primarily these
# functions are used for data that is formatted as a table or a list.
def adHoc_section_XC_functionals(self):
"""Used to extract the functional information.
......@@ -327,33 +167,6 @@ class CP2KMainParser(MainHierarchicalParser):
return wrapper
def adHoc_cp2k_section_cell(self):
"""Used to extract the cell information.
"""
def wrapper(parser):
# Read the lines containing the cell vectors
a_line = parser.fIn.readline()
b_line = parser.fIn.readline()
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.regex_f)
regex_compiled = re.compile(regex_string)
a_result = regex_compiled.match(a_line)
b_result = regex_compiled.match(b_line)
c_result = regex_compiled.match(c_line)
# Convert the string results into a 3x3 numpy array
cell = np.zeros((3, 3))
cell[0, :] = [float(x) for x in a_result.groups()]
cell[1, :] = [float(x) for x in b_result.groups()]
cell[2, :] = [float(x) for x in c_result.groups()]
# Push the results to the correct section
parser.backend.addArrayValues("simulation_cell", cell, unit="angstrom")
return wrapper
def adHoc_cp2k_section_quickstep_atom_information(self):
"""Used to extract the initial atomic coordinates and names in the
Quickstep module.
......@@ -361,7 +174,7 @@ class CP2KMainParser(MainHierarchicalParser):
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.regex_f)
regex_string = r"\s+\d+\s+\d+\s+(\w+)\s+\d+\s+({0})\s+({0})\s+({0})".format(self.cm.regex_f)
regex_compiled = re.compile(regex_string)
match = True
......@@ -421,3 +234,118 @@ class CP2KMainParser(MainHierarchicalParser):
parser.backend.addArrayValues("atom_forces", force_array, unit="forceAu")
return wrapper
def adHoc_single_point_converged(self):
"""Called when the SCF cycle of a single point calculation has converged.
"""
def wrapper(parser):
parser.backend.addValue("single_configuration_calculation_converged", True)