ATLAS Offline Software
Loading...
Searching...
No Matches
TrackFitter.cxx
Go to the documentation of this file.
1
2// Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3
5
7#include "GaudiKernel/MsgStream.h"
9
10#include <numeric> //std::accumulate
11#include <memory>
12#include <stdexcept>
13
14std::vector<FPGATrackSimTrack>::const_iterator getBestChi2(std::vector<FPGATrackSimTrack> const & tracks);
15bool hasGoodFit(std::vector<FPGATrackSimTrack> const & track_cands, float minchi2);
16
17
19// Constructor
21
23 const std::vector<const FPGATrackSimFitConstantBank*>& droppedLayerbanks, bool guessingHits) :
24 AthMessaging (Athena::getMessageSvc(), "FPGATrackSimTrackFitter")
25{
26 m_guessinghits = guessingHits;
27 m_nominalBank = nominalbank;
28
29 if (!m_nominalBank) ATH_MSG_FATAL("Constant bank not set");
30 if (!m_guessinghits)
31 for (auto bank : droppedLayerbanks)
32 m_droppedLayerBanks.push_back(bank);
33}
34
35
37{
38 m_idbase = 0;
39 m_nfits = 0;
40 m_nfits_maj = 0;
43 m_nfits_rec = 0;
45}
46
47
49// fitTracks
51
52
53int TrackFitter::fitTracks(const std::vector<FPGATrackSimRoad>& roads, std::vector<FPGATrackSimTrack>& tracks) {
55 for (const FPGATrackSimRoad& cur_road : roads) {
56 std::vector<FPGATrackSimTrack> t;
57 int isOK = fitTracks(cur_road, t);
58 if (isOK != FITTRACKS_OK) return isOK;
59 else tracks.insert(tracks.end(), t.begin(), t.end());
60 }
61 return FITTRACKS_OK;
62}
63
64
71 int TrackFitter::fitTracks(const FPGATrackSimRoad &road, std::vector<FPGATrackSimTrack>& tracks)
72{
74
75 double y = 0.0;
76 double x = 0.0;
78 y = road.getY();
79 x = road.getX();
80 ATH_MSG_DEBUG("Attempting to fit Hough road with y = " << y << ", x = " << x << ", sector = " << road.getSector() << "and nhits = " << road.getNHits());
81 }
82
83 int sector = 0;
85 // Error checking
86 sector = road.getSector();
87 if (sector < 0) {
88 ATH_MSG_DEBUG("Bad sector " << sector);
89 return FITTRACKS_OK;
90 }
91 else if (sector >= m_nominalBank->getNSectors()) {
92 ATH_MSG_WARNING("Constants for sector " << sector << " don't exist");
93 return FITTRACKS_BAD;
94 }
95 else if (!m_nominalBank->getIsGood(sector)) {
96 ATH_MSG_WARNING("Constants for sector " << sector << " are not valid");
97 return FITTRACKS_BAD;
98 }
99 }
100
101 // Get info on layers with missing hits
102 int nMissing;
103 bool missPixel;
104 bool missStrip;
105 layer_bitmask_t missing_mask;
106 layer_bitmask_t norecovery_mask; // mask to prevent majority in planes with multiple hits
107 getMissingInfo(road, nMissing, missPixel, missStrip, missing_mask, norecovery_mask);
108 // Create a template track with common parameters filled already for initializing below
110 if(!m_do2ndStage){
111 temp.setTrackStage(TrackStage::FIRST);
112 if (!m_fitFromRoad) temp.setFirstSectorID(road.getSector());
113 }
114 else{
115 temp.setTrackStage(TrackStage::SECOND);
116 temp.setSecondSectorID(road.getSector());
117 }
118 temp.setNLayers(m_pmap->getNLogiLayers());
119 temp.setBankID(-1); // TODO
120 temp.setPatternID(road.getPID());
121 temp.setHitMap(missing_mask);
122 temp.setNMissing(nMissing);
123 temp.setHoughX(x);
124 temp.setHoughY(y);
125 temp.setQOverPt(y);
126 temp.setTrackCorrType(m_IdealCoordFitType);
127 temp.setDoDeltaGPhis(m_doDeltaGPhis);
128
129 temp.setSubRegion(road.getSubRegion());
130 temp.setHoughXBin(road.getXBin());
131 temp.setHoughYBin(road.getYBin());
132
133 temp.setBinIdx(road.getBinIdx());
134
135 // Create a list of track candidates by taking all possible combinations of hits in road.
136 std::vector<FPGATrackSimTrack> track_cands;
137
138 // This may not even need to be a class member. Create the vector of possible combinations.
139 std::vector<std::vector<int>> comboIndices = getComboIndices(road.getNHits_layer());
140 ATH_MSG_DEBUG("There are theoretically " << comboIndices.size() << " combinations to process for this road");
141 size_t nFits = 0;
142 for (size_t icomb = 0; icomb < comboIndices.size(); icomb++) {
143 FPGATrackSimTrack track_cand = makeTrackCandidate(road, temp, comboIndices[icomb]);
144
145 // Before we start, make sure this track candidate has not been marked as invalid
146 // due to combinatorics issues with spacepoints.
147 bool ok = track_cand.isValidCand();
148 if (!ok) {
149 continue;
150 }
151
152 // If the "fit from road" flag is set, then just assign track chi2 and parameters from that.
153 if (!m_do2ndStage && m_fitFromRoad) {
154 // Then actually do it using the road values.
155 track_cand.setChi2(road.getFitChi2());
156 track_cand.setChi2Phi(road.getFitChi2Phi());
157 track_cand.setChi2Eta(road.getFitChi2Eta());
158 track_cand.setPars(road.getFitParams());
159 ATH_MSG_DEBUG("Assigned chi2 = " << track_cand.getChi2() << " and parameters from genscan tool");
160 ATH_MSG_DEBUG("Set q/pt = " << track_cand.getQOverPt());
161 ATH_MSG_DEBUG("Set d0 = " << track_cand.getD0());
162 ATH_MSG_DEBUG("Set z0 = " << track_cand.getZ0());
163 ATH_MSG_DEBUG("Set eta = " << track_cand.getEta());
164 ATH_MSG_DEBUG("Set phi = " << track_cand.getPhi());
165 tracks.push_back(track_cand);
166 continue;
167 } else {
168
169 if (nMissing == 0 || m_guessinghits)
170 {
171 ok = m_nominalBank->linfit(sector, track_cand, m_do2ndStage);
172 }
173 else
174 {
175 unsigned int ic = 0; // this will be the leading coordinate with a missing hit
176 for (unsigned int ic = 0; ic < m_pmap->getNCoords(); ic++) {
177 if (!( (missing_mask >> ic) & 0x1)) break; // if we found one, stop
178 }
179 // banks are indexed by plane, not by coordinate
180 ok = m_droppedLayerBanks[m_pmap->getCoordLayer(ic)]->linfit(sector, track_cand, m_do2ndStage);
181 }
182
183 if (!ok) {
184 continue;
185 }
186 track_cand.setOrigChi2(track_cand.getChi2());
187 if (getDoMissingHitsCheck()) {
188 if (track_cand.getNMissing() == 0) { // missing 0 hits, drop hits one at a time and refit!
189 for (unsigned icoord = 0; icoord < m_pmap->getNCoords(); icoord++) { // 9 coordinates to refit, nominally
190
191 FPGATrackSimTrack newtrack = track_cand;
192 newtrack.setNMissing(1);
193 unsigned int bitmask = ( 1 << m_pmap->getNCoords() ) - 1; // all bits set to 1
194 bitmask &= ~(1 << icoord); // remove this coordinate
195 if (m_pmap->getDim(icoord) == 2) { // if we need to do two coordinates at once
196 bitmask &= ~(1 << (icoord+1)); // remove the next coordinate, too
197 newtrack.setNMissing(2);
198 }
199 newtrack.setHitMap(bitmask); // update bitmask
200 m_nominalBank->linfit(sector, newtrack, m_do2ndStage);
201 m_tracks_missinghits_track.push_back(newtrack);
202 if (m_pmap->getDim(icoord) == 2) {
203 icoord++; // skip 2nd of pixel coordinates so we don't do them twice
204 }
205 }
206 }
207 }
208 }
209 tracks.push_back(track_cand);
210
211 // Enforce m_max_ncomb here, but only for the tracks that we actually fit.
212 // Let's see how often this warning fires.
213 nFits += 1;
214 if (nFits > (size_t)m_max_ncomb) {
215 ATH_MSG_WARNING("Reached " << m_max_ncomb << " fits when processing track combinations for single road, breaking");
216 }
217 }
218 ATH_MSG_DEBUG("Processed " << tracks.size() << " actual/valid tracks for this road");
219
220 // Increment counters
221 m_nfits += nFits;
222 if (nMissing > 0)
223 {
224 m_nfits_maj += nFits;
225 if (missPixel) m_nfits_maj_pix += nFits;
226 if (missStrip) m_nfits_maj_SCT += nFits;
227 }
228
229 // Do recovery fits
230 if ((nMissing == 0) && (!m_fitFromRoad)) {
231 // In the case of m_do_majority > 1, we only do majority fits if ALL full fits fail the chi2 cut
232 if (m_do_majority == 1 || (m_do_majority > 1 && !hasGoodFit(tracks, m_Chi2Dof_recovery_min)))
233 {
234 for (FPGATrackSimTrack& t : tracks)
235 if (t.getChi2ndof() > m_Chi2Dof_recovery_min && t.getChi2ndof() < m_Chi2Dof_recovery_max) {
236 double y(0);
238 y = road.getY();
239 t = recoverTrack(t, norecovery_mask, sector, y);
240
241 }
242 }
243 }
244
245 // Add truth info
246 for (FPGATrackSimTrack & t : tracks) {
247 compute_truth(t); // match the track to a geant particle using the channel-level geant info in the hit data.
248 }
249
250 return FITTRACKS_OK;
251}
252
253
255// fitTracks core helper functions
257
258
259// Given road, populates the supplied variables with info on which layers missed hits
260void TrackFitter::getMissingInfo(const FPGATrackSimRoad & road, int & nMissing, bool & missPixel, bool & missStrip,
261 layer_bitmask_t & missing_mask, layer_bitmask_t & norecovery_mask)
262{
263 nMissing = m_pmap->getNCoords(); // init with nCoords and decrement as we find misses
264 missPixel = false;
265 missStrip = false;
266 missing_mask = 0;
267 norecovery_mask = 0;
268 unsigned int wclayers = road.getWCLayers();
269 for (unsigned layer = 0; layer < m_pmap->getNLogiLayers(); layer++)
270 {
271 int nHits = road.getHitPtrs(layer).size();
272 if (nHits==0)
273 {
274 if (m_IdealCoordFitType == TrackCorrType::None && ((wclayers >> layer) & 1))
275 {
276 int ix = m_pmap->getCoordOffset(layer);
277 int iy = ix + 1;
278 if (m_pmap->isSCT(layer))
279 {
280 missing_mask |= 1 << ix;
281 nMissing -= 1;
282 }
283 else
284 {
285 missing_mask |= (1<<ix) | (1<<iy);
286 nMissing -= 2;
287 }
288 }
289 else
290 {
291 if (m_pmap->isSCT(layer)) missStrip = true;
292 else missPixel = true;
293 }
294 }
295 else if (!((wclayers >> layer) & 1)) { // we have a hit
296 int ix = m_pmap->getCoordOffset(layer);
297 int iy = ix + 1;
298 if (m_pmap->isSCT(layer))
299 {
300 missing_mask |= 1 << ix;
301 nMissing -= 1;
302 }
303 else
304 {
305 missing_mask |= (1<<ix) | (1<<iy);
306 nMissing -= 2;
307 }
308
309 // set the mask to avoid recovery in crowded planes
311 norecovery_mask |= (1<<layer);
312 }
313 }
314}
315
316
317
318// This is a version of the above that produces a single track candidate on demand.
319FPGATrackSimTrack TrackFitter::makeTrackCandidate(const FPGATrackSimRoad & road, const FPGATrackSimTrack & temp, const std::vector<int>& hit_indices)
320{
321 m_idbase += 1;
322
323 FPGATrackSimTrack track_cand(temp);
324
325 //get the WC hits:
326 layer_bitmask_t wcbits= road.getWCLayers();
327
328 //Need to set the ID and the hits size of this track
329 track_cand.setTrackID(m_idbase);
330 track_cand.setNLayers(m_pmap->getNLogiLayers());
331
332 // If this is an idealized coordinate fit; keep references to the idealized radii.
333 track_cand.setIdealRadii(m_rmap->getAvgRadii(0));
334
335 for (unsigned layer = 0; layer < m_pmap->getNLogiLayers(); layer++)
336 {
337 if (hit_indices[layer] < 0) // Set a dummy hit if road has no hits in this layer
338 {
340 newhit.setLayer(layer);
341 newhit.setSection(0);
342 if (m_pmap->getDim(layer) == 2) newhit.setDetType(SiliconTech::pixel);
343 else newhit.setDetType(SiliconTech::strip);
344
345 if (wcbits & (1 << layer ) ) {
347 newhit.setLayer(layer);
348 }
349
350 track_cand.setFPGATrackSimHit(layer, std::make_shared<FPGATrackSimHit>(newhit));
351 }
352 else
353 {
354 const std::shared_ptr<const FPGATrackSimHit> hit = road.getHitPtrs(layer)[hit_indices[layer]];
355 // If this is an outer spacepoint, and it is not the same as the inner spacepoint, reject it.
356 // Here we "reject" it by marking the candidate as "invalid", to be rejected later.
357 // That require another field on the track object, but it avoids having to change the sizes
358 // of arrays computed above.
359 if (hit->getHitType() == HitType::spacepoint && hit->getSide() == 1 && layer > 0) {
360 auto inner_hit_ptr = track_cand.getFPGATrackSimHitPtrs().at(layer - 1); //avoid negative index
361 if (!inner_hit_ptr) throw std::runtime_error("Null inner hit pointer in TrackFitter: inner layer should have a hit when comparing spacepoints");
362 const FPGATrackSimHit inner_hit = *inner_hit_ptr;
363 if ((hit->getX() != inner_hit.getX()) || (hit->getY() != inner_hit.getY()) || (hit->getZ() != inner_hit.getZ())) {
364 track_cand.setValidCand(false);
365 break;
366 }
367 }
368 track_cand.setFPGATrackSimHit(layer, std::move(hit));
369 }
370 }
371
372 return track_cand;
373}
374
383{
384 m_nfits_rec += 1;
385
386 std::vector<FPGATrackSimTrack> recovered_tracks(m_pmap->getNLogiLayers(),t); // start with the complete track in all layers
387
388 float best_chi2ndof = t.getChi2ndof();
389 int best_i = -1;
390
391 // Remove a hit from each layer and do a fit
392 for (unsigned layer = (m_require_first ? 1 : 0); layer < recovered_tracks.size(); ++layer)
393 {
394 // Skip planes with multiple hits
395 if (norecovery_mask & (1<<layer)) continue;
396 m_nfits_addrec += 1;
397 // Zero the cluster for the missing layer, make sure to set the number of dimensions for it
398
400 newhit.setLayer(layer);
401 newhit.setSection(0);
403 recovered_tracks[layer].setFPGATrackSimHit(layer, std::make_shared<FPGATrackSimHit>(newhit));
404 // Set the number of missing points and the related bitmask
405 int ix = m_pmap->getCoordOffset(layer);
406
407 unsigned int missing_mask = t.getHitMap();
408 missing_mask &= ~(1 << ix);
409
410 if (m_pmap->isPixel(layer)) missing_mask &= ~(1 << (ix + 1));
411
412 recovered_tracks[layer].setHitMap(missing_mask);
413 recovered_tracks[layer].setNMissing(t.getNMissing() + m_pmap->getDim(layer));
414
415 // reset the pt
416 recovered_tracks[layer].setQOverPt(qoverpt);
417 recovered_tracks[layer].setTrackCorrType(m_IdealCoordFitType);
418
419
420 // If not guessing do the fit using layer+1 since 1st set of constants are the 8/8
421 if (m_guessinghits) m_nominalBank->linfit(sector, recovered_tracks[layer], m_do2ndStage);
422 else m_droppedLayerBanks[layer]->linfit(sector, recovered_tracks[layer], m_do2ndStage);
423
424
425 // Check if the chi2ndof is better
426 if (recovered_tracks[layer].getChi2ndof() < best_chi2ndof)
427 {
428 best_chi2ndof = recovered_tracks[layer].getChi2ndof();
429 best_i = layer;
430 }
431 }
432
433 if (best_i >= 0) {// a better track was found
434 return recovered_tracks[best_i];
435 } else
436 return t;
437}
438
439
440
441
442// compute the best geant match, the barcode with the largest number of hits contributing to the track,
443// and store in the track.
445{
446 std::vector<FPGATrackSimMultiTruth> mtv;
447
448 for (unsigned layer = 0; layer < m_pmap->getNLogiLayers(); layer++)
449 {
450 if (t.getHitMap() & (1 << m_pmap->getCoordOffset(layer))) continue; // no hit in this plane
451 //Sanity check that we have enough hits.
452 if (layer < t.getFPGATrackSimHitPtrs().size()) {
453 auto hit_ptr = t.getFPGATrackSimHitPtrs().at(layer);
454 if (!hit_ptr) throw std::runtime_error("Null hit pointer in TrackFitter::compute_truth: tracks should not have unassigned layers");
455 mtv.push_back(hit_ptr->getTruth());
456 }
457
458 // adjust weight for hits without (and also with) a truth match, so that each is counted with the same weight.
459 if (!mtv.empty()) mtv.back().assign_equal_normalization();
460 }
461
462 FPGATrackSimMultiTruth mt( std::accumulate(mtv.begin(), mtv.end(), FPGATrackSimMultiTruth(), FPGATrackSimMultiTruth::AddAccumulator()) );
463 // frac is then the fraction of the total number of hits on the track attributed to the barcode.
464
467 const bool ok = mt.best(tbarcode, tfrac);
468 if (ok)
469 {
470 t.setEventIndex(tbarcode.first);
471 t.setBarcode(tbarcode.second);
472 t.setBarcodeFrac(tfrac);
473 }
474}
475
476
478// findTracks utility helper functions
480
481// Returns true if there is a fit in track_cands with chi2ndof < minchi2ndof
482bool hasGoodFit(std::vector<FPGATrackSimTrack> const & track_cands, float minchi2ndof)
483{
484 for (FPGATrackSimTrack const & t: track_cands)
485 {
486 if (t.getChi2ndof() > 0 && t.getChi2ndof() < minchi2ndof)
487 return true;
488 }
489 return false;
490}
#define ATH_MSG_FATAL(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
std::vector< std::vector< int > > getComboIndices(std::vector< size_t > const &sizes)
Given a vector of sizes (of arrays), generates a vector of all combinations of indices to index one e...
#define FITTRACKS_OK
int32_t sector_t
#define FITTRACKS_BAD
uint32_t layer_bitmask_t
static const uint32_t nHits
std::vector< FPGATrackSimTrack >::const_iterator getBestChi2(std::vector< FPGATrackSimTrack > const &tracks)
bool hasGoodFit(std::vector< FPGATrackSimTrack > const &track_cands, float minchi2)
#define y
#define x
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
float getY() const
void setLayer(unsigned v)
float getX() const
void setHitType(HitType type)
float getZ() const
void setSection(unsigned v)
void setDetType(SiliconTech detType)
bool best(FPGATrackSimMultiTruth::Barcode &code, FPGATrackSimMultiTruth::Weight &weight) const
std::pair< unsigned long, unsigned long > Barcode
double getFitChi2() const
unsigned getYBin() const
double getFitChi2Phi() const
size_t getNHits() const
sector_t getSector() const
float getY() const
const std::vector< unsigned > & getBinIdx() const
int getSubRegion() const
const std::vector< std::shared_ptr< const FPGATrackSimHit > > & getHitPtrs(size_t layer) const
pid_t getPID() const
layer_bitmask_t getWCLayers() const
double getFitChi2Eta() const
std::vector< size_t > getNHits_layer() const
const FPGATrackSimTrackPars & getFitParams() const
float getX() const
unsigned getXBin() const
float getQOverPt() const
void setOrigChi2(float v)
const std::vector< std::shared_ptr< const FPGATrackSimHit > > & getFPGATrackSimHitPtrs() const
void setIdealRadii(const std::vector< double > &v)
void setFPGATrackSimHit(unsigned i, std::shared_ptr< const FPGATrackSimHit > hit)
void setChi2Eta(float v)
void setPars(FPGATrackSimTrackPars const &pars)
bool isValidCand() const
void setChi2Phi(float v)
void setNLayers(int)
set the number of layers in the track.
void setHitMap(unsigned int v)
void setValidCand(bool v)
float getChi2() const
void compute_truth(FPGATrackSimTrack &newtrk) const
FPGATrackSimTrack makeTrackCandidate(const FPGATrackSimRoad &road, const FPGATrackSimTrack &temp, const std::vector< int > &hit_indices)
std::vector< const FPGATrackSimFitConstantBank * > m_droppedLayerBanks
TrackFitter(void)
FPGATrackSimTrack recoverTrack(FPGATrackSimTrack const &t, sector_t sector, layer_bitmask_t norecovery_mask, double qoverpt)
Given a N/N track that has a bad chi2, try to refit the track by taking away a single hit,...
void getMissingInfo(const FPGATrackSimRoad &road, int &nMissing, bool &missPixel, bool &missStrip, layer_bitmask_t &missing_mask, layer_bitmask_t &norecovery_mask)
void resetCounters()
int fitTracks(const std::vector< FPGATrackSimRoad > &roads, std::vector< FPGATrackSimTrack > &tracks)
singleton-like access to IMessageSvc via open function and helper
Some weak symbol referencing magic... These are declared in AthenaKernel/getMessageSvc....