From c07dec8d882ca050fca4821b60423bde10fa36eb Mon Sep 17 00:00:00 2001 From: Shunsuke-Kurita Date: Thu, 21 Dec 2023 14:29:25 +0900 Subject: [PATCH 1/3] add kDeltaPhiPair Histogram --- PWGDQ/Core/HistogramsLibrary.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/PWGDQ/Core/HistogramsLibrary.cxx b/PWGDQ/Core/HistogramsLibrary.cxx index 808ac8f622a..51c71d0d729 100644 --- a/PWGDQ/Core/HistogramsLibrary.cxx +++ b/PWGDQ/Core/HistogramsLibrary.cxx @@ -519,6 +519,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_Pt", "", false, 750, 0.0, 30.0, VarManager::kMass, 120, 0.0, 30.0, VarManager::kPt); hm->AddHistogram(histClass, "Mass_Rapidity", "", false, 750, 0.0, 30.0, VarManager::kMass, 500, -1.0, 4.0, VarManager::kRap); hm->AddHistogram(histClass, "Mass_VtxZ", "", true, 30, -15.0, 15.0, VarManager::kVtxZ, 750, 0.0, 30.0, VarManager::kMass); + hm->AddHistogram(histClass, "DeltaPhiPair", "", false, 130, -6.5, 6.5, VarManager::kDeltaPhiPair); } if (subGroupStr.Contains("dielectrons")) { if (subGroupStr.Contains("phiv")) { From 70122025d4374533429c8abc8558f47f7d9a55d8 Mon Sep 17 00:00:00 2001 From: Shunsuke-Kurita Date: Sat, 25 Jul 2026 01:36:51 +0900 Subject: [PATCH 2/3] [PWGDQ] Add post-prefilter single-track selection task to dqEfficiency_withAssoc AnalysisTrackSelection runs before AnalysisPrefilterSelection, so the effect of the conversion-prefilter veto cannot be inspected at the single-track level anywhere in the workflow (the only prefilter-aware consumers are the pairing tasks). The new AnalysisPostPrefilterTrackSelection task joins the track-collision associations with both the BarrelTrackCuts and the Prefilter decisions and fills track-level histograms, separately for tracks surviving the prefilter veto and for vetoed tracks, per track cut and per MC signal. Use cases: post-prefilter per-origin DCA templates for HF-electron template fits, MC measurement of the photonic-electron tagging efficiency (vetoed / (vetoed + survived)), and track-level prefilter QA. Co-Authored-By: Claude Fable 5 --- PWGDQ/Tasks/dqEfficiency_withAssoc.cxx | 218 +++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 42c42441774..fea3f19016f 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -1350,6 +1350,223 @@ struct AnalysisPrefilterSelection { PROCESS_SWITCH(AnalysisPrefilterSelection, processDummy, "Do nothing", true); }; +// Single-track selection on the barrel tracks after the prefilter decisions (which are not visible in the +// AnalysisTrackSelection histograms): fills track-level histograms separately for surviving and vetoed tracks, +// per track cut and per MC signal. +// NOTE: Histograms are filled per association, regardless of whether the association is the correct one. +struct AnalysisPostPrefilterTrackSelection { + OutputObj fOutputList{"output"}; + + Configurable fConfigTrackCuts{"cfgTrackCuts", "", "Comma separated list of track cuts (among those of analysis-track-selection) for which to fill histograms"}; + Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; + Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + Configurable fConfigMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; + Configurable fConfigMCSignalsJSON{"cfgTrackMCsignalsJSON", "", "Additional list of MC signals via JSON"}; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + + Service fCCDB{}; + + HistogramManager* fHistMan = nullptr; + std::vector fMCSignals; + std::vector fCutBits; // bit positions of the requested cuts in the track-selection task cut list + std::vector fHistNamesSurvived; // [icut] + std::vector fHistNamesVetoed; // [icut] + std::vector fHistNamesMCSurvived; // [icut * nSignals + isig] + std::vector fHistNamesMCVetoed; // [icut * nSignals + isig] + + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) + + void init(o2::framework::InitContext& context) + { + if (context.mOptions.get("processDummy")) { + return; + } + VarManager::SetDefaultVarNames(); + + fCurrentRun = 0; + TString trackCutsStr = fConfigTrackCuts.value; + std::unique_ptr objArrayCuts(trackCutsStr.Tokenize(",")); + if (objArrayCuts == nullptr || objArrayCuts->GetEntries() == 0) { + LOG(fatal) << " No track cuts specified! Check the cfgTrackCuts configurable"; + } + // get the full list of cuts computed by the track-selection task and resolve the requested cuts to bit positions + std::string trackCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", trackCuts, false); + TString allTrackCutsStr = trackCuts; + // check also the cuts added via JSON and add them to the string of cuts + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); + TString addTrackCutsStr = trackCuts; + if (addTrackCutsStr != "") { + std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { + allTrackCutsStr += Form(",%s", t->GetName()); + } + } + std::unique_ptr objArrayAllCuts(allTrackCutsStr.Tokenize(",")); + if (objArrayAllCuts == nullptr) { + LOG(fatal) << " Not getting any track cuts from the barrel-track-selection "; + } + std::vector cutNames; + for (int icut = 0; icut < objArrayCuts->GetEntries(); ++icut) { + TString cutName = objArrayCuts->At(icut)->GetName(); + if (objArrayAllCuts->FindObject(cutName.Data()) == nullptr) { + LOG(fatal) << " Track cut " << cutName << " not among the cuts calculated by the track-selection task! "; + } + for (int jcut = 0; jcut < objArrayAllCuts->GetEntries(); ++jcut) { + if (cutName.CompareTo(objArrayAllCuts->At(jcut)->GetName()) == 0) { + fCutBits.push_back(jcut); + } + } + cutNames.push_back(cutName); + } + + // Setting the MC signals + TString configSigNamesStr = fConfigMCSignals.value; + std::unique_ptr sigNamesArray(configSigNamesStr.Tokenize(",")); + for (int isig = 0; isig < sigNamesArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(sigNamesArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() != 1) { // NOTE: only 1 prong signals + continue; + } + fMCSignals.push_back(sig); + } + } + // Add the MCSignals from the JSON config + TString addMCSignalsStr = fConfigMCSignalsJSON.value; + if (addMCSignalsStr != "") { + std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); + for (auto const& mcIt : addMCSignals) { + if (mcIt->GetNProngs() != 1) { // NOTE: only 1 prong signals + continue; + } + fMCSignals.push_back(mcIt); + } + } + + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); + + // Configure histogram classes for each track cut, separately for prefilter-surviving and prefilter-vetoed tracks, + // and for each requested MC signal (reconstructed tracks with MC truth) + TString histClasses = ""; + for (auto const& cutName : cutNames) { + TString nameStr = Form("TrackBarrelPostPrefilter_%s", cutName.Data()); + fHistNamesSurvived.push_back(nameStr); + histClasses += Form("%s;", nameStr.Data()); + nameStr = Form("TrackBarrelPrefilterVetoed_%s", cutName.Data()); + fHistNamesVetoed.push_back(nameStr); + histClasses += Form("%s;", nameStr.Data()); + for (auto const& sig : fMCSignals) { + TString nameStr2 = Form("TrackBarrelPostPrefilter_%s_%s", cutName.Data(), sig->GetName()); + fHistNamesMCSurvived.push_back(nameStr2); + histClasses += Form("%s;", nameStr2.Data()); + nameStr2 = Form("TrackBarrelPrefilterVetoed_%s_%s", cutName.Data(), sig->GetName()); + fHistNamesMCVetoed.push_back(nameStr2); + histClasses += Form("%s;", nameStr2.Data()); + } + } + + DefineHistograms(fHistMan, histClasses.Data(), fConfigAddTrackHistogram.value.data()); + dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + fCCDB->setURL(fConfigCcdbUrl.value); + fCCDB->setCaching(true); + fCCDB->setLocalObjectValidityChecking(); + } + + template + void runPostPrefilterTrackSelection(soa::Join const& assocs, + TEvents const& events, TTracks const& /*tracks*/, TTracksMC const& tracksMC) + { + // load the magnetic field (needed to recompute the DCA with respect to the associated collision) + if (events.size() > 0 && fCurrentRun != events.begin().runNumber()) { + auto grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); + if (grpmag != nullptr) { + VarManager::SetMagneticField(grpmag->getNominalL3Field()); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", events.begin().timestamp()); + } + fCurrentRun = events.begin().runNumber(); + } + + // Loop over associations + for (auto const& assoc : assocs) { + auto event = assoc.template reducedevent_as(); + if (!event.isEventSelected_bit(0)) { + continue; + } + + // check which of the requested cuts are fulfilled by this association and which of those are prefilter-vetoed + uint32_t selectedMap = 0; + uint32_t vetoMap = 0; + for (std::size_t icut = 0; icut < fCutBits.size(); ++icut) { + if (assoc.isBarrelSelected_bit(fCutBits[icut])) { + selectedMap |= (static_cast(1) << icut); + if (!assoc.isBarrelSelectedPrefilter_bit(fCutBits[icut])) { + vetoMap |= (static_cast(1) << icut); + } + } + } + if (selectedMap == 0) { + continue; + } + + VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); + // fill event information which might be needed in histograms that combine track and event properties + VarManager::FillEvent(event); + if (event.has_reducedMCevent()) { + VarManager::FillEvent(event.reducedMCevent()); + } + + auto track = assoc.template reducedtrack_as(); + VarManager::FillTrack(track); + // compute quantities which depend on the associated collision, such as DCA + VarManager::FillTrackCollision(track, event); + if (track.has_reducedMCTrack()) { + VarManager::FillTrackMC(tracksMC, track.reducedMCTrack()); + } + + for (std::size_t icut = 0; icut < fCutBits.size(); ++icut) { + if (!(selectedMap & (static_cast(1) << icut))) { + continue; + } + bool isVetoed = (vetoMap & (static_cast(1) << icut)) > 0; + fHistMan->FillHistClass((isVetoed ? fHistNamesVetoed[icut] : fHistNamesSurvived[icut]).Data(), dqefficiency_helpers::varValues()); + if (track.has_reducedMCTrack()) { + int isig = 0; + for (auto sig = fMCSignals.begin(); sig != fMCSignals.end(); sig++, isig++) { + if ((*sig)->CheckSignal(true, track.reducedMCTrack())) { + fHistMan->FillHistClass((isVetoed ? fHistNamesMCVetoed : fHistNamesMCSurvived)[icut * fMCSignals.size() + isig].Data(), dqefficiency_helpers::varValues()); + } + } + } + } // end loop over cuts + } // end loop over associations + } + + void processSkimmed(soa::Join const& assocs, MyEventsSelected const& events, MyBarrelTracks const& tracks, ReducedMCEvents const& /*eventsMC*/, ReducedMCTracks const& tracksMC) + { + runPostPrefilterTrackSelection(assocs, events, tracks, tracksMC); + } + void processSkimmedWithCov(soa::Join const& assocs, MyEventsVtxCovSelected const& events, MyBarrelTracksWithCov const& tracks, ReducedMCEvents const& /*eventsMC*/, ReducedMCTracks const& tracksMC) + { + runPostPrefilterTrackSelection(assocs, events, tracks, tracksMC); + } + void processDummy(MyEvents const&) + { + // do nothing + } + + PROCESS_SWITCH(AnalysisPostPrefilterTrackSelection, processSkimmed, "Run barrel track post-prefilter selection on DQ skimmed track associations", false); + PROCESS_SWITCH(AnalysisPostPrefilterTrackSelection, processSkimmedWithCov, "Run barrel track post-prefilter selection on DQ skimmed track associations w/ cov matrix", false); + PROCESS_SWITCH(AnalysisPostPrefilterTrackSelection, processDummy, "Dummy function", true); +}; + // Run the same-event pairing // This task assumes that both legs of the resonance fulfill the same cuts (symmetric decay channel) // Runs combinatorics for barrel-barrel, muon-muon and barrel-muon combinations @@ -5183,6 +5400,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; From 6db764914eda08f57bbef7b8764f5ece64cd273c Mon Sep 17 00:00:00 2001 From: Shunsuke-Kurita Date: Mon, 27 Jul 2026 22:41:58 +0900 Subject: [PATCH 3/3] [PWGDQ] Fill electron-leg DCA variables in FillPair for kElectronMuon pairs --- PWGDQ/Core/VarManager.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index 3bdd863c5e4..1819bcde122 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -3923,6 +3923,11 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kQuadDCAabsXY] = std::sqrt((dca1XY * dca1XY + dca2XY * dca2XY) / 2.); } } + if constexpr ((pairType == kElectronMuon) && ((fillMap & ReducedTrackBarrel) > 0)) { + // DCA of the barrel (electron) leg; not filled in FillPairME since the mixed-event barrel track type has no DCA columns + values[kDCAxy1] = t1.dcaXY(); + values[kDCAz1] = t1.dcaZ(); + } if (fgUsedVars[kPairPhiv]) { values[kPairPhiv] = calculatePhiV(t1, t2); }