Skip to content

Commit 851e437

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

File tree

3 files changed

+188
-4
lines changed

3 files changed

+188
-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: 74 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>
@@ -29,6 +29,10 @@
2929
#include <THnSparse.h>
3030
#include <TObjArray.h>
3131
#include <TTree.h>
32+
#include <TPad.h>
33+
#include <TCanvas.h>
34+
#include <algorithm>
35+
#include <stdexcept>
3236

3337
namespace o2::mergers::algorithm
3438
{
@@ -43,6 +47,51 @@ size_t estimateTreeSize(TTree* tree)
4347
return totalSize;
4448
}
4549

50+
// Mergeable objects are kept as primitives in TCanvas object in underlying TPad.
51+
// TPad is a linked list of primitives of any type (https://root.cern.ch/doc/master/classTPad.html)
52+
// including other TPads. So in order to collect all mergeable objects from TCanvas
53+
// we need to recursively transverse whole TPad structure.
54+
auto collectUnderlyingObjects(TCanvas* canvas) -> std::vector<TObject*>
55+
{
56+
auto collectFromTPad = [](TPad* pad, std::vector<TObject*>& objects, const auto& collectFromTPad) {
57+
if (!pad) {
58+
return;
59+
}
60+
auto* primitives = pad->GetListOfPrimitives();
61+
for (int i = 0; i < primitives->GetSize(); ++i) {
62+
auto* primitive = primitives->At(i);
63+
if (auto* primitivePad = dynamic_cast<TPad*>(primitive)) {
64+
collectFromTPad(primitivePad, objects, collectFromTPad);
65+
} else {
66+
objects.push_back(primitive);
67+
}
68+
}
69+
};
70+
71+
std::vector<TObject*> collectedObjects;
72+
collectFromTPad(canvas, collectedObjects, collectFromTPad);
73+
74+
return collectedObjects;
75+
}
76+
77+
struct MatchedCollectedObjects {
78+
TObject* target;
79+
TObject* other;
80+
};
81+
82+
auto matchCollectedToPairs(const std::vector<TObject*>& targetObjects, const std::vector<TObject*> otherObjects) -> std::vector<MatchedCollectedObjects>
83+
{
84+
std::vector<MatchedCollectedObjects> matchedObjects;
85+
matchedObjects.reserve(std::max(targetObjects.size(), otherObjects.size()));
86+
for (const auto& targetObject : targetObjects) {
87+
if (const auto found_it = std::ranges::find_if(otherObjects, [&targetObject](TObject* obj) { return std::string_view(targetObject->GetName()) == std::string_view(obj->GetName()); });
88+
found_it != otherObjects.end()) {
89+
matchedObjects.emplace_back(targetObject, *found_it);
90+
}
91+
}
92+
return matchedObjects;
93+
}
94+
4695
void merge(TObject* const target, TObject* const other)
4796
{
4897
if (target == nullptr) {
@@ -82,6 +131,29 @@ void merge(TObject* const target, TObject* const other)
82131
}
83132
}
84133
delete otherIterator;
134+
} else if (auto targetCanvas = dynamic_cast<TCanvas*>(target)) {
135+
136+
auto otherCanvas = dynamic_cast<TCanvas*>(other);
137+
if (otherCanvas == nullptr) {
138+
throw std::runtime_error(std::string("The target object '") + target->GetName() +
139+
"' is a TCanvas, while the other object '" + other->GetName() + "' is not.");
140+
}
141+
142+
const auto targetObjects = collectUnderlyingObjects(targetCanvas);
143+
const auto otherObjects = collectUnderlyingObjects(otherCanvas);
144+
if (targetObjects.size() != otherObjects.size()) {
145+
throw std::runtime_error(std::string("Trying to merge canvas: ") + targetCanvas->GetName() + " and canvas " + otherObjects.size() + "but contents are not the same");
146+
}
147+
148+
const auto matched = matchCollectedToPairs(targetObjects, otherObjects);
149+
if (targetObjects.size() != matched.size()) {
150+
throw std::runtime_error(std::string("Trying to merge canvas: ") + targetCanvas->GetName() + " and canvas " + otherObjects.size() + "but contents are not the same");
151+
}
152+
153+
for (const auto& [targetObject, otherObject] : matched) {
154+
merge(targetObject, otherObject);
155+
}
156+
85157
} else {
86158
Long64_t errorCode = 0;
87159
TObjArray otherCollection;
@@ -169,4 +241,4 @@ void deleteTCollections(TObject* obj)
169241
}
170242
}
171243

172-
} // namespace o2::mergers::algorithm
244+
} // 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)