// root.cern.ch/root/htmldoc/guides/spectrum/Spectrum.html#fitting // root.cern.ch/doc/master/FitAwmi_8C.html // Caution: in the fitted model all the peaks have the same sigma. // This macro fits the source spectrum using the AWMI algorithm // from the "TSpectrumFit" class ("TSpectrum" class is used to find peaks). #include "TROOT.h" #include "TMath.h" #include "TRandom.h" #include "TH1.h" #include "TF1.h" #include "TCanvas.h" #include "TSpectrum.h" #include "TSpectrumFit.h" #include "TPolyMarker.h" #include "TList.h" #include using namespace std; TH1F* FitAwmi_Create_Spectrum () { // This function creates well separated peaks with randomized means and areas // Note: TSpectrumFit assumes that all the peaks have the same sigma (!) Int_t NBins = 1000; Double_t xmin = -10., xmax = 10., xpos = xmin; TH1F* hspectrum = new TH1F ("hspectrum", "Simulated spectrum", NBins, xmin, xmax); hspectrum->SetStats (kFALSE); TF1 fgauss ("fgauss", "TMath::Gaus (x, [0] , [1] , 1 )", xmin, xmax); fgauss.SetParNames ( "mean", "sigma"); gRandom->SetSeed(0); // Make it really random Double_t sigma = hspectrum->GetBinWidth (1) * gRandom->Uniform (2., 6.); fgauss.SetParameter (1, sigma); Int_t NPeaks = 0; for (double xpos = xmin + 3.*sigma; xpos + 3.*sigma < xmax ; xpos += 6.*sigma) { NPeaks++; fgauss.SetParameter (0, xpos); Double_t Area = gRandom->Uniform (1., 11.); hspectrum->Add (&fgauss, Area, ""); // "" ... or ... "I" cout << "Created peak at " << xpos << " with [Amp, Area]: [" << Area / sigma / TMath::Sqrt(TMath::TwoPi()) << " , " << Area << "]\n"; } cout << "\nTotal number of created peaks = " << NPeaks << " with sigma = " << sigma << "\n\n"; return hspectrum; } void FitAwmi () { /***** Stage 1: creating spectrum *****/ TH1F* hspec = FitAwmi_Create_Spectrum (); TCanvas* cFit = new TCanvas ("cFit", "cFit", 10, 10, 1000, 700); hspec->Draw ("L"); cFit->Modified(); cFit->Update(); cout << "* Press Enter to [1] search for peak positions and (later) [2] fit."; cin.ignore(); /***** Stage 2: peak search *****/ Int_t i, NFound, binnum; Int_t NBins = hspec->GetNbinsX (); Double_t* source = new Double_t [NBins]; Double_t* destin = new Double_t [NBins]; for (i = 0; i < NBins; i++) source[i] = hspec->GetBinContent (i+1); TSpectrum* Analyser1dim = new TSpectrum (); // note: default maxpositions = 100 // Searching for Candidate Peaks' positions NFound = Analyser1dim->SearchHighRes (source, destin, NBins, 2., 2., kFALSE, 10000, kFALSE, 0); /* Int_t TSpectrum::SearchHighRes ( Double_t* source, : pointer to the vector of source spectrum Double_t* destVector, : pointer to the vector of resulting deconvolved spectrum Int_t ssize, : length of source spectrum Double_t sigma, : sigma of searched peaks (see manual) Double_t threshold, : threshold value in % for selected peaks. Peaks with amplitude < threshold*highest_peak/100 are ignored, see manual bool backgroundRemove, : logical variable, set if the removal of background before deconvolution is desired Int_t deconIterations, : number of iterations in deconvolution operation bool markov, : true = first the source spectrum is replaced by new spectrum calculated using Markov chains method Int_t averWindow : (Markov: ) averaging window of searched peaks, (see manual) ) The goal of this function is to identify automatically the peaks in spectrum with the presence of the continuous background and statistical fluctuations - noise. 1. Background is removed (if desired), 2. Markov smoothed spectrum is calculated (if desired), 3. Response function is generated according to given sigma + Deconvolution is carried out. See root.cern.ch/doc/v608/classTSpectrum.html#a5ba181a45b117934b48c4ef5f78d0b2b */ cout << "\n* SearchHighRes has found: " << NFound << " peaks.\n\n"; /***** Stage 3: preparation for fitting of peaks *****/ // Filling in the initial estimates of the input parameters Double_t* Amplit = new Double_t [NFound]; Double_t* Posit = Analyser1dim->GetPositionX (); // returns X positions of found peaks Bool_t* FixPosit = new Bool_t [NFound]; // should these parameters be fixed? [T/F] Bool_t* FixAmplit = new Bool_t [NFound]; for (i = 0; i < NFound; i++) { FixAmplit[i] = kFALSE; FixPosit [i] = kFALSE; binnum = 1 + Int_t (Posit[i] + 0.5); // the "nearest" bin Amplit [i] = hspec->GetBinContent (binnum); // estimating the peak amplitude } cout << "* Press Enter to fit the spectra using the AWMI approach."; cin.ignore(); // Creation of the fitting tool TSpectrumFit* Fitter1dim = new TSpectrumFit (NFound); /* Class for fitting 1D spectra using AWMI (algorithm without matrix inversion) and conjugate gradient algorithms for symmetrical matrices (Stiefel-Hestens method). AWMI method allows to fit simultaneously 100s up to 1000s peaks. Stiefel method is very stable, it converges faster, but is more time consuming. See: root.cern.ch/doc/v608/classTSpectrumFit.html */ Fitter1dim->SetFitParameters (0, NBins-1 , 1000, 0.1, Fitter1dim->kFitOptimChiCounts, Fitter1dim->kFitAlphaHalving, Fitter1dim->kFitPower2, Fitter1dim->kFitTaylorOrderFirst ); /* void TSpectrumFit::SetFitParameters ( Int_t xmin, : Fitting region Int_t xmax, : Fitting region Int_t numberIterations, : # of desired iterations in the fit Double_t alpha, : Convergence coeff. = (0,1], for details see references Int_t statisticType, : Type of statistics, possible values: kFitOptimChiCounts = chi2 stats with counts as weight coefs. kFitOptimChiFuncValues = chi2 stats with function values as weight coefs. kFitOptimMaxLikelihood Int_t alphaOptim, : Optimization of convergence algorithm, possible values: kFitAlphaHalving, kFitAlphaOptimal Int_t power, : Possible values kFitPower2,4,6,8,10,12, see Refs. Only for Awmi fitting function. Int_t fitTaylor : Order of Taylor expansion. Only for Awmi fitting function. Possible values: kFitTaylorOrderFirst, kFitTaylorOrderSecond ) */ Fitter1dim->SetPeakParameters (2., kFALSE, Posit, FixPosit, Amplit, FixAmplit); /* void TSpectrumFit::SetPeakParameters ( Double_t sigma, : initial value of sigma parameter Bool_t fixSigma, : true if sigma should be fixed const Double_t* positionInit, : array of initial values of peaks positions const Bool_t* fixPosition, : array for peaks. True if a given position should be fixed const Double_t* ampInit, : array of initial values of peaks amplitudes const Bool_t* fixAmp : array for amplitudes. True if a given amp should be fixed ) See: */ // Fitter1dim->SetBackgroundParameters (source[0], kFALSE, 0., kFALSE, 0., kFALSE); /***** Stage 4: do the fitting *****/ Fitter1dim->FitAwmi (source); /***** Stage 5: extracting found parameters *****/ Double_t* Positions = Fitter1dim-> GetPositions (); // Caution: Xscale is Bin No. Double_t* PositionsErrors = Fitter1dim-> GetPositionsErrors (); // Caution: Xscale is Bin No. Double_t* Amplitudes = Fitter1dim-> GetAmplitudes (); Double_t* AmplitudesErrors = Fitter1dim-> GetAmplitudesErrors (); Double_t* Areas = Fitter1dim-> GetAreas (); // Caution: Xscale is Bin No. Double_t* AreasErrors = Fitter1dim-> GetAreasErrors (); // Caution: Xscale is Bin No. Double_t x1 = hspec->GetBinCenter(1), dx = hspec->GetBinWidth (1); Double_t sigma, sigmaErr; Fitter1dim->GetSigma (sigma, sigmaErr); // Caution: Xscale is Bin No. // Current TSpectrumFit needs a sqrt(2) correction factor for sigma sigma /= TMath::Sqrt2(); sigmaErr /= TMath::Sqrt2(); // Convert "bin numbers" into "x-axis values" sigma *= dx; sigmaErr *= dx; cout << "\n * Overall sigma found by fit: " << sigma << " (+-" << sigmaErr << ")" << endl; cout << " * Fit chi^2 = " << Fitter1dim->GetChi () << "\n\n"; for (i = 0; i < NFound; i++) { // Convert "bin numbers" into "x-axis values" Positions [i] = x1 + Positions[i] * dx; PositionsErrors[i] *= dx; Areas [i] *= dx; AreasErrors [i] *= dx; cout << "Found: " << Positions [i] << " (+-" << PositionsErrors [i] << ") " << Amplitudes[i] << " (+-" << AmplitudesErrors[i] << ") " << Areas [i] << " (+-" << AreasErrors [i] << ")" << endl; } hspec->Draw ("SAME HIST"); TPolyMarker *pm = new TPolyMarker (NFound, Positions, Amplitudes); hspec->GetListOfFunctions()->Add (pm); pm->SetMarkerStyle (23); pm->SetMarkerColor (kRed); pm->SetMarkerSize (1); }