diff --git a/Project.toml b/Project.toml index 25d4106c..eecef388 100644 --- a/Project.toml +++ b/Project.toml @@ -15,15 +15,18 @@ ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" 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" LsqFit = "2fda8390-95c7-5789-9bda-21331edee243" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +Optim = "429524aa-4258-5aef-a3af-852621145aeb" Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba" OptimizationBBO = "3e6eede4-6085-4f62-9a71-46d9bc1eb92b" OptimizationNLopt = "4e6fcdb7-1186-4e1f-a706-475e75c168bb" +OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e" PropDicts = "4dc08600-4268-439e-8673-d706fafbb426" RadiationSpectra = "4f207c7e-01da-51d7-a1a0-c8c06dd1d883" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -59,16 +62,19 @@ ForwardDiff = "0.10" IntervalSets = "0.7" InverseFunctions = "0.1" IrrationalConstants = "0.1, 0.2" +KernelDensity = "0.5, 0.6" LaTeXStrings = "1.3" LinearAlgebra = "1" LinearRegression = "0.2" LsqFit = "0.14, 0.15" Measurements = "2" Measures = "0.3" +Optim = "1" Optimization = "3, 4" OptimizationBBO = "0.3" OptimizationNLopt = "0.2" Plots = "<0.0.1, 1" +OptimizationOptimJL = "0.3" PropDicts = "0.2" RadiationSpectra = "0.5.11" Random = "1" diff --git a/ext/LegendSpecFitsRecipesBaseExt.jl b/ext/LegendSpecFitsRecipesBaseExt.jl index 8bb2d1eb..f395aed5 100644 --- a/ext/LegendSpecFitsRecipesBaseExt.jl +++ b/ext/LegendSpecFitsRecipesBaseExt.jl @@ -7,7 +7,7 @@ import Plots using Unitful, Format, Measurements, LaTeXStrings using Measurements: value, uncertainty using StatsBase, LinearAlgebra - +using KernelDensity function round_wo_units(x::Unitful.RealOrRealQuantity; digits::Integer=2) if unit(x) == NoUnits round(x, digits=digits) @@ -462,72 +462,74 @@ 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)}) - layout := (1, 3) - thickness_scaling := 1.0 + if !("StatsPlots" in string.(Base.loaded_modules_array())) + throw(ErrorException("StatsPlots not loaded. Please load StatsPlots before using this recipe.")) + end + layout := (2, 1) + size := (1000, 1000) + framestyle := :semi + grid := false + left_margin --> (5, :mm) + right_margin --> (5, :mm) + bottom_margin := (-4, :mm) + margins --> (0, :mm) + link --> :x + foreground_color_legend := :silver + background_color_legend := :white xtickfontsize := 12 xlabelfontsize := 14 ylabelfontsize := 14 ytickfontsize := 12 - legendfontsize := 10 - size := (1000, 300) - margin := (8, :mm) + legendfontsize := 12 + xl = (first(report_ctc.h_before.edges[1]), last(report_ctc.h_before.edges[1])) @series begin - seriestype := :histogram2d - bins := (ustrip.(unit(first(report_ctc.e_peak)), minimum(report_ctc.e_peak):report_ctc.bin_width:maximum(report_ctc.e_peak)), quantile(report_ctc.qdrift_peak, 0.01):report_ctc.bin_width_qdrift:quantile(report_ctc.qdrift_peak, 0.99)) - color := :inferno - xlabel := "Energy" - ylabel := "QDrift" - title := "Before Correction" - titlelocation := (0.5, 1.1) - xlims := (2600, 2630) - ylims := (0, quantile(report_ctc.qdrift_peak, 0.99)) - yformatter := :plain - legend := :none - colorbar_scale := :log10 + seriestype := :stepbins + fill := true + color := :darkgrey + label := "Before correction" + legend := :topleft subplot := 1 - report_ctc.e_peak, report_ctc.qdrift_peak + ylims := (0, :auto) + report_ctc.h_before end @series begin - seriestype := :histogram2d - bins := (ustrip.(unit(first(report_ctc.e_peak)), minimum(report_ctc.e_peak):report_ctc.bin_width:maximum(report_ctc.e_peak)), quantile(report_ctc.qdrift_peak, 0.01):report_ctc.bin_width_qdrift:quantile(report_ctc.qdrift_peak, 0.99)) - color := :magma - xlabel := "Energy" - ylabel := "QDrift" - title := "After Correction" - xlims := (2600, 2630) - titlelocation := (0.5, 1.1) - xlims := (2600, 2630) - ylims := (0, quantile(report_ctc.qdrift_peak, 0.99)) - yformatter := :plain - legend := :none - colorbar_scale := :log10 - subplot := 3 - report_ctc.e_ctc, report_ctc.qdrift_peak + seriestype := :stepbins + fill := true + alpha := 0.5 + color := :purple + label := "After correction" + legend := :topleft + subplot := 1 + ylims := (0, :auto) + xlims := xl + xlabel := "" + xticks := ([], []) + ylabel := "Counts / $(round_wo_units(report_ctc.bin_width, digits=2))" + report_ctc.h_after end @series begin - seriestype := :stepbins - color := :red - label := "Before CTC" - xlabel := "Energy (keV)" - ylabel := "Counts" - yscale := :log10 + seriestype := :line subplot := 2 - report_ctc.h_before + c := :binary + colorbar := :none + fill := true + label := "Before correction" + kde((ustrip(report_ctc.e_peak), report_ctc.qdrift_peak ./ maximum(report_ctc.qdrift_peak))) end @series begin - seriestype := :stepbins - color := :green - label := "After CTC" - xlabel := "Energy (keV)" - ylabel := "Counts" - title := "FWHM $(round(u"keV", report_ctc.fwhm_after, digits=2))" - titlelocation := (0.5, 1.1) - xlims := (2600, 2630) - xticks := (2600:10:2630) - legend := :bottomright - yscale := :log10 + seriestype := :line subplot := 2 - report_ctc.h_after + c := :plasma + colorbar := :none + fill := false + label := "After correction" + xlims := xl + ylims := (0, 1) + yticks := 0.1:0.1:0.9 + yformatter := :plain + xlabel := "Energy ($(unit(report_ctc.peak)))" + ylabel := "Eff. Drift time (a.u.)" + kde((ustrip(report_ctc.e_ctc), report_ctc.qdrift_peak ./ maximum(report_ctc.qdrift_peak))) end end diff --git a/src/LegendSpecFits.jl b/src/LegendSpecFits.jl index 398096c6..278042e6 100644 --- a/src/LegendSpecFits.jl +++ b/src/LegendSpecFits.jl @@ -25,9 +25,11 @@ using LsqFit using Measurements using Measurements: value as mvalue using Measurements: uncertainty as muncert +using Optim using Optimization using OptimizationBBO using OptimizationNLopt +using OptimizationOptimJL using PropDicts using RadiationSpectra using Roots diff --git a/src/aoefit_combined.jl b/src/aoefit_combined.jl index debd8c75..06bea9ed 100644 --- a/src/aoefit_combined.jl +++ b/src/aoefit_combined.jl @@ -105,7 +105,7 @@ function fit_single_aoe_compton_with_fixed_μ_and_σ(h::Histogram, μ::Number, end # This function calculates the same thing as fit_single_aoe_compton_with_fixed_μ_and_σ, but just returns the value of the negative log-likelihood -function neg_log_likelihood_single_aoe_compton_with_fixed_μ_and_σ(h::Histogram, μ::Number, σ::Number, ps::NamedTuple; fit_func::Symbol = :f_fit, background_center::Union{Real,Nothing} = μ) +function neg_log_likelihood_single_aoe_compton_with_fixed_μ_and_σ(h::Histogram, μ::Real, σ::Real, ps::NamedTuple; fit_func::Symbol = :f_fit, background_center::Union{Real,Nothing} = μ, optimize::Bool=true) # create pseudo priors pseudo_prior = get_aoe_pseudo_prior(h, ps, fit_func; @@ -126,14 +126,18 @@ function neg_log_likelihood_single_aoe_compton_with_fixed_μ_and_σ(h::Histogram end # MLE - optf = OptimizationFunction((u, p) -> ((-) ∘ f_loglike ∘ inverse(f_trafo))(u), AutoForwardDiff()) - optpro = OptimizationProblem(optf, v_init, []) - res = solve(optpro, Optimization.LBFGS(), maxiters = 3000, maxtime=optim_time_limit) + if optimize + optf = OptimizationFunction((u, p) -> ((-) ∘ f_loglike ∘ inverse(f_trafo))(u), AutoForwardDiff()) + optpro = OptimizationProblem(optf, v_init, []) + res = solve(optpro, Optimization.LBFGS(), maxiters = 3000, maxtime=optim_time_limit) - converged = (res.retcode == ReturnCode.Success) - if !converged @warn "Fit did not converge" end + converged = (res.retcode == ReturnCode.Success) + if !converged @warn "Fit did not converge" end - return res.objective + return res.objective + else + return f_loglike(merge((μ = μ, σ = σ), ps)) + end end """ @@ -236,6 +240,19 @@ function fit_aoe_compton_combined(peakhists::Vector{<:Histogram}, peakstats::Str if uncertainty && converged + # create loglikehood function for single hist + function _get_neg_log_likehood(e::Real, h::Histogram, vi::NamedTuple) + # get fit function with background center + fit_function = get_aoe_fit_functions(; background_center = f_aoe_mu(e, (v_ml.μA, v_ml.μB)))[fit_func] + + # create loglikehood function + f_loglike = let f_fit=fit_function, h=h + v -> hist_loglike(x -> x in Interval(extrema(h.edges[1])...) ? f_fit(x, v) : 0, h) + end + - f_loglike(vi) + end + + # create full loglikehood function f_loglike_array = array -> begin pars = array_to_tuple(array, v_ml) @@ -246,18 +263,19 @@ function fit_aoe_compton_combined(peakhists::Vector{<:Histogram}, peakstats::Str # get histogram and peakstats h = peakhists[i] - ps = peakstats[i] + vi = v_results[i] e = ustrip(compton_bands[i]) μ = f_aoe_mu(e, (pars.μA, pars.μB)) σ = f_aoe_sigma(e, (pars.σA, pars.σB)) + + neg_log_likelihoods[i] = _get_neg_log_likehood(e, h, merge(mvalue(vi), (μ = μ, σ = σ))) # fit peak - try - neg_log_likelihoods[i] = neg_log_likelihood_single_aoe_compton_with_fixed_μ_and_σ(h, μ, σ, ps; fit_func=fit_func) - catch e - @warn "Error fitting band $(compton_bands[i]): $e" - continue - end + # try + # catch e + # @warn "Error fitting band $(compton_bands[i]): $e" + # continue + # end end return sum(neg_log_likelihoods) end @@ -294,7 +312,7 @@ function fit_aoe_compton_combined(peakhists::Vector{<:Histogram}, peakstats::Str @debug "σB: $(v_ml.σB) ± $(v_ml_err.σB)" result = merge(NamedTuple{keys(v_ml)}([measurement(v_ml[k], v_ml_err[k]) for k in keys(v_ml)]...), - (gof = (pvalue = pval, chi2 = chi2, dof = dof, covmat = param_covariance, + (gof = (converged = converged, pvalue = pval, chi2 = chi2, dof = dof, covmat = param_covariance, residuals = residuals, residuals_norm = residuals_norm),) #, bin_centers = bin_centers),) ) else @@ -304,7 +322,7 @@ function fit_aoe_compton_combined(peakhists::Vector{<:Histogram}, peakstats::Str @debug "σA: $(v_ml.σA)" @debug "σB: $(v_ml.σB)" - result = merge(v_ml, ) + result = merge(v_ml, (gof = (converged = converged, ), )) end # Add same fields to result as fit_aoe_corrections