#include "QFramework/TQCut.h"
#include "TLeaf.h"
#include "TObjArray.h"
#include "QFramework/TQAnalysisJob.h"
#include "QFramework/TQStringUtils.h"
#include "QFramework/TQValue.h"
#include "QFramework/TQSample.h"
#include "QFramework/TQIterator.h"
#include "QFramework/TQUtils.h"
#include "QFramework/TQUniqueCut.h"
#include "QFramework/TQToken.h"
#include "QFramework/TQLibrary.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <stdexcept>
ClassImp(TQCut)
TQCut::VariantDefinitionList* getVariantDefinitions(TQSampleFolder* sf) {
TQTaggable variantTags;
if (!variantTags.importTagsWithoutPrefix(sf,".variant.")) {
return nullptr;
}
TQTaggable replacements;
replacements.importTagsWithoutPrefix(variantTags,"replaceCut.");
std::shared_ptr<TQTaggable> aliases = TQTaggable::getGlobalTaggable("aliases");
std::map<TString,std::vector<TQCut::VariantDefinition>> suffixToVariantsMap;
TQStringIterator setItr(replacements.getListOfTagNames(),true);
while(setItr.hasNext()) {
const TString& setName = setItr.readNext()->GetString();
TString cutNamePattern = replacements.getTagStringDefault(setName,"");
TQTaggable setCutVariants, setWeightVariants;
setCutVariants.importTagsWithoutPrefix(variantTags, TString::Format("cut.%s.",setName.Data()));
setWeightVariants.importTagsWithoutPrefix(variantTags, TString::Format("weight.%s.",setName.Data()));
TQStringIterator varCutItr(setCutVariants.getListOfTagNames(),true);
while(varCutItr.hasNext()) {
TString suffix = varCutItr.readNext()->String();
TQCut::VariantDefinition varDef;
varDef.matchingPattern = cutNamePattern;
setCutVariants.getTagString(suffix,varDef.cutExpression);
setWeightVariants.getTagString(suffix,varDef.weightExpression);
if (aliases) {
varDef.cutExpression = aliases->replaceInTextRecursive(varDef.cutExpression);
varDef.weightExpression = aliases->replaceInTextRecursive(varDef.weightExpression);
}
if (!suffixToVariantsMap.count(suffix)) {
suffixToVariantsMap[suffix] = {};
}
suffixToVariantsMap[suffix].push_back(varDef);
}
TQStringIterator varWeightItr(setWeightVariants.getListOfTagNames(),true);
while(varWeightItr.hasNext()) {
TString suffix = varWeightItr.readNext()->String();
if (setCutVariants.hasTag(suffix)) continue;
TQCut::VariantDefinition varDef;
varDef.matchingPattern = cutNamePattern;
setWeightVariants.getTagString(suffix,varDef.weightExpression);
if (aliases) {
varDef.weightExpression = aliases->replaceInTextRecursive(varDef.weightExpression);
}
if (!suffixToVariantsMap.count(suffix)) {
suffixToVariantsMap[suffix] = {};
}
suffixToVariantsMap[suffix].push_back(varDef);
}
}
TQCut::VariantDefinitionList* retVal = new TQCut::VariantDefinitionList();
for (std::pair<const TString,std::vector<TQCut::VariantDefinition>> & element : suffixToVariantsMap) {
retVal->push_back({element.first,element.second});
}
return retVal;
}
void TQCut::VariantResults::resize(size_t size) {
this->passed.clear();
this->passed.resize(size);
this->weight.clear();
this->weight.resize(size);
this->reset(1.);
}
void TQCut::VariantResults::reset(double baseWeight) {
std::fill(this->passed.begin(), this->passed.end(), true);
std::fill(this->weight.begin(), this->weight.end(), baseWeight);
}
bool TQCut::VariantResults::passAny() {
return std::any_of(this->passed.begin(),this->passed.end(), [](bool p) {return p;});
}
bool TQCut::isValidName(const TString& name_) {
return TQStringUtils::isValidIdentifier(name_,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_",1);
}
bool TQCut::parseCutDefinition(const TString& definition_, TString * name_,
TString * baseCutName_, TString * cutExpr_, TString * weightExpr_) {
if(name_ && baseCutName_ && cutExpr_ && weightExpr_)
return TQCut::parseCutDefinition(definition_, *name_, *baseCutName_, *cutExpr_, *weightExpr_);
return false;
}
bool TQCut::parseCutDefinition(TString definition, TString& name, TString& baseCutName, TString& cutExpr, TString& weightExpr) {
TString buf;
TQStringUtils::readUpTo(definition,buf, ":","{}","\"\"");
if(buf.IsNull()) return false;
name = TQStringUtils::trim(buf);
if (!isValidName(name)) { return false; }
TQStringUtils::readToken(definition,buf,":");
buf.Clear();
if(definition.Contains("<<")){
TQStringUtils::readUpTo(definition,buf,"<","{}","\"\"");
baseCutName = TQStringUtils::trim(buf);
TQStringUtils::readToken(definition,buf,"<");
if(!isValidName(baseCutName)) return false;
} else {
baseCutName="";
}
buf.Clear();
TQStringUtils::readUpTo(definition,buf,":;","{}()[]","\"\"");
if(definition.IsNull()){
cutExpr = TQStringUtils::compactify(buf);
} else {
cutExpr = TQStringUtils::compactify(buf);
buf.Clear();
while(TQStringUtils::readToken(definition,buf,":")>=2) {
if (buf.Length()>2) {
TQLibrary::ERRORclass("Unexpected number of consecutive ':'s in basecut definition found (max. 2)!");
return false;
}
TQStringUtils::readUpTo(definition,buf,":","{}()[]","\"\"");
cutExpr+=buf;
buf.Clear();
}
TQStringUtils::readToken(definition,buf," ");
buf.Clear();
TQStringUtils::readUpTo(definition,buf,";","{}()[]","\"\"");
weightExpr = TQStringUtils::compactify(buf);
}
if(cutExpr.IsNull()) return false;
return true;
}
TQCut * TQCut::createCut(const TString& definition_) {
TString name, baseCutName, cutExpr, weightExpr;
TQCut * cut = NULL;
if (parseCutDefinition(definition_, name, baseCutName, cutExpr, weightExpr)) {
if (baseCutName.Length() > 0) { return 0; }
cut = new TQCut(name);
cut->setCutExpression(cutExpr);
cut->setWeightExpression(weightExpr);
}
return cut;
}
TQCut * TQCut::createCut(const TString& name, const TString& cutExpr, const TString& weightExpr) {
TQCut* cut = new TQCut(name);
cut->setCutExpression(cutExpr);
cut->setWeightExpression(weightExpr);
return cut;
}
TQCut* TQCut::createFromFolderInternal(TQFolder* folder, TQTaggable* tags){
if (!folder) return 0;
TString name = folder->GetName();
TString title = folder->GetTitle();
folder->getTagString(".name",name);
folder->getTagString(".title",title);
TQValue * objCutExpr = dynamic_cast<TQValue*>(folder->getObject(".cutExpression"));
TQValue * objWeightExpr = dynamic_cast<TQValue*>(folder->getObject(".weightExpression"));
TString cutExpression = folder->getTagStringDefault(".cutExpression", objCutExpr ? objCutExpr ->getString() : "");
TString weightExpression = folder->getTagStringDefault(".weightExpression",objWeightExpr ? objWeightExpr->getString() : "");
if (cutExpression.Length()==0) {
if (weightExpression.Length()==0) {
throw std::runtime_error(TString::Format("Cut with name '%s' and title '%s' has neither tags '.cutExpression' nor '.weightExpression', please check your cut definitions!",name.Data(),title.Data()).Data());
}else{
WARNclass("Found weight expression but no cut expression found when creating cut with name '%s' and title '%s'. If you do not intend to veto events at this cut please consider adding '.cutExpression=\"1.\"' to this cut.",name.Data(),title.Data());
}
}
bool skipAnalysisJobsGlobal = folder->getTagBoolDefault(".skipJobs",false);
int printEvents = folder->getTagIntegerDefault(".printResult",0);
TQCut* newcut = nullptr;
if(cutExpression.BeginsWith("#UNIQUE")){
TString buffer;
TString runno,evtno;
TQStringUtils::readUpTo(cutExpression,buffer,"(");
buffer.Clear();
TQStringUtils::readBlock(cutExpression,buffer ,"()");
TQStringUtils::readUpTo(buffer,runno ,",");
TQStringUtils::removeLeading(buffer,",");
evtno = buffer;
newcut = new TQUniqueCut(name,runno,evtno);
newcut->SetTitle(title);
} else {
newcut = new TQCut(name,title);
newcut->setCutExpression (tags ? tags->replaceInTextRecursive(cutExpression) : cutExpression );
newcut->setWeightExpression(tags ? tags->replaceInTextRecursive(weightExpression) : weightExpression);
}
newcut->setSkipAnalysisJobsGlobal(skipAnalysisJobsGlobal);
newcut->setPrintResults(printEvents);
return newcut;
}
void TQCut::importFromFolderInternal(TQFolder * folder, TQTaggable* tags){
TQFolderIterator itr(folder->getListOfFolders("?"),true);
while(itr.hasNext()){
TQFolder* f = itr.readNext();
if(!f) continue;
DEBUGclass("reading folder '%s' for import",f->GetName());
TQCut * subCut = TQCut::createFromFolderInternal(f,tags);
if (subCut){
if(!this->addCut(subCut)){
WARNclass("cannot add cut '%s' to '%s' - a cut with the given name already exists at '%s'!",subCut->GetName(),this->getPath().Data(),this->getRoot()->getCut(subCut->GetName())->getBase()->getPath().Data());
delete subCut;
} else {
subCut->importFromFolderInternal(f,tags);
}
} else {
WARNclass("unable to import cut from folder '%s'",f->GetName());
}
}
this->sort();
}
TQCut * TQCut::importFromFolder(TQFolder * folder, TQTaggable* tags) {
TQCut* cut = createFromFolderInternal(folder,tags);
cut->importFromFolderInternal(folder,tags);
return cut;
}
TQCut::TQCut() :
TNamed("<template>", "template"),
fCutItr(this->fCuts),
fJobItr(this->fAnalysisJobs)
{
}
TQCut::TQCut(const TString& name_, const TString& title_, const TString& cutExpression, const TString& weightExpression) :
TNamed(name_, title_),
fCutItr(this->fCuts),
fJobItr(this->fAnalysisJobs)
{
this->setCutExpression(cutExpression);
this->setWeightExpression(weightExpression);
}
TList * TQCut::exportDefinitions(bool terminatingColon) {
TList * defs = new TList();
defs->SetOwner(true);
TString cutDef = TString(this->GetName()) + ": ";
if (fBase) {
cutDef.Append(TString::Format("%s << ", fBase->GetName()));
}
cutDef.Append(this->getCutExpression());
TString weights = this->getWeightExpression();
if (!weights.IsNull()) {
cutDef.Append(weights.Prepend(" : "));
}
if (terminatingColon) {
cutDef.Append(";");
}
defs->Add(new TObjString(cutDef.Data()));
this->fCutItr.reset();
while (this->fCutItr.hasNext()){
TQCut * c = this->fCutItr.readNext();
TList * subDefs = c->exportDefinitions(terminatingColon);
defs->AddAll(subDefs);
subDefs->SetOwner(false);
delete subDefs;
}
return defs;
}
void TQCut::setBase(TQCut * base_) {
fBase = base_;
}
TQCut * TQCut::getBase() {
return fBase;
}
TString TQCut::getPath() {
if(this->fBase) return TQFolder::concatPaths(this->fBase->getPath(),this->GetName());
else return this->GetName();
}
const TQCut * TQCut::getBaseConst() const {
return fBase;
}
TQCut * TQCut::getRoot() {
if (fBase)
return fBase->getRoot();
else
return this;
}
void TQCut::printEvaluation() const {
Long64_t iEvent = this->fTree->GetReadEntry();
this->printEvaluation(iEvent);
}
void TQCut::printWeightComponents() const{
Long64_t iEvent = this->fTree->GetReadEntry();
this->printWeightComponents(iEvent);
}
void TQCut::printWeightComponents(Long64_t) const{
TQIterator itr(this->fWeightObservable->getBranchNames(),true);
int index = 0;
while(itr.hasNext()){
if(index != 0){
std::cout << ", ";
}
TObject* next = itr.readNext();
if(!next) continue;
TLeaf *leaf = fTree->GetLeaf(next->GetName());
if(!leaf) continue;
if(fTree->GetBranchStatus(next->GetName()) == 1){
std::cout << next->GetName() << "=" << leaf->GetValue();
} else {
std::cout << TQStringUtils::makeBoldRed(next->GetName()) << " " << TQStringUtils::makeBoldRed("undefined");
}
index++;
}
std::cout << std::endl;
}
void TQCut::printEvaluation(Long64_t iEvent) const {
std::cout << TQStringUtils::makeBoldWhite(TString::Format("considering entry %lld...",iEvent)) << std::endl;
this->printEvaluationStep(0);
int index = 0;
const TQCut* c = this;
do {
TQIterator itr(c->fCutObservable->getBranchNames(),true);
while(itr.hasNext()){
if(index != 0){
std::cout << ", ";
}
TObject* next = itr.readNext();
if(!next) continue;
TLeaf *leaf = fTree->GetLeaf(next->GetName());
if(!leaf) continue;
if(fTree->GetBranchStatus(next->GetName()) == 1){
std::cout << next->GetName() << "=" << leaf->GetValue();
} else {
std::cout << TQStringUtils::makeBoldRed(next->GetName()) << " " << TQStringUtils::makeBoldRed("undefined");
}
index++;
}
c = c->passed() ? c->getBaseConst() : NULL;
} while (c);
std::cout << std::endl;
}
bool TQCut::printEvaluationStep(size_t indent) const {
bool passed = this->passed();
std::cout << TQStringUtils::makeBoldWhite("[");
if(passed) std::cout << TQStringUtils::makeBoldGreen(" OK ");
else std::cout << TQStringUtils::makeBoldRed("FAIL");
std::cout << TQStringUtils::makeBoldWhite("]");
std::cout << TQStringUtils::repeat("\t",indent);
if(indent > 0) std::cout << "&& ";
std::cout << this->getActiveCutExpression();
std::cout << std::endl;
if(passed && this->fBase){
passed = this->fBase->printEvaluationStep(indent+1);
}
return passed;
}
void TQCut::setCutExpression(const TString& cutExpression){
this->fCutExpression = TQStringUtils::minimize(cutExpression);
}
void TQCut::setWeightExpression(const TString& weightExpression) {
this->fWeightExpression = TQStringUtils::minimize(weightExpression);
}
TQObservable* TQCut::getCutObservable() {
return this->fCutObservable;
}
TQObservable* TQCut::getWeightObservable(){
return this->fWeightObservable;
}
const TString& TQCut::getCutExpression() const {
return this->fCutExpression;
}
TString TQCut::getActiveCutExpression() const {
return (this->fCutObservable ? this->fCutObservable->getActiveExpression() : TQStringUtils::emptyString);
}
TString TQCut::getCompiledCutExpression(TQTaggable* tags) {
return TQObservable::compileExpression(this->fCutExpression,tags);
}
TString TQCut::getGlobalCutExpression(TQTaggable* tags) {
if(this->fBase){
TString expr = this->fBase->getGlobalCutExpression(tags);
expr.Append(" && ( ");
expr.Append(this->getCompiledCutExpression(tags));
expr.Append(")");
return expr;
}
return this->getCompiledCutExpression(tags);
}
const TString& TQCut::getWeightExpression() const {
return this->fWeightExpression;
}
TString TQCut::getActiveWeightExpression() const {
return (this->fWeightObservable ? this->fWeightObservable->getActiveExpression() : TQStringUtils::emptyString);
}
TString TQCut::getCompiledWeightExpression(TQTaggable* tags) {
return TQObservable::compileExpression(this->fWeightExpression,tags);
}
TString TQCut::getGlobalWeightExpression(TQTaggable* tags) {
if(this->fBase){
TString expr = this->fBase->getGlobalWeightExpression(tags);
expr.Append(" * ");
expr.Append(this->getCompiledWeightExpression(tags));
return expr;
}
return this->getCompiledWeightExpression(tags);
}
TQCut * TQCut::addAndReturnCut(const TString& definition_) {
TString name, baseCutName, cutExpr, weightExpr;
if (!parseCutDefinition(definition_, name, baseCutName, cutExpr, weightExpr))
return NULL;
TQCut * cut = new TQCut(name);
cut->setCutExpression(cutExpr);
cut->setWeightExpression(weightExpr);
if (addCut(cut, baseCutName)) {
return cut;
} else {
delete cut;
}
return NULL;
}
bool TQCut::addCut(const TString& definition_) {
if (addAndReturnCut(definition_)) {
return true;
} else {
return false;
}
}
bool TQCut::addCut(TQCut * cut_, const TString& baseCutName) {
TQCut * cut = getCut(baseCutName);
if (!cut) { return false; }
return cut->addCut(cut_);
}
bool TQCut::addCut(TQCut * cut_) {
if (!cut_) { return false; }
if (this->getRoot()->getCut(cut_->GetName())) { return false; }
cut_->setBase(this);
fCuts->Add(cut_);
return true;
}
bool TQCut::isMergeable() const {
return true;
}
void TQCut::consolidate() {
for (int iChild = 0; iChild < fCuts->GetEntries(); iChild++) {
((TQCut*)fCuts->At(iChild))->consolidate();
}
int i = 0;
while (i < fCuts->GetEntries()) {
if (removeCut(fCuts->At(i)->GetName()))
i = 0;
else
i++;
}
}
int TQCut::getDepth() const{
int retval = 0;
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* cut = this->fCutItr.readNext();
if(!cut) continue;
retval = std::max(retval,cut->getDepth()+1);
}
return retval;
}
int TQCut::getWidth() const{
return this->fCuts->GetEntries();
}
int TQCut::Compare(const TObject* obj) const {
const TQCut* c = dynamic_cast<const TQCut*>(obj);
if(!c) return 0;
int otherDepth = c->getDepth();
int myDepth = this->getDepth();
if(otherDepth > myDepth) return 1;
if(otherDepth < myDepth) return -1;
int otherWidth = c->getWidth();
int myWidth = this->getWidth();
if(otherWidth > myWidth) return -1;
if(otherWidth < myWidth) return 1;
return 0;
}
void TQCut::sort() {
if(this->fCuts->GetEntries() < 2) return;
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* cut = this->fCutItr.readNext();
if(!cut) continue;
cut->sort();
}
this->fCuts->Sort();
}
bool TQCut::isTrivialTrue(const TString& expr){
TString e(TQStringUtils::trim(expr));
if(e == "1" || e == "1.") return true;
return false;
}
bool TQCut::isTrivialFalse(const TString& expr){
TString e(TQStringUtils::trim(expr));
if(e == "0" || e == "0.") return true;
return false;
}
bool TQCut::includeBase() {
if (!fBase) { return false; }
if (!isMergeable() || !fBase->isMergeable()) { return false; }
if(TQCut::isTrivialTrue(this->getCutExpression())){
this->setCutExpression(this->fBase->getCutExpression());
} else if(TQCut::isTrivialTrue(this->fBase->getCutExpression()) || TQCut::isTrivialFalse(this->getCutExpression())){
} else if(TQCut::isTrivialFalse(this->fBase->getCutExpression())){
this->setCutExpression("0.");
} else {
this->setCutExpression(TString::Format("( %s ) && ( %s )",this->fBase->getCutExpression().Data(),this->getCutExpression().Data()));
}
TString newWeightExpression = fBase->getWeightExpression();
if (newWeightExpression.Length() > 0 && this->fWeightObservable){
newWeightExpression.Append(" * "); }
newWeightExpression.Append(this->getWeightExpression());
this->setWeightExpression(newWeightExpression);
return true;
}
bool TQCut::removeCut(const TString& name) {
int iRemove = -1;
for (int iChild = 0; iChild < fCuts->GetEntries(); iChild++) {
if (name.CompareTo(fCuts->At(iChild)->GetName(), TString::kIgnoreCase) == 0) {
iRemove = iChild;
}
}
if (iRemove >= 0) {
TQCut * cutToRemove = (TQCut*)fCuts->At(iRemove);
if (cutToRemove->getNAnalysisJobs() > 0 || !cutToRemove->isMergeable())
return false;
TObjArray * newSubCuts = new TObjArray();
bool success = true;
TObjArray * subCuts = cutToRemove->getCuts();
for (int iSubChild = 0; iSubChild < subCuts->GetEntries(); iSubChild++) {
TQCut * subChild = (TQCut*)(subCuts->At(iSubChild)->Clone());
success = subChild->includeBase() && success;
newSubCuts->Add(subChild);
subChild->setBase(this);
}
if (success) {
delete cutToRemove;
TObjArray * newCuts = new TObjArray();
for (int iChild = 0; iChild < fCuts->GetEntries(); iChild++) {
if (iChild < iRemove || iChild > iRemove)
newCuts->Add(fCuts->At(iChild));
else
for (int iSubChild = 0; iSubChild < newSubCuts->GetEntries(); iSubChild++)
newCuts->Add(newSubCuts->At(iSubChild));
}
delete fCuts;
fCuts = newCuts;
} else {
for (int j = 0; j < newSubCuts->GetEntries(); j++)
delete newSubCuts->At(j);
}
return success;
} else {
for (int iChild = 0; iChild < fCuts->GetEntries(); iChild++) {
if (((TQCut*)fCuts->At(iChild))->removeCut(name)) {
return true;
}
}
return false;
}
}
void TQCut::printCut(const TString& options) {
this->printInternal(options,0);
}
void TQCut::printActiveCutExpression(size_t indent) const {
std::cout << TQStringUtils::repeat("\t",indent);
if(indent > 0) std::cout << "&& ";
std::cout << this->getActiveCutExpression();
std::cout << std::endl;
if(this->fBase){
this->fBase->printActiveCutExpression(indent+1);
}
}
void TQCut::print(const TString& options) {
this->printInternal(options,0);
}
void TQCut::printCuts(const TString& options) {
this->printInternal(options,0);
}
void TQCut::printInternal(const TString& options, int indent) {
const int cColWidth_Total = TQLibrary::getConsoleWidth() - 4;
const int cColWidth_Name = 0.5*cColWidth_Total;
const int cColWidth_nJobs = 10;
const int cColWidth_CutExpr = 0.3*cColWidth_Total;
const int cColWidth_WeightExpr = cColWidth_Total - cColWidth_Name - cColWidth_nJobs - cColWidth_CutExpr;
bool printRecursive = options.Contains("r", TString::kIgnoreCase);
if (indent == 0) {
TString headline = TString::Format("%-*s %*s %-*s %-*s",
cColWidth_Name, "Name", cColWidth_nJobs, "# Jobs",
cColWidth_CutExpr, "Cut Expression", cColWidth_WeightExpr, "Weight Expression");
std::cout << TQStringUtils::makeBoldWhite(headline) << std::endl;
std::cout << TQStringUtils::makeBoldWhite(TQStringUtils::repeat("=", TQStringUtils::getWidth(headline))) << std::endl;
}
TString line;
line.Append(TQStringUtils::fixedWidth(TString::Format("%*s%s", indent, "", GetName()), cColWidth_Name, "l."));
line.Append(" ");
line.Append(TQStringUtils::fixedWidth(TString::Format("%d", getNAnalysisJobs()), cColWidth_nJobs,"r"));
line.Append(" ");
line.Append(TQStringUtils::fixedWidth(this->getCutExpression(), cColWidth_CutExpr, "l."));
line.Append(" ");
line.Append(TQStringUtils::fixedWidth(this->getWeightExpression(), cColWidth_WeightExpr, "l."));
std::cout << line.Data() << std::endl;
if (printRecursive) {
TQIterator itr(fCuts);
while(itr.hasNext()){
TQCut* c = dynamic_cast<TQCut*>(itr.readNext());
if(!c) continue;
c->printInternal(options, indent + 1);
}
}
}
void TQCut::writeDiagramHeader(std::ostream & os, TQTaggable& tags){
bool standalone = tags.getTagBoolDefault("standalone",false);
if(standalone) os << "\\documentclass{standalone}" << std::endl << "\\usepackage{tikz}" << std::endl << "\\usepackage{underscore}" << std::endl << "\\usepackage[T1]{fontenc}" << std::endl << "\\usetikzlibrary{positioning,arrows}" << std::endl << "\\begin{document}" << std::endl;
TString format = tags.getTagStringDefault("format","tikz");
if(format == "tikz"){
os << "\\begingroup" << std::endl;
os << "\\tikzstyle{block} = [rectangle, draw, fill=" << tags.getTagStringDefault("nodes.color","blue") << "!" << tags.getTagIntegerDefault("nodes.opacity",20) << ", text width=" << tags.getTagStringDefault("nodes.width","10em") <<", inner sep=" << tags.getTagStringDefault("nodes.padding","2.5pt") << ", text centered, rounded corners, minimum height=" << tags.getTagStringDefault("nodes.height","2em") << "]" << std::endl;
os << "\\tikzstyle{job} = [rectangle, draw, fill=" << tags.getTagStringDefault("jobs.color","red") << "!" << tags.getTagIntegerDefault("jobs.opacity",20) << ", inner sep=" << tags.getTagStringDefault("jobs.padding","2.5pt") << ", rounded corners, font={\\tiny}]" << std::endl;
os << "\\tikzstyle{line} = [draw, -latex']" << std::endl;
os << std::endl;
os << "\\begin{tikzpicture}[" << "]" << std::endl;
}
}
void TQCut::writeDiagramFooter(std::ostream & os, TQTaggable& tags){
bool standalone = tags.getTagBoolDefault("standalone",false);
TString format = tags.getTagStringDefault("format","tikz");
if(format == "tikz"){
os << "\\end{tikzpicture}" << std::endl;
os << "\\endgroup" << std::endl;
}
if(standalone) os << "\\end{document}" << std::endl;
}
TString TQCut::getNodeName(){
TString name(this->GetName());
name.ToLower();
return TQStringUtils::makeValidIdentifier(name,TQStringUtils::lowerLetters+TQStringUtils::numerals);
}
int TQCut::writeDiagramText(std::ostream& os, TQTaggable& tags, TString pos){
TString format = tags.getTagStringDefault("format","tikz");
if(format == "tikz"){
os << "\\node [block";
if(!pos.IsNull()) os << ", " << pos;
TString title = TQStringUtils::isEmpty(this->GetTitle()) ? this->GetName() : this->GetTitle();
if(title.Contains("\\") && !title.Contains("$")) title="$"+title+"$";
os << "] (" << this->getNodeName() << ") {" << title << "};" << std::endl;
} else {
WARNclass("unknown format '%s'!",format.Data());
return 0;
}
TQCutIterator itr(fCuts);
int width = 0;
pos = "below=of " + this->getNodeName();
TString anchor = "south";
while(itr.hasNext()){
TQCut* c = itr.readNext();
if(!c) continue;
int newwidth = c->writeDiagramText(os,tags,pos);
os << "\\path [line] (node cs:name=" << this->getNodeName() << ", anchor=" << anchor << ") -| (node cs:name=" << c->getNodeName() << ", anchor=north);" << std::endl;
pos = TString::Format("right=%d*%s+%d*(%s+2*%s) of %s",newwidth,tags.getTagStringDefault("nodes.distance","0.1cm").Data(),newwidth-1,tags.getTagStringDefault("nodes.width","10em").Data(),tags.getTagStringDefault("nodes.padding","2.5pt").Data(),c->getNodeName().Data());
anchor = "east";
width += newwidth;
}
if(tags.getTagBoolDefault("showJobs",false)){
TQAnalysisJobIterator itr(fAnalysisJobs);
TString tmppos("below = 1mm of ");
tmppos.Append(this->getNodeName());
tmppos.Append(".south east");
while(itr.hasNext()){
TQAnalysisJob* aj = itr.readNext();
if(!aj) continue;
TString name = this->getNodeName() + ".job" + TString::Format("%d",itr.getLastIndex());
os << "\\node[job, " << tmppos << "] (" << name << ") {" << aj->GetName() << "};" << std::endl;
tmppos = "below = 1mm of " + name + ".south";
}
}
return std::max(width,1);
}
bool TQCut::writeDiagramToFile(const TString& filename, const TString& options){
TQTaggable tags(options);
return this->writeDiagramToFile(filename,tags);
}
bool TQCut::writeDiagramToFile(const TString& filename, TQTaggable& tags){
if(filename.IsNull()) return false;
TQUtils::ensureDirectoryForFile(filename);
std::ofstream of(filename.Data());
if(!of.is_open()){
ERRORclass("unable to open file '%s'",filename.Data());
return false;
}
TQCut::writeDiagramHeader(of,tags);
this->writeDiagramText(of,tags);
TQCut::writeDiagramFooter(of,tags);
return true;
}
bool TQCut::printDiagram(const TString& options){
TQTaggable tags(options);
return this->printDiagram(tags);
}
bool TQCut::printDiagram(TQTaggable& tags){
TQCut::writeDiagramHeader(std::cout,tags);
this->writeDiagramText(std::cout,tags);
TQCut::writeDiagramFooter(std::cout,tags);
return true;
}
TString TQCut::writeDiagramToString(TQTaggable& tags){
std::stringstream of;
TQCut::writeDiagramHeader(of,tags);
this->writeDiagramText(of,tags);
TQCut::writeDiagramFooter(of,tags);
TString retval(of.str().c_str());
return retval;
}
int TQCut::dumpToFolder(TQFolder * folder) {
if (!folder)
return 0;
int nCuts = 1;
TQFolder * cutFolder = folder->getFolder(TString::Format("%s+", GetName()));
if (!cutFolder)
return 0;
cutFolder->setTagString(".name",this->GetName());
cutFolder->setTagString(".title",this->GetTitle());
cutFolder->setTagString(".cutExpression",this->getCutExpression());
cutFolder->setTagString(".weightExpression",this->getWeightExpression());
cutFolder->setTagInteger(".nAnalysisJobs",this->getNAnalysisJobs());
if ( this->getSkipAnalysisJobsGlobal() ) cutFolder->setTagBool(".skipJobs",true);
if ( fPrintResult != 0 ) cutFolder->setTagInteger(".printResult",fPrintResult);
TQIterator itr(fCuts);
while (itr.hasNext()){
TQCut* c = dynamic_cast<TQCut*>(itr.readNext());
if(!c) continue;
nCuts += c->dumpToFolder(cutFolder);
}
return nCuts;
}
TQCut * TQCut::getCut(const TString& name) {
if (name.CompareTo(this->GetName()) == 0)
return this;
TQCut * found = 0;
this->fCutItr.reset();
while(this->fCutItr.hasNext() && !found){
TQCut* c = this->fCutItr.readNext();
if(c) found = c->getCut(name);
}
return found;
}
void TQCut::getMatchingCuts(TObjArray& matchingCuts, const TString& name) {
TObjArray name_segments;
TQIterator itr(TQStringUtils::tokenize(name, "/"),true);
while (itr.hasNext()) {
name_segments.Add(itr.readNext());
}
getMatchingCuts(matchingCuts, name_segments);
}
std::vector<TString> TQCut::getCutNames(const TString& cutName) {
std::vector<TString> cutnames;
TObjArray matchingCuts;
getMatchingCuts(matchingCuts, cutName);
for (int i = 0; i < matchingCuts.GetEntries(); i++) {
TQCut* fullcut = dynamic_cast<TQCut*>(matchingCuts.At(i));
cutnames.push_back(fullcut->GetName());
}
return cutnames;
}
void TQCut::propagateMatchingCuts(TObjArray& matchingCuts, const TObjArray& name_segments, int offset) {
this->fCutItr.reset();
while(this->fCutItr.hasNext()) {
TQCut* c = this->fCutItr.readNext();
if (c) {
c->getMatchingCuts(matchingCuts, name_segments, offset);
}
}
}
void TQCut::getMatchingCuts(TObjArray& matchingCuts, const TObjArray& name_segments, int offset) {
if (offset >= name_segments.GetEntries()) {
return;
}
TObjString* first_obj = dynamic_cast<TObjString*>(name_segments.At(0 + offset));
if (!first_obj) {
return;
}
TString first = first_obj->GetString();
DEBUGclass("%s: current token: %s; offset: %d", this->GetName(), first.Data(), offset);
if (first.CompareTo("*") == 0) {
if (offset + 1 < name_segments.GetEntries()) {
DEBUGclass("%s: parsed asterisk; propagate next segment", this->GetName());
getMatchingCuts(matchingCuts, name_segments, offset + 1);
if (offset != 0) {
DEBUGclass("%s: propagate asterisk", this->GetName());
propagateMatchingCuts(matchingCuts, name_segments, offset);
}
} else {
DEBUGclass("%s: parsed asterisk; asterisk is final part", this->GetName());
if (matchingCuts.IndexOf(this) == -1) {
matchingCuts.Add(this);
}
DEBUGclass("%s: added <----", this->GetName());
if (offset != 0) {
DEBUGclass("%s: propagate asterisk", this->GetName());
propagateMatchingCuts(matchingCuts, name_segments, offset);
}
}
} else if (first.CompareTo("?") == 0) {
if (offset + 1 < name_segments.GetEntries()) {
DEBUGclass("%s: parsed question mark; propagate next segment", this->GetName());
if (isResidualMatchingSegmentOptional(name_segments, offset)) {
DEBUGclass("%s: residual is optional; added <----", this->GetName());
if (matchingCuts.IndexOf(this) == -1) {
matchingCuts.Add(this);
}
}
propagateMatchingCuts(matchingCuts, name_segments, offset + 1);
} else {
DEBUGclass("%s: parsed question mark; wildcard is final part", this->GetName());
if (matchingCuts.IndexOf(this) == -1) {
matchingCuts.Add(this);
}
DEBUGclass("%s: added <----", this->GetName());
}
} else {
DEBUGclass("%s: regular path segment (%s)", this->GetName(), first.Data());
if (TQStringUtils::matches(this->GetName(), first)) {
if (offset + 1 < name_segments.GetEntries()) {
if (isResidualMatchingSegmentOptional(name_segments, offset)) {
DEBUGclass("%s: residual is optional; added <----", this->GetName());
if (matchingCuts.IndexOf(this) == -1) {
matchingCuts.Add(this);
}
}
DEBUGclass("%s: matched; propagate next segment", this->GetName());
propagateMatchingCuts(matchingCuts, name_segments, offset + 1);
} else {
DEBUGclass("%s: this was final; added <----", this->GetName());
if (matchingCuts.IndexOf(this) == -1) {
matchingCuts.Add(this);
}
}
}
}
if (offset == 0) {
DEBUGclass("%s: offset==0, propagate recursively", this->GetName());
propagateMatchingCuts(matchingCuts, name_segments, 0);
}
}
bool TQCut::isResidualMatchingSegmentOptional(const TObjArray& name_segments, int offset) {
for (int i = offset + 1; i < name_segments.GetEntries(); i++) {
TObjString* obj = dynamic_cast<TObjString*>(name_segments.At(i));
if (!obj) { continue; }
TString seg = obj->GetString();
if (seg.CompareTo("*") != 0) {
return false;
}
}
return true;
}
TObjArray * TQCut::getCuts() {
return fCuts;
}
TObjArray * TQCut::getJobs() {
return fAnalysisJobs;
}
void TQCut::setCuts(TObjArray* cuts){
fCuts = cuts;
}
void TQCut::printAnalysisJobs(const TString& options) {
TQAnalysisJobIterator itr(this->fAnalysisJobs);
while(itr.hasNext()){
TQAnalysisJob* job = itr.readNext();
job->print(options);
}
}
TObjArray * TQCut::getListOfCuts() {
TObjArray * result = new TObjArray();
result->Add(this);
if(!this->fCuts){
ERRORclass("unable to retrieve child list from cut '%s'!",this->GetName());
return result;
}
TQCutIterator itr(fCuts);
while(itr.hasNext()){
TQCut* c = itr.readNext();
if(!c) continue;
TObjArray* list = c->getListOfCuts();
if(!list){
ERRORclass("unable to retrieve child list from cut '%s'!",c->GetName());
continue;
}
result->AddAll(list);
delete list;
}
return result;
}
TObjArray* TQCut::getOwnBranches() {
if(!this->fSample){
throw std::runtime_error(TString::Format("TQCut '%s': cannot retrieve branches on uninitialized object!", this->GetName()).Data());
}
TObjArray* branchNames = new TObjArray();
if(this->fCutObservable){
DEBUGclass("retrieving branches from cutObservable %s %s", this->getCutExpression().Data(), this->fCutObservable->ClassName());
TCollection* cutBranches = this->fCutObservable->getBranchNames();
if(cutBranches){
cutBranches->SetOwner(false);
branchNames->AddAll(cutBranches);
delete cutBranches;
}
}
if(this->fWeightObservable){
DEBUGclass("retrieving branches from weightObservable %s %s", this->getWeightExpression().Data(), this->fWeightObservable->ClassName());
TCollection* weightBranches = this->fWeightObservable->getBranchNames();
if(weightBranches){
weightBranches->SetOwner(false);
branchNames->AddAll(weightBranches);
delete weightBranches;
}
}
if (this->fVariantStack.size()>0) {
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
if (variant.cutObs) {
TCollection* varCutBranches = variant.cutObs->getBranchNames();
if(varCutBranches){
varCutBranches->SetOwner(false);
branchNames->AddAll(varCutBranches);
delete varCutBranches;
}
}
if (variant.weightObs) {
TCollection* varWeightBranches = variant.weightObs->getBranchNames();
if(varWeightBranches){
varWeightBranches->SetOwner(false);
branchNames->AddAll(varWeightBranches);
delete varWeightBranches;
}
}
}
}
return branchNames;
}
TObjArray* TQCut::getListOfBranches() {
TString expr;
DEBUGclass("getting own branches");
TObjArray* branchNames = this->getOwnBranches();
DEBUGclass("getting analysis job branches");
{
if ( (!this->fSkipAnalysisJobs) && (!this->fSkipAnalysisJobsGlobal) ) {
if (this->fVariantStack.size()==0) {
TQAnalysisJobIterator itr(fAnalysisJobs);
while(itr.hasNext()){
TQAnalysisJob* job = itr.readNext();
if(!job) continue;
TObjArray* bNames = job -> getBranchNames();
#ifdef _DEBUG_
if(bNames && bNames->GetEntries()>0){
DEBUGclass("recieved list of branches from analysis job '%s':",job->GetName());
bNames->Print();
} else {
DEBUGclass("recieved empty list of branches from analysis job '%s'",job->GetName());
}
#endif
if(bNames){
branchNames -> AddAll(bNames);
bNames->SetOwner(false);
delete bNames;
}
}
} else {
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
if (!anaJob) continue;
TObjArray* bNames = anaJob->getBranchNames();
if (bNames) {
branchNames -> AddAll(bNames);
bNames->SetOwner(false);
delete bNames;
}
}
}
}
}
}
DEBUGclass("getting sub-cut branches");
{
TQCutIterator itr(fCuts);
while(itr.hasNext()){
TQCut* cut = itr.readNext();
if(!cut) continue;
TObjArray* bNames = cut -> getListOfBranches();
if(bNames){
branchNames ->AddAll(bNames);
bNames->SetOwner(false);
delete bNames;
}
}
}
DEBUGclass("merging branches");
TQToken* tok = this->fSample ? this->fSample->getTreeToken() : NULL;
if(tok){
tok->setOwner(this);
TTree* tree = (TTree*)(tok->getContent());
if(tree){
TQIterator itr(branchNames);
while(itr.hasNext()){
TObject* obj = itr.readNext();
TString name(obj->GetName());
if(name.First('*') == kNPOS && !tree->FindBranch(obj->GetName())){
branchNames->Remove(obj);
delete obj;
}
}
}
this->fSample->returnTreeToken(tok);
}
branchNames -> Compress();
for (int iName = 0; iName < branchNames -> GetEntries(); iName++) {
TString name1 = branchNames -> At(iName)->GetName();
for (int jName = branchNames->GetEntries()-1; jName > iName; jName--) {
TString name2 = branchNames -> At(jName)->GetName();
if (name1 == name2) {
branchNames -> RemoveAt(jName);
}
}
branchNames -> Compress();
}
return branchNames;
}
bool TQCut::addAnalysisJob(TQAnalysisJob * newJob_, const TString& cuts_) {
if (!newJob_) { return false; }
if (cuts_.IsNull()) {
TQAnalysisJob * newJob = newJob_->getClone();
newJob->setCut(this);
newJob->SetTitle(TString::Format("%s @ %s", newJob->IsA()->GetName(), this->GetName()));
fAnalysisJobs->Add(newJob);
return true;
}
TString cuts = TQStringUtils::trim(cuts_);
bool success = true;
TQIterator itr(TQStringUtils::tokenize(cuts_), true);
while(itr.hasNext()){
TObject* obj = itr.readNext();
if(!obj) continue;
TString cutName = TQStringUtils::trim(obj->GetName());
TObjArray matchingCuts;
getMatchingCuts(matchingCuts, cutName);
if (matchingCuts.GetEntries() == 0) {
ERRORclass("unable to find cut '%s'",cutName.Data());
success = false;
}
for (int i = 0; i < matchingCuts.GetEntries(); i++) {
TQCut* cut = dynamic_cast<TQCut*>(matchingCuts.At(i));
if (!cut) {
continue;
}
if (!cut->addAnalysisJob(newJob_)) {
success = false;
}
}
}
return success;
}
bool TQCut::executeAnalysisJobs(double weight) {
if (this->fSkipAnalysisJobs || this->fSkipAnalysisJobsGlobal) {
return true;
}
DEBUGclass("executing %d analysis jobs at cut '%s'",fAnalysisJobs->GetEntriesFast(),this->GetName());
bool success = true;
this->fJobItr.reset();
while(this->fJobItr.hasNext()){
TQAnalysisJob* j = this->fJobItr.readNext();
if(!j){
throw std::runtime_error("encountered NULL job in execute!");
}
DEBUGclass("executing job '%s'",j->GetName());
success = j->execute(weight) && success;
DEBUGclass("done executing job!");
}
return success;
}
int TQCut::getNAnalysisJobs() {
return fAnalysisJobs->GetEntriesFast();
}
bool TQCut::passed(bool doPrint) const {
if(!fCutObservable){
if (doPrint) INFOclass("[TreeIndex: %ld] Cut '%s' passed (no Observable assinged)", this->fTree?this->fTree->GetReadEntry():-1, this->GetName());
return true;
}
DEBUGclass("checking '%s'...",this->GetName());
const double val = fCutObservable->getValue();
if (doPrint) {
INFOclass("[TreeIndex: %ld] Cut '%s' %s, cutObservable evaluates to %f", this->fTree?this->fTree->GetReadEntry():-1, this->GetName(), val!=0 ? "passed" : "failed", val);
}
return (val != 0);
return false;
}
bool TQCut::passedGlobally() const {
return (!fBase || fBase->passedGlobally()) && this->passed();
}
double TQCut::getWeight(bool doPrint) const {
if (fWeightObservable){
DEBUGclass("retrieving weight for '%s'...",this->GetName());
if (doPrint) {
double weight = fWeightObservable->getValue();
INFOclass(" [TreeIndex: %ld] Cut '%s' applies (multiplicative) weight %f", this->fTree?this->fTree->GetReadEntry():-1, this->GetName(), weight);
return weight;
}
return fWeightObservable->getValue();
}
if (doPrint) {
INFOclass(" [TreeIndex: %ld] Cut '%s' applies (multiplicative) weight %f (no weight Observable assigned)", this->fTree?this->fTree->GetReadEntry():-1, this->GetName(), 1.0);
}
return 1.;
}
double TQCut::getGlobalWeight() const {
return getWeight() * (fBase ? fBase->getGlobalWeight() : 1.);
}
bool TQCut::skipAnalysisJobs(TQSampleFolder * sf) {
if (!sf) {
return false;
}
TString skipList;
if (!sf->getTagString("~.cc.skipAnalysisJobs", skipList)) {
return false;
}
TQIterator itr(TQStringUtils::tokenize(skipList, ",", true), true);
while (itr.hasNext()) {
TString item = itr.readNext()->GetName();
if ((item.CompareTo("*") == 0) || (TQStringUtils::removeTrailing(item, "*", 1) && this->isDescendantOf(item))
|| (item.CompareTo(this->GetName()) == 0)) {
sf->setTagInteger(TString::Format(".cc.nSkippedAnalysisJobs.%s", this->GetName()),
fAnalysisJobs->GetEntriesFast());
return true;
}
}
return false;
}
bool TQCut::isDescendantOf(TString cutName) {
if (fBase) {
if (cutName.CompareTo(fBase->GetName()) == 0) {
return true;
} else {
return fBase->isDescendantOf(cutName);
}
} else {
return false;
}
}
bool TQCut::initializeObservables() {
DEBUGclass("\tcut: %s",this->fCutExpression.Data());
DEBUGclass("\tweight:%s",this->fWeightExpression.Data());
if(!this->fCutExpression.IsNull() ){
this->fCutObservable = TQObservable::getObservable(this->fCutExpression,this->fSample);
if(!this->fCutObservable) return false;
if(!fCutObservable->initialize(this->fSample)){
ERRORclass("Failed to initialize cut observable with expression '%s' for cut '%s'.",this->fCutExpression.Data(),this->GetName());
this->fCutObservable = NULL;
return false;
}
DEBUGclass("cut '%s' with expression '%s' has retrieved cut observable '%s' with expression '%s'",this->GetName(),this->fCutExpression.Data(),this->fCutObservable->GetName(),this->fCutObservable->getExpression().Data());
}
if(!this->fWeightExpression.IsNull() ){
this->fWeightObservable = TQObservable::getObservable(this->fWeightExpression,this->fSample);
if(!this->fWeightObservable) return false;
if(!fWeightObservable->initialize(this->fSample)){
ERRORclass("Failed to initialize weight observable with expression '%s' for cut '%s'.",this->fWeightExpression.Data(),this->GetName());
this->fWeightObservable = NULL;
this->fCutObservable = NULL;
return false;
}
DEBUGclass("cut '%s' with expression '%s' has retrieved weight observable '%s' with expression '%s'",this->GetName(),this->fWeightExpression.Data(),this->fWeightObservable->GetName(),this->fWeightObservable->getExpression().Data());
}
return true;
}
bool TQCut::finalizeObservables() {
bool retval = true;
if (fCutObservable){
DEBUGclass("finalizing observable '%s'",this->fCutObservable->GetName());
if(!fCutObservable->finalize()) retval = false;
this->fCutObservable = NULL;
}
if (fWeightObservable){
DEBUGclass("finalizing observable '%s'",this->fWeightObservable->GetName());
if(!fWeightObservable->finalize()) retval = false;
this->fWeightObservable = NULL;
}
return retval;
}
bool TQCut::initializeVariants(TQSample* sample, bool hasNewVariantLayer) {
bool success = true;
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
variant.nominalSFs.push_back(sample);
if (!hasNewVariantLayer) {
TQSample* variantSF = variant.variantSFs.back()->getSample(sample->GetName());
if (!variantSF) {
variant.variantSFs.back()->print("dt");
throw std::runtime_error(TString::Format("[Cut: %s] Failed to find variant SampleFolder with name '%s' within variant SampleFolder with path '%s'. Contents of the latter are printed above",this->GetName(),sample->GetName(), variant.variantSFs.back()->getPath().Data()).Data());
}
variant.variantSFs.push_back(variantSF);
}
TQSample* asSample = dynamic_cast<TQSample*>(variant.variantSFs.back());
asSample->setProxySample(sample);
if (!asSample) {
throw std::runtime_error("Internal logic error: somehow the top of the initialization stack is not a TQSample while trying to initialize on one...");
}
if ( (!this->fSkipAnalysisJobs) && (!this->fSkipAnalysisJobsGlobal) ) {
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
if (!anaJob->initialize(asSample )) {
success = false;
}
}
}
if(!variant.cutExpression.IsNull() ){
variant.cutObs = TQObservable::getObservable(variant.cutExpression,asSample);
if(!variant.cutObs) return false;
if(!variant.cutObs->initialize(asSample)){
ERRORclass("Failed to initialize variant cut observable with expression '%s' for cut '%s'.",variant.cutExpression.Data(),this->GetName());
variant.cutObs = nullptr;
return false;
}
}
if(!variant.weightExpression.IsNull() ){
variant.weightObs = TQObservable::getObservable(variant.weightExpression,asSample);
if(!variant.weightObs) return false;
if(!variant.weightObs->initialize(asSample)){
ERRORclass("Failed to initialize variant weight observable with expression '%s' for cut '%s'.",variant.weightExpression.Data(),this->GetName());
variant.weightObs = nullptr;
return false;
}
}
}
return success;
}
bool TQCut::verifyVariantSetup(const std::vector<VariantHandle>& parentHandles) {
bool allGood = ( this->fVariantStack.back()->payload.size() == parentHandles.size() );
if (allGood) {
for (size_t index=0; index < parentHandles.size(); ++index) {
const VariantHandle& myVariant = this->fVariantStack.back()->payload[index];
const VariantHandle& parVariant = parentHandles[index];
allGood = allGood && ( myVariant.variantSFs.back() == parVariant.variantSFs.back() );
allGood = allGood && ( myVariant.nominalSFs.back() == parVariant.nominalSFs.back() );
}
}
if (!allGood) {
throw std::runtime_error(TString::Format("Inconsistent setup of variants between different TQCut instances detected at cut '%s'- please contact experts!", this->GetName()).Data());
}
if(fCuts){
DEBUGclass("iterating over child cuts");
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* c = this->fCutItr.readNext();
if(!c) throw std::runtime_error("encountered NULL cut");
allGood = allGood && c->verifyVariantSetup(this->fVariantStack.back()->payload);
}
}
return allGood;
}
bool TQCut::initialize(TQSample* sample){
TQCut::VariantDefinitionList* variationDefs = getVariantDefinitions(sample);
bool retVal = this->initialize(sample, variationDefs);
delete variationDefs;
variationDefs=nullptr;
return retVal;
}
bool TQCut::initialize(TQSample * sample, const TQCut::VariantDefinitionList* variantDefs) {
if (!sample) {
throw std::runtime_error(TString::Format("cannot initialize cut '%s' with NULL sample",this->GetName()).Data());
return false;
}
TQSampleFolder* previousInit = fInitializationHistory.size()>0 ? fInitializationHistory[fInitializationHistory.size()-1] : nullptr;
if ( previousInit && !(previousInit->areRelated(sample)>0) ) {
throw std::runtime_error(TString::Format("Caught attempt to initialize cut '%s' using sample with path '%s' while it was previously initialized on sample folder '%s'. Either TQCut::finalizeSampleFolder was not called correctly or something went terribly wrong.", this->GetName(), sample->getPath().Data(), previousInit->getPath().Data() ).Data());
return false;
}
if (fTreeToken){
return false;
}
DEBUGclass("retrieving tree");
this->fTreeToken = sample->getTreeToken();
if (!this->fTreeToken){
throw std::runtime_error(TString::Format("unable to initialize cut '%s': unable to obtain tree token",this->GetName()).Data());
DEBUGclass("unable to retrieve tree token");
return false;
}
this->fTreeToken->setOwner(this);
this->fSample = sample;
this->fTree = (TTree*)(fTreeToken->getContent());
if(!fTree){
DEBUGclass("received invalid tree pointer");
sample->returnTreeToken(fTreeToken);
throw std::runtime_error(TString::Format("unable to initialize cut '%s': received invalid tree pointer",this->GetName()).Data());
return false;
}
bool hasNewVariantLayer = false;
if (variantDefs) {
createVariants(sample, variantDefs);
hasNewVariantLayer = true;
}
this->fSkipAnalysisJobs = skipAnalysisJobs(sample);
if(fCuts){
DEBUGclass("iterating over child cuts");
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* c = this->fCutItr.readNext();
if(!c) throw std::runtime_error("encountered NULL cut");
if(!c->initialize(sample, variantDefs)) throw std::runtime_error(TString::Format("unable to initialize cut '%s'",c->GetName()).Data());
}
}
if (this->fVariantStack.size()==0) {
if ( (!this->fSkipAnalysisJobs) && (!this->fSkipAnalysisJobsGlobal) ) {
DEBUGclass("iterating over analysis jobs for cut '%s'",this->GetName());
TQAnalysisJobIterator itr(fAnalysisJobs);
while(itr.hasNext()){
TQAnalysisJob* job = itr.readNext();
if(!job) throw std::runtime_error("encountered NULL job");
if(!job->initialize(sample)) throw std::runtime_error(TString::Format("unable to initialize analysis job %s at %s",job->GetName(),this->GetName()).Data());
}
}
} else {
this->initializeVariants(sample,hasNewVariantLayer);
if (!this->fVariantResults) {
this->fVariantResults = new VariantResults;
size_t nVariants = this->fVariantStack.back()->payload.size();
this->fVariantResults->resize(nVariants);
if (!this->verifyVariantSetup(this->fVariantStack.back()->payload)) {
return false;
}
} else {
throw std::runtime_error("found already existing VariantResults pointer during cut initialization - this should not happen!");
return false;
}
}
if(!this->initializeObservables()) {
throw std::runtime_error(TString::Format("unable to initialize observables for cut '%s'",this->GetName()).Data());
}
return true;
}
bool TQCut::finalizeVariants() {
bool success = true;
size_t nStacksEmpty = 0;
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
if ( (!this->fSkipAnalysisJobs) && (!this->fSkipAnalysisJobsGlobal) ) {
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
if (!anaJob->finalize()) {
success = false;
}
}
}
if (variant.cutObs) {
success = variant.cutObs->finalize() && success;
variant.cutObs = nullptr;
}
if (variant.weightObs) {
success = variant.weightObs->finalize() && success;
variant.weightObs = nullptr;
}
}
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
TQSample* asSample = dynamic_cast<TQSample*>(variant.variantSFs.back());
if (!asSample) {
throw std::runtime_error("Internal logic error: somehow the top of the initialization stack is not a TQSample while trying to finalize on one...");
}
TQSample* asNominalSample = dynamic_cast<TQSample*>(variant.nominalSFs.back());
if (!asNominalSample) {
throw std::runtime_error("Internal logic error: somehow the top of the nominal initialization stack is not a TQSample while trying to finalize on one...");
}
asSample->mergeTags(asNominalSample);
if (!fBase) asSample->setProxySample(nullptr);
variant.variantSFs.pop_back();
variant.nominalSFs.pop_back();
if (variant.variantSFs.empty()) {
nStacksEmpty++;
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
delete anaJob;
}
}
}
if (nStacksEmpty == this->fVariantStack.back()->payload.size()) {
VariantStackElement* toDelete = this->fVariantStack.back();
this->fVariantStack.pop_back();
delete toDelete;
} else if (nStacksEmpty > 0) {
throw std::runtime_error("Inconsistent stack sizes of variants SampleFolder stacks detected, please inform experts!");
return false;
}
return success;
}
bool TQCut::finalize() {
this->finalizeObservables();
bool success = true;
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* cut = this->fCutItr.readNext();
if(!cut) continue;
if(!cut->finalize()) success = false;
}
if (this->fVariantStack.size()==0) {
if ( (!fSkipAnalysisJobsGlobal) && (!fSkipAnalysisJobs) ) {
this->fJobItr.reset();
while(this->fJobItr.hasNext()){
TQAnalysisJob* job = this->fJobItr.readNext();
if(!job) continue;
if(!job->finalize()) success = false;
}
}
} else {
success = this->finalizeVariants() && success;
if (this->fVariantResults) {
delete this->fVariantResults;
this->fVariantResults = nullptr;
}
}
if(this->fSample){
success = this->fSample->returnToken(this->fTreeToken) && success;
}
this->fTreeToken = 0;
this->fSample = 0;
this->fTree = 0;
return success;
}
bool TQCut::canInitialize(TQSampleFolder* sf) const {
if (!sf) return false;
TQSampleFolder* previousInit = fInitializationHistory.size()>0 ? fInitializationHistory[fInitializationHistory.size()-1] : nullptr;
if ( previousInit && !(previousInit->areRelated(sf)>0) ) {
DEBUGclass("Sample (folder) with path '%s' is not eligible for initializing cut '%s'. The cut was last initialized on '%s'.",sf->getPath().Data(),this->GetName(),previousInit->getPath().Data());
return false;
}
return true;
}
int TQCut::createVariantFolders(TQSampleFolder* sf, const TQCut::VariantDefinitionList* variantDefs) {
if (!variantDefs) return -1;
int nCreated = 0;
for (auto const& [name, varDefs] : *variantDefs) {
TQSampleFolder* base = sf->getBaseSampleFolder();
if (!base) {
throw std::runtime_error("Cannot create variants for the root node of a SampleFolder structure, please ensure that variants are only requested starting from the second folder level!");
}
TString varSFname = TString::Format("%s_%s",sf->GetName(), name.Data());
TQSampleFolder* varSF = base->getSampleFolder(varSFname);
if (!varSF) {
varSF = dynamic_cast<TQSampleFolder*>(sf->copy(varSFname));
varSF->setIsVariant(true);
base->addFolder(varSF);
nCreated++;
}
}
return nCreated;
}
TQCut::VariantStackElement* TQCut::createVariantLayer(const std::vector<VariantHandle>& preceedingHandles, TQSampleFolder* nextSF, const VariantDefinitionList* variantDefs) {
DEBUGclass("%s: function called with %d preceeding handles and %d definitions",this->GetName(),preceedingHandles.size(),variantDefs->size());
VariantStackElement* newElement = new VariantStackElement();
for (const VariantHandle& preHandle : preceedingHandles) {
TQSampleFolder* preVarSF = preHandle.variantSFs.back();
for (auto const& [name, varDefs] : *variantDefs) {
DEBUGclass("%s: creating new handle '%s' at '%s'",this->GetName(),name.Data(),preVarSF->getPath().Data());
VariantHandle newHandle;
for (TQAnalysisJob* preAnaJob : preHandle.analysisJobs) {
if(preAnaJob->isPooling()){
throw std::runtime_error("Cannot create variants on a sample folder with jobs that are pooling at a higher level, please fix your pooling specifications to be more specific than your variants!");
}
TQAnalysisJob* anaJob = preAnaJob->getClone();
TString jobname = TString::Format("%s.%s.%s",preAnaJob->GetName(),this->GetName(), name.Data());
DEBUGclass(" cloning job %s",jobname.Data());
anaJob->SetName(jobname);
anaJob->copyTransientMembersFrom(preAnaJob);
newHandle.analysisJobs.push_back(anaJob);
}
const VariantDefinition* matchingDef = nullptr;
for (const VariantDefinition &vd : varDefs) {
if (TQStringUtils::matchesFilter(this->GetName(), vd.matchingPattern, ",", true)) {
matchingDef = &vd;
break;
}
}
newHandle.cutExpression = preHandle.cutExpression;
newHandle.weightExpression = preHandle.weightExpression;
if (matchingDef && matchingDef->cutExpression.Length()>0) {
newHandle.cutExpression = matchingDef->cutExpression;
}
if (matchingDef && matchingDef->weightExpression.Length()>0) {
newHandle.weightExpression = matchingDef->weightExpression;
}
TQSampleFolder* firstVarFolder = preVarSF->getSampleFolder(TString::Format("%s_%s",nextSF->GetName(), name.Data()));
newHandle.variantSFs.push_back(firstVarFolder);
newHandle.nominalSFs.push_back(nextSF);
newElement->payload.push_back(newHandle);
newElement->variantRoots.push_back(firstVarFolder);
}
}
return newElement;
}
bool TQCut::createVariants(TQSampleFolder* nextSF, const VariantDefinitionList* variantDefs) {
if (!nextSF || !nextSF->getBaseSampleFolder()) {
throw std::runtime_error("Cannot create variants for the root node of a SampleFolder structure!");
return false;
}
if (fVariantStack.size() == 0) {
if (!fBase) {
DEBUGclass("'%s': creating all variant folders at '%s'",this->GetName(),nextSF->getPath().Data());
this->createVariantFolders(nextSF, variantDefs);
}
VariantHandle tempHandle;
std::vector<TQAnalysisJob*> anaJobs;
if (this->fAnalysisJobs) {
TQAnalysisJobIterator itr(this->fAnalysisJobs,false);
while(itr.hasNext()) {
anaJobs.push_back(itr.readNext());
}
}
tempHandle.analysisJobs = anaJobs;
tempHandle.variantSFs.push_back(nextSF->getBaseSampleFolder());
fVariantStack.push_back(this->createVariantLayer( {tempHandle}, nextSF, variantDefs ));
} else {
if (!fBase) {
for (const VariantHandle& preHan : fVariantStack.back()->payload) {
TQSampleFolder* varNominal = preHan.variantSFs.back()->getSampleFolder(nextSF->GetName());
if (!varNominal) {
throw std::runtime_error(TString::Format("Failed to create new layer of variants, could not find subfolder with name '%s' in variant folder '%s'",nextSF->GetName(),preHan.variantSFs.back()->getPath().Data()).Data());
return false;
}
this->createVariantFolders(varNominal, variantDefs);
}
}
fVariantStack.push_back(this->createVariantLayer( fVariantStack.back()->payload, nextSF, variantDefs ));
}
return true;
}
bool TQCut::initializeVariantsSampleFolder(TQSampleFolder* sf, bool hasNewVariantLayer) {
bool success = true;
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
variant.nominalSFs.push_back(sf);
if (!hasNewVariantLayer) {
TQSampleFolder* variantSF = variant.variantSFs.back()->getSampleFolder(sf->GetName());
if (!variantSF) {
variant.variantSFs.back()->print("dt");
throw std::runtime_error(TString::Format("Failed to find variant SampleFolder with name '%s' within variant SampleFolder with path '%s'. Contents of the latter are printed above",sf->GetName(), variant.variantSFs.back()->getPath().Data()).Data());
}
variant.variantSFs.push_back(variantSF);
}
if ( (!this->fSkipAnalysisJobs) && (!this->fSkipAnalysisJobsGlobal) ) {
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
if (!anaJob->initializeSampleFolder(variant.variantSFs.back() )) {
success = false;
}
}
}
}
return success;
}
bool TQCut::initializeSampleFolder(TQSampleFolder* sf){
TQCut::VariantDefinitionList* variationDefs = getVariantDefinitions(sf);
bool retVal = this->initializeSampleFolder(sf, variationDefs);
delete variationDefs;
variationDefs=nullptr;
return retVal;
}
bool TQCut::initializeSampleFolder(TQSampleFolder* sf, const TQCut::VariantDefinitionList* variantDefs){
this->initializeSelfSampleFolder(sf);
if ( !this->canInitialize(sf) ) {
if (!sf) {
WARNclass("Cannot initialize cut '%s' with a nullptr to a TQSampleFolder",this->GetName());
return false;
}
TQSampleFolder* previousInit = this->fInitializationHistory.size()>0 ? fInitializationHistory[fInitializationHistory.size()-1] : nullptr;
throw std::runtime_error(TString::Format("Caught attempt to initialize cut '%s' using sample folder with path '%s' while it was previously initialized on sample folder '%s'. Either TQCut::finalizeSampleFolder was not called or something went terribly wrong.", this->GetName(), sf->getPath().Data(), previousInit==nullptr? "<none>" : previousInit->getPath().Data() ).Data());
return false;
}
fInitializationHistory.push_back(sf);
if (!fBase) {
DEBUGclass("Initializing cut '%s' on sample folder '%s'",this->GetName(),sf->getPath().Data());
}
bool hasNewVariantLayer = false;
if (variantDefs) {
createVariants(sf, variantDefs);
hasNewVariantLayer = true;
}
bool success = true;
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* cut = this->fCutItr.readNext();
if(!cut) continue;
if(!cut->initializeSampleFolder(sf,variantDefs)) success = false;
}
if (this->fVariantStack.size()==0) {
DEBUGclass("%s: initializing sample folder '%s' without variants",this->GetName(),sf->getPath().Data());
if ( (!fSkipAnalysisJobsGlobal) && (!fSkipAnalysisJobs) ) {
this->fJobItr.reset();
while(this->fJobItr.hasNext()){
TQAnalysisJob* job = this->fJobItr.readNext();
if(!job) continue;
if(!job->initializeSampleFolder(sf)) success = false;
}
}
} else {
DEBUGclass("%s: initializing sample folder '%s' with variants",this->GetName(),sf->getPath().Data());
this->initializeVariantsSampleFolder(sf,hasNewVariantLayer);
}
this->fPrintResultCount = 0;
return success;
}
bool TQCut::canFinalize(TQSampleFolder* sf) const {
if (!sf) return false;
if (sf == this->fSample) return true;
for (auto hist : fInitializationHistory) {
if (sf == hist) return true;
}
return false;
}
bool TQCut::finalizeVariantsSampleFolder(TQSampleFolder* sf) {
bool success = true;
size_t nStacksEmpty = 0;
std::set<TQSampleFolder*> uniqueNominals;
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
TQSampleFolder* varSF = variant.variantSFs.back();
if (!(variant.nominalSFs.back() == sf)) {
throw std::runtime_error("Internal logic error: the nominal SampleFolder to finalize for does not match the nominal one in the stack");
}
if ( (!this->fSkipAnalysisJobs) && (!this->fSkipAnalysisJobsGlobal) ) {
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
if (!anaJob->finalizeSampleFolder(varSF)) {
success = false;
}
}
}
if (success) {
sf->setTagBool(".asv.revisit.purge",true);
}
variant.variantSFs.pop_back();
uniqueNominals.insert(variant.nominalSFs.back());
variant.nominalSFs.pop_back();
if (variant.variantSFs.empty()) {
nStacksEmpty++;
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
delete anaJob;
}
}
}
if (nStacksEmpty == this->fVariantStack.back()->payload.size()) {
VariantStackElement* toDelete = this->fVariantStack.back();
if (!fBase) {
for (TQSampleFolder* nom : uniqueNominals) {
if (nom != sf) {
delete nom;
}
}
}
this->fVariantStack.pop_back();
delete toDelete;
} else if (nStacksEmpty > 0) {
throw std::runtime_error("Inconsistent stack sizes of variants SampleFolder stacks detected, please inform experts!");
return false;
}
return success;
}
bool TQCut::finalizeSampleFolder(TQSampleFolder* sf){
if (this->fSample && !this->finalize()) return false;
if (!sf) return false;
this->finalizeSelfSampleFolder(sf);
if (!fBase) {
DEBUGclass("Finalizing cut '%s' on sample folder '%s'. History has %d entries",this->GetName(),sf->getPath().Data(),(int)fInitializationHistory.size());
}
TQSampleFolder* lastInit = fInitializationHistory.size()>0 ? fInitializationHistory[fInitializationHistory.size()-1] : nullptr;
while (lastInit != nullptr && (lastInit->areRelated(sf)<0) ) {
if (!this->finalizeSampleFolder(lastInit)) return false;
lastInit = fInitializationHistory.size()>0 ? fInitializationHistory[fInitializationHistory.size()-1] : nullptr;
}
if ( !lastInit || lastInit != sf ) {
throw std::runtime_error(TString::Format("Caught attempt to finalize cut '%s' using sample folder with path '%s' while it was not initialized on it.", this->GetName(), sf->getPath().Data() ).Data());
return false;
}
if (fInitializationHistory.size()>0) fInitializationHistory.pop_back();
bool success = true;
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* cut = this->fCutItr.readNext();
if(!cut) continue;
if(!cut->finalizeSampleFolder(sf)) success = false;
}
if (this->fVariantStack.size()==0) {
if ( (!fSkipAnalysisJobsGlobal) && (!fSkipAnalysisJobs) ) {
this->fJobItr.reset();
while(this->fJobItr.hasNext()){
TQAnalysisJob* job = this->fJobItr.readNext();
if(!job) continue;
if(!job->finalizeSampleFolder(sf)) success = false;
}
}
} else {
success = finalizeVariantsSampleFolder(sf) && success;
}
return success;
}
TQCut::~TQCut() {
this->finalize();
this->fCuts->Delete();
delete fCuts;
this->fAnalysisJobs->Delete();
delete fAnalysisJobs;
}
void TQCut::clearAnalysisJobs(){
if(fAnalysisJobs)
fAnalysisJobs->Delete();
}
TQCut * TQCut::getSingleCut(TString name, TString excl_pattern) {
TString tmpName = name;
bool addDescendants = TQStringUtils::removeTrailing(tmpName, "*") > 0;
if (tmpName.CompareTo(this->GetName()) == 0) {
TQCut * tmpcut = this->getBase();
TQCut * aggregatedCut = new TQCut(this->GetName());
TString thiscutname = this->GetName();
if (thiscutname.Contains(excl_pattern)) {
aggregatedCut->setCutExpression("1");
aggregatedCut->setWeightExpression("1");
} else {
aggregatedCut->setCutExpression(this->getCutExpression());
if (!(this->getWeightExpression().IsNull()))
aggregatedCut->setWeightExpression(this->getWeightExpression());
else
aggregatedCut->setWeightExpression("1");
}
if (addDescendants)
aggregatedCut->setCuts(this->getCuts());
while (tmpcut) {
TString cutexprorg = aggregatedCut->getCutExpression();
TString cutexprnew = tmpcut->getCutExpression();
TString cutnamenew = tmpcut->GetName();
if (cutnamenew.Contains(excl_pattern)) {
tmpcut = tmpcut->getBase();
continue;
}
if (!cutexprnew.IsNull())
aggregatedCut->setCutExpression("(" + cutexprorg + ")*(" + cutexprnew + ")");
TString wgtexprorg = aggregatedCut->getWeightExpression();
TString wgtexprnew = tmpcut->getWeightExpression();
if (!wgtexprnew.IsNull())
aggregatedCut->setWeightExpression("(" + wgtexprorg + ")*(" + wgtexprnew + ")");
tmpcut = tmpcut->getBase();
}
TString cutexpr = aggregatedCut->getCutExpression();
cutexpr.ReplaceAll(" ","");
return aggregatedCut;
} else {
this->fCutItr.reset();
while (this->fCutItr.hasNext()){
TQCut* c = this->fCutItr.readNext();
if(!c) continue;
TQCut* found = c->getSingleCut(name, excl_pattern);
if(found) return found;
}
return NULL;
}
}
TQSample* TQCut::getSample(){
return this->fSample;
}
TQCut* TQCut::getClone(){
TQCut* clone = nullptr;
if (this->IsA() == TQUniqueCut::Class()) {
TQUniqueCut* asUnique = static_cast<TQUniqueCut*>(this);
clone = new TQUniqueCut(asUnique->GetName(), asUnique->getRunNumberExpression(), asUnique->getEventNumberExpression());
clone->SetTitle(asUnique->GetTitle());
clone->setWeightExpression(asUnique->getWeightExpression());
} else {
clone = new TQCut(this->GetName(),this->GetTitle(),this->getCutExpression(),this->getWeightExpression());
}
clone->setSkipAnalysisJobsGlobal(this->getSkipAnalysisJobsGlobal());
clone->setPrintResults(this->fPrintResult);
this->fJobItr.reset();
while(this->fJobItr.hasNext()){
TQAnalysisJob* j = this->fJobItr.readNext();
clone->addAnalysisJob(j->getClone());
}
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* c = this->fCutItr.readNext();
TQCut* cl = c->getClone();
clone->addCut(cl);
}
return clone;
}
TQCut* TQCut::getCompiledClone(TQTaggable* tags){
if(!tags) return this->getClone();
TQCut* clone = new TQCut(this->GetName(),this->GetTitle(),
tags->replaceInText(this->getCutExpression()),
tags->replaceInText(this->getWeightExpression()));
this->fJobItr.reset();
while(this->fJobItr.hasNext()){
TQAnalysisJob* j = this->fJobItr.readNext();
clone->addAnalysisJob(j->getClone());
}
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
TQCut* c = this->fCutItr.readNext();
TQCut* cl = c->getCompiledClone(tags);
clone->addCut(cl);
}
return clone;
}
void TQCut::analyse(double weight, bool useWeights) {
DEBUGclass("Cut: '%s' with useweights '%d", this->GetName(), useWeights);
bool doPrint = false;
if ( (fPrintResult>fPrintResultCount) || fPrintResult<0 ) {
doPrint = true;
++fPrintResultCount;
}
if (this->fVariantStack.size()==0) {
const bool passed = this->passed(doPrint);
if (!passed) {
return;
}
DEBUGclass("passed cut '%s' with expression '%s'",this->GetName(),this->fCutObservable ? this->fCutObservable->getActiveExpression().Data() : "");
if (useWeights) {
weight *= this->getWeight(doPrint);
}
this->executeAnalysisJobs(weight);
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
this->fCutItr.readNext()->analyse(weight, useWeights);
}
} else {
this->analyseVariants(nullptr, useWeights, weight);
}
}
void TQCut::analyseVariants(const VariantResults* preceedingResults_, bool useWeights, double baseWeight) {
const VariantResults* preceedingResults = preceedingResults_;
if (!preceedingResults) {
this->fVariantResults->reset(baseWeight);
preceedingResults = this->fVariantResults;
}
size_t variantIndex = 0;
bool fallBackPass = this->passed(false);
LazyWeight fallBackWeight(this);
for (VariantHandle& variant : this->fVariantStack.back()->payload) {
if (variant.cutObs) {
this->fVariantResults->passed[variantIndex] = preceedingResults->passed[variantIndex] && variant.cutObs->getValue();
} else {
this->fVariantResults->passed[variantIndex] = fallBackPass && preceedingResults->passed[variantIndex];
}
if (this->fVariantResults->passed[variantIndex]) {
if (variant.weightObs) {
this->fVariantResults->weight[variantIndex] = preceedingResults->weight[variantIndex] * variant.weightObs->getValue();
} else {
this->fVariantResults->weight[variantIndex] = double(fallBackWeight) * preceedingResults->weight[variantIndex];
}
if (this->fSkipAnalysisJobs || this->fSkipAnalysisJobsGlobal) {
continue;
}
bool success = true;
for (TQAnalysisJob* anaJob : variant.analysisJobs) {
if(!anaJob){
throw std::runtime_error("encountered NULL job in execute!");
}
DEBUGclass("executing job '%s'",anaJob->GetName());
success = anaJob->execute(this->fVariantResults->weight[variantIndex]) && success;
DEBUGclass("done executing job!");
}
if (!success) {
throw std::runtime_error("There was an error executing one or more AnalysisJobs at Cut '%s', variant '%s'");
}
}
variantIndex++;
}
if (this->fVariantResults->passAny() || this->fVariantResults->passed.size()==0) {
this->fCutItr.reset();
while(this->fCutItr.hasNext()){
this->fCutItr.readNext()->analyseVariants(this->fVariantResults, useWeights);
}
}
}
bool TQCut::initializeSelfSampleFolder(TQSampleFolder*){ return true;}
bool TQCut::finalizeSelfSampleFolder (TQSampleFolder*){ return true;}