diff --git a/roofit/histfactory/src/ParamHistFunc.cxx b/roofit/histfactory/src/ParamHistFunc.cxx index e492cf58982848b593ad75de04f445224dd7f811..19ec94514dc2693438ca6a995078c7b93180035a 100644 --- a/roofit/histfactory/src/ParamHistFunc.cxx +++ b/roofit/histfactory/src/ParamHistFunc.cxx @@ -124,8 +124,8 @@ ParamHistFunc::ParamHistFunc(const char* name, const char* title, //////////////////////////////////////////////////////////////////////////////// -/// Create a function which returns binewise-values -/// This class contains N RooRealVar's, one for each +/// Create a function which returns bin-wise values. +/// This class contains N RooRealVars, one for each /// bin from the given RooRealVar. /// /// The value of the function in the ith bin is @@ -133,8 +133,7 @@ ParamHistFunc::ParamHistFunc(const char* name, const char* title, /// /// F(i) = gamma_i * nominal(i) /// -/// Where the nominal values are simply fixed -/// numbers (default = 1.0 for all i) +/// Where the nominal values are taken from the histogram. ParamHistFunc::ParamHistFunc(const char* name, const char* title, const RooArgList& vars, const RooArgList& paramSet, const TH1* Hist ) : @@ -214,7 +213,7 @@ ParamHistFunc::~ParamHistFunc() //////////////////////////////////////////////////////////////////////////////// /// Get the index of the gamma parameter associated -/// with the current bin +/// with the current bin. /// This number is the "RooDataSet" style index /// and it must be because it uses the RooDataSet method directly /// This is intended to be fed into the getParameter(Int_t) method: @@ -460,9 +459,7 @@ RooArgList ParamHistFunc::createParamSet(RooWorkspace& w, const std::string& Pre RooArgList params = ParamHistFunc::createParamSet( w, Prefix, vars ); - RooFIter paramIter = params.fwdIterator() ; - RooAbsArg* comp ; - while((comp = (RooAbsArg*) paramIter.next())) { + for (auto comp : params) { RooRealVar* var = (RooRealVar*) comp; @@ -493,7 +490,7 @@ RooArgList ParamHistFunc::createParamSet(const std::string& Prefix, Int_t numBin if( gamma_max <= gamma_min ) { - std::cout << "Warming: gamma_min <= gamma_max: Using default values (0, 10)" << std::endl; + std::cout << "Warning: gamma_min <= gamma_max: Using default values (0, 10)" << std::endl; gamma_min = 0.0; gamma_max = 10.0; diff --git a/roofit/roofit/inc/RooHistConstraint.h b/roofit/roofit/inc/RooHistConstraint.h index c199bc68c36823bec5c65f6b821e777ee46bde68..b0ef2ede05e566ae83f2b73982a728311dfc602f 100644 --- a/roofit/roofit/inc/RooHistConstraint.h +++ b/roofit/roofit/inc/RooHistConstraint.h @@ -28,18 +28,15 @@ public: protected: - Double_t logSum(Int_t i) const ; - RooListProxy _gamma ; RooListProxy _nominal ; - RooListProxy _nominalErr ; Bool_t _relParam ; Double_t evaluate() const ; private: - ClassDef(RooHistConstraint,1) // Your description goes here... + ClassDef(RooHistConstraint, 2) }; #endif diff --git a/roofit/roofit/src/RooHistConstraint.cxx b/roofit/roofit/src/RooHistConstraint.cxx index 8d1e16123f18ea457dc56aaa79baed3db7dcbd7e..03f26eb4fc6b0989b58a05fcaeafd385774b6bb7 100644 --- a/roofit/roofit/src/RooHistConstraint.cxx +++ b/roofit/roofit/src/RooHistConstraint.cxx @@ -5,9 +5,16 @@ *****************************************************************************/ /** \class RooHistConstraint - \ingroup Roofit - -**/ + * \ingroup Roofit + * The RooHistConstraint implements constraint terms for a binned PDF with statistical uncertainties. + * Following the Barlow-Beeston method, it adds Poisson constraints for each bin that + * constrain the statistical uncertainty of the template histogram. + * + * It can therefore be used to estimate the Monte Carlo uncertainty of a fit. + * + * Check also the tutorial rf709_BarlowBeeston.C + * + */ #include "Riostream.h" @@ -24,12 +31,16 @@ using namespace std; ClassImp(RooHistConstraint); //////////////////////////////////////////////////////////////////////////////// - -RooHistConstraint::RooHistConstraint(const char *name, const char *title, const RooArgSet& phfSet, Int_t threshold) : +/// Create a new RooHistConstraint. +/// \param[in] name Name of the PDF. This is used to identify it in a likelihood model. +/// \param[in] title Title for plotting etc. +/// \param[in] phfSet Set of parametrised histogram functions (RooParamHistFunc). +/// \param[in] threshold Threshold (bin content) up to which statistcal uncertainties are taken into account. +RooHistConstraint::RooHistConstraint(const char *name, const char *title, + const RooArgSet& phfSet, Int_t threshold) : RooAbsPdf(name,title), _gamma("gamma","gamma",this), _nominal("nominal","nominal",this), - _nominalErr("nominalErr","nominalErr",this), _relParam(kTRUE) { // Implementing constraint on sum of RooParamHists @@ -45,28 +56,30 @@ RooHistConstraint::RooHistConstraint(const char *name, const char *title, const if (!phf) { coutE(InputArguments) << "RooHistConstraint::ctor(" << GetName() - << ") ERROR: input object must be a RooParamHistFunc" << endl ; - throw std::string("RoohistConstraint::ctor ERROR incongruent input arguments") ; + << ") ERROR: input object must be a RooParamHistFunc" << endl ; + throw std::string("RooHistConstraint::ctor ERROR incongruent input arguments") ; } // Now populate nominal with parameters RooArgSet allVars ; for (Int_t i=0 ; i<phf->_dh.numEntries() ; i++) { phf->_dh.get(i) ; - if (phf->_dh.weight()<threshold) { - const char* vname = Form("%s_nominal_bin_%i",GetName(),i) ; - RooRealVar* var = new RooRealVar(vname,vname,0,1000) ; - var->setVal(phf->_dh.weight()) ; - var->setConstant(kTRUE) ; - allVars.add(*var) ; - _nominal.add(*var) ; - RooRealVar* gam = (RooRealVar*) phf->_p.at(i) ; - if (var->getVal()>0) { - gam->setConstant(kFALSE) ; - } - _gamma.add(*gam) ; + if (phf->_dh.weight()<threshold && phf->_dh.weight() != 0.) { + const char* vname = Form("%s_nominal_bin_%i",GetName(),i) ; + RooRealVar* var = new RooRealVar(vname,vname,0,1.E30) ; + var->setVal(phf->_dh.weight()) ; + var->setConstant(true); + allVars.add(*var) ; + _nominal.add(*var) ; + + RooRealVar* gam = (RooRealVar*) phf->_p.at(i) ; + if (var->getVal()>0) { + gam->setConstant(false); + } + _gamma.add(*gam) ; } } + addOwnedComponents(allVars) ; return ; @@ -74,37 +87,37 @@ RooHistConstraint::RooHistConstraint(const char *name, const char *title, const - RooFIter phiter = phfSet.fwdIterator() ; - RooAbsArg* arg ; Int_t nbins(-1) ; vector<RooParamHistFunc*> phvec ; RooArgSet gammaSet ; string bin0_name ; - while((arg=phiter.next())) { + for (const auto arg : phfSet) { RooParamHistFunc* phfComp = dynamic_cast<RooParamHistFunc*>(arg) ; if (phfComp) { phvec.push_back(phfComp) ; if (nbins==-1) { - nbins = phfComp->_p.getSize() ; - bin0_name = phfComp->_p.at(0)->GetName() ; - gammaSet.add(phfComp->_p) ; + nbins = phfComp->_p.getSize() ; + bin0_name = phfComp->_p.at(0)->GetName() ; + gammaSet.add(phfComp->_p) ; } else { - if (phfComp->_p.getSize()!=nbins) { - coutE(InputArguments) << "RooHistConstraint::ctor(" << GetName() - << ") ERROR: incongruent input arguments: all input RooParamHistFuncs should have same #bins" << endl ; - throw std::string("RoohistConstraint::ctor ERROR incongruent input arguments") ; - } - if (bin0_name != phfComp->_p.at(0)->GetName()) { - coutE(InputArguments) << "RooHistConstraint::ctor(" << GetName() - << ") ERROR: incongruent input arguments: all input RooParamHistFuncs should have the same bin parameters" << endl ; - throw std::string("RoohistConstraint::ctor ERROR incongruent input arguments") ; - } + if (phfComp->_p.getSize()!=nbins) { + coutE(InputArguments) << "RooHistConstraint::ctor(" << GetName() + << ") ERROR: incongruent input arguments: all input RooParamHistFuncs should have same #bins" << endl ; + throw std::string("RooHistConstraint::ctor ERROR incongruent input arguments") ; + } + if (bin0_name != phfComp->_p.at(0)->GetName()) { + coutE(InputArguments) << "RooHistConstraint::ctor(" << GetName() + << ") ERROR: incongruent input arguments: all input RooParamHistFuncs should have the same bin parameters.\n" + << "Previously found " << bin0_name << ", now found " << phfComp->_p.at(0)->GetName() << ".\n" + << "Check that the right RooParamHistFuncs have been passed to this RooHistConstraint." << std::endl; + throw std::string("RooHistConstraint::ctor ERROR incongruent input arguments") ; + } } } else { coutW(InputArguments) << "RooHistConstraint::ctor(" << GetName() - << ") WARNING: ignoring input argument " << arg->GetName() << " which is not of type RooParamHistFunc" << endl; + << ") WARNING: ignoring input argument " << arg->GetName() << " which is not of type RooParamHistFunc" << endl; } } @@ -115,18 +128,18 @@ RooHistConstraint::RooHistConstraint(const char *name, const char *title, const for (Int_t i=0 ; i<nbins ; i++) { Double_t sumVal(0) ; - for (vector<RooParamHistFunc*>::iterator iter = phvec.begin() ; iter != phvec.end() ; ++iter) { - sumVal += (*iter)->getNominal(i) ; + for (const auto phfunc : phvec) { + sumVal += phfunc->getNominal(i); } - if (sumVal<threshold) { + if (sumVal<threshold && sumVal != 0.) { const char* vname = Form("%s_nominal_bin_%i",GetName(),i) ; RooRealVar* var = new RooRealVar(vname,vname,0,1000) ; Double_t sumVal2(0) ; for (vector<RooParamHistFunc*>::iterator iter = phvec.begin() ; iter != phvec.end() ; ++iter) { - sumVal2 += (*iter)->getNominal(i) ; + sumVal2 += (*iter)->getNominal(i) ; } var->setVal(sumVal2) ; var->setConstant(kTRUE) ; @@ -136,14 +149,14 @@ RooHistConstraint::RooHistConstraint(const char *name, const char *title, const Double_t sumErr2(0) ; for (vector<RooParamHistFunc*>::iterator iter = phvec.begin() ; iter != phvec.end() ; ++iter) { - sumErr2 += pow((*iter)->getNominalError(i),2) ; + sumErr2 += pow((*iter)->getNominalError(i),2) ; } vare->setVal(sqrt(sumErr2)) ; vare->setConstant(kTRUE) ; allVars.add(RooArgSet(*var,*vare)) ; _nominal.add(*var) ; - _nominalErr.add(*vare) ; + // _nominalErr.add(*vare) ; ((RooRealVar*)_gamma.at(i))->setConstant(kFALSE) ; @@ -158,7 +171,6 @@ RooHistConstraint::RooHistConstraint(const char *name, const char *title, const RooAbsPdf(other,name), _gamma("gamma",this,other._gamma), _nominal("nominal",this,other._nominal), - _nominalErr("nominalErr",this,other._nominalErr), _relParam(other._relParam) { } @@ -167,69 +179,46 @@ RooHistConstraint::RooHistConstraint(const char *name, const char *title, const Double_t RooHistConstraint::evaluate() const { - Double_t prod(1) ; - RooFIter niter = _nominal.fwdIterator() ; - RooFIter giter = _gamma.fwdIterator() ; - RooAbsReal *gam, *nom ; - while ((gam = (RooAbsReal*) giter.next())) { - nom = (RooAbsReal*) niter.next() ; - Double_t gamVal = gam->getVal() ; - if (_relParam) gamVal *= nom->getVal() ; - Double_t pois = TMath::Poisson(nom->getVal(),gamVal) ; - prod *= pois ; + double prod(1); + + for (unsigned int i=0; i < _nominal.size(); ++i) { + const auto& gamma = static_cast<const RooAbsReal&>(_gamma[i]); + const auto& nominal = static_cast<const RooAbsReal&>(_nominal[i]); + + double gamVal = gamma.getVal(); + if (_relParam) + gamVal *= nominal.getVal(); + + const double pois = TMath::Poisson(nominal.getVal(),gamVal); + prod *= pois; } - return prod ; + + return prod; } //////////////////////////////////////////////////////////////////////////////// Double_t RooHistConstraint::getLogVal(const RooArgSet* /*set*/) const { - Double_t sum(0) ; - RooFIter niter = _nominal.fwdIterator() ; - RooFIter giter = _gamma.fwdIterator() ; - RooAbsReal *gamv, *nomv ; - while ((gamv = (RooAbsReal*) giter.next())) { - nomv = (RooAbsReal*) niter.next() ; - Double_t gam = gamv->getVal() ; - Int_t nom = (Int_t) nomv->getVal() ; - if (_relParam) gam *= nom ; + double sum = 0.; + for (unsigned int i=0; i < _nominal.size(); ++i) { + const auto& gamma = static_cast<const RooAbsReal&>(_gamma[i]); + const auto& nominal = static_cast<const RooAbsReal&>(_nominal[i]); + double gam = gamma.getVal(); + Int_t nom = (Int_t) nominal.getVal(); + + if (_relParam) + gam *= nom; + if (gam>0) { - Double_t logPoisson = nom*log(gam) - gam - logSum(nom) ; + const double logPoisson = nom * log(gam) - gam - std::lgamma(nom+1); sum += logPoisson ; } else if (nom>0) { - cout << "ERROR gam=0 and nom>0" << endl ; + cerr << "ERROR in RooHistConstraint: gam=0 and nom>0" << endl ; } } + return sum ; } -//////////////////////////////////////////////////////////////////////////////// -Double_t RooHistConstraint::logSum(Int_t i) const -{ - static Double_t* _lut = 0 ; - if (!_lut) { - _lut = new Double_t[5000] ; - for (Int_t ii=0 ; ii<5000 ; ii++) _lut[ii] = 0 ; - - for (Int_t j=1 ; j<=5000 ; j++) { - Double_t logj = log((Double_t)j) ; - for (Int_t ii=j ; ii<=5000 ; ii++) { - _lut[ii-1] += logj ; - } - } - } - - if (i<5000) { - return _lut[i-1] ; - } else { - Double_t ret = _lut[4999] ; - cout << "logSum i=" << i << endl ; - for (Int_t j=5000 ; j<=i ; j++) { - ret += log((Double_t)j) ; - } - return ret ; - } - -} diff --git a/roofit/roofit/src/RooParamHistFunc.cxx b/roofit/roofit/src/RooParamHistFunc.cxx index f3dca0100ff96883b68c59656fd3f20e66c72251..407d30d2bb4036d40c2f914f2e3f986a05d29a59 100644 --- a/roofit/roofit/src/RooParamHistFunc.cxx +++ b/roofit/roofit/src/RooParamHistFunc.cxx @@ -5,9 +5,19 @@ *****************************************************************************/ /** \class RooParamHistFunc - \ingroup Roofit - -*/ + * \ingroup Roofit + * A histogram function that assigns scale parameters to every bin. Instead of the bare bin contents, + * it therefore yields: + * \f[ + * \gamma_{i} * \mathrm{bin}_i + * \f] + * + * The \f$ \gamma_i \f$ can therefore be used to parametrise statistical uncertainties of the histogram + * template. In conjuction with a constraint term, this can be used to implement the Barlow-Beeston method. + * The constraint can be implemented using RooHistConstraint. + * + * See also the tutorial rf709_BarlowBeeston.C + */ #include "Riostream.h" #include "RooParamHistFunc.h" @@ -37,6 +47,7 @@ RooParamHistFunc::RooParamHistFunc(const char *name, const char *title, RooDataH RooArgSet allVars ; for (Int_t i=0 ; i<_dh.numEntries() ; i++) { _dh.get(i) ; + const char* vname = Form("%s_gamma_bin_%i",GetName(),i) ; RooRealVar* var = new RooRealVar(vname,vname,0,1000) ; var->setVal(_relParam ? 1 : _dh.weight()) ; @@ -237,22 +248,19 @@ Double_t RooParamHistFunc::analyticalIntegralWN(Int_t code, const RooArgSet* /*n { R__ASSERT(code==1) ; - RooFIter iter = _p.fwdIterator() ; - RooAbsReal* p ; Double_t ret(0) ; Int_t i(0) ; - while((p=(RooAbsReal*)iter.next())) { + for (const auto param : _p) { + auto p = static_cast<const RooAbsReal*>(param); Double_t bin = p->getVal() ; if (_relParam) bin *= getNominal(i++) ; ret += bin ; } // WVE fix this!!! Assume uniform binning for now! - RooFIter xiter = _x.fwdIterator() ; - RooAbsArg* obs ; Double_t binV(1) ; - while((obs=xiter.next())) { - RooRealVar* xx = (RooRealVar*) obs ; + for (const auto obs : _x) { + auto xx = static_cast<const RooRealVar*>(obs); binV *= (xx->getMax()-xx->getMin())/xx->numBins() ; } diff --git a/tutorials/roofit/rf706_histpdf.C b/tutorials/roofit/rf706_histpdf.C index bf28d2725a3344c3e8fe024c241adf50780f7500..9c323ecfba3ddd33e230c2a6e1bcc2f0d98a5e3e 100644 --- a/tutorials/roofit/rf706_histpdf.C +++ b/tutorials/roofit/rf706_histpdf.C @@ -1,7 +1,7 @@ /// \file /// \ingroup tutorial_roofit /// \notebook -js -/// Speecial p.d.f.'s: histogram based p.d.f.s and functions +/// Special p.d.f.'s: histogram-based p.d.f.s and functions /// /// \macro_image /// \macro_output diff --git a/tutorials/roofit/rf709_BarlowBeeston.C b/tutorials/roofit/rf709_BarlowBeeston.C new file mode 100644 index 0000000000000000000000000000000000000000..61c1ce69f9accc8a9d992ba4b2de5c2b9438db7a --- /dev/null +++ b/tutorials/roofit/rf709_BarlowBeeston.C @@ -0,0 +1,233 @@ +/// \file rf709_BarlowBeeston.C +/// \ingroup tutorial_roofit +/// \notebook -js +/// Implementing the Barlow-Beeston method for taking into account the statistical uncertainty of a Monte-Carlo fit template. +/// \macro_image +/// \macro_output +/// \macro_code +/// \author 06/2019 - Stephan Hageboeck, CERN +/// Based on a demo by Wouter Verkerke + +#include "RooRealVar.h" +#include "RooGaussian.h" +#include "RooUniform.h" +#include "RooDataSet.h" +#include "RooDataHist.h" +#include "RooHistFunc.h" +#include "RooRealSumPdf.h" +#include "RooParamHistFunc.h" +#include "RooHistConstraint.h" +#include "RooProdPdf.h" +#include "RooPlot.h" + +#include "TCanvas.h" +#include "TPaveText.h" + +#include <iostream> +#include <memory> + +using namespace RooFit; + +void rf709_BarlowBeeston() +{ + // First, construct a likelihood model with a Gaussian signal on top of a uniform background + RooRealVar x("x", "x", -20, 20); + x.setBins(25); + + RooRealVar meanG("meanG", "meanG", 1, -10, 10); + RooRealVar sigG("sigG", "sigG", 1.5, -10, 10); + RooGaussian g("g", "Gauss", x, meanG, sigG); + RooUniform u("u", "Uniform", x); + + + // Generate the data to be fitted + std::unique_ptr<RooDataSet> sigData(g.generate(x, 50)); + std::unique_ptr<RooDataSet> bkgData(u.generate(x, 1000)); + + RooDataSet sumData("sumData", "Gauss + Uniform", x); + sumData.append(*sigData); + sumData.append(*bkgData); + + + // Make histogram templates for signal and background. + // Let's take a signal distribution with low statistics and a more accurate + // background distribution. + // Normally, these come from Monte Carlo simulations, but we will just generate them. + std::unique_ptr<RooDataHist> dh_sig( g.generateBinned(x, 50) ); + std::unique_ptr<RooDataHist> dh_bkg( u.generateBinned(x, 10000) ); + + + // ***** Case 0 - 'Rigid templates' ***** + + // Construct histogram shapes for signal and background + RooHistFunc p_h_sig("p_h_sig","p_h_sig",x,*dh_sig); + RooHistFunc p_h_bkg("p_h_bkg","p_h_bkg",x,*dh_bkg); + + // Construct scale factors for adding the two distributions + RooRealVar Asig0("Asig","Asig",1,0.01,5000); + RooRealVar Abkg0("Abkg","Abkg",1,0.01,5000); + + // Construct the sum model + RooRealSumPdf model0("model0","model0", + RooArgList(p_h_sig,p_h_bkg), + RooArgList(Asig0,Abkg0), + true); + + + + // ***** Case 1 - 'Barlow Beeston' ***** + + // Construct parameterized histogram shapes for signal and background + RooParamHistFunc p_ph_sig1("p_ph_sig","p_ph_sig",*dh_sig); + RooParamHistFunc p_ph_bkg1("p_ph_bkg","p_ph_bkg",*dh_bkg); + + RooRealVar Asig1("Asig","Asig",1,0.01,5000); + RooRealVar Abkg1("Abkg","Abkg",1,0.01,5000); + + // Construct the sum of these + RooRealSumPdf model_tmp("sp_ph", "sp_ph", + RooArgList(p_ph_sig1,p_ph_bkg1), + RooArgList(Asig1,Abkg1), + true); + + // Construct the subsidiary poisson measurements constraining the histogram parameters + // These ensure that the bin contents of the histograms are only allowed to vary within + // the statistical uncertainty of the Monte Carlo. + RooHistConstraint hc_sig("hc_sig","hc_sig",p_ph_sig1); + RooHistConstraint hc_bkg("hc_bkg","hc_bkg",p_ph_bkg1); + + // Construct the joint model with template PDFs and constraints + RooProdPdf model1("model1","model1",RooArgSet(hc_sig,hc_bkg),Conditional(model_tmp,x)); + + + + // ***** Case 2 - 'Barlow Beeston' light (one parameter per bin for all samples) ***** + + // Construct the histogram shapes, using the same parameters for signal and background + // This requires passing the first histogram to the second, so that their common parameters + // can be re-used. + // The first ParamHistFunc will create one parameter per bin, such as `p_ph_sig2_gamma_bin_0`. + // This allows bin 0 to fluctuate up and down. + // Then, the SAME parameters are connected to the background histogram, so the bins flucutate + // synchronously. This reduces the number of parameters. + RooParamHistFunc p_ph_sig2("p_ph_sig2", "p_ph_sig2", *dh_sig); + RooParamHistFunc p_ph_bkg2("p_ph_bkg2", "p_ph_bkg2", *dh_bkg, p_ph_sig2, true); + + RooRealVar Asig2("Asig","Asig",1,0.01,5000); + RooRealVar Abkg2("Abkg","Abkg",1,0.01,5000); + + // As before, construct the sum of signal2 and background2 + RooRealSumPdf model2_tmp("sp_ph","sp_ph", + RooArgList(p_ph_sig2,p_ph_bkg2), + RooArgList(Asig2,Abkg2), + true); + + // Construct the subsidiary poisson measurements constraining the statistical fluctuations + RooHistConstraint hc_sigbkg("hc_sigbkg","hc_sigbkg",RooArgSet(p_ph_sig2,p_ph_bkg2)); + + // Construct the joint model + RooProdPdf model2("model2","model2",hc_sigbkg, RooFit::Conditional(model2_tmp,x)); + + + + // ************ Fit all models to data and plot ********************* + + auto result0 = model0.fitTo(sumData, PrintLevel(0), Save()); + auto result1 = model1.fitTo(sumData, PrintLevel(0), Save()); + auto result2 = model2.fitTo(sumData, PrintLevel(0), Save()); + + + TCanvas* can = new TCanvas("can", "", 1500, 600); + can->Divide(3,1); + + TPaveText pt(-19.5, 1, -2, 25); + pt.SetFillStyle(0); + pt.SetBorderSize(0); + + + can->cd(1); + auto frame = x.frame(Title("No template uncertainties")); + // Plot data to enable automatic determination of model0 normalisation: + sumData.plotOn(frame); + model0.plotOn(frame, LineColor(kBlue), VisualizeError(*result0)); + // Plot data again to show it on top of model0 error bands: + sumData.plotOn(frame); + // Plot model components + model0.plotOn(frame, LineColor(kBlue)); + model0.plotOn(frame, Components(p_h_sig), LineColor(kAzure)); + model0.plotOn(frame, Components(p_h_bkg), LineColor(kRed)); + model0.paramOn(frame); + + sigData->plotOn(frame, MarkerColor(kBlue)); + frame->Draw(); + + for (auto text : { + "No template uncertainties", + "are taken into account.", + "This leads to low errors", + "for the parameters A, since", + "the only source of errors", + "are the data statistics."}) { + pt.AddText(text); + } + pt.DrawClone(); + + + can->cd(2); + frame = x.frame(Title("Barlow Beeston for Sig & Bkg separately")); + sumData.plotOn(frame); + model1.plotOn(frame, LineColor(kBlue), VisualizeError(*result1)); + // Plot data again to show it on top of error bands: + sumData.plotOn(frame); + model1.plotOn(frame, LineColor(kBlue)); + model1.plotOn(frame, Components(p_ph_sig1), LineColor(kAzure)); + model1.plotOn(frame, Components(p_ph_bkg1), LineColor(kRed)); + model1.paramOn(frame, Parameters(RooArgSet(Asig1, Abkg1))); + + sigData->plotOn(frame, MarkerColor(kBlue)); + frame->Draw(); + + pt.Clear(); + for (auto text : { + "With gamma parameters, the", + "signal & background templates", + "can adapt to the data.", + "Note how the blue signal", + "template changes its shape.", + "This leads to higher errors", + "of the scale parameters A."}) { + pt.AddText(text); + } + pt.DrawClone(); + + can->cd(3); + frame = x.frame(Title("Barlow Beeston light for (Sig+Bkg)")); + sumData.plotOn(frame); + model2.plotOn(frame, LineColor(kBlue), VisualizeError(*result2)); + // Plot data again to show it on top of model0 error bands: + sumData.plotOn(frame); + model2.plotOn(frame, LineColor(kBlue)); + model2.plotOn(frame, Components(p_ph_sig2), LineColor(kAzure)); + model2.plotOn(frame, Components(p_ph_bkg2), LineColor(kRed)); + model2.paramOn(frame, Parameters(RooArgSet(Asig2, Abkg2))); + + sigData->plotOn(frame, MarkerColor(kBlue)); + frame->Draw(); + + pt.Clear(); + for (auto text : { + "When signal and background", + "template share one gamma para-", + "meter per bin, they adapt less.", + "The errors of the A parameters", + "also shrink slightly."}) { + pt.AddText(text); + } + pt.DrawClone(); + + + std::cout << "Asig [normal ] = " << Asig0.getVal() << " +/- " << Asig0.getError() << std::endl; + std::cout << "Asig [BB ] = " << Asig1.getVal() << " +/- " << Asig1.getError() << std::endl; + std::cout << "Asig [BBlight] = " << Asig2.getVal() << " +/- " << Asig2.getError() << std::endl; + +}