Belle II Software  release-06-02-00
Manager.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 #include <analysis/VariableManager/Manager.h>
10 #include <analysis/dataobjects/Particle.h>
11 
12 #include <framework/logging/Logger.h>
13 #include <framework/utilities/Conversion.h>
14 #include <framework/utilities/GeneralCut.h>
15 
16 #include <boost/algorithm/string.hpp>
17 
18 #include <string>
19 #include <regex>
20 #include <set>
21 
22 using namespace Belle2;
23 
24 Variable::Manager::~Manager() = default;
26 {
27  static Variable::Manager v;
28  return v;
29 }
30 
32 {
33  // resolve aliases. Aliases might point to other aliases so we need to keep a
34  // set of what we have seen so far to avoid running into infinite loops
35  std::set<std::string> aliasesSeen;
36  for (auto aliasIter = m_alias.find(name); aliasIter != m_alias.end(); aliasIter = m_alias.find(name)) {
37  const auto [it, added] = aliasesSeen.insert(name);
38  if (!added) {
39  B2FATAL("Encountered a loop in the alias definitions between the aliases "
40  << boost::algorithm::join(aliasesSeen, ", "));
41  }
42  name = aliasIter->second;
43  }
44  auto mapIter = m_variables.find(name);
45  if (mapIter == m_variables.end()) {
46  if (!createVariable(name)) return nullptr;
47  mapIter = m_variables.find(name);
48  if (mapIter == m_variables.end()) return nullptr;
49  }
50  return mapIter->second.get();
51 }
52 
53 std::vector<const Variable::Manager::Var*> Variable::Manager::getVariables(const std::vector<std::string>& variables)
54 {
55 
56  std::vector<const Variable::Manager::Var*> variable_pointers;
57  for (auto& variable : variables) {
58  const Var* x = getVariable(variable);
59  if (x == nullptr) {
60  B2WARNING("Couldn't find variable " << variable << " via the Variable::Manager. Check the name!");
61  }
62  variable_pointers.push_back(x);
63  }
64  return variable_pointers;
65 
66 }
67 
68 
69 bool Variable::Manager::addAlias(const std::string& alias, const std::string& variable)
70 {
71 
72  assertValidName(alias);
73 
74  if (m_alias.find(alias) != m_alias.end()) {
75  if (variable == m_alias[alias]) { return true; }
76  B2WARNING("An alias with the name '" << alias << "' exists and is set to '" << m_alias[alias] << "', setting it to '" << variable <<
77  "'. Be aware: only the last alias defined before processing the events will be used!");
78  m_alias[alias] = variable;
79  return true;
80  }
81 
82  if (m_variables.find(alias) != m_variables.end()) {
83  B2ERROR("Variable with the name '" << alias << "' exists already, cannot add it as an alias!");
84  return false;
85  }
86 
87  m_alias.insert(std::make_pair(alias, variable));
88  return true;
89 }
90 
91 
93 {
94  long unsigned int longest_alias_size = 0;
95  for (const auto& a : m_alias) {
96  if (a.first.length() > longest_alias_size) {
97  longest_alias_size = a.first.length();
98  }
99  }
100  B2INFO("=====================================");
101  B2INFO("The following aliases are registered:");
102  for (const auto& a : m_alias) {
103  B2INFO(std::string(a.first, 0, longest_alias_size) << std::string(longest_alias_size - a.first.length(),
104  ' ') << " --> " << a.second);
105  }
106  B2INFO("=====================================");
107 }
108 
109 bool Variable::Manager::addCollection(const std::string& collection, const std::vector<std::string>& variables)
110 {
111 
112  assertValidName(collection);
113 
114  if (m_collection.find(collection) != m_collection.end()) {
115  B2WARNING("Another collection with the name'" << collection << "' is already set! I overwrite it!");
116  m_collection[collection] = variables;
117  return true;
118  }
119 
120  if (m_variables.find(collection) != m_variables.end()) {
121  B2ERROR("Variable with the name '" << collection << "' exists already, won't add it as an collection!");
122  return false;
123  }
124 
125  m_collection.insert(std::make_pair(collection, variables));
126  return true;
127 }
128 
129 
130 std::vector<std::string> Variable::Manager::getCollection(const std::string& collection)
131 {
132 
133  return m_collection[collection];
134 
135 }
136 
137 std::vector<std::string> Variable::Manager::resolveCollections(const std::vector<std::string>& variables)
138 {
139 
140  std::vector<std::string> temp;
141 
142  for (const auto& var : variables) {
143  auto it = m_collection.find(var);
144  if (it != m_collection.end()) {
145  temp.insert(temp.end(), it->second.begin(), it->second.end());
146  } else {
147  temp.push_back(var);
148  }
149  }
150  return temp;
151 
152 }
153 
154 
155 void Variable::Manager::assertValidName(const std::string& name)
156 {
157  const static std::regex allowedNameRegex("^[a-zA-Z0-9_]*$");
158 
159  if (!std::regex_match(name, allowedNameRegex)) {
160  B2FATAL("Variable '" << name <<
161  "' contains forbidden characters! Only alphanumeric characters plus underscores (_) are allowed for variable names.");
162  }
163 }
164 
165 
166 void Variable::Manager::setVariableGroup(const std::string& groupName)
167 {
168  m_currentGroup = groupName;
169 }
170 
171 bool Variable::Manager::createVariable(const std::string& name)
172 {
173  std::match_results<std::string::const_iterator> results;
174 
175  // Check if name is a simple number
176  if (std::regex_match(name, results, std::regex("^([0-9]+\\.?[0-9]*)$"))) {
177  float float_number = std::stof(results[1]);
178  auto func = [float_number](const Particle*) -> double {
179  return float_number;
180  };
181  m_variables[name] = std::make_shared<Var>(name, func, std::string("Returns number ") + name);
182  return true;
183  }
184 
185  // Check if name is a function call
186  if (std::regex_match(name, results, std::regex("^([a-zA-Z0-9_]*)\\((.*)\\)$"))) {
187 
188  std::string functionName = results[1];
189  boost::algorithm::trim(functionName);
190  std::vector<std::string> functionArguments = splitOnDelimiterAndConserveParenthesis(results[2], ',', '(', ')');
191  for (auto& str : functionArguments) {
192  boost::algorithm::trim(str);
193  }
194 
195  // Search function name in parameter variables
196  auto parameterIter = m_parameter_variables.find(functionName);
197  if (parameterIter != m_parameter_variables.end()) {
198 
199  std::vector<double> arguments;
200  for (auto& arg : functionArguments) {
201  double number = 0;
202  number = Belle2::convertString<double>(arg);
203  arguments.push_back(number);
204  }
205  auto pfunc = parameterIter->second->function;
206  auto func = [pfunc, arguments](const Particle * particle) -> double { return pfunc(particle, arguments); };
207  m_variables[name] = std::make_shared<Var>(name, func, parameterIter->second->description, parameterIter->second->group);
208  return true;
209 
210  }
211 
212  // Search function name in meta variables
213  auto metaIter = m_meta_variables.find(functionName);
214  if (metaIter != m_meta_variables.end()) {
215  auto func = metaIter->second->function(functionArguments);
216  m_variables[name] = std::make_shared<Var>(name, func, metaIter->second->description, metaIter->second->group);
217  return true;
218  }
219  }
220 
221  B2FATAL("Encountered bad variable name '" << name << "'. Maybe you misspelled it?");
222  return false;
223 }
224 
225 
227  const std::string& description)
228 {
229  if (!f) {
230  B2FATAL("No function provided for variable '" << name << "'.");
231  }
232 
233  assertValidName(name);
234 
235  auto mapIter = m_variables.find(name);
236  if (mapIter == m_variables.end()) {
237  auto var = std::make_shared<Var>(name, f, description, m_currentGroup);
238  B2DEBUG(19, "Registered Variable " << name);
239  m_variables[name] = var;
240  m_variablesInRegistrationOrder.push_back(var.get());
241  } else {
242  B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
243  }
244 }
245 
247  const std::string& description)
248 {
249  if (!f) {
250  B2FATAL("No function provided for variable '" << name << "'.");
251  }
252 
253  auto mapIter = m_parameter_variables.find(name);
254  if (mapIter == m_parameter_variables.end()) {
255  auto var = std::make_shared<ParameterVar>(name, f, description, m_currentGroup);
256  std::string rawName = name.substr(0, name.find('('));
257  assertValidName(rawName);
258  B2DEBUG(19, "Registered parameter Variable " << rawName);
259  m_parameter_variables[rawName] = var;
260  m_variablesInRegistrationOrder.push_back(var.get());
261  } else {
262  B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
263  }
264 }
265 
267  const std::string& description)
268 {
269  if (!f) {
270  B2FATAL("No function provided for variable '" << name << "'.");
271  }
272 
273  auto mapIter = m_meta_variables.find(name);
274  if (mapIter == m_meta_variables.end()) {
275  auto var = std::make_shared<MetaVar>(name, f, description, m_currentGroup);
276  std::string rawName = name.substr(0, name.find('('));
277  assertValidName(rawName);
278  B2DEBUG(19, "Registered meta Variable " << rawName);
279  m_meta_variables[rawName] = var;
280  m_variablesInRegistrationOrder.push_back(var.get());
281  } else {
282  B2FATAL("A variable named '" << name << "' was already registered! Note that all variables need a unique name!");
283  }
284 }
285 
286 void Variable::Manager::deprecateVariable(const std::string& name, bool make_fatal, const std::string& version,
287  const std::string& description)
288 {
289  auto varIter = m_deprecated.find(name);
290  if (varIter == m_deprecated.end())
291  m_deprecated.insert(std::make_pair(name, std::make_pair(make_fatal, description)));
292  else
293  B2FATAL("There seem to be two calls to deprecate the variable: Please remove one.");
294 
295  auto mapIter = m_variables.find(name);
296  if (mapIter != m_variables.end()) {
297  if (make_fatal) {
298  mapIter->second.get()->extendDescriptionString("\n\n.. warning:: ");
299  } else {
300  mapIter->second.get()->extendDescriptionString("\n\n.. note:: ");
301  }
302  mapIter->second.get()->extendDescriptionString(".. deprecated:: " + version + "\n " + description);
303  }
304 
305 }
306 
307 void Variable::Manager::checkDeprecatedVariable(const std::string& name)
308 {
309  auto varIter = m_deprecated.find(name);
310 
311  if (varIter == m_deprecated.end())
312  return;
313  else {
314  bool make_fatal = varIter->second.first;
315  std::string message = varIter->second.second;
316  if (make_fatal)
317  B2FATAL("Variable " << name << " is deprecated. " << message);
318  else
319  B2WARNING("Variable " << name << " is deprecated. " << message);
320  }
321 }
322 
323 
324 std::vector<std::string> Variable::Manager::getNames() const
325 {
326  std::vector<std::string> names;
327  for (const VarBase* var : m_variablesInRegistrationOrder) {
328  names.push_back(var->name);
329  }
330  return names;
331 }
332 
333 std::vector<std::string> Variable::Manager::getAliasNames() const
334 {
335  std::vector<std::string> names;
336  for (auto al : m_alias) names.push_back(al.first);
337  return names;
338 }
339 
340 double Variable::Manager::evaluate(const std::string& varName, const Particle* p)
341 {
342  const Var* var = getVariable(varName);
343  if (!var) {
344  throw std::runtime_error("Variable::Manager::evaluate(): variable '" + varName + "' not found!");
345  return 0.0; //never reached, suppresses cppcheck warning
346  }
347 
348  return var->function(p);
349 }
Class to store reconstructed particles.
Definition: Particle.h:74
Global list of available variables.
Definition: Manager.h:98
std::function< double(const Particle *, const std::vector< double > &)> ParameterFunctionPtr
parameter functions stored take a const Particle*, const std::vector<double>& and return double.
Definition: Manager.h:110
std::vector< std::string > getAliasNames() const
Return a list of all variable alias names (in reverse order added).
Definition: Manager.cc:333
void registerVariable(const std::string &name, const Manager::FunctionPtr &f, const std::string &description)
Register a variable.
Definition: Manager.cc:226
std::vector< std::string > resolveCollections(const std::vector< std::string > &variables)
Resolve Collection Returns variable names corresponding to the given collection or if it is not a col...
Definition: Manager.cc:137
const Var * getVariable(std::string name)
Get the variable belonging to the given key.
Definition: Manager.cc:31
void deprecateVariable(const std::string &name, bool make_fatal, const std::string &version, const std::string &description)
Make a variable deprecated.
Definition: Manager.cc:286
std::vector< const Belle2::Variable::Manager::VarBase * > getVariables() const
Return list of all variables (in order registered).
Definition: Manager.h:196
static Manager & Instance()
get singleton instance.
Definition: Manager.cc:25
void printAliases()
Print existing aliases.
Definition: Manager.cc:92
bool createVariable(const std::string &name)
Creates and registers a concrete variable (Var) from a MetaVar, ParameterVar or numeric constant.
Definition: Manager.cc:171
void assertValidName(const std::string &name)
Abort with B2FATAL if name is not a valid name for a variable.
Definition: Manager.cc:155
std::function< FunctionPtr(const std::vector< std::string > &)> MetaFunctionPtr
meta functions stored take a const std::vector<std::string>& and return a FunctionPtr.
Definition: Manager.h:112
std::vector< std::string > getNames() const
Return list of all variable names (in order registered).
Definition: Manager.cc:324
std::function< double(const Particle *)> FunctionPtr
NOTE: the python interface is documented manually in analysis/doc/Variables.rst (because we use ROOT ...
Definition: Manager.h:108
void setVariableGroup(const std::string &groupName)
All variables registered after VARIABLE_GROUP(groupName) will be added to this group.
Definition: Manager.cc:166
std::vector< std::string > getCollection(const std::string &collection)
Get Collection Returns variable names corresponding to the given collection.
Definition: Manager.cc:130
bool addAlias(const std::string &alias, const std::string &variable)
Add alias Return true if the alias was successfully added.
Definition: Manager.cc:69
bool addCollection(const std::string &collection, const std::vector< std::string > &variables)
Add collection Return true if the collection was successfully added.
Definition: Manager.cc:109
void checkDeprecatedVariable(const std::string &name)
Check if a variable is deprecated.
Definition: Manager.cc:307
double evaluate(const std::string &varName, const Particle *p)
evaluate variable 'varName' on given Particle.
Definition: Manager.cc:340
std::vector< std::string > splitOnDelimiterAndConserveParenthesis(std::string str, char delimiter, char open, char close)
Split into std::vector on delimiter ignoring delimiters between parenthesis.
Definition: GeneralCut.cc:79
Abstract base class for different kinds of events.
Base class for information common to all types of variables.
Definition: Manager.h:117
A variable returning a floating-point value for a given Particle.
Definition: Manager.h:133