Skip to content

Commit

Permalink
Merge branch 'extended-log-summary' into 'master'
Browse files Browse the repository at this point in the history
Extended OGS log summary

See merge request ogs/ogs!5086
  • Loading branch information
bilke committed Aug 29, 2024
2 parents bfcd81e + 10d5771 commit 44489d5
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 168 deletions.
88 changes: 70 additions & 18 deletions Applications/Python/ogs/dev/ogs_log_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"Comparing data array `(?P<array_1>.+)' from file `(?P<file_1>.+)' to data array `(?P<array_2>.+)' from file `(?P<file_2>.+)'[.]"
)
re_line_max_norm = re.compile(r"(abs|rel) maximum norm = \[([-+.,0-9e infa]+)\]")
re_line_error = re.compile(
r"Absolute and relative error [(]maximum norm[)] are larger than the corresponding thresholds .* and .*[.]"
)

# A typical OGS output file name is, e.g., ramped_Neumann_BC_ts_50_t_0.625000.vtu.
# This regex matches the numerical parts of the file name including underscore
Expand Down Expand Up @@ -73,7 +76,7 @@ def parse_ogs_log(log_fh):
if mode is None:
if line.endswith("info: ---------- vtkdiff begin ----------"):
mode = "vtkdiff"
vtkdiff_rec = {}
vtkdiff_rec = {"check_succeeded": True}
elif m := re_prj_file.search(line):
prj_file = m.group(1)
logger.debug(
Expand All @@ -96,6 +99,8 @@ def parse_ogs_log(log_fh):
abs_or_rel = m.group(1)
error_norm = [float(comp) for comp in m.group(2).split(",")]
vtkdiff_rec[abs_or_rel + "_maximum_norm"] = error_norm
elif re_line_error.search(line):
vtkdiff_rec["check_succeeded"] = False

if mode is not None:
logger.warning("File '%s' contains an incomplete vtkdiff block.", log_fn)
Expand Down Expand Up @@ -123,8 +128,13 @@ def parse_ogs_log(log_fh):
return recs, prj_file


def agg_max_by_field(df):
return df.groupby("array").max()
def agg_by_field(df):
agg = {
col: "min" if col == "check_succeeded" else "max"
for col in df.columns
if col != "array"
}
return df.groupby("array").agg(agg)


def round_up_2_digits(df):
Expand Down Expand Up @@ -166,8 +176,15 @@ def write_xml_snippet(df_max, xml_out_file):
for tup in df_max.itertuples():
file = Path(tup.file).name
file_re = numbers_to_regexes(file)
error_msg = (
"""
<!-- Check failed -->"""
if not tup.check_succeeded
else ""
)

fh.write(
f"""
f"""{error_msg}
<vtkdiff>
<regex>{file_re}</regex>
<!-- <file>{file}</file> -->
Expand Down Expand Up @@ -263,12 +280,12 @@ def aggregate_log_files(log_files_dirs):
for prj_file, df_vtkdiff in map_prj_file_to_df_vtkdiff_combined.items():
df_vtkdiff = remove_duplicate_columns(df_vtkdiff) # noqa: PLW2901

map_prj_file_to_agg_vtkdiff_stats[prj_file] = agg_max_by_field(df_vtkdiff)
map_prj_file_to_agg_vtkdiff_stats[prj_file] = agg_by_field(df_vtkdiff)

return map_prj_file_to_agg_vtkdiff_stats


def run(log_files_dirs, xml_out_dir, verbose):
def run(log_files_dirs, out_dir, *, snippet_out, csv_out, verbose):
map_prj_file_to_agg_vtkdiff_stats = aggregate_log_files(log_files_dirs)

for i, (prj_file, df_vtkdiff_max) in enumerate(
Expand All @@ -279,18 +296,25 @@ def run(log_files_dirs, xml_out_dir, verbose):
print() # blank line as a separator
print(f"###### {prj_file}\n")

df_vtkdiff_max = round_up_2_digits(df_vtkdiff_max) # noqa: PLW2901
df_vtkdiff_max_rounded = round_up_2_digits(df_vtkdiff_max)

if verbose:
print_table_summary(df_vtkdiff_max, sys.stdout)
print_table_summary(df_vtkdiff_max_rounded, sys.stdout)

if xml_out_dir is not None:
if out_dir is not None and (snippet_out or csv_out):
prj_dir = Path(prj_file).parent
assert not prj_dir.is_absolute()
this_xml_out_dir = xml_out_dir / prj_dir
this_xml_out_dir.mkdir(parents=True, exist_ok=True)
this_xml_out_file = this_xml_out_dir / Path(prj_file).name
write_xml_snippet(df_vtkdiff_max, this_xml_out_file)
this_out_dir = out_dir / prj_dir
this_out_dir.mkdir(parents=True, exist_ok=True)

if snippet_out:
this_xml_out_file = this_out_dir / Path(prj_file).name
write_xml_snippet(df_vtkdiff_max_rounded, this_xml_out_file)

if csv_out:
this_csv_out_file = this_out_dir / f"{Path(prj_file).name}.csv"
# for 17 digits precision cf. also https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
df_vtkdiff_max.to_csv(this_csv_out_file, sep="\t", float_format="%.17g")


if __name__ == "__main__":
Expand All @@ -302,7 +326,20 @@ def run(log_files_dirs, xml_out_dir, verbose):
"--verbose", "-v", action="store_true", help="Produce verbose cmdline output"
)
parser.add_argument(
"--snippet-out", type=Path, help="OGS prj snippet output directory"
"-o",
"--out",
type=Path,
help="output directory root for prj file snippets and CSV files.",
)
parser.add_argument(
"--snippet-out",
action="store_true",
help="Output <test_definition> snippets for prj files.",
)
parser.add_argument(
"--csv-out",
action="store_true",
help="Output aggregated statistics as CSV files.",
)
parser.add_argument(
"log_files_dirs",
Expand All @@ -314,9 +351,8 @@ def run(log_files_dirs, xml_out_dir, verbose):
args = parser.parse_args()

snippet_out = args.snippet_out

if snippet_out is not None:
assert snippet_out.is_dir()
csv_out = args.csv_out
outdir = args.out

logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
Expand All @@ -325,11 +361,27 @@ def run(log_files_dirs, xml_out_dir, verbose):
ch.setFormatter(formatter)
logger.addHandler(ch)

if outdir and not (snippet_out or csv_out):
logger.INFO("Output directory will not be used.")

if (snippet_out or csv_out) and not outdir:
logger.ERROR("No output directory specified for XML and/or CSV output.")
sys.exit()

if outdir is not None:
assert outdir.is_dir()

# suppress error message from Python interpreter, e.g., if a command
# pipeline exits early, see
# https://docs.python.org/3/library/signal.html#note-on-sigpipe
try:
run(args.log_files_dirs, snippet_out, args.verbose)
run(
args.log_files_dirs,
outdir,
snippet_out=snippet_out,
csv_out=csv_out,
verbose=args.verbose,
)
except BrokenPipeError:
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
Loading

0 comments on commit 44489d5

Please sign in to comment.