testUnfold1.C

Go to the documentation of this file.
00001 // Test program for the classes TUnfold, TUnfoldSys
00002 // Author: Stefan Schmitt
00003 // DESY, 14.10.2008
00004 
00005 //  Version 16, parallel to changes in TUnfold
00006 //
00007 //  History:
00008 //    Version 15, with automated L-curve scan
00009 //    Version 14, with changes in TUnfoldSys.cxx
00010 //    Version 13, include test of systematic errors
00011 //    Version 12, catch error when defining the input
00012 //    Version 11,  print chi**2 and number of degrees of freedom
00013 //    Version 10,  with bug-fix in TUnfold.cxx
00014 //    Version 9,  with bug-fix in TUnfold.cxx and TUnfold.h
00015 //    Version 8,  with bug-fix in TUnfold.cxx and TUnfold.h
00016 //    Version 7,  with bug-fix in TUnfold.cxx and TUnfold.h
00017 //    Version 6a, fix problem with dynamic array allocation under windows
00018 //    Version 6, bug-fixes in TUnfold.C
00019 //    Version 5, replace main() by testUnfold1()
00020 //    Version 4, with bug-fix in TUnfold.C
00021 //    Version 3, with bug-fix in TUnfold.C
00022 //    Version 2, with changed ScanLcurve() arguments
00023 //    Version 1, remove L curve analysis, use ScanLcurve() method instead
00024 //    Version 0, L curve analysis included here
00025 
00026 
00027 #include <TError.h>
00028 #include <TMath.h>
00029 #include <TCanvas.h>
00030 #include <TRandom3.h>
00031 #include <TFitter.h>
00032 #include <TF1.h>
00033 #include <TStyle.h>
00034 #include <TVector.h>
00035 #include <TGraph.h>
00036 
00037 #include <TUnfoldSys.h>
00038 
00039 // #define VERBOSE_LCURVE_SCAN
00040 
00041 using namespace std;
00042 
00043 ///////////////////////////////////////////////////////////////////////
00044 // 
00045 //  Test program for the classes TUnfold, TUnfoldSys
00046 //
00047 //  (1) Generate Monte Carlo and Data events
00048 //      The events consist of
00049 //        signal
00050 //        background
00051 //
00052 //      The signal is a resonance. It is generated with a Breit-Wigner,
00053 //      smeared by a Gaussian
00054 //
00055 //  (2) Unfold the data. The result is:
00056 //      The background level
00057 //      The shape of the resonance, corrected for detector effects
00058 //
00059 //      Systematic errors from the MC shape variation are included
00060 //      and propagated to the result
00061 //
00062 //  (3) fit the unfolded distribution, including the correlation matrix
00063 //
00064 //  (4) save six plots to a file testUnfold1.ps
00065 //        1  2  3
00066 //        4  5  6
00067 //      1: 2d-plot of the matrix decsribing the migrations
00068 //      2: generator-level distributions
00069 //             blue: unfolded data, total errors
00070 //             green: unfolded data, statistical errors
00071 //             red: generated data
00072 //             black: fit to green data points
00073 //      3: detector level distributions
00074 //             blue: unfoldede data, folded back through the matrix
00075 //             black: Monte Carlo (with wrong peal position)
00076 //             blue: data
00077 //      4: global correlation coefficients
00078 //      5: chi**2 as a function of log(tau)
00079 //           the star indicates the final choice of tau
00080 //      6: the L curve
00081 //
00082 ///////////////////////////////////////////////////////////////////////
00083 
00084 TRandom *rnd=0;
00085 
00086 TH2D *gHistInvEMatrix;
00087 
00088 TVirtualFitter *gFitter=0;
00089 
00090 void chisquare_corr(Int_t &npar, Double_t * /*gin */, Double_t &f, Double_t *u, Int_t /*flag */) {
00091   //  Minimization function for H1s using a Chisquare method
00092   //  only one-dimensional histograms are supported
00093   //  Corelated errors are taken from an external inverse covariance matrix
00094   //  stored in a 2-dimensional histogram
00095   Double_t x;
00096   TH1 *hfit = (TH1*)gFitter->GetObjectFit();
00097   TF1 *f1   = (TF1*)gFitter->GetUserFunc();
00098    
00099   f1->InitArgs(&x,u);
00100   npar = f1->GetNpar();
00101   f = 0;
00102    
00103   Int_t npfit = 0;
00104   Int_t nPoints=hfit->GetNbinsX();
00105   Double_t *df=new Double_t[nPoints];
00106   for (Int_t i=0;i<nPoints;i++) {
00107     x     = hfit->GetBinCenter(i+1);
00108     TF1::RejectPoint(kFALSE);
00109     df[i] = f1->EvalPar(&x,u)-hfit->GetBinContent(i+1);
00110     if (TF1::RejectedPoint()) df[i]=0.0;
00111     else npfit++;
00112   }
00113   for (Int_t i=0;i<nPoints;i++) {
00114     for (Int_t j=0;j<nPoints;j++) {
00115       f += df[i]*df[j]*gHistInvEMatrix->GetBinContent(i+1,j+1);
00116     }
00117   }
00118   delete[] df;
00119   f1->SetNumberFitPoints(npfit);
00120 }
00121 
00122 Double_t bw_func(Double_t *x,Double_t *par) {
00123   Double_t dm=x[0]-par[1];
00124   return par[0]/(dm*dm+par[2]*par[2]);
00125 }
00126 
00127 
00128 // generate an event
00129 // output:
00130 //  negative mass: background event
00131 //  positive mass: signal event
00132 Double_t GenerateEvent(Double_t bgr, // relative fraction of background
00133                        Double_t mass, // peak position
00134                        Double_t gamma) // peak width
00135 {
00136   Double_t t;
00137   if(rnd->Rndm()>bgr) {
00138     // generate signal event
00139     // with positive mass
00140     do {
00141       do {
00142         t=rnd->Rndm();
00143       } while(t>=1.0); 
00144       t=TMath::Tan((t-0.5)*TMath::Pi())*gamma+mass;
00145     } while(t<=0.0);
00146     return t;
00147   } else {
00148     // generate background event
00149     // generate events following a power-law distribution
00150     //   f(E) = K * TMath::power((E0+E),N0)
00151     static Double_t const E0=2.4;
00152     static Double_t const N0=2.9;
00153     do {
00154       do {
00155         t=rnd->Rndm();
00156       } while(t>=1.0);
00157       // the mass is returned negative
00158       // In our example a convenient way to indicate it is a background event.
00159       t= -(TMath::Power(1.-t,1./(1.-N0))-1.0)*E0;
00160     } while(t>=0.0);
00161     return t;
00162   }
00163 }
00164 
00165 // smear the event to detector level
00166 // input:
00167 //   mass on generator level (mTrue>0 !)
00168 // output:
00169 //   mass on detector level
00170 Double_t DetectorEvent(Double_t mTrue) {
00171   // smear by double-gaussian
00172   static Double_t frac=0.1;
00173   static Double_t wideBias=0.03;
00174   static Double_t wideSigma=0.5;
00175   static Double_t smallBias=0.0;
00176   static Double_t smallSigma=0.1;
00177   if(rnd->Rndm()>frac) {
00178     return rnd->Gaus(mTrue+smallBias,smallSigma);
00179   } else {
00180     return rnd->Gaus(mTrue+wideBias,wideSigma);
00181   }
00182 }
00183 
00184 int testUnfold1()
00185 {
00186   // switch on histogram errors
00187   TH1::SetDefaultSumw2();
00188 
00189   // show fit result
00190   gStyle->SetOptFit(1111);
00191 
00192   // random generator
00193   rnd=new TRandom3();
00194 
00195   // data and MC luminosity, cross-section
00196   Double_t const luminosityData=100000;
00197   Double_t const luminosityMC=1000000;
00198   Double_t const crossSection=1.0;
00199 
00200   Int_t const nDet=250;
00201   Int_t const nGen=100;
00202   Double_t const xminDet=0.0;
00203   Double_t const xmaxDet=10.0;
00204   Double_t const xminGen=0.0;
00205   Double_t const xmaxGen=10.0;
00206   
00207   //============================================
00208   // generate MC distribution
00209   //
00210   TH1D *histMgenMC=new TH1D("MgenMC",";mass(gen)",nGen,xminGen,xmaxGen);
00211   TH1D *histMdetMC=new TH1D("MdetMC",";mass(det)",nDet,xminDet,xmaxDet);
00212   TH2D *histMdetGenMC=new TH2D("MdetgenMC",";mass(det);mass(gen)",
00213                                nDet,xminDet,xmaxDet,nGen,xminGen,xmaxGen);
00214   Int_t neventMC=rnd->Poisson(luminosityMC*crossSection);
00215   for(Int_t i=0;i<neventMC;i++) {
00216     Double_t mGen=GenerateEvent(0.3, // relative fraction of background
00217                                 4.0, // peak position in MC
00218                                 0.2); // peak width in MC
00219     Double_t mDet=DetectorEvent(TMath::Abs(mGen));
00220     // the generated mass is negative for background
00221     // and positive for signal
00222     // so it will be filled in the underflow bin
00223     // this is very convenient for the unfolding:
00224     // the unfolded result will contain the number of background
00225     // events in the underflow bin
00226 
00227     // generated MC distribution (for comparison only)
00228     histMgenMC->Fill(mGen,luminosityData/luminosityMC);
00229     // reconstructed MC distribution (for comparison only)
00230     histMdetMC->Fill(mDet,luminosityData/luminosityMC);
00231 
00232     // matrix describing how the generator input migrates to the
00233     // reconstructed level. Unfolding input.
00234     // NOTE on underflow/overflow bins:
00235     //  (1) the detector level under/overflow bins are used for
00236     //       normalisation ("efficiency" correction)
00237     //       in our toy example, these bins are populated from tails
00238     //       of the initial MC distribution.
00239     //  (2) the generator level underflow/overflow bins are
00240     //       unfolded. In this example:
00241     //       underflow bin: background events reconstructed in the detector
00242     //       overflow bin: signal events generated at masses > xmaxDet
00243     // for the unfolded result these bins will be filled
00244     //  -> the background normalisation will be contained in the underflow bin
00245     histMdetGenMC->Fill(mDet,mGen,luminosityData/luminosityMC);
00246   }
00247 
00248   //============================================
00249   // generate alternative MC
00250   // this will be used to derive a systematic error due to MC
00251   // parameter uncertainties
00252   TH2D *histMdetGenSysMC=new TH2D("MdetgenSysMC",";mass(det);mass(gen)",
00253                                   nDet,xminDet,xmaxDet,nGen,xminGen,xmaxGen);
00254   neventMC=rnd->Poisson(luminosityMC*crossSection);
00255   for(Int_t i=0;i<neventMC;i++) {
00256     Double_t mGen=GenerateEvent
00257        (0.5, // relative fraction of background
00258         3.6, // peak position in MC with systematic shift
00259         0.15); // peak width in MC
00260     Double_t mDet=DetectorEvent(TMath::Abs(mGen));
00261     histMdetGenSysMC->Fill(mDet,mGen,luminosityData/luminosityMC);
00262   }
00263 
00264   //============================================
00265   // generate data distribution
00266   //
00267   TH1D *histMgenData=new TH1D("MgenData",";mass(gen)",nGen,xminGen,xmaxGen);
00268   TH1D *histMdetData=new TH1D("MdetData",";mass(det)",nDet,xminDet,xmaxDet);
00269   Int_t neventData=rnd->Poisson(luminosityData*crossSection);
00270   for(Int_t i=0;i<neventData;i++) {
00271     Double_t mGen=GenerateEvent(0.4, // relative fraction of background
00272                                 3.8, // peak position in data
00273                                 0.15); // peak width in data
00274     Double_t mDet=DetectorEvent(TMath::Abs(mGen));
00275     // generated data mass for comparison plots
00276     // for real data, we do not have this histogram
00277     histMgenData->Fill(mGen);
00278 
00279     // reconstructed mass, unfolding input
00280     histMdetData->Fill(mDet);
00281   }
00282 
00283   //=========================================================================
00284   // set up the unfolding
00285   // define migration matrix
00286   TUnfoldSys unfold(histMdetGenMC,TUnfold::kHistMapOutputVert);
00287 
00288   // define input and bias scame
00289   // do not use the bias, because MC peak may be at the wrong place
00290   // watch out for error codes returned by the SetInput method
00291   // errors larger or equal 10000 are fatal:
00292   // the data points specified as input are not sufficient to constrain the
00293   // unfolding process
00294   if(unfold.SetInput(histMdetData)>=10000) {
00295     std::cout<<"Unfolding result may be wrong\n";
00296   }
00297 
00298   //========================================================================
00299   // the unfolding is done here
00300   //
00301   // scan L curve and find best point
00302   Int_t nScan=30;
00303   // use automatic L-curve scan: start with taumin=taumax=0.0
00304   Double_t tauMin=0.0;
00305   Double_t tauMax=0.0;
00306   Int_t iBest;
00307   TSpline *logTauX,*logTauY;
00308   TGraph *lCurve;
00309 
00310   // if required, report Info messages (for debugging the L-curve scan)
00311 #ifdef VERBOSE_LCURVE_SCAN
00312   Int_t oldinfo=gErrorIgnoreLevel;
00313   gErrorIgnoreLevel=kInfo;
00314 #endif
00315   // this method scans the parameter tau and finds the kink in the L curve
00316   // finally, the unfolding is done for the best choice of tau
00317   iBest=unfold.ScanLcurve(nScan,tauMin,tauMax,&lCurve,&logTauX,&logTauY);
00318 
00319   // if required, switch to previous log-level
00320 #ifdef VERBOSE_LCURVE_SCAN
00321   gErrorIgnoreLevel=oldinfo;
00322 #endif
00323 
00324   //==========================================================================
00325   // define a correlated systematic error
00326   // for example, assume there is a 10% correlated error for all reconstructed
00327   // masses larger than 7
00328   Double_t SYS_ERROR1_MSTART=6;
00329   Double_t SYS_ERROR1_SIZE=0.1;
00330   TH2D *histMdetGenSys1=new TH2D("Mdetgensys1",";mass(det);mass(gen)",
00331                                  nDet,xminDet,xmaxDet,nGen,xminGen,xmaxGen);
00332   for(Int_t i=0;i<=nDet+1;i++) {
00333      if(histMdetData->GetBinCenter(i)>=SYS_ERROR1_MSTART) {
00334         for(Int_t j=0;j<=nGen+1;j++) {
00335            histMdetGenSys1->SetBinContent(i,j,SYS_ERROR1_SIZE);
00336         }
00337      }
00338   }
00339   unfold.AddSysError(histMdetGenSysMC,"SYSERROR_MC",TUnfold::kHistMapOutputVert,
00340                      TUnfoldSys::kSysErrModeMatrix);
00341   unfold.AddSysError(histMdetGenSys1,"SYSERROR1",TUnfold::kHistMapOutputVert,
00342                      TUnfoldSys::kSysErrModeRelative);
00343 
00344   //==========================================================================
00345   // print some results
00346   //
00347   std::cout<<"tau="<<unfold.GetTau()<<"\n";
00348   std::cout<<"chi**2="<<unfold.GetChi2A()<<"+"<<unfold.GetChi2L()
00349            <<" / "<<unfold.GetNdf()<<"\n";
00350   std::cout<<"chi**2(sys)="<<unfold.GetChi2Sys()<<"\n";
00351 
00352 
00353   //==========================================================================
00354   // create graphs with one point to visualize the best choice of tau
00355   //
00356   Double_t t[1],x[1],y[1];
00357   logTauX->GetKnot(iBest,t[0],x[0]);
00358   logTauY->GetKnot(iBest,t[0],y[0]);
00359   TGraph *bestLcurve=new TGraph(1,x,y);
00360   TGraph *bestLogTauLogChi2=new TGraph(1,t,x);
00361 
00362   //==========================================================================
00363   // retreive results into histograms, using a bin map
00364   //
00365   // the binMap maps the output of the unfolding to the final histogram bins
00366   //
00367   // In this example, the underflow and overflow bin are discarded,
00368   // whereas the other bins are just copied.
00369   //
00370   // This procedure is important for determining the inverse of the
00371   // covariance matrix. The covariance matrix is used for a fit later
00372   // on. Also, the global
00373   // correlation corefficients depend on the proper mapping
00374   Int_t *binMap=new Int_t[nGen+2];
00375   for(Int_t i=1;i<=nGen;i++) binMap[i]=i;
00376   binMap[0]=-1; // discarde underflow bin (here: the background normalisation)
00377   binMap[nGen+1]=-1; // discarde overflow bin (here: bins with mass>10)
00378 
00379   // get unfolded distribution using the binMap
00380   TH1D *histMunfold=new TH1D("Unfolded",";mass(gen)",nGen,xminGen,xmaxGen);
00381   unfold.GetOutput(histMunfold,binMap);
00382 
00383   // get unfolding result, folded back
00384   TH1D *histMdetFold=unfold.GetFoldedOutput("FoldedBack",";mass(det)",
00385                                               xminDet,xmaxDet);
00386 
00387   // get error matrix (input distribution [stat] errors only)
00388   // using the binMap
00389   TH2D *histEmatData=new TH2D("EmatData",";mass(gen);mass(gen)",
00390                                nGen,xminGen,xmaxGen,nGen,xminGen,xmaxGen);
00391   unfold.GetEmatrix(histEmatData,binMap);
00392 
00393   // get total error matrix:
00394   //   migration matrix uncorrelated and colrrelated systematic errors
00395   //   added inquadrature with the data statistical errors
00396   TH2D *histEmatTotal=new TH2D("EmatTotal",";mass(gen);mass(gen)",
00397                                nGen,xminGen,xmaxGen,nGen,xminGen,xmaxGen);
00398   unfold.GetEmatrixTotal(histEmatTotal,binMap);
00399 
00400   // create data histogram with the total errors
00401   TH1D *histTotalError=
00402      new TH1D("TotalError",";mass(gen)",nGen,xminGen,xmaxGen);
00403   for(Int_t bin=1;bin<=nGen;bin++) {
00404     histTotalError->SetBinContent(bin,histMunfold->GetBinContent(bin));
00405     histTotalError->SetBinError
00406        (bin,TMath::Sqrt(histEmatTotal->GetBinContent(bin,bin)));
00407   }
00408 
00409   // get global correlation coefficients and inverse of covariance
00410   // matrix.
00411   // !!! these quantities do not include the systematic errors !!!
00412   gHistInvEMatrix=new TH2D("invEmat",";mass(gen);mass(gen)",
00413                            nGen,xminGen,xmaxGen,nGen,xminGen,xmaxGen);
00414   TH1D *histRhoi=new TH1D("rho_I",";mass(gen)",nGen,xminGen,xmaxGen);
00415   unfold.GetRhoI(histRhoi,gHistInvEMatrix,binMap);
00416 
00417   // remove the binMap, it is not needed anymore
00418   delete[] binMap;
00419   binMap=0; // just in case You think it is still defined
00420 
00421   //======================================================================
00422   // fit Breit-Wigner shape to unfolded data, using the full error matrix
00423   // here we use a "user" chi**2 function to take into account
00424   // the full covariance matrix
00425   // !!! Note: the systematic errors are not included in this fit
00426   // !!! one would have to invert the matrix histEmatTotal
00427   // !!! to include syst. errors in the fit
00428   gFitter=TVirtualFitter::Fitter(histMunfold);
00429   gFitter->SetFCN(chisquare_corr);
00430 
00431   TF1 *bw=new TF1("bw",bw_func,xminGen,xmaxGen,3);
00432   bw->SetParameter(0,1000.);
00433   bw->SetParameter(1,3.8);
00434   bw->SetParameter(2,0.2);
00435   // for (wrong!) fitting without correlations, drop the option "U"
00436   // here.
00437   histMunfold->Fit(bw,"UE");
00438 
00439   //=====================================================================
00440   // plot some histograms
00441   TCanvas output;
00442   output.Divide(3,2);
00443 
00444   // Show the matrix which connects input and output
00445   // There are overflow bins at the bottom, not shown in the plot
00446   // These contain the background shape.
00447   // The overflow bins to the left and right contain
00448   // events which are not reconstructed. These are necessary for proper MC
00449   // normalisation
00450   output.cd(1);
00451   histMdetGenMC->Draw("BOX");
00452 
00453   // draw generator-level distribution:
00454   //   data (red) [for real data this is not available]
00455   //   MC input (black) [with completely wrong peak position and shape]
00456   //   unfolded data (blue)
00457   output.cd(2);
00458   histTotalError->SetLineColor(kBlue);
00459   histTotalError->Draw("E");
00460   histMunfold->SetLineColor(kGreen);
00461   histMunfold->Draw("SAME E1");
00462   histMgenData->SetLineColor(kRed);
00463   histMgenData->Draw("SAME");
00464   histMgenMC->Draw("SAME HIST");
00465 
00466   // show detector level distributions
00467   //    data (red)
00468   //    MC (black) [with completely wrong peak position and shape]
00469   //    unfolded data (blue)
00470   output.cd(3);
00471   histMdetFold->SetLineColor(kBlue);
00472   histMdetFold->Draw();
00473   histMdetMC->Draw("SAME HIST");
00474   TH1D *histInput=unfold.GetInput("Minput",";mass(det)",xminDet,xmaxDet);
00475   histInput->SetLineColor(kRed);
00476   histInput->Draw("SAME");
00477 
00478   // show correlation coefficients
00479   output.cd(4);
00480   histRhoi->Draw();
00481 
00482   // show tau as a function of chi**2
00483   output.cd(5);
00484   logTauX->Draw();
00485   bestLogTauLogChi2->SetMarkerColor(kRed);
00486   bestLogTauLogChi2->Draw("*");
00487 
00488   // show the L curve
00489   output.cd(6);
00490   lCurve->Draw("AL");
00491   bestLcurve->SetMarkerColor(kRed);
00492   bestLcurve->Draw("*");
00493 
00494   output.SaveAs("testUnfold1.ps");
00495 
00496   return 0;
00497 }

Generated on Tue Jul 5 15:44:50 2011 for ROOT_528-00b_version by  doxygen 1.5.1