#include "TCanvas.h"
#include "TStyle.h"
#include "TH1.h"
#include "TH2.h"
#include "TProfile.h"
#include "TLegend.h"
#include "TLatex.h"
#include "TLine.h"
#include "QFramework/TQIterator.h"
#include "QFramework/TQDefaultPlotter.h"
#include "QFramework/TQLibrary.h"
#define DEFAULTTEXTSIZE 0.04
ClassImp(TQDefaultPlotter)
namespace {
enum PlotType {
Stack,Profile,Contours,Heatmap,Migration,Scatter
};
enum SubPlotType {
None,Ratio,DataMinusBackground,CutOptimization,CutOptimizationHighpass,CutOptimizationLowpass
};
}
TCanvas * TQDefaultPlotter::makePlot(TQTaggable& tags){
bool verbose = tags.getTagBoolDefault("verbose",false);
gStyle->SetOptStat(false);
gStyle->SetOptFit(false);
gStyle->SetOptTitle(false);
if (!tags.hasTag("style.textSize"))
tags.setTagDouble("style.textSize", DEFAULTTEXTSIZE);
if (!tags.hasTag("style.legend.textSize"))
tags.setTagDouble("style.legend.textSize", 0.85*tags.getTagDoubleDefault("style.textSize",DEFAULTTEXTSIZE));
if (!tags.hasTag("geometry.legend.nRows"))
tags.setTagDouble("geometry.legend.nRows",4.);
if(verbose) VERBOSEclass("collecting histograms");
TObjArray* histos = this->collectHistograms(tags);
if(!histos) return NULL;
TQTH1Iterator histitr(histos);
int nEntries = 0;
PlotType plottype = Stack;
while(histitr.hasNext()){
TH1* hist = histitr.readNext();
nEntries += hist->GetEntries();
if(dynamic_cast<TH2*>(hist)){
if(tags.hasTagString("style.heatmap")){
plottype=Heatmap;
} else if(tags.hasTagString("style.migration")){
plottype=Migration;
} else if(tags.hasTagString("style.scatter")) {
plottype=Scatter;
} else {
plottype=Contours;
}
}
if(dynamic_cast<TProfile*>(hist)) plottype=Profile;
}
if(nEntries < 1){
WARNclass("refusing to plot histogram - no entries!");
return NULL;
}
TString subplotTypeString;
std::vector<TString> subplot_strings;
std::vector<SubPlotType> subplots;
for(auto subplot:tags.getTagVString("style.subPlot")){
subplot.ToLower();
subplot_strings.push_back(subplot);
}
for(const auto& subplotString:subplot_strings){
SubPlotType subplot = None;
if(subplotString == "ratio") subplot = Ratio;
else if(subplotString == "cutopt") subplot = CutOptimization;
else if(subplotString == "cutopt-lowpass") subplot = CutOptimizationLowpass;
else if(subplotString == "cutopt-highpass") subplot = CutOptimizationHighpass;
else if(subplotString == "dmb") subplot = DataMinusBackground;
else if(subplotString == "none") subplot = None;
else {
WARNclass("unknown subplot type '%s'",subplotTypeString.Data());
}
if (plottype != Stack)
continue;
if(subplot != None)
subplots.push_back(subplot);
}
double subpadSize = .35;
if(!tags.getTagDouble("geometry.sub.height",subpadSize)){
tags.setTagDouble("geometry.sub.height",subpadSize);
}
double midpadSize = .20;
if(!tags.getTagDouble("geometry.mid.height",midpadSize)){
tags.setTagDouble("geometry.mid.height",midpadSize);
}
double mainPadScaling = 1.;
double scaleCorrection = (subpadSize + ((subplots.size()-1) * midpadSize));
tags.setTagDouble("geometry.sub.scaleCorrection",scaleCorrection);
tags.setTagDouble("geometry.mid.scaleCorrection",scaleCorrection);
tags.setTagDouble("geometry.sub.yscaling",1./subpadSize);
tags.setTagDouble("geometry.sub.xscaling",1.);
tags.setTagDouble("geometry.mid.yscaling",1./midpadSize);
tags.setTagDouble("geometry.mid.xscaling",1.);
tags.setTagDouble("geometry.sub.margins.top", tags.getTagDoubleDefault("geometry.sub.margins.top", 0.0));
tags.setTagDouble("geometry.mid.margins.top", tags.getTagDoubleDefault("geometry.mid.margins.top", 0.0));
tags.setTagDouble("geometry.mid.margins.bottom", tags.getTagDoubleDefault("geometry.mid.margins.bottom", 0.0));
tags.setTagDouble("geometry.sub.margins.bottom", tags.getTagDoubleDefault("geometry.sub.margins.bottom", 0.1/scaleCorrection));
tags.setTagBool("style.mid.xAxis.showLabels",false);
tags.setTagBool("style.mid.xAxis.showTitle",false);
if(subplots.size() > 0){
tags.setTagDouble("geometry.main.scaling",mainPadScaling);
tags.setTagDouble("geometry.main.margins.bottom", scaleCorrection / mainPadScaling);
tags.setTagBool("style.main.xAxis.showLabels",false);
tags.setTagBool("style.main.xAxis.showTitle",false);
}
TH1* hMaster = this->getObject<TH1>("Graph_master");
if (!hMaster){
if(verbose) VERBOSEclass("no master histogram found, quitting");
return NULL;
}
this->setAxisLabels(tags);
this->applyGeometry(tags,hMaster, "main");
this->applyStyle (tags,hMaster, "main");
TH1* hTotalBkg = this->getObject<TH1>("totalBkg");
if(hTotalBkg){
this->applyStyle (tags,hTotalBkg,"main.totalBkg");
}
TH1* hTotalSig = this->getObject<TH1>("totalSig");
if(hTotalSig){
this->applyStyle (tags,hTotalSig,"main.totalSig");
}
if(verbose) VERBOSEclass("creating canvas");
TCanvas * canvas = this->createCanvas(tags);
if(!canvas) return NULL;
canvas->Draw();
TPad* pad = this->getPad("main");
if(!pad){
delete canvas;
return NULL;
}
canvas->cd();
if(plottype == Stack){
this->stackHistograms(tags,"stack");
}
if(verbose) VERBOSEclass("creating legend");
if(tags.getTagBoolDefault("style.useLegendPad",false)){
canvas->cd();
TPad * legendPad = this->createPad(tags,"legend");
legendPad->Draw();
this->getPad("legend");
if(!legendPad) return NULL;
this->makeLegend(tags,histos);
} else {
this->makeLegend(tags,histos);
}
if(verbose) VERBOSEclass("setting labels");
TString label;
tags.setGlobalOverwrite(true);
int labelidx = 0;
if (tags.getTagString("labels.lumi", label)){
labelidx++;
tags.setTagString(TString::Format("labels.%d",labelidx), label);
}
if (tags.getTagString("labels.process", label)){
labelidx++;
tags.setTagString(TString::Format("labels.%d",labelidx), label);
}
if(tags.hasTag("errors.showSys")){
if(verbose) VERBOSEclass("including systematics");
this->includeSystematics(tags);
}
this->setErrors(tags,tags.getTagStringDefault("errors.source","totalBkg"));
tags.setGlobalOverwrite(false);
if(verbose) VERBOSEclass("drawing main pad");
pad->cd();
bool ok = false;
switch(plottype){
case Profile:
ok = this->drawProfiles(tags);
this->drawLegend(tags);
break;
case Stack:
ok = this->drawStack(tags);
this->drawLegend(tags);
if(verbose) VERBOSEclass("drawing cut lines");
this->drawCutLines1D(tags);
break;
case Heatmap:
ok = this->drawHeatmap(tags);
this->drawAdditionalAxes(tags);
this->drawLegend(tags);
break;
case Scatter:
ok = this->drawScatter(tags);
this->drawLegend(tags);
break;
case Migration:
ok = this->drawMigration(tags);
if(tags.getTagBoolDefault("style.useLegendPad",false)){
TPad * legendPad = this->getPad("legend");
legendPad->cd();
}
this->drawLegend(tags);
pad->cd();
break;
case Contours:
ok = this->drawContours(tags);
if(tags.getTagBoolDefault("style.useLegendPad",false)){
TPad * legendPad = this->getPad("legend");
legendPad->cd();
}
this->drawLegend(tags);
pad->cd();
this->drawHeightLines(tags);
this->drawAdditionalAxes(tags);
}
if (not ok) VERBOSEclass("Did not attempt to draw main pad or drawing was unsuccessful");
if(verbose) VERBOSEclass("drawing labels");
this->drawLabels(tags);
if(verbose) VERBOSEclass("refreshing drawing area");
pad->RedrawAxis();
pad->Update();
pad->cd();
int isub = subplots.size();
for(const auto& subplot:subplots){
--isub;
canvas->cd();
TPad * subPad = NULL;
tags.setTagDouble("geometry.mid.yMin",(isub == 0 ? 0. : (subpadSize + (isub-1) * midpadSize)));
TString key = isub == 0 ? "sub" : "mid";
subPad = this->createPad(tags,key);
if (tags.getTagBoolDefault ("style.logScaleX",false )){
subPad->SetLogx();
}
subPad->Draw();
subPad->cd();
if(tags.getTagBoolDefault("style.sub.yAxis.grid",false)) subPad->SetGridy(true);
switch(subplot){
case Ratio:
if(verbose) VERBOSEclass("drawing ratio");
this->drawSub_Ratio(tags);
break;
case CutOptimization:
if(verbose) VERBOSEclass("drawing cut optimization scan");
this->drawSub_CutOptimization(tags);
break;
case CutOptimizationLowpass:
if(verbose) VERBOSEclass("drawing low-pass cut optimization scan");
this->drawSub_CutOptimizationOneDirection(tags, false);
break;
case CutOptimizationHighpass:
if(verbose) VERBOSEclass("drawing high-pass cut optimization scan");
this->drawSub_CutOptimizationOneDirection(tags, true);
break;
case DataMinusBackground:
if(verbose) VERBOSEclass("drawing data-minus-background");
this->drawSub_DataMinusBackground(tags);
break;
default:
if(verbose) VERBOSEclass("Do not draw subplot");
}
}
if(verbose) VERBOSEclass("all done!");
return canvas;
}
void TQDefaultPlotter::drawLabels(TQTaggable& tags){
bool verbose = tags.getTagBoolDefault("verbose",false);
this->getPad("main");
double scaling = tags.getTagDoubleDefault("geometry.main.scaling",1.);
double textsize = tags.getTagDoubleDefault("style.textSize",DEFAULTTEXTSIZE);
int font = tags.getTagDoubleDefault("style.text.font",42);
int color = tags.getTagDoubleDefault("style.text.color",1);
double x = tags.getTagDoubleDefault("geometry.labels.xPos",0.2);
double y = tags.getTagDoubleDefault("geometry.labels.yPos",0.86);
TString atlasLabel;
if(tags.getTagString("labels.atlas",atlasLabel) && atlasLabel != "false"){
double atlasScale = tags.getTagDoubleDefault("labels.atlas.scale",1.25) * scaling;
TLatex atlas;
atlas.SetNDC();
atlas.SetTextFont(72);
atlas.SetTextSize(textsize * atlasScale);
atlas.SetTextColor(1);
double atlas_y = tags.getTagDoubleDefault("labels.atlas.yPos", y);
atlas.DrawLatex(x, atlas_y, "ATLAS");
if(!atlasLabel.IsNull() && atlasLabel != "true"){
TLatex label;
label.SetNDC();
label.SetTextFont(font);
label.SetTextColor(color);
label.SetTextSize(textsize * atlasScale);
label.DrawLatex(x + tags.getTagDoubleDefault("labels.atlas.xPos",0.16), atlas_y, atlasLabel.Data());
}
}
TString nfLabel;
if(tags.getTagBoolDefault ("labels.drawNFInfo",false)){
TString tmpLabel = tags.getTagStringDefault("labels.nfInfo","#color[2]{(NF applied for %s)}");
if(TQStringUtils::countText(tmpLabel,"%s") == 1){
TString nflist = this->getScaleFactorList(tags.getTagStringDefault("input.histogram",""));
if(!nflist.IsNull()){
nfLabel = TString::Format(tmpLabel.Data(),TQStringUtils::convertLaTeX2ROOTTeX(nflist).Data());
}
}
}
TString infoLabel;
bool drawInfo = tags.getTagString ("labels.info",infoLabel);
if (verbose and not drawInfo) VERBOSEclass("Did not get infoLabel tag");
if(infoLabel.IsNull() || infoLabel == "true"){
infoLabel = TString::Format("Plot: \"%s\"", tags.getTagStringDefault("input.histogram","histogram").Data() );
if(!nfLabel.IsNull()){
infoLabel.Prepend(" ");
infoLabel.Prepend(nfLabel);
}
}
if (!infoLabel.IsNull() && infoLabel != "false") {
TLatex l0;
l0.SetNDC();
l0.SetTextFont(font);
l0.SetTextSize(textsize * tags.getTagDoubleDefault("labels.info.size",0.6) * scaling);
l0.SetTextColor(color);
double xpos = tags.getTagDoubleDefault("geometry.main.margins.left",0.16) + tags.getTagDoubleDefault("labels.info.xPos",1.)*(1. - tags.getTagDoubleDefault("geometry.main.margins.right",0.05) - tags.getTagDoubleDefault("geometry.main.margins.left",0.16));
double ypos = 1. - scaling*(1.-tags.getTagDoubleDefault("labels.info.yPos",0.2))*tags.getTagDoubleDefault("geometry.main.margins.top",0.05);
l0.SetTextAlign(tags.getTagIntegerDefault("labels.info.align",31));
l0.DrawLatex(xpos, ypos, infoLabel.Data());
}
double marginStep = tags.getTagDoubleDefault("geometry.labels.marginStep",0.045);
double labelTextScale = tags.getTagDoubleDefault("geometry.labels.scale",0.85);
TQIterator itr(tags.getTagList("labels"),true);
size_t index = 1;
while(itr.hasNext()){
TObject* obj = itr.readNext();
if(!obj) break;
TLatex latex;
latex.SetNDC();
latex.SetTextFont(font);
latex.SetTextSize(textsize * labelTextScale * scaling);
latex.SetTextColor(color);
latex.DrawLatex(x, y - marginStep * index * scaling,obj->GetName());
index++;
}
}
void TQDefaultPlotter::drawSub_Ratio(TQTaggable& tags){
bool verbose = tags.getTagBoolDefault("verbose",false);
if (verbose) VERBOSEclass("'verbose' tag enabled");
TString numeratorFilter = tags.getTagStringDefault("style.ratio.numeratorFilter",".isData");
TString denominatorName = tags.getTagStringDefault("style.ratio.denominator","totalStack");
TH1* denominatorOrig = this->getObject<TH1>(denominatorName);
if(!denominatorOrig){
ERRORclass("unable to find denominator '%s'",denominatorName.Data());
return;
}
double xmin = denominatorOrig->GetXaxis()->GetXmin();
double xmax = denominatorOrig->GetXaxis()->GetXmax();
TString dataLabel("");
std::vector<TH1*> ratios;
std::vector<TString> drawOptions;
TQTaggableIterator numerator_processes(fProcesses);
bool copied_xaxis_style = false;
double yLowerLimit = tags.getTagDoubleDefault("style.sub.linMin",-std::numeric_limits<double>::infinity());
double yLowerLimitLog = std::max(tags.getTagDoubleDefault("style.sub.logMin"), tags.getTagDoubleDefault("style.sub.logMinMin", 1e-9));
TH1* errorsHist = TQHistogramUtils::copyHistogram(denominatorOrig,TString::Format("ratioDenominatorErrors_%s",denominatorOrig->GetName()));
TQHistogramUtils::divideHistogramWithoutError(errorsHist,denominatorOrig);
double ymax = TQHistogramUtils::getMaximumBinValue(errorsHist,xmin,xmax,true);
double ymin = TQHistogramUtils::getMinimumBinValue(errorsHist,xmin,xmax,true,yLowerLimit);
double yminLog = TQHistogramUtils::getMinimumBinValue(errorsHist,xmin,xmax,true,yLowerLimitLog);
TGraph * errors = TQHistogramUtils::getGraph(errorsHist);
delete errorsHist;
bool showUnity = tags.getTagBoolDefault("style.ratio.showUnity", true);
if (!showUnity){
ymax = -std::numeric_limits<double>::infinity();
ymin = std::numeric_limits<double>::infinity();
yminLog = std::numeric_limits<double>::infinity();
}
while(numerator_processes.hasNext()){
TQNamedTaggable* numeratorProcess = numerator_processes.readNext();
if(!numeratorProcess || !numeratorProcess->getTagBoolDefault(numeratorFilter,false)) continue;
TH1 * numeratorOrig = this->getObject<TH1>(this->makeHistogramIdentifier(numeratorProcess));
if(!numeratorOrig) continue;
if (!copied_xaxis_style) {
TQHistogramUtils::copyAxisStyle(numeratorOrig, errors->GetHistogram());
copied_xaxis_style = true;
}
TH1* ratio = TQHistogramUtils::copyHistogram(numeratorOrig,TString::Format("ratio_%s_%s",numeratorOrig->GetName(),denominatorOrig->GetName()));
if (ratio->GetFillColor() != kWhite){
ratio->SetLineColor(ratio->GetFillColor());
}
ratio->SetLineWidth(2);
TQHistogramUtils::divideHistogramWithoutError(ratio,denominatorOrig);
ymax = std::max(ymax,TQHistogramUtils::getMaximumBinValue(ratio,xmin,xmax,true));
ymin = std::min(ymin,TQHistogramUtils::getMinimumBinValue(ratio,xmin,xmax,true,yLowerLimit));
yminLog = std::min(yminLog,TQHistogramUtils::getMinimumBinValue(ratio,xmin,xmax,true,yLowerLimitLog));
ratios.push_back(ratio);
drawOptions.push_back(numeratorProcess->getTagStringDefault("sub.drawOption", "P E"));
}
if (tags.getTagBoolDefault("style.sub.logScaleY", false)){
ymin = yminLog;
}
double dy = ymax - ymin;
ymin -= 0.1*dy;
ymax += 0.1*dy;
TString denominatorTitle = tags.getTagStringDefault("style.ratio.denominator.title",denominatorOrig->GetTitle());
TString numeratorTitle = tags.getTagStringDefault("style.ratio.numerator.title","Data");
TString title = "";
if (!TQStringUtils::isEmpty(denominatorTitle, true))
title = tags.getTagStringDefault("style.ratio.title",TString::Format("%s / %s",numeratorTitle.Data(),denominatorTitle.Data()));
else
title = tags.getTagStringDefault("style.ratio.title",TString::Format("%s",numeratorTitle.Data()));
tags.getTagString("style.sub.title",title);
errors->GetYaxis()->SetTitle(title);
bool lastLabel = tags.getTagBoolDefault("style.sub.lastLabel",false);
if (!lastLabel) errors->GetYaxis()->ChangeLabel(-1,-1,0.0);
bool firstLabel = tags.getTagBoolDefault("style.sub.firstLabel",false);
if (!firstLabel) errors->GetYaxis()->ChangeLabel(1,-1,0.0);
tags.getTagDouble("style.ratio.min",ymin);
tags.getTagDouble("style.ratio.max",ymax);
errors->SetMaximum(ymax);
errors->SetMinimum(ymin);
std::string padkey = gPad->GetName();
this->applyGeometry(tags,errors, padkey);
this->applyStyle (tags,errors, padkey);
this->applyStyle (tags,errors, "ratio");
this->applyStyle (tags,errors, "ratio.errors");
if (showUnity){
errors->Draw("AE2");
TLine line;
line.SetLineStyle(errors->GetLineStyle());
line.SetLineWidth(errors->GetLineWidth());
line.DrawLine(errors->GetXaxis()->GetXmin(),1,errors->GetXaxis()->GetXmax(),1);
} else{
errors->SetLineWidth(0);
errors->SetMarkerSize(0);
errors->Draw("A");
}
for(size_t i = 0; i < ratios.size(); i++) {
ratios[i]->Draw(drawOptions[i] + "SAME");
if (drawOptions[i].Contains("E")) {
TH1* ratioClone = (TH1*) ratios[i]->Clone();
ratioClone->SetMarkerSize(0);
ratioClone->Draw("SAME " + drawOptions[i] + "0");
}
this->drawArrows(tags,ratios[i],ymin,ymax);
}
}
void TQDefaultPlotter::drawSub_DataMinusBackground(TQTaggable& tags){
TString subtractionName = tags.getTagStringDefault("style.dmb.subtraction","totalBkg");
TH1* subtraction = this->getObject<TH1>(subtractionName);
if(!subtraction) return;
TString errorSourceName = tags.getTagStringDefault("style.dmb.errors","totalBkg");
TString shiftToName = tags.getTagStringDefault("style.dmb.shiftTo",errorSourceName);
TH1* errorSource = this->getObject<TH1>(errorSourceName);
TH1* shiftTo = this->getObject<TH1>(shiftToName);
if(!errorSource) return;
std::string padkey = gPad->GetName();
TString dataLabel("");
double max = 0;
double min = 0;
std::vector<TH1*> v_signal;
std::vector<TH1*> v_data;
std::vector<TString> drawOptions;
TQTaggableIterator itrSig(fProcesses);
while(itrSig.hasNext()){
TQNamedTaggable* process = itrSig.readNext();
if(!process) continue;
if(!process->getTagBoolDefault(".isSignal",false)) continue;
TH1* h_sig = this->getObject<TH1>(this->makeHistogramIdentifier(process));
h_sig = TQHistogramUtils::copyHistogram(h_sig,TString::Format("%s_dminusb",h_sig->GetName()));
this->applyStyle(tags,h_sig,"sub.sig");
max = std::max(max,TQHistogramUtils::getMax(h_sig,false));
min = std::min(min,TQHistogramUtils::getMin(h_sig,false));
if(!h_sig) continue;
v_signal.push_back(h_sig);
}
TQTaggableIterator itr(fProcesses);
while(itr.hasNext()){
TQNamedTaggable* process = itr.readNext();
if(!process) continue;
if(!process->getTagBoolDefault(".isData",false)) continue;
TH1 * h_data = this->getObject<TH1>(this->makeHistogramIdentifier(process));
if(!h_data) continue;
h_data = TQHistogramUtils::copyHistogram(h_data,TString::Format("%s_minus_bkg",h_data->GetName()));
this->applyStyle(tags,h_data,"sub.data");
TQHistogramUtils::addHistogramWithoutError(h_data,subtraction,-1.);
max = std::max(max,TQHistogramUtils::getMax(h_data,true));
min = std::min(min,TQHistogramUtils::getMin(h_data,true));
v_data.push_back(h_data);
drawOptions.push_back(process->getTagStringDefault("sub.drawOption", "P E"));
}
gStyle->SetEndErrorSize(0);
TGraphErrors* errorGraph = new TGraphErrors(errorSource->GetNbinsX());
TQHistogramUtils::copyStyle(errorGraph,errorSource);
for(int i=1; i<errorSource->GetNbinsX(); ++i){
double val = errorSource->GetBinError(i);
max = std::max(max,1.1*val);
min = std::min(min,-1.1*val);
errorGraph->SetPoint(i,shiftTo->GetBinCenter(i),shiftTo->GetBinContent(i)-subtraction->GetBinContent(i));
errorGraph->SetPointError(i,errorSource->GetBinCenter(i)-errorSource->GetBinLowEdge(i),val);
}
double margin = tags.getTagDoubleDefault("style.sub.margin",1.1);
TH1* frame = TQHistogramUtils::copyHistogram(subtraction,"Graph_subFrame");
frame->Reset();
frame->SetMaximum(margin*max);
frame->SetMinimum(margin*min);
this->applyGeometry(tags,frame,padkey);
this->applyStyle (tags,frame, "sub");
this->applyStyle (tags,frame, "dmb");
frame->GetYaxis()->SetTitle(tags.getTagStringDefault("style.sub.title",tags.getTagStringDefault("style.dmb.title","Data-Bkg.")));
frame->Draw("HIST");
std::sort(v_signal.begin(),v_signal.end(),[&](TH1* a, TH1* b){ return a->GetSumOfWeights() >= b->GetSumOfWeights();});
for(auto h:v_signal){
h->Draw("HISTSAME");
}
errorGraph->SetTitle("mc error band");
this->applyStyle(tags,errorGraph,"main.totalStackError");
errorGraph->Draw("E2SAME");
for(size_t i = 0; i < v_data.size(); i++) {
v_data[i]->Draw(drawOptions[i] + "SAME");
TH1* hClone = (TH1*) v_data[i]->Clone();
hClone->SetMarkerSize(0);
hClone->Draw("SAME " + drawOptions[i] + "0");
}
}
void TQDefaultPlotter::drawSub_CutOptimization(TQTaggable& tags){
std::string padkey = gPad->GetName();
bool verbose = tags.getTagBoolDefault("verbose",false);
TH1* hTotalStack = this->getObject<TH1>("totalStack");
if(!hTotalStack) return;
TQTaggableIterator itr(this->fProcesses);
TH1* hSig = NULL;
while(itr.hasNext() && !hSig){
TQNamedTaggable* process = itr.readNext();
if(!process) continue;
if(process->getTagBoolDefault(".isSignal",false)){
hSig = this->getObject<TH1>(this->makeHistogramIdentifier(process));
}
}
if(!hSig) return;
TString fommodestr = tags.getTagStringDefault("optScan.FOMmode",tags.getTagStringDefault ("style.FOMmode","s/sqrt(b)"));
TQHistogramUtils::FOM FOMmode = TQHistogramUtils::readFOM(fommodestr);
if(FOMmode == TQHistogramUtils::kUndefined){
WARNclass("unknown figure of merit '%s'!",fommodestr.Data());
return;
}
bool binByBin = tags.getTagBoolDefault("optScan.FOMbbb",tags.getTagBoolDefault ("style.FOMbbb",false));
bool drawLegend = !binByBin;
std::vector<TH1*> bkgSystHistos;
collectOptScanSimplifiedSystHistograms(bkgSystHistos, tags);
std::map<std::string,TH1*> hists;
double actualmax = 0;
if(binByBin){
if(verbose){
VERBOSEclass("drawing bin-by-bin significances with FOM=%s for S=%s and B=%s",TQHistogramUtils::getFOMTitleROOT(FOMmode).Data(),hSig->GetTitle(),hTotalStack->GetTitle());
}
TH1* hFOM = TQHistogramUtils::getFOMHistogram(FOMmode,hSig, hTotalStack, 0, bkgSystHistos);
actualmax = hFOM->GetMaximum() * tags.getTagDoubleDefault("optScan.enlargeY",1.3);
this->addObject(hFOM,"hist_FOM_bbb");
hists["default"] = hFOM;
} else {
if(verbose){
VERBOSEclass("drawing optimization scan with FOM=%s for S=%s and B=%s",TQHistogramUtils::getFOMTitleROOT(FOMmode).Data(),hSig->GetTitle(),hTotalStack->GetTitle());
}
TH1* hFOMl = TQHistogramUtils::getFOMScan(FOMmode,hSig, hTotalStack, true, 0.05,verbose, bkgSystHistos);
TH1* hFOMr = TQHistogramUtils::getFOMScan(FOMmode,hSig, hTotalStack, false,0.05,verbose, bkgSystHistos);
if (FOMmode == TQHistogramUtils::kSoSqB){
double rmax = hFOMr->GetBinContent(hFOMr->GetMaximumBin());
double rmaxxval = hFOMr->GetBinLowEdge(hFOMr->GetMaximumBin()) + hFOMr->GetBinWidth(hFOMr->GetMaximumBin());
double lmax = hFOMl->GetBinContent(hFOMl->GetMaximumBin());
double lmaxxval = hFOMl->GetBinLowEdge(hFOMl->GetMaximumBin());
hFOMl->SetTitle(TString::Format("#rightarrow cut Max=%.2f (%.2f)",lmax,lmaxxval));
hFOMr->SetTitle(TString::Format("#leftarrow cut Max=%.2f (%.2f)",rmax,rmaxxval));
} else {
hFOMl->SetTitle("#rightarrow cut");
hFOMr->SetTitle("#leftarrow cut" );
}
if(tags.getTagBoolDefault("optScan.autoselect",false)){
if(verbose) VERBOSEclass("autoselecting opt scan");
if(TQHistogramUtils::hasGreaterMaximumThan(hFOMr,hFOMl)){
if(verbose) VERBOSEclass("removing left-hand FOM histogram");
this->addObject(hFOMr,"hist_FOM");
hists["right"] = hFOMr;
delete hFOMl;
actualmax = hFOMr->GetMaximum() * tags.getTagDoubleDefault("optScan.enlargeY",1.3);
} else {
if(verbose) VERBOSEclass("removing right-hand FOM histogram");
this->addObject(hFOMl,"hist_FOM");
hists["left"] = hFOMl;
delete hFOMr;
actualmax = hFOMl->GetMaximum() * tags.getTagDoubleDefault("optScan.enlargeY",1.3);
}
} else {
if(verbose) VERBOSEclass("using all opt scans");
hists["right"] = hFOMr;
hists["left"] = hFOMl;
this->addObject(hFOMl,"hist_FOM_left");
this->addObject(hFOMr,"hist_FOM_right");
actualmax = std::max(hFOMl->GetMaximum(),hFOMr->GetMaximum()) * tags.getTagDoubleDefault("optScan.enlargeY",1.3);
}
}
for (TH1* toDel : bkgSystHistos) {
delete toDel;
}
bkgSystHistos.clear();
TLegend* leg = NULL;
if(drawLegend){
leg = new TLegend(0.21,0.85,0.93,0.95);
leg->SetNColumns(2);
leg->SetFillColor(0);
leg->SetFillStyle(0);
leg->SetLineColor(0);
leg->SetLineWidth(0);
}
bool first = true;
for(auto h:hists){
if(verbose) VERBOSEclass("drawing FOM histogram");
this->applyStyle (tags,h.second,"optScan."+h.first);
this->applyGeometry(tags,h.second,"optScan");
this->applyGeometry(tags,h.second,padkey);
h.second->SetMaximum(actualmax);
h.second->SetFillStyle(0);
h.second->SetMinimum(0);
h.second->SetNdivisions(50008);
h.second->SetLineWidth(3);
h.second->GetYaxis()->SetNdivisions(50004);
h.second->Draw(first ? "HIST" : "HIST SAME");
if(drawLegend){
leg->AddEntry(h.second,h.second->GetTitle(),"l");
}
first = false;
}
if(drawLegend){
leg->Draw("SAME");
}
}
void TQDefaultPlotter::drawSub_CutOptimizationOneDirection(TQTaggable& tags, bool highpass){
std::string padkey = gPad->GetName();
bool verbose = tags.getTagBoolDefault("verbose",false);
TH1* hTotalStack = this->getObject<TH1>("totalStack");
if(!hTotalStack) return;
TQTaggableIterator itr(this->fProcesses);
std::vector<std::pair<TH1*, TQNamedTaggable*>> signals;
while(itr.hasNext()){
TQNamedTaggable* process = itr.readNext();
if(!process) continue;
if(process->getTagBoolDefault(".isSignal",false)){
TH1* hSig = this->getObject<TH1>(this->makeHistogramIdentifier(process));
signals.push_back({hSig, process});
}
}
if(signals.size() == 0) {
return;
}
TString legendarrow = "#leftarrow";
if (highpass) {
legendarrow = "#rightarrow";
}
TString fommodestr = tags.getTagStringDefault("optScan.FOMmode",tags.getTagStringDefault ("style.FOMmode","s/sqrt(b)"));
TQHistogramUtils::FOM FOMmode = TQHistogramUtils::readFOM(fommodestr);
if(FOMmode == TQHistogramUtils::kUndefined){
WARNclass("unknown figure of merit '%s'!",fommodestr.Data());
return;
}
std::vector<TH1*> bkgSystHistos;
collectOptScanSimplifiedSystHistograms(bkgSystHistos, tags);
std::map<std::string,TH1*> hists;
double actualmax = 0;
for (auto histprocpair : signals) {
TH1* hSig = histprocpair.first;
TQNamedTaggable* process = histprocpair.second;
if (verbose) {
VERBOSEclass("drawing multi-signal optimization scan with FOM=%s for S=%s and B=%s",TQHistogramUtils::getFOMTitleROOT(FOMmode).Data(),hSig->GetTitle(),hTotalStack->GetTitle());
}
TH1* hFOM = TQHistogramUtils::getFOMScan(FOMmode, hSig, hTotalStack, highpass, 0.05, verbose, bkgSystHistos);
TQHistogramUtils::applyStyle(hFOM, process);
hists[process->getTagStringDefault(".name", "moep").Data()] = hFOM;
actualmax = std::max(hFOM->GetMaximum() * tags.getTagDoubleDefault("optScan.enlargeY", 1.3), actualmax);
}
for (TH1* toDel : bkgSystHistos) {
delete toDel;
}
bkgSystHistos.clear();
bool binByBin = tags.getTagBoolDefault("optScan.FOMbbb",tags.getTagBoolDefault ("style.FOMbbb",false));
bool drawLegend = !binByBin;
TLegend* leg = NULL;
if(drawLegend){
if (highpass) {
leg = new TLegend(0.21,0.85,0.93,0.95);
} else {
leg = new TLegend(0.73,0.85,0.93,0.95);
}
leg->SetNColumns(2);
leg->SetFillColor(0);
leg->SetFillStyle(0);
leg->SetLineColor(0);
leg->SetLineWidth(0);
leg->SetTextSize(.15);
}
bool first = true;
for(auto h:hists){
if(verbose) VERBOSEclass("drawing FOM histogram");
this->applyStyle (tags,h.second,"optScan."+h.first);
this->applyGeometry(tags,h.second,padkey);
h.second->SetMaximum(actualmax);
h.second->SetFillStyle(0);
h.second->SetMinimum(0);
h.second->SetNdivisions(50008);
h.second->GetYaxis()->SetNdivisions(50004);
h.second->Draw(first ? "HIST" : "HIST SAME");
if (first && drawLegend) {
leg->AddEntry(h.second, highpass ? "#rightarrow" : "#leftarrow" ,"l");
}
first = false;
}
if (drawLegend) {
leg->Draw("SAME");
}
}