Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:12:32

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2019-2021 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #include <bitset>
0010 #include <fstream>
0011 #include <iostream>
0012 #include <limits>
0013 #include <map>
0014 #include <string>
0015 #include <vector>
0016 
0017 #include <TCanvas.h>
0018 #include <TChain.h>
0019 #include <TF1.h>
0020 #include <TFile.h>
0021 #include <TH1F.h>
0022 #include <TH2F.h>
0023 #include <TH3F.h>
0024 #include <TLegend.h>
0025 #include <TMath.h>
0026 #include <TStyle.h>
0027 #include <TTree.h>
0028 #include <TVectorF.h>
0029 
0030 #include "CommonUtils.h"
0031 #include "TreeReader.h"
0032 
0033 using namespace ROOT;
0034 
0035 /// This ROOT script will plot the residual and pull of perigee track parameters
0036 /// (d0, z0, phi, theta, q/p, pT, t) from root file produced by the
0037 /// RootTrackSummaryWriter
0038 ///
0039 /// @param inFiles the list of input files
0040 /// @param inTree the name of the input tree
0041 /// @param outFile the name of the output file
0042 /// @param inConfig the (optional) input configuration JSON file
0043 /// @param outConfig the (optional) output configuration JSON file
0044 /// @param residualPulls the bitset of the parameters set
0045 ///
0046 ///
0047 /// @note A lot of this could be done with creating appropriate TH3F histograms
0048 /// and chose the relevant projections, but this would need one loop through the
0049 /// entries per parameter and would create a very long analysis turn-around.
0050 /// That's why the data/histograms are prepared in handles and then filled in a
0051 /// single event analysis loop.
0052 ///
0053 int trackSummaryAnalysis(
0054     const std::vector<std::string>& inFiles, const std::string& inTree,
0055     const std::string& outFile, const std::string& inConfig = "",
0056     const std::string& outConfig = "", unsigned long nEntries = 0,
0057     unsigned int nPeakEntries = 0, float pullRange = 6.,
0058     unsigned int nHistBins = 61, unsigned int nPhiBins = 10,
0059     const std::array<float, 2>& phiRange = {-M_PI, M_PI},
0060     unsigned int nEtaBins = 10, const std::array<float, 2>& etaRange = {-3, 3},
0061     const std::vector<double>& ptBorders =
0062         {0., std::numeric_limits<double>::infinity()},
0063     const std::bitset<7>& residualPulls = std::bitset<7>{"1111111"},
0064     const std::bitset<5>& auxiliary = std::bitset<5>{"11111"}) {
0065   // Load the tree chain
0066   TChain* treeChain = new TChain(inTree.c_str());
0067   for (const auto& inFile : inFiles) {
0068     treeChain->Add(inFile.c_str());
0069     // Open root file written by RootTrackWriter
0070     std::cout << "*** Adding file: " << inFile << std::endl;
0071   }
0072 
0073   if (treeChain->GetEntries() == 0) {
0074     std::cout << "xxx No entries found ... bailing out." << std::endl;
0075     return -1;
0076   }
0077 
0078   TCanvas* rangeCanvas =
0079       new TCanvas("rangeCanvas", "Range Estimation", 100, 100, 620, 400);
0080 
0081   TrackSummaryReader tracks(treeChain, true);
0082 
0083   // Loop over the entries of the keys
0084   unsigned long entries = estimateEntries(*tracks.tree, nEntries);
0085   unsigned long peakEntries = estimateEntries(*tracks.tree, nPeakEntries);
0086 
0087   // Temporary counter
0088   unsigned int histBarcode = 0;
0089 
0090   // Deduction
0091   unsigned int nPtBins = ptBorders.size() - 1;
0092 
0093   // One time initialization of the residual and pull handles
0094   using ResidualPulls = std::vector<ResidualPullHandle>;
0095   ResidualPulls baseResidualPulls = {};
0096 
0097   if (residualPulls.test(0)) {
0098     // A standard d0Handle
0099     ResidualPullHandle d0Handle;
0100     d0Handle.tag = "d0";
0101     d0Handle.residualStr = "d_{0}^{rec} - d_{0}^{true}";
0102     d0Handle.residualUnit = "[mm]";
0103     d0Handle.errorStr = "#sigma(d_{0})";
0104     d0Handle.rangeDrawStr = "eLOC0_fit-t_d0";
0105     d0Handle.rangeMaxStr = "(1000,-10,10)";
0106     d0Handle.value = ResidualAccessor{tracks.eLOC0_fit, tracks.t_d0};
0107     d0Handle.error = DirectAccessor<float>{tracks.err_eLOC0_fit};
0108     d0Handle.accept = AcceptAll{};
0109     // Push it
0110     baseResidualPulls.push_back(d0Handle);
0111   }
0112 
0113   if (residualPulls.test(1)) {
0114     // A standard z0Handle
0115     ResidualPullHandle z0Handle;
0116     z0Handle.tag = "z0";
0117     z0Handle.residualStr = "z_{0}^{rec} - z_{0}^{true}";
0118     z0Handle.residualUnit = "[mm]";
0119     z0Handle.errorStr = "#sigma(z_{0})";
0120     z0Handle.rangeDrawStr = "eLOC1_fit-t_z0";
0121     z0Handle.rangeMaxStr = "(1000,-10,10)";
0122     z0Handle.value = ResidualAccessor{tracks.eLOC1_fit, tracks.t_z0};
0123     z0Handle.error = DirectAccessor<float>{tracks.err_eLOC1_fit};
0124     z0Handle.accept = AcceptAll{};
0125     // Push it
0126     baseResidualPulls.push_back(z0Handle);
0127   }
0128 
0129   if (residualPulls.test(2)) {
0130     // A standard phi0Handle
0131     ResidualPullHandle phi0Handle;
0132     phi0Handle.tag = "phi0";
0133     phi0Handle.residualStr = "#phi_{0}^{rec} - #phi_{0}^{true}";
0134     phi0Handle.errorStr = "#sigma(phi_{0})";
0135     phi0Handle.rangeDrawStr = "ePHI_fit-t_phi";
0136     phi0Handle.rangeMaxStr = "(1000,-0.01,0.01)";
0137     phi0Handle.value = ResidualAccessor{tracks.ePHI_fit, tracks.t_phi};
0138     phi0Handle.error = DirectAccessor<float>{tracks.err_ePHI_fit};
0139     phi0Handle.accept = AcceptAll{};
0140     // Push it
0141     baseResidualPulls.push_back(phi0Handle);
0142   }
0143 
0144   if (residualPulls.test(3)) {
0145     // A standard theta0Handle
0146     ResidualPullHandle theta0Handle;
0147     theta0Handle.tag = "theta0";
0148     theta0Handle.residualStr = "#theta_{0}^{rec} - #theta_{0}^{true}";
0149     theta0Handle.errorStr = "#sigma(theta_{0})";
0150     theta0Handle.rangeDrawStr = "eTHETA_fit-t_theta";
0151     theta0Handle.rangeMaxStr = "(1000,-0.01,0.01)";
0152     theta0Handle.value = ResidualAccessor{tracks.eTHETA_fit, tracks.t_theta};
0153     theta0Handle.error = DirectAccessor<float>{tracks.err_eTHETA_fit};
0154     theta0Handle.accept = AcceptAll{};
0155     // Push it
0156     baseResidualPulls.push_back(theta0Handle);
0157   }
0158 
0159   if (residualPulls.test(4)) {
0160     // The standard qop Handle
0161     ResidualPullHandle qopHandle;
0162     qopHandle.tag = "qop";
0163     qopHandle.residualStr = "q/p^{rec} - q/p^{true}";
0164     qopHandle.residualUnit = "[GeV^{-1}]";
0165     qopHandle.errorStr = "#sigma(q/p)";
0166     qopHandle.rangeDrawStr = "eQOP_fit-t_charge/t_p";
0167     qopHandle.rangeMaxStr = "(1000,-0.1,0.1)";
0168     qopHandle.value =
0169         QopResidualAccessor{tracks.eQOP_fit, tracks.t_charge, tracks.t_p};
0170     qopHandle.error = DirectAccessor<float>{tracks.err_eQOP_fit};
0171     qopHandle.accept = AcceptAll{};
0172     // Push it
0173     baseResidualPulls.push_back(qopHandle);
0174   }
0175 
0176   if (residualPulls.test(5)) {
0177     // The pt measurement
0178     ResidualPullHandle tHandle;
0179     tHandle.tag = "t";
0180     tHandle.residualStr = "t^{rec} - t^{true}";
0181     tHandle.residualUnit = "[ns]";
0182     tHandle.errorStr = "#sigma(t})";
0183     tHandle.rangeDrawStr = "eT_fit - t_time";
0184     tHandle.rangeMaxStr = "(1000,-10.,10.)";
0185     tHandle.value = ResidualAccessor{tracks.eT_fit, tracks.t_time};
0186     tHandle.error = DirectAccessor<float>{tracks.err_eT_fit};
0187     tHandle.accept = AcceptAll{};
0188     // Push it
0189     baseResidualPulls.push_back(tHandle);
0190   }
0191 
0192   if (residualPulls.test(6)) {
0193     // The pt measurement
0194     ResidualPullHandle ptHandle;
0195     ptHandle.tag = "pt";
0196     ptHandle.residualStr = "p_{T}^{rec} - p_{T}^{true}";
0197     ptHandle.residualUnit = "[GeV]";
0198     ptHandle.errorStr = "#sigma(p_{T})";
0199     ptHandle.rangeDrawStr = "1./abs(eQOP_fit) * sin(eTHETA_fit) - (1./t_pT)";
0200     ptHandle.rangeMaxStr = "(1000,-10.,10.)";
0201     ptHandle.value =
0202         PtResidualAccessor{tracks.eQOP_fit, tracks.eTHETA_fit, tracks.t_pT};
0203     ptHandle.error = PtErrorAccessor{tracks.eQOP_fit, tracks.err_eQOP_fit,
0204                                      tracks.eTHETA_fit, tracks.err_eTHETA_fit};
0205     ptHandle.accept = AcceptAll{};
0206     // Push it
0207     baseResidualPulls.push_back(ptHandle);
0208   }
0209 
0210   using Auxiliaries = std::vector<SingleHandle>;
0211   Auxiliaries baseAuxilaries;
0212 
0213   if (auxiliary.test(0)) {
0214     // Chi2/ndf
0215     SingleHandle chi2ndf;
0216     chi2ndf.tag = "chi2ndf";
0217     chi2ndf.label = "#Chi^{2}/ndf";
0218     chi2ndf.bins = nHistBins;
0219     chi2ndf.range = {0., 5.};
0220     chi2ndf.value =
0221         DivisionAccessor<float, unsigned int>{tracks.chi2Sum, tracks.NDF};
0222     chi2ndf.accept = AcceptAll{};
0223     // Push it
0224     baseAuxilaries.push_back(chi2ndf);
0225   }
0226 
0227   if (auxiliary.test(1)) {
0228     // Measurements
0229     SingleHandle measurements;
0230     measurements.tag = "measurements";
0231     measurements.label = "#(measurements)";
0232     measurements.rangeDrawStr = "nMeasurements";
0233     measurements.value = DirectAccessor<unsigned int>{tracks.nMeasurements};
0234     measurements.accept = AcceptAll{};
0235     estimateIntegerRange(measurements, *rangeCanvas, *tracks.tree, peakEntries,
0236                          250, 5, ++histBarcode);
0237     // Push it
0238     baseAuxilaries.push_back(measurements);
0239   }
0240 
0241   if (auxiliary.test(2)) {
0242     // Holes
0243     SingleHandle holes;
0244     holes.tag = "holes";
0245     holes.label = "#(holes)";
0246     holes.rangeDrawStr = "nHoles";
0247     holes.value = DirectAccessor<unsigned int>{tracks.nHoles};
0248     holes.accept = AcceptAll{};
0249     estimateIntegerRange(holes, *rangeCanvas, *tracks.tree, peakEntries, 10, 2,
0250                          ++histBarcode);
0251     // Push it
0252     baseAuxilaries.push_back(holes);
0253   }
0254 
0255   if (auxiliary.test(3)) {
0256     // Holes
0257     SingleHandle outliers;
0258     outliers.tag = "outliers";
0259     outliers.label = "#(outliers)";
0260     outliers.rangeDrawStr = "nOutliers";
0261     outliers.value = DirectAccessor<unsigned int>{tracks.nOutliers};
0262     outliers.accept = AcceptAll{};
0263     estimateIntegerRange(outliers, *rangeCanvas, *tracks.tree, peakEntries, 10,
0264                          2, ++histBarcode);
0265     // Push it
0266     baseAuxilaries.push_back(outliers);
0267   }
0268 
0269   if (auxiliary.test(4)) {
0270     // Holes
0271     SingleHandle shared;
0272     shared.tag = "shared";
0273     shared.label = "#(shared)";
0274     shared.rangeDrawStr = "nSharedHits";
0275     shared.value = DirectAccessor<unsigned int>{tracks.nSharedHits};
0276     shared.accept = AcceptAll{};
0277     estimateIntegerRange(shared, *rangeCanvas, *tracks.tree, peakEntries, 10, 2,
0278                          ++histBarcode);
0279     // Push it
0280     baseAuxilaries.push_back(shared);
0281   }
0282 
0283   // Preparation phase for handles
0284 #ifdef NLOHMANN_AVAILABLE
0285   nlohmann::json handle_configs;
0286   if (! inConfig.empty()) {
0287     std::ifstream ifs(inConfig.c_str());
0288     handle_configs = nlohmann::json::parse(ifs);
0289   }
0290 
0291   /// Helper method to handle the range, it is either read in or estimated
0292   ///
0293   /// It attempts to read in from a json input, if that does not work,
0294   /// it will estimate it (time consuming)
0295   ///
0296   /// @param handle the parameter handle in question
0297   /// @param handleTag the unique tangle tag
0298   /// @param peakE the number of entries used for range peaking
0299   auto handleRange = [&](ResidualPullHandle& handle, const TString& handleTag, unsigned int peakE) -> void {
0300     bool rangeDetermined = false;
0301     if (! inConfig.empty()) {
0302       if (handle_configs.contains((handleTag).Data())) {
0303         auto handle_config = handle_configs[(handleTag).Data()];
0304         handle.range = handle_config["range"].get<std::array<float, 2>>();
0305         rangeDetermined = true;
0306       }
0307     }
0308     if (! rangeDetermined) {
0309       estimateResiudalRange(handle, *rangeCanvas, *tracks.tree, peakE,
0310                             ++histBarcode);
0311     }
0312 
0313     if (! outConfig.empty()) {
0314       nlohmann::json range_config;
0315       range_config["range"] = handle.range;
0316       handle_configs[(handleTag).Data()] = range_config;
0317     }
0318   };
0319 #else
0320   /// Helper method to handle range without possibility to read in
0321   ///
0322   /// @param handle the parameter handle in question
0323   /// @param handleTag the unique tangle tag
0324   /// @param peakEntries the number of entries used for range peaking
0325   auto handleRange = [&](ResidualPullHandle& handle, const TString& handleTag,
0326                          unsigned long peakEntries) -> void {
0327     estimateResiudalRange(handle, *rangeCanvas, *tracks.tree, peakEntries,
0328                           ++histBarcode);
0329   };
0330 #endif
0331 
0332   // Full Range handles - they accept all tracks
0333   ResidualPulls fullResidualPulls = baseResidualPulls;
0334   // Range Estimation and booking histogram
0335   for (auto& fHandle : fullResidualPulls) {
0336     // The full tag
0337     TString handleTag(fHandle.tag + std::string("_all"));
0338     handleRange(fHandle, handleTag, peakEntries);
0339     // Book histograms
0340     bookHistograms(fHandle, pullRange, nHistBins, ++histBarcode);
0341     // The Histogram names
0342     TString residualN = TString("res_") + handleTag;
0343     TString pullN = TString("pull_") + handleTag;
0344     // Style and name
0345     setHistStyle(fHandle.residualHist);
0346     fHandle.residualHist->SetName(residualN);
0347     setHistStyle(fHandle.pullHist);
0348     fHandle.pullHist->SetName(pullN);
0349   }
0350 
0351   // Regional/Binned  handles
0352   using ResidualPullsVector = std::vector<ResidualPulls>;
0353   using ResidualPullsMatrix = std::vector<ResidualPullsVector>;
0354 
0355   // Eta-Pt residual/pull handles
0356   ResidualPullsVector ptResidualPulls =
0357       ResidualPullsVector(nPtBins, baseResidualPulls);
0358   ResidualPullsMatrix etaPtResidualPulls =
0359       ResidualPullsMatrix(nEtaBins, ptResidualPulls);
0360 
0361   // Eta-Phi residual/pull handles
0362   ResidualPullsVector phiResidualPulls =
0363       ResidualPullsVector(nPhiBins, baseResidualPulls);
0364   ResidualPullsMatrix etaPhiResidualPulls =
0365       ResidualPullsMatrix(nEtaBins, phiResidualPulls);
0366 
0367   Auxiliaries fullAuxiliaries = baseAuxilaries;
0368 
0369   // Histogram booking for full range auxiliaries
0370   for (auto& fAuxiliary : fullAuxiliaries) {
0371     fAuxiliary.hist =
0372         new TH1F(fAuxiliary.tag.c_str(), fAuxiliary.tag.c_str(),
0373                  fAuxiliary.bins, fAuxiliary.range[0], fAuxiliary.range[1]);
0374     fAuxiliary.hist->GetXaxis()->SetTitle(fAuxiliary.label.c_str());
0375     fAuxiliary.hist->GetYaxis()->SetTitle("Entries");
0376     setHistStyle(fAuxiliary.hist);
0377   }
0378 
0379   using AuxiliariesVector = std::vector<Auxiliaries>;
0380   using AuxiliariesMatrix = std::vector<AuxiliariesVector>;
0381 
0382   // Eta-Pt auxiliaries
0383   AuxiliariesVector ptAuxiliaries = AuxiliariesVector(nPtBins, baseAuxilaries);
0384   AuxiliariesMatrix etaPtAuxiliaries =
0385       AuxiliariesMatrix(nEtaBins, ptAuxiliaries);
0386 
0387   // Eta-Phi auxiliaries
0388   AuxiliariesVector phiAuxiliaries =
0389       AuxiliariesVector(nPhiBins, baseAuxilaries);
0390   AuxiliariesMatrix etaPhiAuxiliaries =
0391       AuxiliariesMatrix(nEtaBins, phiAuxiliaries);
0392 
0393   // Loop over the binned handles & fill the acceptors
0394   float phiStep = (phiRange[1] - phiRange[0]) / nPhiBins;
0395   float etaStep = (etaRange[1] - etaRange[0]) / nEtaBins;
0396 
0397   TVectorF phiVals(nPhiBins + 1);
0398   TVectorF etaVals(nEtaBins + 1);
0399   TVectorF ptVals(nPtBins + 1);
0400 
0401   phiVals[0] = phiRange[0];
0402   etaVals[0] = etaRange[0];
0403   ptVals[0] = ptBorders[0];
0404 
0405 #ifdef BOOST_AVAILABLE
0406   std::cout << "*** Handle Preparation: " << std::endl;
0407   progress_display handle_preparation_progress(
0408       nPhiBins * nEtaBins * nPtBins * baseResidualPulls.size());
0409 #endif
0410 
0411   std::string land = " && ";
0412 
0413   /// Preparation of handles / acceptance range
0414   for (unsigned int iphi = 0; iphi < nPhiBins; ++iphi) {
0415     // Prepare the phi range for this batch
0416     float phiMin = phiRange[0] + iphi * phiStep;
0417     float phiMax = phiRange[0] + (iphi + 1) * phiStep;
0418     phiVals[iphi + 1] = phiMax;
0419 
0420     // Acceptance range
0421     AcceptRange phiBin{tracks.t_phi, {phiMin, phiMax}};
0422     // Name tag
0423     TString phiTag = "_phi";
0424     phiTag += iphi;
0425     // Range cut string
0426     std::string phiCut = "t_phi >= ";
0427     phiCut += std::to_string(phiMin);
0428     phiCut += land;
0429     phiCut += std::string("t_phi < ");
0430     phiCut += std::to_string(phiMax);
0431 
0432     for (unsigned int ieta = 0; ieta < nEtaBins; ++ieta) {
0433       // Prepare the eta ragne for this batch
0434       float etaMin = etaRange[0] + ieta * etaStep;
0435       float etaMax = etaRange[0] + (ieta + 1) * etaStep;
0436       etaVals[ieta + 1] = etaMax;
0437       // Acceptance range
0438       AcceptRange etaBin{tracks.t_eta, {etaMin, etaMax}};
0439       // Name tag
0440       TString etaTag = "_eta";
0441       etaTag += ieta;
0442       // Range cut string
0443       std::string etaCut = "t_eta >= ";
0444       etaCut += std::to_string(etaMin);
0445       etaCut += land;
0446       etaCut += std::string("t_eta < ");
0447       etaCut += std::to_string(etaMax);
0448 
0449       // Combined eta/phi acceptance
0450       AcceptCombination etaPhiBin{etaBin, phiBin};
0451       TString etaPhiTag = etaTag + phiTag;
0452 
0453       for (unsigned int ipt = 0; ipt < nPtBins; ++ipt) {
0454         // Acceptance range for this pT bin
0455         float ptMin = static_cast<float>(ptBorders[ipt]);
0456         float ptMax = static_cast<float>(ptBorders[ipt + 1]);
0457         AcceptRange ptBin{tracks.t_pT, {ptMin, ptMax}};
0458 
0459         float upperPtBorder =
0460             ptBorders[ipt + 1] == std::numeric_limits<double>::infinity()
0461                 ? 100.
0462                 : ptBorders[ipt + 1];
0463         ptVals[ipt + 1] = upperPtBorder;
0464         // Name tag
0465         TString ptTag = "_pt";
0466         ptTag += ipt;
0467 
0468         // Range cut string
0469         std::string ptCut = "t_pT >= ";
0470         ptCut += std::to_string(ptMin);
0471         ptCut += land;
0472         ptCut += std::string("t_pT < ");
0473         ptCut += std::to_string(ptMax);
0474 
0475         // Combined eta/pt acceptance
0476         AcceptCombination etaPtBin{etaBin, ptBin};
0477 
0478         for (unsigned int iresp = 0; iresp < baseResidualPulls.size();
0479              ++iresp) {
0480           // Eta-Pt handles -- restrict for iphi == 0
0481           if (iphi == 0) {
0482             auto& etaPtHandle = etaPtResidualPulls[ieta][ipt][iresp];
0483             // Create the handle tag
0484             TString handleTag(etaPtHandle.tag + etaTag + ptTag);
0485             // Accept range and range cut
0486             etaPtHandle.accept = etaPtBin;
0487             etaPtHandle.rangeCutStr = ptCut + land + etaCut;
0488             handleRange(etaPtHandle, handleTag, peakEntries);
0489             bookHistograms(etaPtHandle, pullRange, nHistBins, ++histBarcode);
0490 
0491             // Set name and style/
0492             TString residualN = TString("res_") + handleTag;
0493             TString pullN = TString("pull_") + handleTag;
0494             // Range histogram does not exist when read in from configuration
0495             if (etaPtHandle.rangeHist != nullptr) {
0496               TString rangeN = TString("range_") + handleTag;
0497               setHistStyle(etaPtHandle.rangeHist);
0498               etaPtHandle.rangeHist->SetName(rangeN);
0499             }
0500             setHistStyle(etaPtHandle.residualHist);
0501             etaPtHandle.residualHist->SetName(residualN);
0502             setHistStyle(etaPtHandle.pullHist);
0503             etaPtHandle.pullHist->SetName(pullN);
0504           }
0505           // Eta-Phi handles --- restrice for ipt == 0
0506           if (ipt == 0) {
0507             auto& etaPhiHandle = etaPhiResidualPulls[ieta][iphi][iresp];
0508             // Create the handle tag
0509             TString handleTag(etaPhiHandle.tag + etaTag + phiTag);
0510             etaPhiHandle.accept = etaPhiBin;
0511             handleRange(etaPhiHandle, handleTag, peakEntries);
0512             bookHistograms(etaPhiHandle, pullRange, nHistBins, ++histBarcode);
0513 
0514             // Set name and style
0515             TString residualN = TString("res_") + handleTag;
0516             TString pullN = TString("pull_") + handleTag;
0517 
0518             // Range histogram does not exist when read in from configuration
0519             if (etaPhiHandle.rangeHist != nullptr) {
0520               TString rangeN = TString("range_") + handleTag;
0521               setHistStyle(etaPhiHandle.rangeHist);
0522               etaPhiHandle.rangeHist->SetName(rangeN);
0523             }
0524             setHistStyle(etaPhiHandle.residualHist);
0525             etaPhiHandle.residualHist->SetName(residualN);
0526             setHistStyle(etaPhiHandle.pullHist);
0527             etaPhiHandle.pullHist->SetName(pullN);
0528           }
0529 
0530 #ifdef BOOST_AVAILABLE
0531           ++handle_preparation_progress;
0532 #endif
0533         }
0534 
0535         // Auxiliary plots
0536         for (unsigned int iaux = 0; iaux < baseAuxilaries.size(); ++iaux) {
0537           // Eta-Pt handles - restrict to iphi == 0
0538           if (iphi == 0) {
0539             auto& etaPtAux = etaPtAuxiliaries[ieta][ipt][iaux];
0540             etaPtAux.accept = etaPtBin;
0541             TString handleTag(etaPtAux.tag + etaTag + ptTag);
0542             etaPtAux.hist =
0543                 new TH1F(handleTag.Data(), etaPtAux.tag.c_str(), etaPtAux.bins,
0544                          etaPtAux.range[0], etaPtAux.range[1]);
0545             etaPtAux.hist->GetXaxis()->SetTitle(etaPtAux.label.c_str());
0546             etaPtAux.hist->GetYaxis()->SetTitle("Entries");
0547             setHistStyle(etaPtAux.hist);
0548           }
0549 
0550           // Eta-Phi handles - restrict to ipt == 0
0551           if (ipt == 0) {
0552             auto& etaPhiAux = etaPhiAuxiliaries[ieta][iphi][iaux];
0553             etaPhiAux.accept = etaPhiBin;
0554             TString handleTag(etaPhiAux.tag + etaTag + phiTag);
0555             etaPhiAux.hist = new TH1F(handleTag.Data(), etaPhiAux.tag.c_str(),
0556                                       etaPhiAux.bins, etaPhiAux.range[0],
0557                                       etaPhiAux.range[1]);
0558             etaPhiAux.hist->GetXaxis()->SetTitle(etaPhiAux.label.c_str());
0559             etaPhiAux.hist->GetYaxis()->SetTitle("Entries");
0560             setHistStyle(etaPhiAux.hist);
0561           }
0562         }
0563       }
0564     }
0565   }
0566 
0567 #ifdef NLOHMANN_AVAILABLE
0568   if (! outConfig.empty()) {
0569     std::ofstream config_out;
0570     config_out.open(outConfig.c_str());
0571     config_out << handle_configs.dump(4);
0572   }
0573 #endif
0574 
0575 #ifdef BOOST_AVAILABLE
0576   std::cout << "*** Event Loop: " << std::endl;
0577   progress_display event_loop_progress(entries);
0578 #endif
0579 
0580   for (unsigned long ie = 0; ie < entries; ++ie) {
0581 #ifdef BOOST_AVAILABLE
0582     ++event_loop_progress;
0583 #endif
0584 
0585     // Make sure you have the entry
0586     tracks.tree->GetEntry(ie);
0587     std::size_t nTracks = tracks.hasFittedParams->size();
0588     for (std::size_t it = 0; it < nTracks; ++it) {
0589       if (tracks.hasFittedParams->at(it)) {
0590         // Residual handlesL
0591         // Full range handles
0592         for (auto& fHandle : fullResidualPulls) {
0593           fHandle.fill(it);
0594         }
0595 
0596         // Eta-Pt handles
0597         for (auto& etaBatch : etaPtResidualPulls) {
0598           for (auto& ptBatch : etaBatch) {
0599             for (auto& bHandle : ptBatch) {
0600               bHandle.fill(it);
0601             }
0602           }
0603         }
0604 
0605         // Eta-Phi handles
0606         for (auto& etaBatch : etaPhiResidualPulls) {
0607           for (auto& phiBatch : etaBatch) {
0608             for (auto& bHandle : phiBatch) {
0609               bHandle.fill(it);
0610             }
0611           }
0612         }
0613       }
0614 
0615       // Auxiliary handles:
0616       // Full range handles
0617       for (auto& fAuxiliary : fullAuxiliaries) {
0618         fAuxiliary.fill(it);
0619       }
0620 
0621       // Eta-Pt handles
0622       for (auto& etaBatch : etaPtAuxiliaries) {
0623         for (auto& ptBatch : etaBatch) {
0624           for (auto& bHandle : ptBatch) {
0625             bHandle.fill(it);
0626           }
0627         }
0628       }
0629 
0630       // Eta-Phi handles
0631       for (auto& etaBatch : etaPhiAuxiliaries) {
0632         for (auto& phiBatch : etaBatch) {
0633           for (auto& bHandle : phiBatch) {
0634             bHandle.fill(it);
0635           }
0636         }
0637       }
0638     }
0639   }
0640 
0641   // The output file section
0642   auto output = TFile::Open(outFile.c_str(), "recreate");
0643   output->cd();
0644 
0645   // Full range handles : residual and pulls
0646   for (auto& fHandle : fullResidualPulls) {
0647     if (fHandle.rangeHist != nullptr) {
0648       fHandle.rangeHist->Write();
0649     }
0650     fHandle.residualHist->Write();
0651     fHandle.pullHist->Write();
0652   }
0653 
0654   // Full range handles : auxiliaries
0655   for (auto& fAuxiliary : fullAuxiliaries) {
0656     fAuxiliary.hist->SetName(fAuxiliary.hist->GetName() + TString("_all"));
0657     fAuxiliary.hist->Write();
0658   }
0659 
0660   struct SummaryHistograms {
0661     TH2F* fillStats = nullptr;
0662 
0663     std::vector<TH2F*> residualRMS;
0664     std::vector<TH2F*> residualMean;
0665     std::vector<TH2F*> pullSigma;
0666     std::vector<TH2F*> pullMean;
0667 
0668     std::vector<TH2F*> auxiliaries;
0669   };
0670 
0671   /// Helper method to analyse bins
0672   /// @param residualPullsMatrix the 2D matrix of handles
0673   /// @param auxiliaryMatrix the 2D matrix of the auxiliary handles
0674   /// @param matrixTag the identification tag for the matrix
0675   /// @param outputBorders the border vector for the outer bins
0676   /// @param innerBorders the border vector for the inner bins
0677   /// @param fXTitle the title of the x axis of the first projection
0678   /// @param sXTitle the title of the x axis of the second projection
0679   ///
0680   /// @note this is a void function
0681   auto analyseBins = [&](ResidualPullsMatrix& residualPullsMatrix,
0682                          AuxiliariesMatrix& auxiliaryMatrix,
0683                          const TString& matrixTag, const TVectorF& outerBorders,
0684                          const TVectorF& innerBorders,
0685                          const TString& fXTitle = "#eta",
0686                          const TString& sXTitle = "#phi") -> void {
0687     // The summary histogram set
0688     SummaryHistograms summary;
0689 
0690     // 2D handles ---------------------------
0691     unsigned int nOuterBins = outerBorders.GetNrows() - 1;
0692     auto outerValues = outerBorders.GetMatrixArray();
0693     unsigned int nInnerBins = innerBorders.GetNrows() - 1;
0694     auto innerValues = innerBorders.GetMatrixArray();
0695 
0696     TString statN = TString("entries") + matrixTag;
0697     summary.fillStats =
0698         new TH2F(statN, "", nOuterBins, outerValues, nInnerBins, innerValues);
0699 
0700 #ifdef BOOST_AVAILABLE
0701     progress_display analysis_progress(nOuterBins * nInnerBins);
0702 #endif
0703 
0704     // Prepare by looping over the base bHandles - residuals
0705     for (auto& bHandle : baseResidualPulls) {
0706       // Create a unique handle tag
0707       TString handleTag = TString(bHandle.tag) + matrixTag;
0708       // ... and names
0709       TString residualRMSN = TString("res_rms_") + handleTag;
0710       TString residualMeanN = TString("res_mean_") + handleTag;
0711       TString pullSigmaN = TString("pull_sigma_") + handleTag;
0712       TString pullMeanN = TString("pull_mean_") + handleTag;
0713 
0714       TH2F* residualRMS = new TH2F(residualRMSN, "", nOuterBins, outerValues,
0715                                    nInnerBins, innerValues);
0716       TH2F* residualMean = new TH2F(residualMeanN, "", nOuterBins, outerValues,
0717                                     nInnerBins, innerValues);
0718       TH2F* pullSigma = new TH2F(pullSigmaN, "", nOuterBins, outerValues,
0719                                  nInnerBins, innerValues);
0720       TH2F* pullMean = new TH2F(pullMeanN, "", nOuterBins, outerValues,
0721                                 nInnerBins, innerValues);
0722       // Booked & ready
0723       summary.residualRMS.push_back(residualRMS);
0724       summary.residualMean.push_back(residualMean);
0725       summary.pullSigma.push_back(pullSigma);
0726       summary.pullMean.push_back(pullMean);
0727     }
0728 
0729     // Prepare by looping over the base handles - auxiliaries
0730     for (auto& aHandle : baseAuxilaries) {
0731       // Create a unique handle tag
0732       TString auxiliaryTag = TString(aHandle.tag) + matrixTag;
0733       TH2F* auxHist = new TH2F(auxiliaryTag, auxiliaryTag, nOuterBins,
0734                                  outerValues, nInnerBins, innerValues);
0735       summary.auxiliaries.push_back(auxHist);
0736     }
0737 
0738     unsigned int io = 0;
0739     for (auto& outerBatch : residualPullsMatrix) {
0740       unsigned int ii = 0;
0741       for (auto& innerBatch : outerBatch) {
0742         // residual/pull loop
0743         unsigned int iresp = 0;
0744         for (auto& bHandle : innerBatch) {
0745           // Range estimates / could be empty if read from configuration
0746           if (bHandle.rangeHist != nullptr) {
0747             bHandle.rangeHist->Write();
0748           }
0749           // Fill the stats
0750           if (iresp == 0) {
0751             summary.fillStats->SetBinContent(io + 1, ii + 1, bHandle.accepted);
0752           }
0753 
0754           // Residuals
0755           // Get RMS/ RMSError
0756           float rrms = bHandle.residualHist->GetRMS();
0757           float rrerr = bHandle.residualHist->GetRMSError();
0758           float rmean = bHandle.residualHist->GetMean();
0759           float rmerr = bHandle.residualHist->GetMeanError();
0760           summary.residualRMS[iresp]->SetBinContent(io + 1, ii + 1, rrms);
0761           summary.residualRMS[iresp]->SetBinError(io + 1, ii + 1, rrerr);
0762           summary.residualMean[iresp]->SetBinContent(io + 1, ii + 1, rmean);
0763           summary.residualMean[iresp]->SetBinError(io + 1, ii + 1, rmerr);
0764           bHandle.residualHist->Write();
0765           // Pulls
0766           bHandle.pullHist->Fit("gaus", "q");
0767           TF1* gauss = bHandle.pullHist->GetFunction("gaus");
0768           if (gauss != nullptr) {
0769             float pmu = gauss->GetParameter(1);
0770             float pmerr = gauss->GetParError(1);
0771             float psigma = gauss->GetParameter(2);
0772             float pserr = gauss->GetParError(2);
0773             summary.pullSigma[iresp]->SetBinContent(io + 1, ii + 1, psigma);
0774             summary.pullSigma[iresp]->SetBinError(io + 1, ii + 1, pserr);
0775             summary.pullMean[iresp]->SetBinContent(io + 1, ii + 1, pmu);
0776             summary.pullMean[iresp]->SetBinError(io + 1, ii + 1, pmerr);
0777           }
0778           bHandle.pullHist->Write();
0779           ++iresp;
0780         }
0781 
0782         // auxilaiary loop
0783         auto auxiliaryBatch = auxiliaryMatrix[io][ii];
0784         unsigned int iaux = 0;
0785         for (auto& aHandle : auxiliaryBatch) {
0786           float value = aHandle.hist->GetMean();
0787           float error = aHandle.hist->GetMeanError();
0788           summary.auxiliaries[iaux]->SetBinContent(io + 1, ii + 1, value);
0789           summary.auxiliaries[iaux]->SetBinError(io + 1, ii + 1, error);
0790           aHandle.hist->Write();
0791 
0792           ++iaux;
0793         }
0794 #ifdef BOOST_AVAILABLE
0795         ++analysis_progress;
0796 #endif
0797         ++ii;
0798       }
0799       ++io;
0800     }
0801 
0802     /// Write out the projection histogram
0803     ///
0804     /// @param h2 the 2D histogram as source for the projects
0805     /// @param fXTitle the title of the x axis of the first projection
0806     /// @param fYTitle the title of the y axis of the first projection
0807     /// @param sXTitle the title of the x axis of the second projection
0808     /// @param sYTitle the title of the y axis of the second projection
0809     auto writeProjections = [](const TH2F& h2, const TString& fXTitleP = "#eta",
0810                                const TString& fYTitleP = "sigma",
0811                                const TString& sXTitleP = "#phi",
0812                                const TString& sYTitleP = "sigma") -> void {
0813       const TString& fTag = "_pX";
0814       const TString& sTag = "_pY";
0815 
0816       unsigned int nBinsX = h2.GetXaxis()->GetNbins();
0817       unsigned int nBinsY = h2.GetYaxis()->GetNbins();
0818       if (fTag != "") {
0819         TH1D* pX =
0820             dynamic_cast<TH1D*>(h2.ProjectionX((h2.GetName() + fTag).Data()));
0821         setHistStyle(pX);
0822         if (pX != nullptr) {
0823           pX->GetXaxis()->SetTitle(fXTitleP.Data());
0824           pX->GetYaxis()->SetTitle(fYTitleP.Data());
0825           pX->Write();
0826         }
0827         // Bin-wise projections
0828         for (unsigned int iy = 1; iy <= nBinsY; ++iy) {
0829           pX = dynamic_cast<TH1D*>(h2.ProjectionX(
0830               (h2.GetName() + fTag + sTag + (iy - 1)).Data(), iy, iy));
0831           setHistStyle(pX);
0832           if (pX != nullptr) {
0833             pX->GetXaxis()->SetTitle(fXTitleP.Data());
0834             pX->GetYaxis()->SetTitle(fYTitleP.Data());
0835             pX->Write();
0836           }
0837         }
0838       }
0839       if (sTag != "") {
0840         TH1D* pY =
0841             dynamic_cast<TH1D*>(h2.ProjectionY((h2.GetName() + sTag).Data()));
0842         setHistStyle(pY);
0843         if (pY != nullptr) {
0844           pY->GetXaxis()->SetTitle(sXTitleP.Data());
0845           pY->GetYaxis()->SetTitle(sYTitleP.Data());
0846           pY->Write();
0847         }
0848         // Bin-wise projections
0849         for (unsigned int ix = 1; ix <= nBinsX; ++ix) {
0850           pY = dynamic_cast<TH1D*>(h2.ProjectionY(
0851               (h2.GetName() + sTag + fTag + (ix - 1)).Data(), ix, ix));
0852           setHistStyle(pY);
0853           if (pY != nullptr) {
0854             pY->GetXaxis()->SetTitle(sXTitleP.Data());
0855             pY->GetYaxis()->SetTitle(sYTitleP.Data());
0856             pY->Write();
0857           }
0858         }
0859       }
0860     };
0861 
0862     setHistStyle(summary.fillStats);
0863     summary.fillStats->Write();
0864 
0865     // Write mapped residual/pull histograms and their projections
0866     for (unsigned int iresp = 0; iresp < baseResidualPulls.size(); ++iresp) {
0867       // Get the handle for writing out
0868       auto bHandle = baseResidualPulls[iresp];
0869 
0870       TString rrms = TString("RMS[") + bHandle.residualStr + TString("] ") +
0871                      bHandle.residualUnit;
0872       TString rmu = TString("#mu[") + bHandle.residualStr + TString("] ") +
0873                     bHandle.residualUnit;
0874 
0875       TString psigma = TString("#sigma[(") + bHandle.residualStr +
0876                        TString(")/") + bHandle.errorStr + TString("]");
0877       TString pmu = TString("#mu[(") + bHandle.residualStr + TString(")/") +
0878                     bHandle.errorStr + TString("]");
0879 
0880       // 2D map histograms
0881       setHistStyle(summary.residualRMS[iresp]);
0882       summary.residualRMS[iresp]->GetXaxis()->SetTitle(fXTitle);
0883       summary.residualRMS[iresp]->GetYaxis()->SetTitle(sXTitle);
0884       summary.residualRMS[iresp]->GetZaxis()->SetTitle(rrms);
0885       summary.residualRMS[iresp]->Write();
0886 
0887       setHistStyle(summary.residualMean[iresp]);
0888       summary.residualMean[iresp]->GetXaxis()->SetTitle(fXTitle);
0889       summary.residualMean[iresp]->GetYaxis()->SetTitle(sXTitle);
0890       summary.residualMean[iresp]->GetZaxis()->SetTitle(rmu);
0891       summary.residualMean[iresp]->Write();
0892 
0893       setHistStyle(summary.pullSigma[iresp]);
0894       adaptColorPalette(summary.pullSigma[iresp], 0., 4., 1., 0.1, 104);
0895       summary.pullSigma[iresp]->GetXaxis()->SetTitle(fXTitle);
0896       summary.pullSigma[iresp]->GetYaxis()->SetTitle(sXTitle);
0897       summary.pullSigma[iresp]->GetZaxis()->SetRangeUser(0., 4.);
0898       summary.pullSigma[iresp]->GetZaxis()->SetTitle(psigma);
0899       summary.pullSigma[iresp]->Write();
0900 
0901       setHistStyle(summary.pullMean[iresp]);
0902       adaptColorPalette(summary.pullMean[iresp], -1., 1., 0., 0.1, 104);
0903       summary.pullMean[iresp]->GetXaxis()->SetTitle(fXTitle);
0904       summary.pullMean[iresp]->GetYaxis()->SetTitle(sXTitle);
0905       summary.pullMean[iresp]->GetZaxis()->SetRangeUser(-1., 1.);
0906       summary.pullMean[iresp]->GetZaxis()->SetTitle(pmu);
0907       summary.pullMean[iresp]->Write();
0908 
0909       // Write the projection histograms
0910       writeProjections(*summary.residualRMS[iresp], fXTitle, rrms, sXTitle,
0911                        rrms);
0912       writeProjections(*summary.residualMean[iresp], fXTitle, rmu, sXTitle,
0913                        rmu);
0914       writeProjections(*summary.pullSigma[iresp], fXTitle, psigma, sXTitle,
0915                        psigma);
0916       writeProjections(*summary.pullMean[iresp], fXTitle, pmu, sXTitle, pmu);
0917     }
0918 
0919     // Write mapped auxiliary histograms and their projections
0920     for (unsigned int iaux = 0; iaux < baseAuxilaries.size(); ++iaux) {
0921       setHistStyle(summary.auxiliaries[iaux]);
0922       summary.auxiliaries[iaux]->GetXaxis()->SetTitle(fXTitle);
0923       summary.auxiliaries[iaux]->GetYaxis()->SetTitle(sXTitle);
0924       summary.auxiliaries[iaux]->GetZaxis()->SetTitle(
0925           baseAuxilaries[iaux].label.c_str());
0926       summary.auxiliaries[iaux]->Write();
0927 
0928       writeProjections(*summary.auxiliaries[iaux], fXTitle,
0929                        baseAuxilaries[iaux].label, sXTitle,
0930                        baseAuxilaries[iaux].label);
0931     }
0932 
0933     return;
0934   };
0935 
0936 // The handle matrices
0937 #ifdef BOOST_AVAILABLE
0938   std::cout << "*** Bin/Projection Analysis: " << std::endl;
0939 #endif
0940   analyseBins(etaPtResidualPulls, etaPtAuxiliaries, TString("_eta_pt"), etaVals,
0941               ptVals, "#eta", "p_{T} [GeV]");
0942   analyseBins(etaPhiResidualPulls, etaPhiAuxiliaries, TString("_eta_phi"),
0943               etaVals, phiVals, "#eta", "#phi");
0944 
0945   output->Close();
0946 
0947   return 1;
0948 }