From 39527cd272d392d8877e6682e45a6f26017657db Mon Sep 17 00:00:00 2001 From: Ian Tomalin Date: Fri, 22 May 2020 09:55:21 +0100 Subject: [PATCH] Addition of L1 tracking code for "TMTT" & (part of) "Hybrid" algorithms (L1Trigger/TrackFindingTMTT) (#29381) * create separate PRs for the two L1TK packages * Improved KF efficiency at high eta * Moved MC data files to cms-data * Removed old file * Removed KF HLS to put instead in external library * Ran scram b code-format * Delete KF4ParamsComb.h.bak * Delete KF4ParamsCombIV.bak * Delete KF4ParamsCombV2.bak * Delete KF5ParamsComb.h.bak * Delete KF4ParamsComb.cc.bak * Delete KF4ParamsCombIV.bak * Delete KF4ParamsCombV2.bak * Delete KF5ParamsComb.cc.bak * L1 tk integration tmtt pre5 (#7) * Added CMS code style fixes * Removed old file * Reapplied stub b code-format * All code review changes (#13) * Fix clang errors (#14) * fixed clang error * directory for MC txt files * Fixed clang warnings + minor simplifications (#15) * tweak * tweak * Fixed clang warnings and small simplifications * Fixed clang warnings and small simplifications * All remaining review comments addressed (#16) * Replaced vector size with empty function * Simplified DegradeBend and StubWindowSuggest * Fixed more review comments * More review comments * code reformat * Ran exhaustive clang tidy * Added library to BuildFile.xml (#17) * Deleted TrackFindingTMT/data/README (#18) * Added library to BuildFile.xml (This was already done yesterday. Not sure why it appears again) * README file in data directory deleted * Fix review comments (#20) Co-authored-by: Louise Skinnari --- L1Trigger/TrackFindingTMTT/BuildFile.xml | 26 + L1Trigger/TrackFindingTMTT/README.md | 139 ++ .../interface/ChiSquaredFit4.h | 21 + .../interface/ChiSquaredFitBase.h | 61 + .../interface/ConverterToTTTrack.h | 38 + .../TrackFindingTMTT/interface/DegradeBend.h | 74 + .../TrackFindingTMTT/interface/DigitalStub.h | 172 ++ .../TrackFindingTMTT/interface/DigitalTrack.h | 231 +++ .../interface/DupFitTrkKiller.h | 58 + .../interface/GlobalCacheTMTT.h | 60 + L1Trigger/TrackFindingTMTT/interface/HTbase.h | 147 ++ L1Trigger/TrackFindingTMTT/interface/HTcell.h | 155 ++ L1Trigger/TrackFindingTMTT/interface/HTrphi.h | 170 ++ L1Trigger/TrackFindingTMTT/interface/Histos.h | 365 ++++ .../TrackFindingTMTT/interface/InputData.h | 73 + .../TrackFindingTMTT/interface/KFParamsComb.h | 55 + .../interface/KFTrackletTrack.h | 250 +++ L1Trigger/TrackFindingTMTT/interface/KFbase.h | 170 ++ .../TrackFindingTMTT/interface/KalmanState.h | 101 + .../interface/L1fittedTrack.h | 412 ++++ .../TrackFindingTMTT/interface/L1track2D.h | 141 ++ .../TrackFindingTMTT/interface/L1track3D.h | 283 +++ .../TrackFindingTMTT/interface/L1trackBase.h | 64 + .../TrackFindingTMTT/interface/Make3Dtracks.h | 98 + .../TrackFindingTMTT/interface/MiniHTstage.h | 60 + .../TrackFindingTMTT/interface/MuxHToutputs.h | 77 + .../TrackFindingTMTT/interface/PrintL1trk.h | 29 + L1Trigger/TrackFindingTMTT/interface/Sector.h | 101 + .../TrackFindingTMTT/interface/Settings.h | 666 +++++++ .../TrackFindingTMTT/interface/SimpleLR4.h | 68 + L1Trigger/TrackFindingTMTT/interface/Stub.h | 297 +++ .../interface/StubFEWindows.h | 52 + .../TrackFindingTMTT/interface/StubKiller.h | 80 + .../interface/StubWindowSuggest.h | 57 + L1Trigger/TrackFindingTMTT/interface/TP.h | 141 ++ .../interface/TrackFitFactory.h | 27 + .../interface/TrackFitGeneric.h | 37 + .../interface/TrackerModule.h | 136 ++ .../TrackFindingTMTT/interface/TrkRZfilter.h | 111 ++ .../TrackFindingTMTT/interface/Utility.h | 67 + .../TrackFindingTMTT/plugins/BuildFile.xml | 5 + .../plugins/TMTrackProducer.cc | 424 ++++ .../plugins/TMTrackProducer.h | 98 + .../python/TMTrackProducer_Defaults_cfi.py | 465 +++++ .../python/TMTrackProducer_Ultimate_cff.py | 77 + .../python/TMTrackProducer_cff.py | 165 ++ .../TrackFindingTMTT/src/ChiSquaredFit4.cc | 174 ++ .../TrackFindingTMTT/src/ChiSquaredFitBase.cc | 136 ++ .../src/ConverterToTTTrack.cc | 75 + L1Trigger/TrackFindingTMTT/src/DegradeBend.cc | 67 + L1Trigger/TrackFindingTMTT/src/DigitalStub.cc | 250 +++ .../TrackFindingTMTT/src/DigitalTrack.cc | 312 +++ .../TrackFindingTMTT/src/DupFitTrkKiller.cc | 275 +++ L1Trigger/TrackFindingTMTT/src/HTbase.cc | 224 +++ L1Trigger/TrackFindingTMTT/src/HTcell.cc | 140 ++ L1Trigger/TrackFindingTMTT/src/HTrphi.cc | 684 +++++++ L1Trigger/TrackFindingTMTT/src/Histos.cc | 1762 +++++++++++++++++ L1Trigger/TrackFindingTMTT/src/InputData.cc | 155 ++ .../TrackFindingTMTT/src/KFParamsComb.cc | 285 +++ L1Trigger/TrackFindingTMTT/src/KFbase.cc | 968 +++++++++ L1Trigger/TrackFindingTMTT/src/KalmanState.cc | 103 + .../TrackFindingTMTT/src/L1fittedTrack.cc | 41 + .../TrackFindingTMTT/src/Make3Dtracks.cc | 129 ++ L1Trigger/TrackFindingTMTT/src/MiniHTstage.cc | 252 +++ .../TrackFindingTMTT/src/MuxHToutputs.cc | 152 ++ L1Trigger/TrackFindingTMTT/src/Sector.cc | 274 +++ L1Trigger/TrackFindingTMTT/src/Settings.cc | 474 +++++ L1Trigger/TrackFindingTMTT/src/SimpleLR4.cc | 489 +++++ L1Trigger/TrackFindingTMTT/src/Stub.cc | 389 ++++ .../TrackFindingTMTT/src/StubFEWindows.cc | 78 + L1Trigger/TrackFindingTMTT/src/StubKiller.cc | 263 +++ .../TrackFindingTMTT/src/StubWindowSuggest.cc | 118 ++ L1Trigger/TrackFindingTMTT/src/TP.cc | 194 ++ .../TrackFindingTMTT/src/TrackFitFactory.cc | 42 + .../TrackFindingTMTT/src/TrackerModule.cc | 153 ++ L1Trigger/TrackFindingTMTT/src/TrkRZfilter.cc | 291 +++ L1Trigger/TrackFindingTMTT/src/Utility.cc | 184 ++ .../test/L1TrackNtupleMaker_cfg.py | 211 ++ .../TrackFindingTMTT/test/PlotEtaSectors.C | 183 ++ L1Trigger/TrackFindingTMTT/test/chi2dof_cut.C | 112 ++ L1Trigger/TrackFindingTMTT/test/make_stubs.py | 61 + L1Trigger/TrackFindingTMTT/test/plot.C | 132 ++ .../test/tmtt_tf_analysis_cfg.py | 211 ++ 83 files changed, 16647 insertions(+) create mode 100644 L1Trigger/TrackFindingTMTT/BuildFile.xml create mode 100644 L1Trigger/TrackFindingTMTT/README.md create mode 100644 L1Trigger/TrackFindingTMTT/interface/ChiSquaredFit4.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/ChiSquaredFitBase.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/ConverterToTTTrack.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/DegradeBend.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/DigitalStub.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/DigitalTrack.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/DupFitTrkKiller.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/GlobalCacheTMTT.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/HTbase.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/HTcell.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/HTrphi.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/Histos.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/InputData.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/KFParamsComb.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/KFTrackletTrack.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/KFbase.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/KalmanState.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/L1track2D.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/L1track3D.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/L1trackBase.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/Make3Dtracks.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/MiniHTstage.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/MuxHToutputs.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/Sector.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/Settings.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/SimpleLR4.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/Stub.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/StubKiller.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/TP.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/TrackFitFactory.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/TrackerModule.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/TrkRZfilter.h create mode 100644 L1Trigger/TrackFindingTMTT/interface/Utility.h create mode 100644 L1Trigger/TrackFindingTMTT/plugins/BuildFile.xml create mode 100644 L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.cc create mode 100644 L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.h create mode 100644 L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Defaults_cfi.py create mode 100644 L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Ultimate_cff.py create mode 100644 L1Trigger/TrackFindingTMTT/python/TMTrackProducer_cff.py create mode 100644 L1Trigger/TrackFindingTMTT/src/ChiSquaredFit4.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/ChiSquaredFitBase.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/ConverterToTTTrack.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/DegradeBend.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/DigitalStub.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/DigitalTrack.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/DupFitTrkKiller.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/HTbase.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/HTcell.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/HTrphi.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/Histos.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/InputData.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/KFParamsComb.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/KFbase.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/KalmanState.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/L1fittedTrack.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/Make3Dtracks.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/MiniHTstage.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/MuxHToutputs.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/Sector.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/Settings.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/SimpleLR4.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/Stub.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/StubFEWindows.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/StubKiller.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/StubWindowSuggest.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/TP.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/TrackFitFactory.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/TrackerModule.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/TrkRZfilter.cc create mode 100644 L1Trigger/TrackFindingTMTT/src/Utility.cc create mode 100644 L1Trigger/TrackFindingTMTT/test/L1TrackNtupleMaker_cfg.py create mode 100644 L1Trigger/TrackFindingTMTT/test/PlotEtaSectors.C create mode 100644 L1Trigger/TrackFindingTMTT/test/chi2dof_cut.C create mode 100644 L1Trigger/TrackFindingTMTT/test/make_stubs.py create mode 100644 L1Trigger/TrackFindingTMTT/test/plot.C create mode 100644 L1Trigger/TrackFindingTMTT/test/tmtt_tf_analysis_cfg.py diff --git a/L1Trigger/TrackFindingTMTT/BuildFile.xml b/L1Trigger/TrackFindingTMTT/BuildFile.xml new file mode 100644 index 0000000000000..74090f8a441c5 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/BuildFile.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L1Trigger/TrackFindingTMTT/README.md b/L1Trigger/TrackFindingTMTT/README.md new file mode 100644 index 0000000000000..f7c403dfdee44 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/README.md @@ -0,0 +1,139 @@ +Two options: + +1) Run the TMTT L1 tracking algorithm (with detailed internal histos) + + cd L1Trigger/TrackFindingTMTT/test/ + cmsRun -n 4 tmtt_tf_analysis_cfg.py + +editing if necessary variables: GEOMETRY (CMS geometry version), inputMCtxt (MC file), makeStubs (regenerate stubs instead of using those from input MC), outputDataSet (write TTTrack collection to file). + +- Look at the printout from the job. At the end, it prints the number of track candidates reconstructed + and the algorithmic tracking efficiency. + +- Look at the performance histograms Hist.root (explained in class "Histos" below) + +2) cmsRun -n 4 L1TrackNtupleMaker_cfg.py +after editing it to change L1TRACKALGO = 'TMTT'. This writes a TTree of the fitted L1 tracks to .root file, from which tracking performance can be studied with ROOT macro L1Trigger/TrackFindingTracklet/test/L1TrackNtuplePlot.C. Other values of L1TRACKALGO permit to run the Hybrid or Tracklet emulation, or floating point emulation. + +Both (1) & (2) are able to write a dataset containing the TTTrack collection of the fitted tracks. + +N.B. .txt files listing available MC samples can be found in https://github.com/cms-data/L1Trigger-TrackFindingTMTT . + +------------- + +=== Reproducing stubs ===: + +The makeStubs option to remake the stubs is very slow. If you need to do this, it may be better to "cmsRun make_stubs.py" to write the new stubs to disk. + +=== Changing configuration options ===: + +a) The full set of config parameters, which comments explaining what each is, can be found in +specifiied in L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Defaults_cfi.py. + +b) The file mentioned on (a) is imported by +L1Trigger/TrackFindingTMTT/python/TMTrackProducer_cff.py , +which can optionally override the values of some parameters. This file lists the subset of the cfg +parameters that are most useful. + +e.g. Enable tracking down to 2 GeV, enabled displaced tracking etc. + +c) Alternatively, you can use L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Ultimate_cff.py , +which is like (b) but includes improvements not yet available in the firmware, such as reducing the +Pt threshold to 2 GeV. It is suitable for L1 trigger studies. + +d) You can also override cfg parameters in tmtt_tf_analysis_cfg.py . A line there illustrates how. +This file imports TMTrackProducer_cff.py. + +e) The python files in python/ all disable production of histograms & tracking performance summaries to save CPU. However, tmtt_tf_analysis_cfg overrides this default and switches them on via parameters EnableMCtruth & EnableHistos. If you do not care about this analysis information, and only care about producing TTTracks, then keep them switched off. + +------------- + +=== Software structure ===: + +1) Class "TMTrackProducer" -- This is the main routine, which uses classes: "InputData" to unpack the useful +data from the MC dataset, and "Sector" & "HTphi" to do the L1 Hough transform track-finding, +and "Get3Dtracks" to estimate the helix params in 3D, optionally by running an r-z track filter. +It creates matrices of "Sector", "HTphi" & "Get3Dtracks", where the matrix elements each correspond to +a different (phi,eta) sector. It then uses "TrackFitGeneric" to do the track fitting and optionally +"KillDupFitTrks" to remove duplicate tracks after the fit. It employs "Histos" to create the analysis + histograms. + To allow comparison of our tracks with those of the AM & Tracklet groups, it also converts our tracks +to the agree common TTTrack format, with this conversion done by the "ConverterToTTTrack" class. + +2) Class "InputData" -- This unpacks the most useful information from the Stubs and Tracking Particle +(truth particles) collections in the MC dataset, and it for convenient access in the "Stub" and "TP" +classes. Info about tracker silicon modules is stored in "ModuleInfo". The "Stub" class uses a class called "DigitalStub" to digitize and then undigitize again the stub data. This process degrades slightly the resolution, as would happen in the real firmware. The digitisation is optional. It is called from TMTrackProducer after the stubs have been assigned to +sectors. + +3) Class "Sector" -- This knows about the division of the Tracker into (phi,eta) sectors that we use +for the L1 tracking, and decides which of these sectors each stub belongs to. + +4) Class "HTrphi" implements the Hough transforms in the r-phi plane. It inherits from +a base class "HTbase". The Hough transform array is implemented as a matrix of "HTcell" +objects. The HTrphi class stores tracks it finds using the "L1track2D" class. It optionally +uses class "KillDupTrks" to attempt to eliminate duplicate tracks. And optionally, class "MuxHToutputs" +can be used to multiplex the tracks found in different HT arrays (sectors) onto a single output +optical link pair. + +5) Class "HTcell" -- This represents a single cell in an HT array. It provides functions allowing stubs +to be added to this cell, to check if the stubs in a cell give a good track candidate, and to check +if this matches a tracking particle (truth). + +6) Class "Get3Dtracks" makes an estimate of the track parameters in 3D, stored in the "L1track3D" +class, by taking the r-phi track found by the HT, assuming z0 = 0 and that eta is given by the centre +of the eta sector that track is in. Optionally it can also create a second collection of L1track3D, +by running an r-z track filter (from class "TrkRZfilter") on the tracks found by the HT, which gives +cleaner tracks with more precise r-z helix param info. + +7) Class "L1track2D" represents a 2D track, reconstructed in the r-phi or r-z plane by a Hough transform. +Class "L1track3D" represents a 3D tracks, obtained by combining the information in the 2D tracks +from r-phi and r-z Hough transforms. These classes give access to the stubs associated to each track, +to the reconstructed helix parameters, and to the associated truth particle (if any). They represent +the result of the track finding. Both inherit from a pure virtual class L1trackBase, which contains +no functionality but imposes common function names. + +8) Class "KillDupTrks" contains algorithms for killing duplicate tracks found within a single +HT array. Class "KillDupFitTrks" contains algorithms for killing duplicate fitted tracks. + +9) Class "TrkRZfilter" contains r-z track filters, such as the Seed Filter, that check if the stubs +on a track are consistent with a straight line in the r-z plane. + +10) Class "TrackFitGeneric" does one (or more) helix fit(s) to the track candidates, using various +other classes that implement linearized chi2, linear regression or Kalman filter fits. These are: + + - ChiSquared4ParamsApprox (chi2 linear fit, with maths simplified for easier use in FPGA) + - SimpleLR (linear regression fit, which is similar to chi2 fit, but assumes all hits have same uncertainty). + - KFParamsComb: Kalman Filter fit to a 4 or 5 parameter helix. + +The fit also uses a classe to represent the helix state + stubs: KalmanState. + +11) Class "L1fittedTrack" contains the result of running a track fitter (the algorithm for which is +implemented in class "TrackFitAlgo") on the L1track3D track candidate found by the Hough transform. +It gives access to the fitted track parameters and chi2, and via a link to the L1track3D candidate +that produced it, also to the stubs on the track and the associated truth particle (if any). +It inherits from the pure virutal L1trackBase, ensuring it has some common classes with L1track3D and +L1track2D. + +13) Class "DegradeBend" -- This is used by class "Stub" to degrade the resolution on the stub +bend information to that expected in the electronics, as opposed to that currently in CMSSW. + +14) "Utility" -- contains a few useful functions, that are not part of a class. + +15) Class "Settings" -- Reads in the configuration parameters. + +16) Class "Histos" -- Books and fills all the histograms. There are several categories of histograms, +with each category being booked/filled by its own function inside "Histos", and being placed inside its +own ROOT directory in the output histogram file. The categories are "InputData" = plots made with the +Stubs & Tracking Particles; "CheckEtaPhiSectors" = plots checking assignment of stubs to (eta,phi) +sectors; "HT" = plots checking how stubs are stored in the Hough Transform arrays; "TrackCands" = plots +showing number of track candidates found & investigating why tracking sometimes failed, +"EffiAndFakeRate" = plots of tracking efficiency. + +Each user of the code will probably want to book their own set of histograms inside "Histos". So +just consider the version of this class in GIT as a set of examples of how to do things. Don't feel +obliged to understand what every histogram does. + +17) Class "StubKiller" emulates dead modules. It was written to +model the scenarios requested by the Stress Test committee. + +18) Class "GlobalCacheTMTT" contains data shared by all threads. It includes configuration data and histograms. diff --git a/L1Trigger/TrackFindingTMTT/interface/ChiSquaredFit4.h b/L1Trigger/TrackFindingTMTT/interface/ChiSquaredFit4.h new file mode 100644 index 0000000000000..a0b866b4a2ff4 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/ChiSquaredFit4.h @@ -0,0 +1,21 @@ +#ifndef L1Trigger_TrackFindingTMTT_ChiSquaredFit4_h +#define L1Trigger_TrackFindingTMTT_ChiSquaredFit4_h + +#include "L1Trigger/TrackFindingTMTT/interface/ChiSquaredFitBase.h" + +namespace tmtt { + + class ChiSquaredFit4 : public ChiSquaredFitBase { + public: + ChiSquaredFit4(const Settings* settings, const uint nPar); + + protected: + TVectorD seed(const L1track3D& l1track3D) override; + TVectorD residuals(const TVectorD& x) override; + TMatrixD D(const TVectorD& x) override; + TMatrixD Vinv() override; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/ChiSquaredFitBase.h b/L1Trigger/TrackFindingTMTT/interface/ChiSquaredFitBase.h new file mode 100644 index 0000000000000..86aea89b3e387 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/ChiSquaredFitBase.h @@ -0,0 +1,61 @@ +#ifndef L1Trigger_TrackFindingTMTT_ChiSquaredFitBase_h +#define L1Trigger_TrackFindingTMTT_ChiSquaredFitBase_h + +///=== This is the base class for the linearised chi-squared track fit algorithms. + +///=== Written by: Sioni Summers and Alexander D. Morton. + +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include +#include + +#include +#include +#include + +namespace tmtt { + + class ChiSquaredFitBase : public TrackFitGeneric { + public: + enum PAR_IDS { INVR, PHI0, T, Z0, D0 }; + + public: + ChiSquaredFitBase(const Settings* settings, const uint nPar); + + L1fittedTrack fit(const L1track3D& l1track3D) override; + + protected: + /* Methods */ + virtual TVectorD seed(const L1track3D& l1track3D) = 0; + virtual TVectorD residuals(const TVectorD& x) = 0; // Stub residuals/uncertainty + virtual TMatrixD D(const TVectorD& x) = 0; // derivatives + virtual TMatrixD Vinv() = 0; // Covariances + + /* Variables */ + double qOverPt_seed_; + std::vector stubs_; + TVectorD trackParams_; + uint nPar_; + float largestresid_; + int ilargestresid_; + double chiSq_; + + private: + void calculateChiSq(const TVectorD& resids); + void calculateDeltaChiSq(const TVectorD& deltaX, const TVectorD& covX); + + int numFittingIterations_; + int killTrackFitWorstHit_; + double generalResidualCut_; + double killingResidualCut_; + + unsigned int minStubLayers_; + unsigned int minStubLayersRed_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/ConverterToTTTrack.h b/L1Trigger/TrackFindingTMTT/interface/ConverterToTTTrack.h new file mode 100644 index 0000000000000..5c77d9c6833b7 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/ConverterToTTTrack.h @@ -0,0 +1,38 @@ +#ifndef L1Trigger_TrackFindingTMTT_ConverterToTTTrack_h +#define L1Trigger_TrackFindingTMTT_ConverterToTTTrack_h + +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" + +#include "DataFormats/L1TrackTrigger/interface/TTTypes.h" + +//=== Convert non-persistent L1 track collection to the official persistent CMSSW EDM TTTrack format. +//=== Works for both L1track3D and for L1fittedTrk objects. + +namespace tmtt { + + typedef edmNew::DetSetVector > TTStubDetSetVec; + typedef edm::Ref > TTStubRef; + + class ConverterToTTTrack { + public: + // Initialize constants. + ConverterToTTTrack(const Settings* settings) : settings_(settings), invPtToInvR_(settings->invPtToInvR()) {} + + // Convert L1fittedTrack or L1track3D (track candidates after/before fit) to TTTrack format. + TTTrack makeTTTrack(const L1trackBase* trk, + unsigned int iPhiSec, + unsigned int iEtaReg) const; + + private: + // Get references to stubs on track. (Works for either L1track3D or L1fittedTrack). + std::vector stubRefs(const L1trackBase* trk) const; + + private: + const Settings* settings_; // Configuration parameters. + float invPtToInvR_; // converts 1/Pt to 1/radius_of_curvature + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/DegradeBend.h b/L1Trigger/TrackFindingTMTT/interface/DegradeBend.h new file mode 100644 index 0000000000000..66c308061e978 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/DegradeBend.h @@ -0,0 +1,74 @@ +#ifndef L1Trigger_TrackFindingTMTT_DegradeBend_h +#define L1Trigger_TrackFindingTMTT_DegradeBend_h + +#include "L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h" + +#include "L1Trigger/TrackTrigger/interface/TTStubAlgorithm_official.h" +#include "DataFormats/DetId/interface/DetId.h" + +#include + +class TrackerTopology; + +namespace tmtt { + + class DegradeBend { + /* + *------------------------------------------------------------------------------------------------------------------- + * Implements reduced bits to encode stub bend information: 3 bits for PS, 4 bits for 2S, since the Tracker + * doesn't have the bandwidth to output the unreduced data from the FE electronics. + * + * This obtains the stub window sizes from L1Trigger/TrackTrigger/python/TTStubAlgorithmRegister_cfi.py , + * which must be loaded into the cfg file (with the same params used originally to make the stubs). + * + * The TMTT L1 tracking code can optionally tighten these windows further (cfg option "KillLowPtStubs"). + * This gives slightly more granular encoding with Pt > 3 GeV. + * + * TMTT histograms "hisBendFEVsLayerOrRingPS" & "hisBendFEVsLayerOrRing2S" produced by the "Histos" class + * are useful for debugging. *------------------------------------------------------------------------------------------------------------------- + */ + + public: + typedef TTStubAlgorithm_official StubAlgorithmOfficial; + + DegradeBend(const TrackerTopology* trackerTopo, const StubFEWindows* sw, const StubAlgorithmOfficial* stubAlgo) + : theTrackerTopo_(trackerTopo), sw_(sw), stubAlgo_(stubAlgo) {} + + // Given the original bend, flag indicating if this is a PS or 2S module, & detector identifier, + // this return the degraded stub bend, a boolean indicatng if stub bend was outside the assumed window + // size programmed below, and an integer indicating how many values of the original bend + // were grouped together into this single value of the degraded bend. + // + // (Input argument windowFEnew specifies the stub window size that should be used for this stub instead + // of the window sizes specified in TTStubAlgorithmRegister_cfi.py , but it will ONLY replace the latter + // sizes if it windowFEnew is smaller. If you always want to use TTStubAlgorithmRegister_cfi.py, then + // std::set windowFEnew to a large number, such as 99999.). + void degrade(float bend, + bool psModule, + const DetId& stDetId, + float windowFEnew, + float& degradedBend, + unsigned int& numInGroup) const; + + private: + // Does the actual work of degrading the bend. + void work(float bend, + bool psModule, + const DetId& stDetId, + float windowFEnew, + float& degradedBend, + unsigned int& numInGroup, + unsigned int& windowHalfStrips) const; + + private: + const TrackerTopology* theTrackerTopo_; + + // Stub window sizes as encoded in L1Trigger/TrackTrigger/interface/TTStubAlgorithm_official.h + const StubFEWindows* sw_; + + // TTStub produce algo used to make stubs. + const StubAlgorithmOfficial* stubAlgo_; + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/DigitalStub.h b/L1Trigger/TrackFindingTMTT/interface/DigitalStub.h new file mode 100644 index 0000000000000..71a5b1cf94659 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/DigitalStub.h @@ -0,0 +1,172 @@ +#ifndef L1Trigger_TrackFindingTMTT_DigitalStub_h +#define L1Trigger_TrackFindingTMTT_DigitalStub_h + +#include "FWCore/Utilities/interface/Exception.h" +#include +#include + +//=== Digtizes stubs for input to GP, HT & KF + +namespace tmtt { + + class Settings; + + class DigitalStub { + public: + //--- Hybrid tracking: simplified digitization for KF. + + DigitalStub(const Settings* settings, double r, double phi, double z, unsigned int iPhiSec); + + //--- TMTT tracking: + // Initialize stub with floating point stub coords, range of HT m-bin values consistent with bend, + // bend and phi sector. + DigitalStub(const Settings* settings, + double phi_orig, + double r_orig, + double z_orig, + unsigned int mbin_min_orig, + unsigned int mbin_max_orig, + double bend_orig, + unsigned int iPhiSec); + + // Redo phi digitisation assigning stub to a different phi sector; + // (Return arg indicates if any work done). + bool changePhiSec(unsigned int iPhiSec); + + //--- Original floating point stub data before digitization. + double r_orig() const { return r_orig_; } + double rt_orig() const { return rt_orig_; } // r with respect to reference radius + double phi_orig() const { return phi_orig_; } + double phiS_orig() const { return phiS_orig_; } // with respect to centre of sector + double phiN_orig() const { return phiN_orig_; } // with respect to centre of nonant + double z_orig() const { return z_orig_; } + unsigned int mbin_min_orig() const { return mbin_min_orig_; } + unsigned int mbin_max_orig() const { return mbin_max_orig_; } + double bend_orig() const { return bend_orig_; } + + //--- Digitised stub data + + int iDigi_PhiN() const { return iDigi_PhiN_; } + int iDigi_Bend() const { return iDigi_Bend_; } + int iDigi_PhiS() const { return iDigi_PhiS_; } + int mbin_min() const { return mbin_min_; } + int mbin_max() const { return mbin_max_; } + int iDigi_Rt() const { return iDigi_Rt_; } + unsigned int iDigi_R() const { return iDigi_R_; } + int iDigi_Z() const { return iDigi_Z_; } + + //--- floating point stub data following digitisation & undigitisation again + // "GP" indicates valid variable for input to GP etc. If no such name, always valid. + double phiN() const { return phiN_; } + double phi_GP() const { return phi_GP_; } + double bend() const { return bend_; } + double phiS() const { return phiS_; } + double phi_HT_TF() const { return phi_HT_TF_; } + double rt_GP_HT() const { return rt_GP_HT_; } + double r_GP_HT() const { return r_GP_HT_; } + double r_SF_TF() const { return r_SF_TF_; } + double rt_SF_TF() const { return rt_SF_TF_; } + double z() const { return z_; } + + //--- Utility: return phi nonant number corresponding to given phi sector number. + + unsigned int iNonant(unsigned int iPhiSec) const { return floor(iPhiSec * numPhiNonants_ / numPhiSectors_); } + + private: + // Set cfg params. + void setCfgParams(const Settings* settings); + + // Digitize stub + void digitize(unsigned int iPhiSec); + + // Undigitize stub again + void undigitize(unsigned int iPhiSec); + + // Check that stub coords. & bend angle are within assumed digitization range. + void checkInRange() const; + + // Check that digitisation followed by undigitisation doesn't change significantly the stub coordinates. + void checkAccuracy() const; + + private: + //--- Configuration + + // Digitization configuration + int iFirmwareType_; + unsigned int phiSectorBits_; + unsigned int phiSBits_; + double phiSRange_; + unsigned int rtBits_; + double rtRange_; + unsigned int zBits_; + double zRange_; + unsigned int phiNBits_; + double phiNRange_; + unsigned int bendBits_; + double bendRange_; + + // Digitization multipliers + double phiSMult_; + double rtMult_; + double zMult_; + double phiNMult_; + double bendMult_; + + // Number of phi sectors and phi nonants. + unsigned int numPhiSectors_; + unsigned int numPhiNonants_; + // Phi sector and phi nonant width (radians) + double phiSectorWidth_; + double phiNonantWidth_; + // Centre of phi sector 0. + double phiCentreSec0_; + // Centre of this phi sector & nonant. + double phiSectorCentre_; + double phiNonantCentre_; + // Radius from beamline with respect to which stub r coord. is measured. + double chosenRofPhi_; + // Number of q/Pt bins in Hough transform array. + unsigned int nbinsPt_; + // Min. of HT m-bin array in firmware. + int min_array_mbin_; + + // Used to check if new digitisation requests were already done. + unsigned int iPhiSec_done_; + + //--- Original floating point stub data before digitization. + double r_orig_; + double rt_orig_; + double phi_orig_; + double phiS_orig_; + double phiN_orig_; + double z_orig_; + unsigned int mbin_min_orig_; + unsigned int mbin_max_orig_; + double bend_orig_; + + //--- Digitised stub data + + int iDigi_PhiN_; + int iDigi_Bend_; + int iDigi_PhiS_; + int mbin_min_; + int mbin_max_; + int iDigi_Rt_; + unsigned int iDigi_R_; + int iDigi_Z_; + + //--- floating point stub data following digitisation & undigitisation again + double phiN_; + double phi_GP_; + double bend_; + double phiS_; + double phi_HT_TF_; + double rt_GP_HT_; + double r_GP_HT_; + double r_SF_TF_; + double rt_SF_TF_; + double z_; + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/DigitalTrack.h b/L1Trigger/TrackFindingTMTT/interface/DigitalTrack.h new file mode 100644 index 0000000000000..c3e7e7022733c --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/DigitalTrack.h @@ -0,0 +1,231 @@ +#ifndef L1Trigger_TrackFindingTMTT_DigitalTrack_h +#define L1Trigger_TrackFindingTMTT_DigitalTrack_h + +#include "FWCore/Utilities/interface/Exception.h" +#include +#include +#include + +namespace tmtt { + + class Settings; + class L1fittedTrack; + + //==================================================================================================== + /** + * Used to digitize the fitted track helix params. + * WARNING: Digitizes according to common format agreed for KF and SimpleLR4 fitters, + * and uses KF digitisation cfg for all fitters except SimpleLR4. + */ + //==================================================================================================== + + class DigitalTrack { + public: + // Digitize track + DigitalTrack(const Settings* settings, const std::string& fitterName, const L1fittedTrack* fitTrk); + + //--- The functions below return variables post-digitization. + + // half inverse curvature of track. + int iDigi_oneOver2r() const { return iDigi_oneOver2r_; } + int iDigi_d0() const { return iDigi_d0_; } + // measured relative to centre of sector + int iDigi_phi0rel() const { return iDigi_phi0rel_; } + int iDigi_z0() const { return iDigi_z0_; } + int iDigi_tanLambda() const { return iDigi_tanLambda_; } + unsigned int iDigi_chisquaredRphi() const { return iDigi_chisquaredRphi_; } + unsigned int iDigi_chisquaredRz() const { return iDigi_chisquaredRz_; } + + // Digits corresponding to track params with post-fit beam-spot constraint. + int iDigi_oneOver2r_bcon() const { return iDigi_oneOver2r_bcon_; } // half inverse curvature of track. + int iDigi_phi0rel_bcon() const { return iDigi_phi0rel_bcon_; } // measured relative to centre of sector + unsigned int iDigi_chisquaredRphi_bcon() const { return iDigi_chisquaredRphi_bcon_; } + + // Floating point track params derived from digitized info (so with degraded resolution). + float qOverPt() const { return qOverPt_; } + float oneOver2r() const { return oneOver2r_; } // half inverse curvature of track. + float d0() const { return d0_; } + float phi0() const { return phi0_; } + float phi0rel() const { return phi0rel_; } // measured relative to centre of sector + float z0() const { return z0_; } + float tanLambda() const { return tanLambda_; } + float chisquaredRphi() const { return chisquaredRphi_; } + float chisquaredRz() const { return chisquaredRz_; } + + // Floating point track params derived from digitized track params with post-fit beam-spot constraint. + float qOverPt_bcon() const { return qOverPt_bcon_; } + float oneOver2r_bcon() const { return oneOver2r_bcon_; } // half inverse curvature of track. + float phi0_bcon() const { return phi0_bcon_; } + float phi0rel_bcon() const { return phi0rel_bcon_; } // measured relative to centre of sector + float chisquaredRphi_bcon() const { return chisquaredRphi_bcon_; } + + unsigned int iPhiSec() const { return iPhiSec_; } + unsigned int iEtaReg() const { return iEtaReg_; } + int mBinhelix() const { return mBinhelix_; } + int cBinhelix() const { return cBinhelix_; } + unsigned int nlayers() const { return nlayers_; } + int mBinHT() const { return mBin_; } + int cBinHT() const { return cBin_; } + bool accepted() const { return accepted_; } + unsigned int hitPattern() const { return hitPattern_; } + + //--- The functions below give access to the original variables prior to digitization. + //%%% Those common to GP & HT input. + float orig_qOverPt() const { return qOverPt_orig_; } + float orig_oneOver2r() const { return oneOver2r_orig_; } // half inverse curvature of track. + float orig_d0() const { return d0_orig_; } + float orig_phi0() const { return phi0_orig_; } + float orig_phi0rel() const { return phi0rel_orig_; } // measured relative to centre of sector + float orig_z0() const { return z0_orig_; } + float orig_tanLambda() const { return tanLambda_orig_; } + float orig_chisquaredRphi() const { return chisquaredRphi_orig_; } + float orig_chisquaredRz() const { return chisquaredRz_orig_; } + + float tp_pt() const { return tp_pt_; } + float tp_eta() const { return tp_eta_; } + float tp_d0() const { return tp_d0_; } + float tp_phi0() const { return tp_phi0_; } + float tp_tanLambda() const { return tp_tanLambda_; } + float tp_z0() const { return tp_z0_; } + float tp_qoverpt() const { return tp_qoverpt_; } + int tp_index() const { return tp_index_; } + float tp_useForAlgEff() const { return tp_useForAlgEff_; } + float tp_useForEff() const { return tp_useForEff_; } + float tp_pdgId() const { return tp_pdgId_; } + + //--- Utility: return phi nonant number corresponding to given phi sector number. + unsigned int iGetNonant(unsigned int iPhiSec) const { return floor(iPhiSec * numPhiNonants_ / numPhiSectors_); } + + private: + // Load digitisation configuration parameters for the specific track fitter being used here. + void loadDigiCfg(const std::string& fitterName); + + // Digitize track + void makeDigitalTrack(); + + // Check that stub coords. are within assumed digitization range. + void checkInRange() const; + + // Check that digitisation followed by undigitisation doesn't change significantly the track params. + void checkAccuracy() const; + + private: + // Configuration params + const Settings* settings_; + + // Number of phi sectors and phi nonants. + unsigned int numPhiSectors_; + unsigned int numPhiNonants_; + double phiSectorWidth_; + double phiNonantWidth_; + double phiSectorCentre_; + float chosenRofPhi_; + unsigned int nbinsPt_; + float invPtToDPhi_; + + // Digitization configuration + bool skipTrackDigi_; + unsigned int oneOver2rBits_; + float oneOver2rRange_; + unsigned int d0Bits_; + float d0Range_; + unsigned int phi0Bits_; + float phi0Range_; + unsigned int z0Bits_; + float z0Range_; + unsigned int tanLambdaBits_; + float tanLambdaRange_; + unsigned int chisquaredBits_; + float chisquaredRange_; + + double oneOver2rMult_; + double d0Mult_; + double phi0Mult_; + double z0Mult_; + double tanLambdaMult_; + double chisquaredMult_; + + // Fitted track + + std::string fitterName_; + unsigned int nHelixParams_; + + // Integer data after digitization (which doesn't degrade its resolution, but can recast it in a different form). + unsigned int nlayers_; + unsigned int iPhiSec_; + unsigned int iEtaReg_; + int mBin_; + int cBin_; + int mBinhelix_; + int cBinhelix_; + unsigned int hitPattern_; + bool consistent_; + bool consistentSect_; + bool accepted_; + + //--- Original floating point stub coords before digitization. + + float qOverPt_orig_; + float oneOver2r_orig_; + float d0_orig_; + float phi0_orig_; + float phi0rel_orig_; + float tanLambda_orig_; + float z0_orig_; + float chisquaredRphi_orig_; + float chisquaredRz_orig_; + + float qOverPt_bcon_orig_; + float oneOver2r_bcon_orig_; + float phi0_bcon_orig_; + float phi0rel_bcon_orig_; + float chisquaredRphi_bcon_orig_; + + //--- Digits corresponding to track params. + + int iDigi_oneOver2r_; + int iDigi_d0_; + int iDigi_phi0rel_; + int iDigi_z0_; + int iDigi_tanLambda_; + unsigned int iDigi_chisquaredRphi_; + unsigned int iDigi_chisquaredRz_; + + int iDigi_oneOver2r_bcon_; + int iDigi_phi0rel_bcon_; + unsigned int iDigi_chisquaredRphi_bcon_; + + //--- Floating point track coords derived from digitized info (so with degraded resolution). + + float qOverPt_; + float oneOver2r_; + float d0_; + float phi0_; + float phi0rel_; + float z0_; + float tanLambda_; + float chisquaredRphi_; + float chisquaredRz_; + + float qOverPt_bcon_; + float oneOver2r_bcon_; + float phi0_bcon_; + float phi0rel_bcon_; + float chisquaredRphi_bcon_; + + // Truth + float tp_qoverpt_; + float tp_pt_; + float tp_eta_; + float tp_d0_; + float tp_phi0_; + float tp_tanLambda_; + float tp_z0_; + int tp_index_; + bool tp_useForAlgEff_; + bool tp_useForEff_; + int tp_pdgId_; + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/DupFitTrkKiller.h b/L1Trigger/TrackFindingTMTT/interface/DupFitTrkKiller.h new file mode 100644 index 0000000000000..94ac4593f1f27 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/DupFitTrkKiller.h @@ -0,0 +1,58 @@ +#ifndef L1Trigger_TrackFindingTMTT_DupFitTrkKiller_h +#define L1Trigger_TrackFindingTMTT_DupFitTrkKiller_h + +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" + +#include +#include + +/** +* Kill duplicate fitted tracks. +* +* Currently this is intended to run only on tracks found within a single (eta,phi) sector. +*/ + +namespace tmtt { + + class Settings; + + class DupFitTrkKiller { + public: + enum class DupAlgoName { None = 0, Algo1 = 1, Algo2 = 2 }; + + /** + * Make available cfg parameters & specify which algorithm is to be used for duplicate track removal. + */ + DupFitTrkKiller(const Settings* settings); + + /** + * Eliminate duplicate tracks from the input collection, and so return a reduced list of tracks. + */ + std::list filter(const std::list& vecTracks) const; + + private: + /** + * Duplicate removal algorithm that eliminates duplicates + * by requiring that the fitted (q/Pt, phi0) of the track correspond to the same HT cell in which the track + * was originally found by the HT. + */ + std::list filterAlg1(const std::list& tracks) const; + + /** + * Duplicate removal algorithm that eliminates duplicates + * by requiring that no two tracks should have fitted (q/Pt, phi0) that correspond to the same HT + * cell. If they do, then only the first to arrive is kept. + */ + std::list filterAlg2(const std::list& tracks) const; + + // Debug printout of which tracks are duplicates. + void printDuplicateTracks(const std::list& tracks) const; + + private: + const Settings* settings_; // Configuration parameters. + DupAlgoName dupTrkAlg_; // Specifies choice of algorithm for duplicate track removal. + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/GlobalCacheTMTT.h b/L1Trigger/TrackFindingTMTT/interface/GlobalCacheTMTT.h new file mode 100644 index 0000000000000..4698c5f4c3daa --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/GlobalCacheTMTT.h @@ -0,0 +1,60 @@ +#ifndef L1Trigger_TrackFindingTMTT_GlobalCacheTMTT_h +#define L1Trigger_TrackFindingTMTT_GlobalCacheTMTT_h + +// Data shared across all streams by TMTT L1 tracking. +// +// Provides the python configuration parameters +// & optional histogramming/debugging. + +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" +#include "L1Trigger/TrackFindingTMTT/interface/Histos.h" + +#include +#include + +namespace tmtt { + + class GlobalCacheTMTT { + public: + GlobalCacheTMTT(const edm::ParameterSet& iConfig) + : settings_(iConfig), // Python configuration params + htRphiErrMon_(), // rphi HT error monitoring + stubWindowSuggest_(&settings_), // Recommend FE stub window sizes. + hists_(&settings_) // Histograms + { + hists_.book(); + } + + // Get functions + const Settings& settings() const { return settings_; } + HTrphi::ErrorMonitor& htRphiErrMon() const { return htRphiErrMon_; } + StubWindowSuggest& stubWindowSuggest() const { return stubWindowSuggest_; } + const std::list& listTrackerModule() const { return listTrackerModule_; } + Histos& hists() const { return hists_; } + + // Set functions + void setListTrackerModule(const std::list& list) const { + // Allow only one thread to run this function at a time + static std::mutex myMutex; + std::lock_guard myGuard(myMutex); + + // Only need one copy of tracker geometry for histogramming. + if (listTrackerModule_.empty()) + listTrackerModule_ = list; + } + + private: + Settings settings_; + mutable HTrphi::ErrorMonitor htRphiErrMon_; + mutable StubWindowSuggest stubWindowSuggest_; + mutable std::list listTrackerModule_; + mutable Histos hists_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/HTbase.h b/L1Trigger/TrackFindingTMTT/interface/HTbase.h new file mode 100644 index 0000000000000..eb048b10cd687 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/HTbase.h @@ -0,0 +1,147 @@ +#ifndef L1Trigger_TrackFindingTMTT_HTbase_h +#define L1Trigger_TrackFindingTMTT_HTbase_h + +#include "L1Trigger/TrackFindingTMTT/interface/HTcell.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track2D.h" + +#include + +#include +#include +#include +#include + +using boost::numeric::ublas::matrix; + +//=== Base class for Hough Transform array for a single (eta,phi) sector. + +namespace tmtt { + + class Settings; + class Stub; + class TP; + class L1fittedTrack; + + class HTbase { + public: + // Initialization. + HTbase( + const Settings* settings, unsigned int iPhiSec, unsigned int iEtaReg, unsigned int nBinsX, unsigned int nBinsY); + + virtual ~HTbase() = default; + + // Termination. Causes HT array to search for tracks etc. + virtual void end(); + + //=== Get info about filtered stubs in HT array. + //=== (N.B. The `filtered stubs' are stubs passing any requested stub filters, e.g. bend and/or rapidity. + //=== If no filters were requested, they are identical to the unfiltered stubs.) + + // Get sum of number of filtered stubs stored in each cell of HT array (so a stub appearing in multiple cells is counted multiple times). + virtual unsigned int numStubsInc() const; + + // Get sum the number of filtered stubs in the HT array, where each individual stub is counted only once, even if it appears in multiple cells. + virtual unsigned int numStubsExc() const; + + // Get all the cells that make up the array, which in turn give access to the stubs inside them. + // N.B. You can use allCells().size1() and allCells().size2() to get the dimensions ofthe array. + virtual const matrix>& allCells() const { return htArray_; } + + //=== Info about track candidates found. + + // N.B. If a duplicate track filter was run inside the HT, this will contain the reduced list of tracks passing this filter. + // N.B. If some tracks could not be read out during the TM period, then such tracks are deleted from this list. + + // Get list of all track candidates found in this HT array, giving access to stubs on each track + // and helix parameters. + virtual const std::list& trackCands2D() const { return trackCands2D_; } + + // Number of track candidates found in this HT array. + // If a duplicate track filter was run, this will contain the reduced list of tracks passing this filter. + virtual unsigned int numTrackCands2D() const { return trackCands2D_.size(); } + + // Get number of filtered stubs assigned to track candidates found in this HT array. + virtual unsigned int numStubsOnTrackCands2D() const; + + // Get all reconstructed tracks that were associated to the given tracking particle. + // (If the std::vector is empty, then the tracking particle was not reconstructed in this sector). + virtual std::vector assocTrackCands2D(const TP& tp) const; + + //=== Function to replace the collection of 2D tracks found by this HT. + + // (This is used by classes MuxHToutputs & MiniHTstage). + virtual void replaceTrackCands2D(const std::list& newTracks) { trackCands2D_ = newTracks; } + + //=== Utilities + + // Get the values of the track helix params corresponding to middle of a specified HT cell (i,j). + // The helix parameters returned will be those corresponding to the two axes of the HT array. + // So they might be (q/pt, phi65), (eta, z0) or (z110, z0) etc. depending on the configuration. + virtual std::pair helix2Dhough(unsigned int i, unsigned int j) const = 0; + + // Get the values of the track helix params corresponding to middle of a specified HT cell (i,j). + // The helix parameters returned will be always be (q/Pt, phi0) or (tan_lambda, z0), irrespective of + // how the axes of the HT array are defined. + virtual std::pair helix2Dconventional(unsigned int i, unsigned int j) const = 0; + + // Which cell in HT array should this TP be in, based on its true trajectory? + // Returns 999999 in at least one index if TP not expected to be in any cell in this array. + virtual std::pair trueCell(const TP* tp) const = 0; + + // Which cell in HT array should this fitted track be in, based on its fitted trajectory? + // Returns 999999 in at least one index if fitted track not expected to be in any cell in this array. + virtual std::pair cell(const L1fittedTrack* fitTrk) const = 0; + + // Disable filters (used for debugging). + virtual void disableBendFilter(); + + protected: + // Given a range in one of the coordinates specified by coordRange, calculate the corresponding range of bins. The other arguments specify the axis. And also if some cells nominally associated to stub are to be killed. + virtual std::pair convertCoordRangeToBinRange(std::pair coordRange, + unsigned int nBinsAxis, + float coordAxisMin, + float coordAxisBinSize, + unsigned int killSomeHTcells) const; + + private: + // Return a list of all track candidates found in this array, giving access to all the stubs on each one + // and the track helix parameters, plus the associated truth particle (if any). + virtual std::list calcTrackCands2D() const; + + // If requested, kill those tracks in this sector that can't be read out during the time-multiplexed period, because + // the HT has associated too many stubs to tracks. + virtual std::list killTracksBusySec(const std::list& tracks) const = 0; + + // Define the order in which the hardware processes rows of the HT array when it outputs track candidates. + virtual std::vector rowOrder(unsigned int numRows) const = 0; + + // Calculate output opto-link ID from HT, assuming there is no MUX stage. + virtual unsigned int calcOptoLinkID() const { + unsigned int numPhiSecPerNon = settings_->numPhiSectors() / settings_->numPhiNonants(); + return (iEtaReg_ * numPhiSecPerNon + iPhiSec_); + } + + protected: + const Settings* settings_; // configuration parameters. + + unsigned int iPhiSec_; // Sector number. + unsigned int iEtaReg_; + + unsigned int nBinsX_; // Bins in HT array. + unsigned int nBinsY_; + + // Hough transform array. + // This has two dimensions, representing the two track helix parameters being varied. + matrix> htArray_; + + unsigned int optoLinkID_; // ID of opto-link from HT to Track Fitter. + + // List of all track candidates found by HT & their associated properties. + // If a duplicate track filter was run inside the HT, this will contain the reduced list of tracks passing this filter. + // If some tracks could not be read out during the TM period, then such tracks are deleted from this list. + std::list trackCands2D_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/HTcell.h b/L1Trigger/TrackFindingTMTT/interface/HTcell.h new file mode 100644 index 0000000000000..2e20b503be496 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/HTcell.h @@ -0,0 +1,155 @@ +#ifndef L1Trigger_TrackFindingTMTT_HTcell_h +#define L1Trigger_TrackFindingTMTT_HTcell_h + +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include +#include +#include +#include + +//=== A single cell in a Hough Transform array. + +namespace tmtt { + + class Settings; + class Stub; + + class HTcell { + public: + // Initialization with cfg params, + // sector number, rapidity range of current sector, estimated q/Pt of cell, + // and the bin number of the cell along the q/Pt axis of the r-phi HT array, + // and a flag indicating if this cell is the merge of smaller HT cells. + HTcell(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float qOverPt, + unsigned int ibin_qOverPt, + bool mergedCell, + bool miniHTcell = false); + + // Add stub to this cell in HT array. + void store(Stub* stub) { vStubs_.push_back(stub); } + + // Add stub to this cell in HT array, indicating also which subsectors within the sector is consistent with. + void store(Stub* stub, const std::vector& inSubSecs) { + this->store(stub); + subSectors_[stub] = inSubSecs; + if (inSubSecs.size() != numSubSecs_) + throw cms::Exception("LogicError") << "HTcell: Wrong number of subsectors!"; + } + + // Termination. Search for track in this HT cell etc. + void end(); + + //=== Cfg of cell + + // Does this HTcell correspond to merged HT cells (e.g. 2x2)? + bool mergedCell() const { return mergedCell_; } + + //=== Get results + //=== Most of these functions operate on the filtered stubs, which are stubs passing any requested stub filters (e.g. bend filter). + //=== If no filters were requested, they are identical to the unfiltered stubs.) + + // Get filtered stubs in this cell in HT array. + const std::vector& stubs() const { return vFilteredStubs_; } + + // Check if a specific stub is in this cell and survived filtering. + bool stubInCell(const Stub* stub) const { + return (std::count(vFilteredStubs_.begin(), vFilteredStubs_.end(), stub) > 0); + } + + // Check if a specific stub was stored to this cell (without checking if it survived filtering). + bool stubStoredInCell(const Stub* stub) const { return (std::count(vStubs_.begin(), vStubs_.end(), stub) > 0); } + + //-- Return info useful for deciding if there is a track candidate in this cell. + // Number of filtered stubs + unsigned int numStubs() const { return vFilteredStubs_.size(); } + // Number of tracker layers with filtered stubs + unsigned int numLayers() const { return numFilteredLayersInCell_; } + // Number of tracker layers with filtered stubs, requiring all stubs to be in same subsector to be counted. The number returned is the highest layer count found in any of the subsectors in this sector. If subsectors are not used, it is equal to numLayers(). + unsigned int numLayersSubSec() const { return numFilteredLayersInCellBestSubSec_; } + + // Useful for debugging. + unsigned int numUnfilteredStubs() const { return vStubs_.size(); } // Number of unfiltered stubs + + //=== Check if stubs in this cell form valid track candidate. + + // N.B. If subsectors within a sector are not being used, then numFilteredLayersInCellBestSubSec_ = numFilteredLayersInCell_. + // WARNING: If some tracks are killed as the r-phi HT array can't read them out within the TM period, + // killed tracks are still found by this function. It is in HTbase::calcTrackCands2D() that they are killed. + bool trackCandFound() const { + return (numFilteredLayersInCellBestSubSec_ >= + Utility::numLayerCut(Utility::AlgoStep::HT, settings_, iPhiSec_, iEtaReg_, std::abs(qOverPtCell_))); + } + + //=== Disable filters (used for debugging). + + void disableBendFilter() { useBendFilter_ = false; } + + private: + // Calculate how many tracker layers the filtered stubs in this cell are in + unsigned int calcNumFilteredLayers() const { return Utility::countLayers(settings_, vFilteredStubs_); } + + // Calculate how many tracker layers the filter stubs in this cell are in, when only the subset of those stubs + // that are in the specified subsector are counted. + unsigned int calcNumFilteredLayers(unsigned int iSubSec) const; + + // Estimate track bend angle at a given radius, derived using the track q/Pt at the centre of this HT cell, ignoring scattering. + float dphi(float rad) const { return (invPtToDphi_ * rad * qOverPtCell_); } + + // Produce a filtered collection of stubs in this cell that all have consistent bend + std::vector bendFilter(const std::vector& stubs) const; + + // Filter stubs so as to prevent more than specified number of stubs being stored in one cell. + // This reflects finite memory of hardware. + std::vector maxStubCountFilter(const std::vector& stubs) const; + + private: + //=== Configuration parameters + + const Settings* settings_; + + unsigned int iPhiSec_; // Sector number + unsigned int iEtaReg_; + + float etaMinSector_; // rapidity range of this sector. + float etaMaxSector_; + + float qOverPtCell_; // track q/Pt corresponding to centre of this cell. + + // Note bin number of cell along q/Pt axis of r-phi HT array. (Not used for r-z HT). + unsigned int ibin_qOverPt_; + // Is this HT cell the merge of smaller HT cells? + bool mergedCell_; + + // Is this cell in Mini-HT? + bool miniHTcell_; + + float invPtToDphi_; // B*c/2E11 + + // Use filter in each HT cell using only stubs which have consistent bend? + bool useBendFilter_; + // A filter is used each HT cell, which prevents more than the specified number of stubs being stored in the cell. (Reflecting memory limit of hardware). + unsigned int maxStubsInCell_; + + // Number of subsectors (if any) within each sector. + unsigned int numSubSecs_; + + //=== data + + std::vector vStubs_; // Stubs in this cell + std::vector vFilteredStubs_; // Ditto after all requested stub filters (e.g. bend filter) + + unsigned int numFilteredLayersInCell_; + unsigned int numFilteredLayersInCellBestSubSec_; + std::map> subSectors_; // Subsectors in sector stub consistent with. + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/HTrphi.h b/L1Trigger/TrackFindingTMTT/interface/HTrphi.h new file mode 100644 index 0000000000000..72ff72df7511c --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/HTrphi.h @@ -0,0 +1,170 @@ +#ifndef L1Trigger_TrackFindingTMTT_HTrphi_h +#define L1Trigger_TrackFindingTMTT_HTrphi_h + +#include "L1Trigger/TrackFindingTMTT/interface/HTbase.h" + +#include +#include +#include +#include + +//=== The r-phi Hough Transform array for a single (eta,phi) sector. +//=== +//=== Its axes are (q/Pt, phiTrk), where phiTrk is the phi at which the track crosses a +//=== user-configurable radius from the beam-line. + +namespace tmtt { + + class Settings; + class Stub; + class TP; + class L1fittedTrack; + + class HTrphi : public HTbase { + public: + enum class HTshape { square, diamond, hexagon, brick }; + + //--- Error monitoring + + struct ErrorMonitor { + // Maximum |gradient| of line corresponding to any stub. FW assumes it's < 1.0. + std::atomic maxLineGradient; + // Error count when stub added to cell not NE, E or SE of cell stub added to in previous HT column. + std::atomic numErrorsTypeA; + // Error count when stub added to more than 2 cells in one HT column + std::atomic numErrorsTypeB; + // Error count normalisation + std::atomic numErrorsNorm; + }; + + // Initialization with sector number, eta range covered by sector and phi coordinate of its centre. + HTrphi(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float phiCentreSector, + ErrorMonitor* errMon = nullptr); + + ~HTrphi() override = default; + + // Add stub to HT array. + // If eta subsectors are being used within each sector, specify which ones the stub is compatible with. + void store(Stub* stub, const std::vector& inEtaSubSecs); + + // Termination. Causes HT array to search for tracks etc. + // ... function end() is in base class ... + + //=== Info about track candidates found. + + // ... is available via base class ... + + //=== Utilities + + // Get the values of the track helix params corresponding to middle of a specified HT cell (i,j). + // The helix parameters returned will be those corresponding to the two axes of the HT array. + // So they might be (q/pt, phi0) or (q/pt, phi65) etc. depending on the configuration. + std::pair helix2Dhough(unsigned int i, unsigned int j) const override; + + // Get the values of the track helix params corresponding to middle of a specified HT cell (i,j). + // The helix parameters returned will be always be (q/pt, phi0), irrespective of how the axes + // of the HT array are defined. + std::pair helix2Dconventional(unsigned int i, unsigned int j) const override; + + // Which cell in HT array should this TP be in, based on its true trajectory? + // (If TP is outside HT array, it it put in the closest bin inside it). + std::pair trueCell(const TP* tp) const override; + + // Which cell in HT array should this fitted track be in, based on its fitted trajectory? + // Always uses beam-spot constrained trajectory if available. + // (If fitted track is outside HT array, it it put in the closest bin inside it). + std::pair cell(const L1fittedTrack* fitTrk) const override; + + // Check if specified cell has been merged with its 2x2 neighbours into a single cell, + // as it is in low Pt region. + bool mergedCell(unsigned int iQoverPtBin, unsigned int jPhiTrkBin) const; + + // Number of stubs received from GP, irrespective of whether the stub was actually stored in + // a cell in the HT array. + unsigned int nReceivedStubs() const { return nReceivedStubs_; } + + // Determine which m-bin (q/pt) range the specified track is in. (Used if outputting each m bin range on a different opto-link). + unsigned int getMbinRange(const L1track2D& trk) const; + + private: + // For a given Q/Pt bin, find the range of phi bins that a given stub is consistent with. + std::pair iPhiRange(const Stub* stub, + unsigned int iQoverPtBin, + bool debug = false) const; + + // Check that limitations of firmware would not prevent stub being stored correctly in this HT column. + void countFirmwareErrors(unsigned int iQoverPtBin, + unsigned int iPhiTrkBinMin, + unsigned int iPhiTrkBinMax, + unsigned int jPhiTrkBinMinLast, + unsigned int jPhiTrkBinMaxLast); + + // Calculate line |gradient| of stubs in HT array, so can check it doesn't exceed 1. + float calcLineGradArray(float r) const; + + // If requested, kill those tracks in this sector that can't be read out during the time-multiplexed period, because + // the HT has associated too many stubs to tracks. + std::list killTracksBusySec(const std::list& tracks) const override; + + // Define the order in which the hardware processes rows of the HT array when it outputs track candidates. + // Currently corresponds to highest Pt tracks first. + // If two tracks have the same Pt, the -ve charge one is output before the +ve charge one. + std::vector rowOrder(unsigned int numRows) const override; + + private: + float invPtToDphi_; // conversion constant. + + HTshape shape_; + std::vector > > cellCenters_; + + //--- Specifications of HT array. + + float maxAbsQoverPtAxis_; // Max. |q/Pt| covered by HT array. + unsigned int nBinsQoverPtAxis_; // Number of bins in HT array in q/Pt. + float binSizeQoverPtAxis_; // HT array bin size in q/Pt. + + float chosenRofPhi_; // Use phi of track at radius="chosenRofPhi" to define one of the r-phi HT axes. + float phiCentreSector_; // phiTrk angle of centre of this (eta,phi) sector. + float maxAbsPhiTrkAxis_; // Half-width of phiTrk axis in HT array. + unsigned int nBinsPhiTrkAxis_; // Number of bins in HT array in phiTrk axis. + float binSizePhiTrkAxis_; // HT array bin size in phiTrk + // Optionally merge 2x2 neighbouring cells into a single cell at low Pt, to reduce efficiency loss due to scattering. (Used also by mini-HT). + bool enableMerge2x2_; + float minInvPtToMerge2x2_; + + //--- Options when filling HT array. + + // Take all cells in HT array crossed by line corresponding to each stub (= 0) or take only some to reduce rate at cost of efficiency ( > 0) + unsigned int killSomeHTCellsRphi_; + // Options for killing stubs/tracks that cant be sent within time-multiplexed period. + bool busyInputSectorKill_; + bool busySectorKill_; + unsigned int busyInputSectorNumStubs_; + unsigned int busySectorNumStubs_; + std::vector busySectorMbinRanges_; + std::vector busySectorMbinOrder_; + bool busySectorUseMbinRanges_; + bool busySectorUseMbinOrder_; + std::vector busySectorMbinLow_; + std::vector busySectorMbinHigh_; + + // Number of stubs received from GP, irrespective of whether the stub was actually stored in + // a cell in the HT array. + unsigned int nReceivedStubs_; + + // Error monitoring. + ErrorMonitor* errMon_; + + // ... The Hough transform array data is in the base class ... + + // ... The list of found track candidates is in the base class ... + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/Histos.h b/L1Trigger/TrackFindingTMTT/interface/Histos.h new file mode 100644 index 0000000000000..74f4a13acaf44 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/Histos.h @@ -0,0 +1,365 @@ +#ifndef L1Trigger_TrackFindingTMTT_Histos_h +#define L1Trigger_TrackFindingTMTT_Histos_h + +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "CommonTools/UtilAlgos/interface/TFileService.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" + +#include +using boost::numeric::ublas::matrix; + +#include +#include +#include +#include + +class TH1F; +class TH2F; +class TH2Poly; +class TF1; +class TProfile; +class TGraphAsymmErrors; +class TGraph; +class TEfficiency; + +namespace tmtt { + + class InputData; + class TP; + class Sector; + class HTrphi; + class Make3Dtracks; + class L1fittedTrack; + class L1fittedTrk4and5; + + class Histos { + public: + // Store cfg parameters. + Histos(const Settings* settings); + + virtual ~Histos() = default; + + // Book & fill all histograms. + virtual void book(); + virtual void fill(const InputData& inputData, + const matrix>& mSectors, + const matrix>& mHtPhis, + const matrix>& mGet3Dtrks, + const std::map>& mapFinalTracks); + + // Print tracking performance summary & make tracking efficiency histograms. + virtual void endJobAnalysis(const HTrphi::ErrorMonitor* htRphiErrMon = nullptr); + + // Determine "B" parameter, used in GP firmware to allow for tilted modules. + virtual void trackerGeometryAnalysis(const std::list& listTrackerModule); + + // Did user request output histograms via the TFileService in their cfg? + virtual bool available() const { return fs_.isAvailable(); } + + // Should histograms be produced? + virtual bool enabled() const { return (settings_->enableHistos() && available()); } + + protected: + // Book histograms for specific topics. + virtual TFileDirectory bookInputData(); + virtual TFileDirectory bookEtaPhiSectors(); + virtual TFileDirectory bookRphiHT(); + virtual TFileDirectory bookRZfilters(); + virtual TFileDirectory bookTrackCands(const std::string& tName); + virtual std::map bookTrackFitting(); + + // Fill histograms for specific topics. + virtual void fillInputData(const InputData& inputData); + virtual void fillEtaPhiSectors(const InputData& inputData, const matrix>& mSectors); + virtual void fillRphiHT(const matrix>& mHtRphis); + virtual void fillRZfilters(const matrix>& mMake3Dtrks); + virtual void fillTrackCands(const InputData& inputData, + const matrix>& mMake3Dtrks, + const std::string& tName); + virtual void fillTrackCands(const InputData& inputData, + const std::vector& tracks, + const std::string& tName); + virtual void fillTrackFitting(const InputData& inputData, + const std::map>& mapFinalTracks); + + // Produce plots of tracking efficiency after HZ or after r-z track filter (run at end of job) + virtual TFileDirectory plotTrackEfficiency(const std::string& tName); + // Produce plots of tracking efficiency after track fit (run at end of job). + virtual TFileDirectory plotTrackEffAfterFit(const std::string& fitName); + + // For Hybrid tracking + // Produce plots of tracklet seed finding efficiency before track reco + virtual void plotTrackletSeedEfficiency(){}; + // Produce plots of hybrid duplicate removal efficiency after track reco + virtual void plotHybridDupRemovalEfficiency(){}; + + virtual void makeEfficiencyPlot( + TFileDirectory& inputDir, TEfficiency* outputEfficiency, TH1F* pass, TH1F* all, TString name, TString title); + + // Print summary of track-finding performance after track pattern reco. + virtual void printTrackPerformance(const std::string& tName); + + // Print summary of track-finding performance after helix fit for given track fitter. + virtual void printFitTrackPerformance(const std::string& fitName); + + // For Hybrid tracking + // Print summary of seed finding and extrapolation performance during track pattern reco. + virtual void printTrackletSeedFindingPerformance(){}; + + // Print summary of duplicate removal performance after track pattern reco. + virtual void printHybridDupRemovalPerformance(){}; + + protected: + edm::Service fs_; + + // Configuration parameters. + const Settings* settings_; + unsigned int genMinStubLayers_; + unsigned int numPhiSectors_; + unsigned int numEtaRegions_; + float houghMinPt_; + unsigned int houghNbinsPt_; + unsigned int houghNbinsPhi_; + float chosenRofZ_; + std::vector trackFitters_; + std::vector useRZfilter_; + bool ranRZfilter_; + bool resPlotOpt_; + + bool oldSumW2opt_; + + // Histograms of input data. + TH1F* hisStubsVsEta_; + TH1F* hisStubsVsR_; + + TH1F* hisNumLayersPerTP_; + TH1F* hisNumPSLayersPerTP_; + + TProfile* hisStubKillFE_; + TProfile* hisStubIneffiVsInvPt_; + TProfile* hisStubIneffiVsEta_; + TH1F* hisBendStub_; + TH1F* hisBendResStub_; + + // Histograms of B parameter for tilted modules. + TGraph* graphBVsZoverR_; + + // Histograms checking that (eta,phi) sector definition is good. + TH1F* hisNumEtaSecsPerStub_; + TH1F* hisNumPhiSecsPerStub_; + TH1F* hisNumStubsPerSec_; + + // Histograms studying 3D track candidates. + std::map profNumTrackCands_; + std::map profNumTracksVsEta_; + std::map hisNumTracksVsQoverPt_; + std::map hisNumTrksPerNon_; + std::map profStubsOnTracks_; + std::map hisStubsOnTracksPerNon_; + std::map hisStubsPerTrack_; + std::map hisLayersPerTrack_; + + std::map hisNumStubsPerLink_; + std::map profMeanStubsPerLink_; + std::map hisFracMatchStubsOnTracks_; + std::map profDupTracksVsEta_; + std::map profDupTracksVsInvPt_; + + // Histos of track params after HT. + std::map hisQoverPt_; + std::map hisPhi0_; + std::map hisEta_; + std::map hisZ0_; + + // Histograms of track parameter resolution after HT transform. + std::map hisQoverPtRes_; + std::map hisPhi0Res_; + std::map hisEtaRes_; + std::map hisZ0Res_; + + // Histos used for denominator of tracking efficiency plots. + TH1F* hisTPinvptForEff_; + TH1F* hisTPetaForEff_; + TH1F* hisTPphiForEff_; + TH1F* hisTPd0ForEff_; + TH1F* hisTPz0ForEff_; + // + TH1F* hisTPinvptForAlgEff_; + TH1F* hisTPetaForAlgEff_; + TH1F* hisTPphiForAlgEff_; + TH1F* hisTPd0ForAlgEff_; + TH1F* hisTPz0ForAlgEff_; + + // Histograms used to make efficiency plots with 3D track candidates prior to fit. + std::map hisRecoTPinvptForEff_; + std::map hisRecoTPetaForEff_; + std::map hisRecoTPphiForEff_; + std::map hisRecoTPd0ForEff_; + std::map hisRecoTPz0ForEff_; + // + std::map hisPerfRecoTPinvptForEff_; + std::map hisPerfRecoTPetaForEff_; + // + std::map hisRecoTPinvptForAlgEff_; + std::map hisRecoTPetaForAlgEff_; + std::map hisRecoTPphiForAlgEff_; + std::map hisRecoTPd0ForAlgEff_; + std::map hisRecoTPz0ForAlgEff_; + // + std::map hisPerfRecoTPinvptForAlgEff_; + std::map hisPerfRecoTPetaForAlgEff_; + + // Histograms for track fitting evaluation, where std::map index specifies name of track fitting algorithm used. + + std::map profNumFitTracks_; + std::map hisNumFitTrks_; + std::map hisNumFitTrksPerNon_; + std::map hisNumFitTrksPerSect_; + std::map hisStubsPerFitTrack_; + std::map profStubsOnFitTracks_; + + std::map hisFitQinvPtMatched_; + std::map hisFitPhi0Matched_; + std::map hisFitD0Matched_; + std::map hisFitZ0Matched_; + std::map hisFitEtaMatched_; + + std::map hisFitQinvPtUnmatched_; + std::map hisFitPhi0Unmatched_; + std::map hisFitD0Unmatched_; + std::map hisFitZ0Unmatched_; + std::map hisFitEtaUnmatched_; + + std::map hisKalmanNumUpdateCalls_; + std::map hisKalmanChi2DofSkipLay0Matched_; + std::map hisKalmanChi2DofSkipLay1Matched_; + std::map hisKalmanChi2DofSkipLay2Matched_; + std::map hisKalmanChi2DofSkipLay0Unmatched_; + std::map hisKalmanChi2DofSkipLay1Unmatched_; + std::map hisKalmanChi2DofSkipLay2Unmatched_; + + std::map hisFitChi2DofRphiMatched_; + std::map hisFitChi2DofRzMatched_; + std::map profFitChi2DofRphiVsInvPtMatched_; + + std::map hisFitChi2DofRphiUnmatched_; + std::map hisFitChi2DofRzUnmatched_; + std::map profFitChi2DofRphiVsInvPtUnmatched_; + + std::map hisQoverPtResVsTrueEta_; + std::map hisPhi0ResVsTrueEta_; + std::map hisEtaResVsTrueEta_; + std::map hisZ0ResVsTrueEta_; + std::map hisD0ResVsTrueEta_; + + std::map hisQoverPtResVsTrueInvPt_; + std::map hisPhi0ResVsTrueInvPt_; + std::map hisEtaResVsTrueInvPt_; + std::map hisZ0ResVsTrueInvPt_; + std::map hisD0ResVsTrueInvPt_; + + std::map profDupFitTrksVsEta_; + std::map profDupFitTrksVsInvPt_; + + // Histograms used for efficiency plots made with fitted tracks. + std::map hisFitTPinvptForEff_; + std::map hisFitTPetaForEff_; + std::map hisFitTPphiForEff_; + std::map hisFitTPd0ForEff_; + std::map hisFitTPz0ForEff_; + std::map hisPerfFitTPinvptForEff_; + std::map hisPerfFitTPetaForEff_; + std::map hisFitTPinvptForAlgEff_; + std::map hisFitTPetaForAlgEff_; + std::map hisFitTPphiForAlgEff_; + std::map hisFitTPd0ForAlgEff_; + std::map hisFitTPz0ForAlgEff_; + std::map hisPerfFitTPinvptForAlgEff_; + std::map hisPerfFitTPetaForAlgEff_; + + // Histograms of tracking efficiency & fake rate after Hough transform or after r-z track filter. + std::map teffEffVsInvPt_; + std::map teffEffVsEta_; + std::map teffEffVsPhi_; + std::map teffEffVsD0_; + std::map teffEffVsZ0_; + // + std::map teffPerfEffVsInvPt_; + std::map teffPerfEffVsEta_; + std::map teffAlgEffVsD0_; + std::map teffAlgEffVsZ0_; + // + std::map teffAlgEffVsInvPt_; + std::map teffAlgEffVsEta_; + std::map teffAlgEffVsPhi_; + // + std::map teffPerfAlgEffVsInvPt_; + std::map teffPerfAlgEffVsPt_; + std::map teffPerfAlgEffVsEta_; + + // Histograms of tracking efficiency & fake rate after Hough transform based on tracks after the track fit. + std::map teffEffFitVsInvPt_; + std::map teffEffFitVsEta_; + std::map teffEffFitVsPhi_; + std::map teffEffFitVsD0_; + std::map teffEffFitVsZ0_; + // + std::map teffPerfEffFitVsInvPt_; + std::map teffPerfEffFitVsEta_; + // + std::map teffAlgEffFitVsInvPt_; + std::map teffAlgEffFitVsEta_; + std::map teffAlgEffFitVsPhi_; + std::map teffAlgEffFitVsD0_; + std::map teffAlgEffFitVsZ0_; + // + std::map teffPerfAlgEffFitVsInvPt_; + std::map teffPerfAlgEffFitVsEta_; + + // Number of genuine reconstructed and perfectly reconstructed tracks which were fitted. + std::map numFitAlgEff_; + std::map numFitPerfAlgEff_; + + // Number of genuine reconstructed and perfectly reconstructed tracks which were fitted post-cut. + std::map numFitAlgEffPass_; + std::map numFitPerfAlgEffPass_; + + // Range in r of each barrel layer. + std::map mapBarrelLayerMinR_; + std::map mapBarrelLayerMaxR_; + // Range in z of each endcap wheel. + std::map mapEndcapWheelMinZ_; + std::map mapEndcapWheelMaxZ_; + + // Range in (r,z) of each module type. + std::map mapModuleTypeMinR_; + std::map mapModuleTypeMaxR_; + std::map mapModuleTypeMinZ_; + std::map mapModuleTypeMaxZ_; + // Extra std::maps for wierd barrel layers 1-2 & endcap wheels 3-5. + std::map mapExtraAModuleTypeMinR_; + std::map mapExtraAModuleTypeMaxR_; + std::map mapExtraAModuleTypeMinZ_; + std::map mapExtraAModuleTypeMaxZ_; + std::map mapExtraBModuleTypeMinR_; + std::map mapExtraBModuleTypeMaxR_; + std::map mapExtraBModuleTypeMinZ_; + std::map mapExtraBModuleTypeMaxZ_; + std::map mapExtraCModuleTypeMinR_; + std::map mapExtraCModuleTypeMaxR_; + std::map mapExtraCModuleTypeMinZ_; + std::map mapExtraCModuleTypeMaxZ_; + std::map mapExtraDModuleTypeMinR_; + std::map mapExtraDModuleTypeMaxR_; + std::map mapExtraDModuleTypeMinZ_; + std::map mapExtraDModuleTypeMaxZ_; + + bool bApproxMistake_; + + bool printedGeomAnalysis_; + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/InputData.h b/L1Trigger/TrackFindingTMTT/interface/InputData.h new file mode 100644 index 0000000000000..789c059796171 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/InputData.h @@ -0,0 +1,73 @@ +#ifndef L1Trigger_TrackFindingTMTT_InputData_h +#define L1Trigger_TrackFindingTMTT_InputData_h + +#include "FWCore/Framework/interface/EDAnalyzer.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include + +namespace tmtt { + + class Settings; + class StubWindowSuggest; + class DegradeBend; + + //=== Unpacks stub & tracking particle (truth) data into user-friendlier format in Stub & TP classes. + //=== Also makes B-field available to Settings class. + + class InputData { + public: + InputData(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + const Settings* settings, + StubWindowSuggest* stubWindowSuggest, + const DegradeBend* degradeBend, + const TrackerGeometry* trackerGeometry, + const TrackerTopology* trackerTopology, + const std::list& listTrackerModule, + const edm::EDGetTokenT tpToken, + const edm::EDGetTokenT stubToken, + const edm::EDGetTokenT stubTruthToken, + const edm::EDGetTokenT clusterTruthToken, + const edm::EDGetTokenT genJetToken); + + // Info about each tracker module + const std::list& trackerModules() const { return trackerModules_; }; + + // Get tracking particles + const std::list& getTPs() const { return vTPs_; } + // Get stubs that would be output by the front-end readout electronics + const std::list& stubs() const { return vStubs_; } + // Ditto but const + const std::list& stubsConst() const { return vStubsConst_; } + + //--- of minor importance ... + + // Get number of stubs prior to applying tighted front-end readout electronics cuts specified in section StubCuts of Analyze_Defaults_cfi.py. (Only used to measure the efficiency of these cuts). + const std::list& allStubs() const { return vAllStubs_; } + + private: + bool enableMCtruth_; // Notes if job will use MC truth info. + + std::list trackerModules_; // Info about each tracker module. + + std::list vTPs_; // tracking particles + std::list vStubs_; // stubs that would be output by the front-end readout electronics. + std::list vStubsConst_; // ditto but const + + //--- Used for a few minor studies ... + + // all stubs, even those that would fail any tightened front-end readout electronic cuts specified in section StubCuts of Analyze_Defaults_cfi.py. (Only used to measure the efficiency of these cuts). + std::list vAllStubs_; + + // Recommends optimal FE stub window sizes. + StubWindowSuggest* stubWindowSuggest_; + // Degrades bend to allow for FE stub bend encoding. + const DegradeBend* degradeBend_; + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/KFParamsComb.h b/L1Trigger/TrackFindingTMTT/interface/KFParamsComb.h new file mode 100644 index 0000000000000..a87d9c2548e6d --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/KFParamsComb.h @@ -0,0 +1,55 @@ +#ifndef L1Trigger_TrackFindingTMTT_KFParamsComb_h +#define L1Trigger_TrackFindingTMTT_KFParamsComb_h + +#include "L1Trigger/TrackFindingTMTT/interface/KFbase.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" + +///=== This is the Kalman Combinatorial Filter for 4 & 5 helix parameters track fit algorithm. +///=== All variable names & equations come from Fruhwirth KF paper +///=== http://dx.doi.org/10.1016/0168-9002%2887%2990887-4 + +namespace tmtt { + + class KFParamsComb : public KFbase { + public: + KFParamsComb(const Settings* settings, const uint nHelixPar, const std::string& fitterName); + + ~KFParamsComb() override = default; + + protected: + //--- Input data + + // Seed track helix params & covariance matrix + TVectorD seedX(const L1track3D& l1track3D) const override; + TMatrixD seedC(const L1track3D& l1track3D) const override; + + // Stub coordinate measurements & resolution + TVectorD vectorM(const Stub* stub) const override; + TMatrixD matrixV(const Stub* stub, const KalmanState* state) const override; + + //--- KF maths matrix multiplications + + // Derivate of helix intercept point w.r.t. helix params. + TMatrixD matrixH(const Stub* stub) const override; + // Kalman helix ref point extrapolation matrix + TMatrixD matrixF(const Stub* stub, const KalmanState* state) const override; + + // Convert to physical helix params instead of local ones used by KF + TVectorD trackParams(const KalmanState* state) const override; + TVectorD trackParams_BeamConstr(const KalmanState* state, double& chi2rphi) const override; + + // Does helix state pass cuts? + bool isGoodState(const KalmanState& state) const override; + + protected: + std::vector kfLayerVsPtToler_; + std::vector kfLayerVsD0Cut5_; + std::vector kfLayerVsZ0Cut5_; + std::vector kfLayerVsZ0Cut4_; + std::vector kfLayerVsChiSq5_; + std::vector kfLayerVsChiSq4_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/KFTrackletTrack.h b/L1Trigger/TrackFindingTMTT/interface/KFTrackletTrack.h new file mode 100644 index 0000000000000..ae86d424d2df9 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/KFTrackletTrack.h @@ -0,0 +1,250 @@ +#ifndef L1Trigger_TrackFindingTMTT_KFTrackletTrack_h +#define L1Trigger_TrackFindingTMTT_KFTrackletTrack_h + +#include "FWCore/Utilities/interface/Exception.h" +#include "DataFormats/Math/interface/deltaPhi.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1trackBase.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/DigitalTrack.h" + +#include +#include +#include + +//=== This is used uniquely for HYBRID TRACKING. +//=== It is the equivalent of class L1fittedTrack. +//=== +//=== This represents a fitted L1 track candidate found in 3 dimensions. +//=== It gives access to the fitted helix parameters & chi2 etc. +//=== It also calculates & gives access to associated truth particle (Tracking Particle) if any. +//=== It also gives access to the 3D hough-transform track candidate (L1track3D) on which the fit was run. + +namespace tmtt { + + class KFTrackletTrack { + public: + // Store a new fitted track, specifying the input Hough transform track, the stubs used for the fit, + // bit-encoded hit layers, + // the fitted helix parameters & chi2, + // and the number of helix parameters being fitted (=5 if d0 is fitted, or =4 if d0 is not fitted). + // Also specify phi sector and eta region used by track-finding code that this track was in. + // And if track fit declared this to be a valid track (enough stubs left on track after fit etc.). + KFTrackletTrack(const L1track3D* l1track3D, + const std::vector& stubs, + unsigned int hitPattern, + float qOverPt, + float d0, + float phi0, + float z0, + float tanLambda, + float chi2rphi, + float chi2rz, + unsigned int nHelixParam, + unsigned int iPhiSec, + unsigned int iEtaReg, + bool accepted = true) + : l1track3D_(l1track3D), + stubs_(stubs), + hitPattern_(hitPattern), + qOverPt_(qOverPt), + d0_(d0), + phi0_(phi0), + z0_(z0), + tanLambda_(tanLambda), + chi2rphi_(chi2rphi), + chi2rz_(chi2rz), + done_bcon_(false), + qOverPt_bcon_(qOverPt), + d0_bcon_(d0), + phi0_bcon_(phi0), + chi2rphi_bcon_(chi2rphi), + nHelixParam_(nHelixParam), + iPhiSec_(iPhiSec), + iEtaReg_(iEtaReg), + optoLinkID_(l1track3D->optoLinkID()), + nSkippedLayers_(0), + numUpdateCalls_(0), + numIterations_(0), + accepted_(accepted) {} + + //--- Optionally std::set track helix params & chi2 if beam-spot constraint is used (for 5-parameter fit). + void setBeamConstr(float qOverPt_bcon, float phi0_bcon, float chi2rphi_bcon) { + done_bcon_ = true; + qOverPt_bcon_ = qOverPt_bcon; + d0_bcon_ = 0.0, phi0_bcon_ = phi0_bcon; + chi2rphi_bcon_ = chi2rphi_bcon; + } + + //--- Set/get additional info about fitted track that is specific to individual track fit algorithms (KF, LR, chi2) + //--- and is used for debugging/histogramming purposes. + + void setInfoKF(unsigned int nSkippedLayers, unsigned int numUpdateCalls) { + nSkippedLayers_ = nSkippedLayers; + numUpdateCalls_ = numUpdateCalls; + } + + void infoKF(unsigned int& nSkippedLayers, unsigned int& numUpdateCalls) const { + nSkippedLayers = nSkippedLayers_; + numUpdateCalls = numUpdateCalls_; + } + + const L1track3D* l1track3D() const { return l1track3D_; } + + // Get stubs on fitted track (can differ from those on HT track if track fit kicked out stubs with bad residuals) + const std::vector& stubs() const { return stubs_; } + // Get number of stubs on fitted track. + unsigned int numStubs() const { return stubs_.size(); } + // Get number of tracker layers these stubs are in. + unsigned int numLayers() const { return nLayers_; } + // Get number of stubs deleted from track candidate by fitter (because they had large residuals) + unsigned int numKilledStubs() const { return l1track3D_->numStubs() - this->numStubs(); } + + // Get bit-encoded hit pattern (where layer number assigned by increasing distance from origin, according to layers track expected to cross). + unsigned int hitPattern() const { return hitPattern_; } + + //--- Get the fitted track helix parameters. + + float qOverPt() const { return qOverPt_; } + float charge() const { return (qOverPt_ > 0 ? 1 : -1); } + float invPt() const { return std::abs(qOverPt_); } + // Protect pt against 1/pt = 0. + float pt() const { + constexpr float small = 1.0e-6; + return 1. / (small + this->invPt()); + } + float d0() const { return d0_; } + float phi0() const { return phi0_; } + float z0() const { return z0_; } + float tanLambda() const { return tanLambda_; } + float theta() const { return atan2(1., tanLambda_); } // Use atan2 to ensure 0 < theta < pi. + float eta() const { return -log(tan(0.5 * this->theta())); } + + //--- Get the fitted helix parameters with beam-spot constraint. + //--- If constraint not applied (e.g. 4 param fit) then these are identical to unconstrained values. + + bool done_bcon() const { return done_bcon_; } // Was beam-spot constraint aplied? + float qOverPt_bcon() const { return qOverPt_bcon_; } + float charge_bcon() const { return (qOverPt_bcon_ > 0 ? 1 : -1); } + float invPt_bcon() const { return std::abs(qOverPt_bcon_); } + float pt_bcon() const { return 1. / (1.0e-6 + this->invPt_bcon()); } + float phi0_bcon() const { return phi0_bcon_; } + + // Phi and z coordinates at which track crosses "chosenR" values used by r-phi HT and rapidity sectors respectively. + // (Optionally with beam-spot constraint applied). + float phiAtChosenR(bool beamConstraint) const { + if (beamConstraint) { + return reco::deltaPhi(phi0_bcon_ - + asin((settings_->invPtToDphi() * settings_->chosenRofPhi()) * qOverPt_bcon_) - + d0_bcon_ / (settings_->chosenRofPhi()), + 0.); + } else { + return reco::deltaPhi(phi0_ - asin((settings_->invPtToDphi() * settings_->chosenRofPhi()) * qOverPt_) - + d0_ / (settings_->chosenRofPhi()), + 0.); + } + } + float zAtChosenR() const { + return (z0_ + (settings_->chosenRofZ()) * tanLambda_); + } // neglects transverse impact parameter & track curvature. + + // Get the number of helix parameters being fitted (=5 if d0 is fitted or =4 if d0 is not fitted). + float nHelixParam() const { return nHelixParam_; } + + // Get the fit degrees of freedom, chi2 & chi2/DOF + unsigned int numDOF() const { return 2 * this->numStubs() - nHelixParam_; } + unsigned int numDOFrphi() const { return this->numStubs() - (nHelixParam_ - 2); } + unsigned int numDOFrz() const { return this->numStubs() - 2; } + float chi2rphi() const { return chi2rphi_; } + float chi2rz() const { return chi2rz_; } + float chi2() const { return chi2rphi_ + chi2rz_; } + float chi2dof() const { return (this->chi2()) / this->numDOF(); } + + //--- Ditto, but if beam-spot constraint is applied. + //--- If constraint not applied (e.g. 4 param fit) then these are identical to unconstrained values. + unsigned int numDOF_bcon() const { return (this->numDOF() - 1); } + unsigned int numDOFrphi_bcon() const { return (this->numDOFrphi() - 1); } + float chi2rphi_bcon() const { return chi2rphi_bcon_; } + float chi2_bcon() const { return chi2rphi_bcon_ + chi2rz_; } + float chi2dof_bcon() const { return (this->chi2_bcon()) / this->numDOF_bcon(); } + + //--- Get phi sector and eta region used by track finding code that this track is in. + unsigned int iPhiSec() const { return iPhiSec_; } + unsigned int iEtaReg() const { return iEtaReg_; } + + //--- Opto-link ID used to send this track from HT to Track Fitter + unsigned int optoLinkID() const { return optoLinkID_; } + + //--- Get whether the track has been rejected or accepted by the fit + + bool accepted() const { return accepted_; } + + // Digitize track and degrade helix parameter resolution according to effect of digitisation. + void digitizeTrack(const std::string& fitterName); + + // Access to detailed info about digitized track + const DigitalTrack* digitaltrack() const { return digitalTrack_.get(); } + + private: + //--- Configuration parameters + const Settings* settings_; + + //--- The 3D hough-transform track candidate which was fitted. + const L1track3D* l1track3D_; + + //--- The stubs on the fitted track (can differ from those on HT track if fit kicked off stubs with bad residuals) + std::vector stubs_; + unsigned int nLayers_; + + //--- Bit-encoded hit pattern (where layer number assigned by increasing distance from origin, according to layers track expected to cross). + unsigned int hitPattern_; + + //--- The fitted helix parameters and fit chi-squared. + float qOverPt_; + float d0_; + float phi0_; + float z0_; + float tanLambda_; + float chi2rphi_; + float chi2rz_; + + //--- Ditto with beam-spot constraint applied in case of 5-parameter fit, plus boolean to indicate + bool done_bcon_; + float qOverPt_bcon_; + float d0_bcon_; + float phi0_bcon_; + float chi2rphi_bcon_; + + //--- The number of helix parameters being fitted (=5 if d0 is fitted or =4 if d0 is not fitted). + unsigned int nHelixParam_; + + //--- Phi sector and eta region used track finding code that this track was in. + unsigned int iPhiSec_; + unsigned int iEtaReg_; + //--- Opto-link ID from HT to Track Fitter. + unsigned int optoLinkID_; + + //--- Information about its association (if any) to a truth Tracking Particle. + const TP* matchedTP_; + std::vector matchedStubs_; + unsigned int nMatchedLayers_; + + //--- Info specific to KF fitter. + unsigned int nSkippedLayers_; + unsigned int numUpdateCalls_; + //--- Info specific to LR fitter. + int numIterations_; + std::string lostMatchingState_; + std::unordered_map stateCalls_; + + std::shared_ptr digitalTrack_; // Class used to digitize track if required. + + //--- Has the track fit declared this to be a valid track? + bool accepted_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/KFbase.h b/L1Trigger/TrackFindingTMTT/interface/KFbase.h new file mode 100644 index 0000000000000..36ff3642d6764 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/KFbase.h @@ -0,0 +1,170 @@ +#ifndef L1Trigger_TrackFindingTMTT_KFbase_h +#define L1Trigger_TrackFindingTMTT_KFbase_h + +#include "L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/KalmanState.h" +#include +#include + +#include +#include +#include +#include +#include + +class TH1F; +class TH2F; + +///=== This is the base class for the Kalman Combinatorial Filter track fit algorithm. +///=== All variable names & equations come from Fruhwirth KF paper +///=== http://dx.doi.org/10.1016/0168-9002%2887%2990887-4 + +namespace tmtt { + + class TP; + class KalmanState; + + class KFbase : public TrackFitGeneric { + public: + enum PAR_IDS { INV2R, PHI0, T, Z0, D0 }; + enum PAR_IDS_VAR { QOVERPT }; + enum MEAS_IDS { PHI, Z }; + + public: + // Initialize configuration + KFbase(const Settings *settings, const uint nHelixPar, const std::string &fitterName = "", const uint nMeas = 2); + + ~KFbase() override { this->resetStates(); } + + KFbase(const KFbase &kf) = delete; // Disable copy & move of this class. + KFbase(KFbase &&kf) = delete; + KFbase &operator=(const KFbase &kf) = delete; + KFbase &operator=(KFbase &&kf) = delete; + + // Do KF fit + L1fittedTrack fit(const L1track3D &l1track3D) override; + + protected: + // Do KF fit (internal) + const KalmanState *doKF(const L1track3D &l1track3D, const std::vector &stubs, const TP *tpa); + + // Add one stub to a helix state + virtual const KalmanState *kalmanUpdate( + unsigned nSkipped, unsigned layer, Stub *stub, const KalmanState *state, const TP *tp); + + // Create a KalmanState, containing a helix state & next stub it is to be updated with. + const KalmanState *mkState(const L1track3D &candidate, + unsigned nSkipped, + unsigned layer, + const KalmanState *last_state, + const TVectorD &x, + const TMatrixD &pxx, + const TMatrixD &K, + const TMatrixD &dcov, + Stub *stub, + double chi2rphi, + double chi2rz); + + //--- Input data + + // Seed track helix params & covariance matrix + virtual TVectorD seedX(const L1track3D &l1track3D) const = 0; + virtual TMatrixD seedC(const L1track3D &l1track3D) const = 0; + + // Stub coordinate measurements & resolution + virtual TVectorD vectorM(const Stub *stub) const = 0; + virtual TMatrixD matrixV(const Stub *stub, const KalmanState *state) const = 0; + + //--- KF maths matrix multiplications + + // Derivate of helix intercept point w.r.t. helix params. + virtual TMatrixD matrixH(const Stub *stub) const = 0; + // Kalman helix ref point extrapolation matrix + virtual TMatrixD matrixF(const Stub *stub = nullptr, const KalmanState *state = nullptr) const = 0; + // Product of H*C*H(transpose) (where C = helix covariance matrix) + TMatrixD matrixHCHt(const TMatrixD &h, const TMatrixD &c) const; + // Get inverted Kalman R matrix: inverse(V + HCHt) + TMatrixD matrixRinv(const TMatrixD &matH, const TMatrixD &matCref, const TMatrixD &matV) const; + // Kalman gain matrix + TMatrixD getKalmanGainMatrix(const TMatrixD &h, const TMatrixD &pxcov, const TMatrixD &covRinv) const; + + // Residuals of stub with respect to helix. + virtual TVectorD residual(const Stub *stub, const TVectorD &x, double candQoverPt) const; + + // Update helix state & its covariance matrix with new stub + void adjustState(const TMatrixD &K, + const TMatrixD &pxcov, + const TVectorD &x, + const TMatrixD &h, + const TVectorD &delta, + TVectorD &new_x, + TMatrixD &new_xcov) const; + // Update track fit chi2 with new stub + virtual void adjustChi2(const KalmanState *state, + const TMatrixD &covRinv, + const TVectorD &delta, + double &chi2rphi, + double &chi2rz) const; + + //--- Utilities + + // Reset internal data ready for next track. + void resetStates(); + + // Convert to physical helix params instead of local ones used by KF + virtual TVectorD trackParams(const KalmanState *state) const = 0; + // Ditto after applying beam-spot constraint. + virtual TVectorD trackParams_BeamConstr(const KalmanState *state, double &chi2rphi_bcon) const = 0; + + // Get phi of centre of sector containing track. + double sectorPhi() const { + float phiCentreSec0 = -M_PI / float(settings_->numPhiNonants()) + M_PI / float(settings_->numPhiSectors()); + return 2. * M_PI * float(iPhiSec_) / float(settings_->numPhiSectors()) + phiCentreSec0; + } + + // Get KF layer (which is integer representing order layers cross) + virtual unsigned int kalmanLayer( + unsigned int iEtaReg, unsigned int layerIDreduced, bool barrel, float r, float z) const; + // Check if it is unclear whether a particle is expect to cross this layer. + virtual bool kalmanAmbiguousLayer(unsigned int iEtaReg, unsigned int kfLayer); + // KF algo mods to cope with dead tracker layers. + std::set kalmanDeadLayers(bool &remove2PSCut) const; + + // Function to calculate approximation for tilted barrel modules (aka B) copied from Stub class. + float approxB(float z, float r) const; + + // Is this HLS code? + virtual bool isHLS() { return false; }; + + // Helix state pases cuts. + virtual bool isGoodState(const KalmanState &state) const = 0; + + //--- Debug printout + void printTP(const TP *tp) const; + void printStubLayers(const std::vector &stubs, unsigned int iEtaReg) const; + void printStub(const Stub *stub) const; + void printStubs(const std::vector &stubs) const; + + protected: + unsigned nHelixPar_; + unsigned nMeas_; + unsigned numEtaRegions_; + + unsigned int iPhiSec_; + unsigned int iEtaReg_; + + unsigned int numUpdateCalls_; + + // All helix states KF produces for current track. + std::vector> listAllStates_; + + const TP *tpa_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/KalmanState.h b/L1Trigger/TrackFindingTMTT/interface/KalmanState.h new file mode 100644 index 0000000000000..21fa4881d8287 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/KalmanState.h @@ -0,0 +1,101 @@ +#ifndef L1Trigger_TrackFindingTMTT_KalmanState_h +#define L1Trigger_TrackFindingTMTT_KalmanState_h + +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/KFbase.h" +#include +#include + +#include + +///=== Represents helix state & last associated stub. +///=== All variable names & equations come from Fruhwirth KF paper +///=== http://dx.doi.org/10.1016/0168-9002%2887%2990887-4 + +namespace tmtt { + + class KFbase; + class KalmanState; + class Stub; + class Settings; + + class KalmanState { + public: + KalmanState(const Settings *settings, + const L1track3D &candidate, + unsigned nSkipped, + int kLayer, + const KalmanState *last_state, + const TVectorD &vecX, + const TMatrixD &matC, + const TMatrixD &matK, + const TMatrixD &matV, + Stub *stub, + double chi2rphi, + double chi2rz); + + const Settings *settings() const { return settings_; } + // KF layer where next stub to extend this state should be sought. + unsigned nextLayer() const { return (1 + kLayer_); } + // KF layer of last added stub. (-1 if no stubs yet). + int layer() const { return kLayer_; } + bool barrel() const { return barrel_; } + unsigned nSkippedLayers() const { return nSkipped_; } + // Hit coordinates. + double r() const { return r_; } + double z() const { return z_; } + const KalmanState *last_state() const { return last_state_; } + // Helix parameters (1/2R, phi relative to sector, z0, tanLambda) + const TVectorD &vectorX() const { return vecX_; } + // Covariance matrix on helix params. + const TMatrixD &matrixC() const { return matC_; } + // Kalman Gain matrix + const TMatrixD &matrixK() const { return matK_; } + // Hit position covariance matrix. + const TMatrixD &matrixV() const { return matV_; } + // Last added stub + Stub *stub() const { return stub_; } + // Track used to seed KF. + const L1track3D &candidate() const { return l1track3D_; } + + double chi2() const { return chi2rphi_ + chi2rz_; } + double chi2scaled() const { return chi2rphi_ / kalmanChi2RphiScale_ + chi2rz_; } // Improves electron performance. + double chi2rphi() const { return chi2rphi_; } + double chi2rz() const { return chi2rz_; } + unsigned nStubLayers() const { return n_stubs_; } + unsigned int hitPattern() const { return hitPattern_; } // Bit-encoded KF layers the fitted track has stubs in. + + bool good(const TP *tp) const; + double reducedChi2() const; + const KalmanState *last_update_state() const; + std::vector stubs() const; + + void setChi2(double chi2rphi, double chi2rz) { + chi2rphi_ = chi2rphi; + chi2rz_ = chi2rz; + } + + private: + const Settings *settings_; + int kLayer_; + double r_; + double z_; + const KalmanState *last_state_; + TVectorD vecX_; + TMatrixD matC_; + TMatrixD matK_; + TMatrixD matV_; + Stub *stub_; + double chi2rphi_; + double chi2rz_; + unsigned int kalmanChi2RphiScale_; + unsigned n_stubs_; + bool barrel_; + unsigned nSkipped_; + L1track3D l1track3D_; + unsigned int hitPattern_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h b/L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h new file mode 100644 index 0000000000000..f0373166609d3 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h @@ -0,0 +1,412 @@ +#ifndef L1Trigger_TrackFindingTMTT_L1fittedTrack_h +#define L1Trigger_TrackFindingTMTT_L1fittedTrack_h + +#include "FWCore/Utilities/interface/Exception.h" +#include "DataFormats/Math/interface/deltaPhi.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1trackBase.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/Sector.h" +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" +#include "L1Trigger/TrackFindingTMTT/interface/DigitalTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/KFTrackletTrack.h" + +#include +#include +#include +#include +#include + +//=== This represents a fitted L1 track candidate found in 3 dimensions. +//=== It gives access to the fitted helix parameters & chi2 etc. +//=== It also calculates & gives access to associated truth particle (Tracking Particle) if any. +//=== It also gives access to the 3D hough-transform track candidate (L1track3D) on which the fit was run. + +namespace tmtt { + + class L1fittedTrack : public L1trackBase { + public: + // Store a new fitted track, specifying the input Hough transform track, the stubs used for the fit, + // bit-encoded hit layer pattern (numbered by increasing distance from origin), + // the fitted helix parameters & chi2, + // and the number of helix parameters being fitted (=5 if d0 is fitted, or =4 if d0 is not fitted). + // And if track fit declared this to be a valid track (enough stubs left on track after fit etc.). + L1fittedTrack(const Settings* settings, + const L1track3D* l1track3D, + const std::vector& stubs, + unsigned int hitPattern, + float qOverPt, + float d0, + float phi0, + float z0, + float tanLambda, + float chi2rphi, + float chi2rz, + unsigned int nHelixParam, + bool accepted = true) + : L1trackBase(), + settings_(settings), + l1track3D_(l1track3D), + stubs_(stubs), + stubsConst_(stubs_.begin(), stubs_.end()), + hitPattern_(hitPattern), + qOverPt_(qOverPt), + d0_(d0), + phi0_(phi0), + z0_(z0), + tanLambda_(tanLambda), + chi2rphi_(chi2rphi), + chi2rz_(chi2rz), + done_bcon_(false), + qOverPt_bcon_(qOverPt), + d0_bcon_(d0), + phi0_bcon_(phi0), + chi2rphi_bcon_(chi2rphi), + nHelixParam_(nHelixParam), + nSkippedLayers_(0), + numUpdateCalls_(0), + numIterations_(0), + accepted_(accepted) { + if (l1track3D != nullptr) { + iPhiSec_ = l1track3D->iPhiSec(); + iEtaReg_ = l1track3D->iEtaReg(); + optoLinkID_ = l1track3D->optoLinkID(); + } else { // Rejected track + iPhiSec_ = 0; + iEtaReg_ = 0; + optoLinkID_ = 0; + } + if (settings != nullptr) { + // Count tracker layers these stubs are in + nLayers_ = Utility::countLayers(settings, stubs_); + // Find associated truth particle & calculate info about match. + matchedTP_ = Utility::matchingTP(settings, stubs_, nMatchedLayers_, matchedStubs_); + } else { // Rejected track + nLayers_ = 0; + matchedTP_ = nullptr; + } + // Set d0 = 0 for 4 param fit, in case fitter didn't do it. + if (nHelixParam == 4) { + d0_ = 0.; + d0_bcon_ = 0.; + } + if (settings != nullptr && not settings->hybrid()) { + //Sector class used to check if fitted track trajectory is in expected sector. + secTmp_ = std::make_shared(settings, iPhiSec_, iEtaReg_); + // HT class used to identify HT cell that corresponds to fitted helix parameters. + htRphiTmp_ = std::make_shared( + settings, iPhiSec_, iEtaReg_, secTmp_->etaMin(), secTmp_->etaMax(), secTmp_->phiCentre()); + this->setConsistentHTcell(); + } else { + consistentCell_ = false; + } + } + + // Creates track rejected by fitter. + L1fittedTrack() : L1fittedTrack(nullptr, nullptr, noStubs_, 0, 0., 0., 0., 0., 0., 0., 0., 0, false) {} + + ~L1fittedTrack() override = default; + + //--- Optionally std::set track helix params & chi2 if beam-spot constraint is used (for 5-parameter fit). + void setBeamConstr(float qOverPt_bcon, float phi0_bcon, float chi2rphi_bcon) { + done_bcon_ = true; + qOverPt_bcon_ = qOverPt_bcon; + d0_bcon_ = 0.0, phi0_bcon_ = phi0_bcon; + chi2rphi_bcon_ = chi2rphi_bcon; + } + + //--- Set/get additional info about fitted track that is specific to individual track fit algorithms (KF, LR, chi2) + //--- and is used for debugging/histogramming purposes. + + void setInfoKF(unsigned int nSkippedLayers, unsigned int numUpdateCalls) { + nSkippedLayers_ = nSkippedLayers; + numUpdateCalls_ = numUpdateCalls; + } + void setInfoLR(int numIterations, std::string lostMatchingState, std::unordered_map stateCalls) { + numIterations_ = numIterations; + lostMatchingState_ = lostMatchingState; + stateCalls_ = stateCalls; + } + void setInfoCHI2() {} + + void infoKF(unsigned int& nSkippedLayers, unsigned int& numUpdateCalls) const { + nSkippedLayers = nSkippedLayers_; + numUpdateCalls = numUpdateCalls_; + } + void infoLR(int& numIterations, + std::string& lostMatchingState, + std::unordered_map& stateCalls) const { + numIterations = numIterations_; + lostMatchingState = lostMatchingState_; + stateCalls = stateCalls_; + } + void infoCHI2() const {} + + //--- Convert fitted track to KFTrackletTrack format, for use with HYBRID. + + KFTrackletTrack returnKFTrackletTrack() { + KFTrackletTrack trk_(l1track3D(), + stubsConst(), + hitPattern(), + qOverPt(), + d0(), + phi0(), + z0(), + tanLambda(), + chi2rphi(), + chi2rz(), + nHelixParam(), + iPhiSec(), + iEtaReg(), + accepted()); + return trk_; + } + + //--- Get the 3D Hough transform track candididate corresponding to the fitted track, + //--- Provide direct access to some of the info it contains. + + // Get track candidate from HT (before fit). + const L1track3D* l1track3D() const { return l1track3D_; } + + // Get stubs on fitted track (can differ from those on HT track if track fit kicked out stubs with bad residuals) + const std::vector& stubsConst() const override { return stubsConst_; } + const std::vector& stubs() const override { return stubs_; } + // Get number of stubs on fitted track. + unsigned int numStubs() const override { return stubs_.size(); } + // Get number of tracker layers these stubs are in. + unsigned int numLayers() const override { return nLayers_; } + // Get number of stubs deleted from track candidate by fitter (because they had large residuals) + unsigned int numKilledStubs() const { return l1track3D_->numStubs() - this->numStubs(); } + // Get bit-encoded hit pattern (where layer number assigned by increasing distance from origin, according to layers track expected to cross). + unsigned int hitPattern() const { return hitPattern_; } + + // Get Hough transform cell locations in units of bin number, corresponding to the fitted helix parameters of the track. + // Always uses the beam-spot constrained helix params if they are available. + // (If fitted track is outside HT array, it it put in the closest bin inside it). + std::pair cellLocationFit() const { return htRphiTmp_->cell(this); } + // Also get HT cell determined by Hough transform. + std::pair cellLocationHT() const override { return l1track3D_->cellLocationHT(); } + + //--- Get information about its association (if any) to a truth Tracking Particle. + //--- Can differ from that of corresponding HT track, if track fit kicked out stubs with bad residuals. + + // Get best matching tracking particle (=nullptr if none). + const TP* matchedTP() const override { return matchedTP_; } + // Get the matched stubs with this Tracking Particle + const std::vector& matchedStubs() const override { return matchedStubs_; } + // Get number of matched stubs with this Tracking Particle + unsigned int numMatchedStubs() const override { return matchedStubs_.size(); } + // Get number of tracker layers with matched stubs with this Tracking Particle + unsigned int numMatchedLayers() const override { return nMatchedLayers_; } + // Get purity of stubs on track (i.e. fraction matching best Tracking Particle) + float purity() const { return numMatchedStubs() / float(numStubs()); } + // Get number of stubs matched to correct TP that were deleted from track candidate by fitter. + unsigned int numKilledMatchedStubs() const { + unsigned int nStubCount = l1track3D_->numMatchedStubs(); + if (nStubCount > 0) { // Original HT track candidate did match a truth particle + const TP* tp = l1track3D_->matchedTP(); + for (const Stub* s : stubs_) { + std::set assTPs = s->assocTPs(); + if (assTPs.find(tp) != assTPs.end()) + nStubCount--; // We found a stub matched to original truth particle that survived fit. + } + } + return nStubCount; + } + + //--- Get the fitted track helix parameters. + + float qOverPt() const override { return qOverPt_; } + float charge() const { return (qOverPt_ > 0 ? 1 : -1); } + float invPt() const { return std::abs(qOverPt_); } + // Protect pt against 1/pt = 0. + float pt() const { + constexpr float small = 1.0e-6; + return 1. / (small + this->invPt()); + } + float d0() const { return d0_; } + float phi0() const override { return phi0_; } + float z0() const { return z0_; } + float tanLambda() const { return tanLambda_; } + float theta() const { return atan2(1., tanLambda_); } // Use atan2 to ensure 0 < theta < pi. + float eta() const { return -log(tan(0.5 * this->theta())); } + + //--- Get the fitted helix parameters with beam-spot constraint. + //--- If constraint not applied (e.g. 4 param fit) then these are identical to unconstrained values. + + bool done_bcon() const { return done_bcon_; } // Was beam-spot constraint aplied? + float qOverPt_bcon() const { return qOverPt_bcon_; } + float charge_bcon() const { return (qOverPt_bcon_ > 0 ? 1 : -1); } + float invPt_bcon() const { return std::abs(qOverPt_bcon_); } + // Protect pt against 1/pt = 0. + float pt_bcon() const { + constexpr float small = 1.0e-6; + return 1. / (small + this->invPt_bcon()); + } + float phi0_bcon() const { return phi0_bcon_; } + + // Phi and z coordinates at which track crosses "chosenR" values used by r-phi HT and rapidity sectors respectively. + // (Optionally with beam-spot constraint applied). + float phiAtChosenR(bool beamConstraint) const { + if (beamConstraint) { + return reco::deltaPhi(phi0_bcon_ - ((settings_->invPtToDphi() * settings_->chosenRofPhi()) * qOverPt_bcon_) - + d0_bcon_ / (settings_->chosenRofPhi()), + 0.); + } else { + return reco::deltaPhi(phi0_ - ((settings_->invPtToDphi() * settings_->chosenRofPhi()) * qOverPt_) - + d0_ / (settings_->chosenRofPhi()), + 0.); + } + } + float zAtChosenR() const { + return (z0_ + (settings_->chosenRofZ()) * tanLambda_); + } // neglects transverse impact parameter & track curvature. + + // Get the number of helix parameters being fitted (=5 if d0 is fitted or =4 if d0 is not fitted). + float nHelixParam() const { return nHelixParam_; } + + // Get the fit degrees of freedom, chi2 & chi2/DOF (also in r-phi & r-z planes). + unsigned int numDOF() const { return 2 * this->numStubs() - nHelixParam_; } + unsigned int numDOFrphi() const { return this->numStubs() - (nHelixParam_ - 2); } + unsigned int numDOFrz() const { return this->numStubs() - 2; } + float chi2rphi() const { return chi2rphi_; } + float chi2rz() const { return chi2rz_; } + float chi2() const { return chi2rphi_ + chi2rz_; } + float chi2dof() const { return (this->chi2()) / this->numDOF(); } + + //--- Ditto, but if beam-spot constraint is applied. + //--- If constraint not applied (e.g. 4 param fit) then these are identical to unconstrained values. + unsigned int numDOF_bcon() const { return (this->numDOF() - 1); } + unsigned int numDOFrphi_bcon() const { return (this->numDOFrphi() - 1); } + float chi2rphi_bcon() const { return chi2rphi_bcon_; } + float chi2_bcon() const { return chi2rphi_bcon_ + chi2rz_; } + float chi2dof_bcon() const { return (this->chi2_bcon()) / this->numDOF_bcon(); } + + //--- Get phi sector and eta region used by track finding code that this track is in. + unsigned int iPhiSec() const override { return iPhiSec_; } + unsigned int iEtaReg() const override { return iEtaReg_; } + + //--- Opto-link ID used to send this track from HT to Track Fitter + unsigned int optoLinkID() const override { return optoLinkID_; } + + //--- Get whether the track has been rejected or accepted by the fit + + bool accepted() const { return accepted_; } + + //--- Functions to help eliminate duplicate tracks. + + // Is the fitted track trajectory should lie within the same HT cell in which the track was originally found? + bool consistentHTcell() const { return consistentCell_; } + + // Determine if the fitted track trajectory should lie within the same HT cell in which the track was originally found? + void setConsistentHTcell() { + // Use helix params with beam-spot constaint if done in case of 5 param fit. + + std::pair htCell = this->cellLocationHT(); + bool consistent = (htCell == this->cellLocationFit()); + + if (l1track3D_->mergedHTcell()) { + // If this is a merged cell, check other elements of merged cell. + std::pair htCell10(htCell.first + 1, htCell.second); + std::pair htCell01(htCell.first, htCell.second + 1); + std::pair htCell11(htCell.first + 1, htCell.second + 1); + if (htCell10 == this->cellLocationFit()) + consistent = true; + if (htCell01 == this->cellLocationFit()) + consistent = true; + if (htCell11 == this->cellLocationFit()) + consistent = true; + } + + consistentCell_ = consistent; + } + + // Is the fitted track trajectory within the same (eta,phi) sector of the HT used to find it? + bool consistentSector() const { + bool insidePhi = + (std::abs(reco::deltaPhi(this->phiAtChosenR(done_bcon_), secTmp_->phiCentre())) < secTmp_->sectorHalfWidth()); + bool insideEta = + (this->zAtChosenR() > secTmp_->zAtChosenR_Min() && this->zAtChosenR() < secTmp_->zAtChosenR_Max()); + return (insidePhi && insideEta); + } + + // Digitize track and degrade helix parameter resolution according to effect of digitisation. + void digitizeTrack(const std::string& fitterName); + + // Access to detailed info about digitized track. (Gets nullptr if trk not digitized) + const DigitalTrack* digitaltrack() const { return digitalTrack_.get(); } + + private: + //--- Configuration parameters + const Settings* settings_; + + //--- The 3D hough-transform track candidate which was fitted. + const L1track3D* l1track3D_; + + //--- The stubs on the fitted track (can differ from those on HT track if fit kicked off stubs with bad residuals) + std::vector stubs_; + std::vector stubsConst_; + unsigned int nLayers_; + + //--- Bit-encoded hit pattern (where layer number assigned by increasing distance from origin, according to layers track expected to cross). + unsigned int hitPattern_; + + //--- The fitted helix parameters and fit chi-squared. + float qOverPt_; + float d0_; + float phi0_; + float z0_; + float tanLambda_; + float chi2rphi_; + float chi2rz_; + + //--- Ditto with beam-spot constraint applied in case of 5-parameter fit, plus boolean to indicate + bool done_bcon_; + float qOverPt_bcon_; + float d0_bcon_; + float phi0_bcon_; + float chi2rphi_bcon_; + + //--- The number of helix parameters being fitted (=5 if d0 is fitted or =4 if d0 is not fitted). + unsigned int nHelixParam_; + + //--- Phi sector and eta region used track finding code that this track was in. + unsigned int iPhiSec_; + unsigned int iEtaReg_; + //--- Opto-link ID from HT to Track Fitter. + unsigned int optoLinkID_; + + //--- Information about its association (if any) to a truth Tracking Particle. + const TP* matchedTP_; + std::vector matchedStubs_; + unsigned int nMatchedLayers_; + + //--- Sector class used to check if fitted track trajectory is in same sector as HT used to find it. + std::shared_ptr secTmp_; // shared so as to allow copy of L1fittedTrack. + //--- r-phi HT class used to determine HT cell location that corresponds to fitted track helix parameters. + std::shared_ptr htRphiTmp_; + + //--- Info specific to KF fitter. + unsigned int nSkippedLayers_; + unsigned int numUpdateCalls_; + //--- Info specific to LR fitter. + int numIterations_; + std::string lostMatchingState_; + std::unordered_map stateCalls_; + + std::shared_ptr digitalTrack_; // Class used to digitize track if required. + + static const std::vector noStubs_; // Empty vector used to initialize rejected tracks. + + bool consistentCell_; + + //--- Has the track fit declared this to be a valid track? + bool accepted_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/L1track2D.h b/L1Trigger/TrackFindingTMTT/interface/L1track2D.h new file mode 100644 index 0000000000000..45353709284fb --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/L1track2D.h @@ -0,0 +1,141 @@ +#ifndef L1Trigger_TrackFindingTMTT_L1track2D_h +#define L1Trigger_TrackFindingTMTT_L1track2D_h + +#include "FWCore/Utilities/interface/Exception.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1trackBase.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" + +#include +#include + +//=== L1 track cand found in 2 dimensions. +//=== Gives access to all stubs on track and to its 2D helix parameters. +//=== Also calculates & gives access to associated truth particle (Tracking Particle) if any. + +namespace tmtt { + + class L1track2D : public L1trackBase { + public: + // Give stubs on track, its cell location inside HT array, its 2D helix parameters. + L1track2D(const Settings* settings, + const std::vector& stubs, + std::pair cellLocationHT, + std::pair helix2D, + unsigned int iPhiSec, + unsigned int iEtaReg, + unsigned int optoLinkID, + bool mergedHTcell) + : L1trackBase(), + settings_(settings), + stubs_(stubs), + stubsConst_(stubs_.begin(), stubs_.end()), + cellLocationHT_(cellLocationHT), + helix2D_(helix2D), + estValid_(false), + estZ0_(0.), + estTanLambda_(0.), + iPhiSec_(iPhiSec), + iEtaReg_(iEtaReg), + optoLinkID_(optoLinkID), + mergedHTcell_(mergedHTcell) { + nLayers_ = Utility::countLayers(settings, stubs_); // Count tracker layers these stubs are in + matchedTP_ = Utility::matchingTP(settings, + stubs_, + nMatchedLayers_, + matchedStubs_); // Find associated truth particle & calculate info about match. + } + + ~L1track2D() override = default; + + //--- Get information about the reconstructed track. + + // Get stubs on track candidate. + const std::vector& stubsConst() const override { return stubsConst_; } + const std::vector& stubs() const override { return stubs_; } + // Get number of stubs on track candidate. + unsigned int numStubs() const override { return stubs_.size(); } + // Get number of tracker layers these stubs are in. + unsigned int numLayers() const override { return nLayers_; } + // Get cell location of track candidate in Hough Transform array in units of bin number. + std::pair cellLocationHT() const override { return cellLocationHT_; } + // The two conventionally agreed track helix parameters relevant in this 2D plane. + // i.e. (q/Pt, phi0). + std::pair helix2D() const { return helix2D_; } + + //--- User-friendlier access to the helix parameters obtained from track location inside HT array. + + float qOverPt() const override { return helix2D_.first; } + float phi0() const override { return helix2D_.second; } + + //--- In the case of tracks found by the r-phi HT, a rough estimate of the (z0, tan_lambda) may be provided by any r-z + //--- track filter run after the r-phi HT. These two functions give std::set/get access to these. + //--- The "get" function returns a boolean indicating if an estimate exists (i.e. "set" has been called). + + void setTrkEstZ0andTanLam(float estZ0, float estTanLambda) { + estZ0_ = estZ0; + estTanLambda_ = estTanLambda; + estValid_ = true; + } + + bool trkEstZ0andTanLam(float& estZ0, float& estTanLambda) const { + estZ0 = estZ0_; + estTanLambda = estTanLambda_; + return estValid_; + } + + //--- Get phi sector and eta region used by track finding code that this track is in. + unsigned int iPhiSec() const override { return iPhiSec_; } + unsigned int iEtaReg() const override { return iEtaReg_; } + + //--- Opto-link ID used to send this track from HT to Track Fitter. Both read & write functions. + unsigned int optoLinkID() const override { return optoLinkID_; } + void setOptoLinkID(unsigned int linkID) { optoLinkID_ = linkID; } + + //--- Was this track produced from a marged HT cell (e.g. 2x2)? + bool mergedHTcell() const { return mergedHTcell_; } + + //--- Get information about its association (if any) to a truth Tracking Particle. + + // Get matching tracking particle (=nullptr if none). + const TP* matchedTP() const override { return matchedTP_; } + // Get the matched stubs. + const std::vector& matchedStubs() const override { return matchedStubs_; } + // Get number of matched stubs. + unsigned int numMatchedStubs() const override { return matchedStubs_.size(); } + // Get number of tracker layers with matched stubs. + unsigned int numMatchedLayers() const override { return nMatchedLayers_; } + + private: + //--- Configuration parameters + const Settings* settings_; + + //--- Information about the reconstructed track from Hough transform. + std::vector stubs_; + std::vector stubsConst_; + unsigned int nLayers_; + std::pair cellLocationHT_; + std::pair helix2D_; + + //--- Rough estimate of r-z track parameters from r-z filter, which may be present in case of r-phi Hough transform + bool estValid_; + float estZ0_; + float estTanLambda_; + + unsigned int iPhiSec_; + unsigned int iEtaReg_; + unsigned int optoLinkID_; + + bool mergedHTcell_; + + //--- Information about its association (if any) to a truth Tracking Particle. + const TP* matchedTP_; + std::vector matchedStubs_; + unsigned int nMatchedLayers_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/L1track3D.h b/L1Trigger/TrackFindingTMTT/interface/L1track3D.h new file mode 100644 index 0000000000000..3b3b7dc77e3a2 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/L1track3D.h @@ -0,0 +1,283 @@ +#ifndef L1Trigger_TrackFindingTMTT_L1track3D_h +#define L1Trigger_TrackFindingTMTT_L1track3D_h + +#include "DataFormats/Math/interface/deltaPhi.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1trackBase.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/Sector.h" +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" + +#include +#include +#include +#include + +//=== L1 track candidate found in 3 dimensions. +//=== Gives access to all stubs on track and to its 3D helix parameters. +//=== Also calculates & gives access to associated truth particle (Tracking Particle) if any. + +namespace tmtt { + + class L1track3D : public L1trackBase { + public: + // Seeding layers of tracklet pattern reco. + enum TrackletSeedType { L1L2, L2L3, L3L4, L5L6, D1D2, D3D4, L1D1, L2D1, L3L4L2, L5L6L4, L2L3D1, D1D2L2, NONE }; + + public: + L1track3D(const Settings* settings, + const std::vector& stubs, + std::pair cellLocationHT, + std::pair helixRphi, + std::pair helixRz, + float helixD0, + unsigned int iPhiSec, + unsigned int iEtaReg, + unsigned int optoLinkID, + bool mergedHTcell) + : L1trackBase(), + settings_(settings), + stubs_(stubs), + stubsConst_(stubs_.begin(), stubs_.end()), + cellLocationHT_(cellLocationHT), + helixRphi_(helixRphi), + helixRz_(helixRz), + helixD0_(helixD0), + iPhiSec_(iPhiSec), + iEtaReg_(iEtaReg), + optoLinkID_(optoLinkID), + mergedHTcell_(mergedHTcell), + seedLayerType_(TrackletSeedType::NONE), + seedPS_(999) { + nLayers_ = Utility::countLayers(settings, stubs_); // Count tracker layers these stubs are in + matchedTP_ = Utility::matchingTP(settings, + stubs_, + nMatchedLayers_, + matchedStubs_); // Find associated truth particle & calculate info about match. + } + + // TMTT tracking: constructor + + L1track3D(const Settings* settings, + const std::vector& stubs, + std::pair cellLocationHT, + std::pair helixRphi, + std::pair helixRz, + unsigned int iPhiSec, + unsigned int iEtaReg, + unsigned int optoLinkID, + bool mergedHTcell) + : L1track3D( + settings, stubs, cellLocationHT, helixRphi, helixRz, 0.0, iPhiSec, iEtaReg, optoLinkID, mergedHTcell) {} + + ~L1track3D() override = default; + + //--- Set/get optional info for tracklet tracks. + + // Tracklet seeding layer pair (from Tracklet::calcSeedIndex()) + void setSeedLayerType(unsigned int seedLayerType) { seedLayerType_ = static_cast(seedLayerType); } + TrackletSeedType seedLayerType() const { return seedLayerType_; } + + // Tracklet seed stub pair uses PS modules (from FPGATracket::PSseed()) + void setSeedPS(unsigned int seedPS) { seedPS_ = seedPS; } + unsigned int seedPS() const { return seedPS_; } + + // Best stub (stub with smallest Phi residual in each layer/disk) + void setBestStubs(std::unordered_set bestStubs) { bestStubs_ = bestStubs; } + std::unordered_set bestStubs() const { return bestStubs_; } + + //--- Get information about the reconstructed track. + + // Get stubs on track candidate. + const std::vector& stubsConst() const override { return stubsConst_; } + const std::vector& stubs() const override { return stubs_; } + // Get number of stubs on track candidate. + unsigned int numStubs() const override { return stubs_.size(); } + // Get number of tracker layers these stubs are in. + unsigned int numLayers() const override { return nLayers_; } + // Get cell location of track candidate in r-phi Hough Transform array in units of bin number. + std::pair cellLocationHT() const override { return cellLocationHT_; } + // The two conventionally agreed track helix parameters relevant in r-phi plane. i.e. (q/Pt, phi0) + std::pair helixRphi() const { return helixRphi_; } + // The two conventionally agreed track helix parameters relevant in r-z plane. i.e. (z0, tan_lambda) + std::pair helixRz() const { return helixRz_; } + + //--- Return chi variables, (both digitized & undigitized), which are the stub coords. relative to track. + + std::vector chiPhi() { + std::vector result; + for (const Stub* s : stubs_) { + float chi_phi = reco::deltaPhi(s->phi(), this->phi0() - s->r() * this->qOverPt() * settings_->invPtToDphi()); + result.push_back(chi_phi); + } + return result; + } + + std::vector chiPhiDigi() { + std::vector result; + const float phiMult = pow(2, settings_->phiSBits()) / settings_->phiSRange(); + for (const float& chi_phi : this->chiPhi()) { + int iDigi_chi_phi = floor(chi_phi * phiMult); + result.push_back(iDigi_chi_phi); + } + return result; + } + + std::vector chiZ() { + std::vector result; + for (const Stub* s : stubs_) { + float chi_z = s->z() - (this->z0() + s->r() * this->tanLambda()); + result.push_back(chi_z); + } + return result; + } + + std::vector chiZDigi() { + std::vector result; + const float zMult = pow(2, settings_->zBits()) / settings_->zRange(); + for (const float& chi_z : this->chiZ()) { + int iDigi_chi_z = floor(chi_z * zMult); + result.push_back(iDigi_chi_z); + } + return result; + } + + //--- User-friendlier access to the helix parameters. + + float qOverPt() const override { return helixRphi_.first; } + float charge() const { return (this->qOverPt() > 0 ? 1 : -1); } + float invPt() const { return std::abs(this->qOverPt()); } + // Protect pt against 1/pt = 0. + float pt() const { + constexpr float small = 1.0e-6; + return 1. / (small + this->invPt()); + } + float d0() const { return helixD0_; } // Hough transform assumes d0 = 0. + float phi0() const override { return helixRphi_.second; } + float z0() const { return helixRz_.first; } + float tanLambda() const { return helixRz_.second; } + float theta() const { return atan2(1., this->tanLambda()); } // Use atan2 to ensure 0 < theta < pi. + float eta() const { return -log(tan(0.5 * this->theta())); } + + // Phi and z coordinates at which track crosses "chosenR" values used by r-phi HT and rapidity sectors respectively. + float phiAtChosenR() const { + return reco::deltaPhi(this->phi0() - (settings_->invPtToDphi() * settings_->chosenRofPhi()) * this->qOverPt(), + 0.); + } + float zAtChosenR() const { + return (this->z0() + (settings_->chosenRofZ()) * this->tanLambda()); + } // neglects transverse impact parameter & track curvature. + + //--- Get phi sector and eta region used by track finding code that this track is in. + unsigned int iPhiSec() const override { return iPhiSec_; } + unsigned int iEtaReg() const override { return iEtaReg_; } + + //--- Opto-link ID used to send this track from HT to Track Fitter + unsigned int optoLinkID() const override { return optoLinkID_; } + + //--- Was this track produced from a marged HT cell (e.g. 2x2)? + bool mergedHTcell() const { return mergedHTcell_; } + + //--- Get information about its association (if any) to a truth Tracking Particle. + + // Get best matching tracking particle (=nullptr if none). + const TP* matchedTP() const override { return matchedTP_; } + // Get the matched stubs with this Tracking Particle + const std::vector& matchedStubs() const override { return matchedStubs_; } + // Get number of matched stubs with this Tracking Particle + unsigned int numMatchedStubs() const override { return matchedStubs_.size(); } + // Get number of tracker layers with matched stubs with this Tracking Particle + unsigned int numMatchedLayers() const override { return nMatchedLayers_; } + // Get purity of stubs on track candidate (i.e. fraction matching best Tracking Particle) + float purity() const { return numMatchedStubs() / float(numStubs()); } + + //--- For debugging purposes. + + // Remove incorrect stubs from the track using truth information. + // Also veto tracks where the HT cell estimated from the true helix parameters is inconsistent with the cell the HT found the track in, (since probable duplicates). + // Also veto tracks that match a truth particle not used for the algo efficiency measurement. + // Return a boolean indicating if the track should be kept. (i.e. Is genuine & non-duplicate). + bool cheat() { + bool keep = false; + + std::vector stubsSel; + if (matchedTP_ != nullptr) { // Genuine track + for (Stub* s : stubs_) { + const TP* tp = s->assocTP(); + if (tp != nullptr) { + if (matchedTP_->index() == tp->index()) { + stubsSel.push_back(s); // This stub was produced by same truth particle as rest of track, so keep it. + } + } + } + } + stubs_ = stubsSel; + stubsConst_ = std::vector(stubs_.begin(), stubs_.end()); + + nLayers_ = Utility::countLayers(settings_, stubs_); // Count tracker layers these stubs are in + matchedTP_ = Utility::matchingTP(settings_, + stubs_, + nMatchedLayers_, + matchedStubs_); // Find associated truth particle & calculate info about match. + + bool genuine = (matchedTP_ != nullptr); + + if (genuine && matchedTP_->useForAlgEff()) { + Sector secTmp(settings_, iPhiSec_, iEtaReg_); + HTrphi htRphiTmp(settings_, iPhiSec_, iEtaReg_, secTmp.etaMin(), secTmp.etaMax(), secTmp.phiCentre()); + std::pair trueCell = htRphiTmp.trueCell(matchedTP_); + + std::pair htCell = this->cellLocationHT(); + bool consistent = (htCell == trueCell); // If true, track is probably not a duplicate. + if (mergedHTcell_) { + // If this is a merged cell, check other elements of merged cell. + std::pair htCell10(htCell.first + 1, htCell.second); + std::pair htCell01(htCell.first, htCell.second + 1); + std::pair htCell11(htCell.first + 1, htCell.second + 1); + if (htCell10 == trueCell) + consistent = true; + if (htCell01 == trueCell) + consistent = true; + if (htCell11 == trueCell) + consistent = true; + } + if (consistent) + keep = true; + } + + return keep; // Indicate if track should be kept. + } + + private: + //--- Configuration parameters + const Settings* settings_; + + //--- Information about the reconstructed track. + std::vector stubs_; + std::vector stubsConst_; + std::unordered_set bestStubs_; + unsigned int nLayers_; + std::pair cellLocationHT_; + std::pair helixRphi_; + std::pair helixRz_; + float helixD0_; + unsigned int iPhiSec_; + unsigned int iEtaReg_; + unsigned int optoLinkID_; + bool mergedHTcell_; + + //--- Optional info used for tracklet tracks. + TrackletSeedType seedLayerType_; + unsigned int seedPS_; + + //--- Information about its association (if any) to a truth Tracking Particle. + const TP* matchedTP_; + std::vector matchedStubs_; + unsigned int nMatchedLayers_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/L1trackBase.h b/L1Trigger/TrackFindingTMTT/interface/L1trackBase.h new file mode 100644 index 0000000000000..8bd7fcbfc7551 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/L1trackBase.h @@ -0,0 +1,64 @@ +#ifndef L1Trigger_TrackFindingTMTT_L1trackBase_h +#define L1Trigger_TrackFindingTMTT_L1trackBase_h + +#include +#include + +//=== L1 track base class +//=== This is a pure virtual class containing no implemented functions or data members. +//=== However, it declares functions that are common to the derived classes L1trackBase, L1track3D and L1fittedTrack, +//=== allowing software to analyse objects of all three types in the same way. + +namespace tmtt { + + class Stub; + class TP; + + class L1trackBase { + public: + L1trackBase() {} + + virtual ~L1trackBase() = default; + + //--- Get information about the reconstructed track. + + // Get stubs on track candidate. + virtual const std::vector& stubsConst() const = 0; + virtual const std::vector& stubs() const = 0; + // Get number of stubs on track candidate. + virtual unsigned int numStubs() const = 0; + // Get number of tracker layers these stubs are in. + virtual unsigned int numLayers() const = 0; + + //--- User-friendly access to the helix parameters. + + virtual float qOverPt() const = 0; + virtual float phi0() const = 0; + //virtual float z0() const = 0; + //virtual float tanLambda() const = 0; + + //--- Cell locations of the track candidate in the r-phi Hough transform array in units of bin number. + virtual std::pair cellLocationHT() const = 0; + + //--- Get phi sector and eta region used by track finding code that this track is in. + virtual unsigned int iPhiSec() const = 0; + virtual unsigned int iEtaReg() const = 0; + + //--- Opto-link ID used to send this track from HT to Track Fitter + virtual unsigned int optoLinkID() const = 0; + + //--- Get information about its association (if any) to a truth Tracking Particle. + + // Get matching tracking particle (=nullptr if none). + virtual const TP* matchedTP() const = 0; + // Get the matched stubs. + virtual const std::vector& matchedStubs() const = 0; + // Get number of matched stubs. + virtual unsigned int numMatchedStubs() const = 0; + // Get number of tracker layers with matched stubs. + virtual unsigned int numMatchedLayers() const = 0; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/Make3Dtracks.h b/L1Trigger/TrackFindingTMTT/interface/Make3Dtracks.h new file mode 100644 index 0000000000000..369802e8ca448 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/Make3Dtracks.h @@ -0,0 +1,98 @@ +#ifndef L1Trigger_TrackFindingTMTT_Make3Dtracks_h +#define L1Trigger_TrackFindingTMTT_Make3Dtracks_h + +#include "L1Trigger/TrackFindingTMTT/interface/TrkRZfilter.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" + +#include +#include +#include +#include + +//=== This reconstructs 3D tracks from the 2D tracks found by the Hough transform. +//=== It can do this by simply estimating the r-z helix parameters from the centre of the eta sector +//=== and/or by running an r-z filter (e.g. Seed Filter), which also cleans up the tracks by +//=== checking their stubs consistency with a straight line in the r-z plane. +//=== +//=== To create 3D tracks, call run() and then get tracks via trackCands3D(). + +namespace tmtt { + + class Settings; + class Stub; + class TP; + + class Make3Dtracks { + public: + // Initialize + Make3Dtracks(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float phiCentreSector); + + //=== Main routines to make 3D tracks. + + // Make 3D track collections. + void run(const std::list& vecTracksRphi) { + this->makeUnfilteredTrks(vecTracksRphi); + if (runRZfilter_) + this->makeRZfilteredTrks(vecTracksRphi); + } + + //=== Get 3D tracks. + + // Get 3D tracks (either r-z filtered or unfiltered, depending on the boolean). + // (Each L1track3D object gives access to stubs on each track and helix parameters + // & also to the associated truth tracking particle). + const std::list& trackCands3D(bool rzFiltered) const { + if (rzFiltered) { + return vecTracks3D_rzFiltered_; + } else { + return vecTracks3D_unfiltered_; + } + } + + // Get all 3D track candidates (either r-z filtered on unfiltered, depending on the boolean), + // that are associated to the given tracking particle. + // (If the std::vector is empty, then the tracking particle was not reconstructed in this sector). + std::vector assocTrackCands3D(const TP& tp, bool rzFiltered) const; + + //=== Access to track r-z filter in case internal info from it required. + + bool ranRZfilter() const { return runRZfilter_; } // Was r-z filter required/run? + + const TrkRZfilter* rzFilter() const { return rzFilter_.get(); } + + private: + // Convert 2D HT tracks within the current sector to 3D tracks, + // by adding a rough estimate of their r-z helix parameters, without running any r-z track filter. + void makeUnfilteredTrks(const std::list& vecTracksRphi); + + // Make 3D tracks from the 2D HT tracks within the current sector, by running the r-z track filter. + // The r-z filter also adds an estimate of the r-z helix parameters to each track. + // (Not filled if no track fitter needs the r-z filter). + void makeRZfilteredTrks(const std::list& vecTracksRphi); + + private: + // Configuration parameters + const Settings* settings_; + unsigned int iPhiSec_; // Sector number. + unsigned int iEtaReg_; + float etaMinSector_; // Range of eta sector + float etaMaxSector_; // Range of eta sector + float phiCentreSector_; // Phi angle of centre of this (eta,phi) sector. + + bool runRZfilter_; // Does r-z track filter need to be run. + + // Track filter(s), such as r-z filters, run after the r-phi Hough transform. + std::unique_ptr rzFilter_; + + // List of all found 3D track candidates and their associated properties. + std::list vecTracks3D_rzFiltered_; // After r-z filter run + std::list vecTracks3D_unfiltered_; // Before r-z filter run. + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/MiniHTstage.h b/L1Trigger/TrackFindingTMTT/interface/MiniHTstage.h new file mode 100644 index 0000000000000..f38fe47e10f86 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/MiniHTstage.h @@ -0,0 +1,60 @@ +#ifndef L1Trigger_TrackFindingTMTT_MiniHTstage_h +#define L1Trigger_TrackFindingTMTT_MiniHTstage_h + +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" +#include "L1Trigger/TrackFindingTMTT/interface/MuxHToutputs.h" + +using boost::numeric::ublas::matrix; +#include + +namespace tmtt { + + class Settings; + + class MiniHTstage { + public: + MiniHTstage(const Settings* settings); + + void exec(matrix>& mHtRphis); + + private: + // Do load balancing + unsigned int linkIDLoadBalanced( + unsigned int link, + unsigned int mBin, + unsigned int cBin, + unsigned int numStubs, + std::map, unsigned int>& numStubsPerLinkStage1, + std::map, unsigned int>& numStubsPerLinkStage2, + bool test = false) const; + + private: + const Settings* settings_; // Configuration parameters + bool miniHTstage_; + MuxHToutputs::MuxAlgoName muxOutputsHT_; + unsigned int houghNbinsPt_; + unsigned int houghNbinsPhi_; + unsigned int miniHoughLoadBalance_; + unsigned int miniHoughNbinsPt_; + unsigned int miniHoughNbinsPhi_; + float miniHoughMinPt_; + bool miniHoughDontKill_; + float miniHoughDontKillMinPt_; + unsigned int numSubSecsEta_; + unsigned int numPhiNonants_; + unsigned int numPhiSecPerNon_; + unsigned int numEtaRegions_; + bool busySectorKill_; + unsigned int busySectorNumStubs_; + std::vector busySectorMbinRanges_; + float chosenRofPhi_; + float binSizeQoverPtAxis_; + float binSizePhiTrkAxis_; + float invPtToDphi_; + unsigned int nMiniHTcells_; + unsigned int nHTlinksPerNonant_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/MuxHToutputs.h b/L1Trigger/TrackFindingTMTT/interface/MuxHToutputs.h new file mode 100644 index 0000000000000..7bb90fa3cbcec --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/MuxHToutputs.h @@ -0,0 +1,77 @@ +#ifndef L1Trigger_TrackFindingTMTT_MuxHToutputs_h +#define L1Trigger_TrackFindingTMTT_MuxHToutputs_h + +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" + +#include "boost/numeric/ublas/matrix.hpp" +#include +#include + +using boost::numeric::ublas::matrix; + +//================================================================================================== +/** +* Multiplex the tracks found by several HT onto a single output optical link. +* (where throughout this class, the word "link" corresponds to a pair of links in the hardware). +* so that tracks that can't be sent down the link within the time-multiplexed period are killed. +* +* This class replaces the 2D track collection in the r-phi HTs with the subset of the tracks +* that can be output within the TM period. +* +* If you wish to change the multiplexing algorithm, then edit this class ... +*/ +//================================================================================================== + +namespace tmtt { + + class Settings; + + class MuxHToutputs { + public: + enum class MuxAlgoName { None = 0, mBinPerLink = 1 }; + + // Initialize constants from configuration parameters. + MuxHToutputs(const Settings* settings); + + // Determine which tracks are transmitted on each HT output optical link, taking into account the multiplexing + // of multiple (eta,phi) sectors onto single links and the truncation of the tracks caused by the requirement + // to output all the tracks within the time-multiplexed period. + // This function replaces the 2D track collection in the r-phi HT with the subset surviving the TM cut. + void exec(matrix>& mHtRphis) const; + + // Determine number of optical links used to output tracks from each phi nonant + // (where "link" refers to a pair of links in the hardware). + unsigned int numLinksPerNonant() const { + unsigned int iCorr = (settings_->miniHTstage()) ? 1 : 0; + return numPhiSecPerNon_ * numEtaRegions_ * (busySectorMbinRanges_.size() - iCorr) / this->muxFactor(); + } + + private: + // Define the number of (eta,phi) sectors that each output opto-link takes tracks from. (Depends on MUX scheme). + unsigned int muxFactor() const; + + // Define the MUX algorithm by which tracks from the specified m-bin range in the HT for a given (phi,eta) + // sector within a phi nonant are multiplexed onto a single output optical link. + unsigned int linkID(unsigned int iSecInNon, unsigned int iEtaReg, unsigned int mBinRange) const; + + // Do sanity check of the MUX algorithm implemented in linkID(). + void sanityCheck(); + + private: + const Settings* settings_; // Configuration parameters + + // Configuration parameters + MuxAlgoName muxOutputsHT_; + unsigned int numPhiNonants_; + unsigned int numPhiSectors_; + unsigned int numPhiSecPerNon_; + unsigned int numEtaRegions_; + bool busySectorKill_; + unsigned int busySectorNumStubs_; + std::vector busySectorMbinRanges_; + bool busySectorUseMbinRanges_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h b/L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h new file mode 100644 index 0000000000000..3a3423e747e6a --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h @@ -0,0 +1,29 @@ +#ifndef L1Trigger_TrackFindingTMTT_PrintL1trk +#define L1Trigger_TrackFindingTMTT_PrintL1trk + +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include + +// Use LogVerbatim with "L1track" category & floating point precision specified here. +// Example use: PrintL1trk() << "My message "< + edm::LogVerbatim& operator<<(const T& t) { + lv_ << std::fixed << std::setprecision(nDigits_) << t; + return lv_; + } + + private: + edm::LogVerbatim lv_; + const unsigned int nDigits_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/Sector.h b/L1Trigger/TrackFindingTMTT/interface/Sector.h new file mode 100644 index 0000000000000..cc51fd96a07ad --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/Sector.h @@ -0,0 +1,101 @@ +#ifndef L1Trigger_TrackFindingTMTT_Sector_h +#define L1Trigger_TrackFindingTMTT_Sector_h + +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" + +#include +#include + +namespace tmtt { + + class Settings; + class Stub; + + class Sector { + public: + // Initialization. + Sector(const Settings* settings, unsigned int iPhiSec, unsigned int iEtaSec); + + // Check if stub within the eta and/or phi boundaries of this sector. + bool inside(const Stub* stub) const { return (this->insideEta(stub) && this->insidePhi(stub)); } + bool insideEta(const Stub* stub) const; + bool insidePhi(const Stub* stub) const; + + // Check if stub is within subsectors in eta that sector may be divided into. + std::vector insideEtaSubSecs(const Stub* stub) const; + + unsigned int iPhiSec() const { return iPhiSec_; } // Sector number. + unsigned int iEtaReg() const { return iEtaReg_; } + float phiCentre() const { return phiCentre_; } // Return phi of centre of this sector. + float etaMin() const { return etaMin_; } // Eta range covered by this sector. + float etaMax() const { return etaMax_; } // Eta range covered by this sector. + + float sectorHalfWidth() const { return sectorHalfWidth_; } // Half width in phi of sector measured in radians. + float zAtChosenR_Min() const { + return zOuterMin_; + } // Range in z of particle at chosen radius from beam line covered by this sector. + float zAtChosenR_Max() const { return zOuterMax_; } + + // For performance studies, note which stubs on given tracking particle are inside the sector. + // Returns two booleans for each stub, indicating if they are in phi & eta sectors respectively. + // You can AND them together to check if stub is in (eta,phi) sector. + std::unordered_map> stubsInside(const TP& tp) const; + + // Count number of stubs in given tracking particle which are inside this (phi,eta) sector; + // or inside it if only the eta cuts are applied; or inside it if only the phi cuts are applied. + // The results are returned as the 3 last arguments of the function. + void numStubsInside(const TP& tp, + unsigned int& nStubsInsideEtaPhi, + unsigned int& nStubsInsideEta, + unsigned int& nStubsInsidePhi) const; + + // Check if the helix parameters of a tracking particle (truth) are consistent with this sector. + bool insidePhiSec(const TP& tp) const { + return (std::abs(tp.trkPhiAtR(chosenRofPhi_) - phiCentre_) < sectorHalfWidth_); + } + bool insideEtaReg(const TP& tp) const { + return (tp.trkZAtR(chosenRofZ_) > zOuterMin_ && tp.trkZAtR(chosenRofZ_) < zOuterMax_); + } + + private: + // Check if stub is within eta sector or subsector that is delimated by specified zTrk range. + bool insideEtaRange(const Stub* stub, float zRangeMin, float zRangeMax) const; + + // Digitize a floating point number to 2s complement integer, dropping anything after the decimal point. (Kristian Harder) + int64_t forceBitWidth(const float value, const UInt_t nBits) const; + + // Check if stub is within subsectors in eta that sector may be divided into. Uses digitized calculation corresponding to GP firmware. (Kristian Harder) + std::vector subEtaFwCalc(const int rT, const int z) const; + + private: + const Settings* settings_; + + // Sector number + unsigned int iPhiSec_; + unsigned int iEtaReg_; + float beamWindowZ_; + float etaMin_; // Range in eta covered by this sector. + float etaMax_; + float chosenRofZ_; // Use z of track at radius="chosenRofZ" to define eta sectors. + float zOuterMin_; // z range of sector at reference radius + float zOuterMax_; + + // Define phi sector. + float phiCentre_; // phi of centre of sector. + float sectorHalfWidth_; // sector half-width excluding overlaps. + float chosenRofPhi_; // Use phi of track at radius="chosenRofPhi" to define phi sectors. + float minPt_; // Min Pt covered by HT array. + float assumedPhiTrkRes_; // Tolerance in stub phi0 (or phi65) assumed to be this fraction of phi sector width. (N.B. If > 0.5, then stubs can be shared by more than 2 phi sectors). + bool useStubPhi_; // Require stub phi to be consistent with track of Pt > HTArraySpec.HoughMinPt that crosses HT phi axis? + bool useStubPhiTrk_; // Require stub phi0 (or phi65 etc.) as estimated from stub bend, to lie within HT phi axis, allowing tolerance specified below? + bool calcPhiTrkRes_; // If true, tolerance in stub phi0 (or phi65 etc.) will be reduced below AssumedPhiTrkRes if stub bend resolution specified in HTFilling.BendResolution suggests it is safe to do so. + + // Possible subsectors in eta within each sector. + unsigned int numSubSecsEta_; + std::vector zOuterMinSub_; + std::vector zOuterMaxSub_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/Settings.h b/L1Trigger/TrackFindingTMTT/interface/Settings.h new file mode 100644 index 0000000000000..123e3e6aa0f4b --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/Settings.h @@ -0,0 +1,666 @@ +#ifndef L1Trigger_TrackFindingTMTT_Settings_h +#define L1Trigger_TrackFindingTMTT_Settings_h + +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/Utilities/interface/ESInputTag.h" +#include "FWCore/Utilities/interface/Exception.h" +#include "CLHEP/Units/PhysicalConstants.h" +#include +#include +#include + +// Stores all configuration parameters + some hard-wired constants. + +namespace tmtt { + + class Settings { + public: + // Constructor for HYBRID (sets config to hard-wired consts to allow use outside CMSSW). + Settings(); + + // Constructor for TMTT (reads config from python cfg) + Settings(const edm::ParameterSet& iConfig); + + // Input tags for ES & ED data. + edm::ESInputTag magneticFieldInputTag() const { return magneticFieldInputTag_; } + edm::ESInputTag trackerGeometryInputTag() const { return trackerGeometryInputTag_; } + edm::ESInputTag trackerTopologyInputTag() const { return trackerTopologyInputTag_; } + edm::ESInputTag ttStubAlgoInputTag() const { return ttStubAlgoInputTag_; } + + edm::InputTag stubInputTag() const { return stubInputTag_; } + edm::InputTag tpInputTag() const { return tpInputTag_; } + edm::InputTag stubTruthInputTag() const { return stubTruthInputTag_; } + edm::InputTag clusterTruthInputTag() const { return clusterTruthInputTag_; } + edm::InputTag genJetInputTag() const { return genJetInputTag_; } + + //=== General settings. + + // Enable all use of MC truth info (disable to save CPU). + bool enableMCtruth() const { return enableMCtruth_; } + // Enable output histograms & job tracking performance summary (disable to save CPU). + bool enableHistos() const { return enableHistos_; } + // Enable output of TTTracks from part-way through tracking chain (after HT & RZ). + bool enableOutputIntermediateTTTracks() const { return enableOutputIntermediateTTTracks_; } + + //=== Cuts on MC truth tracks for tracking efficiency measurements. + + double genMinPt() const { return genMinPt_; } + double genMaxAbsEta() const { return genMaxAbsEta_; } + double genMaxVertR() const { return genMaxVertR_; } + double genMaxVertZ() const { return genMaxVertZ_; } + double genMaxD0() const { return genMaxD0_; } + double genMaxZ0() const { return genMaxZ0_; } + const std::vector& genPdgIds() const { return genPdgIds_; } + // Additional cut on MC truth tracks for algorithmic tracking efficiency measurements. + unsigned int genMinStubLayers() const { return genMinStubLayers_; } // Min. number of layers TP made stub in. + + //=== Cuts applied to stubs before arriving in L1 track finding board. + + // Reduce number of bits used by front-end chips to store stub bend info? + // = 0 (no); = 1 (yes using official recipe); = 2 (yes using TMTT method) + unsigned int degradeBendRes() const { return degradeBendRes_; } + // Don't use stubs with eta beyond this cut, since the tracker geometry makes it impossible to reconstruct tracks with them. + double maxStubEta() const { return maxStubEta_; } + // Don't use stubs whose measured Pt from bend info is significantly below HTArraySpec.HoughMinPt, where "significantly" means allowing for resolution in q/Pt derived from stub bend resolution HTFilling.BendResolution + bool killLowPtStubs() const { return killLowPtStubs_; } + // Print stub windows corresponding to KillLowPtStubs, in python cfg format used by CMSSW. + bool printStubWindows() const { return printStubWindows_; } + // Bend resolution assumed by bend filter in units of strip pitch. Also used when assigning stubs to sectors if calcPhiTrkRes() is true. + double bendCut() const { return bendCut_; } + // Additional contribution to bend resolution from its encoding into a reduced number of bits. + // This number is the assumed resolution relative to the naive guess of its value. + // It is ignored in DegradeBendRes = 0. + double bendCutExtra() const { return bendCutExtra_; } + // Order stubs by bend in DTC, such that highest Pt stubs are transmitted first. + bool orderStubsByBend() const { return orderStubsByBend_; } + + //=== Optional stub digitization configuration + + bool enableDigitize() const { return enableDigitize_; } + //--- Parameters available in MP board. + unsigned int phiSectorBits() const { return phiSectorBits_; } + unsigned int phiSBits() const { return phiSBits_; } + double phiSRange() const { return phiSRange_; } + unsigned int rtBits() const { return rtBits_; } + double rtRange() const { return rtRange_; } + unsigned int zBits() const { return zBits_; } + double zRange() const { return zRange_; } + //--- Parameters available in GP board (excluding any in common with MP specified above). + unsigned int phiNBits() const { return phiNBits_; } + double phiNRange() const { return phiNRange_; } + unsigned int bendBits() const { return bendBits_; } + + //=== Tracker module type for FW. + const std::vector& pitchVsType() const { return pitchVsType_; } + const std::vector& spaceVsType() const { return spaceVsType_; } + const std::vector& barrelVsType() const { return barrelVsType_; } + const std::vector& psVsType() const { return psVsType_; } + const std::vector& tiltedVsType() const { return tiltedVsType_; } + + //=== Configuration of Geometric Processor. + // Use an FPGA-friendly approximation to determine track angle dphi from bend in GP? + bool useApproxB() const { return useApproxB_; } + double bApprox_gradient() const { return bApprox_gradient_; } + double bApprox_intercept() const { return bApprox_intercept_; } + + //=== Definition of phi sectors. + + unsigned int numPhiNonants() const { return numPhiNonants_; } + unsigned int numPhiSectors() const { return numPhiSectors_; } + // Use phi of track at this radius as sector hourglass reference radius. + double chosenRofPhi() const { return chosenRofPhi_; } + // Require stub phi to be consistent with track of Pt > HTArraySpec.HoughMinPt that crosses HT phi axis? + bool useStubPhi() const { return useStubPhi_; } + // Require stub phi0 (or phi65 etc.) as estimated from stub bend, to lie within HT phi axis, allowing tolerance specified below? + bool useStubPhiTrk() const { return useStubPhiTrk_; } + // Tolerance in stub phi0 (or phi65) assumed to be this fraction of phi sector width. (N.B. If > 0.5, then stubs can be shared by more than 2 phi sectors). + double assumedPhiTrkRes() const { return assumedPhiTrkRes_; } + // If true, tolerance in stub phi0 (or phi65 etc.) will be reduced below AssumedPhiTrkRes if stub bend resolution specified in StubCuts.BendResolution suggests it is safe to do so. + bool calcPhiTrkRes() const { return calcPhiTrkRes_; } + + //=== Definition of eta sectors. + + const std::vector& etaRegions() const { return etaRegions_; } // Boundaries of eta regions de + unsigned int numEtaRegions() const { return (etaRegions_.size() - 1); } + // Use z of track at this radius for assignment of stubs to phi sectors & also for one of the axes of the r-z HT. + double chosenRofZ() const { return chosenRofZ_; } + // Half-width of window supposed to contain beam-spot in z. + double beamWindowZ() const { return beamWindowZ_; } + // If True, the code will not throw an error if a stub is assigned to 3 or more eta sectors. + bool allowOver2EtaSecs() const { return allowOver2EtaSecs_; } + + //=== r-phi Hough transform array specifications. + + double houghMinPt() const { return houghMinPt_; } + // Dimension in any q/Pt related variable. (If MiniHTstage = True, this refers to mini cells in whole HT array). + unsigned int houghNbinsPt() const { return houghNbinsPt_; } + // Dimension in any track-phi related variable. (If MiniHTstage = True, this refers to mini cells in whole HT array). + unsigned int houghNbinsPhi() const { return houghNbinsPhi_; } + // Groups of neighbouring 2x2 cells in HT will be treated as if they are a single large cell. (Also enabled in MiniHTstage = True). + bool enableMerge2x2() const { return enableMerge2x2_; } + // but only cells with pt < maxPtToMerge2x2() will be merged in this way (irrelevant if enableMerge2x2() = false). + double maxPtToMerge2x2() const { return maxPtToMerge2x2_; } + // Subdivide each sector into this number of subsectors in eta within r-phi HT. + unsigned int numSubSecsEta() const { return numSubSecsEta_; } + // define cell shape (0 square, 1 diamond, 2 hexagon, 3 brick) + unsigned int shape() const { return shape_; } + // Run 2nd stage HT with mini cells inside each 1st stage normal HT cell. N.B. This automatically std::sets EnableMerge2x2 = True & MaxPtToMerge = 999999. + bool miniHTstage() const { return miniHTstage_; } + // Number of mini cells along q/Pt & phi axes inside each normal HT cell. + unsigned int miniHoughNbinsPt() const { return miniHoughNbinsPt_; } + unsigned int miniHoughNbinsPhi() const { return miniHoughNbinsPhi_; } + // Below this Pt threshold, the mini HT will not be used, so only tracks found by 1st stage coarse HT will be output. (Used to improve low Pt tracking). (HT cell numbering remains as if mini HT were in use everywhere). + float miniHoughMinPt() const { return miniHoughMinPt_; } + // If true, allows tracks found by 1st stage coarse HT to be output if 2nd stage mini HT finds no tracks. + bool miniHoughDontKill() const { return miniHoughDontKill_; } + // If MiniHoughDontKill=True, this option restricts it to keep 1st stage HT tracks only if their Pt is exceeds this cut. (Used to improve electron tracking above this threshold). + float miniHoughDontKillMinPt() const { return miniHoughDontKillMinPt_; } + // load balancing disabled = 0; static load balancing of output links = 1; dynamic load balancing of output links = 2. + unsigned int miniHoughLoadBalance() const { return miniHoughLoadBalance_; } + + //=== Rules governing how stubs are filled into the r-phi Hough Transform array. + + // Take all cells in HT array crossed by line corresponding to each stub (= 0) or take only some to reduce rate at cost + // of efficiency ( > 0). If this option is > 0, it can be 1 or 2, corresponding to different algorithms for rejecting some of the cells. + unsigned int killSomeHTCellsRphi() const { return killSomeHTCellsRphi_; } + // Use filter in each HT cell using only stubs which have consistent bend, allowing for resolution specified in StubCuts.BendResolution. + bool useBendFilter() const { return useBendFilter_; } + // A filter is used each HT cell, which prevents more than the specified number of stubs being stored in the cell. (Reflecting memory limit of hardware). N.B. If mini-HT is in use, then this cut applies to coarse-HT. + unsigned int maxStubsInCell() const { return maxStubsInCell_; } + // Similar cut for Mini-HT. + unsigned int maxStubsInCellMiniHough() const { return maxStubsInCellMiniHough_; } + // If this returns true, and if more than busySectorNumStubs() stubs are assigned to tracks by an r-phi HT array, then + // the excess tracks are killed, with lowest Pt ones killed first. This is because hardware has finite readout time. + bool busySectorKill() const { return busySectorKill_; } + unsigned int busySectorNumStubs() const { return busySectorNumStubs_; } + // If this returns a non-empty std::vector, then the BusySectorNumStubs cut is instead applied to the subset of tracks appearing in the following m bin ranges (q/Pt) of the HT array. The sum of the entries in the std::vector should equal the number of m bins in the HT, although the entries will be rescaled if this is not the case. If the std::vector is empty, this option is disabled. (P.S. If the HT includes "merged" cells, then the m bin ranges specified here should correspond to the bins before merging). + const std::vector& busySectorMbinRanges() const { return busySectorMbinRanges_; } + // If BusySecMbinOrder is empty, then the groupings specified in BusySectorMbinRanges are applied to the m bins in the order + // 0,1,2,3,4,5 ... . If it is not empty, then they are grouped in the order specified here. + const std::vector& busySectorMbinOrder() const { return busySectorMbinOrder_; } + // If this is True, and more than BusyInputSectorNumStubs() are input to the HT array from the GP, then + // the excess stubs are killed. This is because HT hardware has finite readin time. + bool busyInputSectorKill() const { return busyInputSectorKill_; } + unsigned int busyInputSectorNumStubs() const { return busyInputSectorNumStubs_; } + // Multiplex the outputs from several HTs onto a single pair of output optical links? + // Options: 0 = disable Mux; 1 = Dec. 2016 Mux; 2 = Mar 2018 Mux (transverse HT readout by m-bin); + // 3 = Sept 2019 Mux (transverse HT readout by m-bin) + unsigned int muxOutputsHT() const { return muxOutputsHT_; } + // Is specified eta sector enabled? + bool isHTRPhiEtaRegWhitelisted(unsigned const iEtaReg) const; + + //=== Options controlling r-z track filters (or any other track filters run after the Hough transform, as opposed to inside it). + + // Specify preferred r-z filter (from those available inside TrkRZfilter.cc) - currently only "SeedFilter". + const std::string& rzFilterName() const { return rzFilterName_; } + // --- Options relevant for Seed filter, (so only relevant if rzFilterName()="SeedFilter"). + // Cut at this many standard deviations on seed resolution. + double seedResCut() const { return seedResCut_; } + // Store stubs compatible with all possible good seed (relevant for Seed filter)? + bool keepAllSeed() const { return keepAllSeed_; } + // Maximum number of seed combinations to check (relevant for Seed filter). + unsigned int maxSeedCombinations() const { return maxSeedCombinations_; } + // Maximum number of seed combinations consistent with (z0,eta) sector constraints to bother checking per track candidate. + unsigned int maxGoodSeedCombinations() const { return maxGoodSeedCombinations_; } + // Maximum number of seeds that a single stub can be included in. + unsigned int maxSeedsPerStub() const { return maxSeedsPerStub_; } + // Check that estimated zTrk from seeding stub is within the sector boundaries (relevant for Seed filter)? + bool zTrkSectorCheck() const { return zTrkSectorCheck_; } + // Min. number of layers in rz track that must have stubs for track to be declared found. + unsigned int minFilterLayers() const { return minFilterLayers_; } + + //=== Rules for deciding when the (HT) track finding has found an L1 track candidate + + // Min. number of layers in HT cell that must have stubs for track to be declared found. + unsigned int minStubLayers() const { return minStubLayers_; } + // Change min. number of layers cut to (MinStubLayers - 1) for tracks with Pt exceeding this cut. + // If this is std::set to > 10000, this option is disabled. + double minPtToReduceLayers() const { return minPtToReduceLayers_; } + // Change min. number of layers cut to (MinStubLayers - 1) for tracks in these rapidity sectors. + const std::vector& etaSecsReduceLayers() const { return etaSecsReduceLayers_; } + //Reduce this layer ID, so that it takes no more than 8 different values in any eta region (simplifies firmware)? + bool reduceLayerID() const { return reduceLayerID_; } + + //=== Specification of algorithm to eliminate duplicate tracks + + // Algorithm run on tracks after the track helix fit has been done. + unsigned int dupTrkAlgFit() const { return dupTrkAlgFit_; } + + //=== Rules for deciding when a reconstructed L1 track matches a MC truth particle (i.e. tracking particle). + + //--- Three different ways to define if a tracking particle matches a reco track candidate. (Usually, std::set two of them to ultra loose). + // Min. fraction of matched stubs relative to number of stubs on reco track. + double minFracMatchStubsOnReco() const { return minFracMatchStubsOnReco_; } + // Min. fraction of matched stubs relative to number of stubs on tracking particle. + double minFracMatchStubsOnTP() const { return minFracMatchStubsOnTP_; } + // Min. number of matched layers & min. number of matched PS layers.. + unsigned int minNumMatchLayers() const { return minNumMatchLayers_; } + unsigned int minNumMatchPSLayers() const { return minNumMatchPSLayers_; } + // Associate stub to TP only if the TP contributed to both its clusters? (If False, then associate even if only one cluster was made by TP). + bool stubMatchStrict() const { return stubMatchStrict_; } + + //=== Track Fitting Settings + + //--- Options applicable to all track fitters --- + + // Track fitting algorithms to use. You can run several in parallel. + const std::vector& trackFitters() const { return trackFitters_; } + // Indicate subset of fitters wanting r-z track filter to be run before them. + // (Excludes fitters that are not run). + const std::vector& useRZfilter() const { return useRZfilter_; } + // Print detailed summary of track fit performance at end of job (as opposed to a brief one)? + bool detailedFitOutput() const { return detailedFitOutput_; } + // Use MC truth to eliminate all fake tracks & all incorrect stubs assigned to tracks before doing fit. + bool trackFitCheat() const { return trackFitCheat_; } + + //--- Options for chi2 track fitter --- + + // Number of iterations that the track fit should perform. + unsigned int numTrackFitIterations() const { return numTrackFitIterations_; } + // Optionally remove hit with worst residual in track fit? (Only used by chi2 track fit). + bool killTrackFitWorstHit() const { return killTrackFitWorstHit_; } + // Cuts in standard deviations used to kill hits with big residuals during fit. If the residual exceeds the "General" + // cut, the hit is killed providing it leaves the track with enough hits to survive. If the residual exceeds the + // "Killing" cut, the hit is killed even if that kills the track. + double generalResidualCut() const { return generalResidualCut_; } + double killingResidualCut() const { return killingResidualCut_; } + + //--- Additional options for Davide Cieri's Simple Linear Regression track fitter --- + + // Digitize Simple Linear Regression variables & calculation. (Disabled if EnableDigitize=False). + bool digitizeSLR() const { return digitizeSLR_; } + /// Number of bits to be used in hardware to compute the division needed to calculate the helix parameters + unsigned int dividerBitsHelix() const { return dividerBitsHelix_; } + unsigned int dividerBitsHelixZ() const { return dividerBitsHelixZ_; } + /// Number of bits to reduce the RPhi helix parameter denominator calculation weight + unsigned int ShiftingBitsDenRPhi() const { return ShiftingBitsDenRPhi_; } + + /// Number of bits to reduce the RZ helix parameter denominator calculation weight + unsigned int ShiftingBitsDenRZ() const { return ShiftingBitsDenRZ_; } + /// Number of bits to reduce the qOverPt parameter numerator calculation weight + unsigned int ShiftingBitsPt() const { return ShiftingBitsPt_; } + /// Number of bits to reduce the PhiT parameter numerator calculation weight + unsigned int ShiftingBitsPhi() const { return ShiftingBitsPhi_; } + /// Number of bits to reduce the tanLambda parameter calculation weight + unsigned int ShiftingBitsLambda() const { return ShiftingBitsLambda_; } + /// Number of bits to reduce the tanLambda parameter calculation weight + unsigned int ShiftingBitsZ0() const { return ShiftingBitsZ0_; } + /// ChiSquare Cut + double slr_chi2cut() const { return slr_chi2cut_; } + /// Cut on RPhi Residual (radians) + double ResidualCut() const { return residualCut_; } + + //--- Options for Kalman filter track fitters --- + + // Larger number has more debugging printout. + unsigned kalmanDebugLevel() const { return kalmanDebugLevel_; } + // Fit will reject fitted tracks unless it can assign at least this number of stubs to them. + unsigned int kalmanMinNumStubs() const { return kalmanMinNumStubs_; } + // Fit will attempt to add up to this nummber of stubs to each fitted tracks, but won't bother adding more. + unsigned int kalmanMaxNumStubs() const { return kalmanMaxNumStubs_; } + // For 5-param helix fits, calculate also beam-constrained helix params after fit is complete, & use them for duplicate removal if DupTrkAlgFit=1. + bool kalmanAddBeamConstr() const { return kalmanAddBeamConstr_; } + // Remove requirement of at least 2 PS layers per track. + bool kalmanRemove2PScut() const { return kalmanRemove2PScut_; } + // Allow the KF to skip this many layers in total per track for "hard" or "easy" input tracks + unsigned int kalmanMaxSkipLayersHard() const { return kalmanMaxSkipLayersHard_; } + unsigned int kalmanMaxSkipLayersEasy() const { return kalmanMaxSkipLayersEasy_; } + // Max #stubs an input track can have to be defined "easy". + unsigned int kalmanMaxStubsEasy() const { return kalmanMaxStubsEasy_; } + // Cuts applied to KF states as a function of the last KF tracker layer they had a stub in. + // (If "4" or "5" in name, cut only applies to 4 or 5 param helix fit). + const std::vector& kfLayerVsPtToler() const { return kfLayerVsPtToler_; } + const std::vector& kfLayerVsD0Cut5() const { return kfLayerVsD0Cut5_; } + const std::vector& kfLayerVsZ0Cut5() const { return kfLayerVsZ0Cut5_; } + const std::vector& kfLayerVsZ0Cut4() const { return kfLayerVsZ0Cut4_; } + const std::vector& kfLayerVsChiSq5() const { return kfLayerVsChiSq5_; } + const std::vector& kfLayerVsChiSq4() const { return kfLayerVsChiSq4_; } + // KF will consider only this no. of stubs per layer. + unsigned int kalmanMaxStubsPerLayer() const { return kalmanMaxStubsPerLayer_; } + // Multiple scattering term - inflate hit phi errors by this divided by Pt + double kalmanMultiScattTerm() const { return kalmanMultiScattTerm_; } + // Scale down chi2 in r-phi plane by this factor to improve electron performance. + unsigned int kalmanChi2RphiScale() const { return kalmanChi2RphiScale_; } + //--- Enable Higher order corrections + // Treat z uncertainty in tilted barrel modules correctly. + bool kalmanHOtilted() const { return kalmanHOtilted_; } + // Higher order circle explansion terms for low Pt. + bool kalmanHOhelixExp() const { return kalmanHOhelixExp_; } + // Alpha correction for non-radial 2S endcap strips. (0=disable correction, 1=correct with offset, 2=correct with non-diagonal stub covariance matrix). + unsigned int kalmanHOalpha() const { return kalmanHOalpha_; } + // Projection from (r,phi) to (z,phi) for endcap 2S modules. (0=disable correction, 1=correct with offset, 2=correct with non-diagonal stub covariance matrix). + unsigned int kalmanHOprojZcorr() const { return kalmanHOprojZcorr_; } + // Use approx calc to account for non-radial endcap 2S modules corresponding to current FW, with no special treatment for tilted modules. + bool kalmanHOfw() const { return kalmanHOfw_; } + + //=== Treatment of dead modules. + // + // Emulate dead/inefficient modules using the StubKiller code, with stubs killed according to the scenarios of the Stress Test group. + // (0=Don't kill any stubs; 1-5 = Scenarios described in StubKiller.cc). + unsigned int killScenario() const { return killScenario_; } + // Modify TMTT tracking to try to recover tracking efficiency in presence of dead modules. (Does nothing if KillScenario = 0). + bool killRecover() const { return killRecover_; } + + //=== Track fit digitisation configuration for various track fitters + + // These are used only for SimpleLR4 track fitter. + bool slr_skipTrackDigi() const { return slr_skipTrackDigi_; } + unsigned int slr_oneOver2rBits() const { return slr_oneOver2rBits_; } + double slr_oneOver2rRange() const { return slr_oneOver2rRange_; } + unsigned int slr_d0Bits() const { return slr_d0Bits_; } + double slr_d0Range() const { return slr_d0Range_; } + unsigned int slr_phi0Bits() const { return slr_phi0Bits_; } + double slr_phi0Range() const { return slr_phi0Range_; } + unsigned int slr_z0Bits() const { return slr_z0Bits_; } + double slr_z0Range() const { return slr_z0Range_; } + unsigned int slr_tanlambdaBits() const { return slr_tanlambdaBits_; } + double slr_tanlambdaRange() const { return slr_tanlambdaRange_; } + unsigned int slr_chisquaredBits() const { return slr_chisquaredBits_; } + double slr_chisquaredRange() const { return slr_chisquaredRange_; } + // These are used for KF track fitter and for all other track fitters (though are probably not right for other track fitters ...) + bool kf_skipTrackDigi() const { return kf_skipTrackDigi_; } + unsigned int kf_oneOver2rBits() const { return kf_oneOver2rBits_; } + double kf_oneOver2rRange() const { return kf_oneOver2rRange_; } + unsigned int kf_d0Bits() const { return kf_d0Bits_; } + double kf_d0Range() const { return kf_d0Range_; } + unsigned int kf_phi0Bits() const { return kf_phi0Bits_; } + double kf_phi0Range() const { return kf_phi0Range_; } + unsigned int kf_z0Bits() const { return kf_z0Bits_; } + double kf_z0Range() const { return kf_z0Range_; } + unsigned int kf_tanlambdaBits() const { return kf_tanlambdaBits_; } + double kf_tanlambdaRange() const { return kf_tanlambdaRange_; } + unsigned int kf_chisquaredBits() const { return kf_chisquaredBits_; } + double kf_chisquaredRange() const { return kf_chisquaredRange_; } + const std::vector& kf_chisquaredBinEdges() const { return kf_chisquaredBinEdges_; } + // Skip track digitisation when fitted is not SimpleLR4 or KF? + bool other_skipTrackDigi() const { return other_skipTrackDigi_; } + + //=== Debug printout & plots + + // When making helix parameter resolution plots, only use particles from the physics event (True) + // or also use particles from pileup (False) ? + bool resPlotOpt() const { return resPlotOpt_; } + + // Booleain indicating if an output EDM file will be written. + // N.B. This parameter does not appear inside TMTrackProducer_Defaults_cfi.py . It is created inside tmtt_tf_analysis_cfg.py . + bool writeOutEdmFile() const { return writeOutEdmFile_; } + + //=== Hard-wired constants + + double cSpeed() const { return 1.0e8 * CLHEP::c_light; } // Speed of light, with (mm/ns) to (cm/s) + // B*c/1E11 - converts q/Pt to 1/radius_of_curvature + double invPtToInvR() const { return (this->magneticField()) * (this->cSpeed()) / 1.0E13; } + // B*c/2E11 - converts q/Pt to track angle at some radius from beamline. + double invPtToDphi() const { return (this->magneticField()) * (this->cSpeed()) / 2.0E13; } + //=== Set and get B-field value (mutable) in Tesla. + // N.B. This must bet std::set for each run, and can't be initialized at the beginning of the job. + void setMagneticField(float magneticField) const { magneticField_ = magneticField; } + float magneticField() const { + if (magneticField_ == 0.) + throw cms::Exception("LogicError") << "Settings: You attempted to access the B field before it was initialized"; + return magneticField_; + } + + //=== Settings used for HYBRID TRACKING code only. + // Is hybrid tracking in use? + bool hybrid() const { return hybrid_; } + + private: + // Input tags for ES & ED data. + const edm::ESInputTag magneticFieldInputTag_; + const edm::ESInputTag trackerGeometryInputTag_; + const edm::ESInputTag trackerTopologyInputTag_; + const edm::ESInputTag ttStubAlgoInputTag_; + + const edm::InputTag stubInputTag_; + const edm::InputTag tpInputTag_; + const edm::InputTag stubTruthInputTag_; + const edm::InputTag clusterTruthInputTag_; + const edm::InputTag genJetInputTag_; + + // Parameter std::sets for differents types of configuration parameter. + edm::ParameterSet genCuts_; + edm::ParameterSet stubCuts_; + edm::ParameterSet stubDigitize_; + edm::ParameterSet trackerModuleType_; + edm::ParameterSet geometricProc_; + edm::ParameterSet phiSectors_; + edm::ParameterSet etaSectors_; + edm::ParameterSet htArraySpecRphi_; + edm::ParameterSet htFillingRphi_; + edm::ParameterSet rzFilterOpts_; + edm::ParameterSet l1TrackDef_; + edm::ParameterSet dupTrkRemoval_; + edm::ParameterSet trackMatchDef_; + edm::ParameterSet trackFitSettings_; + edm::ParameterSet deadModuleOpts_; + edm::ParameterSet trackDigi_; + + // General settings + bool enableMCtruth_; + bool enableHistos_; + bool enableOutputIntermediateTTTracks_; + + // Cuts on truth tracking particles. + double genMinPt_; + double genMaxAbsEta_; + double genMaxVertR_; + double genMaxVertZ_; + double genMaxD0_; + double genMaxZ0_; + std::vector genPdgIds_; + unsigned int genMinStubLayers_; + + // Cuts applied to stubs before arriving in L1 track finding board. + unsigned int degradeBendRes_; + double maxStubEta_; + bool killLowPtStubs_; + bool printStubWindows_; + double bendCut_; + double bendCutExtra_; + bool orderStubsByBend_; + + // Optional stub digitization. + bool enableDigitize_; + unsigned int phiSectorBits_; + unsigned int phiSBits_; + double phiSRange_; + unsigned int rtBits_; + double rtRange_; + unsigned int zBits_; + double zRange_; + unsigned int phiNBits_; + double phiNRange_; + unsigned int bendBits_; + + // Tracker module type for FW. + std::vector pitchVsType_; + std::vector spaceVsType_; + std::vector barrelVsType_; + std::vector psVsType_; + std::vector tiltedVsType_; + std::vector barrelVsTypeTmp_; + std::vector psVsTypeTmp_; + std::vector tiltedVsTypeTmp_; + + // Configuration of Geometric Processor. + bool useApproxB_; + double bApprox_gradient_; + double bApprox_intercept_; + + // Definition of phi sectors. + unsigned int numPhiNonants_; + unsigned int numPhiSectors_; + double chosenRofPhi_; + bool useStubPhi_; + bool useStubPhiTrk_; + double assumedPhiTrkRes_; + bool calcPhiTrkRes_; + + // Definition of eta sectors. + std::vector etaRegions_; + double chosenRofZ_; + double beamWindowZ_; + bool allowOver2EtaSecs_; + + // r-phi Hough transform array specifications. + double houghMinPt_; + unsigned int houghNbinsPt_; + unsigned int houghNbinsPhi_; + bool enableMerge2x2_; + double maxPtToMerge2x2_; + unsigned int numSubSecsEta_; + unsigned int shape_; + bool miniHTstage_; + unsigned int miniHoughNbinsPt_; + unsigned int miniHoughNbinsPhi_; + double miniHoughMinPt_; + bool miniHoughDontKill_; + double miniHoughDontKillMinPt_; + unsigned int miniHoughLoadBalance_; + + // Rules governing how stubs are filled into the r-phi Hough Transform array. + unsigned int killSomeHTCellsRphi_; + bool useBendFilter_; + unsigned int maxStubsInCell_; + unsigned int maxStubsInCellMiniHough_; + bool busySectorKill_; + unsigned int busySectorNumStubs_; + std::vector busySectorMbinRanges_; + std::vector busySectorMbinOrder_; + bool busyInputSectorKill_; + unsigned int busyInputSectorNumStubs_; + unsigned int muxOutputsHT_; + std::vector etaRegWhitelist_; + + // Options controlling r-z track filters (or any other track filters run after the Hough transform, as opposed to inside it). + std::string rzFilterName_; + double seedResCut_; + bool keepAllSeed_; + unsigned int maxSeedCombinations_; + unsigned int maxGoodSeedCombinations_; + unsigned int maxSeedsPerStub_; + bool zTrkSectorCheck_; + unsigned int minFilterLayers_; + + // Rules for deciding when the track-finding has found an L1 track candidate + unsigned int minStubLayers_; + double minPtToReduceLayers_; + std::vector etaSecsReduceLayers_; + bool reduceLayerID_; + + // Specification of algorithm to eliminate duplicate tracks + unsigned int dupTrkAlgFit_; + + // Rules for deciding when a reconstructed L1 track matches a MC truth particle (i.e. tracking particle). + double minFracMatchStubsOnReco_; + double minFracMatchStubsOnTP_; + unsigned int minNumMatchLayers_; + unsigned int minNumMatchPSLayers_; + bool stubMatchStrict_; + + // Track Fitting Settings + std::vector trackFitters_; + std::vector useRZfilter_; + double chi2OverNdfCut_; + bool detailedFitOutput_; + bool trackFitCheat_; + // + unsigned int numTrackFitIterations_; + bool killTrackFitWorstHit_; + double generalResidualCut_; + double killingResidualCut_; + // + bool digitizeSLR_; + unsigned int dividerBitsHelix_; + unsigned int dividerBitsHelixZ_; + unsigned int ShiftingBitsDenRPhi_; + unsigned int ShiftingBitsDenRZ_; + unsigned int ShiftingBitsPt_; + unsigned int ShiftingBitsPhi_; + + unsigned int ShiftingBitsLambda_; + unsigned int ShiftingBitsZ0_; + double slr_chi2cut_; + double residualCut_; + // + unsigned kalmanDebugLevel_; + unsigned int kalmanMinNumStubs_; + unsigned int kalmanMaxNumStubs_; + bool kalmanAddBeamConstr_; + bool kalmanRemove2PScut_; + unsigned int kalmanMaxSkipLayersHard_; + unsigned int kalmanMaxSkipLayersEasy_; + unsigned int kalmanMaxStubsEasy_; + + std::vector kfLayerVsPtToler_; + std::vector kfLayerVsD0Cut5_; + std::vector kfLayerVsZ0Cut5_; + std::vector kfLayerVsZ0Cut4_; + std::vector kfLayerVsChiSq5_; + std::vector kfLayerVsChiSq4_; + + unsigned int kalmanMaxStubsPerLayer_; + double kalmanMultiScattTerm_; + unsigned int kalmanChi2RphiScale_; + bool kalmanHOtilted_; + bool kalmanHOhelixExp_; + unsigned int kalmanHOalpha_; + unsigned int kalmanHOprojZcorr_; + bool kalmanHOfw_; + + // Treatment of dead modules. + unsigned int killScenario_; + bool killRecover_; + + // Track digitisation configuration for various track fitters + bool slr_skipTrackDigi_; + unsigned int slr_oneOver2rBits_; + double slr_oneOver2rRange_; + double slr_oneOver2rMult_; + unsigned int slr_d0Bits_; + double slr_d0Range_; + unsigned int slr_phi0Bits_; + double slr_phi0Range_; + unsigned int slr_z0Bits_; + double slr_z0Range_; + unsigned int slr_tanlambdaBits_; + double slr_tanlambdaRange_; + unsigned int slr_chisquaredBits_; + double slr_chisquaredRange_; + // + bool kf_skipTrackDigi_; + unsigned int kf_oneOver2rBits_; + double kf_oneOver2rRange_; + double kf_oneOver2rMult_; + unsigned int kf_d0Bits_; + double kf_d0Range_; + unsigned int kf_phi0Bits_; + double kf_phi0Range_; + unsigned int kf_z0Bits_; + double kf_z0Range_; + unsigned int kf_tanlambdaBits_; + double kf_tanlambdaRange_; + unsigned int kf_chisquaredBits_; + double kf_chisquaredRange_; + std::vector kf_chisquaredBinEdges_; + // + bool other_skipTrackDigi_; + + // Debug printout + bool resPlotOpt_; + + // Boolean indicating an an EDM output file will be written. + bool writeOutEdmFile_; + + // B-field in Tesla + mutable std::atomic magneticField_; + + // Hybrid tracking + bool hybrid_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/SimpleLR4.h b/L1Trigger/TrackFindingTMTT/interface/SimpleLR4.h new file mode 100644 index 0000000000000..50c611923a911 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/SimpleLR4.h @@ -0,0 +1,68 @@ +#ifndef L1Trigger_TrackFindingTMTT_SimpleLR4_h +#define L1Trigger_TrackFindingTMTT_SimpleLR4_h + +///=== This is the simple linear regression with 4 helix parameters (qOverPt, phiT, z0, tanLambda) track fit algorithm. + +///=== Written by: Davide Cieri (davide.cieri@stfc.ac.uk) + +#include "L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" + +#include +#include +#include + +namespace tmtt { + + class SimpleLR4 : public TrackFitGeneric { + public: + SimpleLR4(const Settings* settings); + + ~SimpleLR4() override = default; + + L1fittedTrack fit(const L1track3D& l1track3D) override; + + protected: + float phiSectorWidth_; + float phiSectorCentre_; + float phiNonantWidth_; + + float phiMult_; + float rTMult_; + float zMult_; + float qOverPtMult_; + float phiTMult_; + float z0Mult_; + float tanLambdaMult_; + float numeratorPtMult_; + float numeratorZ0Mult_; + float numeratorLambdaMult_; + float numeratorPhiMult_; + float denominatorMult_; + float chi2Mult_; + float resMult_; + float chi2cut_; + float invPtToDPhi_; + float chosenRofPhi_; + unsigned int minStubLayersRed_; + + unsigned int dividerBitsHelix_; + unsigned int dividerBitsHelixZ_; + unsigned int dividerBitsChi2_; + unsigned int shiftingBitsPhi_; + unsigned int shiftingBitsDenRPhi_; + unsigned int shiftingBitsDenRZ_; + unsigned int shiftingBitsPt_; + unsigned int shiftingBitsz0_; + unsigned int shiftingBitsLambda_; + bool digitize_; + + bool debug_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/Stub.h b/L1Trigger/TrackFindingTMTT/interface/Stub.h new file mode 100644 index 0000000000000..a20dfd1110bd2 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/Stub.h @@ -0,0 +1,297 @@ +#ifndef L1Trigger_TrackFindingTMTT_Stub_h +#define L1Trigger_TrackFindingTMTT_Stub_h + +#include "DataFormats/L1TrackTrigger/interface/TTTypes.h" +#include "DataFormats/L1TrackTrigger/interface/TTStub.h" +#include "DataFormats/Phase2TrackerDigi/interface/Phase2TrackerDigi.h" +#include "SimTracker/TrackTriggerAssociation/interface/TTStubAssociationMap.h" +#include "DataFormats/DetId/interface/DetId.h" +#include "DataFormats/Common/interface/Ref.h" +#include "DataFormats/Common/interface/DetSetVector.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" + +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/DigitalStub.h" +#include "L1Trigger/TrackFindingTMTT/interface/DegradeBend.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" + +#include +#include +#include +#include +#include + +class TrackerGeometry; +class TrackerTopology; + +namespace tmtt { + + class TP; + class DegradeBend; + class StubKiller; + + typedef edmNew::DetSetVector > TTStubDetSetVec; + typedef edmNew::DetSet > TTStubDetSet; + typedef edm::Ref > TTStubRef; + typedef edm::Ref >, TTCluster > + TTClusterRef; + typedef TTStubAssociationMap TTStubAssMap; + typedef TTClusterAssociationMap TTClusterAssMap; + + //=== Represents a Tracker stub (=pair of hits) + + class Stub { + public: + // Hybrid L1 tracking: stub constructor. + Stub(const Settings* settings, + unsigned int idStub, + double phi, + double r, + double z, + double bend, + unsigned int iphi, + double alpha, + unsigned int layerId, + unsigned int iPhiSec, + bool psModule, + bool barrel, + bool tiltedBarrel, + float stripPitch, + float stripLength, + unsigned int nStrips); + + // TMTT L1 tracking: stub constructor. + Stub(const TTStubRef& ttStubRef, + unsigned int index_in_vStubs, + const Settings* settings, + const TrackerTopology* trackerTopology, + const TrackerModule* trackerModule, + const DegradeBend* degradeBend, + const StubKiller* stubKiller); + + bool operator==(const Stub& stubOther) { return (this->index() == stubOther.index()); } + + // Return reference to original TTStub. + const TTStubRef& ttStubRef() const { return ttStubRef_; } + + // Info about tracker module containing stub. + const TrackerModule* trackerModule() const { return trackerModule_; } + + // Fill truth info with association from stub to tracking particles. + void fillTruth(const std::map, const TP*>& translateTP, + const edm::Handle& mcTruthTTStubHandle, + const edm::Handle& mcTruthTTClusterHandle); + + // Calculate HT m-bin range consistent with bend. + void calcQoverPtrange(); + + // Digitize stub for input to GP, HT, SF, TF + enum class DigiStage { NONE, GP, HT, SF, TF }; + void digitize(unsigned int iPhiSec, DigiStage digiStep); + + // Control warning messages about accessing non-digitized quantities. + void setDigitizeWarningsOn(bool newVal) { digitizeWarningsOn_ = newVal; } + + // Access to digitized version of stub coords. + const DigitalStub* digitalStub() const { return digitalStub_.get(); } + + // === Functions for returning info about reconstructed stubs === + + // Location in InputData::vStubs_ + unsigned int index() const { return index_in_vStubs_; } + + //--- Stub data and quantities derived from it --- + + // Stub coordinates (optionally after digitisation, if digitisation requested via cfg). + // N.B. Digitisation is only run if Stub::digitize() is called. + float phi() const { return phi_; } + float r() const { return r_; } + float z() const { return z_; } + float theta() const { return atan2(r_, z_); } + float eta() const { return asinh(z_ / r_); } + + // Location of stub in module in units of strip/pixel number in phi direction. + // Range from 0 to (nStrips - 1) inclusive. + unsigned int iphi() const { return iphi_; } + // alpha correction for non-radial strips in endcap 2S modules. + // (If true hit at larger r than stub r by deltaR, then stub phi needs correcting by +alpha*deltaR). + // *** TO DO *** : Digitize this. + float alpha() const { return alpha_; } + + // Get stub bend and its resolution, as available within the front end chip (i.e. prior to loss of bits + // or digitisation). + float bendInFrontend() const { return bendInFrontend_; } + float bendCutInFrontend() const { return settings_->bendCut(); } + // Get stub bend (i.e. displacement between two hits in stub in units of strip pitch). + float bend() const { return bend_; } + // Bend resolution. + float bendCut() const { return (settings_->bendCut() + (numMergedBend_ - 1) * settings_->bendCutExtra()); } + // No. of bend values merged into FE bend encoding of this stub. + float numMergedBend() const { return numMergedBend_; } + // Estimated track q/Pt based on stub bend info. + float qOverPt() const { return (this->qOverPtOverBend() * this->bend()); } + float qOverPtcut() const { return (this->qOverPtOverBend() * this->bendCut()); } + // Range in q/Pt bins in HT array compatible with stub bend. + unsigned int min_qOverPt_bin() const { return min_qOverPt_bin_; } + unsigned int max_qOverPt_bin() const { return max_qOverPt_bin_; } + // Difference in phi between stub and angle at which track crosses given radius, assuming track has given Pt. + float phiDiff(float rad, float Pt) const { return std::abs(r_ - rad) * (settings_->invPtToDphi()) / Pt; } + // Phi angle at which particle consistent with this stub & its bend cross specified radius. + float trkPhiAtR(float rad) const { return phi_ + (bend_ * dphiOverBend_) * (1. - rad / r_); } + // Its resolution + float trkPhiAtRcut(float rad) const { return (bendCut() * dphiOverBend_) * std::abs(1. - rad / r_); } + + // -- conversion factors + // Ratio of track crossing angle to bend. + float dphiOverBend() const { return dphiOverBend_; } + // Ratio of q/Pt to bend. + float qOverPtOverBend() const { return dphiOverBend_ / (r_ * settings_->invPtToDphi()); } + + //--- Info about the two clusters that make up the stub. + + // Coordinates in frame of sensor, measured in units of strip pitch along two orthogonal axes running perpendicular and parallel to longer axis of pixels/strips (U & V). + std::array localU_cluster() const { return localU_cluster_; } + std::array localV_cluster() const { return localV_cluster_; } + + //--- Check if this stub will be output by FE. Stub failing this not used for L1 tracks. + bool frontendPass() const { return frontendPass_; } + // Indicates if stub would have passed DE cuts, were it not for window size encoded in DegradeBend.h + bool stubFailedDegradeWindow() const { return stubFailedDegradeWindow_; } + + //--- Truth info + + // Association of stub to tracking particles + const std::set& assocTPs() const { + return assocTPs_; + } // Return TPs associated to this stub. (Whether only TPs contributing to both clusters are returned is determined by "StubMatchStrict" config param.) + bool genuine() const { return (not assocTPs_.empty()); } // Did stub match at least one TP? + const TP* assocTP() const { + return assocTP_; + } // If only one TP contributed to both clusters, this tells you which TP it is. Returns nullptr if none. + + // Association of both clusters making up stub to tracking particles + std::array genuineCluster() const { + return std::array{{(assocTPofCluster_[0] != nullptr), (assocTPofCluster_[1] != nullptr)}}; + } // Was cluster produced by a single TP? + std::array assocTPofCluster() const { + return assocTPofCluster_; + } // Which TP made each cluster. Warning: If cluster was not produced by a single TP, then returns nullptr! (P.S. If both clusters match same TP, then this will equal assocTP()). + + //--- Quantities common to all stubs in a given module --- + // N.B. Not taken from trackerModule_ to cope with Hybrid tracking. + + // Angle between normal to module and beam-line along +ve z axis. (In range -PI/2 to +PI/2). + float tiltAngle() const { return tiltAngle_; } + // Uncertainty in stub (r,z) + float sigmaR() const { return (barrel() ? 0. : sigmaPar()); } + float sigmaZ() const { return (barrel() ? sigmaPar() : 0.); } + // Hit resolution perpendicular to strip. Measures phi. + float sigmaPerp() const { return invRoot12 * stripPitch_; } + // Hit resolution parallel to strip. Measures r or z. + float sigmaPar() const { return invRoot12 * stripLength_; } + + //--- These module variables could be taken directly from trackerModule_, were it not for need + //--- to support Hybrid. + // Module type: PS or 2S? + bool psModule() const { return psModule_; } + // Tracker layer ID number (1-6 = barrel layer; 11-15 = endcap A disk; 21-25 = endcap B disk) + unsigned int layerId() const { return layerId_; } + // Reduced layer ID (in range 1-7). This encodes the layer ID in only 3 bits (to simplify firmware) by merging some barrel layer and endcap disk layer IDs into a single ID. + unsigned int layerIdReduced() const { return layerIdReduced_; } + bool barrel() const { return barrel_; } + // True if stub is in tilted barrel module. + bool tiltedBarrel() const { return tiltedBarrel_; } + // Strip pitch (or pixel pitch along shortest axis). + float stripPitch() const { return stripPitch_; } + // Strip length (or pixel pitch along longest axis). + float stripLength() const { return stripLength_; } + // No. of strips in sensor. + unsigned int nStrips() const { return nStrips_; } + + private: + // Degrade assumed stub bend resolution. + // And return an integer indicating how many values of bend are merged into this single one. + void degradeResolution(float bend, float& degradedBend, unsigned int& num) const; + + // Set the frontendPass_ flag, indicating if frontend readout electronics will output this stub. + void setFrontend(const StubKiller* stubKiller); + + // Set info about the module that this stub is in. + void setTrackerModule(const TrackerGeometry* trackerGeometry, + const TrackerTopology* trackerTopology, + const DetId& detId); + + // Function to calculate approximation for dphiOverBendCorrection aka B + double approxB(); + + // Calculate variables giving ratio of track intercept angle to stub bend. + void calcDphiOverBend(); + + private: + TTStubRef ttStubRef_; // Reference to original TTStub + + const Settings* settings_; // configuration parameters. + + unsigned int index_in_vStubs_; // location of this stub in InputData::vStubs + + //--- Parameters passed along optical links from PP to MP (or equivalent ones if easier for analysis software to use). + // WARNING: If you add any variables in this section, take care to ensure that they are digitized correctly by Stub::digitize(). + float phi_; // stub coords, optionally after digitisation. + float r_; + float z_; + float bend_; // bend of stub. + float dphiOverBend_; // related to rho parameter. + unsigned int min_qOverPt_bin_; // Range in q/Pt bins in HT array compatible with stub bend. + unsigned int max_qOverPt_bin_; + + //--- Info about the two clusters that make up the stub. + std::array localU_cluster_; + std::array localV_cluster_; + + unsigned int iphi_; + float alpha_; + + // Would front-end electronics output this stub? + bool frontendPass_; + // Did stub fail window cuts assumed in DegradeBend.h? + bool stubFailedDegradeWindow_; + // Bend in front end chip (prior to degredation by loss of bits & digitization). + float bendInFrontend_; + // Used for stub bend resolution degrading. + unsigned int numMergedBend_; + + //--- Truth info about stub. + const TP* assocTP_; + std::set assocTPs_; + //--- Truth info about the two clusters that make up the stub + std::array assocTPofCluster_; + + std::unique_ptr digitalStub_; // Class used to digitize stub if required. + DigiStage lastDigiStep_; + bool digitizeWarningsOn_; // Enable warnings about accessing non-digitized quantities. + + // Info about tracker module containing stub. + const TrackerModule* trackerModule_; + + // Used to degrade stub bend information. + const DegradeBend* degradeBend_; + + // These module variables are needed only to support the Hybrid stub constructor. + // (Otherwise, they could be taken from trackerModule_). + unsigned int layerId_; + unsigned int layerIdReduced_; + float tiltAngle_; + float stripPitch_; + float stripLength_; + unsigned int nStrips_; + bool psModule_; + bool barrel_; + bool tiltedBarrel_; + + const float rejectedStubBend_ = 99999.; // Bend set to this if stub rejected. + + const float invRoot12 = sqrt(1. / 12.); + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h b/L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h new file mode 100644 index 0000000000000..0d586ca9a1812 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h @@ -0,0 +1,52 @@ +#ifndef L1Trigger_TrackFindingTMTT_StubFEWindows_h +#define L1Trigger_TrackFindingTMTT_StubFEWindows_h + +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "DataFormats/DetId/interface/DetId.h" + +#include + +// Window sizes used by FE electronics to select stubs. + +class TrackerTopology; + +namespace tmtt { + + class StubFEWindows { + public: + // Initialize stub window sizes from TTStubProducer cfg. + StubFEWindows(const edm::ParameterSet& pSetStubAlgo); + + // Set all FE stub bend windows to zero. + void setZero(); + + // Access window size arrays (const functions). + const std::vector& windowSizeBarrelLayers() const { return windowSizeBarrelLayers_; } + const std::vector >& windowSizeEndcapDisksRings() const { return windowSizeEndcapDisksRings_; } + const std::vector >& windowSizeTiltedLayersRings() const { + return windowSizeTiltedLayersRings_; + } + + // Access window size arrays (non-const functions). + std::vector& windowSizeBarrelLayers() { return windowSizeBarrelLayers_; } + std::vector >& windowSizeEndcapDisksRings() { return windowSizeEndcapDisksRings_; } + std::vector >& windowSizeTiltedLayersRings() { return windowSizeTiltedLayersRings_; } + + // Number of tilted barrel modules each half of each PS barrel layer. + const std::vector& numTiltedLayerRings() const { return numTiltedLayerRings_; } + + // Const/non-const access to element of array giving window size for specific module. + const double* storedWindowSize(const TrackerTopology* trackerTopo, const DetId& detId) const; + double* storedWindowSize(const TrackerTopology* trackerTopo, const DetId& detId); + + private: + // Stub window sizes as encoded in L1Trigger/TrackTrigger/interface/TTStubAlgorithm_official.h + std::vector windowSizeBarrelLayers_; + std::vector > windowSizeEndcapDisksRings_; + std::vector > windowSizeTiltedLayersRings_; + std::vector numTiltedLayerRings_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/StubKiller.h b/L1Trigger/TrackFindingTMTT/interface/StubKiller.h new file mode 100644 index 0000000000000..e8a95268b2252 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/StubKiller.h @@ -0,0 +1,80 @@ +#ifndef L1Trigger_TrackFindingTMTT_StubKiller_h +#define L1Trigger_TrackFindingTMTT_StubKiller_h + +// Kill some stubs to emulate dead tracker modules. +// Author: Emyr Clement (2018) +// Tidy up: Ian Tomalin (2020) + +#include "FWCore/Framework/interface/Event.h" +#include "DataFormats/L1TrackTrigger/interface/TTTypes.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "Geometry/CommonTopologies/interface/PixelGeomDetUnit.h" +#include "Geometry/CommonTopologies/interface/PixelTopology.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "FWCore/Utilities/interface/RandomNumberGenerator.h" +#include "CLHEP/Random/RandomEngine.h" + +namespace tmtt { + + class StubKiller { + public: + enum class KillOptions { none = 0, layer5 = 1, layer1 = 2, layer1layer2 = 3, layer1disk1 = 4, random = 5 }; + + StubKiller(KillOptions killScenario, + const TrackerTopology* trackerTopology, + const TrackerGeometry* trackerGeometry, + const edm::Event& iEvent); + + // Indicate if given stub was killed by dead tracker module, based on dead module scenario. + bool killStub(const TTStub* stub) const; + + // Indicate if given stub was killed by dead tracker module, based on dead regions specified here, + // and ignoring dead module scenario. + bool killStub(const TTStub* stub, + const std::vector& layersToKill, + const double minPhiToKill, + const double maxPhiToKill, + const double minZToKill, + const double maxZToKill, + const double minRToKill, + const double maxRToKill, + const double fractionOfStubsToKillInLayers, + const double fractionOfStubsToKillEverywhere) const; + + // Indicate if given stub was in (partially) dead tracker module, based on dead module scenario. + bool killStubInDeadModule(const TTStub* stub) const; + + // List of all modules declared as (partially) dead, with fractional deadness of each. + const std::map& listOfDeadModules() const { return deadModules_; } + + private: + // Identify modules to be killed, chosen randomly from those in the whole tracker. + void chooseModulesToKill(); + // Identify modules to be killed, chosen based on location in tracker. + void addDeadLayerModulesToDeadModuleList(); + + KillOptions killScenario_; + const TrackerTopology* trackerTopology_; + const TrackerGeometry* trackerGeometry_; + + std::vector layersToKill_; + double minPhiToKill_; + double maxPhiToKill_; + double minZToKill_; + double maxZToKill_; + double minRToKill_; + double maxRToKill_; + double fractionOfStubsToKillInLayers_; + double fractionOfStubsToKillEverywhere_; + double fractionOfModulesToKillEverywhere_; + + std::map deadModules_; + + edm::Service rndmService_; + CLHEP::HepRandomEngine* rndmEngine_; + }; + +}; // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h b/L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h new file mode 100644 index 0000000000000..8a5ac807a380d --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h @@ -0,0 +1,57 @@ +#ifndef L1Trigger_TrackFindingTMTT_StubWindowsSuggest_h +#define L1Trigger_TrackFindingTMTT_StubWindowsSuggest_h + +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h" + +#include +#include + +class TrackerTopology; + +namespace tmtt { + + class Stub; + + /** + * ======================================================================================================== + * This provides recommendations to CMS for the stub window sizes to be used in the FE electronics. + * It prints the output as a python configuration file in the form of + * L1Trigger/TrackTrigger/python/TTStubAlgorithmRegister_cfi.py . + * + * The recommendations are based on the TMTT method of using the stub bend. Whilst they give + * high efficiency, they do not take into account the requirement to limit the FE electronics band-width, + * so tighter cuts may be needed in reality. + * ======================================================================================================== + */ + + class StubWindowSuggest { + public: + // Configure + StubWindowSuggest(const Settings* settings) : settings_(settings), ptMin_(settings->houghMinPt()) {} + + // Get FE window size arrays (via copy) used with stub producer, but set to zero. + void setFEWindows(const StubFEWindows* sw); + + // Analyse stub window required for this stub. + void process(const TrackerTopology* trackerTopo, const Stub* stub); + + // Print results (should be done in endJob(); + void printResults() const; + + private: + // Update stored stub window size with this stub. + void updateStoredWindow(const TrackerTopology* trackerTopo, const Stub* stub, double bendWind); + + private: + // Configuration parameters. + const Settings* settings_; + const float ptMin_; + + // Stub window sizes as encoded in L1Trigger/TrackTrigger/interface/TTStubAlgorithm_official.h + std::unique_ptr sw_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/TP.h b/L1Trigger/TrackFindingTMTT/interface/TP.h new file mode 100644 index 0000000000000..158806f538e04 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/TP.h @@ -0,0 +1,141 @@ +#ifndef L1Trigger_TrackFindingTMTT_TP_h +#define L1Trigger_TrackFindingTMTT_TP_h + +#include "DataFormats/Math/interface/deltaPhi.h" +#include "SimDataFormats/TrackingAnalysis/interface/TrackingParticle.h" +#include "DataFormats/Common/interface/Ptr.h" + +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" + +#include "DataFormats/JetReco/interface/GenJetCollection.h" +#include "DataFormats/JetReco/interface/GenJet.h" + +#include +#include + +namespace tmtt { + + class Stub; + + typedef edm::Ptr TrackingParticlePtr; + + class TP { + public: + // Fill useful info about tracking particle. + TP(const TrackingParticlePtr& tpPtr, unsigned int index_in_vTPs, const Settings* settings); + + // Return pointer to original tracking particle. + const TrackingParticlePtr& trackingParticlePtr() const { return trackingParticlePtr_; } + + bool operator==(const TP& tpOther) const { return (this->index() == tpOther.index()); } + + // Fill truth info with association from tracking particle to stubs. + void fillTruth(const std::list& vStubs); + + // == Functions for returning info about tracking particles === + + // Location in InputData::vTPs_ + unsigned int index() const { return index_in_vTPs_; } + // Basic TP properties + int pdgId() const { return pdgId_; } + // Did TP come from in-time or out-of-time bunch crossing? + bool inTimeBx() const { return inTimeBx_; } + // Did it come from the main physics collision or from pileup? + bool physicsCollision() const { return physicsCollision_; } + int charge() const { return charge_; } + float mass() const { return mass_; } + float pt() const { return pt_; } + // Protect against pt=0; + float qOverPt() const { + constexpr float big = 9.9e9; + return (pt_ > 0) ? charge_ / pt_ : big; + } + float eta() const { return eta_; } + float theta() const { return theta_; } + float tanLambda() const { return tanLambda_; } + float phi0() const { return phi0_; } + // TP production vertex (x,y,z) coordinates. + float vx() const { return vx_; } + float vy() const { return vy_; } + float vz() const { return vz_; } + // d0 and z0 impact parameters with respect to (x,y) = (0,0). + float d0() const { return d0_; } + float z0() const { return z0_; } + // Estimate track bend angle at a given radius, ignoring scattering. + float dphi(float rad) const { return asin(settings_->invPtToDphi() * rad * charge_ / pt_); } + // Estimated phi angle at which TP trajectory crosses a given radius rad, ignoring scattering. + float trkPhiAtR(float rad) const { return reco::deltaPhi(phi0_ - this->dphi(rad) - d0_ / rad, 0.); } + // Estimated z coord at which TP trajectory crosses a given radius rad, ignoring scattering. + float trkZAtR(float rad) const { return (vz_ + rad * tanLambda_); } + // Estimated phi angle at which TP trajectory crosses the module containing the given stub. + float trkPhiAtStub(const Stub* stub) const; + // Estimated r coord at which TP trajectory crosses the module containing the given stub. + float trkRAtStub(const Stub* stub) const; + // Estimated z coord at which TP trajectory crosses the module containing the given stub. + float trkZAtStub(const Stub* stub) const; + + // == Functions returning stubs produced by tracking particle. + const std::vector& assocStubs() const { + return assocStubs_; + } // associated stubs. (Includes those failing tightened front-end electronics cuts supplied by user). (Which stubs are returned is affected by "StubMatchStrict" config param.) + unsigned int numAssocStubs() const { return assocStubs_.size(); } + unsigned int numLayers() const { return nLayersWithStubs_; } + // TP is worth keeping (e.g. for fake rate measurement) + bool use() const { return use_; } + // TP can be used for efficiency measurement (good kinematics, in-time Bx, optionally specified PDG ID). + bool useForEff() const { return useForEff_; } + // TP can be used for algorithmic efficiency measurement (also requires stubs in enough layers). + bool useForAlgEff() const { return useForAlgEff_; } + + void fillNearestJetInfo(const reco::GenJetCollection* genJets); // Store info (deltaR, pt) with nearest jet + + // Check if TP is in a jet (for performance studies in jets) + float tpInJet(float genJetPtCut = 30.) const { return (tpInJet_ && nearestJetPt_ > genJetPtCut); } + float nearestJetPt() const { return nearestJetPt_; } // -ve if no nearest jet. + + private: + void fillUse(); // Fill the use_ flag. + void fillUseForEff(); // Fill the useForEff_ flag. + void fillUseForAlgEff(); // Fill the useforAlgEff_ flag. + + // Calculate how many tracker layers this TP has stubs in. + void calcNumLayers() { nLayersWithStubs_ = Utility::countLayers(settings_, assocStubs_, false); } + + private: + TrackingParticlePtr trackingParticlePtr_; // Pointer to original TrackingParticle. + + unsigned int index_in_vTPs_; // location of this TP in InputData::vTPs + + const Settings* settings_; // Configuration parameters + + int pdgId_; + bool inTimeBx_; // TP came from in-time bunch crossing. + bool physicsCollision_; // True if TP from physics collision rather than pileup. + int charge_; + float mass_; + float pt_; // TP kinematics + float eta_; + float theta_; + float tanLambda_; + float phi0_; + float vx_; // TP production point. + float vy_; + float vz_; + float d0_; // d0 impact parameter with respect to (x,y) = (0,0) + float z0_; // z0 impact parameter with respect to (x,y) = (0,0) + + std::vector assocStubs_; + unsigned int nLayersWithStubs_; // Number of tracker layers with stubs from this TP. + + bool use_; // TP is worth keeping (e.g. for fake rate measurement) + bool useForEff_; // TP can be used for tracking efficiency measurement. + bool useForAlgEff_; // TP can be used for tracking algorithmic efficiency measurement. + + bool tpInJet_; + float nearestJetPt_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/TrackFitFactory.h b/L1Trigger/TrackFindingTMTT/interface/TrackFitFactory.h new file mode 100644 index 0000000000000..a16353f3842fe --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/TrackFitFactory.h @@ -0,0 +1,27 @@ +#ifndef L1Trigger_TrackFindingTMTT_TrackFitFactory_h +#define L1Trigger_TrackFindingTMTT_TrackFitFactory_h + +///=== Create requested track fitter + +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h" + +#include +#include +#include + +namespace tmtt { + + class Settings; + + namespace trackFitFactory { + + // Function to produce a fitter based on a std::string + std::unique_ptr create(const std::string& fitterName, const Settings* settings); + + } // namespace trackFitFactory + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h b/L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h new file mode 100644 index 0000000000000..3ba19090af2c3 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/TrackFitGeneric.h @@ -0,0 +1,37 @@ +#ifndef L1Trigger_TrackFindingTMTT_TrackFitGeneric_h +#define L1Trigger_TrackFindingTMTT_TrackFitGeneric_h + +///=== This is the base class for all the track fit algorithms + +///=== Written by: Alexander D. Morton and Sioni Summers + +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" + +#include +#include + +namespace tmtt { + + class Settings; + + class TrackFitGeneric { + public: + // Set configuration parameters. + TrackFitGeneric(const Settings* settings, const std::string& fitterName = "") + : settings_(settings), fitterName_(fitterName) {} + + virtual ~TrackFitGeneric() = default; + + // Fit a track candidate obtained from the Hough Transform. + virtual L1fittedTrack fit(const L1track3D& l1track3D) { return L1fittedTrack(); } + + protected: + // Configuration parameters + const Settings* settings_; + const std::string fitterName_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/TrackerModule.h b/L1Trigger/TrackFindingTMTT/interface/TrackerModule.h new file mode 100644 index 0000000000000..c386e8d2998dc --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/TrackerModule.h @@ -0,0 +1,136 @@ +#ifndef L1Trigger_TrackFindingTMTT_TrackerModule_h +#define L1Trigger_TrackFindingTMTT_TrackerModule_h + +#include "DataFormats/DetId/interface/DetId.h" +#include "DataFormats/Common/interface/Ref.h" +#include "DataFormats/Common/interface/DetSetVector.h" +#include "FWCore/Framework/interface/Frameworkfwd.h" + +#include +#include +#include +#include + +class TrackerGeometry; +class TrackerTopology; +class PixelGeomDetUnit; +class PixelTopology; + +namespace tmtt { + + //=== Get info about tracker module + + class TrackerModule { + public: + enum BarrelModuleType { tiltedMinusZ = 1, tiltedPlusZ = 2, flat = 3 }; + + // Info used to define firmware module type. + struct ModuleTypeCfg { + std::vector pitchVsType; + std::vector spaceVsType; + std::vector barrelVsType; + std::vector psVsType; + std::vector tiltedVsType; + }; + + // Here detId is ID of lower sensor in stacked module. + TrackerModule(const TrackerGeometry* trackerGeometry, + const TrackerTopology* trackerTopology, + const ModuleTypeCfg& moduleTypeCfg, + const DetId& detId); + + // Det ID of lower sensor in stacked module. + const DetId& detId() const { return detId_; } + unsigned int rawDetId() const { return detId_.rawId(); } + // Det ID of stacked module. + const DetId& stackedDetId() const { return stackedDetId_; } + unsigned int rawStackedDetId() const { return stackedDetId_.rawId(); } + // Tracker specific DetUnit & topology. + const PixelGeomDetUnit* specDet() const { return specDet_; } + const PixelTopology* specTopol() const { return specTopol_; } + // Coordinates of centre of two sensors in (r,phi,z) + float minR() const { return moduleMinR_; } + float maxR() const { return moduleMaxR_; } + float minPhi() const { return moduleMinPhi_; } + float maxPhi() const { return moduleMaxPhi_; } + float minZ() const { return moduleMinZ_; } + float maxZ() const { return moduleMaxZ_; } + // Polar angle of module. + float theta() const { return atan2(moduleMinR_, moduleMinZ_); } + // Which of two sensors in module is furthest from beam-line? + bool outerModuleAtSmallerR() const { return outerModuleAtSmallerR_; } + // Module type: PS or 2S? + bool psModule() const { return psModule_; } + bool barrel() const { return barrel_; } + // Tracker layer ID number (1-6 = barrel layer; 11-15 = endcap A disk; 21-25 = endcap B disk) + unsigned int layerId() const { return layerId_; } + // Reduced layer ID (in range 1-7), for packing into 3 bits to simplify the firmware. + unsigned int layerIdReduced() const { return layerIdReduced_; } + // Endcap ring of module (returns zero in case of barrel) + unsigned int endcapRing() const { return endcapRing_; } + // True if stub is in tilted barrel module. + bool tiltedBarrel() const { return tiltedBarrel_; } + // Angle between normal to module and beam-line along +ve z axis. (In range -PI/2 to +PI/2). + float tiltAngle() const { return tiltAngle_; } + // Width of sensitive region of sensor. + float sensorWidth() const { return sensorWidth_; } + // Sensor spacing in module + float sensorSpacing() const { return sensorSpacing_; } + // No. of strips in sensor. + unsigned int nStrips() const { return nStrips_; } + // Strip pitch (or pixel pitch along shortest axis). + float stripPitch() const { return stripPitch_; } + // Strip length (or pixel pitch along longest axis). + float stripLength() const { return stripLength_; } + // Hit resolution perpendicular to strip (or to longest pixel axis). Measures phi. + float sigmaPerp() const { return invRoot12 * stripPitch_; } + // Hit resolution parallel to strip (or to longest pixel axis). Measures r or z. + float sigmaPar() const { return invRoot12 * stripLength_; } + // Sensor pitch over separation. + float pitchOverSep() const { return stripPitch_ / sensorSpacing_; } + // "B" parameter correction for module tilt. + float paramB() const { return std::abs(cos(theta() - tiltAngle()) / sin(theta())); } + // Module type ID defined by firmware. + unsigned int moduleTypeID() const { return moduleTypeID_; } + + //--- Utilties + + // Calculate reduced layer ID (in range 1-7), for packing into 3 bits to simplify the firmware. + static unsigned int calcLayerIdReduced(unsigned int layerId); + + // Get module type ID defined by firmware. + unsigned int calcModuleType(float pitch, float space, bool barrel, bool tiltedBarrel, bool psModule) const; + + private: + DetId detId_; + DetId stackedDetId_; + const PixelGeomDetUnit* specDet_; + const PixelTopology* specTopol_; + float moduleMinR_; + float moduleMaxR_; + float moduleMinPhi_; + float moduleMaxPhi_; + float moduleMinZ_; + float moduleMaxZ_; + bool outerModuleAtSmallerR_; + bool psModule_; + bool barrel_; + unsigned int layerId_; + unsigned int layerIdReduced_; + unsigned int endcapRing_; + bool tiltedBarrel_; + float tiltAngle_; + float sensorWidth_; + float sensorSpacing_; + unsigned int nStrips_; + float stripPitch_; + float stripLength_; + unsigned int moduleTypeID_; + + ModuleTypeCfg moduleTypeCfg_; + + static const float invRoot12; + }; + +} // namespace tmtt +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/TrkRZfilter.h b/L1Trigger/TrackFindingTMTT/interface/TrkRZfilter.h new file mode 100644 index 0000000000000..682f5eeb6334a --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/TrkRZfilter.h @@ -0,0 +1,111 @@ +#ifndef L1Trigger_TrackFindingTMTT_TrkRZfilter_h +#define L1Trigger_TrackFindingTMTT_TrkRZfilter_h + +#include "L1Trigger/TrackFindingTMTT/interface/L1track2D.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" + +#include +#include + +//=== This class runs filters in track candidates previously found by the r-phi Hough transform, +//=== which check that each track's stubs are consistent with a straight line in the r-z plane. +//=== +//=== The filtering removes inconsistent stubs from the track candidates, & also kills some track candidates +//=== altogether if the filter leaves them with too few stubs. +//=== +//=== The r-z filters also add an estimate of the r-z helix parameters to the selected track candidates, +//=== The filtered tracks are returned at L1track3D type, since they contain this information. +//=== +//=== It does NOT contain filters such as the bend filter, which are so simple that the firmware can run them +//=== INSIDE the r-phi HT. Simple filters of this kind are in class HTcell. +//=== +//=== After creating the L1track3D tracks, TrkRZfilter can optionally run duplicate removal on them, +//=== before they are output. + +namespace tmtt { + + class Settings; + class Stub; + + class TrkRZfilter { + public: + // Initialize configuration parameters, and note sector number, eta range covered by sector and phi coordinate of its centre. + TrkRZfilter(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float phiCentreSector); + + // Filters track candidates (found by the r-phi Hough transform), removing inconsistent stubs from the tracks, + // also killing some of the tracks altogether if they are left with too few stubs. + // Also adds an estimate of r-z helix parameters to the selected track objects, returning the tracks as L1track3D type. + // + std::list filterTracks(const std::list& tracks); + + //=== Extra information about each track input to filter. (Only use after you have first called filterTracks). + + // Number of seed combinations considered by the Seed Filter for each input track. + const std::vector& numSeedCombsPerTrk() const { return numSeedCombsPerTrk_; } + const std::vector& numGoodSeedCombsPerTrk() const { + return numGoodSeedCombsPerTrk_; + } // Only counts seeds compatible with beam-spot. + + private: + //--- Filters returning filtered stubs based on input ones. + + // Use Seed Filter to produce a filtered collection of stubs on this track candidate that are consistent with a straight line + // in r-z using tracklet algo. + std::vector seedFilter(const std::vector& stubs, float trkQoverPt, bool print); + + //--- Estimate r-z helix parameters from centre of eta-sector if no better estimate provided by r-z filter. + void estRZhelix(); + + private: + //=== Configuration parameters + + const Settings* settings_; + + unsigned int iPhiSec_; // Sector number. + unsigned int iEtaReg_; + float etaMinSector_; // rapidity range of this sector. + float etaMaxSector_; + float phiCentreSector_; // phi coordinate of its centre. + + // Track (z0, tan_lambda) estimate from r-z filter or centre of eta sector, and boolean to indicate if this data is filled. + float rzHelix_z0_; + float rzHelix_tanL_; + bool rzHelix_set_; + + // Useful info for r-z filters. + float chosenRofZ_; // Radius used to defined zTrkMinSector and zTrkMaxSector. + float zTrkMinSector_; // corresponding range of this sector specified as z coordinate of track at given radius. + float zTrkMaxSector_; + float beamWindowZ_; // Assumed length of beam spot in z. + + // Name of r-z track filter algorithm to run. + std::string rzFilterName_; + + // Filter stubs in cell using Seed Filter? (a tracklet-like algorithm in r-z plane). + bool useSeedFilter_; + + // Options for Seed filter. + bool keepAllSeed_; + float seedResCut_; + + // Number of seed combinations considered by the Seed Filter, for each input track. + std::vector numSeedCombsPerTrk_; + std::vector numGoodSeedCombsPerTrk_; + unsigned int maxSeedCombinations_; + unsigned int maxGoodSeedCombinations_; + unsigned int maxSeedsPerStub_; + bool zTrkSectorCheck_; + + // For debugging + unsigned int minNumMatchLayers_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/interface/Utility.h b/L1Trigger/TrackFindingTMTT/interface/Utility.h new file mode 100644 index 0000000000000..38e91bd80f261 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/interface/Utility.h @@ -0,0 +1,67 @@ +#ifndef L1Trigger_TrackFindingTMTT_Utility_h +#define L1Trigger_TrackFindingTMTT_Utility_h + +#include +#include + +namespace tmtt { + + class TP; + class Stub; + class Settings; + + namespace Utility { + // Count number of tracker layers a given list of stubs are in. + // + // By default uses the "reduced" layer ID if the configuration file requested it. However, + // you can insist on "normal" layer ID being used instead, ignoring the configuration file, by + // std::setting disableReducedLayerID = true. + // + // N.B. The "reduced" layer ID merges some layer IDs, so that no more than 8 ID are needed in any + // eta region, so as to simplify the firmware. + // + // N.B. You should std::set disableReducedLayerID = false when counting the number of layers on a tracking + // particle or how many layers it shares with an L1 track. Such counts by CMS convention use "normal" layer ID. + // + // By default, considers both PS+2S modules, but optionally considers only the PS ones if onlyPS = true. + + enum AlgoStep { HT, SEED, DUP, FIT }; + + unsigned int countLayers(const Settings* settings, + const std::vector& stubs, + bool disableReducedLayerID = false, + bool onlyPS = false); + + unsigned int countLayers(const Settings* settings, + const std::vector& stubs, + bool disableReducedLayerID = false, + bool onlyPS = false); + + // Given a std::set of stubs (presumably on a reconstructed track candidate) + // return the best matching Tracking Particle (if any), + // the number of tracker layers in which one of the stubs matched one from this tracking particle, + // and the list of the subset of the stubs which match those on the tracking particle. + + const TP* matchingTP(const Settings* settings, + const std::vector& vstubs, + unsigned int& nMatchedLayersBest, + std::vector& matchedStubsBest); + + const TP* matchingTP(const Settings* settings, + const std::vector& vstubs, + unsigned int& nMatchedLayersBest, + std::vector& matchedStubsBest); + + // Determine min number of layers a track candidate must have stubs in to be defined as a track. + // 1st argument indicates from which step in chain this function is called: HT, SEED, DUP or FIT. + unsigned int numLayerCut(Utility::AlgoStep algo, + const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float invPt, + float eta = 0.); + } // namespace Utility + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/plugins/BuildFile.xml b/L1Trigger/TrackFindingTMTT/plugins/BuildFile.xml new file mode 100644 index 0000000000000..959b8fdd463f7 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/plugins/BuildFile.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.cc b/L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.cc new file mode 100644 index 0000000000000..fa917b72eea94 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.cc @@ -0,0 +1,424 @@ +#include "L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.h" +#include "L1Trigger/TrackFindingTMTT/interface/InputData.h" +#include "L1Trigger/TrackFindingTMTT/interface/Sector.h" +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" +#include "L1Trigger/TrackFindingTMTT/interface/Make3Dtracks.h" +#include "L1Trigger/TrackFindingTMTT/interface/DupFitTrkKiller.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackFitFactory.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/ConverterToTTTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/HTcell.h" +#include "L1Trigger/TrackFindingTMTT/interface/MuxHToutputs.h" +#include "L1Trigger/TrackFindingTMTT/interface/MiniHTstage.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" + +#include "FWCore/MessageService/interface/MessageLogger.h" +#include "FWCore/Framework/interface/ESHandle.h" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using boost::numeric::ublas::matrix; + +namespace tmtt { + + namespace { + std::once_flag printOnce; + std::once_flag callOnce; + } // namespace + + std::unique_ptr TMTrackProducer::initializeGlobalCache(edm::ParameterSet const& iConfig) { + return std::make_unique(iConfig); + } + + TMTrackProducer::TMTrackProducer(const edm::ParameterSet& iConfig, GlobalCacheTMTT const* globalCacheTMTT) + : settings_(iConfig), // Set configuration parameters + stubWindowSuggest_(globalCacheTMTT->stubWindowSuggest()), // For tuning FE stub window sizes + hists_(globalCacheTMTT->hists()), // Initialize histograms + htRphiErrMon_(globalCacheTMTT->htRphiErrMon()), // rphi HT error monitoring + debug_(true) // Debug printout + { + using namespace edm; + + // Get tokens for ES data access. + magneticFieldToken_ = + esConsumes(settings_.magneticFieldInputTag()); + trackerGeometryToken_ = esConsumes( + settings_.trackerGeometryInputTag()); + trackerTopologyToken_ = + esConsumes(settings_.trackerTopologyInputTag()); + ttStubAlgoToken_ = + esConsumes(settings_.ttStubAlgoInputTag()); + + // Get tokens for ED data access. + stubToken_ = consumes(settings_.stubInputTag()); + if (settings_.enableMCtruth()) { + // These lines use lots of CPU, even if no use of truth info is made later. + tpToken_ = consumes(settings_.tpInputTag()); + stubTruthToken_ = consumes(settings_.stubTruthInputTag()); + clusterTruthToken_ = consumes(settings_.clusterTruthInputTag()); + genJetToken_ = consumes(settings_.genJetInputTag()); + } + + trackFitters_ = settings_.trackFitters(); + useRZfilter_ = settings_.useRZfilter(); + runRZfilter_ = (not useRZfilter_.empty()); // Do any fitters require an r-z track filter to be run? + + // Book histograms. + //hists_.book(); + + // Create track fitting algorithm + for (const string& fitterName : trackFitters_) { + fitterWorkerMap_[fitterName] = trackFitFactory::create(fitterName, &settings_); + } + + //--- Define EDM output to be written to file (if required) + + if (settings_.enableOutputIntermediateTTTracks()) { + // L1 tracks found by Hough Transform + produces("TML1TracksHT").setBranchAlias("TML1TracksHT"); + // L1 tracks found by r-z track filter. + if (runRZfilter_) + produces("TML1TracksRZ").setBranchAlias("TML1TracksRZ"); + } + // L1 tracks after track fit by each of the fitting algorithms under study + for (const string& fitterName : trackFitters_) { + string edmName = string("TML1Tracks") + fitterName; + produces(edmName).setBranchAlias(edmName); + } + } + + //=== Run every run + + void TMTrackProducer::beginRun(const edm::Run& iRun, const edm::EventSetup& iSetup) { + // Get the B-field and store its value in the Settings class. + const MagneticField* theMagneticField = &(iSetup.getData(magneticFieldToken_)); + float bField = theMagneticField->inTesla(GlobalPoint(0, 0, 0)).z(); // B field in Tesla. + settings_.setMagneticField(bField); + + // Set also B field in GlobalCacheTMTT (used only for Histogramming) + globalCache()->settings().setMagneticField(bField); + + std::stringstream text; + text << "\n--- B field = " << bField << " Tesla ---\n"; + std::call_once( + printOnce, [](string t) { PrintL1trk() << t; }, text.str()); + + // Get tracker geometry + trackerGeometry_ = &(iSetup.getData(trackerGeometryToken_)); + trackerTopology_ = &(iSetup.getData(trackerTopologyToken_)); + + // Loop over tracker modules to get module info. + + // Identifies tracker module type for firmware. + TrackerModule::ModuleTypeCfg moduleTypeCfg; + moduleTypeCfg.pitchVsType = settings_.pitchVsType(); + moduleTypeCfg.spaceVsType = settings_.spaceVsType(); + moduleTypeCfg.barrelVsType = settings_.barrelVsType(); + moduleTypeCfg.psVsType = settings_.psVsType(); + moduleTypeCfg.tiltedVsType = settings_.tiltedVsType(); + + listTrackerModule_.clear(); + for (const GeomDet* gd : trackerGeometry_->dets()) { + DetId detId = gd->geographicalId(); + // Phase 2 Outer Tracker uses TOB for entire barrel & TID for entire endcap. + if (detId.subdetId() != StripSubdetector::TOB && detId.subdetId() != StripSubdetector::TID) + continue; + if (trackerTopology_->isLower(detId)) { // Select only lower of the two sensors in a module. + // Store info about this tracker module. + listTrackerModule_.emplace_back(trackerGeometry_, trackerTopology_, moduleTypeCfg, detId); + } + } + + // Takes one copy of this to GlobalCacheTMTT for later histogramming. + globalCache()->setListTrackerModule(listTrackerModule_); + + // Get TTStubProducerAlgorithm algorithm, to adjust stub bend FE encoding. + stubAlgo_ = dynamic_cast(&iSetup.getData(ttStubAlgoToken_)); + // Get FE stub window size from TTStub producer configuration + const edm::ESHandle stubAlgoHandle = iSetup.getHandle(ttStubAlgoToken_); + const edm::ParameterSet& pSetStubAlgo = getParameterSet(stubAlgoHandle.description()->pid_); + stubFEWindows_ = std::make_unique(pSetStubAlgo); + // Initialize utilities needing FE window size. + stubWindowSuggest_.setFEWindows(stubFEWindows_.get()); + degradeBend_ = std::make_unique(trackerTopology_, stubFEWindows_.get(), stubAlgo_); + } + + //=== Run every event + + void TMTrackProducer::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) { + // Note useful info about MC truth particles and about reconstructed stubs . + InputData inputData(iEvent, + iSetup, + &settings_, + &stubWindowSuggest_, + degradeBend_.get(), + trackerGeometry_, + trackerTopology_, + listTrackerModule_, + tpToken_, + stubToken_, + stubTruthToken_, + clusterTruthToken_, + genJetToken_); + + const list& vTPs = inputData.getTPs(); + const list& vStubs = inputData.stubs(); + + // Creates matrix of Sector objects, which decide which stubs are in which (eta,phi) sector + matrix> mSectors(settings_.numPhiSectors(), settings_.numEtaRegions()); + // Create matrix of r-phi Hough-Transform arrays, with one-to-one correspondence to sectors. + matrix> mHtRphis(settings_.numPhiSectors(), settings_.numEtaRegions()); + // Create matrix of Make3Dtracks objects, to run optional r-z track filter, with one-to-one correspondence to sectors. + matrix> mMake3Dtrks(settings_.numPhiSectors(), settings_.numEtaRegions()); + // Create matrix of tracks from each fitter in each sector + matrix>> mapmFitTrks(settings_.numPhiSectors(), settings_.numEtaRegions()); + // Final tracks after duplicate removal from each track fitter in entire tracker. + map> mapFinalTracks; + + //=== Initialization + // Create utility for converting L1 tracks from our private format to official CMSSW EDM format. + const ConverterToTTTrack converter(&settings_); + + // Pointers to TTTrack collections for ED output. + auto htTTTracksForOutput = std::make_unique(); + auto rzTTTracksForOutput = std::make_unique(); + map> allFitTTTracksForOutput; + for (const string& fitterName : trackFitters_) { + auto fitTTTracksForOutput = std::make_unique(); + allFitTTTracksForOutput[fitterName] = std::move(fitTTTracksForOutput); + } + + //=== Do tracking in the r-phi Hough transform within each sector. + + // Fill Hough-Transform arrays with stubs. + for (unsigned int iPhiSec = 0; iPhiSec < settings_.numPhiSectors(); iPhiSec++) { + for (unsigned int iEtaReg = 0; iEtaReg < settings_.numEtaRegions(); iEtaReg++) { + // Initialize constants for this sector. + mSectors(iPhiSec, iEtaReg) = std::make_unique(&settings_, iPhiSec, iEtaReg); + Sector* sector = mSectors(iPhiSec, iEtaReg).get(); + + mHtRphis(iPhiSec, iEtaReg) = std::make_unique( + &settings_, iPhiSec, iEtaReg, sector->etaMin(), sector->etaMax(), sector->phiCentre(), &htRphiErrMon_); + HTrphi* htRphi = mHtRphis(iPhiSec, iEtaReg).get(); + + // Check sector is enabled (always true, except if user disabled some for special studies). + if (settings_.isHTRPhiEtaRegWhitelisted(iEtaReg)) { + for (Stub* stub : vStubs) { + // Digitize stub as would be at input to GP. This doesn't need the nonant number, since we assumed an integer number of + // phi digitisation bins inside an nonant. N.B. This changes the coordinates & bend stored in the stub. + + if (settings_.enableDigitize()) + stub->digitize(iPhiSec, Stub::DigiStage::GP); + + // Check if stub is inside this sector + bool inside = sector->inside(stub); + + if (inside) { + // Check which eta subsectors within the sector the stub is compatible with (if subsectors being used). + const vector inEtaSubSecs = sector->insideEtaSubSecs(stub); + + // Digitize stub if as would be at input to HT, which slightly degrades its coord. & bend resolution, affecting the HT performance. + if (settings_.enableDigitize()) + stub->digitize(iPhiSec, Stub::DigiStage::HT); + + // Store stub in Hough transform array for this sector, indicating its compatibility with eta subsectors with sector. + htRphi->store(stub, inEtaSubSecs); + } + } + } + + // Find tracks in r-phi HT array. + htRphi->end(); // Calls htArrayRphi_.end() -> HTBase::end() + } + } + + if (settings_.muxOutputsHT() > 0) { + // Multiplex outputs of several HT onto one pair of output opto-links. + // This only affects tracking performance if option busySectorKill is enabled, so that tracks that + // can't be sent down the link within the time-multiplexed period are killed. + MuxHToutputs muxHT(&settings_); + muxHT.exec(mHtRphis); + } + + // Optionally, run 2nd stage mini HT -- WITHOUT TRUNCATION ??? + if (settings_.miniHTstage()) { + MiniHTstage miniHTstage(&settings_); + miniHTstage.exec(mHtRphis); + } + + //=== Make 3D tracks, optionally running r-z track filters (such as Seed Filter) & duplicate track removal. + + for (unsigned int iPhiSec = 0; iPhiSec < settings_.numPhiSectors(); iPhiSec++) { + for (unsigned int iEtaReg = 0; iEtaReg < settings_.numEtaRegions(); iEtaReg++) { + const Sector* sector = mSectors(iPhiSec, iEtaReg).get(); + + // Get tracks found by r-phi HT. + const HTrphi* htRphi = mHtRphis(iPhiSec, iEtaReg).get(); + const list& vecTracksRphi = htRphi->trackCands2D(); + + // Initialize utility for making 3D tracks from 2D ones. + mMake3Dtrks(iPhiSec, iEtaReg) = std::make_unique( + &settings_, iPhiSec, iEtaReg, sector->etaMin(), sector->etaMax(), sector->phiCentre()); + Make3Dtracks* make3Dtrk = mMake3Dtrks(iPhiSec, iEtaReg).get(); + + // Convert 2D tracks found by HT to 3D tracks (optionally by running r-z filters & duplicate track removal) + make3Dtrk->run(vecTracksRphi); + + if (settings_.enableOutputIntermediateTTTracks()) { + // Convert these tracks to EDM format for output (used for collaborative work outside TMTT group). + // Do this for tracks output by HT & optionally also for those output by r-z track filter. + const list& vecTrk3D_ht = make3Dtrk->trackCands3D(false); + for (const L1track3D& trk : vecTrk3D_ht) { + TTTrack htTTTrack = converter.makeTTTrack(&trk, iPhiSec, iEtaReg); + htTTTracksForOutput->push_back(htTTTrack); + } + + if (runRZfilter_) { + const list& vecTrk3D_rz = make3Dtrk->trackCands3D(true); + for (const L1track3D& trk : vecTrk3D_rz) { + TTTrack rzTTTrack = converter.makeTTTrack(&trk, iPhiSec, iEtaReg); + rzTTTracksForOutput->push_back(rzTTTrack); + } + } + } + } + } + + //=== Do a helix fit to all the track candidates. + + // Loop over all the fitting algorithms we are trying. + for (const string& fitterName : trackFitters_) { + for (unsigned int iPhiSec = 0; iPhiSec < settings_.numPhiSectors(); iPhiSec++) { + for (unsigned int iEtaReg = 0; iEtaReg < settings_.numEtaRegions(); iEtaReg++) { + const Make3Dtracks* make3Dtrk = mMake3Dtrks(iPhiSec, iEtaReg).get(); + + // Does this fitter require r-z track filter to be run before it? + bool useRZfilt = (std::count(useRZfilter_.begin(), useRZfilter_.end(), fitterName) > 0); + + // Get 3D track candidates found by Hough transform (plus optional r-z filters/duplicate removal) in this sector. + const list& vecTrk3D = make3Dtrk->trackCands3D(useRZfilt); + + // Find list where fitted tracks will be stored. + list& fitTrksInSec = mapmFitTrks(iPhiSec, iEtaReg)[fitterName]; + + // Fit all tracks in this sector + for (const L1track3D& trk : vecTrk3D) { + // Ensure stubs assigned to this track is digitized with respect to the phi sector the track is in. + if (settings_.enableDigitize()) { + const vector& stubsOnTrk = trk.stubs(); + for (Stub* s : stubsOnTrk) { + // Also digitize stub in way this specific track fitter uses it. + s->digitize(iPhiSec, Stub::DigiStage::TF); + } + } + + L1fittedTrack fitTrk = fitterWorkerMap_[fitterName]->fit(trk); + + if (fitTrk.accepted()) { // If fitter accepted track, then store it. + // Optionally digitize fitted track, degrading slightly resolution. + if (settings_.enableDigitize()) + fitTrk.digitizeTrack(fitterName); + // Store fitted tracks, such that there is one fittedTracks corresponding to each HT tracks. + fitTrksInSec.push_back(fitTrk); + } + } + } + } + } + + // Run duplicate track removal on the fitted tracks if requested. + + // Initialize the duplicate track removal algorithm that can optionally be run after the track fit. + DupFitTrkKiller killDupFitTrks(&settings_); + + // Loop over all the fitting algorithms we used. + for (const string& fitterName : trackFitters_) { + for (unsigned int iPhiSec = 0; iPhiSec < settings_.numPhiSectors(); iPhiSec++) { + for (unsigned int iEtaReg = 0; iEtaReg < settings_.numEtaRegions(); iEtaReg++) { + // Get fitted tracks in sector + const list& fitTrksInSec = mapmFitTrks(iPhiSec, iEtaReg)[fitterName]; + + // Run duplicate removal + list filteredFitTrksInSec = killDupFitTrks.filter(fitTrksInSec); + + // Prepare TTTrack collection. + for (const L1fittedTrack* fitTrk : filteredFitTrksInSec) { + // Convert these fitted tracks to EDM format for output (used for collaborative work outside TMTT group). + TTTrack fitTTTrack = converter.makeTTTrack(fitTrk, iPhiSec, iEtaReg); + allFitTTTracksForOutput[fitterName]->push_back(fitTTTrack); + } + + // Store fitted tracks from entire tracker. + mapFinalTracks[fitterName].insert( + mapFinalTracks[fitterName].end(), filteredFitTrksInSec.begin(), filteredFitTrksInSec.end()); + } + } + } + + // Debug printout + if (debug_) { + PrintL1trk() << "INPUT #TPs = " << vTPs.size() << " #STUBs = " << vStubs.size(); + unsigned int numHTtracks = 0; + for (unsigned int iPhiSec = 0; iPhiSec < settings_.numPhiSectors(); iPhiSec++) { + for (unsigned int iEtaReg = 0; iEtaReg < settings_.numEtaRegions(); iEtaReg++) { + const Make3Dtracks* make3Dtrk = mMake3Dtrks(iPhiSec, iEtaReg).get(); + numHTtracks += make3Dtrk->trackCands3D(false).size(); + } + } + PrintL1trk() << "Number of tracks after HT = " << numHTtracks; + for (const auto p : mapFinalTracks) { + const string& fitName = p.first; + const list fittedTracks = p.second; + PrintL1trk() << "Number of tracks after " << fitName << " track helix fit = " << fittedTracks.size(); + } + } + + // Allow histogramming to plot undigitized variables. + for (Stub* stub : vStubs) { + if (settings_.enableDigitize()) + stub->setDigitizeWarningsOn(false); + } + + // Fill histograms to monitor input data & tracking performance. + hists_.fill(inputData, mSectors, mHtRphis, mMake3Dtrks, mapFinalTracks); + + //=== Store output EDM track and hardware stub collections. + if (settings_.enableOutputIntermediateTTTracks()) { + iEvent.put(std::move(htTTTracksForOutput), "TML1TracksHT"); + if (runRZfilter_) + iEvent.put(std::move(rzTTTracksForOutput), "TML1TracksRZ"); + } + for (const string& fitterName : trackFitters_) { + string edmName = string("TML1Tracks") + fitterName; + iEvent.put(std::move(allFitTTTracksForOutput[fitterName]), edmName); + } + } + + void TMTrackProducer::globalEndJob(GlobalCacheTMTT* globalCacheTMTT) { + const Settings& settings = globalCacheTMTT->settings(); + + // Print stub window sizes that TMTT recommends CMS uses in FE chips. + if (settings.printStubWindows()) + globalCacheTMTT->stubWindowSuggest().printResults(); + + // Print (once) info about tracker geometry. + globalCacheTMTT->hists().trackerGeometryAnalysis(globalCacheTMTT->listTrackerModule()); + + PrintL1trk() << "\n Number of (eta,phi) sectors used = (" << settings.numEtaRegions() << "," + << settings.numPhiSectors() << ")"; + + // Print job summary + globalCacheTMTT->hists().endJobAnalysis(&(globalCacheTMTT->htRphiErrMon())); + } + +} // namespace tmtt + +DEFINE_FWK_MODULE(tmtt::TMTrackProducer); diff --git a/L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.h b/L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.h new file mode 100644 index 0000000000000..70f9bf6df3e91 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/plugins/TMTrackProducer.h @@ -0,0 +1,98 @@ +#ifndef L1Trigger_TrackFindingTMTT_TMTrackProducer_h +#define L1Trigger_TrackFindingTMTT_TMTrackProducer_h + +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Histos.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h" +#include "L1Trigger/TrackFindingTMTT/interface/GlobalCacheTMTT.h" + +#include "FWCore/Framework/interface/stream/EDProducer.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "DataFormats/L1TrackTrigger/interface/TTTypes.h" +#include "DataFormats/Phase2TrackerDigi/interface/Phase2TrackerDigi.h" +#include "SimDataFormats/TrackingAnalysis/interface/TrackingParticle.h" +#include "SimTracker/TrackTriggerAssociation/interface/TTClusterAssociationMap.h" +#include "SimTracker/TrackTriggerAssociation/interface/TTStubAssociationMap.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "MagneticField/Engine/interface/MagneticField.h" +#include "MagneticField/Records/interface/IdealMagneticFieldRecord.h" +#include "L1Trigger/TrackTrigger/interface/TTStubAlgorithmRecord.h" +#include "L1Trigger/TrackTrigger/interface/TTStubAlgorithm_official.h" +//#include "L1Trigger/TrackTrigger/interface/TTStubAlgorithm.h" + +#include +#include +#include +#include + +namespace tmtt { + + class TrackFitGeneric; + + typedef TTStubAlgorithm StubAlgorithm; + typedef TTStubAlgorithm_official StubAlgorithmOfficial; + + class TMTrackProducer : public edm::stream::EDProducer> { + public: + explicit TMTrackProducer(const edm::ParameterSet &, GlobalCacheTMTT const *globalCacheTMTT); + ~TMTrackProducer() override {} + + static std::unique_ptr initializeGlobalCache(edm::ParameterSet const &iConfig); + + static void globalEndJob(GlobalCacheTMTT *globalCacheTMTT); + + private: + typedef std::vector> TTTrackCollection; + + void beginRun(const edm::Run &, const edm::EventSetup &) override; + + void produce(edm::Event &, const edm::EventSetup &) override; + + private: + // ES tokens + edm::ESGetToken magneticFieldToken_; + edm::ESGetToken trackerGeometryToken_; + edm::ESGetToken trackerTopologyToken_; + edm::ESGetToken ttStubAlgoToken_; + // ED tokens + edm::EDGetTokenT stubToken_; + edm::EDGetTokenT tpToken_; + edm::EDGetTokenT stubTruthToken_; + edm::EDGetTokenT clusterTruthToken_; + edm::EDGetTokenT genJetToken_; + + // Info about tracker geometry + const TrackerGeometry *trackerGeometry_; + const TrackerTopology *trackerTopology_; + std::list listTrackerModule_; + + // Configuration parameters + Settings settings_; + std::vector trackFitters_; + std::vector useRZfilter_; + bool runRZfilter_; + + // Stub window sizes used by FE electronics. + const StubAlgorithmOfficial *stubAlgo_; + std::unique_ptr stubFEWindows_; + StubWindowSuggest &stubWindowSuggest_; + std::unique_ptr degradeBend_; + + Histos &hists_; + HTrphi::ErrorMonitor &htRphiErrMon_; + + std::map> fitterWorkerMap_; + + bool debug_; + }; + +} // namespace tmtt + +#endif diff --git a/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Defaults_cfi.py b/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Defaults_cfi.py new file mode 100644 index 0000000000000..9e52fe7f1fdba --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Defaults_cfi.py @@ -0,0 +1,465 @@ +import FWCore.ParameterSet.Config as cms + +#--------------------------------------------------------------------------------------------------------- +# This describes the full TMTT track reconstruction chain with 3 GeV threshold, where: +# the GP divides the tracker into 18 eta sectors (each sub-divided into 2 virtual eta subsectors); +# the HT uses a 32x18 array followed by 2x2 mini-HT array, with transverese HT readout & multiplexing, +# followed by the KF (or optionally SF+SLR) track fit; duplicate track removal (Algo50) is run. +#--------------------------------------------------------------------------------------------------------- + +TMTrackProducer_params = cms.PSet( + + # Tags for ES products + magneticFieldInputTag = cms.ESInputTag( "VolumeBasedMagneticFieldESProducer", "" ), + trackerGeometryInputTag = cms.ESInputTag( "trackerGeometry", "" ), + trackerTopologyInputTag = cms.ESInputTag( "trackerTopology", "" ), + ttStubAlgoInputTag = cms.ESInputTag( "TTStubAlgorithm_official_Phase2TrackerDigi_", "" ), + + # Tags for ED products + tpInputTag = cms.InputTag("mix", "MergedTrackTruth"), + stubInputTag = cms.InputTag("TTStubsFromPhase2TrackerDigis", "StubAccepted"), + stubTruthInputTag = cms.InputTag("TTStubAssociatorFromPixelDigis", "StubAccepted"), + clusterTruthInputTag = cms.InputTag("TTClusterAssociatorFromPixelDigis", "ClusterAccepted"), + genJetInputTag = cms.InputTag("ak4GenJets", ""), + + # Enable output of TTTracks from part-way through tracking chain (after HT & RZ). + EnableOutputIntermediateTTTracks = cms.bool(False), + + # Enable all use of MC truth info (disable to save CPU) + EnableMCtruth = cms.bool(False), + # Enable output histograms & job tracking performance summary (disable to save CPU) + EnableHistos = cms.bool(False), + + #=== Cuts on MC truth particles (i.e., tracking particles) used for tracking efficiency measurements. + + GenCuts = cms.PSet( + GenMinPt = cms.double(3.0), + GenMaxAbsEta = cms.double(2.4), + GenMaxVertR = cms.double(1.0), # Max distance of particle production vertex from centre of CMS. + GenMaxVertZ = cms.double(30.0), + GenMaxD0 = cms.double(5.0), # Max transverse impact parameter. + GenMaxZ0 = cms.double(999.0), # Max transverse impact parameter. + GenPdgIds = cms.vuint32(), # Only particles with these PDG codes used for efficiency measurement. + + # Cut on MC truth tracks used for algorithmic tracking efficiency measurements. + GenMinStubLayers = cms.uint32(4) + ), + + #=== Cuts applied to stubs before arriving in L1 track finding board. + + StubCuts = cms.PSet( + # Reduce number of bits used by front-end chips to store stub bend info? + # = 0 (no); = 1 (yes using official recipe); = 2 (yes using TMTT method) + DegradeBendRes = cms.uint32(2), + # Don't use stubs with eta beyond this cut, since the tracker geometry makes it impossible to reconstruct tracks with them. + MaxStubEta = cms.double(2.4), + # Don't use stubs whose measured Pt from bend info is significantly below HTArraySpec.HoughMinPt, where "significantly" means allowing for resolution in q/Pt derived from stub bend resolution specified below. + KillLowPtStubs = cms.bool(True), + # Print FE stub window sizes recommended by this code (in python cfg format used by CMSSW). + PrintStubWindows = cms.bool(False), + # Bend resolution assumed by bend filter in units of strip pitch. Also used when assigning stubs to sectors if EtaPhiSectors.CalcPhiTrkRes=True. And by the bend filter if HTFillingRphi.UseBendFilter=True. + # Suggested value: 1.19 if DegradeBendRes = 0, or 1.249 if it > 0. + # N.B. Avoid 1/4-integer values due to rounding error issues. + BendCut = cms.double(1.249), + # Additional contribution to bend resolution from its encoding into a reduced number of bits. + # This number is the assumed resolution relative to the naive guess of its value. + # It is ignored in DegradeBendRes = 0. + BendCutExtra = cms.double(0.0), + # Order stubs by bend in DTC, such that highest Pt stubs are transmitted first. + OrderStubsByBend = cms.bool(True) + ), + + #=== Optional Stub digitization. + + StubDigitize = cms.PSet( + EnableDigitize = cms.bool(True), # Digitize stub coords? If not, use floating point coords. + # + #--- Parameters available in MP board. (And in case of Hybrid used internally in KF) + # + PhiSectorBits = cms.uint32(6), # Bits used to store phi sector number -- NOT USED + PhiSBits = cms.uint32(14), # Bits used to store phiS coord. (13 enough?) + PhiSRange = cms.double(0.698131700), # Range phiS coord. covers in radians. + RtBits = cms.uint32(12), # Bits used to store Rt coord. + RtRange = cms.double(91.652837), # Range Rt coord. covers in units of cm. + ZBits = cms.uint32(14), # Bits used to store z coord. + ZRange = cms.double(733.2227), # Range z coord. covers in units of cm. + # + #--- Parameters available in GP board (excluding any in common with MP specified above). + # + PhiNBits = cms.uint32(15), # Bits used to store PhiO parameter. + PhiNRange = cms.double(1.3962634), # Range PhiO parameter covers. + BendBits = cms.uint32(6) # Bits used to store stub bend. + ), + + #=== Configuration of tracker module type. Only provides test data for firmware. + + TrackerModuleType = cms.PSet( + # Modules matching these criteria are type 0, 1, 2, 3 ... + PitchVsType = cms.vdouble(0.0099, 0.0099, 0.0099, 0.0099, 0.0089, 0.0099, 0.0089, 0.0089), + SpaceVsType = cms.vdouble(0.26 , 0.26 , 0.16 , 0.4 , 0.18 , 0.4 , 0.18 , 0.4 ), + # (Type vbool not implemented, so use vuint32 instead ...) + BarrelVsType = cms.vuint32( 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), + PSVsType = cms.vuint32( 1 , 1 , 1 , 1 , 0 , 1 , 0 , 0 ), + TiltedVsType = cms.vuint32( 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 ) + ), + + #=== Configuration of Geometric Processor. + + GeometricProc = cms.PSet( + # Use an FPGA-friendly approximation to determine track angle dphi from bend in GP? + UseApproxB = cms.bool(True), # Use approximation for B + # Params of approximation if used. + BApprox_gradient = cms.double(0.886454), # Gradient term of linear equation for approximating B + BApprox_intercept = cms.double(0.504148) # Intercept term of linear equation for approximating B + ), + + #=== Division of Tracker into phi sectors. + + PhiSectors = cms.PSet( + NumPhiNonants = cms.uint32(9), # Divisions of Tracker at DTC + NumPhiSectors = cms.uint32(18), # Divisions of Tracker at GP. + ChosenRofPhi = cms.double(67.240), # Use phi of track at this radius for assignment of stubs to phi sectors & also for one of the axes of the r-phi HT. If ChosenRofPhi=0, then use track phi0. - Should be an integer multiple of the stub r digitisation granularity. + #--- You can set one or both the following parameters to True. + UseStubPhi = cms.bool(True), # Require stub phi to be consistent with track of Pt > HTArraySpec.HoughMinPt that crosses HT phi axis? + UseStubPhiTrk = cms.bool(True), # Require stub phi0 (or phi65 etc.) as estimated from stub bend, to lie within HT phi axis, allowing tolerance(s) specified below? + AssumedPhiTrkRes = cms.double(0.5), # Tolerance in stub phi0 (or phi65) assumed to be this fraction of phi sector width. (N.B. If > 0.5, then stubs can be shared by more than 2 phi sectors). + CalcPhiTrkRes = cms.bool(True) # If true, tolerance in stub phi0 (or phi65 etc.) will be reduced below AssumedPhiTrkRes if stub bend resolution specified in StubCuts.BendCut suggests it is safe to do so. + ), + + #=== Division of Tracker into eta sectors + + EtaSectors = cms.PSet( +# Eta boundaries for 18 eta regions +# EtaRegions = cms.vdouble(-2.4,-2.16,-1.95,-1.7,-1.43,-1.16,-0.89,-0.61,-0.31,0.0,0.31,0.61,0.89,1.16,1.43,1.7,1.95,2.16,2.4), +# Eta boundaries for 16 eta regions + EtaRegions = cms.vdouble(-2.4,-2.08,-1.68,-1.26,-0.90,-0.62,-0.41,-0.20,0.0,0.20,0.41,0.62,0.90,1.26,1.68,2.08,2.4), + ChosenRofZ = cms.double(50.), # Use z of track at this radius for assignment of tracks to eta sectors & also for one of the axes of the r-z HT. Do not set to zero! + BeamWindowZ = cms.double(15), # Half-width of window assumed to contain beam-spot in z. + AllowOver2EtaSecs = cms.bool(True) # If True, the code will not throw an error if a stub is assigned to 3 or more eta sectors. + ), + + #=== r-phi Hough transform array specifications. + + HTArraySpecRphi = cms.PSet( + HoughMinPt = cms.double(3.0), # Min track Pt that Hough Transform must find. Also used by StubCuts.KillLowPtStubs and by EtaPhiSectors.UseStubPhi. + # If MiniHTstage = True, these refers to mini cells in whole HT array. + HoughNbinsPt = cms.uint32(32), # HT array dimension in track q/Pt. (If MiniHTstage = True, this refers to mini cells in whole HT array). + HoughNbinsPhi = cms.uint32(64), # HT array dimension in track phi0 (or phi65 or any other track phi angle. (If MiniHTstage = True, this refers to mini cells in whole HT array). + EnableMerge2x2 = cms.bool(False), # Groups of neighbouring 2x2 cells in HT will be treated as if they are a single large cell? N.B. You can only enable this option if your HT array has even numbers of bins in both dimensions. And this cfg param ignored if MiniHTstage = True. HISTORIC OPTION. SUGGEST NOT USING! + MaxPtToMerge2x2 = cms.double(3.5), # but only cells with pt < MaxPtToMerge2x2 will be merged in this way (irrelevant if EnableMerge2x2 = false). + NumSubSecsEta = cms.uint32(2), # Subdivide each sector into this number of subsectors in eta within r-phi HT. + Shape = cms.uint32(0), # cell shape: 0 for square, 1 for diamond, 2 hexagon (with vertical sides), 3 square with alternate rows shifted by 0.5*cell_width. + MiniHTstage = cms.bool(True), # Run 2nd stage HT with mini cells inside each 1st stage normal HT cell.. + MiniHoughNbinsPt = cms.uint32(2), # Number of mini cells along q/Pt axis inside each normal HT cell. + MiniHoughNbinsPhi = cms.uint32(2), # Number of mini cells along phi axis inside each normal HT cell. + MiniHoughMinPt = cms.double(3.0), # Below this Pt threshold, the mini HT will not be used, to reduce sensitivity to scattering, with instead tracks found by 1st stage coarse HT sent to output. (HT cell numbering remains as if mini HT were in use everywhere). + MiniHoughDontKill = cms.bool(False), # If true, allows tracks found by 1st stage coarse HT to be output if 2nd stage mini HT finds no tracks. + MiniHoughDontKillMinPt = cms.double(8.0), # If MiniHoughDontKill=True, this option restricts it to keep 1st stage HT tracks only if their Pt is exceeds this cut. (Used to improve electron tracking above this threshold). + MiniHoughLoadBalance = cms.uint32(2) # Load balancing disabled = 0; static load balancing of output links = 1; dynamic load balancing of output links = 2. + ), + + #=== Rules governing how stubs are filled into the r-phi Hough Transform array. + + HTFillingRphi = cms.PSet( + # Take all cells in r-phi HT array crossed by line corresponding to each stub (= 0) or take only some to reduce rate at cost + # of efficiency ( > 0). If this option is > 0, it can be 1 or 2, corresponding to different algorithms for rejecting + # some of the cells. "1" is an algorithm invented by Ian, whereas "2" corresponds to Thomas' 1st firmware implementation which only handled 1 cell per HT column. + # Suggest setting KillSomeHTCellsRphi=1 (=0) if HTArraySpec.ChosenRofPhi=0 (>0) + KillSomeHTCellsRphi = cms.uint32(0), + # Use filter in each r-phi HT cell, filling it only with stubs that have consistent bend information? + # The assumed bend resolution is specified in StubCuts.BendCut. + UseBendFilter = cms.bool(True), + # Use filter in each HT cell, preventing more than the specified number of stubs being stored in the cell. (Reflecting memory limit of hardware). N.B. Results depend on assumed order of stubs. + # N.B. If mini-HT is in use, then this cut applies to coarse-HT. + #MaxStubsInCell = cms.uint32(99999), # Setting this to anything more than 999 disables this option + MaxStubsInCell = cms.uint32(32), # set it equal to value used in hardware. + MaxStubsInCellMiniHough = cms.uint32(16), # Same type of cut for mini-HT (if in use) + # If BusySectorKill = True, and more than BusySectorNumStubs stubs are assigned to tracks by an r-phi HT array, then the excess tracks are killed, with lowest Pt ones killed first. This is because HT hardware has finite readout time. + BusySectorKill = cms.bool(True), + BusySectorNumStubs = cms.uint32(162), # Or 144 if only 320 MHz FW. + # If BusySectorMbinRanges is not empty, then the BusySectorNumStubs cut is instead applied to the subset of tracks appearing in the following m bin (q/Pt) ranges of the HT array. The sum of the entries in the vector should equal the number of m bins in the HT. (N.B. If EnableMerge2x2 or MiniHTstage = True, then the m bin ranges here correspond to the bins before merging. Also in these cases, the odd m-bin numbers don't correspond to HT outputs, so should be all grouped together on a single imaginary link). + # If BusySectorMbinOrder is not empty, then the m-bins are grouped in the specified order, instead of sequentially. + # (Histos NumStubsPerLink, NumStubsVsLink & MeanStubsPerLink useful for optimising this option). + # + # Choice for 16x32 coarse HT array followed by 2x2 mini-HT array with 3 GeV Pt threshold. + BusySectorMbinRanges = cms.vuint32(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 16), + BusySectorMbinOrder = cms.vuint32(0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30, 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31), + # Choice for 24x32 coarse HT array followed by 2x2 mini-HT array with 2 GeV Pt threshold. + #BusySectorMbinRanges = cms.vuint32(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 24), + #BusySectorMbinOrder = cms.vuint32(0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46, 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47), + # + # If BusyInputSectorKill = True, and more than BusyInputSectorNumStubs are input to the HT array from the GP, then + # the excess stubs are killed. This is because HT hardware has finite readin time. + # Results unreliable as depend on assumed order of stubs. + BusyInputSectorKill = cms.bool(True), + BusyInputSectorNumStubs = cms.uint32(162), # Or 144 if only 320 MHz FW + # Multiplex the outputs from several HTs onto a single pair of output optical links? + # Options: 0 = disable Mux; 1 = Sept 2019 Mux (transerse HT readout by m-bin), with single m bin in entire nonant going to each link. + MuxOutputsHT = cms.uint32(1), + # If this is non-empty, then only the specified eta sectors are enabled, to study them individually. + EtaRegWhitelist = cms.vuint32() + ), + + #=== Options controlling r-z track filters (or any other track filters run after the Hough transform, as opposed to inside it). + #=== (Irrelevant for track fitters that don't require any r-z filter run before them). + + RZfilterOpts = cms.PSet( + # Specify preferred r-z filter (from those available inside TrkRZfilter.cc) - currently only "SeedFilter". + RZFilterName = cms.string("SeedFilter"), + #--- Options relevant for Seed filter, (so only relevant if rzFilterName="SeedFilter"). + # Cut at this many standard deviations on seed resolution. + SeedResCut = cms.double(1.732), + # Store stubs compatible with all possible good seed. + KeepAllSeed = cms.bool(False), + # Maximum number of seed combinations to bother checking per track candidate. + #MaxSeedCombinations = cms.uint32(999), + MaxSeedCombinations = cms.uint32(15), + # Maximum number of seed combinations consistent with (z0,eta) sector constraints to bother checking per track candidate. + #MaxGoodSeedCombinations = cms.uint32(13), + MaxGoodSeedCombinations = cms.uint32(10), + # Maximum number of seeds that a single stub can be included in. + MaxSeedsPerStub = cms.uint32(4), + # Reject tracks whose estimated rapidity from seed filter is inconsistent range of with eta sector. (Kills some duplicate tracks). + zTrkSectorCheck = cms.bool(True), + # Min. number of layers in rz track that must have stubs for track to be declared found by seed filter. + MinFilterLayers = cms.uint32(4) + ), + + #=== Rules for deciding when the (HT) track finding has found an L1 track candidate + + L1TrackDef = cms.PSet( + # Min. number of layers the track must have stubs in. + MinStubLayers = cms.uint32(5), + # Change min. number of layers cut to (MinStubLayers - 1) for tracks with Pt exceeding this cut. + # If this is set to a -ve number, this option is disabled. + MinPtToReduceLayers = cms.double(-99999.), + # Change min. number of layers cut to (MinStubLayers - 1) for tracks in these rapidity sectors. + # (Histogram "AlgEffVsEtaSec" will help you identify which sectors to declare). + #EtaSecsReduceLayers = cms.vuint32(), + EtaSecsReduceLayers = cms.vuint32(5,12), + # Reduce this layer ID, so that it takes no more than 8 different values in any eta region (simplifies firmware). + ReducedLayerID = cms.bool(True) + ), + + #=== Specification of algorithm to eliminate duplicate tracks. + + DupTrkRemoval = cms.PSet( + # Algorithm run on tracks after the track helix fit has been done. + # (Disable dup removal = 0; two alternative algos = 1 or 2). + DupTrkAlgFit = cms.uint32(1) + ), + + #=== Rules for deciding when a reconstructed L1 track matches a MC truth particle (i.e. tracking particle). + + TrackMatchDef = cms.PSet( + #--- Three different ways to define if a tracking particle matches a reco track candidate. (Usually, set two of them to ultra loose). + # Min. fraction of matched stubs relative to number of stubs on reco track. + MinFracMatchStubsOnReco = cms.double(-99.), + # Min. fraction of matched stubs relative to number of stubs on tracking particle. + MinFracMatchStubsOnTP = cms.double(-99.), + # Min. number of matched layers. + MinNumMatchLayers = cms.uint32(4), + # Min. number of matched PS layers. + MinNumMatchPSLayers = cms.uint32(0), + # Associate stub to TP only if the TP contributed to both its clusters? (If False, then associate even if only one cluster was made by TP). + StubMatchStrict = cms.bool(False) + ), + + #=== Track Fitting Algorithm Settings. + + TrackFitSettings = cms.PSet( + # + #--- Options applicable to all track fitters --- + # + # Track Fitting algortihms to use. You can run several in parallel. + # TrackFitLinearAlgo & ChiSquared* are chi2 fits, KF* is a Kalman filter fit, + # & SimpleLR4 is a linear regression fit that neglects the hit uncertainties. + # The number 4 or 5 in the name indicates if 4 or 5 helix parameters are fitted. + # Options KF4ParamsComb, KF5ParamsComb or SimpleLR4 are the best ones. + # KF*ParamsCombHLS is the HLS version of the code, which only works if linked with Vivado libraries. + TrackFitters = cms.vstring( + # "ChiSquaredFit4", + # "SimpleLR4", + # "KF4ParamsCombHLS", + # "KF5ParamsCombHLS", + "KF5ParamsComb", + "KF4ParamsComb" + ), + # Indicate subset of fitters wanting r-z track filter to be run before them. (Irrelevant for those not specified in "TrackFitters"). + # Typically, Chi2 & LR fits work best with r-z filter & KF works best without it. + UseRZfilter = cms.vstring( + "ChiSquaredFit4", + "SimpleLR4" + ), + # Print detailed summary of track fit performance at end of job (as opposed to a brief one). + DetailedFitOutput = cms.bool(False), + # + # Use MC truth to eliminate all fake tracks & all incorrect stubs assigned to tracks before doing fit. + TrackFitCheat = cms.bool(False), + # + #--- Options for chi2 track fitter --- + # + # Number of fitting iterations to undertake. (15 is not realistic in hardware, but is necessary to kill bad hits) + NumTrackFitIterations = cms.uint32(15), + # Optionally kill hit with biggest residuals in track fit (occurs after the first fit, so three iterations would have two killings). + KillTrackFitWorstHit = cms.bool(True), + # Cuts in standard deviations used to kill hits with big residuals during fit. If the residual exceeds the "General" cut, the hit is killed providing it leaves the track with enough hits to survive. If the residual exceeds the "Killing" cut, the hit is killed even if that kills the track. + GeneralResidualCut = cms.double(3.0), + KillingResidualCut = cms.double(20.0), + # + #--- Additional options for Thomas Schuh's Linear Regression track fitter --- + # + # Maximum allowed number of iterations of LR fitter. + MaxIterationsLR = cms.uint32( 8 ), + # If False: residual of a stub is the max of its r-phi & r-z residuals. + # If True: the residual is the mean of these residuals. + CombineResiduals = cms.bool( True ), + # Correct stub phi coordinate for higher orders in circle expansion, so that a trajectory is straight in r-phi. + LineariseStubPosition = cms.bool( True ), + # Checks if the fitted track is consistent with the sector, if not it will be not accepted. + CheckSectorConsistency = cms.bool( False ), + # Checks if the fitted track r phi parameter are consistent with the HT candidate parameter within in range of +- 2 cells. + CheckHTCellConsistency = cms.bool( False ), + # Tracks must have stubs in at least this number of PS layers. + MinPSLayers = cms.uint32( 2 ), + # Digitization + DigitizeLR = cms.bool( False ), + PhiPrecision = cms.double( 0.009 / 108. ), + RPrecision = cms.double( 0.14 ), + ZPrecision = cms.double( 0.28 ), + ZSlopeWidth = cms.uint32( 11 ), + ZInterceptWidth = cms.uint32( 11 ), + # + #--- Additional options for Davide Cieri's Simple Linear Regression track fitter --- + # + # Digitize Simple Linear Regression variables and calculation. (Disabled if EnableDigitize=False). + DigitizeSLR = cms.bool(False), # Disable, as was never retuned for nonants + # Number of bits to be used in hardware to compute the division needed to calculate the helix params + DividerBitsHelix = cms.uint32(23), + DividerBitsHelixZ = cms.uint32(23), + # Number of bits to reduce the rphi helix parameter calculation weight + ShiftingBitsDenRPhi = cms.uint32(14), + # Number of bits to reduce the rphi helix parameter calculation weight + ShiftingBitsDenRZ = cms.uint32(14), + # Number of bits to reduce the phi0 parameter calculation weight + ShiftingBitsPhi = cms.uint32(10), + # Number of bits to reduce the qOverPt parameter calculation weight + ShiftingBitsPt = cms.uint32(3), + # Number of bits to reduce the tanLambda parameter calculation weight + ShiftingBitsLambda = cms.uint32(1), + # Number of bits to reduce the z0 parameter calculation weight + ShiftingBitsZ0 = cms.uint32(16), + # Fit ChiSquare Cut (tightening reduces fake track rate at cost of efficiency) + SLR_chi2cut = cms.double(300.), + # Cut on Rphi Residuals (radians) - stubs killed until only 4 left or all have residuals below this cut. + ResidualCut = cms.double(0.0), + #ResidualCut = cms.double(0.0005), # This allows more than 4 stubs per track. + # + #--- Options for Kalman filter track fitters --- + # + # Larger number has more debug printout. "1" is useful for understanding why tracks are lost, best combined with TrackFitCheat=True. + KalmanDebugLevel = cms.uint32(0), + # Fit will reject fitted tracks unless it can assign at least this number of stubs to them. + KalmanMinNumStubs = cms.uint32(4), + # Fit will attempt to add up to this nummber of stubs to each fitted tracks, but won't bother adding more. + KalmanMaxNumStubs = cms.uint32(4), + # For 5-param helix fits, calculate also beam-constrained helix params after fit is complete, & use them for duplicate removal if DupTrkAlgFit=1. + KalmanAddBeamConstr = cms.bool(True), + # Remove requirement of at least 2 PS layers per track. + KalmanRemove2PScut = cms.bool(False), + # Allow the KF to skip this many layers in total per track. + KalmanMaxSkipLayersHard = cms.uint32(1), # For HT tracks with many stubs + KalmanMaxSkipLayersEasy = cms.uint32(2), # For HT tracks with few stubs + KalmanMaxStubsEasy = cms.uint32(10), # Max stubs an HT track can have to be "easy". + #--- Cuts applied to KF states as a function of the last KF tracker layer they had a stub in. + # (If "4" or "5" in name, cut only applies to 4 or 5 param helix fit). + KFLayerVsPtToler = cms.vdouble(999., 999., 0.1 , 0.1 , 0.05, 0.05, 0.05), + # d0 cut only applied to 5 param helix fit. + KFLayerVsD0Cut5 = cms.vdouble(999., 999., 999., 10. , 10. , 10. ,10. ), + KFLayerVsZ0Cut5 = cms.vdouble(999., 999., 25.5, 25.5, 25.5, 25.5,25.5 ), + KFLayerVsZ0Cut4 = cms.vdouble(999., 999. ,15. , 15. , 15. , 15. ,15. ), + # Chi2 cuts should be retuned if KalmanMultiScattTerm value changed. + KFLayerVsChiSq5 = cms.vdouble(999., 999., 10. , 30. , 80. , 120., 160.), + KFLayerVsChiSq4 = cms.vdouble(999., 999., 10. , 30. , 80. , 120., 160.), + # KF will consider at most this #stubs per layer to save time. + KalmanMaxStubsPerLayer = cms.uint32(4), + # Multiple scattering term - inflate hit phi errors by this divided by Pt + # (0.00075 gives best helix resolution & 0.00450 gives best chi2 distribution). + KalmanMultiScattTerm = cms.double(0.00075), + # Scale down chi2 in r-phi plane by this factor to improve electron performance (should be power of 2) + KalmanChi2RphiScale = cms.uint32(8), + # N.B. KF track fit chi2 cut is not cfg param, but instead is hard-wired in KF4ParamsComb::isGoodState(...). + #--- Enable Higher order corrections + # Treat z uncertainty in tilted barrel modules correctly. + KalmanHOtilted = cms.bool(False), + # Higher order circle explansion terms for low Pt. + KalmanHOhelixExp = cms.bool(False), + # Alpha correction for non-radial 2S endcap strips. (0=disable correction, 1=correct with offset, 2=correct with non-diagonal stub covariance matrix). -- Option 1 is easier in FPGA, but only works if fit adds PS stubs before 2S ones. + KalmanHOalpha = cms.uint32(0), + # Projection from (r,phi) to (z,phi) for endcap 2S modules. (0=disable correction, 1=correct with offset, 2=correct with non-diagonal stub covariance matrix). -- Option 1 is easier in FPGA, but only works if fit adds PS stubs before 2S ones. + KalmanHOprojZcorr = cms.uint32(0), + # Use approx calc to account for non-radial endcap 2S modules corresponding to current FW, with no special treatment for tilted modules. + KalmanHOfw = cms.bool(True) + ), + + #=== Treatment of dead modules. + + DeadModuleOpts = cms.PSet( + # Emulate dead/inefficient modules using the StubKiller code, with stubs killed according to the scenarios of the Stress Test group. + # (0=Don't kill any stubs; 1-5 = Scenarios described in StubKiller.cc) + KillScenario = cms.uint32(0), + # Modify TMTT tracking to try to recover tracking efficiency in presence of dead modules. (Does nothing if KillScenario = 0). + KillRecover = cms.bool (True) + ), + + #=== Fitted track digitisation. + + TrackDigi=cms.PSet( + # For firmware reasons, can't use common digitisation cfg for all fitters. + + #======= SimpleLR4 digi parameters ======== + SLR_skipTrackDigi = cms.bool( False ), # Optionally skip track digitisation if done internally inside fitting code. + SLR_oneOver2rBits = cms.uint32(13), + SLR_oneOver2rRange = cms.double(0.01354135), + SLR_d0Bits = cms.uint32(12), # Made up by Ian as never yet discussed. + SLR_d0Range = cms.double(10.), + SLR_phi0Bits = cms.uint32(18), + SLR_phi0Range = cms.double(1.3962636), # phi0 is actually only digitised relative to centre of sector. + SLR_z0Bits = cms.uint32(12), + SLR_z0Range = cms.double(51.555509), + SLR_tanlambdaBits = cms.uint32(15), + SLR_tanlambdaRange = cms.double(32.0), + SLR_chisquaredBits = cms.uint32(10), + SLR_chisquaredRange = cms.double(512.), + + #====== Kalman Filter digi parameters ======== + KF_skipTrackDigi = cms.bool( False ), # Optionally skip track digitisation if done internally inside fitting code. + KF_oneOver2rBits = cms.uint32(15), + KF_oneOver2rRange = cms.double(0.0076171313), # pT > 1.5 GeV + KF_d0Bits = cms.uint32(12), + KF_d0Range = cms.double(31.992876), + KF_phi0Bits = cms.uint32(12), + KF_phi0Range = cms.double(0.6981317), # phi0 digitised relative to centre of sector. (Required range 2pi/18 + 2*overlap; overlap = 0.19206rads*(2GeV/ptCut)*(chosenR/67.24). MUST DOUBLE TO GO TO 2 GEV. + KF_z0Bits = cms.uint32(12), + KF_z0Range = cms.double(45.826419), + KF_tanlambdaBits = cms.uint32(16), + KF_tanlambdaRange = cms.double(16.), + KF_chisquaredBits = cms.uint32(15), # N.B. 17 bits are used internally inside KF. + KF_chisquaredRange = cms.double(1024.), + KF_chisquaredBinEdges = cms.vdouble(0, 0.5, 1, 2, 3, 5, 7, 10, 20, 40, 100, 200, 500, 1000, 3000 ), # Additional bin for >3000 + KF_bendchisquaredBinEdges = cms.vdouble(0, 0.5, 1, 2, 3, 5, 10, 50 ), # Additional bin for >50 + + #====== Other track fitter Digi params. + # Currently equal to those for KF, although you can skip track digitisation for them with following. + Other_skipTrackDigi = cms.bool( True ) + ), + + #===== Use HYBRID TRACKING (Tracklet pattern reco + TMTT KF -- requires tracklet C++ too) ===== + + Hybrid = cms.bool( False), + + #===== Debug plot options + # When making helix parameter resolution plots, only use particles from the physics event (True) + # or also use particles from pileup (False) ? + ResPlotOpt = cms.bool (True) +) diff --git a/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Ultimate_cff.py b/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Ultimate_cff.py new file mode 100644 index 0000000000000..626399a8f8915 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_Ultimate_cff.py @@ -0,0 +1,77 @@ +import FWCore.ParameterSet.Config as cms + +#--------------------------------------------------------------------------------------------------------- +# This describes the full TMTT track reconstruction chain with 2 GeV threshold, where: +# the GP divides the tracker into 18 eta sectors (each sub-divided into 2 virtual eta subsectors); +# the HT uses a 32x24 array followed by 2x2 Mini-HT array, with transverese HT readout & multiplexing, +# followed by the track fit (KF); duplicate track removal (Algo1) is run. +# +# It represents the tracking as planned for 2026. It is a good basis for L1 trigger studies etc. +#--------------------------------------------------------------------------------------------------------- + +#=== TMTT tracking needs to get FE stub window sizes from this. + +from L1Trigger.TrackTrigger.TTStubAlgorithmRegister_cfi import * + +#=== Random number generator for Stub Killer (dead module emulation) + +RandomNumberGeneratorService = cms.Service("RandomNumberGeneratorService", + TMTrackProducer = cms.PSet(initialSeed = cms.untracked.uint32(12345)) +) + +#=== Import default values for all parameters & define EDProducer. + +from L1Trigger.TrackFindingTMTT.TMTrackProducer_Defaults_cfi import TMTrackProducer_params + +TMTrackProducer = cms.EDProducer('tmtt::TMTrackProducer', + # Load cfg parameters from TMTrackProducer_Defaults_cfi.py + TMTrackProducer_params +) + +#=================================================================================================== +# Uncomment the following 2 lines to enable use of MC truth info & output histograms. +# (This costs CPU, and is unnecessary if you only care about producing TTTrack collection). +#=================================================================================================== + +#TMTrackProducer.EnableMCtruth = True +#TMTrackProducer.EnableHistos = True + +#=================================================================================================== +#=== The following override the default values. +#=================================================================================================== + +#--- Configure track fitter(s). + +# Use only 4 parameter helix fit Kalman Filter. +TMTrackProducer.TrackFitSettings.TrackFitters = ["KF4ParamsComb"] + +# Allow KF to assign stubs in up to this many layers to fitted tracks. +TMTrackProducer.TrackFitSettings.KalmanMaxNumStubs = 6 +# Enable more sophisticated fit mathematics in KF. +TMTrackProducer.TrackFitSettings.KalmanHOtilted = True +TMTrackProducer.TrackFitSettings.KalmanHOhelixExp = True +TMTrackProducer.TrackFitSettings.KalmanHOalpha = 1 +TMTrackProducer.TrackFitSettings.KalmanHOprojZcorr = 1 +TMTrackProducer.TrackFitSettings.KalmanHOfw = False + +#--- Switch on 2nd stage Mini HT with 2 GeV Pt threshold & allow it to find tracks with stubs in as few as 4 layers. + +TMTrackProducer.HTArraySpecRphi.HoughNbinsPt = 48 +TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = 64 +TMTrackProducer.GenCuts.GenMinPt = 2.0 +TMTrackProducer.HTArraySpecRphi.HoughMinPt = 2.0 +TMTrackProducer.HTArraySpecRphi.MiniHoughMinPt = 3.0 # Mini-HT not used below this Pt, to reduce sensitivity to scattering. +TMTrackProducer.L1TrackDef.MinStubLayers = 4 +TMTrackProducer.HTFillingRphi.BusySectorMbinRanges = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 24] +TMTrackProducer.HTFillingRphi.BusySectorMbinOrder = [0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47] + +#--- phi digitisation range needs to be increased to go down to 2 GeV. +#--- phi0 digitised relative to centre of sector. (Required range 2pi/18 + 2*overlap; overlap = 0.19206rads*(2GeV/ptCut)*(chosenR/67.24) + +TMTrackProducer.TrackDigi.KF_phi0Range = 2*0.6981317 +# FIX: To make this change in KF FW, change phi0 bit selection in DRstate.vhd to bits 17-6 (instead of 16-5). + +# MuxHToutputs sends all HT outputs for an entire nonant and 1 m-bin to a single output link. +# This works for Pt > 3 GeV, gives truncation for Pt > 2 GeV. To solve, need to double number of outputs, +# with one for each phi sector in nonant. Not yet implemented, so for now disable HT output truncation. +TMTrackProducer.HTFillingRphi.BusySectorNumStubs = 999 diff --git a/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_cff.py b/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_cff.py new file mode 100644 index 0000000000000..498c7e25b3b5a --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/python/TMTrackProducer_cff.py @@ -0,0 +1,165 @@ +import FWCore.ParameterSet.Config as cms + +#--------------------------------------------------------------------------------------------------------- +# This describes the full TMTT track reconstruction chain with 3 GeV threshold, where: +# the GP divides the tracker into 16 eta sectors; +# the HT uses a 32x16 array followed by 2x2 Mini-HT array, with transverese HT readout & multiplexing, +# followed by the track fit (KF); and duplicate track removal (Algo1) is run. +# +# This usually corresponds to the current firmware. +#--------------------------------------------------------------------------------------------------------- + + +#=== TMTT tracking needs to get FE stub window sizes from this. + +from L1Trigger.TrackTrigger.TTStubAlgorithmRegister_cfi import * + +#=== Random number generator for Stub Killer (dead module emulation) + +RandomNumberGeneratorService = cms.Service("RandomNumberGeneratorService", + TMTrackProducer = cms.PSet(initialSeed = cms.untracked.uint32(12345)) +) + +#=== Import default values for all parameters & define EDProducer. + +from L1Trigger.TrackFindingTMTT.TMTrackProducer_Defaults_cfi import TMTrackProducer_params + +TMTrackProducer = cms.EDProducer('tmtt::TMTrackProducer', + # Load cfg parameters from TMTrackProducer_Defaults_cfi.py + TMTrackProducer_params +) + +#=================================================================================================== +# Uncomment the following 2 lines to enable use of MC truth info & output histograms. +# (This costs CPU, and is unnecessary if you only care about producing TTTrack collection). +#=================================================================================================== + +#TMTrackProducer.EnableMCtruth = cms.bool(True) +#TMTrackProducer.EnableHistos = cms.bool(True) + +#=================================================================================================== +#=== All the following parameters already have identical values in TMTrackProducer_Defaults_cfi . +#=== They are listed here just to remind you of the most interesting parameters to play with. +#=================================================================================================== + +#--- Configure track fitting + +# Use only 4 parameter helix fit Kalman Filter (which automatically runs on tracks produced with no r-z track filter) +#TMTrackProducer.TrackFitSettings.TrackFitters = cms.vstring("KF4ParamsComb") + +# Allow KF to assign stubs in up to this many layers to fitted tracks. +#TMTrackProducer.TrackFitSettings.KalmanMaxNumStubs = cms.uint32(6) +# Enable more sophisticated fit mathematics in KF. +#TMTrackProducer.TrackFitSettings.KalmanHOtilted = cms.bool(True) +#TMTrackProducer.TrackFitSettings.KalmanHOhelixExp = cms.bool(True) +#TMTrackProducer.TrackFitSettings.KalmanHOalpha = cms.uint32(2) +#TMTrackProducer.TrackFitSettings.KalmanHOprojZcorr = cms.uint32(2) +#TMTrackProducer.TrackFitSettings.KalmanHOfw = cms.bool(False) + +#--- Switch off parts of the track reconstruction chain. + +#TMTrackProducer.DupTrkRemoval.DupTrkAlgFit = cms.uint32(0) +#TMTrackProducer.TrackFitSettings.TrackFitters = cms.vstring() + +#--- Keep Pt threshold at 3 GeV, with coarse HT, but switch off Mini-HT. + +#TMTrackProducer.HTArraySpecRphi.MiniHTstage = cms.bool(False) +#TMTrackProducer.HTFillingRphi.MaxStubsInCell = cms.uint32(16) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPt = cms.uint32(16) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = cms.uint32(32) +#TMTrackProducer.HTFillingRphi.BusySectorMbinRanges = cms.vuint32(2,2,2,2,2,2,2,2) +#TMTrackProducer.HTFillingRphi.BusySectorMbinOrder = cms.vuint32(0,8, 1,9, 2,10, 3,11, 4,12, 5,13, 6,14, 7,15) + +#--- Reduce Pt threshold to 2 GeV, with coarse HT, and switch off Mini-HT. + +#TMTrackProducer.HTArraySpecRphi.MiniHTstage = cms.bool(False) +#TMTrackProducer.HTFillingRphi.MaxStubsInCell = cms.uint32(16) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPt = cms.uint32(24) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = cms.uint32(32) +#TMTrackProducer.GenCuts.GenMinPt = cms.double(2.0) +#TMTrackProducer.HTArraySpecRphi.HoughMinPt = cms.double(2.0) +#TMTrackProducer.HTFillingRphi.BusySectorMbinRanges = cms.vuint32(2,2,2,2,2,2,2,2,2,2,2,2) +#TMTrackProducer.HTFillingRphi.BusySectorMbinOrder = cms.vuint32(0,12, 1,13, 2,14, 3,15, 4,16, 5,17, 6,18, 7,19, 8,20, 9,21, 10,22, 11,23) + +#--- Reduce Pt threshold to 2 GeV, with coarse HT, followed by Mini-HT. + +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPt = cms.uint32(48) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = cms.uint32(64) +#TMTrackProducer.GenCuts.GenMinPt = cms.double(2.0) +#TMTrackProducer.HTArraySpecRphi.HoughMinPt = cms.double(2.0) +#TMTrackProducer.HTArraySpecRphi.MiniHoughMinPt = cms.double(3.0) # Mini-HT not used below this Pt, to reduce sensitivity to scattering. +#TMTrackProducer.HTFillingRphi.BusySectorMbinRanges = cms.vuint32(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 24) +#TMTrackProducer.HTFillingRphi.BusySectorMbinOrder = cms.vuint32(0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47) + +#--- Additional Mini-HT options to improve electron/displaced tracking. + +# Next 2 lines cause tracks found by 1st stage HT to be output if above specified Pt threshold & mini-HT found no tracks. +# Improves electron tracking. Setting Pt threshold to 0 improves displaced tracking. +#TMTrackProducer.HTArraySpecRphi.MiniHoughDontKill = cms.bool(True) +#TMTrackProducer.HTArraySpecRphi.MiniHoughDontKillMinPt = cms.double(8.) +# Extreme displaced tracking also benefits from following. +#TMTrackProducer.L1TrackDef.MinStubLayers = cms.uint32(4) # HT accepts tracks with >= 4 layers +#TMTrackProducer.TrackFitSettings.KalmanRemove2PScut = cms.bool(True) +#To study displaced tracking, include non-prompt particles in efficiency definition. +#TMTrackProducer.GenCuts.GenMaxVertR = cms.double(30.) + +#--- Unusual HT cell shapes + +# Simplify HT MUX to allow easy playing with the number of m bins. +#TMTrackProducer.HTFillingRphi.BusySectorMbinOrder = cms.vuint32() + +# Diamond shaped cells: (64,62), (34,32) or (46,44) sized array interesting. +#TMTrackProducer.HTArraySpecRphi.Shape = cms.uint32(1) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPt = cms.uint32(38) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = cms.uint32(32) + +# Hexagonal shaped cells: (64,42), (50,32) or (56,36) sized array interesting. +#TMTrackProducer.HTArraySpecRphi.Shape = cms.uint32(2) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPt = cms.uint32(56) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = cms.uint32(32) + +# Brick-wall arranged cells: (64,30) or (66,32) sized array interesting. +#TMTrackProducer.HTArraySpecRphi.Shape = cms.uint32(3) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPt = cms.uint32(64) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = cms.uint32(27) + +#--- Older cfg giving similar tracking performance with slightly larger resource use. + +#TMTrackProducer.PhiSectors.NumPhiSectors = cms.uint32(36) +#TMTrackProducer.EtaSectors.EtaRegions = cms.vdouble(-2.4, -2.0, -1.53, -0.98, -0.37, 0.37, 0.98, 1.53, 2.0, 2.4) +#TMTrackProducer.EtaSectors.ChosenRofZ = cms.double(45.) +#TMTrackProducer.EtaSectors.AllowOver2EtaSecs = cms.bool(False) +#TMTrackProducer.HTArraySpecRphi.HoughNbinsPhi = cms.uint32(32) +#TMTrackProducer.HTArraySpecRphi.NumSubSecsEta = cms.uint32(1) + +#--- Stub digitization (switch on/off and/or change defaults). + +#TMTrackProducer.StubDigitize.EnableDigitize = cms.bool(True) + +#--- Reduce requirement on number of layers a track must have stubs in, either globally or in specific eta regions. + +#TMTrackProducer.L1TrackDef.MinStubLayers = cms.uint32(4) # Reduce it globally +#TMTrackProducer.L1TrackDef.EtaSecsReduceLayers = cms.vuint32(5,12) # barrel-endcap transition region + +#--- If globally reducing number of layers cut, best to also use just one HT output opto-link per m-bin. +# For 3 GeV threshold with no mini-HT. +#TMTrackProducer.HTFillingRphi.BusySectorMbinRanges = cms.vuint32(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1) +# For 2 GeV threshold with mini-HT. +#TMTrackProducer.HTFillingRphi.BusySectorMbinRanges = cms.vuint32(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 24) + +#--- Change TP to track matching criteria. + +#TMTrackProducer.GenCuts.GenMinStubLayers = cms.uint32(4) +#TMTrackProducer.TrackMatchDef.MinNumMatchLayers = cms.uint32(4) + +#--- Switch off data truncation due to finite band-width. + +#TMTrackProducer.HTFillingRphi.BusySectorKill = cms.bool(False) +#TMTrackProducer.HTFillingRphi.BusyInputSectorKill = cms.bool(False) + +# Don't order stubs by bend in DTC, such that highest Pt stubs are transmitted first. +#TMTrackProducer.StubCuts.OrderStubsByBend = cms.bool(False) + +#--- Switch on FPGA-friendly approximation to B parameter in GP - will be used in future GP firmware. +#--- (used to relate track angle dphi to stub bend) +#TMTrackProducer.GeometricProc.UseApproxB = cms.bool(True) diff --git a/L1Trigger/TrackFindingTMTT/src/ChiSquaredFit4.cc b/L1Trigger/TrackFindingTMTT/src/ChiSquaredFit4.cc new file mode 100644 index 0000000000000..74091c0127070 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/ChiSquaredFit4.cc @@ -0,0 +1,174 @@ +#include "L1Trigger/TrackFindingTMTT/interface/ChiSquaredFit4.h" +#include "DataFormats/Math/interface/deltaPhi.h" + +using namespace std; + +namespace tmtt { + + ChiSquaredFit4::ChiSquaredFit4(const Settings* settings, const uint nPar) : ChiSquaredFitBase(settings, nPar) { + largestresid_ = -1.0; + ilargestresid_ = -1; + } + + TVectorD ChiSquaredFit4::seed(const L1track3D& l1track3D) { + TVectorD x(4); + x[INVR] = settings_->invPtToInvR() * l1track3D.qOverPt(); + x[PHI0] = l1track3D.phi0(); + x[T] = l1track3D.tanLambda(); + x[Z0] = l1track3D.z0(); + return x; + } + + //=== Calculate derivatives of track intercept with respect to track parameters + + TMatrixD ChiSquaredFit4::D(const TVectorD& x) { + TMatrixD D(2 * stubs_.size(), nPar_); // Empty matrix + D.Zero(); + int j = 0; + double rInv = x[INVR]; + double phi0 = x[PHI0]; + double t = x[T]; + for (unsigned i = 0; i < stubs_.size(); i++) { + double ri = stubs_[i]->r(); + if (stubs_[i]->barrel()) { + // Derivatives of r*phi + D(j, INVR) = -0.5 * ri * ri; + D(j, PHI0) = ri; + j++; + // Derivatives of z + D(j, T) = ri; + D(j, Z0) = 1; + j++; + } else { + double phii = stubs_[i]->phi(); + int iphi = stubs_[i]->iphi(); + + // N.B. These represent HALF the width and number of strips of sensor. + double width = 0.5 * stubs_[i]->trackerModule()->sensorWidth(); + double nstrip = 0.5 * stubs_[i]->nStrips(); + + double Deltai = width * (iphi - nstrip) / nstrip; // Non-radial endcap 2S strip correction + if (stubs_[i]->z() > 0.0) + Deltai = -Deltai; + double DeltaiOverRi = Deltai / ri; + double theta0 = DeltaiOverRi + (2. / 3.) * DeltaiOverRi * DeltaiOverRi * DeltaiOverRi; + + double phi_track = phi0 - 0.5 * rInv * ri; //Expected phi hit given the track + + double tInv = 1 / t; + // Derivatives of r + D(j, INVR) = -1 * ri * ri * ri * rInv; + D(j, PHI0) = 0; + D(j, T) = -ri * tInv; + D(j, Z0) = -1 * tInv; + j++; + // Derivatives of r*phi + D(j, INVR) = -0.5 * ri * ri; + D(j, PHI0) = ri; + D(j, T) = ri * 0.5 * rInv * ri * tInv - ((phi_track - phii) - theta0) * ri * tInv; + D(j, Z0) = ri * 0.5 * rInv * tInv - ((phi_track - phii) - theta0) * tInv; + j++; + } + } + return D; + } + + //=== In principle, this is the stub position covariance matrix. + //=== In practice, it misses a factor "sigma", because unconventionally, this is absorbed into residuals() function. + + TMatrixD ChiSquaredFit4::Vinv() { + TMatrixD Vinv(2 * stubs_.size(), 2 * stubs_.size()); + // Scattering term scaling as 1/Pt. + double sigmaScat = settings_->kalmanMultiScattTerm() * std::abs(qOverPt_seed_); + for (unsigned i = 0; i < stubs_.size(); i++) { + double sigmaPerp = stubs_[i]->sigmaPerp(); + double sigmaPar = stubs_[i]->sigmaPar(); + double ri = stubs_[i]->r(); + sigmaPerp = sqrt(sigmaPerp * sigmaPerp + sigmaScat * sigmaScat * ri * ri); + if (stubs_[i]->barrel()) { + Vinv(2 * i, 2 * i) = 1 / sigmaPerp; + Vinv(2 * i + 1, 2 * i + 1) = 1 / sigmaPar; + } else { + Vinv(2 * i, 2 * i) = 1 / sigmaPar; + Vinv(2 * i + 1, 2 * i + 1) = 1 / sigmaPerp; + } + } + return Vinv; + } + + //=== Calculate residuals w.r.t. track divided by uncertainty. + + TVectorD ChiSquaredFit4::residuals(const TVectorD& x) { + unsigned int n = stubs_.size(); + + TVectorD delta(2 * n); + + double rInv = x[INVR]; + double phi0 = x[PHI0]; + double t = x[T]; + double z0 = x[Z0]; + + double chiSq = 0.0; + + unsigned int j = 0; + + largestresid_ = -1.0; + ilargestresid_ = -1; + + // Scattering term scaling as 1/Pt. + double sigmaScat = settings_->kalmanMultiScattTerm() * std::abs(qOverPt_seed_); + + for (unsigned int i = 0; i < n; i++) { + double ri = stubs_[i]->r(); + double zi = stubs_[i]->z(); + double phii = stubs_[i]->phi(); + double sigmaPerp = stubs_[i]->sigmaPerp(); + double sigmaPar = stubs_[i]->sigmaPar(); + sigmaPerp = sqrt(sigmaPerp * sigmaPerp + sigmaScat * sigmaScat * ri * ri); + + if (stubs_[i]->barrel()) { + double halfRinvRi = 0.5 * ri * rInv; + double aSinHalfRinvRi = halfRinvRi + (2. / 3.) * halfRinvRi * halfRinvRi * halfRinvRi; + double deltaphi = reco::deltaPhi(phi0 - aSinHalfRinvRi - phii, 0.); + delta[j++] = (ri * deltaphi) / sigmaPerp; + delta[j++] = (z0 + (2.0 / rInv) * t * aSinHalfRinvRi - zi) / sigmaPar; + } else { + double tInv = 1 / t; + double r_track = (zi - z0) * tInv; + double phi_track = phi0 - 0.5 * rInv * (zi - z0) * tInv; + int iphi = stubs_[i]->iphi(); + + // N.B. These represent HALF the width and number of strips of sensor. + double width = 0.5 * stubs_[i]->trackerModule()->sensorWidth(); + double nstrip = 0.5 * stubs_[i]->nStrips(); + + double Deltai = width * (iphi - nstrip) / nstrip; // Non-radial endcap 2S strip correction + + if (stubs_[i]->z() > 0.0) + Deltai = -Deltai; + + double DeltaiOverRi = Deltai / ri; + double theta0 = DeltaiOverRi + (2. / 3.) * DeltaiOverRi * DeltaiOverRi * DeltaiOverRi; + double Delta = Deltai - r_track * (theta0 - (phi_track - phii)); + + delta[j++] = (r_track - ri) / sigmaPar; + delta[j++] = Delta / sigmaPerp; + } + + chiSq += delta[j - 2] * delta[j - 2] + delta[j - 1] * delta[j - 1]; + + if (std::abs(delta[j - 2]) > largestresid_) { + largestresid_ = std::abs(delta[j - 2]); + ilargestresid_ = i; + } + + if (std::abs(delta[j - 1]) > largestresid_) { + largestresid_ = std::abs(delta[j - 1]); + ilargestresid_ = i; + } + } + + return delta; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/ChiSquaredFitBase.cc b/L1Trigger/TrackFindingTMTT/src/ChiSquaredFitBase.cc new file mode 100644 index 0000000000000..168e19269af71 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/ChiSquaredFitBase.cc @@ -0,0 +1,136 @@ +///=== This is the base class for the linearised chi-squared track fit algorithms. + +///=== Written by: Sioni Summers and Alexander D. Morton + +#include "L1Trigger/TrackFindingTMTT/interface/ChiSquaredFitBase.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" + +#include +#include +#include + +namespace tmtt { + + ChiSquaredFitBase::ChiSquaredFitBase(const Settings* settings, const uint nPar) + : TrackFitGeneric(settings), chiSq_(0.0) { + // Bad stub killing settings + numFittingIterations_ = settings_->numTrackFitIterations(); + killTrackFitWorstHit_ = settings_->killTrackFitWorstHit(); + generalResidualCut_ = settings_->generalResidualCut(); // The cut used to remove bad stubs (if nStubs > minLayers) + killingResidualCut_ = settings_->killingResidualCut(); // The cut used to kill off tracks entirely + + //--- These two parameters are used to check if after the fit, there are still enough stubs on the track + minStubLayers_ = settings_->minStubLayers(); + nPar_ = nPar; + } + + void ChiSquaredFitBase::calculateChiSq(const TVectorD& resids) { + chiSq_ = 0.0; + uint j = 0; + for (uint i = 0; i < stubs_.size(); i++) { + chiSq_ += resids[j] * resids[j] + resids[j + 1] * resids[j + 1]; + j = j + 2; + } + } + + void ChiSquaredFitBase::calculateDeltaChiSq(const TVectorD& delX, const TVectorD& covX) { + for (int i = 0; i < covX.GetNrows(); i++) { + chiSq_ -= (delX[i]) * covX[i]; + } + } + + L1fittedTrack ChiSquaredFitBase::fit(const L1track3D& l1track3D) { + qOverPt_seed_ = l1track3D.qOverPt(); + stubs_ = l1track3D.stubs(); + + // Get cut on number of layers including variation due to dead sectors, pt dependence etc. + minStubLayersRed_ = Utility::numLayerCut(Utility::AlgoStep::FIT, + settings_, + l1track3D.iPhiSec(), + l1track3D.iEtaReg(), + std::abs(l1track3D.qOverPt()), + l1track3D.eta()); + + TVectorD x = seed(l1track3D); + + for (int i = 0; i < numFittingIterations_; i++) { + TMatrixD d = D(x); + TMatrixD dTrans(TMatrixD::kTransposed, d); + TMatrixD dtVinv = dTrans * Vinv(); + TMatrixD dtVinvTrans(TMatrixD::kTransposed, dtVinv); + //TMatrixD M = dtVinv * d; // Must insert extra factor Vinv, due to unconventional Vinv() definition. + TMatrixD M = dtVinv * dtVinvTrans; + TMatrixD Minv(TMatrixD::kInverted, M); + TVectorD resids = residuals(x); + TVectorD deltaX = Minv * dtVinv * resids; + x = x - deltaX; + TVectorD covX = dTrans * Vinv() * resids; + calculateChiSq(resids); + calculateDeltaChiSq(deltaX, covX); + + if (i < numFittingIterations_ - 1) { // Don't kill stub if will not refit. + + resids = residuals(x); // update resids & largestresid_ + + bool killWorstStub = false; + if (killTrackFitWorstHit_) { + if (largestresid_ > killingResidualCut_) { + killWorstStub = true; + } else if (largestresid_ > generalResidualCut_) { + std::vector stubsTmp = stubs_; + stubsTmp.erase(stubsTmp.begin() + ilargestresid_); + if (Utility::countLayers(settings_, stubsTmp) >= minStubLayersRed_) + killWorstStub = true; + } else { + // Get better apparent tracking performance by always killing worst stub until only 4 layers left. + if (Utility::countLayers(settings_, stubs_) > minStubLayersRed_) + killWorstStub = true; + } + } + + if (killWorstStub) { + stubs_.erase(stubs_.begin() + ilargestresid_); + + // Reject tracks with too many killed stubs & stop iterating. + unsigned int nLayers = Utility::countLayers(settings_, stubs_); // Count tracker layers with stubs + bool valid = nLayers >= minStubLayersRed_; + + if (not valid) { + L1fittedTrack rejectedTrk; + return rejectedTrk; + } + } else { + break; + } + } + } + + // Reject tracks with too many killed stubs + unsigned int nLayers = Utility::countLayers(settings_, stubs_); // Count tracker layers with stubs + bool valid = nLayers >= minStubLayersRed_; + + if (valid) { + const unsigned int hitPattern = 0; // FIX: Needs setting + const float chi2rz = 0; // FIX: Needs setting + return L1fittedTrack(settings_, + &l1track3D, + stubs_, + hitPattern, + x[INVR] / (settings_->invPtToInvR()), + 0, + x[PHI0], + x[Z0], + x[T], + chiSq_, + chi2rz, + nPar_); + } else { + L1fittedTrack rejectedTrk; + return rejectedTrk; + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/ConverterToTTTrack.cc b/L1Trigger/TrackFindingTMTT/src/ConverterToTTTrack.cc new file mode 100644 index 0000000000000..d142946cf0b99 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/ConverterToTTTrack.cc @@ -0,0 +1,75 @@ +#include "L1Trigger/TrackFindingTMTT/interface/ConverterToTTTrack.h" +#include "FWCore/Utilities/interface/Exception.h" + +using namespace std; + +namespace tmtt { + + //=== Convert L1fittedTrack or L1track3D (track candidates after/before fit) to TTTrack format. + + TTTrack ConverterToTTTrack::makeTTTrack(const L1trackBase* trk, + unsigned int iPhiSec, + unsigned int iEtaReg) const { + unsigned int nPar, hitPattern; + double d0, z0, tanL, chi2rphi, chi2rz; + + const L1fittedTrack* fitTrk = dynamic_cast(trk); + + // Handle variables that differ for L1fittedTrack & L1track3D + if (fitTrk == nullptr) { + // This is an L1track3D type (track before fit) + nPar = 4; // Before fit, TMTT algorithm assumes 4 helix params + // Set to zero variables that are unavailable for this track type. + hitPattern = 0; + d0 = 0.; + z0 = 0; + tanL = 0; + chi2rphi = 0.; + chi2rz = 0; + } else { + // This is an L1fittedTrack type (track after fit) + if (not fitTrk->accepted()) + throw cms::Exception("LogicError") << "ConverterToTTTrack ERROR: requested to convert invalid L1fittedTrack"; + nPar = fitTrk->nHelixParam(); // Number of helix parameters in track fit + hitPattern = fitTrk->hitPattern(); + d0 = fitTrk->d0(); + z0 = fitTrk->z0(); + tanL = fitTrk->tanLambda(); + chi2rphi = fitTrk->chi2rphi(); + chi2rz = fitTrk->chi2rz(); + } + + const double& rinv = invPtToInvR_ * trk->qOverPt(); + const double& phi0 = trk->phi0(); + constexpr double mva = -1.; // MVA quality flags not yet set. + const double& magneticField = settings_->magneticField(); + + TTTrack track( + rinv, phi0, tanL, z0, d0, chi2rphi, chi2rz, mva, mva, mva, hitPattern, nPar, magneticField); + + // Set references to stubs on this track. + std::vector ttstubrefs = this->stubRefs(trk); + track.setStubRefs(ttstubrefs); + + // Note which (eta,phi) sector this track was reconstructed in. + track.setPhiSector(iPhiSec); + track.setEtaSector(iEtaReg); + + track.setStubPtConsistency(-1); // not yet filled. + + return track; + } + + //=== Get references to stubs on track. (Works for either L1track3D or L1fittedTrack). + + std::vector ConverterToTTTrack::stubRefs(const L1trackBase* trk) const { + std::vector ttstubrefs; + const std::vector& stubs = trk->stubs(); + for (Stub* s : stubs) { + const TTStubRef& ref = s->ttStubRef(); + ttstubrefs.push_back(ref); + } + return ttstubrefs; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/DegradeBend.cc b/L1Trigger/TrackFindingTMTT/src/DegradeBend.cc new file mode 100644 index 0000000000000..2f0b0aea42fa0 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/DegradeBend.cc @@ -0,0 +1,67 @@ +#include "L1Trigger/TrackFindingTMTT/interface/DegradeBend.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" + +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "DataFormats/SiStripDetId/interface/StripSubdetector.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace tmtt { + + //--- Given the original bend, flag indicating if this is a PS or 2S module, & detector identifier, + //--- this returns the degraded stub bend, a boolean indicatng if stub bend was outside the assumed window + //--- size programmed below, and an integer indicating how many values of the original bend + //--- were grouped together into this single value of the degraded bend. + + void DegradeBend::degrade(float bend, + bool psModule, + const DetId& stDetId, + float windowFEnew, + float& degradedBend, + unsigned int& numInGroup) const { + // Get degraded bend value. + unsigned int windowHalfStrips; + this->work(bend, psModule, stDetId, windowFEnew, degradedBend, numInGroup, windowHalfStrips); + } + + //--- Does the actual work of degrading the bend. + + void DegradeBend::work(float bend, + bool psModule, + const DetId& stDetId, + float windowFEnew, + float& degradedBend, + unsigned int& numInGroup, + unsigned int& windowHalfStrips) const { + // Calculate stub window size in half-strip units used to produce stubs. + // Code accessing geometry inspired by L1Trigger/TrackTrigger/src/TTStubAlgorithm_official.cc + + const double* storedHalfWindow = sw_->storedWindowSize(theTrackerTopo_, stDetId); + + // Compare this with the possibly tighter window provided by the user, converting to half-strip units. + const double window = std::min(*storedHalfWindow, double(windowFEnew)); + windowHalfStrips = (unsigned int)(2 * window); + + // Bend is measured with granularity of 0.5 strips. + // Convert it to integer measured in half-strip units for this calculation! + int b = std::round(2 * bend); + + if ((unsigned int)(std::abs(b)) <= windowHalfStrips) { + // Call the official CMS bend encoding algorithm. + degradedBend = stubAlgo_->degradeBend(psModule, windowHalfStrips, b); + } else { + // This should only happen for stubs subsequently rejected by the FE. + numInGroup = 0; + constexpr float rejectedStubBend = 99999.; + degradedBend = rejectedStubBend; + } + } +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/DigitalStub.cc b/L1Trigger/TrackFindingTMTT/src/DigitalStub.cc new file mode 100644 index 0000000000000..f7db8523855a6 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/DigitalStub.cc @@ -0,0 +1,250 @@ +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/DigitalStub.h" + +#include "DataFormats/Math/interface/deltaPhi.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include + +using namespace std; + +namespace tmtt { + + //=== Hybrid tracking: simplified digitization for KF. + + DigitalStub::DigitalStub(const Settings* settings, double r, double phi, double z, unsigned int iPhiSec) + : phiSBits_(settings->phiSBits()), // No. of bits to store phiS coord. + phiSRange_(settings->phiSRange()), // Range of phiS coord. in radians. + rtBits_(settings->rtBits()), // No. of bits to store rT coord. + rtRange_(settings->rtRange()), // Range of rT coord. in cm. + zBits_(settings->zBits()), // No. of bits to store z coord. + zRange_(settings->zRange()), // Range of z coord in cm. + phiSMult_(pow(2, phiSBits_) / phiSRange_), + rtMult_(pow(2, rtBits_) / rtRange_), + zMult_(pow(2, zBits_) / zRange_), + numPhiSectors_(settings->numPhiSectors()), + numPhiNonants_(9), + phiSectorWidth_(2. * M_PI / double(numPhiSectors_)), + chosenRofPhi_(settings->chosenRofPhi()) { + // Centre of this sector in phi. (Nonant 0 is centred on x-axis). + phiCentreSec0_ = -M_PI / double(numPhiNonants_) + M_PI / double(numPhiSectors_); + phiSectorCentre_ = phiSectorWidth_ * double(iPhiSec) + phiCentreSec0_; + + // Used to check if new digi requests are for same sector as old + iPhiSec_done_ = iPhiSec; + + r_orig_ = r; + phi_orig_ = phi; + z_orig_ = z; + + rt_orig_ = r_orig_ - chosenRofPhi_; + phiS_orig_ = reco::deltaPhi(phi_orig_, phiSectorCentre_); + + // Digitize + iDigi_Rt_ = floor(rt_orig_ * rtMult_); + iDigi_PhiS_ = floor(phiS_orig_ * phiSMult_); + iDigi_Z_ = floor(z_orig_ * zMult_); + } + + //=== TMTT tracking algorithm: digitisaton for entire L1 tracking chain. + // Initialize stub with floating point stub coords, range of HT m-bin values consistent with bend, + // bend and phi sector. + + DigitalStub::DigitalStub(const Settings* settings, + double phi_orig, + double r_orig, + double z_orig, + unsigned int mbin_min_orig, + unsigned int mbin_max_orig, + double bend_orig, + unsigned int iPhiSec) { + // Set cfg params + this->setCfgParams(settings); + + // Store variable prior to digitisation + r_orig_ = r_orig; + rt_orig_ = r_orig_ - chosenRofPhi_; + phi_orig_ = phi_orig; + z_orig_ = z_orig; + mbin_min_orig_ = mbin_min_orig; + mbin_max_orig_ = mbin_max_orig; + bend_orig_ = bend_orig; + + // Phi of centre of phi sector and of nonant. + unsigned int iNonant = this->iNonant(iPhiSec); + phiSectorCentre_ = phiSectorWidth_ * double(iPhiSec) + phiCentreSec0_; + phiNonantCentre_ = phiNonantWidth_ * double(iNonant); + + phiS_orig_ = reco::deltaPhi(phi_orig_, phiSectorCentre_); + phiN_orig_ = reco::deltaPhi(phi_orig_, phiNonantCentre_); + + // Used to check if new digi requests are for same sector as old + iPhiSec_done_ = iPhiSec; + + // Check that stub coords. are within assumed digitization range. + this->checkInRange(); + + // Digitize and then undigitize stub. + this->digitize(iPhiSec); + this->undigitize(iPhiSec); + + // Check that digitization followed by undigitization doesn't change results too much. + this->checkAccuracy(); + } + + //=== Redo phi digitisation assigning stub to a different phi sector; + + bool DigitalStub::changePhiSec(unsigned int iPhiSec) { + bool doUpdate = (iPhiSec != iPhiSec_done_); + + if (doUpdate) { + // phi sector has changed since last time digitisation was done, so update. + iPhiSec_done_ = iPhiSec; + unsigned int iNonant = this->iNonant(iPhiSec); + // Update original, floating point phi w.r.t. phi sector/nonant centre. + phiSectorCentre_ = phiSectorWidth_ * double(iPhiSec) + phiCentreSec0_; + phiNonantCentre_ = phiNonantWidth_ * double(iNonant); + phiS_orig_ = reco::deltaPhi(phi_orig_, phiSectorCentre_); + phiN_orig_ = reco::deltaPhi(phi_orig_, phiNonantCentre_); + // Update digitised phi. + iDigi_PhiN_ = floor(phiN_orig_ * phiNMult_); + iDigi_PhiS_ = floor(phiS_orig_ * phiSMult_); + // Update digitized then undigitized phi. + phiN_ = (iDigi_PhiN_ + 0.5) / phiNMult_; + phi_GP_ = reco::deltaPhi(phiN_, -phiNonantCentre_); + phiS_ = (iDigi_PhiS_ + 0.5) / phiSMult_; + phi_HT_TF_ = reco::deltaPhi(phiS_, -phiSectorCentre_); + } + return doUpdate; + } + + //=== Set configuration parameters. + + void DigitalStub::setCfgParams(const Settings* settings) { + // Digitization configuration parameters + phiSectorBits_ = settings->phiSectorBits(); // No. of bits to store phi sector number + //--- Parameters available in HT board. + phiSBits_ = settings->phiSBits(); // No. of bits to store phiS coord. + phiSRange_ = settings->phiSRange(); // Range of phiS coord. in radians. + rtBits_ = settings->rtBits(); // No. of bits to store rT coord. + rtRange_ = settings->rtRange(); // Range of rT coord. in cm. + zBits_ = settings->zBits(); // No. of bits to store z coord. + zRange_ = settings->zRange(); // Range of z coord in cm. + //--- Parameters available in GP board (excluding any in common with HT specified above). + phiNBits_ = settings->phiNBits(); // No. of bits to store phiN parameter. + phiNRange_ = settings->phiNRange(); // Range of phiN parameter + bendBits_ = settings->bendBits(); // No. of bits to store stub bend. + + // Number of phi sectors and phi nonants. + numPhiSectors_ = settings->numPhiSectors(); + numPhiNonants_ = settings->numPhiNonants(); + // Phi sector and phi nonant width (radians) + phiSectorWidth_ = 2. * M_PI / double(numPhiSectors_); + phiNonantWidth_ = 2. * M_PI / double(numPhiNonants_); + // Centre of phi sector 0. + phiCentreSec0_ = -M_PI / double(numPhiNonants_) + M_PI / double(numPhiSectors_); + // Radius from beamline with respect to which stub r coord. is measured. + chosenRofPhi_ = settings->chosenRofPhi(); + + // Number of q/Pt bins in Hough transform array. + nbinsPt_ = (int)settings->houghNbinsPt(); + // Min. of m-bin range in firmware, + min_array_mbin_ = (nbinsPt_ % 2 == 0) ? -(nbinsPt_ / 2) : -(nbinsPt_ - 1) / 2; + + // Calculate multipliers to digitize the floating point numbers. + phiSMult_ = pow(2, phiSBits_) / phiSRange_; + rtMult_ = pow(2, rtBits_) / rtRange_; + zMult_ = pow(2, zBits_) / zRange_; + phiNMult_ = pow(2, phiNBits_) / phiNRange_; + + // No precision lost by digitization, since original bend (after encoding) has steps of 0.25 (in units of pitch). + bendMult_ = 4.; + bendRange_ = round(pow(2, bendBits_) / bendMult_); // discrete values, so digitisation different + } + + //=== Digitize stub + + void DigitalStub::digitize(unsigned int iPhiSec) { + //--- Digitize variables used exclusively in GP input. + iDigi_PhiN_ = floor(phiN_orig_ * phiNMult_); + iDigi_Bend_ = round(bend_orig_ * bendMult_); // discrete values, so digitisation different + + //--- Digitize variables used exclusively in HT input. + iDigi_PhiS_ = floor(phiS_orig_ * phiSMult_); + + // Offset m-bin range allowed by bend to correspond to firmware. + mbin_min_ = mbin_min_orig_ + min_array_mbin_; + mbin_max_ = mbin_max_orig_ + min_array_mbin_; + + //--- Digitize variables used in both GP & HT input. + iDigi_Rt_ = floor(rt_orig_ * rtMult_); + + //-- Digitize variables used by SF & TF input + iDigi_R_ = iDigi_Rt_ + std::round(chosenRofPhi_ * rtMult_); + + //-- Digitize variables used by everything + iDigi_Z_ = floor(z_orig_ * zMult_); + } + + //=== Undigitize stub again. + + void DigitalStub::undigitize(unsigned int iPhiSec) { + //--- Undigitize variables used exclusively in GP. + phiN_ = (iDigi_PhiN_ + 0.5) / phiNMult_; + phi_GP_ = reco::deltaPhi(phiN_, -phiNonantCentre_); + bend_ = iDigi_Bend_ / bendMult_; // discrete values, so digitisation different + + //--- Undigitize variables used exclusively by HT & SF/TF + phiS_ = (iDigi_PhiS_ + 0.5) / phiSMult_; + phi_HT_TF_ = reco::deltaPhi(phiS_, -phiSectorCentre_); + + //--- Undigitize variables used in both GP & HT. + rt_GP_HT_ = (iDigi_Rt_ + 0.5) / rtMult_; + r_GP_HT_ = rt_GP_HT_ + chosenRofPhi_; + + //--- Undigitize variables used exclusively by SF/TF. + r_SF_TF_ = (iDigi_R_ + 0.5) / rtMult_; + rt_SF_TF_ = r_SF_TF_ - chosenRofPhi_; + + //--- Undigitize variables used exclusively by everything. + z_ = (iDigi_Z_ + 0.5) / zMult_; + } + + //=== Check that stub coords. are within assumed digitization range. + + void DigitalStub::checkInRange() const { + if (std::abs(rt_orig_) >= 0.5 * rtRange_) + throw cms::Exception("BadConfig") << "DigitalStub: Stub rT is out of assumed digitization range." + << " |rt| = " << std::abs(rt_orig_) << " > " << 0.5 * rtRange_; + if (std::abs(z_orig_) >= 0.5 * zRange_) + throw cms::Exception("BadConfig") << "DigitalStub: Stub z is out of assumed digitization range." + << " |z| = " << std::abs(z_orig_) << " > " << 0.5 * zRange_; + if (std::abs(bend_orig_) >= 0.5 * bendRange_) + throw cms::Exception("BadConfig") << "DigitalStub: Stub bend is out of assumed digitization range." + << " |bend| = " << std::abs(bend_orig_) << " > " << 0.5 * bendRange_; + //--- Can't check phi range, as DigitalStub called for stubs before sector assignment. + //if (std::abs(phiS_orig_) >= 0.5 * phiSRange_) + // throw cms::Exception("BadConfig") << "DigitalStub: Stub phiS is out of assumed digitization range." + // << " |phiS| = " << std::abs(phiS_orig_) << " > " << 0.5 * phiSRange_; + //if (std::abs(phiN_orig_) >= 0.5 * phiNRange_) + // throw cms::Exception("BadConfig") << "DigitalStub: Stub phiN is out of assumed digitization range." + // << " |phiN| = " << std::abs(phiN_orig_) << " > " << 0.5 * phiNRange_; + } + + //=== Check that digitisation followed by undigitisation doesn't change significantly the stub coordinates. + + void DigitalStub::checkAccuracy() const { + double TA = reco::deltaPhi(phi_HT_TF_, phi_orig_); + double TB = r_GP_HT_ - r_orig_; + double TC = z_ - z_orig_; + double TD = bend_ - bend_orig_; + + // Compare to small numbers, representing acceptable precision loss. + constexpr double smallTA = 0.001, smallTB = 0.3, smallTC = 0.25, smallTD = 0.01; + if (std::abs(TA) > smallTA || std::abs(TB) > smallTB || std::abs(TC) > smallTC || std::abs(TD) > smallTD) { + throw cms::Exception("LogicError") << "WARNING: DigitalStub lost precision: " << TA << " " << TB << " " << TC + << " " << TD; + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/DigitalTrack.cc b/L1Trigger/TrackFindingTMTT/src/DigitalTrack.cc new file mode 100644 index 0000000000000..9586e01c3c55d --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/DigitalTrack.cc @@ -0,0 +1,312 @@ +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/DigitalTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" + +#include "DataFormats/Math/interface/deltaPhi.h" +#include "FWCore/Utilities/interface/Exception.h" + +using namespace std; + +namespace tmtt { + + //=== Note configuration parameters. + + DigitalTrack::DigitalTrack(const Settings* settings, const string& fitterName, const L1fittedTrack* fitTrk) + : + + // Digitization configuration parameters + settings_(settings), + + // Number of phi sectors and phi nonants. + numPhiSectors_(settings->numPhiSectors()), + numPhiNonants_(settings->numPhiNonants()), + // Phi sector and phi nonant width (radians) + phiSectorWidth_(2. * M_PI / float(numPhiSectors_)), + phiNonantWidth_(2. * M_PI / float(numPhiNonants_)), + // Radius from beamline with respect to which stub r coord. is measured. + chosenRofPhi_(settings->chosenRofPhi()), + + // Number of q/Pt bins in Hough transform array. + nbinsPt_((int)settings->houghNbinsPt()), + invPtToDPhi_(settings->invPtToDphi()), + + // Info about fitted track + fitterName_(fitterName), + nHelixParams_(fitTrk->nHelixParam()), + + nlayers_(fitTrk->numLayers()), + iPhiSec_(fitTrk->iPhiSec()), + iEtaReg_(fitTrk->iEtaReg()), + mBin_(int(fitTrk->cellLocationHT().first) - floor(settings_->houghNbinsPt() / 2)), + cBin_(int(fitTrk->cellLocationHT().second) - floor(settings_->houghNbinsPhi() / 2)), + mBinhelix_(int(fitTrk->cellLocationFit().first) - floor(settings_->houghNbinsPt() / 2)), + cBinhelix_(int(fitTrk->cellLocationFit().second) - floor(settings_->houghNbinsPhi() / 2)), + hitPattern_(fitTrk->hitPattern()), + consistent_(fitTrk->consistentHTcell()), + consistentSect_(fitTrk->consistentSector()), + accepted_(fitTrk->accepted()), + + qOverPt_orig_(fitTrk->qOverPt()), + oneOver2r_orig_(fitTrk->qOverPt() * invPtToDPhi_), + d0_orig_(fitTrk->d0()), + phi0_orig_(fitTrk->phi0()), + tanLambda_orig_(fitTrk->tanLambda()), + z0_orig_(fitTrk->z0()), + chisquaredRphi_orig_(fitTrk->chi2rphi()), + chisquaredRz_orig_(fitTrk->chi2rz()), + + // Same again with beam-spot constraint. + qOverPt_bcon_orig_(fitTrk->qOverPt_bcon()), + oneOver2r_bcon_orig_(fitTrk->qOverPt_bcon() * invPtToDPhi_), + phi0_bcon_orig_(fitTrk->phi0_bcon()), + chisquaredRphi_bcon_orig_(fitTrk->chi2rphi_bcon()) { + // Get digitisation parameters for this particular track fitter. + this->loadDigiCfg(fitterName); + + // Complete storage of info about fitted track & truth particle. + double phiCentreSec0 = -M_PI / float(numPhiNonants_) + M_PI / float(numPhiSectors_); + phiSectorCentre_ = phiSectorWidth_ * float(iPhiSec_) + phiCentreSec0; + phi0rel_orig_ = reco::deltaPhi(fitTrk->phi0(), phiSectorCentre_); + phi0rel_bcon_orig_ = reco::deltaPhi(fitTrk->phi0_bcon(), phiSectorCentre_); + + // FIX: Remove this BODGE once BCHI increased to 11 in KFstate.h + if (chisquaredRphi_orig_ >= chisquaredRange_) + chisquaredRphi_orig_ = chisquaredRange_ - 0.1; + if (chisquaredRphi_bcon_orig_ >= chisquaredRange_) + chisquaredRphi_bcon_orig_ = chisquaredRange_ - 0.1; + + // Associated truth, if any. + const TP* tp = fitTrk->matchedTP(); + bool tpOK = (tp != nullptr); + tp_tanLambda_ = tpOK ? tp->tanLambda() : 0; + tp_qoverpt_ = tpOK ? tp->qOverPt() : 0; + tp_pt_ = tpOK ? tp->pt() : 0; + tp_d0_ = tpOK ? tp->d0() : 0; + tp_eta_ = tpOK ? tp->eta() : 0; + tp_phi0_ = tpOK ? tp->phi0() : 0; + tp_z0_ = tpOK ? tp->z0() : 0; + tp_index_ = tpOK ? tp->index() : -1; + tp_useForAlgEff_ = tpOK ? tp->useForAlgEff() : false; + tp_useForEff_ = tpOK ? tp->useForEff() : false; + tp_pdgId_ = tpOK ? tp->pdgId() : 0; + + // Digitize track. + this->makeDigitalTrack(); + } + + //=== Load digitisation configuration parameters for the specific track fitter being used here. + + void DigitalTrack::loadDigiCfg(const string& fitterName) { + if (fitterName == "SimpleLR4") { + // SimpleLR4 track fitter + skipTrackDigi_ = settings_->slr_skipTrackDigi(); + oneOver2rBits_ = settings_->slr_oneOver2rBits(); + oneOver2rRange_ = settings_->slr_oneOver2rRange(); + d0Bits_ = settings_->slr_d0Bits(); + d0Range_ = settings_->slr_d0Range(); + phi0Bits_ = settings_->slr_phi0Bits(); + phi0Range_ = settings_->slr_phi0Range(); + z0Bits_ = settings_->slr_z0Bits(); + z0Range_ = settings_->slr_z0Range(); + tanLambdaBits_ = settings_->slr_tanlambdaBits(); + tanLambdaRange_ = settings_->slr_tanlambdaRange(); + chisquaredBits_ = settings_->slr_chisquaredBits(); + chisquaredRange_ = settings_->slr_chisquaredRange(); + } else { + // KF track fitter + // Also used for all other fitters, though unlikely to be correct them them ... + if (fitterName == "KF4ParamsComb" || fitterName == "KF5ParamsComb" || fitterName == "KF4ParamsCombHLS") { + skipTrackDigi_ = settings_->kf_skipTrackDigi(); + } else { + skipTrackDigi_ = settings_->other_skipTrackDigi(); // Allows to skip digitisation for other fitters + } + oneOver2rBits_ = settings_->kf_oneOver2rBits(); + oneOver2rRange_ = settings_->kf_oneOver2rRange(); + d0Bits_ = settings_->kf_d0Bits(); + d0Range_ = settings_->kf_d0Range(); + phi0Bits_ = settings_->kf_phi0Bits(); + phi0Range_ = settings_->kf_phi0Range(); + z0Bits_ = settings_->kf_z0Bits(); + z0Range_ = settings_->kf_z0Range(); + tanLambdaBits_ = settings_->kf_tanlambdaBits(); + tanLambdaRange_ = settings_->kf_tanlambdaRange(); + chisquaredBits_ = settings_->kf_chisquaredBits(); + chisquaredRange_ = settings_->kf_chisquaredRange(); + } + + // Calculate multipliers to digitize the floating point numbers. + oneOver2rMult_ = pow(2., oneOver2rBits_) / oneOver2rRange_; + d0Mult_ = pow(2., d0Bits_) / d0Range_; + phi0Mult_ = pow(2., phi0Bits_) / phi0Range_; + z0Mult_ = pow(2., z0Bits_) / z0Range_; + tanLambdaMult_ = pow(2., tanLambdaBits_) / tanLambdaRange_; + chisquaredMult_ = pow(2., chisquaredBits_) / chisquaredRange_; + } + + //=== Digitize track + + void DigitalTrack::makeDigitalTrack() { + if (skipTrackDigi_) { + // Optionally skip track digitisaton if done internally inside track fitting code, so + // retain original helix params. + iDigi_oneOver2r_ = 0; + iDigi_d0_ = 0; + iDigi_phi0rel_ = 0; + iDigi_tanLambda_ = 0; + iDigi_z0_ = 0; + iDigi_chisquaredRphi_ = 0; + iDigi_chisquaredRz_ = 0; + + iDigi_oneOver2r_bcon_ = 0; + iDigi_phi0rel_bcon_ = 0; + iDigi_chisquaredRphi_bcon_ = 0; + + oneOver2r_ = oneOver2r_orig_; + qOverPt_ = qOverPt_orig_; + d0_ = d0_orig_; + phi0rel_ = phi0rel_orig_; + phi0_ = phi0_orig_; + tanLambda_ = tanLambda_orig_; + z0_ = z0_orig_; + chisquaredRphi_ = chisquaredRphi_orig_; + chisquaredRz_ = chisquaredRz_orig_; + + // Same again with beam-spot constraint. + oneOver2r_bcon_ = oneOver2r_bcon_orig_; + qOverPt_bcon_ = qOverPt_bcon_orig_; + phi0rel_bcon_ = phi0rel_bcon_orig_; + phi0_bcon_ = phi0_bcon_orig_; + chisquaredRphi_bcon_ = chisquaredRphi_bcon_orig_; + + } else { + //--- Digitize variables + + iDigi_oneOver2r_ = floor(oneOver2r_orig_ * oneOver2rMult_); + iDigi_d0_ = floor(d0_orig_ * d0Mult_); + iDigi_phi0rel_ = floor(phi0rel_orig_ * phi0Mult_); + iDigi_tanLambda_ = floor(tanLambda_orig_ * tanLambdaMult_); + iDigi_z0_ = floor(z0_orig_ * z0Mult_); + iDigi_chisquaredRphi_ = floor(chisquaredRphi_orig_ * chisquaredMult_); + iDigi_chisquaredRz_ = floor(chisquaredRz_orig_ * chisquaredMult_); + + // If fitted declared track invalid, it will have set its chi2 to very large number. + // So truncate it at maximum allowed by digitisation range. + if (!accepted_) { + iDigi_chisquaredRphi_ = pow(2., chisquaredBits_) - 1; + iDigi_chisquaredRz_ = pow(2., chisquaredBits_) - 1; + } + + // Same again with beam-spot constraint. + iDigi_oneOver2r_bcon_ = floor(oneOver2r_bcon_orig_ * oneOver2rMult_); + iDigi_phi0rel_bcon_ = floor(phi0rel_bcon_orig_ * phi0Mult_); + iDigi_chisquaredRphi_bcon_ = floor(chisquaredRphi_bcon_orig_ * chisquaredMult_); + if (!accepted_) + iDigi_chisquaredRphi_bcon_ = pow(2., chisquaredBits_) - 1; + + //--- Determine floating point track params from digitized numbers (so with degraded resolution). + + oneOver2r_ = (iDigi_oneOver2r_ + 0.5) / oneOver2rMult_; + qOverPt_ = oneOver2r_ / invPtToDPhi_; + if (nHelixParams_ == 5) { + d0_ = (iDigi_d0_ + 0.5) / d0Mult_; + } else { + d0_ = 0.; + } + phi0rel_ = (iDigi_phi0rel_ + 0.5) / phi0Mult_; + phi0_ = reco::deltaPhi(phi0rel_, -phiSectorCentre_); + tanLambda_ = (iDigi_tanLambda_ + 0.5) / tanLambdaMult_; + z0_ = (iDigi_z0_ + 0.5) / z0Mult_; + chisquaredRphi_ = (iDigi_chisquaredRphi_ + 0.5) / chisquaredMult_; + chisquaredRz_ = (iDigi_chisquaredRz_ + 0.5) / chisquaredMult_; + + // Same again with beam-spot constraint. + if (nHelixParams_ == 5) { + oneOver2r_bcon_ = (iDigi_oneOver2r_bcon_ + 0.5) / oneOver2rMult_; + qOverPt_bcon_ = oneOver2r_bcon_ / invPtToDPhi_; + phi0rel_bcon_ = (iDigi_phi0rel_bcon_ + 0.5) / phi0Mult_; + phi0_bcon_ = reco::deltaPhi(phi0rel_bcon_, -phiSectorCentre_); + chisquaredRphi_bcon_ = (iDigi_chisquaredRphi_bcon_ + 0.5) / chisquaredMult_; + } else { + oneOver2r_bcon_ = oneOver2r_; + qOverPt_bcon_ = qOverPt_; + phi0rel_bcon_ = phi0rel_; + phi0_bcon_ = phi0_; + chisquaredRphi_bcon_ = chisquaredRphi_; + } + + // Check that track coords. are within assumed digitization range. + this->checkInRange(); + + // Check that digitization followed by undigitization doesn't change results too much. + this->checkAccuracy(); + } + } + + //=== Check that stub coords. are within assumed digitization range. + + void DigitalTrack::checkInRange() const { + if (accepted_) { // Don't bother apply to tracks rejected by the fitter. + if (std::abs(oneOver2r_orig_) >= 0.5 * oneOver2rRange_) + throw cms::Exception("BadConfig") + << "DigitalTrack: Track oneOver2r is out of assumed digitization range." + << " |oneOver2r| = " << std::abs(oneOver2r_orig_) << " > " << 0.5 * oneOver2rRange_ + << "; Fitter=" << fitterName_ << "; track accepted = " << accepted_; + if (consistentSect_) { // don't bother if track will fail sector consistency cut. + if (std::abs(phi0rel_orig_) >= 0.5 * phi0Range_) + throw cms::Exception("BadConfig") << "DigitalTrack: Track phi0rel is out of assumed digitization range." + << " |phi0rel| = " << std::abs(phi0rel_orig_) << " > " << 0.5 * phi0Range_ + << "; Fitter=" << fitterName_ << "; track accepted = " << accepted_; + } + if (std::abs(z0_orig_) >= 0.5 * z0Range_) + throw cms::Exception("BadConfig") << "DigitalTrack: Track z0 is out of assumed digitization range." + << " |z0| = " << std::abs(z0_orig_) << " > " << 0.5 * z0Range_ + << "; Fitter=" << fitterName_ << "; track accepted = " << accepted_; + if (std::abs(d0_orig_) >= 0.5 * d0Range_) + throw cms::Exception("BadConfig") << "DigitalTrack: Track d0 is out of assumed digitization range." + << " |d0| = " << std::abs(d0_orig_) << " > " << 0.5 * d0Range_ + << "; Fitter=" << fitterName_ << "; track accepted = " << accepted_; + if (std::abs(tanLambda_orig_) >= 0.5 * tanLambdaRange_) + throw cms::Exception("BadConfig") + << "DigitalTrack: Track tanLambda is out of assumed digitization range." + << " |tanLambda| = " << std::abs(tanLambda_orig_) << " > " << 0.5 * tanLambdaRange_ + << "; Fitter=" << fitterName_ << "; track accepted = " << accepted_; + if (accepted_) { // Tracks declared invalid by fitter can have very large original chi2. + if (chisquaredRphi_orig_ >= chisquaredRange_ or chisquaredRphi_orig_ < 0.) + throw cms::Exception("BadConfig") + << "DigitalTrack: Track chisquaredRphi is out of assumed digitization range." + << " chisquaredRphi = " << chisquaredRphi_orig_ << " > " << chisquaredRange_ << " or < 0" + << "; Fitter=" << fitterName_ << "; track accepted = " << accepted_; + if (chisquaredRz_orig_ >= chisquaredRange_ or chisquaredRz_orig_ < 0.) + throw cms::Exception("BadConfig") + << "DigitalTrack: Track chisquaredRz is out of assumed digitization range." + << " chisquaredRz = " << chisquaredRz_orig_ << " > " << chisquaredRange_ << " or < 0" + << "; Fitter=" << fitterName_ << "; track accepted = " << accepted_; + } + } + } + + //=== Check that digitisation followed by undigitisation doesn't change significantly the stub coordinates. + + void DigitalTrack::checkAccuracy() const { + if (accepted_) { // Don't bother apply to tracks rejected by the fitter. + float TA = qOverPt_ - qOverPt_orig_; + float TB = reco::deltaPhi(phi0_, phi0_orig_); + float TC = z0_ - z0_orig_; + float TD = tanLambda_ - tanLambda_orig_; + float TE = d0_ - d0_orig_; + float TF = chisquaredRphi_ - chisquaredRphi_orig_; + float TG = chisquaredRz_ - chisquaredRz_orig_; + + // Compare to small numbers, representing acceptable precision loss. + constexpr float smallTA = 0.01, smallTB = 0.001, smallTC = 0.05, smallTD = 0.002, smallTE = 0.05, smallTF = 0.5, + smallTG = 0.5; + if (std::abs(TA) > smallTA || std::abs(TB) > smallTB || std::abs(TC) > smallTC || std::abs(TD) > smallTD || + std::abs(TE) > smallTE || std::abs(TF) > smallTF || std::abs(TG) > smallTG) { + throw cms::Exception("LogicError") + << "WARNING: DigitalTrack lost precision: " << fitterName_ << " accepted=" << accepted_ << " " << TA << " " + << TB << " " << TC << " " << TD << " " << TE << " " << TF << " " << TG; + } + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/DupFitTrkKiller.cc b/L1Trigger/TrackFindingTMTT/src/DupFitTrkKiller.cc new file mode 100644 index 0000000000000..d52f22c321ace --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/DupFitTrkKiller.cc @@ -0,0 +1,275 @@ +#include "L1Trigger/TrackFindingTMTT/interface/DupFitTrkKiller.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" +#include "FWCore/Utilities/interface/Exception.h" +#include + +using namespace std; + +namespace tmtt { + + //=== Make available cfg parameters & specify which algorithm is to be used for duplicate track removal. + + DupFitTrkKiller::DupFitTrkKiller(const Settings* settings) + : settings_(settings), dupTrkAlg_(static_cast(settings->dupTrkAlgFit())) {} + + //=== Eliminate duplicate tracks from the input collection, and so return a reduced list of tracks. + + list DupFitTrkKiller::filter(const list& vecTracks) const { + if (dupTrkAlg_ == DupAlgoName::None) { + // We are not running duplicate removal, so return original fitted track collection. + list copyTracks; + for (const L1fittedTrack& trk : vecTracks) { + copyTracks.push_back(&trk); + } + return copyTracks; + + } else { + // Choose which algorithm to run, based on parameter dupTrkAlg_. + switch (dupTrkAlg_) { + // Run filters that only work on fitted tracks. + case DupAlgoName::Algo1: + return filterAlg1(vecTracks); + break; + case DupAlgoName::Algo2: + return filterAlg2(vecTracks); + break; + default: + throw cms::Exception("BadConfig") << "KillDupTrks: Cfg option DupFitTrkAlg has invalid value."; + } + } + } + + //=== Duplicate removal algorithm designed to run after the track helix fit, which eliminates duplicates + //=== simply by requiring that the fitted (q/Pt, phi0) of the track correspond to the same HT cell in + //=== which the track was originally found by the HT. + //=== N.B. This code runs on tracks in a single sector. It could be extended to run on tracks in entire + //=== tracker by adding the track's sector number to memory "htCellUsed" below. + + list DupFitTrkKiller::filterAlg1(const list& tracks) const { + // Hard-wired options to play with. + constexpr bool debug = false; + constexpr bool doRecoveryStep = true; // Do 2nd pass through rejected tracks to see if any should be rescued. + constexpr bool reduceDups = true; // Option attempting to reduce duplicate tracks during 2nd pass. + constexpr bool memorizeAllHTcells = + false; // First pass stores in memory all cells that the HT found tracks in, not just those of tracks accepted by the first pass. + constexpr bool doSectorCheck = false; // Require fitted helix to lie within sector. + constexpr bool usePtAndZ0Cuts = false; + constexpr bool goOutsideArray = true; // Also store in memory stubs outside the HT array during 2nd pass. + constexpr bool limitDiff = true; // Limit allowed diff. between HT & Fit cell to <= 1. + + if (debug && not tracks.empty()) + PrintL1trk() << "Start DupFitTrkKiller" << tracks.size(); + + list tracksFiltered; + + // Make a first pass through the tracks, doing initial identification of duplicate tracks. + // N.B. BY FILLING THIS WITH CELLS AROUND SELECTED TRACKS, RATHER THAN JUST THE CELL CONTAINING THE + // TRACK, ONE CAN REDUCE THE DUPLICATE RATE FURTHER, AT COST TO EFFICIENCY. + set> htCellUsed; + list tracksRejected; + + // For checking if multiple tracks corresponding to same TP are accepted by duplicate removal. + map> tpFound; + map tpFoundAtPass; + + for (const L1fittedTrack& trk : tracks) { + // Only consider tracks whose fitted helix parameters are in the same sector as the HT originally used to find the track. + if ((!doSectorCheck) || trk.consistentSector()) { + if ((!usePtAndZ0Cuts) || + (std::abs(trk.z0()) < settings_->beamWindowZ() && trk.pt() > settings_->houghMinPt() - 0.2)) { + // For debugging. + const TP* tp = trk.matchedTP(); + + // Check if this track's fitted (q/pt, phi0) helix parameters correspond to the same HT cell as the HT originally found the track in. + bool consistentCell = trk.consistentHTcell(); + if (consistentCell) { + // This track is probably not a duplicate, so keep & and store its HT cell location (which equals the HT cell corresponding to the fitted track). + tracksFiltered.push_back(&trk); + // Memorize HT cell location corresponding to this track (identical for HT track & fitted track). + if (!memorizeAllHTcells) { + pair htCell = trk.cellLocationHT(); + htCellUsed.insert(htCell); + if (trk.l1track3D()->mergedHTcell()) { + // If this is a merged cell, block the other elements too, in case a track found by the HT in an unmerged cell + // has a fitted cell there. + pair htCell10(htCell.first + 1, htCell.second); + pair htCell01(htCell.first, htCell.second + 1); + pair htCell11(htCell.first + 1, htCell.second + 1); + htCellUsed.insert(htCell10); + htCellUsed.insert(htCell01); + htCellUsed.insert(htCell11); + } + } + + if (debug && tp != nullptr) { + PrintL1trk() << "FIRST PASS: m=" << trk.cellLocationHT().first << "/" << trk.cellLocationFit().first + << " c=" << trk.cellLocationHT().second << "/" << trk.cellLocationFit().second + << " Delta(m,c)=(" << int(trk.cellLocationHT().first) - int(trk.cellLocationFit().first) + << "," << int(trk.cellLocationHT().second) - int(trk.cellLocationFit().second) + << ") pure=" << trk.purity() << " merged=" << trk.l1track3D()->mergedHTcell() + << " #layers=" << trk.l1track3D()->numLayers() << " tp=" << tp->index() << " dupCell=(" + << tpFound[tp->index()].first << "," << tpFound[tp->index()].second + << ") dup=" << tpFoundAtPass[tp->index()]; + // If the following two variables are non-zero in printout, then track has already been found, + // so we have mistakenly kept a duplicate. + if (tpFound.find(tp->index()) != tpFound.end()) + tpFound[tp->index()] = trk.cellLocationFit(); + tpFoundAtPass[tp->index()] = 1; + } + + } else { + if (limitDiff) { + const unsigned int maxDiff = 1; + if (abs(int(trk.cellLocationHT().first) - int(trk.cellLocationFit().first)) <= int(maxDiff) && + abs(int(trk.cellLocationHT().second) - int(trk.cellLocationFit().second)) <= int(maxDiff)) + tracksRejected.push_back(&trk); + } else { + tracksRejected.push_back(&trk); + } + + if (debug && tp != nullptr) { + PrintL1trk() << "FIRST REJECT: m=" << trk.cellLocationHT().first << "/" << trk.cellLocationFit().first + << " c=" << trk.cellLocationHT().second << "/" << trk.cellLocationFit().second + << " Delta(m,c)=(" << int(trk.cellLocationHT().first) - int(trk.cellLocationFit().first) + << "," << int(trk.cellLocationHT().second) - int(trk.cellLocationFit().second) + << ") pure=" << trk.purity() << " merged=" << trk.l1track3D()->mergedHTcell() + << " #layers=" << trk.l1track3D()->numLayers() << " tp=" << tp->index() << " dupCell=(" + << tpFound[tp->index()].first << "," << tpFound[tp->index()].second + << ") dup=" << tpFoundAtPass[tp->index()]; + } + } + // Memorize HT cell location corresponding to this track, even if it was not accepted by first pass.. + if (memorizeAllHTcells) { + pair htCell = + trk.cellLocationFit(); // Intentionally used fit instead of HT here. + htCellUsed.insert(htCell); + if (trk.l1track3D()->mergedHTcell()) { + // If this is a merged cell, block the other elements too, in case a track found by the HT in an unmerged cell + // has a fitted cell there. + // N.B. NO GOOD REASON WHY "-1" IS NOT DONE HERE TOO. MIGHT REDUCE DUPLICATE RATE? + pair htCell10(htCell.first + 1, htCell.second); + pair htCell01(htCell.first, htCell.second + 1); + pair htCell11(htCell.first + 1, htCell.second + 1); + htCellUsed.insert(htCell10); + htCellUsed.insert(htCell01); + htCellUsed.insert(htCell11); + } + } + } + } + } + + if (doRecoveryStep) { + // Making a second pass through the rejected tracks, checking if any should be rescued. + for (const L1fittedTrack* trk : tracksRejected) { + // Get location in HT array corresponding to fitted track helix parameters. + pair htCell = trk->cellLocationFit(); + // If this HT cell was not already memorized, rescue this track, since it is probably not a duplicate, + // but just a track whose fitted helix parameters are a bit wierd for some reason. + if (std::count(htCellUsed.begin(), htCellUsed.end(), htCell) == 0) { + tracksFiltered.push_back(trk); // Rescue track. + // Optionally store cell location to avoid rescuing other tracks at the same location, which may be duplicates of this track. + bool outsideCheck = (goOutsideArray || trk->pt() > settings_->houghMinPt()); + if (reduceDups && outsideCheck) + htCellUsed.insert(htCell); + + // For debugging. + const TP* tp = trk->matchedTP(); + + if (debug && tp != nullptr) { + PrintL1trk() << "SECOND PASS: m=" << trk->cellLocationHT().first << "/" << trk->cellLocationFit().first + << " c=" << trk->cellLocationHT().second << "/" << trk->cellLocationFit().second + << " Delta(m,c)=(" << int(trk->cellLocationHT().first) - int(trk->cellLocationFit().first) + << "," << int(trk->cellLocationHT().second) - int(trk->cellLocationFit().second) + << ") pure=" << trk->purity() << " merged=" << trk->l1track3D()->mergedHTcell() + << " #layers=" << trk->l1track3D()->numLayers() << " tp=" << tp->index() << " dupCell=(" + << tpFound[tp->index()].first << "," << tpFound[tp->index()].second + << ") dup=" << tpFoundAtPass[tp->index()]; + if (tpFound.find(tp->index()) != tpFound.end()) + tpFound[tp->index()] = htCell; + tpFoundAtPass[tp->index()] = 2; + } + } + } + } + + // Debug printout to identify duplicate tracks that survived. + if (debug) + this->printDuplicateTracks(tracksFiltered); + + return tracksFiltered; + } + + //=== Duplicate removal algorithm designed to run after the track helix fit, which eliminates duplicates + //=== simply by requiring that no two tracks should have fitted (q/Pt, phi0) that correspond to the same HT + //=== cell. If they do, then only the first to arrive is kept. + //=== N.B. This code runs on tracks in a single sector. It could be extended to run on tracks in entire + //=== tracker by adding the track's sector number to memory "htCellUsed" below. + + list DupFitTrkKiller::filterAlg2(const list& tracks) const { + // Hard-wired options to play with. + const bool debug = false; + + if (debug && not tracks.empty()) + PrintL1trk() << "START " << tracks.size(); + + list tracksFiltered; + set> htCellUsed; + + for (const L1fittedTrack& trk : tracks) { + // Get location in HT array corresponding to fitted track helix parameters. + pair htCell = trk.cellLocationFit(); + // If this HT cell was not already memorized, rescue this track, since it is probably not a duplicate, + // but just a track whose fitted helix parameters are a bit wierd for some reason. + if (std::count(htCellUsed.begin(), htCellUsed.end(), htCell) == 0) { + tracksFiltered.push_back(&trk); // Rescue track. + // Store cell location to avoid rescuing other tracks at the same location, which may be duplicates of this track. + htCellUsed.insert(htCell); + if (debug) { + const TP* tp = trk.matchedTP(); + int tpIndex = (tp != nullptr) ? tp->index() : -999; + PrintL1trk() << "ALG51: m=" << trk.cellLocationHT().first << "/" << trk.cellLocationFit().first + << " c=" << trk.cellLocationHT().second << "/" << trk.cellLocationFit().second + << " tp=" << tpIndex << " pure=" << trk.purity(); + } + } + } + + // Debug printout to identify duplicate tracks that survived. + if (debug) + this->printDuplicateTracks(tracksFiltered); + + return tracksFiltered; + } + + // Debug printout of which tracks are duplicates. + void DupFitTrkKiller::printDuplicateTracks(const list& tracks) const { + map> tpMap; + for (const L1fittedTrack* trk : tracks) { + const TP* tp = trk->matchedTP(); + if (tp != nullptr) { + tpMap[tp].push_back(trk); + } + } + for (const auto& p : tpMap) { + const TP* tp = p.first; + const list vecTrk = p.second; + if (vecTrk.size() > 1) { + for (const L1fittedTrack* trk : vecTrk) { + PrintL1trk() << " MESS UP : m=" << trk->cellLocationHT().first << "/" << trk->cellLocationFit().first + << " c=" << trk->cellLocationHT().second << "/" << trk->cellLocationFit().second + << " tp=" << tp->index() << " tp_pt=" << tp->pt() << " fit_pt=" << trk->pt() + << " pure=" << trk->purity(); + PrintL1trk() << " stubs = "; + for (const Stub* s : trk->stubs()) + PrintL1trk() << s->index() << " "; + PrintL1trk(); + } + } + } + if (not tracks.empty()) + PrintL1trk() << "FOUND " << tracks.size(); + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/HTbase.cc b/L1Trigger/TrackFindingTMTT/src/HTbase.cc new file mode 100644 index 0000000000000..5629fd6a51087 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/HTbase.cc @@ -0,0 +1,224 @@ +#include "L1Trigger/TrackFindingTMTT/interface/HTbase.h" +#include "L1Trigger/TrackFindingTMTT/interface/InputData.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" + +#include "DataFormats/Math/interface/deltaPhi.h" + +#include + +using namespace std; + +namespace tmtt { + + // Initialization. + HTbase::HTbase( + const Settings* settings, unsigned int iPhiSec, unsigned int iEtaReg, unsigned int nBinsX, unsigned int nBinsY) + : settings_(settings), + iPhiSec_(iPhiSec), + iEtaReg_(iEtaReg), + nBinsX_(nBinsX), + nBinsY_(nBinsY), + htArray_(nBinsX, nBinsY), + optoLinkID_(this->calcOptoLinkID()) {} + + //=== Termination. Causes HT array to search for tracks etc. + + void HTbase::end() { + // Calculate useful info about each cell in array. + for (unsigned int i = 0; i < nBinsX_; i++) { + for (unsigned int j = 0; j < nBinsY_; j++) { + htArray_(i, j)->end(); // Calls HTcell::end() + } + } + + // Produce a list of all track candidates found in this array, each containing all the stubs on each one + // and the track helix parameters, plus the associated truth particle (if any). + trackCands2D_ = this->calcTrackCands2D(); + + // If requested, kill those tracks in this sector that can't be read out during the time-multiplexed period, because + // the HT has associated too many stubs to tracks. + if (settings_->busySectorKill()) { + trackCands2D_ = this->killTracksBusySec(trackCands2D_); + } + } + + //=== Number of filtered stubs in each cell summed over all cells in HT array. + //=== If a stub appears in multiple cells, it will be counted multiple times. + unsigned int HTbase::numStubsInc() const { + unsigned int nStubs = 0; + + // Loop over cells in HT array. + for (unsigned int i = 0; i < nBinsX_; i++) { + for (unsigned int j = 0; j < nBinsY_; j++) { + nStubs += htArray_(i, j)->numStubs(); // Calls HTcell::numStubs() + } + } + + return nStubs; + } + + //=== Number of filtered stubs in HT array. + //=== If a stub appears in multiple cells, it will be counted only once. + unsigned int HTbase::numStubsExc() const { + unordered_set stubIDs; // Each ID stored only once, no matter how often it is added. + + // Loop over cells in HT array. + for (unsigned int i = 0; i < nBinsX_; i++) { + for (unsigned int j = 0; j < nBinsY_; j++) { + // Loop over stubs in each cells, storing their IDs. + const vector& vStubs = htArray_(i, j)->stubs(); // Calls HTcell::stubs() + for (const Stub* stub : vStubs) { + stubIDs.insert(stub->index()); + } + } + } + + return stubIDs.size(); + } + + //=== Get number of filtered stubs assigned to track candidates found in this HT array. + + unsigned int HTbase::numStubsOnTrackCands2D() const { + unsigned int nStubs = 0; + + // Loop over track candidates + for (const L1track2D& trk : trackCands2D_) { + nStubs += trk.stubs().size(); + } + + return nStubs; + } + + //=== Get all reconstructed tracks that were associated to the given tracking particle. + //=== (If the vector is empty, then the tracking particle was not reconstructed in this sector). + + vector HTbase::assocTrackCands2D(const TP& tp) const { + vector assocRecoTrk; + + // Loop over track candidates, looking for those associated to given TP. + for (const L1track2D& trk : trackCands2D_) { + if (trk.matchedTP() != nullptr) { + if (trk.matchedTP()->index() == tp.index()) + assocRecoTrk.push_back(&trk); + } + } + + return assocRecoTrk; + } + + //=== Disable filters (used for debugging). + + void HTbase::disableBendFilter() { + // Loop over cells in HT array. + for (unsigned int i = 0; i < nBinsX_; i++) { + for (unsigned int j = 0; j < nBinsY_; j++) { + htArray_(i, j)->disableBendFilter(); + } + } + } + + //=== Given a range in one of the coordinates specified by coordRange, calculate the corresponding range of bins. The other arguments specify the axis. And also if some cells nominally associated to stub are to be killed. + + pair HTbase::convertCoordRangeToBinRange(pair coordRange, + unsigned int nBinsAxis, + float coordAxisMin, + float coordAxisBinSize, + unsigned int killSomeHTcells) const { + float coordMin = coordRange.first; + float coordMax = coordRange.second; + float coordAvg = (coordRange.first + coordRange.second) / 2.; + + int iCoordBinMin, iCoordBinMax; + + //--- There are various options for doing this. + //--- Option killSomeHTcells = 0 is the obvious one. + //--- If killSomeHTcells > 0, then some of the cells nominally associated with the stub are killed. + + if (killSomeHTcells == 0) { + // Take the full range of phi bins consistent with the stub. + iCoordBinMin = floor((coordMin - coordAxisMin) / coordAxisBinSize); + iCoordBinMax = floor((coordMax - coordAxisMin) / coordAxisBinSize); + } else if (killSomeHTcells == 1) { + // Use the reduced range of bins. + // This algorithm, proposed by Ian, should reduce the rate, at the cost of some efficiency. + const float fracCut = 0.3; + iCoordBinMin = floor((coordMin - coordAxisMin) / coordAxisBinSize); + iCoordBinMax = floor((coordMax - coordAxisMin) / coordAxisBinSize); + unsigned int nbins = iCoordBinMax - iCoordBinMin + 1; + if (nbins >= 2) { // Can't reduce range if already only 1 bin + float lower = coordAxisMin + (iCoordBinMin + 1) * coordAxisBinSize; // upper edge of lowest bin + float upper = coordAxisMin + (iCoordBinMax)*coordAxisBinSize; // lower edge of highest bin. + // Calculate fractional amount of min and max bin that this stub uses. + float extraLow = (lower - coordMin) / coordAxisBinSize; + float extraUp = (coordMax - upper) / coordAxisBinSize; + constexpr float small = 0.001; // allow tolerance on floating point precision. + if (min(extraLow, extraUp) < -small || max(extraLow, extraUp) > (1.0 + small)) + throw cms::Exception("LogicError") << "HTbase: convertCoordRangeToBinRange error"; + if (extraLow < fracCut && (nbins >= 3 || extraLow < extraUp)) + iCoordBinMin += 1; + if (extraUp < fracCut && (nbins >= 3 || extraUp < extraLow)) + iCoordBinMax -= 1; + } + } else if (killSomeHTcells == 2) { + // This corresponds to Thomas's firmware implementation, which can't fill more than one HT cell per column. + iCoordBinMin = floor((coordAvg - coordAxisMin) / coordAxisBinSize); + iCoordBinMax = iCoordBinMin; + } else { + throw cms::Exception("BadConfig") << "HT: invalid KillSomeHTCells option in cfg"; + } + + // Limit range to dimensions of HT array. + iCoordBinMin = max(iCoordBinMin, 0); + iCoordBinMax = min(iCoordBinMax, int(nBinsAxis) - 1); + + // If whole range is outside HT array, flag this by setting range to specific values with min > max. + if (iCoordBinMin > int(nBinsAxis) - 1 || iCoordBinMax < 0) { + iCoordBinMin = int(nBinsAxis) - 1; + iCoordBinMax = 0; + } + + return pair(iCoordBinMin, iCoordBinMax); + } + + //=== Return a list of all track candidates found in this array, giving access to all the stubs on each one + //=== and the track helix parameters, plus the associated truth particle (if any). + + list HTbase::calcTrackCands2D() const { + list trackCands2D; + + // Check if the hardware processes rows of the HT array in a specific order when outputting track candidates. + // Currently this is by decreasing Pt for r-phi HT and unordered for r-z HT. + const vector iOrder = this->rowOrder(nBinsX_); + bool wantOrdering = (not iOrder.empty()); + + // Loop over cells in HT array. + for (unsigned int i = 0; i < nBinsX_; i++) { + // Access rows in specific order if required. + unsigned int iPos = wantOrdering ? iOrder[i] : i; + + for (unsigned int j = 0; j < nBinsY_; j++) { + if (htArray_(iPos, j)->trackCandFound()) { // track candidate found in this cell. + + // Note if this corresponds to a merged HT cell (e.g. 2x2). + const bool merged = htArray_(iPos, j)->mergedCell(); + + // Get stubs on this track candidate. + const vector& stubs = htArray_(iPos, j)->stubs(); + + // And note location of cell inside HT array. + const pair cellLocation(iPos, j); + + // Get (q/Pt, phi0) or (tan_lambda, z0) corresponding to middle of this cell. + const pair helixParams2D = this->helix2Dconventional(iPos, j); + + // Create track and store it. + trackCands2D.emplace_back( + settings_, stubs, cellLocation, helixParams2D, iPhiSec_, iEtaReg_, optoLinkID_, merged); + } + } + } + + return trackCands2D; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/HTcell.cc b/L1Trigger/TrackFindingTMTT/src/HTcell.cc new file mode 100644 index 0000000000000..fa7661ae9b2cc --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/HTcell.cc @@ -0,0 +1,140 @@ +#include "L1Trigger/TrackFindingTMTT/interface/HTcell.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" + +using namespace std; + +namespace tmtt { + + //=== Initialization with cfg params, + //=== rapidity range of current sector, and estimated q/Pt of cell, + //=== and the bin number of the cell along the q/Pt axis of the r-phi HT array, + //=== and a flag indicating if this cell is the merge of smaller HT cells. + + HTcell::HTcell(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float qOverPt, + unsigned int ibin_qOverPt, + bool mergedCell, + bool miniHTcell) + : settings_(settings), + // Sector number + iPhiSec_(iPhiSec), + iEtaReg_(iEtaReg), + // Rapidity range of sector. + etaMinSector_(etaMinSector), + etaMaxSector_(etaMaxSector), + // Track q/Pt. + qOverPtCell_(qOverPt), + // Note bin number of cell along q/Pt axis of r-phi HT array. (Not used if r-z HT). + ibin_qOverPt_(ibin_qOverPt), + mergedCell_(mergedCell), + // Is cell in Mini-HT? + miniHTcell_(miniHTcell), + invPtToDphi_(settings->invPtToDphi()), // B*c/2E11 + // Use filter in each HT cell using only stubs which have consistent bend? + useBendFilter_(settings->useBendFilter()), + // Check if subsectors are being used within each sector. These are only ever used for r-phi HT. + numSubSecs_(settings->numSubSecsEta()) { + // A filter is used each HT cell, which prevents more than the specified number of stubs being stored in the cell. (Reflecting memory limit of hardware). + if (miniHTcell_) { + maxStubsInCell_ = settings->maxStubsInCellMiniHough(); + } else { + maxStubsInCell_ = settings->maxStubsInCell(); + } + } + + //=== Termination. Search for track in this HT cell etc. + + void HTcell::end() { + // Produce list of filtered stubs by applying all requested filters (e.g. on stub bend). + // (If no filters are requested, then filtered & unfiltered stub collections will be identical). + + // N.B. Other filters, such as the r-z filters, which the firmware runs after the HT because they are too slow within it, + // are not defined here, but instead inside class TrkFilterAfterRphiHT. + + vFilteredStubs_ = vStubs_; + if (useBendFilter_) + vFilteredStubs_ = this->bendFilter(vFilteredStubs_); + + // Prevent too many stubs being stored in a single HT cell if requested (to reflect hardware memory limits). + // N.B. This MUST be the last filter applied. + constexpr unsigned int disableThreshold = 999; + if (maxStubsInCell_ < disableThreshold) + vFilteredStubs_ = this->maxStubCountFilter(vFilteredStubs_); + + // Calculate the number of layers the filtered stubs in this cell are in. + numFilteredLayersInCell_ = this->calcNumFilteredLayers(); + + if (numSubSecs_ > 1) { + // If using subsectors within each sector, calculate the number of layers the filters stubs in this cell are in, + // when one considers only the subset of the stubs within each subsector. + // Look for the "best" subsector. + numFilteredLayersInCellBestSubSec_ = 0; + for (unsigned int i = 0; i < numSubSecs_; i++) { + unsigned int numLaySubSec = this->calcNumFilteredLayers(i); + numFilteredLayersInCellBestSubSec_ = max(numFilteredLayersInCellBestSubSec_, numLaySubSec); + } + } else { + // If only 1 sub-sector, then subsector and sector are identical. + numFilteredLayersInCellBestSubSec_ = numFilteredLayersInCell_; + } + } + + // Calculate how many tracker layers the filter stubs in this cell are in, when only the subset of those stubs + // that are in the specified subsector are counted. + + unsigned int HTcell::calcNumFilteredLayers(unsigned int iSubSec) const { + vector stubsInSubSec; + for (const Stub* s : vFilteredStubs_) { + const vector& inSubSec = subSectors_.at(s); // Find out which subsectors this stub is in. + if (inSubSec[iSubSec]) + stubsInSubSec.push_back(s); + } + return Utility::countLayers(settings_, stubsInSubSec); + } + + //=== Produce a filtered collection of stubs in this cell that all have consistent bend. + //=== Only called for r-phi Hough transform. + + vector HTcell::bendFilter(const vector& stubs) const { + // Create bend-filtered stub collection. + vector filteredStubs; + for (Stub* s : stubs) { + // Require stub bend to be consistent with q/Pt of this cell. + + unsigned int minBin = s->min_qOverPt_bin(); + unsigned int maxBin = s->max_qOverPt_bin(); + if (mergedCell_) { + if (minBin % 2 == 1) + minBin--; + } + if (minBin <= ibin_qOverPt_ && ibin_qOverPt_ <= maxBin) + filteredStubs.push_back(s); + } + + return filteredStubs; + } + + //=== Filter stubs so as to prevent more than specified number of stubs being stored in one cell. + //=== This reflects finite memory of hardware. + + vector HTcell::maxStubCountFilter(const vector& stubs) const { + vector filteredStubs; + // If there are too many stubs in a cell, the hardware keeps (maxStubsInCell - 1) of the first stubs in the list + // plus the last stub. + if (stubs.size() > maxStubsInCell_) { + for (unsigned int i = 0; i < maxStubsInCell_ - 1; i++) { // first stubs + filteredStubs.push_back(stubs[i]); + } + filteredStubs.push_back(stubs[stubs.size() - 1]); // plus last stub + } else { + filteredStubs = stubs; + } + return filteredStubs; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/HTrphi.cc b/L1Trigger/TrackFindingTMTT/src/HTrphi.cc new file mode 100644 index 0000000000000..e341c8ae55c7f --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/HTrphi.cc @@ -0,0 +1,684 @@ +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" +#include "L1Trigger/TrackFindingTMTT/interface/InputData.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" +#include "FWCore/ServiceRegistry/interface/Service.h" + +#include "DataFormats/Math/interface/deltaPhi.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include +#include +#include +#include +#include + +using namespace std; + +namespace tmtt { + + //=== The r-phi Hough Transform array for a single (eta,phi) sector. + //=== + //=== Its axes are (q/Pt, phiTrk), where phiTrk is the phi at which the track crosses a + //=== user-configurable radius from the beam-line. + + //=== Initialise + + HTrphi::HTrphi(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float phiCentreSector, + HTrphi::ErrorMonitor* errMon) + : HTbase(settings, iPhiSec, iEtaReg, settings->houghNbinsPt(), settings->houghNbinsPhi()), + invPtToDphi_((settings->invPtToDphi())), + shape_(static_cast(settings->shape())), // shape of HT cells + + //--- Specification of HT q/Pt axis. + maxAbsQoverPtAxis_(1. / settings->houghMinPt()), // Max. |q/Pt| covered by HT array. + nBinsQoverPtAxis_(settings->houghNbinsPt()), // No. of bins in HT array in q/Pt. + binSizeQoverPtAxis_(2 * maxAbsQoverPtAxis_ / nBinsQoverPtAxis_), + + //--- Specification of HT phiTrk axis + // (phiTrk corresponds to phi where track crosses radius = chosenRofPhi_). + chosenRofPhi_(settings->chosenRofPhi()), + phiCentreSector_(phiCentreSector), // Centre of phiTrk sector. + maxAbsPhiTrkAxis_(M_PI / float(settings->numPhiSectors())), // Half-width of phiTrk axis in HT array. + nBinsPhiTrkAxis_(settings->houghNbinsPhi()), // No. of bins in HT array phiTrk + binSizePhiTrkAxis_(2 * maxAbsPhiTrkAxis_ / nBinsPhiTrkAxis_), + errMon_(errMon) { + // Deal with unusually shaped HT cells. + if (shape_ != HTshape::square) + binSizeQoverPtAxis_ = 2. * maxAbsQoverPtAxis_ / (nBinsQoverPtAxis_ - 1.); + if (shape_ == HTshape::hexagon) + binSizePhiTrkAxis_ = 2. * maxAbsPhiTrkAxis_ / (nBinsPhiTrkAxis_ - 1. / 6.); + else if (shape_ == HTshape::diamond) + binSizePhiTrkAxis_ = 2. * maxAbsPhiTrkAxis_ / (nBinsPhiTrkAxis_ - 1. / 2.); + + // Optionally merge 2x2 neighbouring cells into a single cell at low Pt, to reduce efficiency loss due to + // scattering. (Do this if either of options EnableMerge2x2 or MiniHTstage are enabled. + // N.B These two options are never both enabled). + enableMerge2x2_ = (settings->enableMerge2x2() || settings->miniHTstage()); + if (settings->miniHTstage()) { + // Mini-HT stage cfg: Merge all bins, irrespective of Pt. + minInvPtToMerge2x2_ = 0.; + } else { + // Merged cells cfg: Merge bins below specified Pt threshold. + minInvPtToMerge2x2_ = 1. / (settings->maxPtToMerge2x2()); + if (minInvPtToMerge2x2_ > maxAbsQoverPtAxis_) + enableMerge2x2_ = false; + } + + // Merging 2x2 cells into 1 merged cell is only allowed if HT array dimensions are even. + // (This restriction could be removed along q/Pt axis, since there are also unmerged cells there. But this + // would require correcting the code after each called to mergedCell() below, since + // "if (i%2 == 1) iStore = i - 1" not correct in this case). + if (enableMerge2x2_ && (nBinsQoverPtAxis_ % 2 != 0 || nBinsPhiTrkAxis_ % 2 != 0)) + throw cms::Exception("BadConfig") << "HTrphi: You are not allowed to set EnableMerge2x2 or MiniHTstage = True if " + "you have an odd number of bins " + "in r-phi HT array " + << nBinsQoverPtAxis_ << " " << nBinsPhiTrkAxis_; + + //--- Other options used when filling the HT. + + // Don't fill all HT cells nominally crossed by line corresponding to stub. + killSomeHTCellsRphi_ = settings->killSomeHTCellsRphi(); + + // Used to kill excess stubs or tracks that can't be transmitted within time-multiplexed period. + nReceivedStubs_ = 0; + busyInputSectorKill_ = settings_->busyInputSectorKill(); // Kill excess stubs going fron GP to HT? + busySectorKill_ = settings_->busySectorKill(); // Kill excess tracks flowing out of HT? + // Max. num. of stubs that can be sent from GP to HT within TM period + busyInputSectorNumStubs_ = settings_->busyInputSectorNumStubs(); + // Max. num. of stubs that can be sent out of HT within TM period + busySectorNumStubs_ = settings_->busySectorNumStubs(); + // or individual m bin (=q/Pt) ranges to be output to optical links. + busySectorMbinRanges_ = settings_->busySectorMbinRanges(); + // Specifies which m bins should be grouped together by BusySectorMbinRanges. If empty, then they are grouped in order 0,1,2,3,4,5 ... + busySectorMbinOrder_ = settings_->busySectorMbinOrder(); + // m bin ranges option disabled if vector empty + busySectorUseMbinRanges_ = (not busySectorMbinRanges_.empty()); + busySectorUseMbinOrder_ = (not busySectorMbinOrder_.empty()); + + bool rescaleMbins = false; + if (busySectorUseMbinRanges_) { + // Check if the total number of bins specified in cfg option BusySectorMbinRanges corresponds + // to the number of m bins (q/Pt) in the HT. If not, determine how much the ranges must be scaled + // to make this true. + unsigned int nTotalBins = 0; + for (unsigned int j = 0; j < busySectorMbinRanges_.size(); j++) { + nTotalBins += busySectorMbinRanges_[j]; + } + rescaleMbins = (nTotalBins != nBinsQoverPtAxis_); + // No rescaling allowed with MBinOrder option. + if (rescaleMbins && busySectorUseMbinOrder_) + throw cms::Exception("BadConfig") << "HTrphi: BusySectorUserMbin error"; + float rescaleFactor = rescaleMbins ? float(nBinsQoverPtAxis_) / float(nTotalBins) : 1.; + // Find lower and upper inclusive limits of each m bin range to be sent to a separate optical link. + busySectorMbinLow_.resize(busySectorMbinRanges_.size()); + busySectorMbinHigh_.resize(busySectorMbinRanges_.size()); + float mBinSum = 0.; + for (unsigned int i = 0; i < busySectorMbinRanges_.size(); i++) { + busySectorMbinLow_[i] = std::round(mBinSum); + busySectorMbinHigh_[i] = std::round(mBinSum + rescaleFactor * busySectorMbinRanges_[i]) - 1; + mBinSum += rescaleFactor * busySectorMbinRanges_[i]; + } + } + // + for (unsigned int i = 0; i < nBinsQoverPtAxis_; i++) { + for (unsigned int j = 0; j < nBinsPhiTrkAxis_; j++) { + pair helix = this->helix2Dconventional(i, j); // Get track params at centre of cell. + float qOverPt = helix.first; + // Check if this cell is merged with its neighbours (as in low Pt region). + bool mergedCell = false; + if (enableMerge2x2_ && this->mergedCell(i, j)) + mergedCell = true; + // Initialize each cell in HT array. + HTbase::htArray_(i, j) = + std::make_unique(settings, iPhiSec, iEtaReg, etaMinSector, etaMaxSector, qOverPt, i, mergedCell); + } + } + + std::stringstream text; + text << "\n"; + text << "=== R-PHI HOUGH TRANSFORM AXES RANGES: abs(q/Pt) < " << maxAbsQoverPtAxis_ << " & abs(track-phi) < " + << maxAbsPhiTrkAxis_ << " ===\n"; + text << "=== R-PHI HOUGH TRANSFORM ARRAY SIZE: q/Pt bins = " << nBinsQoverPtAxis_ + << " & track-phi bins = " << nBinsPhiTrkAxis_ << " ===\n"; + text << "=== R-PHI HOUGH TRANSFORM BIN SIZE: BIN(q/Pt) = " << binSizeQoverPtAxis_ + << " & BIN(track-phi) = " << binSizePhiTrkAxis_ << " ===\n\n"; + if (busySectorKill_ && busySectorUseMbinRanges_ && rescaleMbins) { + text << "=== R-PHI HOUGH TRANSFORM WARNING: Rescaled m bin ranges specified by cfg parameter " + "BusySectorMbinRanges, as they were inconsistent with total number of m bins in HT.\n"; + text << "=== Rescaled values for BusySectorMbinRanges ="; + for (unsigned int i = 0; i < busySectorMbinRanges_.size(); i++) { + text << " " << (busySectorMbinHigh_[i] - busySectorMbinLow_[i] + 1); + } + } + text << "\n"; + static std::once_flag printOnce; + std::call_once( + printOnce, [](string t) { PrintL1trk() << t; }, text.str()); + + // Note helix parameters at the centre of each HT cell. + cellCenters_.clear(); + for (unsigned int m = 0; m < nBinsQoverPtAxis_; m++) { + std::vector > binCenters; + for (unsigned int c = 0; c < nBinsPhiTrkAxis_; c++) + binCenters.push_back(this->helix2Dhough(m, c)); + cellCenters_.push_back(binCenters); + } + } + + //=== Add stub to HT array. + //=== If eta subsectors are being used within each sector, specify which ones the stub is compatible with. + + void HTrphi::store(Stub* stub, const vector& inEtaSubSecs) { + // Optionally, only store stubs that can be sent from GP to HT within TM period. + if ((!busyInputSectorKill_) || (nReceivedStubs_ < busyInputSectorNumStubs_)) { + nReceivedStubs_++; + + unsigned int jPhiTrkBinMinLast = 0; // Used for error checking + unsigned int jPhiTrkBinMaxLast = 99999; + + // Loop over q/Pt related bins in HT array. + for (unsigned int i = 0; i < nBinsQoverPtAxis_; i++) { + if (shape_ == HTshape::square) { + //--- This is a traditional HT with square cells. + + // In this q/Pt bin, find the range of phi bins that this stub is consistent with. + pair jRange = this->iPhiRange(stub, i); + unsigned int jPhiTrkBinMin = jRange.first; + unsigned int jPhiTrkBinMax = jRange.second; + + // Store stubs in these cells. + for (unsigned int j = jPhiTrkBinMin; j <= jPhiTrkBinMax; j++) { + bool canStoreStub = true; + unsigned int iStore = i; + unsigned int jStore = j; + + // Optionally merge 2x2 neighbouring cells into a single cell at low Pt, to reduce efficiency loss + // due to scattering. + if (enableMerge2x2_) { + // Check if this cell is merged with its neighbours (as in low Pt region). + if (this->mergedCell(i, j)) { + // Get location of cell that this cell is merged into (iStore, jStore). + // Calculation assumes HT array has even number of bins in both dimensions. + if (i % 2 == 1) + iStore = i - 1; + if (j % 2 == 1) + jStore = j - 1; + // If this stub was already stored in this merged 2x2 cell, then don't store it again. + if (HTbase::htArray_(iStore, jStore)->stubStoredInCell(stub)) + canStoreStub = false; + } + } + + if (canStoreStub) + HTbase::htArray_(iStore, jStore)->store(stub, inEtaSubSecs); // Calls HTcell::store() + } + + // Check that limitations of firmware would not prevent stub being stored correctly in this HT column. + if (errMon_ != nullptr) { + this->countFirmwareErrors(i, jPhiTrkBinMin, jPhiTrkBinMax, jPhiTrkBinMinLast, jPhiTrkBinMaxLast); + jPhiTrkBinMinLast = jPhiTrkBinMin; + jPhiTrkBinMaxLast = jPhiTrkBinMax; + } + + } else { + //--- This is are novel HT with unusual shaped cells. + + if (shape_ == HTshape::diamond) { + //--- This HT has diamond shaped cells. + + float qOverPtBin = -maxAbsQoverPtAxis_ + i * binSizeQoverPtAxis_; + float phiTrk = reco::deltaPhi(stub->phi(), phiCentreSector_) + + invPtToDphi_ * qOverPtBin * (stub->r() - chosenRofPhi_) + maxAbsPhiTrkAxis_; + if (i % 2 == 0) + phiTrk += binSizePhiTrkAxis_ / 2.; + unsigned int binCenter = std::floor(phiTrk / binSizePhiTrkAxis_); + if (binCenter < nBinsPhiTrkAxis_) + HTbase::htArray_(i, binCenter)->store(stub, inEtaSubSecs); + + } else if (shape_ == HTshape::hexagon) { + //--- This HT has hexagonal cells (with two of its sides parallel to the phi axis). + + float qOverPtBin = -maxAbsQoverPtAxis_ + i * binSizeQoverPtAxis_; + float qOverPtBinVar = binSizeQoverPtAxis_; + float phiTrk = reco::deltaPhi(stub->phi(), phiCentreSector_) + + invPtToDphi_ * qOverPtBin * (stub->r() - chosenRofPhi_) + maxAbsPhiTrkAxis_; + float phiTrkVar = invPtToDphi_ * qOverPtBinVar * std::abs(stub->r() - chosenRofPhi_); + float phiTrkMin = phiTrk - phiTrkVar; + float phiTrkMax = phiTrk + phiTrkVar; + if (i % 2 == 0) + phiTrk += binSizePhiTrkAxis_ / 6.; + else { + phiTrk -= binSizePhiTrkAxis_ / 3.; + phiTrkMin -= binSizePhiTrkAxis_ / 2.; + phiTrkMax -= binSizePhiTrkAxis_ / 2.; + } + unsigned int iCenter = std::floor(phiTrk / binSizePhiTrkAxis_ * 3.); + unsigned int iMin = std::floor(phiTrkMin / binSizePhiTrkAxis_ * 3.); + unsigned int iMax = std::floor(phiTrkMax / binSizePhiTrkAxis_ * 3.); + std::pair binCenter; + std::pair binMin; + std::pair binMax; + binCenter.second = iCenter / 3; + binMin.second = iMin / 3; + binMax.second = iMax / 3; + binCenter.first = !(iCenter % 3 == 2); + binMin.first = (iMin % 3 == 0); + binMax.first = (iMax % 3 == 0); + if (binCenter.first && binCenter.second < nBinsPhiTrkAxis_) + HTbase::htArray_(i, binCenter.second)->store(stub, inEtaSubSecs); + else if (binMin.first && binMin.second < nBinsPhiTrkAxis_) + HTbase::htArray_(i, binMin.second)->store(stub, inEtaSubSecs); + else if (binMax.first && binMax.second < nBinsPhiTrkAxis_) + HTbase::htArray_(i, binMax.second)->store(stub, inEtaSubSecs); + + } else if (shape_ == HTshape::brick) { + //--- This HT has square cells with alternate rows shifted horizontally by 0.5*cell_width. + + float qOverPtBin = -maxAbsQoverPtAxis_ + i * binSizeQoverPtAxis_; + float qOverPtBinVar = binSizeQoverPtAxis_; + float phiTrk = reco::deltaPhi(stub->phi(), phiCentreSector_) + + invPtToDphi_ * qOverPtBin * (stub->r() - chosenRofPhi_) + maxAbsPhiTrkAxis_; + float phiTrkVar = invPtToDphi_ * qOverPtBinVar * std::abs(stub->r() - chosenRofPhi_); + float phiTrkMin = phiTrk - phiTrkVar; + float phiTrkMax = phiTrk + phiTrkVar; + unsigned int iMin = std::floor(phiTrkMin / binSizePhiTrkAxis_ * 2.); + unsigned int iMax = std::floor(phiTrkMax / binSizePhiTrkAxis_ * 2.); + std::pair binMin; + std::pair binMax; + binMin.second = iMin / 2; + binMax.second = iMax / 2; + binMin.first = (iMin % 2 == i % 2); + binMax.first = (iMax % 2 == i % 2); + if (binMin.first && binMin.second < nBinsPhiTrkAxis_) + HTbase::htArray_(i, binMin.second)->store(stub, inEtaSubSecs); + else if (binMax.first && binMax.second < nBinsPhiTrkAxis_) + HTbase::htArray_(i, binMax.second)->store(stub, inEtaSubSecs); + } + } + } + // Note max. |gradient| that the line corresponding to any stub in any of the r-phi HT arrays could have. + // Firmware assumes this should not exceed 1.0; + if (errMon_ != nullptr) { + errMon_->maxLineGradient = max(errMon_->maxLineGradient.load(), this->calcLineGradArray(stub->r())); + } + } + } + + //=== Determine the m-bin (q/pt) range the specified track is in. (Used if outputting each m bin range on a different opto-link). + + unsigned int HTrphi::getMbinRange(const L1track2D& trk) const { + if (busySectorUseMbinRanges_) { + unsigned int mBin = trk.cellLocationHT().first; + unsigned int mBinOrder; + if (busySectorUseMbinOrder_) { + // User wants to group bins in a wierd order. + mBinOrder = 99999; + for (unsigned int k = 0; k < busySectorMbinOrder_.size(); k++) { + if (mBin == busySectorMbinOrder_[k]) + mBinOrder = k; + } + if (mBinOrder == 99999) + throw cms::Exception("LogicError") << "HTrphi::getMbinRange() mBinOrder calculation wrong."; + } else { + // User grouping bins in numerical order 0,1,2,3,4,5... + mBinOrder = mBin; + } + for (unsigned int i = 0; i < busySectorMbinRanges_.size(); i++) { + if (mBinOrder >= busySectorMbinLow_[i] && mBinOrder <= busySectorMbinHigh_[i]) + return i; + } + throw cms::Exception("LogicError") << "HTrphi::getMbinRange() messed up"; + } else { + return 0; + } + } + + //=== For a given Q/Pt bin, find the range of phi bins that a given stub is consistent with. + //=== Return as a pair (min bin, max bin) + //=== If it range lies outside the HT array, then the min bin will be set larger than the max bin. + + pair HTrphi::iPhiRange(const Stub* stub, unsigned int iQoverPtBin, bool debug) const { + // Note q/Pt value corresponding to centre of this bin. + float qOverPtBin = -maxAbsQoverPtAxis_ + (iQoverPtBin + 0.5) * binSizeQoverPtAxis_; + // Note change in this q/Pt value needed to reach either edge of the bin. + float qOverPtBinVar = 0.5 * binSizeQoverPtAxis_; + + // Reducing effective bin width can reduce fake rate. + //qOverPtVar = 0.4*binSizeQoverPtAxis_; + + // Calculate range of track-phi that would allow a track in this q/Pt range to pass through the stub. + float phiTrk = stub->phi() + invPtToDphi_ * qOverPtBin * (stub->r() - chosenRofPhi_); + // The next line does the phiTrk calculation without the usual approximation, but it doesn't + // improve performance. + //float phiTrk = stub->phi() + asin(invPtToDphi_ * qOverPtBin * stub->r()) - asin(invPtToDphi_ * qOverPtBin * chosenRofPhi_); + float phiTrkVar = invPtToDphi_ * qOverPtBinVar * std::abs(stub->r() - chosenRofPhi_); + float phiTrkMin = phiTrk - phiTrkVar; + float phiTrkMax = phiTrk + phiTrkVar; + + float deltaPhiMin = reco::deltaPhi(phiTrkMin, phiCentreSector_); // Offset to centre of sector. + float deltaPhiMax = reco::deltaPhi(phiTrkMax, phiCentreSector_); + pair phiTrkRange(deltaPhiMin, deltaPhiMax); + + // Determine which HT array cell range in track-phi this range "phiTrkRange" corresponds to. + pair iPhiTrkBinRange = this->HTbase::convertCoordRangeToBinRange( + phiTrkRange, nBinsPhiTrkAxis_, (-maxAbsPhiTrkAxis_), binSizePhiTrkAxis_, killSomeHTCellsRphi_); + + return iPhiTrkBinRange; + } + + //=== Check that limitations of firmware would not prevent stub being stored correctly in this HT column. + + void HTrphi::countFirmwareErrors(unsigned int iQoverPtBin, + unsigned int jPhiTrkBinMin, + unsigned int jPhiTrkBinMax, + unsigned int jPhiTrkBinMinLast, + unsigned int jPhiTrkBinMaxLast) { + // Only do check if stub is being stored somewhere in this HT column. + if (jPhiTrkBinMax >= jPhiTrkBinMin) { + //--- Remaining code below checks that firmware could successfully store this stub in this column. + // (a) Does cell lie NE, E or SE of cell filled in previous column? + bool OK_a = (jPhiTrkBinMin + 1 >= jPhiTrkBinMinLast) && (jPhiTrkBinMax <= jPhiTrkBinMaxLast + 1); + // (b) Are no more than 2 cells filled in this column + bool OK_b = (jPhiTrkBinMax - jPhiTrkBinMin + 1 <= 2); + + if (!OK_a) + errMon_->numErrorsTypeA++; + if (!OK_b) + errMon_->numErrorsTypeB++; + errMon_->numErrorsNorm++; // No. of times a stub is added to an HT column. + } + } + + //=== Get the values of the track helix params corresponding to middle of a specified HT cell (i,j). + //=== The helix parameters returned will be those corresponding to the two axes of the HT array. + //=== So they might be (q/pt, phi0) or (q/pt, phi65) etc. depending on the configuration. + + pair HTrphi::helix2Dhough(unsigned int i, unsigned int j) const { + unsigned int qOverPtBin = i; + unsigned int phiTrkBin = j; + + // If using merged 2x2 cells in low Pt parts of array, must correct for this. + bool merged = false; + if (enableMerge2x2_) { + // Check if this cell is merged with its neighbours (as in low Pt region). + if (this->mergedCell(i, j)) { + merged = true; + // Get location of cell that this cell is merged into (iStore, jStore). + // Calculation assumes HT array has even number of bins in both dimensions. + if (i % 2 == 1) + qOverPtBin = i - 1; + if (j % 2 == 1) + phiTrkBin = j - 1; + } + } + + float qOverPtBinCenter = .5; + float phiTrkBinCenter = .5; + + if (shape_ != HTshape::square) { + qOverPtBinCenter = 0.; + + float evenPhiPos = 0., oddPhiPos = 0.; + if (shape_ == HTshape::hexagon) { + evenPhiPos = 1. / 6.; + oddPhiPos = 2. / 3.; + } else if (shape_ == HTshape::diamond) { + evenPhiPos = 0.; + oddPhiPos = 0.5; + } else if (shape_ == HTshape::brick) { + evenPhiPos = 0.25; + oddPhiPos = 0.75; + } + phiTrkBinCenter = (qOverPtBin % 2 == 0) ? evenPhiPos : oddPhiPos; + } + + float qOverPt = -maxAbsQoverPtAxis_ + (qOverPtBin + qOverPtBinCenter) * binSizeQoverPtAxis_; + float phiTrk = -maxAbsPhiTrkAxis_ + (phiTrkBin + phiTrkBinCenter) * binSizePhiTrkAxis_; + + if (merged) { + qOverPt += 0.5 * binSizeQoverPtAxis_; + phiTrk += 0.5 * binSizePhiTrkAxis_; + } + + // Correct phiTrk to centre of sector, taking care of 2*pi wrapping + phiTrk = reco::deltaPhi(phiTrk + phiCentreSector_, 0.); + return pair(qOverPt, phiTrk); + } + + //=== Get the values of the track helix params corresponding to middle of a specified HT cell (i,j). + //=== The helix parameters returned will be always be (q/pt, phi0), irrespective of how the axes + //=== of the HT array are defined. + + pair HTrphi::helix2Dconventional(unsigned int i, unsigned int j) const { + // Get the helix parameters corresponding to the axes definitions of the HT. + pair helix2Dht = this->helix2Dhough(i, j); + // Convert to the conventionally agreed pair of helix parameters, (q/pt, phi0). + float qOverPt = helix2Dht.first; // easy + // If HT defined track phi other than at r=0, must correct to get phi0. Allow for 2*pi wrapping of phi. + float phi0 = reco::deltaPhi(helix2Dht.second + invPtToDphi_ * chosenRofPhi_ * qOverPt, 0.); + return pair(qOverPt, phi0); + } + + //=== Which cell in HT array should this TP be in, based on its true trajectory? + //=== (If TP is outside HT array, it it put in the closest bin inside it). + + pair HTrphi::trueCell(const TP* tp) const { + // Get HT axis variables corresponding to this TP. + float qOverPt = tp->qOverPt(); + float phiTrk = tp->trkPhiAtR(chosenRofPhi_); + // Measure phi relative to centre of sector. + float deltaPhi = reco::deltaPhi(phiTrk, phiCentreSector_); + // Convert to bin numbers inside HT array. + int iQoverPt = floor((qOverPt - (-maxAbsQoverPtAxis_)) / binSizeQoverPtAxis_); + int iPhiTrk = floor((deltaPhi - (-maxAbsPhiTrkAxis_)) / binSizePhiTrkAxis_); + // Check if this cell was within the HT array. + if (iQoverPt >= 0 && iQoverPt < int(nBinsQoverPtAxis_) && iPhiTrk >= 0 && iPhiTrk < int(nBinsPhiTrkAxis_)) { + // Check if this cell is merged with its neighbours (as in low Pt region), and if so return merged cell location. + // New: because 2nd stage mini HT may recreate tracks from merged cells with finer cell granularity, one can't predict + // if a merged cell was used to create a track merely by looking at its cell location. + // So instead ask L1track3D, which knows if it was created from a merged HT cell or not. + ; + } else { + // TP is not in this HT array at all. Flag this by setting "outside" bin index to 0 (Nbins) if outside array below (above). + if (iQoverPt < 0) + iQoverPt = 0; + if (iQoverPt >= int(nBinsQoverPtAxis_)) + iQoverPt = nBinsQoverPtAxis_ - 1; + if (iPhiTrk < 0) + iPhiTrk = 0; + if (iPhiTrk >= int(nBinsPhiTrkAxis_)) + iPhiTrk = nBinsPhiTrkAxis_ - 1; + } + return pair(iQoverPt, iPhiTrk); + } + + //=== Which cell in HT array should this fitted track be in, based on its fitted trajectory? + //=== Always uses beam-spot constrained trajectory if available. + //=== (If fitted track is outside HT array, it it put in the closest bin inside it). + + pair HTrphi::cell(const L1fittedTrack* fitTrk) const { + bool beamConstraint = fitTrk->done_bcon(); // Is beam-spot constraint available? (e.g. 5 param helix fit) + // Get HT axis variables corresponding to this fitted track. + float qOverPt = beamConstraint ? fitTrk->qOverPt_bcon() : fitTrk->qOverPt(); + // Convert phi0 to phi at chosen radius used in HT. + float phiTrk = fitTrk->phiAtChosenR(beamConstraint); + // Measure phi relative to centre of sector. + float deltaPhi = reco::deltaPhi(phiTrk, phiCentreSector_); + // Convert to bin numbers inside HT array. + int iQoverPt = 999999; + int iPhiTrk = 999999; + + if (shape_ == HTshape::square) { + //--- This is a traditional HT with square cells. + + iQoverPt = floor((qOverPt - (-maxAbsQoverPtAxis_)) / binSizeQoverPtAxis_); + iPhiTrk = floor((deltaPhi - (-maxAbsPhiTrkAxis_)) / binSizePhiTrkAxis_); + + // Check if this cell was within the HT array. + if (iQoverPt >= 0 && iQoverPt < int(nBinsQoverPtAxis_) && iPhiTrk >= 0 && iPhiTrk < int(nBinsPhiTrkAxis_)) { + // Check if this cell is merged with its neighbours (as in low Pt region), and if so return merged cell location. + // New: because 2nd stage mini HT may recreate tracks from merged cells with finer cell granularity, one can't predict + // if a merged cell was used to create a track merely by looking at its cell location. + // So instead ask L1track3D, which knows if it was created from a merged HT cell or not. + ; + } else { + // Fitted track is not in this HT array at all. Flag this by setting "outside" bin index to 0 (Nbins-1) if outside array below (above). + if (iQoverPt < 0) + iQoverPt = 0; + if (iQoverPt >= int(nBinsQoverPtAxis_)) + iQoverPt = nBinsQoverPtAxis_ - 1; + if (iPhiTrk < 0) + iPhiTrk = 0; + if (iPhiTrk >= int(nBinsPhiTrkAxis_)) + iPhiTrk = nBinsPhiTrkAxis_ - 1; + } + + } else { + //--- This is are novel HT with unusual shaped cells. + + float minD = std::numeric_limits::infinity(); + float d(0); + unsigned int m(0); + for (auto binCenters : cellCenters_) { + unsigned int c(0); + for (auto cellCenter : binCenters) { + d = std::pow((cellCenter.first - qOverPt) / (float)binSizeQoverPtAxis_, 2) + + std::pow((cellCenter.second - phiTrk) / (float)binSizePhiTrkAxis_, 2); + if (d < minD) { + minD = d; + iQoverPt = m; + iPhiTrk = c; + } + c++; + } + m++; + } + // Fitted track is not in this HT array at all. Flag this by setting "outside" bin index to 0 (Nbins-1) if outside array below (above). + if (iQoverPt < 0) + iQoverPt = 0; + if (iQoverPt >= int(nBinsQoverPtAxis_)) + iQoverPt = nBinsQoverPtAxis_ - 1; + if (iPhiTrk < 0) + iPhiTrk = 0; + if (iPhiTrk >= int(nBinsPhiTrkAxis_)) + iPhiTrk = nBinsPhiTrkAxis_ - 1; + } + + return pair(iQoverPt, iPhiTrk); + } + + //=== Check if specified cell is merged with its 2x2 neighbours into a single cell, + //=== as it is in low Pt region. + + bool HTrphi::mergedCell(unsigned int iQoverPtBin, unsigned int jPhiTrkBin) const { + bool merge = false; + + if (enableMerge2x2_) { + unsigned int i = iQoverPtBin; + //unsigned int j = jPhiTrkBin; + + // Calculate number of merged bins on each q/Pt side of array. + float fMergeBins = (maxAbsQoverPtAxis_ - minInvPtToMerge2x2_) / (2. * binSizeQoverPtAxis_); + // Number of unmerged bins this corresponds to, which must be even, since each merged bin comprises two normal q/pt bins. + unsigned int numQoverPtBinsToMerge = 2 * min((unsigned int)(std::round(fMergeBins)), (nBinsQoverPtAxis_ / 4)); + const float small = 0.001; + if (minInvPtToMerge2x2_ < small && (unsigned int)(std::round(2. * fMergeBins)) % 2 == 1) + numQoverPtBinsToMerge++; + unsigned int iB = (nBinsQoverPtAxis_ - 1) - i; // Count backwards across array. + if (min(i, iB) < numQoverPtBinsToMerge) + merge = true; + } + + return merge; + } + + //=== Calculate line |gradient| of stubs in HT array, so can check it doesn't exceed 1. + + float HTrphi::calcLineGradArray(float r) const { + float grad = std::abs(invPtToDphi_ * (r - chosenRofPhi_)); + // Convert it to units of bin width. + grad *= binSizeQoverPtAxis_ / binSizePhiTrkAxis_; + if (shape_ == HTshape::hexagon) + grad *= 3.; + else if (shape_ == HTshape::diamond) + grad *= 2.; + else if (shape_ == HTshape::brick) + grad *= 4.; + return grad; + } + + //=== If requested, kill those tracks in this sector that can't be read out during the time-multiplexed period, because + //=== the HT has associated too many stubs to tracks. + + list HTrphi::killTracksBusySec(const list& tracks) const { + list outTracks; + + if (busySectorKill_) { + unsigned int nStubsOut = 0; // #stubs assigned to tracks in this sector. + // #stubs assigned to each m bin range in this sector. + vector nStubsOutInRange(busySectorMbinRanges_.size(), 0); + + for (const L1track2D& trk : tracks) { + bool keep = true; + unsigned int nStubs = trk.numStubs(); // #stubs on this track. + if (busySectorUseMbinRanges_) { // Are tracks from different m bin ranges output seperately to increase bandwidth? + unsigned int mBinRange = this->getMbinRange(trk); // Which m bin range is this track in? + nStubsOutInRange[mBinRange] += nStubs; + if (nStubsOutInRange[mBinRange] > busySectorNumStubs_) + keep = false; + } else { + nStubsOut += nStubs; + if (nStubsOut > busySectorNumStubs_) + keep = false; + } + + if (keep) + outTracks.push_back(trk); + } + + } else { + outTracks = tracks; + } + + return outTracks; + } + + //=== Define the order in which the hardware processes rows of the HT array when it outputs track candidates. + //=== Currently corresponds to highest Pt tracks first. + //=== If two tracks have the same Pt, the -ve charge one is output before the +ve charge one. + + vector HTrphi::rowOrder(unsigned int numRows) const { + vector iOrder; + + // Logic slightly different depending on whether HT array has even or odd number of rows. + const bool oddNumRows = (numRows % 2 == 1); + + // This selects middle rows first before moving to exterior ones. + if (oddNumRows) { + unsigned int middleRow = (numRows - 1) / 2; + iOrder.push_back(middleRow); + for (unsigned int i = 1; i <= (numRows - 1) / 2; i++) { + iOrder.push_back(middleRow - i); // -ve charge + iOrder.push_back(middleRow + i); // +ve charge + } + } else { + unsigned int startRowPos = numRows / 2; + unsigned int startRowNeg = startRowPos - 1; + for (unsigned int i = 0; i < numRows / 2; i++) { + iOrder.push_back(startRowNeg - i); // -ve charge + iOrder.push_back(startRowPos + i); // +ve charge + } + } + + return iOrder; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/Histos.cc b/L1Trigger/TrackFindingTMTT/src/Histos.cc new file mode 100644 index 0000000000000..012e79ac805e8 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/Histos.cc @@ -0,0 +1,1762 @@ +#include "L1Trigger/TrackFindingTMTT/interface/Histos.h" +#include "L1Trigger/TrackFindingTMTT/interface/InputData.h" +#include "L1Trigger/TrackFindingTMTT/interface/Sector.h" +#include "L1Trigger/TrackFindingTMTT/interface/HTrphi.h" +#include "L1Trigger/TrackFindingTMTT/interface/Make3Dtracks.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrkRZfilter.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" + +#include "DataFormats/Math/interface/deltaPhi.h" +#include "DataFormats/Math/interface/deltaR.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace tmtt { + + //=== Store cfg parameters. + + Histos::Histos(const Settings* settings) : settings_(settings), oldSumW2opt_(false), bApproxMistake_(false) { + genMinStubLayers_ = settings->genMinStubLayers(); + numPhiSectors_ = settings->numPhiSectors(); + numEtaRegions_ = settings->numEtaRegions(); + houghMinPt_ = settings->houghMinPt(); + houghNbinsPt_ = settings->houghNbinsPt(); + houghNbinsPhi_ = settings->houghNbinsPhi(); + chosenRofZ_ = settings->chosenRofZ(); + trackFitters_ = settings->trackFitters(); + useRZfilter_ = settings->useRZfilter(); + ranRZfilter_ = (not useRZfilter_.empty()); // Was any r-z track filter run? + resPlotOpt_ = settings->resPlotOpt(); // Only use signal events for helix resolution plots? + } + + //=== Book all histograms + + void Histos::book() { + // Don't bother booking histograms if user didn't request them via TFileService in their cfg. + if (not this->enabled()) + return; + + oldSumW2opt_ = TH1::GetDefaultSumw2(); + TH1::SetDefaultSumw2(true); + + // Book histograms about input data. + this->bookInputData(); + // Book histograms checking if (eta,phi) sector definition choices are good. + this->bookEtaPhiSectors(); + // Book histograms checking filling of r-phi HT array. + this->bookRphiHT(); + // Book histograms about r-z track filters. + if (ranRZfilter_) + this->bookRZfilters(); + // Book histograms studying 3D track candidates found after HT. + this->bookTrackCands("HT"); + // Book histograms studying 3D track candidates found after r-z track filter. + if (ranRZfilter_) + this->bookTrackCands("RZ"); + // Book histograms studying track fitting performance + this->bookTrackFitting(); + } + + //=== Fill all histograms + + void Histos::fill(const InputData& inputData, + const matrix>& mSectors, + const matrix>& mHtRphis, + const matrix>& mMake3Dtrks, + const std::map>& mapFinalTracks) { + // Each function here protected by a mytex lock, so only one thread can run it at a time. + + // Don't bother filling histograms if user didn't request them via TFileService in their cfg. + if (not this->enabled()) + return; + + // Fill histograms about input data. + this->fillInputData(inputData); + + // Fill histograms checking if (eta,phi) sector definition choices are good. + this->fillEtaPhiSectors(inputData, mSectors); + + // Fill histograms checking filling of r-phi HT array. + this->fillRphiHT(mHtRphis); + + // Fill histograms about r-z track filters. + if (ranRZfilter_) + this->fillRZfilters(mMake3Dtrks); + + // Fill histograms studying 3D track candidates found after HT. + this->fillTrackCands(inputData, mMake3Dtrks, "HT"); + + // Fill histograms studying 3D track candidates found after r-z track filter. + if (ranRZfilter_) { + this->fillTrackCands(inputData, mMake3Dtrks, "RZ"); + } + + // Fill histograms studying track fitting performance + this->fillTrackFitting(inputData, mapFinalTracks); + } + + //=== Book histograms using input stubs and tracking particles. + + TFileDirectory Histos::bookInputData() { + TFileDirectory inputDir = fs_->mkdir("InputData"); + + hisStubsVsEta_ = inputDir.make("StubsVsEta", "; #eta; No. stubs in tracker", 30, -3.0, 3.0); + hisStubsVsR_ = inputDir.make("StubsVsR", "; radius (cm); No. stubs in tracker", 1200, 0., 120.); + + hisNumLayersPerTP_ = + inputDir.make("NumLayersPerTP", "; Number of layers per TP for alg. eff.", 20, -0.5, 19.5); + hisNumPSLayersPerTP_ = + inputDir.make("NumPSLayersPerTP", "; Number of PS layers per TP for alg. eff.", 20, -0.5, 19.5); + + // Study efficiency of tightened front end-electronics cuts. + + hisStubKillFE_ = inputDir.make( + "StubKillFE", "; barrelLayer or 10+endcapRing; Stub fraction rejected by FE chip", 30, -0.5, 29.5); + hisStubIneffiVsInvPt_ = + inputDir.make("StubIneffiVsPt", "; 1/Pt; Inefficiency of FE chip for good stubs", 25, 0.0, 0.5); + hisStubIneffiVsEta_ = + inputDir.make("StubIneffiVsEta", "; |#eta|; Inefficiency of FE chip for good stubs", 15, 0.0, 3.0); + + // Study stub resolution. + + hisBendStub_ = inputDir.make("BendStub", "; Stub bend in units of strips", 59, -7.375, 7.375); + hisBendResStub_ = inputDir.make("BendResStub", "; Stub bend minus TP bend in units of strips", 100, -5., 5.); + + // Histos for denominator of tracking efficiency + hisTPinvptForEff_ = inputDir.make("TPinvptForEff", "; TP 1/Pt (for effi.);", 50, 0., 0.5); + hisTPetaForEff_ = inputDir.make("TPetaForEff", "; TP #eta (for effi.);", 20, -3., 3.); + hisTPphiForEff_ = inputDir.make("TPphiForEff", "; TP #phi (for effi.);", 20, -M_PI, M_PI); + hisTPd0ForEff_ = inputDir.make("TPd0ForEff", "; TP d0 (for effi.);", 40, 0., 4.); + hisTPz0ForEff_ = inputDir.make("TPz0ForEff", "; TP z0 (for effi.);", 50, 0., 25.); + // + hisTPinvptForAlgEff_ = inputDir.make("TPinvptForAlgEff", "; TP 1/Pt (for alg. effi.);", 50, 0., 0.5); + hisTPetaForAlgEff_ = inputDir.make("TPetaForAlgEff", "; TP #eta (for alg. effi.);", 20, -3., 3.); + hisTPphiForAlgEff_ = inputDir.make("TPphiForAlgEff", "; TP #phi (for alg. effi.);", 20, -M_PI, M_PI); + hisTPd0ForAlgEff_ = inputDir.make("TPd0ForAlgEff", "; TP d0 (for alg. effi.);", 40, 0., 4.); + hisTPz0ForAlgEff_ = inputDir.make("TPz0ForAlgEff", "; TP z0 (for alg. effi.);", 50, 0., 25.); + + return inputDir; + } + + //=== Fill histograms using input stubs and tracking particles. + + void Histos::fillInputData(const InputData& inputData) { + // Allow only one thread to run this function at a time + static std::mutex myMutex; + std::lock_guard myGuard(myMutex); + + const list& vStubs = inputData.stubsConst(); + const list& vTPs = inputData.getTPs(); + + for (const Stub* stub : vStubs) { + hisStubsVsEta_->Fill(stub->eta()); + hisStubsVsR_->Fill(stub->r()); + } + + // Study efficiency of stubs to pass front-end electronics cuts. + + const list& vAllStubs = inputData.allStubs(); // Get all stubs prior to FE cuts to do this. + for (const Stub& s : vAllStubs) { + unsigned int layerOrTenPlusRing = s.barrel() ? s.layerId() : 10 + s.trackerModule()->endcapRing(); + // Fraction of all stubs (good and bad) failing tightened front-end electronics cuts. + hisStubKillFE_->Fill(layerOrTenPlusRing, (!s.frontendPass())); + } + + // Study efficiency for good stubs of tightened front end-electronics cuts. + for (const TP& tp : vTPs) { + if (tp.useForAlgEff()) { // Only bother for stubs that are on TP that we have a chance of reconstructing. + const vector& stubs = tp.assocStubs(); + for (const Stub* s : stubs) { + hisStubIneffiVsInvPt_->Fill(1. / tp.pt(), (!s->frontendPass())); + hisStubIneffiVsEta_->Fill(std::abs(tp.eta()), (!s->frontendPass())); + } + } + } + + // Plot stub bend-derived information. + for (const Stub* stub : vStubs) { + hisBendStub_->Fill(stub->bend()); + } + + // Look at stub resolution. + for (const TP& tp : vTPs) { + if (tp.useForAlgEff()) { + const vector& assStubs = tp.assocStubs(); + + for (const Stub* stub : assStubs) { + hisBendResStub_->Fill(stub->bend() - tp.dphi(stub->r()) / stub->dphiOverBend()); + } + + if (std::abs(tp.eta()) < 0.5) { + double nLayersOnTP = Utility::countLayers(settings_, assStubs, true, false); + double nPSLayersOnTP = Utility::countLayers(settings_, assStubs, true, true); + hisNumLayersPerTP_->Fill(nLayersOnTP); + hisNumPSLayersPerTP_->Fill(nPSLayersOnTP); + } + } + } + + // Determine r (z) range of each barrel layer (endcap wheel). + + for (const Stub* stub : vStubs) { + unsigned int layer = stub->layerId(); + if (stub->barrel()) { + // Get range in r of each barrel layer. + float r = stub->r(); + if (mapBarrelLayerMinR_.find(layer) == mapBarrelLayerMinR_.end()) { + mapBarrelLayerMinR_[layer] = r; + mapBarrelLayerMaxR_[layer] = r; + } else { + if (mapBarrelLayerMinR_[layer] > r) + mapBarrelLayerMinR_[layer] = r; + if (mapBarrelLayerMaxR_[layer] < r) + mapBarrelLayerMaxR_[layer] = r; + } + } else { + layer = layer % 10; + // Range in |z| of each endcap wheel. + float z = std::abs(stub->z()); + if (mapEndcapWheelMinZ_.find(layer) == mapEndcapWheelMinZ_.end()) { + mapEndcapWheelMinZ_[layer] = z; + mapEndcapWheelMaxZ_[layer] = z; + } else { + if (mapEndcapWheelMinZ_[layer] > z) + mapEndcapWheelMinZ_[layer] = z; + if (mapEndcapWheelMaxZ_[layer] < z) + mapEndcapWheelMaxZ_[layer] = z; + } + } + } + + // Determine Range in (r,|z|) of each module type. + + for (const Stub* stub : vStubs) { + float r = stub->r(); + float z = std::abs(stub->z()); + unsigned int modType = stub->trackerModule()->moduleTypeID(); + // Do something ugly, as modules in 1-2nd & 3-4th endcap wheels are different to those in wheel 5 ... + // And boundary between flat & tilted modules in barrel layers 1-3 varies in z. + if (stub->barrel() && stub->layerId() == 1) { // barrel layer 1 + if (mapExtraAModuleTypeMinR_.find(modType) == mapExtraAModuleTypeMinR_.end()) { + mapExtraAModuleTypeMinR_[modType] = r; + mapExtraAModuleTypeMaxR_[modType] = r; + mapExtraAModuleTypeMinZ_[modType] = z; + mapExtraAModuleTypeMaxZ_[modType] = z; + } else { + if (mapExtraAModuleTypeMinR_[modType] > r) + mapExtraAModuleTypeMinR_[modType] = r; + if (mapExtraAModuleTypeMaxR_[modType] < r) + mapExtraAModuleTypeMaxR_[modType] = r; + if (mapExtraAModuleTypeMinZ_[modType] > z) + mapExtraAModuleTypeMinZ_[modType] = z; + if (mapExtraAModuleTypeMaxZ_[modType] < z) + mapExtraAModuleTypeMaxZ_[modType] = z; + } + } else if (stub->barrel() && stub->layerId() == 2) { // barrel layer 2 + if (mapExtraBModuleTypeMinR_.find(modType) == mapExtraBModuleTypeMinR_.end()) { + mapExtraBModuleTypeMinR_[modType] = r; + mapExtraBModuleTypeMaxR_[modType] = r; + mapExtraBModuleTypeMinZ_[modType] = z; + mapExtraBModuleTypeMaxZ_[modType] = z; + } else { + if (mapExtraBModuleTypeMinR_[modType] > r) + mapExtraBModuleTypeMinR_[modType] = r; + if (mapExtraBModuleTypeMaxR_[modType] < r) + mapExtraBModuleTypeMaxR_[modType] = r; + if (mapExtraBModuleTypeMinZ_[modType] > z) + mapExtraBModuleTypeMinZ_[modType] = z; + if (mapExtraBModuleTypeMaxZ_[modType] < z) + mapExtraBModuleTypeMaxZ_[modType] = z; + } + } else if (!stub->barrel() && (stub->layerId() % 10 == 1 || stub->layerId() % 10 == 2)) { // endcap wheel 1-2 + if (mapExtraCModuleTypeMinR_.find(modType) == mapExtraCModuleTypeMinR_.end()) { + mapExtraCModuleTypeMinR_[modType] = r; + mapExtraCModuleTypeMaxR_[modType] = r; + mapExtraCModuleTypeMinZ_[modType] = z; + mapExtraCModuleTypeMaxZ_[modType] = z; + } else { + if (mapExtraCModuleTypeMinR_[modType] > r) + mapExtraCModuleTypeMinR_[modType] = r; + if (mapExtraCModuleTypeMaxR_[modType] < r) + mapExtraCModuleTypeMaxR_[modType] = r; + if (mapExtraCModuleTypeMinZ_[modType] > z) + mapExtraCModuleTypeMinZ_[modType] = z; + if (mapExtraCModuleTypeMaxZ_[modType] < z) + mapExtraCModuleTypeMaxZ_[modType] = z; + } + } else if (!stub->barrel() && (stub->layerId() % 10 == 3 || stub->layerId() % 10 == 4)) { // endcap wheel 3-4 + if (mapExtraDModuleTypeMinR_.find(modType) == mapExtraDModuleTypeMinR_.end()) { + mapExtraDModuleTypeMinR_[modType] = r; + mapExtraDModuleTypeMaxR_[modType] = r; + mapExtraDModuleTypeMinZ_[modType] = z; + mapExtraDModuleTypeMaxZ_[modType] = z; + } else { + if (mapExtraDModuleTypeMinR_[modType] > r) + mapExtraDModuleTypeMinR_[modType] = r; + if (mapExtraDModuleTypeMaxR_[modType] < r) + mapExtraDModuleTypeMaxR_[modType] = r; + if (mapExtraDModuleTypeMinZ_[modType] > z) + mapExtraDModuleTypeMinZ_[modType] = z; + if (mapExtraDModuleTypeMaxZ_[modType] < z) + mapExtraDModuleTypeMaxZ_[modType] = z; + } + } else { // barrel layer 3-6 or endcap wheel 5. + if (mapModuleTypeMinR_.find(modType) == mapModuleTypeMinR_.end()) { + mapModuleTypeMinR_[modType] = r; + mapModuleTypeMaxR_[modType] = r; + mapModuleTypeMinZ_[modType] = z; + mapModuleTypeMaxZ_[modType] = z; + } else { + if (mapModuleTypeMinR_[modType] > r) + mapModuleTypeMinR_[modType] = r; + if (mapModuleTypeMaxR_[modType] < r) + mapModuleTypeMaxR_[modType] = r; + if (mapModuleTypeMinZ_[modType] > z) + mapModuleTypeMinZ_[modType] = z; + if (mapModuleTypeMaxZ_[modType] < z) + mapModuleTypeMaxZ_[modType] = z; + } + } + } + + //=== Make denominator of tracking efficiency plots + + for (const TP& tp : vTPs) { + if (tp.useForEff()) { // Check TP is good for efficiency measurement. + // Plot kinematics of all good TP. + hisTPinvptForEff_->Fill(1. / tp.pt()); + hisTPetaForEff_->Fill(tp.eta()); + hisTPphiForEff_->Fill(tp.phi0()); + // Plot also production point of all good TP. + hisTPd0ForEff_->Fill(std::abs(tp.d0())); + hisTPz0ForEff_->Fill(std::abs(tp.z0())); + + if (tp.useForAlgEff()) { // Check TP is good for algorithmic efficiency measurement. + hisTPinvptForAlgEff_->Fill(1. / tp.pt()); + hisTPetaForAlgEff_->Fill(tp.eta()); + hisTPphiForAlgEff_->Fill(tp.phi0()); + // Plot also production point of all good TP. + hisTPd0ForAlgEff_->Fill(std::abs(tp.d0())); + hisTPz0ForAlgEff_->Fill(std::abs(tp.z0())); + } + } + } + } + + //=== Book histograms checking if (eta,phi) sector defis(nition choices are good. + + TFileDirectory Histos::bookEtaPhiSectors() { + TFileDirectory inputDir = fs_->mkdir("CheckSectors"); + + // Check if stubs excessively duplicated between overlapping sectors. + hisNumEtaSecsPerStub_ = + inputDir.make("NumEtaSecPerStub", "; No. of #eta sectors each stub in", 20, -0.5, 19.5); + hisNumPhiSecsPerStub_ = + inputDir.make("NumPhiSecPerStub", "; No. of #phi sectors each stub in", 20, -0.5, 19.5); + + // Count stubs per (eta,phi) sector. + hisNumStubsPerSec_ = inputDir.make("NumStubsPerSec", "; No. of stubs per sector", 150, -0.5, 299.5); + + return inputDir; + } + + //=== Fill histograms checking if (eta,phi) sector definition choices are good. + + void Histos::fillEtaPhiSectors(const InputData& inputData, const matrix>& mSectors) { + // Allow only one thread to run this function at a time + static std::mutex myMutex; + std::lock_guard myGuard(myMutex); + + const list& vStubs = inputData.stubsConst(); + //const list& vTPs = inputData.getTPs(); + + //=== Loop over all stubs, counting how many sectors each one appears in. + + for (const Stub* stub : vStubs) { + // Number of (eta,phi), phi & eta sectors containing this stub. + unsigned int nEtaSecs = 0; + unsigned int nPhiSecs = 0; + + // Loop over (eta, phi) sectors. + for (unsigned int iPhiSec = 0; iPhiSec < numPhiSectors_; iPhiSec++) { + for (unsigned int iEtaReg = 0; iEtaReg < numEtaRegions_; iEtaReg++) { + const Sector* sector = mSectors(iPhiSec, iEtaReg).get(); + + // Check if sector contains stub stub, and if so count it. + // Take care to just use one eta (phi) typical region when counting phi (eta) sectors. + if (iPhiSec == 0 && sector->insideEta(stub)) + nEtaSecs++; + if (iEtaReg == 0 && sector->insidePhi(stub)) + nPhiSecs++; + } + } + + // Plot number of sectors each stub appears in. + hisNumEtaSecsPerStub_->Fill(nEtaSecs); + hisNumPhiSecsPerStub_->Fill(nPhiSecs); + } + + //=== Loop over all sectors, counting the stubs in each one. + for (unsigned int iEtaReg = 0; iEtaReg < numEtaRegions_; iEtaReg++) { + for (unsigned int iPhiSec = 0; iPhiSec < numPhiSectors_; iPhiSec++) { + const Sector* sector = mSectors(iPhiSec, iEtaReg).get(); + + unsigned int nStubs = 0; + for (const Stub* stub : vStubs) { + if (sector->inside(stub)) + nStubs++; + } + hisNumStubsPerSec_->Fill(nStubs); + } + } + } + + //=== Book histograms checking filling of r-phi HT array. + + TFileDirectory Histos::bookRphiHT() { + TFileDirectory inputDir = fs_->mkdir("HTrphi"); + + return inputDir; + } + + //=== Fill histograms checking filling of r-phi HT array. + + void Histos::fillRphiHT(const matrix>& mHtRphis) { + //--- Loop over (eta,phi) sectors, counting the number of stubs in the HT array of each. + + // Allow only one thread to run this function at a time (UNCOMMENT IF YOU ADD HISTOS HERE) + //static std::mutex myMutex; + //std::lock_guard myGuard(myMutex); + } + + //=== Book histograms about r-z track filters (or other filters applied after r-phi HT array). + + TFileDirectory Histos::bookRZfilters() { + TFileDirectory inputDir = fs_->mkdir("RZfilters"); + + return inputDir; + } + + //=== Fill histograms about r-z track filters. + + void Histos::fillRZfilters(const matrix>& mMake3Dtrks) { + // Allow only one thread to run this function at a time (UNCOMMENT IF YOU ADD HISTOS HERE) + //static std::mutex myMutex; + //std::lock_guard myGuard(myMutex); + } + + //=== Book histograms studying track candidates found by Hough Transform. + + TFileDirectory Histos::bookTrackCands(const string& tName) { + // Now book histograms for studying tracking in general. + + auto addn = [tName](const string& s) { return TString::Format("%s_%s", s.c_str(), tName.c_str()); }; + + TFileDirectory inputDir = fs_->mkdir(addn("TrackCands").Data()); + + bool TMTT = (tName == "HT" || tName == "RZ"); + + // Count tracks in various ways (including/excluding duplicates, excluding fakes ...) + profNumTrackCands_[tName] = + inputDir.make(addn("NumTrackCands"), "; class; N. of tracks in tracker", 7, 0.5, 7.5); + profNumTrackCands_[tName]->GetXaxis()->SetBinLabel(7, "TP for eff recoed"); + profNumTrackCands_[tName]->GetXaxis()->SetBinLabel(6, "TP recoed"); + profNumTrackCands_[tName]->GetXaxis()->SetBinLabel(5, "TP recoed x #eta sector dups"); + profNumTrackCands_[tName]->GetXaxis()->SetBinLabel(4, "TP recoed x sector dups"); + profNumTrackCands_[tName]->GetXaxis()->SetBinLabel(2, "TP recoed x track dups"); + profNumTrackCands_[tName]->GetXaxis()->SetBinLabel(1, "reco tracks including fakes"); + profNumTrackCands_[tName]->LabelsOption("d"); + + hisNumTrksPerNon_[tName] = inputDir.make(addn("NumTrksPerNon"), "; No. tracks per nonant;", 100, -0.5, 399.5); + + unsigned int nEta = numEtaRegions_; + hisNumTracksVsQoverPt_[tName] = + inputDir.make(addn("NumTracksVsQoverPt"), "; Q/Pt; No. of tracks in tracker", 100, -0.5, 0.5); + if (TMTT) { + profNumTracksVsEta_[tName] = inputDir.make( + addn("NumTracksVsEta"), "; #eta region; No. of tracks in tracker", nEta, -0.5, nEta - 0.5); + } + + // Count stubs per event assigned to tracks (determines HT data output rate) + + profStubsOnTracks_[tName] = + inputDir.make(addn("StubsOnTracks"), "; ; No. of stubs on tracks per event", 1, 0.5, 1.5); + hisStubsOnTracksPerNon_[tName] = + inputDir.make(addn("StubsOnTracksPerNon"), "; No. of stubs on tracks per nonant", 100, -0.5, 4999.5); + + hisStubsPerTrack_[tName] = inputDir.make(addn("StubsPerTrack"), ";No. of stubs per track;", 50, -0.5, 49.5); + hisLayersPerTrack_[tName] = + inputDir.make(addn("LayersPerTrack"), ";No. of layers with stubs per track;", 20, -0.5, 19.5); + + if (TMTT) { + hisNumStubsPerLink_[tName] = + inputDir.make(addn("NumStubsPerLink"), "; Mean #stubs per MHT output opto-link;", 50, -0.5, 249.5); + profMeanStubsPerLink_[tName] = + inputDir.make(addn("MeanStubsPerLink"), "; Mean #stubs per MHT output opto-link;", 36, -0.5, 35.5); + } + + hisFracMatchStubsOnTracks_[tName] = inputDir.make( + addn("FracMatchStubsOnTracks"), "; Frac. of stubs per trk matching best TP;", 101, -0.005, 1.005); + + if (TMTT) { + // Study duplication of tracks within an individual HT array. + profDupTracksVsEta_[tName] = + inputDir.make(addn("DupTracksVsTPeta"), "; #eta; No. of dup. trks per TP;", 15, 0.0, 3.0); + profDupTracksVsInvPt_[tName] = + inputDir.make(addn("DupTracksVsInvPt"), "; 1/Pt; No. of dup. trks per TP", 25, 0., 0.5); + } + + // Histos of track params. + hisQoverPt_[tName] = inputDir.make(addn("QoverPt"), "; track q/Pt", 100, -0.5, 0.5); + hisPhi0_[tName] = inputDir.make(addn("Phi0"), "; track #phi0", 70, -3.5, 3.5); + hisEta_[tName] = inputDir.make(addn("Eta"), "; track #eta", 60, -3.0, 3.0); + hisZ0_[tName] = inputDir.make(addn("Z0"), "; track z0", 100, -25.0, 25.0); + + // Histos of track parameter resolution + hisQoverPtRes_[tName] = inputDir.make(addn("QoverPtRes"), "; track resolution in q/Pt", 100, -0.06, 0.06); + hisPhi0Res_[tName] = inputDir.make(addn("Phi0Res"), "; track resolution in #phi0", 100, -0.04, 0.04); + hisEtaRes_[tName] = inputDir.make(addn("EtaRes"), "; track resolution in #eta", 100, -1.0, 1.0); + hisZ0Res_[tName] = inputDir.make(addn("Z0Res"), "; track resolution in z0", 100, -10.0, 10.0); + + // Histos for tracking efficiency vs. TP kinematics + hisRecoTPinvptForEff_[tName] = + inputDir.make(addn("RecoTPinvptForEff"), "; TP 1/Pt of recoed tracks (for effi.);", 50, 0., 0.5); + hisRecoTPetaForEff_[tName] = + inputDir.make(addn("RecoTPetaForEff"), "; TP #eta of recoed tracks (for effi.);", 20, -3., 3.); + hisRecoTPphiForEff_[tName] = + inputDir.make(addn("RecoTPphiForEff"), "; TP #phi of recoed tracks (for effi.);", 20, -M_PI, M_PI); + + // Histo for efficiency to reconstruct track perfectly (no incorrect hits). + hisPerfRecoTPinvptForEff_[tName] = inputDir.make( + addn("PerfRecoTPinvptForEff"), "; TP 1/Pt of recoed tracks (for perf. effi.);", 50, 0., 0.5); + hisPerfRecoTPetaForEff_[tName] = + inputDir.make(addn("PerfRecoTPetaForEff"), "; TP #eta of recoed tracks (for perf. effi.);", 20, -3., 3.); + + // Histos for tracking efficiency vs. TP production point + hisRecoTPd0ForEff_[tName] = + inputDir.make(addn("RecoTPd0ForEff"), "; TP d0 of recoed tracks (for effi.);", 40, 0., 4.); + hisRecoTPz0ForEff_[tName] = + inputDir.make(addn("RecoTPz0ForEff"), "; TP z0 of recoed tracks (for effi.);", 50, 0., 25.); + + // Histos for algorithmic tracking efficiency vs. TP kinematics + hisRecoTPinvptForAlgEff_[tName] = + inputDir.make(addn("RecoTPinvptForAlgEff"), "; TP 1/Pt of recoed tracks (for alg. effi.);", 50, 0., 0.5); + hisRecoTPetaForAlgEff_[tName] = + inputDir.make(addn("RecoTPetaForAlgEff"), "; TP #eta of recoed tracks (for alg. effi.);", 20, -3., 3.); + hisRecoTPphiForAlgEff_[tName] = inputDir.make( + addn("RecoTPphiForAlgEff"), "; TP #phi of recoed tracks (for alg. effi.);", 20, -M_PI, M_PI); + + // Histo for efficiency to reconstruct track perfectly (no incorrect hits). + hisPerfRecoTPinvptForAlgEff_[tName] = inputDir.make( + addn("PerfRecoTPinvptForAlgEff"), "; TP 1/Pt of recoed tracks (for perf. alg. effi.);", 50, 0., 0.5); + hisPerfRecoTPetaForAlgEff_[tName] = + inputDir.make(addn("PerfRecoTPetaForAlgEff"), "; TP #eta (for perf. alg. effi.);", 20, -3., 3.); + + // Histos for algorithmic tracking efficiency vs. TP production point + hisRecoTPd0ForAlgEff_[tName] = + inputDir.make(addn("RecoTPd0ForAlgEff"), "; TP d0 of recoed tracks (for alg. effi.);", 40, 0., 4.); + hisRecoTPz0ForAlgEff_[tName] = + inputDir.make(addn("RecoTPz0ForAlgEff"), "; TP z0 of recoed tracks (for alg. effi.);", 50, 0., 25.); + + return inputDir; + } + + //=== Fill histograms studying track candidates found before track fit is run. + + void Histos::fillTrackCands(const InputData& inputData, + const matrix>& mMake3Dtrks, + const string& tName) { + // Allow only one thread to run this function at a time + static std::mutex myMutex; + std::lock_guard myGuard(myMutex); + + vector tracks; + bool withRZfilter = (tName == "RZ") ? true : false; + for (unsigned int iEtaReg = 0; iEtaReg < numEtaRegions_; iEtaReg++) { + for (unsigned int iPhiSec = 0; iPhiSec < numPhiSectors_; iPhiSec++) { + const Make3Dtracks* make3Dtrk = mMake3Dtrks(iPhiSec, iEtaReg).get(); + const std::list& tracksSec = make3Dtrk->trackCands3D(withRZfilter); + tracks.insert(tracks.end(), tracksSec.begin(), tracksSec.end()); + } + } + this->fillTrackCands(inputData, tracks, tName); + } + + //=== Fill histograms studying track candidates found before track fit is run. + + void Histos::fillTrackCands(const InputData& inputData, const vector& tracks, const string& tName) { + bool withRZfilter = (tName == "RZ"); + + bool algoTMTT = (tName == "HT" || tName == "RZ"); // Check if running TMTT or Hybrid L1 tracking. + + const list& vTPs = inputData.getTPs(); + + //=== Count track candidates found in the tracker. + + const unsigned int numPhiNonants = settings_->numPhiNonants(); + vector nTrksPerEtaReg(numEtaRegions_, 0); + vector nTrksPerNonant(numPhiNonants, 0); + for (const L1track3D& t : tracks) { + unsigned int iNonant = floor((t.iPhiSec()) * numPhiNonants / (numPhiSectors_)); // phi nonant number + nTrksPerEtaReg[t.iEtaReg()]++; + nTrksPerNonant[iNonant]++; + } + + profNumTrackCands_[tName]->Fill(1.0, tracks.size()); // Plot mean number of tracks/event. + if (algoTMTT) { + for (unsigned int iEtaReg = 0; iEtaReg < numEtaRegions_; iEtaReg++) { + profNumTracksVsEta_[tName]->Fill(iEtaReg, nTrksPerEtaReg[iEtaReg]); + } + } + for (unsigned int iNonant = 0; iNonant < numPhiNonants; iNonant++) { + hisNumTrksPerNon_[tName]->Fill(nTrksPerNonant[iNonant]); + } + + //=== Count stubs per event assigned to track candidates in the Tracker + + unsigned int nStubsOnTracks = 0; + matrix nStubsOnTracksInSec(numPhiSectors_, numEtaRegions_, 0); + vector nStubsOnTracksInNonant(numPhiNonants, 0); + + for (const L1track3D& t : tracks) { + const vector& stubs = t.stubsConst(); + unsigned int nStubs = stubs.size(); + unsigned int iNonant = floor((t.iPhiSec()) * numPhiNonants / (numPhiSectors_)); // phi nonant number + // Count stubs on all tracks in this sector & nonant. + nStubsOnTracks += nStubs; + nStubsOnTracksInNonant[iNonant] += nStubs; + } + + profStubsOnTracks_[tName]->Fill(1.0, nStubsOnTracks); + + for (unsigned int iNonant = 0; iNonant < numPhiNonants; iNonant++) { + hisStubsOnTracksPerNon_[tName]->Fill(nStubsOnTracksInNonant[iNonant]); + } + + // Plot number of tracks & number of stubs per output HT opto-link. + + if (algoTMTT && not withRZfilter) { + //const unsigned int numPhiSecPerNon = numPhiSectors_ / numPhiNonants; + // Hard-wired bodge + const unsigned int nLinks = houghNbinsPt_ / 2; // Hard-wired to number of course HT bins. Check. + + for (unsigned int iPhiNon = 0; iPhiNon < numPhiNonants; iPhiNon++) { + // Each nonant has a separate set of links. + vector stubsToLinkCount(nLinks, 0); // Must use vectors to count links with zero entries. + for (const L1track3D& trk : tracks) { + unsigned int iNonantTrk = floor((trk.iPhiSec()) * numPhiNonants / (numPhiSectors_)); // phi nonant number + if (iPhiNon == iNonantTrk) { + unsigned int link = trk.optoLinkID(); + if (link < nLinks) { + stubsToLinkCount[link] += trk.numStubs(); + } else { + std::stringstream text; + text << "\n ===== HISTOS MESS UP: Increase size of nLinks ===== " << link << "\n"; + static std::once_flag printOnce; + std::call_once( + printOnce, [](string t) { edm::LogWarning("L1track") << t; }, text.str()); + } + } + } + + for (unsigned int link = 0; link < nLinks; link++) { + unsigned int nstbs = stubsToLinkCount[link]; + hisNumStubsPerLink_[tName]->Fill(nstbs); + profMeanStubsPerLink_[tName]->Fill(link, nstbs); + } + } + } + + // Plot q/pt spectrum of track candidates, and number of stubs/tracks + for (const L1track3D& trk : tracks) { + hisNumTracksVsQoverPt_[tName]->Fill(trk.qOverPt()); // Plot reconstructed q/Pt of track cands. + hisStubsPerTrack_[tName]->Fill(trk.numStubs()); // Stubs per track. + hisLayersPerTrack_[tName]->Fill(trk.numLayers()); + } + + // Count fraction of stubs on each track matched to a TP that are from same TP. + + for (const L1track3D& trk : tracks) { + // Only consider tracks that match a tracking particle for the alg. efficiency measurement. + const TP* tp = trk.matchedTP(); + if (tp != nullptr) { + if (tp->useForAlgEff()) { + hisFracMatchStubsOnTracks_[tName]->Fill(trk.purity()); + } + } + } + + // Count total number of tracking particles in the event that were reconstructed, + // counting also how many of them were reconstructed multiple times (duplicate tracks). + + unsigned int nRecoedTPsForEff = 0; // Total no. of TPs for effi measurement recoed as >= 1 track. + unsigned int nRecoedTPs = 0; // Total no. of TPs recoed as >= 1 one track. + unsigned int nEtaSecsMatchingTPs = 0; // Total no. of eta sectors that all TPs were reconstructed in + unsigned int nSecsMatchingTPs = 0; // Total no. of eta x phi sectors that all TPs were reconstructed in + unsigned int nTrksMatchingTPs = 0; // Total no. of tracks that all TPs were reconstructed as + + for (const TP& tp : vTPs) { + vector matchedTrks; + for (const L1track3D& trk : tracks) { + const TP* tpAssoc = trk.matchedTP(); + if (tpAssoc != nullptr) { + if (tpAssoc->index() == tp.index()) + matchedTrks.push_back(&trk); + } + } + unsigned int nTrk = matchedTrks.size(); + + bool tpRecoed = false; + + if (nTrk > 0) { + tpRecoed = true; // This TP was reconstructed at least once in tracker. + nTrksMatchingTPs += nTrk; // Increment sum by no. of tracks this TP was reconstructed as + + set iEtaRegRecoed; + for (const L1track3D* trk : matchedTrks) + iEtaRegRecoed.insert(trk->iEtaReg()); + nEtaSecsMatchingTPs = iEtaRegRecoed.size(); + + set> iSecRecoed; + for (const L1track3D* trk : matchedTrks) + iSecRecoed.insert({trk->iPhiSec(), trk->iEtaReg()}); + nSecsMatchingTPs = iSecRecoed.size(); + + if (algoTMTT) { + for (const auto& p : iSecRecoed) { + unsigned int nTrkInSec = 0; + for (const L1track3D* trk : matchedTrks) { + if (trk->iPhiSec() == p.first && trk->iEtaReg() == p.second) + nTrkInSec++; + } + if (nTrkInSec > 0) { + profDupTracksVsEta_[tName]->Fill( + std::abs(tp.eta()), nTrkInSec - 1); // Study duplication of tracks within an individual HT array. + profDupTracksVsInvPt_[tName]->Fill( + std::abs(tp.qOverPt()), nTrkInSec - 1); // Study duplication of tracks within an individual HT array. + } + } + } + } + + if (tpRecoed) { + // Increment sum each time a TP is reconstructed at least once inside Tracker + if (tp.useForEff()) + nRecoedTPsForEff++; + nRecoedTPs++; + } + } + + //--- Plot mean number of tracks/event, counting number due to different kinds of duplicates + + // Plot number of TPs for the efficiency measurement that are reconstructed. + profNumTrackCands_[tName]->Fill(7.0, nRecoedTPsForEff); + // Plot number of TPs that are reconstructed. + profNumTrackCands_[tName]->Fill(6.0, nRecoedTPs); + // Plot number of TPs that are reconstructed. Count +1 for each eta sector they are reconstructed in. + profNumTrackCands_[tName]->Fill(5.0, nEtaSecsMatchingTPs); + // Plot number of TPs that are reconstructed. Count +1 for each (eta,phi) sector they are reconstructed in. + profNumTrackCands_[tName]->Fill(4.0, nSecsMatchingTPs); + // Plot number of TP that are reconstructed. Count +1 for each track they are reconstructed as. + profNumTrackCands_[tName]->Fill(2.0, nTrksMatchingTPs); + + // Histos of track helix params. + for (const L1track3D& trk : tracks) { + hisQoverPt_[tName]->Fill(trk.qOverPt()); + hisPhi0_[tName]->Fill(trk.phi0()); + hisEta_[tName]->Fill(trk.eta()); + hisZ0_[tName]->Fill(trk.z0()); + } + + // Histos of track parameter resolution + + for (const TP& tp : vTPs) { + if ((resPlotOpt_ && tp.useForAlgEff()) || + (not resPlotOpt_)) { // Check TP is good for efficiency measurement (& also comes from signal event if requested) + + // For each tracking particle, find the corresponding reconstructed track(s). + for (const L1track3D& trk : tracks) { + const TP* tpAssoc = trk.matchedTP(); + if (tpAssoc != nullptr) { + if (tpAssoc->index() == tp.index()) { + hisQoverPtRes_[tName]->Fill(trk.qOverPt() - tp.qOverPt()); + hisPhi0Res_[tName]->Fill(reco::deltaPhi(trk.phi0(), tp.phi0())); + hisEtaRes_[tName]->Fill(trk.eta() - tp.eta()); + hisZ0Res_[tName]->Fill(trk.z0() - tp.z0()); + } + } + } + } + } + + //=== Study tracking efficiency by looping over tracking particles. + + for (const TP& tp : vTPs) { + if (tp.useForEff()) { // Check TP is good for efficiency measurement. + + // Check if this TP was reconstructed anywhere in the tracker.. + bool tpRecoed = false; + bool tpRecoedPerfect = false; + for (const L1track3D& trk : tracks) { + const TP* tpAssoc = trk.matchedTP(); + if (tpAssoc != nullptr) { + if (tpAssoc->index() == tp.index()) { + tpRecoed = true; + if (trk.purity() == 1.) + tpRecoedPerfect = true; + } + } + } + + // If TP was reconstucted by HT, then plot its kinematics. + if (tpRecoed) { + hisRecoTPinvptForEff_[tName]->Fill(1. / tp.pt()); + hisRecoTPetaForEff_[tName]->Fill(tp.eta()); + hisRecoTPphiForEff_[tName]->Fill(tp.phi0()); + // Plot also production point of all good reconstructed TP. + hisRecoTPd0ForEff_[tName]->Fill(std::abs(tp.d0())); + hisRecoTPz0ForEff_[tName]->Fill(std::abs(tp.z0())); + // Also plot efficiency to perfectly reconstruct the track (no fake hits) + if (tpRecoedPerfect) { + hisPerfRecoTPinvptForEff_[tName]->Fill(1. / tp.pt()); + hisPerfRecoTPetaForEff_[tName]->Fill(tp.eta()); + } + if (tp.useForAlgEff()) { // Check TP is good for algorithmic efficiency measurement. + hisRecoTPinvptForAlgEff_[tName]->Fill(1. / tp.pt()); + hisRecoTPetaForAlgEff_[tName]->Fill(tp.eta()); + hisRecoTPphiForAlgEff_[tName]->Fill(tp.phi0()); + // Plot also production point of all good reconstructed TP. + hisRecoTPd0ForAlgEff_[tName]->Fill(std::abs(tp.d0())); + hisRecoTPz0ForAlgEff_[tName]->Fill(std::abs(tp.z0())); + + // Also plot efficiency to perfectly reconstruct the track (no fake hits) + if (tpRecoedPerfect) { + hisPerfRecoTPinvptForAlgEff_[tName]->Fill(1. / tp.pt()); + hisPerfRecoTPetaForAlgEff_[tName]->Fill(tp.eta()); + } + } + } + } + } + } + + //=== Book histograms for studying track fitting. + + map Histos::bookTrackFitting() { + map inputDirMap; + + for (const string& fitName : trackFitters_) { + // Define lambda function to facilitate adding "fitName" histogram names. + auto addn = [fitName](const string& s) { return TString::Format("%s_%s", s.c_str(), fitName.c_str()); }; + + TFileDirectory inputDir = fs_->mkdir(fitName); + inputDirMap[fitName] = inputDir; + + profNumFitTracks_[fitName] = + inputDir.make(addn("NumFitTracks"), "; class; # of fitted tracks", 11, 0.5, 11.5, -0.5, 9.9e6); + profNumFitTracks_[fitName]->GetXaxis()->SetBinLabel(7, "TP for eff fitted"); + profNumFitTracks_[fitName]->GetXaxis()->SetBinLabel(6, "TP fitted"); + profNumFitTracks_[fitName]->GetXaxis()->SetBinLabel(2, "Fit tracks that are genuine"); + profNumFitTracks_[fitName]->GetXaxis()->SetBinLabel(1, "Fit tracks including fakes"); + profNumFitTracks_[fitName]->LabelsOption("d"); + + hisNumFitTrks_[fitName] = + inputDir.make(addn("NumFitTrks"), "; No. fitted tracks in tracker;", 200, -0.5, 399.5); + hisNumFitTrksPerNon_[fitName] = + inputDir.make(addn("NumFitTrksPerNon"), "; No. fitted tracks per nonant;", 200, -0.5, 199.5); + + hisStubsPerFitTrack_[fitName] = + inputDir.make(addn("StubsPerFitTrack"), "; No. of stubs per fitted track", 20, -0.5, 19.5); + profStubsOnFitTracks_[fitName] = inputDir.make( + addn("StubsOnFitTracks"), "; ; No. of stubs on all fitted tracks per event", 1, 0.5, 1.5); + + hisFitQinvPtMatched_[fitName] = + inputDir.make(addn("FitQinvPtMatched"), "Fitted q/p_{T} for matched tracks", 120, -0.6, 0.6); + hisFitPhi0Matched_[fitName] = + inputDir.make(addn("FitPhi0Matched"), "Fitted #phi_{0} for matched tracks", 70, -3.5, 3.5); + hisFitD0Matched_[fitName] = + inputDir.make(addn("FitD0Matched"), "Fitted d_{0} for matched tracks", 100, -2., 2.); + hisFitZ0Matched_[fitName] = + inputDir.make(addn("FitZ0Matched"), "Fitted z_{0} for matched tracks", 100, -25., 25.); + hisFitEtaMatched_[fitName] = + inputDir.make(addn("FitEtaMatched"), "Fitted #eta for matched tracks", 70, -3.5, 3.5); + + hisFitQinvPtUnmatched_[fitName] = + inputDir.make(addn("FitQinvPtUnmatched"), "Fitted q/p_{T} for unmatched tracks", 120, -0.6, 0.6); + hisFitPhi0Unmatched_[fitName] = + inputDir.make(addn("FitPhi0Unmatched"), "Fitted #phi_{0} for unmatched tracks", 70, -3.5, 3.5); + hisFitD0Unmatched_[fitName] = + inputDir.make(addn("FitD0Unmatched"), "Fitted d_{0} for unmatched tracks", 100, -2., 2.); + hisFitZ0Unmatched_[fitName] = + inputDir.make(addn("FitZ0Unmatched"), "Fitted z_{0} for unmatched tracks", 100, -25., 25.); + hisFitEtaUnmatched_[fitName] = + inputDir.make(addn("FitEtaUnmatched"), "Fitted #eta for unmatched tracks", 70, -3.5, 3.5); + + const unsigned int nBinsChi2 = 39; + const float chi2dofBins[nBinsChi2 + 1] = {0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, + 2.0, 2.4, 2.8, 3.2, 3.6, 4.0, 4.5, 5.0, 6.0, 7.0, + 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, + 40.0, 50.0, 75.0, 100.0, 150.0, 200.0, 250.0, 350.0, 500.0, 1000.0}; + + hisFitChi2DofRphiMatched_[fitName] = + inputDir.make(addn("FitChi2DofRphiMatched"), ";#chi^{2}rphi;", nBinsChi2, chi2dofBins); + hisFitChi2DofRzMatched_[fitName] = + inputDir.make(addn("FitChi2DofRzMatched"), ";#chi^{2}rz/DOF;", nBinsChi2, chi2dofBins); + profFitChi2DofRphiVsInvPtMatched_[fitName] = + inputDir.make(addn("FitChi2DofRphiVsInvPtMatched"), "; 1/p_{T}; Fit #chi^{2}rphi/dof", 25, 0., 0.5); + + hisFitChi2DofRphiUnmatched_[fitName] = + inputDir.make(addn("FitChi2DofRphiUnmatched"), ";#chi^{2}rphi/DOF;", nBinsChi2, chi2dofBins); + hisFitChi2DofRzUnmatched_[fitName] = + inputDir.make(addn("FitChi2DofRzUnmatched"), ";#chi^{2}rz/DOF;", nBinsChi2, chi2dofBins); + profFitChi2DofRphiVsInvPtUnmatched_[fitName] = inputDir.make( + addn("FitChi2DofRphiVsInvPtUnmatched"), "; 1/p_{T}; Fit #chi^{2}rphi/dof", 25, 0., 0.5); + + // Monitoring specific track fit algorithms. + if (fitName.find("KF") != string::npos) { + hisKalmanNumUpdateCalls_[fitName] = + inputDir.make(addn("KalmanNumUpdateCalls"), "; Calls to KF updator;", 100, -0.5, 99.5); + + hisKalmanChi2DofSkipLay0Matched_[fitName] = inputDir.make( + addn("KalmanChi2DofSkipLay0Matched"), ";#chi^{2} for nSkippedLayers = 0;", nBinsChi2, chi2dofBins); + hisKalmanChi2DofSkipLay1Matched_[fitName] = inputDir.make( + addn("KalmanChi2DofSkipLay1Matched"), ";#chi^{2} for nSkippedLayers = 1;", nBinsChi2, chi2dofBins); + hisKalmanChi2DofSkipLay2Matched_[fitName] = inputDir.make( + addn("KalmanChi2DofSkipLay2Matched"), ";#chi^{2} for nSkippedLayers = 2;", nBinsChi2, chi2dofBins); + hisKalmanChi2DofSkipLay0Unmatched_[fitName] = inputDir.make( + addn("KalmanChi2DofSkipLay0Unmatched"), ";#chi^{2} for nSkippedLayers = 0;", nBinsChi2, chi2dofBins); + hisKalmanChi2DofSkipLay1Unmatched_[fitName] = inputDir.make( + addn("KalmanChi2DofSkipLay1Unmatched"), ";#chi^{2} for nSkippedLayers = 1;", nBinsChi2, chi2dofBins); + hisKalmanChi2DofSkipLay2Unmatched_[fitName] = inputDir.make( + addn("KalmanChi2DofSkipLay2Unmatched"), ";#chi^{2} for nSkippedLayers = 2;", nBinsChi2, chi2dofBins); + } + + // Plots of helix param resolution. + + hisQoverPtResVsTrueEta_[fitName] = inputDir.make( + addn("QoverPtResVsTrueEta"), "q/p_{T} resolution; |#eta|; q/p_{T} resolution", 30, 0.0, 3.0); + hisPhi0ResVsTrueEta_[fitName] = inputDir.make( + addn("PhiResVsTrueEta"), "#phi_{0} resolution; |#eta|; #phi_{0} resolution", 30, 0.0, 3.0); + hisEtaResVsTrueEta_[fitName] = + inputDir.make(addn("EtaResVsTrueEta"), "#eta resolution; |#eta|; #eta resolution", 30, 0.0, 3.0); + hisZ0ResVsTrueEta_[fitName] = + inputDir.make(addn("Z0ResVsTrueEta"), "z_{0} resolution; |#eta|; z_{0} resolution", 30, 0.0, 3.0); + hisD0ResVsTrueEta_[fitName] = + inputDir.make(addn("D0ResVsTrueEta"), "d_{0} resolution; |#eta|; d_{0} resolution", 30, 0.0, 3.0); + + hisQoverPtResVsTrueInvPt_[fitName] = inputDir.make( + addn("QoverPtResVsTrueInvPt"), "q/p_{T} resolution; 1/p_{T}; q/p_{T} resolution", 25, 0.0, 0.5); + hisPhi0ResVsTrueInvPt_[fitName] = inputDir.make( + addn("PhiResVsTrueInvPt"), "#phi_{0} resolution; 1/p_{T}; #phi_{0} resolution", 25, 0.0, 0.5); + hisEtaResVsTrueInvPt_[fitName] = + inputDir.make(addn("EtaResVsTrueInvPt"), "#eta resolution; 1/p_{T}; #eta resolution", 25, 0.0, 0.5); + hisZ0ResVsTrueInvPt_[fitName] = inputDir.make( + addn("Z0ResVsTrueInvPt"), "z_{0} resolution; 1/p_{T}; z_{0} resolution", 25, 0.0, 0.5); + hisD0ResVsTrueInvPt_[fitName] = inputDir.make( + addn("D0ResVsTrueInvPt"), "d_{0} resolution; 1/p_{T}; d_{0} resolution", 25, 0.0, 0.5); + + // Duplicate track histos. + profDupFitTrksVsEta_[fitName] = + inputDir.make(addn("DupFitTrksVsEta"), "; #eta; No. of duplicate tracks per TP", 12, 0., 3.); + profDupFitTrksVsInvPt_[fitName] = + inputDir.make(addn("DupFitTrksVsInvPt"), "; 1/Pt; No. of duplicate tracks per TP", 25, 0., 0.5); + + // Histos for tracking efficiency vs. TP kinematics. (Binning must match similar histos in bookTrackCands()). + hisFitTPinvptForEff_[fitName] = + inputDir.make(addn("FitTPinvptForEff"), "; TP 1/Pt of fitted tracks (for effi.);", 50, 0., 0.5); + hisFitTPetaForEff_[fitName] = + inputDir.make(addn("FitTPetaForEff"), "; TP #eta of fitted tracks (for effi.);", 20, -3., 3.); + hisFitTPphiForEff_[fitName] = + inputDir.make(addn("FitTPphiForEff"), "; TP #phi of fitted tracks (for effi.);", 20, -M_PI, M_PI); + + // Histo for efficiency to reconstruct track perfectly (no incorrect hits). (Binning must match similar histos in bookTrackCands()). + hisPerfFitTPinvptForEff_[fitName] = inputDir.make( + addn("PerfFitTPinvptForEff"), "; TP 1/Pt of fitted tracks (for perf. effi.);", 50, 0., 0.5); + hisPerfFitTPetaForEff_[fitName] = inputDir.make( + addn("PerfFitTPetaForEff"), "; TP #eta of fitted tracks (for perfect effi.);", 20, -3., 3.); + + // Histos for tracking efficiency vs. TP production point. (Binning must match similar histos in bookTrackCands()). + hisFitTPd0ForEff_[fitName] = + inputDir.make(addn("FitTPd0ForEff"), "; TP d0 of fitted tracks (for effi.);", 40, 0., 4.); + hisFitTPz0ForEff_[fitName] = + inputDir.make(addn("FitTPz0ForEff"), "; TP z0 of fitted tracks (for effi.);", 50, 0., 25.); + + // Histos for algorithmic tracking efficiency vs. TP kinematics. (Binning must match similar histos in bookTrackCands()). + hisFitTPinvptForAlgEff_[fitName] = + inputDir.make(addn("FitTPinvptForAlgEff"), "; TP 1/Pt of fitted tracks (for alg. effi.);", 50, 0., 0.5); + hisFitTPetaForAlgEff_[fitName] = + inputDir.make(addn("FitTPetaForAlgEff"), "; TP #eta of fitted tracks (for alg. effi.);", 20, -3., 3.); + hisFitTPphiForAlgEff_[fitName] = inputDir.make( + addn("FitTPphiForAlgEff"), "; TP #phi of fitted tracks (for alg. effi.);", 20, -M_PI, M_PI); + + // Histo for efficiency to reconstruct track perfectly (no incorrect hits). (Binning must match similar histos in bookTrackCands()). + hisPerfFitTPinvptForAlgEff_[fitName] = inputDir.make( + addn("PerfFitTPinvptForAlgEff"), "; TP 1/Pt of fitted tracks (for perf. alg. effi.);", 50, 0., 0.5); + hisPerfFitTPetaForAlgEff_[fitName] = + inputDir.make(addn("PerfFitTPetaForAlgEff"), "; TP #eta (for perf. alg. effi.);", 20, -3., 3.); + + // Histos for algorithmic tracking efficiency vs. TP production point. (Binning must match similar histos in bookTrackCands()). + hisFitTPd0ForAlgEff_[fitName] = + inputDir.make(addn("FitTPd0ForAlgEff"), "; TP d0 of fitted tracks (for alg. effi.);", 40, 0., 4.); + hisFitTPz0ForAlgEff_[fitName] = + inputDir.make(addn("FitTPz0ForAlgEff"), "; TP z0 of fitted tracks (for alg. effi.);", 50, 0., 25.); + } + return inputDirMap; + } + + //=== Fill histograms for studying track fitting. + + void Histos::fillTrackFitting(const InputData& inputData, + const map>& mapFinalTracks) { + // Allow only one thread to run this function at a time + static std::mutex myMutex; + std::lock_guard myGuard(myMutex); + + const list& vTPs = inputData.getTPs(); + + // Loop over all the fitting algorithms we are trying. + for (const string& fitName : trackFitters_) { + const list& fittedTracks = mapFinalTracks.at(fitName); // Get fitted tracks. + + // Count tracks + unsigned int nFitTracks = 0; + unsigned int nFitsMatchingTP = 0; + + const unsigned int numPhiNonants = settings_->numPhiNonants(); + vector nFitTracksPerNonant(numPhiNonants, 0); + + for (const L1fittedTrack* fitTrk : fittedTracks) { + nFitTracks++; + + // Get matched truth particle, if any. + const TP* tp = fitTrk->matchedTP(); + if (tp != nullptr) + nFitsMatchingTP++; + // Count fitted tracks per nonant. + unsigned int iNonant = (numPhiSectors_ > 0) ? floor(fitTrk->iPhiSec() * numPhiNonants / (numPhiSectors_)) + : 0; // phi nonant number + nFitTracksPerNonant[iNonant]++; + } + + profNumFitTracks_[fitName]->Fill(1, nFitTracks); + profNumFitTracks_[fitName]->Fill(2, nFitsMatchingTP); + + hisNumFitTrks_[fitName]->Fill(nFitTracks); + for (const unsigned int& num : nFitTracksPerNonant) { + hisNumFitTrksPerNon_[fitName]->Fill(num); + } + + // Count stubs assigned to fitted tracks. + unsigned int nTotStubs = 0; + for (const L1fittedTrack* fitTrk : fittedTracks) { + unsigned int nStubs = fitTrk->numStubs(); + hisStubsPerFitTrack_[fitName]->Fill(nStubs); + nTotStubs += nStubs; + } + profStubsOnFitTracks_[fitName]->Fill(1., nTotStubs); + + // Note truth particles that are successfully fitted. And which give rise to duplicate tracks. + + map tpRecoedMap; // Note which truth particles were successfully fitted. + map + tpPerfRecoedMap; // Note which truth particles were successfully fitted with no incorrect hits. + map tpRecoedDup; // Note that this TP gave rise to duplicate tracks. + for (const TP& tp : vTPs) { + tpRecoedMap[&tp] = false; + tpPerfRecoedMap[&tp] = false; + unsigned int nMatch = 0; + for (const L1fittedTrack* fitTrk : fittedTracks) { + const TP* assocTP = fitTrk->matchedTP(); // Get the TP the fitted track matches to, if any. + if (assocTP == &tp) { + tpRecoedMap[&tp] = true; + if (fitTrk->purity() == 1.) + tpPerfRecoedMap[&tp] = true; + nMatch++; + } + } + tpRecoedDup[&tp] = nMatch; + } + + // Count truth particles that are successfully fitted. + + unsigned int nFittedTPs = 0; + unsigned int nFittedTPsForEff = 0; + for (const TP& tp : vTPs) { + if (tpRecoedMap[&tp]) { // Was this truth particle successfully fitted? + nFittedTPs++; + if (tp.useForEff()) + nFittedTPsForEff++; + } + } + + profNumFitTracks_[fitName]->Fill(6, nFittedTPs); + profNumFitTracks_[fitName]->Fill(7, nFittedTPsForEff); + + // Loop over fitted tracks again. + + for (const L1fittedTrack* fitTrk : fittedTracks) { + // Info for specific track fit algorithms. + unsigned int nSkippedLayers = 0; + unsigned int numUpdateCalls = 0; + if (fitName.find("KF") != string::npos) { + fitTrk->infoKF(nSkippedLayers, numUpdateCalls); + hisKalmanNumUpdateCalls_[fitName]->Fill(numUpdateCalls); + } + + //--- Compare fitted tracks that match truth particles to those that don't. + + // Get matched truth particle, if any. + const TP* tp = fitTrk->matchedTP(); + + if (tp != nullptr) { + hisFitQinvPtMatched_[fitName]->Fill(fitTrk->qOverPt()); + hisFitPhi0Matched_[fitName]->Fill(fitTrk->phi0()); + hisFitD0Matched_[fitName]->Fill(fitTrk->d0()); + hisFitZ0Matched_[fitName]->Fill(fitTrk->z0()); + hisFitEtaMatched_[fitName]->Fill(fitTrk->eta()); + + // Only plot matched chi2 for tracks with no incorrect stubs. + if (fitTrk->purity() == 1.) { + hisFitChi2DofRphiMatched_[fitName]->Fill(fitTrk->chi2rphi() / fitTrk->numDOFrphi()); + hisFitChi2DofRzMatched_[fitName]->Fill(fitTrk->chi2rz() / fitTrk->numDOFrz()); + profFitChi2DofRphiVsInvPtMatched_[fitName]->Fill(std::abs(fitTrk->qOverPt()), + (fitTrk->chi2rphi() / fitTrk->numDOFrphi())); + + if (fitName.find("KF") != string::npos) { + // No. of skipped layers on track during Kalman track fit. + if (nSkippedLayers == 0) { + hisKalmanChi2DofSkipLay0Matched_[fitName]->Fill(fitTrk->chi2dof()); + } else if (nSkippedLayers == 1) { + hisKalmanChi2DofSkipLay1Matched_[fitName]->Fill(fitTrk->chi2dof()); + } else if (nSkippedLayers >= 2) { + hisKalmanChi2DofSkipLay2Matched_[fitName]->Fill(fitTrk->chi2dof()); + } + } + } + + } else { + hisFitQinvPtUnmatched_[fitName]->Fill(fitTrk->qOverPt()); + hisFitPhi0Unmatched_[fitName]->Fill(fitTrk->phi0()); + hisFitD0Unmatched_[fitName]->Fill(fitTrk->d0()); + hisFitZ0Unmatched_[fitName]->Fill(fitTrk->z0()); + hisFitEtaUnmatched_[fitName]->Fill(fitTrk->eta()); + + hisFitChi2DofRphiUnmatched_[fitName]->Fill(fitTrk->chi2rphi() / fitTrk->numDOFrphi()); + hisFitChi2DofRzUnmatched_[fitName]->Fill(fitTrk->chi2rz() / fitTrk->numDOFrz()); + profFitChi2DofRphiVsInvPtUnmatched_[fitName]->Fill(std::abs(fitTrk->qOverPt()), + (fitTrk->chi2rphi() / fitTrk->numDOFrphi())); + + if (fitName.find("KF") != string::npos) { + // No. of skipped layers on track during Kalman track fit. + if (nSkippedLayers == 0) { + hisKalmanChi2DofSkipLay0Unmatched_[fitName]->Fill(fitTrk->chi2dof()); + } else if (nSkippedLayers == 1) { + hisKalmanChi2DofSkipLay1Unmatched_[fitName]->Fill(fitTrk->chi2dof()); + } else if (nSkippedLayers >= 2) { + hisKalmanChi2DofSkipLay2Unmatched_[fitName]->Fill(fitTrk->chi2dof()); + } + } + } + } + + // Study helix param resolution. + + for (const L1fittedTrack* fitTrk : fittedTracks) { + const TP* tp = fitTrk->matchedTP(); + if (tp != nullptr) { + // IRT + if ((resPlotOpt_ && tp->useForAlgEff()) || + (not resPlotOpt_)) { // Check TP is good for efficiency measurement (& also comes from signal event if requested) + + // Plot helix parameter resolution against eta or Pt. + hisQoverPtResVsTrueEta_[fitName]->Fill(std::abs(tp->eta()), std::abs(fitTrk->qOverPt() - tp->qOverPt())); + hisPhi0ResVsTrueEta_[fitName]->Fill(std::abs(tp->eta()), + std::abs(reco::deltaPhi(fitTrk->phi0(), tp->phi0()))); + hisEtaResVsTrueEta_[fitName]->Fill(std::abs(tp->eta()), std::abs(fitTrk->eta() - tp->eta())); + hisZ0ResVsTrueEta_[fitName]->Fill(std::abs(tp->eta()), std::abs(fitTrk->z0() - tp->z0())); + hisD0ResVsTrueEta_[fitName]->Fill(std::abs(tp->eta()), std::abs(fitTrk->d0() - tp->d0())); + + hisQoverPtResVsTrueInvPt_[fitName]->Fill(std::abs(tp->qOverPt()), + std::abs(fitTrk->qOverPt() - tp->qOverPt())); + hisPhi0ResVsTrueInvPt_[fitName]->Fill(std::abs(tp->qOverPt()), + std::abs(reco::deltaPhi(fitTrk->phi0(), tp->phi0()))); + hisEtaResVsTrueInvPt_[fitName]->Fill(std::abs(tp->qOverPt()), std::abs(fitTrk->eta() - tp->eta())); + hisZ0ResVsTrueInvPt_[fitName]->Fill(std::abs(tp->qOverPt()), std::abs(fitTrk->z0() - tp->z0())); + hisD0ResVsTrueInvPt_[fitName]->Fill(std::abs(tp->qOverPt()), std::abs(fitTrk->d0() - tp->d0())); + } + } + } + + //=== Study duplicate tracks. + + for (const TP& tp : vTPs) { + if (tpRecoedMap[&tp]) { // Was this truth particle successfully fitted? + profDupFitTrksVsEta_[fitName]->Fill(std::abs(tp.eta()), tpRecoedDup[&tp] - 1); + profDupFitTrksVsInvPt_[fitName]->Fill(std::abs(tp.qOverPt()), tpRecoedDup[&tp] - 1); + } + } + + //=== Study tracking efficiency by looping over tracking particles. + + for (const TP& tp : vTPs) { + if (tp.useForEff()) { // Check TP is good for efficiency measurement. + + // If TP was reconstucted by HT, then plot its kinematics. + if (tpRecoedMap[&tp]) { // This truth particle was successfully fitted. + hisFitTPinvptForEff_[fitName]->Fill(1. / tp.pt()); + hisFitTPetaForEff_[fitName]->Fill(tp.eta()); + hisFitTPphiForEff_[fitName]->Fill(tp.phi0()); + // Plot also production point of all good reconstructed TP. + hisFitTPd0ForEff_[fitName]->Fill(std::abs(tp.d0())); + hisFitTPz0ForEff_[fitName]->Fill(std::abs(tp.z0())); + // Also plot efficiency to perfectly reconstruct the track (no fake hits) + if (tpPerfRecoedMap[&tp]) { // This truth particle was successfully fitted with no incorrect hits. + hisPerfFitTPinvptForEff_[fitName]->Fill(1. / tp.pt()); + hisPerfFitTPetaForEff_[fitName]->Fill(tp.eta()); + } + if (tp.useForAlgEff()) { // Check TP is good for algorithmic efficiency measurement. + hisFitTPinvptForAlgEff_[fitName]->Fill(1. / tp.pt()); + hisFitTPetaForAlgEff_[fitName]->Fill(tp.eta()); + hisFitTPphiForAlgEff_[fitName]->Fill(tp.phi0()); + // Plot also production point of all good reconstructed TP. + hisFitTPd0ForAlgEff_[fitName]->Fill(std::abs(tp.d0())); + hisFitTPz0ForAlgEff_[fitName]->Fill(std::abs(tp.z0())); + // Also plot efficiency to perfectly reconstruct the track (no fake hits) + if (tpPerfRecoedMap[&tp]) { + hisPerfFitTPinvptForAlgEff_[fitName]->Fill(1. / tp.pt()); + hisPerfFitTPetaForAlgEff_[fitName]->Fill(tp.eta()); + } + } + } + } + } + } + } + + //=== Produce plots of tracking efficiency after HT or after r-z track filter (run at end of job). + + TFileDirectory Histos::plotTrackEfficiency(const string& tName) { + // Define lambda function to facilitate adding "tName" to directory & histogram names. + auto addn = [tName](const string& s) { return TString::Format("%s_%s", s.c_str(), tName.c_str()); }; + + TFileDirectory inputDir = fs_->mkdir(addn("Effi").Data()); + // Plot tracking efficiency + makeEfficiencyPlot(inputDir, + teffEffVsInvPt_[tName], + hisRecoTPinvptForEff_[tName], + hisTPinvptForEff_, + addn("EffVsInvPt"), + "; 1/Pt; Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffEffVsEta_[tName], + hisRecoTPetaForEff_[tName], + hisTPetaForEff_, + addn("EffVsEta"), + "; #eta; Tracking efficiency"); + + makeEfficiencyPlot(inputDir, + teffEffVsPhi_[tName], + hisRecoTPphiForEff_[tName], + hisTPphiForEff_, + addn("EffVsPhi"), + "; #phi; Tracking efficiency"); + + makeEfficiencyPlot(inputDir, + teffEffVsD0_[tName], + hisRecoTPd0ForEff_[tName], + hisTPd0ForEff_, + addn("EffVsD0"), + "; d0 (cm); Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffEffVsZ0_[tName], + hisRecoTPz0ForEff_[tName], + hisTPz0ForEff_, + addn("EffVsZ0"), + "; z0 (cm); Tracking efficiency"); + + // Also plot efficiency to reconstruct track perfectly. + makeEfficiencyPlot(inputDir, + teffPerfEffVsInvPt_[tName], + hisPerfRecoTPinvptForEff_[tName], + hisTPinvptForEff_, + addn("PerfEffVsInvPt"), + "; 1/Pt; Tracking perfect efficiency"); + makeEfficiencyPlot(inputDir, + teffPerfEffVsEta_[tName], + hisPerfRecoTPetaForEff_[tName], + hisTPetaForEff_, + addn("PerfEffVsEta"), + "; #eta; Tracking perfect efficiency"); + + // Plot algorithmic tracking efficiency + makeEfficiencyPlot(inputDir, + teffAlgEffVsInvPt_[tName], + hisRecoTPinvptForAlgEff_[tName], + hisTPinvptForAlgEff_, + addn("AlgEffVsInvPt"), + "; 1/Pt; Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffAlgEffVsEta_[tName], + hisRecoTPetaForAlgEff_[tName], + hisTPetaForAlgEff_, + addn("AlgEffVsEta"), + "; #eta; Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffAlgEffVsPhi_[tName], + hisRecoTPphiForAlgEff_[tName], + hisTPphiForAlgEff_, + addn("AlgEffVsPhi"), + "; #phi; Tracking efficiency"); + + makeEfficiencyPlot(inputDir, + teffAlgEffVsD0_[tName], + hisRecoTPd0ForAlgEff_[tName], + hisTPd0ForAlgEff_, + addn("AlgEffVsD0"), + "; d0 (cm); Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffAlgEffVsZ0_[tName], + hisRecoTPz0ForAlgEff_[tName], + hisTPz0ForAlgEff_, + addn("AlgEffVsZ0"), + "; z0 (cm); Tracking efficiency"); + + // Also plot algorithmic efficiency to reconstruct track perfectly. + makeEfficiencyPlot(inputDir, + teffPerfAlgEffVsInvPt_[tName], + hisPerfRecoTPinvptForAlgEff_[tName], + hisTPinvptForAlgEff_, + addn("PerfAlgEffVsInvPt"), + "; 1/Pt; Tracking perfect efficiency"); + makeEfficiencyPlot(inputDir, + teffPerfAlgEffVsEta_[tName], + hisPerfRecoTPetaForAlgEff_[tName], + hisTPetaForAlgEff_, + addn("PerfAlgEffVsEta"), + "; #eta; Tracking perfect efficiency"); + + return inputDir; + } + + //=== Produce plots of tracking efficiency after track fit (run at end of job). + + TFileDirectory Histos::plotTrackEffAfterFit(const string& fitName) { + // Define lambda function to facilitate adding "fitName" to directory & histogram names. + auto addn = [fitName](const string& s) { return TString::Format("%s_%s", s.c_str(), fitName.c_str()); }; + + TFileDirectory inputDir = fs_->mkdir(addn("Effi").Data()); + // Plot tracking efficiency + makeEfficiencyPlot(inputDir, + teffEffFitVsInvPt_[fitName], + hisFitTPinvptForEff_[fitName], + hisTPinvptForEff_, + addn("EffFitVsInvPt"), + "; 1/Pt; Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffEffFitVsEta_[fitName], + hisFitTPetaForEff_[fitName], + hisTPetaForEff_, + addn("EffFitVsEta"), + "; #eta; Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffEffFitVsPhi_[fitName], + hisFitTPphiForEff_[fitName], + hisTPphiForEff_, + addn("EffFitVsPhi"), + "; #phi; Tracking efficiency"); + + makeEfficiencyPlot(inputDir, + teffEffFitVsD0_[fitName], + hisFitTPd0ForEff_[fitName], + hisTPd0ForEff_, + addn("EffFitVsD0"), + "; d0 (cm); Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffEffFitVsZ0_[fitName], + hisFitTPz0ForEff_[fitName], + hisTPz0ForEff_, + addn("EffFitVsZ0"), + "; z0 (cm); Tracking efficiency"); + + // Also plot efficiency to reconstruct track perfectly. + makeEfficiencyPlot(inputDir, + teffPerfEffFitVsInvPt_[fitName], + hisPerfFitTPinvptForEff_[fitName], + hisTPinvptForEff_, + addn("PerfEffFitVsInvPt"), + "; 1/Pt; Tracking perfect efficiency"); + makeEfficiencyPlot(inputDir, + teffPerfEffFitVsEta_[fitName], + hisPerfFitTPetaForEff_[fitName], + hisTPetaForEff_, + addn("PerfEffFitVsEta"), + "; #eta; Tracking perfect efficiency"); + + // Plot algorithmic tracking efficiency + makeEfficiencyPlot(inputDir, + teffAlgEffFitVsInvPt_[fitName], + hisFitTPinvptForAlgEff_[fitName], + hisTPinvptForAlgEff_, + addn("AlgEffFitVsInvPt"), + "; 1/Pt; Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffAlgEffFitVsEta_[fitName], + hisFitTPetaForAlgEff_[fitName], + hisTPetaForAlgEff_, + addn("AlgEffFitVsEta"), + "; #eta; Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffAlgEffFitVsPhi_[fitName], + hisFitTPphiForAlgEff_[fitName], + hisTPphiForAlgEff_, + addn("AlgEffFitVsPhi"), + "; #phi; Tracking efficiency"); + + makeEfficiencyPlot(inputDir, + teffAlgEffFitVsD0_[fitName], + hisFitTPd0ForAlgEff_[fitName], + hisTPd0ForAlgEff_, + addn("AlgEffFitVsD0"), + "; d0 (cm); Tracking efficiency"); + makeEfficiencyPlot(inputDir, + teffAlgEffFitVsZ0_[fitName], + hisFitTPz0ForAlgEff_[fitName], + hisTPz0ForAlgEff_, + addn("AlgEffFitVsZ0"), + "; z0 (cm); Tracking efficiency"); + + // Also plot algorithmic efficiency to reconstruct track perfectly. + makeEfficiencyPlot(inputDir, + teffPerfAlgEffFitVsInvPt_[fitName], + hisPerfFitTPinvptForAlgEff_[fitName], + hisTPinvptForAlgEff_, + addn("PerfAlgEffFitVsInvPt"), + "; 1/Pt; Tracking perfect efficiency"); + makeEfficiencyPlot(inputDir, + teffPerfAlgEffFitVsEta_[fitName], + hisPerfFitTPetaForAlgEff_[fitName], + hisTPetaForAlgEff_, + addn("Perf AlgEffFitVsEta"), + "; #eta; Tracking perfect efficiency"); + return inputDir; + } + + void Histos::makeEfficiencyPlot( + TFileDirectory& inputDir, TEfficiency* outputEfficiency, TH1F* pass, TH1F* all, TString name, TString title) { + outputEfficiency = inputDir.make(*pass, *all); + outputEfficiency->SetName(name); + outputEfficiency->SetTitle(title); + } + + //=== Print summary of track-finding performance after track pattern reco. + + void Histos::printTrackPerformance(const string& tName) { + float numTrackCands = profNumTrackCands_[tName]->GetBinContent(1); // No. of track cands + float numTrackCandsErr = profNumTrackCands_[tName]->GetBinError(1); // No. of track cands uncertainty + float numMatchedTrackCandsIncDups = + profNumTrackCands_[tName]->GetBinContent(2); // Ditto, counting only those matched to TP + float numMatchedTrackCandsExcDups = profNumTrackCands_[tName]->GetBinContent(6); // Ditto, but excluding duplicates + float numFakeTracks = numTrackCands - numMatchedTrackCandsIncDups; + float numExtraDupTracks = numMatchedTrackCandsIncDups - numMatchedTrackCandsExcDups; + float fracFake = numFakeTracks / (numTrackCands + 1.0e-6); + float fracDup = numExtraDupTracks / (numTrackCands + 1.0e-6); + + float numStubsOnTracks = profStubsOnTracks_[tName]->GetBinContent(1); + float meanStubsPerTrack = + numStubsOnTracks / (numTrackCands + 1.0e-6); //protection against demoninator equals zero. + unsigned int numRecoTPforAlg = hisRecoTPinvptForAlgEff_[tName]->GetEntries(); + // Histograms of input truth particles (e.g. hisTPinvptForAlgEff_), used for denominator of efficiencies, are identical, + // irrespective of whether made after HT or after r-z track filter, so always use the former. + unsigned int numTPforAlg = hisTPinvptForAlgEff_->GetEntries(); + unsigned int numPerfRecoTPforAlg = hisPerfRecoTPinvptForAlgEff_[tName]->GetEntries(); + float algEff = float(numRecoTPforAlg) / (numTPforAlg + 1.0e-6); //protection against demoninator equals zero. + float algEffErr = sqrt(algEff * (1 - algEff) / (numTPforAlg + 1.0e-6)); // uncertainty + float algPerfEff = + float(numPerfRecoTPforAlg) / (numTPforAlg + 1.0e-6); //protection against demoninator equals zero. + float algPerfEffErr = sqrt(algPerfEff * (1 - algPerfEff) / (numTPforAlg + 1.0e-6)); // uncertainty + + PrintL1trk() << "========================================================================="; + if (tName == "HT") { + PrintL1trk() << " TRACK-FINDING SUMMARY AFTER HOUGH TRANSFORM "; + } else if (tName == "RZ") { + PrintL1trk() << " TRACK-FINDING SUMMARY AFTER R-Z TRACK FILTER "; + } else if (tName == "TRACKLET") { + PrintL1trk() << " TRACK-FINDING SUMMARY AFTER TRACKLET PATTERN RECO "; + } + PrintL1trk() << "Number of track candidates found per event = " << numTrackCands << " +- " << numTrackCandsErr; + PrintL1trk() << " with mean stubs/track = " << meanStubsPerTrack; + PrintL1trk() << "Fraction of track cands that are fake = " << fracFake; + PrintL1trk() << "Fraction of track cands that are genuine, but extra duplicates = " << fracDup; + + PrintL1trk() << std::fixed << std::setprecision(4) << "Algorithmic tracking efficiency = " << numRecoTPforAlg << "/" + << numTPforAlg << " = " << algEff << " +- " << algEffErr; + PrintL1trk() << "Perfect algorithmic tracking efficiency = " << numPerfRecoTPforAlg << "/" << numTPforAlg << " = " + << algPerfEff << " +- " << algPerfEffErr << " (no incorrect hits)"; + } + + //=== Print summary of track-finding performance after helix fit for given track fitter. + + void Histos::printFitTrackPerformance(const string& fitName) { + float numFitTracks = profNumFitTracks_[fitName]->GetBinContent(1); // No. of track cands + float numFitTracksErr = profNumFitTracks_[fitName]->GetBinError(1); // No. of track cands uncertainty + float numMatchedFitTracksIncDups = + profNumFitTracks_[fitName]->GetBinContent(2); // Ditto, counting only those matched to TP + float numMatchedFitTracksExcDups = profNumFitTracks_[fitName]->GetBinContent(6); // Ditto, but excluding duplicates + float numFakeFitTracks = numFitTracks - numMatchedFitTracksIncDups; + float numExtraDupFitTracks = numMatchedFitTracksIncDups - numMatchedFitTracksExcDups; + float fracFakeFit = numFakeFitTracks / (numFitTracks + 1.0e-6); + float fracDupFit = numExtraDupFitTracks / (numFitTracks + 1.0e-6); + + float numStubsOnFitTracks = profStubsOnFitTracks_[fitName]->GetBinContent(1); + float meanStubsPerFitTrack = + numStubsOnFitTracks / (numFitTracks + 1.0e-6); //protection against demoninator equals zero. + unsigned int numFitTPforAlg = hisFitTPinvptForAlgEff_[fitName]->GetEntries(); + // Histograms of input truth particles (e.g. hisTPinvptForAlgEff_), used for denominator of efficiencies, are identical, + // irrespective of whether made after HT or after r-z track filter, so always use the former. + unsigned int numTPforAlg = hisTPinvptForAlgEff_->GetEntries(); + unsigned int numPerfFitTPforAlg = hisPerfFitTPinvptForAlgEff_[fitName]->GetEntries(); + float fitEff = float(numFitTPforAlg) / (numTPforAlg + 1.0e-6); //protection against demoninator equals zero. + float fitEffErr = sqrt(fitEff * (1 - fitEff) / (numTPforAlg + 1.0e-6)); // uncertainty + float fitPerfEff = + float(numPerfFitTPforAlg) / (numTPforAlg + 1.0e-6); //protection against demoninator equals zero. + float fitPerfEffErr = sqrt(fitPerfEff * (1 - fitPerfEff) / (numTPforAlg + 1.0e-6)); // uncertainty + + // Does this fitter require r-z track filter to be run before it? + bool useRZfilt = (std::count(useRZfilter_.begin(), useRZfilter_.end(), fitName) > 0); + + PrintL1trk() << "========================================================================="; + PrintL1trk() << " TRACK FIT SUMMARY FOR: " << fitName; + PrintL1trk() << "Number of fitted track candidates found per event = " << numFitTracks << " +- " << numFitTracksErr; + PrintL1trk() << " with mean stubs/track = " << meanStubsPerFitTrack; + PrintL1trk() << "Fraction of fitted tracks that are fake = " << fracFakeFit; + PrintL1trk() << "Fraction of fitted tracks that are genuine, but extra duplicates = " << fracDupFit; + PrintL1trk() << "Algorithmic fitting efficiency = " << numFitTPforAlg << "/" << numTPforAlg << " = " << fitEff + << " +- " << fitEffErr; + PrintL1trk() << "Perfect algorithmic fitting efficiency = " << numPerfFitTPforAlg << "/" << numTPforAlg << " = " + << fitPerfEff << " +- " << fitPerfEffErr << " (no incorrect hits)"; + if (useRZfilt) + PrintL1trk() << "(The above fitter used the '" << settings_->rzFilterName() << "' r-z track filter.)"; + } + + //=== Print tracking performance summary & make tracking efficiency histograms. + + void Histos::endJobAnalysis(const HTrphi::ErrorMonitor* htRphiErrMon) { + // Don't bother producing summary if user didn't request histograms via TFileService in their cfg. + if (not this->enabled()) + return; + + // Protection when running in wierd mixed hybrid-TMTT modes. + bool wierdMixedMode = (hisRecoTPinvptForEff_.find("TRACKLET") == hisRecoTPinvptForEff_.end()); + + if (settings_->hybrid() && not wierdMixedMode) { + // Produce plots of tracking efficieny after tracklet pattern reco. + this->plotTrackletSeedEfficiency(); + this->plotTrackEfficiency("TRACKLET"); + this->plotHybridDupRemovalEfficiency(); + + } else { + // Produce plots of tracking efficiency using track candidates found after HT. + this->plotTrackEfficiency("HT"); + + // Optionally produce plots of tracking efficiency using track candidates found after r-z track filter. + if (ranRZfilter_) + this->plotTrackEfficiency("RZ"); + } + + // Produce more plots of tracking efficiency using track candidates after track fit. + for (auto& fitName : trackFitters_) { + this->plotTrackEffAfterFit(fitName); + } + + PrintL1trk() << "========================================================================="; + + // Print r (z) range in which each barrel layer (endcap wheel) appears. + // (Needed by firmware). + PrintL1trk(); + PrintL1trk() << "--- r range in which stubs in each barrel layer appear ---"; + for (const auto& p : mapBarrelLayerMinR_) { + unsigned int layer = p.first; + PrintL1trk() << " layer = " << layer << " : " << mapBarrelLayerMinR_[layer] << " < r < " + << mapBarrelLayerMaxR_[layer]; + } + PrintL1trk() << "--- |z| range in which stubs in each endcap wheel appear ---"; + for (const auto& p : mapEndcapWheelMinZ_) { + unsigned int layer = p.first; + PrintL1trk() << " wheel = " << layer << " : " << mapEndcapWheelMinZ_[layer] << " < |z| < " + << mapEndcapWheelMaxZ_[layer]; + } + + // Print (r,|z|) range in which each module type (defined in DigitalStub) appears. + // (Needed by firmware). + PrintL1trk(); + PrintL1trk() << "--- (r,|z|) range in which each module type (defined in DigitalStub) appears ---"; + for (const auto& p : mapModuleTypeMinR_) { + unsigned int modType = p.first; + PrintL1trk() << " Module type = " << modType << setprecision(1) << " : r range = (" + << mapModuleTypeMinR_[modType] << "," << mapModuleTypeMaxR_[modType] << "); z range = (" + << mapModuleTypeMinZ_[modType] << "," << mapModuleTypeMaxZ_[modType] << ")"; + } + // Ugly bodge to allow for modules in barrel layers 1-2 & endcap wheels 3-5 being different. + PrintL1trk() << "and in addition"; + for (const auto& p : mapExtraAModuleTypeMinR_) { + unsigned int modType = p.first; + PrintL1trk() << " Module type = " << modType << setprecision(1) << " : r range = (" + << mapExtraAModuleTypeMinR_[modType] << "," << mapExtraAModuleTypeMaxR_[modType] << "); z range = (" + << mapExtraAModuleTypeMinZ_[modType] << "," << mapExtraAModuleTypeMaxZ_[modType] << ")"; + } + PrintL1trk() << "and in addition"; + for (const auto& p : mapExtraBModuleTypeMinR_) { + unsigned int modType = p.first; + PrintL1trk() << " Module type = " << modType << setprecision(1) << " : r range = (" + << mapExtraBModuleTypeMinR_[modType] << "," << mapExtraBModuleTypeMaxR_[modType] << "); z range = (" + << mapExtraBModuleTypeMinZ_[modType] << "," << mapExtraBModuleTypeMaxZ_[modType] << ")"; + } + PrintL1trk() << "and in addition"; + for (const auto& p : mapExtraCModuleTypeMinR_) { + unsigned int modType = p.first; + PrintL1trk() << " Module type = " << modType << setprecision(1) << " : r range = (" + << mapExtraCModuleTypeMinR_[modType] << "," << mapExtraCModuleTypeMaxR_[modType] << "); z range = (" + << mapExtraCModuleTypeMinZ_[modType] << "," << mapExtraCModuleTypeMaxZ_[modType] << ")"; + } + PrintL1trk() << "and in addition"; + for (const auto& p : mapExtraDModuleTypeMinR_) { + unsigned int modType = p.first; + PrintL1trk() << " Module type = " << modType << setprecision(1) << " : r range = (" + << mapExtraDModuleTypeMinR_[modType] << "," << mapExtraDModuleTypeMaxR_[modType] << "); z range = (" + << mapExtraDModuleTypeMinZ_[modType] << "," << mapExtraDModuleTypeMaxZ_[modType] << ")"; + } + PrintL1trk(); + + if (settings_->hybrid() && not wierdMixedMode) { + //--- Print summary of tracklet pattern reco + this->printTrackletSeedFindingPerformance(); + this->printTrackPerformance("TRACKLET"); + this->printHybridDupRemovalPerformance(); + } else { + //--- Print summary of track-finding performance after HT + this->printTrackPerformance("HT"); + //--- Optionally print summary of track-finding performance after r-z track filter. + if (ranRZfilter_) + this->printTrackPerformance("RZ"); + } + + //--- Print summary of track-finding performance after helix fit, for each track fitting algorithm used. + for (const string& fitName : trackFitters_) { + this->printFitTrackPerformance(fitName); + } + PrintL1trk() << "========================================================================="; + + if (htRphiErrMon != nullptr && not settings_->hybrid()) { + // Check that stub filling was consistent with known limitations of HT firmware design. + + PrintL1trk() << "Max. |gradients| of stub lines in HT array is: r-phi = " << htRphiErrMon->maxLineGradient; + + if (htRphiErrMon->maxLineGradient > 1.) { + PrintL1trk() + << "WARNING: Line |gradient| exceeds 1, which firmware will not be able to cope with! Please adjust HT " + "array size to avoid this."; + + } else if (htRphiErrMon->numErrorsTypeA > 0.) { + float frac = float(htRphiErrMon->numErrorsTypeA) / float(htRphiErrMon->numErrorsNorm); + PrintL1trk() + << "WARNING: Despite line gradients being less than one, some fraction of HT columns have filled cells " + "with no filled neighbours in W, SW or NW direction. Firmware will object to this! "; + PrintL1trk() << "This fraction = " << frac << " for r-phi HT"; + + } else if (htRphiErrMon->numErrorsTypeB > 0.) { + float frac = float(htRphiErrMon->numErrorsTypeB) / float(htRphiErrMon->numErrorsNorm); + PrintL1trk() + << "WARNING: Despite line gradients being less than one, some fraction of HT columns recorded individual " + "stubs being added to more than two cells! Thomas firmware will object to this! "; + PrintL1trk() << "This fraction = " << frac << " for r-phi HT"; + } + } + + // Check if GP B approximation cfg params are inconsistent. + if (bApproxMistake_) + PrintL1trk() << "\n WARNING: BApprox cfg params are inconsistent - see printout above."; + + // Restore original ROOT default cfg. + TH1::SetDefaultSumw2(oldSumW2opt_); + } + + //=== Determine "B" parameter, used in GP firmware to allow for tilted modules. + + void Histos::trackerGeometryAnalysis(const list& listTrackerModule) { + // Don't bother producing summary if user didn't request histograms via TFileService in their cfg. + if (not this->enabled()) + return; + + map dataForGraph; + for (const TrackerModule& trackerModule : listTrackerModule) { + if (trackerModule.tiltedBarrel()) { + float paramB = trackerModule.paramB(); + float zOverR = std::abs(trackerModule.minZ()) / trackerModule.minR(); + dataForGraph[paramB] = zOverR; // Only store each unique paramB once, to get better histo weights. + } + } + + const int nEntries = dataForGraph.size(); + vector vecParamB(nEntries); + vector vecZoverR(nEntries); + unsigned int i = 0; + for (const auto& p : dataForGraph) { + vecParamB[i] = p.first; + vecZoverR[i] = p.second; + i++; + } + + PrintL1trk() << "========================================================================="; + PrintL1trk() << "--- Fit to cfg params for FPGA-friendly approximation to B parameter in GP & KF ---"; + PrintL1trk() << "--- (used to allowed for tilted barrel modules) ---"; + + TFileDirectory inputDir = fs_->mkdir("InputDataB"); + graphBVsZoverR_ = inputDir.make(nEntries, &vecZoverR[0], &vecParamB[0]); + graphBVsZoverR_->SetNameTitle("B vs module Z/R", "; Module Z/R; B"); + graphBVsZoverR_->Fit("pol1", "q"); + TF1* fittedFunction = graphBVsZoverR_->GetFunction("pol1"); + double gradient = fittedFunction->GetParameter(1); + double intercept = fittedFunction->GetParameter(0); + double gradient_err = fittedFunction->GetParError(1); + double intercept_err = fittedFunction->GetParError(0); + PrintL1trk() << " BApprox_gradient (fitted) = " << gradient << " +- " << gradient_err; + PrintL1trk() << " BApprox_intercept (fitted) = " << intercept << " +- " << intercept_err; + PrintL1trk() << "========================================================================="; + + // Check fitted params consistent with those assumed in cfg file. + if (settings_->useApproxB()) { + double gradientDiff = std::abs(gradient - settings_->bApprox_gradient()); + double interceptDiff = std::abs(intercept - settings_->bApprox_intercept()); + constexpr unsigned int nSigma = 5; + if (gradientDiff > nSigma * gradient_err || + interceptDiff > nSigma * intercept_err) { // Uncertainty independent of number of events + PrintL1trk() << "\n WARNING: fitted parameters inconsistent with those specified in cfg file:"; + PrintL1trk() << " BApprox_gradient (cfg) = " << settings_->bApprox_gradient(); + PrintL1trk() << " BApprox_intercept (cfg) = " << settings_->bApprox_intercept(); + bApproxMistake_ = true; // Note that problem has occurred. + } + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/InputData.cc b/L1Trigger/TrackFindingTMTT/src/InputData.cc new file mode 100644 index 0000000000000..4d16a2651cfe5 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/InputData.cc @@ -0,0 +1,155 @@ +#include "FWCore/Framework/interface/ESHandle.h" +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Utilities/interface/InputTag.h" +#include "FWCore/Framework/interface/EDAnalyzer.h" +#include "SimDataFormats/Track/interface/SimTrack.h" +#include "SimDataFormats/EncodedEventId/interface/EncodedEventId.h" +#include "SimDataFormats/TrackingAnalysis/interface/TrackingParticle.h" +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "Geometry/Records/interface/TrackerDigiGeometryRecord.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "DataFormats/JetReco/interface/GenJetCollection.h" +#include "DataFormats/JetReco/interface/GenJet.h" + +#include "L1Trigger/TrackFindingTMTT/interface/InputData.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubKiller.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h" +#include "L1Trigger/TrackFindingTMTT/interface/DegradeBend.h" + +#include +#include + +using namespace std; + +namespace tmtt { + + InputData::InputData(const edm::Event& iEvent, + const edm::EventSetup& iSetup, + const Settings* settings, + StubWindowSuggest* stubWindowSuggest, + const DegradeBend* degradeBend, + const TrackerGeometry* trackerGeometry, + const TrackerTopology* trackerTopology, + const list& listTrackerModule, + const edm::EDGetTokenT tpToken, + const edm::EDGetTokenT stubToken, + const edm::EDGetTokenT stubTruthToken, + const edm::EDGetTokenT clusterTruthToken, + const edm::EDGetTokenT genJetToken) + : // Note if job will use MC truth info (or skip it to save CPU). + enableMCtruth_(settings->enableMCtruth()) { + edm::Handle tpHandle; + edm::Handle ttStubHandle; + edm::Handle mcTruthTTStubHandle; + edm::Handle mcTruthTTClusterHandle; + edm::Handle genJetHandle; + iEvent.getByToken(stubToken, ttStubHandle); + if (enableMCtruth_) { + iEvent.getByToken(tpToken, tpHandle); + iEvent.getByToken(stubTruthToken, mcTruthTTStubHandle); + iEvent.getByToken(clusterTruthToken, mcTruthTTClusterHandle); + iEvent.getByToken(genJetToken, genJetHandle); + } + + // Get TrackingParticle info + + if (enableMCtruth_) { + unsigned int tpCount = 0; + for (unsigned int i = 0; i < tpHandle->size(); i++) { + const TrackingParticle& tPart = tpHandle->at(i); + // Creating Ptr uses CPU, so apply Pt cut here, copied from TP::fillUse(), to avoid doing it too often. + constexpr float ptMinScale = 0.7; + const float ptMin = min(settings->genMinPt(), ptMinScale * settings->houghMinPt()); + if (tPart.pt() > ptMin) { + TrackingParticlePtr tpPtr(tpHandle, i); + // Store the TrackingParticle info, using class TP to provide easy access to the most useful info. + TP tp(tpPtr, tpCount, settings); + // Only bother storing tp if it could be useful for tracking efficiency or fake rate measurements. + if (tp.use()) { + if (genJetHandle.isValid()) { + tp.fillNearestJetInfo(genJetHandle.product()); + } + + vTPs_.push_back(tp); + tpCount++; + } + } + } + } + + // Also create map relating edm::Ptr to TP. + + map, const TP*> translateTP; + + if (enableMCtruth_) { + for (const TP& tp : vTPs_) { + const TrackingParticlePtr& tpPtr = tp.trackingParticlePtr(); + translateTP[tpPtr] = &tp; + } + } + + // Initialize code for killing some stubs to model detector problems. + const StubKiller::KillOptions killOpt = static_cast(settings->killScenario()); + std::unique_ptr stubKiller; + if (killOpt != StubKiller::KillOptions::none) { + stubKiller = std::make_unique(killOpt, trackerTopology, trackerGeometry, iEvent); + } + + // Loop over tracker modules to get module info & stubs. + + for (const TrackerModule& trackerModule : listTrackerModule) { + const DetId& stackedDetId = trackerModule.stackedDetId(); + TTStubDetSetVec::const_iterator p_module = ttStubHandle->find(stackedDetId); + if (p_module != ttStubHandle->end()) { + for (TTStubDetSet::const_iterator p_ttstub = p_module->begin(); p_ttstub != p_module->end(); p_ttstub++) { + TTStubRef ttStubRef = edmNew::makeRefTo(ttStubHandle, p_ttstub); + const unsigned int stubIndex = vAllStubs_.size(); + + // Store the Stub info, using class Stub to provide easy access to the most useful info. + vAllStubs_.emplace_back( + ttStubRef, stubIndex, settings, trackerTopology, &trackerModule, degradeBend, stubKiller.get()); + + // Also fill truth associating stubs to tracking particles. + if (enableMCtruth_) { + Stub& stub = vAllStubs_.back(); + stub.fillTruth(translateTP, mcTruthTTStubHandle, mcTruthTTClusterHandle); + } + } + } + } + + // Produced reduced list containing only the subset of stubs that the user has declared will be + // output by the front-end readout electronics. + for (Stub& s : vAllStubs_) { + if (s.frontendPass()) { + vStubs_.push_back(&s); + vStubsConst_.push_back(&s); + } + } + // Optionally sort stubs according to bend, so highest Pt ones are sent from DTC to GP first. + if (settings->orderStubsByBend()) { + auto orderStubsByBend = [](const Stub* a, const Stub* b) { return (std::abs(a->bend()) < std::abs(b->bend())); }; + vStubs_.sort(orderStubsByBend); + } + + // Note list of stubs produced by each tracking particle. + // (By passing vAllStubs_ here instead of vStubs_, it means that any algorithmic efficiencies + // measured will be reduced if the tightened frontend electronics cuts, specified in section StubCuts + // of Analyze_Defaults_cfi.py, are not 100% efficient). + if (enableMCtruth_) { + for (TP& tp : vTPs_) { + tp.fillTruth(vAllStubs_); + } + } + + // If requested, recommend better FE stub window cuts. + if (settings->printStubWindows()) { + for (const Stub& s : vAllStubs_) { + stubWindowSuggest->process(trackerTopology, &s); + } + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/KFParamsComb.cc b/L1Trigger/TrackFindingTMTT/src/KFParamsComb.cc new file mode 100644 index 0000000000000..119f6969c8eb8 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/KFParamsComb.cc @@ -0,0 +1,285 @@ +///=== This is the Kalman Combinatorial Filter for 4 helix parameters track fit algorithm. + +#include "L1Trigger/TrackFindingTMTT/interface/KFParamsComb.h" +#include "L1Trigger/TrackFindingTMTT/interface/KalmanState.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" +#include "DataFormats/Math/interface/deltaPhi.h" + +#include +#include + +using namespace std; + +namespace tmtt { + + /* Initialize */ + + KFParamsComb::KFParamsComb(const Settings* settings, const uint nHelixPar, const std::string& fitterName) + : KFbase(settings, nHelixPar, fitterName), + // Initialize cuts applied to helix states vs KF layer number of last added stub. + kfLayerVsPtToler_(settings->kfLayerVsPtToler()), + kfLayerVsD0Cut5_(settings->kfLayerVsD0Cut5()), + kfLayerVsZ0Cut5_(settings->kfLayerVsZ0Cut5()), + kfLayerVsZ0Cut4_(settings->kfLayerVsZ0Cut4()), + kfLayerVsChiSq5_(settings->kfLayerVsChiSq5()), + kfLayerVsChiSq4_(settings->kfLayerVsChiSq4()) {} + + /* Helix state seed */ + + TVectorD KFParamsComb::seedX(const L1track3D& l1track3D) const { + TVectorD vecX(nHelixPar_); + vecX[INV2R] = settings_->invPtToInvR() * l1track3D.qOverPt() / 2; + vecX[PHI0] = reco::deltaPhi(l1track3D.phi0() - sectorPhi(), 0.); + vecX[Z0] = l1track3D.z0(); + vecX[T] = l1track3D.tanLambda(); + if (nHelixPar_ == 5) { // fit without d0 constraint + vecX[D0] = l1track3D.d0(); + } + + return vecX; + } + + /* Helix state seed covariance matrix */ + + TMatrixD KFParamsComb::seedC(const L1track3D& l1track3D) const { + TMatrixD matC(nHelixPar_, nHelixPar_); + + double invPtToInv2R = settings_->invPtToInvR() / 2; + + // Assumed track seed (from HT) uncertainty in transverse impact parameter. + + // Constants optimised by hand for TMTT algo. + const float inv2Rsigma = 0.0314 * invPtToInv2R; + constexpr float phi0sigma = 0.0102; + constexpr float z0sigma = 5.0; + constexpr float tanLsigma = 0.5; + constexpr float d0Sigma = 1.0; + // (z0, tanL, d0) uncertainties could be smaller for Hybrid, if seeded in PS? -- To check! + // if (L1track3D.seedPS() > 0) z0sigma /= 4; ??? + matC[INV2R][INV2R] = pow(inv2Rsigma, 2); + matC[PHI0][PHI0] = pow(phi0sigma, 2); + matC[Z0][Z0] = pow(z0sigma, 2); + matC[T][T] = pow(tanLsigma, 2); + if (nHelixPar_ == 5) { // fit without d0 constraint + matC[D0][D0] = pow(d0Sigma, 2); + } + return matC; + } + + /* Stub position measurements in (phi,z) */ + + TVectorD KFParamsComb::vectorM(const Stub* stub) const { + TVectorD meas(2); + meas[PHI] = reco::deltaPhi(stub->phi(), sectorPhi()); + meas[Z] = stub->z(); + return meas; + } + + // Stub position resolution in (phi,z) + + TMatrixD KFParamsComb::matrixV(const Stub* stub, const KalmanState* state) const { + // Take Pt from input track candidate as more stable. + double inv2R = (settings_->invPtToInvR()) * 0.5 * state->candidate().qOverPt(); + double inv2R2 = inv2R * inv2R; + + double tanl = state->vectorX()(T); // factor of 0.9 improves rejection + double tanl2 = tanl * tanl; + + double vphi(0); + double vz(0); + double vcorr(0); + + double a = stub->sigmaPerp() * stub->sigmaPerp(); + double b = stub->sigmaPar() * stub->sigmaPar(); + double r2 = stub->r() * stub->r(); + double invr2 = 1. / r2; + + // Scattering term scaling as 1/Pt. + double sigmaScat = settings_->kalmanMultiScattTerm() / (state->candidate().pt()); + double sigmaScat2 = sigmaScat * sigmaScat; + + if (stub->barrel()) { + vphi = (a * invr2) + sigmaScat2; + + if (stub->tiltedBarrel()) { + // Convert uncertainty in (r,phi) to (z,phi). + float scaleTilted = 1.; + if (settings_->kalmanHOtilted()) { + if (settings_->useApproxB()) { // Simple firmware approximation + scaleTilted = approxB(stub->z(), stub->r()); + } else { // Exact C++ implementation. + float tilt = stub->tiltAngle(); + scaleTilted = sin(tilt) + cos(tilt) * tanl; + } + } + float scaleTilted2 = scaleTilted * scaleTilted; + // This neglects the non-radial strip effect, assumed negligeable for PS. + vz = b * scaleTilted2; + } else { + vz = b; + } + + if (settings_->kalmanHOfw()) { + // Use approximation corresponding to current firmware. + vz = b; + } + + } else { + vphi = a * invr2 + sigmaScat2; + vz = (b * tanl2); + + if (not stub->psModule()) { // Neglect these terms in PS + double beta = 0.; + // Add correlation term related to conversion of stub residuals from (r,phi) to (z,phi). + if (settings_->kalmanHOprojZcorr() == 2) + beta += -inv2R; + // Add alpha correction for non-radial 2S endcap strips.. + if (settings_->kalmanHOalpha() == 2) + beta += -stub->alpha(); // alpha is 0 except in endcap 2S disks + + double beta2 = beta * beta; + vphi += b * beta2; + vcorr = b * (beta * tanl); + + if (settings_->kalmanHOfw()) { + // Use approximation corresponding to current firmware. + vphi = (a * invr2) + (b * inv2R2) + sigmaScat2; + vcorr = 0.; + vz = (b * tanl2); + } + } + } + + TMatrixD matV(2, 2); + matV(PHI, PHI) = vphi; + matV(Z, Z) = vz; + matV(PHI, Z) = vcorr; + matV(Z, PHI) = vcorr; + + return matV; + } + + /* The Kalman measurement matrix = derivative of helix intercept w.r.t. helix params */ + /* Here I always measure phi(r), and z(r) */ + + TMatrixD KFParamsComb::matrixH(const Stub* stub) const { + TMatrixD matH(2, nHelixPar_); + double r = stub->r(); + matH(PHI, INV2R) = -r; + matH(PHI, PHI0) = 1; + if (nHelixPar_ == 5) { // fit without d0 constraint + matH(PHI, D0) = -1. / r; + } + matH(Z, Z0) = 1; + matH(Z, T) = r; + return matH; + } + + /* Kalman helix ref point extrapolation matrix */ + + TMatrixD KFParamsComb::matrixF(const Stub* stub, const KalmanState* state) const { + const TMatrixD unitMatrix(TMatrixD::kUnit, TMatrixD(nHelixPar_, nHelixPar_)); + return unitMatrix; + } + + /* Get physical helix params */ + + TVectorD KFParamsComb::trackParams(const KalmanState* state) const { + TVectorD vecX = state->vectorX(); + TVectorD vecY(nHelixPar_); + vecY[QOVERPT] = 2. * vecX[INV2R] / settings_->invPtToInvR(); + vecY[PHI0] = reco::deltaPhi(vecX[PHI0] + sectorPhi(), 0.); + vecY[Z0] = vecX[Z0]; + vecY[T] = vecX[T]; + if (nHelixPar_ == 5) { // fit without d0 constraint + vecY[D0] = vecX[D0]; + } + return vecY; + } + + /* If using 5 param helix fit, get track params with beam-spot constraint & track fit chi2 from applying it. */ + /* (N.B. chi2rz unchanged by constraint) */ + + TVectorD KFParamsComb::trackParams_BeamConstr(const KalmanState* state, double& chi2rphi) const { + if (nHelixPar_ == 5) { // fit without d0 constraint + TVectorD vecX = state->vectorX(); + TMatrixD matC = state->matrixC(); + TVectorD vecY(nHelixPar_); + double delChi2rphi = (vecX[D0] * vecX[D0]) / matC[D0][D0]; + chi2rphi = state->chi2rphi() + delChi2rphi; + // Apply beam-spot constraint to helix params in transverse plane only, as most sensitive to it. + vecX[INV2R] -= vecX[D0] * (matC[INV2R][D0] / matC[D0][D0]); + vecX[PHI0] -= vecX[D0] * (matC[PHI0][D0] / matC[D0][D0]); + vecX[D0] = 0.0; + vecY[QOVERPT] = 2. * vecX[INV2R] / settings_->invPtToInvR(); + vecY[PHI0] = reco::deltaPhi(vecX[PHI0] + sectorPhi(), 0.); + vecY[Z0] = vecX[Z0]; + vecY[T] = vecX[T]; + vecY[D0] = vecX[D0]; + return vecY; + } else { + return (this->trackParams(state)); + } + } + + /* Check if helix state passes cuts */ + + bool KFParamsComb::isGoodState(const KalmanState& state) const { + // Set cut values that are different for 4 & 5 param helix fits. + vector kfLayerVsZ0Cut = (nHelixPar_ == 5) ? kfLayerVsZ0Cut5_ : kfLayerVsZ0Cut4_; + vector kfLayerVsChiSqCut = (nHelixPar_ == 5) ? kfLayerVsChiSq5_ : kfLayerVsChiSq4_; + + unsigned nStubLayers = state.nStubLayers(); + bool goodState(true); + + TVectorD vecY = trackParams(&state); + double qOverPt = vecY[QOVERPT]; + double pt = std::abs(1 / qOverPt); + double z0 = std::abs(vecY[Z0]); + + // state parameter selections + + if (z0 > kfLayerVsZ0Cut[nStubLayers]) + goodState = false; + if (pt < settings_->houghMinPt() - kfLayerVsPtToler_[nStubLayers]) + goodState = false; + if (nHelixPar_ == 5) { // fit without d0 constraint + double d0 = std::abs(state.vectorX()[D0]); + if (d0 > kfLayerVsD0Cut5_[nStubLayers]) + goodState = false; + } + + // chi2 selection + + double chi2scaled = state.chi2scaled(); // chi2(r-phi) scaled down to improve electron performance. + + if (chi2scaled > kfLayerVsChiSqCut[nStubLayers]) + goodState = false; // No separate pT selection needed + + const bool countUpdateCalls = false; // Print statement to count calls to Updator. + + if (countUpdateCalls || (settings_->kalmanDebugLevel() >= 2 && tpa_ != nullptr) || + (settings_->kalmanDebugLevel() >= 2 && settings_->hybrid())) { + std::stringstream text; + text << std::fixed << std::setprecision(4); + if (not goodState) + text << "State veto:"; + if (goodState) + text << "State kept:"; + text << " nlay=" << nStubLayers << " nskip=" << state.nSkippedLayers() << " chi2_scaled=" << chi2scaled; + if (tpa_ != nullptr) + text << " pt(mc)=" << tpa_->pt(); + text << " pt=" << pt << " q/pt=" << qOverPt << " tanL=" << vecY[T] << " z0=" << vecY[Z0] + << " phi0=" << vecY[PHI0]; + if (nHelixPar_ == 5) // fit without d0 constraint + text << " d0=" << vecY[D0]; + text << " fake" << (tpa_ == nullptr); + if (tpa_ != nullptr) + text << " pt(mc)=" << tpa_->pt(); + PrintL1trk() << text.str(); + } + + return goodState; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/KFbase.cc b/L1Trigger/TrackFindingTMTT/src/KFbase.cc new file mode 100644 index 0000000000000..f0f20e2c0ada1 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/KFbase.cc @@ -0,0 +1,968 @@ +///=== This is the base class for the Kalman Combinatorial Filter track fit algorithm. + +///=== Written by: S. Summers, K. Uchida, M. Pesaresi, I.Tomalin + +#include "L1Trigger/TrackFindingTMTT/interface/KFbase.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/KalmanState.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubKiller.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "CommonTools/UtilAlgos/interface/TFileService.h" +#include "DataFormats/Math/interface/deltaPhi.h" +#include "TMatrixD.h" + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace tmtt { + + /* Initialize cfg parameters */ + + KFbase::KFbase(const Settings *settings, const uint nHelixPar, const string &fitterName, const uint nMeas) + : TrackFitGeneric(settings, fitterName) { + nHelixPar_ = nHelixPar; + nMeas_ = nMeas; + numEtaRegions_ = settings->numEtaRegions(); + } + + /* Do track fit */ + + L1fittedTrack KFbase::fit(const L1track3D &l1track3D) { + iPhiSec_ = l1track3D.iPhiSec(); + iEtaReg_ = l1track3D.iEtaReg(); + resetStates(); + numUpdateCalls_ = 0; + + vector stubs = l1track3D.stubs(); + + auto orderByLayer = [](const Stub *a, const Stub *b) { return bool(a->layerId() < b->layerId()); }; + sort(stubs.begin(), stubs.end(), orderByLayer); // Makes debug printout pretty. + + //TP + const TP *tpa(nullptr); + if (l1track3D.matchedTP()) { + tpa = l1track3D.matchedTP(); + } + tpa_ = tpa; + + //track information dump + if (settings_->kalmanDebugLevel() >= 1) { + PrintL1trk() << "==============================================================================="; + std::stringstream text; + text << std::fixed << std::setprecision(4); + text << "Input track cand: [phiSec,etaReg]=[" << l1track3D.iPhiSec() << "," << l1track3D.iEtaReg() << "]"; + text << " HT(m,c)=(" << l1track3D.cellLocationHT().first << "," << l1track3D.cellLocationHT().second + << ") q/pt=" << l1track3D.qOverPt() << " tanL=" << l1track3D.tanLambda() << " z0=" << l1track3D.z0() + << " phi0=" << l1track3D.phi0() << " nStubs=" << l1track3D.numStubs() << " d0=" << l1track3D.d0(); + PrintL1trk() << text.str(); + if (not settings_->hybrid()) + printTP(tpa); + if (settings_->kalmanDebugLevel() >= 2) { + printStubLayers(stubs, l1track3D.iEtaReg()); + printStubs(stubs); + } + } + + //Kalman Filter + const KalmanState *cand = doKF(l1track3D, stubs, tpa); + + //return L1fittedTrk for the selected state (if KF produced one it was happy with). + if (cand != nullptr) { + // Get track helix params. + TVectorD trackPars = trackParams(cand); + double d0 = (nHelixPar_ == 5) ? trackPars[D0] : 0.; + + L1fittedTrack fitTrk(settings_, + &l1track3D, + cand->stubs(), + cand->hitPattern(), + trackPars[QOVERPT], + d0, + trackPars[PHI0], + trackPars[Z0], + trackPars[T], + cand->chi2rphi(), + cand->chi2rz(), + nHelixPar_); + + // Store supplementary info, specific to KF fitter. + fitTrk.setInfoKF(cand->nSkippedLayers(), numUpdateCalls_); + + // If doing 5 parameter fit, optionally also calculate helix params & chi2 with beam-spot constraint applied, + // and store inside L1fittedTrack object. + if (settings_->kalmanAddBeamConstr()) { + if (nHelixPar_ == 5) { + double chi2rphi_bcon = 0.; + TVectorD trackPars_bcon = trackParams_BeamConstr(cand, chi2rphi_bcon); + fitTrk.setBeamConstr(trackPars_bcon[QOVERPT], trackPars_bcon[PHI0], chi2rphi_bcon); + } + } + + // Fitted track params must lie in same sector as HT originally found track in. + if (!settings_->hybrid()) { // consistentSector() function not yet working for Hybrid. + + // Bodge to take into account digitisation in sector consistency check. + if (settings_->enableDigitize()) + fitTrk.digitizeTrack("KF4ParamsComb"); + + if (!fitTrk.consistentSector()) { + if (settings_->kalmanDebugLevel() >= 1) + PrintL1trk() << "Track rejected by sector consistency test"; + L1fittedTrack rejectedTrk; + return rejectedTrk; + } + } + + return fitTrk; + + } else { // Track rejected by fitter + + if (settings_->kalmanDebugLevel() >= 1) { + bool goodTrack = (tpa && tpa->useForAlgEff()); // Matches truth particle. + if (goodTrack) { + int tpin = tpa->index(); + PrintL1trk() << "TRACK LOST: eta=" << l1track3D.iEtaReg() << " pt=" << l1track3D.pt() << " tp=" << tpin; + + for (auto stub : stubs) { + int kalmanLay = + this->kalmanLayer(l1track3D.iEtaReg(), stub->layerIdReduced(), stub->barrel(), stub->r(), stub->z()); + std::stringstream text; + text << std::fixed << std::setprecision(4); + text << " Stub: lay_red=" << stub->layerIdReduced() << " KFlay=" << kalmanLay << " r=" << stub->r() + << " z=" << stub->z() << " assoc TPs ="; + for (const TP *tp_i : stub->assocTPs()) + text << " " << tp_i->index(); + PrintL1trk() << text.str(); + if (stub->assocTPs().empty()) + PrintL1trk() << " none"; + } + PrintL1trk() << "====================="; + } + } + + //dump on the missed TP for efficiency calculation. + if (settings_->kalmanDebugLevel() >= 3) { + if (tpa && tpa->useForAlgEff()) { + PrintL1trk() << "TP for eff. missed addr. index : " << tpa << " " << tpa->index(); + printStubs(stubs); + } + } + + L1fittedTrack rejectedTrk; + return rejectedTrk; + } + } + + /* Do track fit (internal function) */ + + const KalmanState *KFbase::doKF(const L1track3D &l1track3D, const vector &stubs, const TP *tpa) { + const KalmanState *finished_state = nullptr; + + map> + best_state_by_nstubs; // Best state (if any) for each viable no. of stubs on track value. + + // seed helix params & their covariance. + TVectorD x0 = seedX(l1track3D); + TMatrixD pxx0 = seedC(l1track3D); + TMatrixD K(nHelixPar_, 2); + TMatrixD dcov(2, 2); + + const KalmanState *state0 = mkState(l1track3D, 0, -1, nullptr, x0, pxx0, K, dcov, nullptr, 0, 0); + + // internal containers - i.e. the state FIFO. Contains estimate of helix params in last/next layer, with multiple entries if there were multiple stubs, yielding multiple states. + vector new_states; + vector prev_states; + prev_states.push_back(state0); + + // Get dead layers, if any. + bool remove2PSCut = settings_->kalmanRemove2PScut(); + set kfDeadLayers = kalmanDeadLayers(remove2PSCut); + + // arrange stubs into Kalman layers according to eta region + int etaReg = l1track3D.iEtaReg(); + map> layerStubs; + + for (auto stub : stubs) { + // Get Kalman encoded layer ID for this stub. + int kalmanLay = this->kalmanLayer(etaReg, stub->layerIdReduced(), stub->barrel(), stub->r(), stub->z()); + + constexpr unsigned int invalidKFlayer = 7; + if (kalmanLay != invalidKFlayer) { + if (layerStubs[kalmanLay].size() < settings_->kalmanMaxStubsPerLayer()) { + layerStubs[kalmanLay].push_back(stub); + } else { + // If too many stubs, FW keeps the last stub. + layerStubs[kalmanLay].back() = stub; + } + } + } + + // iterate using state->nextLayer() to determine next Kalman layer(s) to add stubs from + constexpr unsigned int nTypicalLayers = 6; // Number of tracker layers a typical track can pass through. + // If user asked to add up to 7 layers to track, increase number of iterations by 1. + const unsigned int maxIterations = std::max(nTypicalLayers, settings_->kalmanMaxNumStubs()); + for (unsigned iteration = 0; iteration < maxIterations; iteration++) { + int combinations_per_iteration = 0; + + bool easy = (l1track3D.numStubs() < settings_->kalmanMaxStubsEasy()); + unsigned int kalmanMaxSkipLayers = + easy ? settings_->kalmanMaxSkipLayersEasy() : settings_->kalmanMaxSkipLayersHard(); + + // update each state from previous iteration (or seed) using stubs in next Kalman layer + vector::const_iterator i_state = prev_states.begin(); + for (; i_state != prev_states.end(); i_state++) { + const KalmanState *the_state = *i_state; + + unsigned int layer = the_state->nextLayer(); // Get KF layer where stubs to be searched for next + unsigned nSkipped = the_state->nSkippedLayers(); + + // If this layer is known to be dead, skip to the next layer (layer+1) + // The next_states_skipped will then look at layer+2 + // However, if there are stubs in this layer, then don't skip (e.g. our phi/eta boundaries might not line up exactly with a dead region) + // Continue to skip until you reach a functioning layer (or a layer with stubs) + unsigned nSkippedDeadLayers = 0; + unsigned nSkippedAmbiguousLayers = 0; + while (kfDeadLayers.find(layer) != kfDeadLayers.end() && layerStubs[layer].empty()) { + layer += 1; + ++nSkippedDeadLayers; + } + while (this->kalmanAmbiguousLayer(etaReg, layer) && layerStubs[layer].empty()) { + layer += 1; + ++nSkippedAmbiguousLayers; + } + + // containers for updated state+stub combinations + vector next_states; + vector next_states_skipped; + + // find stubs for this layer + // (If layer > 6, this will return empty vector, so safe). + vector thislay_stubs = layerStubs[layer]; + + // find stubs for next layer if we skip a layer, except when we are on the penultimate layer, + // or we have exceeded the max skipped layers + vector nextlay_stubs; + + // If the next layer (layer+1) is a dead layer, then proceed to the layer after next (layer+2), if possible + // Also note if we need to increase "skipped" by one more for these states + unsigned nSkippedDeadLayers_nextStubs = 0; + unsigned nSkippedAmbiguousLayers_nextStubs = 0; + if (nSkipped < kalmanMaxSkipLayers) { + if (kfDeadLayers.find(layer + 1) != kfDeadLayers.end() && layerStubs[layer + 1].empty()) { + nextlay_stubs = layerStubs[layer + 2]; + nSkippedDeadLayers_nextStubs++; + } else if (this->kalmanAmbiguousLayer(etaReg, layer) && layerStubs[layer + 1].empty()) { + nextlay_stubs = layerStubs[layer + 2]; + nSkippedAmbiguousLayers_nextStubs++; + } else { + nextlay_stubs = layerStubs[layer + 1]; + } + } + + // If track was not rejected by isGoodState() is previous iteration, failure here usually means the tracker ran out of layers to explore. + // (Due to "kalmanLay" not having unique ID for each layer within a given eta sector). + if (settings_->kalmanDebugLevel() >= 2 && best_state_by_nstubs.empty() && thislay_stubs.empty() && + nextlay_stubs.empty()) + PrintL1trk() << "State is lost by start of iteration " << iteration + << " : #thislay_stubs=" << thislay_stubs.size() << " #nextlay_stubs=" << nextlay_stubs.size() + << " layer=" << layer << " eta=" << l1track3D.iEtaReg(); + + // If we skipped over a dead layer, only increment "nSkipped" after the stubs in next+1 layer have been obtained + nSkipped += nSkippedDeadLayers; + nSkipped += nSkippedAmbiguousLayers; + + // check to guarantee no fewer than 2PS hits per state at iteration 1 + // (iteration 0 will always include a PS hit, but iteration 1 could use 2S hits + // unless we include this) + if (iteration == 1 && !remove2PSCut) { + vector temp_thislaystubs; + vector temp_nextlaystubs; + for (auto stub : thislay_stubs) { + if (stub->psModule()) + temp_thislaystubs.push_back(stub); + } + for (auto stub : nextlay_stubs) { + if (stub->psModule()) + temp_nextlaystubs.push_back(stub); + } + thislay_stubs = temp_thislaystubs; + nextlay_stubs = temp_nextlaystubs; + } + + combinations_per_iteration += thislay_stubs.size() + nextlay_stubs.size(); + + // loop over each stub in this layer and check for compatibility with this state + for (unsigned i = 0; i < thislay_stubs.size(); i++) { + Stub *stub = thislay_stubs[i]; + + // Update helix params by adding this stub. + const KalmanState *new_state = kalmanUpdate(nSkipped, layer, stub, the_state, tpa); + + // Cut on track chi2, pt etc. + if (isGoodState(*new_state)) + next_states.push_back(new_state); + } + + // loop over each stub in next layer if we skip, and check for compatibility with this state + for (unsigned i = 0; i < nextlay_stubs.size(); i++) { + Stub *stub = nextlay_stubs[i]; + + const KalmanState *new_state = + kalmanUpdate(nSkipped + 1 + nSkippedDeadLayers_nextStubs + nSkippedAmbiguousLayers_nextStubs, + layer + 1 + nSkippedDeadLayers_nextStubs + nSkippedAmbiguousLayers_nextStubs, + stub, + the_state, + tpa); + + if (isGoodState(*new_state)) + next_states_skipped.push_back(new_state); + } + + // post Kalman filter local sorting per state + auto orderByChi2 = [](const KalmanState *a, const KalmanState *b) { + return bool(a->chi2scaled() < b->chi2scaled()); + }; + sort(next_states.begin(), next_states.end(), orderByChi2); + sort(next_states_skipped.begin(), next_states_skipped.end(), orderByChi2); + + new_states.insert(new_states.end(), next_states.begin(), next_states.end()); + new_states.insert(new_states.end(), next_states_skipped.begin(), next_states_skipped.end()); + /* + i = 0; + for (auto state : next_states) { + new_states.push_back(state); + i++; + } + + i = 0; + for (auto state : next_states_skipped) { + new_states.push_back(state); + i++; + } +*/ + } //end of state loop + + // copy new_states into prev_states for next iteration or end if we are on + // last iteration by clearing all states and making final state selection + + auto orderByMinSkipChi2 = [](const KalmanState *a, const KalmanState *b) { + return bool((a->chi2scaled()) * (a->nSkippedLayers() + 1) < (b->chi2scaled()) * (b->nSkippedLayers() + 1)); + }; + sort(new_states.begin(), new_states.end(), orderByMinSkipChi2); // Sort by chi2*(skippedLayers+1) + + unsigned int nStubs = iteration + 1; + // Success. We have at least one state that passes all cuts. Save best state found with this number of stubs. + if (nStubs >= settings_->kalmanMinNumStubs() && not new_states.empty()) + best_state_by_nstubs[nStubs] = new_states[0]; + + if (nStubs == settings_->kalmanMaxNumStubs()) { + // We're done. + prev_states.clear(); + new_states.clear(); + + } else { + // Continue iterating. + prev_states = new_states; + new_states.clear(); + } + } + + if (not best_state_by_nstubs.empty()) { + // Select state with largest number of stubs. + finished_state = best_state_by_nstubs.begin()->second; // First element has largest number of stubs. + if (settings_->kalmanDebugLevel() >= 1) { + std::stringstream text; + text << std::fixed << std::setprecision(4); + text << "Track found! final state selection: nLay=" << finished_state->nStubLayers() + << " hitPattern=" << std::hex << finished_state->hitPattern() << std::dec + << " phiSec=" << l1track3D.iPhiSec() << " etaReg=" << l1track3D.iEtaReg() << " HT(m,c)=(" + << l1track3D.cellLocationHT().first << "," << l1track3D.cellLocationHT().second << ")"; + TVectorD y = trackParams(finished_state); + text << " q/pt=" << y[QOVERPT] << " tanL=" << y[T] << " z0=" << y[Z0] << " phi0=" << y[PHI0]; + if (nHelixPar_ == 5) + text << " d0=" << y[D0]; + text << " chosen from states:"; + for (const auto &p : best_state_by_nstubs) + text << " " << p.second->chi2() << "/" << p.second->nStubLayers(); + PrintL1trk() << text.str(); + } + } else { + if (settings_->kalmanDebugLevel() >= 1) { + PrintL1trk() << "Track lost"; + } + } + + return finished_state; + } + + /*--- Update a helix state by adding a stub. */ + + const KalmanState *KFbase::kalmanUpdate( + unsigned nSkipped, unsigned int layer, Stub *stub, const KalmanState *state, const TP *tpa) { + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "---------------"; + PrintL1trk() << "kalmanUpdate"; + PrintL1trk() << "---------------"; + printStub(stub); + } + + numUpdateCalls_++; // For monitoring, count calls to updator per track. + + // Helix params & their covariance. + TVectorD vecX = state->vectorX(); + TMatrixD matC = state->matrixC(); + if (state->barrel() && !stub->barrel()) { + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "STATE BARREL TO ENDCAP BEFORE "; + PrintL1trk() << "state : " << vecX[0] << " " << vecX[1] << " " << vecX[2] << " " << vecX[3]; + PrintL1trk() << "cov(x): "; + matC.Print(); + } + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "STATE BARREL TO ENDCAP AFTER "; + PrintL1trk() << "state : " << vecX[0] << " " << vecX[1] << " " << vecX[2] << " " << vecX[3]; + PrintL1trk() << "cov(x): "; + matC.Print(); + } + } + // Matrix to propagate helix reference point from one layer to next. + TMatrixD matF = matrixF(stub, state); + TMatrixD matFtrans(TMatrixD::kTransposed, matF); + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "matF"; + matF.Print(); + } + + // Multiply matrices to get helix params relative to reference point at next layer. + TVectorD vecXref = matF * vecX; + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "vecFref = ["; + for (unsigned i = 0; i < nHelixPar_; i++) + PrintL1trk() << vecXref[i] << ", "; + PrintL1trk() << "]"; + } + + // Get stub residuals. + TVectorD delta = residual(stub, vecXref, state->candidate().qOverPt()); + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "delta = " << delta[0] << ", " << delta[1]; + } + + // Derivative of predicted (phi,z) intercept with layer w.r.t. helix params. + TMatrixD matH = matrixH(stub); + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "matH"; + matH.Print(); + } + + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "previous state covariance"; + matC.Print(); + } + // Get scattering contribution to helix parameter covariance (currently zero). + TMatrixD matScat(nHelixPar_, nHelixPar_); + + // Get covariance on helix parameters at new reference point including scattering.. + TMatrixD matCref = matF * matC * matFtrans + matScat; + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "matCref"; + matCref.Print(); + } + // Get hit position covariance matrix. + TMatrixD matV = matrixV(stub, state); + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "matV"; + matV.Print(); + } + + TMatrixD matRinv = matrixRinv(matH, matCref, matV); + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "matRinv"; + matRinv.Print(); + } + + // Calculate Kalman Gain matrix. + TMatrixD matK = getKalmanGainMatrix(matH, matCref, matRinv); + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "matK"; + matK.Print(); + } + + // Update helix state & its covariance matrix with new stub. + TVectorD new_vecX(nHelixPar_); + TMatrixD new_matC(nHelixPar_, nHelixPar_); + adjustState(matK, matCref, vecXref, matH, delta, new_vecX, new_matC); + + // Update track fit chi2 with new stub. + double new_chi2rphi = 0, new_chi2rz = 0; + this->adjustChi2(state, matRinv, delta, new_chi2rphi, new_chi2rz); + + if (settings_->kalmanDebugLevel() >= 4) { + if (nHelixPar_ == 4) + PrintL1trk() << "adjusted x = " << new_vecX[0] << ", " << new_vecX[1] << ", " << new_vecX[2] << ", " + << new_vecX[3]; + else if (nHelixPar_ == 5) + PrintL1trk() << "adjusted x = " << new_vecX[0] << ", " << new_vecX[1] << ", " << new_vecX[2] << ", " + << new_vecX[3] << ", " << new_vecX[4]; + PrintL1trk() << "adjusted C "; + new_matC.Print(); + PrintL1trk() << "adjust chi2rphi=" << new_chi2rphi << " chi2rz=" << new_chi2rz; + } + + const KalmanState *new_state = mkState( + state->candidate(), nSkipped, layer, state, new_vecX, new_matC, matK, matV, stub, new_chi2rphi, new_chi2rz); + + return new_state; + } + + /* Create a KalmanState, containing a helix state & next stub it is to be updated with. */ + + const KalmanState *KFbase::mkState(const L1track3D &candidate, + unsigned nSkipped, + unsigned layer, + const KalmanState *last_state, + const TVectorD &vecX, + const TMatrixD &matC, + const TMatrixD &matK, + const TMatrixD &matV, + Stub *stub, + double chi2rphi, + double chi2rz) { + auto new_state = std::make_unique( + settings_, candidate, nSkipped, layer, last_state, vecX, matC, matK, matV, stub, chi2rphi, chi2rz); + + const KalmanState *p_new_state = new_state.get(); + listAllStates_.push_back(std::move(new_state)); // Vector keeps ownership of all states. + return p_new_state; + } + + /* Product of H*C*H(transpose) (where C = helix covariance matrix) */ + + TMatrixD KFbase::matrixHCHt(const TMatrixD &matH, const TMatrixD &matC) const { + TMatrixD matHtrans(TMatrixD::kTransposed, matH); + return matH * matC * matHtrans; + } + + /* Get inverted Kalman R matrix: inverse(V + HCHt) */ + + TMatrixD KFbase::matrixRinv(const TMatrixD &matH, const TMatrixD &matCref, const TMatrixD &matV) const { + TMatrixD matHCHt = matrixHCHt(matH, matCref); + TMatrixD matR = matV + matHCHt; + TMatrixD matRinv(2, 2); + if (matR.Determinant() > 0) { + matRinv = TMatrixD(TMatrixD::kInverted, matR); + } else { + // Protection against rare maths instability. + const TMatrixD unitMatrix(TMatrixD::kUnit, TMatrixD(nHelixPar_, nHelixPar_)); + const double big = 9.9e9; + matRinv = big * unitMatrix; + } + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "matHCHt"; + matHCHt.Print(); + PrintL1trk() << "matR"; + matR.Print(); + } + return matRinv; + } + + /* Determine Kalman gain matrix K */ + + TMatrixD KFbase::getKalmanGainMatrix(const TMatrixD &matH, const TMatrixD &matCref, const TMatrixD &matRinv) const { + TMatrixD matHtrans(TMatrixD::kTransposed, matH); + TMatrixD matCrefht = matCref * matHtrans; + TMatrixD matK = matCrefht * matRinv; + return matK; + } + + /* Calculate stub residual w.r.t. helix */ + + TVectorD KFbase::residual(const Stub *stub, const TVectorD &vecX, double candQoverPt) const { + TVectorD vd = vectorM(stub); // Get (phi relative to sector, z) of hit. + TMatrixD h = matrixH(stub); + TVectorD hx = h * vecX; // Get intercept of helix with layer (linear approx). + TVectorD delta = vd - hx; + + // Calculate higher order corrections to residuals. + + if (not settings_->kalmanHOfw()) { + TVectorD correction(2); + + float inv2R = (settings_->invPtToInvR()) * 0.5 * candQoverPt; + float tanL = vecX[T]; + float z0 = vecX[Z0]; + + float deltaS = 0.; + if (settings_->kalmanHOhelixExp()) { + // Higher order correction correction to circle expansion for improved accuracy at low Pt. + double corr = stub->r() * inv2R; + + // N.B. In endcap 2S, this correction to correction[0] is exactly cancelled by the deltaS-dependent correction to it below. + correction[0] += (1. / 6.) * pow(corr, 3); + + deltaS = (1. / 6.) * (stub->r()) * pow(corr, 2); + correction[1] -= deltaS * tanL; + } + + if ((not stub->barrel()) && not(stub->psModule())) { + // These corrections rely on inside --> outside tracking, so r-z track params in 2S modules known. + float rShift = (stub->z() - z0) / tanL - stub->r(); + + if (settings_->kalmanHOhelixExp()) + rShift -= deltaS; + + if (settings_->kalmanHOprojZcorr() == 1) { + // Add correlation term related to conversion of stub residuals from (r,phi) to (z,phi). + correction[0] += inv2R * rShift; + } + + if (settings_->kalmanHOalpha() == 1) { + // Add alpha correction for non-radial 2S endcap strips.. + correction[0] += stub->alpha() * rShift; + } + } + + // Apply correction to residuals. + delta += correction; + } + + delta[0] = reco::deltaPhi(delta[0], 0.); + + return delta; + } + + /* Update helix state & its covariance matrix with new stub */ + + void KFbase::adjustState(const TMatrixD &matK, + const TMatrixD &matCref, + const TVectorD &vecXref, + const TMatrixD &matH, + const TVectorD &delta, + TVectorD &new_vecX, + TMatrixD &new_matC) const { + new_vecX = vecXref + matK * delta; + const TMatrixD unitMatrix(TMatrixD::kUnit, TMatrixD(nHelixPar_, nHelixPar_)); + TMatrixD tmp = unitMatrix - matK * matH; + new_matC = tmp * matCref; + } + + /* Update track fit chi2 with new stub */ + + void KFbase::adjustChi2(const KalmanState *state, + const TMatrixD &matRinv, + const TVectorD &delta, + double &chi2rphi, + double &chi2rz) const { + // Change in chi2 (with r-phi/r-z correlation term included in r-phi component) + double delChi2rphi = delta[PHI] * delta[PHI] * matRinv[PHI][PHI] + 2 * delta[PHI] * delta[Z] * matRinv[PHI][Z]; + double delChi2rz = delta[Z] * delta[Z] * matRinv[Z][Z]; + + if (settings_->kalmanDebugLevel() >= 4) { + PrintL1trk() << "delta(chi2rphi)=" << delChi2rphi << " delta(chi2rz)= " << delChi2rz; + } + chi2rphi = state->chi2rphi() + delChi2rphi; + chi2rz = state->chi2rz() + delChi2rz; + return; + } + + /* Reset internal data ready for next track. */ + + void KFbase::resetStates() { listAllStates_.clear(); } + + /* Get Kalman layer mapping (i.e. layer order in which stubs should be processed) */ + + unsigned int KFbase::kalmanLayer( + unsigned int iEtaReg, unsigned int layerIDreduced, bool barrel, float r, float z) const { + // index across is GP encoded layer ID (where barrel layers=1,2,7,5,4,3 & endcap wheels=3,4,5,6,7 & 0 never occurs) + // index down is eta reg + // element is kalman layer, where 7 is invalid + + // If stub with given GP encoded layer ID can have different KF layer ID depending on whether it + // is barrel or endcap, then in layerMap, the the barrel case is assumed. + // The endcap case is fixed by hand later in this function. + + const unsigned int nEta = 16; + const unsigned int nGPlayID = 7; + + if (nEta != numEtaRegions_) + throw cms::Exception("LogicError") + << "ERROR KFbase::getKalmanLayer hardwired value of nEta differs from NumEtaRegions cfg param"; + + // In cases where identical GP encoded layer ID present in this sector from both barrel & endcap, this array filled considering barrel. The endcap is fixed by subsequent code. + + constexpr unsigned layerMap[nEta / 2][nGPlayID + 1] = { + {7, 0, 1, 5, 4, 3, 7, 2}, // B1 B2 B3 B4 B5 B6 + {7, 0, 1, 5, 4, 3, 7, 2}, // B1 B2 B3 B4 B5 B6 + {7, 0, 1, 5, 4, 3, 7, 2}, // B1 B2 B3 B4 B5 B6 + {7, 0, 1, 5, 4, 3, 7, 2}, // B1 B2 B3 B4 B5 B6 + {7, 0, 1, 5, 4, 3, 7, 2}, // B1 B2 B3 B4(/D3) B5(/D2) B6(/D1) + + {7, 0, 1, 3, 4, 2, 6, 2}, // B1 B2 B3(/D5)+B4(/D3) D1 D2 X D4 -- current FW + //{ 7, 0, 1, 3, 4, 3, 6, 2 }, // B1 B2 B3(/D5) D1+B4(/D3) D2 X D4 -- for use with "Fix cases" below. + + {7, 0, 1, 1, 2, 3, 4, 5}, // B1 B2+D1 D2 D3 D5 D6 + + //{ 7, 0, 7, 1, 2, 3, 4, 5 }, // B1 D1 D2 D3 D4 D5 = current FW (or when Ambiguous function used) + {7, 0, 7, 0, 1, 2, 3, 4}, // Avoid effi loss for eta > 2.3 when Ambiguous function not used. + }; + + unsigned int kfEtaReg; // KF VHDL eta sector def: small in barrel & large in endcap. + if (iEtaReg < numEtaRegions_ / 2) { + kfEtaReg = numEtaRegions_ / 2 - 1 - iEtaReg; + } else { + kfEtaReg = iEtaReg - numEtaRegions_ / 2; + } + + unsigned int kalmanLay = layerMap[kfEtaReg][layerIDreduced]; + + // Fixes to endcap stubs, for cases where identical GP encoded layer ID present in this sector from both barrel & endcap. + + if (not barrel) { + switch (kfEtaReg) { + case 4: // B1 B2 B3 B4 B5/D1 B6/D2 D3 + if (layerIDreduced == 3) { + kalmanLay = 4; + } else if (layerIDreduced == 4) { + kalmanLay = 5; + } else if (layerIDreduced == 5) { + kalmanLay = 6; + } + break; + //case 5: // B1 B2 B3+B4 D1 D2 D3 D4/D5 + case 5: // B1 B2 B3 D1+B4 D2 D3 D4/D5 + if (layerIDreduced == 5) { + kalmanLay = 5; + } else if (layerIDreduced == 7) { + kalmanLay = 6; + } + break; + default: + break; + } + } + + /* + // Fix cases where a barrel layer only partially crosses the eta sector. + // (Logically should work, but actually reduces efficiency -- INVESTIGATE). + + const float barrelHalfLength = 120.; + const float barrel4Radius = 68.8; + const float barrel5Radius = 86.1; + + if ( not barrel) { + switch ( kfEtaReg ) { + case 4: + if (layerIDreduced==3) { // D1 + float disk1_rCut = barrel5Radius*(std::abs(z)/barrelHalfLength); + if (r > disk1_rCut) kalmanLay++; + } + break; + case 5: + if (layerIDreduced==3) { // D1 + float disk1_rCut = barrel4Radius*(std::abs(z)/barrelHalfLength); + if (r > disk1_rCut) kalmanLay++; + } + if (layerIDreduced==4) { // D2 + float disk2_rCut = barrel4Radius*(std::abs(z)/barrelHalfLength); + if (r > disk2_rCut) kalmanLay++; + } + break; + default: + break; + } + } + */ + + return kalmanLay; + } + + /*=== Check if particles in given eta sector are uncertain to go through the given KF layer. */ + /*=== (If so, count layer for numbers of hit layers, but not for number of skipped layers). */ + + bool KFbase::kalmanAmbiguousLayer(unsigned int iEtaReg, unsigned int kfLayer) { + // Only helps in extreme forward sector, and there not significantly. + // UNDERSTAND IF CAN BE USED ELSEWHERE. + + /* + const unsigned int nEta = 16; + const unsigned int nKFlayer = 7; + constexpr bool ambiguityMap[nEta/2][nKFlayer] = + { + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false}, + {true , false, false, false, false, false, false}, + }; + + unsigned int kfEtaReg; // KF VHDL eta sector def: small in barrel & large in endcap. + if (iEtaReg < numEtaRegions_/2) { + kfEtaReg = numEtaRegions_/2 - 1 - iEtaReg; + } else { + kfEtaReg = iEtaReg - numEtaRegions_/2; + } + + bool ambiguous = ambiguityMap[kfEtaReg][kfLayer]; + */ + + bool ambiguous = false; + return ambiguous; + } + + /* Adjust KF algorithm to allow for any dead tracker layers */ + + set KFbase::kalmanDeadLayers(bool &remove2PSCut) const { + // Kill scenarios described StubKiller.cc + + // By which Stress Test scenario (if any) are dead modules being emulated? + const StubKiller::KillOptions killScenario = static_cast(settings_->killScenario()); + // Should TMTT tracking be modified to reduce efficiency loss due to dead modules? + const bool killRecover = settings_->killRecover(); + + set> deadGPlayers; // GP layer ID & boolean indicating if in barrel. + + // Range of sectors chosen to cover dead regions from StubKiller. + if (killRecover) { + if (killScenario == StubKiller::KillOptions::layer5) { // barrel layer 5 + if (iEtaReg_ >= 3 && iEtaReg_ <= 7 && iPhiSec_ >= 1 && iPhiSec_ <= 5) { + deadGPlayers.insert(pair(4, true)); + } + } else if (killScenario == StubKiller::KillOptions::layer1) { // barrel layer 1 + if (iEtaReg_ <= 7 && iPhiSec_ >= 1 && iPhiSec_ <= 5) { + deadGPlayers.insert(pair(1, true)); + } + remove2PSCut = true; + } else if (killScenario == StubKiller::KillOptions::layer1layer2) { // barrel layers 1 & 2 + if (iEtaReg_ <= 7 && iPhiSec_ >= 1 && iPhiSec_ <= 5) { + deadGPlayers.insert(pair(1, true)); + } + if (iEtaReg_ >= 1 && iEtaReg_ <= 7 && iPhiSec_ >= 1 && iPhiSec_ <= 5) { + deadGPlayers.insert(pair(2, true)); + } + remove2PSCut = true; + } else if (killScenario == StubKiller::KillOptions::layer1disk1) { // barrel layer 1 & disk 1 + if (iEtaReg_ <= 7 && iPhiSec_ >= 1 && iPhiSec_ <= 5) { + deadGPlayers.insert(pair(1, true)); + } + if (iEtaReg_ <= 3 && iPhiSec_ >= 1 && iPhiSec_ <= 5) { + deadGPlayers.insert(pair(3, false)); + } + remove2PSCut = true; + } + } + + set kfDeadLayers; + for (const auto &p : deadGPlayers) { + unsigned int layer = p.first; + bool barrel = p.second; + float r = 0.; // This fails for r-dependent parts of kalmanLayer(). FIX + float z = 999.; + unsigned int kalmanLay = this->kalmanLayer(iEtaReg_, layer, barrel, r, z); + kfDeadLayers.insert(kalmanLay); + } + + return kfDeadLayers; + } + + //=== Function to calculate approximation for tilted barrel modules (aka B) copied from Stub class. + + float KFbase::approxB(float z, float r) const { + return settings_->bApprox_gradient() * std::abs(z) / r + settings_->bApprox_intercept(); + } + + /* Print truth particle */ + + void KFbase::printTP(const TP *tp) const { + TVectorD tpParams(5); + bool useForAlgEff(false); + if (tp) { + useForAlgEff = tp->useForAlgEff(); + tpParams[QOVERPT] = tp->qOverPt(); + tpParams[PHI0] = tp->phi0(); + tpParams[Z0] = tp->z0(); + tpParams[T] = tp->tanLambda(); + tpParams[D0] = tp->d0(); + } + std::stringstream text; + text << std::fixed << std::setprecision(4); + if (tp) { + text << " TP index = " << tp->index() << " useForAlgEff = " << useForAlgEff << " "; + const string helixNames[5] = {"qOverPt", "phi0", "z0", "tanL", "d0"}; + for (int i = 0; i < tpParams.GetNrows(); i++) { + text << helixNames[i] << ":" << tpParams[i] << ", "; + } + text << " inv2R = " << tp->qOverPt() * settings_->invPtToInvR() * 0.5; + } else { + text << " Fake"; + } + PrintL1trk() << text.str(); + } + + /* Print tracker layers with stubs */ + + void KFbase::printStubLayers(const vector &stubs, unsigned int iEtaReg) const { + std::stringstream text; + text << std::fixed << std::setprecision(4); + if (stubs.empty()) + text << "stub layers = []\n"; + else { + text << "stub layers = [ "; + for (unsigned i = 0; i < stubs.size(); i++) { + text << stubs[i]->layerId(); + if (i != stubs.size() - 1) + text << ", "; + } + text << " ] "; + text << "KF stub layers = [ "; + for (unsigned j = 0; j < stubs.size(); j++) { + unsigned int kalmanLay = + this->kalmanLayer(iEtaReg, stubs[j]->layerIdReduced(), stubs[j]->barrel(), stubs[j]->r(), stubs[j]->z()); + text << kalmanLay; + if (j != stubs.size() - 1) + text << ", "; + } + text << " ]\n"; + } + PrintL1trk() << text.str(); + } + + /* Print a stub */ + + void KFbase::printStub(const Stub *stub) const { + std::stringstream text; + text << std::fixed << std::setprecision(4); + text << "stub "; + text << "index=" << stub->index() << " "; + text << "layerId=" << stub->layerId() << " "; + text << "r=" << stub->r() << " "; + text << "phi=" << stub->phi() << " "; + text << "z=" << stub->z() << " "; + text << "sigmaX=" << stub->sigmaPerp() << " "; + text << "sigmaZ=" << stub->sigmaPar() << " "; + text << "TPids="; + std::set tps = stub->assocTPs(); + for (auto tp : tps) + text << tp->index() << ","; + PrintL1trk() << text.str(); + } + + /* Print all stubs */ + + void KFbase::printStubs(const vector &stubs) const { + for (auto &stub : stubs) { + printStub(stub); + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/KalmanState.cc b/L1Trigger/TrackFindingTMTT/src/KalmanState.cc new file mode 100644 index 0000000000000..b44fe190b5d42 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/KalmanState.cc @@ -0,0 +1,103 @@ +#include "L1Trigger/TrackFindingTMTT/interface/KalmanState.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "TMatrixD.h" + +using namespace std; + +namespace tmtt { + + KalmanState::KalmanState(const Settings *settings, + const L1track3D &candidate, + unsigned nSkipped, + int kLayer, + const KalmanState *last_state, + const TVectorD &vecX, + const TMatrixD &matC, + const TMatrixD &matK, + const TMatrixD &matV, + Stub *stub, + double chi2rphi, + double chi2rz) + : settings_(settings), + kLayer_(kLayer), + last_state_(last_state), + vecX_(vecX), + stub_(stub), + chi2rphi_(chi2rphi), + chi2rz_(chi2rz), + nSkipped_(nSkipped), + l1track3D_(candidate) { + matC_.Clear(); + matC_.ResizeTo(matC.GetNrows(), matC.GetNcols()); + matC_ = matC; + matK_.ResizeTo(matK.GetNrows(), matK.GetNcols()); + matK_ = matK; + matV_.ResizeTo(matV.GetNrows(), matV.GetNcols()); + matV_ = matV; + kalmanChi2RphiScale_ = settings_->kalmanChi2RphiScale(); + + hitPattern_ = 0; + if (last_state != nullptr) + hitPattern_ = last_state->hitPattern(); // Bit encoded list of hit layers + if (stub != nullptr && kLayer_ >= 0) + hitPattern_ |= (1 << (kLayer_)); + + r_ = 0.1; + z_ = 0; + barrel_ = true; + + if (stub != nullptr) { + r_ = stub->r(); + z_ = stub->z(); + barrel_ = stub->barrel(); + } + + n_stubs_ = 1 + kLayer_ - nSkipped_; + } + + bool KalmanState::good(const TP *tp) const { + const KalmanState *state = this; + while (state) { + Stub *stub = state->stub(); + if (stub != nullptr) { + const set &tps = stub->assocTPs(); + if (tps.find(tp) == tps.end()) + return false; + } + state = state->last_state(); + } + return true; + } + + double KalmanState::reducedChi2() const { + if (2 * n_stubs_ - vecX_.GetNrows() > 0) + return (this->chi2()) / (2 * n_stubs_ - vecX_.GetNrows()); + else + return 0; + } + + const KalmanState *KalmanState::last_update_state() const { + const KalmanState *state = this; + while (state) { + if (state->stub() != nullptr) + return state; + state = state->last_state(); + } + return nullptr; + } + + std::vector KalmanState::stubs() const { + std::vector all_stubs; + + const KalmanState *state = this; + while (state) { + Stub *stub = state->stub(); + if (stub != nullptr) + all_stubs.push_back(stub); + state = state->last_state(); + } + std::reverse(all_stubs.begin(), all_stubs.end()); // Put innermost stub first. + return all_stubs; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/L1fittedTrack.cc b/L1Trigger/TrackFindingTMTT/src/L1fittedTrack.cc new file mode 100644 index 0000000000000..96eeac6ed7b5e --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/L1fittedTrack.cc @@ -0,0 +1,41 @@ +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" + +using namespace std; + +namespace tmtt { + + // Empty vector used to initialize rejected tracks. + const std::vector L1fittedTrack::noStubs_; + + // Digitize track and degrade helix parameter resolution according to effect of digitisation. + + void L1fittedTrack::digitizeTrack(const string& fitterName) { + if (settings_->enableDigitize()) { + if (not digitalTrack_) { + // Create & run digitizer. + digitalTrack_ = std::make_shared(settings_, fitterName, this); + + // Convert digitized track params back to floating point with degraded resolution. + qOverPt_ = digitalTrack_->qOverPt(); + if (nHelixParam_ == 5) + d0_ = digitalTrack_->d0(); + phi0_ = digitalTrack_->phi0(); + z0_ = digitalTrack_->z0(); + tanLambda_ = digitalTrack_->tanLambda(); + chi2rphi_ = digitalTrack_->chisquaredRphi(); + chi2rz_ = digitalTrack_->chisquaredRz(); + + // Ditto for beam-spot constrained values. + if (nHelixParam_ == 5) { + qOverPt_bcon_ = digitalTrack_->qOverPt_bcon(); + phi0_bcon_ = digitalTrack_->phi0_bcon(); + chi2rphi_bcon_ = digitalTrack_->chisquaredRphi_bcon(); + } + + // Recalculate consistency flag using updated helix params. + this->setConsistentHTcell(); + } + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/Make3Dtracks.cc b/L1Trigger/TrackFindingTMTT/src/Make3Dtracks.cc new file mode 100644 index 0000000000000..4e401ec8c8cdf --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/Make3Dtracks.cc @@ -0,0 +1,129 @@ +#include "L1Trigger/TrackFindingTMTT/interface/Make3Dtracks.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track2D.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" + +#include +#include + +using namespace std; + +namespace tmtt { + + class Settings; + + //=== Initialization + + Make3Dtracks::Make3Dtracks(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float phiCentreSector) + : // Store config params & arguments. + settings_(settings), + iPhiSec_(iPhiSec), // Sector number + iEtaReg_(iEtaReg), + etaMinSector_(etaMinSector), // Range of eta sector + etaMaxSector_(etaMaxSector), // Range of eta sector + phiCentreSector_(phiCentreSector), // Centre of phi sector + + // Note if any fitters require an r-z track filter to be run. + runRZfilter_(not settings->useRZfilter().empty()) { + // Initialize any track filters (e.g. r-z) run after the r-phi Hough transform. + if (runRZfilter_) + rzFilter_ = + std::make_unique(settings_, iPhiSec_, iEtaReg_, etaMinSector_, etaMaxSector_, phiCentreSector_); + } + + //=== Convert 2D tracks found by HT within the current sector to 3D tracks, without running any r-z track filter. + //=== by adding a rough estimate of their r-z helix parameters. + + void Make3Dtracks::makeUnfilteredTrks(const list& vecTracksRphi) { + vecTracks3D_unfiltered_.clear(); + + for (const L1track2D& trkRphi : vecTracksRphi) { + const vector& stubsOnTrkRphi = trkRphi.stubs(); // stubs assigned to track + + float qOverPt = trkRphi.helix2D().first; + float phi0 = trkRphi.helix2D().second; + + if (settings_->enableDigitize()) { + // Centre of HT bin lies on boundary of two fitted track digi bins, so nudge slightly +ve (like FW) + // to remove ambiguity. + const float small = 0.1; + const unsigned int nHelixBits = 18; // Bits used internally in KF HLS to represent helix params. + qOverPt += (2. / settings_->invPtToInvR()) * small * settings_->kf_oneOver2rRange() / pow(2., nHelixBits); + phi0 += small * settings_->kf_phi0Range() / pow(2., nHelixBits); + } + pair helixRphi(qOverPt, phi0); + + // Estimate r-z track helix parameters from centre of eta sector. + float z0 = 0.; + float tan_lambda = 0.5 * (1 / tan(2 * atan(exp(-etaMinSector_))) + 1 / tan(2 * atan(exp(-etaMaxSector_)))); + + pair helixRz(z0, tan_lambda); + + // Create 3D track, by adding r-z helix params to 2D track + L1track3D trk3D(settings_, + stubsOnTrkRphi, + trkRphi.cellLocationHT(), + helixRphi, + helixRz, + iPhiSec_, + iEtaReg_, + trkRphi.optoLinkID(), + trkRphi.mergedHTcell()); + + // Optionally use MC truth to eliminate all fake tracks & all incorrect stubs assigned to tracks + // before doing fit (for debugging). + bool cheat_keep = true; + if (settings_->trackFitCheat()) + cheat_keep = trk3D.cheat(); + + // Add to list of stored 3D tracks. + if (cheat_keep) + vecTracks3D_unfiltered_.push_back(trk3D); + } + } + + //=== Make 3D tracks from the 2D tracks found by the HT within the current sector, by running the r-z track filter. + //=== The r-z filter also adds an estimate of the r-z helix parameters to each track. + + void Make3Dtracks::makeRZfilteredTrks(const list& vecTracksRphi) { + vecTracks3D_rzFiltered_ = rzFilter_->filterTracks(vecTracksRphi); + + // Optionally use MC truth to eliminate all fake tracks & all incorrect stubs assigned to tracks + // before doing fit (for debugging). + if (settings_->trackFitCheat()) { + list vecTracks3D_tmp; + for (const L1track3D& trk : vecTracks3D_rzFiltered_) { + L1track3D trk_tmp = trk; + bool cheat_keep = trk_tmp.cheat(); + if (cheat_keep) + vecTracks3D_tmp.push_back(trk_tmp); + } + vecTracks3D_rzFiltered_ = vecTracks3D_tmp; + } + } + + //=== Get all 3D track candidates (either r-z filtered on unfiltered, depending on the boolean), + //=== that are associated to the given tracking particle. + //=== (If the vector is empty, then the tracking particle was not reconstructed in this sector). + + vector Make3Dtracks::assocTrackCands3D(const TP& tp, bool rzFiltered) const { + const list& allTracks3D = (rzFiltered) ? vecTracks3D_rzFiltered_ : vecTracks3D_unfiltered_; + + vector assocRecoTrk; + + // Loop over track candidates, looking for those associated to given TP. + for (const L1track3D& trk : allTracks3D) { + if (trk.matchedTP() != nullptr) { + if (trk.matchedTP()->index() == tp.index()) + assocRecoTrk.push_back(&trk); + } + } + + return assocRecoTrk; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/MiniHTstage.cc b/L1Trigger/TrackFindingTMTT/src/MiniHTstage.cc new file mode 100644 index 0000000000000..625b1305841e5 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/MiniHTstage.cc @@ -0,0 +1,252 @@ +#include "L1Trigger/TrackFindingTMTT/interface/MiniHTstage.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Sector.h" + +using namespace std; + +namespace tmtt { + + MiniHTstage::MiniHTstage(const Settings* settings) + : settings_(settings), + miniHTstage_(settings_->miniHTstage()), + muxOutputsHT_(static_cast(settings_->muxOutputsHT())), + houghNbinsPt_(settings_->houghNbinsPt()), + houghNbinsPhi_(settings_->houghNbinsPhi()), + miniHoughLoadBalance_(settings_->miniHoughLoadBalance()), + miniHoughNbinsPt_(settings_->miniHoughNbinsPt()), + miniHoughNbinsPhi_(settings_->miniHoughNbinsPhi()), + miniHoughMinPt_(settings_->miniHoughMinPt()), + miniHoughDontKill_(settings_->miniHoughDontKill()), + miniHoughDontKillMinPt_(settings_->miniHoughDontKillMinPt()), + numSubSecsEta_(settings_->numSubSecsEta()), + numPhiNonants_(settings_->numPhiNonants()), + numPhiSecPerNon_(settings_->numPhiSectors() / numPhiNonants_), + numEtaRegions_(settings_->numEtaRegions()), + busySectorKill_(settings_->busySectorKill()), + busySectorNumStubs_(settings_->busySectorNumStubs()), + busySectorMbinRanges_(settings_->busySectorMbinRanges()), + chosenRofPhi_(settings_->chosenRofPhi()), + // Get size of 1st stage HT cells. + binSizeQoverPtAxis_(miniHoughNbinsPt_ * 2. / (float)settings->houghMinPt() / (float)houghNbinsPt_), + binSizePhiTrkAxis_(miniHoughNbinsPhi_ * 2. * M_PI / (float)settings->numPhiSectors() / (float)houghNbinsPhi_), + invPtToDphi_(settings_->invPtToDphi()), + nHTlinksPerNonant_(0) { + nMiniHTcells_ = miniHoughNbinsPt_ * miniHoughNbinsPhi_; + + if (miniHoughLoadBalance_ != 0) { + if (muxOutputsHT_ == MuxHToutputs::MuxAlgoName::mBinPerLink) { // Multiplexer at output of HT enabled. + nHTlinksPerNonant_ = busySectorMbinRanges_.size() - 1; + } else { + throw cms::Exception("BadConfig") << "MiniHTstage: Unknown MuxOutputsHT configuration option!"; + } + } + } + + void MiniHTstage::exec(matrix>& mHtRphis) { + for (unsigned int iPhiNon = 0; iPhiNon < numPhiNonants_; iPhiNon++) { + // Indices are ([link ID, MHT cell], #stubs). + map, unsigned int> numStubsPerLinkStage1; + // Indices are ([link ID, MHT cell], #stubs). + map, unsigned int> numStubsPerLinkStage2; + // Indices are (link ID, #stubs). + map numStubsPerLink; + for (unsigned int iSecInNon = 0; iSecInNon < numPhiSecPerNon_; iSecInNon++) { + unsigned int iPhiSec = iPhiNon * numPhiSecPerNon_ + iSecInNon; + for (unsigned int iEtaReg = 0; iEtaReg < numEtaRegions_; iEtaReg++) { + Sector sector(settings_, iPhiSec, iEtaReg); + const float& phiCentre = sector.phiCentre(); + HTrphi* htRphi = mHtRphis(iPhiSec, iEtaReg).get(); + const list& roughTracks = htRphi->trackCands2D(); + list fineTracks; + + for (const L1track2D& roughTrk : roughTracks) { + float roughTrkPhi = + reco::deltaPhi(roughTrk.phi0() - chosenRofPhi_ * invPtToDphi_ * roughTrk.qOverPt() - phiCentre, 0.); + const pair& cell = roughTrk.cellLocationHT(); + const vector& stubs = roughTrk.stubs(); + bool fineTrksFound = false; + bool storeCoarseTrack = false; + const unsigned int& link = roughTrk.optoLinkID(); + + if (std::abs(roughTrk.qOverPt()) < + 1. / miniHoughMinPt_) { // Not worth using mini-HT at low Pt due to scattering. + + for (unsigned int mBin = 0; mBin < miniHoughNbinsPt_; mBin++) { + float qOverPtBin = roughTrk.qOverPt() - binSizeQoverPtAxis_ / 2. + + (mBin + .5) * binSizeQoverPtAxis_ / settings_->miniHoughNbinsPt(); + for (unsigned int cBin = 0; cBin < miniHoughNbinsPhi_; cBin++) { + float phiBin = reco::deltaPhi(roughTrkPhi - binSizePhiTrkAxis_ / 2. + + (cBin + .5) * binSizePhiTrkAxis_ / settings_->miniHoughNbinsPhi(), + 0.); + const bool mergedCell = false; // This represents mini cell. + const bool miniHTcell = true; + HTcell htCell(settings_, + iPhiSec, + iEtaReg, + sector.etaMin(), + sector.etaMax(), + qOverPtBin, + cell.first + mBin, + mergedCell, + miniHTcell); + // Firmware doesn't use bend filter in MHT. + htCell.disableBendFilter(); + + for (auto& stub : stubs) { + // Ensure stubs are digitized with respect to the current phi sector. + if (settings_->enableDigitize()) + stub->digitize(iPhiSec, Stub::DigiStage::HT); + float phiStub = reco::deltaPhi( + stub->phi() + invPtToDphi_ * qOverPtBin * (stub->r() - chosenRofPhi_) - phiCentre, 0.); + float dPhi = reco::deltaPhi(phiBin - phiStub, 0.); + float dPhiMax = binSizePhiTrkAxis_ / miniHoughNbinsPhi_ / 2. + + invPtToDphi_ * binSizeQoverPtAxis_ / (float)miniHoughNbinsPt_ * + std::abs(stub->r() - chosenRofPhi_) / 2.; + if (std::abs(dPhi) <= std::abs(reco::deltaPhi(dPhiMax, 0.))) + htCell.store(stub, sector.insideEtaSubSecs(stub)); + } + htCell.end(); + if (htCell.trackCandFound()) { + // Do load balancing. + unsigned int trueLinkID = linkIDLoadBalanced( + link, mBin, cBin, htCell.numStubs(), numStubsPerLinkStage1, numStubsPerLinkStage2); + + pair cellLocation(cell.first + mBin, cell.second + cBin); + pair helix2D( + qOverPtBin, reco::deltaPhi(phiBin + chosenRofPhi_ * invPtToDphi_ * qOverPtBin + phiCentre, 0.)); + L1track2D fineTrk( + settings_, htCell.stubs(), cellLocation, helix2D, iPhiSec, iEtaReg, trueLinkID, mergedCell); + // Truncation due to output opto-link bandwidth. + bool keep(true); + numStubsPerLink[trueLinkID] += htCell.numStubs(); + if (busySectorKill_ && numStubsPerLink[trueLinkID] > busySectorNumStubs_) + keep = false; + if (keep) { + fineTracks.push_back(fineTrk); + fineTrksFound = true; + } + } + } + } + + } else { + // Keep rough track if below Pt threshold where mini-HT in use. + storeCoarseTrack = true; + } + + if (storeCoarseTrack || ((not fineTrksFound) && miniHoughDontKill_ && + std::abs(roughTrk.qOverPt()) < 1. / miniHoughDontKillMinPt_)) { + // Keeping original track instead of mini-HTtracks. + // Invent dummy miniHT cells so as to be able to reuse load balancing, trying all combinations to identify the least used link. + pair bestCell = {0, 0}; + unsigned int bestNumStubsPerLink = 999999; + for (unsigned int mBin = 0; mBin < miniHoughNbinsPt_; mBin++) { + for (unsigned int cBin = 0; cBin < miniHoughNbinsPhi_; cBin++) { + unsigned int testLinkID = linkIDLoadBalanced( + link, mBin, cBin, roughTrk.numStubs(), numStubsPerLinkStage1, numStubsPerLinkStage2, true); + if (numStubsPerLink[testLinkID] < bestNumStubsPerLink) { + bestCell = {mBin, cBin}; + bestNumStubsPerLink = numStubsPerLink[testLinkID]; + } + } + } + + // Repeat for best link, this time incremementing stub counters. + unsigned int trueLinkID = linkIDLoadBalanced(link, + bestCell.first, + bestCell.second, + roughTrk.numStubs(), + numStubsPerLinkStage1, + numStubsPerLinkStage2); + + bool keep(true); + numStubsPerLink[trueLinkID] += roughTrk.numStubs(); + if (busySectorKill_ && numStubsPerLink[trueLinkID] > busySectorNumStubs_) + keep = false; + if (keep) { + fineTracks.push_back(roughTrk); + fineTracks.back().setOptoLinkID(trueLinkID); + } + } + } + // Replace all existing tracks inside HT array with new ones. + htRphi->replaceTrackCands2D(fineTracks); + } + } + } + } + + //=== Do load balancing + //=== (numStubs is stubs on this track, "link" is link ID before load balancing, and return argument is link ID after load balancing). + //=== (numStubsPerLinkStage* are stub counters per link used to determine best balance. If test=true, then these counters are not to be incrememented). + + unsigned int MiniHTstage::linkIDLoadBalanced( + unsigned int link, + unsigned int mBin, + unsigned int cBin, + unsigned int numStubs, + // Indices are ([link ID, MHT cell], #stubs). + map, unsigned int>& numStubsPerLinkStage1, + // Indices are ([link ID, MHT cell], #stubs). + map, unsigned int>& numStubsPerLinkStage2, + bool test) const { + unsigned int mhtCell = miniHoughNbinsPhi_ * mBin + cBin; // Send each mini-cell to a different output link + + // Number of output links after static load balancing roughly same as number of + // input links with this, with nSep per MHT cell. + unsigned int nSep = std::ceil(float(nHTlinksPerNonant_) / float(nMiniHTcells_)); + + unsigned int newLink, newerLink, newererLink; + + enum LoadBalancing { None = 0, Static = 1, Dynamic = 2 }; // Load balancing options + + if (miniHoughLoadBalance_ >= LoadBalancing::Static) { + // Static load balancing, 4 -> 1, with each MHT cell sent to seperate output link. + newLink = link % nSep; // newLink in range 0 to nSep-1. + } else { + newLink = link; + } + + if (miniHoughLoadBalance_ >= LoadBalancing::Dynamic) { + // 2-stage dynamic load balancing amongst links corresponding to same MHT cell. + + // Dynamically mix pairs of neighbouring links. + unsigned int balancedLinkA = 2 * (newLink / 2); + unsigned int balancedLinkB = balancedLinkA + 1; + + pair encodedLinkA(balancedLinkA, + mhtCell); // balancedLink* here in range 0 to nSep-1. + pair encodedLinkB(balancedLinkB, mhtCell); + if (numStubsPerLinkStage1[encodedLinkA] < numStubsPerLinkStage1[encodedLinkB]) { + newerLink = balancedLinkA; + } else { + newerLink = balancedLinkB; + } + pair encodedLinkAB(newerLink, mhtCell); + if (not test) + numStubsPerLinkStage1[encodedLinkAB] += numStubs; + + // Dynamically mix pairs of next-to-neighbouring links. + unsigned int balancedLinkY = newerLink; + unsigned int balancedLinkZ = (newerLink + 2) % nSep; + + pair encodedLinkY(balancedLinkY, mhtCell); + pair encodedLinkZ(balancedLinkZ, mhtCell); + if (numStubsPerLinkStage2[encodedLinkY] < numStubsPerLinkStage2[encodedLinkZ]) { + newererLink = balancedLinkY; + } else { + newererLink = balancedLinkZ; + } + pair encodedLinkYZ(newererLink, mhtCell); + if (not test) + numStubsPerLinkStage2[encodedLinkYZ] += numStubs; + + } else { + newererLink = newLink; + } + unsigned int trueLinkID = + (miniHoughLoadBalance_ != LoadBalancing::None) ? nMiniHTcells_ * newererLink + mhtCell : newererLink; + return trueLinkID; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/MuxHToutputs.cc b/L1Trigger/TrackFindingTMTT/src/MuxHToutputs.cc new file mode 100644 index 0000000000000..4e627d3ab9468 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/MuxHToutputs.cc @@ -0,0 +1,152 @@ +//--- Note that the word "link" appearing in the C++ or comments in this class actually corresponds +//--- to a pair of links in the hardware. + +#include "L1Trigger/TrackFindingTMTT/interface/MuxHToutputs.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" + +#include "FWCore/Utilities/interface/Exception.h" + +#include +#include + +using namespace std; + +namespace tmtt { + + //=== Initialize constants from configuration parameters. + + MuxHToutputs::MuxHToutputs(const Settings* settings) + : settings_(settings), + muxOutputsHT_(static_cast(settings_->muxOutputsHT())), + numPhiNonants_(settings_->numPhiNonants()), + numPhiSectors_(settings_->numPhiSectors()), + numPhiSecPerNon_(numPhiSectors_ / numPhiNonants_), + numEtaRegions_(settings_->numEtaRegions()), + busySectorKill_(settings_->busySectorKill()), // Kill excess tracks flowing out of HT? + busySectorNumStubs_(settings_->busySectorNumStubs()), // Max. num. of stubs that can be sent within TM period + busySectorMbinRanges_( + settings_->busySectorMbinRanges()), // Individual m bin (=q/Pt) ranges to be output to opto-links. + busySectorUseMbinRanges_(not busySectorMbinRanges_.empty()) // m bin ranges option disabled if vector empty. + { + // Implemented MUX algorithm relies on same number of sectors per nonant. + if (numPhiSectors_ % numPhiNonants_ != 0) + throw cms::Exception("BadConfig") + << "MuxHToutputs: Number of phi sectors is not a multiple of number of nonants!"; + + if (!busySectorUseMbinRanges_) + throw cms::Exception("BadConfig") << "MuxHToutputs: The implemented MUX algorithm requires you to be using the " + "busySectorMbinRanges cfg option!"; + + // Check that the MUX algorithm implemented in linkID() is not obviously wrong. + this->sanityCheck(); + + std::stringstream text; + text << "=== The R-PHI HT output is multiplexed onto " << this->numLinksPerNonant() + << " pairs of opto-links per nonant."; + static std::once_flag printOnce; + std::call_once( + printOnce, [](string t) { PrintL1trk() << t; }, text.str()); + } + + //=== Determine which tracks are transmitted on each HT output optical link, taking into account the multiplexing + //=== of multiple (eta,phi) sectors onto single links and the truncation of the tracks caused by the requirement + //=== to output all the tracks within the time-multiplexed period. + //=== This function replaces the 2D track collection in the r-phi HT with the subset surviving the TM cut. + + void MuxHToutputs::exec(matrix>& mHtRphis) const { + // As this loops over sectors in order of increasing sector number, this MUX algorithm always transmits tracks + // from the lowest sector numbers on each link first. So the highest sector numbers are more likely to be + // truncated by the TM period. The algorithm assumes that two or more m-bin ranges from the same sector will never + // be transmitted down the same link, as if this happens, it does not predict the order in which they will be + // transmitted. + + for (unsigned int iPhiNon = 0; iPhiNon < numPhiNonants_; iPhiNon++) { + vector numStubsPerLink(this->numLinksPerNonant(), 0); + + for (unsigned int iSecInNon = 0; iSecInNon < numPhiSecPerNon_; iSecInNon++) { + unsigned int iPhiSec = iPhiNon * numPhiSecPerNon_ + iSecInNon; + + for (unsigned int iEtaReg = 0; iEtaReg < numEtaRegions_; iEtaReg++) { + HTrphi* htRphi = mHtRphis(iPhiSec, iEtaReg).get(); // Get a mutable version of the r-phi HT. + + list keptTracks; + const list& tracks = htRphi->trackCands2D(); + + for (const L1track2D& trk : tracks) { + L1track2D trkTmp = trk; + unsigned int nStubs = trkTmp.numStubs(); // #stubs on this track. + unsigned int mBinRange = htRphi->getMbinRange(trkTmp); // Which m bin range is this track in? + // Get the output optical link corresponding to this sector & m-bin range. + unsigned int link = this->linkID(iSecInNon, iEtaReg, mBinRange); + // Make a note of opto-link number inside track object. + trkTmp.setOptoLinkID(link); + + numStubsPerLink[link] += nStubs; + // Check if this track can be output within the time-multiplexed period. + bool keep = ((not busySectorKill_) || (numStubsPerLink[link] <= busySectorNumStubs_)); + // FIX: with 2 GeV threshold, this causes significant truncation. + // Consider using one output link for each phi sector in nonant + if (keep) + keptTracks.push_back(trkTmp); + } + + // Replace the collection of 2D tracks in the r-phi HT with the subset of them surviving the TM cut. + htRphi->replaceTrackCands2D(keptTracks); + } + } + } + } + + //=== Define the number of (eta,phi) sectors that each output opto-link takes tracks from. (Depends on MUX scheme). + + unsigned int MuxHToutputs::muxFactor() const { + if (muxOutputsHT_ == MuxAlgoName::mBinPerLink) { + return numEtaRegions_ * numPhiSecPerNon_; + } else { + throw cms::Exception("BadConfig") << "MuxHToutputs: Unknown MuxOutputsHT configuration option!"; + } + } + + //=== Define the MUX algorithm by which tracks from the specified m-bin range in the HT for a given (phi,eta) + //=== sector within a phi nonant are multiplexed onto a single output optical link. + + unsigned int MuxHToutputs::linkID(unsigned int iSecInNon, unsigned int iEtaReg, unsigned int mBinRange) const { + unsigned int link; + + if (muxOutputsHT_ == MuxAlgoName::mBinPerLink) { + //--- This is the Sept. 2019 Mux for the transverse HT readout organised by m-bin. (Each m bin in entire nonant goes to a different link). + + link = 0; + link += mBinRange; + + } else { + throw cms::Exception("BadConfig") << "MuxHToutputs: Unknown MuxOutputsHT configuration option!"; + } + + if (link >= this->numLinksPerNonant()) + throw cms::Exception("LogicError") << "MuxHToutputs: Calculated link ID exceeded expected number of links! " + << link << " " << this->numLinksPerNonant(); + return link; + } + + //=== Do sanity check of the MUX algorithm implemented in linkID(). + + void MuxHToutputs::sanityCheck() { + if (numPhiSecPerNon_ * numEtaRegions_ % this->muxFactor() != 0) + throw cms::Exception("LogicError") + << "MuxHToutputs: Number of sectors per phi nonant is not a multiple of muxFactor()."; + + vector nObsElementsPerLink(this->numLinksPerNonant(), 0); + for (unsigned int iSecInNon = 0; iSecInNon < numPhiSecPerNon_; iSecInNon++) { + for (unsigned int iEtaReg = 0; iEtaReg < numEtaRegions_; iEtaReg++) { + unsigned int iCorr = (settings_->miniHTstage()) ? 1 : 0; + for (unsigned int mBinRange = 0; mBinRange < busySectorMbinRanges_.size() - iCorr; mBinRange++) { + unsigned int link = this->linkID(iSecInNon, iEtaReg, mBinRange); + nObsElementsPerLink[link] += 1; + } + } + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/Sector.cc b/L1Trigger/TrackFindingTMTT/src/Sector.cc new file mode 100644 index 0000000000000..1f933379fbacc --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/Sector.cc @@ -0,0 +1,274 @@ +#include "L1Trigger/TrackFindingTMTT/interface/Sector.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" + +#include "DataFormats/Math/interface/deltaPhi.h" + +using namespace std; + +namespace tmtt { + + //=== Initialise + + Sector::Sector(const Settings* settings, unsigned int iPhiSec, unsigned int iEtaReg) + : settings_(settings), + // Sector number + iPhiSec_(iPhiSec), + iEtaReg_(iEtaReg), + + beamWindowZ_(settings->beamWindowZ()), // Assumed half-length of beam-spot + + //=== Characteristics of this eta region. + // Using lines of specified rapidity drawn from centre of CMS, determine the z coords at which + // they cross the radius chosenRofZ_. + etaMin_(settings->etaRegions()[iEtaReg]), + etaMax_(settings->etaRegions()[iEtaReg + 1]), + chosenRofZ_(settings->chosenRofZ()), + // Get range in z of tracks covered by this sector at chosen radius from beam-line + zOuterMin_(chosenRofZ_ / tan(2. * atan(exp(-etaMin_)))), + zOuterMax_(chosenRofZ_ / tan(2. * atan(exp(-etaMax_)))), + + //=== Characteristics of this phi region. + chosenRofPhi_(settings->chosenRofPhi()), + minPt_(settings->houghMinPt()), // Min Pt covered by HT array. + assumedPhiTrkRes_(settings->assumedPhiTrkRes()), + useStubPhi_(settings->useStubPhi()), + useStubPhiTrk_(settings->useStubPhiTrk()), + calcPhiTrkRes_(settings->calcPhiTrkRes()), + //=== Check if subsectors in eta are being used within each sector. + numSubSecsEta_(settings->numSubSecsEta()) { + // Centre of phi (tracking) nonant zero must be along x-axis to be consistent with tracker cabling map. + // Define phi sector zero to start at lower end of phi range in nonant 0. + float phiCentreSec0 = -M_PI / float(settings->numPhiNonants()) + M_PI / float(settings->numPhiSectors()); + // Centre of sector in phi + phiCentre_ = 2. * M_PI * float(iPhiSec) / float(settings->numPhiSectors()) + phiCentreSec0; + sectorHalfWidth_ = M_PI / float(settings->numPhiSectors()); // Sector half width excluding overlaps. + + // If eta subsectors have equal width in rapidity, do this. + float subSecWidth = (etaMax_ - etaMin_) / float(numSubSecsEta_); + for (unsigned int i = 0; i < numSubSecsEta_; i++) { + float subSecEtaMin = etaMin_ + i * subSecWidth; + float subSecEtaMax = subSecEtaMin + subSecWidth; + float subSecZmin = chosenRofZ_ / tan(2. * atan(exp(-subSecEtaMin))); + float subSecZmax = chosenRofZ_ / tan(2. * atan(exp(-subSecEtaMax))); + zOuterMinSub_.push_back(subSecZmin); + zOuterMaxSub_.push_back(subSecZmax); + } + } + + //=== Check if stub is inside this eta region. + + bool Sector::insideEta(const Stub* stub) const { + // Lower edge of this eta region defined by line from (r,z) = (0,-beamWindowZ) to (chosenRofZ_, zOuterMin_). + // Upper edge of this eta region defined by line from (r,z) = (0, beamWindowZ) to (chosenRofZ_, zOuterMax_). + + bool inside = this->insideEtaRange(stub, zOuterMin_, zOuterMax_); + return inside; + } + + //=== Check if stub is within subsectors in eta that sector may be divided into. + + vector Sector::insideEtaSubSecs(const Stub* stub) const { + if (settings_->enableDigitize() && numSubSecsEta_ == 2) { + // Use (complicated) digitized firmware emulation + return subEtaFwCalc(stub->digitalStub()->iDigi_Rt(), stub->digitalStub()->iDigi_Z()); + + } else { + // Use (simpler) floating point calculation. + + vector insideVec; + + // Loop over subsectors. + for (unsigned int i = 0; i < numSubSecsEta_; i++) { + bool inside = this->insideEtaRange(stub, zOuterMinSub_[i], zOuterMaxSub_[i]); + insideVec.push_back(inside); + } + + return insideVec; + } + } + + //=== Check if stub is within eta sector or subsector that is delimated by specified zTrk range. + + bool Sector::insideEtaRange(const Stub* stub, float zRangeMin, float zRangeMax) const { + // Lower edge of this eta region defined by line from (r,z) = (0,-beamWindowZ) to (chosenRofZ_, zRangeMin). + // Upper edge of this eta region defined by line from (r,z) = (0, beamWindowZ) to (chosenRofZ_, zRangeMax). + + float zMin, zMax; + bool inside; + + // Calculate z coordinate of lower edge of this eta region, evaluated at radius of stub. + zMin = (zRangeMin * stub->r() - beamWindowZ_ * std::abs(stub->r() - chosenRofZ_)) / chosenRofZ_; + // Calculate z coordinate of upper edge of this eta region, evaluated at radius of stub. + zMax = (zRangeMax * stub->r() + beamWindowZ_ * std::abs(stub->r() - chosenRofZ_)) / chosenRofZ_; + + inside = (stub->z() > zMin && stub->z() < zMax); + return inside; + } + + //=== Check if stub is inside this phi region. + + bool Sector::insidePhi(const Stub* stub) const { + // N.B. The logic here for preventing a stub being assigned to > 2 sectors seems overly agressive. + // But attempts at improving it have failed ... + + bool okPhi = true; + bool okPhiTrk = true; + + if (useStubPhi_) { + float delPhi = + reco::deltaPhi(stub->phi(), phiCentre_); // Phi difference between stub & sector in range -PI to +PI. + float tolerancePhi = stub->phiDiff( + chosenRofPhi_, minPt_); // How much stub phi might differ from track phi because of track curvature. + float outsidePhi = std::abs(delPhi) - sectorHalfWidth_ - + tolerancePhi; // If > 0, then stub is not compatible with being inside this sector. + if (outsidePhi > 0) + okPhi = false; + } + + if (useStubPhiTrk_) { + // Estimate either phi0 of track from stub info, or phi of the track at radius chosenRofPhi_. + float phiTrk = stub->trkPhiAtR(chosenRofPhi_); + // Phi difference between stub & sector in range -PI to +PI. + float delPhiTrk = reco::deltaPhi(phiTrk, phiCentre_); + // Set tolerance equal to nominal resolution assumed in phiTrk + float tolerancePhiTrk = assumedPhiTrkRes_ * (2 * sectorHalfWidth_); + if (calcPhiTrkRes_) { + // Calculate uncertainty in phiTrk due to poor resolution in stub bend + float phiTrkRes = stub->trkPhiAtRcut(chosenRofPhi_); + // Reduce tolerance if this is smaller than the nominal assumed resolution. + tolerancePhiTrk = min(tolerancePhiTrk, phiTrkRes); + } + // If following > 0, then stub is not compatible with being inside this sector. + float outsidePhiTrk = std::abs(delPhiTrk) - sectorHalfWidth_ - tolerancePhiTrk; + + if (outsidePhiTrk > 0) + okPhiTrk = false; + } + + return (okPhi && okPhiTrk); + } + + //=== For performance studies, note which stubs on given tracking particle are inside the sector. + //=== Returns two booleans for each stub, indicating if they are in phi & eta sectors respectively. + //=== AND them together to get (eta,phi) sector decision. + + unordered_map > Sector::stubsInside(const TP& tp) const { + unordered_map > inside; + // Loop over stubs produced by tracking particle + const vector& assStubs = tp.assocStubs(); + for (const Stub* stub : assStubs) { + // Check if this stub is inside sector + inside[stub] = pair(this->insidePhi(stub), this->insideEta(stub)); + } + return inside; + } + + //=== Count number of stubs in given tracking particle which are inside this (phi,eta) sector; + //=== or inside it if only the eta cuts are applied; or inside it if only the phi cuts are applied. + //=== The results are returned as the 3 last arguments of the function. + + void Sector::numStubsInside(const TP& tp, + unsigned int& nStubsInsideEtaPhi, + unsigned int& nStubsInsideEta, + unsigned int& nStubsInsidePhi) const { + nStubsInsideEtaPhi = 0; + nStubsInsideEta = 0; + nStubsInsidePhi = 0; + for (const auto& iter : this->stubsInside(tp)) { + bool insidePhi = iter.second.first; + bool insideEta = iter.second.second; + if (insidePhi && insideEta) + nStubsInsideEtaPhi++; + if (insideEta) + nStubsInsideEta++; + if (insidePhi) + nStubsInsidePhi++; + } + } + + // Digitize a floating point number to 2s complement integer, dropping anything after the decimal point. (Kristian Harder) + + int64_t Sector::forceBitWidth(const float value, const UInt_t nBits) const { + // slightly hand-waving treatment of 2s complement + int64_t sign = 1; + if (value < 0) + sign = -1; + int64_t iValue = int64_t(std::abs(value)); + int64_t mask = (int64_t(1) << nBits) - int64_t(1); + int64_t result = sign * (iValue & mask); + if (std::abs(result - value) > 1) + throw cms::Exception("LogicError") + << "Sector::forceBitWidth is messing up by using too few bits to digitize number" + << " nBits=" << nBits << " Input float=" << value << " Output digi = " << result; + return result; + // Check that result is compatible with value. Throw error if not. + } + + //=== Check if stub is within subsectors in eta that sector may be divided into. Uses digitized calculation corresponding to GP firmware. (Kristian Harder) + //=== Modified to configurable number of rT and z digisation bits by Ian, with advice from Luis. + + vector Sector::subEtaFwCalc(const int rT, const int z) const { + // Note number of reference bits used to digitize rT and z, used when GP authors determined some constants below. + unsigned int rtBitsRef = 10; + unsigned int zBitsRef = 12; + + // This replaces Kristian's hard-wired constants with configurable ones. + unsigned int rtBits = settings_->rtBits(); + unsigned int zBits = settings_->zBits(); + float rtRange = settings_->rtRange(); + float zRange = settings_->zRange(); + constexpr float cm_to_mm = 10.; // firwmare is in mm and CMSSW in cm. + float zBase = cm_to_mm / (pow(2, zBits) / zRange); + float rTBase = cm_to_mm / (pow(2, rtBits) / rtRange); + + // Number of bits used by DSP in UltraScale-Plus FPGA (where DSP does D = A*B + C) + constexpr unsigned int nDSPa = 27; + //constexpr unsigned int nDSPb = 18; + constexpr unsigned int nDSPc = 48; + constexpr unsigned int nDSPd = 48; + + // unit transformations: firmware uses mm, software uses cm + float BeamWindow = cm_to_mm * beamWindowZ_; + float T_rphi = cm_to_mm * chosenRofPhi_; + float T_rz = cm_to_mm * chosenRofZ_; + + // actual algorithm as used in firmware, mostly using same variable names + float Beam_over_T = BeamWindow / T_rz; + // Value chosen so that number digitized below when calculating "bot" uses most of the nDSPa bits, without overflowing them. This is done assuming reference number of bits for rT and z mentioned above. + unsigned int nShiftA = 24; + // Guess from to keep "bot" in correct range (nDSPa) if number of digitsation bits are changed. + nShiftA += (rtBits - rtBitsRef) - (zBits - zBitsRef); + float Beam_over_T_base = 1. / (1 << nShiftA); + int64_t bot = forceBitWidth(Beam_over_T * rTBase / zBase / Beam_over_T_base, nDSPa); + int64_t bw = forceBitWidth(BeamWindow / zBase / Beam_over_T_base, nDSPc); + float etaSecMid = (settings_->etaRegions()[iEtaReg_] + settings_->etaRegions()[iEtaReg_ + 1]) / 2.0; + float tanlSecMid = 1.0 / tan(2.0 * atan(exp(-etaSecMid))); + // Value chosen so that number digitized below when calculating "tanlSec_Mid" uses most of the nDSPa bits, without overflowing them. This is done assuming reference number of bits for rT and z mentioned above. + unsigned int nShiftB = 16; + // Guess to keep "tanlSec_Mid" in correct range (nDSPa) if number of digitsation bits are changed. + nShiftB += (rtBits - rtBitsRef) - (zBits - zBitsRef); + float tanlSecBase = 1. / (1 << nShiftB); + int64_t tanlSec_Mid = forceBitWidth(int(tanlSecMid * rTBase / zBase / tanlSecBase), nDSPa); + // Number of extra bits used to digitise r instead of rT within GP code, if both encoded as signed int. + constexpr unsigned int nExtraBitsR = 2; + unsigned int rBits = rtBits + nExtraBitsR; + int64_t r = forceBitWidth(rT + T_rphi / rTBase, rBits); + int64_t g = forceBitWidth(bot * r - bw, nDSPd); + int64_t absg = abs(g); + // Number of useful bits left of the nDSPd assigned to "absg" after right-shifting by nShiftA bits. + const unsigned nBitsRemainingA = nDSPd - nShiftA; + int64_t shift_g = forceBitWidth((absg >> nShiftA), nBitsRemainingA); + // Number of bits is sum of those in two numbers being multiplied. + int64_t tlsr = forceBitWidth(tanlSec_Mid * r, nDSPa + rBits); + // Number of useful bits left of (nDSPa + rBits) assigned to "tlsr" after right-shifting by nShiftB bits. + const unsigned nBitsRemainingB = (nDSPa + rBits) - nShiftB; + int64_t shift_tlsr = forceBitWidth((tlsr >> nShiftB), nBitsRemainingB); + + vector insideVec; + insideVec.push_back(z <= (shift_tlsr + shift_g)); + insideVec.push_back(z >= (shift_tlsr - shift_g)); + return insideVec; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/Settings.cc b/L1Trigger/TrackFindingTMTT/src/Settings.cc new file mode 100644 index 0000000000000..bc95f65268a1c --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/Settings.cc @@ -0,0 +1,474 @@ +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "FWCore/Utilities/interface/Exception.h" +#include +#include + +using namespace std; + +namespace tmtt { + + ///=== Hybrid Tracking + ///=== Set config params for HYBRID TRACKING via hard-wired consts to allow use outside CMSSW. + + Settings::Settings() + : //-------------------------------------------------------------------------------------------------- + // TMTT related configuration parameters, including Kalman Filter. + // Meaning of these parameters explained in TrackFindingTMTT/python/TMTrackProducer_Defaults_cfi.py + //-------------------------------------------------------------------------------------------------- + + // General cfg params + enableDigitize_(false), + useApproxB_(true), + bApprox_gradient_(0.886454), + bApprox_intercept_(0.504148), + numPhiNonants_(9), + numPhiSectors_(9), + chosenRofPhi_(55.), // Hourglass radius in r-phi (tracklet) + etaRegions_( + {-2.4, -2.08, -1.68, -1.26, -0.90, -0.62, -0.41, -0.20, 0.0, 0.20, 0.41, 0.62, 0.90, 1.26, 1.68, 2.08, 2.4}), + chosenRofZ_(50.0), // Hourglass radius in r-z (this must be tmtt) + houghMinPt_(2.0), // L1 track pt cut + minStubLayers_(4), + minPtToReduceLayers_(99999.), + reduceLayerID_(true), + minFracMatchStubsOnReco_(-99), + minFracMatchStubsOnTP_(-99), + minNumMatchLayers_(4), + minNumMatchPSLayers_(0), + stubMatchStrict_(false), + + // Kalman filter track fit cfg + kalmanDebugLevel_(0), + //kalmanDebugLevel_(2), // Good for debugging + kalmanMinNumStubs_(4), + kalmanMaxNumStubs_(6), + kalmanRemove2PScut_(true), + kalmanMaxSkipLayersHard_(1), // On "hard" input tracks + kalmanMaxSkipLayersEasy_(2), // On "easy" input tracks + kalmanMaxStubsEasy_(10), // Max. #stubs an input track can have to be defined "easy" + kfLayerVsPtToler_({999., 999., 0.1, 0.1, 0.05, 0.05, 0.05}), + kfLayerVsD0Cut5_({999., 999., 999., 10., 10., 10., 10.}), + kfLayerVsZ0Cut5_({999., 999., 25.5, 25.5, 25.5, 25.5, 25.5}), + kfLayerVsZ0Cut4_({999., 999., 15., 15., 15., 15., 15.}), + kfLayerVsChiSq5_({999., 999., 10., 30., 80., 120., 160.}), + kfLayerVsChiSq4_({999., 999., 10., 30., 80., 120., 160.}), + kalmanMaxStubsPerLayer_(4), // To save resources, consider at most this many stubs per layer per track. + kalmanMultiScattTerm_(0.00075), + kalmanChi2RphiScale_(8), + kalmanHOtilted_(true), + kalmanHOhelixExp_(true), + kalmanHOalpha_(1), + kalmanHOprojZcorr_(1), + kalmanHOfw_(false) { + hybrid_ = true; + magneticField_ = 0.; // Value set later + killScenario_ = 0; // Emulation of dead modules + + if (hybrid_) { + if (not useApproxB_) { + throw cms::Exception("BadConfig") + << "TMTT Settings Error: module tilt angle unknown, so must set useApproxB = true"; + } + } + } + + ///=== TMTT tracking. + ///=== Get configuration parameters from python cfg for TMTT tracking. + + Settings::Settings(const edm::ParameterSet& iConfig) + : + + // See either Analyze_Defaults_cfi.py or Settings.h for description of these parameters. + + //=== Tags for Input ES & ED data. + magneticFieldInputTag_(iConfig.getParameter("magneticFieldInputTag")), + trackerGeometryInputTag_(iConfig.getParameter("trackerGeometryInputTag")), + trackerTopologyInputTag_(iConfig.getParameter("trackerTopologyInputTag")), + ttStubAlgoInputTag_(iConfig.getParameter("ttStubAlgoInputTag")), + + stubInputTag_(iConfig.getParameter("stubInputTag")), + tpInputTag_(iConfig.getParameter("tpInputTag")), + stubTruthInputTag_(iConfig.getParameter("stubTruthInputTag")), + clusterTruthInputTag_(iConfig.getParameter("clusterTruthInputTag")), + genJetInputTag_(iConfig.getParameter("genJetInputTag")), + + //=== Parameter sets for differents types of configuration parameter. + genCuts_(iConfig.getParameter("GenCuts")), + stubCuts_(iConfig.getParameter("StubCuts")), + stubDigitize_(iConfig.getParameter("StubDigitize")), + trackerModuleType_(iConfig.getParameter("TrackerModuleType")), + geometricProc_(iConfig.getParameter("GeometricProc")), + phiSectors_(iConfig.getParameter("PhiSectors")), + etaSectors_(iConfig.getParameter("EtaSectors")), + htArraySpecRphi_(iConfig.getParameter("HTArraySpecRphi")), + htFillingRphi_(iConfig.getParameter("HTFillingRphi")), + rzFilterOpts_(iConfig.getParameter("RZfilterOpts")), + l1TrackDef_(iConfig.getParameter("L1TrackDef")), + dupTrkRemoval_(iConfig.getParameter("DupTrkRemoval")), + trackMatchDef_(iConfig.getParameter("TrackMatchDef")), + trackFitSettings_(iConfig.getParameter("TrackFitSettings")), + deadModuleOpts_(iConfig.getParameter("DeadModuleOpts")), + trackDigi_(iConfig.getParameter("TrackDigi")), + + //=== General settings + + enableMCtruth_(iConfig.getParameter("EnableMCtruth")), + enableHistos_(iConfig.getParameter("EnableHistos")), + enableOutputIntermediateTTTracks_(iConfig.getParameter("EnableOutputIntermediateTTTracks")), + + //=== Cuts on MC truth tracks used for tracking efficiency measurements. + + genMinPt_(genCuts_.getParameter("GenMinPt")), + genMaxAbsEta_(genCuts_.getParameter("GenMaxAbsEta")), + genMaxVertR_(genCuts_.getParameter("GenMaxVertR")), + genMaxVertZ_(genCuts_.getParameter("GenMaxVertZ")), + genMaxD0_(genCuts_.getParameter("GenMaxD0")), + genMaxZ0_(genCuts_.getParameter("GenMaxZ0")), + genMinStubLayers_(genCuts_.getParameter("GenMinStubLayers")), + + //=== Cuts applied to stubs before arriving in L1 track finding board. + + degradeBendRes_(stubCuts_.getParameter("DegradeBendRes")), + maxStubEta_(stubCuts_.getParameter("MaxStubEta")), + killLowPtStubs_(stubCuts_.getParameter("KillLowPtStubs")), + printStubWindows_(stubCuts_.getParameter("PrintStubWindows")), + bendCut_(stubCuts_.getParameter("BendCut")), + bendCutExtra_(stubCuts_.getParameter("BendCutExtra")), + orderStubsByBend_(stubCuts_.getParameter("OrderStubsByBend")), + + //=== Optional stub digitization. + + enableDigitize_(stubDigitize_.getParameter("EnableDigitize")), + + //--- Parameters available in MP board. + phiSectorBits_(stubDigitize_.getParameter("PhiSectorBits")), + phiSBits_(stubDigitize_.getParameter("PhiSBits")), + phiSRange_(stubDigitize_.getParameter("PhiSRange")), + rtBits_(stubDigitize_.getParameter("RtBits")), + rtRange_(stubDigitize_.getParameter("RtRange")), + zBits_(stubDigitize_.getParameter("ZBits")), + zRange_(stubDigitize_.getParameter("ZRange")), + //--- Parameters available in GP board (excluding any in common with MP specified above). + phiNBits_(stubDigitize_.getParameter("PhiNBits")), + phiNRange_(stubDigitize_.getParameter("PhiNRange")), + bendBits_(stubDigitize_.getParameter("BendBits")), + + //=== Tracker Module Type for FW. + pitchVsType_(trackerModuleType_.getParameter>("PitchVsType")), + spaceVsType_(trackerModuleType_.getParameter>("SpaceVsType")), + barrelVsTypeTmp_(trackerModuleType_.getParameter>("BarrelVsType")), + psVsTypeTmp_(trackerModuleType_.getParameter>("PSVsType")), + tiltedVsTypeTmp_(trackerModuleType_.getParameter>("TiltedVsType")), + + //=== Configuration of Geometric Processor. + useApproxB_(geometricProc_.getParameter("UseApproxB")), + bApprox_gradient_(geometricProc_.getParameter("BApprox_gradient")), + bApprox_intercept_(geometricProc_.getParameter("BApprox_intercept")), + + //=== Division of Tracker into phi sectors. + numPhiNonants_(phiSectors_.getParameter("NumPhiNonants")), + numPhiSectors_(phiSectors_.getParameter("NumPhiSectors")), + chosenRofPhi_(phiSectors_.getParameter("ChosenRofPhi")), + useStubPhi_(phiSectors_.getParameter("UseStubPhi")), + useStubPhiTrk_(phiSectors_.getParameter("UseStubPhiTrk")), + assumedPhiTrkRes_(phiSectors_.getParameter("AssumedPhiTrkRes")), + calcPhiTrkRes_(phiSectors_.getParameter("CalcPhiTrkRes")), + + //=== Division of Tracker into eta sectors. + etaRegions_(etaSectors_.getParameter>("EtaRegions")), + chosenRofZ_(etaSectors_.getParameter("ChosenRofZ")), + beamWindowZ_(etaSectors_.getParameter("BeamWindowZ")), + allowOver2EtaSecs_(etaSectors_.getParameter("AllowOver2EtaSecs")), + + //=== r-phi Hough transform array specifications. + houghMinPt_(htArraySpecRphi_.getParameter("HoughMinPt")), + houghNbinsPt_(htArraySpecRphi_.getParameter("HoughNbinsPt")), + houghNbinsPhi_(htArraySpecRphi_.getParameter("HoughNbinsPhi")), + enableMerge2x2_(htArraySpecRphi_.getParameter("EnableMerge2x2")), + maxPtToMerge2x2_(htArraySpecRphi_.getParameter("MaxPtToMerge2x2")), + numSubSecsEta_(htArraySpecRphi_.getParameter("NumSubSecsEta")), + shape_(htArraySpecRphi_.getParameter("Shape")), + miniHTstage_(htArraySpecRphi_.getParameter("MiniHTstage")), + miniHoughNbinsPt_(htArraySpecRphi_.getParameter("MiniHoughNbinsPt")), + miniHoughNbinsPhi_(htArraySpecRphi_.getParameter("MiniHoughNbinsPhi")), + miniHoughMinPt_(htArraySpecRphi_.getParameter("MiniHoughMinPt")), + miniHoughDontKill_(htArraySpecRphi_.getParameter("MiniHoughDontKill")), + miniHoughDontKillMinPt_(htArraySpecRphi_.getParameter("MiniHoughDontKillMinPt")), + miniHoughLoadBalance_(htArraySpecRphi_.getParameter("MiniHoughLoadBalance")), + + //=== Rules governing how stubs are filled into the r-phi Hough Transform array. + killSomeHTCellsRphi_(htFillingRphi_.getParameter("KillSomeHTCellsRphi")), + useBendFilter_(htFillingRphi_.getParameter("UseBendFilter")), + maxStubsInCell_(htFillingRphi_.getParameter("MaxStubsInCell")), + maxStubsInCellMiniHough_(htFillingRphi_.getParameter("MaxStubsInCellMiniHough")), + busySectorKill_(htFillingRphi_.getParameter("BusySectorKill")), + busySectorNumStubs_(htFillingRphi_.getParameter("BusySectorNumStubs")), + busySectorMbinRanges_(htFillingRphi_.getParameter>("BusySectorMbinRanges")), + busySectorMbinOrder_(htFillingRphi_.getParameter>("BusySectorMbinOrder")), + busyInputSectorKill_(htFillingRphi_.getParameter("BusyInputSectorKill")), + busyInputSectorNumStubs_(htFillingRphi_.getParameter("BusyInputSectorNumStubs")), + muxOutputsHT_(htFillingRphi_.getParameter("MuxOutputsHT")), + etaRegWhitelist_(htFillingRphi_.getParameter>("EtaRegWhitelist")), + + //=== Options controlling r-z track filters (or any other track filters run after the Hough transform, as opposed to inside it). + + rzFilterName_(rzFilterOpts_.getParameter("RZFilterName")), + seedResCut_(rzFilterOpts_.getParameter("SeedResCut")), + keepAllSeed_(rzFilterOpts_.getParameter("KeepAllSeed")), + maxSeedCombinations_(rzFilterOpts_.getParameter("MaxSeedCombinations")), + maxGoodSeedCombinations_(rzFilterOpts_.getParameter("MaxGoodSeedCombinations")), + maxSeedsPerStub_(rzFilterOpts_.getParameter("MaxSeedsPerStub")), + zTrkSectorCheck_(rzFilterOpts_.getParameter("zTrkSectorCheck")), + minFilterLayers_(rzFilterOpts_.getParameter("MinFilterLayers")), + + //=== Rules for deciding when the track finding has found an L1 track candidate + + minStubLayers_(l1TrackDef_.getParameter("MinStubLayers")), + minPtToReduceLayers_(l1TrackDef_.getParameter("MinPtToReduceLayers")), + etaSecsReduceLayers_(l1TrackDef_.getParameter>("EtaSecsReduceLayers")), + reduceLayerID_(l1TrackDef_.getParameter("ReducedLayerID")), + + //=== Specification of algorithm to eliminate duplicate tracks. + + dupTrkAlgFit_(dupTrkRemoval_.getParameter("DupTrkAlgFit")), + + //=== Rules for deciding when a reconstructed L1 track matches a MC truth particle (i.e. tracking particle). + + minFracMatchStubsOnReco_(trackMatchDef_.getParameter("MinFracMatchStubsOnReco")), + minFracMatchStubsOnTP_(trackMatchDef_.getParameter("MinFracMatchStubsOnTP")), + minNumMatchLayers_(trackMatchDef_.getParameter("MinNumMatchLayers")), + minNumMatchPSLayers_(trackMatchDef_.getParameter("MinNumMatchPSLayers")), + stubMatchStrict_(trackMatchDef_.getParameter("StubMatchStrict")), + + //=== Track Fitting Settings + + trackFitters_(trackFitSettings_.getParameter>("TrackFitters")), + useRZfilter_(trackFitSettings_.getParameter>("UseRZfilter")), + detailedFitOutput_(trackFitSettings_.getParameter("DetailedFitOutput")), + trackFitCheat_(trackFitSettings_.getParameter("TrackFitCheat")), + // + numTrackFitIterations_(trackFitSettings_.getParameter("NumTrackFitIterations")), + killTrackFitWorstHit_(trackFitSettings_.getParameter("KillTrackFitWorstHit")), + generalResidualCut_(trackFitSettings_.getParameter("GeneralResidualCut")), + killingResidualCut_(trackFitSettings_.getParameter("KillingResidualCut")), + // + digitizeSLR_(trackFitSettings_.getParameter("DigitizeSLR")), + dividerBitsHelix_(trackFitSettings_.getParameter("DividerBitsHelix")), + dividerBitsHelixZ_(trackFitSettings_.getParameter("DividerBitsHelixZ")), + ShiftingBitsDenRPhi_(trackFitSettings_.getParameter("ShiftingBitsDenRPhi")), + ShiftingBitsDenRZ_(trackFitSettings_.getParameter("ShiftingBitsDenRZ")), + ShiftingBitsPt_(trackFitSettings_.getParameter("ShiftingBitsPt")), + ShiftingBitsPhi_(trackFitSettings_.getParameter("ShiftingBitsPhi")), + + ShiftingBitsLambda_(trackFitSettings_.getParameter("ShiftingBitsLambda")), + ShiftingBitsZ0_(trackFitSettings_.getParameter("ShiftingBitsZ0")), + slr_chi2cut_(trackFitSettings_.getParameter("SLR_chi2cut")), + residualCut_(trackFitSettings_.getParameter("ResidualCut")), + // + kalmanDebugLevel_(trackFitSettings_.getParameter("KalmanDebugLevel")), + kalmanMinNumStubs_(trackFitSettings_.getParameter("KalmanMinNumStubs")), + kalmanMaxNumStubs_(trackFitSettings_.getParameter("KalmanMaxNumStubs")), + kalmanAddBeamConstr_(trackFitSettings_.getParameter("KalmanAddBeamConstr")), + kalmanRemove2PScut_(trackFitSettings_.getParameter("KalmanRemove2PScut")), + kalmanMaxSkipLayersHard_(trackFitSettings_.getParameter("KalmanMaxSkipLayersHard")), + kalmanMaxSkipLayersEasy_(trackFitSettings_.getParameter("KalmanMaxSkipLayersEasy")), + kalmanMaxStubsEasy_(trackFitSettings_.getParameter("KalmanMaxStubsEasy")), + + kfLayerVsPtToler_(trackFitSettings_.getParameter>("KFLayerVsPtToler")), + kfLayerVsD0Cut5_(trackFitSettings_.getParameter>("KFLayerVsD0Cut5")), + kfLayerVsZ0Cut5_(trackFitSettings_.getParameter>("KFLayerVsZ0Cut5")), + kfLayerVsZ0Cut4_(trackFitSettings_.getParameter>("KFLayerVsZ0Cut4")), + kfLayerVsChiSq5_(trackFitSettings_.getParameter>("KFLayerVsChiSq5")), + kfLayerVsChiSq4_(trackFitSettings_.getParameter>("KFLayerVsChiSq4")), + + kalmanMaxStubsPerLayer_(trackFitSettings_.getParameter("KalmanMaxStubsPerLayer")), + kalmanMultiScattTerm_(trackFitSettings_.getParameter("KalmanMultiScattTerm")), + kalmanChi2RphiScale_(trackFitSettings_.getParameter("KalmanChi2RphiScale")), + kalmanHOtilted_(trackFitSettings_.getParameter("KalmanHOtilted")), + kalmanHOhelixExp_(trackFitSettings_.getParameter("KalmanHOhelixExp")), + kalmanHOalpha_(trackFitSettings_.getParameter("KalmanHOalpha")), + kalmanHOprojZcorr_(trackFitSettings_.getParameter("KalmanHOprojZcorr")), + kalmanHOfw_(trackFitSettings_.getParameter("KalmanHOfw")), + + //=== Treatment of dead modules. + + killScenario_(deadModuleOpts_.getParameter("KillScenario")), + killRecover_(deadModuleOpts_.getParameter("KillRecover")), + + //=== Track digitisation configuration for various track fitters + + slr_skipTrackDigi_(trackDigi_.getParameter("SLR_skipTrackDigi")), + slr_oneOver2rBits_(trackDigi_.getParameter("SLR_oneOver2rBits")), + slr_oneOver2rRange_(trackDigi_.getParameter("SLR_oneOver2rRange")), + slr_d0Bits_(trackDigi_.getParameter("SLR_d0Bits")), + slr_d0Range_(trackDigi_.getParameter("SLR_d0Range")), + slr_phi0Bits_(trackDigi_.getParameter("SLR_phi0Bits")), + slr_phi0Range_(trackDigi_.getParameter("SLR_phi0Range")), + slr_z0Bits_(trackDigi_.getParameter("SLR_z0Bits")), + slr_z0Range_(trackDigi_.getParameter("SLR_z0Range")), + slr_tanlambdaBits_(trackDigi_.getParameter("SLR_tanlambdaBits")), + slr_tanlambdaRange_(trackDigi_.getParameter("SLR_tanlambdaRange")), + slr_chisquaredBits_(trackDigi_.getParameter("SLR_chisquaredBits")), + slr_chisquaredRange_(trackDigi_.getParameter("SLR_chisquaredRange")), + // + kf_skipTrackDigi_(trackDigi_.getParameter("KF_skipTrackDigi")), + kf_oneOver2rBits_(trackDigi_.getParameter("KF_oneOver2rBits")), + kf_oneOver2rRange_(trackDigi_.getParameter("KF_oneOver2rRange")), + kf_d0Bits_(trackDigi_.getParameter("KF_d0Bits")), + kf_d0Range_(trackDigi_.getParameter("KF_d0Range")), + kf_phi0Bits_(trackDigi_.getParameter("KF_phi0Bits")), + kf_phi0Range_(trackDigi_.getParameter("KF_phi0Range")), + kf_z0Bits_(trackDigi_.getParameter("KF_z0Bits")), + kf_z0Range_(trackDigi_.getParameter("KF_z0Range")), + kf_tanlambdaBits_(trackDigi_.getParameter("KF_tanlambdaBits")), + kf_tanlambdaRange_(trackDigi_.getParameter("KF_tanlambdaRange")), + kf_chisquaredBits_(trackDigi_.getParameter("KF_chisquaredBits")), + kf_chisquaredRange_(trackDigi_.getParameter("KF_chisquaredRange")), + kf_chisquaredBinEdges_(trackDigi_.getParameter>("KF_chisquaredBinEdges")), + // + other_skipTrackDigi_(trackDigi_.getParameter("Other_skipTrackDigi")), + + // Plot options + resPlotOpt_(iConfig.getParameter("ResPlotOpt")), + + // Name of output EDM file if any. + // N.B. This parameter does not appear inside TMTrackProducer_Defaults_cfi.py . It is created inside + // tmtt_tf_analysis_cfg.py . + writeOutEdmFile_(iConfig.getUntrackedParameter("WriteOutEdmFile", true)), + + // Bfield in Tesla. (Unknown at job initiation. Set to true value for each event + magneticField_(0.), + + // Hybrid tracking + hybrid_(iConfig.getParameter("Hybrid")) { + // If user didn't specify any PDG codes, use e,mu,pi,K,p, to avoid picking up unstable particles like Xi-. + vector genPdgIdsUnsigned(genCuts_.getParameter>("GenPdgIds")); + if (genPdgIdsUnsigned.empty()) { + genPdgIdsUnsigned = {11, 13, 211, 321, 2212}; + } + + // For simplicity, user need not distinguish particles from antiparticles in configuration file. + // But here we must store both explicitely in Settings, since TrackingParticleSelector expects them. + for (unsigned int i = 0; i < genPdgIdsUnsigned.size(); i++) { + genPdgIds_.push_back(genPdgIdsUnsigned[i]); + genPdgIds_.push_back(-genPdgIdsUnsigned[i]); + } + + // Clean up list of fitters that require the r-z track filter to be run before them, + // by removing those fitters that are not to be run. + vector useRZfilterTmp; + for (const string& name : useRZfilter_) { + if (std::count(trackFitters_.begin(), trackFitters_.end(), name) > 0) + useRZfilterTmp.push_back(name); + } + useRZfilter_ = useRZfilterTmp; + + // As python cfg doesn't know type "vbool", fix it here. + for (unsigned int i = 0; i < barrelVsTypeTmp_.size(); i++) { + barrelVsType_.push_back(bool(barrelVsTypeTmp_[i])); + psVsType_.push_back(bool(psVsTypeTmp_[i])); + tiltedVsType_.push_back(bool(tiltedVsTypeTmp_[i])); + } + + //--- Sanity checks + + if (!(useStubPhi_ || useStubPhiTrk_)) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - You cant set both UseStubPhi & useStubPhiTrk to false."; + + if (minNumMatchLayers_ > minStubLayers_) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - You are setting the minimum number of layers incorrectly : type A."; + if (genMinStubLayers_ > minStubLayers_) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - You are setting the minimum number of layers incorrectly : type B."; + if (minNumMatchLayers_ > genMinStubLayers_) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - You are setting the minimum number of layers incorrectly : type C."; + + // If reducing number of required layers for high Pt tracks, then above checks must be redone. + bool doReduceLayers = (minPtToReduceLayers_ < 10000. || not etaSecsReduceLayers_.empty()); + if (doReduceLayers && minStubLayers_ > 4) { + if (minNumMatchLayers_ > minStubLayers_ - 1) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - You are setting the minimum number of layers incorrectly : type D."; + if (genMinStubLayers_ > minStubLayers_ - 1) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - You are setting the minimum number of layers incorrectly : type E."; + } + + constexpr float verySmall = 0.1; + if (houghMinPt_ < verySmall) + throw cms::Exception("BadConfig") << "Settings: Invalid cfg parameters -- HoughMinPt must be positive."; + miniHoughMinPt_ = std::max(miniHoughMinPt_, houghMinPt_); + + for (const unsigned int& iEtaReg : etaSecsReduceLayers_) { + if (iEtaReg >= etaRegions_.size()) + throw cms::Exception("BadConfig") << "Settings: You specified an eta sector number in EtaSecsReduceLayers " + "which exceeds the total number of eta sectors! " + << iEtaReg << " " << etaRegions_.size(); + } + + // Chains of m bin ranges for output of HT. + if (!busySectorMbinOrder_.empty()) { + // User has specified an order in which the m bins should be chained together. Check if it makes sense. + if (busySectorMbinOrder_.size() != houghNbinsPt_) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - BusySectorMbinOrder used by HT MUX contains wrong number of " + "elements. Unless you are optimising the MUX, suggest you configure it to an empty vector."; + set mOrderCheck; + for (const unsigned int& m : busySectorMbinOrder_) { + mOrderCheck.insert(m); + } + if (mOrderCheck.size() != houghNbinsPt_) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - BusySectorMbinOrder used by HT MUX contains duplicate elements."; + unsigned int sum_nr = 0; + for (unsigned int nr : busySectorMbinRanges_) { + sum_nr += nr; + } + if (sum_nr != houghNbinsPt_) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - Sum of entries in BusySectorMbinRanges is incorrect."; + } + + if (miniHTstage_) { + if (enableMerge2x2_) + throw cms::Exception("BadConfig") + << "Settings: it is not allowed to enable both MiniHTstage & EnableMerge2x2 options."; + // Options for 2nd stage mini HT + if (shape_ != 0) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - 2nd stage mini HT only allowed for square-shaped cells."; + if (miniHoughNbinsPt_ != 2 || miniHoughNbinsPhi_ != 2) + throw cms::Exception("BadConfig") << "Settings: 2nd mini HT has so dar only been implemented in C++ for 2x2."; + } + + if (enableMerge2x2_) { + if (miniHTstage_) + throw cms::Exception("BadConfig") + << "Settings: it is not allowed to enable both MiniHTstage & EnableMerge2x2 options."; + // Merging of HT cells has not yet been implemented for diamond or hexagonal HT cell shape. + if (enableMerge2x2_ && shape_ != 0) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - merging only allowed for square-shaped cells."; + } + + // Check Kalman fit params. + if (kalmanMaxNumStubs_ < kalmanMinNumStubs_) + throw cms::Exception("BadConfig") + << "Settings: Invalid cfg parameters - KalmanMaxNumStubs is less than KalmanMaxNumStubs."; + } + + bool Settings::isHTRPhiEtaRegWhitelisted(unsigned const iEtaReg) const { + bool whitelisted = true; + + bool const whitelist_enabled = (!etaRegWhitelist_.empty()); + if (whitelist_enabled) { + whitelisted = (std::count(etaRegWhitelist_.begin(), etaRegWhitelist_.end(), iEtaReg) > 0); + } + + return whitelisted; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/SimpleLR4.cc b/L1Trigger/TrackFindingTMTT/src/SimpleLR4.cc new file mode 100644 index 0000000000000..6ea41e6077a3b --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/SimpleLR4.cc @@ -0,0 +1,489 @@ +///=== This is the global Linear Regression for 4 helix parameters track fit algorithm. + +///=== Written by: Davide Cieri + +#include "L1Trigger/TrackFindingTMTT/interface/SimpleLR4.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1fittedTrack.h" +#include "L1Trigger/TrackFindingTMTT/interface/L1track3D.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" +#include "DataFormats/Math/interface/deltaPhi.h" + +#include +#include +#include +#include + +using namespace std; + +namespace tmtt { + + SimpleLR4::SimpleLR4(const Settings* settings) : TrackFitGeneric(settings) { + // Initialize digitization parameters + phiMult_ = pow(2., settings_->phiSBits()) / settings_->phiSRange(); + rTMult_ = pow(2., settings_->rtBits()) / settings_->rtRange(); + zMult_ = pow(2., settings_->zBits()) / settings_->zRange(); + z0Mult_ = pow(2., settings_->slr_z0Bits()) / settings_->slr_z0Range(); + phiTMult_ = pow(2., settings_->slr_phi0Bits()) / settings_->slr_phi0Range(); + + qOverPtMult_ = pow(2., settings_->slr_oneOver2rBits()) / settings_->slr_oneOver2rRange(); + tanLambdaMult_ = pow(2., settings_->slr_tanlambdaBits()) / settings_->slr_tanlambdaRange(); + chi2Mult_ = pow(2., settings_->slr_chisquaredBits()) / settings_->slr_chisquaredRange(); + + numeratorPtMult_ = rTMult_ * phiMult_; + numeratorPhiMult_ = rTMult_ * rTMult_ * phiMult_; + numeratorZ0Mult_ = rTMult_ * rTMult_ * z0Mult_; + numeratorLambdaMult_ = rTMult_ * z0Mult_; + denominatorMult_ = rTMult_ * rTMult_; + resMult_ = rTMult_ * qOverPtMult_; + + dividerBitsHelix_ = settings_->dividerBitsHelix(); + dividerBitsHelixZ_ = settings_->dividerBitsHelixZ(); + shiftingBitsDenRPhi_ = settings_->ShiftingBitsDenRPhi(); + shiftingBitsDenRZ_ = settings_->ShiftingBitsDenRZ(); + shiftingBitsPhi_ = settings_->ShiftingBitsPhi(); + shiftingBitsz0_ = settings_->ShiftingBitsZ0(); + shiftingBitsPt_ = settings_->ShiftingBitsPt(); + shiftingBitsLambda_ = settings_->ShiftingBitsLambda(); + digitize_ = settings_->digitizeSLR() and settings_->enableDigitize(); + + phiSectorWidth_ = 2. * M_PI / float(settings_->numPhiSectors()); + phiNonantWidth_ = 2. * M_PI / float(settings_->numPhiNonants()); + + chi2cut_ = settings_->slr_chi2cut(); + chosenRofPhi_ = settings_->chosenRofPhi(); + if (digitize_) + chosenRofPhi_ = floor(chosenRofPhi_ * rTMult_) / rTMult_; + + debug_ = false; // Enable debug printout. + }; + + static bool pair_compare(std::pair a, std::pair b) { + return (a.second < b.second); + } + + L1fittedTrack SimpleLR4::fit(const L1track3D& l1track3D) { + if (debug_) + PrintL1trk() << "=============== FITTING SimpleLR TRACK ===================="; + + minStubLayersRed_ = Utility::numLayerCut(Utility::AlgoStep::FIT, + settings_, + l1track3D.iPhiSec(), + l1track3D.iEtaReg(), + std::abs(l1track3D.qOverPt()), + l1track3D.eta()); + + invPtToDPhi_ = -settings_->invPtToDphi(); + + double phiCentreSec0 = -0.5 * phiNonantWidth_ + 0.5 * phiSectorWidth_; + phiSectorCentre_ = phiSectorWidth_ * double(l1track3D.iPhiSec()) - phiCentreSec0; + + if (digitize_) + phiSectorCentre_ = floor(phiSectorCentre_ * phiTMult_) / phiTMult_; + + // Inizialise track fit parameters + double qOverPt = 0.; + double phiT = 0.; + double phi0 = 0.; + double z0 = 0.; + double zT = 0.; + double tanLambda = 0.; + + // Inizialise Sums + double SumRPhi = 0.; + double SumR = 0.; + double SumPhi = 0.; + double SumR2 = 0.; + double SumRZ = 0.; + double SumZ = 0.; + + unsigned int numStubs = 0; + // Calc helix parameters on Rphi Plane (STEP 1) + // This loop calculates the sums needed to calculate the numerators and the denominator to compute the helix parameters in the R-Phi plane (q/pT, phiT) + for (Stub* stub : l1track3D.stubs()) { + numStubs++; + + if (digitize_) { + const DigitalStub* digiStub = stub->digitalStub(); + + SumRPhi = SumRPhi + digiStub->rt_SF_TF() * digiStub->phiS(); + SumR = SumR + digiStub->rt_SF_TF(); + SumPhi = SumPhi + digiStub->phiS(); + SumR2 = SumR2 + digiStub->rt_SF_TF() * digiStub->rt_SF_TF(); + if (debug_) + PrintL1trk() << "Input stub (digi): phiS " << digiStub->iDigi_PhiS() << " rT " << digiStub->iDigi_Rt() + << " z " << digiStub->iDigi_Z(); + } else { + float phi = 0; + if (l1track3D.iPhiSec() == 0 and stub->phi() > 0) { + phi = stub->phi() - 2 * M_PI; + } else if (l1track3D.iPhiSec() == settings_->numPhiSectors() and stub->phi() < 0) { + phi = stub->phi() + 2 * M_PI; + } else { + phi = stub->phi(); + } + SumRPhi = SumRPhi + stub->r() * phi; + SumR = SumR + stub->r(); + SumPhi = SumPhi + phi; + SumR2 = SumR2 + stub->r() * stub->r(); + if (debug_) + PrintL1trk() << "InputStub (float): phi " << phi << " r " << stub->r() << " z " << stub->z(); + } + } + + double numeratorPt, digiNumeratorPt; + double denominator, digiDenominator; + double numeratorPhi, digiNumeratorPhi; + double reciprocal, digiReciprocal; + double numeratorZ0; + double numeratorLambda; + + digiNumeratorPt = (numStubs * SumRPhi - SumR * SumPhi); + digiDenominator = (numStubs * SumR2 - SumR * SumR); + digiNumeratorPhi = (SumR2 * SumPhi - SumR * SumRPhi); + + if (!digitize_) { + qOverPt = (numStubs * SumRPhi - SumR * SumPhi) / (numStubs * SumR2 - SumR * SumR); + phi0 = (SumR2 * SumPhi - SumR * SumRPhi) / (numStubs * SumR2 - SumR * SumR); + } else { + digiNumeratorPt /= pow(2., shiftingBitsPt_); + digiNumeratorPt = floor(digiNumeratorPt * numeratorPtMult_); + numeratorPt = digiNumeratorPt / numeratorPtMult_; + + digiNumeratorPhi /= pow(2., shiftingBitsPhi_); + digiNumeratorPhi = floor(digiNumeratorPhi * numeratorPhiMult_); + numeratorPhi = digiNumeratorPhi / numeratorPhiMult_; + + digiDenominator /= pow(2., shiftingBitsDenRPhi_); + digiDenominator = (floor(digiDenominator * denominatorMult_) + 0.5); + denominator = digiDenominator / denominatorMult_; + digiReciprocal = (pow(2., dividerBitsHelix_) - 1) / (denominator); // To be moved + digiReciprocal = floor(digiReciprocal / denominatorMult_); + reciprocal = digiReciprocal * denominatorMult_; + + qOverPt = numeratorPt * reciprocal / pow(2., dividerBitsHelix_ + shiftingBitsDenRPhi_ - shiftingBitsPt_); + phiT = numeratorPhi * reciprocal / pow(2., dividerBitsHelix_ + shiftingBitsDenRPhi_ - shiftingBitsPhi_); + + qOverPt = floor(qOverPt * qOverPtMult_) / (qOverPtMult_); + phiT = floor(phiT * phiTMult_) / phiTMult_; + } + + if (debug_) { + if (digitize_) { + PrintL1trk() << setw(10) << "Input helix (digi): qOverPt = " << qOverPt << " (" << floor(qOverPt * qOverPtMult_) + << "), phiT = " << phiT << " (" << floor(phiT * phiTMult_) << ") "; + } else { + PrintL1trk() << "Input Helix (float): qOverPt = " << qOverPt << " phi0 " << phi0; + } + } + + // ================== RESIDUAL CALCULATION ON RPHI ======================== + std::vector > vRes; + unsigned int psStubs = 0; + for (Stub* stub : l1track3D.stubs()) { + if (stub->psModule()) + psStubs++; + double ResPhi; + + if (digitize_) { + const DigitalStub* digiStub = stub->digitalStub(); + + ResPhi = + digiStub->iDigi_PhiS() * pow(2., shiftingBitsDenRPhi_ - shiftingBitsPt_) - + floor(phiT * phiTMult_) * + pow(2., shiftingBitsDenRPhi_ - shiftingBitsPt_ - settings_->slr_phi0Bits() + settings_->phiSBits()) - + floor(qOverPt * qOverPtMult_) * digiStub->iDigi_Rt(); + + ResPhi = floor(ResPhi) / resMult_; + } + + else { + ResPhi = reco::deltaPhi(stub->phi(), phi0 + qOverPt * stub->r()); + } + + double Res = std::abs(ResPhi); + + std::pair ResStubPair(stub, Res); + vRes.push_back(ResStubPair); + if (debug_) { + if (stub->assocTP() != nullptr) + PrintL1trk() << " Stub rphi residual " << Res << " TP " << stub->assocTP()->index(); + else + PrintL1trk() << " Stub rphi residual " << Res << " TP nullptr"; + } + } + + double largestResidual = 9999.; + // Find largest residuals + while (vRes.size() > minStubLayersRed_ and largestResidual > settings_->ResidualCut()) { + std::vector >::iterator maxResIt = max_element(vRes.begin(), vRes.end(), pair_compare); + largestResidual = (*maxResIt).second; + if (debug_) + PrintL1trk() << "Largest Residual " << largestResidual; + + if (largestResidual > settings_->ResidualCut()) { + if ((*maxResIt).first->psModule()) { + if (psStubs > 2) { + if (debug_) + PrintL1trk() << "removing PS residual " << (*maxResIt).second; + vRes.erase(maxResIt); + psStubs--; + } else { + if (debug_) + PrintL1trk() << "residual " << (*maxResIt).second << " set to -1. "; + (*maxResIt).second = -1.; + } + } else { + vRes.erase(maxResIt); + if (debug_) + PrintL1trk() << "removing residual " << (*maxResIt).second; + } + } + } + + std::vector fitStubs; + for (std::pair ResStubPair : vRes) { + fitStubs.push_back(ResStubPair.first); + } + + phiT = 0.; + zT = 0.; + + SumRPhi = 0.; + SumR = 0.; + SumPhi = 0.; + SumR2 = 0.; + SumRZ = 0.; + SumZ = 0.; + double SumR_ps = 0.; + double SumR2_ps = 0.; + + numStubs = 0; + psStubs = 0; + + for (const Stub* stub : fitStubs) { + if (stub->psModule()) + psStubs++; + + numStubs++; + if (digitize_) { + const DigitalStub* digiStub = stub->digitalStub(); + SumRPhi += digiStub->rt_SF_TF() * digiStub->phiS(); + SumR += digiStub->rt_SF_TF(); + SumPhi += digiStub->phiS(); + SumR2 += digiStub->rt_SF_TF() * digiStub->rt_SF_TF(); + if (stub->psModule()) { + SumRZ += digiStub->rt_SF_TF() * digiStub->z(); + SumZ += digiStub->z(); + SumR_ps += digiStub->rt_SF_TF(); + SumR2_ps += digiStub->rt_SF_TF() * digiStub->rt_SF_TF(); + } + if (debug_) { + PrintL1trk() << "phiS " << digiStub->iDigi_PhiS() << " rT " << digiStub->iDigi_Rt() << " z " + << digiStub->iDigi_Z(); + } + } else { + float phi = 0; + if (l1track3D.iPhiSec() == 0 and stub->phi() > 0) { + phi = stub->phi() - 2 * M_PI; + } else if (l1track3D.iPhiSec() == settings_->numPhiSectors() and stub->phi() < 0) { + phi = stub->phi() + 2 * M_PI; + } else { + phi = stub->phi(); + } + + SumRPhi += stub->r() * phi; + SumR += stub->r(); + SumPhi += phi; + SumR2 += stub->r() * stub->r(); + if (stub->psModule()) { + SumRZ += stub->r() * stub->z(); + SumZ += stub->z(); + SumR_ps += stub->r(); + SumR2_ps += stub->r() * stub->r(); + } + if (debug_) + PrintL1trk() << "phi " << phi << " r " << stub->r() << " z " << stub->z(); + } + } + + numeratorZ0 = (SumR2_ps * SumZ - SumR_ps * SumRZ); + numeratorLambda = (psStubs * SumRZ - SumR_ps * SumZ); + numeratorPt = (numStubs * SumRPhi - SumR * SumPhi); + denominator = (numStubs * SumR2 - SumR * SumR); + double denominatorZ = (psStubs * SumR2_ps - SumR_ps * SumR_ps); + numeratorPhi = (SumR2 * SumPhi - SumR * SumRPhi); + double reciprocalZ; + if (!digitize_) { + z0 = numeratorZ0 / denominatorZ; + tanLambda = numeratorLambda / denominatorZ; + qOverPt = (numStubs * SumRPhi - SumR * SumPhi) / (numStubs * SumR2 - SumR * SumR); + phi0 = (SumR2 * SumPhi - SumR * SumRPhi) / (numStubs * SumR2 - SumR * SumR); + } else { + numeratorPt /= pow(2., shiftingBitsPt_); + numeratorPt = floor(numeratorPt * numeratorPtMult_) / numeratorPtMult_; + + numeratorPhi /= pow(2., shiftingBitsPhi_); + numeratorPhi = floor(numeratorPhi * numeratorPhiMult_) / numeratorPhiMult_; + + numeratorLambda /= pow(2., shiftingBitsLambda_); + numeratorLambda = floor(numeratorLambda * numeratorLambdaMult_) / numeratorLambdaMult_; + + numeratorZ0 /= pow(2., shiftingBitsz0_); + numeratorZ0 = floor(numeratorZ0 * numeratorZ0Mult_) / numeratorZ0Mult_; + + denominator /= pow(2., shiftingBitsDenRPhi_); + denominator = (floor(denominator * denominatorMult_) + 0.5) / denominatorMult_; + reciprocal = (pow(2., dividerBitsHelix_) - 1) / (denominator); + reciprocal = floor(reciprocal / denominatorMult_) * denominatorMult_; + + denominatorZ /= pow(2., shiftingBitsDenRZ_); + denominatorZ = (floor(denominatorZ * denominatorMult_) + 0.5) / denominatorMult_; + reciprocalZ = (pow(2., dividerBitsHelixZ_) - 1) / (denominatorZ); + reciprocalZ = floor(reciprocalZ / denominatorMult_) * denominatorMult_; + + qOverPt = numeratorPt * reciprocal / pow(2., dividerBitsHelix_ + shiftingBitsDenRPhi_ - shiftingBitsPt_); + phiT = numeratorPhi * reciprocal / pow(2., dividerBitsHelix_ + shiftingBitsDenRPhi_ - shiftingBitsPhi_); + + tanLambda = + numeratorLambda * reciprocalZ / pow(2., dividerBitsHelixZ_ + shiftingBitsDenRZ_ - shiftingBitsLambda_); + zT = numeratorZ0 * reciprocalZ / pow(2., dividerBitsHelixZ_ + shiftingBitsDenRZ_ - shiftingBitsz0_); + + phi0 = phiSectorCentre_ + phiT - qOverPt * settings_->chosenRofPhi(); + z0 = zT - tanLambda * settings_->chosenRofPhi(); + + qOverPt = floor(qOverPt * qOverPtMult_) / qOverPtMult_; + phiT = floor(phiT * phiTMult_) / phiTMult_; + } + + if (debug_ and digitize_) { + PrintL1trk() << "HT mbin " << int(l1track3D.cellLocationHT().first) - 16 << " cbin " + << int(l1track3D.cellLocationHT().second) - 32 << " iPhi " << l1track3D.iPhiSec() << " iEta " + << l1track3D.iEtaReg(); + PrintL1trk() << "Second Helix variables: numeratorPt = " << numeratorPt << ", numeratorPhi = " << numeratorPhi + << ", numeratorZ0 = " << numeratorZ0 << " numeratorLambda = " << numeratorLambda + << " denominator = " << denominator << " reciprocal = " << reciprocal + << " denominatorZ = " << denominatorZ << " reciprocalZ = " << reciprocalZ; + PrintL1trk() << setw(10) << "Final Helix parameters: qOverPt = " << qOverPt << " (" + << floor(qOverPt * qOverPtMult_) << "), phiT = " << phiT << " (" << floor(phiT * phiTMult_) + << "), zT = " << zT << " (" << floor(zT * z0Mult_) << "), tanLambda = " << tanLambda << " (" + << floor(tanLambda * tanLambdaMult_) << ")" + << " z0 " << z0; + } else if (debug_) { + PrintL1trk() << setw(10) << "Final Helix parameters: qOverPt = " << qOverPt << ", phi0 = " << phi0 + << ", z0 = " << z0 << ", tanLambda = " << tanLambda; + } + + double chi2_phi = 0.; + double chi2_z = 0.; + + for (const Stub* stub : fitStubs) { + double ResPhi = 0.; + double ResZ = 0.; + if (digitize_) { + const DigitalStub* digiStub = stub->digitalStub(); + ResPhi = digiStub->phiS() - phiT - qOverPt * digiStub->rt_SF_TF(); + ResZ = digiStub->z() - zT - tanLambda * digiStub->rt_SF_TF(); + } else { + ResPhi = reco::deltaPhi(stub->phi(), phi0 + qOverPt * stub->r()); + ResZ = stub->z() - z0 - tanLambda * stub->r(); + } + + double RPhiSigma = 0.0002; + float RZSigma = stub->sigmaZ() + std::abs(tanLambda) * stub->sigmaR(); + + if (not stub->barrel()) + RPhiSigma = 0.0004; + + if (digitize_) { + RPhiSigma = floor(RPhiSigma * phiMult_) / phiMult_; + } + + ResPhi /= RPhiSigma; + ResZ /= RZSigma; + + chi2_phi += std::abs(ResPhi * ResPhi); + chi2_z += std::abs(ResZ * ResZ); + if (debug_) { + PrintL1trk() << "Stub ResPhi " << ResPhi * RPhiSigma << " ResSigma " << RPhiSigma << " Res " << ResPhi + << " chi2 " << chi2_phi; + PrintL1trk() << "Stub ResZ " << ResZ * RZSigma << " ResSigma " << RZSigma << " Res " << ResZ << " chi2 " + << chi2_z; + } + } + qOverPt /= invPtToDPhi_; + + bool accepted = false; + + //double chi2 = chi2_phi; // Ignore r-z residuals due to poor 2S resolution? + double chi2 = chi2_phi + chi2_z; + if (digitize_) + chi2 = floor(chi2 * chi2Mult_) / chi2Mult_; + + constexpr unsigned int nHelixPar = 4; + float dof = 2 * fitStubs.size() - nHelixPar; + float chi2dof = chi2 / dof; + if (chi2 < chi2cut_) + accepted = true; + + if (debug_) + PrintL1trk() << "qOverPt " << qOverPt << " phiT " << phiT; + + // This condition can only happen if cfg param TrackFitCheat = True. + if (fitStubs.size() < minStubLayersRed_) + accepted = false; + + // Kinematic cuts -- NOT YET IN FIRMWARE!!! + constexpr float tolerance = 0.1; + if (std::abs(qOverPt) > 1. / (settings_->houghMinPt() - tolerance)) + accepted = false; + if (std::abs(z0) > 20.) + accepted = false; + + if (accepted) { + // Create the L1fittedTrack object + const unsigned int hitPattern = 0; // FIX: Needs setting + L1fittedTrack fitTrk( + settings_, &l1track3D, fitStubs, hitPattern, qOverPt, 0., phi0, z0, tanLambda, chi2_phi, chi2_z, nHelixPar); + + if (settings_->enableDigitize()) + fitTrk.digitizeTrack("SimpleLR4"); + + if (debug_ and digitize_) { + PrintL1trk() << "Digitized parameters "; + PrintL1trk() << "HT mbin " << int(l1track3D.cellLocationHT().first) - 16 << " cbin " + << int(l1track3D.cellLocationHT().second) - 32 << " iPhi " << l1track3D.iPhiSec() << " iEta " + << l1track3D.iEtaReg(); + PrintL1trk() << setw(10) << "First Helix parameters: qOverPt = " << fitTrk.qOverPt() << " oneOver2r " + << fitTrk.digitaltrack()->oneOver2r() << " (" + << floor(fitTrk.digitaltrack()->oneOver2r() * qOverPtMult_) + << "), phi0 = " << fitTrk.digitaltrack()->phi0() << " (" << fitTrk.digitaltrack()->iDigi_phi0rel() + << "), zT = " << zT << " (" << floor(zT * z0Mult_) << "), tanLambda = " << tanLambda << " (" + << floor(tanLambda * tanLambdaMult_) << ")"; + } + + if (debug_) { + PrintL1trk() << "FitTrack helix parameters " << int(fitTrk.cellLocationFit().first) - 16 << ", " + << int(fitTrk.cellLocationFit().second) - 32 << " HT parameters " + << int(fitTrk.cellLocationHT().first) - 16 << ", " << int(fitTrk.cellLocationHT().second) - 32; + + if (fitTrk.matchedTP() != nullptr) { + PrintL1trk() << "True track: chi2/ndf " << chi2dof; + PrintL1trk() << "TP qOverPt " << fitTrk.matchedTP()->qOverPt() << " phi0 " << fitTrk.matchedTP()->phi0(); + if (!accepted) + PrintL1trk() << "Track rejected " << chi2 << " chi2/ndof " << chi2dof; + } else { + PrintL1trk() << "Fake track!!! " << chi2 << " chi2/ndof " << chi2dof; + } + PrintL1trk() << "layers in track " << fitTrk.numLayers(); + } + + return fitTrk; + + } else { + L1fittedTrack rejectedTrk; + return rejectedTrk; + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/Stub.cc b/L1Trigger/TrackFindingTMTT/src/Stub.cc new file mode 100644 index 0000000000000..988096d9f31d2 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/Stub.cc @@ -0,0 +1,389 @@ +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "Geometry/CommonTopologies/interface/PixelGeomDetUnit.h" +#include "Geometry/CommonTopologies/interface/PixelTopology.h" +#include "DataFormats/Math/interface/deltaPhi.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/StubKiller.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" + +#include + +using namespace std; + +namespace tmtt { + + //=== Hybrid L1 tracking: stub constructor. + + Stub::Stub(const Settings* settings, + unsigned int idStub, + double phi, + double r, + double z, + double bend, + unsigned int iphi, + double alpha, + unsigned int layerId, + unsigned int iPhiSec, + bool psModule, + bool barrel, + bool tiltedBarrel, + float stripPitch, + float stripLength, + unsigned int nStrips) + : index_in_vStubs_(idStub), // A unique ID to label the stub. + phi_(phi), + r_(r), + z_(z), + bend_(bend), + iphi_(iphi), + alpha_(alpha), + digitalStub_(std::make_unique(settings, r, phi, z, iPhiSec)), + layerId_(layerId), + layerIdReduced_(TrackerModule::calcLayerIdReduced(layerId)), + stripPitch_(stripPitch), + stripLength_(stripLength), + nStrips_(nStrips), + psModule_(psModule), + barrel_(barrel), + tiltedBarrel_(tiltedBarrel) {} + + //=== TMTT L1 tracking: stub constructor. + + Stub::Stub(const TTStubRef& ttStubRef, + unsigned int index_in_vStubs, + const Settings* settings, + const TrackerTopology* trackerTopology, + const TrackerModule* trackerModule, + const DegradeBend* degradeBend, + const StubKiller* stubKiller) + : ttStubRef_(ttStubRef), + settings_(settings), + index_in_vStubs_(index_in_vStubs), + assocTP_(nullptr), // Initialize in case job is using no MC truth info. + lastDigiStep_(Stub::DigiStage::NONE), + digitizeWarningsOn_(true), + trackerModule_(trackerModule), // Info about tracker module containing stub + degradeBend_(degradeBend), // Used to degrade stub bend information. + // Module related variables (need to be stored for Hybrid) + layerId_(trackerModule->layerId()), + layerIdReduced_(trackerModule->layerIdReduced()), + tiltAngle_(trackerModule->tiltAngle()), + stripPitch_(trackerModule->stripPitch()), + stripLength_(trackerModule->stripLength()), + nStrips_(trackerModule->nStrips()), + psModule_(trackerModule->psModule()), + barrel_(trackerModule->barrel()), + tiltedBarrel_(trackerModule->tiltedBarrel()) { + // Get coordinates of stub. + const TTStub* ttStubP = ttStubRef_.get(); + + const PixelGeomDetUnit* specDet = trackerModule_->specDet(); + const PixelTopology* specTopol = trackerModule_->specTopol(); + MeasurementPoint measurementPoint = ttStubRef_->clusterRef(0)->findAverageLocalCoordinatesCentered(); + LocalPoint clustlp = specTopol->localPosition(measurementPoint); + GlobalPoint pos = specDet->surface().toGlobal(clustlp); + + phi_ = pos.phi(); + r_ = pos.perp(); + z_ = pos.z(); + + // Get the coordinates of the two clusters that make up this stub, measured in units of strip pitch, and measured + // in the local frame of the sensor. They have a granularity of 0.5*pitch. + for (unsigned int iClus = 0; iClus <= 1; iClus++) { // Loop over two clusters in stub. + localU_cluster_[iClus] = ttStubP->clusterRef(iClus)->findAverageLocalCoordinatesCentered().x(); + localV_cluster_[iClus] = ttStubP->clusterRef(iClus)->findAverageLocalCoordinatesCentered().y(); + } + + // Get location of stub in module in units of strip number (or pixel number along finest granularity axis). + // Range from 0 to (nStrips - 1) inclusive. + // N.B. Since iphi is integer, this degrades the granularity by a factor 2. This seems silly, but track fit wants it. + iphi_ = localU_cluster_[0]; // granularity 1*strip (unclear why we want to degrade it ...) + + // Determine alpha correction for non-radial strips in endcap 2S modules. + // (If true hit at larger r than stub r by deltaR, then stub phi needs correcting by +alpha*deltaR). + alpha_ = 0.; + if ((not barrel()) && (not psModule())) { + float fracPosInModule = (float(iphi_) - 0.5 * float(nStrips())) / float(nStrips()); + float phiRelToModule = trackerModule_->sensorWidth() * fracPosInModule / r_; + if (z_ < 0) + phiRelToModule *= -1; + if (trackerModule_->outerModuleAtSmallerR()) + phiRelToModule *= -1; // Module flipped. + // If true hit at larger r than stub r by deltaR, then stub phi needs correcting by +alpha*deltaR. + alpha_ = -phiRelToModule / r_; + } + + // Calculate variables giving ratio of track intercept angle to stub bend. + this->calcDphiOverBend(); + + // Get stub bend that is available in front-end electronics, where bend is displacement between + // two hits in stubs in units of strip pitch. + bendInFrontend_ = ttStubRef_->bendFE(); + if ((not barrel()) && pos.z() > 0) + bendInFrontend_ *= -1; + if (barrel()) + bendInFrontend_ *= -1; + + // Get stub bend that is available in off-detector electronics, allowing for degredation of + // bend resolution due to bit encoding by FE chip if required. + numMergedBend_ = 1; // Number of bend values merged into single degraded one. + if (settings->degradeBendRes() == 2) { + float degradedBend; // degraded bend + // This returns values of degradedBend & numMergedBend_ + this->degradeResolution(bendInFrontend_, degradedBend, numMergedBend_); + bend_ = degradedBend; + } else if (settings->degradeBendRes() == 1) { + bend_ = ttStubRef_->bendBE(); // Degraded bend from official CMS recipe. + if ((not barrel()) && pos.z() > 0) + bend_ *= -1; + if (barrel()) + bend_ *= -1; + } else { + bend_ = bendInFrontend_; + } + + // Fill frontendPass_ flag, indicating if frontend readout electronics will output this stub. + this->setFrontend(stubKiller); + + // Calculate bin range along q/Pt axis of r-phi Hough transform array consistent with bend of this stub. + this->calcQoverPtrange(); + + // Initialize truth info to false in case job is using no MC truth info. + for (unsigned int iClus = 0; iClus <= 1; iClus++) { + assocTPofCluster_[iClus] = nullptr; + } + } + + //=== Calculate bin range along q/Pt axis of r-phi Hough transform array consistent with bend of this stub. + + void Stub::calcQoverPtrange() { + // First determine bin range along q/Pt axis of HT array + // (Use "int" as nasty things happen if multiply "int" and "unsigned int"). + const int nbinsPt = (int)settings_->houghNbinsPt(); + const int min_array_bin = 0; + const int max_array_bin = nbinsPt - 1; + // Now calculate range of q/Pt bins allowed by bend filter. + float qOverPtMin = this->qOverPtOverBend() * (this->bend() - this->bendCut()); + float qOverPtMax = this->qOverPtOverBend() * (this->bend() + this->bendCut()); + int houghNbinsPt = settings_->houghNbinsPt(); + const float houghMaxInvPt = 1. / settings_->houghMinPt(); + float qOverPtBinSize = (2. * houghMaxInvPt) / houghNbinsPt; + if (settings_->shape() == 2 || settings_->shape() == 1 || settings_->shape() == 3) // Non-square HT cells. + qOverPtBinSize = 2. * houghMaxInvPt / (houghNbinsPt - 1); + // Convert to bin number along q/Pt axis of HT array. + // N.B. For square HT cells, setting "tmp = -0.5" causeas cell to be accepted if q/Pt at its centre is consistent + // with the stub bend. Instead using "tmp = 0.0" accepts cells if q/Pt at any point in cell is consistent with bend. + // So if you use change from -0.5 to 0.0, you have to tighten the bend cut (by ~0.05) to get similar performance. + // Decision to set tmp = 0.0 taken in softare & GP firmware on 9th August 2016. + + float tmp = (settings_->shape() == 2 || settings_->shape() == 1 || settings_->shape() == 3) ? 1. : 0.; + int min_bin = std::floor(-tmp + (qOverPtMin + houghMaxInvPt) / qOverPtBinSize); + int max_bin = std::floor(tmp + (qOverPtMax + houghMaxInvPt) / qOverPtBinSize); + + // Limit it to range of HT array. + min_bin = max(min_bin, min_array_bin); + max_bin = min(max_bin, max_array_bin); + // If min_bin > max_bin at this stage, it means that the Pt estimated from the bend is below the cutoff for track-finding. + // Keep min_bin > max_bin, so such stubs can be rejected, but set both variables to values inside the HT bin range. + if (min_bin > max_bin) { + min_bin = max_array_bin; + max_bin = min_array_bin; + } + min_qOverPt_bin_ = (unsigned int)min_bin; + max_qOverPt_bin_ = (unsigned int)max_bin; + } + + //=== Digitize stub for input to Geographic Processor, with digitized phi coord. measured relative to closest phi sector. + //=== (This approximation is valid if their are an integer number of digitisation bins inside each phi nonant). + + void Stub::digitize(unsigned int iPhiSec, Stub::DigiStage digiStep) { + if (settings_->enableDigitize()) { + bool updated = true; + if (not digitalStub_) { + // Digitize stub if not yet done. + digitalStub_ = + std::make_unique(settings_, phi_, r_, z_, min_qOverPt_bin_, max_qOverPt_bin_, bend_, iPhiSec); + } else { + // If digitization already done, redo phi digi if phi sector has changed. + updated = digitalStub_->changePhiSec(iPhiSec); + } + + // Save CPU by only updating if something has changed. + if (updated || digiStep != lastDigiStep_) { + lastDigiStep_ = digiStep; + + // Replace stub coords with those degraded by digitization process. + if (digiStep == DigiStage::GP) { + phi_ = digitalStub_->phi_GP(); + } else { + phi_ = digitalStub_->phi_HT_TF(); + } + if (digiStep == DigiStage::GP || digiStep == DigiStage::HT) { + r_ = digitalStub_->r_GP_HT(); + } else { + r_ = digitalStub_->r_SF_TF(); + } + z_ = digitalStub_->z(); + bend_ = digitalStub_->bend(); + + // Update data members that depend on updated coords. + // (Logically part of digitisation, so disable warnings) + digitizeWarningsOn_ = false; + if (digiStep == DigiStage::GP) + this->calcDphiOverBend(); + if (digiStep == DigiStage::HT) + this->calcQoverPtrange(); + digitizeWarningsOn_ = true; + } + } + } + + //=== Degrade assumed stub bend resolution. + //=== And return an integer indicating how many values of bend are merged into this single one. + + void Stub::degradeResolution(float bend, float& degradedBend, unsigned int& num) const { + // If TMTT code is tightening official CMS FE stub window cuts, then calculate TMTT stub windows. + float windowFE; + if (settings_->killLowPtStubs()) { + // Window size corresponding to Pt cut used for tracking. + float invPtMax = 1. / (settings_->houghMinPt()); + windowFE = invPtMax / std::abs(this->qOverPtOverBend()); + // Increase half-indow size to allow for resolution in bend. + windowFE += this->bendCutInFrontend(); + } else { + windowFE = rejectedStubBend_; // TMTT is not tightening windows. + } + + degradeBend_->degrade(bend, psModule(), trackerModule_->detId(), windowFE, degradedBend, num); + } + + //=== Set flag indicating if stub will be output by front-end readout electronics + //=== (where we can reconfigure the stub window size and rapidity cut). + + void Stub::setFrontend(const StubKiller* stubKiller) { + frontendPass_ = true; // Did stub pass cuts applied in front-end chip + stubFailedDegradeWindow_ = false; // Did it only fail cuts corresponding to windows encoded in DegradeBend.h? + // Don't use stubs at large eta, since it is impossible to form L1 tracks from them, so they only contribute to combinatorics. + if (std::abs(this->eta()) > settings_->maxStubEta()) + frontendPass_ = false; + // Don't use stubs whose Pt is significantly below the Pt cut used in the L1 tracking, allowing for uncertainty in q/Pt due to stub bend resolution. + const float qOverPtCut = 1. / settings_->houghMinPt(); + if (settings_->killLowPtStubs()) { + // Apply this cut in the front-end electronics. + if (std::abs(this->bendInFrontend()) - this->bendCutInFrontend() > qOverPtCut / this->qOverPtOverBend()) + frontendPass_ = false; + } + + if (frontendPass_ && this->bend() == rejectedStubBend_) { + throw cms::Exception( + "BadConfig: FE stub bend window sizes provided in cfg ES source are tighter than those to make the stubs. " + "Please fix them"); + } + + if (settings_->killLowPtStubs()) { + // Reapply the same cut using the degraded bend information available in the off-detector electronics. + // The reason is that the bend degredation can move the Pt below the Pt cut, making the stub useless to the off-detector electronics. + if (std::abs(this->bend()) - this->bendCut() > qOverPtCut / this->qOverPtOverBend()) + frontendPass_ = false; + } + + // Emulate stubs in dead tracker regions.. + StubKiller::KillOptions killScenario = static_cast(settings_->killScenario()); + if (killScenario != StubKiller::KillOptions::none) { + bool kill = stubKiller->killStub(ttStubRef_.get()); + if (kill) + frontendPass_ = false; + } + } + + //=== Function to calculate approximation for dphiOverBendCorrection aka B + double Stub::approxB() { + if (tiltedBarrel()) { + return settings_->bApprox_gradient() * std::abs(z_) / r_ + settings_->bApprox_intercept(); + } else { + return barrel() ? 1 : std::abs(z_) / r_; + } + } + + //=== Calculate variables giving ratio of track intercept angle to stub bend. + + void Stub::calcDphiOverBend() { + // Uses stub (r,z) instead of module (r,z). Logically correct but has negligable effect on results. + if (settings_->useApproxB()) { + float dphiOverBendCorrection_approx_ = approxB(); + dphiOverBend_ = trackerModule_->pitchOverSep() * dphiOverBendCorrection_approx_; + } else { + float dphiOverBendCorrection_ = std::abs(cos(this->theta() - trackerModule_->tiltAngle()) / sin(this->theta())); + dphiOverBend_ = trackerModule_->pitchOverSep() * dphiOverBendCorrection_; + } + } + + //=== Note which tracking particle(s), if any, produced this stub. + //=== The 1st argument is a map relating TrackingParticles to TP. + + void Stub::fillTruth(const map, const TP*>& translateTP, + const edm::Handle& mcTruthTTStubHandle, + const edm::Handle& mcTruthTTClusterHandle) { + //--- Fill assocTP_ info. If both clusters in this stub were produced by the same single tracking particle, find out which one it was. + + bool genuine = mcTruthTTStubHandle->isGenuine(ttStubRef_); // Same TP contributed to both clusters? + assocTP_ = nullptr; + + // Require same TP contributed to both clusters. + if (genuine) { + edm::Ptr tpPtr = mcTruthTTStubHandle->findTrackingParticlePtr(ttStubRef_); + if (translateTP.find(tpPtr) != translateTP.end()) { + assocTP_ = translateTP.at(tpPtr); + // N.B. Since not all tracking particles are stored in InputData::vTPs_, sometimes no match will be found. + } + } + + // Fill assocTPs_ info. + + if (settings_->stubMatchStrict()) { + // We consider only stubs in which this TP contributed to both clusters. + if (assocTP_ != nullptr) + assocTPs_.insert(assocTP_); + + } else { + // We consider stubs in which this TP contributed to either cluster. + + for (unsigned int iClus = 0; iClus <= 1; iClus++) { // Loop over both clusters that make up stub. + const TTClusterRef& ttClusterRef = ttStubRef_->clusterRef(iClus); + + // Now identify all TP's contributing to either cluster in stub. + vector > vecTpPtr = mcTruthTTClusterHandle->findTrackingParticlePtrs(ttClusterRef); + + for (edm::Ptr tpPtr : vecTpPtr) { + if (translateTP.find(tpPtr) != translateTP.end()) { + assocTPs_.insert(translateTP.at(tpPtr)); + // N.B. Since not all tracking particles are stored in InputData::vTPs_, sometimes no match will be found. + } + } + } + } + + //--- Also note which tracking particles produced the two clusters that make up the stub + + for (unsigned int iClus = 0; iClus <= 1; iClus++) { // Loop over both clusters that make up stub. + const TTClusterRef& ttClusterRef = ttStubRef_->clusterRef(iClus); + + bool genuineCluster = mcTruthTTClusterHandle->isGenuine(ttClusterRef); // Only 1 TP made cluster? + assocTPofCluster_[iClus] = nullptr; + + // Only consider clusters produced by just one TP. + if (genuineCluster) { + edm::Ptr tpPtr = mcTruthTTClusterHandle->findTrackingParticlePtr(ttClusterRef); + + if (translateTP.find(tpPtr) != translateTP.end()) { + assocTPofCluster_[iClus] = translateTP.at(tpPtr); + // N.B. Since not all tracking particles are stored in InputData::vTPs_, sometimes no match will be found. + } + } + } + } +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/StubFEWindows.cc b/L1Trigger/TrackFindingTMTT/src/StubFEWindows.cc new file mode 100644 index 0000000000000..29301fe732d79 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/StubFEWindows.cc @@ -0,0 +1,78 @@ +#include "L1Trigger/TrackFindingTMTT/interface/StubFEWindows.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "DataFormats/SiStripDetId/interface/StripSubdetector.h" + +#include + +using namespace std; + +namespace tmtt { + + //=== Initialize stub window sizes from TTStubProducer cfg. + + StubFEWindows::StubFEWindows(const edm::ParameterSet& pSetStubAlgo) { + numTiltedLayerRings_ = pSetStubAlgo.getParameter>("NTiltedRings"); + windowSizeBarrelLayers_ = pSetStubAlgo.getParameter>("BarrelCut"); + const auto& pSetTiltedLayer = pSetStubAlgo.getParameter>("TiltedBarrelCutSet"); + const auto& pSetEncapDisks = pSetStubAlgo.getParameter>("EndcapCutSet"); + windowSizeTiltedLayersRings_.reserve(pSetTiltedLayer.size()); + for (const auto& pSet : pSetTiltedLayer) { + windowSizeTiltedLayersRings_.emplace_back(pSet.getParameter>("TiltedCut")); + } + windowSizeEndcapDisksRings_.reserve(pSetEncapDisks.size()); + for (const auto& pSet : pSetEncapDisks) { + windowSizeEndcapDisksRings_.emplace_back(pSet.getParameter>("EndcapCut")); + } + } + + //=== Set all FE stub bend windows to zero. + + void StubFEWindows::setZero() { + std::fill(windowSizeBarrelLayers_.begin(), windowSizeBarrelLayers_.end(), 0.); + for (auto& x : windowSizeEndcapDisksRings_) + std::fill(x.begin(), x.end(), 0.); + for (auto& y : windowSizeTiltedLayersRings_) + std::fill(y.begin(), y.end(), 0.); + } + + //=== Const/non-const access to element of array giving window size for specific module. + + const double* StubFEWindows::storedWindowSize(const TrackerTopology* trackerTopo, const DetId& detId) const { + // Code accessing geometry inspired by L1Trigger/TrackTrigger/src/TTStubAlgorithm_official.cc + + const double* storedHalfWindow = nullptr; + if (detId.subdetId() == StripSubdetector::TOB) { + unsigned int layer = trackerTopo->layer(detId); + unsigned int ladder = trackerTopo->tobRod(detId); + int type = 2 * trackerTopo->tobSide(detId) - 3; // -1 for tilted-, 1 for tilted+, 3 for flat + double corr = 0; + + if (type != TrackerModule::BarrelModuleType::flat) { + // Tilted barrel + corr = (numTiltedLayerRings_.at(layer) + 1) / 2.; + // Corrected ring number, between 0 and barrelNTilt.at(layer), in ascending |z| + ladder = corr - (corr - ladder) * type; + storedHalfWindow = &(windowSizeTiltedLayersRings_.at(layer).at(ladder)); + } else { + // Flat barrel + storedHalfWindow = &(windowSizeBarrelLayers_.at(layer)); + } + + } else if (detId.subdetId() == StripSubdetector::TID) { + // Endcap + unsigned int wheel = trackerTopo->tidWheel(detId); + unsigned int ring = trackerTopo->tidRing(detId); + storedHalfWindow = &(windowSizeEndcapDisksRings_.at(wheel).at(ring)); + } + return storedHalfWindow; + } + + double* StubFEWindows::storedWindowSize(const TrackerTopology* trackerTopo, const DetId& detId) { + // Code accessing geometry inspired by L1Trigger/TrackTrigger/src/TTStubAlgorithm_official.cc + + // Scott Meyers's solution to give const & non-const versions of same function, without + // code duplication. + return const_cast((static_cast(this))->storedWindowSize(trackerTopo, detId)); + } +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/StubKiller.cc b/L1Trigger/TrackFindingTMTT/src/StubKiller.cc new file mode 100644 index 0000000000000..df9351eacac80 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/StubKiller.cc @@ -0,0 +1,263 @@ +#include "L1Trigger/TrackFindingTMTT/interface/StubKiller.h" +#include "DataFormats/Math/interface/deltaPhi.h" +#include "FWCore/Utilities/interface/Exception.h" + +using namespace std; + +namespace tmtt { + + StubKiller::StubKiller(StubKiller::KillOptions killScenario, + const TrackerTopology* trackerTopology, + const TrackerGeometry* trackerGeometry, + const edm::Event& event) + : killScenario_(killScenario), + trackerTopology_(trackerTopology), + trackerGeometry_(trackerGeometry), + minPhiToKill_(0), + maxPhiToKill_(0), + minZToKill_(0), + maxZToKill_(0), + minRToKill_(0), + maxRToKill_(0), + fractionOfStubsToKillInLayers_(0), + fractionOfStubsToKillEverywhere_(0), + fractionOfModulesToKillEverywhere_(0) { + if (rndmService_.isAvailable()) { + rndmEngine_ = &(rndmService_->getEngine(event.streamID())); + } else { + throw cms::Exception("BadConfig") + << "StubKiller: requires RandomNumberGeneratorService, not present in cfg file, namely:" << endl + << "process.RandomNumberGeneratorService=cms.Service('RandomNumberGeneratorService',TMTrackProducer=cms.PSet(" + "initialSeed=cms.untracked.uint32(12345)))"; + } + + // These scenarios correspond to slide 12 of https://indico.cern.ch/event/719985/contributions/2970687/attachments/1634587/2607365/StressTestTF-Acosta-Apr18.pdf + // Scenario 1 + + // kill layer 5 in one quadrant +5 % random module loss to connect to what was done before + if (killScenario_ == KillOptions::layer5) { + layersToKill_ = {5}; + minPhiToKill_ = 0; + maxPhiToKill_ = 0.5 * M_PI; + minZToKill_ = -1000; + maxZToKill_ = 0; + minRToKill_ = 0; + maxRToKill_ = 1000; + fractionOfStubsToKillInLayers_ = 1; + fractionOfStubsToKillEverywhere_ = 0; + fractionOfModulesToKillEverywhere_ = 0.05; + } + // Scenario 2 + // kill layer 1 in one quadrant +5 % random module loss + else if (killScenario_ == KillOptions::layer1) { + layersToKill_ = {1}; + minPhiToKill_ = 0; + maxPhiToKill_ = 0.5 * M_PI; + minZToKill_ = -1000; + maxZToKill_ = 0; + minRToKill_ = 0; + maxRToKill_ = 1000; + fractionOfStubsToKillInLayers_ = 1; + fractionOfStubsToKillEverywhere_ = 0; + fractionOfModulesToKillEverywhere_ = 0.05; + } + // Scenario 3 + // kill layer 1 + layer 2, both in same quadrant + else if (killScenario_ == KillOptions::layer1layer2) { + layersToKill_ = {1, 2}; + minPhiToKill_ = 0; + maxPhiToKill_ = 0.5 * M_PI; + minZToKill_ = -1000; + maxZToKill_ = 0; + minRToKill_ = 0; + maxRToKill_ = 1000; + fractionOfStubsToKillInLayers_ = 1; + fractionOfStubsToKillEverywhere_ = 0; + fractionOfModulesToKillEverywhere_ = 0; + } + // Scenario 4 + // kill layer 1 and disk 1, both in same quadrant + else if (killScenario_ == KillOptions::layer1disk1) { + layersToKill_ = {1, 11}; + minPhiToKill_ = 0; + maxPhiToKill_ = 0.5 * M_PI; + minZToKill_ = -1000; + maxZToKill_ = 0; + minRToKill_ = 0; + maxRToKill_ = 66.5; + fractionOfStubsToKillInLayers_ = 1; + fractionOfStubsToKillEverywhere_ = 0; + fractionOfModulesToKillEverywhere_ = 0; + } + // An extra scenario not listed in the slides + // 5% random module loss throughout tracker + else if (killScenario_ == KillOptions::random) { + layersToKill_ = {}; + fractionOfStubsToKillInLayers_ = 0; + fractionOfStubsToKillEverywhere_ = 0.; + fractionOfModulesToKillEverywhere_ = 0.05; + } + + deadModules_.clear(); + if (fractionOfModulesToKillEverywhere_ > 0) { + this->chooseModulesToKill(); + } + this->addDeadLayerModulesToDeadModuleList(); + } + + // Indicate if given stub was killed by dead tracker module, based on dead module scenario. + + bool StubKiller::killStub(const TTStub* stub) const { + if (killScenario_ == KillOptions::none) + return false; + else { + // Check if stub is in dead region specified by *ToKill_ + // And randomly kill stubs throughout tracker (not just thos in specific regions/modules) + bool killStubRandomly = killStub(stub, + layersToKill_, + minPhiToKill_, + maxPhiToKill_, + minZToKill_, + maxZToKill_, + minRToKill_, + maxRToKill_, + fractionOfStubsToKillInLayers_, + fractionOfStubsToKillEverywhere_); + // Kill modules in specifid modules + // Random modules throughout the tracker, and those modules in specific regions (so may already have been killed by killStub above) + bool killStubInDeadModules = killStubInDeadModule(stub); + return killStubRandomly || killStubInDeadModules; + } + } + + // Indicate if given stub was killed by dead tracker module, based on specified dead regions + // rather than based on the dead module scenario. + // layersToKill - a vector stating the layers we are killing stubs in. Can be an empty vector. + // Barrel layers are encoded as 1-6. The endcap layers are encoded as 11-15 (-z) and 21-25 (+z) + // min/max Phi/Z/R - stubs within the region specified by these boundaries and layersToKill are flagged for killing + // fractionOfStubsToKillInLayers - The fraction of stubs to kill in the specified layers/region. + // fractionOfStubsToKillEverywhere - The fraction of stubs to kill throughout the tracker + + bool StubKiller::killStub(const TTStub* stub, + const vector& layersToKill, + const double minPhiToKill, + const double maxPhiToKill, + const double minZToKill, + const double maxZToKill, + const double minRToKill, + const double maxRToKill, + const double fractionOfStubsToKillInLayers, + const double fractionOfStubsToKillEverywhere) const { + // Only kill stubs in specified layers + if (not layersToKill.empty()) { + // Get the layer the stub is in, and check if it's in the layer you want to kill + DetId stackDetid = stub->getDetId(); + DetId geoDetId(stackDetid.rawId() + 1); + + // If this module is in the deadModule list, don't also try to kill the stub here + if (deadModules_.empty() || deadModules_.find(geoDetId) == deadModules_.end()) { + bool isInBarrel = geoDetId.subdetId() == StripSubdetector::TOB || geoDetId.subdetId() == StripSubdetector::TIB; + + int layerID = 0; + if (isInBarrel) { + layerID = trackerTopology_->layer(geoDetId); + } else { + layerID = 10 * trackerTopology_->side(geoDetId) + trackerTopology_->tidWheel(geoDetId); + } + + if (find(layersToKill.begin(), layersToKill.end(), layerID) != layersToKill.end()) { + // Get the phi and z of stub, and check if it's in the region you want to kill + const GeomDetUnit* det0 = trackerGeometry_->idToDetUnit(geoDetId); + const PixelGeomDetUnit* theGeomDet = dynamic_cast(det0); + const PixelTopology* topol = dynamic_cast(&(theGeomDet->specificTopology())); + MeasurementPoint measurementPoint = stub->clusterRef(0)->findAverageLocalCoordinatesCentered(); + LocalPoint clustlp = topol->localPosition(measurementPoint); + GlobalPoint pos = theGeomDet->surface().toGlobal(clustlp); + + double stubPhi = reco::deltaPhi(pos.phi(), 0.); + + if (stubPhi > minPhiToKill && stubPhi < maxPhiToKill && pos.z() > minZToKill && pos.z() < maxZToKill && + pos.perp() > minRToKill && pos.perp() < maxRToKill) { + // Kill fraction of stubs + if (fractionOfStubsToKillInLayers == 1) { + return true; + } else { + if (rndmEngine_->flat() < fractionOfStubsToKillInLayers) { + return true; + } + } + } + } + } + } + + // Kill fraction of stubs throughout tracker + if (fractionOfStubsToKillEverywhere > 0) { + if (rndmEngine_->flat() < fractionOfStubsToKillEverywhere) { + return true; + } + } + + return false; + } + + // Indicate if given stub was in (partially) dead tracker module, based on dead module scenario. + + bool StubKiller::killStubInDeadModule(const TTStub* stub) const { + if (not deadModules_.empty()) { + DetId stackDetid = stub->getDetId(); + DetId geoDetId(stackDetid.rawId() + 1); + auto deadModule = deadModules_.find(geoDetId); + if (deadModule != deadModules_.end()) { + if (deadModule->second == 1) { + return true; + } else { + if (rndmEngine_->flat() < deadModule->second) { + return true; + } + } + } + } + + return false; + } + + // Identify modules to be killed, chosen randomly from those in the whole tracker. + + void StubKiller::chooseModulesToKill() { + for (const GeomDetUnit* gd : trackerGeometry_->detUnits()) { + if (!trackerTopology_->isLower(gd->geographicalId())) + continue; + if (rndmEngine_->flat() < fractionOfModulesToKillEverywhere_) { + deadModules_[gd->geographicalId()] = 1; + } + } + } + + // Identify modules to be killed, chosen based on location in tracker. + + void StubKiller::addDeadLayerModulesToDeadModuleList() { + for (const GeomDetUnit* gd : trackerGeometry_->detUnits()) { + float moduleR = gd->position().perp(); + float moduleZ = gd->position().z(); + float modulePhi = reco::deltaPhi(gd->position().phi(), 0.); + DetId geoDetId = gd->geographicalId(); + bool isInBarrel = geoDetId.subdetId() == StripSubdetector::TOB || geoDetId.subdetId() == StripSubdetector::TIB; + + int layerID = 0; + if (isInBarrel) { + layerID = trackerTopology_->layer(geoDetId); + } else { + layerID = 10 * trackerTopology_->side(geoDetId) + trackerTopology_->tidWheel(geoDetId); + } + if (find(layersToKill_.begin(), layersToKill_.end(), layerID) != layersToKill_.end()) { + if (modulePhi > minPhiToKill_ && modulePhi < maxPhiToKill_ && moduleZ > minZToKill_ && moduleZ < maxZToKill_ && + moduleR > minRToKill_ && moduleR < maxRToKill_) { + if (deadModules_.find(gd->geographicalId()) == deadModules_.end()) { + deadModules_[gd->geographicalId()] = fractionOfStubsToKillInLayers_; + } + } + } + } + } +}; // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/StubWindowSuggest.cc b/L1Trigger/TrackFindingTMTT/src/StubWindowSuggest.cc new file mode 100644 index 0000000000000..c890e97d74d1f --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/StubWindowSuggest.cc @@ -0,0 +1,118 @@ +#include "L1Trigger/TrackFindingTMTT/interface/StubWindowSuggest.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" + +#include "DataFormats/DetId/interface/DetId.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include +#include + +using namespace std; + +namespace tmtt { + + //=== Get FE window size arrays (via copy) used with stub producer, but set to zero. + + void StubWindowSuggest::setFEWindows(const StubFEWindows* sw) { + static std::mutex myMutex; + std::lock_guard myGuard(myMutex); // Allow only one thread. + // Only need to create FE windows once. + if (not sw_) { + sw_ = std::make_unique(*sw); // Copy + sw_->setZero(); + } + } + + //=== Analyse stub window required for this stub. + + void StubWindowSuggest::process(const TrackerTopology* trackerTopo, const Stub* stub) { + static std::mutex myMutex; + std::lock_guard myGuard(myMutex); // Allow only one thread. + + // Half-size of FE chip bend window corresponding to Pt range in which tracks are to be found. + const double invPtMax = 1 / ptMin_; + double bendHalfWind = invPtMax / std::abs(stub->qOverPtOverBend()); + // Increase half-indow size to allow for resolution in bend. + bendHalfWind += stub->bendCutInFrontend(); + // Stub bend is measured here in half-integer values. + bendHalfWind = int(2 * bendHalfWind) / 2.; + + // Compare with half-size of FE bend window stored in arrays. + this->updateStoredWindow(trackerTopo, stub, bendHalfWind); + } + + //=== Update stored stub window size with this stub. + + void StubWindowSuggest::updateStoredWindow(const TrackerTopology* trackerTopo, + const Stub* stub, + double bendHalfWind) { + // Code accessing geometry inspired by L1Trigger/TrackTrigger/src/TTStubAlgorithm_official.cc + + DetId stDetId(stub->trackerModule()->detId()); + + double* storedHalfWindow = sw_->storedWindowSize(trackerTopo, stDetId); + + if (*storedHalfWindow < bendHalfWind) + *storedHalfWindow = bendHalfWind; + } + + //=== Print results (should be done in endJob(); + + void StubWindowSuggest::printResults() const { + PrintL1trk(1) << "=============================================================================="; + PrintL1trk(1) << " Stub window sizes that TMTT suggests putting inside "; + PrintL1trk(1) << " /L1Trigger/TrackTrigger/python/TTStubAlgorithmRegister_cfi.py"; + PrintL1trk(1) << " (These should give good efficiency, but tighter windows may be needed to"; + PrintL1trk(1) << " limit the data rate from the FE tracker electronics)."; + PrintL1trk(1) << "=============================================================================="; + + std::stringstream text; + string div; + + text << "BarrelCut = cms.vdouble( "; + div = ""; + for (const auto& cut : sw_->windowSizeBarrelLayers()) { + text << div << cut; + div = ", "; + } + text << "),"; + PrintL1trk(1) << text.str(); + + PrintL1trk(1) << "TiltedBarrelCutSet = cms.VPSET( "; + for (const auto& cutVec : sw_->windowSizeTiltedLayersRings()) { + text.str(""); + text << " cms.PSet( TiltedCut = cms.vdouble("; + if (cutVec.empty()) + text << "0"; + div = ""; + for (const auto& cut : cutVec) { + text << div << cut; + div = ", "; + } + text << "), ),"; + PrintL1trk(1) << text.str(); + } + PrintL1trk(1) << "),"; + + PrintL1trk(1) << "EndcapCutSet = cms.VPSET( "; + for (const auto& cutVec : sw_->windowSizeEndcapDisksRings()) { + text.str(""); + text << " cms.PSet( EndcapCut = cms.vdouble("; + if (cutVec.empty()) + text << "0"; + div = ""; + for (const auto& cut : cutVec) { + text << div << cut; + div = ", "; + } + text << "), ),"; + PrintL1trk(1) << text.str(); + } + PrintL1trk(1) << ")"; + + PrintL1trk(1) << "=============================================================================="; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/TP.cc b/L1Trigger/TrackFindingTMTT/src/TP.cc new file mode 100644 index 0000000000000..832ae09949768 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/TP.cc @@ -0,0 +1,194 @@ +#include "SimTracker/Common/interface/TrackingParticleSelector.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "DataFormats/Math/interface/deltaR.h" + +#include + +using namespace std; + +namespace tmtt { + + //=== Store useful info about this tracking particle + + TP::TP(const TrackingParticlePtr& tpPtr, unsigned int index_in_vTPs, const Settings* settings) + : trackingParticlePtr_(tpPtr), + index_in_vTPs_(index_in_vTPs), + settings_(settings), + pdgId_(tpPtr->pdgId()), + charge_(tpPtr->charge()), + mass_(tpPtr->mass()), + pt_(tpPtr->pt()), + eta_(tpPtr->eta()), + theta_(tpPtr->theta()), + tanLambda_(1. / tan(theta_)), + phi0_(tpPtr->phi()), + vx_(tpPtr->vertex().x()), + vy_(tpPtr->vertex().y()), + vz_(tpPtr->vertex().z()), + d0_(vx_ * sin(phi0_) - vy_ * cos(phi0_)), // Copied from CMSSW class TrackBase::d0(). + z0_(vz_ - (vx_ * cos(phi0_) + vy_ * sin(phi0_)) * tanLambda_) // Copied from CMSSW class TrackBase::dz(). + { + const vector& vst = tpPtr->g4Tracks(); + EncodedEventId eid = vst.at(0).eventId(); + inTimeBx_ = (eid.bunchCrossing() == 0); // TP from in-time or out-of-time Bx. + physicsCollision_ = (eid.event() == 0); // TP from physics collision or from pileup. + + this->fillUse(); // Fill use_ flag, indicating if TP is worth keeping. + this->fillUseForEff(); // Fill useForEff_ flag, indicating if TP is good for tracking efficiency measurement. + } + + //=== Fill truth info with association from tracking particle to stubs. + + void TP::fillTruth(const list& vStubs) { + for (const Stub& s : vStubs) { + for (const TP* tp_i : s.assocTPs()) { + if (tp_i->index() == this->index()) + assocStubs_.push_back(&s); + } + } + + this->fillUseForAlgEff(); // Fill useForAlgEff_ flag. + + this->calcNumLayers(); // Calculate number of tracker layers this TP has stubs in. + } + + //=== Check if this tracking particle is worth keeping. + //=== (i.e. If there is the slightest chance of reconstructing it, so as to measure fake rate). + + void TP::fillUse() { + constexpr bool useOnlyInTimeParticles = false; + constexpr bool useOnlyTPfromPhysicsCollisionFalse = false; + // Use looser cuts here those those used for tracking efficiency measurement. + // Keep only those TP that have a chance (allowing for finite track resolution) of being reconstructed as L1 tracks. L1 tracks not matching these TP will be defined as fake. + + // Include all possible particle types here, as if some are left out, L1 tracks matching one of missing types will be declared fake. + constexpr std::array genPdgIdsAllUnsigned = {{11, 13, 211, 321, 2212}}; + vector genPdgIdsAll; + for (const int& iPdg : genPdgIdsAllUnsigned) { + genPdgIdsAll.push_back(iPdg); + genPdgIdsAll.push_back(-iPdg); + } + + // Range big enough to include all TP needed to measure tracking efficiency + // and big enough to include any TP that might be reconstructed for fake rate measurement. + constexpr float ptMinScale = 0.7; + const float ptMin = min(settings_->genMinPt(), ptMinScale * settings_->houghMinPt()); + constexpr double ptMax = 9.9e9; + const float etaExtra = 0.2; + const float etaMax = max(settings_->genMaxAbsEta(), etaExtra + std::abs(settings_->etaRegions()[0])); + constexpr double fixedVertRcut = 10.; + constexpr double fixedVertZcut = 35.; + + static const TrackingParticleSelector trackingParticleSelector(ptMin, + ptMax, + -etaMax, + etaMax, + max(fixedVertRcut, settings_->genMaxVertR()), + max(fixedVertZcut, settings_->genMaxVertZ()), + 0, + useOnlyTPfromPhysicsCollisionFalse, + useOnlyInTimeParticles, + true, + false, + genPdgIdsAll); + + use_ = trackingParticleSelector(*trackingParticlePtr_); + } + + //=== Check if this tracking particle can be used to measure the L1 tracking efficiency. + + void TP::fillUseForEff() { + useForEff_ = false; + if (use_) { + constexpr bool useOnlyInTimeParticles = true; + constexpr bool useOnlyTPfromPhysicsCollision = true; + constexpr double ptMax = 9.9e9; + static const TrackingParticleSelector trackingParticleSelector(settings_->genMinPt(), + ptMax, + -settings_->genMaxAbsEta(), + settings_->genMaxAbsEta(), + settings_->genMaxVertR(), + settings_->genMaxVertZ(), + 0, + useOnlyTPfromPhysicsCollision, + useOnlyInTimeParticles, + true, + false, + settings_->genPdgIds()); + + useForEff_ = trackingParticleSelector(*trackingParticlePtr_); + + // Add additional cut on particle transverse impact parameter. + if (std::abs(d0_) > settings_->genMaxD0()) + useForEff_ = false; + if (std::abs(z0_) > settings_->genMaxZ0()) + useForEff_ = false; + } + } + + //=== Check if this tracking particle can be used to measure the L1 tracking algorithmic efficiency (makes stubs in enough layers). + + void TP::fillUseForAlgEff() { + useForAlgEff_ = false; + if (useForEff_) { + useForAlgEff_ = (Utility::countLayers(settings_, assocStubs_, true) >= settings_->genMinStubLayers()); + } + } + + //== Estimated phi angle at which TP trajectory crosses the module containing the stub. + + float TP::trkPhiAtStub(const Stub* stub) const { + float trkPhi = phi0_ - this->dphi(this->trkRAtStub(stub)); + return trkPhi; + } + + //== Estimated r coord. at which TP trajectory crosses the module containing the given stub. + //== Only works for modules orientated parallel or perpendicular to beam-axis, + //== and makes the approximation that tracks are straight-lines in r-z plane. + + float TP::trkRAtStub(const Stub* stub) const { + float rTrk = (stub->barrel()) ? stub->r() : (stub->z() - z0_) / tanLambda_; + return rTrk; + } + + //== Estimated z coord. at which TP trajectory crosses the module containing the given stub. + //== Only works for modules orientated parallel or perpendicular to beam-axis, + //== and makes the approximation that tracks are straight-lines in r-z plane. + + float TP::trkZAtStub(const Stub* stub) const { + float zTrk = (stub->barrel()) ? z0_ + tanLambda_ * stub->r() : stub->z(); + return zTrk; + } + + void TP::fillNearestJetInfo(const reco::GenJetCollection* genJets) { + double minDR = 999.; + double ptOfNearestJet = -1; + + reco::GenJetCollection::const_iterator iterGenJet; + for (iterGenJet = genJets->begin(); iterGenJet != genJets->end(); ++iterGenJet) { + reco::GenJet myJet = reco::GenJet(*iterGenJet); + + // Don't consider GenJets failing these cuts. + constexpr float minPt = 30.0; + constexpr float maxEta = 2.5; + + if (myJet.pt() > minPt && std::abs(myJet.eta()) > maxEta) { + double deltaR = reco::deltaR(this->eta(), this->phi0(), myJet.eta(), myJet.phi()); + + if (deltaR < minDR) { + minDR = deltaR; + ptOfNearestJet = myJet.pt(); + } + } + } + + // Only consider GenJets within this distance of TP. + constexpr float cutDR = 0.4; + tpInJet_ = (minDR < cutDR); + nearestJetPt_ = tpInJet_ ? ptOfNearestJet : -1.; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/TrackFitFactory.cc b/L1Trigger/TrackFindingTMTT/src/TrackFitFactory.cc new file mode 100644 index 0000000000000..1a399ac030c89 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/TrackFitFactory.cc @@ -0,0 +1,42 @@ +///=== Create requested track fitter + +#include "L1Trigger/TrackFindingTMTT/interface/TrackFitFactory.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/ChiSquaredFit4.h" +#include "L1Trigger/TrackFindingTMTT/interface/KFParamsComb.h" +#include "L1Trigger/TrackFindingTMTT/interface/SimpleLR4.h" +#ifdef USE_HLS +#include "L1Trigger/TrackFindingTMTT/interface/HLS/KFParamsCombCallHLS.h" +#endif +#include "FWCore/Utilities/interface/Exception.h" + +using namespace std; + +namespace tmtt { + + namespace trackFitFactory { + + std::unique_ptr create(const std::string& fitterName, const Settings* settings) { + if (fitterName == "ChiSquaredFit4") { + return std::make_unique(settings, 4); + } else if (fitterName == "KF4ParamsComb") { + return std::make_unique(settings, 4, fitterName); + } else if (fitterName == "KF5ParamsComb") { + return std::make_unique(settings, 5, fitterName); + } else if (fitterName == "SimpleLR4") { + return std::make_unique(settings); +#ifdef USE_HLS + } else if (fitterName == "KF4ParamsCombHLS") { + return std::make_unique(settings, 4, fitterName); + } else if (fitterName == "KF5ParamsCombHLS") { + return std::make_unique(settings, 5, fitterName); +#endif + } else { + throw cms::Exception("BadConfig") + << "TrackFitFactory: ERROR you requested unknown track fitterName: " << fitterName; + } + } + + } // namespace trackFitFactory + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/TrackerModule.cc b/L1Trigger/TrackFindingTMTT/src/TrackerModule.cc new file mode 100644 index 0000000000000..0c3650d8fc8b2 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/TrackerModule.cc @@ -0,0 +1,153 @@ +#include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h" +#include "DataFormats/TrackerCommon/interface/TrackerTopology.h" +#include "Geometry/CommonTopologies/interface/PixelGeomDetUnit.h" +#include "Geometry/CommonTopologies/interface/PixelTopology.h" +#include "DataFormats/SiStripDetId/interface/StripSubdetector.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" + +#include "L1Trigger/TrackFindingTMTT/interface/TrackerModule.h" + +#include +#include +#include + +using namespace std; + +namespace tmtt { + + namespace { + std::once_flag printOnce; + } + + const float TrackerModule::invRoot12 = sqrt(1. / 12.); + + //=== Get info about tracker module (where detId is ID of lower sensor in stacked module). + + TrackerModule::TrackerModule(const TrackerGeometry* trackerGeometry, + const TrackerTopology* trackerTopology, + const ModuleTypeCfg& moduleTypeCfg, + const DetId& detId) + : moduleTypeCfg_(moduleTypeCfg) { + detId_ = detId; // Det ID of lower sensor in stacked module. + stackedDetId_ = trackerTopology->stack(detId); // Det ID of stacked module. + + // Get min & max (r,phi,z) coordinates of the centre of the two sensors containing this stub. + const GeomDetUnit* det0 = trackerGeometry->idToDetUnit(detId); + const GeomDetUnit* det1 = trackerGeometry->idToDetUnit(trackerTopology->partnerDetId(detId)); + specDet_ = dynamic_cast(det0); + specTopol_ = dynamic_cast(&(specDet_->specificTopology())); + + float R0 = det0->position().perp(); + float R1 = det1->position().perp(); + float PHI0 = det0->position().phi(); + float PHI1 = det1->position().phi(); + float Z0 = det0->position().z(); + float Z1 = det1->position().z(); + moduleMinR_ = std::min(R0, R1); + moduleMaxR_ = std::max(R0, R1); + moduleMinPhi_ = std::min(PHI0, PHI1); + moduleMaxPhi_ = std::max(PHI0, PHI1); + moduleMinZ_ = std::min(Z0, Z1); + moduleMaxZ_ = std::max(Z0, Z1); + + // Note if modules are flipped back-to-front. + outerModuleAtSmallerR_ = (det0->position().mag() > det1->position().mag()); + + // Note if module is PS or 2S, and whether in barrel or endcap. + // From Geometry/TrackerGeometryBuilder/README.md + psModule_ = (trackerGeometry->getDetectorType(detId) == TrackerGeometry::ModuleType::Ph2PSP); + barrel_ = detId.subdetId() == StripSubdetector::TOB || detId.subdetId() == StripSubdetector::TIB; + + // Encode layer ID (barrel layers: 1-6, endcap disks: 11-15 + 21-25) + if (barrel_) { + layerId_ = trackerTopology->layer(detId); // barrel layer 1-6 encoded as 1-6 + } else { + layerId_ = 10 * trackerTopology->side(detId) + trackerTopology->tidWheel(detId); + } + // Get reduced layer ID (in range 1-7), requiring only 3 bits for firmware. + layerIdReduced_ = TrackerModule::calcLayerIdReduced(layerId_); + + // Note module ring in endcap + endcapRing_ = barrel_ ? 0 : trackerTopology->tidRing(detId); + if (not barrel_) { + // Apply bodge, since Topology class annoyingly starts ring count at 1, even in endcap wheels where + // inner rings are absent. + unsigned int iWheel = trackerTopology->tidWheel(detId); + if (iWheel >= 3 && iWheel <= 5) + endcapRing_ += 3; + } + + // Note if tilted barrel module & get tilt angle (in range 0 to PI). + tiltedBarrel_ = barrel_ && (trackerTopology->tobSide(detId) != BarrelModuleType::flat); + float deltaR = std::abs(R1 - R0); + float deltaZ = (R1 - R0 > 0) ? (Z1 - Z0) : -(Z1 - Z0); + tiltAngle_ = atan(deltaR / deltaZ); + + // Get sensor strip or pixel pitch using innermost sensor of pair. + + const Bounds& bounds = det0->surface().bounds(); + sensorWidth_ = bounds.width(); // Width of sensitive region of sensor (= stripPitch * nStrips). + sensorSpacing_ = sqrt((moduleMaxR_ - moduleMinR_) * (moduleMaxR_ - moduleMinR_) + + (moduleMaxZ_ - moduleMinZ_) * (moduleMaxZ_ - moduleMinZ_)); + nStrips_ = specTopol_->nrows(); // No. of strips in sensor + std::pair pitch = specTopol_->pitch(); + stripPitch_ = pitch.first; // Strip pitch (or pixel pitch along shortest axis) + stripLength_ = pitch.second; // Strip length (or pixel pitch along longest axis) + + // Get module type ID defined by firmware. + + moduleTypeID_ = TrackerModule::calcModuleType(stripPitch_, sensorSpacing_, barrel_, tiltedBarrel_, psModule_); + } + + //=== Calculate reduced layer ID (in range 1-7), for packing into 3 bits to simplify the firmware. + + unsigned int TrackerModule::calcLayerIdReduced(unsigned int layerId) { + // Don't bother distinguishing two endcaps, as no track can have stubs in both. + unsigned int lay = (layerId < 20) ? layerId : layerId - 10; + + // No genuine track can have stubs in both barrel layer 6 and endcap disk 11 etc., so merge their layer IDs. + if (lay == 6) + lay = 11; + if (lay == 5) + lay = 12; + if (lay == 4) + lay = 13; + if (lay == 3) + lay = 15; + // At this point, the reduced layer ID can have values of 1, 2, 11, 12, 13, 14, 15. So correct to put in range 1-7. + if (lay > 10) + lay -= 8; + + if (lay < 1 || lay > 7) + throw cms::Exception("LogicError") << "TrackerModule: Reduced layer ID out of expected range"; + + return lay; + } + + //=== Get module type ID defined by firmware. + + unsigned int TrackerModule::calcModuleType( + float pitch, float space, bool barrel, bool tiltedBarrel, bool psModule) const { + // Calculate unique module type ID, allowing sensor pitch/seperation of module to be determined in FW. + + unsigned int moduleType = 999; + constexpr float tol = 0.001; // Tolerance + + for (unsigned int i = 0; i < moduleTypeCfg_.pitchVsType.size(); i++) { + if (std::abs(pitch - moduleTypeCfg_.pitchVsType[i]) < tol && + std::abs(space - moduleTypeCfg_.spaceVsType[i]) < tol && barrel == moduleTypeCfg_.barrelVsType[i] && + tiltedBarrel == moduleTypeCfg_.tiltedVsType[i] && psModule == moduleTypeCfg_.psVsType[i]) { + moduleType = i; + } + } + + if (moduleType == 999) { + std::stringstream text; + text << "WARNING: TrackerModule found tracker module type unknown to firmware: pitch=" << pitch + << " separation=" << space << " barrel=" << barrel << " tilted=" << tiltedBarrel << " PS=" << psModule; + std::call_once( + printOnce, [](string t) { edm::LogWarning("L1track") << t; }, text.str()); + } + return moduleType; + } +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/TrkRZfilter.cc b/L1Trigger/TrackFindingTMTT/src/TrkRZfilter.cc new file mode 100644 index 0000000000000..f5794d769e4d1 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/TrkRZfilter.cc @@ -0,0 +1,291 @@ +#include "FWCore/Utilities/interface/Exception.h" + +#include "L1Trigger/TrackFindingTMTT/interface/TrkRZfilter.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/PrintL1trk.h" + +#include + +using namespace std; + +namespace tmtt { + + //=== Initialize configuration parameters, and note eta range covered by sector and phi coordinate of its centre. + + TrkRZfilter::TrkRZfilter(const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float etaMinSector, + float etaMaxSector, + float phiCentreSector) + : // Configuration parameters. + settings_(settings), + // Sector number. + iPhiSec_(iPhiSec), + iEtaReg_(iEtaReg) { + // Eta range of sector & phi coord of its centre. + etaMinSector_ = etaMinSector; + etaMaxSector_ = etaMaxSector; + phiCentreSector_ = phiCentreSector; + + // Note that no estimate of the r-z helix params yet exists. + rzHelix_set_ = false; // No valid estimate yet. + + // Calculate z coordinate a track would have at given radius if on eta sector boundary. + chosenRofZ_ = settings->chosenRofZ(); + zTrkMinSector_ = chosenRofZ_ / tan(2. * atan(exp(-etaMinSector_))); + zTrkMaxSector_ = chosenRofZ_ / tan(2. * atan(exp(-etaMaxSector_))); + unsigned int zbits = settings->zBits(); + unsigned int rtbits = settings_->rtBits(); + float zrange = settings_->zRange(); + float rtRange = settings_->rtRange(); + float zMultiplier = pow(2., zbits) / zrange; + float rMultiplier = pow(2., rtbits) / rtRange; + + if (settings_->enableDigitize()) { + zTrkMinSector_ = floor(zTrkMinSector_ * zMultiplier) / zMultiplier; + zTrkMaxSector_ = floor(zTrkMaxSector_ * zMultiplier) / zMultiplier; + chosenRofZ_ = floor(chosenRofZ_ * rMultiplier) / rMultiplier; + } + + // Assumed length of beam-spot in z. + beamWindowZ_ = settings->beamWindowZ(); + + // Name of r-z track filter algorithm to run. + rzFilterName_ = settings->rzFilterName(); + + // --- Options for Seed filter. + // Keep stubs compatible with all possible good seed. + keepAllSeed_ = settings->keepAllSeed(); + //Added resolution for a tracklet-like filter algorithm, beyond that estimated from hit resolution. + seedResCut_ = settings->seedResCut(); + // Maximum number of seed combinations to bother checking per track candidate. + maxSeedCombinations_ = settings->maxSeedCombinations(); + // Maximum number of seed combinations consistent with sector (z0,eta) constraints to bother checking per track candidate. + maxGoodSeedCombinations_ = settings->maxGoodSeedCombinations(); + // Maximum number of seeds that a single stub can be included in. + maxSeedsPerStub_ = settings->maxSeedsPerStub(); + // Reject tracks whose estimated rapidity from seed filter is inconsistent range of with eta sector. (Kills some duplicate tracks). + zTrkSectorCheck_ = settings->zTrkSectorCheck(); + + // For debugging + minNumMatchLayers_ = settings->minNumMatchLayers(); + } + + //=== Filters track candidates (found by the r-phi Hough transform), removing inconsistent stubs from the tracks, + //=== also killing some of the tracks altogether if they are left with too few stubs. + //=== Also adds an estimate of r-z helix parameters to the selected track objects, if the filters used provide this. + + list TrkRZfilter::filterTracks(const list& tracks) { + list filteredTracks; + + for (const L1track2D& trkIN : tracks) { + const vector& stubs = trkIN.stubs(); // stubs assigned to track + + // Declare this to be worth keeping (for now). + bool trackAccepted = true; + + // Note that no estimate of the r-z helix params of this track yet exists. + rzHelix_set_ = false; // No valid estimate yet. + + // Digitize stubs for r-z filter if required. + if (settings_->enableDigitize()) { + for (Stub* s : stubs) { + s->digitize(iPhiSec_, Stub::DigiStage::SF); + } + } + + //--- Filter stubs assigned to track, checking they are consistent with requested criteria. + + // Get debug printout for specific regions. + bool print = false; + + vector filteredStubs = stubs; + if (rzFilterName_ == "SeedFilter") { + filteredStubs = this->seedFilter(filteredStubs, trkIN.qOverPt(), print); + } else { + throw cms::Exception("BadConfig") << "TrkRzFilter: ERROR unknown r-z track filter requested: " << rzFilterName_; + } + + // Check if track still has stubs in enough layers after filter. + + unsigned int numLayersAfterFilters = Utility::countLayers(settings_, filteredStubs); + if (numLayersAfterFilters < settings_->minFilterLayers()) + trackAccepted = false; + //if (numLayersAfterFilters < Utility::numLayerCut(Utility::AlgoStep::SEED, settings_, iPhiSec_, iEtaReg_, std::abs(trkIN.qOverPt()))) trackAccepted = false; + + if (trackAccepted) { + // Estimate r-z helix parameters from centre of eta-sector if no estimate provided by r-z filter. + if (!rzHelix_set_) + this->estRZhelix(); + + pair helixRZ(rzHelix_z0_, rzHelix_tanL_); + + // Create copy of original track, except now using its filtered stubs, to be added to filteredTrack collection. + filteredTracks.emplace_back(settings_, + filteredStubs, + trkIN.cellLocationHT(), + trkIN.helix2D(), + helixRZ, + trkIN.iPhiSec(), + trkIN.iEtaReg(), + trkIN.optoLinkID(), + trkIN.mergedHTcell()); + } + } + + return filteredTracks; + } + + //=== Use Seed Filter to produce a filtered collection of stubs on this track candidate that are consistent with a straight line + //=== in r-z using tracklet algo. + + vector TrkRZfilter::seedFilter(const std::vector& stubs, float trkQoverPt, bool print) { + unsigned int numLayers; //Num of Layers in the cell after that filter has been applied + std::vector filtStubs = stubs; // Copy stubs vector in filtStubs + bool FirstSeed = true; + //Allowed layers for the first & second seeding stubs + constexpr std::array FirstSeedLayers = {{1, 2, 11, 21, 3, 12, 22, 4}}; + constexpr std::array SecondSeedLayers = {{1, 2, 11, 3, 21, 22, 12, 23, 13, 4}}; + set uniqueFilteredStubs; + + unsigned int numSeedCombinations = 0; // Counter for number of seed combinations considered. + unsigned int numGoodSeedCombinations = 0; // Counter for seed combinations with z0 within beam spot length. + vector filteredStubs; // Filter Stubs vector to be returned + + unsigned int oldNumLay = 0; //Number of Layers counter, used to keep the seed with more layers + + auto orderByLayer = [](const Stub* a, const Stub* b) { return bool(a->layerId() < b->layerId()); }; + std::sort(filtStubs.begin(), filtStubs.end(), orderByLayer); + + // Loop over stubs in the HT Cell + for (Stub* s0 : filtStubs) { + // Select the first available seeding stub (r<70) + if (s0->psModule() && std::find(std::begin(FirstSeedLayers), std::end(FirstSeedLayers), s0->layerId()) != + std::end(FirstSeedLayers)) { + unsigned int numSeedsPerStub = 0; + + for (Stub* s1 : filtStubs) { + if (numGoodSeedCombinations < maxGoodSeedCombinations_ && numSeedCombinations < maxSeedCombinations_ && + numSeedsPerStub < maxSeedsPerStub_) { + // Select the second seeding stub (r<90) + if (s1->psModule() && s1->layerId() > s0->layerId() && + std::find(std::begin(SecondSeedLayers), std::end(SecondSeedLayers), s1->layerId()) != + std::end(SecondSeedLayers)) { + numSeedsPerStub++; + numSeedCombinations++; //Increase filter cycles counter + if (print) + PrintL1trk() << "s0: " + << "z: " << s0->z() << ", r: " << s0->r() << ", id:" << s0->layerId() << " ****** s1: " + << "z: " << s1->z() << ", r: " << s1->r() << ", id:" << s1->layerId(); + //double sumSeedDist = 0.; + //double oldSumSeedDist = 1000000.; //Define variable used to estimate the quality of seeds + vector tempStubs; //Create a temporary container for stubs + tempStubs.push_back(s0); //Store the first seeding stub in the temporary container + tempStubs.push_back(s1); //Store the second seeding stub in the temporary container + + // Estimate a value of z at the beam spot using the two seeding stubs + double z0 = s1->z() + (-s1->z() + s0->z()) * s1->r() / (s1->r() - s0->r()); + // COMMENTED OUT CODE GIVES OPTION OF ALLOWING FOR UNCERTAINTY. NOT WORTH IT? + //double z0err = s1->sigmaZ() + ( s1->sigmaZ() + s0->sigmaZ() )*s1->r()/std::abs(s1->r()-s0->r()) + std::abs(-s1->z()+s0->z())*(s1->sigmaR()*std::abs(s1->r()-s0->r()) + s1->r()*(s1->sigmaR() + s0->sigmaR()) )/((s1->r()-s0->r())*(s1->r()-s0->r())); + // Estimate a value of z at a chosen Radius using the two seeding stubs + float zTrk = s1->z() + (-s1->z() + s0->z()) * (s1->r() - chosenRofZ_) / (s1->r() - s0->r()); + // float zTrkErr = s1->sigmaZ() + ( s1->sigmaZ() + s0->sigmaZ() )*std::abs(s1->r()-chosenRofZ_)/std::abs(s1->r()-s0->r()) + std::abs(-s1->z()+s0->z())*(s1->sigmaR()*std::abs(s1->r()-s0->r()) + std::abs(s1->r()-chosenRofZ_)*(s1->sigmaR() + s0->sigmaR()) )/((s1->r()-s0->r())*(s1->r()-s0->r())); + float leftZtrk = zTrk * std::abs(s1->r() - s0->r()); + float rightZmin = zTrkMinSector_ * std::abs(s1->r() - s0->r()); + float rightZmax = zTrkMaxSector_ * std::abs(s1->r() - s0->r()); + + // If z0 is within the beamspot range loop over the other stubs in the cell + //if (std::abs(z0)<=beamWindowZ_+z0err) { + if (std::abs(z0) <= beamWindowZ_) { + // Check track r-z helix parameters are consistent with it being assigned to current rapidity sector (kills duplicates due to overlapping sectors). + // if ( (! zTrkSectorCheck_) || (zTrk > zTrkMinSector_ - zTrkErr && zTrk < zTrkMaxSector_ + zTrkErr) ) { + if ((!zTrkSectorCheck_) || (leftZtrk > rightZmin && leftZtrk < rightZmax)) { + numGoodSeedCombinations++; + unsigned int LiD = 0; //Store the layerId of the stub (KEEP JUST ONE STUB PER LAYER) + // double oldseed = 1000.; //Store the seed value of the current stub (KEEP JUST ONE STUB PER LAYER) + + // Loop over stubs in vector different from the seeding stubs + for (Stub* s : filtStubs) { + // if(s!= s0 && s!= s1){ + // Calculate the seed and its tolerance + double seedDist = + (s->z() - s1->z()) * (s1->r() - s0->r()) - (s->r() - s1->r()) * (s1->z() - s0->z()); + double seedDistRes = (s->sigmaZ() + s1->sigmaZ()) * std::abs(s1->r() - s0->r()) + + (s->sigmaR() + s1->sigmaR()) * std::abs(s1->z() - s0->z()) + + (s0->sigmaZ() + s1->sigmaZ()) * std::abs(s->r() - s1->r()) + + (s0->sigmaR() + s1->sigmaR()) * std::abs(s->z() - s1->z()); + seedDistRes *= seedResCut_; // Scale cut by this amount. + //If seed is lower than the tolerance push back the stub (KEEP JUST ONE STUB PER LAYER, NOT ENABLED BY DEFAULT) + if (std::abs(seedDist) <= seedDistRes) { + if (s->layerId() != LiD and s->layerId() != s0->layerId() and s->layerId() != s1->layerId()) { + tempStubs.push_back(s); + LiD = s->layerId(); + } + } + } + } + } + + numLayers = Utility::countLayers( + settings_, tempStubs); // Count the number of layers in the temporary stubs container + + // Check if the current seed has more layers then the previous one (Keep the best seed) + if (keepAllSeed_ == false) { + if (numLayers > oldNumLay) { + // Check if the current seed has better quality than the previous one + //if(sumSeedDist < oldSumSeedDist){ + filteredStubs = + tempStubs; //Copy the temporary stubs vector in the filteredStubs vector, which will be returned + // oldSumSeedDist = sumSeedDist; //Update value of oldSumSeedDist + oldNumLay = numLayers; //Update value of oldNumLay + rzHelix_z0_ = z0; //Store estimated z0 + rzHelix_tanL_ = (s1->z() - s0->z()) / (s1->r() - s0->r()); // Store estimated tanLambda + rzHelix_set_ = true; + } + //} + } else { + // Check if the current seed satisfies the minimum layers requirement (Keep all seed algorithm) + if (numLayers >= Utility::numLayerCut( + Utility::AlgoStep::SEED, settings_, iPhiSec_, iEtaReg_, std::abs(trkQoverPt))) { + uniqueFilteredStubs.insert(tempStubs.begin(), tempStubs.end()); //Insert the uniqueStub set + + // If these are the first seeding stubs store the values of z0 and tanLambda + if (FirstSeed) { + FirstSeed = false; + rzHelix_z0_ = z0; //Store estimated z0 + rzHelix_tanL_ = (s1->z() - s0->z()) / (s1->r() - s0->r()); // Store estimated tanLambda + rzHelix_set_ = true; + } + } + } + } + } + } + } + } + + // Copy stubs from the uniqueFilteredStubs set to the filteredStubs vector (Keep all seed algorithm) + if (keepAllSeed_ == true) { + for (Stub* stub : uniqueFilteredStubs) { + filteredStubs.push_back(stub); + } + } + + // Note number of seed combinations used for this track. + numSeedCombsPerTrk_.push_back(numSeedCombinations); + numGoodSeedCombsPerTrk_.push_back(numGoodSeedCombinations); + + return filteredStubs; // Return the filteredStubs vector + } + + // Estimate r-z helix parameters from centre of eta-sector if no better estimate provided by r-z filter. + + void TrkRZfilter::estRZhelix() { + rzHelix_z0_ = 0.; + rzHelix_tanL_ = 0.5 * (1 / tan(2 * atan(exp(-etaMinSector_))) + 1 / tan(2 * atan(exp(-etaMaxSector_)))); + rzHelix_set_ = true; + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/src/Utility.cc b/L1Trigger/TrackFindingTMTT/src/Utility.cc new file mode 100644 index 0000000000000..32f285571f150 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/src/Utility.cc @@ -0,0 +1,184 @@ +#include "L1Trigger/TrackFindingTMTT/interface/Utility.h" +#include "L1Trigger/TrackFindingTMTT/interface/TP.h" +#include "L1Trigger/TrackFindingTMTT/interface/Stub.h" +#include "L1Trigger/TrackFindingTMTT/interface/Settings.h" + +#include "FWCore/Utilities/interface/Exception.h" + +#include + +using namespace std; + +namespace tmtt { + + //=== Count number of tracker layers a given list of stubs are in. + //=== By default, consider both PS+2S modules, but optionally consider only the PS ones. + + unsigned int Utility::countLayers(const Settings* settings, + const std::vector& stubs, + bool disableReducedLayerID, + bool onlyPS) { + std::vector stubsConst(stubs.begin(), stubs.end()); + return countLayers(settings, stubsConst, disableReducedLayerID, onlyPS); + } + + unsigned int Utility::countLayers(const Settings* settings, + const vector& vstubs, + bool disableReducedLayerID, + bool onlyPS) { + //=== Unpack configuration parameters + + // Note if using reduced layer ID, so tracker layer can be encoded in 3 bits. + const bool reduceLayerID = settings->reduceLayerID(); + + // Disable use of reduced layer ID if requested, otherwise take from cfg. + bool reduce = (disableReducedLayerID) ? false : reduceLayerID; + + // Count layers using CMSSW layer ID. + unordered_set foundLayers; + for (const Stub* stub : vstubs) { + if ((!onlyPS) || stub->psModule()) { // Consider only stubs in PS modules if that option specified. + // Use either normal or reduced layer ID depending on request. + int layerID = reduce ? stub->layerIdReduced() : stub->layerId(); + foundLayers.insert(layerID); + } + } + + return foundLayers.size(); + } + + //=== Given a set of stubs (presumably on a reconstructed track candidate) + //=== return the best matching Tracking Particle (if any), + //=== the number of tracker layers in which one of the stubs matched one from this tracking particle, + //=== and the list of the subset of the stubs which match those on the tracking particle. + + const TP* Utility::matchingTP(const Settings* settings, + const std::vector& vstubs, + unsigned int& nMatchedLayersBest, + std::vector& matchedStubsBest) { + std::vector stubsConst(vstubs.begin(), vstubs.end()); + return matchingTP(settings, stubsConst, nMatchedLayersBest, matchedStubsBest); + } + + const TP* Utility::matchingTP(const Settings* settings, + const vector& vstubs, + unsigned int& nMatchedLayersBest, + vector& matchedStubsBest) { + // Get matching criteria + const double minFracMatchStubsOnReco = settings->minFracMatchStubsOnReco(); + const double minFracMatchStubsOnTP = settings->minFracMatchStubsOnTP(); + const unsigned int minNumMatchLayers = settings->minNumMatchLayers(); + const unsigned int minNumMatchPSLayers = settings->minNumMatchPSLayers(); + + // Loop over the given stubs, looking at the TP that produced each one. + + map > tpsToStubs; + map > tpsToStubsStrict; + + for (const Stub* s : vstubs) { + // If this stub was produced by one or more TPs, store a link from the TPs to the stub. + // (The assocated TPs here are influenced by config param "StubMatchStrict"). + for (const TP* tp_i : s->assocTPs()) { + tpsToStubs[tp_i].push_back(s); + } + // To resolve tie-break situations, do the same, but now only considering strictly associated TP, where the TP contributed + // to both clusters making up stub. + if (s->assocTP() != nullptr) { + tpsToStubsStrict[s->assocTP()].push_back(s); + } + } + + // Loop over all the TP that matched the given stubs, looking for the best matching TP. + + nMatchedLayersBest = 0; + unsigned int nMatchedLayersStrictBest = 0; + matchedStubsBest.clear(); + const TP* tpBest = nullptr; + + for (const auto& iter : tpsToStubs) { + const TP* tp = iter.first; + const vector matchedStubsFromTP = iter.second; + + const vector matchedStubsStrictFromTP = + tpsToStubsStrict[tp]; // Empty vector, if this TP didnt produce both clusters in stub. + + // Count number of the given stubs that came from this TP. + unsigned int nMatchedStubs = matchedStubsFromTP.size(); + // Count number of tracker layers in which the given stubs came from this TP. + unsigned int nMatchedLayers = Utility::countLayers(settings, matchedStubsFromTP, true); + unsigned int nMatchedPSLayers = Utility::countLayers(settings, matchedStubsFromTP, true, true); + + // For tie-breaks, count number of tracker layers in which both clusters of the given stubs came from this TP. + unsigned int nMatchedLayersStrict = Utility::countLayers(settings, matchedStubsStrictFromTP, true); + + // If enough layers matched, then accept this tracking particle. + // Of the three criteria used here, usually only one is used, with the cuts on the other two set ultra loose. + + if (nMatchedStubs >= + minFracMatchStubsOnReco * vstubs.size() && // Fraction of matched stubs relative to number of given stubs + nMatchedStubs >= minFracMatchStubsOnTP * + tp->numAssocStubs() && // Fraction of matched stubs relative to number of stubs on TP. + nMatchedLayers >= minNumMatchLayers && + nMatchedPSLayers >= minNumMatchPSLayers) { // Number of matched layers + // In case more than one matching TP found in this cell, note best match based on number of matching layers. + // In tie-break situation, count layers in which both clusters in stub came from same TP. + if (nMatchedLayersBest < nMatchedLayers || + (nMatchedLayersBest == nMatchedLayers && nMatchedLayersStrictBest < nMatchedLayersStrict)) { + // Store data for this TP match. + nMatchedLayersBest = nMatchedLayers; + matchedStubsBest = matchedStubsFromTP; + tpBest = tp; + } + } + } + + return tpBest; + } + + //=== Determine min number of layers a track candidate must have stubs in to be defined as a track. + //=== 1st argument indicates from which step in chain this function is called: HT, SEED, DUP or FIT. + + unsigned int Utility::numLayerCut(Utility::AlgoStep algo, + const Settings* settings, + unsigned int iPhiSec, + unsigned int iEtaReg, + float invPt, + float eta) { + if (algo == HT || algo == SEED || algo == DUP || algo == FIT) { + unsigned int nLayCut = settings->minStubLayers(); + + //--- Check if should reduce cut on number of layers by 1 for any reason. + + bool reduce = false; + + // to increase efficiency for high Pt tracks. + bool applyMinPt = (settings->minPtToReduceLayers() > 0); + if (applyMinPt && std::abs(invPt) < 1 / settings->minPtToReduceLayers()) + reduce = true; + + // or to increase efficiency in the barrel-endcap transition or very forward regions. + const vector& etaSecsRed = settings->etaSecsReduceLayers(); + if (std::count(etaSecsRed.begin(), etaSecsRed.end(), iEtaReg) != 0) + reduce = true; + + // or to increase efficiency in sectors containing dead modules (hard-wired in KF only) + // Not implemented here. + + if (reduce) + nLayCut--; + + constexpr unsigned int minLayCut = 4; // Minimum viable layer cut. + nLayCut = std::max(nLayCut, minLayCut); + + // Seed Filter & Track Fitters require only 4 layers. + constexpr unsigned int nFitLayCut = 4; + if (algo == SEED || algo == FIT) + nLayCut = nFitLayCut; + + return nLayCut; + } else { + throw cms::Exception("LogicError") << "Utility::numLayerCut() called with invalid algo argument! " << algo; + } + } + +} // namespace tmtt diff --git a/L1Trigger/TrackFindingTMTT/test/L1TrackNtupleMaker_cfg.py b/L1Trigger/TrackFindingTMTT/test/L1TrackNtupleMaker_cfg.py new file mode 100644 index 0000000000000..733453e741bbc --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/test/L1TrackNtupleMaker_cfg.py @@ -0,0 +1,211 @@ +############################################################ +# define basic process +############################################################ + +import FWCore.ParameterSet.Config as cms +import FWCore.Utilities.FileUtils as FileUtils +import os +process = cms.Process("L1TrackNtuple") + + +############################################################ +# edit options here +############################################################ + +GEOMETRY = "D49" + +# Specify L1 tracking algo ('HYBRID', 'HYBRID_DISPLACED', 'TMTT','HYBRID_FLOAT', 'TRACKLET_FLOAT'), +# (To run Tracklet, set L1TRKALGO='HYBRID', and set USEHYBRID in TrackFindingTracklet/interface/Settings.h) +L1TRKALGO = 'TMTT' + +# Write output dataset? +WRITE_DATA = False + +if (L1TRKALGO == 'HYBRID_FLOAT'): + if ( not os.path.exists( os.environ['CMSSW_BASE']+'/src/L1Trigger/HybridFloat' ) ): + print "=== ERROR: Please checkout HybridFloat code from git before using this option ==="; exit + + +############################################################ +# import standard configurations +############################################################ + +process.load('Configuration.StandardSequences.Services_cff') +process.load('FWCore.MessageService.MessageLogger_cfi') +process.MessageLogger.categories.append('L1track') +process.MessageLogger.categories.append('Tracklet') +process.load('Configuration.EventContent.EventContent_cff') +process.load('Configuration.StandardSequences.MagneticField_cff') + +if GEOMETRY == "D49": + print "using geometry " + GEOMETRY + " (tilted)" + process.load('Configuration.Geometry.GeometryExtended2026D49Reco_cff') + process.load('Configuration.Geometry.GeometryExtended2026D49_cff') +else: + print "this is not a valid geometry!!!" + exit + +process.load('Configuration.StandardSequences.EndOfProcess_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') + +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:phase2_realistic', '') + + +############################################################ +# input and output +############################################################ + +process.maxEvents = cms.untracked.PSet(input = cms.untracked.int32(50)) + +# Get list of MC datasets from repo, or specify yourself. + +def getTxtFile(txtFileName): + return FileUtils.loadListFromFile(os.environ['CMSSW_BASE']+'/src/L1Trigger/TrackFindingTMTT/data/MCsamples/'+txtFileName) + +# MC txt files are in https://github.com/cms-data/L1Trigger-TrackFindingTMTT. +if GEOMETRY == "D49": + inputMC = getTxtFile('1110/RelVal/TTbar/PU200.txt') + +else: + print "this is not a valid geometry!!!" + +process.source = cms.Source("PoolSource", + fileNames = cms.untracked.vstring(*inputMC), + inputCommands = cms.untracked.vstring( + 'keep *_*_*_*', + 'drop l1tEMTFHit2016*_*_*_*', + 'drop l1tEMTFTrack2016*_*_*_*' + ) + ) + +process.TFileService = cms.Service("TFileService", fileName = cms.string('TTbar_PU200_'+L1TRKALGO+'.root'), closeFileFast = cms.untracked.bool(True)) + +process.Timing = cms.Service("Timing", summaryOnly = cms.untracked.bool(True)) + + +############################################################ +# L1 tracking +############################################################ + +# remake stubs ? +process.load('L1Trigger.TrackTrigger.TrackTrigger_cff') +from L1Trigger.TrackTrigger.TTStubAlgorithmRegister_cfi import * +process.load("SimTracker.TrackTriggerAssociation.TrackTriggerAssociator_cff") + +if GEOMETRY != "TkOnly": + from SimTracker.TrackTriggerAssociation.TTClusterAssociation_cfi import * + TTClusterAssociatorFromPixelDigis.digiSimLinks = cms.InputTag("simSiPixelDigis","Tracker") + +process.TTClusterStub = cms.Path(process.TrackTriggerClustersStubs) +process.TTClusterStubTruth = cms.Path(process.TrackTriggerAssociatorClustersStubs) + +NHELIXPAR = 4 +if (L1TRKALGO == 'HYBRID'): + process.load("L1Trigger.TrackFindingTracklet.Tracklet_cfi") + L1TRK_PROC = process.TTTracksFromTrackletEmulation + L1TRK_NAME = "TTTracksFromTrackletEmulation" + L1TRK_LABEL = "Level1TTTracks" +elif (L1TRKALGO == 'HYBRID_DISPLACED'): + process.load("L1Trigger.TrackFindingTracklet.Tracklet_cfi") + L1TRK_PROC = process.TTTracksFromExtendedTrackletEmulation + L1TRK_NAME = "TTTracksFromExtendedTrackletEmulation" + L1TRK_LABEL = "Level1TTTracks" + NHELIXPAR = 5 +elif (L1TRKALGO == 'TMTT'): + process.load("L1Trigger.TrackFindingTMTT.TMTrackProducer_Ultimate_cff") + L1TRK_PROC = process.TMTrackProducer + L1TRK_NAME = "TMTrackProducer" + L1TRK_LABEL = "TML1TracksKF4ParamsComb" + L1TRK_PROC.EnableMCtruth = cms.bool(False) # Reduce CPU use by disabling internal histos. + L1TRK_PROC.EnableHistos = cms.bool(False) +elif (L1TRKALGO == 'HYBRID_FLOAT'): + process.load("L1Trigger.HybridFloat.HybridTrackProducer_cff") + L1TRK_PROC = process.HybridTrackProducer + L1TRK_NAME = "HybridTrackProducer" + L1TRK_LABEL = "HybridL1TracksKF4ParamsComb" + L1TRK_PROC.EnableMCtruth = cms.bool(False) # Reduce CPU use by disabling internal histos. + L1TRK_PROC.EnableHistos = cms.bool(False) +elif (L1TRKALGO == 'TRACKLET_FLOAT'): + process.load("L1Trigger.TrackFindingTracklet.L1TrackletTracks_cff") + L1TRK_PROC = process.TTTracksFromTracklet + L1TRK_NAME = "TTTracksFromTracklet" + L1TRK_LABEL = "Level1TTTracks" +else: + print "ERROR: Unknown L1TRKALGO option" + exit(1) + +process.load("RecoVertex.BeamSpotProducer.BeamSpot_cfi") +process.load("SimTracker.TrackTriggerAssociation.TrackTriggerAssociator_cff") +process.TTTrackAssociatorFromPixelDigis.TTTracks = cms.VInputTag( cms.InputTag(L1TRK_NAME, L1TRK_LABEL) ) + +## emulation +process.TTTracksEmulation = cms.Path(process.offlineBeamSpot*L1TRK_PROC) +process.TTTracksEmulationWithTruth = cms.Path(process.offlineBeamSpot*L1TRK_PROC*process.TrackTriggerAssociatorTracks) + + +############################################################ +# Define the track ntuple process, MyProcess is the (unsigned) PDGID corresponding to the process which is run +# e.g. single electron/positron = 11 +# single pion+/pion- = 211 +# single muon+/muon- = 13 +# pions in jets = 6 +# taus = 15 +# all TPs = 1 +############################################################ + +process.L1TrackNtuple = cms.EDAnalyzer('L1TrackNtupleMaker', + MyProcess = cms.int32(1), + DebugMode = cms.bool(False), # printout lots of debug statements + SaveAllTracks = cms.bool(True), # save *all* L1 tracks, not just truth matched to primary particle + SaveStubs = cms.bool(False), # save some info for *all* stubs + L1Tk_nPar = cms.int32(NHELIXPAR), # use 4 or 5-parameter L1 tracking? + L1Tk_minNStub = cms.int32(4), # L1 tracks with >= 4 stubs + TP_minNStub = cms.int32(4), # require TP to have >= X number of stubs associated with it + TP_minNStubLayer = cms.int32(4), # require TP to have stubs in >= X layers/disks + TP_minPt = cms.double(2.0), # only save TPs with pt > X GeV + TP_maxEta = cms.double(2.5), # only save TPs with |eta| < X + TP_maxZ0 = cms.double(30.0), # only save TPs with |z0| < X cm + L1TrackInputTag = cms.InputTag(L1TRK_NAME, L1TRK_LABEL), # TTTrack input + MCTruthTrackInputTag = cms.InputTag("TTTrackAssociatorFromPixelDigis", L1TRK_LABEL), ## MCTruth input + # other input collections + L1StubInputTag = cms.InputTag("TTStubsFromPhase2TrackerDigis","StubAccepted"), + MCTruthClusterInputTag = cms.InputTag("TTClusterAssociatorFromPixelDigis", "ClusterAccepted"), + MCTruthStubInputTag = cms.InputTag("TTStubAssociatorFromPixelDigis", "StubAccepted"), + TrackingParticleInputTag = cms.InputTag("mix", "MergedTrackTruth"), + TrackingVertexInputTag = cms.InputTag("mix", "MergedTrackTruth"), + ## tracking in jets stuff (--> requires AK4 genjet collection present!) + TrackingInJets = cms.bool(True), + GenJetInputTag = cms.InputTag("ak4GenJets", ""), + ) + +process.ana = cms.Path(process.L1TrackNtuple) + +# use this if you want to re-run the stub making +# process.schedule = cms.Schedule(process.TTClusterStub,process.TTClusterStubTruth,process.TTTracksEmulationWithTruth,process.ana) + +# use this if cluster/stub associators not available +# process.schedule = cms.Schedule(process.TTClusterStubTruth,process.TTTracksEmulationWithTruth,process.ana) + +# use this to only run tracking + track associator +process.schedule = cms.Schedule(process.TTTracksEmulationWithTruth,process.ana) + +if (WRITE_DATA): + + process.writeDataset = cms.OutputModule("PoolOutputModule", + splitLevel = cms.untracked.int32(0), + eventAutoFlushCompressedSize = cms.untracked.int32(5242880), + outputCommands = process.RAWSIMEventContent.outputCommands, + fileName = cms.untracked.string('output_dataset.root'), ## ADAPT IT ## + dataset = cms.untracked.PSet( + filterName = cms.untracked.string(''), + dataTier = cms.untracked.string('GEN-SIM') + ) + ) + # Include TMTT L1 tracks & associators + stubs. + process.writeDataset.outputCommands.append('keep *TTTrack*_*_*_*') + process.writeDataset.outputCommands.append('keep *TTStub*_*_*_*') + + process.pd = cms.EndPath(process.writeDataset) + process.schedule.append(process.pd) + diff --git a/L1Trigger/TrackFindingTMTT/test/PlotEtaSectors.C b/L1Trigger/TrackFindingTMTT/test/PlotEtaSectors.C new file mode 100644 index 0000000000000..8a7303811562a --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/test/PlotEtaSectors.C @@ -0,0 +1,183 @@ +{ + //=== Draw rapidity sector boundaries on picture of tracker in r-z view. + + // WARNING: this script only works on machines where the line: + // OpenGL.CanvasPreferGL: 1 + // appears in $ROOTSYS/etc/system.rootrc (not at RAL). + // Required for semi-transparent plotting. + + // In unnamed scripts, variables not forgotten at end, so must delete them before rerunning script, so ... + gROOT->Reset(); + + // Adjust these to line up histogram hisTracker with underlying image of CMS Tracker. + // Then when aligned, comment out line trackerBorder.Draw() below. + const float leftMargin = 23; + const float rightMargin = 33; + const float topMargin = 13; + const float bottomMargin = 14; + + gStyle->SetOptTitle(0); + gStyle->SetOptStat(""); + gStyle->SetPadGridX(false); + gStyle->SetPadGridY(false); + + const float trkInRad = 20; + const float trkOutRad = 110; + const float trkLength = 270; + const float beamLen = 15; + + const unsigned int nSec = 9; + const float chosenR = 50; + const unsigned int nSecEdges = nSec + 1; + // Standard eta sectors + const float eta[nSecEdges] = {0, 0.31, 0.61, 0.89, 1.16, 1.43, 1.7, 1.95, 2.16, 2.4}; + // Sectors optimised by Ben Gayther to balance GP output data rate (at expense of increased HT tracks). + //const float eta[nSecEdges] = {0.0, 0.19, 0.38, 0.57, 0.77, 1.01, 1.31, 1.66, 2.03, 2.40}; + const unsigned int nSubSec = 2; + const unsigned int nSubSecEdges = nSubSec + 1; + + // Optionally draw a stub with the given digitized (r,z) coordinates. + const bool drawStub = true; + const int iDigi_RT = 492; + const int iDigi_Z = -1403; + + TCanvas d1("d1", "d1", 1000, 800); + + // Open picture of CMS tracker + // http://ghugo.web.cern.ch/ghugo/layouts/cabling/OT614_200_IT404_layer2_10G/layout.html + // Adjust the range of trackerBorder below to correspond to the coordinate range shown in this picture. + TImage *img = TImage::Open("TrackerLayout.png"); + img->Draw("x"); + d1.Update(); + + //Create a transparent pad filling the full canvas + TPad p("p", "p", 0, 0, 1, 1); + p.Range(-beamLen - leftMargin, -1 - bottomMargin, trkLength + 30 + rightMargin, trkOutRad + 15 + topMargin); + p.SetFillStyle(4000); + p.SetFrameFillStyle(4000); + p.Draw(); + p.cd(); + + TPolyLine trackerBorder; + + trackerBorder.SetNextPoint(0., 0.); + trackerBorder.SetNextPoint(295., 0.); + trackerBorder.SetNextPoint(295., 123.); + trackerBorder.SetNextPoint(0., 123.); + trackerBorder.SetNextPoint(0., 0.); + //trackerBorder.Draw(); + + /* + TPolyLine subsecBoundary[nSec][nSubSecEdges]; + + // Draw sub-sector boundaries. + for (unsigned int i = 0; i < nSec; i++) { + float subsecWidth = (eta[i+1] - eta[i])/float(nSubSec); + for (unsigned int j = 0; j < nSubSecEdges; j++) { + float subsecEtaEdge = eta[i] + subsecWidth * j; + // z at r = chosenR; + float z = chosenR/tan(2 * atan(exp(-subsecEtaEdge))); + // Calculate (r,z) at periphery of Tracker from two ends of beam spot. + // Start by assuming exit through barrel. + float rPeriphNeg = trkOutRad; + float rPeriphPos = trkOutRad; + float zPeriphNeg = -beamLen + (z + beamLen)*(rPeriphNeg/chosenR); + float zPeriphPos = beamLen + (z - beamLen)*(rPeriphNeg/chosenR); + // Now check if actual exit through endcap. + if (fabs(zPeriphNeg) > trkLength) { + int whichEndcap = (zPeriphNeg + beamLen > 0) ? 1 : -1; + zPeriphNeg = whichEndcap*trkLength; + rPeriphNeg = chosenR*(zPeriphNeg + beamLen)/(z + beamLen); + } + if (fabs(zPeriphPos) > trkLength) { + int whichEndcap = (zPeriphPos - beamLen > 0) ? 1 : -1; + zPeriphPos = whichEndcap*trkLength; + rPeriphPos = chosenR*(zPeriphPos - beamLen)/(z - beamLen); + } + subsecBoundary[i][j].SetNextPoint(-beamLen,0); + subsecBoundary[i][j].SetNextPoint(zPeriphNeg, rPeriphNeg); + subsecBoundary[i][j].SetNextPoint(zPeriphPos, rPeriphPos); + subsecBoundary[i][j].SetNextPoint(beamLen,0); + subsecBoundary[i][j].SetNextPoint(-beamLen,0); + subsecBoundary[i][j].SetFillColor(kYellow); + unsigned int iHash = 3405+k*10; + subsecBoundary[i][j].SetFillStyle(iHash); + subsecBoundary[i][j].Draw("f"); + d1.Update(); + } + } + */ + + TPolyLine secBoundary[nSecEdges]; + + // Draw sector boundaries + for (unsigned int k = 0; k < nSecEdges; k++) { + // z at r = chosenR; + float z = chosenR / tan(2.0 * atan(exp(-eta[k]))); + // Calculate (r,z) at periphery of Tracker from two ends of beam spot. + // Start by assuming exit through barrel. + float rPeriphNeg = trkOutRad; + float rPeriphPos = trkOutRad; + float zPeriphNeg = -beamLen + (z + beamLen) * (rPeriphNeg / chosenR); + float zPeriphPos = beamLen + (z - beamLen) * (rPeriphNeg / chosenR); + // Now check if actual exit through endcap. + if (fabs(zPeriphNeg) > trkLength) { + int whichEndcap = (zPeriphNeg + beamLen > 0) ? 1 : -1; + zPeriphNeg = whichEndcap * trkLength; + rPeriphNeg = chosenR * (zPeriphNeg + beamLen) / (z + beamLen); + } + if (fabs(zPeriphPos) > trkLength) { + int whichEndcap = (zPeriphPos - beamLen > 0) ? 1 : -1; + zPeriphPos = whichEndcap * trkLength; + rPeriphPos = chosenR * (zPeriphPos - beamLen) / (z - beamLen); + } + secBoundary[k].SetNextPoint(-beamLen, 0); + secBoundary[k].SetNextPoint(zPeriphNeg, rPeriphNeg); + secBoundary[k].SetNextPoint(zPeriphPos, rPeriphPos); + secBoundary[k].SetNextPoint(beamLen, 0); + secBoundary[k].SetNextPoint(-beamLen, 0); + secBoundary[k].SetFillColor(kGreen - 2); + //cout<Reset("a"); + gStyle->SetOptTitle(0); + gStyle->SetOptStat(""); + //gStyle->SetOptStat("emr"); + // gStyle->SetOptStat("euom"); + gStyle->SetStatFontSize(0.035); + gStyle->SetHistFillColor(kBlue); + gStyle->SetHistFillStyle(1001); + gStyle->SetMarkerSize(1.2); + + gStyle->SetStatFormat("5.3f"); + gStyle->SetStatFontSize(0.04); + gStyle->SetOptFit(0111); + gStyle->SetStatW(0.30); + gStyle->SetStatH(0.02); + gStyle->SetStatX(0.9); + gStyle->SetStatY(0.9); + gStyle->SetPadLeftMargin(0.20); + gStyle->SetTitleYOffset(1.6); + gStyle->SetTitleSize(0.05, "XYZ"); + + gStyle->SetLabelSize(.04, "x"); + gStyle->SetLabelSize(.04, "y"); + + gStyle->SetCanvasDefH(600); + gStyle->SetCanvasDefW(600); + + TCanvas d1("d1"); + + TH1F* hisMatchTot; + TH1F* hisUnmatchTot; + TH1F* hisMatch; + TH1F* hisUnmatch; + + //TFile file1("out_ttbar_ultimate_20180831_182908/Hist.root"); + //TFile file1("out_ttbar_ultimate_offall_20180831_183257/Hist.root"); + TFile file1("Hist.root"); + + TLegend leg(0.25, 0.15, 0.55, 0.30); + file1.GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofMatched_KF4ParamsComb", hisMatchTot); + file1.GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofUnmatched_KF4ParamsComb", hisUnmatchTot); + if (nSkip == 0) { + file1.GetObject("TMTrackProducer/KF4ParamsComb/KalmanChi2DofSkipLay0Matched_KF4ParamsComb", hisMatch); + file1.GetObject("TMTrackProducer/KF4ParamsComb/KalmanChi2DofSkipLay0Unmatched_KF4ParamsComb", hisUnmatch); + } else if (nSkip == 1) { + file1.GetObject("TMTrackProducer/KF4ParamsComb/KalmanChi2DofSkipLay1Matched_KF4ParamsComb", hisMatch); + file1.GetObject("TMTrackProducer/KF4ParamsComb/KalmanChi2DofSkipLay1Unmatched_KF4ParamsComb", hisUnmatch); + } else if (nSkip == 2) { + file1.GetObject("TMTrackProducer/KF4ParamsComb/KalmanChi2DofSkipLay2Matched_KF4ParamsComb", hisMatch); + file1.GetObject("TMTrackProducer/KF4ParamsComb/KalmanChi2DofSkipLay2Unmatched_KF4ParamsComb", hisUnmatch); + } else if (nSkip == -1) { + file1.GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofMatched_KF4ParamsComb", hisMatch); + file1.GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofUnmatched_KF4ParamsComb", hisUnmatch); + } + //file1.GetObject("TMTrackProducer/KF4ParamsComb/FitBeamChi2DofMatched_KF4ParamsComb", hisMatch); + //file1.GetObject("TMTrackProducer/KF4ParamsComb/FitBeamChi2DofUnmatched_KF4ParamsComb", hisUnmatch); + + TH1* hisMatchCum = hisMatch->GetCumulative(false); + TH1* hisUnmatchCum = hisUnmatch->GetCumulative(false); + unsigned int nBins = hisMatchCum->GetNbinsX(); + + // TH1::GetCumulative() ignores overflow bins, so add them by hand. + float overMatch = hisMatchCum->GetBinContent(nBins + 1); + float overUnmatch = hisUnmatchCum->GetBinContent(nBins + 1); + float lastMatch = hisMatchCum->GetBinContent(nBins); + float lastUnmatch = hisUnmatchCum->GetBinContent(nBins); + hisMatchCum->SetBinContent(nBins, lastMatch + overMatch); + hisUnmatchCum->SetBinContent(nBins, lastUnmatch + overUnmatch); + + hisMatchCum->Scale(1. / hisMatchTot->GetEntries()); + hisUnmatchCum->Scale(1. / hisUnmatchTot->GetEntries()); + hisMatchCum->SetMarkerStyle(20); + hisUnmatchCum->SetMarkerStyle(24); + hisMatchCum->SetAxisRange(0.9, 35, "X"); // dangerous. don't make lower cut too big. + hisMatchCum->SetTitle("; chi2/dof; efficiency loss"); + d1.SetLogx(1); + d1.SetLogy(1); + float ymax = max(hisMatchCum->GetMaximum(), hisUnmatchCum->GetMaximum()); + hisMatchCum->SetMaximum(2 * ymax); + hisMatchCum->SetMarkerStyle(20); + hisMatchCum->Draw("P"); + leg.AddEntry(hisMatchCum, "match", "P"); + hisUnmatchCum->SetMarkerStyle(24); + hisUnmatchCum->Draw("P SAME"); + leg.AddEntry(hisUnmatchCum, "unmatch", "P"); + leg.Draw(); + + d1.Update(); + cin.get(); + + TGraph graph_killed(nBins); + graph_killed.SetTitle("; Good tracks killed; Fake tracks killed"); + for (unsigned int i = 1; i <= nBins + 1; i++) { + float fracMatch = hisMatchCum->GetBinContent(i); + float fracUnmatch = hisUnmatchCum->GetBinContent(i); + graph_killed.SetPoint(i - 1, fracMatch, fracUnmatch); + } + graph_killed.Draw("AP"); + + d1.Update(); + d1.Print("plot.pdf"); + cin.get(); + + file1.Close(); +} diff --git a/L1Trigger/TrackFindingTMTT/test/make_stubs.py b/L1Trigger/TrackFindingTMTT/test/make_stubs.py new file mode 100644 index 0000000000000..6c6f49a192a50 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/test/make_stubs.py @@ -0,0 +1,61 @@ +import FWCore.ParameterSet.Config as cms +import FWCore.Utilities.FileUtils as FileUtils +import FWCore.ParameterSet.VarParsing as VarParsing + +process = cms.Process("STUBS") + +process.load('Configuration.Geometry.GeometryExtended2023D17Reco_cff') +process.load('Configuration.Geometry.GeometryExtended2023D17_cff') +process.load('Configuration.StandardSequences.MagneticField_cff') +process.load('Configuration.EventContent.EventContent_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:upgradePLS3', '') + +process.load("FWCore.MessageLogger.MessageLogger_cfi") + +options = VarParsing.VarParsing ('analysis') + +#--- Specify input MC +options.register('inputMC', '../../../Samples91X/930pre3/TTbar/PU200.txt', VarParsing.VarParsing.multiplicity.singleton, VarParsing.VarParsing.varType.string, "Files to be processed") + +#--- Specify number of events to process. +options.register('Events',1000,VarParsing.VarParsing.multiplicity.singleton, VarParsing.VarParsing.varType.int,"Number of Events to analyze") + +options.parseArguments() + +#--- input and output +list = FileUtils.loadListFromFile(options.inputMC) +readFiles = cms.untracked.vstring(*list) + + +process.options = cms.untracked.PSet( wantSummary = cms.untracked.bool(False) ) +process.maxEvents = cms.untracked.PSet( input = cms.untracked.int32(options.Events) ) + +process.source = cms.Source ("PoolSource", + fileNames = readFiles, + ) + +process.Timing = cms.Service("Timing", summaryOnly = cms.untracked.bool(True)) + +process.RAWSIMoutput = cms.OutputModule("PoolOutputModule", + splitLevel = cms.untracked.int32(0), + eventAutoFlushCompressedSize = cms.untracked.int32(5242880), + outputCommands = process.RAWSIMEventContent.outputCommands, + fileName = cms.untracked.string('output_PU200.root'), ## ADAPT IT ## + dataset = cms.untracked.PSet( + filterName = cms.untracked.string(''), + dataTier = cms.untracked.string('GEN-SIM') + ) +) +process.RAWSIMoutput.outputCommands.append('keep *_*_ClusterAccepted_*') +process.RAWSIMoutput.outputCommands.append('keep *_*_StubAccepted_*') +process.RAWSIMoutput.outputCommands.append('keep *_*_MergedTrackTruth_*') +process.RAWSIMoutput_step = cms.EndPath(process.RAWSIMoutput) + + +process.load('L1Trigger.TrackTrigger.TrackTrigger_cff') +process.load('SimTracker.TrackTriggerAssociation.TrackTriggerAssociator_cff') +process.TTClusterAssociatorFromPixelDigis.digiSimLinks = cms.InputTag("simSiPixelDigis","Tracker") +process.p = cms.Path(process.TrackTriggerClustersStubs * process.TrackTriggerAssociatorClustersStubs) + diff --git a/L1Trigger/TrackFindingTMTT/test/plot.C b/L1Trigger/TrackFindingTMTT/test/plot.C new file mode 100644 index 0000000000000..550310b15c370 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/test/plot.C @@ -0,0 +1,132 @@ +{ + // In unnamed scripts, variables not forgotten at end, so must delete them before rerunning script, so ... + gROOT->Reset("a"); + gStyle->SetOptTitle(0); + gStyle->SetOptStat(""); + //gStyle->SetOptStat("emr"); + // gStyle->SetOptStat("euom"); + gStyle->SetStatFontSize(0.035); + gStyle->SetHistFillColor(kBlue); + gStyle->SetHistFillStyle(1001); + gStyle->SetMarkerSize(2.0); + + gStyle->SetStatFormat("5.3f"); + gStyle->SetStatFontSize(0.04); + gStyle->SetOptFit(0111); + gStyle->SetStatW(0.30); + gStyle->SetStatH(0.02); + gStyle->SetStatX(0.9); + gStyle->SetStatY(0.9); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetTitleYOffset(1.3); + gStyle->SetTitleSize(0.05, "XYZ"); + + gStyle->SetLabelSize(.04, "x"); + gStyle->SetLabelSize(.04, "y"); + + gStyle->SetCanvasDefH(500); + gStyle->SetCanvasDefW(800); + + TCanvas d1("d1"); + + TFile *file[7]; + + /* + file[1] = new TFile("out_ttbar_ultimate_off1_20180831_183411/Hist.root"); // Corr 1 off + file[2] = new TFile("out_ttbar_ultimate_off2_20180831_183030/Hist.root"); // Corr 2 off + file[3] = new TFile("out_ttbar_ultimate_off3_20180831_183117/Hist.root"); // Corr 3 off + file[4] = new TFile("out_ttbar_ultimate_off4_20180831_183220/Hist.root"); // Corr 4 off + file[5] = new TFile("out_ttbar_ultimate_20180831_182908/Hist.root"); // All on + file[6] = new TFile("out_ttbar_ultimate_offall_20180831_183257/Hist.root"); // All off + TLegend leg(0.7,0.15,0.9,0.45); + */ + + // Testing variants + /* + file[1] = new TFile("out_ttbar_ultimate_option1_20180831_183601/Hist.root"); // All on + file[2] = new TFile("out_ttbar_ultimate_approxB_20180831_183534/Hist.root"); // All on + file[3] = new TFile("out_ttbar_ultimate_dsbydr1_20180901_230848/Hist.root"); // Corr 3 off + // file[4] = new TFile("out_ttbar_ultimate_option1_deltaS0_way1_20180901_231217/Hist.root"); // Corr 4 off - worse resolution near eta = 1.5 + file[4] = new TFile("out_ttbar_ultimate_option1_deltaS0_way2_20180901_231405//Hist.root"); // Corr 4 off - Even worse resolution near eta = 1.5 - 1.7 + file[5] = new TFile("out_ttbar_ultimate_20180831_182908/Hist.root"); // All on + file[6] = new TFile("out_ttbar_ultimate_offall_20180831_183257/Hist.root"); // All off + TLegend leg(0.7,0.15,0.9,0.45); + */ + + /* + // Different particle types + file[1] = new TFile("out_ttbar_ultimate_onlyE_20180903_115714/Hist.root"); + file[2] = new TFile("out_ttbar_ultimate_noE_20180903_115836/Hist.root"); + file[3] = new TFile("out_ttbar_ultimate_20180831_182908/Hist.root"); + d1.SetLogy(1); + TLegend leg(0.7,0.15,0.9,0.45); + */ + + file[1] = new TFile("out_muon_ultimate_off1_20180903_145832/Hist.root"); // Corr 1 off + file[2] = new TFile("out_muon_ultimate_off2_20180903_145511/Hist.root"); // Corr 2 off + file[3] = new TFile("out_muon_ultimate_off3_20180903_145549/Hist.root"); // Corr 3 off + file[4] = new TFile("out_muon_ultimate_off4_20180903_145621/Hist.root"); // Corr 4 off + file[5] = new TFile("out_muon_ultimate_20180903_145432/Hist.root"); // All on + file[6] = new TFile("out_muon_ultimate_offall_20180903_145655/Hist.root"); // All off + TLegend leg(0.2, 0.6, 0.4, 0.9); + + TString name[7] = {"", "Corr. 1 off", "Corr. 2 off", "Corr. 3 off", "Corr. 4 off", "All on", "All off"}; + //TString name[7] = {"", "e", "#mu, #pi, K, p", "e, #mu, #pi, K, p", "Corr. 4 off", "All on", "All off"}; + unsigned int icol[7] = {0, 1, 2, 3, 6, 8, 9}; + + TH1F *his; + TH2F *his2D; + TProfile *prof[7]; + TEfficiency *teffi1, *teffi2, *teffi3; + + float ymax = 0.; + + bool first = true; + for (unsigned int i = 1; i <= 6; i++) { + //if (i > 3) continue; + + file[i]->GetObject("TMTrackProducer/KF4ParamsComb/QoverPtResVsTrueEta_KF4ParamsComb", prof[i]); + //file[i]->GetObject("TMTrackProducer/KF4ParamsComb/QoverPtResVsTrueInvPt_KF4ParamsComb", prof[i]); + //file[i]->GetObject("TMTrackProducer/KF4ParamsComb/Z0ResVsTrueEta_KF4ParamsComb", prof[i]); + //file[i]->GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofVsInvPtMatched_KF4ParamsComb", prof[i]); + //file[i]->GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofVsInvPtUnmatched_KF4ParamsComb", prof[i]); + //file[i]->GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofVsEtaMatched_KF4ParamsComb", prof[i]); + //file[i]->GetObject("TMTrackProducer/KF4ParamsComb/FitChi2DofVsEtaUnmatched_KF4ParamsComb", prof[i]); + + float ym = prof[i]->GetMaximum(); + if (ymax < ym) + ymax = ym; + prof[i]->SetMaximum(1.8 * ymax); + //prof[i]->SetMaximum(0.02); + prof[i]->SetMinimum(0.0); + + if (prof[i] == nullptr) { + cout << "ERROR: Input histogram missing " << i << endl; + cin.get(); + continue; + } + + prof[i]->SetMarkerStyle(20 + i); + prof[i]->SetMarkerColor(icol[i]); + if (first) { + first = false; + prof[i]->Draw("P "); + } else { + prof[i]->Draw("P SAME"); + } + leg.AddEntry(prof[i], name[i], "P"); + leg.Draw(); + d1.Draw(); + d1.Update(); + } + + //prof1->SetTitle(";1/Pt (1/GeV); #phi_{0} resolution"); + + d1.Print("plot.pdf"); + cin.get(); + + for (unsigned int i = 1; i <= 6; i++) { + file[i]->Close(); + delete file[i]; + } +} diff --git a/L1Trigger/TrackFindingTMTT/test/tmtt_tf_analysis_cfg.py b/L1Trigger/TrackFindingTMTT/test/tmtt_tf_analysis_cfg.py new file mode 100644 index 0000000000000..60807b026a2f4 --- /dev/null +++ b/L1Trigger/TrackFindingTMTT/test/tmtt_tf_analysis_cfg.py @@ -0,0 +1,211 @@ +################################################################################################ +# To run execute do +# cmsRun tmtt_tf_analysis_cfg.py Events=50 inputMC=Samples/Muons/PU0.txt histFile=outputHistFile.root +# where the arguments take default values if you don't specify them. You can change defaults below. +################################################################################################# + +import FWCore.ParameterSet.Config as cms +import FWCore.Utilities.FileUtils as FileUtils +import FWCore.ParameterSet.VarParsing as VarParsing +import os + +process = cms.Process("Demo") + +GEOMETRY = "D49" + +if GEOMETRY == "D49": + process.load('Configuration.Geometry.GeometryExtended2026D49Reco_cff') + process.load('Configuration.Geometry.GeometryExtended2026D49_cff') +else: + print "this is not a valid geometry!!!" + exit + +process.load('Configuration.StandardSequences.MagneticField_cff') +process.load('Configuration.StandardSequences.FrontierConditions_GlobalTag_cff') +from Configuration.AlCa.GlobalTag import GlobalTag +process.GlobalTag = GlobalTag(process.GlobalTag, 'auto:phase2_realistic', '') + +process.load("FWCore.MessageLogger.MessageLogger_cfi") +process.MessageLogger.categories.append('L1track') +process.MessageLogger.L1track = cms.untracked.PSet(limit = cms.untracked.int32(-1)) + +options = VarParsing.VarParsing ('analysis') + +def getTxtFile(txtFileName): + return os.environ['CMSSW_BASE']+'/src/L1Trigger/TrackFindingTMTT/data/'+txtFileName + +#--- Specify input MC +# MC txt files are in https://github.com/cms-data/L1Trigger-TrackFindingTMTT. +if GEOMETRY == "D49": + inputMCtxt = getTxtFile('MCsamples/1110/RelVal/TTbar/PU200.txt') + +# Fastest to use a local copy ... +#inputMCtxt = getTxtFile('MCsamples/1110/RelVal/TTbar/localRAL/PU200.txt') + +options.register('inputMC', inputMCtxt, VarParsing.VarParsing.multiplicity.singleton, VarParsing.VarParsing.varType.string, "Files to be processed") + +#--- Specify number of events to process. +options.register('Events',100,VarParsing.VarParsing.multiplicity.singleton, VarParsing.VarParsing.varType.int,"Number of Events to analyze") + +#--- Specify name of output histogram file. (If name = '', then no histogram file will be produced). +options.register('histFile','Hist.root',VarParsing.VarParsing.multiplicity.singleton, VarParsing.VarParsing.varType.string,"Name of output histogram file") + +#--- Specify if stubs need to be (re)made, e.g. because they are not available in the input file +options.register('makeStubs',0,VarParsing.VarParsing.multiplicity.singleton,VarParsing.VarParsing.varType.int,"Make stubs, and truth association, on the fly") + +#--- Specify whether to output a GEN-SIM-DIGI-RAW dataset containing the TMTT L1 tracks & associators. +# (Warning: you may need to edit the associator python below to specify which track fitter you are using). +options.register('outputDataset',0,VarParsing.VarParsing.multiplicity.singleton,VarParsing.VarParsing.varType.int,"Create GEN-SIM-DIGI-RAW dataset containing TMTT L1 tracks") + +options.parseArguments() + +#--- input and output + +list = FileUtils.loadListFromFile(options.inputMC) +readFiles = cms.untracked.vstring(*list) +secFiles = cms.untracked.vstring() + +# Override input dataset. +#readFiles = cms.untracked.vstring('/store/user/abhijith/DisplacedMuPlus.root') + +outputHistFile = options.histFile + +process.options = cms.untracked.PSet( wantSummary = cms.untracked.bool(False) ) + +process.maxEvents = cms.untracked.PSet( input = cms.untracked.int32(options.Events) ) + +if outputHistFile != "": + process.TFileService = cms.Service("TFileService", fileName = cms.string(outputHistFile)) + +process.source = cms.Source ("PoolSource", + fileNames = readFiles, + secondaryFileNames = secFiles, + duplicateCheckMode = cms.untracked.string('noDuplicateCheck'), + # Following needed to read CMSSW 9 datasets with CMSSW 10 + inputCommands = cms.untracked.vstring( + 'keep *_*_*_*', + 'drop l1tEMTFHit2016*_*_*_*', + 'drop l1tEMTFTrack2016*_*_*_*' + ) + ) + +process.Timing = cms.Service("Timing", summaryOnly = cms.untracked.bool(True)) + +#--- Load code that produces our L1 tracks and makes corresponding histograms. +#process.load('L1Trigger.TrackFindingTMTT.TMTrackProducer_cff') + +#--- Alternative cfg including improvements not yet in the firmware. Aimed at L1 trigger studies. +process.load('L1Trigger.TrackFindingTMTT.TMTrackProducer_Ultimate_cff') +# +# Enable histogramming & use of MC truth (considerably increases CPU) +process.TMTrackProducer.EnableMCtruth = True +process.TMTrackProducer.EnableHistos = True +# +#--- Optionally override default configuration parameters here (example given of how). + +#process.TMTrackProducer.TrackFitSettings.TrackFitters = [ +# "KF5ParamsComb", +# "KF4ParamsComb" +# "KF4ParamsCombHLS", +# "ChiSquaredFit4", +# "SimpleLR4" +# ] + +# If the input samples contain stubs and the truth association, then you can just use the following path +process.p = cms.Path(process.TMTrackProducer) + +# Optionally (re)make the stubs on the fly +if options.makeStubs == 1: + process.load('L1Trigger.TrackTrigger.TrackTrigger_cff') + process.load('SimTracker.TrackTriggerAssociation.TrackTriggerAssociator_cff') + process.TTClusterAssociatorFromPixelDigis.digiSimLinks = cms.InputTag("simSiPixelDigis","Tracker") + + LOOSE_STUBS = False + + if (LOOSE_STUBS): + # S.Viret's loose Dec. 2017 stub windows from commented out part of + # L1Trigger/TrackTrigger/python/TTStubAlgorithmRegister_cfi.py in CMSSW 9.3.7 + # Optimised for electrons. + + process.TTStubAlgorithm_official_Phase2TrackerDigi_ = cms.ESProducer("TTStubAlgorithm_official_Phase2TrackerDigi_", + zMatchingPS = cms.bool(True), + zMatching2S = cms.bool(True), + #Number of tilted rings per side in barrel layers (for tilted geom only) + NTiltedRings = cms.vdouble( 0., 12., 12., 12., 0., 0., 0.), + BarrelCut = cms.vdouble( 0, 2.0, 3, 4.5, 6, 6.5, 7.0), + TiltedBarrelCutSet = cms.VPSet( + cms.PSet( TiltedCut = cms.vdouble( 0 ) ), + cms.PSet( TiltedCut = cms.vdouble( 0, 3, 3., 2.5, 3., 3., 2.5, 2.5, 2., 1.5, 1.5, 1, 1) ), + cms.PSet( TiltedCut = cms.vdouble( 0, 4., 4, 4, 4, 4., 4., 4.5, 5, 4., 3.5, 3.5, 3) ), + cms.PSet( TiltedCut = cms.vdouble( 0, 5, 5, 5, 5, 5, 5, 5.5, 5, 5, 5.5, 5.5, 5.5) ), + ), + EndcapCutSet = cms.VPSet( + cms.PSet( EndcapCut = cms.vdouble( 0 ) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1., 2.5, 2.5, 3.5, 5.5, 5.5, 6, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 7, 7) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 0.5, 2.5, 2.5, 3, 5, 6, 6, 6.5, 6.5, 6.5, 6.5, 6.5, 6.5, 7, 7) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1, 3., 4.5, 6., 6.5, 6.5, 6.5, 7, 7, 7, 7, 7) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1., 2.5, 3.5, 6., 6.5, 6.5, 6.5, 6.5, 7, 7, 7, 7) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 0.5, 1.5, 3., 4.5, 6.5, 6.5, 7, 7, 7, 7, 7, 7) ), + ) + ) + + else: + # S.Viret's July 2017 stub windows (tight) from commented out part of + # L1Trigger/TrackTrigger/python/TTStubAlgorithmRegister_cfi.py in CMSSW 9.3.2 + + process.TTStubAlgorithm_official_Phase2TrackerDigi_ = cms.ESProducer("TTStubAlgorithm_official_Phase2TrackerDigi_", + zMatchingPS = cms.bool(True), + zMatching2S = cms.bool(True), + #Number of tilted rings per side in barrel layers (for tilted geom only) + NTiltedRings = cms.vdouble( 0., 12., 12., 12., 0., 0., 0.), + BarrelCut = cms.vdouble( 0, 2.0, 2.0, 3.5, 4.5, 5.5, 6.5), #Use 0 as dummy to have direct access using DetId to the correct element + + TiltedBarrelCutSet = cms.VPSet( + cms.PSet( TiltedCut = cms.vdouble( 0 ) ), + cms.PSet( TiltedCut = cms.vdouble( 0, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2., 2., 1.5, 1.5, 1., 1.) ), + cms.PSet( TiltedCut = cms.vdouble( 0, 3., 3., 3., 3., 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2, 2) ), + cms.PSet( TiltedCut = cms.vdouble( 0, 4.5, 4.5, 4, 4, 4, 4, 3.5, 3.5, 3.5, 3, 3, 3) ), + ), + EndcapCutSet = cms.VPSet( + cms.PSet( EndcapCut = cms.vdouble( 0 ) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1, 1.5, 1.5, 2, 2, 2.5, 3, 3, 3.5, 4, 2.5, 3, 3.5, 4.5, 5.5) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1, 1.5, 1.5, 2, 2, 2, 2.5, 3, 3, 3, 2, 3, 4, 5, 5.5) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1.5, 1.5, 2, 2, 2.5, 2.5, 2.5, 3.5, 2.5, 5, 5.5, 6) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1.0, 1.5, 1.5, 2, 2, 2, 2, 3, 3, 6, 6, 6.5) ), + cms.PSet( EndcapCut = cms.vdouble( 0, 1.0, 1.5, 1.5, 1.5, 2, 2, 2, 3, 3, 6, 6, 6.5) ), + ) + ) + + process.p = cms.Path(process.TrackTriggerClustersStubs * process.TrackTriggerAssociatorClustersStubs * process.TMTrackProducer) + +# Optionally create output GEN-SIM-DIGI-RAW dataset containing TMTT L1 tracks & associators. +if options.outputDataset == 1: + + # Associate TMTT L1 tracks to truth particles + process.load('SimTracker.TrackTriggerAssociation.TrackTriggerAssociator_cff') + process.TTAssociatorTMTT = process.TTTrackAssociatorFromPixelDigis.clone( +# Edit to specify which input L1 track collection to run associator on. + TTTracks = cms.VInputTag(cms.InputTag("TMTrackProducer", 'TML1TracksKF4ParamsComb')) +# TTTracks = cms.VInputTag(cms.InputTag("TMTrackProducer", 'TML1TracksglobalLinearRegression')) + ) + process.pa = cms.Path(process.TTAssociatorTMTT) + + # Write output dataset + process.load('Configuration.EventContent.EventContent_cff') + + process.writeDataset = cms.OutputModule("PoolOutputModule", + splitLevel = cms.untracked.int32(0), + eventAutoFlushCompressedSize = cms.untracked.int32(5242880), + outputCommands = process.RAWSIMEventContent.outputCommands, + fileName = cms.untracked.string('output_dataset.root'), ## ADAPT IT ## + dataset = cms.untracked.PSet( + filterName = cms.untracked.string(''), + dataTier = cms.untracked.string('GEN-SIM') + ) + ) + # Include TMTT L1 tracks & associators + stubs. + process.writeDataset.outputCommands.append('keep *TTTrack*_*_*_*') + process.writeDataset.outputCommands.append('keep *TTStub*_*_*_*') + + process.pd = cms.EndPath(process.writeDataset) + process.schedule = cms.Schedule(process.p, process.pa, process.pd)