From edc8cfa5fdab7909f62e8124d866dc4095faf846 Mon Sep 17 00:00:00 2001 From: Alice Date: Wed, 1 Jul 2026 18:46:38 +0200 Subject: [PATCH] outline python class a bit --- CMakeLists.txt | 4 +- include/BadChannels.hpp | 6 +- include/JungfrauCalibration.hpp | 0 python/src/JungfrauCalibration.py | 136 ++++++++------------ python/src/JungfrauCalibrationParameters.py | 77 +++++++++++ src/BadChannels.cpp | 17 ++- 6 files changed, 145 insertions(+), 95 deletions(-) create mode 100644 include/JungfrauCalibration.hpp create mode 100644 python/src/JungfrauCalibrationParameters.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 3539207..113acf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,8 +47,10 @@ add_library(jungfraucalibration STATIC ${SourceFiles} ${PUBLICHEADERS}) target_include_directories( jungfraucalibration PUBLIC "$") -target_link_libraries(jungfraucalibration PRIVATE aare_core) # TODO: should it be public? +target_link_libraries(jungfraucalibration PUBLIC aare_core) # TODO: should it be public? # TODO: add other compile options? target_compile_features(jungfraucalibration PRIVATE cxx_std_17) +#add_subdirectory(examples) + diff --git a/include/BadChannels.hpp b/include/BadChannels.hpp index 3e0f5f3..8938857 100644 --- a/include/BadChannels.hpp +++ b/include/BadChannels.hpp @@ -1,14 +1,14 @@ #include "aare/JungfrauDataFile.hpp" -#include "aare/utils/SparseMask.hpp" +#include "aare/NDArray.hpp" using namespace aare; namespace jungfraucalibration { - SparseMask CreateBadChannelPixelMask(JungfrauDataFile &pedestal_file, const size_t num_pedestals_g0, const size_t num_pedestals_g1, const size_t num_pedestals_g2); + NDArray CreateBadChannelPixelMask(JungfrauDataFile &pedestal_file, const size_t num_pedestals_g0, const size_t num_pedestals_g1, const size_t num_pedestals_g2); - SparseMask CreateBadChannelPixelMask(JungfrauDataFile &pedestals_g0_file, const JungfrauDataFile &pedestals_g1_file, const JungfrauDataFile &pedestal_g2_file); + NDArray CreateBadChannelPixelMask(JungfrauDataFile &pedestals_g0_file, const JungfrauDataFile &pedestals_g1_file, const JungfrauDataFile &pedestal_g2_file); } // namespace jungfraucalibration \ No newline at end of file diff --git a/include/JungfrauCalibration.hpp b/include/JungfrauCalibration.hpp new file mode 100644 index 0000000..e69de29 diff --git a/python/src/JungfrauCalibration.py b/python/src/JungfrauCalibration.py index f24d28d..a0ec3d3 100644 --- a/python/src/JungfrauCalibration.py +++ b/python/src/JungfrauCalibration.py @@ -1,108 +1,77 @@ from pathlib import Path +import JungfrauCalibrationParameters from aare import JungfrauDataFile from helpers import get_first_file +import numpy as np -class JungfrauCalibrationParameters: - """ - A class to hold calibration parameters for the Jungfrau detector. - """ +from aare.calibration import get_gain - @property - def pedestal_file_dir(self) -> Path: - return self._pedestal_file_dir - @pedestal_file_dir.setter - def pedestal_file_dir(self, filepath : Path): - if not filepath.exists(): - raise ValueError(f"Pedestal file directory {filepath} does not exist.") - self._pedestal_file_dir = filepath +class JungfrauCalibration: - @property - def pedestal_g0_file_prefix(self) -> str: - return self._pedestal_g0_file_prefix + def __init__(self, calibration_params: JungfrauCalibrationParameters): + self.calibration_params = calibration_params + self.bad_channel_mask : np.ndarray + self.histogram : np.ndarray - @pedestal_g0_file_prefix.setter - def pedestal_g0_file_prefix(self, file_prefix : str): - self._pedestal_g0_file_prefix = file_prefix - - # TODO: add deprecated decorator - @property - def pedestal_file_prefix(self) -> str: - return self._pedestal_file_prefix - @pedestal_file_prefix.setter - def pedestal_file_prefix(self, file_prefix : str): - self._pedestal_file_prefix = file_prefix - - @property - def num_pedestals_g0(self) -> int: - return self._num_pedestals_g0 - @num_pedestals_g0.setter - def num_pedestals_g0(self, num_pedestals : int): - self._num_pedestals_g0 = num_pedestals + # TODO: maybe pass num pedestals, pedestal file instead of calibration params? + def calculate_bad_pixels_mask(self) -> np.ndarray: + """ + Calculate the bad pixels mask for the Jungfrau detector. - @property - def num_pedestals_g1(self) -> int: - return self._num_pedestals_g1 - - @num_pedestals_g1.setter - def num_pedestals_g1(self, num_pedestals : int): - self._num_pedestals_g1 = num_pedestals + Returns: + np.ndarray: A boolean array where True indicates a bad pixel. + """ + if(self.calibration_params.num_pedestals_g0 is not None and self.calibration_params.num_pedestals_g1 is not None and self.calibration_params.num_pedestals_g2 is not None): + + jungfrau_file = JungfrauDataFile(get_first_file(self.calibration_params.pedestal_file_dir, self.calibration_params.pedestal_file_prefix)) - @property - def num_pedestals_g2(self) -> int: - return self._num_pedestals_g2 - - @num_pedestals_g2.setter - def num_pedestals_g2(self, num_pedestals : int): - self._num_pedestals_g2 = num_pedestals + g0_pedestal_frames = jungfrau_file.read_n(self.calibration_params.num_pedestals_g0) # TODO: option to only read gain? - mmh reading things twice from filesystem also bad + g1_pedestal_frames = jungfrau_file.read_n(self.calibration_params.num_pedestals_g1) - @property - def raw_file_dir(self) -> Path: - return self._raw_file_dir - - @raw_file_dir.setter - def raw_file_dir(self, filepath : Path): - if not filepath.exists(): - raise ValueError(f"Raw file directory {filepath} does not exist.") - self._raw_file_dir = filepath + g2_pedestal_frames = jungfrau_file.read_n(self.calibration_params.num_pedestals_g2) - @property - def raw_file_prefix(self) -> str: - return self._raw_file_prefix - - @raw_file_prefix.setter - def raw_file_prefix(self, file_prefix : str): - self._raw_file_prefix = file_prefix + # get gain from each pixel - update mask + max_frames = max(self.calibration_params.num_pedestals_g0, self.calibration_params.num_pedestals_g1, self.calibration_params.num_pedestals_g2) + bad_channel_mask = np.zeros((jungfrau_file.rows(), jungfrau_file.cols()), dtype=bool) # bad channels pixel mask - # TODO add a read_config method - - -def calculate_bad_pixels_mask(calibration_params: JungfrauCalibrationParameters): - """ - Calculate the bad pixels mask for the Jungfrau detector. - - Returns: - np.ndarray: A boolean array where True indicates a bad pixel. - """ - - if(calibration_params.num_pedestals_g0 is not None and calibration_params.num_pedestals_g1 is not None and calibration_params.num_pedestals_g2 is not None): + # TODO loop to etensive + + return bad_channel_mask - jungfrau_file = JungfrauDataFile(get_first_file(calibration_params.pedestal_file_dir, calibration_params.pedestal_file_prefix)) - g0_pedestal_frames = jungfrau_file.read_n(calibration_params.num_pedestals_g0) # TODO: option to only read gain? - mmh reading things twice from filesystem also bad + def calculate_histogram(self): + """ + Calculate the histogram of each pixel. - g1_pedestal_frames = jungfrau_file.read_n(calibration_params.num_pedestals_g1) + Returns: + np.ndarray: The histogram for each pixel value + """ - g2_pedestal_frames = jungfrau_file.read_n(calibration_params.num_pedestals_g2) + + def fit_function(self): + """ + Fit a Gaussian to the histogram of each pixel. + Returns: + np.ndarray: The fitted Gaussian parameters for each pixel. + """ + def calibrate_G0(self): + """ + Calibrate the G0 gain of the Jungfrau detector. - # Placeholder for actual implementation - # This function should analyze the detector data and identify bad pixels - pass + Returns: + np.ndarray: The calibrated G0 gain values for each pixel. + """ + self.calculate_bad_pixels_mask() + self.calculate_histogram() + self.fit_function() + +## additional plot methods for visualizing the histogram and fitted Gaussian parameters can be added here - depens how fast not neccessary to compute on the fly def main(): calibration_params = JungfrauCalibrationParameters() @@ -114,6 +83,9 @@ def main(): calibration_params.num_pedestals_g0 = 1000 calibration_params.num_pedestals_g1 = 1000 calibration_params.num_pedestals_g2 = 1000 + + + diff --git a/python/src/JungfrauCalibrationParameters.py b/python/src/JungfrauCalibrationParameters.py new file mode 100644 index 0000000..5d1d120 --- /dev/null +++ b/python/src/JungfrauCalibrationParameters.py @@ -0,0 +1,77 @@ +from pathlib import Path + +class JungfrauCalibrationParameters: + """ + A class to hold calibration parameters for the Jungfrau detector. + """ + + @property + def pedestal_file_dir(self) -> Path: + return self._pedestal_file_dir + + @pedestal_file_dir.setter + def pedestal_file_dir(self, filepath : Path): + if not filepath.exists(): + raise ValueError(f"Pedestal file directory {filepath} does not exist.") + self._pedestal_file_dir = filepath + + @property + def pedestal_g0_file_prefix(self) -> str: + return self._pedestal_g0_file_prefix + + @pedestal_g0_file_prefix.setter + def pedestal_g0_file_prefix(self, file_prefix : str): + self._pedestal_g0_file_prefix = file_prefix + + # TODO: add deprecated decorator + @property + def pedestal_file_prefix(self) -> str: + return self._pedestal_file_prefix + @pedestal_file_prefix.setter + def pedestal_file_prefix(self, file_prefix : str): + self._pedestal_file_prefix = file_prefix + + @property + def num_pedestals_g0(self) -> int: + return self._num_pedestals_g0 + + @num_pedestals_g0.setter + def num_pedestals_g0(self, num_pedestals : int): + self._num_pedestals_g0 = num_pedestals + + @property + def num_pedestals_g1(self) -> int: + return self._num_pedestals_g1 + + @num_pedestals_g1.setter + def num_pedestals_g1(self, num_pedestals : int): + self._num_pedestals_g1 = num_pedestals + + @property + def num_pedestals_g2(self) -> int: + return self._num_pedestals_g2 + + @num_pedestals_g2.setter + def num_pedestals_g2(self, num_pedestals : int): + self._num_pedestals_g2 = num_pedestals + + @property + def raw_file_dir(self) -> Path: + return self._raw_file_dir + + @raw_file_dir.setter + def raw_file_dir(self, filepath : Path): + if not filepath.exists(): + raise ValueError(f"Raw file directory {filepath} does not exist.") + self._raw_file_dir = filepath + + @property + def raw_file_prefix(self) -> str: + return self._raw_file_prefix + + @raw_file_prefix.setter + def raw_file_prefix(self, file_prefix : str): + self._raw_file_prefix = file_prefix + + + # TODO add a read_config method \ No newline at end of file diff --git a/src/BadChannels.cpp b/src/BadChannels.cpp index a2daac7..d95c0b5 100644 --- a/src/BadChannels.cpp +++ b/src/BadChannels.cpp @@ -7,7 +7,7 @@ using namespace aare; namespace jungfraucalibration { - SparseMask CreateBadChannelPixelMask(JungfrauDataFile &pedestal_file, const size_t num_pedestals_g0, const size_t num_pedestals_g1, const size_t num_pedestals_g2) + NDArray CreateBadChannelPixelMask(JungfrauDataFile &pedestal_file, const size_t num_pedestals_g0, const size_t num_pedestals_g1, const size_t num_pedestals_g2) { auto pedestals_g0 = pedestal_file.read_n(num_pedestals_g0); auto pedestals_g1 = pedestal_file.read_n(num_pedestals_g1); @@ -17,30 +17,29 @@ namespace jungfraucalibration const size_t cols = pedestal_file.cols(); // get gain from each pixel - update mask - SparseMask bad_channel_mask(STORAGEFORMAT::ROWMAJOR, rows, cols); // bad channels pixel mask + NDArray bad_channel_mask({static_cast(rows), static_cast(cols)}, false); // bad channels pixel mask size_t max_frames = std::max({num_pedestals_g0, num_pedestals_g1, num_pedestals_g2}); // TODO is this more efficient e.g. all three frames fit into cache instead of doing one file at the time? for (size_t frame_idx = 0; frame_idx < max_frames; ++frame_idx) { - for (size_t row = 0; row < rows; ++row) { for (size_t col = 0; col < cols; ++col) { // TODO: nicer to access element from frame directly instead of view()? What is the type? - if (frame_idx < num_pedestals_g0 && get_gain(pedestals_g0[frame_idx].view()(row, col)) != 0) + if (frame_idx < num_pedestals_g0 && get_gain(pedestals_g0[frame_idx].view()(row, col)) != 0) { - bad_channel_mask.insert(row, col); + bad_channel_mask(row, col) = true; } - if (frame_idx < num_pedestals_g1 && get_gain(pedestals_g1[frame_idx].view()(row, col)) != 1) + if (frame_idx < num_pedestals_g1 && get_gain(pedestals_g1[frame_idx].view()(row, col)) != 1) { - bad_channel_mask.insert(row, col); + bad_channel_mask(row, col) = true; } - if (frame_idx < num_pedestals_g2 && get_gain(pedestals_g2[frame_idx].view()(row, col)) != 2) + if (frame_idx < num_pedestals_g2 && get_gain(pedestals_g2[frame_idx].view()(row, col)) != 2) { - bad_channel_mask.insert(row, col); + bad_channel_mask(row, col) = true; } } }