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
11#include <framework/gearbox/Const.h>
12
13#include <vxd/geometry/GeoCache.h>
14
15#include <reconstruction/dataobjects/VXDDedxTrack.h>
16#include <reconstruction/dataobjects/VXDDedxLikelihood.h>
17#include <mdst/dataobjects/Track.h>
18#include <mdst/dataobjects/MCParticle.h>
19#include <tracking/dataobjects/RecoTrack.h>
20#include <svd/dataobjects/SVDCluster.h>
21#include <pxd/dataobjects/PXDCluster.h>
22#include <svd/dbobjects/SVDdEdxPDFs.h>
23#include <pxd/dbobjects/PXDdEdxPDFs.h>
24
25#include <genfit/MaterialEffects.h>
26
27#include <Math/VectorUtil.h>
28#include <TH2F.h>
29
30#include <memory>
31#include <cassert>
32#include <cmath>
33#include <algorithm>
34
35using namespace Belle2;
36using namespace Dedx;
37
38REG_MODULE(VXDDedxPID);
39
41{
43
44 //Set module properties
45 setDescription("Extract dE/dx and corresponding log-likelihood from fitted tracks and hits in the SVD and PXD.");
46
47 //Parameter definitions
48 addParam("useIndividualHits", m_useIndividualHits,
49 "Use individual hits (true) or truncated mean (false) to determine likelihoods", false);
50 addParam("onlyPrimaryParticles", m_onlyPrimaryParticles,
51 "For MC only: if true, only save data for primary particles (as determined by MC truth)", false);
52 addParam("usePXD", m_usePXD, "Use PXDClusters for dE/dx calculation", false);
53 addParam("useSVD", m_useSVD, "Use SVDClusters for dE/dx calculation", true);
54
55 m_eventID = -1;
56 m_trackID = 0;
57}
58
60
62{
63 if (m_usePXD) {
64 if (not m_PXDDedxPDFs) B2FATAL("No PXD dE/dx PDF's available");
65 bool ok = m_PXDDedxPDFs->checkPDFs(not m_useIndividualHits);
66 if (not ok) B2FATAL("Binning or ranges of PXD dE/dx PDF's differ");
67 }
68 if (m_useSVD) {
69 if (not m_SVDDedxPDFs) B2FATAL("No SVD Dedx PDF's available");
70 bool ok = m_SVDDedxPDFs->checkPDFs(not m_useIndividualHits);
71 if (not ok) B2FATAL("Binning or ranges of SVD dE/dx PDF's differ");
72 }
73}
74
76{
77
78 // required inputs
79 m_tracks.isRequired();
80 m_recoTracks.isRequired();
81
82 //optional inputs
83 m_mcparticles.isOptional();
84 m_tracks.optionalRelationTo(m_mcparticles);
85
86 if (m_useSVD)
87 m_svdClusters.isRequired();
88 else
89 m_svdClusters.isOptional();
90 if (m_usePXD)
91 m_pxdClusters.isRequired();
92 else
93 m_pxdClusters.isOptional();
94
95 // register dE/dx data points
96 m_dedxTracks.registerInDataStore();
97 m_tracks.registerRelationTo(m_dedxTracks);
98
99 // register likelihoods
100 m_dedxLikelihoods.registerInDataStore();
101 m_tracks.registerRelationTo(m_dedxLikelihoods);
102
103 m_SVDDedxPDFs.addCallback([this]() {checkPDFs();});
104 m_PXDDedxPDFs.addCallback([this]() {checkPDFs();});
105 checkPDFs();
106
107
108 // create instances here to not confuse profiling
110
111 if (!genfit::MaterialEffects::getInstance()->isInitialized()) {
112 B2FATAL("Need to have SetupGenfitExtrapolationModule in path before this one.");
113 }
114}
115
117{
118 m_dedxTracks.clear();
119 m_dedxLikelihoods.clear();
120
121 // go through Tracks
122 // get fitresult and gftrack and do extrapolations, save corresponding dE/dx and likelihood values
123 // get genfit::TrackCand through genfit::Track::getCand()
124 // get hit indices through genfit::TrackCand::getHit(...)
125 // create one VXDDedxTrack per fitresult/gftrack
126 //create one DedkLikelihood per Track (plus rel)
127 m_eventID++;
128
129 // inputs
130 const int numMCParticles = m_mcparticles.getEntries();
131
132 // **************************************************
133 //
134 // LOOP OVER TRACKS
135 //
136 // **************************************************
137
138 for (const auto& track : m_tracks) {
139 m_trackID++;
140
141 std::shared_ptr<VXDDedxTrack> dedxTrack = std::make_shared<VXDDedxTrack>();
142 dedxTrack->m_eventID = m_eventID;
143 dedxTrack->m_trackID = m_trackID;
144
145 // load the pion fit hypothesis or the hypothesis which is the closest in mass to a pion
146 // the tracking will not always successfully fit with a pion hypothesis
147 const TrackFitResult* fitResult = track.getTrackFitResultWithClosestMass(Const::pion);
148 if (!fitResult) {
149 B2WARNING("No related fit for track ...");
150 continue;
151 }
152
153 if (numMCParticles != 0) {
154 // find MCParticle corresponding to this track
155 const MCParticle* mcpart = track.getRelatedTo<MCParticle>();
156
157 if (mcpart) {
159 continue; //not a primary particle, ignore
160 }
161
162 //add some MC truths to VXDDedxTrack object
163 dedxTrack->m_pdg = mcpart->getPDG();
164 const MCParticle* mother = mcpart->getMother();
165 dedxTrack->m_motherPDG = mother ? mother->getPDG() : 0;
166
167 const ROOT::Math::XYZVector trueMomentum = mcpart->getMomentum();
168 dedxTrack->m_pTrue = trueMomentum.R();
169 }
170 }
171
172 // get momentum (at origin) from fit result
173 const ROOT::Math::XYZVector& trackMom = fitResult->getMomentum();
174 dedxTrack->m_p = trackMom.R();
175 dedxTrack->m_cosTheta = cos(trackMom.Theta());
176 dedxTrack->m_charge = fitResult->getChargeSign();
177
178 // dE/dx values will be calculated using associated RecoTrack
179 const RecoTrack* recoTrack = track.getRelatedTo<RecoTrack>();
180 if (!recoTrack) {
181 B2WARNING("No related track for this fit...");
182 continue;
183 }
184
185 // Check to see if the track is pruned. We use the cardinal representation for this, as we do not expect that one
186 // representation is pruned whereas the other is not.
187 if (recoTrack->hasTrackFitStatus()) {
188 if (recoTrack->getTrackFitStatus()->isTrackPruned()) {
189 B2ERROR("GFTrack is pruned, please run VXDDedxPID only on unpruned tracks! Skipping this track.");
190 continue;
191 }
192 } else // The RecoTrack might not have a fit status for reasons: let's skip it
193 continue;
194
195 if (m_usePXD) {
196 const std::vector<PXDCluster*>& pxdClusters = recoTrack->getPXDHitList();
197 saveSiHits(dedxTrack.get(), pxdClusters, recoTrack);
198 }
199
200 if (m_useSVD) {
201 const std::vector<SVDCluster*>& svdClusters = recoTrack->getSVDHitList();
202 saveSiHits(dedxTrack.get(), svdClusters, recoTrack);
203 }
204
205 if (dedxTrack->dedx.empty()) {
206 B2DEBUG(50, "Found track with no hits, ignoring.");
207 continue;
208 }
209
210 // add a few last things to the VXDDedxTrack
211 const int numDedx = dedxTrack->dedx.size();
212 dedxTrack->m_nHits = numDedx;
213 // we remove the highest 2 dE/dx values, except when there is only 2 - then we remove 1 highest
214 dedxTrack->m_nHitsUsed = numDedx - 2;
215 if (numDedx == 2) {
216 dedxTrack->m_nHitsUsed = 1;
217 }
218
219 // calculate log likelihoods
220 dedxTrack->clearLogLikelihoods();
221 bool truncated = not m_useIndividualHits;
222 if (m_usePXD) dedxTrack->addLogLikelihoods(m_PXDDedxPDFs->getPDFs(truncated), Dedx::c_PXD, truncated);
223 if (m_useSVD) dedxTrack->addLogLikelihoods(m_SVDDedxPDFs->getPDFs(truncated), Dedx::c_SVD, truncated);
224
225 // save log likelihoods
226 if (dedxTrack->areLogLikelihoodsAvailable()) {
227 VXDDedxLikelihood* likelihoodObj = m_dedxLikelihoods.appendNew(dedxTrack->m_vxdLogl);
228 track.addRelationTo(likelihoodObj);
229 }
230
231 // save the VXDDedxTrack
232 VXDDedxTrack* newVXDDedxTrack = m_dedxTracks.appendNew(*dedxTrack);
233 track.addRelationTo(newVXDDedxTrack);
234
235 } // end of loop over tracks
236}
237
239{
240
241 B2DEBUG(50, "VXDDedxPIDModule exiting after processing " << m_trackID <<
242 " tracks in " << m_eventID + 1 << " events.");
243}
244
245
246void VXDDedxPIDModule::calculateMeans(double& mean, double& truncatedMean, double& truncatedMeanErr,
247 const std::vector<double>& dedx)
248{
249 // Calculate the truncated average by rejecting the highest two values (or highest one, if there are only 2 measurements)
250 std::vector<double> sortedDedx = dedx;
251 std::sort(sortedDedx.begin(), sortedDedx.end());
252
253 double truncatedMeanTmp = 0.0;
254 double meanTmp = 0.0;
255 double sumOfSquares = 0.0;
256 const int numDedx = sortedDedx.size();
257
258
259 for (int i = 0; i < numDedx; i++) {
260 meanTmp += sortedDedx[i];
261 }
262 if (numDedx != 0) {
263 meanTmp /= numDedx;
264 }
265
266 for (int i = 0; i < numDedx - 2; i++) {
267 truncatedMeanTmp += sortedDedx[i];
268 sumOfSquares += sortedDedx[i] * sortedDedx[i];
269 }
270 if (numDedx - 2 != 0) {
271 truncatedMeanTmp /= numDedx - 2;
272 } else {
273 truncatedMeanTmp = sortedDedx[0];
274 }
275
276 mean = meanTmp;
277 truncatedMean = truncatedMeanTmp;
278
279 if (numDedx - 2 > 1) {
280 truncatedMeanErr = sqrt(sumOfSquares / double(numDedx - 2) - truncatedMeanTmp * truncatedMeanTmp) / double((numDedx - 2) - 1);
281 } else {
282 truncatedMeanErr = 0;
283 }
284}
285
286template <class HitClass> double VXDDedxPIDModule::getTraversedLength(const HitClass* hit, const RecoTrack* recoTrack, double& p)
287{
289 const VXD::SensorInfoBase& sensor = geo.getSensorInfo(hit->getSensorID());
290 const ROOT::Math::XYZVector& sensorNormal = sensor.vectorToGlobal(ROOT::Math::XYZVector(0.0, 0.0, 1.0));
291
292 const auto* hitInfo = recoTrack->getRecoHitInformation(hit);
293 if (not hitInfo) return 0;
294
295 const auto* trackPoint = recoTrack->getCreatedTrackPoint(hitInfo);
296 if (not trackPoint) return 0;
297
298 const auto* fitterInfo = trackPoint->getFitterInfo();
299 if (not fitterInfo) return 0;
300
301 double cosTheta = 0;
302 try {
303 const genfit::MeasuredStateOnPlane& mop = fitterInfo->getFittedState();
304 auto pocaMom = ROOT::Math::XYZVector(mop.getMom());
305 p = pocaMom.R();
306 cosTheta = sensorNormal.Dot(pocaMom.Unit());
307 } catch (genfit::Exception&) {
308 B2WARNING("recoTrack: " << recoTrack->getArrayIndex() << ": genfit::MeasuredStateOnPlane exception occured");
309 return 0;
310 }
311
312 return std::min(sensor.getWidth(), sensor.getThickness() / std::abs(cosTheta));
313}
314
315
316template <class HitClass> void VXDDedxPIDModule::saveSiHits(VXDDedxTrack* track, const std::vector<HitClass*>& hits,
317 const RecoTrack* recoTrack)
318{
319 const int numHits = hits.size();
320 if (numHits == 0)
321 return;
322
324
325 //figure out which detector to assign hits to
326 const int currentDetector = geo.getSensorInfo(hits.front()->getSensorID()).getType();
327 assert(currentDetector == VXD::SensorInfoBase::PXD or currentDetector == VXD::SensorInfoBase::SVD);
328 // assert(currentDetector <= 1); //used as array index
329 assert(currentDetector == 0 or currentDetector == 1); //used as array index
330
331 std::vector<double> siliconDedx; //used for averages
332 siliconDedx.reserve(numHits);
333
334 VxdID prevSensor;
335 double psum = 0;
336 double p = 0;
337 for (int i = 0; i < numHits; i++) {
338 const HitClass* hit = hits[i];
339 if (!hit) {
340 B2ERROR("Added hit is a null pointer!");
341 continue;
342 }
343 const VxdID& currentSensor = hit->getSensorID();
344 int layer = -currentSensor.getLayerNumber();
345 assert(layer >= -6 && layer < 0);
346
347 //active medium traversed, in cm (can traverse one sensor at most)
348 double totalDistance = getTraversedLength(hit, recoTrack, p);
349 psum += p;
350 const double charge = hit->getCharge();
351 const double dedx = charge / totalDistance;
352 if (dedx <= 0) {
353 B2WARNING("dE/dx is " << dedx << " in layer " << layer);
354 } else if (i == 0 or prevSensor != currentSensor) { //only save once per sensor (u and v hits share charge!)
355 prevSensor = currentSensor;
356 //store data
357 if (totalDistance > 0) {
358 siliconDedx.push_back(dedx);
359 track->m_dedxAvg[currentDetector] += dedx;
360 track->addDedx(layer, totalDistance, dedx);
361 }
362 }
363
364 track->addHit(currentSensor, layer, charge, totalDistance, dedx);
365 track->m_p = psum / numHits;
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:662
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 getPDG() const
Return PDG code of particle.
Definition MCParticle.h:101
ROOT::Math::XYZVector getMomentum() const
Return momentum.
Definition MCParticle.h:187
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
Module()
Constructor.
Definition Module.cc:30
@ 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::UsedPXDHit * > getPXDHitList() const
Return an unsorted list of pxd hits.
Definition RecoTrack.h:447
std::vector< Belle2::RecoTrack::UsedSVDHit * > getSVDHitList() const
Return an unsorted list of svd hits.
Definition RecoTrack.h:450
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:544
RecoHitInformation * getRecoHitInformation(HitType *hit) const
Return the reco hit information for a generic hit from the storeArray.
Definition RecoTrack.h:312
const genfit::TrackPoint * getCreatedTrackPoint(const RecoHitInformation *recoHitInformation) const
Get a pointer to the TrackPoint that was created from this hit.
Definition RecoTrack.cc:230
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:619
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
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.
Container for likelihoods obtained by the VXD dE/dx PID (VXDDedxPIDModule).
static void saveSiHits(VXDDedxTrack *track, const std::vector< HitClass * > &hits, const RecoTrack *recoTrack)
save energy loss and hit information from SVD/PXDHits to track
DBObjPtr< SVDdEdxPDFs > m_SVDDedxPDFs
SVD DB object for dedx vs.
static void calculateMeans(double &mean, double &truncatedMean, double &truncatedMeanErr, const std::vector< double > &dedx)
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.
DBObjPtr< PXDdEdxPDFs > m_PXDDedxPDFs
PXD DB object for dedx vs.
StoreArray< VXDDedxTrack > m_dedxTracks
Output array of VXDDedxTracks.
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)
static double getTraversedLength(const HitClass *hit, const RecoTrack *recoTrack, double &p)
returns traversed length through active medium of given hit
StoreArray< PXDCluster > m_pxdClusters
Optional array of PXDClusters.
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.
virtual ~VXDDedxPIDModule() override
Destructor.
Debug output for VXDDedxPID module.
Class to facilitate easy access to sensor information of the VXD like coordinate transformations or p...
Definition GeoCache.h:38
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:32
baseType getLayerNumber() const
Get the layer id.
Definition VxdID.h:95
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:591
Abstract base class for different kinds of events.