Skip to content

Commit e76663c

Browse files
author
Michal Tichák
committed
added merging of TCanvas to mergers
1 parent c4f4a41 commit e76663c

File tree

3 files changed

+191
-4
lines changed

3 files changed

+191
-4
lines changed

Utilities/Mergers/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ o2_add_library(Mergers
1515
SOURCES src/FullHistoryMerger.cxx src/IntegratingMerger.cxx src/Mergeable.cxx
1616
src/MergerAlgorithm.cxx src/MergerBuilder.cxx src/MergerInfrastructureBuilder.cxx
1717
src/ObjectStore.cxx
18-
PUBLIC_LINK_LIBRARIES O2::Framework AliceO2::InfoLogger)
18+
PUBLIC_LINK_LIBRARIES O2::Framework AliceO2::InfoLogger ROOT::Gpad)
1919

2020
o2_target_root_dictionary(
2121
Mergers

Utilities/Mergers/src/MergerAlgorithm.cxx

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616

1717
#include "Mergers/MergerAlgorithm.h"
1818

19-
#include "Framework/Logger.h"
2019
#include "Mergers/MergeInterface.h"
2120
#include "Mergers/ObjectStore.h"
21+
#include "Framework/Logger.h"
2222

2323
#include <TEfficiency.h>
2424
#include <TGraph.h>
@@ -28,7 +28,12 @@
2828
#include <THn.h>
2929
#include <THnSparse.h>
3030
#include <TObjArray.h>
31+
#include <TObject.h>
3132
#include <TTree.h>
33+
#include <TPad.h>
34+
#include <TCanvas.h>
35+
#include <algorithm>
36+
#include <stdexcept>
3237

3338
namespace o2::mergers::algorithm
3439
{
@@ -43,6 +48,53 @@ size_t estimateTreeSize(TTree* tree)
4348
return totalSize;
4449
}
4550

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.
55+
auto 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+
78+
struct MatchedCollectedObjects {
79+
MatchedCollectedObjects(TObject* t, TObject* o) : target(t), other(o) {}
80+
81+
TObject* target;
82+
TObject* other;
83+
};
84+
85+
auto 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+
4698
void merge(TObject* const target, TObject* const other)
4799
{
48100
if (target == nullptr) {
@@ -82,6 +134,29 @@ void merge(TObject* const target, TObject* const other)
82134
}
83135
}
84136
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+
85160
} else {
86161
Long64_t errorCode = 0;
87162
TObjArray otherCollection;
@@ -169,4 +244,4 @@ void deleteTCollections(TObject* obj)
169244
}
170245
}
171246

172-
} // namespace o2::mergers::algorithm
247+
} // namespace o2::mergers::algorithm

Utilities/Mergers/test/test_Algorithm.cxx

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
///
1515
/// \author Piotr Konopka, piotr.jan.konopka@cern.ch
1616

17-
#include <boost/test/tools/interface.hpp>
1817
#include <gsl/span>
1918
#include <memory>
19+
#include <stdexcept>
2020
#define BOOST_TEST_MODULE Test Utilities MergerAlgorithm
2121
#define BOOST_TEST_MAIN
2222
#define BOOST_TEST_DYN_LINK
@@ -39,6 +39,7 @@
3939
#include <TF1.h>
4040
#include <TGraph.h>
4141
#include <TProfile.h>
42+
#include <TCanvas.h>
4243

4344
// using namespace o2::framework;
4445
using namespace o2::mergers;
@@ -305,6 +306,117 @@ BOOST_AUTO_TEST_CASE(MergerCollection)
305306
delete target;
306307
}
307308

309+
TCanvas* createCanvas(std::string name, std::string title, std::vector<std::shared_ptr<TH1I>>& histograms)
310+
{
311+
auto canvas = new TCanvas(name.c_str(), title.c_str(), 100, 100);
312+
canvas->Divide(histograms.size(), 1);
313+
for (size_t i = 1; const auto& hist : histograms) {
314+
canvas->cd(i);
315+
hist->Draw();
316+
++i;
317+
}
318+
return canvas;
319+
}
320+
321+
auto collectUnderlyingObjects(TCanvas* canvas) -> std::vector<TObject*>
322+
{
323+
auto collectFromTPad = [](TPad* pad, std::vector<TObject*>& objects, const auto& collectFromTPad) {
324+
if (!pad) {
325+
return;
326+
}
327+
auto* primitives = pad->GetListOfPrimitives();
328+
for (int i = 0; i < primitives->GetSize(); ++i) {
329+
auto* primitive = primitives->At(i);
330+
if (auto* primitivePad = dynamic_cast<TPad*>(primitive)) {
331+
collectFromTPad(primitivePad, objects, collectFromTPad);
332+
} else {
333+
objects.push_back(primitive);
334+
}
335+
}
336+
};
337+
338+
std::vector<TObject*> collectedObjects;
339+
collectFromTPad(canvas, collectedObjects, collectFromTPad);
340+
341+
return collectedObjects;
342+
}
343+
344+
BOOST_AUTO_TEST_CASE(MergerTCanvas)
345+
{
346+
// working example
347+
{
348+
std::vector<std::shared_ptr<TH1I>> histsC1{
349+
std::make_shared<TH1I>("th1", "obj1", bins, min, max),
350+
std::make_shared<TH1I>("th2", "obj2", bins, min, max),
351+
};
352+
histsC1[0]->Fill(5);
353+
histsC1[1]->Fill(2);
354+
BOOST_CHECK_EQUAL(histsC1[0]->GetBinContent(histsC1[0]->FindBin(5)), 1);
355+
BOOST_CHECK_EQUAL(histsC1[1]->GetBinContent(histsC1[1]->FindBin(2)), 1);
356+
357+
std::vector<std::shared_ptr<TH1I>> histsC2{
358+
std::make_shared<TH1I>("th1", "obj1", bins, min, max),
359+
std::make_shared<TH1I>("th2", "obj2", bins, min, max),
360+
};
361+
362+
histsC2[0]->Fill(5);
363+
histsC2[1]->Fill(2);
364+
BOOST_CHECK_EQUAL(histsC2[0]->GetBinContent(histsC2[0]->FindBin(5)), 1);
365+
BOOST_CHECK_EQUAL(histsC2[1]->GetBinContent(histsC2[1]->FindBin(2)), 1);
366+
367+
auto targetCanvas = createCanvas("c1", "test title 1", histsC1);
368+
auto otherCanvas = createCanvas("c2", "test title 2", histsC2);
369+
370+
algorithm::merge(targetCanvas, otherCanvas);
371+
372+
auto targetObjects = collectUnderlyingObjects(targetCanvas);
373+
374+
BOOST_CHECK_EQUAL(targetObjects.size(), 2);
375+
for (const auto& object : targetObjects) {
376+
auto th = static_cast<TH1*>(object);
377+
if (std::string(th->GetName()) == "th1") {
378+
BOOST_CHECK_EQUAL(th->GetBinContent(th->FindBin(5)), 2);
379+
}
380+
if (std::string(th->GetName()) == "th2") {
381+
BOOST_CHECK_EQUAL(th->GetBinContent(th->FindBin(2)), 2);
382+
}
383+
}
384+
}
385+
386+
// throw because we try to merge canvases with different number of underlying items
387+
{
388+
std::vector<std::shared_ptr<TH1I>> histsC1{
389+
std::make_shared<TH1I>("th1", "obj1", bins, min, max),
390+
std::make_shared<TH1I>("th2", "obj2", bins, min, max),
391+
};
392+
393+
std::vector<std::shared_ptr<TH1I>> histsC2{
394+
std::make_shared<TH1I>("th1", "obj1", bins, min, max),
395+
};
396+
397+
auto targetCanvas = createCanvas("c1", "test title 1", histsC1);
398+
auto otherCanvas = createCanvas("c2", "test title 2", histsC2);
399+
400+
BOOST_CHECK_THROW(algorithm::merge(targetCanvas, otherCanvas), std::runtime_error);
401+
}
402+
403+
// throw because we try to merge canvases with different underlying items
404+
{
405+
std::vector<std::shared_ptr<TH1I>> histsC1{
406+
std::make_shared<TH1I>("th1", "obj1", bins, min, max),
407+
};
408+
409+
std::vector<std::shared_ptr<TH1I>> histsC2{
410+
std::make_shared<TH1I>("th2", "obj2", bins, min, max),
411+
};
412+
413+
auto targetCanvas = createCanvas("c1", "test title 1", histsC1);
414+
auto otherCanvas = createCanvas("c2", "test title 2", histsC2);
415+
416+
BOOST_CHECK_THROW(algorithm::merge(targetCanvas, otherCanvas), std::runtime_error);
417+
}
418+
}
419+
308420
BOOST_AUTO_TEST_CASE(Deleting)
309421
{
310422
TObjArray* main = new TObjArray();

0 commit comments

Comments
 (0)