ATLAS Offline Software
Loading...
Searching...
No Matches
SpacePointMakerAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
5
12#include <GaudiKernel/IMessageSvc.h>
13#include <memory>
14#include <type_traits>
21
22
23#include "Acts/Surfaces/detail/LineHelper.hpp"
24namespace {
25 inline std::vector<std::shared_ptr<unsigned>> matchCountVec(unsigned n) {
26 std::vector<std::shared_ptr<unsigned>> out{};
27 out.reserve(n);
28 for (unsigned p = 0; p < n ;++p) {
29 out.emplace_back(std::make_shared<unsigned>(0));
30 }
31 return out;
32 }
38 template<class MeasType>
39 Amg::Transform3D toChamberTransform(const ActsTrk::GeometryContext& gctx,
40 const Amg::Transform3D& sectorTrans,
41 const MeasType& meas) {
42 const MuonGMR4::MuonReadoutElement* reEle{meas.readoutElement()};
43 if constexpr(std::is_same_v<MeasType, xAOD::MdtDriftCircle>) {
44 return sectorTrans * reEle->localToGlobalTrans(gctx, meas.measurementHash());
45 } else {
46 return sectorTrans * reEle->localToGlobalTrans(gctx, meas.layerHash());
47 }
48 }
51 template <typename PrdType>
52 double sensorHalfLength(const PrdType& prd) {
53 const auto* re = prd.readoutElement();
54 if constexpr(std::is_same_v<PrdType, xAOD::MdtDriftCircle>) {
55 return 0.5 * re->activeTubeLength(prd.measurementHash());
56 } else if constexpr(std::is_same_v<PrdType, xAOD::RpcMeasurement>) {
57 return 0.5*(prd.measuresPhi() ? re->stripPhiLength() : re->stripEtaLength());
58 } else if constexpr(std::is_same_v<PrdType, xAOD::TgcStrip>) {
59 return 0.5 * re->sensorLayout(prd.layerHash())->design(prd.measuresPhi()).stripLength(prd.channelNumber());
60 } else if constexpr(std::is_same_v<PrdType, xAOD::MMCluster>) {
61 return 0.5* re->stripLayer(prd.layerHash()).design().stripLength(prd.channelNumber());
62 } else if constexpr(std::is_same_v<PrdType, xAOD::sTgcMeasurement>) {
63 return 0.5* re->stripLayer(prd.layerHash()).design().stripLength(prd.channelNumber());
64 }
65 return 0.;
66 }
67}
68
69namespace MuonR4 {
70
75 if (techIdx != other.techIdx) {
76 return static_cast<int>(techIdx) < static_cast<int>(other.techIdx);
77 }
78 if (stIdx != other.stIdx) {
79 return static_cast<int>(stIdx) < static_cast<int>(other.stIdx);
80 }
81 return eta < other.eta;
82}
88
89void SpacePointMakerAlg::SpacePointStatistics::addToStat(const std::vector<SpacePoint>& spacePoints){
90 std::lock_guard guard{m_mutex};
91 for (const SpacePoint& sp : spacePoints){
92 FieldKey key{};
93 key.stIdx = m_idHelperSvc->stationIndex(sp.identify());
94 key.techIdx = m_idHelperSvc->technologyIndex(sp.identify());
95 key.eta = m_idHelperSvc->stationEta(sp.identify());
96 StatField & stats = m_map[key];
97 if (sp.measuresEta() && sp.measuresPhi()) {
98 ++stats.measEtaPhi;
99 } else {
100 stats.measEta += sp.measuresEta();
101 stats.measPhi += sp.measuresPhi();
102 }
103 }
104}
106 using KeyVal = std::pair<FieldKey, StatField>;
107 std::vector<KeyVal> sortedstats{};
108 sortedstats.reserve(m_map.size());
110 for (const auto & [key, stats] : m_map){
111 sortedstats.emplace_back(std::make_pair(key, stats));
112 }
113 std::stable_sort(sortedstats.begin(), sortedstats.end(), [](const KeyVal& a, const KeyVal&b) {
114 return a.second.allHits() > b.second.allHits();
115 });
116 msg<<MSG::ALWAYS<<"###########################################################################"<<endmsg;
117 for (const auto & [key, stats] : sortedstats) {
118 msg<<MSG::ALWAYS<<" "<<Muon::MuonStationIndex::technologyName(key.techIdx)
119 <<" "<<Muon::MuonStationIndex::stName(key.stIdx)
120 <<" "<<std::abs(key.eta)<<(key.eta < 0 ? "A" : "C")
121 <<" "<<std::setw(8)<<stats.measEtaPhi
122 <<" "<<std::setw(8)<<stats.measEta
123 <<" "<<std::setw(8)<<stats.measPhi<<endmsg;
124 }
125 msg<<MSG::ALWAYS<<"###########################################################################"<<endmsg;
126
127}
128
132
133
135 if (m_statCounter) {
136 m_statCounter->dumpStatisics(msgStream());
137 }
138 return StatusCode::SUCCESS;
139}
141 ATH_CHECK(m_geoCtxKey.initialize());
142 ATH_CHECK(m_mdtKey.initialize(!m_mdtKey.empty()));
143 ATH_CHECK(m_rpcKey.initialize(!m_rpcKey.empty()));
144 ATH_CHECK(m_tgcKey.initialize(!m_tgcKey.empty()));
145 ATH_CHECK(m_mmKey.initialize(!m_mmKey.empty()));
146 ATH_CHECK(m_stgcKey.initialize(!m_stgcKey.empty()));
147 ATH_CHECK(m_idHelperSvc.retrieve());
148 ATH_CHECK(m_writeKey.initialize());
149 if (m_doStat) {
150 m_statCounter = std::make_unique<SpacePointStatistics>(m_idHelperSvc.get());
151 }
152 return StatusCode::SUCCESS;
153}
154
155template <>
157 const PrdVec_t<const xAOD::TgcStrip*>& phiHits) const {
158 if (etaHits.empty() || phiHits.empty()) {
159 return false;
160 }
161 const MuonGMR4::TgcReadoutElement* re = etaHits[0]->readoutElement();
162 ATH_MSG_VERBOSE("Collected "<<etaHits.size()<<"/"<<phiHits.size()<<" hits in "<<m_idHelperSvc->toStringGasGap(etaHits[0]->identify()));
163 return ((1.*etaHits.size()) / ((1.*re->numChannels(etaHits[0]->measurementHash())))) < m_maxOccTgcEta &&
164 ((1.*phiHits.size()) / ((1.*re->numChannels(phiHits[0]->measurementHash())))) < m_maxOccTgcPhi;
165 }
166template <>
168 const PrdVec_t<const xAOD::RpcMeasurement*>& phiHits) const {
169 if (etaHits.empty() || phiHits.empty()) {
170 return false;
171 }
172 const MuonGMR4::RpcReadoutElement* re = etaHits[0]->readoutElement();
173 ATH_MSG_VERBOSE("Collected "<<etaHits.size()<<"/"<<phiHits.size()<<" hits in "<<m_idHelperSvc->toStringGasGap(etaHits[0]->identify()));
174 return ((1.*etaHits.size()) / (1.*re->nEtaStrips())) < m_maxOccRpcEta &&
175 ((1.*phiHits.size()) / (1.*re->nPhiStrips())) < m_maxOccRpcPhi;
176 }
177
178template <>
180 const PrdVec_t<const xAOD::sTgcMeasurement*>& phiHits) const {
181 if (etaHits.empty() || phiHits.empty()) {
182 return false;
183 }
184 const MuonGMR4::sTgcReadoutElement* re = etaHits[0]->readoutElement();
185 return ((1.*etaHits.size()) / (1.*re->numChannels(etaHits[0]->measurementHash()))) < m_maxOccStgcEta &&
186 ((1.*phiHits.size()) / (1.*re->numChannels(phiHits[0]->measurementHash()))) < m_maxOccStgcPhi;
187 }
188template <>
190 const PrdVec_t<const xAOD::MMCluster*>& /*phiHits*/) const {
191 return false;
192 }
193template <typename PrdType>
195 const Amg::Transform3D& sectorTrans,
196 const PrdVec_t<PrdType*>& prdsToFill,
197 std::vector<SpacePoint>& outColl) const {
198 if (prdsToFill.empty()) {
199 return;
200 }
201 const PrdType* refMeas = prdsToFill.front();
202 bool allSpArePhi{false};
203
204 const Amg::Transform3D toSectorTrans = toChamberTransform(gctx, sectorTrans, *refMeas);
206 Amg::Vector3D sensorDir{Amg::Vector3D::Zero()}, toNextSen{Amg::Vector3D::Zero()};
208 if constexpr(std::is_same_v<PrdType, xAOD::RpcMeasurement> ||
209 std::is_same_v<PrdType, xAOD::TgcStrip>) {
210 allSpArePhi = refMeas->measuresPhi();
211 const auto& stripLayout = refMeas->readoutElement()->sensorLayout(refMeas->layerHash());
212 const auto& design = stripLayout->design(allSpArePhi);
213 sensorDir = toSectorTrans.rotation() * stripLayout->to3D(design.stripDir(), allSpArePhi);
214 toNextSen = toSectorTrans.rotation() * stripLayout->to3D(design.stripNormal(), allSpArePhi);
215 } else {
216 sensorDir = toSectorTrans.rotation().col(Amg::y);
217 toNextSen = toSectorTrans.rotation().col(Amg::x);
218 }
219 outColl.reserve(outColl.size() + prdsToFill.size());
220 for (const PrdType* prd: prdsToFill) {
221 SpacePoint& newSp = outColl.emplace_back(prd);
222 if constexpr (std::is_same_v<PrdType, xAOD::TgcStrip>) {
223 if (allSpArePhi) {
224 const auto& stripLayout = refMeas->readoutElement()->sensorLayout(refMeas->layerHash());
225 const auto& radialDesign = static_cast<const MuonGMR4::RadialStripDesign&>(stripLayout->design(allSpArePhi));
226 toNextSen = toSectorTrans.rotation() * stripLayout->to3D(radialDesign.stripNormal(prd->channelNumber()), allSpArePhi);
227 sensorDir = toSectorTrans.rotation() * stripLayout->to3D(radialDesign.stripDir(prd->channelNumber()), allSpArePhi);
228 }
229 }
230 newSp.setPosition(toSectorTrans * prd->localMeasurementPos());
231 newSp.setDirection(sensorDir, toNextSen);
232 auto cov = Acts::filledArray<double,3>(0.);
233 if (prd->numDimensions() == 2) {
234 if constexpr(std::is_same_v<PrdType, xAOD::RpcMeasurement>) {
235 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->template localCovariance<2>()(0,0);
236 cov[Acts::toUnderlying(CovIdx::phiCov)] = prd->template localCovariance<2>()(1,1);
237 } else if constexpr(std::is_same_v<PrdType, xAOD::sTgcMeasurement>) {
238 cov[Acts::toUnderlying(CovIdx::phiCov)] = prd->template localCovariance<2>()(0,0);
239 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->template localCovariance<2>()(1,1);
240 } else {
241 ATH_MSG_WARNING("Unsupported measurement type. "<<typeid(PrdType).name());
242 // Prevent division by zero later on.
243 cov[Acts::toUnderlying(CovIdx::phiCov)] = 1;
244 cov[Acts::toUnderlying(CovIdx::etaCov)] = 1;
245 }
246 } else {
248 if (newSp.measuresEta()) {
249 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->template localCovariance<1>()[0];
250 cov[Acts::toUnderlying(CovIdx::phiCov)] = Acts::square(sensorHalfLength(*prd));
251 } else {
252 cov[Acts::toUnderlying(CovIdx::phiCov)] = prd->template localCovariance<1>()[0];
253 cov[Acts::toUnderlying(CovIdx::etaCov)] = Acts::square(sensorHalfLength(*prd));
254 }
255 }
256 newSp.setCovariance(std::move(cov));
257 }
258}
259
260
261template <typename ContType>
264 std::vector<EtaPhi2DHits<typename ContType::const_value_type>> hitsPerGasGap{};
265 for (const auto& prd : viewer) {
266 ATH_MSG_VERBOSE("Create space point from "<<m_idHelperSvc->toString(prd->identify())
267 <<", hash: "<<prd->identifierHash());
268
269 unsigned gapIdx = prd->gasGap() -1;
270 if constexpr (std::is_same_v<ContType, xAOD::RpcMeasurementContainer>) {
271 gapIdx = prd->readoutElement()->createHash(0, prd->gasGap(), prd->doubletPhi(), false);
272 }
273 if (hitsPerGasGap.size() <= gapIdx) {
274 hitsPerGasGap.resize(gapIdx + 1);
275 }
276 bool measPhi{false};
277 if constexpr(std::is_same_v<ContType, xAOD::sTgcMeasContainer>) {
279 measPhi = prd->channelType() == sTgcIdHelper::sTgcChannelTypes::Wire;
280 } else if constexpr(!std::is_same_v<ContType, xAOD::MMClusterContainer>) {
282 measPhi = prd->measuresPhi();
283 }
284
285 if (prd->numDimensions() == 2) {
286 hitsPerGasGap[gapIdx][2].push_back(prd);
287 continue;
288 }
290 auto& toPush = hitsPerGasGap[gapIdx][measPhi];
291 if (toPush.capacity() == toPush.size()) {
292 toPush.reserve(toPush.size() + m_capacityBucket);
293 }
294 toPush.push_back(prd);
295 }
296 return hitsPerGasGap;
297}
298
299template <typename ContType>
300 StatusCode SpacePointMakerAlg::loadContainerAndSort(const EventContext& ctx,
302 PreSortedSpacePointMap& fillContainer) const {
303 const ContType* measurementCont{nullptr};
304 ATH_CHECK(SG::get(measurementCont, key, ctx));
305 if (!measurementCont || measurementCont->empty()){
306 ATH_MSG_DEBUG("nothing to do");
307 return StatusCode::SUCCESS;
308 }
309 const ActsTrk::GeometryContext* gctx{nullptr};
310 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
311
312 xAOD::ChamberViewer viewer{*measurementCont};
313
314 do {
315 SpacePointsPerChamber& pointsInChamb = fillContainer[viewer.at(0)->readoutElement()->msSector()];
316 const Amg::Transform3D sectorTrans = viewer.at(0)->readoutElement()->msSector()->globalToLocalTrans(*gctx);
317 ATH_MSG_DEBUG("Fill space points for chamber "<<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
318 if constexpr( std::is_same_v<ContType, xAOD::MdtDriftCircleContainer>) {
319 pointsInChamb.etaHits.reserve(pointsInChamb.etaHits.capacity() + viewer.size());
320 for (const auto& prd : viewer) {
321 Amg::Transform3D toChamberTrans{toChamberTransform(*gctx, sectorTrans, *prd)};
322 SpacePoint& sp{pointsInChamb.etaHits.emplace_back(prd)};
323 sp.setPosition(toChamberTrans*prd->localMeasurementPos());
324 sp.setDirection(toChamberTrans.rotation().col(Amg::z),
325 toChamberTrans.rotation().col(Amg::y));
326 std::array<double, 3> cov{Acts::filledArray<double,3>(0.)};
327 cov[Acts::toUnderlying(CovIdx::etaCov)] = prd->driftRadiusCov();
328 cov[Acts::toUnderlying(CovIdx::phiCov)] = Acts::square(sensorHalfLength(*prd));
329 if (ATH_UNLIKELY(prd->numDimensions() == 2)){
330 cov[Acts::toUnderlying(CovIdx::phiCov)] = static_cast<const xAOD::MdtTwinDriftCircle*>(prd)->posAlongWireCov();
331 }
332 sp.setCovariance(std::move(cov));
333 }
334 } else {
336 for (auto& [etaHits, phiHits, two2DHits] : splitHitsPerGasGap(viewer)) {
337 ATH_MSG_DEBUG("Found "<<etaHits.size()<<"/"<<phiHits.size()
338 <<" 1D and "<<two2DHits.size()<<" 2D hits in chamber "
339 <<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
341 fillUncombinedSpacePoints(*gctx, sectorTrans, two2DHits, pointsInChamb.etaHits);
343 // Check if we do not have 2D occupancy (missing phi or eta hits)
344 if (!passOccupancy2D(etaHits, phiHits)) {
345 fillUncombinedSpacePoints(*gctx, sectorTrans, etaHits, pointsInChamb.etaHits);
346 fillUncombinedSpacePoints(*gctx, sectorTrans, phiHits, pointsInChamb.phiHits);
347 continue;
348 }
349
350 std::vector<std::shared_ptr<unsigned>> etaCounts{matchCountVec(etaHits.size())},
351 phiCounts{matchCountVec(phiHits.size())};
352
353 pointsInChamb.etaHits.reserve(pointsInChamb.etaHits.size() + etaHits.size()*phiHits.size());
355 const auto& firstEta{etaHits.front()};
356 const Amg::Transform3D toSectorTransEta = toChamberTransform(*gctx, sectorTrans, *firstEta);
357
358 Amg::Vector3D toNextDir{Amg::Vector3D::Zero()}, sensorDir{Amg::Vector3D::Zero()};
359 if constexpr (std::is_same_v<xAOD::RpcMeasurementContainer, ContType> ||
360 std::is_same_v<xAOD::TgcStripContainer, ContType>) {
361 const auto& stripLayout = firstEta->readoutElement()->sensorLayout(firstEta->layerHash());
362 const auto& design = stripLayout->design();
363 sensorDir = toSectorTransEta.rotation() * stripLayout->to3D(design.stripDir(), false);
364 toNextDir = toSectorTransEta.rotation() * stripLayout->to3D(design.stripNormal(), false);
365 } else {
366 toNextDir = toSectorTransEta.rotation().col(Amg::x);
367 sensorDir = toSectorTransEta.rotation().col(Amg::y);
368 }
369
370 using namespace Acts::detail::LineHelper;
371 for (unsigned etaP = 0; etaP < etaHits.size(); ++etaP) {
373 for (unsigned phiP = 0; phiP < phiHits.size(); ++ phiP) {
375 if constexpr(std::is_same_v<xAOD::TgcStripContainer, ContType>) {
376 if (!(etaHits[etaP]->bcBitMap() & phiHits[phiP]->bcBitMap())){
377 continue;
378 }
379 const auto& stripLay = phiHits[phiP]->readoutElement()->sensorLayout(phiHits[phiP]->layerHash());
380 const auto& radialDesign = static_cast<const MuonGMR4::RadialStripDesign&>(stripLay->design(true));
381 toNextDir = toSectorTransEta.rotation() * stripLay->to3D(radialDesign.stripDir(phiHits[phiP]->channelNumber()), true);
382 }
383
384 SpacePoint& newSp = pointsInChamb.etaHits.emplace_back(etaHits[etaP], phiHits[phiP]);
385 newSp.setInstanceCounts(etaCounts[etaP], phiCounts[phiP]);
386
387 auto spIsect = lineIntersect(toSectorTransEta*etaHits[etaP]->localMeasurementPos(), sensorDir,
388 toSectorTransEta*phiHits[phiP]->localMeasurementPos(), toNextDir);
389 newSp.setPosition(spIsect.position());
390 newSp.setDirection(sensorDir, toNextDir);
391 auto cov = Acts::filledArray<double, 3>(0.);
392 cov[Acts::toUnderlying(CovIdx::etaCov)] = etaHits[etaP]->template localCovariance<1>()[0];
393 cov[Acts::toUnderlying(CovIdx::phiCov)] = phiHits[phiP]->template localCovariance<1>()[0];
394 newSp.setCovariance(std::move(cov));
395 ATH_MSG_VERBOSE("Created new space point "<<newSp);
396 }
397 }
398 }
399 }
400 } while (viewer.next());
401 return StatusCode::SUCCESS;
402}
403
404template<>
405StatusCode SpacePointMakerAlg::loadContainerAndSort(const EventContext& ctx,
407 PreSortedSpacePointMap& fillContainer) const {
408
409 const xAOD::sTgcMeasContainer* measurementCont{nullptr};
410 ATH_CHECK(SG::get(measurementCont, key, ctx));
411 if (!measurementCont || measurementCont->empty()){
412 ATH_MSG_DEBUG("nothing to do");
413 return StatusCode::SUCCESS;
414 }
415 const ActsTrk::GeometryContext* gctx{nullptr};
416 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
417 xAOD::ChamberViewer viewer{*measurementCont};
418 using namespace Acts::detail::LineHelper;
419 do {
420 SpacePointsPerChamber& pointsInChamb = fillContainer[viewer.at(0)->readoutElement()->msSector()];
421 const Amg::Transform3D sectorTrans = viewer.at(0)->readoutElement()->msSector()->globalToLocalTrans(*gctx);
422 ATH_MSG_DEBUG("Fill space points for chamber "<<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
423 for(auto& HitColls: splitHitsPerGasGap(viewer)){
424 auto& [etaHits, phiHits, two2DHits] = HitColls;
425 ATH_MSG_DEBUG("Found "<<etaHits.size()<<"/"<<phiHits.size()<<" 1D and "<<two2DHits.size()<<" 2D hits in chamber "<<m_idHelperSvc->toStringDetEl(viewer.at(0)->identify()));
426
427 std::array<std::vector<std::shared_ptr<unsigned>>, 3> instanceCounts{matchCountVec(etaHits.size()),
428 matchCountVec(phiHits.size()),
429 matchCountVec(two2DHits.size())};
430 //loop through the Prds and try to combine according` to the hierarchy
431 // Strip+Wire
432 // Strip+Pad
433 // Wire+Pad
434 // 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
456 for(std::size_t idxA = 0; idxA < collA.size(); idxA++){
457 if(!combinedFlagsA[idxA]){
458 continue;
459 }
460 for(std::size_t idxB = 0; idxB < collB.size(); idxB++){
461 if(!combinedFlagsB[idxB] || !combFunc(collA[idxA], collB[idxB])){
462 continue;
463 }
464 //create space point
465 ATH_MSG_VERBOSE("Creating combined sTgc space point from "<<m_idHelperSvc->toString(collA[idxA]->identify())
466 <<" and "<<m_idHelperSvc->toString(collB[idxB]->identify()));
467 SpacePoint& newSp = pointsInChamb.etaHits.emplace_back(collB[idxB], collA[idxA]);
468 auto crossPoint = lineIntersect<3>(collA[idxA]->localMeasurementPos(),
469 Amg::Vector3D::UnitX(),
470 collB[idxB]->localMeasurementPos(),
471 Amg::Vector3D::UnitY());
472 newSp.setPosition(crossPoint.position());
473 newSp.setDirection(Amg::Vector3D::UnitX(), Amg::Vector3D::UnitY());
474 auto cov = Acts::filledArray<double, 3>(0.);
475 cov[Acts::toUnderlying(CovIdx::phiCov)] = collA[idxA]->template localCovariance<1>()[0];
476 cov[Acts::toUnderlying(CovIdx::etaCov)] = collB[idxB]->template localCovariance<1>()[0];
477 newSp.setCovariance(std::move(cov));
478 newSp.setInstanceCounts(instanceCounts[collIdxB][idxB], instanceCounts[collIdxA][idxA]);
479 ATH_MSG_VERBOSE("Created new space point "<<newSp);
480 }
481 }
482 };
483
484 //try to combine strip with wire measurements first
485 combineMe(1, 0, [&](const xAOD::sTgcMeasurement* wire,
487 // do not combine the strips with the wire that are in the etaZero region
488 auto readoutElement = strip->readoutElement();
489 Amg::Vector2D localPos2D = strip->localMeasurementPos().block<2,1>(0,0);
490 if(readoutElement->isEtaZero(strip->measurementHash(), localPos2D)){
491 return false;
492 }
493 //ignore combinations where the wire and the strip are not crossing
494 //check if the projection of the crossing point is within the bounds of the layer
495 auto crossPoint = strip->localMeasurementPos() + wire->localMeasurementPos();
496 const Acts::Surface& surf = readoutElement->surface(strip->layerHash());
497 return surf.insideBounds(crossPoint.block<2,1>(0,0));
498 });
499
500 //combine strip and pad measurements
501 combineMe(2, 0, [&](const xAOD::sTgcMeasurement* pad,
503 // do not combine the strips with the pads that are not overlayed
504 auto readoutElement = pad ->readoutElement();
505 const MuonGMR4::PadDesign& padDesign = readoutElement->padDesign(pad->measurementHash());
506 double padHeight = padDesign.padHeight();
507 const Amg::Vector3D& padCenter = pad->localMeasurementPos();
508
509 return std::abs(strip->localMeasurementPos().x() - padCenter.x()) < 0.5*padHeight;
510 });
511
512 //finally combine wire and pad measurements
513 combineMe(1, 2, [&](const xAOD::sTgcMeasurement* wire,
514 const xAOD::sTgcMeasurement* pad){
515 // do not combine the wires with the pads that are not overlayed
516 auto readoutElement = pad ->readoutElement();
517 std::array<Amg::Vector2D, 4> localPadCorners = readoutElement->localPadCorners(pad->measurementHash());
518 auto [min,max] = std::ranges::minmax_element(localPadCorners.begin(), localPadCorners.end(),
519 [](const Amg::Vector2D& a, const Amg::Vector2D& b){
520 return a.y() < b.y();
521 });
522 return (wire->localMeasurementPos().y() > min->y() || wire->localMeasurementPos().y() < max->y());
523
524 });
525
526 //fill uncombined strip, wire and pad measurements that have not been used in combination
527 for(std::size_t collIdx = 0; collIdx < HitColls.size(); collIdx++){
528 const auto& hits = HitColls[collIdx];
529 std::vector<const xAOD::sTgcMeasurement*> unusedHits{};
530 unusedHits.reserve(hits.size());
531
532 for(std::size_t idx = 0; idx < hits.size(); idx++){
533 if(*(instanceCounts[collIdx][idx]) == 0){
534 unusedHits.push_back(hits[idx]);
535 }
536 }
537
538 fillUncombinedSpacePoints(*gctx, sectorTrans, unusedHits, pointsInChamb.etaHits);
539 }
540 }
541 } while (viewer.next());
542 return StatusCode::SUCCESS;
543}
544
545
546StatusCode SpacePointMakerAlg::execute(const EventContext& ctx) const {
547 PreSortedSpacePointMap preSortedContainer{};
548 ATH_CHECK(loadContainerAndSort(ctx, m_mdtKey, preSortedContainer));
549 ATH_CHECK(loadContainerAndSort(ctx, m_rpcKey, preSortedContainer));
550 ATH_CHECK(loadContainerAndSort(ctx, m_tgcKey, preSortedContainer));
551 ATH_CHECK(loadContainerAndSort(ctx, m_mmKey, preSortedContainer));
552 ATH_CHECK(loadContainerAndSort(ctx, m_stgcKey, preSortedContainer));
553 std::unique_ptr<SpacePointContainer> outContainer = std::make_unique<SpacePointContainer>();
554
555 for (auto &[chamber, hitsPerChamber] : preSortedContainer){
556 ATH_MSG_DEBUG("Fill space points for chamber "<<chamber->identString() << " with "<<hitsPerChamber.etaHits.size()
557 <<" primary and "<<hitsPerChamber.phiHits.size()<<" phi space points.");
558 distributePointsAndStore(std::move(hitsPerChamber), *outContainer);
559 }
560 SG::WriteHandle writeHandle{m_writeKey, ctx};
561 ATH_CHECK(writeHandle.record(std::move(outContainer)));
562 return StatusCode::SUCCESS;
563}
564
566 SpacePointContainer& finalContainer) const {
567 SpacePointBucketVec splittedHits{};
568 splittedHits.emplace_back();
569 if (m_statCounter){
570 m_statCounter->addToStat(hitsPerChamber.etaHits);
571 m_statCounter->addToStat(hitsPerChamber.phiHits);
572
573 }
574 distributePrimaryPoints(std::move(hitsPerChamber.etaHits), splittedHits);
575 splittedHits.erase(std::remove_if(splittedHits.begin(), splittedHits.end(),
576 [](const SpacePointBucket& bucket) {
577 return bucket.size() <= 1;
578 }), splittedHits.end());
579 distributePhiPoints(std::move(hitsPerChamber.phiHits), splittedHits);
580
581 for (SpacePointBucket& bucket : splittedHits) {
582
583 std::ranges::sort(bucket, MuonR4::SpacePointPerLayerSorter{});
584
585 if (msgLvl(MSG::VERBOSE)){
586 std::stringstream spStr{};
587 for (const std::shared_ptr<SpacePoint>& sp : bucket){
588 spStr<<"SpacePoint: PrimaryMeas: " <<(*sp)<<std::endl;
589 }
590 ATH_MSG_VERBOSE("Created a bucket, printing all spacepoints..."<<std::endl<<spStr.str());
591 }
593 finalContainer.push_back(std::make_unique<SpacePointBucket>(std::move(bucket)));
594 }
595
596}
597void SpacePointMakerAlg::distributePhiPoints(std::vector<SpacePoint>&& spacePoints,
598 SpacePointBucketVec& splittedContainer) const{
599 for (SpacePoint& sp : spacePoints) {
600 auto phiPoint = std::make_shared<SpacePoint>(std::move(sp));
601 const double dY = std::sqrt(phiPoint->covariance()[Acts::toUnderlying(CovIdx::etaCov)]);
602 const double minY = phiPoint->localPosition().y() - dY;
603 const double maxY = phiPoint->localPosition().y() + dY;
604 for (SpacePointBucket& bucket : splittedContainer){
607 if (! (maxY < bucket.coveredMin() || bucket.coveredMax() < minY) ) {
608 bucket.emplace_back(phiPoint);
609 }
610 }
611 }
612}
614 const double firstSpPos,
615 const SpacePointBucketVec& sortedPoints) const {
617 const double spY = spacePoint.localPosition().y();
618 if (spY - firstSpPos > m_maxBucketLength){
619 return true;
620 }
621
622 if (sortedPoints.empty() || sortedPoints.back().empty()) {
623 return false;
624 }
625 return spY - sortedPoints.back().back()->localPosition().y() > m_spacePointWindow;
626}
628 SpacePointBucketVec& sortedPoints) const {
629 SpacePointBucket& newContainer = sortedPoints.emplace_back();
630 newContainer.setBucketId(sortedPoints.size() -1);
631
633 SpacePointBucket& overlap{sortedPoints[sortedPoints.size() - 2]};
634 overlap.setCoveredRange(overlap.front()->localPosition().y(),
635 overlap.back()->localPosition().y());
636
637 const double refBound = refSpacePoint.localPosition().y();
638
640 for (const std::shared_ptr<SpacePoint>& pointInBucket : overlap | std::views::reverse) {
641 const double overlapPos = pointInBucket->localPosition().y() +
642 std::sqrt(pointInBucket->covariance()[Acts::toUnderlying(CovIdx::etaCov)]);
643 if (refBound - overlapPos < m_spacePointOverlap) {
644 newContainer.insert(newContainer.begin(), pointInBucket);
645 } else {
646 break;
647 }
648 }
649
650}
651
652void SpacePointMakerAlg::distributePrimaryPoints(std::vector<SpacePoint>&& spacePoints,
653 SpacePointBucketVec& splittedHits) const {
654
655 if (spacePoints.empty()) return;
656
658 std::ranges::sort(spacePoints,
659 [] (const SpacePoint& a, const SpacePoint& b) {
660 return a.localPosition().y() < b.localPosition().y();
661 });
662
663 double firstPointPos = spacePoints.front().localPosition().y();
664
665 for (SpacePoint& toSort : spacePoints) {
666 ATH_MSG_VERBOSE("Add new primary space point "<<toSort);
667
668 if (splitBucket(toSort, firstPointPos, splittedHits)){
669 newBucket(toSort, splittedHits);
670 firstPointPos = splittedHits.back().empty() ? toSort.localPosition().y() : splittedHits.back().front()->localPosition().y();
671 ATH_MSG_VERBOSE("New bucket: id " << splittedHits.back().bucketId() << " Coverage: " << firstPointPos);
672 }
673 std::shared_ptr<SpacePoint> spacePoint = std::make_shared<SpacePoint>(std::move(toSort));
674 splittedHits.back().emplace_back(spacePoint);
675 }
676 SpacePointBucket& lastBucket{splittedHits.back()};
677 lastBucket.setCoveredRange(lastBucket.front()->localPosition().y(),
678 lastBucket.back()->localPosition().y());
679}
680
681}
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_WARNING(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.
The MuonReadoutElement is an abstract class representing the geometry representing the muon detector.
const Amg::Transform3D & localToGlobalTrans(const ActsTrk::GeometryContext &ctx) const
Returns the local to global transformation into the ATLAS coordinate system.
double padHeight() const
Returns the height of all the pads that are not adjacent to the bottom edge of the trapezoid active a...
localCornerArray localPadCorners(const IdentifierHash &measHash) 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_maxOccStgcPhi
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
Fills all space points that are beloni.
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.
Gaudi::Property< double > m_maxOccStgcEta
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.
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.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
This header ties the generic definitions in this package.
SpacePoint::CovIdx CovIdx
########################################## SpacePointMakerAlg #######################################...
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 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.