Project
Loading...
Searching...
No Matches
HistogramRegistry.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
13#include "Framework/ASoA.h"
14#include <regex>
15#include <TList.h>
16#include <TClass.h>
17
18namespace o2::framework
19{
20
21template void HistogramRegistry::fill(const HistName& histName, double);
22template void HistogramRegistry::fill(const HistName& histName, float);
23template void HistogramRegistry::fill(const HistName& histName, int);
24
25constexpr HistogramRegistry::HistName::HistName(char const* const name)
26 : str(name),
28 idx(hash & REGISTRY_BITMASK)
29{
30}
31
32HistogramRegistry::HistogramRegistry(char const* const name, std::vector<HistogramSpec> histSpecs, OutputObjHandlingPolicy policy, bool sortHistos, bool createRegistryDir)
33 : mName(name), mPolicy(policy), mCreateRegistryDir(createRegistryDir), mSortHistos(sortHistos), mRegistryKey(), mRegistryValue()
34{
35 mRegistryKey.fill(0u);
36 for (auto& histSpec : histSpecs) {
37 insert(histSpec);
38 }
39}
40
41// return the OutputSpec associated to the HistogramRegistry
43{
45 auto lhash = runtime_hash(mName.data());
46 std::memset(desc.str, '_', 16);
47 std::stringstream s;
48 s << std::hex << lhash;
49 s << std::hex << mTaskHash;
50 s << std::hex << reinterpret_cast<uint64_t>(this);
51 std::memcpy(desc.str, s.str().data(), 12);
52 return OutputSpec{OutputLabel{mName}, "ATSK", desc, 0, Lifetime::QA};
53}
54
55OutputRef HistogramRegistry::ref(uint16_t pipelineIndex, uint16_t pipelineSize) const
56{
57 OutputObjHeader header{mPolicy, OutputObjSourceType::HistogramRegistrySource, mTaskHash, pipelineIndex, pipelineSize};
58 // Copy the name of the registry to the haeder.
59 strncpy(header.containerName, mName.data(), OutputObjHeader::MAX_REGISTRY_NAME_SIZE);
60 header.createContainer = mCreateRegistryDir ? 1 : 0;
61 return OutputRef{std::string{mName}, 0, o2::header::Stack{header}};
62}
63
65{
66 mTaskHash = hash;
67}
68
69// create histogram from specification and insert it into the registry
70HistPtr HistogramRegistry::insert(const HistogramSpec& histSpec)
71{
72 validateHistName(histSpec.name, histSpec.hash);
73 const uint32_t idx = imask(histSpec.hash);
74 for (auto i = 0u; i < MAX_REGISTRY_SIZE; ++i) {
75 TObject* rawPtr = nullptr;
76 std::visit([&](const auto& sharedPtr) { rawPtr = sharedPtr.get(); }, mRegistryValue[imask(idx + i)]);
77 if (!rawPtr) {
78 registerName(histSpec.name);
79 mRegistryKey[imask(idx + i)] = histSpec.hash;
80 mRegistryValue[imask(idx + i)] = HistFactory::createHistVariant(histSpec);
81 lookup += i;
82 return mRegistryValue[imask(idx + i)];
83 }
84 }
85 LOGF(fatal, R"(Internal array of HistogramRegistry "%s" is full.)", mName);
86 return HistPtr();
87}
88
89// helper function that checks if histogram name can be used in registry
90void HistogramRegistry::validateHistName(const std::string& name, const uint32_t hash)
91{
92 // check that there are still slots left in the registry
93 if (mRegisteredNames.size() == MAX_REGISTRY_SIZE) {
94 LOGF(fatal, R"(HistogramRegistry "%s" is full! It can hold only %d histograms.)", mName, MAX_REGISTRY_SIZE);
95 }
96
97 // validate that hash is unique
98 auto it = std::find(mRegistryKey.begin(), mRegistryKey.end(), hash);
99 if (it != mRegistryKey.end()) {
100 auto idx = it - mRegistryKey.begin();
101 std::string collidingName{};
102 std::visit([&](const auto& hist) { collidingName = hist->GetName(); }, mRegistryValue[idx]);
103 LOGF(fatal, R"(Hash collision in HistogramRegistry "%s"! Please rename histogram "%s" or "%s".)", mName, name, collidingName);
104 }
105
106 // validate that name contains only allowed characters
107 if (!std::regex_match(name, std::regex("([a-zA-Z0-9])(([\\/_-])?[a-zA-Z0-9])*"))) {
108 LOGF(fatal, R"(Histogram name "%s" contains invalid characters. Only letters, numbers, and (except for the beginning or end of the word) the special characters '/', '_', '-' are allowed.)", name);
109 }
110}
111
113{
114 return insert(histSpec);
115}
116
117HistPtr HistogramRegistry::add(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2)
118{
119 return insert({name, title, histConfigSpec, callSumw2});
120}
121
122HistPtr HistogramRegistry::add(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2)
123{
124 return insert({name, title, {histType, axes}, callSumw2});
125}
126
127HistPtr HistogramRegistry::add(const std::string& name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2)
128{
129 return add(name.c_str(), title, histType, axes, callSumw2);
130}
131
132// store a copy of an existing histogram (or group of histograms) under a different name
133void HistogramRegistry::addClone(const std::string& source, const std::string& target)
134{
135 auto doInsertClone = [&](const auto& sharedPtr) {
136 if (!sharedPtr.get()) {
137 return;
138 }
139 std::string sourceName{((TNamed*)sharedPtr.get())->GetName()};
140 // search for histograms starting with source_ substring
141 if (sourceName.rfind(source, 0) == 0) {
142 // when cloning groups of histograms source_ and target_ must end with "/"
143 if (sourceName.size() != source.size() && (source.back() != '/' || target.back() != '/')) {
144 return;
145 }
146 // when cloning a single histogram the specified target_ must not be a group name
147 if (sourceName.size() == source.size() && target.back() == '/') {
148 LOGF(fatal, "Cannot turn histogram into folder!");
149 }
150 std::string targetName{target};
151 targetName += sourceName.substr(sourceName.find(source) + source.size());
152 insertClone(targetName.data(), sharedPtr);
153 }
154 };
155
156 for (auto& histVariant : mRegistryValue) {
157 std::visit(doInsertClone, histVariant);
158 }
159}
160
161// function to query if name is already in use
162bool HistogramRegistry::contains(const HistName& histName)
163{
164 // check for all occurances of the hash
165 auto iter = mRegistryKey.begin();
166 while ((iter = std::find(iter, mRegistryKey.end(), histName.hash)) != mRegistryKey.end()) {
167 const char* curName = nullptr;
168 std::visit([&](auto&& hist) { if(hist) { curName = hist->GetName(); } }, mRegistryValue[iter - mRegistryKey.begin()]);
169 // if hash is the same, make sure that name is indeed the same
170 if (strcmp(curName, histName.str) == 0) {
171 return true;
172 }
173 }
174 return false;
175}
176
177// get rough estimate for size of histogram stored in registry
178double HistogramRegistry::getSize(const HistName& histName, double fillFraction)
179{
180 double size{};
181 std::visit([&fillFraction, &size](auto&& hist) { size = HistFiller::getSize(hist, fillFraction); }, mRegistryValue[getHistIndex(histName)]);
182 return size;
183}
184
185// get rough estimate for size of all histograms stored in registry
186double HistogramRegistry::getSize(double fillFraction)
187{
188 double size{};
189 for (auto j = 0u; j < MAX_REGISTRY_SIZE; ++j) {
190 std::visit([&fillFraction, &size](auto&& hist) { if(hist) { size += HistFiller::getSize(hist, fillFraction);} }, mRegistryValue[j]);
191 }
192 return size;
193}
194
196{
197 for (auto& value : mRegistryValue) {
198 std::visit([](auto&& hist) { hist.reset(); }, value);
199 }
200}
201
202// print some useful meta-info about the stored histograms
203void HistogramRegistry::print(bool showAxisDetails)
204{
205 std::vector<double> fillFractions{0.1, 0.25, 0.5};
206 std::vector<double> totalSizes(fillFractions.size());
207
208 uint32_t nHistos{};
209 bool containsSparseHist{};
210 auto printHistInfo = [&](auto&& hist) {
211 if (hist) {
212 using T = std::decay_t<decltype(*hist)>;
213 bool isSparse{};
214 if (hist->InheritsFrom(THnSparse::Class())) {
215 isSparse = true;
216 containsSparseHist = true;
217 }
218 ++nHistos;
219 std::vector<double> sizes;
220 std::string sizeInfo{};
221 if (isSparse) {
222 std::transform(std::begin(fillFractions), std::end(fillFractions), std::back_inserter(sizes), [&hist](auto& fraction) { return HistFiller::getSize(hist, fraction); });
223 for (int i = 0; i < fillFractions.size(); ++i) {
224 sizeInfo += fmt::format("{:.2f} kB ({:.0f} %)", sizes[i] * 1024, fillFractions[i] * 100);
225 if (i != fillFractions.size() - 1) {
226 sizeInfo += ", ";
227 }
228 }
229 } else {
230 double size = HistFiller::getSize(hist);
231 sizes.resize(fillFractions.size(), size);
232 sizeInfo = fmt::format("{:.2f} kB", sizes[0] * 1024);
233 }
234 std::transform(totalSizes.begin(), totalSizes.end(), sizes.begin(), totalSizes.begin(), std::plus<double>());
235 LOGF(info, "Hist %03d: %-35s %-19s [%s]", nHistos, hist->GetName(), hist->IsA()->GetName(), sizeInfo);
236
237 if (showAxisDetails) {
238 int nDim = 0;
239 if constexpr (std::is_base_of_v<THnBase, T>) {
240 nDim = hist->GetNdimensions();
241 } else if constexpr (std::is_base_of_v<TH1, T>) {
242 nDim = hist->GetDimension();
243 }
244 TAxis* axis{nullptr};
245 for (int d = 0; d < nDim; ++d) {
246 if constexpr (std::is_base_of_v<THnBase, T> || std::is_base_of_v<StepTHn, T>) {
247 axis = hist->GetAxis(d);
248 } else {
249 if (d == 0) {
250 axis = hist->GetXaxis();
251 } else if (d == 1) {
252 axis = hist->GetYaxis();
253 } else if (d == 2) {
254 axis = hist->GetZaxis();
255 }
256 }
257 LOGF(info, "- Axis %d: %-20s (%d bins)", d, axis->GetTitle(), axis->GetNbins());
258 }
259 }
260 }
261 };
262
263 std::string titleString{"======================== HistogramRegistry ========================"};
264 LOGF(info, "");
265 LOGF(info, "%s", titleString);
266 LOGF(info, "%s\"%s\"", std::string((int)(0.5 * titleString.size() - (1 + 0.5 * mName.size())), ' '), mName);
267 for (auto& curHistName : mRegisteredNames) {
268 std::visit(printHistInfo, mRegistryValue[getHistIndex(HistName{curHistName.data()})]);
269 }
270 std::string totalSizeInfo{};
271 if (containsSparseHist) {
272 for (int i = 0; i < totalSizes.size(); ++i) {
273 totalSizeInfo += fmt::format("{:.2f} MB ({:.0f} %)", totalSizes[i], fillFractions[i] * 100);
274 if (i != totalSizes.size() - 1) {
275 totalSizeInfo += ", ";
276 }
277 }
278 } else {
279 totalSizeInfo = fmt::format("{:.2f} MB", totalSizes[0]);
280 }
281 LOGF(info, "%s", std::string(titleString.size(), '='), titleString);
282 LOGF(info, "Total: %d histograms, ca. %s", nHistos, totalSizeInfo);
283 if (lookup) {
284 LOGF(info, "Due to index collisions, histograms were shifted by %d registry slots in total.", lookup);
285 }
286 LOGF(info, "%s", std::string(titleString.size(), '='), titleString);
287 LOGF(info, "");
288}
289
290void HistogramRegistry::apply(std::function<void(HistogramRegistry const&, TNamed* named)> callback) const
291{
292 // Keep the list sorted as originally done to avoid hidden dependency on the order, for now , for now.
293 auto finalList = mRegisteredNames;
294 auto caseInsensitiveCompare = [](const std::string& s1, const std::string& s2) {
295 return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
296 [](char c1, char c2) { return std::tolower(static_cast<unsigned char>(c1)) < std::tolower(static_cast<unsigned char>(c2)); });
297 };
298 if (mSortHistos) {
299 std::sort(finalList.begin(), finalList.end(), caseInsensitiveCompare);
300 }
301 for (auto& curHistName : finalList) {
302 TNamed* rawPtr = nullptr;
303 std::visit([&](const auto& sharedPtr) { rawPtr = (TNamed*)sharedPtr.get(); }, mRegistryValue[getHistIndex(HistName{curHistName.data()})]);
304 if (!rawPtr) {
305 // Skipping empty histograms
306 continue;
307 }
308 callback(*this, rawPtr);
309 }
310}
311
312// helper function to split user defined path/to/hist/name string
313std::deque<std::string> HistogramRegistry::splitPath(const std::string& pathAndNameUser)
314{
315 std::istringstream pathAndNameStream(pathAndNameUser);
316 std::deque<std::string> pathAndName;
317 std::string curDir;
318 while (std::getline(pathAndNameStream, curDir, '/')) {
319 pathAndName.push_back(curDir);
320 }
321 return pathAndName;
322}
323
324// helper function that checks if name of histogram is reasonable and keeps track of names already in use
325void HistogramRegistry::registerName(const std::string& name)
326{
327 if (name.empty() || name.back() == '/') {
328 LOGF(fatal, "Invalid name for a histogram.");
329 }
330 std::deque<std::string> path = splitPath(name);
331 std::string cumulativeName{};
332 int depth = path.size();
333 for (auto& step : path) {
334 if (step.empty()) {
335 LOGF(fatal, R"(Found empty group name in path for histogram "%s".)", name);
336 }
337 cumulativeName += step;
338 for (auto& curName : mRegisteredNames) {
339 // there is already a histogram where we want to put a folder or histogram
340 if (cumulativeName == curName) {
341 LOGF(fatal, R"(Histogram name "%s" is not compatible with existing names.)", name);
342 }
343 // for the full new histogram name we need to check that none of the existing histograms already uses this as a group name
344 if (depth == 1) {
345 if (curName.rfind(cumulativeName, 0) == 0 && curName.size() > cumulativeName.size() && curName.at(cumulativeName.size()) == '/') {
346 LOGF(fatal, R"(Histogram name "%s" is not compatible with existing names.)", name);
347 }
348 }
349 }
350 --depth;
351 cumulativeName += "/";
352 }
353 mRegisteredNames.push_back(name);
354}
355
356void HistFiller::badHistogramFill(char const* name)
357{
358 LOGF(fatal, "The number of arguments in fill function called for histogram %s is incompatible with histogram dimensions.", name);
359}
360
361template <typename T>
362HistPtr HistogramRegistry::insertClone(const HistName& histName, const std::shared_ptr<T> originalHist)
363{
364 validateHistName(histName.str, histName.hash);
365 for (auto i = 0u; i < MAX_REGISTRY_SIZE; ++i) {
366 TObject* rawPtr = nullptr;
367 std::visit([&](const auto& sharedPtr) { rawPtr = sharedPtr.get(); }, mRegistryValue[imask(histName.idx + i)]);
368 if (!rawPtr) {
369 registerName(histName.str);
370 mRegistryKey[imask(histName.idx + i)] = histName.hash;
371 mRegistryValue[imask(histName.idx + i)] = std::shared_ptr<T>(static_cast<T*>(originalHist->Clone(histName.str)));
372 lookup += i;
373 return mRegistryValue[imask(histName.idx + i)];
374 }
375 }
376 LOGF(fatal, R"(Internal array of HistogramRegistry "%s" is full.)", mName);
377 return HistPtr();
378}
379
380template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<TH1>);
381template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<TH2>);
382template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<TH3>);
383template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<TProfile>);
384template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<TProfile2D>);
385template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<TProfile3D>);
386template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<THnSparse>);
387template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<THn>);
388template HistPtr HistogramRegistry::insertClone(const HistName&, const std::shared_ptr<StepTHn>);
389
390template <typename T>
391std::shared_ptr<T> HistogramRegistry::add(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2)
392{
393 auto histVariant = add(name, title, histConfigSpec, callSumw2);
394 if (auto histPtr = std::get_if<std::shared_ptr<T>>(&histVariant)) {
395 return *histPtr;
396 } else {
397 throw runtime_error_f(R"(Histogram type specified in add<>("%s") does not match the actual type of the histogram!)", name);
398 }
399}
400
401template <typename T>
402std::shared_ptr<T> HistogramRegistry::add(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2)
403{
404 auto histVariant = add(name, title, histType, axes, callSumw2);
405 if (auto histPtr = std::get_if<std::shared_ptr<T>>(&histVariant)) {
406 return *histPtr;
407 } else {
408 throw runtime_error_f(R"(Histogram type specified in add<>("%s") does not match the actual type of the histogram!)", name);
409 }
410}
411
412template std::shared_ptr<TH1> HistogramRegistry::add<TH1>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
413template std::shared_ptr<TH1> HistogramRegistry::add<TH1>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
414template std::shared_ptr<TH2> HistogramRegistry::add<TH2>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
415template std::shared_ptr<TH2> HistogramRegistry::add<TH2>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
416template std::shared_ptr<TH3> HistogramRegistry::add<TH3>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
417template std::shared_ptr<TH3> HistogramRegistry::add<TH3>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
418template std::shared_ptr<TProfile> HistogramRegistry::add<TProfile>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
419template std::shared_ptr<TProfile> HistogramRegistry::add<TProfile>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
420template std::shared_ptr<TProfile2D> HistogramRegistry::add<TProfile2D>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
421template std::shared_ptr<TProfile2D> HistogramRegistry::add<TProfile2D>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
422template std::shared_ptr<TProfile3D> HistogramRegistry::add<TProfile3D>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
423template std::shared_ptr<TProfile3D> HistogramRegistry::add<TProfile3D>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
424template std::shared_ptr<THn> HistogramRegistry::add<THn>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
425template std::shared_ptr<THn> HistogramRegistry::add<THn>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
426template std::shared_ptr<THnSparse> HistogramRegistry::add<THnSparse>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
427template std::shared_ptr<THnSparse> HistogramRegistry::add<THnSparse>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
428template std::shared_ptr<StepTHn> HistogramRegistry::add<StepTHn>(char const* const name, char const* const title, const HistogramConfigSpec& histConfigSpec, bool callSumw2);
429template std::shared_ptr<StepTHn> HistogramRegistry::add<StepTHn>(char const* const name, char const* const title, HistType histType, const std::vector<AxisSpec>& axes, bool callSumw2);
430
431} // namespace o2::framework
uint32_t hash
int32_t i
uint32_t j
Definition RawData.h:0
constexpr uint32_t runtime_hash(char const *str)
bool contains(const HistName &histName)
double getSize(const HistName &histName, double fillFraction=1.)
void fill(const HistName &histName, Ts... positionAndWeight)
void addClone(const std::string &source, const std::string &target)
void print(bool showAxisDetails=false)
HistPtr add(const HistogramSpec &histSpec)
void apply(std::function< void(HistogramRegistry const &, TNamed *named)> callback) const
OutputRef ref(uint16_t idx, uint16_t pipelineSize) const
void clean()
deletes all the histograms from the registry
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
Definition glcorearb.h:2595
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
Definition glcorearb.h:5034
GLuint const GLchar * name
Definition glcorearb.h:781
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum target
Definition glcorearb.h:1641
GLint GLint GLsizei GLsizei GLsizei depth
Definition glcorearb.h:470
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::variant< std::shared_ptr< THn >, std::shared_ptr< THnSparse >, std::shared_ptr< TH3 >, std::shared_ptr< TH2 >, std::shared_ptr< TH1 >, std::shared_ptr< TProfile3D >, std::shared_ptr< TProfile2D >, std::shared_ptr< TProfile >, std::shared_ptr< StepTHn > > HistPtr
RuntimeErrorRef runtime_error_f(const char *,...)
OutputObjHandlingPolicy
Policy enum to determine OutputObj handling when writing.
value_T step
Definition TrackUtils.h:42
static HistPtr createHistVariant(const HistogramSpec &histSpec)
static double getSize(std::shared_ptr< T > hist, double fillFraction=1.)
O2 header for OutputObj metadata.
static constexpr const uint32_t MAX_REGISTRY_NAME_SIZE
a move-only header stack with serialized headers This is the flat buffer where all the headers in a m...
Definition Stack.h:33
const std::string str