Crombie Tools
LoadTree.h
Go to the documentation of this file.
1 #ifndef CROMBIE_LOADTREE_H
2 #define CROMBIE_LOADTREE_H
3 
4 #include <string>
5 #include <vector>
6 #include <regex>
7 #include <exception>
8 
9 #include "crombie/Types.h"
10 #include "crombie/Misc.h"
11 
12 #include "TFile.h"
13 #include "TTree.h"
14 #include "TBranch.h"
15 #include "TTreeFormula.h"
16 
17 namespace crombie {
18  namespace LoadTree {
19 
20  std::mutex rootlock;
21 
22  class Formulas {
23  public:
24  Formulas (TTree* tree) : tree{tree} {}
25  // I hate ROOT: this causes things to crash
26  /* virtual ~Formulas () { */
27  /* for (auto& form : forms) */
28  /* delete form.second; */
29  /* forms.clear(); */
30  /* } */
31 
32  void add (const std::string& expr) {
33  Debug::Debug(__PRETTY_FUNCTION__, "Adding formula", expr);
34  if (forms.find(expr) == forms.end()) {
35  TTreeFormula* form = new TTreeFormula(expr.data(), expr.data(), tree);
36  forms[expr] = std::make_pair(0.0, form);
37  }
38  }
39  void add (const Types::strings& exprs) {
40  for (auto expr : exprs)
41  add(expr);
42  }
43  template<typename A, typename... Args> void add(const A& expr, Args... args) {
44  add(expr);
45  add(args...);
46  }
47 
48  /// Get a reference to the result for a given formula
49  double& result (const std::string& expr) {
50  auto i_form = forms.find(expr);
51  if (i_form != forms.end())
52  return i_form->second.first;
53  throw std::logic_error{expr + ": Asking for expression that wasn't loaded"};
54  }
55 
56  /// Evaluates all of the formulas and stores the results
57  void eval () {
58  for (auto& form : forms)
59  form.second.first = form.second.second->EvalInstance();
60  }
61 
62  private:
63  TTree* tree;
64  Types::map<std::pair<double, TTreeFormula*>> forms {}; /// Key to map is the full formula
65  };
66 
67  /**
68  Class that holds a loaded tree from a file.
69  The tree name is given in the environment variable `tree`, and defaults to `"events"`.
70  */
71  class Tree {
72  public:
73  /**
74  Constructor of a loaded tree. Note that copy and move constructors won't work because of the TFile member.
75  @param infile is the name of the input file to read
76  @param args is a variable number of arguments that can be of type ``std::string`` or ``crombie::Types::strings>``.
77  */
78  template<typename... Args> Tree(const std::string& infile, Args... args);
79  ~Tree () { file->Close(); delete file; }
80 
81  /// Loads the next event into memory and evaluates all of the formulas
82  bool next();
83 
84  /// Get a reference to the result for a given formula
85  double& result (const std::string& expr) { return forms.result(expr); }
86 
87  /**
88  Get a bare pointer to a TObject inside of this file.
89  @param C is the type of pointer you would like to get
90  @param name is the name of the object, searched for with TFile::Get
91  */
92  template<typename C> C* get(const std::string& name);
93 
94  private:
95  TFile* file; ///< The TFile that is being read
96  TTree* tree; ///< Holds the pointer to the tree
97  long long nentries; ///< Total number of events in the tree
98  long long ientry{0}; ///< Which entry are we on
99  Formulas forms; ///< Holds formulas initialized with
100  };
101 
102  namespace {
103 
104  /// Get the branches needed to evaluate an expression
105  void add_branches(std::set<std::string>& needed, TTree& tree, const std::string& expr) {
106  for (auto branch : *(tree.GetListOfBranches())) {
107  auto name = branch->GetName();
108  if (needed.find(name) == needed.end() && expr.find(name) != std::string::npos) {
109  // Only save branches that are really in the expression
110  std::regex regexpr {std::string("\\b") + name + "\\b"};
111  std::smatch matches;
112  if (std::regex_search(expr, matches, regexpr))
113  needed.insert(name);
114  }
115  }
116  }
117 
118  void add_branches(std::set<std::string>& needed, TTree& tree, const Types::strings& exprs) {
119  for (auto expr : exprs)
120  add_branches(needed, tree, expr);
121  }
122 
123  template<typename A, typename... Args> void add_branches(std::set<std::string>& needed, TTree& tree, const A& expr, Args... args) {
124  add_branches(needed, tree, expr);
125  add_branches(needed, tree, args...);
126  }
127 
128  /// Get the branches needed from the TTree to satisfy args
129  template<typename... Args> std::set<std::string> needed_branches(TTree& tree, Args... args) {
130  std::set<std::string> needed;
131  add_branches(needed, tree, args...);
132  return needed;
133  }
134 
135  }
136 
137  template<typename... Args> Tree::Tree(const std::string& infile, Args... args)
138  : file{TFile::Open(infile.data())} {
139  if (not file)
140  std::runtime_error(std::string("Error opening file ") + infile);
141 
142  tree = get<TTree>(Misc::env("tree", "events"));
143  nentries = tree->GetEntries();
144  forms = Formulas(tree);
145 
146  tree->SetBranchStatus("*", 0);
147  auto needed = needed_branches(*tree, args...);
148  for (auto need : needed)
149  tree->SetBranchStatus(need.data(), 1);
150 
151  rootlock.lock();
152  forms.add(args...);
153  rootlock.unlock();
154  }
155 
156  bool Tree::next() {
157  if (ientry == nentries)
158  return false;
159  tree->GetEntry(ientry++);
160  forms.eval();
161  return true;
162  }
163 
164  template<typename C> C* Tree::get(const std::string& name) {
165  auto* obj = dynamic_cast<C*>(file->Get(name.data()));
166  if (not obj)
167  throw std::runtime_error(std::string("Could not find object '") + name + "' in " + file->GetName());
168  return obj;
169  }
170 
171  }
172 }
173 
174 #endif