ATLAS Offline Software
Loading...
Searching...
No Matches
StripClusteringTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
5
12
13#include <algorithm>
14#include <cmath>
15#include <stdexcept>
16
17
18namespace ActsTrk {
19constexpr double ONE_TWELFTH = 1./12.;
20constexpr float oneStripSF = 1.1025;
21constexpr float twoStripSF = 0.0729;
22
24 const std::string& type, const std::string& name, const IInterface* parent)
25 : base_class(type,name,parent)
26{
27}
28
30{
31 ATH_MSG_DEBUG("Initializing " << name() << "...");
32
39
40 ATH_CHECK(m_lorentzAngleTool.retrieve());
42
43 ATH_CHECK(m_stripDetElStatus.initialize(not m_stripDetElStatus.empty()));
44 ATH_CHECK(m_stripDetEleCollKey.initialize());
45
46 ATH_CHECK( detStore()->retrieve(m_stripID, "SCT_ID") );
47
48 return StatusCode::SUCCESS;
49}
50
51std::pair<unsigned int, unsigned int>
52StripClusteringTool::countCells(const RDOContainer& rdoContainer,
53 const std::vector<IdentifierHash> &listOfIds,
54 const InDetDD::SiDetectorElementCollection &detector_elements) const {
55 auto getNHits =[](const InDetRawDataCollection<SCT_RDORawData> &RDOs,
56 [[maybe_unused]] const InDetDD::SiDetectorElementCollection &elements )
57 -> unsigned int
58 {
59 unsigned int n_hits = 0u;
60 for (const SCT_RDORawData* rdo : RDOs) {
61 n_hits += rdo->getGroupSize();
62 }
63 return n_hits;
64 };
65 unsigned int n_hits=0;
66 if (listOfIds.empty()) {
67 for (const InDetRawDataCollection<SCT_RDORawData> *RDOs : rdoContainer) {
68 assert( RDOs);
69 n_hits += getNHits(*RDOs, detector_elements);
70 }
71 }
72 else {
73 for (const IdentifierHash& id : listOfIds) {
74 if (not id.is_valid()) continue;
75 const InDetRawDataCollection<SCT_RDORawData> *RDOs = rdoContainer.indexFindPtr(id);
76 if (RDOs) { // necessary ?
77 n_hits += getNHits(*RDOs, detector_elements);
78 }
79 }
80 }
81 return {n_hits,n_hits};
82}
83
85 StripAuxDataCache(SG::AuxVectorData& cont, unsigned int n_cluster_rdos)
86 : xAOD::VariableStruct(cont),
87 rdoList(cont, xAOD::StripCluster::rdoListAcc(), n_cluster_rdos)
88 {}
90};
91
93 std::size_t nClusterRDOs) const
94{
95 return std::any (StripAuxDataCache (cont, nClusterRDOs));
96}
97
99{
100 for (size_t i = 0; i < m_timeBinStr.size(); i++) {
101 if (i >= 3) {
102 ATH_MSG_WARNING("Time bin string has excess characters");
103 break;
104 }
105 switch (std::toupper(m_timeBinStr[i])) {
106 case 'X': m_timeBinBits[i] = -1; break;
107 case '0': m_timeBinBits[i] = 0; break;
108 case '1': m_timeBinBits[i] = 1; break;
109 default:
110 ATH_MSG_FATAL("Invalid time bin string: " << m_timeBinStr);
111 return StatusCode::FAILURE;
112 }
113 }
114 return StatusCode::SUCCESS;
115}
116
117StatusCode
118StripClusteringTool::clusterize([[maybe_unused]] const EventContext& ctx,
119 const RawDataCollection& RDOs,
120 const InDet::SiDetectorElementStatus& stripDetElStatus,
121 const InDetDD::SiDetectorElement& element,
122 IStripClusteringTool::CellContainer &cellContainer) const
123{
124 IdentifierHash idHash = RDOs.identifyHash();
126
127 // At least an empty cluster collection needs to be always added because the
128 // assumption is that there is one element per element RawDataCollection.
129 bool goodModule = true;
130 if (m_checkBadModules.value()) {
131 goodModule = stripDetElStatus.isGood(idHash);
132 }
133
134 // If more than a certain number of RDOs set module to bad
135 // in this case we skip clusterization
136 if (!goodModule && m_maxFiredStrips != 0u) {
137 unsigned int nFiredStrips = 0u;
138 for (const SCT_RDORawData* rdo : RDOs) {
139 nFiredStrips += rdo->getGroupSize();
140 }
141 goodModule |= (nFiredStrips <m_maxFiredStrips);
142 }
143
144 if (goodModule) {
145 std::span<IStripClusteringTool::CellContainer::Cell>
146 cellRange = unpackRDOs(RDOs, stripDetElStatus, element, cellContainer);
147
148 static constexpr unsigned int SORT_BY_LOCAL_X=0u;
149 namespace CL=Acts::InPlaceClusterization;
150 CL::clusterize<SORT_BY_LOCAL_X, std::uint16_t>(cellRange,
151 CL::defaultConnectionHelper<CL::EConnectionType::CommonEdgeOrCorner>(cellRange));
152
153 // set the cell range per cluster
155 [&cellContainer](std::span<IStripClusteringTool::CellContainer::Cell> &,
156 unsigned int idx_begin,
157 unsigned int idx_end) {
158 cellContainer.registerNewCluster(idx_begin,idx_end);
159 });
160 }
161 // must add a range for every call otherwise the cell container and
162 // the list of processed modules get out of sync.
163 cellContainer.registerClustersForNewModule(rangeGuard.range());
164 return StatusCode::SUCCESS;
165}
166
167
168StatusCode
169StripClusteringTool::makeClusters(const EventContext& ctx,
170 [[maybe_unused]] const RDOContainer &rdoContainer,
171 const IStripClusteringTool::CellContainer& cellContainer,
172 unsigned int imodule,
173 const InDetDD::SiDetectorElement& element,
174 unsigned int icluster,
176 std::any& cache) const
177{
178 const IdentifierHash idHash = element.identifyHash();
179 double lorentzShift = m_lorentzAngleTool->getLorentzShift(idHash, ctx);
180
181 const InDetDD::SiDetectorDesign& design = element.design();
182 // get the pitch, this will be the local covariance for the cluster
183
184 assert((not m_isITk) || element.isBarrel() || dynamic_cast<const InDetDD::StripStereoAnnulusDesign*>(&element.design()) !=nullptr);
185 float pitch = (element.isBarrel() or (not m_isITk))
186 ? design.phiPitch()
187 : static_cast<const InDetDD::StripStereoAnnulusDesign&>(element.design()).phiPitchPhi();
188 Eigen::Matrix<float,1,1> localCov(pitch * pitch * ONE_TWELFTH);
189
190 StripAuxDataCache* auxDataCache = std::any_cast<StripAuxDataCache> (&cache);
191 if (!auxDataCache) throw std::bad_any_cast();
192
196 CellContainerProxy cellContainerProxy(&cellContainer);
197 ModuleProxy moduleProxy(cellContainerProxy[imodule]);
198
199 for (ClusterProxy clusterProxy: moduleProxy) {
200 assert( icluster< cont.size() && cont[icluster]);
201 xAOD::StripCluster &stripCluster= *cont[icluster];
202 ATH_CHECK(makeCluster(icluster++,
203 stripCluster,
204 clusterProxy,
205 element,
206 design,
207 lorentzShift,
208 localCov,
209 *auxDataCache));
210 }
211 return StatusCode::SUCCESS;
212}
213
214static
215std::pair<
216 Eigen::Matrix<float,1,1>,
217 Eigen::Matrix<float,3,1>>
219 const InDetDD::SiDetectorElement& element,
220 const InDetDD::SiDetectorDesign& design,
221 double lorentzShift,
222 bool isITk )
223{
224
225
226 InDetDD::SiCellId frontId = cluster.front().coordinates()[0];
227 InDetDD::SiLocalPosition pos = design.localPositionOfCell(frontId);
228 if (cluster.size()>1) {
229 InDetDD::SiCellId backId = cluster.back().coordinates()[0];
231 design.localPositionOfCell(backId);
232 pos = 0.5 * (pos + backPos);
233 }
234
235 // update the xPhi position
236 pos.xPhi( pos.xPhi() + lorentzShift );
237 // @TODO use local to global of Acts surface instead
238 Eigen::Matrix<float,3,1> posG(element.surface().localToGlobal(pos).cast<float>());
239
240 if ((not element.isBarrel()) and isITk) {
241 assert(dynamic_cast<const InDetDD::StripStereoAnnulusDesign*>(&design) != nullptr);
242 const InDetDD::StripStereoAnnulusDesign& annulusDesign =
243 static_cast<const InDetDD::StripStereoAnnulusDesign&>
244 (design);
245 // convert the cluster centre directly, rather than going via the cell id,
246 // which would snap the position to the centre of a single strip
247 pos = annulusDesign.localPositionPC(pos);
248 }
249
250 return std::make_pair(Eigen::Matrix<float,1,1>(pos.xPhi()),
251 std::move(posG));
252}
253
254static
257 const InDetDD::SiDetectorDesign& design)
258{ // For Inner Detector SCT, compute the local position of the cluster center using the strip positions.
259 InDetDD::SiCellId frontId = cluster.front().coordinates()[0];
260 InDetDD::SiLocalPosition pos = design.localPositionOfCell(frontId);
261 if (cluster.size() > 1) {
262 InDetDD::SiCellId backId = cluster.back().coordinates()[0];
263 InDetDD::SiLocalPosition backPos = design.localPositionOfCell(backId);
264 pos = 0.5 * (pos + backPos);
265 }
266 return pos;
267}
268
269static
271 float localPos,
272 float localCov,
273 const InDetDD::SiDetectorElement& element,
274 const InDetDD::SiDetectorDesign& design)
275{ // For Inner Detector SCT, in the case of endcap modules, rotate the local covariance to account for the stereo angle.
276 const bool rotate = (design.shape() == InDetDD::Trapezoid || design.shape() == InDetDD::Annulus);
277 if (!rotate) {
278 return localCov;
279 }
280
281 const auto* sctDesign = dynamic_cast<const InDetDD::SCT_ModuleSideDesign*>(&design);
282 if (sctDesign == nullptr) {
283 return localCov;
284 }
285
286 const InDetDD::SiLocalPosition centrePos = computeCentrePosition(cluster, design);
287 const auto ends = sctDesign->endsOfStrip(centrePos);
288 const double stripL = std::abs(ends.first.xEta() - ends.second.xEta());
289 const double iphipitch = 1. / element.phiPitch();
290 const Amg::Vector2D localPos2D{localPos, 0.0};
291 const double w = element.phiPitch(localPos2D) * iphipitch;
292
293 const double sn = element.sinStereoLocal(localPos2D);
294 const double sn2 = sn * sn;
295 const double cs2 = 1. - sn2;
296 const double v0 = localCov * w * w;
297 const double v1 = stripL * stripL * ONE_TWELFTH;
298
299 const float rotatedCov = cs2 * v0 + sn2 * v1;
300 // copied from InDet::SCT_ClusteringTool, but in ACTS-based tracking, SCT cov is 1-dimensional, just keep the rotatedCov(0,0) for the moment
301 // rotatedCov(0,1) = rotatedCov = sn * sqrt(cs2) * (v0 - v1);
302 // rotatedCov(1,1) = sn2 * v0 + cs2 * v1;
303 return rotatedCov;
304}
305
306StatusCode
309 const StripClusteringTool::ClusterProxy &clusterProxy,
310 const InDetDD::SiDetectorElement& element,
311 const InDetDD::SiDetectorDesign& design,
312 const double lorentzShift,
313 Eigen::Matrix<float,1,1>& localCov,
314 StripAuxDataCache &auxDataCache) const
315{
316 auto [localPos, globalPos]
317 = computePosition(clusterProxy, element, design, lorentzShift, m_isITk);
318
319 // For Strip Clusters the identifier is taken from the front rod list object
320 // This is the same strategy used in Athena:
321 // Since clusterId is arbitary (it only needs to be unique) just use ID of first strip
322 // For strip Cluster it has been found that "identifierOfPosition" does not produces unique values
323
324
325 // If requiring broad errors, use the cluster size as error -
326 // TODO use SiWidth to get the right cluster size as I'm assuming equal strip pitch
327 std::size_t size = clusterProxy.size();
328 if (m_errorStrategy == 1) {// use width
329
330 localCov *= size*size;
331
332 } else if (m_errorStrategy == 2) { //use tuned error as function of size
333
334 if (size == 1)
335 localCov *= oneStripSF;
336 else if (size == 2)
337 localCov *= twoStripSF*size*size;
338 else
339 localCov *= size*size;
340
341 }
342
343 if (not m_isITk) {
344 localCov(0,0) = computeRotatedLocalCov(clusterProxy, localPos(0,0), localCov(0,0), element, design);
345 }
346
347 cl.setMeasurement<1>(element.identifyHash(), localPos, localCov);
348 Identifier frontRDOId = m_stripID->strip_id(element.identify(), clusterProxy.front().coordinates()[0]);
349 cl.setIdentifier( frontRDOId.get_compact() );
350
351 // Do I really need the global position in fast tracking?
352 cl.globalPosition() = globalPos;
353
354 cl.setChannelsInPhi(size);
355
356 unsigned int n_rdos = auxDataCache.rdoList.getBeginIndex(icluster);
357 Identifier waferId=element.identify();
359 for (CellProxy cellProxy : clusterProxy) {
360 Identifier rdoId = m_stripID->strip_id(waferId, cellProxy.coordinates()[0]);
361 auxDataCache.rdoList.setValue(n_rdos,rdoId.get_compact());
362 ++n_rdos;
363 }
364 auxDataCache.rdoList.updateEndIndex(icluster,n_rdos);
365
366 return StatusCode::SUCCESS;
367}
368
369
370bool StripClusteringTool::passTiming(const std::bitset<3>& timePattern) const {
371 // Convert the given timebin to a bit set and test each bit
372 // if bit is -1 (i.e. X) it always passes, other wise require exact match of 0/1
373 // N.B bitset has opposite order to the bit pattern we define
374 if (m_timeBinBits[0] != -1 and timePattern.test(2) != static_cast<bool>(m_timeBinBits[0])) return false;
375 if (m_timeBinBits[1] != -1 and timePattern.test(1) != static_cast<bool>(m_timeBinBits[1])) return false;
376 if (m_timeBinBits[2] != -1 and timePattern.test(0) != static_cast<bool>(m_timeBinBits[2])) return false;
377 return true;
378}
379
380
382 IdentifierHash waferHash,
383 std::int16_t strip)
384{
385 if (stripDetElStatus) {
386 return not stripDetElStatus->isCellGood(waferHash.value(), strip) ;
387 }
388 return false;
389}
390
391
392std::span<IStripClusteringTool::CellContainer::Cell>
393StripClusteringTool::unpackRDOs(const RawDataCollection& RDOs,
394 const InDet::SiDetectorElementStatus& stripDetElStatus,
395 const InDetDD::SiDetectorElement& element,
396 IStripClusteringTool::CellContainer &cellContainer) const
397{
398 const InDetDD::SiDetectorDesign& design = element.design();
399
400 //Check type in debug build otherwise assume it is correct
401 assert(dynamic_cast<const InDetDD::SCT_ModuleSideDesign*>(&design)!=nullptr);
402 std::size_t ncells = static_cast<size_t>(static_cast<const InDetDD::SCT_ModuleSideDesign&>(design).cells());
403
404 IStripClusteringTool::CellContainer::ModuleRangeGuard rangeGuard(cellContainer, RDOs.identifyHash() );
405 // Simple single-entry cache
406 IdentifierHash waferHash=RDOs.identifyHash();
407
408 for (unsigned int rdo_i=0; rdo_i<RDOs.size(); ++rdo_i) {
409 const StripRDORawData *raw=RDOs[rdo_i];
410
411 //Check type in debug build otherwise assume it is correct
412 assert(dynamic_cast<const SCT3_RawData*>(raw)!=nullptr);
413 const SCT3_RawData* raw3 = static_cast<const SCT3_RawData*>(raw);
414
415 std::bitset<3> timePattern(raw3->getTimeBin());
416 if (!passTiming(timePattern)) {
417 ATH_MSG_DEBUG("Strip failed timing check");
418 continue;
419 }
420
421 Identifier firstStripId = raw->identify();
422 Identifier waferId = m_stripID->wafer_id(firstStripId);
423
424 assert( waferHash == m_stripID->wafer_hash(waferId) );
425
426 std::int16_t iFirstStrip = static_cast<std::int16_t>(m_stripID->strip(firstStripId));
427 assert(m_stripID->strip(firstStripId)==iFirstStrip);
428
429 assert( ncells == static_cast<std::uint16_t>(ncells) );
430 std::int16_t iMaxStrip = std::min(
431 static_cast<std::int16_t>(iFirstStrip + raw->getGroupSize()),
432 static_cast<std::int16_t>(ncells));
433
434 for (std::int16_t strip_i = iFirstStrip; strip_i < iMaxStrip; strip_i++) {
435 if (isBadStrip(&stripDetElStatus, waferHash, strip_i)) {
436 // Bad strip, throw it out to minimize useless work.
437 ATH_MSG_DEBUG("Bad strip encountered on module " << waferHash << " : "
438 << m_stripID->strip_id(waferId, strip_i)
439 << ", wafer is: " << waferId << " strip is "<< strip_i
440 << " hash " << m_stripID->wafer_hash(waferId));
441 } else {
442 // Good strip!
443
444 std::array<std::int16_t,1> coordinates{strip_i};
445 cellContainer.emplace_back_cell(coordinates, rdo_i);
446 }
447 }
448 }
449
450 return rangeGuard.moduleCellSpan();
451}
452
453} // namespace ActsTrk
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_FATAL(x,...)
size_t size() const
Number of registered mappings.
void rotate(double angler, GeoTrf::Vector2D &vector)
Define structures for fast filling of xAOD objects.
Gaudi::Property< bool > m_isITk
static bool isBadStrip(const InDet::SiDetectorElementStatus *sctDetElStatus, IdentifierHash waferHash, std::int16_t strip)
Gaudi::Property< unsigned int > m_maxFiredStrips
Gaudi::Property< bool > m_checkBadModules
bool passTiming(const std::bitset< 3 > &timePattern) const
virtual std::pair< unsigned int, unsigned int > countCells(const RDOContainer &rdo_collection, const std::vector< IdentifierHash > &listOfIds, const InDetDD::SiDetectorElementCollection &detector_elements) const override
virtual std::any createEventDataCache(xAOD::StripClusterContainer &cont, std::size_t nClusterRDOs) const override
virtual StatusCode initialize() override
virtual StatusCode makeClusters(const EventContext &ctx, const RDOContainer &rdo_container, const IStripClusteringTool::CellContainer &cellContainer, unsigned int module_i, const InDetDD::SiDetectorElement &element, unsigned int icluster, xAOD::StripClusterContainer &cont, std::any &vars) const override
StripClusteringTool(const std::string &type, const std::string &name, const IInterface *parent)
ToolHandle< ISiLorentzAngleTool > m_lorentzAngleTool
virtual StatusCode clusterize(const EventContext &ctx, const RawDataCollection &RDOs, const InDet::SiDetectorElementStatus &stripDetElStatus, const InDetDD::SiDetectorElement &element, IStripClusteringTool::CellContainer &cellContainer) const override
StatusCode makeCluster(size_t icluster, xAOD::StripCluster &cl, const ClusterProxy &cluster_proxy, const InDetDD::SiDetectorElement &element, const InDetDD::SiDetectorDesign &design, const double lorentzShift, Eigen::Matrix< float, 1, 1 > &localCov, StripAuxDataCache &auxDataCache) const
SG::ReadCondHandleKey< InDetDD::SiDetectorElementCollection > m_stripDetEleCollKey
Gaudi::Property< unsigned int > m_errorStrategy
SG::ReadHandleKey< InDet::SiDetectorElementStatus > m_stripDetElStatus
InPlaceClusterization::ClusterProxy< const IStripClusteringTool::CellContainer > ClusterProxy
std::span< IStripClusteringTool::CellContainer::Cell > unpackRDOs(const RawDataCollection &RDOs, const InDet::SiDetectorElementStatus &stripDetElStatus, const InDetDD::SiDetectorElement &element, IStripClusteringTool::CellContainer &cellContainer) const
size_type size() const noexcept
Returns the number of elements in the collection.
This is a "hash" representation of an Identifier.
constexpr value_type value() const
value_type get_compact() const
Get the compact id.
virtual DetectorShape shape() const
Shape of element.
virtual SiLocalPosition localPositionOfCell(const SiCellId &cellId) const =0
readout or diode id -> position.
virtual double phiPitch() const =0
Pitch in phi direction.
Base class for the SCT module side design, extended by the Forward and Barrel module design.
int cells() const
number of readout stips within module side:
Identifier for the strip or pixel cell.
Definition SiCellId.h:29
Base class for the detector design classes for Pixel and SCT.
Class to hold the SiDetectorElement objects to be put in the detector store.
Class to hold geometrical description of a silicon detector element.
virtual const SiDetectorDesign & design() const override final
access to the local description (inline):
double phiPitch() const
Pitch (inline methods).
double sinStereoLocal(const Amg::Vector2D &localPos) const
Angle of strip in local frame with respect to the etaAxis.
Class to represent a position in the natural frame of a silicon sensor, for Pixel and SCT For Pixel: ...
virtual IdentifierHash identifyHash() const override final
identifier hash (inline)
virtual Identifier identify() const override final
identifier of this detector element (inline)
Trk::Surface & surface()
Element Surface.
double phiPitchPhi(const SiLocalPosition &localPosition) const
SiLocalPosition localPositionPC(const SiLocalPosition &pos) const
virtual Identifier identify() const override final
bool isCellGood(IdentifierHash hash, unsigned short cell_i) const
bool isGood(IdentifierHash hash) const
int getTimeBin() const
virtual int getGroupSize() const =0
Manage lookup of vectors of auxiliary data.
virtual void localToGlobal(const Amg::Vector2D &locp, const Amg::Vector3D &mom, Amg::Vector3D &glob) const =0
Specified by each surface type: LocalToGlobal method without dynamic memory allocation.
Define structures for fast filling of xAOD objects.
VariableStruct(SG::AuxVectorData &cont)
void updateEndIndex(unsigned int obj_i, unsigned int elm_i)
unsigned int getBeginIndex(unsigned int obj_i) const
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
constexpr float oneStripSF
static float computeRotatedLocalCov(const InPlaceClusterization::ClusterProxy< const IStripClusteringTool::CellContainer > &cluster, float localPos, float localCov, const InDetDD::SiDetectorElement &element, const InDetDD::SiDetectorDesign &design)
static InDetDD::SiLocalPosition computeCentrePosition(const InPlaceClusterization::ClusterProxy< const IStripClusteringTool::CellContainer > &cluster, const InDetDD::SiDetectorDesign &design)
constexpr float twoStripSF
constexpr double ONE_TWELFTH
static std::pair< Eigen::Matrix< float, 1, 1 >, Eigen::Matrix< float, 3, 1 > > computePosition(const InPlaceClusterization::ClusterProxy< const IStripClusteringTool::CellContainer > &cluster, const InDetDD::SiDetectorElement &element, const InDetDD::SiDetectorDesign &design, double lorentzShift, bool isITk)
void for_each_cluster(cell_collection_t &cells, func_t func)
call the given function for each cluster of a label sorted cell collection.
Eigen::Matrix< double, 2, 1 > Vector2D
ICaloAffectedTool is abstract interface for tools checking if 4 mom is in calo affected region.
StripCluster_v1 StripCluster
Define the version of the strip cluster class.
StripClusterContainer_v1 StripClusterContainer
Define the version of the strip cluster container.
void registerNewCluster(index_t cell_begin_idx, index_t cell_end_idx)
void emplace_back_cell(const std::array< coordinates_t, NDIM > &the_coordinates, index_t src_index)
void registerClustersForNewModule(const ClusterRange &a_range)
ModuleRangeGuard startNewModule(unsigned int id_hash)
StripAuxDataCache(SG::AuxVectorData &cont, unsigned int n_cluster_rdos)
xAOD::xAODInDetMeasurement::Utilities::JaggedVecEltCache< Identifier::value_type > rdoList
std::size_t size() const
Default implementation to compute the number of elements this proxy container contains/refers to The ...
auto front() const
Get a proxy for the first child element (read-only) The operation is undefined if there are no child ...
auto back() const
Get a proxy for the last child element (read-only).