RooFFTConvPdf.cxx

Go to the documentation of this file.
00001 
00002  /***************************************************************************** 
00003   * Project: RooFit                                                           * 
00004   *                                                                           * 
00005   * Copyright (c) 2000-2005, Regents of the University of California          * 
00006   *                          and Stanford University. All rights reserved.    * 
00007   *                                                                           * 
00008   * Redistribution and use in source and binary forms,                        * 
00009   * with or without modification, are permitted according to the terms        * 
00010   * listed in LICENSE (http://roofit.sourceforge.net/license.txt)             * 
00011   *****************************************************************************/ 
00012 
00013 //////////////////////////////////////////////////////////////////////////////
00014 //
00015  // 
00016  // This class implement a generic one-dimensional numeric convolution of two p.d.f.
00017  // and can convolve any two RooAbsPdfs. The class exploits the convolution theorem
00018  //
00019  //       f(x) (*) g(x) --F--> f(k_i) * g(k_i)
00020  //
00021  // and calculate the convolution by calculate a Real->Complex FFT of both input p.d.fs
00022  // multiplying the complex coefficients and performing the reverse Complex->Real FFT
00023  // to get the result in the input space. This class using the ROOT FFT Interface to
00024  // the (free) FFTW3 package (www.fftw.org) and requires that your ROOT installation is
00025  // compiled with the --enable-fftw3 option (instructions for Linux follow)
00026  //
00027  // Note that the performance in terms of speed and stability of RooFFTConvPdf is 
00028  // vastly superior to that of RooNumConvPdf 
00029  //
00030  // An important feature of FFT convolutions is that the observable is treated in a
00031  // cyclical way. This is correct & desirable behavior for cyclical observables such as angles,
00032  // but it may not be for other observables. The effect that is observed is that if
00033  // p.d.f is zero at xMin and non-zero at xMax some spillover occurs and
00034  // a rising tail may appear at xMin. This effect can be reduced or eliminated by
00035  // introducing a buffer zone in the FFT calculation. If this feature is activated
00036  // input the sampling array for the FFT calculation is extended in both directions
00037  // and filled with repetitions of the lowest bin value and highest bin value
00038  // respectively. The buffer bins are stripped again when the FFT output values
00039  // are transferred to the p.d.f cache. The default buffer size is 10% of the
00040  // observable domain size and can be changed with setBufferFraction() member function.
00041  // 
00042  // This class is a caching p.d.f inheriting from RooAbsCachedPdf. If this p.d.f 
00043  // is evaluated for a particular value of x, the FFT calculate the values for the
00044  // p.d.f at all points in observables space for the given choice of parameters,
00045  // which are stored in the cache. Subsequent evaluations of RooFFTConvPdf with
00046  // identical parameters will retrieve results from the cache. If one or more
00047  // of the parameters change, the cache will be updated.
00048  // 
00049  // The sampling density of the cache is controlled by the binning of the 
00050  // the convolution observable, which can be changed from RooRealVar::setBins(N)
00051  // For good results N should be large (>1000). Additional interpolation of
00052  // cache values may improve the result if courser binning are chosen. These can be 
00053  // set in the constructor or through the setInterpolationOrder() member function. 
00054  // For N>1000 interpolation will not substantially improve the performance.
00055  //
00056  // Additionial information on caching activities can be displayed by monitoring
00057  // the message stream with topic "Caching" at the INFO level, i.e. 
00058  // do RooMsgService::instance().addStream(RooMsgService::INFO,Topic("Caching")) 
00059  // to see these message on stdout
00060  //
00061  // Multi-dimensional convolutions are not supported yet, but will be in the future
00062  // as FFTW can calculate them
00063  //
00064  // ---
00065  // 
00066  // Installing a copy of FFTW on Linux and compiling ROOT to use it
00067  // 
00068  // 1) Go to www.fftw.org and download the latest stable version (a .tar.gz file)
00069  //
00070  // If you have root access to your machine and want to make a system installation of FFTW
00071  //
00072  //   2) Untar fftw-XXX.tar.gz in /tmp, cd into the untarred directory 
00073  //       and type './configure' followed by 'make install'. 
00074  //       This will install fftw in /usr/local/bin,lib etc...
00075  //
00076  //   3) Start from a source installation of ROOT. If you now have a binary distribution,
00077  //      first download a source tar ball from root.cern.ch for your ROOT version and untar it.
00078  //      Run 'configure', following the instruction from 'configure --help' but be sure run 'configure' 
00079  //      with additional flags '--enable-fftw3' and '--enable-roofit', then run 'make'
00080  //         
00081  // 
00082  // If you do not have root access and want to make a private installation of FFTW
00083  //
00084  //   2) Make a private install area for FFTW, e.g. /home/myself/fftw
00085  //
00086  //   3) Untar fftw-XXX.tar.gz in /tmp, cd into the untarred directory
00087  //       and type './configure --prefix=/home/myself/fftw' followed by 'make install'. 
00088  //       Substitute /home/myself/fftw with a directory of your choice. This
00089  //       procedure will install FFTW in the location designated by you
00090  // 
00091  //   4) Start from a source installation of ROOT. If you now have a binary distribution,
00092  //      first download a source tar ball from root.cern.ch for your ROOT version and untar it.
00093  //      Run 'configure', following the instruction from 'configure --help' but be sure run 'configure' 
00094  //      with additional flags
00095  //       '--enable-fftw3', 
00096  //       '--with-fftw3-incdir=/home/myself/fftw/include', 
00097  //       '--width-fftw3-libdir=/home/myself/fftw/lib' and 
00098  //       '--enable-roofit' 
00099  //      Then run 'make'
00100 //
00101 
00102 
00103 #include "Riostream.h" 
00104 
00105 #include "RooFit.h"
00106 #include "RooFFTConvPdf.h" 
00107 #include "RooAbsReal.h" 
00108 #include "RooMsgService.h"
00109 #include "RooDataHist.h"
00110 #include "RooHistPdf.h"
00111 #include "RooRealVar.h"
00112 #include "TComplex.h"
00113 #include "TVirtualFFT.h"
00114 #include "RooGenContext.h"
00115 #include "RooConvGenContext.h"
00116 #include "RooBinning.h"
00117 #include "RooLinearVar.h"
00118 #include "RooCustomizer.h"
00119 #include "RooGlobalFunc.h"
00120 #include "RooLinearVar.h"
00121 #include "RooConstVar.h"
00122 #include "TClass.h"
00123 #include "TSystem.h"
00124 
00125 using namespace std ;
00126 
00127 ClassImp(RooFFTConvPdf) 
00128 
00129 
00130 
00131 //_____________________________________________________________________________
00132 RooFFTConvPdf::RooFFTConvPdf(const char *name, const char *title, RooRealVar& convVar, RooAbsPdf& pdf1, RooAbsPdf& pdf2, Int_t ipOrder) :
00133   RooAbsCachedPdf(name,title,ipOrder),
00134   _x("!x","Convolution Variable",this,convVar),
00135   _xprime("!xprime","External Convolution Variable",this,0),
00136   _pdf1("!pdf1","pdf1",this,pdf1,kFALSE),
00137   _pdf2("!pdf2","pdf2",this,pdf2,kFALSE),
00138   _params("!params","effective parameters",this),
00139   _bufFrac(0.1),
00140   _bufStrat(Extend),
00141   _shift1(0),
00142   _shift2(0),
00143   _cacheObs("!cacheObs","Cached observables",this,kFALSE,kFALSE)
00144  { 
00145    // Constructor for convolution of pdf1 (x) pdf2 in observable convVar. The binning used for the FFT sampling is controlled
00146    // by the binning named "cache" in the convolution observable. The resulting FFT convolved histogram is interpolated at
00147    // order 'ipOrder' A minimum binning of 1000 bins is recommended.
00148 
00149    if (!convVar.hasBinning("cache")) {
00150      convVar.setBinning(convVar.getBinning(),"cache") ;
00151    }
00152    
00153    _shift2 = (convVar.getMax()+convVar.getMin())/2 ;
00154 
00155    calcParams() ;
00156 
00157  } 
00158 
00159 
00160 
00161 //_____________________________________________________________________________
00162 RooFFTConvPdf::RooFFTConvPdf(const char *name, const char *title, RooAbsReal& pdfConvVar, RooRealVar& convVar, RooAbsPdf& pdf1, RooAbsPdf& pdf2, Int_t ipOrder) :
00163   RooAbsCachedPdf(name,title,ipOrder),
00164   _x("!x","Convolution Variable",this,convVar,kFALSE,kFALSE),
00165   _xprime("!xprime","External Convolution Variable",this,pdfConvVar),
00166   _pdf1("!pdf1","pdf1",this,pdf1,kFALSE),
00167   _pdf2("!pdf2","pdf2",this,pdf2,kFALSE),
00168   _params("!params","effective parameters",this),
00169   _bufFrac(0.1),
00170   _bufStrat(Extend),
00171   _shift1(0),
00172   _shift2(0),
00173   _cacheObs("!cacheObs","Cached observables",this,kFALSE,kFALSE)
00174  { 
00175    // Constructor for convolution of pdf1 (x) pdf2 in observable convVar. The binning used for the FFT sampling is controlled
00176    // by the binning named "cache" in the convolution observable. The resulting FFT convolved histogram is interpolated at
00177    // order 'ipOrder' A minimum binning of 1000 bins is recommended.
00178 
00179    if (!convVar.hasBinning("cache")) {
00180      convVar.setBinning(convVar.getBinning(),"cache") ;
00181    }
00182    
00183    _shift2 = (convVar.getMax()+convVar.getMin())/2 ;
00184 
00185    calcParams() ;
00186  } 
00187 
00188 
00189 
00190 //_____________________________________________________________________________
00191 RooFFTConvPdf::RooFFTConvPdf(const RooFFTConvPdf& other, const char* name) :  
00192   RooAbsCachedPdf(other,name),
00193   _x("!x",this,other._x),
00194   _xprime("!xprime",this,other._xprime),
00195   _pdf1("!pdf1",this,other._pdf1),
00196   _pdf2("!pdf2",this,other._pdf2),
00197   _params("!params",this,other._params),
00198   _bufFrac(other._bufFrac),
00199   _bufStrat(other._bufStrat),
00200   _shift1(other._shift1),
00201   _shift2(other._shift2),
00202   _cacheObs("!cacheObs",this,other._cacheObs)
00203  { 
00204    // Copy constructor
00205  } 
00206 
00207 
00208 
00209 //_____________________________________________________________________________
00210 RooFFTConvPdf::~RooFFTConvPdf() 
00211 {
00212   // Destructor 
00213 }
00214 
00215 
00216 
00217 //_____________________________________________________________________________
00218 const char* RooFFTConvPdf::inputBaseName() const 
00219 {
00220   // Return base name component for cache components in this case 'PDF1_CONV_PDF2'
00221 
00222   static TString name ;
00223   name = _pdf1.arg().GetName() ;
00224   name.Append("_CONV_") ;
00225   name.Append(_pdf2.arg().GetName()) ;
00226   return name.Data() ;
00227 }
00228 
00229 
00230 
00231 
00232 //_____________________________________________________________________________
00233 RooFFTConvPdf::PdfCacheElem* RooFFTConvPdf::createCache(const RooArgSet* nset) const 
00234 {
00235   // Return specialized cache subclass for FFT calculations
00236   return new FFTCacheElem(*this,nset) ;
00237 }
00238 
00239 
00240 
00241 
00242 //_____________________________________________________________________________
00243 RooFFTConvPdf::FFTCacheElem::FFTCacheElem(const RooFFTConvPdf& self, const RooArgSet* nsetIn) : 
00244   PdfCacheElem(self,nsetIn),
00245   fftr2c1(0),fftr2c2(0),fftc2r(0) 
00246 {
00247 
00248   // Clone input pdf and attach to dataset
00249   RooAbsPdf* clonePdf1 = (RooAbsPdf*) self._pdf1.arg().cloneTree() ;
00250   RooAbsPdf* clonePdf2 = (RooAbsPdf*) self._pdf2.arg().cloneTree() ;
00251   clonePdf1->attachDataSet(*hist()) ;
00252   clonePdf2->attachDataSet(*hist()) ;
00253    
00254    // Shift observable
00255    RooRealVar* convObs = (RooRealVar*) hist()->get()->find(self._x.arg().GetName()) ;
00256 
00257    // Install FFT reference range 
00258    string refName = Form("refrange_fft_%s",self.GetName()) ;
00259    convObs->setRange(refName.c_str(),convObs->getMin(),convObs->getMax()) ;   
00260   
00261    if (self._shift1!=0) {
00262      RooLinearVar* shiftObs1 = new RooLinearVar(Form("%s_shifted_FFTBuffer1",convObs->GetName()),"shiftObs1",
00263                                                *convObs,RooFit::RooConst(1),RooFit::RooConst(-1*self._shift1)) ;
00264 
00265     RooArgSet clonedBranches1 ;
00266     RooCustomizer cust(*clonePdf1,"fft") ;
00267     cust.replaceArg(*convObs,*shiftObs1) ;  
00268 
00269     pdf1Clone = (RooAbsPdf*) cust.build() ;
00270 
00271     pdf1Clone->addOwnedComponents(*shiftObs1) ;
00272     pdf1Clone->addOwnedComponents(*clonePdf1) ;
00273 
00274   } else {
00275     pdf1Clone = clonePdf1 ;
00276   }
00277 
00278   if (self._shift2!=0) {
00279     RooLinearVar* shiftObs2 = new RooLinearVar(Form("%s_shifted_FFTBuffer2",convObs->GetName()),"shiftObs2",
00280                                                *convObs,RooFit::RooConst(1),RooFit::RooConst(-1*self._shift2)) ;
00281 
00282     RooArgSet clonedBranches2 ;
00283     RooCustomizer cust(*clonePdf2,"fft") ;
00284     cust.replaceArg(*convObs,*shiftObs2) ;  
00285 
00286     pdf1Clone->addOwnedComponents(*shiftObs2) ;
00287     pdf1Clone->addOwnedComponents(*clonePdf2) ;
00288 
00289     pdf2Clone = (RooAbsPdf*) cust.build() ;
00290 
00291   } else {
00292     pdf2Clone = clonePdf2 ;
00293   }
00294 
00295 
00296   // Attach cloned pdf to all original parameters of self
00297   RooArgSet* fftParams = self.getParameters(*convObs) ;
00298 
00299   // Remove all cache histogram from fftParams as these
00300   // observable need to remain attached to the histogram
00301   fftParams->remove(*hist()->get(),kTRUE,kTRUE) ;
00302 
00303   pdf1Clone->recursiveRedirectServers(*fftParams) ;
00304   pdf2Clone->recursiveRedirectServers(*fftParams) ;
00305   pdf1Clone->fixAddCoefRange(refName.c_str()) ;
00306   pdf2Clone->fixAddCoefRange(refName.c_str()) ;
00307 
00308   delete fftParams ;
00309 
00310   // Save copy of original histX binning and make alternate binning
00311   // for extended range scanning
00312 
00313   Int_t N = convObs->numBins() ;
00314   Int_t Nbuf = static_cast<Int_t>((N*self.bufferFraction())/2 + 0.5) ;
00315   Double_t obw = (convObs->getMax() - convObs->getMin())/N ;
00316   Int_t N2 = N+2*Nbuf ;
00317 
00318   scanBinning = new RooUniformBinning (convObs->getMin()-Nbuf*obw,convObs->getMax()+Nbuf*obw,N2) ;
00319   histBinning = convObs->getBinning().clone() ;
00320 
00321   // Deactivate dirty state propagation on datahist observables
00322   // and set all nodes on both pdfs to operMode AlwaysDirty
00323   hist()->setDirtyProp(kFALSE) ;  
00324   convObs->setOperMode(ADirty,kTRUE) ;
00325 } 
00326 
00327 
00328 //_____________________________________________________________________________
00329 TString RooFFTConvPdf::histNameSuffix() const
00330 {
00331   // Suffix for cache histogram (added in addition to suffix for cache name)
00332   return TString(Form("_BufFrac%3.1f_BufStrat%d",_bufFrac,_bufStrat)) ;
00333 }
00334 
00335 
00336 
00337 //_____________________________________________________________________________
00338 RooArgList RooFFTConvPdf::FFTCacheElem::containedArgs(Action a) 
00339 {
00340   // Returns all RooAbsArg objects contained in the cache element
00341   RooArgList ret(PdfCacheElem::containedArgs(a)) ;
00342 
00343   ret.add(*pdf1Clone) ;
00344   ret.add(*pdf2Clone) ;
00345   if (pdf1Clone->ownedComponents()) {
00346     ret.add(*pdf1Clone->ownedComponents()) ;
00347   }
00348   if (pdf2Clone->ownedComponents()) {
00349     ret.add(*pdf2Clone->ownedComponents()) ;
00350   }
00351 
00352   return ret ;
00353 }
00354 
00355 
00356 //_____________________________________________________________________________
00357 RooFFTConvPdf::FFTCacheElem::~FFTCacheElem() 
00358 { 
00359   delete fftr2c1 ; 
00360   delete fftr2c2 ; 
00361   delete fftc2r ; 
00362 
00363   delete pdf1Clone ;
00364   delete pdf2Clone ;
00365 
00366   delete histBinning ;
00367   delete scanBinning ;
00368 
00369 }
00370 
00371 
00372 
00373 
00374 //_____________________________________________________________________________
00375 void RooFFTConvPdf::fillCacheObject(RooAbsCachedPdf::PdfCacheElem& cache) const 
00376 {
00377   // Fill the contents of the cache the FFT convolution output
00378   RooDataHist& cacheHist = *cache.hist() ;
00379   
00380   ((FFTCacheElem&)cache).pdf1Clone->setOperMode(ADirty,kTRUE) ;
00381   ((FFTCacheElem&)cache).pdf2Clone->setOperMode(ADirty,kTRUE) ;
00382 
00383   // Determine if there other observables than the convolution observable in the cache
00384   RooArgSet otherObs ;
00385   RooArgSet(*cacheHist.get()).snapshot(otherObs) ;
00386 
00387   RooAbsArg* histArg = otherObs.find(_x.arg().GetName()) ;
00388   if (histArg) {
00389     otherObs.remove(*histArg,kTRUE,kTRUE) ;
00390     delete histArg ;
00391   } 
00392 
00393   // Handle trivial scenario -- no other observables
00394   if (otherObs.getSize()==0) {
00395     fillCacheSlice((FFTCacheElem&)cache,RooArgSet()) ;
00396     return ;
00397   }
00398 
00399   // Handle cases where there are other cache slices
00400   // Iterator over available slice positions and fill each
00401 
00402   // Determine number of bins for each slice position observable
00403   Int_t n = otherObs.getSize() ;
00404   Int_t* binCur = new Int_t[n+1] ;
00405   Int_t* binMax = new Int_t[n+1] ;
00406   Int_t curObs = 0 ;
00407 
00408   RooAbsLValue** obsLV = new RooAbsLValue*[n] ;
00409   TIterator* iter = otherObs.createIterator() ;
00410   RooAbsArg* arg ;
00411   Int_t i(0) ;
00412   while((arg=(RooAbsArg*)iter->Next())) {
00413     RooAbsLValue* lvarg = dynamic_cast<RooAbsLValue*>(arg) ;
00414     obsLV[i] = lvarg ;
00415     binCur[i] = 0 ;
00416     // coverity[FORWARD_NULL]
00417     binMax[i] = lvarg->numBins(binningName())-1 ;    
00418     i++ ;
00419   }
00420   delete iter ;
00421 
00422   Bool_t loop(kTRUE) ;
00423   while(loop) {
00424     // Set current slice position
00425     for (Int_t j=0 ; j<n ; j++) { obsLV[j]->setBin(binCur[j],binningName()) ; }
00426 
00427 //     cout << "filling slice: bin of obsLV[0] = " << obsLV[0]->getBin() << endl ;
00428 
00429     // Fill current slice
00430     fillCacheSlice((FFTCacheElem&)cache,otherObs) ;
00431 
00432     // Determine which iterator to increment
00433     while(binCur[curObs]==binMax[curObs]) {
00434       
00435       // Reset current iterator and consider next iterator ;
00436       binCur[curObs]=0 ;      
00437       curObs++ ;
00438 
00439       // master termination condition
00440       if (curObs==n) {
00441         loop=kFALSE ;
00442         break ;
00443       }
00444     }
00445 
00446     // Increment current iterator
00447     binCur[curObs]++ ;
00448     curObs=0 ;      
00449     
00450   }
00451 
00452   delete[] obsLV ;
00453   delete[] binMax ;
00454   delete[] binCur ;
00455   
00456 }
00457 
00458 
00459 //_____________________________________________________________________________
00460 void RooFFTConvPdf::fillCacheSlice(FFTCacheElem& aux, const RooArgSet& slicePos) const 
00461 {
00462   // Fill a slice of cachePdf with the output of the FFT convolution calculation
00463 
00464   // Extract histogram that is the basis of the RooHistPdf
00465   RooDataHist& cacheHist = *aux.hist() ;
00466 
00467   // Sample array of input points from both pdfs 
00468   // Note that returned arrays have optional buffers zones below and above range ends
00469   // to reduce cyclical effects and have been cyclically rotated so that bin containing
00470   // zero value is at position zero. Example:
00471   // 
00472   //     original:                -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5
00473   //     add buffer zones:    U U -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 O O
00474   //     rotate:              0 +1 +2 +3 +4 +5 O O U U -5 -4 -3 -2 -1
00475   //
00476   // 
00477 
00478   Int_t N,N2,binShift1,binShift2 ;
00479   
00480   RooRealVar* histX = (RooRealVar*) cacheHist.get()->find(_x.arg().GetName()) ;
00481   if (_bufStrat==Extend) histX->setBinning(*aux.scanBinning) ;
00482   Double_t* input1 = scanPdf((RooRealVar&)_x.arg(),*aux.pdf1Clone,cacheHist,slicePos,N,N2,binShift1,_shift1) ;
00483   Double_t* input2 = scanPdf((RooRealVar&)_x.arg(),*aux.pdf2Clone,cacheHist,slicePos,N,N2,binShift2,_shift2) ;
00484   if (_bufStrat==Extend) histX->setBinning(*aux.histBinning) ;
00485 
00486   // Retrieve previously defined FFT transformation plans
00487   if (!aux.fftr2c1) {
00488     aux.fftr2c1 = TVirtualFFT::FFT(1, &N2, "R2CK");
00489     aux.fftr2c2 = TVirtualFFT::FFT(1, &N2, "R2CK");
00490     aux.fftc2r  = TVirtualFFT::FFT(1, &N2, "C2RK");
00491   }
00492   
00493   // Real->Complex FFT Transform on p.d.f. 1 sampling
00494   aux.fftr2c1->SetPoints(input1);
00495   aux.fftr2c1->Transform();
00496 
00497   // Real->Complex FFT Transform on p.d.f 2 sampling
00498   aux.fftr2c2->SetPoints(input2);
00499   aux.fftr2c2->Transform();
00500 
00501   // Loop over first half +1 of complex output results, multiply 
00502   // and set as input of reverse transform
00503   for (Int_t i=0 ; i<N2/2+1 ; i++) {
00504     Double_t re1,re2,im1,im2 ;
00505     aux.fftr2c1->GetPointComplex(i,re1,im1) ;
00506     aux.fftr2c2->GetPointComplex(i,re2,im2) ;
00507     Double_t re = re1*re2 - im1*im2 ;
00508     Double_t im = re1*im2 + re2*im1 ;
00509     TComplex t(re,im) ;
00510     aux.fftc2r->SetPointComplex(i,t) ;
00511   }
00512 
00513   // Reverse Complex->Real FFT transform product
00514   aux.fftc2r->Transform() ;
00515 
00516   Int_t totalShift = binShift1 + (N2-N)/2 ;
00517 
00518   // Store FFT result in cache
00519   TIterator* iter = const_cast<RooDataHist&>(cacheHist).sliceIterator(const_cast<RooAbsReal&>(_x.arg()),slicePos) ;
00520   for (Int_t i =0 ; i<N ; i++) {
00521 
00522     // Cyclically shift array back so that bin containing zero is back in zeroBin
00523     Int_t j = i + totalShift ;
00524     while (j<0) j+= N2 ;
00525     while (j>=N2) j-= N2 ;
00526 
00527     iter->Next() ;
00528     cacheHist.set(aux.fftc2r->GetPointReal(j)) ;    
00529   }
00530   delete iter ;
00531 
00532   // cacheHist.dump2() ;
00533 
00534   // Delete input arrays
00535   delete[] input1 ;
00536   delete[] input2 ;
00537 
00538 }
00539 
00540 
00541 //_____________________________________________________________________________
00542 Double_t*  RooFFTConvPdf::scanPdf(RooRealVar& obs, RooAbsPdf& pdf, const RooDataHist& hist, const RooArgSet& slicePos, 
00543                                   Int_t& N, Int_t& N2, Int_t& zeroBin, Double_t shift) const
00544 {
00545   // Scan the values of 'pdf' in observable 'obs' using the bin values stored in 'hist' at slice position 'slicePos'
00546   // N is filled with the number of bins defined in hist, N2 is filled with N plus the number of buffer bins
00547   // The return value is an array of doubles of length N2 with the sampled values. The caller takes ownership
00548   // of the array
00549 
00550 
00551   RooRealVar* histX = (RooRealVar*) hist.get()->find(obs.GetName()) ;
00552 
00553   // Calculate number of buffer bins on each size to avoid cyclical flow
00554   N = histX->numBins(binningName()) ;
00555   Int_t Nbuf = static_cast<Int_t>((N*bufferFraction())/2 + 0.5) ;
00556   N2 = N+2*Nbuf ;
00557 
00558   
00559   // Allocate array of sampling size plus optional buffer zones
00560   Double_t* array = new Double_t[N2] ;
00561   
00562   // Set position of non-convolution observable to that of the cache slice that were are processing now
00563   hist.get(slicePos) ;
00564 
00565   // Find bin ID that contains zero value
00566   zeroBin = 0 ;
00567   if (histX->getMax()>=0 && histX->getMin()<=0) {
00568     zeroBin = histX->getBinning().binNumber(0) ;
00569   } else if (histX->getMin()>0) {
00570     Double_t bw = (histX->getMax() - histX->getMin())/N2 ;
00571     zeroBin = Int_t(-histX->getMin()/bw) ;
00572   } else {
00573     Double_t bw = (histX->getMax() - histX->getMin())/N2 ;
00574     zeroBin = Int_t(-1*histX->getMax()/bw) ;
00575   }
00576 
00577   Int_t binShift = Int_t((N2* shift) / (histX->getMax()-histX->getMin())) ;
00578 
00579   zeroBin += binShift ;
00580   while(zeroBin>=N2) zeroBin-= N2 ;
00581   while(zeroBin<0) zeroBin+= N2 ;
00582 
00583   // First scan hist into temp array 
00584   Double_t *tmp = new Double_t[N2] ;
00585   Int_t k(0) ;
00586   switch(_bufStrat) {
00587 
00588   case Extend:
00589     // Sample entire extended range (N2 samples)
00590     for (k=0 ; k<N2 ; k++) {
00591       histX->setBin(k) ;
00592       tmp[k] = pdf.getVal(hist.get()) ;    
00593     }  
00594     break ;
00595 
00596   case Flat:    
00597     // Sample original range (N samples) and fill lower and upper buffer
00598     // bins with p.d.f. value at respective boundary
00599     {
00600       histX->setBin(0) ;
00601       Double_t val = pdf.getVal(hist.get()) ;  
00602       for (k=0 ; k<Nbuf ; k++) {
00603         tmp[k] = val ;
00604       }
00605       for (k=0 ; k<N ; k++) {
00606         histX->setBin(k) ;
00607         tmp[k+Nbuf] = pdf.getVal(hist.get()) ;    
00608       }  
00609       histX->setBin(N-1) ;
00610       val = pdf.getVal(hist.get()) ;  
00611       for (k=0 ; k<Nbuf ; k++) {
00612         tmp[N+Nbuf+k] = val ;
00613       }  
00614     }
00615     break ;
00616 
00617   case Mirror:
00618     // Sample original range (N samples) and fill lower and upper buffer
00619     // bins with mirror image of sampled range
00620     for (k=0 ; k<N ; k++) {
00621       histX->setBin(k) ;
00622       tmp[k+Nbuf] = pdf.getVal(hist.get()) ;    
00623     }  
00624     for (k=1 ; k<=Nbuf ; k++) {
00625       histX->setBin(k) ;
00626       tmp[Nbuf-k] = pdf.getVal(hist.get()) ;    
00627       histX->setBin(N-k) ;
00628       tmp[Nbuf+N+k-1] = pdf.getVal(hist.get()) ;    
00629     }  
00630     break ;
00631   }
00632 
00633   // Scan function and store values in array
00634   for (Int_t i=0 ; i<N2 ; i++) {
00635     // Cyclically shift writing location by zero bin position    
00636     Int_t j = i - (zeroBin) ;
00637     if (j<0) j+= N2 ;
00638     if (j>=N2) j-= N2 ;
00639     array[i] = tmp[j] ;
00640   }  
00641 
00642   // Cleanup 
00643   delete[] tmp ;
00644   return array ;
00645 }
00646 
00647 
00648 
00649 //_____________________________________________________________________________
00650 RooArgSet* RooFFTConvPdf::actualObservables(const RooArgSet& nset) const 
00651 {
00652   // Return the observables to be cached given the normalization set nset
00653   //
00654   // If the cache observables is in nset then this is 
00655   //    - the convolution observable plus 
00656   //    - any member of nset that is either a RooCategory, 
00657   //    - or was previously specified through setCacheObservables().
00658   //
00659   // In case the cache observable is _not_ in nset, then it is
00660   //    - the convolution observable plus 
00661   //    - all member of nset are observables of this p.d.f.
00662   // 
00663 
00664   // Get complete list of observables 
00665   RooArgSet* obs1 = _pdf1.arg().getObservables(nset) ;
00666   RooArgSet* obs2 = _pdf2.arg().getObservables(nset) ;
00667   obs1->add(*obs2,kTRUE) ;
00668 
00669   // Check if convolution observable is in nset
00670   if (nset.contains(_x.arg())) {
00671 
00672     // Now strip out all non-category observables
00673     TIterator* iter = obs1->createIterator() ;
00674     RooAbsArg* arg ;
00675     RooArgSet killList ;
00676     while((arg=(RooAbsArg*)iter->Next())) {
00677       if (arg->IsA()->InheritsFrom(RooAbsReal::Class()) && !_cacheObs.find(arg->GetName())) {
00678         killList.add(*arg) ;
00679       }
00680     }
00681     delete iter ;
00682     obs1->remove(killList) ;
00683     
00684     // And add back the convolution observables
00685     obs1->add(_x.arg(),kTRUE) ; 
00686     delete obs2 ;
00687     
00688   } else {
00689 
00690     // If cacheObs was filled, cache only observables in there
00691     if (_cacheObs.getSize()>0) {
00692       TIterator* iter = obs1->createIterator() ;
00693       RooAbsArg* arg ;
00694       RooArgSet killList ;
00695       while((arg=(RooAbsArg*)iter->Next())) {
00696         if (arg->IsA()->InheritsFrom(RooAbsReal::Class()) && !_cacheObs.find(arg->GetName())) {
00697           killList.add(*arg) ;
00698         }
00699       }
00700       delete iter ;
00701       obs1->remove(killList) ;
00702     }
00703 
00704 
00705     // Make sure convolution observable is always in there
00706     obs1->add(_x.arg(),kTRUE) ; 
00707     delete obs2 ;
00708     
00709   }
00710   
00711   return obs1 ;  
00712 }
00713 
00714 
00715 
00716 //_____________________________________________________________________________
00717 RooArgSet* RooFFTConvPdf::actualParameters(const RooArgSet& nset) const 
00718 {  
00719   // Return the parameters on which the cache depends given normalization
00720   // set nset. For this p.d.f these are the parameters of the input p.d.f.
00721   // but never the convolution variable, it case it is not part of nset
00722 
00723   RooArgSet* vars = getVariables() ;
00724   RooArgSet* obs = actualObservables(nset) ;
00725   vars->remove(*obs) ;
00726   delete obs ;
00727 
00728   return vars ;
00729 }
00730 
00731 
00732 
00733 //_____________________________________________________________________________
00734 RooAbsArg& RooFFTConvPdf::pdfObservable(RooAbsArg& histObservable) const 
00735 {
00736   // Return p.d.f. observable (which can be a function) to substitute given
00737   // p.d.f. observable. Substitute x by xprime if xprime is set
00738 
00739   if (_xprime.absArg() && string(histObservable.GetName())==_x.absArg()->GetName()) {
00740     return (*_xprime.absArg()) ;
00741   }
00742   return histObservable ;
00743 }
00744 
00745 
00746 
00747 //_____________________________________________________________________________
00748 RooAbsGenContext* RooFFTConvPdf::genContext(const RooArgSet &vars, const RooDataSet *prototype, 
00749                                             const RooArgSet* auxProto, Bool_t verbose) const 
00750 {
00751   // Create appropriate generator context for this convolution. If both input p.d.f.s support
00752   // internal generation, if it is safe to use them and if no observables other than the convolution
00753   // observable are requested for generation, use the specialized convolution generator context
00754   // which implements a smearing strategy in the convolution observable. If not return the
00755   // regular accept/reject generator context
00756 
00757   RooArgSet vars2(vars) ;
00758   vars2.remove(_x.arg(),kTRUE,kTRUE) ;
00759   Int_t numAddDep = vars2.getSize() ;
00760 
00761   RooArgSet dummy ;
00762   Bool_t pdfCanDir = (((RooAbsPdf&)_pdf1.arg()).getGenerator(_x.arg(),dummy) != 0 && \
00763                       ((RooAbsPdf&)_pdf1.arg()).isDirectGenSafe(_x.arg())) ;
00764   Bool_t resCanDir = (((RooAbsPdf&)_pdf2.arg()).getGenerator(_x.arg(),dummy) !=0  && 
00765                       ((RooAbsPdf&)_pdf2.arg()).isDirectGenSafe(_x.arg())) ;
00766 
00767   if (pdfCanDir) {
00768     cxcoutI(Generation) << "RooFFTConvPdf::genContext() input p.d.f " << _pdf1.arg().GetName() 
00769                         << " has internal generator that is safe to use in current context" << endl ;
00770   }
00771   if (resCanDir) {
00772     cxcoutI(Generation) << "RooFFTConvPdf::genContext() input p.d.f. " << _pdf2.arg().GetName() 
00773                         << " has internal generator that is safe to use in current context" << endl ;
00774   }
00775   if (numAddDep>0) {
00776     cxcoutI(Generation) << "RooFFTConvPdf::genContext() generation requested for observables other than the convolution observable " << _x.arg().GetName() << endl ;
00777   }  
00778 
00779   
00780   if (numAddDep>0 || !pdfCanDir || !resCanDir) {
00781     // Any resolution model with more dependents than the convolution variable
00782     // or pdf or resmodel do not support direct generation
00783     cxcoutI(Generation) << "RooFFTConvPdf::genContext() selecting accept/reject generator context because one or both of the input "
00784                         << "p.d.f.s cannot use internal generator and/or " 
00785                         << "observables other than the convolution variable are requested for generation" << endl ;
00786     return new RooGenContext(*this,vars,prototype,auxProto,verbose) ;
00787   } 
00788   
00789   // Any other resolution model: use specialized generator context
00790   cxcoutI(Generation) << "RooFFTConvPdf::genContext() selecting specialized convolution generator context as both input "
00791                       << "p.d.fs are safe for internal generator and only "
00792                       << "the convolution observables is requested for generation" << endl ;
00793   return new RooConvGenContext(*this,vars,prototype,auxProto,verbose) ;
00794 }
00795 
00796 
00797 
00798 //_____________________________________________________________________________
00799 void RooFFTConvPdf::setBufferFraction(Double_t frac) 
00800 {
00801   // Change the size of the buffer on either side of the observable range to frac times the
00802   // size of the range of the convolution observable
00803 
00804   if (frac<0) {
00805     coutE(InputArguments) << "RooFFTConvPdf::setBufferFraction(" << GetName() << ") fraction should be greater than or equal to zero" << endl ;
00806     return ;
00807   }
00808   _bufFrac = frac ;
00809 
00810   // Sterilize the cache as certain partial results depend on buffer fraction
00811   _cacheMgr.sterilize() ;
00812 }
00813 
00814 
00815 //_____________________________________________________________________________
00816 void RooFFTConvPdf::setBufferStrategy(BufStrat bs) 
00817 {
00818   // Change strategy to fill the overflow buffer on either side of the convolution observable range.
00819   //
00820   // 'Extend' means is that the input p.d.f convolution observable range is widened to include the buffer range
00821   // 'Flat' means that the buffer is filled with the p.d.f. value at the boundary of the observable range
00822   // 'Mirror' means that the buffer is filled with a ,irror image of the p.d.f. around the convolution observable boundary 
00823   //
00824   // The default strategy is extend. If one of the input p.d.f.s is a RooAddPdf, it is configured so that the interpretation
00825   // range of the fraction coefficients is kept at the nominal convolutions observable range (instead of interpreting coefficients
00826   // in the widened range including the buffer)
00827 
00828   _bufStrat = bs ;
00829 }
00830 
00831 
00832 
00833 //_____________________________________________________________________________
00834 void RooFFTConvPdf::printMetaArgs(ostream& os) const 
00835 {
00836   // Customized printing of arguments of a RooNumConvPdf to more intuitively reflect the contents of the
00837   // product operator construction
00838 
00839   os << _pdf1.arg().GetName() << "(" << _x.arg().GetName() << ") (*) " << _pdf2.arg().GetName() << "(" << _x.arg().GetName() << ") " ;
00840 }
00841 
00842 
00843 
00844 //_____________________________________________________________________________
00845 void RooFFTConvPdf::calcParams() 
00846 {
00847   // (Re)calculate effective parameters of this p.d.f.
00848 
00849   RooArgSet* params1 = _pdf1.arg().getParameters(_x.arg()) ;
00850   RooArgSet* params2 = _pdf2.arg().getParameters(_x.arg()) ;
00851   _params.removeAll() ;
00852   _params.add(*params1) ;
00853   _params.add(*params2,kTRUE) ;
00854   delete params1 ;
00855   delete params2 ;
00856 }
00857 
00858 
00859 
00860 //_____________________________________________________________________________
00861 Bool_t RooFFTConvPdf::redirectServersHook(const RooAbsCollection& /*newServerList*/, Bool_t /*mustReplaceAll*/, Bool_t /*nameChange*/, Bool_t /*isRecursive*/) 
00862 {
00863   //calcParams() ;
00864   return kFALSE ;
00865 }

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