Belle II Software  release-06-01-15
CDCDedxPIDModule.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/CDCDedxPID/CDCDedxPIDModule.h>
10 
11 #include <reconstruction/dataobjects/DedxConstants.h>
12 #include <reconstruction/modules/CDCDedxPID/LineHelper.h>
13 
14 #include <framework/gearbox/Const.h>
15 
16 #include <cdc/dataobjects/CDCHit.h>
17 #include <cdc/dataobjects/CDCRecoHit.h>
18 
19 #include <cdc/translators/LinearGlobalADCCountTranslator.h>
20 #include <cdc/translators/RealisticTDCCountTranslator.h>
21 
22 #include <cdc/geometry/CDCGeometryPar.h>
23 #include <tracking/dataobjects/RecoHitInformation.h>
24 
25 #include <genfit/AbsTrackRep.h>
26 #include <genfit/Exception.h>
27 #include <genfit/MaterialEffects.h>
28 #include <genfit/KalmanFitterInfo.h>
29 
30 #include <TH2F.h>
31 #include <TRandom.h>
32 
33 #include <memory>
34 #include <cmath>
35 #include <algorithm>
36 
37 using namespace Belle2;
38 using namespace CDC;
39 using namespace Dedx;
40 
41 REG_MODULE(CDCDedxPID)
42 
44 {
45  setPropertyFlags(c_ParallelProcessingCertified);
46 
47  //Set module properties
48  setDescription("Extract dE/dx and corresponding log-likelihood from fitted tracks and hits in the CDC.");
49 
50  //Parameter definitions
51  addParam("usePrediction", m_usePrediction,
52  "Use parameterized means and resolutions to determine PID values. If false, lookup table PDFs are used.", true);
53  addParam("removeLowest", m_removeLowest,
54  "Portion of events with low dE/dx that should be discarded", double(0.05));
55  addParam("removeHighest", m_removeHighest,
56  "Portion of events with high dE/dx that should be discarded", double(0.25));
57  addParam("useBackHalfCurlers", m_backHalfCurlers,
58  "Whether to use the back half of curlers", false);
59  addParam("enableDebugOutput", m_enableDebugOutput,
60  "Option to write out debugging information to CDCDedxTracks (DataStore objects).", true);
61  addParam("useIndividualHits", m_useIndividualHits,
62  "If using lookup table PDFs, include PDF value for each hit in likelihood. If false, the truncated mean of dedx values will be used.",
63  true);
64  addParam("ignoreMissingParticles", m_ignoreMissingParticles,
65  "Ignore particles for which no PDFs are found", false);
66  addParam("trackLevel", m_trackLevel,
67  "ONLY USEFUL FOR MC: Use track-level MC. If false, use hit-level MC", true);
68  addParam("onlyPrimaryParticles", m_onlyPrimaryParticles,
69  "ONLY USEFUL FOR MC: Only save data for primary particles", false);
70 }
71 
73 
75 {
76  if (!m_DBDedxPDFs) B2FATAL("No Dedx pdfs found in database");
77  //load dedx:momentum PDFs
78  int nBinsX, nBinsY;
79  double xMin, xMax, yMin, yMax;
80  nBinsX = nBinsY = -1;
81  xMin = xMax = yMin = yMax = 0.0;
82  for (unsigned int iPart = 0; iPart < 6; iPart++) {
83  const int pdgCode = Const::chargedStableSet.at(iPart).getPDGCode();
84  const TH2F* pdf = m_DBDedxPDFs->getCDCPDF(iPart, !m_useIndividualHits);
85 
86  if (pdf->GetEntries() == 0) {
87  if (m_ignoreMissingParticles)
88  continue;
89  B2FATAL("Couldn't find PDF for PDG " << pdgCode);
90  }
91 
92  //check that PDFs have the same dimensions and same binning
93  const double epsFactor = 1e-5;
94  if (nBinsX == -1 and nBinsY == -1) {
95  nBinsX = pdf->GetNbinsX();
96  nBinsY = pdf->GetNbinsY();
97  xMin = pdf->GetXaxis()->GetXmin();
98  xMax = pdf->GetXaxis()->GetXmax();
99  yMin = pdf->GetYaxis()->GetXmin();
100  yMax = pdf->GetYaxis()->GetXmax();
101  } else if (nBinsX != pdf->GetNbinsX()
102  or nBinsY != pdf->GetNbinsY()
103  or fabs(xMin - pdf->GetXaxis()->GetXmin()) > epsFactor * xMax
104  or fabs(xMax - pdf->GetXaxis()->GetXmax()) > epsFactor * xMax
105  or fabs(yMin - pdf->GetYaxis()->GetXmin()) > epsFactor * yMax
106  or fabs(yMax - pdf->GetYaxis()->GetXmax()) > epsFactor * yMax) {
107  B2FATAL("PDF for PDG " << pdgCode << " has binning/dimensions differing from previous PDF.");
108  }
109  }
110 }
111 
113 {
114 
115  // required inputs
116  m_tracks.isRequired();
117  m_recoTracks.isRequired();
118 
119  // optional inputs
120  m_mcparticles.isOptional();
121  m_tracks.optionalRelationTo(m_mcparticles);
122 
123  // register optional outputs
124  if (m_enableDebugOutput) {
125  m_dedxTracks.registerInDataStore();
126  m_tracks.registerRelationTo(m_dedxTracks);
127  }
128 
129  // register outputs
130  m_dedxLikelihoods.registerInDataStore();
131  m_tracks.registerRelationTo(m_dedxLikelihoods);
132 
133  m_DBDedxPDFs.addCallback([this]() { checkPDFs(); });
134  checkPDFs();
135 
136  // lookup table for number of wires per layer (indexed on superlayer)
137  m_nLayerWires[0] = 1280;
138  for (int i = 1; i < 9; ++i) {
139  m_nLayerWires[i] = m_nLayerWires[i - 1] + 6 * (160 + (i - 1) * 32);
140  }
141 
142  // make sure the mean and resolution parameters are reasonable
143  if (!m_DBMeanPars || m_DBMeanPars->getSize() == 0) {
144  B2WARNING("No dE/dx mean parameters!");
145  for (int i = 0; i < 15; ++i)
146  m_meanpars.push_back(1.0);
147  } else m_meanpars = m_DBMeanPars->getMeanPars();
148 
149  if (!m_DBSigmaPars || m_DBSigmaPars->getSize() == 0) {
150  B2WARNING("No dE/dx sigma parameters!");
151  for (int i = 0; i < 12; ++i)
152  m_sigmapars.push_back(1.0);
153  } else m_sigmapars = m_DBSigmaPars->getSigmaPars();
154 
155  // get the hadron correction parameters
156  if (!m_DBHadronCor || m_DBHadronCor->getSize() == 0) {
157  B2WARNING("No hadron correction parameters!");
158  for (int i = 0; i < 4; ++i)
159  m_hadronpars.push_back(0.0);
160  m_hadronpars.push_back(1.0);
161  } else m_hadronpars = m_DBHadronCor->getHadronPars();
162 
163  // create instances here to not confuse profiling
165 
166  if (!genfit::MaterialEffects::getInstance()->isInitialized()) {
167  B2FATAL("Need to have SetupGenfitExtrapolationModule in path before this one.");
168  }
169 }
170 
172 {
173  // go through Tracks
174  // get fitresult and RecoTrack and do extrapolations, save corresponding dE/dx and likelihood values
175  // get hit indices through RecoTrack::getHitPointsWithMeasurement(...)
176  // create one CDCDedxTrack per fitresult/recoTrack
177  // create one DedkLikelihood per Track (plus rel)
178 
179  // inputs
180  const int numMCParticles = m_mcparticles.getEntries();
181 
182  // get the geometry of the cdc
183  static CDCGeometryPar& cdcgeo = CDCGeometryPar::Instance();
184 
185  // **************************************************
186  //
187  // LOOP OVER TRACKS
188  //
189  // **************************************************
190 
191  m_dedxTracks.clear();
192  m_dedxLikelihoods.clear();
193 
194  int mtrack = 0;
195  for (const auto& track : m_tracks) {
196  std::shared_ptr<CDCDedxTrack> dedxTrack = std::make_shared<CDCDedxTrack>();
197  dedxTrack->m_track = mtrack++;
198 
199  // load the pion fit hypothesis or the hypothesis which is the closest in mass to a pion
200  // the tracking will not always successfully fit with a pion hypothesis
201  const TrackFitResult* fitResult = track.getTrackFitResultWithClosestMass(Const::pion);
202  if (!fitResult) {
203  B2WARNING("No related fit for track ...");
204  continue;
205  }
206 
207  if ((m_enableDebugOutput or m_onlyPrimaryParticles) and numMCParticles != 0) {
208  // find MCParticle corresponding to this track
209  const MCParticle* mcpart = track.getRelatedTo<MCParticle>();
210 
211  if (mcpart) {
212  if (m_onlyPrimaryParticles && !mcpart->hasStatus(MCParticle::c_PrimaryParticle)) {
213  continue; //not a primary particle, ignore
214  }
215 
216  //add some MC truths to CDCDedxTrack object
217  dedxTrack->m_pdg = mcpart->getPDG();
218  dedxTrack->m_mcmass = mcpart->getMass();
219  const MCParticle* mother = mcpart->getMother();
220  dedxTrack->m_motherPDG = mother ? mother->getPDG() : 0;
221 
222  const TVector3 trueMomentum = mcpart->getMomentum();
223  dedxTrack->m_pTrue = trueMomentum.Mag();
224  dedxTrack->m_cosThetaTrue = trueMomentum.CosTheta();
225  }
226  } else {
227  dedxTrack->m_pdg = -999;
228  }
229 
230  // get momentum (at origin) from fit result
231  const TVector3& trackMom = fitResult->getMomentum();
232  dedxTrack->m_p = trackMom.Mag();
233  bool nomom = (dedxTrack->m_p != dedxTrack->m_p);
234  double costh = std::cos(std::atan(1 / fitResult->getCotTheta()));
235  int charge = 1;
236  if (!nomom) {
237  costh = trackMom.CosTheta();
238  charge = fitResult->getChargeSign();
239  }
240  dedxTrack->m_cosTheta = costh;
241  dedxTrack->m_charge = charge;
242 
243  // dE/dx values will be calculated using associated RecoTrack
244  const RecoTrack* recoTrack = track.getRelatedTo<RecoTrack>();
245  if (!recoTrack) {
246  B2WARNING("No related track for this fit...");
247  continue;
248  }
249 
250  // Check to see if the track is pruned
251  if (recoTrack->getTrackFitStatus()->isTrackPruned()) {
252  B2ERROR("GFTrack is pruned, please run CDCDedxPID only on unpruned tracks! Skipping this track.");
253  continue;
254  }
255 
256  // scale factor to make electron dE/dx ~ 1
257  dedxTrack->m_scale = (m_DBScaleFactor) ? m_DBScaleFactor->getScaleFactor() : 1.0;
258 
259  // store run gains only for data!
260  dedxTrack->m_runGain = (m_DBRunGain && m_usePrediction && numMCParticles == 0) ? m_DBRunGain->getRunGain() : 1.0;
261 
262  // get the cosine correction only for data!
263  dedxTrack->m_cosCor = (m_DBCosineCor && m_usePrediction && numMCParticles == 0) ? m_DBCosineCor->getMean(costh) : 1.0;
264 
265  // get the cosine edge correction only for data!
266  bool isEdge = false;
267  if ((abs(costh + 0.860) < 0.010) || (abs(costh - 0.955) <= 0.005))isEdge = true;
268  dedxTrack->m_cosEdgeCor = (m_DBCosEdgeCor && m_usePrediction && numMCParticles == 0
269  && isEdge) ? m_DBCosEdgeCor->getMean(costh) : 1.0;
270 
271  // initialize a few variables to be used in the loop over track points
272  double layerdE = 0.0; // total charge in current layer
273  double layerdx = 0.0; // total path length in current layer
274  double cdcMom = 0.0; // momentum valid in the CDC
275  int nhitscombined = 0; // number of hits combined per layer
276  int wirelongesthit = 0; // wire number of longest hit
277  double longesthit = 0; // path length of longest hit
278 
279  // loop over all CDC hits from this track
280  // Get the TrackPoints, which contain the hit information we need.
281  // Then iterate over each point.
282  int tpcounter = 0;
283  const std::vector< genfit::AbsTrackRep* >& gftrackRepresentations = recoTrack->getRepresentations();
284  const std::vector< genfit::TrackPoint* >& gftrackPoints = recoTrack->getHitPointsWithMeasurement();
285  for (std::vector< genfit::TrackPoint* >::const_iterator tp = gftrackPoints.begin();
286  tp != gftrackPoints.end(); ++tp) {
287  tpcounter++;
288 
289  // should also be possible to use this for svd and pxd hits...
290  genfit::AbsMeasurement* aAbsMeasurementPtr = (*tp)->getRawMeasurement(0);
291  const CDCRecoHit* cdcRecoHit = dynamic_cast<const CDCRecoHit* >(aAbsMeasurementPtr);
292  if (!cdcRecoHit) continue;
293  const CDCHit* cdcHit = cdcRecoHit->getCDCHit();
294  if (!cdcHit) continue;
295 
296  // get the poca on the wire and track momentum for this hit
297  // make sure the fitter info exists
298  const genfit::AbsFitterInfo* fi = (*tp)->getFitterInfo();
299  if (!fi) {
300  B2DEBUG(29, "No fitter info, skipping...");
301  continue;
302  }
303 
304  // get the wire ID (not between 0 and 14336) and the layer info
305  WireID wireID = cdcRecoHit->getWireID();
306  const int wire = wireID.getIWire(); // use getEWire() for encoded wire number
307  int layer = cdcHit->getILayer(); // layer within superlayer
308  int superlayer = cdcHit->getISuperLayer();
309 
310  // check which algorithm found this hit
311  int foundByTrackFinder = 0;
312  const RecoHitInformation* hitInfo = recoTrack->getRecoHitInformation(cdcHit);
313  foundByTrackFinder = hitInfo->getFoundByTrackFinder();
314 
315  // add weights for hypotheses
316  double weightPionHypo = 0;
317  double weightProtHypo = 0;
318  double weightKaonHypo = 0;
319  // loop over all the present hypotheses
320  for (std::vector<genfit::AbsTrackRep* >::const_iterator trep = gftrackRepresentations.begin();
321  trep != gftrackRepresentations.end(); ++trep) {
322  const int pdgCode = TMath::Abs((*trep)->getPDG());
323  // configured to only save weights for one of these 3
324  if (!(pdgCode == Const::pion.getPDGCode() ||
325  pdgCode == Const::kaon.getPDGCode() ||
326  pdgCode == Const::proton.getPDGCode())) continue;
327  const genfit::KalmanFitterInfo* kalmanFitterInfo = (*tp)->getKalmanFitterInfo(*trep);
328  if (kalmanFitterInfo == NULL) {
329  //B2WARNING("No KalmanFitterInfo for hit in "<<pdgCode<<" track representationt. Skipping.");
330  continue;
331  }
332 
333  // there are always 2 hits, one of which is ~1, the other ~0; or both ~0. Store only the largest.
334  std::vector<double> weights = kalmanFitterInfo->getWeights();
335  const double maxWeight = weights[0] > weights[1] ? weights[0] : weights[1];
336  if (pdgCode == Const::pion.getPDGCode()) weightPionHypo = maxWeight;
337  else if (pdgCode == Const::kaon.getPDGCode()) weightKaonHypo = maxWeight;
338  else if (pdgCode == Const::proton.getPDGCode()) weightProtHypo = maxWeight;
339 
340  }
341 
342  // continuous layer number
343  int currentLayer = (superlayer == 0) ? layer : (8 + (superlayer - 1) * 6 + layer);
344  int nextLayer = currentLayer;
345 
346  // dense packed wire number (between 0 and 14336)
347  const int iwire = (superlayer == 0) ? 160 * layer + wire : m_nLayerWires[superlayer - 1] + (160 + 32 *
348  (superlayer - 1)) * layer + wire;
349 
350  // if multiple hits in a layer, we may combine the hits
351  const bool lastHit = (tp + 1 == gftrackPoints.end());
352  bool lastHitInCurrentLayer = lastHit;
353  if (!lastHit) {
354  // peek at next hit
355  genfit::AbsMeasurement* aAbsMeasurementPtrNext = (*(tp + 1))->getRawMeasurement(0);
356  const CDCRecoHit* nextcdcRecoHit = dynamic_cast<const CDCRecoHit* >(aAbsMeasurementPtrNext);
357  // if next hit fails, assume this is the last hit in the layer
358  if (!nextcdcRecoHit || !(cdcRecoHit->getCDCHit()) || !((*(tp + 1))->getFitterInfo())) {
359  lastHitInCurrentLayer = true;
360  break;
361  }
362  const CDCHit* nextcdcHit = nextcdcRecoHit->getCDCHit();
363  const int nextILayer = nextcdcHit->getILayer();
364  const int nextSuperlayer = nextcdcHit->getISuperLayer();
365  nextLayer = (nextSuperlayer == 0) ? nextILayer : (8 + (nextSuperlayer - 1) * 6 + nextILayer);
366  lastHitInCurrentLayer = (nextLayer != currentLayer);
367  }
368 
369  // find the position of the endpoints of the sense wire
370  const TVector3& wirePosF = cdcgeo.wireForwardPosition(wireID, CDCGeometryPar::c_Aligned);
371  const TVector3& wirePosB = cdcgeo.wireBackwardPosition(wireID, CDCGeometryPar::c_Aligned);
372  const TVector3 wireDir = (wirePosB - wirePosF).Unit();
373 
374  int nWires = cdcgeo.nWiresInLayer(currentLayer);
375 
376  // radii of field wires for this layer
377  double inner = cdcgeo.innerRadiusWireLayer()[currentLayer];
378  double outer = cdcgeo.outerRadiusWireLayer()[currentLayer];
379 
380  double topHeight = outer - wirePosF.Perp();
381  double bottomHeight = wirePosF.Perp() - inner;
382  double cellHeight = topHeight + bottomHeight;
383  double topHalfWidth = M_PI * outer / nWires;
384  double bottomHalfWidth = M_PI * inner / nWires;
385  double cellHalfWidth = M_PI * wirePosF.Perp() / nWires;
386 
387  // first construct the boundary lines, then create the cell
388  const DedxPoint tl = DedxPoint(-topHalfWidth, topHeight);
389  const DedxPoint tr = DedxPoint(topHalfWidth, topHeight);
390  const DedxPoint br = DedxPoint(bottomHalfWidth, -bottomHeight);
391  const DedxPoint bl = DedxPoint(-bottomHalfWidth, -bottomHeight);
392  DedxDriftCell c = DedxDriftCell(tl, tr, br, bl);
393 
394  // make sure the MOP is reasonable (an exception is thrown for bad numerics)
395  try {
396  const genfit::MeasuredStateOnPlane& mop = fi->getFittedState();
397 
398  // use the MOP to determine the DOCA and entrance angle
399  B2Vector3D fittedPoca = mop.getPos();
400  const TVector3& pocaMom = mop.getMom();
401  if (tp == gftrackPoints.begin() || cdcMom == 0) {
402  cdcMom = pocaMom.Mag();
403  dedxTrack->m_pCDC = cdcMom;
404  }
405  if (nomom)
406  dedxTrack->m_p = cdcMom;
407 
408  // get the doca and entrance angle information.
409  // constructPlane places the coordinate center in the POCA to the
410  // wire. Using this is the default behavior. If this should be too
411  // slow, as it has to re-evaluate the POCA
412  // B2Vector3D pocaOnWire = cdcRecoHit->constructPlane(mop)->getO();
413 
414  // uses the plane determined by the track fit.
415  B2Vector3D pocaOnWire = mop.getPlane()->getO(); // DOUBLE CHECK THIS --\/
416 
417  // The vector from the wire to the track.
418  B2Vector3D B2WireDoca = fittedPoca - pocaOnWire;
419 
420  // the sign of the doca is defined here to be positive in the +x dir in the cell
421  double doca = B2WireDoca.Perp();
422  double phidiff = fittedPoca.Phi() - pocaOnWire.Phi();
423  // be careful about "wrap around" cases when the poca and wire are close, but
424  // the difference in phi is largy
425  if (phidiff > -3.1416 && (phidiff < 0 || phidiff > 3.1416)) doca *= -1;
426 
427  // The opening angle of the track momentum direction
428  const double px = pocaMom.x();
429  const double py = pocaMom.y();
430  const double wx = pocaOnWire.x();
431  const double wy = pocaOnWire.y();
432  const double cross = wx * py - wy * px;
433  const double dot = wx * px + wy * py;
434  double entAng = atan2(cross, dot);
435 
436  // re-scaled (RS) doca and entAng variable: map to square cell
437  double cellR = 2 * cellHalfWidth / cellHeight;
438  double tana = 100.0;
439  if (std::abs(2 * atan(1) - std::abs(entAng)) < 0.01)tana = 100 * (entAng / std::abs(entAng)); //avoid infinity at pi/2
440  else tana = std::tan(entAng);
441  double docaRS = doca * std::sqrt((1 + cellR * cellR * tana * tana) / (1 + tana * tana));
442  double entAngRS = std::atan(tana / cellR);
443 
445  int adcbaseCount = cdcHit->getADCCount(); // pedestal subtracted?
446  int adcCount = (m_DBNonlADC && m_usePrediction
447  && numMCParticles == 0) ? m_DBNonlADC->getCorrectedADC(adcbaseCount, currentLayer) : adcbaseCount;
448 
449  double hitCharge = translator.getCharge(adcCount, wireID, false, pocaOnWire.Z(), pocaMom.Phi());
450  int driftT = cdcHit->getTDCCount();
451 
452  // we want electrons to be one, so artificially scale the adcCount
453  double dadcCount = 1.0 * adcCount;
454  if (dedxTrack->m_scale != 0) dadcCount /= dedxTrack->m_scale;
455 
456  RealisticTDCCountTranslator realistictdc;
457  double driftDRealistic = realistictdc.getDriftLength(driftT, wireID, 0, true, pocaOnWire.Z(), pocaMom.Phi(), pocaMom.Theta());
458  double driftDRealisticRes = realistictdc.getDriftLengthResolution(driftDRealistic, wireID, true, pocaOnWire.Z(), pocaMom.Phi(),
459  pocaMom.Theta());
460 
461  // now calculate the path length for this hit
462  double celldx = c.dx(doca, entAng);
463  if (c.isValid()) {
464  // get the wire gain constant
465  double wiregain = (m_DBWireGains && m_usePrediction && numMCParticles == 0) ? m_DBWireGains->getWireGain(iwire) : 1.0;
466 
467  //normalization of rescaled doca wrt cell size for layer dependent 2D corrections
468  double normDocaRS = docaRS / cellHalfWidth;
469  double twodcor = (m_DB2DCell && m_usePrediction
470  && numMCParticles == 0) ? m_DB2DCell->getMean(currentLayer, normDocaRS, entAngRS) : 1.0;
471 
472  // get the 1D cleanup correction
473  double onedcor = (m_DB1DCell && m_usePrediction && numMCParticles == 0) ? m_DB1DCell->getMean(currentLayer, entAngRS) : 1.0;
474 
475  // apply the calibration to dE to propagate to both hit and layer measurements
476  // Note: could move the sin(theta) here since it is common accross the track
477  // It is applied in two places below (hit level and layer level)
478  double correction = dedxTrack->m_runGain * dedxTrack->m_cosCor * dedxTrack->m_cosEdgeCor * wiregain * twodcor * onedcor;
479 
480  // --------------------
481  // save individual hits (even for dead wires)
482  // --------------------
483  if (correction != 0) dadcCount = dadcCount / correction;
484  else dadcCount = 0;
485 
486  double cellDedx = (dadcCount / celldx);
487 
488  // correct for path length through the cell
489  if (nomom) cellDedx *= std::sin(std::atan(1 / fitResult->getCotTheta()));
490  else cellDedx *= std::sin(trackMom.Theta());
491 
492  if (m_enableDebugOutput)
493  dedxTrack->addHit(wire, iwire, currentLayer, doca, docaRS, entAng, entAngRS,
494  adcCount, adcbaseCount, hitCharge, celldx, cellDedx, cellHeight, cellHalfWidth, driftT,
495  driftDRealistic, driftDRealisticRes, wiregain, twodcor, onedcor,
496  foundByTrackFinder, weightPionHypo, weightKaonHypo, weightProtHypo);
497 
498  // --------------------
499  // save layer hits only with active wires
500  // --------------------
501  if (correction != 0) {
502 
503  layerdE += dadcCount;
504  layerdx += celldx;
505 
506  if (celldx > longesthit) {
507  longesthit = celldx;
508  wirelongesthit = iwire;
509  }
510  nhitscombined++;
511  }
512  }
513  } catch (genfit::Exception&) {
514  B2WARNING("Track: " << mtrack << ": genfit::MeasuredStateOnPlane exception...");
515  continue;
516  }
517 
518  // check if there are any more hits in this layer
519  if (lastHitInCurrentLayer) {
520  if (layerdx > 0) {
521  double totalDistance;
522  if (nomom) totalDistance = layerdx / std::sin(std::atan(1 / fitResult->getCotTheta()));
523  else totalDistance = layerdx / std::sin(trackMom.Theta());
524  double layerDedx = layerdE / totalDistance;
525 
526  // save the information for this layer
527  if (layerDedx > 0) {
528  dedxTrack->addDedx(nhitscombined, wirelongesthit, currentLayer, totalDistance, layerDedx);
529  // save the PID information if using individual hits
530  if (m_useIndividualHits) {
531  // use the momentum valid in the cdc
532  saveLookupLogl(dedxTrack->m_cdcLogl, dedxTrack->m_pCDC, layerDedx);
533  }
534  }
535  }
536  // stop when the track starts to curl
537  if (!m_backHalfCurlers && nextLayer < currentLayer) break;
538 
539  layerdE = 0;
540  layerdx = 0;
541  nhitscombined = 0;
542  wirelongesthit = 0;
543  longesthit = 0;
544  }
545  } // end of loop over CDC hits for this track
546 
547  // Determine the number of hits to be used
548  //m_lDedx is a ector for layerDedx and created w/ ->adddedx method above
549  if (dedxTrack->m_lDedx.empty()) {
550  B2DEBUG(29, "Found track with no hits, ignoring.");
551  continue;
552  } else {
553  // determine the number of hits for this track (used below)
554  const int numDedx = dedxTrack->m_lDedx.size();
555  // add a factor of 0.51 here to make sure we are rounding appropriately...
556  const int lowEdgeTrunc = int(numDedx * m_removeLowest + 0.51);
557  const int highEdgeTrunc = int(numDedx * (1 - m_removeHighest) + 0.51);
558  dedxTrack->m_lNHitsUsed = highEdgeTrunc - lowEdgeTrunc;
559  }
560 
561  // Get the truncated mean
562  //
563  // If using track-level MC, get the predicted mean and resolution and throw a random
564  // number to get the simulated dE/dx truncated mean. Otherwise, calculate the truncated
565  // mean from the simulated hits (hit-level MC).
566  if (!m_useIndividualHits or m_enableDebugOutput) {
567  calculateMeans(&(dedxTrack->m_dedxAvg),
568  &(dedxTrack->m_dedxAvgTruncatedNoSat),
569  &(dedxTrack->m_dedxAvgTruncatedErr),
570  dedxTrack->m_lDedx);
571 
572  // apply the "hadron correction" only to data
573  if (numMCParticles == 0)
574  dedxTrack->m_dedxAvgTruncated = D2I(costh, I2D(costh, 1.00) / 1.00 * dedxTrack->m_dedxAvgTruncatedNoSat);
575  else dedxTrack->m_dedxAvgTruncated = dedxTrack->m_dedxAvgTruncatedNoSat;
576  }
577 
578  // If this is a MC track, get the track-level dE/dx
579  if (numMCParticles != 0 && dedxTrack->m_mcmass > 0 && dedxTrack->m_pTrue != 0) {
580  // determine the predicted mean and resolution
581  double mean = getMean(dedxTrack->m_pCDC / dedxTrack->m_mcmass);
582  double sigma = getSigma(mean, dedxTrack->m_lNHitsUsed, std::sqrt(1 - dedxTrack->m_cosTheta * dedxTrack->m_cosTheta));
583  dedxTrack->m_simDedx = gRandom->Gaus(mean, sigma);
584  while (dedxTrack->m_simDedx < 0)
585  dedxTrack->m_simDedx = gRandom->Gaus(mean, sigma);
586  if (m_trackLevel)
587  dedxTrack->m_dedxAvgTruncated = dedxTrack->m_simDedx;
588  }
589 
590  // save the PID information for both lookup tables and parameterized means and resolutions
591  saveChiValue(dedxTrack->m_cdcChi, dedxTrack->m_predmean, dedxTrack->m_predres, dedxTrack->m_pCDC, dedxTrack->m_dedxAvgTruncated,
592  std::sqrt(1 - dedxTrack->m_cosTheta * dedxTrack->m_cosTheta), dedxTrack->m_lNHitsUsed);
593  if (!m_useIndividualHits)
594  saveLookupLogl(dedxTrack->m_cdcLogl, dedxTrack->m_pCDC, dedxTrack->m_dedxAvgTruncated);
595 
596  // save CDCDedxLikelihood
597  // use parameterized method if called or if pdf file for lookup tables are empty
598  double pidvalues[Const::ChargedStable::c_SetSize];
600  if (m_usePrediction) {
601  for (const Const::ChargedStable pdgIter : set) {
602  pidvalues[pdgIter.getIndex()] = -0.5 * dedxTrack->m_cdcChi[pdgIter.getIndex()] * dedxTrack->m_cdcChi[pdgIter.getIndex()];
603  }
604  } else {
605  for (const Const::ChargedStable pdgIter : set) {
606  pidvalues[pdgIter.getIndex()] = dedxTrack->m_cdcLogl[pdgIter.getIndex()];
607  }
608  }
609 
610  CDCDedxLikelihood* likelihoodObj = m_dedxLikelihoods.appendNew(pidvalues);
611  track.addRelationTo(likelihoodObj);
612 
613  if (m_enableDebugOutput) {
614  // book the information for this track
615  CDCDedxTrack* newCDCDedxTrack = m_dedxTracks.appendNew(*dedxTrack);
616  track.addRelationTo(newCDCDedxTrack);
617  }
618 
619  } // end of loop over tracks
620 }
621 
623 {
624 
625  B2DEBUG(29, "CDCDedxPIDModule exiting");
626 }
627 
628 void CDCDedxPIDModule::calculateMeans(double* mean, double* truncatedMean, double* truncatedMeanErr,
629  const std::vector<double>& dedx) const
630 {
631  // Calculate the truncated average by skipping the lowest & highest
632  // events in the array of dE/dx values
633  std::vector<double> sortedDedx = dedx;
634  std::sort(sortedDedx.begin(), sortedDedx.end());
635  sortedDedx.erase(std::remove(sortedDedx.begin(), sortedDedx.end(), 0), sortedDedx.end());
636  sortedDedx.shrink_to_fit();
637 
638  double truncatedMeanTmp = 0.0;
639  double meanTmp = 0.0;
640  double sumOfSquares = 0.0;
641  int numValuesTrunc = 0;
642  const int numDedx = sortedDedx.size();
643 
644  // add a factor of 0.51 here to make sure we are rounding appropriately...
645  const int lowEdgeTrunc = int(numDedx * m_removeLowest + 0.51);
646  const int highEdgeTrunc = int(numDedx * (1 - m_removeHighest) + 0.51);
647  for (int i = 0; i < numDedx; i++) {
648  meanTmp += sortedDedx[i];
649  if (i >= lowEdgeTrunc and i < highEdgeTrunc) {
650  truncatedMeanTmp += sortedDedx[i];
651  sumOfSquares += sortedDedx[i] * sortedDedx[i];
652  numValuesTrunc++;
653  }
654  }
655 
656  if (numDedx != 0) {
657  meanTmp /= numDedx;
658  }
659  if (numValuesTrunc != 0) {
660  truncatedMeanTmp /= numValuesTrunc;
661  } else {
662  truncatedMeanTmp = meanTmp;
663  }
664 
665  *mean = meanTmp;
666  *truncatedMean = truncatedMeanTmp;
667 
668  if (numValuesTrunc > 1) {
669  *truncatedMeanErr = sqrt(sumOfSquares / double(numValuesTrunc) - truncatedMeanTmp * truncatedMeanTmp) / double(
670  numValuesTrunc - 1);
671  } else {
672  *truncatedMeanErr = 0;
673  }
674 }
675 
676 void CDCDedxPIDModule::saveLookupLogl(double(&logl)[Const::ChargedStable::c_SetSize], double p, double dedx)
677 {
678  //all pdfs have the same dimensions
679  const TH2F* pdf = m_DBDedxPDFs->getCDCPDF(0, !m_useIndividualHits);
680  const Int_t binX = pdf->GetXaxis()->FindFixBin(p);
681  const Int_t binY = pdf->GetYaxis()->FindFixBin(dedx);
682 
683  for (unsigned int iPart = 0; iPart < Const::ChargedStable::c_SetSize; iPart++) {
684  pdf = m_DBDedxPDFs->getCDCPDF(iPart, !m_useIndividualHits);
685  if (pdf->GetEntries() == 0) { //might be NULL if m_ignoreMissingParticles is set
686  B2WARNING("NO CDC PDFS...");
687  continue;
688  }
689  double probability = 0.0;
690 
691  //check if this is still in the histogram, take overflow bin otherwise
692  if (binX < 1 or binX > pdf->GetNbinsX()
693  or binY < 1 or binY > pdf->GetNbinsY()) {
694  probability = pdf->GetBinContent(binX, binY);
695  } else {
696  //in normal histogram range. Of course ROOT has a bug that Interpolate()
697  //is not declared as const but it does not modify the internal state so
698  //fine, const_cast it is.
699  probability = const_cast<TH2F*>(pdf)->Interpolate(p, dedx);
700  }
701 
702  if (probability != probability)
703  B2ERROR("probability NAN for a track with p=" << p << " and dedx=" << dedx);
704 
705  //my pdfs aren't perfect...
706  if (probability == 0.0)
707  probability = m_useIndividualHits ? (1e-5) : (1e-3); //likelihoods for truncated mean are much higher
708  logl[iPart] += log(probability);
709  }
710 }
711 
712 double CDCDedxPIDModule::D2I(const double cosTheta, const double D) const
713 {
714  double absCosTheta = fabs(cosTheta);
715  double projection = pow(absCosTheta, m_hadronpars[3]) + m_hadronpars[2];
716  if (projection == 0) {
717  B2WARNING("Something wrong with dE/dx hadron constants!");
718  return D;
719  }
720 
721  double chargeDensity = D / projection;
722  double numerator = 1 + m_hadronpars[0] * chargeDensity;
723  double denominator = 1 + m_hadronpars[1] * chargeDensity;
724 
725  if (denominator == 0) {
726  B2WARNING("Something wrong with dE/dx hadron constants!");
727  return D;
728  }
729 
730  double I = D * m_hadronpars[4] * numerator / denominator;
731  return I;
732 }
733 
734 double CDCDedxPIDModule::I2D(const double cosTheta, const double I) const
735 {
736  double absCosTheta = fabs(cosTheta);
737  double projection = pow(absCosTheta, m_hadronpars[3]) + m_hadronpars[2];
738 
739  if (projection == 0 || m_hadronpars[4] == 0) {
740  B2WARNING("Something wrong with dE/dx hadron constants!");
741  return I;
742  }
743 
744  double a = m_hadronpars[0] / projection;
745  double b = 1 - m_hadronpars[1] / projection * (I / m_hadronpars[4]);
746  double c = -1.0 * I / m_hadronpars[4];
747 
748  if (b == 0 && a == 0) {
749  B2WARNING("both a and b coefficiants for hadron correction are 0");
750  return I;
751  }
752 
753  double D = (a != 0) ? (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a) : -c / b;
754  if (D < 0) {
755  B2WARNING("D is less 0! will try another solution");
756  D = (a != 0) ? (-b - sqrt(b * b + 4.0 * a * c)) / (2.0 * a) : -c / b;
757  if (D < 0) {
758  B2WARNING("D is still less 0! just return uncorrectecd value");
759  return I;
760  }
761  }
762 
763  return D;
764 }
765 
766 double CDCDedxPIDModule::meanCurve(double* x, double* par, int version) const
767 {
768  // calculate the predicted mean value as a function of beta-gamma (bg)
769  // this is done with a different function depending on the value of bg
770  double f = 0;
771 
772  if (version == 0) {
773  if (par[0] == 1)
774  f = par[1] * std::pow(std::sqrt(x[0] * x[0] + 1), par[3]) / std::pow(x[0], par[3]) *
775  (par[2] - par[5] * std::log(1 / x[0])) - par[4] + std::exp(par[6] + par[7] * x[0]);
776  else if (par[0] == 2)
777  f = par[1] * std::pow(x[0], 3) + par[2] * x[0] * x[0] + par[3] * x[0] + par[4];
778  else if (par[0] == 3)
779  f = -1.0 * par[1] * std::log(par[4] + std::pow(1 / x[0], par[2])) + par[3];
780  }
781 
782  return f;
783 }
784 
785 double CDCDedxPIDModule::getMean(double bg) const
786 {
787  // define the section of the mean to use
788  double A = 0, B = 0, C = 0;
789  if (bg < 4.5)
790  A = 1;
791  else if (bg < 10)
792  B = 1;
793  else
794  C = 1;
795 
796  double x[1]; x[0] = bg;
797  double parsA[9];
798  double parsB[5];
799  double parsC[5];
800 
801  parsA[0] = 1; parsB[0] = 2; parsC[0] = 3;
802  for (int i = 0; i < 15; ++i) {
803  if (i < 7) parsA[i + 1] = m_meanpars[i];
804  else if (i < 11) parsB[i % 7 + 1] = m_meanpars[i];
805  else parsC[i % 11 + 1] = m_meanpars[i];
806  }
807 
808  // calculate dE/dx from the Bethe-Bloch mean
809  double partA = meanCurve(x, parsA, 0);
810  double partB = meanCurve(x, parsB, 0);
811  double partC = meanCurve(x, parsC, 0);
812 
813  return (A * partA + B * partB + C * partC);
814 }
815 
816 double CDCDedxPIDModule::sigmaCurve(double* x, double* par, int version) const
817 {
818  // calculate the predicted mean value as a function of beta-gamma (bg)
819  // this is done with a different function depending dE/dx, nhit, and sin(theta)
820  double f = 0;
821 
822  if (version == 0) {
823  if (par[0] == 1) { // return dedx parameterization
824  f = par[1] + par[2] * x[0];
825  } else if (par[0] == 2) { // return nhit or sin(theta) parameterization
826  f = par[1] * std::pow(x[0], 4) + par[2] * std::pow(x[0], 3) +
827  par[3] * x[0] * x[0] + par[4] * x[0] + par[5];
828  }
829  }
830 
831  return f;
832 }
833 
834 
835 double CDCDedxPIDModule::getSigma(double dedx, double nhit, double sin) const
836 {
837  if (nhit < 5) nhit = 5;
838  if (sin > 0.99) sin = 0.99;
839 
840  double x[1];
841  double dedxpar[3];
842  double nhitpar[6];
843  double sinpar[6];
844 
845  dedxpar[0] = 1; nhitpar[0] = 2; sinpar[0] = 2;
846  for (int i = 0; i < 5; ++i) {
847  if (i < 2) dedxpar[i + 1] = m_sigmapars[i];
848  nhitpar[i + 1] = m_sigmapars[i + 2];
849  sinpar[i + 1] = m_sigmapars[i + 7];
850  }
851 
852  // determine sigma from the parameterization
853  x[0] = dedx;
854  double corDedx = sigmaCurve(x, dedxpar, 0);
855  x[0] = nhit;
856  double corNHit = sigmaCurve(x, nhitpar, 0);
857  if (nhit > 42) corNHit = 1.0;
858  x[0] = sin;
859  double corSin = sigmaCurve(x, sinpar, 0);
860 
861  return (corDedx * corSin * corNHit);
862 }
863 
865  double(&predmean)[Const::ChargedStable::c_SetSize], double(&predsigma)[Const::ChargedStable::c_SetSize], double p, double dedx,
866  double sin, int nhit) const
867 {
868  // determine a chi value for each particle type
870  for (const Const::ChargedStable pdgIter : set) {
871  double bg = p / pdgIter.getMass();
872 
873  // determine the predicted mean and resolution
874  double mean = getMean(bg);
875  double sigma = getSigma(mean, nhit, sin);
876 
877  predmean[pdgIter.getIndex()] = mean;
878  predsigma[pdgIter.getIndex()] = sigma;
879 
880  // fill the chi value for this particle type
881  if (sigma != 0) chi[pdgIter.getIndex()] = ((dedx - mean) / (sigma));
882  }
883 }
DataType Phi() const
The azimuth angle.
Definition: B2Vector3.h:136
DataType Z() const
access variable Z (= .at(2) without boundary check)
Definition: B2Vector3.h:420
DataType y() const
access variable Y (= .at(1) without boundary check)
Definition: B2Vector3.h:412
DataType x() const
access variable X (= .at(0) without boundary check)
Definition: B2Vector3.h:410
DataType Perp() const
The transverse component (R in cylindrical coordinate system).
Definition: B2Vector3.h:185
Container for likelihoods obtained by the CDC dE/dx PID (CDCDedxPIDModule).
Extract CDC dE/dx information from fitted tracks.
double I2D(double cosTheta, double I) const
hadron saturation parameterization part 1
double getSigma(double dedx, double nhit, double sin) const
calculate the predicted resolution using the parameterized resolution
double D2I(double cosTheta, double D) const
hadron saturation parameterization part 2
double getMean(double bg) const
calculate the predicted mean using the parameterized resolution
virtual ~CDCDedxPIDModule()
Destructor.
virtual void initialize() override
Initialize the module.
virtual void event() override
This method is called for each event.
void saveLookupLogl(double(&logl)[Const::ChargedStable::c_SetSize], double p, double dedx)
for all particles, save log-likelihood values into 'logl'.
void checkPDFs()
Check the pdfs for consistency everytime they change in the database.
virtual void terminate() override
End of the event processing.
double meanCurve(double *x, double *par, int version) const
parameterized beta-gamma curve for predicted means
void calculateMeans(double *mean, double *truncatedMean, double *truncatedMeanErr, const std::vector< double > &dedx) const
Save arithmetic and truncated mean for the 'dedx' values.
double sigmaCurve(double *x, double *par, int version) const
parameterized resolution for predictions
void saveChiValue(double(&chi)[Const::ChargedStable::c_SetSize], double(&predmean)[Const::ChargedStable::c_SetSize], double(&predres)[Const::ChargedStable::c_SetSize], double p, double dedx, double sin, int nhit) const
for all particles, save chi values into 'chi'.
Debug output for CDCDedxPID module.
Definition: CDCDedxTrack.h:25
Class containing the result of the unpacker in raw data and the result of the digitizer in simulation...
Definition: CDCHit.h:40
short getTDCCount() const
Getter for TDC count.
Definition: CDCHit.h:219
unsigned short getADCCount() const
Getter for integrated charge.
Definition: CDCHit.h:230
unsigned short getISuperLayer() const
Getter for iSuperLayer.
Definition: CDCHit.h:184
unsigned short getILayer() const
Getter for iLayer.
Definition: CDCHit.h:172
This class is used to transfer CDC information to the track fit.
Definition: CDCRecoHit.h:32
const CDCHit * getCDCHit() const
get the pointer to the CDCHit object that was used to create this CDCRecoHit object.
Definition: CDCRecoHit.h:112
WireID getWireID() const
Getter for WireID object.
Definition: CDCRecoHit.h:49
The Class for CDC Geometry Parameters.
const TVector3 wireBackwardPosition(int layerId, int cellId, EWirePosition set=c_Base) const
Returns the backward position of the input sense wire.
const TVector3 wireForwardPosition(int layerId, int cellId, EWirePosition set=c_Base) const
Returns the forward position of the input sense wire.
unsigned nWiresInLayer(int layerId) const
Returns wire numbers in a layer.
const double * innerRadiusWireLayer() const
Returns an array of inner radius of wire layers.
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
const double * outerRadiusWireLayer() const
Returns an array of outer radius of wire layers.
This class simply assumes a linear translation through (0,0)
float getCharge(unsigned short adcCount, const WireID &, bool, float, float)
just multiply with the conversion factor and return.
Translator mirroring the realistic Digitization.
double getDriftLength(unsigned short tdcCount, const WireID &wireID=WireID(), double timeOfFlightEstimator=0, bool leftRight=false, double z=0, double alpha=0, double theta=static_cast< double >(TMath::Pi()/2.), unsigned short adcCount=0) override
Get Drift length.
double getDriftLengthResolution(double driftLength, const WireID &wireID=WireID(), bool leftRight=false, double z=0, double alpha=0, double=static_cast< double >(TMath::Pi()/2.)) override
Get position resolution^2 corresponding to the drift length from getDriftLength of this class.
Provides a type-safe way to pass members of the chargedStableSet set.
Definition: Const.h:470
static const unsigned int c_SetSize
Number of elements (for use in array bounds etc.)
Definition: Const.h:496
A set of ParticleType objects, with defined order.
Definition: Const.h:398
const ParticleType & at(unsigned int index) const
Return particle at given index, or end() if out of range.
Definition: Const.h:430
int getPDGCode() const
PDG code.
Definition: Const.h:354
static const ParticleSet chargedStableSet
set of charged stable particles
Definition: Const.h:499
static const ChargedStable pion
charged pion particle
Definition: Const.h:542
static const ChargedStable proton
proton particle
Definition: Const.h:544
static const ChargedStable kaon
charged kaon particle
Definition: Const.h:543
A class to hold the geometry of a cell.
Definition: LineHelper.h:186
A collection of classes that are useful for making a simple path length correction to the dE/dx measu...
Definition: LineHelper.h:29
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
float getMass() const
Return the particle mass in GeV.
Definition: MCParticle.h:135
bool hasStatus(unsigned short int bitmask) const
Return if specific status bit is set.
Definition: MCParticle.h:129
TVector3 getMomentum() const
Return momentum.
Definition: MCParticle.h:198
int m_pdg
PDG-Code of the particle.
Definition: MCParticle.h:525
int getPDG() const
Return PDG code of particle.
Definition: MCParticle.h:112
Base class for Modules.
Definition: Module.h:72
This class stores additional information to every CDC/SVD/PXD hit stored in a RecoTrack.
OriginTrackFinder getFoundByTrackFinder() const
Get which track finder has found the track.
This is the Reconstruction Event-Data Model Track.
Definition: RecoTrack.h:76
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:536
const std::vector< genfit::TrackPoint * > & getHitPointsWithMeasurement() const
Return a list of measurements and track points, which can be used e.g. to extrapolate....
Definition: RecoTrack.h:606
const std::vector< genfit::AbsTrackRep * > & getRepresentations() const
Return a list of track representations. You are not allowed to modify or delete them!
Definition: RecoTrack.h:553
RecoHitInformation * getRecoHitInformation(HitType *hit) const
Return the reco hit information for a generic hit from the storeArray.
Definition: RecoTrack.h:307
Values of the result of a track fit with a given particle hypothesis.
short getChargeSign() const
Return track charge (1 or -1).
double getCotTheta() const
Getter for tanLambda with CDF naming convention.
TVector3 getMomentum() const
Getter for vector of momentum at closest approach of track in r/phi projection.
The Unit class.
Definition: Unit.h:40
Class to identify a wire inside the CDC.
Definition: WireID.h:34
unsigned short getIWire() const
Getter for wire within the layer.
Definition: WireID.h:145
This class collects all information needed and produced by a specific AbsFitter and is specific to on...
Definition: AbsFitterInfo.h:42
Contains the measurement and covariance in raw detector coordinates.
Exception class for error handling in GENFIT (provides storage for diagnostic information)
Definition: Exception.h:48
bool isTrackPruned() const
Has the track been pruned after the fit?
Definition: FitStatus.h:116
Collects information needed and produced by a AbsKalmanFitter implementations and is specific to one ...
std::vector< double > getWeights() const
Get weights of measurements.
#StateOnPlane with additional covariance matrix.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
MCParticle * getMother() const
Returns a pointer to the mother particle.
Definition: MCParticle.h:582
Abstract base class for different kinds of events.