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