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);
299 mCascHyps[HypCascade::XiMinus].set(PID::XiMinus, PID::Lambda, PID::Pion, mSVParams->pidCutsXiMinus, bz, mSVParams->maximalCascadeWidth);
301
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.setFitterID(fitCounter++);
356 fitter.setBz(mBz);
357 fitter.setUseAbsDCA(mSVParams->useAbsDCA);
358 fitter.setPropagateToPCA(false);
359 fitter.setMaxR(mSVParams->maxRIni);
360 fitter.setMinParamChange(mSVParams->minParamChange);
361 fitter.setMinRelChi2Change(mSVParams->minRelChi2Change);
362 fitter.setMaxDZIni(mSVParams->maxDZIni);
363 fitter.setMaxDXYIni(mSVParams->maxDXYIni);
364 fitter.setMaxChi2(mSVParams->maxChi2);
365 fitter.setMatCorrType(o2::base::Propagator::MatCorrType(mSVParams->matCorr));
366 fitter.setUsePropagator(mSVParams->usePropagator);
367 fitter.setRefitWithMatCorr(mSVParams->refitWithMatCorr);
368 fitter.setMaxStep(mSVParams->maxStep);
369 fitter.setMaxSnp(mSVParams->maxSnp);
370 fitter.setMinXSeed(mSVParams->minXSeed);
371 }
372 mFitterCasc.resize(mNThreads);
373 fitCounter = 1000;
374 for (auto& fitter : mFitterCasc) {
375 fitter.setFitterID(fitCounter++);
376 fitter.setBz(mBz);
377 fitter.setUseAbsDCA(mSVParams->useAbsDCA);
378 fitter.setPropagateToPCA(false);
379 fitter.setMaxR(mSVParams->maxRIniCasc);
380 fitter.setMinParamChange(mSVParams->minParamChange);
381 fitter.setMinRelChi2Change(mSVParams->minRelChi2Change);
382 fitter.setMaxDZIni(mSVParams->maxDZIni);
383 fitter.setMaxDXYIni(mSVParams->maxDXYIni);
384 fitter.setMaxChi2(mSVParams->maxChi2);
385 fitter.setMatCorrType(o2::base::Propagator::MatCorrType(mSVParams->matCorr));
386 fitter.setUsePropagator(mSVParams->usePropagator);
387 fitter.setRefitWithMatCorr(mSVParams->refitWithMatCorr);
388 fitter.setMaxStep(mSVParams->maxStep);
389 fitter.setMaxSnp(mSVParams->maxSnp);
390 fitter.setMinXSeed(mSVParams->minXSeed);
391 }
392
393 mFitter3body.resize(mNThreads);
394 fitCounter = 2000;
395 for (auto& fitter : mFitter3body) {
396 fitter.setFitterID(fitCounter++);
397 fitter.setBz(mBz);
398 fitter.setUseAbsDCA(mSVParams->useAbsDCA);
399 fitter.setPropagateToPCA(false);
400 fitter.setMaxR(mSVParams->maxRIni3body);
401 fitter.setMinParamChange(mSVParams->minParamChange);
402 fitter.setMinRelChi2Change(mSVParams->minRelChi2Change);
403 fitter.setMaxDZIni(mSVParams->maxDZIni);
404 fitter.setMaxDXYIni(mSVParams->maxDXYIni);
405 fitter.setMaxChi2(mSVParams->maxChi2);
406 fitter.setMatCorrType(o2::base::Propagator::MatCorrType(mSVParams->matCorr));
407 fitter.setUsePropagator(mSVParams->usePropagator);
408 fitter.setRefitWithMatCorr(mSVParams->refitWithMatCorr);
409 fitter.setMaxStep(mSVParams->maxStep);
410 fitter.setMaxSnp(mSVParams->maxSnp);
411 fitter.setMinXSeed(mSVParams->minXSeed);
412 }
413}
414
415//__________________________________________________________________
416bool SVertexer::acceptTrack(const GIndex gid, const o2::track::TrackParCov& trc) const
417{
418 if (gid.isPVContributor() && mSVParams->maxPVContributors < 1) {
419 return false;
420 }
421
422 // DCA to mean vertex
423 if (mSVParams->minDCAToPV > 0.f) {
424 o2::track::TrackPar trp(trc);
425 std::array<float, 2> dca;
426 auto* prop = o2::base::Propagator::Instance();
427 if (mSVParams->usePropagator) {
428 if (trp.getX() > mSVParams->minRFor3DField && !prop->PropagateToXBxByBz(trp, mSVParams->minRFor3DField, mSVParams->maxSnp, mSVParams->maxStep, o2::base::Propagator::MatCorrType(mSVParams->matCorr))) {
429 return true; // we don't need actually to propagate to the beam-line
430 }
431 if (!prop->propagateToDCA(mMeanVertex.getXYZ(), trp, prop->getNominalBz(), mSVParams->maxStep, o2::base::Propagator::MatCorrType(mSVParams->matCorr), &dca)) {
432 return true;
433 }
434 } else {
435 if (!trp.propagateParamToDCA(mMeanVertex.getXYZ(), prop->getNominalBz(), &dca)) {
436 return true;
437 }
438 }
439 if (std::abs(dca[0]) < mSVParams->minDCAToPV) {
440 return false;
441 }
442 }
443 return true;
444}
445
446//__________________________________________________________________
447void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // accessor to various tracks
448{
449 // build track->vertices from vertices->tracks, rejecting vertex contributors if requested
450 auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks
451 auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs
452 bool isTPCloaded = recoData.isTrackSourceLoaded(GIndex::TPC);
453 bool isITSloaded = recoData.isTrackSourceLoaded(GIndex::ITS);
454 bool isITSTPCloaded = recoData.isTrackSourceLoaded(GIndex::ITSTPC);
455 if (isTPCloaded && !mSVParams->mExcludeTPCtracks) {
456 mTPCTracksArray = recoData.getTPCTracks();
457 mTPCTrackClusIdx = recoData.getTPCTracksClusterRefs();
458 mTPCClusterIdxStruct = &recoData.inputsTPCclusters->clusterIndex;
459 mTPCRefitterShMap = recoData.clusterShMapTPC;
460 mTPCRefitterOccMap = mRecoCont->occupancyMapTPC;
461 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());
462 }
463
464 std::unordered_map<GIndex, std::pair<int, int>> tmap;
465 std::unordered_map<GIndex, bool> rejmap;
466 // The last entry is for unassigned tracks, ignore them. A timeframe holding no collision at
467 // all has no entry, and the subtraction would then wrap around.
468 int nv = vtxRefs.size() > 0 ? vtxRefs.size() - 1 : 0;
469 for (int i = 0; i < 2; i++) {
470 mTracksPool[i].clear();
471 mVtxFirstTrack[i].clear();
472 mVtxFirstTrack[i].resize(nv, -1);
473 }
474 for (int iv = 0; iv < nv; iv++) {
475 const auto& vtref = vtxRefs[iv];
476 int it = vtref.getFirstEntry(), itLim = it + vtref.getEntries();
477 for (; it < itLim; it++) {
478 auto tvid = trackIndex[it];
479 if (!recoData.isTrackSourceLoaded(tvid.getSource())) {
480 continue;
481 }
482 if (tvid.getSource() == GIndex::TPC) {
483 if (mSVParams->mExcludeTPCtracks) {
484 continue;
485 }
486 // unconstrained TPC tracks require special treatment: there is no point in checking DCA to mean vertex since it is not precise,
487 // but we need to create a clone of TPC track constrained to this particular vertex time.
488 if (processTPCTrack(mTPCTracksArray[tvid], tvid, iv)) {
489 continue;
490 }
491 }
492
493 if (tvid.isAmbiguous()) { // was this track already processed?
494 auto tref = tmap.find(tvid);
495 if (tref != tmap.end()) {
496 mTracksPool[tref->second.second][tref->second.first].vBracket.setMax(iv); // this track was already processed with other vertex, account the latter
497 continue;
498 }
499 // was it already rejected?
500 if (rejmap.find(tvid) != rejmap.end()) {
501 continue;
502 }
503 }
504 const auto& trc = recoData.getTrackParam(tvid);
505
506 bool hasTPC = false;
507 bool heavyIonisingParticle = false;
508 bool compatibleWithProton = mSVParams->mFractiondEdxforCascBaryons > 0.999f; // if 1 or above, accept all regardless of TPC
509 auto tpcGID = recoData.getTPCContributorGID(tvid);
510 if (tpcGID.isIndexSet() && isTPCloaded) {
511 hasTPC = true;
512 auto& tpcTrack = recoData.getTPCTrack(tpcGID);
513 float dEdxTPC = tpcTrack.getdEdx().dEdxTotTPC;
514 if (dEdxTPC > mSVParams->minTPCdEdx && trc.getP() > mSVParams->minMomTPCdEdx) // accept high dEdx tracks (He3, He4)
515 {
516 heavyIonisingParticle = true;
517 }
518 auto protonId = o2::track::PID::Proton;
519 float dEdxExpected = mPIDresponse.getExpectedSignal(tpcTrack, protonId);
520 float fracDevProton = std::abs((dEdxTPC - dEdxExpected) / dEdxExpected);
521 if (fracDevProton < mSVParams->mFractiondEdxforCascBaryons) {
522 compatibleWithProton = true;
523 }
524 }
525
526 // get Nclusters in the ITS if available
527 int8_t nITSclu = -1;
528 bool shortOBITSOnlyTrack = false;
529 auto itsGID = recoData.getITSContributorGID(tvid);
530 if (itsGID.getSource() == GIndex::ITS) {
531 if (isITSloaded) {
532 auto& itsTrack = recoData.getITSTrack(itsGID);
533 nITSclu = itsTrack.getNumberOfClusters();
534 if (itsTrack.hasHitOnLayer(6) && itsTrack.hasHitOnLayer(5) && itsTrack.hasHitOnLayer(4) && itsTrack.hasHitOnLayer(3)) {
535 shortOBITSOnlyTrack = true;
536 }
537 }
538 } else if (itsGID.getSource() == GIndex::ITSAB) {
539 if (isITSTPCloaded) {
540 auto& itsABTracklet = recoData.getITSABRef(itsGID);
541 nITSclu = itsABTracklet.getNClusters();
542 }
543 }
544 if (!acceptTrack(tvid, trc) && !heavyIonisingParticle) {
545 if (tvid.isAmbiguous()) {
546 rejmap[tvid] = true;
547 }
548 continue;
549 }
550 if ((isTPCloaded && !hasTPC) && (isITSloaded && (nITSclu < mSVParams->mITSSAminNclu && (!shortOBITSOnlyTrack || mSVParams->mRejectITSonlyOBtrack)))) {
551 continue; // reject short ITS-only
552 }
553
554 int posneg = trc.getSign() < 0 ? 1 : 0;
555 float r = std::sqrt(trc.getX() * trc.getX() + trc.getY() * trc.getY());
556 mTracksPool[posneg].emplace_back(TrackCand{trc, tvid, {iv, iv}, r, hasTPC, nITSclu, compatibleWithProton});
557 if (tvid.getSource() == GIndex::TPC) { // constrained TPC track?
558 correctTPCTrack(mTracksPool[posneg].back(), mTPCTracksArray[tvid], -1, -1);
559 }
560 if (tvid.isAmbiguous()) { // track attached to >1 vertex, remember that it was already processed
561 tmap[tvid] = {mTracksPool[posneg].size() - 1, posneg};
562 }
563 }
564 }
565 // register 1st track of each charge for each vertex
566 for (int pn = 0; pn < 2; pn++) {
567 auto& vtxFirstT = mVtxFirstTrack[pn];
568 const auto& tracksPool = mTracksPool[pn];
569 for (unsigned i = 0; i < tracksPool.size(); i++) {
570 const auto& t = tracksPool[i];
571 for (int j{t.vBracket.getMin()}; j <= t.vBracket.getMax(); ++j) {
572 if (vtxFirstT[j] == -1) {
573 vtxFirstT[j] = i;
574 }
575 }
576 }
577 }
578
579 LOG(info) << "Collected " << mTracksPool[POS].size() << " positive and " << mTracksPool[NEG].size() << " negative seeds";
580}
581
582//__________________________________________________________________
583bool SVertexer::checkV0(const TrackCand& seedP, const TrackCand& seedN, int iP, int iN, int ithread)
584{
585 auto& fitterV0 = mFitterV0[ithread];
586 // Fast rough cuts on pairs before feeding to DCAFitter, tracks are not in the same Frame or at same X
587 bool isTPConly = (seedP.gid.getSource() == GIndex::TPC || seedN.gid.getSource() == GIndex::TPC);
588 if (mSVParams->mTPCTrackPhotonTune && isTPConly) {
589 // Check if Tgl is close enough
590 if (std::abs(seedP.getTgl() - seedN.getTgl()) > mSVParams->maxV0TglAbsDiff) {
591 LOG(debug) << "RejTgl";
592 return false;
593 }
594 // Check in transverse plane
595 float sna, csa;
596 o2::math_utils::CircleXYf_t trkPosCircle;
597 seedP.getCircleParams(mBz, trkPosCircle, sna, csa);
598 o2::math_utils::CircleXYf_t trkEleCircle;
599 seedN.getCircleParams(mBz, trkEleCircle, sna, csa);
600 // Does the radius of both tracks compare to their absolute circle center distance
601 float c2c = std::hypot(trkPosCircle.xC - trkEleCircle.xC,
602 trkPosCircle.yC - trkEleCircle.yC);
603 float r2r = trkPosCircle.rC + trkEleCircle.rC;
604 float dcr = c2c - r2r;
605 if (std::abs(dcr) > mSVParams->mTPCTrackD2R) {
606 LOG(debug) << "RejD2R " << c2c << " " << r2r << " " << dcr;
607 return false;
608 }
609 // Will the conversion point look somewhat reasonable
610 float r1_r = trkPosCircle.rC / r2r;
611 float r2_r = trkEleCircle.rC / r2r;
612 float dR = std::hypot(r2_r * trkPosCircle.xC + r1_r * trkEleCircle.xC, r2_r * trkPosCircle.yC + r1_r * trkEleCircle.yC);
613 if (dR > mSVParams->mTPCTrackDR) {
614 LOG(debug) << "RejDR" << dR;
615 return false;
616 }
617
618 // Setup looser cuts for the DCAFitter
619 fitterV0.setMaxDZIni(mSVParams->mTPCTrackMaxDZIni);
620 fitterV0.setMaxDXYIni(mSVParams->mTPCTrackMaxDXYIni);
621 fitterV0.setMaxChi2(mSVParams->mTPCTrackMaxChi2);
622 fitterV0.setCollinear(true);
623 }
624
625 // feed DCAFitter
626 int nCand = fitterV0.process(seedP, seedN);
627 if (mSVParams->mTPCTrackPhotonTune && isTPConly) {
628 // Reset immediately to the defaults
629 fitterV0.setMaxDZIni(mSVParams->maxDZIni);
630 fitterV0.setMaxDXYIni(mSVParams->maxDXYIni);
631 fitterV0.setMaxChi2(mSVParams->maxChi2);
632 fitterV0.setCollinear(false);
633 }
634
635 if (nCand == 0) { // discard this pair
636 LOG(debug) << "RejDCAFitter";
637 return false;
638 }
639 const auto& v0XYZ = fitterV0.getPCACandidate();
640 // validate V0 radial position
641 // check closeness to the beam-line
642 float dxv0 = v0XYZ[0] - mMeanVertex.getX(), dyv0 = v0XYZ[1] - mMeanVertex.getY(), r2v0 = dxv0 * dxv0 + dyv0 * dyv0;
643 if (r2v0 < mMinR2ToMeanVertex) {
644 LOG(debug) << "RejMinR2ToMeanVertex";
645 return false;
646 }
647 float rv0 = std::sqrt(r2v0), drv0P = rv0 - seedP.minR, drv0N = rv0 - seedN.minR;
648 if (drv0P > mSVParams->causalityRTolerance || drv0P < -mSVParams->maxV0ToProngsRDiff ||
649 drv0N > mSVParams->causalityRTolerance || drv0N < -mSVParams->maxV0ToProngsRDiff) {
650 LOG(debug) << "RejCausality " << drv0P << " " << drv0N;
651 return false;
652 }
653 const int cand = 0;
654 if (!fitterV0.isPropagateTracksToVertexDone(cand) && !fitterV0.propagateTracksToVertex(cand)) {
655 LOG(debug) << "RejProp failed";
656 return false;
657 }
658 const auto& trPProp = fitterV0.getTrack(0, cand);
659 const auto& trNProp = fitterV0.getTrack(1, cand);
660 std::array<float, 3> pP{}, pN{};
661 trPProp.getPxPyPzGlo(pP);
662 trNProp.getPxPyPzGlo(pN);
663 // estimate DCA of neutral V0 track to beamline: straight line with parametric equation
664 // x = X0 + pV0[0]*t, y = Y0 + pV0[1]*t reaches DCA to beamline (Xv, Yv) at
665 // t = -[ (x0-Xv)*pV0[0] + (y0-Yv)*pV0[1]) ] / ( pT(pV0)^2 )
666 // Similar equation for 3D distance involving pV0[2]
667 std::array<float, 3> pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]};
668 float pt2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1], prodXYv0 = dxv0 * pV0[0] + dyv0 * pV0[1], tDCAXY = prodXYv0 / pt2V0;
669 if (pt2V0 < mMinPt2V0) { // pt cut
670 LOG(debug) << "RejPt2 " << pt2V0;
671 return false;
672 }
673 if (pV0[2] * pV0[2] / pt2V0 > mMaxTgl2V0) { // tgLambda cut
674 LOG(debug) << "RejTgL " << pV0[2] * pV0[2] / pt2V0;
675 return false;
676 }
677 float p2V0 = pt2V0 + pV0[2] * pV0[2], ptV0 = std::sqrt(pt2V0);
678 // apply mass selections
679 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];
680
681 bool goodHyp = false, photonOnly = mSVParams->mTPCTrackPhotonTune && isTPConly;
682 std::array<bool, NHypV0> hypCheckStatus{};
683 int nPID = photonOnly ? (Photon + 1) : NHypV0;
684 for (int ipid = 0; (ipid < nPID) && mSVParams->checkV0Hypothesis; ipid++) {
685 if (mV0Hyps[ipid].check(p2Pos, p2Neg, p2V0, ptV0)) {
686 goodHyp = hypCheckStatus[ipid] = true;
687 }
688 }
689 // check tight lambda mass only
690 bool goodLamForCascade = false, goodALamForCascade = false;
691 bool usesTPCOnly = (seedP.hasTPC && !seedP.hasITS()) || (seedN.hasTPC && !seedN.hasITS());
692 bool usesShortITSOnly = (!seedP.hasTPC && seedP.nITSclu < mSVParams->mITSSAminNcluCascades) || (!seedN.hasTPC && seedN.nITSclu < mSVParams->mITSSAminNcluCascades);
693 if (ptV0 > mSVParams->minPtV0FromCascade && (!mSVParams->mSkipTPCOnlyCascade || !usesTPCOnly) && !usesShortITSOnly) {
694 if (mV0Hyps[Lambda].checkTight(p2Pos, p2Neg, p2V0, ptV0) && (!mSVParams->mRequireTPCforCascBaryons || seedP.hasTPC) && seedP.compatibleProton) {
695 goodLamForCascade = true;
696 }
697 if (mV0Hyps[AntiLambda].checkTight(p2Pos, p2Neg, p2V0, ptV0) && (!mSVParams->mRequireTPCforCascBaryons || seedN.hasTPC) && seedN.compatibleProton) {
698 goodALamForCascade = true;
699 }
700 }
701
702 // apply mass selections for 3-body decay
703 bool good3bodyV0Hyp = false;
704 for (int ipid = 2; ipid < 4; ipid++) {
705 float massForLambdaHyp = mV0Hyps[ipid].calcMass(p2Pos, p2Neg, p2V0);
706 if (massForLambdaHyp - mV0Hyps[ipid].getMassV0Hyp() < mV0Hyps[ipid].getMargin(ptV0)) {
707 good3bodyV0Hyp = true;
708 break;
709 }
710 }
711
712 // 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-)))
713 bool checkFor3BodyDecays = mEnable3BodyDecays &&
714 (!mSVParams->checkV0Hypothesis || good3bodyV0Hyp) &&
715 (pt2V0 > 0.5) &&
716 (!mSVParams->mSkipTPCOnly3Body || !isTPConly);
717 bool rejectAfter3BodyCheck = false; // To reject v0s which can be 3-body decay candidates but not cascade or v0
718 bool checkForCascade = mEnableCascades &&
719 (!mSVParams->mSkipTPCOnlyCascade || !usesTPCOnly) &&
720 r2v0 < mMaxR2ToMeanVertexCascV0 &&
721 (!mSVParams->checkV0Hypothesis || (goodLamForCascade || goodALamForCascade) &&
722 (!isTPConly || !hypCheckStatus[HypV0::Photon]));
723 bool rejectIfNotCascade = false;
724
725 if (!goodHyp && mSVParams->checkV0Hypothesis) {
726 LOG(debug) << "RejHypo";
727 if (!checkFor3BodyDecays && !checkForCascade) {
728 return false;
729 } else {
730 rejectAfter3BodyCheck = true;
731 }
732 }
733
734 float dcaX = dxv0 - pV0[0] * tDCAXY, dcaY = dyv0 - pV0[1] * tDCAXY, dca2 = dcaX * dcaX + dcaY * dcaY;
735 float cosPAXY = prodXYv0 / std::sqrt(r2v0 * pt2V0);
736
737 if (checkForCascade) { // use looser cuts for cascade v0 candidates
738 if (dca2 > mMaxDCAXY2ToMeanVertexV0Casc || cosPAXY < mSVParams->minCosPAXYMeanVertexCascV0) {
739 LOG(debug) << "Rej for cascade DCAXY2: " << dca2 << " << cosPAXY: " << cosPAXY;
740 if (!checkFor3BodyDecays) {
741 return false;
742 } else {
743 rejectAfter3BodyCheck = true;
744 }
745 }
746 }
747 if (checkFor3BodyDecays) { // use looser cuts for 3-body decay candidates
748 if (dca2 > mMaxDCAXY2ToMeanVertex3bodyV0 || cosPAXY < mSVParams->minCosPAXYMeanVertex3bodyV0) {
749 LOG(debug) << "Rej for 3 body decays DCAXY2: " << dca2 << " << cosPAXY: " << cosPAXY;
750 checkFor3BodyDecays = false;
751 }
752 }
753
754 if (dca2 > mMaxDCAXY2ToMeanVertex || cosPAXY < mSVParams->minCosPAXYMeanVertex) {
755 if (checkForCascade) {
756 rejectIfNotCascade = true;
757 } else if (checkFor3BodyDecays) {
758 rejectAfter3BodyCheck = true;
759 } else {
760 if (mSVParams->mTPCTrackPhotonTune && isTPConly) {
761 // Check for looser cut for tpc-only photons only
762 if (dca2 > mSVParams->mTPCTrackMaxDCAXY2ToMeanVertex) {
763 return false;
764 }
765 } else {
766 return false;
767 }
768 }
769 }
770
771 auto vlist = seedP.vBracket.getOverlap(seedN.vBracket); // indices of vertices shared by both seeds
772 bool candFound = false;
773 auto bestCosPA = checkForCascade ? mSVParams->minCosPACascV0 : mSVParams->minCosPA;
774 bestCosPA = checkFor3BodyDecays ? std::min(mSVParams->minCosPA3bodyV0, bestCosPA) : bestCosPA;
775 V0 v0new;
776 V0Index v0Idxnew;
777
778 for (int iv = vlist.getMin(); iv <= vlist.getMax(); iv++) {
779 const auto& pv = mPVertices[iv];
780 const auto v0XYZ = fitterV0.getPCACandidatePos(cand);
781 // check cos of pointing angle
782 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];
783 float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0);
784 if (cosPA < bestCosPA) {
785 LOG(debug) << "Rej. cosPA: " << cosPA;
786 continue;
787 }
788 if (!candFound) {
789 new (&v0new) V0(v0XYZ, pV0, fitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp);
790 new (&v0Idxnew) V0Index(-1, seedP.gid, seedN.gid);
791 v0new.setDCA(fitterV0.getChi2AtPCACandidate(cand));
792 candFound = true;
793 }
794 v0new.setCosPA(cosPA);
795 v0Idxnew.setVertexID(iv);
796 bestCosPA = cosPA;
797 }
798 if (!candFound) {
799 return false;
800 }
801 if (bestCosPA < mSVParams->minCosPACascV0) {
802 rejectAfter3BodyCheck = true;
803 }
804 if (bestCosPA < mSVParams->minCosPA && checkForCascade) {
805 rejectIfNotCascade = true;
806 }
807 int nV0Ini = mV0sIdxTmp[ithread].size();
808 // check 3 body decays
809 if (checkFor3BodyDecays) {
810 int n3bodyDecays = 0;
811 n3bodyDecays += check3bodyDecays(v0Idxnew, v0new, rv0, pV0, p2V0, iN, NEG, vlist, ithread);
812 n3bodyDecays += check3bodyDecays(v0Idxnew, v0new, rv0, pV0, p2V0, iP, POS, vlist, ithread);
813 }
814 if (rejectAfter3BodyCheck) {
815 return false;
816 }
817
818 // check cascades
819 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)
820 if (checkForCascade) {
821 if (goodLamForCascade || !mSVParams->checkCascadeHypothesis) {
822 nV0Used += checkCascades(v0Idxnew, v0new, rv0, pV0, p2V0, iN, NEG, vlist, ithread);
823 }
824 if (goodALamForCascade || !mSVParams->checkCascadeHypothesis) {
825 nV0Used += checkCascades(v0Idxnew, v0new, rv0, pV0, p2V0, iP, POS, vlist, ithread);
826 }
827 }
828
829 if (nV0Used) { // need to fix the index of V0 for the cascades using this v0
830 for (unsigned int ic = nCascIni; ic < mCascadesIdxTmp[ithread].size(); ic++) {
831 if (mCascadesIdxTmp[ithread][ic].getV0ID() == -1) {
832 mCascadesIdxTmp[ithread][ic].setV0ID(nV0Ini);
833 }
834 }
835 }
836
837 if (nV0Used || !rejectIfNotCascade) { // need to add this v0
838 mV0sIdxTmp[ithread].push_back(v0Idxnew);
839 if (!rejectIfNotCascade) {
840 mV0sIdxTmp[ithread].back().setStandaloneV0();
841 }
842 if (photonOnly) {
843 mV0sIdxTmp[ithread].back().setPhotonOnly();
844 mV0sIdxTmp[ithread].back().setCollinear();
845 }
846
847 if (mSVParams->createFullV0s) {
848 mV0sTmp[ithread].push_back(v0new);
849 }
850 }
851
852 if (mStrTracker) {
853 for (int iv = nV0Ini; iv < (int)mV0sIdxTmp[ithread].size(); iv++) {
854 mStrTracker->processV0(iv, v0new, v0Idxnew, ithread);
855 }
856 }
857
858 return mV0sIdxTmp[ithread].size() - nV0Ini != 0;
859}
860
861//__________________________________________________________________
862int 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)
863{
864 // check last added V0 for belonging to cascade
865 auto& fitterCasc = mFitterCasc[ithread];
866 auto& tracks = mTracksPool[posneg];
867 int nCascIni = mCascadesIdxTmp[ithread].size(), nv0use = 0;
868
869 // check if a given PV has already been used in a cascade
870 std::unordered_map<int, int> pvMap;
871
872 // start from the 1st bachelor track compatible with earliest vertex in the v0vlist
873 int firstTr = mVtxFirstTrack[posneg][v0vlist.getMin()], nTr = tracks.size();
874 if (firstTr < 0) {
875 firstTr = nTr;
876 }
877 for (int it = firstTr; it < nTr; it++) {
878 if (it == avoidTrackID) {
879 continue; // skip the track used by V0
880 }
881 auto& bach = tracks[it];
882 if (mSVParams->mSkipTPCOnlyCascade && (bach.gid.getSource() == GIndex::TPC)) {
883 continue; // reject TPC-only bachelors
884 }
885 if (!bach.hasTPC && bach.nITSclu < mSVParams->mITSSAminNcluCascades) {
886 continue; // reject short ITS-only
887 }
888
889 if (bach.vBracket.getMin() > v0vlist.getMax()) {
890 LOG(debug) << "Skipping";
891 break; // all other bachelor candidates will be also not compatible with this PV
892 }
893 auto cascVlist = v0vlist.getOverlap(bach.vBracket); // indices of vertices shared by V0 and bachelor
894 if (mSVParams->selectBestV0) {
895 // select only the best V0 candidate among the compatible ones
896 if (v0Idx.getVertexID() < cascVlist.getMin() || v0Idx.getVertexID() > cascVlist.getMax()) {
897 continue;
898 }
899 cascVlist.setMin(v0Idx.getVertexID());
900 cascVlist.setMax(v0Idx.getVertexID());
901 }
902
903 int nCandC = fitterCasc.process(v0, bach);
904 if (nCandC == 0) { // discard this pair
905 continue;
906 }
907 const int candC = 0;
908 const auto& cascXYZ = fitterCasc.getPCACandidatePos(candC);
909
910 // make sure the cascade radius is smaller than that of the mean vertex
911 float dxc = cascXYZ[0] - mMeanVertex.getX(), dyc = cascXYZ[1] - mMeanVertex.getY(), r2casc = dxc * dxc + dyc * dyc;
912 if (rv0 * rv0 - r2casc < mMinR2DiffV0Casc || r2casc < mMinR2ToMeanVertex) {
913 continue;
914 }
915 // do we want to apply mass cut ?
916 //
917 if (!fitterCasc.isPropagateTracksToVertexDone(candC) && !fitterCasc.propagateTracksToVertex(candC)) {
918 continue;
919 }
920
921 auto& trNeut = fitterCasc.getTrack(0, candC);
922 auto& trBach = fitterCasc.getTrack(1, candC);
923 trNeut.setPID(o2::track::PID::Lambda);
924 trBach.setPID(o2::track::PID::Pion);
925 std::array<float, 3> pNeut, pBach;
926 trNeut.getPxPyPzGlo(pNeut);
927 trBach.getPxPyPzGlo(pBach);
928 std::array<float, 3> pCasc = {pNeut[0] + pBach[0], pNeut[1] + pBach[1], pNeut[2] + pBach[2]};
929
930 float pt2Casc = pCasc[0] * pCasc[0] + pCasc[1] * pCasc[1], p2Casc = pt2Casc + pCasc[2] * pCasc[2];
931 if (pt2Casc < mMinPt2Casc) { // pt cut
932 LOG(debug) << "Casc pt too low";
933 continue;
934 }
935 if (pCasc[2] * pCasc[2] / pt2Casc > mMaxTgl2Casc) { // tgLambda cut
936 LOG(debug) << "Casc tgLambda too high";
937 continue;
938 }
939
940 // compute primary vertex and cosPA of the cascade
941 auto bestCosPA = mSVParams->minCosPACasc;
942 auto cascVtxID = -1;
943
944 for (int iv = cascVlist.getMin(); iv <= cascVlist.getMax(); iv++) {
945 const auto& pv = mPVertices[iv];
946 // check cos of pointing angle
947 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];
948 float cosPA = prodXYZcasc / std::sqrt((dx * dx + dy * dy + dz * dz) * p2Casc);
949 if (cosPA < bestCosPA) {
950 LOG(debug) << "Rej. cosPA: " << cosPA;
951 continue;
952 }
953 cascVtxID = iv;
954 bestCosPA = cosPA;
955 }
956 if (cascVtxID == -1) {
957 LOG(debug) << "Casc not compatible with any vertex";
958 continue;
959 }
960
961 const auto& cascPv = mPVertices[cascVtxID];
962 float dxCasc = cascXYZ[0] - cascPv.getX(), dyCasc = cascXYZ[1] - cascPv.getY(), dzCasc = cascXYZ[2] - cascPv.getZ();
963 auto prodPPos = pV0[0] * dxCasc + pV0[1] * dyCasc + pV0[2] * dzCasc;
964 if (prodPPos < 0.) { // causality cut
965 LOG(debug) << "Casc not causally compatible";
966 continue;
967 }
968
969 float p2Bach = pBach[0] * pBach[0] + pBach[1] * pBach[1] + pBach[2] * pBach[2];
970 float ptCasc = std::sqrt(pt2Casc);
971 bool goodHyp = false;
972 for (int ipid = 0; ipid < NHypCascade; ipid++) {
973 if (mCascHyps[ipid].check(p2V0, p2Bach, p2Casc, ptCasc)) {
974 goodHyp = true;
975 break;
976 }
977 }
978 if (!goodHyp) {
979 LOG(debug) << "Casc not compatible with any hypothesis";
980 continue;
981 }
982 // 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
983 // but not necessarily at the and of current v0s vector, since meanwhile checkCascades may add v0 clones (with PV redefined).
984 Cascade casc(cascXYZ, pCasc, fitterCasc.calcPCACovMatrixFlat(candC), trNeut, trBach);
985 o2::track::TrackParCov trc = casc;
987 if (!trc.propagateToDCA(cascPv, fitterCasc.getBz(), &dca, 5.) ||
988 std::abs(dca.getY()) > mSVParams->maxDCAXYCasc || std::abs(dca.getZ()) > mSVParams->maxDCAZCasc) {
989 LOG(debug) << "Casc not compatible with PV";
990 LOG(debug) << "DCA: " << dca.getY() << " " << dca.getZ();
991 continue;
992 }
993 CascadeIndex cascIdx(cascVtxID, -1, bach.gid); // the v0Idx was not yet added, this will be done after the checkCascades
994
995 LOGP(debug, "cascade successfully validated");
996
997 // clone the V0, set new cosPA and VerteXID, add it to the list of V0s
998 if (cascVtxID != v0Idx.getVertexID()) {
999 auto pvIdx = pvMap.find(cascVtxID);
1000 if (pvIdx != pvMap.end()) {
1001 cascIdx.setV0ID(pvIdx->second); // V0 already exists, add reference to the cascade
1002 } else { // add V0 clone for this cascade (may be used also by other cascades)
1003 const auto& pv = mPVertices[cascVtxID];
1004 cascIdx.setV0ID(mV0sIdxTmp[ithread].size()); // set the new V0 index in the cascade
1005 pvMap[cascVtxID] = mV0sTmp[ithread].size(); // add the new V0 index to the map
1006 mV0sIdxTmp[ithread].emplace_back(cascVtxID, v0Idx.getProngs());
1007 if (mSVParams->createFullV0s) {
1008 mV0sTmp[ithread].push_back(v0);
1009 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];
1010 mV0sTmp[ithread].back().setCosPA(prodXYZ / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0));
1011 }
1012 }
1013 } else {
1014 nv0use++; // original v0 was used
1015 }
1016 mCascadesIdxTmp[ithread].push_back(cascIdx);
1017 if (mSVParams->createFullCascades) {
1018 casc.setCosPA(bestCosPA);
1019 casc.setDCA(fitterCasc.getChi2AtPCACandidate(candC));
1020 mCascadesTmp[ithread].push_back(casc);
1021 }
1022 if (mStrTracker) {
1023 mStrTracker->processCascade(mCascadesIdxTmp[ithread].size() - 1, casc, cascIdx, v0, ithread);
1024 }
1025 }
1026
1027 return nv0use;
1028}
1029
1030//__________________________________________________________________
1031int 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)
1032{
1033 // check last added V0 for belonging to cascade
1034 auto& fitter3body = mFitter3body[ithread];
1035 auto& tracks = mTracksPool[posneg];
1036 int n3BodyIni = m3bodyIdxTmp[ithread].size();
1037
1038 // start from the 1st bachelor track compatible with earliest vertex in the v0vlist
1039 int firstTr = mVtxFirstTrack[posneg][v0vlist.getMin()], nTr = tracks.size();
1040 if (firstTr < 0) {
1041 firstTr = nTr;
1042 }
1043
1044 // 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.
1045 // Otherwise, we should pair it with all negative particles, and the negative particle in the V0 is a antiproton.
1046
1047 // start from the 1st track compatible with V0's primary vertex
1048 for (int it = firstTr; it < nTr; it++) {
1049 if (it == avoidTrackID) {
1050 continue; // skip the track used by V0
1051 }
1052 auto& bach = tracks[it];
1053 if (mSVParams->mSkipTPCOnly3Body && (bach.gid.getSource() == GIndex::TPC)) {
1054 continue; // reject TPC-only bachelors
1055 }
1056 if (bach.vBracket > v0vlist.getMax()) {
1057 LOG(debug) << "Skipping";
1058 break; // all other bachelor candidates will be also not compatible with this PV
1059 }
1060 auto decay3bodyVlist = v0vlist.getOverlap(bach.vBracket); // indices of vertices shared by V0 and bachelor
1061 if (mSVParams->selectBestV0) {
1062 // select only the best V0 candidate among the compatible ones
1063 if (v0Idx.getVertexID() < decay3bodyVlist.getMin() || v0Idx.getVertexID() > decay3bodyVlist.getMax()) {
1064 continue;
1065 }
1066 decay3bodyVlist.setMin(v0Idx.getVertexID());
1067 decay3bodyVlist.setMax(v0Idx.getVertexID());
1068 }
1069
1070 if (bach.getPt() < 0.6) {
1071 continue;
1072 }
1073
1074 int n3bodyVtx = fitter3body.process(v0.getProng(0), v0.getProng(1), bach);
1075 if (n3bodyVtx == 0) { // discard this pair
1076 continue;
1077 }
1078 int cand3B = 0;
1079 const auto& vertexXYZ = fitter3body.getPCACandidatePos(cand3B);
1080
1081 // make sure the 3 body vertex radius is close to that of the mean vertex
1082 float dxc = vertexXYZ[0] - mMeanVertex.getX(), dyc = vertexXYZ[1] - mMeanVertex.getY(), dzc = vertexXYZ[2] - mMeanVertex.getZ(), r2vertex = dxc * dxc + dyc * dyc;
1083 if (std::abs(rv0 - std::sqrt(r2vertex)) > mSVParams->maxRDiffV03body || r2vertex < mMinR2ToMeanVertex) {
1084 continue;
1085 }
1086 float drvtxBach = std::sqrt(r2vertex) - bach.minR;
1087 if (drvtxBach > mSVParams->causalityRTolerance || drvtxBach < -mSVParams->maxV0ToProngsRDiff) {
1088 LOG(debug) << "RejCausality " << drvtxBach;
1089 }
1090 //
1091 if (!fitter3body.isPropagateTracksToVertexDone() && !fitter3body.propagateTracksToVertex()) {
1092 continue;
1093 }
1094
1095 auto& tr0 = fitter3body.getTrack(0, cand3B);
1096 auto& tr1 = fitter3body.getTrack(1, cand3B);
1097 auto& tr2 = fitter3body.getTrack(2, cand3B);
1098 std::array<float, 3> p0, p1, p2;
1099 tr0.getPxPyPzGlo(p0);
1100 tr1.getPxPyPzGlo(p1);
1101 tr2.getPxPyPzGlo(p2);
1102
1103 bool goodHyp = false;
1104 o2::track::PID pidHyp = o2::track::PID::Electron; // Update if goodHyp is true
1105 auto decay3bodyVtxID = -1;
1106 auto vtxCosPA = -1;
1107
1108 std::array<float, 3> pbach = {0, 0, 0}, p3B = {0, 0, 0}; // Update during the check of invariant mass
1109 for (int ipid = 0; ipid < NHyp3body; ipid++) {
1110 // check mass based on hypothesis of charge of bachelor (pos and neg expected to be proton/pion)
1111 float bachChargeFactor = m3bodyHyps[ipid].getChargeBachProng() / tr2.getAbsCharge();
1112 pbach = {bachChargeFactor * p2[0], bachChargeFactor * p2[1], bachChargeFactor * p2[2]};
1113 p3B = {p0[0] + p1[0] + pbach[0], p0[1] + p1[1] + pbach[1], p0[2] + p1[2] + pbach[2]};
1114 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];
1115 float pt2Candidate = p3B[0] * p3B[0] + p3B[1] * p3B[1], p2Candidate = pt2Candidate + p3B[2] * p3B[2];
1116 float ptCandidate = std::sqrt(pt2Candidate);
1117 if (m3bodyHyps[ipid].check(sqP0, sqP1, sqPBach, p2Candidate, ptCandidate)) {
1118 if (pt2Candidate < mMinPt23Body) { // pt cut
1119 continue;
1120 }
1121 if (p3B[2] * p3B[2] > pt2Candidate * mMaxTgl23Body) { // tgLambda cut
1122 continue;
1123 }
1124
1125 // compute primary vertex and cosPA of the 3-body decay
1126 auto bestCosPA = mSVParams->minCosPA3body;
1127 for (int iv = decay3bodyVlist.getMin(); iv <= decay3bodyVlist.getMax(); iv++) {
1128 const auto& pv = mPVertices[iv];
1129 // check cos of pointing angle
1130 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];
1131 float cosPA = prodXYZ3body / std::sqrt((dx * dx + dy * dy + dz * dz) * p2Candidate);
1132 if (cosPA < bestCosPA) {
1133 LOG(debug) << "Rej. cosPA: " << cosPA;
1134 continue;
1135 }
1136 decay3bodyVtxID = iv;
1137 bestCosPA = cosPA;
1138 }
1139 if (decay3bodyVtxID == -1) {
1140 LOG(debug) << "3-body decay not compatible with any vertex";
1141 continue;
1142 }
1143
1144 goodHyp = true;
1145 pidHyp = m3bodyHyps[ipid].getPIDHyp();
1146 vtxCosPA = bestCosPA;
1147 break;
1148 }
1149 }
1150 if (!goodHyp) {
1151 continue;
1152 }
1153
1154 const auto& decay3bodyPv = mPVertices[decay3bodyVtxID];
1155 Decay3Body candidate3B(vertexXYZ, p3B, fitter3body.calcPCACovMatrixFlat(cand3B), tr0, tr1, tr2, pidHyp);
1156 o2::track::TrackParCov trc = candidate3B;
1158 if (!trc.propagateToDCA(decay3bodyPv, fitter3body.getBz(), &dca, 5.) ||
1159 std::abs(dca.getY()) > mSVParams->maxDCAXY3Body || std::abs(dca.getZ()) > mSVParams->maxDCAZ3Body) {
1160 continue;
1161 }
1162 if (mSVParams->createFull3Bodies) {
1163 candidate3B.setCosPA(vtxCosPA);
1164 candidate3B.setDCA(fitter3body.getChi2AtPCACandidate());
1165 m3bodyTmp[ithread].push_back(candidate3B);
1166 }
1167 m3bodyIdxTmp[ithread].emplace_back(decay3bodyVtxID, v0Idx.getProngID(0), v0Idx.getProngID(1), bach.gid);
1168
1169 Decay3BodyIndex decay3bodyIdx(decay3bodyVtxID, v0Idx.getProngID(0), v0Idx.getProngID(1), bach.gid);
1170 if (mStrTracker) {
1171 mStrTracker->process3Body(m3bodyIdxTmp[ithread].size() - 1, candidate3B, decay3bodyIdx, ithread);
1172 }
1173 }
1174 return m3bodyIdxTmp[ithread].size() - n3BodyIni;
1175}
1176
1177//__________________________________________________________________
1178template <class TVI, class TCI, class T3I, class TR>
1179void SVertexer::extractPVReferences(const TVI& v0s, TR& vtx2V0Refs, const TCI& cascades, TR& vtx2CascRefs, const T3I& vtx3, TR& vtx2body3Refs)
1180{
1181 // V0s, cascades and 3bodies are already sorted in PV ID
1182 vtx2V0Refs.clear();
1183 vtx2V0Refs.resize(mPVertices.size());
1184 vtx2CascRefs.clear();
1185 vtx2CascRefs.resize(mPVertices.size());
1186 vtx2body3Refs.clear();
1187 vtx2body3Refs.resize(mPVertices.size());
1188 int nv0 = v0s.size(), nCasc = cascades.size(), n3body = vtx3.size();
1189
1190 // relate V0s to primary vertices
1191 int pvID = -1, nForPV = 0;
1192 for (int iv = 0; iv < nv0; iv++) {
1193 if (pvID < v0s[iv].getVertexID()) {
1194 if (pvID > -1) {
1195 vtx2V0Refs[pvID].setEntries(nForPV);
1196 }
1197 pvID = v0s[iv].getVertexID();
1198 vtx2V0Refs[pvID].setFirstEntry(iv);
1199 nForPV = 0;
1200 }
1201 nForPV++;
1202 }
1203 if (pvID != -1) { // finalize
1204 vtx2V0Refs[pvID].setEntries(nForPV);
1205 // fill empty slots
1206 int ent = nv0;
1207 for (int ip = vtx2V0Refs.size(); ip--;) {
1208 if (vtx2V0Refs[ip].getEntries()) {
1209 ent = vtx2V0Refs[ip].getFirstEntry();
1210 } else {
1211 vtx2V0Refs[ip].setFirstEntry(ent);
1212 }
1213 }
1214 }
1215
1216 // relate Cascades to primary vertices
1217 pvID = -1;
1218 nForPV = 0;
1219 for (int iv = 0; iv < nCasc; iv++) {
1220 if (pvID < cascades[iv].getVertexID()) {
1221 if (pvID > -1) {
1222 vtx2CascRefs[pvID].setEntries(nForPV);
1223 }
1224 pvID = cascades[iv].getVertexID();
1225 vtx2CascRefs[pvID].setFirstEntry(iv);
1226 nForPV = 0;
1227 }
1228 nForPV++;
1229 }
1230 if (pvID != -1) { // finalize
1231 vtx2CascRefs[pvID].setEntries(nForPV);
1232 // fill empty slots
1233 int ent = nCasc;
1234 for (int ip = vtx2CascRefs.size(); ip--;) {
1235 if (vtx2CascRefs[ip].getEntries()) {
1236 ent = vtx2CascRefs[ip].getFirstEntry();
1237 } else {
1238 vtx2CascRefs[ip].setFirstEntry(ent);
1239 }
1240 }
1241 }
1242
1243 // relate 3 body decays to primary vertices
1244 pvID = -1;
1245 nForPV = 0;
1246 for (int iv = 0; iv < n3body; iv++) {
1247 const auto& vertex3body = vtx3[iv];
1248 if (pvID < vertex3body.getVertexID()) {
1249 if (pvID > -1) {
1250 vtx2body3Refs[pvID].setEntries(nForPV);
1251 }
1252 pvID = vertex3body.getVertexID();
1253 vtx2body3Refs[pvID].setFirstEntry(iv);
1254 nForPV = 0;
1255 }
1256 nForPV++;
1257 }
1258 if (pvID != -1) { // finalize
1259 vtx2body3Refs[pvID].setEntries(nForPV);
1260 // fill empty slots
1261 int ent = n3body;
1262 for (int ip = vtx2body3Refs.size(); ip--;) {
1263 if (vtx2body3Refs[ip].getEntries()) {
1264 ent = vtx2body3Refs[ip].getFirstEntry();
1265 } else {
1266 vtx2body3Refs[ip].setFirstEntry(ent);
1267 }
1268 }
1269 }
1270}
1271
1272//__________________________________________________________________
1274{
1275#ifdef WITH_OPENMP
1276 mNThreads = n > 0 ? n : 1;
1277#else
1278 mNThreads = 1;
1279#endif
1280}
1281
1282//______________________________________________
1283bool SVertexer::processTPCTrack(const o2::tpc::TrackTPC& trTPC, GIndex gid, int vtxid)
1284{
1285 if (mSVParams->mTPCTrackMaxX > 0. && trTPC.getX() > mSVParams->mTPCTrackMaxX) {
1286 return true;
1287 }
1288 // if TPC trackis unconstrained, try to create in the tracks pool a clone constrained to vtxid vertex time.
1289 if (trTPC.hasBothSidesClusters()) { // this is effectively constrained track
1290 return false; // let it be processed as such
1291 }
1292 const auto& vtx = mPVertices[vtxid];
1293 auto twe = vtx.getTimeStamp();
1294 int posneg = trTPC.getSign() < 0 ? 1 : 0;
1295
1296 bool compatibleWithProton = false;
1297 if (!(mSVParams->mSkipTPCOnlyCascade)) {
1298 // Cascade retrieve dEdx proton frac
1299 const auto protonId = o2::track::PID::Proton;
1300 float dEdxTPC = trTPC.getdEdx().dEdxTotTPC;
1301 float dEdxExpected = mPIDresponse.getExpectedSignal(trTPC, protonId);
1302 float fracDevProton = std::abs((dEdxTPC - dEdxExpected) / dEdxExpected);
1303 if (fracDevProton < mSVParams->mFractiondEdxforCascBaryons) {
1304 compatibleWithProton = true;
1305 }
1306 }
1307
1308 auto& trLoc = mTracksPool[posneg].emplace_back(TrackCand{trTPC, gid, {vtxid, vtxid}, 0., true, -1, compatibleWithProton});
1309 auto err = correctTPCTrack(trLoc, trTPC, twe.getTimeStamp(), twe.getTimeStampError());
1310 if (err < 0) {
1311 mTracksPool[posneg].pop_back(); // discard
1312 return true;
1313 }
1314
1315 if (mSVParams->mTPCTrackPhotonTune) {
1316 // require minimum of tpc clusters
1317 bool dCls = trTPC.getNClusters() < mSVParams->mTPCTrackMinNClusters;
1318 // check track z cuts
1319 bool dDPV = std::abs(trLoc.getX() * trLoc.getTgl() - trLoc.getZ() + vtx.getZ()) > mSVParams->mTPCTrack2Beam;
1320 // check track transveres cuts
1321 float sna{0}, csa{0};
1323 trLoc.getCircleParams(mBz, trkCircle, sna, csa);
1324 float cR = std::hypot(trkCircle.xC, trkCircle.yC);
1325 float drd2 = std::sqrt(cR * cR - trkCircle.rC * trkCircle.rC);
1326 bool dRD2 = drd2 > mSVParams->mTPCTrackXY2Radius;
1327
1328 if (dCls || dDPV || dRD2) {
1329 mTracksPool[posneg].pop_back();
1330 return true;
1331 }
1332 }
1333
1334 return true;
1335}
1336
1337//______________________________________________
1338float SVertexer::correctTPCTrack(SVertexer::TrackCand& trc, const o2::tpc::TrackTPC& tTPC, float tmus, float tmusErr) const
1339{
1340 // Correct the track copy trc of the TPC track for the assumed interaction time
1341 // return extra uncertainty in Z due to the interaction time uncertainty
1342 // TODO: at the moment, apply simple shift, but with Z-dependent calibration we may
1343 // need to do corrections on TPC cluster level and refit
1344 // This is almosto clone of the MatchTPCITS::correctTPCTrack
1345
1346 float tTB, tTBErr;
1347 if (tmusErr < 0) { // use track data
1348 tTB = tTPC.getTime0();
1349 tTBErr = 0.5 * (tTPC.getDeltaTBwd() + tTPC.getDeltaTFwd());
1350 } else {
1351 tTB = tmus * mMUS2TPCBin;
1352 tTBErr = tmusErr * mMUS2TPCBin;
1353 }
1354 float dDrift = (tTB - tTPC.getTime0()) * mTPCBin2Z;
1355 float driftErr = tTBErr * mTPCBin2Z;
1356 if (driftErr < 0.) { // early return will be discarded anyway
1357 return driftErr;
1358 }
1359 // eventually should be refitted, at the moment we simply shift...
1360 trc.setZ(tTPC.getZ() + (tTPC.hasASideClustersOnly() ? dDrift : -dDrift));
1361 trc.setCov(trc.getSigmaZ2() + driftErr * driftErr, o2::track::kSigZ2);
1362 uint8_t sector, row;
1363 auto cl = &tTPC.getCluster(mTPCTrackClusIdx, tTPC.getNClusters() - 1, *mTPCClusterIdxStruct, sector, row);
1364 float x = 0, y = 0, z = 0;
1365 mTPCCorrMaps->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, tTB);
1368 }
1369 trc.minR = std::sqrt(x * x + y * y);
1370 LOGP(debug, "set MinR = {} for row {}, x:{}, y:{}, z:{}", trc.minR, row, x, y, z);
1371 return driftErr;
1372}
1373
1374//______________________________________________
1375std::array<size_t, 3> SVertexer::getNFitterCalls() const
1376{
1377 std::array<size_t, 3> calls{};
1378 for (int i = 0; i < mNThreads; i++) {
1379 calls[0] += mFitterV0[i].getCallID();
1380 calls[1] += mFitterCasc[i].getCallID();
1381 calls[2] += mFitter3body[i].getCallID();
1382 }
1383 return calls;
1384}
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:178
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)
static constexpr ID HyperHelium4
Definition PID.h:117
static constexpr ID Electron
Definition PID.h:94
static constexpr ID HyperTriton
Definition PID.h:113
static constexpr ID Lambda
Definition PID.h:112
static constexpr ID Helium3
Definition PID.h:101
static constexpr ID Kaon
Definition PID.h:97
static constexpr ID Pion
Definition PID.h:96
static constexpr ID HyperHelium5
Definition PID.h:118
static constexpr ID Deuteron
Definition PID.h:99
static constexpr ID K0
Definition PID.h:111
static constexpr ID OmegaMinus
Definition PID.h:116
static constexpr ID Photon
Definition PID.h:110
static constexpr ID Proton
Definition PID.h:98
static constexpr ID Triton
Definition PID.h:100
static constexpr ID XiMinus
Definition PID.h:115
static constexpr ID Hyperhydrog4
Definition PID.h:114
static constexpr ID Alpha
Definition PID.h:102
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 createFullV0s
fill V0s prongs/kinematics
float maxTglV0
maximum tgLambda of V0
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< int > row