10 #include <analysis/variables/PIDVariables.h> 
   12 #include <analysis/dataobjects/Particle.h> 
   13 #include <analysis/utility/ReferenceFrame.h> 
   14 #include <mdst/dataobjects/PIDLikelihood.h> 
   15 #include <mdst/dataobjects/TrackFitResult.h> 
   18 #include <framework/logging/Logger.h> 
   19 #include <framework/utilities/Conversion.h> 
   20 #include <framework/gearbox/Const.h> 
   24 #include <analysis/dbobjects/PIDCalibrationWeight.h> 
   25 #include <analysis/utility/PIDCalibrationWeightUtil.h> 
   26 #include <analysis/utility/PIDNeuralNetwork.h> 
   28 #include <boost/algorithm/string.hpp> 
   50     Const::ChargedStable hypothesisConversion(
const int hypothesis)
 
   54           return Const::electron;
 
   69     Const::PIDDetectorSet parseDetectors(
const std::vector<std::string>& arguments)
 
   71       Const::PIDDetectorSet result;
 
   72       for (std::string val : arguments) {
 
   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);
 
   87     Const::PIDDetectorSet parseDetectorsChargedBDT(
const std::vector<std::string>& arguments)
 
   89       Const::PIDDetectorSet result;
 
   90       for (std::string val : arguments) {
 
   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.");
 
  105     double particleID(
const Particle* p)
 
  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 std::numeric_limits<float>::quiet_NaN();
 
  117     Manager::FunctionPtr pidLogLikelihoodValueExpert(
const std::vector<std::string>& arguments)
 
  119       if (arguments.size() < 2) {
 
  120         B2ERROR(
"Need at least two arguments to pidLogLikelihoodValueExpert");
 
  125         pdgCode = Belle2::convertString<int>(arguments[0]);
 
  126       } 
catch (std::invalid_argument& e) {
 
  127         B2ERROR(
"First argument of pidLogLikelihoodValueExpert must be a PDG code");
 
  130       std::vector<std::string> detectors(arguments.begin() + 1, arguments.end());
 
  132       Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
 
  133       auto hypType = Const::ChargedStable(abs(pdgCode));
 
  135       auto func = [hypType, detectorSet](
const Particle * part) -> 
double {
 
  136         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  138           return std::numeric_limits<float>::quiet_NaN();
 
  140         if (pid->getLogL(hypType, detectorSet) == 0)
 
  141           return std::numeric_limits<float>::quiet_NaN();
 
  143         return pid->getLogL(hypType, detectorSet);
 
  150     Manager::FunctionPtr pidDeltaLogLikelihoodValueExpert(
const std::vector<std::string>& arguments)
 
  152       if (arguments.size() < 3) {
 
  153         B2ERROR(
"Need at least three arguments to pidDeltaLogLikelihoodValueExpert");
 
  156       int pdgCodeHyp, pdgCodeTest;
 
  158         pdgCodeHyp = Belle2::convertString<int>(arguments[0]);
 
  159       } 
catch (std::invalid_argument& e) {
 
  160         B2ERROR(
"First argument of pidDeltaLogLikelihoodValueExpert must be a PDG code");
 
  164         pdgCodeTest = Belle2::convertString<int>(arguments[1]);
 
  165       } 
catch (std::invalid_argument& e) {
 
  166         B2ERROR(
"Second argument of pidDeltaLogLikelihoodValueExpert must be a PDG code");
 
  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));
 
  175       auto func = [hypType, testType, detectorSet](
const Particle * part) -> 
double {
 
  176         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  177         if (!pid) 
return std::numeric_limits<float>::quiet_NaN();
 
  179         if (pid->getLogL(hypType, detectorSet) == 0)
 
  180           return std::numeric_limits<float>::quiet_NaN();
 
  182         return (pid->getLogL(hypType, detectorSet) - pid->getLogL(testType, detectorSet));
 
  188     Manager::FunctionPtr pidPairProbabilityExpert(
const std::vector<std::string>& arguments)
 
  190       if (arguments.size() < 3) {
 
  191         B2ERROR(
"Need at least three arguments to pidPairProbabilityExpert");
 
  194       int pdgCodeHyp = 0, pdgCodeTest = 0;
 
  196         pdgCodeHyp = Belle2::convertString<int>(arguments[0]);
 
  197       } 
catch (std::invalid_argument& e) {
 
  198         B2ERROR(
"First argument of pidPairProbabilityExpert must be PDG code");
 
  202         pdgCodeTest = Belle2::convertString<int>(arguments[1]);
 
  203       } 
catch (std::invalid_argument& e) {
 
  204         B2ERROR(
"Second argument of pidPairProbabilityExpert must be PDG code");
 
  208       std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
 
  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 std::numeric_limits<float>::quiet_NaN();
 
  217         if (pid->getLogL(hypType, detectorSet) == 0)
 
  218           return std::numeric_limits<float>::quiet_NaN();
 
  220         return pid->getProbability(hypType, testType, detectorSet);
 
  226     Manager::FunctionPtr pidProbabilityExpert(
const std::vector<std::string>& arguments)
 
  228       if (arguments.size() < 2) {
 
  229         B2ERROR(
"Need at least two arguments for pidProbabilityExpert");
 
  234         pdgCodeHyp = Belle2::convertString<int>(arguments[0]);
 
  235       } 
catch (std::invalid_argument& e) {
 
  236         B2ERROR(
"First argument of pidProbabilityExpert must be PDG code");
 
  240       std::vector<std::string> detectors(arguments.begin() + 1, arguments.end());
 
  241       Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
 
  242       auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
 
  245       const unsigned int n = Const::ChargedStable::c_SetSize;
 
  247       for (
double& i : frac) i = 1.0;  
 
  249       auto func = [hypType, frac, detectorSet](
const Particle * part) -> 
double {
 
  250         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  251         if (!pid) 
return std::numeric_limits<float>::quiet_NaN();
 
  253         if (pid->getLogL(hypType, detectorSet) == 0)
 
  254           return std::numeric_limits<float>::quiet_NaN();
 
  256         return pid->getProbability(hypType, frac, detectorSet);
 
  262     Manager::FunctionPtr pidMissingProbabilityExpert(
const std::vector<std::string>& arguments)
 
  264       if (arguments.size() < 1) {
 
  265         B2ERROR(
"Need at least one argument to pidMissingProbabilityExpert");
 
  269       std::vector<std::string> detectors(arguments.begin(), arguments.end());
 
  270       Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
 
  272       auto func = [detectorSet](
const Particle * part) -> 
double {
 
  273         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  274         if (!pid) 
return std::numeric_limits<double>::quiet_NaN();
 
  275         if (not pid->isAvailable(detectorSet))
 
  282     Manager::FunctionPtr pidWeightedLogLikelihoodValueExpert(
const std::vector<std::string>& arguments)
 
  284       if (arguments.size() < 3) {
 
  285         B2ERROR(
"Need at least three arguments to pidWeightedLogLikelihoodValueExpert");
 
  288       std::string matrixName = arguments[0];
 
  292         pdgCode = Belle2::convertString<int>(arguments[1]);
 
  293       } 
catch (std::invalid_argument& e) {
 
  294         B2ERROR(
"Second argument of pidWeightedLogLikelihoodValueExpert must be a PDG code");
 
  297       std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
 
  298       Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
 
  299       auto hypType = Const::ChargedStable(abs(pdgCode));
 
  301       auto func = [hypType, detectorSet, matrixName](
const Particle * part) -> 
double {
 
  302         PIDCalibrationWeightUtil weightMatrix(matrixName);
 
  303         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  305           return std::numeric_limits<float>::quiet_NaN();
 
  307         if (pid->getLogL(hypType, detectorSet) == 0)
 
  308           return std::numeric_limits<float>::quiet_NaN();
 
  310         const auto& frame = ReferenceFrame::GetCurrent();
 
  311         auto mom = frame.getMomentum(part);
 
  313         auto theta = mom.Theta();
 
  316         for (
const Const::EDetector& detector : Const::PIDDetectorSet::set())
 
  318           if (detectorSet.contains(detector))
 
  319             LogL += pid->getLogL(hypType, detector) * weightMatrix.getWeight(hypType.getPDGCode(), detector, p, theta);
 
  327     Manager::FunctionPtr pidWeightedProbabilityExpert(
const std::vector<std::string>& arguments)
 
  329       if (arguments.size() < 3) {
 
  330         B2ERROR(
"Need at least three arguments for pidWeightedProbabilityExpert");
 
  333       std::string matrixName = arguments[0];
 
  337         pdgCodeHyp = Belle2::convertString<int>(arguments[1]);
 
  338       } 
catch (std::invalid_argument& e) {
 
  339         B2ERROR(
"Second argument of pidWeightedProbabilityExpert must be PDG code");
 
  343       std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
 
  344       Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
 
  345       auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
 
  347       auto func = [hypType, detectorSet, matrixName](
const Particle * part) -> 
double {
 
  348         PIDCalibrationWeightUtil weightMatrix(matrixName);
 
  349         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  350         if (!pid) 
return std::numeric_limits<float>::quiet_NaN();
 
  352         if (pid->getLogL(hypType, detectorSet) == 0)
 
  353           return std::numeric_limits<float>::quiet_NaN();
 
  355         const auto& frame = ReferenceFrame::GetCurrent();
 
  356         auto mom = frame.getMomentum(part);
 
  358         auto theta = mom.Theta();
 
  360         double LogL[Const::ChargedStable::c_SetSize];
 
  363         for (
const auto& pdgIter : Const::chargedStableSet)
 
  365           const int index_pdg = pdgIter.getIndex();
 
  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);
 
  373           if (!hasMax || (LogL[index_pdg] > LogL_max)) {
 
  374             LogL_max = LogL[index_pdg];
 
  380         for (
unsigned i = 0; i < Const::ChargedStable::c_SetSize; ++i)
 
  381           norm += exp(LogL[i] - LogL_max);
 
  384           return exp(LogL[hypType.getIndex()] - LogL_max) / norm;
 
  392     Manager::FunctionPtr pidNeuralNetworkValueExpert(
const std::vector<std::string>& arguments)
 
  394       if (arguments.size() == 0) {
 
  395         B2ERROR(
"Need pdg code for pidNeuralNetworkValueExpert");
 
  398       if (arguments.size() > 2) {
 
  399         B2ERROR(
"pidNeuralNetworkValueExpert expects at most two arguments, i.e. the pdg code and the pidNeuralNetworkName");
 
  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");
 
  410       std::shared_ptr<PIDNeuralNetwork> neuralNetworkPtr;
 
  411       if (arguments.size() == 2) {
 
  412         std::string parameterSetName = arguments[1];
 
  413         neuralNetworkPtr = std::make_shared<PIDNeuralNetwork>(parameterSetName);
 
  415         neuralNetworkPtr = std::make_shared<PIDNeuralNetwork>();
 
  418       neuralNetworkPtr->hasPdgCode(pdgCode, 
true); 
 
  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(
 
  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();
 
  448       std::vector<size_t> mapInputsNN2InputsAll;  
 
  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!");
 
  455         mapInputsNN2InputsAll.push_back(
static_cast<size_t>(itr - inputsAllNames.begin()));
 
  458       std::map<int, std::string> extraInfoNames; 
 
  459       for (
const auto outputPdgCode : neuralNetworkPtr->getOutputSpeciesPdg()) {
 
  460         extraInfoNames[outputPdgCode] = 
"pidNeuralNetworkValueExpert(" + std::to_string(outputPdgCode) \
 
  461                                         + ((arguments.size() == 2) ? (
"," + arguments[1]) : 
"") + 
")";
 
  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);
 
  470         const auto& neuralNetwork = *neuralNetworkPtr;
 
  471         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  473           return std::numeric_limits<float>::quiet_NaN();
 
  475           auto hypType = Const::ChargedStable(pdgCode);
 
  476           if (pid->getLogL(hypType) == 0)
 
  477             return std::numeric_limits<float>::quiet_NaN();
 
  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())
 
  491           for (
const auto& hypType : Const::chargedStableSet) {
 
  492             const float logL = pid->getLogL(hypType, detector);
 
  493             if (logL == 0) inputsAll.push_back(std::numeric_limits<float>::quiet_NaN());
 
  494             else inputsAll.push_back(logL);
 
  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());
 
  503         std::vector<float> inputsNN;
 
  504         inputsNN.reserve(neuralNetwork.getInputSize());
 
  505         for (
const auto& indexAll : mapInputsNN2InputsAll)
 
  507           inputsNN.push_back(inputsAll[indexAll]);
 
  510         const auto probabilities = neuralNetwork.predict(inputsNN);
 
  513         for (
const auto element : probabilities)
 
  515           const auto [pdgCodeElement, probability] = element;
 
  516           const_cast<Particle*
>(part)->addExtraInfo(extraInfoNames.at(pdgCodeElement), probability);
 
  519         return probabilities.at(pdgCode);
 
  526     Manager::FunctionPtr pidWeightedPairProbabilityExpert(
const std::vector<std::string>& arguments)
 
  528       if (arguments.size() < 4) {
 
  529         B2ERROR(
"Need at least four arguments to pidWeightedPairProbabilityExpert");
 
  532       std::string matrixName = arguments[0];
 
  534       int pdgCodeHyp = 0, pdgCodeTest = 0;
 
  536         pdgCodeHyp = Belle2::convertString<int>(arguments[1]);
 
  537       } 
catch (std::invalid_argument& e) {
 
  538         B2ERROR(
"Second argument of pidWeightedPairProbabilityExpert must be PDG code");
 
  542         pdgCodeTest = Belle2::convertString<int>(arguments[2]);
 
  543       } 
catch (std::invalid_argument& e) {
 
  544         B2ERROR(
"Third argument of pidWeightedPairProbabilityExpert must be PDG code");
 
  548       std::vector<std::string> detectors(arguments.begin() + 3, arguments.end());
 
  550       Const::PIDDetectorSet detectorSet = parseDetectors(detectors);
 
  551       auto hypType = Const::ChargedStable(abs(pdgCodeHyp));
 
  552       auto testType = Const::ChargedStable(abs(pdgCodeTest));
 
  554       auto func = [hypType, testType, detectorSet, matrixName](
const Particle * part) -> 
double {
 
  555         PIDCalibrationWeightUtil weightMatrix(matrixName);
 
  557         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  558         if (!pid) 
return std::numeric_limits<float>::quiet_NaN();
 
  560         if (pid->getLogL(hypType, detectorSet) == 0)
 
  561           return std::numeric_limits<float>::quiet_NaN();
 
  563         const auto& frame = ReferenceFrame::GetCurrent();
 
  564         auto mom = frame.getMomentum(part);
 
  566         auto theta = mom.Theta();
 
  568         double LogL_hypType(0), LogL_testType(0);
 
  569         for (
const Const::EDetector& detector : Const::PIDDetectorSet::set())
 
  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);
 
  577         double deltaLogL = LogL_testType - LogL_hypType;
 
  581           double eLogL = exp(deltaLogL);
 
  582           res = 1. / (1. + eLogL);
 
  585           double eLogL = exp(-deltaLogL);
 
  586           res = eLogL / (1.0 + eLogL);
 
  589         if (std::isfinite(res))
 
  597     double electronID(
const Particle* part)
 
  599       static Manager::FunctionPtr pidFunction =
 
  600         pidProbabilityExpert({
"11", 
"ALL"});
 
  601       return std::get<double>(pidFunction(part));
 
  604     double muonID(
const Particle* part)
 
  606       static Manager::FunctionPtr pidFunction =
 
  607         pidProbabilityExpert({
"13", 
"ALL"});
 
  608       return std::get<double>(pidFunction(part));
 
  611     double pionID(
const Particle* part)
 
  613       static Manager::FunctionPtr pidFunction =
 
  614         pidProbabilityExpert({
"211", 
"ALL"});
 
  615       return std::get<double>(pidFunction(part));
 
  618     double kaonID(
const Particle* part)
 
  620       static Manager::FunctionPtr pidFunction =
 
  621         pidProbabilityExpert({
"321", 
"ALL"});
 
  622       return std::get<double>(pidFunction(part));
 
  625     double protonID(
const Particle* part)
 
  627       static Manager::FunctionPtr pidFunction =
 
  628         pidProbabilityExpert({
"2212", 
"ALL"});
 
  629       return std::get<double>(pidFunction(part));
 
  632     double deuteronID(
const Particle* part)
 
  634       static Manager::FunctionPtr pidFunction =
 
  635         pidProbabilityExpert({
"1000010020", 
"ALL"});
 
  636       return std::get<double>(pidFunction(part));
 
  639     double binaryPID(
const Particle* part, 
const std::vector<double>& arguments)
 
  641       if (arguments.size() != 2) {
 
  642         B2ERROR(
"The variable binaryPID needs exactly two arguments: the PDG codes of two hypotheses.");
 
  643         return std::numeric_limits<float>::quiet_NaN();;
 
  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));
 
  652     double electronID_noSVD(
const Particle* part)
 
  655       static Manager::FunctionPtr pidFunction =
 
  656         pidProbabilityExpert({
"11", 
"CDC", 
"TOP", 
"ARICH", 
"ECL", 
"KLM"});
 
  657       return std::get<double>(pidFunction(part));
 
  660     double muonID_noSVD(
const Particle* part)
 
  663       static Manager::FunctionPtr pidFunction =
 
  664         pidProbabilityExpert({
"13", 
"CDC", 
"TOP", 
"ARICH", 
"ECL", 
"KLM"});
 
  665       return std::get<double>(pidFunction(part));
 
  668     double pionID_noSVD(
const Particle* part)
 
  671       static Manager::FunctionPtr pidFunction =
 
  672         pidProbabilityExpert({
"211", 
"CDC", 
"TOP", 
"ARICH", 
"ECL", 
"KLM"});
 
  673       return std::get<double>(pidFunction(part));
 
  676     double kaonID_noSVD(
const Particle* part)
 
  679       static Manager::FunctionPtr pidFunction =
 
  680         pidProbabilityExpert({
"321", 
"CDC", 
"TOP", 
"ARICH", 
"ECL", 
"KLM"});
 
  681       return std::get<double>(pidFunction(part));
 
  684     double protonID_noSVD(
const Particle* part)
 
  687       static Manager::FunctionPtr pidFunction =
 
  688         pidProbabilityExpert({
"2212", 
"CDC", 
"TOP", 
"ARICH", 
"ECL", 
"KLM"});
 
  689       return std::get<double>(pidFunction(part));
 
  692     double deuteronID_noSVD(
const Particle* part)
 
  695       static Manager::FunctionPtr pidFunction =
 
  696         pidProbabilityExpert({
"1000010020", 
"CDC", 
"TOP", 
"ARICH", 
"ECL", 
"KLM"});
 
  697       return std::get<double>(pidFunction(part));
 
  700     double binaryPID_noSVD(
const Particle* part, 
const std::vector<double>& arguments)
 
  703       if (arguments.size() != 2) {
 
  704         B2ERROR(
"The variable binaryPID_noSVD needs exactly two arguments: the PDG codes of two hypotheses.");
 
  705         return std::numeric_limits<float>::quiet_NaN();;
 
  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));
 
  714     double electronID_noTOP(
const Particle* part)
 
  717       static Manager::FunctionPtr pidFunction =
 
  718         pidProbabilityExpert({
"11", 
"SVD", 
"CDC", 
"ARICH", 
"ECL", 
"KLM"});
 
  719       return std::get<double>(pidFunction(part));
 
  722     double binaryElectronID_noTOP(
const Particle* part, 
const std::vector<double>& arguments)
 
  725       if (arguments.size() != 1) {
 
  726         B2ERROR(
"The variable binaryElectronID_noTOP needs exactly one argument: the PDG code of the test hypothesis.");
 
  727         return std::numeric_limits<float>::quiet_NaN();;
 
  730       int pdgCodeHyp = Const::electron.getPDGCode();
 
  731       int pdgCodeTest = std::abs(
int(std::lround(arguments[0])));
 
  733       const auto var = 
"pidPairProbabilityExpert(" + std::to_string(pdgCodeHyp) + 
", " +
 
  734                        std::to_string(pdgCodeTest) + 
", SVD, CDC, ARICH, ECL, KLM)";
 
  736       return std::get<double>(Manager::Instance().getVariable(var)->
function(part));
 
  739     double electronID_noSVD_noTOP(
const Particle* part)
 
  742       static Manager::FunctionPtr pidFunction =
 
  743         pidProbabilityExpert({
"11", 
"CDC", 
"ARICH", 
"ECL", 
"KLM"});
 
  744       return std::get<double>(pidFunction(part));
 
  747     double binaryElectronID_noSVD_noTOP(
const Particle* part, 
const std::vector<double>& arguments)
 
  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 std::numeric_limits<float>::quiet_NaN();;
 
  755       int pdgCodeHyp = Const::electron.getPDGCode();
 
  756       int pdgCodeTest = std::abs(
int(std::lround(arguments[0])));
 
  758       const auto var = 
"pidPairProbabilityExpert(" + std::to_string(pdgCodeHyp) + 
", " +
 
  759                        std::to_string(pdgCodeTest) + 
", CDC, ARICH, ECL, KLM)";
 
  761       return std::get<double>(Manager::Instance().getVariable(var)->
function(part));
 
  765     double pionID_noARICHwoECL(
const Particle* part)
 
  768       const ECLCluster* cluster = part->getECLCluster();
 
  770         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  771         if (!pid) 
return std::numeric_limits<float>::quiet_NaN();
 
  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));
 
  782     double kaonID_noARICHwoECL(
const Particle* part)
 
  785       const ECLCluster* cluster = part->getECLCluster();
 
  787         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  788         if (!pid) 
return std::numeric_limits<float>::quiet_NaN();
 
  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));
 
  799     double binaryPID_noARICHwoECL(
const Particle* part, 
const std::vector<double>& arguments)
 
  802       if (arguments.size() != 2) {
 
  803         B2ERROR(
"The variable binaryPID_noARICHwoECL needs exactly two arguments: the PDG codes of two hypotheses.");
 
  804         return std::numeric_limits<float>::quiet_NaN();;
 
  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));
 
  811       const ECLCluster* cluster = part->getECLCluster();
 
  813         const PIDLikelihood* pid = part->getPIDLikelihood();
 
  814         if (!pid) 
return std::numeric_limits<float>::quiet_NaN();
 
  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));
 
  823       return binaryPID(part, arguments);
 
  829     double antineutronID(
const Particle* particle)
 
  831       if (particle->hasExtraInfo(
"nbarID")) {
 
  832         return particle->getExtraInfo(
"nbarID");
 
  834         if (particle->getPDGCode() == -Const::neutron.getPDGCode()) {
 
  835           B2WARNING(
"The extraInfo nbarID is not registered! \n" 
  836                     "Please use function getNbarIDMVA in modularAnalysis.");
 
  838         return std::numeric_limits<float>::quiet_NaN();
 
  842     Manager::FunctionPtr pidChargedBDTScore(
const std::vector<std::string>& arguments)
 
  844       if (arguments.size() != 2) {
 
  845         B2ERROR(
"Need exactly two arguments for pidChargedBDTScore: pdgCodeHyp, detector");
 
  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).");
 
  856       Const::ChargedStable hypType = Const::ChargedStable(hypPdgId);
 
  858       std::vector<std::string> detectors(arguments.begin() + 1, arguments.end());
 
  859       Const::PIDDetectorSet detectorSet = parseDetectorsChargedBDT(detectors);
 
  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)
 
  865           name += 
"_" + std::to_string(detector);
 
  867         return (part->hasExtraInfo(name)) ? part->getExtraInfo(name) : std::numeric_limits<float>::quiet_NaN();
 
  872     Manager::FunctionPtr pidPairChargedBDTScore(
const std::vector<std::string>& arguments)
 
  874       if (arguments.size() != 3) {
 
  875         B2ERROR(
"Need exactly three arguments for pidPairChargedBDTScore: pdgCodeHyp, pdgCodeTest, detector.");
 
  879       int hypPdgId, testPdgId;
 
  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).");
 
  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).");
 
  892       Const::ChargedStable hypType = Const::ChargedStable(hypPdgId);
 
  893       Const::ChargedStable testType = Const::ChargedStable(testPdgId);
 
  895       std::vector<std::string> detectors(arguments.begin() + 2, arguments.end());
 
  896       Const::PIDDetectorSet detectorSet = parseDetectorsChargedBDT(detectors);
 
  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)
 
  902           name += 
"_" + std::to_string(detector);
 
  904         return (part->hasExtraInfo(name)) ? part->getExtraInfo(name) : std::numeric_limits<float>::quiet_NaN();
 
  909     double mostLikelyPDG(
const Particle* part, 
const std::vector<double>& arguments)
 
  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 std::numeric_limits<double>::quiet_NaN();
 
  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;
 
  919         copy(arguments.begin(), arguments.end(), prob);
 
  922       auto* pid = part->getPIDLikelihood();
 
  923       if (!pid) 
return std::numeric_limits<double>::quiet_NaN();
 
  924       return pid->getMostLikely(prob).getPDGCode();
 
  927     bool isMostLikely(
const Particle* part, 
const std::vector<double>& arguments)
 
  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");
 
  933       return mostLikelyPDG(part, arguments) == abs(part->getPDGCode());
 
  936     Manager::FunctionPtr weightedElectronID(
const std::vector<std::string>& arguments)
 
  939       if (arguments.size() == 0) {
 
  940         varName = 
"pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 11, ALL)";
 
  941       } 
else if (arguments.size() == 1) {
 
  942         varName = 
"pidWeightedProbabilityExpert(" + arguments[0] + 
", 11, ALL)";
 
  944         B2ERROR(
"Need zero or one argument for weightedElectronID");
 
  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));
 
  955     Manager::FunctionPtr weightedMuonID(
const std::vector<std::string>& arguments)
 
  958       if (arguments.size() == 0) {
 
  959         varName = 
"pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 13, ALL)";
 
  960       } 
else if (arguments.size() == 1) {
 
  961         varName = 
"pidWeightedProbabilityExpert(" + arguments[0] + 
", 13, ALL)";
 
  963         B2ERROR(
"Need zero or one argument for weightedMuonID");
 
  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));
 
  974     Manager::FunctionPtr weightedPionID(
const std::vector<std::string>& arguments)
 
  977       if (arguments.size() == 0) {
 
  978         varName = 
"pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 211, ALL)";
 
  979       } 
else if (arguments.size() == 1) {
 
  980         varName = 
"pidWeightedProbabilityExpert(" + arguments[0] + 
", 211, ALL)";
 
  982         B2ERROR(
"Need zero or one argument for weightedPionID");
 
  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));
 
  993     Manager::FunctionPtr weightedKaonID(
const std::vector<std::string>& arguments)
 
  996       if (arguments.size() == 0) {
 
  997         varName = 
"pidWeightedProbabilityExpert(PIDCalibrationWeightMatrix, 321, ALL)";
 
  998       } 
else if (arguments.size() == 1) {
 
  999         varName = 
"pidWeightedProbabilityExpert(" + arguments[0] + 
", 321, ALL)";
 
 1001         B2ERROR(
"Need zero or one argument for weightedKaonID");
 
 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));
 
 1012     Manager::FunctionPtr weightedProtonID(
const std::vector<std::string>& arguments)
 
 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)";
 
 1020         B2ERROR(
"Need zero or one argument for weightedProtonID");
 
 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));
 
 1031     Manager::FunctionPtr weightedDeuteronID(
const std::vector<std::string>& arguments)
 
 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)";
 
 1039         B2ERROR(
"Need zero or one argument for weightedDeuteronID");
 
 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));
 
 1051     double pionIDNN(
const Particle* particle)
 
 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));
 
 1059     double kaonIDNN(
const Particle* particle)
 
 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));
 
 1072     double muIDBelle(
const Particle* particle)
 
 1074       const PIDLikelihood* pid = particle->getPIDLikelihood();
 
 1075       if (!pid) 
return 0.5; 
 
 1077       if (pid->isAvailable(Const::KLM))
 
 1078         return exp(pid->getLogL(Const::muon, Const::KLM));
 
 1083     double muIDBelleQuality(
const Particle* particle)
 
 1085       const PIDLikelihood* pid = particle->getPIDLikelihood();
 
 1088       return pid->isAvailable(Const::KLM);
 
 1091     double atcPIDBelle(
const Particle* particle,  
const std::vector<double>& sigAndBkgHyp)
 
 1093       int sigHyp = int(std::lround(sigAndBkgHyp[0]));
 
 1094       int bkgHyp = int(std::lround(sigAndBkgHyp[1]));
 
 1096       const PIDLikelihood* pid = particle->getPIDLikelihood();
 
 1097       if (!pid) 
return 0.5; 
 
 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));
 
 1104       if (acc_sig + acc_bkg  > 0.0)
 
 1105         acc = acc_sig / (acc_sig + acc_bkg);
 
 1109       double tof_sig = exp(pid->getLogL(hypothesisConversion(sigHyp), set));
 
 1110       double tof_bkg = exp(pid->getLogL(hypothesisConversion(bkgHyp), set));
 
 1112       double tof_all = tof_sig + tof_bkg;
 
 1114         tof = tof_sig / tof_all;
 
 1115         if (tof < 0.001) tof = 0.001;
 
 1116         if (tof > 0.999) tof = 0.999;
 
 1121       double cdc_sig = exp(pid->getLogL(hypothesisConversion(sigHyp), set));
 
 1122       double cdc_bkg = exp(pid->getLogL(hypothesisConversion(bkgHyp), set));
 
 1124       double cdc_all = cdc_sig + cdc_bkg;
 
 1126         cdc = cdc_sig / cdc_all;
 
 1127         if (cdc < 0.001) cdc = 0.001;
 
 1128         if (cdc > 0.999) cdc = 0.999;
 
 1132       double pid_sig = acc * tof * cdc;
 
 1133       double pid_bkg = (1. - acc) * (1. - tof) * (1. - cdc);
 
 1135       return pid_sig / (pid_sig + pid_bkg);
 
 1139     double eIDBelle(
const Particle* part)
 
 1141       const PIDLikelihood* pid = part->getPIDLikelihood();
 
 1142       if (!pid) 
return 0.5; 
 
 1144       Const::PIDDetectorSet set = Const::ECL;
 
 1145       return pid->getProbability(Const::electron, Const::pion, set);
 
 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. 
 1177 - 1  signal(antineutron) like 
 1179 - -1 invalid using this PID due to some ECL variables used unavailable 
 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"); 
 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);
 
 1220     REGISTER_METAVARIABLE(
"weightedElectronID(weightMatrixName)", weightedElectronID,
 
 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. 
 1227                           Manager::VariableDataType::c_double); 
 1228     REGISTER_METAVARIABLE("weightedMuonID(weightMatrixName)", weightedMuonID,
 
 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. 
 1235                           Manager::VariableDataType::c_double); 
 1236     REGISTER_METAVARIABLE("weightedPionID(weightMatrixName)", weightedPionID,
 
 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. 
 1243                           Manager::VariableDataType::c_double); 
 1244     REGISTER_METAVARIABLE("weightedKaonID(weightMatrixName)", weightedKaonID,
 
 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. 
 1251                           Manager::VariableDataType::c_double); 
 1252     REGISTER_METAVARIABLE("weightedProtonID(weightMatrixName)", weightedProtonID,
 
 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. 
 1259                           Manager::VariableDataType::c_double); 
 1260     REGISTER_METAVARIABLE("weightedDeuteronID(weightMatrixName)", weightedDeuteronID,
 
 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. 
 1267                           Manager::VariableDataType::c_double); 
 1270     REGISTER_VARIABLE("pionIDNN", pionIDNN,
 
 1272 pion identification probability as calculated from the PID neural network. 
 1274     REGISTER_VARIABLE("kaonIDNN", kaonIDNN,
 
 1276 kaon identification probability as calculated from the PID neural network. 
 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,
 
 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"); 
 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);
 
 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). 
 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). 
 1344 .. warning:: The behaviour is different from Belle II PID variables which typically return NaN in case of error. 
 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. 
 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). 
 1354 .. warning:: The behaviour is different from Belle II PID variables which typically return NaN in case of error. 
Abstract base class for different kinds of events.