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<const 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.linear() * stripLayout->to3D(design.stripDir(), allSpArePhi);
222 toNextSen = toSectorTrans.linear() * stripLayout->to3D(design.stripNormal(), allSpArePhi);
223 ATH_MSG_VERBOSE("Fill space points for "<<m_idHelperSvc->toString(refMeas->identify())
224 <<" -> sensor: "<<Amg::toString(sensorDir)<<", "<<Amg::toString(toNextSen));
225 } else if constexpr (std::is_same_v<PrdType, xAOD::sTgcMeasurement>){
226 allSpArePhi = refMeas->channelType() == xAOD::sTgcMeasurement::sTgcChannelTypes::Wire;
227 const auto& stripLayout = refMeas->readoutElement()->stripLayer(refMeas->measurementHash());
228 const auto& design = stripLayout.design(allSpArePhi);
229 sensorDir = toSectorTrans.linear() * stripLayout.to3D(design.stripDir(), allSpArePhi);
230 toNextSen = toSectorTrans.linear() * stripLayout.to3D(design.stripNormal(), allSpArePhi);
231 } else {
232 sensorDir = toSectorTrans.linear().col(Amg::y);
233 toNextSen = toSectorTrans.linear().col(Amg::x);
234 }
235 outColl.reserve(outColl.size() + prdsToFill.size());
236 for (const PrdType* prd: prdsToFill) {
237 SpacePoint& newSp = outColl.emplace_back(prd);
238 if constexpr (std::is_same_v<PrdType, xAOD::TgcStrip>) {
239 if (allSpArePhi) {
240 const auto& stripLayout = refMeas->readoutElement()->sensorLayout(refMeas->layerHash());
241 const auto& radialDesign = static_cast<const MuonGMR4::RadialStripDesign&>(stripLayout->design(allSpArePhi));
242 toNextSen = toSectorTrans.linear() * stripLayout->to3D(radialDesign.stripNormal(prd->channelNumber()), allSpArePhi);
243 sensorDir = toSectorTrans.linear() * stripLayout->to3D(radialDesign.stripDir(prd->channelNumber()), allSpArePhi);
244 }
245 }
246 newSp.setPosition(toSectorTrans * prd->localMeasurementPos());
247 newSp.setDirection(sensorDir, toNextSen);
248 auto cov = Acts::filledArray<double,3>(0.);
249 if (prd->numDimensions() == 2) {
250 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->template localCovariance<2>()(0,0);
251 cov[Acts::toUnderlying(CovIdx::phiCov)] = prd->template localCovariance<2>()(1,1);
252 } else {
254 auto covIdx{Acts::toUnderlying(CovIdx::etaCov)},
255 lenIdx{Acts::toUnderlying(CovIdx::phiCov)};
256 if (!newSp.measuresEta()) {
257 std::swap(covIdx, lenIdx);
258 }
259 cov[covIdx] = prd->template localCovariance<1>()[0];
260 cov[lenIdx] = Acts::square(sensorHalfLength(*prd));
261 }
262 newSp.setCovariance(std::move(cov));
263 }
264}
265
266
267template <typename ContType>
270 std::vector<EtaPhi2DHits<typename ContType::const_value_type>> hitsPerGasGap{};
271 for (const auto& prd : viewer) {
272 ATH_MSG_VERBOSE("Create space point from "<<m_idHelperSvc->toString(prd->identify())
273 <<", hash: "<<prd->identifierHash());
274
275 unsigned gapIdx = prd->gasGap() -1;
276 if constexpr (std::is_same_v<ContType, xAOD::RpcMeasurementContainer>) {
277 gapIdx = prd->readoutElement()->createHash(0, prd->gasGap(), prd->doubletPhi(), false);
278 }
279 if (hitsPerGasGap.size() <= gapIdx) {
280 hitsPerGasGap.resize(gapIdx + 1);
281 }
282 bool measPhi{false};
283 if constexpr(std::is_same_v<ContType, xAOD::sTgcMeasContainer>) {
285 measPhi = prd->channelType() == sTgcIdHelper::sTgcChannelTypes::Wire;
286 } else if constexpr(!std::is_same_v<ContType, xAOD::MMClusterContainer>) {
288 measPhi = prd->measuresPhi();
289 }
290
291 if (prd->numDimensions() == 2) {
292 hitsPerGasGap[gapIdx][2].push_back(prd);
293 continue;
294 }
296 auto& toPush = hitsPerGasGap[gapIdx][measPhi];
297 if (toPush.capacity() == toPush.size()) {
298 toPush.reserve(toPush.size() + m_capacityBucket);
299 }
300 toPush.push_back(prd);
301 }
302 return hitsPerGasGap;
303}
304
305template <typename ContType>
306 StatusCode SpacePointMakerAlg::loadContainerAndSort(const EventContext& ctx,
308 PreSortedSpacePointMap& fillContainer) const {
309 const ContType* measurementCont{nullptr};
310 ATH_CHECK(SG::get(measurementCont, key, ctx));
311 if (!measurementCont || measurementCont->empty()){
312 ATH_MSG_DEBUG("nothing to do");
313 return StatusCode::SUCCESS;
314 }
315 const ActsTrk::GeometryContext* gctx{nullptr};
316 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
317
318 xAOD::ChamberViewer viewer{*measurementCont};
319
320 do {
321 SpacePointsPerChamber& pointsInChamb = fillContainer[viewer.at(0)->readoutElement()->msSector()];
322 const Amg::Transform3D sectorTrans = viewer.at(0)->readoutElement()->msSector()->globalToLocalTransform(*gctx);
323 ATH_MSG_DEBUG("Fill space points for chamber "<<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
324 if constexpr( std::is_same_v<ContType, xAOD::MdtDriftCircleContainer>) {
325 pointsInChamb.etaHits.reserve(pointsInChamb.etaHits.capacity() + viewer.size());
326 for (const auto& prd : viewer) {
327 Amg::Transform3D toChamberTrans{toChamberTransform(*gctx, sectorTrans, *prd)};
328 SpacePoint& sp{pointsInChamb.etaHits.emplace_back(prd)};
329 sp.setPosition(toChamberTrans*prd->localMeasurementPos());
330 sp.setDirection(toChamberTrans.linear().col(Amg::z),
331 toChamberTrans.linear().col(Amg::y));
332 std::array<double, 3> cov{Acts::filledArray<double,3>(0.)};
333 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->driftRadiusCov();
334 cov[Acts::toUnderlying(CovIdx::phiCov)] = Acts::square(sensorHalfLength(*prd));
335 if (ATH_UNLIKELY(prd->numDimensions() == 2)){
336 cov[Acts::toUnderlying(CovIdx::phiCov)] = static_cast<const xAOD::MdtTwinDriftCircle*>(prd)->posAlongWireCov();
337 }
338 sp.setCovariance(std::move(cov));
339 }
340 } else {
342 for (auto& [etaHits, phiHits, two2DHits] : splitHitsPerGasGap(viewer)) {
343 ATH_MSG_DEBUG("Found "<<etaHits.size()<<"/"<<phiHits.size()
344 <<" 1D and "<<two2DHits.size()<<" 2D hits in chamber "
345 <<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
347 fillUncombinedSpacePoints(*gctx, sectorTrans, two2DHits, pointsInChamb.etaHits);
349 // Check if we do not have 2D occupancy (missing phi or eta hits)
350 if (!passOccupancy2D(etaHits, phiHits)) {
351 fillUncombinedSpacePoints(*gctx, sectorTrans, etaHits, pointsInChamb.etaHits);
352 fillUncombinedSpacePoints(*gctx, sectorTrans, phiHits, pointsInChamb.phiHits);
353 continue;
354 }
355
356 std::vector<std::shared_ptr<unsigned>> etaCounts{matchCountVec(etaHits.size())},
357 phiCounts{matchCountVec(phiHits.size())};
358
359 pointsInChamb.etaHits.reserve(pointsInChamb.etaHits.size() + etaHits.size()*phiHits.size());
361 const auto& firstEta{etaHits.front()};
362 const Amg::Transform3D toSectorTrans = toChamberTransform(*gctx, sectorTrans, *firstEta);
363
364 Amg::Vector3D toNextDir{Amg::Vector3D::Zero()}, sensorDir{Amg::Vector3D::Zero()};
365 if constexpr (std::is_same_v<xAOD::RpcMeasurementContainer, ContType> ||
366 std::is_same_v<xAOD::TgcStripContainer, ContType>) {
367 const auto& stripLayout = firstEta->readoutElement()->sensorLayout(firstEta->layerHash());
368 const auto& design = stripLayout->design();
369 sensorDir = toSectorTrans.linear() * stripLayout->to3D(design.stripDir(), false);
370 toNextDir = toSectorTrans.linear() * stripLayout->to3D(design.stripNormal(), false);
371 } else if constexpr (std::is_same_v<xAOD::sTgcMeasContainer, ContType>){
372 const auto& stripLayout = firstEta->readoutElement()->stripLayer(firstEta->measurementHash());
373 const auto& design = stripLayout.design(false);
374 sensorDir = toSectorTrans.linear() * stripLayout.to3D(design.stripDir(), false);
375 toNextDir = toSectorTrans.linear() * stripLayout.to3D(design.stripNormal(), false);
376 } else {
377 ATH_MSG_ERROR("Unsupported container type");
378 return StatusCode::FAILURE;
379 }
380
381 using namespace Acts::detail::LineHelper;
382 for (unsigned etaP = 0; etaP < etaHits.size(); ++etaP) {
384 for (unsigned phiP = 0; phiP < phiHits.size(); ++ phiP) {
386 if constexpr(std::is_same_v<xAOD::TgcStripContainer, ContType>) {
387 if (!(etaHits[etaP]->bcBitMap() & phiHits[phiP]->bcBitMap())){
388 continue;
389 }
390 const auto& stripLay = phiHits[phiP]->readoutElement()->sensorLayout(phiHits[phiP]->layerHash());
391 const auto& radialDesign = static_cast<const MuonGMR4::RadialStripDesign&>(stripLay->design(true));
392 toNextDir = toSectorTrans.linear() * stripLay->to3D(radialDesign.stripDir(phiHits[phiP]->channelNumber()), true);
393 }
394
395 SpacePoint& newSp = pointsInChamb.etaHits.emplace_back(etaHits[etaP], phiHits[phiP]);
396 newSp.setInstanceCounts(etaCounts[etaP], phiCounts[phiP]);
397
398 auto spIsect = lineIntersect(toSectorTrans*etaHits[etaP]->localMeasurementPos(), sensorDir,
399 toSectorTrans*phiHits[phiP]->localMeasurementPos(), toNextDir);
400 newSp.setPosition(spIsect.position());
401 newSp.setDirection(sensorDir, toNextDir);
402 auto cov = Acts::filledArray<double, 3>(0.);
403 cov[Acts::toUnderlying(CovIdx::etaCov)] = etaHits[etaP]->template localCovariance<1>()[0];
404 cov[Acts::toUnderlying(CovIdx::phiCov)] = phiHits[phiP]->template localCovariance<1>()[0];
406 if constexpr(std::is_same_v<xAOD::TgcStripContainer, ContType>) {
407 const auto& stripLay = phiHits[phiP]->readoutElement()->sensorLayout(phiHits[phiP]->layerHash());
408 const auto& radialDesign = static_cast<const MuonGMR4::RadialStripDesign&>(stripLay->design(true));
409 const Amg::Vector2D planePos = stripLay->to2D(toSectorTrans.inverse()*spIsect.position(), true);
410 cov[Acts::toUnderlying(CovIdx::phiCov)] =
411 Acts::square(radialDesign.stripPitch(phiHits[phiP]->channelNumber(), planePos)) / 12.;
412 }
413
414 newSp.setCovariance(std::move(cov));
415 ATH_MSG_VERBOSE("Created new space point "<<newSp);
416 }
417 }
418 }
419 }
420 } while (viewer.next());
421 return StatusCode::SUCCESS;
422}
423
424template<>
425StatusCode SpacePointMakerAlg::loadContainerAndSort(const EventContext& ctx,
427 PreSortedSpacePointMap& fillContainer) const {
428
429 const xAOD::sTgcMeasContainer* measurementCont{nullptr};
430 ATH_CHECK(SG::get(measurementCont, key, ctx));
431 if (!measurementCont || measurementCont->empty()){
432 ATH_MSG_DEBUG("nothing to do");
433 return StatusCode::SUCCESS;
434 }
435 const ActsTrk::GeometryContext* gctx{nullptr};
436 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
437 xAOD::ChamberViewer viewer{*measurementCont};
438 using namespace Acts::detail::LineHelper;
439 do {
440 SpacePointsPerChamber& pointsInChamb = fillContainer[viewer.at(0)->readoutElement()->msSector()];
441 const Amg::Transform3D sectorTrans = viewer.at(0)->readoutElement()->msSector()->globalToLocalTransform(*gctx);
442 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Fill space points for multiplet "<<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
443 for(auto& HitColls: splitHitsPerGasGap(viewer)){
444 auto& [etaHits, phiHits, two2DHits] = HitColls;
445 std::array<std::vector<std::shared_ptr<unsigned>>, 3> instanceCounts{matchCountVec(etaHits.size()),
446 matchCountVec(phiHits.size()),
447 matchCountVec(two2DHits.size())};
448
449 //loop through the Prds and try to combine according` to the hierarchy
450 // Strip+Wire
451 // Strip+Pad
452 // Wire+Pad
453 // Pad
461 auto combineMe = [&](const std::size_t collIdxA,
462 const std::size_t collIdxB,
463 const std::function<bool(const xAOD::sTgcMeasurement*,
464 const xAOD::sTgcMeasurement*)>& combFunc) {
465 std::vector<char> combinedFlagsA{}, combinedFlagsB{};
466 std::ranges::transform(instanceCounts[collIdxA], std::back_inserter(combinedFlagsA),
467 [](const std::shared_ptr<unsigned>& countPtr){
468 return (*countPtr) == 0;
469 });
470 std::ranges::transform(instanceCounts[collIdxB], std::back_inserter(combinedFlagsB),
471 [](const std::shared_ptr<unsigned>& countPtr){
472 return (*countPtr) == 0;
473 });
474
475 const auto& collA = HitColls[collIdxA];
476 const auto& collB = HitColls[collIdxB];
477
479 if(collA.empty() || collB.empty()) {
480 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Skipping combination: both collections empty");
481 return;
482 }
483
485 const xAOD::sTgcMeasurement* firstHit = collB.front();
486 const Amg::Transform3D toSectorTrans = toChamberTransform(*gctx, sectorTrans, *firstHit);
487
488 for(std::size_t idxA = 0; idxA < collA.size(); ++idxA) {
490 if(!combinedFlagsA[idxA]) {
491 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Hit "<<m_idHelperSvc->toString(collA[idxA]->identify())
492 <<" has been used in previous iteration");
493 continue;
494 }
495 for(std::size_t idxB = 0; idxB < collB.size(); ++idxB) {
496 if(!combinedFlagsB[idxB] || !combFunc(collA[idxA], collB[idxB])){
497 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Hit "<<m_idHelperSvc->toString(collB[idxB]->identify())
498 <<" has been used in previous iteration. Or is incompatible with "
499 <<m_idHelperSvc->toString(collA[idxA]->identify()));
500 continue;
501 }
502 //create space point
503 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Combine sTgc measurements "
504 <<m_idHelperSvc->toString(collA[idxA]->identify())<<" and "
505 <<m_idHelperSvc->toString(collB[idxB]->identify())<< "with local positions"
506 << Amg::toString(collA[idxA]->localMeasurementPos()) << " and "
507 << Amg::toString(collB[idxB]->localMeasurementPos())
508 <<" to new space point");
509
510 SpacePoint& newSp = pointsInChamb.etaHits.emplace_back(collB[idxB], collA[idxA]);
511 auto crossPoint = lineIntersect<3>(collA[idxA]->localMeasurementPos(),
512 Amg::Vector3D::UnitX(),
513 collB[idxB]->localMeasurementPos(),
514 Amg::Vector3D::UnitY());
515
516 newSp.setPosition(toSectorTrans*crossPoint.position());
517 newSp.setDirection(Amg::Vector3D::UnitX(), Amg::Vector3D::UnitY());
518 auto cov = Acts::filledArray<double, 3>(0.);
519 cov[Acts::toUnderlying(CovIdx::phiCov)] = covElement(*collA[idxA], CovIdx::phiCov);
520 cov[Acts::toUnderlying(CovIdx::etaCov)] = covElement(*collB[idxB], CovIdx::etaCov);
521 newSp.setCovariance(std::move(cov));
522 newSp.setInstanceCounts(instanceCounts[collIdxB][idxB], instanceCounts[collIdxA][idxA]);
523 ATH_MSG_VERBOSE("Created new space point "<<newSp);
524 }
525 }
526 };
527
528 //try to combine strip with wire measurements first
529 combineMe(1, 0, [&](const xAOD::sTgcMeasurement* wire,
531 // do not combine the strips with the wire that are in the etaZero region
532 const MuonGMR4::sTgcReadoutElement* readoutElement = strip->readoutElement();
533 if(readoutElement->isEtaZero(strip->measurementHash(),
534 strip->localMeasurementPos().block<2,1>(0,0))){
535 return false;
536 }
537 //ignore combinations where the wire and the strip are not crossing
538 //check if the projection of the crossing point is within the bounds of the layer
539 Amg::Vector3D crossPoint = strip->localMeasurementPos() + wire->localMeasurementPos();
540 const Acts::Surface& surf = readoutElement->surface(strip->layerHash());
541 return surf.insideBounds(crossPoint.block<2,1>(0,0));
542 });
543
544 //combine strip and pad measurements
545 combineMe(2, 0, [&](const xAOD::sTgcMeasurement* pad,
547 // do not combine the strips with the pads that are not overlayed
548 const MuonGMR4::sTgcReadoutElement* readoutElement = pad ->readoutElement();
549 const MuonGMR4::PadDesign& padDesign = readoutElement->padDesign(pad->measurementHash());
550 double padHeight = padDesign.padHeight();
551 const Amg::Vector3D padCenter = pad->localMeasurementPos();
552
553 return std::abs(strip->localMeasurementPos().x() - padCenter.x()) < 0.5*padHeight;
554 });
555
556 //finally combine wire and pad measurements
557 combineMe(1, 2, [&](const xAOD::sTgcMeasurement* wire,
558 const xAOD::sTgcMeasurement* pad){
559 // do not combine the wires with the pads that are not overlayed
560 const MuonGMR4::sTgcReadoutElement* readoutElement = pad ->readoutElement();
561 const std::array<Amg::Vector2D, 4> localPadCorners = readoutElement->localPadCorners(pad->measurementHash());
562 auto [min,max] = std::ranges::minmax_element(localPadCorners.begin(), localPadCorners.end(),
563 [](const Amg::Vector2D& a, const Amg::Vector2D& b){
564 return a.y() < b.y();
565 });
566 return (wire->localMeasurementPos().y() > min->y() || wire->localMeasurementPos().y() < max->y());
567
568 });
569
570 //fill uncombined strip, wire and pad measurements that have not been used in combination
571 for(std::size_t collIdx = 0; collIdx < HitColls.size(); ++collIdx){
572 const auto& hits = HitColls[collIdx];
573 std::vector<const xAOD::sTgcMeasurement*> unusedHits{};
574 unusedHits.reserve(hits.size());
575
576 for(std::size_t idx = 0; idx < hits.size(); ++idx){
577 if((*instanceCounts[collIdx][idx]) == 0){
578 unusedHits.push_back(hits[idx]);
579 }
580 }
581 fillUncombinedSpacePoints(*gctx, sectorTrans, unusedHits, pointsInChamb.etaHits);
582 }
583 }
584 } while (viewer.next());
585 return StatusCode::SUCCESS;
586}
587
588
589StatusCode SpacePointMakerAlg::execute(const EventContext& ctx) const {
590 PreSortedSpacePointMap preSortedContainer{};
591 ATH_CHECK(loadContainerAndSort(ctx, m_mdtKey, preSortedContainer));
592 ATH_CHECK(loadContainerAndSort(ctx, m_rpcKey, preSortedContainer));
593 ATH_CHECK(loadContainerAndSort(ctx, m_tgcKey, preSortedContainer));
594 ATH_CHECK(loadContainerAndSort(ctx, m_mmKey, preSortedContainer));
595 ATH_CHECK(loadContainerAndSort(ctx, m_stgcKey, preSortedContainer));
596 std::unique_ptr<SpacePointContainer> outContainer = std::make_unique<SpacePointContainer>();
597
598 for (auto &[chamber, hitsPerChamber] : preSortedContainer){
599 ATH_MSG_DEBUG("Fill space points for chamber "<<chamber->identString() << " with "<<hitsPerChamber.etaHits.size()
600 <<" primary and "<<hitsPerChamber.phiHits.size()<<" phi space points.");
601 distributePointsAndStore(std::move(hitsPerChamber), *outContainer);
602 }
603 SG::WriteHandle writeHandle{m_writeKey, ctx};
604 ATH_CHECK(writeHandle.record(std::move(outContainer)));
605 return StatusCode::SUCCESS;
606}
607
609 SpacePointContainer& finalContainer) const {
610 SpacePointBucketVec splittedHits{};
611 splittedHits.emplace_back();
612 if (m_statCounter){
613 m_statCounter->addToStat(hitsPerChamber.etaHits);
614 m_statCounter->addToStat(hitsPerChamber.phiHits);
615
616 }
617 distributePrimaryPoints(std::move(hitsPerChamber.etaHits), splittedHits);
618 splittedHits.erase(std::remove_if(splittedHits.begin(), splittedHits.end(),
619 [](const SpacePointBucket& bucket) {
620 return bucket.size() <= 1;
621 }), splittedHits.end());
622 distributePhiPoints(std::move(hitsPerChamber.phiHits), splittedHits);
623
624 for (SpacePointBucket& bucket : splittedHits) {
625
626 std::ranges::sort(bucket, MuonR4::SpacePointPerLayerSorter{});
627
628 if (msgLvl(MSG::VERBOSE)){
629 std::stringstream spStr{};
630 for (const std::shared_ptr<SpacePoint>& sp : bucket){
631 spStr<<"SpacePoint: PrimaryMeas: " <<(*sp)<<std::endl;
632 }
633 ATH_MSG_VERBOSE("Created a bucket, printing all spacepoints..."<<std::endl<<spStr.str());
634 }
636 finalContainer.push_back(std::make_unique<SpacePointBucket>(std::move(bucket)));
637 }
638
639}
640void SpacePointMakerAlg::distributePhiPoints(std::vector<SpacePoint>&& spacePoints,
641 SpacePointBucketVec& splittedContainer) const{
642 for (SpacePoint& sp : spacePoints) {
643 auto phiPoint = std::make_shared<SpacePoint>(std::move(sp));
644 const double dY = std::sqrt(phiPoint->covariance()[Acts::toUnderlying(CovIdx::etaCov)]);
645 const double minY = phiPoint->localPosition().y() - dY;
646 const double maxY = phiPoint->localPosition().y() + dY;
647 for (SpacePointBucket& bucket : splittedContainer){
650 if (! (maxY < bucket.coveredMin() || bucket.coveredMax() < minY) ) {
651 bucket.emplace_back(phiPoint);
652 }
653 }
654 }
655}
657 const double firstSpPos,
658 const SpacePointBucketVec& sortedPoints) const {
660 const double spY = spacePoint.localPosition().y();
661 if (spY - firstSpPos > m_maxBucketLength){
662 return true;
663 }
664
665 if (sortedPoints.empty() || sortedPoints.back().empty()) {
666 return false;
667 }
668 return spY - sortedPoints.back().back()->localPosition().y() > m_spacePointWindow;
669}
671 SpacePointBucketVec& sortedPoints) const {
672 SpacePointBucket& newContainer = sortedPoints.emplace_back();
673 newContainer.setBucketId(sortedPoints.size() -1);
674
676 SpacePointBucket& overlap{sortedPoints[sortedPoints.size() - 2]};
677 overlap.setCoveredRange(overlap.front()->localPosition().y(),
678 overlap.back()->localPosition().y());
679
680 const double refBound = refSpacePoint.localPosition().y();
681
683 for (const std::shared_ptr<SpacePoint>& pointInBucket : overlap | std::views::reverse) {
684 const double overlapPos = pointInBucket->localPosition().y() +
685 std::sqrt(pointInBucket->covariance()[Acts::toUnderlying(CovIdx::etaCov)]);
686 if (refBound - overlapPos < m_spacePointOverlap) {
687 newContainer.insert(newContainer.begin(), pointInBucket);
688 } else {
689 break;
690 }
691 }
692
693}
694
695void SpacePointMakerAlg::distributePrimaryPoints(std::vector<SpacePoint>&& spacePoints,
696 SpacePointBucketVec& splittedHits) const {
697
698 if (spacePoints.empty()) return;
699
701 std::ranges::sort(spacePoints,
702 [] (const SpacePoint& a, const SpacePoint& b) {
703 return a.localPosition().y() < b.localPosition().y();
704 });
705
706 double firstPointPos = spacePoints.front().localPosition().y();
707
708 for (SpacePoint& toSort : spacePoints) {
709 ATH_MSG_VERBOSE("Add new primary space point "<<toSort);
710
711 if (splitBucket(toSort, firstPointPos, splittedHits)){
712 newBucket(toSort, splittedHits);
713 firstPointPos = splittedHits.back().empty() ? toSort.localPosition().y() : splittedHits.back().front()->localPosition().y();
714 ATH_MSG_VERBOSE("New bucket: id " << splittedHits.back().bucketId() << " Coverage: " << firstPointPos);
715 }
716 std::shared_ptr<SpacePoint> spacePoint = std::make_shared<SpacePoint>(std::move(toSort));
717 splittedHits.back().emplace_back(spacePoint);
718 }
719 SpacePointBucket& lastBucket{splittedHits.back()};
720 lastBucket.setCoveredRange(lastBucket.front()->localPosition().y(),
721 lastBucket.back()->localPosition().y());
722}
723
724}
const boost::regex re(r_e)
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#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
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.
void fillUncombinedSpacePoints(const ActsTrk::GeometryContext &gctx, const Amg::Transform3D &sectorTrans, const PrdVec_t< const PrdType * > &prdsToFill, std::vector< SpacePoint > &outColl) const
Transform the uncombined space prd measurements to space points.
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 setDirection(const Amg::Vector3D &sensorDir, const Amg::Vector3D &toNextSensor)
Setter for the direction of the measurement channel in the sector frame.
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.
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.
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.
bool next()
Loads the hits from the next chamber.
const MuonGMR4::sTgcReadoutElement * readoutElement() const override final
Retrieve the associated sTgcReadoutElement.
IdentifierHash measurementHash() const override final
Returns the hash of the measurement channel w.r.t ReadoutElement.
Amg::Vector3D localMeasurementPos() const override final
Returns the local measurement position as 3-vector.
#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.