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