Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-21 09:58:12

0001 // SPDX-License-Identifier: MIT
0002 // Copyright 2019 Moritz Kiehn
0003 //
0004 // Permission is hereby granted, free of charge, to any person obtaining a copy
0005 // of this software and associated documentation files (the "Software"), to deal
0006 // in the Software without restriction, including without limitation the rights
0007 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0008 // copies of the Software, and to permit persons to whom the Software is
0009 // furnished to do so, subject to the following conditions:
0010 //
0011 // The above copyright notice and this permission notice shall be included in
0012 // all copies or substantial portions of the Software.
0013 //
0014 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0017 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0018 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0019 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
0020 // SOFTWARE.
0021 
0022 /// \file
0023 /// \brief   Wrappers to simplify reading/writing ROOT TTree-based files
0024 /// \author  Moritz Kiehn <msmk@cern.ch>
0025 /// \date    2019-04-00, Initial version
0026 
0027 #pragma once
0028 
0029 #include <array>
0030 #include <stdexcept>
0031 #include <string>
0032 #include <tuple>
0033 #include <type_traits>
0034 
0035 #include <TFile.h>
0036 #include <TTree.h>
0037 
0038 namespace dfe {
0039 
0040 /// Write records into a ROOT TTree.
0041 template<typename NamedTuple>
0042 class NamedTupleRootWriter {
0043 public:
0044   NamedTupleRootWriter() = delete;
0045   NamedTupleRootWriter(const NamedTupleRootWriter&) = delete;
0046   NamedTupleRootWriter(NamedTupleRootWriter&&) = delete;
0047   NamedTupleRootWriter& operator=(const NamedTupleRootWriter&) = delete;
0048   NamedTupleRootWriter& operator=(NamedTupleRootWriter&&) = delete;
0049 
0050   /// Create a file at the given path. Overwrites existing data.
0051   ///
0052   /// \param path       Path to the output file
0053   /// \param tree_name  Name of the output tree within the file
0054   NamedTupleRootWriter(const std::string& path, const std::string& tree_name);
0055   /// Create a tree in a ROOT directory. Overwrites existing data.
0056   ///
0057   /// \param dir        Output directory for the tree
0058   /// \param tree_name  Name of the output tree relative to the directory
0059   ///
0060   /// When the writer is created with an existing ROOT directory, the user
0061   /// is responsible for ensuring the underlying file is closed.
0062   NamedTupleRootWriter(TDirectory* dir, const std::string& tree_name);
0063   /// Write the tree and close the owned file.
0064   ~NamedTupleRootWriter();
0065 
0066   /// Append a record to the file.
0067   void append(const NamedTuple& record);
0068 
0069 private:
0070   // the equivalent std::tuple-like type
0071   using Tuple = typename NamedTuple::Tuple;
0072 
0073   TFile* m_file;
0074   TTree* m_tree;
0075   Tuple m_data;
0076 
0077   template<std::size_t... I>
0078   void setup_branches(std::index_sequence<I...>);
0079 };
0080 
0081 /// Read records from a ROOT TTree.
0082 template<typename NamedTuple>
0083 class NamedTupleRootReader {
0084 public:
0085   NamedTupleRootReader() = delete;
0086   NamedTupleRootReader(const NamedTupleRootReader&) = delete;
0087   NamedTupleRootReader(NamedTupleRootReader&&) = delete;
0088   NamedTupleRootReader& operator=(const NamedTupleRootReader&) = delete;
0089   NamedTupleRootReader& operator=(NamedTupleRootReader&&) = delete;
0090 
0091   /// Open a file at the given path.
0092   ///
0093   /// \param path       Path to the input file
0094   /// \param tree_name  Name of the input tree within the file
0095   NamedTupleRootReader(const std::string& path, const std::string& tree_name);
0096   /// Open a tree from a ROOT directory.
0097   ///
0098   /// \param dir        Input directory for the tree
0099   /// \param tree_name  Name of the input tree relative to the directory
0100   ///
0101   /// When the reader is created with an existing ROOT directory, the user
0102   /// is responsible for ensuring the underlying file is closed.
0103   NamedTupleRootReader(TDirectory* dir, const std::string& tree_name);
0104   /// Write the tree and close the owned file.
0105   ~NamedTupleRootReader();
0106 
0107   /// Read the next record from the file.
0108   ///
0109   /// \returns true   if a record was successfully read
0110   /// \returns false  if no more records are available
0111   bool read(NamedTuple& record);
0112 
0113 private:
0114   // the equivalent std::tuple-like type
0115   using Tuple = typename NamedTuple::Tuple;
0116 
0117   TFile* m_file;
0118   TTree* m_tree;
0119   int64_t m_next;
0120   Tuple m_data;
0121 
0122   template<std::size_t... I>
0123   void setup_branches(std::index_sequence<I...>);
0124 };
0125 
0126 // implementation writer
0127 
0128 template<typename NamedTuple>
0129 inline NamedTupleRootWriter<NamedTuple>::NamedTupleRootWriter(
0130   const std::string& path, const std::string& tree_name)
0131   : m_file(new TFile(path.c_str(), "RECREATE"))
0132   , m_tree(new TTree(tree_name.c_str(), "", 99, m_file)) {
0133   if (not m_file) {
0134     throw std::runtime_error("Could not create file");
0135   }
0136   if (not m_file->IsOpen()) {
0137     throw std::runtime_error("Could not open file");
0138   }
0139   if (not m_tree) {
0140     throw std::runtime_error("Could not create tree");
0141   }
0142   setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
0143 }
0144 
0145 template<typename NamedTuple>
0146 inline NamedTupleRootWriter<NamedTuple>::NamedTupleRootWriter(
0147   TDirectory* dir, const std::string& tree_name)
0148   : m_file(nullptr) // no file since it is not owned by the writer
0149   , m_tree(new TTree(tree_name.c_str(), "", 99, dir)) {
0150   if (not dir) {
0151     throw std::runtime_error("Invalid output directory given");
0152   }
0153   if (not m_tree) {
0154     throw std::runtime_error("Could not create tree");
0155   }
0156   setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
0157 }
0158 
0159 namespace namedtuple_root_impl {
0160 
0161 // see: https://cpppatterns.com/patterns/class-template-sfinae.html
0162 template<typename T, typename Enable = void>
0163 struct TypeCode;
0164 // non-integer types
0165 template<char Code>
0166 struct TypeCodePlainImpl {
0167   static constexpr char value = Code;
0168 };
0169 template<>
0170 struct TypeCode<bool> : TypeCodePlainImpl<'O'> {};
0171 template<>
0172 struct TypeCode<float> : TypeCodePlainImpl<'F'> {};
0173 template<>
0174 struct TypeCode<double> : TypeCodePlainImpl<'D'> {};
0175 // integer types
0176 // you might think that you could just define this for all the stdint types;
0177 // but no, this breaks because ROOT [U]Long64_t might not be the same type as
0178 // [u]int64_t depending on the machine, os, moon phase, ... .
0179 // Why you ask? Because the universe hates you.
0180 template<typename T, char Unsigned, char Signed>
0181 struct TypeCodeIntImpl {
0182   static constexpr char value = std::is_unsigned<T>::value ? Unsigned : Signed;
0183 };
0184 template<typename T, std::size_t S>
0185 constexpr bool is_integer_with_size_v = std::is_integral<T>::value
0186                                         and (sizeof(T) == S);
0187 template<typename T>
0188 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 1>>>
0189   : TypeCodeIntImpl<T, 'b', 'B'> {};
0190 template<typename T>
0191 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 2>>>
0192   : TypeCodeIntImpl<T, 's', 'S'> {};
0193 template<typename T>
0194 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 4>>>
0195   : TypeCodeIntImpl<T, 'i', 'I'> {};
0196 template<typename T>
0197 struct TypeCode<T, typename std::enable_if_t<is_integer_with_size_v<T, 8>>>
0198   : TypeCodeIntImpl<T, 'l', 'L'> {};
0199 
0200 } // namespace namedtuple_root_impl
0201 
0202 template<typename NamedTuple>
0203 template<std::size_t... I>
0204 inline void
0205 NamedTupleRootWriter<NamedTuple>::setup_branches(std::index_sequence<I...>) {
0206   static_assert(
0207     sizeof...(I) == std::tuple_size<Tuple>::value, "Something is very wrong");
0208 
0209   // construct leaf names w/ type info
0210   std::array<std::string, sizeof...(I)> names = NamedTuple::names();
0211   std::array<std::string, sizeof...(I)> leafs = {
0212     (names[I] + '/'
0213      + namedtuple_root_impl::TypeCode<
0214        std::tuple_element_t<I, Tuple>>::value)...};
0215   // construct branches
0216   // NOTE 2019-05-13 msmk:
0217   // the documentation suggests that ROOT can figure out the branch types on
0218   // its own, but doing so seems to break for {u}int64_t. do it manually for
0219   // now.
0220   (void)std::array<TBranch*, sizeof...(I)>{m_tree->Branch(
0221     names[I].c_str(), &std::get<I>(m_data), leafs[I].c_str())...};
0222 }
0223 
0224 template<typename NamedTuple>
0225 inline NamedTupleRootWriter<NamedTuple>::~NamedTupleRootWriter() {
0226   // alway overwrite old data
0227   if (m_tree) {
0228     m_tree->Write(nullptr, TObject::kOverwrite);
0229   }
0230   // writer owns the file
0231   if (m_file) {
0232     m_file->Close();
0233     delete m_file;
0234   }
0235 }
0236 
0237 template<typename NamedTuple>
0238 inline void
0239 NamedTupleRootWriter<NamedTuple>::append(const NamedTuple& record) {
0240   m_data = record;
0241   if (m_tree->Fill() == -1) {
0242     throw std::runtime_error("Could not fill an entry");
0243   }
0244 }
0245 
0246 // implementation reader
0247 
0248 template<typename NamedTuple>
0249 inline NamedTupleRootReader<NamedTuple>::NamedTupleRootReader(
0250   const std::string& path, const std::string& tree_name)
0251   : m_file(new TFile(path.c_str(), "READ")), m_tree(nullptr), m_next(0) {
0252   if (not m_file) {
0253     throw std::runtime_error("Could not open file");
0254   }
0255   if (not m_file->IsOpen()) {
0256     throw std::runtime_error("Could not open file");
0257   }
0258   m_tree = static_cast<TTree*>(m_file->Get(tree_name.c_str()));
0259   if (not m_tree) {
0260     throw std::runtime_error("Could not read tree");
0261   }
0262   setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
0263 }
0264 
0265 template<typename NamedTuple>
0266 inline NamedTupleRootReader<NamedTuple>::NamedTupleRootReader(
0267   TDirectory* dir, const std::string& tree_name)
0268   : m_file(nullptr) // no file since it is not owned by the writer
0269   , m_tree(nullptr)
0270   , m_next(0) {
0271   if (not dir) {
0272     throw std::runtime_error("Invalid input directory given");
0273   }
0274   m_tree = static_cast<TTree*>(dir->Get(tree_name.c_str()));
0275   if (not m_tree) {
0276     throw std::runtime_error("Could not read tree");
0277   }
0278   setup_branches(std::make_index_sequence<std::tuple_size<Tuple>::value>());
0279 }
0280 
0281 namespace io_root_impl {
0282 
0283 // WARNING this is a hack to get around inconsistent ROOT types for 8bit chars
0284 // and 64bit intengers compared to the stdint types.
0285 __attribute__((unused)) inline ULong64_t*
0286 get_address(uint64_t& x) {
0287   static_assert(
0288     sizeof(ULong64_t) == sizeof(uint64_t), "Inconsistent type sizes");
0289   return reinterpret_cast<ULong64_t*>(&x);
0290 }
0291 __attribute__((unused)) inline char*
0292 get_address(int8_t& x) {
0293   static_assert(sizeof(char) == sizeof(int8_t), "Inconsistent type sizes");
0294   return reinterpret_cast<char*>(&x);
0295 }
0296 __attribute__((unused)) inline Long64_t*
0297 get_address(int64_t& x) {
0298   static_assert(sizeof(Long64_t) == sizeof(int64_t), "Inconsistent type sizes");
0299   return reinterpret_cast<Long64_t*>(&x);
0300 }
0301 template<typename T>
0302 inline T*
0303 get_address(T& x) {
0304   return &x;
0305 }
0306 
0307 } // namespace io_root_impl
0308 
0309 template<typename NamedTuple>
0310 template<std::size_t... I>
0311 inline void
0312 NamedTupleRootReader<NamedTuple>::setup_branches(std::index_sequence<I...>) {
0313   static_assert(
0314     sizeof...(I) == std::tuple_size<Tuple>::value, "Something is very wrong");
0315 
0316   using std::get;
0317 
0318   // construct leaf names w/ type info
0319   std::array<std::string, sizeof...(I)> names = NamedTuple::names();
0320   // construct branches
0321   (void)std::array<Int_t, sizeof...(I)>{m_tree->SetBranchAddress(
0322     names[I].c_str(), io_root_impl::get_address(get<I>(m_data)))...};
0323 }
0324 
0325 template<typename NamedTuple>
0326 inline NamedTupleRootReader<NamedTuple>::~NamedTupleRootReader() {
0327   // reader owns the file
0328   if (m_file) {
0329     m_file->Close();
0330     delete m_file;
0331   }
0332 }
0333 
0334 template<typename NamedTuple>
0335 inline bool
0336 NamedTupleRootReader<NamedTuple>::read(NamedTuple& record) {
0337   auto ret = m_tree->GetEntry(m_next);
0338   // i/o error occured
0339   if (ret < 0) {
0340     throw std::runtime_error("Could not read entry");
0341   }
0342   // the entry does not exist, probably end-of-file reached
0343   if (ret == 0) {
0344     return false;
0345   }
0346   // GetEntry(...) has already filled the local buffer
0347   record = m_data;
0348   m_next += 1;
0349   return true;
0350 }
0351 
0352 } // namespace dfe