@@ -86,9 +86,10 @@ struct ResonanceModuleInitializer {
8686 Service<o2::ccdb::BasicCCDBManager> ccdb; // /< CCDB manager service
8787 Service<o2::framework::O2DatabasePDG> pdg; // /< PDG database service
8888
89- Produces<aod::ResoCollisions> resoCollisions; // /< Output table for resonance collisions
90- Produces<aod::ResoCollisionColls> resoCollisionColls; // /< Output table for collision references
91- Produces<aod::ResoMCCollisions> resoMCCollisions; // /< Output table for MC resonance collisions
89+ Produces<aod::ResoCollisions> resoCollisions; // /< Output table for resonance collisions
90+ Produces<aod::ResoCollisionColls> resoCollisionColls; // /< Output table for collision references
91+ Produces<aod::ResoCollisionGroups> resoCollisionGroups; // /< Canonical original-collision grouping references
92+ Produces<aod::ResoMCCollisions> resoMCCollisions; // /< Output table for MC resonance collisions
9293
9394 // CCDB options
9495 struct : ConfigurableGroup {
@@ -492,6 +493,7 @@ struct ResonanceModuleInitializer {
492493
493494 resoCollisions (collision.multNTracksPV (), collision.multNTracksPVeta1 (), collision.multNTracksPVetaHalf (), collision.posX (), collision.posY (), collision.posZ (), centEst (collision), dBz, isRecINELgt0);
494495 resoCollisionColls (collision.globalIndex ());
496+ resoCollisionGroups (collision.globalIndex ());
495497 }
496498 PROCESS_SWITCH (ResonanceModuleInitializer, processRun3, " Default process for RUN3" , false );
497499
@@ -516,6 +518,7 @@ struct ResonanceModuleInitializer {
516518
517519 resoCollisions (0 , 0 , 0 , collision.posX (), collision.posY (), collision.posZ (), centrality, dBz, 0 );
518520 resoCollisionColls (collision.globalIndex ());
521+ resoCollisionGroups (collision.globalIndex ());
519522 }
520523 PROCESS_SWITCH (ResonanceModuleInitializer, processRun2, " process for RUN2" , false );
521524
@@ -699,6 +702,7 @@ struct ResonanceDaughterInitializer {
699702 // Keep ResonanceModuleInitializer::cfgBypassCollIndexFill disabled and enable
700703 // the matching Run 2/Run 3 base event process for MC workflows.
701704 using ResoCollisionWithIndex = soa::Join<aod::ResoCollisions, aod::ResoCollisionColls>;
705+ using SelectedResoCollisions = soa::Join<aod::ResoCollisions, aod::ResoCollisionGroups>;
702706
703707 /* *
704708 * @brief Initializes the task
@@ -707,25 +711,35 @@ struct ResonanceDaughterInitializer {
707711 */
708712 void init (InitContext&)
709713 {
710- const bool processTrackDataEnabled = doprocessData || doprocessDataWithPairGate;
714+ const bool processTrackDataEnabled = doprocessData || doprocessDataOptimized || doprocessDataHybrid || doprocessDataWithPairGate;
711715 const bool processTrackMCEnabled = doprocessMC || doprocessMCWithPairGate;
716+ const bool processV0DataEnabled = doprocessV0Data || doprocessV0DataHybrid;
717+ const bool processCascDataEnabled = doprocessCascData || doprocessCascDataHybrid;
712718 const int enabledTrackProcesses = static_cast <int >(doprocessData) +
719+ static_cast <int >(doprocessDataOptimized) +
720+ static_cast <int >(doprocessDataHybrid) +
713721 static_cast <int >(doprocessDataWithPairGate) +
714722 static_cast <int >(doprocessMC) +
715723 static_cast <int >(doprocessMCWithPairGate);
716724
717725 if (enabledTrackProcesses > 1 ) {
718726 LOGF (fatal, " Only one track process can be enabled in ResonanceDaughterInitializer" );
719727 }
720- if ((doprocessData || doprocessMC) &&
728+ if (static_cast <int >(doprocessV0Data) + static_cast <int >(doprocessV0DataHybrid) + static_cast <int >(doprocessV0MC) > 1 ) {
729+ LOGF (fatal, " Only one V0 process can be enabled in ResonanceDaughterInitializer" );
730+ }
731+ if (static_cast <int >(doprocessCascData) + static_cast <int >(doprocessCascDataHybrid) + static_cast <int >(doprocessCascMC) > 1 ) {
732+ LOGF (fatal, " Only one cascade process can be enabled in ResonanceDaughterInitializer" );
733+ }
734+ if ((doprocessData || doprocessDataOptimized || doprocessDataHybrid || doprocessMC) &&
721735 (FilterForDerivedTables.cfgBypassNoPairV0s || FilterForDerivedTables.cfgBypassNoPairCascades )) {
722- LOGF (warn, " Pair-gate options are ignored by processData/processMC; enable the matching *WithPairGate process to apply them" );
736+ LOGF (warn, " Pair-gate options are ignored by processData/processDataOptimized/processDataHybrid/ processMC; enable the matching *WithPairGate process to apply them" );
723737 }
724- if (doprocessDataWithPairGate && FilterForDerivedTables.cfgBypassNoPairV0s && !doprocessV0Data ) {
725- LOGF (fatal, " cfgBypassNoPairV0s requires processV0Data so an accepted V0 is written for every retained collision" );
738+ if (doprocessDataWithPairGate && FilterForDerivedTables.cfgBypassNoPairV0s && !processV0DataEnabled ) {
739+ LOGF (fatal, " cfgBypassNoPairV0s requires processV0Data or processV0DataHybrid so an accepted V0 is written for every retained collision" );
726740 }
727- if (doprocessDataWithPairGate && FilterForDerivedTables.cfgBypassNoPairCascades && !doprocessCascData ) {
728- LOGF (fatal, " cfgBypassNoPairCascades requires processCascData so an accepted cascade is written for every retained collision" );
741+ if (doprocessDataWithPairGate && FilterForDerivedTables.cfgBypassNoPairCascades && !processCascDataEnabled ) {
742+ LOGF (fatal, " cfgBypassNoPairCascades requires processCascData or processCascDataHybrid so an accepted cascade is written for every retained collision" );
729743 }
730744
731745 if (!std::isfinite (TrackCuts.cfgCutMinPt .value ) ||
@@ -860,7 +874,7 @@ struct ResonanceDaughterInitializer {
860874 }
861875 }
862876
863- if (doprocessV0Data || doprocessV0MC) {
877+ if (processV0DataEnabled || doprocessV0MC) {
864878 qaRegistry.add (" QA/hGoodV0Indices" , " hGoodV0Indices" , kTH1D , {idxAxis});
865879 if (doprocessV0MC) {
866880 qaRegistry.add (" QA/hGoodMCV0Indices" , " hGoodMCV0Indices" , kTH1D , {idxAxis});
@@ -871,7 +885,7 @@ struct ResonanceDaughterInitializer {
871885 qaRegistry.add (" QA/hV0CosPA" , " V0 CosPA" , kTH1F , {cosPAAxis});
872886 }
873887
874- if (doprocessCascData || doprocessCascMC) {
888+ if (processCascDataEnabled || doprocessCascMC) {
875889 AxisSpec radiusAxis = {100 , 0.0 , 200.0 , " Cascade Radius" };
876890 AxisSpec cosPAAxis = {100 , 0.97 , 1.0 , " Cascade CosPA" };
877891 qaRegistry.add (" QA/hGoodCascIndices" , " hGoodCascIndices" , kTH1D , {idxAxis});
@@ -885,19 +899,19 @@ struct ResonanceDaughterInitializer {
885899 if (processTrackDataEnabled || processTrackMCEnabled) {
886900 LOGF (info, " ResonanceDaughterInitializer initialized with tracks" );
887901 }
888- if (doprocessV0Data || doprocessV0MC) {
902+ if (processV0DataEnabled || doprocessV0MC) {
889903 LOGF (info, " ResonanceDaughterInitializer initialized with V0s" );
890904 }
891- if (doprocessCascData || doprocessCascMC) {
905+ if (processCascDataEnabled || doprocessCascMC) {
892906 LOGF (info, " ResonanceDaughterInitializer initialized with cascades" );
893907 }
894908
895909 // Check if the module is initialized with both data and MC
896- if ((processTrackDataEnabled && processTrackMCEnabled) || (doprocessV0Data && doprocessV0MC) || (doprocessCascData && doprocessCascMC)) {
910+ if ((processTrackDataEnabled && processTrackMCEnabled) || (processV0DataEnabled && doprocessV0MC) || (processCascDataEnabled && doprocessCascMC)) {
897911 LOGF (fatal, " ResonanceDaughterInitializer initialized with both data and MC" );
898912 }
899913 // Check if none of the processes are enabled
900- if (!doprocessDummy && !processTrackDataEnabled && !processTrackMCEnabled && !doprocessV0Data && !doprocessV0MC && !doprocessCascData && !doprocessCascMC) {
914+ if (!doprocessDummy && !processTrackDataEnabled && !processTrackMCEnabled && !processV0DataEnabled && !doprocessV0MC && !processCascDataEnabled && !doprocessCascMC) {
901915 LOGF (fatal, " ResonanceDaughterInitializer not initialized, enable at least one process" );
902916 }
903917 }
@@ -1874,6 +1888,25 @@ struct ResonanceDaughterInitializer {
18741888 }
18751889 PROCESS_SWITCH (ResonanceDaughterInitializer, processDummy, " Process dummy" , true );
18761890
1891+ /* *
1892+ * @brief Fills all enabled track tables from an already grouped track slice
1893+ *
1894+ * @tparam isMC Boolean indicating if it's MC
1895+ * @param collision Reduced collision used as the output foreign key
1896+ * @param tracks Tracks belonging to the corresponding original collision
1897+ */
1898+ template <bool isMC, typename CollisionType, typename TrackTableType>
1899+ void fillTrackTables (CollisionType const & collision, TrackTableType const & tracks)
1900+ {
1901+ fillTracks<isMC>(collision, tracks);
1902+ if (FilterForDerivedTables.cfgFillMicroTracks ) {
1903+ fillMicroTracks<isMC>(collision, tracks);
1904+ }
1905+ if (FilterForDerivedTables.cfgFillUltraMicroTracks ) {
1906+ fillUltraMicroTracks<isMC>(collision, tracks);
1907+ }
1908+ }
1909+
18771910 /* *
18781911 * @brief Fills track tables for one original collision
18791912 *
@@ -1888,13 +1921,7 @@ struct ResonanceDaughterInitializer {
18881921 void fillTrackTablesForCollision (CollisionType const & collision, TrackTableType const & tracks, PresliceType const & perCollision)
18891922 {
18901923 auto tracksThisCollision = tracks.sliceBy (perCollision, collision.collisionId ());
1891- fillTracks<isMC>(collision, tracksThisCollision);
1892- if (FilterForDerivedTables.cfgFillMicroTracks ) {
1893- fillMicroTracks<isMC>(collision, tracksThisCollision);
1894- }
1895- if (FilterForDerivedTables.cfgFillUltraMicroTracks ) {
1896- fillUltraMicroTracks<isMC>(collision, tracksThisCollision);
1897- }
1924+ fillTrackTables<isMC>(collision, tracksThisCollision);
18981925 }
18991926
19001927 /* *
@@ -1910,6 +1937,43 @@ struct ResonanceDaughterInitializer {
19101937 }
19111938 PROCESS_SWITCH (ResonanceDaughterInitializer, processData, " Process tracks for data" , false );
19121939
1940+ /* *
1941+ * @brief Processes data tracks grouped automatically by their original collision
1942+ *
1943+ * The canonical fIndexCollisions column in ResoCollisionGroups lets
1944+ * GroupSlicer associate both reduced collisions and tracks to the same
1945+ * original aod::Collision. The tracks argument is therefore already the
1946+ * selected slice for this collision and must not be sliced again.
1947+ */
1948+ void processDataOptimized (aod::Collision const &,
1949+ soa::SmallGroups<SelectedResoCollisions> const & reducedCollisions,
1950+ soa::Filtered<aod::ResoTrackCandidates> const & tracks)
1951+ {
1952+ if (reducedCollisions.size () == 0 ) {
1953+ return ;
1954+ }
1955+ if (reducedCollisions.size () != 1 ) {
1956+ LOGF (fatal, " Expected exactly one reduced collision for an original collision, found %zu" , reducedCollisions.size ());
1957+ }
1958+ auto reducedCollision = reducedCollisions.begin ();
1959+ fillTrackTables<false >(reducedCollision, tracks);
1960+ }
1961+ PROCESS_SWITCH (ResonanceDaughterInitializer, processDataOptimized, " Process data tracks with original-collision grouping" , false );
1962+
1963+ /* *
1964+ * @brief Production hybrid path using original-collision automatic grouping
1965+ *
1966+ * This named path preserves the existing optimized prototype for A/B
1967+ * compatibility while exposing the two-stage hybrid architecture explicitly.
1968+ */
1969+ void processDataHybrid (aod::Collision const & originalCollision,
1970+ soa::SmallGroups<SelectedResoCollisions> const & reducedCollisions,
1971+ soa::Filtered<aod::ResoTrackCandidates> const & tracks)
1972+ {
1973+ processDataOptimized (originalCollision, reducedCollisions, tracks);
1974+ }
1975+ PROCESS_SWITCH (ResonanceDaughterInitializer, processDataHybrid, " Process data tracks with the two-stage hybrid grouping" , false );
1976+
19131977 /* *
19141978 * @brief Processes data tracks with configurable selected-V0 and selected-cascade gates
19151979 */
@@ -1984,6 +2048,29 @@ struct ResonanceDaughterInitializer {
19842048 }
19852049 PROCESS_SWITCH (ResonanceDaughterInitializer, processV0Data, " Process V0s for data" , false );
19862050
2051+ /* *
2052+ * @brief Processes data V0s grouped automatically by their original collision
2053+ *
2054+ * Both V0s and tracks are already restricted to the current original
2055+ * aod::Collision by GroupSlicer. The unfiltered track table is required for
2056+ * resolving the positive and negative daughter indices.
2057+ */
2058+ void processV0DataHybrid (aod::Collision const &,
2059+ soa::SmallGroups<SelectedResoCollisions> const & reducedCollisions,
2060+ aod::ResoV0Candidates const & v0s,
2061+ aod::ResoTrackCandidates const & tracks)
2062+ {
2063+ if (reducedCollisions.size () == 0 ) {
2064+ return ;
2065+ }
2066+ if (reducedCollisions.size () != 1 ) {
2067+ LOGF (fatal, " Expected exactly one reduced collision for an original collision, found %zu" , reducedCollisions.size ());
2068+ }
2069+ auto reducedCollision = reducedCollisions.begin ();
2070+ fillV0s<false >(reducedCollision, v0s, tracks);
2071+ }
2072+ PROCESS_SWITCH (ResonanceDaughterInitializer, processV0DataHybrid, " Process data V0s with the two-stage hybrid grouping" , false );
2073+
19872074 /* *
19882075 * @brief Processes MC V0 data
19892076 *
@@ -2012,6 +2099,29 @@ struct ResonanceDaughterInitializer {
20122099 }
20132100 PROCESS_SWITCH (ResonanceDaughterInitializer, processCascData, " Process Cascades for data" , false );
20142101
2102+ /* *
2103+ * @brief Processes data cascades grouped automatically by their original collision
2104+ *
2105+ * Cascades and tracks arrive as original-collision groups. Keeping this as a
2106+ * separate callback from V0 processing avoids enabling either upstream input
2107+ * dependency unless its process switch is selected.
2108+ */
2109+ void processCascDataHybrid (aod::Collision const &,
2110+ soa::SmallGroups<SelectedResoCollisions> const & reducedCollisions,
2111+ aod::ResoCascadesCandidates const & cascades,
2112+ aod::ResoTrackCandidates const & tracks)
2113+ {
2114+ if (reducedCollisions.size () == 0 ) {
2115+ return ;
2116+ }
2117+ if (reducedCollisions.size () != 1 ) {
2118+ LOGF (fatal, " Expected exactly one reduced collision for an original collision, found %zu" , reducedCollisions.size ());
2119+ }
2120+ auto reducedCollision = reducedCollisions.begin ();
2121+ fillCascades<false >(reducedCollision, cascades, tracks);
2122+ }
2123+ PROCESS_SWITCH (ResonanceDaughterInitializer, processCascDataHybrid, " Process data cascades with the two-stage hybrid grouping" , false );
2124+
20152125 /* *
20162126 * @brief Processes MC cascade data
20172127 *
0 commit comments