-
Benedict Winter authoredBenedict Winter authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ActionPlotResults.cxx 59.58 KiB
#include "QFramework/TQFolder.h"
#include "QFramework/TQIterator.h"
#include "QFramework/TQUtils.h"
#include "QFramework/TQTable.h"
#include "QFramework/TQStringUtils.h"
#include "QFramework/TQPathManager.h"
#include "SFramework/TSStatisticsManager.h"
#include "SFramework/TSStatisticsPlotter.h"
#include "TSystem.h"
#include "TROOT.h"
#include <fstream>
#include <algorithm>
#include <vector>
#include <list>
// #define _DEBUG_
#include "QFramework/TQLibrary.h"
/*<cafdoc name=PlotResults>
ActionPlotResults/ActionPlotResultsTikZ
===========================
Plot the results of a fit, parameter by parameter.
Several types of data sources are supported.
* Pulls and constraints
* Breakdowns
* Impacts
The `PlotResults` action uses the `ROOT` plotter as a graphics
backend, whereas the `PlotResultsTikZ` variant produces a `tex` file
with TikZ code for the graphic.
Usage:
---------------
```
+PlotResultsTikZ.asimov.full {
+Morphing{
<outputFile="workspaces/$(fitLabel)/pulls-full_AL.tex">
+PULLS.asimov {
<parameterSet = "LikelihoodScans/ScanLikelihood.2D/Scan/p.0/.fit/floatParsFinal">
<style.title = "$a_{L} = \hat{a_{L}}$ (Asimov)", style.color = kBlue>
}
+BREAKDOWN.asimov {
<parameterSet = "Breakdowns/asimov/Breakdown/AL">
<style.title = "Asimov breakdown", style.fillColor = kMagenta-7>
<style.drawOption = 'E2'>
<master=true>
}
+IMPACTS.asimov {
<parameterSet = "Impacts/asimov/Impacts/AL">
<style.title = "Asimov impacts", style.fillColor = kBlack, style.lineColor = kBlack>
<style.fillStyle=0>
<style.drawOption = 'E5'>
}
}
}
```
Datasets can be added using the `PULLS`, `BREAKDOWN` and `IMPACTS`
subfolders. Several datasets of the same type can be added to one
plot by suffixing them with identifiers like `.asimov` or
`.observed`.
Global options include
* `writeTable` in addition to the plot, write a table to the given filename.
* `writeJSON` in addition to the plot, write the data in JSON format to the given filename.
For each folder, the source of the parameter sets needs to be
identified using the `parameterSet` tag. Other than that, the
following style options are supported:
* `style.title` Change the title of the dataset.
* `style.color` Change the line and fill color of the dataset.
* `style.fillColor` Change the fill color of the dataset.
* `style.fillStyle` Change the fill style of the dataset.
* `style.lineColor` Change the line color of the dataset.
* `style.lineStyle` Change the line style of the dataset.
* `style.drawOption` Change the draw option of the dataset.
Specific tags for the ranking (BREAKDOWN/IMPACT) displays include
* `ranking.relativeToValue` normalize everything to the postfit POI value.
* `ranking.relativeToUncertainty` normalize everything to the postfit POI uncertainty.
</cafdoc> */
namespace {
int extractColorNumberFromTag(TQTaggable* config, const TString& tagName, int fallback/*TColor number*/) {
if (!config) return fallback;
int color = 0;
TString hexColor = TQStringUtils::trim(config->getTagStringDefault(tagName+".rgb",""));
if (hexColor.Length()>0) {
hexColor = TQStringUtils::replace(hexColor,"0x","#"); //if someone uses the nicer notation for hex numbers...
TQStringUtils::ensureLeadingText(hexColor,"#"); //for the lazy ones (not specifying any indication of hex numbers)
color = TColor::GetColor(hexColor);
} else { //no rgb color specified, so fall back to a regular TColor number
color = config->getTagIntegerDefault(tagName,fallback);
if (!gROOT->GetColor(color)) { //color is not known
color = fallback;
}
}
return color;
}
//just a lengthy definition...
TString customTikzHatchPattern = "\n\
% defining the new dimensions and parameters\n\
\\newlength{\\hatchspread}\n\
\\newlength{\\hatchthickness}\n\
\\newlength{\\hatchshift}\n\
\\newcommand{\\hatchcolor}{}\n\
% declaring the keys in tikz\n\
\\tikzset{hatchspread/.code={\\setlength{\\hatchspread}{#1}},\n\
hatchthickness/.code={\\setlength{\\hatchthickness}{#1}},\n\
hatchshift/.code={\\setlength{\\hatchshift}{#1}},% must be >= 0\n\
hatchcolor/.code={\\renewcommand{\\hatchcolor}{#1}}}\n\
% setting the default values\n\
\\tikzset{hatchspread=3pt,\n\
hatchthickness=0.4pt,\n\
hatchshift=0pt,% must be >= 0\n\
hatchcolor=black}\n\
% declaring the pattern\n\
\\pgfdeclarepatternformonly[\\hatchspread,\\hatchthickness,\\hatchshift,\\hatchcolor]% variables\n\
{custom north west lines}% name\n\
{\\pgfqpoint{\\dimexpr-2\\hatchthickness}{\\dimexpr-2\\hatchthickness}}% lower left corner\n\
{\\pgfqpoint{\\dimexpr\\hatchspread+2\\hatchthickness}{\\dimexpr\\hatchspread+2\\hatchthickness}}% upper right corner\n\
{\\pgfqpoint{\\dimexpr\\hatchspread}{\\dimexpr\\hatchspread}}% tile size\n\
{% shape description\n\
\\pgfsetlinewidth{\\hatchthickness}\n\
\\pgfpathmoveto{\\pgfqpoint{0pt}{\\dimexpr\\hatchspread+\\hatchshift}}\n\
\\pgfpathlineto{\\pgfqpoint{\\dimexpr\\hatchspread+0.15pt+\\hatchshift}{-0.15pt}}\n\
\\ifdim \\hatchshift > 0pt\n\
\\pgfpathmoveto{\\pgfqpoint{0pt}{\\hatchshift}}\n\
\\pgfpathlineto{\\pgfqpoint{\\dimexpr0.15pt+\\hatchshift}{-0.15pt}}\n\
\\fi\n\
\\pgfsetstrokecolor{\\hatchcolor}\n\
\\pgfusepath{stroke}\n\
}\n\
";
/*
TString defineTikzHatchPattern(double distance, const TString& name_, TString& fullPatternName) {
TString name = TQStringUtils::makeValidIdentifier(name_,TQStringUtils::getLowerLetters());
fullPatternName = name+" north west lines";
TString def =
"\\pgfdeclarepatternformonly[\\LineSpace"+name+"]{"+name+" north west lines}{\\pgfqpoint{-1pt}{-1pt}}{\\pgfqpoint{\\LineSpace"+name+"}{\\LineSpace"+name+"}}{\\pgfqpoint{\\LineSpace"+name+"}{\\LineSpace"+name+"}}%\n" +
"{\n\n\
\\pgfsetcolor{\\tikz@pattern@color} \n\n\
\\pgfsetlinewidth{0.4pt}\n\n\
\\pgfpathmoveto{\\pgfqpoint{0pt}{0pt}}\n\n\
\\pgfpathlineto{\\pgfqpoint{\\LineSpace"+name+" + 0.1pt}{\\LineSpace"+name+" + 0.1pt}}\n\n\
\\pgfusepath{stroke}\n\n\
}\n\n\
\\makeatother\n\n\
\\newdimen\\LineSpace"+name+"\n\n\
\\tikzset{\n\n\
line space/.code={\\LineSpace"+name+"=#1},\n\n\
line space="+std::to_string(distance)+"pt\n\n\
}";
return def;
}
*/
}
namespace TSBaseActions {
class PlotResults : public TSStatisticsManager::Action {
virtual TQFolder* setup(TQFolder* plotconfig,TQFolder* results) const {
TQFolder* parSets = collectParameterSets(plotconfig,results);
std::vector<TString> parameter = plotconfig->getTagVString("parameters");
if(parameter.size() > 0){
std::reverse(parameter.begin(),parameter.end());
} else {
parameter = makeParameterList(parSets,plotconfig);
}
if(parameter.size() == 0) return NULL;
TQFolder * output = TQFolder::newFolder("output");
int overlayIndex = 0;
TCollection* breakdowns = parSets->getListOfFolders("BREAKDOWN*,IMPACTS*,RANKING*");
int nbreakdowns = 0;
bool hasMaster = false;
if(breakdowns){
breakdowns->SetOwner(false);
TQFolderIterator breakdownSets(breakdowns, true);
double max = -1;
std::vector<TQFolder*> subs;
while (breakdownSets.hasNext()) {
TQFolder* set = breakdownSets.readNext();
if(!set) continue;
DEBUG("investigating uncertainty set '%s'",set->GetName());
#ifdef _DEBUG_
set->print("rdt");
#endif
bool master = set->getTagBoolDefault("master",false);
if(master && !hasMaster){
makeParameterBreakdown(plotconfig,set,output,parameter,true,max);
hasMaster = true;
nbreakdowns = 1;
} else {
subs.push_back(set);
}
}
for(auto set:subs){
makeParameterBreakdown(plotconfig,set,output,parameter,!hasMaster,max);
nbreakdowns ++;
hasMaster = true;
}
}
TCollection* pulls = parSets->getListOfFolders("PULLS*");
if(!pulls && nbreakdowns==0){
manager->error("unable to obtain PULLS or BREAKDOWN configuration!");
return NULL;
}
if(pulls){
pulls->SetOwner(false);
int npulls = pulls->GetEntries();
TQFolderIterator pullSets(pulls, true);
double step = plotconfig->getTagDoubleDefault("style.stepWidth",0.2);
while (pullSets.hasNext()) {
TQFolder* set = pullSets.readNext();
if(!set) continue;
DEBUG("investigating pull set '%s'",set->GetName());
#ifdef _DEBUG_
set->print("rdt");
#endif
bool master = set->getTagBoolDefault("master",false);
double pos = step*overlayIndex;
pos -= 0.5*step*(npulls-1);
makeParameterPulls(plotconfig,set,output,parameter,master,pos);
overlayIndex++;
}
}
makeLabels(plotconfig,output,parameter);
// delete parSets;
return output;
}
bool execute(TQFolder * config) const override {
TQFolder * result = results()->getFolder(config->GetName());
if (!config) {
return 0;
}
Int_t nResults = 0;
if (result) {
TQFolder* plotconfig = config->getFolder("PlotFitParameter");
if(!plotconfig) plotconfig = config;
TQFolder* output = setup(plotconfig,result);
if(output){ nResults++;
bool ok = makePlot(plotconfig,output);
nResults += ok;
TString jsonoutname;
if(plotconfig->getTagString("writeJSON",jsonoutname)){
TQUtils::ensureDirectoryForFile(TQPathManager::getPathManager()->getTargetPath(jsonoutname).c_str());
writeJSON(output,TQPathManager::getPathManager()->getTargetPath(jsonoutname).c_str());
}
TString tableoutname;
if(plotconfig->getTagString("writeTable",tableoutname)){
TQTable* tab = makeTable(config,output);
if(tab){
for (int i = 0; i < tab->getNcols(); i++) { tab->setColAlign(i, plotconfig->getTagStringDefault("colAlign", "l")); }
//@tag[writeTableFormats] list of formats in which the table should be written. For possible options see TQTable documentation
std::vector<TString> formats = plotconfig->getTagVString("writeTableFormats");
if(formats.size()==0) formats.push_back("tex");
TQTaggable tags("standalone=true");
for(auto fmt:formats){
TString path(TQPathManager::getPathManager()->getTargetPath(tableoutname));
TQStringUtils::ensureTrailingText(path,"."+fmt);
tab->write(path,fmt,tags);
}
delete tab;
} else {
manager->warn("failed to create table!");
}
}
} else {
manager->error(TString::Format("unable to setup plot %s, skipping",plotconfig->GetName()));;
}
delete output;
}
if (nResults == 0) {
manager->warn(TString::Format("%s: No results have been plotted", config->GetName()));
return false;
}
return true;
}
virtual TQFolder* collectParameterSets(TQFolder* config, TQFolder* results, const TString& expression = "?") const{
// the folder containing all parameter sets to be plotted onto one plot
TQFolder * parSets = TQFolder::newFolder("parSets");
TQIterator itr(config->getListOfFolders(expression), true);
while (itr.hasNext()) {
TQFolder * group = (TQFolder*)itr.readNext();
TString name = group->GetName();
TQTaggable tags(group);
TString parSetName;
if (!tags.getTagString("parameterSet", parSetName)) {
manager->warn(TString::Format("Missing parameter set for plotting of group '%s'. Skipping ...", name.Data()));
continue;
}
TString uncondPath;
TQFolder* uncond = NULL;
if(tags.getTagString("unconditional",uncondPath)){
uncond = results->getFolder(uncondPath);
}
TQFolder * parSet = results->getFolder(parSetName);
if (!parSet) {
manager->warn(TString::Format(
"Failed to find parameter set '%s' for plotting of group '%s' in result '%s'. Skipping ...",
parSetName.Data(), name.Data(), results->getPath().Data()));
continue;
}
if(parSet->isEmpty()){
manager->error("found empty parameter set!");
} else {
TQFolder * copyOfParSet = parSet->copy(name);
copyOfParSet->importTags(tags);
parSets->addObject(copyOfParSet);
if(uncond) copyOfParSet->importTags(uncond);
}
}
return parSets;
};
virtual void makeLabels(TQFolder* config, TQFolder* output, const std::vector<TString>& parameter) const {
// make the labels
std::vector<TString> stripPrefixes = config->getTagVString("stripPrefixes");
std::vector<TString> stripSuffixes = config->getTagVString("stripSuffixes");
TQFolder* textReplacements = TQFolder::newFolder("textReplacements");
for(const auto& replfile:config->getTagVString("textReplacements")){
textReplacements->importFromTextFile(TQPathManager::getPathManager()->findConfigPath(replfile));
}
bool vertical = config->getTagBoolDefault("style.vertical",false);
double textSize = config->getTagDoubleDefault("style.textSize",0.025);
double shift = config->getTagDoubleDefault("style.yshift",0.4);
int boxColor;
bool showBoxes = config->getTagInteger("style.highlight",boxColor);
double labelOffset = config->getTagDoubleDefault("style.labelOffset",1.5);
Int_t i = -1;
for(auto name:parameter){
TQFolder* f = textReplacements->getFolder(name);
if (f && f->getTagBoolDefault("skipParameter",false)) continue;
i++;
TQFolder* text = output->getFolder(TString::Format("Text.p.i%d+", i));
if(vertical){
text->setTagDouble("y", -shift+i);
text->setTagDouble("x", labelOffset);
} else {
text->setTagDouble("x", i);
text->setTagDouble("y", labelOffset);
}
if(showBoxes && i%2){
TQFolder* box = output->getFolder(TString::Format("Box.p.i%d+", i));
if(vertical){
box->setTagDouble("y1", i-0.5);
box->setTagDouble("y2", i+0.5);
} else {
box->setTagDouble("x1", i-0.5);
box->setTagDouble("x2", i+0.5);
}
box->setTagInteger("style.color", config->getTagIntegerDefault("style.highlight", 16));
box->setTagString("level","back");
}
TString t(f ? f->getTagStringDefault("title",name) : name);
for(auto s:stripPrefixes){
TQStringUtils::removeLeadingText(t,s);
}
for(auto s:stripSuffixes){
TQStringUtils::removeTrailingText(t,s);
}
text->setTagString("text", t);
text->setTagDouble("style.textSize", textSize);
text->setTagInteger("style.textAngle", vertical ? 0 : 60);
}
delete textReplacements;
}
std::vector<TString> makeParameterList(TQFolder* input, TQFolder* config) const {
// collect list of parameters present in at least one set
TQTaggable par;
TQFolderIterator itrPar(input->getListOfFolders("?/?"), true);
while (itrPar.hasNext()) {
par.setTagBool(itrPar.readNext()->GetName(), true);
}
// organize the list of parameters by adding those from the list created
// in the previous step that pass the parameter filter <parFilter>
std::vector<TString> parFilter = config->getTagVString("parameterFilter");
if(parFilter.size() == 0){
parFilter.push_back("*");
}
std::vector<TString> blacklist = config->getTagVString("blacklist");
if ( !config->getTagBoolDefault("showIndividualStatUncertainties",false) ) {
blacklist.push_back("*gamma_stat*");
}
blacklist.push_back(".cond.*");
blacklist.push_back(".uncond");
std::vector<TString> whitelist = config->getTagVString("whitelist");
std::vector<TString> result;
for(auto item:parFilter){
TCollection* c = par.getListOfKeys(item);
if(!c) continue;
TQIterator itr(c,true);
c->SetOwner(true);
while(itr.hasNext()){
TObject* p = itr.readNext();
bool remove = false;
for(auto item:blacklist){
if(TQStringUtils::matches(p->GetName(),item)){
remove = true;
}
}
for(auto item:whitelist){
if(TQStringUtils::matches(p->GetName(),item)){
remove = false;
}
}
if(!remove){
result.push_back(p->GetName());
}
}
}
if(result.size() == 0){
manager->error(TString::Format("obtained empty parameter list from '%s'\n whitelist=%s\n blacklist=%s\n filter=%s",
input->getPath().Data(),
TQStringUtils::concat(whitelist).Data(),
TQStringUtils::concat(blacklist).Data(),
TQStringUtils::concat(parFilter).Data()));
}
return result;
}
virtual bool makeParameterBreakdown(TQFolder * config, TQFolder * set, TQFolder* output, std::vector<TString>& parameter, bool master, double& max) const{
if(parameter.size()==0){
manager->error("no parameter set given!");
return false;
}
TQFolder* overlay = output->getFolder(TString::Format("Overlay.Ranking.%s+", set->GetName()));
TQFolder* axis = NULL;
if(master) axis = output->getFolder(TString::Format("Axis.%s+", set->GetName()));
bool vertical = config->getTagBoolDefault("style.vertical",false);
TString setName = set->GetName();
overlay->importTags(set);
/* loop over parameter */
Int_t i = -1;
bool relativeToValue = config->getTagBoolDefault("ranking.relativeToValue",false);
bool relativeToUncertainty = config->getTagBoolDefault("ranking.relativeToUncertainty",false);
if(relativeToValue && relativeToUncertainty){
manager->error("the options ranking.relativeToValue and ranking.relativeToUncertainty are mutually exclusive!");
return false;
}
std::vector<TQFolder*> folders;
bool findMax = (max<0);
for(auto name:parameter){
TQFolder * var = set->getFolder(name);
if (!var) {
manager->warn(TString::Format("Parameter '%s' does not exist in set '%s'", name.Data(), setName.Data()));
folders.push_back(NULL);
} else if(!var->getTagBoolDefault("IsOk",true)){
manager->warn(TString::Format("Parameter '%s' from set '%s' is not OK, skipping", name.Data(), setName.Data()));
folders.push_back(NULL);
} else {
folders.push_back(var);
if(findMax){
Double_t high,low;
var->getTagDouble("High", high);
var->getTagDouble("Low", low);
max = std::max(max,std::max(fabs(high),fabs(low)));
}
}
}
if(relativeToValue) max /= fabs(set->getTagDoubleDefault("val",0.));
if(relativeToUncertainty) max /= std::max(fabs(set->getTagDoubleDefault("errHigh",0.)),fabs(set->getTagDoubleDefault("errLow",0.)));
if(master){
if(config->getTagBoolDefault("sort",master)){
if(relativeToValue){
std::sort(folders.begin(), folders.end(),
[](TQFolder* a,TQFolder* b) -> bool
{
return (a && b && (fabs(a->getTagDoubleDefault( "Avg_Rel" ,0.)) > fabs(b->getTagDoubleDefault("Avg_Rel",0.)))) || (a && !b);
}
);
} else {
std::sort(folders.begin(), folders.end(),
[](TQFolder* a,TQFolder* b) -> bool
{
return (a && b && (fabs(a->getTagDoubleDefault( "Avg" ,0.)) > fabs(b->getTagDoubleDefault("Avg",0.)))) || (a && !b);
});
}
}
parameter.clear();
std::vector<TQFolder*> tmp;
int nMax = std::min((int)(folders.size()),config->getTagIntegerDefault("nMax",folders.size()));
if(nMax <= 0) nMax = folders.size();
for(size_t i = nMax; i>0; --i){
TQFolder* f = folders[i-1];
if(!f){
// if some parameter is missing from the master list, we just skip it - it's not included in the plot then
continue;
} else {
parameter.push_back(f->GetName());
tmp.push_back(f);
}
}
folders = tmp;
}
manager->info(TString::Format("plotting parameter breakdown '%s' as %s",set->GetName(),master ? "master" : "slave"));
for(auto var:folders){
i++;
if (!var) {
continue;
}
TQFolder * par = overlay->getFolder(TString::Format("p.i%d+", i));
double high = 0;
double low = 0;
if(relativeToValue){
overlay->setTagBool(".relativeToValue", true);
if(!var->getTagDouble("High_Rel",high)){ var->getTagDouble("High",high); high /= fabs(set->getTagDoubleDefault("val",0.)); par->setTagBool(".fallback", true); };
if(!var->getTagDouble("Low_Rel",low)){ var->getTagDouble("Low",low); low /= fabs(set->getTagDoubleDefault("val",0.)); par->setTagBool(".fallback", true);};
} else if(relativeToUncertainty){
overlay->setTagBool(".relativeToUncertainty", true);
if(!var->getTagDouble("High_RelUnc",high)){ var->getTagDouble("High",high); high /= fabs(set->getTagDoubleDefault("errHigh",0.)); par->setTagBool(".fallback", true); };
if(!var->getTagDouble("Low_RelUnc",low)){ var->getTagDouble("Low",low); low /= fabs(set->getTagDoubleDefault("errLow",0.)); par->setTagBool(".fallback", true);};
} else {
var->getTagDouble("High",high);
var->getTagDouble("Low",low);
}
par->setTagDouble(".index", i);
par->setTagDouble(".low", low);
par->setTagDouble(".high", high);
if(vertical){
par->setTagDouble("y", i);
par->setTagDouble("x", 0);
par->setTagDouble("yp", 0.5+i);
par->setTagDouble("yn", -0.5+i);
par->setTagDouble("xn", low /max);
par->setTagDouble("xp", high/max);
} else {
par->setTagDouble("x", i);
par->setTagDouble("xp", 0.5+i);
par->setTagDouble("xn", -0.5+i);
par->setTagDouble("y", 0);
par->setTagDouble("yn", low /max);
par->setTagDouble("yp", high/max);
}
}
overlay->setTagString("template", "ranking");
if(axis){
if(vertical){
output->setTagDouble("style.yMaxMin",(0.015*i)+i+2);
axis->setTagDouble("x1",-1.1);
axis->setTagDouble("x2", 1.1);
axis->setTagDouble("y",(0.015*i)+i+1);
axis->setTagString("style.drawOption","<->");
axis->setTagDouble("style.labelSize", set->getTagDoubleDefault("style.labelSize", 0.025));
axis->setTagDouble("style.textSize", set->getTagDoubleDefault("style.textSize", 0.025));
axis->setTagDouble("style.titleOffset", set->getTagDoubleDefault("style.titleOffsetSize", 1));
axis->setTagInteger("ndiv", set->getTagIntegerDefault("ndiv", 510));
axis->setTagInteger("style.lineColor", set->getTagIntegerDefault("style.lineColor", 1));
axis->setTagDouble("style.lineWidth", set->getTagDoubleDefault("style.lineWidth", 2.));
}
axis->setTagString("style.title",config->getTagStringDefault("ranking.axis",relativeToValue ? "#Delta#mu/#mu" : "#Delta#mu"));
axis->setTagDouble("wmin",-max);
axis->setTagDouble("wmax",+max);
}
return i;
}
bool makeParameterPulls(TQFolder * config, TQFolder * set, TQFolder* output, std::vector<TString>& parameter, bool master, double shift) const{
TQFolder* overlay = output->getFolder(TString::Format("Overlay.Pulls.%s+", set->GetName()));
bool vertical = config->getTagBoolDefault("style.vertical",false);
TString setName = set->GetName();
overlay->importTags(set);
if(config->getTagBoolDefault("sort",master)){
std::sort(parameter.begin(), parameter.end(),
[set](const TString& aname, const TString& bname) -> bool
{
TQFolder* a = set->getFolder(aname);
TQFolder* b = set->getFolder(bname);
if(!a || !b) return false;
double aerrsym = a->getTagDoubleDefault( "err" ,0.);
double berrsym = b->getTagDoubleDefault( "err" ,0.);
double aerr = 0.5*(fabs(a->getTagDoubleDefault( "errLow" ,aerrsym))+fabs(a->getTagDoubleDefault( "errHigh" ,aerrsym)));
double berr = 0.5*(fabs(b->getTagDoubleDefault( "errLow" ,berrsym))+fabs(b->getTagDoubleDefault( "errHigh" ,berrsym)));
return aerr > berr;
}
);
}
double constraintThreshold = config->getTagDoubleDefault("constraintThreshold",-500);
/* loop over parameter */
Int_t i = -1;
for(auto name:parameter){
DEBUG("collecting " + name);
i++;
/* load parameter */
TQFolder * var = set->getFolder(name);
if (!var) {
manager->warn(TString::Format("Parameter '%s' does not exist in set '%s'", name.Data(), setName.Data()));
continue;
}
Double_t val;
Double_t err;
// parameter central value
var->getTagDouble("val", val);
// parameter symmetric uncertainty
var->getTagDouble("err", err);
// parameter asymmetric uncertainties [please note: uncertainties will
// be either symmetric (one value) or asymmetric (two values). Here, the
// symmetric value is used as default for the asymmetric values]
Double_t errHigh = err;
Double_t errLow = -err;
var->getTagDouble("errHigh", errHigh);
var->getTagDouble("errLow", errLow);
double constrain = 1.-(fabs(errHigh-errLow)/2);
if(100*constrain < constraintThreshold) continue;
TQFolder * par = overlay->getFolder(TString::Format("p.i%d+", i));
par->setTagDouble(".index", i);
par->setTagDouble(".val", val);
par->setTagDouble(".errLow", errLow);
par->setTagDouble(".errHigh", errHigh);
if(vertical){
par->setTagDouble("y", shift + i);
par->setTagDouble("x", val);
par->setTagDouble("xn", val - fabs(errLow));
par->setTagDouble("xp", val + fabs(errHigh));
} else {
par->setTagDouble("x", shift + i);
par->setTagDouble("y", val);
par->setTagDouble("yn", val - fabs(errLow));
par->setTagDouble("yp", val + fabs(errHigh));
}
}
overlay->setTagString("template", "nuis");
return i;
}
virtual void writeJSON(TQFolder* output, const TString& ofname)const{
if(!output){
throw std::runtime_error("no data given!");
}
TCollection* pars = output->getListOfFolders("Text.p.*");
if(!pars || pars->GetEntries()<1){
manager->error("parameter list is empty!");
}
//const int npar = pars->GetEntries();
std::ofstream out(TQPathManager::getPathManager()->getTargetPath(ofname).c_str());
TQFolderIterator pnames(output->getListOfFolders("Text.p.*"),true);
std::vector<TString> pnamelist;
while(pnames.hasNext()){
TQFolder* line = pnames.readNext();
TString text = line->getTagStringDefault("text");
pnamelist.push_back(text);
}
out << "ranking_plots = {\n";
TQFolderIterator rankings(output->getListOfFolders("Overlay.Ranking.*"),true);
//int nrankings = 0;
// rankings
while(rankings.hasNext()){
TQFolder* ranking = rankings.readNext();
TString name(ranking->GetName());
TQStringUtils::removeLeadingText(name,"Overlay.");
TQStringUtils::removeLeadingText(name,"Ranking.");
TQStringUtils::removeLeadingText(name,"RANKING.");
out << " " << TQStringUtils::quote(name) << " : {\n";
TString title;
out << " ";
if(ranking->getTagString("style.title",title)){
out << TQStringUtils::quote("title") << " : " << TQStringUtils::quote(title) << ", ";
}
out << TQStringUtils::quote("data") << " : {\n";
TQFolderIterator lines(ranking->getListOfFolders("p.*"),true);
while(lines.hasNext()){
TQFolder* line = lines.readNext();
int index = line->getTagIntegerDefault(".index",-1);
double xn = line->getTagDoubleDefault(".low");
double xp = line->getTagDoubleDefault(".high");
out << " " << TQStringUtils::quote(pnamelist[index]) << " : { "
<< TQStringUtils::quote("low") << " : " << xn << ", "
<< TQStringUtils::quote("high") << " : " << xp << " },\n";
}
out << " }\n }\n";
}
out << "}\n";
// pulls
out << "pull_plots = {\n";
TQFolderIterator pulls(output->getListOfFolders("Overlay.Pulls.*"),true);
while(pulls.hasNext()){
TQFolder* pull = pulls.readNext();
TString name(pull->GetName());
TQStringUtils::removeLeadingText(name,"Overlay.");
TQStringUtils::removeLeadingText(name,"Pulls.");
TQStringUtils::removeLeadingText(name,"PULLS.");
out << " " << TQStringUtils::quote(name) << " : {\n";
// points and lines
TString title;
out << " ";
if(pull->getTagString("style.title",title)){
out << TQStringUtils::quote("title") << " : " << TQStringUtils::quote(title) << ", ";
}
out << TQStringUtils::quote("data") << " : {\n";
TQFolderIterator lines(pull->getListOfFolders("p.*"),true);
while(lines.hasNext()){
TQFolder* line = lines.readNext();
double xn = line->getTagDoubleDefault(".errLow");
double xp = line->getTagDoubleDefault(".errHigh");
double x = line->getTagDoubleDefault(".val");
//double constrain = fabs(xn-xp)/2;
int index = line->getTagIntegerDefault(".index",-1);
out << " " << TQStringUtils::quote(pnamelist[index]) << " : { "
<< TQStringUtils::quote("central") << " : " << x << ", "
<< TQStringUtils::quote("low") << " : " << xn << ", "
<< TQStringUtils::quote("high") << " : " << xp << " },\n";
}
out << " }\n },\n";
}
out << "}\n";
out.close();
}
virtual TQTable* makeTable(TQFolder* config, TQFolder* output)const{
if(!output){
throw std::runtime_error("no data given!");
}
TCollection* pars = output->getListOfFolders("Text.p.*");
if(!pars || pars->GetEntries()<1){
manager->error("parameter list is empty!");
return NULL;
}
const int npar = pars->GetEntries();
TQTable* tab = new TQTable("summary");
tab->importTagsWithoutPrefix(config,"writeTable.style.");
tab->setEntry(0,0,"Contribution");
tab->expand(npar+2,5);
std::list<TQFolder*> pnamelist;
TQFolderIterator pnames(output->getListOfFolders("Text.p.*"),true);
while(pnames.hasNext()){
TQFolder* line = pnames.readNext();
pnamelist.push_front(line);
}
size_t ipar=2;
for(auto line:pnamelist){
TString text = line->getTagStringDefault("text");
if(text.Contains("\\")){
tab->setEntry(ipar,0,"\\ensuremath{"+text+"}");
} else {
tab->setEntry(ipar,0,text);
}
ipar++;
}
int ncols = 0;
// breakdowns
if(config->getTagBoolDefault("showBreakdown",true)){
TQFolderIterator breakdowns(output->getListOfFolders("Overlay.Ranking.*"),true);
while(breakdowns.hasNext()){
TQFolder* breakdown = breakdowns.readNext();
ncols++;
TString title;
TString POIname = breakdown->getTagStringDefault("~style.POI", "POI");
if (breakdown->getTagBoolDefault(".relativeToValue", false)){
tab->setEntry(0,ncols,TString::Format("Avg. contribution (%% of %s)", POIname.Data()));
}
else if (breakdown->getTagBoolDefault(".relativeToUncertainty", false)){
tab->setEntry(0,ncols,TString::Format("Avg. contribution (%% of %s unc.)", POIname.Data()));
}
else{
tab->setEntry(0,ncols,TString::Format("Avg. contribution (abs. to %s)", POIname.Data()));
}
if(breakdown->getTagString("style.title",title)){
tab->setEntry(1,ncols,title);
}
TQFolderIterator lines(breakdown->getListOfFolders("p.*"),true);
std::list<TQFolder*> linelist;
while(lines.hasNext()){
TQFolder* line = lines.readNext();
linelist.push_front(line);
}
ipar=2;
for(auto line:linelist){
double xn = line->getTagDoubleDefault(".low");
double xp = line->getTagDoubleDefault(".high");
tab->setEntryValue(ipar,ncols,100.*0.5*(fabs(xn)+fabs(xp)));
if (line->getTagBoolDefault(".fallback", false)){
tab->setProperty(ipar,ncols,"italic", true);
}
ipar++;
}
}
}
// central values
if(config->getTagBoolDefault("writeTable.showCentralValue",true)){
TQFolderIterator pulls(output->getListOfFolders("Overlay.Pulls.*"),true);
while(pulls.hasNext()){
TQFolder* pull = pulls.readNext();
ncols++;
TString title;
tab->setEntry(0,ncols,"Central Value");
if(pull->getTagString("style.title",title)){
tab->setEntry(1,ncols,title);
}
TQFolderIterator lines(pull->getListOfFolders("p.*"),true);
std::list<TQFolder*> linelist;
while(lines.hasNext()){
TQFolder* line = lines.readNext();
linelist.push_front(line);
}
for(auto line:linelist){
TString name(line->GetName());
TQStringUtils::removeLeadingText(name,"p.i");
int idx = atof(name.Data());
double cv = line->getTagDoubleDefault(".val");
tab->setEntryValue(1+npar-idx,ncols,cv);
}
ncols++;
}
}
// errors
if(config->getTagBoolDefault("writeTable.showErrors",false)){
TQFolderIterator pulls(output->getListOfFolders("Overlay.Pulls.*"),true);
while(pulls.hasNext()){
TQFolder* pull = pulls.readNext();
ncols++;
TString title;
tab->setEntry(0,ncols,"Uncertainty");
if(pull->getTagString("style.title",title)){
tab->setEntry(1,ncols,title);
}
TQFolderIterator lines(pull->getListOfFolders("p.*"),true);
std::list<TQFolder*> linelist;
while(lines.hasNext()){
TQFolder* line = lines.readNext();
linelist.push_front(line);
}
for(auto line:linelist){
TString name(line->GetName());
TQStringUtils::removeLeadingText(name,"p.i");
int idx = atof(name.Data());
double xn = line->getTagDoubleDefault(".errLow");
double xp = line->getTagDoubleDefault(".errHigh");
//double x = line->getTagDoubleDefault(".val");
tab->setEntryValue(1+npar-idx,ncols,xn);
tab->setEntryValue(1+npar-idx,ncols+1,xp);
}
ncols++;
}
}
// pulls
if(config->getTagBoolDefault("writeTable.showConstraint",true)){
TQFolderIterator pulls(output->getListOfFolders("Overlay.Pulls.*"),true);
while(pulls.hasNext()){
TQFolder* pull = pulls.readNext();
ncols++;
TString title;
tab->setEntry(0,ncols,"Constraint in \\%");
if(pull->getTagString("style.title",title)){
tab->setEntry(1,ncols,title);
}
TQFolderIterator lines(pull->getListOfFolders("p.*"),true);
std::list<TQFolder*> linelist;
while(lines.hasNext()){
TQFolder* line = lines.readNext();
linelist.push_front(line);
}
for(auto line:linelist){
TString name(line->GetName());
TQStringUtils::removeLeadingText(name,"p.i");
int idx = atof(name.Data());
double xn = line->getTagDoubleDefault(".errLow");
double xp = line->getTagDoubleDefault(".errHigh");
//double x = line->getTagDoubleDefault(".val");
double constrain = 1.-(fabs(xn-xp)/2);
tab->setEntryValue(1+npar-idx,ncols,100*constrain);
}
ncols++;
}
}
// Consistency of pull with constraint
if(config->getTagBoolDefault("writeTable.showPullSignificance",true)){
TQFolderIterator pullSigs(output->getListOfFolders("Overlay.Pulls.*"),true);
while(pullSigs.hasNext()){
TQFolder* pull = pullSigs.readNext();
ncols++;
TString title;
tab->setEntry(0,ncols,"Pull significance");
if(pull->getTagString("style.title",title)){
tab->setEntry(1,ncols,title);
}
TQFolderIterator lines(pull->getListOfFolders("p.*"),true);
std::list<TQFolder*> linelist;
while(lines.hasNext()){
TQFolder* line = lines.readNext();
linelist.push_front(line);
}
for(auto line:linelist){
TString name(line->GetName());
TQStringUtils::removeLeadingText(name,"p.i");
int idx = atof(name.Data());
double xn = line->getTagDoubleDefault(".errLow");
double xp = line->getTagDoubleDefault(".errHigh");
double x = line->getTagDoubleDefault(".val");
double pullErr=(abs(xp)+abs(xn))/2.;
// The pull significance is x/(sqrt(1-pullErr^2)), where pullErr is the average of the high/low errors.
// If the pull error is close to 1 the method is not accurate, and eventually the sqrt cannot be evaluated
// Assume a post-fit error of 0.999, for which the pullSig is 1 if the pull is 0.014 (very small)
if (abs(pullErr)>0.999) pullErr=0.999;
double pullSig = x/(sqrt(1-pullErr*pullErr));
tab->setEntryValue(1+npar-idx,ncols,pullSig);
}
ncols++;
}
}
for(int i=0; i<tab->getNrows(); ++i){
bool keep = false;
for(int j=1; j<tab->getNcols(); ++j){
if(tab->hasEntry(i,j)) keep=true;
}
if(!keep) tab->removeEntry(i,0);
}
tab->shrink();
return tab;
}
virtual bool makePlot(TQFolder* config, TQFolder* output) const {
TSStatisticsPlotter pl;
TString tqpath = TQLibrary::getTQPATH();
// set plot style templates
TQFolder * templates = TQFolder::loadFromTextFile(tqpath+"/../SFramework/share/templates/OverlayTemplates.txt");
pl.setTemplates(templates);
bool vertical = config->getTagBoolDefault("style.vertical",false);
// load some basic plot elements (e.g. lines representing 0, and +/- 1)
TString path = config->getTagStringDefault("arrangement",
tqpath+"/../SFramework/share/templates/PlotNuisPull_"+(vertical ? "vertical" : "horizontal")+".txt");
config->exportTags(output,"","style.*");
config->exportTags(output,"","labels.*");
gSystem->ExpandPathName(path);
output->setGlobalOverwrite(false);
output->importFromTextFile(TQPathManager::getPathManager()->getTargetPath(path).c_str());
if(config->getTagBoolDefault("ATLAS",false)){
output->setTagBool("labels.drawATLAS",true);
TString label;
if(config->getTagString("ATLASlabel",label)){
output->setTagString("labels.atlas",label);
}
}
TCanvas * c = pl.plot(output);
delete templates;
if (c) {
TString outputfile = config->getTagStringDefault("outputFile", "plot.pdf");
TQUtils::ensureDirectoryForFile(TQPathManager::getPathManager()->getTargetPath(outputfile).c_str());
c->SaveAs(TQPathManager::getPathManager()->getTargetPath(outputfile).c_str());
delete c;
return true;
}
return false;
}
};
namespace {
bool available = TSStatisticsManager::registerAction(new PlotResults(),"PlotResults");
}
class PlotResultsTikZ : public TSBaseActions::PlotResults {
virtual bool makePlot(TQFolder* config, TQFolder* output)const{
if(!config){
throw std::runtime_error("no config given!");
}
if(!output){
throw std::runtime_error("no output given!");
}
TString outputfile;
if(!config->getTagString("outputFile",outputfile)){
manager->error("no output filename set!");
return false;
}
if(TQStringUtils::removeTrailingText(outputfile,".pdf") > 0){
manager->warn("renaming output file from '.pdf' to '.tex'");
TQStringUtils::ensureTrailingText(outputfile,".tex");
}
TQUtils::ensureDirectoryForFile(TQPathManager::getPathManager()->getTargetPath(outputfile).c_str());
double textscale = config->getTagDoubleDefault("tikz.textScale",1.0);
bool vertical = config->getTagBoolDefault("style.vertical",false);
std::stringstream tex;
std::stringstream defs;
std::stringstream legend;
TCollection* pars = output->getListOfFolders("Text.p.*");
if(!pars || pars->GetEntries()<1){
manager->error("parameter list is empty!");
return false;
}
const int npar = pars->GetEntries();
delete pars;
tex << "\\pgfdeclarelayer{background}\\pgfsetlayers{background,main}\n";
TQFolderIterator pnames(output->getListOfFolders("Text.p.*"),true);
std::list<TQFolder*> pnamelist;
while(pnames.hasNext()){
TQFolder* line = pnames.readNext();
pnamelist.push_front(line);
}
for(auto line:pnamelist){
double x = line->getTagDoubleDefault("x");
double y = line->getTagDoubleDefault("y");
TString text = line->getTagStringDefault("text");
tex << "\\node[scale="<<textscale;
if(vertical) tex << ",anchor=west";
else tex << ",anchor=east";
if(line->hasTagDouble("style.textAngle")) tex << ",rotate=" << line->getTagDoubleDefault("style.textAngle",0);
tex << "] at (";
if(vertical) tex << x << "," << y-npar;
else tex << x+1 << "," << -2;
tex << ") {";
if(text.Contains("{") && !text.Contains("$")){
tex << "\\ensuremath{" << text << "}";
} else if(!text.Contains("$")){
text.ReplaceAll("_","\\_");
tex << text;
} else {
tex << text;
}
tex << "};\n";
}
TQFolderIterator rankings(output->getListOfFolders("Overlay.Ranking.*"),true);
int ilegend = 0;
int nrankings = 0;
//add definition for custom hatching patterns (e.g. with support for custom densities)
const TString hatchPatternName = "custom north west lines";
// rankings
std::stringstream breakdowntext;
while(rankings.hasNext()){
TQFolder* ranking = rankings.readNext();
int fillstyle = ranking->getTagIntegerDefault("style.fillStyle",1);
double fillOpacity = ranking->getTagDoubleDefault("style.fillOpacity",1.0);
TString styleName = TQFolder::makeValidIdentifier(ranking->GetName());
TString colorNamePos = "col"+styleName+"Pos";
TString colorNameNeg = "col"+styleName+"Neg";
TString colorNameHatchPos = "col"+styleName+"HatchPos";
TString colorNameHatchNeg = "col"+styleName+"HatchNeg";
//extractColorNumberFromTag(TQTaggable* config, const TString& tagName, int fallback/*TColor number*/)
int color = extractColorNumberFromTag(ranking, "style.fillColor", kOrange-2);
//ranking->getTagIntegerDefault("style.fillColor",kOrange-2);
int poscolor = extractColorNumberFromTag(ranking, "style.fillColor.pos", color);
//ranking->getTagIntegerDefault("style.fillColor.pos",color);
int negcolor = extractColorNumberFromTag(ranking, "style.fillColor.neg", color);
//ranking->getTagIntegerDefault("style.fillColor.neg",color);
defs << TQStringUtils::getColorDefStringLaTeX(colorNamePos,poscolor)<<"\n";
defs << TQStringUtils::getColorDefStringLaTeX(colorNameNeg,negcolor)<<"\n";
bool hatchDefault = ranking->getTagBoolDefault("style.hatch",false);
int hatchSpread = ranking->getTagIntegerDefault("style.hatchSpread",-1);
//hatching/pattern color
int posHatchColor = extractColorNumberFromTag(ranking, "style.hatchColor.pos", poscolor);
int negHatchColor = extractColorNumberFromTag(ranking, "style.hatchColor.neg", negcolor);
int posHatchSpread = ranking->getTagIntegerDefault("style.hatchSpread.pos",hatchSpread);
int negHatchSpread = ranking->getTagIntegerDefault("style.hatchSpread.neg",hatchSpread);
defs << TQStringUtils::getColorDefStringLaTeX(colorNameHatchPos,posHatchColor)<<"\n";
defs << TQStringUtils::getColorDefStringLaTeX(colorNameHatchNeg,negHatchColor)<<"\n";
/*
TString hatchPatternName = "";
if (ranking->getTagBoolDefault("style.hatch.pos",hatchDefault) || ranking->getTagBoolDefault("style.hatch.neg",hatchDefault)) {
double hatchDistance = ranking->getTagDoubleDefault("style.hatchDistance",10.); //distance between lines in pt
defs << defineTikzHatchPattern(hatchDistance, styleName, hatchPatternName) << "\n";
}
*/
defs << "\\tikzset{"<<styleName<<"Pos" << "/.style={";
if(fillstyle==1){
defs << "draw=none,fill=" << colorNamePos << ",text opacity = 1.0, fill opacity=" << fillOpacity << ",";
} else if (fillstyle == 0){
defs << "draw=" << colorNamePos << ",";
}
if (ranking->getTagBoolDefault("style.hatch.pos",hatchDefault)) {
//defs << "pattern=" << hatchPatternName << ", pattern color=" << colorNameHatchPos << ",";
defs << "pattern=" << hatchPatternName << ", hatchcolor=" << colorNameHatchPos << ",";
if (posHatchSpread>0) defs <<"hatchspread=" << posHatchSpread << "pt,";
}
defs << "}}\n";
defs << "\\tikzset{"<<styleName<<"Neg" << "/.style={";
if(fillstyle==1){
defs << "draw=none,fill=" << colorNameNeg << ",text opacity = 1.0, fill opacity=" << fillOpacity << ",";
} else if (fillstyle == 0){
defs << "draw=" << colorNameNeg << ",";
}
if (ranking->getTagBoolDefault("style.hatch.neg",hatchDefault)) {
//defs << "pattern=" << hatchPatternName << ", pattern color=" << colorNameHatchNeg << ",";
defs << "pattern=" << hatchPatternName << ", hatchcolor=" << colorNameHatchNeg << ",";
if (negHatchSpread>0) defs <<"hatchspread=" << negHatchSpread << "pt,";
}
defs << "}}\n";
// boxes
TQFolderIterator lines(ranking->getListOfFolders("p.*"),true);
std::list<TQFolder*> linelist;
while(lines.hasNext()){
TQFolder* line = lines.readNext();
linelist.push_front(line);
}
for(auto line:linelist){
double yn = line->getTagDoubleDefault("yn");
double yp = line->getTagDoubleDefault("yp");
double xn = line->getTagDoubleDefault(".low");
double xp = line->getTagDoubleDefault(".high");
breakdowntext << " \\draw[" << styleName << "Neg](" << xn << "," << yn-npar << ") rectangle (" << 0 << "," << yp-npar << ");\n";
breakdowntext << " \\draw[" << styleName << "Pos](" << 0 << "," << yn-npar << ") rectangle (" << xp << "," << yp-npar << ");\n";
}
// legend
TString title;
if(ranking->hasTagString("style.title.pos") || ranking->hasTagString("style.title.neg")) {
//positive part
legend << "\\draw[" << styleName << "Pos](" << -1 << "," << -0.3+ilegend << ") rectangle (" << 1 << "," << 0.3+ilegend << ");\n";
legend << "\\node[anchor=west] at(1.1,"<<ilegend<<"){" << TQStringUtils::convertROOTTeX2LaTeX(ranking->getTagStringDefault("style.title.pos",ranking->getTagStringDefault("style.title","missing title, use style.title.pos to specify") ) ) << "};\n";
ilegend++;
//negative part
legend << "\\draw[" << styleName << "Neg](" << -1 << "," << -0.3+ilegend << ") rectangle (" << 1 << "," << 0.3+ilegend << ");\n";
legend << "\\node[anchor=west] at(1.1,"<<ilegend<<"){" << TQStringUtils::convertROOTTeX2LaTeX(ranking->getTagStringDefault("style.title.neg",ranking->getTagStringDefault("style.title","missing title, use style.title.neg to specify") ) ) << "};\n";
ilegend++;
} else if(ranking->getTagString("style.title",title)) {
legend << "\\draw[" << styleName << "Neg](" << -1 << "," << -0.3+ilegend << ") rectangle (" << 0 << "," << 0.3+ilegend << ");\n";
legend << "\\draw[" << styleName << "Pos](" << 0 << "," << -0.3+ilegend << ") rectangle (" << 1 << "," << 0.3+ilegend << ");\n";
legend << "\\node[anchor=west] at(1.1,"<<ilegend<<"){" << TQStringUtils::convertROOTTeX2LaTeX(title) << "};\n";
ilegend++;
}
nrankings++;
}
TString namePOI = config->getTagStringDefault("~style.POI", "\\mu");
TQFolder* textReplacements = TQFolder::newFolder("textReplacements");
for(const auto& replfile:config->getTagVString("textReplacements")){
textReplacements->importFromTextFile(TQPathManager::getPathManager()->findConfigPath(replfile));
}
TString titlePOI = namePOI;
if(textReplacements->getFolder(namePOI)){
textReplacements->getFolder(namePOI)->getTagString("title",titlePOI);
}
delete textReplacements;
// ranking axis
if(nrankings>0){
int effColorInt = extractColorNumberFromTag(config, "style.effectAxisColor", kRed);
defs << TQStringUtils::getColorDefStringLaTeX("colEffectAxes",effColorInt)<<"\n";
tex << "\\begin{scope}[name=ranking,colEffectAxes,";
TQFolder* rankingaxis = output->getFolder("Axis.*");
double wmin = rankingaxis->getTagDoubleDefault("wmin");
double wmax = rankingaxis->getTagDoubleDefault("wmax");
double abswmax = std::max(fabs(wmin),fabs(wmax));
double abswmax_round = TQUtils::roundAutoUp(2*abswmax,1)/2.;
double xscale = 1./abswmax_round;
tex << "xscale=" << xscale;
tex << "]\n";
tex << breakdowntext.str();
int nTicks = config->getTagIntegerDefault("nRankingTicksPerSide",2);
double step = abswmax_round/nTicks; // with a total of 2*n+1 ticks (including the one at 0), we have 2*n intervalls. Hence, the interval width (=step) is given as calculated here. abswmax_round is already reasonably rounded, so no need for fancy rounding magic here anymore (we get at most one additional digit which is always a 5 (or 0).
//std::cout<<"abswmax = "<<abswmax<<", abswmax_round = "<<abswmax_round<<", step = "<<step<<std::endl;
for(double x=0; x<=abswmax_round; x+=step){
tex << " \\draw (" << x << "," << -0.2 << ") -- (" << x << "," << 0 << ") node [axlbl,above=3pt]{";
tex << x;
tex << "};\n";
}
for(double x=-step; x>=-abswmax_round; x-=step){
tex << " \\draw (" << x << "," << -0.2 << ") -- (" << x << "," << 0 << ") node [axlbl,above=3pt]{";
tex << x;
tex << "};\n";
}
tex << " \\draw (" << -abswmax_round << "," << 0 << ") -- (" << abswmax_round << "," << 0 << ");\n";
tex << " \\node[axlbl,above left=5pt] at (" << -1.1*abswmax_round << "," << 0 << ") {";
if (config->hasTagString("style.effectAxisLabel")) {
tex << config->getTagStringDefault("style.effectAxisLabel","");
} else {
if(config->getTagBoolDefault("ranking.relativeToValue",false)){
tex << "$\\frac{\\Delta "+titlePOI+"}{"+titlePOI+"}$";
} else if(config->getTagBoolDefault("ranking.relativeToUncertainty",false)){
tex << "$\\frac{\\Delta "+titlePOI+"}{\\sigma}$";
} else {
tex << "$\\Delta "+titlePOI+"$";
}
}
tex << "};\n";
tex << "\\end{scope}\n";
}
tex << "\\begin{scope}[name=pulls]\n";
// pulls
int nPulls = 0;
TQFolderIterator pulls(output->getListOfFolders("Overlay.Pulls.*"),true);
while(pulls.hasNext()){
TQFolder* pull = pulls.readNext();
int color = pull->getTagIntegerDefault("style.color",kBlack);
TString styleName = TQFolder::makeValidIdentifier(pull->GetName());
TString colorName = styleName;
colorName.Prepend("col");
defs << TQStringUtils::getColorDefStringLaTeX(colorName,color)<<"\n";
defs << "\\tikzset{"<<styleName << "/.style={pull,color="<<colorName<<"}}\n";
// points and lines
TQFolderIterator lines(pull->getListOfFolders("p.*"),true);
std::list<TQFolder*> linelist;
while(lines.hasNext()){
TQFolder* line = lines.readNext();
linelist.push_front(line);
}
for(auto line:linelist){
double x = line->getTagDoubleDefault("x");
double y = line->getTagDoubleDefault("y");
if(vertical){
double xn = line->getTagDoubleDefault("xn");
double xp = line->getTagDoubleDefault("xp");
tex << " \\draw["<<styleName<<"] (" << xn << "," << y-npar << ") -- (" << xp << "," << y-npar << ");";
tex << " \\node[dot,"<<colorName<<"] at (" << x << "," << y-npar << ") {};\n";
} else {
double yn = line->getTagDoubleDefault("yn");
double yp = line->getTagDoubleDefault("yp");
tex << " \\draw["<<styleName<<"] (" << x+1 << "," << yn << ") -- (" << x+1 << "," << yp << ");";
tex << " \\node[dot,"<<colorName<<"] at (" << x+1 << "," << y << ") {};\n";
}
}
nPulls++;
// legend
TString title; if(pull->getTagString("style.title",title)){
legend << "\\draw["<<styleName<<"](" << -1 << "," << ilegend << ") -- (" << 1 << "," << ilegend << ");";
legend << "\\node[dot,"<<colorName<<"] at (" << 0 << "," << ilegend << ") {};\n";
legend << "\\node[anchor=west] at(1.1,"<<ilegend<<"){" << TQStringUtils::convertROOTTeX2LaTeX(title) << "};\n";
ilegend++;
}
}
// pull axis
int pullColorInt = extractColorNumberFromTag(config, "style.pullAxisColor", kRed);
defs << TQStringUtils::getColorDefStringLaTeX("colPullAxes",pullColorInt)<<"\n";
if(nPulls>0){
double pullAxMin = config->getTagDoubleDefault("style.pullAxisMin",-1);
double pullAxDiv = config->getTagDoubleDefault("style.pullAxisDiv",.25);
double pullAxMid = config->getTagDoubleDefault("style.pullAxisMid",0);
double pullAxMax = config->getTagDoubleDefault("style.pullAxisMax",+1);
TString pullAxLabel = config->getTagStringDefault("style.pullAxisTitle","$\\frac{\\theta-\\theta_0}{\\Delta\\theta_0}$");
for(double x=pullAxMin; x<=pullAxMax; x+=pullAxDiv){
tex << "\\draw[colPullAxes] (";
if(vertical) tex << x << "," << -npar-0.5+0.2;
else tex << -0.2 << "," << x;
tex << ") -- (";
if(vertical) tex << x << "," << -npar-0.5;
else tex << 0 << "," << x;
tex << ") node [axlbl,";
if(vertical) tex << "below=3pt";
else tex << "left=3pt";
tex << "]{" << x << "};\n";
}
tex << "\\node[axlbl,colPullAxes,anchor=north east,";
tex << "xshift=-1em,yshift=-1em";
tex << "] at (";
if(vertical) tex << pullAxMin << "," << -npar-0.5;
else tex << 0 << "," << pullAxMin;
tex << ") {" << pullAxLabel << "};\n";
tex << "\\draw[colPullAxes] (";
if(vertical) tex << pullAxMin << "," << -npar-0.5;
else tex << 0 << "," << pullAxMin;
tex << ") -- (";
if(vertical) tex << pullAxMax << "," << -npar-0.5;
else tex << 0 << "," << pullAxMax;
tex << ");\n";
tex << "\\draw[dashed,black] (";
if(vertical) tex << pullAxMid << "," << -npar-0.5;
else tex << 0 << "," << pullAxMid;
tex << ") -- (";
if(vertical) tex << pullAxMid << "," << 0;
else tex << npar << "," << pullAxMid;
tex << ");\n";
tex << "\\draw[dashed,colPullAxes] (";
if(vertical) tex << pullAxMin << "," << -npar-0.5;
else tex << 0 << "," << pullAxMin;
tex << ") -- (";
if(vertical) tex << pullAxMin << "," << 0;
else tex << npar << "," << pullAxMin;
tex << ");\n";
tex << "\\draw[dashed,colPullAxes] (";
if(vertical) tex << pullAxMax << "," << -npar-0.5;
else tex << 0 << "," << pullAxMax;
tex << ") -- (";
if(vertical) tex << pullAxMax << "," << 0;
else tex << npar << "," << pullAxMax;
tex << ");\n";
}
tex << "\\end{scope}\n";
// background highlighting
int highlightColor;
if(config->getTagInteger("style.highlight",highlightColor)){
defs << TQStringUtils::getColorDefStringLaTeX("colHighlighting",highlightColor)<<"\n";
tex << "\\begin{pgfonlayer}{background}\n";
tex << " \\foreach \\i in ";
if(npar>3) tex << "{-1,-3,...,"<<-2*((npar+1)/2)<<"}";
else tex << "{1}";
tex << "{\\fill[fill=colHighlighting] let \\p1 = (current bounding box.east), \\p2 = (current bounding box.west) in (\\x1,\\i-0.5) rectangle (\\x2,\\i+0.5); }\n";
tex << "\\end{pgfonlayer}\n";
}
// put everything together
std::ofstream out(TQPathManager::getPathManager()->getTargetPath(outputfile).c_str());
bool standalone = config->getTagBoolDefault("standalone",false);
if(standalone){
out << "\\documentclass{standalone}\n";
out << "\\usepackage{tikz}\n";
out << "\\usetikzlibrary{calc,arrows.meta,decorations.markings,math,patterns}\n";
out << "\\ifpdftex\\usepackage[scaled=1]{helvet}\\fi\n";
out << "\\ifxetex\\usepackage{fontspec}\\setsansfont{TeX Gyre Heros}\\fi\n";
} else {
out << "\\begingroup\n";
}
bool atlas = config->getTagBoolDefault("ATLAS",false);
if(atlas){
out << "\\ifpdftex\n";
out << "\\renewcommand\\sfdefault{phv}\n";
out << "\\renewcommand\\rmdefault{phv}\n";
out << "\\renewcommand\\ttdefault{pcr}\n";
out << "\\fi\n";
}
out << customTikzHatchPattern <<"\n";
if(standalone){
out << "\\begin{document}\n";
}
out << defs.str();
TString xunit = config->getTagStringDefault("tikz.xUnit","3cm");
TString yunit = config->getTagStringDefault("tikz.yUnit",".5cm");
out << "\\begin{tikzpicture}[x="<<xunit<<",y="<<yunit<<",%\n";
out << " axlbl/.style={scale=" << textscale << ",anchor=center},%\n";
out << " pull/.style={{|[scale=" << textscale << "]}-{|[scale=" << textscale << "]}},%\n";
out << " dot/.style={circle,fill,inner sep=1pt,scale="<<textscale<<"},\n";
out << " every node/.append style={font=\\sffamily}\n";
out << "]\n";
TString description = "";
bool hasDesc = config->getTagString("description",description);
if(atlas || hasDesc){
out << "\\node[scale=1.,align=left] at (-0.1,2) {";
if (atlas) {
out << "\\huge\\textbf{ATLAS}";
TString label = config->getTagStringDefault("ATLASlabel","");
if(!label.IsNull()){
out << " " << label;
}
if (hasDesc) out<<"\\\\"; //append newline in case we also have a description
}
if (hasDesc) {
out<<description;
}
out << "};\n";
}
TString legxunit = config->getTagStringDefault("tikz.legend.xUnit","0.3cm");
TString legyunit = config->getTagStringDefault("tikz.legend.yUnit",".5cm");
out << "\\begin{scope}[name=legend,shift={(" << 1.5 << ","<<1<<")},x="<<legxunit<<",y="<<legyunit<<"]";
out << legend.str();
out << "\\end{scope}";
out << tex.str();
out << "\\end{tikzpicture}\n";
if(standalone){
out << "\\end{document}\n";
} else {
out << "\\endgroup\n";
}
manager->info(TString::Format("wrote file '%s'",outputfile.Data()));
return true;
}
};
namespace {
bool available_tikz = TSStatisticsManager::registerAction(new PlotResultsTikZ(),"PlotResultsTikZ");
}
}