ATLAS Offline Software
Loading...
Searching...
No Matches
RootVisualizationService.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
5
8
9#include "GeoModelKernel/throwExcept.h"
10
11#include <TCanvas.h>
12#include <TH2F.h>
13#include <TROOT.h>
14#include <TStyle.h>
15
16#include <format>
17#include <thread>
18#include <filesystem>
19
20
21namespace{
23 std::size_t cleanTrashed(PlotVec_t& toDraw) {
24 toDraw.erase(std::remove_if(toDraw.begin(), toDraw.end(),
25 [](const auto& plot){
26 return plot->trashed();
27 }), toDraw.end());
28 return toDraw.size();
29 }
30}
31
32namespace MuonValR4{
33 using ICanvasObject = RootVisualizationService::ICanvasObject;
35 gROOT->SetStyle("ATLAS");
36 TStyle* plotStyle = gROOT->GetStyle("ATLAS");
37 plotStyle->SetOptTitle(0);
38 plotStyle->SetHistLineWidth(1.);
39 plotStyle->SetPalette(kViridis);
40
41 return StatusCode::SUCCESS;
42 }
43 StatusCode RootVisualizationService::registerClient(const ClientToken& token) {
44 if (token.preFixName.empty()){
45 ATH_MSG_FATAL("Prefix name must not be empty");
46 return StatusCode::FAILURE;
47 }
48 std::unique_lock guard{m_storageMutex};
49 auto insert_itr = m_storage.insert(std::make_pair(token, PlotsPerClient{}));
50 if (!insert_itr.second) {
51 ATH_MSG_FATAL("The token "<<token.preFixName<<" is already registered");
52 return StatusCode::FAILURE;
53 }
54 ATH_MSG_INFO("Registered new client "<<token.preFixName
55 <<", maximum number of plots "<<token.canvasLimit
56 <<", formats: "<<token.fileFormats<<", save single: "
57 <<(token.saveSinglePlots ? "yay" : "nay")
58 <<", save summary: "<<(token.saveSummaryPlot ? "yay" : "nay"));
59 return StatusCode::SUCCESS;
60 }
61
62 std::shared_ptr<ICanvasObject>
63 RootVisualizationService::prepareCanvas(const EventContext& ctx, const ClientToken& token,
64 const std::string& canvasName) {
65 if (canvasName.empty()) {
66 THROW_EXCEPTION("The canvas name must not be empty");
67 }
68 std::unique_lock lock_guard{m_storageMutex};
69 StorageMap_t::iterator store_itr = m_storage.find(token);
70 if (store_itr == m_storage.end()) {
71 THROW_EXCEPTION("The token "<<token.preFixName<<" is unknown.");
72 }
73 PlotsPerClient& dataHolder = store_itr->second;
74 if (!dataHolder.elementsDrawn &&
75 cleanTrashed(dataHolder.toDraw) < store_itr->first.canvasLimit) {
76 const std::size_t evt = ctx.eventID().event_number();
77 ATH_MSG_VERBOSE("Provide new canvas "<<canvasName<<" for stream "<<token.preFixName
78 <<" in "<<ctx.eventID());
79 auto newCanvas = dataHolder.toDraw.emplace_back(std::make_shared<detail::DrawCanvasObject>(canvasName, evt));
80 newCanvas->setRangeScale(m_canvasExtraScale, m_quadCanvas);
81 return newCanvas;
82 } else if (!dataHolder.elementsDrawn) {
83 dataHolder.elementsDrawn = true;
84 lock_guard.unlock();
85 paintObjects(store_itr->first, std::move(dataHolder.toDraw));
86 }
87 ATH_MSG_VERBOSE("Maximum elements for "<<token.preFixName
88 <<" reached. Don't provide any new canvas");
89 return nullptr;
90 }
91 void RootVisualizationService::paintObjects(const ClientToken& token,
92 PlotVec_t&& toDraw) {
93 if (toDraw.empty()){
94 return;
95 }
97 std::ranges::stable_sort(toDraw, [](const PlotPtr_t& a, const PlotPtr_t& b){
98 return a->event() < b->event();
99 });
100 std::unique_lock guard{m_canvasMutex};
101 std::unique_ptr<TCanvas> summaryCan{};
102 const std::string summaryPdfName = std::format("{:}/All{}.pdf",
103 m_outDir.value(), token.preFixName);
104 if (token.saveSummaryPlot) {
105 ATH_MSG_DEBUG("Open "<<summaryPdfName<<" to dump all canvases in a common file");
106 ensureDirectory(summaryPdfName);
107 summaryCan = std::make_unique<TCanvas>("allCan","allCan", m_canvasWidth, m_canvasHeight);
108 summaryCan->SaveAs(std::format("{:}[", summaryPdfName).c_str());
109 }
110 if (token.fileFormats.count("root") && !m_outFile) {
111 std::string outFile = std::format("{:}/{:}", m_outDir.value(),
112 m_outRootFileName.value());
114 m_outFile.reset(TFile::Open(outFile.c_str(), "RECREATE"));
115 if (!m_outFile || m_outFile->IsZombie()) {
116 THROW_EXCEPTION("Failed to create "<<outFile<<".");
117 }
118 ATH_MSG_DEBUG("Open "<<outFile<<" to save the plots in root format");
119 }
120 std::size_t currEvt{toDraw.back()->event()}, plotCount{0};
121 for (const PlotPtr_t& drawMe : toDraw) {
123 while (!drawMe.unique()) {
124 using namespace std::chrono_literals;
125 ATH_MSG_DEBUG("Wait until "<<drawMe->name()<<" is finished.");
126 std::this_thread::sleep_for(10ms);
127 }
129 if (currEvt != drawMe->event()) {
130 currEvt = drawMe->event();
131 plotCount = 0;
132 }
133 std::string plotName = std::format("{:}/{:}/{:}_{:}_{:}_{:}", m_outDir.value(),
134 token.subDirectory, token.preFixName,
135 drawMe->event(), ++plotCount,
136 removeNonAlphaNum(drawMe->name()));
137
138 auto singleCan = std::make_unique<TCanvas>("can", "can" , m_canvasWidth, m_canvasHeight);
139 singleCan->cd();
141 using enum ICanvasObject::AxisRanges;
142 ATH_MSG_VERBOSE("Crate new canvas: "<<plotName<<" ["<<drawMe->corner(xLow)<<";"<<drawMe->corner(xHigh)
143 <<"], ["<<drawMe->corner(yLow)<<";"<<drawMe->corner(yHigh)<<"].");
144 auto frameH = std::make_unique<TH2F>("frameH",
145 std::format("frame;{:};{:};{:}", drawMe->xTitle(), drawMe->yTitle(), drawMe->zTitle()).c_str(),
146 1, drawMe->corner(xLow), drawMe->corner(xHigh),
147 1, drawMe->corner(yLow), drawMe->corner(yHigh));
148 frameH->Draw("AXIS");
149
150 if (token.drawAtlas) {
151 drawMe->add(drawAtlasLabel(token.atlasLabelPos[0], token.atlasLabelPos[1], token.atlasLabel));
152 }
153 if (token.drawSqrtS) {
154 drawMe->add(drawLumiSqrtS(token.atlasLabelPos[0], token.atlasLabelPos[1] - 0.05,
155 token.sqrtSLabel, token.lumiLabel));
156 }
158 for (auto& [primitive, opt] : drawMe->primitives()) {
159 primitive->Draw(opt.c_str());
160 }
162 if (token.saveSinglePlots) {
163 for (const std::string& fileExt : token.fileFormats) {
164 if (fileExt != "root") {
165 ensureDirectory(plotName);
166 singleCan->SaveAs(std::format("{:}.{:}", plotName, fileExt).c_str());
167 }
168 }
169 }
170 if (token.fileFormats.count("root")) {
171 TDirectory* writeTo = m_outFile.get();
172 if (!token.subDirectory.empty()) {
173 writeTo = m_outFile->GetDirectory(token.subDirectory.c_str());
174 if (!writeTo) {
175 writeTo = m_outFile->mkdir(token.subDirectory.c_str());
176 }
177 }
178 writeTo->WriteObject(singleCan.get(),
179 std::format("{:}_{:}_{:}_{:}",token.preFixName,
180 drawMe->event(), plotCount,
181 removeNonAlphaNum(drawMe->name())).c_str());
182 }
183 if (summaryCan) {
184 singleCan->SaveAs(summaryPdfName.c_str());
185 }
186 }
187 if (summaryCan) {
188 summaryCan->SaveAs(std::format("{:}]", summaryPdfName).c_str());
189 }
190 }
192 ATH_MSG_DEBUG("Finalize the visualization service. Dump all canvases that not have yet been drawn");
193 for (auto& [token, dataHolder] : m_storage) {
194 if (!dataHolder.elementsDrawn) {
195 paintObjects(token, std::move(dataHolder.toDraw));
196 }
197 dataHolder.toDraw.clear();
198 }
199 m_outFile.reset();
200 return StatusCode::SUCCESS;
201 }
202}
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_DEBUG(x)
static Double_t a
Gaudi::Property< std::string > m_outDir
Directory into which all plots are written to.
Gaudi::Property< std::string > m_outRootFileName
Name of the ROOT file into which the output Canvases are written (needs root to be in the list of out...
std::unique_ptr< TFile > m_outFile
File into which all Canvases are saved if root is defined as extension.
virtual std::shared_ptr< ICanvasObject > prepareCanvas(const EventContext &ctx, const ClientToken &token, const std::string &canvasName) override final
void paintObjects(const ClientToken &token, PlotVec_t &&toDraw)
Gaudi::Property< unsigned > m_canvasHeight
Height of all drawn Canvases.
Gaudi::Property< bool > m_quadCanvas
Ensure that the canvas has the same interval sizes in x & y.
Gaudi::Property< unsigned > m_canvasWidth
Width of all drawn Canvases.
virtual StatusCode finalize() override final
Gaudi::Property< double > m_canvasExtraScale
Extra safety margin to zoom out from the Canvas.
virtual StatusCode initialize() override final
virtual StatusCode registerClient(const ClientToken &token) override final
std::shared_ptr< detail::DrawCanvasObject > PlotPtr_t
STL class.
Lightweight algorithm to read xAOD MDT sim hits and (fast-digitised) drift circles from SG and fill a...
void ensureDirectory(const std::string &path)
Ensures that the subdirectory in the path is created.
std::string removeNonAlphaNum(std::string str)
Removes all non-alpha numerical characters from a string.
std::unique_ptr< TLatex > drawLumiSqrtS(const double xPos, const double yPos, const std::string_view sqrtS="14", const std::string_view lumi="")
Create a luminosity sqrtS label.
std::unique_ptr< TLatex > drawAtlasLabel(const double xPos, const double yPos, const std::string &status="Internal")
Create a ATLAS label.
RootVisualizationService::ICanvasObject ICanvasObject
DataModel_detail::iterator< DVL > remove_if(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end, Predicate pred)
Specialization of remove_if for DataVector/List.
Helper struct to group all plots that are belonging to a client of the service.
PlotVec_t toDraw
List of already registered Canvases.
bool elementsDrawn
Flag to indicate whether the plots were already dumped on disk.
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10