Skip to content

Commit cb41e93

Browse files
Merge pull request #92 from cdunn314/update_channel_reading
Fix bug in custom peak_kwargs input into find_peaks
2 parents e3f81c4 + e5c5318 commit cb41e93

2 files changed

Lines changed: 144 additions & 16 deletions

File tree

libra_toolbox/neutron_detection/activation_foils/compass.py

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -536,19 +536,18 @@ def compute_detection_efficiency(
536536
detection_efficiency = nb_counts_measured / expected_nb_counts
537537

538538
return detection_efficiency
539-
540-
def get_peaks(self, hist: np.ndarray, **kwargs) -> np.ndarray:
541-
"""Returns the peak indices of the histogram
539+
540+
def get_peak_fitting_parameters(self, hist: np.ndarray, **kwargs) -> Dict[str, Union[float, List[float]]]:
541+
"""Returns the peak fitting parameters for the given histogram and check source nuclide.
542542
543543
Args:
544544
hist: a histogram
545545
kwargs: optional parameters for the peak finding algorithm
546546
see scipy.signal.find_peaks for more information
547547
548548
Returns:
549-
the peak indices in ``hist``
549+
the peak fitting parameters
550550
"""
551-
552551
if self.detector_type.lower() == 'nai':
553552
# peak finding parameters
554553
start_index = 100
@@ -604,22 +603,46 @@ def get_peaks(self, hist: np.ndarray, **kwargs) -> np.ndarray:
604603

605604
# update the parameters if kwargs are provided
606605
if kwargs:
606+
print("Using custom peak finding parameters from kwargs: ", kwargs)
607607
start_index = kwargs.get("start_index", start_index)
608608
prominence = kwargs.get("prominence", prominence)
609609
height = kwargs.get("height", height)
610610
width = kwargs.get("width", width)
611611
distance = kwargs.get("distance", distance)
612612

613+
return {
614+
"start_index": start_index,
615+
"prominence": prominence,
616+
"height": height,
617+
"width": width,
618+
"distance": distance
619+
}
620+
621+
def get_peaks(self, hist: np.ndarray, **kwargs) -> np.ndarray:
622+
"""Returns the peak indices of the histogram
623+
624+
Args:
625+
hist: a histogram
626+
kwargs: optional parameters for the peak finding algorithm
627+
see scipy.signal.find_peaks for more information
628+
629+
Returns:
630+
the peak indices in ``hist``
631+
"""
632+
633+
# get the peak fitting parameters based on the check source nuclide and the detector type
634+
peak_fitting_parameters = self.get_peak_fitting_parameters(hist, **kwargs)
635+
613636
# run the peak finding algorithm
614637
# NOTE: the start_index is used to ignore the low energy region
615638
peaks, peak_data = find_peaks(
616-
hist[start_index:],
617-
prominence=prominence,
618-
height=height,
619-
width=width,
620-
distance=distance,
639+
hist[peak_fitting_parameters["start_index"]:],
640+
prominence=peak_fitting_parameters["prominence"],
641+
height=peak_fitting_parameters["height"],
642+
width=peak_fitting_parameters["width"],
643+
distance=peak_fitting_parameters["distance"],
621644
)
622-
peaks = np.array(peaks) + start_index
645+
peaks = np.array(peaks) + peak_fitting_parameters["start_index"]
623646

624647
# special case for Mn-54, only keep the first high count energy peak
625648
if self.check_source.nuclide == mn54 and len(peaks) > 1:
@@ -798,6 +821,13 @@ def get_neutron_rate(
798821

799822
return flux
800823

824+
def _get_nuclide_peak_kwargs(measurement: CheckSourceMeasurement, peak_kwargs: dict = None) -> dict:
825+
kwargs = {}
826+
if peak_kwargs is not None:
827+
if measurement.check_source.nuclide.name in peak_kwargs.keys():
828+
kwargs = peak_kwargs[measurement.check_source.nuclide.name]
829+
return kwargs
830+
801831

802832
def get_calibration_data(
803833
check_source_measurements: List[CheckSourceMeasurement],
@@ -838,11 +868,9 @@ def get_calibration_data(
838868
hist, bin_edges = detector.get_energy_hist_background_substract(
839869
background_detector, bins=None
840870
)
841-
kwargs = {}
842-
if peak_kwargs is not None:
843-
if measurement.check_source.nuclide in peak_kwargs.keys():
844-
kwargs = peak_kwargs[measurement.check_source.nuclide]
845-
found_a_nuclide = True
871+
kwargs = _get_nuclide_peak_kwargs(measurement, peak_kwargs)
872+
if kwargs:
873+
found_a_nuclide = True
846874

847875
peaks_ind = measurement.get_peaks(hist, **kwargs)
848876
peaks = bin_edges[peaks_ind]

test/neutron_detection/test_compass.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def test_get_channel(filename, expected_channel):
3333
assert ch == expected_channel
3434

3535

36+
3637
def create_empty_csv_files(directory, base_name, count, channel):
3738
"""
3839
Creates empty CSV files in a specified directory with a specific pattern.
@@ -1724,3 +1725,102 @@ def test_get_peaks(detector_type, nuclide, signal_to_background_ratio):
17241725
for test_energy, expected_energy in zip(test_peaks, nuclide.energy):
17251726
print(f"Detected peak at {test_energy:.2f} keV, expected at {expected_energy} keV")
17261727
assert np.isclose(test_energy, expected_energy, rtol=0.05)
1728+
1729+
1730+
@pytest.mark.parametrize(
1731+
"peak_kwargs, expected",
1732+
[
1733+
(None, {}),
1734+
({na22.name: {"start_index": 123, "height": 4.5},
1735+
co60.name: {"start_index": 456, "height": 7.8}},
1736+
{"start_index": 123, "height": 4.5}),
1737+
({co60.name: {"distance": 99}}, {}),
1738+
],
1739+
)
1740+
def test_get_nuclide_peak_kwargs(peak_kwargs, expected):
1741+
measurement = compass.CheckSourceMeasurement("test")
1742+
measurement.check_source = CheckSource(
1743+
nuclide=na22,
1744+
activity_date=datetime.datetime(2024, 1, 1),
1745+
activity=1.0,
1746+
)
1747+
1748+
result = compass._get_nuclide_peak_kwargs(measurement, peak_kwargs)
1749+
1750+
assert result == expected
1751+
1752+
1753+
@pytest.mark.parametrize(
1754+
"detector_type, nuclide",
1755+
[
1756+
("NaI", na22),
1757+
("NaI", co60),
1758+
("NaI", ba133),
1759+
("NaI", mn54),
1760+
("HPGe", na22),
1761+
("HPGe", co60),
1762+
("HPGe", ba133),
1763+
("HPGe", mn54),
1764+
],
1765+
)
1766+
def test_get_peak_fitting_parameters_with_kwargs(detector_type, nuclide):
1767+
"""Test that kwargs properly override default parameters in get_peak_fitting_parameters."""
1768+
1769+
# Create a test histogram (random values)
1770+
hist = np.random.rand(1000) * 100
1771+
hist[500:600] = np.random.rand(100) * 1000 # Add a peak
1772+
1773+
# Create a CheckSourceMeasurement
1774+
check_source = CheckSource(
1775+
nuclide=nuclide,
1776+
activity_date=datetime.datetime(2024, 1, 1),
1777+
activity=1.0
1778+
)
1779+
1780+
measurement = compass.CheckSourceMeasurement(name="test_measurement")
1781+
measurement.check_source = check_source
1782+
measurement.detector_type = detector_type
1783+
1784+
# Test 1: Get default parameters (no kwargs)
1785+
default_params = measurement.get_peak_fitting_parameters(hist)
1786+
1787+
# Check that all expected keys are present
1788+
assert "start_index" in default_params
1789+
assert "prominence" in default_params
1790+
assert "height" in default_params
1791+
assert "width" in default_params
1792+
assert "distance" in default_params
1793+
1794+
# Test 2: Override with custom kwargs
1795+
custom_kwargs = {
1796+
"start_index": 200,
1797+
"prominence": 50.0,
1798+
"height": 75.0,
1799+
"width": [5, 100],
1800+
"distance": 50,
1801+
}
1802+
1803+
custom_params = measurement.get_peak_fitting_parameters(hist, **custom_kwargs)
1804+
1805+
# Verify all kwargs were applied
1806+
assert custom_params["start_index"] == 200, "start_index kwarg not applied"
1807+
assert custom_params["prominence"] == 50.0, "prominence kwarg not applied"
1808+
assert custom_params["height"] == 75.0, "height kwarg not applied"
1809+
assert custom_params["width"] == [5, 100], "width kwarg not applied"
1810+
assert custom_params["distance"] == 50, "distance kwarg not applied"
1811+
1812+
# Test 3: Partial kwargs override (some default, some custom)
1813+
partial_kwargs = {
1814+
"start_index": 300,
1815+
"height": 100.0,
1816+
}
1817+
1818+
partial_params = measurement.get_peak_fitting_parameters(hist, **partial_kwargs)
1819+
1820+
# Verify partial overrides work
1821+
assert partial_params["start_index"] == 300, "partial start_index kwarg not applied"
1822+
assert partial_params["height"] == 100.0, "partial height kwarg not applied"
1823+
# Other parameters should remain as defaults
1824+
assert partial_params["prominence"] == default_params["prominence"], "Other params should not change"
1825+
assert partial_params["width"] == default_params["width"], "Other params should not change"
1826+
assert partial_params["distance"] == default_params["distance"], "Other params should not change"

0 commit comments

Comments
 (0)