#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2019 Chris Sewell
#
# This file is part of aiida-gulp.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms and conditions
# of version 3 of the GNU Lesser General Public License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
import re
from aiida_gulp import __version__
from aiida_gulp.parsers.raw.parse_output_common import read_gulp_table
[docs]def parse_file(file_obj, parser_class=None):
""" parse a file resulting from a GULP `fit` run """
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": [],
}
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
configs = re.findall(
"Total number of configurations input =[\\s]+([0-9]+)", line
)
if configs:
output["total_configurations"] = int(configs[0])
continue
if line.strip().startswith("* General input information"):
section = "pre_info"
continue
if line.strip().startswith("Start of fitting :"):
section = "fitting"
continue
if section == "fitting":
if line.strip().startswith("Cycle:"):
output["num_cycles"] = output.get("num_cycles", 0) + 1
if line.strip().startswith("**** Fit completed successfully ****"):
output["fit_succeeded"] = True
section = "post_info"
continue
if line.strip().startswith("**** No lower sum of squares could be found ****"):
output["errors"].append("**** No lower sum of squares could be found ****")
output["fit_succeeded"] = False
section = "post_info"
continue
if line.strip().startswith(
"**** No. of variables exceeds no. of observables ****"
):
output["errors"].append(
"**** No. of variables exceeds no. of observables ****"
)
output["fit_succeeded"] = False
section = "post_info"
continue
if (
line.strip().startswith("Final sum of squares")
and not output["fit_succeeded"]
):
output["fit_succeeded"] = False
section = "post_info"
continue
if section == "post_info":
try:
if line.strip().startswith("Final values of parameters"):
lineno, output["final_parameters"] = read_gulp_table(
lines,
lineno,
["parameter", "original", "final", "type"],
[int, float, float, assess_species],
)
continue
if line.strip().startswith(
"Final values of numerical parameter gradients"
):
lineno, output["final_gradients"] = read_gulp_table(
lines,
lineno,
["parameter", "gradient", "type"],
[int, float, assess_species],
)
continue
if line.strip().startswith("Final values of residuals"):
lineno, output["final_residuals"] = read_gulp_table(
lines,
lineno,
[
"observable",
"type",
"value",
"calculated",
"residual",
"error",
],
[int, str, float, float, float, float],
)
continue
if line.strip().startswith(
"Comparison of initial and final observables"
):
lineno, output["calculated_observables"] = read_gulp_table(
lines,
lineno,
["observable", "type", "value", "initial", "final"],
[int, str, float, float, float],
)
continue
if line.strip().startswith("Energy shifts for configurations"):
lineno, output["energy_shifts"] = read_gulp_table(
lines,
lineno,
["configuration", "energy", "scale_factor"],
[int, float, float],
)
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
except IOError as err:
output["parser_errors"].append(str(err))
continue
return (
output,
assign_exit_code(
output.get("fit_succeeded", False),
output["errors"],
output["parser_errors"],
),
)
[docs]def assess_species(value):
""" some table finish with 'Parameter Type' 'Species',
Species can be either:
- 0 for 'Energy shift'
- blank (for global variables)
- <i> <j> for species specific
"""
# TODO assess_species
if value.startswith("Energy shift"):
return value[:12]
return value
[docs]def assign_exit_code(fit_succeeded, gulp_errors, parser_errors):
""" given the error messages, assign an exit code """
if "**** No. of variables exceeds no. of observables ****" in gulp_errors:
return "ERROR_NOT_ENOUGH_OBSERVABLES"
elif "**** No lower sum of squares could be found ****" in gulp_errors:
return "ERROR_FIT_UNSUCCESFUL"
elif not fit_succeeded:
return "ERROR_FIT_UNSUCCESFUL"
elif gulp_errors:
return "ERROR_GULP_UNKNOWN"
elif parser_errors:
return "ERROR_PARSING_STDOUT"
return None