Belle II Software  release-05-02-19
VariablesToNtupleModule.cc
1 /**************************************************************************
2 * BASF2 (Belle Analysis Framework 2) *
3 * Copyright(C) 2013-2018 Belle II Collaboration *
4 * *
5 * Author: The Belle II Collaboration *
6 * Contributors: Christian Pulvermacher *
7 * Thomas Keck *
8 * Simon Wehle *
9 * Sam Cunliffe *
10 * *
11 * This software is provided "as is" without any warranty. *
12 **************************************************************************/
13 
14 #include <analysis/modules/VariablesToNtuple/VariablesToNtupleModule.h>
15 
16 // analysis
17 #include <analysis/dataobjects/ParticleList.h>
18 #include <analysis/VariableManager/Manager.h>
19 #include <analysis/VariableManager/Utility.h>
20 
21 // framework
22 #include <framework/logging/Logger.h>
23 #include <framework/pcore/ProcHandler.h>
24 #include <framework/core/ModuleParam.templateDetails.h>
25 
26 // framework - root utilities
27 #include <framework/utilities/MakeROOTCompatible.h>
28 #include <framework/utilities/RootFileCreationManager.h>
29 
30 #include <cmath>
31 
32 using namespace std;
33 using namespace Belle2;
34 
35 // Register module in the framework
36 REG_MODULE(VariablesToNtuple)
37 
38 
40  Module(), m_tree("", DataStore::c_Persistent)
41 {
42  //Set module properties
43  setDescription("Calculate variables specified by the user for a given ParticleList and save them into a TNtuple. The TNtuple is candidate-based, meaning that the variables of each candidate are saved separate rows.");
44  setPropertyFlags(c_ParallelProcessingCertified | c_TerminateInAllProcesses);
45 
46  vector<string> emptylist;
47  addParam("particleList", m_particleList,
48  "Name of particle list with reconstructed particles. If no list is provided the variables are saved once per event (only possible for event-type variables)",
49  std::string(""));
50  addParam("variables", m_variables,
51  "List of variables (or collections) to save. Variables are taken from Variable::Manager, and are identical to those available to e.g. ParticleSelector.",
52  emptylist);
53 
54  addParam("fileName", m_fileName, "Name of ROOT file for output.", string("VariablesToNtuple.root"));
55  addParam("treeName", m_treeName, "Name of the NTuple in the saved file.", string("ntuple"));
56 
57  std::tuple<std::string, std::map<int, unsigned int>> default_sampling{"", {}};
58  addParam("sampling", m_sampling,
59  "Tuple of variable name and a map of integer values and inverse sampling rate. E.g. (signal, {1: 0, 0:10}) selects all signal candidates and every 10th background candidate.",
60  default_sampling);
61 }
62 
63 void VariablesToNtupleModule::initialize()
64 {
65  m_eventMetaData.isRequired();
66  if (not m_particleList.empty())
67  StoreObjPtr<ParticleList>().isRequired(m_particleList);
68 
69 
70  // Initializing the output root file
71  if (m_fileName.empty()) {
72  B2FATAL("Output root file name is not set. Please set a vaild root output file name (\"fileName\" module parameter).");
73  }
74  // See if there is already a file in which case add a new tree to it ...
75  // otherwise create a new file (all handled by framework)
76  m_file = RootFileCreationManager::getInstance().getFile(m_fileName);
77  if (!m_file) {
78  B2ERROR("Could not create file \"" << m_fileName <<
79  "\". Please set a vaild root output file name (\"fileName\" module parameter).");
80  return;
81  }
82 
83  TDirectory::TContext directoryGuard(m_file.get());
84 
85  // check if TTree with that name already exists
86  if (m_file->Get(m_treeName.c_str())) {
87  B2FATAL("Tree with the name \"" << m_treeName
88  << "\" already exists in the file \"" << m_fileName << "\"\n"
89  << "\nYou probably want to either set the output fileName or the treeName to something else:\n\n"
90  << " from modularAnalysis import variablesToNtuple\n"
91  << " variablesToNtuple('pi+:all', ['p'], treename='pions', filename='variablesToNtuple.root')\n"
92  << " variablesToNtuple('gamma:all', ['p'], treename='photons', filename='variablesToNtuple.root') # two trees, same file\n"
93  << "\n == Or ==\n"
94  << " from modularAnalysis import variablesToNtuple\n"
95  << " variablesToNtuple('pi+:all', ['p'], filename='pions.root')\n"
96  << " variablesToNtuple('gamma:all', ['p'], filename='photons.root') # two files\n"
97  );
98  return;
99  }
100 
101  // set up tree and register it in the datastore
102  m_tree.registerInDataStore(m_fileName + m_treeName, DataStore::c_DontWriteOut);
103  m_tree.construct(m_treeName.c_str(), "");
104  m_tree->get().SetCacheSize(100000);
105 
106  // declare counter branches - pass through variable list, remove counters added by user
107  m_tree->get().Branch("__experiment__", &m_experiment, "__experiment__/I");
108  m_tree->get().Branch("__run__", &m_run, "__run__/I");
109  m_tree->get().Branch("__event__", &m_event, "__event__/I");
110  if (not m_particleList.empty()) {
111  m_tree->get().Branch("__candidate__", &m_candidate, "__candidate__/I");
112  m_tree->get().Branch("__ncandidates__", &m_ncandidates, "__ncandidates__/I");
113  }
114  for (const auto& variable : m_variables)
115  if (Variable::isCounterVariable(variable)) {
116  B2WARNING("The counter '" << variable
117  << "' is handled automatically by VariablesToNtuple, you don't need to add it.");
118  }
119 
120  // declare branches and get the variable strings
121  m_variables = Variable::Manager::Instance().resolveCollections(m_variables);
122  m_branchAddresses.resize(m_variables.size() + 1);
123  m_tree->get().Branch("__weight__", &m_branchAddresses[0], "__weight__/D");
124  size_t enumerate = 1;
125  for (const string& varStr : m_variables) {
126  string branchName = makeROOTCompatible(varStr);
127  m_tree->get().Branch(branchName.c_str(), &m_branchAddresses[enumerate], (branchName + "/D").c_str());
128 
129  // also collection function pointers
130  const Variable::Manager::Var* var = Variable::Manager::Instance().getVariable(varStr);
131  if (!var) {
132  B2ERROR("Variable '" << varStr << "' is not available in Variable::Manager!");
133  } else {
134  if (m_particleList.empty() && var->description.find("[Eventbased]") == string::npos) {
135  B2ERROR("Variable '" << varStr << "' is not an event-based variable, "
136  "but you are using VariablesToNtuple without a decay string, i.e. in the event-wise mode.\n"
137  "If you have created an event-based alias you can wrap your alias with `eventCached` to "
138  "declare it as event based, which avoids this error.\n\n"
139  "vm.addAlias('myAliasName', 'eventCached(myAlias)')");
140  continue;
141  }
142  m_functions.push_back(var->function);
143  }
144  enumerate++;
145  }
146  m_tree->get().SetBasketSize("*", 1600);
147 
148  m_sampling_name = std::get<0>(m_sampling);
149  m_sampling_rates = std::get<1>(m_sampling);
150 
151  if (m_sampling_name != "") {
152  m_sampling_variable = Variable::Manager::Instance().getVariable(m_sampling_name);
153  if (m_sampling_variable == nullptr) {
154  B2FATAL("Couldn't find sample variable " << m_sampling_name << " via the Variable::Manager. Check the name!");
155  }
156  for (const auto& pair : m_sampling_rates)
157  m_sampling_counts[pair.first] = 0;
158  } else {
159  m_sampling_variable = nullptr;
160  }
161 }
162 
163 
164 float VariablesToNtupleModule::getInverseSamplingRateWeight(const Particle* particle)
165 {
166  if (m_sampling_variable == nullptr)
167  return 1.0;
168 
169  long target = std::lround(m_sampling_variable->function(particle));
170  if (m_sampling_rates.find(target) != m_sampling_rates.end() and m_sampling_rates[target] > 0) {
171  m_sampling_counts[target]++;
172  if (m_sampling_counts[target] % m_sampling_rates[target] != 0)
173  return 0;
174  else {
175  m_sampling_counts[target] = 0;
176  return m_sampling_rates[target];
177  }
178  }
179  return 1.0;
180 }
181 
182 void VariablesToNtupleModule::event()
183 {
184  m_event = m_eventMetaData->getEvent();
185  m_run = m_eventMetaData->getRun();
186  m_experiment = m_eventMetaData->getExperiment();
187 
188  if (m_particleList.empty()) {
189  m_branchAddresses[0] = getInverseSamplingRateWeight(nullptr);
190  if (m_branchAddresses[0] > 0) {
191  for (unsigned int iVar = 0; iVar < m_variables.size(); iVar++) {
192  m_branchAddresses[iVar + 1] = m_functions[iVar](nullptr);
193  }
194  m_tree->get().Fill();
195  }
196 
197  } else {
198  StoreObjPtr<ParticleList> particlelist(m_particleList);
199  m_ncandidates = particlelist->getListSize();
200  for (unsigned int iPart = 0; iPart < m_ncandidates; iPart++) {
201  m_candidate = iPart;
202  const Particle* particle = particlelist->getParticle(iPart);
203  m_branchAddresses[0] = getInverseSamplingRateWeight(particle);
204  if (m_branchAddresses[0] > 0) {
205  for (unsigned int iVar = 0; iVar < m_variables.size(); iVar++) {
206  m_branchAddresses[iVar + 1] = m_functions[iVar](particle);
207  }
208  m_tree->get().Fill();
209  }
210  }
211  }
212 }
213 
214 void VariablesToNtupleModule::terminate()
215 {
216  if (!ProcHandler::parallelProcessingUsed() or ProcHandler::isOutputProcess()) {
217  B2INFO("Writing NTuple " << m_treeName);
218  TDirectory::TContext directoryGuard(m_file.get());
219  m_tree->write(m_file.get());
220 
221  const bool writeError = m_file->TestBit(TFile::kWriteError);
222  m_file.reset();
223  if (writeError) {
224  B2FATAL("A write error occured while saving '" << m_fileName << "', please check if enough disk space is available.");
225  }
226  }
227 }
Belle2::Variable::Manager::Var
A variable returning a floating-point value for a given Particle.
Definition: Manager.h:137
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::makeROOTCompatible
std::string makeROOTCompatible(std::string str)
Remove special characters that ROOT dislikes in branch names, e.g.
Definition: MakeROOTCompatible.cc:74
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::StoreObjPtr
Type-safe access to single objects in the data store.
Definition: ParticleList.h:33
Belle2::Particle
Class to store reconstructed particles.
Definition: Particle.h:77
Belle2::VariablesToNtupleModule
Module to calculate variables specified by the user for a given ParticleList and save them into a ROO...
Definition: VariablesToNtupleModule.h:49
Belle2::DataStore
In the store you can park objects that have to be accessed by various modules.
Definition: DataStore.h:53