1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
#include "CommonTools/RecoAlgos/interface/PrimaryVertexAssignment.h"
#include "DataFormats/ParticleFlowCandidate/interface/PFCandidate.h"
#include "DataFormats/VertexReco/interface/Vertex.h"
#include "TrackingTools/TransientTrack/interface/TransientTrackBuilder.h"
#include "DataFormats/Math/interface/deltaR.h"
#include "TrackingTools/IPTools/interface/IPTools.h"
#include "FWCore/Utilities/interface/isFinite.h"
std::pair<int, PrimaryVertexAssignment::Quality> PrimaryVertexAssignment::chargedHadronVertex(
const reco::VertexCollection& vertices,
const reco::TrackRef& trackRef,
const reco::Track* track,
float time,
float timeReso, // <0 if timing not available for this object
const edm::View<reco::Candidate>& jets,
const TransientTrackBuilder& builder) const {
typedef reco::VertexCollection::const_iterator IV;
typedef reco::Vertex::trackRef_iterator IT;
int iVertex = -1;
size_t index = 0;
float bestweight = 0;
for (auto const& vtx : vertices) {
float w = vtx.trackWeight(trackRef);
if (w > bestweight) {
bestweight = w;
iVertex = index;
}
index++;
}
return chargedHadronVertex(vertices, iVertex, track, time, timeReso, jets, builder);
}
std::pair<int, PrimaryVertexAssignment::Quality> PrimaryVertexAssignment::chargedHadronVertex(
const reco::VertexCollection& vertices,
int iVertex,
const reco::Track* track,
float time,
float timeReso, // <0 if timing not available for this object
const edm::View<reco::Candidate>& jets,
const TransientTrackBuilder& builder) const {
typedef reco::VertexCollection::const_iterator IV;
typedef reco::Vertex::trackRef_iterator IT;
bool useTime = useTiming_;
if (edm::isNotFinite(time) || timeReso < 1e-6) {
useTime = false;
time = 0.;
timeReso = -1.;
}
if (preferHighRanked_) {
for (IV iv = vertices.begin(); iv != vertices.end(); ++iv) {
int ivtx = iv - vertices.begin();
if (useVertexFit_ && (iVertex == ivtx))
return {ivtx, PrimaryVertexAssignment::UsedInFit};
double dz = std::abs(track->dz(iv->position()));
double dt = std::abs(time - iv->t());
bool useTimeVtx = useTime && iv->tError() > 0.;
if ((dz < maxDzForPrimaryAssignment_ or dz / track->dzError() < maxDzSigForPrimaryAssignment_) and
(!useTimeVtx or dt / timeReso < maxDtSigForPrimaryAssignment_)) {
return {ivtx, PrimaryVertexAssignment::PrimaryDz};
}
}
}
double firstVertexDz = std::numeric_limits<double>::max();
if (!vertices.empty())
firstVertexDz = std::abs(track->dz(vertices[0].position()));
// recover cases where the primary vertex is split
if (useVertexFit_ && (iVertex > 0) && (iVertex <= int(fNumOfPUVtxsForCharged_)) &&
firstVertexDz < fDzCutForChargedFromPUVtxs_)
return {0, PrimaryVertexAssignment::PrimaryDz};
if (useVertexFit_ && (iVertex >= 0))
return {iVertex, PrimaryVertexAssignment::UsedInFit};
double distmin = std::numeric_limits<double>::max();
double dzmin = std::numeric_limits<double>::max();
double dtmin = std::numeric_limits<double>::max();
int vtxIdMinSignif = -1;
for (IV iv = vertices.begin(); iv != vertices.end(); ++iv) {
double dz = std::abs(track->dz(iv->position()));
double dt = std::abs(time - iv->t());
double dzsig = dz / track->dzError();
double dist = dzsig * dzsig;
bool useTimeVtx = useTime && iv->tError() > 0.;
if (useTime && !useTimeVtx) {
dt = timeReso;
}
if (useTime) {
double dtsig = dt / timeReso;
dist += dtsig * dtsig;
}
if (dist < distmin) {
distmin = dist;
dzmin = dz;
dtmin = dt;
vtxIdMinSignif = iv - vertices.begin();
}
}
// protect high pT particles from association to pileup vertices and assign them to the first vertex
if ((fPtMaxCharged_ > 0) && (vtxIdMinSignif >= 0) && (track->pt() > fPtMaxCharged_)) {
iVertex = 0;
} else {
// first use "closest in Z" with tight cuts (targetting primary particles)
const float add_cov = vtxIdMinSignif >= 0 ? vertices[vtxIdMinSignif].covariance(2, 2) : 0.f;
const float dzE = sqrt(track->dzError() * track->dzError() + add_cov);
if (!fOnlyUseFirstDz_) {
if (vtxIdMinSignif >= 0 and
(dzmin < maxDzForPrimaryAssignment_ and dzmin / dzE < maxDzSigForPrimaryAssignment_ and
track->dzError() < maxDzErrorForPrimaryAssignment_) and
(!useTime or dtmin / timeReso < maxDtSigForPrimaryAssignment_))
iVertex = vtxIdMinSignif;
} else {
// consider only distances to first vertex for association (originally used in PUPPI)
if ((vtxIdMinSignif >= 0) && (std::abs(track->eta()) > fEtaMinUseDz_) &&
((firstVertexDz < maxDzForPrimaryAssignment_ && firstVertexDz / dzE < maxDzSigForPrimaryAssignment_ &&
track->dzError() < maxDzErrorForPrimaryAssignment_) &&
(!useTime || std::abs(time - vertices[0].t()) / timeReso < maxDtSigForPrimaryAssignment_)))
iVertex = 0;
}
}
if (iVertex >= 0)
return {iVertex, PrimaryVertexAssignment::PrimaryDz};
// if track not assigned yet, it could be a b-decay secondary , use jet axis dist criterion
// first find the closest jet within maxJetDeltaR_
int jetIdx = -1;
double minDeltaR2 = maxJetDeltaR_ * maxJetDeltaR_;
for (edm::View<reco::Candidate>::const_iterator ij = jets.begin(); ij != jets.end(); ++ij) {
if (ij->pt() < minJetPt_)
continue; // skip jets below the jet Pt threshold
double deltaR2 = reco::deltaR2(*ij, *track);
if (deltaR2 < minDeltaR2 and track->dzError() < maxDzErrorForPrimaryAssignment_) {
minDeltaR2 = deltaR2;
jetIdx = std::distance(jets.begin(), ij);
}
}
// if jet found
if (jetIdx != -1) {
reco::TransientTrack transientTrack = builder.build(*track);
GlobalVector direction(jets.at(jetIdx).px(), jets.at(jetIdx).py(), jets.at(jetIdx).pz());
// find the vertex with the smallest distanceToJetAxis that is still within maxDistaneToJetAxis_
int vtxIdx = -1;
double minDistanceToJetAxis = maxDistanceToJetAxis_;
for (IV iv = vertices.begin(); iv != vertices.end(); ++iv) {
// only check for vertices that are close enough in Z and for tracks that have not too high dXY
if (std::abs(track->dz(iv->position())) > maxDzForJetAxisAssigment_ ||
std::abs(track->dxy(iv->position())) > maxDxyForJetAxisAssigment_)
continue;
double distanceToJetAxis = IPTools::jetTrackDistance(transientTrack, direction, *iv).second.value();
if (distanceToJetAxis < minDistanceToJetAxis) {
minDistanceToJetAxis = distanceToJetAxis;
vtxIdx = std::distance(vertices.begin(), iv);
}
}
if (vtxIdx >= 0) {
iVertex = vtxIdx;
}
}
if (iVertex >= 0)
return {iVertex, PrimaryVertexAssignment::BTrack};
// if the track is not compatible with other PVs but is compatible with the BeamSpot, we may simply have not reco'ed the PV!
// we still point it to the closest in Z, but flag it as possible orphan-primary
if (!vertices.empty() && std::abs(track->dxy(vertices[0].position())) < maxDxyForNotReconstructedPrimary_ &&
std::abs(track->dxy(vertices[0].position()) / track->dxyError()) < maxDxySigForNotReconstructedPrimary_)
return {vtxIdMinSignif, PrimaryVertexAssignment::NotReconstructedPrimary};
//FIXME: here we could better handle V0s and NucInt
// all other tracks could be non-B secondaries and we just attach them with closest Z
if (vtxIdMinSignif >= 0)
return {vtxIdMinSignif, PrimaryVertexAssignment::OtherDz};
//If for some reason even the dz failed (when?) we consider the track not assigned
return {-1, PrimaryVertexAssignment::Unassigned};
}
|