|
| 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