R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandTamexReader2.cxx
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) 2019 GSI Helmholtzzentrum für Schwerionenforschung GmbH *
3 * Copyright (C) 2019-2023 Members of R3B Collaboration *
4 * *
5 * This software is distributed under the terms of the *
6 * GNU General Public Licence (GPL) version 3, *
7 * copied verbatim in the file "LICENSE". *
8 * *
9 * In applying this license GSI does not waive the privileges and immunities *
10 * granted to it by virtue of its status as an Intergovernmental Organization *
11 * or submit itself to any jurisdiction. *
12 ******************************************************************************/
13
14#include "FairRootManager.h"
15
16#include "R3BException.h"
17#include "R3BLogger.h"
20#include "R3BReader.h"
21#include "ext_data_struct_info.hh"
22#include "ext_h101_raw_nnp_tamex.h"
23#include <R3BShared.h>
24#include <Rtypes.h>
25#include <TH1.h>
26#include <array>
27
28// TODO: C++20 std::span
29#include <boost/regex/v5/regex.hpp>
30#include <boost/regex/v5/regex_fwd.hpp>
31#include <boost/regex/v5/regex_match.hpp>
32#include <boost/regex/v5/regex_search.hpp>
33#include <cstddef>
34#include <fairlogger/Logger.h>
35#include <fmt/core.h>
36#include <gsl/span>
37// TODO: C++20 std::range
38#include <map>
39#include <range/v3/algorithm/equal.hpp>
40#include <range/v3/view/iota.hpp>
41#include <range/v3/view/take.hpp>
42#include <range/v3/view/zip.hpp>
43#include <string>
44#include <string_view>
45#include <utility>
46
51
52using gsl::span;
53namespace views = ranges::views;
54using R3B::Side;
56
57namespace
58{
59 constexpr size_t INITIAL_SIGNAL_SIZE = 20;
60 constexpr size_t PRINT_ERROR_MAX = 100000;
61 constexpr auto BarsPerPlane = 50;
62 const auto errorStrings = std::map<Errors, std::string>{
63 { Errors::module_size, "Incompatible module size" },
64 { Errors::data_size, "Incompatible signal size" },
65 { Errors::divider, "Incompatible signal dividers" },
66 { Errors::indices, "Incompatible module IDs" },
67 };
68
69 const auto SIDES = std::array<Side, 2>{ Side::left, Side::right };
70 template <typename BinaryOperation, typename Item0, typename Item1>
71 constexpr auto CheckAmong(BinaryOperation optn, const Item0& item0, const Item1& item1) -> bool
72 {
73 return optn(item0, item1);
74 }
75
76 template <typename BinaryOperation, typename Item0, typename Item1, typename... Items>
77 constexpr auto CheckAmong(BinaryOperation optn, const Item0& item0, const Item1& item1, const Items&... items)
78 -> bool
79 {
80 return CheckAmong(optn, item0, item1) && CheckAmong(optn, item1, items...);
81 }
82
83 template <typename UnaryOperation, typename... Items>
84 inline auto CheckAny(UnaryOperation optn, const Items&... items)
85 {
86 return (optn(items) || ...);
87 }
88
89 template <typename UnaryFunction, typename... Items>
90 inline auto Zip_from(UnaryFunction optn, const Items&... items)
91 {
92 return ranges::zip_view(optn(items)...);
93 }
94
95 template <typename Signals, typename ModuleNum_divider, typename UnaryFunction>
96 inline void FillData(const Signals& signals_view,
97 const ModuleNum_divider& moduleNum_divider_view,
98 UnaryFunction optn)
99 {
100 auto range_begin = signals_view.begin();
101 auto range_iter = signals_view.begin();
102 for (const auto& [module_num, divider] : moduleNum_divider_view)
103 {
104 for (; !(range_iter == signals_view.end() || range_iter == (range_begin + divider)); ++range_iter)
105 {
106 optn(*range_iter, module_num);
107 }
108 }
109 }
110} // namespace
111
113 : R3BReader("R3BNeulandTamexReader2")
114 , max_limit_(PRINT_ERROR_MAX)
115 , fOffset(offset)
116 , numPlanes_(static_cast<int>(R3B::GetSize(inputData_->NN_P)))
117 , inputData_{ data }
120{
121 mappedData_.reserve(INITIAL_SIGNAL_SIZE);
122 error_log_.insert_or_assign(Errors::module_size, 0);
123 error_log_.insert_or_assign(Errors::data_size, 0);
124 error_log_.insert_or_assign(Errors::indices, 0);
125 error_log_.insert_or_assign(Errors::divider, 0);
126}
127
128bool R3BNeulandTamexReader2::Init(ext_data_struct_info* a_struct_info) // NOLINT
129{
130 auto status_ok = 1;
131 R3BLOG(info, "");
132 EXT_STR_h101_raw_nnp_tamex_ITEMS_INFO(status_ok, *a_struct_info, fOffset, EXT_STR_h101_raw_nnp_tamex, 0); // NOLINT
133
134 if (status_ok == 0)
135 {
136 R3BLOG(error, "Failed to setup structure information.");
137 return false;
138 }
139
140 R3BLOG(info, "Number of planes " << numPlanes_);
141
142 // Register output arrays in tree
143 auto* rootMan = FairRootManager::Instance();
144 rootMan->RegisterAny("NeulandMappedData", mappedDataPtr_, !is_online_);
145 if (is_triggered_)
146 {
147 rootMan->RegisterAny("NeulandTrigMappedData", mappedTrigDataPtr_, !is_online_);
148 }
149 Reset();
150 if (not is_online_)
151 {
153 }
154 return true;
155}
156
158{
159 const auto BarBinSize = numPlanes_ * 50;
160 histograms_.add_hist<TH1I>(
161 "bar_FT_l", "number of hits on left PMTS for each bar", BarBinSize, 0.5, 0.5 + BarBinSize);
162 histograms_.add_hist<TH1I>(
163 "bar_FT_r", "number of hits on right PMTS for each bar", BarBinSize, 0.5, 0.5 + BarBinSize);
164}
165
166namespace
167{
168 template <typename ErrorLog, typename... Items>
169 auto CheckCondition(ErrorLog& log, const Items&... items) -> bool
170 {
171 auto ApplyCriterium = [&log, &items...](Errors error, auto criterium) -> bool
172 {
173 if (!CheckAmong(criterium, items...))
174 {
175 ++log.at(error);
176 return false;
177 }
178 return true;
179 };
180 return ApplyCriterium(Errors::module_size,
181 [](const auto& left, const auto& right) { return left.BM == right.BM; }) &&
182 ApplyCriterium(Errors::data_size,
183 [](const auto& left, const auto& right) { return left.B == right.B; }) &&
184 ApplyCriterium(
185 Errors::indices,
186 [](const auto& left, const auto& right)
187 { return ranges::equal(views::take(left.BMI, left.BM), views::take(right.BMI, right.BM)); }) &&
188 ApplyCriterium(
189 Errors::divider,
190 [](const auto& left, const auto& right)
191 { return ranges::equal(views::take(left.BME, left.BM), views::take(right.BME, right.BM)); });
192 }
193} // namespace
194
195template <typename ViewType>
196auto R3BNeulandTamexReader2::extract_plane_signals(const ViewType& signalsPlane, int planeNum)
197{
198 auto planeSignals = R3BPaddleTamexMappedData2{ planeNum };
199 const auto signals_sides_view =
200 ranges::zip_view(signalsPlane.tcl_T, signalsPlane.tfl_T, signalsPlane.tct_T, signalsPlane.tft_T, SIDES);
201 for (const auto& [coarse_leading, fine_leading, coarse_trailing, fine_trailing, side] : signals_sides_view)
202 {
203 // pass if no data
204 if (CheckAny([](const auto& item) { return item.BM == 0; },
205 coarse_leading,
206 fine_leading,
207 coarse_trailing,
208 fine_trailing))
209 {
210 R3BLOG(debug3, fmt::format("No signals at the current event from plane {}.", planeNum));
211 continue;
212 }
213
214 if (!CheckCondition(error_log_, coarse_leading, fine_leading, coarse_trailing, fine_trailing))
215 {
217 }
218
219 const auto module_size = coarse_leading.BM;
220 const auto signal_size = coarse_leading.B;
221 R3BLOG(debug, fmt::format("Signals at the current event from plane {}: {}", planeNum, signal_size));
222 mappedData_.reserve(signal_size);
223 const auto barNum_divider_view = ranges::zip_view(span(coarse_leading.BMI), span(coarse_leading.BME));
224
225 const auto signals_view = Zip_from([](const auto& item) { return span(item.Bv); },
226 coarse_leading,
227 fine_leading,
228 coarse_trailing,
229 fine_trailing);
230 // TODO C++20: planeID and side can be directly captured by the lambda. Before C++20, capturing struture
231 // bindings is undefined behaviour
232 auto side_temp = side;
233 FillData(
234 signals_view | ranges::views::take(signal_size),
235 barNum_divider_view | ranges::views::take(module_size),
236 [&](const auto& signals, const auto& barNum)
237 {
238 const auto& [cl_time, fl_time, ct_time, ft_time] = signals;
239 auto doubleEdgeSignal = R3B::DoubleEdgeSignal{};
240 doubleEdgeSignal.leading.fine = fl_time;
241 doubleEdgeSignal.leading.coarse = cl_time;
242 doubleEdgeSignal.trailing.fine = ft_time;
243 doubleEdgeSignal.trailing.coarse = ct_time;
244
245 planeSignals.push_back(side_temp, barNum, doubleEdgeSignal);
246 R3BLOG(
247 debug3,
248 fmt::format(
249 "Writing a doube edge signal: barNum: {}, fl_time: {}, cl_time: {}, ft_time: {}, ct_time: {}",
250 barNum,
251 fl_time,
252 cl_time,
253 ft_time,
254 ct_time));
255
256 if (is_online_)
257 {
258 return;
259 }
260
261 if (side_temp == R3B::Side::left)
262 {
263 histograms_.get("bar_FT_l")->Fill(barNum + (planeNum - 1) * BarsPerPlane);
264 }
265 else
266 {
267 histograms_.get("bar_FT_r")->Fill(barNum + (planeNum - 1) * BarsPerPlane);
268 }
269 });
270 }
271 return planeSignals;
272}
273
275{
276 const auto signalsAllPlanes = span(inputData->NN_P);
277
278 mappedData_.clear();
279 for (const auto& [signalsPlane, planeID] : ranges::zip_view(signalsAllPlanes, ranges::iota_view(1)))
280 {
281 auto planeSignals = extract_plane_signals(signalsPlane, planeID);
282 if (!planeSignals.empty())
283 {
284 if (not is_online_)
285 {
286 histogram_action(planeSignals);
287 }
288 mappedData_.emplace_back(std::move(planeSignals));
289 }
290 }
291
292 return true;
293}
294
296{
297 for (const auto& [hist_name, action] : hist_actions_)
298 {
299 action(mappedData, histograms_.get(hist_name));
300 }
301}
302
303namespace
304{
305 template <typename ErrorLog, typename... Items>
306 auto Checkondition(ErrorLog& log, EXT_STR_h101_raw_nnp_tamex_onion* inputData)
307 {
308 auto ApplyCriterium = [&log](Errors error, bool criterium) -> bool
309 {
310 if (!criterium)
311 {
312 ++log.at(error);
313 return false;
314 }
315 return true;
316 };
317 return ApplyCriterium(Errors::module_size, inputData->NN_TRIGCM == inputData->NN_TRIGFM) &&
318 ApplyCriterium(Errors::data_size, inputData->NN_TRIGC == inputData->NN_TRIGF) &&
319 ApplyCriterium(Errors::indices,
320 ranges::equal(views::take(inputData->NN_TRIGCMI, inputData->NN_TRIGCM),
321 views::take(inputData->NN_TRIGFMI, inputData->NN_TRIGFM))) &&
322 ApplyCriterium(Errors::divider,
323 ranges::equal(views::take(inputData->NN_TRIGCME, inputData->NN_TRIGCM),
324 views::take(inputData->NN_TRIGFME, inputData->NN_TRIGFM)));
325 }
326} // namespace
327
329{
330 if (!is_triggered_ || inputData->NN_TRIGCM == 0 || inputData->NN_TRIGFM == 0)
331 {
332 return true;
333 }
334
335 if (!Checkondition(error_log_, inputData))
336 {
337 return false;
338 }
339
340 const auto module_size = inputData->NN_TRIGCM;
341 const auto signal_size = inputData->NN_TRIGC;
342 auto const trigger_signals_view =
343 Zip_from([](const auto& item) { return span(item); }, inputData->NN_TRIGCv, inputData->NN_TRIGFv);
344 auto const moduleNum_divider_view = ranges::zip_view(span(inputData->NN_TRIGCMI), span(inputData->NN_TRIGCME));
345 FillData(ranges::views::take(trigger_signals_view, signal_size),
346 ranges::views::take(moduleNum_divider_view, module_size),
347 [&](const auto& signal_pack, const auto& module_num)
348 {
349 const auto& [coarse_time, fine_time] = signal_pack;
350 auto trigDatum = mappedTrigData_.emplace(module_num, R3BPaddleTamexTrigMappedData{}).first;
351 trigDatum->second.module_num = module_num;
352 trigDatum->second.signal.fine = fine_time;
353 trigDatum->second.signal.coarse = coarse_time;
354 R3BLOG(debug3,
355 fmt::format("Writing a trig signal: module ID: {}, fine time: {}, coarse_time: {}",
356 module_num,
357 fine_time,
358 coarse_time));
359 // std::cout << "trig signal: " << module_num << "\t" << fine_time << "\t" << coarse_time << "\n";
360 });
361 return true;
362}
363
365{
367 {
368 return false;
369 }
370 if (fair::Logger::GetConsoleSeverity() <= fair::Severity::info && ++counter_ == max_limit_)
371 {
372 counter_ = 0;
373 print_error();
374 }
375 return true;
376}
377
378auto R3BNeulandTamexReader2::check_trigger_needed(std::string_view item_name) const -> bool
379{
380 // TODO: not the exact regex, but it suffices
381 const auto neuland_trigger_regex = boost::regex{ "^NN_TRIG(C|F)(v|M)?(I|E)?$" };
382 auto res = is_triggered_ && boost::regex_match(item_name.data(), neuland_trigger_regex); // NOLINT
383 return res;
384}
385
386auto R3BNeulandTamexReader2::check_bar_needed(std::string_view item_name) const -> bool
387{
388 const auto neuland_bar_regex = boost::regex{ R"(^NN_P(\d*)t.*$)" };
389 auto result = boost::cmatch{};
390 if (boost::regex_search(item_name.data(), result, neuland_bar_regex)) // NOLINT
391 {
392 const auto plane_num = std::stoi(result.str(1));
393 if (plane_num != 0 and plane_num <= numPlanes_)
394 {
395 return true;
396 }
397 }
398 return false;
399}
400
401auto R3BNeulandTamexReader2::MismappedItemRequired(std::string_view item_name) const -> bool
402{
403 if (numPlanes_ == 0)
404 {
405 throw R3B::logic_error(
406 "The plane number of NeuLAND is not set. Please use R3BNeulandTamexReader2::SetMaxNbPalnes to set it.");
407 }
408
409 return check_trigger_needed(item_name) || check_bar_needed(item_name);
410}
411
413{
414 mappedData_.clear();
415 mappedTrigData_.clear();
416}
417
419{
420 fmt::print("----------Tamex Reader Error Summary:--------------\n");
421 fmt::print("{0:^30}|{1:^10}\n", "Causes", "Counts");
422 for (const auto& [key, counts] : error_log_)
423 {
424 fmt::print("{0:<30}|{1:^10}\n", errorStrings.at(key), counts);
425 }
426 fmt::print("---------------------------------------------------\n");
427}
428
#define R3BLOG(severity, x)
Definition R3BLogger.h:32
ClassImp(R3BNeulandTamexReader2)
R3BNeulandTamexReader2::Errors Errors
struct EXT_STR_h101_raw_nnp_tamex_onion_t EXT_STR_h101_raw_nnp_tamex_onion
struct EXT_STR_h101_raw_nnp_tamex_t EXT_STR_h101_raw_nnp_tamex
R3B::PaddleTamexMappedData R3BPaddleTamexMappedData2
R3B::PaddleTamexTrigMappedData R3BPaddleTamexTrigMappedData
auto R3BRead() -> bool override
std::map< std::string, std::function< void(const R3B::PaddleTamexMappedData &, TH1 *)> > hist_actions_
auto ReadSignals(EXT_STR_h101_raw_nnp_tamex_onion *inputData) -> bool
auto check_bar_needed(std::string_view item_name) const -> bool
R3BNeulandTamexReader2(EXT_STR_h101_raw_nnp_tamex_onion *, size_t)
auto Init(ext_data_struct_info *) -> bool override
auto extract_plane_signals(const ViewType &signalsPlane, int planeNum)
TrigMappedDataVector * mappedTrigDataPtr_
auto ReadTriggerSignals(EXT_STR_h101_raw_nnp_tamex_onion *inputData) -> bool
EXT_STR_h101_raw_nnp_tamex_onion * inputData_
auto check_trigger_needed(std::string_view item_name) const -> bool
std::map< Errors, uint > error_log_
TrigMappedDataVector mappedTrigData_
void histogram_action(const R3B::PaddleTamexMappedData &mappedData)
MappedDataVector * mappedDataPtr_
auto MismappedItemRequired(std::string_view item_name) const -> bool override
R3BReader(TString const &)
Definition R3BReader.cxx:16