ATLAS Offline Software
Loading...
Searching...
No Matches
Muon/L1MuonNSW/src/MMT_Diamond.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "MMT_Diamond.h"
7#include "Acts/Utilities/AngleHelpers.hpp"
8#include "Acts/Utilities/MathHelpers.hpp"
9#include <algorithm>
10#include <numbers>
11
12namespace {
13 constexpr int bc_wind = 4; // fixed time window (in bunch crossings) during which the algorithm collects ART hits
14}
15
16namespace L1Muon::L1MM {
17
18MMT_Diamond::MMT_Diamond(const int diamXthreshold, const bool uv, const int diamUVthreshold, const int roadSize,
19 const int olapEtaUp, const int olapEtaDown, const int olapStereoUp, const int olapStereoDown):
20 AthMessaging(Athena::getMessageSvc(), "L1MM::MMT_Diamond"),
21 m_uvflag{uv},
22 m_roadSize{roadSize},
23 m_roadSizeUpX{olapEtaUp},
24 m_roadSizeDownX{olapEtaDown},
25 m_roadSizeUpUV{olapStereoUp},
26 m_roadSizeDownUV{olapStereoDown},
27 m_xthr{diamXthreshold},
28 m_uvthr{diamUVthreshold} {
29}
30
31void MMT_Diamond::createRoads(std::vector<MMT_Road>& roads, const bool isLarge, const bool isEta1) const {
32 const char sec = (isLarge) ? 'L' : 'S';
33 /*
34 * This computation is done as follows: 1024 X roads, from 8192 strips in total (5120 for |eta|==1 and 3072 for |eta|==2)
35 * MML: for i in [0,8] -> i*6 UV roads. Then: (1024-9)*6*9 UV roads
36 * MMS: for i in [0,6] -> i*6 UV roads. Then: (1024-7)*6*7 UV roads
37 */
38 const unsigned int vecRoads = 35000;
39 roads.reserve(vecRoads);
40 const int nroad = kStripsPerLayer/m_roadSize;
41 for (int i = 0; i < nroad; ++i) {
42 const int div = kStripsEta1/m_roadSize;
43 if((isEta1 && i>div) || (!isEta1 && i<=div)) continue;
45
46 /*
47 * The computation of "nuv" is:
48 * B = (1./std::tan(1.5/180.*M_PI));
49 * nuv = std::round( par->getlWidth() / (B * 0.4 * 2.)/this->getRoadSize() );
50 * As getlWidth() has to deal with the full wedge, only two (fixed by construction) integer values are allowed, according to small (7) or large (9) wedge
51 */
52 if(m_uvflag) {
53 const int nuv = (isLarge) ? 9 : 7;
54 for (int uv = 1; uv <= nuv; uv++) {
55 if (i-uv < 0) continue;
56 roads.emplace_back(sec, m_roadSize, m_roadSizeUpX, m_roadSizeDownX, m_roadSizeUpUV, m_roadSizeDownUV, m_xthr, m_uvthr, i, i+uv, i-uv);
57 roads.emplace_back(sec, m_roadSize, m_roadSizeUpX, m_roadSizeDownX, m_roadSizeUpUV, m_roadSizeDownUV, m_xthr, m_uvthr, i, i-uv, i+uv);
58 roads.emplace_back(sec, m_roadSize, m_roadSizeUpX, m_roadSizeDownX, m_roadSizeUpUV, m_roadSizeDownUV, m_xthr, m_uvthr, i, i+uv-1, i-uv);
59 roads.emplace_back(sec, m_roadSize, m_roadSizeUpX, m_roadSizeDownX, m_roadSizeUpUV, m_roadSizeDownUV, m_xthr, m_uvthr, i, i-uv, i+uv-1);
60 roads.emplace_back(sec, m_roadSize, m_roadSizeUpX, m_roadSizeDownX, m_roadSizeUpUV, m_roadSizeDownUV, m_xthr, m_uvthr, i, i-uv+1, i+uv);
61 roads.emplace_back(sec, m_roadSize, m_roadSizeUpX, m_roadSizeDownX, m_roadSizeUpUV, m_roadSizeDownUV, m_xthr, m_uvthr, i, i+uv, i-uv+1);
62 }
63 }
64 }
65}
66
67void MMT_Diamond::findDiamonds(std::vector<MMT_Hit>& hits, std::vector<MMT_Road>& roads, std::vector<slope_t>& diamondSlopes,
68 const int sectorPhi, const double tanStereoAngle) const {
69
70 // Comparison with lambda function (easier to implement)
71 std::ranges::sort(hits, [](const MMT_Hit& h1, const MMT_Hit& h2){ return h1.getBC() < h2.getBC(); });
72 const int bc_start = hits.front().getBC();
73 const int bc_end = hits.front().getBC() + 16;
74 ATH_MSG_DEBUG("Window Start: " << bc_start << " - Window End: " << bc_end);
75
76 // Hits of the current bunch crossing (non-owning, they point into hits, which is not modified below)
77 std::vector<const MMT_Hit*> hits_now;
78 std::vector< std::pair<int, float> > vmm_same;
79 std::vector< std::pair<int, int> > addc_same;
80 std::vector<int> to_erase;
81
82 // each road makes independent triggers, evaluated on each BC
83 unsigned int ibc = 0;
84 for (int bc = hits.front().getBC(); bc < bc_end; bc++) {
85 // Cleaning stuff
86 hits_now.clear();
87
88 for (unsigned int j = ibc; j < hits.size(); j++) {
89 if (hits[j].getBC() == bc) hits_now.push_back(&hits[j]);
90 else if (hits[j].getBC() > bc) {
91 ibc = j;
92 break;
93 }
94 }
95
96 // Simulate harware filters: ART hits + ADDC filter
97 for (unsigned int ib = 0; ib < kPlanes; ib++) { //loop on plane from 0 to 7
98 // VMM-ART-hit filter
99 for (int j = 0; j < kVMMsPerLayer; j++) {
100 vmm_same.clear();
101 unsigned int k = 0;
102 for (const auto &hit_pointer: hits_now) {
103 if (static_cast<unsigned int>(hit_pointer->getPlane()) != ib) continue;
104 if (hit_pointer->getVMM() == j){
105 vmm_same.push_back( std::make_pair(k, hit_pointer->getTime()) );
106 }
107 k++;
108 }
109 if (vmm_same.size() > 1) {
110 to_erase.clear();
111 std::ranges::sort(vmm_same, {}, &std::pair<int, float>::second);
112 for (auto pair: vmm_same) to_erase.push_back(pair.first);
113 // reverse and erase
114 std::sort(to_erase.rbegin(), to_erase.rend());
115 to_erase.pop_back(); //removes the hit with the earliest time
116 for (auto l : to_erase) {
117 hits_now.erase(hits_now.begin() + l);
118 }
119 }
120 }
121 // ADDC-like filter
122 for (int ia = 0; ia < kARTsPerLayer; ia++) { // From 0 to 3 (local index of the ART ASIC in the layer)
123 addc_same.clear();
124 for (unsigned int k = 0; k < hits_now.size(); k++) {
125 if (static_cast<unsigned int>(hits_now[k]->getPlane()) != ib) continue;
126 // the global strip index on the sector layer (getChannel returns the module's local strip index)
127 if (hits_now[k]->getART() == ia) addc_same.emplace_back(k, hits_now[k]->getGlobalStrip());
128 }
129
130 if (addc_same.size() > kMaxHitsPerART) {
131 // priority encode the hits by channel number; remember hits 8+
132 to_erase.clear();
133
134 std::ranges::sort(addc_same, {}, &std::pair<int, int>::second);
135 for (unsigned int it = kMaxHitsPerART; it < addc_same.size(); it++) to_erase.push_back(addc_same[it].first);
136
137 // reverse and erase
138 std::sort(to_erase.rbegin(), to_erase.rend());
139 for (auto l : to_erase) {
140 hits_now.erase(hits_now.begin() + l);
141 }
142 }
143 }
144 } // loop on plane for VMM and ART ASIC filter
145
146 for (auto &road : roads) {
147
148 if (!road.getHitVector().empty()) road.incrementAge(bc_wind);
149 if (!hits_now.empty()) road.addHits(hits_now);
150
151 if (road.checkCoincidences(bc_wind) && bc >= (bc_start - 1)) {
152
153 ATH_MSG_DEBUG("------------------------------------------------------------------");
154 ATH_MSG_DEBUG("Coincidence FOUND @BC: " << bc);
155 ATH_MSG_DEBUG("Road (x, u, v, count): (" << road.iRoadx() << ", " << road.iRoadu() << ", " << road.iRoadv() << ", " << road.countHits() << ")");
156 ATH_MSG_DEBUG("------------------------------------------------------------------");
157
158 std::vector<int> bcidVec;
159 for (const auto &hit: road.getHitVector()) {
160 bcidVec.push_back(hit.getBC());
161 }
162 std::ranges::sort(bcidVec);
163
164 // evaluating mode of the BCID of the hits in the diamond
165 // default setting in the firmware is the mode of the hits's bcid in the diamond
166 int bcidVal=bcidVec[0], bcidCount=1, modeCount=1, bcidMode=bcidVec[0];
167 for (unsigned int i=1; i<bcidVec.size(); i++){
168 if (bcidVec[i] == bcidVal){
169 bcidCount++;
170 } else {
171 bcidCount = 1;
172 bcidVal = bcidVec[i];
173 }
174 if (bcidCount > modeCount) {
175 modeCount = bcidCount;
176 bcidMode = bcidVal;
177 }
178 }
179
180 slope_t slope;
181 slope.BC = bcidMode;
182 slope.totalCount = road.countHits();
183 slope.iRoad = road.iRoadx();
184 slope.iRoadu = road.iRoadu();
185 slope.iRoadv = road.iRoadv();
186 slope.xCount = road.countXHits();
187 slope.uCount = road.countUHits();
188 slope.age = slope.BC - bc_start;
189 slope.mxl = road.mxl();
190 slope.my = road.avgSofXUV(LayerType::X); // defined as my in ATL-COM-UPGRADE-2015-033
191 slope.uavg = road.avgSofXUV(LayerType::U);
192 slope.vavg = road.avgSofXUV(LayerType::V);
193 slope.mx = (slope.uavg-slope.vavg)/(2.*tanStereoAngle);
194 const double theta = std::atan(std::hypot(slope.mx, slope.my));
195 slope.theta = (slope.my > 0.) ? theta : std::numbers::pi - theta;
196 slope.eta = Acts::AngleHelpers::etaFromTheta(slope.theta);
197 slope.dtheta = (slope.mxl - slope.my)/(1. + slope.mxl*slope.my);
198 slope.side = (slope.my > 0.) ? SideType::A : SideType::C;
199 // Local phi relative to the sector centre, in (-pi/2, pi/2) (my is never 0)
200 slope.phi = std::atan(slope.mx/slope.my);
201 slope.phiShf = phiShift(sectorPhi, slope.phi, slope.side);
202 slope.lowRes = road.evaluateLowRes();
203
204 diamondSlopes.push_back(slope);
205 }
206 }
207 }
208}
209
210double MMT_Diamond::phiShift(const int n, const double phi, const SideType side) const {
211 const double Phi = -Acts::copySign(phi, side); // local phi flipped on side A
212 double shift = (n > 8) ? (16-n)*std::numbers::pi/8. : n*std::numbers::pi/8.;
213 if (n < 8) return (Phi + shift);
214 else if (n == 8) return (Phi + ((Phi > 0.) ? -1. : 1.)*shift);
215 else return (Phi - shift);
216}
217
218} // namespace L1Muon::L1MM
Scalar phi() const
phi method
Scalar theta() const
theta method
#define ATH_MSG_DEBUG(x,...)
bool hit(const Container &ids, int pdgId)
AthMessaging(IMessageSvc *msgSvc, const std::string &name)
Constructor.
MMT_Diamond(const int diamXthreshold, const bool uv, const int diamUVthreshold, const int roadSize, const int olapEtaUp, const int olapEtaDown, const int olapStereoUp, const int olapStereoDown)
void findDiamonds(std::vector< MMT_Hit > &hits, std::vector< MMT_Road > &roads, std::vector< slope_t > &diamondSlopes, const int sectorPhi, const double tanStereoAngle) const
double phiShift(const int n, const double phi, const SideType side) const
void createRoads(std::vector< MMT_Road > &roads, const bool isLarge, const bool isEta1) const
One MM strip hit as seen by the MM trigger processor.
STL class.
singleton-like access to IMessageSvc via open function and helper
Some weak symbol referencing magic... These are declared in AthenaKernel/getMessageSvc....
Definition AthDsoUtils.h:10
Definitions shared by the classes of the MM trigger processor emulation.
constexpr int kStripsEta1
Nominal strips of one layer of the |stationEta| = 1 module (5120).
SideType
Detector side. The value is the sign of z, so that it can be used as a sign (e.g. Acts::copySign).
constexpr int kStripsPerLayer
Nominal strips of one sector layer, both modules (8192).
constexpr int kMaxHitsPerART
Hits that one ART ASIC can forward per bunch crossing.
constexpr int kARTsPerLayer
ART ASICs of one sector layer (2 ADDC cards x 2 ART ASICs).
constexpr int kVMMsPerLayer
VMMs of one sector layer (128).
constexpr int kPlanes
Planes of the MM octuplet (2 multilayers x 4 gas gaps).
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
One MM trigger candidate as found by MMT_Diamond::findDiamonds: road indices, hit counts,...