Belle II Software development
VXDDedxPIDModule.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
9#include <reconstruction/modules/VXDDedxPID/VXDDedxPIDModule.h>
10#include <reconstruction/modules/VXDDedxPID/HelixHelper.h>
11
12#include <framework/gearbox/Const.h>
13
14#include <vxd/geometry/GeoCache.h>
15
16#include <genfit/MaterialEffects.h>
17
18#include <Math/VectorUtil.h>
19#include <TH2F.h>
20
21#include <memory>
22#include <cassert>
23#include <cmath>
24#include <algorithm>
25
26using namespace Belle2;
27using namespace Dedx;
28
29REG_MODULE(VXDDedxPID);
30
32{
34
35 //Set module properties
36 setDescription("Extract dE/dx and corresponding log-likelihood from fitted tracks and hits in the SVD and PXD.");
37
38 //Parameter definitions
39 addParam("useIndividualHits", m_useIndividualHits,
40 "Use individual hits (true) or truncated mean (false) to determine likelihoods", false);
41 addParam("onlyPrimaryParticles", m_onlyPrimaryParticles,
42 "For MC only: if true, only save data for primary particles (as determined by MC truth)", false);
43 addParam("usePXD", m_usePXD, "Use PXDClusters for dE/dx calculation", false);
44 addParam("useSVD", m_useSVD, "Use SVDClusters for dE/dx calculation", true);
45 addParam("trackDistanceThreshold", m_trackDistanceThreshhold,
46 "Use a faster helix parametrisation, with corrections as soon as the approximation is more than ... cm off.", double(4.0));
47
48 m_eventID = -1;
49 m_trackID = 0;
50}
51
53
55{
56 if (m_usePXD) {
57 if (not m_PXDDedxPDFs) B2FATAL("No PXD dE/dx PDF's available");
58 bool ok = m_PXDDedxPDFs->checkPDFs(not m_useIndividualHits);
59 if (not ok) B2FATAL("Binning or ranges of PXD dE/dx PDF's differ");
60 }
61 if (m_useSVD) {
62 if (not m_SVDDedxPDFs) B2FATAL("No SVD Dedx PDF's available");
63 bool ok = m_SVDDedxPDFs->checkPDFs(not m_useIndividualHits);
64 if (not ok) B2FATAL("Binning or ranges of SVD dE/dx PDF's differ");
65 }
66}
67
69{
70
71 // required inputs
72 m_tracks.isRequired();
73 m_recoTracks.isRequired();
74
75 //optional inputs
77 m_tracks.optionalRelationTo(m_mcparticles);
78
79 if (m_useSVD)
81 else
83 if (m_usePXD)
85 else
87
88 // register dE/dx data points
89 m_dedxTracks.registerInDataStore();
90 m_tracks.registerRelationTo(m_dedxTracks);
91
92 // register likelihoods
93 m_dedxLikelihoods.registerInDataStore();
94 m_tracks.registerRelationTo(m_dedxLikelihoods);
95
96 m_SVDDedxPDFs.addCallback([this]() {checkPDFs();});
97 m_PXDDedxPDFs.addCallback([this]() {checkPDFs();});
98 checkPDFs();
99
100
101 // create instances here to not confuse profiling
103
104 if (!genfit::MaterialEffects::getInstance()->isInitialized()) {
105 B2FATAL("Need to have SetupGenfitExtrapolationModule in path before this one.");
106 }
107}
108
110{
111 m_dedxTracks.clear();
112 m_dedxLikelihoods.clear();
113
114 // go through Tracks
115 // get fitresult and gftrack and do extrapolations, save corresponding dE/dx and likelihood values
116 // get genfit::TrackCand through genfit::Track::getCand()
117 // get hit indices through genfit::TrackCand::getHit(...)
118 // create one VXDDedxTrack per fitresult/gftrack
119 //create one DedkLikelihood per Track (plus rel)
120 m_eventID++;
121
122 // inputs
123 const int numMCParticles = m_mcparticles.getEntries();
124
125 // **************************************************
126 //
127 // LOOP OVER TRACKS
128 //
129 // **************************************************
130
131 for (const auto& track : m_tracks) {
132 m_trackID++;
133
134 std::shared_ptr<VXDDedxTrack> dedxTrack = std::make_shared<VXDDedxTrack>();
135 dedxTrack->m_eventID = m_eventID;
136 dedxTrack->m_trackID = m_trackID;
137
138 // load the pion fit hypothesis or the hypothesis which is the closest in mass to a pion
139 // the tracking will not always successfully fit with a pion hypothesis
140 const TrackFitResult* fitResult = track.getTrackFitResultWithClosestMass(Const::pion);
141 if (!fitResult) {
142 B2WARNING("No related fit for track ...");
143 continue;
144 }
145
146 if (numMCParticles != 0) {
147 // find MCParticle corresponding to this track
148 const MCParticle* mcpart = track.getRelatedTo<MCParticle>();
149
150 if (mcpart) {
152 continue; //not a primary particle, ignore
153 }
154
155 //add some MC truths to VXDDedxTrack object
156 dedxTrack->m_pdg = mcpart->getPDG();
157 const MCParticle* mother = mcpart->getMother();
158 dedxTrack->m_motherPDG = mother ? mother->getPDG() : 0;
159
160 const ROOT::Math::XYZVector trueMomentum = mcpart->getMomentum();
161 dedxTrack->m_pTrue = trueMomentum.R();
162 }
163 }
164
165 // get momentum (at origin) from fit result
166 const ROOT::Math::XYZVector& trackPos = fitResult->getPosition();
167 const ROOT::Math::XYZVector& trackMom = fitResult->getMomentum();
168 dedxTrack->m_p = trackMom.R();
169 dedxTrack->m_cosTheta = cos(trackMom.Theta());
170 dedxTrack->m_charge = fitResult->getChargeSign();
171
172 // dE/dx values will be calculated using associated RecoTrack
173 const RecoTrack* recoTrack = track.getRelatedTo<RecoTrack>();
174 if (!recoTrack) {
175 B2WARNING("No related track for this fit...");
176 continue;
177 }
178
179 // Check to see if the track is pruned. We use the cardinal representation for this, as we do not expect that one
180 // representation is pruned whereas the other is not.
181 if (recoTrack->hasTrackFitStatus()) {
182 if (recoTrack->getTrackFitStatus()->isTrackPruned()) {
183 B2ERROR("GFTrack is pruned, please run VXDDedxPID only on unpruned tracks! Skipping this track.");
184 continue;
185 }
186 } else // The RecoTrack might not have a fit status for reasons: let's skip it
187 continue;
188
189 //used for PXD/SVD hits
190 const HelixHelper helixAtOrigin(trackPos, trackMom, dedxTrack->m_charge);
191
192 if (m_usePXD) {
193 const std::vector<PXDCluster*>& pxdClusters = recoTrack->getPXDHitList();
194 saveSiHits(dedxTrack.get(), helixAtOrigin, pxdClusters);
195 }
196
197 if (m_useSVD) {
198 const std::vector<SVDCluster*>& svdClusters = recoTrack->getSVDHitList();
199 saveSiHits(dedxTrack.get(), helixAtOrigin, svdClusters);
200 }
201
202 if (dedxTrack->dedx.empty()) {
203 B2DEBUG(50, "Found track with no hits, ignoring.");
204 continue;
205 }
206
207 // add a few last things to the VXDDedxTrack
208 const int numDedx = dedxTrack->dedx.size();
209 dedxTrack->m_nHits = numDedx;
210 // no need to define lowedgetruncated and highedgetruncated as we always remove the highest 2 dE/dx values from 8 dE/dx value
211 dedxTrack->m_nHitsUsed = numDedx - 2;
212
213 // calculate log likelihoods
214 dedxTrack->clearLogLikelihoods();
215 bool truncated = not m_useIndividualHits;
216 if (m_usePXD) dedxTrack->addLogLikelihoods(m_PXDDedxPDFs->getPDFs(truncated), Dedx::c_PXD, truncated);
217 if (m_useSVD) dedxTrack->addLogLikelihoods(m_SVDDedxPDFs->getPDFs(truncated), Dedx::c_SVD, truncated);
218
219 // save log likelihoods
220 if (dedxTrack->areLogLikelihoodsAvailable()) {
221 VXDDedxLikelihood* likelihoodObj = m_dedxLikelihoods.appendNew(dedxTrack->m_vxdLogl);
222 track.addRelationTo(likelihoodObj);
223 }
224
225 // save the VXDDedxTrack
226 VXDDedxTrack* newVXDDedxTrack = m_dedxTracks.appendNew(*dedxTrack);
227 track.addRelationTo(newVXDDedxTrack);
228
229 } // end of loop over tracks
230}
231
233{
234
235 B2DEBUG(50, "VXDDedxPIDModule exiting after processing " << m_trackID <<
236 " tracks in " << m_eventID + 1 << " events.");
237}
238
239
240// calculateMeans need some change as we always remove highest 2 dE/dx values
241void VXDDedxPIDModule::calculateMeans(double& mean, double& truncatedMean, double& truncatedMeanErr,
242 const std::vector<double>& dedx) const
243{
244 // Calculate the truncated average by skipping only highest two value
245 std::vector<double> sortedDedx = dedx;
246 std::sort(sortedDedx.begin(), sortedDedx.end());
247
248 double truncatedMeanTmp = 0.0;
249 double meanTmp = 0.0;
250 double sumOfSquares = 0.0;
251 const int numDedx = sortedDedx.size();
252
253
254 for (int i = 0; i < numDedx; i++) {
255 meanTmp += sortedDedx[i];
256 }
257 if (numDedx != 0) {
258 meanTmp /= numDedx;
259 }
260
261 for (int i = 0; i < numDedx - 2; i++) {
262 truncatedMeanTmp += sortedDedx[i];
263 sumOfSquares += sortedDedx[i] * sortedDedx[i];
264 }
265 if (numDedx - 2 != 0) {
266 truncatedMeanTmp /= numDedx - 2;
267 }
268
269 mean = meanTmp;
270 truncatedMean = truncatedMeanTmp;
271
272 if (numDedx - 2 > 1) {
273 truncatedMeanErr = sqrt(sumOfSquares / double(numDedx - 2) - truncatedMeanTmp * truncatedMeanTmp) / double((numDedx - 2) - 1);
274 } else {
275 truncatedMeanErr = 0;
276 }
277}
278
280{
282 const VXD::SensorInfoBase& sensor = geo.getSensorInfo(hit->getSensorID());
283
284 const ROOT::Math::XYZVector localPos(hit->getU(), hit->getV(), 0.0); //z-component is height over the center of the detector plane
285 const ROOT::Math::XYZVector& globalPos = sensor.pointToGlobal(localPos);
286 const ROOT::Math::XYZVector& localMomentum = helix->momentum(helix->pathLengthToPoint(globalPos));
287
288 const ROOT::Math::XYZVector& sensorNormal = sensor.vectorToGlobal(ROOT::Math::XYZVector(0.0, 0.0, 1.0));
289 const double angle = ROOT::Math::VectorUtil::Angle(sensorNormal, localMomentum); //includes theta and phi components
290
291 //I'm assuming there's only one hit per sensor, there are _very_ rare exceptions to that (most likely curlers)
292 return TMath::Min(sensor.getWidth(), sensor.getThickness() / fabs(cos(angle)));
293}
294
295
297{
299 const VXD::SensorInfoBase& sensor = geo.getSensorInfo(hit->getSensorID());
300
301 ROOT::Math::XYZVector a, b;
302 if (hit->isUCluster()) {
303 const double u = hit->getPosition();
304 a = sensor.pointToGlobal(ROOT::Math::XYZVector(sensor.getBackwardWidth() / sensor.getWidth(0) * u, -0.5 * sensor.getLength(), 0.0));
305 b = sensor.pointToGlobal(ROOT::Math::XYZVector(sensor.getForwardWidth() / sensor.getWidth(0) * u, +0.5 * sensor.getLength(), 0.0));
306 } else {
307 const double v = hit->getPosition();
308 a = sensor.pointToGlobal(ROOT::Math::XYZVector(-0.5 * sensor.getWidth(v), v, 0.0));
309 b = sensor.pointToGlobal(ROOT::Math::XYZVector(+0.5 * sensor.getWidth(v), v, 0.0));
310 }
311 const double pathLength = helix->pathLengthToLine(ROOT::Math::XYZVector(a), ROOT::Math::XYZVector(b));
312 const ROOT::Math::XYZVector& localMomentum = helix->momentum(pathLength);
313
314 const ROOT::Math::XYZVector& sensorNormal = sensor.vectorToGlobal(ROOT::Math::XYZVector(0.0, 0.0, 1.0));
315 const double angle = ROOT::Math::VectorUtil::Angle(sensorNormal, localMomentum); //includes theta and phi components
316
317 return TMath::Min(sensor.getWidth(), sensor.getThickness() / fabs(cos(angle)));
318}
319
320
321template <class HitClass> void VXDDedxPIDModule::saveSiHits(VXDDedxTrack* track, const HelixHelper& helix,
322 const std::vector<HitClass*>& hits) const
323{
324 const int numHits = hits.size();
325 if (numHits == 0)
326 return;
327
329
330 //figure out which detector to assign hits to
331 const int currentDetector = geo.getSensorInfo(hits.front()->getSensorID()).getType();
332 assert(currentDetector == VXD::SensorInfoBase::PXD or currentDetector == VXD::SensorInfoBase::SVD);
333 // assert(currentDetector <= 1); //used as array index
334 assert(currentDetector == 0 or currentDetector == 1); //used as array index
335
336 std::vector<double> siliconDedx; //used for averages
337 siliconDedx.reserve(numHits);
338
339 VxdID prevSensor;
340 for (int i = 0; i < numHits; i++) {
341 const HitClass* hit = hits[i];
342 if (!hit) {
343 B2ERROR("Added hit is a null pointer!");
344 continue;
345 }
346 const VxdID& currentSensor = hit->getSensorID();
347 int layer = -currentSensor.getLayerNumber();
348 assert(layer >= -6 && layer < 0);
349
350 //active medium traversed, in cm (can traverse one sensor at most)
351 //assumption: Si detectors are close enough to the origin that helix is still accurate
352 const double totalDistance = getTraversedLength(hit, &helix);
353 const double charge = hit->getCharge();
354 const double dedx = charge / totalDistance;
355 if (dedx <= 0) {
356 B2WARNING("dE/dx is " << dedx << " in layer " << layer);
357 } else if (i == 0 or prevSensor != currentSensor) { //only save once per sensor (u and v hits share charge!)
358 prevSensor = currentSensor;
359 //store data
360 siliconDedx.push_back(dedx);
361 track->m_dedxAvg[currentDetector] += dedx;
362 track->addDedx(layer, totalDistance, dedx);
363 }
364
365 track->addHit(currentSensor, layer, charge, totalDistance, dedx);
366 }
367
368 //save averages
369 calculateMeans(track->m_dedxAvg[currentDetector],
370 track->m_dedxAvgTruncated[currentDetector],
371 track->m_dedxAvgTruncatedErr[currentDetector],
372 siliconDedx);
373}
374
static const ChargedStable pion
charged pion particle
Definition: Const.h:661
Helper class representing a helical track.
Definition: HelixHelper.h:28
double pathLengthToPoint(const ROOT::Math::XYZVector &p) const
returns the path length (along the helix) to the helix point closest to p.
Definition: HelixHelper.h:68
ROOT::Math::XYZVector momentum(double s=0) const
momentum of the particle, at the helix point corresponding to a flown path length s (from poca).
Definition: HelixHelper.h:109
double pathLengthToLine(const ROOT::Math::XYZVector &a, const ROOT::Math::XYZVector &b) const
returns the path length (along the helix) to the helix point closest to the line going through points...
Definition: HelixHelper.h:87
A Class to store the Monte Carlo particle information.
Definition: MCParticle.h:32
@ c_PrimaryParticle
bit 0: Particle is primary particle.
Definition: MCParticle.h:47
bool hasStatus(unsigned short int bitmask) const
Return if specific status bit is set.
Definition: MCParticle.h:118
int m_pdg
PDG-Code of the particle.
Definition: MCParticle.h:533
int getPDG() const
Return PDG code of particle.
Definition: MCParticle.h:101
ROOT::Math::XYZVector getMomentum() const
Return momentum.
Definition: MCParticle.h:187
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
The PXD Cluster class This class stores all information about reconstructed PXD clusters The position...
Definition: PXDCluster.h:30
This is the Reconstruction Event-Data Model Track.
Definition: RecoTrack.h:79
std::vector< Belle2::RecoTrack::UsedPXDHit * > getPXDHitList() const
Return an unsorted list of pxd hits.
Definition: RecoTrack.h:449
std::vector< Belle2::RecoTrack::UsedSVDHit * > getSVDHitList() const
Return an unsorted list of svd hits.
Definition: RecoTrack.h:452
bool hasTrackFitStatus(const genfit::AbsTrackRep *representation=nullptr) const
Check, if there is a fit status for the given representation or for the cardinal one.
Definition: RecoTrack.cc:543
const genfit::FitStatus * getTrackFitStatus(const genfit::AbsTrackRep *representation=nullptr) const
Return the track fit status for the given representation or for the cardinal one. You are not allowed...
Definition: RecoTrack.h:621
The SVD Cluster class This class stores all information about reconstructed SVD clusters.
Definition: SVDCluster.h:29
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
bool isOptional(const std::string &name="")
Tell the DataStore about an optional input.
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
Values of the result of a track fit with a given particle hypothesis.
short getChargeSign() const
Return track charge (1 or -1).
ROOT::Math::XYZVector getMomentum() const
Getter for vector of momentum at closest approach of track in r/phi projection.
ROOT::Math::XYZVector getPosition() const
Getter for vector of position at closest approach of track in r/phi projection.
Container for likelihoods obtained by the VXD dE/dx PID (VXDDedxPIDModule).
DBObjPtr< SVDdEdxPDFs > m_SVDDedxPDFs
SVD DB object for dedx vs.
void calculateMeans(double &mean, double &truncatedMean, double &truncatedMeanErr, const std::vector< double > &dedx) const
Save arithmetic and truncated mean for the 'dedx' values.
int m_trackID
counter for tracks in this event
virtual void initialize() override
Initialize the module.
StoreArray< SVDCluster > m_svdClusters
Optional array of SVDClusters.
bool m_usePXD
use PXD data for likelihood
virtual void event() override
This method is called for each event.
StoreArray< MCParticle > m_mcparticles
Optional array of MCParticles.
StoreArray< VXDDedxLikelihood > m_dedxLikelihoods
Output array of VXDDedxLikelihoods.
void checkPDFs()
Check the pdfs for consistency every time they change in the database.
virtual void terminate() override
End of the event processing.
void saveSiHits(VXDDedxTrack *track, const HelixHelper &helix, const std::vector< HitClass * > &hits) const
save energy loss and hit information from SVD/PXDHits to track
DBObjPtr< PXDdEdxPDFs > m_PXDDedxPDFs
PXD DB object for dedx vs.
StoreArray< VXDDedxTrack > m_dedxTracks
Output array of VXDDedxTracks.
virtual ~VXDDedxPIDModule()
Destructor.
int m_eventID
counter for events
bool m_useSVD
use SVD data for likelihood
StoreArray< Track > m_tracks
Required array of Tracks.
bool m_onlyPrimaryParticles
For MC only: if true, only save data for primary particles (as determined by MC truth)
StoreArray< PXDCluster > m_pxdClusters
Optional array of PXDClusters.
static double getTraversedLength(const PXDCluster *hit, const HelixHelper *helix)
returns traversed length through active medium of given PXDCluster.
StoreArray< RecoTrack > m_recoTracks
Required array of RecoTracks.
bool m_useIndividualHits
use individual hits (true) or truncated mean (false) to determine likelihoods
VXDDedxPIDModule()
Default constructor.
double m_trackDistanceThreshhold
track distance threshold
Debug output for VXDDedxPID module.
Definition: VXDDedxTrack.h:29
Class to facilitate easy access to sensor information of the VXD like coordinate transformations or p...
Definition: GeoCache.h:39
const SensorInfoBase & getSensorInfo(Belle2::VxdID id) const
Return a reference to the SensorInfo of a given SensorID.
Definition: GeoCache.cc:67
static GeoCache & getInstance()
Return a reference to the singleton instance.
Definition: GeoCache.cc:214
Base class to provide Sensor Information for PXD and SVD.
SensorType getType() const
Return the Type of the Sensor.
Class to uniquely identify a any structure of the PXD and SVD.
Definition: VxdID.h:33
baseType getLayerNumber() const
Get the layer id.
Definition: VxdID.h:96
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:559
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:649
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
MCParticle * getMother() const
Returns a pointer to the mother particle.
Definition: MCParticle.h:590
Abstract base class for different kinds of events.