Commit 344a68ed authored by Simon Spannagel's avatar Simon Spannagel
Browse files

new module: ROOTObjectWriter

parent afcfaa29
# Define module
CORRYVRECKAN_GLOBAL_MODULE(MODULE_NAME)
# Add source files to library
CORRYVRECKAN_MODULE_SOURCES(${MODULE_NAME}
ROOTObjectWriter.cpp
)
# Provide standard install target
CORRYVRECKAN_MODULE_INSTALL(${MODULE_NAME})
# ROOTObjectWriter
**Maintainer**: Simon Spannagel (<simon.spannagel@cern.ch>), Koen Wolters (<koen.wolters@cern.ch>)
**Module Type**: *GLOBAL*
**Status**: Functional
### Description
Reads all objects from the clipboard in vector ob base class object pointers. The first time a new type of object is received, a new tree is created bearing the class name of this object. For every detector, a new branch is created within this tree. A leaf is automatically created for every member of the object. The vector of objects is then written to the file for every event it is dispatched, saving an empty vector if an event does not include the specific object.
### Parameters
* `file_name` : Name of the data file to create, relative to the output directory of the framework. The file extension `.root` will be appended if not present.
* `include` : Array of object names (without `corryvreckan::` prefix) to write to the ROOT trees, all other object names are ignored (cannot be used together simultaneously with the *exclude* parameter).
* `exclude`: Array of object names (without `corryvreckan::` prefix) that are not written to the ROOT trees (cannot be used together simultaneously with the *include* parameter).
### Usage
To create the default file (with the name *data.root*) containing trees for all objects except for Cluster, the following configuration can be placed at the end of the main configuration:
```ini
[ROOTObjectWriter]
file_name = "data.root"
exclude = "Cluster"
```
/**
* @file
* @brief Implementation of ROOT data file writer module
* @copyright Copyright (c) 2019 CERN and the Corryvreckan authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#include "ROOTObjectWriter.h"
#include <fstream>
#include <string>
#include <utility>
#include <TBranchElement.h>
#include <TClass.h>
#include "core/utils/file.h"
#include "core/utils/log.h"
#include "core/utils/type.h"
#include "objects/Object.hpp"
#include "objects/objects.h"
using namespace corryvreckan;
ROOTObjectWriter::ROOTObjectWriter(Configuration config, std::vector<std::shared_ptr<Detector>> detectors)
: Module(std::move(config), std::move(detectors)) {}
/**
* @note Objects cannot be stored in smart pointers due to internal ROOT logic
*/
ROOTObjectWriter::~ROOTObjectWriter() {
// Delete all object pointers
for(auto& index_data : write_list_) {
delete index_data.second;
}
}
void ROOTObjectWriter::initialise() {
// Create output file
output_file_name_ =
createOutputFile(corryvreckan::add_file_extension(m_config.get<std::string>("file_name", "data"), "root"), true);
output_file_ = std::make_unique<TFile>(output_file_name_.c_str(), "RECREATE");
output_file_->cd();
// Read include and exclude list
if(m_config.has("include") && m_config.has("exclude")) {
throw InvalidValueError(m_config, "exclude", "include and exclude parameter are mutually exclusive");
} else if(m_config.has("include")) {
auto inc_arr = m_config.getArray<std::string>("include");
include_.insert(inc_arr.begin(), inc_arr.end());
} else if(m_config.has("exclude")) {
auto exc_arr = m_config.getArray<std::string>("exclude");
exclude_.insert(exc_arr.begin(), exc_arr.end());
}
}
StatusCode ROOTObjectWriter::run(std::shared_ptr<Clipboard> clipboard) {
auto data = clipboard->getAll();
LOG(DEBUG) << "Clipboard has " << data.size() << " different object types.";
for(auto& block : data) {
try {
auto type_idx = block.first;
auto class_name = corryvreckan::demangle(type_idx.name());
auto class_name_full = corryvreckan::demangle(type_idx.name(), true);
LOG(TRACE) << "ROOT object writer received objects of type \"" << class_name << "\"";
// Check if this message should be kept
if((!include_.empty() && include_.find(class_name) == include_.end()) ||
(!exclude_.empty() && exclude_.find(class_name) != exclude_.end())) {
LOG(TRACE) << "ROOT object writer ignored object " << corryvreckan::demangle(type_idx.name())
<< " because it has been excluded or not explicitly included";
continue;
}
for(auto& detector_block : block.second) {
// Get the detector name
std::string detector_name;
if(!detector_block.first.empty()) {
detector_name = detector_block.first;
}
auto objects = std::static_pointer_cast<Objects>(detector_block.second);
LOG(TRACE) << " - " << detector_name << ": " << objects->size();
// Create a new branch of the correct type if this object has not been received before
auto index_tuple = std::make_tuple(type_idx, detector_name);
if(write_list_.find(index_tuple) == write_list_.end()) {
// Add vector of objects to write to the write list
write_list_[index_tuple] = new std::vector<Object*>();
auto addr = &write_list_[index_tuple];
if(trees_.find(class_name) == trees_.end()) {
// Create new tree
output_file_->cd();
trees_.emplace(
class_name,
std::make_unique<TTree>(class_name.c_str(), (std::string("Tree of ") + class_name).c_str()));
}
std::string branch_name = detector_name.empty() ? "global" : detector_name;
trees_[class_name]->Bronch(
branch_name.c_str(), (std::string("std::vector<") + class_name_full + "*>").c_str(), addr);
}
// Fill the branch vector
for(auto& object : *objects) {
++write_cnt_;
write_list_[index_tuple]->push_back(object);
}
}
} catch(...) {
LOG(WARNING) << "ROOT object writer cannot process object of type" << corryvreckan::demangle(block.first.name());
return StatusCode::NoData;
}
}
LOG(TRACE) << "Writing new objects to tree";
output_file_->cd();
// Fill the tree with the current received messages
for(auto& tree : trees_) {
tree.second->Fill();
}
// Clear the current message list
for(auto& index_data : write_list_) {
index_data.second->clear();
}
return StatusCode::Success;
}
void ROOTObjectWriter::finalise() {
LOG(TRACE) << "Writing objects to file";
output_file_->cd();
int branch_count = 0;
for(auto& tree : trees_) {
// Update statistics
branch_count += tree.second->GetListOfBranches()->GetEntries();
}
// Finish writing to output file
output_file_->Write();
// Print statistics
LOG(STATUS) << "Wrote " << write_cnt_ << " objects to " << branch_count << " branches in file:" << std::endl
<< output_file_name_;
}
/**
* @file
* @brief Definition of ROOT data file writer module
* @copyright Copyright (c) 2019 CERN and the Corryvreckan authors.
* This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
* In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
* Intergovernmental Organization or submit itself to any jurisdiction.
*/
#include <map>
#include <string>
#include <TFile.h>
#include <TTree.h>
#include "core/module/Module.hpp"
namespace corryvreckan {
/**
* @ingroup Modules
* @brief Module to write object data to ROOT trees in file for persistent storage
*
* Reads the whole clipboard. Creates a tree as soon as a new type of object is encountered and
* saves the data in those objects to tree for every event. The tree name is the class name of the object. A separate
* branch is created for every combination of detector name and message name that outputs this object.
*/
class ROOTObjectWriter : public Module {
public:
/**
* @brief Constructor for this unique module
* @param config Configuration object for this module as retrieved from the steering file
* @param messenger Pointer to the messenger object to allow binding to messages on the bus
* @param geo_mgr Pointer to the geometry manager, containing the detectors
*/
ROOTObjectWriter(Configuration config, std::vector<std::shared_ptr<Detector>> detectors);
/**
* @brief Destructor deletes the internal objects used to build the ROOT Tree
*/
~ROOTObjectWriter() override;
/**
* @brief Opens the file to write the objects to
*/
void initialise() override;
/**
* @brief Writes the objects fetched to their specific tree, constructing trees on the fly for new objects.
*/
StatusCode run(std::shared_ptr<Clipboard> clipboard) override;
/**
* @brief Add the main configuration and the detector setup to the data file and write it, also write statistics
* information.
*/
void finalise() override;
private:
// Object names to include or exclude from writing
std::set<std::string> include_;
std::set<std::string> exclude_;
// Output data file to write
std::unique_ptr<TFile> output_file_;
std::string output_file_name_{};
// List of trees that are stored in data file
std::map<std::string, std::unique_ptr<TTree>> trees_;
// List of objects of a particular type, bound to a specific detector and having a particular name
std::map<std::tuple<std::type_index, std::string>, std::vector<Object*>*> write_list_;
// Statistical information about number of objects
unsigned long write_cnt_{};
};
} // namespace corryvreckan
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment