Belle II Software  release-08-01-10
PIDVariables.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 // Own header.
10 #include <analysis/variables/PIDVariables.h>
11 
12 #include <analysis/dataobjects/Particle.h>
13 #include <analysis/utility/ReferenceFrame.h>
14 #include <mdst/dataobjects/PIDLikelihood.h>
15 #include <mdst/dataobjects/TrackFitResult.h>
16 
17 // framework aux
18 #include <framework/logging/Logger.h>
19 #include <framework/utilities/Conversion.h>
20 #include <framework/gearbox/Const.h>
21 
22 // database
23 //#include <framework/database/DBObjPtr.h>
24 #include <analysis/dbobjects/PIDCalibrationWeight.h>
25 #include <analysis/utility/PIDCalibrationWeightUtil.h>
26 #include <analysis/utility/PIDNeuralNetwork.h>
27 
28 #include <boost/algorithm/string.hpp>
29 
30 #include <iostream>
31 #include <cmath>
32 
33 using namespace std;
34 
35 namespace Belle2 {
40  namespace Variable {
41 
42 
43 
44  //*************
45  // Utilities
46  //*************
47 
48  // converts Belle numbering scheme for charged final state particles
49  // to Belle II ChargedStable
50  Const::ChargedStable hypothesisConversion(const int hypothesis)
51  {
52  switch (hypothesis) {
53  case 0:
54  return Const::electron;
55  case 1:
56  return Const::muon;
57  case 2:
58  return Const::pion;
59  case 3:
60  return Const::kaon;
61  case 4:
62  return Const::proton;
63  }
64 
65  return Const::pion;
66  }
67 
68 
69  Const::PIDDetectorSet parseDetectors(const std::vector<std::string>& arguments)
70  {
71  Const::PIDDetectorSet result;
72  for (std::string val : arguments) {
73  boost::to_lower(val);
74  if (val == "all") return Const::PIDDetectors::set();
75  else if (val == "svd") result += Const::SVD;
76  else if (val == "cdc") result += Const::CDC;
77  else if (val == "top") result += Const::TOP;
78  else if (val == "arich") result += Const::ARICH;
79  else if (val == "ecl") result += Const::ECL;
80  else if (val == "klm") result += Const::KLM;
81  else B2ERROR("Unknown detector component: " << val);
82  }
83  return result;
84  }
85 
86  // Specialisation of valid detectors parser for ChargedBDT.
87  Const::PIDDetectorSet parseDetectorsChargedBDT(const std::vector<std::string>& arguments)
88  {
89  Const::PIDDetectorSet result;
90  for (std::string val : arguments) {
91  boost::to_lower(val);
92  if (val == "all") return Const::PIDDetectors::set();
93  else if (val == "ecl") result += Const::ECL;
94  else B2ERROR("Invalid detector component: " << val << " for charged BDT.");
95  }
96  return result;
97  }
98 
99  //*************
100  // Belle II
101  //*************
102 
103  // a "smart" variable:
104  // finds the global probability based on the PDG code of the input particle
105  double particleID(const Particle* p)
106  {
107  int pdg = abs(p->getPDGCode());
108  if (pdg == Const::electron.getPDGCode()) return electronID(p);
109  else if (pdg == Const::muon.getPDGCode()) return muonID(p);
110  else if (pdg == Const::pion.getPDGCode()) return pionID(p);
111  else if (pdg == Const::kaon.getPDGCode()) return kaonID(p);
112  else if (pdg == Const::proton.getPDGCode()) return protonID(p);
113  else if (pdg == Const::deuteron.getPDGCode()) return deuteronID(p);
114  else return Const::doubleNaN;
115  }
116 
117  Manager::FunctionPtr pidLogLikelihoodValueExpert(const std::vector<std::string>& arguments)
118  {
119  if (arguments.size() < 2) {
120  B2ERROR("Need at least two arguments to pidLogLikelihoodValueExpert");
121  return nullptr;
122  }
123  int pdgCode;
124  try {
125  pdgCode = Belle2::convertString<int>(arguments[0]);
126  } catch (std::invalid_argument& e) {
127  B2ERROR("First argument of pidLogLikelihoodValueExpert must be a PDG code");
128  return nullptr;
129  }
130  std::vector<std::string> detectors(arguments.begin() + 1, arguments.end());
131 
132  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
133  auto hypType = Const::ChargedStable(abs(pdgCode));
134 
135  auto func = [hypType, detectorSet](const Particle * part) -> double {
136  const PIDLikelihood* pid = part->getPIDLikelihood();
137  if (!pid)
138  return Const::doubleNaN;
139  // No information from any subdetector in the list
140  if (pid->getLogL(hypType, detectorSet) == 0)
141  return Const::doubleNaN;
142 
143  return pid->getLogL(hypType, detectorSet);
144  };
145  return func;
146  }
147 
148 
149 
150  Manager::FunctionPtr pidDeltaLogLikelihoodValueExpert(const std::vector<std::string>& arguments)
151  {
152  if (arguments.size() < 3) {
153  B2ERROR("Need at least three arguments to pidDeltaLogLikelihoodValueExpert");
154  return nullptr;
155  }
156  int pdgCodeHyp, pdgCodeTest;
157  try {
158  pdgCodeHyp = Belle2::convertString<int>(arguments[0]);
159  } catch (std::invalid_argument& e) {
160  B2ERROR("First argument of pidDeltaLogLikelihoodValueExpert must be a PDG code");
161  return nullptr;
162  }
163  try {
164  pdgCodeTest = Belle2::convertString<int>(arguments[1]);
165  } catch (std::invalid_argument& e) {
166  B2ERROR("Second argument of pidDeltaLogLikelihoodValueExpert must be a PDG code");
167  return nullptr;
168  }
169 
170  std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
171  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
172  auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
173  auto testType = Const::ChargedStable(abs(pdgCodeTest));
174 
175  auto func = [hypType, testType, detectorSet](const Particle * part) -> double {
176  const PIDLikelihood* pid = part->getPIDLikelihood();
177  if (!pid) return Const::doubleNaN;
178  // No information from any subdetector in the list
179  if (pid->getLogL(hypType, detectorSet) == 0)
180  return Const::doubleNaN;
181 
182  return (pid->getLogL(hypType, detectorSet) - pid->getLogL(testType, detectorSet));
183  };
184  return func;
185  }
186 
187 
188  Manager::FunctionPtr pidPairProbabilityExpert(const std::vector<std::string>& arguments)
189  {
190  if (arguments.size() < 3) {
191  B2ERROR("Need at least three arguments to pidPairProbabilityExpert");
192  return nullptr;
193  }
194  int pdgCodeHyp = 0, pdgCodeTest = 0;
195  try {
196  pdgCodeHyp = Belle2::convertString<int>(arguments[0]);
197  } catch (std::invalid_argument& e) {
198  B2ERROR("First argument of pidPairProbabilityExpert must be PDG code");
199  return nullptr;
200  }
201  try {
202  pdgCodeTest = Belle2::convertString<int>(arguments[1]);
203  } catch (std::invalid_argument& e) {
204  B2ERROR("Second argument of pidPairProbabilityExpert must be PDG code");
205  return nullptr;
206  }
207 
208  std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
209 
210  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
211  auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
212  auto testType = Const::ChargedStable(abs(pdgCodeTest));
213  auto func = [hypType, testType, detectorSet](const Particle * part) -> double {
214  const PIDLikelihood* pid = part->getPIDLikelihood();
215  if (!pid) return Const::doubleNaN;
216  // No information from any subdetector in the list
217  if (pid->getLogL(hypType, detectorSet) == 0)
218  return Const::doubleNaN;
219 
220  return pid->getProbability(hypType, testType, detectorSet);
221  };
222  return func;
223  }
224 
225 
226  Manager::FunctionPtr pidProbabilityExpert(const std::vector<std::string>& arguments)
227  {
228  if (arguments.size() < 2) {
229  B2ERROR("Need at least two arguments for pidProbabilityExpert");
230  return nullptr;
231  }
232  int pdgCodeHyp = 0;
233  try {
234  pdgCodeHyp = Belle2::convertString<int>(arguments[0]);
235  } catch (std::invalid_argument& e) {
236  B2ERROR("First argument of pidProbabilityExpert must be PDG code");
237  return nullptr;
238  }
239 
240  std::vector<std::string> detectors(arguments.begin() + 1, arguments.end());
241  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
242  auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
243 
244  // Placeholder for the priors
245  const unsigned int n = Const::ChargedStable::c_SetSize;
246  double frac[n];
247  for (double& i : frac) i = 1.0; // flat priors
248 
249  auto func = [hypType, frac, detectorSet](const Particle * part) -> double {
250  const PIDLikelihood* pid = part->getPIDLikelihood();
251  if (!pid) return Const::doubleNaN;
252  // No information from any subdetector in the list
253  if (pid->getLogL(hypType, detectorSet) == 0)
254  return Const::doubleNaN;
255 
256  return pid->getProbability(hypType, frac, detectorSet);
257  };
258  return func;
259  }
260 
261 
262  Manager::FunctionPtr pidMissingProbabilityExpert(const std::vector<std::string>& arguments)
263  {
264  if (arguments.size() < 1) {
265  B2ERROR("Need at least one argument to pidMissingProbabilityExpert");
266  return nullptr;
267  }
268 
269  std::vector<std::string> detectors(arguments.begin(), arguments.end());
270  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
271 
272  auto func = [detectorSet](const Particle * part) -> double {
273  const PIDLikelihood* pid = part->getPIDLikelihood();
274  if (!pid) return Const::doubleNaN;
275  if (not pid->isAvailable(detectorSet))
276  return 1;
277  else return 0;
278  };
279  return func;
280  }
281 
282  Manager::FunctionPtr pidWeightedLogLikelihoodValueExpert(const std::vector<std::string>& arguments)
283  {
284  if (arguments.size() < 3) {
285  B2ERROR("Need at least three arguments to pidWeightedLogLikelihoodValueExpert");
286  return nullptr;
287  }
288  std::string matrixName = arguments[0];
289 
290  int pdgCode;
291  try {
292  pdgCode = Belle2::convertString<int>(arguments[1]);
293  } catch (std::invalid_argument& e) {
294  B2ERROR("Second argument of pidWeightedLogLikelihoodValueExpert must be a PDG code");
295  return nullptr;
296  }
297  std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
298  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
299  auto hypType = Const::ChargedStable(abs(pdgCode));
300 
301  auto func = [hypType, detectorSet, matrixName](const Particle * part) -> double {
302  PIDCalibrationWeightUtil weightMatrix(matrixName);
303  const PIDLikelihood* pid = part->getPIDLikelihood();
304  if (!pid)
305  return Const::doubleNaN;
306  // No information from any subdetector in the list
307  if (pid->getLogL(hypType, detectorSet) == 0)
308  return Const::doubleNaN;
309 
310  const auto& frame = ReferenceFrame::GetCurrent();
311  auto mom = frame.getMomentum(part);
312  auto p = mom.P();
313  auto theta = mom.Theta();
314 
315  double LogL = 0;
316  for (const Const::EDetector& detector : Const::PIDDetectorSet::set())
317  {
318  if (detectorSet.contains(detector))
319  LogL += pid->getLogL(hypType, detector) * weightMatrix.getWeight(hypType.getPDGCode(), detector, p, theta);
320  }
321  return LogL;
322  };
323 
324  return func;
325  }
326 
327  Manager::FunctionPtr pidWeightedProbabilityExpert(const std::vector<std::string>& arguments)
328  {
329  if (arguments.size() < 3) {
330  B2ERROR("Need at least three arguments for pidWeightedProbabilityExpert");
331  return nullptr;
332  }
333  std::string matrixName = arguments[0];
334 
335  int pdgCodeHyp = 0;
336  try {
337  pdgCodeHyp = Belle2::convertString<int>(arguments[1]);
338  } catch (std::invalid_argument& e) {
339  B2ERROR("Second argument of pidWeightedProbabilityExpert must be PDG code");
340  return nullptr;
341  }
342 
343  std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
344  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
345  auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
346 
347  auto func = [hypType, detectorSet, matrixName](const Particle * part) -> double {
348  PIDCalibrationWeightUtil weightMatrix(matrixName);
349  const PIDLikelihood* pid = part->getPIDLikelihood();
350  if (!pid) return Const::doubleNaN;
351  // No information from any subdetector in the list
352  if (pid->getLogL(hypType, detectorSet) == 0)
353  return Const::doubleNaN;
354 
355  const auto& frame = ReferenceFrame::GetCurrent();
356  auto mom = frame.getMomentum(part);
357  auto p = mom.P();
358  auto theta = mom.Theta();
359 
360  std::vector<double> LogL(Const::ChargedStable::c_SetSize);
361  double LogL_max = 0;
362  bool hasMax = false;
363  for (const auto& pdgIter : Const::chargedStableSet)
364  {
365  const int index_pdg = pdgIter.getIndex();
366 
367  LogL[index_pdg] = 0;
368  for (const Const::EDetector& detector : Const::PIDDetectorSet::set()) {
369  if (detectorSet.contains(detector))
370  LogL[index_pdg] += pid->getLogL(pdgIter, detector) * weightMatrix.getWeight(pdgIter.getPDGCode(), detector, p, theta);
371  }
372 
373  if (!hasMax || (LogL[index_pdg] > LogL_max)) {
374  LogL_max = LogL[index_pdg];
375  hasMax = true;
376  }
377  }
378 
379  double norm = 0;
380  for (auto LogL_i : LogL)
381  norm += exp(LogL_i - LogL_max);
382 
383  if (norm > 0)
384  return exp(LogL[hypType.getIndex()] - LogL_max) / norm;
385  else
386  return -1;
387  };
388  return func;
389  }
390 
391 
392  Manager::FunctionPtr pidNeuralNetworkValueExpert(const std::vector<std::string>& arguments)
393  {
394  if (arguments.size() == 0) {
395  B2ERROR("Need pdg code for pidNeuralNetworkValueExpert");
396  return nullptr;
397  }
398  if (arguments.size() > 2) {
399  B2ERROR("pidNeuralNetworkValueExpert expects at most two arguments, i.e. the pdg code and the pidNeuralNetworkName");
400  return nullptr;
401  }
402  int pdgCode;
403  try {
404  pdgCode = abs(Belle2::convertString<int>(arguments[0]));
405  } catch (std::invalid_argument& e) {
406  B2ERROR("First argument of pidNeuralNetworkValueExpert must be a PDG code");
407  return nullptr;
408  }
409 
410  std::shared_ptr<PIDNeuralNetwork> neuralNetworkPtr;
411  if (arguments.size() == 2) {
412  std::string parameterSetName = arguments[1];
413  neuralNetworkPtr = std::make_shared<PIDNeuralNetwork>(parameterSetName);
414  } else {
415  neuralNetworkPtr = std::make_shared<PIDNeuralNetwork>();
416  }
417 
418  neuralNetworkPtr->hasPdgCode(pdgCode, true); // raise exception if pdg code is not predicted
419 
433  // build list of all input names
434  std::vector<std::string> inputsAllNames;
435  for (const Const::EDetector& detector : Const::PIDDetectorSet::set()) {
436  for (const auto& hypeType : Const::chargedStableSet) {
437  inputsAllNames.push_back("pidLogLikelihood_Of_" + std::to_string(abs(hypeType.getPDGCode())) + "_From_" + Const::parseDetectors(
438  detector));
439  }
440  }
441  inputsAllNames.push_back("momentum");
442  inputsAllNames.push_back("cosTheta");
443  inputsAllNames.push_back("phi");
444  inputsAllNames.push_back("charge");
445  const size_t inputsAllSize = inputsAllNames.size();
446 
447  //build mapping for neural-network inputs
448  std::vector<size_t> mapInputsNN2InputsAll; // mapInputsNN2InputsAll[<NN-index>] = <all-index>
449  for (const auto& inputName : neuralNetworkPtr->getInputNames()) {
450  const auto itr = std::find(inputsAllNames.begin(), inputsAllNames.end(), inputName);
451  if (itr == inputsAllNames.end()) {
452  B2ERROR("Neural network needs input '" + inputName + "', but this input is not (yet) available!");
453  return nullptr;
454  }
455  mapInputsNN2InputsAll.push_back(static_cast<size_t>(itr - inputsAllNames.begin()));
456  }
457 
458  std::map<int, std::string> extraInfoNames; // mapping from pdg code to extra info names
459  for (const auto outputPdgCode : neuralNetworkPtr->getOutputSpeciesPdg()) {
460  extraInfoNames[outputPdgCode] = "pidNeuralNetworkValueExpert(" + std::to_string(outputPdgCode) \
461  + ((arguments.size() == 2) ? ("," + arguments[1]) : "") + ")";
462  }
463 
464 
465  auto func = [neuralNetworkPtr, pdgCode, mapInputsNN2InputsAll, inputsAllSize, extraInfoNames](const Particle * part) -> double {
466  const std::string extraInfoName = extraInfoNames.at(pdgCode);
467  if (part->hasExtraInfo(extraInfoName))
468  return part->getExtraInfo(extraInfoName);
469 
470  const auto& neuralNetwork = *neuralNetworkPtr;
471  const PIDLikelihood* pid = part->getPIDLikelihood();
472  if (!pid)
473  return Const::doubleNaN;
474  {
475  auto hypType = Const::ChargedStable(pdgCode);
476  if (pid->getLogL(hypType) == 0)
477  return Const::doubleNaN;
478  }
479 
480 
486  std::vector<float> inputsAll;
487  inputsAll.reserve(inputsAllSize);
488  const auto mom = part->getTrackFitResult()->getMomentum();
489  for (const Const::EDetector& detector : Const::PIDDetectorSet::set())
490  {
491  for (const auto& hypType : Const::chargedStableSet) {
492  const float logL = pid->getLogL(hypType, detector);
493  if (logL == 0) inputsAll.push_back(Const::doubleNaN);
494  else inputsAll.push_back(logL);
495  }
496  }
497  inputsAll.push_back(mom.R());
498  inputsAll.push_back(cos(mom.Theta()));
499  inputsAll.push_back(mom.Phi());
500  inputsAll.push_back(part->getCharge());
501 
502  // collect only those inputs needed for the neural network in the correct order
503  std::vector<float> inputsNN;
504  inputsNN.reserve(neuralNetwork.getInputSize());
505  for (const auto& indexAll : mapInputsNN2InputsAll)
506  {
507  inputsNN.push_back(inputsAll[indexAll]);
508  }
509 
510  const auto probabilities = neuralNetwork.predict(inputsNN);
511 
512  // store all probabilities for all hypotheses in extraInfo
513  for (const auto element : probabilities)
514  {
515  const auto [pdgCodeElement, probability] = element;
516  const_cast<Particle*>(part)->addExtraInfo(extraInfoNames.at(pdgCodeElement), probability);
517  }
518 
519  return probabilities.at(pdgCode);
520  };
521 
522  return func;
523  }
524 
525 
526  Manager::FunctionPtr pidWeightedPairProbabilityExpert(const std::vector<std::string>& arguments)
527  {
528  if (arguments.size() < 4) {
529  B2ERROR("Need at least four arguments to pidWeightedPairProbabilityExpert");
530  return nullptr;
531  }
532  std::string matrixName = arguments[0];
533 
534  int pdgCodeHyp = 0, pdgCodeTest = 0;
535  try {
536  pdgCodeHyp = Belle2::convertString<int>(arguments[1]);
537  } catch (std::invalid_argument& e) {
538  B2ERROR("Second argument of pidWeightedPairProbabilityExpert must be PDG code");
539  return nullptr;
540  }
541  try {
542  pdgCodeTest = Belle2::convertString<int>(arguments[2]);
543  } catch (std::invalid_argument& e) {
544  B2ERROR("Third argument of pidWeightedPairProbabilityExpert must be PDG code");
545  return nullptr;
546  }
547 
548  std::vector<std::string> detectors(arguments.begin() + 3, arguments.end());
549 
550  Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
551  auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
552  auto testType = Const::ChargedStable(abs(pdgCodeTest));
553 
554  auto func = [hypType, testType, detectorSet, matrixName](const Particle * part) -> double {
555  PIDCalibrationWeightUtil weightMatrix(matrixName);
556 
557  const PIDLikelihood* pid = part->getPIDLikelihood();
558  if (!pid) return Const::doubleNaN;
559  // No information from any subdetector in the list
560  if (pid->getLogL(hypType, detectorSet) == 0)
561  return Const::doubleNaN;
562 
563  const auto& frame = ReferenceFrame::GetCurrent();
564  auto mom = frame.getMomentum(part);
565  auto p = mom.P();
566  auto theta = mom.Theta();
567 
568  double LogL_hypType(0), LogL_testType(0);
569  for (const Const::EDetector& detector : Const::PIDDetectorSet::set())
570  {
571  if (detectorSet.contains(detector)) {
572  LogL_hypType += pid->getLogL(hypType, detector) * weightMatrix.getWeight(hypType.getPDGCode(), detector, p, theta);
573  LogL_testType += pid->getLogL(testType, detector) * weightMatrix.getWeight(testType.getPDGCode(), detector, p, theta);
574  }
575  }
576 
577  double deltaLogL = LogL_testType - LogL_hypType;
578  double res;
579  if (deltaLogL < 0)
580  {
581  double eLogL = exp(deltaLogL);
582  res = 1. / (1. + eLogL);
583  } else
584  {
585  double eLogL = exp(-deltaLogL);
586  res = eLogL / (1.0 + eLogL);
587  }
588 
589  if (std::isfinite(res))
590  return res;
591 
592  return 0;
593  };
594  return func;
595  }
596 
597  double electronID(const Particle* part)
598  {
599  static Manager::FunctionPtr pidFunction =
600  pidProbabilityExpert({"11", "ALL"});
601  return std::get<double>(pidFunction(part));
602  }
603 
604  double muonID(const Particle* part)
605  {
606  static Manager::FunctionPtr pidFunction =
607  pidProbabilityExpert({"13", "ALL"});
608  return std::get<double>(pidFunction(part));
609  }
610 
611  double pionID(const Particle* part)
612  {
613  static Manager::FunctionPtr pidFunction =
614  pidProbabilityExpert({"211", "ALL"});
615  return std::get<double>(pidFunction(part));
616  }
617 
618  double kaonID(const Particle* part)
619  {
620  static Manager::FunctionPtr pidFunction =
621  pidProbabilityExpert({"321", "ALL"});
622  return std::get<double>(pidFunction(part));
623  }
624 
625  double protonID(const Particle* part)
626  {
627  static Manager::FunctionPtr pidFunction =
628  pidProbabilityExpert({"2212", "ALL"});
629  return std::get<double>(pidFunction(part));
630  }
631 
632  double deuteronID(const Particle* part)
633  {
634  static Manager::FunctionPtr pidFunction =
635  pidProbabilityExpert({"1000010020", "ALL"});
636  return std::get<double>(pidFunction(part));
637  }
638 
639  double binaryPID(const Particle* part, const std::vector<double>& arguments)
640  {
641  if (arguments.size() != 2) {
642  B2ERROR("The variable binaryPID needs exactly two arguments: the PDG codes of two hypotheses.");
643  return Const::doubleNaN;;
644  }
645  int pdgCodeHyp = std::abs(int(std::lround(arguments[0])));
646  int pdgCodeTest = std::abs(int(std::lround(arguments[1])));
647  return std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(" + std::to_string(
648  pdgCodeHyp) + ", " + std::to_string(
649  pdgCodeTest) + ", ALL)")->function(part));
650  }
651 
652  double electronID_noSVD(const Particle* part)
653  {
654  // Excluding SVD for electron ID. This variable is temporary. BII-8760
655  static Manager::FunctionPtr pidFunction =
656  pidProbabilityExpert({"11", "CDC", "TOP", "ARICH", "ECL", "KLM"});
657  return std::get<double>(pidFunction(part));
658  }
659 
660  double muonID_noSVD(const Particle* part)
661  {
662  // Excluding SVD for muon ID. This variable is temporary. BII-8760
663  static Manager::FunctionPtr pidFunction =
664  pidProbabilityExpert({"13", "CDC", "TOP", "ARICH", "ECL", "KLM"});
665  return std::get<double>(pidFunction(part));
666  }
667 
668  double pionID_noSVD(const Particle* part)
669  {
670  // Excluding SVD for pion ID. This variable is temporary. BII-8760
671  static Manager::FunctionPtr pidFunction =
672  pidProbabilityExpert({"211", "CDC", "TOP", "ARICH", "ECL", "KLM"});
673  return std::get<double>(pidFunction(part));
674  }
675 
676  double kaonID_noSVD(const Particle* part)
677  {
678  // Excluding SVD for kaon ID. This variable is temporary. BII-8760
679  static Manager::FunctionPtr pidFunction =
680  pidProbabilityExpert({"321", "CDC", "TOP", "ARICH", "ECL", "KLM"});
681  return std::get<double>(pidFunction(part));
682  }
683 
684  double protonID_noSVD(const Particle* part)
685  {
686  // Excluding SVD for proton ID. This variable is temporary. BII-8760
687  static Manager::FunctionPtr pidFunction =
688  pidProbabilityExpert({"2212", "CDC", "TOP", "ARICH", "ECL", "KLM"});
689  return std::get<double>(pidFunction(part));
690  }
691 
692  double deuteronID_noSVD(const Particle* part)
693  {
694  // Excluding SVD for deuteron ID. This variable is temporary. BII-8760
695  static Manager::FunctionPtr pidFunction =
696  pidProbabilityExpert({"1000010020", "CDC", "TOP", "ARICH", "ECL", "KLM"});
697  return std::get<double>(pidFunction(part));
698  }
699 
700  double binaryPID_noSVD(const Particle* part, const std::vector<double>& arguments)
701  {
702  // Excluding SVD for binary ID. This variable is temporary. BII-8760
703  if (arguments.size() != 2) {
704  B2ERROR("The variable binaryPID_noSVD needs exactly two arguments: the PDG codes of two hypotheses.");
705  return Const::doubleNaN;;
706  }
707  int pdgCodeHyp = std::abs(int(std::lround(arguments[0])));
708  int pdgCodeTest = std::abs(int(std::lround(arguments[1])));
709  return std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(" + std::to_string(
710  pdgCodeHyp) + ", " + std::to_string(
711  pdgCodeTest) + ", CDC, TOP, ARICH, ECL, KLM)")->function(part));
712  }
713 
714  double electronID_noTOP(const Particle* part)
715  {
716  // Excluding TOP for electron ID. This variable is temporary. BII-8444
717  static Manager::FunctionPtr pidFunction =
718  pidProbabilityExpert({"11", "SVD", "CDC", "ARICH", "ECL", "KLM"});
719  return std::get<double>(pidFunction(part));
720  }
721 
722  double binaryElectronID_noTOP(const Particle* part, const std::vector<double>& arguments)
723  {
724  // Excluding TOP for electron ID. This is temporary. BII-8444
725  if (arguments.size() != 1) {
726  B2ERROR("The variable binaryElectronID_noTOP needs exactly one argument: the PDG code of the test hypothesis.");
727  return Const::doubleNaN;;
728  }
729 
730  int pdgCodeHyp = Const::electron.getPDGCode();
731  int pdgCodeTest = std::abs(int(std::lround(arguments[0])));
732 
733  const auto var = "pidPairProbabilityExpert(" + std::to_string(pdgCodeHyp) + ", " +
734  std::to_string(pdgCodeTest) + ", SVD, CDC, ARICH, ECL, KLM)";
735 
736  return std::get<double>(Manager::Instance().getVariable(var)->function(part));
737  }
738 
739  double electronID_noSVD_noTOP(const Particle* part)
740  {
741  // Excluding SVD and TOP for electron ID. This variable is temporary. BII-8444, BII-8760.
742  static Manager::FunctionPtr pidFunction =
743  pidProbabilityExpert({"11", "CDC", "ARICH", "ECL", "KLM"});
744  return std::get<double>(pidFunction(part));
745  }
746 
747  double binaryElectronID_noSVD_noTOP(const Particle* part, const std::vector<double>& arguments)
748  {
749  // Excluding SVD and TOP for electron ID. This is temporary. BII-8444, BII-8760.
750  if (arguments.size() != 1) {
751  B2ERROR("The variable binaryElectronID_noSVD_noTOP needs exactly one argument: the PDG code of the test hypothesis.");
752  return Const::doubleNaN;;
753  }
754 
755  int pdgCodeHyp = Const::electron.getPDGCode();
756  int pdgCodeTest = std::abs(int(std::lround(arguments[0])));
757 
758  const auto var = "pidPairProbabilityExpert(" + std::to_string(pdgCodeHyp) + ", " +
759  std::to_string(pdgCodeTest) + ", CDC, ARICH, ECL, KLM)";
760 
761  return std::get<double>(Manager::Instance().getVariable(var)->function(part));
762  }
763 
764 
765  double pionID_noARICHwoECL(const Particle* part)
766  {
767  // remove arich if no ecl cluster + identified as kaon in arich
768  const ECLCluster* cluster = part->getECLCluster();
769  if (!cluster) {
770  const PIDLikelihood* pid = part->getPIDLikelihood();
771  if (!pid) return Const::doubleNaN;
772  if (pid->getLogL(Const::kaon, Const::ARICH) > pid->getLogL(Const::pion, Const::ARICH)) {
773  static Manager::FunctionPtr pidFunction =
774  pidProbabilityExpert({"211", "SVD", "CDC", "TOP", "ECL", "KLM"});
775  return std::get<double>(pidFunction(part));
776  }
777  }
778  return pionID(part);
779  }
780 
781 
782  double kaonID_noARICHwoECL(const Particle* part)
783  {
784  // remove arich if no ecl cluster + identified as kaon in arich
785  const ECLCluster* cluster = part->getECLCluster();
786  if (!cluster) {
787  const PIDLikelihood* pid = part->getPIDLikelihood();
788  if (!pid) return Const::doubleNaN;
789  if (pid->getLogL(Const::kaon, Const::ARICH) > pid->getLogL(Const::pion, Const::ARICH)) {
790  static Manager::FunctionPtr pidFunction =
791  pidProbabilityExpert({"321", "SVD", "CDC", "TOP", "ECL", "KLM"});
792  return std::get<double>(pidFunction(part));
793  }
794  }
795  return kaonID(part);
796  }
797 
798 
799  double binaryPID_noARICHwoECL(const Particle* part, const std::vector<double>& arguments)
800  {
801  // Excluding ARICH for tracks without ECL cluster and identified as heavier of the two hypotheses from binary ID.
802  if (arguments.size() != 2) {
803  B2ERROR("The variable binaryPID_noARICHwoECL needs exactly two arguments: the PDG codes of two hypotheses.");
804  return Const::doubleNaN;;
805  }
806  int pdgCodeHyp = std::abs(int(std::lround(arguments[0])));
807  int pdgCodeTest = std::abs(int(std::lround(arguments[1])));
808  auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
809  auto testType = Const::ChargedStable(abs(pdgCodeTest));
810 
811  const ECLCluster* cluster = part->getECLCluster();
812  if (!cluster) {
813  const PIDLikelihood* pid = part->getPIDLikelihood();
814  if (!pid) return Const::doubleNaN;
815  double lkhdiff = pid->getLogL(hypType, Const::ARICH) - pid->getLogL(testType, Const::ARICH);
816  if ((lkhdiff > 0 && pdgCodeHyp > pdgCodeTest) || (lkhdiff < 0 && pdgCodeHyp < pdgCodeTest)) {
817  return std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(" + std::to_string(
818  pdgCodeHyp) + ", " + std::to_string(
819  pdgCodeTest) + ", SVD, CDC, TOP, ECL, KLM)")->function(part));
820  }
821  }
822 
823  return binaryPID(part, arguments);
824 
825  }
826 
827 
828 
829  double antineutronID(const Particle* particle)
830  {
831  if (particle->hasExtraInfo("nbarID")) {
832  return particle->getExtraInfo("nbarID");
833  } else {
834  if (particle->getPDGCode() == -Const::neutron.getPDGCode()) {
835  B2WARNING("The extraInfo nbarID is not registered! \n"
836  "Please use function getNbarIDMVA in modularAnalysis.");
837  }
838  return Const::doubleNaN;
839  }
840  }
841 
842  Manager::FunctionPtr pidChargedBDTScore(const std::vector<std::string>& arguments)
843  {
844  if (arguments.size() != 2) {
845  B2ERROR("Need exactly two arguments for pidChargedBDTScore: pdgCodeHyp, detector");
846  return nullptr;
847  }
848 
849  int hypPdgId;
850  try {
851  hypPdgId = Belle2::convertString<int>(arguments.at(0));
852  } catch (std::invalid_argument& e) {
853  B2ERROR("First argument of pidChargedBDTScore must be an integer (PDG code).");
854  return nullptr;
855  }
856  Const::ChargedStable hypType = Const::ChargedStable(hypPdgId);
857 
858  std::vector<std::string> detectors(arguments.begin() + 1, arguments.end());
859  Const::PIDDetectorSet detectorSet = parseDetectorsChargedBDT(detectors);
860 
861  auto func = [hypType, detectorSet](const Particle * part) -> double {
862  auto name = "pidChargedBDTScore_" + std::to_string(hypType.getPDGCode());
863  for (const Const::EDetector& detector : detectorSet)
864  {
865  name += "_" + std::to_string(detector);
866  }
867  return (part->hasExtraInfo(name)) ? part->getExtraInfo(name) : Const::doubleNaN;
868  };
869  return func;
870  }
871 
872  Manager::FunctionPtr pidPairChargedBDTScore(const std::vector<std::string>& arguments)
873  {
874  if (arguments.size() != 3) {
875  B2ERROR("Need exactly three arguments for pidPairChargedBDTScore: pdgCodeHyp, pdgCodeTest, detector.");
876  return nullptr;
877  }
878 
879  int hypPdgId, testPdgId;
880  try {
881  hypPdgId = Belle2::convertString<int>(arguments.at(0));
882  } catch (std::invalid_argument& e) {
883  B2ERROR("First argument of pidPairChargedBDTScore must be an integer (PDG code).");
884  return nullptr;
885  }
886  try {
887  testPdgId = Belle2::convertString<int>(arguments.at(1));
888  } catch (std::invalid_argument& e) {
889  B2ERROR("First argument of pidPairChargedBDTScore must be an integer (PDG code).");
890  return nullptr;
891  }
892  Const::ChargedStable hypType = Const::ChargedStable(hypPdgId);
893  Const::ChargedStable testType = Const::ChargedStable(testPdgId);
894 
895  std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
896  Const::PIDDetectorSet detectorSet = parseDetectorsChargedBDT(detectors);
897 
898  auto func = [hypType, testType, detectorSet](const Particle * part) -> double {
899  auto name = "pidPairChargedBDTScore_" + std::to_string(hypType.getPDGCode()) + "_VS_" + std::to_string(testType.getPDGCode());
900  for (const Const::EDetector& detector : detectorSet)
901  {
902  name += "_" + std::to_string(detector);
903  }
904  return (part->hasExtraInfo(name)) ? part->getExtraInfo(name) : Const::doubleNaN;
905  };
906  return func;
907  }
908 
909  double mostLikelyPDG(const Particle* part, const std::vector<double>& arguments)
910  {
911  if (arguments.size() != 0 and arguments.size() != Const::ChargedStable::c_SetSize) {
912  B2ERROR("Need zero or exactly " << Const::ChargedStable::c_SetSize << " arguments for pidMostLikelyPDG");
913  return Const::doubleNaN;
914  }
915  double prob[Const::ChargedStable::c_SetSize];
916  if (arguments.size() == 0) {
917  for (unsigned int i = 0; i < Const::ChargedStable::c_SetSize; i++) prob[i] = 1. / Const::ChargedStable::c_SetSize;
918  } else {
919  copy(arguments.begin(), arguments.end(), prob);
920  }
921 
922  auto* pid = part->getPIDLikelihood();
923  if (!pid) return Const::doubleNaN;
924  return pid->getMostLikely(prob).getPDGCode();
925  }
926 
927  bool isMostLikely(const Particle* part, const std::vector<double>& arguments)
928  {
929  if (arguments.size() != 0 and arguments.size() != Const::ChargedStable::c_SetSize) {
930  B2ERROR("Need zero or exactly " << Const::ChargedStable::c_SetSize << " arguments for pidIsMostLikely");
931  return false;
932  }
933  return mostLikelyPDG(part, arguments) == abs(part->getPDGCode());
934  }
935 
936  Manager::FunctionPtr weightedElectronID(const std::vector<std::string>& arguments)
937  {
938  std::string varName;
939  if (arguments.size() == 0) {
940  varName = "pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 11, ALL)";
941  } else if (arguments.size() == 1) {
942  varName = "pidWeightedProbabilityExpert(" + arguments[0] + ", 11, ALL)";
943  } else {
944  B2ERROR("Need zero or one argument for weightedElectronID");
945  return nullptr;
946  }
947 
948  const Variable::Manager::Var* var = Manager::Instance().getVariable(varName);
949  auto func = [var](const Particle * particle) -> double {
950  return std::get<double>(var->function(particle));
951  };
952  return func;
953  };
954 
955  Manager::FunctionPtr weightedMuonID(const std::vector<std::string>& arguments)
956  {
957  std::string varName;
958  if (arguments.size() == 0) {
959  varName = "pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 13, ALL)";
960  } else if (arguments.size() == 1) {
961  varName = "pidWeightedProbabilityExpert(" + arguments[0] + ", 13, ALL)";
962  } else {
963  B2ERROR("Need zero or one argument for weightedMuonID");
964  return nullptr;
965  }
966 
967  const Variable::Manager::Var* var = Manager::Instance().getVariable(varName);
968  auto func = [var](const Particle * particle) -> double {
969  return std::get<double>(var->function(particle));
970  };
971  return func;
972  };
973 
974  Manager::FunctionPtr weightedPionID(const std::vector<std::string>& arguments)
975  {
976  std::string varName;
977  if (arguments.size() == 0) {
978  varName = "pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 211, ALL)";
979  } else if (arguments.size() == 1) {
980  varName = "pidWeightedProbabilityExpert(" + arguments[0] + ", 211, ALL)";
981  } else {
982  B2ERROR("Need zero or one argument for weightedPionID");
983  return nullptr;
984  }
985 
986  const Variable::Manager::Var* var = Manager::Instance().getVariable(varName);
987  auto func = [var](const Particle * particle) -> double {
988  return std::get<double>(var->function(particle));
989  };
990  return func;
991  };
992 
993  Manager::FunctionPtr weightedKaonID(const std::vector<std::string>& arguments)
994  {
995  std::string varName;
996  if (arguments.size() == 0) {
997  varName = "pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 321, ALL)";
998  } else if (arguments.size() == 1) {
999  varName = "pidWeightedProbabilityExpert(" + arguments[0] + ", 321, ALL)";
1000  } else {
1001  B2ERROR("Need zero or one argument for weightedKaonID");
1002  return nullptr;
1003  }
1004 
1005  const Variable::Manager::Var* var = Manager::Instance().getVariable(varName);
1006  auto func = [var](const Particle * particle) -> double {
1007  return std::get<double>(var->function(particle));
1008  };
1009  return func;
1010  };
1011 
1012  Manager::FunctionPtr weightedProtonID(const std::vector<std::string>& arguments)
1013  {
1014  std::string varName;
1015  if (arguments.size() == 0) {
1016  varName = "pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 2212, ALL)";
1017  } else if (arguments.size() == 1) {
1018  varName = "pidWeightedProbabilityExpert(" + arguments[0] + ", 2212, ALL)";
1019  } else {
1020  B2ERROR("Need zero or one argument for weightedProtonID");
1021  return nullptr;
1022  }
1023 
1024  const Variable::Manager::Var* var = Manager::Instance().getVariable(varName);
1025  auto func = [var](const Particle * particle) -> double {
1026  return std::get<double>(var->function(particle));
1027  };
1028  return func;
1029  };
1030 
1031  Manager::FunctionPtr weightedDeuteronID(const std::vector<std::string>& arguments)
1032  {
1033  std::string varName;
1034  if (arguments.size() == 0) {
1035  varName = "pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 1000010020, ALL)";
1036  } else if (arguments.size() == 1) {
1037  varName = "pidWeightedProbabilityExpert(" + arguments[0] + ", 1000010020, ALL)";
1038  } else {
1039  B2ERROR("Need zero or one argument for weightedDeuteronID");
1040  return nullptr;
1041  }
1042 
1043  const Variable::Manager::Var* var = Manager::Instance().getVariable(varName);
1044  auto func = [var](const Particle * particle) -> double {
1045  return std::get<double>(var->function(particle));
1046  };
1047  return func;
1048  };
1049 
1050 
1051  double pionIDNN(const Particle* particle)
1052  {
1053  if (particle->hasExtraInfo("pidNeuralNetworkValueExpert(211)"))
1054  return particle->getExtraInfo("pidNeuralNetworkValueExpert(211)");
1055  static auto func = pidNeuralNetworkValueExpert({"211"});
1056  return std::get<double>(func(particle));
1057  }
1058 
1059  double kaonIDNN(const Particle* particle)
1060  {
1061  if (particle->hasExtraInfo("pidNeuralNetworkValueExpert(321)"))
1062  return particle->getExtraInfo("pidNeuralNetworkValueExpert(321)");
1063  static auto func = pidNeuralNetworkValueExpert({"321"});
1064  return std::get<double>(func(particle));
1065  }
1066 
1067 
1068  //*************
1069  // B2BII
1070  //*************
1071 
1072  double muIDBelle(const Particle* particle)
1073  {
1074  const PIDLikelihood* pid = particle->getPIDLikelihood();
1075  if (!pid) return 0.5; // Belle standard
1076 
1077  if (pid->isAvailable(Const::KLM))
1078  return exp(pid->getLogL(Const::muon, Const::KLM));
1079  else
1080  return 0; // Belle standard
1081  }
1082 
1083  double muIDBelleQuality(const Particle* particle)
1084  {
1085  const PIDLikelihood* pid = particle->getPIDLikelihood();
1086  if (!pid) return 0;// Belle standard
1087 
1088  return pid->isAvailable(Const::KLM);
1089  }
1090 
1091  double atcPIDBelle(const Particle* particle, const std::vector<double>& sigAndBkgHyp)
1092  {
1093  int sigHyp = int(std::lround(sigAndBkgHyp[0]));
1094  int bkgHyp = int(std::lround(sigAndBkgHyp[1]));
1095 
1096  const PIDLikelihood* pid = particle->getPIDLikelihood();
1097  if (!pid) return 0.5; // Belle standard
1098 
1099  // ACC = ARICH
1100  Const::PIDDetectorSet set = Const::ARICH;
1101  double acc_sig = exp(pid->getLogL(hypothesisConversion(sigHyp), set));
1102  double acc_bkg = exp(pid->getLogL(hypothesisConversion(bkgHyp), set));
1103  double acc = 0.5; // Belle standard
1104  if (acc_sig + acc_bkg > 0.0)
1105  acc = acc_sig / (acc_sig + acc_bkg);
1106 
1107  // TOF = TOP
1108  set = Const::TOP;
1109  double tof_sig = exp(pid->getLogL(hypothesisConversion(sigHyp), set));
1110  double tof_bkg = exp(pid->getLogL(hypothesisConversion(bkgHyp), set));
1111  double tof = 0.5; // Belle standard
1112  double tof_all = tof_sig + tof_bkg;
1113  if (tof_all != 0) {
1114  tof = tof_sig / tof_all;
1115  if (tof < 0.001) tof = 0.001;
1116  if (tof > 0.999) tof = 0.999;
1117  }
1118 
1119  // dE/dx = CDC
1120  set = Const::CDC;
1121  double cdc_sig = exp(pid->getLogL(hypothesisConversion(sigHyp), set));
1122  double cdc_bkg = exp(pid->getLogL(hypothesisConversion(bkgHyp), set));
1123  double cdc = 0.5; // Belle standard
1124  double cdc_all = cdc_sig + cdc_bkg;
1125  if (cdc_all != 0) {
1126  cdc = cdc_sig / cdc_all;
1127  if (cdc < 0.001) cdc = 0.001;
1128  if (cdc > 0.999) cdc = 0.999;
1129  }
1130 
1131  // Combined
1132  double pid_sig = acc * tof * cdc;
1133  double pid_bkg = (1. - acc) * (1. - tof) * (1. - cdc);
1134 
1135  return pid_sig / (pid_sig + pid_bkg);
1136  }
1137 
1138 
1139  double eIDBelle(const Particle* part)
1140  {
1141  const PIDLikelihood* pid = part->getPIDLikelihood();
1142  if (!pid) return 0.5; // Belle standard
1143 
1144  Const::PIDDetectorSet set = Const::ECL;
1145  return pid->getProbability(Const::electron, Const::pion, set);
1146  }
1147 
1148 
1149  // PID variables to be used for analysis
1150  VARIABLE_GROUP("PID");
1151  REGISTER_VARIABLE("particleID", particleID,
1152  "the particle identification probability under the particle's own hypothesis, using info from all available detectors");
1153  REGISTER_VARIABLE("electronID", electronID,
1154  "electron identification probability defined as :math:`\\mathcal{L}_e/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors");
1155  REGISTER_VARIABLE("muonID", muonID,
1156  "muon identification probability defined as :math:`\\mathcal{L}_\\mu/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors");
1157  REGISTER_VARIABLE("pionID", pionID,
1158  "pion identification probability defined as :math:`\\mathcal{L}_\\pi/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors");
1159  REGISTER_VARIABLE("kaonID", kaonID,
1160  "kaon identification probability defined as :math:`\\mathcal{L}_K/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors");
1161  REGISTER_VARIABLE("protonID", protonID,
1162  "proton identification probability defined as :math:`\\mathcal{L}_p/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors");
1163  REGISTER_VARIABLE("deuteronID", deuteronID,
1164  "deuteron identification probability defined as :math:`\\mathcal{L}_d/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors");
1165  REGISTER_METAVARIABLE("binaryPID(pdgCode1, pdgCode2)", binaryPID,
1166  "Returns the binary probability for the first provided mass hypothesis with respect to the second mass hypothesis using all detector components",
1167  Manager::VariableDataType::c_double);
1168  REGISTER_METAVARIABLE("pidChargedBDTScore(pdgCodeHyp, detector)", pidChargedBDTScore,
1169  "Returns the charged Pid BDT score for a certain mass hypothesis with respect to all other charged stable particle hypotheses. The second argument specifies which BDT training to use: based on 'ALL' PID detectors (NB: 'SVD' is currently excluded), or 'ECL' only. The choice depends on the ChargedPidMVAMulticlassModule's configuration.",
1170  Manager::VariableDataType::c_double);
1171  REGISTER_METAVARIABLE("pidPairChargedBDTScore(pdgCodeHyp, pdgCodeTest, detector)", pidPairChargedBDTScore,
1172  "Returns the charged Pid BDT score for a certain mass hypothesis with respect to an alternative hypothesis. The second argument specifies which BDT training to use: based on 'ALL' PID detectors (NB: 'SVD' is currently excluded), or 'ECL' only. The choice depends on the ChargedPidMVAModule's configuration.",
1173  Manager::VariableDataType::c_double);
1174  REGISTER_VARIABLE("nbarID", antineutronID, R"DOC(
1175 Returns MVA classifier for antineutron PID.
1176 
1177 - 1 signal(antineutron) like
1178 - 0 background like
1179 - -1 invalid using this PID due to some ECL variables used unavailable
1180 
1181 This PID is only for antineutron. Neutron is also considered as background.
1182 The variables used are `clusterPulseShapeDiscriminationMVA`, `clusterE`, `clusterLAT`, `clusterE1E9`, `clusterE9E21`,
1183 `clusterAbsZernikeMoment40`, `clusterAbsZernikeMoment51`, `clusterZernikeMVA`.)DOC");
1184 
1185  // Special temporary variables defined for users' convenience.
1186  REGISTER_VARIABLE("electronID_noSVD", electronID_noSVD,
1187  "**(SPECIAL (TEMP) variable)** electron identification probability defined as :math:`\\mathcal{L}_e/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the SVD*");
1188  REGISTER_VARIABLE("muonID_noSVD", muonID_noSVD,
1189  "**(SPECIAL (TEMP) variable)** muon identification probability defined as :math:`\\mathcal{L}_\\mu/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the SVD*");
1190  REGISTER_VARIABLE("pionID_noSVD", pionID_noSVD,
1191  "**(SPECIAL (TEMP) variable)** pion identification probability defined as :math:`\\mathcal{L}_\\pi/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the SVD*");
1192  REGISTER_VARIABLE("kaonID_noSVD", kaonID_noSVD,
1193  "**(SPECIAL (TEMP) variable)** kaon identification probability defined as :math:`\\mathcal{L}_K/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the SVD*");
1194  REGISTER_VARIABLE("protonID_noSVD", protonID_noSVD,
1195  "**(SPECIAL (TEMP) variable)** proton identification probability defined as :math:`\\mathcal{L}_p/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the SVD*");
1196  REGISTER_VARIABLE("deuteronID_noSVD", deuteronID_noSVD,
1197  "**(SPECIAL (TEMP) variable)** deuteron identification probability defined as :math:`\\mathcal{L}_d/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the SVD*");
1198  REGISTER_METAVARIABLE("binaryPID_noSVD(pdgCode1, pdgCode2)", binaryPID_noSVD,
1199  "Returns the binary probability for the first provided mass hypothesis with respect to the second mass hypothesis using all detector components, *excluding the SVD*.",
1200  Manager::VariableDataType::c_double);
1201  REGISTER_VARIABLE("electronID_noTOP", electronID_noTOP,
1202  "**(SPECIAL (TEMP) variable)** electron identification probability defined as :math:`\\mathcal{L}_e/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the TOP*. *NB:* this variable must be used in place of `electronID` when analysing data (MC) processed (simulated) in *release 6*");
1203  REGISTER_METAVARIABLE("binaryElectronID_noTOP(pdgCodeTest)", binaryElectronID_noTOP,
1204  "**(SPECIAL (TEMP) variable)** Returns the binary probability for the electron mass hypothesis with respect to another mass hypothesis using all detector components, *excluding the TOP*. *NB:* this variable must be used in place of `binaryPID` (``pdgCode1=11``) when analysing data (MC) processed (simulated) in **release 6**",
1205  Manager::VariableDataType::c_double);
1206  REGISTER_VARIABLE("electronID_noSVD_noTOP", electronID_noSVD_noTOP,
1207  "**(SPECIAL (TEMP) variable)** electron identification probability defined as :math:`\\mathcal{L}_e/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors *excluding the SVD and the TOP*. *NB:* this variable must be used in place of `electronID` when analysing data (MC) processed (simulated) in *release 5*");
1208  REGISTER_METAVARIABLE("binaryElectronID_noSVD_noTOP(pdgCodeTest)", binaryElectronID_noSVD_noTOP,
1209  "**(SPECIAL (TEMP) variable)** Returns the binary probability for the electron mass hypothesis with respect to another mass hypothesis using all detector components, *excluding the SVD and the TOP*. *NB:* this variable must be used in place of `binaryPID` (``pdgCode1=11``) when analysing data (MC) processed (simulated) in **release 5**",
1210  Manager::VariableDataType::c_double);
1211  REGISTER_VARIABLE("pionID_noARICHwoECL", pionID_noARICHwoECL,
1212  "**(SPECIAL (TEMP) variable)** pion identification probability defined as :math:`\\mathcal{L}_\\pi/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors but ARICH info excluded for tracks without associated ECL cluster");
1213  REGISTER_VARIABLE("kaonID_noARICHwoECL", kaonID_noARICHwoECL,
1214  "**(SPECIAL (TEMP) variable)** kaon identification probability defined as :math:`\\mathcal{L}_K/(\\mathcal{L}_e+\\mathcal{L}_\\mu+\\mathcal{L}_\\pi+\\mathcal{L}_K+\\mathcal{L}_p+\\mathcal{L}_d)`, using info from all available detectors but ARICH info excluded for tracks without associated ECL cluster");
1215  REGISTER_METAVARIABLE("binaryPID_noARICHwoECL(pdgCode1, pdgCode2)", binaryPID_noARICHwoECL,
1216  "Returns the binary probability for the first provided mass hypothesis with respect to the second mass hypothesis using all detector components, but ARICH info excluded for tracks without associated ECL cluster",
1217  Manager::VariableDataType::c_double);
1218 
1219 
1220  REGISTER_METAVARIABLE("weightedElectronID(weightMatrixName)", weightedElectronID,
1221  R"DOC(
1222 weighted electron identification probability defined as :math:`\frac{\mathcal{\tilde{L}}_e}{\sum_{i=e,\mu,\pi,K,p,d} \mathcal{\tilde{L}}_i}`,
1223 where :math:`\mathcal{\tilde{L}}_i` is defined as :math:`\log\mathcal{\tilde{L}}_i = \sum_{j={\mathrm{SVD, CDC, TOP, ARICH, ECL, KLM}}} \mathcal{w}_{ij}\log\mathcal{L}_{ij}`.
1224 The :math:`\mathcal{L}_{ij}` is the original likelihood and :math:`\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.
1225 One can provide the name of the weight matrix as the argument.
1226 )DOC",
1227  Manager::VariableDataType::c_double);
1228  REGISTER_METAVARIABLE("weightedMuonID(weightMatrixName)", weightedMuonID,
1229  R"DOC(
1230 weighted muon identification probability defined as :math:`\frac{\mathcal{\tilde{L}}_\mu}{\sum_{i=e,\mu,\pi,K,p,d} \mathcal{\tilde{L}}_i}`,
1231 where :math:`\mathcal{\tilde{L}}_i` is defined as :math:`\log\mathcal{\tilde{L}}_i = \sum_{j={\mathrm{SVD, CDC, TOP, ARICH, ECL, KLM}}} \mathcal{w}_{ij}\log\mathcal{L}_{ij}`.
1232 The :math:`\mathcal{L}_{ij}` is the original likelihood and :math:`\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.
1233 One can provide the name of the weight matrix as the argument.
1234 )DOC",
1235  Manager::VariableDataType::c_double);
1236  REGISTER_METAVARIABLE("weightedPionID(weightMatrixName)", weightedPionID,
1237  R"DOC(
1238 weighted pion identification probability defined as :math:`\frac{\mathcal{\tilde{L}}_\pi}{\sum_{i=e,\mu,\pi,K,p,d} \mathcal{\tilde{L}}_i}`,
1239 where :math:`\mathcal{\tilde{L}}_i` is defined as :math:`\log\mathcal{\tilde{L}}_i = \sum_{j={\mathrm{SVD, CDC, TOP, ARICH, ECL, KLM}}} \mathcal{w}_{ij}\log\mathcal{L}_{ij}`.
1240 The :math:`\mathcal{L}_{ij}` is the original likelihood and :math:`\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.
1241 One can provide the name of the weight matrix as the argument.
1242 )DOC",
1243  Manager::VariableDataType::c_double);
1244  REGISTER_METAVARIABLE("weightedKaonID(weightMatrixName)", weightedKaonID,
1245  R"DOC(
1246 weighted kaon identification probability defined as :math:`\frac{\mathcal{\tilde{L}}_K}{\sum_{i=e,\mu,\pi,K,p,d} \mathcal{\tilde{L}}_i}`,
1247 where :math:`\mathcal{\tilde{L}}_i` is defined as :math:`\log\mathcal{\tilde{L}}_i = \sum_{j={\mathrm{SVD, CDC, TOP, ARICH, ECL, KLM}}} \mathcal{w}_{ij}\log\mathcal{L}_{ij}`.
1248 The :math:`\mathcal{L}_{ij}` is the original likelihood and :math:`\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.
1249 One can provide the name of the weight matrix as the argument.
1250 )DOC",
1251  Manager::VariableDataType::c_double);
1252  REGISTER_METAVARIABLE("weightedProtonID(weightMatrixName)", weightedProtonID,
1253  R"DOC(
1254 weighted proton identification probability defined as :math:`\frac{\mathcal{\tilde{L}}_p}{\sum_{i=e,\mu,\pi,K,p,d} \mathcal{\tilde{L}}_i}`,
1255 where :math:`\mathcal{\tilde{L}}_i` is defined as :math:`\log\mathcal{\tilde{L}}_i = \sum_{j={\mathrm{SVD, CDC, TOP, ARICH, ECL, KLM}}} \mathcal{w}_{ij}\log\mathcal{L}_{ij}`.
1256 The :math:`\mathcal{L}_{ij}` is the original likelihood and :math:`\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.
1257 One can provide the name of the weight matrix as the argument.
1258 )DOC",
1259  Manager::VariableDataType::c_double);
1260  REGISTER_METAVARIABLE("weightedDeuteronID(weightMatrixName)", weightedDeuteronID,
1261  R"DOC(
1262 weighted deuteron identification probability defined as :math:`\frac{\mathcal{\tilde{L}}_d}{\sum_{i=e,\mu,\pi,K,p,d} \mathcal{\tilde{L}}_i}`,
1263 where :math:`\mathcal{\tilde{L}}_i` is defined as :math:`\log\mathcal{\tilde{L}}_i = \sum_{j={\mathrm{SVD, CDC, TOP, ARICH, ECL, KLM}}} \mathcal{w}_{ij}\log\mathcal{L}_{ij}`.
1264 The :math:`\mathcal{L}_{ij}` is the original likelihood and :math:`\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.
1265 One can provide the name of the weight matrix as the argument.
1266 )DOC",
1267  Manager::VariableDataType::c_double);
1268 
1269 
1270  REGISTER_VARIABLE("pionIDNN", pionIDNN,
1271  R"DOC(
1272 pion identification probability as calculated from the PID neural network.
1273 )DOC");
1274  REGISTER_VARIABLE("kaonIDNN", kaonIDNN,
1275  R"DOC(
1276 kaon identification probability as calculated from the PID neural network.
1277 )DOC");
1278 
1279 
1280  // Metafunctions for experts to access the basic PID quantities
1281  VARIABLE_GROUP("PID_expert");
1282  REGISTER_METAVARIABLE("pidLogLikelihoodValueExpert(pdgCode, detectorList)", pidLogLikelihoodValueExpert,
1283  "returns the log likelihood value of for a specific mass hypothesis and set of detectors.", Manager::VariableDataType::c_double);
1284  REGISTER_METAVARIABLE("pidDeltaLogLikelihoodValueExpert(pdgCode1, pdgCode2, detectorList)", pidDeltaLogLikelihoodValueExpert,
1285  "returns LogL(hyp1) - LogL(hyp2) (aka DLL) for two mass hypotheses and a set of detectors.", Manager::VariableDataType::c_double);
1286  REGISTER_METAVARIABLE("pidPairProbabilityExpert(pdgCodeHyp, pdgCodeTest, detectorList)", pidPairProbabilityExpert,
1287  "Pair (or binary) probability for the pdgCodeHyp mass hypothesis respect to the pdgCodeTest one, using an arbitrary set of detectors. :math:`\\mathcal{L}_{hyp}/(\\mathcal{L}_{test}+\\mathcal{L}_{hyp})`",
1288  Manager::VariableDataType::c_double);
1289  REGISTER_METAVARIABLE("pidProbabilityExpert(pdgCodeHyp, detectorList)", pidProbabilityExpert,
1290  "probability for the pdgCodeHyp mass hypothesis respect to all the other ones, using an arbitrary set of detectors :math:`\\mathcal{L}_{hyp}/(\\Sigma_{\\text{all~hyp}}\\mathcal{L}_{i})`. ",
1291  Manager::VariableDataType::c_double);
1292  REGISTER_METAVARIABLE("pidMissingProbabilityExpert(detectorList)", pidMissingProbabilityExpert,
1293  "returns 1 if the PID probabiliy is missing for the provided detector list, otherwise 0. ", Manager::VariableDataType::c_double);
1294  REGISTER_VARIABLE("pidMostLikelyPDG(ePrior=1/6, muPrior=1/6, piPrior=1/6, KPrior=1/6, pPrior=1/6, dPrior=1/6)", mostLikelyPDG,
1295  R"DOC(
1296 Returns PDG code of the largest PID likelihood, or NaN if PID information is not available.
1297 This function accepts either no arguments, or 6 floats as priors for the charged particle hypotheses
1298 following the order shown in the metavariable's declaration. Flat priors are assumed as default.)DOC");
1299  REGISTER_VARIABLE("pidIsMostLikely(ePrior=1/6, muPrior=1/6, piPrior=1/6, KPrior=1/6, pPrior=1/6, dPrior=1/6)", isMostLikely, R"DOC(
1300 Returns True if the largest PID likelihood of a given particle corresponds to its particle hypothesis.
1301 This function accepts either no arguments, or 6 floats as priors for the charged particle hypotheses
1302 following the order shown in the metavariable's declaration. Flat priors are assumed as default.)DOC");
1303 
1304  REGISTER_METAVARIABLE("pidWeightedLogLikelihoodValueExpert(weightMatrixName, pdgCode, detectorList)",
1305  pidWeightedLogLikelihoodValueExpert,
1306  "returns the weighted log likelihood value of for a specific mass hypothesis and set of detectors, "
1307  ":math:`\\log\\mathcal{\\tilde{L}}_{hyp} = \\sum_{j\\in\\mathrm{detectorList}} \\mathcal{w}_{hyp,j}\\log\\mathcal{L}_{hyp,j}`. "
1308  "The :math:`\\mathcal{L}_{ij}` is the original likelihood and :math:`\\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.",
1309  Manager::VariableDataType::c_double);
1310  REGISTER_METAVARIABLE("pidWeightedPairProbabilityExpert(weightMatrixName, pdgCodeHyp, pdgCodeTest, detectorList)",
1311  pidWeightedPairProbabilityExpert,
1312  "Weighted pair (or binary) probability for the pdgCodeHyp mass hypothesis with respect to the pdgCodeTest one, using an arbitrary set of detectors, "
1313  ":math:`\\mathcal{\\tilde{L}}_{hyp}/(\\mathcal{\\tilde{L}}_{test}+\\mathcal{\\tilde{L}}_{hyp})` where :math:`\\mathcal{\\tilde{L}}_{i}` is defined as "
1314  ":math:`\\log\\mathcal{\\tilde{L}}_{i} = \\sum_{j\\in\\mathrm{detectorList}} \\mathcal{w}_{i,j}\\log\\mathcal{L}_{i,j}`. "
1315  "The :math:`\\mathcal{L}_{ij}` is the original likelihood and :math:`\\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.",
1316  Manager::VariableDataType::c_double);
1317  REGISTER_METAVARIABLE("pidWeightedProbabilityExpert(weightMatrixName, pdgCodeHyp, detectorList)",
1318  pidWeightedProbabilityExpert,
1319  "Weighted probability for the pdgCodeHyp mass hypothesis with respect to all the other ones, using an arbitrary set of detectors, "
1320  ":math:`\\mathcal{\\tilde{L}}_{hyp}/\\sum_{i=e,\\mu,\\pi,K,p,d} \\mathcal{\\tilde{L}}_i` where :math:`\\mathcal{\\tilde{L}}_{i}` is defined as "
1321  ":math:`\\log\\mathcal{\\tilde{L}}_{i} = \\sum_{j\\in\\mathrm{detectorList}} \\mathcal{w}_{i,j}\\log\\mathcal{L}_{i,j}`. "
1322  "The :math:`\\mathcal{L}_{ij}` is the original likelihood and :math:`\\mathcal{w}_{ij}` is the PID calibration weight of i-th particle type and j-th detector.",
1323  Manager::VariableDataType::c_double);
1324  REGISTER_METAVARIABLE("pidNeuralNetworkValueExpert(pdgCodeHyp, PIDNeuralNetworkName)",
1325  pidNeuralNetworkValueExpert,
1326  "Probability for the particle hypothesis pdgCodeHype calculated from a neural network, which uses high-level information as inputs, "
1327  "such as the likelihood from the 6 subdetectors for PID for all 6 hypotheses, "
1328  ":math:`\\mathcal{\\tilde{L}}_{hyp}^{det}`, or the track momentum and charge",
1329  Manager::VariableDataType::c_double);
1330 
1331  // B2BII PID
1332  VARIABLE_GROUP("Belle PID variables");
1333  REGISTER_METAVARIABLE("atcPIDBelle(i,j)", atcPIDBelle, R"DOC(
1334 [Legacy] Returns Belle's PID atc variable: ``atc_pid(3,1,5,i,j).prob()``.
1335 Parameters i,j are signal and background hypothesis: (0 = electron, 1 = muon, 2 = pion, 3 = kaon, 4 = proton)
1336 Returns 0.5 in case there is no likelihood found and a factor of 0.5 will appear in the product if any of the subdetectors don't report a likelihood (Belle behaviour).
1337 
1338 .. warning:: The behaviour is different from Belle II PID variables which typically return NaN in case of error.
1339  )DOC", Manager::VariableDataType::c_double);
1340  REGISTER_VARIABLE("muIDBelle", muIDBelle, R"DOC(
1341 [Legacy] Returns Belle's PID ``Muon_likelihood()`` variable.
1342 Returns 0.5 in case there is no likelihood found and returns zero if the muon likelihood is not usable (Belle behaviour).
1343 
1344 .. warning:: The behaviour is different from Belle II PID variables which typically return NaN in case of error.
1345  )DOC");
1346  REGISTER_VARIABLE("muIDBelleQuality", muIDBelleQuality, R"DOC(
1347 [Legacy] Returns true if Belle's PID ``Muon_likelihood()`` is usable (reliable).
1348 Returns zero/false if not usable or if there is no PID found.
1349  )DOC");
1350  REGISTER_VARIABLE("eIDBelle", eIDBelle, R"DOC(
1351 [Legacy] Returns Belle's electron ID ``eid(3,-1,5).prob()`` variable.
1352 Returns 0.5 in case there is no likelihood found (Belle behaviour).
1353 
1354 .. warning:: The behaviour is different from Belle II PID variables which typically return NaN in case of error.
1355  )DOC");
1356  }
1358 }
Abstract base class for different kinds of events.