-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generic simple_calibration #108
base: main
Are you sure you want to change the base?
Changes from 5 commits
a0b628f
fa21052
1639f38
888996b
421e2c3
692c1de
b8dda3f
8e924d9
13a85f9
4851690
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -490,6 +490,41 @@ | |
end | ||
end | ||
|
||
@recipe function f(report::NamedTuple{(:h_calsimple, :h_uncal, :c, :peak_guess, :peakhists, :peakstats)}; cal=true) | ||
ylabel := "Counts" | ||
legend := :topright | ||
yscale := :log10 | ||
fill := false | ||
if cal | ||
h = LinearAlgebra.normalize(report.h_calsimple, mode = :density) | ||
xlabel := "Energy (keV)" | ||
xlims := (0, 3000) | ||
xticks := (0:500:3000, ["$i" for i in 0:500:3000]) | ||
ylims := (0.2, maximum(h.weights)*1.1) | ||
peak_guess = ustrip(report.c * report.peak_guess) | ||
else | ||
h = LinearAlgebra.normalize(report.h_uncal, mode = :density) | ||
xlabel := "Energy (ADC)" | ||
xlims := (0, 1.2*report.peak_guess) | ||
xticks := (0:3000:1.2*report.peak_guess, ["$i" for i in 0:3000:1.2*report.peak_guess]) | ||
ylims := (0.2, maximum(h.weights)*1.1) | ||
peak_guess = report.peak_guess | ||
end | ||
@series begin | ||
seriestype := :stepbins | ||
label := "Energy" | ||
h | ||
end | ||
y_vline = 0.2:1:maximum(h.weights)*1.1 | ||
@series begin | ||
seriestype := :line | ||
label := "Peak guess"#: $(round(peak_guess, digits = 1))" | ||
color := :red2 | ||
linewidth := 1.5 | ||
fill(peak_guess, length(y_vline)), y_vline | ||
end | ||
end | ||
|
||
@recipe function f(report::NamedTuple{(:peakpos, :peakpos_cal, :h_uncal, :h_calsimple)}; cal=true) | ||
legend := :topright | ||
size := (1000, 600) | ||
|
@@ -820,7 +855,8 @@ | |
if plot_ribbon | ||
ribbon := uncertainty.(report.f_fit.(0:1:1.2*value(maximum(report.x)))) | ||
end | ||
0:1:1.2*value(maximum(report.x)), value.(report.f_fit.(0:1:1.2*value(maximum(report.x)))) | ||
xstep = value(maximum(report.x))/100 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this also work for the |
||
0:xstep:1.2*value(maximum(report.x)), value.(report.f_fit.(0:xstep:1.2*value(maximum(report.x)))) | ||
end | ||
@series begin | ||
seriestype := :scatter | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,23 +18,25 @@ | |
function simple_calibration end | ||
export simple_calibration | ||
|
||
function simple_calibration(e_uncal::Vector{<:Real}, th228_lines::Vector{<:Unitful.Energy{<:Real}}, window_sizes::Vector{<:Tuple{Unitful.Energy{<:Real}, Unitful.Energy{<:Real}}},; kwargs...) | ||
function simple_calibration(e_uncal::Vector{<:Real}, gamma_lines::Vector{<:Unitful.Energy{<:Real}}, window_sizes::Vector{<:Tuple{Unitful.Energy{<:Real}, Unitful.Energy{<:Real}}},; kwargs...) | ||
# remove calib type from kwargs | ||
@assert haskey(kwargs, :calib_type) "Calibration type not specified" | ||
calib_type = kwargs[:calib_type] | ||
# remove :calib_type from kwargs | ||
kwargs = pairs(NamedTuple(filter(k -> !(:calib_type in k), kwargs))) | ||
if calib_type == :th228 | ||
@debug "Use simple calibration for Th228 lines" | ||
return simple_calibration_th228(e_uncal, th228_lines, window_sizes,; kwargs...) | ||
return simple_calibration_th228(e_uncal, gamma_lines, window_sizes,; kwargs...) | ||
elseif calib_type == :gamma | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This works also for any calibration data. I think we should have only 1 generic |
||
return simple_calibration_gamma(e_uncal, gamma_lines, window_sizes,; kwargs...) | ||
else | ||
error("Calibration type not supported") | ||
end | ||
end | ||
simple_calibration(e_uncal::Vector{<:Real}, th228_lines::Vector{<:Unitful.Energy{<:Real}}, left_window_sizes::Vector{<:Unitful.Energy{<:Real}}, right_window_sizes::Vector{<:Unitful.Energy{<:Real}}; kwargs...) = simple_calibration(e_uncal, th228_lines, [(l,r) for (l,r) in zip(left_window_sizes, right_window_sizes)],; kwargs...) | ||
simple_calibration(e_uncal::Vector{<:Real}, gamma_lines::Vector{<:Unitful.Energy{<:Real}}, left_window_sizes::Vector{<:Unitful.Energy{<:Real}}, right_window_sizes::Vector{<:Unitful.Energy{<:Real}}; kwargs...) = simple_calibration(e_uncal, gamma_lines, [(l,r) for (l,r) in zip(left_window_sizes, right_window_sizes)],; kwargs...) | ||
|
||
|
||
function simple_calibration_th228(e_uncal::Vector{<:Real}, th228_lines::Vector{<:Unitful.Energy{<:Real}}, window_sizes::Vector{<:Tuple{Unitful.Energy{<:Real}, Unitful.Energy{<:Real}}},; n_bins::Int=15000, quantile_perc::Float64=NaN, binning_peak_window::Unitful.Energy{<:Real}=10.0u"keV") | ||
function simple_calibration_th228(e_uncal::Vector{<:Real}, th228_lines::Vector{<:Unitful.Energy{<:Real}}, window_sizes::Vector{<:Tuple{Unitful.Energy{<:Real}, Unitful.Energy{<:Real}}},; n_bins::Int=15000, quantile_perc::Float64=NaN, binning_peak_window::Unitful.Energy{<:Real}=10.0u"keV", e_unit::Unitful.EnergyUnits=u"keV") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I always had the unit and therefore the scale fixed in the code since for some of the values I assumed that everything from there on is in |
||
# initial binning | ||
bin_width = get_friedman_diaconis_bin_width(filter(in(quantile(e_uncal, 0.05)..quantile(e_uncal, 0.5)), e_uncal)) | ||
# create initial peak search histogram | ||
|
@@ -55,7 +57,6 @@ | |
# get calibration constant for simple calibration | ||
c = 2614.5*u"keV" / fep_guess | ||
e_simple = e_uncal .* c | ||
e_unit = u"keV" | ||
# get peakhists and peakstats | ||
peakhists, peakstats, h_calsimple, bin_widths = get_peakhists_th228(e_simple, th228_lines, window_sizes; binning_peak_window=binning_peak_window, e_unit=e_unit) | ||
|
||
|
@@ -81,7 +82,68 @@ | |
return result, report | ||
end | ||
|
||
|
||
""" | ||
simple_calibration_gamma(e_uncal::Vector{<:Real}, gamma_lines::Vector{<:Unitful.Energy{<:Real}}, window_sizes::Vector{<:Tuple{Unitful.Energy{<:Real}, Unitful.Energy{<:Real}}},; n_bins::Int=15000, quantile_perc::Float64=NaN, binning_peak_window::Unitful.Energy{<:Real}=10.0u"keV", peak_quantile::ClosedInterval{<:Real} = 0.5..1.0) | ||
Perform a simple calibration for the uncalibrated energy array `e_uncal` for any gamma source | ||
* 1. Run a peak-finding algorithm. Only the energy window that is defined by `peak_quantile` is considered for the peak search. | ||
* 2. Estimate the calibration constant `c` by dividing the energy of the `gamma_lines` by the energy of the most prominent peak from peak-finder | ||
* Return peakstats and peakhists of gamma lines for further calibration | ||
INPUTS: | ||
* `e_uncal`: uncalibrated energy array | ||
* `gamma_lines`: array of gamma lines | ||
* `window_sizes`: array of tuples with left and right window sizes around the gamma lines | ||
KEYWORD ARGUMENTS: | ||
* `n_bins`: number of bins for the histogram (not used, legacy from simple_calibration) | ||
* `quantile_perc`: quantile percentage for the most prominent peak (not used, legacy from simple_calibration) | ||
* `binning_peak_window`: window size for the peak search histogram (default: 10 keV) | ||
* `peak_quantile`: quantile range for the peak search (default: 0.5..1.0) | ||
""" | ||
function simple_calibration_gamma(e_uncal::Vector{<:Real}, gamma_lines::Vector{<:Unitful.Energy{<:Real}}, window_sizes::Vector{<:Tuple{Unitful.Energy{<:Real}, Unitful.Energy{<:Real}}},; n_bins::Int=15000, quantile_perc::Float64=NaN, binning_peak_window::Unitful.Energy{<:Real}=10.0u"keV", peak_quantile::ClosedInterval{<:Real} = 0.5..1.0, e_unit::Unitful.EnergyUnits=u"keV") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe also rename to |
||
e_min = quantile(e_uncal, leftendpoint(peak_quantile)) | ||
e_max = quantile(e_uncal, rightendpoint(peak_quantile)) | ||
# initial binning | ||
bin_width = get_friedman_diaconis_bin_width(filter(in(e_min..e_max), e_uncal)) | ||
# create initial peak search histogram | ||
h_uncal = fit(Histogram, e_uncal, 0:bin_width:maximum(e_uncal)) | ||
peak_guess, peak_idx = if isnan(quantile_perc) | ||
h_peaksearch = fit(Histogram, e_uncal, e_min:bin_width:e_max) | ||
# search all possible peak candidates | ||
h_decon, peakpos = RadiationSpectra.peakfinder(h_peaksearch, σ=2.0, backgroundRemove=true, threshold=10) | ||
# find the most prominent peak in the deconvoluted histogram | ||
peakpos_idxs = StatsBase.binindex.(Ref(h_decon), peakpos) | ||
cts_peakpos = h_decon.weights[peakpos_idxs] | ||
peakpos[argmax(cts_peakpos)], argmax(cts_peakpos) | ||
else | ||
quantile(e_uncal, quantile_perc), length(gamma_lines) | ||
end | ||
@info "Identified most prominent peak (peak $(peak_idx), $(gamma_lines[peak_idx])). Peak guess: $peak_guess" | ||
# get calibration constant for simple calibration | ||
c = gamma_lines[peak_idx] / peak_guess | ||
e_simple = e_uncal .* c | ||
# get peakhists and peakstats | ||
peakhists, peakstats, h_calsimple, bin_widths = get_peakhists_th228(e_simple, gamma_lines, window_sizes; binning_peak_window=binning_peak_window, e_unit=e_unit) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it does. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes sense to rename it, but maybe this could go along then with a PR onto the dataflow where we do a search and replace for these functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. I can take care of this beginning of next year. In that context, I'd suggest to change the julia energy-calibration metadata as well:
OR
I prefer option 1, because this allows you to switch easily back and fourth between different calibration sources. |
||
|
||
result = ( | ||
h_calsimple = h_calsimple, | ||
h_uncal = h_uncal, | ||
c = c, | ||
unit = e_unit, | ||
bin_width = median(bin_widths), | ||
peak_guess = peak_guess, | ||
peakbinwidths = bin_widths, | ||
peakhists = peakhists, | ||
peakstats = peakstats | ||
) | ||
report = ( | ||
h_calsimple = result.h_calsimple, | ||
h_uncal = result.h_uncal, | ||
c = result.c, | ||
peak_guess = result.peak_guess, | ||
peakhists = result.peakhists, | ||
peakstats = result.peakstats | ||
) | ||
return result, report | ||
end | ||
""" | ||
get_peakhists_th228(e::Array, th228_lines::Array, window_sizes::Array, e_unit::String="keV", proxy_binning_peak::Float64=2103.5, proxy_binning_peak_window::Float64=10.0) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why this is a new recipe. Was this not here already before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is almost a duplicate of the existing recipe. However, the old one has some stuff hardcoded, e.g. "FEP" in the plot label. I didn't want to mess with the existing one, since we're int he middle of the processing.