11 #include <reconstruction/modules/CDCDedxPID/CDCDedxPIDModule.h>
13 #include <reconstruction/dataobjects/DedxConstants.h>
14 #include <reconstruction/modules/CDCDedxPID/LineHelper.h>
16 #include <framework/gearbox/Const.h>
18 #include <cdc/dataobjects/CDCHit.h>
19 #include <cdc/dataobjects/CDCRecoHit.h>
21 #include <cdc/translators/LinearGlobalADCCountTranslator.h>
22 #include <cdc/translators/RealisticTDCCountTranslator.h>
24 #include <cdc/geometry/CDCGeometryPar.h>
25 #include <tracking/dataobjects/RecoHitInformation.h>
27 #include <genfit/AbsTrackRep.h>
28 #include <genfit/Exception.h>
29 #include <genfit/MaterialEffects.h>
30 #include <genfit/KalmanFitterInfo.h>
47 setPropertyFlags(c_ParallelProcessingCertified);
50 setDescription(
"Extract dE/dx and corresponding log-likelihood from fitted tracks and hits in the CDC.");
53 addParam(
"usePrediction", m_usePrediction,
54 "Use parameterized means and resolutions to determine PID values. If false, lookup table PDFs are used.",
true);
55 addParam(
"removeLowest", m_removeLowest,
56 "Portion of events with low dE/dx that should be discarded",
double(0.05));
57 addParam(
"removeHighest", m_removeHighest,
58 "Portion of events with high dE/dx that should be discarded",
double(0.25));
59 addParam(
"useBackHalfCurlers", m_backHalfCurlers,
60 "Whether to use the back half of curlers",
false);
61 addParam(
"enableDebugOutput", m_enableDebugOutput,
62 "Option to write out debugging information to CDCDedxTracks (DataStore objects).",
true);
63 addParam(
"useIndividualHits", m_useIndividualHits,
64 "If using lookup table PDFs, include PDF value for each hit in likelihood. If false, the truncated mean of dedx values will be used.",
66 addParam(
"ignoreMissingParticles", m_ignoreMissingParticles,
67 "Ignore particles for which no PDFs are found",
false);
68 addParam(
"trackLevel", m_trackLevel,
69 "ONLY USEFUL FOR MC: Use track-level MC. If false, use hit-level MC",
true);
70 addParam(
"onlyPrimaryParticles", m_onlyPrimaryParticles,
71 "ONLY USEFUL FOR MC: Only save data for primary particles",
false);
78 if (!m_DBDedxPDFs) B2FATAL(
"No Dedx pdfs found in database");
81 double xMin, xMax, yMin, yMax;
83 xMin = xMax = yMin = yMax = 0.0;
84 for (
unsigned int iPart = 0; iPart < 6; iPart++) {
86 const TH2F* pdf = m_DBDedxPDFs->getCDCPDF(iPart, !m_useIndividualHits);
88 if (pdf->GetEntries() == 0) {
89 if (m_ignoreMissingParticles)
91 B2FATAL(
"Couldn't find PDF for PDG " << pdgCode);
95 const double epsFactor = 1e-5;
96 if (nBinsX == -1 and nBinsY == -1) {
97 nBinsX = pdf->GetNbinsX();
98 nBinsY = pdf->GetNbinsY();
99 xMin = pdf->GetXaxis()->GetXmin();
100 xMax = pdf->GetXaxis()->GetXmax();
101 yMin = pdf->GetYaxis()->GetXmin();
102 yMax = pdf->GetYaxis()->GetXmax();
103 }
else if (nBinsX != pdf->GetNbinsX()
104 or nBinsY != pdf->GetNbinsY()
105 or fabs(xMin - pdf->GetXaxis()->GetXmin()) > epsFactor * xMax
106 or fabs(xMax - pdf->GetXaxis()->GetXmax()) > epsFactor * xMax
107 or fabs(yMin - pdf->GetYaxis()->GetXmin()) > epsFactor * yMax
108 or fabs(yMax - pdf->GetYaxis()->GetXmax()) > epsFactor * yMax) {
109 B2FATAL(
"PDF for PDG " << pdgCode <<
" has binning/dimensions differing from previous PDF.");
118 m_tracks.isRequired();
119 m_recoTracks.isRequired();
122 m_mcparticles.isOptional();
123 m_tracks.optionalRelationTo(m_mcparticles);
126 if (m_enableDebugOutput) {
127 m_dedxTracks.registerInDataStore();
128 m_tracks.registerRelationTo(m_dedxTracks);
132 m_dedxLikelihoods.registerInDataStore();
133 m_tracks.registerRelationTo(m_dedxLikelihoods);
135 m_DBDedxPDFs.addCallback([
this]() { checkPDFs(); });
139 m_nLayerWires[0] = 1280;
140 for (
int i = 1; i < 9; ++i) {
141 m_nLayerWires[i] = m_nLayerWires[i - 1] + 6 * (160 + (i - 1) * 32);
145 if (!m_DBMeanPars || m_DBMeanPars->getSize() == 0) {
146 B2WARNING(
"No dE/dx mean parameters!");
147 for (
int i = 0; i < 15; ++i)
148 m_meanpars.push_back(1.0);
149 }
else m_meanpars = m_DBMeanPars->getMeanPars();
151 if (!m_DBSigmaPars || m_DBSigmaPars->getSize() == 0) {
152 B2WARNING(
"No dE/dx sigma parameters!");
153 for (
int i = 0; i < 12; ++i)
154 m_sigmapars.push_back(1.0);
155 }
else m_sigmapars = m_DBSigmaPars->getSigmaPars();
158 if (!m_DBHadronCor || m_DBHadronCor->getSize() == 0) {
159 B2WARNING(
"No hadron correction parameters!");
160 for (
int i = 0; i < 4; ++i)
161 m_hadronpars.push_back(0.0);
162 m_hadronpars.push_back(1.0);
163 }
else m_hadronpars = m_DBHadronCor->getHadronPars();
168 if (!genfit::MaterialEffects::getInstance()->isInitialized()) {
169 B2FATAL(
"Need to have SetupGenfitExtrapolationModule in path before this one.");
182 const int numMCParticles = m_mcparticles.getEntries();
193 m_dedxTracks.clear();
194 m_dedxLikelihoods.clear();
197 for (
const auto& track : m_tracks) {
198 std::shared_ptr<CDCDedxTrack> dedxTrack = std::make_shared<CDCDedxTrack>();
205 B2WARNING(
"No related fit for track ...");
209 if ((m_enableDebugOutput or m_onlyPrimaryParticles) and numMCParticles != 0) {
222 dedxTrack->
m_motherPDG = mother ? mother->getPDG() : 0;
224 const TVector3 trueMomentum = mcpart->
getMomentum();
225 dedxTrack->
m_pTrue = trueMomentum.Mag();
229 dedxTrack->
m_pdg = -999;
233 const TVector3& trackMom = fitResult->
getMomentum();
234 dedxTrack->
m_p = trackMom.Mag();
235 bool nomom = (dedxTrack->
m_p != dedxTrack->
m_p);
236 double costh = std::cos(std::atan(1 / fitResult->
getCotTheta()));
239 costh = trackMom.CosTheta();
248 B2WARNING(
"No related track for this fit...");
254 B2ERROR(
"GFTrack is pruned, please run CDCDedxPID only on unpruned tracks! Skipping this track.");
259 dedxTrack->
m_scale = (m_DBScaleFactor) ? m_DBScaleFactor->getScaleFactor() : 1.0;
262 dedxTrack->
m_runGain = (m_DBRunGain && m_usePrediction && numMCParticles == 0) ? m_DBRunGain->getRunGain() : 1.0;
265 dedxTrack->
m_cosCor = (m_DBCosineCor && m_usePrediction && numMCParticles == 0) ? m_DBCosineCor->getMean(costh) : 1.0;
269 if ((abs(costh + 0.860) < 0.010) || (abs(costh - 0.955) <= 0.005))isEdge =
true;
270 dedxTrack->
m_cosEdgeCor = (m_DBCosEdgeCor && m_usePrediction && numMCParticles == 0
271 && isEdge) ? m_DBCosEdgeCor->getMean(costh) : 1.0;
274 double layerdE = 0.0;
275 double layerdx = 0.0;
277 int nhitscombined = 0;
278 int wirelongesthit = 0;
279 double longesthit = 0;
285 const std::vector< genfit::AbsTrackRep* >& gftrackRepresentations = recoTrack->
getRepresentations();
287 for (std::vector< genfit::TrackPoint* >::const_iterator tp = gftrackPoints.begin();
288 tp != gftrackPoints.end(); ++tp) {
294 if (!cdcRecoHit)
continue;
296 if (!cdcHit)
continue;
302 B2DEBUG(29,
"No fitter info, skipping...");
313 int foundByTrackFinder = 0;
318 double weightPionHypo = 0;
319 double weightProtHypo = 0;
320 double weightKaonHypo = 0;
322 for (std::vector<genfit::AbsTrackRep* >::const_iterator trep = gftrackRepresentations.begin();
323 trep != gftrackRepresentations.end(); ++trep) {
324 const int pdgCode = TMath::Abs((*trep)->getPDG());
330 if (kalmanFitterInfo == NULL) {
336 std::vector<double> weights = kalmanFitterInfo->
getWeights();
337 const double maxWeight = weights[0] > weights[1] ? weights[0] : weights[1];
338 if (pdgCode ==
Const::pion.getPDGCode()) weightPionHypo = maxWeight;
339 else if (pdgCode ==
Const::kaon.getPDGCode()) weightKaonHypo = maxWeight;
340 else if (pdgCode ==
Const::proton.getPDGCode()) weightProtHypo = maxWeight;
345 int currentLayer = (superlayer == 0) ? layer : (8 + (superlayer - 1) * 6 + layer);
346 int nextLayer = currentLayer;
349 const int iwire = (superlayer == 0) ? 160 * layer + wire : m_nLayerWires[superlayer - 1] + (160 + 32 *
350 (superlayer - 1)) * layer + wire;
353 const bool lastHit = (tp + 1 == gftrackPoints.end());
354 bool lastHitInCurrentLayer = lastHit;
360 if (!nextcdcRecoHit || !(cdcRecoHit->
getCDCHit()) || !((*(tp + 1))->getFitterInfo())) {
361 lastHitInCurrentLayer =
true;
365 const int nextILayer = nextcdcHit->
getILayer();
367 nextLayer = (nextSuperlayer == 0) ? nextILayer : (8 + (nextSuperlayer - 1) * 6 + nextILayer);
368 lastHitInCurrentLayer = (nextLayer != currentLayer);
374 const TVector3 wireDir = (wirePosB - wirePosF).
Unit();
382 double topHeight = outer - wirePosF.Perp();
383 double bottomHeight = wirePosF.Perp() - inner;
384 double cellHeight = topHeight + bottomHeight;
385 double topHalfWidth = M_PI * outer / nWires;
386 double bottomHalfWidth = M_PI * inner / nWires;
387 double cellHalfWidth = M_PI * wirePosF.Perp() / nWires;
402 const TVector3& pocaMom = mop.getMom();
403 if (tp == gftrackPoints.begin() || cdcMom == 0) {
404 cdcMom = pocaMom.Mag();
405 dedxTrack->
m_pCDC = cdcMom;
408 dedxTrack->
m_p = cdcMom;
417 B2Vector3D pocaOnWire = mop.getPlane()->getO();
420 B2Vector3D B2WireDoca = fittedPoca - pocaOnWire;
423 double doca = B2WireDoca.
Perp();
424 double phidiff = fittedPoca.
Phi() - pocaOnWire.Phi();
427 if (phidiff > -3.1416 && (phidiff < 0 || phidiff > 3.1416)) doca *= -1;
430 const double px = pocaMom.x();
431 const double py = pocaMom.y();
432 const double wx = pocaOnWire.x();
433 const double wy = pocaOnWire.y();
434 const double cross = wx * py - wy * px;
435 const double dot = wx * px + wy * py;
436 double entAng = atan2(cross, dot);
439 double cellR = 2 * cellHalfWidth / cellHeight;
441 if (std::abs(2 * atan(1) - std::abs(entAng)) < 0.01)tana = 100 * (entAng / std::abs(entAng));
442 else tana = std::tan(entAng);
443 double docaRS = doca * std::sqrt((1 + cellR * cellR * tana * tana) / (1 + tana * tana));
444 double entAngRS = std::atan(tana / cellR);
448 int adcCount = (m_DBNonlADC && m_usePrediction
449 && numMCParticles == 0) ? m_DBNonlADC->getCorrectedADC(adcbaseCount, currentLayer) : adcbaseCount;
451 double hitCharge = translator.
getCharge(adcCount, wireID,
false, pocaOnWire.Z(), pocaMom.Phi());
455 double dadcCount = 1.0 * adcCount;
459 double driftDRealistic = realistictdc.
getDriftLength(driftT, wireID, 0,
true, pocaOnWire.Z(), pocaMom.Phi(), pocaMom.Theta());
460 double driftDRealisticRes = realistictdc.
getDriftLengthResolution(driftDRealistic, wireID,
true, pocaOnWire.Z(), pocaMom.Phi(),
464 double celldx = c.dx(doca, entAng);
467 double wiregain = (m_DBWireGains && m_usePrediction && numMCParticles == 0) ? m_DBWireGains->getWireGain(iwire) : 1.0;
470 double normDocaRS = docaRS / cellHalfWidth;
471 double twodcor = (m_DB2DCell && m_usePrediction
472 && numMCParticles == 0) ? m_DB2DCell->getMean(currentLayer, normDocaRS, entAngRS) : 1.0;
475 double onedcor = (m_DB1DCell && m_usePrediction && numMCParticles == 0) ? m_DB1DCell->getMean(currentLayer, entAngRS) : 1.0;
485 if (correction != 0) dadcCount = dadcCount / correction;
488 double cellDedx = (dadcCount / celldx);
491 if (nomom) cellDedx *= std::sin(std::atan(1 / fitResult->
getCotTheta()));
492 else cellDedx *= std::sin(trackMom.Theta());
494 if (m_enableDebugOutput)
495 dedxTrack->
addHit(wire, iwire, currentLayer, doca, docaRS, entAng, entAngRS,
496 adcCount, adcbaseCount, hitCharge, celldx, cellDedx, cellHeight, cellHalfWidth, driftT,
497 driftDRealistic, driftDRealisticRes, wiregain, twodcor, onedcor,
498 foundByTrackFinder, weightPionHypo, weightKaonHypo, weightProtHypo);
503 if (correction != 0) {
505 layerdE += dadcCount;
508 if (celldx > longesthit) {
510 wirelongesthit = iwire;
516 B2WARNING(
"Track: " << mtrack <<
": genfit::MeasuredStateOnPlane exception...");
521 if (lastHitInCurrentLayer) {
523 double totalDistance;
524 if (nomom) totalDistance = layerdx / std::sin(std::atan(1 / fitResult->
getCotTheta()));
525 else totalDistance = layerdx / std::sin(trackMom.Theta());
526 double layerDedx = layerdE / totalDistance;
530 dedxTrack->
addDedx(nhitscombined, wirelongesthit, currentLayer, totalDistance, layerDedx);
532 if (m_useIndividualHits) {
539 if (!m_backHalfCurlers && nextLayer < currentLayer)
break;
551 if (dedxTrack->
m_lDedx.empty()) {
552 B2DEBUG(29,
"Found track with no hits, ignoring.");
556 const int numDedx = dedxTrack->
m_lDedx.size();
558 const int lowEdgeTrunc = int(numDedx * m_removeLowest + 0.51);
559 const int highEdgeTrunc = int(numDedx * (1 - m_removeHighest) + 0.51);
568 if (!m_useIndividualHits or m_enableDebugOutput) {
575 if (numMCParticles == 0)
581 if (numMCParticles != 0 && dedxTrack->
m_mcmass > 0 && dedxTrack->
m_pTrue != 0) {
585 dedxTrack->
m_simDedx = gRandom->Gaus(mean, sigma);
587 dedxTrack->
m_simDedx = gRandom->Gaus(mean, sigma);
595 if (!m_useIndividualHits)
602 if (m_usePrediction) {
604 pidvalues[pdgIter.getIndex()] = -0.5 * dedxTrack->
m_cdcChi[pdgIter.getIndex()] * dedxTrack->
m_cdcChi[pdgIter.getIndex()];
608 pidvalues[pdgIter.getIndex()] = dedxTrack->
m_cdcLogl[pdgIter.getIndex()];
613 track.addRelationTo(likelihoodObj);
615 if (m_enableDebugOutput) {
617 CDCDedxTrack* newCDCDedxTrack = m_dedxTracks.appendNew(*dedxTrack);
618 track.addRelationTo(newCDCDedxTrack);
627 B2DEBUG(29,
"CDCDedxPIDModule exiting");
631 const std::vector<double>& dedx)
const
635 std::vector<double> sortedDedx = dedx;
636 std::sort(sortedDedx.begin(), sortedDedx.end());
637 sortedDedx.erase(std::remove(sortedDedx.begin(), sortedDedx.end(), 0), sortedDedx.end());
638 sortedDedx.shrink_to_fit();
640 double truncatedMeanTmp = 0.0;
641 double meanTmp = 0.0;
642 double sumOfSquares = 0.0;
643 int numValuesTrunc = 0;
644 const int numDedx = sortedDedx.size();
647 const int lowEdgeTrunc = int(numDedx * m_removeLowest + 0.51);
648 const int highEdgeTrunc = int(numDedx * (1 - m_removeHighest) + 0.51);
649 for (
int i = 0; i < numDedx; i++) {
650 meanTmp += sortedDedx[i];
651 if (i >= lowEdgeTrunc and i < highEdgeTrunc) {
652 truncatedMeanTmp += sortedDedx[i];
653 sumOfSquares += sortedDedx[i] * sortedDedx[i];
661 if (numValuesTrunc != 0) {
662 truncatedMeanTmp /= numValuesTrunc;
664 truncatedMeanTmp = meanTmp;
668 *truncatedMean = truncatedMeanTmp;
670 if (numValuesTrunc > 1) {
671 *truncatedMeanErr = sqrt(sumOfSquares /
double(numValuesTrunc) - truncatedMeanTmp * truncatedMeanTmp) / double(
674 *truncatedMeanErr = 0;
681 const TH2F* pdf = m_DBDedxPDFs->getCDCPDF(0, !m_useIndividualHits);
682 const Int_t binX = pdf->GetXaxis()->FindFixBin(p);
683 const Int_t binY = pdf->GetYaxis()->FindFixBin(dedx);
686 pdf = m_DBDedxPDFs->getCDCPDF(iPart, !m_useIndividualHits);
687 if (pdf->GetEntries() == 0) {
688 B2WARNING(
"NO CDC PDFS...");
691 double probability = 0.0;
694 if (binX < 1 or binX > pdf->GetNbinsX()
695 or binY < 1 or binY > pdf->GetNbinsY()) {
696 probability = pdf->GetBinContent(binX, binY);
701 probability =
const_cast<TH2F*
>(pdf)->Interpolate(p, dedx);
704 if (probability != probability)
705 B2ERROR(
"probability NAN for a track with p=" << p <<
" and dedx=" << dedx);
708 if (probability == 0.0)
709 probability = m_useIndividualHits ? (1e-5) : (1e-3);
710 logl[iPart] += log(probability);
716 double absCosTheta = fabs(cosTheta);
717 double projection = pow(absCosTheta, m_hadronpars[3]) + m_hadronpars[2];
718 if (projection == 0) {
719 B2WARNING(
"Something wrong with dE/dx hadron constants!");
723 double chargeDensity = D / projection;
724 double numerator = 1 + m_hadronpars[0] * chargeDensity;
725 double denominator = 1 + m_hadronpars[1] * chargeDensity;
727 if (denominator == 0) {
728 B2WARNING(
"Something wrong with dE/dx hadron constants!");
732 double I = D * m_hadronpars[4] * numerator / denominator;
738 double absCosTheta = fabs(cosTheta);
739 double projection = pow(absCosTheta, m_hadronpars[3]) + m_hadronpars[2];
741 if (projection == 0 || m_hadronpars[4] == 0) {
742 B2WARNING(
"Something wrong with dE/dx hadron constants!");
746 double a = m_hadronpars[0] / projection;
747 double b = 1 - m_hadronpars[1] / projection * (I / m_hadronpars[4]);
748 double c = -1.0 * I / m_hadronpars[4];
750 if (b == 0 && a == 0) {
751 B2WARNING(
"both a and b coefficiants for hadron correction are 0");
755 double D = (a != 0) ? (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a) : -c / b;
757 B2WARNING(
"D is less 0! will try another solution");
758 D = (a != 0) ? (-b - sqrt(b * b + 4.0 * a * c)) / (2.0 * a) : -c / b;
760 B2WARNING(
"D is still less 0! just return uncorrectecd value");
776 f = par[1] * std::pow(std::sqrt(x[0] * x[0] + 1), par[3]) / std::pow(x[0], par[3]) *
777 (par[2] - par[5] * std::log(1 / x[0])) - par[4] + std::exp(par[6] + par[7] * x[0]);
778 else if (par[0] == 2)
779 f = par[1] * std::pow(x[0], 3) + par[2] * x[0] * x[0] + par[3] * x[0] + par[4];
780 else if (par[0] == 3)
781 f = -1.0 * par[1] * std::log(par[4] + std::pow(1 / x[0], par[2])) + par[3];
790 double A = 0, B = 0, C = 0;
798 double x[1]; x[0] = bg;
803 parsA[0] = 1; parsB[0] = 2; parsC[0] = 3;
804 for (
int i = 0; i < 15; ++i) {
805 if (i < 7) parsA[i + 1] = m_meanpars[i];
806 else if (i < 11) parsB[i % 7 + 1] = m_meanpars[i];
807 else parsC[i % 11 + 1] = m_meanpars[i];
811 double partA = meanCurve(x, parsA, 0);
812 double partB = meanCurve(x, parsB, 0);
813 double partC = meanCurve(x, parsC, 0);
815 return (A * partA + B * partB + C * partC);
826 f = par[1] + par[2] * x[0];
827 }
else if (par[0] == 2) {
828 f = par[1] * std::pow(x[0], 4) + par[2] * std::pow(x[0], 3) +
829 par[3] * x[0] * x[0] + par[4] * x[0] + par[5];
839 if (nhit < 5) nhit = 5;
840 if (sin > 0.99) sin = 0.99;
847 dedxpar[0] = 1; nhitpar[0] = 2; sinpar[0] = 2;
848 for (
int i = 0; i < 5; ++i) {
849 if (i < 2) dedxpar[i + 1] = m_sigmapars[i];
850 nhitpar[i + 1] = m_sigmapars[i + 2];
851 sinpar[i + 1] = m_sigmapars[i + 7];
856 double corDedx = sigmaCurve(x, dedxpar, 0);
858 double corNHit = sigmaCurve(x, nhitpar, 0);
859 if (nhit > 42) corNHit = 1.0;
861 double corSin = sigmaCurve(x, sinpar, 0);
863 return (corDedx * corSin * corNHit);
868 double sin,
int nhit)
const
873 double bg = p / pdgIter.getMass();
876 double mean = getMean(bg);
877 double sigma = getSigma(mean, nhit, sin);
879 predmean[pdgIter.getIndex()] = mean;
880 predsigma[pdgIter.getIndex()] = sigma;
883 if (sigma != 0) chi[pdgIter.getIndex()] = ((dedx - mean) / (sigma));