From 227bc6ea182964ecac9cdc313434eb82b07a12ae Mon Sep 17 00:00:00 2001
From: Remy Prechelt <prechelt@hawaii.edu>
Date: Fri, 2 Oct 2020 21:00:30 -1000
Subject: [PATCH] Add `read_hist` method with coverage info.

---
 .gitlab-ci.yml                |  4 +-
 Python/Makefile               |  2 +-
 Python/corsika/__init__.py    |  7 ++++
 Python/corsika/io/__init__.py | 14 +++++++
 Python/corsika/io/hist.py     | 70 +++++++++++++++++++++++++++++++++++
 Python/setup.cfg              |  4 ++
 Python/setup.py               |  2 +-
 Python/tests/test_hist.py     | 36 ++++++++++++++++++
 8 files changed, 135 insertions(+), 4 deletions(-)
 create mode 100644 Python/corsika/io/__init__.py
 create mode 100644 Python/corsika/io/hist.py
 create mode 100644 Python/tests/test_hist.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 464ce2402..bf5ccd361 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -622,8 +622,8 @@ sanity:
     - cd ${CI_PROJECT_DIR}/Python  # change into the Python directory
     - pip install --user -e '.[test]'  # install the package + test deps
     - make all 2&>1 | tee python-test.log  # this runs all of the Python tests
-    - echo "finished" >> python-test.log # create even an empty file...
-    - cd ${CI_PROJECT_DIR}  # reset the directory    
+    - cd ${CI_PROJECT_DIR}  # reset the directory
+  coverage: '/^TOTAL\s*\d+\s*\d+\s*(.*\%)/'
   artifacts:
     when: always
     expire_in: 1 year
diff --git a/Python/Makefile b/Python/Makefile
index 59a1613fd..ccc13257f 100644
--- a/Python/Makefile
+++ b/Python/Makefile
@@ -16,7 +16,7 @@ tests:
 	${PYTHON} -m pytest --cov=corsika tests
 
 flake:
-	${PYTHON} -m flake8 corsika
+	${PYTHON} -m flake8 corsika tests
 
 black:
 	${PYTHON} -m black -t py37 corsika tests
diff --git a/Python/corsika/__init__.py b/Python/corsika/__init__.py
index cf4f1519e..f5331efad 100644
--- a/Python/corsika/__init__.py
+++ b/Python/corsika/__init__.py
@@ -1,4 +1,6 @@
 """
+ A Python interface to CORSIKA 8.
+
  (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
 
  This software is distributed under the terms of the GNU General Public
@@ -6,4 +8,9 @@
  the license.
 """
 
+from . import io
+
+# all imported objects
+__all__ = ["io"]
+
 __version__: str = "8.0.0-alpha"
diff --git a/Python/corsika/io/__init__.py b/Python/corsika/io/__init__.py
new file mode 100644
index 000000000..bd8a0d8bc
--- /dev/null
+++ b/Python/corsika/io/__init__.py
@@ -0,0 +1,14 @@
+"""
+ The 'io' module provides for reading CORSIKA8 output files.
+
+ (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+
+ This software is distributed under the terms of the GNU General Public
+ Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ the license.
+"""
+
+from .hist import read_hist
+
+# all exported objects
+__all__ = ["read_hist"]
diff --git a/Python/corsika/io/hist.py b/Python/corsika/io/hist.py
new file mode 100644
index 000000000..e12dcfd1b
--- /dev/null
+++ b/Python/corsika/io/hist.py
@@ -0,0 +1,70 @@
+"""
+ This file supports reading boost_histograms from CORSIKA8.
+
+ (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+
+ This software is distributed under the terms of the GNU General Public
+ Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ the license.
+"""
+
+import boost_histogram as bh
+import numpy as np
+
+
+def read_hist(filename: str) -> bh.Histogram:
+    """
+    Read a histogram produced with CORSIKA8's `save_hist()` function.
+
+    Parameters
+    ----------
+    filename: str
+        The filename of the .npy file containing the histogram.
+
+    Returns
+    -------
+    hist: bh.Histogram
+        An initialized bh.Histogram instance.
+
+    Throws
+    ------
+    ValueError:
+        If the histogram type is not supported.
+    """
+
+    # load the filenames
+    d = np.load(filename)
+
+    # extract the axis and overflows information
+    axistypes = d["axistypes"].view("c")
+    overflow = d["overflow"]
+    underflow = d["underflow"]
+
+    # this is where we store the axes that we extract from the file.
+    axes = []
+
+    # we now loop over the axes
+    for i, (at, has_overflow, has_underflow) in enumerate(
+        zip(axistypes, overflow, underflow)
+    ):
+
+        # continuous histogram
+        if at == b"c":
+            axes.append(
+                bh.axis.Variable(
+                    d[f"binedges_{i}"], overflow=has_overflow, underflow=has_underflow
+                )
+            )
+        # discrete histogram
+        elif at == b"d":
+            axes.append(bh.axis.IntCategory(d[f"bins_{i}"], growth=(not has_overflow)))
+
+        # and unknown histogram type
+        else:
+            raise ValueError(f"'{at}' is not a valid C8 histogram axistype.")
+
+    # create the histogram and fill it in
+    h = bh.Histogram(*axes)
+    h.view(flow=True)[:] = d["data"]
+
+    return h
diff --git a/Python/setup.cfg b/Python/setup.cfg
index d318235ee..2544189ca 100644
--- a/Python/setup.cfg
+++ b/Python/setup.cfg
@@ -59,3 +59,7 @@ ignore_missing_imports = True
 # ignore missing types for matplotlib
 [mypy-matplotlib.*]
 ignore_missing_imports = True
+
+# ignore missing types for boost_histogram
+[mypy-boost_histogram.*]
+ignore_missing_imports = True
diff --git a/Python/setup.py b/Python/setup.py
index 36360358f..350fbe9fa 100644
--- a/Python/setup.py
+++ b/Python/setup.py
@@ -32,7 +32,7 @@ setup(
     keywords=["cosmic ray", "physics", "astronomy", "simulation"],
     packages=["corsika"],
     python_requires=">=3.6*, <4",
-    install_requires=["numpy", "pyyaml",],
+    install_requires=["numpy", "pyyaml", "boost_histogram"],
     extras_require={
         "test": [
             "pytest",
diff --git a/Python/tests/test_hist.py b/Python/tests/test_hist.py
new file mode 100644
index 000000000..0ad70b734
--- /dev/null
+++ b/Python/tests/test_hist.py
@@ -0,0 +1,36 @@
+"""
+ Tests for `corsika.io.hist`
+
+ (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+
+ This software is distributed under the terms of the GNU General Public
+ Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ the license.
+"""
+import pytest
+
+import corsika
+
+
+def test_corsika_io() -> None:
+    """
+    Test I can corsika.io without a further import.
+    """
+    corsika.io.read_hist
+
+
+def test_corsika_read_hist() -> None:
+    """
+    Check that I can read in the test histograms with `read_hist`.
+    """
+
+    # try and read in a continuous histogram
+
+    # try and read in a discrete histogram
+
+
+def test_corsika_read_hist_fail() -> None:
+    """
+    Check that an exception is thrown when reading
+    an incorrectly formatted histogram.
+    """
-- 
GitLab