// root.cern.ch/doc/v608/classTSpectrum.html // root.cern.ch/root/htmldoc/guides/spectrum/Spectrum.html // Illustrates how to find peaks in histograms. // // This script generates a random number of gaussian peaks // on top of a linear background. // The positions of the peaks are found via TSpectrum // and injected as initial values of parameters to make a global fit. // The background is computed and drawn on top of the original histogram. // // To execute this example, do (in ROOT 5 or ROOT 6): // // ~~~{.cpp} // root > .x peaks.C (generate 10 peaks by default) // root > .x peaks.C++ (use the compiler) // root > .x peaks.C++(30) (generates 30 peaks) // ~~~ // // Author: Rene Brun #include "TCanvas.h" #include "TMath.h" #include "TH1.h" #include "TF1.h" #include "TRandom.h" #include "TSpectrum.h" #include "TVirtualFitter.h" Int_t NPeaks = 10; Double_t funPeaks (Double_t *x, Double_t *par) { Double_t Result = par[0] + par[1]*x[0]; for (Int_t p = 0; p < NPeaks; p++) { Double_t Norm = par [3*p + 2]; Double_t Mean = par [3*p + 3]; Double_t Sigma = par [3*p + 4]; Result += Norm * TMath::Gaus (x[0], Mean, Sigma); } return Result; } void peaks (Int_t NPeaks = 10) { TH1F* hspect = new TH1F ("hspect", "test", 500, 0, 1000); // Generate [NPeaks] peaks at random TF1* funModel = new TF1 ("funModel", funPeaks, 0, 1000, 2+3*NPeaks); funModel->SetNpx (10000); Double_t par [3000]; par[0] = 0.8; par[1] = -0.6/1000; for (Int_t p = 0; p < NPeaks; p++) { par [3*p + 2] = 1; // "norm" par [3*p + 3] = 40 + gRandom->Rndm()*920; // "mean" par [3*p + 4] = 3 + 2*gRandom->Rndm(); // "sigma" } funModel->SetParameters (par); TCanvas* c1 = new TCanvas ("c1", "c1", 10, 10, 1000, 900); c1->Divide (1,2); c1->cd (1); hspect->FillRandom ("funModel", 200000); hspect->Draw (); TH1F* h2 = (TH1F*) hspect->Clone ("h2"); /*** ***/ /*** From here we will treat hspect as unknown spectrum. ***/ /*** We intend to find Bkgnd, Peaks, and later - Fit peaks ***/ /*** ***/ // Use TSpectrum to find the peak candidates TSpectrum* Analyser1dim = new TSpectrum (2*NPeaks); Int_t NFound = Analyser1dim->Search (hspect, 2, "", 0.10); /* Int_t TSpectrum::Search ( const TH1* hin, : pointer to the histogram of source spectrum Double_t sigma = 2, : sigma of searched peaks, see manual for details Option_t* option = "", : Double_t threshold = 0.05 : [0,1]. Peaks with Amp <= threshold*HighestPeak are discarded. ) The number of found peaks and their positions are written into members: fNpeaks, fPositionX and fPositionY. Positions are retrievable via TSpectrum::GetPositionX() and GetPositionY() methods, which return the arrays of resp. X and Y positions of found peaks. The search is performed in the current histogram range. Options: - Background is removed by default. Otherwise: "nobackground" - Smoothing is done by default. Otherwise: "nomarkov" - Polymarker with peaks is added by default. Otherwise: "goff" - The resulting TH1 is drawn by default. Otherwise: "nodraw" See: root.cern.ch/doc/master/classTSpectrum.html#a5f8b7b208f813670259b51a8bcdcbfc5 */ printf ("Found %d candidate peaks to fit\n", NFound); // Estimate background using TSpectrum::Background TH1* hbkgnd = Analyser1dim->Background (hspect, 20, "same"); /* TH1* TSpectrum::Background ( const TH1* h, : input 1-d histogram Int_t niter = 20, : numberIterations. Rising niter makes the result smoother and lower Option_t* option = "" : Options, see below. ) Options: - To set the direction parameter "BackIncreasingWindow". (Default: BackDecreasingWindow) - Order of clipping filter (default "BackOrder2"). Possible values= BackOrder4, ..6, ..8 - "nosmoothing" : if selected, the background is not smoothed (Default: smoothed) - Width of smoothing window, (Default: BackSmoothing3). Possible: 3, ..., 15 - "Compton" : if selected, the Compton edges will be estimated. - "same" : if selected, the background histogram is superimposed This function calculates the background spectrum in the input histogram h. The background is returned as a TH1* histogram. See root.cern.ch/doc/master/classTSpectrum.html#a56e15d1c36c7557b17d5f6d68fa315a7 */ if (hbkgnd) c1->Update(); // Estimate the linear background using the very simple fit c1->cd (2); TF1* funLine = new TF1 ("funline", "pol1", 0, 1000); hspect->Fit ("funline", "QN"); /*** For every found peak: ***/ /*** - eliminate too weak peaks ***/ /*** - use found (X,Y) as inits for further fitting ***/ par[0] = funLine->GetParameter (0); // Estimate initial fit parameters par[1] = funLine->GetParameter (1); NPeaks = 0; Double_t* xpeaks = Analyser1dim->GetPositionX (); // Returns X-positions of found peaks Double_t* ypeaks = Analyser1dim->GetPositionY (); for (Int_t p = 0; p < NFound; p++) { Double_t xp = xpeaks[p]; Int_t bin = hspect->GetXaxis()->FindBin (xp); Double_t yp = hspect->GetBinContent (bin); if (yp - sqrt(yp) < funLine->Eval(xp)) continue; // Simple 'significance' test par [3*NPeaks + 2] = yp; par [3*NPeaks + 3] = xp; par [3*NPeaks + 4] = 3; NPeaks++; } printf ( "Found %d useful peaks to fit\n", NPeaks); printf ( "Now fitting: Be patient\n" ); TF1* funFit = new TF1 ("funfit", funPeaks, 0, 1000, 2+3*NPeaks); funFit->SetParName (0, "Bg const"); funFit->SetParName (1, "Bg lin" ); for (Int_t p = 0; p < NPeaks; p++) { funFit->SetParName (3*p+2 , Form ("P%d Norm" , p) ); funFit->SetParName (3*p+3 , Form ("P%d Mean" , p) ); funFit->SetParName (3*p+4 , Form ("P%d Sigma", p) ); } // We may have more than the default 25 parameters TVirtualFitter::Fitter (h2, 10+3*NPeaks); funFit->SetParameters (par); funFit->SetNpx (10000); h2->Fit ("funfit"); // Now fitting. }