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 FPGATrackSimHit hit = (track.getFPGATrackSimHits())[layer];
393 if (((hit.getPhysLayer() %2) == 1) && hit.getHitType() == HitType::spacepoint) phishift = 0.0;
394
395 a[i] -= m_maj_kk(sector, col, missid[i])*(phishift+track.getPhiCoord(m_pmap->getCoordLayer(col)));
396
397 if (m_pmap->getDim(m_pmap->getCoordLayer(col)) == 2) { // do two at a time if 2d, then skip ahead
398 a[i] -= m_maj_kk(sector, col+1, missid[i])*track.getEtaCoord(m_pmap->getCoordLayer(col));
399 ++col;
400 }
401 }
402
403 for (int j=0; j<nmissing; ++j)
404 {
405 // here for missing pieces need to know the coordinate and sector number only
406 coef(i,j) = m_maj_kk(sector, missid[i], missid[j]);
407 }
408 }
409
410 if (coef.determinant()==0) return -1;
411 Eigen::VectorXd missing_hits = coef.inverse()*a; // our missing hits!
412
413 for(int m=0; m<nmissing; m++){
414
415 int missedplane = m_pmap->getCoordLayer(missid[m]);
416
417 if(m_pmap->getDim(m_pmap->getCoordLayer(missid[m])) == 1){
418
419 FPGATrackSimHit newhit;
420 if(!doExtrapolation)
422 else
425 newhit.setLayer(missedplane);
426 newhit.setSection(0);
427 if (m_isIdealCoordFit) {
428 // TODO: all the missing hit logic will eventually need spacepoint updates.
429 double target_r = track.getIdealRadius(missedplane);
430 newhit.setX(target_r*TMath::Cos(missing_hits[m]));
431 newhit.setY(target_r*TMath::Sin(missing_hits[m]));
432 }
433 else{
434 newhit.setPhiIndex(missing_hits[m]);
435 }
436
437 track.setFPGATrackSimHit(missedplane, newhit);
438 }
439 else if (m_pmap->getDim(m_pmap->getCoordLayer(missid[m])) == 2){
440
441 FPGATrackSimHit newhit;
442 if(!doExtrapolation)
444 else
447 newhit.setLayer(missedplane);
448 newhit.setSection(0);
449 if (m_isIdealCoordFit) {
450 // TODO This will also eventually ned spacepoint updates.
451 double target_r = track.getIdealRadius(missedplane);
452 newhit.setX(target_r*TMath::Cos(missing_hits[m]));
453 newhit.setY(target_r*TMath::Sin(missing_hits[m]));
454 newhit.setZ(missing_hits[m+1]);
455 }
456 else {
457 newhit.setPhiIndex(missing_hits[m]);
458 newhit.setEtaIndex(missing_hits[m+1]);
459 }
460 m++; //skip ahead
461
462 track.setFPGATrackSimHit(missedplane, newhit);
463 }
464 }
465
466 return nmissing;
467}
468
470{
471 float chi2 = 0;
472
473 for (int i = 0; i < m_nconstr; i++)
474 {
475 float chi_component = m_kaverage(sector, i);
476
477 for (int coord = 0; coord < m_ncoords; coord++) {
478 unsigned layer = m_pmap->getCoordLayer(coord);
479
480 if (m_pmap->getDim(m_pmap->getCoordLayer(coord)) == 2) { // do two at a time if 2d, then skip ahead
481 chi_component += m_kernel(sector, i, coord) * (m_phiShift+trk.getPhiCoord(layer));
482 chi_component += m_kernel(sector, i, coord+1) * trk.getEtaCoord(layer);
483 ++coord;
484 }
485 else { // strip coords
487 float phishift = m_phiShift;
488 FPGATrackSimHit hit = (trk.getFPGATrackSimHits())[layer];
489 if (((hit.getPhysLayer() %2) == 1) && hit.getHitType() == HitType::spacepoint) phishift = 0.0;
490 chi_component += m_kernel(sector, i, coord) * (phishift+trk.getPhiCoord(layer));
491 }
492 }
493 chi2 += chi_component * chi_component;
494
495 }
496 trk.setChi2(chi2);
497}
498
499
500
501/* This function takes as input the pointer to the geometrical constants
502 structure, the coords array and and array that will contain the
503 results of the fit.
504
505 The fits return the number of useful parameter for the fits.
506 */
508{
509 std::vector<float> pars(m_npars);
510
511 for (int ip = 0; ip < m_npars; ip++)
512 {
513 pars[ip] = m_fit_const(sector, ip);
514
515 for (int coord = 0; coord < m_ncoords; coord++) {
516
518 float phishift = m_phiShift;
519 int layer = m_pmap->getCoordLayer(coord);
520 FPGATrackSimHit hit = (trk.getFPGATrackSimHits())[layer];
521 if (((hit.getPhysLayer() %2) == 1) && hit.getHitType() == HitType::spacepoint) phishift = 0.0;
522
523 pars[ip] += m_fit_pars(sector, ip, coord) * (phishift+trk.getPhiCoord(layer));
524 if (m_pmap->getDim(m_pmap->getCoordLayer(coord)) == 2) { // do two at a time if 2d, then skip ahead
525 pars[ip] += m_fit_pars(sector, ip, coord+1) * trk.getEtaCoord(layer);
526 ++coord;
527 }
528 }
529 }
530
531 trk.setQOverPt(pars[0]);
532 trk.setD0(pars[1]);
533 trk.setPhi(pars[2]-m_phiShift); // angle is moved within -pi to +pi, and shift phi back to the range we want
534 trk.setZ0(pars[3]);
535 trk.setEta(pars[4]);
536 if (trk.getDoDeltaGPhis()) {
537 trk.setQOverPt(trk.getHoughY()/1000.0 - trk.getQOverPt()); // final q/pT = q/pT from HT + delta q/pT
538 trk.setPhi(trk.getHoughX() - trk.getPhi()); // final phi_0 = phi_0 from HT + delta phi_0
539 }
540
541}
542
543
544
548void FPGATrackSimFitConstantBank::invlinfit(sector_t sector, FPGATrackSimTrack &track, double const *constr) const
549{
550 // vector of the acutal parameters, it is zeroed
551 Eigen::VectorXf pars(m_ncoords);
552
553 for (int ip = 0; ip < m_npars; ++ip)
554 {
555 // The first elements are the track parameters. The track are shifted using the sector constants
556 pars(ip) = track.getParameter(ip) - m_fit_const(sector, ip);
557 if (ip == 2) pars(ip) = remainder(pars(ip), 2.*M_PI);
558 }
559 for (int ic = 0; ic < m_nconstr; ++ic)
560 {
561 // The rest of the paramaters are the external constraints.
562 // The external constraint is also shifted by the kAverage value
563 if (!constr)
564 pars(m_npars+ic) = -m_kaverage(sector, ic);
565 else
566 pars(m_npars+ic) = constr[ic] - m_kaverage(sector, ic);
567 }
568
569 // The raw hits are obtained multiplying the parameters to the inverted constants
570 Eigen::VectorXf rawhits = (m_invfit_consts[sector]) * pars;
571
572 // The hits are assigned to the original track
573 for (int j = 0; j < m_ncoords ; ++j) {
574 int plane = m_pmap->getCoordLayer(j);
575 SiliconTech Tech = m_pmap->getDetType(plane);
576 FPGATrackSimHit hit;
578 hit.setDetType(Tech);
579 hit.setLayer(plane);
580 if (m_WCs(sector,j))
581 hit.setPhiIndex(-1); // to keep track later on, set hit position negative
582 else
583 hit.setPhiIndex(rawhits(j));
584
585 if (Tech == SiliconTech::pixel) {
586 if (m_WCs(sector,j))
587 hit.setEtaIndex(-1); // to keep track later on, set hit position negative
588 else
589 hit.setEtaIndex(rawhits(j+1));
590
591 ++j; // skip a coordinate if doing two at once
592 }
593 track.setFPGATrackSimHit(plane, hit);
594 }
595}
#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
void setChi2(float v)
float getEtaCoord(int ilayer) const
void setQOverPt(float v)
void setPhi(float v, bool ForceRange=true)
bool getDoDeltaGPhis() const
float getHoughY() const
const std::vector< FPGATrackSimHit > & getFPGATrackSimHits() 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)