76 std::map<int, std::string> debugStr = {
86 decayDescriptor.init(decayString);
87 auto pListName = decayDescriptor.getMother()->getFullName();
89 unsigned short m_nSelectedDaughters = decayDescriptor.getSelectionNames().size();
93 B2FATAL(
"ParticleList: " << pListName <<
" could not be found. Aborting...");
96 auto pListSize = pList->getListSize();
98 B2DEBUG(11,
"ParticleList: " << pList->getParticleListName() <<
" - N = " << pListSize <<
" particles.");
100 const auto nTargetParticles = (m_nSelectedDaughters == 0) ? pListSize : pListSize * m_nSelectedDaughters;
103 std::vector<int> pdgs;
104 if (m_nSelectedDaughters == 0) {
105 pdgs.push_back(pList->getPDGCode());
107 pdgs = decayDescriptor.getSelectionPDGCodes();
109 for (
auto pdg : pdgs) {
112 B2FATAL(
"PDG: " << pdg <<
" of ParticleList: " << pListName <<
113 " is not that of a valid particle in Const::chargedStableSet! Aborting...");
116 std::vector<const Particle*> targetParticles;
117 if (m_nSelectedDaughters > 0) {
118 for (
unsigned int iPart(0); iPart < pListSize; ++iPart) {
119 auto* iParticle = pList->getParticle(iPart);
120 auto daughters = decayDescriptor.getSelectionParticles(iParticle);
121 for (
auto* iDaughter : daughters) {
122 targetParticles.push_back(iDaughter);
127 for (
unsigned int ipart(0); ipart < nTargetParticles; ++ipart) {
129 const Particle* particle = (m_nSelectedDaughters > 0) ? targetParticles[ipart] : pList->getParticle(ipart);
133 const ECLCluster* eclCluster = particle->getECLCluster();
135 B2DEBUG(11,
"\nParticle [" << ipart <<
"] has invalid Track-ECLCluster relation, skip MVA application...");
144 auto p = particle->getP();
147 if (std::isnan(theta) or std::isnan(p) or std::isnan(charge)) {
148 B2DEBUG(11,
"\nParticle [" << ipart <<
"] has invalid input variable, skip MVA application..." <<
149 " polar angle: " << theta <<
", p: " << p <<
", charge: " << charge);
153 int idx_theta, idx_p, idx_charge;
156 auto hasMatch = std::isnormal(Variable::eclClusterTrackMatched(particle));
158 debugStr[11] +=
"\n";
159 debugStr[11] += (
"Particle [" + std::to_string(ipart) +
"]\n");
160 debugStr[11] += (
"Has ECL cluster match? " + std::to_string(hasMatch) +
"\n");
161 debugStr[11] += (
"polar angle: " + thVarName +
" = " + std::to_string(theta) +
" [rad]\n");
162 debugStr[11] += (
"p = " + std::to_string(p) +
" [GeV/c]\n");
164 debugStr[11] += (
"charge = " + std::to_string(charge) +
"\n");
166 debugStr[11] += (
"Is brems corrected ? " + std::to_string(particle->hasExtraInfo(
"bremsCorrected")) +
"\n");
167 debugStr[11] += (
"Weightfile idx = " + std::to_string(index) +
" - (polar angle, p, charge) = (" + std::to_string(
168 idx_theta) +
", " + std::to_string(idx_p) +
", " +
169 std::to_string(idx_charge) +
")\n");
171 debugStr[11] += (
"Category cut: " +
m_cuts.at(index)->decompile() +
"\n");
174 B2DEBUG(11, debugStr[11]);
175 debugStr[11].clear();
179 if (!
m_cuts.at(index)->check(particle)) {
180 B2DEBUG(11,
"\nParticle [" << ipart <<
"] didn't pass MVA category cut, skip MVA application...");
187 debugStr[11] +=
"\n";
188 debugStr[11] +=
"MVA variables:\n";
191 for (
unsigned int ivar(0); ivar < nvars; ++ivar) {
195 double var = std::numeric_limits<double>::quiet_NaN();
196 auto var_result = varobj->function(particle);
197 if (std::holds_alternative<double>(var_result)) {
198 var = std::get<double>(var_result);
199 }
else if (std::holds_alternative<int>(var_result)) {
200 var = std::get<int>(var_result);
201 }
else if (std::holds_alternative<bool>(var_result)) {
202 var = std::get<bool>(var_result);
204 B2ERROR(
"Variable '" << varobj->name <<
"' has wrong data type! It must be one of double, integer, or bool.");
209 var = (std::isnan(var)) ? -999.0 : var;
212 debugStr[11] += (
"\tvar[" + std::to_string(ivar) +
"] : " + varobj->name +
" = " + std::to_string(var) +
"\n");
218 B2DEBUG(11, debugStr[11]);
219 debugStr[11].clear();
224 debugStr[12] +=
"\n";
225 debugStr[12] +=
"MVA spectators:\n";
228 for (
unsigned int ispec(0); ispec < nspecs; ++ispec) {
232 double spec = std::numeric_limits<double>::quiet_NaN();
233 auto spec_result = specobj->function(particle);
234 if (std::holds_alternative<double>(spec_result)) {
235 spec = std::get<double>(spec_result);
236 }
else if (std::holds_alternative<int>(spec_result)) {
237 spec = std::get<int>(spec_result);
238 }
else if (std::holds_alternative<bool>(spec_result)) {
239 spec = std::get<bool>(spec_result);
241 B2ERROR(
"Variable '" << specobj->name <<
"' has wrong data type! It must be one of double, integer, or bool.");
244 debugStr[12] += (
"\tspec[" + std::to_string(ispec) +
"] : " + specobj->name +
" = " + std::to_string(spec) +
"\n");
246 m_datasets.at(index)->m_spectators[ispec] = spec;
250 B2DEBUG(12, debugStr[12]);
251 debugStr[12].clear();
257 debugStr[11] +=
"\n";
258 debugStr[12] +=
"\n";
259 debugStr[11] +=
"MVA response:\n";
261 std::string score_varname(
"");
263 std::vector<float> scores =
m_experts.at(index)->applyMulticlass(*
m_datasets.at(index))[0];
265 for (
unsigned int classID(0); classID <
m_classes.size(); ++classID) {
267 const std::string className(
m_classes.at(classID));
269 score_varname =
"pidChargedBDTScore_" + className;
272 score_varname +=
"_" + std::to_string(Const::ECL);
275 score_varname +=
"_" + std::to_string(det);
279 debugStr[11] += (
"\tclass[" + std::to_string(classID) +
"] = " + className +
" - score = " + std::to_string(
280 scores[classID]) +
"\n");
281 debugStr[12] += (
"\textraInfo: " + score_varname +
"\n");
284 m_particles[particle->getArrayIndex()]->writeExtraInfo(score_varname, scores[classID]);
288 B2DEBUG(11, debugStr[11]);
289 B2DEBUG(12, debugStr[12]);
290 debugStr[11].clear();
291 debugStr[12].clear();
305 std::string epsilon(
"1e-8");
307 std::map<std::string, std::string> aliasesLegacy;
309 aliasesLegacy.insert(std::make_pair(
"__event__",
"evtNum"));
316 aliasesLegacy.insert(std::make_pair(
"missingLogL_" + detName,
"pidMissingProbabilityExpert(" + detName +
")"));
320 std::string alias = fullName +
"ID_" + detName;
321 std::string var =
"pidProbabilityExpert(" + std::to_string(pdgId) +
", " + detName +
")";
322 std::string aliasLogTrf = alias +
"_LogTransfo";
323 std::string varLogTrf =
"formula(-1. * log10(formula(((1. - " + alias +
") + " + epsilon +
") / (" + alias +
" + " + epsilon +
326 aliasesLegacy.insert(std::make_pair(alias, var));
327 aliasesLegacy.insert(std::make_pair(aliasLogTrf, varLogTrf));
329 if (it.getIndex() == 0) {
330 aliasLogTrf = fullName +
"ID_LogTransfo";
331 varLogTrf =
"formula(-1. * log10(formula(((1. - " + fullName +
"ID) + " + epsilon +
") / (" + fullName +
"ID + " + epsilon +
333 aliasesLegacy.insert(std::make_pair(aliasLogTrf, varLogTrf));
340 B2INFO(
"Setting hard-coded aliases for the ChargedPidMVA algorithm.");
342 std::string debugStr(
"\n");
343 for (
const auto& [alias, variable] : aliasesLegacy) {
344 debugStr += (alias +
" --> " + variable +
"\n");
346 B2ERROR(
"Something went wrong with setting alias: " << alias <<
" for variable: " << variable);
349 B2DEBUG(10, debugStr);
388 ". Load supported MVA interfaces for multi-class charged particle identification...");
397 B2INFO(
"\tLoading weightfiles from the payload class.");
400 auto nfiles = serialized_weightfiles->size();
402 B2INFO(
"\tConstruct the MVA experts and datasets from N = " << nfiles <<
" weightfiles...");
412 for (
unsigned int idx(0); idx < nfiles; idx++) {
414 B2DEBUG(12,
"\t\tweightfile[" << idx <<
"]");
417 std::stringstream ss(serialized_weightfiles->at(idx));
421 weightfile.getOptions(general_options);
425 m_variables[idx] = manager.getVariables(general_options.m_variables);
426 m_spectators[idx] = manager.getVariables(general_options.m_spectators);
428 B2DEBUG(12,
"\t\tRetrieved N = " << general_options.m_variables.size()
429 <<
" variables, N = " << general_options.m_spectators.size()
433 m_experts[idx] = supported_interfaces[general_options.m_method]->getExpert();
436 B2DEBUG(12,
"\t\tweightfile loaded successfully into expert[" << idx <<
"]!");
439 std::vector<float> v(general_options.m_variables.size(), 0.0);
440 std::vector<float> s(general_options.m_spectators.size(), 0.0);
441 m_datasets[idx] = std::make_unique<MVA::SingleDataset>(general_options, v, 1.0, s);
443 B2DEBUG(12,
"\t\tdataset[" << idx <<
"] created successfully!");
447 const auto cutstr = (!cuts->empty()) ? cuts->at(idx) :
"";
450 B2DEBUG(12,
"\t\tcut[" << idx <<
"] created successfully!");
458 weightfile.getOptions(specific_options);
460 if (specific_options.m_classes.empty()) {
461 B2FATAL(
"MVA::SpecificOptions of weightfile[" << idx <<
462 "] has no registered MVA classes! This shouldn't happen in multi-class mode. Aborting...");
466 for (
const auto& cls : specific_options.m_classes) {
Class to store reconstructed particles.