diff --git a/README.md b/README.md index e08e3537..c2585ecc 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Information about all fields in all branches is stored in Configuration object. ROOT6 is needed for installation. Follow CERN ROOT [instructions](https://root.cern/install/) to install it. Version compiled with c++17 flag is preferred, otherwise CMAKE_CXX_STANDARD flag needs to be explicitly specified (see below). - +```bash git clone https://github.com/HeavyIonAnalysis/AnalysisTree.git cd AnalysisTree mkdir build install @@ -50,14 +50,14 @@ Version compiled with c++17 flag is preferred, otherwise CMAKE_CXX_STANDARD flag source path-to-root-installation/bin/thisroot.sh cmake -DCMAKE_INSTALL_PREFIX=../install ../ make -j install - +``` *path-to-root-installation* must be replaced with your actual location of Root install directory. ### List of CMake options: To apply the flag use -D{Name}={value}, for example, if you want to compile using c++11: - +```bash cmake -DCMAKE_CXX_STANDARD=11 ../ - +``` | Name | Default value | Possible values | | ------------- | ------------- | ---------- | | CMAKE_BUILD_TYPE | RELEASE | RELEASE/DEBUG | @@ -70,16 +70,16 @@ To apply the flag use -D{Name}={value}, for example, if you want to compile usin ### Setting AnalysisTree environment Whatever you are going to do with AnalysisTree - read the file, perform analysis based on information stored in it or create your own file, first of all you need to set up environment variables. It can be done in a single command: - +```bash source path-to-analysis_tree-installation/bin/AnalysisTreeConfig.sh - +``` ### Reading files from ROOT session An example of AnalysisTree ROOT file can be downloaded by this [link](https://sf.gsi.de/f/3ba5a9e3ff5248edba2c/?dl=1) Open a ROOT-file - +```bash root -l 1.analysistree.root - +``` Check its content .ls @@ -98,9 +98,9 @@ and, finally, AnalysisTree::DataHeader object named *DataHeader* containing info #### Reading the configuration In order to know the structure of the tree, perform following command: - +```c++ Configuration->Print() - +``` An output will contain plenty of branches and matchings between them. Let us look at one of them: @@ -114,7 +114,7 @@ Negative ids belong to default fields of branches while positive ids and 0 - to Middle column contains string name of the field, and right column - a description of it. #### Digesting the tree content - +```c++ Configuration->GetBranchConfig("SimParticles").GetType() // to know to which type (Hit, Track, Module, Particle or EventHeader belongs SimParticles branch) @@ -149,27 +149,27 @@ Middle column contains string name of the field, and right column - a descriptio // drawing a histogram with user-defined binning and ranges. rTree->Draw("TMath::Log(TMath::Abs(SimParticles.channels_.GetPx()))") - // drawing a distribution of derived quantites calculated by formula - + // drawing a distribution of derived quantities calculated by formula +``` Moreover, for default fields which are explicitly present in Container (i.e. px is OK, but not pt, which is not stored but calculated on fly) there is a possibility to draw them using TTree::Draw syntax: - +```c++ rTree->Draw("SimParticles.px_") rTree->Draw("TMath::Log(TMath::Abs(SimParticles.px_))") rTree->Draw("TMath::Log(TMath::Abs(SimParticles.px_)) * TMath::Cos(SimParticles.py_)") - +``` Also you can open a ROOT interactive session and create an AnalysisTree::Chain: - +```c++ AnalysisTree::Chain t("1.analysistree.root", "rTree") // Chain constructor with a single file // or AnalysisTree::Chain t({"filelist.txt"}, {"rTree"}) // Chain constructor with a file list - +``` and then build any fields including user-defined and implicitly present fields (such as phi or pt): - +```c++ t.Draw("SimParticles.px") t.Draw("SimParticles.pid") t.Draw("VtxTracks.chi2") // 2D histograms, cuts, drawing options and math formulas mentioned above are also available - +``` ### Reading file with a macro Building of distributions in interactive mode is a good approach only if the commands are simple, and the number of events to be analized is not so big. diff --git a/core/BranchConfig.cpp b/core/BranchConfig.cpp index b044f1b3..85eca5de 100644 --- a/core/BranchConfig.cpp +++ b/core/BranchConfig.cpp @@ -14,7 +14,7 @@ void BranchConfig::GenerateId() { id_ = id_hasher(name_); } -BranchConfig::BranchConfig(std::string name, DetType type, std::string title) : name_(std::move(name)), type_(type), title_(std::move(title)) { +BranchConfig::BranchConfig(std::string name, DetType type, std::string title) : name_(std::move(name)), title_(std::move(title)), type_(type) { GenerateId(); if (type_ == DetType::kTrack) { @@ -156,12 +156,12 @@ void VectorConfig::Print() const { std::vector result; std::vector newlinepositions{-1}; int it{0}; - while (it < std::string::npos) { + while (it < static_cast(std::string::npos)) { it = input.find("\n", it + 1); newlinepositions.emplace_back(it); } newlinepositions.back() = input.size(); - for (int ip = 0; ip < newlinepositions.size() - 1; ++ip) { + for (int ip = 0; ip < static_cast(newlinepositions.size()) - 1; ++ip) { result.emplace_back(input.substr(newlinepositions.at(ip) + 1, newlinepositions.at(ip + 1) - newlinepositions.at(ip) - 1)); } return result; @@ -174,7 +174,7 @@ void VectorConfig::Print() const { } else { auto est = SplitString(entry.second.title_); print_row({{std::to_string(entry.second.id_), 10}, {entry.first, name_strlen}, {est.at(0), 50}}); - for (int iest = 1; iest < est.size(); ++iest) { + for (int iest = 1; iest < static_cast(est.size()); ++iest) { print_row({{"", 10}, {"", name_strlen}, {est.at(iest), 50}}); } } diff --git a/core/Configuration.cpp b/core/Configuration.cpp index e8fc0ffc..2d381e2a 100644 --- a/core/Configuration.cpp +++ b/core/Configuration.cpp @@ -19,14 +19,12 @@ namespace AnalysisTree { void Configuration::Streamer(TBuffer& rb) { if (rb.IsReading()) { - bool read_ok = false; UInt_t rs = 0, rc = 0; auto version_from_file = rb.ReadVersion(&rs, &rc, Configuration::Class()); if (version_from_file == Class()->GetClassVersion()) { Configuration::Class()->ReadBuffer(rb, this, version_from_file, rs, rc); // populate the transient field this->matches_index_ = MakeMatchingIndex(matches_); - read_ok = true; } else if (version_from_file == 3) { // below structure description for version 3 of this class Warning(__func__, "Reading AnalysisTree::Configuration of version %d in compatibility mode. " @@ -40,14 +38,12 @@ void Configuration::Streamer(TBuffer& rb) { this->matches_ = MakeMatchConfigsFromIndex(conf_v3.matches_); // populate the transient field this->matches_index_ = conf_v3.matches_; - read_ok = true; } else { Warning(__func__, "Current version of AnalysisTree::Configuration (%d) " "is different from the version from file (%d) and no rule to read this version was implemented. " "Contact developers if dedicated Streamer implementation is needed for this version.", Configuration::Class()->GetClassVersion(), version_from_file); - read_ok = false; } } else { diff --git a/core/Configuration.test.cpp b/core/Configuration.test.cpp index 189affdd..629b702b 100644 --- a/core/Configuration.test.cpp +++ b/core/Configuration.test.cpp @@ -30,9 +30,6 @@ TEST(Configuration, Basics) { EXPECT_EQ(config.GetNumberOfBranches(), 1); // EXPECT_EQ(config.GetLastId(), 0); - - const auto& br1 = config.GetBranchConfig("RecTrack"); - // EXPECT_EQ(br1.GetId(), 0); } TEST(Configuration, Match) { diff --git a/core/Particle.hpp b/core/Particle.hpp index ec00a2b9..00049dc5 100644 --- a/core/Particle.hpp +++ b/core/Particle.hpp @@ -102,7 +102,7 @@ class Particle : public Track { PdgCode_t pid_{0}; bool is_allowed_set_charge_and_mass_explicitly_{false};//! - ClassDefOverride(Particle, 2); + ClassDefOverride(Particle, 3); }; }// namespace AnalysisTree diff --git a/core/Track.hpp b/core/Track.hpp index 9063e548..e715e87f 100644 --- a/core/Track.hpp +++ b/core/Track.hpp @@ -164,7 +164,7 @@ class Track : public Container { Floating_t pz_{UndefValueFloat};///< z-component of track's momentum Integer_t charge_{-1000}; - ClassDefOverride(Track, 2); + ClassDefOverride(Track, 3); }; }// namespace AnalysisTree diff --git a/examples/example.cpp b/examples/example.cpp index db6b92b2..913e6093 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -1,8 +1,8 @@ /* Copyright (C) 2019-2021 GSI, Universität Tübingen SPDX-License-Identifier: GPL-3.0-only Authors: Viktor Klochkov, Ilya Selyuzhenkov */ -#include #include +#include using namespace AnalysisTree; @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { return 0; } -void example(const std::string& filename, const std::string& treename){ +void example(const std::string& filename, const std::string& treename) { auto* chain = new Chain(filename, treename); chain->InitPointersToBranches({"VtxTracks", "SimParticles"}); @@ -34,13 +34,12 @@ void example(const std::string& filename, const std::string& treename){ config->Print(); auto rec_particles = chain->GetBranchObject("VtxTracks"); - auto rec2sim_particles = chain->GetMatching("VtxTracks", "SimParticles"); auto rec_pT = rec_particles.GetField("pT"); for (long i_event = 0; i_event < 10; ++i_event) { chain->GetEntry(i_event); - for(size_t i=0; i= 0 && i_channel_ < branch_->size()) { + if (i_channel_ < branch_->size()) { data_ptr_ = ANALYSISTREE_UTILS_VISIT(get_channel_struct(i_channel_), branch_->GetData()); } else { throw std::out_of_range(""); diff --git a/infra/GenericContainerFiller.cpp b/infra/GenericContainerFiller.cpp index 49818ce5..d2517a3a 100644 --- a/infra/GenericContainerFiller.cpp +++ b/infra/GenericContainerFiller.cpp @@ -55,7 +55,7 @@ void GenericContainerFiller::Init() { config_.AddBranchConfig(branchConfig); - for (int iV = 0; iV < branch_values_.size(); iV++) { + for (int iV = 0; iV < static_cast(branch_values_.size()); iV++) { SetAddressFICS(branch_map_.at(iV).name_, branch_map_.at(iV), branch_values_.at(iV)); } @@ -95,8 +95,8 @@ void GenericContainerFiller::Finish() { void GenericContainerFiller::Run(int nEntries) { Init(); - const size_t nTreeEntries = tree_in_->GetEntries(); - const size_t nRunEntries = (nEntries < 0 || nEntries > nTreeEntries) ? nTreeEntries : nEntries; + const int nTreeEntries = tree_in_->GetEntries(); + const int nRunEntries = (nEntries < 0 || nEntries > nTreeEntries) ? nTreeEntries : nEntries; int previousTriggerVar{-799}; for (int iEntry = 0; iEntry < nRunEntries; iEntry++) { @@ -108,8 +108,8 @@ void GenericContainerFiller::Run(int nEntries) { } int GenericContainerFiller::DetermineFieldIdByName(const std::vector& iMap, const std::string& name) { - auto distance = std::distance(iMap.begin(), std::find_if(iMap.begin(), iMap.end(), [&name](const IndexMap& p) { return p.name_ == name; })); - if (distance == iMap.size()) throw std::runtime_error("DetermineFieldIdByName(): name " + name + " is missing"); + const auto distance = static_cast(std::distance(iMap.begin(), std::find_if(iMap.begin(), iMap.end(), [&name](const IndexMap& p) { return p.name_ == name; }))); + if (distance == static_cast(iMap.size())) throw std::runtime_error("DetermineFieldIdByName(): name " + name + " is missing"); return distance; } @@ -126,7 +126,7 @@ void GenericContainerFiller::SetAddressFICS(const std::string& branchName, const } void GenericContainerFiller::SetFieldsFICS(const std::vector& imap, Container& container, const std::vector& ficc) { - for (int iV = 0; iV < ficc.size(); iV++) { + for (int iV = 0; iV < static_cast(ficc.size()); iV++) { if (imap.at(iV).field_type_ == "TLeafF") container.SetField(ficc.at(iV).float_, imap.at(iV).index_); else if (imap.at(iV).field_type_ == "TLeafI") container.SetField(ficc.at(iV).int_, imap.at(iV).index_); @@ -137,4 +137,4 @@ void GenericContainerFiller::SetFieldsFICS(const std::vector& imap, Co else throw std::runtime_error("GenericContainerFiller::SetFieldsFICS(): unsupported filed type " + imap.at(iV).field_type_); } -} \ No newline at end of file +} diff --git a/infra/HelperFunctions.hpp b/infra/HelperFunctions.hpp index 800c5b71..d5a514aa 100644 --- a/infra/HelperFunctions.hpp +++ b/infra/HelperFunctions.hpp @@ -4,6 +4,7 @@ #include "SimpleCut.hpp" #include +#include #include #include @@ -60,7 +61,7 @@ inline std::vector CreateRangeCuts(const std::vector sliceCuts; - for (int iRange = 0; iRange < ranges.size() - 1; iRange++) { + for (int iRange = 0; iRange < static_cast(ranges.size()) - 1; iRange++) { const std::string cutName = cutNamePrefix + ToStringWithPrecision(ranges.at(iRange), precision) + "_" + ToStringWithPrecision(ranges.at(iRange + 1), precision); sliceCuts.emplace_back(AnalysisTree::RangeCut(branchFieldName, ranges.at(iRange), ranges.at(iRange + 1), cutName)); } @@ -103,6 +104,14 @@ inline std::vector MergeVectors(const std::vector& vec1, const std::vector return MergeVectors(vec1, MergeVectors(vec2, args...)); } +inline TFile* OpenFileWithNullptrCheck(const std::string& fileName, const std::string& option = "read") { + TFile* file = TFile::Open(fileName.c_str(), option.c_str()); + if (file == nullptr) { + throw std::runtime_error("HelperFunctions::OpenFileWithNullptrCheck() - file " + fileName + " is missing"); + } + return file; +} + template inline T* GetObjectWithNullptrCheck(TFile* fileIn, const std::string& objectName) { T* ptr = fileIn->Get(objectName.c_str()); @@ -112,5 +121,73 @@ inline T* GetObjectWithNullptrCheck(TFile* fileIn, const std::string& objectName return ptr; } +inline void CheckHistogramsForXaxisIdentity(const TH1* h1, const TH1* h2) { + if (h1->GetNbinsX() != h2->GetNbinsX()) { + throw std::runtime_error("HelperFunctions::CheckHistogramsForXaxisIdentity(): nBinsX do not match for " + static_cast(h1->GetName()) + " and " + h2->GetName()); + } + const int nBins = h1->GetNbinsX(); + for (int iBin = 1; iBin <= nBins; iBin++) { + if (std::abs(h1->GetBinCenter(iBin) - h2->GetBinCenter(iBin)) > 1e-6) { + throw std::runtime_error("HelperFunctions::CheckHistogramsForXaxisIdentity(): bins do not coincide for " + static_cast(h1->GetName()) + " and " + h2->GetName()); + } + } +} + +inline void Sumw2IfNotYet(TH1* histo, bool value = true) { + const bool isSumw2Already = histo->GetSumw2N() > 0; + if (isSumw2Already != value) histo->Sumw2(value); +} + +inline TH1* MergeHistograms(const std::vector& histos) { + bool isSumw2{false}; + for(const auto& h : histos) { + CheckHistogramsForXaxisIdentity(h, histos.at(0)); + isSumw2 |= h->GetSumw2N() > 0; + } + + TH1* hResult = dynamic_cast(histos.at(0)->Clone("hMerged")); + Sumw2IfNotYet(hResult); + hResult->SetDirectory(nullptr); + for(size_t iH = 1, nHs = histos.size(); iH < nHs; ++iH) { + hResult->Add(histos.at(iH)); + } + Sumw2IfNotYet(hResult, isSumw2); + + return hResult; +} + +inline TH1* MergeHistograms(TFile* fileIn, const std::vector& histoNames) { + std::vector histos; + for (const auto& hN : histoNames) { + histos.emplace_back(GetObjectWithNullptrCheck(fileIn, hN)); + } + TH1* hResult = MergeHistograms(histos); + + return hResult; +} + +template +inline TDirectory* MkDirIfNotExists(T* fileOrDirectory, const std::string& name) { + if (fileOrDirectory == nullptr) throw std::runtime_error("HelperFunctions::MkDirIfNotExists(): file or directory ptr is null"); + TDirectory* result = fileOrDirectory->GetDirectory(name.c_str()); + if (result == nullptr) fileOrDirectory->mkdir(name.c_str()); + result = fileOrDirectory->GetDirectory(name.c_str()); + return result; +} + +template +inline void CD(T* fileOrDirectory, const std::string& name) { + auto destination = MkDirIfNotExists(fileOrDirectory, name); + destination->cd(); +} + +inline double InterpolateTH1SuppressWarning(const TH1* h, double value) { + double result; + if (value <= h->GetBinLowEdge(1) || value >= h->GetBinLowEdge(h->GetNbinsX() + 1)) result = 0.; + else + result = h->Interpolate(value); + return result; +} + }// namespace HelperFunctions #endif// ANALYSISTREE_INFRA_HELPER_FUNCTIONS_HPP diff --git a/infra/SimpleCut.cpp b/infra/SimpleCut.cpp index ff3c979e..71006704 100644 --- a/infra/SimpleCut.cpp +++ b/infra/SimpleCut.cpp @@ -3,8 +3,6 @@ Authors: Viktor Klochkov, Ilya Selyuzhenkov */ #include "SimpleCut.hpp" -#include "HelperFunctions.hpp" - #include namespace AnalysisTree { @@ -30,30 +28,30 @@ bool operator==(const SimpleCut& that, const SimpleCut& other) { } SimpleCut RangeCut(const std::string& variable_name, double lo, double hi, const std::string& title) { - return SimpleCut(Variable::FromString(variable_name), lo, hi, title); + return {Variable::FromString(variable_name), lo, hi, title}; } SimpleCut EqualsCut(const std::string& variable_name, int value, const std::string& title) { - return SimpleCut(Variable::FromString(variable_name), value, title); + return {Variable::FromString(variable_name), value, title}; } SimpleCut RangeCut(const Variable& var, double lo, double hi, const std::string& title) { - return SimpleCut(var, lo, hi, title); + return {var, lo, hi, title}; } SimpleCut EqualsCut(const Variable& var, int value, const std::string& title) { - return SimpleCut(var, value, title); + return {var, value, title}; } -SimpleCut OpenCut(const std::string& branchName, const std::string& title) { - return SimpleCut({branchName + ".ones"}, [](const std::vector& par) { return true; }); +SimpleCut OpenCut(const std::string& branchName) { + return SimpleCut({branchName + ".ones"}, [](const std::vector&) { return true; }); } SimpleCut::SimpleCut(const Variable& var, int value, std::string title) : title_(std::move(title)) { vars_.emplace_back(var); lambda_ = [value](std::vector& vars) { return vars[0] <= value + SmallNumber && vars[0] >= value - SmallNumber; }; FillBranchNames(); - const std::string stringForHash = var.GetName() + HelperFunctions::ToStringWithPrecision(value, 6) + title_; + const std::string stringForHash = var.GetName() + std::to_string(value) + title_; std::hash hasher; hash_ = hasher(stringForHash); } @@ -62,7 +60,7 @@ SimpleCut::SimpleCut(const Variable& var, double min, double max, std::string ti vars_.emplace_back(var); lambda_ = [max, min](std::vector& vars) { return vars[0] <= max && vars[0] >= min; }; FillBranchNames(); - const std::string stringForHash = var.GetName() + HelperFunctions::ToStringWithPrecision(min, 6) + HelperFunctions::ToStringWithPrecision(max, 6) + title_; + const std::string stringForHash = var.GetName() + std::to_string(min) + std::to_string(max) + title_; std::hash hasher; hash_ = hasher(stringForHash); } @@ -80,8 +78,8 @@ bool SimpleCut::Apply(std::vector& bch, std::vector brch_vec{a_ptr, b_ptr}; std::vector id_vec{a_id, b_id}; bool result = Apply(brch_vec, id_vec); diff --git a/infra/SimpleCut.hpp b/infra/SimpleCut.hpp index 9b00481b..533beddd 100644 --- a/infra/SimpleCut.hpp +++ b/infra/SimpleCut.hpp @@ -89,7 +89,7 @@ class SimpleCut { * is iterated and an iteration with no-cut is needed) * @param branchName name of the branch, which is present in other cuts */ - friend SimpleCut OpenCut(const std::string& branchName, const std::string& title); + friend SimpleCut OpenCut(const std::string& branchName); /** * @brief Evaluates cut @@ -139,7 +139,7 @@ SimpleCut RangeCut(const std::string& variable_name, double lo, double hi, const SimpleCut EqualsCut(const std::string& variable_name, int value, const std::string& title = ""); SimpleCut RangeCut(const Variable& var, double lo, double hi, const std::string& title = ""); SimpleCut EqualsCut(const Variable& var, int value, const std::string& title = ""); -SimpleCut OpenCut(const std::string& branchName, const std::string& title = "alwaysTrue"); +SimpleCut OpenCut(const std::string& branchName); }// namespace AnalysisTree #endif//ANALYSISTREE_SIMPLECUT_H diff --git a/infra/Variable.cpp b/infra/Variable.cpp index df32559a..e32577f3 100644 --- a/infra/Variable.cpp +++ b/infra/Variable.cpp @@ -79,7 +79,7 @@ double Variable::GetValue(std::vector& bch, std::vector(bch.size()); i++) { if (field.GetBranchId() == id.at(i)) { vars_.emplace_back(bch.at(i)->Value(field)); success = true; diff --git a/infra/Variable.hpp b/infra/Variable.hpp index e3e2acdc..06d794ad 100644 --- a/infra/Variable.hpp +++ b/infra/Variable.hpp @@ -82,7 +82,7 @@ class Variable { void Print() const; - void IfEmptyVariableConvertToOnes(const Variable& var); + ANALYSISTREE_ATTR_DEPRECATED() void IfEmptyVariableConvertToOnes(const Variable& var); protected: std::string name_;