R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BFileSource2.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 "R3BFileSource2.h"
15
16#include "R3BException.h"
17#include "R3BLogger.h"
18#include "R3BShared.h"
19#include <FairEventHeader.h>
20#include <FairFileHeader.h>
21#include <FairRootManager.h>
22#include <FairRun.h>
23#include <Rtypes.h>
24#include <RtypesCore.h>
25#include <TBranchElement.h>
26#include <TClonesArray.h>
27#include <TCollection.h>
28#include <TFolder.h>
29#include <TKey.h>
30#include <TObjString.h>
31#include <TObject.h>
32#include <algorithm>
33#include <array>
34#include <chrono>
35#include <cmath>
36#include <cstdint>
37#include <cstdio>
38#include <fairlogger/Logger.h>
39#include <fmt/chrono.h> // NOLINT
40#include <fmt/color.h>
41#include <fmt/core.h>
42#include <memory>
43#include <optional>
44#include <string>
45#include <string_view>
46#include <sys/types.h>
47#include <type_traits>
48#include <typeinfo>
49#include <utility>
50#include <vector>
51
52namespace
53{
54 constexpr auto DEFAULT_TITLE = "InputRootFile";
55
56 template <typename ContainerType, typename DataType>
57 auto Vector2TContainer(std::vector<DataType>& vec) -> std::unique_ptr<ContainerType>
58 {
59 using RawType = std::remove_reference_t<std::remove_pointer_t<DataType>>;
60 static_assert(std::is_base_of_v<TObject, RawType>);
61 auto list = std::make_unique<ContainerType>();
62 for (auto& iter : vec)
63 {
64 if constexpr (std::is_pointer_v<DataType>)
65 {
66 list->Add(iter);
67 }
68 else
69 {
70 list->Add(&iter);
71 }
72 }
73 return list;
74 }
75
76 template <typename StringType = std::string>
77 auto GetBranchList(TFile* rootFile, std::string_view listName) -> std::vector<StringType>
78 {
79 auto branchList = std::vector<StringType>{};
80 if (auto* list = dynamic_cast<TList*>(rootFile->Get(listName.data())); list != nullptr)
81 {
82 for (const auto& str : TRangeDynCast<TObjString>(list))
83 {
84 branchList.emplace_back(str->GetString().Data());
85 }
86 }
87 else
88 {
89 throw R3B::logic_error(
90 fmt::format("No branch list named {0} in input file {1}", listName, rootFile->GetName()));
91 }
92 return branchList;
93 }
94
95 template <typename UnaryFunc>
96 void loop_through_branch_elements(TFile* root_file, std::string_view tree_name, UnaryFunc action)
97 {
98 auto* tree = root_file->Get<TTree>(tree_name.data());
99 auto* branches = tree->GetListOfBranches();
100 R3BLOG(debug,
101 fmt::format("Get {} branches from the tree file {}", branches->GetEntries(), root_file->GetName()));
102 for (auto* branch_obj : TRangeDynCast<TObject>(branches))
103 {
104 auto* branch = dynamic_cast<TBranchElement*>(branch_obj);
105 if (branch == nullptr)
106 {
107 continue;
108 }
109 action(branch);
110 }
111 }
112
113 template <typename UnaryFunc>
114 void loop_through_branches(TFile* root_file, std::string_view tree_name, UnaryFunc action)
115 {
116 auto* tree = root_file->Get<TTree>(tree_name.data());
117 auto* branches = tree->GetListOfBranches();
118 R3BLOG(debug,
119 fmt::format("Get {} branches from the tree file {}", branches->GetEntries(), root_file->GetName()));
120 for (auto* branch_obj : TRangeDynCast<TObject>(branches))
121 {
122 auto* branch = dynamic_cast<TBranch*>(branch_obj);
123 if (branch == nullptr)
124 {
125 continue;
126 }
127 action(branch);
128 }
129 }
130
131 template <typename StringType = std::string>
132 auto GetBranchListFromTree(TFile* root_file, std::string_view tree_name) -> std::vector<StringType>
133 {
134 auto branch_name_list = std::vector<StringType>{};
135 loop_through_branch_elements(root_file,
136 tree_name,
137 [&branch_name_list](auto* branch)
138 { branch_name_list.emplace_back(branch->GetName()); });
139 return branch_name_list;
140 }
141
142 auto get_tca_data_class(TBranchElement* branch) -> std::string
143 {
144 TClonesArray* buffer = nullptr;
145 branch->SetAddress(&buffer); // NOLINT: Nasty ROOT API
146 branch->GetEntry(0);
147 branch->SetAddress(nullptr);
148 if (buffer != nullptr)
149 {
150 auto class_name = std::string{ buffer->GetClass()->GetName() };
151 R3BLOG(debug,
152 fmt::format("Determine the class name {:?} of the branch {:?}", class_name, branch->GetName()));
153 return class_name;
154 }
155 R3BLOG(warn, fmt::format("Cannot determine the class name of the branch {:?}", branch->GetName()));
156 return std::string{ "TObject" };
157 }
158
159 void add_branches_to_folder(TFolder* folder, TFile* root_file, std::string_view tree_name)
160 {
161 loop_through_branch_elements(root_file,
162 tree_name,
163 [folder](auto* branch)
164 {
165 auto class_name = std::string_view{ branch->GetClassName() };
166 if (class_name == "TClonesArray")
167 {
168 const auto data_class = get_tca_data_class(branch);
169 auto tca_obj = std::make_unique<TClonesArray>(data_class.data());
170 tca_obj->SetName(branch->GetName());
171 folder->Add(tca_obj.release());
172 }
173 });
174
175 // TODO: what if it's branch with signle literal value?
176 }
177
178 auto HasBranchList(TFile* rootFile, const std::vector<std::string>& branchList) -> bool
179 {
180 auto const newBranchList = GetBranchList(rootFile, "BranchList");
181 auto view1 = std::vector<std::string_view>(branchList.begin(), branchList.end());
182 auto view2 = std::vector<std::string_view>(newBranchList.begin(), newBranchList.end());
183
184 std::sort(view1.begin(), view1.end());
185 std::sort(view2.begin(), view2.end());
186 return view1 == view2;
187 }
188
189 template <typename ContainerType>
190 auto GetDataFromAnyFolder(TFile* rootFile, const ContainerType& folderNames) -> std::optional<TKey*>
191 {
192 for (auto const& name : folderNames)
193 {
194 R3BLOG(debug, "looking for " + name);
195 auto* dataFolder = dynamic_cast<TKey*>(rootFile->FindKey(name.c_str()));
196 if (dataFolder != nullptr)
197 {
198 R3BLOG(debug, name + " has been found!");
199 return dataFolder;
200 }
201 }
202 return {};
203 }
204
205 auto Get_TChain_FromFairRM(FairRootManager* rootMan) -> TChain*
206 {
207 auto const chainTitle = "/" + std::string{ FairRootManager::GetFolderName() };
208 auto inChain = std::make_unique<TChain>(FairRootManager::GetTreeName(), chainTitle.c_str());
209 R3BLOG(debug, "Chain created");
210 LOG(info) << "chain name: " << FairRootManager::GetTreeName();
211 rootMan->SetInChain(inChain.release());
212 return FairRootManager::Instance()->GetInChain();
213 }
214} // namespace
215
217{
218 if (rate <= 0.)
219 {
220 throw R3B::logic_error(fmt::format("Refresh rate {} must be a positive floating point value", rate));
221 }
222
223 refresh_rate_ = rate;
224 refresh_period_ = std::chrono::milliseconds(static_cast<int>(1000. / rate));
225}
226
228{
229 if (event_num < 1)
230 {
231 return;
232 }
233 if (max_event_num_ == 0)
234 {
235 throw R3B::logic_error("Maximal event number has not been set up!");
236 }
237 const auto now_t = std::chrono::steady_clock::now();
238 const auto time_spent = std::chrono::ceil<std::chrono::milliseconds>(now_t - previous_t_);
239 if (time_spent > refresh_period_)
240 {
241 const auto processed_events = event_num - previous_event_num_;
242 const auto events_per_millisecond =
243 static_cast<double>(processed_events) / static_cast<double>(time_spent.count());
244 Print(event_num, events_per_millisecond);
245
246 previous_t_ = now_t;
247 previous_event_num_ = event_num;
248 }
249}
250
251void R3BEventProgressPrinter::Print(uint64_t event_num, double speed_per_ms)
252{
253 if (speed_per_ms <= 0.)
254 {
255 return;
256 }
257 const auto event_num_str =
258 fmt::format(fg(fmt::terminal_color::bright_green) | fmt::emphasis::bold, "{:^5d}k", event_num / 1000);
259 const auto speed_str = fmt::format(fg(fmt::color::white), "{:^6.1F}k/s", speed_per_ms);
260 const auto progress_str = fmt::format(fg(fmt::terminal_color::bright_yellow) | fmt::emphasis::bold,
261 "{:^6.2F}",
262 100. * static_cast<double>(event_num) / static_cast<double>(max_event_num_));
263 const auto time_left_ms =
264 std::chrono::milliseconds{ (max_event_num_ - event_num) / static_cast<int>(std::ceil(speed_per_ms)) };
265 fmt::print("Events processed: {0} ({1}) Progress: {2}% (time left: {3:%H h %M m %S s}) Run ID: {4}\r",
266 event_num_str,
267 speed_str,
268 progress_str,
269 std::chrono::ceil<std::chrono::seconds>(time_left_ms),
270 run_id_);
271 std::fflush(stdout);
272}
273
274auto R3BInputRootFiles::AddFileName(std::string fileName, bool is_tree_file) -> std::optional<std::string>
275{
276 auto const msg = fmt::format("Adding {} to file source\n", fileName);
277 R3BLOG(info, msg);
278 if (fileNames_.empty())
279 {
280 Intitialize(fileName, is_tree_file);
282 }
283 if (!ValidateFile(fileName, is_tree_file))
284 {
285 return fileName;
286 }
287 fileNames_.emplace_back(std::move(fileName));
288 return {};
289}
290
292{
293
294 for (auto const& branchName : branchList_)
295 {
296 FairRootManager::Instance()->AddBranchToList(branchName.c_str());
297 }
298}
299
301{
302 if (rootChain_ != nullptr)
303 {
304 throw R3B::logic_error("TChain has already been created!");
305 }
306 rootChain_ = chain;
307 for (auto const& filename : fileNames_)
308 {
309 rootChain_->AddFile(filename.c_str(), TTree::kMaxEntries, treeName_.c_str());
310 }
311}
312void R3BInputRootFiles::RegisterTo(FairRootManager* rootMan)
313{
314 if (is_friend_)
315 {
316 return;
317 }
318
319 if (validMainFolders_.empty())
320 {
321 throw R3B::runtime_error("There is no main folder to be registered!");
322 }
323
324 if (!is_friend_)
325 {
326 auto listOfFolders = Vector2TContainer<TObjArray>(validMainFolders_);
327 R3BLOG(debug, fmt::format("Set {} main folder(s) to FairRootManager.", listOfFolders->GetEntries()));
328 rootMan->SetListOfFolders(listOfFolders.release());
329 rootMan->SetTimeBasedBranchNameList(Vector2TContainer<TList>(timeBasedBranchList_).release());
330 SetInputFileChain(Get_TChain_FromFairRM(rootMan));
331 }
332}
333
334auto R3BInputRootFiles::ExtractMainFolder(TFile* rootFile) -> std::optional<TKey*>
335{
336 auto const folderNames =
337 std::array<std::string, 4>{ FairRootManager::GetFolderName(), "r3broot", "cbmout", "cbmroot" };
338
339 return GetDataFromAnyFolder(rootFile, folderNames);
340}
341
342auto R3BInputRootFiles::ValidateFile(const std::string& filename, bool is_tree_file) -> bool
343{
344 auto rootFile = R3B::make_rootfile(filename.c_str());
345
346 if (is_tree_file)
347 {
348 if (!is_friend_)
349 {
350 auto folder = std::make_unique<TFolder>("r3broot", "r3broot");
351 add_branches_to_folder(folder.get(), rootFile.get(), treeName_);
352 validRootFiles_.push_back(std::move(rootFile));
353 validMainFolders_.push_back(folder.release());
354 }
355 return true;
356 }
357
358 auto folderKey = ExtractMainFolder(rootFile.get());
359 auto res1 = folderKey.has_value();
360 auto res2 = HasBranchList(rootFile.get(), branchList_);
361 if (res1 and res2)
362 {
363 if (!folderName_.empty() && (folderKey.value()->GetName() != folderName_))
364 {
365 R3BLOG(warn, "Different folder name!");
366 }
367 if (!is_friend_)
368 {
369 validRootFiles_.push_back(std::move(rootFile));
370 validMainFolders_.push_back((folderKey.value())->ReadObject<TFolder>());
371 }
372 }
373 else
374 {
375 if (not res1)
376 {
377 R3BLOG(warn, "folder has no key");
378 }
379 if (not res2)
380 {
381 R3BLOG(warn, "HasBranchList is false!");
382 }
383 }
384 return res1 and res2;
385}
386
387auto R3BInputRootFiles::ExtractRunId(TFile* rootFile) -> std::optional<uint>
388{
389 //
390 auto* header = rootFile->Get<FairFileHeader>(fileHeader_.c_str());
391 if (header == nullptr)
392 {
393 return {};
394 }
395 auto runID = header->GetRunId();
396 return runID;
397}
398
399void R3BInputRootFiles::Intitialize(std::string_view filename, bool is_tree_file)
400{
401 auto file = R3B::make_rootfile(filename.data());
402
403 if (is_tree_file)
404 {
405 branchList_ = GetBranchListFromTree(file.get(), treeName_);
406 return;
407 }
408
409 if (const auto runID = ExtractRunId(file.get()); runID.has_value() && runID.value() != 0)
410 {
411 auto const msg = fmt::format(R"(Successfully extract RunID "{}" from root file "{}")", runID.value(), filename);
412 R3BLOG(debug, msg);
413 initial_RunID_ = runID.value();
414 }
415 else
416 {
417 auto const msg = fmt::format("Failed to extract RunID from root file \"{}\"", filename);
418 R3BLOG(error, msg);
419 }
420
421 if (auto folderKey = ExtractMainFolder(file.get()); folderKey.has_value())
422 {
423 folderName_ = folderKey.value()->GetName();
424 }
425 else
426 {
427 throw R3B::logic_error(fmt::format("Cannot find main folder from the root file {}!", filename));
428 }
429
430 branchList_ = GetBranchList(file.get(), "BranchList");
431
432 if (timeBasedBranchList_ = GetBranchList<TObjString>(file.get(), "TimeBasedBranchList");
433 timeBasedBranchList_.empty())
434 {
435 LOG(warn) << "No time based branch list in input file";
436 }
437}
438
440{
441 if (is_friend_)
442 {
443 throw R3B::logic_error("Can not set friendFiles with another friendFile!");
444 }
445 auto chain = std::make_unique<TChain>(friendFiles.GetTitle().c_str(), friendFiles.GetFolderName().c_str());
446 friendFiles.SetInputFileChain(chain.get());
447 rootChain_->AddFriend(chain.release());
448}
449
450[[nodiscard]] auto R3BInputRootFiles::GetEntries() const -> int64_t
451{
452 if (rootChain_ == nullptr)
453 {
454 throw R3B::logic_error("Can't get entries before being initialized!");
455 }
456 return rootChain_->GetEntries();
457}
458
459R3BFileSource2::R3BFileSource2(std::vector<std::string> fileNames, std::string_view title)
460{
461 LOG(debug) << "Creating a new R3BFileSource!";
462 inputDataFiles_.SetTitle(title);
463 inputDataFiles_.SetFileHeaderName("FileHeader");
464 for (auto& name : fileNames)
465 {
466 if (name.empty())
467 {
468 continue;
469 }
470 AddFile(std::move(name));
471 }
472}
473
474R3BFileSource2::R3BFileSource2(std::string file, std::string_view title)
475 : R3BFileSource2(std::vector<std::string>{ std::move(file) }, title)
476{
477}
478
479R3BFileSource2::R3BFileSource2(std::vector<std::string> fileNames)
480 : R3BFileSource2(std::move(fileNames), DEFAULT_TITLE)
481{
482}
483
485 : R3BFileSource2(std::string{})
486{
487}
488
489void R3BFileSource2::AddFile(std::string file_name, bool is_tree_file)
490{
491 if (auto const res = inputDataFiles_.AddFileName(std::move(file_name), is_tree_file); res.has_value())
492 {
493 if (not dataFileNames_.empty())
494 {
495
496 R3BLOG(
497 error,
498 fmt::format(
499 "Root file {0} is incompatible with the first root file {1}", res.value(), dataFileNames_.front()));
500 }
501 else
502 {
503 R3BLOG(error, fmt::format("Failed to add the first root file {:?}", file_name));
504 }
505 }
506 dataFileNames_.emplace_back(file_name);
507}
508
509void R3BFileSource2::AddFile(std::vector<std::string> file_names, bool is_tree_file)
510{
511 for (auto& file_name : file_names)
512 {
513 AddFile(std::move(file_name), is_tree_file);
514 }
515}
516
517void R3BFileSource2::AddFriend(std::vector<std::string> file_names, bool is_tree_file)
518{
519 for (auto& file_name : file_names)
520 {
521 AddFriend(std::move(file_name), is_tree_file);
522 }
523}
524
525void R3BFileSource2::AddFriend(std::string file_name, bool is_tree_file)
526{
527 //
528 auto rootfile = R3B::make_rootfile(file_name.c_str());
529 auto friendGroup = std::find_if(inputFriendFiles_.begin(),
530 inputFriendFiles_.end(),
531 [&rootfile](const auto& friends)
532 { return HasBranchList(rootfile.get(), friends.GetBranchListRef()); });
533 if (friendGroup == inputFriendFiles_.end())
534 {
535 auto newFriendGroup = R3BInputRootFiles{};
536 newFriendGroup.Make_as_friend();
537 inputFriendFiles_.push_back(std::move(newFriendGroup));
538 friendGroup = --inputFriendFiles_.end();
539 friendGroup->SetTitle(fmt::format("FriendTree_{}", inputFriendFiles_.size()));
540 }
541 auto res = friendGroup->AddFileName(file_name, is_tree_file);
542 if (res.has_value())
543 {
544 R3BLOG(error,
545 fmt::format("Friend file {0} is incompatible with the first friend file {1}",
546 res.value(),
547 friendGroup->GetBaseFileName()));
548 }
549 else
550 {
551 // TODO: really need it?
552 friendFileNames_.emplace_back(std::move(file_name));
553 }
554}
555
557{
558 if (inputDataFiles_.is_empty())
559 {
560 throw R3B::logic_error{ "No input file available!" };
561 }
562
563 inputDataFiles_.RegisterTo(FairRootManager::Instance());
564
565 for (auto& friendGroup : inputFriendFiles_)
566 {
567 inputDataFiles_.SetFriend(friendGroup);
568 }
569
570 event_progress_.SetMaxEventNum(inputDataFiles_.GetEntries());
571 event_progress_.SetRunID(inputDataFiles_.GetInitialRunID());
572
573 return true;
574}
575
576void R3BFileSource2::FillEventHeader(FairEventHeader* evtHeader)
577{
578 if (evtHeader == nullptr)
579 {
580 throw R3B::logic_error("Filled event header is empty!");
581 }
582 evtHeader_ = evtHeader;
583
584 // Set runID for event header:
585 auto const init_runID = inputDataFiles_.GetInitialRunID();
586
587 if (init_runID == 0)
588 {
589 throw R3B::logic_error("RunId is not being set!");
590 }
591
592 if (init_runID != GetRunId())
593 {
594 R3BLOG(
595 warn,
596 fmt::format("runID {} being set is different from the runID {} in the data file!", GetRunId(), init_runID));
597 }
598 SetRunId(init_runID); // NOLINT
599 evtHeader->SetRunId(init_runID);
600}
601
603{
604 event_end_ = (EvtEnd <= 0) ? inputDataFiles_.GetEntries() : EvtEnd; // NOLINT
605 R3BLOG(info, fmt::format("Setting printing event max to {}", event_end_));
606 event_progress_.SetMaxEventNum(event_end_);
607 return event_end_;
608}
609
610void R3BFileSource2::ReadBranchEvent(const char* BrName)
611{
612 auto const currentEventID = evtHeader_->GetMCEntryNumber();
613 ReadBranchEvent(BrName, currentEventID);
614}
615
616void R3BFileSource2::ReadBranchEvent(const char* BrName, Int_t entryID)
617{
618 auto const read_bytes = inputDataFiles_.GetChain()->FindBranch(BrName)->GetEntry(entryID);
619 if (read_bytes == 0)
620 {
621 LOG(warn) << fmt::format("Failed to read the data of the event {0} from the branch {1}", entryID, BrName);
622 }
623}
624
625Int_t R3BFileSource2::ReadEvent(UInt_t eventID)
626{
627 auto* chain = inputDataFiles_.GetChain();
628 if (fair::Logger::GetConsoleSeverity() == fair::Severity::info)
629 {
630 event_progress_.ShowProgress(eventID);
631 }
632
633 auto read_bytes = chain->GetEntry(eventID);
634 if (read_bytes == 0)
635 {
636 LOG(warn) << fmt::format("Failed to read the data of the event {0} from the source", eventID);
637 return 1;
638 }
639 return 0;
640}
641
642Bool_t R3BFileSource2::ActivateObject(TObject** obj, const char* BrName)
643{
644 auto* chain = inputDataFiles_.GetChain();
645 chain->SetBranchStatus(BrName, true);
646 chain->SetBranchAddress(BrName, obj);
647 return kTRUE;
648}
649
650Bool_t R3BFileSource2::ActivateObjectAny(void** obj, const std::type_info& info, const char* BrName)
651{
652 auto* chain = inputDataFiles_.GetChain();
653 if (chain != nullptr)
654 {
655 return ActivateObjectAnyImpl(chain, obj, info, BrName);
656 }
657 return kFALSE;
658}
659
ClassImp(R3BFileSource2)
#define R3BLOG(severity, x)
Definition R3BLogger.h:32
void SetRefreshRate_Hz(float rate)
std::chrono::milliseconds refresh_period_
void Print(uint64_t event_num, double speed_per_ms)
void ShowProgress(uint64_t event_num)
std::chrono::time_point< std::chrono::steady_clock > previous_t_
Int_t ReadEvent(UInt_t eventID=0) override
Bool_t ActivateObject(TObject **obj, const char *BrName) override
void ReadBranchEvent(const char *BrName) override
R3BEventProgressPrinter event_progress_
std::vector< R3BInputRootFiles > inputFriendFiles_
void AddFriend(std::string, bool is_tree_file=false)
Int_t CheckMaxEventNo(Int_t EvtEnd=0) override
R3BInputRootFiles inputDataFiles_
Bool_t ActivateObjectAny(void **obj, const std::type_info &info, const char *BrName) override
FairEventHeader * evtHeader_
void AddFile(std::string file_name, bool is_tree_file=false)
std::vector< std::string > dataFileNames_
void FillEventHeader(FairEventHeader *evtHeader) override
std::vector< std::string > friendFileNames_
Bool_t Init() override
static auto ExtractMainFolder(TFile *) -> std::optional< TKey * >
std::vector< TObjString > timeBasedBranchList_
auto GetTitle() const -> const auto &
R3BInputRootFiles()=default
auto GetEntries() const -> int64_t
void SetFriend(R3BInputRootFiles &friendFiles)
std::string folderName_
void Intitialize(std::string_view filename, bool is_tree_file=false)
void RegisterTo(FairRootManager *)
std::string fileHeader_
auto AddFileName(std::string name, bool is_tree_file=false) -> std::optional< std::string >
std::vector< TFolder * > validMainFolders_
void SetInputFileChain(TChain *chain)
auto GetFolderName() const -> const auto &
auto ValidateFile(const std::string &filename, bool is_tree_file=false) -> bool
std::string treeName_
std::vector< R3B::unique_rootfile > validRootFiles_
auto ExtractRunId(TFile *rootFile) -> std::optional< uint >
auto make_rootfile(Args &&... args)
Definition R3BShared.h:94