Skip to content

Commit f55863b

Browse files
authored
[DPG] Introduce treeCreatorTpcDiagnostics workflow (#17086)
1 parent e292500 commit f55863b

3 files changed

Lines changed: 364 additions & 1 deletion

File tree

DPG/Tasks/TPC/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,9 @@ o2physics_add_dpl_workflow(pid-tpc-skimscreation
1717
o2physics_add_dpl_workflow(pid-tpc-tree-creator-light
1818
SOURCES tpcTreeCreatorLight.cxx
1919
PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore
20-
COMPONENT_NAME Analysis)
20+
COMPONENT_NAME Analysis)
21+
22+
o2physics_add_dpl_workflow(tree-creator-pid-tpc-diagnostics
23+
SOURCES treeCreatorPidTpcDiagnostics.cxx
24+
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB
25+
COMPONENT_NAME Analysis)
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \file treeCreatorPidTpcDiagnostics.cxx
13+
/// \brief Creates trees with PID QA variables along with variables used for NN training
14+
///
15+
/// \author Ana Marin <ana.marin@cern.ch>
16+
/// \author Oleksii Lubynets <oleksii.lubynets@cern.ch>
17+
18+
#include "treeCreatorPidTpcDiagnostics.h"
19+
20+
#include "Common/CCDB/RCTSelectionFlags.h"
21+
#include "Common/CCDB/ctpRateFetcher.h"
22+
#include "Common/DataModel/EventSelection.h"
23+
#include "Common/DataModel/Multiplicity.h"
24+
#include "Common/DataModel/PIDResponseTOF.h"
25+
#include "Common/DataModel/PIDResponseTPC.h"
26+
#include "Common/DataModel/TrackSelectionTables.h"
27+
#include "DPG/Tasks/TPC/tpcSkimsTableCreator.h"
28+
#include "DPG/Tasks/TPC/utilsTpcSkimsTableCreator.h"
29+
30+
#include <CCDB/BasicCCDBManager.h>
31+
#include <Framework/ASoA.h>
32+
#include <Framework/AnalysisDataModel.h>
33+
#include <Framework/AnalysisHelpers.h>
34+
#include <Framework/AnalysisTask.h>
35+
#include <Framework/Configurable.h>
36+
#include <Framework/HistogramRegistry.h>
37+
#include <Framework/HistogramSpec.h>
38+
#include <Framework/InitContext.h>
39+
#include <Framework/Logger.h>
40+
#include <Framework/StaticFor.h>
41+
#include <Framework/runDataProcessing.h>
42+
#include <ReconstructionDataFormats/PID.h>
43+
44+
#include <TString.h>
45+
46+
#include <array>
47+
#include <cmath>
48+
#include <cstdint>
49+
#include <string>
50+
51+
using namespace o2;
52+
using namespace o2::framework;
53+
using namespace o2::track;
54+
using namespace o2::dpg_tpcskimstablecreator;
55+
56+
#define DO_FOR_ALL_PARTICLES(MACRO) \
57+
MACRO(El, Electron) \
58+
MACRO(Mu, Muon) \
59+
MACRO(Pi, Pion) \
60+
MACRO(Ka, Kaon) \
61+
MACRO(Pr, Proton) \
62+
MACRO(De, Deuteron) \
63+
MACRO(Tr, Triton) \
64+
MACRO(He, Helium3) \
65+
MACRO(Al, Alpha)
66+
67+
struct TreeCreatorPidTpcDiagnostics {
68+
Produces<o2::aod::PidTpcDiagnostics> rowPidTpcDiagnostics;
69+
70+
Configurable<int> applyEvSel{"applyEvSel", 2, "Flag to apply event selection: 0 -> no event selection, 1 -> Run 2 event selection, 2 -> Run 3 event selection"};
71+
Configurable<float> cutVtxZ{"cutVtxZ", 10.f, "Cut on vertex Z position [cm]"};
72+
Configurable<int> trackSelection{"trackSelection", 1, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"};
73+
Configurable<bool> requireGlobalTrack{"requireGlobalTrack", true, "Skip non-global tracks"};
74+
Configurable<bool> requireIts{"requireIts", true, "Skip tracks without ITS"};
75+
Configurable<int16_t> cutMinTPCNcls{"cutMinTPCNcls", 0, "Minimum number or TPC Clusters for tracks"};
76+
Configurable<float> cutRapidity{"cutRapidity", 999.f, "Rapidity cut"};
77+
Configurable<float> nClNorm{"nClNorm", 152.f, "Number of cluster normalization. Run 2: 159, Run 3 152"};
78+
// Configurable for the path of CCDB General Run Parameters LHC Interface information
79+
Configurable<std::string> ccdbPathGrpLhcIf{"ccdbPathGrpLhcIf", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"};
80+
// Configurables for output tables reservation size
81+
Configurable<float> reserveRatio{"reserveRatio", 1.f, "Ratio of how many rows expected in the output table to the input Tracks table size"};
82+
Configurable<bool> saveReserveQaHisto{"saveReserveQaHisto", true, "Flag to save the DF-wise ratio of output table size to that of input table"};
83+
// Configurables for run condtion table
84+
Configurable<std::string> rctLabel{"rctLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"};
85+
Configurable<bool> checkZdc{"checkZdc", false, "set ZDC flag for PbPb"};
86+
Configurable<bool> treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"};
87+
Configurable<bool> requireGoodRct{"requireGoodRct", false, "require good detector flag in run condtion table"};
88+
89+
#define DECLARE_PARTICLE_WISE_CONFIGURABLES(ParticleNameShort, ParticleNameLong) \
90+
Configurable<float> cutTpcInnerParameterMin##ParticleNameLong{"cutTpcInnerParameterMin" #ParticleNameLong, 0.f, "Lower-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \
91+
Configurable<float> cutTpcInnerParameterMax##ParticleNameLong{"cutTpcInnerParameterMax" #ParticleNameLong, 999.f, "Upper-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \
92+
Configurable<float> cutNSigmaTpcAbs##ParticleNameLong{"cutNSigmaTpcAbs" #ParticleNameLong, 999.f, "Cut on absolute value of nSigmaTpc for " #ParticleNameLong}; // o2-linter: disable=name/configurable (Configurable defined in macro)
93+
94+
DO_FOR_ALL_PARTICLES(DECLARE_PARTICLE_WISE_CONFIGURABLES)
95+
#undef DECLARE_PARTICLE_WISE_CONFIGURABLES
96+
97+
#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutTpcInnerParameterMin##ParticleNameLong,
98+
std::array<Configurable<float>*, PID::Alpha + 1> cutTpcInnerParameterMin{
99+
DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)};
100+
#undef PACK_CONFIGURABLES_TO_ARRAY
101+
102+
#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutTpcInnerParameterMax##ParticleNameLong,
103+
std::array<Configurable<float>*, PID::Alpha + 1> cutTpcInnerParameterMax{
104+
DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)};
105+
#undef PACK_CONFIGURABLES_TO_ARRAY
106+
107+
#define PACK_CONFIGURABLES_TO_ARRAY(ParticleNameShort, ParticleNameLong) &cutNSigmaTpcAbs##ParticleNameLong,
108+
std::array<Configurable<float>*, PID::Alpha + 1> cutNSigmaTpcAbs{
109+
DO_FOR_ALL_PARTICLES(PACK_CONFIGURABLES_TO_ARRAY)};
110+
#undef PACK_CONFIGURABLES_TO_ARRAY
111+
112+
HistogramRegistry registry{"registry", {}};
113+
114+
Service<o2::ccdb::BasicCCDBManager> ccdb{};
115+
116+
ctpRateFetcher mRateFetcher;
117+
118+
o2::aod::rctsel::RCTFlagsChecker rctChecker;
119+
120+
using CollisionsExtra = soa::Join<aod::Collisions, aod::Mults, aod::EvSels>;
121+
using TrackCandidates = soa::Join<aod::Tracks, aod::TracksExtra, aod::TrackSelection>;
122+
123+
Preslice<TrackCandidates> perCollisionTracks = aod::track::collisionId;
124+
125+
int mEnabledParticles{0};
126+
int mProcessedParticles{0};
127+
128+
template <o2::track::PID::ID ParticleId>
129+
bool initPerParticle()
130+
{
131+
static_assert(ParticleId >= 0 && ParticleId <= PID::Alpha && "Particle index outside limits");
132+
int enabledProcesses{0};
133+
134+
switch (ParticleId) {
135+
#define INIT_PARTICLE(ParticleNameShort, ParticleNameLong) \
136+
case PID::ParticleNameLong: \
137+
if (!doprocess##ParticleNameLong && !doprocessFull##ParticleNameLong && !doprocessFullWithTOF##ParticleNameLong) { \
138+
return false; \
139+
} \
140+
if (doprocess##ParticleNameLong) { \
141+
++enabledProcesses; \
142+
} \
143+
if (doprocessFull##ParticleNameLong) { \
144+
++enabledProcesses; \
145+
} \
146+
if (doprocessFullWithTOF##ParticleNameLong) { \
147+
++enabledProcesses; \
148+
} \
149+
LOG(info) << "Enabled TPC QA for " << #ParticleNameLong; \
150+
break;
151+
152+
DO_FOR_ALL_PARTICLES(INIT_PARTICLE)
153+
#undef INIT_PARTICLE
154+
}
155+
if (enabledProcesses != 1) {
156+
LOG(fatal) << "Cannot enable more than one process function per particle, check and retry!";
157+
}
158+
return true;
159+
}
160+
161+
void init(o2::framework::InitContext&)
162+
{
163+
static_for<0, PID::Alpha>([&](auto ParticleId) {
164+
mEnabledParticles += static_cast<int>(initPerParticle<ParticleId>());
165+
});
166+
167+
if (mEnabledParticles == 0) {
168+
LOG(fatal) << "At least one process function should be enabled, check and retry!";
169+
}
170+
171+
ccdb->setURL("http://alice-ccdb.cern.ch");
172+
ccdb->setCaching(true);
173+
ccdb->setFatalWhenNull(false);
174+
175+
rctChecker.init(rctLabel, checkZdc, treatLimitedAcceptanceAsBad);
176+
177+
if (saveReserveQaHisto) {
178+
registry.add("hOutputRatio", "Table out/in ratio;Table out/in ratio;Entries", {HistType::kTH1F, {{100, 0, reserveRatio}}});
179+
}
180+
}
181+
182+
template <o2::track::PID::ID ParticleId, bool IsFullTable, bool IsTofTable, typename TrackType>
183+
void processSingleParticle(CollisionsExtra const& collisions,
184+
TrackType const& tracks)
185+
{
186+
if (mProcessedParticles == 0) {
187+
rowPidTpcDiagnostics.reserve(tracks.size() * reserveRatio);
188+
}
189+
190+
std::string irSource{};
191+
float sqrtSNN{}; // placeholder to satisfy evaluateIrSourceAndSqrtSnn's signature
192+
bool isFirstCollision{true};
193+
for (const auto& collision : collisions) {
194+
if (!isEventSelected(collision, applyEvSel) || (std::abs(collision.posZ()) > cutVtxZ)) {
195+
continue;
196+
}
197+
198+
const bool isGoodRctEvent = rctChecker.checkTable(collision);
199+
if (requireGoodRct && !isGoodRctEvent) {
200+
continue;
201+
}
202+
203+
const auto bc = collision.bc_as<aod::BCsWithTimestamps>();
204+
if (isFirstCollision) {
205+
evaluateIrSourceAndSqrtSnn(ccdb, ccdbPathGrpLhcIf, bc.timestamp(), irSource, sqrtSNN);
206+
}
207+
isFirstCollision = false;
208+
const float ft0Occ = collision.ft0cOccupancyInTimeRange();
209+
const float multTPC = collision.multTPC() / MultiplicityNorm;
210+
const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo : 0.;
211+
212+
const auto tracksFromCollision = tracks.sliceBy(perCollisionTracks, static_cast<int>(collision.globalIndex()));
213+
214+
for (const auto& track : tracksFromCollision) {
215+
bool isGoodTrack = isTrackSelected(track, trackSelection);
216+
isGoodTrack &= (!requireGlobalTrack || track.isGlobalTrack());
217+
isGoodTrack &= (!requireIts || track.hasITS());
218+
isGoodTrack &= track.hasTPC();
219+
isGoodTrack &= (track.tpcNClsFound() >= cutMinTPCNcls);
220+
221+
const float rapidity = track.rapidity(PID::getMass(ParticleId));
222+
const float momentum = track.p();
223+
const float nClNormalized = std::sqrt(nClNorm / track.tpcNClsFound());
224+
const auto nclPID = static_cast<float>(track.tpcNClsPID());
225+
const float phi = track.phi();
226+
const float tgl = track.tgl();
227+
const float tpcInnerParam = track.tpcInnerParam();
228+
const float signed1Pt = track.signed1Pt();
229+
const float nSigmaTpc = o2::aod::pidutils::tpcNSigma<ParticleId>(track);
230+
231+
isGoodTrack &= (std::fabs(rapidity) <= cutRapidity);
232+
isGoodTrack &= (tpcInnerParam >= *cutTpcInnerParameterMin.at(ParticleId));
233+
isGoodTrack &= (tpcInnerParam <= *cutTpcInnerParameterMax.at(ParticleId));
234+
isGoodTrack &= (std::fabs(nSigmaTpc) <= *cutNSigmaTpcAbs.at(ParticleId));
235+
236+
if (!isGoodTrack) {
237+
continue;
238+
}
239+
240+
float dedxDiff{UndefValueFloat};
241+
float dedxExpected{UndefValueFloat};
242+
float expSigma{UndefValueFloat};
243+
244+
if constexpr (IsFullTable) {
245+
dedxDiff = o2::aod::pidutils::tpcExpSignalDiff<ParticleId>(track);
246+
dedxExpected = track.tpcSignal() - dedxDiff;
247+
expSigma = o2::aod::pidutils::tpcExpSigma<ParticleId>(track);
248+
}
249+
250+
float nSigmaTof{UndefValueFloat};
251+
252+
if constexpr (IsTofTable) {
253+
nSigmaTof = o2::aod::pidutils::tofNSigma<ParticleId>(track);
254+
}
255+
256+
rowPidTpcDiagnostics(isGoodRctEvent, ParticleId, ft0Occ, hadronicRate, multTPC, nClNormalized, nclPID, phi, tgl, tpcInnerParam, rapidity, momentum, signed1Pt, nSigmaTpc, dedxExpected, dedxDiff, expSigma, nSigmaTof);
257+
} // tracksFromCollision
258+
} // collisions
259+
++mProcessedParticles;
260+
if (mProcessedParticles == mEnabledParticles) {
261+
mProcessedParticles = 0;
262+
if (saveReserveQaHisto) {
263+
registry.fill(HIST("hOutputRatio"), static_cast<double>((rowPidTpcDiagnostics.lastIndex() + 1)) / tracks.size());
264+
}
265+
}
266+
}
267+
268+
#define MAKE_PROCESS_FUNCTIONS(ParticleNameShort, ParticleNameLong) \
269+
void process##ParticleNameLong(CollisionsExtra const& collisions, \
270+
soa::Join<TrackCandidates, aod::pidTPC##ParticleNameShort> const& tracks, \
271+
aod::BCsWithTimestamps const&) \
272+
{ \
273+
processSingleParticle<PID::ParticleNameLong, false, false>(collisions, tracks); \
274+
} \
275+
PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, process##ParticleNameLong, Form("Process for the %s hypothesis for TPC NSigma QA", #ParticleNameLong), false); \
276+
\
277+
void processFull##ParticleNameLong(CollisionsExtra const& collisions, \
278+
soa::Join<TrackCandidates, aod::pidTPCFull##ParticleNameShort> const& tracks, \
279+
aod::BCsWithTimestamps const&) \
280+
{ \
281+
processSingleParticle<PID::ParticleNameLong, true, false>(collisions, tracks); \
282+
} \
283+
PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, processFull##ParticleNameLong, Form("Process for the %s hypothesis for full TPC PID QA", #ParticleNameLong), false); \
284+
\
285+
void processFullWithTOF##ParticleNameLong(CollisionsExtra const& collisions, \
286+
soa::Join<TrackCandidates, aod::pidTPCFull##ParticleNameShort, aod::pidTOFFull##ParticleNameShort> const& tracks, \
287+
aod::BCsWithTimestamps const&) \
288+
{ \
289+
processSingleParticle<PID::ParticleNameLong, true, true>(collisions, tracks); \
290+
} \
291+
PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, processFullWithTOF##ParticleNameLong, Form("Process for the %s hypothesis for full TPC PID QA with the TOF info added", #ParticleNameLong), false);
292+
293+
DO_FOR_ALL_PARTICLES(MAKE_PROCESS_FUNCTIONS)
294+
#undef MAKE_PROCESS_FUNCTIONS
295+
};
296+
297+
WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
298+
{
299+
return WorkflowSpec{adaptAnalysisTask<TreeCreatorPidTpcDiagnostics>(cfgc)};
300+
}
301+
#undef DO_FOR_ALL_PARTICLES

0 commit comments

Comments
 (0)