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,145 @@ 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 assuming a 4 or 8 fold connectivity
203+ if (m_commonCorner) {
204+ Acts::InPlaceClusterization::clusterize<
205+ AXIS, std::vector<Cell>, index_t ,
206+ Acts::InPlaceClusterization::EConnectionType::CommonEdgeOrCorner>(
207+ cells);
208+ } else {
209+ Acts::InPlaceClusterization::clusterize<
210+ AXIS, std::vector<Cell>, index_t ,
211+ Acts::InPlaceClusterization::EConnectionType::CommonEdge>(cells);
212+ }
213+
214+ std::vector<ModuleValue> newVals;
215+ newVals.reserve (m_moduleValues.size ());
216+ // Helper to create a cluster module value for the given cell range
217+ // the ModuleValues the cell range refers to will be squashed and then
218+ // added to a new Cluster.
219+ auto addClusters = [this , &newVals](std::vector<Cell>& all_cells,
220+ unsigned int idx_begin,
221+ unsigned int idx_end) {
222+ auto cluster_cells =
223+ std::span (all_cells.begin () + idx_begin, all_cells.begin () + idx_end);
224+ if (cluster_cells.size () == 1 ) {
225+ // no need to merge parameters
226+ ClusterModuleValues<Cell> temp{&m_moduleValues, cluster_cells};
227+ newVals.push_back (squash (temp));
228+ } else {
229+ ClusterModuleValues<Cell> temp{&m_moduleValues, cluster_cells};
230+ std::ranges::sort (cluster_cells, [](const Cell& a, const Cell& b) {
231+ return Acts::InPlaceClusterization::traits::getCellCoordinate (a, AXIS) <
232+ Acts::InPlaceClusterization::traits::getCellCoordinate (b, AXIS);
233+ });
234+
235+ for (std::vector<ModuleValue>& remerged : mergeParameters (temp)) {
236+ newVals.push_back (squash (remerged));
237+ }
238+ }
239+ };
240+
241+ // Create ModuleValues for each cell range which forms a cluster.
242+ for_each_cluster (cells, addClusters);
243+ m_moduleValues = std::move (newVals);
244+ }
99245
100246void ModuleClusters::merge () {
101247 std::vector<ModuleValue> cells = createCellCollection ();
@@ -117,7 +263,6 @@ void ModuleClusters::merge() {
117263 // only. Still have to check if they match based on the other
118264 // indices (a good example of this would a for a timing
119265 // detector).
120-
121266 for (std::vector<ModuleValue>& remerged : mergeParameters (cellv)) {
122267 newVals.push_back (squash (remerged));
123268 }
@@ -134,7 +279,7 @@ void ModuleClusters::merge() {
134279
135280// ATTN: returns vector of index into `indices'
136281std::vector<std::size_t > ModuleClusters::nonGeoEntries (
137- std::vector<Acts::BoundIndices>& indices) {
282+ const std::vector<Acts::BoundIndices>& indices) const {
138283 std::vector<std::size_t > retv;
139284 for (std::size_t i = 0 ; i < indices.size (); i++) {
140285 auto idx = indices.at (i);
@@ -146,8 +291,9 @@ std::vector<std::size_t> ModuleClusters::nonGeoEntries(
146291}
147292
148293// Merging based on parameters
294+ template <class cell_list_t >
149295std::vector<std::vector<ModuleValue>> ModuleClusters::mergeParameters (
150- std::vector<ModuleValue> values) {
296+ cell_list_t & values) const {
151297 std::vector<std::vector<ModuleValue>> retv;
152298
153299 std::vector<bool > used (values.size (), false );
@@ -181,7 +327,7 @@ std::vector<std::vector<ModuleValue>> ModuleClusters::mergeParameters(
181327 // The cluster to be merged into can have more than one
182328 // associated value at this point, so we have to consider them
183329 // all
184- for (ModuleValue& thisval : thisvec) {
330+ for (const ModuleValue& thisval : thisvec) {
185331 // Loop over non-geometric dimensions
186332 for (auto k : nonGeoEntries (thisval.paramIndices )) {
187333 double p_i = thisval.paramValues .at (k);
@@ -221,14 +367,15 @@ std::vector<std::vector<ModuleValue>> ModuleClusters::mergeParameters(
221367 return retv;
222368}
223369
224- ModuleValue ModuleClusters::squash (std::vector<ModuleValue>& values) {
370+ template <class cell_list_t >
371+ ModuleValue ModuleClusters::squash (cell_list_t & values) const {
225372 ModuleValue mval;
226373 double tot = 0 ;
227374 double tot2 = 0 ;
228375 std::vector<double > weights;
229376
230377 // First, start by computing cell weights
231- for (ModuleValue& other : values) {
378+ for (const ModuleValue& other : values) {
232379 if (std::holds_alternative<Cluster::Cell>(other.value )) {
233380 weights.push_back (std::get<Cluster::Cell>(other.value ).activation );
234381 } else {
@@ -240,7 +387,7 @@ ModuleValue ModuleClusters::squash(std::vector<ModuleValue>& values) {
240387
241388 // Now, go over the non-geometric indices
242389 for (std::size_t i = 0 ; i < values.size (); i++) {
243- ModuleValue& other = values.at (i);
390+ const ModuleValue& other = values.at (i);
244391 for (std::size_t j = 0 ; j < other.paramIndices .size (); j++) {
245392 auto idx = other.paramIndices .at (j);
246393 if (!rangeContainsValue (m_geoIndices, idx)) {
@@ -272,7 +419,7 @@ ModuleValue ModuleClusters::squash(std::vector<ModuleValue>& values) {
272419 std::size_t b1max = 0 ;
273420
274421 for (std::size_t i = 0 ; i < values.size (); i++) {
275- ModuleValue& other = values.at (i);
422+ const ModuleValue& other = values.at (i);
276423 if (!std::holds_alternative<Cluster::Cell>(other.value )) {
277424 continue ;
278425 }
0 commit comments