]> git.uio.no Git - u/mrichter/AliRoot.git/blob - FMD/AliFMDInput.cxx
Added some fancy flow stuff and scripts
[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 heaedr information 
295   if (TESTBIT(fTreeMask, kHeader)) {
296     // AliInfo("Getting FMD hits");
297     if (!fLoader /* || fLoader->LoadHeader()*/) return kFALSE;
298     fHeader = fLoader->GetHeader();
299   }
300
301   // Possibly load FMD Digit information 
302   if (TESTBIT(fTreeMask, kDigits)) {
303     // AliInfo("Getting FMD digits");
304     if (!fFMDLoader || fFMDLoader->LoadDigits()) return kFALSE;
305     fTreeD = fFMDLoader->TreeD();
306     if (fTreeD) {
307       if (!fArrayD) fArrayD = fFMD->Digits();
308     }
309     else {
310       fArrayD = 0;
311       AliWarning(Form("Failed to load FMD Digits"));
312     } 
313   }
314
315   // Possibly load FMD Sdigit information 
316   if (TESTBIT(fTreeMask, kSDigits)) {
317     // AliInfo("Getting FMD summable digits");
318     if (!fFMDLoader || fFMDLoader->LoadSDigits()) return kFALSE;
319     fTreeS = fFMDLoader->TreeS();
320     if (!fArrayS) fArrayS = fFMD->SDigits();
321   }
322
323   // Possibly load FMD RecPoints information 
324   if (TESTBIT(fTreeMask, kRecPoints)) {
325     // AliInfo("Getting FMD reconstructed points");
326     if (!fFMDLoader || fFMDLoader->LoadRecPoints()) return kFALSE;
327     fTreeR = fFMDLoader->TreeR();
328     if (!fArrayR) fArrayR = new TClonesArray("AliFMDRecPoint");
329     fTreeR->SetBranchAddress("FMD",  &fArrayR);
330   }  
331
332   // Possibly load FMD ESD information 
333   if (TESTBIT(fTreeMask, kESD)) {
334     // AliInfo("Getting FMD event summary data");
335     Int_t read = fChainE->GetEntry(event);
336     if (read <= 0) return kFALSE;
337     fESD = fESDEvent->GetFMDData();
338     if (!fESD) return kFALSE;
339 #if 0
340     TFile* f = fChainE->GetFile();
341     if (f) {
342       TObject* o = f->GetStreamerInfoList()->FindObject("AliFMDMap");
343       if (o) {
344         TStreamerInfo* info = static_cast<TStreamerInfo*>(o);
345         std::cout << "AliFMDMap class version read is " 
346                   <<  info->GetClassVersion() << std::endl;
347       }
348     }
349     // fESD->CheckNeedUShort(fChainE->GetFile());
350 #endif
351   }
352
353   // Possibly load FMD Digit information 
354   if (TESTBIT(fTreeMask, kRaw)) {
355     // AliInfo("Getting FMD raw data digits");
356     if (!fReader->NextEvent()) return kFALSE;
357     AliFMDRawReader r(fReader, 0);
358     fArrayA->Clear();
359     r.ReadAdcs(fArrayA);
360   }
361   fEventCount++;
362   return kTRUE;
363 }
364
365
366 //____________________________________________________________________
367 Bool_t 
368 AliFMDInput::Event()
369 {
370   // Process one event.  The default implementation one or more of 
371   //
372   //   -  ProcessHits       if the hits are loaded. 
373   //   -  ProcessDigits     if the digits are loaded. 
374   //   -  ProcessSDigits    if the sumbable digits are loaded. 
375   //   -  ProcessRecPoints  if the reconstructed points are loaded. 
376   //   -  ProcessESD        if the event summary data is loaded
377   // 
378   if (TESTBIT(fTreeMask, kHits)) 
379     if (!ProcessHits()) return kFALSE; 
380   if (TESTBIT(fTreeMask, kTracks)) 
381     if (!ProcessTracks()) return kFALSE; 
382   if (TESTBIT(fTreeMask, kDigits)) 
383     if (!ProcessDigits()) return kFALSE;
384   if (TESTBIT(fTreeMask, kSDigits)) 
385     if (!ProcessSDigits()) return kFALSE;
386   if (TESTBIT(fTreeMask, kRaw)) 
387     if (!ProcessRawDigits()) return kFALSE;
388   if (TESTBIT(fTreeMask, kRecPoints)) 
389     if (!ProcessRecPoints()) return kFALSE;
390   if (TESTBIT(fTreeMask, kESD))
391     if (!ProcessESDs()) return kFALSE;
392   
393   return kTRUE;
394 }
395
396 //____________________________________________________________________
397 Bool_t 
398 AliFMDInput::ProcessHits()
399 {
400   // Read the hit tree, and pass each hit to the member function
401   // ProcessHit.
402   if (!fTreeH) {
403     AliError("No hit tree defined");
404     return kFALSE;
405   }
406   if (!fArrayH) {
407     AliError("No hit array defined");
408     return kFALSE;
409   }
410
411   Int_t nTracks = fTreeH->GetEntries();
412   for (Int_t i = 0; i < nTracks; i++) {
413     Int_t hitRead  = fTreeH->GetEntry(i);
414     if (hitRead <= 0) continue;
415
416     Int_t nHit = fArrayH->GetEntries();
417     if (nHit <= 0) continue;
418   
419     for (Int_t j = 0; j < nHit; j++) {
420       AliFMDHit* hit = static_cast<AliFMDHit*>(fArrayH->At(j));
421       if (!hit) continue;
422
423       TParticle* track = 0;
424       if (TESTBIT(fTreeMask, kKinematics) && fStack) {
425         Int_t trackno = hit->Track();
426         track = fStack->Particle(trackno);
427       }
428       if (!ProcessHit(hit, track)) return kFALSE;
429     }    
430   }
431   return kTRUE;
432 }
433
434 //____________________________________________________________________
435 Bool_t 
436 AliFMDInput::ProcessTracks()
437 {
438   // Read the hit tree, and pass each hit to the member function
439   // ProcessTrack.
440   if (!fStack) {
441     AliError("No track tree defined");
442     return kFALSE;
443   }
444   if (!fTreeH) {
445     AliError("No hit tree defined");
446     return kFALSE;
447   }
448   if (!fArrayH) {
449     AliError("No hit array defined");
450     return kFALSE;
451   }
452
453   // Int_t nTracks = fStack->GetNtrack();
454   Int_t nTracks = fTreeH->GetEntries();
455   for (Int_t i = 0; i < nTracks; i++) {
456     Int_t      trackno = nTracks - i - 1;
457     TParticle* track   = fStack->Particle(trackno);
458     if (!track) continue;
459
460     // Get the hits for this track. 
461     Int_t hitRead  = fTreeH->GetEntry(i);
462     Int_t nHit     = fArrayH->GetEntries();
463     if (nHit == 0 || hitRead <= 0) { 
464       // Let user code see the track, even if there's no hits. 
465       if (!ProcessTrack(trackno, track, 0)) return kFALSE;
466       continue;
467     }
468
469     // Loop over the hits corresponding to this track.
470     for (Int_t j = 0; j < nHit; j++) {
471       AliFMDHit* hit = static_cast<AliFMDHit*>(fArrayH->At(j));
472       if (!ProcessTrack(trackno, track, hit)) return kFALSE;
473     }   
474   }
475   return kTRUE;
476 }
477
478 //____________________________________________________________________
479 Bool_t 
480 AliFMDInput::ProcessDigits()
481 {
482   // Read the digit tree, and pass each digit to the member function
483   // ProcessDigit.
484   Int_t nEv = fTreeD->GetEntries();
485   for (Int_t i = 0; i < nEv; i++) {
486     Int_t digitRead  = fTreeD->GetEntry(i);
487     if (digitRead <= 0) continue;
488     Int_t nDigit = fArrayD->GetEntries();
489     if (nDigit <= 0) continue;
490     for (Int_t j = 0; j < nDigit; j++) {
491       AliFMDDigit* digit = static_cast<AliFMDDigit*>(fArrayD->At(j));
492       if (!digit) continue;
493       if (!ProcessDigit(digit)) return kFALSE;
494     }    
495   }
496   return kTRUE;
497 }
498
499 //____________________________________________________________________
500 Bool_t 
501 AliFMDInput::ProcessSDigits()
502 {
503   // Read the summable digit tree, and pass each sumable digit to the
504   // member function ProcessSdigit.
505   Int_t nEv = fTreeD->GetEntries();
506   for (Int_t i = 0; i < nEv; i++) {
507     Int_t sdigitRead  = fTreeS->GetEntry(i);
508     if (sdigitRead <= 0) continue;
509     Int_t nSdigit = fArrayS->GetEntries();
510     if (nSdigit <= 0) continue;
511     for (Int_t j = 0; j < nSdigit; j++) {
512       AliFMDSDigit* sdigit = static_cast<AliFMDSDigit*>(fArrayS->At(j));
513       if (!sdigit) continue;
514       if (!ProcessSDigit(sdigit)) return kFALSE;
515     }    
516   }
517   return kTRUE;
518 }
519
520 //____________________________________________________________________
521 Bool_t 
522 AliFMDInput::ProcessRawDigits()
523 {
524   // Read the digit tree, and pass each digit to the member function
525   // ProcessDigit.
526   Int_t nDigit = fArrayA->GetEntries();
527   if (nDigit <= 0) return kTRUE;
528   for (Int_t j = 0; j < nDigit; j++) {
529     AliFMDDigit* digit = static_cast<AliFMDDigit*>(fArrayA->At(j));
530     if (!digit) continue;
531     if (!ProcessRawDigit(digit)) return kFALSE;
532   }    
533   return kTRUE;
534 }
535
536 //____________________________________________________________________
537 Bool_t 
538 AliFMDInput::ProcessRecPoints()
539 {
540   // Read the reconstrcted points tree, and pass each reconstruction
541   // object (AliFMDRecPoint) to either ProcessRecPoint.
542   Int_t nEv = fTreeR->GetEntries();
543   for (Int_t i = 0; i < nEv; i++) {
544     Int_t recRead  = fTreeR->GetEntry(i);
545     if (recRead <= 0) continue;
546     Int_t nRecPoint = fArrayR->GetEntries();
547     for (Int_t j = 0; j < nRecPoint; j++) {
548       AliFMDRecPoint* recPoint = static_cast<AliFMDRecPoint*>(fArrayR->At(j));
549       if (!recPoint) continue;
550       if (!ProcessRecPoint(recPoint)) return kFALSE;
551     }    
552   }
553   return kTRUE;
554 }
555
556 //____________________________________________________________________
557 Bool_t 
558 AliFMDInput::ProcessESDs()
559 {
560   // Process event summary data
561   if (!fESD) return kFALSE;
562   for (UShort_t det = 1; det <= 3; det++) {
563     Char_t rings[] = { 'I', (det == 1 ? '\0' : 'O'), '\0' };
564     for (Char_t* rng = rings; *rng != '\0'; rng++) {
565       UShort_t nsec = (*rng == 'I' ?  20 :  40);
566       UShort_t nstr = (*rng == 'I' ? 512 : 256);
567       for (UShort_t sec = 0; sec < nsec; sec++) {
568         for (UShort_t str = 0; str < nstr; str++) {
569           Float_t eta  = fESD->Eta(det,*rng,sec,str);
570           Float_t mult = fESD->Multiplicity(det,*rng,sec,str);
571           if (!fESD->IsAngleCorrected()) 
572             mult *= TMath::Abs(TMath::Cos(2.*TMath::ATan(TMath::Exp(-eta))));
573           if (!ProcessESD(det, *rng, sec, str, eta, mult)) continue;
574         }
575       }
576     }
577   }
578   return kTRUE;
579 }
580
581 //____________________________________________________________________
582 Bool_t
583 AliFMDInput::End()
584 {
585   // Called at the end of each event.  Per default, it unloads the
586   // data trees and resets the pointers to the output arrays.   Users
587   // can overload this, but should call this member function in the
588   // overloaded member function of the derived class. 
589
590   // Check if we have been initialized
591   if (!fIsInit) { 
592     AliError("Not initialized");
593     return fIsInit;
594   }
595   // Possibly unload global kinematics information 
596   if (TESTBIT(fTreeMask, kKinematics) || TESTBIT(fTreeMask, kTracks)) {
597     fLoader->UnloadKinematics();
598     // fTreeK = 0;
599     fStack = 0;
600   }
601   // Possibly unload FMD Hit information 
602   if (TESTBIT(fTreeMask, kHits) || TESTBIT(fTreeMask, kTracks)) {
603     fFMDLoader->UnloadHits();
604     fTreeH = 0;
605   }
606   // Possibly unload FMD Digit information 
607   if (TESTBIT(fTreeMask, kDigits)) {
608     fFMDLoader->UnloadDigits();
609     fTreeD = 0;
610   }
611   // Possibly unload FMD Sdigit information 
612   if (TESTBIT(fTreeMask, kSDigits)) {
613     fFMDLoader->UnloadSDigits();
614     fTreeS = 0;
615   }
616   // Possibly unload FMD RecPoints information 
617   if (TESTBIT(fTreeMask, kRecPoints)) {
618     fFMDLoader->UnloadRecPoints();
619     fTreeR = 0;
620   }
621   // AliInfo("Now out event");
622   return kTRUE;
623 }
624
625 //____________________________________________________________________
626 Bool_t
627 AliFMDInput::Run()
628 {
629   // Run over all events and files references in galice.root 
630
631   Bool_t retval;
632   if (!(retval = Init())) return retval;
633
634   Int_t nEvents = NEvents();
635   for (Int_t event = 0; event < nEvents; event++) {
636     if (!(retval = Begin(event))) break;
637     if (!(retval = Event())) break;
638     if (!(retval = End())) break;
639   }
640   if (!retval) return retval;
641   retval = Finish();
642   return retval;
643 }
644
645 //__________________________________________________________________
646 TArrayF 
647 AliFMDInput::MakeLogScale(Int_t n, Double_t min, Double_t max) 
648 {
649   // Service function to define a logarithmic axis. 
650   // Parameters: 
651   //   n    Number of bins 
652   //   min  Minimum of axis 
653   //   max  Maximum of axis 
654   TArrayF bins(n+1);
655   bins[0]      = min;
656   if (n <= 20) {
657     for (Int_t i = 1; i < n+1; i++) bins[i] = bins[i-1] + (max-min)/n;
658     return bins;
659   }
660   Float_t dp   = n / TMath::Log10(max / min);
661   Float_t pmin = TMath::Log10(min);
662   for (Int_t i = 1; i < n+1; i++) {
663     Float_t p = pmin + i / dp;
664     bins[i]   = TMath::Power(10, p);
665   }
666   return bins;
667 }
668
669
670
671 //____________________________________________________________________
672 //
673 // EOF
674 //