|
| 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 dataCreatorHiddenCharmReduced.cxx |
| 13 | +/// \brief Reduced data creator for hidden-charm analyses at midrapidity |
| 14 | +/// |
| 15 | +/// \author A. Palasciano, <antonio.palasciano@cern.ch>, INFN Bari |
| 16 | +/// \author S. Politanò <stefano.politano@cern.ch>, CERN |
| 17 | + |
| 18 | +#include "PWGHF/Core/CentralityEstimation.h" |
| 19 | +#include "PWGHF/Core/SelectorCuts.h" |
| 20 | +#include "PWGHF/D2H/DataModel/ReducedDataModel.h" |
| 21 | +#include "PWGHF/D2H/Utils/utilsRedDataFormat.h" |
| 22 | +#include "PWGHF/DataModel/AliasTables.h" |
| 23 | +#include "PWGHF/DataModel/CandidateSelectionTables.h" |
| 24 | +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" |
| 25 | +#include "PWGHF/Utils/utilsAnalysis.h" |
| 26 | +#include "PWGHF/Utils/utilsBfieldCCDB.h" |
| 27 | +#include "PWGHF/Utils/utilsEvSelHf.h" |
| 28 | + |
| 29 | +#include "Common/Core/TPCVDriftManager.h" |
| 30 | +#include "Common/Core/ZorroSummary.h" |
| 31 | +#include "Common/DataModel/Centrality.h" |
| 32 | +#include "Common/DataModel/CollisionAssociationTables.h" |
| 33 | +#include "Common/DataModel/EventSelection.h" |
| 34 | +#include "Common/DataModel/PIDResponseTOF.h" |
| 35 | +#include "Common/DataModel/PIDResponseTPC.h" |
| 36 | +#include "Common/DataModel/TrackSelectionTables.h" |
| 37 | + |
| 38 | +#include <CCDB/BasicCCDBManager.h> |
| 39 | +#include <CCDB/CcdbApi.h> |
| 40 | +#include <DetectorsBase/MatLayerCylSet.h> |
| 41 | +#include <DetectorsBase/Propagator.h> |
| 42 | +#include <Framework/AnalysisDataModel.h> |
| 43 | +#include <Framework/AnalysisTask.h> |
| 44 | +#include <Framework/Configurable.h> |
| 45 | +#include <Framework/DeviceSpec.h> |
| 46 | +#include <Framework/HistogramRegistry.h> |
| 47 | +#include <Framework/HistogramSpec.h> |
| 48 | +#include <Framework/InitContext.h> |
| 49 | +#include <Framework/Logger.h> |
| 50 | +#include <Framework/runDataProcessing.h> |
| 51 | + |
| 52 | +#include <array> |
| 53 | +#include <cmath> |
| 54 | +#include <numeric> |
| 55 | +#include <string> |
| 56 | +#include <vector> |
| 57 | + |
| 58 | +using namespace o2; |
| 59 | +using namespace o2::analysis; |
| 60 | +using namespace o2::aod; |
| 61 | +using namespace o2::framework; |
| 62 | +using namespace o2::framework::expressions; |
| 63 | +using namespace o2::hf_centrality; |
| 64 | + |
| 65 | +enum TrackType : uint8_t { |
| 66 | + Pion = 0, |
| 67 | + Kaon, |
| 68 | + Proton |
| 69 | +}; |
| 70 | + |
| 71 | +struct HfDataCreatorHiddenCharmReduced { |
| 72 | + |
| 73 | + Produces<aod::HfRedCollisions> hfReducedCollision; |
| 74 | + Produces<aod::HfOrigColCounts> hfCollisionCounter; |
| 75 | + Produces<aod::HcSelTracks> hfTrackLite; |
| 76 | + |
| 77 | + struct : ConfigurableGroup { |
| 78 | + // track quality |
| 79 | + Configurable<bool> fillHistograms{"fillHistograms", true, "Fill proton QA histograms"}; |
| 80 | + Configurable<bool> selectProtons{"selectProtons", true, "Select protons"}; |
| 81 | + Configurable<int> itsNClsMin{"itsNClsMin", 5, "Minimum number of ITS clusters"}; |
| 82 | + Configurable<int> tpcNClsFoundMin{"tpcNClsFoundMin", 50, "Minimum number of found TPC clusters"}; |
| 83 | + Configurable<int> tpcNClsCrossedRowsMin{"tpcNClsCrossedRowsMin", 80, "Minimum number of crossed TPC rows"}; |
| 84 | + Configurable<double> ptMinTrack{"ptMinTrack", 0.5, "Minimum proton-track pT"}; |
| 85 | + Configurable<double> etaMaxTrack{"etaMaxTrack", 0.8, "Maximum proton-track |eta|"}; |
| 86 | + Configurable<float> momForCombinedPid{"momForCombinedPid", 0.75f, "Momentum threshold above which combined TPC+TOF proton PID is used"}; |
| 87 | + Configurable<std::vector<double>> binsPtTrack{"binsPtTrack", std::vector<double>{hf_cuts_single_track::vecBinsPtTrack}, "Track pT bin limits for DCA cuts"}; |
| 88 | + Configurable<LabeledArray<double>> cutsTrack{"cutsTrack", {hf_cuts_single_track::CutsTrack[0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track DCA selections per pT bin"}; |
| 89 | + Configurable<float> maxNsigmaTofPi{"maxNsigmaTofPi", 2.f, "Maximum pion n-sigma in TOF for proton rejection"}; |
| 90 | + Configurable<float> maxNsigmaTofKa{"maxNsigmaTofKa", 2.f, "Maximum kaon n-sigma in TOF for proton rejection"}; |
| 91 | + Configurable<float> maxNsigmaCombinedPr{"maxNsigmaCombinedPr", 3.f, "Maximum combined proton n-sigma from TPC and TOF"}; |
| 92 | + Configurable<float> maxNsigmaTpcPi{"maxNsigmaTpcPi", 2.f, "Maximum pion n-sigma in TPC for proton rejection"}; |
| 93 | + Configurable<float> maxNsigmaTpcKa{"maxNsigmaTpcKa", 2.f, "Maximum kaon n-sigma in TPC for proton rejection"}; |
| 94 | + Configurable<float> maxNsigmaTpcPr{"maxNsigmaTpcPr", 3.f, "Maximum proton n-sigma in TPC"}; |
| 95 | + Configurable<bool> forceTOF{"forceTOF", false, "Require TOF PID information for proton selection"}; |
| 96 | + } config; |
| 97 | + |
| 98 | + // Configurable (others) |
| 99 | + Configurable<std::string> ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; |
| 100 | + Configurable<std::string> ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; |
| 101 | + Configurable<bool> doMcRecQa{"doMcRecQa", true, "Fill QA histograms for Mc matching"}; |
| 102 | + Configurable<bool> rejectPairsWithCommonDaughter{"rejectPairsWithCommonDaughter", true, "flag to reject already at this stage the pairs that share a daughter track"}; |
| 103 | + Configurable<bool> rejectCollisionsWithBadEvSel{"rejectCollisionsWithBadEvSel", true, "flag to reject collisions with bad event selection"}; |
| 104 | + |
| 105 | + o2::hf_evsel::HfEventSelection hfEvSel; |
| 106 | + o2::hf_evsel::HfEventSelectionMc hfEvSelMc; |
| 107 | + |
| 108 | + double bz{0.}; |
| 109 | + int runNumber{0}; // needed to detect if the run changed and trigger update of calibrations etc. |
| 110 | + |
| 111 | + // material correction for track propagation |
| 112 | + o2::base::MatLayerCylSet* lut{}; |
| 113 | + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; |
| 114 | + o2::aod::common::TPCVDriftManager vDriftMgr; |
| 115 | + o2::ccdb::CcdbApi ccdbApi; |
| 116 | + Service<o2::ccdb::BasicCCDBManager> ccdb{}; |
| 117 | + |
| 118 | + using BCsInfo = soa::Join<aod::BCs, aod::Timestamps, aod::BcSels>; |
| 119 | + using TracksWithPID = soa::Join<aod::Tracks, aod::TracksExtra, aod::TracksDCA, aod::TrackSelection, aod::pidTPCFullPi, aod::pidTOFFullPi, aod::pidTPCFullKa, aod::pidTOFFullKa, aod::pidTPCFullPr, aod::pidTOFFullPr>; |
| 120 | + |
| 121 | + Preslice<aod::TrackAssoc> trackIndicesPerCollision = aod::track_association::collisionId; |
| 122 | + |
| 123 | + HistogramRegistry registry{"registry"}; |
| 124 | + OutputObj<ZorroSummary> zorroSummary{"zorroSummary"}; |
| 125 | + |
| 126 | + void init(InitContext& initContext) |
| 127 | + { |
| 128 | + // Configure CCDB access |
| 129 | + ccdb->setURL(ccdbUrl.value); |
| 130 | + ccdb->setCaching(true); |
| 131 | + ccdb->setLocalObjectValidityChecking(); |
| 132 | + ccdb->setCreatedNotAfter(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()); |
| 133 | + ccdbApi.init(ccdbUrl); |
| 134 | + runNumber = 0; |
| 135 | + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get<o2::base::MatLayerCylSet>("GLO/Param/MatLUT")); |
| 136 | + |
| 137 | + if (config.fillHistograms) { |
| 138 | + const AxisSpec axisPt{360, 0., 36., "#it{p}_{T}^{proton} (GeV/#it{c})"}; |
| 139 | + const AxisSpec axisEta{100, -1., 1., "#eta"}; |
| 140 | + const AxisSpec axisDca{400, -2., 2., "DCA_{xy} to primary vertex (cm)"}; |
| 141 | + const AxisSpec axisNSigma{100, -5., 5., "n#sigma"}; |
| 142 | + |
| 143 | + registry.add("hPzVtx", "Z position of primary vertex for selected tracks;z_{vtx} (cm);entries", {HistType::kTH1D, {AxisSpec{200, -20., 20., "z_{vtx} (cm)"}}}); |
| 144 | + registry.add("hPtNoCuts", "All associated tracks;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPt}}); |
| 145 | + registry.add("hPtCutsProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPt}}); |
| 146 | + registry.add("hEtaCutsProton", "Selected proton tracks;#eta;entries", {HistType::kTH1D, {axisEta}}); |
| 147 | + registry.add("hDCAToPrimXYVsPtCutsProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});DCA_{xy} to primary vertex (cm)", {HistType::kTH2D, {axisPt, axisDca}}); |
| 148 | + registry.add("hNSigmaTPCProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});n#sigma_{TPC}", {HistType::kTH2D, {axisPt, axisNSigma}}); |
| 149 | + registry.add("hNSigmaTOFProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});n#sigma_{TOF}", {HistType::kTH2D, {axisPt, axisNSigma}}); |
| 150 | + registry.add("hInvMass", "Invariant mass of selected proton with all other tracks in the event;#it{p}_{T}^{proton} (GeV/#it{c});invariant mass with other tracks (GeV/#it{c}^{2})", {HistType::kTH2D, {axisPt, AxisSpec{100, 2.85, 3.25, "invariant mass with other tracks (GeV/#it{c}^{2})"}}}); |
| 151 | + } |
| 152 | + |
| 153 | + // init HF event selection helper |
| 154 | + hfEvSel.init(registry, &zorroSummary); |
| 155 | + |
| 156 | + const auto& workflows = initContext.services().get<RunningWorkflowInfo const>(); |
| 157 | + for (const DeviceSpec& device : workflows.devices) { |
| 158 | + if (device.name == "hf-data-creator-charm-reso-to-d0-reduced") { |
| 159 | + // init HF event selection helper |
| 160 | + hfEvSelMc.init(device, registry); |
| 161 | + break; |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + template <typename TTrack> |
| 167 | + bool isSelectedPid(TTrack const& track) const |
| 168 | + { |
| 169 | + const float momForCombinedPid = config.momForCombinedPid.value; |
| 170 | + const float maxNsigmaTpcPr = config.maxNsigmaTpcPr.value; |
| 171 | + const float maxNsigmaTpcPi = config.maxNsigmaTpcPi.value; |
| 172 | + const float maxNsigmaTpcKa = config.maxNsigmaTpcKa.value; |
| 173 | + const float maxNsigmaCombinedPr = config.maxNsigmaCombinedPr.value; |
| 174 | + const float maxNsigmaTofPi = config.maxNsigmaTofPi.value; |
| 175 | + const float maxNsigmaTofKa = config.maxNsigmaTofKa.value; |
| 176 | + const bool forceTOF = config.forceTOF.value; |
| 177 | + |
| 178 | + const float mom = std::hypot(track.px(), track.py(), track.pz()); |
| 179 | + const float nSigmaTPCPr = track.tpcNSigmaPr(); |
| 180 | + const float nSigmaTPCPi = track.tpcNSigmaPi(); |
| 181 | + const float nSigmaTPCKa = track.tpcNSigmaKa(); |
| 182 | + const bool hasTOF = track.hasTOF(); |
| 183 | + if (!hasTOF && forceTOF) { |
| 184 | + return false; |
| 185 | + } |
| 186 | + |
| 187 | + bool isProton = false; |
| 188 | + bool rejectAsPion = false; |
| 189 | + bool rejectAsKaon = false; |
| 190 | + |
| 191 | + if (mom < momForCombinedPid || !hasTOF) { |
| 192 | + isProton = std::abs(nSigmaTPCPr) < maxNsigmaTpcPr; |
| 193 | + rejectAsPion = std::abs(nSigmaTPCPi) < maxNsigmaTpcPi; |
| 194 | + rejectAsKaon = std::abs(nSigmaTPCKa) < maxNsigmaTpcKa; |
| 195 | + } else { |
| 196 | + const float nSigmaTOFPr = track.tofNSigmaPr(); |
| 197 | + const float nSigmaTOFPi = track.tofNSigmaPi(); |
| 198 | + const float nSigmaTOFKa = track.tofNSigmaKa(); |
| 199 | + isProton = std::hypot(nSigmaTPCPr, nSigmaTOFPr) < maxNsigmaCombinedPr; |
| 200 | + rejectAsPion = std::hypot(nSigmaTPCPi, nSigmaTOFPi) < maxNsigmaTofPi; |
| 201 | + rejectAsKaon = std::hypot(nSigmaTPCKa, nSigmaTOFKa) < maxNsigmaTofKa; |
| 202 | + } |
| 203 | + return isProton && !rejectAsPion && !rejectAsKaon; |
| 204 | + } |
| 205 | + |
| 206 | + template <typename TTrack> |
| 207 | + bool isSelectedTrack(TTrack const& track) const |
| 208 | + { |
| 209 | + const int tpcNClsFoundMin = config.tpcNClsFoundMin.value; |
| 210 | + const int tpcNClsCrossedRowsMin = config.tpcNClsCrossedRowsMin.value; |
| 211 | + const int itsNClsMin = config.itsNClsMin.value; |
| 212 | + const double etaMaxTrack = config.etaMaxTrack.value; |
| 213 | + const double ptMinTrack = config.ptMinTrack.value; |
| 214 | + |
| 215 | + if (!track.isGlobalTrackWoDCA()) { |
| 216 | + return false; |
| 217 | + } |
| 218 | + if (track.tpcNClsFound() < tpcNClsFoundMin) { |
| 219 | + return false; |
| 220 | + } |
| 221 | + if (track.tpcNClsCrossedRows() < tpcNClsCrossedRowsMin) { |
| 222 | + return false; |
| 223 | + } |
| 224 | + if (track.itsNCls() < itsNClsMin) { |
| 225 | + return false; |
| 226 | + } |
| 227 | + if (std::abs(track.eta()) > etaMaxTrack) { |
| 228 | + return false; |
| 229 | + } |
| 230 | + if (track.pt() < ptMinTrack) { |
| 231 | + return false; |
| 232 | + } |
| 233 | + if (!isSelectedTrackDca(config.binsPtTrack, config.cutsTrack, track.pt(), track.dcaXY(), track.dcaZ())) { |
| 234 | + return false; |
| 235 | + } |
| 236 | + return isSelectedPid(track); |
| 237 | + } |
| 238 | + |
| 239 | + void processEtaCTrack(soa::Join<aod::Collisions, aod::EvSels> const& collisions, |
| 240 | + aod::TrackAssoc const& trackIndices, |
| 241 | + TracksWithPID const& tracks, |
| 242 | + aod::BCsWithTimestamps const&) |
| 243 | + { |
| 244 | + hfTrackLite.reserve(tracks.size()); |
| 245 | + |
| 246 | + int zvtxColl{0}; |
| 247 | + int sel8Coll{0}; |
| 248 | + int zvtxAndSel8Coll{0}; |
| 249 | + int zvtxAndSel8CollAndSoftTrig{0}; |
| 250 | + int allSelColl{0}; |
| 251 | + constexpr int NDaughtersCharmMeson = 2; |
| 252 | + for (const auto& collision : collisions) { |
| 253 | + const auto hfRejMap = o2::hf_evsel::getEvSel<true, o2::hf_centrality::CentralityEstimator::None, aod::BCsWithTimestamps>(collision, hfEvSel, zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl, ccdb, registry); |
| 254 | + if (rejectCollisionsWithBadEvSel && hfRejMap != 0) { |
| 255 | + continue; |
| 256 | + } |
| 257 | + const auto thisCollId = collision.globalIndex(); |
| 258 | + const auto trackIds = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); |
| 259 | + if (config.fillHistograms) { |
| 260 | + registry.fill(HIST("hPzVtx"), collision.posZ()); |
| 261 | + } |
| 262 | + std::vector<int> selectedTrackIds; |
| 263 | + for (const auto& trkId : trackIds) { |
| 264 | + auto trk = trkId.track_as<TracksWithPID>(); |
| 265 | + if (config.fillHistograms) { |
| 266 | + registry.fill(HIST("hPtNoCuts"), trk.pt()); |
| 267 | + } |
| 268 | + if (!isSelectedTrack(trk)) { |
| 269 | + continue; |
| 270 | + } |
| 271 | + std::array pVecProton{trk.pVector()}; |
| 272 | + hfTrackLite(trk.globalIndex(), collision.globalIndex(), pVecProton[0], pVecProton[1], pVecProton[2], trk.sign(), static_cast<uint8_t>(TrackType::Proton)); |
| 273 | + selectedTrackIds.push_back(trk.globalIndex()); |
| 274 | + if (config.fillHistograms) { |
| 275 | + registry.fill(HIST("hPtCutsProton"), trk.pt()); |
| 276 | + registry.fill(HIST("hEtaCutsProton"), trk.eta()); |
| 277 | + registry.fill(HIST("hDCAToPrimXYVsPtCutsProton"), trk.pt(), trk.dcaXY()); |
| 278 | + registry.fill(HIST("hNSigmaTPCProton"), trk.pt(), trk.tpcNSigmaPr()); |
| 279 | + if (trk.hasTOF()) { |
| 280 | + registry.fill(HIST("hNSigmaTOFProton"), trk.pt(), trk.tofNSigmaPr()); |
| 281 | + } |
| 282 | + } |
| 283 | + } |
| 284 | + if (selectedTrackIds.size() < nDaughtersCharmMeson) { |
| 285 | + continue; |
| 286 | + } |
| 287 | + hfReducedCollision(collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), hfRejMap, bz); |
| 288 | + for (size_t i = 0; i < selectedTrackIds.size(); ++i) { |
| 289 | + auto t1 = tracks.rawIteratorAt(selectedTrackIds[i]); |
| 290 | + std::array pVec1{t1.pVector()}; |
| 291 | + for (size_t j = i + 1; j < selectedTrackIds.size(); ++j) { |
| 292 | + auto t2 = tracks.rawIteratorAt(selectedTrackIds[j]); |
| 293 | + if (t1.sign() * t2.sign() > 0) { |
| 294 | + continue; |
| 295 | + } |
| 296 | + std::array pVec2{t2.pVector()}; |
| 297 | + float invMass = RecoDecay::m(std::array{pVec1, pVec2}, std::array{o2::constants::physics::MassProton, o2::constants::physics::MassProton}); |
| 298 | + float ptEtac = RecoDecay::pt(RecoDecay::sumOfVec(pVec1, pVec2)); |
| 299 | + if (config.fillHistograms) { |
| 300 | + registry.fill(HIST("hInvMass"), ptEtac, invMass); |
| 301 | + } |
| 302 | + } |
| 303 | + } |
| 304 | + } |
| 305 | + hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); |
| 306 | + } |
| 307 | + |
| 308 | + PROCESS_SWITCH(HfDataCreatorHiddenCharmReduced, processEtaCTrack, "EtaC -> p pbar reconstruction", true); |
| 309 | +}; |
| 310 | + |
| 311 | +// struct |
| 312 | + |
| 313 | +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) |
| 314 | +{ |
| 315 | + WorkflowSpec workflow{}; |
| 316 | + workflow.push_back(adaptAnalysisTask<HfDataCreatorHiddenCharmReduced>(cfgc)); |
| 317 | + return workflow; |
| 318 | +} |
0 commit comments