Back to home page

sPhenix code displayed by LXR

 
 

    


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

0001 
0002 #include <TEllipse.h>
0003 #include "corral_class.h"
0004 #include <algorithm>
0005 #include <deque>
0006 #include <set>
0007 #include <sstream>
0008 #include <fstream>
0009 #include <TF1.h>
0010 #include <TH1.h>
0011 #include <TH2.h>
0012 #include <TGraph.h>
0013 #include <TGraph2D.h>
0014 #include <TProfile.h>
0015 #include <TProfile2D.h>
0016 #include <TStyle.h>
0017 #include <TLatex.h>
0018 #include <TLine.h>
0019 #include <TLegend.h>
0020 #include <TBox.h>
0021 #include <TPaveText.h>
0022 #include <TLegendEntry.h>
0023 #include <TCanvas.h>
0024 #include <TProfile.h>
0025 #include <TSystem.h>
0026 
0027 #include "fluct_common.h"
0028 #include "CQResonances.h"   // C(Q) signposts and the ir2=2 Q binning (README_meanpT_from_CQ.md)
0029 #include "QPTPage.h"        // ir2=2 (Q,pT_pair) summary page, shared with Finalize
0030 #include "CalcRm.h"
0031 //#include "CalcR3mid.h"
0032 #include "PairTypes.h"
0033 #include "EtaLabels.h"      // h+, h- pages and maps in eta
0034 #include "finalize_hists.h" // CorralDir(): where dedxGates_KFP.root lives
0035 
0036 double fDEDXexpected(double *x, double *par) {  // bichsel function...
0037     double amass    = par[1];
0038     double momentum = x[0];
0039     //double atno       = 1.0;
0040     //if ( fabs(amass-1.875)<0.1) atno = 2;
0041     return par[0]*(1.0 + ( amass*amass/momentum/momentum ));
0042 }
0043 
0044 //---- README_CQ: C(Q) signposts. A dashed line + label for each CQResonances.h entry of this species pair
0045 //---- inside the current pad's x range (the same list sets the fine Q bins of ir2=2).
0046 void DrawCQSignposts(int ipid1, int ipid2, double ymin, double ymax, double xmax){
0047     int iy  = 0;
0048     for (const CQResonance &p : CQ_RESONANCES){
0049         if (!CQResonanceMatches(p,ipid1,ipid2)) continue;
0050         double q    = CQResonanceQ(p.M,p.m1,p.m2);
0051         if (q<=0. || q>=xmax) continue;
0052         TLine *ln   = new TLine(q,ymin,q,ymax);
0053         ln->SetLineStyle(2); ln->SetLineColor(kGray+2); ln->Draw();
0054         TLatex *tx  = new TLatex(q,ymax-(0.16+0.06*iy)*(ymax-ymin),Form(" %s (%.3f)",p.lab,q));
0055         tx->SetTextFont(42); tx->SetTextSize(0.030); tx->SetTextColor(kGray+2); tx->Draw();
0056         ++iy;
0057     }
0058 }
0059 
0060 //---- offline "worse-daughter-track PV_DCA" -- see README_treeHandoff.md section 6:
0061 //---- KFParticle_sPHENIX's own online version of this cut is broken (calcMinPV_DCA()
0062 //---- pulls the minimum over every vertex in the whole trigger frame, not just the
0063 //---- crossing-matched one), so it was left un-enforced at production time. it1/it2 --
0064 //---- via v0indtr1[iv0]/v0indtr2[iv0] -- index into THIS ROW's track arrays, so dcaxy/
0065 //---- dcaz here are already correctly crossing-matched to this candidate's own vertex.
0066 double WorseDaughterPVDCA(TTreeReaderArray<Float_t> &dcaxy, TTreeReaderArray<Float_t> &dcaz, int it1, int it2){
0067     double dca1 = sqrt(dcaxy[it1]*dcaxy[it1] + dcaz[it1]*dcaz[it1]);
0068     double dca2 = sqrt(dcaxy[it2]*dcaxy[it2] + dcaz[it2]*dcaz[it2]);
0069     return (dca1>dca2) ? dca1 : dca2;
0070 }
0071 
0072 //---- worse-track PV_DCA thresholds (cm), p-cuts working point (tighter, purity-floor --
0073 //---- e-cuts = efficiency-oriented, p-cuts = purity-oriented, per README_settingKFPcuts.md
0074 //---- naming) -- from README_settingKFPcuts.md. The *_sig / *_sig_xy significance variants
0075 //---- recommended alongside these can't be computed here: Collect's outTree has no
0076 //---- per-track DCA-uncertainty branch, only the plain dcaxy/dcaz used above.
0077 //---- LA: Part 4 p-cuts value (0.0091 cm), skeleton unchanged by the Part 5 ana532
0078 //---- re-derivation (LA's re-derived lever was PV_DCA_sig_xy on top of this, not a
0079 //---- replacement for it).
0080 //---- AL: Part 5's ana532-re-derived p-cuts value (0.057 cm), NOT Part 4's ana532_0 value
0081 //---- (0.050 -- close by coincidence at p-cuts, but Part 4's e-cuts value of 0.050 is
0082 //---- wrong for this dataset) -- this run's raw-hit tag (ana532_nocdbtag_v001,
0083 //---- README_treeHandoff.md section 2) matches Part 5's "ana532" sample, where Part 5
0084 //---- found ana532's p-cuts at this threshold actually beat Part 4's original at the
0085 //---- same efficiency floor.
0086 static const double LA_WORSEDCA_CUT = 0.0091;
0087 static const double AL_WORSEDCA_CUT = 0.057;
0088 
0089 //---------------------------------------------------------------------------
0090 void corral::Loop(){
0091 
0092     if (fChain == 0) return;
0093 
0094     //---- RunIndex (runindex.root, hRunIndex, kRunIndex, hri_* QA hists) is disabled --
0095     //---- it's a QA-only convenience for plotting vs. a chronological run/segment
0096     //---- index and isn't needed to process events or fill CFs. The hRunIndex member is
0097     //---- gone (2026-09-26); the hri_* histogram block below is also disabled.
0098     //int NRUNSSEEN = 0;
0099     //cout<<"Reading ~/corral/runindex.root..."<<endl;
0100     //  fri->cd();
0101     //  TH1D* htemp     = (TH1D*)fri->Get("hnrs");
0102     //  if (htemp){
0103     //      NRUNSSEEN   = htemp->GetBinContent(1);
0104     //      cout<<"NRUNSSEEN = "<<NRUNSSEEN<<endl;
0105     //      hRunIndex   = (TH2D*)fri->Get("hrunindex");
0106     //      hRunIndex   ->SetDirectory(0);
0107     //      delete htemp; htemp=0;
0108     //  }
0109     //fri->Close();
0110     const int NCOUNTERS     = 10;
0111     int counters[NCOUNTERS] = {0};
0112     long nv0fixEta=0,nv0fixPhi=0,nv0fixMass=0,nv0fixCtau=0;
0113     long nv0Paired[3]={0,0,0}, nv0All[3]={0,0,0}, nPeakDauNotIndv0=0;   // README_PID.md sec 12: V0s in the pairs, daughters indv0 missed   // README_v0etaSpike.md: V0s whose eta/phi/mass/ctau branch was a 0 sentinel, recomputed
0114     //
0115     //---- set up dedx curves...
0116     const int NPART             =  4;
0117     const double masses[NPART]  = {0.139570392,0.493677,0.9382720894,1.875612945};
0118     TF1 *fdedxexp[NPART];
0119     for (int ip=0;ip<NPART;ip++){
0120         fdedxexp[ip]    = new TF1(Form("fdedxexp%d",ip),fDEDXexpected,0.1,5.1,2);
0121         fdedxexp[ip]    ->SetNpx(1000);
0122         fdedxexp[ip]    ->SetParameter(0, 200.0);       // free! (overall dE/dx scale -- was 320, rescaled for this data)
0123         fdedxexp[ip]    ->SetParameter(1,masses[ip]);   // hypothesis mass
0124         fdedxexp[ip]    ->SetLineWidth(1);
0125         fdedxexp[ip]    ->SetLineColor(1);
0126     }   
0127     //
0128     //---- README_PID.md (2026-09-28): KFP dE/dx gates (dedxGates_KFP.root, README_dedxGates.md), g_<sp>_lo/_hi
0129     //---- vs |p| (GeV/c). Applied to dedxKFP (what KFP itself cuts on), not dedx70s. Undefined outside
0130     //---- 0.1<|p|<5 GeV/c -> unidentified there. Order pi, then p, then K: a track takes the first gate it is in.
0131     const int NGATE             = 4;                    // 0 pi, 1 K, 2 p, 3 d (d: QA overlay only)
0132     const char* gateSp[NGATE]   = {"pi","K","p","d"};
0133     TGraph *gGateLo[NGATE]      = {0}, *gGateHi[NGATE] = {0};
0134     const double GATE_PMIN      = 0.1, GATE_PMAX = 5.0;
0135     if (!doOldPID){
0136         TString gname   = CorralDir() + "dedxGates_KFP.root";
0137         TDirectory *dsave   = gDirectory;
0138         TFile *fgate    = TFile::Open(gname.Data(),"READ");
0139         if (!fgate || fgate->IsZombie()){ cout<<"corral::Loop -- cannot open dE/dx gates "<<gname<<" (\"oldpid\" runs without them), exit"<<endl; exit(1); }
0140         for (int ig=0;ig<NGATE;ig++){
0141             TGraph *lo  = (TGraph*)fgate->Get(Form("g_%s_lo",gateSp[ig]));
0142             TGraph *hi  = (TGraph*)fgate->Get(Form("g_%s_hi",gateSp[ig]));
0143             if (!lo || !hi){ cout<<"corral::Loop -- "<<gname<<" has no g_"<<gateSp[ig]<<"_lo/_hi, exit"<<endl; exit(1); }
0144             gGateLo[ig] = (TGraph*)lo->Clone(Form("gGateLo_%s",gateSp[ig]));
0145             gGateHi[ig] = (TGraph*)hi->Clone(Form("gGateHi_%s",gateSp[ig]));
0146         }
0147         fgate->Close(); delete fgate;
0148         dsave->cd();
0149         cout<<"corral::Loop -- PID: KFP dE/dx gates from "<<gname<<" (pi, then p outside K, then K outside p, on dedxKFP, "<<GATE_PMIN<<"<p<"<<GATE_PMAX<<")"<<endl;
0150     } else {
0151         cout<<"corral::Loop -- PID: legacy (oldpid) -- pi = dedx70s<400 at any momentum, no p or K"<<endl;
0152     }
0153     auto inGate = [&](int ig, double p, double dedx){
0154         return dedx>=gGateLo[ig]->Eval(p) && dedx<=gGateHi[ig]->Eval(p);
0155     };
0156     //---- README_PID.md sec 11: is V0 iv0 in its mass peak? (v0mass can be an exact-0 sentinel: rebuild it from E, p)
0157     auto v0InPeak   = [&](int iv0){
0158         double m    = v0mass[iv0];
0159         if (m==0.0){ double m2 = v0ene[iv0]*v0ene[iv0] - v0ptot[iv0]*v0ptot[iv0]; m = (m2>0.) ? sqrt(m2) : 0.; }
0160         int k   = (v0pid[iv0]==310) ? 0 : (v0pid[iv0]==3122) ? 1 : (v0pid[iv0]==-3122) ? 2 : -1;
0161         if (k<0) return true;
0162         return fabs(m-V0PEAK_MU[k]) < V0PEAK_NSIG*V0PEAK_SIG[k];
0163     };
0164     //---- PID of track it: 0 pi, 1 K, 2 p, 3 unidentified (README_PID.md sec 1)
0165     auto pidOf  = [&](int it){
0166         int k   = 3;
0167         if (doOldPID){
0168             if (dedx70s[it]<400.) k = 0;
0169         } else if (ptot[it]>GATE_PMIN && ptot[it]<GATE_PMAX && dedxKFP[it]>0.){
0170             double pp   = ptot[it];                                         // each species only below its ptot cap (fluct_common.h)
0171             if      (pp<Species_pmax_pid[0] && inGate(0,pp,dedxKFP[it])) k = 0;     // pions first,
0172             else if (pp<Species_pmax_pid[2] && inGate(2,pp,dedxKFP[it])
0173                      && !inGate(1,pp,dedxKFP[it])) k = 2;                           // then protons outside the K band (K and p overlap near 0.75 GeV/c),
0174             else if (pp<Species_pmax_pid[1] && inGate(1,pp,dedxKFP[it])
0175                      && !inGate(2,pp,dedxKFP[it])) k = 1;                           // then kaons outside the p band (they overlap from 0.45 GeV/c)
0176         }
0177         return k;
0178     };
0179     //
0180     //---- class definitions...
0181     double field            = 1.4;  
0182     bool WeightDensities    = false;                //!!!!!! should be settable!!!!
0183     const int   MAX_CALCR_N             =  200;     //!!! set same as in both CalcRx.h variable ntrkmax!!!
0184     int         calcr_n_1[NPairTypes]   =  {0}; 
0185     int         calcr_n_2[NPairTypes]   =  {0}; 
0186     double      Pvec_1[NPairTypes][MAX_CALCR_N][3];
0187     double      Pvec_2[NPairTypes][MAX_CALCR_N][3];
0188     double      Pvec_1_call[MAX_CALCR_N][3];
0189     double      Pvec_2_call[MAX_CALCR_N][3];
0190     double      eff_1[MAX_CALCR_N];
0191     double      eff_2[MAX_CALCR_N];
0192     //
0193     //---- output base name (2026-09-27): "corral" (was "corral_m", a holdover from the removed convolution
0194     //---- class); "corral_x" in the NOCORRELATIONS build. Older output files keep their corral_m names.
0195     TString ClassStr    = NOCORRELATIONS ? TString("corral_x") : TString("corral");
0196     //
0197     int     thisNMIX    =   10;     //50
0198     double  thisPTNB    =    1;
0199     int     thisMAXMULT =  200;
0200     int     thisYNB0    =   32;     // reverted (sec 13.8.15) after both crossing-on imaging jobs
0201                                         // (92x96 job 40411028, 128x120 job 40411030) confirmed their
0202                                         // binary copy -- back to the permanent production default.
0203                                     // 2026-09-21 (sec 13.8.9/13.8.10): pushed to 52 to study the raw
0204                                     // (dy,dphi)=(0,0) spike's physical footprint (stayed a clean 1x2
0205                                     // bin pattern through 22->32->42->52, no spillover into neighbors --
0206                                     // confirmed delta-function-like, see 13.8.9), then settled back to
0207                                     // 32 (was 22) as the new physics-analysis production default -- a
0208                                     // deliberate, permanent choice, not a revert to the original value.
0209                                     // The split-track candidate SEARCH window is now a fixed box
0210                                     // (PREGATE_DETA/PREGATE_DPHI below) independent of this binning choice,
0211                                     // so changing this default no longer changes what counts as a
0212                                     // split-track candidate (sec 13.8.10).
0213     int     thisPHINB0  =   36;     // reverted (sec 13.8.15) -- see thisYNB0 comment above
0214     int     thisYNB;
0215     int     thisPHINB;
0216     if (FinePhiBinning){  thisYNB = 5; thisPHINB = 48; }
0217     double  thisYL      = -1.1; 
0218     double  thisYU      = +1.1;
0219     //---- 2026-09-30 (user): acceptance = the eta fiducial abs(eta) < 1.1 (the detector edge) and, per species, the rapidity
0220     //---- window abs(y) < Species_yu (fluct_common.h: pi 1.0, K 0.7, p 0.6, K0s/Lambda/Lbar 0.7). 2026-10-05 (user): one y bin
0221     //---- width per pair type (PairTypes_Info[][3], units of 0.01) for y1, y2 and dy; it must divide both windows.
0222     double pairEtaEdge[NPairTypes], pairYL1[NPairTypes], pairYU1[NPairTypes], pairYL2[NPairTypes], pairYU2[NPairTypes], pairDYBW[NPairTypes];
0223     int pairYNB1[NPairTypes], pairYNB2[NPairTypes];
0224     for (int ip=0;ip<NPairTypes;ip++){
0225         pairEtaEdge[ip] = thisYU;
0226         pairYU1[ip]     = IsHadronParticleID(PairTypes_Info[ip][0]) ? Hadron_etau : Species_yu[GetSpecies(PairTypes_Info[ip][0])]; pairYL1[ip] = -pairYU1[ip];  // h+, h-: eta
0227         pairYU2[ip]     = IsHadronParticleID(PairTypes_Info[ip][1]) ? Hadron_etau : Species_yu[GetSpecies(PairTypes_Info[ip][1])]; pairYL2[ip] = -pairYU2[ip];
0228         const int ibw   = PairTypes_Info[ip][3];
0229         const int iw1   = (int)lround(200.*pairYU1[ip]), iw2 = (int)lround(200.*pairYU2[ip]);   // window widths, units of 0.01
0230         if (ibw<=0 || iw1%ibw!=0 || iw2%ibw!=0 || iw1/ibw<2 || iw2/ibw<2){
0231             cout<<"corral::Loop -- pair type "<<ip<<" "<<ParticleIDNames[PairTypes_Info[ip][0]]<<" "<<ParticleIDNames[PairTypes_Info[ip][1]]
0232                 <<": y bin width "<<ibw<<" (x0.01) does not divide the y windows "<<iw1<<" and "<<iw2<<" -- see PairTypes.h, exit"<<endl;
0233             exit(1);
0234         }
0235         pairYNB1[ip]    = iw1/ibw;
0236         pairYNB2[ip]    = iw2/ibw;
0237         pairDYBW[ip]    = 0.01*ibw;
0238         cout<<"corral::Loop -- pair type "<<ip<<" "<<ParticleIDNames[PairTypes_Info[ip][0]]<<" "<<ParticleIDNames[PairTypes_Info[ip][1]]
0239             <<": abs(eta) < "<<pairEtaEdge[ip]<<", abs("<<(IsHadronParticleID(PairTypes_Info[ip][0])?"eta1":"y1")<<") < "<<pairYU1[ip]<<" ("<<pairYNB1[ip]<<" bins), abs("
0240             <<(IsHadronParticleID(PairTypes_Info[ip][1])?"eta2":"y2")<<") < "<<pairYU2[ip]<<" ("<<pairYNB2[ip]<<" bins), "<<(IsHPairType(ip)?"eta and deta":"y and dy")<<" bin width "<<pairDYBW[ip]<<endl;
0241     }
0242     if (!doOldPID) cout<<"corral::Loop -- PID ptot caps: pi "<<Species_pmax_pid[0]<<", p "<<Species_pmax_pid[2]<<", K "<<Species_pmax_pid[1]<<" GeV/c"<<endl;
0243     double  thisPTL     =  0.1;
0244     double  thisPTU     = 20.1;
0245     double  thisPTBWMEV = 2000;     // MeV!     (20 GeV/2 GeV -> 10 PT bins),  
0246     //                          1000,2000,2500,4000,5000,10000,20000
0247     //                            20,  10,   8,   5,   4,    2,    1  
0248     TH1::AddDirectory(kFALSE);
0249         cout<<"MIXING with NMIX="<<thisNMIX<<endl;
0250         CalcRm* R[NPairTypes];
0251     //
0252     cout<<"doCrossing = "<<(int)doCrossing<<endl;
0253     cout<<"   NTPCCUT = "<<NTPCCUT<<endl;
0254     if (doQCut){ cout<<"QCut Enabled..."<<endl;
0255                  cout<<"Qcut value = "<<valQCut<<endl; }
0256     if (doSplitRemoval){ cout<<"SplitRemoval pre-pass Enabled..."<<endl;
0257                           cout<<"pre-pass valSLCut = "<<valSLCut<<", valSiKeyCut = "<<valSiKeyCut<<", valRadialGapCut = "<<valRadialGapCut<<endl; }
0258     if (doULSTest){ cout<<"ULS split-track removal (sec 18.10: TRACK-level, folded into the same pre-pass as LS) Enabled... valULSTestCut = "<<valULSTestCut<<endl; }
0259     cout<<"   FinePhi = "<<(int)FinePhiBinning<<endl;
0260     if (NOCORRELATIONS){
0261         cout<<"Correlations DISABLED..."<<endl;
0262     }
0263     //
0264     if (!NOCORRELATIONS){
0265         for (int ipaty=0;ipaty<NPairTypes;ipaty++){
0266             //
0267             int ipid1           = PairTypes_Info[ipaty][0];
0268             int ipid2           = PairTypes_Info[ipaty][1];
0269             //int kCentralityStyle  = PairTypes_Info[ipaty][2];
0270             int iSpecies1       = GetSpecies(ipid1);
0271             int iSpecies2       = GetSpecies(ipid2);
0272             int iChg1           = ParticleCharge[ipid1];
0273             int iChg2           = ParticleCharge[ipid2];
0274             cout<<"Instantiating CalcR2m/c class for ipaty = "<<ipaty
0275                 <<"\t pid: "<<ParticleIDNames[ipid1]<<" "<<ParticleIDNames[ipid2]<<endl;
0276             thisYNB     = thisYNB0;     // default 
0277             thisPHINB   = thisPHINB0;   // default 
0278             if (IsV0ParticleID(ipid1) || IsV0ParticleID(ipid2)){    // This pairtype includes a V0
0279                 thisYNB     =  8;
0280                 thisPHINB   = 12;
0281             }
0282             //
0283                 R[ipaty]    = new CalcRm();     //---- Instantiate Mixing Class
0284                 R[ipaty]    ->SetNMIX(thisNMIX);
0285                 if (doMixNoAdjTF) R[ipaty]->SetExcludeAdjTF(doXTFClean ? 1 : 2);    // sec 18.23
0286             //
0287             R[ipaty]    ->SetMAXMULT(thisMAXMULT);  //
0288                 R[ipaty]    ->SetField(field);          // needed in Book() for the crossing dirty-side rule
0289             R[ipaty]    ->SetDoCrossing(doCrossing);    // enable pt-ordering in the class, bin-flipping will be done in this code below
0290             R[ipaty]    ->SetFlipDy(    false);     // README_Crossing sec 8 (2026-09-25): pt-ordering flips dphi ONLY; the correction symmetrizes in dphi only
0291             R[ipaty]    ->SetDoBaseline(false);     // enable additive shift of R2 using factorial cumulants
0292             R[ipaty]    ->SetDoQcut(doQCut);        // enable Q cut
0293             R[ipaty]    ->SetTrackSep(valTSepDy,valTSepDphi,true);
0294             R[ipaty]    ->SetInttSep(valISepDphi);      // README_SplitTracks573 sec 29 (off if <= 0)   // min over R (the R = 0.5 m variant is dropped)    // README_SplitTracks573 sec 11 (off if valTSepDy<=0)
0295             R[ipaty]    ->SetQcut( valQCut);        // set Q cut (a lower limit)
0296             //R[ipaty]  ->SetDoMinvCut( doMcut);    // Minv cut only (mixing num & denom, convolution num only)
0297             //R[ipaty]  ->SetDoMinvLLCut(false);    // Minv LL cut only (mixing num & denom, convolution num only)
0298             R[ipaty]    ->SetPid1(ipid1);           //
0299             R[ipaty]    ->SetPid2(ipid2);           //
0300             R[ipaty]    ->SetYNB1(pairYNB1[ipaty]);     // 2026-09-30: per-species y windows, bins per pair type
0301             R[ipaty]    ->SetYL1( pairYL1[ipaty] );
0302             R[ipaty]    ->SetYU1( pairYU1[ipaty] );
0303             R[ipaty]    ->SetYNB2(pairYNB2[ipaty]);
0304             R[ipaty]    ->SetYL2( pairYL2[ipaty] );
0305             R[ipaty]    ->SetYU2( pairYU2[ipaty] );
0306             R[ipaty]    ->SetDYBW(pairDYBW[ipaty]);     // 2026-09-30: dy width per pair type
0307             R[ipaty]    ->SetPHINB(thisPHINB);
0308             R[ipaty]    ->SetPTL1( thisPTL  );
0309             R[ipaty]    ->SetPTU1( thisPTU  );
0310             R[ipaty]    ->SetPTL2( thisPTL  );
0311             R[ipaty]    ->SetPTU2( thisPTU  );
0312             R[ipaty]    ->SetZVTXNB(valZvtxNB); // 16 (sec 38)
0313             R[ipaty]    ->SetZVTXL( -16);
0314             R[ipaty]    ->SetZVTXU( +16);
0315             //
0316             R[ipaty]    ->Book();
0317             //
0318         }   // end PairTypes loop
0319     }   // end nocorrelations
0320     //
0321     TH1::AddDirectory(kTRUE);
0322     //
0323     //---- histograms...
0324     //  
0325     TString OutputFileBase;
0326     if (OutputName!="") OutputFileBase  = OutputName;   // -o (job lists): exactly this name
0327     else
0328     if (RunString.Length()){
0329         OutputFileBase  = TString(Form("%s_%s",ClassStr.Data(),RunString.Data()));
0330     } else {
0331         OutputFileBase  = ClassStr;
0332     }
0333     TString RootFileName    = TString("./root/") + OutputFileBase.Data() + ".root";
0334     TString OutputFileNameO = TString("./pdf/")  + OutputFileBase.Data() + ".ps(";
0335     TString OutputFileName  = TString("./pdf/")  + OutputFileBase.Data() + ".ps";
0336     TString OutputFileNameC = TString("./pdf/")  + OutputFileBase.Data() + ".ps]";
0337     TString OutputFileNameP = TString("./pdf/")  + OutputFileBase.Data() + ".pdf";
0338     //
0339     gStyle->SetHistMinimumZero();
0340     cout<<"Opening root output file "<<RootFileName.Data()<<endl;
0341     TFile *fout         = new TFile(RootFileName.Data(),"RECREATE");
0342     //
0343     //---- run axis for all per-run QA: Run-3 pp (RUNAXIS_*, corral_class.h; was 49700-54200, so every
0344     //---- Run-3 run landed in overflow and these pages were empty). The Nevt-vs-(run,segment) map
0345     //---- (hRunSeg) and its JUSTFILLRUNSEG first-pass mode are gone: they only fed the RunIndex
0346     //---- machinery (disabled, see top of Loop), and condor already says which segments ran.
0347     TH2D *htrigRun  = new TH2D("htrigRun","Trigger vs RunNumber" ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,64,-0.5,63.5);
0348     //
0349     TH1D *htrig0    = new TH1D("htrig0","htrig IDs raw",64,-0.5,63.5);
0350     TH1D *heta0     = new TH1D("heta0","heta raw",240,-1.5,1.5);
0351     TH1D *hphi0     = new TH1D("hphi0","hphi raw",96,-M_PI,M_PI);
0352     TH1D *hpt0      = new TH1D("hpt0","hpt raw" ,200,0,20.0);
0353     TH1D *hntpc0    = new TH1D("hntpc0","hntpc raw",70,0.5,70.5);
0354     TH1D *hnmvtx0   = new TH1D("hnmvtx0","hnmvtx raw",20,-0.5,19.5);
0355     TH1D *hnintt0   = new TH1D("hnintt0","hnintt raw",10,-0.5,9.5);
0356     TH2D *hphieta0  = new TH2D("hphieta0","hphieta raw",75,-1.5,1.5,60,-M_PI,M_PI);
0357     TH2D *hpteta0   = new TH2D("hpteta0","hpteta raw",90,-1.5,1.5,100,0.,20.);
0358     TH2D *hptphi0   = new TH2D("hptphi0","hptphi raw",192,-M_PI,M_PI,100,0.,20.);
0359     TH1D *hdcaxy0   = new TH1D("hdcaxy0","hdcaxy raw",400,-2,2);
0360     TH1D *hdcaz0    = new TH1D("hdcaz0","hdcaz raw",400,-2,2);
0361     TH2D *hdcaxyz0  = new TH2D("hdcaxyz0","hdcaxyz raw",400,-2,2,400,-2,2);
0362     TH1D *hvtxx0    = new TH1D("hvtxx0","hvtxx raw",200,-1,1);
0363     TH1D *hvtxy0    = new TH1D("hvtxy0","hvtxy raw",200,-1,1);
0364     TH1D *hvtxz0    = new TH1D("hvtxz0","hvtxz raw",200,-20,20);
0365     TH1D *hntrk0    = new TH1D("hntrk0","hntrk raw",50,-0.5,49.5);
0366     TH1D *hdedx0    = new TH1D("hdedx0","hdedx raw",500,0,5000.);
0367     TH1D *hndedx0   = new TH1D("hndedx0","hndedx raw",49,-0.5,48.5);
0368     TProfile *hdedxphi0     = new TProfile("hdedxphi0"    ,"raw mean dedx vs phi"       ,48,-M_PI,M_PI,0,5000);
0369     TProfile *hdedxphi_etap0    = new TProfile("hdedxphi_etap0","raw mean dedx vs phi, eta>0",48,-M_PI,M_PI,0,5000);
0370     TProfile *hdedxphi_etan0    = new TProfile("hdedxphi_etan0","raw mean dedx vs phi, eta<0",48,-M_PI,M_PI,0,5000);
0371     //
0372     //---- "kept" (post-AcceptTrack/AcceptEvent) counterparts of the _0 raw histograms above --
0373     //---- drawn as a green overlay on top of the raw ones in the paint section below (cosmetics below).
0374     TH1D *htrig     = new TH1D("htrig","htrig IDs",64,-0.5,63.5);
0375     TH1D *heta      = new TH1D("heta","heta",240,-1.5,1.5);
0376     TH1D *hphi      = new TH1D("hphi","hphi",96,-M_PI,M_PI);
0377     TH1D *hpt       = new TH1D("hpt","hpt ",200,0,20.0);
0378     TH1D *hntpc     = new TH1D("hntpc" ,"hntpc",70,0.5,70.5);
0379     TH1D *hnmvtx    = new TH1D("hnmvtx","hnmvtx",20,-0.5,19.5);
0380     TH1D *hnintt    = new TH1D("hnintt","hnintt",10,-0.5,9.5);
0381     TH2D *hphieta   = new TH2D("hphieta","hphieta",75,-1.5,1.5,60,-M_PI,M_PI);
0382     TH2D *hpteta    = new TH2D("hpteta" ,"hpteta",90,-1.5,1.5,100,0.,20.);
0383     TH2D *hptphi    = new TH2D("hptphi" ,"hptphi",192,-M_PI,M_PI,100,0.,20.);
0384     TH1D *hdcaxy    = new TH1D("hdcaxy","hdcaxy",400,-2,2);
0385     TH1D *hdcaz     = new TH1D("hdcaz","hdcaz",400,-2,2);
0386     TH2D *hdcaxyz   = new TH2D("hdcaxyz","hdcaxyz",400,-2,2,400,-2,2);
0387     //---- range widened 0-20 -> 0-2000 (sec 15): quality has no upper bound enforced by
0388     //---- AcceptTrack() and a real long tail -- the old 0-20 range silently truncated ~35% of
0389     //---- entries into the overflow bin (caught while doing a sec-15 loser-leg comparison that
0390     //---- needed real, untruncated quality means; this production histogram had the exact same
0391     //---- bug independently discovered in the sec-15 diagnostic histograms).
0392     TH1D *hquality  = new TH1D("hquality","hquality (kept tracks)",200,0,2000);
0393     //
0394     //---- split-track SL monitor QA (see README_SplitTracks.md sec 9/10) -- diagnostic only, does
0395     //---- NOT remove anything from PID tagging/QA/CF pair-building any more (that's now CalcRm's
0396     //---- job, sec 10.4). Same shape as the "kept" hists above, filled for tracks this event's SL
0397     //---- test would flag (only computed when doSplitRemoval is set -- see corral_class.h, sec 13.8.6).
0398     //---- Retires the old same-Si-seed proxy mechanism (KILLSPLITTRACKS) in favor of the real layermask-based SL.
0399     TH1D *heta_killed       = new TH1D("heta_killed"   ,"flagged (split-candidate) tracks -- eta"   ,240,-1.5,1.5);
0400     TH1D *hphi_killed       = new TH1D("hphi_killed"   ,"flagged (split-candidate) tracks -- phi"   ,96,-M_PI,M_PI);
0401     TH1D *hpt_killed        = new TH1D("hpt_killed"    ,"flagged (split-candidate) tracks -- pt"    ,200,0,20.0);
0402     TH1D *hntpc_killed      = new TH1D("hntpc_killed"  ,"flagged (split-candidate) tracks -- ntpc"  ,70,0.5,70.5);
0403     TH2D *hphieta_killed    = new TH2D("hphieta_killed","flagged (split-candidate) tracks -- eta vs phi",75,-1.5,1.5,60,-M_PI,M_PI);
0404     TH1D *hdcaxy_killed = new TH1D("hdcaxy_killed" ,"flagged (split-candidate) tracks -- dcaxy" ,400,-2,2);
0405     TH1D *hdcaz_killed      = new TH1D("hdcaz_killed"  ,"flagged (split-candidate) tracks -- dcaz"  ,400,-2,2);
0406     TH1D *hquality_killed   = new TH1D("hquality_killed","flagged (split-candidate) tracks -- quality",200,0,2000); // range widened, see hquality above
0407     //---- new-mechanism-specific plots (sec 10): filled for every same-charge, kinematically-close
0408     //---- candidate pair every event, regardless of doSplitRemoval, so the threshold scan (sec 10.9
0409     //---- step 3) always has something to look at even before a cut value is chosen.
0410     //---- README_SplitTracks573 sec 6 (2026-09-27): DIAGNOSTIC ONLY, no cut reads these. Same-charge
0411     //---- pion pairs near (0,0), split by charge (0: ++, 1: --) and pair pT class (0: min pt < 0.2,
0412     //---- 1: min pt >= 0.2). "surv" = both tracks survive every pre-pass removal (XTF, LS, ULS).
0413     //---- central = |deta|<0.034 && |dphi|<10deg (the hR2_1 central bin), side = |deta|<0.034 && 10<=|dphi|<20deg.
0414     //---- outcome, pairs in central with SKF >= valSiKeyCut: 0 removed (one track flagged), 1 surv, outside
0415     //---- the pregate ellipse, 2 surv, in ellipse, SL and RG both fail, 3 surv, in ellipse, flagged but tied
0416     //---- (no loser), 4 surv, in ellipse, other (e.g. doSplitRemoval off).
0417     TH1D *hD573_SKF_surv_cen[2][2], *hD573_SKF_surv_side[2][2], *hD573_outcome[2][2], *hD573_SL_survInEll[2][2];
0418     TH2D *hD573_pos_survHi[2][2], *hD573_pos_allHi[2][2], *hD573_dphiPtinv_allHi[2], *hD573_dphiDinvpt_allHi[2], *hD573_dphiDinvpt_survHi[2];
0419     //---- README_SplitTracks573 sec 10: LS pregate visualization (one PDF page, LS pions only, both charges).
0420     //---- dphi(deg) vs deta in bins of abs(1/pt1-1/pt2): all LS pion pairs / SKF>=cut pairs / pairs whose two
0421     //---- tracks both survive every pre-pass removal.
0422     static const int NGATEVIS   = 6;
0423     static const double GATEVIS_EDGE[NGATEVIS+1]    = {0.,1.,2.,3.,4.,6.,10.};
0424     TH2D *hGateVis_all[NGATEVIS], *hGateVis_hi[NGATEVIS], *hGateVis_surv[NGATEVIS];
0425     //---- the same SKF>=cut / kept pairs in the dps gate's own frame: abs(dphi) - dphi0 (each pair's own
0426     //---- dphi0 = DPsCentre, R = valDPsR), where the dps gate is ONE fixed ellipse centred at 0 (user, sec 10)
0427     TH2D *hGateVis_hiRes[NGATEVIS], *hGateVis_survRes[NGATEVIS];
0428     TH1D *hGateVis_dinvHi[NGATEVIS];
0429     //---- user 2026-09-28: the same populations in FINE slices of each pair's own dphi0 (= DPsCentre, the gate's
0430     //---- centre), plotted as abs(dphi) vs deta: inside a slice every pair's gate is the drawn ellipse to within
0431     //---- half the slice width, so the red curve is the cut Corral applies (three extra PDF pages after the gate page).
0432     static const int NGATEFINE  = 16;
0433     static const double GATEFINE_W  = 0.4;      // slice width in dphi0 (deg): slices 0-0.4, ..., 6.0-6.4 (dphi0 < ~6.3 at pt > 0.1)
0434     TH2D *hGateFine_hi[NGATEFINE], *hGateFine_hiSurv[NGATEFINE], *hGateFine_surv[NGATEFINE];
0435     //---- user 2026-09-28: gate page row 1 (all LS pairs, signed dphi, index order) in the same fine dphi0 slices
0436     TH2D *hGateFine_all[NGATEFINE];
0437     for (int k=0;k<NGATEFINE;k++){
0438         const char* rng = Form("%.1f#leq#Delta#phi_{0}<%.1f#circ",k*GATEFINE_W,(k+1)*GATEFINE_W);
0439         hGateFine_all[k]    = new TH2D(Form("hGateFine_all_%d",k),Form("all, %s;#Delta#eta;#Delta#phi (deg)",rng),64,-0.08,0.08,192,-12.,12.);
0440         hGateFine_hi[k]     = new TH2D(Form("hGateFine_hi_%d",k),Form("SKF#geqcut, %s;#Delta#eta;|#Delta#phi| (deg)",rng),64,-0.08,0.08,96,0.,12.);
0441         hGateFine_hiSurv[k] = new TH2D(Form("hGateFine_hiSurv_%d",k),Form("SKF#geqcut, both kept, %s;#Delta#eta;|#Delta#phi| (deg)",rng),64,-0.08,0.08,96,0.,12.);
0442         hGateFine_surv[k]   = new TH2D(Form("hGateFine_surv_%d",k),Form("all kept, %s;#Delta#eta;|#Delta#phi| (deg)",rng),64,-0.08,0.08,96,0.,12.);
0443     }
0444     //---- README_SplitTracks573 sec 25: cosmic-ray test of the pi+pi- away-side spike at dy ~ 0 (diagnostic only, no cut reads
0445     //---- these). Accepted, surviving pions; pairs with abs(dy) < 0.1 in three windows: [0] opposite charge, abs(dphi) >= 170 deg
0446     //---- (the spike); [1] opposite charge, 150 <= abs(dphi) < 160 (control); [2] same charge, abs(dphi) >= 170 (control).
0447     //---- A cosmic muon through the TPC, reconstructed as two tracks out of the vertex: opposite charges, pt1 ~ pt2,
0448     //---- a near-vertical axis (phi ~ +-90 deg), eta ~ 0, mirrored DCAs, few other tracks.
0449     //---- README_SplitTracks573 sec 38: the central-membrane (CM) hole in R2(y1,y2) (diagnostic only, no cut reads these).
0450     //---- Surviving accepted charged tracks, abs(vtxz) < 8 cm. w = (eta + CM_K vtxz) * (vtxz<0 ? +1 : -1): the hole centre at
0451     //---- w ~ 0 in every slice (hetazvtx: minimum at eta ~ -0.018 vtxz), the sharp edge at w < 0, the slow recovery at w > 0.
0452     //---- Pairs with both tracks in the band (CM_BLO < w < CM_BHI, [0]) or both in the control (CM_CLO < abs(w) < CM_CHI, [1]);
0453     //---- second index 0 = opposite charge, 1 = same charge. A particle crossing the CM rebuilt as two TPC pieces: dphi ~ deta ~ 0,
0454     //---- short ntpc each, inner / outer TPC layers, shared silicon.
0455     static const double CM_K = 0.018, CM_BLO = -0.03, CM_BHI = 0.05, CM_CLO = 0.20, CM_CHI = 0.35;
0456     TH1D *hCM_w     = new TH1D("hCM_w","CM study: surviving tracks, abs(vtxz)<8;w = (#eta + 0.018 z_{vtx}) sign(-z_{vtx});tracks",200,-0.5,0.5);
0457     TH2D *hCM_w_ntpc    = new TH2D("hCM_w_ntpc","CM study: ntpc vs w;w;ntpc",100,-0.5,0.5,50,0.,50.);
0458     TH2D *hCM_w_xing    = new TH2D("hCM_w_xing","CM study: w vs the event's crossing;w;crossing",100,-0.5,0.5,24,-100.,500.);
0459     TH2D *hCM_nbnc  = new TH2D("hCM_nbnc","CM study: per event, tracks in the band vs in the control;n_{control};n_{band}",40,0.,40.,10,0.,10.);    // event-by-event hole depth
0460     TH2D *hCM_w_tpcfl[2];       // first vs last TPC layer (0-47) of tracks in the band [0] / control [1]
0461     TH2D *hCM_dphideta[2][2], *hCM_ntpc12[2][2], *hCM_nsi12[2][2], *hCM_pt12[2][2];
0462     TH1D *hCM_ntpcsum[2][2], *hCM_skf[2][2], *hCM_dw[2][2];
0463     {
0464         const char* cn[2]   = {"band","control"};
0465         const char* qn[2]   = {"OS","SS"};
0466         for (int c=0;c<2;c++){
0467             hCM_w_tpcfl[c]  = new TH2D(Form("hCM_w_tpcfl_%d",c),Form("CM study, %s tracks;first TPC layer;last TPC layer",cn[c]),48,0.,48.,48,0.,48.);
0468             for (int q=0;q<2;q++){
0469                 hCM_dphideta[c][q]  = new TH2D(Form("hCM_dphideta_%d_%d",c,q),Form("CM study, %s %s pairs;#Delta#eta;#Delta#phi (deg)",cn[c],qn[q]),80,-0.2,0.2,120,-30.,30.);
0470                 hCM_ntpc12[c][q]    = new TH2D(Form("hCM_ntpc12_%d_%d",c,q),Form("CM study, %s %s pairs;ntpc_{1};ntpc_{2}",cn[c],qn[q]),50,0.,50.,50,0.,50.);
0471                 hCM_nsi12[c][q]     = new TH2D(Form("hCM_nsi12_%d_%d",c,q),Form("CM study, %s %s pairs;n_{Si,1};n_{Si,2}",cn[c],qn[q]),8,0.,8.,8,0.,8.);
0472                 hCM_pt12[c][q]      = new TH2D(Form("hCM_pt12_%d_%d",c,q),Form("CM study, %s %s pairs;p_{T,1};p_{T,2}",cn[c],qn[q]),40,0.,2.,40,0.,2.);
0473                 hCM_ntpcsum[c][q]   = new TH1D(Form("hCM_ntpcsum_%d_%d",c,q),Form("CM study, %s %s pairs;ntpc_{1}+ntpc_{2};pairs",cn[c],qn[q]),100,0.,100.);
0474                 hCM_skf[c][q]       = new TH1D(Form("hCM_skf_%d_%d",c,q),Form("CM study, %s %s pairs;SiSplitScore;pairs",cn[c],qn[q]),50,0.,1.0001);
0475                 hCM_dw[c][q]        = new TH1D(Form("hCM_dw_%d_%d",c,q),Form("CM study, %s %s pairs;w_{1}-w_{2};pairs",cn[c],qn[q]),80,-0.2,0.2);
0476             }
0477         }
0478     }
0479     static const int NCOS   = 3;
0480     TH1D *hCos_psum[NCOS], *hCos_minv[NCOS], *hCos_axisBB[NCOS], *hCos_minvBB[NCOS];
0481     TH1D *hCos_axis[NCOS], *hCos_ptAsym[NCOS], *hCos_eta[NCOS], *hCos_npi[NCOS], *hCos_vtxntr[NCOS], *hCos_pt[NCOS], *hCos_etotoh[NCOS];
0482     TH2D *hCos_dcaxy[NCOS], *hCos_dcaz[NCOS];
0483     { const char* wn[NCOS]  = {"ULS, |d#phi|#geq170#circ","ULS, 150#leq|d#phi|<160#circ","LS, |d#phi|#geq170#circ"};
0484       for (int w=0;w<NCOS;w++){
0485         hCos_axis[w]    = new TH1D(Form("hCos_axis_%d",w),Form("%s, |dy|<0.1;#phi of the pair axis, folded to [0,180) (deg)",wn[w]),36,0.,180.);
0486         hCos_ptAsym[w]  = new TH1D(Form("hCos_ptAsym_%d",w),Form("%s, |dy|<0.1;(p_{T1}-p_{T2})/(p_{T1}+p_{T2})",wn[w]),50,-1.,1.);
0487         hCos_eta[w]     = new TH1D(Form("hCos_eta_%d",w),Form("%s, |dy|<0.1;#eta_{1}",wn[w]),44,-1.1,1.1);
0488         hCos_npi[w]     = new TH1D(Form("hCos_npi_%d",w),Form("%s, |dy|<0.1;accepted pions in the event",wn[w]),40,-0.5,39.5);
0489         hCos_vtxntr[w]  = new TH1D(Form("hCos_vtxntr_%d",w),Form("%s, |dy|<0.1;vtxntr",wn[w]),60,-0.5,59.5);
0490         hCos_pt[w]      = new TH1D(Form("hCos_pt_%d",w),Form("%s, |dy|<0.1;mean p_{T} of the pair",wn[w]),40,0.,4.);
0491         hCos_etotoh[w]  = new TH1D(Form("hCos_etotoh_%d",w),Form("%s, |dy|<0.1;etotoh",wn[w]),50,0.,25.);
0492         hCos_dcaxy[w]   = new TH2D(Form("hCos_dcaxy_%d",w),Form("%s, |dy|<0.1;dcaxy_{1};dcaxy_{2}",wn[w]),60,-0.3,0.3,60,-0.3,0.3);
0493         hCos_dcaz[w]    = new TH2D(Form("hCos_dcaz_%d",w),Form("%s, |dy|<0.1;dcaz_{1};dcaz_{2}",wn[w]),60,-0.3,0.3,60,-0.3,0.3);
0494         //---- sec 25.1 (2026-09-29): p1 = -p2 pairs (cosmic halves or decays at rest): relative 3-momentum sum, Minv (pion
0495         //---- masses), and axis / Minv of the clean subset (relative sum < 0.05)
0496         hCos_psum[w]    = new TH1D(Form("hCos_psum_%d",w),Form("%s, |dy|<0.1;|#vec{p}_{1}+#vec{p}_{2}| / (|p_{1}|+|p_{2}|)",wn[w]),50,0.,0.5);
0497         hCos_minv[w]    = new TH1D(Form("hCos_minv_%d",w),Form("%s, |dy|<0.1;M_{inv}(#pi#pi) (GeV)",wn[w]),80,0.,4.);
0498         hCos_axisBB[w]  = new TH1D(Form("hCos_axisBB_%d",w),Form("%s, |dy|<0.1, rel. p sum < 0.05;#phi of the pair axis, folded to [0,180) (deg)",wn[w]),36,0.,180.);
0499         hCos_minvBB[w]  = new TH1D(Form("hCos_minvBB_%d",w),Form("%s, |dy|<0.1, rel. p sum < 0.05;M_{inv}(#pi#pi) (GeV)",wn[w]),80,0.,4.);
0500       } }
0501     //---- README_SplitTracks573 sec 28: TPC sector-gap "spokes". Accepted charged tracks (the sec 6 list, before the
0502     //---- pre-pass removals), [charge 0:+ 1:-][0: vertex phi | 1: phi at R = 0.55 m, phi + q*asin(aR/pt), the sign the
0503     //---- data pick (sec 28)] vs pt. At the vertex a gap fixed in the detector is a curve phi_gap + q*asin(aR/pt) (a spoke bent
0504     //---- oppositely for the two charges); at R it is a vertical line.
0505     TH2D *hSpoke[2][2];
0506     for (int q=0;q<2;q++) for (int f=0;f<2;f++)
0507         hSpoke[q][f]    = new TH2D(Form("hSpoke_%d_%d",q,f),Form("%s, %s;%s (deg);p_{T} (GeV/c)",q?"negative":"positive",f?"#phi at R = 0.55 m":"vertex #phi",f?"#phi(R=0.55 m)":"#phi"),180,-180.,180.,28,0.1,1.5);
0508     //---- TPC acceptance vs time (README.md, segCells): one entry per (run, DST segment) with the TPC hits of the accepted
0509     //---- charged tracks (the hphieta list) per FEE: cell = tpcsector*26 + feeMap (tpcsector = 2*sectorId+side, side 0 =
0510     //---- South z<0; feeMap 0-25 = the TPC_FEE_CHANNEL_MAP index, R1 0-5, R2 6-13, R3 14-25). A hit's FEE = the FEE of the
0511     //---- nearest pad in (tpcsector, layer, phi_local = tpcarclen*2mm / pad radius), from tpcFeeMap/tpc_pad_fee_map_all.txt
0512     //---- (SDCC 2026-10-12, tpcFeeMap/README_tpcFeeMap.md; its phi_local = phi_global - sector center, south mirror
0513     //---- included, as Collect's tpcarclen). tpcarclen is the distortion-corrected phi: hits near FEE edges can land in the
0514     //---- neighbour. nevt = events reaching the track loop, ntrk = accepted charged tracks.
0515     //---- 2026-10-12: one entry per segment READ (nraw rows), also when segveto skips it (veto = 1, nothing else filled);
0516     //---- per-segment QA sums for Finalize's vs-segment pages: nvtx (raw vertices in the TF, summed over nevt), pT, ntpc,
0517     //---- abs(dcaxy), abs(dcaz) (summed over ntrk), identified tracks nid = pi+ pi- K+ K- p pbar, in-peak V0s nv0 = K0S La Lbar.
0518     const int SC_NFEE   = 26, SC_NCELL = 24*SC_NFEE;
0519     std::vector<std::vector<std::pair<float,int>>> scPad(24*48);    // per (tpcsector, layer slot): (phi_local, feeMap), sorted
0520     std::vector<double> scR(48,0.);                                 // mean pad radius per layer slot (mm)
0521     {
0522         TString fn  = CorralDir()+"tpcFeeMap/tpc_pad_fee_map_all.txt";
0523         std::ifstream fm(fn.Data());
0524         if (!fm.good()){ cout<<"corral::Loop -- segCells: cannot open "<<fn<<", exit"<<endl; exit(1); }
0525         std::string line; std::vector<int> nR(48,0); long np = 0;
0526         while (std::getline(fm,line)){
0527             if (line.empty() || line[0]=='#') continue;
0528             std::istringstream is(line); int ebdc, side, sid, sct, lay, phibin, feeHW, feeMap, chan, padnr; std::string mod; double R, phg, phl;
0529             if (!(is>>ebdc>>side>>sid>>sct>>lay>>phibin>>feeHW>>feeMap>>chan>>mod>>padnr>>R>>phg>>phl)) continue;
0530             if (lay<7 || lay>54 || sct<0 || sct>23 || feeMap<0 || feeMap>=SC_NFEE) continue;
0531             scPad[sct*48+lay-7].push_back({(float)phl,feeMap}); scR[lay-7] += R; nR[lay-7]++; np++;
0532         }
0533         for (auto &p : scPad) std::sort(p.begin(),p.end());
0534         for (int L=0;L<48;L++){ if (!nR[L]){ cout<<"corral::Loop -- segCells: no pads for layer "<<L+7<<" in "<<fn<<", exit"<<endl; exit(1); } scR[L] /= nR[L]; }
0535         cout<<"corral::Loop -- segCells: "<<np<<" pads from "<<fn<<" (cells = tpcsector*26 + feeMap)"<<endl;
0536     }
0537     auto scFee = [&](int sct, int L, double u){     // feeMap of the pad nearest to phi_local u
0538         const auto &P = scPad[sct*48+L]; if (P.empty()) return -1;
0539         auto it = std::lower_bound(P.begin(),P.end(),std::make_pair((float)u,-1));
0540         if (it==P.end()) --it; else if (it!=P.begin() && fabs((it-1)->first-u)<fabs(it->first-u)) --it;
0541         return it->second; };
0542     Int_t sc_run=-1, sc_seg=-1, sc_nevt=0, sc_ntrk=0, sc_nraw=0, sc_veto=0, sc_nid[6]={0}, sc_nv0[3]={0};
0543     Float_t sc_nvtx=0, sc_sumpt=0, sc_sumntpc=0, sc_sadxy=0, sc_sadz=0;
0544     std::vector<Float_t> sc_hits(SC_NCELL,0.f);
0545     TTree *tSegCells    = new TTree("segCells","TPC hits of accepted tracks per (run, DST segment) per FEE, cell = tpcsector*26 + feeMap (README.md)");
0546     tSegCells->SetDirectory(fout);
0547     tSegCells->Branch("run",&sc_run,"run/I");
0548     tSegCells->Branch("segment",&sc_seg,"segment/I");
0549     tSegCells->Branch("nevt",&sc_nevt,"nevt/I");
0550     tSegCells->Branch("ntrk",&sc_ntrk,"ntrk/I");
0551     tSegCells->Branch("fee",sc_hits.data(),Form("fee[%d]/F",SC_NCELL));
0552     tSegCells->Branch("nraw",&sc_nraw,"nraw/I");
0553     tSegCells->Branch("veto",&sc_veto,"veto/I");
0554     tSegCells->Branch("nvtx",&sc_nvtx,"nvtx/F");
0555     tSegCells->Branch("sumpt",&sc_sumpt,"sumpt/F");
0556     tSegCells->Branch("sumntpc",&sc_sumntpc,"sumntpc/F");
0557     tSegCells->Branch("sumadcaxy",&sc_sadxy,"sumadcaxy/F");
0558     tSegCells->Branch("sumadcaz",&sc_sadz,"sumadcaz/F");
0559     tSegCells->Branch("nid",sc_nid,"nid[6]/I");
0560     tSegCells->Branch("nv0",sc_nv0,"nv0[3]/I");
0561     auto scFlush = [&](){ if (sc_run>=0 && sc_nraw>0) tSegCells->Fill(); sc_nevt=0; sc_ntrk=0; sc_nraw=0; sc_veto=0; sc_nvtx=0; sc_sumpt=0; sc_sumntpc=0; sc_sadxy=0; sc_sadz=0;
0562         std::fill(sc_nid,sc_nid+6,0); std::fill(sc_nv0,sc_nv0+3,0); std::fill(sc_hits.begin(),sc_hits.end(),0.f); };
0563     //---- README_SplitTracks573 sec 15: residual and charge-asymmetry diagnostics (no cut reads these).
0564     //---- Silicon match code per pair: x = MVTX (denM*4 + numM: layers either / both-matching track has,
0565     //---- 0-15), y = INTT (denI*5 + numI, 0-24). For SKF>=cut LS pion pairs in the central bin:
0566     //---- removed / surviving in gate / at 0.04 <= abs(deta) < 0.05 (the tail seen on the gate page).
0567     TH2D *hD573_match[3];
0568     const char* matchName[3]    = {"removed","survInGate","detaTail"};
0569     for (int k=0;k<3;k++) hD573_match[k] = new TH2D(Form("hD573_match_%s",matchName[k]),Form("LS pairs, SKF#geqcut, %s;MVTX 4#timesden+num;INTT 5#timesden+num",matchName[k]),16,-0.5,15.5,25,-0.5,24.5);
0570     //---- sec 19: losers per removal path and charge (accepted pions only), vs pt. Path: 0 LS duplicate (SL),
0571     //---- 1 LS complementary (RadialGap), 2 LS MVTX match (path 3), 3 ULS veto, 4 XTF cleaner, 5 looper veto (sec 35).
0572     TH1D *hLoserPath_pt[6][2];
0573     { const char* pn[6]={"dup","comp","mvtx","uls","xtf","loop"};
0574       for (int k=0;k<6;k++) for (int c=0;c<2;c++) hLoserPath_pt[k][c] = new TH1D(Form("hLoserPath_pt_%s_%s",pn[k],c==0?"pos":"neg"),Form("losers, path %s, %s;p_{T}",pn[k],c==0?"#pi+":"#pi-"),40,0,2); }
0575     //---- per-charge accepted pions and LS-path losers vs pt, eta, phi, ntpc ([0] pi+, [1] pi-)
0576     TH1D *hAsym_all_pt[2], *hAsym_los_pt[2], *hAsym_all_eta[2], *hAsym_los_eta[2], *hAsym_all_phi[2], *hAsym_los_phi[2], *hAsym_all_ntpc[2], *hAsym_los_ntpc[2];
0577     for (int c=0;c<2;c++){
0578         const char* cn  = c==0 ? "pos" : "neg";
0579         hAsym_all_pt[c]     = new TH1D(Form("hAsym_all_pt_%s",cn),Form("accepted pions %s;p_{T}",cn),40,0,2);
0580         hAsym_los_pt[c]     = new TH1D(Form("hAsym_los_pt_%s",cn),Form("LS-path losers %s;p_{T}",cn),40,0,2);
0581         hAsym_all_eta[c]    = new TH1D(Form("hAsym_all_eta_%s",cn),Form("accepted pions %s;#eta",cn),22,-1.1,1.1);
0582         hAsym_los_eta[c]    = new TH1D(Form("hAsym_los_eta_%s",cn),Form("LS-path losers %s;#eta",cn),22,-1.1,1.1);
0583         hAsym_all_phi[c]    = new TH1D(Form("hAsym_all_phi_%s",cn),Form("accepted pions %s;#phi",cn),72,-M_PI,M_PI);
0584         hAsym_los_phi[c]    = new TH1D(Form("hAsym_los_phi_%s",cn),Form("LS-path losers %s;#phi",cn),72,-M_PI,M_PI);
0585         hAsym_all_ntpc[c]   = new TH1D(Form("hAsym_all_ntpc_%s",cn),Form("accepted pions %s;ntpc",cn),49,-0.5,48.5);
0586         hAsym_los_ntpc[c]   = new TH1D(Form("hAsym_los_ntpc_%s",cn),Form("LS-path losers %s;ntpc",cn),49,-0.5,48.5);
0587     }   // abs(1/pt1-1/pt2) of the SKF>=cut pairs per column: its mean places the row-1 ellipses
0588     for (int ib=0;ib<NGATEVIS;ib++){
0589         const char* rng = Form("%.0f<|1/p_{T1}-1/p_{T2}|<%.0f",GATEVIS_EDGE[ib],GATEVIS_EDGE[ib+1]);
0590         hGateVis_all[ib]    = new TH2D(Form("hGateVis_all_%d",ib),Form("LS #pi pairs, all, %s;#Delta#eta;#Delta#phi (deg)",rng),50,-0.05,0.05,96,-12,12);
0591         hGateVis_hi[ib]     = new TH2D(Form("hGateVis_hi_%d",ib),Form("LS #pi pairs, SiSplitScore#geqcut, %s;#Delta#eta;#Delta#phi (deg)",rng),50,-0.05,0.05,96,-12,12);
0592         hGateVis_surv[ib]   = new TH2D(Form("hGateVis_surv_%d",ib),Form("LS #pi pairs, both tracks kept, %s;#Delta#eta;#Delta#phi (deg)",rng),50,-0.05,0.05,96,-12,12);
0593         hGateVis_dinvHi[ib] = new TH1D(Form("hGateVis_dinvHi_%d",ib),Form("SiSplitScore#geqcut, %s;|1/p_{T1}-1/p_{T2}|",rng),100,GATEVIS_EDGE[ib],GATEVIS_EDGE[ib+1]);
0594         hGateVis_hiRes[ib]  = new TH2D(Form("hGateVis_hiRes_%d",ib),Form("SiSplitScore#geqcut, %s;#Delta#eta;|#Delta#phi|-#Delta#phi_{0} (deg)",rng),50,-0.05,0.05,128,-8,8);
0595         hGateVis_survRes[ib]    = new TH2D(Form("hGateVis_survRes_%d",ib),Form("both tracks kept, %s;#Delta#eta;|#Delta#phi|-#Delta#phi_{0} (deg)",rng),50,-0.05,0.05,128,-8,8);
0596     }
0597     for (int ic=0;ic<2;ic++){
0598         const char* cn = ic==0 ? "pp" : "mm";
0599         for (int ipc=0;ipc<2;ipc++){
0600             const char* pn = ipc==0 ? "lopt" : "hipt";
0601             hD573_SKF_surv_cen[ic][ipc] = new TH1D(Form("hD573_SKF_surv_cen_%s_%s",cn,pn),Form("surviving %s pairs, central, %s -- SiSplitScore",cn,pn),44,-1.05,1.05);
0602             hD573_SKF_surv_side[ic][ipc]    = new TH1D(Form("hD573_SKF_surv_side_%s_%s",cn,pn),Form("surviving %s pairs, dphi sideband, %s -- SiSplitScore",cn,pn),44,-1.05,1.05);
0603             hD573_outcome[ic][ipc]      = new TH1D(Form("hD573_outcome_%s_%s",cn,pn),Form("%s pairs, central, SKF>=cut, %s -- 0 removed 1 outEll 2 SL+RGfail 3 tie 4 other",cn,pn),5,-0.5,4.5);
0604             hD573_SL_survInEll[ic][ipc] = new TH1D(Form("hD573_SL_survInEll_%s_%s",cn,pn),Form("surviving %s pairs in ellipse, SKF>=cut, %s -- SL",cn,pn),120,-1.1,1.1);
0605             hD573_pos_survHi[ic][ipc]   = new TH2D(Form("hD573_pos_survHi_%s_%s",cn,pn),Form("surviving %s pairs, SKF>=cut, %s -- dphi(deg) vs deta",cn,pn),50,-0.05,0.05,80,-20,20);
0606             hD573_pos_allHi[ic][ipc]    = new TH2D(Form("hD573_pos_allHi_%s_%s",cn,pn),Form("all %s pairs, SKF>=cut, %s -- dphi(deg) vs deta",cn,pn),50,-0.05,0.05,80,-20,20);
0607         }
0608         hD573_dphiDinvpt_allHi[ic]  = new TH2D(Form("hD573_dphiDinvpt_allHi_%s",cn),Form("all %s pairs, |deta|<0.05, SKF>=cut -- |dphi|(deg) vs |1/pt1-1/pt2|",cn),120,0,12,120,0,30);
0609         hD573_dphiDinvpt_survHi[ic] = new TH2D(Form("hD573_dphiDinvpt_survHi_%s",cn),Form("surviving %s pairs, |deta|<0.05, SKF>=cut -- |dphi|(deg) vs |1/pt1-1/pt2|",cn),120,0,12,120,0,30);
0610         hD573_dphiPtinv_allHi[ic]   = new TH2D(Form("hD573_dphiPtinv_allHi_%s",cn),Form("all %s pairs, |deta|<0.05, SKF>=cut -- dphi(deg) vs 2/(pt1+pt2)",cn),50,0,10,80,-20,20);
0611     }
0612     TH1D *hSL_cand          = new TH1D("hSL_cand","split-candidate pairs -- Splitting Level (STAR sec 2.4.1)",120,-1.1,1.1);
0613     TH2D *hntpc_ij_cand = new TH2D("hntpc_ij_cand","split-candidate pairs -- ntpc[i] vs ntpc[j] (sec 9.6)",49,-0.5,48.5,49,-0.5,48.5);
0614     //---- SiKeyFrac (sec 12a.4/13.8) monitor, same convention as hSL_cand above
0615     TH1D *hSiKeyFrac_cand   = new TH1D("hSiKeyFrac_cand","split-candidate pairs -- SiKeyFrac (sec 12a.4)",44,-1.05,1.05);
0616     //---- MVTX/INTT split diagnostics (sec 13.9) -- also feed the production discriminator below
0617     //---- (SiSplitScore, fluct_common.h) as of this update. See fluct_common.h::SiKeyFracSplit for
0618     //---- why: INTT strips are far coarser than MVTX pixels, so an accidental match on an INTT slot
0619     //---- is intrinsically more likely, yet the combined SiKeyFrac above weighs both sub-detectors'
0620     //---- slots identically.
0621     TH1D *hMVTXKeyFrac_cand     = new TH1D("hMVTXKeyFrac_cand","split-candidate pairs -- MVTX-only SiKeyFrac (sec 13.9)",42,-1.05,1.05);
0622     TH1D *hINTTKeyFrac_cand     = new TH1D("hINTTKeyFrac_cand","split-candidate pairs -- INTT-only SiKeyFrac (sec 13.9)",42,-1.05,1.05);
0623     TH2D *hMVTXvsINTTavail_cand = new TH2D("hMVTXvsINTTavail_cand","split-candidate pairs -- available slots, MVTX vs INTT (sec 13.9)",4,-0.5,3.5,5,-0.5,4.5);
0624     TH2D *hMVTXvsINTTKeyFrac_cand   = new TH2D("hMVTXvsINTTKeyFrac_cand","split-candidate pairs -- MVTX-only vs INTT-only SiKeyFrac (sec 13.9)",42,-1.05,1.05,42,-1.05,1.05);
0625     //---- production discriminator distribution (sec 13.9) -- what valSiKeyCut is actually gated
0626     //---- against below, as of this update; hSiKeyFrac_cand above is now the LEGACY combined metric,
0627     //---- kept purely as a retained comparison point in case this change needs backing out.
0628     TH1D *hSiSplitScore_cand    = new TH1D("hSiSplitScore_cand","split-candidate pairs -- SiSplitScore, MVTX-primary/INTT-secondary (sec 13.9)",44,-1.05,1.05);
0629     //---- RadialGap (sec 17.11) -- population-wide, every candidate, not just the two restricted
0630     //---- ridge-box buckets checked so far. Lets us see its full distribution and how it relates to
0631     //---- SL/SKF across the whole candidate population, not just the two extreme corners.
0632     TH1D *hRadialGap_cand       = new TH1D("hRadialGap_cand","split-candidate pairs -- RadialGap (sec 17.11)",48,-0.02,1.02);
0633     TH2D *hRadialGap_vs_SL_cand = new TH2D("hRadialGap_vs_SL_cand","split-candidate pairs -- SL vs RadialGap (sec 17.11)",120,-1.1,1.1,48,-0.02,1.02);
0634     TH2D *hRadialGap_vs_SKF_cand    = new TH2D("hRadialGap_vs_SKF_cand","split-candidate pairs -- SiSplitScore vs RadialGap (sec 17.11)",44,-1.05,1.05,48,-0.02,1.02);
0635     //---- sec 17: does INTT actually help discriminate WITHIN the genuinely-partial-MVTX band
0636     //---- (mvtxFrac=1/3 or 2/3), or does it just add scatter? Two independent, purely diagnostic
0637     //---- checks (neither touches doSplitRemoval/any cut): (a) fine (deta,dphi) position maps,
0638     //---- one per partial-MVTX x INTT-agreement sub-population -- true split-track duplicates
0639     //---- should sit essentially exactly at (0,0) like the mvtxFrac==1 reference population, so a
0640     //---- sub-population that's spread across the pregate like the mvtxFrac==0 background instead
0641     //---- looks like INTT noise, not corroboration; (b) INTT-agreement rate in the sideband
0642     //---- (background-only, no duplicates expected, hMVTXvsINTTKeyFrac_far_LS below) vs this
0643     //---- in-box candidate population -- if INTT reads "full match" just as often on unrelated
0644     //---- sideband pairs, its corroboration in the partial band isn't informative. Ranges below
0645     //---- are literal copies of PREGATE_DETA/PREGATE_DPHI (declared later, inside the loop) --
0646     //---- keep in sync if the pregate ever changes.
0647     TH2D *hPos_mvtx13_inttAgree = new TH2D("hPos_mvtx13_inttAgree","cand, mvtxFrac~1/3 & INTT agrees (>=0.75);d#eta;d#phi(deg)",22,-0.022,0.022,22,-4.1,4.1);
0648     TH2D *hPos_mvtx13_inttDisagree  = new TH2D("hPos_mvtx13_inttDisagree","cand, mvtxFrac~1/3 & INTT disagrees (<0.75);d#eta;d#phi(deg)",22,-0.022,0.022,22,-4.1,4.1);
0649     TH2D *hPos_mvtx23_inttAgree = new TH2D("hPos_mvtx23_inttAgree","cand, mvtxFrac~2/3 & INTT agrees (>=0.75);d#eta;d#phi(deg)",22,-0.022,0.022,22,-4.1,4.1);
0650     TH2D *hPos_mvtx23_inttDisagree  = new TH2D("hPos_mvtx23_inttDisagree","cand, mvtxFrac~2/3 & INTT disagrees (<0.75);d#eta;d#phi(deg)",22,-0.022,0.022,22,-4.1,4.1);
0651     TH2D *hPos_mvtx0        = new TH2D("hPos_mvtx0","cand, mvtxFrac==0 (background reference);d#eta;d#phi(deg)",22,-0.022,0.022,22,-4.1,4.1);
0652     TH2D *hPos_mvtx1        = new TH2D("hPos_mvtx1","cand, mvtxFrac==1 (unambiguous-match reference);d#eta;d#phi(deg)",22,-0.022,0.022,22,-4.1,4.1);
0653     //---- sec 17.10: "hot line of death" box test -- characterizes the residual (dy,dphi) excess
0654     //---- that survives the finalized SL<0.18/SKF>=0.10 cut (sec 17.6), reusing sec 15's ULS
0655     //---- "is this a real split track" diagnostics (pt/ptot symmetry, ntpc/quality asymmetry
0656     //---- between legs). Box measured directly off hzoom_S/hzoom_M at the finalized working point
0657     //---- (root/corral_m_mvled_removecut18skf10.root): |dy|<0.010, |dphi|<0.2deg. Two
0658     //---- populations per variable: "survive" = box pairs the CURRENT cut does NOT flag (i.e. what's
0659     //---- still visible in the zoom map after cuts) vs "flagged" = box pairs the cut WOULD remove
0660     //---- (already being removed in production) -- a built-in contrast/sanity check.
0661     TH1D *hptreldiff_ridge_survive      = new TH1D("hptreldiff_ridge_survive","ridge box, NOT flagged by SL/SKF -- relative |pt[i]-pt[j]| (sec 17.10)",100,0,2.1);
0662     TH1D *hptreldiff_ridge_flagged      = new TH1D("hptreldiff_ridge_flagged","ridge box, flagged by SL/SKF -- relative |pt[i]-pt[j]| (sec 17.10)",100,0,2.1);
0663     TH1D *hptotreldiff_ridge_survive    = new TH1D("hptotreldiff_ridge_survive","ridge box, NOT flagged by SL/SKF -- relative |ptot[i]-ptot[j]| (sec 17.10)",100,0,2.1);
0664     TH1D *hptotreldiff_ridge_flagged    = new TH1D("hptotreldiff_ridge_flagged","ridge box, flagged by SL/SKF -- relative |ptot[i]-ptot[j]| (sec 17.10)",100,0,2.1);
0665     TH1D *hqualitydiff_ridge_survive    = new TH1D("hqualitydiff_ridge_survive","ridge box, NOT flagged by SL/SKF -- |quality[i]-quality[j]| (sec 17.10)",200,0,2000);
0666     TH1D *hqualitydiff_ridge_flagged    = new TH1D("hqualitydiff_ridge_flagged","ridge box, flagged by SL/SKF -- |quality[i]-quality[j]| (sec 17.10)",200,0,2000);
0667     TH2D *hntpc_ij_ridge_survive        = new TH2D("hntpc_ij_ridge_survive","ridge box, NOT flagged by SL/SKF -- ntpc[i] vs ntpc[j] (sec 17.10)",49,-0.5,48.5,49,-0.5,48.5);
0668     TH2D *hntpc_ij_ridge_flagged        = new TH2D("hntpc_ij_ridge_flagged","ridge box, flagged by SL/SKF -- ntpc[i] vs ntpc[j] (sec 17.10)",49,-0.5,48.5,49,-0.5,48.5);
0669     //---- test #3 (sec 17.10 follow-up, pt/ptot confirmed redundant -- see sec 15.4, same box
0670     //---- selection pins eta/y so pz collapses onto pt): DCA correlation between legs. A real
0671     //---- single-particle split shares essentially the same trajectory (same DCA); two
0672     //---- coincidentally-nearby independent tracks don't -- nothing about matching (y,phi,pt)
0673     //---- already implies matching DCA, so this is a genuinely independent axis.
0674     TH1D *hdcaxydiff_ridge_survive  = new TH1D("hdcaxydiff_ridge_survive","ridge box, NOT flagged by SL/SKF -- |dcaxy[i]-dcaxy[j]| (sec 17.10)",200,0,1.0);
0675     TH1D *hdcaxydiff_ridge_flagged  = new TH1D("hdcaxydiff_ridge_flagged","ridge box, flagged by SL/SKF -- |dcaxy[i]-dcaxy[j]| (sec 17.10)",200,0,1.0);
0676     TH1D *hdcazdiff_ridge_survive   = new TH1D("hdcazdiff_ridge_survive","ridge box, NOT flagged by SL/SKF -- |dcaz[i]-dcaz[j]| (sec 17.10)",200,0,2.0);
0677     TH1D *hdcazdiff_ridge_flagged   = new TH1D("hdcazdiff_ridge_flagged","ridge box, flagged by SL/SKF -- |dcaz[i]-dcaz[j]| (sec 17.10)",200,0,2.0);
0678     //---- user question (sec 17.19): is dE/dx also similar between legs of a "duplicate" pair, the
0679     //---- way ntpc/quality already show measurable asymmetry (not identical copies)?
0680     TH1D *hdedxdiff_ridge_survive   = new TH1D("hdedxdiff_ridge_survive","ridge box, NOT flagged by SL/SKF -- |dedx70s[i]-dedx70s[j]| (sec 17.19)",200,0,1000);
0681     TH1D *hdedxdiff_ridge_flagged   = new TH1D("hdedxdiff_ridge_flagged","ridge box, flagged by SL/SKF -- |dedx70s[i]-dedx70s[j]| (sec 17.19)",200,0,1000);
0682     //---- user's sharper follow-up (sec 17.19): track-level <dedx> is fluctuation-dominated, not a
0683     //---- clean test. Real test: per-SHARED-TPC-layer cluster position agreement (sec 12b.5/12b.6's
0684     //---- d_mm formula) -- if "duplicate" really means the same physical cluster reused by both
0685     //---- legs, d_mm should sit at ~0 (detector resolution) at every shared layer, not just close.
0686     TH1D *hDmm_ridge_survive        = new TH1D("hDmm_ridge_survive","ridge box, NOT flagged -- d_mm at shared TPC layers, matching sector (sec 17.19)",150,0,300);
0687     TH1D *hDmm_ridge_flagged        = new TH1D("hDmm_ridge_flagged","ridge box, flagged (duplicate) -- d_mm at shared TPC layers, matching sector (sec 17.19)",150,0,300);
0688     TH1D *hSectorMatchFrac_ridge_survive    = new TH1D("hSectorMatchFrac_ridge_survive","ridge box, NOT flagged -- frac of shared TPC layers w/ matching sector (sec 17.19)",22,0,1.1);
0689     TH1D *hSectorMatchFrac_ridge_flagged    = new TH1D("hSectorMatchFrac_ridge_flagged","ridge box, flagged (duplicate) -- frac of shared TPC layers w/ matching sector (sec 17.19)",22,0,1.1);
0690     //---- user question: is the "survive" DCA tightness circular -- do survivors still share a Si
0691     //---- seed (high SKF, just failing SL) or not (low SKF, genuinely no Si match)? "flagged" pairs
0692     //---- share both by definition, so their DCA tightness is close to tautological; this settles
0693     //---- which case "survive" actually is before reading anything into its DCA result.
0694     TH2D *hSLvsSKF_ridge_survive    = new TH2D("hSLvsSKF_ridge_survive","ridge box, NOT flagged by SL/SKF -- SL vs SiSplitScore (sec 17.10)",44,-1.05,1.05,44,-1.05,1.05);
0695     TH2D *hSLvsSKF_ridge_flagged    = new TH2D("hSLvsSKF_ridge_flagged","ridge box, flagged by SL/SKF -- SL vs SiSplitScore (sec 17.10)",44,-1.05,1.05,44,-1.05,1.05);
0696     //---- sec 17.11: radial structure test. SL (pad-row OVERLAP only) is structurally blind to a
0697     //---- clean STAR-fig-1b radial split (one real track, TPC clusters partitioned into disjoint
0698     //---- inner/outer halves) -- such a split forces Nboth=0, Ndiff=Ntot, SL=+1 exactly, identical
0699     //---- to two genuinely independent tracks. The layermask's TPC bit INDEX (bits 7-54, sec 9.1)
0700     //---- corresponds directly to physical pad-row/radius, so this checks what SL discards: per
0701     //---- pair, sort legs by mean occupied TPC-bit index (proxy for mean radius) into
0702     //---- "lower"/"upper", fill occupancy-vs-layer for each. A clean radial split (fig 1b) shows up
0703     //---- as lower/upper occupying disjoint, non-interleaved bit ranges; the KNOWN-duplicate
0704     //---- "flagged" population (SKF>=0.10 && SL<0.18, shares pad-rows by definition) is the negative
0705     //---- control -- it should show heavy lower/upper overlap, not a clean split.
0706     TH1D *hTPClayer_lower_complementary = new TH1D("hTPClayer_lower_complementary","ridge box, SKF>=0.95 & SL>0.9 -- lower-mean-index leg, TPC bit occupancy (sec 17.11)",48,-0.5,47.5);
0707     TH1D *hTPClayer_upper_complementary = new TH1D("hTPClayer_upper_complementary","ridge box, SKF>=0.95 & SL>0.9 -- upper-mean-index leg, TPC bit occupancy (sec 17.11)",48,-0.5,47.5);
0708     TH2D *hTPCmeanIdx_complementary     = new TH2D("hTPCmeanIdx_complementary","ridge box, SKF>=0.95 & SL>0.9 -- mean TPC bit index, lower leg vs upper leg (sec 17.11)",48,-0.5,47.5,48,-0.5,47.5);
0709     TH1D *hTPClayer_lower_flagged       = new TH1D("hTPClayer_lower_flagged","ridge box, flagged by SL/SKF -- lower-mean-index leg, TPC bit occupancy (sec 17.11)",48,-0.5,47.5);
0710     TH1D *hTPClayer_upper_flagged       = new TH1D("hTPClayer_upper_flagged","ridge box, flagged by SL/SKF -- upper-mean-index leg, TPC bit occupancy (sec 17.11)",48,-0.5,47.5);
0711     TH2D *hTPCmeanIdx_flagged       = new TH2D("hTPCmeanIdx_flagged","ridge box, flagged by SL/SKF -- mean TPC bit index, lower leg vs upper leg (sec 17.11)",48,-0.5,47.5,48,-0.5,47.5);
0712     //---- ULS (opposite-charge pi+pi-) diagnostic (README sec 15/11.9 item 3) -- re-checking the
0713     //---- still-open opposite-charge (0,0) spike against the SHARP siclukey-based discriminators
0714     //---- now available, since the only prior check (sec 5) used a much blunter nmvtx/nintt/dcaxy
0715     //---- proxy and found no elevation -- that same blunt proxy badly underestimated the
0716     //---- same-charge signal too (13.9% false-positive rate vs. the real 0.003% once cluster
0717     //---- identity was available), so "no effect" from the old proxy is not conclusive. Purely
0718     //---- diagnostic -- does NOT feed doSplitRemoval/any cut, opposite-charge pairs are never
0719     //---- flagged or removed by anything in this codebase (by design, sec 5).
0720     //---- "_cand"/"LS" = inside the same close box used for the production same-charge cut;
0721     //---- "_far"/sideband = clearly-unrelated control region (0.05<=dr<0.5, same definition as
0722     //---- sec 5's table), for BOTH charge combinations, so a close-vs-sideband elevation ratio
0723     //---- can be read the same way sec 5's original table did, just with a sharper metric.
0724     TH1D *hSL_cand_ULS          = new TH1D("hSL_cand_ULS","ULS (opp-charge) close-box pairs -- Splitting Level (sec 15)",120,-1.1,1.1);
0725     TH1D *hSiKeyFrac_cand_ULS   = new TH1D("hSiKeyFrac_cand_ULS","ULS (opp-charge) close-box pairs -- flat SiKeyFrac (sec 15)",44,-1.05,1.05);
0726     TH1D *hSiSplitScore_cand_ULS    = new TH1D("hSiSplitScore_cand_ULS","ULS (opp-charge) close-box pairs -- MVTX-led SiSplitScore (sec 15)",44,-1.05,1.05);
0727     //---- sec 15 follow-up: SL and fit-quality asymmetry WITHIN the ULS close-box population,
0728     //---- split by MVTX-led SiSplitScore (>=0.60 "high" -- presumed real duplicates, mirrors the
0729     //---- production threshold's own value threshold -- vs <0.60 "low" -- presumed background) --
0730     //---- checking whether the high-SKF subpopulation shows the SAME winner/loser fit-quality
0731     //---- asymmetry the confirmed same-charge duplicates show (sec 9.5), which would support the
0732     //---- "one leg is a poorly-reconstructed fragment, more susceptible to curvature-sign
0733     //---- confusion" mechanism directly, not just cluster-sharing alone.
0734     TH2D *hSLvsSKF_cand_ULS     = new TH2D("hSLvsSKF_cand_ULS","ULS close-box pairs -- SL vs MVTX-led SiSplitScore (sec 15)",60,-1.1,1.1,44,-1.05,1.05);
0735     //---- sec 18.18: SIBLING d_mm (sec 17.19's formula), peak circle vs exact mirror circle vs true
0736     //---- origin -- cheap, reuses raw per-event tpcarclen/tpcz/tpcsector directly, no new buffer.
0737     TH1D *hDmm_ULSridge_sibling = new TH1D("hDmm_ULSridge_sibling","ULS SIBLING, ridge circle;d_mm at shared TPC layer, matching sector (mm);pairs",150,0,300);
0738     TH1D *hDmm_ULSmirror_sibling    = new TH1D("hDmm_ULSmirror_sibling","ULS SIBLING, mirror circle;d_mm at shared TPC layer, matching sector (mm);pairs",150,0,300);
0739     TH1D *hDmm_ULSorigin_sibling    = new TH1D("hDmm_ULSorigin_sibling","ULS SIBLING, true-origin circle;d_mm at shared TPC layer, matching sector (mm);pairs",150,0,300);
0740     TH1D *hntpc_loser_ULS_hiSKF = new TH1D("hntpc_loser_ULS_hiSKF","ULS close-box, SKF>=0.60 -- ntpc of would-be loser leg (sec 15)",49,-0.5,48.5);
0741     TH1D *hntpc_winner_ULS_hiSKF    = new TH1D("hntpc_winner_ULS_hiSKF","ULS close-box, SKF>=0.60 -- ntpc of would-be winner leg (sec 15)",49,-0.5,48.5);
0742     TH1D *hntpc_loser_ULS_loSKF = new TH1D("hntpc_loser_ULS_loSKF","ULS close-box, SKF<0.60 -- ntpc of would-be loser leg (sec 15)",49,-0.5,48.5);
0743     TH1D *hntpc_winner_ULS_loSKF    = new TH1D("hntpc_winner_ULS_loSKF","ULS close-box, SKF<0.60 -- ntpc of would-be winner leg (sec 15)",49,-0.5,48.5);
0744     TH1D *hquality_loser_ULS_hiSKF  = new TH1D("hquality_loser_ULS_hiSKF","ULS close-box, SKF>=0.60 -- quality of would-be loser leg (sec 15)",200,0,2000);
0745     TH1D *hquality_winner_ULS_hiSKF = new TH1D("hquality_winner_ULS_hiSKF","ULS close-box, SKF>=0.60 -- quality of would-be winner leg (sec 15)",200,0,2000);
0746     TH1D *hquality_loser_ULS_loSKF  = new TH1D("hquality_loser_ULS_loSKF","ULS close-box, SKF<0.60 -- quality of would-be loser leg (sec 15)",200,0,2000);
0747     TH1D *hquality_winner_ULS_loSKF = new TH1D("hquality_winner_ULS_loSKF","ULS close-box, SKF<0.60 -- quality of would-be winner leg (sec 15)",200,0,2000);
0748     //---- proper (non-tautological) within-pair asymmetry, symmetric under i<->j swap, unlike the
0749     //---- loser/winner split above (which is a min/max split by construction and, as measured,
0750     //---- carries no discriminating signal -- see README sec 15). |quality[i]-quality[j]| directly
0751     //---- tests whether real-duplicate-like ULS pairs (high SKF) show a BIGGER internal quality
0752     //---- gap than background-like ones (low SKF), independent of which leg happens to "win."
0753     TH1D *hqualitydiff_ULS_hiSKF    = new TH1D("hqualitydiff_ULS_hiSKF","ULS close-box, SKF>=0.60 -- |quality[i]-quality[j]| (sec 15)",200,0,2000);
0754     TH1D *hqualitydiff_ULS_loSKF    = new TH1D("hqualitydiff_ULS_loSKF","ULS close-box, SKF<0.60 -- |quality[i]-quality[j]| (sec 15)",200,0,2000);
0755     //---- pt-similarity mechanism test (sec 15, user's proposal): if the high-SKF ULS population
0756     //---- really is one physical trajectory with a curvature-sign error, both legs should
0757     //---- reconstruct to roughly the SAME |pt| (a sign flip on a shared fit barely changes the
0758     //---- fitted radius) -- a much tighter loser/winner pt correlation than the low-SKF background
0759     //---- population would show. pt is bounded [0.1,20.1] by AcceptTrack(), unlike quality (no
0760     //---- learned that lesson the hard way just now with the quality histograms' overflow bug) --
0761     //---- range chosen accordingly, no overflow risk expected.
0762     //---- Relative difference (symmetric under i<->j, non-tautological, same spirit as
0763     //---- hqualitydiff above): |pt[i]-pt[j]| / (0.5*(pt[i]+pt[j])), range [0,2).
0764     TH1D *hptreldiff_ULS_hiSKF  = new TH1D("hptreldiff_ULS_hiSKF","ULS close-box, SKF>=0.60 -- relative |pt[i]-pt[j]| (sec 15)",100,0,2.1);
0765     TH1D *hptreldiff_ULS_loSKF  = new TH1D("hptreldiff_ULS_loSKF","ULS close-box, SKF<0.60 -- relative |pt[i]-pt[j]| (sec 15)",100,0,2.1);
0766     //---- ptot (full 3-momentum magnitude, already a branch) cross-check (user's suggestion) --
0767     //---- with eta already tightly fixed by the box (|deta|<0.02), pt and pz=pt*sinh(eta) are
0768     //---- largely redundant for THIS population, so this is mainly a robustness cross-check
0769     //---- rather than genuinely independent information -- still cheap and worth confirming
0770     //---- directly rather than assuming. Relative-difference form again, so no need to guess an
0771     //---- absolute range for ptot itself (learned that lesson from the quality histograms).
0772     TH1D *hptotreldiff_ULS_hiSKF    = new TH1D("hptotreldiff_ULS_hiSKF","ULS close-box, SKF>=0.60 -- relative |ptot[i]-ptot[j]| (sec 15)",100,0,2.1);
0773     TH1D *hptotreldiff_ULS_loSKF    = new TH1D("hptotreldiff_ULS_loSKF","ULS close-box, SKF<0.60 -- relative |ptot[i]-ptot[j]| (sec 15)",100,0,2.1);
0774     //---- Q_inv check (sec 15, user's proposal): the real production C(Q) plot (root/corral_m.root,
0775     //---- no RunString, sec 14.4) shows a genuine low-Q Coulomb enhancement below ~10 MeV for ULS
0776     //---- pion pairs (expected physics) PLUS a distinct second enhancement around Q~25 MeV. Checked
0777     //---- directly: the rho(770) (M~775 MeV) maps to Q_inv=sqrt(M^2-4*mpi^2)~723 MeV -- nowhere near
0778     //---- 25 MeV, and outside the hCQ histograms' own 0-300 MeV range entirely, so NOT the rho. Given
0779     //---- high-SKF ULS candidates were just measured to have large pt/ptot mismatch between legs
0780     //---- (~63% relative, sec 15) despite being angularly adjacent (that's the box definition), their
0781     //---- Q_inv (which depends on the FULL 4-momentum difference, not just angle) should land away
0782     //---- from 0 at some characteristic value, not smear across the whole range like independent
0783     //---- background pairs -- checking directly whether that characteristic value lands near the
0784     //---- observed ~25 MeV bump. Same identical-mass Q_inv formula as CalcRm::PairInfo
0785     //---- (Qinv=sqrt(dPx^2+dPy^2+dPz^2-dPE^2)), built directly from (pt,eta,phi) since eta/pt alone
0786     //---- geometrically fix the 3-momentum regardless of assumed mass -- no need to go through
0787     //---- CalcRm's own rapidity-based internal parameterization.
0788     TH1D *hQinv_cand_ULS_hiSKF  = new TH1D("hQinv_cand_ULS_hiSKF","ULS close-box, SKF>=0.60 -- Q_inv (sec 15)",300,0,0.3);
0789     TH1D *hQinv_cand_ULS_loSKF  = new TH1D("hQinv_cand_ULS_loSKF","ULS close-box, SKF<0.60 -- Q_inv (sec 15)",300,0,0.3);
0790     //---- sec 15 follow-up: the box-restricted Qinv check above came back null (neither hiSKF nor
0791     //---- loSKF showed a localized peak near the real hCQ_0's observed Q~25MeV bump) -- the tight
0792     //---- (dy,dphi) box likely biases toward the SMALLEST-divergence (hence smallest Q_inv)
0793     //---- duplicates, systematically excluding whatever produces the mid-Q feature. This time: NO
0794     //---- angular restriction at all -- every opposite-charge pion pair in the event, correlating
0795     //---- SiSplitScore directly against Q_inv, same binning as hCQ_0 for direct shape comparison.
0796     TH1D *hQinv_allULS_hiSKF    = new TH1D("hQinv_allULS_hiSKF","ALL ULS pairs (no box), SKF>=0.60 -- Q_inv (sec 15)",300,0,0.3);
0797     TH1D *hQinv_allULS_loSKF    = new TH1D("hQinv_allULS_loSKF","ALL ULS pairs (no box), SKF<0.60 -- Q_inv (sec 15)",300,0,0.3);
0798     //---- full 2D picture, loser/winner convention (sec 9.5 rule, same as elsewhere in this file).
0799     TH2D *hpt_loser_vs_winner_ULS_hiSKF = new TH2D("hpt_loser_vs_winner_ULS_hiSKF","ULS close-box, SKF>=0.60 -- pt[loser] vs pt[winner] (sec 15)",100,0,20,100,0,20);
0800     TH2D *hpt_loser_vs_winner_ULS_loSKF = new TH2D("hpt_loser_vs_winner_ULS_loSKF","ULS close-box, SKF<0.60 -- pt[loser] vs pt[winner] (sec 15)",100,0,20,100,0,20);
0801     TH1D *hSL_far_LS            = new TH1D("hSL_far_LS","LS (same-charge) sideband (0.05<=dr<0.5) pairs -- Splitting Level (sec 15)",120,-1.1,1.1);
0802     TH1D *hSiKeyFrac_far_LS     = new TH1D("hSiKeyFrac_far_LS","LS (same-charge) sideband pairs -- flat SiKeyFrac (sec 15)",44,-1.05,1.05);
0803     TH1D *hSiSplitScore_far_LS  = new TH1D("hSiSplitScore_far_LS","LS (same-charge) sideband pairs -- MVTX-led SiSplitScore (sec 15)",44,-1.05,1.05);
0804     //---- RadialGap sideband reference (sec 17.11) -- background-only population, to check RadialGap
0805     //---- doesn't just reflect generic uncorrelated tracks (it shouldn't: real independent
0806     //---- full-length tracks typically span, and so overlap on, most of the 48 TPC layers).
0807     TH1D *hRadialGap_far_LS = new TH1D("hRadialGap_far_LS","LS sideband pairs -- RadialGap (sec 17.11)",48,-0.02,1.02);
0808     //---- sec 17: MVTX/INTT split for the LS sideband, mirroring hMVTXKeyFrac_cand/hINTTKeyFrac_cand/
0809     //---- hMVTXvsINTTKeyFrac_cand above -- gives the background-only INTT-agreement rate to compare
0810     //---- against the in-box candidate population.
0811     TH1D *hMVTXKeyFrac_far_LS       = new TH1D("hMVTXKeyFrac_far_LS","LS sideband pairs -- MVTX-only SiKeyFrac (sec 17)",42,-1.05,1.05);
0812     TH1D *hINTTKeyFrac_far_LS       = new TH1D("hINTTKeyFrac_far_LS","LS sideband pairs -- INTT-only SiKeyFrac (sec 17)",42,-1.05,1.05);
0813     TH2D *hMVTXvsINTTKeyFrac_far_LS = new TH2D("hMVTXvsINTTKeyFrac_far_LS","LS sideband pairs -- MVTX-only vs INTT-only SiKeyFrac (sec 17)",42,-1.05,1.05,42,-1.05,1.05);
0814     TH1D *hSL_far_ULS           = new TH1D("hSL_far_ULS","ULS (opp-charge) sideband pairs -- Splitting Level (sec 15)",120,-1.1,1.1);
0815     TH1D *hSiKeyFrac_far_ULS    = new TH1D("hSiKeyFrac_far_ULS","ULS (opp-charge) sideband pairs -- flat SiKeyFrac (sec 15)",44,-1.05,1.05);
0816     TH1D *hSiSplitScore_far_ULS = new TH1D("hSiSplitScore_far_ULS","ULS (opp-charge) sideband pairs -- MVTX-led SiSplitScore (sec 15)",44,-1.05,1.05);
0817     long nSplitFlagged_total    = 0;
0818     long nSplitFlagged_evts = 0;
0819     long nFlagged_duplicate = 0;    // sec 17.14: per-path breakdown -- path 1 (SL<cut, live since 17.6)
0820     long nFlagged_complementary = 0;    // path 2 (RadialGap>=cut, new sec 17.14)
0821     long nFlagged_fullmvtx      = 0;    // path 3 (full MVTX match, README_SplitTracks573 sec 8)
0822     long nFlagged_Looper        = 0;    // README_SplitTracks573 sec 35: looper veto, tracks flagged
0823     long nFlagged_dau[2]        = {0,0};    // README_CQcomparison.md sec 2.2: split partners of in-peak V0 daughters, [0] LS [1] ULS
0824     //---- README_CQcomparison.md sec 2.2 follow-up (2026-10-03): what the daughter-partner check removes. One set per path
0825     //---- (0 SL, 1 RadialGap, 2 MVTX, 3 ULS), y = daughter: 0 K0S pi+, 1 K0S pi-, 2 Lambda p, 3 Lambda pi-, 4 Lbar pbar, 5 Lbar pi+.
0826     //---- Kinematic histograms: NEW losers only (tracks not already removed by the main pre-pass / XTF), i.e. the change.
0827     //---- Q = 2k* (CalcRm::PairInfo dq); d with its V0-daughter mass, t with its dE/dx PID mass (unidentified: pion).
0828     //---- dR(t,d) of every LS-flagged partner of a Lambda/Lbar daughter BEFORE the dR cone; y = 4*daughter + PID(t),
0829     //---- daughter 0 Lambda p, 1 Lambda pi-, 2 Lbar pbar, 3 Lbar pi+; PID 0 pi 1 K 2 p 3 unid
0830     TH2D *hDauP_dRLS    = new TH2D("hDauP_dRLS","LS-flagged partners of #Lambda/#bar{#Lambda} daughters before the dR cone;#DeltaR(t,d);4*daughter (#Lambda p,#pi^{-},#bar{#Lambda} #bar{p},#pi^{+}) + PID(t) (#pi K p unid)",150,0.,0.3,16,-0.5,15.5);
0831     //---- 2026-10-03 (README_CQcomparison STATUS 16:30, p-Lbar / pbar-Lambda low-Q excess since 2c): EVERY opposite-charge track
0832     //---- t of a Lambda p / Lbar pbar daughter d, before any cut (also the dE/dx (anti)protons 2c protects): SiSplitScore(t,d) by
0833     //---- PID(t); for the proton-tagged t, Q(t,V0), |dpT|/pT and dR(t,d) by SiSplitScore class. y = 4*side (0 Lambda p, 1 Lbar pbar)
0834     //---- + PID(t) (0 pi 1 K 2 p 3 unid), or + class (0: skf < valULSTestCut, 1: < 0.5, 2: < 0.99, 3: >= 0.99). Diagnostic only.
0835     TH2D *hDauP_ulsSkfPid   = new TH2D("hDauP_ulsSkfPid","ULS tracks of a #Lambda p / #bar{#Lambda} #bar{p} daughter;SiSplitScore(t,d);4*side (#Lambda p, #bar{#Lambda} #bar{p}) + PID(t) (#pi K p unid)",42,0.,1.05,8,-0.5,7.5);
0836     TH2D *hDauP_ulsP_QtV    = new TH2D("hDauP_ulsP_QtV","proton-tagged ULS tracks of a #Lambda p / #bar{#Lambda} #bar{p} daughter: Q(t,V0);Q (GeV);4*side + SiSplitScore class (<cut, <0.5, <0.99, >=0.99)",400,0.,2.,8,-0.5,7.5);
0837     TH2D *hDauP_ulsP_dpt    = new TH2D("hDauP_ulsP_dpt","proton-tagged ULS tracks of a #Lambda p / #bar{#Lambda} #bar{p} daughter: |p_{T,t}-p_{T,d}|/p_{T,d};|#Deltap_{T}|/p_{T};4*side + SiSplitScore class",100,0.,2.,8,-0.5,7.5);
0838     TH2D *hDauP_ulsP_dR = new TH2D("hDauP_ulsP_dR","proton-tagged ULS tracks of a #Lambda p / #bar{#Lambda} #bar{p} daughter: #DeltaR(t,d);#sqrt{#Delta#eta^{2}+#Delta#phi^{2}};4*side + SiSplitScore class",320,0.,3.2,8,-0.5,7.5);
0839     TH2D *hDauP_n[2], *hDauP_dptrel[4], *hDauP_dR[4], *hDauP_Qtd[4], *hDauP_QtV[4], *hDauP_skf[4], *hDauP_tpid[4];
0840     for (int k=0;k<2;k++) hDauP_n[k] = new TH2D(Form("hDauP_n%s",k?"new":"all"),Form("V0-daughter partners flagged (%s);path (0 SL, 1 RG, 2 MVTX, 3 ULS);daughter (K0S #pi^{+},#pi^{-}; #Lambda p,#pi^{-}; #bar{#Lambda} #bar{p},#pi^{+})",k?"new losers only":"all"),4,-0.5,3.5,6,-0.5,5.5);
0841     for (int p=0;p<4;p++){
0842         hDauP_dptrel[p] = new TH2D(Form("hDauP_dptrel_%d",p),Form("V0-daughter partners, path %d: |p_{T,t}-p_{T,d}|/p_{T,d};|#Deltap_{T}|/p_{T};daughter",p),100,0.,2.,6,-0.5,5.5);
0843         hDauP_dR[p]     = new TH2D(Form("hDauP_dR_%d",p),Form("V0-daughter partners, path %d: #DeltaR(t,d);#sqrt{#Delta#eta^{2}+#Delta#phi^{2}};daughter",p),320,0.,3.2,6,-0.5,5.5);
0844         hDauP_Qtd[p]    = new TH2D(Form("hDauP_Qtd_%d",p),Form("V0-daughter partners, path %d: Q(t,d);Q (GeV);daughter",p),400,0.,2.,6,-0.5,5.5);
0845         hDauP_QtV[p]    = new TH2D(Form("hDauP_QtV_%d",p),Form("V0-daughter partners, path %d: Q(t,V0);Q (GeV);daughter",p),400,0.,2.,6,-0.5,5.5);
0846         hDauP_skf[p]    = new TH2D(Form("hDauP_skf_%d",p),Form("V0-daughter partners, path %d: SiSplitScore(t,d);SiSplitScore;daughter",p),42,0.,1.05,6,-0.5,5.5);
0847         hDauP_tpid[p]   = new TH2D(Form("hDauP_tpid_%d",p),Form("V0-daughter partners, path %d: PID of t (0 #pi 1 K 2 p 3 unid; +4 if negative);PID(t);daughter",p),8,-0.5,7.5,6,-0.5,5.5);
0848     }
0849     TH1D *hLooper_p     = new TH1D("hLooper_p","looper veto: |p| of the removed track;|p| (GeV/c);tracks",40,0.,0.4);
0850     TH1D *hLooper_rs    = new TH1D("hLooper_rs","looper veto: OS pairs, |p|<LOOPER_PMAX, #Delta#phi#geq170#circ;|#vec{p}_{1}+#vec{p}_{2}|/(|p_{1}|+|p_{2}|);pairs",50,0.,0.25);
0851     long nFlagged_ULS           = 0;    // sec 18.10: opposite-charge SiSplitScore path -- now TRACK-level
0852                                     // removal (folded into nSplitFlagged_thisevt), not just a sibling-only
0853                                     // pair veto -- fixes the mixed-side contamination sec 18's own work found
0854                                     // (a phantom track surviving into mix_part1/2 for every other pairtype).
0855     TH1D *hvtxx     = new TH1D("hvtxx","hvtxx",200,-1,1);
0856     TH1D *hvtxy     = new TH1D("hvtxy","hvtxy",200,-1,1);
0857     TH1D *hvtxz     = new TH1D("hvtxz","hvtxz",200,-20,20);
0858     //---- chunkStats occupancy (2026-10-11, Finalize work/chunkStats): nvtx (raw vertices in the TF) of the events reaching CalcRm
0859     TH2D *hnvtxCR   = new TH2D("hnvtxCR","events reaching CalcRm;vtx z (cm);nvtx (raw vertices in the TF)",32,-16.,16.,200,-0.5,199.5);
0860     TH1D *hntrk     = new TH1D("hntrk","hntrk",50,-0.5,49.5);
0861     TH1D *hdedx     = new TH1D("hdedx","hdedx",500,0,5000.);
0862     TH1D *hndedx    = new TH1D("hndedx","hndedx",49,-0.5,48.5);
0863     TProfile *hdedxphi      = new TProfile("hdedxphi"     ,"mean dedx vs phi"       ,48,-M_PI,M_PI,0,5000);
0864     TProfile *hdedxphi_etap = new TProfile("hdedxphi_etap","mean dedx vs phi, eta>0",48,-M_PI,M_PI,0,5000);
0865     TProfile *hdedxphi_etan = new TProfile("hdedxphi_etan","mean dedx vs phi, eta<0",48,-M_PI,M_PI,0,5000);
0866     TH1D *heta_mvt      = new TH1D("heta_mvt"   ,"heta_mvt"   ,240,-1.5,1.5);
0867     TH1D *heta_int      = new TH1D("heta_int"   ,"heta_int"   ,240,-1.5,1.5);
0868     TH1D *heta_mvtint   = new TH1D("heta_mvtint","heta_mvtint",240,-1.5,1.5);
0869     TH2D *hetazvtx      = new TH2D("hetazvtx"     ,"hetazvtx"              ,40,-20.,20.,320,-2.,2.);
0870     TH2D *hetazvtx_pta  = new TH2D("hetazvtx_pta" ,"hetazvtx, pt>0.5 GeV/c",40,-20.,20.,320,-2.,2.);
0871     TH2D *hetazvtx_ptb  = new TH2D("hetazvtx_ptb" ,"hetazvtx, pt>1.0 GeV/c",40,-20.,20.,320,-2.,2.);
0872     TH2D     *hntpc_phieta  = new TH2D(    "hntpc_phieta",    "hntpc_phieta",75,-1.5,1.5,60,-M_PI,M_PI);
0873     TH2D *habsdcaxy_phieta  = new TH2D("habsdcaxy_phieta","habsdcaxy_phieta",75,-1.5,1.5,60,-M_PI,M_PI);
0874     TH2D  *habsdcaz_phieta  = new TH2D( "habsdcaz_phieta", "habsdcaz_phieta",75,-1.5,1.5,60,-M_PI,M_PI);
0875     TH2D     *hdedx_phieta  = new TH2D(    "hdedx_phieta",    "hdedx_phieta",75,-1.5,1.5,60,-M_PI,M_PI);
0876     //
0877     const char* strch[2]    = {"pos","neg"};
0878     const char* strv0[3]    = {"K^{0}_{S}","#Lambda_{0}","#bar{#Lambda}_{0}"};  
0879     TH1D*       hpt_tagged[2][3];
0880     TH2D*       hdedxp_tagged[2][3];
0881     for (int iv=0;iv<3;iv++){
0882         for (int ic=0;ic<2;ic++){
0883             hpt_tagged[ic][iv]          = new TH1D(Form("hpt_tagged_%d_%d",ic,iv),Form("P_{T}, %s#rightarrow%s",strv0[iv],strch[ic]),1000,0.,5.);
0884             hdedxp_tagged[ic][iv]       = new TH2D(Form("hdedxp_tagged_%d_%d",ic,iv),Form("dE/dx vs p, %s#rightarrow%s",strv0[iv],strch[ic]),250,0.,5.,400,0.,8000.);
0885         }
0886     }
0887     //
0888     TH2D *hdedxp        = new TH2D("hdedxp"  ,"dedx vs p"      ,400,0,4.,1000,0.,10000.);
0889     TH2D *hdedxpz       = new TH2D("hdedxpz" ,"dedx vs p, zoom",200,0,2.,200,0., 2000.);
0890     //---- README_PID.md: dedxKFP (the value the gates are defined on), and its match to our dedx70s
0891     TH2D *hdedxKFPp     = new TH2D("hdedxKFPp"   ,"dedxKFP vs p, all tracks;p (GeV/c);dedxKFP",200,0,2.,200,0.,4000.);
0892     TH2D *hdedxKFPp_id[4];                                  // 0 pi, 1 K, 2 p, 3 unidentified
0893     const char* idName[4]   = {"#pi","K","p","unidentified"};
0894     for (int k=0;k<4;k++) hdedxKFPp_id[k] = new TH2D(Form("hdedxKFPp_id%d",k),Form("dedxKFP vs p, PID = %s;p (GeV/c);dedxKFP",idName[k]),200,0,2.,200,0.,4000.);
0895     TProfile *hdedxratio_p  = new TProfile("hdedxratio_p","dedxKFP/dedx70s vs p;p (GeV/c);<dedxKFP/dedx70s>",100,0.,2.,0.,5.);
0896     TH2D *hdedxratio2_p     = new TH2D("hdedxratio2_p","dedxKFP/dedx70s vs p;p (GeV/c);dedxKFP/dedx70s",100,0.,2.,200,0.5,1.5);
0897     TH2D *hdedxKFPp_tagged[2][3];                           // V0 daughters, as hdedxp_tagged but on dedxKFP
0898     for (int iv=0;iv<3;iv++) for (int ic=0;ic<2;ic++)
0899         hdedxKFPp_tagged[ic][iv]    = new TH2D(Form("hdedxKFPp_tagged_%d_%d",ic,iv),Form("dedxKFP vs p, %s#rightarrow%s;p (GeV/c);dedxKFP",strv0[iv],strch[ic]),200,0.,2.,200,0.,4000.);
0900     //---- gate efficiency on V0 daughters: [0] Lambda->p & Lbar->pbar (true protons), [1] all K0s->pi (true pions);
0901     //---- y bin = PID given (0 pi, 1 K, 2 p, 3 none)
0902     TH2D *hPIDtagged[2];
0903     hPIDtagged[0]   = new TH2D("hPIDtagged_p" ,"V0-daughter protons: PID given vs p;p (GeV/c);PID (0 #pi,1 K,2 p,3 none)",40,0.,2.,4,-0.5,3.5);
0904     hPIDtagged[1]   = new TH2D("hPIDtagged_pi","K^{0}_{S}-daughter pions: PID given vs p;p (GeV/c);PID (0 #pi,1 K,2 p,3 none)",40,0.,2.,4,-0.5,3.5);
0905     TH1D *hPIDcount = new TH1D("hPIDcount","accepted tracks by PID;;tracks",8,-0.5,7.5);
0906     const char* pidLab[8]   = {"#pi+","#pi-","K+","K-","p","#bar{p}","none +","none -"};
0907     for (int k=0;k<8;k++) hPIDcount->GetXaxis()->SetBinLabel(k+1,pidLab[k]);
0908     TH1D *hpt_id[3][2];                                     // pt of identified pi/K/p, pos/neg
0909     //---- README_PID.md sec 8: the p-pi ULS ridge just below dphi=0 (dphi = phi(-)-phi(+), the class's ULS convention).
0910     //---- Sibling p-pi pairs (identified, as they enter the pair types), abs(dy)<0.7: [box][cc], box 0 = ridge
0911     //---- (-10<=dphi<0 deg), 1 = its mirror (0<=dphi<10, the clean side); cc 0 = p pi-, 1 = pbar pi+.
0912     TH1D *hRidge_dphi[2], *hRidge_Mppi[2][2], *hRidge_Mee[2][2], *hRidge_open[2][2], *hRidge_prat[2][2], *hRidge_skf[2][2];
0913     TH1D *hRidge_dcap[2][2], *hRidge_dcapi[2][2], *hRidge_ptp[2][2];
0914     {
0915         const char* bx[2]   = {"ridge","mirror"};
0916         const char* cc[2]   = {"p#pi^{-}","#bar{p}#pi^{+}"};
0917         for (int c=0;c<2;c++){
0918             hRidge_dphi[c]  = new TH1D(Form("hRidge_dphi_%d",c),Form("%s sibling, abs(dy)<0.7;#phi(-)-#phi(+) (deg);pairs",cc[c]),160,-20.,20.);
0919             for (int b=0;b<2;b++){
0920                 hRidge_Mppi[b][c]   = new TH1D(Form("hRidge_Mppi_%d_%d",b,c),Form("%s %s: M_{inv} as p#pi;M_{p#pi} (GeV)",cc[c],bx[b]),100,1.07,1.27);
0921                 hRidge_Mee[b][c]    = new TH1D(Form("hRidge_Mee_%d_%d",b,c),Form("%s %s: M_{inv} as e^{+}e^{-};M_{ee} (GeV)",cc[c],bx[b]),100,0.,0.2);
0922                 hRidge_open[b][c]   = new TH1D(Form("hRidge_open_%d_%d",b,c),Form("%s %s: 3D opening angle;#theta_{open} (deg)",cc[c],bx[b]),90,0.,45.);
0923                 hRidge_prat[b][c]   = new TH1D(Form("hRidge_prat_%d_%d",b,c),Form("%s %s: p(#pi)/p(p);ratio",cc[c],bx[b]),50,0.,2.5);
0924                 hRidge_skf[b][c]    = new TH1D(Form("hRidge_skf_%d_%d",b,c),Form("%s %s: SiSplitScore;SKF",cc[c],bx[b]),22,-0.05,1.05);
0925                 hRidge_dcap[b][c]   = new TH1D(Form("hRidge_dcap_%d_%d",b,c),Form("%s %s: 3D DCA of the (anti)proton;DCA (cm)",cc[c],bx[b]),100,0.,2.);
0926                 hRidge_dcapi[b][c]  = new TH1D(Form("hRidge_dcapi_%d_%d",b,c),Form("%s %s: 3D DCA of the pion;DCA (cm)",cc[c],bx[b]),100,0.,2.);
0927                 hRidge_ptp[b][c]    = new TH1D(Form("hRidge_ptp_%d_%d",b,c),Form("%s %s: p_{T} of the (anti)proton;p_{T} (GeV/c)",cc[c],bx[b]),50,0.,1.25);
0928             }
0929         }
0930     }
0931     //---- README_CQcomparison.md sec 2.1-2.2: V0 daughter sharing / duplicates, and <pT> vs N_ch (sibling only, all events).
0932     //---- kv: 0 K0s, 1 Lambda, 2 Lbar. "In the lists" = as the pair types take them (ridgePID, Species_ptmin, Species_yu).
0933     //---- dR = sqrt(deta^2+dphi^2) (rad); CQC_DRDUP = the near-duplicate radius used for the categories below.
0934     const double CQC_DRDUP  = 0.05;
0935     //---- README_CQcomparison sec 2.3 (2026-10-03): (track, nearer V0 daughter) separation maps, sibling vs mixed -- HISTOGRAMS
0936     //---- ONLY (two-track merging search, no cut). c: 0 p-Lambda, 1 pbar-Lbar, 2 pi+Lambda, 3 pi-Lbar; sm: 0 sibling, 1 mixed.
0937     //---- hSep: deta vs dphi* to the nearer daughter; hSepQ: that separation vs Q (does a small-separation loss map onto a Q bin?);
0938     //---- hSepLS: same as hSepQ but only when the nearer daughter has the track's charge.
0939     const char* sepnm[4]    = {"p#Lambda","#bar{p}#bar{#Lambda}","#pi^{+}#Lambda","#pi^{-}#bar{#Lambda}"};
0940     TH2D *hSep[4][2], *hSepQ[4][2], *hSepLS[4][2];
0941     for (int c=0;c<4;c++) for (int sm=0;sm<2;sm++){
0942         const char* sn  = sm ? "mixed" : "sibling";
0943         hSep[c][sm]     = new TH2D(Form("hSep_%d_%d",c,sm),Form("%s %s: track vs nearer V0 daughter;#Delta#eta;#Delta#phi* (deg, min over R = 0.30-0.78 m)",sepnm[c],sn),60,-0.3,0.3,120,-30.,30.);
0944         hSepQ[c][sm]    = new TH2D(Form("hSepQ_%d_%d",c,sm),Form("%s %s: separation to the nearer daughter vs Q;#sqrt{#Delta#eta^{2}+#Delta#phi*^{2}} (rad);Q (GeV)",sepnm[c],sn),50,0.,0.5,50,0.,1.);
0945         hSepLS[c][sm]   = new TH2D(Form("hSepLS_%d_%d",c,sm),Form("%s %s: nearer daughter like-sign, separation vs Q;#sqrt{#Delta#eta^{2}+#Delta#phi*^{2}} (rad);Q (GeV)",sepnm[c],sn),50,0.,0.5,50,0.,1.);
0946     }
0947     const char* v0nm[3]     = {"K^{0}_{S}","#Lambda","#bar{#Lambda}"};
0948     TH2D *hV0share      = new TH2D("hV0share","in-peak V0 pairs sharing a daughter index;V0 1;V0 2",3,-0.5,2.5,3,-0.5,2.5);
0949     TH2D *hV0pairs      = new TH2D("hV0pairs","all in-peak V0 pairs;V0 1;V0 2",3,-0.5,2.5,3,-0.5,2.5);
0950     TH2D *hV0shareOff   = new TH2D("hV0shareOff","in-peak V0 sharing a daughter with an off-peak V0;in-peak V0;off-peak V0",3,-0.5,2.5,3,-0.5,2.5);
0951     TH1D *hDauNear_dR[3][2], *hDauNear_skf[3][2];   // [kv][daughter charge 0 +, 1 -]: nearest same-charge, same-PID track in the lists
0952     TH2D *hDauNear_dRdpt[3][2];
0953     for (int kv=0;kv<3;kv++) for (int q=0;q<2;q++){
0954         const char* dn  = (kv==0) ? (q?"#pi^{-}":"#pi^{+}") : (kv==1) ? (q?"#pi^{-}":"p") : (q?"#bar{p}":"#pi^{+}");
0955         hDauNear_dR[kv][q]      = new TH1D(Form("hDauNear_dR_%d_%d",kv,q),Form("in-peak %s, daughter %s: nearest same-PID track in the lists;#DeltaR (rad);daughters",v0nm[kv],dn),150,0.,0.3);
0956         hDauNear_skf[kv][q]     = new TH1D(Form("hDauNear_skf_%d_%d",kv,q),Form("in-peak %s, daughter %s: SiSplitScore with the nearest, #DeltaR<%.2f;SKF",v0nm[kv],dn,CQC_DRDUP),22,-0.05,1.05);
0957         hDauNear_dRdpt[kv][q]   = new TH2D(Form("hDauNear_dRdpt_%d_%d",kv,q),Form("in-peak %s, daughter %s: nearest;#DeltaR (rad);abs(#Deltap_{T})/p_{T}",v0nm[kv],dn),50,0.,0.1,50,0.,0.5);
0958     }
0959     //---- p-Lambda [0] and pbar-Lbar [1] sibling Q = 2k*, by the (anti)proton: 0 within CQC_DRDUP of the V0's own
0960     //---- (anti)proton daughter, 1 daughter of an off-peak V0, 2 neither
0961     const char* pLcat[3]    = {"near-dup of own daughter","off-peak V0 daughter","neither"};
0962     TH1D *hpLa_Q[2][3];
0963     TH2D *hpLa_QdR[2];
0964     TH1D *hLaLa_Q[2][3];        // Lambda-Lambda [0], Lbar-Lbar [1]: 0 share a daughter, 1 same-charge daughters within CQC_DRDUP, 2 neither
0965     const char* LLcat[3]    = {"share a daughter","daughters #DeltaR<0.05","neither"};
0966     TH1D *hnLApeak[2];
0967     for (int a=0;a<2;a++){
0968         const char* pn  = a ? "#bar{p}#bar{#Lambda}" : "p#Lambda";
0969         const char* ln  = a ? "#bar{#Lambda}#bar{#Lambda}" : "#Lambda#Lambda";
0970         for (int c=0;c<3;c++){
0971             hpLa_Q[a][c]    = new TH1D(Form("hpLa_Q_%d_%d",a,c),Form("%s sibling, (anti)proton: %s;Q = 2k* (GeV/c);pairs",pn,pLcat[c]),50,0.,1.);
0972             hLaLa_Q[a][c]   = new TH1D(Form("hLaLa_Q_%d_%d",a,c),Form("%s sibling: %s;Q (GeV/c);pairs",ln,LLcat[c]),40,0.,4.);
0973         }
0974         hpLa_QdR[a] = new TH2D(Form("hpLa_QdR_%d",a),Form("%s sibling;Q = 2k* (GeV/c);#DeltaR((anti)proton, V0's own daughter) (rad)",pn),50,0.,1.,50,0.,0.5);
0975         hnLApeak[a] = new TH1D(Form("hnLApeak_%d",a),Form("in-peak %s per event, in the pair window;n;events",a?"#bar{#Lambda}":"#Lambda"),8,-0.5,7.5);
0976     }
0977     TProfile *hptNch[3][2], *hptNchV0[3];   // <pT> vs N_ch (ntrkept) of what enters the pair types: [pi,K,p][+,-], V0 [kv]
0978     for (int k=0;k<3;k++) for (int q=0;q<2;q++)
0979         hptNch[k][q]    = new TProfile(Form("hptNch_%d_%d",k,q),Form("<p_{T}> vs N_{ch}, %s %s;N_{ch} (accepted tracks);<p_{T}> (GeV/c)",idName[k],q?"-":"+"),40,-0.5,39.5);
0980     for (int kv=0;kv<3;kv++)
0981         hptNchV0[kv]    = new TProfile(Form("hptNchV0_%d",kv),Form("<p_{T}> vs N_{ch}, in-peak %s;N_{ch} (accepted tracks);<p_{T}> (GeV/c)",v0nm[kv]),40,-0.5,39.5);
0982     long nNchKept   = 0, nNchSeen = 0;  // events inside / seen by the nchLLHH class (sec 2.1)
0983     TH2D *hypt_id[3];                                       // y vs pt of identified pi/K/p inside the charged eta fiducial:
0984     for (int k=0;k<3;k++) hypt_id[k] = new TH2D(Form("hypt_id%d",k),Form("y vs p_{T}, PID = %s, abs(#eta) fiducial;y;p_{T} (GeV/c)",idName[k]),88,-1.1,1.1,100,0.,2.);  // with the Species_yu windows drawn
0985     for (int k=0;k<3;k++) for (int ic=0;ic<2;ic++) hpt_id[k][ic] = new TH1D(Form("hpt_id%d_%d",k,ic),Form("p_{T}, PID = %s, %s;p_{T} (GeV/c)",idName[k],strch[ic]),200,0.,2.);
0986     TH2D *hdcaxy_id[3][2], *hdcaz_id[3][2];             // README_PID.md sec 10: DCA vs pt of identified pi/K/p, pos/neg (spallation protons)
0987     for (int k=0;k<3;k++) for (int ic=0;ic<2;ic++){
0988         hdcaxy_id[k][ic]    = new TH2D(Form("hdcaxy_id%d_%d",k,ic),Form("dcaxy vs p_{T}, PID = %s, %s;p_{T} (GeV/c);dcaxy (cm)",idName[k],strch[ic]),40,0.,2.,300,-1.5,1.5);
0989         hdcaz_id[k][ic]     = new TH2D(Form("hdcaz_id%d_%d" ,k,ic),Form("dcaz vs p_{T}, PID = %s, %s;p_{T} (GeV/c);dcaz (cm)"  ,idName[k],strch[ic]),40,0.,2.,300,-1.5,1.5);
0990     }
0991     TH2D *hadca_phi[2][2][2];                               // README_PID.md sec 10.2: the phi ~ 105 deg strip. [xy|z][charge][vertex phi | phi at
0992     for (int d=0;d<2;d++) for (int q=0;q<2;q++) for (int f=0;f<2;f++)   // R = 0.55 m as hSpoke] vs abs(dca), accepted charged tracks
0993         hadca_phi[d][q][f]  = new TH2D(Form("hadca_phi_%d_%d_%d",d,q,f),Form("abs(%s), %s;%s (deg);abs(%s) (cm)",d?"dcaz":"dcaxy",q?"negative":"positive",f?"#phi(R=0.55 m)":"vertex #phi",d?"dcaz":"dcaxy"),360,-180.,180.,75,0.,1.5);
0994     //---- README_PID.md sec 10.3: the vertex-phi mask page. Tracks passing AcceptTrackBase, before the mask: [q] vertex phi
0995     //---- vs abs(dcaxy) and vs abs(dcaz), and PID (0-3) x charge x [0 outside | 1 inside a mask window]
0996     TH2D *hmask_dcaxy[2], *hmask_dcaz[2];
0997     for (int q=0;q<2;q++){
0998         hmask_dcaxy[q]  = new TH2D(Form("hmask_dcaxy_%d",q),Form("before the #phi mask, %s;vertex #phi (deg);abs(dcaxy) (cm)",q?"negative":"positive"),360,-180.,180.,75,0.,1.5);
0999         hmask_dcaz[q]   = new TH2D(Form("hmask_dcaz_%d",q) ,Form("before the #phi mask, %s;vertex #phi (deg);abs(dcaz) (cm)" ,q?"negative":"positive"),360,-180.,180.,75,0.,1.5);
1000     }
1001     TH2D *hmask_pid = new TH2D("hmask_pid","tracks before the #phi mask;PID x charge;0 outside, 1 inside a window",8,-0.5,7.5,2,-0.5,1.5);
1002     TH2D *hphieta_id[3][2];                                 // README_PID.md sec 10: (eta,phi) of identified pi/K/p, pos/neg
1003     for (int k=0;k<3;k++) for (int ic=0;ic<2;ic++)
1004         hphieta_id[k][ic]   = new TH2D(Form("hphieta_id%d_%d",k,ic),Form("(#eta,#phi), PID = %s, %s;#eta;#phi",idName[k],strch[ic]),30,-1.5,1.5,36,-M_PI,M_PI);
1005     //---- same, vs (eta,phi): [0] mean dcaxy, [1] mean abs(dcaxy), [2] mean dcaz, [3] mean abs(dcaz) (alignment/distortions by region)
1006     TProfile2D *pdca_etaphi_id[3][2][4];
1007     const char* dcaVar[4]   = {"dcaxy","|dcaxy|","dcaz","|dcaz|"};
1008     for (int k=0;k<3;k++) for (int ic=0;ic<2;ic++) for (int iv=0;iv<4;iv++)
1009         pdca_etaphi_id[k][ic][iv]   = new TProfile2D(Form("pdca_etaphi_id%d_%d_%d",k,ic,iv),Form("<%s> vs (#eta,#phi), PID = %s, %s;#eta;#phi;<%s> (cm)",dcaVar[iv],idName[k],strch[ic],dcaVar[iv]),30,-1.5,1.5,36,-M_PI,M_PI);
1010     TH1D *hhighestpt    = new TH1D("hhighestpt","hhighestpt",400,0.0,20.0);
1011     //
1012     TH1D *hxing         = new TH1D("hxing","crossing, all events",600,-100.5,499.5);
1013     TH1D *hKSxing       = new TH1D("hKSxing","crossing, KS found",600,-100.5,499.5);
1014     TH1D *hLAxing       = new TH1D("hLAxing","crossing, LA found",600,-100.5,499.5);
1015     TH1D *hALxing       = new TH1D("hALxing","crossing, AL found",600,-100.5,499.5);
1016     //---- 2026-10-08: does the triggered collision (crossing 0) leak into its neighbours? <ntrk>, <pT> and
1017     //---- events vs crossing, split by the TF's trigger class (the trigger bits belong to the whole TF, so
1018     //---- crossing!=0 events of a jet-triggered TF are streaming events next to a busy collision).
1019     //---- one bin per crossing over [-100,480]. [0]=all [1]=clock [2]=MB trigger [3]=jet [4]=photon.
1020     const int NXTRIGCL  = 5;
1021     const char *xtrigName[NXTRIGCL] = {"all TFs","clock trigger","MB trigger","jet trigger","photon trigger"};
1022     const int xtrigCol[NXTRIGCL]    = {1,kGray+1,kBlue+1,kRed+1,kGreen+2};
1023     std::vector<double> xtrigEdges;
1024     for (double x=-100.5;x<480.6;x+=1.)     xtrigEdges.push_back(x);        // one bin per crossing, no variable widths
1025     TProfile *pntrk_xing[NXTRIGCL], *ppt_xing[NXTRIGCL];
1026     TH1D *hnev_xing[NXTRIGCL];
1027     TProfile *pntrkraw_xing[NXTRIGCL], *pptraw_xing[NXTRIGCL];  // raw: filled before the crossing selection and event cuts
1028     TH1D *hnevraw_xing[NXTRIGCL];
1029     for (int c=0;c<NXTRIGCL;c++){
1030         pntrkraw_xing[c]    = new TProfile(Form("pntrkraw_xing%d",c),Form("<N_{trk}> (raw ntr) vs crossing, before crossing selection, %s;crossing;<N_{trk}> raw",xtrigName[c]),xtrigEdges.size()-1,xtrigEdges.data());
1031         pptraw_xing[c]      = new TProfile(Form("pptraw_xing%d",c),Form("<p_{T}> (all tracks) vs crossing, before crossing selection, %s;crossing;<p_{T}> (GeV/c)",xtrigName[c]),xtrigEdges.size()-1,xtrigEdges.data());
1032         hnevraw_xing[c]     = new TH1D(Form("hnevraw_xing%d",c),Form("events vs crossing, before crossing selection, %s;crossing;events",xtrigName[c]),xtrigEdges.size()-1,xtrigEdges.data());
1033         for (TH1 *h : {(TH1*)pntrkraw_xing[c],(TH1*)pptraw_xing[c],(TH1*)hnevraw_xing[c]}){ h->SetLineColor(xtrigCol[c]); h->SetMarkerColor(xtrigCol[c]); h->SetMarkerStyle(20); h->SetMarkerSize(0.5); }
1034         pntrk_xing[c]   = new TProfile(Form("pntrk_xing%d",c),Form("<N_{trk}> (kept) vs crossing, %s;crossing;<N_{trk}>",xtrigName[c]),xtrigEdges.size()-1,xtrigEdges.data());
1035         ppt_xing[c]     = new TProfile(Form("ppt_xing%d",c),Form("<p_{T}> (kept tracks) vs crossing, %s;crossing;<p_{T}> (GeV/c)",xtrigName[c]),xtrigEdges.size()-1,xtrigEdges.data());
1036         hnev_xing[c]    = new TH1D(Form("hnev_xing%d",c),Form("events vs crossing, %s;crossing;events",xtrigName[c]),xtrigEdges.size()-1,xtrigEdges.data());
1037         for (TH1 *h : {(TH1*)pntrk_xing[c],(TH1*)ppt_xing[c],(TH1*)hnev_xing[c]}){ h->SetLineColor(xtrigCol[c]); h->SetMarkerColor(xtrigCol[c]); h->SetMarkerStyle(20); h->SetMarkerSize(0.5); }
1038     }
1039     //---- coarser than hxing/hKSxing/etc -- with only O(10-100) V0 candidates, the fine
1040     //---- 600-bin crossing axis left entries too sparse (sub-pixel-wide bins) to actually see.
1041     TH2D *hKSmass_xing  = new TH2D("hKSmass_xing","hKSmass_xing" ,120,-100.5,499.5,100, 0.4, 0.6);
1042     TH2D *hLAmass_xing  = new TH2D("hLAmass_xing","hLAmass_xing" ,120,-100.5,499.5,100,1.085,1.145);
1043     TH2D *hALmass_xing  = new TH2D("hALmass_xing","hALmass_xing" ,120,-100.5,499.5,100,1.085,1.145);
1044     //
1045     //---- V0 kinematics, by species (KS/LA/AL)...
1046     TH1D *hKSmass   = new TH1D("hKSmass" ,"K^{0}_{S} mass"           ,200,0.4,0.6);
1047     TH1D *hKSpt     = new TH1D("hKSpt"   ,"K^{0}_{S} p_{T}"          ,200,0.,10.);
1048     TH1D *hKSeta    = new TH1D("hKSeta"  ,"K^{0}_{S} #eta"           ,120,-1.5,1.5);
1049     TH1D *hKSphi    = new TH1D("hKSphi"  ,"K^{0}_{S} #varphi"        ,96,-M_PI,M_PI);
1050     TH1D *hKSdira   = new TH1D("hKSdira" ,"K^{0}_{S} DIRA"           ,200,0.99,1.0);
1051     TH1D *hKSpvdca  = new TH1D("hKSpvdca","K^{0}_{S} PV_DCA"         ,200,0.,2.);
1052     TH1D *hKSworsedca   = new TH1D("hKSworsedca","K^{0}_{S} worse-daughter DCA (offline)"  ,200,0.,2.);
1053     TH1D *hLAmass   = new TH1D("hLAmass" ,"#Lambda_{0} mass"         ,60,1.085,1.145);
1054     TH1D *hLAmass_clean = new TH1D("hLAmass_clean","#Lambda_{0} mass, worse-track PV_DCA cut",60,1.085,1.145);
1055     hLAmass_clean   ->SetFillColor(3);  hLAmass_clean   ->SetLineColor(1);
1056     TH1D *hLApt     = new TH1D("hLApt"   ,"#Lambda_{0} p_{T}"        ,200,0.,10.);
1057     TH1D *hLAeta    = new TH1D("hLAeta"  ,"#Lambda_{0} #eta"         ,120,-1.5,1.5);
1058     TH1D *hLAphi    = new TH1D("hLAphi"  ,"#Lambda_{0} #varphi"      ,96,-M_PI,M_PI);
1059     TH1D *hLAdira   = new TH1D("hLAdira" ,"#Lambda_{0} DIRA"         ,200,0.99,1.0);
1060     TH1D *hLApvdca  = new TH1D("hLApvdca","#Lambda_{0} PV_DCA"       ,200,0.,2.);
1061     TH1D *hLAworsedca   = new TH1D("hLAworsedca","#Lambda_{0} worse-daughter DCA (offline)",200,0.,2.);
1062     TH1D *hALmass   = new TH1D("hALmass" ,"#bar{#Lambda}_{0} mass"   ,60,1.085,1.145);
1063     TH1D *hALmass_clean = new TH1D("hALmass_clean","#bar{#Lambda}_{0} mass, worse-track PV_DCA cut",60,1.085,1.145);
1064     hALmass_clean   ->SetFillColor(3);  hALmass_clean   ->SetLineColor(1);
1065     TH1D *hALpt     = new TH1D("hALpt"   ,"#bar{#Lambda}_{0} p_{T}"  ,200,0.,10.);
1066     TH1D *hALeta    = new TH1D("hALeta"  ,"#bar{#Lambda}_{0} #eta"   ,120,-1.5,1.5);
1067     TH1D *hALphi    = new TH1D("hALphi"  ,"#bar{#Lambda}_{0} #varphi",96,-M_PI,M_PI);
1068     TH1D *hALdira   = new TH1D("hALdira" ,"#bar{#Lambda}_{0} DIRA"   ,200,0.99,1.0);
1069     TH1D *hALpvdca  = new TH1D("hALpvdca","#bar{#Lambda}_{0} PV_DCA" ,200,0.,2.);
1070     TH1D *hALworsedca   = new TH1D("hALworsedca","#bar{#Lambda}_{0} worse-daughter DCA (offline)",200,0.,2.);
1071     //
1072     TH1D* hetotem   = new TH1D("hetotem","hetotem",500,-2,48);
1073     TH1D* hetotih   = new TH1D("hetotih","hetotih",500,-2,48);
1074     TH1D* hetotoh   = new TH1D("hetotoh","hetotoh",500,-2,48);
1075     TH1D* hetotioh  = new TH1D("hetotioh","hetotioh",500,-2,48);
1076     TH1D* hetotepd  = new TH1D("hetotepd","hetotepd",700,-2,698);
1077     //
1078     hntrk0  ->SetLineWidth(1);
1079     hntpc0  ->SetLineWidth(1);
1080     hnmvtx0 ->SetLineWidth(1);
1081     hnintt0 ->SetLineWidth(1);
1082     heta0   ->SetLineWidth(1);
1083     hphi0   ->SetLineWidth(1);
1084     hpt0    ->SetLineWidth(1);
1085     hdcaxy0 ->SetLineWidth(1);
1086     hdcaz0  ->SetLineWidth(1);
1087     hvtxx0  ->SetLineWidth(1);
1088     hvtxy0  ->SetLineWidth(1);
1089     hvtxz0  ->SetLineWidth(1);
1090     htrig0  ->SetLineWidth(1);
1091     hntrk   ->SetFillColor(3);      hntrk   ->SetLineColor(1);
1092     hntpc   ->SetFillColor(3);      hntpc   ->SetLineColor(1);
1093     hnmvtx  ->SetFillColor(3);      hnmvtx  ->SetLineColor(1);
1094     hnintt  ->SetFillColor(3);      hnintt  ->SetLineColor(1);
1095     heta    ->SetFillColor(3);      heta    ->SetLineColor(1);
1096     hphi    ->SetFillColor(3);      hphi    ->SetLineColor(1);
1097     hpt     ->SetFillColor(3);      hpt     ->SetLineColor(1);
1098     hdcaxy  ->SetFillColor(3);      hdcaxy  ->SetLineColor(1);
1099     hdcaz   ->SetFillColor(3);      hdcaz   ->SetLineColor(1);
1100     hvtxx   ->SetFillColor(3);      hvtxx   ->SetLineColor(1);
1101     hvtxy   ->SetFillColor(3);      hvtxy   ->SetLineColor(1);
1102     hvtxz   ->SetFillColor(3);      hvtxz   ->SetLineColor(1);
1103     htrig   ->SetFillColor(3);      htrig   ->SetLineColor(1);
1104 
1105     //---- by (*run), filled over events
1106     TProfile* hrirun_ntrk   = new TProfile("hrirun_ntrk"   ,"hrirun_ntrk"   ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI, -0.5,399.5);
1107     TProfile* hrirun_vtxx   = new TProfile("hrirun_vtxx"   ,"hrirun_vtxx"   ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,     -5,5  );
1108     TProfile* hrirun_vtxy   = new TProfile("hrirun_vtxy"   ,"hrirun_vtxy"   ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,     -5,5  );
1109     TProfile* hrirun_vtxz   = new TProfile("hrirun_vtxz"   ,"hrirun_vtxz"   ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,    -20,20 );
1110     TProfile* hrirun_negfr  = new TProfile("hrirun_negfr"  ,"hrirun_negfr"  ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,      0,1  );
1111     TH1D*     hrirun_nev    = new     TH1D("hrirun_nev"    ,"hrirun_nev"    ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI);
1112     TH1D*     hrirun_nevmb  = new     TH1D("hrirun_nevmb"  ,"hrirun_nevmb"  ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI);
1113     TH1D*     hrirun_nevjet = new     TH1D("hrirun_nevjet" ,"hrirun_nevjet" ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI);
1114     TH1D*     hrirun_nevpho = new     TH1D("hrirun_nevpho" ,"hrirun_nevpho" ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI);
1115     //---- by (*run)(index), filled over tracks
1116     TProfile* hrirun_eta    = new TProfile("hrirun_eta"    ,"hrirun_eta"    ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,   -1.5,1.5);
1117     TProfile* hrirun_phi    = new TProfile("hrirun_phi"    ,"hrirun_phi"    ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI, -M_PI,M_PI);
1118     TProfile* hrirun_pt     = new TProfile("hrirun_pt"     ,"hrirun_pt"     ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,    0.,100.);
1119     TProfile* hrirun_ntpc   = new TProfile("hrirun_ntpc"   ,"hrirun_ntpc"   ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,     0.,50.);
1120     TProfile* hrirun_dedx   = new TProfile("hrirun_dedx"   ,"hrirun_dedx"   ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,   0.,2000.);
1121     TProfile* hrirun_ndedx  = new TProfile("hrirun_ndedx"  ,"hrirun_ndedx"  ,RUNAXIS_NBIN,RUNAXIS_LO,RUNAXIS_HI,     0.,50.);
1122   
1123     //---- by runindex, filled over events
1124 //  const int MAXRUNSSEEN   = 200;
1125     const int NTRIGTYPES    =   5;      // all,clock,mb,jet,pho
1126     //TProfile* hri_ntrk[NTRIGTYPES];
1127     //TProfile* hri_vtxx[NTRIGTYPES];
1128     //TProfile* hri_vtxy[NTRIGTYPES];
1129     //TProfile* hri_vtxz[NTRIGTYPES];
1130     //TProfile* hri_negfr[NTRIGTYPES];
1131     //TH1D*     hri_nev[NTRIGTYPES];
1132     //TH1D*     hri_nevmb[NTRIGTYPES];
1133     //TH1D*     hri_nevjet[NTRIGTYPES];
1134     //TH1D*     hri_nevpho[NTRIGTYPES];
1135     //TProfile* hri_eta[NTRIGTYPES];
1136     //TProfile* hri_phi[NTRIGTYPES];
1137     //TProfile* hri_pt[NTRIGTYPES];
1138     //TProfile* hri_ntpc[NTRIGTYPES];
1139     //TProfile* hri_dedx[NTRIGTYPES];
1140     //TProfile* hri_ndedx[NTRIGTYPES];
1141     for (int itt=0;itt<NTRIGTYPES;itt++){   
1142         //hri_ntrk[itt] = new TProfile(Form("hri_ntrk%d"  ,itt),Form("hri_ntrk%d"  ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5, -0.5,399.5);
1143         //hri_vtxx[itt] = new TProfile(Form("hri_vtxx%d"  ,itt),Form("hri_vtxx%d"  ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,     -5,5  );
1144         //hri_vtxy[itt] = new TProfile(Form("hri_vtxy%d"  ,itt),Form("hri_vtxy%d"  ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,     -5,5  );
1145         //hri_vtxz[itt] = new TProfile(Form("hri_vtxz%d"  ,itt),Form("hri_vtxz%d"  ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,    -20,20 );
1146         //hri_negfr[itt]    = new TProfile(Form("hri_negfr%d" ,itt),Form("hri_negfr%d" ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,      0,1  );
1147         //hri_nev[itt]  = new     TH1D(Form("hri_nev%d"   ,itt),Form("hri_nev%d"   ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5);
1148         //hri_nevmb[itt]    = new     TH1D(Form("hri_nevmb%d" ,itt),Form("hri_nevmb%d" ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5);
1149         //hri_nevjet[itt]   = new     TH1D(Form("hri_nevjet%d",itt),Form("hri_nevjet%d",itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5);
1150         //hri_nevpho[itt]   = new     TH1D(Form("hri_nevpho%d",itt),Form("hri_nevpho%d",itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5);
1151         //hri_eta[itt]  = new TProfile(Form("hri_eta%d"   ,itt),Form("hri_eta%d"   ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,   -1.5,1.5);
1152         //hri_phi[itt]  = new TProfile(Form("hri_phi%d"   ,itt),Form("hri_phi%d"   ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5, -M_PI,M_PI);
1153         //hri_pt[itt]       = new TProfile(Form("hri_pt%d"    ,itt),Form("hri_pt%d"    ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,    0.,100.);
1154         //hri_ntpc[itt] = new TProfile(Form("hri_ntpc%d"  ,itt),Form("hri_ntpc%d"  ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,     0.,50.);
1155         //hri_dedx[itt] = new TProfile(Form("hri_dedx%d"  ,itt),Form("hri_dedx%d"  ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,   0.,2000.);
1156         //hri_ndedx[itt]    = new TProfile(Form("hri_ndedx%d" ,itt),Form("hri_ndedx%d" ,itt),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5,     0.,50.);
1157     }
1158     //TH1D* hri_counters[NCOUNTERS];
1159     for (int icounter=0;icounter<NCOUNTERS;icounter++){
1160         //hri_counters[icounter]    = new TH1D(Form("hri_counters%d",icounter),Form("hri_counters%d",icounter),NRUNSSEEN,-0.5,((double)NRUNSSEEN)-0.5); 
1161     }
1162     //
1163     TH1::AddDirectory(kFALSE);
1164     //
1165     //---- end booking
1166     
1167     //---- loop through the tree...
1168     //
1169     int run_highest = 0;
1170     int run_lowest  = 999999;
1171     //
1172     Long64_t nentries;
1173     if (ntodo!=0){ nentries = ntodo;        } 
1174             else { nentries = nentriesfile; }
1175     if (nentries>nentriesfile){ nentries = nentriesfile; }
1176     cout<<"corral::Loop -- Event Loop over "<<nentries<<" events starting..."<<endl;    
1177     //
1178     if (doXTFClean) BuildXTFLosers(nentries);   // sec 18.22
1179     if (doTFDup)    BuildTFDupRows(nentries);   // overlapping-TF collision copies
1180     if (doIsoVtx)   BuildIsoRows(nentries);     // isolated vertices only (isovtxNNZZ)
1181     //
1182     int runprev         = -1;
1183     int segmentprev     = -1;
1184     //int kRunIndex     = -1;
1185     //int kRunIndexprev = -1;
1186     int lastKeptTF_run  = -1;   // sec 18.21
1187     int lastKeptTF_evt  = -1;
1188     long nSkippedSameTF = 0;
1189     int lastPosTF_run   = -1;   // sec 18.21 follow-up 2
1190     int lastPosTF_evt   = -1;
1191     long nSkippedXingPos    = 0;
1192     long nSkippedXing       = 0;    // Xing0 / NearX0 / ZB
1193     //---- README.md "segveto" (2026-10-12): the (run, DST segment)s of bad_segments.txt (ana573/SegCellsView_ana573.C:
1194     //---- segments below their run's live-fraction band) are skipped before ANY processing. File = $CORRAL_SEGVETO, else
1195     //---- bad_segments.txt in the folder of the -l list. Lines "run segment", '#' = comment.
1196     std::set<std::pair<int,int>> segVeto;
1197     long nSkippedSegVeto    = 0;
1198     long nSkippedIso        = 0;    // isovtxNNZZ
1199     long nRowsRead          = 0, nEvtTracks = 0;    // info/count_*: rows read, events reaching the track loop
1200     TString segVetoFile     = "";
1201     if (doSegVeto){
1202         if (getenv("CORRAL_SEGVETO")) segVetoFile = getenv("CORRAL_SEGVETO");
1203         else if (ListName!="") segVetoFile = TString(gSystem->DirName(ListName.Data()))+"/bad_segments.txt";
1204         std::ifstream fv(segVetoFile.Data());
1205         if (segVetoFile=="" || !fv.good()){ cout<<"corral::Loop -- segveto: no bad_segments.txt ("<<segVetoFile<<"; set CORRAL_SEGVETO or use -l), exit"<<endl; exit(1); }
1206         std::string line;
1207         while (std::getline(fv,line)){ if (line.empty() || line[0]=='#') continue; std::istringstream is(line); int r, s; if (is>>r>>s) segVeto.insert({r,s}); }
1208         cout<<"corral::Loop -- segveto: "<<segVeto.size()<<" (run, segment)s from "<<segVetoFile<<endl;
1209     }
1210     for (Long64_t jentry=0; jentry<nentries && fReader.Next(); jentry++) {
1211         if (jentry%100000==0) cout<<"processing "<<jentry<<endl;
1212         if ((*run)!=sc_run || (*segment)!=sc_seg){ scFlush(); sc_run = (*run); sc_seg = (*segment); }   // segCells
1213         ++sc_nraw; ++nRowsRead;
1214         //
1215         //---- a collision already seen in an overlapping earlier TF: not a new event (see doTFDup)
1216         if (doTFDup && jentry<(Long64_t)tfDupRows.size() && tfDupRows[jentry]) continue;
1217         //---- isovtxNNZZ: a vertex with a neighbour, before ANY processing (counted in segCells nraw only)
1218         if (doIsoVtx && jentry<(Long64_t)nonIsoRows.size() && nonIsoRows[jentry]){ ++nSkippedIso; continue; }
1219         //---- segveto: an acceptance-outlier segment, before ANY processing (segCells: veto = 1, nraw only)
1220         if (doSegVeto && segVeto.count({(*run),(*segment)})){ ++nSkippedSegVeto; sc_veto = 1; continue; }
1221         //
1222         //---- new event processing...
1223         //---- sec 18.21: opt-in TF-deduplication -- keep only the FIRST row seen for each (run,evt)
1224         //---- (evt IS the trigger frame's own EvtSequence, constant across every row from that TF,
1225         //---- SDCC-claude sec 18.20), skip every subsequent row from the same TF entirely, before
1226         //---- ANY processing. Relies on same-TF rows being written consecutively (SDCC-claude
1227         //---- confirmed this within one file; cross-file TChain boundaries are not guaranteed to
1228         //---- preserve it, but a same-(run,evt) coincidence exactly at a file boundary is negligible).
1229         if (ONLY_FIRST_TF){
1230             if ((*run)==lastKeptTF_run && (*evt)==lastKeptTF_evt){
1231                 ++nSkippedSameTF;
1232                 continue;
1233             }
1234             lastKeptTF_run  = (*run);
1235             lastKeptTF_evt  = (*evt);
1236         }
1237         //---- sec 18.21 follow-up 2: "XingPos" -- keep only the first crossing>0 row of each TF (pure
1238         //---- streaming, <=1 row/TF). Same consecutive-rows assumption as ONLY_FIRST_TF above.
1239         if (ONLY_FIRST_XINGPOS){
1240             if ((*crossing)<=0 || ((*run)==lastPosTF_run && (*evt)==lastPosTF_evt)){
1241                 ++nSkippedXingPos;
1242                 continue;
1243             }
1244             lastPosTF_run   = (*run);
1245             lastPosTF_evt   = (*evt);
1246         }
1247         //---- 2026-10-08: "Xing0" / "NearX0" / "ZB" -- crossing 0 (triggered), its neighbours [XNEAR_LO,XNEAR_HI]
1248         //---- without 0, or the rest of [ZB_LO,ZB_HI]; pure per-row cut, before ANY processing.
1249         //---- trigger class of the TF (same bits as goodClock/goodMB/goodJet/goodPho below), for the
1250         //---- crossing pages; the raw page is filled here, before the crossing selection and event cuts.
1251         bool xtrigIn[NXTRIGCL]  = {true,false,false,false,false};
1252         {
1253             int ib  = 0;
1254             for (bool on : *trigVec){
1255                 if (on){
1256                     if (ib==0)                                      xtrigIn[1] = true;
1257                     if (ib==3 || (ib>=10&&ib<=14))                  xtrigIn[2] = true;
1258                     if ((ib>=16&&ib<=23) || (ib>=32&&ib<=35))       xtrigIn[3] = true;
1259                     if ((ib>=24&&ib<=31) || (ib>=36&&ib<=38))       xtrigIn[4] = true;
1260                 }
1261                 if (++ib>=NTRIGS) break;
1262             }
1263             double sumpt    = 0;
1264             for (int it=0;it<(*ntr);it++) sumpt += pt[it];
1265             for (int c=0;c<NXTRIGCL;c++) if (xtrigIn[c]){
1266                 hnevraw_xing[c] ->Fill((*crossing));
1267                 pntrkraw_xing[c]->Fill((*crossing),(*ntr));
1268                 if ((*ntr)>0) pptraw_xing[c]->Fill((*crossing),sumpt/(*ntr),(*ntr));    // track-weighted, as ppt_xing
1269             }
1270         }
1271         if (xingSel>0){
1272             int xs  = (*crossing)==0 ? 1 : (((*crossing)>=XNEAR_LO && (*crossing)<=XNEAR_HI) ? 2 :
1273                       (((*crossing)>=ZB_LO && (*crossing)<=ZB_HI) ? 3 : 4));        // 4 = readout edges, in no sample
1274             if (xs!=xingSel){
1275                 ++nSkippedXing;
1276                 continue;
1277             }
1278         }
1279         //
1280         //---- get runindex...      
1281         if ((*run)!=runprev || (*segment)!=segmentprev ){
1282             //int krunbin   = hRunIndex->GetXaxis()->FindBin((*run));
1283             //int ksegbin   = hRunIndex->GetYaxis()->FindBin((*segment));
1284             //kRunIndex = hRunIndex->GetBinContent(krunbin,ksegbin);
1285             //cout<<"New run/segment seen... run="<<(*run)
1286             //  <<"\t bins="<<krunbin<<" "<<ksegbin
1287             //  <<"\t runc="<<hRunIndex->GetXaxis()->GetBinCenter(krunbin)
1288             //  <<"\t index="<<kRunIndex<<endl;
1289             if (runprev!=-1){   // don't fill in very first event!
1290                 for (int icounter=0;icounter<NCOUNTERS;icounter++){
1291                     //hri_counters[icounter]    ->Fill(kRunIndexprev,counters[icounter]);
1292                     counters[icounter]  = 0;
1293                 }
1294             }
1295         }   
1296         //runprev   = (*run);   segmentprev = (*segment);   kRunIndexprev = kRunIndex;
1297         //if (kRunIndex<0){ cout<<"No RunIndex Found for Run="<<(*run)<<endl; exit(0); }
1298         if ((*run)>run_highest) run_highest = (*run);
1299         if ((*run)<run_lowest)  run_lowest  = (*run);
1300         //      
1301         bool triggerIDs[64] = {0};
1302         int  itrig          =  0 ;
1303         vector<bool>::iterator ptr; 
1304         for (ptr=trigVec->begin(); ptr<trigVec->end(); ptr++){
1305             if (*ptr){ 
1306                 triggerIDs[itrig] = true; 
1307                 htrig0      ->Fill(itrig,1.); 
1308                 htrigRun    ->Fill((*run),itrig,1.); 
1309             }   // bit is on!
1310             ++itrig;
1311         }
1312         hvtxx0      ->Fill((*vtxx));
1313         hvtxy0      ->Fill((*vtxy));
1314         hvtxz0      ->Fill((*vtxz));
1315         hntrk0      ->Fill((*ntr));
1316         //------------------------------
1317         bool keepevt    = AcceptEvent();
1318         if (!keepevt) continue;
1319         //------------------------------
1320         hvtxx       ->Fill((*vtxx));
1321         hvtxy       ->Fill((*vtxy));
1322         hvtxz       ->Fill((*vtxz));
1323         //
1324         itrig           = 0;
1325         bool goodClock  = false;
1326         bool goodMB     = false;
1327         bool goodJet    = false;
1328         bool goodPho    = false;
1329         for (int itrig=0;itrig<NTRIGS;itrig++){
1330             if (triggerIDs[itrig]){     // this bit is ON!
1331                 htrig->Fill(itrig,1.); 
1332                 if (itrig== 0           ) goodClock = true;
1333                 if (itrig== 3           ) goodMB    = true;
1334                 if (itrig>=10&&itrig<=14) goodMB    = true;
1335                 if (itrig>=16&&itrig<=23) goodJet   = true;
1336                 if (itrig>=24&&itrig<=31) goodPho   = true;
1337                 if (itrig>=32&&itrig<=35) goodJet   = true;
1338                 if (itrig>=36&&itrig<=38) goodPho   = true;
1339             }   // end check of trigger bit
1340         }   // end loop over trigger bits
1341         //
1342         //
1343         double  highestpt       = 0.0;
1344         //int   highestpt_it    = 0.0;
1345         for (int it=0;it<(*ntr);it++){
1346             if (AcceptTrack(it)){
1347                 if (pt[it]>highestpt){
1348                     highestpt       = pt[it];
1349                     //highestpt_it  = it;
1350                 }
1351             }
1352         }
1353         hhighestpt  ->Fill(highestpt);
1354         hxing       ->Fill((*crossing));
1355         if ((*crossing)==0){
1356             hetotem     ->Fill((*etotem) );
1357             hetotih     ->Fill((*etotih) );
1358             hetotoh     ->Fill((*etotoh) );
1359             hetotioh    ->Fill((*etotioh));
1360             hetotepd    ->Fill((*etotepd));
1361         }
1362         //
1363         //---- first loop through the found V0's and make sure all daughters are distinct!!  NOW DONE ON SDCC
1364         //---- must search across V0 PIDs too!!!!
1365         //std::vector<int> tr1ids;
1366         //std::vector<int> tr2ids;
1367         //if ((*nv0)>1){
1368         //  //cout<<"-----------------------------------------"<<endl;
1369         //  for (int iv0=0;iv0<(*nv0);iv0++){
1370         //      //cout<<"IV0 PRELOOP .. "<<iv0<<" "<<(*nv0)<<"\t "<<v0pid[iv0]<<" "<<v0mass[iv0]<<"\t "<<v0indtr1[iv0]<<" "<<v0indtr2[iv0]<<endl;
1371         //      tr1ids.push_back(v0indtr1[iv0]);
1372         //      tr2ids.push_back(v0indtr2[iv0]);
1373         //      for (int jv0=0;jv0<iv0;jv0++){  // compare to all previous values
1374         //          if (v0indtr1[iv0]!=-1 && v0indtr1[iv0]==tr1ids.at(jv0)){
1375         //              //cout<<"TR1 dup "<<jentry<<"\t "<<v0indtr1[iv0]<<"\t jv0="<<jv0<<" iv0="<<iv0<<"\t "<<v0pid[jv0]<<" "<<v0pid[iv0]
1376         //              //  <<"\t "<<v0chi2ndf[jv0]<<" "<<v0chi2ndf[iv0]<<endl;
1377         //          }
1378         //          if (v0indtr2[iv0]!=-1 && v0indtr2[iv0]==tr2ids.at(jv0)){
1379         //              //cout<<"TR2 dup "<<jentry<<"\t "<<v0indtr2[iv0]<<"\t jv0="<<jv0<<" iv0="<<iv0<<"\t "<<v0pid[jv0]<<" "<<v0pid[iv0]
1380         //              //  <<"\t "<<v0chi2ndf[jv0]<<" "<<v0chi2ndf[iv0]<<endl;
1381         //          }
1382         //      }
1383         //  }
1384         //  tr1ids.clear();
1385         //  tr2ids.clear();
1386         //  //cout<<"\t\t that was event "<<jentry<<" ----------- "<<endl;
1387         //} 
1388         //
1389         //---- README_PID.md sec 12: the daughters of every in-peak V0, from the V0's own track indices (indv0 holds
1390         //---- one V0 per track, so a track shared by an in-peak and an off-peak candidate could escape through it)
1391         std::vector<int> peakV0Of((*ntr),-1);
1392         for (int iv0=0;iv0<(*nv0);iv0++){
1393             if (!v0InPeak(iv0)) continue;
1394             const int itd[2]    = {v0indtr1[iv0], v0indtr2[iv0]};
1395             for (int kd=0;kd<2;kd++){
1396                 if (itd[kd]<0 || itd[kd]>=(*ntr) || peakV0Of[itd[kd]]>=0) continue;
1397                 peakV0Of[itd[kd]]   = iv0;
1398                 if (!(indv0[itd[kd]]>=0 && v0InPeak(indv0[itd[kd]]))) ++nPeakDauNotIndv0;
1399             }
1400         }
1401         auto isPeakDaughter = [&](int it){ return peakV0Of[it]>=0; };
1402         //
1403         //---- split-track removal pre-pass (README_SplitTracks.md sec 13.8.4/13.8.6/13.8.8): computes
1404         //---- the real STAR-style SL (layermask, sec 9.1/9.2) AND SiKeyFrac (siclukey hit-identity,
1405         //---- sec 12a.4) for every same-charge candidate pair inside the actual (dy,dphi) spike --
1406         //---- |deta|<0.05, |dphi|<10deg, the EXACT edges of the two dphi bins straddling dphi=0 in
1407         //---- the hR2_1_<ipaty> histogram itself (sec 13.8.8), replacing the earlier, narrower,
1408         //---- ad-hoc |deta|<0.01,|dphi|<0.03 guess (sec 3) outright -- a direct measurement (sec
1409         //---- 13.8.8) showed same-charge pairs inside the true spike bin but outside that narrower
1410         //---- window still carry a real split-track signature (mean SiKeyFrac 0.357 and 29.1% exact
1411         //---- Si-cluster match, vs ~0% for a genuinely far/independent control) that the old window
1412         //---- was silently missing -- fills hSL_cand/hSiKeyFrac_cand/
1413         //---- hntpc_ij_cand every event regardless of doSplitRemoval, so a threshold scan always has
1414         //---- something to look at. Gated by its OWN switch, doSplitRemoval/valSLCut/valSiKeyCut
1415         //---- (sec 13.8.6: this used to also share a boolean with a since-deleted standalone
1416         //---- CalcRm::PairInfo SL+SiSeedMatch cut that was NOT kinematically-gated at all -- that
1417         //---- confound is why the two were split apart, and sec 17.9 later removed the old cut
1418         //---- entirely). A pair is flagged split when BOTH SiKeyFrac>=valSiKeyCut (literal
1419         //---- shared-cluster identity -- the sharp gate) AND SL<valSLCut (shared TPC coverage) --
1420         //---- this REPLACES the old SiSeedMatch-based hybrid gate used here through 2026-09-20, now
1421         //---- that SiKeyFrac (~0.003% far-population false-positive rate) is a much sharper signal
1422         //---- than SiSeedMatch's coverage-pattern match (~14%, sec 11.5/13.8.1). Winner/loser: fewer
1423         //---- ntpc loses, tie-break worse/higher quality loses, else no loser (sec 9.5, unchanged).
1424         //---- Unlike the SL-monitor-only era (2026-09-19 through 2026-09-20), the loser is now
1425         //---- REALLY REMOVED below (revives the pre-`layermask` KILLSPLITTRACKS mechanism's
1426         //---- `continue`, sec 5, but driven by this far sharper discriminator) -- this fixes event
1427         //---- multiplicity/rho1 double-counting that a CalcRm::PairInfo-only pairwise exclusion
1428         //---- cannot reach (sec 13.8.4's tradeoff writeup). The old, separate CalcRm::PairInfo
1429         //---- SL+SiSeedMatch cut (sec 10.4) this pre-pass replaced is gone entirely (sec 17.9).
1430         std::vector<int> nSplitFlagged_thisevt;
1431         std::vector<int> lsLosers_thisevt;  // README_SplitTracks573 sec 15: LS-path losers only (charge-asymmetry diagnostic)
1432         std::vector<std::pair<int,int>> loserPath_thisevt;  // sec 19: (track, removal path) -- 0 LS duplicate (SL),
1433                                 // 1 LS complementary (RadialGap), 2 LS path 3 (MVTX match), 3 ULS veto, 4 XTF cleaner
1434         //---- sec 18.22: cross-crossing duplicates found by BuildXTFLosers() -- same track-level
1435         //---- removal as the LS/ULS paths below (they skip anything already in this list).
1436         if (doXTFClean){
1437             auto itx = xtfLosers.find(jentry);
1438             if (itx!=xtfLosers.end()){ nSplitFlagged_thisevt = itx->second; for (int t : itx->second) loserPath_thisevt.push_back({t,4}); }
1439         }
1440         {
1441             //---- fixed, measured, deliberately-margined box (sec 13.8.13), NOT the old ad-hoc
1442             //---- |deta|<0.01,|dphi|<0.03 guess
1443             //---- FIXED box (sec 13.8.13), pure track-level thresholds -- deliberately NOT tied to any
1444             //---- histogram binning (thisYNB0/thisPHINB0), so changing the analysis binning never
1445             //---- again silently changes what counts as a split-track candidate (sec 13.8.8's bug).
1446             //---- A 92x96 full-stats, no-cut "imaging" pass (sec 13.8.11) directly measured the true
1447             //---- spike's extent at |deta|<0.012,|dphi|<1.875deg, comfortably inside even the previous
1448             //---- 52x72-derived box (1.1/52, 5deg) -- zero spillover found at every binning tested,
1449             //---- 22x36 up through 92x96 (secs 13.8.9/13.8.11). Rather than match that measurement
1450             //---- exactly, deliberately kept a real (not maximal) safety margin given this is
1451             //---- preliminary, self-produced data, not a finalized production (user's call, sec
1452             //---- 13.8.13) -- 0.02 in |deta|, 3deg in |dphi|, tighter than the old 52x72-derived box
1453             //---- but looser than the measured true edge. See sec 13.8.13 for the monitoring plan
1454             //---- (watch the post-cut (dy,dphi)=(0,0) bin for a reappearing spike) if a future
1455             //---- production's real spike turns out to need a looser box than this.
1456             //---- 2026-09-22 (README sec 16.8/16.9): ELLIPTICAL pregate, replacing the old 0.02 x 4deg
1457             //---- rectangle (whose "measured edges" were only bin widths at 92x96/128x120, i.e. upper
1458             //---- limits). Sized from the full-stats fine zoom (hzoomS/hzoomM, 0.001 x 0.1deg, job
1459             //---- 40415627) with SpikeExtent.C: smallest ellipse containing every NON-ISOLATED >3 sigma
1460             //---- sibling/mixed bin -- pi+pi+ a=0.0216 b=3.99deg, pi-pi- a=0.0198 b=4.06deg; this is their
1461             //---- envelope rounded up to the zoom's bin size. User's rule: contain the ENTIRE spike, as
1462             //---- narrow as possible around it, NO safety margin. LOCKED (user, 2026-09-22), modulo the
1463             //---- pt-ordered zoom run (job 40415716). The ellipse avoids the rectangle's corners, where
1464             //---- the neighbouring acceptance (a ~25-30% dip, possibly crossing) sits.
1465             static const double PREGATE_DETA    = 0.022;            // ellipse semi-axis in deta
1466             static const double PREGATE_DPHI    = 4.1*M_PI/180.0;   // ellipse semi-axis in dphi (rad)
1467             //---- ULS diagnostic sideband (sec 15) -- clearly-unrelated control region, same
1468             //---- definition as sec 5's original close-vs-sideband table (0.05<=dr<0.5).
1469             static const double ULSSIDEBAND_LO  = 0.05;
1470             static const double ULSSIDEBAND_HI  = 0.5;
1471             std::vector<int> pionidx;
1472             for (int jt=0;jt<(*ntr);jt++){
1473                 if (isPeakDaughter(jt)) continue;       // README_PID.md sec 11/12: only peak V0s claim daughters
1474                 if (!AcceptTrack(jt)) continue;
1475                 if (doOldPID && dedx70s[jt]>=400.) continue;    // README_PID.md sec 8: with the dE/dx PID, every accepted
1476                 if (chg[jt]==0) continue;                       // charged track is a candidate (protons split too); was pions only
1477                 pionidx.push_back(jt);
1478             }
1479             for (size_t ii=0;ii<pionidx.size();ii++){
1480                 for (size_t jj=ii+1;jj<pionidx.size();jj++){
1481                     int i=pionidx[ii], j=pionidx[jj];
1482                     //---- sec 15: no longer charge-gated here -- classify same-charge (LS) vs
1483                     //---- opposite-charge (ULS) below instead of skipping ULS outright, so the ULS
1484                     //---- diagnostic (this section) can run alongside the unchanged LS production
1485                     //---- logic. ULS never reaches the doSplitRemoval flagging/removal block below.
1486                     double deta = eta[i]-eta[j];
1487                     double dphi = phi[i]-phi[j];
1488                     if (dphi> M_PI) dphi -= 2*M_PI;
1489                     if (dphi<-M_PI) dphi += 2*M_PI;
1490                     bool sameCharge = (chg[i]*chg[j] > 0);
1491                     //---- elliptical pregate, sec 16.9; the dphi semi-axis is PREGATE_DPHI unless the
1492                     //---- (the pregate itself: corral_class.h InLSPregate, README_SplitTracks573 sec 12/18).
1493                     //---- (deta semi-axis: valPregateDEta = PREGATE_DETA 0.022 unless "pgdetaNNN"; "dpsNN" centres
1494                     //---- the ellipse on the split peak, README_SplitTracks573 sec 12 -- all in InLSPregate)
1495                     bool inBox      = InLSPregate(deta,dphi,pt[i],pt[j]);
1496                     double dr       = sqrt(deta*deta+dphi*dphi);
1497                     bool inSideband = (!inBox && dr>=ULSSIDEBAND_LO && dr<ULSSIDEBAND_HI);
1498                     //---- sec 18.24: ULS TRACK-level veto, NO angular gate. Was inside the LS pregate
1499                     //---- ellipse below (dphi semi-axis 4.1deg), but the shared-Si-seed pi+pi- ridge sits at
1500                     //---- dphi ~ 1.25 + 1.13/pt deg (sec 18.22), i.e. beyond 4.1deg at low pt -- those pairs
1501                     //---- were only caught by CalcRm::PairInfo's sibling-only pair veto (fDoULSTest, now
1502                     //---- removed: a numerator-only cut, 0.40% of sibling pi+pi- pairs). Within one event a
1503                     //---- shared siclukey IS a shared physical cluster, so no angular gate is needed.
1504                     //---- Same loser rule as before (fewer ntpc loses, tie-break worse/higher quality).
1505                     if (!sameCharge && doULSTest){
1506                         double SKFv = SiSplitScore(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1507                         if (SKFv>=valULSTestCut){
1508                             int loserv = -1;
1509                             if (ntpc[i]!=ntpc[j]){
1510                                 loserv  = (ntpc[i]<ntpc[j]) ? i : j;
1511                             } else if (quality[i]!=quality[j]){
1512                                 loserv  = (quality[i]>quality[j]) ? i : j;
1513                             }
1514                             if (loserv>=0){
1515                                 ++nFlagged_ULS;
1516                                 if (std::find(nSplitFlagged_thisevt.begin(),nSplitFlagged_thisevt.end(),loserv)==nSplitFlagged_thisevt.end()){
1517                                     nSplitFlagged_thisevt.push_back(loserv);
1518                                     loserPath_thisevt.push_back({loserv,3});
1519                                 }
1520                             }
1521                         }
1522                     }
1523                     //---- README_SplitTracks573 sec 35: LOOPER veto. A track with pt < ~0.17 GeV/c curls back inside the TPC; the
1524                     //---- returning half is reconstructed as a second, opposite-charge track exactly back to back with the same
1525                     //---- |p| (sec 33.4). OS pairs with both |p| < LOOPER_PMAX and |p1+p2|/(|p1|+|p2|) < valLooperRSum: one track,
1526                     //---- remove the worse one (fewer ntpc loses; tie: larger abs(dca) sum). No angular gate beyond the sum.
1527                     //---- "nolooper" turns it off.
1528                     if (!sameCharge && doLooperVeto){
1529                         double px1=pt[i]*cos(phi[i]), py1=pt[i]*sin(phi[i]), pz1=pt[i]*sinh(eta[i]);
1530                         double px2=pt[j]*cos(phi[j]), py2=pt[j]*sin(phi[j]), pz2=pt[j]*sinh(eta[j]);
1531                         double p1=sqrt(px1*px1+py1*py1+pz1*pz1), p2=sqrt(px2*px2+py2*py2+pz2*pz2);
1532                         if (p1<LOOPER_PMAX && p2<LOOPER_PMAX && fabs(dphi)>=170.0*M_PI/180.0){
1533                             double rs   = sqrt(pow(px1+px2,2)+pow(py1+py2,2)+pow(pz1+pz2,2))/(p1+p2);
1534                             hLooper_rs->Fill(rs);
1535                             if (rs<valLooperRSum){
1536                                 int loserv  = (ntpc[i]!=ntpc[j]) ? ((ntpc[i]<ntpc[j]) ? i : j)
1537                                             : ((fabs(dcaxy[i])+fabs(dcaz[i]) > fabs(dcaxy[j])+fabs(dcaz[j])) ? i : j);
1538                                 ++nFlagged_Looper;
1539                                 hLooper_p->Fill(loserv==i ? p1 : p2);
1540                                 if (std::find(nSplitFlagged_thisevt.begin(),nSplitFlagged_thisevt.end(),loserv)==nSplitFlagged_thisevt.end()){
1541                                     nSplitFlagged_thisevt.push_back(loserv);
1542                                     loserPath_thisevt.push_back({loserv,5});
1543                                 }
1544                             }
1545                         }
1546                     }
1547                     if (!inBox && !inSideband) continue;
1548                     if (inBox && !sameCharge){
1549                         //---- ULS close-box diagnostic (sec 15) -- NOT the production same-charge
1550                         //---- path below, purely a measurement of whether the sharp cluster-identity
1551                         //---- discriminators show any elevation the old blunt proxy (sec 5) missed.
1552                         double SLu      = ComputeSplitSL(layermask[i],layermask[j]);
1553                         double SKFlegacyu   = SiKeyFrac(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1554                         double SKFu     = SiSplitScore(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1555                         hSL_cand_ULS        ->Fill(SLu);
1556                         hSiKeyFrac_cand_ULS ->Fill(SKFlegacyu);
1557                         hSiSplitScore_cand_ULS  ->Fill(SKFu);
1558                         hSLvsSKF_cand_ULS   ->Fill(SLu,SKFu);
1559                         //---- sec 18.18: SIBLING d_mm, ridge/mirror/origin circles (same size, mirror is
1560                         //---- the exact opposite-sign dphi window per user's request, sec 18.17)
1561                         {
1562                             double dphiDeg  = dphi*180.0/M_PI;
1563                             bool isRidgeS   = (fabs(deta)<=0.003 && dphiDeg>=3.0 && dphiDeg<4.0);
1564                             bool isMirrorS  = (fabs(deta)<=0.003 && dphiDeg>=-4.0 && dphiDeg<-3.0);
1565                             bool isOriginS  = (fabs(deta)<=0.003 && dphiDeg>=-0.3 && dphiDeg<0.3);
1566                             if (isRidgeS || isMirrorS || isOriginS){
1567                                 TH1D *target = isRidgeS? hDmm_ULSridge_sibling : (isMirrorS? hDmm_ULSmirror_sibling : hDmm_ULSorigin_sibling);
1568                                 for (int L=0; L<48; L++){
1569                                     bool hasI = layermask[i] & (1ULL<<(7+L));
1570                                     bool hasJ = layermask[j] & (1ULL<<(7+L));
1571                                     if (!hasI || !hasJ) continue;
1572                                     int si = tpcsector[i*48+L], sj = tpcsector[j*48+L];
1573                                     if (si==255 || sj==255 || si!=sj) continue;
1574                                     double dArc = (tpcarclen[i*48+L]-tpcarclen[j*48+L])*2.0;
1575                                     double dZ   = (tpcz[i*48+L]-tpcz[j*48+L])*0.1;
1576                                     double dmm  = sqrt(dArc*dArc+dZ*dZ);
1577                                     target->Fill(dmm);
1578                                 }
1579                             }
1580                         }
1581                         //---- SL/fit-quality asymmetry follow-up (sec 15) -- same winner/loser rule as
1582                         //---- the production same-charge cut (sec 9.5: fewer ntpc loses, tie-break
1583                         //---- worse/higher quality loses), applied here purely as a diagnostic label,
1584                         //---- split by whether this ULS pair looks like a real duplicate (SKFu>=0.60)
1585                         //---- or background (SKFu<0.60).
1586                         //---- proper, non-tautological asymmetry check (sec 15) -- symmetric under
1587                         //---- i<->j swap, filled for every ULS close-box pair regardless of the
1588                         //---- loser/winner labeling below.
1589                         double ptreldiff    = fabs(pt[i]-pt[j]) / (0.5*(pt[i]+pt[j]));
1590                         double ptotreldiff  = fabs(ptot[i]-ptot[j]) / (0.5*(ptot[i]+ptot[j]));
1591                         //---- Q_inv (sec 15) -- build 4-vectors directly from (pt,eta,phi) + assumed
1592                         //---- pion mass, same identical-mass formula as CalcRm::PairInfo.
1593                         double mpi  = Species_mass[0];
1594                         double pxA  = pt[i]*cos(phi[i]), pyA = pt[i]*sin(phi[i]), pzA = pt[i]*sinh(eta[i]);
1595                         double eA   = sqrt(pxA*pxA+pyA*pyA+pzA*pzA+mpi*mpi);
1596                         double pxB  = pt[j]*cos(phi[j]), pyB = pt[j]*sin(phi[j]), pzB = pt[j]*sinh(eta[j]);
1597                         double eB   = sqrt(pxB*pxB+pyB*pyB+pzB*pzB+mpi*mpi);
1598                         double Qinv = sqrt(std::max(0.0, (pxA-pxB)*(pxA-pxB)+(pyA-pyB)*(pyA-pyB)+(pzA-pzB)*(pzA-pzB)-(eA-eB)*(eA-eB)));
1599                         if (SKFu>=0.60){
1600                             hqualitydiff_ULS_hiSKF  ->Fill(fabs(quality[i]-quality[j]));
1601                             hptreldiff_ULS_hiSKF    ->Fill(ptreldiff);
1602                             hptotreldiff_ULS_hiSKF  ->Fill(ptotreldiff);
1603                             hQinv_cand_ULS_hiSKF    ->Fill(Qinv);
1604                         } else {
1605                             hqualitydiff_ULS_loSKF  ->Fill(fabs(quality[i]-quality[j]));
1606                             hptreldiff_ULS_loSKF    ->Fill(ptreldiff);
1607                             hptotreldiff_ULS_loSKF  ->Fill(ptotreldiff);
1608                             hQinv_cand_ULS_loSKF    ->Fill(Qinv);
1609                         }
1610                         {
1611                             int loseru = -1, winneru = -1;
1612                             if (ntpc[i]!=ntpc[j]){
1613                                 loseru  = (ntpc[i]<ntpc[j]) ? i : j;
1614                             } else if (quality[i]!=quality[j]){
1615                                 loseru  = (quality[i]>quality[j]) ? i : j;
1616                             }
1617                             if (loseru>=0){
1618                                 winneru = (loseru==i) ? j : i;
1619                                 if (SKFu>=0.60){
1620                                     hntpc_loser_ULS_hiSKF       ->Fill(ntpc[loseru]);
1621                                     hntpc_winner_ULS_hiSKF      ->Fill(ntpc[winneru]);
1622                                     hquality_loser_ULS_hiSKF    ->Fill(quality[loseru]);
1623                                     hquality_winner_ULS_hiSKF   ->Fill(quality[winneru]);
1624                                     hpt_loser_vs_winner_ULS_hiSKF   ->Fill(pt[loseru],pt[winneru]);
1625                                 } else {
1626                                     hntpc_loser_ULS_loSKF       ->Fill(ntpc[loseru]);
1627                                     hntpc_winner_ULS_loSKF      ->Fill(ntpc[winneru]);
1628                                     hquality_loser_ULS_loSKF    ->Fill(quality[loseru]);
1629                                     hquality_winner_ULS_loSKF   ->Fill(quality[winneru]);
1630                                     hpt_loser_vs_winner_ULS_loSKF   ->Fill(pt[loseru],pt[winneru]);
1631                                 }
1632                                 //---- sec 18.10: TRACK-level removal, same as LS's cascade below -- was a
1633                                 //---- sibling-pair-only veto inside CalcRm::PairInfo (fDoULSTest), which left
1634                                 //---- the loser track sitting in Pvec_1/Pvec_2 and hence mix_part1/2 for every
1635                                 //---- OTHER event pairing (sec 18's own finding: a compact, real spike in the
1636                                 //---- raw MIXED density, hzoomM_0, at the same (dy,dphi) as the sibling ridge --
1637                                 //---- a single-particle-density defect that no sibling-only veto can reach,
1638                                 //---- since event mixing only kills genuine two-body correlations, not a
1639                                 //---- reconstruction artifact reproduced independently in every event).
1640                                 //---- sec 18.24: the flagging itself moved OUT of this pregate block (see the
1641                                 //---- ungated ULS veto just above the pregate `continue`) -- this block is now
1642                                 //---- diagnostics only.
1643                             }
1644                         }
1645                         continue;
1646                     }
1647                     if (inSideband){
1648                         //---- sideband diagnostic (sec 15), both charge combinations -- the
1649                         //---- clearly-unrelated control population for the close-box elevation ratio.
1650                         double SLf      = ComputeSplitSL(layermask[i],layermask[j]);
1651                         double SKFlegacyf   = SiKeyFrac(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1652                         double SKFf     = SiSplitScore(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1653                         if (sameCharge){
1654                             hSL_far_LS      ->Fill(SLf);
1655                             hSiKeyFrac_far_LS   ->Fill(SKFlegacyf);
1656                             hSiSplitScore_far_LS    ->Fill(SKFf);
1657                             double mvtxFracf,inttFracf; int mvtxDenf,inttDenf;      // sec 17
1658                             SiKeyFracSplit(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j],
1659                                            mvtxFracf,mvtxDenf,inttFracf,inttDenf);
1660                             hMVTXKeyFrac_far_LS     ->Fill(mvtxFracf);
1661                             hINTTKeyFrac_far_LS     ->Fill(inttFracf);
1662                             hMVTXvsINTTKeyFrac_far_LS   ->Fill(mvtxFracf,inttFracf);
1663                             double RGf  = ComputeRadialGap(layermask[i],layermask[j]);      // sec 17.11
1664                             if (RGf>=0.0) hRadialGap_far_LS->Fill(RGf);
1665                         } else {
1666                             hSL_far_ULS     ->Fill(SLf);
1667                             hSiKeyFrac_far_ULS  ->Fill(SKFlegacyf);
1668                             hSiSplitScore_far_ULS   ->Fill(SKFf);
1669                         }
1670                         continue;
1671                     }
1672                     //---- from here on: inBox && sameCharge -- the EXISTING, unchanged production
1673                     //---- same-charge path (sec 13.8/13.9/14), byte-identical to before sec 15.
1674                     double SL       = ComputeSplitSL(layermask[i],layermask[j]);
1675                     double SKFlegacy    = SiKeyFrac(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1676                     hSL_cand        ->Fill(SL);
1677                     hSiKeyFrac_cand ->Fill(SKFlegacy);          // LEGACY combined metric, retained for comparison
1678                     hntpc_ij_cand   ->Fill(ntpc[i],ntpc[j]);
1679                     //---- MVTX/INTT split (sec 13.9) -- SKF below (the actual cut-driving value, replacing
1680                     //---- SKFlegacy as of this update) is SiSplitScore, MVTX-primary/INTT-secondary
1681                     double mvtxFrac,inttFrac; int mvtxDen,inttDen;
1682                     SiKeyFracSplit(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j],
1683                                    mvtxFrac,mvtxDen,inttFrac,inttDen);
1684                     hMVTXKeyFrac_cand       ->Fill(mvtxFrac);
1685                     hINTTKeyFrac_cand       ->Fill(inttFrac);
1686                     hMVTXvsINTTavail_cand   ->Fill(mvtxDen,inttDen);
1687                     hMVTXvsINTTKeyFrac_cand ->Fill(mvtxFrac,inttFrac);
1688                     double SKF  = SiSplitScore(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1689                     hSiSplitScore_cand  ->Fill(SKF);
1690                     double RG   = ComputeRadialGap(layermask[i],layermask[j]);      // sec 17.11
1691                     if (RG>=0.0){
1692                         hRadialGap_cand     ->Fill(RG);
1693                         hRadialGap_vs_SL_cand   ->Fill(SL,RG);
1694                         hRadialGap_vs_SKF_cand  ->Fill(SKF,RG);
1695                     }
1696                     //---- sec 17: fine position map, split by partial-MVTX sub-population -- does the
1697                     //---- INTT-agreeing half of each partial band cluster at (0,0) like true duplicates,
1698                     //---- or spread out like the mvtxFrac==0 background reference?
1699                     {
1700                         double dphideg  = dphi*180.0/M_PI;
1701                         bool mvtx13 = (fabs(mvtxFrac-1.0/3.0)<0.01);
1702                         bool mvtx23 = (fabs(mvtxFrac-2.0/3.0)<0.01);
1703                         bool inttAgree  = (inttFrac>=0.75);
1704                         if (mvtxFrac<=0.0)          hPos_mvtx0          ->Fill(deta,dphideg);
1705                         else if (mvtxFrac>=1.0)         hPos_mvtx1          ->Fill(deta,dphideg);
1706                         else if (mvtx13 && inttAgree)       hPos_mvtx13_inttAgree       ->Fill(deta,dphideg);
1707                         else if (mvtx13 && !inttAgree)      hPos_mvtx13_inttDisagree    ->Fill(deta,dphideg);
1708                         else if (mvtx23 && inttAgree)       hPos_mvtx23_inttAgree       ->Fill(deta,dphideg);
1709                         else if (mvtx23 && !inttAgree)      hPos_mvtx23_inttDisagree    ->Fill(deta,dphideg);
1710                     }
1711                     //---- sec 17.10: ridge box test -- see histogram declarations above for the
1712                     //---- rationale. Independent of doSplitRemoval (like the diagnostics above), so it
1713                     //---- always has something to look at.
1714                     {
1715                         double mpiR = Species_mass[0];
1716                         double pxAR = pt[i]*cos(phi[i]), pyAR = pt[i]*sin(phi[i]), pzAR = pt[i]*sinh(eta[i]);
1717                         double eAR  = sqrt(pxAR*pxAR+pyAR*pyAR+pzAR*pzAR+mpiR*mpiR);
1718                         double pxBR = pt[j]*cos(phi[j]), pyBR = pt[j]*sin(phi[j]), pzBR = pt[j]*sinh(eta[j]);
1719                         double eBR  = sqrt(pxBR*pxBR+pyBR*pyBR+pzBR*pzBR+mpiR*mpiR);
1720                         double dyR  = 0.5*log((eAR+pzAR)/(eAR-pzAR)) - 0.5*log((eBR+pzBR)/(eBR-pzBR));
1721                         double dphidegR = dphi*180.0/M_PI;
1722                         if (fabs(dyR)<0.010 && fabs(dphidegR)<0.2){
1723                             bool wouldFlag      = (SKF>=valSiKeyCut && SL<valSLCut);
1724                             double ptreldiffR   = fabs(pt[i]-pt[j]) / (0.5*(pt[i]+pt[j]));
1725                             double ptotreldiffR = fabs(ptot[i]-ptot[j]) / (0.5*(ptot[i]+ptot[j]));
1726                             double qualitydiffR = fabs(quality[i]-quality[j]);
1727                             double dcaxydiffR   = fabs(dcaxy[i]-dcaxy[j]);
1728                             double dcazdiffR    = fabs(dcaz[i]-dcaz[j]);
1729                             double dedxdiffR    = fabs(dedx70s[i]-dedx70s[j]);
1730                             (wouldFlag? hptreldiff_ridge_flagged    : hptreldiff_ridge_survive   )->Fill(ptreldiffR);
1731                             (wouldFlag? hptotreldiff_ridge_flagged  : hptotreldiff_ridge_survive )->Fill(ptotreldiffR);
1732                             (wouldFlag? hqualitydiff_ridge_flagged  : hqualitydiff_ridge_survive )->Fill(qualitydiffR);
1733                             (wouldFlag? hntpc_ij_ridge_flagged      : hntpc_ij_ridge_survive      )->Fill(ntpc[i],ntpc[j]);
1734                             (wouldFlag? hdcaxydiff_ridge_flagged    : hdcaxydiff_ridge_survive    )->Fill(dcaxydiffR);
1735                             (wouldFlag? hdcazdiff_ridge_flagged     : hdcazdiff_ridge_survive     )->Fill(dcazdiffR);
1736                             (wouldFlag? hdedxdiff_ridge_flagged     : hdedxdiff_ridge_survive     )->Fill(dedxdiffR);
1737                             //---- sec 17.19: per-shared-TPC-layer cluster position agreement
1738                             {
1739                                 int nSharedLayer=0, nSectorMatch=0;
1740                                 for (int L=0; L<48; L++){
1741                                     bool hasI = layermask[i] & (1ULL<<(7+L));
1742                                     bool hasJ = layermask[j] & (1ULL<<(7+L));
1743                                     if (!hasI || !hasJ) continue;
1744                                     ++nSharedLayer;
1745                                     int si = tpcsector[i*48+L], sj = tpcsector[j*48+L];
1746                                     if (si==255 || sj==255 || si!=sj) continue;
1747                                     ++nSectorMatch;
1748                                     double dArc = (tpcarclen[i*48+L]-tpcarclen[j*48+L])*2.0;
1749                                     double dZ   = (tpcz[i*48+L]-tpcz[j*48+L])*0.1;
1750                                     double dmm  = sqrt(dArc*dArc+dZ*dZ);
1751                                     (wouldFlag? hDmm_ridge_flagged : hDmm_ridge_survive)->Fill(dmm);
1752                                 }
1753                                 if (nSharedLayer>0){
1754                                     (wouldFlag? hSectorMatchFrac_ridge_flagged : hSectorMatchFrac_ridge_survive)
1755                                         ->Fill((double)nSectorMatch/nSharedLayer);
1756                                 }
1757                             }
1758                             (wouldFlag? hSLvsSKF_ridge_flagged      : hSLvsSKF_ridge_survive      )->Fill(SL,SKF);
1759                             //---- sec 17.11: radial structure -- only for the two populations we're
1760                             //---- actually contrasting (the suspected fig-1b population vs the known-
1761                             //---- duplicate negative control), not every survivor.
1762                             bool isComplementary    = (!wouldFlag && SKF>=0.95 && SL>0.9);
1763                             if (wouldFlag || isComplementary){
1764                                 ULong64_t ma    = layermask[i] & kTPCLayerMask;
1765                                 ULong64_t mb    = layermask[j] & kTPCLayerMask;
1766                                 double sumA=0,sumB=0; int nA=0,nB=0;
1767                                 for (int L=0; L<48; L++){
1768                                     if (ma & (1ULL<<(7+L))){ sumA+=L; ++nA; }
1769                                     if (mb & (1ULL<<(7+L))){ sumB+=L; ++nB; }
1770                                 }
1771                                 if (nA>0 && nB>0){
1772                                     double meanA    = sumA/nA;
1773                                     double meanB    = sumB/nB;
1774                                     ULong64_t mLo   = (meanA<=meanB) ? ma : mb;
1775                                     ULong64_t mHi   = (meanA<=meanB) ? mb : ma;
1776                                     double idxLo    = (meanA<=meanB) ? meanA : meanB;
1777                                     double idxHi    = (meanA<=meanB) ? meanB : meanA;
1778                                     TH1D *hLo   = wouldFlag? hTPClayer_lower_flagged : hTPClayer_lower_complementary;
1779                                     TH1D *hHi   = wouldFlag? hTPClayer_upper_flagged : hTPClayer_upper_complementary;
1780                                     TH2D *h2    = wouldFlag? hTPCmeanIdx_flagged     : hTPCmeanIdx_complementary;
1781                                     for (int L=0; L<48; L++){
1782                                         if (mLo & (1ULL<<(7+L))) hLo->Fill(L);
1783                                         if (mHi & (1ULL<<(7+L))) hHi->Fill(L);
1784                                     }
1785                                     h2->Fill(idxLo,idxHi);
1786                                 }
1787                             }
1788                         }
1789                     }
1790                     if (!doSplitRemoval) continue;              // scan not yet enabled -- monitor only, don't flag
1791                     if (SKF < valSiKeyCut) continue;            // sharp gate, sec 12a.4/13.8.1/13.9 -- SiSplitScore --
1792                                                                 // same-Si-seed requirement for BOTH paths below
1793                     //---- sec 17.14: two independent flagging paths, both requiring the SKF gate above
1794                     //---- (same real particle already established) -- SL alone only ever sees pad-row
1795                     //---- OVERLAP (sec 17.11), so it can only catch the "duplicate" mechanism (path 1).
1796                     //---- RadialGap sees radial POSITION instead, catching the "complementary"/STAR-fig-1b
1797                     //---- mechanism SL is structurally blind to (path 2, mutually exclusive with path 1 by
1798                     //---- construction -- RG was only computed/checked because SL already failed path 1).
1799                     bool flagDuplicate  = (SL < valSLCut);                  // path 1 -- live since sec 17.6
1800                     bool flagComplementary  = (!flagDuplicate && !DISABLE_RG && RG>=0.0 && RG>=valRadialGapCut);    // path 2 -- sec 17.14/17.17
1801                     //---- path 3 (README_SplitTracks573 sec 8, opt-in): exact MVTX match on >= valFullMVTX common
1802                     //---- layers, and abs(1/pt1-1/pt2) >= valFullMVTXdinv, whatever SL/RG say.
1803                     //---- sec 15: ">= valFullMVTX MATCHED MVTX layers" (was: all layers matched and >= N of them;
1804                     //---- identical for N = 3). Two shared MVTX clusters plus the common vertex already fix the
1805                     //---- trajectory, so N = 2 is still one particle unless abs(1/pt1-1/pt2) is tiny.
1806                     int nMatchMVTX  = (int)std::lround(mvtxFrac*mvtxDen);
1807                     bool flagFullMVTX   = (!flagDuplicate && !flagComplementary && valFullMVTX>0
1808                                         && mvtxDen>0 && nMatchMVTX>=valFullMVTX
1809                                         && fabs(1.0/pt[i]-1.0/pt[j])>=valFullMVTXdinv);
1810                     if (!flagDuplicate && !flagComplementary && !flagFullMVTX) continue;    // not flagged by any path
1811                     if (flagDuplicate) ++nFlagged_duplicate; else if (flagComplementary) ++nFlagged_complementary; else ++nFlagged_fullmvtx;
1812                     int loser = -1;
1813                     if (ntpc[i]!=ntpc[j]){
1814                         loser   = (ntpc[i]<ntpc[j]) ? i : j;
1815                     } else if (quality[i]!=quality[j]){
1816                         loser   = (quality[i]>quality[j]) ? i : j;  // worse (higher) quality loses, sec 9.5
1817                     }                                                   // else: still tied -> no loser, sec 9.5 pt.3
1818                     if (loser>=0
1819                      && std::find(nSplitFlagged_thisevt.begin(),nSplitFlagged_thisevt.end(),loser)==nSplitFlagged_thisevt.end()){
1820                         nSplitFlagged_thisevt.push_back(loser); // don't double-count if flagged by >1 partner
1821                         lsLosers_thisevt.push_back(loser);
1822                         loserPath_thisevt.push_back({loser, flagDuplicate ? 0 : (flagComplementary ? 1 : 2)});
1823                     }
1824                 }
1825             }
1826             //---- README_CQcomparison.md sec 2.2 (2026-10-02): in-peak V0 daughters as split-track PARTNERS. The loop above
1827             //---- skips them (a daughter must never be dropped, README_PID.md sec 12), so a split copy of a daughter survived
1828             //---- into the pair lists (p-Lambda Q < 0.2 GeV: 292 of 581 sibling pairs, V0page_ana573_v9). Same tests as above
1829             //---- -- LS: pregate, SiSplitScore gate, then the SL / RadialGap / MVTX paths; ULS: the SiSplitScore veto -- but
1830             //---- the daughter always wins. Daughter-daughter pairs never occur (pionidx has no daughters); no diagnostic
1831             //---- histogram of the loop above sees these pairs. Losers go in the same per-path lists.
1832             //---- 2026-10-03 (option 2b, README_CQcomparison sec 2.2 follow-up): Lambda/Lbar daughters only (both), LS paths only
1833             //---- inside dR(t,d) < DAU_DRMAX (no cone for a pion-tagged partner of a p/pbar: its ghost, dR tail to ~0.12; pi+Lambda Q<0.05
1834             //---- excess 4.13 -> 1.07, laptop-Claude's suggestion), ULS never removes a dE/dx (anti)proton (option 2c). K0S daughters are out: pi K0S had no low-Q excess in v9 yet lost
1835             //---- ~10% of its Q < 0.4 sibling pairs; LS beyond dR 0.05 made part of the p-Lambda dip (comparison/DauPartners.C).
1836             auto q2k    = [](double px1,double py1,double pz1,double m1,double px2,double py2,double pz2,double m2){    // = CalcRm dq
1837                 double e1=sqrt(px1*px1+py1*py1+pz1*pz1+m1*m1), e2=sqrt(px2*px2+py2*py2+pz2*pz2+m2*m2);
1838                 double qinv2    = (e1-e2)*(e1-e2)-(px1-px2)*(px1-px2)-(py1-py2)*(py1-py2)-(pz1-pz2)*(pz1-pz2);
1839                 double minv2    = (e1+e2)*(e1+e2)-(px1+px2)*(px1+px2)-(py1+py2)*(py1+py2)-(pz1+pz2)*(pz1+pz2);
1840                 if (minv2<=0.) return 0.;
1841                 double Q    = (m1*m1-m2*m2)/sqrt(minv2);
1842                 return sqrt(std::max(0.,Q*Q-qinv2));
1843             };
1844             for (int d=0; d<(*ntr); d++){
1845                 if (!isPeakDaughter(d) || chg[d]==0) continue;
1846                 const int ivd   = peakV0Of[d];
1847                 const int kvd   = (v0pid[ivd]==310) ? 0 : (v0pid[ivd]==3122) ? 1 : 2;
1848                 const bool dIsP = (kvd==1 && chg[d]>0) || (kvd==2 && chg[d]<0);
1849                 const int dcat  = 2*kvd + (kvd==0 ? (chg[d]>0 ? 0 : 1) : (dIsP ? 0 : 1));
1850                 if (kvd==0) continue;   // 2026-10-03 option 2b: K0S daughters out (pi K0S had no v9 excess; removal hurt there)
1851                 for (int t : pionidx){
1852                     bool sameCharge = (chg[d]*chg[t] > 0);
1853                     int path    = -1;
1854                     if (!sameCharge && dIsP){   // diagnostic, see hDauP_ulsSkfPid (2026-10-03)
1855                         const double skf    = SiSplitScore(&siclukey[d*7],&siclukey[t*7],layermask[d],layermask[t]);
1856                         const int pidt  = pidOf(t), side = (kvd==1) ? 0 : 1;
1857                         hDauP_ulsSkfPid->Fill(skf, 4*side+pidt);
1858                         if (pidt==2){
1859                             const int cls   = skf<valULSTestCut ? 0 : (skf<0.5 ? 1 : (skf<0.99 ? 2 : 3));
1860                             double deta = eta[d]-eta[t], dphi = phi[d]-phi[t];
1861                             if (dphi> M_PI) dphi -= 2*M_PI;
1862                             if (dphi<-M_PI) dphi += 2*M_PI;
1863                             hDauP_ulsP_QtV->Fill(q2k(pt[t]*cos(phi[t]),pt[t]*sin(phi[t]),pt[t]*sinh(eta[t]),Species_mass[2],
1864                                 v0px[ivd],v0py[ivd],v0pz[ivd],Species_mass[5+kvd]), 4*side+cls);
1865                             hDauP_ulsP_dpt->Fill(fabs(pt[t]-pt[d])/pt[d], 4*side+cls);
1866                             hDauP_ulsP_dR->Fill(sqrt(deta*deta+dphi*dphi), 4*side+cls);
1867                         }
1868                     }
1869                     if (sameCharge && doSplitRemoval && doDauCheck){
1870                         double deta = eta[d]-eta[t];
1871                         double dphi = phi[d]-phi[t];
1872                         if (dphi> M_PI) dphi -= 2*M_PI;
1873                         if (dphi<-M_PI) dphi += 2*M_PI;
1874                         const double dR2    = deta*deta+dphi*dphi;
1875                         if (!InLSPregate(deta,dphi,pt[d],pt[t])) continue;
1876                         if (SiSplitScore(&siclukey[d*7],&siclukey[t*7],layermask[d],layermask[t]) < valSiKeyCut) continue;
1877                         double SL   = ComputeSplitSL(layermask[d],layermask[t]);
1878                         double RG   = ComputeRadialGap(layermask[d],layermask[t]);
1879                         double mvtxFrac,inttFrac; int mvtxDen,inttDen;
1880                         SiKeyFracSplit(&siclukey[d*7],&siclukey[t*7],layermask[d],layermask[t],mvtxFrac,mvtxDen,inttFrac,inttDen);
1881                         int nMatchMVTX  = (int)std::lround(mvtxFrac*mvtxDen);
1882                         if (SL < valSLCut)                                          path = 0;
1883                         else if (!DISABLE_RG && RG>=0.0 && RG>=valRadialGapCut) path = 1;
1884                         else if (valFullMVTX>0 && mvtxDen>0 && nMatchMVTX>=valFullMVTX
1885                                 && fabs(1.0/pt[d]-1.0/pt[t])>=valFullMVTXdinv)      path = 2;
1886                         if (path>=0){
1887                             const int pidt  = pidOf(t);
1888                             hDauP_dRLS->Fill(sqrt(dR2), 4*(dcat-2)+pidt);
1889                             const bool wide = dIsP && pidt==0;  // a pion-tagged LS partner of a p/pbar is its ghost: no cone (2026-10-03)
1890                             if (!wide && dR2 >= DAU_DRMAX*DAU_DRMAX) path = -1; // option 2: close copies only
1891                         }
1892                     } else if (!sameCharge && doULSTest && doDauCheck && (dIsP || pidOf(t)!=2)){    // ULS ghost of the daughter (no angular
1893                         //---- gate, as in the main ULS veto). Option 2c: a ghost is built from the daughter's own hits, so it looks like the
1894                         //---- daughter: a proton-tagged ULS partner of a PION daughter is a real proton (removing it made the v10 p-Lambda dip)
1895                         //---- and is never removed. v13 (2026-10-03): for a p/pbar daughter the proton-tagged partner IS its wrong-sign copy:
1896                         //---- ana573_v12ulsP (chunks 00-19, comparison/DauProtons.C) -- 652 proton-tagged ULS partners of Lbar pbar share Si
1897                         //---- (498 all of it), dR ~0.06 vs 1.9, = 38% of the p-Lbar sibling pairs at Q < 0.15 (34% for pbar-Lambda)
1898                         if (SiSplitScore(&siclukey[d*7],&siclukey[t*7],layermask[d],layermask[t])>=valULSTestCut) path = 3;
1899                     }
1900                     if (path<0) continue;
1901                     ++nFlagged_dau[path==3 ? 1 : 0];
1902                     hDauP_n[0]->Fill(path,dcat);
1903                     if (std::find(nSplitFlagged_thisevt.begin(),nSplitFlagged_thisevt.end(),t)==nSplitFlagged_thisevt.end()){
1904                         {   // README_CQcomparison sec 2.2 follow-up: kinematics of the NEW loser t vs daughter d and parent V0
1905                             double deta = eta[d]-eta[t], dphi = phi[d]-phi[t];
1906                             if (dphi> M_PI) dphi -= 2*M_PI;
1907                             if (dphi<-M_PI) dphi += 2*M_PI;
1908                             int pt_ = pidOf(t);
1909                             double mt   = Species_mass[pt_==1 ? 1 : (pt_==2 ? 2 : 0)];
1910                             double md   = Species_mass[dIsP ? 2 : 0];
1911                             double pxt=pt[t]*cos(phi[t]), pyt=pt[t]*sin(phi[t]), pzt=pt[t]*sinh(eta[t]);
1912                             double pxd=pt[d]*cos(phi[d]), pyd=pt[d]*sin(phi[d]), pzd=pt[d]*sinh(eta[d]);
1913                             hDauP_n[1]      ->Fill(path,dcat);
1914                             hDauP_dptrel[path]->Fill(fabs(pt[t]-pt[d])/pt[d],dcat);
1915                             hDauP_dR[path]  ->Fill(sqrt(deta*deta+dphi*dphi),dcat);
1916                             hDauP_Qtd[path] ->Fill(q2k(pxt,pyt,pzt,mt,pxd,pyd,pzd,md),dcat);
1917                             hDauP_QtV[path] ->Fill(q2k(pxt,pyt,pzt,mt,v0px[ivd],v0py[ivd],v0pz[ivd],Species_mass[5+kvd]),dcat);
1918                             hDauP_skf[path] ->Fill(SiSplitScore(&siclukey[d*7],&siclukey[t*7],layermask[d],layermask[t]),dcat);
1919                             hDauP_tpid[path]->Fill(pt_ + (chg[t]<0 ? 4 : 0),dcat);
1920                         }
1921                         nSplitFlagged_thisevt.push_back(t);
1922                         if (path<3) lsLosers_thisevt.push_back(t);
1923                         loserPath_thisevt.push_back({t,path});
1924                     }
1925                 }
1926             }
1927             //---- sec 15 follow-up: unrestricted-angle Qinv-vs-SiSplitScore check (see the
1928             //---- histogram declarations above for the full rationale). Separate loop, same
1929             //---- pionidx list, deliberately NOT interleaved with the box/sideband logic above to
1930             //---- keep this purely diagnostic addition easy to reason about/remove independently.
1931             for (size_t ii=0;ii<pionidx.size();ii++){
1932                 for (size_t jj=ii+1;jj<pionidx.size();jj++){
1933                     int i=pionidx[ii], j=pionidx[jj];
1934                     if (chg[i]*chg[j]>0) continue;          // opposite-charge (ULS) only, any angle
1935                     double mpiA     = Species_mass[0];
1936                     double pxA2     = pt[i]*cos(phi[i]), pyA2 = pt[i]*sin(phi[i]), pzA2 = pt[i]*sinh(eta[i]);
1937                     double eA2      = sqrt(pxA2*pxA2+pyA2*pyA2+pzA2*pzA2+mpiA*mpiA);
1938                     double pxB2     = pt[j]*cos(phi[j]), pyB2 = pt[j]*sin(phi[j]), pzB2 = pt[j]*sinh(eta[j]);
1939                     double eB2      = sqrt(pxB2*pxB2+pyB2*pyB2+pzB2*pzB2+mpiA*mpiA);
1940                     double Qinv2    = sqrt(std::max(0.0,(pxA2-pxB2)*(pxA2-pxB2)+(pyA2-pyB2)*(pyA2-pyB2)+(pzA2-pzB2)*(pzA2-pzB2)-(eA2-eB2)*(eA2-eB2)));
1941                     if (Qinv2>=0.3) continue;               // match hCQ_0's own range
1942                     double SKFall   = SiSplitScore(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
1943                     if (SKFall>=0.60){
1944                         hQinv_allULS_hiSKF  ->Fill(Qinv2);
1945                     } else {
1946                         hQinv_allULS_loSKF  ->Fill(Qinv2);
1947                     }
1948                 }
1949             }
1950             //---- README_SplitTracks573 sec 6: diagnostic only (see the booking). Runs after all
1951             //---- pre-pass flagging, so "surv" is final. Same pregate and cuts as the LS path above.
1952             {
1953                 static const double D_DETA  = 0.022, D_DPHI = 4.1*M_PI/180.0;   // = PREGATE_DETA/DPHI
1954                 auto flagged = [&](int t){ return std::find(nSplitFlagged_thisevt.begin(),nSplitFlagged_thisevt.end(),t)!=nSplitFlagged_thisevt.end(); };
1955                 std::vector<int> ls;
1956                 for (int jt=0;jt<(*ntr);jt++){
1957                     if (isPeakDaughter(jt) || !AcceptTrack(jt) || (doOldPID && dedx70s[jt]>=400.) || chg[jt]==0) continue;  // README_PID.md sec 8
1958                     ls.push_back(jt);
1959                 }
1960                 for (int jt : ls){  // sec 28: sector-gap spokes (booking above)
1961                     const double aR = 0.3*1.4/2.0*0.55;
1962                     int q       = chg[jt]>0 ? 0 : 1;
1963                     double pd   = phi[jt]*180.0/M_PI;
1964                     hSpoke[q][0]->Fill(pd,pt[jt]);
1965                     if (aR/pt[jt]<1.0){
1966                         double pr   = pd + (chg[jt]>0 ? 1.0 : -1.0)*asin(aR/pt[jt])*180.0/M_PI; // sign from the data (sec 28): the gaps straighten
1967                         while (pr>= 180.) pr -= 360.;
1968                         while (pr< -180.) pr += 360.;
1969                         hSpoke[q][1]->Fill(pr,pt[jt]);
1970                     }
1971                 }
1972                 {   // sec 25: cosmic test (booking above)
1973                     const double mpi    = 0.13957;
1974                     auto rap = [&](int t){ double pz=pt[t]*sinh(eta[t]), p2=pt[t]*pt[t]+pz*pz, E=sqrt(p2+mpi*mpi); return 0.5*log((E+pz)/(E-pz)); };
1975                     std::vector<int> kp;
1976                     for (int t : ls) if (!flagged(t)) kp.push_back(t);
1977                     for (size_t ii=0;ii<kp.size();ii++) for (size_t jj=ii+1;jj<kp.size();jj++){
1978                         int i=kp[ii], j=kp[jj];
1979                         if (fabs(rap(i)-rap(j))>=0.1) continue;
1980                         double adp  = fabs(phi[i]-phi[j]); if (adp>M_PI) adp = 2*M_PI-adp;
1981                         adp *= 180.0/M_PI;
1982                         bool os     = chg[i]*chg[j]<0;
1983                         int w   = -1;
1984                         if (os && adp>=170.) w = 0; else if (os && adp>=150. && adp<160.) w = 1; else if (!os && adp>=170.) w = 2;
1985                         if (w<0) continue;
1986                         double ax   = phi[i]*180.0/M_PI; ax = fmod(ax+360.,180.);
1987                         hCos_axis[w]    ->Fill(ax);
1988                         hCos_ptAsym[w]  ->Fill((pt[i]-pt[j])/(pt[i]+pt[j]));
1989                         hCos_eta[w]     ->Fill(eta[i]);
1990                         hCos_npi[w]     ->Fill(kp.size());
1991                         hCos_vtxntr[w]  ->Fill(*vtxntr);
1992                         hCos_pt[w]      ->Fill(0.5*(pt[i]+pt[j]));
1993                         hCos_etotoh[w]  ->Fill(*etotoh);
1994                         hCos_dcaxy[w]   ->Fill(dcaxy[i],dcaxy[j]);
1995                         hCos_dcaz[w]    ->Fill(dcaz[i],dcaz[j]);
1996                         {   // sec 25.1
1997                             double px1=pt[i]*cos(phi[i]), py1=pt[i]*sin(phi[i]), pz1=pt[i]*sinh(eta[i]);
1998                             double px2=pt[j]*cos(phi[j]), py2=pt[j]*sin(phi[j]), pz2=pt[j]*sinh(eta[j]);
1999                             double p1=sqrt(px1*px1+py1*py1+pz1*pz1), p2=sqrt(px2*px2+py2*py2+pz2*pz2);
2000                             double rs   = sqrt(pow(px1+px2,2)+pow(py1+py2,2)+pow(pz1+pz2,2))/(p1+p2);
2001                             double E    = sqrt(p1*p1+mpi*mpi)+sqrt(p2*p2+mpi*mpi);
2002                             double m2   = E*E - (pow(px1+px2,2)+pow(py1+py2,2)+pow(pz1+pz2,2));
2003                             double mi   = m2>0. ? sqrt(m2) : 0.;
2004                             hCos_psum[w]->Fill(rs);
2005                             hCos_minv[w]->Fill(mi);
2006                             if (rs<0.05){ hCos_axisBB[w]->Fill(ax); hCos_minvBB[w]->Fill(mi); }
2007                         }
2008                     }
2009                 }
2010                 if (fabs(*vtxz)<8.){    // sec 38: CM study (booking above)
2011                     double sg   = (*vtxz<0.) ? 1. : -1.;
2012                     auto wof    = [&](int t){ return (eta[t] + CM_K*(*vtxz))*sg; };
2013                     auto tpcfl  = [&](int t, int& f, int& l){ f=-1; l=-1; for (int b=7;b<55;b++) if ((layermask[t]>>b)&1ULL){ if (f<0) f=b-7; l=b-7; } };
2014                     auto nsi    = [&](int t){ int n=0; for (int b=0;b<7;b++) if ((layermask[t]>>b)&1ULL) ++n; return n; };
2015                     std::vector<int> kc; std::vector<int> cc;
2016                     for (int t : ls){
2017                         if (flagged(t)) continue;
2018                         double w    = wof(t);
2019                         hCM_w->Fill(w); hCM_w_ntpc->Fill(w,ntpc[t]); hCM_w_xing->Fill(w,*crossing);
2020                         int c   = (w>CM_BLO && w<CM_BHI) ? 0 : (fabs(w)>CM_CLO && fabs(w)<CM_CHI) ? 1 : -1;
2021                         if (c<0) continue;
2022                         int f,l; tpcfl(t,f,l); hCM_w_tpcfl[c]->Fill(f,l);
2023                         (c==0 ? kc : cc).push_back(t);
2024                     }
2025                     hCM_nbnc->Fill(cc.size(),kc.size());
2026                     for (int c=0;c<2;c++){
2027                         std::vector<int>& v = (c==0) ? kc : cc;
2028                         for (size_t ii=0;ii<v.size();ii++) for (size_t jj=ii+1;jj<v.size();jj++){
2029                             int i=v[ii], j=v[jj];
2030                             if (c==1 && wof(i)*wof(j)<0.) continue; // control: same side of the hole
2031                             int q   = (chg[i]*chg[j]<0) ? 0 : 1;
2032                             double dphi = phi[i]-phi[j]; if (dphi> M_PI) dphi -= 2*M_PI; if (dphi<-M_PI) dphi += 2*M_PI;
2033                             hCM_dphideta[c][q]->Fill(eta[i]-eta[j],dphi*180.0/M_PI);
2034                             hCM_ntpc12[c][q]->Fill(ntpc[i],ntpc[j]);
2035                             hCM_nsi12[c][q]->Fill(nsi(i),nsi(j));
2036                             hCM_pt12[c][q]->Fill(pt[i],pt[j]);
2037                             hCM_ntpcsum[c][q]->Fill(ntpc[i]+ntpc[j]);
2038                             hCM_skf[c][q]->Fill(SiSplitScore(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]));
2039                             hCM_dw[c][q]->Fill(wof(i)-wof(j));
2040                         }
2041                     }
2042                 }
2043                 for (auto& lp : loserPath_thisevt){ // sec 19: per-path losers among accepted pions
2044                     if (std::find(ls.begin(),ls.end(),lp.first)==ls.end()) continue;
2045                     hLoserPath_pt[lp.second][chg[lp.first]>0?0:1]->Fill(pt[lp.first]);
2046                 }
2047                 for (int jt : ls){  // sec 15: charge asymmetry
2048                     int c   = chg[jt]>0 ? 0 : 1;
2049                     bool los    = std::find(lsLosers_thisevt.begin(),lsLosers_thisevt.end(),jt)!=lsLosers_thisevt.end();
2050                     hAsym_all_pt[c]->Fill(pt[jt]); hAsym_all_eta[c]->Fill(eta[jt]); hAsym_all_phi[c]->Fill(phi[jt]); hAsym_all_ntpc[c]->Fill(ntpc[jt]);
2051                     if (los){ hAsym_los_pt[c]->Fill(pt[jt]); hAsym_los_eta[c]->Fill(eta[jt]); hAsym_los_phi[c]->Fill(phi[jt]); hAsym_los_ntpc[c]->Fill(ntpc[jt]); }
2052                 }
2053                 for (size_t ii=0;ii<ls.size();ii++){
2054                     for (size_t jj=ii+1;jj<ls.size();jj++){
2055                         int i=ls[ii], j=ls[jj];
2056                         if (chg[i]*chg[j]<=0) continue;
2057                         double deta = eta[i]-eta[j];
2058                         double dphi = phi[i]-phi[j];
2059                         if (dphi> M_PI) dphi -= 2*M_PI;
2060                         if (dphi<-M_PI) dphi += 2*M_PI;
2061                         double dphideg  = dphi*180.0/M_PI;
2062                         if (fabs(deta)>=0.08 || fabs(dphideg)>=30.) continue;
2063                         int ic  = chg[i]>0 ? 0 : 1;
2064                         int ipc = std::min(pt[i],pt[j])<0.2 ? 0 : 1;
2065                         bool surv   = !flagged(i) && !flagged(j);
2066                         double SKF  = SiSplitScore(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j]);
2067                         bool hi     = (SKF>=valSiKeyCut);
2068                         {   // fine dphi0 slices (booking above); abs(deta) out to 0.08
2069                             int k   = (int)(DPsCentre(pt[i],pt[j])*180.0/M_PI/GATEFINE_W);
2070                             if (k>=0 && k<NGATEFINE){
2071                                 hGateFine_all[k]->Fill(deta,dphideg);
2072                                 if (hi)         hGateFine_hi[k]    ->Fill(deta,fabs(dphideg));
2073                                 if (hi && surv) hGateFine_hiSurv[k]->Fill(deta,fabs(dphideg));
2074                                 if (surv)       hGateFine_surv[k]  ->Fill(deta,fabs(dphideg));
2075                             }
2076                         }
2077                         if (fabs(deta)>=0.05) continue;
2078                         {
2079                             double dinv = fabs(1.0/pt[i]-1.0/pt[j]);
2080                             int ib      = -1;
2081                             for (int k=0;k<NGATEVIS;k++) if (dinv>=GATEVIS_EDGE[k] && dinv<GATEVIS_EDGE[k+1]) ib = k;
2082                             if (ib>=0){
2083                                 hGateVis_all[ib]->Fill(deta,dphideg);
2084                                 if (hi)   hGateVis_hi[ib]  ->Fill(deta,dphideg);
2085                                 if (surv) hGateVis_surv[ib]->Fill(deta,dphideg);
2086                                 double res  = fabs(dphideg) - DPsCentre(pt[i],pt[j])*180.0/M_PI;
2087                                 if (hi)   hGateVis_hiRes[ib]  ->Fill(deta,res);
2088                                 if (hi)   hGateVis_dinvHi[ib] ->Fill(dinv);
2089                                 if (surv) hGateVis_survRes[ib]->Fill(deta,res);
2090                             }
2091                         }
2092                         bool cen    = (fabs(deta)<0.034 && fabs(dphideg)<10.);
2093                         if (hi){    // sec 15: silicon match code
2094                             ULong64_t mA=layermask[i], mB=layermask[j]; int nM=0,dM=0,nI=0,dI=0;
2095                             for (int L=0;L<7;L++){ bool h=((mA>>L)&1ULL)||((mB>>L)&1ULL); bool mt=(siclukey[i*7+L]==siclukey[j*7+L] && siclukey[i*7+L]!=kSiKeySentinel);
2096                                 if (L<3){ if(h)++dM; if(mt)++nM; } else { if(h)++dI; if(mt)++nI; } }
2097                             double xm=dM*4+nM, yi=dI*5+nI;
2098                             bool inG    = InLSPregate(deta,dphi,pt[i],pt[j]);
2099                             if (cen && !surv)        hD573_match[0]->Fill(xm,yi);
2100                             if (cen && surv && inG)  hD573_match[1]->Fill(xm,yi);
2101                             if (fabs(deta)>=0.04)    hD573_match[2]->Fill(xm,yi);
2102                         }
2103                         bool side   = (fabs(deta)<0.034 && fabs(dphideg)>=10.);
2104                         if (hi){
2105                             hD573_pos_allHi[ic][ipc]    ->Fill(deta,dphideg);
2106                             hD573_dphiPtinv_allHi[ic]   ->Fill(2.0/(pt[i]+pt[j]),dphideg*chg[i]);
2107                             if (surv) hD573_pos_survHi[ic][ipc]->Fill(deta,dphideg);
2108                             hD573_dphiDinvpt_allHi[ic]  ->Fill(fabs(1.0/pt[i]-1.0/pt[j]),fabs(dphideg));
2109                             if (surv) hD573_dphiDinvpt_survHi[ic]->Fill(fabs(1.0/pt[i]-1.0/pt[j]),fabs(dphideg));
2110                         }
2111                         if (surv && cen)  hD573_SKF_surv_cen[ic][ipc] ->Fill(SKF);
2112                         if (surv && side) hD573_SKF_surv_side[ic][ipc]->Fill(SKF);
2113                         if (!(cen && hi)) continue;
2114                         int oc;
2115                         bool inEll  = InLSPregate(deta,dphi,pt[i],pt[j]);   // same gate as the LS path
2116                         if (!surv)          oc = 0;
2117                         else if (!inEll)    oc = 1;
2118                         else {
2119                             double SL   = ComputeSplitSL(layermask[i],layermask[j]);
2120                             double RG   = ComputeRadialGap(layermask[i],layermask[j]);
2121                             double mF,iF; int mD,iD;
2122                             SiKeyFracSplit(&siclukey[i*7],&siclukey[j*7],layermask[i],layermask[j],mF,mD,iF,iD);
2123                             bool fl     = (SL<valSLCut) || (!DISABLE_RG && RG>=0.0 && RG>=valRadialGapCut)
2124                                         || (valFullMVTX>0 && mD>0 && std::lround(mF*mD)>=valFullMVTX && fabs(1.0/pt[i]-1.0/pt[j])>=valFullMVTXdinv);
2125                             hD573_SL_survInEll[ic][ipc]->Fill(SL);
2126                             if (!doSplitRemoval)    oc = 4;
2127                             else if (!fl)           oc = 2;
2128                             else if (ntpc[i]==ntpc[j] && quality[i]==quality[j]) oc = 3;
2129                             else                    oc = 4;
2130                         }
2131                         hD573_outcome[ic][ipc]->Fill(oc);
2132                     }
2133                 }
2134             }
2135             if (nSplitFlagged_thisevt.size()>0) ++nSplitFlagged_evts;
2136             nSplitFlagged_total += (long)nSplitFlagged_thisevt.size();
2137         }
2138         //
2139         ++sc_nevt; sc_nvtx += (*nvtx); ++nEvtTracks;    // segCells
2140         //---- track loop...
2141         int ntrkept     = 0;
2142         double npos=0,nneg=0;
2143         for (int ipaty=0;ipaty<NPairTypes;ipaty++){
2144             calcr_n_1[ipaty] = 0;
2145             calcr_n_2[ipaty] = 0;
2146         }
2147         std::vector<int> ridgePID((*ntr),-1);       // README_PID.md sec 8: PID (0 pi,1 K,2 p) of tracks entering the pair types
2148         std::vector<double> ridgeY((*ntr),0.);
2149         for (int it=0;it<(*ntr);it++){
2150             //
2151             hntpc0      ->Fill(ntpc[it]);
2152             hnmvtx0     ->Fill(nmvtx[it]);
2153             hnintt0     ->Fill(nintt[it]);
2154             heta0       ->Fill(eta[it]);
2155             hphi0       ->Fill(phi[it]);
2156             hpt0        ->Fill(pt[it]);
2157             hphieta0    ->Fill(eta[it],phi[it]);
2158             hpteta0     ->Fill(eta[it],pt[it]);
2159             hptphi0     ->Fill(phi[it],pt[it]);
2160             hdcaxy0     ->Fill(dcaxy[it]);
2161             hdcaz0      ->Fill(dcaz[it]);
2162             hdcaxyz0    ->Fill(dcaz[it],dcaxy[it]);
2163             hdedx0      ->Fill(dedx70s[it]);
2164             hndedx0     ->Fill(dedx70n[it]);
2165             hdedxphi0   ->Fill(phi[it],dedx70s[it]);
2166             if (eta[it]>=0) hdedxphi_etap0->Fill(phi[it],dedx70s[it]);
2167             if (eta[it]< 0) hdedxphi_etan0->Fill(phi[it],dedx70s[it]);
2168             //
2169             //------------------------------
2170             //
2171             //---- README_PID.md sec 10.3: the phi-mask page (before the mask)
2172             if (AcceptTrackBase(it) && chg[it]!=0){
2173                 int q   = chg[it]>0 ? 0 : 1;
2174                 double pd   = phi[it]*180.0/M_PI;
2175                 hmask_dcaxy[q]->Fill(pd,fabs(dcaxy[it]));
2176                 hmask_dcaz[q] ->Fill(pd,fabs(dcaz[it]));
2177                 hmask_pid->Fill(2*pidOf(it)+q, InSiPhiMask(it) ? 1 : 0);
2178             }
2179             //---- track quality cuts...
2180             bool keeptrk    = AcceptTrack(it);
2181             if (!keeptrk) continue;
2182             //---- split-track removal (see README_SplitTracks.md sec 13.8.4): this track was flagged
2183             //---- as the loser of a split-candidate pair above (SiKeyFrac+SL cascade, doSplitRemoval-gated).
2184             //---- `continue`s past PID tagging, the regular "kept" QA histograms, and Pvec_1/Pvec_2
2185             //---- (CF pair-building) entirely -- revives the pre-layermask KILLSPLITTRACKS mechanism
2186             //---- (sec 5), now driven by a far sharper discriminator. This also means the removed
2187             //---- track no longer inflates event multiplicity or rho1 (single-particle density) --
2188             //---- the gap a CalcRm::PairInfo-only pairwise exclusion could not close (sec 13.8.4).
2189             if (std::find(nSplitFlagged_thisevt.begin(),nSplitFlagged_thisevt.end(),it)!=nSplitFlagged_thisevt.end()){
2190                 heta_killed     ->Fill(eta[it]);
2191                 hphi_killed     ->Fill(phi[it]);
2192                 hpt_killed      ->Fill(pt[it]);
2193                 hntpc_killed    ->Fill(ntpc[it]);
2194                 hphieta_killed  ->Fill(eta[it],phi[it]);
2195                 hdcaxy_killed   ->Fill(dcaxy[it]);
2196                 hdcaz_killed    ->Fill(dcaz[it]);
2197                 hquality_killed ->Fill(quality[it]);
2198                 continue;
2199             }
2200             ++ntrkept;
2201             for (int c=0;c<NXTRIGCL;c++) if (xtrigIn[c]) ppt_xing[c]->Fill((*crossing),pt[it]);
2202             if (chg[it]  >   0.){ npos+=1.; }
2203             if (chg[it]  <   0.){ nneg+=1.; }
2204             //
2205             //---- assign PID...
2206             int thisParticleID  = -1;
2207             int thisSpecies     = -1;
2208             int thisindV0       = -1;
2209             int thisPIDk        = pidOf(it);    // README_PID.md: 0 pi, 1 K, 2 p, 3 unidentified
2210             if (thisPIDk<3 && chg[it]!=0){
2211                 const int pidPos[3] = {kParticleIDPionPlus ,kParticleIDKaonPlus ,kParticleIDProton    };
2212                 const int pidNeg[3] = {kParticleIDPionMinus,kParticleIDKaonMinus,kParticleIDAntiProton};
2213                 thisParticleID  = (chg[it]>0) ? pidPos[thisPIDk] : pidNeg[thisPIDk];
2214                 thisSpecies     = GetSpecies(thisParticleID);
2215             }
2216             thisindV0           = peakV0Of[it];     // README_PID.md sec 11/12: peak V0s only (tagged QA)
2217             //
2218             //------------------------------
2219             //
2220             hrirun_eta      ->Fill((*run),eta[it]);
2221             hrirun_phi      ->Fill((*run),phi[it]);
2222             hrirun_pt       ->Fill((*run),pt[it]);
2223             hrirun_ntpc     ->Fill((*run),ntpc[it]);
2224             hrirun_dedx     ->Fill((*run),dedx70s[it]);
2225             hrirun_ndedx    ->Fill((*run),dedx70n[it]);
2226             //
2227                 //hri_eta[0]        ->Fill(kRunIndex,eta[it]);
2228                 //hri_phi[0]        ->Fill(kRunIndex,phi[it]);
2229                 //hri_pt[0]     ->Fill(kRunIndex,pt[it]);
2230                 //hri_ntpc[0]       ->Fill(kRunIndex,ntpc[it]);
2231                 //hri_dedx[0]       ->Fill(kRunIndex,dedx70s[it]);
2232                 //hri_ndedx[0]  ->Fill(kRunIndex,dedx70n[it]);
2233             if (goodClock){
2234                 //hri_eta[1]        ->Fill(kRunIndex,eta[it]);
2235                 //hri_phi[1]        ->Fill(kRunIndex,phi[it]);
2236                 //hri_pt[1]     ->Fill(kRunIndex,pt[it]);
2237                 //hri_ntpc[1]       ->Fill(kRunIndex,ntpc[it]);
2238                 //hri_dedx[1]       ->Fill(kRunIndex,dedx70s[it]);
2239                 //hri_ndedx[1]  ->Fill(kRunIndex,dedx70n[it]);
2240             }
2241             if (goodMB){
2242                 //hri_eta[2]        ->Fill(kRunIndex,eta[it]);
2243                 //hri_phi[2]        ->Fill(kRunIndex,phi[it]);
2244                 //hri_pt[2]     ->Fill(kRunIndex,pt[it]);
2245                 //hri_ntpc[2]       ->Fill(kRunIndex,ntpc[it]);
2246                 //hri_dedx[2]       ->Fill(kRunIndex,dedx70s[it]);
2247                 //hri_ndedx[2]  ->Fill(kRunIndex,dedx70n[it]);
2248             }
2249             if (goodJet){
2250                 //hri_eta[3]        ->Fill(kRunIndex,eta[it]);
2251                 //hri_phi[3]        ->Fill(kRunIndex,phi[it]);
2252                 //hri_pt[3]     ->Fill(kRunIndex,pt[it]);
2253                 //hri_ntpc[3]       ->Fill(kRunIndex,ntpc[it]);
2254                 //hri_dedx[3]       ->Fill(kRunIndex,dedx70s[it]);
2255                 //hri_ndedx[3]  ->Fill(kRunIndex,dedx70n[it]);
2256             }
2257             if (goodPho){
2258                 //hri_eta[4]        ->Fill(kRunIndex,eta[it]);
2259                 //hri_phi[4]        ->Fill(kRunIndex,phi[it]);
2260                 //hri_pt[4]     ->Fill(kRunIndex,pt[it]);
2261                 //hri_ntpc[4]       ->Fill(kRunIndex,ntpc[it]);
2262                 //hri_dedx[4]       ->Fill(kRunIndex,dedx70s[it]);
2263                 //hri_ndedx[4]  ->Fill(kRunIndex,dedx70n[it]);
2264             }
2265             //
2266 //          hntpc       ->Fill(ntpc[it]+nmvtx[it]+nintt[it]);
2267             hntpc       ->Fill(ntpc[it]);
2268             hnmvtx      ->Fill(nmvtx[it]);
2269             hnintt      ->Fill(nintt[it]);
2270             heta        ->Fill(eta[it]);
2271             hphi        ->Fill(phi[it]);
2272             hpt         ->Fill(pt[it]);
2273             hphieta         ->Fill(eta[it],phi[it],1.);
2274             ++sc_ntrk; sc_sumpt += pt[it]; sc_sumntpc += ntpc[it]; sc_sadxy += fabs(dcaxy[it]); sc_sadz += fabs(dcaz[it]);  // segCells
2275             if (thisPIDk<3 && chg[it]!=0) ++sc_nid[2*thisPIDk+(chg[it]<0 ? 1 : 0)];
2276             for (int L=0;L<48;L++){
2277                 const int sct = tpcsector[it*48+L];
2278                 if (sct==255) continue;
2279                 const int fe    = scFee(sct,L,2.0*tpcarclen[it*48+L]/scR[L]);   // phi_local (rad)
2280                 if (fe>=0) sc_hits[sct*SC_NFEE + fe]    += 1.f;
2281             }
2282             hntpc_phieta    ->Fill(eta[it],phi[it],      ntpc[it] );
2283             habsdcaxy_phieta->Fill(eta[it],phi[it],fabs(dcaxy[it]));
2284             habsdcaz_phieta ->Fill(eta[it],phi[it], fabs(dcaz[it]));
2285             hdedx_phieta    ->Fill(eta[it],phi[it],   dedx70s[it] );
2286             if (chg[it]!=0){    // README_PID.md sec 10.2
2287                 const double aR = 0.3*1.4/2.0*0.55;
2288                 int q       = chg[it]>0 ? 0 : 1;
2289                 double pd   = phi[it]*180.0/M_PI, pr = -999.;
2290                 if (aR/pt[it]<1.0){ pr = pd + (chg[it]>0 ? 1.0 : -1.0)*asin(aR/pt[it])*180.0/M_PI; while (pr>=180.) pr -= 360.; while (pr<-180.) pr += 360.; }
2291                 const double ad[2]  = {fabs(dcaxy[it]),fabs(dcaz[it])};
2292                 for (int d=0;d<2;d++){ hadca_phi[d][q][0]->Fill(pd,ad[d]); if (pr>-900.) hadca_phi[d][q][1]->Fill(pr,ad[d]); }
2293             }
2294             //
2295             int kch=0; if(chg[it]<0){ kch=1; }
2296             if (thisindV0>=0){              // is this track a v0 daughter?
2297                 int kv0=0;
2298                 if (v0pid[thisindV0]==  310 ) kv0 = 0;
2299                 if (v0pid[thisindV0]==  3122) kv0 = 1;
2300                 if (v0pid[thisindV0]== -3122) kv0 = 2;
2301                 if (chg[it]         <   0   ) kch = 1;      // pos=0, neg=1
2302                 double fillpt   = pt[it]; if (fillpt>4.99){ fillpt = 4.99; }
2303                                   hpt_tagged[kch][kv0]  ->Fill(fillpt);
2304                                hdedxp_tagged[kch][kv0]  ->Fill(ptot[it],dedx70s[it]);
2305                             hdedxKFPp_tagged[kch][kv0]  ->Fill(ptot[it],dedxKFP[it]);
2306                 //---- README_PID.md: the proton is the + daughter of a Lambda, the - daughter of a Lbar
2307                 if ((kv0==1 && kch==0) || (kv0==2 && kch==1))   hPIDtagged[0]->Fill(ptot[it],thisPIDk);
2308                 if  (kv0==0)                                    hPIDtagged[1]->Fill(ptot[it],thisPIDk);
2309             }
2310             //
2311             if (nmvtx[it]>0) heta_mvt   ->Fill(eta[it]);
2312             if (nintt[it]>0) heta_int   ->Fill(eta[it]);
2313             if (nmvtx[it]>0&&nintt[it]>0) heta_mvtint   ->Fill(eta[it]);
2314             hpteta      ->Fill(eta[it],pt[it]);
2315             hptphi      ->Fill(phi[it],pt[it]);
2316             hdedxp      ->Fill(ptot[it],dedx70s[it]);
2317             hdedxpz     ->Fill(ptot[it],dedx70s[it]);
2318             hdedxKFPp   ->Fill(ptot[it],dedxKFP[it]);
2319             hdedxKFPp_id[thisPIDk]  ->Fill(ptot[it],dedxKFP[it]);
2320             if (dedx70s[it]>0.){ hdedxratio_p->Fill(ptot[it],dedxKFP[it]/dedx70s[it]); hdedxratio2_p->Fill(ptot[it],dedxKFP[it]/dedx70s[it]); }
2321             hPIDcount   ->Fill(2*thisPIDk + kch);
2322             if (thisPIDk<3){ hpt_id[thisPIDk][kch]->Fill(pt[it]); hdcaxy_id[thisPIDk][kch]->Fill(pt[it],dcaxy[it]); hdcaz_id[thisPIDk][kch]->Fill(pt[it],dcaz[it]);
2323                 const double dv[4]  = {dcaxy[it],fabs(dcaxy[it]),dcaz[it],fabs(dcaz[it])};
2324                 for (int iv=0;iv<4;iv++) pdca_etaphi_id[thisPIDk][kch][iv]->Fill(eta[it],phi[it],dv[iv]);
2325                 hphieta_id[thisPIDk][kch]->Fill(eta[it],phi[it]); }
2326             hdcaxy      ->Fill(dcaxy[it]);
2327             hdcaz       ->Fill(dcaz[it]);
2328             hdcaxyz     ->Fill(dcaz[it],dcaxy[it]);
2329             hquality    ->Fill(quality[it]);
2330             hdedx       ->Fill(dedx70s[it]);
2331             hndedx      ->Fill(dedx70n[it]);
2332             hdedxphi    ->Fill(phi[it],dedx70s[it]);
2333             if (eta[it]>=0) hdedxphi_etap->Fill(phi[it],dedx70s[it]);
2334             if (eta[it]< 0) hdedxphi_etan->Fill(phi[it],dedx70s[it]);
2335             //
2336             hetazvtx                        ->Fill((*vtxz),eta[it]);
2337             if (pt[it]>0.5) hetazvtx_pta    ->Fill((*vtxz),eta[it]);
2338             if (pt[it]>1.0) hetazvtx_ptb    ->Fill((*vtxz),eta[it]);
2339             //
2340             //----- save for the class....
2341             //
2342             //---- h+, h- (2026-10-11): every accepted charged track, the dE/dx PID ignored
2343             const int thisHadronID  = (chg[it]>0) ? kParticleIDhPlus : ((chg[it]<0) ? kParticleIDhMinus : -1);
2344             if (thisParticleID<0 && thisHadronID<0) continue;
2345             //
2346             double thiseta,thisphi,thispt;
2347             if (!USESEEDTRACKS){
2348                 thiseta = eta[it];
2349                 thisphi = phi[it];
2350                 thispt  =  pt[it];
2351             } else if (USESEEDTRACKS){
2352                 thiseta = seedeta[it];
2353                 thisphi = seedphi[it];
2354                 thispt  =  seedpt[it];
2355             }
2356             //---- README_PID.md: CalcRm wants RAPIDITY (it builds pz = mt*sinh(y)). eta -> y with the PID mass;
2357             //---- the acceptance cut below stays on eta (the detector edge), and abs(y) <= abs(eta) always.
2358             auto yOf    = [&](int isp){ double mt = sqrt(thispt*thispt + Species_mass[isp]*Species_mass[isp]); return asinh(thispt*sinh(thiseta)/mt); };
2359             const int thisSpeciesH  = (thisHadronID>=0) ? GetSpecies(thisHadronID) : -1;    // h+, h-: the pion window and pT cut
2360             const double thisy  = (thisSpecies >=0) ? yOf(thisSpecies)  : -999.;
2361             const double thisyH = (thisSpeciesH>=0) ? thiseta : -999.;  // h+, h-: pseudorapidity (CalcRm fUseEta)
2362             if (thisParticleID>=0){
2363                 if (thisPIDk<3 && thiseta>=thisYL && thiseta<thisYU) hypt_id[thisPIDk]->Fill(thisy,thispt);
2364                 if (!isPeakDaughter(it) && thiseta>=-1.1 && thiseta<1.1){ ridgePID[it] = thisPIDk; ridgeY[it] = thisy; }
2365             }
2366             //---- is this track in the list of particle ipid for pair type ipaty (as its PID species, or as h+/h-)? yv = its y there
2367             auto inList = [&](int ipid, double yl, double yu, int ipaty, double& yv){
2368                 int isp;
2369                 if      (ipid==thisParticleID){ isp = thisSpecies;  yv = thisy;  }
2370                 else if (ipid==thisHadronID)  { isp = thisSpeciesH; yv = thisyH; }
2371                 else return false;
2372                 if (thispt < Species_ptmin[isp] || thispt >= Species_ptmax[isp]) return false;
2373                 if (thiseta< -pairEtaEdge[ipaty] || thiseta>=pairEtaEdge[ipaty]) return false;  // detector edge
2374                 return (yv >= yl && yv < yu);                                                   // class window (README_PID.md)
2375             };
2376             //---- add this track to all PairType indices that this species is included in...
2377             if (!NOCORRELATIONS){
2378                 for (int ipaty=0;ipaty<NPairTypes;ipaty++){
2379                     //
2380                     //---- protect against keeping a track for correlations that is a daughter of a V0 IN THIS SPECIFIC PAIR
2381                     //      keep track if not known as a V0 daughter
2382                     //      COULD keep track if a known daughter of V0, but that V0 is NOT part of this pair
2383                     if (isPeakDaughter(it)){        // README_PID.md sec 11/12: only peak V0s claim daughters
2384                         continue;
2385                     }
2386                     //
2387                     //---- check if this track is particle 1 / particle 2 for this pair type
2388                     double y1v=0., y2v=0.;
2389                     const bool accept1  = inList(PairTypes_Info[ipaty][0],pairYL1[ipaty],pairYU1[ipaty],ipaty,y1v);
2390                     const bool accept2  = inList(PairTypes_Info[ipaty][1],pairYL2[ipaty],pairYU2[ipaty],ipaty,y2v);
2391                     if (accept1){       // this track is "Particle 1" in this PairType...
2392                         int kk = calcr_n_1[ipaty];
2393                         if (kk== MAX_CALCR_N-1){ 
2394                             cout<<"reader::Loop -- PVEC1 FULL .. max="<<MAX_CALCR_N<<"\t ipaty="<<ipaty<<endl; 
2395                             exit(0);
2396                         }
2397                         Pvec_1[ipaty][kk][0]    = y1v;          // rapidity (README_PID.md)
2398                         Pvec_1[ipaty][kk][1]    = thisphi;
2399                         Pvec_1[ipaty][kk][2]    = thispt;
2400                         ++calcr_n_1[ipaty];
2401                     }   // end accept1
2402                     if (accept2){       // this track is "Particle 2" in this PairType...
2403                         int kk = calcr_n_2[ipaty];
2404                         if (kk== MAX_CALCR_N-1){
2405                             cout<<"reader::Loop -- PVEC2 FULL .. max="<<MAX_CALCR_N<<"\t ipaty="<<ipaty<<endl;
2406                             exit(0);
2407                         }
2408                         Pvec_2[ipaty][kk][0]    = y2v;          // rapidity (README_PID.md)
2409                         Pvec_2[ipaty][kk][1]    = thisphi;
2410                         Pvec_2[ipaty][kk][2]    = thispt;
2411                         ++calcr_n_2[ipaty];
2412                     }   // end accept2
2413                 }   // end pairtypes loop
2414             }   // end nocorrelations
2415             //
2416         }   // end track loop
2417         //---- README_PID.md sec 8: p-pi ULS ridge diagnostic (booking above)
2418         for (int ip=0;ip<(*ntr);ip++){
2419             if (ridgePID[ip]!=2) continue;
2420             for (int jp=0;jp<(*ntr);jp++){
2421                 if (ridgePID[jp]!=0 || chg[ip]*chg[jp]>=0) continue;
2422                 int c       = (chg[ip]>0) ? 0 : 1;
2423                 if (fabs(ridgeY[ip]-ridgeY[jp])>=0.7) continue;
2424                 int ineg    = (chg[ip]<0) ? ip : jp, ipos = (chg[ip]<0) ? jp : ip;
2425                 double dphiD    = phi[ineg]-phi[ipos];
2426                 while (dphiD> M_PI) dphiD -= 2*M_PI;
2427                 while (dphiD<-M_PI) dphiD += 2*M_PI;
2428                 dphiD   *= 180./M_PI;
2429                 hRidge_dphi[c]->Fill(dphiD);
2430                 int b   = (dphiD>=-10. && dphiD<0.) ? 0 : ((dphiD>=0. && dphiD<10.) ? 1 : -1);
2431                 if (b<0) continue;
2432                 double px1=pt[ip]*cos(phi[ip]), py1=pt[ip]*sin(phi[ip]), pz1=pt[ip]*sinh(eta[ip]);
2433                 double px2=pt[jp]*cos(phi[jp]), py2=pt[jp]*sin(phi[jp]), pz2=pt[jp]*sinh(eta[jp]);
2434                 double p1=sqrt(px1*px1+py1*py1+pz1*pz1), p2=sqrt(px2*px2+py2*py2+pz2*pz2);
2435                 auto minv   = [&](double m1, double m2){
2436                     double e1=sqrt(p1*p1+m1*m1), e2=sqrt(p2*p2+m2*m2);
2437                     double m2t=(e1+e2)*(e1+e2)-(px1+px2)*(px1+px2)-(py1+py2)*(py1+py2)-(pz1+pz2)*(pz1+pz2);
2438                     return m2t>0. ? sqrt(m2t) : 0.;
2439                 };
2440                 const double me = 0.000511;
2441                 hRidge_Mppi[b][c]   ->Fill(minv(Species_mass[2],Species_mass[0]));
2442                 hRidge_Mee[b][c]    ->Fill(minv(me,me));
2443                 double cosop    = (px1*px2+py1*py2+pz1*pz2)/(p1*p2);
2444                 hRidge_open[b][c]   ->Fill(acos(std::max(-1.,std::min(1.,cosop)))*180./M_PI);
2445                 hRidge_prat[b][c]   ->Fill(p2/p1);
2446                 hRidge_skf[b][c]    ->Fill(SiSplitScore(&siclukey[ip*7],&siclukey[jp*7],layermask[ip],layermask[jp]));
2447                 hRidge_dcap[b][c]   ->Fill(sqrt(dcaxy[ip]*dcaxy[ip]+dcaz[ip]*dcaz[ip]));
2448                 hRidge_dcapi[b][c]  ->Fill(sqrt(dcaxy[jp]*dcaxy[jp]+dcaz[jp]*dcaz[jp]));
2449                 hRidge_ptp[b][c]    ->Fill(pt[ip]);
2450             }
2451         }
2452         //
2453         //---- collect uncharged particles for class from V0 tree
2454         bool SHOWV0 = false;
2455         int nks=0,nla=0,nal=0;
2456         for (int iv0=0;iv0<(*nv0);iv0++){
2457             if (v0pid[iv0] ==   310){ ++nks; } else 
2458             if (v0pid[iv0] ==  3122){ ++nla; } else 
2459             if (v0pid[iv0] == -3122){ ++nal; }
2460         }
2461         if (nks) hKSxing->Fill((*crossing));        // this (*crossing) has a KS
2462         if (nla) hLAxing->Fill((*crossing));        // this (*crossing) has a LA
2463         if (nal) hALxing->Fill((*crossing));        // this (*crossing) has a AL
2464         //if ((*nv0)>1) SHOWV0 = true;  // 2026-09-24: multi-V0 row printout silenced (user request)
2465         if ((*nv0)>0){  counters[0] += 1; counters[1] += (*nv0); }      // count Total Number of Events w/ a V0, and Total Number of V0s
2466         if ((*nv0)>1){  counters[2] += 1; }                         // count Total Number of Events w/ TWO or more V0s
2467         for (int iv0=0;iv0<(*nv0);iv0++){
2468             int thisParticleID  = -1;
2469             int thisSpecies     = -1;
2470             double thiseta  = v0eta[iv0];
2471             double thisphi  = v0phi[iv0];
2472             double thispt   =  v0pt[iv0];
2473             double thismass = v0mass[iv0];
2474             //---- README_v0etaSpike.md: v0eta/v0phi/v0mass/v0ctau are sometimes an exact-0 sentinel
2475             //---- (likely KFParticle getter whose error calc failed); px,py,pz,ene are always good.
2476             if (v0eta[iv0]==0.0){   thiseta = asinh(v0pz[iv0]/v0pt[iv0]);   ++nv0fixEta;    }
2477             if (v0phi[iv0]==0.0){   thisphi = atan2(v0py[iv0],v0px[iv0]);   ++nv0fixPhi;    }
2478             if (v0mass[iv0]==0.0){
2479                 double m2   = v0ene[iv0]*v0ene[iv0] - v0ptot[iv0]*v0ptot[iv0];
2480                 thismass    = (m2>0.) ? sqrt(m2) : 0.;
2481                 ++nv0fixMass;
2482             }
2483             //---- README_PID.md: rapidity for CalcRm, from the V0 4-vector (px,py,pz,ene are always good)
2484             double thisy    = 0.5*log((v0ene[iv0]+v0pz[iv0])/(v0ene[iv0]-v0pz[iv0]));
2485             double thisctau = v0ctau[iv0];
2486             if (v0ctau[iv0]==0.0){  thisctau    = v0decaylen[iv0]*thismass/v0ptot[iv0]; ++nv0fixCtau;   }   // ctau = L*m/p
2487             double worseDCA = WorseDaughterPVDCA(dcaxy,dcaz,v0indtr1[iv0],v0indtr2[iv0]);
2488             if (v0pid[iv0] == 310){
2489                 thisParticleID  = kParticleIDKshort;
2490                 thisSpecies     = 5;
2491                 ++counters[3];
2492                 hKSmass_xing    ->Fill((*crossing),thismass);
2493                 hKSmass         ->Fill(thismass);
2494                 hKSpt           ->Fill(v0pt[iv0]);
2495                 hKSeta          ->Fill(thiseta);
2496                 hKSphi          ->Fill(thisphi);
2497                 hKSdira         ->Fill(v0dira[iv0]);
2498                 hKSpvdca        ->Fill(v0pvdca[iv0]);
2499                 hKSworsedca     ->Fill(worseDCA);
2500             } else if (v0pid[iv0] == 3122){
2501                 thisParticleID  = kParticleIDLambda;
2502                 thisSpecies     = 6;
2503                 ++counters[4];
2504                 hLAmass_xing    ->Fill((*crossing),thismass);
2505                 hLAmass         ->Fill(thismass);
2506                 hLApt           ->Fill(v0pt[iv0]);
2507                 hLAeta          ->Fill(thiseta);
2508                 hLAphi          ->Fill(thisphi);
2509                 hLAdira         ->Fill(v0dira[iv0]);
2510                 hLApvdca        ->Fill(v0pvdca[iv0]);
2511                 hLAworsedca     ->Fill(worseDCA);
2512                 if (worseDCA>=LA_WORSEDCA_CUT) hLAmass_clean->Fill(thismass);
2513             } else if (v0pid[iv0] == -3122){
2514                 thisParticleID  = kParticleIDAntiLambda;
2515                 thisSpecies     = 7;
2516                 ++counters[5];
2517                 hALmass_xing    ->Fill((*crossing),thismass);
2518                 hALmass         ->Fill(thismass);
2519                 hALpt           ->Fill(v0pt[iv0]);
2520                 hALeta          ->Fill(thiseta);
2521                 hALphi          ->Fill(thisphi);
2522                 hALdira         ->Fill(v0dira[iv0]);
2523                 hALpvdca        ->Fill(v0pvdca[iv0]);
2524                 hALworsedca     ->Fill(worseDCA);
2525                 if (worseDCA>=AL_WORSEDCA_CUT) hALmass_clean->Fill(thismass);
2526             }
2527             //
2528             if (SHOWV0){
2529                 cout<<iv0<<"\t pid= "<<v0pid[iv0]<<", m= "<<thismass
2530                     <<"\t eta= "<<thiseta<<" phi= "<<thisphi<<" pt= "<<v0pt[iv0]
2531                     <<" ctau= "<<thisctau
2532                     <<"\t trkID: "<<v0indtr1[iv0]<<" "<<v0indtr2[iv0]
2533                     <<"\t dedx: "<<dedx70s[v0indtr1[iv0]]<<" "<<dedx70s[v0indtr2[iv0]]
2534                     <<endl;
2535             }
2536             //
2537             //---- add this V0 to all PairType indices that this species is included in...
2538             //---- README_PID.md sec 12: only V0s in their mass peak enter the pairs (their daughters are out of the
2539             //---- track lists); an off-peak candidate would be paired with its own daughter
2540             const int kv    = (v0pid[iv0]==310) ? 0 : (v0pid[iv0]==3122) ? 1 : (v0pid[iv0]==-3122) ? 2 : -1;
2541             const bool v0pair   = v0InPeak(iv0);
2542             if (kv>=0){ ++nv0All[kv]; if (v0pair){ ++nv0Paired[kv]; ++sc_nv0[kv]; } }
2543             if (!NOCORRELATIONS && v0pair){
2544                 for (int ipaty=0;ipaty<NPairTypes;ipaty++){ 
2545                     //---- check if this track is particle 1 for this pair type
2546                     bool accept1    = false;
2547                     if (thisParticleID==PairTypes_Info[ipaty][0]) accept1 = true;
2548                     if (accept1){
2549                         if (thispt < Species_ptmin[thisSpecies]) accept1    = false;
2550                         if (thispt >=Species_ptmax[thisSpecies]) accept1    = false;
2551                         if (thiseta< -pairEtaEdge[ipaty] || thiseta>=pairEtaEdge[ipaty]) accept1    = false;    // detector edge
2552                         if (thisy  <  pairYL1[ipaty]   || thisy  >= pairYU1[ipaty]   ) accept1  = false;    // class window (README_PID.md)
2553                     }
2554                     //---- check if this track is particle 2 for this pair type
2555                     bool accept2    = false;
2556                     if (thisParticleID==PairTypes_Info[ipaty][1]) accept2 = true;
2557                     if (accept2){
2558                         if (thispt < Species_ptmin[thisSpecies]) accept2    = false;
2559                         if (thispt >=Species_ptmax[thisSpecies]) accept2    = false;
2560                         if (thiseta< -pairEtaEdge[ipaty] || thiseta>=pairEtaEdge[ipaty]) accept2    = false;    // detector edge
2561                         if (thisy  <  pairYL2[ipaty]   || thisy  >= pairYU2[ipaty]   ) accept2  = false;    // class window (README_PID.md)
2562                     }
2563                     if (accept1){       // this track is "Particle 1" in this PairType...
2564                         int kk = calcr_n_1[ipaty];
2565                         if (kk== MAX_CALCR_N-1){ 
2566                             cout<<"reader::Loop -- PVEC1 FULL .. max="<<MAX_CALCR_N<<"\t ipaty="<<ipaty<<endl; 
2567                             exit(0);
2568                         }
2569                         //cout<<"Incrementing 1... ipaty="<<ipaty<<"  k="<<kk<<"  n1="<<calcr_n_1[ipaty]<<" \t "<<ety<<endl;
2570                         Pvec_1[ipaty][kk][0]    = thisy;        // rapidity (README_PID.md)
2571                         Pvec_1[ipaty][kk][1]    = thisphi;
2572                         Pvec_1[ipaty][kk][2]    = thispt;
2573                         ++calcr_n_1[ipaty];
2574                     }
2575                     if (accept2){       // this track is "Particle 2" in this PairType...
2576                         int kk = calcr_n_2[ipaty];
2577                         if (kk== MAX_CALCR_N-1){
2578                             cout<<"reader::Loop -- PVEC2 FULL .. max="<<MAX_CALCR_N<<"\t ipaty="<<ipaty<<endl;
2579                             exit(0);
2580                         }
2581                         //cout<<"Incrementing 2... ipaty="<<ipaty<<"  k="<<kk<<"  n2="<<calcr_n_2[ipaty]<<endl;
2582                         Pvec_2[ipaty][kk][0]    = thisy;        // rapidity (README_PID.md)
2583                         Pvec_2[ipaty][kk][1]    = thisphi;
2584                         Pvec_2[ipaty][kk][2]    = thispt;
2585                         ++calcr_n_2[ipaty];
2586                     }
2587                     //
2588                 }   // end pairtypes loop
2589             }   // end nocorrelations
2590             //
2591         }   // end v0 loop
2592         //
2593         //---- README_CQcomparison.md sec 2.1-2.2: V0 daughter sharing / duplicates and <pT> vs N_ch (sibling only, every event,
2594         //---- independent of the nchLLHH class). Q = 2k* exactly as CalcRm::PairInfo's dq, 4-vectors from (pT, y, phi) and the
2595         //---- assigned masses.
2596         {
2597             auto v0kind = [&](int iv0){ return (v0pid[iv0]==310) ? 0 : (v0pid[iv0]==3122) ? 1 : (v0pid[iv0]==-3122) ? 2 : -1; };
2598             auto v0yOf  = [&](int iv0){ return 0.5*log((v0ene[iv0]+v0pz[iv0])/(v0ene[iv0]-v0pz[iv0])); };
2599             auto v0ptOf = [&](int iv0){ return sqrt(v0px[iv0]*v0px[iv0]+v0py[iv0]*v0py[iv0]); };
2600             auto inList = [&](int it, int k){   // track it enters the pair types as PID k (0 pi, 1 K, 2 p)
2601                 return it>=0 && it<(*ntr) && ridgePID[it]==k && pt[it]>=Species_ptmin[k] && fabs(ridgeY[it])<Species_yu[k];
2602             };
2603             auto v0InList   = [&](int iv0){ int kv = v0kind(iv0); return kv>=0 && v0InPeak(iv0) && v0ptOf(iv0)>=Species_ptmin[5+kv] && fabs(v0yOf(iv0))<Species_yu[5+kv]; };
2604             auto dRof   = [&](int a, int b){
2605                 double dp   = phi[a]-phi[b];
2606                 while (dp> M_PI) dp -= 2*M_PI;
2607                 while (dp<-M_PI) dp += 2*M_PI;
2608                 double de   = eta[a]-eta[b];
2609                 return sqrt(dp*dp+de*de);
2610             };
2611             auto dqOf   = [&](double pt1, double y1, double ph1, double m1, double pt2, double y2, double ph2, double m2){
2612                 double mt1=sqrt(pt1*pt1+m1*m1), mt2=sqrt(pt2*pt2+m2*m2);
2613                 double px1=pt1*cos(ph1), py1=pt1*sin(ph1), pz1=mt1*sinh(y1), e1=mt1*cosh(y1);
2614                 double px2=pt2*cos(ph2), py2=pt2*sin(ph2), pz2=mt2*sinh(y2), e2=mt2*cosh(y2);
2615                 double qinv2    = (e1-e2)*(e1-e2) - (px1-px2)*(px1-px2) - (py1-py2)*(py1-py2) - (pz1-pz2)*(pz1-pz2);
2616                 double minv2    = (e1+e2)*(e1+e2) - (px1+px2)*(px1+px2) - (py1+py2)*(py1+py2) - (pz1+pz2)*(pz1+pz2);
2617                 if (minv2<=0.) return 0.;
2618                 double Q    = (m1*m1-m2*m2)/sqrt(minv2);
2619                 return sqrt(std::max(0.,Q*Q-qinv2));
2620             };
2621             //---- daughter index of V0 iv0 with charge sign q (0 +, 1 -), or -1
2622             auto dauOf  = [&](int iv0, int q){
2623                 const int itd[2]    = {v0indtr1[iv0], v0indtr2[iv0]};
2624                 for (int kd=0;kd<2;kd++){
2625                     int it  = itd[kd];
2626                     if (it<0 || it>=(*ntr)) continue;
2627                     if ((q==0 && chg[it]>0) || (q==1 && chg[it]<0)) return it;
2628                 }
2629                 return -1;
2630             };
2631             auto shareDau   = [&](int a, int b){
2632                 const int ia[2] = {v0indtr1[a], v0indtr2[a]}, ib[2] = {v0indtr1[b], v0indtr2[b]};
2633                 for (int i=0;i<2;i++) for (int j=0;j<2;j++) if (ia[i]>=0 && ia[i]==ib[j]) return true;
2634                 return false;
2635             };
2636             //---- README_CQcomparison sec 2.3: separation maps (booking: hSep), only with "sepmaps" (doSepMaps; ~+5 min per chunk).
2637             //---- Own mixing: in-list Lambda/Lbar of the last SEP_DEPTH
2638             //---- events in the same 2 cm zvtx slice (|zvtx| < 16). dphi* as the tsep cut (CalcRm::PairInfo): sign +, min over R.
2639             if (doSepMaps){
2640                 struct SepDau   { double eta, phi, pt, q; };
2641                 struct SepV0    { int kv; double pt, y, phi; SepDau d[2]; };
2642                 static const int SEP_DEPTH  = 5;
2643                 static std::vector<std::deque<std::vector<SepV0>>> sepBuf(16);
2644                 const double aB = 0.3*fabs(field)/2.0;
2645                 std::vector<SepV0> cur;
2646                 for (int iv0=0;iv0<(*nv0);iv0++){
2647                     int kv  = v0kind(iv0);
2648                     if ((kv!=1 && kv!=2) || !v0InList(iv0)) continue;
2649                     int d0=dauOf(iv0,0), d1=dauOf(iv0,1);
2650                     if (d0<0 || d1<0) continue;
2651                     SepV0 v; v.kv=kv; v.pt=v0ptOf(iv0); v.y=v0yOf(iv0); v.phi=atan2(v0py[iv0],v0px[iv0]);
2652                     const int dd[2] = {d0,d1};
2653                     for (int k=0;k<2;k++) v.d[k] = {eta[dd[k]], phi[dd[k]], pt[dd[k]], (double)chg[dd[k]]};
2654                     cur.push_back(v);
2655                 }
2656                 auto fillSep    = [&](int t, int c, const SepV0& v, int sm){
2657                     double best=1e9, bde=0, bdp=0; int bk=-1;
2658                     for (int k=0;k<2;k++){
2659                         const SepDau& d = v.d[k];
2660                         double dp0  = (phi[t]-d.phi)*180.0/M_PI;
2661                         while (dp0>= 180.) dp0 -= 360.;
2662                         while (dp0< -180.) dp0 += 360.;
2663                         double ds   = dp0, dmin = 1e9;
2664                         for (double R=0.30; R<=0.781; R+=0.02){
2665                             double x1 = aB*R/pt[t], x2 = aB*R/d.pt;
2666                             if (x1>=1.0 || x2>=1.0) break;
2667                             double dd   = dp0 - (chg[t]*asin(x1) - d.q*asin(x2))*180.0/M_PI;
2668                             while (dd>= 180.) dd -= 360.;
2669                             while (dd< -180.) dd += 360.;
2670                             if (fabs(dd)<dmin){ dmin = fabs(dd); ds = dd; }
2671                         }
2672                         double de   = eta[t]-d.eta;
2673                         double sp   = sqrt(de*de + pow(ds*M_PI/180.0,2));
2674                         if (sp<best){ best=sp; bde=de; bdp=ds; bk=k; }
2675                     }
2676                     const int kt    = (c<2) ? 2 : 0;
2677                     double Q    = dqOf(pt[t],ridgeY[t],phi[t],Species_mass[kt],v.pt,v.y,v.phi,Species_mass[5+v.kv]);
2678                     hSep[c][sm] ->Fill(bde,bdp);
2679                     hSepQ[c][sm]->Fill(best,Q);
2680                     if (v.d[bk].q*chg[t]>0) hSepLS[c][sm]->Fill(best,Q);
2681                 };
2682                 int iz  = (int)floor(((*vtxz)+16.0)/2.0);
2683                 if (iz>=0 && iz<16){
2684                     for (int t=0;t<(*ntr);t++){
2685                         int c   = -1;
2686                         if (inList(t,2)) c = (chg[t]>0) ? 0 : 1;
2687                         else if (inList(t,0)) c = (chg[t]>0) ? 2 : 3;
2688                         if (c<0) continue;
2689                         const int kvw   = (c==0 || c==2) ? 1 : 2;
2690                         for (const SepV0& v : cur) if (v.kv==kvw) fillSep(t,c,v,0);
2691                         for (const auto& ev : sepBuf[iz]) for (const SepV0& v : ev) if (v.kv==kvw) fillSep(t,c,v,1);
2692                     }
2693                     if (!cur.empty()){
2694                         sepBuf[iz].push_back(cur);
2695                         if ((int)sepBuf[iz].size()>SEP_DEPTH) sepBuf[iz].pop_front();
2696                     }
2697                 }
2698             }
2699             //---- daughters of off-peak V0 candidates (they stay in the track lists)
2700             std::vector<char> offDau((*ntr),0);
2701             for (int iv0=0;iv0<(*nv0);iv0++){
2702                 if (v0kind(iv0)<0 || v0InPeak(iv0)) continue;
2703                 const int itd[2]    = {v0indtr1[iv0], v0indtr2[iv0]};
2704                 for (int kd=0;kd<2;kd++) if (itd[kd]>=0 && itd[kd]<(*ntr)) offDau[itd[kd]] = 1;
2705             }
2706             //---- 1. V0-V0 sharing
2707             for (int iv0=0;iv0<(*nv0);iv0++){
2708                 int k1  = v0kind(iv0);
2709                 if (k1<0) continue;
2710                 bool p1 = v0InPeak(iv0);
2711                 for (int jv0=0;jv0<(*nv0);jv0++){
2712                     if (jv0==iv0) continue;
2713                     int k2  = v0kind(jv0);
2714                     if (k2<0) continue;
2715                     bool p2 = v0InPeak(jv0);
2716                     if (p1 && p2 && jv0>iv0){
2717                         hV0pairs->Fill(std::min(k1,k2),std::max(k1,k2));
2718                         if (shareDau(iv0,jv0)) hV0share->Fill(std::min(k1,k2),std::max(k1,k2));
2719                     }
2720                     if (p1 && !p2 && shareDau(iv0,jv0)) hV0shareOff->Fill(k1,k2);
2721                 }
2722             }
2723             //---- 2. nearest same-charge, same-PID track in the lists to each in-peak daughter
2724             for (int iv0=0;iv0<(*nv0);iv0++){
2725                 int kv  = v0kind(iv0);
2726                 if (kv<0 || !v0InPeak(iv0)) continue;
2727                 for (int q=0;q<2;q++){
2728                     int d   = dauOf(iv0,q);
2729                     if (d<0) continue;
2730                     int sp  = (kv==1 && q==0) || (kv==2 && q==1) ? 2 : 0;   // the (anti)proton, else a pion
2731                     double best = 1e9; int jb = -1;
2732                     for (int it=0;it<(*ntr);it++){
2733                         if (it==d || !inList(it,sp) || chg[it]*chg[d]<=0) continue;
2734                         double r    = dRof(it,d);
2735                         if (r<best){ best = r; jb = it; }
2736                     }
2737                     if (jb<0) continue;
2738                     hDauNear_dR[kv][q]  ->Fill(std::min(best,0.2999));
2739                     hDauNear_dRdpt[kv][q]->Fill(best,fabs(pt[jb]-pt[d])/pt[d]);
2740                     if (best<CQC_DRDUP) hDauNear_skf[kv][q]->Fill(SiSplitScore(&siclukey[jb*7],&siclukey[d*7],layermask[jb],layermask[d]));
2741                 }
2742             }
2743             //---- 3. p-Lambda / pbar-Lbar sibling Q by the (anti)proton's category; 4. Lambda-Lambda
2744             int nLin[2] = {0,0};
2745             for (int iv0=0;iv0<(*nv0);iv0++){
2746                 int kv  = v0kind(iv0);
2747                 if (kv<1 || !v0InList(iv0)) continue;
2748                 int a   = kv-1;
2749                 ++nLin[a];
2750                 int own = dauOf(iv0,a);     // Lambda: the + daughter (p); Lbar: the - daughter (pbar)
2751                 double vpt=v0ptOf(iv0), vy=v0yOf(iv0), vph=atan2(v0py[iv0],v0px[iv0]);
2752                 for (int ip=0;ip<(*ntr);ip++){
2753                     if (!inList(ip,2) || (a==0 && chg[ip]<=0) || (a==1 && chg[ip]>=0)) continue;
2754                     double Q    = dqOf(pt[ip],ridgeY[ip],phi[ip],Species_mass[2],vpt,vy,vph,Species_mass[6+a]);
2755                     double r    = (own>=0) ? dRof(ip,own) : 9.;
2756                     int c       = (r<CQC_DRDUP) ? 0 : (offDau[ip] ? 1 : 2);
2757                     hpLa_Q[a][c]    ->Fill(Q);
2758                     hpLa_QdR[a]     ->Fill(Q,std::min(r,0.4999));
2759                 }
2760                 for (int jv0=iv0+1;jv0<(*nv0);jv0++){
2761                     if (v0kind(jv0)!=kv || !v0InList(jv0)) continue;
2762                     double Q    = dqOf(vpt,vy,vph,Species_mass[6+a],v0ptOf(jv0),v0yOf(jv0),atan2(v0py[jv0],v0px[jv0]),Species_mass[6+a]);
2763                     bool nearD  = false;
2764                     for (int q=0;q<2;q++){
2765                         int d1=dauOf(iv0,q), d2=dauOf(jv0,q);
2766                         if (d1>=0 && d2>=0 && d1!=d2 && dRof(d1,d2)<CQC_DRDUP) nearD = true;
2767                     }
2768                     int c       = shareDau(iv0,jv0) ? 0 : (nearD ? 1 : 2);
2769                     hLaLa_Q[a][c]->Fill(std::min(Q,3.999));
2770                 }
2771             }
2772             for (int a=0;a<2;a++) hnLApeak[a]->Fill(nLin[a]);
2773             //---- <pT> vs N_ch
2774             for (int it=0;it<(*ntr);it++){
2775                 int k   = ridgePID[it];
2776                 if (k<0 || k>2 || !inList(it,k)) continue;
2777                 hptNch[k][chg[it]>0?0:1]->Fill(ntrkept,pt[it]);
2778             }
2779             for (int iv0=0;iv0<(*nv0);iv0++) if (v0InList(iv0)) hptNchV0[v0kind(iv0)]->Fill(ntrkept,v0ptOf(iv0));
2780         }
2781         //
2782         //---- fill some event variable histograms...
2783         hntrk           ->Fill(ntrkept);
2784         for (int c=0;c<NXTRIGCL;c++) if (xtrigIn[c]){
2785             pntrk_xing[c]->Fill((*crossing),ntrkept);
2786             hnev_xing[c] ->Fill((*crossing));       // raw counts (hadd-safe); divided by bin width when drawn
2787         }
2788         double negfr    = -1;
2789         if (npos+nneg>0.){ negfr = nneg/(npos+nneg); }
2790         hrirun_ntrk     ->Fill((*run),(*ntr) );
2791         hrirun_vtxx     ->Fill((*run),(*vtxx));
2792         hrirun_vtxy     ->Fill((*run),(*vtxy));
2793         hrirun_vtxz     ->Fill((*run),(*vtxz));
2794         hrirun_negfr    ->Fill((*run),negfr);
2795         hrirun_nev      ->Fill((*run),1.0);
2796         hrirun_nevmb    ->Fill((*run),(int)goodMB );
2797         hrirun_nevjet   ->Fill((*run),(int)goodJet);
2798         hrirun_nevpho   ->Fill((*run),(int)goodPho);
2799         //
2800             //hri_ntrk[0]   ->Fill(kRunIndex,(*ntr) );
2801             //hri_vtxx[0]   ->Fill(kRunIndex,(*vtxx));
2802             //hri_vtxy[0]   ->Fill(kRunIndex,(*vtxy));
2803             //hri_vtxz[0]   ->Fill(kRunIndex,(*vtxz));
2804             //hri_negfr[0]->Fill(kRunIndex,negfr);
2805             //hri_nev[0]    ->Fill(kRunIndex,1.0);
2806             //hri_nevmb[0]->Fill(kRunIndex,(int)goodMB );
2807             //hri_nevjet[0]->Fill(kRunIndex,(int)goodJet);
2808             //hri_nevpho[0]->Fill(kRunIndex,(int)goodPho);
2809         if (goodClock){
2810             //hri_ntrk[1]   ->Fill(kRunIndex,(*ntr) );
2811             //hri_vtxx[1]   ->Fill(kRunIndex,(*vtxx));
2812             //hri_vtxy[1]   ->Fill(kRunIndex,(*vtxy));
2813             //hri_vtxz[1]   ->Fill(kRunIndex,(*vtxz));
2814             //hri_negfr[1]->Fill(kRunIndex,negfr);
2815             //hri_nev[1]    ->Fill(kRunIndex,1.0);
2816             //hri_nevmb[1]->Fill(kRunIndex,(int)goodMB );
2817             //hri_nevjet[1]->Fill(kRunIndex,(int)goodJet);
2818             //hri_nevpho[1]->Fill(kRunIndex,(int)goodPho);
2819         }
2820         if (goodMB){
2821             //hri_ntrk[2]   ->Fill(kRunIndex,(*ntr) );
2822             //hri_vtxx[2]   ->Fill(kRunIndex,(*vtxx));
2823             //hri_vtxy[2]   ->Fill(kRunIndex,(*vtxy));
2824             //hri_vtxz[2]   ->Fill(kRunIndex,(*vtxz));
2825             //hri_negfr[2]->Fill(kRunIndex,negfr);
2826             //hri_nev[2]    ->Fill(kRunIndex,1.0);
2827             //hri_nevmb[2]->Fill(kRunIndex,(int)goodMB );
2828             //hri_nevjet[2]->Fill(kRunIndex,(int)goodJet);
2829             //hri_nevpho[2]->Fill(kRunIndex,(int)goodPho);
2830         }
2831         if (goodJet){
2832             //hri_ntrk[3]   ->Fill(kRunIndex,(*ntr) );
2833             //hri_vtxx[3]   ->Fill(kRunIndex,(*vtxx));
2834             //hri_vtxy[3]   ->Fill(kRunIndex,(*vtxy));
2835             //hri_vtxz[3]   ->Fill(kRunIndex,(*vtxz));
2836             //hri_negfr[3]->Fill(kRunIndex,negfr);
2837             //hri_nev[3]    ->Fill(kRunIndex,1.0);
2838             //hri_nevmb[3]->Fill(kRunIndex,(int)goodMB );
2839             //hri_nevjet[3]->Fill(kRunIndex,(int)goodJet);
2840             //hri_nevpho[3]->Fill(kRunIndex,(int)goodPho);
2841         }
2842         if (goodPho){
2843             //hri_ntrk[4]   ->Fill(kRunIndex,(*ntr) );
2844             //hri_vtxx[4]   ->Fill(kRunIndex,(*vtxx));
2845             //hri_vtxy[4]   ->Fill(kRunIndex,(*vtxy));
2846             //hri_vtxz[4]   ->Fill(kRunIndex,(*vtxz));
2847             //hri_negfr[4]->Fill(kRunIndex,negfr);
2848             //hri_nev[4]    ->Fill(kRunIndex,1.0);
2849             //hri_nevmb[4]->Fill(kRunIndex,(int)goodMB );
2850             //hri_nevjet[4]->Fill(kRunIndex,(int)goodJet);
2851             //hri_nevpho[4]->Fill(kRunIndex,(int)goodPho);
2852         }
2853         //
2854         //cout<<"----------------------------------"<<endl;
2855         //cout<<npart1<<" "<<npart2<<endl;
2856         //for (int ip=0;ip<npart1;ip++){
2857         //  cout<<"POS \t"<<ip<<"\t "<<part1[ip][0]<<" "<<part1[ip][1]<<" "<<part1[ip][2]<<endl;
2858         //}
2859         //for (int in=0;in<npart2;in++){
2860         //  cout<<"NEG \t"<<in<<"\t "<<part2[in][0]<<" "<<part2[in][1]<<" "<<part2[in][2]<<endl;
2861         //}
2862         //
2863         //---- README_CQcomparison.md sec 2.1: only events in the nchLLHH multiplicity class reach CalcRm
2864         ++nNchSeen;
2865         const bool inNchClass   = (ntrkept>=valNchLo && ntrkept<=valNchHi);
2866         if (inNchClass) ++nNchKept;
2867         if (!NOCORRELATIONS && inNchClass) hnvtxCR->Fill((*vtxz),(*nvtx));
2868         if (!NOCORRELATIONS && inNchClass){
2869             for (int ipaty=0;ipaty<NPairTypes;ipaty++){ 
2870                 int ipid1           = PairTypes_Info[ipaty][0];
2871                 int ipid2           = PairTypes_Info[ipaty][1];
2872                 //int iSpecies1     = ipid1%NSpecies;
2873                 //int iSpecies2     = ipid2%NSpecies;
2874                 //
2875                 int calcr_n_1_call  = calcr_n_1[ipaty];
2876                 bool abort_zeroeff  = false;            // set to true if any particle1 OR particle2 has zero efficiency from eff map
2877                 for (int i=0;i<calcr_n_1_call;i++){ 
2878                     eff_1[i]=1.0;
2879                     double yloc         = Pvec_1[ipaty][i][0];              // rapidity
2880                     double ptloc        = Pvec_1[ipaty][i][2];              // pt
2881                     if (!WeightDensities){
2882                         eff_1[i]    = 1.;
2883                     } else {
2884                         //int kbin  = heffpty_cent[ipid1][kCentrality16]->FindBin(yloc,min(ptloc,MaxPtWithEff-0.05));
2885                         //eff_1[i]  = heffpty_cent[ipid1][kCentrality16]->GetBinContent(kbin);
2886                         eff_1[i]    = 1.;
2887                     }
2888                     if (eff_1[i]<=0.){
2889                         abort_zeroeff   = true;
2890                     }
2891                     //
2892                     for(int k=0;k<3;k++){ Pvec_1_call[i][k] = Pvec_1[ipaty][i][k]; }
2893                 }
2894                 int calcr_n_2_call  = calcr_n_2[ipaty];
2895                 for (int i=0;i<calcr_n_2_call;i++){ 
2896                     eff_2[i]=1.0;
2897                     double yloc         = Pvec_2[ipaty][i][0];              // rapidity
2898                     double ptloc        = Pvec_2[ipaty][i][2];              // pt
2899                     if (!WeightDensities){
2900                         eff_2[i]    = 1.;
2901                     } else {
2902                         //int kbin  = heffpty_cent[ipid2][kCentrality16]->FindBin(yloc,min(ptloc,MaxPtWithEff-0.05));
2903                         //eff_2[i]  = heffpty_cent[ipid2][kCentrality16]->GetBinContent(kbin);
2904                         eff_2[i]    = 1.;
2905                     }
2906                     if (eff_2[i]<=0.){
2907                         abort_zeroeff   = true;
2908                     }
2909                     //
2910                     for(int k=0;k<3;k++){ Pvec_2_call[i][k] = Pvec_2[ipaty][i][k]; }
2911                 }
2912                 //
2913     //          ++nseenzeroeff[ipaty];
2914     //          if (abort_zeroeff){         // skip this event if a particle has zero efficiency in map
2915     //              ++nabortedzeroeff[ipaty];
2916     //              continue;
2917     //          }
2918                 //
2919                     R[ipaty]    ->SetCurrentRunEvt((*run),(*evt));  // sec 18.20/18.23: (run,evt) uniquely IDs the
2920                     R[ipaty]    ->SetCurrentNch(ntrkept);           // README_SplitTracks573 sec 34
2921                                                                     // trigger frame -- used by the mixNoAdjTF
2922                                                                     // neighboring-TF event-pair exclusion
2923                 R[ipaty]    ->Increment((*vtxz),field,
2924                                         calcr_n_1_call,eff_1,Pvec_1_call,
2925                                         calcr_n_2_call,eff_2,Pvec_2_call);
2926                 //      
2927             }   // end ipaty loop...
2928         }   // end nocorrelations
2929         //
2930     }   // end event loop...
2931 
2932     //---- final calculations...
2933     //
2934     TH1::AddDirectory(kTRUE);       // needed for clones in following calculations
2935     //
2936     cout<<"Run numbers seen = "<<run_lowest<<" - "<<run_highest<<endl;
2937     cout<<"V0s in the pair types (README_PID.md sec 12, in their mass peak): K0s "<<nv0Paired[0]<<" of "<<nv0All[0]
2938         <<", Lambda "<<nv0Paired[1]<<" of "<<nv0All[1]<<", Lbar "<<nv0Paired[2]<<" of "<<nv0All[2]
2939         <<"; in-peak daughters that indv0 did not flag: "<<nPeakDauNotIndv0<<endl;
2940     cout<<"multiplicity class (README_CQcomparison.md sec 2.1): "<<valNchLo<<" <= N_ch <= "<<valNchHi<<": "<<nNchKept<<" of "<<nNchSeen<<" events reached CalcRm"<<endl;
2941     cout<<"V0 zero-sentinel fixes (README_v0etaSpike.md): eta "<<nv0fixEta<<"  phi "<<nv0fixPhi<<"  mass "<<nv0fixMass<<"  ctau "<<nv0fixCtau<<endl;
2942     htrigRun        ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2943     hrirun_ntrk     ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2944     hrirun_vtxx     ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2945     hrirun_vtxy     ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2946     hrirun_vtxz     ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2947     hrirun_negfr    ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2948     hrirun_nev      ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2949     hrirun_nevmb    ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2950     hrirun_nevjet   ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2951     hrirun_nevpho   ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2952     hrirun_eta      ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2953     hrirun_phi      ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2954     hrirun_pt       ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2955     hrirun_ntpc     ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2956     hrirun_dedx     ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2957     hrirun_ndedx    ->GetXaxis()->SetRangeUser(run_lowest-10.5,run_highest+10.5);
2958     //
2959     hrirun_nevmb    ->Divide(hrirun_nev);
2960     hrirun_nevjet   ->Divide(hrirun_nev);
2961     hrirun_nevpho   ->Divide(hrirun_nev);
2962     for (int itt=0;itt<NTRIGTYPES;itt++){   
2963         //hri_nevmb[itt]    ->Divide(hri_nev[itt]);
2964         //hri_nevjet[itt]   ->Divide(hri_nev[itt]);
2965         //hri_nevpho[itt]   ->Divide(hri_nev[itt]);
2966     }
2967     //
2968     hntpc_phieta    ->Divide(hphieta);
2969     habsdcaxy_phieta->Divide(hphieta);
2970     habsdcaz_phieta ->Divide(hphieta);
2971     hdedx_phieta    ->Divide(hphieta);
2972 
2973     //---- v0 vs (*crossing)
2974     TH1D *hKSprob_xing  = (TH1D*)hKSxing->Clone(Form("hKSprob_xing"));
2975     TH1D *hLAprob_xing  = (TH1D*)hLAxing->Clone(Form("hLAprob_xing"));
2976     TH1D *hALprob_xing  = (TH1D*)hALxing->Clone(Form("hALprob_xing"));
2977     hKSprob_xing->Divide(hxing);
2978     hLAprob_xing->Divide(hxing);
2979     hALprob_xing->Divide(hxing);
2980 
2981     //---- find pt bins for dedx vs eta,phi 
2982     for (int ipar=0;ipar<3;ipar++){
2983         for (int ichg=0;ichg<2;ichg++){
2984             cout<<"------ ipar= "<<ipar<<" ichg="<<ichg<<" ----------- "<<endl;
2985             TH1D *htemp = (TH1D*)hpt_tagged[ichg][ipar]->Clone("htemp");
2986             htemp       ->Scale(1./htemp->Integral());
2987             int ptbin   = 0;
2988             double sum  = 0;
2989             for (int ibin=1;ibin<=htemp->GetNbinsX();ibin++){
2990                 double apt  = htemp->GetBinCenter (ibin);
2991                 double anc  = htemp->GetBinContent(ibin);
2992                 sum += anc;
2993                 if (sum>0.2*(ptbin+1)){
2994                     cout<<ibin<<"\t "<<ptbin<<" "<<apt<<"\t "<<anc<<" "<<sum<<"\t BOUNDRY"<<endl;
2995                     ptbin+=1;
2996                 }
2997             }
2998             delete htemp;
2999         }
3000     }
3001 
3002 
3003     //---- class hists...
3004     TH1D *hzvtx[NPairTypes]                 = {0};
3005     TH2D *hmult[NPairTypes]                 = {0};
3006     TH1D *hmult1[NPairTypes]                = {0};
3007     TH1D *hmult2[NPairTypes]                = {0};
3008     TH1D *hy1[NPairTypes]                   = {0};
3009     TH1D *hy2[NPairTypes]                   = {0};
3010     TH2D *hrho2[NR2TYPES][NPairTypes]       = {0};      // uncorrected 
3011     TH2D *hrho1rho1[NR2TYPES][NPairTypes]   = {0};      // uncorrected 
3012     TH2D *hC2[NR2TYPES][NPairTypes]         = {0};      // uncorrected C2
3013     TH2D *hR2[NR2TYPES][NPairTypes]         = {0};      // uncorrected R2
3014     TH2D *hrho2C[NPairTypes]                = {0};      // README_Crossing: crossing-corrected (dy,dphi) rho2(S)
3015     TH2D *hC2C[NPairTypes]                  = {0};      // crossing-corrected (dy,dphi) C2
3016     TH2D *hR2C[NPairTypes]                  = {0};      // crossing-corrected (dy,dphi) R2
3017     TH2D *hMempty[NPairTypes]               = {0};      // # of used Zvtx slices with rho2(M)=0, per (dy,dphi) bin
3018     TH1D *hR2yydy[NPairTypes]               = {0};      // 
3019     TH1D *hR2dy[NPairTypes]                 = {0};      // 
3020     TH1D *hR2dphi[NPairTypes]               = {0};      // 
3021     TH1D *hMinv_S[NPairTypes]               = {0};
3022     TH1D *hMinv_M[NPairTypes]               = {0};
3023     TH1D *hMinv[NPairTypes]                 = {0};
3024     TH1D *hCQ[NPairTypes]                   = {0};      // femtoscopic C(Q)=Qsib/Qmix, Zvtx-averaged (CalcRm only)
3025     TH1D *hQsib[NPairTypes]                 = {0};      // raw sibling numerator behind hCQ -- not written before 2026-09-21 (sec 13.8.5)
3026     TH2D *hzoomS[NPairTypes]                = {0};      // README sec 16.8 spike-extent zoom (sibling)
3027     TH2D *hzoomM[NPairTypes]                = {0};      // README sec 16.8 spike-extent zoom (mixed)
3028     TH1D *hMinvFS[NPairTypes]               = {0};      // fine Minv near threshold, sibling (C(Q) page)
3029     TH1D *hMinvFM[NPairTypes]               = {0};      // same, mixed
3030     TH1D *hQmix[NPairTypes]                 = {0};      // raw mixed denominator behind hCQ -- not written before 2026-09-21 (sec 13.8.5)
3031     TH1D *hQsibW[NPairTypes]                = {0};      // long-Q test copies, 0-8 GeV, Zvtx-summed (README_CQcomparison sec 2.3)
3032     TH1D *hQmixW[NPairTypes]                = {0};
3033     TH2D *hCQKT[NPairTypes]                 = {0};      // README_CQ: C(Q) vs (Qinv,kT), STAR's 4 kT bins, Zvtx-averaged
3034     TH2D *hQsibKT[NPairTypes]               = {0};      // its sibling numerator, Zvtx-summed
3035     TH2D *hQmixKT[NPairTypes]               = {0};      // its mixed denominator, Zvtx-summed
3036     if (!NOCORRELATIONS){
3037         TH1::AddDirectory(kFALSE);
3038         for (int ipaty=0;ipaty<NPairTypes;ipaty++){
3039             int ipid1           = PairTypes_Info[ipaty][0];
3040             int ipid2           = PairTypes_Info[ipaty][1];
3041             //int kCentralityStyle  = PairTypes_Info[ipaty][2];
3042             int iSpecies1       = GetSpecies(ipid1);
3043             int iSpecies2       = GetSpecies(ipid2);
3044             int iChg1           = ParticleCharge[ipid1];
3045             int iChg2           = ParticleCharge[ipid2];
3046             TString part1name   = TString(ParticleIDNames[ipid1]);
3047             TString part2name   = TString(ParticleIDNames[ipid2]);
3048             //
3049             R[ipaty]->Calculate();
3050             //
3051             hzvtx[ipaty]    = (TH1D*)R[ipaty]->Gethzvtx();
3052             hzvtx[ipaty]    ->SetName(Form("hzvtx_%d",ipaty));
3053             hmult[ipaty]    = (TH2D*)R[ipaty]->Gethmult();
3054             hmult[ipaty]    ->SetTitle(Form("%s%s, N/evt;n_{1};n_{2}",part1name.Data(),part2name.Data()));
3055             hmult[ipaty]    ->SetName(Form("hmult_%d",ipaty));
3056             hmult1[ipaty]   = (TH1D*)hmult[ipaty]->ProjectionX();
3057             hmult1[ipaty]   ->SetTitle(Form("%s, N/evt;n_{1}",part1name.Data()));
3058             hmult1[ipaty]   ->SetName(Form("hmult1_%d",ipaty));
3059             hmult2[ipaty]   = (TH1D*)hmult[ipaty]->ProjectionY();
3060             hmult2[ipaty]   ->SetTitle(Form("%s, N/evt;n_{2}",part2name.Data()));
3061             hmult2[ipaty]   ->SetName(Form("hmult2_%d",ipaty));
3062             //
3063             hy1[ipaty]      = (TH1D*)R[ipaty]->Gethy1_();
3064             hy1[ipaty]      ->SetTitle(Form("%s%s, dN1/dy/evt;dy",part1name.Data(),part2name.Data()));
3065             hy1[ipaty]      ->SetName(Form("hy1_%d",ipaty));
3066             hy2[ipaty]      = (TH1D*)R[ipaty]->Gethy2_();
3067             hy2[ipaty]      ->SetTitle(Form("%s%s, dN2/dy/evt;dy",part1name.Data(),part2name.Data()));
3068             hy2[ipaty]      ->SetName(Form("hy2_%d",ipaty));
3069             hMinv_S[ipaty]  = (TH1D*)R[ipaty]->GethMinv_S();
3070             hMinv_S[ipaty]  ->SetTitle(Form("%s%s, M_{inv}(S)",part1name.Data(),part2name.Data()));
3071             hMinv_S[ipaty]  ->SetName(Form("hMinv_S_%d",ipaty));
3072             hMinv_M[ipaty]  = (TH1D*)R[ipaty]->GethMinv_M();
3073             hMinv_M[ipaty]  ->SetTitle(Form("%s%s, M_{inv}(M)",part1name.Data(),part2name.Data()));
3074             hMinv_M[ipaty]  ->SetName(Form("hMinv_M_%d",ipaty));
3075             hMinv[ipaty]    = (TH1D*)R[ipaty]->GethMinv();
3076             hMinv[ipaty]    ->SetTitle(Form("%s%s, M_{inv}",part1name.Data(),part2name.Data()));
3077             hMinv[ipaty]    ->SetName(Form("hMinv_%d",ipaty));
3078             hCQ[ipaty]      = (TH1D*)R[ipaty]->GethCQ();
3079             hCQ[ipaty]      ->SetTitle(Form("%s%s, C(Q)=Q_{sib}/Q_{mix};Q_{inv} (GeV)",part1name.Data(),part2name.Data()));
3080             hCQ[ipaty]      ->SetName(Form("hCQ_%d",ipaty));
3081             hQsib[ipaty]    = (TH1D*)R[ipaty]->GethQsib();
3082             hQsib[ipaty]    ->SetTitle(Form("%s%s, Q_{sib} (raw sibling pairs);Q_{inv} (GeV)",part1name.Data(),part2name.Data()));
3083             hQsib[ipaty]    ->SetName(Form("hQsib_%d",ipaty));
3084             hQmix[ipaty]    = (TH1D*)R[ipaty]->GethQmix();
3085             hQmix[ipaty]    ->SetTitle(Form("%s%s, Q_{mix} (raw mixed pairs);Q_{inv} (GeV)",part1name.Data(),part2name.Data()));
3086             hQmix[ipaty]    ->SetName(Form("hQmix_%d",ipaty));
3087             hQsibW[ipaty]   = (TH1D*)R[ipaty]->GethQsibW();
3088             hQsibW[ipaty]   ->SetTitle(Form("%s%s, Q_{sib} 0-8 GeV (long-Q test copy);Q_{inv} (GeV)",part1name.Data(),part2name.Data()));
3089             hQsibW[ipaty]   ->SetName(Form("hQsibW_%d",ipaty));
3090             hQmixW[ipaty]   = (TH1D*)R[ipaty]->GethQmixW();
3091             hQmixW[ipaty]   ->SetTitle(Form("%s%s, Q_{mix} 0-8 GeV (long-Q test copy, #Sigma_{z});Q_{inv} (GeV)",part1name.Data(),part2name.Data()));
3092             hQmixW[ipaty]   ->SetName(Form("hQmixW_%d",ipaty));
3093             hCQKT[ipaty]    = (TH2D*)R[ipaty]->GethCQKT();
3094             hCQKT[ipaty]    ->SetTitle(Form("%s%s, C(Q) vs k_{T};Q_{inv} (GeV);k_{T} (GeV)",part1name.Data(),part2name.Data()));
3095             hCQKT[ipaty]    ->SetName(Form("hCQKT_%d",ipaty));
3096             hQsibKT[ipaty]  = (TH2D*)R[ipaty]->GethQsibKT();
3097             hQsibKT[ipaty]  ->SetTitle(Form("%s%s, Q_{sib} vs k_{T} (raw sibling pairs);Q_{inv} (GeV);k_{T} (GeV)",part1name.Data(),part2name.Data()));
3098             hQsibKT[ipaty]  ->SetName(Form("hQsibKT_%d",ipaty));
3099             hQmixKT[ipaty]  = (TH2D*)R[ipaty]->GethQmixKT();
3100             hQmixKT[ipaty]  ->SetTitle(Form("%s%s, Q_{mix} vs k_{T} (raw mixed pairs);Q_{inv} (GeV);k_{T} (GeV)",part1name.Data(),part2name.Data()));
3101             hQmixKT[ipaty]  ->SetName(Form("hQmixKT_%d",ipaty));
3102             hzoomS[ipaty]   = (TH2D*)R[ipaty]->GethZoomS();
3103             hzoomS[ipaty]   ->SetName(Form("hzoomS_%d",ipaty));
3104             hzoomS[ipaty]   ->SetTitle(Form("%s%s, sibling pairs, zoom;#Delta#eta;#Delta#phi (deg)",part1name.Data(),part2name.Data()));
3105             hrho2C[ipaty]   = (TH2D*)R[ipaty]->Gethrho2C_S();
3106             hrho2C[ipaty]   ->SetName(Form("hrho2C_1_%d",ipaty));
3107             hrho2C[ipaty]   ->SetTitle(Form("%s%s, #rho_{2}(S) crossing-corrected vs. (dy,d#phi);dy;d#phi",part1name.Data(),part2name.Data()));
3108             hC2C[ipaty]     = (TH2D*)R[ipaty]->GethC2C();
3109             hC2C[ipaty]     ->SetName(Form("hC2C_1_%d",ipaty));
3110             hC2C[ipaty]     ->SetTitle(Form("%s%s, C_{2} crossing-corrected vs. (dy,d#phi);dy;d#phi",part1name.Data(),part2name.Data()));
3111             hR2C[ipaty]     = (TH2D*)R[ipaty]->GethR2C();
3112             hR2C[ipaty]     ->SetName(Form("hR2C_1_%d",ipaty));
3113             hR2C[ipaty]     ->SetTitle(Form("%s%s, R_{2} crossing-corrected vs. (dy,d#phi);dy;d#phi",part1name.Data(),part2name.Data()));
3114             hMempty[ipaty]  = (TH2D*)R[ipaty]->GethMempty();
3115             hMempty[ipaty]  ->SetName(Form("hMempty_1_%d",ipaty));
3116             hMempty[ipaty]  ->SetTitle(Form("%s%s, # Zvtx slices with empty #rho_{2}(M) vs. (dy,d#phi);dy;d#phi",part1name.Data(),part2name.Data()));
3117             for (int sm=0;sm<2;sm++) for (int is=0;is<2;is++) for (int ir=0;ir<4;ir++){ // README_SplitTracks573 sec 11
3118                 TH2D* ht    = R[ipaty]->GethTTR(sm,is,ir);
3119                 ht->SetName(Form("hTTR_%s_s%d_r%d_%d",sm?"M":"S",is,ir,ipaty));
3120                 ht->SetTitle(Form("%s%s, %s pairs, sign %s, %s;dy;#Delta#phi* (deg)",part1name.Data(),part2name.Data(),sm?"mixed":"sibling",is?"-":"+",
3121                     ir==0?"R=0.30 m":ir==1?"R=0.50 m":ir==2?"R=0.70 m":"min over R=0.30-0.78 m"));
3122             }
3123             hzoomM[ipaty]   = (TH2D*)R[ipaty]->GethZoomM();
3124             hzoomM[ipaty]   ->SetName(Form("hzoomM_%d",ipaty));
3125             hzoomM[ipaty]   ->SetTitle(Form("%s%s, mixed pairs, zoom;#Delta#eta;#Delta#phi (deg)",part1name.Data(),part2name.Data()));
3126             hMinvFS[ipaty]  = (TH1D*)R[ipaty]->GethMinvFineS();
3127             hMinvFS[ipaty]  ->SetName(Form("hMinvFS_%d",ipaty));
3128             hMinvFS[ipaty]  ->SetTitle(Form("%s%s, M_{inv} near threshold;M_{inv} (GeV);pairs",part1name.Data(),part2name.Data()));
3129             hMinvFM[ipaty]  = (TH1D*)R[ipaty]->GethMinvFineM();
3130             hMinvFM[ipaty]  ->SetName(Form("hMinvFM_%d",ipaty));
3131             hMinvFM[ipaty]  ->SetTitle(Form("%s%s, M_{inv} near threshold, mixed;M_{inv} (GeV);pairs",part1name.Data(),part2name.Data()));
3132             for (int ir2=0;ir2<NR2TYPES;ir2++){
3133                 TString r2string1       = TString("y_{1},y_{2}");   // ir==0 is (y1,y2)
3134                 TString r2string2       = TString(";y_{1};y_{2}");  // ir==0 is (y1,y2)
3135                 if (ir2==1) r2string1   = TString("dy,d#phi");      // ir==1 is (dy,dphi)
3136                 if (ir2==1) r2string2   = TString(";dy;d#phi");     // ir==1 is (dy,dphi)
3137                 if (ir2==2) r2string1   = TString("Q_{inv},p_{T,pair}");                    // ir==2 is (Q_inv,pT_pair), variable width in Q
3138                 if (ir2==2) r2string2   = TString(";Q_{inv} (GeV);p_{T,pair} (GeV/c)");
3139                 //---- get Zvtx-averaged densities...
3140                     hrho2[ir2][ipaty]       = (TH2D*)R[ipaty]->Gethrho2_S(ir2);
3141                     hrho2[ir2][ipaty]       ->SetName(Form("hrho2_%d_%d",ir2,ipaty));
3142                     hrho2[ir2][ipaty]       ->SetTitle(Form("%s%s, #rho_{2}(S) vs. (%s)%s",part1name.Data(),part2name.Data(),r2string1.Data(),r2string2.Data()));
3143                     hrho1rho1[ir2][ipaty]   = (TH2D*)R[ipaty]->Gethrho2_M(ir2);
3144                     hrho1rho1[ir2][ipaty]   ->SetTitle(Form("%s%s, #rho_{2}(M) vs. (%s)%s",part1name.Data(),part2name.Data(),r2string1.Data(),r2string2.Data()));
3145                     hrho1rho1[ir2][ipaty]   ->SetName(Form("hrho1rho1_%d_%d",ir2,ipaty));
3146                 hC2[ir2][ipaty] = (TH2D*)R[ipaty]->GethC2(ir2);
3147                 hC2[ir2][ipaty] ->SetName(Form("hC2_%d_%d",ir2,ipaty));
3148                 hC2[ir2][ipaty]     ->SetTitle(Form("%s%s, C_{2} vs. (%s)%s",part1name.Data(),part2name.Data(),r2string1.Data(),r2string2.Data()));
3149                 hR2[ir2][ipaty] = (TH2D*)R[ipaty]->GethR2(ir2);
3150                 hR2[ir2][ipaty] ->SetName(Form("hR2_%d_%d",ir2,ipaty));
3151                 hR2[ir2][ipaty]     ->SetTitle(Form("%s%s, R_{2} vs. (%s)%s",part1name.Data(),part2name.Data(),r2string1.Data(),r2string2.Data()));
3152             }   // end ir2 loop
3153             //
3154             hR2yydy[ipaty]  = (TH1D*)R[ipaty]->GethR2yydy();
3155             hR2yydy[ipaty]  ->SetTitle(Form("%s%s, R_{2yy}(dy)",part1name.Data(),part2name.Data()));
3156             hR2yydy[ipaty]  ->SetName(Form("hR2yydy_%d",ipaty));
3157             hR2dy[ipaty]    = (TH1D*)R[ipaty]->GethR2dy();
3158             hR2dy[ipaty]    ->SetTitle(Form("%s%s, R_{2}(dy)",part1name.Data(),part2name.Data()));
3159             hR2dy[ipaty]    ->SetName(Form("hR2dy_%d",ipaty));
3160             hR2dphi[ipaty]  = (TH1D*)R[ipaty]->GethR2dphi();
3161             hR2dphi[ipaty]  ->SetTitle(Form("%s%s, R_{2}(dphi)",part1name.Data(),part2name.Data()));
3162             hR2dphi[ipaty]  ->SetName(Form("hR2dphi_%d",ipaty));
3163             //
3164         }   // end ipaty...
3165     }   // end nocorrelations...
3166 
3167 
3168     //---- paint
3169     //
3170     int ican    = -1;
3171     TCanvas *ccan[1000];
3172     int itext   = -1;
3173     TLatex *text[1000];
3174     for (int i=0;i<1000;i++){
3175         text[i] = new TLatex();
3176         text[i] ->SetNDC();
3177         text[i] ->SetTextAlign(31);
3178         text[i] ->SetTextSize(0.09);
3179     }
3180     //
3181     gROOT->SetStyle("Modern");
3182     gStyle->SetOptStat(0);
3183     gStyle->SetPadRightMargin(0.12);
3184     gStyle->SetPadTopMargin(0.01);
3185     gStyle->SetPadBottomMargin(0.08);
3186     gStyle->SetPadLeftMargin(0.14);
3187     //
3188     //---- split-track SL monitor summary (see README_SplitTracks.md sec 9/10) -- always first page,
3189     //---- even if doSplitRemoval=false (then the flagged/"killed" hists are just empty -- hSL_cand
3190     //---- and hntpc_ij_cand are still filled regardless, for choosing the next threshold to scan).
3191     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3192     ccan[ican]->cd(); ccan[ican]->Divide(4,3,0.0001,0.0001);
3193     ccan[ican]->cd(1); hphieta_killed->Draw("colz");
3194     {
3195         TH1D* pairs[7][2]   = { {heta,heta_killed}, {hphi,hphi_killed}, {hpt,hpt_killed}, {hntpc,hntpc_killed},
3196                                 {hquality,hquality_killed}, {hdcaxy,hdcaxy_killed}, {hdcaz,hdcaz_killed} };
3197         for (int ip=0;ip<7;ip++){
3198             ccan[ican]->cd(2+ip);
3199             TH1D* hk    = (TH1D*)pairs[ip][0]->Clone(Form("hcmp_kept_%d",ip));
3200             TH1D* hr    = (TH1D*)pairs[ip][1]->Clone(Form("hcmp_killed_%d",ip));
3201             hk->SetLineColor(1); hr->SetLineColor(2);
3202             if (hk->Integral()>0) hk->Scale(1./hk->Integral());
3203             if (hr->Integral()>0) hr->Scale(1./hr->Integral());
3204             hk->SetTitle(Form("%s (black=kept, red=flagged)",pairs[ip][0]->GetTitle()));
3205             hk->Draw("hist"); hr->Draw("hist same");
3206         }
3207     }
3208     //---- new-mechanism-specific plots (sec 10) -- SL distribution (with the current cut value
3209     //---- marked, if enabled) and the ntpc[i] vs ntpc[j] symmetry check from sec 9.6.
3210     ccan[ican]->cd(9);
3211     hSL_cand->Draw("hist");
3212     TLine *lineSLcut    = 0;
3213     if (doSplitRemoval){
3214         lineSLcut   = new TLine(valSLCut,0,valSLCut,hSL_cand->GetMaximum());
3215         lineSLcut   ->SetLineColor(2); lineSLcut->SetLineWidth(2); lineSLcut->SetLineStyle(2);
3216         lineSLcut   ->Draw();
3217     }
3218     ccan[ican]->cd(10); hntpc_ij_cand->Draw("colz");
3219     ccan[ican]->cd(11);
3220     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3221     text[itext]->DrawLatex(0.88,0.90,Form("doSplitRemoval=%d",(int)doSplitRemoval));
3222     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3223     text[itext]->DrawLatex(0.88,0.81,Form("DISABLE_LScuts=%d  DISABLE_RG=%d",(int)DISABLE_LScuts,(int)DISABLE_RG));
3224     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3225     text[itext]->DrawLatex(0.88,0.72,Form("valSLCut=%.2f  valSiKeyCut=%.2f  valRadialGapCut=%.2f",valSLCut,valSiKeyCut,valRadialGapCut));
3226     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3227     text[itext]->DrawLatex(0.88,0.63,Form("tracks flagged=%ld",nSplitFlagged_total));
3228     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3229     text[itext]->DrawLatex(0.88,0.54,Form("events w/ a flag=%ld",nSplitFlagged_evts));
3230     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3231     text[itext]->DrawLatex(0.88,0.45,Form("pairs: duplicate=%ld complementary=%ld (sec 17.14)",nFlagged_duplicate,nFlagged_complementary));
3232     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3233     text[itext]->DrawLatex(0.88,0.36,Form("ULS (opp-charge) tracks flagged=%ld (sec 18.10, track-level as of now)",nFlagged_ULS));
3234     ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.07);
3235     text[itext]->DrawLatex(0.88,0.27,Form("looper veto %s: tracks flagged=%ld (sec 35)",doLooperVeto?"ON":"OFF",nFlagged_Looper));
3236     ccan[ican]->cd(); ccan[ican]->Update();
3237     ccan[ican]->Print(OutputFileNameO.Data());
3238 
3239     //---- README_SplitTracks573 sec 10: LS pregate before/after, in bins of abs(1/pt1-1/pt2). Rows: all LS
3240     //---- pion pairs, SiSplitScore>=cut pairs (the split candidates), pairs with both tracks kept. Red: the
3241     //---- gate at the bin's two edges (the current gate; with the fixed default both are the same ellipse);
3242     //---- grey dashed: the old fixed ellipse (0.022 x 4.1 deg).
3243     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3244     ccan[ican]->cd();
3245     TPad* gvTop = new TPad("gvTop","",0.,0.95,1.,1.);   // strip for the gate definition
3246     TPad* gvMain    = new TPad("gvMain","",0.,0.,1.,0.95);
3247     gvTop->Draw(); gvMain->Draw();
3248     gvMain->Divide(NGATEVIS,3,0.0001,0.0001);
3249     {
3250         //---- row 1: all LS pairs in plain dphi (index order, so the split lobes are mirrored), old
3251         //---- ellipse for reference. Rows 2-3: SKF>=cut and kept pairs in the dps frame (abs(dphi) - dphi0),
3252         //---- where the dps gate is one ellipse centred at 0. A gate centred at dphi=0 (fixed / dpg) is
3253         //---- drawn in the same frame at the bin's central abs(1/pt1-1/pt2): the curve
3254         //---- abs(dphi) = W sqrt(1-(deta/a)^2), shifted by -dphi0 (it reaches down to abs(dphi)=0, i.e. -dphi0).
3255         TH2D** rows[3]  = { hGateVis_all, hGateVis_hiRes, hGateVis_survRes };
3256         for (int ir=0;ir<3;ir++) for (int ib=0;ib<NGATEVIS;ib++){
3257             gvMain->cd(1+ir*NGATEVIS+ib);
3258             gPad->SetLogz(1); gPad->SetRightMargin(0.13); gPad->SetTopMargin(0.08);
3259             rows[ir][ib]->SetMinimum(0.5);
3260             if (ir==0 && hGateVis_dinvHi[ib]->GetEntries()>0)
3261                 rows[ir][ib]->SetTitle(Form("%s, mean %.2f",rows[ir][ib]->GetTitle(),hGateVis_dinvHi[ib]->GetMean()));
3262             rows[ir][ib]->Draw("colz");
3263             if (ir==0){
3264                 //---- the pregate in plain dphi, at the MEAN abs(1/pt1-1/pt2) of this column's split candidates
3265                 //---- (each pair's own gate differs slightly; rows 2-3 show it exactly, per pair)
3266                 double dm   = hGateVis_dinvHi[ib]->GetEntries()>0 ? hGateVis_dinvHi[ib]->GetMean() : 0.5*(GATEVIS_EDGE[ib]+GATEVIS_EDGE[ib+1]);
3267                 double pA   = 1.0/(1.0+dm);
3268                 {
3269                     //---- user 2026-09-27: centre each ellipse on the MEASURED peak (mean abs(dphi) of this column's
3270                     //---- SKF>=cut pairs) and draw it at the envelope of the column's per-pair gates: half-height
3271                     //---- w + half the spread of dphi0 across the column (dphi0 at the two bin edges).
3272                     double pk   = 0.;
3273                     { TH2D* hh = hGateVis_hi[ib]; double sw=0, sy=0;
3274                       for (int ix=1;ix<=hh->GetNbinsX();ix++) for (int iy=1;iy<=hh->GetNbinsY();iy++){ double v=hh->GetBinContent(ix,iy); sw+=v; sy+=v*fabs(hh->GetYaxis()->GetBinCenter(iy)); }
3275                       pk = sw>0 ? sy/sw : DPsCentre(pA,1.0)*180.0/M_PI; }
3276                     double cLo  = DPsCentre(1.0/(1.0+GATEVIS_EDGE[ib]),1.0)*180.0/M_PI;
3277                     double cHi  = DPsCentre(1.0/(1.0+GATEVIS_EDGE[ib+1]),1.0)*180.0/M_PI;
3278                     double hw   = DPsWidth(cHi*M_PI/180.0)*180.0/M_PI + 0.5*(cHi-cLo);  // sec 33: width grows with dphi0
3279                     for (int sg=-1;sg<=1;sg+=2){
3280                         TEllipse* el    = new TEllipse(0.,sg*pk,valPregateDEta,hw);
3281                         el->SetFillStyle(0); el->SetLineColor(2); el->SetLineWidth(2); el->Draw();
3282                     }
3283                 }
3284                 TEllipse* el0   = new TEllipse(0.,0.,0.022,4.1);
3285                 el0->SetFillStyle(0); el0->SetLineColor(kGray+2); el0->SetLineWidth(2); el0->SetLineStyle(2); el0->Draw();
3286                 continue;
3287             }
3288             TLine* l0   = new TLine(-0.05,0.,0.05,0.); l0->SetLineColor(kGray+2); l0->SetLineStyle(3); l0->Draw();
3289             double dmid = 0.5*(GATEVIS_EDGE[ib]+GATEVIS_EDGE[ib+1]);
3290             double ptA  = 1.0/(1.0+dmid);                       // abs(1/ptA - 1/1) = dmid
3291             double c    = DPsCentre(ptA,1.0)*180.0/M_PI;
3292             {
3293                 TEllipse* el    = new TEllipse(0.,0.,valPregateDEta,DPsWidth(c*M_PI/180.0)*180.0/M_PI);
3294                 el->SetFillStyle(0); el->SetLineColor(2); el->SetLineWidth(2); el->Draw();
3295             }
3296             // the old fixed ellipse in this frame (grey dashed), at the same central abs(1/pt1-1/pt2)
3297             TGraph* g0  = new TGraph();
3298             for (int k=0;k<=100;k++){ double x=-0.022+0.044*k/100.; double u=std::max(0.0,1.0-(x/0.022)*(x/0.022)); g0->SetPoint(g0->GetN(),x,4.1*sqrt(u)-c); }
3299             g0->SetPoint(g0->GetN(),0.022,-c); g0->SetPoint(g0->GetN(),-0.022,-c);
3300             g0->SetLineColor(kGray+2); g0->SetLineWidth(2); g0->SetLineStyle(2); g0->Draw("L");
3301         }
3302         gvTop->cd();
3303         ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.40); text[itext]->SetTextAlign(22);
3304         TString sgate   = Form("#pm max(%.1f, %.0f(%.2f+%.2f#Delta#phi_{0})) around #Delta#phi_{0}(R=%.0f cm)",valDPsW,PREGATE_NSIG,PREGATE_S0,PREGATE_S1,100*valDPsR);
3305         text[itext]->DrawLatex(0.5,0.5,Form("LS #pi pregate: #Delta#phi %s deg, #Delta#eta_{max}=%.3f.  Row 1: all pairs, gate at column mean.  Rows 2-3: SKF#geq%.2f / kept, per pair in |#Delta#phi|-#Delta#phi_{0}.  Grey: old",sgate.Data(),valPregateDEta,valSiKeyCut));
3306     }
3307     ccan[ican]->cd(); ccan[ican]->Update();
3308     ccan[ican]->Print(OutputFileName.Data());
3309 
3310     //---- user 2026-09-28: the pregate in fine slices of dphi0 (booking: hGateFine_*), three pages: SKF>=cut pairs,
3311     //---- SKF>=cut pairs whose tracks both survive, all kept pairs. y = abs(dphi), the variable the gate cuts on,
3312     //---- so the gate of a pair with centre dphi0 is exactly (deta/a)^2 + ((abs(dphi)-dphi0)/w)^2 < 1, cut off at
3313     //---- abs(dphi) = 0 where dphi0 < w. Solid red: the gate at the slice centre; thin dashed: at the slice edges
3314     //---- (every pair's own gate lies between them). Grey dashed: the old fixed ellipse (0.022 x 4.1 deg).
3315     {
3316         auto gateCurve  = [&](double a, double c, double w, int col, int lw, int ls){
3317             TGraph* g   = new TGraph();
3318             for (int k=0;k<=200;k++){ double t=M_PI*k/100.; double x=a*cos(t), y=c+w*sin(t); g->SetPoint(g->GetN(),x,std::max(0.0,y)); }
3319             g->SetLineColor(col); g->SetLineWidth(lw); g->SetLineStyle(ls); g->Draw("L");
3320         };
3321         //---- page 0 = the gate page's row 1 (all LS pairs, signed dphi) in fine slices: the gate is the two
3322         //---- ellipses at +-dphi0 (it cuts abs(dphi)), each pair's own gate between the dashed slice-edge curves
3323         {
3324             auto gateEll    = [&](double a, double c, double w, int col, int lw, int ls){
3325                 TGraph* g   = new TGraph();
3326                 for (int k=0;k<=200;k++){ double t=2*M_PI*k/200.; g->SetPoint(g->GetN(),a*cos(t),c+w*sin(t)); }
3327                 g->SetLineColor(col); g->SetLineWidth(lw); g->SetLineStyle(ls); g->Draw("L");
3328             };
3329             ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3330             ccan[ican]->cd();
3331             TPad* gaTop     = new TPad("gaTop","",0.,0.95,1.,1.);
3332             TPad* gaMain    = new TPad("gaMain","",0.,0.,1.,0.95);
3333             gaTop->Draw(); gaMain->Draw();
3334             gaMain->Divide(4,4,0.0001,0.0001);
3335             for (int k=0;k<NGATEFINE;k++){
3336                 gaMain->cd(1+k);
3337                 gPad->SetLogz(1); gPad->SetRightMargin(0.13); gPad->SetTopMargin(0.09);
3338                 TH2D* h = hGateFine_all[k];
3339                 h->SetMinimum(0.5); h->SetStats(0);
3340                 h->SetTitle(Form("%s, N=%.0f",h->GetTitle(),h->GetEntries()));
3341                 h->Draw("colz");
3342                 double c0   = k*GATEFINE_W, c1 = (k+1)*GATEFINE_W;
3343                 for (int sg=-1;sg<=1;sg+=2){
3344                     gateEll(valPregateDEta,sg*c0,DPsWidth(c0*M_PI/180.0)*180.0/M_PI,2,1,2);
3345                     gateEll(valPregateDEta,sg*c1,DPsWidth(c1*M_PI/180.0)*180.0/M_PI,2,1,2);
3346                     gateEll(valPregateDEta,sg*0.5*(c0+c1),DPsWidth(0.5*(c0+c1)*M_PI/180.0)*180.0/M_PI,2,2,1);
3347                 }
3348                 gateEll(0.022,0.,4.1,kGray+2,2,2);
3349             }
3350             gaTop->cd();
3351             ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.36); text[itext]->SetTextAlign(22);
3352             text[itext]->DrawLatex(0.5,0.5,Form("LS #pi pregate, %.1f#circ slices of #Delta#phi_{0}(R=%.0f cm): all LS pairs, signed #Delta#phi.  Red: (#Delta#eta/%.3f)^{2}+((|#Delta#phi|-#Delta#phi_{0})/w)^{2}<1, w = max(%.1f#circ, 3#sigma(#Delta#phi_{0})) at #pm#Delta#phi_{0}, slice centre / edges.  Grey: old",GATEFINE_W,100*valDPsR,valPregateDEta,valDPsW));
3353             ccan[ican]->cd(); ccan[ican]->Update();
3354             ccan[ican]->Print(OutputFileName.Data());
3355         }
3356         TH2D** pages[3]     = { hGateFine_hi, hGateFine_hiSurv, hGateFine_surv };
3357         const char* pname[3]    = { "SiSplitScore #geq cut (split candidates)", "SiSplitScore #geq cut, both tracks kept (missed)", "all kept pairs" };
3358         for (int ip=0;ip<3;ip++){
3359             ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3360             ccan[ican]->cd();
3361             TPad* gfTop     = new TPad(Form("gfTop%d",ip),"",0.,0.95,1.,1.);
3362             TPad* gfMain    = new TPad(Form("gfMain%d",ip),"",0.,0.,1.,0.95);
3363             gfTop->Draw(); gfMain->Draw();
3364             gfMain->Divide(4,4,0.0001,0.0001);
3365             for (int k=0;k<NGATEFINE;k++){
3366                 gfMain->cd(1+k);
3367                 gPad->SetLogz(1); gPad->SetRightMargin(0.13); gPad->SetTopMargin(0.09);
3368                 TH2D* h = pages[ip][k];
3369                 h->SetMinimum(0.5); h->SetStats(0);
3370                 h->SetTitle(Form("%s, N=%.0f",h->GetTitle(),h->GetEntries()));
3371                 h->Draw("colz");
3372                 double c0   = k*GATEFINE_W, c1 = (k+1)*GATEFINE_W;
3373                 gateCurve(valPregateDEta,c0,DPsWidth(c0*M_PI/180.0)*180.0/M_PI,2,1,2);
3374                 gateCurve(valPregateDEta,c1,DPsWidth(c1*M_PI/180.0)*180.0/M_PI,2,1,2);
3375                 gateCurve(valPregateDEta,0.5*(c0+c1),DPsWidth(0.5*(c0+c1)*M_PI/180.0)*180.0/M_PI,2,2,1);
3376                 gateCurve(0.022,0.,4.1,kGray+2,2,2);
3377             }
3378             gfTop->cd();
3379             ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.36); text[itext]->SetTextAlign(22);
3380             text[itext]->DrawLatex(0.5,0.5,Form("LS #pi pregate, %.1f#circ slices of #Delta#phi_{0}(R=%.0f cm): %s.  Red: (#Delta#eta/%.3f)^{2}+((|#Delta#phi|-#Delta#phi_{0})/w)^{2}<1, w = max(%.1f#circ, 3#sigma(#Delta#phi_{0})), slice centre / edges.  Grey: old",GATEFINE_W,100*valDPsR,pname[ip],valPregateDEta,valDPsW));
3381             ccan[ican]->cd(); ccan[ican]->Update();
3382             ccan[ican]->Print(OutputFileName.Data());
3383         }
3384     }
3385 
3386     //---- README_SplitTracks573 sec 25: cosmic test page (hCos_*): signal window (black), ULS control (blue), LS control (red),
3387     //---- 1-D panels normalized to the signal window's entries
3388     {
3389         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3390         ccan[ican]->cd(); ccan[ican]->Divide(4,3,0.0001,0.0001);
3391         TH1D** one[7]   = { hCos_axis, hCos_ptAsym, hCos_eta, hCos_npi, hCos_vtxntr, hCos_pt, hCos_etotoh };
3392         const int col[NCOS] = { 1, 4, 2 };
3393         for (int k=0;k<7;k++){
3394             ccan[ican]->cd(1+k);
3395             double n0   = one[k][0]->Integral();
3396             TH1D* hd[NCOS]; // drawn copies: the stored histograms stay raw counts
3397             for (int w=0;w<NCOS;w++){
3398                 hd[w]   = (TH1D*)one[k][w]->Clone(Form("%s_draw",one[k][w]->GetName())); hd[w]->SetDirectory(0);
3399                 if (w>0 && hd[w]->Integral()>0 && n0>0) hd[w]->Scale(n0/hd[w]->Integral());
3400                 hd[w]->SetLineColor(col[w]); hd[w]->SetMarkerColor(col[w]); hd[w]->SetStats(0);
3401             }
3402             double mx=0; for (int w=0;w<NCOS;w++) mx=std::max(mx,hd[w]->GetMaximum()); hd[0]->SetMaximum(1.15*mx);
3403             for (int w=0;w<NCOS;w++) hd[w]->Draw(w==0 ? "hist" : "hist same");
3404         }
3405         for (int w=0;w<NCOS;w++){ ccan[ican]->cd(8+w); gPad->SetLogz(1); hCos_dcaxy[w]->SetStats(0); hCos_dcaxy[w]->Draw("colz"); }
3406         ccan[ican]->cd(11); gPad->SetLogz(1); hCos_dcaz[0]->SetStats(0); hCos_dcaz[0]->Draw("colz");
3407         ccan[ican]->cd(12); gPad->SetLogz(1); hCos_dcaz[1]->SetStats(0); hCos_dcaz[1]->Draw("colz");
3408         ccan[ican]->cd(); ccan[ican]->Update();
3409         ccan[ican]->Print(OutputFileName.Data());
3410     }
3411     //---- sec 25.1: p1 = -p2 pairs. Top: overlays as above (black w0 = OS >= 170 deg, blue w1 = OS 150-160, red w2 = LS >= 170;
3412     //---- w1, w2 scaled to w0's entries). Bottom: w0/w1 (raw counts, / the overall w0/w1). Cosmic halves: rel. p sum ~ 0,
3413     //---- broad Minv at 2|p| (> 2 GeV for most muons), axis peaked at 90 deg (vertical). A decay at rest: a fixed Minv.
3414     {
3415         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,800);
3416         ccan[ican]->cd(); ccan[ican]->Divide(4,2,0.0001,0.0001);
3417         TH1D** one[4]   = { hCos_psum, hCos_minv, hCos_axisBB, hCos_minvBB };
3418         const int col[NCOS] = { 1, 4, 2 };
3419         const char* wl[NCOS]    = { "OS, |#Delta#phi|#geq170#circ", "OS, 150-160#circ (control)", "LS, #geq170#circ (control)" };
3420         double r01  = (hCos_axis[1]->Integral()>0) ? hCos_axis[0]->Integral()/hCos_axis[1]->Integral() : 1.;
3421         for (int k=0;k<4;k++){
3422             ccan[ican]->cd(1+k);
3423             double n0   = one[k][0]->Integral();
3424             TH1D* hd[NCOS];
3425             for (int w=0;w<NCOS;w++){
3426                 hd[w]   = (TH1D*)one[k][w]->Clone(Form("%s_draw",one[k][w]->GetName())); hd[w]->SetDirectory(0);
3427                 if (w>0 && hd[w]->Integral()>0 && n0>0) hd[w]->Scale(n0/hd[w]->Integral());
3428                 hd[w]->SetLineColor(col[w]); hd[w]->SetStats(0);
3429             }
3430             double mx=0; for (int w=0;w<NCOS;w++) mx=std::max(mx,hd[w]->GetMaximum()); hd[0]->SetMaximum(1.15*mx); hd[0]->SetMinimum(0.);
3431             for (int w=0;w<NCOS;w++) hd[w]->Draw(w==0 ? "hist" : "hist same");
3432             if (k==0){ TLegend *lg = new TLegend(0.35,0.62,0.89,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.042);
3433                 for (int w=0;w<NCOS;w++) lg->AddEntry(hd[w],wl[w],"l"); lg->Draw(); }
3434             ccan[ican]->cd(5+k);
3435             TH1D *r = (TH1D*)one[k][0]->Clone(Form("%s_r01",one[k][0]->GetName())); r->SetDirectory(0);
3436             r->Divide(one[k][1]); if (r01>0) r->Scale(1./r01);
3437             r->SetTitle(Form("w0/w1 / overall (%.3f);%s;ratio",r01,one[k][0]->GetXaxis()->GetTitle()));
3438             r->SetStats(0); r->SetMarkerStyle(20); r->SetMarkerSize(0.5); r->SetMinimum(0.5); r->SetMaximum(2.0);
3439             r->Draw("E1");
3440             TLine *l1   = new TLine(r->GetXaxis()->GetXmin(),1.,r->GetXaxis()->GetXmax(),1.); l1->SetLineColor(kGray); l1->Draw();
3441             r->Draw("E1 same");
3442         }
3443         ccan[ican]->cd(); ccan[ican]->Update();
3444         ccan[ican]->Print(OutputFileName.Data());
3445     }
3446 
3447     //---- README_SplitTracks573 sec 34: the +-15 deg ULS notch MONITOR (a ULS sibling-pair deficit at 10-20 deg vertex dphi,
3448     //---- not made by any Corral cleaner, sec 32-33). Pions, abs(dy) < 1, S/M each / its 25 <= abs(dphi) < 50 deg level.
3449     //---- Top: ULS (0+1) and LS (2+3) S/M vs dphi, the notch band shaded; ULS S and M separately. Bottom: the notch band
3450     //---- (10-20 deg / 25-50 deg) vs pair <pt> and vs accepted N_ch, ULS (red) and LS (black); ULS S/M vs dphi in N_ch groups.
3451     if (!NOCORRELATIONS){
3452         gStyle->SetOptTitle(1);
3453         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1600,800);
3454         ccan[ican]->cd(); ccan[ican]->Divide(4,2,0.0001,0.0001);
3455         auto sum2   = [&](TH2D* (CalcRm::*get)(int), int sm, int a, int b, const char* nm){
3456             TH2D *h = (TH2D*)(R[a]->*get)(sm)->Clone(nm); h->SetDirectory(0); h->Add((R[b]->*get)(sm)); return h; };
3457         //---- S/M vs dphi (2 deg) of the y rows [y0,y1] of S and M, / its 25-50 deg level
3458         auto shapeDphi  = [&](TH2D *S, TH2D *M, int y0, int y1, const char* nm){
3459             TH1D *s1 = S->ProjectionX(Form("%s_s",nm),y0,y1), *m1 = M->ProjectionX(Form("%s_m",nm),y0,y1); s1->Rebin(2); m1->Rebin(2);
3460             double sr=0,mr=0; for (int i=1;i<=s1->GetNbinsX();i++){ double x=fabs(s1->GetBinCenter(i)); if (x>=25&&x<50){ sr+=s1->GetBinContent(i); mr+=m1->GetBinContent(i); } }
3461             TH1D *r = (TH1D*)s1->Clone(nm); r->Reset(); r->SetDirectory(0);
3462             for (int i=1;i<=s1->GetNbinsX();i++){ double sv=s1->GetBinContent(i), mv=m1->GetBinContent(i); if (sv>0&&mv>0&&sr>0&&mr>0){ double v=(sv/mv)/(sr/mr); r->SetBinContent(i,v); r->SetBinError(i,v*sqrt(1/sv+1/sr)); } }
3463             delete s1; delete m1; r->SetStats(0); return r; };
3464         //---- the notch band ratio vs the y axis of (S,M)
3465         auto bandY  = [&](TH2D *S, TH2D *M, const char* nm){
3466             TH1D *h = S->ProjectionY(nm); h->Reset(); h->SetDirectory(0);
3467             for (int iy=1;iy<=S->GetNbinsY();iy++){ double sb=0,mb=0,sr=0,mr=0;
3468                 for (int ix=1;ix<=S->GetNbinsX();ix++){ double x=fabs(S->GetXaxis()->GetBinCenter(ix)), sv=S->GetBinContent(ix,iy), mv=M->GetBinContent(ix,iy);
3469                     if (x>=10&&x<20){ sb+=sv; mb+=mv; } if (x>=25&&x<50){ sr+=sv; mr+=mv; } }
3470                 if (sb>0&&mb>0&&sr>0&&mr>0){ double v=(sb/mb)/(sr/mr); h->SetBinContent(iy,v); h->SetBinError(iy,v*sqrt(1/sb+1/sr)); } }
3471             h->SetStats(0); return h; };
3472         auto one    = [&](TH1D *h, int col, const char* opt){ h->SetLineColor(col); h->SetMarkerColor(col); h->SetMarkerStyle(20); h->SetMarkerSize(0.5); h->Draw(opt); };
3473         auto band   = [&](double y0, double y1){ for (int sg=-1;sg<=1;sg+=2){ TBox *b = new TBox(sg>0?10.:-20.,y0,sg>0?20.:-10.,y1); b->SetFillColorAlpha(kGray,0.35); b->SetLineWidth(0); b->Draw(); } };
3474         auto unity  = [&](double x0, double x1){ TLine *l = new TLine(x0,1.,x1,1.); l->SetLineColor(kGray+1); l->Draw(); };
3475         TH2D *uS = sum2(&CalcRm::GethGapPt,0,0,1,"nm_uS"), *uM = sum2(&CalcRm::GethGapPt,1,0,1,"nm_uM");
3476         TH2D *lS = sum2(&CalcRm::GethGapPt,0,2,3,"nm_lS"), *lM = sum2(&CalcRm::GethGapPt,1,2,3,"nm_lM");
3477         TH2D *uSn = sum2(&CalcRm::GethGapNch,0,0,1,"nm_uSn"), *uMn = sum2(&CalcRm::GethGapNch,1,0,1,"nm_uMn");
3478         TH2D *lSn = sum2(&CalcRm::GethGapNch,0,2,3,"nm_lSn"), *lMn = sum2(&CalcRm::GethGapNch,1,2,3,"nm_lMn");
3479         int npt = uS->GetNbinsY(), nnc = uSn->GetNbinsY();
3480         //---- pad 1: ULS and LS shapes
3481         ccan[ican]->cd(1);
3482         TH1D *ru = shapeDphi(uS,uM,1,npt,"nm_ru"), *rl = shapeDphi(lS,lM,1,npt,"nm_rl");
3483         double notchU   = 0, notchL = 0;
3484         { TH1D *bu = bandY(uS,uM,"nm_bu_all"); TH1D *bl = bandY(lS,lM,"nm_bl_all");
3485           // all pt: from the summed rows
3486           TH2D *uS1 = (TH2D*)uS->RebinY(npt,"nm_uS1"), *uM1 = (TH2D*)uM->RebinY(npt,"nm_uM1"), *lS1 = (TH2D*)lS->RebinY(npt,"nm_lS1"), *lM1 = (TH2D*)lM->RebinY(npt,"nm_lM1");
3487           notchU = bandY(uS1,uM1,"nm_nu")->GetBinContent(1); notchL = bandY(lS1,lM1,"nm_nl")->GetBinContent(1); delete bu; delete bl; }
3488         ru->SetTitle(Form("#pi#pi S/M / (25-50#circ), |dy|<1: notch band 10-20#circ = %.3f (ULS), %.3f (LS);#Delta#phi_{vtx} (deg);ratio",notchU,notchL));
3489         ru->SetMinimum(0.9); ru->SetMaximum(1.1); ru->Draw("E1"); band(0.9,1.1); unity(-60,60);
3490         one(ru,kRed,"E1 same"); one(rl,kBlack,"E1 same");
3491         { TLegend *lg = new TLegend(0.14,0.14,0.60,0.30); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.04);
3492           lg->AddEntry(ru,"ULS (#pi^{+}#pi^{-}, #pi^{-}#pi^{+})","lp"); lg->AddEntry(rl,"LS (#pi^{+}#pi^{+}, #pi^{-}#pi^{-})","lp"); lg->AddEntry((TObject*)0,"gray: the notch band","");
3493           lg->Draw(); }
3494         //---- pad 2: ULS S and M separately (each / its 25-50 deg mean): which one has the notch
3495         ccan[ican]->cd(2);
3496         { TH1D *s1 = uS->ProjectionX("nm_s1"), *m1 = uM->ProjectionX("nm_m1"); s1->Rebin(2); m1->Rebin(2); s1->SetDirectory(0); m1->SetDirectory(0);
3497           double sr=0,mr=0; int nr=0; for (int i=1;i<=s1->GetNbinsX();i++){ double x=fabs(s1->GetBinCenter(i)); if (x>=25&&x<50){ sr+=s1->GetBinContent(i); mr+=m1->GetBinContent(i); ++nr; } }
3498           if (sr>0&&mr>0){ s1->Scale(nr/sr); m1->Scale(nr/mr); }
3499           s1->SetStats(0); s1->SetTitle("ULS #pi#pi: sibling S (red) and mixed M (blue), each / its 25-50#circ mean;#Delta#phi_{vtx} (deg);/ 25-50#circ mean");
3500           s1->SetMinimum(0.4); s1->SetMaximum(1.2); s1->Draw("E1"); band(0.4,1.2); unity(-60,60); one(s1,kRed,"hist same"); one(m1,kBlue,"hist same"); }
3501         //---- pad 3: ULS S/M in N_ch groups
3502         ccan[ican]->cd(3);
3503         { const int grp[4][2]   = {{1,3},{4,6},{7,8},{9,nnc}}; const int gc[4] = {kBlue,kGreen+2,kOrange+7,kRed};
3504           TLegend *lg = new TLegend(0.14,0.14,0.60,0.34); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.04);
3505           for (int g=0;g<4;g++){ TH1D *r = shapeDphi(uSn,uMn,grp[g][0],grp[g][1],Form("nm_rn%d",g));
3506             if (g==0){ r->SetTitle("ULS #pi#pi S/M / (25-50#circ) in N_{ch} groups;#Delta#phi_{vtx} (deg);ratio"); r->SetMinimum(0.85); r->SetMaximum(1.15); r->Draw("E1"); band(0.85,1.15); unity(-60,60); }
3507             one(r,gc[g],"E1 same");
3508             lg->AddEntry(r,Form("N_{ch} %.0f-%.0f",uSn->GetYaxis()->GetBinLowEdge(grp[g][0])+0.5,uSn->GetYaxis()->GetBinUpEdge(grp[g][1])-0.5),"lp"); }
3509           lg->Draw(); }
3510         //---- pads 4 and 7 (sec 34.2): S/M vs the pair's meeting radius R_m (where the tracks meet in phi), / the overall S/M, in the notch band (pad 4)
3511         //---- and at every dphi (pad 7); gray = the INTT-TPC gap (0.1-0.3 m), where pairs that meet there are lost
3512         {
3513             TH2D *xS[2], *xM[2];
3514             for (int k=0;k<2;k++){ xS[k] = sum2(&CalcRm::GethGapRm,0,k?2:0,k?3:1,Form("nm_xS%d",k)); xM[k] = sum2(&CalcRm::GethGapRm,1,k?2:0,k?3:1,Form("nm_xM%d",k)); }
3515             for (int pad=0;pad<2;pad++){
3516                 ccan[ican]->cd(pad==0?4:7);
3517                 TH1D *h[2];
3518                 for (int k=0;k<2;k++){
3519                     TH2D *S = xS[k], *M = xM[k];
3520                     double sT = S->Integral(), mT = M->Integral();
3521                     int a1 = S->GetXaxis()->FindBin(-19.99), a2 = S->GetXaxis()->FindBin(-10.01), b1 = S->GetXaxis()->FindBin(10.01), b2 = S->GetXaxis()->FindBin(19.99);
3522                     h[k]    = S->ProjectionY(Form("nm_rx%d%d",pad,k)); h[k]->Reset(); h[k]->SetDirectory(0);
3523                     for (int iy=1;iy<S->GetNbinsY();iy++){  // the last bin = no crossing, left out
3524                         double sv = pad==0 ? S->Integral(a1,a2,iy,iy)+S->Integral(b1,b2,iy,iy) : S->Integral(1,S->GetNbinsX(),iy,iy);
3525                         double mv = pad==0 ? M->Integral(a1,a2,iy,iy)+M->Integral(b1,b2,iy,iy) : M->Integral(1,M->GetNbinsX(),iy,iy);
3526                         if (sv<200 || mv<=0 || sT<=0 || mT<=0) continue;
3527                         double v = (sv/mv)/(sT/mT); h[k]->SetBinContent(iy,v); h[k]->SetBinError(iy,v/sqrt(sv));
3528                     }
3529                     h[k]->SetStats(0);
3530                 }
3531                 h[0]->SetTitle(Form("S/M vs meeting radius R_{m} (tracks meet in #phi), %s: ULS (red), LS (black);R_{m} (m);S/M / overall",pad==0?"10-20#circ (notch band)":"all #Delta#phi"));
3532                 h[0]->GetXaxis()->SetRangeUser(0.,1.0); h[0]->SetMinimum(0.85); h[0]->SetMaximum(pad==0?1.25:1.10);
3533                 one(h[0],kRed,"E1");
3534                 TBox *gap = new TBox(0.10,0.85,0.30,pad==0?1.25:1.10); gap->SetFillColorAlpha(kGray,0.35); gap->SetLineWidth(0); gap->Draw();
3535                 unity(0.,1.0); one(h[0],kRed,"E1 same"); one(h[1],kBlack,"E1 same");
3536                 if (pad==0){ TLegend *lg = new TLegend(0.40,0.70,0.89,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.04);
3537                     lg->AddEntry(h[0],"ULS","lp"); lg->AddEntry(h[1],"LS","lp"); lg->AddEntry((TObject*)0,"gray: INTT-TPC gap, 0.1-0.3 m",""); lg->Draw(); }
3538             }
3539         }
3540         //---- pads 5-6: the notch band vs <pt> and vs N_ch, ULS red, LS black
3541         for (int k=0;k<2;k++){
3542             ccan[ican]->cd(5+k);
3543             TH1D *bu = k ? bandY(uSn,uMn,"nm_bun") : bandY(uS,uM,"nm_bup");
3544             TH1D *bl = k ? bandY(lSn,lMn,"nm_bln") : bandY(lS,lM,"nm_blp");
3545             bu->SetTitle(Form("notch band (10-20#circ / 25-50#circ) vs %s: ULS (red), LS (black);%s;ratio",k?"N_{ch}":"pair #LTp_{T}#GT",k?"N_{ch} accepted":"#LTp_{T}#GT (GeV/c)"));
3546             bu->SetMinimum(0.85); bu->SetMaximum(1.10); bu->Draw("E1"); unity(bu->GetXaxis()->GetXmin(),bu->GetXaxis()->GetXmax());
3547             one(bu,kRed,"E1 same"); one(bl,kBlack,"E1 same");
3548         }
3549         //---- pad 8: text
3550         ccan[ican]->cd(8);
3551         TPaveText *pt   = new TPaveText(0.03,0.03,0.97,0.97,"NDC"); pt->SetFillColor(0); pt->SetBorderSize(1); pt->SetTextAlign(12); pt->SetTextSize(0.032);
3552         pt->AddText("The #pm15#circ ULS notch: WATCH (README_SplitTracks573 sec 32-34)");
3553         pt->AddText(Form("  notch band / 25-50#circ, all pairs: ULS %.3f, LS %.3f",notchU,notchL));
3554         pt->AddText("  a deficit of ULS SIBLING pairs (pad 2); LS has none");
3555         pt->AddText("  not made by any Corral cleaner: ULS veto, crossing,");
3556         pt->AddText("    V0 daughters, LS split removal, XTF, TFdup");
3557         pt->AddText("  flat in pair #LT#phi#GT (no stave/ladder period) and #LTy#GT");
3558         pt->AddText("  also in the raw Collect trees and in ana532 (sec 28)");
3559         pt->AddText("  CAUSE (sec 34.2): pairs whose tracks meet in #phi at R 0.1-0.3 m");
3560         pt->AddText("    (INTT-TPC gap: silicon-TPC seed matching) are lost (pad 4)");
3561         pt->Draw();
3562         ccan[ican]->cd(); ccan[ican]->Update();
3563         ccan[ican]->Print(OutputFileName.Data());
3564     }
3565 
3566     //---- README_SplitTracks573 sec 28: the TPC sector gaps. Page 1: the single-track "spokes" (hSpoke, each pt row
3567     //---- divided by its mean): bent at the vertex, straight at R = 0.55 m. Page 2: the pair-level smoking gun (hGap,
3568     //---- CalcRm): sibling and mixed pairs vs (dphi, s), s = the pair's bending-offset difference at R; the gaps line
3569     //---- up on the diagonals dphi - s = 0, +-30, ... in both. R2(dphi) summed over s (= the standard projection)
3570     //---- vs R2 formed in each s bin and averaged with weights w_s = sum over dphi of M_s (no dphi dependence).
3571     {
3572         auto rowNorm    = [&](TH2D* h, const char* nm){
3573             TH2D* c = (TH2D*)h->Clone(nm); c->SetDirectory(0);
3574             for (int iy=1;iy<=c->GetNbinsY();iy++){
3575                 double sw=0; int n=0;
3576                 for (int ix=1;ix<=c->GetNbinsX();ix++){ sw+=c->GetBinContent(ix,iy); ++n; }
3577                 double m    = n>0 ? sw/n : 0.;
3578                 for (int ix=1;ix<=c->GetNbinsX();ix++) c->SetBinContent(ix,iy, m>0 ? c->GetBinContent(ix,iy)/m : 0.);
3579             }
3580             c->SetStats(0); c->SetMinimum(0.); c->SetMaximum(1.6);
3581             return c;
3582         };
3583         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3584         ccan[ican]->cd();
3585         TPad* spTop     = new TPad("spTop","",0.,0.95,1.,1.);
3586         TPad* spMain    = new TPad("spMain","",0.,0.,1.,0.95);
3587         spTop->Draw(); spMain->Draw();
3588         spMain->Divide(3,2,0.0001,0.0001);
3589         for (int q=0;q<2;q++){
3590             for (int f=0;f<2;f++){
3591                 spMain->cd(1+3*q+f); gPad->SetRightMargin(0.13);
3592                 rowNorm(hSpoke[q][f],Form("hSpoke_draw_%d_%d",q,f))->Draw("colz");
3593             }
3594             spMain->cd(3+3*q);
3595             int b0  = hSpoke[q][0]->GetYaxis()->FindBin(0.301), b1 = hSpoke[q][0]->GetYaxis()->FindBin(0.599);
3596             TH1D* pv    = hSpoke[q][0]->ProjectionX(Form("hSpokeV_%d",q),b0,b1); pv->SetDirectory(0);
3597             TH1D* pr    = hSpoke[q][1]->ProjectionX(Form("hSpokeR_%d",q),b0,b1); pr->SetDirectory(0);
3598             for (TH1D* h : {pv,pr}){ double m=h->Integral()/h->GetNbinsX(); if (m>0) h->Scale(1.0/m); h->SetStats(0); }
3599             pv->SetTitle(Form("%s, 0.3<p_{T}<0.6 GeV/c: vertex #phi (black), #phi at R=0.55 m (red);#phi (deg);relative yield",q?"negative":"positive"));
3600             pv->SetLineColor(1); pr->SetLineColor(2); pv->SetMinimum(0.); pv->SetMaximum(1.6);
3601             pv->Draw("hist"); pr->Draw("hist same");
3602         }
3603         spTop->cd();
3604         ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.36); text[itext]->SetTextAlign(22);
3605         text[itext]->DrawLatex(0.5,0.5,"TPC sector gaps (accepted charged tracks, each p_{T} row / its mean): at the vertex a gap is a spoke #phi_{gap}+q asin(aR/p_{T}), bent oppositely for + and -; at R=0.55 m it is a vertical line");
3606         ccan[ican]->cd(); ccan[ican]->Update();
3607         ccan[ican]->Print(OutputFileName.Data());
3608     }
3609     if (!NOCORRELATIONS){
3610         //---- page 2: rows pi+pi+ and pi+pi- (0.4 <= abs(dy) < 1.0): S, M, R2_s maps and the 1-D comparison; row 3:
3611         //---- the 1-D comparison for pi-pi-, pi-pi+ (far) and pi+pi+, pi+pi- (abs(dy) < 0.4). M is normalized so that
3612         //---- the s-summed R2 over abs(dphi) < 60 deg equals the mean of the standard R2(dy,dphi) map in the same window.
3613         struct GapOut { TH2D *S,*M,*R; TH1D *plain,*corr; };
3614         auto gapBuild   = [&](int ip, int w)->GapOut{
3615             GapOut g;
3616             g.S = (TH2D*)R[ip]->GethGap(0,w)->Clone(Form("hGapS_draw_%d_%d",ip,w)); g.S->SetDirectory(0); g.S->RebinX(3);
3617             g.M = (TH2D*)R[ip]->GethGap(1,w)->Clone(Form("hGapM_draw_%d_%d",ip,w)); g.M->SetDirectory(0); g.M->RebinX(3);
3618             double r2ref=0; int nref=0;
3619             TH2D* hm    = hR2[1][ip];
3620             for (int ix=1;ix<=hm->GetNbinsX();ix++) for (int iy=1;iy<=hm->GetNbinsY();iy++){
3621                 double ady=fabs(hm->GetXaxis()->GetBinCenter(ix)), dp=hm->GetYaxis()->GetBinCenter(iy);
3622                 bool in = (w==0) ? (ady>=0.4 && ady<1.0) : (ady<0.4);
3623                 if (in && fabs(dp)<60.){ r2ref+=hm->GetBinContent(ix,iy); ++nref; }
3624             }
3625             r2ref   = nref>0 ? r2ref/nref : 0.;
3626             double sS=g.S->Integral(), sM=g.M->Integral();
3627             double kM   = (sM>0) ? sS/((1.0+r2ref)*sM) : 1.;
3628             g.M->Scale(kM);
3629             int nx=g.S->GetNbinsX(), ny=g.S->GetNbinsY();
3630             g.R = (TH2D*)g.S->Clone(Form("hGapR_draw_%d_%d",ip,w)); g.R->Reset(); g.R->SetDirectory(0);
3631             g.plain = g.S->ProjectionX(Form("hGapP_%d_%d",ip,w)); g.plain->Reset(); g.plain->SetDirectory(0);
3632             g.corr  = (TH1D*)g.plain->Clone(Form("hGapC_%d_%d",ip,w)); g.corr->SetDirectory(0);
3633             std::vector<double> ws(ny+1,0.);
3634             for (int iy=1;iy<=ny;iy++) for (int ix=1;ix<=nx;ix++) ws[iy]+=g.M->GetBinContent(ix,iy);
3635             for (int ix=1;ix<=nx;ix++){
3636                 double ss=0, mm=0, cn=0, ce2=0, cw=0;
3637                 for (int iy=1;iy<=ny;iy++){
3638                     double sv=g.S->GetBinContent(ix,iy), mv=g.M->GetBinContent(ix,iy);
3639                     ss+=sv; mm+=mv;
3640                     if (mv<=0.) continue;
3641                     if (mv/kM>=10.) g.R->SetBinContent(ix,iy,sv/mv-1.); // map only: cells with >= 10 raw mixed pairs
3642                     cn  += ws[iy]*sv/mv; ce2 += ws[iy]*ws[iy]*sv/(mv*mv); cw += ws[iy];
3643                 }
3644                 if (mm>0){ g.plain->SetBinContent(ix,ss/mm-1.); g.plain->SetBinError(ix,sqrt(ss)/mm); }
3645                 if (cw>0){ g.corr->SetBinContent(ix,cn/cw-1.); g.corr->SetBinError(ix,sqrt(ce2)/cw); }
3646             }
3647             return g;
3648         };
3649         auto gapGrid    = [&](double lo, double hi){
3650             for (int k=-2;k<=2;k++){ double x=15.*(2*k+1); if (x<=-60.||x>=60.) continue;
3651                 TLine* l=new TLine(x,lo,x,hi); l->SetLineColor(kGray+1); l->SetLineStyle(2); l->Draw(); }
3652             for (int k=-1;k<=1;k++){ TLine* l=new TLine(30.*k,lo,30.*k,hi); l->SetLineColor(kGray+1); l->SetLineStyle(3); l->Draw(); }
3653         };
3654         auto gap1D  = [&](GapOut& g, const char* title){
3655             g.plain->SetTitle(Form("%s;#Delta#phi (deg);R_{2}",title)); g.plain->SetStats(0);
3656             g.plain->SetLineColor(1); g.plain->SetMarkerColor(1); g.plain->SetMarkerStyle(20); g.plain->SetMarkerSize(0.6);
3657             g.corr->SetLineColor(2); g.corr->SetMarkerColor(2); g.corr->SetMarkerStyle(24); g.corr->SetMarkerSize(0.6);
3658             double lo=std::min(g.plain->GetMinimum(),g.corr->GetMinimum()), hi=std::max(g.plain->GetMaximum(),g.corr->GetMaximum());
3659             double pad  = 0.15*(hi-lo); g.plain->SetMinimum(lo-pad); g.plain->SetMaximum(hi+pad);
3660             g.plain->Draw("E1"); g.corr->Draw("E1 same"); gapGrid(lo-pad,hi+pad);
3661             TLegend* lg = new TLegend(0.14,0.78,0.70,0.90); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.045);
3662             lg->AddEntry(g.plain,"summed over s (standard)","lp"); lg->AddEntry(g.corr,"R_{2} per s bin, then averaged","lp"); lg->Draw();
3663         };
3664         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3665         ccan[ican]->cd();
3666         TPad* gpTop     = new TPad("gpTop","",0.,0.95,1.,1.);
3667         TPad* gpMain    = new TPad("gpMain","",0.,0.,1.,0.95);
3668         gpTop->Draw(); gpMain->Draw();
3669         gpMain->Divide(4,3,0.0001,0.0001);
3670         const int iprow[2]  = { 2, 0 };
3671         const char* pn[4]   = { "#pi^{+}#pi^{-}", "#pi^{-}#pi^{+}", "#pi^{+}#pi^{+}", "#pi^{-}#pi^{-}" };
3672         for (int r=0;r<2;r++){
3673             int ip  = iprow[r];
3674             if (ip>=NPairTypes) continue;
3675             GapOut g    = gapBuild(ip,0);
3676             gpMain->cd(1+4*r); gPad->SetRightMargin(0.13); g.S->SetStats(0); g.S->SetTitle(Form("%s sibling, 0.4#leq|dy|<1;#Delta#phi (deg);s (deg)",pn[ip])); g.S->Draw("colz");
3677             gpMain->cd(2+4*r); gPad->SetRightMargin(0.13); g.M->SetStats(0); g.M->SetTitle(Form("%s mixed, 0.4#leq|dy|<1;#Delta#phi (deg);s (deg)",pn[ip])); g.M->Draw("colz");
3678             gpMain->cd(3+4*r); gPad->SetRightMargin(0.13); g.R->SetStats(0); g.R->SetTitle(Form("%s R_{2} per (#Delta#phi,s) bin;#Delta#phi (deg);s (deg)",pn[ip]));
3679             { double m=g.plain->GetBinContent(g.plain->GetMaximumBin()); g.R->SetMinimum(-0.3); g.R->SetMaximum(std::max(0.5,2.*m)); }
3680             g.R->Draw("colz");
3681             for (int k=-3;k<=3;k++){ TLine* l=new TLine(-60.,-60.+30.*k,60.,60.+30.*k); l->SetLineColor(kGray+2); l->SetLineStyle(3); l->Draw(); }
3682             gpMain->cd(4+4*r); gap1D(g,Form("%s, 0.4#leq|dy|<1",pn[ip]));
3683         }
3684         const int ip3[4]    = { 3, 1, 2, 0 };
3685         const int w3[4]     = { 0, 0, 1, 1 };
3686         for (int c=0;c<4;c++){
3687             if (ip3[c]>=NPairTypes) continue;
3688             GapOut g    = gapBuild(ip3[c],w3[c]);
3689             gpMain->cd(9+c); gap1D(g,Form("%s, %s",pn[ip3[c]],w3[c]?"|dy|<0.4":"0.4#leq|dy|<1"));
3690         }
3691         gpTop->cd();
3692         ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.36); text[itext]->SetTextAlign(22);
3693         text[itext]->DrawLatex(0.5,0.5,"Sector gaps in pairs: s = q_{1}asin(aR/p_{T1}) - q_{2}asin(aR/p_{T2}) at R=0.55 m, so #Delta#phi - s = pair separation at the TPC; gaps align on #Delta#phi - s = 0, #pm30#circ (dotted). Grey dashed: #pm15, #pm45#circ");
3694         ccan[ican]->cd(); ccan[ican]->Update();
3695         ccan[ican]->Print(OutputFileName.Data());
3696     }
3697     //---- README_SplitTracks573 sec 29: the near-vertex two-track loss and the isep cut drawn on it. hISep (CalcRm) holds
3698     //---- every charged pair before the cut (after tsep): sibling / mixed vs (dphi*(R = 3 cm), abs(dy)). Left: S/M, each
3699     //---- abs(dy) row divided by its own value at 4 < abs(dphi*) < 10 deg, with the cut box (red; dashed at the
3700     //---- default 1 deg when isep is off). Right: the same ratio in abs(dy) windows, cut edges marked. LS = ++ and --
3701     //---- summed, ULS = +- and -+ summed.
3702     if (!NOCORRELATIONS && NPairTypes>=4){
3703         const double wcut   = (R[0]->GetISepDphi()>0.) ? R[0]->GetISepDphi() : 1.0;
3704         const bool on       = (R[0]->GetISepDphi()>0.);
3705         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1400,900);
3706         ccan[ican]->cd();
3707         TPad* isTop     = new TPad("isTop","",0.,0.95,1.,1.);
3708         TPad* isMain    = new TPad("isMain","",0.,0.,1.,0.95);
3709         isTop->Draw(); isMain->Draw();
3710         isMain->Divide(2,2,0.0001,0.0001);
3711         const int ipa[2][2] = { {2,3}, {0,1} };
3712         const char* cname[2]    = { "LS (#pi^{+}#pi^{+} + #pi^{-}#pi^{-})", "ULS (#pi^{+}#pi^{-} + #pi^{-}#pi^{+})" };
3713         for (int c=0;c<2;c++){
3714             TH2D* S = (TH2D*)R[ipa[c][0]]->GethISep(0)->Clone(Form("hISepS_draw_%d",c)); S->SetDirectory(0); S->Add(R[ipa[c][1]]->GethISep(0));
3715             TH2D* M = (TH2D*)R[ipa[c][0]]->GethISep(1)->Clone(Form("hISepM_draw_%d",c)); M->SetDirectory(0); M->Add(R[ipa[c][1]]->GethISep(1));
3716             TH2D* Q = (TH2D*)S->Clone(Form("hISepQ_draw_%d",c)); Q->Reset(); Q->SetDirectory(0);
3717             for (int iy=1;iy<=S->GetNbinsY();iy++){
3718                 double rs=0, rm=0;
3719                 for (int ix=1;ix<=S->GetNbinsX();ix++){ double a=fabs(S->GetXaxis()->GetBinCenter(ix)); if (a>4.&&a<10.){ rs+=S->GetBinContent(ix,iy); rm+=M->GetBinContent(ix,iy); } }
3720                 if (rs<=0.||rm<=0.) continue;
3721                 for (int ix=1;ix<=S->GetNbinsX();ix++){ double m=M->GetBinContent(ix,iy); if (m>0.) Q->SetBinContent(ix,iy,(S->GetBinContent(ix,iy)/m)/(rs/rm)); }
3722             }
3723             isMain->cd(1+2*c); gPad->SetRightMargin(0.13);
3724             Q->SetStats(0); Q->SetMinimum(0.7); Q->SetMaximum(1.1);
3725             Q->SetTitle(Form("%s: sibling/mixed, each |dy| row / its 4<|#Delta#phi*|<10#circ value;#Delta#phi*(R=3 cm) (deg);|dy|",cname[c]));
3726             Q->Draw("colz");
3727             TBox* bx    = new TBox(-wcut,0.,wcut,CalcRm::ISEP_DYMAX); bx->SetFillStyle(0); bx->SetLineColor(2); bx->SetLineWidth(2); bx->SetLineStyle(on?1:2); bx->Draw();
3728             isMain->cd(2+2*c);
3729             const double dw[5]  = { 0., 0.2, 0.4, 0.8, 1.5 };
3730             const int dcol[4]   = { 1, 4, 8, kGray+1 };
3731             TLegend* lg = new TLegend(0.14,0.14,0.50,0.36); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.04);
3732             for (int k=0;k<4;k++){
3733                 int b0  = S->GetYaxis()->FindBin(dw[k]+1e-4), b1 = S->GetYaxis()->FindBin(dw[k+1]-1e-4);
3734                 TH1D* s = S->ProjectionX(Form("hISepSp_%d_%d",c,k),b0,b1); s->SetDirectory(0);
3735                 TH1D* m = M->ProjectionX(Form("hISepMp_%d_%d",c,k),b0,b1); m->SetDirectory(0);
3736                 double rs=0, rm=0;
3737                 for (int ix=1;ix<=s->GetNbinsX();ix++){ double a=fabs(s->GetBinCenter(ix)); if (a>4.&&a<10.){ rs+=s->GetBinContent(ix); rm+=m->GetBinContent(ix); } }
3738                 TH1D* r = (TH1D*)s->Clone(Form("hISepR_%d_%d",c,k)); r->SetDirectory(0); r->Reset();
3739                 for (int ix=1;ix<=s->GetNbinsX();ix++){ double sv=s->GetBinContent(ix), mv=m->GetBinContent(ix);
3740                     if (sv>0.&&mv>0.&&rs>0.&&rm>0.){ double v=(sv/mv)/(rs/rm); r->SetBinContent(ix,v); r->SetBinError(ix,v*sqrt(1./sv+1./rs)); } }
3741                 r->SetStats(0); r->SetLineColor(dcol[k]); r->SetMarkerColor(dcol[k]); r->SetMarkerStyle(20); r->SetMarkerSize(0.5);
3742                 r->SetMinimum(0.75); r->SetMaximum(1.15);
3743                 r->SetTitle(Form("%s: sibling/mixed in |dy| windows (1 = no loss);#Delta#phi*(R=3 cm) (deg);S/M, relative",cname[c]));
3744                 r->Draw(k==0 ? "E1" : "E1 same");
3745                 lg->AddEntry(r,Form("%.1f#leq|dy|<%.1f",dw[k],dw[k+1]),"lp");
3746             }
3747             lg->Draw();
3748             for (int sg=-1;sg<=1;sg+=2){ TLine* l=new TLine(sg*wcut,0.75,sg*wcut,1.15); l->SetLineColor(2); l->SetLineWidth(2); l->SetLineStyle(on?1:2); l->Draw(); }
3749             TLine* l1=new TLine(-10.,1.,10.,1.); l1->SetLineColor(kGray+2); l1->SetLineStyle(3); l1->Draw();
3750         }
3751         isTop->cd();
3752         ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.36); text[itext]->SetTextAlign(22);
3753         text[itext]->DrawLatex(0.5,0.5,Form("Near-vertex two-track loss (sibling tracks at the same #phi near the vertex, out to |dy|~0.8; all pairs, before the cut).  Red: isep cut |#Delta#phi*(3 cm)|<%.1f#circ, |dy|<%.1f: %s",wcut,CalcRm::ISEP_DYMAX,on?"ON":"OFF (dashed = proposed)"));
3754         ccan[ican]->cd(); ccan[ican]->Update();
3755         ccan[ican]->Print(OutputFileName.Data());
3756     }
3757 
3758     //---- event QA
3759     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3760     ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
3761     ccan[ican]->cd(1); hxing    ->Draw();
3762     ccan[ican]->cd(2); hvtxx0   ->Draw(); hvtxx ->Draw("same");
3763     ccan[ican]->cd(3); hvtxy0   ->Draw(); hvtxy ->Draw("same");
3764     ccan[ican]->cd(4); hvtxz0   ->Draw(); hvtxz ->Draw("same");
3765     ccan[ican]->cd(5); hntrk0   ->Draw(); hntrk ->Draw("same");
3766     ccan[ican]->cd(6); htrig0   ->Draw(); htrig ->Draw("same");
3767     ccan[ican]->cd(); ccan[ican]->Update();
3768     ccan[ican]->Print(OutputFileName.Data());
3769 
3770     //---- 2026-10-08: crossing vs the TF's trigger class -- does the triggered collision leak into its
3771     //---- neighbours? Two zoom levels per page (top row wider). Thin grey lines: the
3772     //---- NearX0 window [XNEAR_LO,XNEAR_HI] without 0; crossing 0 = Xing0; ZB = the rest of [ZB_LO,ZB_HI] (dashed).
3773     //---- Two pages: raw (filled before the crossing selection and event cuts, so every run shows all crossings;
3774     //---- and kept (filled after everything). One bin per crossing, drawn HIST.
3775     auto drawXingPage = [&](TH1D **hn, TProfile **pn, TProfile **pp, double r0lo, double r0hi, double r1lo, double r1hi, const char *dopt){
3776         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3777         ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
3778         TH1D *hev[NXTRIGCL];
3779         for (int c=0;c<NXTRIGCL;c++){ hev[c] = (TH1D*)hn[c]->Clone(Form("%s%d_draw",hn[c]->GetName(),c)); hev[c]->SetDirectory(0); hev[c]->Scale(1.,"width"); hev[c]->GetYaxis()->SetTitle("events / crossing"); }
3780         TH1 *col[3][NXTRIGCL];
3781         for (int c=0;c<NXTRIGCL;c++){ col[0][c] = hev[c]; col[1][c] = pn[c]; col[2][c] = pp[c]; }
3782         for (int row=0;row<2;row++) for (int k=0;k<3;k++){
3783             ccan[ican]->cd(1+3*row+k); gPad->SetLeftMargin(0.13); gPad->SetRightMargin(0.03);
3784             double xlo = row==0 ? r0lo : r1lo, xhi = row==0 ? r0hi : r1hi;
3785             int b1 = col[k][0]->FindBin(xlo+0.1), b2 = col[k][0]->FindBin(xhi-0.1);
3786             double ymin = 1e30, ymax = -1e30;
3787             for (int c=0;c<NXTRIGCL;c++) for (int b=b1;b<=b2;b++){
3788                 if (col[k][c]->GetBinContent(b)<=0) continue;
3789                 ymin = std::min(ymin,col[k][c]->GetBinContent(b)); ymax = std::max(ymax,col[k][c]->GetBinContent(b));
3790             }
3791             if (ymax<ymin){ ymin = 0.1; ymax = 1.; }
3792             if (k==0){ gPad->SetLogy(1); ymin *= 0.5; ymax *= 3.; } else { double d = ymax-ymin; ymin -= 0.1*d; ymax += 0.35*d; }
3793             //---- every frame draws its own copies: the two rows show different x ranges of the same histograms
3794             TH1 *dh[NXTRIGCL];
3795             for (int c=0;c<NXTRIGCL;c++){ dh[c] = (TH1*)col[k][c]->Clone(Form("%s_p%d_f%d",col[k][c]->GetName(),ican,1+3*row+k)); dh[c]->SetDirectory(0); }
3796             bool first = true;
3797             for (int c=0;c<NXTRIGCL;c++){
3798                 if (dh[c]->GetEntries()<=0) continue;
3799                 dh[c]->GetXaxis()->SetRangeUser(xlo,xhi);
3800                 dh[c]->SetMinimum(ymin); dh[c]->SetMaximum(ymax);
3801                 dh[c]->Draw(first ? dopt : Form("%s same",dopt)); first = false;
3802             }
3803             if (first) continue;
3804             gPad->Update();
3805             double ylo = k==0 ? pow(10,gPad->GetUymin()) : gPad->GetUymin(), yhi = k==0 ? pow(10,gPad->GetUymax()) : gPad->GetUymax();
3806             TLine *lx = 0;
3807             for (double xn : {XNEAR_LO-0.5,XNEAR_HI+0.5}){ if (xn<xlo || xn>xhi) continue; lx = new TLine(xn,ylo,xn,yhi); lx->SetLineColor(kGray+1); lx->SetLineWidth(1); lx->Draw(); }
3808             TLine *lz = 0;
3809             for (double xz : {ZB_LO-0.5,ZB_HI+0.5}){ if (xz<xlo || xz>xhi) continue; lz = new TLine(xz,ylo,xz,yhi); lz->SetLineColor(kMagenta+2); lz->SetLineStyle(2); lz->SetLineWidth(2); lz->Draw(); }
3810             for (int c=0;c<NXTRIGCL;c++) if (dh[c]->GetEntries()>0) dh[c]->Draw(Form("%s same",dopt));
3811             if (row==0 && k==1){
3812                 TLegend *lg = new TLegend(0.15,0.80,0.96,0.90); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.032); lg->SetNColumns(4);  // 2 rows x 4 columns across the frame
3813                 for (int c=0;c<NXTRIGCL;c++) if (dh[c]->GetEntries()>0) lg->AddEntry(dh[c],xtrigName[c],"lp");
3814                 TLine *lnx = new TLine(); lnx->SetLineColor(kGray+1); lnx->SetLineWidth(1);
3815                 lg->AddEntry(lnx,Form("NearX0 [%d,%d]",XNEAR_LO,XNEAR_HI),"l");
3816                 TLine *ll = new TLine(); ll->SetLineColor(kMagenta+2); ll->SetLineStyle(2); ll->SetLineWidth(2);
3817                 lg->AddEntry(ll,Form("ZB [%d,%d]",ZB_LO,ZB_HI),"l");
3818                 lg->Draw();
3819             }
3820         }
3821         ccan[ican]->cd(); ccan[ican]->Update();
3822         ccan[ican]->Print(OutputFileName.Data());
3823     };
3824     //---- raw set twice (the kept set only the first page): (full range, -20..40) and a zoom on crossing 0 vs its neighbours (-50..50, -10..10)
3825     drawXingPage(hnevraw_xing,pntrkraw_xing,pptraw_xing,-100.5,480.5,-20.5,40.5,"HIST");
3826     drawXingPage(hnevraw_xing,pntrkraw_xing,pptraw_xing,-50.5,50.5,-10.5,10.5,"HIST");
3827     drawXingPage(hnev_xing,pntrk_xing,ppt_xing,-100.5,480.5,-20.5,40.5,"HIST");
3828 
3829     //---- track kinematics (raw = black, kept = green overlay)
3830     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3831     ccan[ican]->cd(); ccan[ican]->Divide(3,3,0.0001,0.0001);
3832     ccan[ican]->cd(1); heta0    ->Draw(); heta  ->Draw("same");
3833     ccan[ican]->cd(2); hphi0    ->Draw(); hphi  ->Draw("same");
3834     ccan[ican]->cd(3); gPad->SetLogy(1); hpt0   ->Draw(); hpt   ->Draw("same");
3835     ccan[ican]->cd(4); hphieta          ->Draw("colz");
3836     ccan[ican]->cd(5); hpteta           ->Draw("colz");
3837     ccan[ican]->cd(6); hptphi           ->Draw("colz");
3838     ccan[ican]->cd(7); hetazvtx     ->Draw("colz");
3839     ccan[ican]->cd(8); hetazvtx_pta ->Draw("colz");
3840     ccan[ican]->cd(9); hetazvtx_ptb ->Draw("colz");
3841     ccan[ican]->cd(); ccan[ican]->Update();
3842     ccan[ican]->Print(OutputFileName.Data());
3843 
3844     //---- track quality (raw = black, kept = green overlay)
3845     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3846     ccan[ican]->cd(); ccan[ican]->Divide(3,3,0.0001,0.0001);
3847     ccan[ican]->cd(1); hntpc0   ->Draw(); hntpc ->Draw("same");
3848     ccan[ican]->cd(2); hnmvtx0  ->Draw(); hnmvtx    ->Draw("same");
3849     ccan[ican]->cd(3); hnintt0  ->Draw(); hnintt    ->Draw("same");
3850     ccan[ican]->cd(4); hdcaxy0  ->Draw(); hdcaxy    ->Draw("same");
3851     ccan[ican]->cd(5); hdcaz0   ->Draw(); hdcaz ->Draw("same");
3852     ccan[ican]->cd(6); hdcaxyz              ->Draw("colz");
3853     ccan[ican]->cd(7); hntpc_phieta     ->Draw("colz");
3854     ccan[ican]->cd(8); habsdcaxy_phieta ->Draw("colz");
3855     ccan[ican]->cd(9); habsdcaz_phieta      ->Draw("colz");
3856     ccan[ican]->cd(); ccan[ican]->Update();
3857     ccan[ican]->Print(OutputFileName.Data());
3858 
3859     //---- calorimeter QA
3860     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3861     ccan[ican]->cd(); ccan[ican]->Divide(2,3,0.0001,0.0001);
3862     ccan[ican]->cd(1); gPad->SetLogy(1); hetotem    ->Draw();
3863     ccan[ican]->cd(2); gPad->SetLogy(1); hetotih    ->Draw();
3864     ccan[ican]->cd(3); gPad->SetLogy(1); hetotoh    ->Draw();
3865     ccan[ican]->cd(4); gPad->SetLogy(1); hetotioh   ->Draw();
3866     ccan[ican]->cd(); ccan[ican]->Update();
3867     ccan[ican]->Print(OutputFileName.Data());
3868 
3869     //---- dedx QA
3870     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3871     ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
3872     ccan[ican]->cd(1); hdedx            ->Draw();
3873     ccan[ican]->cd(2); hndedx           ->Draw();
3874     ccan[ican]->cd(3); hdedxphi     ->Draw();
3875     ccan[ican]->cd(4); hdedxphi_etap    ->Draw();
3876     ccan[ican]->cd(5); hdedxphi_etan    ->Draw();
3877     ccan[ican]->cd(6); hdedx_phieta ->Draw("colz");
3878     ccan[ican]->cd(); ccan[ican]->Update();
3879     ccan[ican]->Print(OutputFileName.Data());
3880 
3881     //---- dedx vs p (wide, then zoom) -- same dedx expectation curves on both frames
3882     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3883     ccan[ican]->cd(); ccan[ican]->Divide(2,1,0.0001,0.0001);
3884     ccan[ican]->cd(1); gPad->SetLogz(1); hdedxp ->Draw("colz");
3885     for (int ip=0;ip<NPART;ip++){ fdedxexp[ip]->Draw("same"); }
3886     ccan[ican]->cd(2); gPad->SetLogz(1); hdedxpz    ->Draw("colz");
3887     for (int ip=0;ip<NPART;ip++){ fdedxexp[ip]->Draw("same"); }
3888     ccan[ican]->cd(); ccan[ican]->Update();
3889     ccan[ican]->Print(OutputFileName.Data());
3890 
3891     //---- README_PID.md: dE/dx PID QA. Top: dedxKFP vs p with the KFP gates (pi blue, K green, p red, d gray; dashed
3892     //---- = lo/hi), dedxKFP/dedx70s, tagged-proton PID. Bottom: dedxKFP of what became pi, K, p; y vs pt of p.
3893     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3894     ccan[ican]->cd(); ccan[ican]->Divide(4,2,0.0001,0.0001);
3895     {
3896         const int gcol[NGATE]   = {kBlue,kGreen+2,kRed,kGray+2};
3897         auto drawGates  = [&](){
3898             if (doOldPID) return;
3899             for (int ig=0;ig<NGATE;ig++) for (TGraph *g : {gGateLo[ig],gGateHi[ig]}){
3900                 g->SetLineColor(gcol[ig]); g->SetLineStyle(2); g->SetLineWidth(1); g->Draw("L same");
3901             }
3902         };
3903         ccan[ican]->cd(1); gPad->SetLogz(1); hdedxKFPp->Draw("colz"); drawGates();
3904         ccan[ican]->cd(2); gPad->SetLogz(1); hdedxratio2_p->Draw("colz"); hdedxratio_p->SetLineColor(kRed); hdedxratio_p->Draw("same");
3905         ccan[ican]->cd(3); gPad->SetLogz(1); hdedxKFPp_tagged[0][1]->Draw("colz"); drawGates(); // Lambda -> p (true protons)
3906         ccan[ican]->cd(4);
3907         {   //---- fraction of V0-daughter protons (red) and K0s-daughter pions (blue) given the p PID, vs p
3908             TH1D *hn    = hPIDtagged[0]->ProjectionX("hPIDtagged_p_all",1,4);
3909             TH1D *hf    = hPIDtagged[0]->ProjectionX("hPIDtagged_p_asp",3,3);
3910             hf->Divide(hf,hn,1,1,"B"); hf->SetLineColor(kRed); hf->SetMarkerColor(kRed); hf->SetMarkerStyle(20); hf->SetMarkerSize(0.6);
3911             hf->SetTitle("fraction given PID p: V0 protons (red), K^{0}_{S} pions (blue);p (GeV/c);fraction"); hf->SetMinimum(0.); hf->SetMaximum(1.05); hf->Draw("e");
3912             TH1D *hnp   = hPIDtagged[1]->ProjectionX("hPIDtagged_pi_all",1,4);
3913             TH1D *hfp   = hPIDtagged[1]->ProjectionX("hPIDtagged_pi_asp",3,3);
3914             hfp->Divide(hfp,hnp,1,1,"B"); hfp->SetLineColor(kBlue); hfp->SetMarkerColor(kBlue); hfp->SetMarkerStyle(24); hfp->SetMarkerSize(0.6); hfp->Draw("e same");
3915         }
3916         for (int k=0;k<3;k++){ ccan[ican]->cd(5+k); gPad->SetLogz(1); hdedxKFPp_id[k]->Draw("colz"); drawGates(); }
3917         ccan[ican]->cd(8); gPad->SetLogz(1); hypt_id[2]->Draw("colz");
3918         for (double yy : {-Species_yu[2], Species_yu[2]}){ TLine *l = new TLine(yy,0.,yy,2.); l->SetLineColor(kRed); l->SetLineWidth(2); l->Draw(); }
3919         { TLatex *t = new TLatex(0.,1.85,Form("|y| < %.2f (Species_yu)",Species_yu[2])); t->SetTextAlign(21); t->SetTextSize(0.05); t->SetTextColor(kRed); t->Draw(); }
3920     }
3921     ccan[ican]->cd(); ccan[ican]->Update();
3922     ccan[ican]->Print(OutputFileName.Data());
3923 
3924     //---- README_PID.md sec 10.3: the vertex-phi mask, and every track cut in force (so none is forgotten). Before the mask =
3925     //---- AcceptTrackBase (black), after = the accepted tracks (red); gray boxes = the mask windows.
3926     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
3927     ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
3928     {
3929         TH2D *bxy   = (TH2D*)hmask_dcaxy[0]->Clone("pm_bxy");   bxy->Add(hmask_dcaxy[1]);
3930         TH2D *bz    = (TH2D*)hmask_dcaz[0] ->Clone("pm_bz");    bz ->Add(hmask_dcaz[1]);
3931         TH2D *axy   = (TH2D*)hadca_phi[0][0][0]->Clone("pm_axy");   axy->Add(hadca_phi[0][1][0]);
3932         TH2D *az    = (TH2D*)hadca_phi[1][0][0]->Clone("pm_az");    az ->Add(hadca_phi[1][1][0]);
3933         auto meanX  = [&](TH2D *h, const char* nm){
3934             TH1D *m = h->ProjectionX(nm); m->Reset();
3935             for (int ix=1;ix<=h->GetNbinsX();ix++){ double n=0,sy=0,sy2=0;
3936                 for (int iy=1;iy<=h->GetNbinsY()+1;iy++){ double w=h->GetBinContent(ix,iy), y=h->GetYaxis()->GetBinCenter(iy); n+=w; sy+=w*y; sy2+=w*y*y; }
3937                 if (n>1){ double mu=sy/n; m->SetBinContent(ix,mu); m->SetBinError(ix,sqrt(std::max(0.,sy2/n-mu*mu)/n)); } }
3938             return m;
3939         };
3940         auto boxes  = [&](double y0, double y1){
3941             if (!doSiPhiMask) return;
3942             for (int k=0;k<nSiPhiMask;k++){ TBox *b = new TBox(SIPHIMASK_LO[k],y0,SIPHIMASK_HI[k],y1); b->SetFillColorAlpha(kGray,0.35); b->SetLineWidth(0); b->Draw(); }
3943         };
3944         auto pair2  = [&](TH1D *hb, TH1D *ha, const char* ttl){
3945             hb->SetTitle(ttl); hb->SetLineColor(kBlack); ha->SetLineColor(kRed); hb->SetMinimum(0.); hb->SetMaximum(1.15*std::max(hb->GetMaximum(),ha->GetMaximum()));
3946             hb->Draw("hist"); boxes(0.,hb->GetMaximum()); hb->Draw("hist same"); ha->Draw("hist same");
3947         };
3948         ccan[ican]->cd(1);
3949         pair2(bxy->ProjectionX("pm_nb"),axy->ProjectionX("pm_na"),Form("tracks vs vertex #phi: before SiPhiMask (black), accepted (red, also after split removal)%s;vertex #phi (deg);tracks",doSiPhiMask?"":" (mask OFF)"));
3950         ccan[ican]->cd(2);
3951         pair2(meanX(bxy,"pm_mxyb"),meanX(axy,"pm_mxya"),"<|dcaxy|> vs vertex #phi, before (black) / after (red);vertex #phi (deg);<|dcaxy|> (cm)");
3952         ccan[ican]->cd(3);
3953         pair2(meanX(bz,"pm_mzb"),meanX(az,"pm_mza"),"<|dcaz|> vs vertex #phi, before (black) / after (red);vertex #phi (deg);<|dcaz|> (cm)");
3954         ccan[ican]->cd(4); gPad->SetLogz(1);
3955         bxy->SetTitle("|dcaxy| vs vertex #phi, before the mask;vertex #phi (deg);|dcaxy| (cm)"); bxy->GetYaxis()->SetRangeUser(0.,1.0); bxy->Draw("colz");
3956         if (doSiPhiMask) for (int k=0;k<nSiPhiMask;k++) for (double x : {SIPHIMASK_LO[k],SIPHIMASK_HI[k]}){ TLine *l = new TLine(x,0.,x,1.0); l->SetLineColor(kRed); l->SetLineWidth(2); l->SetLineStyle(2); l->Draw(); }
3957         ccan[ican]->cd(5);
3958         TH1D *hall  = hmask_pid->ProjectionX("pm_pall",1,2);
3959         TH1D *hin   = hmask_pid->ProjectionX("pm_pin",2,2);
3960         hin->Divide(hin,hall,1,1,"B");
3961         const char* pl[8]   = {"#pi+","#pi-","K+","K-","p","#bar{p}","none +","none -"};
3962         for (int k=0;k<8;k++) hin->GetXaxis()->SetBinLabel(k+1,pl[k]);
3963         hin->SetTitle("fraction of tracks inside the mask windows, by PID;;fraction"); hin->SetMinimum(0.); hin->SetMaximum(1.3*std::max(0.01,hin->GetMaximum()));
3964         hin->SetMarkerStyle(20); hin->Draw("E1");
3965         ccan[ican]->cd(6);
3966         TPaveText *pt   = new TPaveText(0.03,0.03,0.97,0.97,"NDC"); pt->SetFillColor(0); pt->SetBorderSize(1); pt->SetTextAlign(12); pt->SetTextSize(0.034);
3967         pt->AddText("Track cuts in force (AcceptTrack, PID, pair building)");
3968         pt->AddText(Form("  %.2f < p_{T} < 20.1 GeV/c,  ntpc #geq %d", PTMINCUT, NTPCCUT));
3969         pt->AddText(Form("  |dcaxy| < %.2f cm,  |dcaz| < %.2f cm",valDcaxyCut,valDcazCut));
3970         if (doSiPhiMask){ TString w; for (int k=0;k<nSiPhiMask;k++) w += Form(" [%.0f,%.0f)",SIPHIMASK_LO[k],SIPHIMASK_HI[k]); pt->AddText(Form("  SiPhiMask ON, vertex #phi (deg):%s",w.Data())); }
3971         else pt->AddText("  SiPhiMask OFF (nosiphimask)");
3972         if (doCMMask) pt->AddText(Form("  CM mask ON: w = (#eta+%.3f z_{c}) sign(-z_{c}) in [%.2f,%.2f), z_{c} = zvtx-slice centre",CMMASK_K,valCMMaskLo,valCMMaskHi));
3973         else pt->AddText("  CM mask OFF (nocmmask)");
3974         if (doOldPID) pt->AddText("  PID: legacy (oldpid), #pi = dedx70s < 400");
3975         else pt->AddText(Form("  PID: KFP gates (dedxKFP), #pi>p>K, p not in K, K not in p; p_{max} %.1f/%.1f/%.1f", Species_pmax_pid[0], Species_pmax_pid[2], Species_pmax_pid[1]));
3976         pt->AddText(Form("  pairs: p_{T} min #pi %.2f, K %.2f, p/#bar{p} %.2f GeV/c", Species_ptmin[0], Species_ptmin[1], Species_ptmin[2]));
3977         pt->AddText(doIsoVtx ? Form("  isolated vertices only: no other vertex within #pm%d crossings, |#Deltaz| < %.0f cm (%ld rows skipped)",valIsoNX,valIsoDZ,nSkippedIso) : "  isolated-vertex selection OFF");
3978         pt->AddText(doSegVeto ? Form("  segment veto ON: %ld rows of %zu listed segments skipped",nSkippedSegVeto,segVeto.size()) : "  segment veto OFF");
3979         pt->AddText("  V0 daughters in no pair type"); pt->AddText(Form("  split-track removal %s", doSplitRemoval ? "ON" : "OFF"));
3980         pt->AddText(Form("  looper veto %s (OS, |p|<%.2f, p-sum<%.2f): %ld tracks", doLooperVeto ? "ON" : "OFF", LOOPER_PMAX, valLooperRSum, nFlagged_Looper));
3981         double nin  = hmask_pid->Integral(1,8,2,2), nall = hmask_pid->Integral(1,8,1,2);
3982         pt->AddText(Form("  tracks inside the windows: %.2f%%%s", nall>0 ? 100.*nin/nall : 0., doSiPhiMask ? " (removed)" : " (kept)"));
3983         pt->Draw();
3984     }
3985     ccan[ican]->cd(); ccan[ican]->Update();
3986     ccan[ican]->Print(OutputFileName.Data());
3987 
3988     //---- README_PID.md sec 10: DCA of the identified species (accepted tracks), as ana573/DCAid_ana573.C. Pages 1-2 (dcaxy,
3989     //---- dcaz), per pt slice: shapes of p, pbar, pi+, pi- (unit area, log y); p/pbar vs dca; fraction kept by abs(dca) < X.
3990     for (int iv=0;iv<2;iv++){
3991         ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,900);
3992         ccan[ican]->cd(); ccan[ican]->Divide(4,3,0.0001,0.0001);
3993         const int NS=4; const double pl[NS+1]={0.1,0.25,0.4,0.7,1.0};
3994         const char* var = iv ? "dcaz" : "dcaxy";
3995         const int kk[4]={2,2,0,0}, cc[4]={0,1,0,1}, col[4]={kRed,kBlue,kGray+2,kGreen+2};
3996         const char* lab[4]={"p","#bar{p}","#pi^{+}","#pi^{-}"};
3997         for (int is=0;is<NS;is++){
3998             TH1D *h[4];
3999             for (int j=0;j<4;j++){
4000                 TH2D *m = iv ? hdcaz_id[kk[j]][cc[j]] : hdcaxy_id[kk[j]][cc[j]];
4001                 int b0=m->GetXaxis()->FindBin(pl[is]+1e-6), b1=m->GetXaxis()->FindBin(pl[is+1]-1e-6);
4002                 h[j]    = m->ProjectionY(Form("dcaid_%d_%d_%d",iv,is,j),b0,b1); h[j]->Rebin(2);
4003                 h[j]->SetLineColor(col[j]); h[j]->SetMarkerColor(col[j]);
4004             }
4005             ccan[ican]->cd(1+is); gPad->SetLogy(1);
4006             for (int j=0;j<4;j++){
4007                 TH1D *sh    = (TH1D*)h[j]->Clone(Form("dcaids_%d_%d_%d",iv,is,j)); if (sh->Integral()>0) sh->Scale(1./sh->Integral());
4008                 sh->SetTitle(Form("%s, %.2f<p_{T}<%.2f GeV/c;%s (cm);unit area",var,pl[is],pl[is+1],var)); sh->SetMinimum(1e-6);
4009                 sh->Draw(j?"hist same":"hist");
4010             }
4011             if (is==0){ TLegend *lg = new TLegend(0.62,0.62,0.89,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0);
4012                 for (int j=0;j<4;j++) lg->AddEntry(h[j],lab[j],"l"); lg->Draw(); }
4013             ccan[ican]->cd(1+NS+is);
4014             TH1D *r     = (TH1D*)h[0]->Clone(Form("dcaidr_%d_%d",iv,is)); TH1D *rb = (TH1D*)h[1]->Clone(Form("dcaidrb_%d_%d",iv,is));
4015             r->Rebin(3); rb->Rebin(3); r->Divide(rb); r->SetLineColor(1); r->SetMarkerColor(1); r->SetMarkerStyle(20); r->SetMarkerSize(0.5);
4016             r->SetTitle(Form("p / #bar{p}, %.2f<p_{T}<%.2f;%s (cm);p/#bar{p}",pl[is],pl[is+1],var)); r->SetMinimum(0); r->SetMaximum(20.);
4017             r->Draw("E1");
4018             ccan[ican]->cd(1+2*NS+is);
4019             for (int j=0;j<4;j++){
4020                 TH1D *k = new TH1D(Form("dcaidk_%d_%d_%d",iv,is,j),Form("kept by |%s|<X, %.2f<p_{T}<%.2f;X (cm);fraction kept",var,pl[is],pl[is+1]),75,0,1.5);
4021                 double tot  = h[j]->Integral();
4022                 for (int ib=1;ib<=75 && tot>0;ib++){ double X=k->GetBinCenter(ib); k->SetBinContent(ib,h[j]->Integral(h[j]->FindBin(-X),h[j]->FindBin(X-1e-6))/tot); }
4023                 k->SetLineColor(col[j]); k->SetMinimum(0.5); k->SetMaximum(1.1);
4024                 if (j==0){ k->Draw("E1"); TLine *l1 = new TLine(0.,1.,1.5,1.); l1->SetLineColor(kGray); l1->Draw(); }
4025                 k->Draw("hist same");
4026             }
4027         }
4028         ccan[ican]->cd(); ccan[ican]->Update();
4029         ccan[ican]->Print(OutputFileName.Data());
4030     }
4031     //---- page 3: (eta,phi) maps. Rows pi+, pi- (mean dcaxy, abs(dcaxy), dcaz, abs(dcaz)); row 3 abs(dcaxy), abs(dcaz) of p, pbar
4032     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,900);
4033     ccan[ican]->cd(); ccan[ican]->Divide(4,3,0.0001,0.0001);
4034     {
4035         const int rk[12]={0,0,0,0, 0,0,0,0, 2,2,2,2}, rc[12]={0,0,0,0, 1,1,1,1, 0,1,0,1}, rv[12]={0,1,2,3, 0,1,2,3, 1,1,3,3};
4036         for (int i=0;i<12;i++){
4037             ccan[ican]->cd(i+1); gPad->SetRightMargin(0.16);
4038             TProfile2D *m   = pdca_etaphi_id[rk[i]][rc[i]][rv[i]];
4039             TH2D *h = m->ProjectionXY(Form("dcaidmap_%d",i)); h->SetTitle(m->GetTitle());
4040             for (int ix=1;ix<=h->GetNbinsX();ix++) for (int iy=1;iy<=h->GetNbinsY();iy++) if (m->GetBinEntries(m->GetBin(ix,iy))<(rk[i]==2?20:200)) h->SetBinContent(ix,iy,-99);
4041             h->GetXaxis()->SetRangeUser(-1.2,1.2);
4042             if (rv[i]==0||rv[i]==2){ h->SetMinimum(-0.1); h->SetMaximum(0.1); } else { h->SetMinimum(0.); h->SetMaximum(rv[i]==1?0.3:0.4); }
4043             h->Draw("colz");
4044         }
4045     }
4046     ccan[ican]->cd(); ccan[ican]->Update();
4047     ccan[ican]->Print(OutputFileName.Data());
4048     //---- page 3b (README_PID.md sec 10.6): 1D profiles of the pion (eta,phi) maps, + (red) and - (blue). Row 1 vs eta: signed
4049     //---- dcaxy, signed dcaz, abs(dcaz). Row 2 vs vertex phi: signed dcaxy, signed dcaz; and abs(dcaz) vs abs(dcaxy) per 1-deg
4050     //---- phi bin (hadca_phi, accepted tracks; do they go bad at the same places?)
4051     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4052     ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
4053     {
4054         const int vv[5] = {0,2,3,0,2};
4055         const char* vn[4]   = {"dcaxy","|dcaxy|","dcaz","|dcaz|"};
4056         for (int ip=0;ip<5;ip++){
4057             ccan[ican]->cd(1+ip);
4058             TProfile *pr[2];
4059             for (int q=0;q<2;q++){
4060                 TProfile2D *m   = pdca_etaphi_id[0][q][vv[ip]];
4061                 pr[q]   = (ip<3) ? m->ProfileX(Form("dcaprof_%d_%d",ip,q)) : m->ProfileY(Form("dcaprof_%d_%d",ip,q));
4062                 pr[q]->SetLineColor(q?kBlue:kRed); pr[q]->SetMarkerColor(q?kBlue:kRed); pr[q]->SetMarkerStyle(20); pr[q]->SetMarkerSize(0.5);
4063             }
4064             double lo=1e9,hi=-1e9;
4065             for (int q=0;q<2;q++) for (int i=1;i<=pr[q]->GetNbinsX();i++){ if (pr[q]->GetBinEntries(i)<100) continue; lo=std::min(lo,pr[q]->GetBinContent(i)); hi=std::max(hi,pr[q]->GetBinContent(i)); }
4066             double pd   = 0.2*(hi-lo); if (vv[ip]==3){ lo = 0.; } else { lo -= pd; }
4067             pr[0]->SetMinimum(lo); pr[0]->SetMaximum(hi+pd);
4068             pr[0]->SetTitle(Form("#pi: <%s> vs %s, + (red), - (blue);%s;<%s> (cm)",vn[vv[ip]],ip<3?"#eta":"vertex #phi",ip<3?"#eta":"vertex #phi (rad)",vn[vv[ip]]));
4069             if (ip<3) pr[0]->GetXaxis()->SetRangeUser(-1.2,1.2);
4070             pr[0]->Draw("E1");
4071             if (vv[ip]!=3){ TLine *l = new TLine(pr[0]->GetXaxis()->GetBinLowEdge(pr[0]->GetXaxis()->GetFirst()),0.,pr[0]->GetXaxis()->GetBinUpEdge(pr[0]->GetXaxis()->GetLast()),0.); l->SetLineColor(kGray); l->Draw(); }
4072             pr[0]->Draw("E1 same"); pr[1]->Draw("E1 same");
4073         }
4074         ccan[ican]->cd(6);
4075         TH2D *x = (TH2D*)hadca_phi[0][0][0]->Clone("dcacorr_x"); x->Add(hadca_phi[0][1][0]);
4076         TH2D *z = (TH2D*)hadca_phi[1][0][0]->Clone("dcacorr_z"); z->Add(hadca_phi[1][1][0]);
4077         TProfile *px    = x->ProfileX("dcacorr_px"), *pz = z->ProfileX("dcacorr_pz");
4078         TGraph *g   = new TGraph();
4079         for (int i=1;i<=px->GetNbinsX();i++) if (px->GetBinEntries(i)>100) g->SetPoint(g->GetN(),px->GetBinContent(i),pz->GetBinContent(i));
4080         g->SetTitle(Form("per 1#circ vertex-#phi bin, accepted tracks: r = %.2f;<|dcaxy|> (cm);<|dcaz|> (cm)",g->GetCorrelationFactor()));
4081         g->SetMarkerStyle(20); g->SetMarkerSize(0.4); g->Draw("AP");
4082     }
4083     ccan[ican]->cd(); ccan[ican]->Update();
4084     ccan[ican]->Print(OutputFileName.Data());
4085     //---- page 4 (README_PID.md sec 10.2, as ana573/DCAstrip_ana573.C): accepted tracks, 1 deg in phi, + (red) and - (blue).
4086     //---- Rows: vertex phi, phi at R = 0.55 m. Columns: mean abs(dcaxy), fraction abs(dcaxy) > 0.3 cm, mean abs(dcaz).
4087     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,900);
4088     ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
4089     {
4090         auto meanOf = [&](TH2D *h, const char* nm){ TH1D *m = h->ProjectionX(nm); m->Reset();
4091             for (int ix=1;ix<=h->GetNbinsX();ix++){ double n=0,sy=0,sy2=0;
4092                 for (int iy=1;iy<=h->GetNbinsY()+1;iy++){ double w=h->GetBinContent(ix,iy), y=h->GetYaxis()->GetBinCenter(iy); n+=w; sy+=w*y; sy2+=w*y*y; }
4093                 if (n>1){ double mu=sy/n; m->SetBinContent(ix,mu); m->SetBinError(ix,sqrt(std::max(0.,sy2/n-mu*mu)/n)); } }
4094             return m; };
4095         auto fracAbove  = [&](TH2D *h, double x, const char* nm){ TH1D *m = h->ProjectionX(nm); m->Reset();
4096             int b   = h->GetYaxis()->FindBin(x+1e-6);
4097             for (int ix=1;ix<=h->GetNbinsX();ix++){ double n=h->Integral(ix,ix,1,h->GetNbinsY()+1), a=h->Integral(ix,ix,b,h->GetNbinsY()+1);
4098                 if (n>0){ double f=a/n; m->SetBinContent(ix,f); m->SetBinError(ix,sqrt(f*(1-f)/n)); } }
4099             return m; };
4100         const char* fl[2]   = {"vertex #phi","#phi at R = 0.55 m"};
4101         const char* yt[3]   = {"<|dcaxy|> (cm)","fraction |dcaxy| > 0.3 cm","<|dcaz|> (cm)"};
4102         for (int f=0;f<2;f++){
4103             TH1D *h[3][2];
4104             for (int q=0;q<2;q++){
4105                 h[0][q] = meanOf(hadca_phi[0][q][f],Form("strip_mxy_%d_%d",f,q));
4106                 h[1][q] = fracAbove(hadca_phi[0][q][f],0.3,Form("strip_fxy_%d_%d",f,q));
4107                 h[2][q] = meanOf(hadca_phi[1][q][f],Form("strip_mz_%d_%d",f,q));
4108             }
4109             for (int r=0;r<3;r++){
4110                 ccan[ican]->cd(1+r+3*f);
4111                 for (int q=0;q<2;q++){
4112                     h[r][q]->SetLineColor(q?kBlue:kRed);
4113                     h[r][q]->SetTitle(Form("%s vs %s, accepted: + (red), - (blue);%s (deg);%s",yt[r],fl[f],fl[f],yt[r]));
4114                     h[r][q]->SetMinimum(0); h[r][q]->SetMaximum(1.2*std::max(h[r][0]->GetMaximum(),h[r][1]->GetMaximum()));
4115                     h[r][q]->Draw(q?"hist same":"hist");
4116                 }
4117             }
4118         }
4119     }
4120     ccan[ican]->cd(); ccan[ican]->Update();
4121     ccan[ican]->Print(OutputFileName.Data());
4122 
4123     //---- README_PID.md sec 8: p-pi ULS ridge diagnostic. Pad 1: dphi, p pi- (black) and pbar pi+ (magenta).
4124     //---- Others: p pi- + pbar pi+ summed, ridge box (red) vs its clean mirror (blue), mirror scaled to the ridge's pairs.
4125     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4126     ccan[ican]->cd(); ccan[ican]->Divide(4,2,0.0001,0.0001);
4127     {
4128         ccan[ican]->cd(1); hRidge_dphi[0]->SetLineColor(kBlack); hRidge_dphi[0]->Draw("hist");
4129         hRidge_dphi[1]->SetLineColor(kMagenta+1); hRidge_dphi[1]->Draw("hist same");
4130         TH1D **hs[7]    = {hRidge_Mppi[0],hRidge_Mee[0],hRidge_open[0],hRidge_prat[0],hRidge_skf[0],hRidge_dcap[0],hRidge_dcapi[0]};
4131         TH1D **hm[7]    = {hRidge_Mppi[1],hRidge_Mee[1],hRidge_open[1],hRidge_prat[1],hRidge_skf[1],hRidge_dcap[1],hRidge_dcapi[1]};
4132         for (int k=0;k<7;k++){
4133             ccan[ican]->cd(2+k); if (k>=4) gPad->SetLogy(1);
4134             TH1D *r = (TH1D*)hs[k][0]->Clone(Form("%s_sum",hs[k][0]->GetName())); r->Add(hs[k][1]);
4135             TH1D *m = (TH1D*)hm[k][0]->Clone(Form("%s_sum",hm[k][0]->GetName())); m->Add(hm[k][1]);
4136             r->SetTitle(TString(r->GetTitle()).ReplaceAll("p#pi^{-} ridge","ridge (red) vs mirror (blue)"));
4137             if (m->Integral()>0.) m->Scale(r->Integral()/m->Integral());
4138             r->SetLineColor(kRed); m->SetLineColor(kBlue);
4139             r->SetMaximum(1.15*std::max(r->GetMaximum(),m->GetMaximum()));
4140             r->Draw("hist"); m->Draw("hist same");
4141         }
4142     }
4143     ccan[ican]->cd(); ccan[ican]->Update();
4144     ccan[ican]->Print(OutputFileName.Data());
4145 
4146     //---- README_CQcomparison.md sec 2.1-2.2: V0 daughter sharing / duplicates, and <pT> vs N_ch.
4147     //---- Row 1: in-peak V0 pairs sharing a daughter (counts, of all in-peak pairs in the title); in-peak V0 sharing with
4148     //---- off-peak candidates; nearest list track to the Lambda p / Lbar pbar daughter (dashed: the CQC_DRDUP radius).
4149     //---- Row 2: p-Lambda, pbar-Lbar sibling Q by proton category (dashed: daughter-parent Q = 0.10 GeV); Lambda-Lambda,
4150     //---- Lbar-Lbar sibling Q by daughter category. Row 3: SKF of near daughters; n_Lambda per event vs Poisson; <pT> vs N_ch.
4151     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4152     ccan[ican]->cd(); ccan[ican]->Divide(4,3,0.0001,0.0001);
4153     {
4154         const char* v0lab[3]    = {"K0s","Lambda","Lbar"};
4155         for (int k=1;k<=3;k++){
4156             hV0share->GetXaxis()->SetBinLabel(k,v0lab[k-1]); hV0share->GetYaxis()->SetBinLabel(k,v0lab[k-1]);
4157             hV0shareOff->GetXaxis()->SetBinLabel(k,v0lab[k-1]); hV0shareOff->GetYaxis()->SetBinLabel(k,v0lab[k-1]);
4158         }
4159         gStyle->SetPaintTextFormat(".0f");
4160         ccan[ican]->cd(1);
4161         hV0share->SetTitle(Form("in-peak V0 pairs sharing a daughter: %.0f of %.0f in-peak pairs",hV0share->Integral(),hV0pairs->Integral()));
4162         hV0share->SetMarkerSize(2.0); hV0share->Draw("text");
4163         ccan[ican]->cd(2);
4164         hV0shareOff->SetTitle("in-peak V0 sharing a daughter with an off-peak candidate");
4165         hV0shareOff->SetMarkerSize(2.0); hV0shareOff->Draw("text");
4166         auto vline  = [&](double x){ gPad->Update(); TLine *l = new TLine(x,gPad->GetUymin(),x,gPad->GetUymax());
4167             if (gPad->GetLogy()) l = new TLine(x,pow(10,gPad->GetUymin()),x,pow(10,gPad->GetUymax()));
4168             l->SetLineStyle(2); l->SetLineColor(kGray+2); l->Draw(); };
4169         const int kvq[2][2] = {{1,0},{2,1}};    // Lambda p, Lbar pbar
4170         for (int i=0;i<2;i++){
4171             ccan[ican]->cd(3+i); gPad->SetLogy(1);
4172             TH1D *h = hDauNear_dR[kvq[i][0]][kvq[i][1]];
4173             h->SetLineColor(kBlack); h->Draw("hist"); vline(CQC_DRDUP);
4174         }
4175         const int col[3]    = {kRed, kBlue, kBlack};
4176         for (int a=0;a<2;a++){
4177             ccan[ican]->cd(5+a);
4178             double mx   = 0;
4179             for (int c=0;c<3;c++) mx = std::max(mx,hpLa_Q[a][c]->GetMaximum());
4180             TLegend *lg = new TLegend(0.45,0.62,0.89,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.035);
4181             for (int c=0;c<3;c++){
4182                 hpLa_Q[a][c]->SetLineColor(col[c]); hpLa_Q[a][c]->SetMaximum(1.15*mx); hpLa_Q[a][c]->SetMinimum(0);
4183                 if (c==0) hpLa_Q[a][c]->SetTitle(Form("%s sibling Q, by the (anti)proton",a?"#bar{p}#bar{#Lambda}":"p#Lambda"));
4184                 hpLa_Q[a][c]->Draw(c?"hist same":"hist");
4185                 lg->AddEntry(hpLa_Q[a][c],Form("%s (%.0f)",pLcat[c],hpLa_Q[a][c]->Integral()),"l");
4186             }
4187             vline(0.10); lg->AddEntry((TObject*)0,"dashed: daughter-parent Q","");
4188             lg->Draw();
4189         }
4190         for (int a=0;a<2;a++){
4191             ccan[ican]->cd(7+a); gPad->SetLogy(1);
4192             double mx   = 0;
4193             for (int c=0;c<3;c++) mx = std::max(mx,hLaLa_Q[a][c]->GetMaximum());
4194             TLegend *lg = new TLegend(0.45,0.70,0.89,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.035);
4195             for (int c=0;c<3;c++){
4196                 hLaLa_Q[a][c]->SetLineColor(col[c]); hLaLa_Q[a][c]->SetMaximum(3.*std::max(mx,1.)); hLaLa_Q[a][c]->SetMinimum(0.5);
4197                 if (c==0) hLaLa_Q[a][c]->SetTitle(Form("%s sibling Q, by the daughters",a?"#bar{#Lambda}#bar{#Lambda}":"#Lambda#Lambda"));
4198                 hLaLa_Q[a][c]->Draw(c?"hist same":"hist");
4199                 lg->AddEntry(hLaLa_Q[a][c],Form("%s (%.0f)",LLcat[c],hLaLa_Q[a][c]->Integral()),"l");
4200             }
4201             lg->Draw();
4202         }
4203         ccan[ican]->cd(9);
4204         {
4205             TH1D *h0    = hDauNear_skf[1][0], *h1 = hDauNear_skf[2][1];
4206             h0->SetLineColor(kRed); h1->SetLineColor(kBlue);
4207             h0->SetTitle(Form("SiSplitScore, (anti)proton daughter vs its nearest (#DeltaR<%.2f)",CQC_DRDUP));
4208             h0->SetMaximum(1.15*std::max(1.,std::max(h0->GetMaximum(),h1->GetMaximum()))); h0->SetMinimum(0);
4209             h0->Draw("hist"); h1->Draw("hist same");
4210             TLegend *lg = new TLegend(0.45,0.75,0.89,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.04);
4211             lg->AddEntry(h0,Form("#Lambda: p (%.0f)",h0->Integral()),"l"); lg->AddEntry(h1,Form("#bar{#Lambda}: #bar{p} (%.0f)",h1->Integral()),"l"); lg->Draw();
4212         }
4213         ccan[ican]->cd(10); gPad->SetLogy(1);
4214         {
4215             TLegend *lg = new TLegend(0.40,0.70,0.89,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.04);
4216             const int cl[2] = {kRed, kBlue};
4217             for (int a=0;a<2;a++){
4218                 TH1D *h = hnLApeak[a];
4219                 double N=h->Integral(), mu=(N>0.)?h->GetMean():0.;
4220                 TH1D *pz    = (TH1D*)h->Clone(Form("%s_poisson",h->GetName())); pz->Reset();
4221                 for (int b=1;b<=pz->GetNbinsX();b++) pz->SetBinContent(b,N*TMath::Poisson(b-1,mu));
4222                 h->SetLineColor(cl[a]); pz->SetLineColor(cl[a]); pz->SetLineStyle(2);
4223                 if (a==0){ h->SetTitle("in-peak #Lambda, #bar{#Lambda} per event (dashed: Poisson, same mean)"); h->SetMinimum(0.5); h->SetMaximum(3.*N); }
4224                 h->Draw(a?"hist same":"hist"); pz->Draw("hist same");
4225                 double s2   = 0;
4226                 for (int b=1;b<=h->GetNbinsX();b++){ double n = b-1; s2 += h->GetBinContent(b)*n*(n-1); }
4227                 double f2   = (mu>0.) ? s2/N/(mu*mu) : 0.;
4228                 lg->AddEntry(h,Form("%s: <n(n-1)>/<n>^{2} = %.2f",a?"#bar{#Lambda}":"#Lambda",f2),"l");
4229             }
4230             lg->AddEntry((TObject*)0,"dashed: Poisson (= 1.00)","");
4231             lg->Draw();
4232         }
4233         for (int j=0;j<2;j++){
4234             ccan[ican]->cd(11+j);
4235             TLegend *lg = new TLegend(0.15,0.70,0.60,0.89); lg->SetBorderSize(0); lg->SetFillStyle(0); lg->SetTextSize(0.04);
4236             const int cs[3] = {kRed, kGreen+2, kBlue};
4237             bool first  = true;
4238             for (int k=0;k<3;k++){
4239                 TProfile *h = (j==0) ? hptNch[k][0] : hptNchV0[k];
4240                 if (j==0){ TProfile *hn = hptNch[k][1]; h = (TProfile*)h->Clone(Form("%s_both",h->GetName())); h->Add(hn); }
4241                 h->SetLineColor(cs[k]); h->SetMarkerColor(cs[k]); h->SetMarkerStyle(20); h->SetMarkerSize(0.6);
4242                 h->SetMinimum(0.); h->SetMaximum(j?2.0:1.2);
4243                 if (first){ h->SetTitle(j?"<p_{T}> vs N_{ch}, in-peak V0s in the pair types":"<p_{T}> vs N_{ch}, #pi K p (both charges) in the pair types"); }
4244                 h->GetXaxis()->SetRangeUser(0.,30.);
4245                 h->Draw(first?"pe":"pe same"); first = false;
4246                 lg->AddEntry(h,j?v0nm[k]:idName[k],"lp");
4247             }
4248             if (valNchHi<9999){
4249                 gPad->Update();
4250                 TBox *bx    = new TBox(valNchLo-0.5,gPad->GetUymin(),valNchHi+0.5,gPad->GetUymax());
4251                 bx->SetFillStyle(3354); bx->SetFillColor(kOrange+1); bx->SetLineColor(kOrange+1); bx->Draw();
4252                 lg->AddEntry(bx,Form("this run's class: %d-%d",valNchLo,valNchHi),"f");
4253             }
4254             lg->Draw();
4255         }
4256         gStyle->SetPaintTextFormat("g");
4257     }
4258     ccan[ican]->cd(); ccan[ican]->Update();
4259     ccan[ican]->Print(OutputFileName.Data());
4260 
4261     //---- V0 timing QA
4262     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4263     ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
4264     ccan[ican]->cd(1); hKSxing          ->Draw();
4265     ccan[ican]->cd(2); hLAxing          ->Draw();
4266     ccan[ican]->cd(3); hALxing          ->Draw();
4267     ccan[ican]->cd(4); hKSmass_xing ->Draw("colz");
4268     ccan[ican]->cd(5); hLAmass_xing ->Draw("colz");
4269     ccan[ican]->cd(6); hALmass_xing ->Draw("colz");
4270     ccan[ican]->cd(); ccan[ican]->Update();
4271     ccan[ican]->Print(OutputFileName.Data());
4272 
4273     //---- V0 kinematics, K-short (worse-daughter DCA = offline README-sec-6 diagnostic, not yet cut on)
4274     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4275     ccan[ican]->cd(); ccan[ican]->Divide(4,2,0.0001,0.0001);
4276     ccan[ican]->cd(1); hKSmass      ->Draw();
4277     ccan[ican]->cd(2); hKSpt        ->Draw();
4278     ccan[ican]->cd(3); hKSeta       ->Draw();
4279     ccan[ican]->cd(4); hKSphi       ->Draw();
4280     ccan[ican]->cd(5); hKSdira      ->Draw();
4281     ccan[ican]->cd(6); hKSpvdca ->Draw();
4282     ccan[ican]->cd(7); gPad->SetLogy(1); hKSworsedca->Draw();
4283     ccan[ican]->cd(); ccan[ican]->Update();
4284     ccan[ican]->Print(OutputFileName.Data());
4285 
4286     //---- V0 kinematics, Lambda (mass panel: green = worse-track PV_DCA offline cut applied,
4287     //---- README_settingKFPcuts.md Part4/5 -- LA_WORSEDCA_CUT, see WorseDaughterPVDCA above)
4288     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4289     ccan[ican]->cd(); ccan[ican]->Divide(4,2,0.0001,0.0001);
4290     ccan[ican]->cd(1); hLAmass      ->Draw(); hLAmass_clean->Draw("same");
4291     ccan[ican]->cd(2); hLApt        ->Draw();
4292     ccan[ican]->cd(3); hLAeta       ->Draw();
4293     ccan[ican]->cd(4); hLAphi       ->Draw();
4294     ccan[ican]->cd(5); hLAdira      ->Draw();
4295     ccan[ican]->cd(6); hLApvdca ->Draw();
4296     ccan[ican]->cd(7); gPad->SetLogy(1); hLAworsedca->Draw();
4297     ccan[ican]->cd(); ccan[ican]->Update();
4298     ccan[ican]->Print(OutputFileName.Data());
4299 
4300     //---- V0 kinematics, anti-Lambda (mass panel: green = worse-track PV_DCA offline cut applied,
4301     //---- README_settingKFPcuts.md Part5 ana532 re-derivation -- AL_WORSEDCA_CUT, see WorseDaughterPVDCA above)
4302     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4303     ccan[ican]->cd(); ccan[ican]->Divide(4,2,0.0001,0.0001);
4304     ccan[ican]->cd(1); hALmass      ->Draw(); hALmass_clean->Draw("same");
4305     ccan[ican]->cd(2); hALpt        ->Draw();
4306     ccan[ican]->cd(3); hALeta       ->Draw();
4307     ccan[ican]->cd(4); hALphi       ->Draw();
4308     ccan[ican]->cd(5); hALdira      ->Draw();
4309     ccan[ican]->cd(6); hALpvdca ->Draw();
4310     ccan[ican]->cd(7); gPad->SetLogy(1); hALworsedca->Draw();
4311     ccan[ican]->cd(); ccan[ican]->Update();
4312     ccan[ican]->Print(OutputFileName.Data());
4313 
4314     //---- original V0-daughter dE/dx QA, moved to the very end (post-processing, MILES away for now)...
4315     //
4316     //---- gated dedx vs p
4317     ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4318     ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
4319     for (int ipar=0;ipar<3;ipar++){
4320         for (int ichg=0;ichg<2;ichg++){
4321             double ymax=2000.;
4322             if (ipar==1&&ichg==0) ymax=4000;
4323             if (ipar==2&&ichg==1) ymax=4000;
4324             int ipad    = 3*ichg + ipar + 1;
4325             ccan[ican]  ->cd(ipad);
4326             gPad        ->SetLogz(1);
4327             hdedxp_tagged[ichg][ipar]->GetYaxis()->SetRangeUser(0.,ymax);
4328             hdedxp_tagged[ichg][ipar]->Draw("colz");
4329             for (int ip=0;ip<NPART;ip++){
4330                 fdedxexp[ip]->Draw("same");
4331             }
4332         }
4333     }
4334     ccan[ican]->cd(); ccan[ican]->Update();
4335     ccan[ican]->Print(OutputFileName.Data());
4336 
4337 
4338     //---- README_CQ: pion C(Q) summary page, 1x2: pi+pi- (black), pi+pi+ (red), pi-pi- (blue) C(Q),
4339     //---- 5 MeV bins, raw levels (per-event norm, i.e. the 1+R2 baseline). Left 0-2.0 GeV with the
4340     //---- pi+pi- decay signposts (the K0s line validates dq itself), right 0-0.3 GeV.
4341     if (!NOCORRELATIONS){
4342         int ipcq[3]     = {-1,-1,-1};       // pi+pi-, pi+pi+, pi-pi-
4343         for (int ipaty=0;ipaty<NPairTypes;ipaty++){
4344             int a   = PairTypes_Info[ipaty][0], b = PairTypes_Info[ipaty][1];
4345             if (a==kParticleIDPionPlus  && b==kParticleIDPionMinus && ipcq[0]<0) ipcq[0] = ipaty;
4346             if (a==kParticleIDPionPlus  && b==kParticleIDPionPlus  && ipcq[1]<0) ipcq[1] = ipaty;
4347             if (a==kParticleIDPionMinus && b==kParticleIDPionMinus && ipcq[2]<0) ipcq[2] = ipaty;
4348         }
4349         if (ipcq[0]>=0 && ipcq[1]>=0 && ipcq[2]>=0){
4350             ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4351             ccan[ican]->cd(); ccan[ican]->Divide(2,1,0.0001,0.0001);
4352             const int   pcol[3]     = {kBlack,kRed,kBlue};
4353             const char* plab[3]     = {"#pi^{+}#pi^{-}","#pi^{+}#pi^{+}","#pi^{-}#pi^{-}"};
4354             for (int ipad=0;ipad<2;ipad++){
4355                 ccan[ican]->cd(ipad+1);
4356                 double xmax = (ipad==0) ? 2.0 : 0.3;
4357                 TH1D *hp[3];
4358                 double ymn=1e30, ymx=-1e30;
4359                 for (int k=0;k<3;k++){
4360                     hp[k]   = (TH1D*)hCQ[ipcq[k]]->Clone(Form("hCQpions_%d_%d",ipad,k));
4361                     hp[k]->SetDirectory(0);
4362                     hp[k]->Rebin(5); hp[k]->Scale(1./5.);
4363                     hp[k]->SetLineColor(pcol[k]); hp[k]->SetMarkerColor(pcol[k]);
4364                     hp[k]->GetXaxis()->SetRangeUser(0.,xmax);
4365                     for (int ib=hp[k]->FindBin(0.0201);ib<=hp[k]->GetXaxis()->GetLast();ib++){  // display range from Q>20 MeV
4366                         if (hp[k]->GetBinError(ib)<=0.) continue;
4367                         double v    = hp[k]->GetBinContent(ib);
4368                         ymn = std::min(ymn,v); ymx = std::max(ymx,v);
4369                     }
4370                 }
4371                 hp[0]->SetTitle(Form("pion C(Q), 5 MeV bins, raw level;Q_{inv} (GeV);C(Q)"));
4372                 if (ymx>ymn){ hp[0]->SetMinimum(ymn-0.05*(ymx-ymn)); hp[0]->SetMaximum(ymx+0.25*(ymx-ymn)); }
4373                 hp[0]->Draw("hist");
4374                 for (int k=1;k<3;k++) hp[k]->Draw("hist same");
4375                 gPad->Update();
4376                 DrawCQSignposts(kParticleIDPionPlus,kParticleIDPionMinus,gPad->GetUymin(),gPad->GetUymax(),xmax);
4377                 TLegend *lgp    = new TLegend(0.72,0.40,0.89,0.58);
4378                 lgp->SetBorderSize(0); lgp->SetFillStyle(0); lgp->SetTextSize(0.04);
4379                 for (int k=0;k<3;k++){
4380                     TLegendEntry *lep   = lgp->AddEntry(hp[k],plab[k],"l");
4381                     lep->SetLineColor(pcol[k]); lep->SetLineWidth(2); lep->SetTextColor(pcol[k]);   // legend line colors don't survive the .ps
4382                 }
4383                 lgp->Draw();
4384             }
4385             ccan[ican]->cd(); ccan[ican]->Update();
4386             ccan[ican]->Print(OutputFileName.Data());
4387         }
4388     }
4389 
4390     //---- CF summary pages, one 4x3 page per PairTypes_Info entry, in order.
4391     //---- Row1: N/evt (+text), R2(dy), R2(dphi), [v0-species-1 mass, if either leg is a v0]
4392     //---- Row2: rho2(S)/rho2(M)/R2 vs (y1,y2), [v0-species-2 mass, if a 2nd distinct v0 species]
4393     //---- Row3: rho2(S)/rho2(M)/R2 vs (dy,dphi), R2(dy,dphi) again as colz
4394     if (!NOCORRELATIONS){
4395         for (int ipaty=0;ipaty<NPairTypes;ipaty++){
4396             int ipid1           = PairTypes_Info[ipaty][0];
4397             int ipid2           = PairTypes_Info[ipaty][1];
4398             TString part1name   = TString(ParticleIDNames[ipid1]);
4399             TString part2name   = TString(ParticleIDNames[ipid2]);
4400             //
4401             //---- individual v0-species mass panels (the v0's OWN reconstructed mass, e.g.
4402             //---- hKSmass from v0mass -- NOT CalcRm's Minv, which is the combined PAIR's
4403             //---- invariant mass and isn't meaningful as "the v0 mass" here). Up to 2 distinct
4404             //---- v0 species can appear in one pairtype (e.g. KS-LA); pick them up in order.
4405             const int NV0MASSPAD            = 2;
4406             TH1D* hV0MassPad[NV0MASSPAD]    = {0,0};
4407             TH1D* hV0MassPadClean[NV0MASSPAD]   = {0,0};
4408             int nv0MassPad  = 0;
4409             for (int ip=0;ip<2;ip++){
4410                 int ipid    = (ip==0) ? ipid1 : ipid2;
4411                 bool dupe   = (ip==1 && ipid2==ipid1);
4412                 if (dupe) continue;
4413                 if      (ipid==kParticleIDKshort    && nv0MassPad<NV0MASSPAD){ hV0MassPad[nv0MassPad]=hKSmass; ++nv0MassPad; }
4414                 else if (ipid==kParticleIDLambda     && nv0MassPad<NV0MASSPAD){ hV0MassPad[nv0MassPad]=hLAmass; hV0MassPadClean[nv0MassPad]=hLAmass_clean; ++nv0MassPad; }
4415                 else if (ipid==kParticleIDAntiLambda && nv0MassPad<NV0MASSPAD){ hV0MassPad[nv0MassPad]=hALmass; hV0MassPadClean[nv0MassPad]=hALmass_clean; ++nv0MassPad; }
4416             }
4417             //
4418             double nevt     = hmult[ipaty]  ->GetEntries();
4419             double meanN1   = hmult1[ipaty] ->GetMean();
4420             double meanN2   = hmult2[ipaty] ->GetMean();
4421             double intN1    = hy1[ipaty]    ->Integral();
4422             double intN2    = hy2[ipaty]    ->Integral();
4423             //
4424             ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4425             ccan[ican]->cd(); ccan[ican]->Divide(4,3,0.0001,0.0001);
4426             //---- row 1...
4427             ccan[ican]->cd(1); gPad->SetLogy(1); hmult1[ipaty]->Draw();
4428             ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.06); text[itext]->DrawLatex(0.88,0.85,Form("N_{evt}=%.0f"        ,nevt  ));
4429             ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.06); text[itext]->DrawLatex(0.88,0.78,Form("<N_{1}>=%.2f"        ,meanN1));
4430             ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.06); text[itext]->DrawLatex(0.88,0.71,Form("INT #rho_{1}(1)=%.2f",intN1 ));
4431             ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.06); text[itext]->DrawLatex(0.88,0.60,Form("<N_{2}>=%.2f"        ,meanN2));
4432             ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.06); text[itext]->DrawLatex(0.88,0.53,Form("INT #rho_{1}(2)=%.2f",intN2 ));
4433             ccan[ican]->cd(2); hR2yydy[ipaty]           ->Draw();
4434             ccan[ican]->cd(3); hR2dphi[ipaty]           ->Draw();
4435             if (nv0MassPad>0){
4436                 ccan[ican]->cd(4); hV0MassPad[0]->Draw(); if (hV0MassPadClean[0]) hV0MassPadClean[0]->Draw("same");
4437             }
4438             //---- row 2...
4439             ccan[ican]->cd(5); hrho2[0][ipaty]          ->Draw("surf3");
4440             ccan[ican]->cd(6); hrho1rho1[0][ipaty]      ->Draw("surf3");
4441             ccan[ican]->cd(7); hR2[0][ipaty]            ->Draw("lego2");
4442             if (nv0MassPad>1){
4443                 ccan[ican]->cd(8); hV0MassPad[1]->Draw(); if (hV0MassPadClean[1]) hV0MassPadClean[1]->Draw("same");
4444             }
4445             //---- row 3...
4446             ccan[ican]->cd(9);  hrho2[1][ipaty]     ->Draw("surf3");
4447             ccan[ican]->cd(10); hrho1rho1[1][ipaty] ->Draw("surf3");
4448             ccan[ican]->cd(11); hR2[1][ipaty]           ->Draw("lego2");
4449             ccan[ican]->cd(12); hR2[1][ipaty]           ->Draw("colz");
4450             ccan[ican]->cd(); ccan[ican]->Update();
4451             EtaLabelsIf(ccan[ican],ipaty); ccan[ican]->Print(OutputFileName.Data());
4452             //
4453             //---- femtoscopic C(Q) page, 2x2
4454             //---- (README_CQ): C(Q) 0-2.0 GeV with decay signposts | C(Q) in STAR's 4 kT bins |
4455             //---- C(Q) 0-0.3 GeV zoom | Minv near threshold. Drawn from clones, the written hists keep full range.
4456             ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4457             ccan[ican]->cd(); ccan[ican]->Divide(2,2,0.0001,0.0001);
4458             ccan[ican]->cd(1);
4459             {
4460                 TH1D *hf    = (TH1D*)hCQ[ipaty]->Clone(Form("hCQfull_%d",ipaty));
4461                 hf->SetDirectory(0);
4462                 hf->Rebin(5); hf->Scale(1./5.);         // 5 MeV for display
4463                 hf->SetTitle(Form("%s (5 MeV bins)",hCQ[ipaty]->GetTitle()));
4464                 double ymn=1e30, ymx=-1e30;         // display range from Q>20 MeV (lowest bins are noisy)
4465                 for (int ib=hf->FindBin(0.0201);ib<=hf->GetNbinsX();ib++){
4466                     if (hf->GetBinError(ib)<=0.) continue;
4467                     ymn = std::min(ymn,hf->GetBinContent(ib)); ymx = std::max(ymx,hf->GetBinContent(ib));
4468                 }
4469                 if (ymx>ymn){ hf->SetMinimum(ymn-0.05*(ymx-ymn)); hf->SetMaximum(ymx+0.25*(ymx-ymn)); }
4470                 hf->Draw();
4471                 gPad->Update();
4472                 DrawCQSignposts(ipid1,ipid2,gPad->GetUymin(),gPad->GetUymax(),hf->GetXaxis()->GetXmax());
4473             }
4474             ccan[ican]->cd(2);
4475             {
4476                 const int kcol[4]   = {kBlue,kGreen+2,kOrange+1,kRed};
4477                 TH1D *hk[4];
4478                 double ymn=1e30, ymx=-1e30;
4479                 for (int ik=0;ik<4;ik++){
4480                     hk[ik]  = hCQKT[ipaty]->ProjectionX(Form("hCQKTpx_%d_%d",ipaty,ik),ik+1,ik+1,"e");
4481                     hk[ik]->SetDirectory(0);
4482                     hk[ik]->Rebin(5); hk[ik]->Scale(1./5.);     // 10 MeV for display
4483                     hk[ik]->SetLineColor(kcol[ik]); hk[ik]->SetMarkerColor(kcol[ik]);
4484                     for (int ib=hk[ik]->FindBin(0.0201);ib<=hk[ik]->GetNbinsX();ib++){  // display range from Q>20 MeV
4485                         double v    = hk[ik]->GetBinContent(ib);
4486                         if (hk[ik]->GetBinError(ib)<=0.) continue;
4487                         ymn = std::min(ymn,v); ymx = std::max(ymx,v);
4488                     }
4489                 }
4490                 hk[0]->SetTitle(Form("%s%s, C(Q) in k_{T} bins (10 MeV bins);Q_{inv} (GeV)",part1name.Data(),part2name.Data()));
4491                 if (ymx>ymn){ hk[0]->SetMinimum(ymn-0.05*(ymx-ymn)); hk[0]->SetMaximum(ymx+0.05*(ymx-ymn)); }
4492                 hk[0]->Draw("hist");
4493                 for (int ik=1;ik<4;ik++) hk[ik]->Draw("hist same");
4494                 TLegend *lgk    = new TLegend(0.55,0.65,0.89,0.89);
4495                 lgk->SetBorderSize(0); lgk->SetFillStyle(0); lgk->SetTextSize(0.035);
4496                 for (int ik=0;ik<4;ik++){
4497                     double k1   = hCQKT[ipaty]->GetYaxis()->GetBinLowEdge(ik+1);
4498                     double k2   = hCQKT[ipaty]->GetYaxis()->GetBinUpEdge(ik+1);
4499                     TLegendEntry *lek   = lgk->AddEntry(hk[ik],Form("%.2f<k_{T}<%.2f GeV",k1,k2),"l");
4500                     lek->SetLineColor(kcol[ik]); lek->SetLineWidth(2); lek->SetTextColor(kcol[ik]); // legend line colors don't survive the .ps
4501                 }
4502                 lgk->Draw();
4503             }
4504             ccan[ican]->cd(3);
4505             {
4506                 TH1D *hz    = (TH1D*)hCQ[ipaty]->Clone(Form("hCQzoom_%d",ipaty));
4507                 hz->SetDirectory(0);
4508                 hz->GetXaxis()->SetRangeUser(0.,0.3);
4509                 hz->SetTitle(Form("%s (1 MeV bins, zoom)",hCQ[ipaty]->GetTitle()));
4510                 double ymn=1e30, ymx=-1e30;         // display range from Q>20 MeV (lowest bins are noisy)
4511                 for (int ib=hz->FindBin(0.0201);ib<=hz->FindBin(0.2999);ib++){
4512                     if (hz->GetBinError(ib)<=0.) continue;
4513                     ymn = std::min(ymn,hz->GetBinContent(ib)); ymx = std::max(ymx,hz->GetBinContent(ib));
4514                 }
4515                 if (ymx>ymn){ hz->SetMinimum(ymn-0.05*(ymx-ymn)); hz->SetMaximum(ymx+0.05*(ymx-ymn)); }
4516                 hz->Draw();
4517             }
4518             //---- pad 4: invariant mass near threshold, sibling (green) vs mixed (blue). Mixed is a
4519             //---- raw-count shape reference, scaled to the sibling integral over the top 150 MeV of the
4520             //---- range -- not expected to match the sibling background exactly.
4521             ccan[ican]->cd(4);
4522             {
4523                 TH1D *ms    = hMinvFS[ipaty];
4524                 TH1D *mm    = (TH1D*)hMinvFM[ipaty]->Clone(Form("hMinvFMdraw_%d",ipaty));
4525                 mm->SetDirectory(0);
4526                 int b1      = ms->GetXaxis()->FindBin(ms->GetXaxis()->GetXmax()-0.150+1e-6);
4527                 int b2      = ms->GetNbinsX();
4528                 double im   = mm->Integral(b1,b2);
4529                 double fmix = (im>0.) ? ms->Integral(b1,b2)/im : 0.;    // mixed is scaled; sibling is raw
4530                 if (fmix>0.) mm->Scale(fmix);
4531                 ms->SetLineColor(kGreen+2); ms->SetLineWidth(1);
4532                 mm->SetLineColor(kBlue);    mm->SetLineWidth(1);
4533                 ms->SetMinimum(0.);
4534                 ms->SetMaximum(1.1*std::max(ms->GetMaximum(),mm->GetMaximum()));
4535                 ms->Draw("hist");
4536                 mm->Draw("hist same");
4537                 TLegend *lgm    = new TLegend(0.30,0.11,0.89,0.21);
4538                 lgm->SetBorderSize(0); lgm->SetFillColor(kWhite); lgm->SetTextSize(0.030);
4539                 TLegendEntry *le1   = lgm->AddEntry(ms,"sibling (raw counts)","l");
4540                 le1->SetLineColor(kGreen+2); le1->SetLineWidth(1);
4541                 TLegendEntry *le2   = lgm->AddEntry(mm,Form("mixed (per event) #times %.4g (sib/mix, top 150 MeV)",fmix),"l");
4542                 le2->SetLineColor(kBlue); le2->SetLineWidth(1);
4543                 lgm->Draw();
4544             }
4545             ccan[ican]->cd(); ccan[ican]->Update();
4546             EtaLabelsIf(ccan[ican],ipaty); ccan[ican]->Print(OutputFileName.Data());
4547             //
4548             //---- ir2=2 (Q_inv, pT_pair) page, 2x2 (README_meanpT_from_CQ.md sec 11; QPTPage.h, same page in Finalize)
4549             ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4550             DrawQPTPage(ccan[ican],hrho2[2][ipaty],hrho1rho1[2][ipaty],0,ipid1,ipid2,part1name+part2name,"zvtx-averaged #rho_{2}",Form("corral_%d",ipaty));
4551             EtaLabelsIf(ccan[ican],ipaty); ccan[ican]->Print(OutputFileName.Data());
4552             //
4553             //---- crossing-correction page (README_Crossing decision 12, step 9), 3x2:
4554             //---- column 1: R2(dy,dphi) uncorrected (colz, red perimeter around dirty-side near bins that differ
4555             //---- from their dphi mirror by >3 sigma, either sign) over R2C(dy,dphi) crossing-corrected (colz).
4556             //---- columns 2,3: R2(dy) (top row) and R2(dphi) (bottom row) projected from the Zvtx-averaged 2D
4557             //---- maps, whole range (col 2: uncorrected black, corrected red; R2(dy) also R2yy(dy) from
4558             //---- R2(y1,y2) in green) | narrow window (col 3: uncorrected blue, corrected magenta).
4559             //---- Valid bin <=> Zvtx-averaged rho2(M)>0 (for R2C: its source bin(s), sec 0).
4560             //---- Projections: mean over valid bins, error sqrt(sum e^2)/n. Drawn from clones only.
4561             if (R[ipaty]->GetDoCrossing()){
4562                 TH2D *hu    = (TH2D*)hR2[1][ipaty]->Clone(Form("hR2draw_%d",ipaty));    hu->SetDirectory(0);
4563                 TH2D *hc    = (TH2D*)hR2C[ipaty]->Clone(Form("hR2Cdraw_%d",ipaty)); hc->SetDirectory(0);
4564                 TH2D *hm    = hrho1rho1[1][ipaty];
4565                 int ds      = R[ipaty]->GetDirtySide();
4566                 int nx      = hu->GetNbinsX();
4567                 int ny      = hu->GetNbinsY();
4568                 double lo   = hu->GetYaxis()->GetXmin();
4569                 //---- dphi mirror bin (same rule as CrossingCorrect.h) and wrapped bin center
4570                 std::vector<int>    jmir(ny+1,0);
4571                 std::vector<double> cwr(ny+1,0.);
4572                 for (int iy=1;iy<=ny;iy++){
4573                     double c    = hu->GetYaxis()->GetBinCenter(iy);
4574                     while (c>= 180.) c -= 360.;
4575                     while (c< -180.) c += 360.;
4576                     cwr[iy]     = c;
4577                     double cm   = -c;
4578                     while (cm<  lo      ) cm += 360.;
4579                     while (cm>= lo+360. ) cm -= 360.;
4580                     jmir[iy]    = hu->GetYaxis()->FindFixBin(cm);
4581                 }
4582                 auto okU    = [&](int ix,int iy){ return hm->GetBinContent(ix,iy)>0.; };
4583                 auto okC    = [&](int ix,int iy){
4584                     if (ds==0) return okU(ix,iy);                                                           // not pt-ordered: R2C = R2
4585                     if (fabs(cwr[iy])<90.) return okU(ix, (cwr[iy]*ds>0.) ? jmir[iy] : iy);         // near side: the clean source
4586                     return okU(ix,iy) || okU(ix,jmir[iy]);                                          // away side: either partner
4587                 };
4588                 //---- common z range over valid bins of both maps
4589                 double zmin =  1e30, zmax = -1e30;
4590                 for (int ix=1;ix<=nx;ix++) for (int iy=1;iy<=ny;iy++){
4591                     if (okU(ix,iy)){ zmin = std::min(zmin,hu->GetBinContent(ix,iy)); zmax = std::max(zmax,hu->GetBinContent(ix,iy)); }
4592                     if (okC(ix,iy)){ zmin = std::min(zmin,hc->GetBinContent(ix,iy)); zmax = std::max(zmax,hc->GetBinContent(ix,iy)); }
4593                 }
4594                 if (zmin<zmax){ hu->SetMinimum(zmin); hu->SetMaximum(zmax); hc->SetMinimum(zmin); hc->SetMaximum(zmax); }
4595                 //---- flagged dirty-side near bins: |R2 - R2(mirror)| > 3 sigma
4596                 std::vector<std::vector<bool>> flag(nx+2,std::vector<bool>(ny+2,false));
4597                 int nflag   = 0;
4598                 if (ds!=0){
4599                     for (int ix=1;ix<=nx;ix++) for (int iy=1;iy<=ny;iy++){
4600                         if (!(fabs(cwr[iy])<90. && cwr[iy]*ds>0.)) continue;
4601                         int jy  = jmir[iy];
4602                         if (!okU(ix,iy) || !okU(ix,jy)) continue;
4603                         double d    = hu->GetBinContent(ix,iy) - hu->GetBinContent(ix,jy);
4604                         double e    = sqrt(pow(hu->GetBinError(ix,iy),2) + pow(hu->GetBinError(ix,jy),2));
4605                         if (e>0. && fabs(d)>3.*e){ flag[ix][iy] = true; ++nflag; }
4606                     }
4607                 }
4608                 //---- projections from the 2D maps
4609                 TH1D *hpu[2], *hpc[2];
4610                 hpu[0]  = new TH1D(Form("hR2dyU_%d",ipaty)  ,Form("%s%s, R_{2}(dy) from R_{2}(dy,d#phi);dy;R_{2}"            ,part1name.Data(),part2name.Data()),nx,hu->GetXaxis()->GetXmin(),hu->GetXaxis()->GetXmax());
4611                 hpu[1]  = new TH1D(Form("hR2dphiU_%d",ipaty),Form("%s%s, R_{2}(d#phi) from R_{2}(dy,d#phi);d#phi (deg);R_{2}",part1name.Data(),part2name.Data()),ny,lo,hu->GetYaxis()->GetXmax());
4612                 hpc[0]  = (TH1D*)hpu[0]->Clone(Form("hR2dyC_%d",ipaty));
4613                 hpc[1]  = (TH1D*)hpu[1]->Clone(Form("hR2dphiC_%d",ipaty));
4614                 for (int k=0;k<2;k++){ hpu[k]->SetDirectory(0); hpc[k]->SetDirectory(0); }
4615                 for (int ip=0;ip<2;ip++){
4616                     TH2D *h2    = (ip==0) ? hu : hc;
4617                     TH1D **hp   = (ip==0) ? hpu : hpc;
4618                     for (int k=0;k<2;k++){
4619                         int nb  = (k==0) ? nx : ny;
4620                         for (int ib=1;ib<=nb;ib++){
4621                             double v=0, e2=0; int n=0;
4622                             int nb2 = (k==0) ? ny : nx;
4623                             for (int jb=1;jb<=nb2;jb++){
4624                                 int ix  = (k==0) ? ib : jb;
4625                                 int iy  = (k==0) ? jb : ib;
4626                                 bool ok = (ip==0) ? okU(ix,iy) : okC(ix,iy);
4627                                 if (!ok) continue;
4628                                 v   += h2->GetBinContent(ix,iy);
4629                                 e2  += pow(h2->GetBinError(ix,iy),2);
4630                                 ++n;
4631                             }
4632                             if (n>0){ hp[k]->SetBinContent(ib,v/n); hp[k]->SetBinError(ib,sqrt(e2)/n); }
4633                         }
4634                     }
4635                 }
4636                 //---- narrow-window projections: the all-range unweighted means dilute the crossing damage, which
4637                 //---- sits at |dy|<~0.2 and on the near side (README_Crossing sec 9).
4638                 //---- hnu/hnc[0] = R2(dy)   over near-side |dphi|<DPHICUTPAGE (dphi bin centers)
4639                 //---- hnu/hnc[1] = R2(dphi) over |dy|<DYCUTPAGE (dy bin centers)
4640                 //---- Both cuts widen to 0.51 x bin width, so the wide V0 bins still keep their central bin(s).
4641                 const double DYCUTPAGE      = 0.1;
4642                 const double DPHICUTPAGE    = 30.;
4643                 double dycut    = std::max(DYCUTPAGE,   0.51*hu->GetXaxis()->GetBinWidth(1));
4644                 double dphicut  = std::max(DPHICUTPAGE, 0.51*hu->GetYaxis()->GetBinWidth(1));
4645                 TH1D *hnu[2], *hnc[2];
4646                 for (int k=0;k<2;k++){
4647                     hnu[k]  = (TH1D*)hpu[k]->Clone(Form("hR2n%dU_%d",k,ipaty)); hnu[k]->Reset(); hnu[k]->SetDirectory(0);
4648                     hnc[k]  = (TH1D*)hpu[k]->Clone(Form("hR2n%dC_%d",k,ipaty)); hnc[k]->Reset(); hnc[k]->SetDirectory(0);
4649                 }
4650                 for (int ip=0;ip<2;ip++){
4651                     TH2D *h2    = (ip==0) ? hu : hc;
4652                     for (int k=0;k<2;k++){
4653                         TH1D *hn    = (ip==0) ? hnu[k] : hnc[k];
4654                         int nb      = (k==0) ? nx : ny;
4655                         for (int ib=1;ib<=nb;ib++){
4656                             double v=0, e2=0; int n=0;
4657                             int nb2 = (k==0) ? ny : nx;
4658                             for (int jb=1;jb<=nb2;jb++){
4659                                 int ix  = (k==0) ? ib : jb;
4660                                 int iy  = (k==0) ? jb : ib;
4661                                 if (k==0 && fabs(cwr[iy])>=dphicut) continue;
4662                                 if (k==1 && fabs(hu->GetXaxis()->GetBinCenter(ix))>=dycut) continue;
4663                                 bool ok = (ip==0) ? okU(ix,iy) : okC(ix,iy);
4664                                 if (!ok) continue;
4665                                 v   += h2->GetBinContent(ix,iy);
4666                                 e2  += pow(h2->GetBinError(ix,iy),2);
4667                                 ++n;
4668                             }
4669                             if (n>0){ hn->SetBinContent(ib,v/n); hn->SetBinError(ib,sqrt(e2)/n); }
4670                         }
4671                     }
4672                 }
4673                 //
4674                 ++ican; ccan[ican]  = new TCanvas(Form("ccan%d",ican),Form("ccan%d",ican),ican*30,30+ican*30,1200,800);
4675                 ccan[ican]->cd(); ccan[ican]->Divide(3,2,0.0001,0.0001);
4676                 ccan[ican]->cd(1); gPad->SetRightMargin(0.14);
4677                 hu->SetTitle(Form("%s%s, R_{2}(dy,d#phi) uncorrected;dy;d#phi (deg)",part1name.Data(),part2name.Data()));
4678                 hu->Draw("colz");
4679                 for (int ix=1;ix<=nx;ix++) for (int iy=1;iy<=ny;iy++){
4680                     if (!flag[ix][iy]) continue;
4681                     double x1   = hu->GetXaxis()->GetBinLowEdge(ix), x2 = hu->GetXaxis()->GetBinUpEdge(ix);
4682                     double y1   = hu->GetYaxis()->GetBinLowEdge(iy), y2 = hu->GetYaxis()->GetBinUpEdge(iy);
4683                     TLine *ln[4]    = {0,0,0,0};
4684                     if (!flag[ix-1][iy]) ln[0] = new TLine(x1,y1,x1,y2);
4685                     if (!flag[ix+1][iy]) ln[1] = new TLine(x2,y1,x2,y2);
4686                     if (!flag[ix][iy-1]) ln[2] = new TLine(x1,y1,x2,y1);
4687                     if (!flag[ix][iy+1]) ln[3] = new TLine(x1,y2,x2,y2);
4688                     for (int k=0;k<4;k++) if (ln[k]){ ln[k]->SetLineColor(kRed); ln[k]->SetLineWidth(2); ln[k]->Draw(); }
4689                 }
4690                 ++itext; text[itext]->SetTextFont(42); text[itext]->SetTextSize(0.035); text[itext]->SetTextColor(kRed); text[itext]->SetTextAlign(11);
4691                 text[itext]->DrawLatex(0.02,0.015,Form("red: dirty |R_{2}-mirror|>3#sigma, %d bins (d#phi%s0)",nflag,ds>0?">":"<"));
4692                 ccan[ican]->cd(4); gPad->SetRightMargin(0.14);
4693                 hc->SetTitle(Form("%s%s, R_{2}(dy,d#phi) crossing-corrected;dy;d#phi (deg)",part1name.Data(),part2name.Data()));
4694                 hc->Draw("colz");
4695                 //---- R2yy(dy) from R2(y1,y2) (CalcRm, Zvtx-averaged): no dphi, so whole range only, not crossing-correctable
4696                 TH1D *hyy   = (TH1D*)hR2yydy[ipaty]->Clone(Form("hR2yydydraw_%d",ipaty));   hyy->SetDirectory(0);
4697                 hyy->SetLineColor(kGreen+2); hyy->SetMarkerColor(kGreen+2); hyy->SetMarkerStyle(21); hyy->SetMarkerSize(0.5);
4698                 for (int k=0;k<2;k++){
4699                     hpu[k]->SetLineColor(kBlack);   hpu[k]->SetMarkerColor(kBlack);   hpu[k]->SetMarkerStyle(20); hpu[k]->SetMarkerSize(0.5);
4700                     hpc[k]->SetLineColor(kRed);     hpc[k]->SetMarkerColor(kRed);     hpc[k]->SetMarkerStyle(24); hpc[k]->SetMarkerSize(0.5);
4701                     hnu[k]->SetLineColor(kBlue);    hnu[k]->SetMarkerColor(kBlue);    hnu[k]->SetMarkerStyle(20); hnu[k]->SetMarkerSize(0.5);
4702                     hnc[k]->SetLineColor(kMagenta); hnc[k]->SetMarkerColor(kMagenta); hnc[k]->SetMarkerStyle(24); hnc[k]->SetMarkerSize(0.5);
4703                     TString cutlab  = (k==0) ? Form("|d#phi|<%.0f#circ",dphicut) : Form("|dy|<%.2g",dycut);
4704                     //---- iw=0: whole range (col 2), iw=1: narrow window (col 3); own y range per frame
4705                     for (int iw=0;iw<2;iw++){
4706                         ccan[ican]->cd(2+3*k+iw);
4707                         TH1D *ha    = (iw==0) ? hpu[k] : hnu[k];
4708                         TH1D *hb    = (iw==0) ? hpc[k] : hnc[k];
4709                         bool doyy   = (iw==0 && k==0);
4710                         double pmin = std::min(ha->GetMinimum(), hb->GetMinimum());
4711                         double pmax = std::max(ha->GetMaximum(), hb->GetMaximum());
4712                         if (doyy){ pmin = std::min(pmin,hyy->GetMinimum()); pmax = std::max(pmax,hyy->GetMaximum()); }
4713                         if (iw==1) ha->SetTitle(Form("%s, %s",hpu[k]->GetTitle(),cutlab.Data()));
4714                         ha->SetMinimum(pmin-0.1*(pmax-pmin)); ha->SetMaximum(pmax+0.30*(pmax-pmin));    // headroom for the legend
4715                         ha->Draw("E1");
4716                         if (k==1){      // thin grey dphi=0 line in the background, then the points again on top
4717                             TLine *l0   = new TLine(0.,ha->GetMinimum(),0.,ha->GetMaximum());
4718                             l0->SetLineColor(kGray); l0->SetLineWidth(1); l0->Draw();
4719                             ha->Draw("E1 same");
4720                         }
4721                         hb->Draw("E1 same");
4722                         if (doyy) hyy->Draw("E1 same");
4723                         //---- legend centered just under the title, boxed, transparent
4724                         TLegend *lgc    = new TLegend(0.25,doyy?0.74:0.78,0.75,0.89);
4725                         lgc->SetBorderSize(1); lgc->SetFillStyle(0); lgc->SetTextSize(0.04);
4726                         lgc->AddEntry(ha,(iw==0) ? "No Corr" : Form("No Corr %s",cutlab.Data()),"lp");
4727                         lgc->AddEntry(hb,(iw==0) ? "Cross Corr" : Form("Cross Corr %s",cutlab.Data()),"lp");
4728                         if (doyy) lgc->AddEntry(hyy,"from R_{2}(y_{1},y_{2}), No Corr","lp");
4729                         lgc->Draw();
4730                     }
4731                 }
4732                 ccan[ican]->cd(); ccan[ican]->Update();
4733                 EtaLabelsIf(ccan[ican],ipaty); ccan[ican]->Print(OutputFileName.Data());
4734             }
4735         }   // end ipaty CF summary pages
4736     }   // end nocorrelations CF summary pages
4737 
4738 
4739     //---- close-out...
4740     //
4741     cout<<"Closing ps file "<<OutputFileName.Data()<<endl;
4742     ccan[ican]->Print(OutputFileNameC.Data());  // close ps file
4743     char buf[200];
4744     sprintf(buf,"/usr/bin/ps2pdf %s %s",OutputFileName.Data(),OutputFileNameP.Data());
4745     cout<<"Executing: "<<buf<<endl;
4746     int iSuccess    = gSystem->Exec(buf);
4747     if (iSuccess==0){
4748         sprintf(buf,"/bin/rm %s",OutputFileName.Data());
4749         gSystem->Exec(buf);
4750     } else {
4751         cout<<"PDF creation failed: "<<OutputFileNameP.Data()<<endl;
4752     }
4753     //
4754     cout<<"split-track pre-pass -- tracks flagged="<<nSplitFlagged_total<<" events w/ a flag="<<nSplitFlagged_evts
4755         <<" pairs: duplicate="<<nFlagged_duplicate<<" complementary="<<nFlagged_complementary<<" (sec 17.14) fullmvtx="<<nFlagged_fullmvtx
4756         <<" ULS(opp-charge)="<<nFlagged_ULS<<" (sec 18.10, track-level)"<<" looper="<<nFlagged_Looper<<" (sec 35)"
4757         <<" V0-daughter partners LS="<<nFlagged_dau[0]<<" ULS="<<nFlagged_dau[1]<<" (README_CQcomparison sec 2.2)"<<endl;
4758     cout<<"sec 18.22 -- doXTFClean="<<doXTFClean<<" strict="<<doXTFStrict<<" neither="<<nXTF_neither<<" duplicate pairs="<<nXTF_pairs<<" byINTT="<<nXTF_byINTT
4759         <<" byQuality="<<nXTF_byQuality<<" losers="<<nXTF_losers<<endl;
4760     cout<<"sec 18.21 -- ONLY_FIRST_TF="<<ONLY_FIRST_TF<<" rows skipped as same-TF duplicates="<<nSkippedSameTF<<endl;
4761     cout<<"doTFDup="<<doTFDup<<" -- rows skipped as overlapping-TF collision copies="<<nTFDup_rows<<endl;
4762     cout<<"sec 18.21 -- ONLY_FIRST_XINGPOS="<<ONLY_FIRST_XINGPOS<<" rows skipped (crossing<=0 or not first crossing>0 in TF)="<<nSkippedXingPos<<endl;
4763     cout<<"crossing selection xingSel="<<xingSel<<" (1 Xing0, 2 NearX0, 3 ZB) -- events skipped="<<nSkippedXing<<endl;
4764     if (doIsoVtx) cout<<"isovtx -- rows skipped (not isolated: another vertex within +-"<<valIsoNX<<" crossings and |dz| < "<<valIsoDZ<<" cm) = "<<nSkippedIso<<endl;
4765     if (doSegVeto) cout<<"segveto -- rows skipped (vetoed segments) = "<<nSkippedSegVeto<<" ("<<segVeto.size()<<" segments in "<<segVetoFile<<")"<<endl;
4766     if (!NOCORRELATIONS){
4767         for (int ipaty=0;ipaty<NPairTypes;ipaty++){
4768             long ntot = R[ipaty]->GetNMixedPairs_total();
4769             long nsame = R[ipaty]->GetNMixedPairs_sameTF();
4770             long nneigh = R[ipaty]->GetNMixedPairs_neighborTF();
4771             cout<<"sec 18.20 -- pairtype "<<ipaty<<": mixed pairs total="<<ntot
4772                 <<" sameTF="<<nsame<<Form(" (%.3f%%)",ntot>0?100.0*nsame/ntot:0.0)
4773                 <<" neighborTF="<<nneigh<<Form(" (%.3f%%)",ntot>0?100.0*nneigh/ntot:0.0)<<endl;
4774         }
4775     }
4776     scFlush();  // last (run, segment) of segCells
4777     cout<<"segCells -- "<<tSegCells->GetEntries()<<" (run, segment) entries"<<endl;
4778     cout<<"Writing root output file "<<RootFileName.Data()<<endl;
4779     fout->cd();
4780     fout->Write();
4781     WriteInfo(fout,{{"segveto",doSegVeto ? Form("%s: %zu (run, segment)s",segVetoFile.Data(),segVeto.size()) : "OFF"}},
4782         {{"rows_read",(double)nRowsRead},{"rows_skipped_tfdup",(double)nTFDup_rows},{"rows_skipped_isovtx",(double)nSkippedIso},
4783          {"rows_skipped_segveto",(double)nSkippedSegVeto},{"events_skipped_crossing",(double)nSkippedXing},{"events_tracks",(double)nEvtTracks}});
4784     //
4785     if (!NOCORRELATIONS){
4786         for (int ipaty=0;ipaty<NPairTypes;ipaty++){ 
4787             //
4788             if (IsHPairType(ipaty)){    // h+, h-: eta, not y, in the titles (Finalize copies the map titles)
4789                 for (TH1* h : {(TH1*)hy1[ipaty],(TH1*)hy2[ipaty],(TH1*)hR2yydy[ipaty],(TH1*)hR2dy[ipaty],(TH1*)hrho2C[ipaty],(TH1*)hC2C[ipaty],(TH1*)hR2C[ipaty],(TH1*)hMempty[ipaty]}) EtaLabelsH(h);
4790                 for (int ir2=0;ir2<NR2TYPES;ir2++) for (TH1* h : {(TH1*)hrho2[ir2][ipaty],(TH1*)hrho1rho1[ir2][ipaty],(TH1*)hC2[ir2][ipaty],(TH1*)hR2[ir2][ipaty]}) EtaLabelsH(h);
4791             }
4792             hzvtx[ipaty]            ->Write();
4793             hmult[ipaty]            ->Write();
4794             hmult1[ipaty]           ->Write();
4795             hmult2[ipaty]           ->Write();
4796             hy1[ipaty]              ->Write();
4797             hy2[ipaty]              ->Write();
4798             for (int ir2=0;ir2<NR2TYPES;ir2++){
4799                 hrho2[ir2][ipaty]   ->Write();      // uncorrected 
4800                 hrho1rho1[ir2][ipaty]->Write();     // uncorrected 
4801                 hC2[ir2][ipaty]     ->Write();      // uncorrected C2
4802                 hR2[ir2][ipaty]     ->Write();      // uncorrected R2
4803             }
4804             hR2yydy[ipaty]          ->Write();
4805             hR2dy[ipaty]            ->Write();
4806             hR2dphi[ipaty]          ->Write();
4807             hMinv_S[ipaty]          ->Write();
4808             hMinv_M[ipaty]          ->Write();
4809             hMinv[ipaty]            ->Write();
4810             hCQ[ipaty]              ->Write();
4811             hQsib[ipaty]            ->Write();
4812             hQmix[ipaty]            ->Write();
4813             hQsibW[ipaty]           ->Write();
4814             hQmixW[ipaty]           ->Write();
4815             hCQKT[ipaty]            ->Write();
4816             hQsibKT[ipaty]          ->Write();
4817             hQmixKT[ipaty]          ->Write();
4818             hzoomS[ipaty]           ->Write();
4819             hrho2C[ipaty]           ->Write();      // crossing-corrected (dy,dphi)
4820             hC2C[ipaty]             ->Write();      // crossing-corrected (dy,dphi)
4821             hR2C[ipaty]             ->Write();      // crossing-corrected (dy,dphi)
4822             hMempty[ipaty]          ->Write();      // empty-denominator diagnostic
4823             //---- README_Finalize: per-zvtx-bin ingredients for combining chunks. rho2(S), rho2(M) for
4824             //---- (y1,y2), (dy,dphi), (dy,dq) and hmult, per zvtx bin; plus nevt (hmult entries) and the
4825             //---- rho2(M) normalization (denomfactor_izv; 0 = zvtx bin skipped in Calculate) vs zvtx bin.
4826             {
4827                 int nzv = R[ipaty]->GetZVTXNB();
4828                 TString part1name   = TString(ParticleIDNames[PairTypes_Info[ipaty][0]]);
4829                 TString part2name   = TString(ParticleIDNames[PairTypes_Info[ipaty][1]]);
4830                 TH1D *hnz   = new TH1D(Form("hnevtz_%d",ipaty) ,Form("%s%s, nevt (hmult entries) per zvtx bin;zvtx bin;events"   ,part1name.Data(),part2name.Data()),nzv,-0.5,nzv-0.5);
4831                 TH1D *hdz   = new TH1D(Form("hdenomz_%d",ipaty),Form("%s%s, #rho_{2}(M) normalization per zvtx bin;zvtx bin;denomfactor",part1name.Data(),part2name.Data()),nzv,-0.5,nzv-0.5);
4832                 for (int izv=0;izv<nzv;izv++){
4833                     TH2D *hmz   = R[ipaty]->Gethmult(izv);
4834                     hnz->SetBinContent(izv+1, hmz->GetEntries());
4835                     hdz->SetBinContent(izv+1, R[ipaty]->GetDenomZ(izv));
4836                     hmz->SetName(Form("hmult_z%02d_%d",izv,ipaty));
4837                     hmz->Write();
4838                     for (int ir2=0;ir2<NR2TYPES;ir2++){
4839                         TH2D *hs    = R[ipaty]->Gethrho2_S(ir2,izv);
4840                         TH2D *hm    = R[ipaty]->Gethrho2_M(ir2,izv);
4841                         hs->SetName(Form("hrho2_%d_z%02d_%d"    ,ir2,izv,ipaty));
4842                         hm->SetName(Form("hrho1rho1_%d_z%02d_%d",ir2,izv,ipaty));
4843                         hs->Write();
4844                         hm->Write();
4845                     }
4846                     //---- README_CQ: C(Q) ingredients per zvtx bin. hQsib is raw counts; hQmix is already
4847                     //---- divided by denomfactor_izv (= hdenomz), exactly what CalcRm::Calculate divides.
4848                     TH1D *hqs   = R[ipaty]->GethQsib(izv);
4849                     TH1D *hqm   = R[ipaty]->GethQmix(izv);
4850                     TH2D *hks   = R[ipaty]->GethQsibKT(izv);
4851                     TH2D *hkm   = R[ipaty]->GethQmixKT(izv);
4852                     hqs->SetName(Form("hQsib_z%02d_%d"  ,izv,ipaty));
4853                     hqm->SetName(Form("hQmix_z%02d_%d"  ,izv,ipaty));
4854                     hks->SetName(Form("hQsibKT_z%02d_%d",izv,ipaty));
4855                     hkm->SetName(Form("hQmixKT_z%02d_%d",izv,ipaty));
4856                     hqs->Write();
4857                     hqm->Write();
4858                     hks->Write();
4859                     hkm->Write();
4860                 }
4861                 hnz->Write();
4862                 hdz->Write();
4863             }
4864             hzoomM[ipaty]           ->Write();
4865             if (ipaty<=3) for (int sm=0;sm<2;sm++) for (int is=0;is<2;is++) for (int ir=0;ir<4;ir++) R[ipaty]->GethTTR(sm,is,ir)->Write();
4866             if (ipaty<=3) for (int sm=0;sm<2;sm++){ TH2D* h=R[ipaty]->GethISep(sm); h->SetName(Form("hISep_%s_%d",sm?"M":"S",ipaty)); h->Write(); } // sec 29
4867             if (ipaty<=3) for (int sm=0;sm<2;sm++){ TH2D* h=R[ipaty]->GethGapDy(sm); h->SetName(Form("hGapDy_%s_%d",sm?"M":"S",ipaty)); h->Write(); }   // sec 28
4868             if (ipaty<=3) for (int sm=0;sm<2;sm++){ // sec 32
4869                 TH2D* h=R[ipaty]->GethGapPhi(sm);  h->SetName(Form("hGapPhi_%s_%d" ,sm?"M":"S",ipaty)); h->Write();
4870                 h=R[ipaty]->GethGapYbar(sm);       h->SetName(Form("hGapYbar_%s_%d",sm?"M":"S",ipaty)); h->Write();
4871                 h=R[ipaty]->GethGapPt(sm);         h->SetName(Form("hGapPt_%s_%d"  ,sm?"M":"S",ipaty)); h->Write();
4872                 h=R[ipaty]->GethGapNch(sm);        h->SetName(Form("hGapNch_%s_%d" ,sm?"M":"S",ipaty)); h->Write();
4873                 h=R[ipaty]->GethGapRm(sm);         h->SetName(Form("hGapRm_%s_%d"  ,sm?"M":"S",ipaty)); h->Write(); }
4874             if (ipaty<=3) for (int sm=0;sm<2;sm++) for (int w=0;w<2;w++){ TH2D* h=R[ipaty]->GethGap(sm,w); h->SetName(Form("hGap_%s_%s_%d",sm?"M":"S",w?"near":"far",ipaty)); h->Write(); } // sec 28
4875             if (ipaty<=3) for (int sm=0;sm<2;sm++) for (int sd=0;sd<2;sd++){ TH2D* h=R[ipaty]->GethTTRside(sm,sd); h->SetName(Form("hTTRside_%s_%s_%d",sm?"M":"S",sd?"dirty":"clean",ipaty)); h->Write(); }
4876             hMinvFS[ipaty]          ->Write();
4877             hMinvFM[ipaty]          ->Write();
4878             //
4879         }   // end ipaty...
4880     }   // end nocorrelations
4881     //
4882     fout->Close();  
4883     //
4884     cout<<"done."<<endl;
4885     //
4886 }
4887 
4888 //---------------------------------------------------------------------------
4889 bool corral::AcceptEvent(){
4890     if (      (*vtxx)  < -0.5 ) return false;
4891     if (      (*vtxx)  >  0.5 ) return false;
4892     if (      (*vtxy)  < -0.5 ) return false;
4893     if (      (*vtxy)  >  0.5 ) return false;
4894     if ( fabs((*vtxz)) > valVzMax ) return false;   // default 16 cm ("vzNN" overrides), README_SplitTracks573 sec 30
4895     return true;
4896 }
4897 //---------------------------------------------------------------------------
4898 //------------------------------------------------------------
4899 // sec 18.22: cross-crossing duplicate-track pre-pass (doXTFClean). Separate chain over the same
4900 // files (same order as fChain, so chain entry == jentry in Loop), only the branches needed.
4901 // Rows of one TF are consecutive (sec 18.20). Within one TF, siclukey IS unique per physical
4902 // cluster (sec 18.11's aliasing is only BETWEEN events), so tracks in different rows sharing
4903 // >=2 Si slots are copies of one silicon seed attached to different TPC seeds (matcher, sec
4904 // 18.22). Loser = the copy whose row crossing != its own INTT time bucket
4905 // ((cluskey>>32 & 0x3FF) - 200, INTT slots 3-6 only); if that doesn't decide, worse (higher)
4906 // quality; if still tied, the later row. Exactly one copy of each pair survives.
4907 void corral::BuildXTFLosers(Long64_t nentries){
4908     TChain *ch  = new TChain(fChain->GetName());
4909     TIter next(fChain->GetListOfFiles());
4910     while (TObject *el = next()) ch->Add(el->GetTitle());
4911     TTreeReader r(ch);
4912     TTreeReaderValue<Int_t>     r_run(r,"run"), r_evt(r,"evt"), r_xing(r,"crossing"), r_ntr(r,"ntr");
4913     TTreeReaderArray<Float_t>   r_pt(r,"pt"), r_dcaxy(r,"dcaxy"), r_dcaz(r,"dcaz"), r_quality(r,"quality");
4914     TTreeReaderArray<UChar_t>   r_ntpc(r,"ntpc");
4915     TTreeReaderArray<ULong64_t> r_key(r,"siclukey");
4916     struct T { Long64_t ent; int idx; int xing; int intt; float q; ULong64_t k[7]; };
4917     std::vector<T> tf;
4918     auto inttXing = [](const ULong64_t *k){     // -9999 = no INTT cluster, -8888 = INTT clusters disagree
4919         int x=-9999;
4920         for (int s=3;s<7;s++){ if (k[s]==~0ULL) continue; int b=(int)((k[s]>>32)&0x3FF)-200;
4921             if (x==-9999) x=b; else if (b!=x) return -8888; }
4922         return x; };
4923     auto flush = [&](){
4924         for (size_t a=0;a<tf.size();a++) for (size_t b=a+1;b<tf.size();b++){
4925             if (tf[a].ent==tf[b].ent) continue;                 // same row: LS/ULS paths handle it
4926             int ns=0; for (int s=0;s<7;s++) if (tf[a].k[s]!=~0ULL && tf[a].k[s]==tf[b].k[s]) ++ns;
4927             if (ns<2) continue;
4928             ++nXTF_pairs;
4929             bool aok = (tf[a].intt==tf[a].xing), bok = (tf[b].intt==tf[b].xing);
4930             bool neither = !aok && !bok && tf[a].intt>-8000 && tf[b].intt>-8000;    // both have consistent INTT, both off
4931             if (neither) ++nXTF_neither;
4932             std::vector<const T*> los;
4933             if (aok!=bok){ los.push_back(aok ? &tf[b] : &tf[a]); ++nXTF_byINTT; }
4934             else if (neither && doXTFStrict){ los.push_back(&tf[a]); los.push_back(&tf[b]); }
4935             else { los.push_back((tf[a].q>tf[b].q) ? &tf[a] : &tf[b]); ++nXTF_byQuality; }  // tie -> later row (b)
4936             for (const T *l : los){
4937                 std::vector<int> &v = xtfLosers[l->ent];
4938                 if (std::find(v.begin(),v.end(),l->idx)==v.end()){ v.push_back(l->idx); ++nXTF_losers; }
4939             }
4940         }
4941         tf.clear(); };
4942     int pr=-1, pe=-1;
4943     cout<<"BuildXTFLosers -- pre-pass over "<<nentries<<" rows..."<<endl;
4944     for (Long64_t je=0; je<nentries && r.Next(); je++){
4945         if (je%5000000==0) cout<<"BuildXTFLosers -- "<<je<<endl;
4946         if ((*r_run)!=pr || (*r_evt)!=pe){ flush(); pr=(*r_run); pe=(*r_evt); }
4947         for (int it=0; it<(*r_ntr); it++){
4948             //---- same cuts as AcceptTrack() below -- keep in sync
4949             if (r_pt[it]<0.1 || r_pt[it]>20.1 || r_ntpc[it]<NTPCCUT
4950              || fabs(r_dcaxy[it])>valDcaxyCut || fabs(r_dcaz[it])>valDcazCut) continue;
4951             T t; t.ent=je; t.idx=it; t.xing=(*r_xing); t.q=r_quality[it];
4952             for (int s=0;s<7;s++) t.k[s]=r_key[it*7+s];
4953             t.intt=inttXing(t.k);
4954             tf.push_back(t);
4955         }
4956     }
4957     flush();
4958     delete ch;
4959     cout<<"BuildXTFLosers -- duplicate pairs="<<nXTF_pairs<<" (decided by INTT="<<nXTF_byINTT
4960         <<", by quality="<<nXTF_byQuality<<", neither="<<nXTF_neither<<(doXTFStrict?" [both dropped]":" [better quality kept]")<<") losers="<<nXTF_losers<<" in "<<xtfLosers.size()<<" rows"<<endl;
4961 }
4962 
4963 //------------------------------------------------------------
4964 // Overlapping-TF collision copies (doTFDup). Separate chain over the same files, same order as
4965 // fChain (chain entry == jentry in Loop), like BuildXTFLosers. When two triggers are closer in
4966 // time than a TF's readout window (~540 crossings), both TFs reconstruct the collisions in the
4967 // overlap: same vertex, crossing shifted by exactly the GL1 BCO difference of the two TFs
4968 // (checked on ana573 79515 seg 0: 5 of 5 shared vertices of evt 45/46 shifted by dBCO=62).
4969 // So bco+crossing is the absolute crossing, and (run, bco+crossing) identifies the bunch crossing.
4970 // Smoke test (79515 seg 99-100): 11.9% of rows are copies, dEvt 1 (94%) and 2 (6%) - so the
4971 // adjacent-TF mixing exclusion (|devt|==1) alone would miss some; 98.6% of copies agree in vtxz
4972 // to <1 mm, 0.7% differ by >1 cm (in-bunch pileup: each TF kept a different one of the crossing's
4973 // vertices - Collect keeps one vertex per crossing - but the rows hold the same crossing's tracks).
4974 // First row with a key is kept (the earlier TF in chain order - neither copy is systematically
4975 // better, ntr more/fewer/equal 30/31/38 on that test), later rows with the key go in tfDupRows.
4976 // Rows with bco==~0 (TF without GL1RAWHIT) are never skipped. No bco branch -> nothing to do.
4977 void corral::BuildTFDupRows(Long64_t nentries){
4978     TChain *ch  = new TChain(fChain->GetName());
4979     TIter next(fChain->GetListOfFiles());
4980     while (TObject *el = next()) ch->Add(el->GetTitle());
4981     ch->LoadTree(0);
4982     if (!ch->GetBranch("bco")){
4983         cout<<"BuildTFDupRows -- no bco branch in these trees: overlapping-TF duplicate removal not possible"<<endl;
4984         delete ch;
4985         return;
4986     }
4987     TTreeReader r(ch);
4988     TTreeReaderValue<Int_t>     r_run(r,"run"), r_evt(r,"evt"), r_xing(r,"crossing");
4989     TTreeReaderValue<ULong64_t> r_bco(r,"bco");
4990     TTreeReaderValue<Double_t>  r_vtxz(r,"vtxz");
4991     //---- Copies are only ever a few TFs apart (dEvt<=3 seen), so only keys from the last TFDUP_WINDOW
4992     //---- TFs of the current run are kept - memory stays small even for the full-statistics job
4993     //---- (all chunks, ~1e9 rows). Rows arrive in TF order within a run (chunks are contiguous).
4994     const int TFDUP_WINDOW = 20;
4995     struct Kept { int evt; double vz; };
4996     std::unordered_map<Long64_t,Kept> seen;         // absolute crossing -> first row, current run only
4997     std::deque<std::pair<int,Long64_t>> order;      // (evt, key) in insertion order, for pruning
4998     int currun = -1;
4999     tfDupRows.assign(nentries, false);
5000     cout<<"BuildTFDupRows -- pre-pass over "<<nentries<<" rows..."<<endl;
5001     for (Long64_t je=0; je<nentries && r.Next(); je++){
5002         if ((*r_run)!=currun){ seen.clear(); order.clear(); currun = (*r_run); }
5003         while (!order.empty() && order.front().first < (*r_evt) - TFDUP_WINDOW){ seen.erase(order.front().second); order.pop_front(); }
5004         if ((*r_bco)==~0ULL) continue;
5005         Long64_t key = (Long64_t)(*r_bco) + (*r_xing);
5006         auto it = seen.find(key);
5007         if (it==seen.end()){ seen[key] = Kept{(*r_evt),(*r_vtxz)}; order.emplace_back((*r_evt),key); continue; }
5008         tfDupRows[je] = true;
5009         ++nTFDup_rows;
5010         ++nTFDup_byDevt[(*r_evt) - it->second.evt];
5011         TFDup_maxAbsDvz = std::max(TFDup_maxAbsDvz, fabs((*r_vtxz) - it->second.vz));
5012     }
5013     delete ch;
5014     cout<<"BuildTFDupRows -- rows skipped="<<nTFDup_rows<<" of "<<nentries<<", max |dvtxz| between copies="<<TFDup_maxAbsDvz<<" cm; by evt(copy)-evt(kept):";
5015     for (auto &p : nTFDup_byDevt) cout<<" "<<p.first<<":"<<p.second;
5016     cout<<endl;
5017 }
5018 
5019 //---- README.md "isovtxNNZZ" (2026-10-12): mark the rows whose vertex has a neighbour -- another vertex of the same run within
5020 //---- +-valIsoNX crossings (absolute crossing = bco + crossing; TF boundaries do not matter) and |dz| < valIsoDZ cm. Rows with
5021 //---- the same absolute crossing are copies of one collision (Collect keeps one vertex per crossing; doTFDup skips them), not
5022 //---- neighbours. Rows without bco (bco == ~0) fall back to the crossing inside their TF (key evt*1e6 + crossing).
5023 //---- Per run: collect (key, z, row), sort by key, scan the window. Memory ~24 B per row of one run.
5024 void corral::BuildIsoRows(Long64_t nentries){
5025     TChain *ch  = new TChain(fChain->GetName());
5026     TIter next(fChain->GetListOfFiles());
5027     while (TObject *el = next()) ch->Add(el->GetTitle());
5028     ch->LoadTree(0);
5029     const bool hasBco   = ch->GetBranch("bco")!=nullptr;
5030     TTreeReader r(ch);
5031     TTreeReaderValue<Int_t>     r_run(r,"run"), r_evt(r,"evt"), r_xing(r,"crossing");
5032     TTreeReaderValue<Double_t>  r_vtxz(r,"vtxz");
5033     TTreeReaderValue<ULong64_t> *r_bco  = hasBco ? new TTreeReaderValue<ULong64_t>(r,"bco") : nullptr;
5034     nonIsoRows.assign(nentries,false);
5035     struct V { Long64_t key; double z; Long64_t row; };
5036     std::vector<V> v; int currun = -1; long nNon = 0, nAll = 0;
5037     auto scan = [&](){
5038         std::sort(v.begin(),v.end(),[](const V &a, const V &b){ return a.key<b.key; });
5039         size_t lo = 0;
5040         for (size_t i=0;i<v.size();i++){
5041             while (v[lo].key < v[i].key - valIsoNX) lo++;
5042             for (size_t j=lo;j<v.size() && v[j].key <= v[i].key + valIsoNX;j++){
5043                 if (j==i || v[j].key==v[i].key) continue;
5044                 if (fabs(v[j].z - v[i].z) < valIsoDZ){ nonIsoRows[v[i].row] = true; nNon++; break; }
5045             }
5046         }
5047         nAll += v.size(); v.clear(); };
5048     cout<<"BuildIsoRows -- pre-pass over "<<nentries<<" rows..."<<endl;
5049     for (Long64_t je=0; je<nentries && r.Next(); je++){
5050         if ((*r_run)!=currun){ scan(); currun = (*r_run); }
5051         Long64_t key    = (hasBco && (**r_bco)!=~0ULL) ? (Long64_t)(**r_bco) + (*r_xing) : (Long64_t)(*r_evt)*1000000LL + (*r_xing);
5052         v.push_back({key,(*r_vtxz),je});
5053     }
5054     scan();
5055     delete r_bco; delete ch;
5056     cout<<"BuildIsoRows -- "<<nNon<<" of "<<nAll<<" rows have a neighbour within +-"<<valIsoNX<<" crossings and |dz| < "<<valIsoDZ<<" cm ("
5057         <<(nAll ? 100.*nNon/nAll : 0.)<<"%), skipped"<<(hasBco ? "" : " (no bco branch: crossing within the TF only)")<<endl;
5058 }
5059 
5060 bool corral::AcceptTrack(int it){
5061     if (!AcceptTrackBase(it)) return false;
5062     if (doSiPhiMask && InSiPhiMask(it)) return false;
5063     if (doCMMask && InCMMask(it)) return false;
5064     return true;
5065 }
5066 bool corral::InCMMask(int it){
5067     //---- sec 38: the mask is FIXED within a zvtx slice (it uses the slice centre, not the event's zvtx): every event of
5068     //---- a slice then has the same acceptance, which cancels in S/M. A mask that follows each event's zvtx moves its sharp
5069     //---- edge by 0.036 in eta across a 2-cm slice, and S (one zvtx) and M (two) see different edges (CMscan, 1st round).
5070     const double zl = -16., zu = 16.;
5071     double zc   = *vtxz;
5072     if (zc>zl && zc<zu){ double bw = (zu-zl)/valZvtxNB; zc = zl + (floor((zc-zl)/bw)+0.5)*bw; }
5073     double w    = (eta[it] + CMMASK_K*zc) * (zc<0. ? 1. : -1.);
5074     return (w>=valCMMaskLo && w<valCMMaskHi);
5075 }
5076 bool corral::InSiPhiMask(int it){
5077     double pd   = phi[it]*180.0/M_PI;
5078     for (int k=0;k<nSiPhiMask;k++) if (pd>=SIPHIMASK_LO[k] && pd<SIPHIMASK_HI[k]) return true;
5079     return false;
5080 }
5081 bool corral::AcceptTrackBase(int it){
5082     //if ( !primary[it]              ) return false;
5083     if (  pt[it]         < PTMINCUT ) return false;
5084     if (  pt[it]         >    20.1 ) return false;
5085     if (  ntpc[it]       < NTPCCUT ) return false;
5086     if ( fabs(dcaxy[it]) > valDcaxyCut ) return false;  // "dxyNN", default 1.5 cm
5087     if ( fabs(dcaz[it])  > valDcazCut ) return false;   // "dczNN", default 1.5 cm
5088     return true;
5089 }
5090 
5091 
5092 
5093 
5094 
5095 
5096