#include "QFramework/TQNFCalculator.h"
#include "QFramework/TQStringUtils.h"
#include "QFramework/TQIterator.h"
#include "QFramework/TQUtils.h"
#include "TMath.h"
#include "TRandom.h"
#include "QFramework/TQHistogramUtils.h"
#include "QFramework/TQNFChainloader.h"
#include <limits>
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <fstream>
#include "TFractionFitter.h"
#include "QFramework/TQLibrary.h"
ClassImp(TQNFCalculator)
TQNFCalculator::TQNFCalculator(TQSampleFolder* f):
TQNFBase("TQNF"),
status(-999),
defaultDataPath("data"),
data(NULL),
mc(NULL),
epsilon(std::numeric_limits<double>::epsilon())
{
this->setSampleFolder(f);
}
TQNFCalculator::TQNFCalculator(TQSampleDataReader* rd):
TQNFBase("TQNF"),
status(-999),
defaultDataPath("data"),
data(NULL),
mc(NULL),
epsilon(std::numeric_limits<double>::epsilon())
{
this->setReader(rd);
}
TString TQNFCalculator::getDefaultDataPath(){
return this->defaultDataPath;
}
TQNFCalculator::~TQNFCalculator(){
this->finalize();
this->clear();
}
int TQNFCalculator::deployNF(const TString& name, const std::vector<TString>& startAtCutNames, const std::vector<TString>& stopAtCutNames, int overwrite, bool applyToStopCut){
if(!this->success()){
WARNclassargs(TQStringUtils::concat(3,name.Data(),TQStringUtils::concat(stopAtCutNames).Data(),TQStringUtils::getStringFromBool(overwrite).Data(),TQStringUtils::getStringFromBool(applyToStopCut).Data()),"cannot deploy NF, no results available, status is %d!",this->status);
return -1;
}
double nf = 0;
double sigma = 0;
int retval = 0;
this->getNFandUncertainty(name,nf,sigma);
TString mcpath = this->getMCPath(name);
std::vector<TString> writeScaleSchemes = this->getTagVString("writeScaleScheme");
if(writeScaleSchemes.size() < 1){
writeScaleSchemes.push_back(this->getTagStringDefault("writeScaleScheme",".default"));
}
std::vector<TString> targets = this->getTargetCuts(startAtCutNames,stopAtCutNames,applyToStopCut);
for (size_t c = 0; c<targets.size(); ++c) {
TString cutName = targets.at(c);
DEBUGclass("deploying NF for %s at %s (overwrite=%d)",name.Data(),cutName.Data(),overwrite);
TQSampleFolderIterator itr(this->fReader->getListOfSampleFolders(mcpath),true);
while(itr.hasNext()){
TQSampleFolder* s = itr.readNext();
if(!s) continue;
for(size_t k=0; k<writeScaleSchemes.size(); k++){
int n = s->setScaleFactor(writeScaleSchemes[k]+":"+cutName+(overwrite>0?"":"<<"), nf,sigma);
if(n == 0){
ERRORclass("unable to set scale factor for cut '%s' on path '%s' with scheme '%s'",cutName.Data(),s->getPath().Data(),writeScaleSchemes[k].Data());
} else {
DEBUG("Set scale factor for cut '%s' on path '%s' with scheme '%s' (value: %.2f +/- %.2f, overwrite: %s) ",cutName.Data(),s->getPath().Data(),writeScaleSchemes[k].Data(),nf,sigma, (overwrite>0?"true":"false") );
}
this->addNFPath(s->getPath(),cutName,writeScaleSchemes[k]);
retval += n;
}
}
if(this->infoFolder){
TQFolder * sfProcessList = this->infoFolder->getFolder(TString::Format(this->getTagStringDefault("nfListPattern",".cut.%s+").Data(),cutName.Data()));
TList* sflist = this->fReader->getListOfSampleFolders(mcpath);
TQSampleFolder * processSampleFolder = ( sflist && sflist->GetEntries() > 0 ) ? (TQSampleFolder*)(sflist->First()) : NULL;
if(sflist) delete sflist;
TString processTitle = name;
if (processSampleFolder)
processSampleFolder->getTagString(this->getTagStringDefault("processTitleKey","style.default.title"), processTitle);
sfProcessList->setTagString(TQFolder::makeValidIdentifier(processTitle),processTitle);
}
}
return retval;
}
int TQNFCalculator::deployResult(const std::vector<TString>& startAtCutNames, const std::vector<TString>& stopAtCutNames, int overwrite, bool applyToStopCut){
if(!this->success()){
WARNclassargs(TQStringUtils::concat(3,TQListUtils::makeCSV(startAtCutNames).Data(),TQStringUtils::concat(stopAtCutNames).Data(),TQStringUtils::getStringFromBool(overwrite).Data()),TQStringUtils::getStringFromBool(applyToStopCut).Data(),"cannot deploy NFs, no results available, status is %d!",this->status);
return -1;
}
int retval = 0;
for(size_t i=0; i<mcNames.size(); i++){
if(mcFixed[i]) continue;
retval += this->deployNF(mcNames[i],startAtCutNames, stopAtCutNames,overwrite,applyToStopCut);
}
return retval;
}
void TQNFCalculator::clear(){
this->dataPaths.clear();
this->cutNames.clear();
this->mcNames.clear();
this->mcPaths.clear();
this->mcFixed.clear();
this->NFs.clear();
this->NFuncertainties.clear();
this->nfBoundUpper.clear();
this->nfBoundLower.clear();
this->initialized = false;
this->status = -999;
if(data){
delete this->data;
this->data = NULL;
}
if(mc){
delete this->mc;
this->mc = NULL;
}
}
void TQNFCalculator::setDefaultDataPath(TString path){
this->defaultDataPath = path;
}
bool TQNFCalculator::addRegion(TString cutName, TString myDataPath){
if(this->initialized){
ERRORclass("cowardly refusing to add region to already initialized instance - please call TQNFCalculator::clear() before reusing this instance!");
return false;
}
if(myDataPath.IsNull())
myDataPath=defaultDataPath;
for(size_t i=0; i<this->cutNames.size(); i++){
if(cutNames[i] == cutName){
dataPaths[i] = myDataPath;
return false;
}
}
this->cutNames.push_back(cutName);
this->dataPaths.push_back(myDataPath);
return true;
}
bool TQNFCalculator::addSample(TString mcPath, TString name, bool fixed){
if(this->initialized){
ERRORclass("cowardly refusing to add sample to already initialized instance - please call TQNFCalculator::clear() before reusing this instance!");
return false;
}
if(name.IsNull()) name = mcPath;
for(size_t i=0; i<this->mcNames.size(); i++){
if(mcNames[i] == name){
mcPaths[i] = mcPath;
mcFixed[i] = fixed;
return false;
}
}
this->mcPaths.push_back(mcPath);
this->mcNames.push_back(name);
this->mcFixed.push_back(fixed);
this->nfBoundLower.push_back(this->getTagDoubleDefault("defaultBoundLower",0.));
this->nfBoundUpper.push_back(this->getTagDoubleDefault("defaultBoundUpper",2.));
return true;
}
bool TQNFCalculator::addSample(TString mcPath, TString name, double boundLower, double boundUpper){
if(this->initialized){
ERRORclass("cowardly refusing to add sample to already initialized instance - please call TQNFCalculator::clear() before reusing this instance!");
return false;
}
if(name.IsNull()) name = mcPath;
for(size_t i=0; i<this->mcNames.size(); i++){
if(mcNames[i] == name){
mcPaths[i] = mcPath;
mcFixed[i] = false;
nfBoundLower[i] = boundLower;
nfBoundUpper[i] = boundUpper;
return false;
}
}
this->mcPaths.push_back(mcPath);
this->mcNames.push_back(name);
this->mcFixed.push_back(false);
this->nfBoundLower.push_back(boundLower);
this->nfBoundUpper.push_back(boundUpper);
return true;
}
bool TQNFCalculator::addFixSample(TString mcPath, TString name){
return this->addSample(mcPath,name,true);
}
bool TQNFCalculator::initializeData(){
if(!this->fReader) return false;
if(this->data) delete this->data;
this->data = new TH1F("data","data",dataPaths.size(),0,dataPaths.size());
this->data->SetDirectory(NULL);
TString scaleScheme = this->getTagStringDefault("readScaleScheme",".nfc.read");
scaleScheme.Prepend("scaleScheme=");
for(size_t i=0; i<this->dataPaths.size(); i++){
TQCounter* c = this->fReader->getCounter(dataPaths[i],cutNames[i],scaleScheme);
if(!c){
ERRORclass("unable to obtain counter '%s' from '%s'!",cutNames[i].Data(),dataPaths[i].Data());
return false;
}
if (chainLoader && iterationNumber > -1) {
const TString path(dataPaths[i]+":"+cutNames[i]);
const double variation = chainLoader->getRelVariation(path,c->getCounter(),c->getError());
DEBUG("variation of path '%s' is '%f'",path.Data(),variation);
c->setCounter(c->getCounter()*variation);
}
this->data->SetBinContent(i+1,c->getCounter());
this->data->SetBinError(i+1,c->getError());
this->data->GetXaxis()->SetBinLabel(i+1,cutNames[i]);
delete c;
}
return true;
}
bool TQNFCalculator::ensurePositiveMC(){
bool action = false;
for(int j=0; j<this->mc->GetEntries(); j++){
TH1* hist = dynamic_cast<TH1*>(this->mc->At(j));
for(int i=0; i<hist->GetNbinsX(); i++){
double val = hist->GetBinContent(i);
if(val < 0){
hist->SetBinContent(i,0);
hist->SetBinError(i,std::max(val,hist->GetBinError(i)));
action = true;
}
}
}
return action;
}
bool TQNFCalculator::initializeMC(){
if(!fReader) return false;
if(this->mc) delete this->mc;
this->mc = new TObjArray();
this->mc->SetOwner(true);
TString scaleScheme = this->getTagStringDefault("readScaleScheme",".nfc.read");
scaleScheme.Prepend("scaleScheme=");
for(size_t j=0; j<this->mcPaths.size(); j++){
TH1F* hist = new TH1F(mcNames[j],mcNames[j],cutNames.size(),0,cutNames.size());
hist->SetDirectory(NULL);
for(size_t i=0; i<this->dataPaths.size(); i++){
TQCounter* c = this->fReader->getCounter(mcPaths[j],cutNames[i],scaleScheme);
if(!c){
this->messages.sendMessage(TQMessageStream::ERROR,"unable to obtain counter '%s' from '%s'!" ,cutNames[i].Data(),mcPaths[j].Data());
return false;
}
if (this->chainLoader && iterationNumber> -1) {
const TString path(mcPaths[j]+":"+cutNames[i]);
const double variation = chainLoader->getRelVariation(path,c->getCounter(),c->getError());
DEBUG("variation of path '%s' is '%f'",path.Data(),variation);
c->setCounter(c->getCounter()*variation);
}
DEBUG("Retrieved counter from path '%s' with value %.2f +/- %.2f",TString(mcPaths[j]+":"+cutNames[i]).Data(),c->getCounter(),c->getError());
hist->SetBinContent(i+1,c->getCounter());
hist->SetBinError(i+1,c->getError());
hist->GetXaxis()->SetBinLabel(i+1,cutNames[i]);
delete c;
}
this->mc->Add(hist);
}
return true;
}
bool TQNFCalculator::finalizeSelf(){
if(this->mc) delete this->mc;
this->mc = NULL;
if(this->data) delete this->data;
this->data = NULL;
this->NFs.clear();
this->NFuncertainties.clear();
return true;
}
bool TQNFCalculator::initializeSelf(){
if(!this->fReader)
return false;
DEBUGclass("initializing data histogram");
if(!this->initializeData())
return false;
DEBUGclass("initializing mc histograms");
if(!this->initializeMC())
return false;
return true;
}
size_t TQNFCalculator::getFloatingSamples(){
size_t n = 0;
for(size_t i=0; i<this->mcFixed.size(); i++){
if(!this->mcFixed[i]){
n++;
}
}
return n;
}
void TQNFCalculator::calculateNFs_MatrixMode(){
const unsigned int n(data->GetNbinsX());
if(n != this->getFloatingSamples()){
this->messages.sendMessage(TQMessageStream::WARNING,"cannot use matrix mode for %d samples in %d regions - only possible for equal N", this->getFloatingSamples(),n);
this->status = -10;
return;
}
if(verbosity > 1){
this->messages.sendMessage(TQMessageStream::INFO,"entering matrix mode");
}
std::vector<TString> vSampleNames;
TVectorD vData(n);
TVectorD vDataErr(n);
TMatrixD mMC(n,n);
TMatrixD mMCerr(n,n);
double max = 1;
for(size_t i=0; i<n; i++){
double dataVal = data->GetBinContent(i+1);
double dataErr2 = pow(data->GetBinError(i+1),2);
int j = 0;
for(size_t idx=0; idx<this->mcFixed.size(); idx++){
if(this->mcFixed[idx]){
TH1* hist = (TH1*)(this->mc->At(idx));
dataVal -= hist->GetBinContent(i+1);
dataErr2 += pow(hist->GetBinError(i+1),2);
} else {
TH1* hist = (TH1*)(this->mc->At(idx));
mMC[i][j] = hist->GetBinContent(i+1);
if(verbosity > 1) this->messages.sendMessage(TQMessageStream::INFO,"%s in %s is %.3f",mcNames[idx].Data(),cutNames[i].Data(),mMC[i][j]);
vSampleNames.push_back(mcNames[idx]);
mMCerr[i][j] = hist->GetBinError(i+1);
j++;
}
}
max = std::max(max,dataVal);
vData [i] = dataVal;
vDataErr[i] = sqrt(dataErr2);
}
TH1F** tmphists = (TH1F**)malloc(n*sizeof(TH1F*));
int i = 0;
for(size_t idx=0; idx<this->mcFixed.size(); idx++){
if(mcFixed[idx]) continue;
tmphists[i] = new TH1F(mcNames[idx],TString::Format("Distribution for NF[%s]",mcNames[idx].Data()),100,nfBoundLower[idx],nfBoundUpper[idx]);
tmphists[i]->SetDirectory(NULL);
i++;
}
size_t nSamples = this->getTagIntegerDefault("mode.matrix.nToyHits",100);
TVectorD vecNFErrs(n);
TVectorD vecNFs(n);
if(nSamples > 1){
TRandom rand;
TVectorD vtmp(vData);
for(size_t i=0; i<n; ++i){
for(size_t j=0; j<n; ++j){
for(size_t x = 0; x<nSamples; ++x){
TMatrixD mattmp(mMC);
mattmp[i][j] = rand.Gaus(mMC[i][j],mMCerr[i][j]);
mattmp.Invert();
for(size_t k=0; k<n; ++k){
for(size_t y = 0; y<nSamples; ++y){
vtmp[k] = rand.Gaus(vData[k],vDataErr[k]);
TVectorD tmpNFs(mattmp * vtmp);
for(size_t l=0; l<n; l++){
tmphists[l]->Fill(tmpNFs[l]);
}
vtmp[k] = vData[k];
}
}
}
}
}
for(size_t i=0; i<n; ++i){
vecNFs[i] = tmphists[i]->GetMean();
vecNFErrs[i] = tmphists[i]->GetRMS();
if(verbosity > 2){
this->messages.newline();
TQHistogramUtils::printHistogramASCII(this->messages.activeStream(),tmphists[i],"");
this->messages.newline();
}
delete tmphists[i];
}
free(tmphists);
} else {
TVectorD vtmp(vData);
TMatrixD mattmp(mMC);
#ifdef _DEBUG_
DEBUG("Printing MC matrix before inversion");
mattmp.Print();
#endif
mattmp.Invert();
#ifdef _DEBUG_
DEBUG("Printing MC matrix after inversion");
mattmp.Print();
#endif
TVectorD tmpNFs(mattmp * vtmp);
for(size_t l=0; l<n; l++){
vecNFs[l] = tmpNFs[l];
vecNFErrs[l] = 0;
}
}
bool ok = true;
TVectorD vClosure = mMC * vecNFs;
double closure = 0;
for(size_t i=0; i<n; i++){
closure += fabs(vClosure[i] - vData[i]);
}
this->messages.newline();
if(verbosity > 2){
const int numwidth = log10(max)+3;
for(size_t i=0; i<n; i++){
TString line = "( ";
for(size_t j=0; j<n; j++){
line += TString::Format("%*.1f ",numwidth,mMC[i][j]);
}
line += ") ";
if(i==0) line += "*";
else line += " ";
line += TString::Format(" ( %*.1f ) ",4,vecNFs[i]);
if(i==0) line += "=";
else line += " ";
line += TString::Format(" ( %*.1f ) ",numwidth,vData[i]);
this->messages.sendMessage(TQMessageStream::INFO,line);
}
}
if(verbosity > 1){
this->messages.sendMessage(TQMessageStream::INFO,"closure = %g",closure);
int j = 0;
for(size_t idx=0; idx<this->mcFixed.size(); idx++){
if(this->mcFixed[idx]) continue;
this->messages.sendMessage(TQMessageStream::INFO,"NF[%s] = %.3f +/- %.3f",mcNames[idx].Data(),vecNFs[j],vecNFErrs[j]);
j++;
}
}
if(!ok){
this->status = 20;
return;
}
double maxClosure;
if(this->getTagDouble("mode.matrix.maxClosure",maxClosure) && closure > maxClosure){
if(verbosity > 1) this->messages.sendMessage(TQMessageStream::WARNING,"closure exceeds maximum '%g', discarding results",maxClosure);
status = 1;
return;
}
int j = 0;
for(size_t idx=0; idx<this->mcFixed.size(); idx++){
if(this->mcFixed[idx]){
this->NFs.push_back (1);
this->NFuncertainties.push_back(0);
} else {
DEBUG("saving result entry %d : %.2f",j,vecNFs[j]);
this->NFs.push_back (vecNFs[j]);
this->NFuncertainties.push_back(vecNFErrs[j]);
j++;
}
}
status = 0;
}
void TQNFCalculator::calculateNFs_singleMode(){
if(this->getFloatingSamples() > 1){
this->messages.sendMessage(TQMessageStream::WARNING,"cannot use single mode for %d samples - only possible for nSamples=1", this->getFloatingSamples());
this->status = -5;
return;
}
double sum = 0;
double sumw = 0;
TH1* mchist = NULL;
if(verbosity > 1){
this->messages.sendMessage(TQMessageStream::INFO,"entering single mode");
}
for(int i=0; i<this->data->GetNbinsX(); i++){
if(verbosity > 1) this->messages.sendMessage(TQMessageStream::INFO,"looking at region %s",cutNames[i].Data());
double targetVal = data->GetBinContent(i+1);
if(verbosity > 1) this->messages.sendMessage(TQMessageStream::INFO,"data is %.3f +/- %.3f",targetVal,data->GetBinError(i+1));
double targetErr2 = pow(data->GetBinError(i+1),2);
for(int j=0; j<this->mc->GetEntries(); j++){
if(this->mcFixed[j]){
TH1* hist = (TH1*)(this->mc->At(j));
if(verbosity > 1) this->messages.sendMessage(TQMessageStream::INFO,"%s is %.3f +/- %.3f",mcNames[j].Data(),hist->GetBinContent(i+1),hist->GetBinError(i+1));
targetVal -= hist->GetBinContent(i+1);
targetErr2 += pow(hist->GetBinError(i+1),2);
} else mchist = (TH1*)(this->mc->At(j));
}
if(verbosity > 1){
this->messages.sendMessage(TQMessageStream::INFO,"background subtracted data: %.3f +/- %.3f",targetVal,sqrt(targetErr2));
this->messages.sendMessage(TQMessageStream::INFO,"contribution from %s is %.3f +/- %.3f",mchist->GetName(),mchist->GetBinContent(i+1),mchist->GetBinError(i+1));
}
double val = targetVal/mchist->GetBinContent(i+1);
double weight = 1./(val*val * (targetErr2 / pow(targetVal,2)
+ pow(mchist->GetBinError(i+1),2) / pow(mchist->GetBinContent(i+1),2)));
if(verbosity > 1) this->messages.sendMessage(TQMessageStream::INFO,"collecting NF = %.3f +/- %.3f from %s",val,1.0/sqrt(weight),cutNames[i].Data());
sum += val * weight;
sumw += weight;
}
if(verbosity > 1) this->messages.sendMessage(TQMessageStream::INFO,"total resulting NF = %.3f +/- %.3f",sum/sumw,1.0/sqrt(sumw));
for(size_t i=0; i<this->mcFixed.size(); i++){
if(this->mcFixed[i]){
this->NFs.push_back (1);
this->NFuncertainties.push_back(0);
} else {
this->NFs.push_back (sum/sumw);
this->NFuncertainties.push_back(1.0/sqrt(sumw));
}
}
this->status = 0;
if(verbosity > 0){
this->messages.sendMessage(TQMessageStream::INFO,"Results of the NF Calculation are as follows:");
this->printResults(&(this->messages.activeStream()));
}
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
void TQNFCalculator::calculateNFs_TFractionFitterMode(){
if(this->getFloatingSamples() < 2){
this->messages.sendMessage(TQMessageStream::WARNING,"cannot use TFractionFitter mode for %d samples - only possible for nSamples>1, please use single mode instead", this->getFloatingSamples());
this->status = -3;
return;
}
const unsigned int n(data->GetNbinsX());
if(n <= this->getFloatingSamples()){
this->messages.sendMessage(TQMessageStream::WARNING,"cannot use TFractionFitter mode for %d samples in %d regions - only possible for #regions > #samples, please use MatrixInversion for #regions == #samples or Simple mode instead", this->getFloatingSamples(),n);
this->status = -5;
return;
}
if(this->verbosity > 1){
this->messages.sendMessage(TQMessageStream::INFO,"entering fraction fitter mode");
}
if(this->ensurePositiveMC()){
this->messages.sendMessage(TQMessageStream::INFO,"one or more bins was shifted to yield positive bin contents");
}
if(this->messages.isFile()){
TQLibrary::redirect(this->messages.getFilename(),true);
this->messages.close();
}
TString voption = "Q";
if(this->verbosity == 1) voption = "";
if(this->verbosity > 1) voption = "V";
TFractionFitter fitter(this->data, this->mc, voption);
int nFloat = 0;
for(int i=0; i<this->mc->GetEntries(); i++){
TH1* mchist = (TH1*)(this->mc->At(i));
double fraction = mchist->Integral() / this->data->Integral();
if(mcFixed[i]){
if(this->verbosity > 0){
printf("\n\n");
TString msg = this->messages.formatMessage(TQMessageStream::INFO,"fraction for %s is %.3f (fixed=true)\n",mcNames[i].Data(),fraction);
printf(msg.Data());
}
fitter.Constrain(i+1,std::max(fraction - epsilon,0.),std::min(fraction + epsilon,1.));
} else {
if(this->verbosity > 0){
printf("\n\n");
TString msg = this->messages.formatMessage(TQMessageStream::INFO,"fraction for %s is %.3f (fixed=false)\n",mcNames[i].Data(),fraction);
printf(msg.Data());
}
fitter.Constrain(i+1,fraction*this->nfBoundLower[i],fraction*this->nfBoundUpper[i]);
nFloat++;
}
}
if(this->verbosity > 0){
printf("\n\n");
TString msg = this->messages.formatMessage(TQMessageStream::INFO,"running simultaneous fit of %d NFs in %d regions\n",nFloat,(int)cutNames.size()).Data();
printf(msg.Data());
}
this->status = fitter.Fit();
if(this->messages.isFile()){
TQLibrary::restore();
this->messages.reopen();
}
if(this->verbosity > 0){
this->messages.newlines(2);
this->messages.sendMessage(TQMessageStream::INFO,"performed fit, status is %d\n",this->status);
}
if(this->success()){
for(size_t i=0; i<this->mcNames.size(); i++){
DEBUGclass("retrieving NF for '%s'",this->mcNames[i].Data());
double nf = 0;
double sigma = 0;
fitter.GetResult(i,nf,sigma);
TH1* mchist = dynamic_cast<TH1*>(this->mc->At(i));
double fraction = 1;
if(!mchist){
ERRORclass("internal error: invalid histogram pointer for '%s' at position %d/%d!",this->mcNames[i].Data(),(int)i,this->mc->GetEntries());
} else {
fraction = mchist->Integral() / this->data->Integral();
}
this->NFs.push_back(nf / fraction);
this->NFuncertainties.push_back(sigma / fraction);
}
}
}
#pragma GCC diagnostic pop
int TQNFCalculator::calculateNFs(const TString& modename){
if(!this->initialize()){
this->status = -10;
return this->status;
}
TString rootfilename;
if(this->getTagString("writePreFitHistograms",rootfilename)){
this->exportHistogramsToFile(rootfilename);
}
this->calculateNFs(this->getMethod(modename));
if(this->success()){
if(this->getTagString("writePostFitHistograms",rootfilename)){
this->exportHistogramsToFile(rootfilename);
}
TString resultfilename;
if(this->getTagString("saveResults",resultfilename)){
this->writeResultsToFile(resultfilename);
}
}
return this->status;
}
int TQNFCalculator::calculateNFs(){
this->messages.sendMessage(TQMessageStream::INFO,"initializing");
this->messages.newline();
if(!this->initialize()){
this->status = -10;
return this->status;
}
TString prefitrootfilename = this->getPathTag("writePreFitHistograms");
if(!prefitrootfilename.IsNull()){
this->exportHistogramsToFile(prefitrootfilename);
}
this->messages.sendMessage(TQMessageStream::INFO,"calculateNFs entering evaluation");
for(size_t i=0; i<this->methods.size(); i++){
this->messages.newline();
this->messages.sendMessage(TQMessageStream::INFO,TQStringUtils::repeat("-",40));
this->messages.newline();
this->calculateNFs(methods[i]);
if(this->success()){
if(verbosity > 1){
this->messages.sendMessage(TQMessageStream::INFO,"succeeded in calculating NFs with method '%s'",this->getMethodName(methods[i]).Data());
}
break;
} else {
this->messages.activeStream().flush();
}
}
if(this->success()){
TString postrootfilename = this->getPathTag("writePostFitHistograms");
if(!postrootfilename.IsNull()){
this->exportHistogramsToFile(postrootfilename);
}
TString resultfilename = this->getPathTag("saveResults");
if(!resultfilename.IsNull()){
this->writeResultsToFile(resultfilename);
}
}
this->messages.activeStream().flush();
return status;
}
void TQNFCalculator::setNFsUnity(){
this->NFs.clear();
this->NFuncertainties.clear();
this->messages.sendMessage(TQMessageStream::WARNING,"TQNFCalculator::setNFsUnity() was called, all NFs are set to unity");
for(size_t i=0; i<this->mcPaths.size(); i++){
this->NFs.push_back(1);
this->NFuncertainties.push_back(0);
}
this->status = 0;
}
void TQNFCalculator::fail(){
this->NFs.clear();
this->NFuncertainties.clear();
this->messages.sendMessage(TQMessageStream::WARNING,"TQNFCalculator::fail() was called, all NFs erased, failure state acquired");
this->status = -1;
}
int TQNFCalculator::calculateNFs(TQNFCalculator::Method method){
if(!this->initialize()){
this->status = -10;
return this->status;
}
int nsamples = this->getFloatingSamples();
if(this->mc->GetEntries() == 0 || nsamples == 0){
this->status = -100;
return this->status;
}
switch(method){
case Single:
this->calculateNFs_singleMode();
break;
case MatrixInversion:
this->calculateNFs_MatrixMode();
break;
case FractionFitter:
this->calculateNFs_TFractionFitterMode();
break;
case Unity:
this->setNFsUnity();
break;
default:
this->fail();
}
this->messages.activeStream().flush();
return this->status;
}
size_t TQNFCalculator::findIndex(TString name){
for(size_t i=0; i<this->mcNames.size(); i++){
if(mcNames[i] == name)
return i;
}
return -1;
}
double TQNFCalculator::getNF(TString name){
size_t n = this->findIndex(name);
if(!this->success() || (n > this->mcNames.size())) return std::numeric_limits<double>::quiet_NaN();
return this->NFs[n];
}
double TQNFCalculator::getNFUncertainty(TString name){
size_t n = this->findIndex(name);
if(!this->success() || (n > this->mcNames.size())) return std::numeric_limits<double>::quiet_NaN();
return this->NFuncertainties[n];
}
bool TQNFCalculator::getNFandUncertainty(TString name, double& nf, double& sigma){
size_t n = this->findIndex(name);
if(n > this->mcNames.size()) return false;
if(this->mcFixed[n]) return false;
nf = this->NFs[n];
sigma = this->NFuncertainties[n];
nf = std::max(nfBoundLower[n],std::min(nfBoundUpper[n],nf));
return true;
}
void TQNFCalculator::printRegions(std::ostream* os){
if(!os) return;
for(size_t i=0; i<cutNames.size(); i++){
*os << cutNames[i] << "\t" << dataPaths[i] << std::endl;
}
}
void TQNFCalculator::printSamples(std::ostream* os){
if(!os) return;
for(size_t i=0; i<mcNames.size(); i++){
*os << mcNames[i] << "\t" << mcPaths[i];
if(this->mcFixed[i]) *os << "\t(fixed)";
else *os << "(" << this->nfBoundLower[i] << "<NF<" << this->nfBoundUpper[i] << ")";
*os << std::endl;
}
}
void TQNFCalculator::printResults(std::ostream* os){
if(!os || !os->good()) return;
if(!this->success()){
WARNclass("no results obtained, status is %d",this->status);
return;
}
for(size_t i=0; i<this->NFs.size(); i++){
double percent = 100*(this->NFs[i]-1);
*os << this->mcNames.at(i) << "\t" << this->NFs.at(i) << " +/- " << this->NFuncertainties.at(i) << "\t~ ";
if(TMath::AreEqualAbs(percent,0,0.05))
*os << "no NF ";
if(this->mcFixed.at(i))
*os << "(fixed)";
else {
if(percent > 0)
*os << "+";
*os << TString::Format("%.2f",percent) << " %";
}
*os << std::endl;
}
}
TH1* TQNFCalculator::getHistogram(const TString& name){
if(!this->initialize()) return NULL;
if(name=="data")
return this->data;
for(size_t i=0; i<this->mcNames[i]; i++){
if(name == mcNames[i])
return (TH1*)(this->mc->At(i));
}
return NULL;
}
bool TQNFCalculator::scaleSample(const TString& name, double val){
if(!TQUtils::isNum(val)) return false;
TH1* hist = this->getHistogram(name);
if(!hist) return false;
hist->Scale(val);
return true;
}
const TString& TQNFCalculator::getMCPath(const TString& name){
for(size_t i=0; i<this->mcPaths.size(); i++){
if(mcNames[i] == name)
return mcPaths[i];
}
return TQStringUtils::emptyString;
}
int TQNFCalculator::getStatus(){
return this->status;
}
TString TQNFCalculator::getStatusMessage(){
switch(this->status){
case -999:
return "uninitialized";
case -100:
return "no data";
case -50:
return "method not implemented";
case -10:
return "error during initialization";
case -5:
return "method not applicable";
case -1:
return "no method succeeded - fail() called";
case 0:
return "all OK";
case 4:
return "TFractionFitter failed to converge";
case 20:
return "matrix method encountered negative NFs";
default:
return "unkown error";
}
}
void TQNFCalculator::printStatus(){
INFOclass("current status of instance '%s' is '%d': %s",this->GetName(),(int)(this->status),this->getStatusMessage().Data());
}
int TQNFCalculator::execute(int itrNumber) {
this->iterationNumber = itrNumber;
return calculateNFs();
}
bool TQNFCalculator::success(){
if(this->initialized && (this->status == 0))
return true;
return false;
}
void TQNFCalculator::setEpsilon(double e){
this->epsilon = e;
}
double TQNFCalculator::getEpsilon(){
return this->epsilon;
}
int TQNFCalculator::writeResultsToFile(const TString& filename){
if(!this->success()) return -1;
TQUtils::ensureDirectoryForFile(filename);
std::ofstream outfile(filename.Data());
if(!outfile.is_open()){
return -1;
}
int retval = this->writeResultsToStream(&outfile);
outfile.close();
return retval;
}
TString TQNFCalculator::getResultsAsString(const TString& name){
if(!this->success()) return "";
for(size_t i=0; i<mcNames.size(); i++){
if(name == mcNames[i]){
TQTaggable* tags = this->getResultsAsTags(i);
TString s = tags->exportTagsAsString();
delete tags;
return s;
}
}
return "";
}
TQTaggable* TQNFCalculator::getResultsAsTags(const TString& name){
if(!this->success()) return NULL;
for(size_t i=0; i<mcNames.size(); i++){
if(name == mcNames[i]){
return this->getResultsAsTags(i);
}
}
return NULL;
}
TQTaggable* TQNFCalculator::getResultsAsTags(size_t i){
if(!this->success()) return NULL;
double nf = 0;
double sigma = 0;
if (!this->getNFandUncertainty(mcNames[i],nf,sigma)) return 0;
TQTaggable* tags = new TQTaggable();
tags->setTagDouble("normalization",nf);
tags->setTagDouble("normalizationUncertainty",sigma);
tags->setTagString("path",this->mcPaths[i]);
if(!this->mcNames[i].IsNull() && (this->mcNames[i] != this->mcPaths[i])){
tags->setTagString("name",mcNames[i]);
}
TQSampleFolder* sf = this->fReader->getSampleFolder();
TQSampleFolder * processSampleFolder = sf->getSampleFolder(this->mcPaths[i]);
TString processTitle = "";
if (processSampleFolder)
processSampleFolder->getTagString(this->getTagStringDefault("processTitleKey","style.default.title"), processTitle);
if(!processTitle.IsNull()){
tags->setTagString("title",mcNames[i]);
}
if(this->cutInfoFolder && sf && (this->cutInfoFolder->getRoot() == sf->getRoot())){
tags->setTagString("info",this->cutInfoFolder->getPath());
}
return tags;
}
int TQNFCalculator::writeResultsToStream(std::ostream* out){
if(!this->success()) return -1;
TQTaggable* tags;
for(size_t i=0; i<mcNames.size(); i++){
tags = this->getResultsAsTags(i);
if (!tags) continue;
*out << tags->exportTagsAsString() << std::endl;
delete tags;
}
return 1;
}
bool TQNFCalculator::readConfiguration(TQFolder* f){
if(!f) return false;
this->SetName(f->GetName());
this->setDefaultDataPath(f->getTagStringDefault("defaultDataPath","data"));
this->setEpsilon(f->getTagDoubleDefault("epsilon",std::numeric_limits<double>::epsilon()));
this->setVerbosity(f->getTagIntegerDefault("verbosity",5));
std::vector<TString> methodStrings = f->getTagVString("methods");
for(size_t i=0; i<methodStrings.size(); i++){
this->addMethod(methodStrings[i]);
}
this->importTags(f);
TQFolderIterator sitr(f->getListOfFolders("samples/?"),true);
int nSamples = 0;
while(sitr.hasNext()){
TQFolder* sample = sitr.readNext();
if(!sample) continue;
TString name = sample->getTagStringDefault("name",sample->getName());
TString path = sample->getTagStringDefault("path",name);
bool fixed = sample->getTagBoolDefault("fixed",false);
double boundLower = sample->getTagDoubleDefault("lower",this->getTagDoubleDefault("defaultBoundLower",0.));
double boundUpper = sample->getTagDoubleDefault("upper",this->getTagDoubleDefault("defaultBoundUpper",2.));
nSamples += !fixed;
if(fixed) this->addFixSample(path,name);
else this->addSample(path,name,boundLower,boundUpper);
}
if(nSamples < 1) return false;
int nRegions = 0;
TQFolderIterator ritr(f->getListOfFolders("regions/?"),true);
while(ritr.hasNext()){
TQFolder* region = ritr.readNext();
if(!region) continue;
TString name = region->getTagStringDefault("name",region->getName());
TString mydatapath = region->getTagStringDefault("datapath","");
bool active = region->getTagBoolDefault("active",true);
if(active){
this->addRegion(name,mydatapath);
nRegions++;
}
}
if(nRegions < 1) return false;
return true;
}
TH1* TQNFCalculator::getMCHistogram(const TString& name){
if(!this->initialize()) return NULL;
for(size_t i=0; i<this->mcPaths.size(); i++){
if(mcNames[i] == name)
return dynamic_cast<TH1*>(mc->At(i));
}
return NULL;
}
TQSampleFolder* TQNFCalculator::exportHistograms(bool postFit){
if(!this->initialize()) return NULL;
TQSampleFolder* folder = TQSampleFolder::newSampleFolder(this->GetName());
if(!this->exportHistogramsToSampleFolder(folder,postFit)){
delete folder;
return NULL;
}
return folder;
}
bool TQNFCalculator::exportHistogramsToFile(const TString& destination, bool recreate, bool postFit){
TQSampleFolder* folder = this->exportHistograms(postFit);
if(!folder) return false;
if(!TQUtils::ensureDirectoryForFile(destination)){
ERRORclass("unable to access or create directory for file '%s'",destination.Data());
return false;
}
if(!folder->writeToFile(destination,recreate)){
ERRORclass("unable to write folder '%s' to file '%s'",folder->GetName(),destination.Data());
delete folder;
return false;
}
delete folder;
return true;
}
bool TQNFCalculator::exportHistogramsToSampleFolder(TQSampleFolder* folder, bool postFit){
if(!this->initialize() || !folder) return false;
TQFolder* plotconf = folder->getFolder("scheme+");
TQSampleFolder* s = folder->getSampleFolder("data+");
TList* sources = this->fReader->getListOfSampleFolders(defaultDataPath);
if(sources && sources->GetEntries() > 0){
TQFolder* source = dynamic_cast<TQFolder*>(sources->First());
source->exportTags(s,"","style.*",true);
}
delete sources;
TQFolder* f = s->getFolder(".histograms+");
TH1* hist = dynamic_cast<TH1*>(this->data->Clone());
if(!hist) return false;
hist->SetName(this->GetName());
f->addObject(hist);
plotconf->setTagString(".processes.0..path","data");
plotconf->setTagString(".processes.0..name","data");
plotconf->setTagBool(".processes.0..isData",true);
for(size_t i=0; i<this->mcPaths.size(); i++){
TString processTag = TString::Format(".processes.%d.",(int)i+1);
TQSampleFolder* s = folder->getSampleFolder(mcNames[i]+"+");
TList* sources = this->fReader->getListOfSampleFolders(mcPaths[i]);
plotconf->setTagString(processTag+".path",mcNames[i]);
plotconf->setTagString(processTag+".name",mcNames[i]);
plotconf->setTagBool(processTag+".isBackground",true);
plotconf->setTagBool(processTag+".isFixed",mcFixed[i]);
if(sources && sources->GetEntries() > 0){
TQFolder* source = dynamic_cast<TQFolder*>(sources->First());
source->exportTags(s,"","style.*",true);
}
delete sources;
TQFolder* f = s->getFolder(".histograms+");
TH1* hist = dynamic_cast<TH1*>(mc->At(i)->Clone());
if(this->success() && postFit) hist->Scale(this->NFs[i]);
hist->SetName(this->GetName());
f->addObject(hist);
}
return true;
}
bool TQNFCalculator::addMethod(const TString& methodName){
Method method = this->getMethod(methodName);
if(method != UNDEFINED){
this->methods.push_back(method);
return true;
}
return false;
}
void TQNFCalculator::clearMethods(){
this->methods.clear();
}
void TQNFCalculator::printMethods(){
for(size_t i=0; i<this->methods.size(); i++){
std::cout << i+1 << ") " << this->getMethodName(methods[i]);
}
}