]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWGLF/FORWARD/analysis2/AliLandauGausFitter.h
Merge branch 'master' of https://git.cern.ch/reps/AliRoot
[u/mrichter/AliRoot.git] / PWGLF / FORWARD / analysis2 / AliLandauGausFitter.h
1 #ifndef ALILANDAUGAUSFITTER_H
2 #define ALILANDAUGAUSFITTER_H
3 /**
4  * @file   AliLandauGausFitter.h
5  * @author Christian Holm Christensen <cholm@nbi.dk>
6  * @date   Tue Mar 11 08:56:03 2014
7  * 
8  * @brief Declaration and implementation of fitter of Landau-Gauss
9  * distributions to energy loss spectra.
10  * 
11  * @ingroup pwglf_forward 
12  */
13 #include <AliLandauGaus.h>
14 #include <TH1.h>
15 #include <TF1.h>
16 #include <TMath.h>
17 #include <TObjArray.h>
18 #include <TString.h>
19 #include <TArray.h>
20 #include <TFitResult.h>
21 #include <TError.h>
22
23 /**
24  * Fit Landau-Gauss distributions to energy loss distributions 
25  * 
26  * @see AliLandauGaus 
27  *
28  * @ingroup pwglf_forward 
29  */
30 class AliLandauGausFitter 
31 {
32 public:
33   /** 
34    * Enumeration of parameters 
35    */
36   enum { 
37     /** Index of pre-constant @f$ C@f$ */
38     kC          = AliLandauGaus::kC,
39     /** Index of most probable value @f$ \Delta_p@f$ */
40     kDelta      = AliLandauGaus::kDelta, 
41     /** Index of Landau width @f$ \xi@f$ */
42     kXi         = AliLandauGaus::kXi, 
43     /** Index of Gaussian width @f$ \sigma@f$ */
44     kSigma      = AliLandauGaus::kSigma, 
45     /** Index of Gaussian additional width @f$ \sigma_n@f$ */
46     kSigmaN     = AliLandauGaus::kSigmaN,
47     /** Index of Number of particles @f$ N@f$ */
48     kN          = AliLandauGaus::kN, 
49     /** Base index of particle strengths @f$ a_i@f$ for 
50         @f$i=2,\ldots,N@f$ */
51     kA          = AliLandauGaus::kA
52   };
53   /** 
54    * Get the fit options to use 
55    * 
56    * @return Fit options used through-out
57    */
58   static const char* GetFitOptions() { return "RNS"; }
59   /** 
60    * Constructor 
61    * 
62    * @param lowCut     Lower cut of spectrum - data below this cuts is ignored
63    * @param maxRange   Maximum range to fit to 
64    * @param minusBins  The number of bins below maximum to use 
65    */
66   AliLandauGausFitter(Double_t lowCut, Double_t maxRange, UShort_t minusBins)
67     : fLowCut(lowCut), fMaxRange(maxRange), fMinusBins(minusBins), 
68       fFitResults(0), fFunctions(0), fDebug(false) 
69   {
70     fFitResults.SetOwner();
71     fFunctions.SetOwner();
72   }
73   /** 
74    * Destructor
75    * 
76    */
77   virtual ~AliLandauGausFitter()
78   {
79     fFitResults.Delete();
80     fFunctions.Delete();
81   }
82   /** 
83    * Enable/disable debugging output. 
84    * 
85    * @param debug If true, enable debugging output
86    */
87   void SetDebug(Bool_t debug=true) { fDebug = debug; }
88   /** 
89    * Clear internal arrays
90    * 
91    */
92   void Clear() 
93   {
94     fFitResults.Clear();
95     fFunctions.Clear();
96   }
97   /** 
98    * Fit a 1-particle signal to the passed energy loss distribution 
99    * 
100    * Note that this function clears the internal arrays first 
101    *
102    * @param dist    Data to fit the function to 
103    * @param sigman If larger than zero, the initial guess of the
104    *               detector induced noise. If zero or less, then this 
105    *               parameter is ignored in the fit (fixed at 0)
106    * 
107    * @return The function fitted to the data 
108    */
109   TF1* Fit1Particle(TH1* dist, Double_t sigman=-1);
110   /** 
111    * Fit a N-particle signal to the passed energy loss distribution 
112    *
113    * If there's no 1-particle fit present, it does that first 
114    *
115    * @param dist   Data to fit the function to 
116    * @param n      Number of particle signals to fit 
117    * @param sigman If larger than zero, the initial guess of the
118    *               detector induced noise. If zero or less, then this 
119    *               parameter is ignored in the fit (fixed at 0)
120    * 
121    * @return The function fitted to the data 
122    */
123   TF1* FitNParticle(TH1* dist, UShort_t n, Double_t sigman=-1);
124   /** 
125    * Fit a composite distribution of energy loss from both primaries
126    * and secondaries
127    * 
128    * @param dist   Distribution 
129    * @param sigman If larger than zero, the initial guess of the
130    *                detector included noise.  If zero or less this
131    *                parameter is fixed to 0.
132    * 
133    * @return Function fitted to the data 
134    */
135   TF1* FitComposite(TH1* dist, Double_t sigman);
136   /**
137    * Get Lower cut on data 
138    *
139    * @return Lower cut on data 
140    */
141   Double_t GetLowCut() const { return fLowCut; }
142   /**
143    * Get Maximum range to fit 
144    *
145    * @return Maximum range to fit 
146    */
147   Double_t GetMaxRange() const { return fMaxRange; }
148   /**
149    * Get Number of bins from maximum to fit 1st peak
150    *
151    * @return Number of bins from maximum to fit 1st peak
152    */
153   UShort_t GetMinusBins() const { return fMinusBins; }
154   /**
155    * Get Array of fit results 
156    *
157    * @return Array of fit results 
158    */
159   const TObjArray& GetFitResults() const { return fFitResults; }
160   /** 
161    * Get Array of fit results  
162    *
163    * @return Array of fit results 
164    */
165   TObjArray& GetFitResults() { return fFitResults; }
166   /**
167    * Get Array of functions 
168    *
169    * @return Array of functions 
170    */
171   const TObjArray& GetFunctions() const { return fFunctions; }
172   /** 
173    * Get Array of functions  
174    *
175    * @return Array of functions 
176    */
177   TObjArray& GetFunctions() { return fFunctions; }  
178 protected:
179   /** 
180    * Set parameter limits on the function @a f.  The limits are only
181    * set if @f$ low \le test \le high@f$
182    * 
183    * @param f     Function object 
184    * @param iPar  Parameter number 
185    * @param test  Initial guess
186    * @param low   Low limit 
187    * @param high  high limit 
188    */
189   void SetParLimits(TF1* f, Int_t iPar, Double_t test, 
190                     Double_t low, Double_t high) 
191   {
192     if (test < low || test > high) {
193       ::Warning("","Initial value of %s=%f not in [%f,%f]", 
194                 f->GetParName(iPar), test, low, high);
195       return;
196     }
197     if (fDebug) 
198       ::Info(/*"SetParLimits"*/"", "Set par limits on %-12s=%f: [%f,%f]",
199              f->GetParName(iPar), test, low, high);
200     f->SetParLimits(iPar, low, high);
201   }
202   const Double_t fLowCut;     // Lower cut on data 
203   const Double_t fMaxRange;   // Maximum range to fit 
204   const UShort_t fMinusBins;  // Number of bins from maximum to fit 1st peak
205   TObjArray fFitResults;      // Array of fit results 
206   TObjArray fFunctions;       // Array of functions 
207   Bool_t    fDebug;           // Debug flag
208 };
209
210
211 //____________________________________________________________________
212 inline TF1*
213 AliLandauGausFitter::Fit1Particle(TH1* dist, Double_t sigman)
214 {
215   // Clear the cache 
216   Clear();
217
218   // Find the fit range 
219   Int_t    cutBin  = TMath::Max(dist->GetXaxis()->FindBin(fLowCut),3);
220   Int_t    maxBin  = TMath::Min(dist->GetXaxis()->FindBin(fMaxRange),
221                                 dist->GetNbinsX());
222   dist->GetXaxis()->SetRange(cutBin, maxBin);
223   // dist->GetXaxis()->SetRangeUser(fLowCut, fMaxRange);
224   
225   // Get the bin with maximum 
226   Int_t    peakBin = dist->GetMaximumBin();
227   Double_t peakE   = dist->GetBinLowEdge(peakBin);
228   Double_t rmsE    = dist->GetRMS();
229   
230   // Get the low edge 
231   // dist->GetXaxis()->SetRangeUser(fLowCut, peakE);
232   Int_t    minBin = peakBin - fMinusBins; // dist->GetMinimumBin();
233   Double_t minE   = TMath::Max(dist->GetBinCenter(minBin),fLowCut);
234   Double_t maxE   = dist->GetBinCenter(peakBin+2*fMinusBins);
235
236   Int_t    minEb = dist->GetXaxis()->FindBin(minE);
237   Int_t    maxEb = dist->GetXaxis()->FindBin(maxE);
238   Double_t intg  = dist->Integral(minEb, maxEb);
239   if (intg <= 0) {
240     ::Warning("Fit1Particle", 
241               "Integral of %s between [%f,%f] [%03d,%03d] = %f < 0", 
242               dist->GetName(), minE, maxE, minEb, maxEb, intg);
243     return 0;
244   }
245     
246   // Restore the range 
247   dist->GetXaxis()->SetRange(1, maxBin);
248   
249   // Define the function to fit 
250   TF1* f = AliLandauGaus::MakeF1(intg,peakE,peakE/10,peakE/5,sigman,minE,maxE);
251   SetParLimits(f, kDelta, peakE,   minE, fMaxRange);
252   SetParLimits(f, kXi,    peakE,   0,    rmsE); // 0.1
253   SetParLimits(f, kSigma, peakE/5, 1e-5, rmsE); // 0.1
254   if (sigman <= 0)  
255     f->FixParameter(kSigmaN, 0);
256   else 
257     SetParLimits(f, kSigmaN, peakE, 0, rmsE);
258   
259
260   TString opts(Form("%s%s", GetFitOptions(), fDebug ? "" : "Q"));
261   // Do the fit, getting the result object 
262   if (fDebug) 
263     ::Info(/*"Fit1Particle"*/"", "Fitting in the range %f,%f", minE, maxE);
264   TFitResultPtr r = dist->Fit(f, opts, "", minE, maxE);
265   if (!r.Get()) { 
266     ::Warning("Fit1Particle", 
267               "No fit returned when processing %s in the range [%f,%f] "
268               "options %s", dist->GetName(), minE, maxE, opts.Data());
269     return 0;
270   }
271   // f->SetRange(minE, fMaxRange);
272   fFitResults.AddAtAndExpand(new TFitResult(*r), 0);
273   fFunctions.AddAtAndExpand(f, 0);
274
275   return f;
276 }
277 //____________________________________________________________________
278 inline TF1*
279 AliLandauGausFitter::FitNParticle(TH1* dist, UShort_t n, Double_t sigman)
280 {
281   // Get the seed fit result 
282   TFitResult* r = static_cast<TFitResult*>(fFitResults.At(0));
283   TF1*        f = static_cast<TF1*>(fFunctions.At(0));
284   if (!r || !f) { 
285     f = Fit1Particle(dist, sigman);
286     r = static_cast<TFitResult*>(fFitResults.At(0));
287     if (!r || !f) { 
288       ::Warning("FitNLandau", "No first shot at landau fit");
289       return 0;
290     }
291   }
292
293   // Get some parameters from seed fit 
294   Double_t delta1  = r->Parameter(kDelta);
295   Double_t xi1     = r->Parameter(kXi);
296   Double_t maxEi   = n * (delta1 + xi1 * TMath::Log(n)) + 2 * n * xi1;
297   Double_t minE    = f->GetXmin();
298
299   Int_t    minEb = dist->GetXaxis()->FindBin(minE);
300   Int_t    maxEb = dist->GetXaxis()->FindBin(maxEi);
301   Double_t rmsE  = dist->GetRMS();
302   Double_t intg  = dist->Integral(minEb, maxEb);
303   if (intg <= 0) {
304     ::Warning("FitNParticle",
305               "Integral of %s between [%f,%f] [%03d,%03d] = %f < 0", 
306               dist->GetName(), minE, maxEi, minEb, maxEb, intg);
307     return 0;
308   }
309
310   // Array of weights 
311   TArrayD a(n-1);
312   for (UShort_t i = 2; i <= n; i++) 
313     a.fArray[i-2] = (n == 2 ? 0.05 : 0.000001);
314   // Make the fit function 
315   f = AliLandauGaus::MakeFn(r->Parameter(kC),
316                             r->Parameter(kDelta),
317                             r->Parameter(kXi),
318                             r->Parameter(kSigma),
319                             r->Parameter(kSigmaN),
320                             n, a.fArray, minE, maxEi);
321   SetParLimits(f,kDelta, f->GetParameter(kDelta), minE, fMaxRange);
322   SetParLimits(f,kXi,    f->GetParameter(kXi),    0,    2*rmsE); //0.1
323   SetParLimits(f,kSigma, f->GetParameter(kSigma), 1e-5, 2*rmsE); //0.1
324   if (sigman <= 0)  f->FixParameter(kSigmaN, 0);
325   else 
326     SetParLimits(f,kSigmaN, f->GetParameter(kSigmaN), 0, rmsE);
327
328   // Set the range and name of the scale parameters 
329   for (UShort_t i = 2; i <= n; i++) {// Take parameters from last fit 
330     SetParLimits(f,kA+i-2, a[i-2], 0, 1);
331   }
332
333   // Do the fit 
334   TString opts(Form("%s%s", GetFitOptions(), fDebug ? "" : "Q"));
335   if (fDebug) 
336     ::Info(/*"FitNParticle"*/"", 
337            "Fitting in the range %f,%f (%d)", minE, maxEi, n);
338   TFitResultPtr tr = dist->Fit(f, opts, "", minE, maxEi);
339   
340   // f->SetRange(minE, fMaxRange);
341   fFitResults.AddAtAndExpand(new TFitResult(*tr), n-1);
342   fFunctions.AddAtAndExpand(f, n-1);
343   
344   return f;
345 }  
346 //____________________________________________________________________
347 inline TF1*
348 AliLandauGausFitter::FitComposite(TH1* dist, Double_t sigman)
349 {
350   // Find the fit range 
351   Int_t    cutBin  = TMath::Max(dist->GetXaxis()->FindBin(fLowCut),3);
352   Int_t    maxBin  = TMath::Min(dist->GetXaxis()->FindBin(fMaxRange),
353                                 dist->GetNbinsX());
354   dist->GetXaxis()->SetRange(cutBin, maxBin);
355   
356   // Get the bin with maximum 
357   Int_t    peakBin = dist->GetMaximumBin();
358   Double_t peakE   = dist->GetBinLowEdge(peakBin);
359   
360   // Get the low edge 
361   // dist->GetXaxis()->SetRangeUser(fLowCut, peakE);
362   Int_t    minBin = peakBin - fMinusBins; // dist->GetMinimumBin();
363   Double_t minE   = TMath::Max(dist->GetBinCenter(minBin),fLowCut);
364   Double_t maxE   = dist->GetBinCenter(peakBin+2*fMinusBins);
365
366   // Get the range in bins and the integral of that range 
367   Int_t    minEb = dist->GetXaxis()->FindBin(minE);
368   Int_t    maxEb = dist->GetXaxis()->FindBin(maxE);
369   Double_t intg  = dist->Integral(minEb, maxEb);
370   if (intg <= 0) {
371     ::Warning("Fit1Particle", 
372               "Integral of %s between [%f,%f] [%03d,%03d] = %f < 0", 
373               dist->GetName(), minE, maxE, minEb, maxEb, intg);
374     return 0;
375   }
376     
377   // Restore the range 
378   dist->GetXaxis()->SetRange(1, maxBin);
379   
380   // Define the function to fit 
381   TF1* seed = AliLandauGaus::MakeF1(1,peakE,peakE/10,peakE/5,sigman,minE,maxE);
382
383   // Set initial guesses, parameter names, and limits  
384   seed->SetParLimits(kDelta, minE, fMaxRange);
385   seed->SetParLimits(kXi,    0.00, 0.1);
386   seed->SetParLimits(kSigma, 1e-5, 0.1);
387   if (sigman <= 0)  seed->FixParameter(kSigmaN, 0);
388   else              seed->SetParLimits(kSigmaN, 0, fMaxRange);
389
390   // Do the fit, getting the result object 
391   if (fDebug) 
392     ::Info(/*"FitComposite"*/"", "Fitting seed in the range %f,%f", minE, maxE);
393   /* TFitResultPtr r = */ dist->Fit(seed, GetFitOptions(), "", minE, maxE);
394
395   maxE = dist->GetXaxis()->GetXmax();
396   TF1* comp = 
397     AliLandauGaus::MakeComposite(0.8 * seed->GetParameter(kC),
398                                  seed->GetParameter(kDelta),
399                                  seed->GetParameter(kDelta)/10,
400                                  seed->GetParameter(kDelta)/5, 
401                                  1.20 * seed->GetParameter(kC),
402                                  seed->GetParameter(kXi),
403                                  minE, maxE);
404   // comp->SetParLimits(kC,       minE, fMaxRange); // C
405   comp->SetParLimits(kDelta,      minE, fMaxRange); // Delta
406   comp->SetParLimits(kXi,         0.00, fMaxRange); // Xi 
407   comp->SetParLimits(kSigma,      1e-5, fMaxRange); // Sigma
408   // comp->SetParLimits(kSigma+1, minE, fMaxRange); // C
409   comp->SetParLimits(kSigma+2,    0.00, fMaxRange); // Xi'
410   comp->SetLineColor(kRed+1);
411   comp->SetLineWidth(3);
412   
413   // Do the fit, getting the result object 
414   TString opts(Form("%s%s", GetFitOptions(), fDebug ? "" : "Q"));
415   if (fDebug) 
416     ::Info("FitComposite", "Fitting composite in the range %f,%f", minE, maxE);
417   /* TFitResultPtr r = */ dist->Fit(comp, opts, "", minE, maxE);
418
419 #if 0
420   // This is to store the two components with the output
421   TF1* part1 = static_cast<TF1*>(seed->Clone("part1"));
422   part1->SetLineColor(kGreen+1);
423   part1->SetLineWidth(4);
424   part1->SetRange(minE, maxE);
425   part1->SetParameters(comp->GetParameter(0), // C 
426                        comp->GetParameter(1), // Delta
427                        comp->GetParameter(2), // Xi
428                        comp->GetParameter(3), // sigma
429                        0);
430   part1->Save(minE,maxE,0,0,0,0);
431   dist->GetListOfFunctions()->Add(part1);
432
433   TF1* part2 = static_cast<TF1*>(seed->Clone("part2"));
434   part2->SetLineColor(kBlue+1);
435   part2->SetLineWidth(4);
436   part2->SetRange(minE, maxE);
437   part2->SetParameters(comp->GetParameter(4), // C 
438                        comp->GetParameter(1), // Delta
439                        comp->GetParameter(5), // Xi
440                        comp->GetParameter(3), // sigma
441                        0);
442   part2->Save(minE,maxE,0,0,0,0);
443   dist->GetListOfFunctions()->Add(part2);
444 #endif
445   return comp;
446 }
447
448 #endif
449 // Local Variables: 
450 //  mode: C++
451 // End: