CbmRoot
Loading...
Searching...
No Matches
KfpV0Finder.cxx
Go to the documentation of this file.
1/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Sergei Zharko [committer] */
4
9
10#include "kfp/KfpV0Finder.h"
11
12#include "global/RecoResults.h"
13
14#include <algorithm>
15#include <limits>
16#include <sstream>
17
20
21
22// ---------------------------------------------------------------------------------------------------------------------
23//
25 const std::vector<RecoResults::HitId_t>& tofHitIds, double t0, ParticleInfo& particleInfo)
26{
27 if (tofHitIds.empty()) {
29 return false;
30 }
31
32 double beta{0.};
33 if constexpr (kUseAverageSpeed) {
34 for (const auto& hitId : tofHitIds) {
35 beta += EstimateBeta(tofHits[hitId.first][hitId.second], t0);
36 }
37 beta /= tofHitIds.size();
38 }
39 else {
40 const auto& hitId = tofHitIds.back();
41 beta = EstimateBeta(tofHits[hitId.first][hitId.second], t0);
42 }
43 if (beta < 0.) {
45 return false;
46 }
47 else if (beta > 1.) {
49 return false;
50 }
51 double gamma{1. / sqrt(1. - beta * beta)};
52 particleInfo.fBeta = beta;
53 particleInfo.fQp = particleInfo.fCharge / (gamma * beta * particleInfo.fMass);
54 return true;
55}
56
57// ---------------------------------------------------------------------------------------------------------------------
58//
60{
61 if (std::isnan(particleInfo.fDca)) {
62 particleInfo.fPdg = kUndefPdg;
64 }
65 else if (particleInfo.fDca > fMinPionDca) {
66 // pi-
67 particleInfo.fPdg = -211;
68 particleInfo.fMass = kPionMass;
69 particleInfo.fCharge = -1;
71 }
72 else if (particleInfo.fDca > fMinProtonDca) {
73 // proton
74 particleInfo.fPdg = 2212;
75 particleInfo.fMass = kProtonMass;
76 particleInfo.fCharge = 1;
78 }
79 else {
80 // primary
81 //particleInfo.fPdg = fPrimaryAssignedPdg;
82 particleInfo.fPdg = kUndefPdg;
84 }
85}
86
87// ---------------------------------------------------------------------------------------------------------------------
88//
89void V0Finder::CollectDca(const RecoResults& recoEvent)
90{
91 const auto& stsHitIndices = recoEvent.trackStsHitIndices;
92 for (size_t iTrk = 0; iTrk < stsHitIndices.size(); ++iTrk) {
93 const auto& stsHitIndicesInTrack = stsHitIndices[iTrk];
94 if (stsHitIndicesInTrack.size() < 2) { // DCA cannot be estimated
96 continue;
97 }
98 auto& particleInfo = fvParticleInfo[iTrk];
99 auto [iPtFst, iHitFst] = stsHitIndicesInTrack[0];
100 auto [iPtSnd, iHitSnd] = stsHitIndicesInTrack[1];
101 particleInfo.fDca = EstimateDca(recoEvent.stsHits[iPtFst][iHitFst], recoEvent.stsHits[iPtSnd][iHitSnd]);
102 AssignPid(particleInfo);
103 }
104}
105
106// ---------------------------------------------------------------------------------------------------------------------
107//
108void V0Finder::CollectT0(gsl::span<const bmon::Hit> bmonHits)
109{
110 fvT0s.clear();
111 fvT0s.reserve(bmonHits.size());
112 std::transform(bmonHits.begin(), bmonHits.end(), std::back_inserter(fvT0s),
113 [&](const auto& h) { return h.GetTime(); });
114}
115
116// ---------------------------------------------------------------------------------------------------------------------
117//
118double V0Finder::EstimateDca(const sts::Hit& fst, const sts::Hit& snd) const
119{
120 double factor{(fst.Z() - fOrigin[2]) / (snd.Z() - fst.Z())};
121 double dcaX{fst.X() - fOrigin[0] - factor * (snd.X() - fst.X())};
122 double dcaY{fst.Y() - fOrigin[1] - factor * (snd.Y() - fst.Y())};
123 return std::sqrt(dcaX * dcaX + dcaY * dcaY);
124}
125
126// ---------------------------------------------------------------------------------------------------------------------
127//
128double V0Finder::EstimateBeta(const tof::Hit& hit, double t0) const
129{
130 double t = hit.Time() - t0 - fTzeroOffset;
131 double x{hit.X() - fOrigin[0]};
132 double y{hit.Y() - fOrigin[1]};
133 double z{hit.Z() - fOrigin[2]};
134 double x2{x * x};
135 double y2{y * y};
136 double z2{z * z};
137 double r2{x2 + y2 + z2};
138 return std::sqrt(r2) / (t * kSpeedOfLight);
139}
140
141// ---------------------------------------------------------------------------------------------------------------------
142//
143bool V0Finder::FindV0Candidates(const RecoResults& recoEvent, double t0)
144{
145 const auto& tracks = recoEvent.tracks;
146
147 // Reset temporary data structures
149 fpTopoReconstructor->Clear();
150 fvSelectedTrackIds.clear();
151 fvSelectedTrackIds.reserve(tracks.size());
157
158 // Preselect tracks
159 uint32_t nProtonCandidates{0};
160 uint32_t nPionCandidates{0};
161 uint32_t nSelectedTracks{0};
163 for (size_t iTrk = 0; iTrk < tracks.size(); ++iTrk) { // Over all tracks
164 auto& particleInfo = fvParticleInfo[iTrk];
165
166 // Cut tracks with undefined dca (by PID == -2)
167 // NOTE: if fPdg == kUndefPdg, beta and QP were not estimated for this track on previous iterations as well
168 if (particleInfo.fPdg == kUndefPdg) {
169 continue;
170 }
171
172 // Reset fields of the ParticleInfo, which could be filled on the previous iteration
173 particleInfo.fBeta = std::numeric_limits<double>::quiet_NaN();
174 particleInfo.fQp = std::numeric_limits<double>::quiet_NaN();
175 particleInfo.fbSelected = false;
176
177 // Assign momentum to tracks
178 if (!AssignMomentum(recoEvent.tofHits, recoEvent.trackTofHitIndices[iTrk], t0, particleInfo)) {
180 continue; // No momentum was assigned
181 }
182
183 // Select tracks
184 if (!SelectTrack(particleInfo)) {
185 continue;
186 }
187 fvSelectedTrackIds.push_back(iTrk);
188 particleInfo.fbSelected = true;
189
190 // Update track parameters
191 double qpVar{particleInfo.fQp * fQpAssignedUncertainty};
192 qpVar = qpVar * qpVar;
193 auto& trkParam{fvTrackParam[iTrk]};
194 trkParam.first.SetQp(particleInfo.fQp);
195 trkParam.first.SetC44(qpVar);
196 trkParam.second.SetQp(particleInfo.fQp);
197 trkParam.second.SetC44(qpVar);
198
199 switch (particleInfo.fPdg) {
200 case -211: ++nPionCandidates; break;
201 case 2212: ++nProtonCandidates; break;
202 }
203 ++nSelectedTracks;
204 }
206 if (!nPionCandidates || !nProtonCandidates) {
207 return false; // no Lambda can be found
208 }
213
214 // Initialize and run the KFParticleFinder
216 KFPTrackVector kfpTracksFst;
217 KFPTrackVector kfpTracksLst;
218 kfpTracksFst.Resize(nSelectedTracks);
219 kfpTracksLst.Resize(nSelectedTracks);
220 for (uint32_t iKfpTrk = 0; iKfpTrk < fvSelectedTrackIds.size(); ++iKfpTrk) { // Over selected tracks
221 uint32_t iCaTrk{fvSelectedTrackIds[iKfpTrk]};
222 const auto& trkParam{fvTrackParam[iCaTrk]};
223 const auto& particleInfo{fvParticleInfo[iCaTrk]};
224 SetKfpTrackParameters(kfpTracksFst, iKfpTrk, iCaTrk, trkParam.first, particleInfo);
225 SetKfpTrackParameters(kfpTracksLst, iKfpTrk, iCaTrk, trkParam.second, particleInfo);
226 }
227 fpTopoReconstructor->Init(kfpTracksFst, kfpTracksLst);
229 fpTopoReconstructor->SortTracks();
232 fpTopoReconstructor->ReconstructParticles();
234 const auto& particles{fpTopoReconstructor->GetParticles()};
235
236 // Scan for Lambda-candidates
237 uint32_t nLambdaCandidates =
238 std::count_if(particles.begin(), particles.end(), [&](const auto& p) { return p.GetPDG() == 3122; });
240
241 return static_cast<bool>(nLambdaCandidates);
242}
243
244// ---------------------------------------------------------------------------------------------------------------------
245//
246void V0Finder::Init() { fpTopoReconstructor->SetTarget({float(fOrigin[0]), float(fOrigin[1]), float(fOrigin[2])}); }
247
248// ---------------------------------------------------------------------------------------------------------------------
249//
251{
252 fvTrackParam.clear();
253 fvTrackParam.reserve(tracks.size());
254 std::transform(tracks.begin(), tracks.end(), std::back_inserter(fvTrackParam),
255 [&](const auto& t) { return std::make_pair(t.fParFirst, t.fParLast); });
256}
257
258// ---------------------------------------------------------------------------------------------------------------------
259//
260KFVertex V0Finder::MakeKfpPrimaryVertex(const std::array<float, 3>& r)
261{
262 KFVertex kfVertex;
263 kfVertex.X() = r[0];
264 kfVertex.Y() = r[1];
265 kfVertex.Z() = r[2];
266 for (int iC = 0; iC < 6; ++iC) {
267 kfVertex.Covariance(iC) = 0.f;
268 }
269 kfVertex.Chi2() = -100.f;
270 return kfVertex;
271}
272
273// ---------------------------------------------------------------------------------------------------------------------
274//
276{
277 //L_(info) << "----------------------------- EVENT --------------";
283
284 // ----- Initialize data-structures
285 fvParticleInfo.clear();
286 fvParticleInfo.resize(recoEvent.tracks.size());
287
288 // ----- Define T0
289 // So far we cannot preselect a hit from multiple ones, we will be using all of them iteratively to find lambdas
292 if (fvT0s.empty()) {
294 return res;
295 }
297
298 // ----- Estimate DCA of tracks and assign PID
299 // If a track has less then two STS hits, and undefined DCA value is stored
301 CollectDca(recoEvent);
303
304 // ----- Try to find lambdas for different T0
305 fSelectedT0 = std::numeric_limits<double>::quiet_NaN();
307 for (double t0 : fvT0s) {
308 if (FindV0Candidates(recoEvent, t0)) {
309 fSelectedT0 = t0;
312 break; // Lambda-candidates were found, there is no sense to scan further
313 }
314 }
317 return res;
318}
319
320// ---------------------------------------------------------------------------------------------------------------------
321//
322bool V0Finder::SelectTrack(const ParticleInfo& particleInfo) const
323{
324 // Speed cut
325 if (particleInfo.fPdg == -211) {
326 if (particleInfo.fBeta < fMinBetaPion || particleInfo.fBeta > fMaxBetaPion) {
327 return false;
328 }
329 }
330 else if (particleInfo.fPdg == 2212) {
331 if (particleInfo.fBeta < fMinBetaProton || particleInfo.fBeta > fMaxBetaProton) {
332 return false;
333 }
334 }
335 return true;
336}
337
338// ---------------------------------------------------------------------------------------------------------------------
339//
340void V0Finder::SetKfpTrackParameters(KFPTrackVector& trackVector, uint32_t iKfpTrk, uint32_t iCaTrk,
341 const ca::Track::TrackParam_t& trkParam, const ParticleInfo& particleInfo) const
342{
343 // ----- Parameter definition
344 double tx{trkParam.GetTx()};
345 double ty{trkParam.GetTy()};
346 double qp{trkParam.GetQp()};
347 double p{particleInfo.fCharge / qp};
348 double p2{p * p};
349 double t2inv{1. / (1. + tx * tx + ty * ty)};
350 double pz{std::sqrt(t2inv * p2)};
351 double px{tx * pz};
352 double py{ty * pz};
353
354 trackVector.SetParameter(trkParam.GetX(), 0, iKfpTrk);
355 trackVector.SetParameter(trkParam.GetY(), 1, iKfpTrk);
356 trackVector.SetParameter(trkParam.GetZ(), 2, iKfpTrk);
357 trackVector.SetParameter(px, 3, iKfpTrk);
358 trackVector.SetParameter(py, 4, iKfpTrk);
359 trackVector.SetParameter(pz, 5, iKfpTrk);
360
361 // Jacobian matrix for (tx, ty, qp) -> (px, py, pz)
362 std::array<std::array<double, 3>, 3> Jp;
363 Jp[2][0] = -t2inv * px; // d(pz)/d(tx)
364 Jp[2][1] = -t2inv * py; // d(pz)/d(ty)
365 Jp[2][2] = -pz / qp; // d(pz)/d(qp)
366 Jp[0][0] = tx * Jp[2][0] + pz; // d(px)/d(tx)
367 Jp[0][1] = tx * Jp[2][1]; // d(px)/d(ty)
368 Jp[0][2] = tx * Jp[2][2]; // d(px)/d(qp)
369 Jp[1][0] = ty * Jp[2][0]; // d(py)/d(tx)
370 Jp[1][1] = ty * Jp[2][1] + pz; // d(py)/d(ty)
371 Jp[1][2] = ty * Jp[2][2]; // d(py)/d(qp)
372
373
374 // ----- Covariance matrix definition
375 // Position covariance
376 trackVector.SetCovariance(trkParam.C00(), 0, iKfpTrk); // var(x)
377 trackVector.SetCovariance(trkParam.C01(), 1, iKfpTrk); // cov(x, y)
378 trackVector.SetCovariance(trkParam.C11(), 2, iKfpTrk); // var(y)
379
380 // Momentum-position covariances
381 auto MomPosCovariance = [&](const int k, const int l) constexpr->double
382 {
383 double val{0.};
384 const auto& JpA = Jp[k];
385 for (int i = 0; i < 3; ++i) {
386 val += JpA[i] * trkParam.C(i + 2, l);
387 }
388 return val;
389 };
390 trackVector.SetCovariance(MomPosCovariance(0, 0), 6, iKfpTrk); // cov(x, px)
391 trackVector.SetCovariance(MomPosCovariance(0, 1), 7, iKfpTrk); // cov(y, px)
392 trackVector.SetCovariance(MomPosCovariance(1, 0), 10, iKfpTrk); // cov(x, py)
393 trackVector.SetCovariance(MomPosCovariance(1, 1), 11, iKfpTrk); // cov(y, py)
394 trackVector.SetCovariance(MomPosCovariance(2, 0), 15, iKfpTrk); // cov(x, pz)
395 trackVector.SetCovariance(MomPosCovariance(2, 1), 16, iKfpTrk); // cov(y, pz)
396
397 // Momentum covariances
398 auto MomentumCovariance = [&](const int k, const int l) constexpr->double
399 {
400 double val{0.};
401 const auto& JpA = Jp[k];
402 const auto& JpB = Jp[l];
403 for (int i = 0; i < 3; ++i) {
404 double factor{0.};
405 for (int j = 0; j < 3; ++j) {
406 factor += JpB[j] * trkParam.C(i + 2, j + 2);
407 }
408 val += JpA[i] * factor;
409 }
410 return val;
411 };
412 trackVector.SetCovariance(MomentumCovariance(0, 0), 9, iKfpTrk); // var(px)
413 trackVector.SetCovariance(MomentumCovariance(1, 0), 13, iKfpTrk); // cov(px, py)
414 trackVector.SetCovariance(MomentumCovariance(1, 1), 14, iKfpTrk); // var(py)
415 trackVector.SetCovariance(MomentumCovariance(2, 0), 18, iKfpTrk); // cov(px, pz)
416 trackVector.SetCovariance(MomentumCovariance(2, 1), 19, iKfpTrk); // cov(py, pz)
417 trackVector.SetCovariance(MomentumCovariance(2, 2), 20, iKfpTrk); // var(pz)
418
419 // Zero covariances (with z-coordinate)
420 trackVector.SetCovariance(0.f, 3, iKfpTrk); // cov(x,z)
421 trackVector.SetCovariance(0.f, 4, iKfpTrk); // cov(y,z)
422 trackVector.SetCovariance(0.f, 5, iKfpTrk); // var(z)
423 trackVector.SetCovariance(0.f, 8, iKfpTrk); // cov(z,px)
424 trackVector.SetCovariance(0.f, 12, iKfpTrk); // cov(z,py)
425 trackVector.SetCovariance(0.f, 17, iKfpTrk); // var(z,pz)
426
427 // ----- Other quantities
428 // Magnetic field (NOTE: zero fom mCBM)
429 // FIXME: Provide a proper initialization for full CBM
430 for (int iF = 0; iF < 10; ++iF) {
431 trackVector.SetFieldCoefficient(0.f, iF, iKfpTrk);
432 }
433
434 trackVector.SetId(iCaTrk, iKfpTrk);
435 trackVector.SetPDG(particleInfo.fPdg, iKfpTrk);
436 trackVector.SetQ(particleInfo.fCharge, iKfpTrk);
437 trackVector.SetNPixelHits(0, iKfpTrk);
438
439 // NOTE: 0 - primary tracks, -1 - secondary tracks. Here for now we assign ALL tracks as secondary
440 trackVector.SetPVIndex(-1, iKfpTrk);
441}
TClonesArray * tracks
friend fvec sqrt(const fvec &a)
A V0 finding algorithm.
A structure for reconstructed results: digi-events, hits and tracks.
Class to store different triggers for a given event.
@ Lambda
Lambda-trigger.
void Set(ETrigger key)
Sets a trigger.
Data class with information on a STS local track.
A vector that is partitioned into multiple subvectors.
void Reset()
Resets all the counters and timers.
void StopTimer(ETimerKey key)
Stops timer.
void IncrementCounter(ECounterKey key)
Increments key counter by 1.
void StartTimer(ETimerKey key)
Starts timer.
void ResetCounter(ECounterKey key)
Resets a particular counter.
T C00() const
Individual getters for covariance matrix elements.
T GetTy() const
Gets slope along y-axis.
T GetZ() const
Gets z position [cm].
T GetTx() const
Gets slope along x-axis.
T GetQp() const
Gets charge over momentum [ec/GeV].
T GetY() const
Gets y position [cm].
T GetX() const
Gets x position [cm].
TrackParam classes of different types.
A V0-finding algorithm.
Definition KfpV0Finder.h:25
double EstimateBeta(const tof::Hit &tofHit, double t0) const
Estimates speed of particle, using TOF measurement.
static constexpr bool kUseAverageSpeed
If an average speed of tof hits is used.
double fMinProtonDca
Minimum DCA to PV for protons.
void InitTrackParamVectors(const ca::Vector< ca::Track > &tracks)
Initializes copies of track parameter vectors.
CbmEventTriggers Process(const RecoResults &recoEvent)
Processes a reconstructed data sample, returns a collection of fired triggers.
void SetKfpTrackParameters(KFPTrackVector &kfpTrkVector, uint32_t iKfpTrk, uint32_t iCaTrk, const ca::Track::TrackParam_t &trkParam, const ParticleInfo &particleInfo) const
Sets KFP track parameters.
double fQpAssignedUncertainty
Assigned relative uncertainty for q/p estimation.
static constexpr int32_t kUndefPdg
PDG for tracks, which PID cannot be inferred.
Definition KfpV0Finder.h:43
double fTzeroOffset
Offset for T0.
bool SelectTrack(const ParticleInfo &particleInfo) const
Applies selection cut on the track.
std::vector< std::pair< ca::Track::TrackParam_t, ca::Track::TrackParam_t > > fvTrackParam
A copy of track parameters (first, last)
double fMinPionDca
Minimum DCA to PV for pions.
std::array< float, 3 > fOrigin
Coordinates of origin [cm].
double fMinBetaPion
Minimal proton velocity (beta) [c].
double EstimateDca(const sts::Hit &fst, const sts::Hit &snd) const
Estimate DCA of a track to origin.
void CollectDca(const RecoResults &recoEvent)
Collects a vector of DCA.
int fBmonPartitionIndex
Index of selected partition in BMON hit vector.
bool AssignMomentum(const PartitionedVector< tof::Hit > &tofHits, const std::vector< RecoResults::HitId_t > &tofHitIds, double t0, ParticleInfo &pidInfo)
Assigns momentum based on the TOF measurement.
std::vector< double > fvT0s
Found t0s [ns] (in event)
void AssignPid(ParticleInfo &info)
Assigns PID info based on the estimated DCA.
double fSelectedT0
A t0 value selected by the lambda-finder.
std::vector< uint32_t > fvSelectedTrackIds
IDs of selected tracks (in event)
void CollectT0(gsl::span< const bmon::Hit > bmonHits)
Collects T0 values among the BMON hits.
double fMaxBetaPion
Maximal proton velocity (beta) [c].
double fMaxBetaProton
Maximal proton velocity (beta) [c].
double fMinBetaProton
Minimal proton velocity (beta) [c].
static KFVertex MakeKfpPrimaryVertex(const std::array< float, 3 > &r)
Makes a KF vertex.
V0FinderMonitorData_t fEventMonitor
Main monitor data instance.
std::vector< ParticleInfo > fvParticleInfo
PID info of tracks (in event)
void Init()
Initializes the instance (called in the beginning of the run)
bool FindV0Candidates(const RecoResults &recoEvent, double t0)
Tries to find V0-candidates for a given t0.
std::unique_ptr< KFParticleTopoReconstructor > fpTopoReconstructor
An instance of the topology reconstructor.
static constexpr double kProtonMass
Proton mass [GeV/c2].
Definition KfpV0Finder.h:41
static constexpr double kSpeedOfLight
Speed of light [cm/ns].
Definition KfpV0Finder.h:42
static constexpr double kPionMass
Pion mass [GeV/c2].
Definition KfpV0Finder.h:40
@ Protons
Number of proton-candidates.
@ PionsDca
Number of raw pion-candidates.
@ Pions
Number of pion-candidates.
@ KfpEventsLambdaCand
Events with lambda-candidates in KF-particle.
@ PrimaryDca
Number of raw proton-candidates.
@ TracksWUnphysicalBeta
Tracks with beta > 1.
@ TracksWoStsHits
Tracks, which have no STS hits.
@ EventsTotal
Total number of events.
@ TracksWoPid
Tracks, which has undefined PID.
@ ProtonsDca
Number of raw proton-candidates.
@ TracksWoMomentum
Tracks, which has no momentum.
@ TracksSelected
Tracks, which satisfy topology PID applicability.
@ TracksWoTofHits
Tracks, which have no TOF hits.
@ EventsLambdaCand
Events with at least one pion and one proton candidate.
@ TracksWNegativeTofHitTime
Tracks, the last TOF hit of which has a negative time (it's time is less then the t0)
@ EventsWoTzero
Number of events with undefined t-zero.
@ KfpLambdaCandidates
Number of lambda-candidates.
@ TracksTotal
Total number of tracks.
@ ExecKfp
Run KFParticleFinder inside the event.
@ FindV0Candidates
V0-finder procedure for a given t0.
@ ProcessEvent
Processing of a single event.
@ PreselectTracks
Track preselection.
@ CollectT0
Collecting T0s.
@ CollectDca
Estimating DCAs.
@ InitKfp
Init KFParticleFinder inside the event.
ca::Vector< std::vector< HitId_t > > trackTofHitIndices
Definition RecoResults.h:51
ca::Vector< ca::Track > tracks
Definition RecoResults.h:49
PartitionedVector< tof::Hit > tofHits
Definition RecoResults.h:45
PartitionedVector< bmon::Hit > bmonHits
Definition RecoResults.h:47
PartitionedSpan< sts::Hit > stsHits
Definition RecoResults.h:44
ca::Vector< std::vector< HitId_t > > trackStsHitIndices
Definition RecoResults.h:50
A structure to keep temporary PID information for tracks.
Definition KfpV0Finder.h:29
double Y() const
double X() const
double Z() const