ATLAS Offline Software
Loading...
Searching...
No Matches
GbtsLayerTool.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 "src/GbtsLayerTool.h"
6
12
13#include <cmath>
14#include <fstream>
15#include <set>
16#include <stdexcept>
17
18namespace ActsTrk {
19
20namespace {
23constexpr int kPixel = 1;
24constexpr int kStrip = 2;
25
28constexpr int kBarrelSideKey = -100;
29} // namespace
30
31GbtsLayerTool::GbtsLayerTool(const std::string& type, const std::string& name,
32 const IInterface* parent)
33 : base_class(type, name, parent) {}
34
36 ATH_MSG_DEBUG("Initializing " << name() << "...");
37
38 ATH_CHECK(detStore()->retrieve(m_pixelId, "PixelID"));
39 ATH_CHECK(detStore()->retrieve(m_stripId, "SCT_ID"));
40 ATH_CHECK(detStore()->retrieve(m_pixelManager, "ITkPixel"));
41 ATH_CHECK(detStore()->retrieve(m_stripManager, "ITkStrip"));
42
44
45 ATH_MSG_INFO("Built " << m_layerDescriptions.size() << " GBTS layers");
46
47 return StatusCode::SUCCESS;
48}
49
51 std::map<LayerKey, std::vector<ModuleEntry>> hashMap;
52
53 // Pixel modules. The GBTS volume id follows the trigger convention:
54 // 7 / 8 / 9 for the negative endcap, the barrel and the positive endcap,
55 // refined by the layer or disk number.
56 for (int hash = 0; hash < static_cast<int>(m_pixelId->wafer_hash_max());
57 ++hash) {
58 const Identifier offlineId = m_pixelId->wafer_id(hash);
59 if (offlineId == 0) {
60 continue;
61 }
62
63 const short barrelEc = m_pixelId->barrel_ec(offlineId);
64 if (std::abs(barrelEc) > 2) {
65 continue; // no DBM needed
66 }
67
68 const short phiIndex = m_pixelId->phi_module(offlineId);
69 const short etaIndex = m_pixelId->eta_module(offlineId);
70 const int layerDisk = m_pixelId->layer_disk(offlineId);
71 const int etaModule = m_pixelId->eta_module(offlineId);
72
73 int volId = -1;
74 if (barrelEc == 0) {
75 volId = 8;
76 } else if (barrelEc == -2) {
77 volId = 7;
78 } else if (barrelEc == 2) {
79 volId = 9;
80 }
81
82 int newVol = 0;
83 int newLay = 0;
84 if (volId == 7 || volId == 9) {
85 newVol = 10 * volId + layerDisk;
86 newLay = etaModule;
87 } else if (volId == 8) {
88 newVol = 10 * volId + layerDisk;
89 newLay = 0;
90 }
91
92 const LayerKey key{barrelEc == 0 ? kBarrelSideKey : barrelEc, kPixel,
93 static_cast<short>(newVol), static_cast<short>(newLay)};
94 hashMap[key].push_back(ModuleEntry{phiIndex, etaIndex, hash});
95 }
96
97 // Strip modules. Volume 13 is the barrel, 12 and 14 the two endcaps.
98 for (int hash = 0; hash < static_cast<int>(m_stripId->wafer_hash_max());
99 ++hash) {
100 const Identifier offlineId = m_stripId->wafer_id(hash);
101 if (offlineId == 0) {
102 continue;
103 }
104
105 const short barrelEc = m_stripId->barrel_ec(offlineId);
106 const short phiIndex = m_stripId->phi_module(offlineId);
107 const short etaIndex = m_stripId->eta_module(offlineId);
108
109 int volId = 13;
110 if (barrelEc != 0) {
111 volId = 12;
112 }
113 if (barrelEc > 0) {
114 volId = 14;
115 }
116
117 const int layerDisk = m_stripId->layer_disk(offlineId);
118
119 const LayerKey key{barrelEc == 0 ? kBarrelSideKey : barrelEc, kStrip,
120 static_cast<short>(volId),
121 static_cast<short>(layerDisk)};
122 hashMap[key].push_back(ModuleEntry{phiIndex, etaIndex, hash});
123 }
124
125 m_pixelLayers.assign(m_pixelId->wafer_hash_max(), kNoLayer);
126 m_stripLayers.assign(m_stripId->wafer_hash_max(), kNoLayer);
127 m_layerDescriptions.clear();
128 m_layerDescriptions.reserve(hashMap.size());
129
130 std::ofstream geometryStream;
131 if (m_dumpGeometry) {
132 geometryStream.open(m_geometryDumpDir);
133 }
134
135 // The layer id packs the volume and the layer into one integer, and the
136 // connection table addresses a layer by it. It has room for the ITk
137 // layouts in the release: the pixel endcaps reach disk 8, and their
138 // volumes, 10 * 7 + disk, only run into the barrel's 80 at disk 10.
139 // Nothing enforces that though, and two layers carrying the same id would
140 // silently become one layer in GbtsGeometry.
141 std::set<Acts::Experimental::GbtsExperimentLayerId> layerIds;
142
143 short layerIndex = 0;
144 for (const auto& [key, modules] : hashMap) {
145 const auto& [sideKey, technology, volId, layId] = key;
146
147 const short barrelEc = sideKey == kBarrelSideKey ? 0 : sideKey;
148 const auto combinedId =
149 static_cast<Acts::Experimental::GbtsExperimentLayerId>(volId) * 1000 +
150 static_cast<Acts::Experimental::GbtsExperimentLayerId>(layId);
151
152 if (!layerIds.insert(combinedId).second) {
153 ATH_MSG_ERROR("Two GBTS layers carry the id "
154 << combinedId << ": this detector does not fit the layer "
155 "id encoding");
156 return StatusCode::FAILURE;
157 }
158
159 float refCoordSum = 0.f;
160 int nModules = 0;
161 double minZ = 100000.0;
162 double maxZ = -100000.0;
163 double minR = 100000.0;
164 double maxR = -100000.0;
165
166 for (const ModuleEntry& module : modules) {
167 const InDetDD::SiDetectorElement* element = nullptr;
168 if (technology == kPixel) {
169 m_pixelLayers[module.hash] = layerIndex;
170 element = m_pixelManager->getDetectorElement(module.hash);
171 } else {
172 m_stripLayers[module.hash] = layerIndex;
173 element = m_stripManager->getDetectorElement(module.hash);
174 }
175 if (element == nullptr) [[unlikely]] {
176 ATH_MSG_WARNING("SiDetectorElement pointer is null.");
177 continue;
178 }
179
180 minZ = std::min(minZ, element->zMin());
181 maxZ = std::max(maxZ, element->zMax());
182 minR = std::min(minR, element->rMin());
183 maxR = std::max(maxR, element->rMax());
184
185 // The reference coordinate is the one that is constant across the
186 // layer: the radius for a barrel layer, z for an endcap one.
187 const Amg::Vector3D& centre = element->center();
188 refCoordSum += barrelEc == 0
189 ? std::hypot(centre(0), centre(1))
190 : centre(2);
191 ++nModules;
192 }
193
194 if (nModules == 0) [[unlikely]] {
195 ATH_MSG_ERROR("GBTS layer " << combinedId << " has no modules.");
196 return StatusCode::FAILURE;
197 }
198
199 Acts::Experimental::GbtsLayerDescription& layer =
200 m_layerDescriptions.emplace_back();
201 layer.id = combinedId;
202 layer.type = barrelEc == 0 ? Acts::Experimental::GbtsLayerType::Barrel
203 : Acts::Experimental::GbtsLayerType::Endcap;
204 layer.technology = technology == kPixel
205 ? Acts::Experimental::GbtsLayerTechnology::Pixel
206 : Acts::Experimental::GbtsLayerTechnology::Strip;
207 layer.refCoord = refCoordSum / nModules;
208 layer.layerThickness = barrelEc == 0 ? maxR-minR : maxZ-minZ;
209 // The bounds span the coordinate the layer extends along.
210 layer.minBound = barrelEc == 0 ? minZ : minR;
211 layer.maxBound = barrelEc == 0 ? maxZ : maxR;
212
213 if (m_dumpGeometry) {
214 geometryStream << minR << " " << maxR << " " << minZ << " " << maxZ << " "
215 << combinedId << "\n";
216 }
217
218 ATH_MSG_DEBUG("Layer " << layerIndex << " (" << combinedId
219 << ") : reference coordinate = " << layer.refCoord
220 << " boundaries: " << layer.minBound << " "
221 << layer.maxBound << " type=" << barrelEc
222 << " technology="
223 << (technology == kPixel ? "pixel" : "strip"));
224
225 ++layerIndex;
226 }
227
228 return StatusCode::SUCCESS;
229}
230
232 const std::vector<Acts::Experimental::GbtsLayerDescription>& layers,
233 std::vector<Acts::Experimental::GbtsLayerConnection>& connections,
234 float& etaBinWidth, std::string connectorInputFile, bool usePixel, bool useStrip) const
235{
236 std::ifstream connectionStream(connectorInputFile);
237 if (!connectionStream.is_open()) {
238 ATH_MSG_ERROR("Cannot open the GBTS connection table "
239 << connectorInputFile);
240 return StatusCode::FAILURE;
241 }
242
243 ReadResult table;
244 try {
245 table = read(connectionStream);
246 } catch (const std::exception& e) {
247 ATH_MSG_ERROR("Cannot read " << connectorInputFile << ": "
248 << e.what());
249 return StatusCode::FAILURE;
250 }
251
252 // the table names a layer by its id, the layer tool by its dense index
253 std::unordered_map<Acts::Experimental::GbtsExperimentLayerId,
254 Acts::Experimental::GbtsLayerTechnology>
255 layerTechnologies;
256 layerTechnologies.reserve(layers.size());
257 for (const Acts::Experimental::GbtsLayerDescription& layer : layers) {
258 layerTechnologies.emplace(layer.id, layer.technology);
259 }
260
261 etaBinWidth = table.etaBinWidth;
262
263 // the stage column only fixes the order the connections are handed over in
264 std::vector<std::pair<std::uint32_t, Acts::Experimental::GbtsLayerConnection>> staged;
265 staged.reserve(table.connections.size());
266
267 std::size_t nOtherTechnology = 0;
268 std::size_t nUnknownLayer = 0;
269
270 for (const Connection& connection :
271 table.connections) {
272 const auto src = layerTechnologies.find(connection.src);
273 const auto dst = layerTechnologies.find(connection.dst);
274 if (src == layerTechnologies.end() || dst == layerTechnologies.end()) {
275 ++nUnknownLayer;
276 continue;
277 }
278
279 const bool wanted = src->second == dst->second &&
280 (src->second == Acts::Experimental::GbtsLayerTechnology::Pixel
281 ? usePixel : useStrip);
282
283 if (!wanted) {
284 ++nOtherTechnology;
285 continue;
286 }
287
288 staged.emplace_back(connection.stage,
289 Acts::Experimental::GbtsLayerConnection{connection.src,
290 connection.dst});
291 }
292
293 std::ranges::stable_sort(staged, {}, [](const auto& entry) { return entry.first; });
294
295 connections.clear();
296 connections.reserve(staged.size());
297 for (const auto& entry : staged) {
298 connections.push_back(entry.second);
299 }
300 const std::size_t nKept = connections.size();
301
302 if (nUnknownLayer != 0) {
303 ATH_MSG_WARNING(nUnknownLayer << " connections of "
304 << connectorInputFile << " name no GBTS layer "
305 "and were dropped");
306 }
307 if (nKept == 0) {
308 ATH_MSG_ERROR("None of the connections of "
309 << connectorInputFile << " are usable: the table "
310 "does not match the detector this job is reconstructing");
311 return StatusCode::FAILURE;
312 }
313 ATH_MSG_DEBUG("Kept " << nKept << " GBTS layer connections, dropping "
314 << nOtherTechnology << " of a technology not asked for, eta bin width "
315 << etaBinWidth);
316
317 return StatusCode::SUCCESS;
318}
319
321 std::istream& inputStream) const {
322 ReadResult result;
323
324 std::uint32_t nConnections{};
325 std::uint32_t iConnection{};
326
327 const auto fail = [&nConnections, &iConnection](const std::string& what) {
328 throw std::runtime_error("GBTS connection table: cannot read " + what +
329 " at connection " + std::to_string(iConnection) +
330 " of " + std::to_string(nConnections));
331 };
332
333 if (!(inputStream >> nConnections >> result.etaBinWidth)) {
334 throw std::runtime_error(
335 "GBTS connection table: cannot read the connection count and the eta "
336 "bin width");
337 }
338
339 for (; iConnection < nConnections; ++iConnection) {
340 std::uint32_t index{};
341 std::uint32_t height{};
342 std::uint32_t width{};
343 std::uint32_t nEntries{};
344 Connection connection;
345
346 if (!(inputStream >> index >> connection.stage >> connection.src >>
347 connection.dst >> height >> width >> nEntries)) {
348 fail("the header");
349 }
350
351 // GBTS recomputes the bin table from the layer geometry
352 const std::uint64_t nBins = std::uint64_t{height} * width;
353 for (std::uint64_t bin = 0; bin < nBins; ++bin) {
354 std::uint32_t unused{};
355 if (!(inputStream >> unused)) {
356 fail("the bin table");
357 }
358 }
359
360 result.connections.push_back(connection);
361 }
362
363 return result;
364}
365
366} // namespace ActsTrk
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_INFO(x,...)
This is an Identifier helper class for the Pixel subdetector.
This is an Identifier helper class for the SCT subdetector.
const double width
void unused(Args &&...)
virtual StatusCode initialize() override
const SCT_ID * m_stripId
std::vector< Acts::Experimental::GbtsLayerDescription > m_layerDescriptions
StatusCode buildLayers()
Group the modules into layers and fill the descriptions and hash maps.
const PixelID * m_pixelId
const InDetDD::PixelDetectorManager * m_pixelManager
Gaudi::Property< std::string > m_geometryDumpDir
Gaudi::Property< bool > m_dumpGeometry
std::tuple< int, int, short, short > LayerKey
Layer key: (side, technology, volume id, layer id).
GbtsLayerTool(const std::string &type, const std::string &name, const IInterface *parent)
StatusCode readConnections(const std::vector< Acts::Experimental::GbtsLayerDescription > &layers, std::vector< Acts::Experimental::GbtsLayerConnection > &connections, float &etaBinWidth, std::string connectorInputFile, bool usePixel, bool useStrips) const override
Reads the connection table and keeps the connections this pass is for: both layers have to be layers ...
const InDetDD::SCT_DetectorManager * m_stripManager
ReadResult read(std::istream &inputStream) const
Read the GBTS layer connection table.
std::vector< short > m_stripLayers
std::vector< short > m_pixelLayers
Wafer hash addressable dense GBTS layer indices.
Class to hold geometrical description of a silicon detector element.
virtual const Amg::Vector3D & center() const override final
Center in global coordinates.
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
Eigen::Matrix< double, 3, 1 > Vector3D
Definition index.py:1
#define unlikely(x)
One row of the table: the two layers it connects, and the stage it is in.
One module, as grouped into a layer.
The table as it is on file.