diff --git a/src/Io/lcio2/include/ACTFW/Io/lcio2/Lcio2TrackerHitWriter.hpp b/src/Io/lcio2/include/ACTFW/Io/lcio2/Lcio2TrackerHitWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d3263e072a66671e39f09269c433bc7dda1925f4 --- /dev/null +++ b/src/Io/lcio2/include/ACTFW/Io/lcio2/Lcio2TrackerHitWriter.hpp @@ -0,0 +1,96 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2018 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include <cstdint> +#include <mutex> +#include <string> + +#include "ACTFW/EventData/SimHit.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "lcio2/TrackerHitData.h" + +class TFile; +class TTree; + +namespace FW { + +/// Write out simulated hits as a flat TTree. +/// +/// Each entry in the TTree corresponds to one hit for optimum writing +/// speed. The event number is part of the written data. +/// +/// Safe to use from multiple writer threads. To avoid thread-saftey issues, +/// the writer must be the sole owner of the underlying file. Thus, the +/// output file pointer can not be given from the outside. +class Lcio2TrackerHitWriter final : public WriterT<SimHitContainer> { + public: + struct Config { + /// Input particle collection to write. + std::string inputSimulatedHits; + /// Path to the output file. + std::string filePath; + /// Output file access mode. + std::string fileMode = "RECREATE"; + /// Name of the tree within the output file. + std::string treeName = "hits"; + }; + + /// Construct the particle writer. + /// + /// @params cfg is the configuration object + /// @params lvl is the logging level + Lcio2TrackerHitWriter(const Config& cfg, Acts::Logging::Level lvl); + + /// Ensure underlying file is closed. + ~Lcio2TrackerHitWriter() final override; + + /// End-of-run hook + ProcessCode endRun() final override; + + protected: + /// Type-specific write implementation. + /// + /// @param[in] ctx is the algorithm context + /// @param[in] hits are the hits to be written + ProcessCode writeT(const AlgorithmContext& ctx, + const SimHitContainer& hits) final override; + + private: + Config m_cfg; + std::mutex m_writeMutex; + TFile* m_outputFile = nullptr; + TTree* m_outputTree = nullptr; + /// Event identifier. + uint32_t m_eventId; + /// Hit surface identifier. + uint64_t m_geometryId; + /// Event-unique particle identifier a.k.a. barcode. + uint64_t m_particleId; + /// True global hit position components in mm. + float m_tx, m_ty, m_tz; + // True global hit time in ns. + float m_tt; + /// True particle four-momentum in GeV at hit position before interaction. + float m_tpx, m_tpy, m_tpz, m_te; + /// True change in particle four-momentum in GeV due to interactions. + float m_deltapx, m_deltapy, m_deltapz, m_deltae; + /// Hit index along the particle trajectory + int32_t m_index; + // Decoded hit surface identifier components. + uint32_t m_volumeId; + uint32_t m_boundaryId; + uint32_t m_layerId; + uint32_t m_approachId; + uint32_t m_sensitiveId; + TrackerHitData + +}; + +} // namespace FW diff --git a/src/Io/lcio2/src/Lcio2TrackerHitWriter.cpp b/src/Io/lcio2/src/Lcio2TrackerHitWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..69b5a141b1fe1d4160d1ded307ea26f6c2152c71 --- /dev/null +++ b/src/Io/lcio2/src/Lcio2TrackerHitWriter.cpp @@ -0,0 +1,123 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2018 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "ACTFW/Io/Root/Lcio2TrackerHitWriter.hpp" + +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <stdexcept> + +#include "Acts/Utilities/Units.hpp" + +FW::Lcio2TrackerHitWriter::Lcio2TrackerHitWriter(const FW::Lcio2TrackerHitWriter::Config& cfg, + Acts::Logging::Level lvl) + : WriterT(cfg.inputSimulatedHits, "Lcio2TrackerHitWriter", lvl), m_cfg(cfg) { + // inputParticles is already checked by base constructor + if (m_cfg.filePath.empty()) { + throw std::invalid_argument("Missing file path"); + } + if (m_cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } + + // open root file and create the tree + m_outputFile = TFile::Open(m_cfg.filePath.c_str(), m_cfg.fileMode.c_str()); + if (m_outputFile == nullptr) { + throw std::ios_base::failure("Could not open '" + m_cfg.filePath + "'"); + } + m_outputFile->cd(); + m_outputTree = new TTree(m_cfg.treeName.c_str(), m_cfg.treeName.c_str()); + if (m_outputTree == nullptr) { + throw std::bad_alloc(); + } + + // setup the branches + m_outputTree->Branch("event_id", &m_eventId); + m_outputTree->Branch("geometry_id", &m_geometryId, "geometry_id/l"); + m_outputTree->Branch("particle_id", &m_particleId, "particle_id/l"); + m_outputTree->Branch("tx", &m_tx); + m_outputTree->Branch("ty", &m_ty); + m_outputTree->Branch("tz", &m_tz); + m_outputTree->Branch("tt", &m_tt); + m_outputTree->Branch("tpx", &m_tpx); + m_outputTree->Branch("tpy", &m_tpy); + m_outputTree->Branch("tpz", &m_tpz); + m_outputTree->Branch("te", &m_te); + m_outputTree->Branch("deltapx", &m_deltapx); + m_outputTree->Branch("deltapy", &m_deltapy); + m_outputTree->Branch("deltapz", &m_deltapz); + m_outputTree->Branch("deltae", &m_deltae); + m_outputTree->Branch("index", &m_index); + m_outputTree->Branch("volume_id", &m_volumeId); + m_outputTree->Branch("boundary_id", &m_boundaryId); + m_outputTree->Branch("layer_id", &m_layerId); + m_outputTree->Branch("approach_id", &m_approachId); + m_outputTree->Branch("sensitive_id", &m_sensitiveId); +} + +FW::Lcio2TrackerHitWriter::~Lcio2TrackerHitWriter() { + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::Lcio2TrackerHitWriter::endRun() { + if (m_outputFile) { + m_outputFile->cd(); + m_outputTree->Write(); + ACTS_VERBOSE("Wrote hits to tree '" << m_cfg.treeName << "' in '" + << m_cfg.filePath << "'"); + } + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::Lcio2TrackerHitWriter::writeT(const AlgorithmContext& ctx, + const FW::SimHitContainer& hits) { + if (not m_outputFile) { + ACTS_ERROR("Missing output file"); + return ProcessCode::ABORT; + } + + // ensure exclusive access to tree/file while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + // Get the event number + m_eventId = ctx.eventNumber; + for (const auto& hit : hits) { + m_particleId = hit.particleId().value(); + m_geometryId = hit.geometryId().value(); + // write hit position + m_tx = hit.position4().x() / Acts::UnitConstants::mm; + m_ty = hit.position4().y() / Acts::UnitConstants::mm; + m_tz = hit.position4().z() / Acts::UnitConstants::mm; + m_tt = hit.position4().w() / Acts::UnitConstants::ns; + // write four-momentum before interaction + m_tpx = hit.momentum4Before().x() / Acts::UnitConstants::GeV; + m_tpy = hit.momentum4Before().y() / Acts::UnitConstants::GeV; + m_tpz = hit.momentum4Before().z() / Acts::UnitConstants::GeV; + m_te = hit.momentum4Before().w() / Acts::UnitConstants::GeV; + // write four-momentum change due to interaction + const auto delta4 = hit.momentum4After() - hit.momentum4Before(); + m_deltapx = delta4.x() / Acts::UnitConstants::GeV; + m_deltapy = delta4.y() / Acts::UnitConstants::GeV; + m_deltapz = delta4.z() / Acts::UnitConstants::GeV; + m_deltae = delta4.w() / Acts::UnitConstants::GeV; + // write hit index along trajectory + m_index = hit.index(); + // decoded geometry for simplicity + m_volumeId = hit.geometryId().volume(); + m_boundaryId = hit.geometryId().boundary(); + m_layerId = hit.geometryId().layer(); + m_approachId = hit.geometryId().approach(); + m_sensitiveId = hit.geometryId().sensitive(); + // Fill the tree + m_outputTree->Fill(); + } + return FW::ProcessCode::SUCCESS; +}