예제 #1
0
RooFitResult * safeFit(RooAbsPdf * pdf, RooDataSet * data, Str2VarMap p, ISVALIDF_PTR isValid, string opt = "", int nfree = -1, RooArgSet * cons = NULL, RooAbsReal * nll = NULL)
{
	RooFitResult * res = NULL;

	RooRealVar cosThetaL("cosThetaL","cosThetaL",0.,-1.,1.);
	RooRealVar cosThetaB("cosThetaB","cosThetaB",0.,-1.,1.);
	
	RooArgSet obs(cosThetaL,cosThetaB);
	
	//if(opt.find("-scan")==string::npos) res = pdf->fitTo(*data,PrintLevel(-1),Save(),Extended(true)); 
	if(p.size()==1 && p.find("afb") != p.end())     p["fL"]  = GetParam(pdf,"fL");
	else if(p.size()==1 && p.find("fL") != p.end()) p["afb"] = GetParam(pdf,"afb");
	RooArgSet * nuisances = NULL;
	/*
	bool afb_iscost = false, fL_iscost = false, afbB_iscost = false;
	if (p.find("afb") != p.end())  { afb_iscost  = ((RooRealVar*)p["afb"])->getAttribute("Constant");  ((RooRealVar*)p["afb"])->setConstant();  }
	if (p.find("fL") != p.end())   { fL_iscost   = ((RooRealVar*)p["fL"])->getAttribute("Constant");   ((RooRealVar*)p["fL"])->setConstant();   }
	if (p.find("afbB") != p.end()) { afbB_iscost = ((RooRealVar*)p["afbB"])->getAttribute("Constant"); ((RooRealVar*)p["afbB"])->setConstant(); }
	RooArgSet * nuisances = copyFreePars(pdf,obs);
	if (p.find("afb") != p.end())  ((RooRealVar*)p["afb"])->setConstant(afb_iscost);
	if (p.find("afbB") != p.end()) ((RooRealVar*)p["afbB"])->setConstant(afbB_iscost);
	if (p.find("fL") != p.end())   ((RooRealVar*)p["fL"])->setConstant(fL_iscost);
	*/
	int np = 20;
	if((!res || res->covQual()!=3 || res->edm() > 0.1) && opt.find("-noscan")==string::npos)
	{
		if(!nll) nll = pdf->createNLL(*data);

		vector < double > mins, maxs, r;

		Str2VarMap::iterator iter; int pp = 0;
		for (iter = p.begin(); iter != p.end(); iter++) 
		{
			RooRealVar * curp = (RooRealVar *)iter->second;
			maxs.push_back(curp->getMax());
			mins.push_back(curp->getMin());
			r.push_back((maxs.back() - mins.back())/(double)np);
			pp++;
		}
		
		findMin(pdf,data,nll,p,mins,maxs,np,isValid,nfree,opt+"-nofit",cons,nuisances);
		
		double prec = 1e6;
		while (prec > 0.001)
		{
			double maxr = 0;
			maxs.clear(); mins.clear(); pp=0;
			for (iter = p.begin(); iter != p.end(); iter++) 
			{
				RooRealVar * curp = (RooRealVar *)iter->second;
				if((curp->getVal() + r[pp]) < curp->getMax()) maxs.push_back(curp->getVal() + r[pp]);
				else maxs.push_back(curp->getMax());
				if((curp->getVal() - r[pp]) > curp->getMin()) mins.push_back(curp->getVal() - r[pp]);
				else mins.push_back(curp->getMin());
				r[pp] = (maxs.back() - mins.back())/(double)np;
				if(r[pp] > maxr) maxr = r[pp];
				pp++;
			}
		
			prec = maxr;
			res = findMin(pdf,data,nll,p,mins,maxs,np,isValid,nfree,opt,cons,nuisances);
		}
		
		//if(!mynll) delete nll;
	}

	return res;
}
예제 #2
0
// internal routine to run the inverter
HypoTestInverterResult *
RooStats::HypoTestInvTool::RunInverter(RooWorkspace * w,
                                       const char * modelSBName, const char * modelBName,
                                       const char * dataName, int type,  int testStatType,
                                       bool useCLs, int npoints, double poimin, double poimax,
                                       int ntoys,
                                       bool useNumberCounting,
                                       const char * nuisPriorName ) {

    std::cout << "Running HypoTestInverter on the workspace " << w->GetName() << std::endl;

    w->Print();


    RooAbsData * data = w->data(dataName);
    if (!data) {
        Error("StandardHypoTestDemo","Not existing data %s",dataName);
        return 0;
    }
    else
        std::cout << "Using data set " << dataName << std::endl;

    if (mUseVectorStore) {
        RooAbsData::setDefaultStorageType(RooAbsData::Vector);
        data->convertToVectorStore() ;
    }


    // get models from WS
    // get the modelConfig out of the file
    ModelConfig* bModel = (ModelConfig*) w->obj(modelBName);
    ModelConfig* sbModel = (ModelConfig*) w->obj(modelSBName);

    if (!sbModel) {
        Error("StandardHypoTestDemo","Not existing ModelConfig %s",modelSBName);
        return 0;
    }
    // check the model
    if (!sbModel->GetPdf()) {
        Error("StandardHypoTestDemo","Model %s has no pdf ",modelSBName);
        return 0;
    }
    if (!sbModel->GetParametersOfInterest()) {
        Error("StandardHypoTestDemo","Model %s has no poi ",modelSBName);
        return 0;
    }
    if (!sbModel->GetObservables()) {
        Error("StandardHypoTestInvDemo","Model %s has no observables ",modelSBName);
        return 0;
    }
    if (!sbModel->GetSnapshot() ) {
        Info("StandardHypoTestInvDemo","Model %s has no snapshot  - make one using model poi",modelSBName);
        sbModel->SetSnapshot( *sbModel->GetParametersOfInterest() );
    }

    // case of no systematics
    // remove nuisance parameters from model
    if (noSystematics) {
        const RooArgSet * nuisPar = sbModel->GetNuisanceParameters();
        if (nuisPar && nuisPar->getSize() > 0) {
            std::cout << "StandardHypoTestInvDemo" << "  -  Switch off all systematics by setting them constant to their initial values" << std::endl;
            RooStats::SetAllConstant(*nuisPar);
        }
        if (bModel) {
            const RooArgSet * bnuisPar = bModel->GetNuisanceParameters();
            if (bnuisPar)
                RooStats::SetAllConstant(*bnuisPar);
        }
    }

    if (!bModel || bModel == sbModel) {
        Info("StandardHypoTestInvDemo","The background model %s does not exist",modelBName);
        Info("StandardHypoTestInvDemo","Copy it from ModelConfig %s and set POI to zero",modelSBName);
        bModel = (ModelConfig*) sbModel->Clone();
        bModel->SetName(TString(modelSBName)+TString("_with_poi_0"));
        RooRealVar * var = dynamic_cast<RooRealVar*>(bModel->GetParametersOfInterest()->first());
        if (!var) return 0;
        double oldval = var->getVal();
        var->setVal(0);
        bModel->SetSnapshot( RooArgSet(*var)  );
        var->setVal(oldval);
    }
    else {
        if (!bModel->GetSnapshot() ) {
            Info("StandardHypoTestInvDemo","Model %s has no snapshot  - make one using model poi and 0 values ",modelBName);
            RooRealVar * var = dynamic_cast<RooRealVar*>(bModel->GetParametersOfInterest()->first());
            if (var) {
                double oldval = var->getVal();
                var->setVal(0);
                bModel->SetSnapshot( RooArgSet(*var)  );
                var->setVal(oldval);
            }
            else {
                Error("StandardHypoTestInvDemo","Model %s has no valid poi",modelBName);
                return 0;
            }
        }
    }

    // check model  has global observables when there are nuisance pdf
    // for the hybrid case the globobs are not needed
    if (type != 1 ) {
        bool hasNuisParam = (sbModel->GetNuisanceParameters() && sbModel->GetNuisanceParameters()->getSize() > 0);
        bool hasGlobalObs = (sbModel->GetGlobalObservables() && sbModel->GetGlobalObservables()->getSize() > 0);
        if (hasNuisParam && !hasGlobalObs ) {
            // try to see if model has nuisance parameters first
            RooAbsPdf * constrPdf = RooStats::MakeNuisancePdf(*sbModel,"nuisanceConstraintPdf_sbmodel");
            if (constrPdf) {
                Warning("StandardHypoTestInvDemo","Model %s has nuisance parameters but no global observables associated",sbModel->GetName());
                Warning("StandardHypoTestInvDemo","\tThe effect of the nuisance parameters will not be treated correctly ");
            }
        }
    }

    // save all initial parameters of the model including the global observables
    RooArgSet initialParameters;
    RooArgSet * allParams = sbModel->GetPdf()->getParameters(*data);
    allParams->snapshot(initialParameters);
    delete allParams;

    // run first a data fit

    const RooArgSet * poiSet = sbModel->GetParametersOfInterest();
    RooRealVar *poi = (RooRealVar*)poiSet->first();

    std::cout << "StandardHypoTestInvDemo : POI initial value:   " << poi->GetName() << " = " << poi->getVal()   << std::endl;

    // fit the data first (need to use constraint )
    TStopwatch tw;

    bool doFit = initialFit;
    if (testStatType == 0 && initialFit == -1) doFit = false;  // case of LEP test statistic
    if (type == 3  && initialFit == -1) doFit = false;         // case of Asymptoticcalculator with nominal Asimov
    double poihat = 0;

    if (minimizerType.size()==0) minimizerType = ROOT::Math::MinimizerOptions::DefaultMinimizerType();
    else
        ROOT::Math::MinimizerOptions::SetDefaultMinimizer(minimizerType.c_str());

    Info("StandardHypoTestInvDemo","Using %s as minimizer for computing the test statistic",
         ROOT::Math::MinimizerOptions::DefaultMinimizerType().c_str() );

    if (doFit)  {

        // do the fit : By doing a fit the POI snapshot (for S+B)  is set to the fit value
        // and the nuisance parameters nominal values will be set to the fit value.
        // This is relevant when using LEP test statistics

        Info( "StandardHypoTestInvDemo"," Doing a first fit to the observed data ");
        RooArgSet constrainParams;
        if (sbModel->GetNuisanceParameters() ) constrainParams.add(*sbModel->GetNuisanceParameters());
        RooStats::RemoveConstantParameters(&constrainParams);
        tw.Start();
        RooFitResult * fitres = sbModel->GetPdf()->fitTo(*data,InitialHesse(false), Hesse(false),
                                Minimizer(minimizerType.c_str(),"Migrad"), Strategy(0), PrintLevel(mPrintLevel), Constrain(constrainParams), Save(true) );
        if (fitres->status() != 0) {
            Warning("StandardHypoTestInvDemo","Fit to the model failed - try with strategy 1 and perform first an Hesse computation");
            fitres = sbModel->GetPdf()->fitTo(*data,InitialHesse(true), Hesse(false),Minimizer(minimizerType.c_str(),"Migrad"), Strategy(1), PrintLevel(mPrintLevel+1), Constrain(constrainParams), Save(true) );
        }
        if (fitres->status() != 0)
            Warning("StandardHypoTestInvDemo"," Fit still failed - continue anyway.....");


        poihat  = poi->getVal();
        std::cout << "StandardHypoTestInvDemo - Best Fit value : " << poi->GetName() << " = "
                  << poihat << " +/- " << poi->getError() << std::endl;
        std::cout << "Time for fitting : ";
        tw.Print();

        //save best fit value in the poi snapshot
        sbModel->SetSnapshot(*sbModel->GetParametersOfInterest());
        std::cout << "StandardHypoTestInvo: snapshot of S+B Model " << sbModel->GetName()
                  << " is set to the best fit value" << std::endl;

    }

    // print a message in case of LEP test statistics because it affects result by doing or not doing a fit
    if (testStatType == 0) {
        if (!doFit)
            Info("StandardHypoTestInvDemo","Using LEP test statistic - an initial fit is not done and the TS will use the nuisances at the model value");
        else
            Info("StandardHypoTestInvDemo","Using LEP test statistic - an initial fit has been done and the TS will use the nuisances at the best fit value");
    }


    // build test statistics and hypotest calculators for running the inverter

    SimpleLikelihoodRatioTestStat slrts(*sbModel->GetPdf(),*bModel->GetPdf());

    // null parameters must includes snapshot of poi plus the nuisance values
    RooArgSet nullParams(*sbModel->GetSnapshot());
    if (sbModel->GetNuisanceParameters()) nullParams.add(*sbModel->GetNuisanceParameters());
    if (sbModel->GetSnapshot()) slrts.SetNullParameters(nullParams);
    RooArgSet altParams(*bModel->GetSnapshot());
    if (bModel->GetNuisanceParameters()) altParams.add(*bModel->GetNuisanceParameters());
    if (bModel->GetSnapshot()) slrts.SetAltParameters(altParams);

    // ratio of profile likelihood - need to pass snapshot for the alt
    RatioOfProfiledLikelihoodsTestStat
    ropl(*sbModel->GetPdf(), *bModel->GetPdf(), bModel->GetSnapshot());
    ropl.SetSubtractMLE(false);
    if (testStatType == 11) ropl.SetSubtractMLE(true);
    ropl.SetPrintLevel(mPrintLevel);
    ropl.SetMinimizer(minimizerType.c_str());

    ProfileLikelihoodTestStat profll(*sbModel->GetPdf());
    if (testStatType == 3) profll.SetOneSided(true);
    if (testStatType == 4) profll.SetSigned(true);
    profll.SetMinimizer(minimizerType.c_str());
    profll.SetPrintLevel(mPrintLevel);

    profll.SetReuseNLL(mOptimize);
    slrts.SetReuseNLL(mOptimize);
    ropl.SetReuseNLL(mOptimize);

    if (mOptimize) {
        profll.SetStrategy(0);
        ropl.SetStrategy(0);
        ROOT::Math::MinimizerOptions::SetDefaultStrategy(0);
    }

    if (mMaxPoi > 0) poi->setMax(mMaxPoi);  // increase limit

    MaxLikelihoodEstimateTestStat maxll(*sbModel->GetPdf(),*poi);
    NumEventsTestStat nevtts;

    AsymptoticCalculator::SetPrintLevel(mPrintLevel);

    // create the HypoTest calculator class
    HypoTestCalculatorGeneric *  hc = 0;
    if (type == 0) hc = new FrequentistCalculator(*data, *bModel, *sbModel);
    else if (type == 1) hc = new HybridCalculator(*data, *bModel, *sbModel);
    // else if (type == 2 ) hc = new AsymptoticCalculator(*data, *bModel, *sbModel, false, mAsimovBins);
    // else if (type == 3 ) hc = new AsymptoticCalculator(*data, *bModel, *sbModel, true, mAsimovBins);  // for using Asimov data generated with nominal values
    else if (type == 2 ) hc = new AsymptoticCalculator(*data, *bModel, *sbModel, false );
    else if (type == 3 ) hc = new AsymptoticCalculator(*data, *bModel, *sbModel, true );  // for using Asimov data generated with nominal values
    else {
        Error("StandardHypoTestInvDemo","Invalid - calculator type = %d supported values are only :\n\t\t\t 0 (Frequentist) , 1 (Hybrid) , 2 (Asymptotic) ",type);
        return 0;
    }

    // set the test statistic
    TestStatistic * testStat = 0;
    if (testStatType == 0) testStat = &slrts;
    if (testStatType == 1 || testStatType == 11) testStat = &ropl;
    if (testStatType == 2 || testStatType == 3 || testStatType == 4) testStat = &profll;
    if (testStatType == 5) testStat = &maxll;
    if (testStatType == 6) testStat = &nevtts;

    if (testStat == 0) {
        Error("StandardHypoTestInvDemo","Invalid - test statistic type = %d supported values are only :\n\t\t\t 0 (SLR) , 1 (Tevatron) , 2 (PLR), 3 (PLR1), 4(MLE)",testStatType);
        return 0;
    }


    ToyMCSampler *toymcs = (ToyMCSampler*)hc->GetTestStatSampler();
    if (toymcs && (type == 0 || type == 1) ) {
        // look if pdf is number counting or extended
        if (sbModel->GetPdf()->canBeExtended() ) {
            if (useNumberCounting)   Warning("StandardHypoTestInvDemo","Pdf is extended: but number counting flag is set: ignore it ");
        }
        else {
            // for not extended pdf
            if (!useNumberCounting  )  {
                int nEvents = data->numEntries();
                Info("StandardHypoTestInvDemo","Pdf is not extended: number of events to generate taken  from observed data set is %d",nEvents);
                toymcs->SetNEventsPerToy(nEvents);
            }
            else {
                Info("StandardHypoTestInvDemo","using a number counting pdf");
                toymcs->SetNEventsPerToy(1);
            }
        }

        toymcs->SetTestStatistic(testStat);

        if (data->isWeighted() && !mGenerateBinned) {
            Info("StandardHypoTestInvDemo","Data set is weighted, nentries = %d and sum of weights = %8.1f but toy generation is unbinned - it would be faster to set mGenerateBinned to true\n",data->numEntries(), data->sumEntries());
        }
        toymcs->SetGenerateBinned(mGenerateBinned);

        toymcs->SetUseMultiGen(mOptimize);

        if (mGenerateBinned &&  sbModel->GetObservables()->getSize() > 2) {
            Warning("StandardHypoTestInvDemo","generate binned is activated but the number of ovservable is %d. Too much memory could be needed for allocating all the bins",sbModel->GetObservables()->getSize() );
        }

        // set the random seed if needed
        if (mRandomSeed >= 0) RooRandom::randomGenerator()->SetSeed(mRandomSeed);

    }

    // specify if need to re-use same toys
    if (reuseAltToys) {
        hc->UseSameAltToys();
    }

    if (type == 1) {
        HybridCalculator *hhc = dynamic_cast<HybridCalculator*> (hc);
        assert(hhc);

        hhc->SetToys(ntoys,ntoys/mNToysRatio); // can use less ntoys for b hypothesis

        // remove global observables from ModelConfig (this is probably not needed anymore in 5.32)
        bModel->SetGlobalObservables(RooArgSet() );
        sbModel->SetGlobalObservables(RooArgSet() );


        // check for nuisance prior pdf in case of nuisance parameters
        if (bModel->GetNuisanceParameters() || sbModel->GetNuisanceParameters() ) {

            // fix for using multigen (does not work in this case)
            toymcs->SetUseMultiGen(false);
            ToyMCSampler::SetAlwaysUseMultiGen(false);

            RooAbsPdf * nuisPdf = 0;
            if (nuisPriorName) nuisPdf = w->pdf(nuisPriorName);
            // use prior defined first in bModel (then in SbModel)
            if (!nuisPdf)  {
                Info("StandardHypoTestInvDemo","No nuisance pdf given for the HybridCalculator - try to deduce  pdf from the model");
                if (bModel->GetPdf() && bModel->GetObservables() )
                    nuisPdf = RooStats::MakeNuisancePdf(*bModel,"nuisancePdf_bmodel");
                else
                    nuisPdf = RooStats::MakeNuisancePdf(*sbModel,"nuisancePdf_sbmodel");
            }
            if (!nuisPdf ) {
                if (bModel->GetPriorPdf())  {
                    nuisPdf = bModel->GetPriorPdf();
                    Info("StandardHypoTestInvDemo","No nuisance pdf given - try to use %s that is defined as a prior pdf in the B model",nuisPdf->GetName());
                }
                else {
                    Error("StandardHypoTestInvDemo","Cannnot run Hybrid calculator because no prior on the nuisance parameter is specified or can be derived");
                    return 0;
                }
            }
            assert(nuisPdf);
            Info("StandardHypoTestInvDemo","Using as nuisance Pdf ... " );
            nuisPdf->Print();

            const RooArgSet * nuisParams = (bModel->GetNuisanceParameters() ) ? bModel->GetNuisanceParameters() : sbModel->GetNuisanceParameters();
            RooArgSet * np = nuisPdf->getObservables(*nuisParams);
            if (np->getSize() == 0) {
                Warning("StandardHypoTestInvDemo","Prior nuisance does not depend on nuisance parameters. They will be smeared in their full range");
            }
            delete np;

            hhc->ForcePriorNuisanceAlt(*nuisPdf);
            hhc->ForcePriorNuisanceNull(*nuisPdf);


        }
    }
    else if (type == 2 || type == 3) {
        if (testStatType == 3) ((AsymptoticCalculator*) hc)->SetOneSided(true);
        if (testStatType != 2 && testStatType != 3)
            Warning("StandardHypoTestInvDemo","Only the PL test statistic can be used with AsymptoticCalculator - use by default a two-sided PL");
    }
    else if (type == 0 || type == 1)
        ((FrequentistCalculator*) hc)->SetToys(ntoys,ntoys/mNToysRatio);


    // Get the result
    RooMsgService::instance().getStream(1).removeTopic(RooFit::NumIntegration);



    HypoTestInverter calc(*hc);
    calc.SetConfidenceLevel(confidenceLevel);


    calc.UseCLs(useCLs);
    calc.SetVerbose(true);

    // can speed up using proof-lite
    if (mUseProof && mNWorkers > 1) {
        ProofConfig pc(*w, mNWorkers, "", kFALSE);
        toymcs->SetProofConfig(&pc);    // enable proof
    }


    if (npoints > 0) {
        if (poimin > poimax) {
            // if no min/max given scan between MLE and +4 sigma
            poimin = int(poihat);
            poimax = int(poihat +  4 * poi->getError());
        }
        std::cout << "Doing a fixed scan  in interval : " << poimin << " , " << poimax << std::endl;
        calc.SetFixedScan(npoints,poimin,poimax);
    }
    else {
        //poi->setMax(10*int( (poihat+ 10 *poi->getError() )/10 ) );
        std::cout << "Doing an  automatic scan  in interval : " << poi->getMin() << " , " << poi->getMax() << std::endl;
    }

    tw.Start();
    HypoTestInverterResult * r = calc.GetInterval();
    std::cout << "Time to perform limit scan \n";
    tw.Print();

    if (mRebuild) {

        std::cout << "\n***************************************************************\n";
        std::cout << "Rebuild the upper limit distribution by re-generating new set of pseudo-experiment and re-compute for each of them a new upper limit\n\n";


        allParams = sbModel->GetPdf()->getParameters(*data);

        // define on which value of nuisance parameters to do the rebuild
        // default is best fit value for bmodel snapshot



        if (mRebuildParamValues != 0) {
            // set all parameters to their initial workspace values
            *allParams = initialParameters;
        }
        if (mRebuildParamValues == 0 || mRebuildParamValues == 1 ) {
            RooArgSet constrainParams;
            if (sbModel->GetNuisanceParameters() ) constrainParams.add(*sbModel->GetNuisanceParameters());
            RooStats::RemoveConstantParameters(&constrainParams);

            const RooArgSet * poiModel = sbModel->GetParametersOfInterest();
            bModel->LoadSnapshot();

            // do a profile using the B model snapshot
            if (mRebuildParamValues == 0 ) {

                RooStats::SetAllConstant(*poiModel,true);

                sbModel->GetPdf()->fitTo(*data,InitialHesse(false), Hesse(false),
                                         Minimizer(minimizerType.c_str(),"Migrad"), Strategy(0), PrintLevel(mPrintLevel), Constrain(constrainParams) );


                std::cout << "rebuild using fitted parameter value for B-model snapshot" << std::endl;
                constrainParams.Print("v");

                RooStats::SetAllConstant(*poiModel,false);
            }
        }
        std::cout << "StandardHypoTestInvDemo: Initial parameters used for rebuilding: ";
        RooStats::PrintListContent(*allParams, std::cout);
        delete allParams;

        calc.SetCloseProof(1);
        tw.Start();
        SamplingDistribution * limDist = calc.GetUpperLimitDistribution(true,mNToyToRebuild);
        std::cout << "Time to rebuild distributions " << std::endl;
        tw.Print();

        if (limDist) {
            std::cout << "Expected limits after rebuild distribution " << std::endl;
            std::cout << "expected upper limit  (median of limit distribution) " << limDist->InverseCDF(0.5) << std::endl;
            std::cout << "expected -1 sig limit (0.16% quantile of limit dist) " << limDist->InverseCDF(ROOT::Math::normal_cdf(-1)) << std::endl;
            std::cout << "expected +1 sig limit (0.84% quantile of limit dist) " << limDist->InverseCDF(ROOT::Math::normal_cdf(1)) << std::endl;
            std::cout << "expected -2 sig limit (.025% quantile of limit dist) " << limDist->InverseCDF(ROOT::Math::normal_cdf(-2)) << std::endl;
            std::cout << "expected +2 sig limit (.975% quantile of limit dist) " << limDist->InverseCDF(ROOT::Math::normal_cdf(2)) << std::endl;

            // Plot the upper limit distribution
            SamplingDistPlot limPlot( (mNToyToRebuild < 200) ? 50 : 100);
            limPlot.AddSamplingDistribution(limDist);
            limPlot.GetTH1F()->SetStats(true); // display statistics
            limPlot.SetLineColor(kBlue);
            new TCanvas("limPlot","Upper Limit Distribution");
            limPlot.Draw();

            /// save result in a file
            limDist->SetName("RULDist");
            TFile * fileOut = new TFile("RULDist.root","RECREATE");
            limDist->Write();
            fileOut->Close();


            //update r to a new updated result object containing the rebuilt expected p-values distributions
            // (it will not recompute the expected limit)
            if (r) delete r;  // need to delete previous object since GetInterval will return a cloned copy
            r = calc.GetInterval();

        }
        else
            std::cout << "ERROR : failed to re-build distributions " << std::endl;
    }

    return r;
}