From 95677644e643d64caf9eacd1c83eb459dda09848 Mon Sep 17 00:00:00 2001 From: Joana Niermann Date: Tue, 10 Oct 2023 22:54:14 +0200 Subject: [PATCH] Adds a digitization configurator that can read a digitization meta configuration and generate a full digitization configuration for a detray geometry. This is mostly useful for geometries that are generated in detray, as this should hopefully become compatible with ACTS generated tracking geometries at some point, in which case the detray surfaces will provide a source link to the corresponding ACTS surface. The configuration can only be done per volume and extra bit. Since the current ACTS version in traccc does not support extra-bit matching in the GeometryHierarchyMap yet, and because detray has no facilities to set extra bits during geometry IO yet, this part of the configuration has not been tested so far. --- examples/io/CMakeLists.txt | 5 +- examples/io/create_digitization_config.cpp | 114 +++++++++++++ io/CMakeLists.txt | 4 + io/include/traccc/io/digitization_config.hpp | 3 + .../traccc/io/digitization_configurator.hpp | 93 +++++++++++ .../traccc/io/digitization_configurator.ipp | 157 ++++++++++++++++++ .../traccc/io/write_digitization_config.hpp | 43 +++++ io/src/read_digitization_config.cpp | 2 + io/src/write_digitization_config.cpp | 100 +++++++++++ tests/io/CMakeLists.txt | 3 +- tests/io/test_digi_configurator.cpp | 51 ++++++ 11 files changed, 573 insertions(+), 2 deletions(-) create mode 100644 examples/io/create_digitization_config.cpp create mode 100644 io/include/traccc/io/digitization_configurator.hpp create mode 100644 io/include/traccc/io/digitization_configurator.ipp create mode 100644 io/include/traccc/io/write_digitization_config.hpp create mode 100644 io/src/write_digitization_config.cpp create mode 100644 tests/io/test_digi_configurator.cpp diff --git a/examples/io/CMakeLists.txt b/examples/io/CMakeLists.txt index 1e804a3e0b..3399072b93 100644 --- a/examples/io/CMakeLists.txt +++ b/examples/io/CMakeLists.txt @@ -5,4 +5,7 @@ # Mozilla Public License Version 2.0 traccc_add_executable( create_binaries "create_binaries.cpp" - LINK_LIBRARIES vecmem::core traccc::core traccc::io traccc::options) \ No newline at end of file + LINK_LIBRARIES vecmem::core traccc::core traccc::io traccc::options) + +traccc_add_executable( create_digitization_config "create_digitization_config.cpp" + LINK_LIBRARIES vecmem::core traccc::core traccc::io traccc::options detray::io) diff --git a/examples/io/create_digitization_config.cpp b/examples/io/create_digitization_config.cpp new file mode 100644 index 0000000000..a7a482a032 --- /dev/null +++ b/examples/io/create_digitization_config.cpp @@ -0,0 +1,114 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2023 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +// Project include(s). +#include "traccc/io/digitization_configurator.hpp" +#include "traccc/io/read_digitization_config.hpp" +#include "traccc/io/write_digitization_config.hpp" +#include "traccc/options/handle_argument_errors.hpp" + +// Detray include(s) +#include "detray/core/detector.hpp" +#include "detray/geometry/surface.hpp" +#include "detray/io/common/detector_reader.hpp" + +// VecMem include(s). +#include + +// Boost +#include + +// System include(s) +#include +#include +#include + +namespace po = boost::program_options; + +/// @brief create a detailed digitization file for the given detector +/// +/// Takes a human editable digitization "meta"-configuration file and assigns +/// the given configuration to the specified surfaces by visitng the geometry. +/// For every discovered surface in a digitization domain, the respective +/// digitization configuration is written into the output json file. +/// +/// @param digi_meta_config_file file that contains the editable "meta"-config +/// @param reader_cfg detray detector reader config, specifying the geometry +int create_digitization_config( + const std::string& digi_meta_config_file, + const detray::io::detector_reader_config& reader_cfg) { + + vecmem::host_memory_resource host_mr; + + const auto [det, names] = + detray::io::read_detector>(host_mr, reader_cfg); + + std::cout << "Running digitization configurator for \"" << names.at(0) + << "\"..." << std::endl; + + const traccc::digitization_config digi_meta_cfg = + traccc::io::read_digitization_config(digi_meta_config_file); + + traccc::io::digitization_configurator digi_configurator{digi_meta_cfg}; + + // Visit all detector surfaces to generate their digitization configuration + for (const auto& sf_desc : det.surface_lookup()) { + digi_configurator(detray::surface{det, sf_desc}); + } + + std::string outfile_name = names.at(0) + "-geometric-config.json"; + traccc::io::write_digitization_config(outfile_name, + digi_configurator.output_digi_cfg); + + std::cout << "Done. Written config to: " << outfile_name << std::endl; + + return EXIT_SUCCESS; +} + +// The main routine +// +int main(int argc, char* argv[]) { + // Options parsing + po::options_description desc("\ntraccc digitization configurator"); + + desc.add_options()("help", "produce help message")( + "geometry_file", po::value(), + "detray geometry input file")("digi_meta_config_file", + po::value(), + "digitization meta-configuration file"); + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + + traccc::handle_argument_errors(vm, desc); + + // Configs to be filled + detray::io::detector_reader_config reader_cfg{}; + + // Input files + if (vm.count("geometry_file")) { + reader_cfg.add_file(vm["geometry_file"].as()); + } else { + std::stringstream err_stream{}; + err_stream << "Please specify a geometry input file!\n\n" << desc; + + throw std::invalid_argument(err_stream.str()); + } + std::string digi_meta_config_file; + if (vm.count("digi_meta_config_file")) { + digi_meta_config_file = vm["digi_meta_config_file"].as(); + } else { + std::stringstream err_stream{}; + err_stream << "Please specify a digitization meta-configuration input" + << " file!\n\n" + << desc; + + throw std::invalid_argument(err_stream.str()); + } + + return create_digitization_config(digi_meta_config_file, reader_cfg); +} diff --git a/io/CMakeLists.txt b/io/CMakeLists.txt index 694f349c83..25e13dab36 100644 --- a/io/CMakeLists.txt +++ b/io/CMakeLists.txt @@ -14,6 +14,8 @@ find_package( OpenMP COMPONENTS CXX ) traccc_add_library( traccc_io io TYPE SHARED # Public headers "include/traccc/io/digitization_config.hpp" + "include/traccc/io/digitization_configurator.hpp" + "include/traccc/io/digitization_configurator.ipp" "include/traccc/io/read.hpp" "include/traccc/io/read_cells.hpp" "include/traccc/io/read_digitization_config.hpp" @@ -26,6 +28,7 @@ traccc_add_library( traccc_io io TYPE SHARED "include/traccc/io/event_map2.hpp" "include/traccc/io/demonstrator_edm.hpp" "include/traccc/io/mapper.hpp" + "include/traccc/io/write_digitization_config.hpp" "include/traccc/io/write.hpp" "include/traccc/io/utils.hpp" "include/traccc/io/details/read_surfaces.hpp" @@ -56,6 +59,7 @@ traccc_add_library( traccc_io io TYPE SHARED "src/utils.cpp" "src/read_binary.hpp" "src/write_binary.hpp" + "src/write_digitization_config.cpp" "src/details/read_surfaces.cpp" "src/csv/make_surface_reader.cpp" "src/csv/read_surfaces.hpp" diff --git a/io/include/traccc/io/digitization_config.hpp b/io/include/traccc/io/digitization_config.hpp index 1be3ccacc8..09e96bb00e 100644 --- a/io/include/traccc/io/digitization_config.hpp +++ b/io/include/traccc/io/digitization_config.hpp @@ -15,6 +15,9 @@ namespace traccc { /// Type describing the digitization configuration of a detector module struct module_digitization_config { + /// Measurement indices + std::vector indices{}; + /// Module segmentation description Acts::BinUtility segmentation; }; diff --git a/io/include/traccc/io/digitization_configurator.hpp b/io/include/traccc/io/digitization_configurator.hpp new file mode 100644 index 0000000000..32be7f4293 --- /dev/null +++ b/io/include/traccc/io/digitization_configurator.hpp @@ -0,0 +1,93 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2023 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#pragma once + +// Local include(s). +#include "traccc/io/data_format.hpp" + +// Project include(s). +#include "traccc/io/digitization_config.hpp" + +// Detray include(s). +#include "detray/geometry/barcode.hpp" +#include "detray/geometry/surface.hpp" + +// System include(s). +#include + +namespace Acts { +class BinUtility; +} + +namespace traccc::io { + +/// Helper configurator that takes a simplified (per volume, per extra bit) +/// input digitization file and creates a full fletched per module +/// digitization configuration file. +/// +/// It acts as a visitor and then builds a fully developed digitization file +/// for the geometric digitization, filling in the correct dimensions and +/// number of bins. +/// +/// The simplified file is assumed to have just one bin for the geometric +/// digitization, which is then used to calculate the number of bins with +/// respect to the bounds range. +/// +/// @see +/// https://github.com/acts-project/acts/blob/main/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfigurator.hpp +struct digitization_configurator { + /// Simplified input components for digitization (meta-configuration) + digitization_config input_digi_cfg; + + /// Final collection of output components + std::vector< + std::pair> + output_digi_cfg; + + /// Needs an input configuration + digitization_configurator() = delete; + + /// Construct from an input configuration @param cfg and initializes an + /// empty output configuration + digitization_configurator(digitization_config cfg) + : input_digi_cfg{cfg}, output_digi_cfg{} {} + + /// The visitor call for the geometry + /// + /// @param surface is the surfaces that is visited + /// + /// Takes the @c input_digi_cfg and adds an appropriate entry into the + /// @c output_digi_cfg for the given surface + template + void operator()(const detray::surface &surface); + + struct segmentation_configurator { + + /// Create the segmentation for a specific surface from the + /// given configuration in @param input_segmentation. + template + void fill_output_segmentation( + const mask_t &mask, const Acts::BinUtility &input_segmentation, + Acts::BinUtility &output_segmentation) const; + + /// Visitor for the surface mask + template + inline void operator()(const mask_group_t &mask_group, + const index_t &index, + const Acts::BinUtility &input_segmentation, + Acts::BinUtility &output_segmentation) const { + + fill_output_segmentation(mask_group[index], input_segmentation, + output_segmentation); + } + }; +}; + +} // namespace traccc::io + +#include "traccc/io/digitization_configurator.ipp" diff --git a/io/include/traccc/io/digitization_configurator.ipp b/io/include/traccc/io/digitization_configurator.ipp new file mode 100644 index 0000000000..43dbb13b63 --- /dev/null +++ b/io/include/traccc/io/digitization_configurator.ipp @@ -0,0 +1,157 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2023 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#pragma once + +// Detray include(s) +#include "detray/masks/masks.hpp" + +// Acts include(s). +#include +#include + +// System include(s) +#include +#include + +namespace traccc::io { + +template +void digitization_configurator::operator()( + const detray::surface &surface) { + + if (!surface.is_sensitive()) { + return; + } + + // For geometries that are generated in detray, there is no source link + Acts::GeometryIdentifier geo_id; + if (surface.source() == + std::numeric_limits::max()) { + geo_id = Acts::GeometryIdentifier(0); + geo_id.setVolume(surface.volume()); + // Translate detray broadcast value to acts broadcast value + geo_id.setExtra(surface.extra() == 255 ? 0 : surface.extra()); + } else { + geo_id = Acts::GeometryIdentifier(surface.source()); + } + + auto input_cfg = input_digi_cfg.find(geo_id); + + if (input_cfg != input_digi_cfg.end()) { + // The output config, copy over the smearing part + module_digitization_config output_cfg; + + if (!input_cfg->indices.empty()) { + // Copy the measurement indices + output_cfg.indices = input_cfg->indices; + + // Create the output segmentation + surface.template visit_mask( + input_cfg->segmentation, output_cfg.segmentation); + } + + // Insert into the output list + output_digi_cfg.push_back({surface.barcode(), output_cfg}); + } +} + +template +void digitization_configurator::segmentation_configurator:: + fill_output_segmentation(const mask_t &mask, + const Acts::BinUtility &input_segmentation, + Acts::BinUtility &output_segmentation) const { + + using bounds = typename mask_t::boundaries; + const auto &bound_values = mask.values(); + + [[maybe_unused]] unsigned int accessBin{ + (input_segmentation.dimensions() == 2) ? 1u : 0u}; + + // The module is a rectangular module + if constexpr (std::is_same_v>) { + if (input_segmentation.binningData()[0].binvalue == Acts::binX) { + + auto minX{-bound_values[bounds::e_half_x]}; + auto maxX{bound_values[bounds::e_half_x]}; + auto n_bins{static_cast(std::round( + (maxX - minX) / input_segmentation.binningData()[0].step))}; + + output_segmentation += + Acts::BinUtility(n_bins, minX, maxX, Acts::open, Acts::binX); + } + if (input_segmentation.binningData()[0].binvalue == Acts::binY or + input_segmentation.dimensions() == 2) { + + auto minY{-bound_values[bounds::e_half_y]}; + auto maxY{bound_values[bounds::e_half_y]}; + auto n_bins{static_cast( + std::round((maxY - minY) / + input_segmentation.binningData()[accessBin].step))}; + + output_segmentation += + Acts::BinUtility(n_bins, minY, maxY, Acts::open, Acts::binY); + } + } + // The module is a trapezoid module + else if constexpr (std::is_same_v>) { + + if (input_segmentation.binningData()[0].binvalue == Acts::binX) { + + auto maxX{std::max(bound_values[bounds::e_half_length_0], + bound_values[bounds::e_half_length_1])}; + auto n_bins{static_cast(std::round( + 2 * maxX / input_segmentation.binningData()[0].step))}; + + output_segmentation += + Acts::BinUtility(n_bins, -maxX, maxX, Acts::open, Acts::binX); + } + if (input_segmentation.binningData()[0].binvalue == Acts::binY or + input_segmentation.dimensions() == 2) { + + auto maxY{bound_values[bounds::e_half_length_2]}; + auto n_bins{static_cast( + std::round((2 * maxY) / + input_segmentation.binningData()[accessBin].step))}; + + output_segmentation += + Acts::BinUtility(n_bins, -maxY, maxY, Acts::open, Acts::binY); + } + } + // The module is an annulus module + else if constexpr (std::is_same_v>) { + + if (input_segmentation.binningData()[0].binvalue == Acts::binR) { + + auto minR{bound_values[bounds::e_min_r]}; + auto maxR{bound_values[bounds::e_max_r]}; + auto n_bins{static_cast(std::round( + (maxR - minR) / input_segmentation.binningData()[0].step))}; + + output_segmentation += + Acts::BinUtility(n_bins, minR, maxR, Acts::open, Acts::binR); + } + if (input_segmentation.binningData()[0].binvalue == Acts::binPhi or + input_segmentation.dimensions() == 2) { + + auto averagePhi{bound_values[bounds::e_average_phi]}; + auto minPhi{averagePhi + bound_values[bounds::e_min_phi_rel]}; + auto maxPhi{averagePhi + bound_values[bounds::e_max_phi_rel]}; + auto n_bins{static_cast( + std::round((maxPhi - minPhi) / + input_segmentation.binningData()[accessBin].step))}; + + output_segmentation += Acts::BinUtility(n_bins, minPhi, maxPhi, + Acts::open, Acts::binPhi); + } + } +} + +} // namespace traccc::io diff --git a/io/include/traccc/io/write_digitization_config.hpp b/io/include/traccc/io/write_digitization_config.hpp new file mode 100644 index 0000000000..2c4bb8b6c7 --- /dev/null +++ b/io/include/traccc/io/write_digitization_config.hpp @@ -0,0 +1,43 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2023 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#pragma once + +// Local include(s). +#include "traccc/io/data_format.hpp" + +// Project include(s). +#include "traccc/io/digitization_config.hpp" + +// Detray include(s). +#include "detray/geometry/barcode.hpp" + +// System include(s). +#include +#include +#include + +namespace Acts { +class GeometryIdentifier; +} + +namespace traccc::io { + +using digitization_out_collection = std::vector< + std::pair>; + +/// Write the detector digitization configuration to an output file +/// +/// @param filename The name of the file to write the data to +/// @param digi_cfg The digitization to be written to file +/// @param format The format of the output file +/// +void write_digitization_config(std::string_view filename, + const digitization_out_collection &digi_cfg, + data_format format = data_format::json); + +} // namespace traccc::io diff --git a/io/src/read_digitization_config.cpp b/io/src/read_digitization_config.cpp index 19c86cc27a..b5cdd2ed04 100644 --- a/io/src/read_digitization_config.cpp +++ b/io/src/read_digitization_config.cpp @@ -34,6 +34,8 @@ void from_json(const nlohmann::json& json, module_digitization_config& cfg) { // Read the object, if possible. if (json.find(geometric) != json.end()) { + cfg.indices = + json[geometric]["indices"].get>(); from_json(json[geometric][segmentation], cfg.segmentation); } } diff --git a/io/src/write_digitization_config.cpp b/io/src/write_digitization_config.cpp new file mode 100644 index 0000000000..69c4bba543 --- /dev/null +++ b/io/src/write_digitization_config.cpp @@ -0,0 +1,100 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2023 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +// Local include(s). +#include "traccc/io/write_digitization_config.hpp" + +#include "traccc/io/utils.hpp" + +// Acts include(s). +#include +#include + +// System include(s). +#include +#include + +namespace traccc { + +/// Function allowing the writing of @c traccc::module_digitization_config +/// objects +inline void to_json(nlohmann::json& json, + const module_digitization_config& cfg) { + + json["geometric"]["indices"] = cfg.indices; + json["geometric"]["segmentation"] = cfg.segmentation; +} + +/// Function allowing the writing of digitization config objects from a +/// collection of detray barcodes and @c traccc::module_digitization_config +inline void to_json(nlohmann::json& in_json, + const io::digitization_out_collection& cfg) { + + nlohmann::json entries = nlohmann::json::array(); + nlohmann::json entry = nlohmann::json::object(); + for (const auto& c : cfg) { + entry["volume"] = c.first.volume(); + entry["sensitive"] = c.first.index(); + if (c.first.extra() != 255) { + entry["extra"] = c.first.extra(); + } + entry["value"] = c.second; + entries.push_back(entry); + } + in_json = entries; +} + +namespace io { +namespace json { + +void write_digitization_config(std::string_view filename, + const digitization_out_collection& digi_cfg) { + + // Open the output file. Relying on exceptions for the error handling. + std::ofstream outfile(filename.data(), std::ifstream::binary); + outfile.exceptions(std::ofstream::failbit | std::ofstream::badbit); + + // Write the digitization data into the json stream + nlohmann::json json; + + // Write header + json["acts-geometry-hierarchy-map"]["format-version"] = 0; + json["acts-geometry-hierarchy-map"]["value-identifier"] = + "digitization-configuration"; + + // Write entries + json["entries"] = digi_cfg; + + // Write to file + outfile << std::setw(2) << json << std::endl; + + if (outfile.bad()) { + std::cout << "ERROR: Could not write to file"; + } + outfile.close(); +} + +} // namespace json + +void write_digitization_config(std::string_view filename, + const digitization_out_collection& digi_cfg, + data_format format) { + + // Construct the full filename. + std::string full_filename = filename.data(); + + // Decide how to write the file. + switch (format) { + case data_format::json: + return json::write_digitization_config(full_filename, digi_cfg); + default: + throw std::invalid_argument("Unsupported data format"); + } +} + +} // namespace io +} // namespace traccc diff --git a/tests/io/CMakeLists.txt b/tests/io/CMakeLists.txt index 4be1dd4250..04e3cd21dd 100644 --- a/tests/io/CMakeLists.txt +++ b/tests/io/CMakeLists.txt @@ -7,8 +7,9 @@ # Declare the io library test(s). traccc_add_test( io "test_binary.cpp" + "test_digi_configurator.cpp" "test_csv.cpp" "test_mapper.cpp" "test_event_map.cpp" LINK_LIBRARIES GTest::gtest_main traccc_tests_common - traccc::core traccc::io ) + traccc::core traccc::io detray::io ) diff --git a/tests/io/test_digi_configurator.cpp b/tests/io/test_digi_configurator.cpp new file mode 100644 index 0000000000..e124bbfd9c --- /dev/null +++ b/tests/io/test_digi_configurator.cpp @@ -0,0 +1,51 @@ +/** TRACCC library, part of the ACTS project (R&D line) + * + * (c) 2023 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +// Project include(s). +#include "traccc/io/read_digitization_config.hpp" +#include "traccc/io/utils.hpp" + +// Detray include(s) +#include "detray/core/detector.hpp" +#include "detray/io/common/detector_reader.hpp" + +// VecMem include(s). +#include + +// GTest include(s). +#include + +// System +#include + +// This defines the local frame test suite for the digitization configuration +TEST(io_digi_configurator, toy_detector) { + + // Read the digitization configuration file + auto digi_cfg = traccc::io::read_digitization_config( + "geometries/toy_detector/toy_detector-geometric-config.json"); + + // Get the detector as reference + vecmem::host_memory_resource host_mr; + detray::io::detector_reader_config reader_cfg{}; + reader_cfg.add_file(traccc::io::data_directory() + + "geometries/toy_detector/toy_detector_geometry.json"); + const auto [det, names] = + detray::io::read_detector>(host_mr, reader_cfg); + + std::size_t n_sensitives{0u}; + std::set volumes; + for (const auto& sf_desc : det.surface_lookup()) { + if (sf_desc.is_sensitive()) { + volumes.insert(sf_desc.volume()); + ++n_sensitives; + } + } + + ASSERT_EQ(volumes.size(), 18u); + ASSERT_EQ(n_sensitives, digi_cfg.size()); +}