diff --git a/src/pyclaw/gauges.py b/src/pyclaw/gauges.py index f908ea88..36af4a63 100644 --- a/src/pyclaw/gauges.py +++ b/src/pyclaw/gauges.py @@ -326,6 +326,13 @@ def compare_gauges(paths, gauge_id, fields='all'): if fields.lower() == 'all': fields = range(gauges[0].q.shape[0]) + num_fields = min(gauge.q.shape[0] for gauge in gauges) + bad_fields = [n for n in fields if not 0 <= n < num_fields] + if bad_fields: + raise ValueError("Fields %s out of range, gauge %s has %s fields " + "available in both paths." + % (bad_fields, gauge_id, num_fields)) + fig = plt.figure() fig.suptitle("Gauge %s" % gauges[0].id) for (i, n) in enumerate(fields): @@ -488,6 +495,28 @@ def check_old_gauge_data(path, gauge_id, new_gauge_path="./regression_data"): return fig + +def find_gauge_ids(path): + r"""Return the sorted gauge ids of the gauge files found in `path` + + Gauge files are named `gaugeNNNNN.txt` where the id is zero padded to at + least 5 digits, so ids with more digits, e.g. `gauge1234567.txt`, are + valid. Files matching `gauge*.txt` without an integer id are ignored. + + :Input: + - *path* (path) - Directory to search for gauge files. + + :Output: + - (list) Sorted list of integer gauge ids. + """ + gauge_ids = [] + for gauge_path in Path(path).glob("gauge*.txt"): + id_str = gauge_path.stem[len("gauge"):] + if id_str.isdigit(): + gauge_ids.append(int(id_str)) + return sorted(gauge_ids) + + if __name__ == "__main__": import matplotlib.pyplot as plt parser = argparse.ArgumentParser() @@ -503,8 +532,7 @@ def check_old_gauge_data(path, gauge_id, new_gauge_path="./regression_data"): if args.id: gauge_ids = args.id else: - gauge_ids = [int(str(path.name[5:10])) - for path in args.path[0].glob("gauge*.txt")] + gauge_ids = find_gauge_ids(args.path[0]) if args.fields: fields = args.fields else: diff --git a/src/pyclaw/meson.build b/src/pyclaw/meson.build index 6c443052..e8e76527 100644 --- a/src/pyclaw/meson.build +++ b/src/pyclaw/meson.build @@ -42,6 +42,7 @@ python_sources = { ], 'tests': [ '__init__.py', + 'test_gauges.py', 'test_io.py', ], } diff --git a/src/pyclaw/tests/test_gauges.py b/src/pyclaw/tests/test_gauges.py new file mode 100644 index 00000000..98fe0eb0 --- /dev/null +++ b/src/pyclaw/tests/test_gauges.py @@ -0,0 +1,42 @@ +"""Tests for the gauge utilities in clawpack.pyclaw.gauges""" + +import numpy as np +import pytest + +from clawpack.pyclaw import gauges + + +def _write_gauge(path, gauge_id, num_eqn=2): + gauge = gauges.GaugeSolution() + gauge.id = gauge_id + gauge.location = (0.0, 0.0) + gauge.level = np.ones(3, dtype=int) + gauge.t = np.linspace(0.0, 1.0, 3) + gauge.q = np.arange(num_eqn * 3, dtype=float).reshape((num_eqn, 3)) + gauge.write(str(path)) + + +def test_find_gauge_ids(tmp_path): + # 5-digit padded ids, an id longer than 5 digits, and non-gauge files + for gauge_id in (12, 1, 1234567): + _write_gauge(tmp_path, gauge_id) + (tmp_path / "gauge_notes.txt").write_text("not a gauge\n") + (tmp_path / "gauges.data").write_text("not a gauge\n") + + assert gauges.find_gauge_ids(tmp_path) == [1, 12, 1234567] + + # Long ids must round trip through the reader with the found id + gauge = gauges.GaugeSolution(gauge_id=1234567, path=str(tmp_path)) + assert gauge.id == 1234567 + + +def test_compare_gauges_bad_fields(tmp_path): + pytest.importorskip("matplotlib") + for name, num_eqn in (("a", 2), ("b", 3)): + (tmp_path / name).mkdir() + _write_gauge(tmp_path / name, 1, num_eqn=num_eqn) + paths = [str(tmp_path / "a"), str(tmp_path / "b")] + + # Field 2 exists only in the second path + with pytest.raises(ValueError, match="out of range"): + gauges.compare_gauges(paths, 1, fields=[0, 2])