@@ -1506,6 +1506,8 @@ struct HfTrackIndexSkimCreator {
15061506 std::array<std::vector<double >, kN2ProngDecays > binsPt2Prong{};
15071507 std::array<LabeledArray<double >, kN3ProngDecays > cut3Prong{};
15081508 std::array<std::vector<double >, kN3ProngDecays > binsPt3Prong{};
1509+ LabeledArray<double > cutsDstar{};
1510+ std::vector<double > binsPtDstar{};
15091511
15101512 // ML response
15111513 o2::analysis::MlResponse<float > hfMlResponse2Prongs; // only D0
@@ -1584,6 +1586,9 @@ struct HfTrackIndexSkimCreator {
15841586 // cuts for 3-prong decays retrieved by json. the order must be then one in hf_cand_3prong::DecayType
15851587 cut3Prong = {config.cutsDplusToPiKPi , config.cutsLcToPKPi , config.cutsDsToKKPi , config.cutsXicToPKPi , config.cutsCdToDeKPi , config.cutsCtToTrKPi , config.cutsChToHeKPi , config.cutsCaToAlKPi };
15861588 binsPt3Prong = {config.binsPtDplusToPiKPi , config.binsPtLcToPKPi , config.binsPtDsToKKPi , config.binsPtXicToPKPi , config.binsPtCdToDeKPi , config.binsPtCtToTrKPi , config.binsPtChToHeKPi , config.binsPtCaToAlKPi };
1589+ // D* cuts retrieved by json
1590+ cutsDstar = config.cutsDstarToD0Pi ;
1591+ binsPtDstar = config.binsPtDstarToD0Pi ;
15871592
15881593 df2.setPropagateToPCA (config.propagateToPCA );
15891594 df2.setMaxR (config.maxR );
@@ -1724,6 +1729,15 @@ struct HfTrackIndexSkimCreator {
17241729 }
17251730 }
17261731
1732+ // / One track of the collision under study, propagated to that collision's primary vertex.
1733+ // / \tparam TTrackIndex is the iterator type of the track-collision associations
1734+ template <typename TTrackIndex>
1735+ struct HfPropagatedSoftPion {
1736+ TTrackIndex trackIndex; // /< track-collision association, carries isSelProng and isIdentifiedPid
1737+ int64_t trackGlobalIndex{-1 }; // /< track global index
1738+ std::array<float , 3 > pVec{}; // /< momentum at the PCA to the primary vertex
1739+ };
1740+
17271741 // / One track of the collision under study, propagated to that collision's primary vertex.
17281742 // / \tparam TTrackIndex is the iterator type of the track-collision associations
17291743 // / \tparam TTrack is the iterator type of the track table
@@ -1741,21 +1755,44 @@ struct HfTrackIndexSkimCreator {
17411755 // / \param collision is the collision under study
17421756 // / \param trackIndices are the track associations of this collision
17431757 // / \param prongs is the cache to be filled, in the order of trackIndices
1744- template <typename TTracks, typename TCollision, typename TTrackIndices, typename TProng>
1758+ template <bool IsSoftPion, typename TTracks, typename TCollision, typename TTrackIndices, typename TProng>
17451759 void fillPropagatedProngCache (TCollision const & collision, TTrackIndices const & trackIndices, std::vector<TProng>& prongs)
17461760 {
17471761 prongs.clear ();
17481762 const auto thisCollId = collision.globalIndex ();
17491763 for (auto trackIndex = trackIndices.begin (); trackIndex != trackIndices.end (); ++trackIndex) {
17501764 const auto track = trackIndex.template track_as <TTracks>();
1751- auto & prong = prongs.emplace_back (TProng{trackIndex, track, getTrackParCov (track), track.pVector (), {track.dcaXY (), track.dcaZ ()}});
1752- if (thisCollId != track.collisionId ()) { // this is not the "default" collision for this track, we have to re-propagate it
1753- o2::base::Propagator::Instance ()->propagateToDCABxByBz ({collision.posX (), collision.posY (), collision.posZ ()}, prong.trackParVar , 2 .f , noMatCorr, &prong.dcaInfo );
1754- getPxPyPz (prong.trackParVar , prong.pVec );
1765+ if constexpr (IsSoftPion) {
1766+ std::array<float , 3 > pVec = track.pVector ();
1767+ if (thisCollId != track.collisionId ()) { // this is not the "default" collision for this track, we have to re-propagate it
1768+ auto trackParCov = getTrackParCov (track);
1769+ std::array<float , 2 > dcaInfo{};
1770+ o2::base::Propagator::Instance ()->propagateToDCABxByBz ({collision.posX (), collision.posY (), collision.posZ ()}, trackParCov, 2 .f , noMatCorr, &dcaInfo);
1771+ getPxPyPz (trackParCov, pVec);
1772+ }
1773+ prongs.push_back (TProng{trackIndex, track.globalIndex (), pVec});
1774+ } else {
1775+ auto & prong = prongs.emplace_back (TProng{trackIndex, track, getTrackParCov (track), track.pVector (), {track.dcaXY (), track.dcaZ ()}});
1776+ if (thisCollId != track.collisionId ()) { // this is not the "default" collision for this track, we have to re-propagate it
1777+ o2::base::Propagator::Instance ()->propagateToDCABxByBz ({collision.posX (), collision.posY (), collision.posZ ()}, prong.trackParVar , 2 .f , noMatCorr, &prong.dcaInfo );
1778+ getPxPyPz (prong.trackParVar , prong.pVec );
1779+ }
17551780 }
17561781 }
17571782 }
17581783
1784+ // / Overloaded function of fillPropagatedProngCache to handle optional track indices.
1785+ // / \param collision is the collision under study
1786+ // / \param trackIndices are the track associations of this collision
1787+ // / \param prongs is the cache to be filled, in the order of trackIndices
1788+ template <bool IsSoftPion, typename TTracks, typename TCollision, typename TTrackIndices, typename TProng>
1789+ void fillPropagatedProngCache (TCollision const & collision, std::optional<TTrackIndices> const & trackIndices, std::vector<TProng>& prongs)
1790+ {
1791+ if (trackIndices.has_value ()) {
1792+ fillPropagatedProngCache<IsSoftPion, TTracks>(collision, *trackIndices, prongs);
1793+ }
1794+ }
1795+
17591796 // / Method to perform selections for 2-prong candidates before vertex reconstruction
17601797 // / \param pVecTrack0 is the momentum array of the first daughter track
17611798 // / \param pVecTrack1 is the momentum array of the second daughter track
@@ -1827,10 +1864,10 @@ struct HfTrackIndexSkimCreator {
18271864
18281865 // additional check for D0 to be used in D* finding
18291866 if (iDecay2P == hf_cand_2prong::DecayType::D0ToPiK && config.doDstar && TESTBIT (isSelected, iDecay2P)) {
1830- const auto binPtDstar = findBin (config. binsPtDstarToD0Pi , pt * 1.2 ); // assuming the D* pT about 20% higher than the one of the D0 to be safe
1867+ const auto binPtDstar = findBin (&binsPtDstar , pt * 1.2 ); // assuming the D* pT about 20% higher than the one of the D0 to be safe
18311868 if (binPtDstar >= 0 ) {
18321869 whichHypo[kN2ProngDecays ] = whichHypo[hf_cand_2prong::DecayType::D0ToPiK];
1833- const double deltaMass = config. cutsDstarToD0Pi -> get (binPtDstar, 1u );
1870+ const double deltaMass = cutsDstar. get (binPtDstar, 1u );
18341871
18351872 if (TESTBIT (whichHypo[iDecay2P], 0 ) && (massHypos[0 ] > (MassD0 + deltaMass) * (MassD0 + deltaMass) || massHypos[0 ] < (MassD0 - deltaMass) * (MassD0 - deltaMass))) {
18361873 CLRBIT (whichHypo[kN2ProngDecays ], 0 );
@@ -2156,18 +2193,18 @@ struct HfTrackIndexSkimCreator {
21562193 // / \param pVecTrack1 is the momentum array of the second daughter track (opposite charge)
21572194 // / \param pVecTrack2 is the momentum array of the third daughter track (same charge)
21582195 // / \param cutStatus is the cut status (filled only in case of debug)
2196+ // / \param invMassD0 is the invariant mass of the D0 candidate
21592197 // / \param deltaMass is the M(Kpipi) - M(Kpi) value to be filled in the control histogram
21602198 // / \return a bitmap with selection outcome
21612199 template <typename T1 , typename T2 >
2162- uint8_t applySelectionDstar (T1 const & pVecTrack0, T1 const & pVecTrack1, T1 const & pVecTrack2, uint8_t & cutStatus, T2 & deltaMass)
2200+ uint8_t applySelectionDstar (T1 const & pVecTrack0, T1 const & pVecTrack1, T1 const & pVecTrack2, uint8_t & cutStatus, T2 invMassD0, T2 & deltaMass)
21632201 {
21642202 uint8_t isSelected{1 };
21652203 const std::array arrMom{pVecTrack0, pVecTrack1, pVecTrack2};
2166- const std::array arrMomD0{pVecTrack0, pVecTrack1};
21672204 const auto pt = RecoDecay::pt (pVecTrack0, pVecTrack1, pVecTrack2) + config.ptTolerance ; // add tolerance because of no reco decay vertex
21682205
21692206 // pT
2170- const auto binPt = findBin (config. binsPtDstarToD0Pi , pt);
2207+ const auto binPt = findBin (&binsPtDstar , pt);
21712208 // return immediately if it is outside the defined pT bins
21722209 if (binPt == -1 ) {
21732210 isSelected = 0 ;
@@ -2178,8 +2215,7 @@ struct HfTrackIndexSkimCreator {
21782215 }
21792216
21802217 // D0 mass
2181- const double deltaMassD0 = config.cutsDstarToD0Pi ->get (binPt, 1u ); // 1u == deltaMassD0Index
2182- const double invMassD0 = RecoDecay::m (arrMomD0, std::array{MassPiPlus, MassKPlus});
2218+ const double deltaMassD0 = cutsDstar.get (binPt, 1u ); // 1u == deltaMassD0Index
21832219 if (std::abs (invMassD0 - MassD0) > deltaMassD0) {
21842220 isSelected = 0 ;
21852221 if (config.debug ) {
@@ -2189,7 +2225,7 @@ struct HfTrackIndexSkimCreator {
21892225 }
21902226
21912227 // D*+ mass
2192- const double maxDeltaMass = config. cutsDstarToD0Pi -> get (binPt, 0u ); // 0u == deltaMassIndex
2228+ const double maxDeltaMass = cutsDstar. get (binPt, 0u ); // 0u == deltaMassIndex
21932229 const double invMassDstar = RecoDecay::m (arrMom, std::array{MassPiPlus, MassKPlus, MassPiPlus});
21942230 deltaMass = invMassDstar - invMassD0;
21952231 if (deltaMass > maxDeltaMass) {
@@ -2356,9 +2392,17 @@ struct HfTrackIndexSkimCreator {
23562392 using PropagatedProng = HfPropagatedProng<TrackIndexIterator, decltype (tracks.rawIteratorAt (0 ))>;
23572393 std::vector<PropagatedProng> prongsPos{};
23582394 std::vector<PropagatedProng> prongsNeg{};
2395+ using SoftPionIterator = decltype (positiveSoftPions->sliceByCached (aod::track::collisionId, 0 , cache).begin ());
2396+ using PropagatedSoftPi = HfPropagatedSoftPion<SoftPionIterator>;
2397+ std::vector<PropagatedSoftPi> softPionsPos{};
2398+ std::vector<PropagatedSoftPi> softPionsNeg{};
23592399
2400+ bool softPiPosCacheFilled{false }, softPiNegCacheFilled{false };
23602401 for (const auto & collision : collisions) {
23612402
2403+ softPiPosCacheFilled = false ;
2404+ softPiNegCacheFilled = false ;
2405+
23622406 // / retrieve PV contributors for the current collision
23632407 std::vector<int64_t > vecPvContributorGlobId{};
23642408 std::vector<o2::track::TrackParCov> vecPvContributorTrackParCov{};
@@ -2438,8 +2482,8 @@ struct HfTrackIndexSkimCreator {
24382482 int lastFilledD0 = -1 ; // index to be filled in table for D* mesons
24392483
24402484 // propagate each track to this collision's PV once, instead of once per pair or triplet
2441- fillPropagatedProngCache<TTracks>(collision, groupedTrackIndicesPos1, prongsPos);
2442- fillPropagatedProngCache<TTracks>(collision, groupedTrackIndicesNeg1, prongsNeg);
2485+ fillPropagatedProngCache<false , TTracks>(collision, groupedTrackIndicesPos1, prongsPos);
2486+ fillPropagatedProngCache<false , TTracks>(collision, groupedTrackIndicesNeg1, prongsNeg);
24432487
24442488 for (std::size_t iPos1 = 0 ; iPos1 < prongsPos.size (); ++iPos1) {
24452489 const auto & trackIndexPos1 = prongsPos[iPos1].trackIndex ;
@@ -3216,32 +3260,35 @@ struct HfTrackIndexSkimCreator {
32163260 }
32173261 }
32183262
3219- if (config.doDstar && TESTBIT (isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance ) * 1.2 > config. binsPtDstarToD0Pi -> at ( 0 ) && whichHypo2Prong[kN2ProngDecays ] != 0 ) { // o2-linter: disable="magic-number" (see comment below)
3220- // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8)
3263+ if (config.doDstar && TESTBIT (isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance ) * 1.2 > binsPtDstar[ 0 ] && whichHypo2Prong[kN2ProngDecays ] != 0 ) { // o2-linter: disable="magic-number" (see comment below)
3264+ // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8)
32213265 // second loop over positive tracks
32223266 if (TESTBIT (whichHypo2Prong[kN2ProngDecays ], 0 ) && (!config.applyKaonPidIn3Prongs || TESTBIT (trackIndexNeg1.isIdentifiedPid (), ChannelKaonPid))) { // only for D0 candidates; moreover if kaon PID enabled, apply to the negative track
3223- if (!groupedTrackIndicesSoftPionsPos) {
3267+
3268+ // Compute D0 invariant mass for all D* candidates
3269+ const std::array arrMomD0{pVecTrackPos1, pVecTrackNeg1};
3270+ const float invMassD0 = RecoDecay::m (arrMomD0, std::array{MassPiPlus, MassKPlus});
3271+
3272+ if (!softPiPosCacheFilled) {
32243273 groupedTrackIndicesSoftPionsPos.emplace (positiveSoftPions->sliceByCached (aod::track::collisionId, collision.globalIndex (), cache));
3274+ fillPropagatedProngCache<true , TTracks>(collision, groupedTrackIndicesSoftPionsPos, softPionsPos);
3275+ softPiPosCacheFilled = true ;
32253276 }
3226- for (auto trackIndexPos2 = groupedTrackIndicesSoftPionsPos->begin (); trackIndexPos2 != groupedTrackIndicesSoftPionsPos->end (); ++trackIndexPos2) {
3227- if (trackIndexPos2 == trackIndexPos1) {
3277+
3278+ for (std::size_t iPos2 = 0 ; iPos2 < softPionsPos.size (); ++iPos2) {
3279+ if (softPionsPos[iPos2].trackIndex == trackIndexPos1) {
32283280 continue ;
32293281 }
3230- auto trackPos2 = trackIndexPos2.template track_as <TTracks>();
3231- std::array pVecTrackPos2{trackPos2.pVector ()};
3232- if (thisCollId != trackPos2.collisionId ()) { // this is not the "default" collision for this track, we have to re-propagate it
3233- auto trackParVarPos2 = getTrackParCov (trackPos2);
3234- std::array dcaInfoPos2{trackPos2.dcaXY (), trackPos2.dcaZ ()};
3235- o2::base::Propagator::Instance ()->propagateToDCABxByBz ({collision.posX (), collision.posY (), collision.posZ ()}, trackParVarPos2, 2 .f , noMatCorr, &dcaInfoPos2);
3236- getPxPyPz (trackParVarPos2, pVecTrackPos2);
3237- }
3282+
3283+ const auto & trackGlobalIndexPos2 = softPionsPos[iPos2].trackGlobalIndex ;
3284+ const auto & pVecTrackPos2 = softPionsPos[iPos2].pVec ;
32383285
32393286 uint8_t isSelectedDstar{0 };
32403287 uint8_t cutStatus{BIT (kNCutsDstar ) - 1 };
32413288 float deltaMass{-1 .};
3242- isSelectedDstar = applySelectionDstar (pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections
3289+ isSelectedDstar = applySelectionDstar (pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections
32433290 if (isSelectedDstar) {
3244- rowTrackIndexDstar (thisCollId, trackPos2. globalIndex () , lastFilledD0);
3291+ rowTrackIndexDstar (thisCollId, trackGlobalIndexPos2 , lastFilledD0);
32453292 if (config.fillHistograms ) {
32463293 registry.fill (HIST (" hMassDstarToD0Pi" ), deltaMass);
32473294 }
@@ -3259,28 +3306,31 @@ struct HfTrackIndexSkimCreator {
32593306
32603307 // second loop over negative tracks
32613308 if (TESTBIT (whichHypo2Prong[kN2ProngDecays ], 1 ) && (!config.applyKaonPidIn3Prongs || TESTBIT (trackIndexPos1.isIdentifiedPid (), ChannelKaonPid))) { // only for D0bar candidates; moreover if kaon PID enabled, apply to the positive track
3262- if (!groupedTrackIndicesSoftPionsNeg) {
3309+
3310+ // Compute D0 invariant mass for all D* candidates
3311+ const std::array arrMomD0{pVecTrackNeg1, pVecTrackPos1};
3312+ const float invMassD0 = RecoDecay::m (arrMomD0, std::array{MassPiPlus, MassKPlus});
3313+
3314+ if (!softPiNegCacheFilled) {
32633315 groupedTrackIndicesSoftPionsNeg.emplace (negativeSoftPions->sliceByCached (aod::track::collisionId, collision.globalIndex (), cache));
3316+ fillPropagatedProngCache<true , TTracks>(collision, groupedTrackIndicesSoftPionsNeg, softPionsNeg);
3317+ softPiNegCacheFilled = true ;
32643318 }
3265- for (auto trackIndexNeg2 = groupedTrackIndicesSoftPionsNeg->begin (); trackIndexNeg2 != groupedTrackIndicesSoftPionsNeg->end (); ++trackIndexNeg2) {
3266- if (trackIndexNeg1 == trackIndexNeg2) {
3319+
3320+ for (std::size_t iNeg2 = 0 ; iNeg2 < softPionsNeg.size (); ++iNeg2) {
3321+ if (trackIndexNeg1 == softPionsNeg[iNeg2].trackIndex ) {
32673322 continue ;
32683323 }
3269- auto trackNeg2 = trackIndexNeg2.template track_as <TTracks>();
3270- std::array pVecTrackNeg2{trackNeg2.pVector ()};
3271- if (thisCollId != trackNeg2.collisionId ()) { // this is not the "default" collision for this track, we have to re-propagate it
3272- auto trackParVarNeg2 = getTrackParCov (trackNeg2);
3273- std::array dcaInfoNeg2{trackNeg2.dcaXY (), trackNeg2.dcaZ ()};
3274- o2::base::Propagator::Instance ()->propagateToDCABxByBz ({collision.posX (), collision.posY (), collision.posZ ()}, trackParVarNeg2, 2 .f , noMatCorr, &dcaInfoNeg2);
3275- getPxPyPz (trackParVarNeg2, pVecTrackNeg2);
3276- }
3324+
3325+ const auto & trackGlobalIndexNeg2 = softPionsNeg[iNeg2].trackGlobalIndex ;
3326+ const auto & pVecTrackNeg2 = softPionsNeg[iNeg2].pVec ;
32773327
32783328 uint8_t isSelectedDstar{0 };
32793329 uint8_t cutStatus{BIT (kNCutsDstar ) - 1 };
32803330 float deltaMass{-1 .};
3281- isSelectedDstar = applySelectionDstar (pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections
3331+ isSelectedDstar = applySelectionDstar (pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections
32823332 if (isSelectedDstar) {
3283- rowTrackIndexDstar (thisCollId, trackNeg2. globalIndex () , lastFilledD0);
3333+ rowTrackIndexDstar (thisCollId, trackGlobalIndexNeg2 , lastFilledD0);
32843334 if (config.fillHistograms ) {
32853335 registry.fill (HIST (" hMassDstarToD0Pi" ), deltaMass);
32863336 }
0 commit comments