Project
Loading...
Searching...
No Matches
StepTHn.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
12// Use StepTHn instead of THn and your memory consumption will be drastically reduced
13// Once you have the merged output, use getTHn() to get a standard histogram
14//
15// this storage container is optimized for small memory usage
16// under/over flow bins do not exist
17// sumw2 structure is float only and only create when the weight != 1
18//
19// Templated version allows also the use of double as storage container
20
21#include "Framework/StepTHn.h"
22#include "TList.h"
23#include "TCollection.h"
24#include "TArrayF.h"
25#include "TArrayD.h"
26#include "THn.h"
27#include "TMath.h"
28
31
32StepTHn::StepTHn() : mNBins(0),
33 mNVars(0),
34 mNSteps(0),
35 mValues(nullptr),
36 mSumw2(nullptr),
37 mTarget(nullptr),
38 mAxisCache(nullptr),
39 mNbinsCache(nullptr),
40 mLastVars(nullptr),
41 mLastBins(nullptr),
42 mLookup(nullptr),
43 mPrototype(nullptr)
44{
45 // Default constructor (for streaming)
46}
47
48StepTHn::StepTHn(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxes) : TNamed(name, title),
49 mNBins(0),
50 mNVars(nAxes),
51 mNSteps(nSteps),
52 mValues(nullptr),
53 mSumw2(nullptr),
54 mTarget(nullptr),
55 mAxisCache(nullptr),
56 mNbinsCache(nullptr),
57 mLastVars(nullptr),
58 mLastBins(nullptr),
59 mLookup(nullptr),
60 mPrototype(nullptr)
61{
62 // Constructor
63 //
64 // This will create a container for <nSteps> steps. The memory for such a step is only allocated once the first value is filled.
65 // Therefore you can easily create many steps which are only filled under certain analysis settings.
66 // For each step a <nAxes> dimensional histogram is created.
67 // The axis have <nBins[i]> bins. The bin edges are given in <binEdges[i]>. If there are only two bin edges, equidistant binning is set.
68
69 init();
70}
71
72// root-like constructor
73template <class TemplateArray>
74StepTHnT<TemplateArray>::StepTHnT(const char* name, const char* title, const int nSteps, const int nAxes, const int* nBins, const double* xmin, const double* xmax) : StepTHn(name, title, nSteps, nAxes)
75{
76 mNBins = 1;
77 for (Int_t i = 0; i < mNVars; i++) {
78 mNBins *= nBins[i];
79 }
80 if (mNBins > 250000000) {
81 LOGF(warning, "StepTHn: Requesting more than 250M bins (%lld). This will need extensive memory.", mNBins);
82 }
83 mPrototype = new THnSparseT<TemplateArray>(Form("%s_sparse", name), title, nAxes, nBins, xmin, xmax);
84}
85
86template <class TemplateArray>
87StepTHnT<TemplateArray>::StepTHnT(const Char_t* name, const Char_t* title, const Int_t nSteps, const Int_t nAxes,
88 Int_t* nBins, std::vector<Double_t> binEdges[], const char** axisTitles) : StepTHn(name, title, nSteps, nAxes)
89{
90 mNBins = 1;
91 for (Int_t i = 0; i < mNVars; i++) {
92 mNBins *= nBins[i];
93 }
94 if (mNBins > 250000000) {
95 LOGF(warning, "StepTHn: Requesting more than 250M bins (%lld). This will need extensive memory.", mNBins);
96 }
97 mPrototype = new THnSparseT<TemplateArray>(Form("%s_sparse", name), title, nAxes, nBins);
98
99 for (Int_t i = 0; i < mNVars; i++) {
100 if (nBins[i] + 1 == binEdges[i].size()) { // variable-width binning
101 mPrototype->GetAxis(i)->Set(nBins[i], &(binEdges[i])[0]);
102 } else if (binEdges[i].size() == 2) { // equidistant binning
103 mPrototype->GetAxis(i)->Set(nBins[i], binEdges[i][0], binEdges[i][1]);
104 } else {
105 LOGF(fatal, "Invalid binning information for axis %d with %d bins and %d entries for bin edges", i, nBins[i], binEdges[i].size());
106 }
107 LOGF(debug, "Histogram %s Axis %d created with %d bins and %d edges", name, i, nBins[i], binEdges[i].size());
108 mPrototype->GetAxis(i)->SetTitle(axisTitles[i]);
109 }
110}
111
113{
114 // initialize
115
116 mValues = new TArray*[mNSteps];
117 mSumw2 = new TArray*[mNSteps];
118
119 for (Int_t i = 0; i < mNSteps; i++) {
120 mValues[i] = nullptr;
121 mSumw2[i] = nullptr;
122 }
123}
124
125StepTHn::StepTHn(const StepTHn& c) : mNBins(c.mNBins),
126 mNVars(c.mNVars),
127 mNSteps(c.mNSteps),
128 mValues(new TArray*[c.mNSteps]),
129 mSumw2(new TArray*[c.mNSteps]),
130 mTarget(nullptr),
131 mAxisCache(nullptr),
132 mNbinsCache(nullptr),
133 mLastVars(nullptr),
134 mLastBins(nullptr),
135 mLookup(nullptr),
136 mPrototype(nullptr)
137{
138 //
139 // StepTHn copy constructor
140 //
141
142 ((StepTHn&)c).Copy(*this);
143}
144
146{
147 // Destructor
148
150
151 delete[] mValues;
152 delete[] mSumw2;
153 delete[] mTarget;
154 delete[] mAxisCache;
155 delete[] mNbinsCache;
156 delete[] mLastVars;
157 delete[] mLastBins;
158 delete[] mLookup;
159 delete mPrototype;
160}
161
163{
164 // delete data containers
165
166 for (Int_t i = 0; i < mNSteps; i++) {
167 if (mValues && mValues[i]) {
168 delete mValues[i];
169 mValues[i] = nullptr;
170 }
171
172 if (mSumw2 && mSumw2[i]) {
173 delete mSumw2[i];
174 mSumw2[i] = nullptr;
175 }
176
177 if (mTarget && mTarget[i]) {
178 delete mTarget[i];
179 mTarget[i] = nullptr;
180 }
181 }
182}
183
185{
186 // assigment operator
187
188 if (this != &c) {
189 ((StepTHn&)c).Copy(*this);
190 }
191
192 return *this;
193}
194
196{
197 // copy function
198
199 StepTHn& target = (StepTHn&)c;
200
201 TNamed::Copy(c);
202
203 target.mNBins = mNBins;
204 target.mNVars = mNVars;
205 target.mNSteps = mNSteps;
206
207 target.init();
208
209 for (Int_t i = 0; i < mNSteps; i++) {
210 if (mValues[i]) {
211 target.mValues[i] = createArray(mValues[i]);
212 } else {
213 target.mValues[i] = nullptr;
214 }
215
216 if (mSumw2[i]) {
217 target.mSumw2[i] = createArray(mSumw2[i]);
218 } else {
219 target.mSumw2[i] = nullptr;
220 }
221 }
222
223 if (mPrototype) {
224 target.mPrototype = dynamic_cast<THnSparse*>(mPrototype->Clone());
225 }
226}
227
228template <class TemplateArray>
229Long64_t StepTHnT<TemplateArray>::Merge(TCollection* list)
230{
231 // Merge a list of StepTHn objects with this (needed for PROOF).
232 // Returns the number of merged objects (including this).
233
234 if (!list) {
235 return 0;
236 }
237
238 if (list->IsEmpty()) {
239 return 1;
240 }
241
242 TIterator* iter = list->MakeIterator();
243 TObject* obj;
244
245 Int_t count = 0;
246 while ((obj = iter->Next())) {
247
249 if (entry == nullptr) {
250 continue;
251 }
252
253 for (Int_t i = 0; i < mNSteps; i++) {
254 if (entry->mValues[i]) {
255 if (!mValues[i]) {
256 mValues[i] = createArray();
257 }
258
259 auto source = dynamic_cast<TemplateArray*>(entry->mValues[i])->GetArray();
260 auto target = dynamic_cast<TemplateArray*>(mValues[i])->GetArray();
261 for (Long64_t l = 0; l < mNBins; l++) {
262 target[l] += source[l];
263 }
264 }
265
266 if (entry->mSumw2[i]) {
267 if (!mSumw2[i]) {
268 mSumw2[i] = createArray();
269 }
270
271 auto source = dynamic_cast<TemplateArray*>(entry->mSumw2[i])->GetArray();
272 auto target = dynamic_cast<TemplateArray*>(mSumw2[i])->GetArray();
273 for (Long64_t l = 0; l < mNBins; l++) {
274 target[l] += source[l];
275 }
276 }
277 }
278
279 count++;
280 }
281
282 return count + 1;
283}
284
285Long64_t StepTHn::getGlobalBinIndex(const Int_t* binIdx)
286{
287 // calculates global bin index
288 // binIdx contains TAxis bin indexes
289 // here bin count starts at 0 because we do not have over/underflow bins
290
291 Long64_t bin = 0;
292 for (Int_t i = 0; i < mNVars; i++) {
293 bin *= mPrototype->GetAxis(i)->GetNbins();
294 bin += binIdx[i] - 1;
295 }
296
297 return bin;
298}
299
300void StepTHn::createTarget(Int_t step, Bool_t sparse)
301{
302 // fills the information stored in the buffer in this class into the target THn
303
304 if (!mValues[step]) {
305 LOGF(fatal, "Histogram request for step %d which is empty.", step);
306 return;
307 }
308
309 if (!mTarget) {
310 mTarget = new THnBase*[mNSteps];
311 for (Int_t i = 0; i < mNSteps; i++) {
312 mTarget[i] = nullptr;
313 }
314 }
315
316 if (mTarget[step]) {
317 return;
318 }
319
320 TArray* source = mValues[step];
321 // if mSumw2 is not stored, the sqrt of the number of bin entries in source is filled below; otherwise we use mSumw2
322 TArray* sourceSumw2 = source;
323 if (mSumw2[step]) {
324 sourceSumw2 = mSumw2[step];
325 }
326
327 if (sparse) {
328 mTarget[step] = THnSparse::CreateSparse(Form("%s_%d", GetName(), step), Form("%s_%d", GetTitle(), step), mPrototype);
329 } else {
330 mTarget[step] = THn::CreateHn(Form("%s_%d", GetName(), step), Form("%s_%d", GetTitle(), step), mPrototype);
331 }
332
333 THnBase* target = mTarget[step];
334 if (mSumw2[step]) {
335 target->Sumw2();
336 }
337
338 Int_t* binIdx = new Int_t[mNVars];
339 Int_t* nBins = new Int_t[mNVars];
340 for (Int_t j = 0; j < mNVars; j++) {
341 binIdx[j] = 1;
342 nBins[j] = target->GetAxis(j)->GetNbins();
343 }
344
345 Long64_t count = 0;
346
347 while (1) {
348 // for (Int_t j=0; j<mNVars; j++)
349 // printf("%d ", binIdx[j]);
350
351 Long64_t globalBin = getGlobalBinIndex(binIdx);
352 // Printf(" --> %lld", globalBin);
353
354 // TODO probably slow
355 double value = source->GetAt(globalBin);
356 if (value != 0) {
357 target->SetBinContent(binIdx, value);
358 target->SetBinError(binIdx, TMath::Sqrt(sourceSumw2->GetAt(globalBin)));
359
360 count++;
361 }
362
363 binIdx[mNVars - 1]++;
364
365 for (Int_t j = mNVars - 1; j > 0; j--) {
366 if (binIdx[j] > nBins[j]) {
367 binIdx[j] = 1;
368 binIdx[j - 1]++;
369 }
370 }
371
372 if (binIdx[0] > nBins[0]) {
373 break;
374 }
375 }
376
377 LOGF(info, "Step %d: copied %lld entries out of %lld bins", step, count, getGlobalBinIndex(binIdx));
378
379 delete[] binIdx;
380 delete[] nBins;
381
382 delete mValues[step];
383 mValues[step] = nullptr;
384}
385
386void StepTHn::Fill(int iStep, int nParams, double positionAndWeight[])
387{
388 if (iStep >= mNSteps) {
389 LOGF(fatal, "Selected step for filling is not in range of StepTHn.");
390 }
391
392 double weight = 1.0;
393 if (nParams == mNVars + 1) {
394 weight = positionAndWeight[mNVars];
395 } else if (nParams != mNVars) {
396 LOGF(fatal, "Fill called with invalid number of parameters (%d vs %d)", mNVars, nParams);
397 }
398
399 // fill axis cache and build lookup tables on first call
400 if (!mAxisCache) {
401 mAxisCache = new TAxis*[mNVars];
402 mNbinsCache = new Int_t[mNVars];
404 for (Int_t i = 0; i < mNVars; i++) {
405 mAxisCache[i] = mPrototype->GetAxis(i);
406 mNbinsCache[i] = mAxisCache[i]->GetNbins();
407
408 // Build lookup table for this axis
409 auto& lut = mLookup[i];
410 lut.nBins = mNbinsCache[i];
411 lut.edges = mAxisCache[i]->GetXbins()->GetArray();
412 lut.xmin = mAxisCache[i]->GetXmin();
413 lut.xmax = mAxisCache[i]->GetXmax();
414
415 if (lut.edges && lut.xmax > lut.xmin) {
416 // Variable-width binning: build slot->bin mapping
417 Double_t slotWidth = (lut.xmax - lut.xmin) / kLookupSize;
418 lut.invSlotWidth = 1.0 / slotWidth;
419 for (Int_t s = 0; s < kLookupSize; s++) {
420 Double_t x = lut.xmin + (s + 0.5) * slotWidth;
421 lut.table[s] = mAxisCache[i]->FindBin(x);
422 }
423 } else {
424 // Uniform binning or degenerate axis: direct formula, no table needed
425 lut.edges = nullptr;
426 lut.invSlotWidth = lut.xmax > lut.xmin ? lut.nBins / (lut.xmax - lut.xmin) : 0;
427 }
428 }
429
430 mLastVars = new Double_t[mNVars];
431 mLastBins = new Int_t[mNVars];
432
433 // initial values to prevent checking for 0 below
434 for (Int_t i = 0; i < mNVars; i++) {
435 mLastVars[i] = positionAndWeight[i];
436 mLastBins[i] = mAxisCache[i]->FindBin(mLastVars[i]);
437 }
438 }
439
440 // calculate global bin index
441 Long64_t bin = 0;
442 for (Int_t i = 0; i < mNVars; i++) {
443 bin *= mNbinsCache[i];
444
445 Int_t tmpBin = 0;
446 if (mLastVars[i] == positionAndWeight[i]) {
447 tmpBin = mLastBins[i];
448 } else {
449 const auto& lut = mLookup[i];
450 Double_t x = positionAndWeight[i];
451
452 if (!lut.edges) {
453 // Uniform binning: direct computation.
454 // Use floor instead of truncation to match TAxis::FindBin rounding,
455 // then verify against the exact edge to handle values that land
456 // precisely on a bin boundary (where float arithmetic can round
457 // either way).
458 Double_t rawBin = (x - lut.xmin) * lut.invSlotWidth;
459 tmpBin = 1 + static_cast<Int_t>(std::floor(rawBin));
460 if (tmpBin >= 1 && tmpBin <= lut.nBins) {
461 // Compute the exact upper edge of this bin the same way TAxis does:
462 // edge = xmin + tmpBin * binWidth
463 // If x is at or above it, move to the next bin.
464 Double_t binWidth = (lut.xmax - lut.xmin) / lut.nBins;
465 Double_t upperEdge = lut.xmin + tmpBin * binWidth;
466 if (x >= upperEdge && tmpBin < lut.nBins) {
467 tmpBin++;
468 }
469 }
470 if (tmpBin < 1) {
471 tmpBin = 0; // underflow
472 } else if (tmpBin > lut.nBins) {
473 tmpBin = lut.nBins + 1; // overflow
474 }
475 } else {
476 // Variable-width binning: lookup table + refine
477 Int_t slot = static_cast<Int_t>((x - lut.xmin) * lut.invSlotWidth);
478 if (slot < 0 || slot >= kLookupSize) {
479 tmpBin = (slot < 0) ? 0 : lut.nBins + 1; // under/overflow
480 } else {
481 tmpBin = lut.table[slot];
482 // Refine: the lookup gives an approximate bin, check edges
483 // Move left if x is below the lower edge of tmpBin
484 while (tmpBin > 1 && x < lut.edges[tmpBin - 1]) {
485 tmpBin--;
486 }
487 // Move right if x is at or above the upper edge of tmpBin
488 while (tmpBin < lut.nBins && x >= lut.edges[tmpBin]) {
489 tmpBin++;
490 }
491 }
492 }
493
494 mLastBins[i] = tmpBin;
495 mLastVars[i] = x;
496 }
497
498 // under/overflow not supported
499 if (tmpBin < 1 || tmpBin > mNbinsCache[i]) {
500 return;
501 }
502
503 // bins start from 0 here
504 bin += tmpBin - 1;
505 }
506
507 updateBin(iStep, bin, weight);
508}
509
510template class StepTHnT<TArrayF>;
511template class StepTHnT<TArrayD>;
std::ostringstream debug
int32_t i
uint32_t j
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
templateClassImp(StepTHnT)
ClassImp(StepTHn)
StepTHnT()
Definition StepTHn.h:106
Long64_t Merge(TCollection *list) override
Definition StepTHn.cxx:229
Double_t * mLastVars
cache Nbins per axis
Definition StepTHn.h:82
Int_t * mLastBins
caching of last used bins (in many loops some vars are the same for a while)
Definition StepTHn.h:83
void Fill(int iStep, const Ts &... valuesAndWeight)
Definition StepTHn.h:152
StepTHn()
Definition StepTHn.cxx:32
Long64_t mNBins
Definition StepTHn.h:72
TArray ** mValues
Definition StepTHn.h:75
TAxis ** mAxisCache
target histogram
Definition StepTHn.h:80
Long64_t getGlobalBinIndex(const Int_t *binIdx)
Definition StepTHn.cxx:285
Int_t mNSteps
Definition StepTHn.h:74
StepTHn & operator=(const StepTHn &corr)
Definition StepTHn.cxx:184
virtual TArray * createArray(const TArray *src=nullptr) const =0
Int_t * mNbinsCache
cache axis pointers (about 50% of the time in Fill is spent in GetAxis otherwise)
Definition StepTHn.h:81
virtual void updateBin(int iStep, Long64_t bin, double weight)=0
void deleteContainers()
Definition StepTHn.cxx:162
void createTarget(Int_t step, Bool_t sparse)
Definition StepTHn.cxx:300
AxisLookup * mLookup
Definition StepTHn.h:95
THnSparse * mPrototype
per-axis lookup tables
Definition StepTHn.h:97
void Copy(TObject &c) const override
Definition StepTHn.cxx:195
TArray ** mSumw2
Definition StepTHn.h:76
static constexpr Int_t kLookupSize
caching of last used bins (in many loops some vars are the same for a while)
Definition StepTHn.h:86
void init()
Definition StepTHn.cxx:112
THnBase ** mTarget
Definition StepTHn.h:78
Int_t mNVars
Definition StepTHn.h:73
~StepTHn() override
Definition StepTHn.cxx:145
GLint GLenum GLint x
Definition glcorearb.h:403
GLint GLsizei count
Definition glcorearb.h:399
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLuint const GLchar * name
Definition glcorearb.h:781
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum target
Definition glcorearb.h:1641
Double_t invSlotWidth
Definition StepTHn.h:88