ATLAS Offline Software
NswSegmentFinderAlg.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 #include "NswSegmentFinderAlg.h"
6 
11 
12 
14 
17 
19 
20 #include "Acts/Seeding/CombinatorialSeedSolver.hpp"
21 
22 #include <ranges>
23 #include <format>
24 
25 using namespace Acts::Experimental::CombinatorialSeedSolver;
26 namespace {
27  inline const MuonGMR4::StripDesign& getDesign(const MuonR4::SpacePoint& sp) {
29  const auto* prd = static_cast<const xAOD::MMCluster*>(sp.primaryMeasurement());
30  return prd->readoutElement()->stripLayer(prd->measurementHash()).design();
31  } else if (sp.type() == xAOD::UncalibMeasType::sTgcStripType) {
32  const auto* prd = static_cast<const xAOD::sTgcMeasurement*>(sp.primaryMeasurement());
33  const auto* re = prd->readoutElement();
34  switch(prd->channelType()) {
36  return re->stripDesign(prd->measurementHash());
37  case sTgcIdHelper::Wire:
38  return re->wireDesign(prd->measurementHash());
39  case sTgcIdHelper::Pad:
40  return re->padDesign(prd->measurementHash());
41  }
42  }
43  THROW_EXCEPTION("Invalid space point for design retrieval "<<sp.msSector()->idHelperSvc()->toString(sp.identify()));
44  }
45  inline double stripHalfLength(const MuonR4::SpacePoint& sp) {
46  const auto& design = getDesign(sp);
48  const auto* prd = static_cast<const xAOD::MMCluster*>(sp.primaryMeasurement());
49  return 0.5* design.stripLength(prd->channelNumber());
50  }
51  return 0.;
52  }
53 }
54 
55 namespace MuonR4 {
56 
57 using namespace SegmentFit;
58 constexpr unsigned int minLayers{4};
60 
62  ATH_CHECK(m_geoCtxKey.initialize());
63  ATH_CHECK(m_etaKey.initialize());
64  ATH_CHECK(m_writeSegmentKey.initialize());
65  ATH_CHECK(m_writeSegmentSeedKey.initialize());
66  ATH_CHECK(m_idHelperSvc.retrieve());
67  ATH_CHECK(m_calibTool.retrieve());
68  ATH_CHECK(m_visionTool.retrieve(DisableTool{m_visionTool.empty()}));
69 
70  if (!(m_idHelperSvc->hasMM() || m_idHelperSvc->hasSTGC())) {
71  ATH_MSG_ERROR("MM or STGC not part of initialized detector layout");
72  return StatusCode::FAILURE;
73  }
74 
76  fitCfg.calibrator = m_calibTool.get();
77  fitCfg.visionTool = m_visionTool.get();
78  fitCfg.idHelperSvc = m_idHelperSvc.get();
79 
80  m_lineFitter = std::make_unique<SegmentFit::SegmentLineFitter>(name(), std::move(fitCfg));
81 
82  if(m_dumpSeedStatistics){
83 
84  m_seedCounter = std::make_unique<SeedStatistics>();
85  }
86 
87  return StatusCode::SUCCESS;
88 }
89 
91  NswSegmentFinderAlg::emptyBookKeeper(const HitLayVec& sortedSp) const{
92  UsedHitMarker_t emptyKeeper(sortedSp.size());
93  for (std::size_t l = 0; l < sortedSp.size(); ++l) {
94  emptyKeeper[l].resize(sortedSp[l].size(), 0);
95  }
96  return emptyKeeper;
97 }
98 
100  NswSegmentFinderAlg::classifyStrip(const SpacePoint& sp) const{
101 
103  const auto& design = getDesign(sp);
104  if (!design.hasStereoAngle()) {
105  return StripOrient::X;
106  }
107  return design.stereoAngle() > 0. ? StripOrient::U : StripOrient::V;
108  } else if (sp.type() == xAOD::UncalibMeasType::sTgcStripType) {
109  const auto* prd = static_cast<const xAOD::sTgcMeasurement*>(sp.primaryMeasurement());
110  if (sp.dimension() == 2) {
111  return StripOrient::C;
112  }
113  return prd->channelType() == sTgcIdHelper::Strip ? StripOrient::X : StripOrient::P;
114 
115  }
116  ATH_MSG_WARNING("Cannot classify orientation of "<<m_idHelperSvc->toString(sp.identify()));
117  return StripOrient::Unknown;
118 }
120  NswSegmentFinderAlg::hitFromIPCorridor(const SpacePoint& testHit,
121  const Amg::Vector3D& beamSpotPos,
122  const Amg::Vector3D& dirEstUp,
123  const Amg::Vector3D& dirEstDn) const{
124 
125  const Amg::Vector3D estPlaneArrivalUp = SeedingAux::extrapolateToPlane(beamSpotPos, dirEstUp, testHit);
126  const Amg::Vector3D estPlaneArrivalDn = SeedingAux::extrapolateToPlane(beamSpotPos, dirEstDn, testHit);
127 
128  bool below{true}, above{true};
129  switch (classifyStrip(testHit)) {
130  using enum StripOrient;
131  case U:
132  case V:{
133  const double halfLength = 0.5* stripHalfLength(testHit);
135  const Amg::Vector3D leftEdge = testHit.localPosition() - halfLength * testHit.sensorDirection();
136  const Amg::Vector3D rightEdge = testHit.localPosition() + halfLength * testHit.sensorDirection();
137 
139  below = estPlaneArrivalDn.y() > std::max(leftEdge.y(), rightEdge.y());
141  above = estPlaneArrivalUp.y() < std::min(leftEdge.y(), rightEdge.y());
142  break;
143  } case X:
144  case C: {
146  const double hY = testHit.localPosition().y();
147  below = estPlaneArrivalDn.y() > hY;
149  above = estPlaneArrivalUp.y() < hY;
150  break;
151  }
152  case P:{
153  break;
154  }
155  case Unknown:{
156  break;
157  }
158 
159  }
160  ATH_MSG_VERBOSE("Hit " << m_idHelperSvc->toString(testHit.identify())
161  << (below || above ? " is outside the window" : " is inside the window"));
162  if(below) {
163  return HitWindow::tooLow;
164  }
165  if(above) {
166  return HitWindow::tooHigh;
167  }
168  return HitWindow::inside;
169 };
170 
172 #define TEST_HIT_CORRIDOR(LAYER, HIT_ITER, START_LAYER) \
173 { \
174  const SpacePoint* testMe = combinatoricLayers[LAYER].get()[HIT_ITER]; \
175  if (usedHits[LAYER].get()[HIT_ITER] > m_maxUsed) { \
176  ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - " \
177  <<m_idHelperSvc->toString(testMe->identify()) \
178  <<" already used in good seed." ); \
179  continue; \
180  } \
181  const HitWindow inWindow = hitFromIPCorridor(*testMe, beamSpot, dirEstUp, dirEstDn); \
182  if(inWindow == HitWindow::tooHigh) { \
183  ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - Hit " \
184  <<m_idHelperSvc->toString(testMe->identify()) \
185  <<" is beyond the corridor. Break loop"); \
186  break; \
187  } else if (inWindow == HitWindow::tooLow) { \
188  START_LAYER = HIT_ITER + 1; \
189  ATH_MSG_VERBOSE(__func__<<":"<<__LINE__<<" - Hit " \
190  <<m_idHelperSvc->toString(testMe->identify()) \
191  <<" is still below the corridor. Update start to " \
192  <<START_LAYER); \
193  continue; \
194  } \
195 }
196 
197 void NswSegmentFinderAlg::constructPrelimnarySeeds(const Amg::Vector3D& beamSpot,
198  const HitLaySpan_t& combinatoricLayers,
199  const UsedHitSpan_t& usedHits,
200  InitialSeedVec_t& seedHitsFromLayers) const {
202  seedHitsFromLayers.clear();
203  std::size_t maxSize{1};
204  for (const HitVec& hitVec : combinatoricLayers) {
205  maxSize = maxSize * hitVec.size();
206  }
207  seedHitsFromLayers.reserve(maxSize);
208 
209 
210  unsigned int iterLay0{0}, iterLay1{0}, iterLay2{0}, iterLay3{0};
211  unsigned int startLay1{0}, startLay2{0}, startLay3{0};
212 
213  for( ; iterLay0 < combinatoricLayers[0].get().size() ; ++iterLay0){
215  if (usedHits[0].get()[iterLay0] > m_maxUsed) {
216  continue;
217  }
218  const SpacePoint* hit0 = combinatoricLayers[0].get()[iterLay0];
220  const Amg::Vector3D initSeedDir{(beamSpot - hit0->localPosition()).unit()};
221  const Amg::Vector3D dirEstUp = Amg::dirFromAngles(initSeedDir.phi(), initSeedDir.theta() - m_windowTheta);
222  const Amg::Vector3D dirEstDn = Amg::dirFromAngles(initSeedDir.phi(), initSeedDir.theta() + m_windowTheta);
223 
224  ATH_MSG_VERBOSE("Reference hit: "<<m_idHelperSvc->toString(hit0->identify())
225  <<", position: "<<Amg::toString(hit0->localPosition())
226  <<", seed dir: "<<Amg::toString(initSeedDir)
227  <<", seed plane: "<<Amg::toString(SeedingAux::extrapolateToPlane(beamSpot, initSeedDir, *hit0)));
229  for( iterLay1 = startLay1; iterLay1 < combinatoricLayers[1].get().size() ; ++iterLay1){
230  TEST_HIT_CORRIDOR(1, iterLay1, startLay1);
231  for( iterLay2 = startLay2; iterLay2 < combinatoricLayers[2].get().size() ; ++iterLay2){
232  TEST_HIT_CORRIDOR(2, iterLay2, startLay2);
233  for( iterLay3 = startLay3; iterLay3 < combinatoricLayers[3].get().size(); ++iterLay3){
234  TEST_HIT_CORRIDOR(3, iterLay3, startLay3);
235  seedHitsFromLayers.emplace_back(std::array{hit0, combinatoricLayers[1].get()[iterLay1],
236  combinatoricLayers[2].get()[iterLay2],
237  combinatoricLayers[3].get()[iterLay3]});
238  }
239  }
240  }
241  }
242 }
243 #undef TEST_HIT_CORRIDOR
244 
246  NswSegmentFinderAlg::extendHits(const Amg::Vector3D& startPos,
247  const Amg::Vector3D& direction,
248  const HitLaySpan_t& extensionLayers,
249  const UsedHitSpan_t& usedHits) const {
250 
251  //the hits we need to return to extend the segment seed
252  HitVec combinatoricHits;
253 
254  //the stripHitsLayers are already the unused ones - only use for the extension
255  for (std::size_t i = 0; i < extensionLayers.size(); ++i) {
256  const HitVec& layer{extensionLayers[i].get()};
257  const Amg::Vector3D extrapPos = SeedingAux::extrapolateToPlane(startPos, direction, *layer.front());
258 
259  unsigned int indexOfHit = layer.size() + 1;
260  unsigned int triedHit{0};
261  double minPull{std::numeric_limits<double>::max()};
262 
263  // loop over the hits on the same layer
264  for (unsigned int j = 0; j < layer.size(); ++j) {
265  if (usedHits[i].get().at(j) > m_maxUsed) {
266  continue;
267  }
268  auto hit = layer.at(j);
269  const double pull = std::sqrt(SeedingAux::chi2Term(extrapPos, direction, *hit));
270  ATH_MSG_VERBOSE("Trying extension with hit " << m_idHelperSvc->toString(hit->identify()));
271 
272  //find the hit with the minimum pull (check at least one hit after we have increasing pulls)
273  if (pull > minPull) {
274  triedHit+=1;
275  continue;
276  }
277 
278  if(triedHit>1){
279  break;
280  }
281 
282  indexOfHit = j;
283  minPull = pull;
284  }
285 
286  // complete the seed with the extended hits
287  if (minPull < m_minPullThreshold) {
288  const auto* bestCand = layer.at(indexOfHit);
289  ATH_MSG_VERBOSE("Extension successfull - hit" << m_idHelperSvc->toString(bestCand->identify())
290  <<", pos: "<<Amg::toString(bestCand->localPosition())
291  <<", dir: "<<Amg::toString(bestCand->sensorDirection())<<" found with pull "<<minPull);
292  combinatoricHits.push_back(bestCand);
293  }
294  }
295  return combinatoricHits;
296 }
297 
298 std::unique_ptr<SegmentSeed>
299  NswSegmentFinderAlg::buildSegmentSeed(const InitialSeed_t& initialSeed,
300  const AmgSymMatrix(2)& bMatrix,
301  const HoughMaximum& max,
302  const HitLaySpan_t& extensionLayers,
303  const UsedHitSpan_t& usedHits) const {
304 
305 
306  //we reject seeds with all clusters' sizes less than min value
307  bool allValid = std::all_of(initialSeed.begin(), initialSeed.end(), [this](const auto& hit){
308 
309  if (hit->type() == xAOD::UncalibMeasType::MMClusterType) {
310  const auto* mmClust = static_cast<const xAOD::MMCluster*>(hit->primaryMeasurement());
311  return mmClust->stripNumbers().size() >= m_minClusSize;
312  }
313 
314  return false;
315  });
316 
317  if (!allValid) {
318  ATH_MSG_VERBOSE("Seed rejection: Not all clusters meet minimum strip size");
319  return nullptr;
320  }
321 
322 
323  std::array<double, 4> params = defineParameters(bMatrix, initialSeed);
324 
325  const auto [segPos, direction] = seedSolution(initialSeed, params);
326 
327  // check the consistency of the parameters - expected to lay in the strip's
328  // length
329  for (std::size_t i = 0; i < 4; ++i) {
330  const double halfLength = stripHalfLength(*initialSeed[i]);
331 
332  if (std::abs(params[i]) > halfLength) {
333  ATH_MSG_VERBOSE("Seed Rejection: Invalid seed - outside of the strip's length");
334  return nullptr;
335  }
336  }
337  double tanAlpha = houghTanAlpha(direction);
338  double tanBeta = houghTanBeta(direction);
339 
340  double interceptX = segPos.x();
341  double interceptY = segPos.y();
342 
343 
344  // extend the seed to the segment -- include hits from the other layers too
345  auto extendedHits = extendHits(segPos, direction, extensionLayers, usedHits);
346  HitVec hits{initialSeed.begin(),initialSeed.end()};
347  hits.insert(hits.end(), extendedHits.begin(), extendedHits.end());
348 
349 
350  return std::make_unique<SegmentSeed>(tanBeta, interceptY, tanAlpha,
351  interceptX, hits.size(),
352  std::move(hits), max.parentBucket());
353 }
354 
355 
356 std::unique_ptr<Segment> NswSegmentFinderAlg::fitSegmentSeed(const EventContext& ctx,
357  const ActsTrk::GeometryContext& gctx,
358  const SegmentSeed* patternSeed) const{
359 
360  ATH_MSG_VERBOSE("Fit the SegmentSeed");
361 
362  //Calibration of the seed spacepoints
363  CalibSpacePointVec calibratedHits = m_calibTool->calibrate(ctx, patternSeed->getHitsInMax(),
364  patternSeed->localPosition(), patternSeed->localDirection(), 0.);
365 
366  const Amg::Transform3D& locToGlob{patternSeed->msSector()->localToGlobalTrans(gctx)};
367 
368  auto segment = m_lineFitter->fitSegment(ctx, patternSeed, patternSeed->parameters(),
369  locToGlob, std::move(calibratedHits));
370 
371  return segment;
372 
373 }
374 
375 std::pair<std::vector<std::unique_ptr<SegmentSeed>>, std::vector<std::unique_ptr<Segment>>>
376 NswSegmentFinderAlg::findSegmentsFromMaximum(const HoughMaximum &max, const ActsTrk::GeometryContext &gctx, const EventContext& ctx) const {
377  // first sort the hits per layer from the maximum
378  SpacePointPerLayerSplitter hitLayers{max.getHitsInMax()};
379 
380  const HitLayVec& stripHitsLayers{hitLayers.stripHits()};
381  const std::size_t layerSize = stripHitsLayers.size();
382 
383  //seeds and segments containers
384  std::vector<std::unique_ptr<SegmentSeed>> seeds{};
385  std::vector<std::unique_ptr<Segment>> segments{};
386 
387  //counters for the number of seeds, extented seeds and segments
388  unsigned int nSeeds{0}, nExtSeeds{0}, nSegments{0};
389 
390 
391  if (layerSize < minLayers) {
392  ATH_MSG_VERBOSE("Not enough layers to build a seed");
393  return std::make_pair(std::move(seeds),std::move(segments));
394  }
395 
396 
397  if (m_visionTool.isEnabled()) {
399  const auto truthHits = getMatchingSimHits(max.getHitsInMax());
400  constexpr double legX{0.2};
401  double legY{0.8};
402  for (const SpacePoint* sp : max.getHitsInMax()) {
403  const auto* mmClust = static_cast<const xAOD::MMCluster*>(sp->primaryMeasurement());
404  const xAOD::MuonSimHit* simHit = getTruthMatchedHit(*mmClust);
405  if (!simHit || !MC::isMuon(simHit)) continue;
406  const MuonGMR4::MmReadoutElement* reEle = mmClust->readoutElement();
407  const MuonGMR4::StripDesign& design = reEle->stripLayer(mmClust->measurementHash()).design();
408  const Amg::Transform3D toChamb = reEle->msSector()->globalToLocalTrans(gctx) *
409  reEle->localToGlobalTrans(gctx, simHit->identify());
410  const Amg::Vector3D hitPos = toChamb * xAOD::toEigen(simHit->localPosition());
411  const Amg::Vector3D hitDir = toChamb.linear() * xAOD::toEigen(simHit->localDirection());
412  const double pull = std::sqrt(SeedingAux::chi2Term(hitPos, hitDir, *sp));
413  const double pull2 = (mmClust->localPosition<1>().x() - simHit->localPosition().x()) / std::sqrt(mmClust->localCovariance<1>().x());
414  primitives.push_back(MuonValR4::drawLabel(std::format("ml: {:1d}, gap: {:1d}, {:}, pull: {:.2f} / {:.2f}", reEle->multilayer(), mmClust->gasGap(),
415  !design.hasStereoAngle() ? "X" : design.stereoAngle() >0 ? "U": "V",pull, pull2),legX,legY,14));
416  legY-=0.05;
417  }
418  m_visionTool->visualizeBucket(ctx, *max.parentBucket(),
419  "truth", std::move(primitives));
420  }
421 
422  UsedHitMarker_t allUsedHits = emptyBookKeeper(stripHitsLayers);
423 
424 
425  const Amg::Transform3D globToLocal = max.msSector()->globalToLocalTrans(gctx);
426  std::array<const SpacePoint*, 4> seedHits{};
427 
428  InitialSeedVec_t preLimSeeds{};
429 
430  for (std::size_t i = 0; i < layerSize - 3; ++i) {
431  seedHits[0] = stripHitsLayers[i].front();
432  for (std::size_t j = i + 1; j < layerSize - 2; ++j) {
433  seedHits[1] = stripHitsLayers[j].front();
434  for (std::size_t k = j + 1; k < layerSize - 1; ++k) {
435  seedHits[2] = stripHitsLayers[k].front();
436  for (std::size_t l = k + 1; l < layerSize; ++l) {
437  seedHits[3] = stripHitsLayers[l].front();
438 
439  const HitLaySpan_t layers{stripHitsLayers[i], stripHitsLayers[j], stripHitsLayers[k], stripHitsLayers[l]};
440 
441  //skip combination with at least one too busy layer
442  if (std::any_of(layers.begin(), layers.end(),
443  [this](const auto& layer) {
444  return layer.get().size() > m_maxClustersInLayer;
445  })) {
446  continue; // skip this combination
447  }
448 
449  AmgSymMatrix(2) bMatrix = betaMatrix(seedHits);
450  if (std::abs(bMatrix.determinant()) < 1.e-6) {
451  continue;
452  }
453  ATH_MSG_DEBUG("Space point positions for seed layers: "
454  << Amg::toString(seedHits[0]->localPosition()) << ", "
455  << Amg::toString(seedHits[1]->localPosition()) << ", "
456  << Amg::toString(seedHits[2]->localPosition()) << ", "
457  << Amg::toString(seedHits[3]->localPosition()));
458 
459  UsedHitSpan_t usedHits{allUsedHits[i], allUsedHits[j], allUsedHits[k], allUsedHits[l]};
460  // each layer may have more than one hit - take the hit combinations
461  constructPrelimnarySeeds(globToLocal.translation(), layers, usedHits, preLimSeeds);
462 
463  //the layers not participated in the seed build - gonna be used for the extension
464  HitLaySpan_t extensionLayers{};
465  UsedHitSpan_t usedExtensionHits{};
466  usedExtensionHits.reserve(stripHitsLayers.size());
467  extensionLayers.reserve(stripHitsLayers.size());
468  for (std::size_t e = 0 ; e < stripHitsLayers.size(); ++e) {
469  if (!(e == i || e == j || e == k || e == l)){
470  extensionLayers.emplace_back(stripHitsLayers[e]);
471  usedExtensionHits.emplace_back(allUsedHits[e]);
472  }
473  }
474  // we have made sure to have hits from all the four layers -
475  // start by 4 hits for the seed and try to build the seed for the combinatorics found
476  for (auto &combinatoricHits : preLimSeeds) {
477  auto seed = buildSegmentSeed(combinatoricHits, bMatrix, max, extensionLayers, usedExtensionHits);
478  if (seed) {
479  //if the seed build is successful, try to build the segment
480  ++nSeeds;
481  if(seed->getHitsInMax().size() < m_minSeedHits){
482  ATH_MSG_VERBOSE("Not succesfully extended seed");
483  continue;
484 
485  }
486  ++nExtSeeds;
487  std::unique_ptr<Segment> segment = fitSegmentSeed(ctx, gctx, seed.get());
488  seeds.push_back(std::move(seed));
489 
490 
491  if (!segment) {
492  ATH_MSG_VERBOSE("Seed Rejection: Segment fit failed");
493  if(m_markHitsFromSeed){
494  //mark hits from extended seed if no succesfully led to segment
495  markHitsAsUsed(seeds.back()->getHitsInMax(), stripHitsLayers, allUsedHits, 1, false);
496  }
497  continue;
498  }
499 
500  ++nSegments;
501  // Flag hits as used and in the window around segment
502  HitVec segMeasSP;
503  segMeasSP.reserve(segment->measurements().size());
504  std::transform(segment->measurements().begin(),
505  segment->measurements().end(),
506  std::back_inserter(segMeasSP),
507  [](const auto& m) { return m->spacePoint(); });
508  //mark hits from segment
509  markHitsAsUsed(segMeasSP, stripHitsLayers, allUsedHits, 10, true);
510  segments.push_back(std::move(segment));
511 
512  }
513  }
514  }
515  }
516  }
517  }
518 
519  if(m_dumpSeedStatistics){
520  m_seedCounter->addToStat(max.msSector(), nSeeds, nExtSeeds, nSegments);
521  }
522 
523  return std::make_pair(std::move(seeds),std::move(segments));
524 }
525 
526 
527 void NswSegmentFinderAlg::markHitsAsUsed(const HitVec& spacePoints,
528  const HitLayVec& allSortHits,
529  UsedHitMarker_t& usedHitMarker,
530  unsigned int incr,
531  bool markNeighborHits) const {
532 
533  SpacePointPerLayerSorter layerSorter{};
534 
535  for(const auto& sp : spacePoints){
536 
537  if(!sp){
538  continue;
539  }
540 
541  unsigned int measLayer = layerSorter.sectorLayerNum(*sp);
542 
543  bool found{false};
544  double spPosX = sp->primaryMeasurement()->localPosition<1>().x();
545 
546  for (std::size_t lIdx = 0; !found && lIdx < allSortHits.size(); ++lIdx) {
547  const HitVec& hVec{allSortHits[lIdx]};
548  //check if they are not in the same layer
549  unsigned int hitLayer = layerSorter.sectorLayerNum(*hVec.front());
550  if(hitLayer != measLayer){
551  ATH_MSG_VERBOSE("Not in the same layer since measLayer = "<< measLayer << " and "<<hitLayer);
552  continue;
553  }
554 
555  for (std::size_t hIdx = 0 ; hIdx < hVec.size(); ++hIdx) {
556  //check the dY between the measurement and the hits
557 
558  auto testHit = hVec[hIdx];
559 
560  if (testHit == sp) {
561  usedHitMarker[lIdx][hIdx] += incr;
562  found = true;
563  if(!markNeighborHits){
564  break;
565  }
566  }
567 
568  //if the hit not found let's see if it is too close to the segment's measurement
569  double deltaX = std::abs(testHit->primaryMeasurement()->localPosition<1>().x() - spPosX);
570  if(deltaX < m_maxdYWindow){
571  usedHitMarker[lIdx][hIdx] += incr;
572 
573  }
574  }
575  }
576 
577  }
578 
579 }
580 
581 StatusCode NswSegmentFinderAlg::execute(const EventContext &ctx) const {
582  // read the inputs
583  const EtaHoughMaxContainer *maxima{nullptr};
584  ATH_CHECK(SG::get( maxima, m_etaKey, ctx));
585 
586  const ActsTrk::GeometryContext *gctx{nullptr};
587  ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
588 
589  // prepare our output collection
590  SG::WriteHandle writeSegments{m_writeSegmentKey, ctx};
591  ATH_CHECK(writeSegments.record(std::make_unique<SegmentContainer>()));
592 
593  SG::WriteHandle writeSegmentSeeds{m_writeSegmentSeedKey, ctx};
594  ATH_CHECK(writeSegmentSeeds.record(std::make_unique<SegmentSeedContainer>()));
595 
596  // we use the information from the previous eta-hough transform
597  // to get the combined hits that belong in the same maxima
598  for (const HoughMaximum *max : *maxima) {
599 
600  auto [seeds, segments] = findSegmentsFromMaximum(*max, *gctx, ctx);
601 
602  if (msgLvl(MSG::VERBOSE)) {
603  for(const auto& hitMax : max->getHitsInMax()){
604  ATH_MSG_VERBOSE("Hit "<<m_idHelperSvc->toString(hitMax->identify())<<", "
605  <<Amg::toString(hitMax->localPosition())<<", dir: "
606  <<Amg::toString(hitMax->sensorDirection()));
607  }
608  }
609 
610  for(auto& seed: seeds){
611 
612  if (msgLvl(MSG::VERBOSE)){
613  std::stringstream sstr{};
614  sstr<<"Seed tanBeta = "<<seed->tanBeta()<<", y0 = "<<seed->interceptY()
615  <<", tanAlpha = "<<seed->tanAlpha()<<", x0 = "<<seed->interceptX()<<", hits in the seed "
616  <<seed->getHitsInMax().size()<<std::endl;
617 
618  for(const auto& hit : seed->getHitsInMax()){
619  sstr<<" *** Hit "<<m_idHelperSvc->toString(hit->identify())<<", "
620  << Amg::toString(hit->localPosition())<<", dir: "<<Amg::toString(hit->sensorDirection())<<std::endl;
621  }
622  ATH_MSG_VERBOSE(sstr.str());
623  }
624  if (m_visionTool.isEnabled()) {
625  m_visionTool->visualizeSeed(ctx, *seed, "#phi-combinatorialSeed");
626  }
627 
628  writeSegmentSeeds->push_back(std::move(seed));
629 
630  }
631 
632  for (auto &seg : segments) {
633 
634  const Parameters pars = localSegmentPars(*gctx, *seg);
635 
636  ATH_MSG_VERBOSE("Segment parameters : "<<toString(pars));
637 
638  if (m_visionTool.isEnabled()) {
639  m_visionTool->visualizeSegment(ctx, *seg, "#phi-segment");
640  }
641 
642  writeSegments->push_back(std::move(seg));
643 
644  }
645  }
646 
647  return StatusCode::SUCCESS;
648 }
649 
651 
652  if(m_dumpSeedStatistics){
653  m_seedCounter->printTableSeedStats(msgStream());
654  }
655  return StatusCode::SUCCESS;
656 }
657 
658 void NswSegmentFinderAlg::SeedStatistics::addToStat(const MuonGMR4::SpectrometerSector* msSector, unsigned int seeds, unsigned int extSeeds, unsigned int segments){
659  std::unique_lock guard{m_mutex};
660  SectorField key{};
661  key.chIdx = msSector->chamberIndex();
662  key.phi = msSector->stationPhi();
663  key.eta = msSector->chambers().front()->stationEta();
664  key.side = msSector->side();
665 
666  auto &entry = m_seedStat[key];
667  entry.nSeeds += seeds;
668  entry.nExtSeeds += extSeeds;
669  entry.nSegments += segments;
670 }
671 
672 void NswSegmentFinderAlg::SeedStatistics::printTableSeedStats(MsgStream& msg) const{
673 
674 
675  msg<<MSG::ALWAYS<<"Seed statistics per sector:"<<endmsg;
676  msg<<MSG::ALWAYS<<"------------------------------------------------------------"<<endmsg;
677  msg<<MSG::ALWAYS<<"| Chamber | Phi | Eta | Side | Seeds | ExtSeeds | Segments |"<<endmsg;
678  msg<<MSG::ALWAYS<<"------------------------------------------------------------"<<endmsg;
679 
681 
682  for (const auto& entry : m_seedStat) {
683  const auto& sector = entry.first;
684  const auto& stats = entry.second;
685 
686 
687  msg<<MSG::ALWAYS << "| " << std::setw(3) << (sector.chIdx == ChIndex::EIL ? "EIL" :"EIS")
688  <<" | " << std::setw(2) << sector.phi
689  << " | " << std::setw(3) << sector.eta
690  << " | " << std::setw(4) << (sector.side > 0 ? "A" : "C")
691  << " | " << std::setw(7) << stats.nSeeds
692  << " | " << std::setw(8) << stats.nExtSeeds
693  << " | " << std::setw(8) << stats.nSegments
694  << " |"<<endmsg;
695 
696 
697  }
698 
699  msg<<MSG::ALWAYS<<"------------------------------------------------------------"<<endmsg;
700  }
701 
702 
703 } // namespace MuonR4
MuonR4::minLayers
constexpr unsigned int minLayers
Definition: NswSegmentFinderAlg.cxx:58
AllowedVariables::e
e
Definition: AsgElectronSelectorTool.cxx:37
make_hlt_rep.pars
pars
Definition: make_hlt_rep.py:90
GetLCDefs::Unknown
@ Unknown
Definition: GetLCDefs.h:21
MuonR4::NswSegmentFinderAlg::HitWindow
HitWindow
To fastly check whether a hit is roughly compatible with a muon trajectory a narrow corridor is opene...
Definition: NswSegmentFinderAlg.h:131
MuonSimHitHelpers.h
xAOD::MuonSimHit_v1
Definition: MuonSimHit_v1.h:18
MuonR4::NswSegmentFinderAlg::InitialSeed_t
std::array< const SpacePoint *, 4 > InitialSeed_t
Abbrivation of the
Definition: NswSegmentFinderAlg.h:119
MuonR4::SpacePoint::msSector
const MuonGMR4::SpectrometerSector * msSector() const
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/src/SpacePoint.cxx:84
MuonGMR4::StripDesign::stereoAngle
double stereoAngle() const
Returns the value of the stereo angle.
MuonGMR4::MmReadoutElement
Definition: MmReadoutElement.h:18
python.tests.PyTestsLib.finalize
def finalize(self)
_info( "content of StoreGate..." ) self.sg.dump()
Definition: PyTestsLib.py:50
MuonGMR4::StripDesign
Definition: StripDesign.h:30
MuonR4::NswSegmentFinderAlg::HitLaySpan_t
std::vector< std::reference_wrapper< const HitVec > > HitLaySpan_t
Abbrivation of the space comprising multiple hit vectors without copy.
Definition: NswSegmentFinderAlg.h:113
MuonR4::SpacePoint::type
xAOD::UncalibMeasType type() const
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/src/SpacePoint.cxx:88
MuonGMR4::SpectrometerSector::side
int8_t side() const
Returns the side of the MS-sector 1 -> A side ; -1 -> C side.
Definition: SpectrometerSector.cxx:57
TRTCalib_Extractor.hits
hits
Definition: TRTCalib_Extractor.py:35
MuonGMR4::MuonReadoutElement::msSector
const SpectrometerSector * msSector() const
Returns the pointer to the envelope volume enclosing all chambers in the sector.
MuonR4::SpacePointPerLayerSplitter
The SpacePointPerLayerSplitter takes a set of spacepoints already sorted by layer Identifier (see Muo...
Definition: SpacePointPerLayerSplitter.h:16
MuonR4::SegmentSeed::localPosition
Amg::Vector3D localPosition() const
Returns the position of the seed in the sector frame.
Definition: SegmentSeed.cxx:52
MuonGMR4::SpectrometerSector
A spectrometer sector forms the envelope of all chambers that are placed in the same MS sector & laye...
Definition: SpectrometerSector.h:40
xAOD::MuonSimHit_v1::identify
Identifier identify() const
Returns the global ATLAS identifier of the SimHit.
Definition: xAODMuonSimHit_V1.cxx:42
vtune_athena.format
format
Definition: vtune_athena.py:14
MuonGMR4::SpectrometerSector::stationPhi
int stationPhi() const
: Returns the station phi of the sector
Definition: SpectrometerSector.cxx:63
xAOD::MMCluster_v1
Definition: MMCluster_v1.h:20
MuonR4::SpacePointPerLayerSorter
The SpacePointPerLayerSorter sort two given space points by their layer Identifier.
Definition: SpacePointPerLayerSorter.h:15
MuonR4::SpacePointPerLayerSorter::sectorLayerNum
unsigned int sectorLayerNum(const SpacePoint &sp) const
method returning the logic layer number
Definition: SpacePointPerLayerSorter.cxx:13
VisualizationHelpers.h
MuonGMR4::SpectrometerSector::localToGlobalTrans
const Amg::Transform3D & localToGlobalTrans(const ActsTrk::GeometryContext &gctx) const
Returns the local -> global tarnsformation from the sector.
Definition: SpectrometerSector.cxx:75
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
MuonGMR4::SpectrometerSector::chambers
const ChamberSet & chambers() const
Returns the associated chambers with this sector.
Definition: SpectrometerSector.cxx:71
DMTest::P
P_v1 P
Definition: P.h:23
MuonR4::NswSegmentFinderAlg::StripOrient
StripOrient
Enumeration to classify the orientation of a NSW strip
Definition: NswSegmentFinderAlg.h:45
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
initialize
void initialize()
Definition: run_EoverP.cxx:894
DMTest::C
C_v1 C
Definition: C.h:26
xAOD::UncalibMeasType::MMClusterType
@ MMClusterType
module_driven_slicing.layers
layers
Definition: module_driven_slicing.py:113
xAOD::MMCluster_v1::readoutElement
const MuonGMR4::MmReadoutElement * readoutElement() const
Retrieve the associated MmReadoutElement.
TEST_HIT_CORRIDOR
#define TEST_HIT_CORRIDOR(LAYER, HIT_ITER, START_LAYER)
Macro to check whether a hit is compatible with the hit corridor.
Definition: NswSegmentFinderAlg.cxx:172
sTgcIdHelper::Strip
@ Strip
Definition: sTgcIdHelper.h:190
UploadAMITag.l
list l
Definition: UploadAMITag.larcaf.py:157
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
xAOD::UncalibMeasType::sTgcStripType
@ sTgcStripType
MuonR4::SegmentFit::SegmentLineFitter::Config
Full configuration object.
Definition: SegmentLineFitter.h:72
trigbs_dumpHLTContentInBS.stats
stats
Definition: trigbs_dumpHLTContentInBS.py:91
x
#define x
MuonValR4::drawLabel
std::unique_ptr< TLatex > drawLabel(const std::string &text, const double xPos, const double yPos, const unsigned int fontSize=18)
Create a TLatex label,.
Definition: VisualizationHelpers.cxx:40
AmgSymMatrix
#define AmgSymMatrix(dim)
Definition: EventPrimitives.h:50
Monitored::X
@ X
Definition: HistogramFillerUtils.h:24
LArG4FSStartPointFilterLegacy.execute
execute
Definition: LArG4FSStartPointFilterLegacy.py:20
MuonR4::SpacePoint::primaryMeasurement
const xAOD::UncalibratedMeasurement * primaryMeasurement() const
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/src/SpacePoint.cxx:75
python.setupRTTAlg.size
int size
Definition: setupRTTAlg.py:39
MuonGMR4::SpectrometerSector::chamberIndex
Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index scheme.
Definition: SpectrometerSector.cxx:62
MuonR4::houghTanBeta
double houghTanBeta(const Amg::Vector3D &v)
Returns the hough tanBeta [y] / [z].
Definition: SegmentFitterEventData.cxx:26
SpacePointPerLayerSplitter.h
Amg::toString
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Definition: GeoPrimitivesToStringConverter.h:40
MuonGMR4::MmReadoutElement::multilayer
int multilayer() const
Returns the multi layer of the element [1-2].
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
TrigConf::MSGTC::ALWAYS
@ ALWAYS
Definition: Trigger/TrigConfiguration/TrigConfBase/TrigConfBase/MsgStream.h:29
lumiFormat.i
int i
Definition: lumiFormat.py:85
SG::get
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
Definition: ReadCondHandle.h:283
endmsg
#define endmsg
Definition: AnalysisConfig_Ntuple.cxx:63
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
sTgcIdHelper::Wire
@ Wire
Definition: sTgcIdHelper.h:190
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
TRT::Hit::layer
@ layer
Definition: HitInfo.h:79
Amg::Transform3D
Eigen::Affine3d Transform3D
Definition: GeoPrimitives.h:46
Amg::transform
Amg::Vector3D transform(Amg::Vector3D &v, Amg::Transform3D &tr)
Transform a point from a Trasformation3D.
Definition: GeoPrimitivesHelpers.h:156
MuonR4::SegmentSeed::getHitsInMax
const std::vector< HitType > & getHitsInMax() const
Returns the list of assigned hits.
Definition: SegmentSeed.cxx:48
MuonR4::SegmentFit::Parameters
Acts::Experimental::CompositeSpacePointLineFitter::ParamVec_t Parameters
Definition: MuonHoughDefs.h:46
MuonGMR4::MuonReadoutElement::localToGlobalTrans
const Amg::Transform3D & localToGlobalTrans(const ActsTrk::GeometryContext &ctx) const
Returns the local to global transformation into the ATLAS coordinate system.
Definition: MuonPhaseII/MuonDetDescr/MuonReadoutGeometryR4/src/MuonReadoutElement.cxx:81
python.StandardJetMods.pull
pull
Definition: StandardJetMods.py:309
xAOD::MuonSimHit_v1::localDirection
ConstVectorMap< 3 > localDirection() const
Returns the local direction of the traversing particle.
Definition: xAODMuonSimHit_V1.cxx:66
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
ActsTrk::GeometryContext
Definition: GeometryContext.h:28
MuonR4::SegmentFit::SegmentLineFitter::ConfigSwitches::calibrator
const ISpacePointCalibrator * calibrator
Pointer to the calibrator.
Definition: SegmentLineFitter.h:51
F600IntegrationConfig.spacePoints
spacePoints
Definition: F600IntegrationConfig.py:122
DataVector
Derived DataVector<T>.
Definition: DataVector.h:795
xAOD::UncalibratedMeasurement_v1::localPosition
ConstVectorMap< N > localPosition() const
Returns the local position of the measurement.
compareGeometries.deltaX
float deltaX
Definition: compareGeometries.py:32
MuonGMR4::StripLayer::design
const StripDesign & design(bool phiView=false) const
Returns the underlying strip design.
MuonR4::NswSegmentFinderAlg::UsedHitSpan_t
std::vector< std::reference_wrapper< std::vector< unsigned int > >> UsedHitSpan_t
Abbrivation of the container to pass a subset of markers wtihout copy.
Definition: NswSegmentFinderAlg.h:117
NswSegmentFinderAlg.h
MuonR4::SpacePoint
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/MuonSpacePoint/SpacePoint.h:24
MuonGMR4::SpectrometerSector::globalToLocalTrans
Amg::Transform3D globalToLocalTrans(const ActsTrk::GeometryContext &gctx) const
Returns the global -> local transformation from the ATLAS global.
Definition: SpectrometerSector.cxx:78
lumiFormat.array
array
Definition: lumiFormat.py:91
MuonGMR4::MmReadoutElement::stripLayer
const StripLayer & stripLayer(const Identifier &measId) const
GetAllXsec.entry
list entry
Definition: GetAllXsec.py:132
MuonR4::NswSegmentFinderAlg::SeedStatistics::SectorField
sector's field to dump the seed statistics
Definition: NswSegmentFinderAlg.h:84
MuonValR4::IPatternVisualizationTool::PrimitiveVec
std::vector< PrimitivePtr > PrimitiveVec
Definition: IPatternVisualizationTool.h:31
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:240
MuonGMR4::SpectrometerSector::idHelperSvc
const Muon::IMuonIdHelperSvc * idHelperSvc() const
Returns the IdHelpeSvc.
Definition: SpectrometerSector.cxx:61
sTgcMeasurement.h
checkTriggerxAOD.found
found
Definition: checkTriggerxAOD.py:328
Amg::Vector3D
Eigen::Matrix< double, 3, 1 > Vector3D
Definition: GeoPrimitives.h:47
THROW_EXCEPTION
#define THROW_EXCEPTION(MESSAGE)
Definition: throwExcept.h:10
Amg::dirFromAngles
Amg::Vector3D dirFromAngles(const double phi, const double theta)
Constructs a direction vector from the azimuthal & polar angles.
Definition: GeoPrimitivesHelpers.h:299
MuonR4
This header ties the generic definitions in this package.
Definition: HoughEventData.h:16
Trk::inside
@ inside
Definition: PropDirection.h:29
SG::WriteHandle
Definition: StoreGate/StoreGate/WriteHandle.h:73
MuonR4::NswSegmentFinderAlg::InitialSeedVec_t
std::vector< InitialSeed_t > InitialSeedVec_t
Vector of initial seeds.
Definition: NswSegmentFinderAlg.h:121
MuonR4::SpacePoint::localPosition
const Amg::Vector3D & localPosition() const
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/src/SpacePoint.cxx:50
MuonR4::SegmentSeed::localDirection
Amg::Vector3D localDirection() const
Returns the direction of the seed in the sector frame.
Definition: SegmentSeed.cxx:53
MuonR4::HoughMaximum
Data class to represent an eta maximum in hough space.
Definition: HoughMaximum.h:14
SegmentFitterEventData.h
Muon::IMuonIdHelperSvc::toString
virtual std::string toString(const Identifier &id) const =0
print all fields to string
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
MuonR4::CalibSpacePointVec
ISpacePointCalibrator::CalibSpacePointVec CalibSpacePointVec
Definition: SpacePointCalibrator.cxx:40
sTgcIdHelper::Pad
@ Pad
Definition: sTgcIdHelper.h:190
unit
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
Definition: AmgMatrixBasePlugin.h:21
MuonR4::SpacePoint::dimension
unsigned dimension() const
Is the space point a 1D or combined 2D measurement.
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/src/SpacePoint.cxx:107
re
const boost::regex re(r_e)
MuonR4::SegmentSeed
Representation of a segment seed (a fully processed hough maximum) produced by the hough transform.
Definition: SegmentSeed.h:14
xAOD::MuonSimHit_v1::localPosition
ConstVectorMap< 3 > localPosition() const
Returns the local postion of the traversing particle.
Definition: xAODMuonSimHit_V1.cxx:60
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition: hcg.cxx:127
MuonR4::getTruthMatchedHit
const xAOD::MuonSimHit * getTruthMatchedHit(const xAOD::UncalibratedMeasurement &prdHit)
Returns the MuonSimHit, if there's any, matched to the uncalibrated muon measurement.
Definition: MuonSimHitHelpers.cxx:27
MuonR4::SegmentSeed::parameters
const Parameters & parameters() const
Returns the parameter array.
Definition: SegmentSeed.cxx:46
MuonR4::SegmentFit::localSegmentPars
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
Definition: SegmentFitterEventData.cxx:42
MMCluster.h
python.BuildSignatureFlags.beamSpot
AthConfigFlags beamSpot(AthConfigFlags flags, str instanceName, str recoMode)
Definition: BuildSignatureFlags.py:491
MuonGMR4::StripDesign::hasStereoAngle
bool hasStereoAngle() const
Returns whether a stereo angle is defined.
MuonR4::HitVec
SpacePointPerLayerSplitter::HitVec HitVec
Definition: SpacePointPerLayerSplitter.cxx:12
MuonR4::NswSegmentFinderAlg::HitLayVec
SpacePointPerLayerSplitter::HitLayVec HitLayVec
Definition: NswSegmentFinderAlg.h:111
Muon::MuonStationIndex::ChIndex
ChIndex
enum to classify the different chamber layers in the muon spectrometer
Definition: MuonStationIndex.h:15
python.Constants.VERBOSE
int VERBOSE
Definition: Control/AthenaCommon/python/Constants.py:13
PowhegControl_ttFCNC_NLO.params
params
Definition: PowhegControl_ttFCNC_NLO.py:226
MuonR4::houghTanAlpha
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
Definition: SegmentFitterEventData.cxx:30
MuonR4::NswSegmentFinderAlg::UsedHitMarker_t
std::vector< std::vector< unsigned int > > UsedHitMarker_t
Abbrivation of the container book keeping whether a hit is used or not.
Definition: NswSegmentFinderAlg.h:115
python.AutoConfigFlags.msg
msg
Definition: AutoConfigFlags.py:7
SpacePointPerLayerSorter.h
MuonR4::getMatchingSimHits
std::unordered_set< const xAOD::MuonSimHit * > getMatchingSimHits(const xAOD::MuonSegment &segment)
: Returns all sim hits matched to a xAOD::MuonSegment
Definition: MuonSimHitHelpers.cxx:38
HepMCHelpers.h
MuonR4::SpacePoint::identify
const Identifier & identify() const
: Identifier of the primary measurement
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/src/SpacePoint.cxx:97
MuonR4::SegmentSeed::msSector
const MuonGMR4::SpectrometerSector * msSector() const
Returns the associated chamber.
Definition: SegmentSeed.cxx:50
fitman.k
k
Definition: fitman.py:528
xAOD::sTgcMeasurement_v1
Definition: sTgcMeasurement_v1.h:21
MuonR4::NswSegmentFinderAlg::HitVec
SpacePointPerLayerSplitter::HitVec HitVec
Definition: NswSegmentFinderAlg.h:109
python.SystemOfUnits.m
float m
Definition: SystemOfUnits.py:106
MuonR4::SpacePoint::sensorDirection
const Amg::Vector3D & sensorDirection() const
Definition: MuonSpectrometer/MuonPhaseII/Event/MuonSpacePoint/src/SpacePoint.cxx:51
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37
isMuon
bool isMuon(const T &p)
Definition: AtlasPID.h:205