Belle II Software  release-05-02-19
ECLChargedPIDModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2013 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Guglielmo De Nardo (denardo@na.infn.it) *
7  * Marco Milesi (marco.milesi@unimelb.edu.au) *
8  * *
9  * This software is provided "as is" without any warranty. *
10  **************************************************************************/
11 
12 #include <math.h>
13 #include <algorithm>
14 #include "TMath.h"
15 
16 #include <ecl/modules/eclChargedPID/ECLChargedPIDModule.h>
17 
18 using namespace Belle2;
19 
20 REG_MODULE(ECLChargedPID)
21 
22 
24 {
25  setDescription("The module implements charged particle identification using ECL-related observables. For each Track matched with a suitable ECLShower, likelihoods for each particle hypothesis are obtained from pdfs stored in a conditions database payload, and then get stored in an ECLPidLikelihood object. The dimensionality of the likelihood depends on how many variables are stored in the payload. The baseline method could be a simple univariate likelihood based on E/p PDFs, but it could be extended to include more ECL quantitites (e.g. shower shape variables, w/ proper decorrelation).");
26 
27  setPropertyFlags(c_ParallelProcessingCertified);
28 
29  addParam("applyClusterTimingSel",
30  m_applyClusterTimingSel,
31  "Set true if you want to apply a abs(clusterTiming)/clusterTimingError<1 cut on clusters. This cut is optimised to achieve 99% timing efficiency for true photons from the IP.",
32  bool(false));
33 }
34 
35 
37 
38 
40 {
41  m_eventMetaData.isRequired();
42 
43  m_eclPidLikelihoods.registerInDataStore();
44  m_tracks.registerRelationTo(m_eclPidLikelihoods);
45 }
46 
47 
49 {
50  if (!m_pdfs) { B2FATAL("No ECL charged PID PDFs payload found in database!"); }
51 }
52 
53 
55 {
56  m_pdfs.addCallback([this]() { checkPdfsDB(); });
57  checkPdfsDB();
58 }
59 
60 
62 {
63 
64  for (const auto& track : m_tracks) {
65 
66  // Don't forget to clear variable map before a track gets processed!
67  m_variables.clear();
68 
69  // Load the pion fit hypothesis or the hypothesis which is the closest in mass to a pion
70  // (the tracking will not always successfully fit with a pion hypothesis).
71  const TrackFitResult* fitRes = track.getTrackFitResultWithClosestMass(Const::pion);
72  if (fitRes == nullptr) continue;
73  const auto relShowers = track.getRelationsTo<ECLShower>();
74  if (relShowers.size() == 0) continue;
75 
76  const double p = fitRes->getMomentum().Mag();
77  const double theta = fitRes->getMomentum().Theta();
78  const auto charge = fitRes->getChargeSign();
79 
80  double shEnergy(0.0), maxEnergy(0.0);
81 
82  const ECLShower* mostEnergeticShower = nullptr;
83 
84  for (const auto& eclShower : relShowers) {
85 
86  if (eclShower.getHypothesisId() != ECLShower::c_nPhotons) continue;
88  if (abs(eclShower.getTime()) > eclShower.getDeltaTime99()) continue;
89  }
90 
91  shEnergy = eclShower.getEnergy();
92  if (shEnergy > maxEnergy) {
93  maxEnergy = shEnergy;
94  mostEnergeticShower = &eclShower;
95  }
96 
97  }
98 
99  double showerTheta = (mostEnergeticShower) ? mostEnergeticShower->getTheta() : -999.0;
100  int showerReg = (mostEnergeticShower) ? mostEnergeticShower->getDetectorRegion() : -1;
101 
102  // These are the variables that can be used to extract PDF templates for the likelihood / for the MVA training.
103  m_variables[ECLChargedPidPDFs::InputVar::c_E1E9] = (mostEnergeticShower) ? mostEnergeticShower->getE1oE9() : -1.0;
104  m_variables[ECLChargedPidPDFs::InputVar::c_E9E21] = (mostEnergeticShower) ? mostEnergeticShower->getE9oE21() : -1.0;
105  m_variables[ECLChargedPidPDFs::InputVar::c_E] = (mostEnergeticShower) ? maxEnergy : -1.0;
106  m_variables[ECLChargedPidPDFs::InputVar::c_EoP] = (mostEnergeticShower) ? maxEnergy / p : -1.0;
107  m_variables[ECLChargedPidPDFs::InputVar::c_Z40] = (mostEnergeticShower) ? mostEnergeticShower->getAbsZernike40() : -999.0;
108  m_variables[ECLChargedPidPDFs::InputVar::c_Z51] = (mostEnergeticShower) ? mostEnergeticShower->getAbsZernike51() : -999.0;
109  m_variables[ECLChargedPidPDFs::InputVar::c_ZMVA] = (mostEnergeticShower) ? mostEnergeticShower->getZernikeMVA() : -999.0;
110  m_variables[ECLChargedPidPDFs::InputVar::c_PSDMVA] = (mostEnergeticShower) ? mostEnergeticShower->getPulseShapeDiscriminationMVA() :
111  -999.0;
112  m_variables[ECLChargedPidPDFs::InputVar::c_DeltaL] = (mostEnergeticShower) ? mostEnergeticShower->getTrkDepth() : -1.0;
113  m_variables[ECLChargedPidPDFs::InputVar::c_LAT] = (mostEnergeticShower) ? mostEnergeticShower->getLateralEnergy() : -999.0;
114 
115  B2DEBUG(20, "EVENT: " << m_eventMetaData->getEvent());
116  B2DEBUG(20, "-------------------------------");
117  B2DEBUG(20, "TRACK properties:");
118  B2DEBUG(20, "p = " << p << " [GeV/c]");
119  B2DEBUG(30, "theta = " << theta << " [rad]");
120  B2DEBUG(20, "charge = " << charge);
121  B2DEBUG(20, "-------------------------------");
122  B2DEBUG(20, "SHOWER properties:");
123  B2DEBUG(20, "showerTheta = " << showerTheta << " [rad], showerReg = " << showerReg);
124  for (const auto& [varid, var] : m_variables) {
125  B2DEBUG(30, "varid: " << static_cast<unsigned int>(varid) << " = " << var);
126  }
127  B2DEBUG(20, "-------------------------------");
128 
129  // For tracks w/:
130  // -) A matched shower
131  // -) Shower is in the tracker acceptance (reg != 0)
132  // -) Well defined charge (+/-1),
133  // get the pdf value for each charged particle hypothesis,
134  // and store a ECLPidLikelihood data object.
135  if (mostEnergeticShower && showerReg && std::abs(charge)) {
136 
137  float likelihoods[Const::ChargedStable::c_SetSize];
138  // Initialise PDF value.
139  // This will correspond to a LogL = 0.0 if unchanged.
140  double pdfval(0.0);
141 
142  // Order of loop is defined in UnitConst.cc: e, mu, pi, K, p, d
143  unsigned int hypo_idx(-1);
144  for (const auto& hypo : Const::chargedStableSet) {
145 
146  hypo_idx = hypo.getIndex();
147 
148  auto pdgId = hypo.getPDGCode();
149 
150  B2DEBUG(20, "\n\thypo[" << hypo_idx << "] = " << pdgId << ", hypo[" << hypo_idx << "] * reco_charge = " << pdgId * charge);
151 
152  // Retrieve the list of enum ids for the input variables from the payload.
153  auto varids = m_pdfs->getVars(pdgId, charge, p, showerTheta);
154 
155  // Fill the vector w/ the values taken from the module map.
156  std::vector<double> variables;
157  for (const auto& varid : *varids) {
158  variables.push_back(m_variables.at(varid));
159  }
160 
161  // Transform the input variables via gaussianisation+decorrelation only if necessary.
162  if (m_pdfs->doVarsTransfo()) {
163  transfoGaussDecorr(pdgId, charge, p, showerTheta, variables);
164  }
165 
166  // Get the PDF templates for the various observables, and multiply the PDF values for this candidate.
167  // If more than 1 observable is used, this assumes they are independent
168  // (or at least linear correlations have been removed by suitably transforming the inputs...).
169  double prod(1.0);
170  double ipdfval;
171  for (unsigned int idx(0); idx < variables.size(); idx++) {
172 
173  auto var = variables.at(idx);
174  auto varid = varids->at(idx);
175 
176  const TF1* pdf = m_pdfs->getPdf(pdgId, charge, p, showerTheta, varid);
177  if (pdf) {
178 
179  ipdfval = getPdfVal(var, pdf);
180 
181  B2DEBUG(30, "\t\tL(" << pdgId * charge << ") = " << prod
182  << " * pdf(varid: " << static_cast<unsigned int>(varid) << ") = "
183  << prod << " * " << ipdfval
184  << " = " << prod * ipdfval);
185 
186  prod *= ipdfval;
187  }
188  }
189 
190  if (prod != 1.0) {
191  pdfval = prod;
192  }
193 
194  B2DEBUG(20, "\tL(" << pdgId * charge << ") = " << pdfval);
195 
196  likelihoods[hypo_idx] = (std::isnormal(pdfval) && pdfval > 0) ? log(pdfval) : c_dummyLogL;
197 
198  B2DEBUG(20, "\tlog(L(" << pdgId * charge << ")) = " << likelihoods[hypo_idx]);
199 
200  } // Loop over hypotheses
201 
202  const auto eclPidLikelihood = m_eclPidLikelihoods.appendNew(likelihoods);
203 
204  track.addRelationTo(eclPidLikelihood);
205 
206  } // Check on good tracks.
207 
208  } // End loop on tracks
209 }
210 
211 double ECLChargedPIDModule::getPdfVal(const double& x, const TF1* pdf)
212 {
213 
214  double y, xmin, xmax;
215  pdf->GetRange(xmin, xmax);
216 
217  if (x <= xmin) { y = pdf->Eval(xmin + 1e-9); }
218  else if (x >= xmax) { y = pdf->Eval(xmax - 1e-9); }
219  else { y = pdf->Eval(x); }
220 
221  return (y) ? y : 1e-9; // Shall we allow for 0? That translates in LogL = NaN...
222 }
223 
224 void ECLChargedPIDModule::transfoGaussDecorr(const unsigned int pdg, const int charge, const double& p, const double& theta,
225  std::vector<double>& variables)
226 {
227 
228  unsigned int nvars = variables.size();
229 
230  // Get the variable transformation settings for this hypo pdg, p, theta.
231  auto vts = m_pdfs->getVTS(pdg, charge, p, theta);
232 
233  B2DEBUG(30, "");
234  B2DEBUG(30, "\tclass path: " << vts->classPath);
235  B2DEBUG(30, "\tgbin = " << vts->gbin << ", (theta,p) = (" << vts->jth << "," << vts->ip << ")");
236  B2DEBUG(30, "\tnvars: " << nvars);
237 
238  auto varids = m_pdfs->getVars(pdg, charge, p, theta);
239 
240  for (unsigned int ivar(0); ivar < nvars; ivar++) {
241  unsigned int ndivs = vts->nDivisions[ivar];
242  B2DEBUG(30, "\tvarid: " << static_cast<unsigned int>(varids->at(ivar)) << " = " << variables.at(ivar) << ", nsteps = " << ndivs);
243  for (unsigned int jdiv(0); jdiv < ndivs; jdiv++) {
244  auto ij = linIndex(ivar, jdiv, vts->nDivisionsMax);
245  B2DEBUG(30, "\t\tx[" << ivar << "][" << jdiv << "] = x[" << ij << "] = " << vts->x[ij]);
246  B2DEBUG(30, "\t\tcumulDist[" << ivar << "][" << jdiv << "] = cumulDist[" << ij << "] = " << vts->cumulDist[ij]);
247  }
248  }
249 
250  std::vector<double> vtransfo_gauss;
251  vtransfo_gauss.reserve(nvars);
252 
253  double cumulant;
254  for (unsigned int ivar(0); ivar < nvars; ivar++) {
255 
256  int ndivs = vts->nDivisions[ivar];
257 
258  int jdiv = 0;
259  auto ij = linIndex(ivar, jdiv, vts->nDivisionsMax);
260  while (variables.at(ivar) > vts->x[ij]) {
261  jdiv++;
262  ij = linIndex(ivar, jdiv, vts->nDivisionsMax);
263  }
264 
265  if (jdiv < 0) { jdiv = 0; }
266  if (jdiv >= ndivs) { jdiv = ndivs - 1; }
267  int jnextdiv = jdiv;
268  if ((variables.at(ivar) > vts->x[ij] && jdiv != ndivs - 1) || jdiv == 0) {
269  jnextdiv++;
270  } else {
271  jnextdiv--;
272  }
273  auto ijnext = linIndex(ivar, jnextdiv, vts->nDivisionsMax);
274 
275  double dx = vts->x[ij] - vts->x[ijnext];
276  double dy = vts->cumulDist[ij] - vts->cumulDist[ijnext];
277  cumulant = vts->cumulDist[ij] + (variables.at(ivar) - vts->x[ijnext]) * dy / dx;
278 
279  cumulant = std::min(cumulant, 1.0 - 10e-10);
280  cumulant = std::max(cumulant, 10e-10);
281 
282  double maxErfInvArgRange = 0.99999999;
283  double arg = 2.0 * cumulant - 1.0;
284 
285  arg = std::min(maxErfInvArgRange, arg);
286  arg = std::max(-maxErfInvArgRange, arg);
287 
288  vtransfo_gauss.at(ivar) = c_sqrt2 * TMath::ErfInverse(arg);
289 
290  }
291  B2DEBUG(30, "\tSHOWER properties (Gaussian-transformed):");
292  for (unsigned int idx(0); idx < nvars; idx++) {
293  B2DEBUG(30, "\tvarid: " << static_cast<unsigned int>(varids->at(idx)) << " = " << vtransfo_gauss.at(idx));
294  }
295  B2DEBUG(30, "\t-------------------------------");
296 
297  std::vector<double> vtransfo_decorr;
298  vtransfo_decorr.reserve(nvars);
299 
300  for (unsigned int i(0); i < nvars; i++) {
301  double vartransfo(0);
302  for (unsigned int j(0); j < nvars; j++) {
303  auto ij = linIndex(i, j, nvars);
304  vartransfo += vtransfo_gauss[j] * vts->covMatrix[ij];
305  }
306  vtransfo_decorr.push_back(vartransfo);
307  }
308 
309  B2DEBUG(30, "\tSHOWER properties (Decorrelation-transformed):");
310  for (unsigned int idx(0); idx < nvars; idx++) {
311  B2DEBUG(30, "\tvarid: " << static_cast<unsigned int>(varids->at(idx)) << " = " << vtransfo_decorr.at(idx));
312  }
313  B2DEBUG(30, "\t-------------------------------");
314 
315  // Now modify the input variables vector.
316  for (unsigned int i(0); i < nvars; i++) {
317  variables[i] = vtransfo_decorr.at(i);
318  }
319 
320 }
321 
323 {
324 }
325 
327 {
328 }
Belle2::ECLShower::getLateralEnergy
double getLateralEnergy() const
Get Lateral Energy in Shower.
Definition: ECLShower.h:351
Belle2::ECLChargedPIDModule::m_tracks
StoreArray< Track > m_tracks
Array of Track objects.
Definition: ECLChargedPIDModule.h:130
Belle2::ECLChargedPidPDFs::InputVar::c_Z51
@ c_Z51
Zernike moment 51.
Belle2::Const::ChargedStable::c_SetSize
static const unsigned int c_SetSize
Number of elements (for use in array bounds etc.)
Definition: Const.h:491
Belle2::ECLChargedPidPDFs::InputVar::c_PSDMVA
@ c_PSDMVA
PSD MVA.
Belle2::ECLChargedPIDModule::m_eclPidLikelihoods
StoreArray< ECLPidLikelihood > m_eclPidLikelihoods
Array of ECLPidLikelihood objects.
Definition: ECLChargedPIDModule.h:135
Belle2::ECLShower::getTrkDepth
double getTrkDepth() const
path on track extrapolation to POCA to average cluster direction
Definition: ECLShower.h:361
Belle2::ECLShower::getTheta
double getTheta() const
Get Theta.
Definition: ECLShower.h:296
Belle2::ECLShower::getE9oE21
double getE9oE21() const
Get energy ratio E9oE21.
Definition: ECLShower.h:401
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::ECLChargedPIDModule::getPdfVal
double getPdfVal(const double &x, const TF1 *pdf)
Extract the PDF value for a given variable from the TF1 object.
Definition: ECLChargedPIDModule.cc:211
Belle2::ECLChargedPIDModule::linIndex
int linIndex(int i, int j, int m)
Get the index corresponding to element i,j in a linearised n*m array.
Definition: ECLChargedPIDModule.h:197
Belle2::ECLChargedPIDModule::initialize
virtual void initialize() override
Use this to initialize resources or memory your module needs.
Definition: ECLChargedPIDModule.cc:39
Belle2::Const::chargedStableSet
static const ParticleSet chargedStableSet
set of charged stable particles
Definition: Const.h:494
Belle2::ECLChargedPidPDFs::InputVar::c_ZMVA
@ c_ZMVA
Zernike MVA.
Belle2::ECLChargedPIDModule::~ECLChargedPIDModule
virtual ~ECLChargedPIDModule()
Destructor, use to clean up anything you created in the constructor.
Definition: ECLChargedPIDModule.cc:36
Belle2::ECLShower::getDetectorRegion
int getDetectorRegion() const
Return detector region: 0: below acceptance, 1: FWD, 2: BRL, 3: BWD, 11: FWDGAP, 13: BWDGAP.
Definition: ECLShower.h:464
Belle2::ECLShower::getPulseShapeDiscriminationMVA
double getPulseShapeDiscriminationMVA() const
Get shower hadron intensity.
Definition: ECLShower.h:411
Belle2::TrackFitResult
Values of the result of a track fit with a given particle hypothesis.
Definition: TrackFitResult.h:59
Belle2::ECLChargedPidPDFs::InputVar::c_EoP
@ c_EoP
E/p.
Belle2::ECLChargedPIDModule::checkPdfsDB
void checkPdfsDB()
Check the PDFs payload for consistency everytime they change in the database.
Definition: ECLChargedPIDModule.cc:48
Belle2::ECLShower::getZernikeMVA
double getZernikeMVA() const
Get Zernike MVA.
Definition: ECLShower.h:386
Belle2::ECLChargedPIDModule::beginRun
virtual void beginRun() override
Called once before a new run begins.
Definition: ECLChargedPIDModule.cc:54
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::ECLChargedPIDModule
The module implements charged particle identification using ECL-related observables.
Definition: ECLChargedPIDModule.h:72
Belle2::Const::pion
static const ChargedStable pion
charged pion particle
Definition: Const.h:535
Belle2::ECLChargedPIDModule::event
virtual void event() override
Called once for each event.
Definition: ECLChargedPIDModule.cc:61
Belle2::ECLChargedPIDModule::m_pdfs
DBObjPtr< ECLChargedPidPDFs > m_pdfs
Interface to get the DB payload for ECL charged PID PDFs.
Definition: ECLChargedPIDModule.h:140
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::ECLShower::getEnergy
double getEnergy() const
Get Energy.
Definition: ECLShower.h:286
Belle2::ECLChargedPidPDFs::InputVar::c_LAT
@ c_LAT
Lateral shower shape.
Belle2::ECLChargedPidPDFs::InputVar::c_E1E9
@ c_E1E9
E1/E9.
Belle2::ECLShower::getAbsZernike51
double getAbsZernike51() const
Get absolute value of Zernike moment 51.
Definition: ECLShower.h:381
Belle2::ECLChargedPidPDFs::InputVar::c_E9E21
@ c_E9E21
E9/E21.
Belle2::ECLChargedPIDModule::c_dummyLogL
static constexpr double c_dummyLogL
Dummy value of Log Likelihood for a particle hypothesis.
Definition: ECLChargedPIDModule.h:147
Belle2::ECLChargedPIDModule::m_variables
std::unordered_map< ECLChargedPidPDFs::InputVar, double > m_variables
Map to contain ECL shower observables.
Definition: ECLChargedPIDModule.h:169
Belle2::ECLShower::c_nPhotons
@ c_nPhotons
CR is split into n photons (N1)
Definition: ECLShower.h:54
Belle2::ECLShower::getAbsZernike40
double getAbsZernike40() const
Get absolute value of Zernike moment 40.
Definition: ECLShower.h:376
Belle2::ECLShower::getE1oE9
double getE1oE9() const
Get energy ratio E1oE9.
Definition: ECLShower.h:396
Belle2::ECLChargedPIDModule::c_sqrt2
static constexpr double c_sqrt2
Defintion of sqrt(2)
Definition: ECLChargedPIDModule.h:222
Belle2::ECLChargedPIDModule::terminate
virtual void terminate() override
Clean up anything you created in initialize().
Definition: ECLChargedPIDModule.cc:326
Belle2::ECLChargedPidPDFs::InputVar::c_Z40
@ c_Z40
Zernike moment 40.
Belle2::ECLChargedPIDModule::transfoGaussDecorr
void transfoGaussDecorr(const unsigned int pdg, const int charge, const double &p, const double &theta, std::vector< double > &variables)
Transform input variables according to:
Definition: ECLChargedPIDModule.cc:224
Belle2::ECLChargedPidPDFs::InputVar::c_E
@ c_E
Energy of maxE shower.
Belle2::ECLChargedPIDModule::endRun
virtual void endRun() override
Called once when a run ends.
Definition: ECLChargedPIDModule.cc:322
Belle2::ECLChargedPidPDFs::InputVar::c_DeltaL
@ c_DeltaL
DeltaL (track depth)
Belle2::ECLChargedPIDModule::m_applyClusterTimingSel
bool m_applyClusterTimingSel
Apply cluster timing selection.
Definition: ECLChargedPIDModule.h:152
Belle2::ECLChargedPIDModule::m_eventMetaData
StoreObjPtr< EventMetaData > m_eventMetaData
The event information.
Definition: ECLChargedPIDModule.h:186
Belle2::ECLShower
Class to store ECL Showers.
Definition: ECLShower.h:42