ATLAS Offline Software
Loading...
Searching...
No Matches
FPGATrackSimFitConstantBank.cxx
Go to the documentation of this file.
1// Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3#include <Eigen/StdVector>
7#include "limits.h"
8#include <TMath.h>
9#include <Eigen/Dense>
10#include <iostream>
11#include <iomanip>
12#include <fstream>
13#include <string>
14#include <cassert>
15#include <cstdio>
16#include <cmath>
17#include <bitset>
18#include <ieee754.h>
19
20#include <sstream>
21
22FPGATrackSimFitConstantBank::FPGATrackSimFitConstantBank(FPGATrackSimPlaneMap const * pmap, int ncoords, std::string const & fname, bool /*isFirstStage*/, float phishift, int missingPlane) :
23 AthMessaging ("FPGATrackSimFitConstantBank"),
24 m_pmap(pmap),
25 m_bankID(0),
26 m_nsectors(0),
27 m_ncoords(ncoords),
28 m_nconstr(0),
29 m_npixcy(0),
30 m_phiShift(phishift),
31 m_missingPlane(missingPlane),
32// m_isFirstStage(isFirstStage),
34{
35 std::ifstream geocfile(fname);
36 if (not geocfile.is_open()) {
37 ATH_MSG_WARNING("FitConstants file: " << fname << " cannot be opened");
38 }
39 else {
40 ATH_MSG_INFO("Reading " << fname);
41 // Read the file header
42 readHeader(geocfile);
43 ATH_MSG_INFO("Settings: m_ncoords="<<m_ncoords<<" m_npars="<<m_npars);
44 // Read the sector constants
45 //coverity[TAINTED_SCALAR]
46 readSectorInfo(geocfile);
47 // Pre-calculate the majority logic elements
48 if (m_missingPlane == -1)
49 //coverity[TAINTED_SCALAR]
51
52 if (sizeof(float) * CHAR_BIT != 32)
53 ATH_MSG_WARNING("Floating points on this computer are not 32 bit. This may cause a problem for the hardware agreement. Be careful!");
54
55 setIdealCoordFit(true);
56 //coverity[TAINTED_SCALAR]
58 }
59}
60
61
62// Reads the file header, sets configuration and sizes the sector variables
63void FPGATrackSimFitConstantBank::readHeader(std::ifstream & geocfile)
64{
65 std::string key;
66 int ival;
67 int nplanes;
68
69 for (int i = 0; i < 5; ++i) getline(geocfile, key); // skip 5 lines
70
71 geocfile >> key;
72 if (key != "NPLANES") ATH_MSG_ERROR("Invalid file format");
73 geocfile >> nplanes;
74
75 geocfile >> key;
76 if (key!="NSECTORS") ATH_MSG_ERROR("Invalid file format");
77 geocfile >> m_nsectors;
78
79 geocfile >> key;
80 if (key!="NDIM") ATH_MSG_ERROR("Invalid file format");
81 geocfile >> ival;
82
83 // Set derived configuration variables
84 if (ival==1) m_npars = 3;
85 else if (ival==2) m_npars = 5;
86 else ATH_MSG_ERROR("Number of dimensions invalid");
87
88 m_npixcy = 2*(m_ncoords - nplanes); // difference between number of coordinates and planes is number of 2d measurements
90
91 // Allocate the block of pointer per sector
92 //m_nsectors should be checked to be sane
93 //coverity[TAINTED_SCALAR]
96 m_fit_const.resize(m_nsectors, 5);
99
100 m_WCs.resize(m_nsectors, m_ncoords);
101
102 m_maj_a.resize(m_nsectors, m_ncoords, 0);
105}
106
107
108// Reads the main body of the constants file
110{
111 std::string key;
112 int ival;
113 double dval; // temp value used to conver from double to float
114
115 for (int isec = 0; isec < m_nsectors; ++isec)
116 {
117 // Read sector number
118 geocfile >> key >> ival;
119 if (ival != isec) ATH_MSG_WARNING("Reached sector " << ival << " but expected sector " << isec);
120
121 // Read fit parameters
122 for (int ipar = 0; ipar < m_npars; ++ipar)
123 {
124 geocfile >> key;
125 // no check on key?
126 for (int icoord = 0; icoord < m_ncoords; ++icoord)
127 {
128 geocfile >> dval;
129 if (geocfile.fail())
130 ATH_MSG_WARNING("par loop (1) key=\"" << key << "\" ipar,icoord=" << ipar << "," << icoord);
131
132 if (dval != 0) m_sector_good[isec] = true;
133 m_fit_pars(isec, ipar, icoord) = dval;
134 }
135 }
136
137 // Read k averages
138 geocfile >> key;
139 assert(key=="kaverages");
140 for (int ik=0;ik<m_nconstr;++ik)
141 {
142 geocfile >> dval;
143 if (dval != 0) m_sector_good[isec] = true;
144 m_kaverage(isec,ik) = dval;
145 }
146
147 // Read kernel
148 geocfile >> key;
149 assert(key=="kernel");
150 for (int ik=0;ik<m_nconstr;++ik)
151 {
152 for (int ik2=0;ik2<m_ncoords;++ik2)
153 {
154 geocfile >> dval;
155 if (dval != 0) m_sector_good[isec] = true;
156 m_kernel(isec, ik, ik2) = dval;
157 }
158 }
159
160 // Read fit const
161 for (int ipar=0;ipar<m_npars;++ipar)
162 {
163 geocfile >> key;
164 geocfile >> dval;
165 if (dval != 0) m_sector_good[isec] = true;
166 m_fit_const(isec, ipar) = dval;
167 }
168
169 }
170}
171
172
173// Majority fits are those for which we have missing hits or when we drop a hit on 8/8 to improve chi2
175{
176
177 for (int isec=0;isec!=m_nsectors;++isec)
178 {
179 // Fill m_maj_a and m_maj_kk
180 for (int ix=0;ix!=m_ncoords;++ix)
181 {
182 for (int row=0;row!=m_nconstr;++row)
183 m_maj_a(isec, ix) += m_kaverage(isec, row) * m_kernel(isec, row, ix);
184 for (int jx=0;jx!=m_ncoords;++jx)
185 {
186 for (int row=0;row!=m_nconstr;++row)
187 m_maj_kk(isec, ix, jx) += m_kernel(isec, row, ix) * m_kernel(isec, row, jx);
188 }
189 }
190
191 // Fill m_maj_invkk
192
193 // Pixel layers (2 coordinates each), assume these come first!
194 for (int ix=0;ix!=m_npixcy;ix+=2)
195 {
196 float det = m_maj_kk(isec, ix, ix) * m_maj_kk(isec, ix+1, ix+1)
197 - m_maj_kk(isec, ix+1, ix) * m_maj_kk(isec, ix, ix+1);
198 if (det == 0) continue;
199
200 m_maj_invkk(isec, ix, ix) = m_maj_kk(isec, ix+1, ix+1)/det;
201 m_maj_invkk(isec, ix, ix+1) = -m_maj_kk(isec, ix+1, ix)/det;
202 m_maj_invkk(isec, ix+1, ix) = -m_maj_kk(isec, ix, ix+1)/det;
203 m_maj_invkk(isec, ix+1, ix+1) = m_maj_kk(isec, ix, ix)/det;
204 }
205
206 // Strip layers (1 coordinate)
207 for (int ix=m_npixcy;ix!=m_ncoords;++ix) {
208 if (m_maj_kk(isec, ix, ix) == 0) continue;
209 m_maj_invkk(isec, ix, ix) = 1./m_maj_kk(isec, ix, ix);
210 }
211 }
212}
213
214
215// Prepares the inverted constants used in the invlinfit()
217{
219
220 for (int isec=0;isec!=m_nsectors;++isec)
221 {
222 Eigen::MatrixXf thisMatrix(m_ncoords, m_ncoords);
223
224 // Parameters, by row
225 for (int ip=0;ip!=m_npars;++ip)
226 for (int ix=0;ix!=m_ncoords;++ix)
227 thisMatrix(ip,ix)= (m_fit_pars(isec, ip, ix));
228
229 // Constraints, by row
230 for (int ic=0;ic!=m_nconstr;++ic)
231 for (int ix=0;ix!=m_ncoords;++ix)
232 thisMatrix(ic+m_npars,ix) = m_kernel(isec, ic, ix);
233
234 // reset usable coordinate
235 for (int ix = 0; ix != m_ncoords; ++ix)
236 m_WCs(isec,ix) = 1; // start with all WCs
237
238 // now let's check if we have any "null" row or column, and if so, we will get rid of them temporarily
239 // we don't have access to module/WC information at this stage should just look for non-zero values
240 for (int ix=0;ix!=m_ncoords;++ix) {
241 bool foundrow = false;
242 bool foundcolumn = false;
243 for (int iy=0;iy!=m_ncoords;++iy) {
244 if (abs(thisMatrix(ix,iy)) > MTX_TOLERANCE) foundrow = true;
245 if (abs(thisMatrix(iy,ix)) > MTX_TOLERANCE) foundcolumn = true;
246 }
247 if (foundrow && foundcolumn) m_WCs(isec,ix) = 0;
248 }
249
250 // now count the number of usable entries
251 size_t nDimToUse = 0; for (int ix=0;ix!=m_ncoords;++ix) if(!m_WCs(isec,ix)) ++nDimToUse;
252
253 Eigen::MatrixXf tempMatrix(nDimToUse, nDimToUse); // this is the matrix we will invert
254
255 size_t counteri = 0, counterj = 0;
256 for (int i = 0; i < m_ncoords; i++) {
257 counterj = 0;
258 if (!m_WCs(isec,i)) {
259 for (int j = 0; j < m_ncoords; j++) {
260 if (!m_WCs(isec,j)) {
261 tempMatrix(counteri,counterj) = thisMatrix(i,j); // if we use this coordinate, copy it over
262 counterj++; // iterate counter
263 }
264 }
265 counteri++;
266 }
267 }
268 /* commenting this out for now because it screws up the new 'non-safeguarded' maps
269 // Invert the matrix
270 Eigen::MatrixXf inverted = tempMatrix.inverse();
271
272 // this is the full matrix that includes inverted coordinates plus the padded zeros
273 Eigen::MatrixXf fullInverted(m_ncoords, m_ncoords);
274
275 // now we put things back into the full matrix, adding back the padded zeros
276 counteri = 0; counterj = 0;
277 for (int i = 0; i < m_ncoords; i++) {
278 counterj = 0;
279 if (!m_WCs(isec,i)) {
280 for (int j = 0; j < m_ncoords; j++) {
281 if (!m_WCs(isec,j)) {
282 fullInverted(i,j) = inverted(counteri,counterj); // if we use this coordinate, copy it over
283 counterj++; // iterate counter
284 }
285 else {
286 fullInverted(i,j) = 0;
287 }
288 }
289 counteri++;
290 }
291 else {
292 for (int j = 0; j < m_ncoords; j++) {
293 fullInverted(i,j) = 0;
294 }
295 }
296 }
297 m_invfit_consts[isec]= fullInverted;
298 */
299 }
300}
301
302
303
305// Main Interface Functions
306// This functions takes in hit coordinates from 'track' and does the linear fit
307// to calculate the track parameters, which are populated back into 'track'.
308// Returns true on success.
309bool FPGATrackSimFitConstantBank::linfit(sector_t sector, FPGATrackSimTrack & track, bool isSecondStage) const
310{
311 // Do majority guess if it's needed
312 if (m_missingPlane == -1 && !m_isIdealCoordFit)
313 {
314 int nmissing = track.getNMissing();
315 if (nmissing > 0)
316 {
317 int guess_res = missing_point_guess(sector, track, !isSecondStage, false);
318 if (nmissing != guess_res) return false; // majority failed
319 }
320 }
321
322 // evaluate the chi2
323 linfit_chisq(sector, track);
324
325 // Do the fit
326 linfit_pars_eval(sector, track);
327
328 return true;
329}
330
331
332int FPGATrackSimFitConstantBank::missing_point_guess(sector_t sector, FPGATrackSimTrack &track, bool isFirstStage, bool doExtrapolation) const
333{
334
335 std::vector<int> coordsmask(m_ncoords);
336 std::vector<int> missid;
337
338 // Keep track of which hits are missing
339 int nmissing = 0;
340 std::vector<int> missing_planes;
341 for (int j = 0; j < m_ncoords ; ++j)
342 {
343 coordsmask[j] = (track.getHitMap()>>j) & 1;
344 if (!coordsmask[j]) {
345 int plane = m_pmap->getCoordLayer(j);
346 if(missing_planes.size() == 0) {
347 missing_planes.push_back(plane);
348 }
349 else {
350 for (unsigned k = 0; k < missing_planes.size(); k++) {
351 if(plane == missing_planes[k]) break;
352 if(k == missing_planes.size() - 1) missing_planes.push_back(plane);
353 }
354 }
355 missid.push_back(j);
356 nmissing++;
357 }
358 }
359
360 if(!doExtrapolation){
361 if(isFirstStage){
362 if(missing_planes.size() > 1){
363 ATH_MSG_WARNING("missing_point_guess() Can only guess 1 layer in the first stage");
364 return 0;
365 }
366 if (nmissing > 2){
367 ATH_MSG_WARNING("missing_point_guess() Cannot guess more than two coordinates in the first stage");
368 return 0;
369 }
370 }
371 else{
372 if(missing_planes.size() > 2){
373 ATH_MSG_WARNING("missing_point_guess() Can only guess 2 layers in the second stage");
374 return 0;
375 }
376 if (nmissing > 4){
377 ATH_MSG_WARNING("missing_point_guess() Cannot guess more than four coordinates in the second stage");
378 return 0;
379 }
380 }
381 }
382
383 Eigen::MatrixXd coef(nmissing,nmissing);
384 Eigen::VectorXd a(nmissing);
385
386 for (int i=0; i<nmissing; ++i)
387 {
388 a[i] = -m_maj_a(sector, missid[i]);
389 // complete the calculation with the terms that depend on the known hits
390 for (int col=0; col<m_ncoords; ++col)
391 {
392 if (!coordsmask[col]) continue;
393
395 float phishift = m_phiShift;
396 int layer = m_pmap->getCoordLayer(col);
397 auto hit_ptr = track.getFPGATrackSimHitPtrs()[layer];
398 if (!hit_ptr) throw std::runtime_error("Null hit pointer in FPGATrackSimFitConstantBank: tracks should not have unassigned layers");
399 FPGATrackSimHit hit = *hit_ptr;
400 if (((hit.getPhysLayer() %2) == 1) && hit.getHitType() == HitType::spacepoint) phishift = 0.0;
401
402 a[i] -= m_maj_kk(sector, col, missid[i])*(phishift+track.getPhiCoord(m_pmap->getCoordLayer(col)));
403
404 if (m_pmap->getDim(m_pmap->getCoordLayer(col)) == 2) { // do two at a time if 2d, then skip ahead
405 a[i] -= m_maj_kk(sector, col+1, missid[i])*track.getEtaCoord(m_pmap->getCoordLayer(col));
406 ++col;
407 }
408 }
409
410 for (int j=0; j<nmissing; ++j)
411 {
412 // here for missing pieces need to know the coordinate and sector number only
413 coef(i,j) = m_maj_kk(sector, missid[i], missid[j]);
414 }
415 }
416
417 if (coef.determinant()==0) return -1;
418 Eigen::VectorXd missing_hits = coef.inverse()*a; // our missing hits!
419
420 for(int m=0; m<nmissing; m++){
421
422 int missedplane = m_pmap->getCoordLayer(missid[m]);
423
424 if(m_pmap->getDim(m_pmap->getCoordLayer(missid[m])) == 1){
425
426 FPGATrackSimHit newhit;
427 if(!doExtrapolation)
429 else
432 newhit.setLayer(missedplane);
433 newhit.setSection(0);
434 if (m_isIdealCoordFit) {
435 // TODO: all the missing hit logic will eventually need spacepoint updates.
436 double target_r = track.getIdealRadius(missedplane);
437 newhit.setX(target_r*TMath::Cos(missing_hits[m]));
438 newhit.setY(target_r*TMath::Sin(missing_hits[m]));
439 }
440 else{
441 newhit.setPhiIndex(missing_hits[m]);
442 }
443
444 track.setFPGATrackSimHit(missedplane, std::make_shared<FPGATrackSimHit>(newhit));
445 }
446 else if (m_pmap->getDim(m_pmap->getCoordLayer(missid[m])) == 2){
447
448 FPGATrackSimHit newhit;
449 if(!doExtrapolation)
451 else
454 newhit.setLayer(missedplane);
455 newhit.setSection(0);
456 if (m_isIdealCoordFit) {
457 // TODO This will also eventually ned spacepoint updates.
458 double target_r = track.getIdealRadius(missedplane);
459 newhit.setX(target_r*TMath::Cos(missing_hits[m]));
460 newhit.setY(target_r*TMath::Sin(missing_hits[m]));
461 newhit.setZ(missing_hits[m+1]);
462 }
463 else {
464 newhit.setPhiIndex(missing_hits[m]);
465 newhit.setEtaIndex(missing_hits[m+1]);
466 }
467 m++; //skip ahead
468
469 track.setFPGATrackSimHit(missedplane, std::make_shared<FPGATrackSimHit>(newhit));
470 }
471 }
472
473 return nmissing;
474}
475
477{
478 float chi2 = 0;
479
480 for (int i = 0; i < m_nconstr; i++)
481 {
482 float chi_component = m_kaverage(sector, i);
483
484 for (int coord = 0; coord < m_ncoords; coord++) {
485 unsigned layer = m_pmap->getCoordLayer(coord);
486
487 if (m_pmap->getDim(m_pmap->getCoordLayer(coord)) == 2) { // do two at a time if 2d, then skip ahead
488 chi_component += m_kernel(sector, i, coord) * (m_phiShift+trk.getPhiCoord(layer));
489 chi_component += m_kernel(sector, i, coord+1) * trk.getEtaCoord(layer);
490 ++coord;
491 }
492 else { // strip coords
494 float phishift = m_phiShift;
495 auto hitPtr = trk.getFPGATrackSimHitPtrs()[layer];
496 if (!hitPtr) throw std::runtime_error("Null hit pointer in getAccumulatorTerm: tracks should not have unassigned layers");
497 if (((hitPtr->getPhysLayer() %2) == 1) && hitPtr->getHitType() == HitType::spacepoint) phishift = 0.0;
498 chi_component += m_kernel(sector, i, coord) * (phishift+trk.getPhiCoord(layer));
499 }
500 }
501 chi2 += chi_component * chi_component;
502
503 }
504 trk.setChi2(chi2);
505}
506
507
508
509/* This function takes as input the pointer to the geometrical constants
510 structure, the coords array and and array that will contain the
511 results of the fit.
512
513 The fits return the number of useful parameter for the fits.
514 */
516{
517 std::vector<float> pars(m_npars);
518
519 for (int ip = 0; ip < m_npars; ip++)
520 {
521 pars[ip] = m_fit_const(sector, ip);
522
523 for (int coord = 0; coord < m_ncoords; coord++) {
524
526 float phishift = m_phiShift;
527 int layer = m_pmap->getCoordLayer(coord);
528 auto hitPtr = trk.getFPGATrackSimHitPtrs()[layer];
529 if (!hitPtr) throw std::runtime_error("Null hit pointer in getTrackPars: tracks should not have unassigned layers");
530 if (((hitPtr->getPhysLayer() %2) == 1) && hitPtr->getHitType() == HitType::spacepoint) phishift = 0.0;
531
532 pars[ip] += m_fit_pars(sector, ip, coord) * (phishift+trk.getPhiCoord(layer));
533 if (m_pmap->getDim(m_pmap->getCoordLayer(coord)) == 2) { // do two at a time if 2d, then skip ahead
534 pars[ip] += m_fit_pars(sector, ip, coord+1) * trk.getEtaCoord(layer);
535 ++coord;
536 }
537 }
538 }
539
540 trk.setQOverPt(pars[0]);
541 trk.setD0(pars[1]);
542 trk.setPhi(pars[2]-m_phiShift); // angle is moved within -pi to +pi, and shift phi back to the range we want
543 trk.setZ0(pars[3]);
544 trk.setEta(pars[4]);
545 if (trk.getDoDeltaGPhis()) {
546 trk.setQOverPt(trk.getHoughY()/1000.0 - trk.getQOverPt()); // final q/pT = q/pT from HT + delta q/pT
547 trk.setPhi(trk.getHoughX() - trk.getPhi()); // final phi_0 = phi_0 from HT + delta phi_0
548 }
549
550}
551
552
553
557void FPGATrackSimFitConstantBank::invlinfit(sector_t sector, FPGATrackSimTrack &track, double const *constr) const
558{
559 // vector of the acutal parameters, it is zeroed
560 Eigen::VectorXf pars(m_ncoords);
561
562 for (int ip = 0; ip < m_npars; ++ip)
563 {
564 // The first elements are the track parameters. The track are shifted using the sector constants
565 pars(ip) = track.getParameter(ip) - m_fit_const(sector, ip);
566 if (ip == 2) pars(ip) = remainder(pars(ip), 2.*M_PI);
567 }
568 for (int ic = 0; ic < m_nconstr; ++ic)
569 {
570 // The rest of the paramaters are the external constraints.
571 // The external constraint is also shifted by the kAverage value
572 if (!constr)
573 pars(m_npars+ic) = -m_kaverage(sector, ic);
574 else
575 pars(m_npars+ic) = constr[ic] - m_kaverage(sector, ic);
576 }
577
578 // The raw hits are obtained multiplying the parameters to the inverted constants
579 Eigen::VectorXf rawhits = (m_invfit_consts[sector]) * pars;
580
581 // The hits are assigned to the original track
582 for (int j = 0; j < m_ncoords ; ++j) {
583 int plane = m_pmap->getCoordLayer(j);
584 SiliconTech Tech = m_pmap->getDetType(plane);
585 FPGATrackSimHit hit;
587 hit.setDetType(Tech);
588 hit.setLayer(plane);
589 if (m_WCs(sector,j))
590 hit.setPhiIndex(-1); // to keep track later on, set hit position negative
591 else
592 hit.setPhiIndex(rawhits(j));
593
594 if (Tech == SiliconTech::pixel) {
595 if (m_WCs(sector,j))
596 hit.setEtaIndex(-1); // to keep track later on, set hit position negative
597 else
598 hit.setEtaIndex(rawhits(j+1));
599
600 ++j; // skip a coordinate if doing two at once
601 }
602 track.setFPGATrackSimHit(plane, std::make_shared<FPGATrackSimHit>(std::move(hit)));
603 }
604}
#define M_PI
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define MTX_TOLERANCE
SiliconTech
int32_t sector_t
double coord
Type of coordination system.
static Double_t a
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
void linfit_pars_eval(sector_t sector, FPGATrackSimTrack &trk) const
std::vector< Eigen::MatrixXf, Eigen::aligned_allocator< Eigen::MatrixXf > > m_invfit_consts
FPGATrackSimFitConstantBank(FPGATrackSimPlaneMap const *pmap, int ncoords, std::string const &fname, bool isFirstStage, float phishift, int missingPlane=-1)
void readHeader(std::ifstream &geocfile)
void linfit_chisq(sector_t sector, FPGATrackSimTrack &trk) const
void invlinfit(sector_t sector, FPGATrackSimTrack &track, double const *constr) const
This method uses the track parameters and additional constraints to use the constants to calculate th...
void readSectorInfo(std::ifstream &geocfile)
FPGATrackSimPlaneMap const * m_pmap
bool linfit(sector_t sector, FPGATrackSimTrack &track, bool isSecondStage) const
int missing_point_guess(sector_t sector, FPGATrackSimTrack &track, bool isFirstStage, bool doExtrapolation) const
void setLayer(unsigned v)
void setEtaIndex(unsigned v)
void setPhiIndex(unsigned v)
void setHitType(HitType type)
void setZ(float v)
unsigned getPhysLayer(bool old=false) const
void setX(float v)
void setY(float v)
void setSection(unsigned v)
HitType getHitType() const
void setDetType(SiliconTech detType)
float getQOverPt() const
float getHoughX() const
float getEtaCoord(int ilayer) const
void setQOverPt(float v)
void setPhi(float v, bool ForceRange=true)
bool getDoDeltaGPhis() const
const std::vector< std::shared_ptr< const FPGATrackSimHit > > & getFPGATrackSimHitPtrs() const
float getHoughY() const
float getPhiCoord(int ilayer) const
std::vector< std::string > remainder(const std::vector< std::string > &v1, const std::vector< std::string > &v2)
double chi2(TH1 *h0, TH1 *h1)