R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandDigitizer.cxx
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) 2019 GSI Helmholtzzentrum für Schwerionenforschung GmbH *
3 * Copyright (C) 2019-2025 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 "R3BNeulandDigitizer.h"
15#include "FairRunAna.h"
16#include "FairRuntimeDb.h"
18#include "NeulandSimCalData.h"
19#include "R3BDataMonitor.h"
21#include "R3BDigitizingEngine.h"
22#include "R3BDigitizingPaddle.h"
26#include "R3BDigitizingTamex.h"
27#include "R3BException.h"
29#include "R3BNeulandCommon.h"
30#include "R3BNeulandGeoPar.h"
31#include "R3BNeulandHit.h"
32#include <FairTask.h>
34#include <R3BShared.h>
35#include <RtypesCore.h>
36#include <TFile.h>
37#include <TH1.h>
38#include <TVector3.h>
39#include <algorithm>
40#include <array>
41#include <fairlogger/Logger.h>
42#include <fmt/core.h>
43#include <functional>
44#include <map>
45#include <memory>
46#include <range/v3/view/zip.hpp>
47#include <string>
48#include <string_view>
49#include <utility>
50#include <vector>
51
52constexpr auto MAX_SIZE_BIN = 20;
53
54namespace R3B::Neuland
55{
56 namespace
57 {
58 inline auto CreateDigiEngine(const DigiTaskOptions& option, std::string_view hit_par_name, FairRun* run)
59 {
60 namespace Digitizing = R3B::Digitizing;
61 using NeulandPaddle = Digitizing::Neuland::Paddle;
62 namespace Tamex = Digitizing::Neuland::Tamex;
63 using TamexChannel = Tamex::Channel;
64 using Digitizing::UseChannel;
65 using Digitizing::UsePaddle;
66 using MockPaddle = Digitizing::Neuland::MockPaddle;
67 using MockChannel = Digitizing::Neuland::MockChannel;
68 using TacquilaChannel = Digitizing::Neuland::TacQuila::Channel;
69
70 auto pileup_strategy = option.pileup_strategy;
71 const auto& tamex_par = option.tamex_par;
72 Cal2HitPar* cal_to_hit_par{ nullptr };
73 if (option.enable_hit_par)
74 {
75 LOGP(info, "cal_to_hit_par is used in digitization task!");
76 cal_to_hit_par = std::make_unique<Cal2HitPar>(hit_par_name).release();
77 run->GetRuntimeDb()->addContainer(cal_to_hit_par);
78 }
79 else
80 {
81 LOGP(info, "cal_to_hit_par is not used in digitization task!");
82 }
83 return std::map<std::pair<const std::string, const std::string>,
84 std::function<std::unique_ptr<Digitizing::EngineInterface>()>>{
85 { { "neuland", "tamex" },
86 [&tamex_par, pileup_strategy, cal_to_hit_par, enable_sim_cal = option.enable_sim_cal]()
87 {
89 UsePaddle<NeulandPaddle>(cal_to_hit_par),
90 UseChannel<TamexChannel>(pileup_strategy, tamex_par, cal_to_hit_par, enable_sim_cal));
91 } },
92 { { "neuland", "tacquila" },
93 [cal_to_hit_par]() {
94 return Digitizing::CreateEngine(UsePaddle<NeulandPaddle>(cal_to_hit_par),
95 UseChannel<TacquilaChannel>());
96 } },
97 { { "mock", "tamex" },
98 [&tamex_par, pileup_strategy, cal_to_hit_par, enable_sim_cal = option.enable_sim_cal]()
99 {
101 UsePaddle<MockPaddle>(),
102 UseChannel<TamexChannel>(pileup_strategy, tamex_par, cal_to_hit_par, enable_sim_cal));
103 } },
104 { { "neuland", "mock" },
105 [cal_to_hit_par]() {
106 return Digitizing::CreateEngine(UsePaddle<NeulandPaddle>(cal_to_hit_par),
107 UseChannel<MockChannel>());
108 } },
109 { { "mock", "mock" },
110 []() { return Digitizing::CreateEngine(UsePaddle<MockPaddle>(), UseChannel<MockChannel>()); } }
111 };
112 }
113
114 } // namespace
115
120
121 Digitizer::Digitizer(std::unique_ptr<Digitizing::EngineInterface> engine,
122 std::string_view points_name,
123 std::string_view hits_name,
124 std::string_view cal_hits_name)
125 : FairTask("R3BNeulandDigitizer")
126 , neuland_points_{ points_name }
127 , neuland_hits_{ hits_name }
128 , neuland_cal_hits_{ cal_hits_name }
129 , digitizing_engine_(std::move(engine))
130 {
131 }
132
133 void Digitizer::SetEngine(std::unique_ptr<Digitizing::EngineInterface> engine)
134 {
135 digitizing_engine_ = std::move(engine);
136 }
137
139 {
140 FairRunAna* run = FairRunAna::Instance();
141 if (run == nullptr)
142 {
143 throw R3B::runtime_error("R3BNeulandDigitizer::SetParContainers: No analysis run");
144 }
145
146 FairRuntimeDb* rtdb = run->GetRuntimeDb();
147 if (rtdb == nullptr)
148 {
149 throw R3B::runtime_error("R3BNeulandDigitizer::SetParContainers: No runtime database");
150 }
151
152 neuland_geo_par_ = dynamic_cast<R3BNeulandGeoPar*>(rtdb->getContainer("R3BNeulandGeoPar"));
153 if (neuland_geo_par_ == nullptr)
154 {
155 throw R3B::runtime_error("R3BNeulandDigitizer::SetParContainers: No R3BNeulandGeoPar");
156 }
157 }
158
159 auto Digitizer::Init() -> InitStatus
160 {
161 if (neuland_geo_par_ == nullptr)
162 {
163 throw R3B::runtime_error("neuland_geo_par is still nullptr during the initialization!");
164 }
166 neuland_points_.init();
167 neuland_hits_.init();
168 if (has_cal_output_)
169 {
170 neuland_cal_hits_.init();
171 }
173
174 return kSUCCESS;
175 }
176
178 {
179 // Initialize control histograms
180 auto const paddle_size = neuland_geo_par_->GetNumberOfModules();
181
182 hist_multi_one_ = data_monitor_.add_hist<TH1I>(
183 "MultiplicityOne", "Paddle multiplicity: only one PMT per paddle", paddle_size, 0, paddle_size);
184
185 hist_multi_two_ = data_monitor_.add_hist<TH1I>(
186 "MultiplicityTwo", "Paddle multiplicity: both PMTs of a paddle", paddle_size, 0, paddle_size);
187 auto const timeBinSize = 200;
189 data_monitor_.add_hist<TH1F>("hRLTimeToTrig", "R/Ltime-triggerTime", timeBinSize, -100., 100.);
191 {
192 hist_paddle_hit_size_ = data_monitor_.add_hist<TH1D>(
193 "paddle_hit_size", "Size of paddle hit per paddle", MAX_SIZE_BIN, 0.5, MAX_SIZE_BIN + 0.5);
195 "channel_signal_size", "Size of channel signals per channel", MAX_SIZE_BIN, 0.5, MAX_SIZE_BIN + 0.5);
196 hist_channel_hit_size_ = data_monitor_.add_hist<TH1D>(
197 "channel_hit_size", "Size of channel hits per channel", MAX_SIZE_BIN, 0.5, MAX_SIZE_BIN + 0.5);
198
199 hist_point_size_ = data_monitor_.add_hist<TH1D>(
200 "filtered_point_size", "Size of points per paddle", MAX_SIZE_BIN, 0.5, MAX_SIZE_BIN + 0.5);
201
202 // init point size checker
203 for (int paddle_id{}; paddle_id < paddle_size; ++paddle_id)
204 {
205 point_size_tracker_.emplace(paddle_id + 1, 0);
206 }
207 }
208 }
209
210 void Digitizer::Exec(Option_t* /*option*/)
211 {
212 neuland_hits_.clear();
213 if (has_cal_output_)
214 {
215 neuland_cal_hits_.clear();
216 }
217 digitizing_engine_->Reset();
218
220
221 digitizing_engine_->Construct();
222
224
225 auto paddle_action = [this](const Digitizing::AbstractPaddle& paddle)
226 {
227 if (!paddle.HasFired())
228 {
229 return;
230 }
231
233 {
234 fill_size_histograms(paddle);
235 }
236
237 if (has_cal_output_)
238 {
239 fill_cal_data(paddle);
240 }
241 fill_hit_data(paddle);
242 };
243
244 digitizing_engine_->DoEachPaddle(paddle_action);
245
246 LOG(debug) << fmt::format("Produced {} hits", neuland_hits_.size());
247 }
248
250 {
251
252 using R3B::Side;
253
254 const auto& paddle_hits = paddle.GetHits();
255 hist_paddle_hit_size_->Fill(static_cast<double>(paddle_hits.size()));
256
257 auto sides = std::array<Side, 2>{ Side::left, Side::right };
258 for (const auto side : sides)
259 {
260 const auto& channel = paddle.GetChannel(side);
261 hist_channel_hit_size_->Fill(static_cast<double>(channel.GetHits().size()));
262 hist_channel_signal_size_->Fill(static_cast<double>(channel.GetSignalSize()));
263 }
264 }
265
267 {
268 static constexpr auto GeVToMeVFac = 1000.;
269
270 // Look at each Land Point, if it deposited energy in the scintillator, store it with reference to the bar
271 for (const auto& point : neuland_points_)
272 {
274 (not neuland_point_filter_.IsPointAllowed(point)))
275 {
276 continue;
277 }
278 if (point.GetEnergyLoss() > 0.)
279 {
280 const auto paddle_ID = point.GetPaddle();
281
282 // Convert position of point to paddle-coordinates, including any rotation or translation
283 const auto& position = point.GetPosition();
284 const auto converted_position = neuland_geo_par_->ConvertToLocalCoordinates(position, paddle_ID);
285 LOGP(debug2, "Point in paddle: {}, with global position XYZ: {}", paddle_ID, position);
286 LOGP(debug2, "Converted to local position XYZ: {}", converted_position);
287
288 // Within the paddle frame, the relevant distance of the light from the pmt is always given by the
289 // X-Coordinate
290 const auto dist = converted_position.X();
291 digitizing_engine_->DepositLight(paddle_ID, point.GetTime(), point.GetLightYield() * GeVToMeVFac, dist);
293 {
294 ++(point_size_tracker_.at(paddle_ID));
295 }
296 } // eloss
297 } // points
298 }
299
301 {
302 // Fill control histograms
303 const auto triggerTime = digitizing_engine_->GetTriggerTime();
304
305 hist_rl_time_to_trig_->Fill(triggerTime);
306 hist_multi_one_->Fill(digitizing_engine_->DoAllPaddles(
307 [](auto paddles_view)
308 {
309 return static_cast<double>(std::count_if(paddles_view.begin(),
310 paddles_view.end(),
311 [](const auto& paddle) { return paddle.HasHalfFired(); }));
312 }));
313
314 hist_multi_two_->Fill(digitizing_engine_->DoAllPaddles(
315 [](auto paddles_view)
316 {
317 return static_cast<double>(std::count_if(
318 paddles_view.begin(), paddles_view.end(), [](const auto& paddle) { return paddle.HasFired(); }));
319 }));
320 if (has_size_monitor_)
321 {
322 for (const auto [key, value] : point_size_tracker_)
323 {
324 if (value == 0)
325 {
326 continue;
327 }
328 hist_point_size_->Fill(value);
329 }
330 }
331 }
332
333 auto Digitizer::Create(const DigiTaskOptions& option, FairRun* run) -> std::unique_ptr<Digitizer>
334 {
335 auto read_branch_names = std::vector<std::string>{};
336 auto write_branch_names = std::vector<std::string>{};
337
338 parse_io_branch_names(option, read_branch_names, 2, write_branch_names, 2); // NOLINT
339 auto engine_map = CreateDigiEngine(option, read_branch_names.at(1), run);
340 auto engine_gen = engine_map.at({ option.paddle, option.channel });
341 auto task = std::make_unique<Digitizer>(
342 engine_gen(), read_branch_names.at(0), write_branch_names.at(0), write_branch_names.at(1));
343 task->EnableCalDataOutput(option.enable_sim_cal);
344 task->EnableSizeMonitor(option.enable_size_monitor);
345 task->SetName(option.name.c_str());
346 auto point_filter = ParticleFilter::Create(option.point_filter);
347 LOG(info) << point_filter.Print();
348 task->SetPointFilter(std::move(point_filter));
349 return task;
350 }
351
353 {
354 const auto& signals = paddle.GetHits();
355
356 for (const auto& signal : signals)
357 {
358 const TVector3 hitPositionLocal = TVector3(signal.position, 0., 0.);
359 const TVector3 hitPositionGlobal =
360 neuland_geo_par_->ConvertToGlobalCoordinates(hitPositionLocal, paddle.GetPaddleID());
361 const TVector3 hitPixel = neuland_geo_par_->ConvertGlobalToPixel(hitPositionGlobal);
362
363 auto hit = R3BNeulandHit{ paddle.GetPaddleID(),
364 signal.left_channel_hit->tdc,
365 signal.right_channel_hit->tdc,
366 signal.time,
367 signal.left_channel_hit->qdcUnSat,
368 signal.right_channel_hit->qdcUnSat,
369 signal.energy,
370 hitPositionGlobal,
371 hitPixel };
372
373 if (hit_filters_.IsValid(hit))
374 {
375 neuland_hits_.get().emplace_back(std::move(hit));
376 LOGP(debug, "Adding neuland hit: {}", hit);
377 }
378 } // loop over all hits for each paddle
379 }
380
382 {
383 auto& cal_hits = neuland_cal_hits_.get();
384
385 const auto paddle_ID = paddle.GetPaddleID();
386 const auto& left_channel = paddle.GetLeftChannelRef();
387 const auto& right_channel = paddle.GetRightChannelRef();
388
389 const auto& left_channel_signals = left_channel.GetCalSignals();
390 const auto& right_channel_signals = right_channel.GetCalSignals();
391
392 if (left_channel_signals.size() != right_channel_signals.size())
393 {
394 return;
395 }
396
397 for (const auto& [left, right] : ranges::zip_view(left_channel_signals, right_channel_signals))
398 {
399
400 auto cal_data = SimCalData{ paddle_ID, left.tot, right.tot, left.tle, right.tle };
401
402 if (cal_hit_filter_.IsValid(cal_data))
403 {
404 cal_hits.push_back(std::move(cal_data));
405 LOGP(debug2, "Adding sim cal with {}", cal_data);
406 }
407 } // loop over signals
408 }
409
410 void Digitizer::FinishTask() { data_monitor_.save_to_sink(); }
411
413 {
415 {
416 for (auto& [key, value] : point_size_tracker_)
417 {
418 value = 0;
419 }
420 }
421 }
422
423} // namespace R3B::Neuland
constexpr auto MAX_SIZE_BIN
auto GetRightChannelRef() const -> auto &
auto GetLeftChannelRef() const -> auto &
auto GetChannel(R3B::Side side) const -> const Digitizing::AbstractChannel &
auto GetHits() const -> const std::vector< Hit > &
Digitizing::Neuland::TacQuila::Channel TacquilaChannel
void Exec(Option_t *) override
static auto Create(const R3B::Neuland::DigiTaskOptions &option, FairRun *run) -> std::unique_ptr< Digitizer >
Generator of the digitizing class.
Filterable< R3BNeulandHit & > hit_filters_
Digitizer()
Constructor with no input parameters.
R3B::Neuland::ParticleFilter neuland_point_filter_
R3BNeulandGeoPar * neuland_geo_par_
Digitizing::UsePaddle< Type > UsePaddle
std::unique_ptr< Digitizing::EngineInterface > digitizing_engine_
void fill_size_histograms(const Digitizing::AbstractPaddle &paddle)
std::unordered_map< int, int > point_size_tracker_
R3B::OutputVectorConnector< R3BNeulandHit > neuland_hits_
auto Init() -> InitStatus override
void fill_hit_data(const R3B::Digitizing::AbstractPaddle &paddle)
Digitizing::Neuland::Paddle NeulandPaddle
void fill_cal_data(const R3B::Digitizing::AbstractPaddle &paddle)
void SetEngine(std::unique_ptr< Digitizing::EngineInterface > engine)
Setter of the internal engine.
Filterable< R3B::Neuland::SimCalData & > cal_hit_filter_
R3B::InputVectorConnector< R3BNeulandPoint > neuland_points_
Digitizing::UseChannel< Type > UseChannel
R3B::OutputVectorConnector< R3B::Neuland::SimCalData > neuland_cal_hits_
static auto Create(const Options &options) -> ParticleFilter
Generator for the filter object from the configuration.
NeuLAND geometry parameter storage.
auto CreateEngine(Args &&... args) -> std::unique_ptr< decltype(Engine{ std::forward< Args >(args)... })>
Simulation of NeuLAND Bar/Paddle.
constexpr auto BarsPerPlane
void parse_io_branch_names(const Option &option, std::vector< std::string > &read, int read_num, std::vector< std::string > &write, int write_num)
Configuration struct for R3B::Neuland::Digitizer used in R3B::Neuland::AnalysisApplication.