R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandCal2HitHistAnalysis.cxx
Go to the documentation of this file.
2#include "R3BDataMonitor.h"
5#include "R3BNeulandCommon.h"
7#include "R3BValueError.h"
9#include <TF1.h>
10#include <TFitResult.h>
11#include <TFitResultPtr.h>
12#include <TH1.h>
13#include <TH2.h>
14#include <array>
15#include <fairlogger/Logger.h>
16#include <fmt/core.h>
17#include <range/v3/range/conversion.hpp>
18#include <range/v3/view/iota.hpp>
19#include <range/v3/view/transform.hpp>
20#include <unordered_map>
21#include <utility>
22#include <vector>
23
25{
26
27 namespace
28 {
29 constexpr auto FITTING_RANGE_RATIO = 0.9;
30
31 struct ParResult
32 {
33 ValueErrorD t_offset;
34 ValueErrorD effective_speed;
35 };
36
37 auto calculate_toffset_speed(TH1D* histogram, int bar_id) -> ParResult
38 {
39
40 auto [cumulative, x_pos] = calculate_CDF_with_quantiles(histogram, FITTING_RANGE_RATIO);
41
42 // First fit to determine the values of the slope and offset
43 auto first_fit_res = cumulative->Fit("pol1", "SQC", "", x_pos[0], x_pos[1]);
44 if (first_fit_res.Get() == nullptr)
45 {
46 LOGP(warn, "First linear fitting on time_diff CFD failed for the bar with id {}", bar_id);
47 return {};
48 }
49 const auto slope = first_fit_res->Parameter(1);
50 const auto offset = first_fit_res->Parameter(0);
51
52 // Second fit to determine parameters and their errors. The slope and offset values are used for their
53 // initial values. The reason why the second fit is needed is because the error of the t_offset value is
54 // dependent on both offset and slope, which are not independent. By redefining the fitting parameter, the
55 // error of t_offset can be directly given by the fitting algorithm.
56 auto fit_fun = TF1{ "fit_fun", "[1]*x + 0.5 - [0] * [1]", x_pos[0], x_pos[1] };
57 fit_fun.SetParameter(0, (0.5 - offset) / slope);
58 fit_fun.SetParameter(1, slope);
59 auto second_fit_res = cumulative->Fit(&fit_fun, "SQC", "", x_pos[0], x_pos[1]);
60 if (second_fit_res.Get() == nullptr)
61 {
62 LOGP(warn, "Second fitting on time_diff CFD failed for the bar with id {}", bar_id);
63 return {};
64 }
65
66 auto par_result = ParResult();
67 par_result.t_offset.value = second_fit_res->Parameter(0);
68 par_result.t_offset.error = second_fit_res->Error(0);
69 par_result.effective_speed.value = second_fit_res->Parameter(1) * 2 * BarLength;
70 par_result.effective_speed.error = second_fit_res->Error(1) * 2 * BarLength;
71 return par_result;
72 }
73 } // namespace
74
76 {
77 const auto module_size = GetModuleSize();
78
79 static constexpr auto TIME_DIFF_MAX = 300.; // ns
80 static constexpr auto TIME_DIFF_BIN_NUM = 600;
81
82 hist_time_diff_ = histograms.add_hist<TH2D>("hist_time_diff",
83 "Time differences between two adjacent PMTs",
84 module_size,
85 0.5,
86 0.5 + module_size,
87 TIME_DIFF_BIN_NUM,
88 -TIME_DIFF_MAX,
89 TIME_DIFF_MAX);
90
91 tsync_engine_.init_hist(histograms);
92 }
93
95 {
96 cal_to_hit_par_ = GetTask()->GetCal2HitPar();
97 tsync_engine_.set_number_of_modules(GetModuleSize());
98 tsync_engine_.set_max_time_difference(DEFAULT_TSYNC_MAX_TIME_DIFF);
99 tsync_engine_.init();
100 }
101
102 void HistAnalysis::AddSignals(const std::vector<BarCalData>& signals)
103 {
104 // all bar signal must have one signal on both sides
105 for (const auto& signal : signals)
106 {
107 if (signal.left.size() != 1 or signal.right.size() != 1)
108 {
109 continue;
110 }
111 fill_hist(signal);
112 }
113 }
114
116 {
117 const auto& left_signal = signal.left.front();
118 const auto& right_signal = signal.right.front();
119
120 const auto module_num = signal.module_num;
121 const auto t_diff = right_signal.leading_time - left_signal.leading_time;
122 const auto t_sum = right_signal.leading_time + left_signal.leading_time;
123
124 hist_time_diff_->Fill(static_cast<int>(module_num), t_diff.value);
125 tsync_engine_.add_point(t_sum.value, module_num);
126 // hist_time_sum_->Fill(static_cast<int>(module_num), t_sum.value);
127 }
128
129 auto HistAnalysis::SignalFilter(const std::vector<BarCalData>& signals) -> bool
130 {
131 // select out rays with few hits
132 return signals.size() >= minimum_hit_;
133 }
134
135 namespace
136 {
137 void calibrate_t_diff(TH2D* hist_time_diff, HitModulePar& module_par)
138 {
139 const auto module_num = module_par.module_num;
140
141 auto* hist_t_diff_py = hist_time_diff->ProjectionY("_py", module_num, module_num);
142 const auto par_res = calculate_toffset_speed(hist_t_diff_py, module_num);
143 module_par.t_diff = par_res.t_offset;
144 module_par.effective_speed = par_res.effective_speed;
145 }
146
147 void add_default_module_pars(Cal2HitPar& hit_par, int module_size)
148 {
149 auto module_pars =
150 ranges::views::iota(1, module_size + 1) |
151 ranges::views::transform([](int module_num)
152 { return std::make_pair(module_num, HitModulePar{ module_num }); }) |
153 ranges::to<std::unordered_map<int, HitModulePar>>();
154 hit_par.SetModulePars(std::move(module_pars));
155 }
156 } // namespace
157
159 {
160 add_default_module_pars(hit_par, GetModuleSize());
161 for (auto& [module_num, module_par] : hit_par.GetListOfModuleParRef())
162 {
163 calibrate_t_diff(hist_time_diff_, module_par);
164 }
165 tsync_engine_.calibrate(hit_par);
166 }
167
168} // namespace R3B::Neuland::Calibration
auto add_hist(std::unique_ptr< TH1 > hist) -> TH1 *
void SetModulePars(std::unordered_map< int, ::R3B::Neuland::HitModulePar > module_pars)
void AddSignals(const std::vector< BarCalData > &signals) override
auto SignalFilter(const std::vector< BarCalData > &signals) -> bool override
void HistInit(DataMonitor &histograms) override
constexpr auto DEFAULT_TSYNC_MAX_TIME_DIFF
auto calculate_CDF_with_quantiles(TH1 *histogram, double ratio) -> std::pair< TH1 *, std::array< double, 2 > >
constexpr auto BarLength
ValueError< double > ValueErrorD
std::vector< CalDataSignal > left
std::vector< CalDataSignal > right
ValueError< double > effective_speed
cm/ns
ValueError< double > t_diff
ns