Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Python library for developer utilities with an initial module for log parsing #499

Merged
merged 10 commits into from
Jun 10, 2024
1 change: 1 addition & 0 deletions doc/developers
1 change: 1 addition & 0 deletions doc/superbuild/source/developers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ meant for SUNDIALS developers.
pull_requests/index
releases/index
packages/index
sundialsdev/index
appendix/index

.. only:: html
Expand Down
18 changes: 18 additions & 0 deletions doc/superbuild/source/developers/style_guide/SourceCode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,24 @@ Coding Conventions and Rules
x;`` to ``return(x);``. Note, however, lots of older SUNDIALS source code
uses ``return(x);``.

#. ``SUNLogger`` statements must be in the format:

.. code-block:: c

[log level][rank][scope][label] key1 = value, key2 = value

or if the payload (the part after the label) is a vector/array:

.. code-block:: c

[log level][rank][scope][label] key(:)
value1
value2

Note that the ``(:)`` is needed for the ``scripts/sundialsdev/logs.py`` Python
utility to understand that the payload is an array.

.. code-block:: c

.. _Style.Formatting:

Expand Down
23 changes: 23 additions & 0 deletions doc/superbuild/source/developers/sundialsdev/index.rst
gardner48 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
..
-----------------------------------------------------------------------------
SUNDIALS Copyright Start
Copyright (c) 2002-2024, Lawrence Livermore National Security
and Southern Methodist University.
All rights reserved.
See the top-level LICENSE and NOTICE files for details.

SPDX-License-Identifier: BSD-3-Clause
SUNDIALS Copyright End
-----------------------------------------------------------------------------

.. _SUNDIALS_DEV:

sundialsdev Python Library
==========================

This is a Python library of utilities SUNDIALS developer may find useful.
Right now it consists of the following modules:

- ``logs``: this module has functions for parsing logs produced by `SUNLogger`.

8 changes: 8 additions & 0 deletions scripts/sundialsdev/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

"""
This is a Python library of utilities SUNDIALS developer may find useful.
Right now it consists of the following modules:
- `logs`: this module has functions for parsing logs produced by `SUNLogger`.
"""
120 changes: 120 additions & 0 deletions scripts/sundialsdev/logs.py
gardner48 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------
# Programmer(s): Cody Balos @ LLNL
# -----------------------------------------------------------------------------
# SUNDIALS Copyright Start
# Copyright (c) 2002-2024, Lawrence Livermore National Security
# and Southern Methodist University.
# All rights reserved.
#
# See the top-level LICENSE and NOTICE files for details.
#
# SPDX-License-Identifier: BSD-3-Clause
# SUNDIALS Copyright End
# -----------------------------------------------------------------------------
# Module of Python functions that may be useful to SUNDIALS developers writing
# scripts to parse logs produced by SUNLogger.
# -----------------------------------------------------------------------------

import re
import numpy as np


def parse_logfile_payload(payload, line_number, all_lines, array_indicator="(:)"):
"""
This function parses the payload of in a SUNDIALS log file line
into a dictionary. The payload of a SUNDIALS log file line
is the part after all the [ ] brackets.
"""
kvpstrs = payload.split(",")
kvp_dict = {}
for kvpstr in kvpstrs:
kvp = kvpstr.split("=")
if len(kvp) == 1:
kvp_dict[kvp[0].strip()] = ""
else:
key, value = kvp
values = []
if array_indicator in key:
for line in all_lines[line_number + 1 :]:
if line.startswith("["):
break
values.append(np.double(line))
kvp_dict[key.strip()] = values
else:
kvp_dict[key.strip()] = value.strip()
return kvp_dict


def parse_logfile_line(line, line_number, all_lines):
"""
This function takes a line from a SUNDIALS log file and parses it into a dictionary.
A log file line has the form:
[loglvl][rank][scope][label] key1 = value, key2 = value
Log file payloads can be multiline if they are an array/vector with one value per line.
I.e.
[loglvl][rank][scope][label] y(:)
y_1
y_2
...
"""
pattern = re.compile("\[(\w+)\]\[(rank \d+)\]\[(.*)\]\[(.*)\](.*)")
matches = pattern.findall(line)
line_dict = {}
if matches:
line_dict["loglvl"] = matches[0][0]
line_dict["rank"] = matches[0][1]
line_dict["scope"] = matches[0][2]
line_dict["label"] = matches[0][3]
line_dict["payload"] = parse_logfile_payload(
matches[0][4], line_number, all_lines
)
return line_dict


def log_file_to_list(filename, step_scope_txt):
"""
This function takes a debug log file from CVODE and creates a list where
balos1 marked this conversation as resolved.
Show resolved Hide resolved
each list entry is a step attempt.
balos1 marked this conversation as resolved.
Show resolved Hide resolved

E.g.,
[
[
{
"loglvl": "DEBUG",
"rank": "rank 0",
"scope": "<step_scope_txt>",
"label": "enter-step-attempt-loop",
"payload": {"step": "0", "h": "1e-06", "q": "1", "t_n": "0"},
}, ...
], ...
]
"""
with open(filename, "r") as logfile:
log = []
lines_for_this_step = None
all_lines = logfile.readlines()
for line_number, line in enumerate(all_lines):
line_dict = parse_logfile_line(line.rstrip(), line_number, all_lines)
if not line_dict:
continue
if (
line_dict["scope"] == step_scope_txt
and line_dict["label"] == "enter-step-attempt-loop"
):
if lines_for_this_step is None:
lines_for_this_step = [line_dict]
else:
log.append(lines_for_this_step)
lines_for_this_step = [line_dict]
else:
lines_for_this_step.append(line_dict)
return log


def cvode_debug_file_to_list(filename):
"""
This function takes a debug log file from CVODE and creates a list where
each list entry is a step attempt. See log_file_to_list.
"""
return log_file_to_list(filename, "CVODE::cvStep")
2 changes: 1 addition & 1 deletion src/cvode/cvode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2704,7 +2704,7 @@ static void cvPredict(CVodeMem cv_mem)

#ifdef SUNDIALS_LOGGING_EXTRA_DEBUG
SUNLogger_QueueMsg(CV_LOGGER, SUN_LOGLEVEL_DEBUG, "CVODE::cvPredict",
"return", "predictor =", "");
"return", "zn0(:) =", "");
N_VPrintFile(cv_mem->cv_zn[0], CV_LOGGER->debug_fp);
#endif
}
Expand Down
Loading