Belle II Software development
TrackCombiner.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/findlets/minimal/TrackCombiner.h>
9
10#include <tracking/trackingUtilities/eventdata/tracks/CDCTrack.h>
11#include <tracking/trackingUtilities/eventdata/segments/CDCSegment3D.h>
12#include <tracking/trackingUtilities/eventdata/hits/CDCRLWireHit.h>
13#include <tracking/trackingUtilities/eventdata/hits/CDCWireHit.h>
14
15#include <tracking/trackingUtilities/numerics/Index.h>
16
17#include <tracking/trackingUtilities/utilities/WeightedRelation.h>
18#include <tracking/trackingUtilities/utilities/Functional.h>
19#include <tracking/trackingUtilities/utilities/Range.h>
20#include <tracking/trackingUtilities/utilities/StringManipulation.h>
21
22#include <framework/core/ModuleParamList.templateDetails.h>
23
24#include <map>
25#include <deque>
26
27using namespace Belle2;
28using namespace CDC;
29using namespace TrackFindingCDC;
30using namespace TrackingUtilities;
31
32namespace {
33 std::array<int, ISuperLayerUtil::c_N> getNHitsByISuperLayer(const CDCTrack& track)
34 {
35 std::array<int, ISuperLayerUtil::c_N> nHitsBySLayer = {0};
36 for (const CDCRecoHit3D& hit : track) {
37 nHitsBySLayer[hit.getISuperLayer()]++;
38 }
39 return nHitsBySLayer;
40 }
41
42 CDCTrack condense(const Path<const CDCSegment3D>& segmentPath)
43 {
44 CDCTrack result;
45 for (const CDCSegment3D* segment : segmentPath) {
46 for (const CDCRecoHit3D& recoHit3D : *segment) {
47 result.push_back(recoHit3D);
48 }
49 }
50 if (segmentPath.size()) {
51 result.setStartTrajectory3D(segmentPath.front()->getTrajectory3D());
52 }
53 return result;
54 }
55
56 std::vector<CDCRLWireHit> getRLWireHitSegment(const CDCSegment3D& segment3D)
57 {
58 std::vector<CDCRLWireHit> result;
59 result.reserve(segment3D.size());
60 for (const CDCRecoHit3D& recoHit3D : segment3D) {
61 result.push_back(recoHit3D.getRLWireHit());
62 }
63 return result;
64 }
65
66 std::vector<std::pair<std::pair<Index, Index>, CDCRLWireHit>> getCommonRLWireHits(const std::vector<CDCRLWireHit>& rlWireHits1,
67 const std::vector<CDCRLWireHit>& rlWireHits2)
68 {
69 std::vector<std::pair<std::pair<Index, Index>, CDCRLWireHit>> result;
70 Index index1 = -1;
71 for (const CDCRLWireHit& rlWireHit : rlWireHits1) {
72 ++index1;
73 auto itRLWireHits2 = std::find(rlWireHits2.begin(), rlWireHits2.end(), rlWireHit);
74 if (itRLWireHits2 == rlWireHits2.end()) continue;
75 Index index2 = std::distance(rlWireHits2.begin(), itRLWireHits2);
76 assert(index2 >= 0);
77 result.push_back({{index1, index2}, rlWireHit});
78 }
79 return result;
80 }
81}
82
84
86{
87 return "Combines two sets of tracks to one final set by merging tracks that have large overlaps.";
88}
89
91 const std::string& prefix)
92{
93 moduleParamList->addParameter(prefixed(prefix, "identifyCommonSegments"),
95 "Activate the identification of common segments",
97}
98
99void TrackCombiner::apply(const std::vector<CDCTrack>& inputTracks,
100 const std::vector<CDCTrack>& secondInputTracks,
101 std::vector<CDCTrack>& outputTracks)
102{
103 using InTracks = std::array<const CDCTrack*, 2>;
104
105 // Constructing map of back links
106 std::map<const CDCWireHit*, InTracks> inTracksByWireHit;
107 for (const CDCTrack& track : inputTracks) {
108 for (const CDCRecoHit3D& recoHit3D : track) {
109 const CDCWireHit& wireHit = recoHit3D.getWireHit();
110 if (inTracksByWireHit.count(&wireHit) == 0) inTracksByWireHit[&wireHit] = {nullptr, nullptr};
111 inTracksByWireHit[&wireHit][0] = &track;
112 // Prepare hits for the cellular automaton
113 wireHit->unsetTemporaryFlags();
114 wireHit->unsetMaskedFlag();
115 }
116 }
117
118 for (const CDCTrack& track : secondInputTracks) {
119 for (const CDCRecoHit3D& recoHit3D : track) {
120 const CDCWireHit& wireHit = recoHit3D.getWireHit();
121 if (inTracksByWireHit.count(&wireHit) == 0) inTracksByWireHit[&wireHit] = {nullptr, nullptr};
122 inTracksByWireHit[&wireHit][1] = &track;
123 // Prepare hits for the cellular automaton
124 wireHit->unsetTemporaryFlags();
125 wireHit->unsetMaskedFlag();
126 }
127 }
128
129 // Rank tracks by number of superlayers touched first and number of hits second
130 std::vector<std::pair<std::pair<int, size_t>, const CDCTrack*> > rankedTracks;
131 for (const CDCTrack& track : inputTracks) {
132 std::array<int, ISuperLayerUtil::c_N> nHitsBySLayer = getNHitsByISuperLayer(track);
133 int nSL = std::count_if(nHitsBySLayer.begin(), nHitsBySLayer.end(), Id() > 0);
134 rankedTracks.push_back({{nSL, track.size()}, &track});
135 }
136 for (const CDCTrack& track : secondInputTracks) {
137 std::array<int, ISuperLayerUtil::c_N> nHitsBySLayer = getNHitsByISuperLayer(track);
138 int nSL = std::count_if(nHitsBySLayer.begin(), nHitsBySLayer.end(), Id() > 0);
139 rankedTracks.push_back({{nSL, track.size()}, &track});
140 }
141 std::sort(rankedTracks.begin(), rankedTracks.end(), GreaterOf<First>());
142
143 // Memory for the split segments
144 std::deque<CDCSegment3D> segments;
145
146 // Memory for the relations between tracks to be followed on linking
147 std::vector<WeightedRelation<const CDCSegment3D>> segmentRelations;
148
149 // Also keep a record to which of the tracks the segment
150 std::map<const CDCSegment3D*, InTracks> inTracksBySegment;
151
152 // Split tracks into segments - break segment such that there will be matching pieces with the other tracks
153 for (const std::pair<std::pair<int, size_t>, const CDCTrack*>& rankedTrack : rankedTracks) {
154 // const std::pair<ISuperLayer, size_t> rank = rankedTrack.first;
155 const CDCTrack* track = rankedTrack.second;
156 std::vector<std::pair<InTracks, CDCSegment3D> > segmentsInTrack;
157
158 // Split track into segments
159 CDCSegment3D* currentSegment = nullptr;
160 ISuperLayer lastISuperLayer = -1;
161 InTracks lastTracksForHit{{nullptr, nullptr}};
162 for (const CDCRecoHit3D& recoHit3D : *track) {
163 ISuperLayer iSuperLayer = recoHit3D.getISuperLayer();
164 std::array<const CDCTrack*, 2> tracksForWireHit = inTracksByWireHit[&recoHit3D.getWireHit()];
165 if (not currentSegment or iSuperLayer != lastISuperLayer or tracksForWireHit != lastTracksForHit) {
166 // Make new segments
167 segmentsInTrack.push_back({tracksForWireHit, CDCSegment3D()});
168 currentSegment = &segmentsInTrack.back().second;
169 currentSegment->setTrajectory3D(track->getStartTrajectory3D());
170 }
171 currentSegment->push_back(recoHit3D);
172 lastISuperLayer = iSuperLayer;
173 lastTracksForHit = tracksForWireHit;
174 }
175
176 // Merge small segments
177 auto absorbsSegment = [](std::pair<InTracks, CDCSegment3D>& segment1,
178 std::pair<InTracks, CDCSegment3D>& segment2) {
179
180 if (segment1.second.getISuperLayer() != segment2.second.getISuperLayer()) return false;
181 if (segment1.second.size() < 3) {
182 segment1.first = segment2.first;
183 }
184 // Absorb segment1 into segment2
185 if (segment1.second.size() < 3 or segment2.second.size() < 3 or
186 segment1.first == segment2.first) {
187 segment1.second.insert(segment1.second.end(),
188 segment2.second.begin(),
189 segment2.second.end());
190 return true;
191 }
192 return false;
193 };
194 auto itSegmentToDrop = std::unique(segmentsInTrack.begin(), segmentsInTrack.end(), absorbsSegment);
195 segmentsInTrack.erase(itSegmentToDrop, segmentsInTrack.end());
196
197 size_t nHits = 0;
198 for (std::pair<InTracks, CDCSegment3D>& segment : segmentsInTrack) {
199 nHits += segment.second.size();
200 }
201 B2ASSERT("Expected the number of hits to be the same", nHits == track->size());
202
203 // Push segments to the common pool
204 const CDCSegment3D* lastSegment = nullptr;
205 for (std::pair<InTracks, CDCSegment3D>& segmentInTrack : segmentsInTrack) {
206 const InTracks& inTracks = segmentInTrack.first;
207 const CDCSegment3D& segment = segmentInTrack.second;
208 segment->setCellWeight(segment.size());
209 segments.push_back(std::move(segment));
210 inTracksBySegment[&segments.back()] = inTracks;
211 if (lastSegment != nullptr) {
212 segmentRelations.push_back({lastSegment, 0, &segments.back()});
213 }
214 lastSegment = &segments.back();
215 }
216 }
217
218 // Sort the relations for the lookup
219 std::sort(segmentRelations.begin(), segmentRelations.end());
220
221 // Identify common segments, also checking whether they have the same orientation
223 std::vector<std::pair<const CDCSegment3D*, const CDCSegment3D*>> segmentContainmentRelation;
224 for (auto itSegment1 = segments.begin(); itSegment1 != segments.end(); ++itSegment1) {
225 const CDCSegment3D& segment1 = *itSegment1;
226 std::vector<CDCRLWireHit> rlWireHits1 = getRLWireHitSegment(segment1);
227 ISuperLayer iSL1 = segment1.getISuperLayer();
228 InTracks inTracks1 = inTracksBySegment[&segment1];
229 for (auto itSegment2 = itSegment1 + 1; itSegment2 != segments.end(); ++itSegment2) {
230 const CDCSegment3D& segment2 = *itSegment2;
231
232 // Should not happen - here for safety reasons
233 if (&segment1 == &segment2) continue;
234
235 ISuperLayer iSL2 = segment2.getISuperLayer();
236 if (iSL1 != iSL2) continue;
237
238 InTracks inTracks2 = inTracksBySegment[&segment2];
239 if (inTracks1 != inTracks2) continue;
240
241 // Now answering the question if segment 1 is contained in segment 2
242 std::vector<CDCRLWireHit> rlWireHits2 = getRLWireHitSegment(segment2);
243 std::vector<std::pair<std::pair<int, int>, CDCRLWireHit>> commonRLWireHits =
244 getCommonRLWireHits(rlWireHits1, rlWireHits2);
245
246 // Check for containment, requiring that the majority of the wire hits of one is in two
247 // However also require that the two is not too much larger compared to the first one
248 bool contains = commonRLWireHits.size() > 2 and
249 commonRLWireHits.size() > rlWireHits1.size() * 0.8 and
250 commonRLWireHits.size() > rlWireHits2.size() * 0.8;
251 if (not contains) continue;
252
253 // Now finally check whether both segments point in the same direction
254 double n = commonRLWireHits.size();
255 double prod11 = 0;
256 double prod12 = 0;
257 double prod22 = 0;
258 double sum1 = 0;
259 double sum2 = 0;
260 for (const auto& commonRLWireHit : commonRLWireHits) {
261 const std::pair<int, int>& indices = commonRLWireHit.first;
262 prod11 += indices.first * indices.first;
263 prod12 += indices.first * indices.second;
264 prod22 += indices.second * indices.second;
265
266 sum1 += indices.first;
267 sum2 += indices.second;
268 }
269 double var1 = (prod11 - sum1 * sum1 / n);
270 double var2 = (prod22 - sum2 * sum2 / n);
271 double cov12 = (prod12 - sum1 * sum2 / n);
272 double cor = cov12 / std::sqrt(var1 * var2);
273 if (not(cor > 0.75)) continue;
274
275 segmentContainmentRelation.push_back({&segment1, &segment2});
276 }
277 }
278
279 // Create additional edges
280 std::vector<WeightedRelation<const CDCSegment3D>> additionalSegmentRelations;
281 for (std::pair<const CDCSegment3D*, const CDCSegment3D*>& rel : segmentContainmentRelation) {
283 asRange(std::equal_range(segmentRelations.begin(), segmentRelations.end(), rel.first))) {
284 additionalSegmentRelations.push_back({rel.second, 0, rel1.getTo()});
285 }
286
287 for (WeightedRelation<const CDCSegment3D> rel2 : asRange(
288 std::equal_range(segmentRelations.begin(), segmentRelations.end(), rel.second))) {
289 additionalSegmentRelations.push_back({rel.first, 0, rel2.getFrom()});
290 }
291 }
292 segmentRelations.insert(segmentRelations.end(),
293 additionalSegmentRelations.begin(),
294 additionalSegmentRelations.end());
295 std::sort(segmentRelations.begin(), segmentRelations.end());
296 }
297
298 // Extract paths
299 // Obtain the segments as pointers
300 std::vector<const CDCSegment3D*> segmentPtrs = as_pointers<const CDCSegment3D>(segments);
301
302 // Memory for the track paths generated from the graph.
303 std::vector<Path<const CDCSegment3D>> segmentPaths;
304 m_cellularPathFinder.apply(segmentPtrs, segmentRelations, segmentPaths);
305
306 // Put the linked segments together
307 outputTracks.clear();
308 for (const std::vector<const CDCSegment3D*>& segmentPath : segmentPaths) {
309 // Reject single left over segments
310 if (segmentPath.size() < 2) continue;
311 outputTracks.push_back(condense(segmentPath));
312 }
313
314 // Simple approach
315 // Incorporate the second input tracks in to the first input tracks by looking for large overlaps
316 // Very simple approach use the first tracks and add the ones from the second tracks with no overlap to the first
317 // outputTracks.insert(outputTracks.end(), inputTracks.begin(), inputTracks.end());
318 // for (const CDCTrack& secondTrack : secondInputTracks) {
319 // std::map<const CDCTrack*, int> overlappingTracks;
320 // for (const CDCRecoHit3D& recoHit3D : secondTrack) {
321 // const CDCWireHit& wireHit = recoHit3D.getWireHit();
322 // inTracksByWireHit.equal_range(&recoHit3D.getWireHit());
323 // auto tracksForWireHit = asRange(inTracksByWireHit.equal_range(&wireHit));
324 // for (const std::pair<const CDCWireHit*, InTracks>& trackForWireHit : inTracksByWireHit) {
325 // const CDCTrack* track = trackForWireHit.second[0];
326 // if (not track) continue;
327 // overlappingTracks[track]++;
328 // }
329 // }
330 // if (overlappingTracks.size() == 0) {
331 // outputTracks.push_back(secondTrack);
332 // }
333 // }
334}
The Module parameter list class.
Implements a path consisting of Module and/or Path objects.
Definition Path.h:38
void apply(const std::vector< TrackingUtilities::CDCTrack > &inputTracks, const std::vector< TrackingUtilities::CDCTrack > &secondInputTracks, std::vector< TrackingUtilities::CDCTrack > &tracks) final
Main algorithm.
std::string getDescription() final
Short description of the findlet.
void exposeParameters(ModuleParamList *moduleParamList, const std::string &prefix) final
Expose the parameters to a module.
bool m_param_identifyCommonSegments
Parameter : Activate the identification of common segments.
TrackingUtilities::MultipassCellularPathFinder< const TrackingUtilities::CDCSegment3D > m_cellularPathFinder
Instance of the cellular automaton path finder.
void unsetMaskedFlag()
Resets the masked flag to false.
void unsetTemporaryFlags()
Resets the assigned, start and cycle marker flag.
Class representing an oriented hit wire including a hypotheses whether the causing track passes left ...
Class representing a three dimensional reconstructed hit.
A segment consisting of three dimensional reconstructed hits.
CDC::ISuperLayer getISuperLayer() const
Returns the common super layer id of all stored tracking hits.
Definition CDCSegment.h:57
Class representing a sequence of three dimensional reconstructed hits.
Definition CDCTrack.h:39
Class representing a hit wire in the central drift chamber.
Definition CDCWireHit.h:58
Type for two related objects with a weight.
void addParameter(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module list.
signed short ISuperLayer
The type of the layer and superlayer ids.
Definition ISuperLayer.h:24
Abstract base class for different kinds of events.
Generic identity functor.
Definition Functional.h:25