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