R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandMilleCalDataProcessor.cxx
Go to the documentation of this file.
6#include <Fit/BinData.h>
7#include <Math/WrappedMultiTF1.h>
8#include <R3BNeulandCommon.h>
9#include <TError.h>
10#include <fairlogger/Logger.h>
11#include <fmt/core.h>
12#include <functional>
13#include <numeric>
14#include <tuple>
15#include <unordered_map>
16#include <utility>
17#include <vector>
18
19#ifdef HAS_CPP_STANDARD_23
20#include <algorithm>
21#include <ranges>
22namespace stdrng = std::ranges;
23#else
24#include <range/v3/algorithm/find_if.hpp>
25#include <range/v3/algorithm/min_element.hpp>
26using namespace stdrng = ranges
27#endif
28
29constexpr auto DEFAULT_LEARNING_RATE = 0.1;
30
32{
33 using DataBufferType = std::unordered_map<int, std::vector<MilleCalData>>;
35 {
36 init_data_registers(num_of_modules);
37 auto& config = huber_regressor_.get_config_ref();
38 config.weight.init = 1.;
39 config.weight.learning_rate = DEFAULT_LEARNING_RATE;
40 config.bias.init = 0.;
41 config.bias.learning_rate = 10.;
42 }
43
45 {
46 const auto num_of_planes = num_of_modules / BarsPerPlane;
47 for (int plane_id{}; plane_id < num_of_planes; ++plane_id)
48 {
49 auto data_iter = data_buffers_.insert_or_assign(plane_id, std::vector<MilleCalData>{}).first;
50 data_iter->second.reserve(BarsPerPlane);
51 }
52 }
53
55
57 {
58 for (auto& [plane_id, bar_data] : data_buffers_)
59 {
60 bar_data.clear();
61 }
63 track_fit_data_.clear();
65 }
66
67 auto MilleDataProcessor::process(const std::vector<BarCalData>& signals, const Cal2HitPar& hit_par) -> bool
68 {
69 // fill only the bar_cal_data with only one pmt signal on both sides
70 for (const auto& signal : signals)
71 {
72 if (signal.left.size() == 1 && signal.right.size() == 1)
73 {
74 const auto module_num = static_cast<int>(signal.module_num);
75 const auto plane_id = ModuleID2PlaneID(module_num - 1);
76 data_buffers_[plane_id].emplace_back(signal);
77 }
78 }
79
81
82 return fit_planes(hit_par);
83 }
84
86 {
87 for (auto& [plane_id, bar_data] : data_buffers_)
88 {
89 if (bar_data.size() < 2)
90 {
91 continue;
92 }
93
94 const auto& bar_signals = bar_data;
95 auto check_if_isolated = [&bar_signals](const auto& signal) -> bool
96 {
97 const auto module_num = static_cast<int>(signal.module_num);
98 return (stdrng::find_if(bar_signals,
99 [module_num](const auto& bar_signal) -> auto
100 { return bar_signal.module_num == module_num - 1; }) == bar_signals.end()) and
101 (stdrng::find_if(bar_signals,
102 [module_num](const auto& bar_signal) -> auto
103 { return bar_signal.module_num == module_num + 1; }) == bar_signals.end());
104 };
105
106#if CPP_STANDARD < 20
107 bar_data.erase(std::remove_if(bar_data.begin(), bar_data.end(), check_if_isolated), bar_data.end());
108#else
109 std::erase_if(bar_data, check_if_isolated);
110#endif
111 }
112 }
113
114 namespace
115 {
116 using TrackInfo = MilleDataProcessor::TrackInfo;
117
118 constexpr auto calculate_diff(const TrackFitPar& fit_result, double val, int module_num) -> float
119 {
120 const auto z_val = ModuleNum2ZPos(module_num);
121 return static_cast<float>(val - (fit_result.slope * z_val) - fit_result.offset);
122 }
123
124 constexpr auto calculate_residual(const TrackFitPar& fit_result, double val, int module_num) -> float
125 {
126 const auto diff = calculate_diff(fit_result, val, module_num);
127 return static_cast<float>(diff * diff);
128 }
129
130 constexpr auto check_outlier_exist(const DataBufferType& data_buffer) -> bool
131 {
132 for (const auto& [plane_num, bars_data] : data_buffer)
133 {
134 if (std::ranges::any_of(bars_data, &MilleCalData::is_outlier))
135 {
136 return true;
137 }
138 }
139 return false;
140 }
141
142 constexpr void remove_outliers(DataBufferType& data_buffer)
143 {
144 for (auto& [plane_num, bars_data] : data_buffer)
145 {
146 std::erase_if(bars_data, [](const auto& bar_data) -> bool { return bar_data.is_outlier; });
147 }
148 }
149
150 } // namespace
151
152 auto MilleDataProcessor::fit_planes(const Cal2HitPar& hit_par) -> bool
153 {
154 auto is_ok = true;
156 LOGP(debug, "Fitting the data of bar displacements ... ");
157 while (not std::ranges::all_of(data_buffers_ | std::views::values,
158 [](const auto& bars) -> bool { return bars.empty(); }))
159 {
160 remove_outliers(data_buffers_);
161 is_ok &=
162 (linear_fit(track_fit_data_.bar_x_z, track_info_.bar_disp_data.x_z, huber_regressor_, p_value_cut_) and
164 if (not is_ok)
165 {
166 return false;
167 }
169 if (not check_outlier_exist(data_buffers_))
170 {
171 break;
172 }
173 }
175 LOGP(debug, "Fitting the data of time-derived positions ... ");
176 if (fair::Logger::GetConsoleSeverity() <= fair::Severity::debug)
177 {
179 }
180 return is_ok;
181 }
182
183 auto MilleDataProcessor::check_is_outlier(int module_num) const -> bool
184 {
185 const auto plane_id = ModuleID2PlaneID(module_num - 1);
186 const auto is_horizontal = IsPlaneIDHorizontal(plane_id);
187
188 const auto& fit_par = is_horizontal ? track_info_.bar_disp_data.y_z : track_info_.bar_disp_data.x_z;
189 const auto pos_z = PlaneID2ZPos(plane_id);
190 const auto pos_disp = GetBarVerticalDisplacement(module_num);
191 return huber_regressor_.check_outlier(pos_z, pos_disp, std::make_pair(fit_par.slope, fit_par.offset)).first;
192 }
193
195 {
196 LOGP(debug,
197 "Bar data size: {{x_z: {}, y_z: {}}}, track data size: {{x_z: {}, y_z: {}}}",
198 track_fit_data_.bar_x_z.size(),
199 track_fit_data_.bar_y_z.size(),
200 track_fit_data_.time_data_x_z.size(),
201 track_fit_data_.time_data_y_z.size());
202
203 LOGP(debug,
204 "Fit result:\n"
205 "bar displacements: {}\n"
206 "positions: slope: {}",
207 track_info_.bar_disp_data,
208 track_info_.time_data);
209
210 if (track_info_.bar_disp_data.x_z != track_info_.time_data.x_z)
211 {
212 LOGP(debug, "x_z plane fit result doesn't match!");
213 LOGP(debug, "x_z plane bar fit data:\n{}", track_fit_data_.bar_x_z);
214 LOGP(debug, "x_z plane time fit data:\n{}", track_fit_data_.time_data_x_z);
215 }
216
217 if (track_info_.bar_disp_data.y_z != track_info_.time_data.y_z)
218 {
219 LOGP(debug, "y_z plane fit result doesn't match!");
220 LOGP(debug, "y_z plane bar fit data:\n{}", track_fit_data_.bar_y_z);
221 LOGP(debug, "y_z plane time fit data:\n{}", track_fit_data_.time_data_y_z);
222 }
223 }
224
226 {
227 for (auto& [plane_id, plane_data] : data_buffers_)
228 {
229 if (plane_data.empty())
230 {
231 continue;
232 }
233 const auto is_plane_horizontal = IsPlaneIDHorizontal(plane_id);
234 auto& bar_disp_fit_data = is_plane_horizontal ? track_fit_data_.bar_y_z : track_fit_data_.bar_x_z;
235 const auto bar_z_val = PlaneID2ZPos(plane_id);
236
237 const auto bar_disp =
238 std::accumulate(plane_data.begin(),
239 plane_data.end(),
240 0.,
241 [](double sum, const MilleCalData& signal) -> double
242 { return sum + GetBarVerticalDisplacement(static_cast<int>(signal.module_num)); }) /
243 static_cast<double>(plane_data.size());
244 bar_disp_fit_data.z_vals.push_back(bar_z_val);
245 bar_disp_fit_data.z_errs.push_back(BarSize_Z / 2.);
246 bar_disp_fit_data.errs.push_back(BarSize_XY / 2.);
247 bar_disp_fit_data.vals.push_back(bar_disp);
248 }
249 }
250
252 {
253 for (auto& [plane_id, plane_data] : data_buffers_)
254 {
255 if (plane_data.empty())
256 {
257 continue;
258 }
259 const auto is_plane_horizontal = IsPlaneIDHorizontal(plane_id);
260 auto& bar_time_data = is_plane_horizontal ? track_fit_data_.time_data_x_z : track_fit_data_.time_data_y_z;
261 const auto bar_z_val = PlaneID2ZPos(plane_id);
262
263 auto iter = stdrng::min_element(plane_data, std::less{}, &MilleCalData::residual);
264 if (iter == plane_data.end())
265 {
266 continue;
267 }
268
269 bar_time_data.z_vals.push_back(bar_z_val);
270 bar_time_data.z_errs.push_back(BarSize_Z / 2.);
271 bar_time_data.errs.push_back(iter->position.error);
272 bar_time_data.vals.push_back(iter->position.value);
273 }
274 }
275
277 const TrackInfo& track_info,
278 const Cal2HitPar& hit_par) const
279 {
280 for (auto& [plane_id, plane_data] : data_buffers)
281 {
282 for (auto& signal : plane_data)
283 {
284 const auto t_diff = (signal.right.leading_time - signal.right.trigger_time) -
285 (signal.left.leading_time - signal.left.trigger_time);
286 const auto& module_par = hit_par.GetModuleParAt(signal.module_num);
287 const auto position_along_bar = (-t_diff + module_par.t_diff) / 2 * module_par.effective_speed;
288 const auto position_vert_bar = Common::GetBarVerticalDisplacement(signal.module_num);
289 signal.position = position_along_bar;
290 const auto& bar_disp_info = track_info.bar_disp_data;
291
292 const auto& [fit_result, fit_result_bar] = IsPlaneIDHorizontal(ModuleID2PlaneID(signal.module_num - 1))
293 ? std::tie(bar_disp_info.x_z, bar_disp_info.y_z)
294 : std::tie(bar_disp_info.y_z, bar_disp_info.x_z);
295 signal.fit_diff = calculate_diff(fit_result, position_along_bar.value, signal.module_num);
296 signal.residual = calculate_residual(fit_result, position_along_bar.value, signal.module_num);
297 signal.residual_bar_pos = calculate_residual(fit_result_bar, position_vert_bar, signal.module_num);
298 signal.is_outlier = check_is_outlier(signal.module_num);
299 }
300 }
301 }
302
304 FitPar& fit_par,
305 HuberRegressor& huber_regressor,
306 double p_value_cut) -> bool
307 {
308
309 // // NOTE: ROOT::FIT::BinData only contains the references to the raw data.
310 // const auto bin_data = ROOT::Fit::BinData{ static_cast<unsigned int>(data.size()),
311 // data.z_vals.data(),
312 // data.vals.data(),
313 // data.z_errs.data(),
314 // data.errs.data() };
315
316 huber_regressor.reset_parameters();
317
318 LOGP(debug, "fitting the data with x: {} and y: {}", data.z_vals, data.vals);
319 auto is_ok = huber_regressor.train_from_data(data.z_vals, data.vals);
320
321 const auto& result = huber_regressor.get_result();
322 LOGP(debug, "Fitting result: {}", result);
323
324 if (not is_ok)
325 {
326 LOGP(debug, "Huber regression minimization failed!");
327 return false;
328 }
329
330 // INFO: scale the error by 2 such that p_value isn't too small.
331 fit_par.p_value = huber_regressor.calculate_p_value(data.errs, 2.);
332 fit_par.slope = result.weight.value;
333 fit_par.offset = result.bias.value;
334 if (fit_par.p_value < p_value_cut)
335 {
336 LOGP(
337 debug, "p-value ({}) is too small from the fit. Must be larger than {}.", fit_par.p_value, p_value_cut);
338 LOGP(debug, "fit data: \n {}", data);
339 return false;
340 }
341 return true;
342
343 // // disable annoying root printouts
344 // auto old_var = gErrorIgnoreLevel;
345 // gErrorIgnoreLevel = kFatal;
346 // auto res = fitter_.Fit(bin_data);
347 // gErrorIgnoreLevel = old_var;
348
349 // if (not res)
350 // {
351 // LOGP(debug, "Linear fitting on x_z data failed");
352 // return false;
353 // }
354 // fit_par.slope = fitter_.Result().Parameter(0);
355 // fit_par.offset = fitter_.Result().Parameter(1);
356 // fit_par.p_value = fitter_.Result().Prob();
357 // if (fit_par.p_value < p_value_cut_)
358 // {
359 // LOGP(debug,
360 // "p-value ({}) is too small from the fit. Must be larger than {}.",
361 // fitter_.Result().Prob(),
362 // p_value_cut_);
363 // LOGP(debug, "fit data: \n {}", data);
364 // return false;
365 // }
366 // return true;
367 }
368} // namespace R3B::Neuland::Calibration
auto GetModuleParAt(int module_num) const -> const ::R3B::Neuland::HitModulePar &
void remove_isolated_bar_signal()
Remove signals that are isolated in the plane.
auto process(const std::vector< BarCalData > &signals, const Cal2HitPar &hit_par) -> bool
Process the cal level data.
static auto linear_fit(const NeulandTrackDataSet &data, FitPar &fit_par, HuberRegressor &huber_regressor, double p_value_cut) -> bool
void set_data_buffers(DataBufferType &data_buffers, const TrackInfo &track_info, const Cal2HitPar &hit_par) const
std::unordered_map< int, std::vector< MilleCalData > > data_buffers_
std::unordered_map< int, std::vector< MilleCalData > > DataBufferType
Data buffer for the event data.
static constexpr auto GetBarVerticalDisplacement(int module_num) -> double
std::unordered_map< int, std::vector< MilleCalData > > DataBufferType
constexpr auto BarsPerPlane
constexpr auto ModuleID2PlaneID(int moduleID) -> int
constexpr auto GetBarVerticalDisplacement(int module_num) -> double
constexpr auto BarSize_Z
constexpr auto BarSize_XY
constexpr auto ModuleNum2ZPos(int module_num) -> T
constexpr auto IsPlaneIDHorizontal(int plane_id) -> bool
constexpr auto PlaneID2ZPos(int plane_id) -> T
Data structure to store the cal level data for the data preprocessing.
float residual
residual value against the fitted line