From 90c9e92b64f4507d1be246631fe393ed0e87f1dc Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Wed, 20 Nov 2024 11:16:25 +0100 Subject: [PATCH 1/4] refactor!: Remove `bFieldMin` from `estimateTrackParamsFromSeed` (#3863) This parameter does not have any benefit for the implementation or interface. The user might have checked or guarantees that the magnetic field is strong enough. At the same time we will check for `NaN`s at the end. For this reason I propose to remove this parameter to keep the interface clean and concise. --- .../Seeding/EstimateTrackParamsFromSeed.hpp | 19 ++----------------- .../src/TrackParamsEstimationAlgorithm.cpp | 8 +++++++- .../src/PrototracksToParameters.cpp | 15 ++++++++++----- .../TruthTracking/ParticleSmearing.cpp | 6 ------ .../EstimateTrackParamsFromSeedTest.cpp | 2 +- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp b/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp index 5d6b4dbb97c..c59a55ad533 100644 --- a/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp +++ b/Core/include/Acts/Seeding/EstimateTrackParamsFromSeed.hpp @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -126,8 +125,6 @@ FreeVector estimateTrackParamsFromSeed(spacepoint_range_t spRange, /// @param surface is the surface of the bottom space point. The estimated bound /// track parameters will be represented also at this surface /// @param bField is the magnetic field vector -/// @param bFieldMin is the minimum magnetic field required to trigger the -/// estimation of q/pt /// @param logger A logger instance /// /// @return optional bound parameters @@ -135,7 +132,7 @@ template std::optional estimateTrackParamsFromSeed( const GeometryContext& gctx, spacepoint_iterator_t spBegin, spacepoint_iterator_t spEnd, const Surface& surface, const Vector3& bField, - ActsScalar bFieldMin, const Acts::Logger& logger = getDummyLogger()) { + const Acts::Logger& logger = getDummyLogger()) { // Check the number of provided space points std::size_t numSP = std::distance(spBegin, spEnd); if (numSP != 3) { @@ -143,19 +140,6 @@ std::optional estimateTrackParamsFromSeed( return std::nullopt; } - // Convert bField to Tesla - ActsScalar bFieldStrength = bField.norm(); - // Check if magnetic field is too small - if (bFieldStrength < bFieldMin) { - // @todo shall we use straight-line estimation and use default q/pt in such - // case? - ACTS_WARNING( - "The magnetic field at the bottom space point: B = " - << bFieldStrength / UnitConstants::T << " T is smaller than |B|_min = " - << bFieldMin / UnitConstants::T << " T. Estimation is not performed."); - return std::nullopt; - } - // The global positions of the bottom, middle and space points std::array spGlobalPositions = {Vector3::Zero(), Vector3::Zero(), Vector3::Zero()}; @@ -252,6 +236,7 @@ std::optional estimateTrackParamsFromSeed( params[eBoundLoc1] = bottomLocalPos.y(); params[eBoundTime] = spGlobalTimes[0].value_or(0.); + ActsScalar bFieldStrength = bField.norm(); // The estimated q/pt in [GeV/c]^-1 (note that the pt is the projection of // momentum on the transverse plane of the new frame) ActsScalar qOverPt = sign / (bFieldStrength * R); diff --git a/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp index c8a30c7147d..dfb0269a56d 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp @@ -112,10 +112,16 @@ ProcessCode TrackParamsEstimationAlgorithm::execute( } Acts::Vector3 field = *fieldRes; + if (field.norm() < m_cfg.bFieldMin) { + ACTS_WARNING("Magnetic field at seed " << iseed << " is too small " + << field.norm()); + continue; + } + // Estimate the track parameters from seed auto optParams = Acts::estimateTrackParamsFromSeed( ctx.geoContext, seed.sp().begin(), seed.sp().end(), *surface, field, - m_cfg.bFieldMin, logger()); + logger()); if (!optParams.has_value()) { ACTS_WARNING("Estimation of track parameters for seed " << iseed << " failed."); diff --git a/Examples/Algorithms/TrackFindingExaTrkX/src/PrototracksToParameters.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/PrototracksToParameters.cpp index c8d34f71bd2..9ffe49ae460 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/src/PrototracksToParameters.cpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/PrototracksToParameters.cpp @@ -154,16 +154,21 @@ ProcessCode PrototracksToParameters::execute( .geometryId(); const auto &surface = *m_cfg.geometry->findSurface(geoId); - auto field = m_cfg.magneticField->getField( + auto fieldRes = m_cfg.magneticField->getField( {bottomSP->x(), bottomSP->y(), bottomSP->z()}, bCache); - if (!field.ok()) { - ACTS_ERROR("Field lookup error: " << field.error()); + if (!fieldRes.ok()) { + ACTS_ERROR("Field lookup error: " << fieldRes.error()); return ProcessCode::ABORT; } + Acts::Vector3 field = *fieldRes; + + if (field.norm() < m_cfg.bFieldMin) { + ACTS_WARNING("Magnetic field at seed is too small " << field.norm()); + continue; + } auto pars = Acts::estimateTrackParamsFromSeed( - ctx.geoContext, seed.sp().begin(), seed.sp().end(), surface, *field, - m_cfg.bFieldMin); + ctx.geoContext, seed.sp().begin(), seed.sp().end(), surface, field); if (not pars) { ACTS_WARNING("Skip track because of bad params"); diff --git a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp index cfc62ab9da9..4c09134ef20 100644 --- a/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp +++ b/Examples/Algorithms/TruthTracking/ActsExamples/TruthTracking/ParticleSmearing.cpp @@ -10,21 +10,15 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/EventData/ParticleHypothesis.hpp" #include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp" #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" -#include "Acts/Utilities/Helpers.hpp" #include "Acts/Utilities/detail/periodic.hpp" #include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/EventData/Track.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Framework/RandomNumbers.hpp" -#include "ActsExamples/Utilities/GroupBy.hpp" -#include "ActsExamples/Utilities/Range.hpp" -#include "ActsFatras/EventData/Particle.hpp" -#include #include #include #include diff --git a/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp b/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp index e39a6a1a900..9aab6c1c70b 100644 --- a/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp +++ b/Tests/UnitTests/Core/Seeding/EstimateTrackParamsFromSeedTest.cpp @@ -176,7 +176,7 @@ BOOST_AUTO_TEST_CASE(trackparameters_estimation_test) { // Test the bound track parameters estimator auto fullParamsOpt = estimateTrackParamsFromSeed( geoCtx, spacePointPtrs.begin(), spacePointPtrs.end(), - *bottomSurface, bField, 0.1_T, *logger); + *bottomSurface, bField, *logger); BOOST_REQUIRE(fullParamsOpt.has_value()); const auto& estFullParams = fullParamsOpt.value(); BOOST_TEST_INFO( From ea831bf234c7847844b9168b940cfa008c224610 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Wed, 20 Nov 2024 13:04:44 +0100 Subject: [PATCH 2/4] refactor: Digitization cleanup in Examples (#3865) Some cleanup of our digitization code in Examples Pulled out of https://github.com/acts-project/acts/pull/3838 --- .../Digitization/DigitizationAlgorithm.hpp | 84 +++++++--- .../Digitization/DigitizationConfig.hpp | 155 +----------------- .../Digitization/DigitizationConfigurator.hpp | 13 +- .../DigitizationCoordinatesConverter.hpp | 8 +- .../Digitization/GeometricConfig.hpp | 98 +++++++++++ .../Digitization/ModuleClusters.hpp | 3 +- .../ActsExamples/Digitization/Smearers.hpp | 3 - .../src/DigitizationAlgorithm.cpp | 82 ++++----- .../Digitization/src/DigitizationConfig.cpp | 56 +------ .../src/DigitizationConfigurator.cpp | 4 +- .../src/DigitizationCoordinatesConverter.cpp | 17 +- .../ActsExamples/EventData/Cluster.hpp | 2 +- .../Io/Json/JsonDigitizationConfig.hpp | 3 - .../Io/Root/RootMeasurementWriter.hpp | 7 - .../Io/Root/src/RootMeasurementWriter.cpp | 3 - .../Python/python/acts/examples/simulation.py | 9 +- Examples/Python/src/Digitization.cpp | 46 +++--- Examples/Python/src/Output.cpp | 13 +- Examples/Python/tests/conftest.py | 4 +- Examples/Python/tests/test_detectors.py | 4 +- Examples/Python/tests/test_writer.py | 1 - .../Scripts/Python/digitization_config.py | 6 +- 22 files changed, 248 insertions(+), 373 deletions(-) create mode 100644 Examples/Algorithms/Digitization/include/ActsExamples/Digitization/GeometricConfig.hpp diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp index fd8937bd300..331408faa9a 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationAlgorithm.hpp @@ -8,18 +8,14 @@ #pragma once -#include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/Geometry/GeometryContext.hpp" #include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/Digitization/DigitizationConfig.hpp" #include "ActsExamples/Digitization/MeasurementCreation.hpp" -#include "ActsExamples/Digitization/SmearingConfig.hpp" #include "ActsExamples/EventData/Cluster.hpp" -#include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/EventData/SimHit.hpp" +#include "ActsExamples/EventData/SimParticle.hpp" #include "ActsExamples/Framework/DataHandle.hpp" #include "ActsExamples/Framework/IAlgorithm.hpp" #include "ActsExamples/Framework/ProcessCode.hpp" @@ -29,28 +25,62 @@ #include "ActsFatras/Digitization/UncorrelatedHitSmearer.hpp" #include -#include #include -#include -#include #include #include -namespace ActsFatras { -class Barcode; -} // namespace ActsFatras - namespace ActsExamples { -struct AlgorithmContext; /// Algorithm that turns simulated hits into measurements by truth smearing. class DigitizationAlgorithm final : public IAlgorithm { public: + class Config { + public: + /// Input collection of simulated hits. + std::string inputSimHits = "simhits"; + /// Output measurements collection. + std::string outputMeasurements = "measurements"; + /// Output cells map (geoID -> collection of cells). + std::string outputCells = "cells"; + /// Output cluster collection. + std::string outputClusters = "clusters"; + /// Output collection to map measured hits to contributing particles. + std::string outputMeasurementParticlesMap = "measurement_particles_map"; + /// Output collection to map measured hits to simulated hits. + std::string outputMeasurementSimHitsMap = "measurement_simhits_map"; + + /// Map of surface by identifier to allow local - to global + std::unordered_map + surfaceByIdentifier; + /// Random numbers tool. + std::shared_ptr randomNumbers = nullptr; + /// Flag to determine whether cell data should be written to the + /// `outputCells` collection; if true, writes (rather voluminous) cell data. + bool doOutputCells = false; + /// Flag to determine whether or not to run the clusterization; if true, + /// clusters, measurements, and sim-hit-maps are output. + bool doClusterization = true; + /// Do we merge hits or not + bool doMerge = false; + /// How close do parameters have to be to consider merged + double mergeNsigma = 1.0; + /// Consider clusters that share a corner as merged (8-cell connectivity) + bool mergeCommonCorner = false; + /// Energy deposit threshold for accepting a hit + /// For a generic readout frontend we assume 1000 e/h pairs, in Si each + /// e/h-pair requiers on average an energy of 3.65 eV (PDG review 2023, + /// Table 35.10) + /// @NOTE The default is set to 0 because this works only well with Geant4 + double minEnergyDeposit = 0.0; // 1000 * 3.65 * Acts::UnitConstants::eV; + /// The digitizers per GeometryIdentifiers + Acts::GeometryHierarchyMap digitizationConfigs; + }; + /// Construct the smearing algorithm. /// /// @param config is the algorithm configuration /// @param level is the logging level - DigitizationAlgorithm(DigitizationConfig config, Acts::Logging::Level level); + DigitizationAlgorithm(Config config, Acts::Logging::Level level); /// Build measurement from simulation hits at input. /// @@ -59,7 +89,7 @@ class DigitizationAlgorithm final : public IAlgorithm { ProcessCode execute(const AlgorithmContext& ctx) const override; /// Get const access to the config - const DigitizationConfig& config() const { return m_cfg; } + const Config& config() const { return m_cfg; } private: /// Helper method for creating digitized parameters from clusters @@ -89,7 +119,7 @@ class DigitizationAlgorithm final : public IAlgorithm { CombinedDigitizer<4>>; /// Configuration of the Algorithm - DigitizationConfig m_cfg; + Config m_cfg; /// Digitizers within geometry hierarchy Acts::GeometryHierarchyMap m_digitizers; /// Geometric digtizer @@ -98,17 +128,17 @@ class DigitizationAlgorithm final : public IAlgorithm { using CellsMap = std::map>; - ReadDataHandle m_simContainerReadHandle{this, - "SimHitContainer"}; - - WriteDataHandle m_measurementWriteHandle{ - this, "Measurements"}; - WriteDataHandle m_cellsWriteHandle{this, "Cells"}; - WriteDataHandle m_clusterWriteHandle{this, "Clusters"}; - WriteDataHandle> - m_measurementParticlesMapWriteHandle{this, "MeasurementParticlesMap"}; - WriteDataHandle> m_measurementSimHitsMapWriteHandle{ - this, "MeasurementSimHitsMap"}; + ReadDataHandle m_inputHits{this, "InputHits"}; + + WriteDataHandle m_outputMeasurements{ + this, "OutputMeasurements"}; + WriteDataHandle m_outputCells{this, "OutputCells"}; + WriteDataHandle m_outputClusters{this, "OutputClusters"}; + + WriteDataHandle> m_outputMeasurementParticlesMap{ + this, "OutputMeasurementParticlesMap"}; + WriteDataHandle> m_outputMeasurementSimHitsMap{ + this, "OutputMeasurementSimHitsMap"}; /// Construct a fixed-size smearer from a configuration. /// diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp index 6a933f50075..213abe4d322 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfig.hpp @@ -8,107 +8,12 @@ #pragma once -#include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/Definitions/Units.hpp" #include "Acts/Geometry/GeometryHierarchyMap.hpp" -#include "Acts/Utilities/BinUtility.hpp" -#include "Acts/Utilities/BinningType.hpp" -#include "Acts/Utilities/Logger.hpp" -#include "Acts/Utilities/Result.hpp" -#include "ActsExamples/Digitization/DigitizationConfig.hpp" -#include "ActsExamples/Digitization/Smearers.hpp" +#include "ActsExamples/Digitization/GeometricConfig.hpp" #include "ActsExamples/Digitization/SmearingConfig.hpp" -#include "ActsExamples/Framework/RandomNumbers.hpp" -#include "ActsFatras/Digitization/UncorrelatedHitSmearer.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Acts { -class GeometryIdentifier; -} // namespace Acts namespace ActsExamples { -/// Configuration struct for geometric digitization -/// -/// If this is defined, then the geometric digitization -/// will create clusters with cells. -/// The BinUtility defines the segmentation and which parameters -/// are defined by this. -/// -struct GeometricConfig { - // The dimensions of the measurement - std::vector indices = {}; - - // The (multidimensional) binning definition for the segmentation of the - // sensor - Acts::BinUtility segmentation; - - // The thickness of the sensor - double thickness = 0.; - - /// The charge smearer - ActsFatras::SingleParameterSmearFunction - chargeSmearer = Digitization::Exact(0); - - // The threshold below a cell activation is ignored - double threshold = 0.; - - // Whether to assume digital readout (activation is either 0 or 1) - bool digital = false; - - // Flag as strip - bool strip = false; - - /// The variances for this digitization - std::map> varianceMap = {}; - - /// Charge generation (configurable via the chargeSmearer) - Acts::ActsScalar charge(Acts::ActsScalar path, RandomEngine &rng) const { - if (!chargeSmearer) { - return path; - } - auto res = chargeSmearer(path, rng); - if (res.ok()) { - return std::max(0.0, res->first); - } else { - throw std::runtime_error(res.error().message()); - } - } - - /// This generates the variances for a given cluster - /// - /// @note either the variances are directly taken from a pre-read - /// variance map, or they are generated from the pitch size - /// - /// @param csizes is the cluster size in the different dimensions - /// @param cmins is the cluster minimum in the different dimensions - /// - /// @return a vector of variances for the cluster - std::vector variances( - const std::array &csizes, - const std::array &cmins) const; - - /// Drift generation (currently not implemented) - /// Takes as an argument the position, and a random engine - /// @return drift direction in local 3D coordinates - Acts::Vector3 drift(const Acts::Vector3 & /*position*/, - RandomEngine & /*rng*/) const { - return Acts::Vector3(0., 0., 0.); - }; -}; - /// Configuration struct for the Digitization algorithm /// /// It contains: @@ -116,63 +21,9 @@ struct GeometricConfig { /// - optional SmearingConfig struct DigiComponentsConfig { GeometricConfig geometricDigiConfig; - SmearingConfig smearingDigiConfig = {}; + SmearingConfig smearingDigiConfig; }; -class DigitizationConfig { - public: - DigitizationConfig(bool merge, double sigma, bool commonCorner) - : DigitizationConfig(merge, sigma, commonCorner, - Acts::GeometryHierarchyMap()) { - } - - DigitizationConfig( - bool doMerge, double mergeNsigma, bool mergeCommonCorner, - Acts::GeometryHierarchyMap &&digiCfgs); - - explicit DigitizationConfig( - Acts::GeometryHierarchyMap &&digiCfgs); +using DigiConfigContainer = Acts::GeometryHierarchyMap; - /// Input collection of simulated hits. - std::string inputSimHits = "simhits"; - /// Output measurements collection. - std::string outputMeasurements = "measurements"; - /// Output cells map (geoID -> collection of cells). - std::string outputCells = "cells"; - /// Output cluster collection. - std::string outputClusters = "clusters"; - /// Output collection to map measured hits to contributing particles. - std::string outputMeasurementParticlesMap = "measurement_particles_map"; - /// Output collection to map measured hits to simulated hits. - std::string outputMeasurementSimHitsMap = "measurement_simhits_map"; - /// Map of surface by identifier to allow local - to global - std::unordered_map - surfaceByIdentifier; - /// Random numbers tool. - std::shared_ptr randomNumbers = nullptr; - /// Flag to determine whether cell data should be written to the - /// `outputCells` collection; if true, writes (rather voluminous) cell data. - bool doOutputCells = false; - /// Flag to determine whether or not to run the clusterization; if true, - /// clusters, measurements, and sim-hit-maps are output. - bool doClusterization = true; - /// Do we merge hits or not - bool doMerge; - /// How close do parameters have to be to consider merged - const double mergeNsigma; - /// Consider clusters that share a corner as merged (8-cell connectivity) - const bool mergeCommonCorner; - /// Energy deposit threshold for accepting a hit - /// For a generic readout frontend we assume 1000 e/h pairs, in Si each - /// e/h-pair requiers on average an energy of 3.65 eV (PDG review 2023, - /// Table 35.10) - /// @NOTE The default is set to 0 because this works only well with Geant4 - double minEnergyDeposit = 0.0; // 1000 * 3.65 * Acts::UnitConstants::eV; - /// The digitizers per GeometryIdentifiers - Acts::GeometryHierarchyMap digitizationConfigs; - - std::vector< - std::pair>> - getBoundIndices() const; -}; } // namespace ActsExamples diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfigurator.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfigurator.hpp index 55754eb79f7..3e6d01dea0f 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfigurator.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationConfigurator.hpp @@ -11,21 +11,9 @@ #include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Geometry/TrackingGeometry.hpp" -#include "Acts/Surfaces/AnnulusBounds.hpp" -#include "Acts/Surfaces/DiscTrapezoidBounds.hpp" -#include "Acts/Surfaces/RadialBounds.hpp" -#include "Acts/Surfaces/RectangleBounds.hpp" -#include "Acts/Surfaces/SurfaceBounds.hpp" -#include "Acts/Surfaces/TrapezoidBounds.hpp" -#include "Acts/Utilities/BinUtility.hpp" -#include "Acts/Utilities/BinningData.hpp" -#include "ActsExamples/Digitization/DigitizationAlgorithm.hpp" #include "ActsExamples/Digitization/DigitizationConfig.hpp" #include -#include -#include -#include namespace Acts { class Surface; @@ -66,4 +54,5 @@ struct DigitizationConfigurator { /// it adds an appropriate entry into the digitisation configuration void operator()(const Acts::Surface* surface); }; + } // namespace ActsExamples diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationCoordinatesConverter.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationCoordinatesConverter.hpp index 21c4f619b8a..382e113844f 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationCoordinatesConverter.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/DigitizationCoordinatesConverter.hpp @@ -8,6 +8,7 @@ #pragma once +#include "ActsExamples/Digitization/DigitizationAlgorithm.hpp" #include "ActsExamples/Digitization/DigitizationConfig.hpp" namespace ActsExamples { @@ -18,10 +19,11 @@ class DigitizationCoordinatesConverter final { /// Construct the converter /// /// @param config is the configuration - explicit DigitizationCoordinatesConverter(DigitizationConfig config); + explicit DigitizationCoordinatesConverter( + DigitizationAlgorithm::Config config); /// Get const access to the config - const DigitizationConfig& config() const { return m_cfg; } + const DigitizationAlgorithm::Config& config() const { return m_cfg; } /// Convert the hit coordinates to the local frame. std::tuple globalToLocal(std::uint64_t moduleId, double x, @@ -33,7 +35,7 @@ class DigitizationCoordinatesConverter final { private: /// Configuration - DigitizationConfig m_cfg; + DigitizationAlgorithm::Config m_cfg; }; } // namespace ActsExamples diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/GeometricConfig.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/GeometricConfig.hpp new file mode 100644 index 00000000000..6b4348e20f6 --- /dev/null +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/GeometricConfig.hpp @@ -0,0 +1,98 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/TrackParametrization.hpp" +#include "Acts/Utilities/BinUtility.hpp" +#include "Acts/Utilities/Result.hpp" +#include "ActsExamples/Digitization/Smearers.hpp" +#include "ActsExamples/Framework/RandomNumbers.hpp" +#include "ActsFatras/Digitization/UncorrelatedHitSmearer.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace ActsExamples { + +/// Configuration struct for geometric digitization +/// +/// If this is defined, then the geometric digitization +/// will create clusters with cells. +/// The BinUtility defines the segmentation and which parameters +/// are defined by this. +/// +struct GeometricConfig { + // The dimensions of the measurement + std::vector indices = {}; + + // The (multidimensional) binning definition for the segmentation of the + // sensor + Acts::BinUtility segmentation; + + // The thickness of the sensor + double thickness = 0.; + + /// The charge smearer + ActsFatras::SingleParameterSmearFunction + chargeSmearer = Digitization::Exact(0); + + // The threshold below a cell activation is ignored + double threshold = 0.; + + // Whether to assume digital readout (activation is either 0 or 1) + bool digital = false; + + // Flag as strip + bool strip = false; + + /// The variances for this digitization + std::map> varianceMap = {}; + + /// Charge generation (configurable via the chargeSmearer) + Acts::ActsScalar charge(Acts::ActsScalar path, RandomEngine &rng) const { + if (!chargeSmearer) { + return path; + } + auto res = chargeSmearer(path, rng); + if (res.ok()) { + return std::max(0.0, res->first); + } else { + throw std::runtime_error(res.error().message()); + } + } + + /// This generates the variances for a given cluster + /// + /// @note either the variances are directly taken from a pre-read + /// variance map, or they are generated from the pitch size + /// + /// @param csizes is the cluster size in the different dimensions + /// @param cmins is the cluster minimum in the different dimensions + /// + /// @return a vector of variances for the cluster + std::vector variances( + const std::array &csizes, + const std::array &cmins) const; + + /// Drift generation (currently not implemented) + /// Takes as an argument the position, and a random engine + /// @return drift direction in local 3D coordinates + Acts::Vector3 drift(const Acts::Vector3 & /*position*/, + RandomEngine & /*rng*/) const { + return Acts::Vector3(0., 0., 0.); + }; +}; + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/ModuleClusters.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/ModuleClusters.hpp index 675f86af07d..452111db2eb 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/ModuleClusters.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/ModuleClusters.hpp @@ -16,10 +16,8 @@ #include "ActsExamples/EventData/Cluster.hpp" #include "ActsExamples/EventData/SimHit.hpp" -#include #include #include -#include #include #include #include @@ -69,4 +67,5 @@ class ModuleClusters { std::vector> mergeParameters( std::vector values); }; + } // namespace ActsExamples diff --git a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/Smearers.hpp b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/Smearers.hpp index c51b8e26b64..8d0130831ae 100644 --- a/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/Smearers.hpp +++ b/Examples/Algorithms/Digitization/include/ActsExamples/Digitization/Smearers.hpp @@ -12,13 +12,10 @@ #include "Acts/Utilities/Result.hpp" #include "ActsExamples/Framework/RandomNumbers.hpp" #include "ActsFatras/Digitization/DigitizationError.hpp" -#include "ActsFatras/Digitization/UncorrelatedHitSmearer.hpp" #include #include #include -#include -#include #include namespace ActsExamples::Digitization { diff --git a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp index 72f3905ab57..07019be74ca 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationAlgorithm.cpp @@ -11,35 +11,24 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" -#include "Acts/Geometry/TrackingGeometry.hpp" -#include "Acts/Surfaces/Surface.hpp" #include "Acts/Utilities/BinUtility.hpp" -#include "Acts/Utilities/Result.hpp" #include "ActsExamples/Digitization/ModuleClusters.hpp" -#include "ActsExamples/EventData/GeometryContainers.hpp" -#include "ActsExamples/EventData/Index.hpp" -#include "ActsExamples/EventData/SimHit.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" -#include "ActsExamples/Utilities/GroupBy.hpp" -#include "ActsExamples/Utilities/Range.hpp" #include "ActsFatras/EventData/Barcode.hpp" -#include "ActsFatras/EventData/Hit.hpp" #include #include -#include -#include #include #include -#include #include #include #include -ActsExamples::DigitizationAlgorithm::DigitizationAlgorithm( - DigitizationConfig config, Acts::Logging::Level level) - : ActsExamples::IAlgorithm("DigitizationAlgorithm", level), - m_cfg(std::move(config)) { +namespace ActsExamples { + +DigitizationAlgorithm::DigitizationAlgorithm(Config config, + Acts::Logging::Level level) + : IAlgorithm("DigitizationAlgorithm", level), m_cfg(std::move(config)) { if (m_cfg.inputSimHits.empty()) { throw std::invalid_argument("Missing simulated hits input collection"); } @@ -69,12 +58,11 @@ ActsExamples::DigitizationAlgorithm::DigitizationAlgorithm( "Missing hit-to-simulated-hits map output collection"); } - m_measurementWriteHandle.initialize(m_cfg.outputMeasurements); - m_clusterWriteHandle.initialize(m_cfg.outputClusters); - m_measurementParticlesMapWriteHandle.initialize( + m_outputMeasurements.initialize(m_cfg.outputMeasurements); + m_outputClusters.initialize(m_cfg.outputClusters); + m_outputMeasurementParticlesMap.initialize( m_cfg.outputMeasurementParticlesMap); - m_measurementSimHitsMapWriteHandle.initialize( - m_cfg.outputMeasurementSimHitsMap); + m_outputMeasurementSimHitsMap.initialize(m_cfg.outputMeasurementSimHitsMap); } if (m_cfg.doOutputCells) { @@ -82,10 +70,10 @@ ActsExamples::DigitizationAlgorithm::DigitizationAlgorithm( throw std::invalid_argument("Missing cell output collection"); } - m_cellsWriteHandle.initialize(m_cfg.outputCells); + m_outputCells.initialize(m_cfg.outputCells); } - m_simContainerReadHandle.initialize(m_cfg.inputSimHits); + m_inputHits.initialize(m_cfg.inputSimHits); // Create the digitizers from the configuration std::vector> digitizerInput; @@ -139,16 +127,16 @@ ActsExamples::DigitizationAlgorithm::DigitizationAlgorithm( m_digitizers = Acts::GeometryHierarchyMap(digitizerInput); } -ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( - const AlgorithmContext& ctx) const { +ProcessCode DigitizationAlgorithm::execute(const AlgorithmContext& ctx) const { // Retrieve input - const auto& simHits = m_simContainerReadHandle(ctx); + const auto& simHits = m_inputHits(ctx); ACTS_DEBUG("Loaded " << simHits.size() << " sim hits"); // Prepare output containers // need list here for stable addresses MeasurementContainer measurements; ClusterContainer clusters; + IndexMultimap measurementParticlesMap; IndexMultimap measurementSimHitsMap; measurements.reserve(simHits.size()); @@ -271,7 +259,7 @@ ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( // Store the cell data into a map. if (m_cfg.doOutputCells) { std::vector cells; - for (const auto& [dParameters, simhits] : + for (const auto& [dParameters, simHitsIdxs] : digitizeParametersResult) { for (const auto& cell : dParameters.cluster.channels) { cells.push_back(cell); @@ -281,21 +269,18 @@ ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( } if (m_cfg.doClusterization) { - for (auto& [dParameters, simhits] : digitizeParametersResult) { - // The measurement container is unordered and the index under - // which the measurement will be stored is known before adding it. - Index measurementIdx = measurements.size(); - - createMeasurement(measurements, moduleGeoId, dParameters); + for (auto& [dParameters, simHitsIdxs] : digitizeParametersResult) { + auto measurement = + createMeasurement(measurements, moduleGeoId, dParameters); clusters.emplace_back(std::move(dParameters.cluster)); - // this digitization does hit merging so there can be more than - // one mapping entry for each digitized hit. - for (auto simHitIdx : simhits) { + + for (auto [i, simHitIdx] : Acts::enumerate(simHitsIdxs)) { measurementParticlesMap.emplace_hint( - measurementParticlesMap.end(), measurementIdx, + measurementParticlesMap.end(), measurement.index(), simHits.nth(simHitIdx)->particleId()); measurementSimHitsMap.emplace_hint(measurementSimHitsMap.end(), - measurementIdx, simHitIdx); + measurement.index(), + simHitIdx); } } } @@ -309,23 +294,22 @@ ActsExamples::ProcessCode ActsExamples::DigitizationAlgorithm::execute( << " skipped in Digitization. Enable DEBUG mode to see more details."); } - if (m_cfg.doOutputCells) { - m_cellsWriteHandle(ctx, std::move(cellsMap)); + if (m_cfg.doClusterization) { + m_outputMeasurements(ctx, std::move(measurements)); + m_outputClusters(ctx, std::move(clusters)); + + m_outputMeasurementParticlesMap(ctx, std::move(measurementParticlesMap)); + m_outputMeasurementSimHitsMap(ctx, std::move(measurementSimHitsMap)); } - if (m_cfg.doClusterization) { - m_measurementWriteHandle(ctx, std::move(measurements)); - m_clusterWriteHandle(ctx, std::move(clusters)); - m_measurementParticlesMapWriteHandle(ctx, - std::move(measurementParticlesMap)); - m_measurementSimHitsMapWriteHandle(ctx, std::move(measurementSimHitsMap)); + if (m_cfg.doOutputCells) { + m_outputCells(ctx, std::move(cellsMap)); } return ProcessCode::SUCCESS; } -ActsExamples::DigitizedParameters -ActsExamples::DigitizationAlgorithm::localParameters( +DigitizedParameters DigitizationAlgorithm::localParameters( const GeometricConfig& geoCfg, const std::vector& channels, RandomEngine& rng) const { @@ -377,3 +361,5 @@ ActsExamples::DigitizationAlgorithm::localParameters( return dParameters; } + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Digitization/src/DigitizationConfig.cpp b/Examples/Algorithms/Digitization/src/DigitizationConfig.cpp index e19452d1e7a..8c23ba729d1 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationConfig.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationConfig.cpp @@ -8,59 +8,9 @@ #include "ActsExamples/Digitization/DigitizationConfig.hpp" -#include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/Geometry/GeometryIdentifier.hpp" -#include "ActsExamples/Digitization/SmearingConfig.hpp" +namespace ActsExamples { -namespace { - -enum SmearingTypes : int { - eGauss = 0, - eGaussTruncated = 1, - eGaussClipped = 2, - eUniform = 3, - eDigital = 4, -}; - -} // namespace - -ActsExamples::DigitizationConfig::DigitizationConfig( - bool merge, double sigma, bool commonCorner, - Acts::GeometryHierarchyMap&& digiCfgs) - : doMerge(merge), mergeNsigma(sigma), mergeCommonCorner(commonCorner) { - digitizationConfigs = std::move(digiCfgs); -} - -ActsExamples::DigitizationConfig::DigitizationConfig( - Acts::GeometryHierarchyMap&& digiCfgs) - : doMerge(false), mergeNsigma(1.0), mergeCommonCorner(false) { - digitizationConfigs = std::move(digiCfgs); -} - -std::vector< - std::pair>> -ActsExamples::DigitizationConfig::getBoundIndices() const { - std::vector< - std::pair>> - bIndexInput; - - for (std::size_t ibi = 0; ibi < digitizationConfigs.size(); ++ibi) { - Acts::GeometryIdentifier geoID = digitizationConfigs.idAt(ibi); - const auto dCfg = digitizationConfigs.valueAt(ibi); - std::vector boundIndices; - boundIndices.insert(boundIndices.end(), - dCfg.geometricDigiConfig.indices.begin(), - dCfg.geometricDigiConfig.indices.end()); - // we assume nobody will add multiple smearers to a single bound index - for (const auto& c : dCfg.smearingDigiConfig) { - boundIndices.push_back(c.index); - } - bIndexInput.push_back({geoID, boundIndices}); - } - return bIndexInput; -} - -std::vector ActsExamples::GeometricConfig::variances( +std::vector GeometricConfig::variances( const std::array& csizes, const std::array& cmins) const { std::vector rVariances; @@ -80,3 +30,5 @@ std::vector ActsExamples::GeometricConfig::variances( } return rVariances; } + +} // namespace ActsExamples diff --git a/Examples/Algorithms/Digitization/src/DigitizationConfigurator.cpp b/Examples/Algorithms/Digitization/src/DigitizationConfigurator.cpp index e7bd2ddfa09..bbf6ec7583b 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationConfigurator.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationConfigurator.cpp @@ -9,7 +9,6 @@ #include "ActsExamples/Digitization/DigitizationConfigurator.hpp" #include "Acts/Definitions/Algebra.hpp" -#include "Acts/Definitions/TrackParametrization.hpp" #include "Acts/Surfaces/AnnulusBounds.hpp" #include "Acts/Surfaces/DiscTrapezoidBounds.hpp" #include "Acts/Surfaces/RadialBounds.hpp" @@ -25,9 +24,9 @@ #include #include -#include namespace { + /// @note This does not really compare if the configs are equal, therefore /// it is no operator==. The contained std::function types cannot really /// be checked for equality. @@ -47,6 +46,7 @@ bool digiConfigMaybeEqual(ActsExamples::DigiComponentsConfig &a, ag.thickness == bg.thickness && ag.threshold == bg.threshold && ag.digital == bg.digital); } + } // namespace void ActsExamples::DigitizationConfigurator::operator()( diff --git a/Examples/Algorithms/Digitization/src/DigitizationCoordinatesConverter.cpp b/Examples/Algorithms/Digitization/src/DigitizationCoordinatesConverter.cpp index e83e7be995b..b996306fb13 100644 --- a/Examples/Algorithms/Digitization/src/DigitizationCoordinatesConverter.cpp +++ b/Examples/Algorithms/Digitization/src/DigitizationCoordinatesConverter.cpp @@ -10,21 +10,22 @@ #include "Acts/Definitions/Algebra.hpp" #include "Acts/Geometry/GeometryContext.hpp" +#include "ActsExamples/Digitization/DigitizationAlgorithm.hpp" -#include #include #include -ActsExamples::DigitizationCoordinatesConverter:: - DigitizationCoordinatesConverter(DigitizationConfig config) +namespace ActsExamples { + +DigitizationCoordinatesConverter::DigitizationCoordinatesConverter( + DigitizationAlgorithm::Config config) : m_cfg(std::move(config)) { if (m_cfg.surfaceByIdentifier.empty()) { throw std::invalid_argument("Missing Surface-GeometryID association map"); } } -std::tuple -ActsExamples::DigitizationCoordinatesConverter::globalToLocal( +std::tuple DigitizationCoordinatesConverter::globalToLocal( std::uint64_t moduleId, double x, double y, double z) const { const Acts::GeometryIdentifier moduleGeoId = moduleId; auto surfaceItr = m_cfg.surfaceByIdentifier.find(moduleGeoId); @@ -44,8 +45,8 @@ ActsExamples::DigitizationCoordinatesConverter::globalToLocal( } std::tuple -ActsExamples::DigitizationCoordinatesConverter::localToGlobal( - std::uint64_t moduleId, double x, double y) const { +DigitizationCoordinatesConverter::localToGlobal(std::uint64_t moduleId, + double x, double y) const { const Acts::GeometryIdentifier moduleGeoId = moduleId; auto surfaceItr = m_cfg.surfaceByIdentifier.find(moduleGeoId); if (surfaceItr == m_cfg.surfaceByIdentifier.end()) { @@ -61,3 +62,5 @@ ActsExamples::DigitizationCoordinatesConverter::localToGlobal( return {pos2Global.x(), pos2Global.y(), pos2Global.z()}; } + +} // namespace ActsExamples diff --git a/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp b/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp index 3eb850b1214..bfd81c9f130 100644 --- a/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp +++ b/Examples/Framework/include/ActsExamples/EventData/Cluster.hpp @@ -8,10 +8,10 @@ #pragma once +#include "Acts/Definitions/Algebra.hpp" #include "ActsFatras/Digitization/Segmentizer.hpp" #include -#include #include namespace ActsExamples { diff --git a/Examples/Io/Json/include/ActsExamples/Io/Json/JsonDigitizationConfig.hpp b/Examples/Io/Json/include/ActsExamples/Io/Json/JsonDigitizationConfig.hpp index 11e063acaa8..a67756b1dbc 100644 --- a/Examples/Io/Json/include/ActsExamples/Io/Json/JsonDigitizationConfig.hpp +++ b/Examples/Io/Json/include/ActsExamples/Io/Json/JsonDigitizationConfig.hpp @@ -9,12 +9,10 @@ #pragma once #include "Acts/Geometry/GeometryHierarchyMap.hpp" -#include "Acts/Plugins/Json/ActsJson.hpp" #include "Acts/Plugins/Json/GeometryHierarchyMapJsonConverter.hpp" #include "ActsExamples/Digitization/DigitizationConfig.hpp" #include "ActsExamples/Digitization/SmearingConfig.hpp" -#include #include #include @@ -44,7 +42,6 @@ void writeDigiConfigToJson( const Acts::GeometryHierarchyMap& cfg, const std::string& path); -using DigiConfigContainer = Acts::GeometryHierarchyMap; using DigiConfigConverter = Acts::GeometryHierarchyMapJsonConverter; diff --git a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMeasurementWriter.hpp b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMeasurementWriter.hpp index 3958e845fb2..a578295e530 100644 --- a/Examples/Io/Root/include/ActsExamples/Io/Root/RootMeasurementWriter.hpp +++ b/Examples/Io/Root/include/ActsExamples/Io/Root/RootMeasurementWriter.hpp @@ -8,9 +8,6 @@ #pragma once -#include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/Geometry/GeometryContext.hpp" -#include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/Geometry/GeometryIdentifier.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/EventData/Cluster.hpp" @@ -24,7 +21,6 @@ #include #include #include -#include class TFile; class TTree; @@ -34,7 +30,6 @@ class TrackingGeometry; } // namespace Acts namespace ActsExamples { -struct AlgorithmContext; /// @class RootMeasurementWriter /// @@ -64,8 +59,6 @@ class RootMeasurementWriter final : public WriterT { /// file access mode std::string fileMode = "RECREATE"; - /// The indices for this digitization configurations - Acts::GeometryHierarchyMap> boundIndices; /// Map of the geometry identifier to the surface std::unordered_map surfaceByIdentifier; diff --git a/Examples/Io/Root/src/RootMeasurementWriter.cpp b/Examples/Io/Root/src/RootMeasurementWriter.cpp index b7ab332fdea..df095557664 100644 --- a/Examples/Io/Root/src/RootMeasurementWriter.cpp +++ b/Examples/Io/Root/src/RootMeasurementWriter.cpp @@ -9,20 +9,17 @@ #include "ActsExamples/Io/Root/RootMeasurementWriter.hpp" #include "Acts/Definitions/TrackParametrization.hpp" -#include "Acts/Utilities/Enumerate.hpp" #include "ActsExamples/EventData/AverageSimHits.hpp" #include "ActsExamples/EventData/Index.hpp" #include "ActsExamples/EventData/Measurement.hpp" #include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Utilities/Range.hpp" -#include #include #include #include #include #include -#include #include #include diff --git a/Examples/Python/python/acts/examples/simulation.py b/Examples/Python/python/acts/examples/simulation.py index 21c4149661f..65b6820d8b8 100644 --- a/Examples/Python/python/acts/examples/simulation.py +++ b/Examples/Python/python/acts/examples/simulation.py @@ -778,8 +778,8 @@ def addDigitization( rnd = rnd or acts.examples.RandomNumbers() # Digitization - digiCfg = acts.examples.DigitizationConfig( - acts.examples.readDigiConfigFromJson( + digiCfg = acts.examples.DigitizationAlgorithm.Config( + digitizationConfigs=acts.examples.readDigiConfigFromJson( str(digiConfigFile), ), surfaceByIdentifier=trackingGeometry.geoIdSurfaceMap(), @@ -788,7 +788,9 @@ def addDigitization( outputMeasurements="measurements", outputMeasurementParticlesMap="measurement_particles_map", outputMeasurementSimHitsMap="measurement_simhits_map", - doMerge=doMerge, + **acts.examples.defaultKWArgs( + doMerge=doMerge, + ), ) # Not sure how to do this in our style @@ -811,7 +813,6 @@ def addDigitization( filePath=str(outputDirRoot / f"{digiAlg.config.outputMeasurements}.root"), surfaceByIdentifier=trackingGeometry.geoIdSurfaceMap(), ) - rmwConfig.addBoundIndicesFromDigiConfig(digiAlg.config) s.addWriter(acts.examples.RootMeasurementWriter(rmwConfig, customLogLevel())) if outputDirCsv is not None: diff --git a/Examples/Python/src/Digitization.cpp b/Examples/Python/src/Digitization.cpp index 948bcc9593b..5bcce4b74ca 100644 --- a/Examples/Python/src/Digitization.cpp +++ b/Examples/Python/src/Digitization.cpp @@ -6,33 +6,22 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -#include "Acts/Definitions/Algebra.hpp" -#include "Acts/Geometry/GeometryHierarchyMap.hpp" #include "Acts/Plugins/Python/Utilities.hpp" #include "Acts/Utilities/Logger.hpp" #include "ActsExamples/Digitization/DigitizationAlgorithm.hpp" #include "ActsExamples/Digitization/DigitizationConfig.hpp" #include "ActsExamples/Digitization/DigitizationConfigurator.hpp" #include "ActsExamples/Digitization/DigitizationCoordinatesConverter.hpp" -#include "ActsExamples/Framework/AlgorithmContext.hpp" #include "ActsExamples/Io/Json/JsonDigitizationConfig.hpp" #include #include #include #include -#include #include #include -namespace Acts { -class GeometryIdentifier; -} // namespace Acts -namespace ActsExamples { -class IAlgorithm; -} // namespace ActsExamples - namespace py = pybind11; using namespace ActsExamples; @@ -47,19 +36,18 @@ void addDigitization(Context& ctx) { mex.def("writeDigiConfigToJson", ActsExamples::writeDigiConfigToJson); { - using Config = ActsExamples::DigitizationConfig; + using Config = ActsExamples::DigitizationAlgorithm::Config; - py::class_>( - mex, "DigitizationAlgorithm") - .def(py::init(), py::arg("config"), - py::arg("level")) - .def_property_readonly("config", - &ActsExamples::DigitizationAlgorithm::config); + auto a = py::class_>( + mex, "DigitizationAlgorithm") + .def(py::init(), + py::arg("config"), py::arg("level")) + .def_property_readonly( + "config", &ActsExamples::DigitizationAlgorithm::config); - auto c = py::class_(mex, "DigitizationConfig") - .def(py::init>()); + auto c = py::class_(a, "Config").def(py::init<>()); ACTS_PYTHON_STRUCT_BEGIN(c, Config); ACTS_PYTHON_MEMBER(inputSimHits); @@ -81,10 +69,15 @@ void addDigitization(Context& ctx) { patchKwargsConstructor(c); - py::class_(mex, "DigiComponentsConfig"); + auto cc = py::class_(mex, "DigiComponentsConfig") + .def(py::init<>()); + + ACTS_PYTHON_STRUCT_BEGIN(cc, DigiComponentsConfig); + ACTS_PYTHON_MEMBER(geometricDigiConfig); + ACTS_PYTHON_MEMBER(smearingDigiConfig); + ACTS_PYTHON_STRUCT_END(); - py::class_>( - mex, "GeometryHierarchyMap_DigiComponentsConfig") + py::class_(mex, "DigiConfigContainer") .def(py::init>>()); } @@ -107,7 +100,8 @@ void addDigitization(Context& ctx) { py::class_>( mex, "DigitizationCoordinatesConverter") - .def(py::init(), py::arg("config")) + .def(py::init(), + py::arg("config")) .def_property_readonly( "config", &ActsExamples::DigitizationCoordinatesConverter::config) .def("globalToLocal", diff --git a/Examples/Python/src/Output.cpp b/Examples/Python/src/Output.cpp index 5adead2a9b4..5f174777429 100644 --- a/Examples/Python/src/Output.cpp +++ b/Examples/Python/src/Output.cpp @@ -12,8 +12,7 @@ #include "Acts/Utilities/Logger.hpp" #include "Acts/Visualization/IVisualization3D.hpp" #include "Acts/Visualization/ViewConfig.hpp" -#include "ActsExamples/Digitization/DigitizationConfig.hpp" -#include "ActsExamples/Framework/ProcessCode.hpp" +#include "ActsExamples/Digitization/DigitizationAlgorithm.hpp" #include "ActsExamples/Io/Csv/CsvBFieldWriter.hpp" #include "ActsExamples/Io/Csv/CsvExaTrkXGraphWriter.hpp" #include "ActsExamples/Io/Csv/CsvMeasurementWriter.hpp" @@ -50,10 +49,8 @@ #include "ActsExamples/Plugins/Obj/ObjPropagationStepsWriter.hpp" #include "ActsExamples/Plugins/Obj/ObjTrackingGeometryWriter.hpp" -#include #include #include -#include #include #include @@ -268,13 +265,6 @@ void addOutput(Context& ctx) { auto c = py::class_(w, "Config").def(py::init<>()); - c.def("addBoundIndicesFromDigiConfig", - [](Writer::Config& self, const DigitizationConfig& digiCfg) { - self.boundIndices = - Acts::GeometryHierarchyMap>( - digiCfg.getBoundIndices()); - }); - ACTS_PYTHON_STRUCT_BEGIN(c, Writer::Config); ACTS_PYTHON_MEMBER(inputMeasurements); ACTS_PYTHON_MEMBER(inputClusters); @@ -282,7 +272,6 @@ void addOutput(Context& ctx) { ACTS_PYTHON_MEMBER(inputMeasurementSimHitsMap); ACTS_PYTHON_MEMBER(filePath); ACTS_PYTHON_MEMBER(fileMode); - ACTS_PYTHON_MEMBER(boundIndices); ACTS_PYTHON_MEMBER(surfaceByIdentifier); ACTS_PYTHON_STRUCT_END(); } diff --git a/Examples/Python/tests/conftest.py b/Examples/Python/tests/conftest.py index b2015c3f6a0..71c0899fe3f 100644 --- a/Examples/Python/tests/conftest.py +++ b/Examples/Python/tests/conftest.py @@ -357,8 +357,8 @@ def _factory(s): s.addAlgorithm(simAlg) # Digitization - digiCfg = acts.examples.DigitizationConfig( - acts.examples.readDigiConfigFromJson( + digiCfg = acts.examples.DigitizationAlgorithm.Config( + digitizationConfigs=acts.examples.readDigiConfigFromJson( str( Path(__file__).parent.parent.parent.parent / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" diff --git a/Examples/Python/tests/test_detectors.py b/Examples/Python/tests/test_detectors.py index c4d2c6ea9ed..3de27524db9 100644 --- a/Examples/Python/tests/test_detectors.py +++ b/Examples/Python/tests/test_detectors.py @@ -161,8 +161,8 @@ def eq(self, other): def test_coordinate_converter(trk_geo): - digiCfg = acts.examples.DigitizationConfig( - acts.examples.readDigiConfigFromJson( + digiCfg = acts.examples.DigitizationAlgorithm.Config( + digitizationConfigs=acts.examples.readDigiConfigFromJson( str( Path(__file__).parent.parent.parent.parent / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" diff --git a/Examples/Python/tests/test_writer.py b/Examples/Python/tests/test_writer.py index 95cd67decd1..4c9e220f899 100644 --- a/Examples/Python/tests/test_writer.py +++ b/Examples/Python/tests/test_writer.py @@ -165,7 +165,6 @@ def test_root_meas_writer(tmp_path, fatras, trk_geo, assert_root_hash): filePath=str(out), surfaceByIdentifier=trk_geo.geoIdSurfaceMap(), ) - config.addBoundIndicesFromDigiConfig(digiAlg.config) s.addWriter(RootMeasurementWriter(level=acts.logging.INFO, config=config)) s.run() diff --git a/Examples/Scripts/Python/digitization_config.py b/Examples/Scripts/Python/digitization_config.py index dfad1913fd6..1b6de07f477 100755 --- a/Examples/Scripts/Python/digitization_config.py +++ b/Examples/Scripts/Python/digitization_config.py @@ -7,7 +7,7 @@ DigitizationConfigurator, writeDigiConfigToJson, GenericDetector, - GeometryHierarchyMap_DigiComponentsConfig, + DigiConfigContainer, ) @@ -27,9 +27,7 @@ def runDigitizationConfig( trackingGeometry.visitSurfaces(digiConfigurator) - outputConfig = GeometryHierarchyMap_DigiComponentsConfig( - digiConfigurator.outputDigiComponents - ) + outputConfig = DigiConfigContainer(digiConfigurator.outputDigiComponents) writeDigiConfigToJson(outputConfig, str(output)) From a30ab3330174a4c44c76b54d9cfa12f7c364863f Mon Sep 17 00:00:00 2001 From: Tim Adye Date: Wed, 20 Nov 2024 18:37:43 +0100 Subject: [PATCH 3/4] feat: full_chain_test.py config with command-line options (#3811) `full_chain_test.py` supports all options from `ckf_tracks.py` (generic detector), `full_chain_odd.py`, and `full_chain_itk.py`, as well as some new additions to help with testing. The idea is to complement those scripts which provide *examples*, where `full_chain_test.py` simplifies running interactive/batch tests with different options specified on the command-line. With all the options, it is not intended to be an easy-to-read example. The existing example scripts could eventually be simplified, since they no longer need to support those test options they do have. The hope is that, in future, `full_chain_test.py` can be used to test new features and see the result in (at least) ODD and ITk environments. The idea for this initial version is: * `full_chain_test.py` exactly matches `full_chain_odd.py` * ODD configuration is the default * `full_chain_test.py -A -M1 -N2` exactly matches `full_chain_itk.py` * `-A` selects the ATLAS ITk configuration * `-M1 -N2` (or `--gen-nvertices 1 --gen-nparticles 2`) changes to 2 particles per event from `full_chain_odd.py`'s default of 800 (`-M 200 -N 4`) * `full_chain_test.py -G` is similar `ckf_tracks.py` * `-G` selects the generic detector configuration * does not exactly match the more rudimentary `ckf_tracks.py`, and doesn't try to reproduce seeding config. Later, if this becomes a useful development test, we can harmonise the detail setup between ODD and ITk. Also, do we need to support options from `full_chain_itk_Gbts.py` and `full_chain_odd_LRT.py`? --- Examples/Scripts/Python/full_chain_test.py | 805 +++++++++++++++++++++ 1 file changed, 805 insertions(+) create mode 100755 Examples/Scripts/Python/full_chain_test.py diff --git a/Examples/Scripts/Python/full_chain_test.py b/Examples/Scripts/Python/full_chain_test.py new file mode 100755 index 00000000000..f0010bd498e --- /dev/null +++ b/Examples/Scripts/Python/full_chain_test.py @@ -0,0 +1,805 @@ +#!/usr/bin/env python3 + +import sys, os, argparse, pathlib + + +def parse_args(): + from acts.examples.reconstruction import SeedingAlgorithm + + parser = argparse.ArgumentParser( + description=""" +Script to test the full chain ACTS simulation and reconstruction. + +This script is provided for interactive developer testing only. +It is not intended (and not supported) for end user use, automated testing, +and certainly should never be called in production. The Python API is the +proper way to access the ActsExamples from scripts. The other Examples/Scripts +are much better examples of how to do that. physmon in the CI is the proper +way to do automated integration tests. This script is only for the case of +interactive testing with one-off configuration specified by command-line options. +""" + ) + parser.add_argument( + "-G", + "--generic-detector", + action="store_true", + help="Use generic detector geometry and config", + ) + parser.add_argument( + "--odd", + default=True, + action=argparse.BooleanOptionalAction, + help="Use Open Data Detector geometry and config (default unless overridden by -G or -A). Requires ACTS_BUILD_ODD.", + ) + parser.add_argument( + "-A", + "--itk", + action="store_true", + help="Use ATLAS ITk geometry and config. Requires acts-itk/ in current directory.", + ) + parser.add_argument( + "-g", + "--geant4", + action="store_true", + help="Use Geant4 instead of Fatras for detector simulation", + ) + parser.add_argument( + "--edm4hep", + type=pathlib.Path, + help="Use edm4hep inputs", + ) + parser.add_argument( + "-b", + "--bf-constant", + action="store_true", + help="Use constant 2T B-field also for ITk; and don't include material map", + ) + parser.add_argument( + "-j", + "--threads", + type=int, + default=-1, + help="Number of parallel threads, negative for automatic (default).", + ) + parser.add_argument( + "-o", + "--output-dir", + "--output", + default=None, + type=pathlib.Path, + help="Directory to write outputs to", + ) + parser.add_argument( + "-O", + "--output-detail", + action="count", + default=0, + help="fewer output files. Use -OO for more output files. Use -OOO to disable all output.", + ) + parser.add_argument( + "-c", + "--output-csv", + action="count", + default=0, + help="Use CSV output instead of ROOT. Specify -cc to output both.", + ) + parser.add_argument( + "-n", + "--events", + type=int, + default=100, + help="The number of events to process (default=%(default)d).", + ) + parser.add_argument( + "-s", + "--skip", + type=int, + default=0, + help="Number of events to skip (default=%(default)d)", + ) + # Many of the following option names were inherited from the old examples binaries and full_chain_odd.py. + # To maintain compatibility, both option names are supported. + parser.add_argument( + "-N", + "--gen-nparticles", + "--gun-particles", + type=int, + default=4, + help="Number of generated particles per vertex from the particle gun (default=%(default)d).", + ) + parser.add_argument( + "-M", + "--gen-nvertices", + "--gun-multiplicity", + "--ttbar-pu", + type=int, + default=200, + help="Number of vertices per event (multiplicity) from the particle gun; or number of pileup events (default=%(default)d)", + ) + parser.add_argument( + "-t", + "--ttbar-pu200", + "--ttbar", + action="store_true", + help="Generate ttbar + mu=200 pile-up using Pythia8", + ) + parser.add_argument( + "-p", + "--gen-pt-range", + "--gun-pt-range", + default="1:10", + help="transverse momentum (pT) range (min:max) of the particle gun in GeV (default=%(default)s)", + ) + parser.add_argument( + "--gen-eta-range", + "--gun-eta-range", + help="Eta range (min:max) of the particle gun (default -2:2 (Generic), -3:3 (ODD), -4:4 (ITk))", + ) + parser.add_argument( + "--gen-cos-theta", + action="store_true", + help="Sample eta as cos(theta) and not uniform", + ) + parser.add_argument( + "-r", + "--random-seed", + type=int, + default=42, + help="Random number seed (default=%(default)d)", + ) + parser.add_argument( + "-F", + "--disable-fpemon", + action="store_true", + help="sets ACTS_SEQUENCER_DISABLE_FPEMON=1", + ) + parser.add_argument( + "-l", + "--loglevel", + type=int, + default=2, + help="The output log level. Please set the wished number (0 = VERBOSE, 1 = DEBUG, 2 = INFO (default), 3 = WARNING, 4 = ERROR, 5 = FATAL).", + ) + parser.add_argument( + "-d", + "--dump-args-calls", + action="store_true", + help="Show pybind function call details", + ) + parser.add_argument( + "--digi-config", + type=pathlib.Path, + help="Digitization configuration file", + ) + parser.add_argument( + "--material-config", + type=pathlib.Path, + help="Material map configuration file", + ) + parser.add_argument( + "-S", + "--seeding-algorithm", + action=EnumAction, + enum=SeedingAlgorithm, + default=SeedingAlgorithm.Default, + help="Select the seeding algorithm to use", + ) + parser.add_argument( + "--ckf", + default=True, + action=argparse.BooleanOptionalAction, + help="Switch CKF on/off", + ) + parser.add_argument( + "--reco", + default=True, + action=argparse.BooleanOptionalAction, + help="Switch reco on/off", + ) + parser.add_argument( + "--vertexing", + default=True, + action=argparse.BooleanOptionalAction, + help="Switch vertexing on/off", + ) + parser.add_argument( + "--MLSeedFilter", + action="store_true", + help="Use the ML seed filter to select seed after the seeding step", + ) + parser.add_argument( + "--ambi-solver", + type=str, + choices=["greedy", "scoring", "ML", "none"], + default="greedy", + help="Set which ambiguity solver to use (default=%(default)s)", + ) + parser.add_argument( + "--ambi-config", + type=pathlib.Path, + default=pathlib.Path.cwd() / "ambi_config.json", + help="Set the configuration file for the Score Based ambiguity resolution (default=%(default)s)", + ) + return parser.parse_args() + + +def full_chain(args): + import acts + + # keep these in memory after we return the sequence + global detector, trackingGeometry, decorators, field, rnd + global logger + + if args.disable_fpemon: + os.environ["ACTS_SEQUENCER_DISABLE_FPEMON"] = "1" + + if args.dump_args_calls: + acts.examples.dump_args_calls(locals()) + + logger = acts.logging.getLogger("full_chain_test") + + nDetArgs = [args.generic_detector, args.odd, args.itk].count(True) + if nDetArgs == 0: + args.generic_detector = True + elif nDetArgs == 2: + args.odd = False + nDetArgs = [args.generic_detector, args.odd, args.itk].count(True) + if nDetArgs != 1: + logger.fatal("require exactly one of: --generic-detector --odd --itk") + sys.exit(2) + if args.generic_detector: + detname = "gen" + elif args.itk: + detname = "itk" + elif args.odd: + detname = "odd" + + u = acts.UnitConstants + + if args.output_detail == 3: + outputDirLess = None + elif args.output_dir is None: + outputDirLess = pathlib.Path.cwd() / f"{detname}_output" + else: + outputDirLess = args.output_dir + + outputDir = None if args.output_detail == 1 else outputDirLess + outputDirMore = None if args.output_detail in (0, 1) else outputDirLess + + outputDirRoot = outputDir if args.output_csv != 1 else None + outputDirLessRoot = outputDirLess if args.output_csv != 1 else None + outputDirMoreRoot = outputDirMore if args.output_csv != 1 else None + outputDirCsv = outputDir if args.output_csv != 0 else None + outputDirLessCsv = outputDirLess if args.output_csv != 0 else None + outputDirMoreCsv = outputDirMore if args.output_csv != 0 else None + + # fmt: off + if args.generic_detector: + etaRange = (-2.0, 2.0) + ptMin = 0.5 * u.GeV + rhoMax = 24.0 * u.mm + geo_dir = pathlib.Path(acts.__file__).resolve().parent.parent.parent.parent.parent + if args.loglevel <= 2: + logger.info(f"Load Generic Detector from {geo_dir}") + if args.digi_config is None: + args.digi_config = geo_dir / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json" + seedingConfigFile = geo_dir / "Examples/Algorithms/TrackFinding/share/geoSelection-genericDetector.json" + args.bf_constant = True + detector, trackingGeometry, decorators = acts.examples.GenericDetector.create() + elif args.odd: + import acts.examples.odd + etaRange = (-3.0, 3.0) + ptMin = 1.0 * u.GeV + rhoMax = 24.0 * u.mm + beamTime = 1.0 * u.ns + geo_dir = acts.examples.odd.getOpenDataDetectorDirectory() + if args.loglevel <= 2: + logger.info(f"Load Open Data Detector from {geo_dir.resolve()}") + if args.digi_config is None: + args.digi_config = geo_dir / "config/odd-digi-smearing-config.json" + seedingConfigFile = geo_dir / "config/odd-seeding-config.json" + if args.material_config is None: + args.material_config = geo_dir / "data/odd-material-maps.root" + args.bf_constant = True + detector, trackingGeometry, decorators = acts.examples.odd.getOpenDataDetector( + odd_dir=geo_dir, + mdecorator=acts.IMaterialDecorator.fromFile(args.material_config), + ) + elif args.itk: + import acts.examples.itk as itk + etaRange = (-4.0, 4.0) + ptMin = 1.0 * u.GeV + rhoMax = 28.0 * u.mm + beamTime = 5.0 * u.ns + geo_dir = pathlib.Path("acts-itk") + if args.loglevel <= 2: + logger.info(f"Load ATLAS ITk from {geo_dir.resolve()}") + if args.digi_config is None: + args.digi_config = geo_dir / "itk-hgtd/itk-smearing-config.json" + seedingConfigFile = geo_dir / "itk-hgtd/geoSelection-ITk.json" + # args.material_config defaulted in itk.buildITkGeometry: geo_dir / "itk-hgtd/material-maps-ITk-HGTD.json" + bFieldFile = geo_dir / "bfield/ATLAS-BField-xyz.root" + detector, trackingGeometry, decorators = itk.buildITkGeometry( + geo_dir, + customMaterialFile=args.material_config, + material=not args.bf_constant, + logLevel=acts.logging.Level(args.loglevel), + ) + # fmt: on + + if args.bf_constant: + field = acts.ConstantBField(acts.Vector3(0.0, 0.0, 2.0 * u.T)) + else: + logger.info("Create magnetic field map from %s" % str(bFieldFile)) + field = acts.examples.MagneticFieldMapXyz(str(bFieldFile)) + rnd = acts.examples.RandomNumbers(seed=42) + + from acts.examples.simulation import ( + MomentumConfig, + EtaConfig, + PhiConfig, + ParticleConfig, + ParticleSelectorConfig, + addDigitization, + addParticleSelection, + ) + + s = acts.examples.Sequencer( + events=args.events, + skip=args.skip, + numThreads=args.threads if not (args.geant4 and args.threads == -1) else 1, + logLevel=acts.logging.Level(args.loglevel), + outputDir="" if outputDirLess is None else str(outputDirLess), + ) + + # is this needed? + for d in decorators: + s.addContextDecorator(d) + + preSelectParticles = ( + ParticleSelectorConfig( + rho=(0.0 * u.mm, rhoMax), + absZ=(0.0 * u.mm, 1.0 * u.m), + eta=etaRange, + pt=(150 * u.MeV, None), + ) + if args.edm4hep or args.geant4 or args.ttbar_pu200 + else ParticleSelectorConfig() + ) + + postSelectParticles = ParticleSelectorConfig( + pt=(ptMin, None), + eta=etaRange if not args.generic_detector else (None, None), + measurements=(9, None), + removeNeutral=True, + ) + + if args.edm4hep: + import acts.examples.edm4hep + + edm4hepReader = acts.examples.edm4hep.EDM4hepReader( + inputPath=str(args.edm4hep), + inputSimHits=[ + "PixelBarrelReadout", + "PixelEndcapReadout", + "ShortStripBarrelReadout", + "ShortStripEndcapReadout", + "LongStripBarrelReadout", + "LongStripEndcapReadout", + ], + outputParticlesGenerator="particles_input", + outputParticlesInitial="particles_initial", + outputParticlesFinal="particles_final", + outputSimHits="simhits", + graphvizOutput="graphviz", + dd4hepDetector=detector, + trackingGeometry=trackingGeometry, + sortSimHitsInTime=True, + level=acts.logging.INFO, + ) + s.addReader(edm4hepReader) + s.addWhiteboardAlias("particles", edm4hepReader.config.outputParticlesGenerator) + + addParticleSelection( + s, + config=preSelectParticles, + inputParticles="particles", + outputParticles="particles_selected", + ) + + else: + + if not args.ttbar_pu200: + from acts.examples.simulation import addParticleGun + + addParticleGun( + s, + MomentumConfig( + *strToRange(args.gen_pt_range, "--gen-pt-range", u.GeV), + transverse=True, + ), + EtaConfig( + *( + strToRange(args.gen_eta_range, "--gen-eta-range") + if args.gen_eta_range + else etaRange + ), + uniform=( + not args.gen_cos_theta + if args.gen_cos_theta or not args.odd + else None + ), + ), + PhiConfig(0.0, 360.0 * u.degree) if not args.itk else PhiConfig(), + ParticleConfig( + args.gen_nparticles, acts.PdgParticle.eMuon, randomizeCharge=True + ), + vtxGen=( + acts.examples.GaussianVertexGenerator( + mean=acts.Vector4(0, 0, 0, 0), + stddev=acts.Vector4( + 0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 1.0 * u.ns + ), + ) + if args.odd + else None + ), + multiplicity=args.gen_nvertices, + rnd=rnd, + outputDirRoot=outputDirMoreRoot, + outputDirCsv=outputDirMoreCsv, + ) + else: + from acts.examples.simulation import addPythia8 + + addPythia8( + s, + hardProcess=["Top:qqbar2ttbar=on"], + npileup=args.gen_nvertices, + vtxGen=acts.examples.GaussianVertexGenerator( + stddev=acts.Vector4( + 0.0125 * u.mm, 0.0125 * u.mm, 55.5 * u.mm, 5.0 * u.ns + ), + mean=acts.Vector4(0, 0, 0, 0), + ), + rnd=rnd, + outputDirRoot=outputDirRoot, + outputDirCsv=outputDirCsv, + ) + + if not args.geant4: + from acts.examples.simulation import addFatras + + addFatras( + s, + trackingGeometry, + field, + rnd=rnd, + preSelectParticles=preSelectParticles, + postSelectParticles=postSelectParticles, + outputDirRoot=outputDirRoot, + outputDirCsv=outputDirCsv, + ) + else: + if s.config.numThreads != 1: + logger.fatal( + f"Geant 4 simulation does not support multi-threading (threads={s.config.numThreads})" + ) + sys.exit(2) + + from acts.examples.simulation import addGeant4 + + # Pythia can sometime simulate particles outside the world volume, a cut on the Z of the track help mitigate this effect + # Older version of G4 might not work, this as has been tested on version `geant4-11-00-patch-03` + # For more detail see issue #1578 + addGeant4( + s, + detector, + trackingGeometry, + field, + rnd=rnd, + preSelectParticles=preSelectParticles, + postSelectParticles=postSelectParticles, + killVolume=trackingGeometry.highestTrackingVolume, + killAfterTime=25 * u.ns, + outputDirRoot=outputDirRoot, + outputDirCsv=outputDirCsv, + ) + + addDigitization( + s, + trackingGeometry, + field, + digiConfigFile=args.digi_config, + rnd=rnd, + outputDirRoot=outputDirRoot, + outputDirCsv=outputDirCsv, + ) + + if not args.reco: + return s + + from acts.examples.reconstruction import ( + addSeeding, + ParticleSmearingSigmas, + addCKFTracks, + CkfConfig, + SeedingAlgorithm, + TrackSelectorConfig, + addAmbiguityResolution, + AmbiguityResolutionConfig, + addVertexFitting, + VertexFinder, + ) + + if args.itk and args.seeding_algorithm == SeedingAlgorithm.Default: + seedingAlgConfig = itk.itkSeedingAlgConfig( + itk.InputSpacePointsType.PixelSpacePoints + ) + else: + seedingAlgConfig = [] + + addSeeding( + s, + trackingGeometry, + field, + *seedingAlgConfig, + seedingAlgorithm=args.seeding_algorithm, + **( + dict( + particleSmearingSigmas=ParticleSmearingSigmas(ptRel=0.01), + rnd=rnd, + ) + if args.seeding_algorithm == SeedingAlgorithm.TruthSmeared + else {} + ), + initialSigmas=[ + 1 * u.mm, + 1 * u.mm, + 1 * u.degree, + 1 * u.degree, + 0.1 * u.e / u.GeV, + 1 * u.ns, + ], + initialSigmaPtRel=0.1, + initialVarInflation=[1.0] * 6, + geoSelectionConfigFile=seedingConfigFile, + outputDirRoot=outputDirLessRoot, + outputDirCsv=outputDirLessCsv, + ) + + if args.MLSeedFilter: + from acts.examples.reconstruction import ( + addSeedFilterML, + SeedFilterMLDBScanConfig, + ) + + addSeedFilterML( + s, + SeedFilterMLDBScanConfig( + epsilonDBScan=0.03, minPointsDBScan=2, minSeedScore=0.1 + ), + onnxModelFile=str( + geo_dir + / "Examples/Scripts/Python/MLAmbiguityResolution/seedDuplicateClassifier.onnx" + ), + outputDirRoot=outputDirLessRoot, + outputDirCsv=outputDirLessCsv, + ) + + if not args.ckf: + return s + + if args.seeding_algorithm != SeedingAlgorithm.TruthSmeared: + ckfConfig = CkfConfig( + seedDeduplication=True, + stayOnSeed=True, + ) + else: + ckfConfig = CkfConfig() + + if not args.itk: + trackSelectorConfig = TrackSelectorConfig( + pt=(ptMin if args.ttbar_pu200 else 0.0, None), + absEta=(None, 3.0), + loc0=(-4.0 * u.mm, 4.0 * u.mm), + nMeasurementsMin=7, + maxHoles=2, + maxOutliers=2, + ) + ckfConfig = ckfConfig._replace( + chi2CutOffMeasurement=15.0, + chi2CutOffOutlier=25.0, + numMeasurementsCutOff=10, + ) + else: + # fmt: off + trackSelectorConfig = ( + TrackSelectorConfig(absEta=(None, 2.0), pt=(0.9 * u.GeV, None), nMeasurementsMin=9, maxHoles=2, maxOutliers=2, maxSharedHits=2), + TrackSelectorConfig(absEta=(None, 2.6), pt=(0.4 * u.GeV, None), nMeasurementsMin=8, maxHoles=2, maxOutliers=2, maxSharedHits=2), + TrackSelectorConfig(absEta=(None, 4.0), pt=(0.4 * u.GeV, None), nMeasurementsMin=7, maxHoles=2, maxOutliers=2, maxSharedHits=2), + ) + # fmt: on + + if args.odd: + ckfConfig = ckfConfig._replace( + pixelVolumes=[16, 17, 18], + stripVolumes=[23, 24, 25], + maxPixelHoles=1, + maxStripHoles=2, + constrainToVolumes=[ + 2, # beam pipe + 32, + 4, # beam pip gap + 16, + 17, + 18, # pixel + 20, # PST + 23, + 24, + 25, # short strip + 26, + 8, # long strip gap + 28, + 29, + 30, # long strip + ], + ) + elif args.itk: + ckfConfig = ckfConfig._replace( + # ITk volumes from Noemi's plot + pixelVolumes=[8, 9, 10, 13, 14, 15, 16, 18, 19, 20], + stripVolumes=[22, 23, 24], + maxPixelHoles=1, + maxStripHoles=2, + ) + + if args.output_detail == 1: + writeDetail = dict(writeTrackSummary=False) + elif args.output_detail == 2: + writeDetail = dict(writeTrackStates=True) + else: + writeDetail = {} + + if args.odd and args.output_detail != 1: + writeCovMat = dict(writeCovMat=True) + else: + writeCovMat = {} + + addCKFTracks( + s, + trackingGeometry, + field, + trackSelectorConfig=trackSelectorConfig, + ckfConfig=ckfConfig, + **writeDetail, + **writeCovMat, + outputDirRoot=outputDirLessRoot, + outputDirCsv=outputDirLessCsv, + ) + + if args.ambi_solver == "ML": + + from acts.examples.reconstruction import ( + addAmbiguityResolutionML, + AmbiguityResolutionMLConfig, + ) + + addAmbiguityResolutionML( + s, + AmbiguityResolutionMLConfig( + maximumSharedHits=3, maximumIterations=1000000, nMeasurementsMin=7 + ), + onnxModelFile=str( + geo_dir + / "Examples/Scripts/Python/MLAmbiguityResolution/duplicateClassifier.onnx" + ), + outputDirRoot=outputDirLessRoot, + outputDirCsv=outputDirLessCsv, + ) + + elif args.ambi_solver == "scoring": + + from acts.examples.reconstruction import ( + addScoreBasedAmbiguityResolution, + ScoreBasedAmbiguityResolutionConfig, + ) + import math + + addScoreBasedAmbiguityResolution( + s, + ScoreBasedAmbiguityResolutionConfig( + minScore=0, + minScoreSharedTracks=1, + maxShared=2, + maxSharedTracksPerMeasurement=2, + pTMax=1400, + pTMin=0.5, + phiMax=math.pi, + phiMin=-math.pi, + etaMax=4, + etaMin=-4, + useAmbiguityFunction=False, + ), + ambiVolumeFile=args.ambi_config, + **writeCovMat, + outputDirRoot=outputDirLessRoot, + outputDirCsv=outputDirLessCsv, + ) + + elif args.ambi_solver == "greedy": + + addAmbiguityResolution( + s, + AmbiguityResolutionConfig( + maximumSharedHits=3, + maximumIterations=10000 if args.itk else 1000000, + nMeasurementsMin=6 if args.itk else 7, + ), + **writeDetail, + **writeCovMat, + outputDirRoot=outputDirLessRoot, + outputDirCsv=outputDirLessCsv, + ) + + if args.vertexing: + addVertexFitting( + s, + field, + vertexFinder=VertexFinder.AMVF, + outputDirRoot=outputDirLessRoot, + ) + + return s + + +def strToRange(s: str, optName: str, unit: float = 1.0): + global logger + try: + range = [float(e) * unit if e != "" else None for e in s.split(":")] + except ValueError: + range = [] + if len(range) == 1: + range.append(range[0]) # 100 -> 100:100 + if len(range) != 2: + logger.fatal(f"bad option value: {optName} {s}") + sys.exit(2) + return range + + +# Graciously taken from https://stackoverflow.com/a/60750535/4280680 (via seeding.py) +class EnumAction(argparse.Action): + """ + Argparse action for handling Enums + """ + + def __init__(self, **kwargs): + import enum + + # Pop off the type value + enum_type = kwargs.pop("enum", None) + + # Ensure an Enum subclass is provided + if enum_type is None: + raise ValueError("type must be assigned an Enum when using EnumAction") + if not issubclass(enum_type, enum.Enum): + raise TypeError("type must be an Enum when using EnumAction") + + # Generate choices from the Enum + kwargs.setdefault("choices", tuple(e.name for e in enum_type)) + + super(EnumAction, self).__init__(**kwargs) + + self._enum = enum_type + + def __call__(self, parser, namespace, values, option_string=None): + for e in self._enum: + if e.name == values: + setattr(namespace, self.dest, e) + break + else: + raise ValueError("%s is not a validly enumerated algorithm." % values) + + +# main program: parse arguments, setup sequence, and run the full chain +full_chain(parse_args()).run() From 11746d11c294c1a94ee9d66afbf6061eceb29a98 Mon Sep 17 00:00:00 2001 From: "Alexander J. Pfleger" <70842573+AJPfleger@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:12:47 +0100 Subject: [PATCH 4/4] chore: move requirements file for fpe-masks (#3884) This way, all requirements files have the same name and just different location. Easier for maintaining them. --- .github/workflows/checks.yml | 2 +- CI/{requirements_fpe_masks.in => fpe_masks/requirements.in} | 0 CI/{requirements_fpe_masks.txt => fpe_masks/requirements.txt} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename CI/{requirements_fpe_masks.in => fpe_masks/requirements.in} (100%) rename CI/{requirements_fpe_masks.txt => fpe_masks/requirements.txt} (100%) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index edd31d051a4..e84044776a8 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -145,7 +145,7 @@ jobs: python-version: '3.12' - name: Install dependencies run: > - pip install -r CI/requirements_fpe_masks.txt + pip install -r CI/fpe_masks/requirements.txt - name: Check run: > CI/check_fpe_masks.py --token ${{ secrets.GITHUB_TOKEN }} diff --git a/CI/requirements_fpe_masks.in b/CI/fpe_masks/requirements.in similarity index 100% rename from CI/requirements_fpe_masks.in rename to CI/fpe_masks/requirements.in diff --git a/CI/requirements_fpe_masks.txt b/CI/fpe_masks/requirements.txt similarity index 100% rename from CI/requirements_fpe_masks.txt rename to CI/fpe_masks/requirements.txt