Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-10-15 08:17:26

0001 #ifndef FINALIZEQA_H
0002 #define FINALIZEQA_H
0003 //---- FinalizeQA.h -- the front of the Finalize PDF and the run record of corral_Finalize.root (2026-10-12, README.md "info/"):
0004 //----   title page (how corral was run: info/ of the chunks, left; contents, right), QA pages of the histograms summed over
0005 //----   the chunks with every cut drawn (events and vertices, tracks, dE/dx PID, V0s), and the vs-segment pages (segCells of
0006 //----   the chunks: one entry per DST segment in time order, segveto-skipped segments shaded).
0007 //---- Included by corral_finalize.cxx only, after fluct_common.h, PairTypes.h, finalize_hists.h and SegLive.h.
0008 #include <TCanvas.h>
0009 #include <TPad.h>
0010 #include <TLatex.h>
0011 #include <TLegend.h>
0012 #include <TLine.h>
0013 #include <TBox.h>
0014 #include <TGraph.h>
0015 #include <TH1D.h>
0016 #include <TH2D.h>
0017 #include <TKey.h>
0018 #include <TNamed.h>
0019 #include <TParameter.h>
0020 #include <TTree.h>
0021 #include <TDatime.h>
0022 #include <TObjString.h>
0023 #include <TObjArray.h>
0024 #include <TSystem.h>
0025 #include <TFile.h>
0026 #include <TH1F.h>
0027 #include <functional>
0028 #include <map>
0029 #include <vector>
0030 #include <set>
0031 
0032 namespace FinQA {
0033 
0034 //---- info/ of the chunks: texts from the first chunk that has them, counters summed, cut values from the first chunk;
0035 //---- every chunk must agree on the run string, the settings and the code (mismatches are listed on the title page)
0036 struct Info {
0037     int         nchunks = 0, nwith = 0;
0038     TString     runstring, settings, code, segveto, listdir;
0039     std::vector<TString>            mismatch;
0040     std::map<TString,double>        counts, cuts;
0041     bool have() const { return nwith>0; }
0042     double cut(const char* k, double def) const { auto it = cuts.find(k); return it==cuts.end() ? def : it->second; }
0043     double count(const char* k) const { auto it = counts.find(k); return it==counts.end() ? -1. : it->second; }
0044 };
0045 
0046 inline Info ReadChunkInfo(const std::vector<TFile*> &fch){
0047     Info I; I.nchunks = fch.size();
0048     int nold    = 0;
0049     for (size_t ic=0;ic<fch.size();ic++){
0050         TDirectory *d   = fch[ic]->GetDirectory("info");
0051         if (!d){ ++nold; continue; }
0052         auto txt    = [&](const char* n){ TNamed *o = (TNamed*)d->Get(n); TString t = o ? o->GetTitle() : ""; delete o; return t; };
0053         TString rs = txt("runstring"), st = txt("settings"), cd = txt("code"), sv = txt("segveto"), ls = gSystem->DirName(txt("list"));
0054         if (I.nwith==0){ I.runstring = rs; I.settings = st; I.code = cd; I.segveto = sv; I.listdir = ls; }
0055         else {
0056             TString w;
0057             if (rs!=I.runstring) w += " run string";
0058             if (st!=I.settings)  w += " settings";
0059             if (cd!=I.code)      w += " code";
0060             if (sv!=I.segveto)   w += " segveto";
0061             if (w!="" && I.mismatch.size()<8) I.mismatch.push_back(Form("chunk %02zu differs from the first in:%s",ic,w.Data()));
0062         }
0063         TIter next(d->GetListOfKeys()); TKey *k;
0064         while ((k = (TKey*)next())){
0065             TString n = k->GetName();
0066             if (!n.BeginsWith("count_") && !n.BeginsWith("cut_")) continue;
0067             TParameter<double> *p = (TParameter<double>*)k->ReadObj(); if (!p) continue;
0068             if (n.BeginsWith("count_")) I.counts[n(6,n.Length())] += p->GetVal();
0069             else if (!I.cuts.count(n(4,n.Length()))) I.cuts[n(4,n.Length())] = p->GetVal();
0070             delete p;
0071         }
0072         ++I.nwith;
0073     }
0074     if (nold>0) I.mismatch.insert(I.mismatch.begin(),Form("%d of %d chunks have no info/ (made before 2026-10-12)",nold,I.nchunks));
0075     return I;
0076 }
0077 
0078 //---- sum of a histogram over the chunks (0 if no chunk has it)
0079 inline TH1* Sum(const std::vector<TFile*> &fch, const char* name){
0080     TH1 *s  = 0;
0081     for (TFile *f : fch){
0082         TH1 *h  = (TH1*)f->Get(name); if (!h) continue;
0083         if (!s){ s = (TH1*)h->Clone(Form("%s_fin",name)); s->SetDirectory(0); } else s->Add(h);
0084         delete h;
0085     }
0086     return s;
0087 }
0088 
0089 //---- drawing helpers: a vertical cut line (dashed), a shaded x window, a legend in the top right
0090 inline TLine* VLine(double x, int col, int style=2){
0091     gPad->Update();
0092     double y0 = gPad->GetUymin(), y1 = gPad->GetUymax();
0093     if (gPad->GetLogy()){ y0 = pow(10,y0); y1 = pow(10,y1); }
0094     TLine *l = new TLine(x,y0,x,y1); l->SetLineColor(col); l->SetLineStyle(style); l->SetLineWidth(2); l->Draw(); return l;
0095 }
0096 inline TBox* XBand(double x0, double x1, int col, double alpha=0.25){
0097     gPad->Update();
0098     double y0 = gPad->GetUymin(), y1 = gPad->GetUymax();
0099     if (gPad->GetLogy()){ y0 = pow(10,y0); y1 = pow(10,y1); }
0100     TBox *b = new TBox(x0,y0,x1,y1); b->SetFillColorAlpha(col,alpha); b->SetLineWidth(0); b->Draw(); return b;
0101 }
0102 inline TLegend* Leg(double x0=0.50, double y0=0.72, double x1=0.89, double y1=0.89){
0103     TLegend *l = new TLegend(x0,y0,x1,y1); l->SetBorderSize(0); l->SetFillColorAlpha(kWhite,0.85); l->SetTextSize(0.040); return l;
0104 }
0105 //---- raw (black) and kept (red) overlay of two summed 1-D histograms
0106 inline void Pair(TH1 *h0, TH1 *h1, const char* title, const char* l0, const char* l1, bool logy, TLegend *lg){
0107     if (!h0 && !h1) return;
0108     TH1 *f  = h0 ? h0 : h1;
0109     gPad->SetLogy(logy);
0110     f->SetTitle(title); f->SetStats(0);
0111     for (TH1 *h : {h0,h1}) if (h){ h->SetFillStyle(0); h->SetLineWidth(1); }
0112     if (h0){ h0->SetLineColor(kBlack); if (h1) h0->SetMaximum((logy ? 3. : 1.15)*std::max(h0->GetMaximum(),h1->GetMaximum())); h0->Draw("hist"); lg->AddEntry(h0,l0,"l"); }
0113     if (h1){ h1->SetLineColor(kRed); h1->Draw(h0 ? "hist same" : "hist"); lg->AddEntry(h1,l1,"l"); }
0114 }
0115 
0116 //---- the outline of the PDF (right side of the title page): sections, then the pair types in their order
0117 inline void TitlePage(TCanvas *c, const TString &set, const Info &I, const TString &finRunString, double fz, const char* finCode,
0118                       const std::vector<int> &runs, double nevtPhys, const std::vector<TString> &pairNames, bool haveRef){
0119     c->Clear(); c->cd();
0120     TLatex t; t.SetNDC(); t.SetTextFont(42); t.SetTextAlign(13);
0121     double y = 0.965;
0122     auto line   = [&](const TString &s, double sz, int col=kBlack, double dy=0.){ t.SetTextSize(sz); t.SetTextColor(col); t.DrawLatex(0.025,y,s.Data()); y -= (dy>0 ? dy : 1.45*sz); };
0123     TDatime now;
0124     line(Form("Corral Finalize: %s",set.Data()),0.034);
0125     line(Form("%s;  Finalize run string \"%s\": physics |z_{vtx}| < %.0f cm;  Finalize code %s",now.AsSQLString(),finRunString.Data(),fz,finCode),0.0145);
0126     TString rl; for (int r : runs) rl += Form(" %d",r);
0127     line(Form("%d chunks (%s), runs:%s",I.nchunks,I.listdir!="" ? I.listdir.Data() : Form("lists/%s",set.Data()),rl.Data()),0.0145);
0128     if (!I.have()){
0129         line("the chunks carry no info/ (made before 2026-10-12): run string, settings and code not recorded",0.0145,kRed+1);
0130     } else {
0131         line(Form("chunk run string \"%s\";  chunk code %s",I.runstring.Data(),I.code.Data()),0.0145);
0132         line(Form("segment veto: %s",I.segveto.Data()),0.0145);
0133         y -= 0.006;
0134         line("Events",0.018);
0135         auto cnt    = [&](const char* k){ double v = I.count(k); return v<0 ? TString("?") : TString(Form("%.4g",v)); };
0136         line(Form("  rows read (vertices in the trees) %s;  skipped: overlapping-TF copies %s, segment veto %s, isolated-vertex %s",
0137             cnt("rows_read").Data(),cnt("rows_skipped_tfdup").Data(),cnt("rows_skipped_segveto").Data(),cnt("rows_skipped_isovtx").Data()),0.0145);
0138         line(Form("  outside the crossing selection %s;  events reaching the track loop (|z_{vtx}| < %.0f cm) %s",
0139             cnt("events_skipped_crossing").Data(),I.cut("vzmax",16.),cnt("events_tracks").Data()),0.0145);
0140         line(Form("  events in CalcRm with |z_{vtx}| < %.0f cm (the physics) %.4g",fz,nevtPhys),0.0145);
0141         y -= 0.006;
0142         line("Settings in force (identical in every chunk unless listed in red below)",0.018);
0143         TObjArray *a    = I.settings.Tokenize("\n");
0144         for (int i=0;i<a->GetEntries();i++) line(TString("  ")+((TObjString*)a->At(i))->GetName(),0.0135);
0145         delete a;
0146     }
0147     if (!I.mismatch.empty()){ y -= 0.006; for (auto &m : I.mismatch) line(m,0.0145,kRed+1); }
0148     else if (I.have()) line(Form("all %d chunks agree on run string, settings and code",I.nchunks),0.0145,kGreen+3);
0149     //---- right: contents
0150     const double xr = 0.645;
0151     TLine *sep  = new TLine(xr-0.012,0.03,xr-0.012,0.97); sep->SetNDC(); sep->SetLineColor(kGray); sep->Draw();
0152     t.SetTextColor(kBlack); t.SetTextSize(0.020); t.DrawLatex(xr,0.965,"Contents");
0153     const char* sec[]   = {
0154         "Title page (this page)",
0155         "Events and vertices (summed over the chunks, cuts drawn)",
0156         "Tracks: kinematics, ntpc, DCA, acceptance",
0157         "dE/dx PID: KFP gates and the identified species",
0158         "V0 candidates: mass peaks (pair window shaded), p_{T}",
0159         "Versus DST segment (time order): events, vertices,",
0160         "     tracks, DCA, TPC live fraction (segveto shaded)",
0161         "Versus DST segment: identified particles and V0s per event",
0162         "Per pair type, in the order below: overview, C(Q),",
0163         "     resonance CF C(Q,p_{T}), crossing page",
0164         "Crossing-correction summary",
0165         haveRef ? "Pages vs the whole-dataset reference" : "(no whole-dataset reference: no reference pages)",
0166         "Crossing-loss (step 1b) pages per pair type, summaries",
0167         "N_{valid} maps" };
0168     double yr   = 0.93; t.SetTextSize(0.0135);
0169     for (const char* s : sec){ t.DrawLatex(xr,yr,s); yr -= 0.021; }
0170     yr -= 0.010; t.SetTextSize(0.016); t.DrawLatex(xr,yr,Form("Pair types (%zu)",pairNames.size())); yr -= 0.026;
0171     const int ncol  = 3, nrow = (pairNames.size()+ncol-1)/ncol;
0172     t.SetTextSize(0.0125);
0173     for (size_t k=0;k<pairNames.size();k++){
0174         int col = k/nrow, row = k%nrow;
0175         t.DrawLatex(xr+col*0.118, yr-row*0.0175, Form("%zu  %s",k,pairNames[k].Data()));
0176     }
0177 }
0178 
0179 //---- QA pages from the summed chunk histograms. Cut values: info/cut_* of the chunks (defaults if the chunks have none).
0180 //---- out: the summed histograms (written to work/qa/ by the caller)
0181 inline void QAPages(TCanvas *c, const char* pdf, const std::vector<TFile*> &fch, const Info &I, double fz, std::vector<TH1*> &out){
0182     const double ptmin = I.cut("ptmin",0.1), ntpcc = I.cut("ntpc",18), dxy = I.cut("dcaxy",1.5), dz = I.cut("dcaz",1.5);
0183     const double vz = I.cut("vzmax",16), vxy = I.cut("vxymax",0.5);
0184     const int xsel = (int)I.cut("xingsel",3), nmask = (int)I.cut("siphimask",NSIPHIMASK_MAIN);
0185     std::map<TString,TH1*> got;     // each histogram summed once (some are drawn on two pads)
0186     auto S  = [&](const char* n){ if (got.count(n)) return got[n]; TH1 *h = Sum(fch,n); if (h){ out.push_back(h); if (h->GetDimension()==1) h->SetFillStyle(0); } got[n] = h; return h; };
0187     const TString cutsrc    = I.have() ? "" : " (default cuts: the chunks record none)";
0188     //---------------- events and vertices
0189     c->Clear(); c->Divide(3,3,0.0001,0.0001);
0190     {
0191         TLegend *lg;
0192         const char* xyz[3]  = {"x","y","z"};
0193         for (int k=0;k<3;k++){
0194             c->cd(1+k); lg = Leg();
0195             TH1 *h0 = S(Form("hvtx%s0",xyz[k])), *h1 = S(Form("hvtx%s",xyz[k]));
0196             Pair(h0,h1,Form("vertex %s%s;vertex %s (cm);events",xyz[k],cutsrc.Data(),xyz[k]),"all rows","accepted events",true,lg);
0197             if (k<2){ VLine(-vxy,kBlue); TLine *l = VLine(vxy,kBlue); lg->AddEntry(l,Form("|%s| < %.1f cm",xyz[k],vxy),"l"); }
0198             else { VLine(-vz,kBlue); TLine *l = VLine(vz,kBlue); lg->AddEntry(l,Form("|z| < %.0f cm (jobs)",vz),"l");
0199                 TBox *b = XBand(-fz,fz,kGreen+1); lg->AddEntry(b,Form("|z| < %.0f cm (physics)",fz),"f"); }
0200             lg->Draw();
0201         }
0202         c->cd(4); lg = Leg();
0203         Pair(S("hntrk0"),S("hntrk"),"tracks per event;n tracks;events","all tracks (rows)","accepted tracks",true,lg); lg->Draw();
0204         c->cd(5); lg = Leg(0.40,0.72,0.89,0.89);
0205         { TH1 *h = S("hxing"); if (h){ gPad->SetLogy(1); h->SetTitle("crossing of the vertex, all rows;crossing;rows"); h->SetStats(0); h->SetLineColor(kBlack); h->Draw("hist");
0206             if (xsel==3){ TBox *b = XBand(corral::ZB_LO-0.5,corral::ZB_HI+0.5,kGreen+1); TBox *n = XBand(corral::XNEAR_LO-0.5,corral::XNEAR_HI+0.5,kWhite,1.);
0207                 h->Draw("hist same"); lg->AddEntry(b,Form("ZB %d..%d (kept)",corral::ZB_LO,corral::ZB_HI),"f"); lg->AddEntry(n,Form("NearX0 %d..%d (out)",corral::XNEAR_LO,corral::XNEAR_HI),"f"); (void)n; }
0208             else lg->AddEntry((TObject*)0,xsel==0?"all crossings kept":xsel==1?"crossing 0 only":"NearX0 only","");
0209             lg->Draw(); } }
0210         c->cd(6); lg = Leg();
0211         { TH2 *h2 = (TH2*)S("hnvtxCR"); if (h2){ TH1D *p = h2->ProjectionY("hnvtxCR_py_fin",h2->GetXaxis()->FindBin(-fz+1e-6),h2->GetXaxis()->FindBin(fz-1e-6)); p->SetDirectory(0);
0212             gPad->SetLogy(1); p->SetTitle(Form("vertices per TF (events in CalcRm, |z| < %.0f cm);vertices per TF;events",fz)); p->SetStats(0); p->SetLineColor(kBlack);
0213             p->GetXaxis()->SetRangeUser(0.,std::min(199.,p->GetMean()*4+10)); p->Draw("hist"); lg->AddEntry(p,Form("mean %.2f",p->GetMean()),"l"); lg->Draw(); } }
0214         c->cd(7); gPad->SetRightMargin(0.13);
0215         { TH2 *h = (TH2*)S("hetazvtx"); if (h){ gPad->SetLogz(1); h->SetTitle("accepted tracks: #eta vs z_{vtx};z_{vtx} (cm);#eta"); h->SetStats(0); h->Draw("colz");
0216             VLine(-fz,kGreen+2,1); VLine(fz,kGreen+2,1); } }
0217         c->cd(8); lg = Leg(0.30,0.15,0.70,0.25);
0218         { TH2 *h2 = (TH2*)S("hnvtxCR"); if (h2){ TH1D *p = h2->ProjectionX("hnvtxCR_px_fin"); p->SetDirectory(0); p->SetTitle("z_{vtx} of the events reaching CalcRm;z_{vtx} (cm);events"); p->SetStats(0); p->SetLineColor(kBlack); p->SetMinimum(0); p->SetMaximum(1.15*p->GetMaximum()); p->Draw("hist");
0219             TBox *b = XBand(-fz,fz,kGreen+1); p->Draw("hist same"); lg->AddEntry(b,Form("physics |z| < %.0f cm",fz),"f"); lg->Draw(); } }
0220         c->cd(9);
0221         { TLatex t; t.SetNDC(); t.SetTextFont(42); t.SetTextSize(0.055); t.SetTextAlign(13); double y = 0.92;
0222           auto L = [&](const TString &s){ t.DrawLatex(0.05,y,s.Data()); y -= 0.075; };
0223           auto v = [&](const char* k){ double x = I.count(k); return x<0 ? TString("?") : TString(Form("%.4g",x)); };
0224           L("#bf{Event flow} (summed over the chunks)");
0225           L(Form("rows read: %s",v("rows_read").Data()));
0226           L(Form("  - overlapping-TF copies: %s",v("rows_skipped_tfdup").Data()));
0227           L(Form("  - segment veto: %s",v("rows_skipped_segveto").Data()));
0228           L(Form("  - isolated-vertex selection: %s",v("rows_skipped_isovtx").Data()));
0229           L(Form("  - crossing selection: %s",v("events_skipped_crossing").Data()));
0230           L(Form("  - vertex x, y, z cuts (rest)"));
0231           L(Form("events reaching the track loop: %s",v("events_tracks").Data()));
0232           TH2 *h2 = (TH2*)S("hnvtxCR");
0233           if (h2) L(Form("events in CalcRm, |z| < %.0f cm: %.4g",fz,h2->Integral(h2->GetXaxis()->FindBin(-fz+1e-6),h2->GetXaxis()->FindBin(fz-1e-6),0,-1))); }
0234     }
0235     c->cd(); c->Print(pdf);
0236     //---------------- tracks
0237     c->Clear(); c->Divide(3,3,0.0001,0.0001);
0238     {
0239         TLegend *lg;
0240         c->cd(1); lg = Leg(); Pair(S("hpt0"),S("hpt"),"p_{T};p_{T} (GeV/c);tracks","all tracks","accepted",true,lg);
0241         { TLine *l = VLine(ptmin,kBlue); lg->AddEntry(l,Form("p_{T} > %.2f GeV/c",ptmin),"l"); } lg->Draw();
0242         c->cd(2); lg = Leg(0.35,0.15,0.75,0.32); Pair(S("heta0"),S("heta"),"#eta;#eta;tracks","all tracks","accepted",false,lg); lg->Draw();
0243         c->cd(3); lg = Leg(0.35,0.15,0.75,0.32); Pair(S("hphi0"),S("hphi"),"#phi at the vertex;#phi (rad);tracks","all tracks","accepted",false,lg);
0244         if (nmask>0){ TBox *b = 0; for (int k=0;k<nmask;k++) b = XBand(SIPHIMASK_LO[k]*M_PI/180.,SIPHIMASK_HI[k]*M_PI/180.,kGray+1,0.35); lg->AddEntry(b,"SiPhiMask (out)","f"); }
0245         lg->Draw();
0246         c->cd(4); lg = Leg(0.15,0.72,0.55,0.89); Pair(S("hntpc0"),S("hntpc"),"TPC clusters;ntpc;tracks","all tracks","accepted",false,lg);
0247         { TLine *l = VLine(ntpcc-0.5,kBlue); lg->AddEntry(l,Form("ntpc #geq %.0f",ntpcc),"l"); } lg->Draw();
0248         c->cd(5); lg = Leg(); Pair(S("hdcaxy0"),S("hdcaxy"),"DCA_{xy};dca_{xy} (cm);tracks","all tracks","accepted",true,lg);
0249         { VLine(-dxy,kBlue); TLine *l = VLine(dxy,kBlue); lg->AddEntry(l,Form("|dca_{xy}| < %.2f cm",dxy),"l"); } lg->Draw();
0250         c->cd(6); lg = Leg(); Pair(S("hdcaz0"),S("hdcaz"),"DCA_{z};dca_{z} (cm);tracks","all tracks","accepted",true,lg);
0251         { VLine(-dz,kBlue); TLine *l = VLine(dz,kBlue); lg->AddEntry(l,Form("|dca_{z}| < %.2f cm",dz),"l"); } lg->Draw();
0252         c->cd(7); lg = Leg(); Pair(S("hnmvtx0"),S("hnmvtx"),"MVTX clusters;nmvtx;tracks","all tracks","accepted",false,lg); lg->Draw();
0253         c->cd(8); gPad->SetRightMargin(0.13);
0254         { TH2 *h = (TH2*)S("hphieta"); if (h){ h->SetTitle("accepted tracks: #phi vs #eta;#eta;#phi (rad)"); h->SetStats(0); h->Draw("colz");
0255             if (nmask>0) for (int k=0;k<nmask;k++) for (double x : {SIPHIMASK_LO[k],SIPHIMASK_HI[k]}){ TLine *l = new TLine(-1.5,x*M_PI/180.,1.5,x*M_PI/180.); l->SetLineColor(kGray+2); l->SetLineStyle(2); l->Draw(); } } }
0256         c->cd(9); lg = Leg(0.15,0.72,0.55,0.89); Pair(S("hnintt0"),S("hnintt"),"INTT clusters;nintt;tracks","all tracks","accepted",false,lg); lg->Draw();
0257     }
0258     c->cd(); c->Print(pdf);
0259     //---------------- dE/dx PID: the KFP gates (dedxGates_KFP.root, as the chunks)
0260     c->Clear(); c->Divide(2,2,0.0001,0.0001);
0261     {
0262         const char* sp[4]   = {"pi","K","p","d"}; const int gcol[4] = {kBlue,kGreen+2,kRed,kGray+2};
0263         std::vector<TGraph*> g;
0264         TFile *fg   = TFile::Open((CorralDir()+"dedxGates_KFP.root").Data());
0265         if (fg && !fg->IsZombie()) for (int i=0;i<4;i++) for (const char* s : {"lo","hi"}){
0266             TGraph *x = (TGraph*)fg->Get(Form("g_%s_%s",sp[i],s)); if (!x) continue; x = (TGraph*)x->Clone(); x->SetLineColor(gcol[i]); x->SetLineStyle(2); x->SetLineWidth(2); g.push_back(x); }
0267         if (fg) fg->Close();
0268         const char* nm[4]   = {"hdedxKFPp","hdedxKFPp_id0","hdedxKFPp_id1","hdedxKFPp_id2"};
0269         const char* tt[4]   = {"all accepted tracks","PID #pi","PID K","PID p"};
0270         for (int k=0;k<4;k++){
0271             c->cd(1+k); gPad->SetLogz(1); gPad->SetRightMargin(0.13);
0272             TH1 *h = S(nm[k]); if (!h) continue;
0273             h->SetTitle(Form("dE/dx (KFP) vs p, %s;p (GeV/c);dE/dx (KFP)",tt[k])); h->SetStats(0); h->Draw("colz");
0274             for (TGraph *x : g) x->Draw("L same");
0275             if (k==0){ TLegend *lg = Leg(0.55,0.65,0.86,0.89); for (int i=0;i<4;i++){ TLine *l = new TLine(); l->SetLineColor(gcol[i]); l->SetLineStyle(2); l->SetLineWidth(2); lg->AddEntry(l,Form("%s gate",sp[i]),"l"); } lg->Draw(); }
0276         }
0277     }
0278     c->cd(); c->Print(pdf);
0279     //---------------- V0s: mass with the pair window (+-V0PEAK_NSIG sigma), p_T
0280     c->Clear(); c->Divide(3,2,0.0001,0.0001);
0281     {
0282         const char* mn[3]   = {"hKSmass","hLAmass","hALmass"}, *pn[3] = {"hKSpt","hLApt","hALpt"};
0283         const char* vn[3]   = {"K^{0}_{S}","#Lambda","#bar{#Lambda}"};
0284         for (int k=0;k<3;k++){
0285             c->cd(1+k); TLegend *lg = Leg(0.55,0.72,0.89,0.89);
0286             TH1 *h = S(mn[k]); if (!h) continue;
0287             h->SetTitle(Form("%s candidates;mass (GeV/c^{2});candidates",vn[k])); h->SetStats(0); h->SetLineColor(kBlack); h->SetMinimum(0); h->Draw("hist");
0288             TBox *b = XBand(V0PEAK_MU[k]-V0PEAK_NSIG*V0PEAK_SIG[k],V0PEAK_MU[k]+V0PEAK_NSIG*V0PEAK_SIG[k],kGreen+1); h->Draw("hist same");
0289             lg->AddEntry(h,Form("%.4g candidates",h->Integral()),"l"); lg->AddEntry(b,Form("in pairs: #pm%.0f#sigma",V0PEAK_NSIG),"f");
0290             if (k>0){ TH1 *hc = S(Form("%s_clean",mn[k])); if (hc){ hc->SetLineColor(kRed); hc->Draw("hist same"); lg->AddEntry(hc,"worse-dau. DCA cut","l"); } }
0291             lg->Draw();
0292             c->cd(4+k); gPad->SetLogy(1);
0293             TH1 *p = S(pn[k]); if (p){ p->SetTitle(Form("%s candidates, all masses;p_{T} (GeV/c);candidates",vn[k])); p->SetStats(0); p->SetLineColor(kBlack); p->Draw("hist"); }
0294         }
0295     }
0296     c->cd(); c->Print(pdf);
0297 }
0298 
0299 //---- vs-segment pages from the segCells trees (one entry per DST segment read, time order = chunk order, then entry order).
0300 //---- Returns the per-segment table work/segQA (owned by the caller).
0301 inline TTree* SegmentPages(TCanvas *c, const char* pdf, const std::vector<TFile*> &fch){
0302     struct Seg { int run, seg, chunk, veto, nraw, nevt, ntrk; float vtx, pt, ntpc, dxy, dz, live[3], nid[6], nv0[3]; };
0303     std::vector<Seg> v;
0304     bool haveQA = false;
0305     for (size_t ic=0;ic<fch.size();ic++){
0306         TTree *t    = (TTree*)fch[ic]->Get("segCells"); if (!t) continue;
0307         const bool qa   = t->GetBranch("nraw")!=0; haveQA |= qa;
0308         t->SetBranchStatus("*",0);
0309         Int_t run, seg, nevt, ntrk, nraw=0, veto=0, nid[6]={0}, nv0[3]={0}; Float_t nvtx=0, spt=0, sntpc=0, sxy=0, sz=0;
0310         for (const char* b : {"run","segment","nevt","ntrk"}) t->SetBranchStatus(b,1);
0311         t->SetBranchAddress("run",&run); t->SetBranchAddress("segment",&seg); t->SetBranchAddress("nevt",&nevt); t->SetBranchAddress("ntrk",&ntrk);
0312         if (qa){
0313             for (const char* b : {"nraw","veto","nvtx","sumpt","sumntpc","sumadcaxy","sumadcaz","nid","nv0"}) t->SetBranchStatus(b,1);
0314             t->SetBranchAddress("nraw",&nraw); t->SetBranchAddress("veto",&veto); t->SetBranchAddress("nvtx",&nvtx); t->SetBranchAddress("sumpt",&spt);
0315             t->SetBranchAddress("sumntpc",&sntpc); t->SetBranchAddress("sumadcaxy",&sxy); t->SetBranchAddress("sumadcaz",&sz);
0316             t->SetBranchAddress("nid",nid); t->SetBranchAddress("nv0",nv0);
0317         }
0318         for (Long64_t e=0;e<t->GetEntries();e++){
0319             t->GetEntry(e);
0320             Seg s; s.run = run; s.seg = seg; s.chunk = ic; s.veto = veto; s.nraw = qa ? nraw : -1; s.nevt = nevt; s.ntrk = ntrk;
0321             const double ne = nevt>0 ? nevt : 1, nt = ntrk>0 ? ntrk : 1;
0322             s.vtx = qa ? nvtx/ne : -1; s.pt = qa ? spt/nt : -1; s.ntpc = qa ? sntpc/nt : -1; s.dxy = qa ? sxy/nt : -1; s.dz = qa ? sz/nt : -1;
0323             for (int i=0;i<6;i++) s.nid[i] = qa ? nid[i]/ne : -1;
0324             for (int i=0;i<3;i++) s.nv0[i] = qa ? nv0[i]/ne : -1;
0325             for (int i=0;i<3;i++) s.live[i] = -1;
0326             v.push_back(s);
0327         }
0328         delete t;
0329     }
0330     if (v.empty()){ cout<<"corral::Finalize -- no segCells in the chunks: no vs-segment pages"<<endl; return 0; }
0331     {
0332         std::vector<TString> files; for (TFile *f : fch) files.push_back(f->GetName());
0333         SegLiveResult L = SegLiveFractions(files);
0334         for (auto &s : v){ auto it = L.live.find({s.run,s.seg}); if (it!=L.live.end()) for (int i=0;i<3;i++) s.live[i] = it->second[i]; }
0335         cout<<"corral::Finalize -- vs-segment pages: "<<v.size()<<" segments, live fraction for "<<L.live.size()<<" ("<<(L.feeMode?"FEE":"generic")<<" cells)"<<endl;
0336     }
0337     const int N = v.size();
0338     //---- one panel: quantity f(s) vs segment index, valid where f >= 0 and the segment has events; y range = 0.5-99.5% quantiles
0339     auto panel  = [&](std::vector<std::function<double(const Seg&)>> fs, std::vector<int> cols, std::vector<const char*> labs, const char* title, bool needEvt = true){
0340         std::vector<double> all;
0341         std::vector<TGraph*> g;
0342         for (size_t q=0;q<fs.size();q++){
0343             TGraph *x = new TGraph(); x->SetMarkerStyle(1); x->SetMarkerColor(cols[q]);
0344             for (int i=0;i<N;i++){ const Seg &s = v[i]; if (needEvt && s.nevt<=0) continue; double y = fs[q](s); if (y<0) continue; x->SetPoint(x->GetN(),i,y); all.push_back(y); }
0345             g.push_back(x);
0346         }
0347         if (all.empty()){ gPad->DrawFrame(0.,0.,1.,1.,""); TLatex t; t.SetNDC(); t.SetTextSize(0.06); t.DrawLatex(0.1,0.5,Form("%s: not in these chunks",title)); return; }
0348         std::sort(all.begin(),all.end());
0349         double lo = all[(size_t)(0.005*(all.size()-1))], hi = all[(size_t)(0.995*(all.size()-1))], d = hi-lo; if (d<=0) d = fabs(hi)*0.1+1e-6;
0350         lo -= 0.15*d; hi += 0.30*d;
0351         TH1F *fr = gPad->DrawFrame(-0.5,lo,N-0.5,hi,Form("%s;DST segment index (time order);",title));
0352         fr->GetXaxis()->SetLabelSize(0.045); fr->GetYaxis()->SetLabelSize(0.045);
0353         //---- segveto-skipped segments: gray strip at the bottom of the frame
0354         TH1D *hv    = new TH1D(Form("hveto_%p",(void*)fr),"",N,-0.5,N-0.5); hv->SetDirectory(0);
0355         int nv = 0; for (int i=0;i<N;i++){ hv->SetBinContent(i+1,lo); if (v[i].veto){ hv->SetBinContent(i+1,lo+0.08*(hi-lo)); ++nv; } }
0356         if (nv){ hv->SetFillColorAlpha(kGray+1,0.6); hv->SetLineWidth(0); hv->SetMinimum(lo); hv->Draw("hist same"); }
0357         for (int i=1;i<N;i++) if (v[i].run!=v[i-1].run){ TLine *l = new TLine(i-0.5,lo,i-0.5,hi); l->SetLineColor(kGray+2); l->SetLineStyle(2); l->Draw(); }
0358         for (auto x : g) x->Draw("P");
0359         TLegend *lg = Leg(0.12,0.80,0.89,0.89); lg->SetNColumns(std::min<int>(6,labs.size()+(nv?1:0)));
0360         for (size_t q=0;q<labs.size();q++){ TGraph *m = new TGraph(); m->SetMarkerStyle(20); m->SetMarkerColor(cols[q]); lg->AddEntry(m,labs[q],"p"); }
0361         if (nv){ lg->AddEntry(hv,Form("segveto (%d)",nv),"f"); }
0362         lg->Draw();
0363     };
0364     auto runLabels  = [&](){ TLatex t; t.SetTextSize(0.035); t.SetTextColor(kGray+2); t.SetTextAlign(13); gPad->Update();
0365         int i0 = 0; for (int i=1;i<=N;i++) if (i==N || v[i].run!=v[i-1].run){ t.DrawLatex(i0+0.01*N,gPad->GetUymin()+0.20*(gPad->GetUymax()-gPad->GetUymin()),Form("%d",v[i0].run)); i0 = i; } };
0366     //---------------- page 1: events, vertices, tracks, DCA, live fraction
0367     c->Clear(); c->Divide(3,3,0.0001,0.0001);
0368     c->cd(1); panel({[](const Seg &s){ return s.nraw>0 ? (double)s.nevt/s.nraw : -1.; }},{kBlack},{"events / rows"},"events reaching the track loop / rows read"); runLabels();
0369     c->cd(2); panel({[](const Seg &s){ return (double)s.vtx; }},{kBlack},{"#LTvertices per TF#GT"},"vertices in the trigger frame (occupancy)");
0370     c->cd(3); panel({[](const Seg &s){ return s.nevt>0 ? (double)s.ntrk/s.nevt : -1.; }},{kBlack},{"#LTn accepted tracks#GT"},"accepted tracks per event");
0371     c->cd(4); panel({[](const Seg &s){ return s.ntrk>0 ? (double)s.pt : -1.; }},{kBlack},{"#LTp_{T}#GT"},"#LTp_{T}#GT of accepted tracks (GeV/c)");
0372     c->cd(5); panel({[](const Seg &s){ return s.ntrk>0 ? (double)s.ntpc : -1.; }},{kBlack},{"#LTntpc#GT"},"#LTntpc#GT of accepted tracks");
0373     c->cd(6); panel({[](const Seg &s){ return s.ntrk>0 ? (double)s.dxy : -1.; }},{kBlack},{"#LT|dca_{xy}|#GT"},"#LT|dca_{xy}|#GT of accepted tracks (cm)");
0374     c->cd(7); panel({[](const Seg &s){ return s.ntrk>0 ? (double)s.dz : -1.; }},{kBlack},{"#LT|dca_{z}|#GT"},"#LT|dca_{z}|#GT of accepted tracks (cm)");
0375     c->cd(8); panel({[](const Seg &s){ return (double)s.live[0]; }},{kBlack},{"all"},"TPC live fraction (FEE cells, segveto rule)");
0376     c->cd(9); panel({[](const Seg &s){ return (double)s.live[1]; },[](const Seg &s){ return (double)s.live[2]; }},{kBlue,kRed},{"South","North"},"TPC live fraction, South and North");
0377     c->cd(); c->Print(pdf);
0378     //---------------- page 2: identified particles and V0s per event
0379     c->Clear(); c->Divide(2,3,0.0001,0.0001);
0380     c->cd(1); panel({[](const Seg &s){ return (double)s.nid[0]; },[](const Seg &s){ return (double)s.nid[1]; }},{kRed,kBlue},{"#pi^{+}","#pi^{-}"},"identified pions per event"); runLabels();
0381     c->cd(2); panel({[](const Seg &s){ return (double)s.nid[2]; },[](const Seg &s){ return (double)s.nid[3]; }},{kRed,kBlue},{"K^{+}","K^{-}"},"identified kaons per event");
0382     c->cd(3); panel({[](const Seg &s){ return (double)s.nid[4]; },[](const Seg &s){ return (double)s.nid[5]; }},{kRed,kBlue},{"p","#bar{p}"},"identified (anti)protons per event");
0383     c->cd(4); panel({[](const Seg &s){ return (double)s.nv0[0]; }},{kBlack},{"K^{0}_{S}"},"K^{0}_{S} in the mass window per event");
0384     c->cd(5); panel({[](const Seg &s){ return (double)s.nv0[1]; },[](const Seg &s){ return (double)s.nv0[2]; }},{kRed,kBlue},{"#Lambda","#bar{#Lambda}"},"#Lambda, #bar{#Lambda} in the mass window per event");
0385     c->cd(6); panel({[](const Seg &s){ return (double)s.nevt; }},{kBlack},{"events"},"events per segment");
0386     c->cd(); c->Print(pdf);
0387     //---- the table
0388     TTree *t    = new TTree("segQA","per DST segment, time order: QA means from the chunks' segCells (Finalize, README.md)");
0389     Seg s;
0390     t->Branch("run",&s.run,"run/I"); t->Branch("segment",&s.seg,"segment/I"); t->Branch("chunk",&s.chunk,"chunk/I"); t->Branch("veto",&s.veto,"veto/I");
0391     t->Branch("nraw",&s.nraw,"nraw/I"); t->Branch("nevt",&s.nevt,"nevt/I"); t->Branch("ntrk",&s.ntrk,"ntrk/I");
0392     t->Branch("vtx",&s.vtx,"vtx/F"); t->Branch("pt",&s.pt,"pt/F"); t->Branch("ntpc",&s.ntpc,"ntpc/F"); t->Branch("adcaxy",&s.dxy,"adcaxy/F"); t->Branch("adcaz",&s.dz,"adcaz/F");
0393     t->Branch("live",s.live,"live[3]/F"); t->Branch("nid",s.nid,"nid[6]/F"); t->Branch("nv0",s.nv0,"nv0[3]/F");
0394     for (auto &x : v){ s = x; t->Fill(); }
0395     t->SetDirectory(0);
0396     if (!haveQA) cout<<"corral::Finalize -- segCells without the QA branches (chunks made before 2026-10-12): events, tracks and live fraction only"<<endl;
0397     return t;
0398 }
0399 
0400 }   // namespace FinQA
0401 #endif