From e64da51dab8336d02030b78dba436819cd53fb00 Mon Sep 17 00:00:00 2001 From: Florian Henkes Date: Thu, 15 Feb 2024 12:09:03 +0100 Subject: [PATCH] Fixed SG filter optimization --- ext/LegendSpecFitsRecipesBaseExt.jl | 22 +++++++++------ src/aoe_calibration.jl | 37 +++++++++++------------- src/cut.jl | 12 ++------ src/filter_optimization.jl | 44 ++++++++++++++++++++--------- src/qc.jl | 4 +-- src/specfit.jl | 21 +++++++++----- src/utils.jl | 7 ++++- 7 files changed, 86 insertions(+), 61 deletions(-) diff --git a/ext/LegendSpecFitsRecipesBaseExt.jl b/ext/LegendSpecFitsRecipesBaseExt.jl index e1e926a8..dfdf6eea 100644 --- a/ext/LegendSpecFitsRecipesBaseExt.jl +++ b/ext/LegendSpecFitsRecipesBaseExt.jl @@ -74,26 +74,32 @@ end end end -@recipe function f(report:: NamedTuple{(:wl, :min_sf, :min_sf_err, :a_grid_wl_sg, :sfs, :sfs_err)}) - xlabel := "Window Length (ns)" - ylabel := "SEP Surrival Fraction (%)" +@recipe function f(report:: NamedTuple{(:wl, :min_sf, :a_grid_wl_sg, :sfs)}) + xlabel := "Window Length ($(unit(first(report.a_grid_wl_sg))))" + ylabel := "SEP Surrival Fraction ($(unit(first(report.sfs))))" grid := :true gridcolor := :black gridalpha := 0.2 gridlinewidth := 0.5 - ylims := (0, 30) + # ylims := (0, 30) @series begin seriestype := :scatter label := "SF" - yerror --> report.sfs_err - ustrip.(report.a_grid_wl_sg), report.sfs + ustrip.(report.a_grid_wl_sg), ustrip.(report.sfs) end @series begin seriestype := :hline - label := "Min. SF (WT: $(report.wl))" + label := "Min. SF $(report.min_sf) (WT: $(report.wl))" color := :red linewidth := 2.5 - [report.min_sf] + [ustrip(Measurements.value(report.min_sf))] + end + @series begin + seriestype := :hspan + label := "" + color := :red + alpha := 0.1 + ustrip.([Measurements.value(report.min_sf)-Measurements.uncertainty(report.min_sf), Measurements.value(report.min_sf)+Measurements.uncertainty(report.min_sf)]) end end diff --git a/src/aoe_calibration.jl b/src/aoe_calibration.jl index a2c9ffc0..84f0f03e 100644 --- a/src/aoe_calibration.jl +++ b/src/aoe_calibration.jl @@ -74,7 +74,7 @@ Prepare an array of uncalibrated DEP energies for parameter extraction and calib - `result`: Result of the initial fit - `report`: Report of the initial fit """ -function prepare_dep_peakhist(e::Array{T}, dep::T,; relative_cut::T=0.5, n_bins_cut::Int=500) where T<:Real +function prepare_dep_peakhist(e::Array{T}, dep::Quantity{T},; relative_cut::T=0.5, n_bins_cut::Int=500, uncertainty::Bool=true) where T<:Real # get cut window around peak cuts = cut_single_peak(e, minimum(e), maximum(e); n_bins=n_bins_cut, relative_cut=relative_cut) # estimate bin width @@ -84,7 +84,7 @@ function prepare_dep_peakhist(e::Array{T}, dep::T,; relative_cut::T=0.5, n_bins_ # get peakstats depstats = estimate_single_peak_stats(dephist) # initial fit for calibration and parameter extraction - result, report = fit_single_peak_th228(dephist, depstats,; uncertainty=true, low_e_tail=false) + result, report = fit_single_peak_th228(dephist, depstats,; uncertainty=uncertainty, low_e_tail=false) # get calibration estimate from peak postion result = merge(result, (m_calib = dep / result.μ, )) return result, report @@ -100,19 +100,14 @@ Get the number of counts after a PSD cut value `psd_cut` for a given `peak` and - `n`: Number of counts after the cut - `n_err`: Uncertainty of the number of counts after the cut """ -function get_n_after_psd_cut(psd_cut::T, aoe::Array{T}, e::Array{T}, peak::T, window::Array{T}, bin_width::T, result_before::NamedTuple, peakstats::NamedTuple; uncertainty::Bool=true, fixed_position::Bool=true) where T<:Real +function get_n_after_psd_cut(psd_cut::Unitful.RealOrRealQuantity, aoe::Vector{<:Unitful.RealOrRealQuantity}, e::Vector{<:T}, peak::T, window::Vector{T}, bin_width::T, result_before::NamedTuple, peakstats::NamedTuple; uncertainty::Bool=true, fixed_position::Bool=true) where T<:Unitful.Energy{<:Real} # get energy after cut and create histogram - peakhist = fit(Histogram, e[aoe .> psd_cut], peak-first(window):bin_width:peak+last(window)) + peakhist = fit(Histogram, ustrip.(e[aoe .> psd_cut]), ustrip(peak-first(window):bin_width:peak+last(window))) # create pseudo_prior with known peak sigma in signal for more stable fit pseudo_prior = NamedTupleDist(σ = Normal(result_before.σ, 0.3), ) # fit peak and return number of signal counts result, _ = fit_single_peak_th228(peakhist, peakstats,; uncertainty=uncertainty, fixed_position=fixed_position, low_e_tail=false, pseudo_prior=pseudo_prior) - if uncertainty - n, n_err = result.n, result.err.n - else - n, n_err = result.n, 0.0 - end - return (n = result.n, n_err = n_err) + return result.n end export get_n_after_psd_cut @@ -128,11 +123,11 @@ The algorhithm utilizes a root search algorithm to find the cut value with a rel - `nsf`: Number of counts after the cut - `err`: Uncertainties """ -function get_psd_cut(aoe::Array{T}, e::Array{T},; dep::T=1592.53, window::Array{T}=[12.0, 10.0], dep_sf::Float64=0.9, cut_search_interval::Tuple{T,T}=(-25.0, 0.0), rtol::T=0.001, bin_width_window::T=3.0, fixed_position::Bool=true, sigma_high_sided::T=NaN) where T<:Real +function get_psd_cut(aoe::Vector{<:Unitful.RealOrRealQuantity}, e::Vector{<:T},; dep::T=1592.53u"keV", window::Vector{T}=[12.0, 10.0]u"keV", dep_sf::Float64=0.9, cut_search_interval::Tuple{Quantity{<:Real}, Quantity{<:Real}}=(-25.0u"keV^-1", 0.0u"keV^-1"), rtol::Float64=0.001, bin_width_window::T=3.0u"keV", fixed_position::Bool=true, sigma_high_sided::Float64=NaN, uncertainty::Bool=true) where T<:Unitful.Energy{<:Real} # estimate bin width bin_width = get_friedman_diaconis_bin_width(e[e .> dep - bin_width_window .&& e .< dep + bin_width_window]) # create histogram - dephist = fit(Histogram, e, dep-first(window):bin_width:dep+last(window)) + dephist = fit(Histogram, ustrip.(e), ustrip(dep-first(window):bin_width:dep+last(window))) # get peakstats depstats = estimate_single_peak_stats(dephist) # cut window around peak @@ -144,16 +139,16 @@ function get_psd_cut(aoe::Array{T}, e::Array{T},; dep::T=1592.53, window::Array{ aoe = aoe[aoe .< sigma_high_sided] end # fit before cut - result_before, _ = fit_single_peak_th228(dephist, depstats,; uncertainty=true, fixed_position=fixed_position, low_e_tail=false) + result_before, _ = fit_single_peak_th228(dephist, depstats,; uncertainty=uncertainty, fixed_position=fixed_position, low_e_tail=false) # get n0 before cut nsf = result_before.n * dep_sf # get psd cut - n_surrival_dep_f = cut -> get_n_after_psd_cut(cut, aoe, e, dep, window, bin_width, result_before, depstats; uncertainty=false, fixed_position=fixed_position).n - nsf + n_surrival_dep_f = cut -> get_n_after_psd_cut(cut, aoe, e, dep, window, bin_width, mvalue(result_before), depstats; uncertainty=false, fixed_position=fixed_position) - nsf psd_cut = find_zero(n_surrival_dep_f, cut_search_interval, Bisection(), rtol=rtol, maxiters=100) # return n_surrival_dep_f.(0.25:0.001:0.5) # get nsf after cut - result_after = get_n_after_psd_cut(psd_cut, aoe, e, dep, window, bin_width, result_before, depstats; uncertainty=true, fixed_position=fixed_position) - return (cut = psd_cut, n0 = result_before.n, nsf = result_after.n, err = (cut = psd_cut * rtol, n0 = result_before.err.n, nsf = result_after.n_err)) + nsf = get_n_after_psd_cut(psd_cut, aoe, e, dep, window, bin_width, mvalue(result_before), depstats; uncertainty=uncertainty, fixed_position=fixed_position) + return (cut = measurement(psd_cut, psd_cut * rtol), n0 = result_before.n, nsf = nsf, sf = nsf / result_before.n * 100*u"percent") end export get_psd_cut @@ -170,11 +165,11 @@ Get the surrival fraction of a peak after a PSD cut value `psd_cut` for a given - `sf`: Surrival fraction - `err`: Uncertainties """ -function get_peak_surrival_fraction(aoe::Array{T}, e::Array{T}, peak::T, window::Array{T}, psd_cut::T,; uncertainty::Bool=true, low_e_tail::Bool=true, bin_width_window::T=2.0, sigma_high_sided::T=NaN) where T<:Real +function get_peak_surrival_fraction(aoe::Vector{<:Unitful.RealOrRealQuantity}, e::Vector{<:T}, peak::T, window::Vector{T}, psd_cut::Unitful.RealOrRealQuantity,; uncertainty::Bool=true, low_e_tail::Bool=true, bin_width_window::T=2.0u"keV", sigma_high_sided::Float64=NaN) where T<:Unitful.Energy{<:Real} # estimate bin width bin_width = get_friedman_diaconis_bin_width(e[e .> peak - bin_width_window .&& e .< peak + bin_width_window]) # get energy before cut and create histogram - peakhist = fit(Histogram, e, peak-first(window):bin_width:peak+last(window)) + peakhist = fit(Histogram, ustrip.(e), ustrip(peak-first(window):bin_width:peak+last(window))) # estimate peak stats peakstats = estimate_single_peak_stats(peakhist) # fit peak and return number of signal counts @@ -189,7 +184,7 @@ function get_peak_surrival_fraction(aoe::Array{T}, e::Array{T}, peak::T, window: # estimate bin width bin_width = get_friedman_diaconis_bin_width(e[e .> peak - bin_width_window .&& e .< peak + bin_width_window]) # get energy after cut and create histogram - peakhist = fit(Histogram, e, peak-first(window):bin_width:peak+last(window)) + peakhist = fit(Histogram, ustrip(e), ustrip(peak-first(window):bin_width:peak+last(window))) # create pseudo_prior with known peak sigma in signal for more stable fit pseudo_prior = NamedTupleDist(μ = ConstValueDist(result_before.μ), σ = Normal(result_before.σ, 0.1)) pseudo_prior = NamedTupleDist(σ = Normal(result_before.σ, 0.1), ) @@ -199,12 +194,12 @@ function get_peak_surrival_fraction(aoe::Array{T}, e::Array{T}, peak::T, window: result_after, report_after = fit_single_peak_th228(peakhist, peakstats,; uncertainty=uncertainty, low_e_tail=low_e_tail) # result_after, report_after = fit_single_peak_th228(peakhist, peakstats,; uncertainty=uncertainty, low_e_tail=low_e_tail, pseudo_prior=pseudo_prior) # calculate surrival fraction - sf = result_after.n / result_before.n + sf = result_after.n / result_before.n * 100.0 * u"percent" result = ( peak = peak, n_before = result_before.n, n_after = result_after.n, - sf = sf * 100*u"percent" + sf = sf ) report = ( peak = result.peak, diff --git a/src/cut.jl b/src/cut.jl index ab426403..3ea1ed26 100644 --- a/src/cut.jl +++ b/src/cut.jl @@ -71,7 +71,7 @@ function get_centered_gaussian_window_cut(x::Vector{T}, min_x::T, max_x::T, n_σ # get bin width bin_width = get_friedman_diaconis_bin_width(x[x .> result_fit.μ - 0.5*result_fit.σ .&& x .< result_fit.μ + 0.5*result_fit.σ]) # prepare histogram - h = fit(Histogram, x, result_fit.μ-5*result_fit.σ:bin_width:result_fit.μ+5*result_fit.σ) + h = fit(Histogram, x, mvalue(result_fit.μ-5*result_fit.σ):mvalue(bin_width):mvalue(result_fit.μ+5*result_fit.σ)) # norm fitted distribution for better plotting # n_fit = length(x[ifelse(left, cuts.low, result_fit.μ) .< x .< ifelse(left, result_fit.μ, cuts.high)]) # n_fit = length(x) @@ -85,18 +85,12 @@ function get_centered_gaussian_window_cut(x::Vector{T}, min_x::T, max_x::T, n_σ σ = result_fit.σ*x_unit, low_cut_fit = ifelse(left, cuts.low, result_fit.μ), high_cut_fit = ifelse(left, result_fit.μ, cuts.high), - max_cut_fit = cuts.max, - err = ( - low_cut = n_σ*result_fit.σ_err*x_unit, - high_cut = n_σ*result_fit.σ_err*x_unit, - center = result_fit.μ_err*x_unit, - σ = result_fit.σ_err*x_unit - ) + max_cut_fit = cuts.max ) report = ( h = LinearAlgebra.normalize(h, mode=:pdf), f_fit = t -> report_fit.f_fit(t), - x_fit = ifelse(left, cuts.low:(result_fit.μ-cuts.low)/1000:result_fit.μ, result_fit.μ:(cuts.high-result_fit.μ)/1000:cuts.high), + x_fit = ifelse(left, cuts.low:mvalue(result_fit.μ-cuts.low)/1000:mvalue(result_fit.μ), mvalue(result_fit.μ):mvalue(cuts.high-result_fit.μ)/1000:cuts.high), low_cut = result.low_cut, high_cut = result.high_cut, low_cut_fit = result.low_cut_fit, diff --git a/src/filter_optimization.jl b/src/filter_optimization.jl index b817bfb8..c8d7334d 100644 --- a/src/filter_optimization.jl +++ b/src/filter_optimization.jl @@ -152,7 +152,20 @@ function fit_fwhm_ft_fep(e_grid::Matrix, e_grid_ft::StepRangeLen{Quantity{<:T}, end export fit_fwhm_ft_fep +""" + fit_sg_wl(dep_sep_data, a_grid_wl_sg, optimization_config) + +Fit the SG window length for the SEP data and return the optimal window length and the corresponding survival fraction. +# Arguments +- `dep_sep_data`: NamedTuple with the DEP and SEP data +- `a_grid_wl_sg`: range of window lengths to sweep through +- `optimization_config`: configuration dictionary + +# Returns +- `result`: optimal window length and corresponding survival fraction +- `report`: report with all window lengths and survival fractions +""" function fit_sg_wl(dep_sep_data::NamedTuple{(:dep, :sep)}, a_grid_wl_sg::StepRangeLen, optimization_config::PropDict) # unpack config dep, dep_window = optimization_config.dep, Float64.(optimization_config.dep_window) @@ -163,11 +176,13 @@ function fit_sg_wl(dep_sep_data::NamedTuple{(:dep, :sep)}, a_grid_wl_sg::StepRan aoe_dep, aoe_sep = dep_sep_data.dep.aoe, dep_sep_data.sep.aoe # prepare peakhist - result_dep, _ = prepare_dep_peakhist(e_dep, dep; n_bins_cut=optimization_config.nbins_dep_cut, relative_cut=optimization_config.dep_rel_cut) - + result_dep, _ = prepare_dep_peakhist(e_dep, dep; n_bins_cut=optimization_config.nbins_dep_cut, relative_cut=optimization_config.dep_rel_cut, uncertainty=false) + + yield() + # get calib constant from fit on DEP peak - e_dep_calib = e_dep .* result_dep.m_calib - e_sep_calib = e_sep .* result_dep.m_calib + e_dep_calib = e_dep .* mvalue(result_dep.m_calib) + e_sep_calib = e_sep .* mvalue(result_dep.m_calib) # create empty arrays for sf and sf_err sep_sfs = Quantity{Measurement}[] @@ -176,7 +191,7 @@ function fit_sg_wl(dep_sep_data::NamedTuple{(:dep, :sep)}, a_grid_wl_sg::StepRan # for each window lenght, calculate the survival fraction in the SEP for (i_aoe, wl) in enumerate(a_grid_wl_sg) - aoe_dep_i = aoe_dep[i_aoe, :][isfinite.(aoe_dep[i_aoe, :])] ./ result_dep.m_calib + aoe_dep_i = aoe_dep[i_aoe, :][isfinite.(aoe_dep[i_aoe, :])] ./ mvalue(result_dep.m_calib) e_dep_i = e_dep_calib[isfinite.(aoe_dep[i_aoe, :])] # prepare AoE @@ -184,35 +199,38 @@ function fit_sg_wl(dep_sep_data::NamedTuple{(:dep, :sep)}, a_grid_wl_sg::StepRan min_aoe_dep_i = quantile(aoe_dep_i, optimization_config.min_aoe_quantile) + optimization_config.min_aoe_offset try - psd_cut = get_psd_cut(aoe_dep_i, e_dep_i; window=dep_window, cut_search_interval=(min_aoe_dep_i, max_aoe_dep_i)) + psd_cut = get_psd_cut(aoe_dep_i, e_dep_i; window=dep_window, cut_search_interval=(min_aoe_dep_i, max_aoe_dep_i), uncertainty=false) aoe_sep_i = aoe_sep[i_aoe, :][isfinite.(aoe_sep[i_aoe, :])] ./ result_dep.m_calib e_sep_i = e_sep_calib[isfinite.(aoe_sep[i_aoe, :])] - result_sep, _ = get_peak_surrival_fraction(aoe_sep_i, e_sep_i, sep, sep_window, psd_cut.cut; uncertainty=true, low_e_tail=false) + result_sep, _ = get_peak_surrival_fraction(aoe_sep_i, e_sep_i, sep, sep_window, psd_cut.cut; uncertainty=false, low_e_tail=false) push!(sep_sfs, result_sep.sf) push!(wls, wl) catch e @warn "Couldn't process window length $wl" end + yield() end # get minimal surrival fraction and window length - sep_sfs_cut = [1.0u"percent" .< sep_sfs .< 100u"percent"] + sep_sfs_cut = 1.0u"percent" .< sep_sfs .< 100u"percent" if isempty(sep_sfs[sep_sfs_cut]) @warn "No valid SEP SF found, setting to NaN" - min_sf = NaN*u"percent" + min_sf = measurement(NaN, NaN)*u"percent" @warn "No valid window length found, setting to default" - wl_sg_min_sf = 100u"ns" + wl_sg_min_sf = last(a_grid_wl_sg[a_grid_wl_sg .< 110u"ns"]) else - min_sf = minimum(sep_sfs[sep_sfs_cut]) - wl_sg_min_sf = a_grid_wl_sg[sep_sfs_cut][findmin(sep_sfs[sep_sfs_cut])[2]] + min_sf = minimum(sep_sfs[sep_sfs_cut]) + wl_sg_min_sf = wls[sep_sfs_cut][findmin(sep_sfs[sep_sfs_cut])[2]] end # generate result and report result = ( wl = measurement(wl_sg_min_sf, step(a_grid_wl_sg)), - sf = min_sf + sf = min_sf, + n_dep = length(e_dep), + n_sep = length(e_sep) ) report = ( wl = result.wl, diff --git a/src/qc.jl b/src/qc.jl index ddb5d452..0b9a260c 100644 --- a/src/qc.jl +++ b/src/qc.jl @@ -13,7 +13,7 @@ function qc_sg_optimization(dsp_dep::NamedTuple{(:aoe, :e, :blmean, :blslope, :t # get half truncated centered cut on blslope for pile-up rejection result_dep_slope_cut, report_dep_slope_cut = get_centered_gaussian_window_cut(blslope_dep, -0.1u"ns^-1", 0.1u"ns^-1", optimization_config.cuts.dep.blslope_sigma, ; n_bins_cut=optimization_config.cuts.dep.nbins_blslope_cut, relative_cut=optimization_config.cuts.dep.rel_cut_blslope_cut) # Cut on blslope, energy and t0 for simple QC - qc_cut_dep = blslope_dep .> result_dep_slope_cut.low_cut .&& blslope_dep .< result_dep_slope_cut.high_cut .&& e_dep .> optimization_config.cuts.dep.min_e .&& quantile(e_dep, first(optimization_config.cuts.dep.e_quantile)) .< e_dep .< quantile(e_dep, last(optimization_config.cuts.dep.e_quantile)) .&& first(optimization_config.cuts.dep.t50)u"µs" .< t50_dep .< last(optimization_config.cuts.dep.t50)u"µs" + qc_cut_dep = blslope_dep .> result_dep_slope_cut.low_cut .&& blslope_dep .< result_dep_slope_cut.high_cut .&& e_dep .> optimization_config.cuts.dep.min_e .&& quantile(e_dep, first(optimization_config.cuts.dep.e_quantile)) .< e_dep .< quantile(e_dep, last(optimization_config.cuts.dep.e_quantile)) .&& first(optimization_config.cuts.dep.t50) .< t50_dep .< last(optimization_config.cuts.dep.t50) aoe_dep, e_dep = aoe_dep[:, qc_cut_dep], e_dep[qc_cut_dep] ### SEP @@ -25,7 +25,7 @@ function qc_sg_optimization(dsp_dep::NamedTuple{(:aoe, :e, :blmean, :blslope, :t result_sep_slope_cut, report_sep_slope_cut = get_centered_gaussian_window_cut(blslope_sep, -0.1u"ns^-1", 0.1u"ns^-1", optimization_config.cuts.sep.blslope_sigma, ; n_bins_cut=optimization_config.cuts.sep.nbins_blslope_cut, relative_cut=optimization_config.cuts.sep.rel_cut_blslope_cut) # Cut on blslope, energy and t0 for simple QC - qc_cut_sep = blslope_sep .> result_sep_slope_cut.low_cut .&& blslope_sep .< result_sep_slope_cut.high_cut .&& e_sep .> optimization_config.cuts.sep.min_e .&& quantile(e_sep, first(optimization_config.cuts.sep.e_quantile)) .< e_sep .< quantile(e_sep, last(optimization_config.cuts.sep.e_quantile)) .&& first(optimization_config.cuts.sep.t50)u"µs" .< t50_sep .< last(optimization_config.cuts.sep.t50)u"µs" + qc_cut_sep = blslope_sep .> result_sep_slope_cut.low_cut .&& blslope_sep .< result_sep_slope_cut.high_cut .&& e_sep .> optimization_config.cuts.sep.min_e .&& quantile(e_sep, first(optimization_config.cuts.sep.e_quantile)) .< e_sep .< quantile(e_sep, last(optimization_config.cuts.sep.e_quantile)) .&& first(optimization_config.cuts.sep.t50) .< t50_sep .< last(optimization_config.cuts.sep.t50) aoe_sep, e_sep = aoe_sep[:, qc_cut_sep], e_sep[qc_cut_sep] return (dep=(aoe=aoe_dep, e=e_dep), sep=(aoe=aoe_sep, e=e_sep)) diff --git a/src/specfit.jl b/src/specfit.jl index 12870dad..fce6b00e 100644 --- a/src/specfit.jl +++ b/src/specfit.jl @@ -169,7 +169,7 @@ function fit_single_peak_th228(h::Histogram, ps::NamedTuple{(:peak_pos, :peak_fw end # MLE - opt_r = optimize((-) ∘ f_loglike ∘ inverse(f_trafo), f_trafo(v_init), Optim.Options(time_limit = 120)) + opt_r = optimize((-) ∘ f_loglike ∘ inverse(f_trafo), f_trafo(v_init), Optim.Options(time_limit = 60)) # best fit results v_ml = inverse(f_trafo)(Optim.minimizer(opt_r)) @@ -193,10 +193,15 @@ function fit_single_peak_th228(h::Histogram, ps::NamedTuple{(:peak_pos, :peak_fw pval, chi2, dof = p_value(th228_fit_functions.f_fit, h, v_ml) # calculate normalized residuals - residuals, residuals_norm, p_value_binwise, bin_centers = get_residuals(th228_fit_functions[fit_func], h, v_ml) + residuals, residuals_norm, p_value_binwise, bin_centers = get_residuals(th228_fit_functions[fit_func], h, v_ml) # get fwhm of peak - fwhm, fwhm_err = get_peak_fwhm_th228(v_ml, param_covariance) + fwhm, fwhm_err = + try + get_peak_fwhm_th228(v_ml, param_covariance) + catch e + get_peak_fwhm_th228(v_ml, v_ml_err) + end @debug "Best Fit values" @debug "μ: $(v_ml.μ) ± $(v_ml_err.μ)" @@ -227,7 +232,9 @@ function fit_single_peak_th228(h::Histogram, ps::NamedTuple{(:peak_pos, :peak_fw @debug "n: $(v_ml.n)" @debug "FWHM: $(fwhm)" - result = merge(v_ml, (fwhm = fwhm, )) + result = merge(NamedTuple{keys(v_ml)}([measurement(v_ml[k], NaN) for k in keys(v_ml)]...), + (fwhm = measurement(fwhm, NaN), )) + # result = merge(v_ml, (fwhm = fwhm, )) report = ( v = v_ml, h = h, @@ -270,7 +277,7 @@ Get the FWHM of a peak from the fit parameters while performing a MC error propa * `fwhm`: the FWHM of the peak * `fwhm_err`: the uncertainty of the FWHM of the peak """ -function get_peak_fwhm_th228(v_ml::NamedTuple, v_ml_err::Union{Matrix,NamedTuple},uncertainty::Bool=true) +function get_peak_fwhm_th228(v_ml::NamedTuple, v_ml_err::Union{Matrix,NamedTuple}, uncertainty::Bool=true) # get fwhm for peak fit fwhm = estimate_fwhm(v_ml) if !uncertainty @@ -278,9 +285,9 @@ function get_peak_fwhm_th228(v_ml::NamedTuple, v_ml_err::Union{Matrix,NamedTuple end # get MC for FWHM err - if isa(v_ml_err,Matrix)# use correlated fit parameter uncertainties + if isa(v_ml_err, Matrix)# use correlated fit parameter uncertainties v_mc = get_mc_value_shapes(v_ml, v_ml_err, 10000) - elseif isa(v_ml_err,NamedTuple) # use uncorrelated fit parameter uncertainties + elseif isa(v_ml_err, NamedTuple) # use uncorrelated fit parameter uncertainties v_mc = get_mc_value_shapes(v_ml, v_ml_err, 1000) end fwhm_mc = estimate_fwhm.(v_mc) diff --git a/src/utils.jl b/src/utils.jl index 22590dfc..ae744518 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -97,6 +97,7 @@ function get_mc_value_shapes(v::NamedTuple, v_err::Matrix, n::Union{Int64,Int32} n = size(v_mc,2) v_mc = [NamedTuple{keys(v)[1:size(v_err,1)]}(v_mc[:,i]) for i=1:n] # convert back to NamedTuple end + """ get_friedman_diaconis_bin_width(x::AbstractArray) @@ -144,4 +145,8 @@ function nearestSPD(A::Matrix{<:Real}) B = (B + B') / 2 # make sure matrix is symmetric return B end -export nearestSPD \ No newline at end of file +export nearestSPD + + +Measurements.value(nt::NamedTuple) = NamedTuple{keys(nt)}([Measurements.value(nt[f]) for f in keys(nt)]...) +Measurements.value(x::AbstractArray) = Measurements.value.(x) \ No newline at end of file