ATLAS Offline Software
Loading...
Searching...
No Matches
SpacePointMakerAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
5
11#include <GaudiKernel/IMessageSvc.h>
12#include <memory>
13#include <type_traits>
20
21
22#include "Acts/Surfaces/detail/LineHelper.hpp"
23namespace {
24 using CovIdx = MuonR4::SpacePoint::CovIdx;
25
26 inline std::vector<std::shared_ptr<unsigned>> matchCountVec(unsigned n) {
27 std::vector<std::shared_ptr<unsigned>> out{};
28 out.reserve(n);
29 for (unsigned p = 0; p < n ;++p) {
30 out.emplace_back(std::make_shared<unsigned>(0));
31 }
32 return out;
33 }
39 template<class MeasType>
40 #if defined(FLATTEN)
41 // We compile this package with optimization, even in debug builds; otherwise,
42 // the heavy use of Eigen makes it too slow. However, from here we may call
43 // to out-of-line Eigen code that is linked from other DSOs; in that case,
44 // it would not be optimized. Avoid this by forcing all Eigen code
45 // to be inlined here if possible.
47 #endif
48 Amg::Transform3D toChamberTransform(const ActsTrk::GeometryContext& gctx,
49 const Amg::Transform3D& sectorTrans,
50 const MeasType& meas) {
51 const MuonGMR4::MuonReadoutElement* reEle{meas.readoutElement()};
52 if constexpr(std::is_same_v<MeasType, xAOD::MdtDriftCircle>) {
53 return sectorTrans * reEle->localToGlobalTransform(gctx, meas.measurementHash());
54 } else {
55 return sectorTrans * reEle->localToGlobalTransform(gctx, meas.layerHash());
56 }
57 }
60 template <typename PrdType>
61 double sensorHalfLength(const PrdType& prd) {
62 const auto* re = prd.readoutElement();
63 if constexpr(std::is_same_v<PrdType, xAOD::MdtDriftCircle>) {
64 return 0.5 * re->activeTubeLength(prd.measurementHash());
65 } else if constexpr(std::is_same_v<PrdType, xAOD::RpcMeasurement>) {
66 return 0.5*(prd.measuresPhi() ? re->stripPhiLength() : re->stripEtaLength());
67 } else if constexpr(std::is_same_v<PrdType, xAOD::TgcStrip>) {
68 return 0.5 * re->sensorLayout(prd.layerHash())->design(prd.measuresPhi()).stripLength(prd.channelNumber());
69 } else if constexpr(std::is_same_v<PrdType, xAOD::MMCluster>) {
70 return 0.5* re->stripLayer(prd.layerHash()).design().stripLength(prd.channelNumber());
71 } else if constexpr(std::is_same_v<PrdType, xAOD::sTgcMeasurement>) {
72 return 0.5* re->stripLayer(prd.layerHash()).design().stripLength(prd.channelNumber());
73 }
74 return 0.;
75 }
80 inline double covElement(const xAOD::sTgcMeasurement& m,
81 const CovIdx covIdx) {
82 if (m.numDimensions() == 2) {
83 const unsigned i = (covIdx != CovIdx::etaCov);
84 return m.localCovariance<2>()(i,i);
85 }
86 return m.localCovariance<1>()[0];
87 }
88}
89
90namespace MuonR4 {
91
96 if (techIdx != other.techIdx) {
97 return static_cast<int>(techIdx) < static_cast<int>(other.techIdx);
98 }
99 if (stIdx != other.stIdx) {
100 return static_cast<int>(stIdx) < static_cast<int>(other.stIdx);
101 }
102 return eta < other.eta;
103}
109
110void SpacePointMakerAlg::SpacePointStatistics::addToStat(const std::vector<SpacePoint>& spacePoints){
111 std::lock_guard guard{m_mutex};
112 for (const SpacePoint& sp : spacePoints){
113 FieldKey key{};
114 key.stIdx = m_idHelperSvc->stationIndex(sp.identify());
115 key.techIdx = m_idHelperSvc->technologyIndex(sp.identify());
116 key.eta = m_idHelperSvc->stationEta(sp.identify());
117 StatField & stats = m_map[key];
118 if (sp.measuresEta() && sp.measuresPhi()) {
119 ++stats.measEtaPhi;
120 } else {
121 stats.measEta += sp.measuresEta();
122 stats.measPhi += sp.measuresPhi();
123 }
124 }
125}
127 using KeyVal = std::pair<FieldKey, StatField>;
128 std::vector<KeyVal> sortedstats{};
129 sortedstats.reserve(m_map.size());
131 for (const auto & [key, stats] : m_map){
132 sortedstats.emplace_back(std::make_pair(key, stats));
133 }
134 std::stable_sort(sortedstats.begin(), sortedstats.end(), [](const KeyVal& a, const KeyVal&b) {
135 return a.second.allHits() > b.second.allHits();
136 });
137 msg<<MSG::ALWAYS<<"###########################################################################"<<endmsg;
138 for (const auto & [key, stats] : sortedstats) {
139 msg<<MSG::ALWAYS<<" "<<Muon::MuonStationIndex::technologyName(key.techIdx)
140 <<" "<<Muon::MuonStationIndex::stName(key.stIdx)
141 <<" "<<std::abs(key.eta)<<(key.eta < 0 ? "A" : "C")
142 <<" "<<std::setw(8)<<stats.measEtaPhi
143 <<" "<<std::setw(8)<<stats.measEta
144 <<" "<<std::setw(8)<<stats.measPhi<<endmsg;
145 }
146 msg<<MSG::ALWAYS<<"###########################################################################"<<endmsg;
147
148}
149
153 if (m_statCounter) {
154 m_statCounter->dumpStatisics(msgStream());
155 }
156 return StatusCode::SUCCESS;
157}
159 ATH_CHECK(m_geoCtxKey.initialize());
160 ATH_CHECK(m_mdtKey.initialize(!m_mdtKey.empty()));
161 ATH_CHECK(m_rpcKey.initialize(!m_rpcKey.empty()));
162 ATH_CHECK(m_tgcKey.initialize(!m_tgcKey.empty()));
163 ATH_CHECK(m_mmKey.initialize(!m_mmKey.empty()));
164 ATH_CHECK(m_stgcKey.initialize(!m_stgcKey.empty()));
165 ATH_CHECK(m_idHelperSvc.retrieve());
166 ATH_CHECK(m_writeKey.initialize());
167 if (m_doStat) {
168 m_statCounter = std::make_unique<SpacePointStatistics>(m_idHelperSvc.get());
169 }
170 return StatusCode::SUCCESS;
171}
172
173template <>
175 const PrdVec_t<const xAOD::TgcStrip*>& phiHits) const {
176 if (etaHits.empty() || phiHits.empty()) {
177 return false;
178 }
179 const MuonGMR4::TgcReadoutElement* re = etaHits[0]->readoutElement();
180 ATH_MSG_VERBOSE("Collected "<<etaHits.size()<<"/"<<phiHits.size()<<" hits in "<<m_idHelperSvc->toStringGasGap(etaHits[0]->identify()));
181 return ((1.*etaHits.size()) / ((1.*re->numChannels(etaHits[0]->measurementHash())))) < m_maxOccTgcEta &&
182 ((1.*phiHits.size()) / ((1.*re->numChannels(phiHits[0]->measurementHash())))) < m_maxOccTgcPhi;
183 }
184template <>
186 const PrdVec_t<const xAOD::RpcMeasurement*>& phiHits) const {
187 if (etaHits.empty() || phiHits.empty()) {
188 return false;
189 }
190 const MuonGMR4::RpcReadoutElement* re = etaHits[0]->readoutElement();
191 ATH_MSG_VERBOSE("Collected "<<etaHits.size()<<"/"<<phiHits.size()<<" hits in "<<m_idHelperSvc->toStringGasGap(etaHits[0]->identify()));
192 return ((1.*etaHits.size()) / (1.*re->nEtaStrips())) < m_maxOccRpcEta &&
193 ((1.*phiHits.size()) / (1.*re->nPhiStrips())) < m_maxOccRpcPhi;
194 }
195
196template <>
198 const PrdVec_t<const xAOD::MMCluster*>& /*phiHits*/) const {
199 return false;
200 }
201template <typename PrdType>
203 const Amg::Transform3D& sectorTrans,
204 const PrdVec_t<PrdType*>& prdsToFill,
205 std::vector<SpacePoint>& outColl) const {
206 if (prdsToFill.empty()) {
207 return;
208 }
209 const PrdType* refMeas = prdsToFill.front();
210 bool allSpArePhi{false};
211
212 const Amg::Transform3D toSectorTrans = toChamberTransform(gctx, sectorTrans, *refMeas);
214 Amg::Vector3D sensorDir{Amg::Vector3D::Zero()}, toNextSen{Amg::Vector3D::Zero()};
216 if constexpr(std::is_same_v<PrdType, xAOD::RpcMeasurement> ||
217 std::is_same_v<PrdType, xAOD::TgcStrip>) {
218 allSpArePhi = refMeas->measuresPhi();
219 const auto& stripLayout = refMeas->readoutElement()->sensorLayout(refMeas->layerHash());
220 const auto& design = stripLayout->design(allSpArePhi);
221 sensorDir = toSectorTrans.rotation() * stripLayout->to3D(design.stripDir(), allSpArePhi);
222 toNextSen = toSectorTrans.rotation() * stripLayout->to3D(design.stripNormal(), allSpArePhi);
223 } else {
224 sensorDir = toSectorTrans.rotation().col(Amg::y);
225 toNextSen = toSectorTrans.rotation().col(Amg::x);
226 }
227 outColl.reserve(outColl.size() + prdsToFill.size());
228 for (const PrdType* prd: prdsToFill) {
229 SpacePoint& newSp = outColl.emplace_back(prd);
230 if constexpr (std::is_same_v<PrdType, xAOD::TgcStrip>) {
231 if (allSpArePhi) {
232 const auto& stripLayout = refMeas->readoutElement()->sensorLayout(refMeas->layerHash());
233 const auto& radialDesign = static_cast<const MuonGMR4::RadialStripDesign&>(stripLayout->design(allSpArePhi));
234 toNextSen = toSectorTrans.rotation() * stripLayout->to3D(radialDesign.stripNormal(prd->channelNumber()), allSpArePhi);
235 sensorDir = toSectorTrans.rotation() * stripLayout->to3D(radialDesign.stripDir(prd->channelNumber()), allSpArePhi);
236 }
237 }
238 newSp.setPosition(toSectorTrans * prd->localMeasurementPos());
239 newSp.setDirection(sensorDir, toNextSen);
240 auto cov = Acts::filledArray<double,3>(0.);
241 if (prd->numDimensions() == 2) {
242 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->template localCovariance<2>()(0,0);
243 cov[Acts::toUnderlying(CovIdx::phiCov)] = prd->template localCovariance<2>()(1,1);
244 } else {
246 auto covIdx{Acts::toUnderlying(CovIdx::etaCov)},
247 lenIdx{Acts::toUnderlying(CovIdx::phiCov)};
248 if (!newSp.measuresEta()) {
249 std::swap(covIdx, lenIdx);
250 }
251 cov[covIdx] = prd->template localCovariance<1>()[0];
252 cov[lenIdx] = Acts::square(sensorHalfLength(*prd));
253 }
254 newSp.setCovariance(std::move(cov));
255 }
256}
257
258
259template <typename ContType>
262 std::vector<EtaPhi2DHits<typename ContType::const_value_type>> hitsPerGasGap{};
263 for (const auto& prd : viewer) {
264 ATH_MSG_VERBOSE("Create space point from "<<m_idHelperSvc->toString(prd->identify())
265 <<", hash: "<<prd->identifierHash());
266
267 unsigned gapIdx = prd->gasGap() -1;
268 if constexpr (std::is_same_v<ContType, xAOD::RpcMeasurementContainer>) {
269 gapIdx = prd->readoutElement()->createHash(0, prd->gasGap(), prd->doubletPhi(), false);
270 }
271 if (hitsPerGasGap.size() <= gapIdx) {
272 hitsPerGasGap.resize(gapIdx + 1);
273 }
274 bool measPhi{false};
275 if constexpr(std::is_same_v<ContType, xAOD::sTgcMeasContainer>) {
277 measPhi = prd->channelType() == sTgcIdHelper::sTgcChannelTypes::Wire;
278 } else if constexpr(!std::is_same_v<ContType, xAOD::MMClusterContainer>) {
280 measPhi = prd->measuresPhi();
281 }
282
283 if (prd->numDimensions() == 2) {
284 hitsPerGasGap[gapIdx][2].push_back(prd);
285 continue;
286 }
288 auto& toPush = hitsPerGasGap[gapIdx][measPhi];
289 if (toPush.capacity() == toPush.size()) {
290 toPush.reserve(toPush.size() + m_capacityBucket);
291 }
292 toPush.push_back(prd);
293 }
294 return hitsPerGasGap;
295}
296
297template <typename ContType>
298 StatusCode SpacePointMakerAlg::loadContainerAndSort(const EventContext& ctx,
300 PreSortedSpacePointMap& fillContainer) const {
301 const ContType* measurementCont{nullptr};
302 ATH_CHECK(SG::get(measurementCont, key, ctx));
303 if (!measurementCont || measurementCont->empty()){
304 ATH_MSG_DEBUG("nothing to do");
305 return StatusCode::SUCCESS;
306 }
307 const ActsTrk::GeometryContext* gctx{nullptr};
308 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
309
310 xAOD::ChamberViewer viewer{*measurementCont};
311
312 do {
313 SpacePointsPerChamber& pointsInChamb = fillContainer[viewer.at(0)->readoutElement()->msSector()];
314 const Amg::Transform3D sectorTrans = viewer.at(0)->readoutElement()->msSector()->globalToLocalTransform(*gctx);
315 ATH_MSG_DEBUG("Fill space points for chamber "<<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
316 if constexpr( std::is_same_v<ContType, xAOD::MdtDriftCircleContainer>) {
317 pointsInChamb.etaHits.reserve(pointsInChamb.etaHits.capacity() + viewer.size());
318 for (const auto& prd : viewer) {
319 Amg::Transform3D toChamberTrans{toChamberTransform(*gctx, sectorTrans, *prd)};
320 SpacePoint& sp{pointsInChamb.etaHits.emplace_back(prd)};
321 sp.setPosition(toChamberTrans*prd->localMeasurementPos());
322 sp.setDirection(toChamberTrans.rotation().col(Amg::z),
323 toChamberTrans.rotation().col(Amg::y));
324 std::array<double, 3> cov{Acts::filledArray<double,3>(0.)};
325 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->driftRadiusCov();
326 cov[Acts::toUnderlying(CovIdx::phiCov)] = Acts::square(sensorHalfLength(*prd));
327 if (ATH_UNLIKELY(prd->numDimensions() == 2)){
328 cov[Acts::toUnderlying(CovIdx::phiCov)] = static_cast<const xAOD::MdtTwinDriftCircle*>(prd)->posAlongWireCov();
329 }
330 sp.setCovariance(std::move(cov));
331 }
332 } else {
334 for (auto& [etaHits, phiHits, two2DHits] : splitHitsPerGasGap(viewer)) {
335 ATH_MSG_DEBUG("Found "<<etaHits.size()<<"/"<<phiHits.size()
336 <<" 1D and "<<two2DHits.size()<<" 2D hits in chamber "
337 <<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
339 fillUncombinedSpacePoints(*gctx, sectorTrans, two2DHits, pointsInChamb.etaHits);
341 // Check if we do not have 2D occupancy (missing phi or eta hits)
342 if (!passOccupancy2D(etaHits, phiHits)) {
343 fillUncombinedSpacePoints(*gctx, sectorTrans, etaHits, pointsInChamb.etaHits);
344 fillUncombinedSpacePoints(*gctx, sectorTrans, phiHits, pointsInChamb.phiHits);
345 continue;
346 }
347
348 std::vector<std::shared_ptr<unsigned>> etaCounts{matchCountVec(etaHits.size())},
349 phiCounts{matchCountVec(phiHits.size())};
350
351 pointsInChamb.etaHits.reserve(pointsInChamb.etaHits.size() + etaHits.size()*phiHits.size());
353 const auto& firstEta{etaHits.front()};
354 const Amg::Transform3D toSectorTrans = toChamberTransform(*gctx, sectorTrans, *firstEta);
355
356 Amg::Vector3D toNextDir{Amg::Vector3D::Zero()}, sensorDir{Amg::Vector3D::Zero()};
357 if constexpr (std::is_same_v<xAOD::RpcMeasurementContainer, ContType> ||
358 std::is_same_v<xAOD::TgcStripContainer, ContType>) {
359 const auto& stripLayout = firstEta->readoutElement()->sensorLayout(firstEta->layerHash());
360 const auto& design = stripLayout->design();
361 sensorDir = toSectorTrans.rotation() * stripLayout->to3D(design.stripDir(), false);
362 toNextDir = toSectorTrans.rotation() * stripLayout->to3D(design.stripNormal(), false);
363 } else {
364 toNextDir = toSectorTrans.rotation().col(Amg::x);
365 sensorDir = toSectorTrans.rotation().col(Amg::y);
366 }
367
368 using namespace Acts::detail::LineHelper;
369 for (unsigned etaP = 0; etaP < etaHits.size(); ++etaP) {
371 for (unsigned phiP = 0; phiP < phiHits.size(); ++ phiP) {
373 if constexpr(std::is_same_v<xAOD::TgcStripContainer, ContType>) {
374 if (!(etaHits[etaP]->bcBitMap() & phiHits[phiP]->bcBitMap())){
375 continue;
376 }
377 const auto& stripLay = phiHits[phiP]->readoutElement()->sensorLayout(phiHits[phiP]->layerHash());
378 const auto& radialDesign = static_cast<const MuonGMR4::RadialStripDesign&>(stripLay->design(true));
379 toNextDir = toSectorTrans.rotation() * stripLay->to3D(radialDesign.stripDir(phiHits[phiP]->channelNumber()), true);
380 }
381
382 SpacePoint& newSp = pointsInChamb.etaHits.emplace_back(etaHits[etaP], phiHits[phiP]);
383 newSp.setInstanceCounts(etaCounts[etaP], phiCounts[phiP]);
384
385 auto spIsect = lineIntersect(toSectorTrans*etaHits[etaP]->localMeasurementPos(), sensorDir,
386 toSectorTrans*phiHits[phiP]->localMeasurementPos(), toNextDir);
387 newSp.setPosition(spIsect.position());
388 newSp.setDirection(sensorDir, toNextDir);
389 auto cov = Acts::filledArray<double, 3>(0.);
390 cov[Acts::toUnderlying(CovIdx::etaCov)] = etaHits[etaP]->template localCovariance<1>()[0];
391 cov[Acts::toUnderlying(CovIdx::phiCov)] = phiHits[phiP]->template localCovariance<1>()[0];
392 newSp.setCovariance(std::move(cov));
393 ATH_MSG_VERBOSE("Created new space point "<<newSp);
394 }
395 }
396 }
397 }
398 } while (viewer.next());
399 return StatusCode::SUCCESS;
400}
401
402template<>
403StatusCode SpacePointMakerAlg::loadContainerAndSort(const EventContext& ctx,
405 PreSortedSpacePointMap& fillContainer) const {
406
407 const xAOD::sTgcMeasContainer* measurementCont{nullptr};
408 ATH_CHECK(SG::get(measurementCont, key, ctx));
409 if (!measurementCont || measurementCont->empty()){
410 ATH_MSG_DEBUG("nothing to do");
411 return StatusCode::SUCCESS;
412 }
413 const ActsTrk::GeometryContext* gctx{nullptr};
414 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
415 xAOD::ChamberViewer viewer{*measurementCont};
416 using namespace Acts::detail::LineHelper;
417 do {
418 SpacePointsPerChamber& pointsInChamb = fillContainer[viewer.at(0)->readoutElement()->msSector()];
419 const Amg::Transform3D sectorTrans = viewer.at(0)->readoutElement()->msSector()->globalToLocalTransform(*gctx);
420 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Fill space points for multiplet "<<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
421 for(auto& HitColls: splitHitsPerGasGap(viewer)){
422 auto& [etaHits, phiHits, two2DHits] = HitColls;
423 std::array<std::vector<std::shared_ptr<unsigned>>, 3> instanceCounts{matchCountVec(etaHits.size()),
424 matchCountVec(phiHits.size()),
425 matchCountVec(two2DHits.size())};
426
427 //loop through the Prds and try to combine according` to the hierarchy
428 // Strip+Wire
429 // Strip+Pad
430 // Wire+Pad
431 // Pad
439 auto combineMe = [&](const std::size_t collIdxA,
440 const std::size_t collIdxB,
441 const std::function<bool(const xAOD::sTgcMeasurement*,
442 const xAOD::sTgcMeasurement*)>& combFunc) {
443 std::vector<char> combinedFlagsA{}, combinedFlagsB{};
444 std::ranges::transform(instanceCounts[collIdxA], std::back_inserter(combinedFlagsA),
445 [](const std::shared_ptr<unsigned>& countPtr){
446 return (*countPtr) == 0;
447 });
448 std::ranges::transform(instanceCounts[collIdxB], std::back_inserter(combinedFlagsB),
449 [](const std::shared_ptr<unsigned>& countPtr){
450 return (*countPtr) == 0;
451 });
452
453 const auto& collA = HitColls[collIdxA];
454 const auto& collB = HitColls[collIdxB];
455
457 if(collA.empty() || collB.empty()) {
458 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Skipping combination: both collections empty");
459 return;
460 }
461
463 const xAOD::sTgcMeasurement* firstHit = collB.front();
464 const Amg::Transform3D toSectorTrans = toChamberTransform(*gctx, sectorTrans, *firstHit);
465
466 for(std::size_t idxA = 0; idxA < collA.size(); ++idxA) {
468 if(!combinedFlagsA[idxA]) {
469 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Hit "<<m_idHelperSvc->toString(collA[idxA]->identify())
470 <<" has been used in previous iteration");
471 continue;
472 }
473 for(std::size_t idxB = 0; idxB < collB.size(); ++idxB) {
474 if(!combinedFlagsB[idxB] || !combFunc(collA[idxA], collB[idxB])){
475 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Hit "<<m_idHelperSvc->toString(collB[idxB]->identify())
476 <<" has been used in previous iteration. Or is incompatible with "
477 <<m_idHelperSvc->toString(collA[idxA]->identify()));
478 continue;
479 }
480 //create space point
481 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Combine sTgc measurements "
482 <<m_idHelperSvc->toString(collA[idxA]->identify())<<" and "
483 <<m_idHelperSvc->toString(collB[idxB]->identify())<< "with local positions"
484 << Amg::toString(collA[idxA]->localMeasurementPos()) << " and "
485 << Amg::toString(collB[idxB]->localMeasurementPos())
486 <<" to new space point");
487
488 SpacePoint& newSp = pointsInChamb.etaHits.emplace_back(collB[idxB], collA[idxA]);
489 auto crossPoint = lineIntersect<3>(collA[idxA]->localMeasurementPos(),
490 Amg::Vector3D::UnitX(),
491 collB[idxB]->localMeasurementPos(),
492 Amg::Vector3D::UnitY());
493
494 newSp.setPosition(toSectorTrans*crossPoint.position());
495 newSp.setDirection(Amg::Vector3D::UnitX(), Amg::Vector3D::UnitY());
496 auto cov = Acts::filledArray<double, 3>(0.);
497 cov[Acts::toUnderlying(CovIdx::phiCov)] = covElement(*collA[idxA], CovIdx::phiCov);
498 cov[Acts::toUnderlying(CovIdx::etaCov)] = covElement(*collB[idxB], CovIdx::etaCov);
499 newSp.setCovariance(std::move(cov));
500 newSp.setInstanceCounts(instanceCounts[collIdxB][idxB], instanceCounts[collIdxA][idxA]);
501 ATH_MSG_VERBOSE("Created new space point "<<newSp);
502 }
503 }
504 };
505
506 //try to combine strip with wire measurements first
507 combineMe(1, 0, [&](const xAOD::sTgcMeasurement* wire,
509 // do not combine the strips with the wire that are in the etaZero region
510 const MuonGMR4::sTgcReadoutElement* readoutElement = strip->readoutElement();
511 if(readoutElement->isEtaZero(strip->measurementHash(),
512 strip->localMeasurementPos().block<2,1>(0,0))){
513 return false;
514 }
515 //ignore combinations where the wire and the strip are not crossing
516 //check if the projection of the crossing point is within the bounds of the layer
517 auto crossPoint = strip->localMeasurementPos() + wire->localMeasurementPos();
518 const Acts::Surface& surf = readoutElement->surface(strip->layerHash());
519 return surf.insideBounds(crossPoint.block<2,1>(0,0));
520 });
521
522 //combine strip and pad measurements
523 combineMe(2, 0, [&](const xAOD::sTgcMeasurement* pad,
525 // do not combine the strips with the pads that are not overlayed
526 const MuonGMR4::sTgcReadoutElement* readoutElement = pad ->readoutElement();
527 const MuonGMR4::PadDesign& padDesign = readoutElement->padDesign(pad->measurementHash());
528 double padHeight = padDesign.padHeight();
529 const Amg::Vector3D padCenter = pad->localMeasurementPos();
530
531 return std::abs(strip->localMeasurementPos().x() - padCenter.x()) < 0.5*padHeight;
532 });
533
534 //finally combine wire and pad measurements
535 combineMe(1, 2, [&](const xAOD::sTgcMeasurement* wire,
536 const xAOD::sTgcMeasurement* pad){
537 // do not combine the wires with the pads that are not overlayed
538 const MuonGMR4::sTgcReadoutElement* readoutElement = pad ->readoutElement();
539 const std::array<Amg::Vector2D, 4> localPadCorners = readoutElement->localPadCorners(pad->measurementHash());
540 auto [min,max] = std::ranges::minmax_element(localPadCorners.begin(), localPadCorners.end(),
541 [](const Amg::Vector2D& a, const Amg::Vector2D& b){
542 return a.y() < b.y();
543 });
544 return (wire->localMeasurementPos().y() > min->y() || wire->localMeasurementPos().y() < max->y());
545
546 });
547
548 //fill uncombined strip, wire and pad measurements that have not been used in combination
549 for(std::size_t collIdx = 0; collIdx < HitColls.size(); ++collIdx){
550 const auto& hits = HitColls[collIdx];
551 std::vector<const xAOD::sTgcMeasurement*> unusedHits{};
552 unusedHits.reserve(hits.size());
553
554 for(std::size_t idx = 0; idx < hits.size(); ++idx){
555 if((*instanceCounts[collIdx][idx]) == 0){
556 unusedHits.push_back(hits[idx]);
557 }
558 }
559 fillUncombinedSpacePoints(*gctx, sectorTrans, unusedHits, pointsInChamb.etaHits);
560 }
561 }
562 } while (viewer.next());
563 return StatusCode::SUCCESS;
564}
565
566
567StatusCode SpacePointMakerAlg::execute(const EventContext& ctx) const {
568 PreSortedSpacePointMap preSortedContainer{};
569 ATH_CHECK(loadContainerAndSort(ctx, m_mdtKey, preSortedContainer));
570 ATH_CHECK(loadContainerAndSort(ctx, m_rpcKey, preSortedContainer));
571 ATH_CHECK(loadContainerAndSort(ctx, m_tgcKey, preSortedContainer));
572 ATH_CHECK(loadContainerAndSort(ctx, m_mmKey, preSortedContainer));
573 ATH_CHECK(loadContainerAndSort(ctx, m_stgcKey, preSortedContainer));
574 std::unique_ptr<SpacePointContainer> outContainer = std::make_unique<SpacePointContainer>();
575
576 for (auto &[chamber, hitsPerChamber] : preSortedContainer){
577 ATH_MSG_DEBUG("Fill space points for chamber "<<chamber->identString() << " with "<<hitsPerChamber.etaHits.size()
578 <<" primary and "<<hitsPerChamber.phiHits.size()<<" phi space points.");
579 distributePointsAndStore(std::move(hitsPerChamber), *outContainer);
580 }
581 SG::WriteHandle writeHandle{m_writeKey, ctx};
582 ATH_CHECK(writeHandle.record(std::move(outContainer)));
583 return StatusCode::SUCCESS;
584}
585
587 SpacePointContainer& finalContainer) const {
588 SpacePointBucketVec splittedHits{};
589 splittedHits.emplace_back();
590 if (m_statCounter){
591 m_statCounter->addToStat(hitsPerChamber.etaHits);
592 m_statCounter->addToStat(hitsPerChamber.phiHits);
593
594 }
595 distributePrimaryPoints(std::move(hitsPerChamber.etaHits), splittedHits);
596 splittedHits.erase(std::remove_if(splittedHits.begin(), splittedHits.end(),
597 [](const SpacePointBucket& bucket) {
598 return bucket.size() <= 1;
599 }), splittedHits.end());
600 distributePhiPoints(std::move(hitsPerChamber.phiHits), splittedHits);
601
602 for (SpacePointBucket& bucket : splittedHits) {
603
604 std::ranges::sort(bucket, MuonR4::SpacePointPerLayerSorter{});
605
606 if (msgLvl(MSG::VERBOSE)){
607 std::stringstream spStr{};
608 for (const std::shared_ptr<SpacePoint>& sp : bucket){
609 spStr<<"SpacePoint: PrimaryMeas: " <<(*sp)<<std::endl;
610 }
611 ATH_MSG_VERBOSE("Created a bucket, printing all spacepoints..."<<std::endl<<spStr.str());
612 }
614 finalContainer.push_back(std::make_unique<SpacePointBucket>(std::move(bucket)));
615 }
616
617}
618void SpacePointMakerAlg::distributePhiPoints(std::vector<SpacePoint>&& spacePoints,
619 SpacePointBucketVec& splittedContainer) const{
620 for (SpacePoint& sp : spacePoints) {
621 auto phiPoint = std::make_shared<SpacePoint>(std::move(sp));
622 const double dY = std::sqrt(phiPoint->covariance()[Acts::toUnderlying(CovIdx::etaCov)]);
623 const double minY = phiPoint->localPosition().y() - dY;
624 const double maxY = phiPoint->localPosition().y() + dY;
625 for (SpacePointBucket& bucket : splittedContainer){
628 if (! (maxY < bucket.coveredMin() || bucket.coveredMax() < minY) ) {
629 bucket.emplace_back(phiPoint);
630 }
631 }
632 }
633}
635 const double firstSpPos,
636 const SpacePointBucketVec& sortedPoints) const {
638 const double spY = spacePoint.localPosition().y();
639 if (spY - firstSpPos > m_maxBucketLength){
640 return true;
641 }
642
643 if (sortedPoints.empty() || sortedPoints.back().empty()) {
644 return false;
645 }
646 return spY - sortedPoints.back().back()->localPosition().y() > m_spacePointWindow;
647}
649 SpacePointBucketVec& sortedPoints) const {
650 SpacePointBucket& newContainer = sortedPoints.emplace_back();
651 newContainer.setBucketId(sortedPoints.size() -1);
652
654 SpacePointBucket& overlap{sortedPoints[sortedPoints.size() - 2]};
655 overlap.setCoveredRange(overlap.front()->localPosition().y(),
656 overlap.back()->localPosition().y());
657
658 const double refBound = refSpacePoint.localPosition().y();
659
661 for (const std::shared_ptr<SpacePoint>& pointInBucket : overlap | std::views::reverse) {
662 const double overlapPos = pointInBucket->localPosition().y() +
663 std::sqrt(pointInBucket->covariance()[Acts::toUnderlying(CovIdx::etaCov)]);
664 if (refBound - overlapPos < m_spacePointOverlap) {
665 newContainer.insert(newContainer.begin(), pointInBucket);
666 } else {
667 break;
668 }
669 }
670
671}
672
673void SpacePointMakerAlg::distributePrimaryPoints(std::vector<SpacePoint>&& spacePoints,
674 SpacePointBucketVec& splittedHits) const {
675
676 if (spacePoints.empty()) return;
677
679 std::ranges::sort(spacePoints,
680 [] (const SpacePoint& a, const SpacePoint& b) {
681 return a.localPosition().y() < b.localPosition().y();
682 });
683
684 double firstPointPos = spacePoints.front().localPosition().y();
685
686 for (SpacePoint& toSort : spacePoints) {
687 ATH_MSG_VERBOSE("Add new primary space point "<<toSort);
688
689 if (splitBucket(toSort, firstPointPos, splittedHits)){
690 newBucket(toSort, splittedHits);
691 firstPointPos = splittedHits.back().empty() ? toSort.localPosition().y() : splittedHits.back().front()->localPosition().y();
692 ATH_MSG_VERBOSE("New bucket: id " << splittedHits.back().bucketId() << " Coverage: " << firstPointPos);
693 }
694 std::shared_ptr<SpacePoint> spacePoint = std::make_shared<SpacePoint>(std::move(toSort));
695 splittedHits.back().emplace_back(spacePoint);
696 }
697 SpacePointBucket& lastBucket{splittedHits.back()};
698 lastBucket.setCoveredRange(lastBucket.front()->localPosition().y(),
699 lastBucket.back()->localPosition().y());
700}
701
702}
const boost::regex re(r_e)
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_DEBUG(x)
#define ATH_UNLIKELY(x)
static Double_t sp
static Double_t a
Handle class for reading from StoreGate.
Handle class for recording to StoreGate.
#define min(a, b)
Definition cfImp.cxx:40
#define max(a, b)
Definition cfImp.cxx:41
bool msgLvl(const MSG::Level lvl) const
value_type push_back(value_type pElem)
Add an element to the end of the collection.
bool empty() const noexcept
Returns true if the collection is empty.
MuonReadoutElement is an abstract class representing the geometry of a muon detector.
const Amg::Transform3D & localToGlobalTransform(const ActsTrk::GeometryContext &ctx) const
Returns the transformation from the local coordinate system of the readout element into the global AT...
const Acts::Surface & surface() const override final
Returns the surface associated with the readout element.
double padHeight() const
Returns the height of all the pads that are not adjacent to the bottom edge of the trapezoid active a...
const PadDesign & padDesign(const IdentifierHash &measHash) const
Retrieves the readoutElement Layer given the Identifier/Hash.
localCornerArray localPadCorners(const IdentifierHash &measHash) const
bool isEtaZero(const IdentifierHash &measurementHash, const Amg::Vector2D &localPosition) const
: The muon space point bucket represents a collection of points that will bre processed together in t...
void setCoveredRange(double min, double max)
set the range in the precision plane covered by the bucket
void setBucketId(unsigned int id)
sets the Identifier of the MuonSpacePointBucket in context of the associated muonChamber
SpacePointStatistics(const Muon::IMuonIdHelperSvc *idHelperSvc)
Standard constructor.
void addToStat(const std::vector< SpacePoint > &spacePoints)
Adds the vector of space points to the overall statistics.
void dumpStatisics(MsgStream &msg) const
Print the statistics table of the built space points per category into the log-file / console.
Gaudi::Property< double > m_maxOccRpcEta
Gaudi::Property< double > m_maxOccTgcEta
Gaudi::Property< double > m_maxOccTgcPhi
Gaudi::Property< double > m_spacePointWindow
Gaudi::Property< bool > m_doStat
SG::ReadHandleKey< ActsTrk::GeometryContext > m_geoCtxKey
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
void fillUncombinedSpacePoints(const ActsTrk::GeometryContext &gctx, const Amg::Transform3D &sectorTrans, const PrdVec_t< PrdType * > &prdsToFill, std::vector< SpacePoint > &outColl) const
Transform the uncombined space prd measurements to space points.
std::vector< EtaPhi2DHits< T > > EtaPhi2DHitsVec
SG::ReadHandleKey< xAOD::TgcStripContainer > m_tgcKey
void newBucket(const SpacePoint &refSp, SpacePointBucketVec &sortedPoints) const
Closes the current processed bucket and creates a new one.
void distributePhiPoints(std::vector< SpacePoint > &&spacePoints, SpacePointBucketVec &splittedContainer) const
Distributs the vector phi space points into the buckets.
Gaudi::Property< double > m_maxOccRpcPhi
Gaudi::Property< double > m_maxBucketLength
SG::ReadHandleKey< xAOD::MMClusterContainer > m_mmKey
bool passOccupancy2D(const PrdVec_t< PrdType > &etaHits, const PrdVec_t< PrdType > &phiHits) const
: Check whether the occupancy cuts of hits in a gasGap are surpassed.
bool splitBucket(const SpacePoint &spacePoint, const double firstSpPos, const SpacePointBucketVec &sortedPoints) const
Returns whether the space point is beyond the bucket boundary.
std::vector< SpacePointBucket > SpacePointBucketVec
Abrivation of a MuonSapcePoint bucket vector.
SG::WriteHandleKey< SpacePointContainer > m_writeKey
StatusCode loadContainerAndSort(const EventContext &ctx, const SG::ReadHandleKey< ContType > &key, PreSortedSpacePointMap &fillContainer) const
Retrieve an uncalibrated measurement container <ContType> and fill the hits into the presorted space ...
SG::ReadHandleKey< xAOD::RpcMeasurementContainer > m_rpcKey
Gaudi::Property< double > m_spacePointOverlap
StatusCode execute(const EventContext &ctx) const override
std::vector< Prd_t > PrdVec_t
void distributePrimaryPoints(std::vector< SpacePoint > &&spacePoints, SpacePointBucketVec &splittedContainer) const
Distributes the vector of primary eta or eta + phi space points and fills them into the buckets.
StatusCode initialize() override
SG::ReadHandleKey< xAOD::sTgcMeasContainer > m_stgcKey
void distributePointsAndStore(SpacePointsPerChamber &&hitsPerChamber, SpacePointContainer &finalContainer) const
Distribute the premade spacepoints per chamber into their individual SpacePoint buckets.
EtaPhi2DHitsVec< typename ContType::const_value_type > splitHitsPerGasGap(xAOD::ChamberViewer< ContType > &viewer) const
Splits the chamber hits of the viewer per gas gap.
StatusCode finalize() override
########################################## SpacePointMakerAlg #######################################...
std::unordered_map< const MuonGMR4::SpectrometerSector *, SpacePointsPerChamber > PreSortedSpacePointMap
Container abrivation of the presorted space point container per MuonChambers.
Gaudi::Property< unsigned > m_capacityBucket
SG::ReadHandleKey< xAOD::MdtDriftCircleContainer > m_mdtKey
The SpacePointPerLayerSorter sort two given space points by their layer Identifier.
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
void setInstanceCounts(std::shared_ptr< unsigned > etaCounts, std::shared_ptr< unsigned > phiCounts)
Set the number of space points built with the same eta / phi prd.
void setDirection(const Amg::Vector3D &sensorDir, const Amg::Vector3D &toNextSensor)
Setter for the direction of the measurement channel in the sector frame.
bool measuresEta() const
: Does the space point contain an eta measurement
Interface for Helper service that creates muon Identifiers and can be used to print Identifiers.
Property holding a SG store/key/clid from which a ReadHandle is made.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
bool next() noexcept
Loads the hits from the next chamber.
const_ref at(const std::size_t idx) const
Returns the i-the measurement from the current chamber.
std::size_t size() const noexcept
Returns how many hits are in the current chamber.
IdentifierHash measurementHash() const
Returns the hash of the measurement channel w.r.t ReadoutElement.
Amg::Vector3D localMeasurementPos() const
Returns the local measurement position as 3-vector.
const MuonGMR4::sTgcReadoutElement * readoutElement() const
Retrieve the associated sTgcReadoutElement.
#define ATH_FLATTEN
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
This header ties the generic definitions in this package.
DataVector< SpacePointBucket > SpacePointContainer
Abrivation of the space point container type.
const std::string & stName(StIndex index)
convert StIndex into a string
const std::string & technologyName(TechnologyIndex index)
convert LayerIndex into a string
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
void stable_sort(DataModel_detail::iterator< DVL > beg, DataModel_detail::iterator< DVL > end)
Specialization of stable_sort for DataVector/List.
DataModel_detail::iterator< DVL > remove_if(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end, Predicate pred)
Specialization of remove_if for DataVector/List.
MdtTwinDriftCircle_v1 MdtTwinDriftCircle
sTgcMeasContainer_v1 sTgcMeasContainer
sTgcMeasurement_v1 sTgcMeasurement
Helper struct to define the counting categories.
bool operator<(const FieldKey &other) const
################################################################ SpacePointStatistics ###############...
Helper struct to count the space-points in each detector category.
unsigned measEtaPhi
Number of space points measuring eta & phi.
unsigned measEta
Number of space points measuring eta only.
unsigned measPhi
Number of space points measuring phi only.
unsigned allHits() const
Helper method returning the sum of the three space point type counts.
: Helper struct to collect the space point per muon chamber, which are later sorted into the space po...
std::vector< SpacePoint > etaHits
Vector of all hits that contain an eta measurement including the ones which are combined with phi mea...
std::vector< SpacePoint > phiHits
Vector of all space points that are built from single phi hits.