Belle II Software development
CDCTriggerRecoMatcherModule.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 "trg/cdc/modules/mcmatcher/CDCTriggerRecoMatcherModule.h"
9
10#include <cdc/dataobjects/CDCHit.h>
11
12#include <Eigen/Dense>
13
14namespace {
15 //small anonymous helper construct making converting a pair of iterators usable
16 //with range based for
17 template<class Iter>
18 struct iter_pair_range : std::pair<Iter, Iter> {
19 explicit iter_pair_range(std::pair<Iter, Iter> const& x) : std::pair<Iter, Iter>(x) {}
20 Iter begin() const {return this->first;}
21 Iter end() const {return this->second;}
22 };
23
24 template<class Iter>
25 inline iter_pair_range<Iter> as_range(std::pair<Iter, Iter> const& x)
26 {
27 return iter_pair_range<Iter>(x);
28 }
29
30 typedef int HitId;
31 typedef int TrackId;
32 typedef float Purity;
33 typedef float Efficiency;
34}
35
36
37
38using namespace Belle2;
39using namespace std;
40
41//this line registers the module with the framework and actually makes it available
42//in steering files or the the module list (basf2 -m).
43REG_MODULE(CDCTriggerRecoMatcher);
44
46{
47 setDescription("A module to match CDCTriggerTracks to RecoTracks.\n"
48 "First makes relations from RecoTracks to CDCTriggerSegmentHits, "
49 "then makes relations from RecoTracks to CDCTriggerTracks "
50 "and vice-versa.");
52
53 addParam("RecoTrackCollectionName", m_RecoTrackCollectionName,
54 "Name of the RecoTrack StoreArray to be matched.",
55 string("RecoTracks"));
56 addParam("TrgTrackCollectionName", m_TrgTrackCollectionName,
57 "Name of the CDCTriggerTrack StoreArray to be matched.",
58 string("TRGCDC2DFinderTracks"));
59 addParam("hitCollectionName", m_hitCollectionName,
60 "Name of the StoreArray of CDCTriggerSegmentHits used for the matching.",
61 string(""));
62 addParam("axialOnly", m_axialOnly,
63 "Switch to ignore stereo hits (= 2D matching).",
64 false);
65 addParam("minPurity", m_minPurity,
66 "Minimum purity for reconstructed tracks.",
67 0.1);
68 addParam("minEfficiency", m_minEfficiency,
69 "Minimum efficiency for MC tracks.",
70 0.1);
71 addParam("relateClonesAndMerged", m_relateClonesAndMerged,
72 "If true, create relations for clones and merged tracks "
73 "(will get negative weight).",
74 true);
75 addParam("relateHitsByID", m_relateHitsByID,
76 "If true, compare ID of CDCTriggerSegmentHits and CDCHits "
77 "to create relations, otherwise use only existing relations.",
78 true);
79}
80
81
82void
84{
88
93}
94
95
96void
98{
99 // create relations from RecoTracks to SegmentHits via CDCHits
100 for (int ireco = 0; ireco < m_recoTracks.getEntries(); ++ireco) {
101 RecoTrack* recoTrack = m_recoTracks[ireco];
102 // if relations exist already, skip this step
103 // (matching may be done several times with different trigger tracks)
104 if (recoTrack->getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName).size() > 0)
105 continue;
106 vector<CDCHit*> cdcHits = recoTrack->getCDCHitList();
107 for (unsigned iHit = 0; iHit < cdcHits.size(); ++iHit) {
108 if (m_relateHitsByID) {
109 // loop over TS hits and compare ID
110 for (CDCTriggerSegmentHit& tsHit : m_segmentHits) {
111 if (tsHit.getID() == cdcHits[iHit]->getID()) {
112 recoTrack->addRelationTo(&tsHit);
113 }
114 }
115 } else {
116 // look for relations between CDC hits and TS hits
118 cdcHits[iHit]->getRelationsFrom<CDCTriggerSegmentHit>(m_hitCollectionName);
119 for (unsigned iTS = 0; iTS < relHits.size(); ++iTS) {
120 // create relations only for priority hits (relation weight 2)
121 if (relHits.weight(iTS) > 1)
122 recoTrack->addRelationTo(relHits[iTS]);
123 }
124 }
125 }
126 }
127
128 // derive relations between trigger tracks and reco tracks,
129 // following the basic ideas of the MCMatcherTracksModule (tracking)
130
131 B2DEBUG(100, "########## start matching ############");
132
133 int nRecoTracks = m_recoTracks.getEntries();
134 int nTrgTracks = m_trgTracks.getEntries();
135
136 B2DEBUG(100, "Number of trigger tracks is " << nTrgTracks);
137 B2DEBUG(100, "Number of reco tracks is " << nRecoTracks);
138
139 if (not nRecoTracks or not nTrgTracks) {
140 // Either no trigger tracks
141 // or no reco tracks are present in this event
142 // Cannot perform matching.
143 return;
144 }
145
146 //### Build a hit_id to track map for easier lookup later ###
147 std::multimap<HitId, TrackId> recoTrackId_by_hitId;
148 {
149 std::multimap<HitId, TrackId>::iterator itRecoInsertHit = recoTrackId_by_hitId.end();
150 TrackId recoTrackId = -1;
151 for (const RecoTrack& recoTrack : m_recoTracks) {
152 ++recoTrackId;
154 recoTrack.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
155 for (unsigned iHit = 0; iHit < relHits.size(); ++iHit) {
156 const HitId hitId = relHits[iHit]->getArrayIndex();
157 itRecoInsertHit = recoTrackId_by_hitId.insert(itRecoInsertHit,
158 make_pair(hitId, recoTrackId));
159 B2DEBUG(250, "hitId " << hitId << " in SL " << relHits[iHit]->getISuperLayer()
160 << ", recoTrackId " << recoTrackId);
161 }
162 }
163 }
164
165 //### Build a hit_id to track map for easier lookup later ###
166 std::multimap<HitId, TrackId> trgTrackId_by_hitId;
167 {
168 std::multimap<HitId, TrackId>::iterator itTrgInsertHit = trgTrackId_by_hitId.end();
169 TrackId trgTrackId = -1;
170 for (const CDCTriggerTrack& trgTrack : m_trgTracks) {
171 ++trgTrackId;
173 trgTrack.getRelationsTo<CDCTriggerSegmentHit>(m_hitCollectionName);
174 for (unsigned int iHit = 0; iHit < relHits.size(); ++iHit) {
175 const HitId hitId = relHits[iHit]->getArrayIndex();
176 itTrgInsertHit = trgTrackId_by_hitId.insert(itTrgInsertHit,
177 make_pair(hitId, trgTrackId));
178 B2DEBUG(250, "hitId " << hitId << " in SL " << relHits[iHit]->getISuperLayer()
179 << ", trgTrackId " << trgTrackId);
180 }
181 }
182 }
183
184 //### Build the confusion matrix ###
185
186 // Reserve enough space for the confusion matrix
187 // In contrast to MC matching, background hits are not counted separately
188 Eigen::MatrixXi confusionMatrix = Eigen::MatrixXi::Zero(nTrgTracks, nRecoTracks);
189
190 // Count total number of hits for each track separately
191 // to avoid double counting (in case tracks share hits)
192 Eigen::RowVectorXi totalHits_by_recoTrackId = Eigen::RowVectorXi::Zero(nRecoTracks);
193 Eigen::VectorXi totalHits_by_trgTrackId = Eigen::VectorXi::Zero(nTrgTracks);
194
195 // examine every hit to which recoTrack and trgTrack it belongs.
196 for (HitId hitId = 0; hitId < m_segmentHits.getEntries(); ++hitId) {
197 // skip stereo hits
198 if (m_axialOnly && m_segmentHits[hitId]->getISuperLayer() % 2) continue;
199
200 // Seek all recoTracks and trgTracks
201 auto range_trgTrackIds = trgTrackId_by_hitId.equal_range(hitId);
202 auto range_recoTrackIds = recoTrackId_by_hitId.equal_range(hitId);
203
204 // Assign the hits to the total vector
205 for (const pair<HitId, TrackId> hitId_and_trgTrackId :
206 as_range(range_trgTrackIds)) {
207 TrackId trgTrackId = hitId_and_trgTrackId.second;
208 totalHits_by_trgTrackId(trgTrackId) += 1;
209 B2DEBUG(200, " trgTrackId for total count: " << trgTrackId);
210 }
211 for (const pair<HitId, TrackId> hitId_and_recoTrackId :
212 as_range(range_recoTrackIds)) {
213 TrackId recoTrackId = hitId_and_recoTrackId.second;
214 totalHits_by_recoTrackId(recoTrackId) += 1;
215 B2DEBUG(200, " recoTrackId for total count: " << recoTrackId);
216 }
217
218 // Count matrix entries for all combinations
219 for (const pair<HitId, TrackId> hitId_and_recoTrackId :
220 as_range(range_recoTrackIds)) {
221 for (const pair<HitId, TrackId> hitId_and_trgTrackId :
222 as_range(range_trgTrackIds)) {
223 TrackId recoTrackId = hitId_and_recoTrackId.second;
224 TrackId trgTrackId = hitId_and_trgTrackId.second;
225 confusionMatrix(trgTrackId, recoTrackId) += 1;
226 B2DEBUG(200, " trgTrackId : " << trgTrackId << "; recoTrackId : " << recoTrackId);
227 }
228 }
229 } //end for hitId
230
231 B2DEBUG(200, "Confusion matrix of the event : " << endl << confusionMatrix);
232
233 B2DEBUG(200, "totalHits_by_trgTrackId : " << endl << totalHits_by_trgTrackId);
234 B2DEBUG(200, "totalHits_by_recoTrackId : " << endl << totalHits_by_recoTrackId);
235
236 // ### Building the trg track to highest purity reco track relation ###
237 vector<pair<TrackId, Purity>> purestRecoTrackId_by_trgTrackId(nTrgTracks);
238
239 for (TrackId trgTrackId = 0; trgTrackId < nTrgTracks; ++trgTrackId) {
240 Eigen::RowVectorXi trgTrackRow = confusionMatrix.row(trgTrackId);
241 Eigen::RowVectorXi::Index purestRecoTrackId;
242
243 //Also sets the index of the highest entry in the row vector
244 int highestHits = trgTrackRow.maxCoeff(&purestRecoTrackId);
245 int totalHits = totalHits_by_trgTrackId(trgTrackId);
246
247 Purity highestPurity = Purity(highestHits) / totalHits;
248
249 purestRecoTrackId_by_trgTrackId[trgTrackId] =
250 pair<TrackId, Purity>(purestRecoTrackId, highestPurity);
251 }
252
253 // Log the trg track to highest purity reco track
254 // relation to debug output
255 {
256 TrackId trgTrackId = -1;
257 B2DEBUG(200, "TrgTrack to highest purity RecoTrack relation");
258 for (const pair< TrackId, Purity>& purestRecoTrackId :
259 purestRecoTrackId_by_trgTrackId) {
260 ++trgTrackId;
261 const Purity& purity = purestRecoTrackId.second;
262 const TrackId& recoTrackId = purestRecoTrackId.first;
263 B2DEBUG(200, "trgTrackId : " << trgTrackId << " -> recoTrackId : " << recoTrackId
264 << " with purity " << purity);
265 }
266 }
267
268 // ### Building the reco track to highest efficiency trg track relation ###
269 vector<pair<TrackId, Efficiency>> mostEfficientTrgTrackId_by_recoTrackId(nRecoTracks);
270
271 for (TrackId recoTrackId = 0; recoTrackId < nRecoTracks; ++recoTrackId) {
272 Eigen::VectorXi recoTrackCol = confusionMatrix.col(recoTrackId);
273 Eigen::VectorXi::Index highestHitsTrgTrackId;
274
275 //Also sets the index of the highest entry in the column vector
276 int highestHits = recoTrackCol.maxCoeff(&highestHitsTrgTrackId);
277 int totalHits = totalHits_by_recoTrackId(recoTrackId);
278
279 Efficiency highestEfficiency = Efficiency(highestHits) / totalHits;
280
281 mostEfficientTrgTrackId_by_recoTrackId[recoTrackId] =
282 pair<TrackId, Efficiency> (highestHitsTrgTrackId, highestEfficiency);
283 }
284
285 // Log the reco track to highest efficiency trg track
286 // relation to debug output
287 {
288 TrackId recoTrackId = -1;
289 B2DEBUG(200, "RecoTrack to highest efficiency TrgTrack relation");
290 for (const pair<TrackId, Efficiency>& mostEfficientTrgTrackId :
291 mostEfficientTrgTrackId_by_recoTrackId) {
292 ++recoTrackId;
293 const Efficiency& efficiency = mostEfficientTrgTrackId.second;
294 const TrackId& trgTrackId = mostEfficientTrgTrackId.first;
295 B2DEBUG(200, "recoTrackId : " << recoTrackId << " -> trgTrackId : " << trgTrackId
296 << " with efficiency " << efficiency);
297 }
298 }
299
300 // ### Classify the trg tracks ###
301 for (TrackId trgTrackId = 0; trgTrackId < nTrgTracks; ++trgTrackId) {
302 CDCTriggerTrack* trgTrack = m_trgTracks[trgTrackId];
303
304 const pair<TrackId, Purity>& purestRecoTrackId = purestRecoTrackId_by_trgTrackId[trgTrackId];
305 const TrackId& recoTrackId = purestRecoTrackId.first;
306 const Purity& purity = purestRecoTrackId.second;
307
308 if (!(purity >= m_minPurity)) {
309 // GHOST
310 B2DEBUG(100, "Classified TrgTrack " << trgTrackId << " as ghost because of too low purity.");
311 } else {
312 // check whether the highest purity reco track has in turn
313 // the highest efficiency trg track equal to this track.
314 RecoTrack* recoTrack = m_recoTracks[recoTrackId];
315
316 const pair<TrackId, Efficiency>& mostEfficientTrgTrackId =
317 mostEfficientTrgTrackId_by_recoTrackId[recoTrackId];
318
319 const TrackId& trgTrackIdCompare = mostEfficientTrgTrackId.first;
320
321 if (trgTrackId != trgTrackIdCompare) {
322 // CLONE
324 // Add the reco track matching relation with negative weight
325 trgTrack->addRelationTo(recoTrack, -purity);
326 }
327 B2DEBUG(100, "Classified TrgTrack " << trgTrackId << " as clone -> recoTrackId "
328 << recoTrackId << " : " << -purity);
329 } else {
330 // MATCHED
331 //Add the reco track matching relation
332 trgTrack->addRelationTo(recoTrack, purity);
333 B2DEBUG(100, "Classified TrgTrack " << trgTrackId << " as match -> recoTrackId "
334 << recoTrackId << " : " << purity);
335 }
336 }
337 }
338
339 // ### Classify the reco tracks ###
340 for (TrackId recoTrackId = 0; recoTrackId < nRecoTracks; ++recoTrackId) {
341 RecoTrack* recoTrack = m_recoTracks[recoTrackId];
342
343 const pair<TrackId, Efficiency>& mostEfficiencyTrgTrackId =
344 mostEfficientTrgTrackId_by_recoTrackId[recoTrackId];
345 const TrackId& trgTrackId = mostEfficiencyTrgTrackId.first;
346 const Efficiency& efficiency = mostEfficiencyTrgTrackId.second;
347
348 if (!(efficiency >= m_minEfficiency)) {
349 // MISSING
350 B2DEBUG(100, "Classified RecoTrack " << recoTrackId << " as missing.");
351 } else {
352 // check whether the highest efficiency trg track has in turn
353 // the highest purity reco track equal to this track.
354 CDCTriggerTrack* trgTrack = m_trgTracks[trgTrackId];
355
356 const pair<TrackId, Purity>& purestRecoTrackId =
357 purestRecoTrackId_by_trgTrackId[trgTrackId];
358 const TrackId& recoTrackIdCompare = purestRecoTrackId.first;
359
360 if (recoTrackId != recoTrackIdCompare) {
361 // MERGED
363 // Add the trg matching relation with negative weight
364 recoTrack->addRelationTo(trgTrack, -efficiency);
365 }
366 B2DEBUG(100, "Classifid RecoTrack " << recoTrackId << " as merge -> trgTrackId "
367 << trgTrackId << " : " << -efficiency);
368
369 } else {
370 // MATCHED
371 // Add the trg matching relation
372 recoTrack->addRelationTo(trgTrack, efficiency);
373 B2DEBUG(100, "Classified RecoTrack " << recoTrackId << " as match -> trgTrackId "
374 << trgTrackId << " : " << efficiency);
375 }
376 }
377 }
378
379 B2DEBUG(100, "########## end matching ############");
380}
bool m_relateHitsByID
switch for creating hit relations based on wire ID
virtual void initialize() override
Initialize the module.
std::string m_RecoTrackCollectionName
Name of the RecoTrack StoreArray to be matched.
virtual void event() override
Called once for each event.
StoreArray< CDCTriggerSegmentHit > m_segmentHits
list of hits that are used for the matching
std::string m_TrgTrackCollectionName
Name of the CDCTriggerTrack Store Array to be matched.
StoreArray< CDCTriggerTrack > m_trgTracks
list of CDCTriggerTracks to be matched
CDCTriggerRecoMatcherModule()
Constructor, for setting module description and parameters.
StoreArray< RecoTrack > m_recoTracks
list of RecoTracks to be matched
std::string m_hitCollectionName
Name of the StoreArray containing the hits that are used for the matching.
bool m_relateClonesAndMerged
switch for creating relations for clones and merged tracks
Combination of several CDCHits to a track segment hit for the trigger.
Track created by the CDC trigger.
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
This is the Reconstruction Event-Data Model Track.
Definition: RecoTrack.h:79
std::vector< Belle2::RecoTrack::UsedCDCHit * > getCDCHitList() const
Return an unsorted list of cdc hits.
Definition: RecoTrack.h:455
Class for type safe access to objects that are referred to in relations.
size_t size() const
Get number of relations.
float weight(int index) const
Get weight with index.
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
RelationVector< TO > getRelationsTo(const std::string &name="", const std::string &namedRelation="") const
Get the relations that point from this object to another store array.
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
bool requireRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, const std::string &namedRelation="") const
Produce error if no relation from this array to 'toArray' has been registered.
Definition: StoreArray.h:155
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.
STL namespace.