Project
Loading...
Searching...
No Matches
SVertexer.cxx
Go to the documentation of this file.
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
15
21#include "TPCFastTransformPOD.h"
27
28#ifdef WITH_OPENMP
29#include <omp.h>
30#endif
31
33
34using namespace o2::vertexing;
35namespace o2f = o2::framework;
40
41//__________________________________________________________________
43{
44 mRecoCont = &recoData;
45 mNV0s = mNCascades = mN3Bodies = 0;
46 updateTimeDependentParams(); // TODO RS: strictly speaking, one should do this only in case of the CCDB objects update
47 mPVertices = recoData.getPrimaryVertices();
48 buildT2V(recoData); // build track->vertex refs from vertex->track (if other workflow will need this, consider producing a message in the VertexTrackMatcher)
49 int ntrP = mTracksPool[POS].size(), ntrN = mTracksPool[NEG].size();
50 if (mStrTracker) {
51 mStrTracker->loadData(recoData);
52 mStrTracker->prepareITStracks();
53 }
54#ifdef WITH_OPENMP
55 int dynGrp = std::min(4, std::max(1, mNThreads / 2));
56#pragma omp parallel for schedule(dynamic, dynGrp) num_threads(mNThreads)
57#endif
58 for (int itp = 0; itp < ntrP; itp++) {
59 auto& seedP = mTracksPool[POS][itp];
60 const int firstN = mVtxFirstTrack[NEG][seedP.vBracket.getMin()];
61 if (firstN < 0) {
62 LOG(debug) << "No partner is found for pos.track " << itp << " out of " << ntrP;
63 continue;
64 }
65 for (int itn = firstN; itn < ntrN; itn++) { // start from the 1st negative track of lowest-ID vertex of positive
66 auto& seedN = mTracksPool[NEG][itn];
67 if (seedN.vBracket > seedP.vBracket) { // all vertices compatible with seedN are in future wrt that of seedP
68 LOG(debug) << "Brackets do not match";
69 break;
70 }
71 if (mSVParams->maxPVContributors < 2 && seedP.gid.isPVContributor() + seedN.gid.isPVContributor() > mSVParams->maxPVContributors) {
72 continue;
73 }
74#ifdef WITH_OPENMP
75 int iThread = omp_get_thread_num();
76#else
77 int iThread = 0;
78#endif
79 checkV0(seedP, seedN, itp, itn, iThread);
80 }
81 }
82
83 produceOutput(pc);
84}
85
86//__________________________________________________________________
88{
89 // sort V0s and Cascades in vertex id
90 struct vid {
91 int thrID;
92 int entry;
93 int vtxID;
94 };
95 for (int ith = 0; ith < mNThreads; ith++) {
96 mNV0s += mV0sIdxTmp[ith].size();
97 mNCascades += mCascadesIdxTmp[ith].size();
98 mN3Bodies += m3bodyIdxTmp[ith].size();
99 }
100 std::vector<vid> v0SortID, cascSortID, nbodySortID;
101 v0SortID.reserve(mNV0s);
102 cascSortID.reserve(mNCascades);
103 nbodySortID.reserve(mN3Bodies);
104 for (int ith = 0; ith < mNThreads; ith++) {
105 for (int j = 0; j < (int)mV0sIdxTmp[ith].size(); j++) {
106 v0SortID.emplace_back(vid{ith, j, mV0sIdxTmp[ith][j].getVertexID()});
107 }
108 for (int j = 0; j < (int)mCascadesIdxTmp[ith].size(); j++) {
109 cascSortID.emplace_back(vid{ith, j, mCascadesIdxTmp[ith][j].getVertexID()});
110 }
111 for (int j = 0; j < (int)m3bodyIdxTmp[ith].size(); j++) {
112 nbodySortID.emplace_back(vid{ith, j, m3bodyIdxTmp[ith][j].getVertexID()});
113 }
114 }
115 std::sort(v0SortID.begin(), v0SortID.end(), [](const vid& a, const vid& b) { return a.vtxID < b.vtxID; });
116 std::sort(cascSortID.begin(), cascSortID.end(), [](const vid& a, const vid& b) { return a.vtxID < b.vtxID; });
117 std::sort(nbodySortID.begin(), nbodySortID.end(), [](const vid& a, const vid& b) { return a.vtxID < b.vtxID; });
118
119 // dpl output
120 auto& v0sIdx = pc.outputs().make<std::vector<V0Index>>(o2f::Output{"GLO", "V0S_IDX", 0});
121 auto& cascsIdx = pc.outputs().make<std::vector<CascadeIndex>>(o2f::Output{"GLO", "CASCS_IDX", 0});
122 auto& body3Idx = pc.outputs().make<std::vector<Decay3BodyIndex>>(o2f::Output{"GLO", "DECAYS3BODY_IDX", 0});
123 auto& fullv0s = pc.outputs().make<std::vector<V0>>(o2f::Output{"GLO", "V0S", 0});
124 auto& fullcascs = pc.outputs().make<std::vector<Cascade>>(o2f::Output{"GLO", "CASCS", 0});
125 auto& full3body = pc.outputs().make<std::vector<Decay3Body>>(o2f::Output{"GLO", "DECAYS3BODY", 0});
126 auto& v0Refs = pc.outputs().make<std::vector<RRef>>(o2f::Output{"GLO", "PVTX_V0REFS", 0});
127 auto& cascRefs = pc.outputs().make<std::vector<RRef>>(o2f::Output{"GLO", "PVTX_CASCREFS", 0});
128 auto& vtx3bodyRefs = pc.outputs().make<std::vector<RRef>>(o2f::Output{"GLO", "PVTX_3BODYREFS", 0});
129
130 // sorted V0s
131 v0sIdx.reserve(mNV0s);
132 if (mSVParams->createFullV0s) {
133 fullv0s.reserve(mNV0s);
134 }
135 // sorted Cascades
136 cascsIdx.reserve(mNCascades);
137 if (mSVParams->createFullCascades) {
138 fullcascs.reserve(mNCascades);
139 }
140 // sorted 3 body decays
141 body3Idx.reserve(mN3Bodies);
142 if (mSVParams->createFull3Bodies) {
143 full3body.reserve(mN3Bodies);
144 }
145
146 for (const auto& id : v0SortID) {
147 auto& v0idx = mV0sIdxTmp[id.thrID][id.entry];
148 int pos = v0sIdx.size();
149 v0sIdx.push_back(v0idx);
150 v0idx.setVertexID(pos); // this v0 copy will be discarded, use its vertexID to store the new position of final V0
151 if (mSVParams->createFullV0s) {
152 fullv0s.push_back(mV0sTmp[id.thrID][id.entry]);
153 }
154 }
155 // since V0s were reshuffled, we need to correct the cascade -> V0 reference indices
156 for (int ith = 0; ith < mNThreads; ith++) { // merge results of all threads
157 for (size_t ic = 0; ic < mCascadesIdxTmp[ith].size(); ic++) { // before merging fix cascades references on v0
158 auto& cidx = mCascadesIdxTmp[ith][ic];
159 cidx.setV0ID(mV0sIdxTmp[ith][cidx.getV0ID()].getVertexID());
160 }
161 }
162 int cascCnt = 0;
163 for (const auto& id : cascSortID) {
164 cascsIdx.push_back(mCascadesIdxTmp[id.thrID][id.entry]);
165 mCascadesIdxTmp[id.thrID][id.entry].setVertexID(cascCnt++); // memorize new ID
166 if (mSVParams->createFullCascades) {
167 fullcascs.push_back(mCascadesTmp[id.thrID][id.entry]);
168 }
169 }
170 int b3cnt = 0;
171 for (const auto& id : nbodySortID) {
172 body3Idx.push_back(m3bodyIdxTmp[id.thrID][id.entry]);
173 m3bodyIdxTmp[id.thrID][id.entry].setVertexID(b3cnt++); // memorize new ID
174 if (mSVParams->createFull3Bodies) {
175 full3body.push_back(m3bodyTmp[id.thrID][id.entry]);
176 }
177 }
178 if (mStrTracker) {
179 mNStrangeTracks = 0;
180 for (int ith = 0; ith < mNThreads; ith++) {
181 mNStrangeTracks += mStrTracker->getNTracks(ith);
182 }
183
184 std::vector<o2::dataformats::StrangeTrack> strTracksTmp;
185 std::vector<o2::strangeness_tracking::ClusAttachments> strClusTmp;
186 std::vector<o2::MCCompLabel> mcLabTmp;
187 strTracksTmp.reserve(mNStrangeTracks);
188 strClusTmp.reserve(mNStrangeTracks);
189 if (mStrTracker->getMCTruthOn()) {
190 mcLabTmp.reserve(mNStrangeTracks);
191 }
192
193 for (int ith = 0; ith < mNThreads; ith++) { // merge results of all threads
194 auto& strTracks = mStrTracker->getStrangeTrackVec(ith);
195 auto& strClust = mStrTracker->getClusAttachments(ith);
196 auto& stcTrMCLab = mStrTracker->getStrangeTrackLabels(ith);
197 for (int i = 0; i < (int)strTracks.size(); i++) {
198 auto& t = strTracks[i];
199 if (t.mPartType == o2::dataformats::kStrkV0) {
200 t.mDecayRef = mV0sIdxTmp[ith][t.mDecayRef].getVertexID(); // reassign merged V0 ID
201 } else if (t.mPartType == o2::dataformats::kStrkCascade) {
202 t.mDecayRef = mCascadesIdxTmp[ith][t.mDecayRef].getVertexID(); // reassign merged Cascase ID
203 } else if (t.mPartType == o2::dataformats::kStrkThreeBody) {
204 t.mDecayRef = m3bodyIdxTmp[ith][t.mDecayRef].getVertexID(); // reassign merged Cascase ID
205 } else {
206 LOGP(fatal, "Unknown strange track decay reference type {} for index {}", int(t.mPartType), t.mDecayRef);
207 }
208
209 strTracksTmp.push_back(t);
210 strClusTmp.push_back(strClust[i]);
211 if (mStrTracker->getMCTruthOn()) {
212 mcLabTmp.push_back(stcTrMCLab[i]);
213 }
214 }
215 }
216
217 auto& strTracksOut = pc.outputs().make<std::vector<o2::dataformats::StrangeTrack>>(o2f::Output{"GLO", "STRANGETRACKS", 0});
218 auto& strClustOut = pc.outputs().make<std::vector<o2::strangeness_tracking::ClusAttachments>>(o2f::Output{"GLO", "CLUSUPDATES", 0});
220 strTracksOut.resize(mNStrangeTracks);
221 strClustOut.resize(mNStrangeTracks);
222 if (mStrTracker->getMCTruthOn()) {
223 mcLabsOut.resize(mNStrangeTracks);
224 }
225
226 std::vector<int> sortIdx(strTracksTmp.size());
227 std::iota(sortIdx.begin(), sortIdx.end(), 0);
228 // if mNTreads > 1 we need to sort tracks, clus and MCLabs by their mDecayRef
229 if (mNThreads > 1 && mNStrangeTracks > 1) {
230 std::sort(sortIdx.begin(), sortIdx.end(), [&strTracksTmp](int i1, int i2) { return strTracksTmp[i1].mDecayRef < strTracksTmp[i2].mDecayRef; });
231 }
232
233 for (int i = 0; i < (int)sortIdx.size(); i++) {
234 strTracksOut[i] = strTracksTmp[sortIdx[i]];
235 strClustOut[i] = strClusTmp[sortIdx[i]];
236 if (mStrTracker->getMCTruthOn()) {
237 mcLabsOut[i] = mcLabTmp[sortIdx[i]];
238 }
239 }
240
241 if (mStrTracker->getMCTruthOn()) {
242 auto& strTrMCLableOut = pc.outputs().make<std::vector<o2::MCCompLabel>>(o2f::Output{"GLO", "STRANGETRACKS_MC", 0});
243 strTrMCLableOut.swap(mcLabsOut);
244 }
245 }
246
247 for (int ith = 0; ith < mNThreads; ith++) { // clean unneeded s.vertices
248 mV0sTmp[ith].clear();
249 mCascadesTmp[ith].clear();
250 m3bodyTmp[ith].clear();
251 mV0sIdxTmp[ith].clear();
252 mCascadesIdxTmp[ith].clear();
253 m3bodyIdxTmp[ith].clear();
254 }
255
256 extractPVReferences(v0sIdx, v0Refs, cascsIdx, cascRefs, body3Idx, vtx3bodyRefs);
257}
258
259//__________________________________________________________________
261{
262}
263
264//__________________________________________________________________
265void SVertexer::updateTimeDependentParams()
266{
267 // TODO RS: strictly speaking, one should do this only in case of the CCDB objects update
268 static bool updatedOnce = false;
269 if (!updatedOnce) {
270 updatedOnce = true;
271 mSVParams = &SVertexerParams::Instance();
272 if (mSVParams->mExcludeTPCtracks && !mRecoCont->isTrackSourceLoaded(GIndex::TPC)) {
273 LOGP(fatal, "TPC tracks requested but not provided");
274 }
275 // precalculated selection cuts
276 mMinR2ToMeanVertex = mSVParams->minRToMeanVertex * mSVParams->minRToMeanVertex;
277 mMaxR2ToMeanVertexCascV0 = mSVParams->maxRToMeanVertexCascV0 * mSVParams->maxRToMeanVertexCascV0;
278 mMaxDCAXY2ToMeanVertex = mSVParams->maxDCAXYToMeanVertex * mSVParams->maxDCAXYToMeanVertex;
279 mMaxDCAXY2ToMeanVertexV0Casc = mSVParams->maxDCAXYToMeanVertexV0Casc * mSVParams->maxDCAXYToMeanVertexV0Casc;
280 mMaxDCAXY2ToMeanVertex3bodyV0 = mSVParams->maxDCAXYToMeanVertex3bodyV0 * mSVParams->maxDCAXYToMeanVertex3bodyV0;
281 mMinR2DiffV0Casc = mSVParams->minRDiffV0Casc * mSVParams->minRDiffV0Casc;
282 mMinPt2V0 = mSVParams->minPtV0 * mSVParams->minPtV0;
283 mMaxTgl2V0 = mSVParams->maxTglV0 * mSVParams->maxTglV0;
284 mMinPt2Casc = mSVParams->minPtCasc * mSVParams->minPtCasc;
285 mMaxTgl2Casc = mSVParams->maxTglCasc * mSVParams->maxTglCasc;
286 mMinPt23Body = mSVParams->minPt3Body * mSVParams->minPt3Body;
287 mMaxTgl23Body = mSVParams->maxTgl3Body * mSVParams->maxTgl3Body;
288 setupThreads();
289 }
290 auto bz = o2::base::Propagator::Instance()->getNominalBz();
291 mV0Hyps[HypV0::Photon].set(PID::Photon, PID::Electron, PID::Electron, mSVParams->pidCutsPhoton, bz);
292 mV0Hyps[HypV0::K0].set(PID::K0, PID::Pion, PID::Pion, mSVParams->pidCutsK0, bz);
293 mV0Hyps[HypV0::Lambda].set(PID::Lambda, PID::Proton, PID::Pion, mSVParams->pidCutsLambda, bz);
294 mV0Hyps[HypV0::AntiLambda].set(PID::Lambda, PID::Pion, PID::Proton, mSVParams->pidCutsLambda, bz);
295 mV0Hyps[HypV0::HyperTriton].set(PID::HyperTriton, PID::Helium3, PID::Pion, mSVParams->pidCutsHTriton, bz);
296 mV0Hyps[HypV0::AntiHyperTriton].set(PID::HyperTriton, PID::Pion, PID::Helium3, mSVParams->pidCutsHTriton, bz);
297 mV0Hyps[HypV0::Hyperhydrog4].set(PID::Hyperhydrog4, PID::Alpha, PID::Pion, mSVParams->pidCutsHhydrog4, bz);
298 mV0Hyps[HypV0::AntiHyperhydrog4].set(PID::Hyperhydrog4, PID::Pion, PID::Alpha, mSVParams->pidCutsHhydrog4, bz);
299 mCascHyps[HypCascade::XiMinus].set(PID::XiMinus, PID::Lambda, PID::Pion, mSVParams->pidCutsXiMinus, bz, mSVParams->maximalCascadeWidth);
300 mCascHyps[HypCascade::OmegaMinus].set(PID::OmegaMinus, PID::Lambda, PID::Kaon, mSVParams->pidCutsOmegaMinus, bz, mSVParams->maximalCascadeWidth);
301
302 m3bodyHyps[Hyp3body::H3L3body].set(PID::HyperTriton, PID::Proton, PID::Pion, PID::Deuteron, mSVParams->pidCutsH3L3body, bz);
303 m3bodyHyps[Hyp3body::AntiH3L3body].set(PID::HyperTriton, PID::Pion, PID::Proton, PID::Deuteron, mSVParams->pidCutsH3L3body, bz);
304 m3bodyHyps[Hyp3body::H4L3body].set(PID::Hyperhydrog4, PID::Proton, PID::Pion, PID::Triton, mSVParams->pidCutsH4L3body, bz);
305 m3bodyHyps[Hyp3body::AntiH4L3body].set(PID::Hyperhydrog4, PID::Pion, PID::Proton, PID::Triton, mSVParams->pidCutsH4L3body, bz);
306 m3bodyHyps[Hyp3body::He4L3body].set(PID::HyperHelium4, PID::Proton, PID::Pion, PID::Helium3, mSVParams->pidCutsHe4L3body, bz);
307 m3bodyHyps[Hyp3body::AntiHe4L3body].set(PID::HyperHelium4, PID::Pion, PID::Proton, PID::Helium3, mSVParams->pidCutsHe4L3body, bz);
308 m3bodyHyps[Hyp3body::He5L3body].set(PID::HyperHelium5, PID::Proton, PID::Pion, PID::Alpha, mSVParams->pidCutsHe5L3body, bz);
309 m3bodyHyps[Hyp3body::AntiHe5L3body].set(PID::HyperHelium5, PID::Pion, PID::Proton, PID::Alpha, mSVParams->pidCutsHe5L3body, bz);
310
311 for (auto& ft : mFitterV0) {
312 ft.setBz(bz);
313 }
314 for (auto& ft : mFitterCasc) {
315 ft.setBz(bz);
316 }
317 for (auto& ft : mFitter3body) {
318 ft.setBz(bz);
319 }
320
321 mPIDresponse.setBetheBlochParams(mSVParams->mBBpars);
322}
323
324//______________________________________________
326{
327 mTPCVDrift = v.refVDrift * v.corrFact;
328 mTPCVDriftCorrFact = v.corrFact;
329 mTPCVDriftRef = v.refVDrift;
330 mTPCDriftTimeOffset = v.getTimeOffset();
331 mTPCBin2Z = mTPCVDrift / mMUS2TPCBin;
332}
333//______________________________________________
335{
336 mTPCCorrMaps = maph;
337}
338
339//__________________________________________________________________
340void SVertexer::setupThreads()
341{
342 if (!mV0sTmp.empty()) {
343 return;
344 }
345 mV0sTmp.resize(mNThreads);
346 mCascadesTmp.resize(mNThreads);
347 m3bodyTmp.resize(mNThreads);
348 mV0sIdxTmp.resize(mNThreads);
349 mCascadesIdxTmp.resize(mNThreads);
350 m3bodyIdxTmp.resize(mNThreads);
351 mFitterV0.resize(mNThreads);
352 mBz = o2::base::Propagator::Instance()->getNominalBz();
353 int fitCounter = 0;
354 for (auto& fitter : mFitterV0) {
355 fitter.setOldMode(mSVParams->oldDCAFitterMode);
356 fitter.setFitterID(fitCounter++);
357 fitter.setBz(mBz);
358 fitter.setUseAbsDCA(mSVParams->useAbsDCA);
359 fitter.setPropagateToPCA(false);
360 fitter.setMaxR(mSVParams->maxRIni);
361 fitter.setMinParamChange(mSVParams->minParamChange);
362 fitter.setMinRelChi2Change(mSVParams->minRelChi2Change);
363 fitter.setMaxDZIni(mSVParams->maxDZIni);
364 fitter.setMaxDXYIni(mSVParams->maxDXYIni);
365 fitter.setMaxChi2(mSVParams->maxChi2);
366 fitter.setMatCorrType(o2::base::Propagator::MatCorrType(mSVParams->matCorr));
367 fitter.setUsePropagator(mSVParams->usePropagator);
368 fitter.setRefitWithMatCorr(mSVParams->refitWithMatCorr);
369 fitter.setMaxStep(mSVParams->maxStep);
370 fitter.setMaxSnp(mSVParams->maxSnp);
371 fitter.setMinXSeed(mSVParams->minXSeed);
372 }
373 mFitterCasc.resize(mNThreads);
374 fitCounter = 1000;
375 for (auto& fitter : mFitterCasc) {
376 fitter.setOldMode(mSVParams->oldDCAFitterMode);
377 fitter.setFitterID(fitCounter++);
378 fitter.setBz(mBz);
379 fitter.setUseAbsDCA(mSVParams->useAbsDCA);
380 fitter.setPropagateToPCA(false);
381 fitter.setMaxR(mSVParams->maxRIniCasc);
382 fitter.setMinParamChange(mSVParams->minParamChange);
383 fitter.setMinRelChi2Change(mSVParams->minRelChi2Change);
384 fitter.setMaxDZIni(mSVParams->maxDZIni);
385 fitter.setMaxDXYIni(mSVParams->maxDXYIni);
386 fitter.setMaxChi2(mSVParams->maxChi2);
387 fitter.setMatCorrType(o2::base::Propagator::MatCorrType(mSVParams->matCorr));
388 fitter.setUsePropagator(mSVParams->usePropagator);
389 fitter.setRefitWithMatCorr(mSVParams->refitWithMatCorr);
390 fitter.setMaxStep(mSVParams->maxStep);
391 fitter.setMaxSnp(mSVParams->maxSnp);
392 fitter.setMinXSeed(mSVParams->minXSeed);
393 }
394
395 mFitter3body.resize(mNThreads);
396 fitCounter = 2000;
397 for (auto& fitter : mFitter3body) {
398 fitter.setOldMode(mSVParams->oldDCAFitterMode);
399 fitter.setFitterID(fitCounter++);
400 fitter.setBz(mBz);
401 fitter.setUseAbsDCA(mSVParams->useAbsDCA);
402 fitter.setPropagateToPCA(false);
403 fitter.setMaxR(mSVParams->maxRIni3body);
404 fitter.setMinParamChange(mSVParams->minParamChange);
405 fitter.setMinRelChi2Change(mSVParams->minRelChi2Change);
406 fitter.setMaxDZIni(mSVParams->maxDZIni);
407 fitter.setMaxDXYIni(mSVParams->maxDXYIni);
408 fitter.setMaxChi2(mSVParams->maxChi2);
409 fitter.setMatCorrType(o2::base::Propagator::MatCorrType(mSVParams->matCorr));
410 fitter.setUsePropagator(mSVParams->usePropagator);
411 fitter.setRefitWithMatCorr(mSVParams->refitWithMatCorr);
412 fitter.setMaxStep(mSVParams->maxStep);
413 fitter.setMaxSnp(mSVParams->maxSnp);
414 fitter.setMinXSeed(mSVParams->minXSeed);
415 }
416}
417
418//__________________________________________________________________
419bool SVertexer::acceptTrack(const GIndex gid, const o2::track::TrackParCov& trc) const
420{
421 if (gid.isPVContributor() && mSVParams->maxPVContributors < 1) {
422 return false;
423 }
424
425 // DCA to mean vertex
426 if (mSVParams->minDCAToPV > 0.f) {
427 o2::track::TrackPar trp(trc);
428 std::array<float, 2> dca;
429 auto* prop = o2::base::Propagator::Instance();
430 if (mSVParams->usePropagator) {
431 if (trp.getX() > mSVParams->minRFor3DField && !prop->PropagateToXBxByBz(trp, mSVParams->minRFor3DField, mSVParams->maxSnp, mSVParams->maxStep, o2::base::Propagator::MatCorrType(mSVParams->matCorr))) {
432 return true; // we don't need actually to propagate to the beam-line
433 }
434 if (!prop->propagateToDCA(mMeanVertex.getXYZ(), trp, prop->getNominalBz(), mSVParams->maxStep, o2::base::Propagator::MatCorrType(mSVParams->matCorr), &dca)) {
435 return true;
436 }
437 } else {
438 if (!trp.propagateParamToDCA(mMeanVertex.getXYZ(), prop->getNominalBz(), &dca)) {
439 return true;
440 }
441 }
442 if (std::abs(dca[0]) < mSVParams->minDCAToPV) {
443 return false;
444 }
445 }
446 return true;
447}
448
449//__________________________________________________________________
450void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // accessor to various tracks
451{
452 // build track->vertices from vertices->tracks, rejecting vertex contributors if requested
453 auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks
454 auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
455 bool isTPCloaded = recoData.isTrackSourceLoaded(GIndex::TPC);
456 bool isITSloaded = recoData.isTrackSourceLoaded(GIndex::ITS);
457 bool isITSTPCloaded = recoData.isTrackSourceLoaded(GIndex::ITSTPC);
458 if (isTPCloaded && !mSVParams->mExcludeTPCtracks) {
459 mTPCTracksArray = recoData.getTPCTracks();
460 mTPCTrackClusIdx = recoData.getTPCTracksClusterRefs();
461 mTPCClusterIdxStruct = &recoData.inputsTPCclusters->clusterIndex;
462 mTPCRefitterShMap = recoData.clusterShMapTPC;
463 mTPCRefitterOccMap = mRecoCont->occupancyMapTPC;
464 mTPCRefitter = std::make_unique<o2::gpu::GPUO2InterfaceRefit>(mTPCClusterIdxStruct, mTPCCorrMaps, o2::base::Propagator::Instance()->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance());
465 }
466
467 std::unordered_map<GIndex, std::pair<int, int>> tmap;
468 std::unordered_map<GIndex, bool> rejmap;
469 // The last entry is for unassigned tracks, ignore them. A timeframe holding no collision at
470 // all has no entry, and the subtraction would then wrap around.
471 int nv = vtxRefs.size() > 0 ? vtxRefs.size() - 1 : 0;
472 for (int i = 0; i < 2; i++) {
473 mTracksPool[i].clear();
474 mVtxFirstTrack[i].clear();
475 mVtxFirstTrack[i].resize(nv, -1);
476 }
477 for (int iv = 0; iv < nv; iv++) {
478 const auto& vtref = vtxRefs[iv];
479 int it = vtref.getFirstEntry(), itLim = it + vtref.getEntries();
480 for (; it < itLim; it++) {
481 auto tvid = trackIndex[it];
482 if (!recoData.isTrackSourceLoaded(tvid.getSource())) {
483 continue;
484 }
485 if (tvid.getSource() == GIndex::TPC) {
486 if (mSVParams->mExcludeTPCtracks) {
487 continue;
488 }
489 // unconstrained TPC tracks require special treatment: there is no point in checking DCA to mean vertex since it is not precise,
490 // but we need to create a clone of TPC track constrained to this particular vertex time.
491 if (processTPCTrack(mTPCTracksArray[tvid], tvid, iv)) {
492 continue;
493 }
494 }
495
496 if (tvid.isAmbiguous()) { // was this track already processed?
497 auto tref = tmap.find(tvid);
498 if (tref != tmap.end()) {
499 mTracksPool[tref->second.second][tref->second.first].vBracket.setMax(iv); // this track was already processed with other vertex, account the latter
500 continue;
501 }
502 // was it already rejected?
503 if (rejmap.find(tvid) != rejmap.end()) {
504 continue;
505 }
506 }
507 const auto& trc = recoData.getTrackParam(tvid);
508
509 bool hasTPC = false;
510 bool heavyIonisingParticle = false;
511 bool compatibleWithProton = mSVParams->mFractiondEdxforCascBaryons > 0.999f; // if 1 or above, accept all regardless of TPC
512 auto tpcGID = recoData.getTPCContributorGID(tvid);
513 if (tpcGID.isIndexSet() && isTPCloaded) {
514 hasTPC = true;
515 auto& tpcTrack = recoData.getTPCTrack(tpcGID);
516 float dEdxTPC = tpcTrack.getdEdx().dEdxTotTPC;
517 if (dEdxTPC > mSVParams->minTPCdEdx && trc.getP() > mSVParams->minMomTPCdEdx) // accept high dEdx tracks (He3, He4)
518 {
519 heavyIonisingParticle = true;
520 }
521 auto protonId = o2::track::PID::Proton;
522 float dEdxExpected = mPIDresponse.getExpectedSignal(tpcTrack, protonId);
523 float fracDevProton = std::abs((dEdxTPC - dEdxExpected) / dEdxExpected);
524 if (fracDevProton < mSVParams->mFractiondEdxforCascBaryons) {
525 compatibleWithProton = true;
526 }
527 }
528
529 // get Nclusters in the ITS if available
530 int8_t nITSclu = -1;
531 bool shortOBITSOnlyTrack = false;
532 auto itsGID = recoData.getITSContributorGID(tvid);
533 if (itsGID.getSource() == GIndex::ITS) {
534 if (isITSloaded) {
535 auto& itsTrack = recoData.getITSTrack(itsGID);
536 nITSclu = itsTrack.getNumberOfClusters();
537 if (itsTrack.hasHitOnLayer(6) && itsTrack.hasHitOnLayer(5) && itsTrack.hasHitOnLayer(4) && itsTrack.hasHitOnLayer(3)) {
538 shortOBITSOnlyTrack = true;
539 }
540 }
541 } else if (itsGID.getSource() == GIndex::ITSAB) {
542 if (isITSTPCloaded) {
543 auto& itsABTracklet = recoData.getITSABRef(itsGID);
544 nITSclu = itsABTracklet.getNClusters();
545 }
546 }
547 if (!acceptTrack(tvid, trc) && !heavyIonisingParticle) {
548 if (tvid.isAmbiguous()) {
549 rejmap[tvid] = true;
550 }
551 continue;
552 }
553 if ((isTPCloaded && !hasTPC) && (isITSloaded && (nITSclu < mSVParams->mITSSAminNclu && (!shortOBITSOnlyTrack || mSVParams->mRejectITSonlyOBtrack)))) {
554 continue; // reject short ITS-only
555 }
556
557 int posneg = trc.getSign() < 0 ? 1 : 0;
558 float r = std::sqrt(trc.getX() * trc.getX() + trc.getY() * trc.getY());
559 mTracksPool[posneg].emplace_back(TrackCand{trc, tvid, {iv, iv}, r, hasTPC, nITSclu, compatibleWithProton});
560 if (tvid.getSource() == GIndex::TPC) { // constrained TPC track?
561 correctTPCTrack(mTracksPool[posneg].back(), mTPCTracksArray[tvid], -1, -1);
562 }
563 if (tvid.isAmbiguous()) { // track attached to >1 vertex, remember that it was already processed
564 tmap[tvid] = {mTracksPool[posneg].size() - 1, posneg};
565 }
566 }
567 }
568 // register 1st track of each charge for each vertex
569 for (int pn = 0; pn < 2; pn++) {
570 auto& vtxFirstT = mVtxFirstTrack[pn];
571 const auto& tracksPool = mTracksPool[pn];
572 for (unsigned i = 0; i < tracksPool.size(); i++) {
573 const auto& t = tracksPool[i];
574 for (int j{t.vBracket.getMin()}; j <= t.vBracket.getMax(); ++j) {
575 if (vtxFirstT[j] == -1) {
576 vtxFirstT[j] = i;
577 }
578 }
579 }
580 }
581
582 LOG(info) << "Collected " << mTracksPool[POS].size() << " positive and " << mTracksPool[NEG].size() << " negative seeds";
583}
584
585//__________________________________________________________________
586bool SVertexer::checkV0(const TrackCand& seedP, const TrackCand& seedN, int iP, int iN, int ithread)
587{
588 auto& fitterV0 = mFitterV0[ithread];
589 // Fast rough cuts on pairs before feeding to DCAFitter, tracks are not in the same Frame or at same X
590 bool isTPConly = (seedP.gid.getSource() == GIndex::TPC || seedN.gid.getSource() == GIndex::TPC);
591 if (mSVParams->mTPCTrackPhotonTune && isTPConly) {
592 // Check if Tgl is close enough
593 if (std::abs(seedP.getTgl() - seedN.getTgl()) > mSVParams->maxV0TglAbsDiff) {
594 LOG(debug) << "RejTgl";
595 return false;
596 }
597 // Check in transverse plane
598 float sna, csa;
599 o2::math_utils::CircleXYf_t trkPosCircle;
600 seedP.getCircleParams(mBz, trkPosCircle, sna, csa);
601 o2::math_utils::CircleXYf_t trkEleCircle;
602 seedN.getCircleParams(mBz, trkEleCircle, sna, csa);
603 // Does the radius of both tracks compare to their absolute circle center distance
604 float c2c = std::hypot(trkPosCircle.xC - trkEleCircle.xC,
605 trkPosCircle.yC - trkEleCircle.yC);
606 float r2r = trkPosCircle.rC + trkEleCircle.rC;
607 float dcr = c2c - r2r;
608 if (std::abs(dcr) > mSVParams->mTPCTrackD2R) {
609 LOG(debug) << "RejD2R " << c2c << " " << r2r << " " << dcr;
610 return false;
611 }
612 // Will the conversion point look somewhat reasonable
613 float r1_r = trkPosCircle.rC / r2r;
614 float r2_r = trkEleCircle.rC / r2r;
615 float dR = std::hypot(r2_r * trkPosCircle.xC + r1_r * trkEleCircle.xC, r2_r * trkPosCircle.yC + r1_r * trkEleCircle.yC);
616 if (dR > mSVParams->mTPCTrackDR) {
617 LOG(debug) << "RejDR" << dR;
618 return false;
619 }
620
621 // Setup looser cuts for the DCAFitter
622 fitterV0.setMaxDZIni(mSVParams->mTPCTrackMaxDZIni);
623 fitterV0.setMaxDXYIni(mSVParams->mTPCTrackMaxDXYIni);
624 fitterV0.setMaxChi2(mSVParams->mTPCTrackMaxChi2);
625 fitterV0.setCollinear(true);
626 }
627
628 // feed DCAFitter
629 int nCand = fitterV0.process(seedP, seedN);
630 if (mSVParams->mTPCTrackPhotonTune && isTPConly) {
631 // Reset immediately to the defaults
632 fitterV0.setMaxDZIni(mSVParams->maxDZIni);
633 fitterV0.setMaxDXYIni(mSVParams->maxDXYIni);
634 fitterV0.setMaxChi2(mSVParams->maxChi2);
635 fitterV0.setCollinear(false);
636 }
637
638 if (nCand == 0) { // discard this pair
639 LOG(debug) << "RejDCAFitter";
640 return false;
641 }
642 const auto& v0XYZ = fitterV0.getPCACandidate();
643 // validate V0 radial position
644 // check closeness to the beam-line
645 float dxv0 = v0XYZ[0] - mMeanVertex.getX(), dyv0 = v0XYZ[1] - mMeanVertex.getY(), r2v0 = dxv0 * dxv0 + dyv0 * dyv0;
646 if (r2v0 < mMinR2ToMeanVertex) {
647 LOG(debug) << "RejMinR2ToMeanVertex";
648 return false;
649 }
650 float rv0 = std::sqrt(r2v0), drv0P = rv0 - seedP.minR, drv0N = rv0 - seedN.minR;
651 if (drv0P > mSVParams->causalityRTolerance || drv0P < -mSVParams->maxV0ToProngsRDiff ||
652 drv0N > mSVParams->causalityRTolerance || drv0N < -mSVParams->maxV0ToProngsRDiff) {
653 LOG(debug) << "RejCausality " << drv0P << " " << drv0N;
654 return false;
655 }
656 const int cand = 0;
657 if (!fitterV0.isPropagateTracksToVertexDone(cand) && !fitterV0.propagateTracksToVertex(cand)) {
658 LOG(debug) << "RejProp failed";
659 return false;
660 }
661 const auto& trPProp = fitterV0.getTrack(0, cand);
662 const auto& trNProp = fitterV0.getTrack(1, cand);
663 std::array<float, 3> pP{}, pN{};
664 trPProp.getPxPyPzGlo(pP);
665 trNProp.getPxPyPzGlo(pN);
666 // estimate DCA of neutral V0 track to beamline: straight line with parametric equation
667 // x = X0 + pV0[0]*t, y = Y0 + pV0[1]*t reaches DCA to beamline (Xv, Yv) at
668 // t = -[ (x0-Xv)*pV0[0] + (y0-Yv)*pV0[1]) ] / ( pT(pV0)^2 )
669 // Similar equation for 3D distance involving pV0[2]
670 std::array<float, 3> pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]};
671 float pt2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1], prodXYv0 = dxv0 * pV0[0] + dyv0 * pV0[1], tDCAXY = prodXYv0 / pt2V0;
672 if (pt2V0 < mMinPt2V0) { // pt cut
673 LOG(debug) << "RejPt2 " << pt2V0;
674 return false;
675 }
676 if (pV0[2] * pV0[2] / pt2V0 > mMaxTgl2V0) { // tgLambda cut
677 LOG(debug) << "RejTgL " << pV0[2] * pV0[2] / pt2V0;
678 return false;
679 }
680 float p2V0 = pt2V0 + pV0[2] * pV0[2], ptV0 = std::sqrt(pt2V0);
681 // apply mass selections
682 float p2Pos = pP[0] * pP[0] + pP[1] * pP[1] + pP[2] * pP[2], p2Neg = pN[0] * pN[0] + pN[1] * pN[1] + pN[2] * pN[2];
683
684 bool goodHyp = false, photonOnly = mSVParams->mTPCTrackPhotonTune && isTPConly;
685 std::array<bool, NHypV0> hypCheckStatus{};
686 int nPID = photonOnly ? (Photon + 1) : NHypV0;
687 for (int ipid = 0; (ipid < nPID) && mSVParams->checkV0Hypothesis; ipid++) {
688 if (mV0Hyps[ipid].check(p2Pos, p2Neg, p2V0, ptV0)) {
689 goodHyp = hypCheckStatus[ipid] = true;
690 }
691 }
692 // check tight lambda mass only
693 bool goodLamForCascade = false, goodALamForCascade = false;
694 bool usesTPCOnly = (seedP.hasTPC && !seedP.hasITS()) || (seedN.hasTPC && !seedN.hasITS());
695 bool usesShortITSOnly = (!seedP.hasTPC && seedP.nITSclu < mSVParams->mITSSAminNcluCascades) || (!seedN.hasTPC && seedN.nITSclu < mSVParams->mITSSAminNcluCascades);
696 if (ptV0 > mSVParams->minPtV0FromCascade && (!mSVParams->mSkipTPCOnlyCascade || !usesTPCOnly) && !usesShortITSOnly) {
697 if (mV0Hyps[Lambda].checkTight(p2Pos, p2Neg, p2V0, ptV0) && (!mSVParams->mRequireTPCforCascBaryons || seedP.hasTPC) && seedP.compatibleProton) {
698 goodLamForCascade = true;
699 }
700 if (mV0Hyps[AntiLambda].checkTight(p2Pos, p2Neg, p2V0, ptV0) && (!mSVParams->mRequireTPCforCascBaryons || seedN.hasTPC) && seedN.compatibleProton) {
701 goodALamForCascade = true;
702 }
703 }
704
705 // apply mass selections for 3-body decay
706 bool good3bodyV0Hyp = false;
707 for (int ipid = 2; ipid < 4; ipid++) {
708 float massForLambdaHyp = mV0Hyps[ipid].calcMass(p2Pos, p2Neg, p2V0);
709 if (massForLambdaHyp - mV0Hyps[ipid].getMassV0Hyp() < mV0Hyps[ipid].getMargin(ptV0)) {
710 good3bodyV0Hyp = true;
711 break;
712 }
713 }
714
715 // we want to reconstruct the 3 body decay of hypernuclei starting from the V0 of a proton and a pion (e.g. H3L->d + (p + pi-), or He4L->He3 + (p + pi-)))
716 bool checkFor3BodyDecays = mEnable3BodyDecays &&
717 (!mSVParams->checkV0Hypothesis || good3bodyV0Hyp) &&
718 (pt2V0 > 0.5) &&
719 (!mSVParams->mSkipTPCOnly3Body || !isTPConly);
720 bool rejectAfter3BodyCheck = false; // To reject v0s which can be 3-body decay candidates but not cascade or v0
721 bool checkForCascade = mEnableCascades &&
722 (!mSVParams->mSkipTPCOnlyCascade || !usesTPCOnly) &&
723 r2v0 < mMaxR2ToMeanVertexCascV0 &&
724 (!mSVParams->checkV0Hypothesis || (goodLamForCascade || goodALamForCascade) &&
725 (!isTPConly || !hypCheckStatus[HypV0::Photon]));
726 bool rejectIfNotCascade = false;
727
728 if (!goodHyp && mSVParams->checkV0Hypothesis) {
729 LOG(debug) << "RejHypo";
730 if (!checkFor3BodyDecays && !checkForCascade) {
731 return false;
732 } else {
733 rejectAfter3BodyCheck = true;
734 }
735 }
736
737 float dcaX = dxv0 - pV0[0] * tDCAXY, dcaY = dyv0 - pV0[1] * tDCAXY, dca2 = dcaX * dcaX + dcaY * dcaY;
738 float cosPAXY = prodXYv0 / std::sqrt(r2v0 * pt2V0);
739
740 if (checkForCascade) { // use looser cuts for cascade v0 candidates
741 if (dca2 > mMaxDCAXY2ToMeanVertexV0Casc || cosPAXY < mSVParams->minCosPAXYMeanVertexCascV0) {
742 LOG(debug) << "Rej for cascade DCAXY2: " << dca2 << " << cosPAXY: " << cosPAXY;
743 if (!checkFor3BodyDecays) {
744 return false;
745 } else {
746 rejectAfter3BodyCheck = true;
747 }
748 }
749 }
750 if (checkFor3BodyDecays) { // use looser cuts for 3-body decay candidates
751 if (dca2 > mMaxDCAXY2ToMeanVertex3bodyV0 || cosPAXY < mSVParams->minCosPAXYMeanVertex3bodyV0) {
752 LOG(debug) << "Rej for 3 body decays DCAXY2: " << dca2 << " << cosPAXY: " << cosPAXY;
753 checkFor3BodyDecays = false;
754 }
755 }
756
757 if (dca2 > mMaxDCAXY2ToMeanVertex || cosPAXY < mSVParams->minCosPAXYMeanVertex) {
758 if (checkForCascade) {
759 rejectIfNotCascade = true;
760 } else if (checkFor3BodyDecays) {
761 rejectAfter3BodyCheck = true;
762 } else {
763 if (mSVParams->mTPCTrackPhotonTune && isTPConly) {
764 // Check for looser cut for tpc-only photons only
765 if (dca2 > mSVParams->mTPCTrackMaxDCAXY2ToMeanVertex) {
766 return false;
767 }
768 } else {
769 return false;
770 }
771 }
772 }
773
774 auto vlist = seedP.vBracket.getOverlap(seedN.vBracket); // indices of vertices shared by both seeds
775 bool candFound = false;
776 auto bestCosPA = checkForCascade ? mSVParams->minCosPACascV0 : mSVParams->minCosPA;
777 bestCosPA = checkFor3BodyDecays ? std::min(mSVParams->minCosPA3bodyV0, bestCosPA) : bestCosPA;
778 V0 v0new;
779 V0Index v0Idxnew;
780
781 for (int iv = vlist.getMin(); iv <= vlist.getMax(); iv++) {
782 const auto& pv = mPVertices[iv];
783 const auto v0XYZ = fitterV0.getPCACandidatePos(cand);
784 // check cos of pointing angle
785 float dx = v0XYZ[0] - pv.getX(), dy = v0XYZ[1] - pv.getY(), dz = v0XYZ[2] - pv.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2];
786 float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0);
787 if (cosPA < bestCosPA) {
788 LOG(debug) << "Rej. cosPA: " << cosPA;
789 continue;
790 }
791 if (!candFound) {
792 new (&v0new) V0(v0XYZ, pV0, fitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp);
793 new (&v0Idxnew) V0Index(-1, seedP.gid, seedN.gid);
794 v0new.setDCA(fitterV0.getChi2AtPCACandidate(cand));
795 candFound = true;
796 }
797 v0new.setCosPA(cosPA);
798 v0Idxnew.setVertexID(iv);
799 bestCosPA = cosPA;
800 }
801 if (!candFound) {
802 return false;
803 }
804 if (bestCosPA < mSVParams->minCosPACascV0) {
805 rejectAfter3BodyCheck = true;
806 }
807 if (bestCosPA < mSVParams->minCosPA && checkForCascade) {
808 rejectIfNotCascade = true;
809 }
810 int nV0Ini = mV0sIdxTmp[ithread].size();
811 // check 3 body decays
812 if (checkFor3BodyDecays) {
813 int n3bodyDecays = 0;
814 n3bodyDecays += check3bodyDecays(v0Idxnew, v0new, rv0, pV0, p2V0, iN, NEG, vlist, ithread);
815 n3bodyDecays += check3bodyDecays(v0Idxnew, v0new, rv0, pV0, p2V0, iP, POS, vlist, ithread);
816 }
817 if (rejectAfter3BodyCheck) {
818 return false;
819 }
820
821 // check cascades
822 int nCascIni = mCascadesIdxTmp[ithread].size(), nV0Used = 0; // number of times this particular v0 (with assigned PV) was used (not counting using its clones with other PV)
823 if (checkForCascade) {
824 if (goodLamForCascade || !mSVParams->checkCascadeHypothesis) {
825 nV0Used += checkCascades(v0Idxnew, v0new, rv0, pV0, p2V0, iN, NEG, vlist, ithread);
826 }
827 if (goodALamForCascade || !mSVParams->checkCascadeHypothesis) {
828 nV0Used += checkCascades(v0Idxnew, v0new, rv0, pV0, p2V0, iP, POS, vlist, ithread);
829 }
830 }
831
832 if (nV0Used) { // need to fix the index of V0 for the cascades using this v0
833 for (unsigned int ic = nCascIni; ic < mCascadesIdxTmp[ithread].size(); ic++) {
834 if (mCascadesIdxTmp[ithread][ic].getV0ID() == -1) {
835 mCascadesIdxTmp[ithread][ic].setV0ID(nV0Ini);
836 }
837 }
838 }
839
840 if (nV0Used || !rejectIfNotCascade) { // need to add this v0
841 mV0sIdxTmp[ithread].push_back(v0Idxnew);
842 if (!rejectIfNotCascade) {
843 mV0sIdxTmp[ithread].back().setStandaloneV0();
844 }
845 if (photonOnly) {
846 mV0sIdxTmp[ithread].back().setPhotonOnly();
847 mV0sIdxTmp[ithread].back().setCollinear();
848 }
849
850 if (mSVParams->createFullV0s) {
851 mV0sTmp[ithread].push_back(v0new);
852 }
853 }
854
855 if (mStrTracker) {
856 for (int iv = nV0Ini; iv < (int)mV0sIdxTmp[ithread].size(); iv++) {
857 mStrTracker->processV0(iv, v0new, v0Idxnew, ithread);
858 }
859 }
860
861 return mV0sIdxTmp[ithread].size() - nV0Ini != 0;
862}
863
864//__________________________________________________________________
865int SVertexer::checkCascades(const V0Index& v0Idx, const V0& v0, float rv0, std::array<float, 3> pV0, float p2V0, int avoidTrackID, int posneg, VBracket v0vlist, int ithread)
866{
867 // check last added V0 for belonging to cascade
868 auto& fitterCasc = mFitterCasc[ithread];
869 auto& tracks = mTracksPool[posneg];
870 int nCascIni = mCascadesIdxTmp[ithread].size(), nv0use = 0;
871
872 // check if a given PV has already been used in a cascade
873 std::unordered_map<int, int> pvMap;
874
875 // start from the 1st bachelor track compatible with earliest vertex in the v0vlist
876 int firstTr = mVtxFirstTrack[posneg][v0vlist.getMin()], nTr = tracks.size();
877 if (firstTr < 0) {
878 firstTr = nTr;
879 }
880 for (int it = firstTr; it < nTr; it++) {
881 if (it == avoidTrackID) {
882 continue; // skip the track used by V0
883 }
884 auto& bach = tracks[it];
885 if (mSVParams->mSkipTPCOnlyCascade && (bach.gid.getSource() == GIndex::TPC)) {
886 continue; // reject TPC-only bachelors
887 }
888 if (!bach.hasTPC && bach.nITSclu < mSVParams->mITSSAminNcluCascades) {
889 continue; // reject short ITS-only
890 }
891
892 if (bach.vBracket.getMin() > v0vlist.getMax()) {
893 LOG(debug) << "Skipping";
894 break; // all other bachelor candidates will be also not compatible with this PV
895 }
896 auto cascVlist = v0vlist.getOverlap(bach.vBracket); // indices of vertices shared by V0 and bachelor
897 if (mSVParams->selectBestV0) {
898 // select only the best V0 candidate among the compatible ones
899 if (v0Idx.getVertexID() < cascVlist.getMin() || v0Idx.getVertexID() > cascVlist.getMax()) {
900 continue;
901 }
902 cascVlist.setMin(v0Idx.getVertexID());
903 cascVlist.setMax(v0Idx.getVertexID());
904 }
905
906 int nCandC = fitterCasc.process(v0, bach);
907 if (nCandC == 0) { // discard this pair
908 continue;
909 }
910 const int candC = 0;
911 const auto& cascXYZ = fitterCasc.getPCACandidatePos(candC);
912
913 // make sure the cascade radius is smaller than that of the mean vertex
914 float dxc = cascXYZ[0] - mMeanVertex.getX(), dyc = cascXYZ[1] - mMeanVertex.getY(), r2casc = dxc * dxc + dyc * dyc;
915 if (rv0 * rv0 - r2casc < mMinR2DiffV0Casc || r2casc < mMinR2ToMeanVertex) {
916 continue;
917 }
918 // do we want to apply mass cut ?
919 //
920 if (!fitterCasc.isPropagateTracksToVertexDone(candC) && !fitterCasc.propagateTracksToVertex(candC)) {
921 continue;
922 }
923
924 auto& trNeut = fitterCasc.getTrack(0, candC);
925 auto& trBach = fitterCasc.getTrack(1, candC);
926 trNeut.setPID(o2::track::PID::Lambda);
927 trBach.setPID(o2::track::PID::Pion);
928 std::array<float, 3> pNeut, pBach;
929 trNeut.getPxPyPzGlo(pNeut);
930 trBach.getPxPyPzGlo(pBach);
931 std::array<float, 3> pCasc = {pNeut[0] + pBach[0], pNeut[1] + pBach[1], pNeut[2] + pBach[2]};
932
933 float pt2Casc = pCasc[0] * pCasc[0] + pCasc[1] * pCasc[1], p2Casc = pt2Casc + pCasc[2] * pCasc[2];
934 if (pt2Casc < mMinPt2Casc) { // pt cut
935 LOG(debug) << "Casc pt too low";
936 continue;
937 }
938 if (pCasc[2] * pCasc[2] / pt2Casc > mMaxTgl2Casc) { // tgLambda cut
939 LOG(debug) << "Casc tgLambda too high";
940 continue;
941 }
942
943 // compute primary vertex and cosPA of the cascade
944 auto bestCosPA = mSVParams->minCosPACasc;
945 auto cascVtxID = -1;
946
947 for (int iv = cascVlist.getMin(); iv <= cascVlist.getMax(); iv++) {
948 const auto& pv = mPVertices[iv];
949 // check cos of pointing angle
950 float dx = cascXYZ[0] - pv.getX(), dy = cascXYZ[1] - pv.getY(), dz = cascXYZ[2] - pv.getZ(), prodXYZcasc = dx * pCasc[0] + dy * pCasc[1] + dz * pCasc[2];
951 float cosPA = prodXYZcasc / std::sqrt((dx * dx + dy * dy + dz * dz) * p2Casc);
952 if (cosPA < bestCosPA) {
953 LOG(debug) << "Rej. cosPA: " << cosPA;
954 continue;
955 }
956 cascVtxID = iv;
957 bestCosPA = cosPA;
958 }
959 if (cascVtxID == -1) {
960 LOG(debug) << "Casc not compatible with any vertex";
961 continue;
962 }
963
964 const auto& cascPv = mPVertices[cascVtxID];
965 float dxCasc = cascXYZ[0] - cascPv.getX(), dyCasc = cascXYZ[1] - cascPv.getY(), dzCasc = cascXYZ[2] - cascPv.getZ();
966 auto prodPPos = pV0[0] * dxCasc + pV0[1] * dyCasc + pV0[2] * dzCasc;
967 if (prodPPos < 0.) { // causality cut
968 LOG(debug) << "Casc not causally compatible";
969 continue;
970 }
971
972 float p2Bach = pBach[0] * pBach[0] + pBach[1] * pBach[1] + pBach[2] * pBach[2];
973 float ptCasc = std::sqrt(pt2Casc);
974 bool goodHyp = false;
975 for (int ipid = 0; ipid < NHypCascade; ipid++) {
976 if (mCascHyps[ipid].check(p2V0, p2Bach, p2Casc, ptCasc)) {
977 goodHyp = true;
978 break;
979 }
980 }
981 if (!goodHyp) {
982 LOG(debug) << "Casc not compatible with any hypothesis";
983 continue;
984 }
985 // note: at the moment the v0 was not added yet. If some cascade will use v0 (with its PV), the v0 will be added after checkCascades
986 // but not necessarily at the and of current v0s vector, since meanwhile checkCascades may add v0 clones (with PV redefined).
987 Cascade casc(cascXYZ, pCasc, fitterCasc.calcPCACovMatrixFlat(candC), trNeut, trBach);
988 o2::track::TrackParCov trc = casc;
990 if (!trc.propagateToDCA(cascPv, fitterCasc.getBz(), &dca, 5.) ||
991 std::abs(dca.getY()) > mSVParams->maxDCAXYCasc || std::abs(dca.getZ()) > mSVParams->maxDCAZCasc) {
992 LOG(debug) << "Casc not compatible with PV";
993 LOG(debug) << "DCA: " << dca.getY() << " " << dca.getZ();
994 continue;
995 }
996 CascadeIndex cascIdx(cascVtxID, -1, bach.gid); // the v0Idx was not yet added, this will be done after the checkCascades
997
998 LOGP(debug, "cascade successfully validated");
999
1000 // clone the V0, set new cosPA and VerteXID, add it to the list of V0s
1001 if (cascVtxID != v0Idx.getVertexID()) {
1002 auto pvIdx = pvMap.find(cascVtxID);
1003 if (pvIdx != pvMap.end()) {
1004 cascIdx.setV0ID(pvIdx->second); // V0 already exists, add reference to the cascade
1005 } else { // add V0 clone for this cascade (may be used also by other cascades)
1006 const auto& pv = mPVertices[cascVtxID];
1007 cascIdx.setV0ID(mV0sIdxTmp[ithread].size()); // set the new V0 index in the cascade
1008 pvMap[cascVtxID] = mV0sTmp[ithread].size(); // add the new V0 index to the map
1009 mV0sIdxTmp[ithread].emplace_back(cascVtxID, v0Idx.getProngs());
1010 if (mSVParams->createFullV0s) {
1011 mV0sTmp[ithread].push_back(v0);
1012 float dx = v0.getX() - pv.getX(), dy = v0.getY() - pv.getY(), dz = v0.getZ() - pv.getZ(), prodXYZ = dx * pV0[0] + dy * pV0[1] + dz * pV0[2];
1013 mV0sTmp[ithread].back().setCosPA(prodXYZ / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0));
1014 }
1015 }
1016 } else {
1017 nv0use++; // original v0 was used
1018 }
1019 mCascadesIdxTmp[ithread].push_back(cascIdx);
1020 if (mSVParams->createFullCascades) {
1021 casc.setCosPA(bestCosPA);
1022 casc.setDCA(fitterCasc.getChi2AtPCACandidate(candC));
1023 mCascadesTmp[ithread].push_back(casc);
1024 }
1025 if (mStrTracker) {
1026 mStrTracker->processCascade(mCascadesIdxTmp[ithread].size() - 1, casc, cascIdx, v0, ithread);
1027 }
1028 }
1029
1030 return nv0use;
1031}
1032
1033//__________________________________________________________________
1034int SVertexer::check3bodyDecays(const V0Index& v0Idx, const V0& v0, float rv0, std::array<float, 3> pV0, float p2V0, int avoidTrackID, int posneg, VBracket v0vlist, int ithread)
1035{
1036 // check last added V0 for belonging to cascade
1037 auto& fitter3body = mFitter3body[ithread];
1038 auto& tracks = mTracksPool[posneg];
1039 int n3BodyIni = m3bodyIdxTmp[ithread].size();
1040
1041 // start from the 1st bachelor track compatible with earliest vertex in the v0vlist
1042 int firstTr = mVtxFirstTrack[posneg][v0vlist.getMin()], nTr = tracks.size();
1043 if (firstTr < 0) {
1044 firstTr = nTr;
1045 }
1046
1047 // If the V0 is a pair of proton and pion, we should pair it with all positive particles, and the positive particle in the V0 is a proton.
1048 // Otherwise, we should pair it with all negative particles, and the negative particle in the V0 is a antiproton.
1049
1050 // start from the 1st track compatible with V0's primary vertex
1051 for (int it = firstTr; it < nTr; it++) {
1052 if (it == avoidTrackID) {
1053 continue; // skip the track used by V0
1054 }
1055 auto& bach = tracks[it];
1056 if (mSVParams->mSkipTPCOnly3Body && (bach.gid.getSource() == GIndex::TPC)) {
1057 continue; // reject TPC-only bachelors
1058 }
1059 if (bach.vBracket > v0vlist.getMax()) {
1060 LOG(debug) << "Skipping";
1061 break; // all other bachelor candidates will be also not compatible with this PV
1062 }
1063 auto decay3bodyVlist = v0vlist.getOverlap(bach.vBracket); // indices of vertices shared by V0 and bachelor
1064 if (mSVParams->selectBestV0) {
1065 // select only the best V0 candidate among the compatible ones
1066 if (v0Idx.getVertexID() < decay3bodyVlist.getMin() || v0Idx.getVertexID() > decay3bodyVlist.getMax()) {
1067 continue;
1068 }
1069 decay3bodyVlist.setMin(v0Idx.getVertexID());
1070 decay3bodyVlist.setMax(v0Idx.getVertexID());
1071 }
1072
1073 if (bach.getPt() < 0.6) {
1074 continue;
1075 }
1076
1077 int n3bodyVtx = fitter3body.process(v0.getProng(0), v0.getProng(1), bach);
1078 if (n3bodyVtx == 0) { // discard this pair
1079 continue;
1080 }
1081 int cand3B = 0;
1082 const auto& vertexXYZ = fitter3body.getPCACandidatePos(cand3B);
1083
1084 // make sure the 3 body vertex radius is close to that of the mean vertex
1085 float dxc = vertexXYZ[0] - mMeanVertex.getX(), dyc = vertexXYZ[1] - mMeanVertex.getY(), dzc = vertexXYZ[2] - mMeanVertex.getZ(), r2vertex = dxc * dxc + dyc * dyc;
1086 if (std::abs(rv0 - std::sqrt(r2vertex)) > mSVParams->maxRDiffV03body || r2vertex < mMinR2ToMeanVertex) {
1087 continue;
1088 }
1089 float drvtxBach = std::sqrt(r2vertex) - bach.minR;
1090 if (drvtxBach > mSVParams->causalityRTolerance || drvtxBach < -mSVParams->maxV0ToProngsRDiff) {
1091 LOG(debug) << "RejCausality " << drvtxBach;
1092 }
1093 //
1094 if (!fitter3body.isPropagateTracksToVertexDone() && !fitter3body.propagateTracksToVertex()) {
1095 continue;
1096 }
1097
1098 auto& tr0 = fitter3body.getTrack(0, cand3B);
1099 auto& tr1 = fitter3body.getTrack(1, cand3B);
1100 auto& tr2 = fitter3body.getTrack(2, cand3B);
1101 std::array<float, 3> p0, p1, p2;
1102 tr0.getPxPyPzGlo(p0);
1103 tr1.getPxPyPzGlo(p1);
1104 tr2.getPxPyPzGlo(p2);
1105
1106 bool goodHyp = false;
1107 o2::track::PID pidHyp = o2::track::PID::Electron; // Update if goodHyp is true
1108 auto decay3bodyVtxID = -1;
1109 auto vtxCosPA = -1;
1110
1111 std::array<float, 3> pbach = {0, 0, 0}, p3B = {0, 0, 0}; // Update during the check of invariant mass
1112 for (int ipid = 0; ipid < NHyp3body; ipid++) {
1113 // check mass based on hypothesis of charge of bachelor (pos and neg expected to be proton/pion)
1114 float bachChargeFactor = m3bodyHyps[ipid].getChargeBachProng() / tr2.getAbsCharge();
1115 pbach = {bachChargeFactor * p2[0], bachChargeFactor * p2[1], bachChargeFactor * p2[2]};
1116 p3B = {p0[0] + p1[0] + pbach[0], p0[1] + p1[1] + pbach[1], p0[2] + p1[2] + pbach[2]};
1117 float sqP0 = p0[0] * p0[0] + p0[1] * p0[1] + p0[2] * p0[2], sqP1 = p1[0] * p1[0] + p1[1] * p1[1] + p1[2] * p1[2], sqPBach = pbach[0] * pbach[0] + pbach[1] * pbach[1] + pbach[2] * pbach[2];
1118 float pt2Candidate = p3B[0] * p3B[0] + p3B[1] * p3B[1], p2Candidate = pt2Candidate + p3B[2] * p3B[2];
1119 float ptCandidate = std::sqrt(pt2Candidate);
1120 if (m3bodyHyps[ipid].check(sqP0, sqP1, sqPBach, p2Candidate, ptCandidate)) {
1121 if (pt2Candidate < mMinPt23Body) { // pt cut
1122 continue;
1123 }
1124 if (p3B[2] * p3B[2] > pt2Candidate * mMaxTgl23Body) { // tgLambda cut
1125 continue;
1126 }
1127
1128 // compute primary vertex and cosPA of the 3-body decay
1129 auto bestCosPA = mSVParams->minCosPA3body;
1130 for (int iv = decay3bodyVlist.getMin(); iv <= decay3bodyVlist.getMax(); iv++) {
1131 const auto& pv = mPVertices[iv];
1132 // check cos of pointing angle
1133 float dx = vertexXYZ[0] - pv.getX(), dy = vertexXYZ[1] - pv.getY(), dz = vertexXYZ[2] - pv.getZ(), prodXYZ3body = dx * p3B[0] + dy * p3B[1] + dz * p3B[2];
1134 float cosPA = prodXYZ3body / std::sqrt((dx * dx + dy * dy + dz * dz) * p2Candidate);
1135 if (cosPA < bestCosPA) {
1136 LOG(debug) << "Rej. cosPA: " << cosPA;
1137 continue;
1138 }
1139 decay3bodyVtxID = iv;
1140 bestCosPA = cosPA;
1141 }
1142 if (decay3bodyVtxID == -1) {
1143 LOG(debug) << "3-body decay not compatible with any vertex";
1144 continue;
1145 }
1146
1147 goodHyp = true;
1148 pidHyp = m3bodyHyps[ipid].getPIDHyp();
1149 vtxCosPA = bestCosPA;
1150 break;
1151 }
1152 }
1153 if (!goodHyp) {
1154 continue;
1155 }
1156
1157 const auto& decay3bodyPv = mPVertices[decay3bodyVtxID];
1158 Decay3Body candidate3B(vertexXYZ, p3B, fitter3body.calcPCACovMatrixFlat(cand3B), tr0, tr1, tr2, pidHyp);
1159 o2::track::TrackParCov trc = candidate3B;
1161 if (!trc.propagateToDCA(decay3bodyPv, fitter3body.getBz(), &dca, 5.) ||
1162 std::abs(dca.getY()) > mSVParams->maxDCAXY3Body || std::abs(dca.getZ()) > mSVParams->maxDCAZ3Body) {
1163 continue;
1164 }
1165 if (mSVParams->createFull3Bodies) {
1166 candidate3B.setCosPA(vtxCosPA);
1167 candidate3B.setDCA(fitter3body.getChi2AtPCACandidate());
1168 m3bodyTmp[ithread].push_back(candidate3B);
1169 }
1170 m3bodyIdxTmp[ithread].emplace_back(decay3bodyVtxID, v0Idx.getProngID(0), v0Idx.getProngID(1), bach.gid);
1171
1172 Decay3BodyIndex decay3bodyIdx(decay3bodyVtxID, v0Idx.getProngID(0), v0Idx.getProngID(1), bach.gid);
1173 if (mStrTracker) {
1174 mStrTracker->process3Body(m3bodyIdxTmp[ithread].size() - 1, candidate3B, decay3bodyIdx, ithread);
1175 }
1176 }
1177 return m3bodyIdxTmp[ithread].size() - n3BodyIni;
1178}
1179
1180//__________________________________________________________________
1181template <class TVI, class TCI, class T3I, class TR>
1182void SVertexer::extractPVReferences(const TVI& v0s, TR& vtx2V0Refs, const TCI& cascades, TR& vtx2CascRefs, const T3I& vtx3, TR& vtx2body3Refs)
1183{
1184 // V0s, cascades and 3bodies are already sorted in PV ID
1185 vtx2V0Refs.clear();
1186 vtx2V0Refs.resize(mPVertices.size());
1187 vtx2CascRefs.clear();
1188 vtx2CascRefs.resize(mPVertices.size());
1189 vtx2body3Refs.clear();
1190 vtx2body3Refs.resize(mPVertices.size());
1191 int nv0 = v0s.size(), nCasc = cascades.size(), n3body = vtx3.size();
1192
1193 // relate V0s to primary vertices
1194 int pvID = -1, nForPV = 0;
1195 for (int iv = 0; iv < nv0; iv++) {
1196 if (pvID < v0s[iv].getVertexID()) {
1197 if (pvID > -1) {
1198 vtx2V0Refs[pvID].setEntries(nForPV);
1199 }
1200 pvID = v0s[iv].getVertexID();
1201 vtx2V0Refs[pvID].setFirstEntry(iv);
1202 nForPV = 0;
1203 }
1204 nForPV++;
1205 }
1206 if (pvID != -1) { // finalize
1207 vtx2V0Refs[pvID].setEntries(nForPV);
1208 // fill empty slots
1209 int ent = nv0;
1210 for (int ip = vtx2V0Refs.size(); ip--;) {
1211 if (vtx2V0Refs[ip].getEntries()) {
1212 ent = vtx2V0Refs[ip].getFirstEntry();
1213 } else {
1214 vtx2V0Refs[ip].setFirstEntry(ent);
1215 }
1216 }
1217 }
1218
1219 // relate Cascades to primary vertices
1220 pvID = -1;
1221 nForPV = 0;
1222 for (int iv = 0; iv < nCasc; iv++) {
1223 if (pvID < cascades[iv].getVertexID()) {
1224 if (pvID > -1) {
1225 vtx2CascRefs[pvID].setEntries(nForPV);
1226 }
1227 pvID = cascades[iv].getVertexID();
1228 vtx2CascRefs[pvID].setFirstEntry(iv);
1229 nForPV = 0;
1230 }
1231 nForPV++;
1232 }
1233 if (pvID != -1) { // finalize
1234 vtx2CascRefs[pvID].setEntries(nForPV);
1235 // fill empty slots
1236 int ent = nCasc;
1237 for (int ip = vtx2CascRefs.size(); ip--;) {
1238 if (vtx2CascRefs[ip].getEntries()) {
1239 ent = vtx2CascRefs[ip].getFirstEntry();
1240 } else {
1241 vtx2CascRefs[ip].setFirstEntry(ent);
1242 }
1243 }
1244 }
1245
1246 // relate 3 body decays to primary vertices
1247 pvID = -1;
1248 nForPV = 0;
1249 for (int iv = 0; iv < n3body; iv++) {
1250 const auto& vertex3body = vtx3[iv];
1251 if (pvID < vertex3body.getVertexID()) {
1252 if (pvID > -1) {
1253 vtx2body3Refs[pvID].setEntries(nForPV);
1254 }
1255 pvID = vertex3body.getVertexID();
1256 vtx2body3Refs[pvID].setFirstEntry(iv);
1257 nForPV = 0;
1258 }
1259 nForPV++;
1260 }
1261 if (pvID != -1) { // finalize
1262 vtx2body3Refs[pvID].setEntries(nForPV);
1263 // fill empty slots
1264 int ent = n3body;
1265 for (int ip = vtx2body3Refs.size(); ip--;) {
1266 if (vtx2body3Refs[ip].getEntries()) {
1267 ent = vtx2body3Refs[ip].getFirstEntry();
1268 } else {
1269 vtx2body3Refs[ip].setFirstEntry(ent);
1270 }
1271 }
1272 }
1273}
1274
1275//__________________________________________________________________
1277{
1278#ifdef WITH_OPENMP
1279 mNThreads = n > 0 ? n : 1;
1280#else
1281 mNThreads = 1;
1282#endif
1283}
1284
1285//______________________________________________
1286bool SVertexer::processTPCTrack(const o2::tpc::TrackTPC& trTPC, GIndex gid, int vtxid)
1287{
1288 if (mSVParams->mTPCTrackMaxX > 0. && trTPC.getX() > mSVParams->mTPCTrackMaxX) {
1289 return true;
1290 }
1291 // if TPC trackis unconstrained, try to create in the tracks pool a clone constrained to vtxid vertex time.
1292 if (trTPC.hasBothSidesClusters()) { // this is effectively constrained track
1293 return false; // let it be processed as such
1294 }
1295 const auto& vtx = mPVertices[vtxid];
1296 auto twe = vtx.getTimeStamp();
1297 int posneg = trTPC.getSign() < 0 ? 1 : 0;
1298
1299 bool compatibleWithProton = false;
1300 if (!(mSVParams->mSkipTPCOnlyCascade)) {
1301 // Cascade retrieve dEdx proton frac
1302 const auto protonId = o2::track::PID::Proton;
1303 float dEdxTPC = trTPC.getdEdx().dEdxTotTPC;
1304 float dEdxExpected = mPIDresponse.getExpectedSignal(trTPC, protonId);
1305 float fracDevProton = std::abs((dEdxTPC - dEdxExpected) / dEdxExpected);
1306 if (fracDevProton < mSVParams->mFractiondEdxforCascBaryons) {
1307 compatibleWithProton = true;
1308 }
1309 }
1310
1311 auto& trLoc = mTracksPool[posneg].emplace_back(TrackCand{trTPC, gid, {vtxid, vtxid}, 0., true, -1, compatibleWithProton});
1312 auto err = correctTPCTrack(trLoc, trTPC, twe.getTimeStamp(), twe.getTimeStampError());
1313 if (err < 0) {
1314 mTracksPool[posneg].pop_back(); // discard
1315 return true;
1316 }
1317
1318 if (mSVParams->mTPCTrackPhotonTune) {
1319 // require minimum of tpc clusters
1320 bool dCls = trTPC.getNClusters() < mSVParams->mTPCTrackMinNClusters;
1321 // check track z cuts
1322 bool dDPV = std::abs(trLoc.getX() * trLoc.getTgl() - trLoc.getZ() + vtx.getZ()) > mSVParams->mTPCTrack2Beam;
1323 // check track transveres cuts
1324 float sna{0}, csa{0};
1326 trLoc.getCircleParams(mBz, trkCircle, sna, csa);
1327 float cR = std::hypot(trkCircle.xC, trkCircle.yC);
1328 float drd2 = std::sqrt(cR * cR - trkCircle.rC * trkCircle.rC);
1329 bool dRD2 = drd2 > mSVParams->mTPCTrackXY2Radius;
1330
1331 if (dCls || dDPV || dRD2) {
1332 mTracksPool[posneg].pop_back();
1333 return true;
1334 }
1335 }
1336
1337 return true;
1338}
1339
1340//______________________________________________
1341float SVertexer::correctTPCTrack(SVertexer::TrackCand& trc, const o2::tpc::TrackTPC& tTPC, float tmus, float tmusErr) const
1342{
1343 // Correct the track copy trc of the TPC track for the assumed interaction time
1344 // return extra uncertainty in Z due to the interaction time uncertainty
1345 // TODO: at the moment, apply simple shift, but with Z-dependent calibration we may
1346 // need to do corrections on TPC cluster level and refit
1347 // This is almosto clone of the MatchTPCITS::correctTPCTrack
1348
1349 float tTB, tTBErr;
1350 if (tmusErr < 0) { // use track data
1351 tTB = tTPC.getTime0();
1352 tTBErr = 0.5 * (tTPC.getDeltaTBwd() + tTPC.getDeltaTFwd());
1353 } else {
1354 tTB = tmus * mMUS2TPCBin;
1355 tTBErr = tmusErr * mMUS2TPCBin;
1356 }
1357 float dDrift = (tTB - tTPC.getTime0()) * mTPCBin2Z;
1358 float driftErr = tTBErr * mTPCBin2Z;
1359 if (driftErr < 0.) { // early return will be discarded anyway
1360 return driftErr;
1361 }
1362 // eventually should be refitted, at the moment we simply shift...
1363 trc.setZ(tTPC.getZ() + (tTPC.hasASideClustersOnly() ? dDrift : -dDrift));
1364 trc.setCov(trc.getSigmaZ2() + driftErr * driftErr, o2::track::kSigZ2);
1365 uint8_t sector, row;
1366 auto cl = &tTPC.getCluster(mTPCTrackClusIdx, tTPC.getNClusters() - 1, *mTPCClusterIdxStruct, sector, row);
1367 float x = 0, y = 0, z = 0;
1368 mTPCCorrMaps->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, tTB);
1371 }
1372 trc.minR = std::sqrt(x * x + y * y);
1373 LOGP(debug, "set MinR = {} for row {}, x:{}, y:{}, z:{}", trc.minR, row, x, y, z);
1374 return driftErr;
1375}
1376
1377//______________________________________________
1378std::array<size_t, 3> SVertexer::getNFitterCalls() const
1379{
1380 std::array<size_t, 3> calls{};
1381 for (int i = 0; i < mNThreads; i++) {
1382 calls[0] += mFitterV0[i].getCallID();
1383 calls[1] += mFitterCasc[i].getCallID();
1384 calls[2] += mFitter3body[i].getCallID();
1385 }
1386 return calls;
1387}
std::ostringstream debug
int32_t i
Some ALICE geometry constants of common interest.
Global index for barrel track: provides provenance (detectors combination), index in respective array...
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
uint16_t pos
Definition RawData.h:3
uint32_t j
Definition RawData.h:0
Secondary vertex finder.
class to create TPC fast transformation
POD correction map.
Reference on ITS/MFT clusters set.
calibration data from laser track calibration
Helper class to obtain TPC clusters / digits / labels from DPL.
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:180
TO BE DONE: extend to generic N body vertex.
Definition Decay3Body.h:26
const std::array< GIndex, N > & getProngs() const
decltype(auto) make(const Output &spec, Args... args)
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
void processV0(int iv0, const V0 &v0, const V0Index &v0Idx, int iThread=0)
std::vector< StrangeTrack > & getStrangeTrackVec(int iThread=0)
void processCascade(int icasc, const Cascade &casc, const CascadeIndex &cascIdx, const V0 &cascV0, int iThread=0)
std::vector< ClusAttachments > & getClusAttachments(int iThread=0)
void process3Body(int i3body, const Decay3Body &dec3body, const Decay3BodyIndex &dec3bodyIdx, int iThread=0)
bool loadData(const o2::globaltracking::RecoContainer &recoData)
std::vector< o2::MCCompLabel > & getStrangeTrackLabels(int iThread=0)
o2::dataformats::V0 V0
Definition SVertexer.h:60
std::array< size_t, 3 > getNFitterCalls() const
void setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD *maph)
void process(const o2::globaltracking::RecoContainer &recoTracks, o2::framework::ProcessingContext &pc)
Definition SVertexer.cxx:42
void setTPCVDrift(const o2::tpc::VDriftCorrFact &v)
static constexpr int POS
Definition SVertexer.h:98
static constexpr int NEG
Definition SVertexer.h:98
o2::dataformats::V0Index V0Index
Definition SVertexer.h:61
void produceOutput(o2::framework::ProcessingContext &pc)
Definition SVertexer.cxx:87
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLfloat v0
Definition glcorearb.h:811
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
uint8_t itsSharedClusterMap uint8_t
constexpr float XTPCInnerRef
reference radius at which TPC provides the tracks
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
void check(const std::vector< std::string > &arguments, const std::vector< ConfigParamSpec > &workflowOptions, const std::vector< DeviceSpec > &deviceSpecs, CheckMatrix &matrix)
std::array< int, 24 > p0
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
TrackParCovF TrackParCov
Definition Track.h:33
struct o2::upgrades_utils::@469 tracks
structure to keep trigger-related info
GTrackID getITSContributorGID(GTrackID source) const
const o2::tpc::TrackTPC & getTPCTrack(GTrackID id) const
const o2::itsmft::TrkClusRef & getITSABRef(GTrackID gid) const
GTrackID getTPCContributorGID(GTrackID source) const
const o2::track::TrackParCov & getTrackParam(GTrackID gidx) const
gsl::span< const unsigned char > clusterShMapTPC
externally set TPC clusters sharing map
const o2::its::TrackITS & getITSTrack(GTrackID gid) const
std::unique_ptr< o2::tpc::internal::getWorkflowTPCInput_ret > inputsTPCclusters
gsl::span< const unsigned int > occupancyMapTPC
externally set TPC clusters occupancy map
int maxPVContributors
max number PV contributors to allow in V0
float pidCutsHhydrog4[SVertexHypothesis::NPIDParams]
float minCosPA
min cos of PA to PV for prompt V0 candidates
float pidCutsHe5L3body[SVertex3Hypothesis::NPIDParams]
float maxDCAXYToMeanVertex
max DCA of V0 from beam line (mean vertex) for prompt V0 candidates
float pidCutsPhoton[SVertexHypothesis::NPIDParams]
bool useAbsDCA
use abs dca minimization
bool usePropagator
use external propagator
float maxDCAXYToMeanVertex3bodyV0
max DCA of V0 from beam line (mean vertex) for 3body V0 candidates
float maxRIni
don't consider as a seed (circles intersection) if its R exceeds this
bool createFullCascades
fill cascades prongs/kinematics
float maxDCAXYToMeanVertexV0Casc
max DCA of V0 from beam line (mean vertex) for cascade V0 candidates
float minParamChange
stop when tracks X-params being minimized change by less that this value
float causalityRTolerance
V0 radius cannot exceed its contributors minR by more than this value.
float maxSnp
max snp when external propagator is used
float minXSeed
minimal X of seed in prong frame (within the radial resolution track should not go to negative X)
int matCorr
material correction to use
float maxV0TglAbsDiff
max absolute difference in Tgl for V0 for photons only
float maxDZIni
don't consider as a seed (circles intersection) if Z distance exceeds this
float maxStep
max step size when external propagator is used
float pidCutsK0[SVertexHypothesis::NPIDParams]
float pidCutsHe4L3body[SVertex3Hypothesis::NPIDParams]
float minPtV0FromCascade
v0 minimum pT for v0 to be used in cascading (lowest pT Run 2 lambda: 0.4)
float mTPCTrackMaxDCAXY2ToMeanVertex
max DCA^2 of V0 from beam line (mean vertex) for prompt V0 candidates, for photon TPC-only track only
float pidCutsLambda[SVertexHypothesis::NPIDParams]
bool createFull3Bodies
fill 3-body decays prongs/kinematics
float minRelChi2Change
stop when chi2 changes by less than this value
float maxChi2
max dca from prongs to vertex
bool selectBestV0
match only the best v0 for each cascade candidate
float minRFor3DField
above this radius use 3D field
bool refitWithMatCorr
refit V0 applying material corrections
float pidCutsH3L3body[SVertex3Hypothesis::NPIDParams]
float minRDiffV0Casc
cascade should be at least this radial distance below V0
float pidCutsOmegaMinus[SVertexHypothesis::NPIDParams]
float minRToMeanVertex
min radial distance of V0 from beam line (mean vertex)
float pidCutsXiMinus[SVertexHypothesis::NPIDParams]
float maxRDiffV03body
Maximum difference between V0 and 3body radii.
float mTPCTrackMaxDXYIni
don't consider as a seed (circles intersection) if XY distance exceeds this, for photon TPC-only trac...
float minDCAToPV
min DCA to PV of single track to accept
float mTPCTrackMaxDZIni
don't consider as a seed (circles intersection) if Z distance exceeds this, for photon TPC-only track...
float pidCutsHTriton[SVertexHypothesis::NPIDParams]
float maxDXYIni
don't consider as a seed (circles intersection) if XY distance exceeds this
float mTPCTrackMaxChi2
max DCA from prongs to vertex for photon TPC-only track only
float pidCutsH4L3body[SVertex3Hypothesis::NPIDParams]
bool oldDCAFitterMode
pre(old) or post(new) PR15610+15784 behaviour of DCAFitter
bool createFullV0s
fill V0s prongs/kinematics
float maxTglV0
maximum tgLambda of V0
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< int > row