]> git.uio.no Git - u/mrichter/AliRoot.git/blob - FMD/AliFMDInput.cxx
New class to represent the CTP bunch-crossing masks
[u/mrichter/AliRoot.git] / FMD / AliFMDInput.cxx
1 /**************************************************************************
2  * Copyright(c) 2004, ALICE Experiment at CERN, All rights reserved. *
3  *                                                                        *
4  * Author: The ALICE Off-line Project.                                    *
5  * Contributors are mentioned in the code where appropriate.              *
6  *                                                                        *
7  * Permission to use, copy, modify and distribute this software and its   *
8  * documentation strictly for non-commercial purposes is hereby granted   *
9  * without fee, provided that the above copyright notice appears in all   *
10  * copies and that both the copyright notice and this permission notice   *
11  * appear in the supporting documentation. The authors make no claims     *
12  * about the suitability of this software for any purpose. It is          *
13  * provided "as is" without express or implied warranty.                  *
14  **************************************************************************/
15 /* $Id$ */
16 /** @file    AliFMDInput.cxx
17     @author  Christian Holm Christensen <cholm@nbi.dk>
18     @date    Mon Mar 27 12:42:40 2006
19     @brief   FMD utility classes for reading FMD data
20 */
21 //___________________________________________________________________
22 //
23 // The classes defined here, are utility classes for reading in data
24 // for the FMD.  They are  put in a seperate library to not polute the
25 // normal libraries.  The classes are intended to be used as base
26 // classes for customized class that do some sort of analysis on the
27 // various types of data produced by the FMD. 
28 //
29 // Latest changes by Christian Holm Christensen
30 //
31 #include "AliFMDInput.h"        // ALIFMDHIT_H
32 #include "AliFMDDebug.h"        // ALIFMDDEBUG_H ALILOG_H
33 #include "AliLoader.h"          // ALILOADER_H
34 #include "AliRunLoader.h"       // ALIRUNLOADER_H
35 #include "AliRun.h"             // ALIRUN_H
36 #include "AliStack.h"           // ALISTACK_H
37 #include "AliRawReaderFile.h"   // ALIRAWREADERFILE_H
38 #include "AliFMD.h"             // ALIFMD_H
39 #include "AliFMDHit.h"          // ALIFMDHIT_H
40 #include "AliFMDDigit.h"        // ALIFMDDigit_H
41 #include "AliFMDSDigit.h"       // ALIFMDDigit_H
42 #include "AliFMDRecPoint.h"     // ALIFMDRECPOINT_H
43 #include "AliFMDRawReader.h"    // ALIFMDRAWREADER_H
44 #include <AliESD.h>
45 #include <AliESDFMD.h>
46 #include <AliESDEvent.h>
47 #include <AliCDBManager.h>
48 #include <AliCDBEntry.h>
49 #include <AliAlignObjParams.h>
50 #include <TTree.h>              // ROOT_TTree
51 #include <TChain.h>             // ROOT_TChain
52 #include <TParticle.h>          // ROOT_TParticle
53 #include <TString.h>            // ROOT_TString
54 #include <TDatabasePDG.h>       // ROOT_TDatabasePDG
55 #include <TMath.h>              // ROOT_TMath
56 #include <TGeoManager.h>        // ROOT_TGeoManager 
57 #include <TSystemDirectory.h>   // ROOT_TSystemDirectory
58 #include <Riostream.h>          // ROOT_Riostream
59 #include <TFile.h>              // ROOT_TFile
60 #include <TStreamerInfo.h>
61 #include <TArrayF.h>
62
63 //____________________________________________________________________
64 ClassImp(AliFMDInput)
65 #if 0
66   ; // This is here to keep Emacs for indenting the next line
67 #endif
68
69
70 //____________________________________________________________________
71 AliFMDInput::AliFMDInput()
72   : fGAliceFile(""), 
73     fLoader(0),
74     fRun(0), 
75     fStack(0),
76     fFMDLoader(0), 
77     fReader(0),
78     fFMD(0),
79     fESD(0),
80     fESDEvent(0),
81     fTreeE(0),
82     fTreeH(0),
83     fTreeD(0),
84     fTreeS(0),
85     fTreeR(0), 
86     fTreeA(0), 
87     fChainE(0),
88     fArrayE(0),
89     fArrayH(0),
90     fArrayD(0),
91     fArrayS(0), 
92     fArrayR(0), 
93     fArrayA(0), 
94     fGeoManager(0),
95     fTreeMask(0), 
96     fIsInit(kFALSE)
97 {
98
99   // Constructor of an FMD input object.  Specify what data to read in
100   // using the AddLoad member function.   Sub-classes should at a
101   // minimum overload the member function Event.   A full job can be
102   // executed using the member function Run. 
103 }
104
105   
106
107 //____________________________________________________________________
108 AliFMDInput::AliFMDInput(const char* gAliceFile)
109   : fGAliceFile(gAliceFile),
110     fLoader(0),
111     fRun(0), 
112     fStack(0),
113     fFMDLoader(0), 
114     fReader(0),
115     fFMD(0),
116     fESD(0),
117     fESDEvent(0),
118     fTreeE(0),
119     fTreeH(0),
120     fTreeD(0),
121     fTreeS(0),
122     fTreeR(0), 
123     fTreeA(0), 
124     fChainE(0),
125     fArrayE(0),
126     fArrayH(0),
127     fArrayD(0),
128     fArrayS(0), 
129     fArrayR(0), 
130     fArrayA(0), 
131     fGeoManager(0),
132     fTreeMask(0), 
133     fIsInit(kFALSE)
134 {
135   
136   // Constructor of an FMD input object.  Specify what data to read in
137   // using the AddLoad member function.   Sub-classes should at a
138   // minimum overload the member function Event.   A full job can be
139   // executed using the member function Run. 
140 }
141
142 //____________________________________________________________________
143 Int_t
144 AliFMDInput::NEvents() const 
145 {
146   // Get number of events
147   if (fTreeE) return fTreeE->GetEntries();
148   return -1;
149 }
150
151 //____________________________________________________________________
152 Bool_t
153 AliFMDInput::Init()
154 {
155   // Initialize the object.  Get the needed loaders, and such. 
156
157   // Check if we have been initialized
158   if (fIsInit) { 
159     AliWarning("Already initialized");
160     return fIsInit;
161   }
162   if (fGAliceFile.IsNull()) fGAliceFile = "galice.root";
163   // Get the loader
164   fLoader = AliRunLoader::Open(fGAliceFile.Data(), "Alice", "read");
165   if (!fLoader) {
166     AliError(Form("Coulnd't read the file %s", fGAliceFile.Data()));
167     return kFALSE;
168   }
169   
170   // Get the run 
171   if  (fLoader->LoadgAlice()) return kFALSE;
172   fRun = fLoader->GetAliRun();
173   
174   // Get the FMD 
175   fFMD = static_cast<AliFMD*>(fRun->GetDetector("FMD"));
176   if (!fFMD) {
177     AliError("Failed to get detector FMD from loader");
178     return kFALSE;
179   }
180   
181   // Get the FMD loader
182   fFMDLoader = fLoader->GetLoader("FMDLoader");
183   if (!fFMDLoader) {
184     AliError("Failed to get detector FMD loader from loader");
185     return kFALSE;
186   }
187   if (fLoader->LoadHeader()) { 
188     AliError("Failed to get event header information from loader");
189     return kFALSE;
190   }
191   fTreeE = fLoader->TreeE();
192
193   // Optionally, get the ESD files
194   if (TESTBIT(fTreeMask, kESD)) {
195     fChainE = new TChain("esdTree");
196     TSystemDirectory dir(".",".");
197     TList*           files = dir.GetListOfFiles();
198     TSystemFile*     file = 0;
199     if (!files) {
200       AliError("No files");
201       return kFALSE;
202     }
203     files->Sort();
204     TIter            next(files);
205     while ((file = static_cast<TSystemFile*>(next()))) {
206       TString fname(file->GetName());
207       if (fname.Contains("AliESDs")) fChainE->AddFile(fname.Data());
208     }
209     fESDEvent = new AliESDEvent();
210     fESDEvent->ReadFromTree(fChainE);
211     //    fChainE->SetBranchAddress("ESD", &fMainESD);
212     
213   }
214     
215   if (TESTBIT(fTreeMask, kRaw)) {
216     AliInfo("Getting FMD raw data digits");
217     fArrayA = new TClonesArray("AliFMDDigit");
218     fReader = new AliRawReaderFile(-1);
219   }
220   
221   // Optionally, get the geometry 
222   if (TESTBIT(fTreeMask, kGeometry)) {
223     TString fname(fRun->GetGeometryFileName());
224     if (fname.IsNull()) {
225       Warning("Init", "No file name for the geometry from AliRun");
226       fname = gSystem->DirName(fGAliceFile);
227       fname.Append("/geometry.root");
228     }
229     fGeoManager = TGeoManager::Import(fname.Data());
230     if (!fGeoManager) {
231       Fatal("Init", "No geometry manager found");
232       return kFALSE;
233     }
234     AliCDBManager* cdb   = AliCDBManager::Instance();
235     AliCDBEntry*   align = cdb->Get("FMD/Align/Data");
236     if (align) {
237       AliInfo("Got alignment data from CDB");
238       TClonesArray* array = dynamic_cast<TClonesArray*>(align->GetObject());
239       if (!array) {
240         AliWarning("Invalid align data from CDB");
241       }
242       else {
243         Int_t nAlign = array->GetEntries();
244         for (Int_t i = 0; i < nAlign; i++) {
245           AliAlignObjParams* a = static_cast<AliAlignObjParams*>(array->At(i));
246           if (!a->ApplyToGeometry()) {
247             AliWarning(Form("Failed to apply alignment to %s", 
248                             a->GetSymName()));
249           }
250         }
251       }
252     }
253   }
254
255   fEventCount = 0;
256   fIsInit = kTRUE;
257   return fIsInit;
258 }
259
260 //____________________________________________________________________
261 Bool_t
262 AliFMDInput::Begin(Int_t event)
263 {
264   // Called at the begining of each event.  Per default, it gets the
265   // data trees and gets pointers to the output arrays.   Users can
266   // overload this, but should call this member function in the
267   // overloaded member function of the derived class. 
268
269   // Check if we have been initialized
270   if (!fIsInit) { 
271     AliError("Not initialized");
272     return fIsInit;
273   }
274
275   // Get the event 
276   if (fLoader->GetEvent(event)) return kFALSE;
277   AliInfo(Form("Now in event %8d/%8d", event, NEvents()));
278
279   // Possibly load global kinematics information 
280   if (TESTBIT(fTreeMask, kKinematics) || TESTBIT(fTreeMask, kTracks)) {
281     // AliInfo("Getting kinematics");
282     if (fLoader->LoadKinematics()) return kFALSE;
283     fStack = fLoader->Stack();
284   }
285
286   // Possibly load FMD Hit information 
287   if (TESTBIT(fTreeMask, kHits) || TESTBIT(fTreeMask, kTracks)) {
288     // AliInfo("Getting FMD hits");
289     if (!fFMDLoader || fFMDLoader->LoadHits()) return kFALSE;
290     fTreeH = fFMDLoader->TreeH();
291     if (!fArrayH) fArrayH = fFMD->Hits(); 
292   }
293
294   // Possibly load FMD Digit information 
295   if (TESTBIT(fTreeMask, kDigits)) {
296     // AliInfo("Getting FMD digits");
297     if (!fFMDLoader || fFMDLoader->LoadDigits()) return kFALSE;
298     fTreeD = fFMDLoader->TreeD();
299     if (fTreeD) {
300       if (!fArrayD) fArrayD = fFMD->Digits();
301     }
302     else {
303       fArrayD = 0;
304       AliWarning(Form("Failed to load FMD Digits"));
305     } 
306   }
307
308   // Possibly load FMD Sdigit information 
309   if (TESTBIT(fTreeMask, kSDigits)) {
310     // AliInfo("Getting FMD summable digits");
311     if (!fFMDLoader || fFMDLoader->LoadSDigits()) return kFALSE;
312     fTreeS = fFMDLoader->TreeS();
313     if (!fArrayS) fArrayS = fFMD->SDigits();
314   }
315
316   // Possibly load FMD RecPoints information 
317   if (TESTBIT(fTreeMask, kRecPoints)) {
318     // AliInfo("Getting FMD reconstructed points");
319     if (!fFMDLoader || fFMDLoader->LoadRecPoints()) return kFALSE;
320     fTreeR = fFMDLoader->TreeR();
321     if (!fArrayR) fArrayR = new TClonesArray("AliFMDRecPoint");
322     fTreeR->SetBranchAddress("FMD",  &fArrayR);
323   }  
324
325   // Possibly load FMD ESD information 
326   if (TESTBIT(fTreeMask, kESD)) {
327     // AliInfo("Getting FMD event summary data");
328     Int_t read = fChainE->GetEntry(event);
329     if (read <= 0) return kFALSE;
330     fESD = fESDEvent->GetFMDData();
331     if (!fESD) return kFALSE;
332 #if 0
333     TFile* f = fChainE->GetFile();
334     if (f) {
335       TObject* o = f->GetStreamerInfoList()->FindObject("AliFMDMap");
336       if (o) {
337         TStreamerInfo* info = static_cast<TStreamerInfo*>(o);
338         std::cout << "AliFMDMap class version read is " 
339                   <<  info->GetClassVersion() << std::endl;
340       }
341     }
342     // fESD->CheckNeedUShort(fChainE->GetFile());
343 #endif
344   }
345
346   // Possibly load FMD Digit information 
347   if (TESTBIT(fTreeMask, kRaw)) {
348     // AliInfo("Getting FMD raw data digits");
349     if (!fReader->NextEvent()) return kFALSE;
350     AliFMDRawReader r(fReader, 0);
351     fArrayA->Clear();
352     r.ReadAdcs(fArrayA);
353   }
354   fEventCount++;
355   return kTRUE;
356 }
357
358
359 //____________________________________________________________________
360 Bool_t 
361 AliFMDInput::Event()
362 {
363   // Process one event.  The default implementation one or more of 
364   //
365   //   -  ProcessHits       if the hits are loaded. 
366   //   -  ProcessDigits     if the digits are loaded. 
367   //   -  ProcessSDigits    if the sumbable digits are loaded. 
368   //   -  ProcessRecPoints  if the reconstructed points are loaded. 
369   //   -  ProcessESD        if the event summary data is loaded
370   // 
371   if (TESTBIT(fTreeMask, kHits)) 
372     if (!ProcessHits()) return kFALSE; 
373   if (TESTBIT(fTreeMask, kTracks)) 
374     if (!ProcessTracks()) return kFALSE; 
375   if (TESTBIT(fTreeMask, kDigits)) 
376     if (!ProcessDigits()) return kFALSE;
377   if (TESTBIT(fTreeMask, kSDigits)) 
378     if (!ProcessSDigits()) return kFALSE;
379   if (TESTBIT(fTreeMask, kRaw)) 
380     if (!ProcessRawDigits()) return kFALSE;
381   if (TESTBIT(fTreeMask, kRecPoints)) 
382     if (!ProcessRecPoints()) return kFALSE;
383   if (TESTBIT(fTreeMask, kESD))
384     if (!ProcessESDs()) return kFALSE;
385   
386   return kTRUE;
387 }
388
389 //____________________________________________________________________
390 Bool_t 
391 AliFMDInput::ProcessHits()
392 {
393   // Read the hit tree, and pass each hit to the member function
394   // ProcessHit.
395   if (!fTreeH) {
396     AliError("No hit tree defined");
397     return kFALSE;
398   }
399   if (!fArrayH) {
400     AliError("No hit array defined");
401     return kFALSE;
402   }
403
404   Int_t nTracks = fTreeH->GetEntries();
405   for (Int_t i = 0; i < nTracks; i++) {
406     Int_t hitRead  = fTreeH->GetEntry(i);
407     if (hitRead <= 0) continue;
408
409     Int_t nHit = fArrayH->GetEntries();
410     if (nHit <= 0) continue;
411   
412     for (Int_t j = 0; j < nHit; j++) {
413       AliFMDHit* hit = static_cast<AliFMDHit*>(fArrayH->At(j));
414       if (!hit) continue;
415
416       TParticle* track = 0;
417       if (TESTBIT(fTreeMask, kKinematics) && fStack) {
418         Int_t trackno = hit->Track();
419         track = fStack->Particle(trackno);
420       }
421       if (!ProcessHit(hit, track)) return kFALSE;
422     }    
423   }
424   return kTRUE;
425 }
426
427 //____________________________________________________________________
428 Bool_t 
429 AliFMDInput::ProcessTracks()
430 {
431   // Read the hit tree, and pass each hit to the member function
432   // ProcessTrack.
433   if (!fStack) {
434     AliError("No track tree defined");
435     return kFALSE;
436   }
437   if (!fTreeH) {
438     AliError("No hit tree defined");
439     return kFALSE;
440   }
441   if (!fArrayH) {
442     AliError("No hit array defined");
443     return kFALSE;
444   }
445
446   // Int_t nTracks = fStack->GetNtrack();
447   Int_t nTracks = fTreeH->GetEntries();
448   for (Int_t i = 0; i < nTracks; i++) {
449     Int_t      trackno = nTracks - i - 1;
450     TParticle* track   = fStack->Particle(trackno);
451     if (!track) continue;
452
453     // Get the hits for this track. 
454     Int_t hitRead  = fTreeH->GetEntry(i);
455     Int_t nHit     = fArrayH->GetEntries();
456     if (nHit == 0 || hitRead <= 0) { 
457       // Let user code see the track, even if there's no hits. 
458       if (!ProcessTrack(trackno, track, 0)) return kFALSE;
459       continue;
460     }
461
462     // Loop over the hits corresponding to this track.
463     for (Int_t j = 0; j < nHit; j++) {
464       AliFMDHit* hit = static_cast<AliFMDHit*>(fArrayH->At(j));
465       if (!ProcessTrack(trackno, track, hit)) return kFALSE;
466     }   
467   }
468   return kTRUE;
469 }
470
471 //____________________________________________________________________
472 Bool_t 
473 AliFMDInput::ProcessDigits()
474 {
475   // Read the digit tree, and pass each digit to the member function
476   // ProcessDigit.
477   Int_t nEv = fTreeD->GetEntries();
478   for (Int_t i = 0; i < nEv; i++) {
479     Int_t digitRead  = fTreeD->GetEntry(i);
480     if (digitRead <= 0) continue;
481     Int_t nDigit = fArrayD->GetEntries();
482     if (nDigit <= 0) continue;
483     for (Int_t j = 0; j < nDigit; j++) {
484       AliFMDDigit* digit = static_cast<AliFMDDigit*>(fArrayD->At(j));
485       if (!digit) continue;
486       if (!ProcessDigit(digit)) return kFALSE;
487     }    
488   }
489   return kTRUE;
490 }
491
492 //____________________________________________________________________
493 Bool_t 
494 AliFMDInput::ProcessSDigits()
495 {
496   // Read the summable digit tree, and pass each sumable digit to the
497   // member function ProcessSdigit.
498   Int_t nEv = fTreeD->GetEntries();
499   for (Int_t i = 0; i < nEv; i++) {
500     Int_t sdigitRead  = fTreeS->GetEntry(i);
501     if (sdigitRead <= 0) continue;
502     Int_t nSdigit = fArrayS->GetEntries();
503     if (nSdigit <= 0) continue;
504     for (Int_t j = 0; j < nSdigit; j++) {
505       AliFMDSDigit* sdigit = static_cast<AliFMDSDigit*>(fArrayS->At(j));
506       if (!sdigit) continue;
507       if (!ProcessSDigit(sdigit)) return kFALSE;
508     }    
509   }
510   return kTRUE;
511 }
512
513 //____________________________________________________________________
514 Bool_t 
515 AliFMDInput::ProcessRawDigits()
516 {
517   // Read the digit tree, and pass each digit to the member function
518   // ProcessDigit.
519   Int_t nDigit = fArrayA->GetEntries();
520   if (nDigit <= 0) return kTRUE;
521   for (Int_t j = 0; j < nDigit; j++) {
522     AliFMDDigit* digit = static_cast<AliFMDDigit*>(fArrayA->At(j));
523     if (!digit) continue;
524     if (!ProcessRawDigit(digit)) return kFALSE;
525   }    
526   return kTRUE;
527 }
528
529 //____________________________________________________________________
530 Bool_t 
531 AliFMDInput::ProcessRecPoints()
532 {
533   // Read the reconstrcted points tree, and pass each reconstruction
534   // object (AliFMDRecPoint) to either ProcessRecPoint.
535   Int_t nEv = fTreeR->GetEntries();
536   for (Int_t i = 0; i < nEv; i++) {
537     Int_t recRead  = fTreeR->GetEntry(i);
538     if (recRead <= 0) continue;
539     Int_t nRecPoint = fArrayR->GetEntries();
540     for (Int_t j = 0; j < nRecPoint; j++) {
541       AliFMDRecPoint* recPoint = static_cast<AliFMDRecPoint*>(fArrayR->At(j));
542       if (!recPoint) continue;
543       if (!ProcessRecPoint(recPoint)) return kFALSE;
544     }    
545   }
546   return kTRUE;
547 }
548
549 //____________________________________________________________________
550 Bool_t 
551 AliFMDInput::ProcessESDs()
552 {
553   // Process event summary data
554   if (!fESD) return kFALSE;
555   for (UShort_t det = 1; det <= 3; det++) {
556     Char_t rings[] = { 'I', (det == 1 ? '\0' : 'O'), '\0' };
557     for (Char_t* rng = rings; *rng != '\0'; rng++) {
558       UShort_t nsec = (*rng == 'I' ?  20 :  40);
559       UShort_t nstr = (*rng == 'I' ? 512 : 256);
560       for (UShort_t sec = 0; sec < nsec; sec++) {
561         for (UShort_t str = 0; str < nstr; str++) {
562           Float_t eta  = fESD->Eta(det,*rng,sec,str);
563           Float_t mult = fESD->Multiplicity(det,*rng,sec,str);
564           if (!fESD->IsAngleCorrected()) 
565             mult *= TMath::Abs(TMath::Cos(2.*TMath::ATan(TMath::Exp(-eta))));
566           if (!ProcessESD(det, *rng, sec, str, eta, mult)) continue;
567         }
568       }
569     }
570   }
571   return kTRUE;
572 }
573
574 //____________________________________________________________________
575 Bool_t
576 AliFMDInput::End()
577 {
578   // Called at the end of each event.  Per default, it unloads the
579   // data trees and resets the pointers to the output arrays.   Users
580   // can overload this, but should call this member function in the
581   // overloaded member function of the derived class. 
582
583   // Check if we have been initialized
584   if (!fIsInit) { 
585     AliError("Not initialized");
586     return fIsInit;
587   }
588   // Possibly unload global kinematics information 
589   if (TESTBIT(fTreeMask, kKinematics) || TESTBIT(fTreeMask, kTracks)) {
590     fLoader->UnloadKinematics();
591     // fTreeK = 0;
592     fStack = 0;
593   }
594   // Possibly unload FMD Hit information 
595   if (TESTBIT(fTreeMask, kHits) || TESTBIT(fTreeMask, kTracks)) {
596     fFMDLoader->UnloadHits();
597     fTreeH = 0;
598   }
599   // Possibly unload FMD Digit information 
600   if (TESTBIT(fTreeMask, kDigits)) {
601     fFMDLoader->UnloadDigits();
602     fTreeD = 0;
603   }
604   // Possibly unload FMD Sdigit information 
605   if (TESTBIT(fTreeMask, kSDigits)) {
606     fFMDLoader->UnloadSDigits();
607     fTreeS = 0;
608   }
609   // Possibly unload FMD RecPoints information 
610   if (TESTBIT(fTreeMask, kRecPoints)) {
611     fFMDLoader->UnloadRecPoints();
612     fTreeR = 0;
613   }
614   // AliInfo("Now out event");
615   return kTRUE;
616 }
617
618 //____________________________________________________________________
619 Bool_t
620 AliFMDInput::Run()
621 {
622   // Run over all events and files references in galice.root 
623
624   Bool_t retval;
625   if (!(retval = Init())) return retval;
626
627   Int_t nEvents = NEvents();
628   for (Int_t event = 0; event < nEvents; event++) {
629     if (!(retval = Begin(event))) break;
630     if (!(retval = Event())) break;
631     if (!(retval = End())) break;
632   }
633   if (!retval) return retval;
634   retval = Finish();
635   return retval;
636 }
637
638 //__________________________________________________________________
639 TArrayF 
640 AliFMDInput::MakeLogScale(Int_t n, Double_t min, Double_t max) 
641 {
642   // Service function to define a logarithmic axis. 
643   // Parameters: 
644   //   n    Number of bins 
645   //   min  Minimum of axis 
646   //   max  Maximum of axis 
647   TArrayF bins(n+1);
648   bins[0]      = min;
649   if (n <= 20) {
650     for (Int_t i = 1; i < n+1; i++) bins[i] = bins[i-1] + (max-min)/n;
651     return bins;
652   }
653   Float_t dp   = n / TMath::Log10(max / min);
654   Float_t pmin = TMath::Log10(min);
655   for (Int_t i = 1; i < n+1; i++) {
656     Float_t p = pmin + i / dp;
657     bins[i]   = TMath::Power(10, p);
658   }
659   return bins;
660 }
661
662
663
664 //____________________________________________________________________
665 //
666 // EOF
667 //