Skip to content

Commit

Permalink
Merge pull request #88 from legend-exp/sipm-cal
Browse files Browse the repository at this point in the history
Sipm calibration routines
  • Loading branch information
theHenks authored Nov 11, 2024
2 parents 7b38a30 + f9036f6 commit 143524e
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 45 deletions.
6 changes: 5 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
Format = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
GaussianMixtures = "cc18c42c-b769-54ff-9e2a-b28141a64aae"
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112"
IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6"
KernelDensity = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b"
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
LinearRegression = "92481ed7-9fb7-40fd-80f2-46fd0f076581"
LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
LsqFit = "2fda8390-95c7-5789-9bda-21331edee243"
Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7"
Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
Expand All @@ -43,8 +45,8 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
ValueShapes = "136a8f8c-c49b-4edb-8b98-f3d64d48be8f"

[weakdeps]
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"

[extensions]
LegendSpecFitsRecipesBaseExt = ["RecipesBase", "Plots"]
Expand All @@ -59,11 +61,13 @@ Distributions = "0.24, 0.25"
FillArrays = "0.7, 0.8, 0.9, 0.10, 0.11, 0.12, 0.13, 1"
Format = "1.2, 1.3"
ForwardDiff = "0.10"
GaussianMixtures = "0.3"
IntervalSets = "0.7"
InverseFunctions = "0.1"
IrrationalConstants = "0.1, 0.2"
KernelDensity = "0.5, 0.6"
LaTeXStrings = "1.3"
LogExpFunctions = "0.3"
LinearAlgebra = "1"
LinearRegression = "0.2"
LsqFit = "0.14, 0.15"
Expand Down
231 changes: 209 additions & 22 deletions ext/LegendSpecFitsRecipesBaseExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,198 @@ end
end
end

@recipe function f(report::NamedTuple{(:peakpos, :peakpos_cal, :h_uncal, :h_calsimple)}; cal=true)
legend := :topright
size := (1000, 600)
thickness_scaling := 1.5
framestyle := :box
yformatter := :plain
if cal
h = LinearAlgebra.normalize(report.h_calsimple, mode = :density)
xlabel := "Peak Amplitudes (P.E.)"
ylabel := "Counts / $(round_wo_units(step(first(h.edges)), digits=2)) P.E."
xticks := (0:0.5:last(first(h.edges)))
pps = report.peakpos_cal
else
h = LinearAlgebra.normalize(report.h_uncal, mode = :density)
xlabel := "Peak Amplitudes (ADC)"
ylabel := "Counts / $(round_wo_units(step(first(h.edges)), digits=2)) ADC"
pps = report.peakpos
end
xlims := (0, last(first(h.edges)))
min_y = minimum(h.weights) == 0.0 ? 1e-3*maximum(h.weights) : 0.8*minimum(h.weights)
ylims --> (min_y, maximum(h.weights)*1.1)
@series begin
seriestype := :stepbins
label := "amps"
h
end
y_vline = min_y:1:maximum(h.weights)*1.1
for (i, p) in enumerate(pps)
@series begin
seriestype := :line
if i == 1
label := "Peak Pos. Guess"
else
label := ""
end
color := :red
linewidth := 1.5
fill(p, length(y_vline)), y_vline
end
end
end

@recipe function f(report_sipm::NamedTuple{(:h_cal, :f_fit, :f_fit_components, :min_pe, :max_pe, :bin_width, :n_mixtures, :n_pos_mixtures, :peaks, :positions, :μ , :gof)}; xerrscaling=1, show_residuals=true, show_peaks=true, show_components=false)
legend := :topright
size := (1000, 600)
margins := (4, :mm)
thickness_scaling := 1.5
framestyle := :box
yformatter := :plain
foreground_color_legend := :silver
background_color_legend := :white
ylabel := "Counts / $(round_wo_units(report_sipm.bin_width * 1e3, digits=2))E-3 P.E."
xlims := (first(first(report_sipm.h_cal.edges)), last(first(report_sipm.h_cal.edges)))
xticks := (ceil(first(first(report_sipm.h_cal.edges)))-0.5:0.5:last(first(report_sipm.h_cal.edges)))
min_y = minimum(report_sipm.h_cal.weights) == 0.0 ? 1e-3*maximum(report_sipm.h_cal.weights) : 0.8*minimum(report_sipm.h_cal.weights)
ylims := (min_y, maximum(report_sipm.h_cal.weights)*1.1)
bin_centers = collect(report_sipm.h_cal.edges[1])[1:end-1] .+ diff(collect(report_sipm.h_cal.edges[1]))[1]/2
@series begin
yscale --> :log10
label := "Amplitudes"
subplot --> 1
seriestype := :bar
alpha --> 1.0
fillalpha --> 0.85
fillcolor --> :lightgrey
linecolor --> :lightgrey
fillrange := 1e-1
bins --> :sqrt
bar_width := diff(report_sipm.h_cal.edges[1])[1]
bin_centers, report_sipm.h_cal.weights
end
@series begin
seriestype := :line
if !isempty(report_sipm.gof)
label := "Best Fit (p = $(round(report_sipm.gof.pvalue, digits=2)))"
else
label := "Best Fit"
end
if show_residuals && !isempty(report_sipm.gof)
xlabel := ""
xticks := []
else
xlabel := "Peak Amplitudes (P.E.)"
end
subplot --> 1
color := :black
linewidth := 1.5
report_sipm.min_pe:report_sipm.bin_width/100:report_sipm.max_pe, report_sipm.f_fit
end
if show_components
for (i, μ) in enumerate(report_sipm.μ)
@series begin
seriestype := :line
if i == 1
label := "Mixture Components"
else
label := ""
end
if show_residuals && !isempty(report_sipm.gof)
xlabel := ""
xticks := []
else
xlabel := "Peak Amplitudes (P.E.)"
end
subplot --> 1
color := i + 1 + length(report_sipm.positions)
linestyle := :dash
linewidth := 1.3
# fillalpha := 1
alpha := 0.4
xi = report_sipm.min_pe:report_sipm.bin_width/100:report_sipm.max_pe
yi = Base.Fix2(report_sipm.f_fit_components, i).(xi)
# ribbon := (yi .- 1, zeros(length(xi)))
xi, yi
end
end
end
if show_peaks
y_vline = [min_y, maximum(report_sipm.h_cal.weights)*1.1]
for (i, p) in enumerate(report_sipm.positions)
@series begin
seriestype := :line
if xerrscaling == 1
label := "$(report_sipm.peaks[i]) P.E. [$(report_sipm.n_pos_mixtures[i]) Mix.]"
else
label := "$(report_sipm.peaks[i]) P.E. [$(report_sipm.n_pos_mixtures[i]) Mix.] (error x$xerrscaling)"
end
subplot --> 1
color := i + 1
linewidth := 1.5
fill(value(p), length(y_vline)), y_vline
end
@series begin
seriestype := :vspan
label := ""
fillalpha := 0.1
subplot --> 1
if show_residuals && !isempty(report_sipm.gof)
xlabel := ""
xticks := []
else
xlabel := "Peak Amplitudes (P.E.)"
end
color := i + 1
[value(p) - xerrscaling * uncertainty(p), value(p) + xerrscaling * uncertainty(p)]
end
end
end
if show_residuals && !isempty(report_sipm.gof)
link --> :x
layout --> @layout([a{0.7h}; b{0.3h}])
@series begin
seriestype := :hline
ribbon := 3
subplot --> 2
fillalpha := 0.5
label := ""
fillcolor := :lightgrey
linecolor := :darkgrey
[0.0]
end
@series begin
seriestype := :hline
ribbon := 1
subplot --> 2
fillalpha := 0.5
label := ""
fillcolor := :grey
linecolor := :darkgrey
[0.0]
end
@series begin
seriestype := :scatter
subplot --> 2
label := ""
title := ""
markercolor --> :darkgrey
markersize --> 3.0
markerstrokewidth := 0.1
ylabel := "Residuals (σ)"
xlabel := "Peak Amplitudes (P.E.)"
link --> :x
top_margin --> (-8, :mm)
ylims := (-6, 6)
xlims := (first(first(report_sipm.h_cal.edges)), last(first(report_sipm.h_cal.edges)))
yscale --> :identity
yticks := ([-3, 0, 3])
report_sipm.gof.bin_centers, [ifelse(abs(r) < 1e-6, 0.0, r) for r in report_sipm.gof.residuals_norm]
end
end
end

@recipe function f(report_ctc::NamedTuple{(:peak, :window, :fct, :bin_width, :bin_width_qdrift, :e_peak, :e_ctc, :qdrift_peak, :h_before, :h_after, :fwhm_before, :fwhm_after, :report_before, :report_after)})
if !("StatsPlots" in string.(Base.loaded_modules_array()))
throw(ErrorException("StatsPlots not loaded. Please load StatsPlots before using this recipe."))
Expand Down Expand Up @@ -608,9 +800,9 @@ end
framestyle := :box
xformatter := :plain
yformatter := :plain
margins := (0, :mm)
if !isempty(report.gof)
layout --> @layout([a{0.8h}; b{0.2h}])
margins --> (-11.5, :mm)
link --> :x
end
@series begin
Expand Down Expand Up @@ -643,6 +835,10 @@ end
label := "Data (x-Error x$(xerrscaling), y-Error x$(yerrscaling))"
end
markercolor --> :black
if !isempty(report.gof)
xguide := ""
xticks := []
end
xerror := uncertainty.(report.x) .* xerrscaling
yerror := uncertainty.(report.y) .* yerrscaling
value.(report.x), value.(report.y)
Expand All @@ -663,6 +859,10 @@ end
ms --> 3
markershape --> :circle
markerstrokecolor --> :black
if !isempty(report.gof)
xguide := ""
xticks := []
end
linewidth --> 0.5
markercolor --> :silver
xerror := uncertainty.(additional_pts.x) .* xerrscaling
Expand Down Expand Up @@ -709,7 +909,8 @@ end
subplot --> 2
label --> ""
markercolor --> :black
ylabel --> "Residuals (σ)"
yguide := "Residuals (σ)"
top_margin --> (-4, :mm)
ylims --> (-5, 5)
yticks --> ([-3, 0, 3])
value.(report.x), report.gof.residuals_norm
Expand All @@ -729,14 +930,14 @@ end
else
NamedTuple()
end
xlabel := "Energy (keV)"
legend := :topleft
framestyle := :box
xlims := (0, 3000)
xticks := (0:500:3000, ["$i" for i in 0:500:3000])
@series begin
grid --> :all
xerrscaling --> xerrscaling
xlabel := "Energy (keV)"
yerrscaling --> yerrscaling
additional_pts --> additional_pts
(par = report.par, f_fit = report.f_fit, x = report.x, y = report.y, gof = get(report, :gof, NamedTuple()))
Expand All @@ -762,36 +963,22 @@ end
if report.type == :cal
additional_pts = if !isempty(additional_pts)
μ_cal = report.f_fit.(additional_pts.μ) .* report.e_unit
(x = additional_pts.μ, y = additional_pts.peaks,
residuals_norm = (value.(μ_cal) .- additional_pts.peaks)./ uncertainty.(μ_cal))
(x = additional_pts.μ, y = ustrip.(report.e_unit, additional_pts.peaks),
residuals_norm = (value.(μ_cal) .- additional_pts.peaks) ./ uncertainty.(μ_cal))
else
NamedTuple()
end
xlabel := "Energy (ADC)"
legend := :bottomright
framestyle := :box
xlims := (0, 168000)
xticks := (0:16000:176000)
xlims := (0, 1.1*maximum(value.(report.x)))
@series begin
xlabel := "Energy (ADC)"
ylabel := "Energy ($(report.e_unit))"
grid --> :all
xerrscaling --> xerrscaling
yerrscaling --> yerrscaling
additional_pts := additional_pts
(par = report.par, f_fit = report.f_fit, x = report.x, y = report.y, gof = report.gof)
end
@series begin
seriestype := :hline
label := L"Q_{\beta \beta}"
color := :green
fillalpha := 0.2
linewidth := 2.5
xticks := :none
ylabel := "Energy ($(report.e_unit))"
ylims := (0, 1.2*value(maximum(report.y)))
yticks := (500:500:3000)
subplot := 1
[2039]
end
end
end

Expand Down
5 changes: 5 additions & 0 deletions src/LegendSpecFits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ using Random
using ArgCheck
using ArraysOfArrays
using BAT
using DensityInterface
using Distributions
using FillArrays
using Format
using ForwardDiff
using GaussianMixtures
using IntervalSets
using InverseFunctions
using IrrationalConstants
using LogExpFunctions
using LsqFit
using Measurements
using Measurements: value
Expand All @@ -31,6 +34,7 @@ using OptimizationNLopt
using OptimizationOptimJL
using PropDicts
using RadiationSpectra
using RadiationSpectra: peakfinder
using Roots
using SpecialFunctions
using StatsBase
Expand Down Expand Up @@ -75,6 +79,7 @@ include("pseudo_prior.jl")
include("specfit_functions.jl")
include("calfunc.jl")
include("sipm_simple_calibration.jl")
include("sipmfit.jl")
abstract type UncertTag end
ForwardDiff.:()(::Type{<:ForwardDiff.Tag}, ::Type{UncertTag}) = true
ForwardDiff.:()(::Type{UncertTag}, ::Type{<:ForwardDiff.Tag}) = false
Expand Down
2 changes: 1 addition & 1 deletion src/fit_calibration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function fit_calibration(pol_order::Int, µ::AbstractVector{<:Union{Unitful.Real
@assert length(peaks) == length(μ) "Number of calibration points does not match the number of energies"
@assert pol_order >= 1 "The polynomial order must be greater than 0"

e_unit = u"keV"
e_unit = unit(first(peaks))
# make all inputs unitless with the dimension e_unit
μ_nounit = if !Unitful.isunitless(unit(first(μ)))
@warn "µ has a unit, it will be converted to $(e_unit) and stripped."
Expand Down
Loading

0 comments on commit 143524e

Please sign in to comment.