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