ATLAS Offline Software
MuonNSWSegmentFinderTool.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3 */
4 
6 
18 #include "TrkTrack/Track.h"
21 
22 #include "CxxUtils/inline_hints.h"
23 
24 namespace {
25  using MuonSegmentVec = std::vector<std::unique_ptr<Muon::MuonSegment>>;
26  const MuonGM::MuonChannelDesign* getDesign(const Muon::MuonClusterOnTrack* cl) {
27  const Trk::TrkDetElementBase* ele = cl->detectorElement();
29  return static_cast<const MuonGM::MMReadoutElement*>(ele)->getDesign(cl->identify());
31  return static_cast<const MuonGM::sTgcReadoutElement*>(ele)->getDesign(cl->identify());
32  return nullptr;
33  }
34 
35  int clusterSize(const Muon::MuonClusterOnTrack* cl) {
36  const Trk::PrepRawData* prd = cl->prepRawData();
38  return static_cast<const Muon::MMPrepData*>(prd)->stripNumbers().size();
39  }
41  return static_cast<const Muon::sTgcPrepData*>(prd)->stripNumbers().size();
42  }
43  return -1;
44  }
45 
46  std::string to_string(const Amg::Vector3D& v) {
47  std::stringstream sstr{};
48  sstr<<"[x,y,z]="<<Amg::toString(v, 2)<<" [theta/eta/phi]=("<<(v.theta() / Gaudi::Units::degree)<<","<<v.eta()<<","<<(v.phi()/ Gaudi::Units::degree)<<")";
49  return sstr.str();
50  }
52  constexpr double minEtaNSW = 1.2;
53  constexpr double maxEtaNSW = 2.8;
54 
55 } // namespace
56 namespace Muon {
82 
94 
104  m_cl{cl},
105  m_dir{cl->detectorElement()->transform(cl->identify()).linear() * Amg::Vector3D::UnitY()} {}
106 
107  NSWSeed::NSWSeed(const MuonNSWSegmentFinderTool* parent, const std::array<SeedMeasurement, 4>& seed,
108  const std::array<double,2>& lengths) :
109  m_parent{parent},
110  m_pos{seed[0].pos() + lengths[0] * seed[0].dir()} {
111 
112  const Amg::Vector3D un_dir = (seed[1].pos() + lengths[1] *seed[1].dir() - m_pos);
113  m_dir = un_dir.unit()*(un_dir.z() * seed[0].pos().z() > 0 ? 1 : -1);
114 
115  if (m_parent->msgLvl(MSG::VERBOSE))
116  m_parent->msgStream() << MSG::VERBOSE << m_parent->printSeed(seed)<<" is a valid seed "<<to_string(m_pos) <<" pointing to "<<to_string(m_dir)<<"."<<endmsg;
117 
119  for (const SeedMeasurement& cl : seed) {
120  insert(cl);
121  m_width = std::hypot(m_width, Amg::error(cl->localCovariance(), Trk::locX));
122  }
123  m_width /= std::sqrt(3);
124  }
126  const SeedMeasurement& _rightM) :
127  m_parent{parent}, m_pos{_leftM.pos()} {
128  m_dir = (_rightM.pos() - m_pos).unit();
129  m_width = std::hypot(Amg::error(_leftM->localCovariance(), Trk::locX),
130  Amg::error(_rightM->localCovariance(), Trk::locX)) / std::sqrt(2);
131  insert(_leftM);
132  insert(_rightM);
133  }
135  m_parent{parent}, m_pos{seg.globalPosition()}, m_dir{seg.globalDirection()} {
136  for (const Trk::MeasurementBase* meas : seg.containedMeasurements()) {
137  const Muon::MuonClusterOnTrack* clus = dynamic_cast<const Muon::MuonClusterOnTrack*>(meas);
138  if (!clus) continue;
139  insert(clus);
140  m_width = std::hypot(m_width, Amg::error(clus->localCovariance(), Trk::locX));
141  }
142  m_width /= std::sqrt(size());
143  }
145  m_parent{parent}, m_pos{pos}, m_dir{dir}, m_size{1} {}
146  int NSWSeed::channel(const SeedMeasurement& meas) const {return m_parent->channel(meas);}
147  double NSWSeed::distance(const SeedMeasurement& meas) const {
148  const Amg::Vector3D& A = pos();
149  const Amg::Vector3D& B = dir();
150  const Amg::Vector3D& C = meas.pos();
151  const Amg::Vector3D& D = meas.dir();
152  const double BdotD = B.dot(D);
153  const double divisor = (1. - std::pow(BdotD, 2));
154  const Amg::Vector3D diff = C - A;
155  if (std::abs(divisor) < std::numeric_limits<double>::epsilon()) return diff.mag();
156  const double beta = (diff.dot(B) - diff.dot(D) * BdotD) / divisor;
157  const double delta = (diff.dot(B) * BdotD - diff.dot(D)) / divisor;
158  return (beta * B - delta * D - diff).mag();
159  }
160  bool NSWSeed::add(SeedMeasurement meas, double max_uncert) {
161  if (!size() || !meas) return false;
162  if (find(meas)) return true;
164  Amg::Vector2D lpos_seed{Amg::Vector2D::Zero()};
165  if (!meas->associatedSurface().globalToLocal(intersect.position, dir(), lpos_seed)) return false;
166  // Dont allow seeds outside of active areas to create seeds
167  // Happens rarely but is good protection to reduce wrong channel to track association
168  if (!meas->associatedSurface().insideBounds(lpos_seed)) return false;
169 
170  // Dont save sTGC wires in inner Q1
171  if (m_parent->isWire(meas)) {
172  const sTgcPrepData* prd = static_cast<const sTgcPrepData*>(meas->prepRawData());
173  if (prd->detectorElement()->isEtaZero(prd->identify(), lpos_seed)) return false;
174  }
175 
176  if (m_parent->isPad(meas)) {
177  const sTgcPrepData* prd = dynamic_cast<const sTgcPrepData*>(meas->prepRawData());
178  if (!prd) return false;
179  const MuonGM::MuonPadDesign* design = prd->detectorElement()->getPadDesign(prd->identify());
180  if (!design) return false;
181  Amg::Vector2D padDist = design->distanceToPad(lpos_seed, channel(meas));
182  const double dist = std::hypot(padDist.x(), padDist.y());
183  meas.setDistance(dist);
184  const double uncertD = std::max(1., m_width);
185  return (meas.distance() / uncertD < max_uncert) && insert(meas);
186  }
187  meas.setDistance(distance(meas));
188  const double uncertD = std::max(1.,std::hypot(m_width, Amg::error(meas->localCovariance(), Trk::locX)));
189  if (m_parent->msgLvl(MSG::VERBOSE)) {
190  m_parent->msgStream() << MSG::VERBOSE << m_parent->print(meas) << " is separated from "
191  << to_string(pos())<<" + lambda " <<to_string(dir())<<" " << meas.distance()
192  << ". covariance: " << (meas.distance() / uncertD) << endmsg;
193  }
194  return (meas.distance() / uncertD < max_uncert) && insert(meas);
195  }
198  unsigned int only_th{0}, only_oth{0};
199  for (size_t m = 0; m < m_measurements.size(); ++m) {
200  if (m_measurements[m]== other.m_measurements[m] &&
201  m_phiMeasurements[m] == other.m_phiMeasurements[m])
202  continue;
204  if (!m_measurements[m]) ++only_oth;
205  if (!m_phiMeasurements[m]) ++only_oth;
206  if (!other.m_measurements[m]) ++only_th;
207  if (!other.m_phiMeasurements[m]) ++only_th;
208  }
209  if (only_oth && !only_th) return SeedOR::SubSet;
210  if (only_th && !only_oth) return SeedOR::SuperSet;
211  return res;
212  }
214  MeasVec meas;
215  meas.reserve(size());
216  for (size_t m = 0; m < m_measurements.size(); ++m) {
217  if (m_measurements[m]) meas.push_back(m_measurements[m]);
218  if (m_phiMeasurements[m]) meas.push_back(m_phiMeasurements[m]);
219  if (m_padMeasurements[m]) meas.push_back(m_padMeasurements[m]);
220  }
221  std::sort(meas.begin(), meas.end(), [](const SeedMeasurement& a, const SeedMeasurement& b) {
222  return std::abs(a->globalPosition().z()) < std::abs(b->globalPosition().z());
223  });
224  return meas;
225  }
227  SeedMeasurement meas{cl};
228  meas.setDistance(distance(meas));
229  return insert(std::move(meas));
230  }
232  SeedMeasCache& seed_vec = m_parent->idHelper()->measuresPhi(meas->identify()) ?
234  SeedMeasurement& seed = seed_vec[m_parent->layerNumber(meas)];
235  if (!seed || meas.distance() < seed.distance()) {
236  m_size += !seed;
237  m_chi2 += meas.distance() / Amg::error(meas->localCovariance(), Trk::locX);
238  // From this point the measurement is now the old one
239  std::swap(seed, meas);
240  if (meas) m_chi2 -= meas.distance() / Amg::error(meas->localCovariance(), Trk::locX);
241  return true;
242  }
243  return false;
244  }
245  const Muon::MuonClusterOnTrack* NSWSeed::newCalibClust(std::unique_ptr<const Muon::MuonClusterOnTrack> new_clust) {
246  return m_calibClust.emplace(std::move(new_clust)).first->get();
247  }
248  bool NSWSeed::find(const SeedMeasurement& meas) const {
249  const int lay = m_parent->layerNumber(meas);
250  if (m_parent->isPad(meas)) return m_padMeasurements[lay] == meas;
251  if (m_parent->idHelper()->measuresPhi(meas->identify())) return m_phiMeasurements[lay] == meas;
252  return m_measurements[lay] == meas;
253  }
254 
255  //============================================================================
256  MuonNSWSegmentFinderTool::MuonNSWSegmentFinderTool(const std::string& type, const std::string& name, const IInterface* parent) :
258  declareInterface<IMuonNSWSegmentFinderTool>(this);
259  }
260 
261  //============================================================================
263  ATH_CHECK(m_slTrackFitter.retrieve());
264  ATH_CHECK(m_printer.retrieve());
265  ATH_CHECK(m_edmHelperSvc.retrieve());
266  ATH_CHECK(m_ambiTool.retrieve());
267  ATH_CHECK(m_trackToSegmentTool.retrieve());
268  ATH_CHECK(m_idHelperSvc.retrieve());
269  ATH_CHECK(m_trackCleaner.retrieve());
270  ATH_CHECK(m_trackSummary.retrieve());
271  ATH_CHECK(m_muonClusterCreator.retrieve());
273  ATH_MSG_FATAL("The muon should come from the IP && horizontally from the Calo..."<<
274  "We need to place a very good basball player at the calo exit to deflect the muon horizontally.");
275  return StatusCode::FAILURE;
276  }
277  ATH_MSG_DEBUG(" Max cut " << m_maxClustDist);
278  return StatusCode::SUCCESS;
279  }
280 
282  //============================================================================
283  void MuonNSWSegmentFinderTool::find(const EventContext& ctx, SegmentMakingCache& cache) const {
284 
285  std::vector<const Muon::MuonClusterOnTrack*> muonClusters{};
286  muonClusters.reserve(cache.inputClust.size());
287  std::transform(cache.inputClust.begin(), cache.inputClust.end(), std::back_inserter(muonClusters),
288  [](const std::unique_ptr<const MuonClusterOnTrack>& cl){return cl.get();});
289  ATH_MSG_DEBUG("Entering MuonNSWSegmentFinderTool with " << muonClusters.size() << " clusters to be fit");
290 
291  MuonSegmentVec out_segments{};
292  {
293  MuonSegmentVec stereoSegs = findStereoSegments(ctx, muonClusters, 0);
294  out_segments.insert(out_segments.end(), std::make_move_iterator(stereoSegs.begin()),
295  std::make_move_iterator(stereoSegs.end()));
296  }
297 
298  auto dump_output = [&]() {
299  cache.constructedSegs.reserve(cache.constructedSegs.size() + out_segments.size());
300  for (std::unique_ptr<Muon::MuonSegment>& seg : out_segments) {
301  for (const Trk::MeasurementBase* meas : seg->containedMeasurements()) {
302  const Muon::MuonClusterOnTrack* clus = dynamic_cast<const Muon::MuonClusterOnTrack*>(meas);
303  if (clus) cache.usedHits.insert(clus->identify());
304  }
305  cache.constructedSegs.push_back(std::move(seg));
306  }
307  };
308 
309  std::vector<const Muon::MuonClusterOnTrack*> clustPostStereo{};
311  std::array<std::set<Identifier>, 16> masked_segs{};
312  for (std::unique_ptr<Muon::MuonSegment>& seg : out_segments) {
313  for (const Trk::MeasurementBase* meas : seg->containedMeasurements()) {
314  const Muon::MuonClusterOnTrack* clus = dynamic_cast<const Muon::MuonClusterOnTrack*>(meas);
315  if (clus) masked_segs[layerNumber(clus)].insert(clus->identify());
316  }
317  }
318 
319  if (!out_segments.empty()) {
320  clustPostStereo.reserve(muonClusters.size());
321  for (const Muon::MuonClusterOnTrack* clus : muonClusters) {
322  if (!masked_segs[layerNumber(clus)].count(clus->identify())) clustPostStereo.push_back(clus);
323  }
324  }
325  const std::vector<const Muon::MuonClusterOnTrack*>& segmentInput = !out_segments.empty() ? clustPostStereo : muonClusters;
326 
328  {
329  MuonSegmentVec etaSegs = findStgcPrecisionSegments(ctx, segmentInput);
330  MuonSegmentVec precSegs = find3DSegments(ctx, segmentInput, etaSegs);
331  out_segments.insert(out_segments.end(),
332  std::make_move_iterator(precSegs.begin()),
333  std::make_move_iterator(precSegs.end()));
334  }
335 
336 
337 
338  if (!cache.buildQuads) {
339  dump_output();
340  return;
341  }
343  for (std::unique_ptr<Muon::MuonSegment>& seg : out_segments) {
344  std::vector<const MuonClusterOnTrack*> seg_hits{};
345  seg_hits.reserve(seg->containedMeasurements().size());
346  for (const Trk::MeasurementBase* meas : seg->containedMeasurements()) {
347  const Muon::MuonClusterOnTrack* clus = dynamic_cast<const Muon::MuonClusterOnTrack*>(meas);
348  if (clus) seg_hits.push_back(clus);
349  }
351  for (int iWedge{1}; iWedge<=4 ; ++iWedge) {
352  MeasVec quad_hits = cleanClusters(seg_hits, HitType::Eta | HitType::Phi, iWedge);
354  if ( quad_hits.size () < 2 || ((iWedge == 2 || iWedge == 3) && quad_hits.size() < 4)) continue;
355  std::vector<const Trk::MeasurementBase*> fit_meas{};
356  std::unique_ptr<Trk::PseudoMeasurementOnTrack> pseudoVtx{ipConstraint(ctx)};
357  if (pseudoVtx) fit_meas.push_back(pseudoVtx.get());
358  std::copy(quad_hits.begin(), quad_hits.end(), std::back_inserter(fit_meas));
360  const Trk::Surface& surf = quad_hits.front()->associatedSurface();
361  Trk::Intersection intersect = surf.straightLineIntersection(seg->globalPosition(), seg->globalDirection(), false, false);
362  const Amg::Vector3D& gpos_seg = intersect.position;
363  Amg::Vector3D gdir_seg{seg->globalDirection()};
364  Amg::Vector3D perpos = gpos_seg - 10 * gdir_seg.unit();
365  if (perpos.dot(gdir_seg) < 0) gdir_seg *= -1;
366  Trk::Perigee startpar{perpos, gdir_seg, 0, perpos};
368  std::unique_ptr<Trk::Track> segtrack = fit(ctx, fit_meas, startpar);
369  if (!segtrack) continue;
370 
371  std::unique_ptr<MuonSegment> seg{m_trackToSegmentTool->convert(ctx, *segtrack)};
372  if (seg) {
373  ATH_MSG_VERBOSE(" adding new quad segment " << m_printer->print(*seg) << std::endl
374  <<"position: "<<to_string(seg->globalPosition())<< std::endl
375  <<"direction: "<<to_string(seg->globalDirection())<< std::endl
376  << m_printer->print(seg->containedMeasurements()));
377  seg->setAuthor(Trk::Segment::NswQuadAlign);
378  cache.quadSegs.emplace_back(std::move(seg));
379  }
380  }
381  }
382  dump_output();
383  }
384  MuonSegmentVec MuonNSWSegmentFinderTool::findStereoSegments(const EventContext& ctx,
385  const std::vector<const Muon::MuonClusterOnTrack*>& allClusts,
386  int singleWedge) const {
387 
388  if (!m_useStereoSeeding) return {};
390  LayerMeasVec orderedClust =
391  classifyByLayer(cleanClusters(allClusts, HitType::Eta | HitType::Phi, singleWedge), HitType::Wire | HitType::Pad);
392 
393  for (MeasVec& hitsInLayer : orderedClust) hitsInLayer = vetoBursts(std::move(hitsInLayer));
394  orderedClust.erase(std::remove_if(orderedClust.begin(), orderedClust.end(),
395  [](const MeasVec& vec) { return vec.empty(); }),
396  orderedClust.end());
397  if (orderedClust.empty()) return {};
398 
399 
400  std::vector<NSWSeed> seeds = segmentSeedFromMM(orderedClust);
401  if (seeds.empty()) return {};
404  for (NSWSeed& seed : seeds) {
407  std::vector<const Trk::MeasurementBase*> fit_meas{};
408 
409  std::unique_ptr<Trk::PseudoMeasurementOnTrack> pseudoVtx{ipConstraint(ctx)};
410  if (pseudoVtx) fit_meas.push_back(pseudoVtx.get());
411  MeasVec calib_clust = getCalibratedClusters(seed);
412  std::copy(calib_clust.begin(), calib_clust.end(), std::back_inserter(fit_meas));
414  const Trk::Surface& surf = calib_clust.front()->associatedSurface();
415 
416  Trk::Intersection intersect = surf.straightLineIntersection(seed.pos(), seed.dir(), false, false);
417  const Amg::Vector3D& gpos_seg = intersect.position;
418  Amg::Vector3D gdir_seg{seed.dir()};
419  Amg::Vector3D perpos = gpos_seg - 10 * gdir_seg.unit();
420  if (perpos.dot(gdir_seg) < 0) gdir_seg *= -1;
421 
422  Trk::Perigee startpar{perpos, gdir_seg, 0, perpos};
423 
425  std::unique_ptr<Trk::Track> segtrack = fit(ctx, fit_meas, startpar);
426  if (segtrack) trackSegs.push_back(std::move(segtrack));
427  }
428  return resolveAmbiguities(ctx, trackSegs, Trk::Segment::Author::NswStereoSeeded);
429  }
430 
431  std::unique_ptr<Trk::Track> MuonNSWSegmentFinderTool::fit(const EventContext& ctx,
432  const std::vector<const Trk::MeasurementBase*>& fit_meas,
433  const Trk::TrackParameters& perigee) const {
434  ATH_MSG_VERBOSE("Fit segment from (" << to_string(perigee.position())<< " pointing to " <<
435  to_string(perigee.momentum())<<". Contained measurements in candidate: " << std::endl
436  << m_printer->print(fit_meas));
437  std::unique_ptr<Trk::Track> segtrack = m_slTrackFitter->fit(ctx, fit_meas, perigee, false, Trk::nonInteracting);
438  if (!segtrack) {
439  ATH_MSG_VERBOSE("Fit failed");
440  return nullptr;
441  }
442  ATH_MSG_VERBOSE("--> Fit succeeded");
443  std::unique_ptr<Trk::Track> cleanedTrack = m_trackCleaner->clean(*segtrack, ctx);
444  if (cleanedTrack && cleanedTrack->perigeeParameters() != segtrack->perigeeParameters()) { segtrack.swap(cleanedTrack); }
445  // quality criteria
446  if (!m_edmHelperSvc->goodTrack(*segtrack, 10)) {
447  if (segtrack->fitQuality()) {
448  ATH_MSG_DEBUG("Segment fit with chi^2/nDoF = " << segtrack->fitQuality()->chiSquared() << "/"
449  << segtrack->fitQuality()->numberDoF());
450  }
451  return nullptr;
452  }
453  // update the track summary and add the track to the collection
454  m_trackSummary->updateTrack(*segtrack);
455  ATH_MSG_VERBOSE("Segment accepted with chi^2/nDoF = " << segtrack->fitQuality()->chiSquared() << "/"
456  << segtrack->fitQuality()->numberDoF());
457  return segtrack;
458  }
459 
460  //============================================================================
461  // find the precision (eta) segments
462  MuonSegmentVec MuonNSWSegmentFinderTool::findStgcPrecisionSegments(const EventContext& ctx,
463  const std::vector<const Muon::MuonClusterOnTrack*>& muonClusters,
464  int singleWedge) const {
465  // clean the muon clusters; select only the eta hits.
466  // in single-wedge mode the eta seeds are retrieved from the specific wedge
467  MeasVec clusters = cleanClusters(muonClusters, HitType::Eta, singleWedge); // eta hits only
468  ATH_MSG_VERBOSE(" After hit cleaning, there are " << clusters.size() << " precision 2D clusters");
469 
470  // classify eta clusters by layer
471  LayerMeasVec orderedClusters = classifyByLayer(clusters, 0);
472  if (orderedClusters.size() < 4) return {}; // at least four layers with eta hits (MM and sTGC)
473 
474  // create segment seeds
475  std::vector<NSWSeed> seeds = segmentSeedFromStgc(orderedClusters, false);
476  ATH_MSG_DEBUG(" Found " << seeds.size() << " 2D seeds");
477  // Loop on seeds: find all clusters near the seed and try to fit
478  MeasVec etaHitVec, phiHitVec;
479  TrackCollection segTrkColl{SG::OWN_ELEMENTS};
480 
481  for (NSWSeed& seed : seeds) {
482  if (seed.size() < 4) continue;
483 
484  etaHitVec = seed.measurements();
485  const Trk::PlaneSurface& surf = static_cast<const Trk::PlaneSurface&>(etaHitVec.front()->associatedSurface());
486  // calculate start parameters for the fit
487  // local position and direction of the eta-seed on the surface of the first cluster
488  Trk::Intersection intersect = surf.straightLineIntersection(seed.pos(), seed.dir(), false, false);
489  Amg::Vector2D lpos_seed{Amg::Vector2D::Zero()};
490  Trk::LocalDirection ldir_seed{};
491  surf.globalToLocal(intersect.position, intersect.position, lpos_seed);
492  surf.globalToLocalDirection(seed.dir(), ldir_seed);
493 
494  // use the seed info to generate start parameters (dummy values for phi)
495  Amg::Vector2D lpos(lpos_seed[Trk::locX], 0.);
496  Trk::LocalDirection ldir(ldir_seed.angleXZ(), -M_PI_2);
497  Amg::Vector3D gpos_seg{Amg::Vector3D::Zero()}, gdir_seg{Amg::Vector3D::Zero()};
498  surf.localToGlobal(lpos, gpos_seg, gpos_seg);
499  surf.localToGlobalDirection(ldir, gdir_seg);
500 
501  Amg::Vector3D perpos = gpos_seg - 10 * gdir_seg.unit();
502  if (perpos.dot(gdir_seg) < 0) gdir_seg *= -1;
503  const auto startpar = Trk::Perigee(perpos, gdir_seg, 0, perpos);
504  ATH_MSG_VERBOSE(" start parameter " << perpos << " pp " << startpar.position() << " gd " << gdir_seg.unit() << " pp "
505  << startpar.momentum().unit());
506 
507  // fit the hits
508  hitsToTrack(ctx, etaHitVec, phiHitVec, startpar, segTrkColl);
509  }
511  return resolveAmbiguities(ctx, segTrkColl, Trk::Segment::Author::NswStgcSeeded);
512  }
513  MuonSegmentVec MuonNSWSegmentFinderTool::resolveAmbiguities(const EventContext& ctx,
514  const TrackCollection& segTrkColl,
515  const Trk::Segment::Author a) const {
516  if (msgLvl(MSG::DEBUG)) {
517  ATH_MSG_DEBUG("Tracks before ambi solving: ");
518  for (const Trk::Track* trk : segTrkColl) {
519  ATH_MSG_DEBUG(m_printer->print(*trk));
520  const DataVector<const Trk::MeasurementBase>* meas = trk->measurementsOnTrack();
521  if (meas) ATH_MSG_DEBUG(m_printer->print(meas->stdcont()));
522  }
523  }
524 
525  MuonSegmentVec segments{};
526  std::unique_ptr<const TrackCollection> resolvedTracks(m_ambiTool->process(&segTrkColl));
527  ATH_MSG_DEBUG("Resolved track candidates: old size " << segTrkColl.size() << " new size " << resolvedTracks->size());
528 
529  // store the resolved segments
530  for (const Trk::Track* trk : *resolvedTracks) {
531  const auto* measurements = trk->measurementsOnTrack();
532  const bool has_eta = std::find_if(measurements->begin(), measurements->end(),
533  [this](const Trk::MeasurementBase* meas) {
534  Identifier id = m_edmHelperSvc->getIdentifier(*meas);
535  return id.is_valid() && !m_idHelperSvc->measuresPhi(id);
536  }) != trk->measurementsOnTrack()->end();
537  if (!has_eta) continue;
538  std::unique_ptr<MuonSegment> seg{m_trackToSegmentTool->convert(ctx, *trk)};
539  if (seg) {
540  ATH_MSG_DEBUG(" adding " << m_printer->print(*seg) << std::endl << m_printer->print(seg->containedMeasurements()));
541  seg->setAuthor(a);
542  segments.emplace_back(std::move(seg));
543  } else {
544  ATH_MSG_VERBOSE("Segment conversion failed, no segment created. ");
545  }
546  }
547  return segments;
548  }
549  //============================================================================
550  MuonSegmentVec MuonNSWSegmentFinderTool::find3DSegments(const EventContext& ctx,
551  const std::vector<const Muon::MuonClusterOnTrack*>& muonClusters,
552  MuonSegmentVec& etaSegs,
553  int singleWedge) const {
554  MuonSegmentVec segments{};
555  // cluster cleaning #1; select only phi hits (must be from all wedges, in order to phi-seed)
556  MeasVec phiClusters = cleanClusters(muonClusters, HitType::Phi | HitType::Wire, singleWedge);
557  ATH_MSG_DEBUG("After hit cleaning, there are " << phiClusters.size() << " phi clusters to be fit");
558  // classify the phi clusters by layer
559  LayerMeasVec orderedWireClusters = classifyByLayer(phiClusters, HitType::Wire);
560  LayerMeasVec orderedPadClusters = classifyByLayer(phiClusters, HitType::Pad); // pads only
561  if (orderedWireClusters.size() + orderedPadClusters.size() < 2) {
562  ATH_MSG_DEBUG("Not enough phi hits present, cannot perform the 3D fit!");
563  segments.insert(segments.end(), std::make_move_iterator(etaSegs.begin()), std::make_move_iterator(etaSegs.end()));
564  return segments;
565  }
566 
567  // cluster cleaning #2; select only eta hits
568  MeasVec etaClusters = cleanClusters(muonClusters, HitType::Eta, singleWedge);
569  LayerMeasVec orderedEtaClusters = classifyByLayer(etaClusters, HitType::Eta);
570 
571  // loop on eta segments
572  bool triedWireSeed{false}; // wire seeds need to be retrieved only once (the first time they are needed)
573  std::vector<NSWSeed> seeds_WiresSTGC;
574  TrackCollection segTrkColl{SG::OWN_ELEMENTS};
575  // Loop on eta segments
576  for (std::unique_ptr<Muon::MuonSegment>& etaSeg : etaSegs) {
577  bool is3Dseg{false};
578  NSWSeed seed2D{this, *etaSeg};
579  getClustersOnSegment(orderedEtaClusters, seed2D, {}); // eta clusters
580 
581  std::vector<NSWSeed> seeds;
584  if (std::abs(etaSeg->globalPosition().eta()) < 2.4) {
585  if (!triedWireSeed) {
586  // wire seeds need to be retrieved only once (they don't depend on the eta segment)
587  triedWireSeed = true;
588  seeds_WiresSTGC = segmentSeedFromStgc(orderedWireClusters, true);
589  }
590 
591  if (!seeds_WiresSTGC.empty()) {
592  seeds = seeds_WiresSTGC;
593  ATH_MSG_DEBUG(" Seeding from sTGC wires");
594  }
595  }
596 
597  // 3 - last resort, try sTGC pads
598  if (seeds.empty()) {
599  seeds = segmentSeedFromPads(orderedPadClusters, *etaSeg);
600  ATH_MSG_DEBUG(" Seeding from sTGC pads");
601  }
602 
603  // Loop on phi seeds
604  MeasVec phiHitVec;
605  const Trk::PlaneSurface& etaSegSurf = etaSeg->associatedSurface();
606  double etaSegLocX = etaSeg->localParameters()[Trk::locX];
607  double etaSegLocXZ = etaSeg->localDirection().angleXZ();
608 
609  for (NSWSeed& seed : seeds) {
610  // calculate start parameters for the fit
611  // combine the local position and direction of the eta-seed (segment)
612  // and local position and direction of the phi-seed to generate 3D starting parameters
613  Trk::Intersection intersect = etaSegSurf.straightLineIntersection(seed.pos(), seed.dir(), false, false);
614  Amg::Vector2D lpos_seed{Amg::Vector2D::Zero()};
615  Trk::LocalDirection ldir_seed{};
616  etaSegSurf.globalToLocal(intersect.position, intersect.position, lpos_seed);
617  etaSegSurf.globalToLocalDirection(seed.dir(), ldir_seed);
618 
619  Amg::Vector2D lpos_seg(etaSegLocX, lpos_seed[Trk::locY]);
620  Trk::LocalDirection ldir_seg(etaSegLocXZ, ldir_seed.angleYZ());
621 
622  Amg::Vector3D gpos_seg{Amg::Vector3D::Zero()}, gdir_seg{Amg::Vector3D::Zero()};
623  etaSegSurf.localToGlobal(lpos_seg, gpos_seg, gpos_seg);
624  etaSegSurf.localToGlobalDirection(ldir_seg, gdir_seg);
625 
626  Amg::Vector3D perpos = gpos_seg - 10 * gdir_seg.unit();
627  if (perpos.dot(gdir_seg) < 0) gdir_seg *= -1;
628  const Trk::Perigee startpar{perpos, gdir_seg, 0, perpos};
629 
630  NSWSeed seed3D{this, perpos, gdir_seg};
631 
632  // gather phi hits aligned with the segment
633  int nPhiHits = getClustersOnSegment(orderedPadClusters, seed3D,{}); // add pad hits (from the requested wedge if any)
634  nPhiHits += getClustersOnSegment(orderedWireClusters, seed3D, {}); // add wire hits (from the requested wedge if any)
635  if (nPhiHits < 2) continue; // at least two phi hits
636 
637  MeasVec phiHitVec = seed3D.measurements();
638  // calibrate the eta hits
639  MeasVec etaHitsCalibrated = getCalibratedClusters(seed2D);
640 
641  // fit
642  if (hitsToTrack(ctx, etaHitsCalibrated, phiHitVec, startpar, segTrkColl)) {
643  is3Dseg = true;
644  ATH_MSG_VERBOSE("Segment successfully fitted for wedge "<<singleWedge<<std::endl<<
645  m_printer->print(*segTrkColl.back()));
646  }
647  } // end loop on phi seeds
648 
649  // if we failed to combine the eta segment with phi measurements,
650  // just add the eta segment to the collection.
651  if (!is3Dseg) { segments.push_back(std::move(etaSeg)); }
652  } // end loop on precision plane segments
653  MuonSegmentVec new_segs = resolveAmbiguities(ctx, segTrkColl, Trk::Segment::NswStgcSeeded);
654  segments.insert(segments.end(), std::make_move_iterator(new_segs.begin()), std::make_move_iterator(new_segs.end()));
655  return segments;
656  }
657 
658  std::unique_ptr<Trk::PseudoMeasurementOnTrack> MuonNSWSegmentFinderTool::caloConstraint(const Trk::TrackParameters& startpar) const {
659  if (!m_caloConstraint) return nullptr;
660  constexpr double errVtx{1.*Gaudi::Units::m};
661  Amg::MatrixX covVtx(2,2);
662  covVtx.setIdentity();
663  covVtx = errVtx * errVtx * covVtx;
664  const Amg::Vector3D parPos{startpar.position()};
665  Amg::Vector3D projection{parPos.x(), parPos.y(), 0};
666  // project
667  Trk::PerigeeSurface perVtx(projection);
668  return std::make_unique<Trk::PseudoMeasurementOnTrack>(Trk::LocalParameters(Trk::DefinedParameter(0, Trk::locX),
670  std::move(covVtx),
671  std::move(perVtx));
672 
673  }
674 
675  std::unique_ptr<Trk::PseudoMeasurementOnTrack> MuonNSWSegmentFinderTool::ipConstraint(const EventContext& /*ctx*/) const {
676  if (!m_ipConstraint) return nullptr;
677  constexpr double errVtx{10.*Gaudi::Units::cm};
678  Amg::MatrixX covVtx(1, 1);
679  covVtx(0, 0) = errVtx * errVtx;
682  return std::make_unique<Trk::PseudoMeasurementOnTrack>(Trk::LocalParameters(Trk::DefinedParameter(0, Trk::locX)), std::move(covVtx),
683  std::move(perVtx));
684  }
686  const Identifier id = clust->identify();
687  return m_idHelperSvc->issTgc(id) && m_idHelperSvc->stgcIdHelper().channelType(id) == sTgcIdHelper::Pad;
688  }
690  const Identifier id = clust->identify();
691  return m_idHelperSvc->issTgc(id) && m_idHelperSvc->stgcIdHelper().channelType(id) == sTgcIdHelper::Strip;
692  }
694  const Identifier id = clust->identify();
695  return m_idHelperSvc->issTgc(id) && m_idHelperSvc->stgcIdHelper().channelType(id) == sTgcIdHelper::Wire;
696  }
697  //============================================================================
698  bool MuonNSWSegmentFinderTool::hitsToTrack(const EventContext& ctx,
699  const MeasVec& etaHitVec,
700  const MeasVec& phiHitVec,
701  const Trk::TrackParameters& startpar,
702  TrackCollection& segTrkColl) const {
703  // vector of hits for the fit
704  std::vector<const Trk::MeasurementBase*> vecFitPts;
705  unsigned int nHitsEta = etaHitVec.size();
706  unsigned int nHitsPhi = phiHitVec.size();
707  vecFitPts.reserve(nHitsEta + nHitsPhi + 2 + m_ipConstraint + m_caloConstraint);
708 
709  std::unique_ptr<Trk::PseudoMeasurementOnTrack> pseudoVtx{ipConstraint(ctx)},
710  pseudoVtxCalo{caloConstraint(startpar)},
711  pseudoPhi1{nullptr}, pseudoPhi2{nullptr};
712  // is chosen, add a pseudo measurement as vtx at the center of ATLAS
713  if (pseudoVtx) { vecFitPts.push_back(pseudoVtx.get()); }
714  if (pseudoVtxCalo) { vecFitPts.push_back(pseudoVtxCalo.get()); }
715 
716  if (!nHitsPhi) {
717  // generate two pseudo phi measurements for the fit,
718  // one on the first hit surface and one on the last hit surface.
719  const unsigned int nMM = std::count_if(etaHitVec.begin(), etaHitVec.end(),
720  [this](const Muon::MuonClusterOnTrack* hit) {
721  return m_idHelperSvc->isMM(hit->identify());
722  });
723  double errPos = (nMM) ? 1000. : 0.1;
724  Amg::MatrixX cov(1, 1);
725  cov(0, 0) = errPos * errPos;
726  static const Trk::LocalParameters loc_pseudopars{Trk::DefinedParameter(0, Trk::locY)};
727  pseudoPhi1 = std::make_unique<Trk::PseudoMeasurementOnTrack>(Trk::LocalParameters(loc_pseudopars),
728  Amg::MatrixX(cov),
729  etaHitVec.front()->associatedSurface());
730  pseudoPhi2 = std::make_unique<Trk::PseudoMeasurementOnTrack>(Trk::LocalParameters(loc_pseudopars),
731  Amg::MatrixX(cov),
732  etaHitVec.back()->associatedSurface());
733 
734  // add the first pseudo phi hit, the hit vector, and the second pseudo phi hit
735  vecFitPts.push_back(pseudoPhi1.get());
736  std::copy(etaHitVec.begin(), etaHitVec.end(), std::back_inserter(vecFitPts));
737  vecFitPts.push_back(pseudoPhi2.get());
738  ATH_MSG_VERBOSE("Fitting a 2D-segment track with " << nHitsEta << " Eta hits");
739 
740  } else {
741  // sorted eta and sorted phi hits combined (sorted by their the z-coordinate)
742  std::merge(phiHitVec.begin(), phiHitVec.end(), etaHitVec.begin(), etaHitVec.end(), std::back_inserter(vecFitPts),
744  double z1 = std::abs(c1->detectorElement()->center(c1->identify()).z());
745  double z2 = std::abs(c2->detectorElement()->center(c2->identify()).z());
746  return z1 < z2;
747  });
748  ATH_MSG_VERBOSE("Fitting a 3D-segment track with " << nHitsEta << " Eta hits and " << nHitsPhi << " Phi hits");
749  }
750 
751  // fit the hits and generate the Trk::Track
752  std::unique_ptr<Trk::Track> segtrack = fit(ctx, vecFitPts, startpar);
753  if (!segtrack) return false;
754  segTrkColl.push_back(std::move(segtrack));
755  return true;
756  }
757 
758  //============================================================================
760  const std::vector<const Muon::MuonClusterOnTrack*>& muonClusters, int hit_sel, int singleWedge /*= 0*/) const {
761  // Keep only eta (MM && sTGC) or phi (sTGC) clusters
762  // In single-wedge mode keep only clusters from the requested wedge
764  clusters.reserve(muonClusters.size());
765  for (const Muon::MuonClusterOnTrack* cluster : muonClusters) {
766  if (!cluster) continue;
767  if (singleWedge && singleWedge != wedgeNumber(cluster)) continue;
768  const Identifier id = cluster->identify();
769  if (((hit_sel & HitType::Eta) && !m_idHelperSvc->measuresPhi(id)) ||
770  ((hit_sel & HitType::Phi) && m_idHelperSvc->measuresPhi(id)))
771  clusters.emplace_back(cluster);
772  }
773  return clusters;
774  }
775 
776  //============================================================================
778  const MeasVec& clusters, int hit_sel) const {
779  // Classifies clusters by layer, starting from the layer closest to the IP and moving outwards.
780  // "clusters" is expected to contain only eta (MM+sTGC strip) or only phi hits (sTGC pads XOR wires).
781  // The returned vector contains only layers that have hits.
782 
783  LayerMeasVec orderedClusters(16);
784  std::array<std::set<Identifier>,16> used_hits{};
785  int nBad{0};
786  for (const Muon::MuonClusterOnTrack* hit : clusters) {
787  const int iorder = layerNumber(hit);
788  if (iorder < 0) {
789  ++nBad;
790  continue;
791  }
792  const Identifier id = hit->identify();
793  if (m_idHelperSvc->issTgc(id)) {
794  const int channelType = m_idHelperSvc->stgcIdHelper().channelType(id);
795  // skip sTGC pads if using wires, or skip wires if using pads
796  if (!(hit_sel & HitType::Pad) && channelType == sTgcIdHelper::Pad) continue;
797  if (!(hit_sel & HitType::Wire) && channelType == sTgcIdHelper::Wire) continue;
798  }
799  std::set<Identifier>& lay_hits = used_hits[iorder];
800  if (lay_hits.count(id)) continue;
801  lay_hits.insert(id);
802  orderedClusters[iorder].emplace_back(hit);
803  }
804  if (nBad) ATH_MSG_WARNING("Unable to classify " << nBad << " clusters by their layer since they are neither MM nor sTGC");
805 
806  // Erase layers without hits
807  orderedClusters.erase(std::remove_if(orderedClusters.begin(), orderedClusters.end(),
808  [](const MeasVec& vec) { return vec.empty(); }),
809  orderedClusters.end());
810 
811 
812  for( MeasVec& lays: orderedClusters){
813  std::sort(lays.begin(),lays.end(), [this](const SeedMeasurement& a, const SeedMeasurement& b){
814  return channel(a) < channel(b);
815  });
816  ATH_MSG_DEBUG("Found in layer "<<m_idHelperSvc->toStringDetEl(lays[0]->identify())<<" "<<lays.size()<<" clusters");
817 
818 
819  }
820  ATH_MSG_VERBOSE("Collected clusters "<<print(orderedClusters));
821 
822  return orderedClusters;
823  }
824 
825  //============================================================================
826  std::vector<NSWSeed> MuonNSWSegmentFinderTool::segmentSeedFromStgc(const LayerMeasVec& orderedClusters,
827  bool usePhi) const {
828  std::vector<NSWSeed> seeds;
829 
830  // oderedClusters should contain either eta clusters (MM and sTGC)
831  // or sTGC phi hits. For MM phi, use the dedicated function.
832  if (orderedClusters.size() < 4) return seeds;
833 
834  // Create seeds using each pair of hits on the two most distant layers (that containing hits).
835  // m_nOfSeedLayers (default = 1) dictates whether we want to also use hits from inner layers.
836 
837  // Loop on layers to get the first seed point
838  int seedingLayersL{0};
839  for (unsigned int ilayerL{0}; (ilayerL < orderedClusters.size() && seedingLayersL < m_nOfSeedLayers); ++ilayerL) {
840  bool usedLayerL{false};
841  for (const SeedMeasurement& hitL : orderedClusters[ilayerL]) {
843  if (usePhi != m_idHelperSvc->measuresPhi(hitL->identify())) continue;
844  else if(m_idHelperSvc->isMM(hitL->identify())) break;
845 
846 
847  // For the second point, loop on layers in reverse to be as far as possible from the first.
848  int seedingLayersR{0};
849  for (unsigned int ilayerR = orderedClusters.size() - 1; (ilayerR > ilayerL && seedingLayersR < m_nOfSeedLayers);
850  --ilayerR) {
851  bool usedLayerR{false};
852  for (const SeedMeasurement& hitR : orderedClusters[ilayerR]) {
853  if (usePhi != m_idHelperSvc->measuresPhi(hitR->identify())) continue;
854  else if (m_idHelperSvc->isMM(hitR->identify())) break;
855  NSWSeed seed{this,hitL, hitR};
856  if (!usePhi && m_ipConstraint) {
857  const double eta = seed.dir().perp() > std::numeric_limits<float>::epsilon() ? std::abs(seed.dir().eta()): FLT_MAX;
858  if (eta < minEtaNSW || eta > maxEtaNSW) {
859  continue;
860  }
861  }
862  usedLayerR = true;
863  usedLayerL = true;
864  getClustersOnSegment(orderedClusters, seed, {ilayerL, ilayerR});
865  seeds.emplace_back(std::move(seed));
866 
867  }
868  if (usedLayerR) ++seedingLayersR;
869  }
870  }
871  if (usedLayerL) ++seedingLayersL;
872  }
873 
874  return resolveAmbiguities(std::move(seeds));
875  }
876 
877  //============================================================================
879  if (m_idHelperSvc->isMM(cluster->identify()))
880  return m_idHelperSvc->mmIdHelper().multilayer(cluster->identify()) + 1; // [IP:2, HO:3]
881  if (m_idHelperSvc->issTgc(cluster->identify()))
882  return 3 * (m_idHelperSvc->stgcIdHelper().multilayer(cluster->identify()) - 1) + 1; // [IP:1, HO:4];
883  return -1;
884  }
886  // Internal logic. Initialize with 16 layers:
887  // [0-3] for the four sTGC IP layers
888  // [4-11] for the eight MM IP+HO layers (empty when phi hits are requested)
889  // [12-15] for the four sTGC HO layers
890  int layer{0};
891  if (m_idHelperSvc->isMM(cluster->identify())) layer = m_idHelperSvc->mmIdHelper().gasGap(cluster->identify());
892  if (m_idHelperSvc->issTgc(cluster->identify())) layer = m_idHelperSvc->stgcIdHelper().gasGap(cluster->identify());
893  return 4 * (wedgeNumber(cluster) - 1) + layer - 1;
894  }
896  if (m_idHelperSvc->isMM(cluster->identify())) return m_idHelperSvc->mmIdHelper().channel(cluster->identify());
897  if (m_idHelperSvc->issTgc(cluster->identify())) return m_idHelperSvc->stgcIdHelper().channel(cluster->identify());
898  return -1;
899  }
900 
901  //============================================================================
903  NSWSeed& seed, const std::set<unsigned int>& exclude) const {
904  ATH_MSG_VERBOSE(" getClustersOnSegment: layers " << orderedclusters.size());
905  int nHitsAdded{0};
906  for (const MeasVec& surfHits : orderedclusters) {
907  if (exclude.count(layerNumber(surfHits[0]))) continue;
908  // get the best hit candidate on this layer
909  for (const SeedMeasurement& hit : surfHits) { nHitsAdded += seed.add(hit, m_maxClustDist); }
910  }
911  ATH_MSG_VERBOSE(" getClustersOnSegment: returning " << nHitsAdded << " hits ");
912  return nHitsAdded;
913  }
914 
915  //============================================================================
917  const LayerMeasVec& orderedClusters, const Muon::MuonSegment& etaSeg) const {
918  std::vector<NSWSeed> seeds;
920  if (orderedClusters.empty()) return seeds;
921 
922  std::vector<std::vector<const Muon::sTgcPrepData*>> sTgcIP(4); // IP: layers nearest to the IP will be added first
923  std::vector<std::vector<const Muon::sTgcPrepData*>> sTgcHO(4); // HO: layers furthest from the IP will be added first
924 
925  // Process clusters separately for each multilayer
926  for (int iml : {1, 2}) {
927  int il = (iml == 1) ? 0 : orderedClusters.size() - 1;
928  int iend = (iml == 1) ? orderedClusters.size() : -1;
929  int idir = (iml == 1) ? 1 : -1;
930  unsigned int nLayersWithHitMatch{0};
931 
932  // Loop on layers (reverse loop for HO)
933  for (; il != iend; il += idir) {
934  double lastDistance{1000.};
935  if (nLayersWithHitMatch >= sTgcIP.size()) {
936  sTgcIP.resize(nLayersWithHitMatch + 1);
937  sTgcHO.resize(nLayersWithHitMatch + 1);
938  }
939  std::vector<const Muon::sTgcPrepData*>& matchedHits =
940  (iml == 1) ? sTgcIP.at(nLayersWithHitMatch) : sTgcHO.at(nLayersWithHitMatch);
941 
942  // Loop on the hits on this layer. Find the one closest (in eta) to the segment intersection.
943  for (const Muon::MuonClusterOnTrack* rio : orderedClusters[il]) {
944  const sTgcPrepData* padHit = dynamic_cast<const sTgcPrepData*>(rio->prepRawData());
945  if (!padHit) continue;
946 
947  // check the multilayer the hit is on
948  if (m_idHelperSvc->stgcIdHelper().multilayer(padHit->identify()) != iml) continue;
949 
950  const MuonGM::MuonPadDesign* design = padHit->detectorElement()->getPadDesign(padHit->identify());
951  if (!design) continue;
952 
953  // local position of the segment intersection with the plane
954  const Trk::Surface& surf = padHit->detectorElement()->surface(padHit->identify());
956  surf.straightLineIntersection(etaSeg.globalPosition(), etaSeg.globalDirection(), false, false);
957  Amg::Vector2D segLocPosOnSurf{Amg::Vector2D::Zero()};
958  surf.globalToLocal(intersect.position, intersect.position, segLocPosOnSurf);
959 
960  // eta distance between the hit and the segment intersection with the plane
961  // check that it's no more than half of the pad eta-pitch.
962  double chWidth = design->channelWidth(padHit->localPosition(), false);
963  double etaDistance = std::abs(padHit->localPosition().y() - segLocPosOnSurf[1]);
964  if (etaDistance > 0.5 * chWidth) continue;
965  ATH_MSG_DEBUG(" etaDistance " << etaDistance << " between pad center and position on the pad.");
966 
967  if (matchedHits.empty()) {
968  // first hit
969  matchedHits.push_back(padHit);
970  ATH_MSG_DEBUG(" best etaDistance: " << etaDistance);
971  } else if (std::abs(etaDistance - lastDistance) < 0.001) {
972  // competing hit pad, keep both (all hit pads of the same eta row will be candidates)
973  matchedHits.push_back(padHit);
974  ATH_MSG_DEBUG(" added etaDistance: " << etaDistance << " size " << matchedHits.size());
975  } else if (etaDistance < lastDistance) {
976  // found a better hit; clear the old ones (possible only for clustered pad hits)
977  matchedHits.clear();
978  matchedHits.push_back(padHit);
979  ATH_MSG_DEBUG(" replacing best etaDistance with: " << etaDistance);
980  } else {
981  continue;
982  }
983  lastDistance = etaDistance;
984  } // end of loop on hits
985 
986  if (!matchedHits.empty()) ++nLayersWithHitMatch;
987 
988  } // end of loop on layers
989 
990  // need at least one hit in each multilayer to create a seed
991  if (!nLayersWithHitMatch) return seeds;
992 
993  } // end of loop on multilayers
994 
995  // get refined phi ranges on each ml, by taking into account pad staggering
996  std::vector<std::pair<double, double>> sTgcIP_phiRanges = getPadPhiOverlap(sTgcIP);
997  std::vector<std::pair<double, double>> sTgcHO_phiRanges = getPadPhiOverlap(sTgcHO);
998 
999  // reference prds on the outermost hit surfaces
1000  const sTgcPrepData* prdL1 = sTgcIP.front().front();
1001  const sTgcPrepData* prdL2 = sTgcHO.front().front();
1002  const auto& surfPrdL1 = prdL1->detectorElement()->surface();
1003  const auto& surfPrdL2 = prdL2->detectorElement()->surface();
1004 
1005  // create a seed for each combination of IP and HO points
1006  for (const std::pair<double, double>& range1 : sTgcIP_phiRanges) {
1007  double midPhi1 = 0.5 * (range1.first + range1.second);
1008  Amg::Vector2D lp1(midPhi1, prdL1->localPosition().y());
1010  surfPrdL1.localToGlobal(lp1, gpL1, gpL1);
1011 
1012  for (const std::pair<double, double>& range2 : sTgcHO_phiRanges) {
1013  double midPhi2 = 0.5 * (range2.first + range2.second);
1014  Amg::Vector2D lp2(midPhi2, prdL2->localPosition().y());
1016  surfPrdL2.localToGlobal(lp2, gpL2, gpL2);
1017  // create the seed taking the average position (w.r.t. IP)
1018  // as global direction (as for an infinite momentum track).
1019  Amg::Vector3D gDir = (gpL2 + gpL1).unit();
1020  seeds.emplace_back(this, gpL1, gDir);
1021  }
1022  }
1023 
1024  ATH_MSG_DEBUG(" segmentSeedFromPads: seeds.size() " << seeds.size());
1025  return seeds;
1026  }
1027 
1028  //============================================================================
1030  const LayerMeasVec& orderedClusters) const {
1031  std::vector<NSWSeed> seeds;
1032  std::array<unsigned int, 4> layers{};
1033  unsigned int trials{0}, used_layers{0};
1035  constexpr size_t lastMMLay = 11;
1036  std::vector<NSWSeed> laySeeds;
1037 
1039  std::stringstream sstr{};
1040  for (int e4 = std::min(lastMMLay, orderedClusters.size() -1); e4 >= 3 ; --e4) {
1041  layers[3] = e4;
1042  for (int e3 = e4 -1 ; e3 >= 2; --e3) {
1043  layers[2] = e3;
1044  for (int e2 = 1 ; e2 < e3; ++e2) {
1045  layers[1] = e2;
1046  for (int e1= 0; e1< e2; ++e1) {
1047  layers[0] = e1;
1048  const unsigned int old_trials = trials;
1049  laySeeds = segmentSeedFromMM(orderedClusters,layers, trials);
1050  if (old_trials == trials) continue;
1051 
1052  used_layers += !laySeeds.empty();
1053  seeds.insert(seeds.end(), std::make_move_iterator(laySeeds.begin()),
1054  std::make_move_iterator(laySeeds.end()));
1055 
1056  if (msgLvl(MSG::VERBOSE)) {
1057  sstr<<" Attempts thus far "<<old_trials<<" attempts now "<<trials<<" --- "<<e1<<","<<e2<<","<<e3<<","<<e4<<std::endl;
1058  for (int lay : layers) {
1059  sstr<<"Layer: "<<lay<<" number of measurements "<<orderedClusters[lay].size()<<std::endl;
1060  for (const SeedMeasurement& meas : orderedClusters[lay] ){
1061  sstr<<" **** "<< print(meas)<<std::endl;
1062  }
1063  sstr<<std::endl<<std::endl<<std::endl;
1064  }
1065  }
1066  }
1067  }
1068  }
1069  }
1070  if (trials > 100000) {
1071  ATH_MSG_VERBOSE(sstr.str());
1072  }
1073  ATH_MSG_VERBOSE("Out of "<<trials<<" possible seeds, "<<seeds.size()<<" were finally built. Used in total "<<used_layers<<" layers");
1074  return resolveAmbiguities(std::move(seeds));
1075  }
1076 
1077  #if defined(FLATTEN)
1078  ATH_FLATTEN
1079  #endif
1080  inline std::vector<NSWSeed> MuonNSWSegmentFinderTool::segmentSeedFromMM(const LayerMeasVec& orderedClusters,
1081  std::array<unsigned int,4> selLayers,
1082  unsigned int& trial_counter) const {
1083  std::vector<NSWSeed> seeds{};
1084 
1085  std::array<unsigned int, 4> lay_ord{};
1086  for (size_t s = 0; s < selLayers.size(); ++s) {
1087  unsigned int lay = selLayers[s];
1088  const SeedMeasurement& seed = orderedClusters[lay].front();
1089  const Identifier id = seed->identify();
1090  if (!m_idHelperSvc->isMM(id)) return seeds;
1091  const MuonGM::MuonChannelDesign* design = getDesign(seed);
1093  if (!design->hasStereoAngle()) lay_ord[s] = 1;
1094  else if (design->stereoAngle() >0.) lay_ord[s] = 2;
1095  else lay_ord[s] = 3;
1096  }
1097  auto swap_strips = [&selLayers, &lay_ord] (unsigned int i, unsigned j){
1098  std::swap(lay_ord[i], lay_ord[j]);
1099  std::swap(selLayers[i],selLayers[j]);
1100  };
1102  if (lay_ord[0] == lay_ord[1]){
1103  if (lay_ord[1] != lay_ord[2]) swap_strips(1,2);
1104  else if (lay_ord[1] != lay_ord[3]) swap_strips(1,3);
1105  else {
1106  ATH_MSG_VERBOSE("Strips are all parallel.");
1107  return seeds;
1108  }
1109  }
1111  if (lay_ord[2] == lay_ord[3]) {
1112  // Check if the last hit can be exchanged by the first one.
1113  // But also ensure that the second and fourth are not the same
1114  if (lay_ord[3] != lay_ord[0] && lay_ord[3] != lay_ord[1]) swap_strips(3,0);
1115  else {
1116  ATH_MSG_VERBOSE("No way to rearrange the strips such that the latter two strips cross.");
1117  return seeds;
1118  }
1119  }
1121  std::array<SeedMeasurement, 4> base_seed{};
1122  for (size_t s = 0; s < selLayers.size(); ++s) {
1123  unsigned int lay = selLayers[s];
1124  base_seed[s] = orderedClusters[lay].front();
1125  }
1126 
1127  const double A = (base_seed[1].pos().z() - base_seed[0].pos().z());
1128  const double G = (base_seed[2].pos().z() - base_seed[0].pos().z());
1129  const double K = (base_seed[3].pos().z() - base_seed[0].pos().z());
1130 
1131  AmgSymMatrix(2) diamond{AmgSymMatrix(2)::Zero()};
1132  diamond.block<2, 1>(0, 1) = ((A - G) * base_seed[3].dirDot(base_seed[1]) * base_seed[1].dir() + G * base_seed[3].dirDot(base_seed[0]) * base_seed[0].dir() - A * base_seed[3].dir()).block<2, 1>(0, 0);
1133  diamond.block<2, 1>(0, 0) = ((K - A) * base_seed[2].dirDot(base_seed[1]) * base_seed[1].dir() - K * base_seed[2].dirDot(base_seed[0]) * base_seed[0].dir() + A * base_seed[2].dir()).block<2, 1>(0, 0);
1134 
1135  if (std::abs(diamond.determinant()) < std::numeric_limits<float>::epsilon()) {
1136  ATH_MSG_VERBOSE(" The seed built from " << printSeed(base_seed) << " cannot constrain phi as " << std::endl
1137  << diamond << std::endl
1138  << " is singular " << diamond.determinant() << " with rank "
1139  << (Eigen::FullPivLU<AmgSymMatrix(2)>{diamond}.rank()));
1140 
1141  return seeds;
1142  }
1143  ATH_MSG_VERBOSE("The combination of " << printSeed(base_seed) << " to " << std::endl
1144  << diamond << std::endl
1145  << "May give a couple of stereo seeds " << diamond.determinant());
1146 
1148  const AmgSymMatrix(2) seed_builder = diamond.inverse();
1150  const double KmG = K-G;
1151  const double KmA = K-A;
1152  const double AmG = A-G;
1153 
1154  const double TwoDotZero = base_seed[2].dirDot(base_seed[0]);
1155  const double ThreeDotZero = base_seed[3].dirDot(base_seed[0]);
1156  const double ThreeDotOne = base_seed[3].dirDot(base_seed[1]);
1157  const double TwoDotOne = base_seed[2].dirDot(base_seed[1]);
1158 
1159  auto estimate_muon = [&] () -> std::optional<std::array<double,2>> {
1160  const Amg::Vector3D Y0 = K * base_seed[2].pos() - G * base_seed[3].pos() - KmG * base_seed[0].pos();
1161  const Amg::Vector3D Y1 = AmG * base_seed[3].pos() - KmG * base_seed[1].pos() + KmA * base_seed[2].pos();
1162  const double Y0dotE0 = base_seed[0].dirDot(Y0);
1163  const double Y1dotE1 = base_seed[1].dirDot(Y1);
1164 
1165  const AmgVector(2) centers = (KmG * (base_seed[0].pos() - base_seed[1].pos()) +
1166  Y0dotE0 * base_seed[0].dir() +
1167  A * (base_seed[3].pos() - base_seed[2].pos()) -
1168  Y1dotE1 * base_seed[1].dir())
1169  .block<2, 1>(0, 0);
1170 
1171  const AmgVector(2) sol_pars = seed_builder * centers;
1172  const std::array<double, 4> lengths{(Y0dotE0 + K * sol_pars[0] * TwoDotZero - G * sol_pars[1] * ThreeDotZero) / KmG,
1173  (Y1dotE1 + AmG * sol_pars[1] *ThreeDotOne + KmA * sol_pars[0] * TwoDotOne) / KmG, sol_pars[0], sol_pars[1]};
1174  bool accept{true};
1175  ATH_MSG_VERBOSE("Check intersections of "<<printSeed(base_seed));
1176  constexpr double tolerance = 10.* Gaudi::Units::mm;
1177  std::optional<Amg::Vector3D> seg_pos{std::nullopt}, seg_dir{std::nullopt};
1178  for (unsigned int i = 0; i < base_seed.size(); ++i) {
1179  const MuonGM::MuonChannelDesign* design = getDesign(base_seed[i]);
1180  const double halfLength = design->channelHalfLength(channel(base_seed[i]), true);
1181  accept &= (halfLength + tolerance > std::abs(lengths[i]));
1182  if (msgLvl(MSG::VERBOSE)) {
1183  if (!seg_pos) {
1184  seg_pos = std::make_optional<Amg::Vector3D>(base_seed[0].pos() +
1185  lengths[0] * base_seed[0].dir());
1186  ATH_MSG_VERBOSE("Position "<<to_string(*seg_pos));
1187  }
1188  if (!seg_dir){
1189  seg_dir = std::make_optional<Amg::Vector3D>((base_seed[1].pos() +
1190  lengths[1] *base_seed[1].dir() - (*seg_pos)).unit());
1191  ATH_MSG_VERBOSE("Direction "<<to_string(*seg_dir));
1192  }
1193  std::optional<double> mu_crossing = Amg::intersect<3>(*seg_pos, *seg_dir, base_seed[i].pos(),base_seed[i].dir());
1194  ATH_MSG_VERBOSE(" ----- "<<(i+1)<<" at "<<to_string(base_seed[i].pos() + lengths[i]*base_seed[i].dir())
1195  << " ("<< std::string( halfLength > std::abs(lengths[i]) ? "inside" : "outside")<<" wedge) "
1196  << halfLength <<" vs. "<<std::abs(lengths[i])<<" crossing point: "<<std::abs(*mu_crossing));
1197  } else if (!accept) return std::nullopt;
1198  }
1199  if (!accept) return std::nullopt;
1200  return std::make_optional<std::array<double,2>>({lengths[0], lengths[1]});
1201  };
1202 
1204  seeds.reserve(200);
1205  MeasVec::const_iterator begin2{orderedClusters[selLayers[1]].begin()};
1206  MeasVec::const_iterator begin3{orderedClusters[selLayers[2]].begin()};
1207  MeasVec::const_iterator begin4{orderedClusters[selLayers[3]].begin()};
1208 
1209  const MeasVec::const_iterator end2{orderedClusters[selLayers[1]].end()};
1210  const MeasVec::const_iterator end3{orderedClusters[selLayers[2]].end()};
1211  const MeasVec::const_iterator end4{orderedClusters[selLayers[3]].end()};
1212 
1213  for (const SeedMeasurement& lay1 : orderedClusters[selLayers[0]]) {
1214  base_seed[0] = lay1;
1215  for (MeasVec::const_iterator lay2 = begin2; lay2 != end2; ++lay2) {
1216 
1217  base_seed[1] = *lay2;
1218  ChannelConstraint chCheck = compatiblyFromIP(lay1, *lay2);
1221  if (chCheck == ChannelConstraint::TooNarrow) {
1222  begin2 = lay2 + 1;
1223  continue;
1224  }
1226  else if (chCheck == ChannelConstraint::TooWide) {
1227  break;
1228  }
1230  for (MeasVec::const_iterator lay3 = begin3; lay3 != end3; ++lay3) {
1231  chCheck = compatiblyFromIP(lay1, *lay3);
1232  const ChannelConstraint chCheck1 = compatiblyFromIP(*lay2, *lay3);
1233  if (chCheck == ChannelConstraint::TooNarrow && chCheck1 == ChannelConstraint::TooNarrow) {
1234  begin3 = lay3 + 1;
1235  continue;
1236  } else if (chCheck == ChannelConstraint::TooWide && chCheck1 == ChannelConstraint::TooWide) {
1237  break;
1238  }
1239  base_seed[2] = *lay3;
1241  for (MeasVec::const_iterator lay4 = begin4 ; lay4 != end4; ++lay4) {
1242  chCheck = compatiblyFromIP(*lay3, *lay4);
1243  if (chCheck == ChannelConstraint::TooNarrow) {
1244  begin4 = lay4 + 1;
1245  continue;
1246  } else if (chCheck == ChannelConstraint::TooWide) {
1247  break;
1248  }
1249 
1250  base_seed[3] = (*lay4);
1251  std::optional<std::array<double, 2>> isects = estimate_muon();
1252  ++trial_counter;
1253  if (!isects) continue;
1254  NSWSeed seed{this, base_seed, *isects};
1255 
1256  if (seed.size() < 4) continue;
1257  if (m_ipConstraint) {
1258  const double eta = std::abs(seed.dir().eta());
1259  if (eta < minEtaNSW || eta > maxEtaNSW) {
1260  continue;
1261  }
1262  if (seed.dir().block<2,1>(0,0).dot(seed.pos().block<2,1>(0,0)) < 0.) continue;
1264  static const Muon::MuonSectorMapping sector_mapping{};
1265  const double deltaPhi = std::abs(seed.dir().deltaPhi(seed.pos()));
1266  if (deltaPhi > sector_mapping.sectorWidth(m_idHelperSvc->sector(base_seed[0]->identify()))) continue;
1267  }
1268  getClustersOnSegment(orderedClusters, seed, {selLayers[0], selLayers[1],selLayers[2], selLayers[3]});
1269  seeds.emplace_back(std::move(seed));
1270  }
1271  }
1272  }
1273  }
1274  return seeds;
1275  }
1279 
1280  // For a given dZ the measurements can only be separated by a certain dR such that the
1282  const double dZ = std::abs(meas2->globalPosition().z()) -
1283  std::abs(meas1->globalPosition().z());
1284 
1288  static const double minTanTheta = 0.75 / std::sinh(maxEtaNSW);
1289  static const double maxTanTheta = 1.25 / std::sinh(minEtaNSW);
1290 
1291  const double minDR = minTanTheta * std::abs(dZ);
1292  const double maxDR = maxTanTheta * std::abs(dZ);
1293 
1294  const std::pair<double, double> rad1 = coveredRadii(meas1);
1295  const std::pair<double, double> rad2 = coveredRadii(meas2);
1297  const double dlR = rad2.first - rad1.first;
1298  const double drR = rad2.second - rad1.second;
1299  ATH_MSG_VERBOSE("compatiblyFromIP() -- Measurements "<<std::endl
1300  <<print(meas1)<<std::endl<<print(meas2)
1301  <<std::endl<<". Separation in dR (left/right) "<<dlR<<"/"<<drR<<", dZ "<<dZ
1302  <<" --> dR has to be in "<<minDR<<" "<<maxDR);
1303  if ((std::abs(dlR) < minDR && std::abs(drR) < minDR) ||
1304  (dZ > 0 && dlR <0 && drR <0) || (dZ < 0 && dlR >0 && drR > 0)) {
1306  } if (std::abs(dlR) > maxDR && std::abs(drR) > maxDR && dlR * drR > 0.){
1308  }
1310  }
1311  inline std::pair<double, double> MuonNSWSegmentFinderTool::coveredRadii(const SeedMeasurement& meas) const {
1312  const MuonGM::MuonChannelDesign* design = getDesign(meas);
1313  const int chNum = channel(meas);
1315  design->leftEdge(chNum, left);
1316  design->rightEdge(chNum, right);
1317  const double radLeft{meas->associatedSurface().localToGlobal(left).perp()};
1318  const double radRight{meas->associatedSurface().localToGlobal(right).perp()};
1319  return std::make_pair(radLeft, radRight);
1320  }
1321  std::vector<NSWSeed> MuonNSWSegmentFinderTool::resolveAmbiguities(std::vector<NSWSeed>&& unresolved) const {
1322  std::vector<NSWSeed> seeds;
1323  seeds.reserve(unresolved.size());
1324  std::sort(unresolved.begin(), unresolved.end(),[](const NSWSeed& a, const NSWSeed& b){
1325  return a.chi2() < b.chi2();
1326  });
1327  for (NSWSeed& seed : unresolved) {
1328  bool add_seed{true};
1329  for (NSWSeed& good : seeds) {
1330  NSWSeed::SeedOR ov = good.overlap(seed);
1331  if (ov == NSWSeed::SeedOR::SubSet) {
1332  std::swap(seed, good);
1333  add_seed = false;
1334  break;
1335  } else if (ov == NSWSeed::SeedOR::Same || ov == NSWSeed::SeedOR::SuperSet) {
1336  add_seed = false;
1337  break;
1338  }
1339  }
1340  if (add_seed) seeds.push_back(std::move(seed));
1341  }
1342  ATH_MSG_VERBOSE(seeds.size()<<" out of "<<unresolved.size()<<" passed the overlap removal");
1343  return seeds;
1344  }
1345 
1346  //============================================================================
1347  std::vector<std::pair<double, double>> MuonNSWSegmentFinderTool::getPadPhiOverlap(
1348  const std::vector<std::vector<const Muon::sTgcPrepData*>>& pads) const {
1349  // 'pads' contains segment hit candidates, classified in four layers (IP or HO).
1350  // Layers are ordered; for IP, the layer with hits that is nearest to
1351  // the IP is first, while for HO, the one furthest from the IP is first.
1352 
1353  std::vector<std::vector<double>> padsPhiL, padsPhiR;
1354  std::vector<double> padsPhiC;
1355 
1356  // Loop on layers
1357  for (const std::vector<const Muon::sTgcPrepData*>& surfHits : pads) {
1358  // Loop on layer hits
1359  std::vector<double> surfPadsPhiL, surfPadsPhiR;
1360  for (const Muon::sTgcPrepData* prd : surfHits) {
1361  const Identifier id = prd->identify();
1362  const MuonGM::MuonPadDesign* design = prd->detectorElement()->getPadDesign(id);
1363  if (!design) {
1364  ATH_MSG_WARNING("No design available for " << m_idHelperSvc->toString(id));
1365  continue;
1366  }
1367 
1368  // Phi boundaries of this pad in local coordinates
1369  const double halfWidthX = 0.5 * design->channelWidth(prd->localPosition(), true);
1370  const double hitPadX = prd->localPosition().x(); // x is in the phi direction
1371 
1372  // Reject hit candidates on pads too close (in phi) to any pad kept so far
1373  // (pad fuzziness) to constrain the number of combinations.
1374  bool samePhi = std::find_if(padsPhiC.begin(), padsPhiC.end(), [&hitPadX, &halfWidthX](const double prevPadPhi) {
1375  return std::abs(hitPadX - prevPadPhi) < 0.9 * halfWidthX;
1376  }) != padsPhiC.end();
1377 
1378  if (samePhi) continue;
1379 
1380  // Store the new pad candidate
1381  surfPadsPhiL.push_back(hitPadX - halfWidthX);
1382  surfPadsPhiR.push_back(hitPadX + halfWidthX);
1383  padsPhiC.push_back(hitPadX);
1384  ATH_MSG_DEBUG(" keep pad id " << m_idHelperSvc->toString(id) << " local x: " << hitPadX << " width: " << halfWidthX);
1385  }
1386 
1387  padsPhiL.push_back(std::move(surfPadsPhiL));
1388  padsPhiR.push_back(std::move(surfPadsPhiR));
1389  }
1390 
1391  unsigned int nSurf = padsPhiR.size();
1392 
1393  // number of combinations we can make out of pads in different layers
1394  // we want to keep combinations of overlapping pads.
1395  unsigned int nCombos{1};
1396  for (const std::vector<double>& surfPadsPhiR : padsPhiR) {
1397  if (!surfPadsPhiR.empty()) nCombos *= surfPadsPhiR.size();
1398  }
1399 
1400  std::vector<std::pair<double, double>> phiOverlap;
1401  phiOverlap.reserve(nCombos);
1402 
1403  if (nCombos <= 100) {
1404  unsigned int N{nCombos};
1405  for (unsigned int isurf{0}; isurf < nSurf; ++isurf) {
1406  if (padsPhiR[isurf].empty()) continue;
1407  unsigned int nSurfHits = padsPhiR[isurf].size();
1408  N /= nSurfHits;
1409 
1410  for (unsigned int icombo{0}; icombo < nCombos; ++icombo) {
1411  // index of the pad that corresponds to this combination
1412  unsigned int padIdx = (icombo / N) % nSurfHits;
1413  if (isurf == 0) {
1414  // first surface: just add the range of each hit pad
1415  phiOverlap.emplace_back(padsPhiL[isurf][padIdx], padsPhiR[isurf][padIdx]);
1416  } else {
1417  // subsequent surfaces: use staggering to narrow the phi ranges
1418  phiOverlap[icombo].first = std::max(padsPhiL[isurf][padIdx], phiOverlap[icombo].first);
1419  phiOverlap[icombo].second = std::min(padsPhiR[isurf][padIdx], phiOverlap[icombo].second);
1420  }
1421  }
1422  }
1423 
1424  // delete bad combinations with xmin > xmax (indicates non overlapping pads)
1425  phiOverlap.erase(std::remove_if(phiOverlap.begin(), phiOverlap.end(),
1426  [](std::pair<double, double>& range) { return range.first >= range.second; }),
1427  phiOverlap.end());
1428  ATH_MSG_DEBUG("Pad seeding - #combinations initial: " << nCombos
1429  << ", after cleaning for non overlapping pads: " << phiOverlap.size());
1430 
1431  } else {
1432  // in case combinations are too many, store the phi ranges of individual pads
1433  for (unsigned int isurf{0}; isurf < nSurf; ++isurf) {
1434  unsigned int nSurfHits = padsPhiR[isurf].size();
1435  for (unsigned int ihit{0}; ihit < nSurfHits; ++ihit) {
1436  phiOverlap.emplace_back(padsPhiL[isurf][ihit], padsPhiR[isurf][ihit]);
1437  }
1438  }
1439  ATH_MSG_DEBUG("Pad seeding - #combinations: " << nCombos << " is too large. Seeding from" << phiOverlap.size()
1440  << " individual pads.");
1441  }
1442 
1443  return phiOverlap;
1444  }
1445 
1446  //============================================================================
1448 
1449  MeasVec calibratedClusters;
1450  MeasVec clusters = seed.measurements();
1451 
1452  // loop on the segment clusters and use the phi of the seed to correct them
1453  for (const SeedMeasurement& clus : clusters) {
1454  std::unique_ptr<const Muon::MuonClusterOnTrack> newClus;
1455 
1456  // get the intercept of the seed direction with the cluster surface
1457  const Identifier hitID = clus->identify();
1458  const Trk::Surface& surf = clus->associatedSurface();
1459  Trk::Intersection intersect = surf.straightLineIntersection(seed.pos(), seed.dir(), false, false);
1460 
1461  if (m_idHelperSvc->isMM(hitID)) {
1462  // build a new MM cluster on track with correct position
1463  std::unique_ptr<const Muon::MuonClusterOnTrack> newClus {m_muonClusterCreator->correct(*clus->prepRawData(), intersect.position, seed.dir())};
1464  calibratedClusters.emplace_back(seed.newCalibClust(std::move(newClus)));
1465  } else if (m_idHelperSvc->issTgc(hitID)) {
1466  // build a new sTGC cluster on track with correct position
1467  std::unique_ptr<const Muon::MuonClusterOnTrack> newClus {m_muonClusterCreator->correct(*clus->prepRawData(), intersect.position, seed.dir())};
1468  calibratedClusters.emplace_back(seed.newCalibClust(std::move(newClus)));
1469  }
1470  }
1471 
1472  return calibratedClusters;
1473  }
1474 
1475  //============================================================================
1476  template <size_t N>
1477  std::string MuonNSWSegmentFinderTool::printSeed(const std::array<SeedMeasurement, N>& seed) const {
1478  std::stringstream sstr{};
1479  sstr << std::endl;
1480  for (const SeedMeasurement& cl : seed) sstr << " *** " << print(cl) << std::endl;
1481  return sstr.str();
1482  }
1483 
1484  //============================================================================
1486  std::stringstream sstr{};
1487  sstr << m_idHelperSvc->toString(cl->identify()) << " at " <<to_string(cl.pos())
1488  <<" pointing to (" <<to_string(cl.dir())<<" cluster size: "<<clusterSize(cl);
1489 
1490  return sstr.str();
1491  }
1492  std::string MuonNSWSegmentFinderTool::print(const MeasVec& measurements) const {
1493  std::stringstream sstr{};
1494  for (const SeedMeasurement& cl : measurements){
1495  sstr<<" *** "<<print(cl)<<std::endl;
1496  }
1497  return sstr.str();
1498  }
1499  std::string MuonNSWSegmentFinderTool::print(const LayerMeasVec& sortedVec) const {
1500  std::stringstream sstr{};
1501  unsigned int lay{0};
1502  for (const MeasVec& clusts: sortedVec){
1503  sstr<<"Clusters in Layer: "<<(lay+1)<<std::endl;
1504  sstr<<"#################################################"<<std::endl;
1505  sstr<<print(clusts);
1506  ++lay;
1507  }
1508  return sstr.str();
1509  }
1510 
1512  if (clustInLay.size() < m_ocupMmNumPerBin || m_idHelperSvc->issTgc(clustInLay[0]->identify())) return clustInLay;
1518  MeasVec prunedMeas{};
1519  prunedMeas.reserve(clustInLay.size());
1520  const unsigned int firstCh = channel(clustInLay[0]);
1521  const unsigned int lastCh = channel(clustInLay[clustInLay.size() -1]);
1523  const unsigned int deltaCh = lastCh - firstCh;
1524  const unsigned int nBins = deltaCh / m_ocupMmBinWidth + (deltaCh % m_ocupMmBinWidth > 0);
1525  ATH_MSG_VERBOSE("Clusters in layer "<<print(clustInLay)<<" lowest channel: "<<
1526  firstCh<<", highest channel: "<<lastCh<<" bin width: "<<m_ocupMmBinWidth<<" number of bins"<<nBins);
1527 
1528  LayerMeasVec occupancyHisto{};
1529  occupancyHisto.resize(nBins);
1530  for (MeasVec& bin : occupancyHisto) {
1531  bin.reserve(clustInLay.size());
1532  }
1533  ATH_MSG_VERBOSE("Clusters sorted into bins "<<print(occupancyHisto));
1535  for (SeedMeasurement& meas : clustInLay){
1536  unsigned int bin = (channel(meas) - firstCh) % nBins;
1537  occupancyHisto[bin].push_back(std::move(meas));
1538  }
1540  for (MeasVec& bin : occupancyHisto) {
1541  if(bin.size() >= m_ocupMmNumPerBin){
1542  ATH_MSG_VERBOSE("The micromegas are slobbering. Detected too many clusters "<<bin.size()<<std::endl<<print(bin));
1543  bin.clear();
1544  }
1545  }
1547  for (unsigned int i = 0; i < occupancyHisto.size() -1; ++i) {
1548  if (occupancyHisto[i].size() + occupancyHisto[i+1].size() >= m_ocupMmNumPerPair){
1549  ATH_MSG_VERBOSE("The two neighbouring bins "<<i<<"&"<<(i+1)<<" have too many clusters "<<std::endl<<
1550  print(occupancyHisto[i])<<std::endl<<print(occupancyHisto[i+1]));
1551  occupancyHisto[i].clear();
1552  occupancyHisto[i+1].clear();
1553  }
1554  }
1556  for (MeasVec& bin : occupancyHisto){
1557  std::copy(std::make_move_iterator(bin.begin()),
1558  std::make_move_iterator(bin.end()),
1559  std::back_inserter(prunedMeas));
1560  }
1561 
1562  ATH_MSG_VERBOSE("Number of measurements before pruning "<<clustInLay.size()<<" number of measurments survived the pruning "<<prunedMeas.size());
1563  return prunedMeas;
1564 
1565  }
1566 } // namespace Muon
Trk::Segment::Author
Author
enum to identify who created the segment.
Definition: TrkEvent/TrkSegment/TrkSegment/Segment.h:63
ReadFromCoolCompare.ov
ov
Definition: ReadFromCoolCompare.py:230
PlotCalibFromCool.il
il
Definition: PlotCalibFromCool.py:381
Muon::MuonNSWSegmentFinderTool::coveredRadii
std::pair< double, double > coveredRadii(const SeedMeasurement &meas) const
Returns the minimal & maximal radial distance of a measurement.
Definition: MuonNSWSegmentFinderTool.cxx:1311
Trk::LocalParameters
Definition: LocalParameters.h:98
Muon::MuonNSWSegmentFinderTool::printSeed
std::string printSeed(const std::array< SeedMeasurement, N > &seed) const
Definition: MuonNSWSegmentFinderTool.cxx:1477
Trk::PlaneSurface::globalToLocal
virtual bool globalToLocal(const Amg::Vector3D &glob, const Amg::Vector3D &mom, Amg::Vector2D &loc) const override final
Specified for PlaneSurface: GlobalToLocal method without dynamic memory allocation - boolean checks i...
Definition: PlaneSurface.cxx:213
python.SystemOfUnits.second
int second
Definition: SystemOfUnits.py:120
Trk::PlaneSurface::localToGlobalDirection
void localToGlobalDirection(const Trk::LocalDirection &locdir, Amg::Vector3D &globdir) const
This method transforms a local direction wrt the plane to a global direction.
Definition: PlaneSurface.cxx:242
MuonGM::MuonPadDesign
Parameters defining the design of the readout sTGC pads.
Definition: MuonPadDesign.h:40
Muon::MuonNSWSegmentFinderTool::print
std::string print(const SeedMeasurement &meas) const
Definition: MuonNSWSegmentFinderTool.cxx:1485
Muon::NSWSeed::measurements
MeasVec measurements() const
Definition: MuonNSWSegmentFinderTool.cxx:213
Muon::NSWSeed::m_parent
const MuonNSWSegmentFinderTool * m_parent
Definition: MuonNSWSegmentFinderTool.h:103
Muon::MuonNSWSegmentFinderTool::isPad
bool isPad(const Muon::MuonClusterOnTrack *clust) const
Definition: MuonNSWSegmentFinderTool.cxx:685
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
Muon::MuonNSWSegmentFinderTool::m_edmHelperSvc
ServiceHandle< IMuonEDMHelperSvc > m_edmHelperSvc
Definition: MuonNSWSegmentFinderTool.h:141
Muon::MMPrepData
Class to represent MM measurements.
Definition: MMPrepData.h:22
Trk::Track::fitQuality
const FitQuality * fitQuality() const
return a pointer to the fit quality const-overload
python.SystemOfUnits.s
int s
Definition: SystemOfUnits.py:131
Amg::MatrixX
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > MatrixX
Dynamic Matrix - dynamic allocation.
Definition: EventPrimitives.h:29
Trk::Intersection
Definition: Intersection.h:24
inline_hints.h
m_dir
TDirectory & m_dir
The directory we need to return to.
Definition: OutputStreamData.cxx:41
python.SystemOfUnits.m
int m
Definition: SystemOfUnits.py:91
max
#define max(a, b)
Definition: cfImp.cxx:41
Muon::NSWSeed::SeedOR::SuperSet
@ SuperSet
TrackParameters.h
Muon::MuonNSWSegmentFinderTool::getPadPhiOverlap
std::vector< std::pair< double, double > > getPadPhiOverlap(const std::vector< std::vector< const Muon::sTgcPrepData * >> &pads) const
Definition: MuonNSWSegmentFinderTool.cxx:1347
Muon::MuonNSWSegmentFinderTool::m_ocupMmNumPerPair
Gaudi::Property< unsigned int > m_ocupMmNumPerPair
Definition: MuonNSWSegmentFinderTool.h:195
Muon::MuonClusterOnTrack::globalPosition
virtual const Amg::Vector3D & globalPosition() const override
Returns global position.
Definition: MuonClusterOnTrack.cxx:93
Trk::PrepRawDataType::MMPrepData
@ MMPrepData
Muon::MuonNSWSegmentFinderTool::getClustersOnSegment
int getClustersOnSegment(const LayerMeasVec &orderedclusters, NSWSeed &seed, const std::set< unsigned int > &exclude) const
Definition: MuonNSWSegmentFinderTool.cxx:902
Trk::locX
@ locX
Definition: ParamDefs.h:43
Trk::locY
@ locY
local cartesian
Definition: ParamDefs.h:44
Muon::MuonNSWSegmentFinderTool::m_trackToSegmentTool
ToolHandle< IMuonTrackToSegmentTool > m_trackToSegmentTool
Definition: MuonNSWSegmentFinderTool.h:158
Trk::Track
The ATLAS Track class.
Definition: Tracking/TrkEvent/TrkTrack/TrkTrack/Track.h:73
Trk::Surface::straightLineIntersection
Intersection straightLineIntersection(const T &pars, bool forceDir=false, const Trk::BoundaryCheck &bchk=false) const
fst straight line intersection schema - templated for charged and neutral parameters
Definition: Tracking/TrkDetDescr/TrkSurfaces/TrkSurfaces/Surface.h:351
egammaEnergyPositionAllSamples::e1
double e1(const xAOD::CaloCluster &cluster)
return the uncorrected cluster energy in 1st sampling
Muon::NSWSeed::m_dir
Amg::Vector3D m_dir
Seed direction.
Definition: MuonNSWSegmentFinderTool.h:115
Trk::PerigeeSurface
Definition: PerigeeSurface.h:43
sTgcReadoutElement.h
eta
Scalar eta() const
pseudorapidity method
Definition: AmgMatrixBasePlugin.h:79
Trk::ParametersBase::position
const Amg::Vector3D & position() const
Access method for the position.
Amg::Vector2D
Eigen::Matrix< double, 2, 1 > Vector2D
Definition: GeoPrimitives.h:48
xAODP4Helpers.h
Muon::MuonNSWSegmentFinderTool::findStgcPrecisionSegments
std::vector< std::unique_ptr< Muon::MuonSegment > > findStgcPrecisionSegments(const EventContext &ctx, const std::vector< const Muon::MuonClusterOnTrack * > &MuonClusters, int singleWedge=0) const
Combines 2 sTgc strip layers to find 2D segments constraining the muon in the eta direction.
Definition: MuonNSWSegmentFinderTool.cxx:462
Muon::MuonClusterOnTrack::prepRawData
virtual const MuonCluster * prepRawData() const override=0
Returns the Trk::PrepRawData - is a MuonCluster in this scope.
Trk::ParametersT
Dummy class used to allow special convertors to be called for surfaces owned by a detector element.
Definition: EMErrorDetail.h:25
xAOD::deltaPhi
setSAddress setEtaMS setDirPhiMS setDirZMS setBarrelRadius setEndcapAlpha setEndcapRadius setInterceptInner setEtaMap setEtaBin setIsTgcFailure setDeltaPt deltaPhi
Definition: L2StandAloneMuon_v1.cxx:160
Muon::NSWSeed::SeedOR::NoOverlap
@ NoOverlap
CutsMETMaker::accept
StatusCode accept(const xAOD::Muon *mu)
Definition: CutsMETMaker.cxx:18
EventPrimitivesHelpers.h
extractSporadic.c1
c1
Definition: extractSporadic.py:134
conifer::pow
constexpr int pow(int x)
Definition: conifer.h:20
Muon::MuonNSWSegmentFinderTool::ChannelConstraint
ChannelConstraint
Definition: MuonNSWSegmentFinderTool.h:294
Muon::MuonNSWSegmentFinderTool::m_idHelperSvc
ServiceHandle< IMuonIdHelperSvc > m_idHelperSvc
Definition: MuonNSWSegmentFinderTool.h:136
Muon::MuonNSWSegmentFinderTool::m_ocupMmNumPerBin
Gaudi::Property< unsigned int > m_ocupMmNumPerBin
Definition: MuonNSWSegmentFinderTool.h:193
Muon::MuonNSWSegmentFinderTool::m_printer
PublicToolHandle< MuonEDMPrinterTool > m_printer
Definition: MuonNSWSegmentFinderTool.h:163
Muon::NSWSeed::SeedMeasurement::dir
const Amg::Vector3D & dir() const
Definition: MuonNSWSegmentFinderTool.h:45
module_driven_slicing.layers
layers
Definition: module_driven_slicing.py:114
compute_lumi.divisor
divisor
Definition: compute_lumi.py:60
Muon::MuonNSWSegmentFinderTool::segmentSeedFromMM
std::vector< NSWSeed > segmentSeedFromMM(const LayerMeasVec &orderedClusters) const
Definition: MuonNSWSegmentFinderTool.cxx:1029
plotBeamSpotVxVal.cov
cov
Definition: plotBeamSpotVxVal.py:201
bin
Definition: BinsDiffFromStripMedian.h:43
MuonGM::sTgcReadoutElement::getDesign
const MuonChannelDesign * getDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:281
Trk::Perigee
ParametersT< 5, Charged, PerigeeSurface > Perigee
Definition: Tracking/TrkEvent/TrkParameters/TrkParameters/TrackParameters.h:29
mc.diff
diff
Definition: mc.SFGenPy8_MuMu_DD.py:14
sTgcIdHelper::Strip
@ Strip
Definition: sTgcIdHelper.h:179
AthCommonMsg::msgLvl
bool msgLvl(const MSG::Level lvl) const
Definition: AthCommonMsg.h:30
Muon::MuonNSWSegmentFinderTool::hitsToTrack
bool hitsToTrack(const EventContext &ctx, const MeasVec &etaHitVec, const MeasVec &phiHitVec, const Trk::TrackParameters &startpar, TrackCollection &segTrkColl) const
Definition: MuonNSWSegmentFinderTool.cxx:698
Muon::MuonNSWSegmentFinderTool
Definition: MuonNSWSegmentFinderTool.h:126
MuonGM::sTgcReadoutElement::isEtaZero
bool isEtaZero(const Identifier &id, const Amg::Vector2D &localPosition) const
is eta=0 of QL1 or QS1? Support for Strip and Pad cathodes is valid when the Strip,...
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:384
Muon::MuonNSWSegmentFinderTool::MuonNSWSegmentFinderTool
MuonNSWSegmentFinderTool(const std::string &type, const std::string &name, const IInterface *parent)
default constructor
Definition: MuonNSWSegmentFinderTool.cxx:256
Muon::NSWSeed::m_measurements
SeedMeasCache m_measurements
Cache the eta measurements.
Definition: MuonNSWSegmentFinderTool.h:107
Trk::PrepRawData::type
virtual bool type(PrepRawDataType type) const =0
Interface method checking the type.
Trk::TrkDetElementBase
Definition: TrkDetElementBase.h:52
Phi
@ Phi
Definition: RPCdef.h:8
JetTiledMap::N
@ N
Definition: TiledEtaPhiMap.h:44
vec
std::vector< size_t > vec
Definition: CombinationsGeneratorTest.cxx:12
exclude
std::set< std::string > exclude
list of directories to be excluded
Definition: hcg.cxx:95
Muon::IMuonIdHelperSvc::measuresPhi
virtual bool measuresPhi(const Identifier &id) const =0
returns whether channel measures phi or not
Muon::MuonNSWSegmentFinderTool::cleanClusters
MeasVec cleanClusters(const std::vector< const Muon::MuonClusterOnTrack * > &MuonClusters, int hit_sel, int singleWedge) const
Definition: MuonNSWSegmentFinderTool.cxx:759
Muon::NSWSeed::m_width
double m_width
seed width
Definition: MuonNSWSegmentFinderTool.h:117
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
Muon
This class provides conversion from CSC RDO data to CSC Digits.
Definition: TrackSystemController.h:49
Muon::MuonNSWSegmentFinderTool::compatiblyFromIP
ChannelConstraint compatiblyFromIP(const SeedMeasurement &meas1, const SeedMeasurement &meas2) const
Checks whether the two measurements are compatible within the IP constraint
Definition: MuonNSWSegmentFinderTool.cxx:1277
Muon::NSWSeed::SeedMeasurement::setDistance
void setDistance(double d)
Definition: MuonNSWSegmentFinderTool.h:51
empty
bool empty(TH1 *h)
Definition: computils.cxx:294
MuonGM::MuonChannelDesign::hasStereoAngle
double hasStereoAngle() const
returns whether the stereo angle is non-zero
Definition: MuonChannelDesign.h:78
Muon::NSWSeed::SeedMeasurement::pos
const Amg::Vector3D & pos() const
Definition: MuonNSWSegmentFinderTool.h:46
MuonGM::MuonClusterReadoutElement::surface
virtual const Trk::PlaneSurface & surface() const override
access to chamber surface (phi orientation), uses the first gas gap
Definition: MuonClusterReadoutElement.h:123
Muon::NSWSeed::NSWSeed
NSWSeed()=default
AmgSymMatrix
#define AmgSymMatrix(dim)
Definition: EventPrimitives.h:52
Trk::TrkDetElementBase::detectorType
virtual DetectorElemType detectorType() const =0
Return the Detector element type.
beamspotPlotBcids.lay
lay
Definition: beamspotPlotBcids.py:464
Trk::DefinedParameter
std::pair< double, ParamDefs > DefinedParameter
Definition: DefinedParameter.h:27
Muon::IMuonNSWSegmentFinderTool::SegmentMakingCache::quadSegs
std::vector< std::unique_ptr< Muon::MuonSegment > > quadSegs
Output vector to which the quadruplet segments are pushed back.
Definition: IMuonNSWSegmentFinderTool.h:37
Muon::NSWSeed::SeedMeasCache
std::array< SeedMeasurement, 16 > SeedMeasCache
Helper pair to cache the measurements with the respective distances.
Definition: MuonNSWSegmentFinderTool.h:105
Track.h
sTgcPrepData.h
cm
const double cm
Definition: Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/tools/FCAL_ChannelMap.cxx:25
python.setupRTTAlg.size
int size
Definition: setupRTTAlg.py:39
Muon::NSWSeed::pos
const Amg::Vector3D & pos() const
Returns the position of the seed.
Definition: MuonNSWSegmentFinderTool.h:77
Muon::NSWSeed::SeedMeasurement::SeedMeasurement
SeedMeasurement()=default
Muon::MuonNSWSegmentFinderTool::ipConstraint
std::unique_ptr< Trk::PseudoMeasurementOnTrack > ipConstraint(const EventContext &ctx) const
creates the IP constraint
Definition: MuonNSWSegmentFinderTool.cxx:675
Muon::MuonNSWSegmentFinderTool::classifyByLayer
LayerMeasVec classifyByLayer(const MeasVec &clusters, int hit_sel) const
Definition: MuonNSWSegmentFinderTool.cxx:777
Amg::toString
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Definition: GeoPrimitivesToStringConverter.h:40
Muon::MuonNSWSegmentFinderTool::vetoBursts
MeasVec vetoBursts(MeasVec &&clustInLay) const
Removes clusters from high activity areas in the detector.
Definition: MuonNSWSegmentFinderTool.cxx:1511
CheckAppliedSFs.e3
e3
Definition: CheckAppliedSFs.py:264
EventPrimitivesToStringConverter.h
lumiFormat.i
int i
Definition: lumiFormat.py:92
SG::OWN_ELEMENTS
@ OWN_ELEMENTS
this data object owns its elements
Definition: OwnershipPolicy.h:17
MuonPrepDataCollection.h
PixelAthClusterMonAlgCfg.e4
e4
Definition: PixelAthClusterMonAlgCfg.py:317
Muon::MuonNSWSegmentFinderTool::find
void find(const EventContext &ctx, SegmentMakingCache &cache) const override
Definition: MuonNSWSegmentFinderTool.cxx:283
ATH_FLATTEN
#define ATH_FLATTEN
Definition: inline_hints.h:52
G
#define G(x, y, z)
Definition: MD5.cxx:113
Identifier
Definition: DetectorDescription/Identifier/Identifier/Identifier.h:32
Muon::MeasVec
NSWSeed::MeasVec MeasVec
Stereo seeds can be formed using hits from 4 independent layers by solving the following system of eq...
Definition: MuonNSWSegmentFinderTool.cxx:102
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:179
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
AmgVector
AmgVector(4) T2BSTrackFilterTool
Definition: T2BSTrackFilterTool.cxx:114
TRT::Hit::layer
@ layer
Definition: HitInfo.h:79
Muon::NSWSeed::m_calibClust
std::set< std::shared_ptr< const Muon::MuonClusterOnTrack > > m_calibClust
Garbage container per seed.
Definition: MuonNSWSegmentFinderTool.h:123
Muon::MuonNSWSegmentFinderTool::m_trackCleaner
ToolHandle< IMuonTrackCleaner > m_trackCleaner
Definition: MuonNSWSegmentFinderTool.h:168
MuonGM::sTgcReadoutElement
An sTgcReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station c...
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:28
Muon::MuonNSWSegmentFinderTool::isWire
bool isWire(const Muon::MuonClusterOnTrack *clust) const
Definition: MuonNSWSegmentFinderTool.cxx:693
MMPrepData.h
res
std::pair< std::vector< unsigned int >, bool > res
Definition: JetGroupProductTest.cxx:14
Amg::transform
Amg::Vector3D transform(Amg::Vector3D &v, Amg::Transform3D &tr)
Transform a point from a Trasformation3D.
Definition: GeoPrimitivesHelpers.h:156
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
PseudoMeasurementOnTrack.h
test_pyathena.parent
parent
Definition: test_pyathena.py:15
Muon::NSWSeed::newCalibClust
const Muon::MuonClusterOnTrack * newCalibClust(std::unique_ptr< const Muon::MuonClusterOnTrack > new_clust)
Adds a calibrated cluster to the garbage collection.
Definition: MuonNSWSegmentFinderTool.cxx:245
Muon::MuonNSWSegmentFinderTool::m_nOfSeedLayers
Gaudi::Property< int > m_nOfSeedLayers
Definition: MuonNSWSegmentFinderTool.h:186
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
Muon::NSWSeed::SeedMeasurement
Struct caching the MuonClusterOnTrack and providing the orientation of the strip in addtion.
Definition: MuonNSWSegmentFinderTool.h:33
Muon::MuonNSWSegmentFinderTool::channel
int channel(const Muon::MuonClusterOnTrack *cluster) const
Returns the channel of the measurement on the layer.
Definition: MuonNSWSegmentFinderTool.cxx:895
Trk::PlaneSurface::globalToLocalDirection
void globalToLocalDirection(const Amg::Vector3D &glodir, Trk::LocalDirection &locdir) const
This method transforms the global direction to a local direction wrt the plane.
Definition: PlaneSurface.cxx:260
Muon::MuonNSWSegmentFinderTool::m_ocupMmBinWidth
Gaudi::Property< unsigned int > m_ocupMmBinWidth
Protection against slobbering Micromega events.
Definition: MuonNSWSegmentFinderTool.h:191
Trk::ParametersBase
Definition: ParametersBase.h:55
MuonReadoutElement.h
Muon::NSWSeed::dir
const Amg::Vector3D & dir() const
Returns the direction of the seed.
Definition: MuonNSWSegmentFinderTool.h:79
DataVector< Trk::Track >
Trk::LocalDirection
represents the three-dimensional global direction with respect to a planar surface frame.
Definition: LocalDirection.h:81
WriteCalibToCool.swap
swap
Definition: WriteCalibToCool.py:94
MuonGM::MuonPadDesign::channelWidth
double channelWidth(const Amg::Vector2D &pos, bool measPhi, bool preciseMeas=false) const
calculate local channel width
Definition: MuonPadDesign.h:142
Muon::MuonNSWSegmentFinderTool::m_maxClustDist
Gaudi::Property< double > m_maxClustDist
Definition: MuonNSWSegmentFinderTool.h:185
Muon::MuonNSWSegmentFinderTool::ChannelConstraint::TooNarrow
@ TooNarrow
Muon::NSWSeed::distance
double distance(const SeedMeasurement &meas) const
Calculates the minimal distance between seed and measurement.
Definition: MuonNSWSegmentFinderTool.cxx:147
Trk::MeasurementBase::localCovariance
const Amg::MatrixX & localCovariance() const
Interface method to get the localError.
Definition: MeasurementBase.h:138
Muon::NSWSeed::overlap
SeedOR overlap(const NSWSeed &other) const
Definition: MuonNSWSegmentFinderTool.cxx:196
beamspotman.dir
string dir
Definition: beamspotman.py:623
Muon::IMuonNSWSegmentFinderTool::SegmentMakingCache::buildQuads
bool buildQuads
Toggle whether quad segments should be built or not.
Definition: IMuonNSWSegmentFinderTool.h:35
min
#define min(a, b)
Definition: cfImp.cxx:40
Muon::MuonNSWSegmentFinderTool::m_trackSummary
ToolHandle< Trk::ITrackSummaryTool > m_trackSummary
Definition: MuonNSWSegmentFinderTool.h:173
Trk::PrepRawData
Definition: PrepRawData.h:62
tolerance
Definition: suep_shower.h:17
Muon::NSWSeed::add
bool add(SeedMeasurement meas, double max_uncert)
Tries to add the measurement to the seeds. Returns false if the measurement is incompatible with the ...
Definition: MuonNSWSegmentFinderTool.cxx:160
Muon::MuonNSWSegmentFinderTool::findStereoSegments
std::vector< std::unique_ptr< Muon::MuonSegment > > findStereoSegments(const EventContext &ctx, const std::vector< const Muon::MuonClusterOnTrack * > &allClusts, int singleWedge=0) const
Runs the NSW segment maker by combining 4 Micromega layers to a stereo seed.
Definition: MuonNSWSegmentFinderTool.cxx:384
Muon::NSWSeed::m_padMeasurements
SeedMeasCache m_padMeasurements
Cache the sTGC pad measurements.
Definition: MuonNSWSegmentFinderTool.h:111
Trk::MeasurementBase
Definition: MeasurementBase.h:58
Trk::PrepRawData::identify
Identifier identify() const
return the identifier
Muon::MuonNSWSegmentFinderTool::LayerMeasVec
std::vector< MeasVec > LayerMeasVec
Definition: MuonNSWSegmentFinderTool.h:200
Muon::MuonNSWSegmentFinderTool::m_ambiTool
ToolHandle< Trk::ITrackAmbiguityProcessorTool > m_ambiTool
Tool for ambiguity solving.
Definition: MuonNSWSegmentFinderTool.h:148
Trk::DetectorElemType::sTgc
@ sTgc
Muon::MuonNSWSegmentFinderTool::segmentSeedFromStgc
std::vector< NSWSeed > segmentSeedFromStgc(const LayerMeasVec &orderedClusters, bool usePhi) const
Definition: MuonNSWSegmentFinderTool.cxx:826
Trk::Track::perigeeParameters
const Perigee * perigeeParameters() const
return Perigee.
Definition: Tracking/TrkEvent/TrkTrack/src/Track.cxx:163
dumpTgcDigiJitter.nBins
list nBins
Definition: dumpTgcDigiJitter.py:29
Muon::NSWSeed::channel
int channel(const SeedMeasurement &meas) const
Returns the channel of the measurement on the layer.
Definition: MuonNSWSegmentFinderTool.cxx:146
Muon::MuonNSWSegmentFinderTool::ChannelConstraint::InWindow
@ InWindow
MuonPadDesign.h
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:192
Muon::MuonNSWSegmentFinderTool::idHelper
const IMuonIdHelperSvc * idHelper() const
Definition: MuonNSWSegmentFinderTool.cxx:281
ActsTrk::to_string
std::string to_string(const DetectorType &type)
Definition: GeometryDefs.h:34
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
compileRPVLLRates.c2
c2
Definition: compileRPVLLRates.py:361
Amg::error
double error(const Amg::MatrixX &mat, int index)
return diagonal error of the matrix caller should ensure the matrix is symmetric and the index is in ...
Definition: EventPrimitivesHelpers.h:40
Muon::NSWSeed::insert
bool insert(const Muon::MuonClusterOnTrack *cl)
Definition: MuonNSWSegmentFinderTool.cxx:226
Trk::PrepRawData::localPosition
const Amg::Vector2D & localPosition() const
return the local position reference
plotBeamSpotVxVal.bin
int bin
Definition: plotBeamSpotVxVal.py:83
Trk::nonInteracting
@ nonInteracting
Definition: ParticleHypothesis.h:25
Trk::PrepRawDataType::sTgcPrepData
@ sTgcPrepData
DataVector::push_back
value_type push_back(value_type pElem)
Add an element to the end of the collection.
Muon::NSWSeed::size
size_t size() const
Returns the number of measurements.
Definition: MuonNSWSegmentFinderTool.h:75
Muon::IMuonNSWSegmentFinderTool::SegmentMakingCache::inputClust
std::vector< std::unique_ptr< const Muon::MuonClusterOnTrack > > inputClust
Input vector containing all muon cluster on track.
Definition: IMuonNSWSegmentFinderTool.h:39
Trk::Surface::insideBounds
virtual bool insideBounds(const Amg::Vector2D &locpos, double tol1=0., double tol2=0.) const =0
virtual methods to be overwritten by the inherited surfaces
Amg::Vector3D
Eigen::Matrix< double, 3, 1 > Vector3D
Definition: GeoPrimitives.h:47
Muon::NSWSeed::m_phiMeasurements
SeedMeasCache m_phiMeasurements
Cache the phi measurements.
Definition: MuonNSWSegmentFinderTool.h:109
Trk::PlaneSurface::straightLineIntersection
virtual Intersection straightLineIntersection(const Amg::Vector3D &pos, const Amg::Vector3D &dir, bool forceDir, Trk::BoundaryCheck bchk) const override final
fast straight line intersection schema - standard: provides closest intersection and (signed) path le...
Definition: PlaneSurface.cxx:223
Muon::MuonNSWSegmentFinderTool::segmentSeedFromPads
std::vector< NSWSeed > segmentSeedFromPads(const LayerMeasVec &orderedClusters, const Muon::MuonSegment &etaSeg) const
Definition: MuonNSWSegmentFinderTool.cxx:916
python.LumiBlobConversion.pos
pos
Definition: LumiBlobConversion.py:18
Muon::NSWSeed::m_size
size_t m_size
Added measurements on track.
Definition: MuonNSWSegmentFinderTool.h:121
Muon::MuonNSWSegmentFinderTool::ChannelConstraint::TooWide
@ TooWide
MuonGM::MuonChannelDesign::channelHalfLength
double channelHalfLength(int st, bool left) const
STRIPS ONLY: calculate channel length on the given side of the x axis (for MM stereo strips)
Definition: MuonChannelDesign.h:381
python.SystemOfUnits.mm
int mm
Definition: SystemOfUnits.py:83
python.PyAthena.v
v
Definition: PyAthena.py:157
Muon::MuonNSWSegmentFinderTool::m_slTrackFitter
ToolHandle< Trk::ITrackFitter > m_slTrackFitter
Definition: MuonNSWSegmentFinderTool.h:153
DataVector::stdcont
const PtrVector & stdcont() const
Return the underlying std::vector of the container.
MuonGM::MuonChannelDesign::rightEdge
bool rightEdge(int channel, Amg::Vector2D &pos) const
STRIPS ONLY: Returns the right edge of the strip.
Definition: MuonChannelDesign.h:498
Trk::ParametersBase::momentum
const Amg::Vector3D & momentum() const
Access method for the momentum.
MuonGM::MuonChannelDesign
Definition: MuonChannelDesign.h:24
Muon::sTgcPrepData::detectorElement
virtual const MuonGM::sTgcReadoutElement * detectorElement() const override final
Returns the detector element corresponding to this PRD.
Definition: sTgcPrepData.h:139
MuonNSWSegmentFinderTool.h
ReadBchFromCool.good
good
Definition: ReadBchFromCool.py:433
MuonSectorMapping.h
Trk::DetectorElemType::MM
@ MM
a
TList * a
Definition: liststreamerinfos.cxx:10
InDetDD::other
@ other
Definition: InDetDD_Defs.h:16
Trk::Surface::globalToLocal
virtual bool globalToLocal(const Amg::Vector3D &glob, const Amg::Vector3D &mom, Amg::Vector2D &loc) const =0
Specified by each surface type: GlobalToLocal method without dynamic memory allocation - boolean chec...
Muon::NSWSeed
Definition: MuonNSWSegmentFinderTool.h:29
egammaEnergyPositionAllSamples::e2
double e2(const xAOD::CaloCluster &cluster)
return the uncorrected cluster energy in 2nd sampling
Amg::intersect
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the closest approach of two lines.
Definition: GeoPrimitivesHelpers.h:302
Muon::NSWSeed::MeasVec
std::vector< SeedMeasurement > MeasVec
Returns the contained measurements.
Definition: MuonNSWSegmentFinderTool.h:82
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
Muon::IMuonNSWSegmentFinderTool::SegmentMakingCache::constructedSegs
std::vector< std::unique_ptr< Muon::MuonSegment > > constructedSegs
Output vector to which the constructed segments are pushed_back.
Definition: IMuonNSWSegmentFinderTool.h:33
Trk::PlaneSurface
Definition: PlaneSurface.h:64
sTgcIdHelper::Pad
@ Pad
Definition: sTgcIdHelper.h:179
Muon::NSWSeed::SeedOR
SeedOR
Definition: MuonNSWSegmentFinderTool.h:89
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
unit
const PlainObject unit() const
This is a plugin that makes Eigen look like CLHEP & defines some convenience methods.
Definition: AmgMatrixBasePlugin.h:20
DeMoScan.first
bool first
Definition: DeMoScan.py:534
DEBUG
#define DEBUG
Definition: page_access.h:11
MuonGM::MuonChannelDesign::stereoAngle
double stereoAngle() const
returns the stereo angle
Definition: MuonChannelDesign.h:73
Muon::IMuonNSWSegmentFinderTool::SegmentMakingCache
Helper struct to parse the data around.
Definition: IMuonNSWSegmentFinderTool.h:31
Muon::MuonNSWSegmentFinderTool::layerNumber
int layerNumber(const Muon::MuonClusterOnTrack *cluster) const
Definition: MuonNSWSegmentFinderTool.cxx:885
Muon::NSWSeed::find
bool find(const SeedMeasurement &meas) const
Checks whether the measurement is already part of the seed.
Definition: MuonNSWSegmentFinderTool.cxx:248
RunTileMonitoring.clusters
clusters
Definition: RunTileMonitoring.py:133
Muon::MuonNSWSegmentFinderTool::wedgeNumber
int wedgeNumber(const Muon::MuonClusterOnTrack *cluster) const
Definition: MuonNSWSegmentFinderTool.cxx:878
Trk::RIO_OnTrack::identify
virtual Identifier identify() const final
return the identifier -extends MeasurementBase
Definition: RIO_OnTrack.h:155
python.utility.LHE.merge
def merge(input_file_pattern, output_file)
Merge many input LHE files into a single output file.
Definition: LHE.py:17
MuonGM::MMReadoutElement
An MMReadoutElement corresponds to a single STGC module; therefore typicaly a barrel muon station con...
Definition: MMReadoutElement.h:23
Muon::MuonNSWSegmentFinderTool::find3DSegments
std::vector< std::unique_ptr< Muon::MuonSegment > > find3DSegments(const EventContext &ctx, const std::vector< const Muon::MuonClusterOnTrack * > &MuonClusters, std::vector< std::unique_ptr< Muon::MuonSegment >> &etaSegs, int singleWedge=0) const
Definition: MuonNSWSegmentFinderTool.cxx:550
Muon::IMuonIdHelperSvc
Interface for Helper service that creates muon Identifiers and can be used to print Identifiers.
Definition: IMuonIdHelperSvc.h:26
Muon::NSWSeed::SeedOR::Same
@ Same
Trk::FitQuality::chiSquared
double chiSquared() const
returns the of the overall track fit
Definition: FitQuality.h:56
Muon::MuonNSWSegmentFinderTool::m_muonClusterCreator
ToolHandle< IMuonClusterOnTrackCreator > m_muonClusterCreator
Definition: MuonNSWSegmentFinderTool.h:179
Muon::MuonSectorMapping
Definition: MuonSectorMapping.h:20
Muon::MuonNSWSegmentFinderTool::caloConstraint
std::unique_ptr< Trk::PseudoMeasurementOnTrack > caloConstraint(const Trk::TrackParameters &startpar) const
Definition: MuonNSWSegmentFinderTool.cxx:658
Trk::FitQuality::numberDoF
int numberDoF() const
returns the number of degrees of freedom of the overall track or vertex fit as integer
Definition: FitQuality.h:60
ReadBchFromCool.nBad
nBad
Definition: ReadBchFromCool.py:455
Muon::MuonSegment::globalPosition
virtual const Amg::Vector3D & globalPosition() const override final
global position
Definition: MuonSpectrometer/MuonReconstruction/MuonRecEvent/MuonSegment/MuonSegment/MuonSegment.h:157
Trk::RIO_OnTrack::associatedSurface
virtual const Surface & associatedSurface() const override=0
returns the surface for the local to global transformation
Muon::MuonNSWSegmentFinderTool::m_ipConstraint
Gaudi::Property< bool > m_ipConstraint
Definition: MuonNSWSegmentFinderTool.h:181
Muon::MuonNSWSegmentFinderTool::fit
std::unique_ptr< Trk::Track > fit(const EventContext &ctx, const std::vector< const Trk::MeasurementBase * > &fit_meas, const Trk::TrackParameters &perigee) const
Definition: MuonNSWSegmentFinderTool.cxx:431
Trk::Segment::NswQuadAlign
@ NswQuadAlign
Definition: TrkEvent/TrkSegment/TrkSegment/Segment.h:79
calibdata.copy
bool copy
Definition: calibdata.py:27
MuonGM::sTgcReadoutElement::getPadDesign
const MuonPadDesign * getPadDesign(const Identifier &id) const
returns the MuonChannelDesign class for the given identifier
Definition: MuonDetDescr/MuonReadoutGeometry/MuonReadoutGeometry/sTgcReadoutElement.h:287
python.Constants.VERBOSE
int VERBOSE
Definition: Control/AthenaCommon/python/Constants.py:14
Muon::MuonSegment
Definition: MuonSpectrometer/MuonReconstruction/MuonRecEvent/MuonSegment/MuonSegment/MuonSegment.h:45
AthAlgTool
Definition: AthAlgTool.h:26
Trk::PlaneSurface::localToGlobal
virtual void localToGlobal(const Amg::Vector2D &locp, const Amg::Vector3D &mom, Amg::Vector3D &glob) const override final
Specified for PlaneSurface: LocalToGlobal method without dynamic memory allocation.
Definition: PlaneSurface.cxx:204
Trk::Segment::NswStgcSeeded
@ NswStgcSeeded
Definition: TrkEvent/TrkSegment/TrkSegment/Segment.h:77
Muon::IMuonNSWSegmentFinderTool::SegmentMakingCache::usedHits
std::set< Identifier > usedHits
Set of all hits used in the segment making.
Definition: IMuonNSWSegmentFinderTool.h:41
MuonParameters::beta
@ beta
Definition: MuonParamDefs.h:144
Muon::MuonNSWSegmentFinderTool::m_caloConstraint
Gaudi::Property< bool > m_caloConstraint
Use a virtual point at the calorimeter exit with same Z as constraint...
Definition: MuonNSWSegmentFinderTool.h:184
FitQuality.h
Trk::Surface
Definition: Tracking/TrkDetDescr/TrkSurfaces/TrkSurfaces/Surface.h:75
Muon::NSWSeed::m_chi2
double m_chi2
Chi2.
Definition: MuonNSWSegmentFinderTool.h:119
MuonGM::MuonPadDesign::distanceToPad
Amg::Vector2D distanceToPad(const Amg::Vector2D &pos, int channel) const
Definition: MuonPadDesign.cxx:118
Muon::sTgcPrepData
Class to represent sTgc measurements.
Definition: sTgcPrepData.h:20
Muon::MuonNSWSegmentFinderTool::getCalibratedClusters
MeasVec getCalibratedClusters(NSWSeed &seed) const
Definition: MuonNSWSegmentFinderTool.cxx:1447
dq_make_web_display.cl
cl
print [x.__class__ for x in toList(dqregion.getSubRegions()) ]
Definition: dq_make_web_display.py:26
DataVector::size
size_type size() const noexcept
Returns the number of elements in the collection.
Muon::MuonNSWSegmentFinderTool::initialize
virtual StatusCode initialize() override
Definition: MuonNSWSegmentFinderTool.cxx:262
Muon::MuonNSWSegmentFinderTool::resolveAmbiguities
std::vector< NSWSeed > resolveAmbiguities(std::vector< NSWSeed > &&unresolved) const
Definition: MuonNSWSegmentFinderTool.cxx:1321
Muon::MuonNSWSegmentFinderTool::isStrip
bool isStrip(const Muon::MuonClusterOnTrack *clust) const
Definition: MuonNSWSegmentFinderTool.cxx:689
python.SystemOfUnits.degree
tuple degree
Definition: SystemOfUnits.py:106
Muon::MuonNSWSegmentFinderTool::m_useStereoSeeding
Gaudi::Property< bool > m_useStereoSeeding
Definition: MuonNSWSegmentFinderTool.h:189
Trk::Surface::localToGlobal
virtual void localToGlobal(const Amg::Vector2D &locp, const Amg::Vector3D &mom, Amg::Vector3D &glob) const =0
Specified by each surface type: LocalToGlobal method without dynamic memory allocation.
Eta
@ Eta
Definition: RPCdef.h:8
Muon::MuonClusterOnTrack
Base class for Muon cluster RIO_OnTracks.
Definition: MuonClusterOnTrack.h:34
Muon::MuonSegment::globalDirection
const Amg::Vector3D & globalDirection() const
global direction
Definition: MuonSpectrometer/MuonReconstruction/MuonRecEvent/MuonSegment/MuonSegment/MuonSegment.h:163
generate::Zero
void Zero(TH1D *hin)
Definition: generate.cxx:32
MuonGM::MuonChannelDesign::leftEdge
bool leftEdge(int channel, Amg::Vector2D &pos) const
STRIPS ONLY: Returns the left edge of the strip.
Definition: MuonChannelDesign.h:491
Trk::PrepRawData::detectorElement
virtual const TrkDetElementBase * detectorElement() const =0
return the detector element corresponding to this PRD The pointer will be zero if the det el is not d...
Muon::NSWSeed::SeedMeasurement::distance
double distance() const
Definition: MuonNSWSegmentFinderTool.h:52
Muon::MuonNSWSegmentFinderTool::MeasVec
NSWSeed::MeasVec MeasVec
Definition: MuonNSWSegmentFinderTool.h:199
Muon::NSWSeed::SeedOR::SubSet
@ SubSet