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