99#include " ActsExamples/Digitization/ModuleClusters.hpp"
1010
1111#include " Acts/Clusterization/Clusterization.hpp"
12+ #include " Acts/Clusterization/InPlaceClusterization.hpp"
1213#include " Acts/Utilities/Helpers.hpp"
1314#include " ActsExamples/Digitization/MeasurementCreation.hpp"
1415#include " ActsFatras/Digitization/Channelizer.hpp"
1920#include < cstdlib>
2021#include < limits>
2122#include < memory>
23+ #include < ranges>
2224#include < stdexcept>
2325#include < type_traits>
2426
@@ -48,7 +50,12 @@ void ModuleClusters::add(DigitizedParameters params, simhit_t simhit) {
4850std::vector<std::pair<DigitizedParameters, std::set<ModuleClusters::simhit_t >>>
4951ModuleClusters::digitizedParameters () {
5052 if (m_merge) { // (re-)build the clusters
51- merge ();
53+ if (m_useInPlaceClusterization && !m_moduleValues.empty () &&
54+ std::holds_alternative<Cluster::Cell>(m_moduleValues.front ().value )) {
55+ mergeWithInPlaceClusterization ();
56+ } else {
57+ merge ();
58+ }
5259 }
5360 std::vector<std::pair<DigitizedParameters, std::set<simhit_t >>> retv;
5461 for (ModuleValue& mval : m_moduleValues) {
@@ -96,6 +103,146 @@ std::vector<ModuleValue> ModuleClusters::createCellCollection() {
96103 }
97104 return cells;
98105}
106+ void ModuleClusters::mergeWithInPlaceClusterization () {
107+ if (m_moduleValues.size () < std::numeric_limits<std::uint16_t >::max ()) {
108+ mergeWithInPlaceClusterizationImpl<std::uint16_t , 0 >();
109+ } else {
110+ mergeWithInPlaceClusterizationImpl<std::uint32_t , 0 >();
111+ }
112+ }
113+
114+ // Helper to adapt an in-place clustered cell collection to imitate a
115+ // ModuleValue collection of a cluster cell_t must have an m_srcIndex member
116+ // which refers to a ModuleValue
117+ template <typename cell_t >
118+ struct ClusterModuleValues {
119+ // iterator to iterate over the ModuleValues of a cluster
120+ struct iterator {
121+ ModuleValue& operator *() {
122+ assert (m_moduleValues && m_iter->m_srcIndex < m_moduleValues->size ());
123+ return (*m_moduleValues)[m_iter->m_srcIndex ];
124+ }
125+
126+ // Test whether two iterators refer to the same ModuleValue
127+ // Assumes that all ModuleValues belong to the same container, and
128+ // that the underlying in-place clustered cell containers are the same.
129+ bool operator ==(const iterator& other) const {
130+ assert (m_moduleValues == other.m_moduleValues );
131+ return other.m_iter == m_iter;
132+ }
133+ iterator& operator ++() {
134+ ++m_iter;
135+ return *this ;
136+ }
137+
138+ std::vector<ModuleValue>* m_moduleValues;
139+ std::span<cell_t >::iterator m_iter;
140+ };
141+
142+ // Iterator to refer to the first ModuleValue of a range
143+ iterator begin () { return iterator{m_moduleValues, m_cells.begin ()}; }
144+ // Iterator to refer to the ModuleValue after the last one of a range
145+ iterator end () { return iterator{m_moduleValues, m_cells.end ()}; }
146+
147+ // Get the number of ModuleValues in the range
148+ std::size_t size () const { return m_cells.size (); }
149+
150+ // Return true if the ModuleValue range is empty
151+ bool empty () const { return m_cells.empty (); }
152+ ModuleValue& operator [](unsigned int idx) {
153+ assert (idx < m_cells.size () && m_moduleValues &&
154+ m_cells[idx].m_srcIndex < m_moduleValues->size ());
155+ return (*m_moduleValues)[m_cells[idx].m_srcIndex ];
156+ }
157+
158+ // return the ModuleValue at a certain position in the range (range checked)
159+ // @param idx the position in the range where 0 refers to the first element in the range
160+ // May throw a range_error if idx is not a valid index in the range.
161+ ModuleValue& at (unsigned int idx) {
162+ assert (idx < m_cells.size () && m_moduleValues &&
163+ m_cells[idx].m_srcIndex < m_moduleValues->size ());
164+ return (*m_moduleValues).at (m_cells[idx].m_srcIndex );
165+ }
166+
167+ // return the ModuleValue at a certain position in the range (no range check)
168+ // @param idx the position in the range where 0 refers to the first element in the range
169+ const ModuleValue& operator [](unsigned int idx) const {
170+ assert (idx < m_cells.size () && m_moduleValues &&
171+ m_cells[idx].m_srcIndex < m_moduleValues->size ());
172+ return (*m_moduleValues)[m_cells[idx]];
173+ }
174+
175+ std::vector<ModuleValue>* m_moduleValues;
176+ std::span<cell_t > m_cells; // A range of cells which refer to a ModuleValue
177+ // in the ModuleValue collection
178+ };
179+
180+ template <typename index_t , unsigned int AXIS>
181+ void ModuleClusters::mergeWithInPlaceClusterizationImpl () {
182+ // create a cell collection which contains the coordinates of the ModuleValues
183+ // and which refer back to the source ModuleValue.
184+ using Cell = Acts::InPlaceClusterization::Cell<std::int16_t , 2 , index_t >;
185+ std::vector<Cell> cells;
186+ cells.reserve (m_moduleValues.size ());
187+ {
188+ index_t idx = 0 ;
189+ for (const ModuleValue& mval : m_moduleValues) {
190+ if (std::holds_alternative<Cluster::Cell>(mval.value )) {
191+ cells.push_back (Cell (
192+ std::array<std::int16_t , 2 >{
193+ static_cast <std::int16_t >(
194+ std::get<ActsExamples::Cluster::Cell>(mval.value ).bin [0 ]),
195+ static_cast <std::int16_t >(
196+ std::get<ActsExamples::Cluster::Cell>(mval.value ).bin [1 ])},
197+ idx));
198+ }
199+ ++idx;
200+ }
201+ }
202+ // Clusterize the cell collection in-place considering common corners and
203+ // edges or just common edges.
204+ if (m_commonCorner) {
205+ Acts::InPlaceClusterization::clusterize<
206+ AXIS, std::vector<Cell>, index_t ,
207+ Acts::InPlaceClusterization::EConnectionType::CommonEdgeOrCorner>(
208+ cells);
209+ } else {
210+ Acts::InPlaceClusterization::clusterize<
211+ AXIS, std::vector<Cell>, index_t ,
212+ Acts::InPlaceClusterization::EConnectionType::CommonEdge>(cells);
213+ }
214+
215+ std::vector<ModuleValue> newVals;
216+ newVals.reserve (m_moduleValues.size ());
217+ // Helper to create a cluster module value for the given cell range
218+ // the ModuleValues the cell range refers to will be squashed and then
219+ // added to a new Cluster.
220+ auto addClusters = [this , &newVals](std::vector<Cell>& all_cells,
221+ unsigned int idx_begin,
222+ unsigned int idx_end) {
223+ auto cluster_cells =
224+ std::span (all_cells.begin () + idx_begin, all_cells.begin () + idx_end);
225+ if (cluster_cells.size () == 1 ) {
226+ // no need to merge parameters
227+ ClusterModuleValues<Cell> temp{&m_moduleValues, cluster_cells};
228+ newVals.push_back (squash (temp));
229+ } else {
230+ ClusterModuleValues<Cell> temp{&m_moduleValues, cluster_cells};
231+ std::ranges::sort (cluster_cells, [](const Cell& a, const Cell& b) {
232+ return Acts::InPlaceClusterization::traits::getCellCoordinate (a, AXIS) <
233+ Acts::InPlaceClusterization::traits::getCellCoordinate (b, AXIS);
234+ });
235+
236+ for (std::vector<ModuleValue>& remerged : mergeParameters (temp)) {
237+ newVals.push_back (squash (remerged));
238+ }
239+ }
240+ };
241+
242+ // Create ModuleValues for each cell range which forms a cluster.
243+ for_each_cluster (cells, addClusters);
244+ m_moduleValues = std::move (newVals);
245+ }
99246
100247void ModuleClusters::merge () {
101248 std::vector<ModuleValue> cells = createCellCollection ();
@@ -117,7 +264,6 @@ void ModuleClusters::merge() {
117264 // only. Still have to check if they match based on the other
118265 // indices (a good example of this would a for a timing
119266 // detector).
120-
121267 for (std::vector<ModuleValue>& remerged : mergeParameters (cellv)) {
122268 newVals.push_back (squash (remerged));
123269 }
@@ -134,7 +280,7 @@ void ModuleClusters::merge() {
134280
135281// ATTN: returns vector of index into `indices'
136282std::vector<std::size_t > ModuleClusters::nonGeoEntries (
137- std::vector<Acts::BoundIndices>& indices) {
283+ const std::vector<Acts::BoundIndices>& indices) const {
138284 std::vector<std::size_t > retv;
139285 for (std::size_t i = 0 ; i < indices.size (); i++) {
140286 auto idx = indices.at (i);
@@ -146,8 +292,9 @@ std::vector<std::size_t> ModuleClusters::nonGeoEntries(
146292}
147293
148294// Merging based on parameters
295+ template <class cell_list_t >
149296std::vector<std::vector<ModuleValue>> ModuleClusters::mergeParameters (
150- std::vector<ModuleValue> values) {
297+ cell_list_t & values) const {
151298 std::vector<std::vector<ModuleValue>> retv;
152299
153300 std::vector<bool > used (values.size (), false );
@@ -181,7 +328,7 @@ std::vector<std::vector<ModuleValue>> ModuleClusters::mergeParameters(
181328 // The cluster to be merged into can have more than one
182329 // associated value at this point, so we have to consider them
183330 // all
184- for (ModuleValue& thisval : thisvec) {
331+ for (const ModuleValue& thisval : thisvec) {
185332 // Loop over non-geometric dimensions
186333 for (auto k : nonGeoEntries (thisval.paramIndices )) {
187334 double p_i = thisval.paramValues .at (k);
@@ -221,14 +368,15 @@ std::vector<std::vector<ModuleValue>> ModuleClusters::mergeParameters(
221368 return retv;
222369}
223370
224- ModuleValue ModuleClusters::squash (std::vector<ModuleValue>& values) {
371+ template <class cell_list_t >
372+ ModuleValue ModuleClusters::squash (cell_list_t & values) const {
225373 ModuleValue mval;
226374 double tot = 0 ;
227375 double tot2 = 0 ;
228376 std::vector<double > weights;
229377
230378 // First, start by computing cell weights
231- for (ModuleValue& other : values) {
379+ for (const ModuleValue& other : values) {
232380 if (std::holds_alternative<Cluster::Cell>(other.value )) {
233381 weights.push_back (std::get<Cluster::Cell>(other.value ).activation );
234382 } else {
@@ -240,7 +388,7 @@ ModuleValue ModuleClusters::squash(std::vector<ModuleValue>& values) {
240388
241389 // Now, go over the non-geometric indices
242390 for (std::size_t i = 0 ; i < values.size (); i++) {
243- ModuleValue& other = values.at (i);
391+ const ModuleValue& other = values.at (i);
244392 for (std::size_t j = 0 ; j < other.paramIndices .size (); j++) {
245393 auto idx = other.paramIndices .at (j);
246394 if (!rangeContainsValue (m_geoIndices, idx)) {
@@ -272,7 +420,7 @@ ModuleValue ModuleClusters::squash(std::vector<ModuleValue>& values) {
272420 std::size_t b1max = 0 ;
273421
274422 for (std::size_t i = 0 ; i < values.size (); i++) {
275- ModuleValue& other = values.at (i);
423+ const ModuleValue& other = values.at (i);
276424 if (!std::holds_alternative<Cluster::Cell>(other.value )) {
277425 continue ;
278426 }
0 commit comments