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