Skip to content

Commit

Permalink
fixed sipm peak search
Browse files Browse the repository at this point in the history
  • Loading branch information
rosannadeckert committed Oct 31, 2024
1 parent 2575f47 commit 275fd17
Showing 1 changed file with 31 additions and 136 deletions.
167 changes: 31 additions & 136 deletions src/sipm_simple_calibration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,29 @@ using just the 1 p.e. and 2 p.e. peak positions estimated by a peakfinder.
Inputs:
* `pe_uncal`: array of uncalibrated peak amplitudes
kwargs:
* `expect_noise_peak`: bool value stating whether noise peak exists in the uncalibrated spectrum or not
* `initial_min_amp`: uncalibrated amplitude value as a left boundary to build the uncalibrated histogram where the peak search is performed on.
For the peak search with noise peak, this value is consecutively increased i.o.t exclude the noise peak from the histogram.
* `initial_max_quantile`: quantile of the uncalibrated amplitude array to used as right boundary to build the uncalibrated histogram
* `step_size_min_amp`: only for if noise peak exists, this gives the step size for the minimal amplitude increase, to exclude the noise peak from the uncalibrated histogram eventually
* `peakfinder_σ`: sigma value in number of bins for peakfinder
* `peakfinder_threshold`: threshold value for peakfinder
Returns
* `pe_simple_cal`: array of the calibrated pe array with the simple calibration
* `func`: function to use for the calibration
* `func`: function to use for the calibration (`pe_simple_cal = pe_uncal .* c .+ offset`)
* `c`: calibration factor
* `offset`: calibration offset
* `peakpos`: 1 p.e. and 2 p.e. peak positions in uncalibrated amplitude
* `peakpos_cal`: 1 p.e. and 2 p.e. peak positions in calibrated amplitude
* `h_uncal`: histogram of the uncalibrated pe array
* `h_calsimple`: histogram of the calibrated pe array with the simple calibration
"""
function sipm_simple_calibration end
export sipm_simple_calibration

function sipm_simple_calibration(pe_uncal::Vector{<:Real};
expect_noise_peak::Bool=false,
function sipm_simple_calibration(pe_uncal::Vector{<:Real};

Check warning on line 29 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L29

Added line #L29 was not covered by tests
kwargs...)

h_uncal, peakpos = if expect_noise_peak
find_peaks_noise_peak_exists(pe_uncal; kwargs...)
else
find_peaks(pe_uncal; kwargs...)
end
h_uncal, peakpos = find_peaks(pe_uncal; kwargs...)

Check warning on line 32 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L32

Added line #L32 was not covered by tests

# simple calibration
gain = peakpos[2] - peakpos[1]
Expand Down Expand Up @@ -64,79 +59,6 @@ end



# search the 1 p.e. and 2 p.e. peak, noise peak exists
function find_peaks_noise_peak_exists(
amps::Vector{<:Real}; initial_min_amp::Real=1.0, initial_max_quantile::Real=0.99,
step_size_min_amp::Real=1.0, peakfinder_σ::Real=2.0, peakfinder_threshold::Real=10.0
)
# Start with a big window where the noise peak is included
min_amp = initial_min_amp
max_quantile = initial_max_quantile
max_amp = quantile(amps, max_quantile)
bin_width = get_friedman_diaconis_bin_width(filter(in(quantile(amps, 0.01)..quantile(amps, 0.9)), amps))

# Initial peak search
h_uncal = fit(Histogram, amps, min_amp:bin_width:max_amp)
h_decon, peakpos = peakfinder(h_uncal, σ=peakfinder_σ, backgroundRemove=true, threshold=peakfinder_threshold)

num_peaks = length(peakpos)

# Determine the noise peak position
peakpos_idxs = StatsBase.binindex.(Ref(h_decon), peakpos)
cts_peakpos = h_decon.weights[peakpos_idxs]
noise_peak_pos = peakpos[argmax(cts_peakpos)]

# helper function
function is_within_range(x0, x_list, range=0.5)
for x in x_list
if abs(x - x0) <= range
return true
end
end
return false
end

# Adjust min_amp to exclude the noise peak
while is_within_range(noise_peak_pos, peakpos)
min_amp += step_size_min_amp

h_uncal = fit(Histogram, amps, min_amp:bin_width:max_amp)
h_decon, peakpos = peakfinder(h_uncal, σ=peakfinder_σ, backgroundRemove=true, threshold=peakfinder_threshold)
println("Current peak positions: ", peakpos)

num_peaks = length(peakpos)

# Safety check to avoid infinite loops
if min_amp >= 50
error("Unable to exclude noise peak within reasonable min_amp range.")
end
end

# If more than two peaks are found, reduce max_quantile to find exactly two peaks
if num_peaks > 2
println("Found more than 2 peaks. Reducing max range. Currently: quantile $max_quantile")

while num_peaks != 2
max_quantile -= 0.01
max_amp = quantile(amps, max_quantile)

h_uncal = fit(Histogram, amps, min_amp:bin_width:max_amp)
h_decon, peakpos = peakfinder(h_uncal, σ=peakfinder_σ, backgroundRemove=true, threshold=peakfinder_threshold)

num_peaks = length(peakpos)

# Safety check to avoid infinite loops
if max_quantile <= 0.5
error("Unable to find exactly two peaks within reasonable quantile range.")
end
end
end

return h_uncal, peakpos
end



function find_peaks(
amps::Vector{<:Real}; initial_min_amp::Real=1.0, initial_max_quantile::Real=0.99,
peakfinder_σ::Real=2.0, peakfinder_threshold::Real=10.0
Expand All @@ -151,76 +73,49 @@ function find_peaks(
h_uncal = fit(Histogram, amps, min_amp:bin_width:max_amp)
h_decon, peakpos = peakfinder(h_uncal, σ=peakfinder_σ, backgroundRemove=true, threshold=peakfinder_threshold)

# Ensure at least 2 peaks
num_peaks = length(peakpos)
if num_peaks == 0
error("No peaks found.")

Check warning on line 79 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L78-L79

Added lines #L78 - L79 were not covered by tests
end

# Determine the 1 p.e. peak position based on the assumption that it is the highest
peakpos_idxs = StatsBase.binindex.(Ref(h_decon), peakpos)
cts_peakpos = h_decon.weights[peakpos_idxs]
first_pe_peak_pos = peakpos[argmax(cts_peakpos)]

# remove all peaks < 1p.e. peak
# Remove all peaks with x vals < x pos of 1p.e. peak
filter!(x -> x >= first_pe_peak_pos, peakpos)
num_peaks = length(peakpos)

while num_peaks < 2
# Try increasing σ first
while num_peaks < 2 && peakfinder_σ < 5.0
h_decon, peakpos = peakfinder(h_uncal, σ=peakfinder_σ, backgroundRemove=true, threshold=peakfinder_threshold)
num_peaks = length(peakpos)

if num_peaks < 2
peakfinder_σ += 0.5
end
end

# If increasing σ doesn't find 2 peaks, start lowering the threshold
if num_peaks < 2
# while less than two peaks found, or second peakpos smaller than 1 pe peakpos, or gain smaller than 1 (peaks too close)
while num_peaks < 2 || peakpos[2] <= first_pe_peak_pos || (peakpos[2] - peakpos[1]) <= 1.0

Check warning on line 92 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L92

Added line #L92 was not covered by tests
# Adjust σ and recheck peaks
if peakfinder_σ < 10.0
println("Increasing peakfinder_σ: ", peakfinder_σ)
peakfinder_σ += 0.5

Check warning on line 96 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L94-L96

Added lines #L94 - L96 were not covered by tests
else
# If σ can't increase further, reduce threshold
println("Adjusting peakfinder_threshold: ", peakfinder_threshold)

Check warning on line 99 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L99

Added line #L99 was not covered by tests
peakfinder_threshold -= 1.0

# Safety check to avoid threshold becoming too low
if peakfinder_threshold < 3.0
error("Unable to find more than one peak within reasonable quantile range.")
end

# Reset σ to its initial value after adjusting threshold
peakfinder_σ = 1.0
end

# Safety check to avoid infinite loops
if peakfinder_σ >= 5.0 && peakfinder_threshold < 3.0
error("Unable to find more than one peak within reasonable quantile range.")
end
end
peakfinder_σ = 2.0 # Reset σ for new threshold

Check warning on line 101 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L101

Added line #L101 was not covered by tests

# helper function
function is_within_range(x0, x_list, range=0.5)
for x in x_list
if abs(x - x0) <= range
return true
# Safety check to avoid lowering threshold too much
if peakfinder_threshold < 2.0
error("Unable to find two distinct peaks within reasonable quantile range.")

Check warning on line 105 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L104-L105

Added lines #L104 - L105 were not covered by tests
end
end
return false
end

# If more than two peaks are found, reduce max_quantile to find exactly two peaks
if num_peaks > 2
println("Found more than 2 peaks. Reducing max range. Currently: quantile $max_quantile")

while num_peaks != 2
max_quantile -= 0.01
max_amp = quantile(amps, max_quantile)

h_uncal = fit(Histogram, amps, min_amp:bin_width:max_amp)
h_decon, peakpos = peakfinder(h_uncal, σ=peakfinder_σ, backgroundRemove=true, threshold=peakfinder_threshold)

num_peaks = length(peakpos)
# Find peaks with updated parameters
h_decon, peakpos = peakfinder(h_uncal, σ=peakfinder_σ, backgroundRemove=true, threshold=peakfinder_threshold)
filter!(x -> x >= first_pe_peak_pos, peakpos)
num_peaks = length(peakpos)

Check warning on line 112 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L110-L112

Added lines #L110 - L112 were not covered by tests

# Safety check to avoid infinite loops
if max_quantile <= 0.5
error("Unable to find exactly two peaks within reasonable quantile range.")
end
# Safety check to avoid infinite loops
if peakfinder_σ >= 10.0 && peakfinder_threshold < 2.0
error("Unable to find two peaks within reasonable quantile range.")

Check warning on line 116 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L115-L116

Added lines #L115 - L116 were not covered by tests
end
end

return h_uncal, peakpos
return h_decon, peakpos

Check warning on line 120 in src/sipm_simple_calibration.jl

View check run for this annotation

Codecov / codecov/patch

src/sipm_simple_calibration.jl#L120

Added line #L120 was not covered by tests
end

0 comments on commit 275fd17

Please sign in to comment.