@@ -338,10 +338,23 @@ struct HStrangeCorrelation {
338338 // see runEventRecoEfficiencyStudy
339339 Configurable<bool > doStageDiagnostics{" doStageDiagnostics" , true , " part 1: fill the PairLossK0 truth-pair reconstruction ladder and its diagnostics" };
340340 Configurable<bool > doRecComparison{" doRecComparison" , false , " part 2: fill the PairLossK0/Comparison cumulative Rec/Truth/Gen/Final ladder (runs the exact reconstructed path internally)" };
341+ Configurable<bool > recComparisonBestCollisionOnly{" recComparisonBestCollisionOnly" , false , " part 2: run the Rec path of PairLossK0/Comparison only in the best reconstructed collision (most PV contributors) of each MC collision, the one the Gen, Truth and Final stages are selected on. Set false to run it in every reconstructed collision, as the data path does" };
341342 Configurable<bool > doGenLevelStudy{" doGenLevelStudy" , false , " part 3: fill the PairLossK0/GenStudy generated-vs-reconstructed split, using generator-level event selection only" };
342343 Configurable<bool > doEventRecoEfficiencyStudy{" doEventRecoEfficiencyStudy" , false , " fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, Nch^gen vs multNTracksPVeta1 and multNTracksPVeta1 vs FT0M percentile and amplitude" };
344+ Configurable<bool > nchGenResponseBestCollisionOnly{" nchGenResponseBestCollisionOnly" , false , " fill PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1 with the best reconstructed collision (most PV contributors) of each MC collision only, instead of with every reconstructed collision; the multNTracksPVeta1 vs FT0M responses stay per reconstructed collision" };
343345 Configurable<bool > applyINELSelectionAtGenLevel{" applyINELSelectionAtGenLevel" , false , " part 2 (pp only): replace the reconstructed INEL cuts by Nch^gen >= 1 of the MC collision in the Rec, Gen, Truth and Final stages (selectINELgtN is ignored) and fill only PairLossK0/Comparison/NchGen (Rec and Gen vs Nch^gen, integrated over vertex z and multiplicity); requires doRecComparison and applyNewMCSelection" };
344346 ConfigurableAxis axisComparisonNchGen{" axisComparisonNchGen" , {VARIABLE_WIDTH , 0 .5f , 4 .5f , 9 .5f , 19 .5f , 1000 .5f }, " Nch^gen classes (charged primaries in |eta| < 1) of PairLossK0/Comparison/NchGen" };
347+ // MC-only event quality cut on the Nch^gen vs multNTracksPVeta1 response
348+ // (PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1): a reconstructed collision
349+ // is kept only if |multNTracksPVeta1 - mu(Nch^gen)| <= nSigma * max(sigma(Nch^gen), sigmaMin),
350+ // with mu a polynomial in Nch^gen and sigma a polynomial in sqrt(Nch^gen), e.g. fits of
351+ // the mean and width of each Nch^gen column of that response. It uses the generated
352+ // multiplicity, so it has no data counterpart
353+ Configurable<bool > applyNchResponseBandCut{" applyNchResponseBandCut" , false , " part 2 (MC only): keep only reconstructed collisions inside the mu +- nSigma*sigma band of the Nch^gen vs multNTracksPVeta1 response, in the Rec (every collision), Gen, Truth and Final (best collision) stages of PairLossK0/Comparison" };
354+ Configurable<std::vector<double >> nchResponseBandMeanPars{" nchResponseBandMeanPars" , {0.0 , 0.9 }, " mean multNTracksPVeta1 = p0 + p1*x + p2*x^2 + ..., x = Nch^gen" };
355+ Configurable<std::vector<double >> nchResponseBandSigmaPars{" nchResponseBandSigmaPars" , {0.0 , 0.35 }, " multNTracksPVeta1 width = p0 + p1*sqrt(x) + p2*x + p3*x^1.5 + ..., x = Nch^gen" };
356+ Configurable<float > nchResponseBandNSigma{" nchResponseBandNSigma" , 3 .0f , " half-width of the band in units of sigma" };
357+ Configurable<float > nchResponseBandSigmaMin{" nchResponseBandSigmaMin" , 1 .0f , " lower bound of sigma, which keeps the band open at low Nch^gen" };
345358 // Generated charged multiplicity of the MC collision, counted in |eta| < 0.8
346359 // by mCounter. Plain ConfigurableAxis: unlike the correlation axes it is NOT
347360 // trimmed by skipUnderOverflowInTHn, so what you configure is what you get.
@@ -2826,6 +2839,10 @@ struct HStrangeCorrelation {
28262839 }
28272840 }
28282841
2842+ if (pairLossK0Configurations.applyNchResponseBandCut && !doprocessPairLossK0MC) {
2843+ LOGF (fatal, " pairLossK0Configurations.applyNchResponseBandCut needs the generated multiplicity; enable processPairLossK0MC" );
2844+ }
2845+
28292846 if (doprocessPairLossK0MC && pairLossK0Configurations.doEventRecoEfficiencyStudy ) {
28302847 // Per MC collision with |vz_gen| <= zVertexCut, each histogram a subset of
28312848 // the previous one: HasRecoColl/All is the MC-collision reconstruction
@@ -2837,10 +2854,21 @@ struct HStrangeCorrelation {
28372854 histos.add (" PairLossK0/EventRecoEfficiency/hNchGenEvSel" , " best reconstructed collision passes the Rec event selection with reconstructed-level INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions" , kTH1D , {axesConfigurations.axisNchGen });
28382855 histos.add (" PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl" , " all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);reconstructed collisions" , kTH2D , {axesConfigurations.axisNchGen , axesConfigurations.axisNRecoCollisions });
28392856 // Per reconstructed collision passing the Rec event selection without INEL
2840- // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut
2841- histos.add (" PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1" , " reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1" , kTH2D , {axesConfigurations.axisNchGen , axesConfigurations.axisNchGen });
2857+ // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut.
2858+ // With nchGenResponseBestCollisionOnly the Nch^gen response takes only the best
2859+ // collision, which drops the split and duplicate vertices of the MC collision
2860+ histos.add (" PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1" ,
2861+ pairLossK0Configurations.nchGenResponseBestCollisionOnly ? " best reconstructed collision passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1"
2862+ : " reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1" ,
2863+ kTH2D , {axesConfigurations.axisNchGen , axesConfigurations.axisNchGen });
28422864 histos.add (" PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M" , " reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile" , kTH2D , {axesConfigurations.axisNchGen , axesConfigurations.axisCentFT0M });
28432865 histos.add (" PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M" , " reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude" , kTH2D , {axesConfigurations.axisNchGen , axesConfigurations.axisMultFT0M });
2866+ if (pairLossK0Configurations.applyNchResponseBandCut ) {
2867+ // Checks of applyNchResponseBandCut: the part of the Nch^gen response it keeps,
2868+ // and hNchGenEvSelNoInelInBand / hNchGenEvSelNoInel, its efficiency vs Nch^gen
2869+ histos.add (" PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand" , " hNchGenVsNTracksPVeta1 inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1" , kTH2D , {axesConfigurations.axisNchGen , axesConfigurations.axisNchGen });
2870+ histos.add (" PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand" , " best reconstructed collision passes the Rec event selection without INEL cuts and lies inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions" , kTH1D , {axesConfigurations.axisNchGen });
2871+ }
28442872 }
28452873
28462874 if (doprocessSameEventHV0s && masterConfigurations.doMultEstimatorResponse ) {
@@ -3438,6 +3466,29 @@ struct HStrangeCorrelation {
34383466 return nChGen > 0 ;
34393467 }
34403468
3469+ // MC-only event quality cut of the PairLoss Comparison stages (applyNchResponseBandCut):
3470+ // multNTracksPVeta1 inside mu(Nch^gen) +- nSigma * max(sigma(Nch^gen), sigmaMin), with mu
3471+ // a polynomial in Nch^gen and sigma one in sqrt(Nch^gen). Evaluated here rather than
3472+ // through TF1 members: the task struct is at the StructToTuple member limit
3473+ bool isInNchResponseBand (int nChGen, int nTracksPVeta1)
3474+ {
3475+ const double sqrtNchGen = std::sqrt (static_cast <double >(std::max (nChGen, 0 )));
3476+ double mean = 0.0 ;
3477+ double power = 1.0 ;
3478+ for (const double par : pairLossK0Configurations.nchResponseBandMeanPars .value ) {
3479+ mean += par * power;
3480+ power *= nChGen;
3481+ }
3482+ double sigma = 0.0 ;
3483+ power = 1.0 ;
3484+ for (const double par : pairLossK0Configurations.nchResponseBandSigmaPars .value ) {
3485+ sigma += par * power;
3486+ power *= sqrtNchGen;
3487+ }
3488+ sigma = std::max (sigma, static_cast <double >(pairLossK0Configurations.nchResponseBandSigmaMin ));
3489+ return std::abs (nTracksPVeta1 - mean) <= pairLossK0Configurations.nchResponseBandNSigma * sigma;
3490+ }
3491+
34413492 // this function allows for all event selections to be done in a modular way
34423493 // applyInelSelection = false skips only the reconstructed INEL>0 / INEL>N cuts
34433494 template <typename TCollision>
@@ -4667,9 +4718,11 @@ struct HStrangeCorrelation {
46674718 TracksComplete const & tracks)
46684719 {
46694720 // Nch^gen of this MC collision (charged primaries in |eta| < 1), shared by the
4670- // event-reconstruction-efficiency study and the generated-level INEL selection
4721+ // event-reconstruction-efficiency study, the generated-level INEL selection and
4722+ // the Nch^gen vs multNTracksPVeta1 band cut
46714723 const bool applyGenInel = pairLossK0Configurations.applyINELSelectionAtGenLevel ;
4672- const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || applyGenInel) ? countNchGenEta1 (mcParticles) : -1 ;
4724+ const bool applyBandCut = pairLossK0Configurations.applyNchResponseBandCut ;
4725+ const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || applyGenInel || applyBandCut) ? countNchGenEta1 (mcParticles) : -1 ;
46734726
46744727 // Part 3: a self-contained generator-level study. It deliberately touches no
46754728 // reconstructed quantity in its event selection or in any of its axes: the
@@ -4887,10 +4940,12 @@ struct HStrangeCorrelation {
48874940 bestCollisionVtxZ = collision.posZ ();
48884941 bestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? collision.centFT0M () : collision.centFT0C ();
48894942 // with applyGenInel (pp only) the reconstructed INEL cuts are replaced by
4890- // Nch^gen >= 1, as in the Rec and Gen stages
4891- pairLossEventSelected = !pairLossK0Configurations.applyRecoEventSelection ||
4892- (masterConfigurations.doPPAnalysis ? isCollisionSelected (collision, !applyGenInel) && (!applyGenInel || isGenInelGt0 (nChGen))
4893- : isCollisionSelectedPbPb (collision, false ));
4943+ // Nch^gen >= 1, as in the Rec and Gen stages; the band cut acts on top,
4944+ // independently of applyRecoEventSelection
4945+ pairLossEventSelected = (!pairLossK0Configurations.applyRecoEventSelection ||
4946+ (masterConfigurations.doPPAnalysis ? isCollisionSelected (collision, !applyGenInel) && (!applyGenInel || isGenInelGt0 (nChGen))
4947+ : isCollisionSelectedPbPb (collision, false ))) &&
4948+ (!applyBandCut || isInNchResponseBand (nChGen, collision.multNTracksPVeta1 ()));
48944949 }
48954950 if (bestCollisionId < 0 ) {
48964951 return ;
@@ -4999,6 +5054,7 @@ struct HStrangeCorrelation {
49995054 bool genBestCollisionGoodZvtxFT0vsPV = false ;
50005055 bool genCollisionSelected = false ;
50015056 int genLargestNContributors = -1 ;
5057+ int genBestCollisionNTracksPVeta1 = -1 ;
50025058 uint32_t genBestCollisionTriggerPresenceMap = 0 ;
50035059 for (auto const & recCollision : recCollisions) {
50045060 if (genLargestNContributors >= recCollision.numContrib ()) {
@@ -5008,6 +5064,7 @@ struct HStrangeCorrelation {
50085064 genBestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? recCollision.centFT0M () : recCollision.centFT0C ();
50095065 // Both branches fill the Gen THn with this vertex position
50105066 genBestCollisionVtxZ = recCollision.posZ ();
5067+ genBestCollisionNTracksPVeta1 = recCollision.multNTracksPVeta1 ();
50115068 if (masterConfigurations.applyNewMCSelection ) {
50125069 genCollisionSelected = (masterConfigurations.doPPAnalysis && isCollisionSelected (recCollision, !applyGenInel)) ||
50135070 (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb (recCollision, false ));
@@ -5034,6 +5091,10 @@ struct HStrangeCorrelation {
50345091 (!masterConfigurations.requireGoodZvtxFT0vsPV || genBestCollisionGoodZvtxFT0vsPV) &&
50355092 genBestCollisionMultiplicity >= axisRanges[5 ][0 ] && genBestCollisionMultiplicity <= axisRanges[5 ][1 ];
50365093 }
5094+ // the band cut acts on top of either Gen event selection, on the best collision
5095+ if (applyBandCut) {
5096+ genEventSelected = genEventSelected && isInNchResponseBand (nChGen, genBestCollisionNTracksPVeta1);
5097+ }
50375098
50385099 if (genEventSelected && masterConfigurations.doCorrelationK0Short && TESTBIT (doCorrelation, IndexK0)) {
50395100 std::vector<uint32_t > genTriggerIndices;
@@ -5282,6 +5343,16 @@ struct HStrangeCorrelation {
52825343 pairLossComparison.nChGen = applyGenInel ? nChGen : -1 ;
52835344 pairLossComparison.active = true ;
52845345 for (auto const & recCollision : recCollisions) {
5346+ // recComparisonBestCollisionOnly: one reconstructed collision per MC collision,
5347+ // the best one, as in the Gen stage
5348+ if (pairLossK0Configurations.recComparisonBestCollisionOnly && recCollision.globalIndex () != bestCollisionId) {
5349+ continue ;
5350+ }
5351+ // The band cut tests every reconstructed collision against the Nch^gen of
5352+ // this MC collision, so it also rejects split and duplicate vertices
5353+ if (applyBandCut && !isInNchResponseBand (nChGen, recCollision.multNTracksPVeta1 ())) {
5354+ continue ;
5355+ }
52855356 const auto recTriggerSlice = triggerTracks.sliceBy (collisionSliceTracks, recCollision.globalIndex ());
52865357 const auto recV0Slice = associatedV0s.sliceBy (collisionSliceV0s, recCollision.globalIndex ());
52875358 const auto recTrackSlice = tracks.sliceBy (pairLossTracksPerCollision, recCollision.globalIndex ());
@@ -5306,14 +5377,44 @@ struct HStrangeCorrelation {
53065377 // responses. The event selection is the one runSameEventHV0s applies before
53075378 // its INEL cut: trigger presence + isCollisionSelected (isCollisionSelectedNoInel)
53085379 auto runEventRecoEfficiencyStudy = [&]() {
5309- // Responses: one entry per reconstructed collision, as in the Rec path
5380+ // Best collision: the one with the most PV contributors, as in the Rec comparison
5381+ int largestNContributors = -1 ;
5382+ bool bestCollisionSelectedNoInel = false ;
5383+ bool bestCollisionSelected = false ;
5384+ int bestCollisionNTracksPVeta1 = -1 ;
5385+ for (auto const & collision : recCollisions) {
5386+ if (collision.numContrib () <= largestNContributors) {
5387+ continue ;
5388+ }
5389+ largestNContributors = collision.numContrib ();
5390+ bestCollisionSelectedNoInel = isCollisionSelectedNoInel (collision);
5391+ // reconstructed-level INEL cuts; the Pb-Pb event selection has none
5392+ bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected (collision));
5393+ bestCollisionNTracksPVeta1 = collision.multNTracksPVeta1 ();
5394+ }
5395+
5396+ // Nch^gen response, and with applyNchResponseBandCut the part of it inside the band
5397+ auto fillNchGenResponse = [&](int nTracksPVeta1) {
5398+ histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1" ), nChGen, nTracksPVeta1);
5399+ if (applyBandCut && isInNchResponseBand (nChGen, nTracksPVeta1)) {
5400+ histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand" ), nChGen, nTracksPVeta1);
5401+ }
5402+ };
5403+
5404+ // Responses: one entry per reconstructed collision, as in the Rec path, except
5405+ // the Nch^gen response with nchGenResponseBestCollisionOnly (best collision only)
53105406 for (auto const & collision : recCollisions) {
53115407 if (isCollisionSelectedNoInel (collision)) {
5312- histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1" ), nChGen, collision.multNTracksPVeta1 ());
5408+ if (!pairLossK0Configurations.nchGenResponseBestCollisionOnly ) {
5409+ fillNchGenResponse (collision.multNTracksPVeta1 ());
5410+ }
53135411 histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M" ), collision.multNTracksPVeta1 (), collision.centFT0M ());
53145412 histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M" ), collision.multNTracksPVeta1 (), collision.multFT0M ());
53155413 }
53165414 }
5415+ if (pairLossK0Configurations.nchGenResponseBestCollisionOnly && bestCollisionSelectedNoInel) {
5416+ fillNchGenResponse (bestCollisionNTracksPVeta1);
5417+ }
53175418
53185419 // Efficiency: one entry per MC collision, evaluated on the best collision
53195420 if (std::abs (mcCollision.posZ ()) > masterConfigurations.zVertexCut ) {
@@ -5325,20 +5426,11 @@ struct HStrangeCorrelation {
53255426 return ;
53265427 }
53275428 histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl" ), nChGen);
5328- int largestNContributors = -1 ;
5329- bool bestCollisionSelectedNoInel = false ;
5330- bool bestCollisionSelected = false ;
5331- for (auto const & collision : recCollisions) {
5332- if (collision.numContrib () <= largestNContributors) {
5333- continue ;
5334- }
5335- largestNContributors = collision.numContrib ();
5336- bestCollisionSelectedNoInel = isCollisionSelectedNoInel (collision);
5337- // reconstructed-level INEL cuts; the Pb-Pb event selection has none
5338- bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected (collision));
5339- }
53405429 if (bestCollisionSelectedNoInel) {
53415430 histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel" ), nChGen);
5431+ if (applyBandCut && isInNchResponseBand (nChGen, bestCollisionNTracksPVeta1)) {
5432+ histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand" ), nChGen);
5433+ }
53425434 }
53435435 if (bestCollisionSelected) {
53445436 histos.fill (HIST (" PairLossK0/EventRecoEfficiency/hNchGenEvSel" ), nChGen);
0 commit comments