Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion PWGLF/DataModel/LFResonanceTables.h
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,14 @@ DECLARE_SOA_DYNAMIC_COLUMN(PassedPtDependentDCAxy, passedPtDependentDCAxy,
[](uint8_t trackSelectionFlags) { return DCAEncoding::testPtDependentDCAxy(trackSelectionFlags); });
DECLARE_SOA_DYNAMIC_COLUMN(PassedPtDependentDCAz, passedPtDependentDCAz,
[](uint8_t trackSelectionFlags) { return DCAEncoding::testPtDependentDCAz(trackSelectionFlags); });
DECLARE_SOA_DYNAMIC_COLUMN(HasITSHitInLayer, hasITSHitInLayer,
[](uint8_t itsClusterMap, int layer) -> bool {
constexpr int NumberOfITSLayers = 7;
if (layer < 0 || layer >= NumberOfITSLayers) {
return false;
}
return (itsClusterMap & (1u << layer)) != 0;
});
} // namespace resomicrodaughter001

// Ultra-micro track representation. The momentum components are quantised
Expand Down Expand Up @@ -1036,6 +1044,8 @@ using ResoMicroTrack = ResoMicroTracks::iterator;

// Keep ResoMicroTracks as the version-0 API for existing producers and
// consumers. Version-1 users must request ResoMicroTracks_001 explicitly.
// Version 1 includes TPC crossed rows and the ITS cluster map; older version-1
// files without these columns must be regenerated or explicitly converted.
DECLARE_SOA_TABLE_VERSIONED(ResoMicroTracks_001, "AOD", "RESOMICROTRACK", 1,
o2::soa::Index<>,
resodaughter::ResoCollisionId,
Expand All @@ -1048,6 +1058,8 @@ DECLARE_SOA_TABLE_VERSIONED(ResoMicroTracks_001, "AOD", "RESOMICROTRACK", 1,
resomicrodaughter::PidNSigmaPrFlag,
resomicrodaughter001::TrackSelectionFlags,
resodaughter::TrackFlags,
resodaughter::TpcNClsCrossedRows,
track::ITSClusterMap,
// Dynamic columns
resomicrodaughter::Pt<resodaughter::Px, resodaughter::Py>,
resodaughter::Eta<resodaughter::Px, resodaughter::Py, resodaughter::Pz>,
Expand All @@ -1069,7 +1081,10 @@ DECLARE_SOA_TABLE_VERSIONED(ResoMicroTracks_001, "AOD", "RESOMICROTRACK", 1,
resodaughter::IsPrimaryTrack<resodaughter::TrackFlags>,
resodaughter::IsPVContributor<resodaughter::TrackFlags>,
resodaughter::HasTOF<resodaughter::TrackFlags>,
resodaughter::Sign<resodaughter::TrackFlags>);
resodaughter::Sign<resodaughter::TrackFlags>,
track::ITSNCls<track::ITSClusterMap>,
track::ITSNClsInnerBarrel<track::ITSClusterMap>,
resomicrodaughter001::HasITSHitInLayer<track::ITSClusterMap>);
// Positional soft-link side table retained for ResoMicroTracks version 000.
// Version 001 stores the same row number as a scalar and should be consumed
// without joining this side table, since both columns expose trackId().
Expand Down
80 changes: 46 additions & 34 deletions PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,7 @@ struct ResonanceDaughterInitializer {
static constexpr float MomentumQuantizationScale = 1000.f;
static constexpr std::size_t StoredMCRelationCount = 2;
static constexpr std::size_t MaxCandidateDaughters = 3;
static constexpr int NumberOfITSLayers = 7;

/// Selected-candidate state and the optional global daughter-ID veto set.
/// By default only candidate existence is recorded and daughter reuse is
Expand Down Expand Up @@ -1132,6 +1133,8 @@ struct ResonanceDaughterInitializer {
// General daughter output options
Configurable<bool> cfgFillQA{"cfgFillQA", false, "Fill QA histograms"};
Configurable<bool> cfgDetailTrackQA{"cfgDetailTrackQA", false, "Fill detailed QA histograms for enabled track output tables"};
Configurable<bool> cfgQAITS{"cfgQAITS", false, "Fill Micro001 ITS quality vs pT (requires cfgFillQA and cfgFillMicroTracks)"};
Configurable<bool> cfgQATPC{"cfgQATPC", false, "Fill Micro001 TPC crossed rows vs pT (requires cfgFillQA and cfgFillMicroTracks)"};

// Track pre-selection and DCA cuts
struct : ConfigurableGroup {
Expand Down Expand Up @@ -1243,13 +1246,12 @@ struct ResonanceDaughterInitializer {
// data-model relation keeps its legacy v000 default target; v001 consumers
// must use resoCollision_as<T>() with the exact bound v001 table type for
// dereferencing (aod::ResoCollisions_001 when the parent is not joined).
// Original-collision grouping is provided by the scalar-only version-1
// ResoCollisionGroups_001 table, avoiding a hard source-AO2D relation.
// Daughter callbacks use the original collision ID from ResoCollisionColls
// to slice input candidates explicitly. The scalar ResoCollisionGroups_001
// output is retained for consumers that do not need source-AO2D dereferencing.
// Collision mappings are always written; cfgBypassCollIndexFill is retained
// only so existing configuration files remain accepted.
using ResoCollisionWithIndex = soa::Join<aod::ResoCollisions_001, aod::ResoCollisionColls>;
using SelectedResoCollisions = soa::Join<aod::ResoCollisions_001, aod::ResoCollisionGroups_001>;
PresliceUnsorted<SelectedResoCollisions> reducedCollisionsPerOriginalCollision = aod::resocollisiongroup001::originalCollisionId;

/**
* @brief Initializes the task
Expand All @@ -1263,7 +1265,7 @@ struct ResonanceDaughterInitializer {
LOGF(fatal, "cfgPairGateMode must be 0 (configured gates) or 1 (V0 or cascade)");
}
const bool useEitherPairGate = FilterForDerivedTables.cfgPairGateMode.value == PairGateModeEither;
const bool processTrackDataEnabled = doprocessData || doprocessDataHybrid || doprocessDataWithPairGate ||
const bool processTrackDataEnabled = doprocessData || doprocessDataWithPairGate ||
doprocessDataWithV0PairGate || doprocessDataWithCascPairGate;
const bool processTrackMCEnabled = doprocessMC || doprocessMCWithPairGate ||
doprocessMCWithV0PairGate || doprocessMCWithCascPairGate;
Expand All @@ -1275,7 +1277,6 @@ struct ResonanceDaughterInitializer {
const bool anyDataProcessEnabled = processTrackDataEnabled || processV0DataEnabled || processCascDataEnabled;
const bool anyMCProcessEnabled = processTrackMCEnabled || doprocessV0MC || doprocessCascMC;
const int enabledTrackProcesses = static_cast<int>(doprocessData) +
static_cast<int>(doprocessDataHybrid) +
static_cast<int>(doprocessDataWithPairGate) +
static_cast<int>(doprocessDataWithV0PairGate) +
static_cast<int>(doprocessDataWithCascPairGate) +
Expand Down Expand Up @@ -1311,10 +1312,10 @@ struct ResonanceDaughterInitializer {
if (static_cast<int>(doprocessCascData) + static_cast<int>(doprocessCascMC) > 1) {
LOGF(fatal, "Only one cascade process can be enabled in ResonanceDaughterInitializer");
}
if ((doprocessData || doprocessDataHybrid || doprocessMC) &&
if ((doprocessData || doprocessMC) &&
(useEitherPairGate || FilterForDerivedTables.cfgBypassNoPairV0s || FilterForDerivedTables.cfgBypassNoPairCascades ||
FilterForDerivedTables.cfgGlobalDaughterVeto)) {
LOGF(warn, "Pair-gate options are ignored by processData/processDataHybrid/processMC; enable the matching *WithPairGate process to apply them");
LOGF(warn, "Pair-gate options are ignored by processData/processMC; enable the matching *WithPairGate process to apply them");
}
const auto validatePairGateOutputs = [&](bool pairProcessEnabled,
bool v0OutputEnabled,
Expand Down Expand Up @@ -1513,6 +1514,24 @@ struct ResonanceDaughterInitializer {
}
}

if ((processTrackDataEnabled || processTrackMCEnabled) && FilterForDerivedTables.cfgFillMicroTracks) {
AxisSpec qualityPtAxis = {300, 0.f, 30.f, "#it{p}_{T} (GeV/#it{c})"};
if (cfgQAITS) {
AxisSpec itsMapAxis = {128, -0.5, 127.5, "ITS cluster map"};
AxisSpec itsNClsAxis = {8, -0.5, 7.5, "ITS occupied layers"};
AxisSpec itsInnerBarrelNClsAxis = {4, -0.5, 3.5, "ITS inner-barrel occupied layers"};
AxisSpec itsLayerAxis = {NumberOfITSLayers, -0.5, NumberOfITSLayers - 0.5, "ITS layer (0 = innermost)"};
qaRegistry.add("QA/h2MicroTrackITSClusterMapVsPt", "ResoMicroTracks ITS cluster map vs pT", kTH2D, {qualityPtAxis, itsMapAxis});
qaRegistry.add("QA/h2MicroTrackITSNClsVsPt", "ResoMicroTracks ITS occupied layers vs pT", kTH2D, {qualityPtAxis, itsNClsAxis});
qaRegistry.add("QA/h2MicroTrackITSNClsInnerBarrelVsPt", "ResoMicroTracks ITS inner-barrel occupied layers vs pT", kTH2D, {qualityPtAxis, itsInnerBarrelNClsAxis});
qaRegistry.add("QA/h2MicroTrackITSLayerHitsVsPt", "ResoMicroTracks ITS hits vs pT (one entry per occupied layer)", kTH2D, {qualityPtAxis, itsLayerAxis});
}
if (cfgQATPC) {
AxisSpec tpcCrossedRowsAxis = {256, -0.5, 255.5, "TPC crossed rows"};
qaRegistry.add("QA/h2MicroTrackTPCCrossedRowsVsPt", "ResoMicroTracks TPC crossed rows vs pT", kTH2D, {qualityPtAxis, tpcCrossedRowsAxis});
}
}

if (processV0DataEnabled || doprocessV0MC) {
qaRegistry.add("QA/hGoodV0Indices", "hGoodV0Indices", kTH1D, {idxAxis});
auto v0Selection = qaRegistry.get<TH1>(HIST("QA/hGoodV0Indices"));
Expand Down Expand Up @@ -2195,6 +2214,22 @@ struct ResonanceDaughterInitializer {
qaRegistry.fill(HIST("QA/h4MicroTrackTOFnSigma"), track.pt(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr());
}
}
// Quality QA follows the same selected rows as Micro001, independently
// of the detailed PID/DCA QA. ITS count getters count occupied layers.
if (cfgFillQA && cfgQAITS) {
const auto itsClusterMap = track.itsClusterMap();
qaRegistry.fill(HIST("QA/h2MicroTrackITSClusterMapVsPt"), track.pt(), itsClusterMap);
qaRegistry.fill(HIST("QA/h2MicroTrackITSNClsVsPt"), track.pt(), track.itsNCls());
qaRegistry.fill(HIST("QA/h2MicroTrackITSNClsInnerBarrelVsPt"), track.pt(), track.itsNClsInnerBarrel());
for (int layer = 0; layer < NumberOfITSLayers; ++layer) {
if ((itsClusterMap & (1u << layer)) != 0) {
qaRegistry.fill(HIST("QA/h2MicroTrackITSLayerHitsVsPt"), track.pt(), layer);
}
}
}
if (cfgFillQA && cfgQATPC) {
qaRegistry.fill(HIST("QA/h2MicroTrackTPCCrossedRowsVsPt"), track.pt(), track.tpcNClsCrossedRows());
}
reso2microtrks(collision.globalIndex(),
track.globalIndex(),
track.px(),
Expand All @@ -2204,7 +2239,9 @@ struct ResonanceDaughterInitializer {
static_cast<uint8_t>(o2::aod::resomicrodaughter001::PidNSigma(track.tpcNSigmaKa(), track.tofNSigmaKa(), track.hasTOF())),
static_cast<uint8_t>(o2::aod::resomicrodaughter001::PidNSigma(track.tpcNSigmaPr(), track.tofNSigmaPr(), track.hasTOF())),
static_cast<uint8_t>(trackSelFlag),
trackFlags);
trackFlags,
static_cast<uint8_t>(track.tpcNClsCrossedRows()),
track.itsClusterMap());
if (!FilterForDerivedTables.cfgBypassTrackIndexFill) {
resoMicroTrackTracks(track.globalIndex());
}
Expand Down Expand Up @@ -2783,31 +2820,6 @@ struct ResonanceDaughterInitializer {
}
PROCESS_SWITCH(ResonanceDaughterInitializer, processData, "Process tracks for data", false);

/**
* @brief Processes data tracks using the two-stage hybrid grouping
*
* GroupSlicer associates tracks automatically to the original
* aod::Collision. Reduced collisions retain a scalar original-collision row
* number and are explicitly sliced from the much smaller mapping table. The
* tracks argument is already the selected slice and must not be sliced again.
*/
void processDataHybrid(aod::Collision const& originalCollision,
SelectedResoCollisions const& reducedCollisions,
soa::Filtered<aod::ResoTrackCandidates> const& tracks)
{
auto reducedCollisionsThisCollision = reducedCollisions.sliceBy(reducedCollisionsPerOriginalCollision, originalCollision.globalIndex());
if (reducedCollisionsThisCollision.size() == 0) {
return;
}
if (reducedCollisionsThisCollision.size() > 1) {
LOGF(error, "Found %zu reduced collisions for one original collision; skipping the ambiguous association", reducedCollisionsThisCollision.size());
return;
}
auto reducedCollision = reducedCollisionsThisCollision.begin();
fillTrackTables<false>(reducedCollision, tracks);
}
PROCESS_SWITCH(ResonanceDaughterInitializer, processDataHybrid, "Process data tracks with the two-stage hybrid grouping", false);

/**
* @brief Processes data tracks with configurable selected V0 and cascade gates
*/
Expand Down
Loading