"""
parse the main.gout file of a GULP run and create the required output nodes
"""
import re
from aiida_crystal17 import __version__
from aiida_crystal17.gulp.parsers.raw.parse_output_common import (
read_gulp_table, read_energy_components, read_reaxff_econtribs)
[docs]def parse_file(file_obj, parser_class=None, single_point_only=False):
""" parse a file resulting from a GULP single energy or `optimise` run,
where one structure (configuration) has been supplied
"""
content = file_obj.read()
lines = content.splitlines()
lines_length = len(lines)
output = {
'parser_version': __version__,
'parser_class': parser_class,
'parser_errors': [],
'parser_warnings': [],
"warnings": [],
"errors": [],
"energy_units": "eV"
}
if not lines:
return output, "ERROR_STDOUT_EMPTY"
lineno = 0
section = "heading"
while lineno < lines_length:
line = lines[lineno]
lineno += 1
if line.strip().startswith("!! ERROR"):
output["errors"].append(line.strip())
continue
if line.strip().startswith("!! WARNING"):
output["warnings"].append(line.strip())
continue
if section == "heading":
version = re.findall("\\* Version = ([0-9]+\\.[0-9]+\\.[0-9]+) \\* Last modified", line)
if version:
output['gulp_version'] = version[0]
continue
if line.strip().startswith("* Output for configuration"):
section = "output"
continue
if lineno >= lines_length:
output['parser_errors'].append("Reached end of file before finding output section")
continue
if section == "output":
optimise_start = re.findall("Start of (bulk|surface|polymer) optimisation", line)
if optimise_start:
output["opt_type"] = optimise_start[0]
section = "optimisation"
continue
if section == "optimisation":
if line.strip().startswith("**** Optimisation achieved ****"):
output["opt_succeeded"] = True
section = "post_opt"
continue
if "Conditions for a minimum have not been satisfied. However" in line:
output["opt_succeeded"] = True
section = "post_opt"
output['warnings'].append(
("Conditions for a minimum have not been satisfied. "
"However no lower point can be found - treat results with caution"))
continue
if "No variables to optimise - single point performed" in line:
output["opt_succeeded"] = True
section = "post_opt"
output['warnings'].append("No variables to optimise - single point performed")
continue
if "**** Too many failed attempts to optimise ****" in line:
output["opt_succeeded"] = False
section = "post_opt"
output['errors'].append("**** Too many failed attempts to optimise ****")
continue
if "**** Maximum number of function calls has been reached ****" in line:
output["opt_succeeded"] = False
section = "post_opt"
output['errors'].append("**** Maximum number of function calls has been reached ****")
continue
if line.strip().startswith("Final energy"):
output["opt_succeeded"] = False
section = "post_opt"
output['parser_errors'].append("Reached final energy, before finding 'Optimisation achieved'")
continue
if section == "output":
if line.strip().startswith("Components of energy :"):
energy, penergy = (("energy", "primitive_energy") if single_point_only
else ("initial_energy", "initial_primitive_energy"))
try:
output[energy], output[penergy], lineno = read_energy_components(
lines, lineno)
except (IOError, ValueError) as err:
output["parser_errors"].append(str(err))
continue
# TODO convert this to energy if single-point calculation
if section == "post_opt":
if line.strip().startswith("Components of energy :"):
try:
output["final_energy"], output["final_primitive_energy"], lineno = read_energy_components(
lines, lineno)
except (IOError, ValueError) as err:
output["parser_errors"].append(str(err))
continue
if section == "output" or section == "optimisation":
# will be in 'output' if single energy calculation
if line.strip().startswith('ReaxFF : Energy contributions:'):
# TODO these are printed for every optimisation step (if `verbose`), should just find last one then read
try:
output['energy_contributions'], lineno = read_reaxff_econtribs(lines, lineno)
except (IOError, ValueError) as err:
output["parser_errors"].append(str(err))
continue
if section == "output" or section == "post_opt":
# will be in 'output' if single energy calculation
# if line.strip().startswith("Final energy ="):
# # this should be the same as the (primitive energy from the components section)
# continue
if line.strip().startswith("Final fractional/Cartesian coordinates of atoms"):
# output for surfaces and polymers
try:
lineno, output['final_coords'] = read_gulp_table(
lines, lineno,
["id", "label", "type", "x", "y", "z", "radius"],
[int, str, str, float, float, float, float])
except (IOError, ValueError) as err:
output["parser_errors"].append(str(err))
continue
if line.strip().startswith("Final charges from ReaxFF"):
lineno, output["reaxff_charges"] = read_gulp_table(
lines, lineno, ["index", "atomic_number", "charge"], [int, int, float])
continue
if line.strip().startswith("Time to end of optimisation"):
# 'Time to end of optimisation = 0.0899 seconds'
time_match = re.findall("Time to end of optimisation[\\s]*=[\\s]*([+-]?[0-9]*[.]?[0-9]+) seconds", line)
if time_match:
output["opt_time_second"] = float(time_match[0])
continue
if line.strip().startswith("Peak dynamic memory used"):
# 'Peak dynamic memory used = 0.56 MB'
mem_match = re.findall("Peak dynamic memory used[\\s]*=[\\s]*([+-]?[0-9]*[.]?[0-9]+) MB", line)
if mem_match:
output["peak_dynamic_memory_mb"] = float(mem_match[0])
continue
if line.strip().startswith("Total CPU time"):
# 'Total CPU time 0.0187'
mem_match = re.findall("Total CPU time[\\s]*([+-]?[0-9]*[.]?[0-9]+)", line)
if mem_match:
output["total_time_second"] = float(mem_match[0])
continue
return output, assign_exit_code(
output.get("opt_succeeded", None),
output["errors"], output["parser_errors"],
single_point_only)
[docs]def assign_exit_code(opt_succeeded, gulp_errors, parser_errors, single_point_only):
""" given the error messages, assign an exit code """
if "**** Too many failed attempts to optimise ****" in gulp_errors:
return "ERROR_OPTIMISE_MAX_ATTEMPTS"
elif "**** Maximum number of function calls has been reached ****" in gulp_errors:
return "ERROR_OPTIMISE_MAX_CALLS"
elif opt_succeeded is False and not single_point_only:
return "ERROR_OPTIMISE_UNSUCCESFUL"
elif gulp_errors:
return "ERROR_GULP_UNHANDLED"
elif parser_errors:
return "ERROR_PARSING_STDOUT"
elif opt_succeeded is None and not single_point_only:
return "ERROR_GULP_UNHANDLED"
return None