9 #include <ecl/modules/eclTrackClusterMatching/ECLTrackClusterMatchingModule.h>
11 #include <ecl/dbobjects/ECLTrackClusterMatchingParameterizations.h>
12 #include <ecl/dbobjects/ECLTrackClusterMatchingThresholds.h>
13 #include <ecl/utility/utilityFunctions.h>
15 #include <framework/gearbox/Const.h>
16 #include <framework/logging/Logger.h>
17 #include <mdst/dataobjects/HitPatternCDC.h>
28 setDescription(
"Creates and saves a Relation between Tracks and ECLCluster in the DataStore. It uses the existing Relation between Tracks and ExtHit as well as the Relation between ECLCluster and ExtHit.");
29 setPropertyFlags(c_ParallelProcessingCertified);
30 addParam(
"useAngularDistanceMatching", m_angularDistanceMatching,
31 "if true use track cluster matching based on angular distance, if false use matching based on entered crystals",
bool(
true));
32 addParam(
"useOptimizedMatchingConsistency", m_useOptimizedMatchingConsistency,
33 "set false if you want to set the matching criterion on your own",
bool(
true));
34 addParam(
"matchingConsistency", m_matchingConsistency,
35 "the 2D consistency of Delta theta and Delta phi has to exceed this value for a track to be matched to an ECL cluster", 1e-6);
36 addParam(
"matchingPTThreshold", m_matchingPTThreshold,
37 "tracks with pt greater than this value will exclusively be matched based on angular distance", 0.3);
38 addParam(
"brlEdgeTheta", m_brlEdgeTheta,
39 "distance of polar angle from gaps where crystal-entering based matching is applied (in rad)", 0.1);
40 addParam(
"minimalCDCHits", m_minimalCDCHits,
41 "bad VXD-standalone tracks cause (too) low photon efficiency in end caps, temporarily fixed by requiring minimal number of CDC hits",
43 addParam(
"skipZeroChargeTracks", m_skipZeroChargeTracks,
44 "switch to exclude tracks with zero charge from track-cluster matching",
bool(
false));
47 ECLTrackClusterMatchingModule::~ECLTrackClusterMatchingModule()
51 void ECLTrackClusterMatchingModule::initialize()
54 m_tracks.isRequired();
55 m_eclClusters.isRequired();
56 m_eclShowers.isRequired();
57 m_eclCalDigits.isRequired();
58 m_extHits.isRequired();
59 m_trackFitResults.isRequired();
61 if (m_angularDistanceMatching) {
62 m_tracks.registerRelationTo(m_eclShowers);
63 m_tracks.registerRelationTo(m_eclClusters);
64 m_tracks.registerRelationTo(m_eclShowers, DataStore::c_Event, DataStore::c_WriteOut,
"AngularDistance");
65 m_tracks.registerRelationTo(m_eclClusters, DataStore::c_Event, DataStore::c_WriteOut,
"AngularDistance");
68 auto updateParameterizationFunctions = [
this]() {
69 const auto& map = m_matchingParameterizations->getRMSParameterizations();
71 f_phiRMSFWDCROSS = map.at(
"PhiFWDCROSS");
72 f_phiRMSFWDDL = map.at(
"PhiFWDDL");
73 f_phiRMSFWDNEAR = map.at(
"PhiFWDNEAR");
74 f_phiRMSBRLCROSS = map.at(
"PhiBRLCROSS");
75 f_phiRMSBRLDL = map.at(
"PhiBRLDL");
76 f_phiRMSBRLNEAR = map.at(
"PhiBRLNEAR");
77 f_phiRMSBWDCROSS = map.at(
"PhiBWDCROSS");
78 f_phiRMSBWDDL = map.at(
"PhiBWDDL");
79 f_phiRMSBWDNEAR = map.at(
"PhiBWDNEAR");
80 f_thetaRMSFWDCROSS = map.at(
"ThetaFWDCROSS");
81 f_thetaRMSFWDDL = map.at(
"ThetaFWDDL");
82 f_thetaRMSFWDNEAR = map.at(
"ThetaFWDNEAR");
83 f_thetaRMSBRLCROSS = map.at(
"ThetaBRLCROSS");
84 f_thetaRMSBRLDL = map.at(
"ThetaBRLDL");
85 f_thetaRMSBRLNEAR = map.at(
"ThetaBRLNEAR");
86 f_thetaRMSBWDCROSS = map.at(
"ThetaBWDCROSS");
87 f_thetaRMSBWDDL = map.at(
"ThetaBWDDL");
88 f_thetaRMSBWDNEAR = map.at(
"ThetaBWDNEAR");
92 auto updateMatchingThresholds = [
this]() {
93 m_matchingThresholdValuesFWD = m_matchingThresholds->getFWDMatchingThresholdValues();
94 m_matchingThresholdValuesBRL = m_matchingThresholds->getBRLMatchingThresholdValues();
95 m_matchingThresholdValuesBWD = m_matchingThresholds->getBWDMatchingThresholdValues();
99 updateParameterizationFunctions();
100 updateMatchingThresholds();
102 m_matchingParameterizations.addCallback(updateParameterizationFunctions);
103 m_matchingThresholds.addCallback(updateMatchingThresholds);
105 m_tracks.registerRelationTo(m_eclShowers, DataStore::c_Event, DataStore::c_WriteOut,
"EnterCrystal");
106 m_tracks.registerRelationTo(m_eclClusters, DataStore::c_Event, DataStore::c_WriteOut,
"EnterCrystal");
110 void ECLTrackClusterMatchingModule::event()
112 for (
auto& eclCluster : m_eclClusters) {
113 eclCluster.setIsTrack(
false);
115 for (
const Track& track : m_tracks) {
116 const TrackFitResult* fitResult = track.getTrackFitResultWithClosestMass(Const::pion);
119 if (m_skipZeroChargeTracks && fitResult->
getChargeSign() == 0)
continue;
120 double theta = TMath::ACos(fitResult->
getMomentum().CosTheta());
122 if (!m_angularDistanceMatching || pt < m_matchingPTThreshold || trackTowardsGap(theta)) {
125 set<int> uniqueShowerIds;
128 set<int> uniquehypothesisIds;
129 vector<int> hypothesisIds;
130 vector<double> energies;
131 vector<int> arrayIndexes;
137 for (
const auto& extHit : track.getRelationsTo<
ExtHit>()) {
138 if (!isECLEnterHit(extHit))
continue;
139 const int cell = extHit.getCopyID() + 1;
142 const auto idigit = find_if(m_eclCalDigits.begin(), m_eclCalDigits.end(),
143 [&](
const ECLCalDigit & d) { return d.getCellId() == cell; }
146 if (idigit == m_eclCalDigits.end())
continue;
149 for (
auto& shower : idigit->getRelationsFrom<
ECLShower>()) {
150 bool inserted = (uniqueShowerIds.insert(shower.getUniqueId())).second;
153 if (!inserted)
continue;
155 hypothesisIds.push_back(shower.getHypothesisId());
156 energies.push_back(shower.getEnergy());
157 arrayIndexes.push_back(shower.getArrayIndex());
158 uniquehypothesisIds.insert(shower.getHypothesisId());
160 B2DEBUG(29, shower.getArrayIndex() <<
" " << shower.getHypothesisId() <<
" " << shower.getEnergy() <<
" " <<
161 shower.getConnectedRegionId());
167 for (
auto hypothesisId : uniquehypothesisIds) {
168 double highestEnergy = 0.0;
171 for (
unsigned ix = 0; ix < energies.size(); ix++) {
172 if (hypothesisIds[ix] == hypothesisId and energies[ix] > highestEnergy) {
173 highestEnergy = energies[ix];
174 arrayindex = arrayIndexes[ix];
179 if (arrayindex > -1) {
180 auto shower = m_eclShowers[arrayindex];
181 shower->setIsTrack(
true);
182 if (m_angularDistanceMatching) {
183 track.addRelationTo(shower);
184 track.addRelationTo(shower, 1.0,
"AngularDistance");
186 track.addRelationTo(shower, 1.0,
"EnterCrystal");
188 B2DEBUG(29, shower->getArrayIndex() <<
" " << shower->getIsTrack());
192 if (cluster !=
nullptr) {
193 cluster->setIsTrack(
true);
194 if (m_angularDistanceMatching) {
195 track.addRelationTo(cluster);
196 track.addRelationTo(cluster, 1.0,
"AngularDistance");
198 track.addRelationTo(cluster, 1.0,
"EnterCrystal");
204 if (m_angularDistanceMatching) {
206 if (track.getRelationsTo<
ECLCluster>(
"",
"AngularDistance").size() > 0)
continue;
208 if (trackTowardsGap(theta))
continue;
210 ECL::DetectorRegion trackDetectorRegion = ECL::getDetectorRegion(theta);
211 if (pt < m_matchingPTThreshold && trackDetectorRegion != ECL::DetectorRegion::FWD)
continue;
213 map<int, pair<double, int>> hypothesisIdBestQualityCROSSArrayIndexMap;
214 map<int, pair<double, int>> hypothesisIdBestQualityDLArrayIndexMap;
215 map<int, pair<double, int>> hypothesisIdBestQualityNEARArrayIndexMap;
216 set<int> uniqueHypothesisIds;
221 for (
const auto& extHit : track.getRelationsTo<
ExtHit>()) {
222 if (!isECLHit(extHit))
continue;
224 if (!eclCluster)
continue;
226 if (eclShower !=
nullptr) {
229 if (abs(eclDetectorRegion - trackDetectorRegion) == 1)
continue;
231 if (pt < m_matchingPTThreshold && eclDetectorRegion == ECL::DetectorRegion::BRL)
continue;
232 double phiHit = extHit.getPosition().Phi();
233 double phiShower = eclShower->
getPhi();
234 double deltaPhi = phiHit - phiShower;
235 if (deltaPhi > M_PI) {
236 deltaPhi = deltaPhi - 2 * M_PI;
237 }
else if (deltaPhi < -M_PI) {
238 deltaPhi = deltaPhi + 2 * M_PI;
240 double thetaHit = extHit.getPosition().Theta();
241 double thetaShower = eclShower->
getTheta();
242 double deltaTheta = thetaHit - thetaShower;
244 double quality = showerQuality(deltaPhi, deltaTheta, pt, eclDetectorRegion, extHitStatus);
246 bool inserted = (uniqueHypothesisIds.insert(hypothesisId)).second;
248 hypothesisIdBestQualityCROSSArrayIndexMap.insert(make_pair(hypothesisId, make_pair(0, -1)));
249 hypothesisIdBestQualityDLArrayIndexMap.insert(make_pair(hypothesisId, make_pair(0, -1)));
250 hypothesisIdBestQualityNEARArrayIndexMap.insert(make_pair(hypothesisId, make_pair(0, -1)));
252 if (extHitStatus == EXT_ECLCROSS) {
253 if (quality > hypothesisIdBestQualityCROSSArrayIndexMap.at(hypothesisId).first) {
254 hypothesisIdBestQualityCROSSArrayIndexMap[hypothesisId] = make_pair(quality, eclShower->
getArrayIndex());
256 }
else if (extHitStatus == EXT_ECLDL) {
257 if (quality > hypothesisIdBestQualityDLArrayIndexMap.at(hypothesisId).first) {
258 hypothesisIdBestQualityDLArrayIndexMap[hypothesisId] = make_pair(quality, eclShower->
getArrayIndex());
261 if (quality > hypothesisIdBestQualityNEARArrayIndexMap.at(hypothesisId).first) {
262 hypothesisIdBestQualityNEARArrayIndexMap[hypothesisId] = make_pair(quality, eclShower->
getArrayIndex());
268 vector<map<int, pair<double, int>>> hypothesisIdBestQualityArrayIndexMaps;
269 hypothesisIdBestQualityArrayIndexMaps.push_back(hypothesisIdBestQualityCROSSArrayIndexMap);
270 hypothesisIdBestQualityArrayIndexMaps.push_back(hypothesisIdBestQualityDLArrayIndexMap);
271 hypothesisIdBestQualityArrayIndexMaps.push_back(hypothesisIdBestQualityNEARArrayIndexMap);
272 if (m_useOptimizedMatchingConsistency) optimizedPTMatchingConsistency(theta, pt);
273 for (
const auto& uniqueHypothesisId : uniqueHypothesisIds) {
274 for (
const auto& hypothesisIdBestQualityArrayIndexMap : hypothesisIdBestQualityArrayIndexMaps) {
275 if (hypothesisIdBestQualityArrayIndexMap.at(uniqueHypothesisId).first > m_matchingConsistency
276 && hypothesisIdBestQualityArrayIndexMap.at(uniqueHypothesisId).second > -1) {
277 auto shower = m_eclShowers[hypothesisIdBestQualityArrayIndexMap.at(uniqueHypothesisId).second];
278 shower->setIsTrack(
true);
279 track.addRelationTo(shower);
280 track.addRelationTo(shower, 1.0,
"AngularDistance");
282 if (cluster !=
nullptr) {
283 cluster->setIsTrack(
true);
284 track.addRelationTo(cluster);
285 track.addRelationTo(cluster, 1.0,
"AngularDistance");
295 void ECLTrackClusterMatchingModule::terminate()
299 bool ECLTrackClusterMatchingModule::isECLEnterHit(
const ExtHit& extHit)
const
301 if ((extHit.
getDetectorID() != Const::EDetector::ECL))
return false;
302 if ((extHit.
getStatus() != EXT_ENTER))
return false;
303 if (extHit.
getCopyID() == -1)
return false;
307 bool ECLTrackClusterMatchingModule::isECLHit(
const ExtHit& extHit)
const
309 if ((extHit.
getDetectorID() != Const::EDetector::ECL))
return false;
311 if (extHitStatus == EXT_ECLCROSS || extHitStatus == EXT_ECLDL || extHitStatus == EXT_ECLNEAR)
return true;
315 double ECLTrackClusterMatchingModule::showerQuality(
double deltaPhi,
double deltaTheta,
double pt,
316 int eclDetectorRegion,
int hitStatus)
const
318 double phi_consistency = phiConsistency(deltaPhi, pt, eclDetectorRegion, hitStatus);
319 double theta_consistency = thetaConsistency(deltaTheta, pt, eclDetectorRegion, hitStatus);
320 return phi_consistency * theta_consistency * (1 - log(phi_consistency * theta_consistency));
323 double ECLTrackClusterMatchingModule::phiConsistency(
double deltaPhi,
double pt,
int eclDetectorRegion,
int hitStatus)
const
326 if (eclDetectorRegion == ECL::DetectorRegion::FWD || eclDetectorRegion == ECL::DetectorRegion::FWDGap) {
327 if (hitStatus == EXT_ECLCROSS) {
328 phi_RMS = f_phiRMSFWDCROSS.Eval(pt);
329 }
else if (hitStatus == EXT_ECLDL) {
330 phi_RMS = f_phiRMSFWDDL.Eval(pt);
332 phi_RMS = f_phiRMSFWDNEAR.Eval(pt);
334 }
else if (eclDetectorRegion == ECL::DetectorRegion::BRL) {
335 if (hitStatus == EXT_ECLCROSS) {
336 phi_RMS = f_phiRMSBRLCROSS.Eval(pt);
337 }
else if (hitStatus == EXT_ECLDL) {
338 phi_RMS = f_phiRMSBRLDL.Eval(pt);
340 phi_RMS = f_phiRMSBRLNEAR.Eval(pt);
342 }
else if (eclDetectorRegion == ECL::DetectorRegion::BWD || eclDetectorRegion == ECL::DetectorRegion::BWDGap) {
343 if (hitStatus == EXT_ECLCROSS) {
344 phi_RMS = f_phiRMSBWDCROSS.Eval(pt);
345 }
else if (hitStatus == EXT_ECLDL) {
346 phi_RMS = f_phiRMSBWDDL.Eval(pt);
348 phi_RMS = f_phiRMSBWDNEAR.Eval(pt);
353 return erfc(abs(deltaPhi) / phi_RMS);
356 double ECLTrackClusterMatchingModule::thetaConsistency(
double deltaTheta,
double pt,
int eclDetectorRegion,
int hitStatus)
const
359 if (eclDetectorRegion == ECL::DetectorRegion::FWD || eclDetectorRegion == ECL::DetectorRegion::FWDGap) {
360 if (hitStatus == EXT_ECLCROSS) {
361 theta_RMS = f_thetaRMSFWDCROSS.Eval(pt);
362 }
else if (hitStatus == EXT_ECLDL) {
363 theta_RMS = f_thetaRMSFWDDL.Eval(pt);
365 theta_RMS = f_thetaRMSFWDNEAR.Eval(pt);
367 }
else if (eclDetectorRegion == ECL::DetectorRegion::BRL) {
368 if (hitStatus == EXT_ECLCROSS) {
369 theta_RMS = f_thetaRMSBRLCROSS.Eval(pt);
370 }
else if (hitStatus == EXT_ECLDL) {
371 theta_RMS = f_thetaRMSBRLDL.Eval(pt);
373 theta_RMS = f_thetaRMSBRLNEAR.Eval(pt);
375 }
else if (eclDetectorRegion == ECL::DetectorRegion::BWD || eclDetectorRegion == ECL::DetectorRegion::BWDGap) {
376 if (hitStatus == EXT_ECLCROSS) {
377 theta_RMS = f_thetaRMSBWDCROSS.Eval(pt);
378 }
else if (hitStatus == EXT_ECLDL) {
379 theta_RMS = f_thetaRMSBWDDL.Eval(pt);
381 theta_RMS = f_thetaRMSBWDNEAR.Eval(pt);
386 return erfc(abs(deltaTheta) / theta_RMS);
389 bool ECLTrackClusterMatchingModule::trackTowardsGap(
double theta)
const
391 if (ECL::getDetectorRegion(theta) == ECL::DetectorRegion::BRL) {
392 if (ECL::getDetectorRegion(theta - m_brlEdgeTheta) != ECL::DetectorRegion::BRL)
return true;
393 else if (ECL::getDetectorRegion(theta + m_brlEdgeTheta) != ECL::DetectorRegion::BRL)
return true;
398 void ECLTrackClusterMatchingModule::optimizedPTMatchingConsistency(
double theta,
double pt)
400 if (ECL::getDetectorRegion(theta) == ECL::DetectorRegion::FWD || ECL::getDetectorRegion(theta) == ECL::DetectorRegion::FWDGap) {
401 for (
const auto& matchingThresholdPair : m_matchingThresholdValuesFWD) {
402 if (pt < matchingThresholdPair.first) {
403 m_matchingConsistency = matchingThresholdPair.second;
407 }
else if (ECL::getDetectorRegion(theta) == ECL::DetectorRegion::BRL) {
408 for (
const auto& matchingThresholdPair : m_matchingThresholdValuesBRL) {
409 if (theta < matchingThresholdPair.first && pt < matchingThresholdPair.second.first) {
410 m_matchingConsistency = matchingThresholdPair.second.second;
414 }
else if (ECL::getDetectorRegion(theta) == ECL::DetectorRegion::BWD
415 || ECL::getDetectorRegion(theta) == ECL::DetectorRegion::BWDGap) {
416 for (
const auto& matchingThresholdPair : m_matchingThresholdValuesBWD) {
417 if (pt < matchingThresholdPair.first) {
418 m_matchingConsistency = matchingThresholdPair.second;
Class to store calibrated ECLDigits: ECLCalDigits.
Class to store ECL Showers.
int getHypothesisId() const
Get Hypothesis Id.
double getPhi() const
Get Phi.
int getDetectorRegion() const
Return detector region: 0: below acceptance, 1: FWD, 2: BRL, 3: BWD, 11: FWDGAP, 13: BWDGAP.
double getTheta() const
Get Theta.
The module creates and saves a Relation between Tracks and ECLCluster in the DataStore.
Class to hold the parameterizations of the RMS for the difference in polar and azimuthal angle betwee...
Class to hold the matching thresholds for the track-ECLCluster matching.
Store one Ext hit as a ROOT object.
ExtHitStatus getStatus() const
Get state of extrapolation at this hit.
int getCopyID() const
Get detector-element ID of sensitive element within detector.
Const::EDetector getDetectorID() const
Get detector ID of this extrapolation hit.
unsigned short getNHits() const
Get the total Number of CDC hits in the fit.
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
FROM * getRelatedFrom(const std::string &name="", const std::string &namedRelation="") const
Get the object from which this object has a relation.
TO * getRelatedTo(const std::string &name="", const std::string &namedRelation="") const
Get the object to which this object has a relation.
Values of the result of a track fit with a given particle hypothesis.
short getChargeSign() const
Return track charge (1 or -1).
TVector3 getMomentum() const
Getter for vector of momentum at closest approach of track in r/phi projection.
double getTransverseMomentum() const
Getter for the absolute value of the transverse momentum at the perigee.
HitPatternCDC getHitPatternCDC() const
Getter for the hit pattern in the CDC;.
Class that bundles various TrackFitResults.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
ExtHitStatus
Define state of extrapolation for each recorded hit.
Abstract base class for different kinds of events.