@@ -249,6 +249,8 @@ struct photonhbt {
249249 Configurable<float > cfgMCMaxQinv{" cfgMCMaxQinv" , 0 .3f , " max q_{inv}^{true} for MC truth efficiency pair loop (GeV/c); <=0 = no cut" };
250250 Configurable<float > cfgMCMinKt{" cfgMCMinKt" , 0 .0f , " min k_{T}^{true} for MC truth efficiency pair loop (GeV/c); <=0 = no cut" };
251251 Configurable<float > cfgMCMaxKt{" cfgMCMaxKt" , 0 .7f , " max k_{T}^{true} for MC truth efficiency pair loop (GeV/c); <=0 = no cut" };
252+ Configurable<bool > cfgDoTruthMix{" cfgDoTruthMix" , false , " enable truth-level event mixing for baseline CF" };
253+ Configurable<int > cfgTruthMixDepth{" cfgTruthMixDepth" , 10 , " depth of truth-level mixing pool" };
252254 } mctruth;
253255
254256 struct : ConfigurableGroup {
@@ -342,6 +344,7 @@ struct photonhbt {
342344 emh2 = nullptr ;
343345 mapMixedEventIdToGlobalBC.clear ();
344346 usedPhotonIdsPerCol.clear ();
347+ truthGammaPool.clear ();
345348 }
346349
347350 HistogramRegistry fRegistry {" output" , {}, OutputObjHandlingPolicy::AnalysisObject, false , false };
@@ -524,6 +527,14 @@ struct photonhbt {
524527 bool valid = true ;
525528 };
526529
530+ struct TruthGamma {
531+ int id = -1 , posId = -1 , negId = -1 ;
532+ float eta = 0 .f, phi = 0 .f, pt = 0 .f, rTrue = -1 .f, legDRtrue = -1 .f;
533+ };
534+
535+ std::map<std::tuple<int , int , int , int >, std::deque<std::vector<TruthGamma>>> truthGammaPool;
536+
537+
527538 void addSinglePhotonQAHistogramsForStep (const std::string& path)
528539 {
529540 fRegistryPairQA .add ((path + " hPt" ).c_str (), " p_{T};p_{T} (GeV/c);counts" , kTH1D , {axisPt}, true );
@@ -1105,6 +1116,21 @@ struct photonhbt {
11051116
11061117 const AxisSpec axQinvMC{60 , 0 .f , 0 .3f , " q_{inv}^{true} (GeV/c)" };
11071118
1119+ // Same-event truth CF
1120+ fRegistryMC .add (" MC/TruthCF/hQinvVsKt_same" ,
1121+ " truth-level same-event CF;k_{T} (GeV/c);q_{inv}^{true} (GeV/c)" ,
1122+ kTH2D , {axisKt, axQinvMC}, true );
1123+ fRegistryMC .add (" MC/TruthCF/hDEtaDPhi_same" ,
1124+ " truth-level same-event #Delta#eta vs #Delta#phi" ,
1125+ kTH2D , {axisDeltaEta, axisDeltaPhi}, true );
1126+ // Mixed-event truth CF
1127+ fRegistryMC .add (" MC/TruthCF/hQinvVsKt_mix" ,
1128+ " truth-level mixed-event CF;k_{T} (GeV/c);q_{inv}^{true} (GeV/c)" ,
1129+ kTH2D , {axisKt, axQinvMC}, true );
1130+ fRegistryMC .add (" MC/TruthCF/hDEtaDPhi_mix" ,
1131+ " truth-level mixed-event #Delta#eta vs #Delta#phi" ,
1132+ kTH2D , {axisDeltaEta, axisDeltaPhi}, true );
1133+
11081134 fRegistryMC .add (" MC/TruthAO2D/hSparse_DEtaDPhi_qinv_truthConverted" ,
11091135 " true converted pairs, denominator;"
11101136 " #Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);q_{inv}^{true} (GeV/c)" ,
@@ -1874,10 +1900,6 @@ struct photonhbt {
18741900 info.passesCut = info.passesCut || passes;
18751901 }
18761902
1877- struct TruthGamma {
1878- int id = -1 , posId = -1 , negId = -1 ;
1879- float eta = 0 .f, phi = 0 .f, pt = 0 .f, rTrue = -1 .f, legDRtrue = -1 .f;
1880- };
18811903 std::vector<TruthGamma> trueGammas;
18821904 trueGammas.reserve (32 );
18831905
@@ -2049,6 +2071,69 @@ struct photonhbt {
20492071 }
20502072 }
20512073 }
2074+ // ─── Truth-level same-event pairs ────────────────────────────────────────
2075+ if (mctruth.cfgDoTruthMix .value ) {
2076+ for (size_t i = 0 ; i < trueGammas.size (); ++i) {
2077+ for (size_t j = i + 1 ; j < trueGammas.size (); ++j) {
2078+ const auto & g1 = trueGammas[i];
2079+ const auto & g2 = trueGammas[j];
2080+ if (!passAsymmetryCut (g1.pt , g2.pt ))
2081+ continue ;
2082+ const float deta = g1.eta - g2.eta ;
2083+ const float dphi = wrapPhi (g1.phi - g2.phi );
2084+ const float px1 = g1.pt * std::cos (g1.phi ), py1 = g1.pt * std::sin (g1.phi );
2085+ const float px2 = g2.pt * std::cos (g2.phi ), py2 = g2.pt * std::sin (g2.phi );
2086+ const float kt = 0 .5f * std::sqrt ((px1 + px2) * (px1 + px2) + (py1 + py2) * (py1 + py2));
2087+ const float e1 = g1.pt * std::cosh (g1.eta ), e2 = g2.pt * std::cosh (g2.eta );
2088+ const float dot = e1 * e2 - (px1 * px2 + py1 * py2 + g1.pt * std::sinh (g1.eta ) * g2.pt * std::sinh (g2.eta ));
2089+ const float qinv_true = std::sqrt (std::max (0 .f , 2 .f * dot));
2090+ fRegistryMC .fill (HIST (" MC/TruthCF/hQinvVsKt_same" ), kt, qinv_true);
2091+ fRegistryMC .fill (HIST (" MC/TruthCF/hDEtaDPhi_same" ), deta, dphi);
2092+ }
2093+ }
2094+
2095+ // ─── Truth-level mixed-event pairs ─────────────────────────────────────
2096+ const float cent[3 ] = {collision.centFT0M (), collision.centFT0A (), collision.centFT0C ()};
2097+ const float centForBin = cent[mixing.cfgCentEstimator .value ];
2098+ const std::array<float , 7 > epArr = {collision.ep2ft0m (), collision.ep2ft0a (), collision.ep2ft0c (),
2099+ collision.ep2fv0a (), collision.ep2btot (), collision.ep2bpos (), collision.ep2bneg ()};
2100+ const float ep2 = epArr[mixing.cfgEP2EstimatorForMix .value ];
2101+ const float occupancy = (mixing.cfgOccupancyEstimator .value == 1 )
2102+ ? static_cast <float >(collision.trackOccupancyInTimeRange ())
2103+ : collision.ft0cOccupancyInTimeRange ();
2104+ auto keyBin = std::make_tuple (binOf (ztxBinEdges, collision.posZ ()),
2105+ binOf (centBinEdges, centForBin),
2106+ binOf (epBinEgdes, ep2),
2107+ binOf (occBinEdges, occupancy));
2108+
2109+ if (truthGammaPool.count (keyBin)) {
2110+ for (const auto & poolEvent : truthGammaPool[keyBin]) {
2111+ for (const auto & g1 : trueGammas) {
2112+ for (const auto & g2 : poolEvent) {
2113+ if (!passAsymmetryCut (g1.pt , g2.pt ))
2114+ continue ;
2115+ const float deta = g1.eta - g2.eta ;
2116+ const float dphi = wrapPhi (g1.phi - g2.phi );
2117+ const float px1 = g1.pt * std::cos (g1.phi ), py1 = g1.pt * std::sin (g1.phi );
2118+ const float px2 = g2.pt * std::cos (g2.phi ), py2 = g2.pt * std::sin (g2.phi );
2119+ const float kt = 0 .5f * std::sqrt ((px1 + px2) * (px1 + px2) + (py1 + py2) * (py1 + py2));
2120+ const float e1 = g1.pt * std::cosh (g1.eta ), e2 = g2.pt * std::cosh (g2.eta );
2121+ const float dot = e1 * e2 - (px1 * px2 + py1 * py2 + g1.pt * std::sinh (g1.eta ) * g2.pt * std::sinh (g2.eta ));
2122+ const float qinv_true = std::sqrt (std::max (0 .f , 2 .f * dot));
2123+ fRegistryMC .fill (HIST (" MC/TruthCF/hQinvVsKt_mix" ), kt, qinv_true);
2124+ fRegistryMC .fill (HIST (" MC/TruthCF/hDEtaDPhi_mix" ), deta, dphi);
2125+ }
2126+ }
2127+ }
2128+ }
2129+
2130+ if (!trueGammas.empty ()) {
2131+ auto & poolBin = truthGammaPool[keyBin];
2132+ poolBin.push_back (trueGammas);
2133+ if (static_cast <int >(poolBin.size ()) > mctruth.cfgTruthMixDepth .value )
2134+ poolBin.pop_front ();
2135+ }
2136+ } // end cfgDoTruthMix
20522137 }
20532138 }
20542139
0 commit comments