Project
Loading...
Searching...
No Matches
MergerAlgorithm.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
16
18
20#include "Mergers/ObjectStore.h"
21#include "Framework/Logger.h"
22
23#include <TEfficiency.h>
24#include <TGraph.h>
25#include <TH1.h>
26#include <TH2.h>
27#include <TH3.h>
28#include <THn.h>
29#include <THnSparse.h>
30#include <TObjArray.h>
31#include <TObject.h>
32#include <TTree.h>
33#include <TPad.h>
34#include <TCanvas.h>
35#include <algorithm>
36#include <stdexcept>
37
39{
40
41size_t estimateTreeSize(TTree* tree)
42{
43 size_t totalSize = 0;
44 auto branchList = tree->GetListOfBranches();
45 for (const auto* branch : *branchList) {
46 totalSize += dynamic_cast<const TBranch*>(branch)->GetTotalSize();
47 }
48 return totalSize;
49}
50
51// Mergeable objects are kept as primitives in TCanvas object in underlying TPad.
52// TPad is a linked list of primitives of any type (https://root.cern.ch/doc/master/classTPad.html)
53// including other TPads. So in order to collect all mergeable objects from TCanvas
54// we need to recursively transverse whole TPad structure.
55auto collectUnderlyingObjects(TCanvas* canvas) -> std::vector<TObject*>
56{
57 auto collectFromTPad = [](TPad* pad, std::vector<TObject*>& objects, const auto& collectFromTPad) {
58 if (!pad) {
59 return;
60 }
61 auto* primitives = pad->GetListOfPrimitives();
62 for (int i = 0; i < primitives->GetSize(); ++i) {
63 auto* primitive = primitives->At(i);
64 if (auto* primitivePad = dynamic_cast<TPad*>(primitive)) {
65 collectFromTPad(primitivePad, objects, collectFromTPad);
66 } else {
67 objects.push_back(primitive);
68 }
69 }
70 };
71
72 std::vector<TObject*> collectedObjects;
73 collectFromTPad(canvas, collectedObjects, collectFromTPad);
74
75 return collectedObjects;
76}
77
84
85auto matchCollectedToPairs(const std::vector<TObject*>& targetObjects, const std::vector<TObject*> otherObjects) -> std::vector<MatchedCollectedObjects>
86{
87 std::vector<MatchedCollectedObjects> matchedObjects;
88 matchedObjects.reserve(std::max(targetObjects.size(), otherObjects.size()));
89 for (const auto& targetObject : targetObjects) {
90 if (const auto found_it = std::ranges::find_if(otherObjects, [&targetObject](TObject* obj) { return std::string_view(targetObject->GetName()) == std::string_view(obj->GetName()); });
91 found_it != otherObjects.end()) {
92 matchedObjects.emplace_back(targetObject, *found_it);
93 }
94 }
95 return matchedObjects;
96}
97
98void merge(TObject* const target, TObject* const other)
99{
100 if (target == nullptr) {
101 throw std::runtime_error("Merging target is nullptr");
102 }
103 if (other == nullptr) {
104 throw std::runtime_error("Object to be merged in is nullptr");
105 }
106 if (other == target) {
107 throw std::runtime_error("Merging target and the other object point to the same address");
108 }
109 // fixme: should we check if names match?
110
111 // We expect that both objects follow the same structure, but we allow to add missing objects to TCollections.
112 // First we check if an object contains a MergeInterface, as it should overlap default Merge() methods of TObject.
113 if (auto custom = dynamic_cast<MergeInterface*>(target)) {
114
115 custom->merge(dynamic_cast<MergeInterface* const>(other));
116
117 } else if (auto targetCollection = dynamic_cast<TCollection*>(target)) {
118
119 auto otherCollection = dynamic_cast<TCollection*>(other);
120 if (otherCollection == nullptr) {
121 throw std::runtime_error(std::string("The target object '") + target->GetName() +
122 "' is a TCollection, while the other object '" + other->GetName() + "' is not.");
123 }
124
125 auto otherIterator = otherCollection->MakeIterator();
126 while (auto otherObject = otherIterator->Next()) {
127 TObject* targetObject = targetCollection->FindObject(otherObject->GetName());
128 if (targetObject) {
129 // That might be another collection or a concrete object to be merged, we walk on the collection recursively.
130 merge(targetObject, otherObject);
131 } else {
132 // We prefer to clone instead of passing the pointer in order to simplify deleting the `other`.
133 targetCollection->Add(otherObject->Clone());
134 }
135 }
136 delete otherIterator;
137 } else if (auto targetCanvas = dynamic_cast<TCanvas*>(target)) {
138
139 auto otherCanvas = dynamic_cast<TCanvas*>(other);
140 if (otherCanvas == nullptr) {
141 throw std::runtime_error(std::string("The target object '") + target->GetName() +
142 "' is a TCanvas, while the other object '" + other->GetName() + "' is not.");
143 }
144
145 const auto targetObjects = collectUnderlyingObjects(targetCanvas);
146 const auto otherObjects = collectUnderlyingObjects(otherCanvas);
147 if (targetObjects.size() != otherObjects.size()) {
148 throw std::runtime_error(std::string("Trying to merge canvas: ") + targetCanvas->GetName() + " and canvas " + otherObjects.size() + "but contents are not the same");
149 }
150
151 const auto matched = matchCollectedToPairs(targetObjects, otherObjects);
152 if (targetObjects.size() != matched.size()) {
153 throw std::runtime_error(std::string("Trying to merge canvas: ") + targetCanvas->GetName() + " and canvas " + otherObjects.size() + "but contents are not the same");
154 }
155
156 for (const auto& [targetObject, otherObject] : matched) {
157 merge(targetObject, otherObject);
158 }
159
160 } else {
161 Long64_t errorCode = 0;
162 TObjArray otherCollection;
163 otherCollection.SetOwner(false);
164 otherCollection.Add(other);
165
166 if (target->InheritsFrom(TH1::Class())) {
167 // this includes TH1, TH2, TH3
168 auto targetTH1 = reinterpret_cast<TH1*>(target);
169 if (targetTH1->TestBit(TH1::kIsAverage)) {
170 // Merge() does not support averages, we have to use Add()
171 // this will break if collection.size != 1
172 if (auto otherTH1 = dynamic_cast<TH1*>(otherCollection.First())) {
173 errorCode = targetTH1->Add(otherTH1);
174 }
175 } else {
176 // Add() does not support histograms with labels, thus we resort to Merge() by default
177 errorCode = targetTH1->Merge(&otherCollection);
178 }
179 } else if (target->InheritsFrom(THnBase::Class())) {
180 // this includes THn and THnSparse
181 errorCode = reinterpret_cast<THnBase*>(target)->Merge(&otherCollection);
182 } else if (target->InheritsFrom(TTree::Class())) {
183 auto targetTree = reinterpret_cast<TTree*>(target);
184 auto otherTree = reinterpret_cast<TTree*>(other);
185 auto targetTreeSize = estimateTreeSize(targetTree);
186 auto otherTreeSize = estimateTreeSize(otherTree);
187 if (auto totalSize = targetTreeSize + otherTreeSize; totalSize > 100000000) {
188 LOG(warn) << "The tree '" << targetTree->GetName() << "' would be larger than 100MB (" << totalSize << "B) after merging, skipping to let the system survive";
189 errorCode = 0;
190 } else {
191 errorCode = targetTree->Merge(&otherCollection);
192 }
193 } else if (target->InheritsFrom(TGraph::Class())) {
194 errorCode = reinterpret_cast<TGraph*>(target)->Merge(&otherCollection);
195 } else if (target->InheritsFrom(TEfficiency::Class())) {
196 errorCode = reinterpret_cast<TEfficiency*>(target)->Merge(&otherCollection);
197 } else {
198 LOG(warn) << "Object '" + std::string(target->GetName()) + "' with type '" + std::string(target->ClassName()) + "' is not one of the mergeable types, skipping";
199 }
200 if (errorCode == -1) {
201 LOG(error) << "Failed to merge the input object '" + std::string(other->GetName()) + "' of type '" + std::string(other->ClassName()) //
202 + " and the target object '" + std::string(target->GetName()) + "' of type '" + std::string(target->ClassName()) + "'";
203 }
204 }
205}
206
207void merge(VectorOfTObjectPtrs& targets, const VectorOfTObjectPtrs& others)
208{
209 for (const auto& other : others) {
210 if (const auto targetSameName = std::find_if(targets.begin(), targets.end(), [&other](const auto& target) {
211 return std::string_view{other->GetName()} == std::string_view{target->GetName()};
212 });
213 targetSameName != targets.end()) {
214 merge(targetSameName->get(), other.get());
215 } else {
216 targets.push_back(std::shared_ptr<TObject>(other->Clone(), deleteTCollections));
217 }
218 }
219}
220
221void deleteRecursive(TCollection* Coll)
222{
223 // I can iterate a collection
224 Coll->SetOwner(false);
225 auto ITelem = Coll->MakeIterator();
226 while (auto* element = ITelem->Next()) {
227 if (auto* Coll2 = dynamic_cast<TCollection*>(element)) {
228 Coll2->SetOwner(false);
229 deleteRecursive(Coll2);
230 }
231 Coll->Remove(element); // Remove from mother collection
232 delete element; // Delete payload
233 }
234 delete ITelem;
235}
236
238{
239 if (auto* L = dynamic_cast<TCollection*>(obj)) {
241 delete L;
242 } else {
243 delete obj;
244 }
245}
246
247} // namespace o2::mergers::algorithm
std::vector< std::string > objects
int32_t i
bool o
Definition of O2 Mergers merging interface, v0.1.
Algorithms for merging objects.
Definition of ObjectStore for Mergers, v0.1.
An interface which allows to merge custom objects.
GLenum target
Definition glcorearb.h:1641
void deleteRecursive(TCollection *Coll)
auto matchCollectedToPairs(const std::vector< TObject * > &targetObjects, const std::vector< TObject * > otherObjects) -> std::vector< MatchedCollectedObjects >
void merge(TObject *const target, TObject *const other)
A function which merges TObjects.
void deleteTCollections(TObject *obj)
auto collectUnderlyingObjects(TCanvas *canvas) -> std::vector< TObject * >
size_t estimateTreeSize(TTree *tree)
std::vector< TObjectPtr > VectorOfTObjectPtrs
Definition ObjectStore.h:43
VectorOfTObjectPtrs other
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))