Belle II Software  release-08-01-10
AxialTrackUtil.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #include <tracking/trackFindingCDC/processing/AxialTrackUtil.h>
9 
10 #include <tracking/trackFindingCDC/fitting/CDCRiemannFitter.h>
11 #include <tracking/trackFindingCDC/fitting/CDCKarimakiFitter.h>
12 #include <tracking/trackFindingCDC/fitting/CDCObservations2D.h>
13 
14 #include <tracking/trackFindingCDC/eventdata/tracks/CDCTrack.h>
15 #include <tracking/trackFindingCDC/eventdata/hits/CDCWireHit.h>
16 #include <tracking/trackFindingCDC/eventdata/trajectories/CDCTrajectorySZ.h>
17 #include <tracking/trackFindingCDC/eventdata/trajectories/CDCTrajectory2D.h>
18 
19 #include <tracking/trackFindingCDC/utilities/Algorithms.h>
20 
21 using namespace Belle2;
22 using namespace TrackFindingCDC;
23 
24 void AxialTrackUtil::addCandidateFromHits(const std::vector<const CDCWireHit*>& foundAxialWireHits,
25  const std::vector<const CDCWireHit*>& allAxialWireHits,
26  std::vector<CDCTrack>& axialTracks,
27  bool withPostprocessing)
28 {
29  if (foundAxialWireHits.empty()) return;
30 
31  // New track
32  CDCTrack track;
33 
34  // Fit trajectory
36  CDCTrajectory2D trajectory2D = fitter.fit(foundAxialWireHits);
37  track.setStartTrajectory3D(CDCTrajectory3D(trajectory2D, CDCTrajectorySZ::basicAssumption()));
38 
39  // Reconstruct and add hits
40  for (const CDCWireHit* wireHit : foundAxialWireHits) {
41  AutomatonCell& automatonCell = wireHit->getAutomatonCell();
42  if (automatonCell.hasTakenFlag()) continue;
43  CDCRecoHit3D recoHit3D = CDCRecoHit3D::reconstructNearest(wireHit, trajectory2D);
44  track.push_back(std::move(recoHit3D));
45 
46  automatonCell.setTakenFlag(true);
47  }
48  track.sortByArcLength2D();
49 
50  // Change everything again in the postprocessing, if desired
51  bool success = withPostprocessing ? postprocessTrack(track, allAxialWireHits) : true;
52  if (success) {
54  for (const CDCRecoHit3D& recoHit3D : track) {
55  recoHit3D.getWireHit().getAutomatonCell().setTakenFlag(true);
56  }
57  axialTracks.emplace_back(std::move(track));
58  } else {
60  for (const CDCRecoHit3D& recoHit3D : track) {
61  recoHit3D.getWireHit().getAutomatonCell().setMaskedFlag(true);
62  recoHit3D.getWireHit().getAutomatonCell().setTakenFlag(false);
63  }
64  }
65 }
66 
67 bool AxialTrackUtil::postprocessTrack(CDCTrack& track, const std::vector<const CDCWireHit*>& allAxialWireHits)
68 {
70 
73  if (not checkTrackQuality(track)) {
74  return false;
75  }
76 
77  AxialTrackUtil::assignNewHitsToTrack(track, allAxialWireHits);
79 
82  if (not checkTrackQuality(track)) {
83  return false;
84  }
85  return true;
86 }
87 
89 {
90  return not(track.size() < 5);
91 }
92 
94 {
95  // Set the start point of the trajectory to the first hit
96  if (track.size() < 5) return;
97 
98  CDCObservations2D observations2D(EFitPos::c_RecoPos);
99  for (const CDCRecoHit3D& item : track) {
100  observations2D.append(item);
101  }
102 
104  CDCTrajectory2D trackTrajectory2D = fitter.fit(observations2D);
105  Vector2D center = trackTrajectory2D.getGlobalCenter();
106 
107  // Arm used as a proxy for the charge of the track
108  // Correct if the track originates close to the origin
109  ESign expectedCharge = AxialTrackUtil::getMajorArmSign(track, center);
110 
111  if (trackTrajectory2D.getChargeSign() != expectedCharge) trackTrajectory2D.reverse();
112 
113  trackTrajectory2D.setLocalOrigin(trackTrajectory2D.getGlobalPerigee());
114  for (CDCRecoHit3D& recoHit : track) {
115  AxialTrackUtil::updateRecoHit3D(trackTrajectory2D, recoHit);
116  }
117 
118  track.sortByArcLength2D();
119 
120  CDCTrajectory3D trajectory3D(trackTrajectory2D, CDCTrajectorySZ::basicAssumption());
121  track.setStartTrajectory3D(trajectory3D);
122 
123  Vector3D backPosition = track.back().getRecoPos3D();
124  trajectory3D.setLocalOrigin(backPosition);
125  track.setEndTrajectory3D(trajectory3D);
126 }
127 
129 {
130  hit = CDCRecoHit3D::reconstructNearest(&hit.getWireHit(), trajectory2D);
131 
132  // Recalculate the perpS of the hits
133  double arcLength2D = hit.getArcLength2D();
134  if (arcLength2D < -trajectory2D.getArcLength2DPeriod() / 4.0) {
135  arcLength2D += trajectory2D.getArcLength2DPeriod();
136  }
137  hit.setArcLength2D(arcLength2D);
138 }
139 
140 void AxialTrackUtil::deleteHitsFarAwayFromTrajectory(CDCTrack& track, double maximumDistance)
141 {
142  const CDCTrajectory2D& trajectory2D = track.getStartTrajectory3D().getTrajectory2D();
143  auto farFromTrajectory = [&trajectory2D, &maximumDistance](CDCRecoHit3D & recoHit3D) {
144  Vector2D refPos2D = recoHit3D.getRefPos2D();
145  double distance = trajectory2D.getDist2D(refPos2D) - recoHit3D.getSignedRecoDriftLength();
146  if (std::fabs(distance) > maximumDistance) {
147  recoHit3D.getWireHit().getAutomatonCell().setTakenFlag(false);
148  // This must be here as the deleted hits must not participate in the hough search again.
149  recoHit3D.getWireHit().getAutomatonCell().setMaskedFlag(true);
150  return true;
151  }
152  return false;
153  };
154  erase_remove_if(track, farFromTrajectory);
155 }
156 
157 void AxialTrackUtil::deleteTracksWithLowFitProbability(std::vector<CDCTrack>& axialTracks,
158  double minimal_probability_for_good_fit)
159 {
161  const auto lowPValue = [&](const CDCTrack & track) {
162  CDCTrajectory2D fittedTrajectory = trackFitter.fit(track);
163  // Keep good fits - p-value is not really a probability,
164  // but what can you do if the original author did not mind...
165  if (not(fittedTrajectory.getPValue() >= minimal_probability_for_good_fit)) {
166  // Release hits
167  track.forwardTakenFlag(false);
168  return true;
169  }
170  return false;
171  };
172  erase_remove_if(axialTracks, lowPValue);
173 }
174 
176  const std::vector<const CDCWireHit*>& allAxialWireHits,
177  double minimalDistance)
178 {
179  if (track.size() < 10) return;
180 
181  const CDCTrajectory2D& trackTrajectory2D = track.getStartTrajectory3D().getTrajectory2D();
182 
183  for (const CDCWireHit* wireHit : allAxialWireHits) {
184  if (wireHit->getAutomatonCell().hasTakenFlag()) continue;
185 
186  CDCRecoHit3D recoHit3D = CDCRecoHit3D::reconstructNearest(wireHit, trackTrajectory2D);
187  const Vector2D& recoPos2D = recoHit3D.getRecoPos2D();
188 
189  if (fabs(trackTrajectory2D.getDist2D(recoPos2D)) < minimalDistance) {
190  track.push_back(std::move(recoHit3D));
191  recoHit3D.getWireHit().getAutomatonCell().setTakenFlag();
192  }
193  }
194 }
195 
196 void AxialTrackUtil::deleteShortTracks(std::vector<CDCTrack>& axialTracks, double minimal_size)
197 {
198  const auto isShort = [&](const CDCTrack & track) {
199  if (track.size() < minimal_size) {
200  // Release hits
201  track.forwardTakenFlag(false);
202  return true;
203  }
204  return false;
205  };
206  erase_remove_if(axialTracks, isShort);
207 }
208 
209 std::vector<CDCRecoHit3D> AxialTrackUtil::splitBack2BackTrack(CDCTrack& track)
210 {
211  std::vector<CDCRecoHit3D> removedHits;
212 
213  if (track.size() < 5) return removedHits;
214  if (not isBack2BackTrack(track)) return removedHits;
215 
216  Vector2D center = track.getStartTrajectory3D().getGlobalCenter();
217  ESign majorArmSign = getMajorArmSign(track, center);
218 
219  auto isOnMajorArm = [&center, &majorArmSign](const CDCRecoHit3D & hit) {
220  return getArmSign(hit, center) == majorArmSign;
221  };
222 
223  auto itFirstMinorArmHit = std::stable_partition(track.begin(),
224  track.end(),
225  isOnMajorArm);
226 
227  for (const CDCRecoHit3D& recoHit3D : asRange(itFirstMinorArmHit, track.end())) {
228  recoHit3D.getWireHit().getAutomatonCell().setTakenFlag(false);
229  removedHits.push_back(recoHit3D);
230  }
231  track.erase(itFirstMinorArmHit, track.end());
232 
233  return removedHits;
234 }
235 
237 {
238  Vector2D center = track.getStartTrajectory3D().getGlobalCenter();
239  int armSignVote = getArmSignVote(track, center);
240  if (std::abs(armSignVote) < int(track.size()) and std::fabs(center.cylindricalR()) > 60.) {
241  return true;
242  }
243  return false;
244 }
245 
247 {
248  int armSignVote = getArmSignVote(track, center);
249  if (armSignVote > 0) {
250  return ESign::c_Plus;
251  } else {
252  return ESign::c_Minus;
253  }
254 }
255 
256 int AxialTrackUtil::getArmSignVote(const CDCTrack& track, const Vector2D& center)
257 {
258  int votePos = 0;
259  int voteNeg = 0;
260 
261  if (center.hasNAN()) {
262  B2WARNING("Trajectory is not setted or wrong!");
263  return 0;
264  }
265 
266  for (const CDCRecoHit3D& hit : track) {
267  ESign armSign = getArmSign(hit, center);
268 
269  if (armSign == ESign::c_Plus) {
270  ++votePos;
271  } else if (armSign == ESign::c_Minus) {
272  ++voteNeg;
273  } else {
274  B2ERROR("Strange behaviour of getArmSignVote");
275  }
276  }
277  int armSignVote = votePos - voteNeg;
278  return armSignVote;
279 }
280 
282 {
283  return sign(center.isRightOrLeftOf(hit.getRecoPos2D()));
284 }
285 
287 {
288  double apogeeArcLength = fabs(track.getStartTrajectory3D().getGlobalCircle().perimeter()) / 2.;
289 
290  std::array<int, ISuperLayerUtil::c_N> nForwardArmHitsBySLayer = {0};
291  std::array<int, ISuperLayerUtil::c_N> nBackwardArmHitsBySLayer = {0};
292 
293  // Count the number of hits in the outgoing and ingoing arm per superlayer.
294  for (const CDCRecoHit3D& hit : track) {
295  if ((hit.getArcLength2D() <= apogeeArcLength) and (hit.getArcLength2D() > 0)) {
296  nForwardArmHitsBySLayer[hit.getISuperLayer()]++;
297  } else {
298  nBackwardArmHitsBySLayer[hit.getISuperLayer()]++;
299  }
300  }
301 
302  std::vector<ISuperLayer> forwardSLayerHoles = getSLayerHoles(nForwardArmHitsBySLayer);
303  std::vector<ISuperLayer> backwardSLayerHoles = getSLayerHoles(nBackwardArmHitsBySLayer);
304 
305  // No missing layers
306  if (forwardSLayerHoles.empty() and backwardSLayerHoles.empty()) return;
307 
308  // We only check for holes in the forward arm for now
309  // We do not use emptyEndingSLayers here, as it would leave to a severy efficiency drop.
310  assert(std::is_sorted(forwardSLayerHoles.begin(), forwardSLayerHoles.end()));
311  if (forwardSLayerHoles.empty()) return;
312 
313  const ISuperLayer breakSLayer = forwardSLayerHoles.front();
314 
315  auto isInBackwardArm = [apogeeArcLength](const CDCRecoHit3D & recoHit3D) {
316  if ((recoHit3D.getArcLength2D() >= apogeeArcLength) or (recoHit3D.getArcLength2D() < 0)) {
317  recoHit3D.getWireHit().getAutomatonCell().unsetTakenFlag();
318  return true;
319  }
320  return false;
321  };
322 
323  erase_remove_if(track, isInBackwardArm);
324 
325  auto isAfterSLayerBreak = [breakSLayer](const CDCRecoHit3D & recoHit3D) {
326  recoHit3D.getWireHit().getAutomatonCell().unsetTakenFlag();
327  if (recoHit3D.getISuperLayer() >= breakSLayer) {
328  recoHit3D.getWireHit().getAutomatonCell().unsetTakenFlag();
329  return true;
330  }
331  return false;
332  };
333 
334  erase_remove_if(track, isAfterSLayerBreak);
335 }
336 
337 std::vector<ISuperLayer> AxialTrackUtil::getSLayerHoles(const std::array<int, ISuperLayerUtil::c_N>& nHitsBySLayer)
338 {
339  std::vector<ISuperLayer> sLayerHoles;
340 
341  // Find the start end end point.
342  ISuperLayer firstSlayer = getFirstOccupiedISuperLayer(nHitsBySLayer);
343  ISuperLayer lastSlayer = getLastOccupiedISuperLayer(nHitsBySLayer);
344 
345  if (ISuperLayerUtil::isInvalid(firstSlayer) or ISuperLayerUtil::isInvalid(lastSlayer)) {
346  return sLayerHoles;
347  }
348 
349  for (ISuperLayer iSLayer = firstSlayer; iSLayer <= lastSlayer; iSLayer += 2) {
350  if (nHitsBySLayer[iSLayer] == 0) {
351  sLayerHoles.push_back(iSLayer);
352  }
353  }
354  return sLayerHoles;
355 }
356 
357 ISuperLayer AxialTrackUtil::getFirstOccupiedISuperLayer(const std::array<int, ISuperLayerUtil::c_N>& nHitsBySLayer)
358 {
359  for (ISuperLayer iSLayer = 0; iSLayer < ISuperLayerUtil::c_N; ++iSLayer) {
360  if (nHitsBySLayer[iSLayer] > 0) return iSLayer;
361  }
363 }
364 
365 ISuperLayer AxialTrackUtil::getLastOccupiedISuperLayer(const std::array<int, ISuperLayerUtil::c_N>& nHitsBySLayer)
366 {
367  for (ISuperLayer iSLayer = ISuperLayerUtil::c_N - 1; iSLayer >= 0; --iSLayer) {
368  if (nHitsBySLayer[iSLayer] > 0) return iSLayer;
369  }
371 }
Cell used by the cellular automata.
Definition: AutomatonCell.h:29
void setTakenFlag(bool setTo=true)
Sets the taken flag to the given value. Default value true.
bool hasTakenFlag() const
Gets the current state of the taken marker flag.
CDCTrajectory2D fit(const CDCObservations2D &observations2D) const
Fits a collection of observation drift circles.
Class implementing the fitter using Karimakis method.
static const CDCKarimakiFitter & getNoDriftVarianceFitter()
Static getter for a general fitter that does not use the drift length variances.
Class serving as a storage of observed drift circles to present to the Riemann fitter.
std::size_t append(const CDCWireHit &wireHit, ERightLeft rlInfo=ERightLeft::c_Unknown)
Appends the hit circle at wire reference position without a right left passage hypotheses.
Class representing a three dimensional reconstructed hit.
Definition: CDCRecoHit3D.h:52
const Vector2D & getRecoPos2D() const
Getter for the 2d position of the hit.
Definition: CDCRecoHit3D.h:297
const CDCWireHit & getWireHit() const
Getter for the wire hit.
Definition: CDCRecoHit3D.h:238
static CDCRecoHit3D reconstructNearest(const CDCWireHit *axialWireHit, const CDCTrajectory2D &trajectory2D)
Reconstruct a three dimensional hit from a wire hit (as in reconstruct(rlWireHit, trajectory2D)),...
Class implementing the Riemann fit for two dimensional trajectory circle.
static const CDCRiemannFitter & getFitter()
Static getter for a general Riemann fitter.
Class representing a sequence of three dimensional reconstructed hits.
Definition: CDCTrack.h:41
Particle trajectory as it is seen in xy projection represented as a circle.
void reverse()
Reverses the trajectory in place.
double setLocalOrigin(const Vector2D &localOrigin)
Setter for the origin of the local coordinate system.
ESign getChargeSign() const
Gets the charge sign of the trajectory.
double getPValue() const
Getter for p-value.
double getArcLength2DPeriod() const
Getter for the arc length for one round trip around the trajectory.
Vector2D getGlobalCenter() const
Getter for the center of the trajectory in global coordinates.
Vector2D getGlobalPerigee() const
Getter for the closest approach on the trajectory to the global origin.
double getDist2D(const Vector2D &point) const
Calculates the distance from the point to the trajectory as seen from the xy projection.
Particle full three dimensional trajectory.
double setLocalOrigin(const Vector3D &localOrigin)
Setter for the origin of the local coordinate system.
static CDCTrajectorySZ basicAssumption()
Constucts a basic assumption, what the z0 start position and the sz slope are, including some broad v...
Class representing a hit wire in the central drift chamber.
Definition: CDCWireHit.h:55
AutomatonCell & getAutomatonCell() const
Mutable getter for the automaton cell.
Definition: CDCWireHit.h:286
A two dimensional vector which is equipped with functions for correct handeling of orientation relat...
Definition: Vector2D.h:35
double cylindricalR() const
Gives the cylindrical radius of the vector. Same as norm()
Definition: Vector2D.h:569
ERightLeft isRightOrLeftOf(const Vector2D &rhs) const
Indicates if the given vector is more left or more right if you looked in the direction of this vecto...
Definition: Vector2D.h:465
bool hasNAN() const
Checks if one of the coordinates is NAN.
Definition: Vector2D.h:161
A three dimensional vector.
Definition: Vector3D.h:33
ESign
Enumeration for the distinct sign values of floating point variables.
Definition: ESign.h:27
Abstract base class for different kinds of events.
static void deleteShortTracks(std::vector< CDCTrack > &axialTracks, double minimal_size=5)
Remove tracks that are shorter than the given number of hits.
static void assignNewHitsToTrack(CDCTrack &track, const std::vector< const CDCWireHit * > &allAxialWireHits, double minimalDistance=0.2)
Assign new hits to the track basing on the distance from the hit to the track.
static void normalizeTrack(CDCTrack &track)
Refit and resort the track. Unmask all hits.
static ESign getArmSign(const CDCRecoHit3D &hit, const Vector2D &center)
Calculate whether the hits is to the right or to the left relative to the line from origin to the giv...
static std::vector< ISuperLayer > getSLayerHoles(const std::array< int, ISuperLayerUtil::c_N > &nHitsBySLayer)
Helper function getting the empty axial! super layers that appear in the chain of super layers that i...
static bool checkTrackQuality(const CDCTrack &track)
Check track quality – currently based on number of hits only.
static void updateRecoHit3D(const CDCTrajectory2D &trajectory2D, CDCRecoHit3D &hit)
update given CDCRecoHit3D with given trajectory
static void removeHitsAfterSuperLayerBreak(CDCTrack &track)
Searches for a break in the super layer chain and remove all hits that come after that.
static void addCandidateFromHits(const std::vector< const CDCWireHit * > &foundAxialWireHits, const std::vector< const CDCWireHit * > &allAxialWireHits, std::vector< CDCTrack > &axialTracks, bool withPostprocessing=true)
Create CDCTrack using CDCWireHit hits and store it in the list. Then call the postprocessing on it.
static void deleteHitsFarAwayFromTrajectory(CDCTrack &track, double maximumDistance=0.2)
Postprocessing: Delete axial hits that do not "match" to the given track.
static int getArmSignVote(const CDCTrack &track, const Vector2D &center)
Calculate the sum of right and left votes for the hits relative to the center.
static ISuperLayer getFirstOccupiedISuperLayer(const std::array< int, ISuperLayerUtil::c_N > &nHitsBySLayer)
Helper function to extract the first filled entry in the array of super layers ( = the start superlay...
static bool isBack2BackTrack(CDCTrack &track)
Checks whether the track has hits on both arms as seen from the origin.
static bool postprocessTrack(CDCTrack &track, const std::vector< const CDCWireHit * > &allAxialWireHits)
Perform all track postprocessing - return whether the track is considered good after the postprocessi...
static ISuperLayer getLastOccupiedISuperLayer(const std::array< int, ISuperLayerUtil::c_N > &nHitsBySLayer)
Helper function to extract the last filled entry in the array of super layers ( = the final superlaye...
static std::vector< CDCRecoHit3D > splitBack2BackTrack(CDCTrack &track)
Tries to split back-to-back tracks into two different tracks.
static ESign getMajorArmSign(const CDCTrack &track, const Vector2D &center)
Calculate whether the majority of hits is to the right or to the left relative to the line from origi...
static void deleteTracksWithLowFitProbability(std::vector< CDCTrack > &axialTracks, double minimal_probability_for_good_fit=0.4)
Check an (improper) p-values of the tracks. If they are below the given value, delete the track from ...
static const ISuperLayer c_Invalid
Constant making an invalid superlayer id.
Definition: ISuperLayer.h:65
static const ISuperLayer c_N
Constant representing the total number of cdc superlayers.
Definition: ISuperLayer.h:56
static bool isInvalid(ISuperLayer iSuperLayer)
Indicates if the given number corresponds to a true cdc superlayer - excludes the logic ids for inner...
Definition: ISuperLayer.cc:38