2 // Class to do the energy correction of FMD ESD data
4 #include "AliFMDEnergyFitter.h"
5 #include "AliForwardCorrectionManager.h"
13 #include <TClonesArray.h>
14 #include <TFitResult.h>
20 ClassImp(AliFMDEnergyFitter)
22 ; // This is for Emacs
25 const char* fgkEDistFormat = "%s_etabin%03d";
29 //____________________________________________________________________
30 AliFMDEnergyFitter::AliFMDEnergyFitter()
42 fUseIncreasingBins(true),
49 // Default Constructor - do not use
53 //____________________________________________________________________
54 AliFMDEnergyFitter::AliFMDEnergyFitter(const char* title)
55 : TNamed("fmdEnergyFitter", title),
66 fUseIncreasingBins(true),
76 // title Title of object - not significant
78 fEtaAxis.SetName("etaAxis");
79 fEtaAxis.SetTitle("#eta");
80 fRingHistos.Add(new RingHistos(1, 'I'));
81 fRingHistos.Add(new RingHistos(2, 'I'));
82 fRingHistos.Add(new RingHistos(2, 'O'));
83 fRingHistos.Add(new RingHistos(3, 'I'));
84 fRingHistos.Add(new RingHistos(3, 'O'));
87 //____________________________________________________________________
88 AliFMDEnergyFitter::AliFMDEnergyFitter(const AliFMDEnergyFitter& o)
92 fNParticles(o.fNParticles),
93 fMinEntries(o.fMinEntries),
94 fFitRangeBinWidth(o.fFitRangeBinWidth),
96 fDoMakeObject(o.fDoMakeObject),
100 fUseIncreasingBins(o.fUseIncreasingBins),
101 fMaxRelParError(o.fMaxRelParError),
102 fMaxChi2PerNDF(o.fMaxChi2PerNDF),
103 fMinWeight(o.fMinWeight),
110 // o Object to copy from
112 TIter next(&o.fRingHistos);
114 while ((obj = next())) fRingHistos.Add(obj);
117 //____________________________________________________________________
118 AliFMDEnergyFitter::~AliFMDEnergyFitter()
123 fRingHistos.Delete();
126 //____________________________________________________________________
128 AliFMDEnergyFitter::operator=(const AliFMDEnergyFitter& o)
131 // Assignment operator
134 // o Object to assign from
139 TNamed::operator=(o);
142 fNParticles = o.fNParticles;
143 fMinEntries = o.fMinEntries;
144 fFitRangeBinWidth= o.fFitRangeBinWidth;
146 fDoMakeObject = o.fDoMakeObject;
147 fEtaAxis.Set(o.fEtaAxis.GetNbins(),o.fEtaAxis.GetXmin(),o.fEtaAxis.GetXmax());
149 fMaxRelParError= o.fMaxRelParError;
150 fMaxChi2PerNDF = o.fMaxChi2PerNDF;
151 fMinWeight = o.fMinWeight;
153 fRingHistos.Delete();
154 TIter next(&o.fRingHistos);
156 while ((obj = next())) fRingHistos.Add(obj);
161 //____________________________________________________________________
162 AliFMDEnergyFitter::RingHistos*
163 AliFMDEnergyFitter::GetRingHistos(UShort_t d, Char_t r) const
166 // Get the ring histogram container
173 // Ring histogram container
177 case 1: idx = 0; break;
178 case 2: idx = 1 + (r == 'I' || r == 'i' ? 0 : 1); break;
179 case 3: idx = 3 + (r == 'I' || r == 'i' ? 0 : 1); break;
181 if (idx < 0 || idx >= fRingHistos.GetEntries()) return 0;
183 return static_cast<RingHistos*>(fRingHistos.At(idx));
186 //____________________________________________________________________
188 AliFMDEnergyFitter::Init(const TAxis& eAxis)
191 // Initialise the task
194 // etaAxis The eta axis to use. Note, that if the eta axis
195 // has already been set (using SetEtaAxis), then this parameter will be
198 if (fEtaAxis.GetNbins() == 0 ||
199 TMath::Abs(fEtaAxis.GetXmax() - fEtaAxis.GetXmin()) < 1e-7)
201 TIter next(&fRingHistos);
203 while ((o = static_cast<RingHistos*>(next())))
204 o->Init(fEtaAxis, fMaxE, fNEbins, fUseIncreasingBins);
206 //____________________________________________________________________
208 AliFMDEnergyFitter::SetEtaAxis(const TAxis& eAxis)
211 // Set the eta axis to use. This will force the code to use this
212 // eta axis definition - irrespective of whatever axis is passed to
213 // the Init member function. Therefore, this member function can be
214 // used to force another eta axis than one found in the correction
218 // etaAxis Eta axis to use
220 SetEtaAxis(eAxis.GetNbins(),eAxis.GetXmin(),eAxis.GetXmax());
222 //____________________________________________________________________
224 AliFMDEnergyFitter::SetEtaAxis(Int_t nBins, Double_t etaMin, Double_t etaMax)
227 // Set the eta axis to use. This will force the code to use this
228 // eta axis definition - irrespective of whatever axis is passed to
229 // the Init member function. Therefore, this member function can be
230 // used to force another eta axis than one found in the correction
234 // nBins Number of bins
235 // etaMin Minimum of the eta axis
236 // etaMax Maximum of the eta axis
238 fEtaAxis.Set(nBins,etaMin,etaMax);
241 //____________________________________________________________________
243 AliFMDEnergyFitter::Accumulate(const AliESDFMD& input,
247 // Fitter the input AliESDFMD object
251 // empty Whether the event is 'empty'
254 // True on success, false otherwise
256 for(UShort_t d = 1; d <= 3; d++) {
257 Int_t nRings = (d == 1 ? 1 : 2);
258 for (UShort_t q = 0; q < nRings; q++) {
259 Char_t r = (q == 0 ? 'I' : 'O');
260 UShort_t nsec = (q == 0 ? 20 : 40);
261 UShort_t nstr = (q == 0 ? 512 : 256);
263 RingHistos* histos = GetRingHistos(d, r);
265 for(UShort_t s = 0; s < nsec; s++) {
266 for(UShort_t t = 0; t < nstr; t++) {
267 Float_t mult = input.Multiplicity(d,r,s,t);
269 // Keep dead-channel information.
270 if(mult == AliESDFMD::kInvalidMult || mult > 10 || mult <= 0)
273 // Get the pseudo-rapidity
274 Double_t eta1 = input.Eta(d,r,s,t);
275 Int_t ieta = fEtaAxis.FindBin(eta1);
276 if (ieta < 1 || ieta > fEtaAxis.GetNbins()) continue;
278 histos->Fill(empty, ieta-1, mult);
288 //____________________________________________________________________
290 AliFMDEnergyFitter::Fit(const TList* dir)
293 // Scale the histograms to the total number of events
296 // dir Where the histograms are
298 if (!fDoFits) return;
300 TList* d = static_cast<TList*>(dir->FindObject(GetName()));
306 // +fNParticles-1 for weights
307 Int_t nStack = kN+fNParticles;
308 THStack* stack[nStack];
309 stack[0] = new THStack("chi2", "#chi^{2}/#nu");
310 stack[kC +1] = new THStack("c", "Constant");
311 stack[kDelta +1] = new THStack("delta", "#Delta_{p}");
312 stack[kXi +1] = new THStack("xi", "#xi");
313 stack[kSigma +1] = new THStack("sigma", "#sigma");
314 stack[kSigmaN+1] = new THStack("sigman", "#sigma_{n}");
315 stack[kN +1] = new THStack("n", "# Particles");
316 for (Int_t i = 2; i <= fNParticles; i++)
317 stack[kN+i] = new THStack(Form("a%d", i), Form("a_{%d}", i));
318 for (Int_t i = 0; i < nStack; i++)
321 TIter next(&fRingHistos);
323 while ((o = static_cast<RingHistos*>(next()))) {
324 TObjArray* l = o->Fit(d, fEtaAxis, fLowCut, fNParticles,
325 fMinEntries, fFitRangeBinWidth,
326 fMaxRelParError, fMaxChi2PerNDF);
328 for (Int_t i = 0; i < l->GetEntriesFast(); i++) {
329 stack[i % nStack]->Add(static_cast<TH1*>(l->At(i)));
333 if (!fDoMakeObject) return;
335 MakeCorrectionsObject(d);
338 //____________________________________________________________________
340 AliFMDEnergyFitter::MakeCorrectionsObject(TList* d)
343 // Generate the corrections object
346 // dir List to analyse
348 AliForwardCorrectionManager& mgr = AliForwardCorrectionManager::Instance();
350 AliFMDCorrELossFit* obj = new AliFMDCorrELossFit;
351 obj->SetEtaAxis(fEtaAxis);
352 obj->SetLowCut(fLowCut);
354 TIter next(&fRingHistos);
356 while ((o = static_cast<RingHistos*>(next()))) {
357 o->FindBestFits(d, *obj, fEtaAxis, fMaxRelParError,
358 fMaxChi2PerNDF, fMinWeight);
361 TString oName(mgr.GetObjectName(AliForwardCorrectionManager::kELossFits));
362 TString fileName(mgr.GetFilePath(AliForwardCorrectionManager::kELossFits));
363 AliInfo(Form("Object %s created in output - should be extracted and copied "
364 "to %s", oName.Data(), fileName.Data()));
365 d->Add(obj, oName.Data());
368 //____________________________________________________________________
370 AliFMDEnergyFitter::DefineOutput(TList* dir)
373 // Define the output histograms. These are put in a sub list of the
374 // passed list. The histograms are merged before the parent task calls
375 // AliAnalysisTaskSE::Terminate
378 // dir Directory to add to
380 TList* d = new TList;
381 d->SetName(GetName());
384 TIter next(&fRingHistos);
386 while ((o = static_cast<RingHistos*>(next()))) {
390 //____________________________________________________________________
392 AliFMDEnergyFitter::SetDebug(Int_t dbg)
395 // Set the debug level. The higher the value the more output
401 TIter next(&fRingHistos);
403 while ((o = static_cast<RingHistos*>(next())))
407 //____________________________________________________________________
409 AliFMDEnergyFitter::Print(Option_t*) const
417 char ind[gROOT->GetDirLevel()+1];
418 for (Int_t i = 0; i < gROOT->GetDirLevel(); i++) ind[i] = ' ';
419 ind[gROOT->GetDirLevel()] = '\0';
420 std::cout << ind << "AliFMDEnergyFitter: " << GetName() << '\n'
421 << ind << " Low cut: " << fLowCut << " E/E_mip\n"
422 << ind << " Max(particles): " << fNParticles << '\n'
423 << ind << " Min(entries): " << fMinEntries << '\n'
424 << ind << " Fit range: "
425 << fFitRangeBinWidth << " bins\n"
426 << ind << " Make fits: "
427 << (fDoFits ? "yes\n" : "no\n")
428 << ind << " Make object: "
429 << (fDoMakeObject ? "yes\n" : "no\n")
430 << ind << " Max E/E_mip: " << fMaxE << '\n'
431 << ind << " N bins: " << fNEbins << '\n'
432 << ind << " Increasing bins: "
433 << (fUseIncreasingBins ?"yes\n" : "no\n")
434 << ind << " max(delta p/p): " << fMaxRelParError << '\n'
435 << ind << " max(chi^2/nu): " << fMaxChi2PerNDF << '\n'
436 << ind << " min(a_i): " << fMinWeight << std::endl;
439 //====================================================================
440 AliFMDEnergyFitter::RingHistos::RingHistos()
441 : AliForwardUtil::RingHistos(),
446 fFits("AliFMDCorrELossFit::ELossFit"),
454 //____________________________________________________________________
455 AliFMDEnergyFitter::RingHistos::RingHistos(UShort_t d, Char_t r)
456 : AliForwardUtil::RingHistos(d,r),
461 fFits("AliFMDCorrELossFit::ELossFit"),
471 fEtaEDists.SetName("EDists");
473 //____________________________________________________________________
474 AliFMDEnergyFitter::RingHistos::RingHistos(const RingHistos& o)
475 : AliForwardUtil::RingHistos(o),
480 fFits("AliFMDCorrELossFit::ELossFit"),
487 // o Object to copy from
490 TIter next(&o.fEtaEDists);
492 while ((obj = next())) fEtaEDists.Add(obj->Clone());
495 fList->SetName(fName);
496 TIter next2(o.fList);
497 while ((obj = next2())) fList->Add(obj->Clone());
501 //____________________________________________________________________
502 AliFMDEnergyFitter::RingHistos&
503 AliFMDEnergyFitter::RingHistos::operator=(const RingHistos& o)
506 // Assignment operator
509 // o Object to assign from
514 AliForwardUtil::RingHistos::operator=(o);
516 if (fEDist) delete fEDist;
517 if (fEmpty) delete fEmpty;
519 if (fList) fList->Delete();
522 fEDist = static_cast<TH1D*>(o.fEDist->Clone());
523 fEmpty = static_cast<TH1D*>(o.fEmpty->Clone());
525 TIter next(&o.fEtaEDists);
527 while ((obj = next())) fEtaEDists.Add(obj->Clone());
531 fList->SetName(fName);
532 TIter next2(o.fList);
533 while ((obj = next2())) fList->Add(obj->Clone());
538 //____________________________________________________________________
539 AliFMDEnergyFitter::RingHistos::~RingHistos()
544 if (fEDist) delete fEDist;
548 //____________________________________________________________________
550 AliFMDEnergyFitter::RingHistos::Fill(Bool_t empty, Int_t ieta, Double_t mult)
556 // empty True if event is empty
566 if (ieta < 0 || ieta >= fEtaEDists.GetEntries()) return;
568 TH1D* h = static_cast<TH1D*>(fEtaEDists.At(ieta));
574 //__________________________________________________________________
576 AliFMDEnergyFitter::RingHistos::MakeIncreasingAxis(Int_t n, Double_t min,
580 // Make an axis with increasing bins
588 // An axis with quadratically increasing bin size
592 Double_t dx = (max-min) / n;
595 for (i = 1; i < n+1; i++) {
596 Double_t dI = float(i)/n;
597 Double_t next = bins[i-1] + dx + dI * dI * dx;
599 if (next > max) break;
605 //____________________________________________________________________
607 AliFMDEnergyFitter::RingHistos::Make(Int_t ieta,
615 // Make E/E_mip histogram
620 // eMax Largest signal
621 // deMax Maximum energy loss
622 // nDeBins Number energy loss bins
623 // incr Whether to make bins of increasing size
626 TString name = Form(fgkEDistFormat, fName.Data(), ieta);
627 TString title = Form("#DeltaE/#DeltaE_{mip} for %s %+5.3f#leq#eta<%+5.3f "
628 "(#eta bin %d)", fName.Data(), emin, emax, ieta);
630 h = new TH1D(name.Data(), title.Data(), nDeBins, 0, deMax);
632 TArrayD deAxis = MakeIncreasingAxis(nDeBins, 0, deMax);
633 h = new TH1D(name.Data(), title.Data(), deAxis.fN-1, deAxis.fArray);
637 h->SetXTitle("#DeltaE/#DeltaE_{mip}");
638 h->SetFillColor(Color());
639 h->SetMarkerColor(Color());
640 h->SetLineColor(Color());
641 h->SetFillStyle(3001);
644 fEtaEDists.AddAt(h, ieta-1);
646 //____________________________________________________________________
647 TH1D* AliFMDEnergyFitter::RingHistos::MakePar(const char* name,
649 const TAxis& eta) const
652 // Make a parameter histogram
655 // name Name of histogram.
656 // title Title of histogram.
662 TH1D* h = new TH1D(Form("%s_%s", fName.Data(), name),
663 Form("%s for %s", title, fName.Data()),
664 eta.GetNbins(), eta.GetXmin(), eta.GetXmax());
665 h->SetXTitle("#eta");
668 h->SetFillColor(Color());
669 h->SetMarkerColor(Color());
670 h->SetLineColor(Color());
671 h->SetFillStyle(3001);
676 //____________________________________________________________________
678 AliFMDEnergyFitter::RingHistos::MakeTotal(const char* name,
687 // Make a histogram that contains the results of the fit over the full ring
695 // val Value of parameter
696 // err Error on parameter
699 // The newly allocated histogram
701 Double_t xlow = eta.GetBinLowEdge(low);
702 Double_t xhigh = eta.GetBinUpEdge(high);
703 TH1D* h = new TH1D(Form("%s_%s", fName.Data(), name),
704 Form("%s for %s", title, fName.Data()),
706 h->SetBinContent(1, val);
707 h->SetBinError(1, err);
708 h->SetXTitle("#eta");
712 h->SetMarkerColor(Color());
713 h->SetLineColor(Color());
721 //____________________________________________________________________
723 AliFMDEnergyFitter::RingHistos::Init(const TAxis& eAxis,
724 Double_t maxDE, Int_t nDEbins,
732 // maxDE Max energy loss to consider
733 // nDEbins Number of bins
734 // useIncrBin Whether to use an increasing bin size
736 fEDist = new TH1D(Form("%s_edist", fName.Data()),
737 Form("#DeltaE/#DeltaE_{mip} for %s", fName.Data()),
739 fEmpty = new TH1D(Form("%s_empty", fName.Data()),
740 Form("#DeltaE/#DeltaE_{mip} for %s (empty events)",
741 fName.Data()), 200, 0, 6);
744 // fEtaEDists.Expand(eAxis.GetNbins());
745 for (Int_t i = 1; i <= eAxis.GetNbins(); i++) {
746 Double_t min = eAxis.GetBinLowEdge(i);
747 Double_t max = eAxis.GetBinUpEdge(i);
748 Make(i, min, max, maxDE, nDEbins, useIncrBin);
750 fList->Add(&fEtaEDists);
752 //____________________________________________________________________
754 AliFMDEnergyFitter::RingHistos::Fit(TList* dir,
760 Double_t relErrorCut,
761 Double_t chi2nuCut) const
764 // Fit each histogram to up to @a nParticles particle responses.
770 // nParticles Max number of convolved landaus to fit
771 // minEntries Minimum number of entries
772 // minusBins Number of bins from peak to subtract to
774 // relErrorCut Cut applied to relative error of parameter.
775 // Note, for multi-particle weights, the cut
776 // is loosend by a factor of 2
777 // chi2nuCut Cut on @f$ \chi^2/\nu@f$ -
778 // the reduced @f$\chi^2@f$
781 // Get our ring list from the output container
782 TList* l = GetOutputList(dir);
785 // Get the energy distributions from the output container
786 TList* dists = static_cast<TList*>(l->FindObject("EDists"));
788 AliWarning(Form("Didn't find %s_EtaEDists in %s",
789 fName.Data(), l->GetName()));
794 // Container of the fit results histograms
795 TObjArray* pars = new TObjArray(3+nParticles+1);
796 pars->SetName("FitResults");
799 // Result objects stored in separate list on the output
807 TH1* hA[nParticles-1];
808 pars->Add(hChi2 = MakePar("chi2", "#chi^{2}/#nu", eta));
809 pars->Add(hC = MakePar("c", "Constant", eta));
810 pars->Add(hDelta = MakePar("delta", "#Delta_{p}", eta));
811 pars->Add(hXi = MakePar("xi", "#xi", eta));
812 pars->Add(hSigma = MakePar("sigma", "#sigma", eta));
813 pars->Add(hSigmaN = MakePar("sigman", "#sigma_{n}", eta));
814 pars->Add(hN = MakePar("n", "N", eta));
815 for (UShort_t i = 1; i < nParticles; i++)
816 pars->Add(hA[i-1] = MakePar(Form("a%d",i+1), Form("a_{%d}",i+1), eta));
819 Int_t nDists = dists->GetEntries();
822 for (Int_t i = 0; i < nDists; i++) {
823 TH1D* dist = static_cast<TH1D*>(dists->At(i));
824 // Ignore empty histograms altoghether
825 if (!dist || dist->GetEntries() <= 0) continue;
827 // Scale to the bin-width
828 dist->Scale(1., "width");
830 // Normalize peak to 1
831 Double_t max = dist->GetMaximum();
832 if (max <= 0) continue;
835 // Check that we have enough entries
836 if (dist->GetEntries() <= minEntries) {
837 AliWarning(Form("Histogram at %3d (%s) has too few entries (%d <= %d)",
838 i, dist->GetName(), Int_t(dist->GetEntries()),
844 TF1* res = FitHist(dist,lowCut,nParticles,minusBins,
845 relErrorCut,chi2nuCut);
847 // dist->GetListOfFunctions()->Add(res);
850 low = TMath::Min(low,i+1);
851 high = TMath::Max(high,i+1);
853 // Get the reduced chi square
854 Double_t chi2 = res->GetChisquare();
855 Int_t ndf = res->GetNDF();
857 // Store results of best fit in output histograms
858 res->SetLineWidth(4);
859 hChi2 ->SetBinContent(i+1, ndf > 0 ? chi2 / ndf : 0);
860 hC ->SetBinContent(i+1, res->GetParameter(kC));
861 hDelta ->SetBinContent(i+1, res->GetParameter(kDelta));
862 hXi ->SetBinContent(i+1, res->GetParameter(kXi));
863 hSigma ->SetBinContent(i+1, res->GetParameter(kSigma));
864 hSigmaN ->SetBinContent(i+1, res->GetParameter(kSigmaN));
865 hN ->SetBinContent(i+1, res->GetParameter(kN));
867 hC ->SetBinError(i+1, res->GetParError(kC));
868 hDelta ->SetBinError(i+1, res->GetParError(kDelta));
869 hXi ->SetBinError(i+1, res->GetParError(kXi));
870 hSigma ->SetBinError(i+1, res->GetParError(kSigma));
871 hSigmaN->SetBinError(i+1, res->GetParError(kSigmaN));
872 hN ->SetBinError(i+1, res->GetParError(kN));
874 for (UShort_t j = 0; j < nParticles-1; j++) {
875 hA[j]->SetBinContent(i+1, res->GetParameter(kA+j));
876 hA[j]->SetBinError(i+1, res->GetParError(kA+j));
880 // Fit the full-ring histogram
881 TH1* total = GetOutputHist(l, Form("%s_edist", fName.Data()));
882 if (total && total->GetEntries() >= minEntries) {
884 // Scale to the bin-width
885 total->Scale(1., "width");
887 // Normalize peak to 1
888 Double_t max = total->GetMaximum();
889 if (max > 0) total->Scale(1/max);
891 TF1* res = FitHist(total,lowCut,nParticles,minusBins,
892 relErrorCut,chi2nuCut);
894 // Make histograms for the result of this fit
895 Double_t chi2 = res->GetChisquare();
896 Int_t ndf = res->GetNDF();
897 pars->Add(MakeTotal("t_chi2", "#chi^{2}/#nu", eta, low, high,
898 ndf > 0 ? chi2/ndf : 0, 0));
899 pars->Add(MakeTotal("t_c", "Constant", eta, low, high,
900 res->GetParameter(kC),
901 res->GetParError(kC)));
902 pars->Add(MakeTotal("t_delta", "#Delta_{p}", eta, low, high,
903 res->GetParameter(kDelta),
904 res->GetParError(kDelta)));
905 pars->Add(MakeTotal("t_xi", "#xi", eta, low, high,
906 res->GetParameter(kXi),
907 res->GetParError(kXi)));
908 pars->Add(MakeTotal("t_sigma", "#sigma", eta, low, high,
909 res->GetParameter(kSigma),
910 res->GetParError(kSigma)));
911 pars->Add(MakeTotal("t_sigman", "#sigma_{n}", eta, low, high,
912 res->GetParameter(kSigmaN),
913 res->GetParError(kSigmaN)));
914 pars->Add(MakeTotal("t_n", "N", eta, low, high,
915 res->GetParameter(kN),0));
916 for (UShort_t j = 0; j < nParticles-1; j++)
917 pars->Add(MakeTotal(Form("t_a%d",j+2),
918 Form("a_{%d}",j+2), eta, low, high,
919 res->GetParameter(kA+j),
920 res->GetParError(kA+j)));
924 // Clean up list of histogram. Histograms with no entries or
925 // no functions are deleted. We have to do this using the TObjLink
926 // objects stored in the list since ROOT cannot guaranty the validity
927 // of iterators when removing from a list - tsck. Should just implement
929 TObjLink* lnk = dists->FirstLink();
931 TH1* h = static_cast<TH1*>(lnk->GetObject());
933 if (h->GetEntries() <= 0) {
934 // AliWarning(Form("No entries in %s - removing", h->GetName()));
937 else if (h->GetListOfFunctions()->GetEntries() <= 0) {
938 // AliWarning(Form("No fuctions associated with %s - removing",
943 TObjLink* keep = lnk->Next();
954 //____________________________________________________________________
956 AliFMDEnergyFitter::RingHistos::FitHist(TH1* dist,
960 Double_t relErrorCut,
961 Double_t chi2nuCut) const
964 // Fit a signal histogram. First, the bin @f$ b_{min}@f$ with
965 // maximum bin content in the range @f$ [E_{min},\infty]@f$ is
966 // found. Then the fit range is set to the bin range
967 // @f$ [b_{min}-\Delta b,b_{min}+2\Delta b]@f$, and a 1
968 // particle signal is fitted to that. The parameters of that fit
969 // is then used as seeds for a fit of the @f$ N@f$ particle response
970 // to the data in the range
971 // @f$ [b_{min}-\Delta b,N(\Delta_1+\xi_1\log(N))+2N\xi@f$
974 // dist Histogram to fit
975 // lowCut Lower cut @f$ E_{min}@f$ on signal
976 // nParticles Max number @f$ N@f$ of convolved landaus to fit
977 // minusBins Number of bins @f$ \Delta b@f$ from peak to
978 // subtract to get the fit range
979 // relErrorCut Cut applied to relative error of parameter.
980 // Note, for multi-particle weights, the cut
981 // is loosend by a factor of 2
982 // chi2nuCut Cut on @f$ \chi^2/\nu@f$ -
983 // the reduced @f$\chi^2@f$
986 // The best fit function
988 Double_t maxRange = 10;
990 // Create a fitter object
991 AliForwardUtil::ELossFitter f(lowCut, maxRange, minusBins);
995 // If we are only asked to fit a single particle, return this fit,
997 if (nParticles == 1) {
998 TF1* r = f.Fit1Particle(dist, 0);
1003 // Fit from 2 upto n particles
1004 for (Int_t i = 2; i <= nParticles; i++) f.FitNParticle(dist, i, 0);
1007 // Now, we need to select the best fit
1008 Int_t nFits = f.fFitResults.GetEntriesFast();
1010 for (Int_t i = nFits-1; i >= 0; i--) {
1012 TF1* ff = static_cast<TF1*>(f.fFunctions.At(i));
1013 // ff->SetLineColor(Color());
1014 ff->SetRange(0, maxRange);
1015 dist->GetListOfFunctions()->Add(new TF1(*ff));
1016 if (CheckResult(static_cast<TFitResult*>(f.fFitResults.At(i)),
1017 relErrorCut, chi2nuCut)) {
1019 ff->SetLineWidth(2);
1020 // f.fFitResults.At(i)->Print("V");
1023 // If all else fails, use the 1 particle fit
1024 TF1* ret = static_cast<TF1*>(f.fFunctions.At(0));
1026 // Find the fit with the most valid particles
1027 for (Int_t i = nFits-1; i > 0; i--) {
1028 if (!good[i]) continue;
1030 AliInfo(Form("Choosing fit with n=%d", i+1));
1031 f.fFitResults.At(i)->Print();
1036 // Give a warning if we're using fall-back
1037 if (ret == f.fFunctions.At(0)) {
1038 AliWarning("Choosing fall-back 1 particle fit");
1040 // Copy our result and return (the functions are owned by the fitter)
1041 TF1* fret = new TF1(*ret);
1045 //____________________________________________________________________
1047 AliFMDEnergyFitter::RingHistos::CheckResult(TFitResult* r,
1048 Double_t relErrorCut,
1049 Double_t chi2nuCut) const
1052 // Check the result of the fit. Returns true if
1053 // - @f$ \chi^2/\nu < \max{\chi^2/\nu}@f$
1054 // - @f$ \Delta p_i/p_i < \delta_e@f$ for all parameters. Note,
1055 // for multi-particle fits, this requirement is relaxed by a
1057 // - @f$ a_{n} > 10^{-7}@f$ when fitting to an @f$ n@f$
1058 // particle response
1061 // r Result to check
1062 // relErrorCut Cut @f$ \delta_e@f$ applied to relative error
1064 // chi2nuCut Cut @f$ \max{\chi^2/\nu}@f$
1067 // true if fit is good.
1069 if (fDebug > 10) r->Print();
1070 TString n = r->GetName();
1071 n.ReplaceAll("TFitResult-", "");
1072 Double_t chi2 = r->Chi2();
1073 Int_t ndf = r->Ndf();
1074 // Double_t prob = r.Prob();
1077 // Check that the reduced chi square isn't larger than 5
1078 if (ndf <= 0 || chi2 / ndf > chi2nuCut) {
1080 AliWarning(Form("%s: chi^2/ndf=%12.5f/%3d=%12.5f>%12.5f",
1081 n.Data(), chi2, ndf, (ndf<0 ? 0 : chi2/ndf),
1086 // Check each parameter
1087 UShort_t nPar = r->NPar();
1088 for (UShort_t i = 0; i < nPar; i++) {
1089 if (i == kN) continue; // Skip the number parameter
1091 // Get value and error and check value
1092 Double_t v = r->Parameter(i);
1093 Double_t e = r->ParError(i);
1094 if (v == 0) continue;
1096 // Calculate the relative error and check it
1097 Double_t re = e / v;
1098 Double_t cut = relErrorCut * (i < kN ? 1 : 2);
1101 AliWarning(Form("%s: Delta %s/%s=%9.5f/%9.5f=%5.1f%%>%5.1f%%",
1102 n.Data(), r->ParName(i).c_str(),
1103 r->ParName(i).c_str(), e, v,
1104 100*(v == 0 ? 0 : e/v),
1110 // Check if we have scale parameters
1113 // Check that the last particle has a significant contribution
1114 Double_t lastScale = r->Parameter(nPar-1);
1115 if (lastScale <= 1e-7) {
1117 AliWarning(Form("%s: %s=%9.6f<1e-7",
1118 n.Data(), r->ParName(nPar-1).c_str(), lastScale)); }
1126 //__________________________________________________________________
1128 AliFMDEnergyFitter::RingHistos::FindBestFits(TList* d,
1129 AliFMDCorrELossFit& obj,
1131 Double_t relErrorCut,
1133 Double_t minWeightCut)
1136 // Find the best fits
1140 // obj Object to add fits to
1142 // relErrorCut Cut applied to relative error of parameter.
1143 // Note, for multi-particle weights, the cut
1144 // is loosend by a factor of 2
1145 // chi2nuCut Cut on @f$ \chi^2/\nu@f$ -
1146 // the reduced @f$\chi^2@f$
1147 // minWeightCut Least valid @f$ a_i@f$
1150 // Get our ring list from the output container
1151 TList* l = GetOutputList(d);
1154 // Get the energy distributions from the output container
1155 TList* dists = static_cast<TList*>(l->FindObject("EDists"));
1157 AliWarning(Form("Didn't find %s_EtaEDists in %s",
1158 fName.Data(), l->GetName()));
1162 Int_t nBin = eta.GetNbins();
1164 for (Int_t b = 1; b <= nBin; b++) {
1165 TString n(Form(fgkEDistFormat, fName.Data(), b));
1166 TH1D* dist = static_cast<TH1D*>(dists->FindObject(n));
1167 // Ignore empty histograms altoghether
1168 if (!dist || dist->GetEntries() <= 0) continue;
1170 AliFMDCorrELossFit::ELossFit* best = FindBestFit(dist,
1175 best->fRing = fRing;
1177 // Double_t eta = fAxis->GetBinCenter(b);
1178 obj.SetFit(fDet, fRing, b, new AliFMDCorrELossFit::ELossFit(*best));
1182 //__________________________________________________________________
1183 AliFMDCorrELossFit::ELossFit*
1184 AliFMDEnergyFitter::RingHistos::FindBestFit(TH1* dist,
1185 Double_t relErrorCut,
1187 Double_t minWeightCut)
1190 // Find the best fit
1194 // relErrorCut Cut applied to relative error of parameter.
1195 // Note, for multi-particle weights, the cut
1196 // is loosend by a factor of 2
1197 // chi2nuCut Cut on @f$ \chi^2/\nu@f$ -
1198 // the reduced @f$\chi^2@f$
1199 // minWeightCut Least valid @f$ a_i@f$
1204 TList* funcs = dist->GetListOfFunctions();
1209 // Info("FindBestFit", "%s", dist->GetName());
1210 while ((func = static_cast<TF1*>(next()))) {
1211 AliFMDCorrELossFit::ELossFit* fit =
1212 new(fFits[i++]) AliFMDCorrELossFit::ELossFit(0,*func);
1213 fit->CalculateQuality(chi2nuCut, relErrorCut, minWeightCut);
1217 return static_cast<AliFMDCorrELossFit::ELossFit*>(fFits.At(0));
1221 //____________________________________________________________________
1223 AliFMDEnergyFitter::RingHistos::Output(TList* dir)
1231 fList = DefineOutputList(dir);
1234 //____________________________________________________________________