diff --git a/Io/CMakeLists.txt b/Io/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..65a9411aeabe0fee673625f98d6a45cc3bffe4f5 --- /dev/null +++ b/Io/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(Csv) +add_subdirectory(HepMC3) +add_subdirectory(Json) +add_subdirectory(Obj) +add_subdirectory(Performance) +add_subdirectory(Root) diff --git a/Io/Csv/CMakeLists.txt b/Io/Csv/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..783ccc90b37ee667b42c29e79c4d221984336583 --- /dev/null +++ b/Io/Csv/CMakeLists.txt @@ -0,0 +1,22 @@ +add_library( + ActsExamplesIoCsv SHARED + src/CsvOptionsReader.cpp + src/CsvOptionsWriter.cpp + src/CsvParticleReader.cpp + src/CsvParticleWriter.cpp + src/CsvPlanarClusterReader.cpp + src/CsvPlanarClusterWriter.cpp + src/CsvTrackingGeometryWriter.cpp) +target_include_directories( + ActsExamplesIoCsv + PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) +target_link_libraries( + ActsExamplesIoCsv + PRIVATE + ActsCore ActsDigitizationPlugin ActsIdentificationPlugin + ActsExamplesFramework + Threads::Threads Boost::program_options dfelibs) + +install( + TARGETS ActsExamplesIoCsv + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Io/Csv/include/ACTFW/Io/Csv/CsvOptionsReader.hpp b/Io/Csv/include/ACTFW/Io/Csv/CsvOptionsReader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3ccbb7a6ab308f22f2e4ba9b8e7ce4b820767077 --- /dev/null +++ b/Io/Csv/include/ACTFW/Io/Csv/CsvOptionsReader.hpp @@ -0,0 +1,29 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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 "ACTFW/Io/Csv/CsvParticleReader.hpp" +#include "ACTFW/Io/Csv/CsvPlanarClusterReader.hpp" +#include "ACTFW/Utilities/OptionsFwd.hpp" + +namespace FW { +namespace Options { + +// There are no additional CSV reader options apart from the +// format-independent, generic input option. + +/// Read the CSV particle reader config. +FW::CsvParticleReader::Config readCsvParticleReaderConfig(const Variables& vm); + +/// Read the CSV particle reader config. +FW::CsvPlanarClusterReader::Config readCsvPlanarClusterReaderConfig( + const Variables& vm); + +} // namespace Options +} // namespace FW diff --git a/Io/Csv/include/ACTFW/Io/Csv/CsvOptionsWriter.hpp b/Io/Csv/include/ACTFW/Io/Csv/CsvOptionsWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1bd5fff34919e05e2efdf912b2ed8467c5bac31d --- /dev/null +++ b/Io/Csv/include/ACTFW/Io/Csv/CsvOptionsWriter.hpp @@ -0,0 +1,34 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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 "ACTFW/Io/Csv/CsvParticleWriter.hpp" +#include "ACTFW/Io/Csv/CsvPlanarClusterWriter.hpp" +#include "ACTFW/Io/Csv/CsvTrackingGeometryWriter.hpp" +#include "ACTFW/Utilities/OptionsFwd.hpp" + +namespace FW { +namespace Options { + +// Add common CSV writer options. +void addCsvWriterOptions(Description& desc); + +/// Read the CSV particle writer options. +FW::CsvParticleWriter::Config readCsvParticleWriterConfig(const Variables& vm); + +/// Read the CSV planar cluster writer options. +FW::CsvPlanarClusterWriter::Config readCsvPlanarClusterWriterConfig( + const Variables& vm); + +/// Read the CSV tracking geometry writer config. +FW::CsvTrackingGeometryWriter::Config readCsvTrackingGeometryWriterConfig( + const Variables& vm); + +} // namespace Options +} // namespace FW diff --git a/Io/Csv/include/ACTFW/Io/Csv/CsvParticleReader.hpp b/Io/Csv/include/ACTFW/Io/Csv/CsvParticleReader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b77f4ac7df9aaf4d392f5c030388ae9451076987 --- /dev/null +++ b/Io/Csv/include/ACTFW/Io/Csv/CsvParticleReader.hpp @@ -0,0 +1,62 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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 <Acts/Utilities/Logger.hpp> +#include <memory> +#include <string> + +#include "ACTFW/Framework/IReader.hpp" + +namespace FW { + +/// Read particles in the TrackML comma-separated-value format. +/// +/// This reads one file per event in the configured input directory +/// and filename. Files are assumed to be named using the following schema +/// +/// event000000001-<stem>.csv +/// event000000002-<stem>.csv +/// +/// and each line in the file corresponds to one particle. The +/// input filename can be configured and defaults to `particles.csv`. +class CsvParticleReader final : public IReader { + public: + struct Config { + /// Where to read input files from. + std::string inputDir; + /// Input filename stem. + std::string inputStem = "particles"; + /// Which particle collection to read into. + std::string outputParticles; + }; + + /// Construct the particle reader. + /// + /// @params cfg is the configuration object + /// @params lvl is the logging level + CsvParticleReader(const Config& cfg, Acts::Logging::Level lvl); + + std::string name() const final override; + + /// Return the available events range. + std::pair<size_t, size_t> availableEvents() const final override; + + /// Read out data from the input stream. + ProcessCode read(const FW::AlgorithmContext& ctx) final override; + + private: + Config m_cfg; + std::pair<size_t, size_t> m_eventsRange; + std::unique_ptr<const Acts::Logger> m_logger; + + const Acts::Logger& logger() const { return *m_logger; } +}; + +} // namespace FW diff --git a/Io/Csv/include/ACTFW/Io/Csv/CsvParticleWriter.hpp b/Io/Csv/include/ACTFW/Io/Csv/CsvParticleWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..67d2bc465a27779bb077de325b19e3cfb78dceb2 --- /dev/null +++ b/Io/Csv/include/ACTFW/Io/Csv/CsvParticleWriter.hpp @@ -0,0 +1,65 @@ +// 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/. + +#pragma once + +#include <limits> +#include <string> +#include <vector> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Framework/WriterT.hpp" + +namespace FW { + +/// Write out particles in the TrackML comma-separated-value format. +/// +/// This writer is restricted to outgoing particles, it is designed for +/// generated particle information. +/// +/// This writes one file per event into the configured output directory. By +/// default it writes to the current working directory. Files are named +/// using the following schema +/// +/// event000000001-<stem>.csv +/// event000000002-<stem>.csv +/// ... +/// +/// and each line in the file corresponds to one particle. +class CsvParticleWriter final : public WriterT<SimParticleContainer> { + public: + struct Config { + /// Input particles collection to write. + std::string inputParticles; + /// Where to place output files. + std::string outputDir; + /// Output filename stem. + std::string outputStem = "particles"; + /// Number of decimal digits for floating point precision in output. + size_t outputPrecision = std::numeric_limits<float>::max_digits10; + }; + + /// Construct the particle writer. + /// + /// @params cfg is the configuration object + /// @params lvl is the logging level + CsvParticleWriter(const Config& cfg, Acts::Logging::Level lvl); + + protected: + /// Type-specific write implementation. + /// + /// @param[in] ctx is the algorithm context + /// @param[in] particles are the particle to be written + ProcessCode writeT(const FW::AlgorithmContext& ctx, + const SimParticleContainer& particles) final override; + + private: + Config m_cfg; //!< Nested configuration struct +}; + +} // namespace FW diff --git a/Io/Csv/include/ACTFW/Io/Csv/CsvPlanarClusterReader.hpp b/Io/Csv/include/ACTFW/Io/Csv/CsvPlanarClusterReader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ce5e8ee0ad5a49e999ef0cf17b881356b2942238 --- /dev/null +++ b/Io/Csv/include/ACTFW/Io/Csv/CsvPlanarClusterReader.hpp @@ -0,0 +1,79 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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 <memory> +#include <string> +#include <unordered_map> + +#include "ACTFW/Framework/IReader.hpp" +#include "Acts/Geometry/GeometryID.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Utilities/Logger.hpp" + +namespace Acts { +class Surface; +} + +namespace FW { + +/// Read in a planar cluster collection in comma-separated-value format. +/// +/// This reads three files per event file in the configured input +/// directory. By default it reads file in the current working directory. +/// Files are assumed to be named using the following schema +/// +/// event000000001-cells.csv +/// event000000001-hits.csv +/// event000000001-truth.csv +/// event000000002-cells.csv +/// event000000002-hits.csv +/// event000000002-truth.csv +/// +/// and each line in the file corresponds to one hit/cluster. +class CsvPlanarClusterReader final : public IReader { + public: + struct Config { + /// Where to read input files from. + std::string inputDir; + /// Output cluster collection. + std::string outputClusters; + /// For each cluster/ hit index the original hit id stored on file. + std::string outputHitIds; + /// Output hit-particles mapping collection. + std::string outputHitParticlesMap; + /// Output simulated (truth) hits collection. + std::string outputSimulatedHits; + /// Tracking geometry required to access global-to-local transforms. + std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry; + }; + + /// Construct the cluster reader. + /// + /// @params cfg is the configuration object + /// @params lvl is the logging level + CsvPlanarClusterReader(const Config& cfg, Acts::Logging::Level lvl); + + std::string name() const final override; + + /// Return the available events range. + std::pair<size_t, size_t> availableEvents() const final override; + + /// Read out data from the input stream. + ProcessCode read(const FW::AlgorithmContext& ctx) final override; + + private: + Config m_cfg; + std::unordered_map<Acts::GeometryID, const Acts::Surface*> m_surfaces; + std::pair<size_t, size_t> m_eventsRange; + std::unique_ptr<const Acts::Logger> m_logger; + + const Acts::Logger& logger() const { return *m_logger; } +}; + +} // namespace FW diff --git a/Io/Csv/include/ACTFW/Io/Csv/CsvPlanarClusterWriter.hpp b/Io/Csv/include/ACTFW/Io/Csv/CsvPlanarClusterWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8ddd3b4737540e843549cf28c21fbb6af26b46fa --- /dev/null +++ b/Io/Csv/include/ACTFW/Io/Csv/CsvPlanarClusterWriter.hpp @@ -0,0 +1,69 @@ +// 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/. + +#pragma once + +#include <limits> +#include <string> + +#include "ACTFW/EventData/GeometryContainers.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "Acts/Plugins/Digitization/PlanarModuleCluster.hpp" + +namespace FW { + +/// Write out a planar cluster collection in comma-separated-value format. +/// +/// This writes multiples file per event containing information about the +/// space points, local constituent cells, and hit-particle truth mapping +/// into the configured output directory. By default it writes to the +/// current working directory. Files are named using the following schema +/// +/// event000000001-cells.csv +/// event000000001-hits.csv +/// event000000001-truth.csv +/// event000000002-cells.csv +/// event000000002-hits.csv +/// event000000002-truth.csv +/// ... +/// +/// and each line in the file corresponds to one hit/cluster. +class CsvPlanarClusterWriter final + : public WriterT<GeometryIdMultimap<Acts::PlanarModuleCluster>> { + public: + struct Config { + /// Which cluster collection to write. + std::string inputClusters; + /// Which simulated (truth) hits collection to use. + std::string inputSimulatedHits; + /// Where to place output files + std::string outputDir; + /// Number of decimal digits for floating point precision in output. + size_t outputPrecision = std::numeric_limits<float>::max_digits10; + }; + + /// Construct the cluster writer. + /// + /// @params cfg is the configuration object + /// @params lvl is the logging level + CsvPlanarClusterWriter(const Config& cfg, Acts::Logging::Level lvl); + + protected: + /// Type-specific write implementation. + /// + /// @param[in] ctx is the algorithm context + /// @param[in] particles are the particle to be written + ProcessCode writeT(const AlgorithmContext& ctx, + const GeometryIdMultimap<Acts::PlanarModuleCluster>& + clusters) final override; + + private: + Config m_cfg; +}; + +} // namespace FW diff --git a/Io/Csv/include/ACTFW/Io/Csv/CsvTrackingGeometryWriter.hpp b/Io/Csv/include/ACTFW/Io/Csv/CsvTrackingGeometryWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..41ac854cf84eeefdc772481df95cc1e0ac905c73 --- /dev/null +++ b/Io/Csv/include/ACTFW/Io/Csv/CsvTrackingGeometryWriter.hpp @@ -0,0 +1,69 @@ +// 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/. + +#pragma once + +#include <Acts/Geometry/TrackingGeometry.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <limits> + +#include "ACTFW/Framework/IWriter.hpp" + +namespace Acts { +class TrackingVolume; +} + +namespace FW { + +/// Write out the geometry for all sensitive detector surfaces. +/// +/// This writes a `detectors.csv` file at the end of the run using the +/// default context to determine the geometry. If configured, it also writes +/// an additional file for each event using the following schema +/// +/// event000000001-detectors.csv +/// event000000002-detectors.csv +/// ... +/// +/// that uses the per-event context to determine the geometry. +class CsvTrackingGeometryWriter : public IWriter { + public: + struct Config { + /// The tracking geometry that should be written. + std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry; + /// Where to place output files. + std::string outputDir; + /// Number of decimal digits for floating point precision in output. + std::size_t outputPrecision = std::numeric_limits<float>::max_digits10; + /// Whether to write the per-event file. + bool writePerEvent = false; + }; + + /// Construct the geometry writer. + /// + /// @param cfg is the configuration object + /// @param lvl is the logging level + CsvTrackingGeometryWriter(const Config& cfg, Acts::Logging::Level lvl); + + std::string name() const final override; + + /// Write geometry using the per-event context (optional). + ProcessCode write(const AlgorithmContext& context) final override; + + /// Write geometry using the default context. + ProcessCode endRun() final override; + + private: + Config m_cfg; + const Acts::TrackingVolume* m_world; + std::unique_ptr<const Acts::Logger> m_logger; + + const Acts::Logger& logger() const { return *m_logger; } +}; + +} // namespace FW diff --git a/Io/Csv/src/CsvOptionsReader.cpp b/Io/Csv/src/CsvOptionsReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d38d000551c5e020442c39e86134ad8450d4c698 --- /dev/null +++ b/Io/Csv/src/CsvOptionsReader.cpp @@ -0,0 +1,29 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/Csv/CsvOptionsReader.hpp" + +#include <boost/program_options.hpp> + +FW::CsvParticleReader::Config FW::Options::readCsvParticleReaderConfig( + const Variables& vm) { + FW::CsvParticleReader::Config cfg; + if (not vm["input-dir"].empty()) { + cfg.inputDir = vm["input-dir"].as<std::string>(); + } + return cfg; +} + +FW::CsvPlanarClusterReader::Config +FW::Options::readCsvPlanarClusterReaderConfig(const Variables& vm) { + FW::CsvPlanarClusterReader::Config cfg; + if (not vm["input-dir"].empty()) { + cfg.inputDir = vm["input-dir"].as<std::string>(); + } + return cfg; +} diff --git a/Io/Csv/src/CsvOptionsWriter.cpp b/Io/Csv/src/CsvOptionsWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76148d8d79b20c67a1e04a64b99fc211e2b749ec --- /dev/null +++ b/Io/Csv/src/CsvOptionsWriter.cpp @@ -0,0 +1,56 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/Csv/CsvOptionsWriter.hpp" + +#include <boost/program_options.hpp> +#include <dfe/dfe_io_dsv.hpp> +#include <limits> + +void FW::Options::addCsvWriterOptions(FW::Options::Description& desc) { + using namespace boost::program_options; + + desc.add_options()( + "csv-output-precision", + value<size_t>()->default_value(std::numeric_limits<float>::max_digits10), + "Floating number output precision.")( + "csv-tg-perevent", bool_switch(), "Write tracking geometry per event."); +} + +FW::CsvParticleWriter::Config FW::Options::readCsvParticleWriterConfig( + const FW::Options::Variables& vm) { + FW::CsvParticleWriter::Config cfg; + if (not vm["output-dir"].empty()) { + cfg.outputDir = vm["output-dir"].as<std::string>(); + } + cfg.outputPrecision = vm["csv-output-precision"].as<size_t>(); + return cfg; +} + +FW::CsvPlanarClusterWriter::Config +FW::Options::readCsvPlanarClusterWriterConfig( + const FW::Options::Variables& vm) { + FW::CsvPlanarClusterWriter::Config cfg; + if (not vm["output-dir"].empty()) { + cfg.outputDir = vm["output-dir"].as<std::string>(); + } + cfg.outputPrecision = vm["csv-output-precision"].as<size_t>(); + return cfg; +} + +FW::CsvTrackingGeometryWriter::Config +FW::Options::readCsvTrackingGeometryWriterConfig( + const FW::Options::Variables& vm) { + FW::CsvTrackingGeometryWriter::Config cfg; + if (not vm["output-dir"].empty()) { + cfg.outputDir = vm["output-dir"].as<std::string>(); + } + cfg.outputPrecision = vm["csv-output-precision"].as<size_t>(); + cfg.writePerEvent = vm.count("csv-tg-perevent"); + return cfg; +} diff --git a/Io/Csv/src/CsvParticleReader.cpp b/Io/Csv/src/CsvParticleReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6de72b4cc5d64be549536695149201efcbb4973c --- /dev/null +++ b/Io/Csv/src/CsvParticleReader.cpp @@ -0,0 +1,77 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/Csv/CsvParticleReader.hpp" + +#include <Acts/Utilities/Units.hpp> +#include <dfe/dfe_io_dsv.hpp> +#include <fstream> +#include <ios> +#include <stdexcept> +#include <string> +#include <vector> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Framework/WhiteBoard.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "TrackMlData.hpp" + +FW::CsvParticleReader::CsvParticleReader( + const FW::CsvParticleReader::Config& cfg, Acts::Logging::Level lvl) + : m_cfg(cfg), + m_eventsRange( + determineEventFilesRange(cfg.inputDir, cfg.inputStem + ".csv")), + m_logger(Acts::getDefaultLogger("CsvParticleReader", lvl)) { + if (m_cfg.inputStem.empty()) { + throw std::invalid_argument("Missing input filename stem"); + } + if (m_cfg.outputParticles.empty()) { + throw std::invalid_argument("Missing output collection"); + } +} + +std::string FW::CsvParticleReader::CsvParticleReader::name() const { + return "CsvParticleReader"; +} + +std::pair<size_t, size_t> FW::CsvParticleReader::availableEvents() const { + return m_eventsRange; +} + +FW::ProcessCode FW::CsvParticleReader::read(const FW::AlgorithmContext& ctx) { + SimParticleContainer::sequence_type unordered; + + auto path = perEventFilepath(m_cfg.inputDir, m_cfg.inputStem + ".csv", + ctx.eventNumber); + // vt and m are an optional columns + dfe::NamedTupleCsvReader<ParticleData> reader(path, {"vt", "m"}); + ParticleData data; + + while (reader.read(data)) { + ActsFatras::Particle particle(ActsFatras::Barcode(data.particle_id), + Acts::PdgParticle(data.particle_type), + data.q * Acts::UnitConstants::e, + data.m * Acts::UnitConstants::GeV); + particle.setProcess(static_cast<ActsFatras::ProcessType>(data.process)); + particle.setPosition4( + data.vx * Acts::UnitConstants::mm, data.vy * Acts::UnitConstants::mm, + data.vz * Acts::UnitConstants::mm, data.vt * Acts::UnitConstants::ns); + // only used for direction; normalization/units do not matter + particle.setDirection(data.px, data.py, data.pz); + particle.setAbsMomentum(std::hypot(data.px, data.py, data.pz) * + Acts::UnitConstants::GeV); + unordered.push_back(std::move(particle)); + } + + // write ordered particles container to the EventStore + SimParticleContainer particles; + particles.adopt_sequence(std::move(unordered)); + ctx.eventStore.add(m_cfg.outputParticles, std::move(particles)); + + return ProcessCode::SUCCESS; +} diff --git a/Io/Csv/src/CsvParticleWriter.cpp b/Io/Csv/src/CsvParticleWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72580e57a1d9c4b2a3d4b64f1c07e4ac12a5e6bd --- /dev/null +++ b/Io/Csv/src/CsvParticleWriter.cpp @@ -0,0 +1,55 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/Csv/CsvParticleWriter.hpp" + +#include <Acts/Utilities/Units.hpp> +#include <dfe/dfe_io_dsv.hpp> +#include <map> +#include <stdexcept> + +#include "ACTFW/Framework/WhiteBoard.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "TrackMlData.hpp" + +FW::CsvParticleWriter::CsvParticleWriter( + const FW::CsvParticleWriter::Config& cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputParticles, "CsvParticleWriter", lvl), m_cfg(cfg) { + // inputParticles is already checked by base constructor + if (m_cfg.outputStem.empty()) { + throw std::invalid_argument("Missing ouput filename stem"); + } +} + +FW::ProcessCode FW::CsvParticleWriter::writeT( + const FW::AlgorithmContext& ctx, const SimParticleContainer& particles) { + auto pathParticles = perEventFilepath( + m_cfg.outputDir, m_cfg.outputStem + ".csv", ctx.eventNumber); + dfe::NamedTupleCsvWriter<ParticleData> writer(pathParticles, + m_cfg.outputPrecision); + + ParticleData data; + for (const auto& particle : particles) { + data.particle_id = particle.particleId().value(); + data.particle_type = particle.pdg(); + data.process = static_cast<decltype(data.process)>(particle.process()); + data.vx = particle.position().x() / Acts::UnitConstants::mm; + data.vy = particle.position().y() / Acts::UnitConstants::mm; + data.vz = particle.position().z() / Acts::UnitConstants::mm; + data.vt = particle.time() / Acts::UnitConstants::ns; + const auto p = particle.absMomentum() / Acts::UnitConstants::GeV; + data.px = p * particle.unitDirection().x(); + data.py = p * particle.unitDirection().y(); + data.pz = p * particle.unitDirection().z(); + data.m = particle.mass() / Acts::UnitConstants::GeV; + data.q = particle.charge() / Acts::UnitConstants::e; + writer.append(data); + } + + return ProcessCode::SUCCESS; +} diff --git a/Io/Csv/src/CsvPlanarClusterReader.cpp b/Io/Csv/src/CsvPlanarClusterReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4729d9650ea82aae7e35d5ddbc38c32f657bd2a --- /dev/null +++ b/Io/Csv/src/CsvPlanarClusterReader.cpp @@ -0,0 +1,287 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/Csv/CsvPlanarClusterReader.hpp" + +#include <dfe/dfe_io_dsv.hpp> + +#include "ACTFW/EventData/GeometryContainers.hpp" +#include "ACTFW/EventData/IndexContainers.hpp" +#include "ACTFW/EventData/SimHit.hpp" +#include "ACTFW/EventData/SimIdentifier.hpp" +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Framework/WhiteBoard.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "ACTFW/Utilities/Range.hpp" +#include "Acts/Plugins/Digitization/PlanarModuleCluster.hpp" +#include "Acts/Plugins/Identification/IdentifiedDetectorElement.hpp" +#include "Acts/Utilities/Units.hpp" +#include "TrackMlData.hpp" + +FW::CsvPlanarClusterReader::CsvPlanarClusterReader( + const FW::CsvPlanarClusterReader::Config& cfg, Acts::Logging::Level lvl) + : m_cfg(cfg) + // TODO check that all files (hits,cells,truth) exists + , + m_eventsRange(determineEventFilesRange(cfg.inputDir, "hits.csv")), + m_logger(Acts::getDefaultLogger("CsvPlanarClusterReader", lvl)) { + if (m_cfg.outputClusters.empty()) { + throw std::invalid_argument("Missing cluster output collection"); + } + if (m_cfg.outputHitIds.empty()) { + throw std::invalid_argument("Missing hit id output collection"); + } + if (m_cfg.outputHitParticlesMap.empty()) { + throw std::invalid_argument("Missing hit-particles map output collection"); + } + if (m_cfg.outputSimulatedHits.empty()) { + throw std::invalid_argument("Missing simulated hits output collection"); + } + if (not m_cfg.trackingGeometry) { + throw std::invalid_argument("Missing tracking geometry"); + } + // fill the geo id to surface map once to speed up lookups later on + m_cfg.trackingGeometry->visitSurfaces([this](const Acts::Surface* surface) { + this->m_surfaces[surface->geoID()] = surface; + }); +} + +std::string FW::CsvPlanarClusterReader::CsvPlanarClusterReader::name() const { + return "CsvPlanarClusterReader"; +} + +std::pair<size_t, size_t> FW::CsvPlanarClusterReader::availableEvents() const { + return m_eventsRange; +} + +namespace { +struct CompareHitId { + // support transparent comparision between identifiers and full objects + using is_transparent = void; + template <typename T> + constexpr bool operator()(const T& left, const T& right) const { + return left.hit_id < right.hit_id; + } + template <typename T> + constexpr bool operator()(uint64_t left_id, const T& right) const { + return left_id < right.hit_id; + } + template <typename T> + constexpr bool operator()(const T& left, uint64_t right_id) const { + return left.hit_id < right_id; + } +}; + +/// Convert separate volume/layer/module id into a single geometry identifier. +inline Acts::GeometryID extractGeometryId(const FW::HitData& data) { + // if available, use the encoded geometry directly + if (data.geometry_id != 0u) { + return data.geometry_id; + } + // otherwise, reconstruct it from the available components + Acts::GeometryID geoId; + geoId.setVolume(data.volume_id); + geoId.setLayer(data.layer_id); + geoId.setSensitive(data.module_id); + return geoId; +} + +struct CompareGeometryId { + bool operator()(const FW::HitData& left, const FW::HitData& right) const { + auto leftId = extractGeometryId(left).value(); + auto rightId = extractGeometryId(right).value(); + return leftId < rightId; + } +}; + +template <typename Data> +inline std::vector<Data> readEverything( + const std::string& inputDir, const std::string& filename, + const std::vector<std::string>& optionalColumns, size_t event) { + std::string path = FW::perEventFilepath(inputDir, filename, event); + dfe::NamedTupleCsvReader<Data> reader(path, optionalColumns); + + std::vector<Data> everything; + Data one; + while (reader.read(one)) { + everything.push_back(one); + } + + return everything; +} + +std::vector<FW::HitData> readHitsByGeoId(const std::string& inputDir, + size_t event) { + // geometry_id and t are optional columns + auto hits = readEverything<FW::HitData>(inputDir, "hits.csv", + {"geometry_id", "t"}, event); + // sort same way they will be sorted in the output container + std::sort(hits.begin(), hits.end(), CompareGeometryId{}); + return hits; +} + +std::vector<FW::CellData> readCellsByHitId(const std::string& inputDir, + size_t event) { + // timestamp is an optional element + auto cells = + readEverything<FW::CellData>(inputDir, "cells.csv", {"timestamp"}, event); + // sort for fast hit id look up + std::sort(cells.begin(), cells.end(), CompareHitId{}); + return cells; +} + +std::vector<FW::TruthHitData> readTruthHitsByHitId(const std::string& inputDir, + size_t event) { + // define all optional columns + std::vector<std::string> optionalColumns = { + "geometry_id", "tt", "te", "deltapx", + "deltapy", "deltapz", "deltae", "index", + }; + auto truths = readEverything<FW::TruthHitData>(inputDir, "truth.csv", + optionalColumns, event); + // sort for fast hit id look up + std::sort(truths.begin(), truths.end(), CompareHitId{}); + return truths; +} + +} // namespace + +FW::ProcessCode FW::CsvPlanarClusterReader::read( + const FW::AlgorithmContext& ctx) { + // hit_id in the files is not required to be neither continuous nor + // monotonic. internally, we want continous indices within [0,#hits) + // to simplify data handling. to be able to perform this mapping we first + // read all data into memory before converting to the internal event data + // types. + auto hits = readHitsByGeoId(m_cfg.inputDir, ctx.eventNumber); + auto cells = readCellsByHitId(m_cfg.inputDir, ctx.eventNumber); + auto truths = readTruthHitsByHitId(m_cfg.inputDir, ctx.eventNumber); + + // prepare containers for the hit data using the framework event data types + GeometryIdMultimap<Acts::PlanarModuleCluster> clusters; + std::vector<uint64_t> hitIds; + IndexMultimap<ActsFatras::Barcode> hitParticlesMap; + SimHitContainer simHits; + clusters.reserve(hits.size()); + hitIds.reserve(hits.size()); + hitParticlesMap.reserve(truths.size()); + simHits.reserve(truths.size()); + + for (const HitData& hit : hits) { + Acts::GeometryID geoId = extractGeometryId(hit); + + // find associated truth/ simulation hits + std::vector<std::size_t> simHitIndices; + { + auto range = makeRange(std::equal_range(truths.begin(), truths.end(), + hit.hit_id, CompareHitId{})); + simHitIndices.reserve(range.size()); + for (const auto& truth : range) { + const auto simGeometryId = Acts::GeometryID(truth.geometry_id); + // TODO validate geo id consistency + const auto simParticleId = ActsFatras::Barcode(truth.particle_id); + const auto simIndex = truth.index; + ActsFatras::Hit::Vector4 simPos4{ + truth.tx * Acts::UnitConstants::mm, + truth.ty * Acts::UnitConstants::mm, + truth.tz * Acts::UnitConstants::mm, + truth.tt * Acts::UnitConstants::ns, + }; + ActsFatras::Hit::Vector4 simMom4{ + truth.tpx * Acts::UnitConstants::GeV, + truth.tpy * Acts::UnitConstants::GeV, + truth.tpz * Acts::UnitConstants::GeV, + truth.te * Acts::UnitConstants::GeV, + }; + ActsFatras::Hit::Vector4 simDelta4{ + truth.deltapx * Acts::UnitConstants::GeV, + truth.deltapy * Acts::UnitConstants::GeV, + truth.deltapz * Acts::UnitConstants::GeV, + truth.deltae * Acts::UnitConstants::GeV, + }; + + // the cluster stores indices to the underlying simulation hits. thus + // their position in the container must be stable. the preordering of + // hits by geometry id should ensure that new sim hits are always added + // at the end and previously created ones rest at their existing + // locations. + auto inserted = simHits.emplace_hint(simHits.end(), simGeometryId, + simParticleId, simPos4, simMom4, + simMom4 + simDelta4, simIndex); + if (std::next(inserted) != simHits.end()) { + ACTS_FATAL("Truth hit sorting broke for input hit id " << hit.hit_id); + return ProcessCode::ABORT; + } + simHitIndices.push_back(simHits.index_of(inserted)); + } + } + + // find matching pixel cell information + std::vector<Acts::DigitizationCell> digitizationCells; + { + auto range = makeRange(std::equal_range(cells.begin(), cells.end(), + hit.hit_id, CompareHitId{})); + for (const auto& c : range) { + digitizationCells.emplace_back(c.ch0, c.ch1, c.value); + } + } + + // identify hit surface + auto it = m_surfaces.find(geoId); + if (it == m_surfaces.end() or not it->second) { + ACTS_FATAL("Could not retrieve the surface for hit " << hit); + return ProcessCode::ABORT; + } + const Acts::Surface& surface = *(it->second); + + // transform global hit coordinates into local coordinates on the surface + Acts::Vector3D pos(hit.x * Acts::UnitConstants::mm, + hit.y * Acts::UnitConstants::mm, + hit.z * Acts::UnitConstants::mm); + double time = hit.t * Acts::UnitConstants::ns; + Acts::Vector3D mom(1, 1, 1); // fake momentum + Acts::Vector2D local(0, 0); + surface.globalToLocal(ctx.geoContext, pos, mom, local); + // TODO what to use as cluster uncertainty? + Acts::ActsSymMatrixD<3> cov = Acts::ActsSymMatrixD<3>::Identity(); + // create the planar cluster + Acts::PlanarModuleCluster cluster( + surface.getSharedPtr(), + Identifier(identifier_type(geoId.value()), std::move(simHitIndices)), + std::move(cov), local[0], local[1], time, std::move(digitizationCells)); + + // due to the previous sorting of the raw hit data by geometry id, new + // clusters should always end up at the end of the container. previous + // elements were not touched; cluster indices remain stable and can + // be used to identify the hit. + auto inserted = + clusters.emplace_hint(clusters.end(), geoId, std::move(cluster)); + if (std::next(inserted) != clusters.end()) { + ACTS_FATAL("Something went horribly wrong with the hit sorting"); + return ProcessCode::ABORT; + } + auto hitIndex = clusters.index_of(inserted); + auto truthRange = makeRange(std::equal_range(truths.begin(), truths.end(), + hit.hit_id, CompareHitId{})); + for (const auto& truth : truthRange) { + hitParticlesMap.emplace_hint(hitParticlesMap.end(), hitIndex, + truth.particle_id); + } + + // map internal hit/cluster index back to original, non-monotonic hit id + hitIds.push_back(hit.hit_id); + } + + // write the data to the EventStore + ctx.eventStore.add(m_cfg.outputClusters, std::move(clusters)); + ctx.eventStore.add(m_cfg.outputHitIds, std::move(hitIds)); + ctx.eventStore.add(m_cfg.outputHitParticlesMap, std::move(hitParticlesMap)); + ctx.eventStore.add(m_cfg.outputSimulatedHits, std::move(simHits)); + + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Csv/src/CsvPlanarClusterWriter.cpp b/Io/Csv/src/CsvPlanarClusterWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d3cb041fe1be32dc72ab642c3d5271c1574f0b0 --- /dev/null +++ b/Io/Csv/src/CsvPlanarClusterWriter.cpp @@ -0,0 +1,135 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/Csv/CsvPlanarClusterWriter.hpp" + +#include <dfe/dfe_io_dsv.hpp> +#include <stdexcept> + +#include "ACTFW/EventData/SimHit.hpp" +#include "ACTFW/EventData/SimIdentifier.hpp" +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/EventData/SimVertex.hpp" +#include "ACTFW/Framework/WhiteBoard.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "Acts/Plugins/Digitization/PlanarModuleCluster.hpp" +#include "Acts/Utilities/Units.hpp" +#include "TrackMlData.hpp" + +FW::CsvPlanarClusterWriter::CsvPlanarClusterWriter( + const FW::CsvPlanarClusterWriter::Config& cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputClusters, "CsvPlanarClusterWriter", lvl), m_cfg(cfg) { + // inputClusters is already checked by base constructor + if (m_cfg.inputSimulatedHits.empty()) { + throw std::invalid_argument("Missing simulated hits input collection"); + } +} + +FW::ProcessCode FW::CsvPlanarClusterWriter::writeT( + const AlgorithmContext& ctx, + const FW::GeometryIdMultimap<Acts::PlanarModuleCluster>& clusters) { + // retrieve simulated hits + const auto& simHits = + ctx.eventStore.get<SimHitContainer>(m_cfg.inputSimulatedHits); + + // open per-event file for all components + std::string pathHits = + perEventFilepath(m_cfg.outputDir, "hits.csv", ctx.eventNumber); + std::string pathCells = + perEventFilepath(m_cfg.outputDir, "cells.csv", ctx.eventNumber); + std::string pathTruth = + perEventFilepath(m_cfg.outputDir, "truth.csv", ctx.eventNumber); + + dfe::NamedTupleCsvWriter<HitData> writerHits(pathHits, m_cfg.outputPrecision); + dfe::NamedTupleCsvWriter<CellData> writerCells(pathCells, + m_cfg.outputPrecision); + dfe::NamedTupleCsvWriter<TruthHitData> writerTruth(pathTruth, + m_cfg.outputPrecision); + + HitData hit; + CellData cell; + TruthHitData truth; + // will be reused as hit counter + hit.hit_id = 0; + + for (const auto& entry : clusters) { + Acts::GeometryID geoId = entry.first; + const Acts::PlanarModuleCluster& cluster = entry.second; + // local cluster information + const auto& parameters = cluster.parameters(); + Acts::Vector2D localPos(parameters[0], parameters[1]); + Acts::Vector3D globalFakeMom(1, 1, 1); + Acts::Vector3D globalPos(0, 0, 0); + // transform local into global position information + cluster.referenceSurface().localToGlobal(ctx.geoContext, localPos, + globalFakeMom, globalPos); + + // encoded geometry identifier + hit.geometry_id = geoId.value(); + // (partially) decoded geometry identifier + hit.volume_id = geoId.volume(); + hit.layer_id = geoId.layer(); + hit.module_id = geoId.sensitive(); + // write global hit information + hit.x = globalPos.x() / Acts::UnitConstants::mm; + hit.y = globalPos.y() / Acts::UnitConstants::mm; + hit.z = globalPos.z() / Acts::UnitConstants::mm; + hit.t = parameters[2] / Acts::UnitConstants::ns; + writerHits.append(hit); + + // write local cell information + cell.hit_id = hit.hit_id; + for (auto& c : cluster.digitizationCells()) { + cell.ch0 = c.channel0; + cell.ch1 = c.channel1; + // TODO store digitial timestamp once added to the cell definition + cell.timestamp = 0; + cell.value = c.data; + writerCells.append(cell); + } + + // write hit-particle truth association + // each hit can have multiple particles, e.g. in a dense environment + truth.hit_id = hit.hit_id; + truth.geometry_id = hit.geometry_id; + for (auto idx : cluster.sourceLink().indices()) { + auto it = simHits.nth(idx); + if (it == simHits.end()) { + ACTS_FATAL("Simulation hit with index " << idx << " does not exist"); + return ProcessCode::ABORT; + } + + const auto& simHit = *it; + truth.particle_id = simHit.particleId().value(); + // hit position + truth.tx = simHit.position().x() / Acts::UnitConstants::mm; + truth.ty = simHit.position().y() / Acts::UnitConstants::mm; + truth.tz = simHit.position().z() / Acts::UnitConstants::mm; + truth.tt = simHit.time() / Acts::UnitConstants::ns; + // particle four-momentum before interaction + truth.tpx = simHit.momentum4Before().x() / Acts::UnitConstants::GeV; + truth.tpy = simHit.momentum4Before().y() / Acts::UnitConstants::GeV; + truth.tpz = simHit.momentum4Before().z() / Acts::UnitConstants::GeV; + truth.te = simHit.momentum4Before().w() / Acts::UnitConstants::GeV; + // particle four-momentum change due to interaction + const auto delta4 = simHit.momentum4After() - simHit.momentum4Before(); + truth.deltapx = delta4.x() / Acts::UnitConstants::GeV; + truth.deltapy = delta4.y() / Acts::UnitConstants::GeV; + truth.deltapz = delta4.z() / Acts::UnitConstants::GeV; + truth.deltae = delta4.w() / Acts::UnitConstants::GeV; + // TODO write hit index along the particle trajectory + truth.index = simHit.index(); + writerTruth.append(truth); + } + + // increase hit id for next iteration + hit.hit_id += 1; + } + + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Csv/src/CsvTrackingGeometryWriter.cpp b/Io/Csv/src/CsvTrackingGeometryWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..208e9c0c67722c483a856f9fad88a9df4a46624a --- /dev/null +++ b/Io/Csv/src/CsvTrackingGeometryWriter.cpp @@ -0,0 +1,168 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/Csv/CsvTrackingGeometryWriter.hpp" + +#include <Acts/Geometry/TrackingVolume.hpp> +#include <Acts/Plugins/Digitization/CartesianSegmentation.hpp> +#include <Acts/Plugins/Digitization/DigitizationModule.hpp> +#include <Acts/Plugins/Identification/IdentifiedDetectorElement.hpp> +#include <Acts/Surfaces/Surface.hpp> +#include <Acts/Utilities/Units.hpp> +#include <dfe/dfe_io_dsv.hpp> +#include <iostream> +#include <sstream> +#include <stdexcept> + +#include "ACTFW/Utilities/Paths.hpp" +#include "TrackMlData.hpp" + +using namespace FW; + +CsvTrackingGeometryWriter::CsvTrackingGeometryWriter( + const CsvTrackingGeometryWriter::Config& cfg, Acts::Logging::Level lvl) + : m_cfg(cfg), + m_world(nullptr), + m_logger(Acts::getDefaultLogger("CsvTrackingGeometryWriter", lvl)) + +{ + if (not m_cfg.trackingGeometry) { + throw std::invalid_argument("Missing tracking geometry"); + } + m_world = m_cfg.trackingGeometry->highestTrackingVolume(); + if (not m_world) { + throw std::invalid_argument("Could not identify the world volume"); + } +} + +std::string CsvTrackingGeometryWriter::name() const { + return "CsvTrackingGeometryWriter"; +} + +namespace { +using SurfaceWriter = dfe::NamedTupleCsvWriter<SurfaceData>; + +/// Write a single surface. +void writeSurface(SurfaceWriter& writer, const Acts::Surface& surface, + const Acts::GeometryContext& geoCtx) { + SurfaceData data; + + // encoded and partially decoded geometry identifier + data.geometry_id = surface.geoID().value(); + data.volume_id = surface.geoID().volume(); + data.layer_id = surface.geoID().layer(); + data.module_id = surface.geoID().sensitive(); + // center position + auto center = surface.center(geoCtx); + data.cx = center.x() / Acts::UnitConstants::mm; + data.cy = center.y() / Acts::UnitConstants::mm; + data.cz = center.z() / Acts::UnitConstants::mm; + // rotation matrix components are unit-less + auto transform = surface.transform(geoCtx); + data.rot_xu = transform(0, 0); + data.rot_xv = transform(0, 1); + data.rot_xw = transform(0, 2); + data.rot_yu = transform(1, 0); + data.rot_yv = transform(1, 1); + data.rot_yw = transform(1, 2); + data.rot_zu = transform(2, 0); + data.rot_zv = transform(2, 1); + data.rot_zw = transform(2, 2); + + // module thickness + if (surface.associatedDetectorElement()) { + const auto* detElement = + dynamic_cast<const Acts::IdentifiedDetectorElement*>( + surface.associatedDetectorElement()); + if (detElement) { + data.module_t = detElement->thickness() / Acts::UnitConstants::mm; + } + } + + // bounds and pitch (if available) + const auto& bounds = surface.bounds(); + const auto* planarBounds = dynamic_cast<const Acts::PlanarBounds*>(&bounds); + if (planarBounds) { + // extract limits from value store + auto boundValues = surface.bounds().values(); + if (boundValues.size() == 2) { + data.module_minhu = boundValues[0] / Acts::UnitConstants::mm; + data.module_minhu = boundValues[0] / Acts::UnitConstants::mm; + data.module_minhu = boundValues[1] / Acts::UnitConstants::mm; + } else if (boundValues.size() == 3) { + data.module_minhu = boundValues[0] / Acts::UnitConstants::mm; + data.module_minhu = boundValues[0] / Acts::UnitConstants::mm; + data.module_minhu = boundValues[1] / Acts::UnitConstants::mm; + } + // get the pitch from the digitization module + const auto* detElement = + dynamic_cast<const Acts::IdentifiedDetectorElement*>( + surface.associatedDetectorElement()); + if (detElement and detElement->digitizationModule()) { + auto dModule = detElement->digitizationModule(); + // dynamic_cast to CartesianSegmentation + const auto* cSegmentation = + dynamic_cast<const Acts::CartesianSegmentation*>( + &(dModule->segmentation())); + if (cSegmentation) { + auto pitch = cSegmentation->pitch(); + data.pitch_u = pitch.first / Acts::UnitConstants::mm; + data.pitch_u = pitch.second / Acts::UnitConstants::mm; + } + } + } + + writer.append(data); +} + +/// Write all child surfaces and descend into confined volumes. +void writeVolume(SurfaceWriter& writer, const Acts::TrackingVolume& volume, + const Acts::GeometryContext& geoCtx) { + // process all layers that are directly stored within this volume + if (volume.confinedLayers()) { + for (auto layer : volume.confinedLayers()->arrayObjects()) { + // we jump navigation layers + if (layer->layerType() == Acts::navigation) { + continue; + } + // check for sensitive surfaces + if (layer->surfaceArray()) { + for (auto surface : layer->surfaceArray()->surfaces()) { + if (surface) { + writeSurface(writer, *surface, geoCtx); + } + } + } + } + } + // step down into hierarchy to process all child volumnes + if (volume.confinedVolumes()) { + for (auto confined : volume.confinedVolumes()->arrayObjects()) { + writeVolume(writer, *confined.get(), geoCtx); + } + } +} +} // namespace + +ProcessCode CsvTrackingGeometryWriter::write(const AlgorithmContext& ctx) { + if (not m_cfg.writePerEvent) { + return ProcessCode::SUCCESS; + } + SurfaceWriter writer( + perEventFilepath(m_cfg.outputDir, "detectors.csv", ctx.eventNumber), + m_cfg.outputPrecision); + writeVolume(writer, *m_world, ctx.geoContext); + return ProcessCode::SUCCESS; +} + +ProcessCode CsvTrackingGeometryWriter::endRun() { + SurfaceWriter writer(joinPaths(m_cfg.outputDir, "detectors.csv"), + m_cfg.outputPrecision); + writeVolume(writer, *m_world, Acts::GeometryContext()); + return ProcessCode::SUCCESS; +} diff --git a/Io/Csv/src/TrackMlData.hpp b/Io/Csv/src/TrackMlData.hpp new file mode 100644 index 0000000000000000000000000000000000000000..411e300de287d85185c5e9cd56a27216c315c1d4 --- /dev/null +++ b/Io/Csv/src/TrackMlData.hpp @@ -0,0 +1,130 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/. + +/// @file +/// @brief Plain structs that each define one row in a TrackML csv file + +#pragma once + +#include <cstdint> +#include <dfe/dfe_namedtuple.hpp> + +namespace FW { + +struct ParticleData { + /// Event-unique particle identifier a.k.a barcode. + uint64_t particle_id; + /// Particle type number a.k.a. PDG particle number. + int32_t particle_type; + /// Production process type. Not available in the TrackML datasets. + uint32_t process = 0u; + /// Production position components in mm. + float vx, vy, vz; + // Production time in ns. Not available in the TrackML datasets. + float vt = 0.0f; + /// Momentum components in GeV. + float px, py, pz; + /// Mass in GeV. Not available in the TrackML datasets + float m = 0.0f; + /// Charge in e. + float q; + + DFE_NAMEDTUPLE(ParticleData, particle_id, particle_type, process, vx, vy, vz, + vt, px, py, pz, m, q); +}; + +struct TruthHitData { + /// Event-unique hit identifier. As defined for the simulated hit below and + /// used to link back to it; same value can appear multiple times here due to + /// shared hits in dense environments. + uint64_t hit_id; + /// Hit surface identifier. Not available in the TrackML datasets. + uint64_t geometry_id = 0u; + /// Event-unique particle identifier of the generating particle. + uint64_t particle_id; + /// True global hit position components in mm. + float tx, ty, tz; + // True global hit time in ns. Not available in the TrackML datasets. + float tt = 0.0f; + /// True particle momentum in GeV before interaction. + float tpx, tpy, tpz; + /// True particle energy in GeV before interaction. + /// Not available in the TrackML datasets. + float te = 0.0f; + /// True four-momentum change in GeV due to interaction. + /// Not available in the TrackML datasets. + float deltapx = 0.0f; + float deltapy = 0.0f; + float deltapz = 0.0f; + float deltae = 0.0f; + // Hit index along the trajectory. Not available in the TrackML datasets. + int32_t index = -1; + + DFE_NAMEDTUPLE(TruthHitData, hit_id, particle_id, geometry_id, tx, ty, tz, tt, + tpx, tpy, tpz, te, deltapx, deltapy, deltapz, deltae, index); +}; + +struct HitData { + /// Event-unique hit identifier. Each value can appear at most once. + uint64_t hit_id; + /// Hit surface identifier. Not available in the TrackML datasets. + uint64_t geometry_id = 0u; + /// Partially decoded hit surface identifier components. + uint32_t volume_id, layer_id, module_id; + /// Global hit position components in mm. + float x, y, z; + /// Global hit time in ns. Not available in the TrackML datasets. + float t = 0.0f; + + DFE_NAMEDTUPLE(HitData, hit_id, geometry_id, volume_id, layer_id, module_id, + x, y, z, t); +}; + +struct CellData { + /// Event-unique hit identifier. As defined for the simulated hit above and + /// used to link back to it; same value can appear multiple times for clusters + /// with more than one active cell. + uint64_t hit_id; + /// Digital cell address/ channel identifier. These should have been named + /// channel{0,1} but we cannot change it now to avoid breaking backward + /// compatibility. + int32_t ch0, ch1; + /// Digital cell timestamp. Not available in the TrackML datasets. + int32_t timestamp = 0; + /// (Digital) measured cell value, e.g. amplitude or time-over-threshold. + int32_t value; + + DFE_NAMEDTUPLE(CellData, hit_id, ch0, ch1, timestamp, value); +}; + +struct SurfaceData { + /// Surface identifier. Not available in the TrackML datasets. + uint64_t geometry_id; + /// Partially decoded surface identifier components. + uint32_t volume_id, layer_id, module_id; + /// Center position components in mm. + float cx, cy, cz; + /// Rotation matrix components. + float rot_xu, rot_xv, rot_xw; + float rot_yu, rot_yv, rot_yw; + float rot_zu, rot_zv, rot_zw; + /// Limits and pitches in mm. Not always available. + float module_t = -1; + float module_minhu = -1; + float module_maxhu = -1; + float module_hv = -1; + float pitch_u = -1; + float pitch_v = -1; + + DFE_NAMEDTUPLE(SurfaceData, geometry_id, volume_id, layer_id, module_id, cx, + cy, cz, rot_xu, rot_xv, rot_xw, rot_yu, rot_yv, rot_yw, rot_zu, + rot_zv, rot_zw, module_t, module_minhu, module_maxhu, + module_hv, pitch_u, pitch_v); +}; + +} // namespace FW diff --git a/Io/HepMC3/CMakeLists.txt b/Io/HepMC3/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e1e8534f345b9b463f760eed1da8d62dd8bb46bc --- /dev/null +++ b/Io/HepMC3/CMakeLists.txt @@ -0,0 +1,18 @@ +find_package(HepMC3 REQUIRED) +add_library( + ActsExamplesIoHepMC3 SHARED + src/HepMC3Event.cpp + src/HepMC3Particle.cpp + src/HepMC3Reader.cpp + src/HepMC3Vertex.cpp + src/HepMC3Writer.cpp) +target_include_directories( + ActsExamplesIoHepMC3 + PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> ${HEPMC3_INCLUDE_DIR}) +target_link_libraries( + ActsExamplesIoHepMC3 + PUBLIC ActsCore ActsExamplesFramework ${HEPMC3_LIBRARIES} HepPID) + +install( + TARGETS ActsExamplesIoHepMC3 + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Event.hpp b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Event.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c093f4d16dcb9d3e703bbc3d6545e4f67827c7a3 --- /dev/null +++ b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Event.hpp @@ -0,0 +1,180 @@ +// 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 <HepMC3/FourVector.h> +#include <HepMC3/GenEvent.h> +#include <HepMC3/GenParticle.h> +#include <HepMC3/GenVertex.h> +#include <HepPID/ParticleIDMethods.hh> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/EventData/SimVertex.hpp" +#include "Acts/Utilities/Units.hpp" + +namespace FW { + +/// Helper struct to convert HepMC3 event to the internal format. +struct HepMC3Event { + public: + /// + /// Setter + /// + + /// @brief Sets new units for momentums + /// @note The allowed units are MeV and Gev + /// @param event event in HepMC data type + /// @param momentumUnit new unit of momentum + void momentumUnit(std::shared_ptr<HepMC3::GenEvent> event, + const double momentumUnit); + + /// @brief Sets new units for lengths + /// @note The allowed units are mm and cm + /// @param event event in HepMC data type + /// @param lengthUnit new unit of length + void lengthUnit(std::shared_ptr<HepMC3::GenEvent> event, + const double lengthUnit); + + /// @brief Shifts the positioning of an event in space and time + /// @param event event in HepMC data type + /// @param deltaPos relative spatial shift that will be applied + /// @param deltaTime relative time shift that will be applied + void shiftPositionBy(std::shared_ptr<HepMC3::GenEvent> event, + const Acts::Vector3D& deltaPos, const double deltaTime); + + /// @brief Shifts the positioning of an event to a paint in space and time + /// @param event event in HepMC data type + /// @param pos new position of the event + /// @param time new time of the event + void shiftPositionTo(std::shared_ptr<HepMC3::GenEvent> event, + const Acts::Vector3D& pos, const double time); + + /// @brief Shifts the positioning of an event to a paint in space + /// @param event event in HepMC data type + /// @param pos new position of the event + void shiftPositionTo(std::shared_ptr<HepMC3::GenEvent> event, + const Acts::Vector3D& pos); + + /// @brief Shifts the positioning of an event to a paint in time + /// @param event event in HepMC data type + /// @param time new time of the event + void shiftPositionTo(std::shared_ptr<HepMC3::GenEvent> event, + const double time); + + /// + /// Adder + /// + + /// @brief Adds a new particle + /// @param event event in HepMC data type + /// @param particle new particle that will be added + void addParticle(std::shared_ptr<HepMC3::GenEvent> event, + std::shared_ptr<SimParticle> particle); + + /// @brief Adds a new vertex + /// @param event event in HepMC data type + /// @param vertex new vertex that will be added + /// @note The statuses are not represented in Acts and therefore set to 0 + void addVertex(std::shared_ptr<HepMC3::GenEvent> event, + const std::shared_ptr<SimVertex> vertex); + /// + /// Remover + /// + + /// @brief Removes a particle from the record + /// @param event event in HepMC data type + /// @param particle particle that will be removed + void removeParticle(std::shared_ptr<HepMC3::GenEvent> event, + const std::shared_ptr<SimParticle>& particle); + + /// @brief Removes a vertex from the record + /// @note The identification of the vertex is potentially unstable (c.f. + /// HepMC3Event::compareVertices()) + /// @param event event in HepMC data type + /// @param vertex vertex that will be removed + void removeVertex(std::shared_ptr<HepMC3::GenEvent> event, + const std::shared_ptr<SimVertex>& vertex); + + /// + /// Getter + /// + + /// @brief Getter of the unit of momentum used + /// @param event event in HepMC data type + /// @return unit of momentum + double momentumUnit(const std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Getter of the unit of length used + /// @param event event in HepMC data type + /// @return unit of length + double lengthUnit(const std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Getter of the position of the event + /// @param event event in HepMC data type + /// @return vector to the location of the event + Acts::Vector3D eventPos(const std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Getter of the time of the event + /// @param event event in HepMC data type + /// @return time of the event + double eventTime(const std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Get list of particles + /// @param event event in HepMC data type + /// @return List of particles + std::vector<std::unique_ptr<SimParticle>> particles( + const std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Get list of vertices + /// @param event event in HepMC data type + /// @return List of vertices + std::vector<std::unique_ptr<SimVertex>> vertices( + const std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Get beam particles + /// @param event event in HepMC data type + /// @return List of beam particles + std::vector<std::unique_ptr<SimParticle>> beams( + const std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Get final state particles + /// @param event event in HepMC data type + /// @return List of final state particles + std::vector<std::unique_ptr<SimParticle>> finalState( + const std::shared_ptr<HepMC3::GenEvent> event); + + private: + /// @brief Converts an SimParticle into HepMC3::GenParticle + /// @note The conversion ignores HepMC status codes + /// @param actsParticle Acts particle that will be converted + /// @return converted particle + HepMC3::GenParticlePtr actsParticleToGen( + std::shared_ptr<SimParticle> actsParticle); + + /// @brief Converts an Acts vertex to a HepMC3::GenVertexPtr + /// @note The conversion ignores HepMC status codes + /// @param actsVertex Acts vertex that will be converted + /// @return Converted Acts vertex to HepMC3::GenVertexPtr + HepMC3::GenVertexPtr createGenVertex( + const std::shared_ptr<SimVertex>& actsVertex); + + /// @brief Compares an Acts vertex with a HepMC3::GenVertex + /// @note An Acts vertex does not store a barcode. Therefore the content of + /// both vertices is compared. The position, time and number of incoming and + /// outgoing particles will be compared. Since a second vertex could exist in + /// the record with identical informations (although unlikely), this + /// comparison could lead to false positive results. On the other hand, a + /// numerical deviation of the parameters could lead to a false negative. + /// @param actsVertex Acts vertex + /// @param genVertex HepMC3::GenVertex + /// @return boolean result if both vertices are identical + bool compareVertices(const std::shared_ptr<SimVertex>& actsVertex, + const HepMC3::GenVertexPtr& genVertex); +}; +} // namespace FW diff --git a/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Particle.hpp b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Particle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8bfcdac829cefddf98187fad75e8529d37782327 --- /dev/null +++ b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Particle.hpp @@ -0,0 +1,95 @@ +// 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 <HepMC3/FourVector.h> +#include <HepMC3/GenParticle.h> +#include <HepMC3/GenVertex.h> +#include <HepPID/ParticleIDMethods.hh> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/EventData/SimVertex.hpp" + +namespace FW { + +/// Helper struct to convert HepMC3 particles to internal format. +struct HepMC3Particle { + public: + /// @brief Returns the particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return corresponding Acts particle + std::unique_ptr<SimParticle> particle( + const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the id of the particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return id of the particle + int id(const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the production vertex of the particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return production vertex of the particle + std::unique_ptr<SimVertex> productionVertex( + const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the end vertex of the particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return end vertex of the particle + std::unique_ptr<SimVertex> endVertex( + const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the PDG code of a particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return PDG code of the particle + int pdgID(const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the momentum of a particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return momentum of the particle + Acts::Vector3D momentum(const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the energy of a particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return energy of the particle + double energy(const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the mass of a particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return mass of the particle + double mass(const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Returns the charge of a particle translated into Acts + /// @param particle HepMC3::GenParticle particle + /// @return charge of the particle + double charge(const std::shared_ptr<HepMC3::GenParticle> particle); + + /// @brief Sets the PDG code of a particle translated from Acts + /// @param particle HepMC3::GenParticle particle + /// @param pid PDG code that will be set + void pdgID(std::shared_ptr<HepMC3::GenParticle> particle, const int pid); + + /// @brief Sets the momentum of a particle translated from Acts + /// @param particle HepMC3::GenParticle particle + /// @param mom momentum that will be set + void momentum(std::shared_ptr<HepMC3::GenParticle> particle, + const Acts::Vector3D& mom); + + /// @brief Sets the energy of a particle translated from Acts + /// @param particle HepMC3::GenParticle particle + /// @param energy energy that will be set + void energy(std::shared_ptr<HepMC3::GenParticle> particle, + const double energy); + + /// @brief Sets the mass of a particle translated from Acts + /// @param particle HepMC3::GenParticle particle + /// @param mass mass that will be set + void mass(std::shared_ptr<HepMC3::GenParticle> particle, const double mass); +}; + +} // namespace FW diff --git a/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Reader.hpp b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Reader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c8b1c34d4ecb0ac50dca6040842d5391fc7a1cb --- /dev/null +++ b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Reader.hpp @@ -0,0 +1,31 @@ +// 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 <HepMC3/GenEvent.h> +#include <HepMC3/ReaderAscii.h> + +namespace FW { + +/// HepMC3 event reader. +struct HepMC3ReaderAscii { + public: + /// @brief Reads an event from file + /// @param reader reader of run files + /// @param event storage of the read event + /// @return boolean indicator if the reading was successful + bool readEvent(HepMC3::ReaderAscii& reader, + std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Reports the status of the reader + /// @param reader reader of run files + /// @return boolean status indicator + bool status(HepMC3::ReaderAscii& reader); +}; +} // namespace FW diff --git a/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Vertex.hpp b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Vertex.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dfcf70625804460edecaad764a9559988e6ad9e5 --- /dev/null +++ b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Vertex.hpp @@ -0,0 +1,121 @@ +// 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 <HepMC3/FourVector.h> +#include <HepMC3/GenParticle.h> +#include <HepMC3/GenVertex.h> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/EventData/SimVertex.hpp" + +namespace FW { + +/// Helper struct to convert HepMC3 vertex into the internal format. +struct HepMC3Vertex { + public: + /// @brief Returns a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @return corresponding Acts vertex + std::unique_ptr<SimVertex> processVertex( + const std::shared_ptr<HepMC3::GenVertex> vertex); + + /// @brief Returns a boolean expression if a vertex is in an event translated + /// into Acts + /// @param vertex vertex in HepMC data type + /// @return boolean expression if the vertex is in an event + bool inEvent(const std::shared_ptr<HepMC3::GenVertex> vertex); + + /// @brief Returns a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @return id of the vertex + int id(const std::shared_ptr<HepMC3::GenVertex> vertex); + + /// @brief Returns the incoming particles of a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @return incoming particles of the vertex + std::vector<SimParticle> particlesIn( + const std::shared_ptr<HepMC3::GenVertex> vertex); + + /// @brief Returns the outgoing particles of a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @return outgoing particles of the vertex + std::vector<SimParticle> particlesOut( + const std::shared_ptr<HepMC3::GenVertex> vertex); + + /// @brief Returns the position of a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @return position of the vertex + Acts::Vector3D position(const std::shared_ptr<HepMC3::GenVertex> vertex); + + /// @brief Returns the time of a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @return time of the vertex + double time(const std::shared_ptr<HepMC3::GenVertex> vertex); + + /// @brief Adds an incoming particle to a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @param particle incoming particle that will be added + void addParticleIn(std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle); + + /// @brief Adds an outgoing particle to a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @param particle outgoing particle that will be added + void addParticleOut(std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle); + + /// @brief Removes an incoming particle from a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @param particle incoming particle that will be removed + void removeParticleIn(std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle); + + /// @brief Removes an outgoing particle from a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @param particle outgoing particle that will be removed + void removeParticleOut(std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle); + + /// @brief Sets the position of a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @param pos new position of the vertex + void position(const std::shared_ptr<HepMC3::GenVertex> vertex, + Acts::Vector3D pos); + + /// @brief Sets the time of a vertex translated into Acts + /// @param vertex vertex in HepMC data type + /// @param time new time of the vertex + void time(const std::shared_ptr<HepMC3::GenVertex> vertex, double time); + + private: + /// @brief Converts HepMC3::GenParticle objects into Acts + /// @param genParticles list of HepMC3::GenParticle objects + /// @return converted list + std::vector<SimParticle> genParticlesToActs( + const std::vector<HepMC3::GenParticlePtr>& genParticles); + + /// @brief Converts an SimParticle into HepMC3::GenParticle + /// @note The conversion ignores HepMC status codes + /// @param actsParticle Acts particle that will be converted + /// @return converted particle + HepMC3::GenParticlePtr actsParticleToGen( + std::shared_ptr<SimParticle> actsParticle); + + /// @brief Finds a HepMC3::GenParticle from a list that matches an + /// SimParticle object + /// @param genParticles list of HepMC particles + /// @param actsParticle Acts particle + /// @return HepMC particle that matched with the Acts particle or nullptr if + /// no match was found + HepMC3::GenParticlePtr matchParticles( + const std::vector<HepMC3::GenParticlePtr>& genParticles, + std::shared_ptr<SimParticle> actsParticle); +}; +} // namespace FW diff --git a/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Writer.hpp b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Writer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e14f860d6ca952041879b86185764a823a3fa84c --- /dev/null +++ b/Io/HepMC3/include/ACTFW/Plugins/HepMC3/HepMC3Writer.hpp @@ -0,0 +1,33 @@ +// 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 <HepMC3/GenEvent.h> +#include <HepMC3/WriterAscii.h> + +namespace FW { + +/// HepMC3 event writer. +struct HepMC3WriterAscii { + public: + /// @brief Writes an event to file + /// @param writer writer of run files + /// @param event storage of the event + /// @return boolean indicator if the writing was successful + /// @note HepMC3 does not state a success or failure. The returned argument is + /// always true. + bool writeEvent(HepMC3::WriterAscii& writer, + std::shared_ptr<HepMC3::GenEvent> event); + + /// @brief Reports the status of the writer + /// @param writer writer of run files + /// @return boolean status indicator + bool status(HepMC3::WriterAscii& writer); +}; +} // namespace FW diff --git a/Io/HepMC3/src/HepMC3Event.cpp b/Io/HepMC3/src/HepMC3Event.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33f00a8f97a6b99900b4b8fb107a3ddaed644f75 --- /dev/null +++ b/Io/HepMC3/src/HepMC3Event.cpp @@ -0,0 +1,286 @@ +// 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/. + +#include "ACTFW/Plugins/HepMC3/HepMC3Event.hpp" + +#include "ACTFW/Plugins/HepMC3/HepMC3Particle.hpp" +#include "ACTFW/Plugins/HepMC3/HepMC3Vertex.hpp" + +/// +/// Setter +/// + +void FW::HepMC3Event::momentumUnit(std::shared_ptr<HepMC3::GenEvent> event, + const double momentumUnit) { + // Check, if the momentum unit fits Acts::units::_MeV or _GeV + HepMC3::Units::MomentumUnit mom; + if (momentumUnit == Acts::units::_MeV) + mom = HepMC3::Units::MomentumUnit::MEV; + else if (momentumUnit == Acts::units::_GeV) + mom = HepMC3::Units::MomentumUnit::GEV; + else { + // Report invalid momentum unit and set GeV + std::cout << "Invalid unit of momentum: " << momentumUnit << std::endl; + std::cout << "Momentum unit [GeV] will be used instead" << std::endl; + mom = HepMC3::Units::MomentumUnit::GEV; + } + // Set units + event->set_units(mom, event->length_unit()); +} + +void FW::HepMC3Event::lengthUnit(std::shared_ptr<HepMC3::GenEvent> event, + const double lengthUnit) { + // Check, if the length unit fits Acts::units::_mm or _cm + HepMC3::Units::LengthUnit len; + if (lengthUnit == Acts::units::_mm) + len = HepMC3::Units::LengthUnit::MM; + else if (lengthUnit == Acts::units::_cm) + len = HepMC3::Units::LengthUnit::CM; + else { + // Report invalid length unit and set mm + std::cout << "Invalid unit of length: " << lengthUnit << std::endl; + std::cout << "Length unit [mm] will be used instead" << std::endl; + len = HepMC3::Units::LengthUnit::MM; + } + + // Set units + event->set_units(event->momentum_unit(), len); +} + +void FW::HepMC3Event::shiftPositionBy(std::shared_ptr<HepMC3::GenEvent> event, + const Acts::Vector3D& deltaPos, + const double deltaTime) { + // Create HepMC3::FourVector from position and time for shift + const HepMC3::FourVector vec(deltaPos(0), deltaPos(1), deltaPos(2), + deltaTime); + event->shift_position_by(vec); +} + +void FW::HepMC3Event::shiftPositionTo(std::shared_ptr<HepMC3::GenEvent> event, + const Acts::Vector3D& pos, + const double time) { + // Create HepMC3::FourVector from position and time for the new position + const HepMC3::FourVector vec(pos(0), pos(1), pos(2), time); + event->shift_position_to(vec); +} + +void FW::HepMC3Event::shiftPositionTo(std::shared_ptr<HepMC3::GenEvent> event, + const Acts::Vector3D& pos) { + // Create HepMC3::FourVector from position and time for the new position + const HepMC3::FourVector vec(pos(0), pos(1), pos(2), event->event_pos().t()); + event->shift_position_to(vec); +} + +void FW::HepMC3Event::shiftPositionTo(std::shared_ptr<HepMC3::GenEvent> event, + const double time) { + // Create HepMC3::FourVector from position and time for the new position + const HepMC3::FourVector vec(event->event_pos().x(), event->event_pos().y(), + event->event_pos().z(), time); + event->shift_position_to(vec); +} + +/// +/// Adder +/// + +HepMC3::GenParticlePtr FW::HepMC3Event::actsParticleToGen( + std::shared_ptr<SimParticle> actsParticle) { + // Extract momentum and energy from Acts particle for HepMC3::FourVector + const auto mom4 = actsParticle->momentum4(); + const HepMC3::FourVector vec(mom4[0], mom4[1], mom4[2], mom4[3]); + // Create HepMC3::GenParticle + HepMC3::GenParticle genParticle(vec, actsParticle->pdg()); + genParticle.set_generated_mass(actsParticle->mass()); + + return std::shared_ptr<HepMC3::GenParticle>(&genParticle); +} + +void FW::HepMC3Event::addParticle(std::shared_ptr<HepMC3::GenEvent> event, + std::shared_ptr<SimParticle> particle) { + // Add new particle + event->add_particle(actsParticleToGen(particle)); +} + +HepMC3::GenVertexPtr FW::HepMC3Event::createGenVertex( + const std::shared_ptr<SimVertex>& actsVertex) { + const HepMC3::FourVector vec( + actsVertex->position4[0], actsVertex->position4[1], + actsVertex->position4[2], actsVertex->position4[3]); + + // Create vertex + HepMC3::GenVertex genVertex(vec); + + // Store incoming particles + for (auto& particle : actsVertex->incoming) { + HepMC3::GenParticlePtr genParticle = + actsParticleToGen(std::make_shared<SimParticle>(particle)); + genVertex.add_particle_in(genParticle); + } + // Store outgoing particles + for (auto& particle : actsVertex->outgoing) { + HepMC3::GenParticlePtr genParticle = + actsParticleToGen(std::make_shared<SimParticle>(particle)); + genVertex.add_particle_out(genParticle); + } + return std::shared_ptr<HepMC3::GenVertex>(&genVertex); +} + +void FW::HepMC3Event::addVertex(std::shared_ptr<HepMC3::GenEvent> event, + const std::shared_ptr<SimVertex> vertex) { + // Add new vertex + event->add_vertex(createGenVertex(vertex)); +} + +/// +/// Remover +/// + +void FW::HepMC3Event::removeParticle( + std::shared_ptr<HepMC3::GenEvent> event, + const std::shared_ptr<SimParticle>& particle) { + const std::vector<HepMC3::GenParticlePtr> genParticles = event->particles(); + const auto id = particle->particleId(); + // Search HepMC3::GenParticle with the same id as the Acts particle + for (auto& genParticle : genParticles) { + if (genParticle->id() == id) { + // Remove particle if found + event->remove_particle(genParticle); + break; + } + } +} + +bool FW::HepMC3Event::compareVertices( + const std::shared_ptr<SimVertex>& actsVertex, + const HepMC3::GenVertexPtr& genVertex) { + // Compare position, time, number of incoming and outgoing particles between + // both vertices. Return false if one criterium does not match, else true. + HepMC3::FourVector genVec = genVertex->position(); + if (actsVertex->position4[0] != genVec.x()) + return false; + if (actsVertex->position4[1] != genVec.y()) + return false; + if (actsVertex->position4[2] != genVec.z()) + return false; + if (actsVertex->position4[3] != genVec.t()) + return false; + if (actsVertex->incoming.size() != genVertex->particles_in().size()) + return false; + if (actsVertex->outgoing.size() != genVertex->particles_out().size()) + return false; + return true; +} + +void FW::HepMC3Event::removeVertex(std::shared_ptr<HepMC3::GenEvent> event, + const std::shared_ptr<SimVertex>& vertex) { + const std::vector<HepMC3::GenVertexPtr> genVertices = event->vertices(); + // Walk over every recorded vertex + for (auto& genVertex : genVertices) + if (compareVertices(vertex, genVertex)) { + // Remove vertex if it matches actsVertex + event->remove_vertex(genVertex); + break; + } +} + +/// +/// Getter +/// + +double FW::HepMC3Event::momentumUnit( + const std::shared_ptr<HepMC3::GenEvent> event) { + // HepMC allows only MEV and GEV. This allows an easy identification. + return (event->momentum_unit() == HepMC3::Units::MomentumUnit::MEV + ? Acts::units::_MeV + : Acts::units::_GeV); +} + +double FW::HepMC3Event::lengthUnit( + const std::shared_ptr<HepMC3::GenEvent> event) { + // HepMC allows only MM and CM. This allows an easy identification. + return (event->length_unit() == HepMC3::Units::LengthUnit::MM + ? Acts::units::_mm + : Acts::units::_cm); +} + +Acts::Vector3D FW::HepMC3Event::eventPos( + const std::shared_ptr<HepMC3::GenEvent> event) { + // Extract the position from HepMC3::FourVector + Acts::Vector3D vec; + vec(0) = event->event_pos().x(); + vec(1) = event->event_pos().y(); + vec(2) = event->event_pos().z(); + return vec; +} + +double FW::HepMC3Event::eventTime( + const std::shared_ptr<HepMC3::GenEvent> event) { + // Extract the time from HepMC3::FourVector + return event->event_pos().t(); +} + +std::vector<std::unique_ptr<FW::SimParticle>> FW::HepMC3Event::particles( + const std::shared_ptr<HepMC3::GenEvent> event) { + std::vector<std::unique_ptr<SimParticle>> actsParticles; + const std::vector<HepMC3::GenParticlePtr> genParticles = event->particles(); + + HepMC3Particle simPart; + + // Translate all particles + for (auto& genParticle : genParticles) + actsParticles.push_back(std::move( + simPart.particle(std::make_shared<HepMC3::GenParticle>(*genParticle)))); + + return std::move(actsParticles); +} + +std::vector<std::unique_ptr<FW::SimVertex>> FW::HepMC3Event::vertices( + const std::shared_ptr<HepMC3::GenEvent> event) { + std::vector<std::unique_ptr<SimVertex>> actsVertices; + const std::vector<HepMC3::GenVertexPtr> genVertices = event->vertices(); + + HepMC3Vertex simVert; + + // Translate all vertices + for (auto& genVertex : genVertices) { + actsVertices.push_back(std::move(simVert.processVertex( + std::make_shared<HepMC3::GenVertex>(*genVertex)))); + } + return std::move(actsVertices); +} + +std::vector<std::unique_ptr<FW::SimParticle>> FW::HepMC3Event::beams( + const std::shared_ptr<HepMC3::GenEvent> event) { + std::vector<std::unique_ptr<SimParticle>> actsBeams; + const std::vector<HepMC3::GenParticlePtr> genBeams = event->beams(); + + HepMC3Particle simPart; + + // Translate beam particles and store the result + for (auto& genBeam : genBeams) + actsBeams.push_back(std::move( + simPart.particle(std::make_shared<HepMC3::GenParticle>(*genBeam)))); + return std::move(actsBeams); +} + +std::vector<std::unique_ptr<FW::SimParticle>> FW::HepMC3Event::finalState( + const std::shared_ptr<HepMC3::GenEvent> event) { + std::vector<HepMC3::GenParticlePtr> particles = event->particles(); + std::vector<std::unique_ptr<SimParticle>> fState; + + HepMC3Particle simPart; + + // Walk over every vertex + for (auto& particle : particles) { + // Collect particles without end vertex + if (!particle->end_vertex()) + fState.push_back( + simPart.particle(std::make_shared<HepMC3::GenParticle>(*particle))); + } + return fState; +} diff --git a/Io/HepMC3/src/HepMC3Particle.cpp b/Io/HepMC3/src/HepMC3Particle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ba3840ea9e1a2a64960a418c868bcf8135faecc --- /dev/null +++ b/Io/HepMC3/src/HepMC3Particle.cpp @@ -0,0 +1,105 @@ +// 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/. + +#include "ACTFW/Plugins/HepMC3/HepMC3Particle.hpp" + +#include "ACTFW/Plugins/HepMC3/HepMC3Vertex.hpp" + +std::unique_ptr<FW::SimParticle> FW::HepMC3Particle::particle( + const std::shared_ptr<HepMC3::GenParticle> particle) { + // TODO this is probably not quite right + ActsFatras::Barcode particleId; + particleId.setParticle(particle->id()); + SimParticle fw(particleId, static_cast<Acts::PdgParticle>(particle->pid()), + HepPID::charge(particle->pid()), particle->generated_mass()); + fw.setDirection(particle->momentum().x(), particle->momentum().y(), + particle->momentum().z()); + fw.setAbsMomentum(particle->momentum().p3mod()); + return std::make_unique<SimParticle>(std::move(fw)); +} + +int FW::HepMC3Particle::id( + const std::shared_ptr<HepMC3::GenParticle> particle) { + return particle->id(); +} + +std::unique_ptr<FW::SimVertex> FW::HepMC3Particle::productionVertex( + const std::shared_ptr<HepMC3::GenParticle> particle) { + HepMC3Vertex simVert; + + // Return the vertex if it exists + if (particle->production_vertex()) + return std::move(simVert.processVertex( + std::make_shared<HepMC3::GenVertex>(*particle->production_vertex()))); + else + return nullptr; +} + +std::unique_ptr<FW::SimVertex> FW::HepMC3Particle::endVertex( + const std::shared_ptr<HepMC3::GenParticle> particle) { + HepMC3Vertex simVert; + + // Return the vertex if it exists + if (particle->end_vertex()) + return std::move(simVert.processVertex( + std::make_shared<HepMC3::GenVertex>(*(particle->end_vertex())))); + else + return nullptr; +} + +int FW::HepMC3Particle::pdgID( + const std::shared_ptr<HepMC3::GenParticle> particle) { + return particle->pid(); +} + +Acts::Vector3D FW::HepMC3Particle::momentum( + const std::shared_ptr<HepMC3::GenParticle> particle) { + Acts::Vector3D mom; + mom(0) = particle->momentum().x(); + mom(1) = particle->momentum().y(); + mom(2) = particle->momentum().z(); + return mom; +} + +double FW::HepMC3Particle::energy( + const std::shared_ptr<HepMC3::GenParticle> particle) { + return particle->momentum().e(); +} + +double FW::HepMC3Particle::mass( + const std::shared_ptr<HepMC3::GenParticle> particle) { + return particle->generated_mass(); +} + +double FW::HepMC3Particle::charge( + const std::shared_ptr<HepMC3::GenParticle> particle) { + return HepPID::charge(particle->pid()); +} + +void FW::HepMC3Particle::pdgID(std::shared_ptr<HepMC3::GenParticle> particle, + const int pid) { + particle->set_pid(pid); +} + +void FW::HepMC3Particle::momentum(std::shared_ptr<HepMC3::GenParticle> particle, + const Acts::Vector3D& mom) { + HepMC3::FourVector fVec(mom(0), mom(1), mom(2), particle->momentum().e()); + particle->set_momentum(fVec); +} + +void FW::HepMC3Particle::energy(std::shared_ptr<HepMC3::GenParticle> particle, + const double energy) { + HepMC3::FourVector fVec(particle->momentum().x(), particle->momentum().y(), + particle->momentum().z(), energy); + particle->set_momentum(fVec); +} + +void FW::HepMC3Particle::mass(std::shared_ptr<HepMC3::GenParticle> particle, + const double mass) { + particle->set_generated_mass(mass); +} diff --git a/Io/HepMC3/src/HepMC3Reader.cpp b/Io/HepMC3/src/HepMC3Reader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60fcf8f07f7cec282ad71261ef65c4429ccaf0d5 --- /dev/null +++ b/Io/HepMC3/src/HepMC3Reader.cpp @@ -0,0 +1,19 @@ +// 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/. + +#include "ACTFW/Plugins/HepMC3/HepMC3Reader.hpp" + +bool FW::HepMC3ReaderAscii::readEvent(HepMC3::ReaderAscii& reader, + std::shared_ptr<HepMC3::GenEvent> event) { + // Read event and store it + return reader.read_event(*event); +} + +bool FW::HepMC3ReaderAscii::status(HepMC3::ReaderAscii& reader) { + return !reader.failed(); +} diff --git a/Io/HepMC3/src/HepMC3Vertex.cpp b/Io/HepMC3/src/HepMC3Vertex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..98ee0496addf7be450ae9c4f53636f5a19d55b25 --- /dev/null +++ b/Io/HepMC3/src/HepMC3Vertex.cpp @@ -0,0 +1,132 @@ +// 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/. + +#include "ACTFW/Plugins/HepMC3/HepMC3Vertex.hpp" + +#include "ACTFW/Plugins/HepMC3/HepMC3Particle.hpp" + +std::vector<FW::SimParticle> FW::HepMC3Vertex::genParticlesToActs( + const std::vector<HepMC3::GenParticlePtr>& genParticles) { + HepMC3Particle simPart; + + std::vector<SimParticle> actsParticles; + // Translate all particles + for (auto& genParticle : genParticles) + actsParticles.push_back(*( + simPart.particle(std::make_shared<HepMC3::GenParticle>(*genParticle)))); + return actsParticles; +} + +std::unique_ptr<FW::SimVertex> FW::HepMC3Vertex::processVertex( + const std::shared_ptr<HepMC3::GenVertex> vertex) { + SimVertex vtx({vertex->position().x(), vertex->position().y(), + vertex->position().z(), vertex->position().t()}); + vtx.incoming = genParticlesToActs(vertex->particles_in()); + vtx.outgoing = genParticlesToActs(vertex->particles_out()); + // Create Acts vertex + return std::make_unique<SimVertex>(std::move(vtx)); +} + +bool FW::HepMC3Vertex::inEvent( + const std::shared_ptr<HepMC3::GenVertex> vertex) { + return vertex->in_event(); +} + +int FW::HepMC3Vertex::id(const std::shared_ptr<HepMC3::GenVertex> vertex) { + return vertex->id(); +} + +std::vector<FW::SimParticle> FW::HepMC3Vertex::particlesIn( + const std::shared_ptr<HepMC3::GenVertex> vertex) { + return genParticlesToActs(vertex->particles_in()); +} + +std::vector<FW::SimParticle> FW::HepMC3Vertex::particlesOut( + const std::shared_ptr<HepMC3::GenVertex> vertex) { + return genParticlesToActs(vertex->particles_out()); +} + +Acts::Vector3D FW::HepMC3Vertex::position( + const std::shared_ptr<HepMC3::GenVertex> vertex) { + Acts::Vector3D vec; + vec(0) = vertex->position().x(); + vec(1) = vertex->position().y(); + vec(2) = vertex->position().z(); + return vec; +} + +double FW::HepMC3Vertex::time(const std::shared_ptr<HepMC3::GenVertex> vertex) { + return vertex->position().t(); +} + +HepMC3::GenParticlePtr FW::HepMC3Vertex::actsParticleToGen( + std::shared_ptr<SimParticle> actsParticle) { + // Extract momentum and energy from Acts particle for HepMC3::FourVector + const auto mom = actsParticle->momentum4(); + const HepMC3::FourVector vec(mom[0], mom[1], mom[2], mom[3]); + // Create HepMC3::GenParticle + HepMC3::GenParticle genParticle(vec, actsParticle->pdg()); + genParticle.set_generated_mass(actsParticle->mass()); + + return std::shared_ptr<HepMC3::GenParticle>(&genParticle); +} + +void FW::HepMC3Vertex::addParticleIn(std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle) { + vertex->add_particle_in(actsParticleToGen(particle)); +} + +void FW::HepMC3Vertex::addParticleOut(std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle) { + vertex->add_particle_out(actsParticleToGen(particle)); +} + +HepMC3::GenParticlePtr FW::HepMC3Vertex::matchParticles( + const std::vector<HepMC3::GenParticlePtr>& genParticles, + std::shared_ptr<SimParticle> actsParticle) { + const auto id = actsParticle->particleId(); + // Search HepMC3::GenParticle with the same id as the Acts particle + for (auto& genParticle : genParticles) { + if (genParticle->id() == id) { + // Return particle if found + return genParticle; + } + } + return nullptr; +} + +void FW::HepMC3Vertex::removeParticleIn( + std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle) { + // Remove particle if it exists + if (HepMC3::GenParticlePtr genParticle = + matchParticles(vertex->particles_in(), particle)) + vertex->remove_particle_in(genParticle); +} + +void FW::HepMC3Vertex::removeParticleOut( + std::shared_ptr<HepMC3::GenVertex> vertex, + std::shared_ptr<SimParticle> particle) { + // Remove particle if it exists + if (HepMC3::GenParticlePtr genParticle = + matchParticles(vertex->particles_out(), particle)) + vertex->remove_particle_out(genParticle); +} + +void FW::HepMC3Vertex::position(const std::shared_ptr<HepMC3::GenVertex> vertex, + Acts::Vector3D pos) { + HepMC3::FourVector fVec(pos(0), pos(1), pos(2), vertex->position().t()); + vertex->set_position(fVec); +} + +void FW::HepMC3Vertex::time(const std::shared_ptr<HepMC3::GenVertex> vertex, + double time) { + HepMC3::FourVector fVec(vertex->position().x(), vertex->position().y(), + vertex->position().z(), time); + vertex->set_position(fVec); +} diff --git a/Io/HepMC3/src/HepMC3Writer.cpp b/Io/HepMC3/src/HepMC3Writer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cb6f7bf5c70fa5e519270df17cd4aa7e7162c68 --- /dev/null +++ b/Io/HepMC3/src/HepMC3Writer.cpp @@ -0,0 +1,20 @@ +// 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/. + +#include "ACTFW/Plugins/HepMC3/HepMC3Writer.hpp" + +bool FW::HepMC3WriterAscii::writeEvent( + HepMC3::WriterAscii& writer, std::shared_ptr<HepMC3::GenEvent> event) { + // Write event from storage + writer.write_event(*event); + return true; +} + +bool FW::HepMC3WriterAscii::status(HepMC3::WriterAscii& writer) { + return writer.failed(); +} diff --git a/Io/Json/CMakeLists.txt b/Io/Json/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..88d07d106424435f016609e87d85f462d8794fdb --- /dev/null +++ b/Io/Json/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library( + ActsExamplesIoJson SHARED + src/JsonMaterialWriter.cpp) +target_include_directories( + ActsExamplesIoJson + PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) +target_link_libraries( + ActsExamplesIoJson + PUBLIC ActsCore ActsJsonPlugin ActsExamplesFramework) + +install( + TARGETS ActsExamplesIoJson + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Io/Json/include/ACTFW/Plugins/Json/JsonMaterialWriter.hpp b/Io/Json/include/ACTFW/Plugins/Json/JsonMaterialWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3022a4cd722befaf750c3ebc3c2fae964ed54cc4 --- /dev/null +++ b/Io/Json/include/ACTFW/Plugins/Json/JsonMaterialWriter.hpp @@ -0,0 +1,79 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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/. + +/////////////////////////////////////////////////////////////////// +// JsonMaterialWriter.h +/////////////////////////////////////////////////////////////////// + +#pragma once + +#include <mutex> + +#include "ACTFW/Framework/ProcessCode.hpp" +#include "Acts/Geometry/GeometryID.hpp" +#include "Acts/Material/ISurfaceMaterial.hpp" +#include "Acts/Material/IVolumeMaterial.hpp" +#include "Acts/Plugins/Json/JsonGeometryConverter.hpp" +#include "Acts/Utilities/Definitions.hpp" +#include "Acts/Utilities/Logger.hpp" + +namespace Acts { + +class TrackingGeometry; + +using SurfaceMaterialMap = + std::map<GeometryID, std::shared_ptr<const ISurfaceMaterial>>; + +using VolumeMaterialMap = + std::map<GeometryID, std::shared_ptr<const IVolumeMaterial>>; + +using DetectorMaterialMaps = std::pair<SurfaceMaterialMap, VolumeMaterialMap>; +} // namespace Acts + +namespace FW { + +namespace Json { + +/// @class Json Material writer +/// +/// @brief Writes out Detector material maps +/// using the Json Geometry converter +class JsonMaterialWriter { + public: + /// Constructor + /// + /// @param cfg The configuration struct of the converter + JsonMaterialWriter(const Acts::JsonGeometryConverter::Config& cfg, + const std::string& fileName); + + /// Virtual destructor + ~JsonMaterialWriter(); + + /// Write out the material map + /// + /// @param detMaterial is the SurfaceMaterial and VolumeMaterial maps + void write(const Acts::DetectorMaterialMaps& detMaterial); + + /// Write out the material map from Geometry + /// + /// @param tGeometry is the TrackingGeometry + void write(const Acts::TrackingGeometry& tGeometry); + + private: + /// The config class of the converter + Acts::JsonGeometryConverter::Config m_cfg; + + /// The file name + std::string m_fileName; + + /// Private access to the logging instance + const Acts::Logger& logger() const { return *m_cfg.logger; } +}; + +} // namespace Json +} // namespace FW diff --git a/Io/Json/include/ACTFW/Plugins/Json/JsonSpacePointWriter.hpp b/Io/Json/include/ACTFW/Plugins/Json/JsonSpacePointWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3aacbae9627c8f9d6f8f6d78000fd3f53fc277a2 --- /dev/null +++ b/Io/Json/include/ACTFW/Plugins/Json/JsonSpacePointWriter.hpp @@ -0,0 +1,113 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/. + +/// @file +/// @date 2016-05-23 Initial version +/// @date 2017-08-07 Rewrite with new interfaces + +#pragma once + +#include <fstream> + +#include "ACTFW/EventData/DataContainers.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "ACTFW/Utilities/Paths.hpp" + +namespace FW { +namespace Json { + +/// Write out a space point collection in JSON format. +/// +/// This writes one file per event into the configured output directory. By +/// default it writes to the current working directory. Files are named +/// using the following schema +/// +/// event000000001-spacepoints.json +/// event000000002-spacepoints.json +/// +template <class T> +class JsonSpacePointWriter : public WriterT<GeometryIdMultimap<T>> { + public: + struct Config { + std::string collection; ///< which collection to write + std::string outputDir; ///< where to place output files + size_t outputPrecision = 6; ///< floating point precision + }; + + JsonSpacePointWriter(const Config& cfg, + Acts::Logging::Level level = Acts::Logging::INFO); + + protected: + FW::ProcessCode writeT( + const FW::AlgorithmContext& context, + const GeometryIdMultimap<T>& spacePoints) final override; + + private: + // since class itself is templated, base class template must be fixed + using Base = WriterT<GeometryIdMultimap<T>>; + + Config m_cfg; +}; + +} // namespace Json +} // namespace FW + +template <class T> +FW::Json::JsonSpacePointWriter<T>::JsonSpacePointWriter( + const FW::Json::JsonSpacePointWriter<T>::Config& cfg, + Acts::Logging::Level level) + : Base(cfg.collection, "JsonSpacePointWriter", level), m_cfg(cfg) { + if (m_cfg.collection.empty()) { + throw std::invalid_argument("Missing input collection"); + } +} + +template <class T> +FW::ProcessCode FW::Json::JsonSpacePointWriter<T>::writeT( + const FW::AlgorithmContext& context, + const GeometryIdMultimap<T>& spacePoints) { + // open per-event file + std::string path = perEventFilepath(m_cfg.outputDir, "spacepoints.json", + context.eventNumber); + std::ofstream os(path, std::ofstream::out | std::ofstream::trunc); + if (!os) { + throw std::ios_base::failure("Could not open '" + path + "' to write"); + } + + os << std::setprecision(m_cfg.outputPrecision); + os << "{\n"; + + bool firstVolume = true; + for (auto& volumeData : spacePoints) { + geo_id_value volumeID = volumeData.first; + + if (!firstVolume) + os << ",\n"; + os << " \"SpacePoints_" << volumeID << "\" : [\n"; + + bool firstPoint = true; + for (auto& layerData : volumeData.second) { + for (auto& moduleData : layerData.second) { + for (auto& data : moduleData.second) { + // set the comma correctly + if (!firstPoint) + os << ",\n"; + // write the space point + os << " [" << data.x() << ", " << data.y() << ", " << data.z() + << "]"; + firstPoint = false; + } + } + } + os << "]"; + firstVolume = false; + } + os << "\n}\n"; + + return ProcessCode::SUCCESS; +} diff --git a/Io/Json/src/JsonMaterialWriter.cpp b/Io/Json/src/JsonMaterialWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e592e3b44f489d606acdccbb7270b7d964d2cf30 --- /dev/null +++ b/Io/Json/src/JsonMaterialWriter.cpp @@ -0,0 +1,48 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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/Plugins/Json/JsonMaterialWriter.hpp" + +#include <fstream> +#include <ios> +#include <iostream> +#include <stdexcept> + +#include "Acts/Geometry/GeometryID.hpp" +#include "Acts/Material/BinnedSurfaceMaterial.hpp" + +FW::Json::JsonMaterialWriter::JsonMaterialWriter( + const Acts::JsonGeometryConverter::Config& cfg, const std::string& fileName) + : m_cfg(cfg), m_fileName(fileName) { + // Validate the configuration + if (m_cfg.name.empty()) { + throw std::invalid_argument("Missing service name"); + } +} + +FW::Json::JsonMaterialWriter::~JsonMaterialWriter() {} + +void FW::Json::JsonMaterialWriter::write( + const Acts::DetectorMaterialMaps& detMaterial) { + // Evoke the converter + Acts::JsonGeometryConverter jmConverter(m_cfg); + auto jout = jmConverter.materialMapsToJson(detMaterial); + // And write the file + std::ofstream ofj(m_fileName); + ofj << std::setw(4) << jout << std::endl; +} + +void FW::Json::JsonMaterialWriter::write( + const Acts::TrackingGeometry& tGeometry) { + // Evoke the converter + Acts::JsonGeometryConverter jmConverter(m_cfg); + auto jout = jmConverter.trackingGeometryToJson(tGeometry); + // And write the file + std::ofstream ofj(m_fileName); + ofj << std::setw(4) << jout << std::endl; +} diff --git a/Io/Obj/CMakeLists.txt b/Io/Obj/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5cad394e273f7efad2cfdaaa974c077d24ea0e52 --- /dev/null +++ b/Io/Obj/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library( + ActsExamplesIoObj SHARED + src/ObjHelper.cpp + src/ObjSurfaceWriter.cpp + src/ObjTrackingGeometryWriter.cpp) +target_include_directories( + ActsExamplesIoObj + PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) +target_link_libraries( + ActsExamplesIoObj + PUBLIC ActsCore ActsExamplesFramework Threads::Threads) + +install( + TARGETS ActsExamplesIoObj + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Io/Obj/include/ACTFW/Plugins/Obj/ObjHelper.hpp b/Io/Obj/include/ACTFW/Plugins/Obj/ObjHelper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d448a414eb8e201de0fe27f805ab7bda572059f3 --- /dev/null +++ b/Io/Obj/include/ACTFW/Plugins/Obj/ObjHelper.hpp @@ -0,0 +1,74 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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 <fstream> +#include <vector> + +#include "Acts/Utilities/Definitions.hpp" + +namespace FW { + +namespace Obj { + +/// This is the counter struct for keeping track of the vertices +struct VtnCounter { + unsigned int vcounter = 0; + unsigned int vtcounter = 0; + unsigned int ncounter = 0; +}; + +/// This will write a vertex to the fstream +/// @param stream is the stream where to write to +/// @param vertex is the vertex to be written out +/// @param cvertex is the current vertex number +void writeVTN(std::ofstream& stream, VtnCounter& vtnCounter, double scalor, + const Acts::Vector3D& vertex, const std::string& vtntype = "v", + bool point = false); + +/// construct vertical faces +/// this takes a range and constructs faces +void constructVerticalFaces(std::ofstream& stream, unsigned int start, + const std::vector<unsigned int>& vsides); + +/// This will write a planar face +/// - normal is given by cross product +/// +/// @param stream is the stream where to write to +/// @param face is the face to be written out +/// @param cvertex is the current vertex number +/// @param thickness is the (optional) thickness +void writePlanarFace(std::ofstream& stream, VtnCounter& vtnCounter, + double scalor, const std::vector<Acts::Vector3D>& vertices, + double thickness = 0., + const std::vector<unsigned int>& vsides = {}); + +/// This will write a cylindrical object +/// +/// @param stream is the stream where to write to +void writeTube(std::ofstream& stream, VtnCounter& vtnCounter, double scalor, + unsigned int nSegments, const Acts::Transform3D& transform, + double r, double hZ, double thickness = 0.); + +/// Helper method for bezier line interpolation +/// +/// @param t is the step parameter along the line +/// @param p0 anker point +/// @param p1 is p0 + direction@p0 +/// @param p2 is p2 - direction@p2 +/// @param p3 is second anker poit +/// +/// @return the bezier point +Acts::Vector3D calculateBezierPoint(double t, const Acts::Vector3D& p0, + const Acts::Vector3D& p1, + const Acts::Vector3D& p2, + const Acts::Vector3D& p3); + +} // namespace Obj +} // namespace FW diff --git a/Io/Obj/include/ACTFW/Plugins/Obj/ObjPropagationStepsWriter.hpp b/Io/Obj/include/ACTFW/Plugins/Obj/ObjPropagationStepsWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..40a7107139df456eb991f4ab92d5c4362432da2f --- /dev/null +++ b/Io/Obj/include/ACTFW/Plugins/Obj/ObjPropagationStepsWriter.hpp @@ -0,0 +1,103 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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 <fstream> + +#include "ACTFW/Framework/WriterT.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "Acts/Propagator/detail/SteppingLogger.hpp" + +namespace FW { + +namespace Obj { + +/// @class ObjPropagationStepsWriter +/// +/// Write out the steps of test propgations for stepping validation +/// Writes one file per event with form: +/// +/// event000000001-propagation-steps.obj +/// event000000002-propagation-steps.obj +/// +/// One Thread per write call and hence thread safe +template <typename step_t> +class ObjPropagationStepsWriter + : public WriterT<std::vector<std::vector<step_t>>> { + public: + struct Config { + std::string collection; ///< which collection to write + std::string outputDir; ///< where to place output files + double outputScalor = 1.0; ///< scale output values + size_t outputPrecision = 6; ///< floating point precision + }; + + /// Constructor with arguments + /// + /// @param cfg configuration struct + /// @param level Output logging level + ObjPropagationStepsWriter(const Config& cfg, + Acts::Logging::Level level = Acts::Logging::INFO) + : WriterT<std::vector<std::vector<step_t>>>(cfg.collection, + "ObjSpacePointWriter", level), + m_cfg(cfg) { + if (m_cfg.collection.empty()) { + throw std::invalid_argument("Missing input collection"); + } + } + + /// Virtual destructor + ~ObjPropagationStepsWriter() override = default; + + /// End-of-run hook + ProcessCode endRun() final override { return FW::ProcessCode::SUCCESS; } + + private: + Config m_cfg; ///!< Internal configuration represenation + + protected: + /// This implementation holds the actual writing method + /// and is called by the WriterT<>::write interface + ProcessCode writeT( + const AlgorithmContext& context, + const std::vector<std::vector<step_t>>& stepCollection) final override { + // open per-event file + std::string path = FW::perEventFilepath( + m_cfg.outputDir, "propagation-steps.obj", context.eventNumber); + std::ofstream os(path, std::ofstream::out | std::ofstream::trunc); + if (!os) { + throw std::ios_base::failure("Could not open '" + path + "' to write"); + } + + // Initialize the vertex counter + unsigned int vCounter = 0; + + for (auto& steps : stepCollection) { + // At least three points to draw + if (steps.size() > 2) { + // We start from one + ++vCounter; + for (auto& step : steps) { + // Write the space point + os << "v " << m_cfg.outputScalor * step.position.x() << " " + << m_cfg.outputScalor * step.position.y() << " " + << m_cfg.outputScalor * step.position.z() << '\n'; + } + // Write out the line - only if we have at least two points created + size_t vBreak = vCounter + steps.size() - 1; + for (; vCounter < vBreak; ++vCounter) + os << "l " << vCounter << " " << vCounter + 1 << '\n'; + } + } + return FW::ProcessCode::SUCCESS; + } +}; + +} // namespace Obj +} // namespace FW diff --git a/Io/Obj/include/ACTFW/Plugins/Obj/ObjSpacePointWriter.hpp b/Io/Obj/include/ACTFW/Plugins/Obj/ObjSpacePointWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f55113adea8b9d79376583921d1da08223a0e6e1 --- /dev/null +++ b/Io/Obj/include/ACTFW/Plugins/Obj/ObjSpacePointWriter.hpp @@ -0,0 +1,96 @@ +// 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/. + +#pragma once + +#include <fstream> + +#include "ACTFW/EventData/GeometryContainers.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "ACTFW/Utilities/Paths.hpp" + +namespace FW { +namespace Obj { + +/// Write out a space point collection in OBJ format. +/// +/// This writes one file per event into the configured output directory. By +/// default it writes to the current working directory. Files are named +/// using the following schema +/// +/// event000000001-spacepoints.obj +/// event000000002-spacepoints.obj +/// +/// One write call per thread and hence thread safe. +template <typename T> +class ObjSpacePointWriter : public WriterT<GeometryIdMultimap<T>> { + public: + struct Config { + std::string collection; ///< which collection to write + std::string outputDir; ///< where to place output files + double outputScalor = 1.0; ///< scale output values + size_t outputPrecision = 6; ///< floating point precision + }; + + ObjSpacePointWriter(const Config& cfg, + Acts::Logging::Level level = Acts::Logging::INFO); + + protected: + ProcessCode writeT(const AlgorithmContext& context, + const GeometryIdMultimap<T>& spacePoints); + + private: + // since class iitself is templated, base class template must be fixed + using Base = WriterT<GeometryIdMultimap<T>>; + + Config m_cfg; +}; + +} // namespace Obj +} // namespace FW + +template <typename T> +inline FW::Obj::ObjSpacePointWriter<T>::ObjSpacePointWriter( + const ObjSpacePointWriter<T>::Config& cfg, Acts::Logging::Level level) + : Base(cfg.collection, "ObjSpacePointWriter", level), m_cfg(cfg) { + if (m_cfg.collection.empty()) { + throw std::invalid_argument("Missing input collection"); + } +} + +template <typename T> +inline FW::ProcessCode FW::Obj::ObjSpacePointWriter<T>::writeT( + const FW::AlgorithmContext& context, + const FW::GeometryIdMultimap<T>& spacePoints) { + // open per-event file + std::string path = FW::perEventFilepath(m_cfg.outputDir, "spacepoints.obj", + context.eventNumber); + std::ofstream os(path, std::ofstream::out | std::ofstream::trunc); + if (!os) { + throw std::ios_base::failure("Could not open '" + path + "' to write"); + } + + os << std::setprecision(m_cfg.outputPrecision); + // count the vertex + size_t vertex = 0; + // loop and fill the space point data + for (auto& volumeData : spacePoints) { + for (auto& layerData : volumeData.second) { + for (auto& moduleData : layerData.second) { + for (auto& data : moduleData.second) { + // write the space point + os << "v " << m_cfg.outputScalor * data.x() << " " + << m_cfg.outputScalor * data.y() << " " + << m_cfg.outputScalor * data.z() << '\n'; + os << "p " << ++vertex << '\n'; + } + } + } + } + return ProcessCode::SUCCESS; +} diff --git a/Io/Obj/include/ACTFW/Plugins/Obj/ObjSurfaceWriter.hpp b/Io/Obj/include/ACTFW/Plugins/Obj/ObjSurfaceWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9415930a8d7d6f1656539679ed5937a20e820e8b --- /dev/null +++ b/Io/Obj/include/ACTFW/Plugins/Obj/ObjSurfaceWriter.hpp @@ -0,0 +1,102 @@ +// 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/. + +#pragma once + +#include <Acts/Surfaces/Surface.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <fstream> +#include <iostream> +#include <mutex> + +#include "ACTFW/Framework/AlgorithmContext.hpp" +#include "ACTFW/Framework/ProcessCode.hpp" +#include "ACTFW/Plugins/Obj/ObjHelper.hpp" + +namespace FW { +namespace Obj { + +/// @class ObjSurfaceWriter +/// +/// An Obj writer for the geometry: surface section +/// +class ObjSurfaceWriter { + public: + // @class Config + // + // The nested config class for the Surface writer + class Config { + public: + /// the default logger + std::shared_ptr<const Acts::Logger> logger; + /// the name of the algorithm + std::string name; + /// approximate cyinders by that + unsigned int outputPhiSegemnts = 72; + /// write thickness if available + double outputThickness = 2.; + /// write sensitive surfaces + bool outputSensitive = true; + /// write the layer surface out + bool outputLayerSurface = true; + /// output scalor + double outputScalor = 1.; + /// precision for out + unsigned int outputPrecision = 6; + /// file prefix to be written out + std::string filePrefix = ""; + /// prefixes + /// @todo These aren't used anywhere, should they be dropped? + std::string planarPrefix = ""; + std::string cylinderPrefix = ""; + std::string diskPrefix = ""; + /// the output stream + std::shared_ptr<std::ofstream> outputStream = nullptr; + + Config(const std::string& lname = "ObjSurfaceWriter", + Acts::Logging::Level lvl = Acts::Logging::INFO) + : logger(Acts::getDefaultLogger(lname, lvl)), name(lname) {} + }; + + /// Constructor + /// + /// @param cfg is the configuration class + ObjSurfaceWriter(const Config& cfg); + + /// Framework name() method + std::string name() const; + + /// The write interface + /// @param context the Algorithm/Event context of this call + /// @param surface to be written out + FW::ProcessCode write(const AlgorithmContext& context, + const Acts::Surface& surface); + + /// write a bit of string + /// @param is the string to be written + FW::ProcessCode write(const std::string& sinfo); + + private: + Config m_cfg; ///< the config class + Obj::VtnCounter m_vtnCounter; ///< vertex, texture, normal + std::mutex m_write_mutex; ///< mutex to protect multi-threaded writes + + /// Private access to the logging instance + const Acts::Logger& logger() const { return *m_cfg.logger; } +}; + +inline FW::ProcessCode ObjSurfaceWriter::write(const std::string& sinfo) { + // lock the mutex for writing + std::lock_guard<std::mutex> lock(m_write_mutex); + // and write + (*m_cfg.outputStream) << sinfo; + return FW::ProcessCode::SUCCESS; +} + +} // namespace Obj +} // namespace FW diff --git a/Io/Obj/include/ACTFW/Plugins/Obj/ObjTrackingGeometryWriter.hpp b/Io/Obj/include/ACTFW/Plugins/Obj/ObjTrackingGeometryWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f8f2374460cfc1b1ca6bc80d5a507863bb641b83 --- /dev/null +++ b/Io/Obj/include/ACTFW/Plugins/Obj/ObjTrackingGeometryWriter.hpp @@ -0,0 +1,84 @@ +// 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/. + +#pragma once + +#include <Acts/Utilities/Logger.hpp> +#include <fstream> +#include <iostream> +#include <mutex> + +#include "ACTFW/Framework/ProcessCode.hpp" +#include "ACTFW/Plugins/Obj/ObjSurfaceWriter.hpp" + +namespace Acts { +class TrackingVolume; +class TrackingGeometry; +} // namespace Acts + +namespace FW { +namespace Obj { + +/// @class ObjTrackingGeometryWriter +/// +/// An Obj writer for the geometry: TrackingGeometry master +/// It delegates the writing of surfaces to the surface writers +class ObjTrackingGeometryWriter { + public: + // @class Config + // + // The nested config class + class Config { + public: + /// the default logger + std::shared_ptr<const Acts::Logger> logger; + /// the name of the writer + std::string name = ""; + /// surfaceWriters + std::vector<std::shared_ptr<ObjSurfaceWriter>> surfaceWriters; + std::string filePrefix = ""; + std::string sensitiveGroupPrefix = ""; + std::string layerPrefix = ""; + + Config(const std::string& lname = "ObjTrackingGeometryWriter", + Acts::Logging::Level lvl = Acts::Logging::INFO) + : logger(Acts::getDefaultLogger(lname, lvl)), + name(lname), + surfaceWriters() {} + }; + + /// Constructor + /// @param cfg is the configuration class + ObjTrackingGeometryWriter(const Config& cfg); + + /// Framework name() method + /// @return the name of the tool + std::string name() const; + + /// The write interface + /// @param context the Algorithm/Event context of this call + /// @param tGeometry is the geometry to be written out + /// @return ProcessCode to indicate success/failure + FW::ProcessCode write(const AlgorithmContext& context, + const Acts::TrackingGeometry& tGeometry); + + private: + Config m_cfg; ///< the config class + + /// process this volume + /// @param context the Algorithm/Event context for this call + /// @param tVolume the volume to be processed + void write(const AlgorithmContext& context, + const Acts::TrackingVolume& tVolume); + + /// Private access to the logging instance + const Acts::Logger& logger() const { return *m_cfg.logger; } +}; + +} // namespace Obj +} // namespace FW diff --git a/Io/Obj/include/ACTFW/Plugins/Obj/ObjWriterOptions.hpp b/Io/Obj/include/ACTFW/Plugins/Obj/ObjWriterOptions.hpp new file mode 100644 index 0000000000000000000000000000000000000000..41adb932cbcc632dd6a54e12e9fad0bff780e437 --- /dev/null +++ b/Io/Obj/include/ACTFW/Plugins/Obj/ObjWriterOptions.hpp @@ -0,0 +1,87 @@ +// 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/. + +#pragma once + +#include <iostream> + +#include "ACTFW/Plugins/Obj/ObjSurfaceWriter.hpp" +#include "ACTFW/Plugins/Obj/ObjTrackingGeometryWriter.hpp" +#include "ACTFW/Utilities/Options.hpp" +#include "Acts/Utilities/Logger.hpp" + +namespace po = boost::program_options; + +namespace FW { + +namespace Options { + +/// Common obj writing options +/// +/// @tparam aopt_t Type of the options object (from BOOST) +/// +/// @param opt The options object, where string based options are attached +template <typename aopt_t> +void addObjWriterOptions(aopt_t& opt) { + opt.add_options()("obj-tg-fileheader", + po::value<std::string>()->default_value(""), + "The (optional) file header for the tracking geometry.")( + "obj-tg-sensitiveheader", po::value<std::string>()->default_value(""), + "The (optional) header in front of sensitive sensors.")( + "obj-tg-layerheader", po::value<std::string>()->default_value(""), + "The (optional) header in front of layer surfaces.")( + "obj-sf-fileheader", po::value<std::string>()->default_value(""), + "The (optional) file header for the surface writer.")( + "obj-sf-phisegments", po::value<int>()->default_value(72), + "Number of phi segments to approximate curves.")( + "obj-sf-outputPrecission", po::value<int>()->default_value(6), + "Floating number output precission.")( + "obj-sf-outputScalor", po::value<double>()->default_value(1.), + "Scale factor to be applied.")("obj-sf-outputThickness", + po::value<double>()->default_value(1.), + "The surface thickness.")( + "obj-sf-outputSensitive", po::value<bool>()->default_value(true), + "Write sensitive surfaces.")("obj-sf-outputLayers", + po::value<bool>()->default_value(true), + "Write layer surfaces."); +} + +/// read the evgen options and return a Config file +template <class AMAP> +FW::Obj::ObjTrackingGeometryWriter::Config readObjTrackingGeometryWriterConfig( + const AMAP& vm, const std::string& name, + Acts::Logging::Level loglevel = Acts::Logging::INFO) { + FW::Obj::ObjTrackingGeometryWriter::Config objTgConfig(name, loglevel); + objTgConfig.filePrefix = vm["obj-tg-fileheader"].template as<std::string>(); + objTgConfig.sensitiveGroupPrefix = + vm["obj-tg-sensitiveheader"].template as<std::string>(); + objTgConfig.layerPrefix = vm["obj-tg-layerheader"].template as<std::string>(); + return objTgConfig; +} + +template <class AMAP> +FW::Obj::ObjSurfaceWriter::Config readObjSurfaceWriterConfig( + const AMAP& vm, const std::string& name, Acts::Logging::Level loglevel) { + FW::Obj::ObjSurfaceWriter::Config objSfConfig(name, + loglevel = Acts::Logging::INFO); + objSfConfig.filePrefix = vm["obj-sf-fileheader"].template as<std::string>(); + objSfConfig.outputPhiSegemnts = vm["obj-sf-phisegments"].template as<int>(); + objSfConfig.outputPrecision = + vm["obj-sf-outputPrecission"].template as<int>(); + objSfConfig.outputScalor = vm["obj-sf-outputScalor"].template as<double>(); + objSfConfig.outputThickness = + vm["obj-sf-outputThickness"].template as<double>(); + objSfConfig.outputSensitive = + vm["obj-sf-outputSensitive"].template as<bool>(); + objSfConfig.outputLayerSurface = + vm["obj-sf-outputLayers"].template as<bool>(); + return objSfConfig; +} + +} // namespace Options +} // namespace FW diff --git a/Io/Obj/src/ObjHelper.cpp b/Io/Obj/src/ObjHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..294a6c2ee7c805c5b099960f9c02f643c7651d77 --- /dev/null +++ b/Io/Obj/src/ObjHelper.cpp @@ -0,0 +1,191 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/Plugins/Obj/ObjHelper.hpp" + +#include <vector> + +void FW::Obj::writeVTN(std::ofstream& stream, VtnCounter& vtnCounter, + double scalor, const Acts::Vector3D& vertex, + const std::string& vtntype, bool point) { + // in case you make a point + unsigned int cp = 0; + // the counter + if (vtntype == "v") { + ++vtnCounter.vcounter; + cp = vtnCounter.vcounter; + } else if (vtntype == "t") { + ++vtnCounter.vtcounter; + cp = vtnCounter.vtcounter; + } else if (vtntype == "vn") { + ++vtnCounter.ncounter; + cp = vtnCounter.ncounter; + } else + return; + + // write out the vertex, texture vertex, normal + stream << vtntype << " " << scalor * vertex.x() << " " << scalor * vertex.y() + << " " << scalor * vertex.z() << '\n'; + // we create a point if needed + if (point) + stream << "p " << cp; +} + +void FW::Obj::constructVerticalFaces(std::ofstream& stream, unsigned int start, + const std::vector<unsigned int>& vsides) { + // construct the vertical faces + size_t nsides = vsides.size(); + unsigned int sstart = start; + for (auto vside : vsides) { + if (vside) { + // start streaming the side + // all but the last + if (start - sstart < nsides - 1) { + stream << "f " << start << " " << start + 1 << " "; + stream << start + nsides + 1 << " " << start + nsides; + } else { + stream << "f " << start << " " << sstart << " "; + stream << sstart + nsides << " " << start + nsides; + } + } + stream << '\n'; + // increase + ++start; + } +} + +void FW::Obj::writePlanarFace(std::ofstream& stream, VtnCounter& vtnCounter, + double scalor, + const std::vector<Acts::Vector3D>& vertices, + double thickness, + const std::vector<unsigned int>& vsides) { + // minimum 3 vertices needed + if (vertices.size() < 3) + return; + // the first vertex + unsigned int fvertex = vtnCounter.vcounter + 1; + // lets create the normal vector first + Acts::Vector3D sideOne = vertices[1] - vertices[0]; + Acts::Vector3D sideTwo = vertices[2] - vertices[1]; + Acts::Vector3D nvector(sideTwo.cross(sideOne).normalized()); + // thickness or not thickness + std::vector<int> sides = {0}; + if (thickness != 0.) + sides = {-1, 1}; + // now write all the vertices - this works w/wo thickness + for (auto side : sides) { + // save the current vertex counter + unsigned int cvc = vtnCounter.vcounter; + // loop over the sides + for (auto v : vertices) + writeVTN(stream, vtnCounter, scalor, + v + (0.5 * side * thickness) * nvector, "v"); + + // now write the face + stream << "f "; + for (auto n = vertices.size(); 0 < n; --n) + stream << ++cvc << " "; + stream << '\n'; + } + // now process the vertical sides + constructVerticalFaces(stream, fvertex, vsides); +} + +void FW::Obj::writeTube(std::ofstream& stream, VtnCounter& vtnCounter, + double scalor, unsigned int nSegments, + const Acts::Transform3D& transform, double r, double hZ, + double thickness) { + // flip along plus/minus and declare the faces + std::vector<int> flip = {-1, 1}; + std::vector<int> vfaces = {1, 2, 4, 3}; + // the number of phisteps + double phistep = 2 * M_PI / nSegments; + // make it twice if necessary + std::vector<double> roffsets = {0.}; + if (thickness != 0.) + roffsets = {-0.5 * thickness, 0.5 * thickness}; + // now loop over the thickness and make an outer and inner + unsigned int cvc = vtnCounter.vcounter; + size_t iside = 0; + for (auto t : roffsets) { + size_t iphi = 0; + // loop over phi steps + for (; iphi < nSegments; ++iphi) { + // currentPhi + double phi = -M_PI + iphi * phistep; + for (auto iflip : flip) { + // create the vertex + Acts::Vector3D point(transform * Acts::Vector3D((r + t) * cos(phi), + (r + t) * sin(phi), + iflip * hZ)); + // write the normal vector + writeVTN(stream, vtnCounter, scalor, point, "v"); + } + } + // now create the faces + iphi = 0; + // side offset for faces + unsigned int soff = 2 * iside * nSegments; + for (; iphi < nSegments - 1; ++iphi) { + // output to file + stream << "f "; + for (auto face : vfaces) + stream << soff + cvc + (2 * iphi) + face << " "; + stream << '\n'; + } + // close the loop + stream << "f " << soff + cvc + (2 * iphi) + 1 << " " + << soff + cvc + (2 * iphi) + 2 << " " << soff + cvc + 2 << " " + << soff + cvc + 1 << '\n'; + // new line at the end of the line + stream << '\n'; + ++iside; + } + + // construct the sides at the end when all vertices are done + // Acts::Vector3D nvectorSide = transform.rotation().col(2); + if (thickness != 0.) { + // loop over the two sides + for (iside = 0; iside < 2; ++iside) { + // rest iphi + size_t iphi = 0; + for (; iphi < nSegments - 1; ++iphi) { + stream << "f "; + unsigned int base = cvc + (2 * iphi) + 1; + stream << iside + base << " "; + stream << iside + base + 2 << " "; + stream << iside + base + (2 * nSegments) + 2 << " "; + stream << iside + base + (2 * nSegments) << '\n'; + } + // close the loop + stream << "f "; + stream << iside + cvc + (2 * iphi) + 1 << " "; + stream << iside + cvc + 1 << " "; + stream << iside + cvc + 1 + (2 * nSegments) << " "; + stream << iside + cvc + (2 * iphi) + 1 + (2 * nSegments) << '\n'; + } + } +} + +// Bezier interpolation, see documentation +Acts::Vector3D FW::Obj::calculateBezierPoint(double t, const Acts::Vector3D& p0, + const Acts::Vector3D& p1, + const Acts::Vector3D& p2, + const Acts::Vector3D& p3) { + double u = 1. - t; + double tt = t * t; + double uu = u * u; + double uuu = uu * u; + double ttt = tt * t; + + Acts::Vector3D p = uuu * p0; // first term + p += 3 * uu * t * p1; // second term + p += 3 * u * tt * p2; // third term + p += ttt * p3; // fourth term + return p; +} diff --git a/Io/Obj/src/ObjSurfaceWriter.cpp b/Io/Obj/src/ObjSurfaceWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3efc747ac6d778deed1961b6d71af666d861bae7 --- /dev/null +++ b/Io/Obj/src/ObjSurfaceWriter.cpp @@ -0,0 +1,127 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/Plugins/Obj/ObjSurfaceWriter.hpp" + +#include <Acts/Geometry/GeometryID.hpp> +#include <Acts/Geometry/Layer.hpp> +#include <Acts/Surfaces/CylinderBounds.hpp> +#include <Acts/Surfaces/PlanarBounds.hpp> +#include <Acts/Surfaces/RadialBounds.hpp> +#include <Acts/Surfaces/SurfaceBounds.hpp> +#include <ios> +#include <iostream> +#include <stdexcept> + +FW::Obj::ObjSurfaceWriter::ObjSurfaceWriter( + const FW::Obj::ObjSurfaceWriter::Config& cfg) + : m_cfg(cfg) { + // Validate the configuration + if (!m_cfg.logger) { + throw std::invalid_argument("Missing logger"); + } else if (m_cfg.name.empty()) { + throw std::invalid_argument("Missing algorithm name"); + } else if (!m_cfg.outputStream) { + throw std::invalid_argument("Missing output stream"); + } + + // Write down the file prefix + (*(m_cfg.outputStream)) << m_cfg.filePrefix << '\n'; +} + +std::string FW::Obj::ObjSurfaceWriter::name() const { + return m_cfg.name; +} + +FW::ProcessCode FW::Obj::ObjSurfaceWriter::write( + const AlgorithmContext& context, const Acts::Surface& surface) { + std::lock_guard<std::mutex> lock(m_write_mutex); + + ACTS_DEBUG(">>Obj: Writer for Surface object called."); + + auto scalor = m_cfg.outputScalor; + // let's get the bounds & the transform + const Acts::SurfaceBounds& surfaceBounds = surface.bounds(); + auto sTransform = surface.transform(context.geoContext); + + // dynamic_cast to PlanarBounds + const Acts::PlanarBounds* planarBounds = + dynamic_cast<const Acts::PlanarBounds*>(&surfaceBounds); + // only continue if the cast worked + if (planarBounds && m_cfg.outputSensitive) { + ACTS_VERBOSE(">>Obj: Writing out a PlaneSurface"); + // set the precision - just to be sure + (*(m_cfg.outputStream)) << '\n'; + (*(m_cfg.outputStream)) << std::setprecision(m_cfg.outputPrecision); + // get the vertices + auto planarVertices = planarBounds->vertices(); + // loop over the vertices + std::vector<Acts::Vector3D> vertices; + vertices.reserve(planarVertices.size()); + for (auto pv : planarVertices) { + // get the point in 3D + Acts::Vector3D v3D(sTransform * Acts::Vector3D(pv.x(), pv.y(), 0.)); + vertices.push_back(v3D); + } + // get the thickness and vertical faces + double thickness = 0.; + std::vector<unsigned int> vfaces; + if (surface.associatedDetectorElement() and m_cfg.outputThickness != 0.) { + // get the thickness form the detector element + thickness = surface.associatedDetectorElement()->thickness(); + vfaces = {1, 1, 1, 1}; + } + // output to file + Obj::writePlanarFace(*(m_cfg.outputStream), m_vtnCounter, scalor, vertices, + thickness, vfaces); + (*(m_cfg.outputStream)) << '\n'; + } + + // check if you have layer and check what your have + // dynamic cast to CylinderBounds work the same + const Acts::CylinderBounds* cylinderBounds = + dynamic_cast<const Acts::CylinderBounds*>(&surfaceBounds); + if (cylinderBounds && m_cfg.outputLayerSurface) { + ACTS_VERBOSE(">>Obj: Writing out a CylinderSurface with r = " + << cylinderBounds->get(Acts::CylinderBounds::eR)); + // name the object + auto layerID = surface.geoID().layer(); + (*(m_cfg.outputStream)) + << " o Cylinder_" << std::to_string(layerID) << '\n'; + // output to the file + Obj::writeTube(*(m_cfg.outputStream), m_vtnCounter, scalor, + m_cfg.outputPhiSegemnts, sTransform, + cylinderBounds->get(Acts::CylinderBounds::eR), + cylinderBounds->get(Acts::CylinderBounds::eHalfLengthZ), + m_cfg.outputThickness); + (*(m_cfg.outputStream)) << '\n'; + } + + ////dynamic cast to RadialBounds or disc bounds work the same + const Acts::RadialBounds* radialBounds = + dynamic_cast<const Acts::RadialBounds*>(&surfaceBounds); + if (radialBounds && m_cfg.outputLayerSurface) { + ACTS_VERBOSE(">>Obj: Writing out a DiskSurface at z = " + << sTransform.translation().z()); + // name the object + auto layerID = surface.geoID().layer(); + (*(m_cfg.outputStream)) << "o Disk_" << std::to_string(layerID) << '\n'; + // we use the tube writer in the other direction + double rMin = radialBounds->rMin(); + double rMax = radialBounds->rMax(); + double thickness = rMax - rMin; + // output to the file + Obj::writeTube(*(m_cfg.outputStream), m_vtnCounter, scalor, + m_cfg.outputPhiSegemnts, sTransform, 0.5 * (rMin + rMax), + m_cfg.outputThickness, thickness); + (*(m_cfg.outputStream)) << '\n'; + } + + // return success + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Obj/src/ObjTrackingGeometryWriter.cpp b/Io/Obj/src/ObjTrackingGeometryWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acddd4e1aa1813f8d73a1e062e85e27b515cef0d --- /dev/null +++ b/Io/Obj/src/ObjTrackingGeometryWriter.cpp @@ -0,0 +1,106 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/Plugins/Obj/ObjTrackingGeometryWriter.hpp" + +#include <Acts/Geometry/Layer.hpp> +#include <Acts/Geometry/TrackingGeometry.hpp> +#include <Acts/Geometry/TrackingVolume.hpp> +#include <Acts/Surfaces/Surface.hpp> +#include <iostream> + +FW::Obj::ObjTrackingGeometryWriter::ObjTrackingGeometryWriter( + const FW::Obj::ObjTrackingGeometryWriter::Config& cfg) + : m_cfg(cfg) {} + +std::string FW::Obj::ObjTrackingGeometryWriter::name() const { + return m_cfg.name; +} + +FW::ProcessCode FW::Obj::ObjTrackingGeometryWriter::write( + const AlgorithmContext& context, const Acts::TrackingGeometry& tGeometry) { + ACTS_DEBUG(">>Obj: Writer for TrackingGeometry object called."); + // get the world volume + auto world = tGeometry.highestTrackingVolume(); + if (world) + write(context, *world); + // return the success code + return FW::ProcessCode::SUCCESS; +} + +/// process this volume +void FW::Obj::ObjTrackingGeometryWriter::write( + const AlgorithmContext& context, const Acts::TrackingVolume& tVolume) { + ACTS_DEBUG(">>Obj: Writer for TrackingVolume object called."); + // get the confined layers and process them + if (tVolume.confinedLayers()) { + ACTS_VERBOSE(">>Obj: Layers are present, process them."); + // loop over the layers + for (auto layer : tVolume.confinedLayers()->arrayObjects()) { + // we jump navigation layers + if (layer->layerType() == Acts::navigation) + continue; + // get the volume name + const std::string& volumeName = tVolume.volumeName(); + // find the right surfacewriter + std::shared_ptr<ObjSurfaceWriter> surfaceWriter = nullptr; + for (auto writer : m_cfg.surfaceWriters) { + // get name and writer + auto writerName = writer->name(); + // and break + ACTS_VERBOSE(">>Obj: The writer name is: " << writerName); + ACTS_VERBOSE(">>Obj: The volume name is: " << volumeName); + if (volumeName.find(writerName) != std::string::npos) { + // asign the writer + surfaceWriter = writer; + // break the loop + break; + } + } + // bail out if you have no surface writer + if (!surfaceWriter) + return; + // layer prefix + surfaceWriter->write(m_cfg.layerPrefix); + // try to write the material surface as well + if (layer->surfaceRepresentation().surfaceMaterial()) { + surfaceWriter->write(context, layer->surfaceRepresentation()); + } + // the the approaching surfaces and check if they have material + if (layer->approachDescriptor()) { + // loop over the contained Surfaces + for (auto& cSurface : layer->approachDescriptor()->containedSurfaces()) + if (cSurface->surfaceMaterial()) { + surfaceWriter->write(context, *cSurface); + } + } + // check for sensitive surfaces + if (layer->surfaceArray() && surfaceWriter) { + ACTS_VERBOSE(">>Obj: There are " + << layer->surfaceArray()->surfaces().size() + << " surfaces."); + // surfaces + // surfaceWriter->write(m_cfg.sensitiveGroupPrefix); + // loop over the surface + for (auto& surface : layer->surfaceArray()->surfaces()) { + if (surface && (surfaceWriter->write(context, *surface)) == + FW::ProcessCode::ABORT) + return; + } + } + } + } + // Recursive self call + // get the confined volumes and step down the hierarchy + if (tVolume.confinedVolumes()) { + // loop over the volumes and write what they have + for (auto volume : tVolume.confinedVolumes()->arrayObjects()) { + write(context, *volume.get()); + } + } +} diff --git a/Io/Performance/ACTFW/Io/Performance/CKFPerformanceWriter.cpp b/Io/Performance/ACTFW/Io/Performance/CKFPerformanceWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec4169f6a3b4ba23a8ace247827e31d824ebcc1a --- /dev/null +++ b/Io/Performance/ACTFW/Io/Performance/CKFPerformanceWriter.cpp @@ -0,0 +1,210 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2020 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 <numeric> +#include <stdexcept> + +#include <TFile.h> +#include <TTree.h> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Io/Performance/CKFPerformanceWriter.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "Acts/EventData/MultiTrajectoryHelpers.hpp" +#include "Acts/EventData/TrackParameters.hpp" + +FW::CKFPerformanceWriter::CKFPerformanceWriter( + FW::CKFPerformanceWriter::Config cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputTrajectories, "CKFPerformanceWriter", lvl), + m_cfg(std::move(cfg)), + m_effPlotTool(m_cfg.effPlotToolConfig, lvl), + m_fakeRatePlotTool(m_cfg.fakeRatePlotToolConfig, lvl), + m_duplicationPlotTool(m_cfg.duplicationPlotToolConfig, lvl), + m_trackSummaryPlotTool(m_cfg.trackSummaryPlotToolConfig, lvl) { + // Input track and truth collection name + if (m_cfg.inputTrajectories.empty()) { + throw std::invalid_argument("Missing input trajectories collection"); + } + if (m_cfg.inputParticles.empty()) { + throw std::invalid_argument("Missing input particles collection"); + } + if (m_cfg.outputFilename.empty()) { + throw std::invalid_argument("Missing output filename"); + } + + // the output file can not be given externally since TFile accesses to the + // same file from multiple threads are unsafe. + // must always be opened internally + auto path = joinPaths(m_cfg.outputDir, m_cfg.outputFilename); + m_outputFile = TFile::Open(path.c_str(), "RECREATE"); + if (not m_outputFile) { + throw std::invalid_argument("Could not open '" + path + "'"); + } + + // initialize the plot tools + m_effPlotTool.book(m_effPlotCache); + m_fakeRatePlotTool.book(m_fakeRatePlotCache); + m_duplicationPlotTool.book(m_duplicationPlotCache); + m_trackSummaryPlotTool.book(m_trackSummaryPlotCache); +} + +FW::CKFPerformanceWriter::~CKFPerformanceWriter() { + m_effPlotTool.clear(m_effPlotCache); + m_fakeRatePlotTool.clear(m_fakeRatePlotCache); + m_duplicationPlotTool.clear(m_duplicationPlotCache); + m_trackSummaryPlotTool.clear(m_trackSummaryPlotCache); + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::CKFPerformanceWriter::endRun() { + if (m_outputFile) { + m_outputFile->cd(); + m_effPlotTool.write(m_effPlotCache); + m_fakeRatePlotTool.write(m_fakeRatePlotCache); + m_duplicationPlotTool.write(m_duplicationPlotCache); + m_trackSummaryPlotTool.write(m_trackSummaryPlotCache); + ACTS_INFO("Wrote performance plots to '" << m_outputFile->GetPath() << "'"); + } + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::CKFPerformanceWriter::writeT( + const AlgorithmContext& ctx, const TrajectoryContainer& trajectories) { + // The number of majority particle hits and fitted track parameters + using RecoTrackInfo = std::pair<size_t, Acts::BoundParameters>; + + // Read truth particles from input collection + const auto& particles = + ctx.eventStore.get<SimParticleContainer>(m_cfg.inputParticles); + + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + // Counter of truth-matched reco tracks + std::map<ActsFatras::Barcode, std::vector<RecoTrackInfo>> matched; + // Counter of truth-unmatched reco tracks + std::map<ActsFatras::Barcode, size_t> unmatched; + + // Loop over all trajectories + for (const auto& traj : trajectories) { + // The trajectory entry indices and the multiTrajectory + const auto& [trackTips, mj] = traj.trajectory(); + if (trackTips.empty()) { + ACTS_WARNING("Empty multiTrajectory."); + continue; + } + + // Loop over all trajectories in a multiTrajectory + for (const size_t& trackTip : trackTips) { + // Collect the trajectory summary info + auto trajState = + Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip); + // Reco track selection + //@TODO: add interface for applying others cuts on reco tracks: + // -> pT, d0, z0, detector-specific hits/holes number cut + if (trajState.nMeasurements < m_cfg.nMeasurementsMin) { + continue; + } + // Check if the reco track has fitted track parameters + if (not traj.hasTrackParameters(trackTip)) { + ACTS_WARNING( + "No fitted track parameters for trajectory with entry index = " + << trackTip); + continue; + } + const auto& fittedParameters = traj.trackParameters(trackTip); + // Fill the trajectory summary info + m_trackSummaryPlotTool.fill(m_trackSummaryPlotCache, fittedParameters, + trajState.nStates, trajState.nMeasurements, + trajState.nOutliers, trajState.nHoles); + + // Get the majority truth particle to this track + std::vector<ParticleHitCount> particleHitCount = + traj.identifyMajorityParticle(trackTip); + if (particleHitCount.empty()) { + ACTS_WARNING( + "No truth particle associated with this trajectory with entry " + "index = " + << trackTip); + continue; + } + // Get the majority particleId and majority particle counts + // Note that the majority particle might be not in the truth seeds + // collection + ActsFatras::Barcode majorityParticleId = + particleHitCount.front().particleId; + size_t nMajorityHits = particleHitCount.front().hitCount; + + // Check if the trajectory is matched with truth. + // If not, it will be classified as 'fake' + bool isFake = false; + if (nMajorityHits * 1. / trajState.nMeasurements >= + m_cfg.truthMatchProbMin) { + matched[majorityParticleId].push_back( + {nMajorityHits, fittedParameters}); + } else { + isFake = true; + unmatched[majorityParticleId]++; + } + // Fill fake rate plots + m_fakeRatePlotTool.fill(m_fakeRatePlotCache, fittedParameters, isFake); + } // end all trajectories in a multiTrajectory + } // end all multiTrajectories + + // Loop over all truth-matched reco tracks for duplication rate plots + for (auto& [particleId, matchedTracks] : matched) { + // Sort the reco tracks matched to this particle by the number of majority + // hits + std::sort(matchedTracks.begin(), matchedTracks.end(), + [](const RecoTrackInfo& lhs, const RecoTrackInfo& rhs) { + return lhs.first > rhs.first; + }); + for (size_t itrack = 0; itrack < matchedTracks.size(); itrack++) { + const auto& [nMajorityHits, fittedParameters] = matchedTracks.at(itrack); + // The tracks with maximum number of majority hits is taken as the 'real' + // track; others are as 'duplicated' + bool isDuplicated = (itrack != 0); + // Fill the duplication rate + m_duplicationPlotTool.fill(m_duplicationPlotCache, fittedParameters, + isDuplicated); + } + } + + // Loop over all truth particle seeds for efficiency plots and reco details. + // These are filled w.r.t. truth particle seed info + for (const auto& particle : particles) { + auto particleId = particle.particleId(); + // Investigate the truth-matched tracks + size_t nMatchedTracks = 0; + bool isReconstructed = false; + auto imatched = matched.find(particleId); + if (imatched != matched.end()) { + nMatchedTracks = imatched->second.size(); + isReconstructed = true; + } + // Fill efficiency plots + m_effPlotTool.fill(m_effPlotCache, particle, isReconstructed); + // Fill number of duplicated tracks for this particle + m_duplicationPlotTool.fill(m_duplicationPlotCache, particle, + nMatchedTracks - 1); + + // Investigate the fake (i.e. truth-unmatched) tracks + size_t nFakeTracks = 0; + auto ifake = unmatched.find(particleId); + if (ifake != unmatched.end()) { + nFakeTracks = ifake->second; + } + // Fill number of reconstructed/truth-matched/fake tracks for this particle + m_fakeRatePlotTool.fill(m_fakeRatePlotCache, particle, nMatchedTracks, + nFakeTracks); + } // end all truth particles + + return ProcessCode::SUCCESS; +} diff --git a/Io/Performance/ACTFW/Io/Performance/CKFPerformanceWriter.hpp b/Io/Performance/ACTFW/Io/Performance/CKFPerformanceWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a0ddc45dae88d2b6ade51b946a0ea4d4d2a4e295 --- /dev/null +++ b/Io/Performance/ACTFW/Io/Performance/CKFPerformanceWriter.hpp @@ -0,0 +1,89 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2020 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 <mutex> + +#include "ACTFW/EventData/Track.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "ACTFW/Validation/DuplicationPlotTool.hpp" +#include "ACTFW/Validation/EffPlotTool.hpp" +#include "ACTFW/Validation/FakeRatePlotTool.hpp" +#include "ACTFW/Validation/TrackSummaryPlotTool.hpp" +#include "Acts/Utilities/Units.hpp" + +class TFile; +class TTree; + +using namespace Acts::UnitLiterals; + +namespace FW { + +/// Write out the performance of CombinatorialKalmanFilter (CKF), e.g. +/// track efficiency, fake rate etc. +/// @TODO: add duplication plots +/// +/// A common file can be provided for to the writer to attach his TTree, +/// this is done by setting the Config::rootFile pointer to an existing file +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +class CKFPerformanceWriter final : public WriterT<TrajectoryContainer> { + public: + struct Config { + /// Input truth particles collection. + std::string inputParticles; + /// Input (found) trajectories collection. + std::string inputTrajectories; + /// Output directory. + std::string outputDir; + /// Output filename. + std::string outputFilename = "performance_ckf.root"; + /// Plot tool configurations. + EffPlotTool::Config effPlotToolConfig; + FakeRatePlotTool::Config fakeRatePlotToolConfig; + DuplicationPlotTool::Config duplicationPlotToolConfig; + TrackSummaryPlotTool::Config trackSummaryPlotToolConfig; + /// Min reco-truth matching probability + double truthMatchProbMin = 0.5; + /// Min number of measurements + size_t nMeasurementsMin = 9; + /// Min transverse momentum + double ptMin = 1_GeV; + }; + + /// Construct from configuration and log level. + CKFPerformanceWriter(Config cfg, Acts::Logging::Level lvl); + ~CKFPerformanceWriter() override; + + /// Finalize plots. + ProcessCode endRun() final override; + + private: + ProcessCode writeT(const AlgorithmContext& ctx, + const TrajectoryContainer& trajectories) final override; + + Config m_cfg; + /// Mutex used to protect multi-threaded writes. + std::mutex m_writeMutex; + TFile* m_outputFile{nullptr}; + /// Plot tool for efficiency + EffPlotTool m_effPlotTool; + EffPlotTool::EffPlotCache m_effPlotCache; + /// Plot tool for fake rate + FakeRatePlotTool m_fakeRatePlotTool; + FakeRatePlotTool::FakeRatePlotCache m_fakeRatePlotCache{}; + /// Plot tool for duplication rate + DuplicationPlotTool m_duplicationPlotTool; + DuplicationPlotTool::DuplicationPlotCache m_duplicationPlotCache{}; + /// Plot tool for track hit info + TrackSummaryPlotTool m_trackSummaryPlotTool; + TrackSummaryPlotTool::TrackSummaryPlotCache m_trackSummaryPlotCache; +}; + +} // namespace FW diff --git a/Io/Performance/ACTFW/Io/Performance/TrackFinderPerformanceWriter.cpp b/Io/Performance/ACTFW/Io/Performance/TrackFinderPerformanceWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30d4c5ef5e62d56120804ceb5b90c53d1e26f0b8 --- /dev/null +++ b/Io/Performance/ACTFW/Io/Performance/TrackFinderPerformanceWriter.cpp @@ -0,0 +1,259 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/Performance/TrackFinderPerformanceWriter.hpp" + +#include <TFile.h> +#include <TTree.h> +#include <algorithm> +#include <cstdint> +#include <mutex> +#include <unordered_map> +#include <vector> + +#include "ACTFW/EventData/IndexContainers.hpp" +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "ACTFW/Utilities/Range.hpp" +#include "ACTFW/Validation/ProtoTrackClassification.hpp" +#include "Acts/Utilities/Helpers.hpp" +#include "Acts/Utilities/Units.hpp" +#include "ActsFatras/EventData/Barcode.hpp" + +namespace { +using SimParticleContainer = FW::SimParticleContainer; +using HitParticlesMap = FW::IndexMultimap<ActsFatras::Barcode>; +using ProtoTrackContainer = FW::ProtoTrackContainer; +} // namespace + +struct FW::TrackFinderPerformanceWriter::Impl { + Config cfg; + TFile* file = nullptr; + + // per-track tree + TTree* trkTree = nullptr; + std::mutex trkMutex; + // track identification + ULong64_t trkEventId; + ULong64_t trkTrackId; + // track content + // number of hits on track + UShort_t trkNumHits; + // number of particles contained in the track + UShort_t trkNumParticles; + // track particle content; for each contributing particle, largest first + std::vector<ULong64_t> trkParticleId; + // total number of hits generated by this particle + std::vector<UShort_t> trkParticleNumHitsTotal; + // number of hits within this track + std::vector<UShort_t> trkParticleNumHitsOnTrack; + + // per-particle tree + TTree* prtTree = nullptr; + std::mutex prtMutex; + // particle identification + ULong64_t prtEventId; + ULong64_t prtParticleId; + Int_t prtParticleType; + // particle kinematics + // vertex position in mm + float prtVx, prtVy, prtVz; + // vertex time in ns + float prtVt; + // particle momentum at production in GeV + float prtPx, prtPy, prtPz; + // particle mass in GeV + float prtM; + // particle charge in e + float prtQ; + // particle reconstruction + UShort_t prtNumHits; // number of hits for this particle + UShort_t prtNumTracks; // number of tracks this particle was reconstructed in + UShort_t prtNumTracksMajority; // number of tracks reconstructed as majority + // extra logger reference for the logging macros + const Acts::Logger& _logger; + + Impl(Config&& c, const Acts::Logger& l) : cfg(std::move(c)), _logger(l) { + if (cfg.inputParticles.empty()) { + throw std::invalid_argument("Missing particles input collection"); + } + if (cfg.inputHitParticlesMap.empty()) { + throw std::invalid_argument("Missing hit-particles map input collection"); + } + if (cfg.inputProtoTracks.empty()) { + throw std::invalid_argument("Missing proto tracks input collection"); + } + if (cfg.outputFilename.empty()) { + throw std::invalid_argument("Missing output filename"); + } + + // the output file can not be given externally since TFile accesses to the + // same file from multiple threads are unsafe. + // must always be opened internally + auto path = joinPaths(cfg.outputDir, cfg.outputFilename); + file = TFile::Open(path.c_str(), "RECREATE"); + if (not file) { + throw std::invalid_argument("Could not open '" + path + "'"); + } + + // construct trees + trkTree = new TTree("track_finder_tracks", ""); + trkTree->SetDirectory(file); + trkTree->Branch("event_id", &trkEventId); + trkTree->Branch("track_id", &trkTrackId); + trkTree->Branch("size", &trkNumHits); + trkTree->Branch("nparticles", &trkNumParticles); + trkTree->Branch("particle_id", &trkParticleId); + trkTree->Branch("particle_nhits_total", &trkParticleNumHitsTotal); + trkTree->Branch("particle_nhits_on_track", &trkParticleNumHitsOnTrack); + prtTree = new TTree("track_finder_particles", ""); + prtTree->SetDirectory(file); + prtTree->Branch("event_id", &prtEventId); + prtTree->Branch("particle_id", &prtParticleId); + prtTree->Branch("particle_type", &prtParticleType); + prtTree->Branch("vx", &prtVx); + prtTree->Branch("vy", &prtVy); + prtTree->Branch("vz", &prtVz); + prtTree->Branch("vt", &prtVt); + prtTree->Branch("px", &prtPx); + prtTree->Branch("py", &prtPy); + prtTree->Branch("pz", &prtPz); + prtTree->Branch("m", &prtM); + prtTree->Branch("q", &prtQ); + prtTree->Branch("nhits", &prtNumHits); + prtTree->Branch("ntracks", &prtNumTracks); + prtTree->Branch("ntracks_majority", &prtNumTracksMajority); + } + + const Acts::Logger& logger() const { return _logger; } + + void write(uint64_t eventId, const SimParticleContainer& particles, + const HitParticlesMap& hitParticlesMap, + const ProtoTrackContainer& tracks) { + // compute the inverse mapping on-the-fly + const auto& particleHitsMap = invertIndexMultimap(hitParticlesMap); + // How often a particle was reconstructed. + std::unordered_map<ActsFatras::Barcode, std::size_t> reconCount; + reconCount.reserve(particles.size()); + // How often a particle was reconstructed as the majority particle. + std::unordered_map<ActsFatras::Barcode, std::size_t> majorityCount; + majorityCount.reserve(particles.size()); + // For each particle within a track, how many hits did it contribute + std::vector<ParticleHitCount> particleHitCounts; + + // write per-track performance measures + { + std::lock_guard<std::mutex> guardTrk(trkMutex); + for (size_t itrack = 0; itrack < tracks.size(); ++itrack) { + const auto& track = tracks[itrack]; + + identifyContributingParticles(hitParticlesMap, track, + particleHitCounts); + // extract per-particle reconstruction counts + // empty track hits counts could originate from a buggy track finder + // that results in empty tracks or from purely noise track where no hits + // is from a particle. + if (not particleHitCounts.empty()) { + auto it = majorityCount + .try_emplace(particleHitCounts.front().particleId, 0u) + .first; + it->second += 1; + } + for (const auto& hc : particleHitCounts) { + auto it = reconCount.try_emplace(hc.particleId, 0u).first; + it->second += 1; + } + + trkEventId = eventId; + trkTrackId = itrack; + trkNumHits = track.size(); + trkNumParticles = particleHitCounts.size(); + trkParticleId.clear(); + trkParticleNumHitsTotal.clear(); + trkParticleNumHitsOnTrack.clear(); + for (const auto& phc : particleHitCounts) { + trkParticleId.push_back(phc.particleId.value()); + // count total number of hits for this particle + auto trueParticleHits = + makeRange(particleHitsMap.equal_range(phc.particleId.value())); + trkParticleNumHitsTotal.push_back(trueParticleHits.size()); + trkParticleNumHitsOnTrack.push_back(phc.hitCount); + } + + trkTree->Fill(); + } + } + + // write per-particle performance measures + { + std::lock_guard<std::mutex> guardPrt(trkMutex); + for (const auto& particle : particles) { + // find all hits for this particle + auto hits = + makeRange(particleHitsMap.equal_range(particle.particleId())); + + // identification + prtEventId = eventId; + prtParticleId = particle.particleId().value(); + prtParticleType = particle.pdg(); + // kinematics + prtVx = particle.position().x() / Acts::UnitConstants::mm; + prtVy = particle.position().y() / Acts::UnitConstants::mm; + prtVz = particle.position().z() / Acts::UnitConstants::mm; + prtVt = particle.time() / Acts::UnitConstants::ns; + const auto p = particle.absMomentum() / Acts::UnitConstants::GeV; + prtPx = p * particle.unitDirection().x(); + prtPy = p * particle.unitDirection().y(); + prtPz = p * particle.unitDirection().z(); + prtM = particle.mass() / Acts::UnitConstants::GeV; + prtQ = particle.charge() / Acts::UnitConstants::e; + // reconstruction + prtNumHits = hits.size(); + auto nt = reconCount.find(particle.particleId()); + prtNumTracks = (nt != reconCount.end()) ? nt->second : 0u; + auto nm = majorityCount.find(particle.particleId()); + prtNumTracksMajority = (nm != majorityCount.end()) ? nm->second : 0u; + + prtTree->Fill(); + } + } + } + /// Write everything to disk and close the file. + void close() { + if (not file) { + ACTS_ERROR("Output file is not available"); + return; + } + file->Write(); + file->Close(); + } +}; + +FW::TrackFinderPerformanceWriter::TrackFinderPerformanceWriter( + FW::TrackFinderPerformanceWriter::Config cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputProtoTracks, "TrackFinderPerformanceWriter", lvl), + m_impl(std::make_unique<Impl>(std::move(cfg), logger())) {} + +FW::TrackFinderPerformanceWriter::~TrackFinderPerformanceWriter() { + // explicit destructor needed for pimpl idiom to work +} + +FW::ProcessCode FW::TrackFinderPerformanceWriter::writeT( + const FW::AlgorithmContext& ctx, const FW::ProtoTrackContainer& tracks) { + const auto& particles = + ctx.eventStore.get<SimParticleContainer>(m_impl->cfg.inputParticles); + const auto& hitParticlesMap = + ctx.eventStore.get<HitParticlesMap>(m_impl->cfg.inputHitParticlesMap); + m_impl->write(ctx.eventNumber, particles, hitParticlesMap, tracks); + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::TrackFinderPerformanceWriter::endRun() { + m_impl->close(); + return ProcessCode::SUCCESS; +} diff --git a/Io/Performance/ACTFW/Io/Performance/TrackFinderPerformanceWriter.hpp b/Io/Performance/ACTFW/Io/Performance/TrackFinderPerformanceWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5834cd8bede47125e49f96b3bc801a0f67eafa28 --- /dev/null +++ b/Io/Performance/ACTFW/Io/Performance/TrackFinderPerformanceWriter.hpp @@ -0,0 +1,51 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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 <memory> +#include <string> + +#include "ACTFW/EventData/ProtoTrack.hpp" +#include "ACTFW/Framework/WriterT.hpp" + +namespace FW { + +/// Write track finder performance measures. +/// +/// Only considers the track finding itself, i.e. grouping of hits into tracks, +/// and computes relevant per-track and per-particles statistics. +class TrackFinderPerformanceWriter final : public WriterT<ProtoTrackContainer> { + public: + struct Config { + /// True set of input particles. + std::string inputParticles; + /// True hit-particles mapping. + std::string inputHitParticlesMap; + /// Reconstructed input proto tracks. + std::string inputProtoTracks; + /// Output directory. + std::string outputDir; + /// Output filename + std::string outputFilename = "performance_track_finder.root"; + }; + + TrackFinderPerformanceWriter(Config cfg, Acts::Logging::Level lvl); + ~TrackFinderPerformanceWriter(); + + ProcessCode endRun() final override; + + private: + ProcessCode writeT(const AlgorithmContext& ctx, + const ProtoTrackContainer& tracks) final override; + + struct Impl; + std::unique_ptr<Impl> m_impl; +}; + +} // namespace FW diff --git a/Io/Performance/ACTFW/Io/Performance/TrackFitterPerformanceWriter.cpp b/Io/Performance/ACTFW/Io/Performance/TrackFitterPerformanceWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..22aeb221d38c34bee8621e7b9e42c15004efd6e1 --- /dev/null +++ b/Io/Performance/ACTFW/Io/Performance/TrackFitterPerformanceWriter.cpp @@ -0,0 +1,162 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/Performance/TrackFitterPerformanceWriter.hpp" + +#include <TFile.h> +#include <TTree.h> +#include <stdexcept> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "Acts/EventData/MultiTrajectoryHelpers.hpp" +#include "Acts/Utilities/Helpers.hpp" + +using Acts::VectorHelpers::eta; + +FW::TrackFitterPerformanceWriter::TrackFitterPerformanceWriter( + FW::TrackFitterPerformanceWriter::Config cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputTrajectories, "TrackFitterPerformanceWriter", lvl), + m_cfg(std::move(cfg)), + m_resPlotTool(m_cfg.resPlotToolConfig, lvl), + m_effPlotTool(m_cfg.effPlotToolConfig, lvl), + m_trackSummaryPlotTool(m_cfg.trackSummaryPlotToolConfig, lvl) + +{ + // Input track and truth collection name + if (m_cfg.inputTrajectories.empty()) { + throw std::invalid_argument("Missing input trajectories collection"); + } + if (m_cfg.inputParticles.empty()) { + throw std::invalid_argument("Missing input particles collection"); + } + if (m_cfg.outputFilename.empty()) { + throw std::invalid_argument("Missing output filename"); + } + + // the output file can not be given externally since TFile accesses to the + // same file from multiple threads are unsafe. + // must always be opened internally + auto path = joinPaths(m_cfg.outputDir, m_cfg.outputFilename); + m_outputFile = TFile::Open(path.c_str(), "RECREATE"); + if (not m_outputFile) { + throw std::invalid_argument("Could not open '" + path + "'"); + } + + // initialize the residual and efficiency plots tool + m_resPlotTool.book(m_resPlotCache); + m_effPlotTool.book(m_effPlotCache); + m_trackSummaryPlotTool.book(m_trackSummaryPlotCache); +} + +FW::TrackFitterPerformanceWriter::~TrackFitterPerformanceWriter() { + m_resPlotTool.clear(m_resPlotCache); + m_effPlotTool.clear(m_effPlotCache); + m_trackSummaryPlotTool.clear(m_trackSummaryPlotCache); + + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::TrackFitterPerformanceWriter::endRun() { + // fill residual and pull details into additional hists + m_resPlotTool.refinement(m_resPlotCache); + + if (m_outputFile) { + m_outputFile->cd(); + m_resPlotTool.write(m_resPlotCache); + m_effPlotTool.write(m_effPlotCache); + m_trackSummaryPlotTool.write(m_trackSummaryPlotCache); + + ACTS_INFO("Wrote performance plots to '" << m_outputFile->GetPath() << "'"); + } + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::TrackFitterPerformanceWriter::writeT( + const AlgorithmContext& ctx, const TrajectoryContainer& trajectories) { + // Read truth particles from input collection + const auto& particles = + ctx.eventStore.get<SimParticleContainer>(m_cfg.inputParticles); + + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + // Truth particles with corresponding reconstructed tracks + std::vector<ActsFatras::Barcode> reconParticleIds; + reconParticleIds.reserve(particles.size()); + + // Loop over all trajectories + for (const auto& traj : trajectories) { + // The trajectory entry indices and the multiTrajectory + const auto& [trackTips, mj] = traj.trajectory(); + if (trackTips.empty()) { + ACTS_WARNING("Empty multiTrajectory."); + continue; + } + + // Check the size of the trajectory entry indices. For track fitting, there + // should be at most one trajectory + if (trackTips.size() > 1) { + ACTS_ERROR("Track fitting should not result in multiple trajectories."); + return ProcessCode::ABORT; + } + // Get the entry index for the single trajectory + auto& trackTip = trackTips.front(); + + // Select reco track with fitted parameters + if (not traj.hasTrackParameters(trackTip)) { + ACTS_WARNING("No fitted track parameters."); + continue; + } + const auto& fittedParameters = traj.trackParameters(trackTip); + + // Get the majority truth particle for this trajectory + const auto particleHitCount = traj.identifyMajorityParticle(trackTip); + if (particleHitCount.empty()) { + ACTS_WARNING("No truth particle associated with this trajectory."); + continue; + } + // Find the truth particle for the majority barcode + const auto ip = particles.find(particleHitCount.front().particleId); + if (ip == particles.end()) { + ACTS_WARNING("Majority particle not found in the particles collection."); + continue; + } + + // Record this majority particle ID of this trajectory + reconParticleIds.push_back(ip->particleId()); + // Fill the residual plots + m_resPlotTool.fill(m_resPlotCache, ctx.geoContext, *ip, + traj.trackParameters(trackTip)); + // Collect the trajectory summary info + auto trajState = + Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip); + // Fill the trajectory summary info + m_trackSummaryPlotTool.fill(m_trackSummaryPlotCache, fittedParameters, + trajState.nStates, trajState.nMeasurements, + trajState.nOutliers, trajState.nHoles); + } + + // Fill the efficiency, defined as the ratio between number of tracks with + // fitted parameter and total truth tracks (assumes one truth partilce has + // one truth track) + for (const auto& particle : particles) { + bool isReconstructed = false; + // Find if the particle has been reconstructed + auto it = std::find(reconParticleIds.begin(), reconParticleIds.end(), + particle.particleId()); + if (it != reconParticleIds.end()) { + isReconstructed = true; + } + m_effPlotTool.fill(m_effPlotCache, particle, isReconstructed); + } + + return ProcessCode::SUCCESS; +} diff --git a/Io/Performance/ACTFW/Io/Performance/TrackFitterPerformanceWriter.hpp b/Io/Performance/ACTFW/Io/Performance/TrackFitterPerformanceWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fec88713d61f17b33cd3ef169110ffe6319ca569 --- /dev/null +++ b/Io/Performance/ACTFW/Io/Performance/TrackFitterPerformanceWriter.hpp @@ -0,0 +1,75 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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 <mutex> + +#include "ACTFW/EventData/Track.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "ACTFW/Validation/EffPlotTool.hpp" +#include "ACTFW/Validation/ResPlotTool.hpp" +#include "ACTFW/Validation/TrackSummaryPlotTool.hpp" + +class TFile; +class TTree; + +namespace FW { + +/// Write out the residual and pull of track parameters and efficiency. +/// +/// Efficiency here is the fraction of smoothed tracks compared to all tracks. +/// +/// A common file can be provided for to the writer to attach his TTree, +/// this is done by setting the Config::rootFile pointer to an existing file +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +class TrackFitterPerformanceWriter final : public WriterT<TrajectoryContainer> { + public: + struct Config { + /// Input truth particles collection. + std::string inputParticles; + /// Input (fitted) trajectories collection. + std::string inputTrajectories; + /// Output directory. + std::string outputDir; + /// Output filename. + std::string outputFilename = "performance_track_fitter.root"; + /// Plot tool configurations. + ResPlotTool::Config resPlotToolConfig; + EffPlotTool::Config effPlotToolConfig; + TrackSummaryPlotTool::Config trackSummaryPlotToolConfig; + }; + + /// Construct from configuration and log level. + TrackFitterPerformanceWriter(Config cfg, Acts::Logging::Level lvl); + ~TrackFitterPerformanceWriter() override; + + /// Finalize plots. + ProcessCode endRun() final override; + + private: + ProcessCode writeT(const AlgorithmContext& ctx, + const TrajectoryContainer& trajectories) final override; + + Config m_cfg; + /// Mutex used to protect multi-threaded writes. + std::mutex m_writeMutex; + TFile* m_outputFile{nullptr}; + /// Plot tool for residuals and pulls. + ResPlotTool m_resPlotTool; + ResPlotTool::ResPlotCache m_resPlotCache; + /// Plot tool for efficiency + EffPlotTool m_effPlotTool; + EffPlotTool::EffPlotCache m_effPlotCache; + /// Plot tool for track hit info + TrackSummaryPlotTool m_trackSummaryPlotTool; + TrackSummaryPlotTool::TrackSummaryPlotCache m_trackSummaryPlotCache; +}; + +} // namespace FW diff --git a/Io/Performance/CMakeLists.txt b/Io/Performance/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6ee0f202917fe5c45447ec17eab3caed308cb687 --- /dev/null +++ b/Io/Performance/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library( + ActsExamplesIoPerformance SHARED + ACTFW/Io/Performance/TrackFinderPerformanceWriter.cpp + ACTFW/Io/Performance/TrackFitterPerformanceWriter.cpp + ACTFW/Io/Performance/CKFPerformanceWriter.cpp) +target_include_directories( + ActsExamplesIoPerformance + PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>) +target_link_libraries( + ActsExamplesIoPerformance + PUBLIC ActsExamplesFramework + PRIVATE ActsCore ROOT::Core ROOT::Tree) + +install( + TARGETS ActsExamplesIoPerformance + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Io/Root/CMakeLists.txt b/Io/Root/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..771909b8b018a9dec6982353fdb86e98bdf2b5e1 --- /dev/null +++ b/Io/Root/CMakeLists.txt @@ -0,0 +1,28 @@ +add_library( + ActsExamplesIoRoot SHARED + src/RootMaterialDecorator.cpp + src/RootMaterialWriter.cpp + src/RootMaterialTrackReader.cpp + src/RootMaterialTrackWriter.cpp + src/RootPlanarClusterWriter.cpp + src/RootParticleWriter.cpp + src/RootPropagationStepsWriter.cpp + src/RootSimHitWriter.cpp + src/RootTrackParameterWriter.cpp + src/RootVertexAndTracksWriter.cpp + src/RootVertexAndTracksReader.cpp + src/RootTrajectoryWriter.cpp) +target_include_directories( + ActsExamplesIoRoot + PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) +target_link_libraries( + ActsExamplesIoRoot + PUBLIC + ActsCore ActsDigitizationPlugin ActsIdentificationPlugin + ActsExamplesFramework ActsExamplesPropagation ActsExamplesTruthTracking + Threads::Threads + PRIVATE ROOT::Core ROOT::Hist ROOT::Tree) + +install( + TARGETS ActsExamplesIoRoot + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/Io/Root/include/ACTFW/Io/Root/RootBFieldWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootBFieldWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8abdd9704782a4a7cfaf492145cf6248941e9d70 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootBFieldWriter.hpp @@ -0,0 +1,318 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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 <Acts/MagneticField/ConstantBField.hpp> +#include <Acts/MagneticField/InterpolatedBFieldMap.hpp> +#include <Acts/Utilities/Helpers.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <Acts/Utilities/Units.hpp> +#include <TFile.h> +#include <TTree.h> +#include <array> +#include <boost/optional.hpp> +#include <ios> +#include <mutex> +#include <sstream> +#include <stdexcept> + +#include "ACTFW/Framework/IService.hpp" +#include "ACTFW/Framework/ProcessCode.hpp" +#include "ACTFW/Plugins/BField/ScalableBField.hpp" + +namespace FW { + +/// @class RootBFieldWriter +/// +/// Writes out the Acts::InterpolatedbFieldMap. Currently implemented for 'rz' +/// and 'xyz' field maps. +template <typename bfield_t> +class RootBFieldWriter { + public: + /// Describes the axes definition of the grid of the magnetic field map. + enum class GridType { rz = 0, xyz = 1 }; + + struct Config { + /// The name of the output tree + std::string treeName = "TTree"; + /// The name of the output file + std::string fileName = "TFile.root"; + /// the file access mode (recreate by default) + std::string fileMode = "recreate"; + /// The magnetic field to be written out + std::shared_ptr<const bfield_t> bField = nullptr; + /// How the magnetic field map should be written out + GridType gridType = GridType::xyz; + /// [optional] Setting the range to be printed out in either r (for + /// cylinder coordinates) or x/y (in cartesian coordinates) + /// @note setting this parameter is optional, in case no boundaries are + /// handed over the full magnetic field map will be printed out + boost::optional<std::array<double, 2>> rBounds; + /// [optional] Setting the range in z to be printed out + /// @note setting this parameter is optional, in case no boundaries are + /// handed over the full magnetic field map will be printed out + boost::optional<std::array<double, 2>> zBounds; + /// Number of bins in r + /// @note setting this parameter is optional, in case no bin numbers are + /// handed over the full magnetic field map will be printed out + size_t rBins = 200; + /// Number of bins in z + // @note setting this parameter is optional, in case no bin numbers are + /// handed over the full magnetic field map will be printed out + size_t zBins = 300; + /// Number of bins in phi + // @note setting this parameter is optional, in case no bin numbers are + /// handed over the full magnetic field map will be printed out + size_t phiBins = 100; + }; + + /// Write down an interpolated magnetic field map + static void run(const Config& cfg, + std::unique_ptr<const Acts::Logger> p_logger = + Acts::getDefaultLogger("RootBFieldWriter", + Acts::Logging::INFO)) { + // Set up (local) logging + // @todo Remove dangerous using declaration once the logger macro + // tolerates it + using namespace Acts; + ACTS_LOCAL_LOGGER(std::move(p_logger)) + + // Check basic configuration + if (cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } else if (cfg.fileName.empty()) { + throw std::invalid_argument("Missing file name"); + } else if (!cfg.bField) { + throw std::invalid_argument("Missing interpolated magnetic field"); + } + + // Setup ROOT I/O + ACTS_INFO("Registering new ROOT output File : " << cfg.fileName); + TFile* outputFile = TFile::Open(cfg.fileName.c_str(), cfg.fileMode.c_str()); + if (!outputFile) { + throw std::ios_base::failure("Could not open '" + cfg.fileName); + } + outputFile->cd(); + TTree* outputTree = new TTree(cfg.treeName.c_str(), cfg.treeName.c_str()); + if (!outputTree) + throw std::bad_alloc(); + + // The position values + double z; + outputTree->Branch("z", &z); + + // The BField values + double Bz; + outputTree->Branch("Bz", &Bz); + + // Get the underlying mapper of the InterpolatedBFieldMap + auto mapper = cfg.bField->getMapper(); + + // Access the minima and maxima of all axes + auto minima = mapper.getMin(); + auto maxima = mapper.getMax(); + auto nBins = mapper.getNBins(); + + if (cfg.gridType == GridType::xyz) { + ACTS_INFO("Map will be written out in cartesian coordinates (x,y,z)."); + + // Write out the interpolated magnetic field map + double stepX = 0., stepY = 0., stepZ = 0.; + double minX = 0., minY = 0., minZ = 0.; + double maxX = 0., maxY = 0., maxZ = 0.; + size_t nBinsX = 0, nBinsY = 0, nBinsZ = 0; + + // The position values in xy + double x; + outputTree->Branch("x", &x); + double y; + outputTree->Branch("y", &y); + // The BField values in xy + double Bx; + outputTree->Branch("Bx", &Bx); + double By; + outputTree->Branch("By", &By); + + // check if range is user defined + if (cfg.rBounds && cfg.zBounds) { + ACTS_INFO("User defined ranges handed over."); + + // print out map in user defined range + minX = cfg.rBounds->at(0); + minY = cfg.rBounds->at(0); + minZ = cfg.zBounds->at(0); + + maxX = cfg.rBounds->at(1); + maxY = cfg.rBounds->at(1); + maxZ = cfg.zBounds->at(1); + + nBinsX = cfg.rBins; + nBinsY = cfg.rBins; + nBinsZ = cfg.zBins; + + } else { + ACTS_INFO( + "No user defined ranges handed over - printing out whole map."); + // print out whole map + // check dimension of Bfieldmap + if (minima.size() == 3 && maxima.size() == 3) { + minX = minima.at(0); + minY = minima.at(1); + minZ = minima.at(2); + + maxX = maxima.at(0); + maxY = maxima.at(1); + maxZ = maxima.at(2); + + nBinsX = nBins.at(0); + nBinsY = nBins.at(1); + nBinsZ = nBins.at(2); + + } else if (minima.size() == 2 && maxima.size() == 2) { + minX = -maxima.at(0); + minY = -maxima.at(0); + minZ = minima.at(1); + + maxX = maxima.at(0); + maxY = maxima.at(0); + maxZ = maxima.at(1); + + nBinsX = nBins.at(0); + nBinsY = nBins.at(0); + nBinsZ = nBins.at(1); + } else { + std::ostringstream errorMsg; + errorMsg + << "BField has wrong dimension. The dimension needs to be " + "either 2 (r,z,Br,Bz) or 3(x,y,z,Bx,By,Bz) in order to be " + "written out by this writer."; + throw std::invalid_argument(errorMsg.str()); + } + } + + stepX = fabs(minX - maxX) / nBinsX; + stepY = fabs(minY - maxY) / nBinsY; + stepZ = fabs(minZ - maxZ) / nBinsZ; + + for (size_t i = 0; i <= nBinsX; i++) { + double raw_x = minX + i * stepX; + for (size_t j = 0; j <= nBinsY; j++) { + double raw_y = minY + j * stepY; + for (size_t k = 0; k <= nBinsZ; k++) { + double raw_z = minZ + k * stepZ; + Acts::Vector3D position(raw_x, raw_y, raw_z); + if (cfg.bField->isInside(position)) { + auto bField = cfg.bField->getField(position); + + x = raw_x / Acts::units::_mm; + y = raw_y / Acts::units::_mm; + z = raw_z / Acts::units::_mm; + Bx = bField.x() / Acts::units::_T; + By = bField.y() / Acts::units::_T; + Bz = bField.z() / Acts::units::_T; + outputTree->Fill(); + } + } // for z + } // for y + } // for x + } else { + ACTS_INFO("Map will be written out in cylinder coordinates (r,z)."); + // The position value in r + double r; + outputTree->Branch("r", &r); + // The BField value in r + double Br; + outputTree->Branch("Br", &Br); + + double minR = 0, maxR = 0; + double minZ = 0, maxZ = 0; + size_t nBinsR = 0, nBinsZ = 0, nBinsPhi = 0; + double stepR = 0, stepZ = 0; + + if (cfg.rBounds && cfg.zBounds) { + ACTS_INFO("User defined ranges handed over."); + + minR = cfg.rBounds->at(0); + minZ = cfg.zBounds->at(0); + + maxR = cfg.rBounds->at(1); + maxZ = cfg.zBounds->at(1); + + nBinsR = cfg.rBins; + nBinsZ = cfg.zBins; + nBinsPhi = cfg.phiBins; + } else { + ACTS_INFO( + "No user defined ranges handed over - printing out whole map."); + + if (minima.size() == 3 && maxima.size() == 3) { + minR = 0.; + minZ = minima.at(2); + + maxR = maxima.at(0); + maxZ = maxima.at(2); + + nBinsR = nBins.at(0); + nBinsZ = nBins.at(2); + nBinsPhi = 100.; + + } else if (minima.size() == 2 || maxima.size() == 2) { + minR = minima.at(0); + minZ = minima.at(1); + + maxR = maxima.at(0); + maxZ = maxima.at(1); + + nBinsR = nBins.at(0); + nBinsZ = nBins.at(1); + nBinsPhi = 100.; + + } else { + std::ostringstream errorMsg; + errorMsg + << "BField has wrong dimension. The dimension needs to be " + "either 2 (r,z,Br,Bz) or 3(x,y,z,Bx,By,Bz) in order to be " + "written out by this writer."; + throw std::invalid_argument(errorMsg.str()); + } + } + double minPhi = -M_PI; + stepR = fabs(minR - maxR) / nBinsR; + stepZ = fabs(minZ - maxZ) / nBinsZ; + double stepPhi = (2 * M_PI) / nBinsPhi; + + for (size_t i = 0; i < nBinsPhi; i++) { + double phi = minPhi + i * stepPhi; + for (size_t k = 0; k < nBinsZ; k++) { + double raw_z = minZ + k * stepZ; + for (size_t j = 0; j < nBinsR; j++) { + double raw_r = minR + j * stepR; + Acts::Vector3D position(raw_r * cos(phi), raw_r * sin(phi), raw_z); + if (cfg.bField->isInside(position)) { + auto bField = cfg.bField->getField(position); + z = raw_z / Acts::units::_mm; + r = raw_r / Acts::units::_mm; + Bz = bField.z() / Acts::units::_T; + Br = VectorHelpers::perp(bField) / Acts::units::_T; + outputTree->Fill(); + } + } + } + } // for + } + + // Tear down ROOT I/O + ACTS_INFO("Closing and Writing ROOT output File : " << cfg.fileName); + outputFile->cd(); + outputTree->Write(); + outputFile->Close(); + } +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootMaterialDecorator.hpp b/Io/Root/include/ACTFW/Io/Root/RootMaterialDecorator.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d60417e9bc074ac1491e26621f8421a05cf312a5 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootMaterialDecorator.hpp @@ -0,0 +1,151 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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 <Acts/Geometry/GeometryID.hpp> +#include <Acts/Geometry/TrackingVolume.hpp> +#include <Acts/Material/IMaterialDecorator.hpp> +#include <Acts/Material/ISurfaceMaterial.hpp> +#include <Acts/Material/IVolumeMaterial.hpp> +#include <Acts/Surfaces/Surface.hpp> +#include <Acts/Utilities/Definitions.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <map> +#include <mutex> + +#include "ACTFW/Framework/ProcessCode.hpp" + +class TFile; + +namespace Acts { +using SurfaceMaterialMap = + std::map<GeometryID, std::shared_ptr<const ISurfaceMaterial>>; +using VolumeMaterialMap = + std::map<GeometryID, std::shared_ptr<const IVolumeMaterial>>; +} // namespace Acts + +namespace FW { + +/// @class RootMaterialDecorator +/// +/// @brief Read the collection of SurfaceMaterial & VolumeMaterial +class RootMaterialDecorator : public Acts::IMaterialDecorator { + public: + /// @class Config + /// Configuration of the Reader + class Config { + public: + /// The name of the output tree + std::string folderNameBase = "Material"; + /// The volume identification string + std::string voltag = "_vol"; + /// The boundary identification string + std::string boutag = "_bou"; + /// The layer identification string + std::string laytag = "_lay"; + /// The approach identification string + std::string apptag = "_app"; + /// The sensitive identification string + std::string sentag = "_sen"; + /// The bin number tag + std::string ntag = "n"; + /// The value tag -> binning values: binZ, binR, binPhi, etc. + std::string vtag = "v"; + /// The option tag -> binning options: open, closed + std::string otag = "o"; + /// The range min tag: min value + std::string mintag = "min"; + /// The range max tag: max value + std::string maxtag = "max"; + /// The thickness tag + std::string ttag = "t"; + /// The x0 tag + std::string x0tag = "x0"; + /// The l0 tag + std::string l0tag = "l0"; + /// The A tag + std::string atag = "A"; + /// The Z tag + std::string ztag = "Z"; + /// The rho tag + std::string rhotag = "rho"; + /// The name of the output file + std::string fileName = "material-maps.root"; + /// The default logger + std::shared_ptr<const Acts::Logger> logger; + // The name of the writer + std::string name = ""; + + /// Constructor + /// + /// @param lname Name of the writer tool + /// @param lvl The output logging level + Config(const std::string& lname = "MaterialReader", + Acts::Logging::Level lvl = Acts::Logging::INFO) + : logger(Acts::getDefaultLogger(lname, lvl)), name(lname) {} + }; + + /// Constructor + /// + /// @param cfg configuration struct for the reader + RootMaterialDecorator(const Config& cfg); + + /// Destructor + ~RootMaterialDecorator(); + + /// Decorate a surface + /// + /// @param surface the non-cost surface that is decorated + void decorate(Acts::Surface& surface) const final { + // Null out the material for this surface + if (m_clearSurfaceMaterial) { + surface.assignSurfaceMaterial(nullptr); + } + // Try to find the surface in the map + auto sMaterial = m_surfaceMaterialMap.find(surface.geoID()); + if (sMaterial != m_surfaceMaterialMap.end()) { + surface.assignSurfaceMaterial(sMaterial->second); + } + } + + /// Decorate a TrackingVolume + /// + /// @param volume the non-cost volume that is decorated + void decorate(Acts::TrackingVolume& volume) const final { + // Null out the material for this volume + if (m_clearSurfaceMaterial) { + volume.assignVolumeMaterial(nullptr); + } + // Try to find the surface in the map + auto vMaterial = m_volumeMaterialMap.find(volume.geoID()); + if (vMaterial != m_volumeMaterialMap.end()) { + volume.assignVolumeMaterial(vMaterial->second); + } + } + + private: + /// The config class + Config m_cfg; + + /// The input file + TFile* m_inputFile{nullptr}; + + /// Surface based material + Acts::SurfaceMaterialMap m_surfaceMaterialMap; + + /// Volume based material + Acts::VolumeMaterialMap m_volumeMaterialMap; + + bool m_clearSurfaceMaterial{true}; + + /// Private access to the logging instance + const Acts::Logger& logger() const { return *m_cfg.logger; } +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootMaterialTrackReader.hpp b/Io/Root/include/ACTFW/Io/Root/RootMaterialTrackReader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..151552852a046c50f55071c53c447d71bf10993b --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootMaterialTrackReader.hpp @@ -0,0 +1,116 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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 <Acts/Propagator/MaterialInteractor.hpp> +#include <Acts/Utilities/Definitions.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <mutex> +#include <vector> + +#include "ACTFW/Framework/IReader.hpp" +#include "ACTFW/Framework/IService.hpp" +#include "ACTFW/Framework/ProcessCode.hpp" + +class TChain; + +namespace FW { + +/// @class RootMaterialTrackReader +/// +/// @brief Reads in MaterialTrack information from a root file +/// and fills it into a format to be understood by the MaterialMapping +/// algorithm +class RootMaterialTrackReader : public IReader { + public: + /// @brief The nested configuration struct + struct Config { + std::string collection = + "material-tracks"; ///< material collection to read + std::string filePath = ""; ///< path of the output file + std::string treeName = "material-tracks"; ///< name of the output tree + std::vector<std::string> fileList; ///< The name of the input file + + unsigned int batchSize = 1; ///!< The number of tracks per event + + /// The default logger + std::shared_ptr<const Acts::Logger> logger; + + /// The name of the service + std::string name; + + /// Constructor + /// @param lname The name of the Material reader + /// @parqam lvl The log level for the logger + Config(const std::string& lname = "MaterialReader", + Acts::Logging::Level lvl = Acts::Logging::INFO) + : logger(Acts::getDefaultLogger(lname, lvl)), name(lname) {} + }; + + /// Constructor + /// @param cfg The Configuration struct + RootMaterialTrackReader(const Config& cfg); + + /// Destructor + ~RootMaterialTrackReader(); + + /// Framework name() method + std::string name() const final override; + + /// Return the available events range. + std::pair<size_t, size_t> availableEvents() const final override; + + /// Read out data from the input stream + /// + /// @param context The algorithm context + ProcessCode read(const FW::AlgorithmContext& context) final override; + + private: + /// Private access to the logging instance + const Acts::Logger& logger() const { return *m_cfg.logger; } + + /// The config class + Config m_cfg; + + /// mutex used to protect multi-threaded reads + std::mutex m_read_mutex; + + /// The number of events + size_t m_events = 0; + + /// The input tree name + TChain* m_inputChain = nullptr; + + float m_v_x; ///< start global x + float m_v_y; ///< start global y + float m_v_z; ///< start global z + float m_v_px; ///< start global momentum x + float m_v_py; ///< start global momentum y + float m_v_pz; ///< start global momentum z + float m_v_phi; ///< start phi direction + float m_v_eta; ///< start eta direction + float m_tX0; ///< thickness in X0/L0 + float m_tL0; ///< thickness in X0/L0 + + std::vector<float>* m_step_x = new std::vector<float>; ///< step x position + std::vector<float>* m_step_y = new std::vector<float>; ///< step y position + std::vector<float>* m_step_z = new std::vector<float>; ///< step z position + std::vector<float>* m_step_dx = new std::vector<float>; ///< step x direction + std::vector<float>* m_step_dy = new std::vector<float>; ///< step y direction + std::vector<float>* m_step_dz = new std::vector<float>; ///< step z direction + std::vector<float>* m_step_length = new std::vector<float>; ///< step length + std::vector<float>* m_step_X0 = new std::vector<float>; ///< step material x0 + std::vector<float>* m_step_L0 = new std::vector<float>; ///< step material l0 + std::vector<float>* m_step_A = new std::vector<float>; ///< step material A + std::vector<float>* m_step_Z = new std::vector<float>; ///< step material Z + std::vector<float>* m_step_rho = + new std::vector<float>; ///< step material rho +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootMaterialTrackWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootMaterialTrackWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..64c3d42e9aa4cc93c8198a204e35f6b0465986bf --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootMaterialTrackWriter.hpp @@ -0,0 +1,139 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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 <Acts/Propagator/MaterialInteractor.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <mutex> + +#include "ACTFW/Framework/IService.hpp" +#include "ACTFW/Framework/ProcessCode.hpp" +#include "ACTFW/Framework/WriterT.hpp" + +class TFile; +class TTree; + +namespace Acts { +// Using some short hands for Recorded Material +using RecordedMaterial = MaterialInteractor::result_type; +// And recorded material track +// - this is start: position, start momentum +// and the Recorded material +using RecordedMaterialTrack = + std::pair<std::pair<Acts::Vector3D, Acts::Vector3D>, RecordedMaterial>; +} // namespace Acts + +namespace FW { + +/// @class RootMaterialTrackWriter +/// +/// @brief Writes out MaterialTrack collections from a root file +/// +/// This service is the root implementation of the IWriterT. +/// It writes out a MaterialTrack which is usually generated from +/// Geant4 material mapping +class RootMaterialTrackWriter + : public WriterT<std::vector<Acts::RecordedMaterialTrack>> { + public: + struct Config { + std::string collection = + "material-tracks"; ///< material collection to write + std::string filePath = ""; ///< path of the output file + std::string fileMode = "RECREATE"; ///< file access mode + std::string treeName = "material-tracks"; ///< name of the output tree + TFile* rootFile = nullptr; ///< common root file + + /// Re-calculate total values from individual steps (for cross-checks) + bool recalculateTotals = false; + /// Write aut pre and post step (for G4), otherwise central step position + bool prePostStep = false; + /// Write the surface to which the material step correpond + bool storesurface = false; + }; + + /// Constructor with + /// @param cfg configuration struct + /// @param output logging level + RootMaterialTrackWriter(const Config& cfg, + Acts::Logging::Level level = Acts::Logging::INFO); + + /// Virtual destructor + ~RootMaterialTrackWriter() override; + + /// Framework intialize method + FW::ProcessCode endRun() final override; + + protected: + // This implementation holds the actual writing method + /// and is called by the WriterT<>::write interface + /// + /// @param ctx The Algorithm context with per event information + /// @param clusters is the data to be written out + ProcessCode writeT(const AlgorithmContext& ctx, + const std::vector<Acts::RecordedMaterialTrack>& + materialtracks) final override; + + private: + /// The config class + Config m_cfg; + /// mutex used to protect multi-threaded writes + std::mutex m_writeMutex; + /// The output file name + TFile* m_outputFile; + /// The output tree name + TTree* m_outputTree; + + float m_v_x; ///< start global x + float m_v_y; ///< start global y + float m_v_z; ///< start global z + float m_v_px; ///< start global momentum x + float m_v_py; ///< start global momentum y + float m_v_pz; ///< start global momentum z + float m_v_phi; ///< start phi direction + float m_v_eta; ///< start eta direction + float m_tX0; ///< thickness in X0/L0 + float m_tL0; ///< thickness in X0/L0 + + std::vector<float> m_step_sx; ///< step x (start) position (optional) + std::vector<float> m_step_sy; ///< step y (start) position (optional) + std::vector<float> m_step_sz; ///< step z (start) position (optional) + std::vector<float> m_step_x; ///< step x position + std::vector<float> m_step_y; ///< step y position + std::vector<float> m_step_z; ///< step z position + std::vector<float> m_step_ex; ///< step x (end) position (optional) + std::vector<float> m_step_ey; ///< step y (end) position (optional) + std::vector<float> m_step_ez; ///< step z (end) position (optional) + std::vector<float> m_step_dx; ///< step x direction + std::vector<float> m_step_dy; ///< step y direction + std::vector<float> m_step_dz; ///< step z direction + std::vector<float> m_step_length; ///< step length + std::vector<float> m_step_X0; ///< step material x0 + std::vector<float> m_step_L0; ///< step material l0 + std::vector<float> m_step_A; ///< step material A + std::vector<float> m_step_Z; ///< step material Z + std::vector<float> m_step_rho; ///< step material rho + + std::vector<std::uint64_t> + m_sur_id; ///< ID of the suface associated with the step + std::vector<int32_t> + m_sur_type; ///< Type of the suface associated with the step + std::vector<float> m_sur_x; ///< x position of the center of the suface + ///< associated with the step + std::vector<float> m_sur_y; ///< y position of the center of the suface + ///< associated with the step + std::vector<float> m_sur_z; ///< z position of the center of the suface + ///< associated with the step + + std::vector<float> + m_sur_range_min; ///< Min range of the suface associated with the step + std::vector<float> + m_sur_range_max; ///< Max range of the suface associated with the step +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootMaterialWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootMaterialWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..043035f05f8f0f779c2f7efa4b714a0da2e53d37 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootMaterialWriter.hpp @@ -0,0 +1,154 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017-2019 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/. + +/////////////////////////////////////////////////////////////////// +// RootMaterialWriter.h +/////////////////////////////////////////////////////////////////// + +#pragma once + +#include <Acts/Geometry/GeometryID.hpp> +#include <Acts/Geometry/TrackingGeometry.hpp> +#include <Acts/Geometry/TrackingVolume.hpp> +#include <Acts/Material/IMaterialDecorator.hpp> +#include <Acts/Material/ISurfaceMaterial.hpp> +#include <Acts/Material/IVolumeMaterial.hpp> +#include <Acts/Surfaces/Surface.hpp> +#include <Acts/Utilities/Definitions.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <map> +#include <mutex> + +#include "ACTFW/Framework/ProcessCode.hpp" + +namespace Acts { +using SurfaceMaterialMap = + std::map<GeometryID, std::shared_ptr<const ISurfaceMaterial>>; +using VolumeMaterialMap = + std::map<GeometryID, std::shared_ptr<const IVolumeMaterial>>; +using DetectorMaterialMaps = std::pair<SurfaceMaterialMap, VolumeMaterialMap>; +} // namespace Acts + +namespace FW { + +/// @brief Material decorator from Root format +/// +/// This reads in material maps for surfaces and volumes +/// from a root file +class RootMaterialWriter { + public: + /// @class Config + /// + /// Configuration of the Writer + struct Config { + /// Steering to handle sensitive data + bool processSensitives = true; + + /// Steering to handle approach data + bool processApproaches = true; + + /// Steering to handle representing data + bool processRepresenting = true; + + /// Steering to handle boundary data + bool processBoundaries = true; + + /// Steering to handle volume data + bool processVolumes = true; + + /// The name of the output tree + std::string folderNameBase = "Material"; + /// The volume identification string + std::string voltag = "_vol"; + /// The boundary identification string + std::string boutag = "_bou"; + /// The layer identification string + std::string laytag = "_lay"; + /// The approach identification string + std::string apptag = "_app"; + /// The sensitive identification string + std::string sentag = "_sen"; + /// The bin number tag + std::string ntag = "n"; + /// The value tag -> binning values: binZ, binR, binPhi, etc. + std::string vtag = "v"; + /// The option tag -> binning options: open, closed + std::string otag = "o"; + /// The range min tag: min value + std::string mintag = "min"; + /// The range max tag: max value + std::string maxtag = "max"; + /// The thickness tag + std::string ttag = "t"; + /// The x0 tag + std::string x0tag = "x0"; + /// The l0 tag + std::string l0tag = "l0"; + /// The A tag + std::string atag = "A"; + /// The Z tag + std::string ztag = "Z"; + /// The rho tag + std::string rhotag = "rho"; + /// The name of the output file + std::string fileName = "material-maps.root"; + /// The default logger + std::shared_ptr<const Acts::Logger> logger; + // The name of the writer + std::string name = ""; + + /// Constructor + /// + /// @param lname Name of the writer tool + /// @param lvl The output logging level + Config(const std::string& lname = "RootMaterialWriter", + Acts::Logging::Level lvl = Acts::Logging::INFO) + : logger(Acts::getDefaultLogger(lname, lvl)), name(lname) {} + }; + + /// Constructor + /// + /// @param cfg The configuration struct + RootMaterialWriter(const Config& cfg); + + /// Virtual destructor + ~RootMaterialWriter() = default; + + /// Write out the material map + /// + /// @param detMaterial is the SurfaceMaterial and VolumeMaterial maps + void write(const Acts::DetectorMaterialMaps& detMaterial); + + /// Write out the material map from Geometry + /// + /// @param tGeometry is the TrackingGeometry + void write(const Acts::TrackingGeometry& tGeometry); + + private: + /// Collect the material from the tracking geometry + /// + /// @param tVolume The TrackingVolume for the material to be collected + /// @param [in,out] detMatMap the map to be filled + void collectMaterial(const Acts::TrackingVolume& tVolume, + Acts::DetectorMaterialMaps& detMatMap); + + /// Collect the material from the tracking geometry + /// + /// @param tLayer The TrackingVolume for the material to be collected + /// @param [in,out] detMatMap the map to be filled + void collectMaterial(const Acts::Layer& tLayer, + Acts::DetectorMaterialMaps& detMatMap); + + /// The config class + Config m_cfg; + + /// Private access to the logging instance + const Acts::Logger& logger() const { return *m_cfg.logger; } +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootParticleWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootParticleWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4e8077d1da5c28e6c0d7bbca9723653f16081678 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootParticleWriter.hpp @@ -0,0 +1,102 @@ +// 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/. + +#pragma once + +#include <cstdint> +#include <mutex> +#include <string> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Framework/WriterT.hpp" + +class TFile; +class TTree; + +namespace FW { + +/// Write out particles as a flat TTree. +/// +/// Each entry in the TTree corresponds to one particle 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 RootParticleWriter final : public WriterT<SimParticleContainer> { + public: + struct Config { + /// Input particle collection to write. + std::string inputParticles; + /// 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 = "particles"; + }; + + /// Construct the particle writer. + /// + /// @params cfg is the configuration object + /// @params lvl is the logging level + RootParticleWriter(const Config& cfg, Acts::Logging::Level lvl); + + /// Ensure underlying file is closed. + ~RootParticleWriter() final override; + + /// End-of-run hook + ProcessCode endRun() final override; + + protected: + /// Type-specific write implementation. + /// + /// @param[in] ctx is the algorithm context + /// @param[in] particles are the particle to be written + ProcessCode writeT(const AlgorithmContext& ctx, + const SimParticleContainer& particles) final override; + + private: + Config m_cfg; + std::mutex m_writeMutex; + TFile* m_outputFile = nullptr; + TTree* m_outputTree = nullptr; + /// Event identifier. + uint32_t m_eventId; + /// Event-unique particle identifier a.k.a barcode. + uint64_t m_particleId; + /// Particle type a.k.a. PDG particle number + int32_t m_particleType; + /// Production process type, i.e. what generated the particle. + uint32_t m_process; + /// Production position components in mm. + float m_vx, m_vy, m_vz; + // Production time in ns. + float m_vt; + /// Momentum components in GeV. + float m_px, m_py, m_pz; + /// Mass in GeV. + float m_m; + /// Charge in e. + float m_q; + // Derived kinematic quantities + /// Direction pseudo-rapidity. + float m_eta; + /// Direction angle in the transverse plane. + float m_phi; + /// Transverse momentum in GeV. + float m_pt; + // Decoded particle identifier; see Barcode definition for details. + uint32_t m_vertexPrimary; + uint32_t m_vertexSecondary; + uint32_t m_particle; + uint32_t m_generation; + uint32_t m_subParticle; +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootPlanarClusterWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootPlanarClusterWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1dbdde6673632dbd085bc42961be7c2100e5cd84 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootPlanarClusterWriter.hpp @@ -0,0 +1,102 @@ +// 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/. + +#pragma once + +#include <mutex> + +#include "ACTFW/EventData/GeometryContainers.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "Acts/Plugins/Digitization/PlanarModuleCluster.hpp" + +class TFile; +class TTree; + +namespace FW { + +/// @class RootPlanarClusterWriter +/// +/// Write out a planar cluster collection into a root file +/// to avoid immense long vectors, each cluster is one entry +/// in the root file for optimised data writing speed +/// The event number is part of the written data. +/// +/// A common file can be provided for to the writer to attach his TTree, +/// this is done by setting the Config::rootFile pointer to an existing file +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +class RootPlanarClusterWriter + : public WriterT<GeometryIdMultimap<Acts::PlanarModuleCluster>> { + public: + struct Config { + /// Which cluster collection to write. + std::string inputClusters; + /// Which simulated (truth) hits collection to use. + std::string inputSimulatedHits; + std::string filePath = ""; ///< path of the output file + std::string fileMode = "RECREATE"; ///< file access mode + std::string treeName = "clusters"; ///< name of the output tree + TFile* rootFile = nullptr; ///< common root file + }; + + /// Constructor with + /// @param cfg configuration struct + /// @param output logging level + RootPlanarClusterWriter(const Config& cfg, Acts::Logging::Level lvl); + + /// Virtual destructor + ~RootPlanarClusterWriter() override; + + /// End-of-run hook + ProcessCode endRun() final override; + + protected: + /// This implementation holds the actual writing method + /// and is called by the WriterT<>::write interface + /// + /// @param ctx The Algorithm context with per event information + /// @param clusters is the data to be written out + ProcessCode writeT(const AlgorithmContext& ctx, + const GeometryIdMultimap<Acts::PlanarModuleCluster>& + clusters) final override; + + private: + Config m_cfg; ///< the configuration object + std::mutex m_writeMutex; ///< protect multi-threaded writes + TFile* m_outputFile; ///< the output file + TTree* m_outputTree; ///< the output tree + int m_eventNr; ///< the event number of + int m_volumeID; ///< volume identifier + int m_layerID; ///< layer identifier + int m_surfaceID; ///< surface identifier + float m_x; ///< global x + float m_y; ///< global y + float m_z; ///< global z + float m_t; ///< global t + float m_lx; ///< local lx + float m_ly; ///< local ly + float m_cov_lx; ///< local covariance lx + float m_cov_ly; ///< local covariance ly + std::vector<int> m_cell_IDx; ///< cell ID in lx + std::vector<int> m_cell_IDy; ///< cell ID in ly + std::vector<float> m_cell_lx; ///< local cell position x + std::vector<float> m_cell_ly; ///< local cell position y + std::vector<float> m_cell_data; ///< local cell position y + + // (optional) the truth position + std::vector<float> m_t_gx; ///< truth position global x + std::vector<float> m_t_gy; ///< truth position global y + std::vector<float> m_t_gz; ///< truth position global z + std::vector<float> m_t_gt; ///< truth time t + std::vector<float> m_t_lx; ///< truth position local x + std::vector<float> m_t_ly; ///< truth position local y + std::vector<unsigned long> + m_t_barcode; ///< associated truth particle barcode +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootPropagationStepsWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootPropagationStepsWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6ccf3a6cf971a887a42c40b791f0c4fb3098c474 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootPropagationStepsWriter.hpp @@ -0,0 +1,91 @@ +// 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 <ACTFW/Framework/WriterT.hpp> +#include <mutex> + +#include "Acts/Propagator/detail/SteppingLogger.hpp" + +class TFile; +class TTree; + +namespace FW { + +using PropagationSteps = std::vector<Acts::detail::Step>; + +/// @class RootPropagationStepsWriter +/// +/// Write out the steps of test propgations for stepping validation, +/// each step sequence is one entry in the in the root file for optimised +/// data writing speed. +/// The event number is part of the written data. +/// +/// A common file can be provided for to the writer to attach his TTree, +/// this is done by setting the Config::rootFile pointer to an existing file +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +class RootPropagationStepsWriter + : public WriterT<std::vector<PropagationSteps>> { + public: + struct Config { + std::string collection = + "propagation_steps"; ///< particle collection to write + std::string filePath = ""; ///< path of the output file + std::string fileMode = "RECREATE"; ///< file access mode + std::string treeName = "propagation_steps"; ///< name of the output tree + TFile* rootFile = nullptr; ///< common root file + }; + + /// Constructor with + /// @param cfg configuration struct + /// @param output logging level + RootPropagationStepsWriter(const Config& cfg, + Acts::Logging::Level level = Acts::Logging::INFO); + + /// Virtual destructor + ~RootPropagationStepsWriter() override; + + /// End-of-run hook + ProcessCode endRun() final override; + + protected: + /// This implementation holds the actual writing method + /// and is called by the WriterT<>::write interface + /// + /// @param context The Algorithm context with per event information + /// @param steps is the data to be written out + ProcessCode writeT(const AlgorithmContext& context, + const std::vector<PropagationSteps>& steps) final override; + + private: + Config m_cfg; ///< the configuration object + std::mutex m_writeMutex; ///< protect multi-threaded writes + TFile* m_outputFile; ///< the output file name + TTree* m_outputTree; ///< the output tree + int m_eventNr; ///< the event number of + std::vector<int> m_volumeID; ///< volume identifier + std::vector<int> m_boundaryID; ///< boundary identifier + std::vector<int> m_layerID; ///< layer identifier if + std::vector<int> m_approachID; ///< surface identifier + std::vector<int> m_sensitiveID; ///< surface identifier + std::vector<float> m_x; ///< global x + std::vector<float> m_y; ///< global y + std::vector<float> m_z; ///< global z + std::vector<float> m_dx; ///< global direction x + std::vector<float> m_dy; ///< global direction y + std::vector<float> m_dz; ///< global direction z + std::vector<int> m_step_type; ///< step type + std::vector<float> m_step_acc; ///< accuracy + std::vector<float> m_step_act; ///< actor check + std::vector<float> m_step_abt; ///< aborter + std::vector<float> m_step_usr; ///< user +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootSimHitWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootSimHitWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b98cd385e59cdb388996ab2a99ff409cb02e09f6 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootSimHitWriter.hpp @@ -0,0 +1,93 @@ +// 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" + +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 RootSimHitWriter 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 + RootSimHitWriter(const Config& cfg, Acts::Logging::Level lvl); + + /// Ensure underlying file is closed. + ~RootSimHitWriter() 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; +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootTrackParameterWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootTrackParameterWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c2e6da18c88ce471ed3e505736b273fe81804ecf --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootTrackParameterWriter.hpp @@ -0,0 +1,70 @@ +// 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/. + +#pragma once + +#include <Acts/EventData/TrackParameters.hpp> +#include <mutex> + +#include "ACTFW/Framework/WriterT.hpp" + +class TFile; +class TTree; + +namespace FW { + +using BoundTrackParameters = Acts::BoundParameters; +using TrackParameterWriter = WriterT<std::vector<BoundTrackParameters>>; + +/// Writes out SingleBoundTrackParamters into a TTree + +class RootTrackParameterWriter final : public TrackParameterWriter { + public: + struct Config { + std::string collection; ///< parameter collection to write + std::string filePath; ///< path of the output file + std::string fileMode = "RECREATE"; ///< file access mode + std::string treeName = "trackparameters"; ///< name of the output tree + TFile* rootFile = nullptr; ///< common root file + }; + + /// Constructor + /// + /// @param cfg Configuration struct + /// @param level Message level declaration + RootTrackParameterWriter(const Config& cfg, + Acts::Logging::Level level = Acts::Logging::INFO); + + /// Virtual destructor + ~RootTrackParameterWriter() override; + + /// End-of-run hook + ProcessCode endRun() final override; + + protected: + /// @brief Write method called by the base class + /// @param [in] ctx is the algorithm context for event information + /// @param [in] trackParams are parameters to write + ProcessCode writeT( + const AlgorithmContext& ctx, + const std::vector<BoundTrackParameters>& trackParams) final override; + + private: + Config m_cfg; ///< The config class + std::mutex m_writeMutex; ///< Mutex used to protect multi-threaded writes + TFile* m_outputFile{nullptr}; ///< The output file + TTree* m_outputTree{nullptr}; ///< The output tree + int m_eventNr{0}; ///< the event number of + float m_d0{0.}; ///< transversal IP d0 + float m_z0{0.}; ///< longitudinal IP z0 + float m_phi{0.}; ///< phi + float m_theta{0.}; ///< theta + float m_qp{0.}; ///< q/p +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootTrajectoryWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootTrajectoryWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..af5865adf8596dcfa544ebb64a1356e0ed9c3ee0 --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootTrajectoryWriter.hpp @@ -0,0 +1,249 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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 <mutex> +#include <vector> + +#include "ACTFW/EventData/Track.hpp" +#include "ACTFW/Framework/WriterT.hpp" +#include "Acts/Utilities/ParameterDefinitions.hpp" + +class TFile; +class TTree; + +namespace FW { + +/// @class RootTrajectoryWriter +/// +/// Write out a trajectory (i.e. a vector of +/// trackState at the moment) into a TTree +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +/// +/// Each entry in the TTree corresponds to one trajectory for optimum +/// writing speed. The event number is part of the written data. +/// +/// A common file can be provided for to the writer to attach his TTree, +/// this is done by setting the Config::rootFile pointer to an existing +/// file +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +class RootTrajectoryWriter final : public WriterT<TrajectoryContainer> { + public: + /// @brief The nested configuration struct + struct Config { + std::string inputParticles; ///< input truth particles collection. + std::string inputTrajectories; ///< input (fitted) trajectories collection + std::string outputDir; ///< output directory + std::string outputFilename = "tracks.root"; ///< output filename + std::string outputTreename = "tracks"; ///< name of the output tree + std::string fileMode = "RECREATE"; ///< file access mode + TFile* rootFile = nullptr; ///< common root file + }; + + /// Constructor + /// + /// @param cfg Configuration struct + /// @param level Message level declaration + RootTrajectoryWriter(const Config& cfg, Acts::Logging::Level lvl); + + /// Virtual destructor + ~RootTrajectoryWriter() final override; + + /// End-of-run hook + ProcessCode endRun() final override; + + protected: + /// @brief Write method called by the base class + /// @param [in] ctx is the algorithm context for event information + /// @param [in] trajectories are what to be written out + ProcessCode writeT(const AlgorithmContext& ctx, + const TrajectoryContainer& trajectories) final override; + + private: + Config m_cfg; ///< The config class + std::mutex m_writeMutex; ///< Mutex used to protect multi-threaded writes + TFile* m_outputFile{nullptr}; ///< The output file + TTree* m_outputTree{nullptr}; ///< The output tree + int m_eventNr{0}; ///< the event number + int m_trajNr{0}; ///< the trajectory number + + unsigned long m_t_barcode{0}; ///< Truth particle barcode + int m_t_charge{0}; ///< Truth particle charge + float m_t_time{0}; ///< Truth particle time + float m_t_vx{-99.}; ///< Truth particle vertex x + float m_t_vy{-99.}; ///< Truth particle vertex y + float m_t_vz{-99.}; ///< Truth particle vertex z + float m_t_px{-99.}; ///< Truth particle initial momentum px + float m_t_py{-99.}; ///< Truth particle initial momentum py + float m_t_pz{-99.}; ///< Truth particle initial momentum pz + float m_t_theta{-99.}; ///< Truth particle initial momentum theta + float m_t_phi{-99.}; ///< Truth particle initial momentum phi + float m_t_pT{-99.}; ///< Truth particle initial momentum pT + float m_t_eta{-99.}; ///< Truth particle initial momentum eta + + std::vector<float> m_t_x; ///< Global truth hit position x + std::vector<float> m_t_y; ///< Global truth hit position y + std::vector<float> m_t_z; ///< Global truth hit position z + std::vector<float> m_t_r; ///< Global truth hit position r + std::vector<float> + m_t_dx; ///< Truth particle direction x at global hit position + std::vector<float> + m_t_dy; ///< Truth particle direction y at global hit position + std::vector<float> + m_t_dz; ///< Truth particle direction z at global hit position + + std::vector<float> m_t_eLOC0; ///< truth parameter eLOC_0 + std::vector<float> m_t_eLOC1; ///< truth parameter eLOC_1 + std::vector<float> m_t_ePHI; ///< truth parameter ePHI + std::vector<float> m_t_eTHETA; ///< truth parameter eTHETA + std::vector<float> m_t_eQOP; ///< truth parameter eQOP + std::vector<float> m_t_eT; ///< truth parameter eT + + int m_nStates{0}; ///< number of all states + int m_nMeasurements{0}; ///< number of states with measurements + std::vector<int> m_volumeID; ///< volume identifier + std::vector<int> m_layerID; ///< layer identifier + std::vector<int> m_moduleID; ///< surface identifier + std::vector<float> m_lx_hit; ///< uncalibrated measurement local x + std::vector<float> m_ly_hit; ///< uncalibrated measurement local y + std::vector<float> m_x_hit; ///< uncalibrated measurement global x + std::vector<float> m_y_hit; ///< uncalibrated measurement global y + std::vector<float> m_z_hit; ///< uncalibrated measurement global z + std::vector<float> m_res_x_hit; ///< hit residual x + std::vector<float> m_res_y_hit; ///< hit residual y + std::vector<float> m_err_x_hit; ///< hit err x + std::vector<float> m_err_y_hit; ///< hit err y + std::vector<float> m_pull_x_hit; ///< hit pull x + std::vector<float> m_pull_y_hit; ///< hit pull y + std::vector<int> m_dim_hit; ///< dimension of measurement + + bool m_hasFittedParams; ///< if the track has fitted parameter + float m_eLOC0_fit{-99.}; ///< fitted parameter eLOC_0 + float m_eLOC1_fit{-99.}; ///< fitted parameter eLOC_1 + float m_ePHI_fit{-99.}; ///< fitted parameter ePHI + float m_eTHETA_fit{-99.}; ///< fitted parameter eTHETA + float m_eQOP_fit{-99.}; ///< fitted parameter eQOP + float m_eT_fit{-99.}; ///< fitted parameter eT + float m_err_eLOC0_fit{-99.}; ///< fitted parameter eLOC_-99.err + float m_err_eLOC1_fit{-99.}; ///< fitted parameter eLOC_1 err + float m_err_ePHI_fit{-99.}; ///< fitted parameter ePHI err + float m_err_eTHETA_fit{-99.}; ///< fitted parameter eTHETA err + float m_err_eQOP_fit{-99.}; ///< fitted parameter eQOP err + float m_err_eT_fit{-99.}; ///< fitted parameter eT err + + int m_nPredicted{0}; ///< number of states with predicted parameter + std::vector<bool> m_prt; ///< predicted status + std::vector<float> m_eLOC0_prt; ///< predicted parameter eLOC0 + std::vector<float> m_eLOC1_prt; ///< predicted parameter eLOC1 + std::vector<float> m_ePHI_prt; ///< predicted parameter ePHI + std::vector<float> m_eTHETA_prt; ///< predicted parameter eTHETA + std::vector<float> m_eQOP_prt; ///< predicted parameter eQOP + std::vector<float> m_eT_prt; ///< predicted parameter eT + std::vector<float> m_res_eLOC0_prt; ///< predicted parameter eLOC0 residual + std::vector<float> m_res_eLOC1_prt; ///< predicted parameter eLOC1 residual + std::vector<float> m_res_ePHI_prt; ///< predicted parameter ePHI residual + std::vector<float> m_res_eTHETA_prt; ///< predicted parameter eTHETA residual + std::vector<float> m_res_eQOP_prt; ///< predicted parameter eQOP residual + std::vector<float> m_res_eT_prt; ///< predicted parameter eT residual + std::vector<float> m_err_eLOC0_prt; ///< predicted parameter eLOC0 error + std::vector<float> m_err_eLOC1_prt; ///< predicted parameter eLOC1 error + std::vector<float> m_err_ePHI_prt; ///< predicted parameter ePHI error + std::vector<float> m_err_eTHETA_prt; ///< predicted parameter eTHETA error + std::vector<float> m_err_eQOP_prt; ///< predicted parameter eQOP error + std::vector<float> m_err_eT_prt; ///< predicted parameter eT error + std::vector<float> m_pull_eLOC0_prt; ///< predicted parameter eLOC0 pull + std::vector<float> m_pull_eLOC1_prt; ///< predicted parameter eLOC1 pull + std::vector<float> m_pull_ePHI_prt; ///< predicted parameter ePHI pull + std::vector<float> m_pull_eTHETA_prt; ///< predicted parameter eTHETA pull + std::vector<float> m_pull_eQOP_prt; ///< predicted parameter eQOP pull + std::vector<float> m_pull_eT_prt; ///< predicted parameter eT pull + std::vector<float> m_x_prt; ///< predicted global x + std::vector<float> m_y_prt; ///< predicted global y + std::vector<float> m_z_prt; ///< predicted global z + std::vector<float> m_px_prt; ///< predicted momentum px + std::vector<float> m_py_prt; ///< predicted momentum py + std::vector<float> m_pz_prt; ///< predicted momentum pz + std::vector<float> m_eta_prt; ///< predicted momentum eta + std::vector<float> m_pT_prt; ///< predicted momentum pT + + int m_nFiltered{0}; ///< number of states with filtered parameter + std::vector<bool> m_flt; ///< filtered status + std::vector<float> m_eLOC0_flt; ///< filtered parameter eLOC0 + std::vector<float> m_eLOC1_flt; ///< filtered parameter eLOC1 + std::vector<float> m_ePHI_flt; ///< filtered parameter ePHI + std::vector<float> m_eTHETA_flt; ///< filtered parameter eTHETA + std::vector<float> m_eQOP_flt; ///< filtered parameter eQOP + std::vector<float> m_eT_flt; ///< filtered parameter eT + std::vector<float> m_res_eLOC0_flt; ///< filtered parameter eLOC0 residual + std::vector<float> m_res_eLOC1_flt; ///< filtered parameter eLOC1 residual + std::vector<float> m_res_ePHI_flt; ///< filtered parameter ePHI residual + std::vector<float> m_res_eTHETA_flt; ///< filtered parameter eTHETA residual + std::vector<float> m_res_eQOP_flt; ///< filtered parameter eQOP residual + std::vector<float> m_res_eT_flt; ///< filtered parameter eT residual + std::vector<float> m_err_eLOC0_flt; ///< filtered parameter eLOC0 error + std::vector<float> m_err_eLOC1_flt; ///< filtered parameter eLOC1 error + std::vector<float> m_err_ePHI_flt; ///< filtered parameter ePHI error + std::vector<float> m_err_eTHETA_flt; ///< filtered parameter eTHETA error + std::vector<float> m_err_eQOP_flt; ///< filtered parameter eQOP error + std::vector<float> m_err_eT_flt; ///< filtered parameter eT error + std::vector<float> m_pull_eLOC0_flt; ///< filtered parameter eLOC0 pull + std::vector<float> m_pull_eLOC1_flt; ///< filtered parameter eLOC1 pull + std::vector<float> m_pull_ePHI_flt; ///< filtered parameter ePHI pull + std::vector<float> m_pull_eTHETA_flt; ///< filtered parameter eTHETA pull + std::vector<float> m_pull_eQOP_flt; ///< filtered parameter eQOP pull + std::vector<float> m_pull_eT_flt; ///< filtered parameter eT pull + std::vector<float> m_x_flt; ///< filtered global x + std::vector<float> m_y_flt; ///< filtered global y + std::vector<float> m_z_flt; ///< filtered global z + std::vector<float> m_px_flt; ///< filtered momentum px + std::vector<float> m_py_flt; ///< filtered momentum py + std::vector<float> m_pz_flt; ///< filtered momentum pz + std::vector<float> m_eta_flt; ///< filtered momentum eta + std::vector<float> m_pT_flt; ///< filtered momentum pT + std::vector<float> m_chi2; ///< chisq from filtering + + int m_nSmoothed{0}; ///< number of states with smoothed parameter + std::vector<bool> m_smt; ///< smoothed status + std::vector<float> m_eLOC0_smt; ///< smoothed parameter eLOC0 + std::vector<float> m_eLOC1_smt; ///< smoothed parameter eLOC1 + std::vector<float> m_ePHI_smt; ///< smoothed parameter ePHI + std::vector<float> m_eTHETA_smt; ///< smoothed parameter eTHETA + std::vector<float> m_eQOP_smt; ///< smoothed parameter eQOP + std::vector<float> m_eT_smt; ///< smoothed parameter eT + std::vector<float> m_res_eLOC0_smt; ///< smoothed parameter eLOC0 residual + std::vector<float> m_res_eLOC1_smt; ///< smoothed parameter eLOC1 residual + std::vector<float> m_res_ePHI_smt; ///< smoothed parameter ePHI residual + std::vector<float> m_res_eTHETA_smt; ///< smoothed parameter eTHETA residual + std::vector<float> m_res_eQOP_smt; ///< smoothed parameter eQOP residual + std::vector<float> m_res_eT_smt; ///< smoothed parameter eT residual + std::vector<float> m_err_eLOC0_smt; ///< smoothed parameter eLOC0 error + std::vector<float> m_err_eLOC1_smt; ///< smoothed parameter eLOC1 error + std::vector<float> m_err_ePHI_smt; ///< smoothed parameter ePHI error + std::vector<float> m_err_eTHETA_smt; ///< smoothed parameter eTHETA error + std::vector<float> m_err_eQOP_smt; ///< smoothed parameter eQOP error + std::vector<float> m_err_eT_smt; ///< smoothed parameter eT error + std::vector<float> m_pull_eLOC0_smt; ///< smoothed parameter eLOC0 pull + std::vector<float> m_pull_eLOC1_smt; ///< smoothed parameter eLOC1 pull + std::vector<float> m_pull_ePHI_smt; ///< smoothed parameter ePHI pull + std::vector<float> m_pull_eTHETA_smt; ///< smoothed parameter eTHETA pull + std::vector<float> m_pull_eQOP_smt; ///< smoothed parameter eQOP pull + std::vector<float> m_pull_eT_smt; ///< smoothed parameter eT pull + std::vector<float> m_x_smt; ///< smoothed global x + std::vector<float> m_y_smt; ///< smoothed global y + std::vector<float> m_z_smt; ///< smoothed global z + std::vector<float> m_px_smt; ///< smoothed momentum px + std::vector<float> m_py_smt; ///< smoothed momentum py + std::vector<float> m_pz_smt; ///< smoothed momentum pz + std::vector<float> m_eta_smt; ///< smoothed momentum eta + std::vector<float> m_pT_smt; ///< smoothed momentum pT +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootVertexAndTracksReader.hpp b/Io/Root/include/ACTFW/Io/Root/RootVertexAndTracksReader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3cff03c4445659f5364e6882429d82af3051536d --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootVertexAndTracksReader.hpp @@ -0,0 +1,87 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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 <Acts/Propagator/MaterialInteractor.hpp> +#include <Acts/Utilities/Definitions.hpp> +#include <Acts/Utilities/Logger.hpp> +#include <mutex> +#include <vector> + +#include "ACTFW/Framework/IReader.hpp" +#include "ACTFW/Framework/IService.hpp" +#include "ACTFW/Framework/ProcessCode.hpp" + +class TChain; + +namespace FW { + +/// @class RootVertexAndTracksReader +/// +/// @brief Reads in vertex and tracks information from a root file +/// and fills it into a format to be understood by the vertexing algorithms +class RootVertexAndTracksReader final : public IReader { + public: + /// @brief The nested configuration struct + struct Config { + std::string outputCollection = "vertexAndTracksCollection"; + std::string treeName = "event"; ///< name of the output tree + std::vector<std::string> fileList; ///< The name of the input file + unsigned int batchSize = 1; ///!< Batch + }; + + /// Constructor + /// @param cfg The Configuration struct + /// @param lvl Message level declaration + RootVertexAndTracksReader(Config cfg, Acts::Logging::Level lvl); + + /// Destructor + ~RootVertexAndTracksReader() final override; + + /// Framework name() method + std::string name() const final override; + + /// Return the available events range. + std::pair<size_t, size_t> availableEvents() const final override; + + /// Read out data from the input stream + /// + /// @param context The algorithm context + ProcessCode read(const FW::AlgorithmContext& context) final override; + + private: + /// The config class + Config m_cfg; + /// mutex used to protect multi-threaded reads + std::mutex m_read_mutex; + /// The number of events + size_t m_events = 0; + /// The input tree name + TChain* m_inputChain = nullptr; + int m_eventNr = 0; + + std::vector<double>* m_ptrVx = new std::vector<double>; + std::vector<double>* m_ptrVy = new std::vector<double>; + std::vector<double>* m_ptrVz = new std::vector<double>; + std::vector<double>* m_ptrD0 = new std::vector<double>; + std::vector<double>* m_ptrZ0 = new std::vector<double>; + std::vector<double>* m_ptrPhi = new std::vector<double>; + std::vector<double>* m_ptrTheta = new std::vector<double>; + std::vector<double>* m_ptrQP = new std::vector<double>; + std::vector<double>* m_ptrTime = new std::vector<double>; + std::vector<int>* m_ptrVtxID = new std::vector<int>; + std::vector<std::vector<double>>* m_ptrTrkCov = + new std::vector<std::vector<double>>; + + std::unique_ptr<const Acts::Logger> m_logger; + + const Acts::Logger& logger() const { return *m_logger; } +}; + +} // namespace FW diff --git a/Io/Root/include/ACTFW/Io/Root/RootVertexAndTracksWriter.hpp b/Io/Root/include/ACTFW/Io/Root/RootVertexAndTracksWriter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1fce8de69aec7a717b3ed81ccbd24a3daa2c87ae --- /dev/null +++ b/Io/Root/include/ACTFW/Io/Root/RootVertexAndTracksWriter.hpp @@ -0,0 +1,183 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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 <mutex> + +#include "ACTFW/Framework/WriterT.hpp" +#include "ACTFW/TruthTracking/VertexAndTracks.hpp" + +class TFile; +class TTree; + +namespace FW { + +/// Write out vertices together with associated tracks into a TTree +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +/// +/// A common file can be provided for to the writer to attach his TTree, +/// this is done by setting the Config::rootFile pointer to an existing file +/// +/// Safe to use from multiple writer threads - uses a std::mutex lock. +class RootVertexAndTracksWriter final + : public WriterT<std::vector<VertexAndTracks>> { + public: + /// @brief The nested configuration struct + struct Config { + std::string collection; ///< particle collection to write + std::string filePath; ///< path of the output file + std::string fileMode = "RECREATE"; ///< file access mode + std::string treeName = "event"; ///< name of the output tree + TFile* rootFile = nullptr; ///< common root file + }; + + /// Constructor + /// + /// @param cfg Configuration struct + /// @param lvl Message level declaration + RootVertexAndTracksWriter(const Config& cfg, Acts::Logging::Level lvl); + + /// Virtual destructor + ~RootVertexAndTracksWriter() final override; + + /// End-of-run hook + ProcessCode endRun() final override; + + protected: + /// @brief Write method called by the base class + /// @param [in] context is the algorithm context for event information + /// @param [in] vertexAndTracksCollection is the VertexAndTracks collection + ProcessCode writeT(const AlgorithmContext& context, + const std::vector<VertexAndTracks>& + vertexAndTracksCollection) final override; + + private: + Config m_cfg; ///< The config class + std::mutex m_writeMutex; ///< Mutex used to protect multi-threaded writes + TFile* m_outputFile{nullptr}; ///< The output file + TTree* m_outputTree{nullptr}; ///< The output tree + int m_eventNr{0}; ///< the event number of + + /// The vertex positions + std::vector<double> m_vx; + std::vector<double> m_vy; + std::vector<double> m_vz; + + /// The track parameter + std::vector<double> m_d0; + std::vector<double> m_z0; + std::vector<double> m_phi; + std::vector<double> m_theta; + std::vector<double> m_qp; + std::vector<double> m_time; + std::vector<int> m_vtxID; + + /// The track covariance matrix + std::vector<double> m_cov11; + std::vector<double> m_cov12; + std::vector<double> m_cov13; + std::vector<double> m_cov14; + std::vector<double> m_cov15; + std::vector<double> m_cov16; + + std::vector<double> m_cov21; + std::vector<double> m_cov22; + std::vector<double> m_cov23; + std::vector<double> m_cov24; + std::vector<double> m_cov25; + std::vector<double> m_cov26; + + std::vector<double> m_cov31; + std::vector<double> m_cov32; + std::vector<double> m_cov33; + std::vector<double> m_cov34; + std::vector<double> m_cov35; + std::vector<double> m_cov36; + + std::vector<double> m_cov41; + std::vector<double> m_cov42; + std::vector<double> m_cov43; + std::vector<double> m_cov44; + std::vector<double> m_cov45; + std::vector<double> m_cov46; + + std::vector<double> m_cov51; + std::vector<double> m_cov52; + std::vector<double> m_cov53; + std::vector<double> m_cov54; + std::vector<double> m_cov55; + std::vector<double> m_cov56; + + std::vector<double> m_cov61; + std::vector<double> m_cov62; + std::vector<double> m_cov63; + std::vector<double> m_cov64; + std::vector<double> m_cov65; + std::vector<double> m_cov66; + + /// Pointers to the vectors + std::vector<double>* m_ptrVx = &m_vx; + std::vector<double>* m_ptrVy = &m_vy; + std::vector<double>* m_ptrVz = &m_vz; + std::vector<double>* m_ptrD0 = &m_d0; + std::vector<double>* m_ptrZ0 = &m_z0; + std::vector<double>* m_ptrPhi = &m_phi; + std::vector<double>* m_ptrTheta = &m_theta; + std::vector<double>* m_ptrQP = &m_qp; + std::vector<double>* m_ptrTime = &m_time; + std::vector<int>* m_ptrVtxID = &m_vtxID; + + std::vector<double>* m_ptrCov11 = &m_cov11; + std::vector<double>* m_ptrCov12 = &m_cov12; + std::vector<double>* m_ptrCov13 = &m_cov13; + std::vector<double>* m_ptrCov14 = &m_cov14; + std::vector<double>* m_ptrCov15 = &m_cov15; + std::vector<double>* m_ptrCov16 = &m_cov16; + + std::vector<double>* m_ptrCov21 = &m_cov21; + std::vector<double>* m_ptrCov22 = &m_cov22; + std::vector<double>* m_ptrCov23 = &m_cov23; + std::vector<double>* m_ptrCov24 = &m_cov24; + std::vector<double>* m_ptrCov25 = &m_cov25; + std::vector<double>* m_ptrCov26 = &m_cov26; + + std::vector<double>* m_ptrCov31 = &m_cov31; + std::vector<double>* m_ptrCov32 = &m_cov32; + std::vector<double>* m_ptrCov33 = &m_cov33; + std::vector<double>* m_ptrCov34 = &m_cov34; + std::vector<double>* m_ptrCov35 = &m_cov35; + std::vector<double>* m_ptrCov36 = &m_cov36; + + std::vector<double>* m_ptrCov41 = &m_cov41; + std::vector<double>* m_ptrCov42 = &m_cov42; + std::vector<double>* m_ptrCov43 = &m_cov43; + std::vector<double>* m_ptrCov44 = &m_cov44; + std::vector<double>* m_ptrCov45 = &m_cov45; + std::vector<double>* m_ptrCov46 = &m_cov46; + + std::vector<double>* m_ptrCov51 = &m_cov51; + std::vector<double>* m_ptrCov52 = &m_cov52; + std::vector<double>* m_ptrCov53 = &m_cov53; + std::vector<double>* m_ptrCov54 = &m_cov54; + std::vector<double>* m_ptrCov55 = &m_cov55; + std::vector<double>* m_ptrCov56 = &m_cov56; + + std::vector<double>* m_ptrCov61 = &m_cov61; + std::vector<double>* m_ptrCov62 = &m_cov62; + std::vector<double>* m_ptrCov63 = &m_cov63; + std::vector<double>* m_ptrCov64 = &m_cov64; + std::vector<double>* m_ptrCov65 = &m_cov65; + std::vector<double>* m_ptrCov66 = &m_cov66; + + /// @brief Clears all vectors + void ClearAll(); +}; + +} // namespace FW diff --git a/Io/Root/src/RootMaterialDecorator.cpp b/Io/Root/src/RootMaterialDecorator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49362889fb8d8305132c6062421eb0735fa8d0ce --- /dev/null +++ b/Io/Root/src/RootMaterialDecorator.cpp @@ -0,0 +1,194 @@ +// 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/RootMaterialDecorator.hpp" + +#include <Acts/Geometry/GeometryID.hpp> +#include <Acts/Material/BinnedSurfaceMaterial.hpp> +#include <Acts/Material/HomogeneousSurfaceMaterial.hpp> +#include <Acts/Utilities/BinUtility.hpp> +#include <Acts/Utilities/BinningType.hpp> +#include <TFile.h> +#include <TH2F.h> +#include <TIterator.h> +#include <TKey.h> +#include <TList.h> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/finder.hpp> +#include <boost/algorithm/string/iter_find.hpp> +#include <cstdio> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <string> + +FW::RootMaterialDecorator::RootMaterialDecorator( + const FW::RootMaterialDecorator::Config& cfg) + : m_cfg(cfg), m_inputFile(nullptr) { + // Validate the configuration + if (m_cfg.folderNameBase.empty()) { + throw std::invalid_argument("Missing ROOT folder name"); + } else if (m_cfg.fileName.empty()) { + throw std::invalid_argument("Missing file name"); + } else if (!m_cfg.logger) { + throw std::invalid_argument("Missing logger"); + } else if (m_cfg.name.empty()) { + throw std::invalid_argument("Missing service name"); + } + + // Setup ROOT I/O + m_inputFile = TFile::Open(m_cfg.fileName.c_str()); + if (!m_inputFile) { + throw std::ios_base::failure("Could not open '" + m_cfg.fileName); + } + + // Get the list of keys from the file + TList* tlist = m_inputFile->GetListOfKeys(); + auto tIter = tlist->MakeIterator(); + tIter->Reset(); + + // Iterate over the keys in the file + while (TKey* key = (TKey*)(tIter->Next())) { + // The surface material to be read in for this + std::shared_ptr<const Acts::ISurfaceMaterial> sMaterial = nullptr; + + // Remember the directory + std::string tdName(key->GetName()); + + ACTS_VERBOSE("Processing directory: " << tdName); + + // volume + std::vector<std::string> splitNames; + iter_split(splitNames, tdName, + boost::algorithm::first_finder(m_cfg.voltag)); + boost::split(splitNames, splitNames[1], boost::is_any_of("_")); + Acts::GeometryID::Value volID = std::stoi(splitNames[0]); + // boundary + iter_split(splitNames, tdName, + boost::algorithm::first_finder(m_cfg.boutag)); + boost::split(splitNames, splitNames[1], boost::is_any_of("_")); + Acts::GeometryID::Value bouID = std::stoi(splitNames[0]); + // layer + iter_split(splitNames, tdName, + boost::algorithm::first_finder(m_cfg.laytag)); + boost::split(splitNames, splitNames[1], boost::is_any_of("_")); + Acts::GeometryID::Value layID = std::stoi(splitNames[0]); + // approach + iter_split(splitNames, tdName, + boost::algorithm::first_finder(m_cfg.apptag)); + boost::split(splitNames, splitNames[1], boost::is_any_of("_")); + Acts::GeometryID::Value appID = std::stoi(splitNames[0]); + // sensitive + iter_split(splitNames, tdName, + boost::algorithm::first_finder(m_cfg.sentag)); + Acts::GeometryID::Value senID = std::stoi(splitNames[1]); + + // Reconstruct the geometry ID + Acts::GeometryID geoID; + geoID.setVolume(volID); + geoID.setBoundary(bouID); + geoID.setLayer(layID); + geoID.setApproach(appID); + geoID.setSensitive(senID); + ACTS_VERBOSE("GeometryID re-constructed as " << geoID); + + // Construct the names + std::string nName = tdName + "/" + m_cfg.ntag; + std::string vName = tdName + "/" + m_cfg.vtag; + std::string oName = tdName + "/" + m_cfg.otag; + std::string minName = tdName + "/" + m_cfg.mintag; + std::string maxName = tdName + "/" + m_cfg.maxtag; + std::string tName = tdName + "/" + m_cfg.ttag; + std::string x0Name = tdName + "/" + m_cfg.x0tag; + std::string l0Name = tdName + "/" + m_cfg.l0tag; + std::string aName = tdName + "/" + m_cfg.atag; + std::string zName = tdName + "/" + m_cfg.ztag; + std::string rhoName = tdName + "/" + m_cfg.rhotag; + + // Get the histograms + TH1F* n = dynamic_cast<TH1F*>(m_inputFile->Get(nName.c_str())); + TH1F* v = dynamic_cast<TH1F*>(m_inputFile->Get(vName.c_str())); + TH1F* o = dynamic_cast<TH1F*>(m_inputFile->Get(oName.c_str())); + TH1F* min = dynamic_cast<TH1F*>(m_inputFile->Get(minName.c_str())); + TH1F* max = dynamic_cast<TH1F*>(m_inputFile->Get(maxName.c_str())); + TH2F* t = dynamic_cast<TH2F*>(m_inputFile->Get(tName.c_str())); + TH2F* x0 = dynamic_cast<TH2F*>(m_inputFile->Get(x0Name.c_str())); + TH2F* l0 = dynamic_cast<TH2F*>(m_inputFile->Get(l0Name.c_str())); + TH2F* A = dynamic_cast<TH2F*>(m_inputFile->Get(aName.c_str())); + TH2F* Z = dynamic_cast<TH2F*>(m_inputFile->Get(zName.c_str())); + TH2F* rho = dynamic_cast<TH2F*>(m_inputFile->Get(rhoName.c_str())); + + // Only go on when you have all histograms + if (n and v and o and min and max and t and x0 and l0 and A and Z and rho) { + // Get the number of bins + int nbins0 = t->GetNbinsX(); + int nbins1 = t->GetNbinsY(); + + // The material matrix + Acts::MaterialPropertiesMatrix materialMatrix( + nbins1, + Acts::MaterialPropertiesVector(nbins0, Acts::MaterialProperties())); + + // We need binned material properties + if (nbins0 * nbins1 > 1) { + // Fill the matrix first + for (int ib0 = 1; ib0 <= nbins0; ++ib0) { + for (int ib1 = 1; ib1 <= nbins1; ++ib1) { + double dt = t->GetBinContent(ib0, ib1); + if (dt > 0.) { + double dx0 = x0->GetBinContent(ib0, ib1); + double dl0 = l0->GetBinContent(ib0, ib1); + double da = A->GetBinContent(ib0, ib1); + double dz = Z->GetBinContent(ib0, ib1); + double drho = rho->GetBinContent(ib0, ib1); + // Create material properties + materialMatrix[ib1 - 1][ib0 - 1] = + Acts::MaterialProperties(dx0, dl0, da, dz, drho, dt); + } + } + } + + // Now reconstruct the bin untilities + Acts::BinUtility bUtility; + for (int ib = 1; ib < n->GetNbinsX() + 1; ++ib) { + size_t nbins = size_t(n->GetBinContent(ib)); + Acts::BinningValue val = Acts::BinningValue(v->GetBinContent(ib)); + Acts::BinningOption opt = Acts::BinningOption(o->GetBinContent(ib)); + float rmin = min->GetBinContent(ib); + float rmax = max->GetBinContent(ib); + bUtility += Acts::BinUtility(nbins, rmin, rmax, opt, val); + } + ACTS_VERBOSE("Created " << bUtility); + + // Construct the binned material with the right bin utility + sMaterial = std::make_shared<const Acts::BinnedSurfaceMaterial>( + bUtility, std::move(materialMatrix)); + + } else { + // Only homogeneous material present + double dt = t->GetBinContent(1, 1); + double dx0 = x0->GetBinContent(1, 1); + double dl0 = l0->GetBinContent(1, 1); + double da = A->GetBinContent(1, 1); + double dz = Z->GetBinContent(1, 1); + double drho = rho->GetBinContent(1, 1); + // Create and set the homogenous surface material + sMaterial = std::make_shared<const Acts::HomogeneousSurfaceMaterial>( + Acts::MaterialProperties(dx0, dl0, da, dz, drho, dt)); + } + } + ACTS_VERBOSE("Successfully read Material for : " << geoID); + + // Insert into the new collection + m_surfaceMaterialMap.insert({geoID, std::move(sMaterial)}); + } +} + +FW::RootMaterialDecorator::~RootMaterialDecorator() { + m_inputFile->Close(); +} diff --git a/Io/Root/src/RootMaterialTrackReader.cpp b/Io/Root/src/RootMaterialTrackReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..03b3c400b6b6a9a696f82057ab83d4e91d6af455 --- /dev/null +++ b/Io/Root/src/RootMaterialTrackReader.cpp @@ -0,0 +1,134 @@ +// 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/RootMaterialTrackReader.hpp" + +#include <TChain.h> +#include <TFile.h> +#include <iostream> + +#include "ACTFW/Framework/WhiteBoard.hpp" + +FW::RootMaterialTrackReader::RootMaterialTrackReader( + const FW::RootMaterialTrackReader::Config& cfg) + : FW::IReader(), m_cfg(cfg), m_events(0), m_inputChain(nullptr) { + m_inputChain = new TChain(m_cfg.treeName.c_str()); + + // Set the branches + m_inputChain->SetBranchAddress("v_x", &m_v_x); + m_inputChain->SetBranchAddress("v_y", &m_v_y); + m_inputChain->SetBranchAddress("v_z", &m_v_z); + m_inputChain->SetBranchAddress("v_px", &m_v_px); + m_inputChain->SetBranchAddress("v_py", &m_v_py); + m_inputChain->SetBranchAddress("v_pz", &m_v_pz); + m_inputChain->SetBranchAddress("v_phi", &m_v_phi); + m_inputChain->SetBranchAddress("v_eta", &m_v_eta); + m_inputChain->SetBranchAddress("t_X0", &m_tX0); + m_inputChain->SetBranchAddress("t_L0", &m_tL0); + m_inputChain->SetBranchAddress("mat_x", &m_step_x); + m_inputChain->SetBranchAddress("mat_y", &m_step_y); + m_inputChain->SetBranchAddress("mat_z", &m_step_z); + m_inputChain->SetBranchAddress("mat_dx", &m_step_dx); + m_inputChain->SetBranchAddress("mat_dy", &m_step_dy); + m_inputChain->SetBranchAddress("mat_dz", &m_step_dz); + m_inputChain->SetBranchAddress("mat_step_length", &m_step_length); + m_inputChain->SetBranchAddress("mat_X0", &m_step_X0); + m_inputChain->SetBranchAddress("mat_L0", &m_step_L0); + m_inputChain->SetBranchAddress("mat_A", &m_step_A); + m_inputChain->SetBranchAddress("mat_Z", &m_step_Z); + m_inputChain->SetBranchAddress("mat_rho", &m_step_rho); + + // loop over the input files + for (auto inputFile : m_cfg.fileList) { + // add file to the input chain + m_inputChain->Add(inputFile.c_str()); + ACTS_DEBUG("Adding File " << inputFile << " to tree '" << m_cfg.treeName + << "'."); + } + + m_events = m_inputChain->GetEntries(); + ACTS_DEBUG("The full chain has " << m_events << " entries."); +} + +FW::RootMaterialTrackReader::~RootMaterialTrackReader() { + delete m_step_x; + delete m_step_y; + delete m_step_z; + delete m_step_length; + delete m_step_X0; + delete m_step_L0; + delete m_step_A; + delete m_step_Z; + delete m_step_rho; +} + +std::string FW::RootMaterialTrackReader::name() const { + return m_cfg.name; +} + +std::pair<size_t, size_t> FW::RootMaterialTrackReader::availableEvents() const { + return {0u, m_events}; +} + +FW::ProcessCode FW::RootMaterialTrackReader::read( + const FW::AlgorithmContext& context) { + ACTS_DEBUG("Trying to read recorded material from tracks."); + // read in the material track + if (m_inputChain && context.eventNumber < m_events) { + // lock the mutex + std::lock_guard<std::mutex> lock(m_read_mutex); + // now read + + // The collection to be written + std::vector<Acts::RecordedMaterialTrack> mtrackCollection; + + for (size_t ib = 0; ib < m_cfg.batchSize; ++ib) { + // Read the correct entry: batch size * event_number + ib + m_inputChain->GetEntry(m_cfg.batchSize * context.eventNumber + ib); + ACTS_VERBOSE("Reading entry: " << m_cfg.batchSize * context.eventNumber + + ib); + + Acts::RecordedMaterialTrack rmTrack; + // Fill the position and momentum + rmTrack.first.first = Acts::Vector3D(m_v_x, m_v_y, m_v_z); + rmTrack.first.second = Acts::Vector3D(m_v_px, m_v_py, m_v_pz); + + // Fill the individual steps + size_t msteps = m_step_length->size(); + ACTS_VERBOSE("Reading " << msteps << " material steps."); + rmTrack.second.materialInteractions.reserve(msteps); + rmTrack.second.materialInX0 = 0.; + rmTrack.second.materialInL0 = 0.; + + for (size_t is = 0; is < msteps; ++is) { + double mX0 = (*m_step_X0)[is]; + double mL0 = (*m_step_L0)[is]; + double s = (*m_step_length)[is]; + + rmTrack.second.materialInX0 += s / mX0; + rmTrack.second.materialInL0 += s / mL0; + + /// Fill the position & the material + Acts::MaterialInteraction mInteraction; + mInteraction.position = + Acts::Vector3D((*m_step_x)[is], (*m_step_y)[is], (*m_step_z)[is]); + mInteraction.direction = Acts::Vector3D( + (*m_step_dx)[is], (*m_step_dy)[is], (*m_step_dz)[is]); + mInteraction.materialProperties = Acts::MaterialProperties( + mX0, mL0, (*m_step_A)[is], (*m_step_Z)[is], (*m_step_rho)[is], s); + rmTrack.second.materialInteractions.push_back(std::move(mInteraction)); + } + mtrackCollection.push_back(std::move(rmTrack)); + } + + // Write to the collection to the EventStore + context.eventStore.add(m_cfg.collection, std::move(mtrackCollection)); + } + // Return success flag + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootMaterialTrackWriter.cpp b/Io/Root/src/RootMaterialTrackWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b78744461ed429a749ae14efd1e576235806cd4 --- /dev/null +++ b/Io/Root/src/RootMaterialTrackWriter.cpp @@ -0,0 +1,280 @@ +// 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/RootMaterialTrackWriter.hpp" + +#include <Acts/Geometry/GeometryID.hpp> +#include <Acts/Surfaces/CylinderBounds.hpp> +#include <Acts/Surfaces/RadialBounds.hpp> +#include <Acts/Utilities/Helpers.hpp> +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <iostream> +#include <stdexcept> + +using Acts::VectorHelpers::eta; +using Acts::VectorHelpers::perp; +using Acts::VectorHelpers::phi; + +FW::RootMaterialTrackWriter::RootMaterialTrackWriter( + const FW::RootMaterialTrackWriter::Config& cfg, Acts::Logging::Level level) + : WriterT(cfg.collection, "RootMaterialTrackWriter", level), + m_cfg(cfg), + m_outputFile(cfg.rootFile) { + // An input collection name and tree name must be specified + if (m_cfg.collection.empty()) { + throw std::invalid_argument("Missing input collection"); + } else if (m_cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } + + // Setup ROOT I/O + if (m_outputFile == nullptr) { + 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(), "TTree from RootMaterialTrackWriter"); + if (m_outputTree == nullptr) + throw std::bad_alloc(); + + // Set the branches + m_outputTree->Branch("v_x", &m_v_x); + m_outputTree->Branch("v_y", &m_v_y); + m_outputTree->Branch("v_z", &m_v_z); + m_outputTree->Branch("v_px", &m_v_px); + m_outputTree->Branch("v_py", &m_v_py); + m_outputTree->Branch("v_pz", &m_v_pz); + m_outputTree->Branch("v_phi", &m_v_phi); + m_outputTree->Branch("v_eta", &m_v_eta); + m_outputTree->Branch("t_X0", &m_tX0); + m_outputTree->Branch("t_L0", &m_tL0); + m_outputTree->Branch("mat_x", &m_step_x); + m_outputTree->Branch("mat_y", &m_step_y); + m_outputTree->Branch("mat_z", &m_step_z); + m_outputTree->Branch("mat_dx", &m_step_dx); + m_outputTree->Branch("mat_dy", &m_step_dy); + m_outputTree->Branch("mat_dz", &m_step_dz); + m_outputTree->Branch("mat_step_length", &m_step_length); + m_outputTree->Branch("mat_X0", &m_step_X0); + m_outputTree->Branch("mat_L0", &m_step_L0); + m_outputTree->Branch("mat_A", &m_step_A); + m_outputTree->Branch("mat_Z", &m_step_Z); + m_outputTree->Branch("mat_rho", &m_step_rho); + + if (m_cfg.prePostStep) { + m_outputTree->Branch("mat_sx", &m_step_sx); + m_outputTree->Branch("mat_sy", &m_step_sy); + m_outputTree->Branch("mat_sz", &m_step_sz); + m_outputTree->Branch("mat_ex", &m_step_ex); + m_outputTree->Branch("mat_ey", &m_step_ey); + m_outputTree->Branch("mat_ez", &m_step_ez); + } + if (m_cfg.storesurface) { + m_outputTree->Branch("sur_id", &m_sur_id); + m_outputTree->Branch("sur_type", &m_sur_type); + m_outputTree->Branch("sur_x", &m_sur_x); + m_outputTree->Branch("sur_y", &m_sur_y); + m_outputTree->Branch("sur_z", &m_sur_z); + m_outputTree->Branch("sur_range_min", &m_sur_range_min); + m_outputTree->Branch("sur_range_max", &m_sur_range_max); + } +} + +FW::RootMaterialTrackWriter::~RootMaterialTrackWriter() { + m_outputFile->Close(); +} + +FW::ProcessCode FW::RootMaterialTrackWriter::endRun() { + // write the tree and close the file + ACTS_INFO("Writing ROOT output File : " << m_cfg.filePath); + m_outputFile->cd(); + m_outputTree->Write(); + return FW::ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::RootMaterialTrackWriter::writeT( + const AlgorithmContext& ctx, + const std::vector<Acts::RecordedMaterialTrack>& materialTracks) { + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + // Loop over the material tracks and write them out + for (auto& mtrack : materialTracks) { + // Clearing the vector first + m_step_sx.clear(); + m_step_sy.clear(); + m_step_sz.clear(); + m_step_x.clear(); + m_step_y.clear(); + m_step_z.clear(); + m_step_ex.clear(); + m_step_ey.clear(); + m_step_ez.clear(); + m_step_dx.clear(); + m_step_dy.clear(); + m_step_dz.clear(); + m_step_length.clear(); + m_step_X0.clear(); + m_step_L0.clear(); + m_step_A.clear(); + m_step_Z.clear(); + m_step_rho.clear(); + + m_sur_id.clear(); + m_sur_type.clear(); + m_sur_x.clear(); + m_sur_y.clear(); + m_sur_z.clear(); + m_sur_range_min.clear(); + m_sur_range_max.clear(); + + // Reserve the vector then + size_t mints = mtrack.second.materialInteractions.size(); + m_step_sx.reserve(mints); + m_step_sy.reserve(mints); + m_step_sz.reserve(mints); + m_step_x.reserve(mints); + m_step_y.reserve(mints); + m_step_z.reserve(mints); + m_step_ex.reserve(mints); + m_step_ey.reserve(mints); + m_step_ez.reserve(mints); + m_step_dx.reserve(mints); + m_step_dy.reserve(mints); + m_step_dz.reserve(mints); + m_step_length.reserve(mints); + m_step_X0.reserve(mints); + m_step_L0.reserve(mints); + m_step_A.reserve(mints); + m_step_Z.reserve(mints); + m_step_rho.reserve(mints); + + m_sur_id.reserve(mints); + m_sur_type.reserve(mints); + m_sur_x.reserve(mints); + m_sur_y.reserve(mints); + m_sur_z.reserve(mints); + m_sur_range_min.reserve(mints); + m_sur_range_max.reserve(mints); + + // reset the global counter + if (m_cfg.recalculateTotals) { + m_tX0 = 0.; + m_tL0 = 0.; + } else { + m_tX0 = mtrack.second.materialInX0; + m_tL0 = mtrack.second.materialInL0; + } + + // set the track information at vertex + m_v_x = mtrack.first.first.x(); + m_v_y = mtrack.first.first.y(); + m_v_z = mtrack.first.first.z(); + m_v_px = mtrack.first.second.x(); + m_v_py = mtrack.first.second.y(); + m_v_pz = mtrack.first.second.z(); + m_v_phi = phi(mtrack.first.second); + m_v_eta = eta(mtrack.first.second); + + // an now loop over the material + for (auto& mint : mtrack.second.materialInteractions) { + // The material step position information + m_step_x.push_back(mint.position.x()); + m_step_y.push_back(mint.position.y()); + m_step_z.push_back(mint.position.z()); + m_step_dx.push_back(mint.direction.x()); + m_step_dy.push_back(mint.direction.y()); + m_step_dz.push_back(mint.direction.z()); + + if (m_cfg.prePostStep) { + Acts::Vector3D prePos = + mint.position - 0.5 * mint.pathCorrection * mint.direction; + Acts::Vector3D posPos = + mint.position + 0.5 * mint.pathCorrection * mint.direction; + m_step_sx.push_back(prePos.x()); + m_step_sy.push_back(prePos.y()); + m_step_sz.push_back(prePos.z()); + m_step_ex.push_back(posPos.x()); + m_step_ey.push_back(posPos.y()); + m_step_ez.push_back(posPos.z()); + } + + if (m_cfg.storesurface) { + const Acts::Surface* surface = mint.surface; + Acts::GeometryID layerID; + if (surface) { + Acts::Intersection intersection = surface->intersectionEstimate( + ctx.geoContext, mint.position, mint.direction, true); + layerID = surface->geoID(); + m_sur_id.push_back(layerID.value()); + m_sur_type.push_back(surface->type()); + m_sur_x.push_back(intersection.position.x()); + m_sur_y.push_back(intersection.position.y()); + m_sur_z.push_back(intersection.position.z()); + + const Acts::SurfaceBounds& surfaceBounds = surface->bounds(); + const Acts::RadialBounds* radialBounds = + dynamic_cast<const Acts::RadialBounds*>(&surfaceBounds); + const Acts::CylinderBounds* cylinderBounds = + dynamic_cast<const Acts::CylinderBounds*>(&surfaceBounds); + + if (radialBounds) { + m_sur_range_min.push_back(radialBounds->rMin()); + m_sur_range_max.push_back(radialBounds->rMax()); + } else if (cylinderBounds) { + m_sur_range_min.push_back( + -cylinderBounds->get(Acts::CylinderBounds::eHalfLengthZ)); + m_sur_range_max.push_back( + cylinderBounds->get(Acts::CylinderBounds::eHalfLengthZ)); + } else { + m_sur_range_min.push_back(0); + m_sur_range_max.push_back(0); + } + } else { + layerID.setVolume(0); + layerID.setBoundary(0); + layerID.setLayer(0); + layerID.setApproach(0); + layerID.setSensitive(0); + m_sur_id.push_back(layerID.value()); + m_sur_type.push_back(-1); + + m_sur_x.push_back(0); + m_sur_y.push_back(0); + m_sur_z.push_back(0); + m_sur_range_min.push_back(0); + m_sur_range_max.push_back(0); + } + } + + // the material information + const auto& mprops = mint.materialProperties; + m_step_length.push_back(mprops.thickness()); + m_step_X0.push_back(mprops.material().X0()); + m_step_L0.push_back(mprops.material().L0()); + m_step_A.push_back(mprops.material().Ar()); + m_step_Z.push_back(mprops.material().Z()); + m_step_rho.push_back(mprops.material().massDensity()); + // re-calculate if defined to do so + if (m_cfg.recalculateTotals) { + m_tX0 += mprops.thicknessInX0(); + m_tL0 += mprops.thicknessInL0(); + } + } + // write to + m_outputTree->Fill(); + } + + // return success + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootMaterialWriter.cpp b/Io/Root/src/RootMaterialWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9aca015c8e7cb5d0774ead5c0607133f43296dbd --- /dev/null +++ b/Io/Root/src/RootMaterialWriter.cpp @@ -0,0 +1,235 @@ +// 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/RootMaterialWriter.hpp" + +#include <Acts/Geometry/GeometryID.hpp> +#include <Acts/Material/BinnedSurfaceMaterial.hpp> +#include <TFile.h> +#include <TH2F.h> +#include <ios> +#include <iostream> +#include <stdexcept> + +FW::RootMaterialWriter::RootMaterialWriter( + const FW::RootMaterialWriter::Config& cfg) + : m_cfg(cfg) { + // Validate the configuration + if (m_cfg.folderNameBase.empty()) { + throw std::invalid_argument("Missing folder name base"); + } else if (m_cfg.fileName.empty()) { + throw std::invalid_argument("Missing file name"); + } else if (!m_cfg.logger) { + throw std::invalid_argument("Missing logger"); + } else if (m_cfg.name.empty()) { + throw std::invalid_argument("Missing service name"); + } +} + +void FW::RootMaterialWriter::write( + const Acts::DetectorMaterialMaps& detMaterial) { + // Setup ROOT I/O + TFile* outputFile = TFile::Open(m_cfg.fileName.c_str(), "recreate"); + if (!outputFile) { + throw std::ios_base::failure("Could not open '" + m_cfg.fileName); + } + + // Change to the output file + outputFile->cd(); + + auto& surfaceMaps = detMaterial.first; + for (auto& [key, value] : surfaceMaps) { + // Get the Surface material + const Acts::ISurfaceMaterial* sMaterial = value.get(); + + // get the geometry ID + Acts::GeometryID geoID = key; + // decode the geometryID + const auto gvolID = geoID.volume(); + const auto gbouID = geoID.boundary(); + const auto glayID = geoID.layer(); + const auto gappID = geoID.approach(); + const auto gsenID = geoID.sensitive(); + // create the directory + std::string tdName = m_cfg.folderNameBase.c_str(); + tdName += m_cfg.voltag + std::to_string(gvolID); + tdName += m_cfg.boutag + std::to_string(gbouID); + tdName += m_cfg.laytag + std::to_string(glayID); + tdName += m_cfg.apptag + std::to_string(gappID); + tdName += m_cfg.sentag + std::to_string(gsenID); + // create a new directory + outputFile->mkdir(tdName.c_str()); + outputFile->cd(tdName.c_str()); + + ACTS_VERBOSE("Writing out map at " << tdName); + + size_t bins0 = 1, bins1 = 1; + // understand what sort of material you have in mind + const Acts::BinnedSurfaceMaterial* bsm = + dynamic_cast<const Acts::BinnedSurfaceMaterial*>(sMaterial); + if (bsm) { + // overwrite the bin numbers + bins0 = bsm->binUtility().bins(0); + bins1 = bsm->binUtility().bins(1); + + // Get the binning data + auto& binningData = bsm->binUtility().binningData(); + // 1-D or 2-D maps + size_t binningBins = binningData.size(); + + // The bin number information + TH1F* n = new TH1F(m_cfg.ntag.c_str(), "bins; bin", binningBins, -0.5, + binningBins - 0.5); + + // The binning value information + TH1F* v = new TH1F(m_cfg.vtag.c_str(), "binning values; bin", binningBins, + -0.5, binningBins - 0.5); + + // The binning option information + TH1F* o = new TH1F(m_cfg.otag.c_str(), "binning options; bin", + binningBins, -0.5, binningBins - 0.5); + + // The binning option information + TH1F* min = new TH1F(m_cfg.mintag.c_str(), "min; bin", binningBins, -0.5, + binningBins - 0.5); + + // The binning option information + TH1F* max = new TH1F(m_cfg.maxtag.c_str(), "max; bin", binningBins, -0.5, + binningBins - 0.5); + + // Now fill the histogram content + size_t b = 1; + for (auto bData : binningData) { + // Fill: nbins, value, option, min, max + n->SetBinContent(b, int(binningData[b - 1].bins())); + v->SetBinContent(b, int(binningData[b - 1].binvalue)); + o->SetBinContent(b, int(binningData[b - 1].option)); + min->SetBinContent(b, binningData[b - 1].min); + max->SetBinContent(b, binningData[b - 1].max); + ++b; + } + n->Write(); + v->Write(); + o->Write(); + min->Write(); + max->Write(); + } + + TH2F* t = new TH2F(m_cfg.ttag.c_str(), "thickness [mm] ;b0 ;b1", bins0, + -0.5, bins0 - 0.5, bins1, -0.5, bins1 - 0.5); + TH2F* x0 = new TH2F(m_cfg.x0tag.c_str(), "X_{0} [mm] ;b0 ;b1", bins0, -0.5, + bins0 - 0.5, bins1, -0.5, bins1 - 0.5); + TH2F* l0 = new TH2F(m_cfg.l0tag.c_str(), "#Lambda_{0} [mm] ;b0 ;b1", bins0, + -0.5, bins0 - 0.5, bins1, -0.5, bins1 - 0.5); + TH2F* A = new TH2F(m_cfg.atag.c_str(), "X_{0} [mm] ;b0 ;b1", bins0, -0.5, + bins0 - 0.5, bins1, -0.5, bins1 - 0.5); + TH2F* Z = new TH2F(m_cfg.ztag.c_str(), "#Lambda_{0} [mm] ;b0 ;b1", bins0, + -0.5, bins0 - 0.5, bins1, -0.5, bins1 - 0.5); + TH2F* rho = new TH2F(m_cfg.rhotag.c_str(), "#rho [g/mm^3] ;b0 ;b1", bins0, + -0.5, bins0 - 0.5, bins1, -0.5, bins1 - 0.5); + + // loop over the material and fill + for (size_t b0 = 0; b0 < bins0; ++b0) { + for (size_t b1 = 0; b1 < bins1; ++b1) { + // get the material for the bin + auto& mat = sMaterial->materialProperties(b0, b1); + if (mat) { + t->SetBinContent(b0 + 1, b1 + 1, mat.thickness()); + x0->SetBinContent(b0 + 1, b1 + 1, mat.material().X0()); + l0->SetBinContent(b0 + 1, b1 + 1, mat.material().L0()); + A->SetBinContent(b0 + 1, b1 + 1, mat.material().Ar()); + Z->SetBinContent(b0 + 1, b1 + 1, mat.material().Z()); + rho->SetBinContent(b0 + 1, b1 + 1, mat.material().massDensity()); + } + } + } + t->Write(); + x0->Write(); + l0->Write(); + A->Write(); + Z->Write(); + rho->Write(); + } + + outputFile->Close(); +} + +void FW::RootMaterialWriter::write(const Acts::TrackingGeometry& tGeometry) { + // Create a detector material map and loop recursively through it + Acts::DetectorMaterialMaps detMatMap; + auto hVolume = tGeometry.highestTrackingVolume(); + if (hVolume != nullptr) { + collectMaterial(*hVolume, detMatMap); + } + // Write the resulting map to the file + write(detMatMap); +} + +void FW::RootMaterialWriter::collectMaterial( + const Acts::TrackingVolume& tVolume, + Acts::DetectorMaterialMaps& detMatMap) { + // If the volume has volume material, write that + if (tVolume.volumeMaterialSharedPtr() != nullptr and m_cfg.processVolumes) { + detMatMap.second[tVolume.geoID()] = tVolume.volumeMaterialSharedPtr(); + } + + // If confined layers exist, loop over them and collect the layer material + if (tVolume.confinedLayers() != nullptr) { + for (auto& lay : tVolume.confinedLayers()->arrayObjects()) { + collectMaterial(*lay, detMatMap); + } + } + + // If any of the boundary surfaces has material collect that + if (m_cfg.processBoundaries) { + for (auto& bou : tVolume.boundarySurfaces()) { + const auto& bSurface = bou->surfaceRepresentation(); + if (bSurface.surfaceMaterialSharedPtr() != nullptr) { + detMatMap.first[bSurface.geoID()] = bSurface.surfaceMaterialSharedPtr(); + } + } + } + + // If the volume has sub volumes, step down + if (tVolume.confinedVolumes() != nullptr) { + for (auto& tvol : tVolume.confinedVolumes()->arrayObjects()) { + collectMaterial(*tvol, detMatMap); + } + } +} + +void FW::RootMaterialWriter::collectMaterial( + const Acts::Layer& tLayer, Acts::DetectorMaterialMaps& detMatMap) { + // If the representing surface has material, collect it + const auto& rSurface = tLayer.surfaceRepresentation(); + if (rSurface.surfaceMaterialSharedPtr() != nullptr and + m_cfg.processRepresenting) { + detMatMap.first[rSurface.geoID()] = rSurface.surfaceMaterialSharedPtr(); + } + + // Check the approach surfaces + if (tLayer.approachDescriptor() != nullptr and m_cfg.processApproaches) { + for (auto& aSurface : tLayer.approachDescriptor()->containedSurfaces()) { + if (aSurface->surfaceMaterialSharedPtr() != nullptr) { + detMatMap.first[aSurface->geoID()] = + aSurface->surfaceMaterialSharedPtr(); + } + } + } + + // Check the sensitive surfaces + if (tLayer.surfaceArray() != nullptr and m_cfg.processSensitives) { + // sensitive surface loop + for (auto& sSurface : tLayer.surfaceArray()->surfaces()) { + if (sSurface->surfaceMaterialSharedPtr() != nullptr) { + detMatMap.first[sSurface->geoID()] = + sSurface->surfaceMaterialSharedPtr(); + } + } + } +} diff --git a/Io/Root/src/RootParticleWriter.cpp b/Io/Root/src/RootParticleWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1b2b6ef05ce168be70117cbfbe57c69afa03f85 --- /dev/null +++ b/Io/Root/src/RootParticleWriter.cpp @@ -0,0 +1,123 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/RootParticleWriter.hpp" + +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <stdexcept> + +#include "Acts/Utilities/Helpers.hpp" +#include "Acts/Utilities/Units.hpp" + +FW::RootParticleWriter::RootParticleWriter( + const FW::RootParticleWriter::Config& cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputParticles, "RootParticleWriter", 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("particle_id", &m_particleId, "particle_id/l"); + m_outputTree->Branch("particle_type", &m_particleType); + m_outputTree->Branch("process", &m_process); + m_outputTree->Branch("vx", &m_vx); + m_outputTree->Branch("vy", &m_vy); + m_outputTree->Branch("vz", &m_vz); + m_outputTree->Branch("vt", &m_vt); + m_outputTree->Branch("px", &m_px); + m_outputTree->Branch("py", &m_py); + m_outputTree->Branch("pz", &m_pz); + m_outputTree->Branch("m", &m_m); + m_outputTree->Branch("q", &m_q); + m_outputTree->Branch("eta", &m_eta); + m_outputTree->Branch("phi", &m_phi); + m_outputTree->Branch("pt", &m_pt); + m_outputTree->Branch("vertex_primary", &m_vertexPrimary); + m_outputTree->Branch("vertex_secondary", &m_vertexSecondary); + m_outputTree->Branch("particle", &m_particle); + m_outputTree->Branch("generation", &m_generation); + m_outputTree->Branch("sub_particle", &m_subParticle); +} + +FW::RootParticleWriter::~RootParticleWriter() { + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::RootParticleWriter::endRun() { + if (m_outputFile) { + m_outputFile->cd(); + m_outputTree->Write(); + ACTS_INFO("Wrote particles to tree '" << m_cfg.treeName << "' in '" + << m_cfg.filePath << "'"); + } + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::RootParticleWriter::writeT( + const AlgorithmContext& ctx, const SimParticleContainer& particles) { + 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); + + m_eventId = ctx.eventNumber; + for (const auto& particle : particles) { + m_particleId = particle.particleId().value(); + m_particleType = particle.pdg(); + m_process = static_cast<decltype(m_process)>(particle.process()); + // position + m_vx = particle.position4().x() / Acts::UnitConstants::mm; + m_vy = particle.position4().y() / Acts::UnitConstants::mm; + m_vz = particle.position4().z() / Acts::UnitConstants::mm; + m_vt = particle.position4().w() / Acts::UnitConstants::ns; + // momentum + const auto p = particle.absMomentum() / Acts::UnitConstants::GeV; + m_px = p * particle.unitDirection().x(); + m_py = p * particle.unitDirection().y(); + m_pz = p * particle.unitDirection().z(); + // particle constants + m_m = particle.mass() / Acts::UnitConstants::GeV; + m_q = particle.charge() / Acts::UnitConstants::e; + // derived kinematic quantities + m_eta = Acts::VectorHelpers::eta(particle.unitDirection()); + m_phi = Acts::VectorHelpers::phi(particle.unitDirection()); + m_pt = p * Acts::VectorHelpers::perp(particle.unitDirection()); + // decoded barcode components + m_vertexPrimary = particle.particleId().vertexPrimary(); + m_vertexSecondary = particle.particleId().vertexSecondary(); + m_particle = particle.particleId().particle(); + m_generation = particle.particleId().generation(); + m_subParticle = particle.particleId().subParticle(); + m_outputTree->Fill(); + } + + return ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootPlanarClusterWriter.cpp b/Io/Root/src/RootPlanarClusterWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af609d966ec44b53f62127d134c15b433b38ad21 --- /dev/null +++ b/Io/Root/src/RootPlanarClusterWriter.cpp @@ -0,0 +1,195 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/RootPlanarClusterWriter.hpp" + +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <stdexcept> + +#include "ACTFW/EventData/SimHit.hpp" +#include "ACTFW/EventData/SimIdentifier.hpp" +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Framework/WhiteBoard.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "Acts/Plugins/Digitization/DigitizationModule.hpp" +#include "Acts/Plugins/Digitization/PlanarModuleCluster.hpp" +#include "Acts/Plugins/Digitization/Segmentation.hpp" +#include "Acts/Plugins/Identification/IdentifiedDetectorElement.hpp" +#include "Acts/Utilities/Units.hpp" + +FW::RootPlanarClusterWriter::RootPlanarClusterWriter( + const FW::RootPlanarClusterWriter::Config& cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputClusters, "RootPlanarClusterWriter", lvl), + m_cfg(cfg), + m_outputFile(cfg.rootFile) { + // inputClusters is already checked by base constructor + if (m_cfg.inputSimulatedHits.empty()) { + throw std::invalid_argument("Missing simulated hits input collection"); + } + if (m_cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } + // Setup ROOT I/O + if (m_outputFile == nullptr) { + 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(), "TTree from RootPlanarClusterWriter"); + if (m_outputTree == nullptr) + throw std::bad_alloc(); + + // Set the branches + m_outputTree->Branch("event_nr", &m_eventNr); + m_outputTree->Branch("volume_id", &m_volumeID); + m_outputTree->Branch("layer_id", &m_layerID); + m_outputTree->Branch("surface_id", &m_surfaceID); + m_outputTree->Branch("g_x", &m_x); + m_outputTree->Branch("g_y", &m_y); + m_outputTree->Branch("g_z", &m_z); + m_outputTree->Branch("g_t", &m_t); + m_outputTree->Branch("l_x", &m_lx); + m_outputTree->Branch("l_y", &m_ly); + m_outputTree->Branch("cov_l_x", &m_cov_lx); + m_outputTree->Branch("cov_l_y", &m_cov_ly); + m_outputTree->Branch("cell_ID_x", &m_cell_IDx); + m_outputTree->Branch("cell_ID_y", &m_cell_IDy); + m_outputTree->Branch("cell_l_x", &m_cell_lx); + m_outputTree->Branch("cell_l_y", &m_cell_ly); + m_outputTree->Branch("cell_data", &m_cell_data); + m_outputTree->Branch("truth_g_x", &m_t_gx); + m_outputTree->Branch("truth_g_y", &m_t_gy); + m_outputTree->Branch("truth_g_z", &m_t_gz); + m_outputTree->Branch("truth_g_t", &m_t_gt); + m_outputTree->Branch("truth_l_x", &m_t_lx); + m_outputTree->Branch("truth_l_y", &m_t_ly); + m_outputTree->Branch("truth_barcode", &m_t_barcode, "truth_barcode/l"); +} + +FW::RootPlanarClusterWriter::~RootPlanarClusterWriter() { + /// Close the file if it's yours + if (m_cfg.rootFile == nullptr) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::RootPlanarClusterWriter::endRun() { + // Write the tree + m_outputFile->cd(); + m_outputTree->Write(); + ACTS_INFO("Wrote particles to tree '" << m_cfg.treeName << "' in '" + << m_cfg.filePath << "'"); + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::RootPlanarClusterWriter::writeT( + const AlgorithmContext& ctx, + const FW::GeometryIdMultimap<Acts::PlanarModuleCluster>& clusters) { + // retrieve simulated hits + const auto& simHits = + ctx.eventStore.get<SimHitContainer>(m_cfg.inputSimulatedHits); + + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + // Get the event number + m_eventNr = ctx.eventNumber; + + // Loop over the planar clusters in this event + for (const auto& entry : clusters) { + Acts::GeometryID geoId = entry.first; + const Acts::PlanarModuleCluster& cluster = entry.second; + // local cluster information: position, @todo coveraiance + auto parameters = cluster.parameters(); + Acts::Vector2D local(parameters[Acts::ParDef::eLOC_0], + parameters[Acts::ParDef::eLOC_1]); + + /// prepare for calculating the + Acts::Vector3D pos(0, 0, 0); + Acts::Vector3D mom(1, 1, 1); + // the cluster surface + const auto& clusterSurface = cluster.referenceSurface(); + // transform local into global position information + clusterSurface.localToGlobal(ctx.geoContext, local, mom, pos); + // identification + m_volumeID = geoId.volume(); + m_layerID = geoId.layer(); + m_surfaceID = geoId.sensitive(); + m_x = pos.x(); + m_y = pos.y(); + m_z = pos.z(); + m_t = parameters[2] / Acts::UnitConstants::ns; + m_lx = local.x(); + m_ly = local.y(); + m_cov_lx = 0.; // @todo fill in + m_cov_ly = 0.; // @todo fill in + // get the cells and run through them + const auto& cells = cluster.digitizationCells(); + auto detectorElement = dynamic_cast<const Acts::IdentifiedDetectorElement*>( + clusterSurface.associatedDetectorElement()); + for (auto& cell : cells) { + // cell identification + m_cell_IDx.push_back(cell.channel0); + m_cell_IDy.push_back(cell.channel1); + m_cell_data.push_back(cell.data); + // for more we need the digitization module + if (detectorElement && detectorElement->digitizationModule()) { + auto digitationModule = detectorElement->digitizationModule(); + const Acts::Segmentation& segmentation = + digitationModule->segmentation(); + // get the cell positions + auto cellLocalPosition = segmentation.cellPosition(cell); + m_cell_lx.push_back(cellLocalPosition.x()); + m_cell_ly.push_back(cellLocalPosition.y()); + } + } + // write hit-particle truth association + // each hit can have multiple particles, e.g. in a dense environment + for (auto idx : cluster.sourceLink().indices()) { + auto it = simHits.nth(idx); + if (it == simHits.end()) { + ACTS_FATAL("Simulation hit with index " << idx << " does not exist"); + return ProcessCode::ABORT; + } + const auto& simHit = *it; + + // local position to be calculated + Acts::Vector2D lPosition; + clusterSurface.globalToLocal(ctx.geoContext, simHit.position(), + simHit.unitDirection(), lPosition); + // fill the variables + m_t_gx.push_back(simHit.position().x()); + m_t_gy.push_back(simHit.position().y()); + m_t_gz.push_back(simHit.position().z()); + m_t_gt.push_back(simHit.time()); + m_t_lx.push_back(lPosition.x()); + m_t_ly.push_back(lPosition.y()); + m_t_barcode.push_back(simHit.particleId().value()); + } + // fill the tree + m_outputTree->Fill(); + // now reset + m_cell_IDx.clear(); + m_cell_IDy.clear(); + m_cell_lx.clear(); + m_cell_ly.clear(); + m_cell_data.clear(); + m_t_gx.clear(); + m_t_gy.clear(); + m_t_gz.clear(); + m_t_gt.clear(); + m_t_lx.clear(); + m_t_ly.clear(); + m_t_barcode.clear(); + } + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootPropagationStepsWriter.cpp b/Io/Root/src/RootPropagationStepsWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e7ba7cf7c6805a2be62de39f910dd21ab1862ac --- /dev/null +++ b/Io/Root/src/RootPropagationStepsWriter.cpp @@ -0,0 +1,181 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/RootPropagationStepsWriter.hpp" + +#include <Acts/Geometry/GeometryID.hpp> +#include <Acts/Geometry/TrackingVolume.hpp> +#include <Acts/Propagator/ConstrainedStep.hpp> +#include <Acts/Surfaces/Surface.hpp> +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <stdexcept> + +#include "ACTFW/Framework/WhiteBoard.hpp" +#include "ACTFW/Utilities/Paths.hpp" + +FW::RootPropagationStepsWriter::RootPropagationStepsWriter( + const FW::RootPropagationStepsWriter::Config& cfg, + Acts::Logging::Level level) + : WriterT(cfg.collection, "RootPropagationStepsWriter", level), + m_cfg(cfg), + m_outputFile(cfg.rootFile) { + // An input collection name and tree name must be specified + if (m_cfg.collection.empty()) { + throw std::invalid_argument("Missing input collection"); + } else if (m_cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } + + // Setup ROOT I/O + if (m_outputFile == nullptr) { + 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(), + "TTree from RootPropagationStepsWriter"); + if (m_outputTree == nullptr) + throw std::bad_alloc(); + + // Set the branches + m_outputTree->Branch("event_nr", &m_eventNr); + 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); + m_outputTree->Branch("g_x", &m_x); + m_outputTree->Branch("g_y", &m_y); + m_outputTree->Branch("g_z", &m_z); + m_outputTree->Branch("d_x", &m_dx); + m_outputTree->Branch("d_y", &m_dy); + m_outputTree->Branch("d_z", &m_dz); + m_outputTree->Branch("type", &m_step_type); + m_outputTree->Branch("step_acc", &m_step_acc); + m_outputTree->Branch("step_act", &m_step_act); + m_outputTree->Branch("step_abt", &m_step_abt); + m_outputTree->Branch("step_usr", &m_step_usr); +} + +FW::RootPropagationStepsWriter::~RootPropagationStepsWriter() { + /// Close the file if it's yours + if (m_cfg.rootFile == nullptr) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::RootPropagationStepsWriter::endRun() { + // Write the tree + m_outputFile->cd(); + m_outputTree->Write(); + ACTS_VERBOSE("Wrote particles to tree '" << m_cfg.treeName << "' in '" + << m_cfg.filePath << "'"); + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::RootPropagationStepsWriter::writeT( + const AlgorithmContext& context, + const std::vector<PropagationSteps>& stepCollection) { + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + // we get the event number + m_eventNr = context.eventNumber; + + // loop over the step vector of each test propagation in this + for (auto& steps : stepCollection) { + // clear the vectors for each collection + m_volumeID.clear(); + m_boundaryID.clear(); + m_layerID.clear(); + m_approachID.clear(); + m_sensitiveID.clear(); + m_x.clear(); + m_y.clear(); + m_z.clear(); + m_dx.clear(); + m_dy.clear(); + m_dz.clear(); + m_step_type.clear(); + m_step_acc.clear(); + m_step_act.clear(); + m_step_abt.clear(); + m_step_usr.clear(); + + // loop over single steps + for (auto& step : steps) { + // the identification of the step + Acts::GeometryID::Value volumeID = 0; + Acts::GeometryID::Value boundaryID = 0; + Acts::GeometryID::Value layerID = 0; + Acts::GeometryID::Value approachID = 0; + Acts::GeometryID::Value sensitiveID = 0; + // get the identification from the surface first + if (step.surface) { + auto geoID = step.surface->geoID(); + volumeID = geoID.volume(); + boundaryID = geoID.boundary(); + layerID = geoID.layer(); + approachID = geoID.approach(); + sensitiveID = geoID.sensitive(); + } + // a current volume overwrites the surface tagged one + if (step.volume) { + volumeID = step.volume->geoID().volume(); + } + // now fill + m_sensitiveID.push_back(sensitiveID); + m_approachID.push_back(approachID); + m_layerID.push_back(layerID); + m_boundaryID.push_back(boundaryID); + m_volumeID.push_back(volumeID); + + // kinematic information + m_x.push_back(step.position.x()); + m_y.push_back(step.position.y()); + m_z.push_back(step.position.z()); + auto direction = step.momentum.normalized(); + m_dx.push_back(direction.x()); + m_dy.push_back(direction.y()); + m_dz.push_back(direction.z()); + + double accuracy = step.stepSize.value(Acts::ConstrainedStep::accuracy); + double actor = step.stepSize.value(Acts::ConstrainedStep::actor); + double aborter = step.stepSize.value(Acts::ConstrainedStep::aborter); + double user = step.stepSize.value(Acts::ConstrainedStep::user); + double act2 = actor * actor; + double acc2 = accuracy * accuracy; + double abo2 = aborter * aborter; + double usr2 = user * user; + + // todo - fold with direction + if (act2 < acc2 && act2 < abo2 && act2 < usr2) { + m_step_type.push_back(0); + } else if (acc2 < abo2 && acc2 < usr2) { + m_step_type.push_back(1); + } else if (abo2 < usr2) { + m_step_type.push_back(2); + } else { + m_step_type.push_back(3); + } + + // step size information + m_step_acc.push_back(accuracy); + m_step_act.push_back(actor); + m_step_abt.push_back(aborter); + m_step_usr.push_back(user); + } + m_outputTree->Fill(); + } + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootSimHitWriter.cpp b/Io/Root/src/RootSimHitWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f00004321a69869542b56f9c874f02e44a809ab8 --- /dev/null +++ b/Io/Root/src/RootSimHitWriter.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/RootSimHitWriter.hpp" + +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <stdexcept> + +#include "Acts/Utilities/Units.hpp" + +FW::RootSimHitWriter::RootSimHitWriter(const FW::RootSimHitWriter::Config& cfg, + Acts::Logging::Level lvl) + : WriterT(cfg.inputSimulatedHits, "RootSimHitWriter", 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::RootSimHitWriter::~RootSimHitWriter() { + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::RootSimHitWriter::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::RootSimHitWriter::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; +} diff --git a/Io/Root/src/RootTrackParameterWriter.cpp b/Io/Root/src/RootTrackParameterWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6b3a3e46d62fa5c9b56270367d9c5c8ec0e0a81 --- /dev/null +++ b/Io/Root/src/RootTrackParameterWriter.cpp @@ -0,0 +1,92 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2017 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/RootTrackParameterWriter.hpp" + +#include <Acts/Utilities/Helpers.hpp> +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <iostream> +#include <stdexcept> + +FW::RootTrackParameterWriter::RootTrackParameterWriter( + const FW::RootTrackParameterWriter::Config& cfg, Acts::Logging::Level level) + : TrackParameterWriter(cfg.collection, "RootTrackParameterWriter", level), + m_cfg(cfg), + m_outputFile(cfg.rootFile) { + // An input collection name and tree name must be specified + if (m_cfg.collection.empty()) { + throw std::invalid_argument("Missing input collection"); + } else if (m_cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } + + // Setup ROOT I/O + if (m_outputFile == nullptr) { + 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(); + else { + // I/O parameters + m_outputTree->Branch("event_nr", &m_eventNr); + m_outputTree->Branch("d0", &m_d0); + m_outputTree->Branch("z0", &m_z0); + m_outputTree->Branch("phi", &m_phi); + m_outputTree->Branch("theta", &m_theta); + m_outputTree->Branch("qp", &m_qp); + // MORE HERE + } +} + +FW::RootTrackParameterWriter::~RootTrackParameterWriter() { + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::RootTrackParameterWriter::endRun() { + if (m_outputFile) { + m_outputFile->cd(); + m_outputTree->Write(); + ACTS_INFO("Wrote trackparameters to tree '" << m_cfg.treeName << "' in '" + << m_cfg.filePath << "'"); + } + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::RootTrackParameterWriter::writeT( + const FW::AlgorithmContext& ctx, + const std::vector<BoundTrackParameters>& trackParams) { + if (m_outputFile == nullptr) + return ProcessCode::SUCCESS; + + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + // Get the event number + m_eventNr = ctx.eventNumber; + + for (auto& params : trackParams) { + m_d0 = params.parameters()[0]; + m_z0 = params.parameters()[1]; + m_phi = params.parameters()[2]; + m_theta = params.parameters()[3]; + m_qp = params.parameters()[4]; + + m_outputTree->Fill(); + } + + return ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootTrajectoryWriter.cpp b/Io/Root/src/RootTrajectoryWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5be1c1246cf8171b93c0a347be43648565834af --- /dev/null +++ b/Io/Root/src/RootTrajectoryWriter.cpp @@ -0,0 +1,924 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/RootTrajectoryWriter.hpp" + +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <stdexcept> + +#include "ACTFW/EventData/SimParticle.hpp" +#include "ACTFW/Utilities/Paths.hpp" +#include "Acts/EventData/Measurement.hpp" +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/MultiTrajectoryHelpers.hpp" +#include "Acts/EventData/TrackParameters.hpp" +#include "Acts/Utilities/Helpers.hpp" + +using Acts::VectorHelpers::eta; +using Acts::VectorHelpers::perp; +using Acts::VectorHelpers::phi; +using Acts::VectorHelpers::theta; +using Measurement = Acts::Measurement<FW::SimSourceLink, Acts::ParDef::eLOC_0, + Acts::ParDef::eLOC_1>; + +FW::RootTrajectoryWriter::RootTrajectoryWriter( + const FW::RootTrajectoryWriter::Config& cfg, Acts::Logging::Level lvl) + : WriterT(cfg.inputTrajectories, "RootTrajectoryWriter", lvl), + m_cfg(cfg), + m_outputFile(cfg.rootFile) { + // An input collection name and tree name must be specified + if (m_cfg.inputTrajectories.empty()) { + throw std::invalid_argument("Missing input trajectory collection"); + } else if (m_cfg.inputParticles.empty()) { + throw std::invalid_argument("Missing input particle collection"); + } else if (cfg.outputFilename.empty()) { + throw std::invalid_argument("Missing output filename"); + } else if (m_cfg.outputTreename.empty()) { + throw std::invalid_argument("Missing tree name"); + } + + // Setup ROOT I/O + if (m_outputFile == nullptr) { + auto path = joinPaths(m_cfg.outputDir, m_cfg.outputFilename); + m_outputFile = TFile::Open(path.c_str(), m_cfg.fileMode.c_str()); + if (m_outputFile == nullptr) { + throw std::ios_base::failure("Could not open '" + path); + } + } + m_outputFile->cd(); + m_outputTree = + new TTree(m_cfg.outputTreename.c_str(), m_cfg.outputTreename.c_str()); + if (m_outputTree == nullptr) + throw std::bad_alloc(); + else { + // I/O parameters + m_outputTree->Branch("event_nr", &m_eventNr); + m_outputTree->Branch("traj_nr", &m_trajNr); + m_outputTree->Branch("t_barcode", &m_t_barcode, "t_barcode/l"); + m_outputTree->Branch("t_charge", &m_t_charge); + m_outputTree->Branch("t_time", &m_t_time); + m_outputTree->Branch("t_vx", &m_t_vx); + m_outputTree->Branch("t_vy", &m_t_vy); + m_outputTree->Branch("t_vz", &m_t_vz); + m_outputTree->Branch("t_px", &m_t_px); + m_outputTree->Branch("t_py", &m_t_py); + m_outputTree->Branch("t_pz", &m_t_pz); + m_outputTree->Branch("t_theta", &m_t_theta); + m_outputTree->Branch("t_phi", &m_t_phi); + m_outputTree->Branch("t_eta", &m_t_eta); + m_outputTree->Branch("t_pT", &m_t_pT); + + m_outputTree->Branch("t_x", &m_t_x); + m_outputTree->Branch("t_y", &m_t_y); + m_outputTree->Branch("t_z", &m_t_z); + m_outputTree->Branch("t_r", &m_t_r); + m_outputTree->Branch("t_dx", &m_t_dx); + m_outputTree->Branch("t_dy", &m_t_dy); + m_outputTree->Branch("t_dz", &m_t_dz); + m_outputTree->Branch("t_eLOC0", &m_t_eLOC0); + m_outputTree->Branch("t_eLOC1", &m_t_eLOC1); + m_outputTree->Branch("t_ePHI", &m_t_ePHI); + m_outputTree->Branch("t_eTHETA", &m_t_eTHETA); + m_outputTree->Branch("t_eQOP", &m_t_eQOP); + m_outputTree->Branch("t_eT", &m_t_eT); + + m_outputTree->Branch("nStates", &m_nStates); + m_outputTree->Branch("nMeasurements", &m_nMeasurements); + m_outputTree->Branch("volume_id", &m_volumeID); + m_outputTree->Branch("layer_id", &m_layerID); + m_outputTree->Branch("module_id", &m_moduleID); + m_outputTree->Branch("l_x_hit", &m_lx_hit); + m_outputTree->Branch("l_y_hit", &m_ly_hit); + m_outputTree->Branch("g_x_hit", &m_x_hit); + m_outputTree->Branch("g_y_hit", &m_y_hit); + m_outputTree->Branch("g_z_hit", &m_z_hit); + m_outputTree->Branch("res_x_hit", &m_res_x_hit); + m_outputTree->Branch("res_y_hit", &m_res_y_hit); + m_outputTree->Branch("err_x_hit", &m_err_x_hit); + m_outputTree->Branch("err_y_hit", &m_err_y_hit); + m_outputTree->Branch("pull_x_hit", &m_pull_x_hit); + m_outputTree->Branch("pull_y_hit", &m_pull_y_hit); + m_outputTree->Branch("dim_hit", &m_dim_hit); + + m_outputTree->Branch("hasFittedParams", &m_hasFittedParams); + m_outputTree->Branch("eLOC0_fit", &m_eLOC0_fit); + m_outputTree->Branch("eLOC1_fit", &m_eLOC1_fit); + m_outputTree->Branch("ePHI_fit", &m_ePHI_fit); + m_outputTree->Branch("eTHETA_fit", &m_eTHETA_fit); + m_outputTree->Branch("eQOP_fit", &m_eQOP_fit); + m_outputTree->Branch("eT_fit", &m_eT_fit); + m_outputTree->Branch("err_eLOC0_fit", &m_err_eLOC0_fit); + m_outputTree->Branch("err_eLOC1_fit", &m_err_eLOC1_fit); + m_outputTree->Branch("err_ePHI_fit", &m_err_ePHI_fit); + m_outputTree->Branch("err_eTHETA_fit", &m_err_eTHETA_fit); + m_outputTree->Branch("err_eQOP_fit", &m_err_eQOP_fit); + m_outputTree->Branch("err_eT_fit", &m_err_eT_fit); + + m_outputTree->Branch("nPredicted", &m_nPredicted); + m_outputTree->Branch("predicted", &m_prt); + m_outputTree->Branch("eLOC0_prt", &m_eLOC0_prt); + m_outputTree->Branch("eLOC1_prt", &m_eLOC1_prt); + m_outputTree->Branch("ePHI_prt", &m_ePHI_prt); + m_outputTree->Branch("eTHETA_prt", &m_eTHETA_prt); + m_outputTree->Branch("eQOP_prt", &m_eQOP_prt); + m_outputTree->Branch("eT_prt", &m_eT_prt); + m_outputTree->Branch("res_eLOC0_prt", &m_res_eLOC0_prt); + m_outputTree->Branch("res_eLOC1_prt", &m_res_eLOC1_prt); + m_outputTree->Branch("res_ePHI_prt", &m_res_ePHI_prt); + m_outputTree->Branch("res_eTHETA_prt", &m_res_eTHETA_prt); + m_outputTree->Branch("res_eQOP_prt", &m_res_eQOP_prt); + m_outputTree->Branch("res_eT_prt", &m_res_eT_prt); + m_outputTree->Branch("err_eLOC0_prt", &m_err_eLOC0_prt); + m_outputTree->Branch("err_eLOC1_prt", &m_err_eLOC1_prt); + m_outputTree->Branch("err_ePHI_prt", &m_err_ePHI_prt); + m_outputTree->Branch("err_eTHETA_prt", &m_err_eTHETA_prt); + m_outputTree->Branch("err_eQOP_prt", &m_err_eQOP_prt); + m_outputTree->Branch("err_eT_prt", &m_err_eT_prt); + m_outputTree->Branch("pull_eLOC0_prt", &m_pull_eLOC0_prt); + m_outputTree->Branch("pull_eLOC1_prt", &m_pull_eLOC1_prt); + m_outputTree->Branch("pull_ePHI_prt", &m_pull_ePHI_prt); + m_outputTree->Branch("pull_eTHETA_prt", &m_pull_eTHETA_prt); + m_outputTree->Branch("pull_eQOP_prt", &m_pull_eQOP_prt); + m_outputTree->Branch("pull_eT_prt", &m_pull_eT_prt); + m_outputTree->Branch("g_x_prt", &m_x_prt); + m_outputTree->Branch("g_y_prt", &m_y_prt); + m_outputTree->Branch("g_z_prt", &m_z_prt); + m_outputTree->Branch("px_prt", &m_px_prt); + m_outputTree->Branch("py_prt", &m_py_prt); + m_outputTree->Branch("pz_prt", &m_pz_prt); + m_outputTree->Branch("eta_prt", &m_eta_prt); + m_outputTree->Branch("pT_prt", &m_pT_prt); + + m_outputTree->Branch("nFiltered", &m_nFiltered); + m_outputTree->Branch("filtered", &m_flt); + m_outputTree->Branch("eLOC0_flt", &m_eLOC0_flt); + m_outputTree->Branch("eLOC1_flt", &m_eLOC1_flt); + m_outputTree->Branch("ePHI_flt", &m_ePHI_flt); + m_outputTree->Branch("eTHETA_flt", &m_eTHETA_flt); + m_outputTree->Branch("eQOP_flt", &m_eQOP_flt); + m_outputTree->Branch("eT_flt", &m_eT_flt); + m_outputTree->Branch("res_eLOC0_flt", &m_res_eLOC0_flt); + m_outputTree->Branch("res_eLOC1_flt", &m_res_eLOC1_flt); + m_outputTree->Branch("res_ePHI_flt", &m_res_ePHI_flt); + m_outputTree->Branch("res_eTHETA_flt", &m_res_eTHETA_flt); + m_outputTree->Branch("res_eQOP_flt", &m_res_eQOP_flt); + m_outputTree->Branch("res_eT_flt", &m_res_eT_flt); + m_outputTree->Branch("err_eLOC0_flt", &m_err_eLOC0_flt); + m_outputTree->Branch("err_eLOC1_flt", &m_err_eLOC1_flt); + m_outputTree->Branch("err_ePHI_flt", &m_err_ePHI_flt); + m_outputTree->Branch("err_eTHETA_flt", &m_err_eTHETA_flt); + m_outputTree->Branch("err_eQOP_flt", &m_err_eQOP_flt); + m_outputTree->Branch("err_eT_flt", &m_err_eT_flt); + m_outputTree->Branch("pull_eLOC0_flt", &m_pull_eLOC0_flt); + m_outputTree->Branch("pull_eLOC1_flt", &m_pull_eLOC1_flt); + m_outputTree->Branch("pull_ePHI_flt", &m_pull_ePHI_flt); + m_outputTree->Branch("pull_eTHETA_flt", &m_pull_eTHETA_flt); + m_outputTree->Branch("pull_eQOP_flt", &m_pull_eQOP_flt); + m_outputTree->Branch("pull_eT_flt", &m_pull_eT_flt); + m_outputTree->Branch("g_x_flt", &m_x_flt); + m_outputTree->Branch("g_y_flt", &m_y_flt); + m_outputTree->Branch("g_z_flt", &m_z_flt); + m_outputTree->Branch("px_flt", &m_px_flt); + m_outputTree->Branch("py_flt", &m_py_flt); + m_outputTree->Branch("pz_flt", &m_pz_flt); + m_outputTree->Branch("eta_flt", &m_eta_flt); + m_outputTree->Branch("pT_flt", &m_pT_flt); + m_outputTree->Branch("chi2", &m_chi2); + + m_outputTree->Branch("nSmoothed", &m_nSmoothed); + m_outputTree->Branch("smoothed", &m_smt); + m_outputTree->Branch("eLOC0_smt", &m_eLOC0_smt); + m_outputTree->Branch("eLOC1_smt", &m_eLOC1_smt); + m_outputTree->Branch("ePHI_smt", &m_ePHI_smt); + m_outputTree->Branch("eTHETA_smt", &m_eTHETA_smt); + m_outputTree->Branch("eQOP_smt", &m_eQOP_smt); + m_outputTree->Branch("eT_smt", &m_eT_smt); + m_outputTree->Branch("res_eLOC0_smt", &m_res_eLOC0_smt); + m_outputTree->Branch("res_eLOC1_smt", &m_res_eLOC1_smt); + m_outputTree->Branch("res_ePHI_smt", &m_res_ePHI_smt); + m_outputTree->Branch("res_eTHETA_smt", &m_res_eTHETA_smt); + m_outputTree->Branch("res_eQOP_smt", &m_res_eQOP_smt); + m_outputTree->Branch("res_eT_smt", &m_res_eT_smt); + m_outputTree->Branch("err_eLOC0_smt", &m_err_eLOC0_smt); + m_outputTree->Branch("err_eLOC1_smt", &m_err_eLOC1_smt); + m_outputTree->Branch("err_ePHI_smt", &m_err_ePHI_smt); + m_outputTree->Branch("err_eTHETA_smt", &m_err_eTHETA_smt); + m_outputTree->Branch("err_eQOP_smt", &m_err_eQOP_smt); + m_outputTree->Branch("err_eT_smt", &m_err_eT_smt); + m_outputTree->Branch("pull_eLOC0_smt", &m_pull_eLOC0_smt); + m_outputTree->Branch("pull_eLOC1_smt", &m_pull_eLOC1_smt); + m_outputTree->Branch("pull_ePHI_smt", &m_pull_ePHI_smt); + m_outputTree->Branch("pull_eTHETA_smt", &m_pull_eTHETA_smt); + m_outputTree->Branch("pull_eQOP_smt", &m_pull_eQOP_smt); + m_outputTree->Branch("pull_eT_smt", &m_pull_eT_smt); + m_outputTree->Branch("g_x_smt", &m_x_smt); + m_outputTree->Branch("g_y_smt", &m_y_smt); + m_outputTree->Branch("g_z_smt", &m_z_smt); + m_outputTree->Branch("px_smt", &m_px_smt); + m_outputTree->Branch("py_smt", &m_py_smt); + m_outputTree->Branch("pz_smt", &m_pz_smt); + m_outputTree->Branch("eta_smt", &m_eta_smt); + m_outputTree->Branch("pT_smt", &m_pT_smt); + } +} + +FW::RootTrajectoryWriter::~RootTrajectoryWriter() { + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::RootTrajectoryWriter::endRun() { + if (m_outputFile) { + m_outputFile->cd(); + m_outputTree->Write(); + ACTS_INFO("Write trajectories to tree '" + << m_cfg.outputTreename << "' in '" + << joinPaths(m_cfg.outputDir, m_cfg.outputFilename) << "'"); + } + return ProcessCode::SUCCESS; +} + +FW::ProcessCode FW::RootTrajectoryWriter::writeT( + const AlgorithmContext& ctx, const TrajectoryContainer& trajectories) { + if (m_outputFile == nullptr) + return ProcessCode::SUCCESS; + + auto& gctx = ctx.geoContext; + + // read truth particles from input collection + const auto& particles = + ctx.eventStore.get<SimParticleContainer>(m_cfg.inputParticles); + + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + // Get the event number + m_eventNr = ctx.eventNumber; + + // Loop over the trajectories + int iTraj = 0; + for (const auto& traj : trajectories) { + m_trajNr = iTraj; + + // The trajectory entry indices and the multiTrajectory + const auto& [trackTips, mj] = traj.trajectory(); + if (trackTips.empty()) { + ACTS_WARNING("Empty multiTrajectory."); + continue; + } + // Check the size of the trajectory entry indices. For track fitting, there + // should be at most one trajectory + if (trackTips.size() > 1) { + ACTS_ERROR("Track fitting should not result in multiple trajectories."); + return ProcessCode::ABORT; + } + // Get the entry index for the single trajectory + auto& trackTip = trackTips.front(); + + // Collect the trajectory summary info + auto trajState = + Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip); + m_nMeasurements = trajState.nMeasurements; + m_nStates = trajState.nStates; + + // Get the majority truth particle to this track + const auto particleHitCount = traj.identifyMajorityParticle(trackTip); + if (not particleHitCount.empty()) { + // Get the barcode of the majority truth particle + m_t_barcode = particleHitCount.front().particleId.value(); + // Find the truth particle via the barcode + auto ip = particles.find(m_t_barcode); + if (ip != particles.end()) { + const auto& particle = *ip; + ACTS_DEBUG("Find the truth particle with barcode = " << m_t_barcode); + // Get the truth particle info at vertex + const auto p = particle.absMomentum(); + m_t_charge = particle.charge(); + m_t_time = particle.time(); + m_t_vx = particle.position().x(); + m_t_vy = particle.position().y(); + m_t_vz = particle.position().z(); + m_t_px = p * particle.unitDirection().x(); + m_t_py = p * particle.unitDirection().y(); + m_t_pz = p * particle.unitDirection().z(); + m_t_theta = theta(particle.unitDirection()); + m_t_phi = phi(particle.unitDirection()); + m_t_eta = eta(particle.unitDirection()); + m_t_pT = p * perp(particle.unitDirection()); + } else { + ACTS_WARNING("Truth particle with barcode = " << m_t_barcode + << " not found!"); + } + } + + // Get the fitted track parameter + m_hasFittedParams = false; + if (traj.hasTrackParameters(trackTip)) { + m_hasFittedParams = true; + const auto& boundParam = traj.trackParameters(trackTip); + const auto& parameter = boundParam.parameters(); + const auto& covariance = *boundParam.covariance(); + m_eLOC0_fit = parameter[Acts::ParDef::eLOC_0]; + m_eLOC1_fit = parameter[Acts::ParDef::eLOC_1]; + m_ePHI_fit = parameter[Acts::ParDef::ePHI]; + m_eTHETA_fit = parameter[Acts::ParDef::eTHETA]; + m_eQOP_fit = parameter[Acts::ParDef::eQOP]; + m_eT_fit = parameter[Acts::ParDef::eT]; + m_err_eLOC0_fit = + sqrt(covariance(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0)); + m_err_eLOC1_fit = + sqrt(covariance(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1)); + m_err_ePHI_fit = sqrt(covariance(Acts::ParDef::ePHI, Acts::ParDef::ePHI)); + m_err_eTHETA_fit = + sqrt(covariance(Acts::ParDef::eTHETA, Acts::ParDef::eTHETA)); + m_err_eQOP_fit = sqrt(covariance(Acts::ParDef::eQOP, Acts::ParDef::eQOP)); + m_err_eT_fit = sqrt(covariance(Acts::ParDef::eT, Acts::ParDef::eT)); + } + + // Get the trackStates on the trajectory + m_nPredicted = 0; + m_nFiltered = 0; + m_nSmoothed = 0; + mj.visitBackwards(trackTip, [&](const auto& state) { + // we only fill the track states with non-outlier measurement + auto typeFlags = state.typeFlags(); + if (not typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) { + return true; + } + + // get the geometry ID + auto geoID = state.referenceSurface().geoID(); + m_volumeID.push_back(geoID.volume()); + m_layerID.push_back(geoID.layer()); + m_moduleID.push_back(geoID.sensitive()); + + auto meas = std::get<Measurement>(*state.uncalibrated()); + + // get local position + Acts::Vector2D local(meas.parameters()[Acts::ParDef::eLOC_0], + meas.parameters()[Acts::ParDef::eLOC_1]); + // get global position + Acts::Vector3D global(0, 0, 0); + Acts::Vector3D mom(1, 1, 1); + meas.referenceSurface().localToGlobal(ctx.geoContext, local, mom, global); + + // get measurement covariance + auto cov = meas.covariance(); + // float resX = sqrt(cov(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0)); + // float resY = sqrt(cov(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1)); + + // push the measurement info + m_lx_hit.push_back(local.x()); + m_ly_hit.push_back(local.y()); + m_x_hit.push_back(global.x()); + m_y_hit.push_back(global.y()); + m_z_hit.push_back(global.z()); + + // get the truth hit corresponding to this trackState + const auto& truthHit = state.uncalibrated().truthHit(); + // get local truth position + Acts::Vector2D truthlocal; + meas.referenceSurface().globalToLocal( + gctx, truthHit.position(), truthHit.unitDirection(), truthlocal); + + // push the truth hit info + m_t_x.push_back(truthHit.position().x()); + m_t_y.push_back(truthHit.position().y()); + m_t_z.push_back(truthHit.position().z()); + m_t_r.push_back(perp(truthHit.position())); + m_t_dx.push_back(truthHit.unitDirection().x()); + m_t_dy.push_back(truthHit.unitDirection().y()); + m_t_dz.push_back(truthHit.unitDirection().z()); + + // get the truth track parameter at this track State + float truthLOC0 = 0, truthLOC1 = 0, truthPHI = 0, truthTHETA = 0, + truthQOP = 0, truthTIME = 0; + truthLOC0 = truthlocal.x(); + truthLOC1 = truthlocal.y(); + truthPHI = phi(truthHit.unitDirection()); + truthTHETA = theta(truthHit.unitDirection()); + truthQOP = + m_t_charge / truthHit.momentum4Before().template head<3>().norm(); + truthTIME = truthHit.time(); + + // push the truth track parameter at this track State + m_t_eLOC0.push_back(truthLOC0); + m_t_eLOC1.push_back(truthLOC1); + m_t_ePHI.push_back(truthPHI); + m_t_eTHETA.push_back(truthTHETA); + m_t_eQOP.push_back(truthQOP); + m_t_eT.push_back(truthTIME); + + // get the predicted parameter + bool predicted = false; + if (state.hasPredicted()) { + predicted = true; + m_nPredicted++; + Acts::BoundParameters parameter( + gctx, state.predictedCovariance(), state.predicted(), + state.referenceSurface().getSharedPtr()); + auto covariance = state.predictedCovariance(); + // local hit residual info + auto H = meas.projector(); + auto resCov = cov + H * covariance * H.transpose(); + auto residual = meas.residual(parameter); + m_res_x_hit.push_back(residual(Acts::ParDef::eLOC_0)); + m_res_y_hit.push_back(residual(Acts::ParDef::eLOC_1)); + m_err_x_hit.push_back( + sqrt(resCov(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_err_y_hit.push_back( + sqrt(resCov(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_pull_x_hit.push_back( + residual(Acts::ParDef::eLOC_0) / + sqrt(resCov(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_pull_y_hit.push_back( + residual(Acts::ParDef::eLOC_1) / + sqrt(resCov(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_dim_hit.push_back(state.calibratedSize()); + + // predicted parameter + m_eLOC0_prt.push_back(parameter.parameters()[Acts::ParDef::eLOC_0]); + m_eLOC1_prt.push_back(parameter.parameters()[Acts::ParDef::eLOC_1]); + m_ePHI_prt.push_back(parameter.parameters()[Acts::ParDef::ePHI]); + m_eTHETA_prt.push_back(parameter.parameters()[Acts::ParDef::eTHETA]); + m_eQOP_prt.push_back(parameter.parameters()[Acts::ParDef::eQOP]); + m_eT_prt.push_back(parameter.parameters()[Acts::ParDef::eT]); + + // predicted residual + m_res_eLOC0_prt.push_back(parameter.parameters()[Acts::ParDef::eLOC_0] - + truthLOC0); + m_res_eLOC1_prt.push_back(parameter.parameters()[Acts::ParDef::eLOC_1] - + truthLOC1); + m_res_ePHI_prt.push_back(parameter.parameters()[Acts::ParDef::ePHI] - + truthPHI); + m_res_eTHETA_prt.push_back( + parameter.parameters()[Acts::ParDef::eTHETA] - truthTHETA); + m_res_eQOP_prt.push_back(parameter.parameters()[Acts::ParDef::eQOP] - + truthQOP); + m_res_eT_prt.push_back(parameter.parameters()[Acts::ParDef::eT] - + truthTIME); + + // predicted parameter error + m_err_eLOC0_prt.push_back( + sqrt(covariance(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_err_eLOC1_prt.push_back( + sqrt(covariance(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_err_ePHI_prt.push_back( + sqrt(covariance(Acts::ParDef::ePHI, Acts::ParDef::ePHI))); + m_err_eTHETA_prt.push_back( + sqrt(covariance(Acts::ParDef::eTHETA, Acts::ParDef::eTHETA))); + m_err_eQOP_prt.push_back( + sqrt(covariance(Acts::ParDef::eQOP, Acts::ParDef::eQOP))); + m_err_eT_prt.push_back( + sqrt(covariance(Acts::ParDef::eT, Acts::ParDef::eT))); + + // predicted parameter pull + m_pull_eLOC0_prt.push_back( + (parameter.parameters()[Acts::ParDef::eLOC_0] - truthLOC0) / + sqrt(covariance(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_pull_eLOC1_prt.push_back( + (parameter.parameters()[Acts::ParDef::eLOC_1] - truthLOC1) / + sqrt(covariance(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_pull_ePHI_prt.push_back( + (parameter.parameters()[Acts::ParDef::ePHI] - truthPHI) / + sqrt(covariance(Acts::ParDef::ePHI, Acts::ParDef::ePHI))); + m_pull_eTHETA_prt.push_back( + (parameter.parameters()[Acts::ParDef::eTHETA] - truthTHETA) / + sqrt(covariance(Acts::ParDef::eTHETA, Acts::ParDef::eTHETA))); + m_pull_eQOP_prt.push_back( + (parameter.parameters()[Acts::ParDef::eQOP] - truthQOP) / + sqrt(covariance(Acts::ParDef::eQOP, Acts::ParDef::eQOP))); + m_pull_eT_prt.push_back( + (parameter.parameters()[Acts::ParDef::eT] - truthTIME) / + sqrt(covariance(Acts::ParDef::eT, Acts::ParDef::eT))); + + // further predicted parameter info + m_x_prt.push_back(parameter.position().x()); + m_y_prt.push_back(parameter.position().y()); + m_z_prt.push_back(parameter.position().z()); + m_px_prt.push_back(parameter.momentum().x()); + m_py_prt.push_back(parameter.momentum().y()); + m_pz_prt.push_back(parameter.momentum().z()); + m_pT_prt.push_back(parameter.pT()); + m_eta_prt.push_back(eta(parameter.position())); + } else { + // push default values if no predicted parameter + m_res_x_hit.push_back(-99.); + m_res_y_hit.push_back(-99.); + m_err_x_hit.push_back(-99.); + m_err_y_hit.push_back(-99.); + m_pull_x_hit.push_back(-99.); + m_pull_y_hit.push_back(-99.); + m_dim_hit.push_back(-99.); + m_eLOC0_prt.push_back(-99.); + m_eLOC1_prt.push_back(-99.); + m_ePHI_prt.push_back(-99.); + m_eTHETA_prt.push_back(-99.); + m_eQOP_prt.push_back(-99.); + m_eT_prt.push_back(-99.); + m_res_eLOC0_prt.push_back(-99.); + m_res_eLOC1_prt.push_back(-99.); + m_res_ePHI_prt.push_back(-99.); + m_res_eTHETA_prt.push_back(-99.); + m_res_eQOP_prt.push_back(-99.); + m_res_eT_prt.push_back(-99.); + m_err_eLOC0_prt.push_back(-99); + m_err_eLOC1_prt.push_back(-99); + m_err_ePHI_prt.push_back(-99); + m_err_eTHETA_prt.push_back(-99); + m_err_eQOP_prt.push_back(-99); + m_err_eT_prt.push_back(-99); + m_pull_eLOC0_prt.push_back(-99.); + m_pull_eLOC1_prt.push_back(-99.); + m_pull_ePHI_prt.push_back(-99.); + m_pull_eTHETA_prt.push_back(-99.); + m_pull_eQOP_prt.push_back(-99.); + m_pull_eT_prt.push_back(-99.); + m_x_prt.push_back(-99.); + m_y_prt.push_back(-99.); + m_z_prt.push_back(-99.); + m_px_prt.push_back(-99.); + m_py_prt.push_back(-99.); + m_pz_prt.push_back(-99.); + m_pT_prt.push_back(-99.); + m_eta_prt.push_back(-99.); + } + + // get the filtered parameter + bool filtered = false; + if (state.hasFiltered()) { + filtered = true; + m_nFiltered++; + Acts::BoundParameters parameter( + gctx, state.filteredCovariance(), state.filtered(), + state.referenceSurface().getSharedPtr()); + auto covariance = state.filteredCovariance(); + // filtered parameter + m_eLOC0_flt.push_back(parameter.parameters()[Acts::ParDef::eLOC_0]); + m_eLOC1_flt.push_back(parameter.parameters()[Acts::ParDef::eLOC_1]); + m_ePHI_flt.push_back(parameter.parameters()[Acts::ParDef::ePHI]); + m_eTHETA_flt.push_back(parameter.parameters()[Acts::ParDef::eTHETA]); + m_eQOP_flt.push_back(parameter.parameters()[Acts::ParDef::eQOP]); + m_eT_flt.push_back(parameter.parameters()[Acts::ParDef::eT]); + + // filtered residual + m_res_eLOC0_flt.push_back(parameter.parameters()[Acts::ParDef::eLOC_0] - + truthLOC0); + m_res_eLOC1_flt.push_back(parameter.parameters()[Acts::ParDef::eLOC_1] - + truthLOC1); + m_res_ePHI_flt.push_back(parameter.parameters()[Acts::ParDef::ePHI] - + truthPHI); + m_res_eTHETA_flt.push_back( + parameter.parameters()[Acts::ParDef::eTHETA] - truthTHETA); + m_res_eQOP_flt.push_back(parameter.parameters()[Acts::ParDef::eQOP] - + truthQOP); + m_res_eT_flt.push_back(parameter.parameters()[Acts::ParDef::eT] - + truthTIME); + + // filtered parameter error + m_err_eLOC0_flt.push_back( + sqrt(covariance(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_err_eLOC1_flt.push_back( + sqrt(covariance(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_err_ePHI_flt.push_back( + sqrt(covariance(Acts::ParDef::ePHI, Acts::ParDef::ePHI))); + m_err_eTHETA_flt.push_back( + sqrt(covariance(Acts::ParDef::eTHETA, Acts::ParDef::eTHETA))); + m_err_eQOP_flt.push_back( + sqrt(covariance(Acts::ParDef::eQOP, Acts::ParDef::eQOP))); + m_err_eT_flt.push_back( + sqrt(covariance(Acts::ParDef::eT, Acts::ParDef::eT))); + + // filtered parameter pull + m_pull_eLOC0_flt.push_back( + (parameter.parameters()[Acts::ParDef::eLOC_0] - truthLOC0) / + sqrt(covariance(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_pull_eLOC1_flt.push_back( + (parameter.parameters()[Acts::ParDef::eLOC_1] - truthLOC1) / + sqrt(covariance(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_pull_ePHI_flt.push_back( + (parameter.parameters()[Acts::ParDef::ePHI] - truthPHI) / + sqrt(covariance(Acts::ParDef::ePHI, Acts::ParDef::ePHI))); + m_pull_eTHETA_flt.push_back( + (parameter.parameters()[Acts::ParDef::eTHETA] - truthTHETA) / + sqrt(covariance(Acts::ParDef::eTHETA, Acts::ParDef::eTHETA))); + m_pull_eQOP_flt.push_back( + (parameter.parameters()[Acts::ParDef::eQOP] - truthQOP) / + sqrt(covariance(Acts::ParDef::eQOP, Acts::ParDef::eQOP))); + m_pull_eT_flt.push_back( + (parameter.parameters()[Acts::ParDef::eT] - truthTIME) / + sqrt(covariance(Acts::ParDef::eT, Acts::ParDef::eT))); + + // more filtered parameter info + m_x_flt.push_back(parameter.position().x()); + m_y_flt.push_back(parameter.position().y()); + m_z_flt.push_back(parameter.position().z()); + m_px_flt.push_back(parameter.momentum().x()); + m_py_flt.push_back(parameter.momentum().y()); + m_pz_flt.push_back(parameter.momentum().z()); + m_pT_flt.push_back(parameter.pT()); + m_eta_flt.push_back(eta(parameter.position())); + m_chi2.push_back(state.chi2()); + } else { + // push default values if no filtered parameter + m_eLOC0_flt.push_back(-99.); + m_eLOC1_flt.push_back(-99.); + m_ePHI_flt.push_back(-99.); + m_eTHETA_flt.push_back(-99.); + m_eQOP_flt.push_back(-99.); + m_eT_flt.push_back(-99.); + m_res_eLOC0_flt.push_back(-99.); + m_res_eLOC1_flt.push_back(-99.); + m_res_ePHI_flt.push_back(-99.); + m_res_eTHETA_flt.push_back(-99.); + m_res_eQOP_flt.push_back(-99.); + m_res_eT_flt.push_back(-99.); + m_err_eLOC0_flt.push_back(-99); + m_err_eLOC1_flt.push_back(-99); + m_err_ePHI_flt.push_back(-99); + m_err_eTHETA_flt.push_back(-99); + m_err_eQOP_flt.push_back(-99); + m_err_eT_flt.push_back(-99); + m_pull_eLOC0_flt.push_back(-99.); + m_pull_eLOC1_flt.push_back(-99.); + m_pull_ePHI_flt.push_back(-99.); + m_pull_eTHETA_flt.push_back(-99.); + m_pull_eQOP_flt.push_back(-99.); + m_pull_eT_flt.push_back(-99.); + m_x_flt.push_back(-99.); + m_y_flt.push_back(-99.); + m_z_flt.push_back(-99.); + m_py_flt.push_back(-99.); + m_pz_flt.push_back(-99.); + m_pT_flt.push_back(-99.); + m_eta_flt.push_back(-99.); + m_chi2.push_back(-99.0); + } + + // get the smoothed parameter + bool smoothed = false; + if (state.hasSmoothed()) { + smoothed = true; + m_nSmoothed++; + Acts::BoundParameters parameter( + gctx, state.smoothedCovariance(), state.smoothed(), + state.referenceSurface().getSharedPtr()); + auto covariance = state.smoothedCovariance(); + + // smoothed parameter + m_eLOC0_smt.push_back(parameter.parameters()[Acts::ParDef::eLOC_0]); + m_eLOC1_smt.push_back(parameter.parameters()[Acts::ParDef::eLOC_1]); + m_ePHI_smt.push_back(parameter.parameters()[Acts::ParDef::ePHI]); + m_eTHETA_smt.push_back(parameter.parameters()[Acts::ParDef::eTHETA]); + m_eQOP_smt.push_back(parameter.parameters()[Acts::ParDef::eQOP]); + m_eT_smt.push_back(parameter.parameters()[Acts::ParDef::eT]); + + // smoothed residual + m_res_eLOC0_smt.push_back(parameter.parameters()[Acts::ParDef::eLOC_0] - + truthLOC0); + m_res_eLOC1_smt.push_back(parameter.parameters()[Acts::ParDef::eLOC_1] - + truthLOC1); + m_res_ePHI_smt.push_back(parameter.parameters()[Acts::ParDef::ePHI] - + truthPHI); + m_res_eTHETA_smt.push_back( + parameter.parameters()[Acts::ParDef::eTHETA] - truthTHETA); + m_res_eQOP_smt.push_back(parameter.parameters()[Acts::ParDef::eQOP] - + truthQOP); + m_res_eT_smt.push_back(parameter.parameters()[Acts::ParDef::eT] - + truthTIME); + + // smoothed parameter error + m_err_eLOC0_smt.push_back( + sqrt(covariance(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_err_eLOC1_smt.push_back( + sqrt(covariance(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_err_ePHI_smt.push_back( + sqrt(covariance(Acts::ParDef::ePHI, Acts::ParDef::ePHI))); + m_err_eTHETA_smt.push_back( + sqrt(covariance(Acts::ParDef::eTHETA, Acts::ParDef::eTHETA))); + m_err_eQOP_smt.push_back( + sqrt(covariance(Acts::ParDef::eQOP, Acts::ParDef::eQOP))); + m_err_eT_smt.push_back( + sqrt(covariance(Acts::ParDef::eT, Acts::ParDef::eT))); + + // smoothed parameter pull + m_pull_eLOC0_smt.push_back( + (parameter.parameters()[Acts::ParDef::eLOC_0] - truthLOC0) / + sqrt(covariance(Acts::ParDef::eLOC_0, Acts::ParDef::eLOC_0))); + m_pull_eLOC1_smt.push_back( + (parameter.parameters()[Acts::ParDef::eLOC_1] - truthLOC1) / + sqrt(covariance(Acts::ParDef::eLOC_1, Acts::ParDef::eLOC_1))); + m_pull_ePHI_smt.push_back( + (parameter.parameters()[Acts::ParDef::ePHI] - truthPHI) / + sqrt(covariance(Acts::ParDef::ePHI, Acts::ParDef::ePHI))); + m_pull_eTHETA_smt.push_back( + (parameter.parameters()[Acts::ParDef::eTHETA] - truthTHETA) / + sqrt(covariance(Acts::ParDef::eTHETA, Acts::ParDef::eTHETA))); + m_pull_eQOP_smt.push_back( + (parameter.parameters()[Acts::ParDef::eQOP] - truthQOP) / + sqrt(covariance(Acts::ParDef::eQOP, Acts::ParDef::eQOP))); + m_pull_eT_smt.push_back( + (parameter.parameters()[Acts::ParDef::eT] - truthTIME) / + sqrt(covariance(Acts::ParDef::eT, Acts::ParDef::eT))); + + // further smoothed parameter info + m_x_smt.push_back(parameter.position().x()); + m_y_smt.push_back(parameter.position().y()); + m_z_smt.push_back(parameter.position().z()); + m_px_smt.push_back(parameter.momentum().x()); + m_py_smt.push_back(parameter.momentum().y()); + m_pz_smt.push_back(parameter.momentum().z()); + m_pT_smt.push_back(parameter.pT()); + m_eta_smt.push_back(eta(parameter.position())); + } else { + // push default values if no smoothed parameter + m_eLOC0_smt.push_back(-99.); + m_eLOC1_smt.push_back(-99.); + m_ePHI_smt.push_back(-99.); + m_eTHETA_smt.push_back(-99.); + m_eQOP_smt.push_back(-99.); + m_eT_smt.push_back(-99.); + m_res_eLOC0_smt.push_back(-99.); + m_res_eLOC1_smt.push_back(-99.); + m_res_ePHI_smt.push_back(-99.); + m_res_eTHETA_smt.push_back(-99.); + m_res_eQOP_smt.push_back(-99.); + m_res_eT_smt.push_back(-99.); + m_err_eLOC0_smt.push_back(-99); + m_err_eLOC1_smt.push_back(-99); + m_err_ePHI_smt.push_back(-99); + m_err_eTHETA_smt.push_back(-99); + m_err_eQOP_smt.push_back(-99); + m_err_eT_smt.push_back(-99); + m_pull_eLOC0_smt.push_back(-99.); + m_pull_eLOC1_smt.push_back(-99.); + m_pull_ePHI_smt.push_back(-99.); + m_pull_eTHETA_smt.push_back(-99.); + m_pull_eQOP_smt.push_back(-99.); + m_pull_eT_smt.push_back(-99.); + m_x_smt.push_back(-99.); + m_y_smt.push_back(-99.); + m_z_smt.push_back(-99.); + m_px_smt.push_back(-99.); + m_py_smt.push_back(-99.); + m_pz_smt.push_back(-99.); + m_pT_smt.push_back(-99.); + m_eta_smt.push_back(-99.); + } + + m_prt.push_back(predicted); + m_flt.push_back(filtered); + m_smt.push_back(smoothed); + return true; + }); // all states + + // fill the variables for one track to tree + m_outputTree->Fill(); + + // now reset + m_t_x.clear(); + m_t_y.clear(); + m_t_z.clear(); + m_t_r.clear(); + m_t_dx.clear(); + m_t_dy.clear(); + m_t_dz.clear(); + m_t_eLOC0.clear(); + m_t_eLOC1.clear(); + m_t_ePHI.clear(); + m_t_eTHETA.clear(); + m_t_eQOP.clear(); + m_t_eT.clear(); + + m_volumeID.clear(); + m_layerID.clear(); + m_moduleID.clear(); + m_lx_hit.clear(); + m_ly_hit.clear(); + m_x_hit.clear(); + m_y_hit.clear(); + m_z_hit.clear(); + m_res_x_hit.clear(); + m_res_y_hit.clear(); + m_err_x_hit.clear(); + m_err_y_hit.clear(); + m_pull_x_hit.clear(); + m_pull_y_hit.clear(); + m_dim_hit.clear(); + + m_prt.clear(); + m_eLOC0_prt.clear(); + m_eLOC1_prt.clear(); + m_ePHI_prt.clear(); + m_eTHETA_prt.clear(); + m_eQOP_prt.clear(); + m_eT_prt.clear(); + m_res_eLOC0_prt.clear(); + m_res_eLOC1_prt.clear(); + m_res_ePHI_prt.clear(); + m_res_eTHETA_prt.clear(); + m_res_eQOP_prt.clear(); + m_res_eT_prt.clear(); + m_err_eLOC0_prt.clear(); + m_err_eLOC1_prt.clear(); + m_err_ePHI_prt.clear(); + m_err_eTHETA_prt.clear(); + m_err_eQOP_prt.clear(); + m_err_eT_prt.clear(); + m_pull_eLOC0_prt.clear(); + m_pull_eLOC1_prt.clear(); + m_pull_ePHI_prt.clear(); + m_pull_eTHETA_prt.clear(); + m_pull_eQOP_prt.clear(); + m_pull_eT_prt.clear(); + m_x_prt.clear(); + m_y_prt.clear(); + m_z_prt.clear(); + m_px_prt.clear(); + m_py_prt.clear(); + m_pz_prt.clear(); + m_eta_prt.clear(); + m_pT_prt.clear(); + + m_flt.clear(); + m_eLOC0_flt.clear(); + m_eLOC1_flt.clear(); + m_ePHI_flt.clear(); + m_eTHETA_flt.clear(); + m_eQOP_flt.clear(); + m_eT_flt.clear(); + m_res_eLOC0_flt.clear(); + m_res_eLOC1_flt.clear(); + m_res_ePHI_flt.clear(); + m_res_eTHETA_flt.clear(); + m_res_eQOP_flt.clear(); + m_res_eT_flt.clear(); + m_err_eLOC0_flt.clear(); + m_err_eLOC1_flt.clear(); + m_err_ePHI_flt.clear(); + m_err_eTHETA_flt.clear(); + m_err_eQOP_flt.clear(); + m_err_eT_flt.clear(); + m_pull_eLOC0_flt.clear(); + m_pull_eLOC1_flt.clear(); + m_pull_ePHI_flt.clear(); + m_pull_eTHETA_flt.clear(); + m_pull_eQOP_flt.clear(); + m_pull_eT_flt.clear(); + m_x_flt.clear(); + m_y_flt.clear(); + m_z_flt.clear(); + m_px_flt.clear(); + m_py_flt.clear(); + m_pz_flt.clear(); + m_eta_flt.clear(); + m_pT_flt.clear(); + m_chi2.clear(); + + m_smt.clear(); + m_eLOC0_smt.clear(); + m_eLOC1_smt.clear(); + m_ePHI_smt.clear(); + m_eTHETA_smt.clear(); + m_eQOP_smt.clear(); + m_eT_smt.clear(); + m_res_eLOC0_smt.clear(); + m_res_eLOC1_smt.clear(); + m_res_ePHI_smt.clear(); + m_res_eTHETA_smt.clear(); + m_res_eQOP_smt.clear(); + m_res_eT_smt.clear(); + m_err_eLOC0_smt.clear(); + m_err_eLOC1_smt.clear(); + m_err_ePHI_smt.clear(); + m_err_eTHETA_smt.clear(); + m_err_eQOP_smt.clear(); + m_err_eT_smt.clear(); + m_pull_eLOC0_smt.clear(); + m_pull_eLOC1_smt.clear(); + m_pull_ePHI_smt.clear(); + m_pull_eTHETA_smt.clear(); + m_pull_eQOP_smt.clear(); + m_pull_eT_smt.clear(); + m_x_smt.clear(); + m_y_smt.clear(); + m_z_smt.clear(); + m_px_smt.clear(); + m_py_smt.clear(); + m_pz_smt.clear(); + m_eta_smt.clear(); + m_pT_smt.clear(); + + iTraj++; + } // all trajectories + + return ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootVertexAndTracksReader.cpp b/Io/Root/src/RootVertexAndTracksReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..251221090005c7c976b99cc3c80fdf36da360e56 --- /dev/null +++ b/Io/Root/src/RootVertexAndTracksReader.cpp @@ -0,0 +1,140 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/RootVertexAndTracksReader.hpp" + +#include <Acts/Surfaces/PerigeeSurface.hpp> +#include <TChain.h> +#include <TFile.h> +#include <iostream> + +#include "ACTFW/Framework/WhiteBoard.hpp" +#include "ACTFW/TruthTracking/VertexAndTracks.hpp" + +FW::RootVertexAndTracksReader::RootVertexAndTracksReader( + FW::RootVertexAndTracksReader::Config cfg, Acts::Logging::Level lvl) + : m_cfg(std::move(cfg)), + m_events(0), + m_inputChain(nullptr), + m_logger(Acts::getDefaultLogger("RootVertexAndTracksReader", lvl)) { + m_inputChain = new TChain(m_cfg.treeName.c_str()); + + m_inputChain->SetBranchAddress("event_nr", &m_eventNr); + m_inputChain->SetBranchAddress("vx", &m_ptrVx); + m_inputChain->SetBranchAddress("vy", &m_ptrVy); + m_inputChain->SetBranchAddress("vz", &m_ptrVz); + + m_inputChain->SetBranchAddress("d0", &m_ptrD0); + m_inputChain->SetBranchAddress("z0", &m_ptrZ0); + m_inputChain->SetBranchAddress("phi", &m_ptrPhi); + m_inputChain->SetBranchAddress("theta", &m_ptrTheta); + m_inputChain->SetBranchAddress("qp", &m_ptrQP); + m_inputChain->SetBranchAddress("time", &m_ptrTime); + m_inputChain->SetBranchAddress("vtxID", &m_ptrVtxID); + m_inputChain->SetBranchAddress("trkCov", &m_ptrTrkCov); + + // loop over the input files + for (auto inputFile : m_cfg.fileList) { + // add file to the input chain + m_inputChain->Add(inputFile.c_str()); + ACTS_DEBUG("Adding File " << inputFile << " to tree '" << m_cfg.treeName + << "'."); + } + + m_events = m_inputChain->GetEntries(); + ACTS_DEBUG("The full chain has " << m_events << " entries."); +} + +FW::RootVertexAndTracksReader::~RootVertexAndTracksReader() { + delete m_ptrVx; + delete m_ptrVy; + delete m_ptrVz; + delete m_ptrD0; + delete m_ptrZ0; + delete m_ptrPhi; + delete m_ptrTheta; + delete m_ptrQP; + delete m_ptrTime; + delete m_ptrVtxID; + delete m_ptrTrkCov; +} + +std::string FW::RootVertexAndTracksReader::name() const { + return "RootVertexAndTracksReader"; +} + +std::pair<size_t, size_t> FW::RootVertexAndTracksReader::availableEvents() + const { + return {0u, m_events}; +} + +FW::ProcessCode FW::RootVertexAndTracksReader::read( + const FW::AlgorithmContext& context) { + ACTS_DEBUG("Trying to read vertex and tracks."); + + if (m_inputChain && context.eventNumber < m_events) { + // Lock the mutex + std::lock_guard<std::mutex> lock(m_read_mutex); + + // The collection to be written + std::vector<FW::VertexAndTracks> mCollection; + + for (size_t ib = 0; ib < m_cfg.batchSize; ++ib) { + // Read the correct entry: batch size * event_number + ib + m_inputChain->GetEntry(m_cfg.batchSize * context.eventNumber + ib); + ACTS_VERBOSE("Reading entry: " << m_cfg.batchSize * context.eventNumber + + ib); + + // Loop over all vertices + for (size_t idx = 0; idx < m_ptrVx->size(); ++idx) { + FW::VertexAndTracks vtxAndTracks; + vtxAndTracks.vertex.position4[0] = (*m_ptrVx)[idx]; + vtxAndTracks.vertex.position4[1] = (*m_ptrVy)[idx]; + vtxAndTracks.vertex.position4[2] = (*m_ptrVz)[idx]; + vtxAndTracks.vertex.position4[3] = 0; + + std::vector<Acts::BoundParameters> tracks; + // Loop over all tracks in current event + for (size_t trkId = 0; trkId < m_ptrD0->size(); ++trkId) { + // Take only tracks that belong to current vertex + if (static_cast<size_t>((*m_ptrVtxID)[trkId]) == idx) { + // Get track parameter + Acts::BoundVector newTrackParams; + newTrackParams << (*m_ptrD0)[trkId], (*m_ptrZ0)[trkId], + (*m_ptrPhi)[trkId], (*m_ptrTheta)[trkId], (*m_ptrQP)[trkId], + (*m_ptrTime)[trkId]; + + // Get track covariance vector + std::vector<double> trkCovVec = (*m_ptrTrkCov)[trkId]; + + // Construct track covariance + Acts::BoundSymMatrix covMat = + Eigen::Map<Acts::BoundSymMatrix>(trkCovVec.data()); + + // Create track parameters and add to track list + std::shared_ptr<Acts::PerigeeSurface> perigeeSurface = + Acts::Surface::makeShared<Acts::PerigeeSurface>( + Acts::Vector3D(0., 0., 0.)); + tracks.push_back( + Acts::BoundParameters(context.geoContext, std::move(covMat), + newTrackParams, perigeeSurface)); + } + } // End loop over all tracks + // Set tracks + vtxAndTracks.tracks = tracks; + // Add to collection + mCollection.push_back(std::move(vtxAndTracks)); + } + } + + // Write to the collection to the EventStore + context.eventStore.add(m_cfg.outputCollection, std::move(mCollection)); + } + // Return success flag + return FW::ProcessCode::SUCCESS; +} diff --git a/Io/Root/src/RootVertexAndTracksWriter.cpp b/Io/Root/src/RootVertexAndTracksWriter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c35878443649a977d9519657be8260cae8ca1d2d --- /dev/null +++ b/Io/Root/src/RootVertexAndTracksWriter.cpp @@ -0,0 +1,256 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2019 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/RootVertexAndTracksWriter.hpp" + +#include <Acts/Utilities/Helpers.hpp> +#include <TFile.h> +#include <TTree.h> +#include <ios> +#include <stdexcept> + +using Acts::VectorHelpers::eta; +using Acts::VectorHelpers::perp; +using Acts::VectorHelpers::phi; + +FW::RootVertexAndTracksWriter::RootVertexAndTracksWriter( + const FW::RootVertexAndTracksWriter::Config& cfg, Acts::Logging::Level lvl) + : WriterT(cfg.collection, "RootVertexAndTracksWriter", lvl), + m_cfg(cfg), + m_outputFile(cfg.rootFile) { + // An input collection name and tree name must be specified + if (m_cfg.collection.empty()) { + throw std::invalid_argument("Missing input collection"); + } else if (m_cfg.treeName.empty()) { + throw std::invalid_argument("Missing tree name"); + } + + // Setup ROOT I/O + if (m_outputFile == nullptr) { + 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(); + else { + // I/O parameters + m_outputTree->Branch("event_nr", &m_eventNr); + m_outputTree->Branch("vx", &m_ptrVx); + m_outputTree->Branch("vy", &m_ptrVy); + m_outputTree->Branch("vz", &m_ptrVz); + + m_outputTree->Branch("d0", &m_ptrD0); + m_outputTree->Branch("z0", &m_ptrZ0); + m_outputTree->Branch("phi", &m_ptrPhi); + m_outputTree->Branch("theta", &m_ptrTheta); + m_outputTree->Branch("qp", &m_ptrQP); + m_outputTree->Branch("time", &m_ptrTime); + m_outputTree->Branch("vtxID", &m_ptrVtxID); + + m_outputTree->Branch("trkCov11", &m_ptrCov11); + m_outputTree->Branch("trkCov12", &m_ptrCov12); + m_outputTree->Branch("trkCov13", &m_ptrCov13); + m_outputTree->Branch("trkCov14", &m_ptrCov14); + m_outputTree->Branch("trkCov15", &m_ptrCov15); + m_outputTree->Branch("trkCov16", &m_ptrCov16); + + m_outputTree->Branch("trkCov21", &m_ptrCov21); + m_outputTree->Branch("trkCov22", &m_ptrCov22); + m_outputTree->Branch("trkCov23", &m_ptrCov23); + m_outputTree->Branch("trkCov24", &m_ptrCov24); + m_outputTree->Branch("trkCov25", &m_ptrCov25); + m_outputTree->Branch("trkCov26", &m_ptrCov26); + + m_outputTree->Branch("trkCov31", &m_ptrCov31); + m_outputTree->Branch("trkCov32", &m_ptrCov32); + m_outputTree->Branch("trkCov33", &m_ptrCov33); + m_outputTree->Branch("trkCov34", &m_ptrCov34); + m_outputTree->Branch("trkCov35", &m_ptrCov35); + m_outputTree->Branch("trkCov36", &m_ptrCov36); + + m_outputTree->Branch("trkCov41", &m_ptrCov41); + m_outputTree->Branch("trkCov42", &m_ptrCov42); + m_outputTree->Branch("trkCov43", &m_ptrCov43); + m_outputTree->Branch("trkCov44", &m_ptrCov44); + m_outputTree->Branch("trkCov45", &m_ptrCov45); + m_outputTree->Branch("trkCov46", &m_ptrCov46); + + m_outputTree->Branch("trkCov51", &m_ptrCov51); + m_outputTree->Branch("trkCov52", &m_ptrCov52); + m_outputTree->Branch("trkCov53", &m_ptrCov53); + m_outputTree->Branch("trkCov54", &m_ptrCov54); + m_outputTree->Branch("trkCov55", &m_ptrCov55); + m_outputTree->Branch("trkCov56", &m_ptrCov56); + + m_outputTree->Branch("trkCov61", &m_ptrCov61); + m_outputTree->Branch("trkCov62", &m_ptrCov62); + m_outputTree->Branch("trkCov63", &m_ptrCov63); + m_outputTree->Branch("trkCov64", &m_ptrCov64); + m_outputTree->Branch("trkCov65", &m_ptrCov65); + m_outputTree->Branch("trkCov66", &m_ptrCov66); + } +} + +FW::RootVertexAndTracksWriter::~RootVertexAndTracksWriter() { + if (m_outputFile) { + m_outputFile->Close(); + } +} + +FW::ProcessCode FW::RootVertexAndTracksWriter::endRun() { + if (m_outputFile) { + m_outputFile->cd(); + m_outputTree->Write(); + ACTS_INFO("Wrote event to tree '" << m_cfg.treeName << "' in '" + << m_cfg.filePath << "'"); + } + return ProcessCode::SUCCESS; +} + +void FW::RootVertexAndTracksWriter::ClearAll() { + m_vx.clear(); + m_vy.clear(); + m_vz.clear(); + m_d0.clear(); + m_z0.clear(); + m_phi.clear(); + m_theta.clear(); + m_qp.clear(); + m_time.clear(); + m_vtxID.clear(); + + m_cov11.clear(); + m_cov12.clear(); + m_cov13.clear(); + m_cov14.clear(); + m_cov15.clear(); + m_cov16.clear(); + + m_cov21.clear(); + m_cov22.clear(); + m_cov23.clear(); + m_cov24.clear(); + m_cov25.clear(); + m_cov26.clear(); + + m_cov31.clear(); + m_cov32.clear(); + m_cov33.clear(); + m_cov34.clear(); + m_cov35.clear(); + m_cov36.clear(); + + m_cov41.clear(); + m_cov42.clear(); + m_cov43.clear(); + m_cov44.clear(); + m_cov45.clear(); + m_cov46.clear(); + + m_cov51.clear(); + m_cov52.clear(); + m_cov53.clear(); + m_cov54.clear(); + m_cov55.clear(); + m_cov56.clear(); + + m_cov61.clear(); + m_cov62.clear(); + m_cov63.clear(); + m_cov64.clear(); + m_cov65.clear(); + m_cov66.clear(); +} + +FW::ProcessCode FW::RootVertexAndTracksWriter::writeT( + const AlgorithmContext& context, + const std::vector<VertexAndTracks>& vertexAndTracksCollection) { + if (m_outputFile == nullptr || vertexAndTracksCollection.empty()) { + return ProcessCode::SUCCESS; + } + + // Exclusive access to the tree while writing + std::lock_guard<std::mutex> lock(m_writeMutex); + + ClearAll(); + + // Get the event number + m_eventNr = context.eventNumber; + + for (auto& vertexAndTracks : vertexAndTracksCollection) { + // Collect the vertex information + m_vx.push_back(vertexAndTracks.vertex.position().x()); + m_vy.push_back(vertexAndTracks.vertex.position().y()); + m_vz.push_back(vertexAndTracks.vertex.position().z()); + + for (auto& track : vertexAndTracks.tracks) { + // Collect the track information + m_d0.push_back(track.parameters()[Acts::ParDef::eLOC_D0]); + m_z0.push_back(track.parameters()[Acts::ParDef::eLOC_Z0]); + m_phi.push_back(track.parameters()[Acts::ParDef::ePHI]); + m_theta.push_back(track.parameters()[Acts::ParDef::eTHETA]); + m_qp.push_back(track.parameters()[Acts::ParDef::eQOP]); + m_time.push_back(track.parameters()[Acts::ParDef::eT]); + // Current vertex index as vertex ID + m_vtxID.push_back(m_vx.size() - 1); + + // Save track covariance + Acts::BoundSymMatrix cov = *track.covariance(); + + m_cov11.push_back(cov(0, 0)); + m_cov12.push_back(cov(0, 1)); + m_cov13.push_back(cov(0, 2)); + m_cov14.push_back(cov(0, 3)); + m_cov15.push_back(cov(0, 4)); + m_cov16.push_back(cov(0, 5)); + + m_cov21.push_back(cov(1, 0)); + m_cov22.push_back(cov(1, 1)); + m_cov23.push_back(cov(1, 2)); + m_cov24.push_back(cov(1, 3)); + m_cov25.push_back(cov(1, 4)); + m_cov26.push_back(cov(1, 5)); + + m_cov31.push_back(cov(2, 0)); + m_cov32.push_back(cov(2, 1)); + m_cov33.push_back(cov(2, 2)); + m_cov34.push_back(cov(2, 3)); + m_cov35.push_back(cov(2, 4)); + m_cov36.push_back(cov(2, 5)); + + m_cov41.push_back(cov(3, 0)); + m_cov42.push_back(cov(3, 1)); + m_cov43.push_back(cov(3, 2)); + m_cov44.push_back(cov(3, 3)); + m_cov45.push_back(cov(3, 4)); + m_cov46.push_back(cov(3, 5)); + + m_cov51.push_back(cov(4, 0)); + m_cov52.push_back(cov(4, 1)); + m_cov53.push_back(cov(4, 2)); + m_cov54.push_back(cov(4, 3)); + m_cov55.push_back(cov(4, 4)); + m_cov56.push_back(cov(4, 5)); + + m_cov61.push_back(cov(5, 0)); + m_cov62.push_back(cov(5, 1)); + m_cov63.push_back(cov(5, 2)); + m_cov64.push_back(cov(5, 3)); + m_cov65.push_back(cov(5, 4)); + m_cov66.push_back(cov(5, 5)); + } + } + + m_outputTree->Fill(); + + return ProcessCode::SUCCESS; +}