2929#include " PWGHF/DataModel/CandidateSelectionTables.h"
3030#include " PWGHF/DataModel/TrackIndexSkimmingTables.h"
3131#include " PWGHF/Utils/utilsEvSelHf.h"
32+ #include " PWGHF/Utils/utilsUpcHf.h"
3233#include " PWGUD/Core/UPCHelpers.h"
3334
3435#include " Common/Core/RecoDecay.h"
@@ -67,6 +68,7 @@ using namespace o2::framework::expressions;
6768using namespace o2 ::hf_centrality;
6869using namespace o2 ::hf_occupancy;
6970using namespace o2 ::hf_evsel;
71+ using namespace o2 ::analysis::hf_upc;
7072
7173// / Λc± → p± K∓ π± analysis task
7274struct HfTaskLc {
@@ -84,7 +86,8 @@ struct HfTaskLc {
8486 Configurable<std::string> ccdbPathGrp{" ccdbPathGrp" , " GLO/GRP/GRP" , " Path of the grp file (Run 2)" };
8587 Configurable<std::string> ccdbPathGrpMag{" ccdbPathGrpMag" , " GLO/Config/GRPMagField" , " CCDB path of the GRPMagField object (Run 3)" };
8688
87- HfEventSelection hfEvSel; // event selection and monitoring
89+ HfEventSelection hfEvSel; // event selection and monitoring
90+ HfUpcGapThresholds upcThresholds; // UPC gap determination thresholds
8891 SliceCache cache;
8992 Service<o2::ccdb::BasicCCDBManager> ccdb;
9093
@@ -121,7 +124,11 @@ struct HfTaskLc {
121124 ConfigurableAxis thnConfigAxisNumPvContr{" thnConfigAxisNumPvContr" , {200 , -0.5 , 199.5 }, " Number of PV contributors" };
122125 ConfigurableAxis thnConfigAxisOccupancy{" thnConfigAxisOccupancy" , {14 , 0 , 14000 }, " axis for centrality" };
123126 ConfigurableAxis thnConfigAxisProperLifetime{" thnConfigAxisProperLifetime" , {200 , 0 , 2 }, " Proper lifetime, ps" };
124-
127+ ConfigurableAxis thnConfigAxisGapType{" thnConfigAxisGapType" , {7 , -1.5 , 5.5 }, " axis for UPC gap type (see TrueGap enum in o2::aod::sgselector)" };
128+ ConfigurableAxis thnConfigAxisFT0{" thnConfigAxisFT0" , {1001 , -1.5 , 999.5 }, " axis for FT0 amplitude (a.u.)" };
129+ ConfigurableAxis thnConfigAxisFV0A{" thnConfigAxisFV0A" , {2001 , -1.5 , 1999.5 }, " axis for FV0-A amplitude (a.u.)" };
130+ ConfigurableAxis thnConfigAxisFDD{" thnConfigAxisFDD" , {200 , 0 ., 4000 .}, " axis for FDD amplitude (a.u.)" };
131+ ConfigurableAxis thnConfigAxisZN{" thnConfigAxisZN" , {510 , -1.5 , 49.5 }, " axis for ZN energy (a.u.)" };
125132 HistogramRegistry registry{" registry" , {}};
126133 HistogramRegistry qaRegistry{" QAHistos" , {}, OutputObjHandlingPolicy::AnalysisObject};
127134
@@ -145,12 +152,6 @@ struct HfTaskLc {
145152 NonPrompt
146153 };
147154
148- enum class GapType {
149- GapA = 0 ,
150- GapC = 1 ,
151- DoubleGap = 2 ,
152- };
153-
154155 void init (InitContext&)
155156 {
156157 const std::array<bool , 14 > doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, doprocessDataWithMl, doprocessDataWithMlWithFT0C, doprocessDataWithMlWithFT0M, doprocessDataWithMlWithUpc, doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M, doprocessMcWithMl, doprocessMcWithMlWithFT0C, doprocessMcWithMlWithFT0M, doprocessDataStdWithUpc};
@@ -265,10 +266,7 @@ struct HfTaskLc {
265266 if (isUpc) {
266267 qaRegistry.add (" Data/fitInfo/ampFT0A_vs_ampFT0C" , " FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)" , {HistType::kTH2F , {{1500 , 0 ., 1500 }, {1500 , 0 ., 1500 }}});
267268 qaRegistry.add (" Data/zdc/energyZNA_vs_energyZNC" , " ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)" , {HistType::kTH2F , {{200 , 0 ., 20 }, {200 , 0 ., 20 }}});
268- qaRegistry.add (" Data/hUpcGapAfterSelection" , " UPC gap type after selection;Gap side;Counts" , {HistType::kTH1F , {{3 , -0.5 , 2.5 }}});
269- qaRegistry.get <TH1>(HIST (" Data/hUpcGapAfterSelection" ))->GetXaxis ()->SetBinLabel (static_cast <int >(GapType::GapA) + 1 , " A" );
270- qaRegistry.get <TH1>(HIST (" Data/hUpcGapAfterSelection" ))->GetXaxis ()->SetBinLabel (static_cast <int >(GapType::GapC) + 1 , " C" );
271- qaRegistry.get <TH1>(HIST (" Data/hUpcGapAfterSelection" ))->GetXaxis ()->SetBinLabel (static_cast <int >(GapType::DoubleGap) + 1 , " Double" );
269+ qaRegistry.add (" Data/hUpcGapAfterSelection" , " UPC gap type after selection;Gap side;Counts" , {HistType::kTH1F , {{7 , -1.5 , 5.5 }}});
272270 }
273271 if (fillTHn) {
274272 const AxisSpec thnAxisMass{thnConfigAxisMass, " inv. mass (p K #pi) (GeV/#it{c}^{2})" };
@@ -289,26 +287,40 @@ struct HfTaskLc {
289287 const AxisSpec thnAxisTracklets{thnConfigAxisNumPvContr, " Number of PV contributors" };
290288 const AxisSpec thnAxisOccupancy{thnConfigAxisOccupancy, " Occupancy" };
291289 const AxisSpec thnAxisProperLifetime{thnConfigAxisProperLifetime, " T_{proper} (ps)" };
290+ const AxisSpec thnAxisGapType{thnConfigAxisGapType, " Gap type" };
291+ const AxisSpec thnAxisFT0A{thnConfigAxisFT0, " FT0-A amplitude" };
292+ const AxisSpec thnAxisFT0C{thnConfigAxisFT0, " FT0-C amplitude" };
293+ const AxisSpec thnAxisFV0A{thnConfigAxisFV0A, " FV0-A amplitude" };
294+ const AxisSpec thnAxisFDDA{thnConfigAxisFDD, " FDD-A amplitude" };
295+ const AxisSpec thnAxisFDDC{thnConfigAxisFDD, " FDD-C amplitude" };
296+ const AxisSpec thnAxisZNA{thnConfigAxisZN, " ZNA energy" };
297+ const AxisSpec thnAxisZNC{thnConfigAxisZN, " ZNC energy" };
292298
293299 bool const isDataWithMl = doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M || doprocessDataWithMlWithUpc;
294300 bool const isMcWithMl = doprocessMcWithMl || doprocessMcWithMlWithFT0C || doprocessMcWithMlWithFT0M;
295301 bool const isDataStd = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M || doprocessDataStdWithUpc;
296302 bool const isMcStd = doprocessMcStd || doprocessMcStdWithFT0C || doprocessMcStdWithFT0M;
297303
298- std::vector<AxisSpec> axesStd, axesWithBdt, axesGen;
304+ std::vector<AxisSpec> axesStd, axesWithBdt, axesGen, axesUpc, axesUpcWithBdt ;
299305
300- if (isDataStd) {
306+ if (isDataStd && !isUpc ) {
301307 axesStd = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets};
302308 }
309+ if (isDataStd && isUpc) {
310+ axesUpc = {thnAxisMass, thnAxisPt, thnAxisRapidity, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets, thnAxisGapType, thnAxisFT0A, thnAxisFT0C, thnAxisFV0A, thnAxisFDDA, thnAxisFDDC, thnAxisZNA, thnAxisZNC};
311+ }
303312 if (isMcStd) {
304313 axesStd = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets, thnAxisPtB, thnAxisCanType};
305314 }
306315 if (isMcStd || isMcWithMl) {
307316 axesGen = {thnAxisPt, thnAxisCentrality, thnAxisY, thnAxisTracklets, thnAxisPtB, thnAxisCanType};
308317 }
309- if (isDataWithMl) {
318+ if (isDataWithMl && !isUpc ) {
310319 axesWithBdt = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets};
311320 }
321+ if (isDataWithMl && isUpc) {
322+ axesUpcWithBdt = {thnAxisMass, thnAxisPt, thnAxisRapidity, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets, thnAxisGapType, thnAxisFT0A, thnAxisFT0C, thnAxisFV0A, thnAxisFDDA, thnAxisFDDC, thnAxisZNA, thnAxisZNC};
323+ }
312324 if (isMcWithMl) {
313325 axesWithBdt = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets, thnAxisPtB, thnAxisCanType};
314326 }
@@ -327,8 +339,13 @@ struct HfTaskLc {
327339 }
328340 }
329341 }
330-
331- if (isDataWithMl) {
342+ if (isUpc) {
343+ if (isDataStd) {
344+ registry.add (" hnLcUpcVars" , " THn for Lambdac candidates for Data in UPC" , HistType::kTHnSparseF , axesUpc);
345+ } else if (isDataWithMl) {
346+ registry.add (" hnLcUpcVarsWithBdt" , " THn for Lambdac candidates with BDT scores for data in UPC" , HistType::kTHnSparseF , axesUpcWithBdt);
347+ }
348+ } else if (isDataWithMl) {
332349 registry.add (" hnLcVarsWithBdt" , " THn for Lambdac candidates with BDT scores for data with ML" , HistType::kTHnSparseF , axesWithBdt);
333350 } else if (isMcWithMl) {
334351 registry.add (" hnLcVarsWithBdt" , " THn for Lambdac candidates with BDT scores for mc with ML" , HistType::kTHnSparseF , axesWithBdt);
@@ -727,36 +744,85 @@ struct HfTaskLc {
727744 // / at least one event selection not satisfied --> reject the candidate
728745 continue ;
729746 }
747+ const auto thisCollId = collision.globalIndex ();
748+ const auto & groupedLcCandidates = candidates.sliceBy (candLcPerCollision, thisCollId);
749+ const auto numPvContributors = collision.numContrib ();
730750 const auto & bc = collision.template bc_as <BCsType>();
751+
752+ // Determine gap type using SGSelector with BC range checking
753+ const auto gapResult = hf_upc::determineGapType (collision, bcs, upcThresholds);
754+ const int gap = gapResult.value ;
755+
756+ // Use the BC with FIT activity if available from SGSelector
757+ auto bcForUPC = bc;
758+ if (gapResult.bc ) {
759+ bcForUPC = *(gapResult.bc );
760+ }
761+
762+ // Get FIT information from the UPC BC
731763 upchelpers::FITInfo fitInfo{};
732- udhelpers::getFITinfo (fitInfo, bc, bcs, ft0s, fv0as, fdds);
764+ udhelpers::getFITinfo (fitInfo, bcForUPC, bcs, ft0s, fv0as, fdds);
765+
766+ // Get ZDC energies if available (extract once and reuse)
767+ const bool hasZdc = bcForUPC.has_zdc ();
768+ float zdcEnergyZNA = -1 .f ;
769+ float zdcEnergyZNC = -1 .f ;
733770
734- GapType gap = GapType::DoubleGap;
735- if (bc.has_zdc ()) {
736- const auto zdc = bc.zdc ();
771+ if (hasZdc) {
772+ const auto zdc = bcForUPC.zdc ();
773+ zdcEnergyZNA = zdc.energyCommonZNA ();
774+ zdcEnergyZNC = zdc.energyCommonZNC ();
737775 qaRegistry.fill (HIST (" Data/fitInfo/ampFT0A_vs_ampFT0C" ), fitInfo.ampFT0A , fitInfo.ampFT0C );
738- qaRegistry.fill (HIST (" Data/zdc/energyZNA_vs_energyZNC" ), zdc.energyCommonZNA (), zdc.energyCommonZNC ());
739- gap = determineGapType (fitInfo.ampFT0A , fitInfo.ampFT0C , zdc.energyCommonZNA (), zdc.energyCommonZNC ());
776+ qaRegistry.fill (HIST (" Data/zdc/energyZNA_vs_energyZNC" ), zdcEnergyZNA, zdcEnergyZNC);
740777 qaRegistry.fill (HIST (" Data/hUpcGapAfterSelection" ), static_cast <int >(gap));
741778 }
742- if (gap == GapType::GapA || gap == GapType::GapC) {
743- fillHistosData<FillMl>(collision, candidates);
744- }
745- }
746- }
779+ for (const auto & candidate : groupedLcCandidates) {
780+ if (!(candidate.hfflag () & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) {
781+ continue ;
782+ }
783+ if (yCandRecoMax >= 0 . && std::abs (HfHelper::yLc (candidate)) > yCandRecoMax) {
784+ continue ;
785+ }
786+ const auto pt = candidate.pt ();
787+ const auto ptProng0 = candidate.ptProng0 ();
788+ const auto ptProng1 = candidate.ptProng1 ();
789+ const auto ptProng2 = candidate.ptProng2 ();
790+ const auto decayLength = candidate.decayLength ();
791+ const auto chi2PCA = candidate.chi2PCA ();
792+ const auto cpa = candidate.cpa ();
793+ const auto rapidity = std::abs (HfHelper::yLc (candidate));
747794
748- GapType determineGapType (float FT0A, float FT0C, float ZNA, float ZNC)
749- {
750- constexpr float FT0AThreshold = 100.0 ;
751- constexpr float FT0CThreshold = 50.0 ;
752- constexpr float ZDCThreshold = 1.0 ;
753- if (FT0A < FT0AThreshold && FT0C > FT0CThreshold && ZNA < ZDCThreshold && ZNC > ZDCThreshold) {
754- return GapType::GapA;
755- }
756- if (FT0A > FT0AThreshold && FT0C < FT0CThreshold && ZNA > ZDCThreshold && ZNC < ZDCThreshold) {
757- return GapType::GapC;
795+ if (fillTHn) {
796+ double outputBkg (-1 ), outputPrompt (-1 ), outputFD (-1 );
797+
798+ auto fillTHnData = [&](bool isPKPi) {
799+ const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi (candidate) : HfHelper::invMassLcToPiKP (candidate);
800+
801+ if constexpr (FillMl) {
802+ const auto & mlProb = isPKPi ? candidate.mlProbLcToPKPi () : candidate.mlProbLcToPiKP ();
803+ if (mlProb.size () == NumberOfMlClasses) {
804+ outputBkg = mlProb[MlClassBackground]; // / bkg score
805+ outputPrompt = mlProb[MlClassPrompt]; // / prompt score
806+ outputFD = mlProb[MlClassNonPrompt]; // / non-prompt score
807+ }
808+ // / Fill the ML outputScores and variables of candidate
809+ std::vector<double > valuesToFill{massLc, pt, rapidity, outputBkg, outputPrompt, outputFD, static_cast <double >(numPvContributors), static_cast <double >(gap), static_cast <double >(fitInfo.ampFT0A ), static_cast <double >(fitInfo.ampFT0C ), static_cast <double >(fitInfo.ampFV0A ), static_cast <double >(fitInfo.ampFDDA ), static_cast <double >(fitInfo.ampFDDC ), static_cast <double >(zdcEnergyZNA), static_cast <double >(zdcEnergyZNC)};
810+ registry.get <THnSparse>(HIST (" hnLcUpcVarsWithBdt" ))->Fill (valuesToFill.data ());
811+ } else {
812+ std::vector<double > valuesToFill{massLc, pt, rapidity, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, static_cast <double >(numPvContributors), static_cast <double >(gap), static_cast <double >(fitInfo.ampFT0A ), static_cast <double >(fitInfo.ampFT0C ), static_cast <double >(fitInfo.ampFV0A ), static_cast <double >(fitInfo.ampFDDA ), static_cast <double >(fitInfo.ampFDDC ), static_cast <double >(zdcEnergyZNA), static_cast <double >(zdcEnergyZNC)};
813+ registry.get <THnSparse>(HIST (" hnLcUpcVars" ))->Fill (valuesToFill.data ());
814+ }
815+ };
816+
817+ if (candidate.isSelLcToPKPi () >= selectionFlagLc) {
818+ fillTHnData (true );
819+ }
820+ if (candidate.isSelLcToPiKP () >= selectionFlagLc) {
821+ fillTHnData (false );
822+ }
823+ }
824+ }
758825 }
759- return GapType::DoubleGap;
760826 }
761827
762828 // / Run the analysis on MC data
0 commit comments