Belle II Software development
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
21using namespace Belle2;
22using namespace TrackFindingCDC;
23
24void 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
67bool 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
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
157void 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));
192 }
193 }
194}
195
196void 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
209std::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
256int 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 set 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
337std::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
357ISuperLayer 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
365ISuperLayer 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 CDCWireHit & getWireHit() const
Getter for the wire hit.
Definition: CDCRecoHit3D.h:238
const Vector2D & getRecoPos2D() const
Getter for the 2d position of the hit.
Definition: CDCRecoHit3D.h:297
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()
Constructs a basic assumption, what the z0 start position and the sz slope are, including some broad ...
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 handling of orientation relate...
Definition: Vector2D.h:32
double cylindricalR() const
Gives the cylindrical radius of the vector. Same as norm()
Definition: Vector2D.h:557
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:453
bool hasNAN() const
Checks if one of the coordinates is NAN.
Definition: Vector2D.h:149
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