diff --git a/notebooks/Jungfrau/Jungfrau_Create_Fit_Spectra_Histos_NBC.ipynb b/notebooks/Jungfrau/Jungfrau_Create_Fit_Spectra_Histos_NBC.ipynb index 9cbc446899960a4f4095e3169935f5be5e9db4b7..14d3be4db671eaa8637511414bf0102b297d89e1 100644 --- a/notebooks/Jungfrau/Jungfrau_Create_Fit_Spectra_Histos_NBC.ipynb +++ b/notebooks/Jungfrau/Jungfrau_Create_Fit_Spectra_Histos_NBC.ipynb @@ -97,6 +97,7 @@ "import cal_tools.restful_config as rest_cfg\n", "from cal_tools.calcat_interface import JUNGFRAU_CalibrationData\n", "from cal_tools.jungfrau import jungfrau_ff\n", + "from cal_tools.jungfrau.jungfraualgs import fill_histogram\n", "from cal_tools.jungfrau.jungfraulib import JungfrauCtrl\n", "from cal_tools.step_timing import StepTimer\n", "from cal_tools.tools import calcat_creation_time" @@ -310,7 +311,7 @@ "outputs": [], "source": [ "h_n_bins = int((h_range[1] - h_range[0])/h_bins_s)\n", - "h_bins = np.linspace(h_range[0], h_range[1], h_n_bins)\n", + "h_bins = np.linspace(h_range[0], h_range[1], h_n_bins, dtype=np.float32)\n", "\n", "h_spectra = dict()\n", "edges = dict()\n", @@ -415,7 +416,7 @@ "\n", " with multiprocessing.Pool() as pool:\n", "\n", - " partial_fill = partial(jungfrau_ff.fill_histogram, h_bins)\n", + " partial_fill = partial(fill_histogram, h_bins)\n", " r_maps = pool.map(partial_fill, chunks)\n", "\n", " for i, r in enumerate(r_maps):\n", diff --git a/setup.py b/setup.py index b8ce52d6d54df2a7be36b59fbd90f74a2c4ee64a..705fef5e8c466b90061c6d84a78425683e510e7c 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,11 @@ ext_modules = [ ["src/cal_tools/gotthard2/gotthard2algs.pyx"], include_dirs=[numpy.get_include()], ), + Extension( + "cal_tools.jungfrau.jungfraualgs", + ["src/cal_tools/jungfrau/jungfraualgs.pyx"], + include_dirs=[numpy.get_include()], + ), ] diff --git a/src/cal_tools/jungfrau/jungfrau_ff.py b/src/cal_tools/jungfrau/jungfrau_ff.py index b442166efabc702a8d85267db833dc3b462125b5..101052345c8352a1434ec8fd98e538b92f8cc949 100644 --- a/src/cal_tools/jungfrau/jungfrau_ff.py +++ b/src/cal_tools/jungfrau/jungfrau_ff.py @@ -90,44 +90,6 @@ def subtract_offset(offset_map, _inp): return imgs -def fill_histogram(h_bins, imgs): - """ - Fills an histogram with shape - (n_bins-1, memory_cells, n_rows, n_cols) - from input images. - - Args: - h_bins (list, float): the binning of the x-axis - imgs (np.ndarray): image data to histogram - (trains, memory_cells, row, col). - - Returns: histogram bin counts, bin edges. - """ - if not isinstance(imgs, np.ndarray): - raise TypeError("Expected imgs numpy ndarray type.") - - if imgs.ndim < 4: - raise ValueError("Expected 4D imgs array.") - - n_cells = imgs.shape[1] - n_rows = imgs.shape[2] - n_cols = imgs.shape[3] - - h_chk = np.zeros((len(h_bins)-1, n_cells, n_rows, n_cols), dtype=np.int32) - - e_chk = None - for cell in range(n_cells): - for row in range(n_rows): - for col in range(n_cols): - this_cell = imgs[:, cell, ...] - this_pix = this_cell[:, row, col] - _h, _e = np.histogram(this_pix, bins=h_bins) - h_chk[..., cell, row, col] += _h - if e_chk is None: - e_chk = np.array(_e) - return h_chk, e_chk - - # peak finder to find the first photon peak and/or the pedestal peak # can/should be replaced with smthg more efficient diff --git a/src/cal_tools/jungfrau/jungfraualgs.pyx b/src/cal_tools/jungfrau/jungfraualgs.pyx new file mode 100644 index 0000000000000000000000000000000000000000..65ffa1fc974540991fd3aa4b101d92a9d1a2f1b5 --- /dev/null +++ b/src/cal_tools/jungfrau/jungfraualgs.pyx @@ -0,0 +1,27 @@ +import numpy as np +cimport numpy as np +cimport cython + +@cython.boundscheck(False) +@cython.wraparound(False) +def fill_histogram(np.ndarray[np.float32_t, ndim=1] h_bins, np.ndarray[np.float32_t, ndim=4] imgs): + cdef int n_trains = imgs.shape[0] + cdef int n_cells = imgs.shape[1] + cdef int n_rows = imgs.shape[2] + cdef int n_cols = imgs.shape[3] + cdef int n_bins = len(h_bins) - 1 + + cdef np.ndarray[np.int32_t, ndim=4] h_out = np.zeros((n_bins, n_cells, n_rows, n_cols), dtype=np.int32) + cdef int train, cell, row, col, bin + cdef double value + + for cell in range(n_cells): + for row in range(n_rows): + for col in range(n_cols): + for train in range(n_trains): + value = imgs[train, cell, row, col] + for bin in range(n_bins): + if h_bins[bin] <= value < h_bins[bin + 1]: + h_out[bin, cell, row, col] += 1 + break + return h_out, h_bins \ No newline at end of file diff --git a/tests/test_jungfrau_ff.py b/tests/test_jungfrau_ff.py index 7f67b2325b01150a89cac365056e07036f14973c..9159086caa4519c9abb6a758b064a12f7912c1db 100644 --- a/tests/test_jungfrau_ff.py +++ b/tests/test_jungfrau_ff.py @@ -5,10 +5,9 @@ import pytest from cal_tools.jungfrau.jungfrau_ff import ( chunk_multi, - fill_histogram, _peak_position, ) - +from cal_tools.jungfrau.jungfraualgs import fill_histogram def test_peak_detection_correctness(): x = np.array([10, 20, 30, 40, 50, 60, 70, 80]) @@ -45,16 +44,16 @@ def test_distance_constraint(): @pytest.fixture def sample_chunk(): - return np.random.randint(0, 1000, size=(100, 1, 256, 64)) # 100 trains, 1 cell, 256x64 chunk + return np.random.rand(100, 1, 256, 64).astype(np.float32) # 100 trains, 1 cell, 256x64 chunk @pytest.fixture def sample_chunk_16_cells(): - return np.random.randint(0, 1000, size=(100, 16, 256, 64)) # 100 trains, 16 cells, 256x64 chunk + return np.random.rand(100, 16, 256, 64).astype(np.float32) # 100 trains, 16 cells, 256x64 chunk def test_fill_histogram_basic(sample_chunk): - h_bins = np.linspace(0, 1000, 101) # 100 bins + h_bins = np.linspace(0, 1000, 101, dtype=np.float32) # 100 bins hist, edges = fill_histogram(h_bins, sample_chunk) assert hist.shape == (100, 1, 256, 64) @@ -64,44 +63,44 @@ def test_fill_histogram_basic(sample_chunk): def test_fill_histogram_16_cells(sample_chunk_16_cells): - h_bins = np.linspace(0, 1000, 101) - hist, edges = fill_histogram(h_bins, sample_chunk_16_cells) + h_bins = np.linspace(0, 1000, 101, dtype=np.float32) + hist, _ = fill_histogram(h_bins, sample_chunk_16_cells) assert hist.shape == (100, 16, 256, 64) assert np.sum(hist) == np.prod(sample_chunk_16_cells.shape) def test_fill_histogram_single_train(): - chunk = np.random.randint(0, 100, size=(1, 1, 256, 64)) - h_bins = np.linspace(0, 100, 11) - hist, edges = fill_histogram(h_bins, chunk) + chunk = np.random.rand(1, 1, 256, 64).astype(np.float32) + h_bins = np.linspace(0, 100, 11, dtype=np.float32) + hist, _ = fill_histogram(h_bins, chunk) assert hist.shape == (10, 1, 256, 64) assert np.sum(hist) == np.prod(chunk.shape) def test_fill_histogram_single_bin(): - chunk = np.ones((10, 1, 256, 64)) - h_bins = np.array([0, 2]) - hist, edges = fill_histogram(h_bins, chunk) + chunk = np.ones((10, 1, 256, 64), dtype=np.float32) + h_bins = np.array([0, 2], dtype=np.float32) + hist, _ = fill_histogram(h_bins, chunk) assert hist.shape == (1, 1, 256, 64) assert np.all(hist == 10) def test_fill_histogram_float_data(): - chunk = np.random.rand(50, 1, 256, 64) - h_bins = np.linspace(0, 1, 11) - hist, edges = fill_histogram(h_bins, chunk) + chunk = np.random.rand(50, 1, 256, 64).astype(np.float32) + h_bins = np.linspace(0, 1, 11, dtype=np.float32) + hist, _ = fill_histogram(h_bins, chunk) assert hist.shape == (10, 1, 256, 64) assert np.sum(hist) == np.prod(chunk.shape) def test_fill_histogram_out_of_range(): - chunk = np.random.randint(-10, 110, size=(100, 1, 256, 64)) - h_bins = np.linspace(0, 100, 11) - hist, edges = fill_histogram(h_bins, chunk) + chunk = np.random.rand(100, 1, 256, 64).astype(np.float32) + h_bins = np.linspace(0, 100, 11, dtype=np.float32) + hist, _ = fill_histogram(h_bins, chunk) assert hist.shape == (10, 1, 256, 64) assert np.sum(hist) <= np.prod(chunk.shape) @@ -113,21 +112,14 @@ def test_fill_histogram_out_of_range(): (200, 1, 128, 32) ]) def test_fill_histogram_various_shapes(shape): - chunk = np.random.randint(0, 1000, size=shape) - h_bins = np.linspace(0, 1000, 101) - hist, edges = fill_histogram(h_bins, chunk) + chunk = np.random.rand(*shape).astype(np.float32) + h_bins = np.linspace(0, 1000, 101, dtype=np.float32) + hist, _ = fill_histogram(h_bins, chunk) assert hist.shape == (100, *shape[1:]) assert np.sum(hist) == np.prod(shape) -def test_fill_histogram_input_validation(): - - # Test invalid chunk shape - with pytest.raises(ValueError): - fill_histogram(np.linspace(0, 100, 11), np.zeros((10, 256, 64))) - - @pytest.fixture def sample_data(): # 100 trains, 1 memory cell, 1024x512 image