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
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
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
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
Double_t invSlotWidth
Definition StepTHn.h:88