Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-05-23 08:08:42

0001 #include <algorithm>
0002 #include <cctype>
0003 #include <cmath>
0004 #include <cstdlib>
0005 #include <iostream>
0006 #include <iomanip>
0007 #include <limits>
0008 #include <map>
0009 #include <set>
0010 #include <sstream>
0011 #include <string>
0012 #include <vector>
0013 
0014 #include "TCanvas.h"
0015 #include "TColor.h"
0016 #include "TFile.h"
0017 #include "TGaxis.h"
0018 #include "TH1D.h"
0019 #include "TH2D.h"
0020 #include "THStack.h"
0021 #include "TKey.h"
0022 #include "TLatex.h"
0023 #include "TLegend.h"
0024 #include "TLine.h"
0025 #include "TPad.h"
0026 #include "TString.h"
0027 #include "TStyle.h"
0028 #include "TSystem.h"
0029 #include "TTree.h"
0030 
0031 // ---------------------------------------------------------------------------
0032 // Build a grid of named TPads that share the canvas margins properly.
0033 // ---------------------------------------------------------------------------
0034 void CanvasPartition(TCanvas *c, int Nx, int Ny, float lMargin, float rMargin, float bMargin, float tMargin)
0035 {
0036     if (!c)
0037         return;
0038 
0039     const float hStep = (1.f - lMargin - rMargin) / Nx;
0040     const float vStep = (1.f - bMargin - tMargin) / Ny;
0041 
0042     for (int i = 0; i < Nx; ++i)
0043     {
0044         float hposl = (i == 0) ? 0.f : lMargin + i * hStep;
0045         float hposr = (i == Nx - 1) ? 1.f : lMargin + (i + 1) * hStep;
0046         float hmarl = (i == 0) ? lMargin / (hposr - hposl) : 0.f;
0047         float hmarr = (i == Nx - 1) ? rMargin / (hposr - hposl) : 0.f;
0048 
0049         for (int j = 0; j < Ny; ++j)
0050         {
0051             float vposd = (j == 0) ? 0.f : bMargin + j * vStep;
0052             float vposu = (j == Ny - 1) ? 1.f : bMargin + (j + 1) * vStep;
0053             float vmard = (j == 0) ? bMargin / (vposu - vposd) : 0.f;
0054             float vmaru = (j == Ny - 1) ? tMargin / (vposu - vposd) : 0.f;
0055 
0056             c->cd(0);
0057             TString name = TString::Format("pad_%d_%d", i, j);
0058             delete c->FindObject(name.Data()); // remove stale pad if any
0059 
0060             auto *pad = new TPad(name, "", hposl, vposd, hposr, vposu);
0061             pad->SetLeftMargin(hmarl);
0062             pad->SetRightMargin(hmarr);
0063             pad->SetBottomMargin(vmard);
0064             pad->SetTopMargin(vmaru);
0065             pad->SetBorderMode(0);
0066             pad->SetBorderSize(0);
0067             pad->SetFrameBorderMode(0);
0068             pad->Draw();
0069         }
0070     }
0071 }
0072 
0073 // ---------------------------------------------------------------------------
0074 // Convert axis-fraction coordinates to pad NDC (accounts for margins).
0075 // ---------------------------------------------------------------------------
0076 static double XtoPad(double x)
0077 {
0078     double lm = gPad->GetLeftMargin(), rm = gPad->GetRightMargin();
0079     return lm + x * (1. - lm - rm);
0080 }
0081 static double YtoPad(double y)
0082 {
0083     double bm = gPad->GetBottomMargin(), tm = gPad->GetTopMargin();
0084     return bm + y * (1. - bm - tm);
0085 }
0086 
0087 template <typename T> static std::string to_string_with_precision(const T a_value, const int n = 2)
0088 {
0089     std::ostringstream out;
0090     out.precision(n);
0091     out << std::fixed << a_value;
0092     return out.str();
0093 }
0094 
0095 static std::string EtaPhiRangeNameSuffix(const TH2D *h2, int ieta, int iphi)
0096 {
0097     const auto *etaAx = h2->GetXaxis();
0098     const auto *phiAx = h2->GetYaxis();
0099     const double etaLow = etaAx->GetBinLowEdge(ieta + 1);
0100     const double etaHigh = etaAx->GetBinUpEdge(ieta + 1);
0101     const double phiLow = phiAx->GetBinLowEdge(iphi + 1);
0102     const double phiHigh = phiAx->GetBinUpEdge(iphi + 1);
0103 
0104     std::ostringstream nm;
0105     nm << "eta" << to_string_with_precision(etaLow) << "-" << to_string_with_precision(etaHigh) << "_phi" << to_string_with_precision(phiLow) << "-" << to_string_with_precision(phiHigh);
0106     return nm.str();
0107 }
0108 
0109 // ---------------------------------------------------------------------------
0110 // Retrieve a cell histogram; tries the requested mode then falls back to dR.
0111 // ---------------------------------------------------------------------------
0112 static TH1D *GetCellHist(TFile *fin, const char *dirname, const std::string &prefix, const std::string &mode, const std::string &tag, int ieta, int iphi, const TH2D *hGrid)
0113 {
0114     if (!fin || !hGrid)
0115         return nullptr;
0116 
0117     std::vector<std::string> dir_candidates;
0118     dir_candidates.push_back(dirname);                // legacy inclusive output
0119     dir_candidates.push_back(prefix + "_" + dirname); // multiplicity-binned outputs
0120 
0121     for (const auto &dir_name : dir_candidates)
0122     {
0123         auto *dir = fin->GetDirectory(dir_name.c_str());
0124         if (!dir)
0125             continue;
0126 
0127         auto tryGet = [&](const std::string &m) -> TH1D *
0128         {
0129             std::string n = prefix + "_" + m + "_" + tag + "_" + EtaPhiRangeNameSuffix(hGrid, ieta, iphi);
0130             return dynamic_cast<TH1D *>(dir->Get(n.c_str()));
0131         };
0132 
0133         if (auto *h = tryGet(mode))
0134             return h; // exact match
0135         if (mode != "dR")
0136             if (auto *h = tryGet("dR"))
0137                 return h; // legacy fallback
0138     }
0139     return nullptr;
0140 }
0141 
0142 static TH2D *GetPrimaryGrid(TFile *fin, const std::string &prefix)
0143 {
0144     if (!fin)
0145         return nullptr;
0146     for (const char *suffix : {"_Sig", "_Bkg", "_Sub"})
0147     {
0148         const std::string hname = prefix + suffix;
0149         if (auto *h = dynamic_cast<TH2D *>(fin->Get(hname.c_str())))
0150         {
0151             return h;
0152         }
0153     }
0154     return nullptr;
0155 }
0156 static TH2D *GetRawCountGrid(TFile *fin, const std::string &prefix)
0157 {
0158     if (!fin)
0159         return nullptr;
0160     return dynamic_cast<TH2D *>(fin->Get((prefix + "_Sub_AbsDphiCount").c_str()));
0161 }
0162 
0163 static constexpr double kDirectRawCountCut = 0.015;
0164 
0165 
0166 
0167 
0168 // ---------------------------------------------------------------------------
0169 // Integral between [xmin, xmax] using bin edges.
0170 // ---------------------------------------------------------------------------
0171 static double IntegralInRange(TH1D *h, double xmin, double xmax)
0172 {
0173     if (!h || xmax <= xmin)
0174         return 0.;
0175     int b1 = h->GetXaxis()->FindBin(xmin + 1e-9);
0176     int b2 = h->GetXaxis()->FindBin(xmax - 1e-9);
0177     return (b2 >= b1) ? h->Integral(b1, b2) : 0.;
0178 }
0179 
0180 static TH1D *MakeFractionalHist(TH1D *h, TH1D *hRef, const std::string &newname)
0181 {
0182     if (!h || !hRef)
0183         return nullptr;
0184 
0185     TH1D *hFrac = dynamic_cast<TH1D *>(h->Clone(newname.c_str()));
0186     if (!hFrac)
0187         return nullptr;
0188     hFrac->SetDirectory(nullptr);
0189 
0190     const int nbins = hFrac->GetNbinsX();
0191     for (int i = 1; i <= nbins; ++i)
0192     {
0193         const double denom = hRef->GetBinContent(i);
0194         if (denom > 0.0)
0195         {
0196             hFrac->SetBinContent(i, h->GetBinContent(i) / denom);
0197             hFrac->SetBinError(i, h->GetBinError(i) / denom);
0198         }
0199         else
0200         {
0201             hFrac->SetBinContent(i, 0.0);
0202             hFrac->SetBinError(i, 0.0);
0203         }
0204     }
0205     return hFrac;
0206 }
0207 
0208 static TH1D *MakeInverseHist(TH1D *h, const std::string &newname)
0209 {
0210     if (!h)
0211         return nullptr;
0212 
0213     TH1D *hInv = dynamic_cast<TH1D *>(h->Clone(newname.c_str()));
0214     if (!hInv)
0215         return nullptr;
0216     hInv->SetDirectory(nullptr);
0217 
0218     const int nbins = hInv->GetNbinsX();
0219     for (int i = 1; i <= nbins; ++i)
0220     {
0221         const double value = h->GetBinContent(i);
0222         const double error = h->GetBinError(i);
0223         if (value != 0.0)
0224         {
0225             hInv->SetBinContent(i, 1.0 / value);
0226             hInv->SetBinError(i, error / (value * value));
0227         }
0228         else
0229         {
0230             hInv->SetBinContent(i, 0.0);
0231             hInv->SetBinError(i, 0.0);
0232         }
0233     }
0234     return hInv;
0235 }
0236 
0237 static void draw2Dhistogram_adjZaxis(TH2 *hist,                                   //
0238                                      bool logz,                                   //
0239                                      const std::string &xtitle,                   //
0240                                      const std::string &ytitle,                   //
0241                                      const std::string &ztitle,                   //
0242                                      std::pair<double, double> zrange,            //
0243                                      const std::vector<std::string> &addinfo,     //
0244                                      const std::string &drawoption = "colz text", //
0245                                      const std::string &paintTextFmt = ".3g",     //
0246                                      bool show_bin_value_with_error = true,       //
0247                                      bool show_bin_value_only = false,            //
0248                                      const std::string &filename = "plot2d")
0249 {
0250     if (!hist)
0251         return;
0252 
0253     gStyle->SetPalette(kLightTemperature);
0254     // gStyle->SetPalette(kThermometer);
0255     // gStyle->SetPalette(kRainbow);
0256     // gStyle->SetPalette(kTemperatureMap);
0257 
0258     TCanvas *c = new TCanvas("c_2d", "c_2d", 900, 800);
0259     c->cd();
0260     gPad->SetRightMargin(0.21);
0261     c->SetLogz(logz);
0262 
0263     gStyle->SetPaintTextFormat(paintTextFmt.c_str());
0264     hist->GetXaxis()->SetTitle(xtitle.c_str());
0265     hist->GetYaxis()->SetTitle(ytitle.c_str());
0266     hist->GetZaxis()->SetTitle(ztitle.c_str());
0267     hist->GetZaxis()->SetTitleOffset(1.5);
0268     if (zrange.first < zrange.second)
0269         hist->GetZaxis()->SetRangeUser(zrange.first, zrange.second);
0270 
0271     std::string drawopt = drawoption;
0272     if (show_bin_value_with_error)
0273     {
0274         const std::vector<std::string> tokens_to_strip = {"TEXT", "text"};
0275         for (const auto &tok : tokens_to_strip)
0276         {
0277             size_t pos = std::string::npos;
0278             while ((pos = drawopt.find(tok)) != std::string::npos)
0279                 drawopt.erase(pos, tok.size());
0280         }
0281         if (drawopt.find("colz") == std::string::npos && drawopt.find("COLZ") == std::string::npos)
0282             drawopt += " colz";
0283     }
0284     else if (show_bin_value_only)
0285     {
0286         const std::vector<std::string> tokens_to_strip = {"TEXT", "text"};
0287         for (const auto &tok : tokens_to_strip)
0288         {
0289             size_t pos = std::string::npos;
0290             while ((pos = drawopt.find(tok)) != std::string::npos)
0291                 drawopt.erase(pos, tok.size());
0292         }
0293         if (drawopt.find("colz") == std::string::npos && drawopt.find("COLZ") == std::string::npos)
0294             drawopt += " colz";
0295     }
0296     hist->Draw(drawopt.c_str());
0297 
0298     if (show_bin_value_with_error)
0299     {
0300         TLatex tbin_val;
0301         tbin_val.SetTextAlign(22);
0302         tbin_val.SetTextFont(42);
0303         tbin_val.SetTextSize(0.018);
0304 
0305         TLatex tbin_err;
0306         tbin_err.SetTextAlign(22);
0307         tbin_err.SetTextFont(42);
0308         tbin_err.SetTextSize(0.015);
0309         // tbin_err.SetTextColor(kRed + 1);
0310 
0311         for (int ibx = 1; ibx <= hist->GetNbinsX(); ++ibx)
0312         {
0313             for (int iby = 1; iby <= hist->GetNbinsY(); ++iby)
0314             {
0315                 const double val = hist->GetBinContent(ibx, iby);
0316                 const double err = hist->GetBinError(ibx, iby);
0317                 const double x = hist->GetXaxis()->GetBinCenter(ibx);
0318                 const double y = hist->GetYaxis()->GetBinCenter(iby);
0319                 const double y_offset = 0.18 * hist->GetYaxis()->GetBinWidth(iby);
0320 
0321                 tbin_val.DrawLatex(x, y + y_offset, Form("%.2f", val));
0322                 tbin_err.DrawLatex(x, y - y_offset, Form("#pm%.3g", err));
0323             }
0324         }
0325     }
0326     else if (show_bin_value_only)
0327     {
0328         TLatex tbin_val;
0329         tbin_val.SetTextAlign(22);
0330         tbin_val.SetTextFont(42);
0331         tbin_val.SetTextSize(0.014);
0332 
0333         for (int ibx = 1; ibx <= hist->GetNbinsX(); ++ibx)
0334         {
0335             for (int iby = 1; iby <= hist->GetNbinsY(); ++iby)
0336             {
0337                 const double val = hist->GetBinContent(ibx, iby);
0338                 const double x = hist->GetXaxis()->GetBinCenter(ibx);
0339                 const double y = hist->GetYaxis()->GetBinCenter(iby);
0340                 tbin_val.DrawLatex(x, y, Form("%.3g", val));
0341             }
0342         }
0343     }
0344 
0345     // for now, the format is only good for 1 line
0346     TLatex latex;
0347     latex.SetNDC();
0348     latex.SetTextFont(42);
0349     latex.SetTextSize(0.035);
0350     for (size_t i = 0; i < addinfo.size(); ++i)
0351     {
0352         latex.DrawLatex(gPad->GetLeftMargin(), (1 - gPad->GetTopMargin() + 0.01) - 0.045 * i, addinfo[i].c_str());
0353     }
0354 
0355     c->SaveAs((filename + ".png").c_str());
0356     c->SaveAs((filename + ".pdf").c_str());
0357     delete c;
0358 }
0359 
0360 static void draw1Dhistogram(TH1 *hist,                                 //
0361                             const bool logy,                           //
0362                             const std::string &xtitle,                 //
0363                             const std::string &ytitle,                 //
0364                             const std::vector<std::string> &addinfo,   //
0365                             const std::string &drawoption = "hist e1", //
0366                             const std::string &filename = "plot1d")
0367 {
0368     if (!hist)
0369         return;
0370 
0371     TCanvas *c = new TCanvas("c_1d", "c_1d", 900, 800);
0372     c->SetLogy(logy);
0373     c->cd();
0374     gPad->SetLeftMargin(0.14);
0375     gPad->SetRightMargin(0.05);
0376     // gPad->SetBottomMargin(0.12);
0377     gPad->SetTopMargin(0.08);
0378 
0379     hist->SetTitle("");
0380     hist->GetXaxis()->SetTitle(xtitle.c_str());
0381     hist->GetYaxis()->SetTitle(ytitle.c_str());
0382     hist->GetYaxis()->SetTitleOffset(1.4);
0383     hist->GetYaxis()->SetRangeUser((logy ? hist->GetMinimum(0) * 0.5 : 0.0), hist->GetMaximum() * (logy ? 10.0 : 1.4));
0384     hist->SetLineWidth(2);
0385     hist->SetLineColor(kBlack);
0386     hist->SetMarkerStyle(kFullCircle);
0387     hist->SetMarkerSize(1.0);
0388     hist->SetMarkerColor(kBlack);
0389     hist->Draw(drawoption.c_str());
0390 
0391     TLatex latex;
0392     latex.SetNDC();
0393     latex.SetTextFont(42);
0394     latex.SetTextSize(0.035);
0395     // set right- and bottom aligned for now, which is only good for 1 line
0396     latex.SetTextAlign(kHAlignRight + kVAlignBottom);
0397     for (size_t i = 0; i < addinfo.size(); ++i)
0398     {
0399         latex.DrawLatex(1 - gPad->GetRightMargin(), (1 - gPad->GetTopMargin() + 0.01) - 0.045 * i, addinfo[i].c_str());
0400     }
0401 
0402     c->SaveAs((filename + ".png").c_str());
0403     c->SaveAs((filename + ".pdf").c_str());
0404     delete c;
0405 }
0406 
0407 static void drawComparisonWithRatio(std::pair<TH1D *, std::string> hDenInput, //
0408                                     std::pair<TH1D *, std::string> hNumInput, //
0409                                     const bool logy,                          //
0410                                     const std::string &xtitle,                //
0411                                     const std::string &ytitle,                //
0412                                     const std::string &ratio_ytitle,          //
0413                                     const std::vector<std::string> &addinfo,  //
0414                                     const std::string &filename)              //
0415 {
0416     if (!hDenInput.first || !hNumInput.first)
0417         return;
0418 
0419     TH1D *hDen = dynamic_cast<TH1D *>(hDenInput.first->Clone((filename + "_den").c_str()));
0420     TH1D *hNum = dynamic_cast<TH1D *>(hNumInput.first->Clone((filename + "_num").c_str()));
0421     TH1D *hRatio = dynamic_cast<TH1D *>(hNumInput.first->Clone((filename + "_ratio").c_str()));
0422     if (!hDen || !hNum || !hRatio)
0423     {
0424         delete hDen;
0425         delete hNum;
0426         delete hRatio;
0427         return;
0428     }
0429 
0430     hDen->SetDirectory(nullptr);
0431     hNum->SetDirectory(nullptr);
0432     hRatio->SetDirectory(nullptr);
0433     hRatio->Reset("ICES");
0434     hRatio->Divide(hNum, hDen, 1.0, 1.0, "B");
0435 
0436     hDen->SetLineColor(kBlack);
0437     hDen->SetMarkerColor(kBlack);
0438     hDen->SetMarkerStyle(kFullCircle);
0439     hDen->SetMarkerSize(0.8);
0440     hDen->SetLineWidth(2);
0441 
0442     hNum->SetLineColor(kRed + 1);
0443     hNum->SetMarkerColor(kRed + 1);
0444     hNum->SetMarkerStyle(kOpenCircle);
0445     hNum->SetMarkerSize(0.8);
0446     hNum->SetLineWidth(2);
0447 
0448     hRatio->SetLineColor(kRed + 1);
0449     hRatio->SetMarkerColor(kRed + 1);
0450     hRatio->SetMarkerStyle(kOpenCircle);
0451     hRatio->SetMarkerSize(0.8);
0452     hRatio->SetLineWidth(2);
0453 
0454     double ymax = std::max(hDen->GetMaximum(), hNum->GetMaximum());
0455     if (ymax <= 0.0)
0456         ymax = 1.0;
0457 
0458     double ymin = std::min(hDen->GetMinimum(0), hNum->GetMinimum(0));
0459 
0460     TCanvas *c = new TCanvas((filename + "_canvas").c_str(), (filename + "_canvas").c_str(), 900, 800);
0461     c->cd();
0462     TPad *pad_top = new TPad("pad_top", "pad_top", 0.0, 0.32, 1.0, 1.0);
0463     TPad *pad_bottom = new TPad("pad_bottom", "pad_bottom", 0.0, 0.0, 1.0, 0.32);
0464     pad_top->SetLeftMargin(0.14);
0465     pad_top->SetRightMargin(0.05);
0466     pad_top->SetTopMargin(0.05);
0467     pad_top->SetBottomMargin(0.025);
0468     pad_bottom->SetLeftMargin(0.14);
0469     pad_bottom->SetRightMargin(0.05);
0470     pad_bottom->SetTopMargin(0.025);
0471     pad_bottom->SetBottomMargin(0.35);
0472     pad_top->Draw();
0473     pad_bottom->Draw();
0474 
0475     float toptitlesize = 0.05;
0476     pad_top->cd();
0477     pad_top->SetLogy(logy);
0478     hDen->SetTitle("");
0479     hDen->GetYaxis()->SetTitle(ytitle.c_str());
0480     hDen->GetYaxis()->SetTitleOffset(1.35);
0481     hDen->GetYaxis()->SetTitleSize(toptitlesize);
0482     hDen->GetYaxis()->SetLabelSize(toptitlesize);
0483     hDen->GetXaxis()->SetLabelSize(0.0);
0484     hDen->GetXaxis()->SetTitleSize(0.0);
0485     // hDen->SetMinimum((logy ? ymin * 0.1 : 0.0));
0486     hDen->SetMaximum((logy ? 100.0 : 1.5) * ymax);
0487     hDen->Draw("e1");
0488     hNum->Draw("e1 same");
0489 
0490     TLegend *leg = new TLegend(gPad->GetLeftMargin() + 0.03,   //
0491                                1 - gPad->GetTopMargin() - 0.2, //
0492                                gPad->GetLeftMargin() + 0.15,   //
0493                                1 - gPad->GetTopMargin() - 0.05 //
0494     );
0495     leg->SetHeader(addinfo[0].c_str(), "L"); // assuming addinfo has only 1 entry
0496     leg->SetBorderSize(0);
0497     leg->SetFillStyle(0);
0498     leg->SetTextSize(0.04);
0499     leg->AddEntry(hDen, hDenInput.second.c_str(), "pel");
0500     leg->AddEntry(hNum, hNumInput.second.c_str(), "pel");
0501     leg->Draw();
0502 
0503     // TLatex latex;
0504     // latex.SetNDC();
0505     // latex.SetTextFont(42);
0506     // latex.SetTextSize(0.035);
0507     // latex.SetTextAlign(kHAlignRight + kVAlignBottom);
0508     // for (size_t i = 0; i < addinfo.size(); ++i)
0509     // {
0510     //     latex.DrawLatex(1 - pad_top->GetRightMargin(), (1 - pad_top->GetTopMargin() + 0.01) - 0.045 * i, addinfo[i].c_str());
0511     // }
0512 
0513     float textsize_scalefactor = 2.1;
0514     pad_bottom->cd();
0515     hRatio->SetTitle("");
0516     hRatio->GetXaxis()->SetTitle(xtitle.c_str());
0517     hRatio->GetYaxis()->SetTitle(ratio_ytitle.c_str());
0518     hRatio->GetXaxis()->SetTitleSize(toptitlesize * textsize_scalefactor);
0519     hRatio->GetXaxis()->SetLabelSize(toptitlesize * textsize_scalefactor);
0520     // hRatio->GetXaxis()->SetTitleOffset(1.1);
0521     hRatio->GetYaxis()->SetTitleSize(toptitlesize * textsize_scalefactor);
0522     hRatio->GetYaxis()->SetLabelSize(toptitlesize * textsize_scalefactor);
0523     hRatio->GetYaxis()->SetTitleOffset(0.6);
0524     hRatio->GetYaxis()->SetNdivisions(505);
0525     // hRatio->SetMinimum(0);
0526     hRatio->SetMaximum(1);
0527     hRatio->Draw("e1");
0528 
0529     TLine *line = new TLine(hRatio->GetXaxis()->GetXmin(), 1.0, hRatio->GetXaxis()->GetXmax(), 1.0);
0530     line->SetLineColor(kBlack);
0531     line->SetLineStyle(2);
0532     line->SetLineWidth(2);
0533     line->Draw("same");
0534 
0535     c->SaveAs((filename + ".png").c_str());
0536     c->SaveAs((filename + ".pdf").c_str());
0537 
0538     delete line;
0539     delete leg;
0540     delete hRatio;
0541     delete hNum;
0542     delete hDen;
0543     delete c;
0544 }
0545 
0546 // ===========================================================================
0547 static void draw_comparison(std::vector<TH1 *> histograms,               //
0548                             const std::vector<std::string> &colors,      //
0549                             std::vector<std::string> labels,             //
0550                             const std::string &xAxisTitle,               //
0551                             const std::string &yAxisTitle,               //
0552                             const std::pair<float, float> &yRange,       //
0553                             const bool logy,                             //
0554                             const bool normalize,                        //
0555                             const int xAxisNdivisions,                   //
0556                             const std::pair<bool, float> &referenceline, //
0557                             const std::string &outputFileName            //
0558 )
0559 {
0560     if (histograms.empty())
0561         return;
0562 
0563     if (normalize)
0564     {
0565         for (auto *hist : histograms)
0566         {
0567             if (hist && hist->Integral(-1, -1) > 0.0)
0568                 hist->Scale(1. / hist->Integral(-1, -1));
0569         }
0570     }
0571 
0572     float yMin = yRange.first, yMax = yRange.second;
0573     if (yRange.first < 0)
0574     {
0575         float histMin = 1E6;
0576         for (auto *hist : histograms)
0577         {
0578             if (hist && hist->GetMinimum(0) < histMin)
0579                 histMin = hist->GetMinimum(0);
0580         }
0581         yMin = logy ? histMin * 0.5f : 0.f;
0582     }
0583 
0584     if (yRange.second < 0)
0585     {
0586         float histMax = -1E6;
0587         for (auto *hist : histograms)
0588         {
0589             if (hist && hist->GetMaximum() > histMax)
0590                 histMax = hist->GetMaximum();
0591         }
0592         yMax = logy ? histMax * 250.f : histMax * 1.5f;
0593     }
0594 
0595     TCanvas *c = new TCanvas("c_comparison", "Comparison", 800, 700);
0596     c->SetLogy(logy);
0597     c->cd();
0598     gPad->SetLeftMargin(0.16);
0599     gPad->SetRightMargin(0.05);
0600     gPad->SetTopMargin(0.07);
0601     for (size_t i = 0; i < histograms.size(); ++i)
0602     {
0603         if (!histograms[i])
0604             continue;
0605         histograms[i]->SetLineColor(TColor::GetColor(colors[i].c_str()));
0606         histograms[i]->SetMarkerColor(TColor::GetColor(colors[i].c_str()));
0607         histograms[i]->SetMarkerStyle(20 + static_cast<int>(i));
0608         histograms[i]->SetMarkerSize(1.0);
0609         histograms[i]->SetLineWidth(2);
0610         if (i == 0)
0611         {
0612             histograms[i]->SetTitle("");
0613             histograms[i]->GetXaxis()->SetTitle(xAxisTitle.c_str());
0614             histograms[i]->GetXaxis()->SetTitleOffset(1.4);
0615             histograms[i]->GetXaxis()->SetNdivisions(xAxisNdivisions);
0616             histograms[i]->GetYaxis()->SetTitle(yAxisTitle.c_str());
0617             histograms[i]->GetYaxis()->SetRangeUser(yMin, yMax);
0618             histograms[i]->GetYaxis()->SetTitleOffset(1.5);
0619             histograms[i]->Draw("HIST E1");
0620         }
0621         else
0622         {
0623             histograms[i]->Draw("HIST E1 SAME");
0624         }
0625     }
0626 
0627     // Draw reference line if requested
0628     if (referenceline.first)
0629     {
0630         TLine *line = new TLine(histograms[0]->GetXaxis()->GetXmin(), referenceline.second, histograms[0]->GetXaxis()->GetXmax(), referenceline.second);
0631         line->SetLineColor(kGray + 1);
0632         line->SetLineStyle(2);
0633         line->SetLineWidth(2);
0634         line->Draw("same");
0635     }
0636 
0637     TLegend *legend = new TLegend(gPad->GetLeftMargin() + 0.04,                                        //
0638                                   (1 - gPad->GetTopMargin()) - 0.035 - 0.04 * (histograms.size() + 1), //
0639                                   gPad->GetLeftMargin() + 0.14,                                        //
0640                                   (1 - gPad->GetTopMargin()) - 0.035                                   //
0641     );
0642     legend->SetTextAlign(kHAlignLeft + kVAlignCenter);
0643     legend->SetTextSize(0.035);
0644     legend->SetFillStyle(0);
0645     legend->SetBorderSize(0);
0646     for (size_t i = 0; i < labels.size() && i < histograms.size(); ++i)
0647     {
0648         if (histograms[i])
0649             legend->AddEntry(histograms[i], labels[i].c_str(), "pel");
0650     }
0651     legend->Draw();
0652 
0653     c->SaveAs(Form("%s.png", outputFileName.c_str()));
0654     c->SaveAs(Form("%s.pdf", outputFileName.c_str()));
0655     delete legend;
0656     delete c;
0657 }
0658 
0659 // ===========================================================================
0660 // Alternative version of draw_comparison for the INTT doublet DCA
0661 static void draw_comparison_alt(std::vector<TH1 *> histograms,               //
0662                                 const std::vector<std::string> &colors,      //
0663                                 const std::vector<int> &markers,             //
0664                                 const std::vector<float> &markerSizes,       //
0665                                 std::vector<std::string> labels,             //
0666                                 const std::string &xAxisTitle,               //
0667                                 const std::string &yAxisTitle,               //
0668                                 const std::pair<float, float> &yRange,       //
0669                                 const bool logy,                             //
0670                                 const bool normalize,                        //
0671                                 const int xAxisNdivisions,                   //
0672                                 const std::pair<bool, float> &referenceline, //
0673                                 const std::string &outputFileName            //
0674 )
0675 {
0676     // make a dummy histogram for axis setting
0677     TH1 *hDummy = new TH1D("hDummy", "", 1, histograms[0]->GetXaxis()->GetXmin(), histograms[0]->GetXaxis()->GetXmax());
0678 
0679     if (histograms.empty())
0680         return;
0681 
0682     if (normalize)
0683     {
0684         for (auto *hist : histograms)
0685         {
0686             if (hist && hist->Integral(-1, -1) > 0.0)
0687                 hist->Scale(1. / hist->Integral(-1, -1));
0688         }
0689     }
0690 
0691     float yMin = yRange.first, yMax = yRange.second;
0692     if (yRange.first < 0)
0693     {
0694         float histMin = std::numeric_limits<float>::max();
0695         for (auto *hist : histograms)
0696         {
0697             std::cout << "hist min: " << (hist ? hist->GetMinimum(0) : -1) << std::endl;
0698             if (hist && hist->GetMinimum(0) < histMin)
0699                 histMin = hist->GetMinimum(0);
0700         }
0701         yMin = logy ? histMin * 0.5f : 0.f;
0702     }
0703 
0704     if (yRange.second < 0)
0705     {
0706         float histMax = -std::numeric_limits<float>::max();
0707         for (auto *hist : histograms)
0708         {
0709             if (hist && hist->GetMaximum() > histMax)
0710                 histMax = hist->GetMaximum();
0711         }
0712         yMax = logy ? histMax * 2.f : histMax * 1.5f;
0713     }
0714 
0715     std::cout << "yMin: " << yMin << ", yMax: " << yMax << std::endl;
0716 
0717     TCanvas *c = new TCanvas("c_comparison", "Comparison", 800, 700);
0718     c->SetLogy(logy);
0719     c->cd();
0720     gPad->SetLeftMargin(0.16);
0721     gPad->SetRightMargin(0.05);
0722     gPad->SetTopMargin(0.07);
0723     // draw dummy first to set axis
0724     hDummy->SetTitle("");
0725     hDummy->GetXaxis()->SetTitle(xAxisTitle.c_str());
0726     hDummy->GetXaxis()->SetTitleOffset(1.4);
0727     hDummy->GetXaxis()->SetNdivisions(xAxisNdivisions);
0728     hDummy->GetYaxis()->SetTitle(yAxisTitle.c_str());
0729     hDummy->GetYaxis()->SetRangeUser(yMin, yMax);
0730     hDummy->GetYaxis()->SetTitleOffset(1.5);
0731     hDummy->Draw("AXIS");
0732 
0733     for (size_t i = 0; i < histograms.size(); ++i)
0734     {
0735         if (!histograms[i])
0736             continue;
0737         histograms[i]->SetLineColor(TColor::GetColor(colors[i].c_str()));
0738         histograms[i]->SetMarkerColor(TColor::GetColor(colors[i].c_str()));
0739         histograms[i]->SetMarkerStyle(markers[i]);
0740         histograms[i]->SetMarkerSize(markerSizes[i]);
0741         histograms[i]->SetLineWidth(2);
0742         histograms[i]->Draw("E1 SAME");
0743     }
0744 
0745     // Draw reference line if requested
0746     if (referenceline.first)
0747     {
0748         TLine *line = new TLine(histograms[0]->GetXaxis()->GetXmin(), referenceline.second, histograms[0]->GetXaxis()->GetXmax(), referenceline.second);
0749         line->SetLineColor(kGray + 1);
0750         line->SetLineStyle(2);
0751         line->SetLineWidth(2);
0752         line->Draw("same");
0753     }
0754 
0755     TLegend *legend = new TLegend(gPad->GetLeftMargin() + 0.25,                                        //
0756                                   (1 - gPad->GetTopMargin()) - 0.035 - 0.04 * (histograms.size() + 1), //
0757                                   gPad->GetLeftMargin() + 0.35,                                        //
0758                                   (1 - gPad->GetTopMargin()) - 0.035                                   //
0759     );
0760     legend->SetTextAlign(kHAlignLeft + kVAlignCenter);
0761     legend->SetTextSize(0.035);
0762     legend->SetFillStyle(0);
0763     legend->SetBorderSize(0);
0764     for (size_t i = 0; i < labels.size() && i < histograms.size(); ++i)
0765     {
0766         if (histograms[i])
0767             legend->AddEntry(histograms[i], labels[i].c_str(), "pel");
0768     }
0769     legend->Draw();
0770 
0771     c->SaveAs(Form("%s.png", outputFileName.c_str()));
0772     c->SaveAs(Form("%s.pdf", outputFileName.c_str()));
0773     delete legend;
0774     delete c;
0775 }
0776 
0777 struct ProjectionRatioOutputs
0778 {
0779     std::string prefix;
0780     std::string label;
0781     TH1D *eta_ratio = nullptr;
0782     TH1D *phi_ratio = nullptr;
0783     TH1D *eta_ratio_good = nullptr;
0784     TH1D *phi_ratio_good = nullptr;
0785 };
0786 
0787 static bool StartsWith(const std::string &value, const std::string &prefix) { return value.rfind(prefix, 0) == 0; }
0788 
0789 static bool EndsWith(const std::string &value, const std::string &suffix) { return value.size() >= suffix.size() && value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0; }
0790 
0791 static int ExtractMultiplicityBinNumber(const std::string &base_prefix, const std::string &object_name)
0792 {
0793     const std::string probe_prefix = base_prefix + "_multPercentileBin";
0794     const std::string suffix = "_Sub";
0795     if (!StartsWith(object_name, probe_prefix) || !EndsWith(object_name, suffix))
0796         return -1;
0797 
0798     const std::string bin_text = object_name.substr(probe_prefix.size(), object_name.size() - probe_prefix.size() - suffix.size());
0799     if (bin_text.empty())
0800         return -1;
0801     for (char ch : bin_text)
0802     {
0803         if (!std::isdigit(static_cast<unsigned char>(ch)))
0804             return -1;
0805     }
0806     return std::stoi(bin_text);
0807 }
0808 
0809 static std::vector<std::string> CollectAvailablePrefixes(TFile *fin, const std::string &base_prefix)
0810 {
0811     std::vector<std::pair<int, std::string>> multiplicity_prefixes;
0812     if (!fin)
0813         return {base_prefix};
0814 
0815     TIter next(fin->GetListOfKeys());
0816     while (auto *key = dynamic_cast<TKey *>(next()))
0817     {
0818         const std::string name = key->GetName();
0819         const int bin_number = ExtractMultiplicityBinNumber(base_prefix, name);
0820         if (bin_number >= 0)
0821             multiplicity_prefixes.push_back({bin_number, base_prefix + "_multPercentileBin" + std::to_string(bin_number)});
0822     }
0823 
0824     std::sort(multiplicity_prefixes.begin(), multiplicity_prefixes.end(), [](const auto &lhs, const auto &rhs) { return lhs.first < rhs.first; });
0825     multiplicity_prefixes.erase(std::unique(multiplicity_prefixes.begin(), multiplicity_prefixes.end(), [](const auto &lhs, const auto &rhs) { return lhs.first == rhs.first; }), multiplicity_prefixes.end());
0826 
0827     std::vector<std::string> prefixes = {base_prefix};
0828     for (const auto &entry : multiplicity_prefixes)
0829         prefixes.push_back(entry.second);
0830     return prefixes;
0831 }
0832 
0833 static std::string PrefixSuffixForOutput(const std::string &base_prefix, const std::string &current_prefix)
0834 {
0835     if (current_prefix == base_prefix)
0836         return "";
0837     if (StartsWith(current_prefix, base_prefix))
0838         return current_prefix.substr(base_prefix.size());
0839     return "_" + current_prefix;
0840 }
0841 
0842 static std::string PrefixDisplayLabel(const std::string &base_prefix, const std::string &current_prefix)
0843 {
0844     if (current_prefix == base_prefix)
0845         return "Inclusive";
0846     if (StartsWith(current_prefix, base_prefix + "_"))
0847         return current_prefix.substr(base_prefix.size() + 1);
0848     return current_prefix;
0849 }
0850 
0851 static int ExtractMultiplicityBinFromPrefix(const std::string &base_prefix, const std::string &current_prefix)
0852 {
0853     const std::string probe_prefix = base_prefix + "_multPercentileBin";
0854     if (!StartsWith(current_prefix, probe_prefix))
0855         return -1;
0856 
0857     const std::string bin_text = current_prefix.substr(probe_prefix.size());
0858     if (bin_text.empty())
0859         return -1;
0860     for (char ch : bin_text)
0861     {
0862         if (!std::isdigit(static_cast<unsigned char>(ch)))
0863             return -1;
0864     }
0865     return std::stoi(bin_text);
0866 }
0867 
0868 static std::string FormatPercentileValue(double value)
0869 {
0870     std::ostringstream out;
0871     if (std::fabs(value - std::round(value)) < 1e-6)
0872         out << static_cast<int>(std::round(value));
0873     else
0874         out << std::fixed << std::setprecision(1) << value;
0875     return out.str();
0876 }
0877 
0878 static std::map<int, std::pair<double, double>> LoadPercentileBoundariesByBin(const bool is_simulation)
0879 {
0880     std::map<int, std::pair<double, double>> boundaries;
0881     const std::string multiplicity_boundary_prefix = is_simulation ? "MC_" : "Data_";
0882     const std::string input_file = "figure/figure-NInttClusterCrossing/" + multiplicity_boundary_prefix + "NInttClusterPercentileBoundaries.root";
0883 
0884     TFile fin(input_file.c_str(), "READ");
0885     if (fin.IsZombie())
0886         return boundaries;
0887 
0888     auto *tree = dynamic_cast<TTree *>(fin.Get("trigger_percentile_intervals"));
0889     if (!tree)
0890         return boundaries;
0891 
0892     int percentile_bin = -1;
0893     double percentile_low = 0.0;
0894     double percentile_high = 0.0;
0895     tree->SetBranchAddress("percentile_bin", &percentile_bin);
0896     tree->SetBranchAddress("percentile_low", &percentile_low);
0897     tree->SetBranchAddress("percentile_high", &percentile_high);
0898 
0899     const Long64_t nentries = tree->GetEntries();
0900     for (Long64_t i = 0; i < nentries; ++i)
0901     {
0902         tree->GetEntry(i);
0903         boundaries[percentile_bin] = {percentile_low, percentile_high};
0904     }
0905 
0906     return boundaries;
0907 }
0908 
0909 static std::string PrefixDisplayLabel(const std::string &base_prefix, const std::string &current_prefix, const std::map<int, std::pair<double, double>> &percentile_boundaries)
0910 {
0911     if (current_prefix == base_prefix)
0912         return "Inclusive";
0913 
0914     const int bin_number = ExtractMultiplicityBinFromPrefix(base_prefix, current_prefix);
0915     if (bin_number >= 0)
0916     {
0917         auto it = percentile_boundaries.find(bin_number);
0918         if (it != percentile_boundaries.end())
0919         {
0920             return "N_{INTT clusters} " + FormatPercentileValue(it->second.first) + "-" + FormatPercentileValue(it->second.second) + "%";
0921         }
0922     }
0923 
0924     if (StartsWith(current_prefix, base_prefix + "_"))
0925         return current_prefix.substr(base_prefix.size() + 1);
0926     return current_prefix;
0927 }
0928 
0929 static double DecodeTruthVtxZHistToken(const std::string &token, bool &is_infinite, bool &ok)
0930 {
0931     is_infinite = false;
0932     ok = false;
0933     if (token == "mInf")
0934     {
0935         is_infinite = true;
0936         ok = true;
0937         return -std::numeric_limits<double>::infinity();
0938     }
0939     if (token == "pInf")
0940     {
0941         is_infinite = true;
0942         ok = true;
0943         return std::numeric_limits<double>::infinity();
0944     }
0945 
0946     std::string value = token;
0947     double sign = 1.0;
0948     if (!value.empty() && value[0] == 'm')
0949     {
0950         sign = -1.0;
0951         value.erase(0, 1);
0952     }
0953     else if (!value.empty() && value[0] == 'p')
0954     {
0955         value.erase(0, 1);
0956     }
0957 
0958     std::replace(value.begin(), value.end(), 'p', '.');
0959     char *endptr = nullptr;
0960     const double parsed = std::strtod(value.c_str(), &endptr);
0961     if (endptr && *endptr == 0)
0962     {
0963         ok = true;
0964         return sign * parsed;
0965     }
0966     return 0.0;
0967 }
0968 
0969 struct TruthVtxZDCAHistBin
0970 {
0971     int bin_index = -1;
0972     double xlow = 0.0;
0973     double xhigh = 0.0;
0974     TH1D *hist = nullptr;
0975 };
0976 
0977 static bool ParseTruthVtxZDCAHistName(const std::string &hist_name, const std::string &prefix, TruthVtxZDCAHistBin &info)
0978 {
0979     const std::string probe_prefix = prefix + "_SilSeedDCA3D_truthVtxZ_bin";
0980     if (!StartsWith(hist_name, probe_prefix))
0981         return false;
0982 
0983     const std::string remainder = hist_name.substr(probe_prefix.size());
0984     if (remainder.size() < 4 || !std::isdigit(static_cast<unsigned char>(remainder[0])) || !std::isdigit(static_cast<unsigned char>(remainder[1])) || remainder[2] != '_')
0985         return false;
0986 
0987     const int bin_index = std::stoi(remainder.substr(0, 2));
0988     const std::string encoded_bounds = remainder.substr(3);
0989     const size_t sep = encoded_bounds.find('_');
0990     if (sep == std::string::npos)
0991         return false;
0992 
0993     bool low_inf = false;
0994     bool high_inf = false;
0995     bool low_ok = false;
0996     bool high_ok = false;
0997     const double low_raw = DecodeTruthVtxZHistToken(encoded_bounds.substr(0, sep), low_inf, low_ok);
0998     const double high_raw = DecodeTruthVtxZHistToken(encoded_bounds.substr(sep + 1), high_inf, high_ok);
0999     if (!low_ok || !high_ok)
1000         return false;
1001 
1002     const double low = low_inf ? (high_raw - 10.0) : low_raw;
1003     const double high = high_inf ? (low_raw + 10.0) : high_raw;
1004     if (!(high > low))
1005         return false;
1006 
1007     info.bin_index = bin_index;
1008     info.xlow = low;
1009     info.xhigh = high;
1010     return true;
1011 }
1012 
1013 static std::vector<TruthVtxZDCAHistBin> CollectTruthVtxZDCAHistBins(TFile *fin, const std::string &prefix)
1014 {
1015     std::vector<TruthVtxZDCAHistBin> bins;
1016     if (!fin)
1017         return bins;
1018 
1019     TIter next(fin->GetListOfKeys());
1020     while (auto *key = dynamic_cast<TKey *>(next()))
1021     {
1022         const std::string hist_name = key->GetName();
1023         TruthVtxZDCAHistBin info;
1024         if (!ParseTruthVtxZDCAHistName(hist_name, prefix, info))
1025             continue;
1026 
1027         info.hist = dynamic_cast<TH1D *>(fin->Get(hist_name.c_str()));
1028         if (!info.hist)
1029             continue;
1030         bins.push_back(info);
1031     }
1032 
1033     std::sort(bins.begin(), bins.end(), [](const TruthVtxZDCAHistBin &lhs, const TruthVtxZDCAHistBin &rhs) { return lhs.bin_index < rhs.bin_index; });
1034     return bins;
1035 }
1036 
1037 static TH2D *BuildTruthVtxZDCAHeatmap(TFile *fin, const std::string &prefix)
1038 {
1039     std::vector<TruthVtxZDCAHistBin> bins = CollectTruthVtxZDCAHistBins(fin, prefix);
1040     if (bins.empty())
1041         return nullptr;
1042 
1043     TH1D *href = bins.front().hist;
1044     if (!href)
1045         return nullptr;
1046 
1047     std::vector<double> xedges;
1048     xedges.reserve(bins.size() + 1);
1049     xedges.push_back(bins.front().xlow);
1050     for (const auto &entry : bins)
1051         xedges.push_back(entry.xhigh);
1052 
1053     std::vector<double> yedges;
1054     yedges.reserve(href->GetNbinsX() + 1);
1055     for (int ibin = 1; ibin <= href->GetNbinsX(); ++ibin)
1056         yedges.push_back(href->GetXaxis()->GetBinLowEdge(ibin));
1057     yedges.push_back(href->GetXaxis()->GetBinUpEdge(href->GetNbinsX()));
1058 
1059     TH2D *h2 = new TH2D((prefix + "_SilSeedDCA3D_truthVtxZ_2D").c_str(), (prefix + "_SilSeedDCA3D_truthVtxZ_2D").c_str(), static_cast<int>(bins.size()), xedges.data(), href->GetNbinsX(), yedges.data());
1060     h2->SetDirectory(nullptr);
1061     h2->Sumw2();
1062 
1063     for (size_t ix = 0; ix < bins.size(); ++ix)
1064     {
1065         TH1D *h1 = bins[ix].hist;
1066         if (!h1)
1067             continue;
1068 
1069         const double norm = h1->Integral(1, h1->GetNbinsX());
1070         for (int iy = 1; iy <= h1->GetNbinsX(); ++iy)
1071         {
1072             const double value = (norm > 0.0) ? (h1->GetBinContent(iy) / norm) : 0.0;
1073             const double error = (norm > 0.0) ? (h1->GetBinError(iy) / norm) : 0.0;
1074             h2->SetBinContent(static_cast<int>(ix) + 1, iy, value);
1075             h2->SetBinError(static_cast<int>(ix) + 1, iy, error);
1076         }
1077     }
1078 
1079     return h2;
1080 }
1081 
1082 static std::vector<std::pair<TH1D *, std::string>> BuildMergedAbsTruthVtxZDCAHists(TFile *fin, const std::string &prefix)
1083 {
1084     std::vector<std::pair<TH1D *, std::string>> merged_hists;
1085     const std::vector<TruthVtxZDCAHistBin> bins = CollectTruthVtxZDCAHistBins(fin, prefix);
1086     if (bins.empty())
1087         return merged_hists;
1088 
1089     auto format_abs_bound = [](double value) -> std::string
1090     {
1091         std::ostringstream out;
1092         out << std::fixed << std::setprecision((std::fabs(value - std::round(value)) < 1e-6) ? 0 : 1) << value;
1093         return out.str();
1094     };
1095 
1096     const size_t npairs = (bins.size() + 1) / 2;
1097     merged_hists.reserve(npairs);
1098     for (size_t i = 0; i < npairs; ++i)
1099     {
1100         const size_t j = bins.size() - 1 - i;
1101         TH1D *href = bins[i].hist;
1102         if (!href)
1103             continue;
1104 
1105         TH1D *hmerge = dynamic_cast<TH1D *>(href->Clone((prefix + "_SilSeedDCA3D_absTruthVtxZ_bin" + std::to_string(i)).c_str()));
1106         if (!hmerge)
1107             continue;
1108         hmerge->SetDirectory(nullptr);
1109         if (j != i && bins[j].hist)
1110             hmerge->Add(bins[j].hist);
1111 
1112         const double abs_low = (j != i) ? std::min(std::fabs(bins[i].xhigh), std::fabs(bins[j].xlow)) : std::min(std::fabs(bins[i].xlow), std::fabs(bins[i].xhigh));
1113         const double abs_high = (j != i) ? std::max(std::fabs(bins[i].xlow), std::fabs(bins[j].xhigh)) : std::max(std::fabs(bins[i].xlow), std::fabs(bins[i].xhigh));
1114         merged_hists.push_back({hmerge, "|z_{truth}|=" + format_abs_bound(abs_low) + "-" + format_abs_bound(abs_high) + " cm"});
1115     }
1116 
1117     return merged_hists;
1118 }
1119 
1120 static ProjectionRatioOutputs plot_tklCombinatoric_single(const std::string &inputfile = "test_OO_combinatoric.root",     //
1121                                                           const std::string &prefix = "tkl_Combinatoric",                 //
1122                                                           const std::string &mode = "absdPhi",                            //
1123                                                           bool is_simulation = false,                                     //
1124                                                           double zvtx_cut_min = -10.0,                                    //
1125                                                           double zvtx_cut_max = 10.0,                                     //
1126                                                           double raw_count_xmin = 0.0,                                    //
1127                                                           double raw_count_xmax = kDirectRawCountCut,                     //
1128                                                           double plot_xmin = 0.0,                                         //
1129                                                           double plot_xmax = 0.2,                                         //
1130                                                           const std::string &outstem = "plot_tklCombinatoric",            //
1131                                                           const std::string &plotdir = "./figure/figure-tklCombinatoric", //
1132                                                           bool zero_x_error = true,                                       //
1133                                                           const std::string &ratio_draw_option = "colz text",             //
1134                                                           const std::string &ratio_text_format = ".3f",                   //
1135                                                           bool use_rounded_zmax = true,                                   //
1136                                                           const std::string &display_label = "")
1137 {
1138     ProjectionRatioOutputs ratio_outputs;
1139     ratio_outputs.prefix = prefix;
1140     ratio_outputs.label = display_label.empty() ? prefix : display_label;
1141 
1142     gStyle->SetOptStat(0);
1143     TGaxis::SetMaxDigits(3);
1144     if (zero_x_error)
1145         gStyle->SetErrorX(0.f);
1146 
1147     gSystem->mkdir(plotdir.c_str(), true);
1148     const std::string single_canvas_dir = plotdir + "/single-canvas";
1149     gSystem->mkdir(single_canvas_dir.c_str(), true);
1150 
1151     auto *fin = TFile::Open(inputfile.c_str(), "READ");
1152     if (!fin || fin->IsZombie())
1153     {
1154         std::cerr << "[plot_tklCombinatoric] Cannot open: " << inputfile << "\n";
1155         return ratio_outputs;
1156     }
1157 
1158     auto *hGrid = GetPrimaryGrid(fin, prefix);
1159     if (!hGrid)
1160     {
1161         std::cerr << "[plot_tklCombinatoric] Cannot load " << prefix << "_Sig/_Bkg/_Sub for grid axes.\n";
1162         fin->Close();
1163         return ratio_outputs;
1164     }
1165     const int nEta = hGrid->GetXaxis()->GetNbins();
1166     const int nPhi = hGrid->GetYaxis()->GetNbins();
1167 
1168     auto *hSub = dynamic_cast<TH2D *>(fin->Get((prefix + "_Sub").c_str()));
1169     if (!hSub)
1170     {
1171         std::cerr << "[plot_tklCombinatoric] Missing " << prefix << "_Sub.\n";
1172         fin->Close();
1173         return ratio_outputs;
1174     }
1175 
1176     auto *hSilSeed = dynamic_cast<TH2D *>(fin->Get((prefix + "_SilSeedEtaPhi").c_str()));
1177     if (!hSilSeed)
1178     {
1179         std::cerr << "[plot_tklCombinatoric] Missing " << prefix << "_SilSeedEtaPhi.\n";
1180         fin->Close();
1181         return ratio_outputs;
1182     }
1183     auto *hSilSeed_nMVTX3nINTT2 = dynamic_cast<TH2D *>(fin->Get((prefix + "_SilSeedEtaPhi_nMVTX3nINTT2").c_str()));
1184     auto *hSilSeed_nMVTX3nINTT1 = dynamic_cast<TH2D *>(fin->Get((prefix + "_SilSeedEtaPhi_nMVTX3nINTT1").c_str()));
1185     auto *hSilSeed_nMVTX2nINTT2 = dynamic_cast<TH2D *>(fin->Get((prefix + "_SilSeedEtaPhi_nMVTX2nINTT2").c_str()));
1186     auto *hSilSeed_nMVTX2nINTT1 = dynamic_cast<TH2D *>(fin->Get((prefix + "_SilSeedEtaPhi_nMVTX2nINTT1").c_str()));
1187     auto *hSilSeed_nMVTXnINTTOther = dynamic_cast<TH2D *>(fin->Get((prefix + "_SilSeedEtaPhi_nMVTXnINTTOther").c_str()));
1188     auto *hINTTClusterVsAssociatedSilSeed = dynamic_cast<TH2D *>(fin->Get((prefix + "_INTTClusterVsAssociatedSilSeed").c_str()));
1189     auto *hINTTClusterVsAllSilSeed = dynamic_cast<TH2D *>(fin->Get((prefix + "_INTTClusterVsAllSilSeed").c_str()));
1190     auto *hSeedCrossing_selectedCrossings = dynamic_cast<TH1D *>(fin->Get((prefix + "_SeedCrossing_selectedCrossings").c_str()));
1191     auto *hSeedCrossing_selectedCrossings_largeDiffNClusterSeeds = dynamic_cast<TH1D *>(fin->Get((prefix + "_SeedCrossing_selectedCrossings_largeDiffNClusterSeeds").c_str()));
1192     auto *hDCA3D_Sig = dynamic_cast<TH1D *>(fin->Get((prefix + "_DCA3D_Sig").c_str()));
1193     auto *hDCA3D_Bkg = dynamic_cast<TH1D *>(fin->Get((prefix + "_DCA3D_Bkg").c_str()));
1194     auto *hSilSeedDCA3D = dynamic_cast<TH1D *>(fin->Get((prefix + "_SilSeedDCA3D").c_str()));
1195     TH2D *hSilSeedDCA3D_truthVtxZ_2D = is_simulation ? BuildTruthVtxZDCAHeatmap(fin, prefix) : nullptr;
1196     std::vector<std::pair<TH1D *, std::string>> hSilSeedDCA3D_truthVtxZ_absMerged = is_simulation ? BuildMergedAbsTruthVtxZDCAHists(fin, prefix) : std::vector<std::pair<TH1D *, std::string>>{};
1197 
1198     if (!hSilSeed_nMVTX3nINTT2 || !hSilSeed_nMVTX3nINTT1 || !hSilSeed_nMVTX2nINTT2 || !hSilSeed_nMVTX2nINTT1 || !hSilSeed_nMVTXnINTTOther)
1199     {
1200         std::cerr << "[plot_tklCombinatoric] Missing one or more silicon-seed category histograms." << std::endl;
1201         fin->Close();
1202         return ratio_outputs;
1203     }
1204 
1205     // Sum the high-quality seed categories: (3+2) + (3+1) + (2+2).
1206     auto *hSilSeed_good = dynamic_cast<TH2D *>(hSilSeed_nMVTX3nINTT2->Clone((prefix + "_SilSeedEtaPhi_good").c_str()));
1207     if (!hSilSeed_good)
1208     {
1209         std::cerr << "[plot_tklCombinatoric] Failed to build " << prefix << "_SilSeedEtaPhi_good." << std::endl;
1210         fin->Close();
1211         return ratio_outputs;
1212     }
1213     hSilSeed_good->SetDirectory(nullptr);
1214     hSilSeed_good->Add(hSilSeed_nMVTX3nINTT1);
1215     hSilSeed_good->Add(hSilSeed_nMVTX2nINTT2);
1216 
1217     // this is cluster on the first layer, not "silicon seeds"
1218     auto *hSeed = dynamic_cast<TH2D *>(fin->Get((prefix + "_SeedEtaPhi").c_str()));
1219     if (!hSeed)
1220     {
1221         std::cerr << "[plot_tklCombinatoric] Missing " << prefix << "_SeedEtaPhi.\n";
1222         fin->Close();
1223         return ratio_outputs;
1224     }
1225 
1226     TH2D *hPrimaryCharged = nullptr;
1227     if (is_simulation)
1228     {
1229         hPrimaryCharged = dynamic_cast<TH2D *>(fin->Get((prefix + "_PrimaryChargedEtaPhi").c_str()));
1230         if (!hPrimaryCharged)
1231         {
1232             std::cerr << "[plot_tklCombinatoric] Missing " << prefix << "_PrimaryChargedEtaPhi for simulation mode.\n";
1233             fin->Close();
1234             return ratio_outputs;
1235         }
1236     }
1237 
1238     bool use_direct_raw_count_grid = false;
1239     TH2D *h2_nraw = nullptr;
1240     if (auto *hRawDirect = GetRawCountGrid(fin, prefix))
1241     {
1242         h2_nraw = dynamic_cast<TH2D *>(hRawDirect->Clone((prefix + "_NRawEtaPhi").c_str()));
1243         if (h2_nraw)
1244         {
1245             h2_nraw->SetDirectory(nullptr);
1246             use_direct_raw_count_grid = true;
1247         }
1248     }
1249     if (!h2_nraw)
1250     {
1251         h2_nraw = dynamic_cast<TH2D *>(hSub->Clone((prefix + "_NRawEtaPhi").c_str()));
1252         h2_nraw->SetDirectory(nullptr);
1253         h2_nraw->Reset("ICES");
1254         std::cerr << "[plot_tklCombinatoric] Missing " << prefix << "_Sub_AbsDphiCount. Falling back to per-cell integration for h2_nraw.\n";
1255     }
1256     if (h2_nraw->GetNbinsX() != hSilSeed->GetNbinsX() || h2_nraw->GetNbinsY() != hSilSeed->GetNbinsY())
1257     {
1258         std::cerr << "[plot_tklCombinatoric] Incompatible binning between " << prefix << "_Sub and " << prefix << "_SilSeedEtaPhi.\n";
1259         delete h2_nraw;
1260         fin->Close();
1261         return ratio_outputs;
1262     }
1263     if (is_simulation && (h2_nraw->GetNbinsX() != hPrimaryCharged->GetNbinsX() || h2_nraw->GetNbinsY() != hPrimaryCharged->GetNbinsY()))
1264     {
1265         std::cerr << "[plot_tklCombinatoric] Incompatible binning between " << prefix << "_NRawEtaPhi and " << prefix << "_PrimaryChargedEtaPhi.\n";
1266         delete h2_nraw;
1267         fin->Close();
1268         return ratio_outputs;
1269     }
1270     auto check_same_binning = [](const TH2D *ha, const TH2D *hb) -> bool { return ha && hb && ha->GetNbinsX() == hb->GetNbinsX() && ha->GetNbinsY() == hb->GetNbinsY(); };
1271     if (!check_same_binning(hSilSeed_nMVTX3nINTT2, hSilSeed) || !check_same_binning(hSilSeed_nMVTX3nINTT1, hSilSeed) || !check_same_binning(hSilSeed_nMVTX2nINTT2, hSilSeed) || !check_same_binning(hSilSeed_nMVTX2nINTT1, hSilSeed) || !check_same_binning(hSilSeed_nMVTXnINTTOther, hSilSeed))
1272     {
1273         std::cerr << "[plot_tklCombinatoric] Incompatible binning between silicon-seed categories and inclusive silicon-seed histogram.\n";
1274         delete h2_nraw;
1275         fin->Close();
1276         return ratio_outputs;
1277     }
1278 
1279     const bool etaAscending = hGrid->GetXaxis()->GetBinCenter(1) <= hGrid->GetXaxis()->GetBinCenter(nEta);
1280     const bool phiAscending = hGrid->GetYaxis()->GetBinCenter(1) <= hGrid->GetYaxis()->GetBinCenter(nPhi);
1281     const bool use_custom_xrange = std::isfinite(plot_xmin) && std::isfinite(plot_xmax) && (plot_xmax > plot_xmin);
1282 
1283     // Compute a shared y-axis max across all available signal/background cell histograms.
1284     double global_ymax = 0.0;
1285     double ref_bin_size = -1.0;
1286     for (int ieta = 0; ieta < nEta; ++ieta)
1287     {
1288         for (int iphi = 0; iphi < nPhi; ++iphi)
1289         {
1290             auto *hSig = GetCellHist(fin, "dR_Sig", prefix, mode, "sig", ieta, iphi, hGrid);
1291             auto *hBkg = GetCellHist(fin, "dR_Bkg", prefix, mode, "bkg", ieta, iphi, hGrid);
1292 
1293             if (hSig)
1294             {
1295                 global_ymax = std::max(global_ymax, hSig->GetMaximum());
1296                 if (ref_bin_size < 0.0)
1297                     ref_bin_size = hSig->GetXaxis()->GetBinWidth(1);
1298             }
1299             if (hBkg)
1300             {
1301                 global_ymax = std::max(global_ymax, hBkg->GetMaximum());
1302                 if (ref_bin_size < 0.0)
1303                     ref_bin_size = hBkg->GetXaxis()->GetBinWidth(1);
1304             }
1305         }
1306     }
1307     const double shared_ymax = (global_ymax > 0.0) ? 1.3 * global_ymax : 1.0;
1308     const std::string yAxisTitle = (ref_bin_size > 0.0) ? Form("Counts / (%.2g radian)", ref_bin_size) : "Counts / (bin size) [radian]";
1309 
1310     auto *c = new TCanvas("c_tklCombinatoric", "c_tklCombinatoric", 5500, 5000);
1311     const float lM = 0.03f, rM = 0.02f, bM = 0.03f, tM = 0.02f;
1312     CanvasPartition(c, nEta, nPhi, lM, rM, bM, tM);
1313 
1314     auto *pad00 = dynamic_cast<TPad *>(c->FindObject("pad_0_0"));
1315     if (!pad00)
1316     {
1317         std::cerr << "[plot_tklCombinatoric] pad_0_0 not found.\n";
1318         delete h2_nraw;
1319         fin->Close();
1320         delete c;
1321         return ratio_outputs;
1322     }
1323 
1324     std::vector<TH1D *> frames;
1325     std::vector<TLine *> cutLines;
1326 
1327     int nDrawn = 0;
1328 
1329     auto styleHist = [](TH1D *h, Style_t ms, Color_t col, float markerSize)
1330     {
1331         h->SetMarkerStyle(ms);
1332         h->SetMarkerSize(markerSize);
1333         h->SetLineColor(col);
1334         h->SetMarkerColor(col);
1335         h->Draw("E1 SAME");
1336     };
1337 
1338     auto createAndDrawCutLine = [&](double x, int linewidth) -> TLine *
1339     {
1340         auto *line = new TLine(x, 0.0, x, shared_ymax);
1341         line->SetLineStyle(3);
1342         line->SetLineWidth(linewidth);
1343         line->SetLineColor(kBlue + 2);
1344         line->Draw("SAME");
1345         cutLines.push_back(line);
1346         return line;
1347     };
1348 
1349     auto SaveSingleCellPlot = [&](bool isdata, TH1D *hSig, TH1D *hBkg, int ieta, int iphi, double etaLow, double etaHigh, double phiLow, double phiHigh, double nraw, std::string str_mode, std::pair<double, double> cutrange)
1350     {
1351         if (!hSig && !hBkg)
1352             return;
1353 
1354         const std::string range_suffix = EtaPhiRangeNameSuffix(hGrid, ieta, iphi);
1355         const std::string cname = "c_single_" + range_suffix;
1356         TCanvas csingle(cname.c_str(), cname.c_str(), 800, 700);
1357         csingle.cd();
1358         gPad->SetLeftMargin(0.16);
1359         gPad->SetRightMargin(0.05);
1360         // gPad->SetBottomMargin(0.13);
1361         gPad->SetTopMargin(0.06);
1362         gPad->SetTicks(1, 1);
1363 
1364         TH1D *frame_single = (TH1D *)(hSig ? hSig : hBkg)->Clone((cname + "_frame").c_str());
1365         frame_single->SetDirectory(nullptr);
1366         frame_single->Reset("ICES");
1367         // frame_single->SetMinimum(0.0);
1368         frame_single->SetMaximum(shared_ymax);
1369         if (use_custom_xrange)
1370             frame_single->GetXaxis()->SetRangeUser(plot_xmin, plot_xmax);
1371         frame_single->GetXaxis()->SetTitle("INTT doublet |#Delta#phi| [radian]");
1372         frame_single->GetYaxis()->SetTitle(yAxisTitle.c_str());
1373         frame_single->GetYaxis()->SetTitleOffset(1.5);
1374         frame_single->Draw("AXIS");
1375 
1376         if (hSig)
1377             styleHist(hSig, kFullCircle, kBlack, 1.0f);
1378         if (hBkg)
1379             styleHist(hBkg, kOpenCircle, kRed + 1, 1.0f);
1380 
1381         TLegend leg(gPad->GetLeftMargin() + 0.05,     //
1382                     1. - gPad->GetTopMargin() - 0.22, //
1383                     gPad->GetLeftMargin() + 0.2,      //
1384                     1. - gPad->GetTopMargin() - 0.04  //
1385         );
1386         leg.SetHeader(Form("%s. #eta=[%s,%s], #phi=[%s,%s]", isdata ? "Data" : "Simulation", to_string_with_precision(etaLow).c_str(), to_string_with_precision(etaHigh).c_str(), to_string_with_precision(phiLow).c_str(), to_string_with_precision(phiHigh).c_str()));
1387         leg.SetBorderSize(0);
1388         leg.SetFillStyle(0);
1389         leg.SetTextSize(0.035);
1390         if (hSig)
1391             leg.AddEntry(hSig, "Unrotated", "pel");
1392         if (hBkg)
1393             leg.AddEntry(hBkg, "Rotated", "pel");
1394         leg.AddEntry((TObject *)0, Form("N^{Uncorr.}_{Doublets}=%.1f (%s=[%s,%s])", nraw, str_mode.c_str(), to_string_with_precision(cutrange.first).c_str(), to_string_with_precision(cutrange.second).c_str()), "");
1395         leg.Draw();
1396 
1397         createAndDrawCutLine(cutrange.first, 2);
1398         createAndDrawCutLine(cutrange.second, 2);
1399 
1400         const std::string outbase = single_canvas_dir + "/" + outstem + "_" + range_suffix;
1401         csingle.SaveAs((outbase + ".pdf").c_str());
1402         csingle.SaveAs((outbase + ".png").c_str());
1403         delete frame_single;
1404     };
1405 
1406     // a lambda to create a string for the mode
1407     auto modeStringLambda = [&]() -> std::string
1408     {
1409         if (mode == "absdPhi")
1410             return "|#Delta#phi|";
1411         else if (mode == "dR")
1412             return "#DeltaR";
1413         else
1414             return mode; // as-is if unrecognized, e.g. for custom modes
1415     };
1416     std::string modeString = modeStringLambda();
1417     std::string multiplicity_text = "inclusive";
1418     const std::string multiplicity_prefix_1 = "Cluster multiplicity percentile ";
1419     const std::string multiplicity_prefix_2 = "N_{INTT clusters} ";
1420     if (StartsWith(ratio_outputs.label, multiplicity_prefix_1))
1421     {
1422         multiplicity_text = ratio_outputs.label.substr(multiplicity_prefix_1.size());
1423     }
1424     else if (StartsWith(ratio_outputs.label, multiplicity_prefix_2))
1425     {
1426         multiplicity_text = ratio_outputs.label.substr(multiplicity_prefix_2.size());
1427     }
1428     else if (!ratio_outputs.label.empty() && ratio_outputs.label != "Inclusive")
1429     {
1430         multiplicity_text = ratio_outputs.label;
1431     }
1432     const std::string sample_label = Form("%s, %s#leqZ_{vtx}^{silicon}#leq%s [cm], %s", is_simulation ? "Simulation" : "Data", to_string_with_precision(zvtx_cut_min, 1).c_str(), to_string_with_precision(zvtx_cut_max, 1).c_str(), multiplicity_text.c_str());
1433 
1434     for (int ieta = 0; ieta < nEta; ++ieta)
1435     {
1436         for (int iphi = 0; iphi < nPhi; ++iphi)
1437         {
1438 
1439             c->cd(0);
1440             const int padEta = etaAscending ? ieta : (nEta - 1 - ieta);
1441             const int padPhi = phiAscending ? iphi : (nPhi - 1 - iphi);
1442 
1443             auto *pad = dynamic_cast<TPad *>(c->FindObject(TString::Format("pad_%d_%d", padEta, padPhi).Data()));
1444             if (!pad)
1445                 continue;
1446 
1447             pad->cd();
1448             pad->SetTicks(1, 1);
1449 
1450             float xFactor = pad00->GetAbsWNDC() / pad->GetAbsWNDC();
1451             float yFactor = pad00->GetAbsHNDC() / pad->GetAbsHNDC();
1452 
1453             auto *hSig = GetCellHist(fin, "dR_Sig", prefix, mode, "sig", ieta, iphi, hGrid);
1454             auto *hBkg = GetCellHist(fin, "dR_Bkg", prefix, mode, "bkg", ieta, iphi, hGrid);
1455             if (!hSig && !hBkg)
1456                 continue;
1457 
1458             // Clone axis template from whichever histogram is available.
1459             TH1D *baseHist = hSig ? hSig : hBkg;
1460             auto *frame = (TH1D *)baseHist->Clone(Form("hframe_ieta%d_iphi%d", ieta, iphi));
1461             frame->SetDirectory(nullptr);
1462             frame->Reset("ICES");
1463             frames.push_back(frame); // keep alive until after SaveAs
1464 
1465             // frame->SetMinimum(0.);
1466             frame->SetMaximum(shared_ymax);
1467             frame->SetTitle("");
1468             frame->GetXaxis()->SetNdivisions(505);
1469             frame->GetYaxis()->SetNdivisions(505);
1470             if (use_custom_xrange)
1471             {
1472                 frame->GetXaxis()->SetRangeUser(plot_xmin, plot_xmax);
1473             }
1474             frame->GetXaxis()->SetLabelFont(43);
1475             frame->GetXaxis()->SetLabelSize(34);
1476             frame->GetYaxis()->SetLabelFont(43);
1477             frame->GetYaxis()->SetLabelSize(34);
1478             frame->GetXaxis()->SetTickLength(yFactor * 0.06f / xFactor);
1479             frame->GetYaxis()->SetTickLength(xFactor * 0.04f / yFactor);
1480             frame->GetXaxis()->SetTitle("");
1481             frame->GetYaxis()->SetTitle("");
1482             frame->Draw("AXIS");
1483 
1484             createAndDrawCutLine(raw_count_xmin, 1);
1485             createAndDrawCutLine(raw_count_xmax, 1);
1486 
1487             if (hSig)
1488                 styleHist(hSig, kFullCircle, kBlack, 0.0f);
1489             if (hBkg)
1490                 styleHist(hBkg, kOpenCircle, kRed + 1, 0.0f);
1491 
1492             double nraw = h2_nraw->GetBinContent(ieta + 1, iphi + 1);
1493             double nraw_err = h2_nraw->GetBinError(ieta + 1, iphi + 1);
1494             if (!use_direct_raw_count_grid)
1495             {
1496                 nraw = IntegralInRange(hSig, raw_count_xmin, raw_count_xmax) - IntegralInRange(hBkg, raw_count_xmin, raw_count_xmax);
1497                 nraw_err = (nraw > 0.0) ? std::sqrt(nraw) : 0.0;
1498                 h2_nraw->SetBinContent(ieta + 1, iphi + 1, nraw);
1499                 h2_nraw->SetBinError(ieta + 1, iphi + 1, nraw_err);
1500             }
1501 
1502             const double etaLow = hGrid->GetXaxis()->GetBinLowEdge(ieta + 1);
1503             const double etaHigh = hGrid->GetXaxis()->GetBinUpEdge(ieta + 1);
1504             const double phiLow = hGrid->GetYaxis()->GetBinLowEdge(iphi + 1);
1505             const double phiHigh = hGrid->GetYaxis()->GetBinUpEdge(iphi + 1);
1506 
1507             TLatex lbl;
1508             lbl.SetNDC();
1509             lbl.SetTextFont(43);
1510             lbl.SetTextSize(28);
1511             lbl.DrawLatex(XtoPad(0.05), YtoPad(0.90), Form("#eta=[%s,%s]", to_string_with_precision(etaLow).c_str(), to_string_with_precision(etaHigh).c_str()));
1512             lbl.DrawLatex(XtoPad(0.05), YtoPad(0.81), Form("#phi=[%s,%s]", to_string_with_precision(phiLow).c_str(), to_string_with_precision(phiHigh).c_str()));
1513             lbl.DrawLatex(XtoPad(0.05), YtoPad(0.70), use_direct_raw_count_grid ? Form("N_{raw}^{stored}=%.1f", nraw) : Form("N_{raw}(%.3f<|#Delta#phi|<%.3f)=%.1f", raw_count_xmin, raw_count_xmax, nraw));
1514 
1515             // SaveSingleCellPlot(!is_simulation, hSig, hBkg, ieta, iphi, etaLow, etaHigh, phiLow, phiHigh, nraw, modeString, std::make_pair(raw_count_xmin, raw_count_xmax));
1516 
1517             pad->Modified();
1518             ++nDrawn;
1519         }
1520     }
1521 
1522     // Global axis titles and header drawn on the main canvas.
1523     c->cd();
1524     TLatex tx;
1525     tx.SetNDC();
1526     tx.SetTextFont(43);
1527 
1528     tx.SetTextSize(50);
1529     tx.SetTextAlign(kHAlignCenter + kVAlignTop);
1530     tx.DrawLatex(0.5, bM / 2., "|#Delta#phi| [radian]");
1531 
1532     tx.SetTextAngle(90);
1533     tx.SetTextAlign(kHAlignCenter + kVAlignBottom);
1534     tx.SetTextSize(50);
1535     tx.DrawLatex(lM / 2., 0.5, yAxisTitle.c_str());
1536 
1537     std::cout << "[plot_tklCombinatoric] Pads with histograms: " << nDrawn << " / " << nEta * nPhi << "\n";
1538 
1539     auto build_and_draw_ratio = [&](const TH2D *hNum,                   //
1540                                     const TH2D *hDen,                   //
1541                                     const std::string &ratio_hist_name, //
1542                                     const std::string &ztitle,          //
1543                                     const std::string &out_suffix,      //
1544                                     int round_zmax_mode = -1,           //
1545                                     const std::string &text_format = "") -> TH2D *
1546     {
1547         double ratio_zmax = -1;
1548         auto *h2_ratio = dynamic_cast<TH2D *>(h2_nraw->Clone(ratio_hist_name.c_str()));
1549         h2_ratio->SetDirectory(nullptr);
1550         h2_ratio->Reset("ICES");
1551 
1552         for (int ibx = 1; ibx <= h2_ratio->GetNbinsX(); ++ibx)
1553         {
1554             for (int iby = 1; iby <= h2_ratio->GetNbinsY(); ++iby)
1555             {
1556                 const double num = hNum->GetBinContent(ibx, iby);
1557                 const double den = hDen->GetBinContent(ibx, iby);
1558                 const double ratio = (den > 0.0) ? (num / den) : 0.0;
1559                 if (ratio > ratio_zmax)
1560                     ratio_zmax = ratio;
1561 
1562                 // Counting errors with sqrt(N), propagated for ratio r = num/den.
1563                 double ratio_err = 0.0;
1564                 if (den > 0.0 && num > 0.0)
1565                 {
1566                     const double sigma_num = std::sqrt(num);
1567                     const double sigma_den = std::sqrt(den);
1568                     const double rel_num = sigma_num / num;
1569                     const double rel_den = sigma_den / den;
1570                     ratio_err = ratio * std::sqrt(rel_num * rel_num + rel_den * rel_den);
1571                 }
1572                 h2_ratio->SetBinContent(ibx, iby, ratio);
1573                 h2_ratio->SetBinError(ibx, iby, ratio_err);
1574             }
1575         }
1576 
1577         // The maximum should be the nearest number of 5, 10, 15, ... above the histogram maximum.
1578         const bool round_zmax = (round_zmax_mode < 0) ? use_rounded_zmax : (round_zmax_mode != 0);
1579         double ratio_zmax_plot = ratio_zmax;
1580         if (round_zmax)
1581         {
1582             ratio_zmax_plot = std::ceil(ratio_zmax / 5.0) * 5.0;
1583             if (ratio_zmax_plot < ratio_zmax)
1584                 ratio_zmax_plot += 5.0;
1585         }
1586         const std::string ratio_paint_fmt = text_format.empty() ? ratio_text_format : text_format;
1587 
1588         draw2Dhistogram_adjZaxis(h2_ratio,        //
1589                                  false,           //
1590                                  "#eta",          //
1591                                  "#phi [radian]", //
1592                                  ztitle,          //
1593                                  std::pair<double, double>(0, ratio_zmax_plot), {sample_label},
1594                                  ratio_draw_option, //
1595                                  ratio_paint_fmt,   //
1596                                  true,              //
1597                                  false,             //
1598                                  plotdir + "/" + outstem + out_suffix);
1599         return h2_ratio;
1600     };
1601 
1602     auto draw_input_2d = [&](TH2D *h2,                                    //
1603                              const std::string &ztitle,                   //
1604                              const std::string &out_suffix,               //
1605                              const std::string &drawoption = "colz",      //
1606                              bool show_bin_value_with_error = false,      //
1607                              bool show_bin_value_only = false,            //
1608                              const std::string &xtitle = "#eta",          //
1609                              const std::string &ytitle = "#phi [radian]", //
1610                              bool logz = false)
1611     {
1612         if (!h2)
1613             return;
1614 
1615         // The maximum should be the nearest number of 5, 10, 15, ... above the histogram maximum.
1616         double zmax = h2->GetMaximum();
1617         double zmax_plot = zmax;
1618         if (use_rounded_zmax)
1619         {
1620             zmax_plot = std::ceil(zmax / 5.0) * 5.0;
1621             if (zmax_plot < zmax)
1622                 zmax_plot += 5.0;
1623         }
1624 
1625         draw2Dhistogram_adjZaxis(h2,                                      //
1626                                  logz,                                    //
1627                                  xtitle,                                  //
1628                                  ytitle,                                  //
1629                                  ztitle,                                  //
1630                                  std::pair<double, double>(0, zmax_plot), //
1631                                  {sample_label},                          //
1632                                  drawoption,                              //
1633                                  ratio_text_format,                       //
1634                                  show_bin_value_with_error,               //
1635                                  show_bin_value_only,                     //
1636                                  plotdir + "/" + outstem + out_suffix);
1637     };
1638 
1639     auto make_projection = [&](TH2D *h2, bool project_x, const std::string &hist_name) -> TH1D *
1640     {
1641         if (!h2)
1642             return nullptr;
1643 
1644         TH1D *hproj = project_x ? h2->ProjectionX(hist_name.c_str(), 1, h2->GetNbinsY()) : h2->ProjectionY(hist_name.c_str(), 1, h2->GetNbinsX());
1645         if (!hproj)
1646             return nullptr;
1647         hproj->SetDirectory(nullptr);
1648         return hproj;
1649     };
1650 
1651     auto draw_projection = [&](TH2D *h2, bool project_x, const std::string &axis_title, const std::string &ytitle, const std::string &out_suffix)
1652     {
1653         if (!h2)
1654             return;
1655 
1656         TH1D *hproj = make_projection(h2, project_x, std::string(h2->GetName()) + (project_x ? "_projX" : "_projY"));
1657         if (!hproj)
1658             return;
1659         hproj->GetXaxis()->SetTitle(axis_title.c_str());
1660         hproj->GetYaxis()->SetTitle(ytitle.c_str());
1661         draw1Dhistogram(hproj, false, axis_title, ytitle, {sample_label}, "hist e1", plotdir + "/" + outstem + out_suffix);
1662         delete hproj;
1663     };
1664 
1665     auto build_projection_ratio = [&](std::pair<TH2D *, std::string> hNum2D, std::pair<TH2D *, std::string> hDen2D, bool project_x, const std::string &axis_title, const std::string &ratio_ytitle, const std::string &out_suffix, const std::string &ratio_hist_name) -> TH1D *
1666     {
1667         if (!hNum2D.first || !hDen2D.first)
1668             return nullptr;
1669 
1670         // Projection, do NOT include overflow and underflow bins for now
1671         TH1D *hNumProj = project_x ? hNum2D.first->ProjectionX((ratio_hist_name + "_num").c_str(), 1, hNum2D.first->GetNbinsY()) : hNum2D.first->ProjectionY((ratio_hist_name + "_num").c_str(), 1, hNum2D.first->GetNbinsX());
1672         TH1D *hDenProj = project_x ? hDen2D.first->ProjectionX((ratio_hist_name + "_den").c_str(), 1, hDen2D.first->GetNbinsY()) : hDen2D.first->ProjectionY((ratio_hist_name + "_den").c_str(), 1, hDen2D.first->GetNbinsX());
1673         if (!hNumProj || !hDenProj)
1674         {
1675             delete hNumProj;
1676             delete hDenProj;
1677             return nullptr;
1678         }
1679 
1680         hNumProj->SetDirectory(nullptr);
1681         hDenProj->SetDirectory(nullptr);
1682         drawComparisonWithRatio(std::make_pair(hDenProj, hDen2D.second), std::make_pair(hNumProj, hNum2D.second), false, axis_title, "Counts", ratio_ytitle, {sample_label}, plotdir + "/" + outstem + out_suffix + "_components");
1683 
1684         TH1D *hRatio = dynamic_cast<TH1D *>(hNumProj->Clone(ratio_hist_name.c_str()));
1685         if (hRatio)
1686         {
1687             hRatio->SetDirectory(nullptr);
1688             hRatio->Reset("ICES");
1689             hRatio->Divide(hNumProj, hDenProj, 1.0, 1.0);
1690             draw1Dhistogram(hRatio, false, axis_title, ratio_ytitle, {sample_label}, "hist e1", plotdir + "/" + outstem + out_suffix);
1691         }
1692 
1693         delete hNumProj;
1694         delete hDenProj;
1695         return hRatio;
1696     };
1697 
1698     auto draw_projection_stack = [&](TH2D *hInclusive, const std::vector<std::pair<TH2D *, std::string>> &components, bool project_x, const std::string &axis_title, const std::string &out_suffix)
1699     {
1700         if (!hInclusive)
1701             return;
1702 
1703         TH1D *hInclusiveProj = make_projection(hInclusive, project_x, std::string(hInclusive->GetName()) + (project_x ? "_projX_stack_inclusive" : "_projY_stack_inclusive"));
1704         if (!hInclusiveProj)
1705             return;
1706 
1707         const float alpha = 0.5f;
1708         const std::vector<std::string> fill_colors = {"#ffa90e", "#6CA651", "#832db6", "#bd1f01", "#3f90da"};
1709 
1710         TCanvas *cstack = new TCanvas((std::string("c") + out_suffix).c_str(), (std::string("c") + out_suffix).c_str(), 800, 700);
1711         cstack->cd();
1712         gPad->SetLeftMargin(0.14);
1713         gPad->SetRightMargin(0.05);
1714         gPad->SetTopMargin(0.08);
1715 
1716         THStack *hs = new THStack((std::string(hInclusive->GetName()) + (project_x ? "_projX_stack" : "_projY_stack")).c_str(), (std::string(";") + axis_title + ";Counts").c_str());
1717         std::vector<TH1D *> component_proj;
1718         std::vector<std::string> component_labels;
1719         component_proj.reserve(components.size());
1720         component_labels.reserve(components.size());
1721 
1722         for (size_t i = 0; i < components.size(); ++i)
1723         {
1724             TH2D *h2 = components[i].first;
1725             if (!h2)
1726                 continue;
1727 
1728             TH1D *hproj = make_projection(h2, project_x, std::string(h2->GetName()) + (project_x ? "_projX_stack_component" : "_projY_stack_component"));
1729             if (!hproj)
1730                 continue;
1731 
1732             const Color_t color = TColor::GetColor(fill_colors[i % fill_colors.size()].c_str());
1733             hproj->SetFillColorAlpha(color, alpha);
1734             hproj->SetLineColor(color);
1735             hproj->SetLineWidth(1);
1736             hs->Add(hproj);
1737             component_proj.push_back(hproj);
1738             component_labels.push_back(components[i].second);
1739         }
1740 
1741         hs->Draw("hist");
1742         hs->GetXaxis()->SetTitle(axis_title.c_str());
1743         hs->GetYaxis()->SetTitle("Counts");
1744         hs->GetYaxis()->SetTitleOffset(1.4);
1745         hs->SetMaximum(hInclusiveProj->GetMaximum() * 1.5);
1746         gPad->Modified();
1747         gPad->Update();
1748 
1749         hInclusiveProj->SetLineColor(kBlack);
1750         hInclusiveProj->SetLineWidth(2);
1751         hInclusiveProj->Draw("hist SAME");
1752 
1753         TLegend *leg = new TLegend(gPad->GetLeftMargin() + 0.02, 1 - gPad->GetTopMargin() - 0.24, gPad->GetLeftMargin() + 0.34, 1 - gPad->GetTopMargin() - 0.04);
1754         leg->SetBorderSize(0);
1755         leg->SetFillStyle(0);
1756         leg->SetTextSize(0.035);
1757         leg->AddEntry(hInclusiveProj, "Inclusive", "l");
1758         for (size_t i = 0; i < component_proj.size(); ++i)
1759             leg->AddEntry(component_proj[i], component_labels[i].c_str(), "f");
1760         leg->Draw();
1761 
1762         TLatex latex;
1763         latex.SetNDC();
1764         latex.SetTextFont(42);
1765         latex.SetTextSize(0.035);
1766         latex.SetTextAlign(kHAlignRight + kVAlignBottom);
1767         latex.DrawLatex(1 - gPad->GetRightMargin(), 1 - gPad->GetTopMargin() + 0.01, sample_label.c_str());
1768 
1769         cstack->SaveAs((plotdir + "/" + outstem + out_suffix + ".png").c_str());
1770         cstack->SaveAs((plotdir + "/" + outstem + out_suffix + ".pdf").c_str());
1771 
1772         delete leg;
1773         for (auto *h : component_proj)
1774             delete h;
1775         delete hs;
1776         delete hInclusiveProj;
1777         delete cstack;
1778     };
1779 
1780     auto draw_projection_stack_fractional = [&](TH2D *hInclusive, const std::vector<std::pair<TH2D *, std::string>> &components, bool project_x, const std::string &axis_title, const std::string &out_suffix)
1781     {
1782         if (!hInclusive)
1783             return;
1784 
1785         TH1D *hInclusiveProj = make_projection(hInclusive, project_x, std::string(hInclusive->GetName()) + (project_x ? "_projX_frac_inclusive" : "_projY_frac_inclusive"));
1786         if (!hInclusiveProj)
1787             return;
1788 
1789         const float alpha = 0.5f;
1790         const std::vector<std::string> fill_colors = {"#ffa90e", "#6CA651", "#832db6", "#bd1f01", "#3f90da"};
1791 
1792         TCanvas *cFrac = new TCanvas((std::string("c") + out_suffix).c_str(), (std::string("c") + out_suffix).c_str(), 800, 700);
1793         cFrac->cd();
1794         gPad->SetLeftMargin(0.14);
1795         gPad->SetRightMargin(0.05);
1796         gPad->SetTopMargin(0.23);
1797 
1798         THStack *hsFrac = new THStack((std::string(hInclusive->GetName()) + (project_x ? "_projX_stack_fractional" : "_projY_stack_fractional")).c_str(), (std::string(";") + axis_title + ";Fractional contribution").c_str());
1799         std::vector<TH1D *> component_frac;
1800         std::vector<std::string> component_labels;
1801         component_frac.reserve(components.size());
1802         component_labels.reserve(components.size());
1803 
1804         for (size_t i = 0; i < components.size(); ++i)
1805         {
1806             TH2D *h2 = components[i].first;
1807             if (!h2)
1808                 continue;
1809 
1810             TH1D *hproj = make_projection(h2, project_x, std::string(h2->GetName()) + (project_x ? "_projX_frac_component" : "_projY_frac_component"));
1811             if (!hproj)
1812                 continue;
1813 
1814             TH1D *hFrac = MakeFractionalHist(hproj, hInclusiveProj, std::string(hproj->GetName()) + "_fractional");
1815             delete hproj;
1816             if (!hFrac)
1817                 continue;
1818 
1819             const Color_t color = TColor::GetColor(fill_colors[i % fill_colors.size()].c_str());
1820             hFrac->SetFillColorAlpha(color, alpha);
1821             hFrac->SetLineColor(color);
1822             hFrac->SetLineWidth(2);
1823             hsFrac->Add(hFrac);
1824             component_frac.push_back(hFrac);
1825             component_labels.push_back(components[i].second);
1826         }
1827 
1828         hsFrac->Draw("hist");
1829         hsFrac->GetXaxis()->SetTitle(axis_title.c_str());
1830         hsFrac->GetYaxis()->SetTitle("Fractional contribution");
1831         hsFrac->GetYaxis()->SetTitleOffset(1.5);
1832         hsFrac->SetMaximum(1.0);
1833         gPad->Modified();
1834         gPad->Update();
1835 
1836         TLine *line1 = new TLine(hInclusiveProj->GetXaxis()->GetXmin(), 1.0, hInclusiveProj->GetXaxis()->GetXmax(), 1.0);
1837         line1->SetLineColor(kBlack);
1838         line1->SetLineWidth(2);
1839 
1840         TLegend *legFrac = new TLegend(gPad->GetLeftMargin() + 0.02, 1 - gPad->GetTopMargin() + 0.01, gPad->GetLeftMargin() + 0.34, 0.99);
1841         legFrac->SetBorderSize(0);
1842         legFrac->SetFillStyle(0);
1843         legFrac->SetTextSize(0.035);
1844         legFrac->AddEntry(line1, "Inclusive", "l");
1845         for (size_t i = 0; i < component_frac.size(); ++i)
1846             legFrac->AddEntry(component_frac[i], component_labels[i].c_str(), "f");
1847         legFrac->Draw();
1848 
1849         TLatex latex;
1850         latex.SetNDC();
1851         latex.SetTextFont(42);
1852         latex.SetTextSize(0.03);
1853         latex.SetTextAlign(kHAlignRight + kVAlignBottom);
1854         latex.DrawLatex(1 - gPad->GetRightMargin(), 1 - gPad->GetTopMargin() + 0.01, sample_label.c_str());
1855 
1856         cFrac->SaveAs((plotdir + "/" + outstem + out_suffix + ".png").c_str());
1857         cFrac->SaveAs((plotdir + "/" + outstem + out_suffix + ".pdf").c_str());
1858 
1859         delete legFrac;
1860         delete line1;
1861         for (auto *h : component_frac)
1862             delete h;
1863         delete hsFrac;
1864         delete hInclusiveProj;
1865         delete cFrac;
1866     };
1867 
1868     draw_input_2d(h2_nraw, "N_{Uncorrected}^{INTT Doublets}", "_h2_nraw");
1869     draw_input_2d(hSilSeed, "N_{Silicon seeds}", "_hSilSeed", "colz text", false, true);
1870     draw_input_2d(hSilSeed_nMVTX3nINTT2, "N_{Silicon seeds}^{nMVTX3nINTT2}", "_hSilSeed_nMVTX3nINTT2", "colz text", false, true);
1871     draw_input_2d(hSilSeed_nMVTX3nINTT1, "N_{Silicon seeds}^{nMVTX3nINTT1}", "_hSilSeed_nMVTX3nINTT1", "colz text", false, true);
1872     draw_input_2d(hSilSeed_nMVTX2nINTT2, "N_{Silicon seeds}^{nMVTX2nINTT2}", "_hSilSeed_nMVTX2nINTT2", "colz text", false, true);
1873     draw_input_2d(hSilSeed_nMVTX2nINTT1, "N_{Silicon seeds}^{nMVTX2nINTT1}", "_hSilSeed_nMVTX2nINTT1", "colz text", false, true);
1874     draw_input_2d(hSilSeed_nMVTXnINTTOther, "N_{Silicon seeds}^{nMVTXnINTTOther}", "_hSilSeed_nMVTXnINTTOther", "colz text", false, true);
1875     draw_input_2d(hSilSeed_good, "N_{Silicon seeds}^{good}", "_hSilSeed_good", "colz text", false, true);
1876     draw_input_2d(hINTTClusterVsAssociatedSilSeed, "Entries", "_hINTTClusterVsAssociatedSilSeed", "colz", false, false, "N_{INTT clusters} (selected crossing)", "N_{Silicon seeds}^{assoc. w. vertex} (selected crossing)", true);
1877     draw_input_2d(hINTTClusterVsAllSilSeed, "Entries", "_hINTTClusterVsAllSilSeed", "colz", false, false, "N_{INTT clusters} (selected crossing)", "N_{Silicon seeds}^{all in crossing} (selected crossing)", true);
1878     drawComparisonWithRatio(std::make_pair(hSeedCrossing_selectedCrossings, "Selected crossings"),                                                                        //
1879                             std::make_pair(hSeedCrossing_selectedCrossings_largeDiffNClusterSeeds, "Selected crossings with N_{INTT clusters}>4#timesN_{Silicon seeds}"), //
1880                             true,                                                                                                                                         //
1881                             "Crossing",                                                                                                                                   //
1882                             "Entries",                                                                                                                                    //
1883                             "Ratio",                                                                                                                                      //
1884                             {sample_label},                                                                                                                               //
1885                             plotdir + "/" + outstem + "_hSeedCrossing_selectedCrossings_comparison"                                                                       //
1886     );
1887     draw_input_2d(hSeed, "N_{Cluster}^{Layer 3+4}", "_hSeed"); // this is cluster in inner layer, not "silicon seeds"
1888     if (is_simulation)
1889         draw_input_2d(hPrimaryCharged, "N_{sPHENIX primary charged}", "_hPrimaryCharged");
1890 
1891     draw_projection(hSilSeed, false, "#phi [radian]", "Counts", "_hSilSeed_phiProjection");
1892     draw_projection(hSilSeed, true, "#eta", "Counts", "_hSilSeed_etaProjection");
1893     draw_projection(hSilSeed_nMVTX3nINTT2, false, "#phi [radian]", "Counts", "_hSilSeed_nMVTX3nINTT2_phiProjection");
1894     draw_projection(hSilSeed_nMVTX3nINTT2, true, "#eta", "Counts", "_hSilSeed_nMVTX3nINTT2_etaProjection");
1895     draw_projection(hSilSeed_nMVTX3nINTT1, false, "#phi [radian]", "Counts", "_hSilSeed_nMVTX3nINTT1_phiProjection");
1896     draw_projection(hSilSeed_nMVTX3nINTT1, true, "#eta", "Counts", "_hSilSeed_nMVTX3nINTT1_etaProjection");
1897     draw_projection(hSilSeed_nMVTX2nINTT2, false, "#phi [radian]", "Counts", "_hSilSeed_nMVTX2nINTT2_phiProjection");
1898     draw_projection(hSilSeed_nMVTX2nINTT2, true, "#eta", "Counts", "_hSilSeed_nMVTX2nINTT2_etaProjection");
1899     draw_projection(hSilSeed_nMVTX2nINTT1, false, "#phi [radian]", "Counts", "_hSilSeed_nMVTX2nINTT1_phiProjection");
1900     draw_projection(hSilSeed_nMVTX2nINTT1, true, "#eta", "Counts", "_hSilSeed_nMVTX2nINTT1_etaProjection");
1901     draw_projection(hSilSeed_nMVTXnINTTOther, false, "#phi [radian]", "Counts", "_hSilSeed_nMVTXnINTTOther_phiProjection");
1902     draw_projection(hSilSeed_nMVTXnINTTOther, true, "#eta", "Counts", "_hSilSeed_nMVTXnINTTOther_etaProjection");
1903     draw_projection_stack(hSilSeed, //
1904                           {
1905                               //
1906                               {hSilSeed_nMVTX3nINTT2, "MVTX+INTT: 3+2"},      //
1907                               {hSilSeed_nMVTX3nINTT1, "MVTX+INTT: 3+1"},      //
1908                               {hSilSeed_nMVTX2nINTT2, "MVTX+INTT: 2+2"},      //
1909                               {hSilSeed_nMVTX2nINTT1, "MVTX+INTT: 2+1"},      //
1910                               {hSilSeed_nMVTXnINTTOther, "MVTX+INTT: Other"}, //
1911                           },                                                  //
1912                           false,                                              //
1913                           "#phi [radian]",                                   //
1914                           "_hSilSeed_phiProjection_stack"                    //
1915     );
1916     draw_projection_stack(hSilSeed, //
1917                           {
1918                               //
1919                               {hSilSeed_nMVTX3nINTT2, "MVTX+INTT: 3+2"},      //
1920                               {hSilSeed_nMVTX3nINTT1, "MVTX+INTT: 3+1"},      //
1921                               {hSilSeed_nMVTX2nINTT2, "MVTX+INTT: 2+2"},      //
1922                               {hSilSeed_nMVTX2nINTT1, "MVTX+INTT: 2+1"},      //
1923                               {hSilSeed_nMVTXnINTTOther, "MVTX+INTT: Other"}, //
1924                           },                                                  //
1925                           true,                                               //
1926                           "#eta",                                            //
1927                           "_hSilSeed_etaProjection_stack"                    //
1928     );
1929     draw_projection_stack_fractional(hSilSeed, //
1930                                      {
1931                                          //
1932                                          {hSilSeed_nMVTX3nINTT2, "MVTX+INTT: 3+2"},      //
1933                                          {hSilSeed_nMVTX3nINTT1, "MVTX+INTT: 3+1"},      //
1934                                          {hSilSeed_nMVTX2nINTT2, "MVTX+INTT: 2+2"},      //
1935                                          {hSilSeed_nMVTX2nINTT1, "MVTX+INTT: 2+1"},      //
1936                                          {hSilSeed_nMVTXnINTTOther, "MVTX+INTT: Other"}, //
1937                                      },                                                  //
1938                                      false,                                              //
1939                                      "#phi [radian]",                                   //
1940                                      "_hSilSeed_phiProjection_stack_fractional"         //
1941     );
1942     draw_projection_stack_fractional(hSilSeed, //
1943                                      {
1944                                          //
1945                                          {hSilSeed_nMVTX3nINTT2, "MVTX+INTT: 3+2"},      //
1946                                          {hSilSeed_nMVTX3nINTT1, "MVTX+INTT: 3+1"},      //
1947                                          {hSilSeed_nMVTX2nINTT2, "MVTX+INTT: 2+2"},      //
1948                                          {hSilSeed_nMVTX2nINTT1, "MVTX+INTT: 2+1"},      //
1949                                          {hSilSeed_nMVTXnINTTOther, "MVTX+INTT: Other"}, //
1950                                      },                                                  //
1951                                      true,                                               //
1952                                      "#eta",                                            //
1953                                      "_hSilSeed_etaProjection_stack_fractional"         //
1954     );
1955 
1956     if (hDCA3D_Sig && hDCA3D_Bkg)
1957     {
1958         TH1D *hDCA3D_Sub = dynamic_cast<TH1D *>(hDCA3D_Sig->Clone((prefix + "_DCA3D_Sub").c_str()));
1959         if (hDCA3D_Sub && hSilSeedDCA3D)
1960         {
1961             hDCA3D_Sub->SetDirectory(nullptr);
1962             hDCA3D_Sub->Add(hDCA3D_Bkg, -1.0);
1963 
1964             double dca_ymin = std::min({hDCA3D_Sig->GetMinimum(0), hDCA3D_Bkg->GetMinimum(0), hDCA3D_Sub->GetMinimum(0), hSilSeedDCA3D->GetMinimum(0)});
1965             double dca_ymax = std::max({hDCA3D_Sig->GetMaximum(), hDCA3D_Bkg->GetMaximum(), hDCA3D_Sub->GetMaximum(), hSilSeedDCA3D->GetMaximum()});
1966             if (dca_ymax <= dca_ymin)
1967                 dca_ymax = dca_ymin + 1.0;
1968             const double dca_margin = 0.15 * (dca_ymax - dca_ymin);
1969 
1970             draw_comparison_alt({hDCA3D_Sig, hDCA3D_Bkg, hDCA3D_Sub},                                                                //
1971                                 {"#1f77b4", "#67B2D8", "#000000"},                                                                       //
1972                                 {24, 25, 20},                                                                                                   //
1973                                 {0.5, 0.5, 1},                                                                                                   //
1974                                 {"INTT doublets - Unrotated", "INTT doublets - Rotated", "INTT doublets - Background subtracted"}, //
1975                                 "DCA_{w.r.t vertex} [cm]",                                                                                          //
1976                                 "Counts",                                                                                                           //
1977                                 {0, std::max({hDCA3D_Sig->GetMaximum(), hDCA3D_Bkg->GetMaximum(), hDCA3D_Sub->GetMaximum()})*1.3},                                                                                                           //
1978                                 false,                                                                                                               //
1979                                 false,                                                                                                              //
1980                                 510,                                                                                                                //
1981                                 {false, 0.f},                                                                                                       //
1982                                 plotdir + "/" + outstem + "_hDCA3D_comparison"                                                              //
1983             );
1984 
1985             draw_comparison_alt({hDCA3D_Sig, hDCA3D_Bkg, hDCA3D_Sub, hSilSeedDCA3D},                                                                //
1986                                 {"#1f77b4", "#67B2D8", "#000000", "#bd1f01"},                                                                       //
1987                                 {24, 25, 20, 21},                                                                                                   //
1988                                 {0.5, 0.5, 1, 1},                                                                                                   //
1989                                 {"INTT doublets - Unrotated", "INTT doublets - Rotated", "INTT doublets - Background subtracted", "Silicon seeds"}, //
1990                                 "DCA_{w.r.t vertex} [cm]",                                                                                          //
1991                                 "Counts",                                                                                                           //
1992                                 {-1, -1},                                                                                                           //
1993                                 true,                                                                                                               //
1994                                 false,                                                                                                              //
1995                                 510,                                                                                                                //
1996                                 {false, 0.f},                                                                                                       //
1997                                 plotdir + "/" + outstem + "_hDCA3D_SilSeed_comparison"                                                              //
1998             );
1999             // delete hDCA3D_Sub;
2000         }
2001     }
2002 
2003     if (hSilSeedDCA3D)
2004         draw1Dhistogram(hSilSeedDCA3D, true, "DCA_{Silicon seeds}^{w.r.t vertex} [cm]", "Counts", {sample_label}, "hist e1", plotdir + "/" + outstem + "_hSilSeedDCA3D");
2005 
2006     if (hSilSeedDCA3D_truthVtxZ_2D)
2007     {
2008         const double heatmap_zmax = std::max(0.05, hSilSeedDCA3D_truthVtxZ_2D->GetMaximum() * 1.1);
2009         hSilSeedDCA3D_truthVtxZ_2D->GetYaxis()->SetRangeUser(0, 3);
2010         draw2Dhistogram_adjZaxis(hSilSeedDCA3D_truthVtxZ_2D,
2011                                  true,
2012                                  "Truth vertex z [cm]",
2013                                  "DCA_{Silicon seeds}^{w.r.t vertex} [cm]",
2014                                  "Normalized entries",
2015                                  std::pair<double, double>(0.0, heatmap_zmax),
2016                                  {sample_label},
2017                                  "colz",
2018                                  ".3g",
2019                                  false,
2020                                  false,
2021                                  plotdir + "/" + outstem + "_hSilSeedDCA3D_truthVtxZ_2D");
2022     }
2023 
2024     if (!hSilSeedDCA3D_truthVtxZ_absMerged.empty())
2025     {
2026         std::vector<TH1 *> hists;
2027         std::vector<std::string> labels;
2028         hists.reserve(hSilSeedDCA3D_truthVtxZ_absMerged.size());
2029         labels.reserve(hSilSeedDCA3D_truthVtxZ_absMerged.size());
2030         for (const auto &entry : hSilSeedDCA3D_truthVtxZ_absMerged)
2031         {
2032             hists.push_back(entry.first);
2033             labels.push_back(entry.second);
2034         }
2035 
2036         draw_comparison(hists,
2037                         {"#4477aa", "#228833", "#ccbb44", "#ee8866", "#cc3311"},
2038                         labels,
2039                         "DCA_{Silicon seeds}^{w.r.t vertex} [cm]",
2040                         "Normalized counts",
2041                         {-1, -1},
2042                         true,
2043                         true,
2044                         510,
2045                         std::make_pair(false, 0.0f),
2046                         plotdir + "/" + outstem + "_hSilSeedDCA3D_truthVtxZAbs_comparison");
2047     }
2048 
2049     // Always produce nraw / silicon-seed ratio.
2050     TH2D *h2_ratio_nraw_over_silseed = build_and_draw_ratio(h2_nraw, hSilSeed,                                     //
2051                                                             prefix + "_NRawOverSilSeedEtaPhi",                     //
2052                                                             "N_{Uncorrected}^{INTT Doublets} / N_{Silicon seeds}", //
2053                                                             "_ratio_nraw_over_silseed");
2054     TH2D *h2_ratio_nraw_over_silseed_good = build_and_draw_ratio(h2_nraw, hSilSeed_good,                                      //
2055                                                                  prefix + "_NRawOverSilSeedGoodEtaPhi",                             //
2056                                                                  "N_{Uncorrected}^{INTT Doublets} / N_{Silicon seeds}^{good}",     //
2057                                                                  "_ratio_nraw_over_silseed_good");
2058 
2059     ratio_outputs.eta_ratio = build_projection_ratio(std::make_pair(h2_nraw, "N_{Uncorrected}^{INTT Doublets}"), //
2060                                                      std::make_pair(hSilSeed, "N_{Silicon seeds}"),              //
2061                                                      true,                                                       //
2062                                                      "#eta",                                                     //
2063                                                      "N_{Uncorrected}^{INTT Doublets} / N_{Silicon seeds}",      //
2064                                                      "_ratio1D_nraw_over_silseed_eta",                           //
2065                                                      prefix + "_NRawOverSilSeedEtaProjection"                    //
2066     );
2067     ratio_outputs.phi_ratio = build_projection_ratio(std::make_pair(h2_nraw, "N_{Uncorrected}^{INTT Doublets}"), //
2068                                                      std::make_pair(hSilSeed, "N_{Silicon seeds}"),              //
2069                                                      false,                                                      //
2070                                                      "#phi [radian]",                                            //
2071                                                      "N_{Uncorrected}^{INTT Doublets} / N_{Silicon seeds}",      //
2072                                                      "_ratio1D_nraw_over_silseed_phi",                           //
2073                                                      prefix + "_NRawOverSilSeedPhiProjection"                    //
2074     );
2075     ratio_outputs.eta_ratio_good = build_projection_ratio(std::make_pair(h2_nraw, "N_{Uncorrected}^{INTT Doublets}"), //
2076                                                           std::make_pair(hSilSeed_good, "N_{Silicon seeds}^{good}"),   //
2077                                                           true,                                                        //
2078                                                           "#eta",                                                      //
2079                                                           "N_{Uncorrected}^{INTT Doublets} / N_{Silicon seeds}^{good}", //
2080                                                           "_ratio1D_nraw_over_silseed_good_eta",                       //
2081                                                           prefix + "_NRawOverSilSeedGoodEtaProjection"                 //
2082     );
2083     ratio_outputs.phi_ratio_good = build_projection_ratio(std::make_pair(h2_nraw, "N_{Uncorrected}^{INTT Doublets}"), //
2084                                                           std::make_pair(hSilSeed_good, "N_{Silicon seeds}^{good}"),   //
2085                                                           false,                                                       //
2086                                                           "#phi [radian]",                                             //
2087                                                           "N_{Uncorrected}^{INTT Doublets} / N_{Silicon seeds}^{good}", //
2088                                                           "_ratio1D_nraw_over_silseed_good_phi",                       //
2089                                                           prefix + "_NRawOverSilSeedGoodPhiProjection"                 //
2090     );
2091 
2092 
2093     // In simulation, add extra ratios: primary charged / nraw and primary charged / silicon-seed.
2094     TH2D *h2_ratio_primary_over_nraw = nullptr;
2095     TH2D *h2_ratio_primary_over_silseed = nullptr;
2096     if (is_simulation)
2097     {
2098         h2_ratio_primary_over_nraw = build_and_draw_ratio(hPrimaryCharged, h2_nraw,                                //
2099                                                           prefix + "_PrimaryChargedOverNRawEtaPhi",                //
2100                                                           "N_{sPHENIX primary charged} / N_{raw}^{INTT Doublets}", //
2101                                                           "_ratio_primarycharged_over_nraw");
2102         h2_ratio_primary_over_silseed = build_and_draw_ratio(hPrimaryCharged, hSilSeed,                         //
2103                                                              prefix + "_PrimaryChargedOverSilSeedEtaPhi",       //
2104                                                              "N_{sPHENIX primary charged} / N_{Silicon seeds}", //
2105                                                              "_ratio_primarycharged_over_silseed");
2106     }
2107 
2108     TH2D *h2_ratio_silseed_nMVTX3nINTT2_over_inclusive = build_and_draw_ratio(hSilSeed_nMVTX3nINTT2, hSilSeed,                        //
2109                                                                               prefix + "_SilSeed_nMVTX3nINTT2_OverInclusive",         //
2110                                                                               "N_{SilSeed}^{nMVTX3nINTT2} / N_{SilSeed}^{Inclusive}", //
2111                                                                               "_ratio_silseed_nMVTX3nINTT2_over_inclusive",           //
2112                                                                               false,                                                  //
2113                                                                               ".2g");
2114     TH2D *h2_ratio_silseed_nMVTX3nINTT1_over_inclusive = build_and_draw_ratio(hSilSeed_nMVTX3nINTT1, hSilSeed,                        //
2115                                                                               prefix + "_SilSeed_nMVTX3nINTT1_OverInclusive",         //
2116                                                                               "N_{SilSeed}^{nMVTX3nINTT1} / N_{SilSeed}^{Inclusive}", //
2117                                                                               "_ratio_silseed_nMVTX3nINTT1_over_inclusive",           //
2118                                                                               false,                                                  //
2119                                                                               ".2g");
2120     TH2D *h2_ratio_silseed_nMVTX2nINTT2_over_inclusive = build_and_draw_ratio(hSilSeed_nMVTX2nINTT2, hSilSeed,                        //
2121                                                                               prefix + "_SilSeed_nMVTX2nINTT2_OverInclusive",         //
2122                                                                               "N_{SilSeed}^{nMVTX2nINTT2} / N_{SilSeed}^{Inclusive}", //
2123                                                                               "_ratio_silseed_nMVTX2nINTT2_over_inclusive",           //
2124                                                                               false,                                                  //
2125                                                                               ".2g");
2126     TH2D *h2_ratio_silseed_nMVTX2nINTT1_over_inclusive = build_and_draw_ratio(hSilSeed_nMVTX2nINTT1, hSilSeed,                        //
2127                                                                               prefix + "_SilSeed_nMVTX2nINTT1_OverInclusive",         //
2128                                                                               "N_{SilSeed}^{nMVTX2nINTT1} / N_{SilSeed}^{Inclusive}", //
2129                                                                               "_ratio_silseed_nMVTX2nINTT1_over_inclusive",           //
2130                                                                               false,                                                  //
2131                                                                               ".2g");
2132     TH2D *h2_ratio_silseed_nMVTXnINTTOther_over_inclusive = build_and_draw_ratio(hSilSeed_nMVTXnINTTOther, hSilSeed,                        //
2133                                                                                  prefix + "_SilSeed_nMVTXnINTTOther_OverInclusive",         //
2134                                                                                  "N_{SilSeed}^{nMVTXnINTTOther} / N_{SilSeed}^{Inclusive}", //
2135                                                                                  "_ratio_silseed_nMVTXnINTTOther_over_inclusive",           //
2136                                                                                  false,                                                     //
2137                                                                                  ".2g");
2138 
2139     c->SaveAs((plotdir + "/" + outstem + ".pdf").c_str());
2140     // c->SaveAs((plotdir + "/" + outstem + ".png").c_str()); // somehow this take a really long time, skip for now
2141 
2142     // Safe to clean up now that the canvas has been saved.
2143     for (auto *f : frames)
2144         delete f;
2145     for (auto *ln : cutLines)
2146         delete ln;
2147     delete h2_ratio_nraw_over_silseed;
2148     delete h2_ratio_nraw_over_silseed_good;
2149     delete h2_ratio_silseed_nMVTX3nINTT2_over_inclusive;
2150     delete h2_ratio_silseed_nMVTX3nINTT1_over_inclusive;
2151     delete h2_ratio_silseed_nMVTX2nINTT2_over_inclusive;
2152     delete h2_ratio_silseed_nMVTX2nINTT1_over_inclusive;
2153     delete h2_ratio_silseed_nMVTXnINTTOther_over_inclusive;
2154     if (h2_ratio_primary_over_nraw)
2155         delete h2_ratio_primary_over_nraw;
2156     if (h2_ratio_primary_over_silseed)
2157         delete h2_ratio_primary_over_silseed;
2158     delete hSilSeedDCA3D_truthVtxZ_2D;
2159     for (auto &entry : hSilSeedDCA3D_truthVtxZ_absMerged)
2160         delete entry.first;
2161     delete hSilSeed_good;
2162     delete h2_nraw;
2163     fin->Close();
2164     delete c;
2165     return ratio_outputs;
2166 }
2167 
2168 // void plot_tklCombinatoric(const std::string &inputfile = "test_OO_combinatoric.root",     //
2169 //                           const std::string &prefix = "tkl_Combinatoric",                 //
2170 //                           const std::string &mode = "absdPhi",                            //
2171 //                           bool is_simulation = false,                                     //
2172 //                           double zvtx_cut_min = -10.0,                                    //
2173 //                           double zvtx_cut_max = 10.0,                                     //
2174 //                           double raw_count_xmin = 0.0,                                    //
2175 //                           double raw_count_xmax = kDirectRawCountCut,                     //
2176 //                           double plot_xmin = 0.0,                                         //
2177 //                           double plot_xmax = 0.2,                                         //
2178 //                           const std::string &outstem = "plot_tklCombinatoric",            //
2179 //                           const std::string &plotdir = "./figure/figure-tklCombinatoric", //
2180 //                           bool zero_x_error = true,                                       //
2181 //                           const std::string &ratio_draw_option = "colz text",             //
2182 //                           const std::string &ratio_text_format = ".3f",                   //
2183 //                           bool use_rounded_zmax = true                                    //
2184 // )
2185 void plot_tklCombinatoric(const std::string &inputfile = "/sphenix/tg/tg01/hf/hjheng/ppg-dNdEta-OOpp/TrackletAna-HistOutput/simulation/ACTS/cotThetaMax2p9/merged_hist.root", //
2186                           const std::string &prefix = "tkl_Combinatoric",                                                                                      //
2187                           const std::string &mode = "absdPhi",                                                                                                 //
2188                           bool is_simulation = true,                                                                                                           //
2189                           double zvtx_cut_min = -10.0,                                                                                                         //
2190                           double zvtx_cut_max = 10.0,                                                                                                          //
2191                           double raw_count_xmin = 0.0,                                                                                                         //
2192                           double raw_count_xmax = kDirectRawCountCut,                                                                                          //
2193                           double plot_xmin = 0.0,                                                                                                              //
2194                           double plot_xmax = 0.2,                                                                                                              //
2195                           const std::string &outstem = "plot_tklCombinatoric",                                                                                 //
2196                           const std::string &plotdir = "./figure/figure-tklCombinatoric-simulation-cotThetaMax2p9",                                                           //
2197                           bool zero_x_error = true,                                                                                                            //
2198                           const std::string &ratio_draw_option = "colz text",                                                                                  //
2199                           const std::string &ratio_text_format = ".3f",                                                                                        //
2200                           bool use_rounded_zmax = true                                                                                                         //
2201 )
2202 {
2203     TFile *fin = TFile::Open(inputfile.c_str(), "READ");
2204     if (!fin || fin->IsZombie())
2205     {
2206         std::cerr << "[plot_tklCombinatoric] Cannot open: " << inputfile << "\n";
2207         delete fin;
2208         return;
2209     }
2210 
2211     const std::vector<std::string> prefixes = CollectAvailablePrefixes(fin, prefix);
2212     const auto percentile_boundaries = LoadPercentileBoundariesByBin(is_simulation);
2213     fin->Close();
2214     delete fin;
2215 
2216     const std::vector<std::string> colors = {"#4477aa", "#228833", "#ccbb44", "#ee8866", "#cc3311", "#aa3377"};
2217     std::vector<ProjectionRatioOutputs> ratio_sets;
2218     ratio_sets.reserve(prefixes.size());
2219 
2220     for (const auto &current_prefix : prefixes)
2221     {
2222         const std::string suffix = PrefixSuffixForOutput(prefix, current_prefix);
2223         ProjectionRatioOutputs outputs = plot_tklCombinatoric_single(inputfile, current_prefix, mode, is_simulation, zvtx_cut_min, zvtx_cut_max, raw_count_xmin, raw_count_xmax, plot_xmin, plot_xmax, outstem + suffix, plotdir, zero_x_error, ratio_draw_option, ratio_text_format, use_rounded_zmax, PrefixDisplayLabel(prefix, current_prefix, percentile_boundaries));
2224         if (outputs.eta_ratio || outputs.phi_ratio || outputs.eta_ratio_good || outputs.phi_ratio_good)
2225             ratio_sets.push_back(outputs);
2226     }
2227 
2228     std::vector<TH1 *> eta_hists;
2229     std::vector<TH1 *> phi_hists;
2230     std::vector<TH1 *> eta_hists_good;
2231     std::vector<TH1 *> phi_hists_good;
2232     std::vector<TH1 *> eta_hists_inverse;
2233     std::vector<TH1 *> phi_hists_inverse;
2234     std::vector<TH1 *> eta_hists_good_inverse;
2235     std::vector<TH1 *> phi_hists_good_inverse;
2236     std::vector<std::string> labels;
2237     std::vector<std::string> labels_good;
2238     std::vector<std::string> comparison_colors;
2239     std::vector<std::string> comparison_colors_good;
2240     for (size_t i = 0; i < ratio_sets.size(); ++i)
2241     {
2242         if (!ratio_sets[i].eta_ratio || !ratio_sets[i].phi_ratio)
2243             continue;
2244         eta_hists.push_back(ratio_sets[i].eta_ratio);
2245         phi_hists.push_back(ratio_sets[i].phi_ratio);
2246         if (TH1D *hInv = MakeInverseHist(ratio_sets[i].eta_ratio, ratio_sets[i].prefix + "_NRawOverSilSeedEtaProjection_inverse"))
2247             eta_hists_inverse.push_back(hInv);
2248         if (TH1D *hInv = MakeInverseHist(ratio_sets[i].phi_ratio, ratio_sets[i].prefix + "_NRawOverSilSeedPhiProjection_inverse"))
2249             phi_hists_inverse.push_back(hInv);
2250         if (ratio_sets[i].eta_ratio_good && ratio_sets[i].phi_ratio_good)
2251         {
2252             eta_hists_good.push_back(ratio_sets[i].eta_ratio_good);
2253             phi_hists_good.push_back(ratio_sets[i].phi_ratio_good);
2254             if (TH1D *hInv = MakeInverseHist(ratio_sets[i].eta_ratio_good, ratio_sets[i].prefix + "_NRawOverSilSeedGoodEtaProjection_inverse"))
2255                 eta_hists_good_inverse.push_back(hInv);
2256             if (TH1D *hInv = MakeInverseHist(ratio_sets[i].phi_ratio_good, ratio_sets[i].prefix + "_NRawOverSilSeedGoodPhiProjection_inverse"))
2257                 phi_hists_good_inverse.push_back(hInv);
2258             labels_good.push_back(ratio_sets[i].label);
2259             comparison_colors_good.push_back((i == 0) ? "#000000" : colors[(i - 1) % colors.size()]);
2260         }
2261         labels.push_back(ratio_sets[i].label);
2262         comparison_colors.push_back((i == 0) ? "#000000" : colors[(i - 1) % colors.size()]);
2263     }
2264 
2265     if (!eta_hists.empty())
2266     {
2267         draw_comparison(eta_hists,                                                         //
2268                         comparison_colors,                                                 //
2269                         labels,                                                            //
2270                         "#eta",                                                            //
2271                         "N_{Uncorrected}^{INTT Doublets}/N_{Silicon seeds}",               //
2272                         {0, (!is_simulation) ? 2 : 3},                                     //
2273                         false,                                                             //
2274                         false,                                                             //
2275                         510,                                                               //
2276                         std::make_pair(true, 1.0),                                         //
2277                         plotdir + "/" + outstem + "_ratio1D_nraw_over_seed_eta_comparison" //
2278         );
2279 
2280         draw_comparison(eta_hists_inverse,                                                  //
2281                         comparison_colors,                                                  //
2282                         labels,                                                             //
2283                         "#eta",                                                             //
2284                         "N_{Silicon seeds}/N_{Uncorrected}^{INTT Doublets}",               //
2285                         {0, (!is_simulation) ? 5 : 2},                                                           //
2286                         false,                                                              //
2287                         false,                                                              //
2288                         510,                                                                //
2289                         std::make_pair(true, 1.0),                                          //
2290                         plotdir + "/" + outstem + "_ratio1D_seed_over_nraw_eta_comparison" //
2291         );
2292     }
2293 
2294     if (!phi_hists.empty())
2295     {
2296         draw_comparison(phi_hists,                                                         //
2297                         comparison_colors,                                                 //
2298                         labels,                                                            //
2299                         "#phi [radian]",                                                  //
2300                         "N_{Uncorrected}^{INTT Doublets}/N_{Silicon seeds}",              //
2301                         {0, (!is_simulation) ? 5 : 1.7},                                   //
2302                         false,                                                             //
2303                         false,                                                             //
2304                         510,                                                               //
2305                         std::make_pair(true, 1.0),                                         //
2306                         plotdir + "/" + outstem + "_ratio1D_nraw_over_seed_phi_comparison" //
2307         );
2308 
2309         draw_comparison(phi_hists_inverse,                                                  //
2310                         comparison_colors,                                                  //
2311                         labels,                                                             //
2312                         "#phi [radian]",                                                  //
2313                         "N_{Silicon seeds}/N_{Uncorrected}^{INTT Doublets}",              //
2314                         {0, (!is_simulation) ? 5 : 2},                                                           //
2315                         false,                                                              //
2316                         false,                                                              //
2317                         510,                                                                //
2318                         std::make_pair(true, 1.0),                                          //
2319                         plotdir + "/" + outstem + "_ratio1D_seed_over_nraw_phi_comparison" //
2320         );
2321     }
2322 
2323     if (!eta_hists_good.empty())
2324     {
2325         draw_comparison(eta_hists_good,                                                          //
2326                         comparison_colors_good,                                                   //
2327                         labels_good,                                                              //
2328                         "#eta",                                                                  //
2329                         "N_{Uncorrected}^{INTT Doublets}/N_{Silicon seeds}^{good}",             //
2330                         {0, (!is_simulation) ? 2 : 3},                                            //
2331                         false,                                                                    //
2332                         false,                                                                    //
2333                         510,                                                                      //
2334                         std::make_pair(true, 1.0),                                                //
2335                         plotdir + "/" + outstem + "_ratio1D_nraw_over_seed_good_eta_comparison" //
2336         );
2337 
2338         draw_comparison(eta_hists_good_inverse,                                                    //
2339                         comparison_colors_good,                                                    //
2340                         labels_good,                                                               //
2341                         "#eta",                                                                   //
2342                         "N_{Silicon seeds}^{good}/N_{Uncorrected}^{INTT Doublets}",              //
2343                         {0, (!is_simulation) ? 2 : 2},                                                                  //
2344                         false,                                                                     //
2345                         false,                                                                     //
2346                         510,                                                                       //
2347                         std::make_pair(true, 1.0),                                                 //
2348                         plotdir + "/" + outstem + "_ratio1D_seed_good_over_nraw_eta_comparison" //
2349         );
2350     }
2351 
2352     if (!phi_hists_good.empty())
2353     {
2354         draw_comparison(phi_hists_good,                                                          //
2355                         comparison_colors_good,                                                   //
2356                         labels_good,                                                              //
2357                         "#phi [radian]",                                                        //
2358                         "N_{Uncorrected}^{INTT Doublets}/N_{Silicon seeds}^{good}",             //
2359                         {0, (!is_simulation) ? 5 : 1.7},                                          //
2360                         false,                                                                    //
2361                         false,                                                                    //
2362                         510,                                                                      //
2363                         std::make_pair(true, 1.0),                                                //
2364                         plotdir + "/" + outstem + "_ratio1D_nraw_over_seed_good_phi_comparison" //
2365         );
2366 
2367         draw_comparison(phi_hists_good_inverse,                                                    //
2368                         comparison_colors_good,                                                    //
2369                         labels_good,                                                               //
2370                         "#phi [radian]",                                                         //
2371                         "N_{Silicon seeds}^{good}/N_{Uncorrected}^{INTT Doublets}",              //
2372                         {0, (!is_simulation) ? 5 : 2},                                                                  //
2373                         false,                                                                     //
2374                         false,                                                                     //
2375                         510,                                                                       //
2376                         std::make_pair(true, 1.0),                                                 //
2377                         plotdir + "/" + outstem + "_ratio1D_seed_good_over_nraw_phi_comparison" //
2378         );
2379     }
2380 
2381     for (auto &outputs : ratio_sets)
2382     {
2383         delete outputs.eta_ratio;
2384         delete outputs.phi_ratio;
2385         delete outputs.eta_ratio_good;
2386         delete outputs.phi_ratio_good;
2387     }
2388 
2389     for (auto *hist : eta_hists_inverse)
2390         delete hist;
2391     for (auto *hist : phi_hists_inverse)
2392         delete hist;
2393     for (auto *hist : eta_hists_good_inverse)
2394         delete hist;
2395     for (auto *hist : phi_hists_good_inverse)
2396         delete hist;
2397 }