Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:02:35

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "ActsExamples/Digitization/ModuleClusters.hpp"
0010 
0011 #include "Acts/Clusterization/Clusterization.hpp"
0012 #include "Acts/Utilities/Helpers.hpp"
0013 #include "ActsExamples/Digitization/MeasurementCreation.hpp"
0014 #include "ActsFatras/Digitization/Channelizer.hpp"
0015 
0016 #include <array>
0017 #include <cmath>
0018 #include <cstdint>
0019 #include <cstdlib>
0020 #include <limits>
0021 #include <memory>
0022 #include <stdexcept>
0023 #include <type_traits>
0024 
0025 namespace ActsExamples {
0026 
0027 void ModuleClusters::add(DigitizedParameters params, simhit_t simhit) {
0028   ModuleValue mval;
0029   mval.paramIndices = std::move(params.indices);
0030   mval.paramValues = std::move(params.values);
0031   mval.paramVariances = std::move(params.variances);
0032   mval.sources = {simhit};
0033 
0034   if (m_merge && !params.cluster.channels.empty()) {
0035     // Break-up the cluster
0036     for (const auto& cell : params.cluster.channels) {
0037       ModuleValue mval_cell = mval;
0038       mval_cell.value = cell;
0039       m_moduleValues.push_back(std::move(mval_cell));
0040     }
0041   } else {
0042     // pass-through mode or smeared indices only
0043     mval.value = std::move(params.cluster);
0044     m_moduleValues.push_back(std::move(mval));
0045   }
0046 }
0047 
0048 std::vector<std::pair<DigitizedParameters, std::set<ModuleClusters::simhit_t>>>
0049 ModuleClusters::digitizedParameters() {
0050   if (m_merge) {  // (re-)build the clusters
0051     merge();
0052   }
0053   std::vector<std::pair<DigitizedParameters, std::set<simhit_t>>> retv;
0054   for (ModuleValue& mval : m_moduleValues) {
0055     if (std::holds_alternative<Cluster::Cell>(mval.value)) {
0056       // Should never happen! Either the cluster should have
0057       // passed-through or the cells merged into clusters in the
0058       // merge() step. Treat this as a bug.
0059       throw std::runtime_error("Invalid cluster!");
0060     }
0061     DigitizedParameters dpars;
0062     dpars.indices = mval.paramIndices;
0063     dpars.values = mval.paramValues;
0064     dpars.variances = mval.paramVariances;
0065     dpars.cluster = std::get<Cluster>(mval.value);
0066     retv.emplace_back(std::move(dpars), mval.sources);
0067   }
0068   return retv;
0069 }
0070 
0071 // Needed for clusterization
0072 int getCellRow(const ModuleValue& mval) {
0073   if (std::holds_alternative<ActsExamples::Cluster::Cell>(mval.value)) {
0074     return std::get<ActsExamples::Cluster::Cell>(mval.value).bin[0];
0075   }
0076   throw std::domain_error("ModuleValue does not contain cell!");
0077 }
0078 
0079 int getCellColumn(const ActsExamples::ModuleValue& mval) {
0080   if (std::holds_alternative<ActsExamples::Cluster::Cell>(mval.value)) {
0081     return std::get<ActsExamples::Cluster::Cell>(mval.value).bin[1];
0082   }
0083   throw std::domain_error("ModuleValue does not contain cell!");
0084 }
0085 
0086 void clusterAddCell(std::vector<ModuleValue>& cl, const ModuleValue& ce) {
0087   cl.push_back(ce);
0088 }
0089 
0090 std::vector<ModuleValue> ModuleClusters::createCellCollection() {
0091   std::vector<ModuleValue> cells;
0092   for (ModuleValue& mval : m_moduleValues) {
0093     if (std::holds_alternative<Cluster::Cell>(mval.value)) {
0094       cells.push_back(mval);
0095     }
0096   }
0097   return cells;
0098 }
0099 
0100 void ModuleClusters::merge() {
0101   std::vector<ModuleValue> cells = createCellCollection();
0102 
0103   std::vector<ModuleValue> newVals;
0104 
0105   if (!cells.empty()) {
0106     // Case where we actually have geometric clusters
0107     Acts::Ccl::ClusteringData data;
0108     std::vector<std::vector<ModuleValue>> merged;
0109     Acts::Ccl::createClusters<std::vector<ModuleValue>,
0110                               std::vector<std::vector<ModuleValue>>>(
0111         data, cells, merged,
0112         Acts::Ccl::DefaultConnect<ModuleValue>(m_commonCorner));
0113 
0114     for (std::vector<ModuleValue>& cellv : merged) {
0115       // At this stage, the cellv vector contains cells that form a
0116       // consistent cluster based on a connected component analysis
0117       // only. Still have to check if they match based on the other
0118       // indices (a good example of this would a for a timing
0119       // detector).
0120 
0121       for (std::vector<ModuleValue>& remerged : mergeParameters(cellv)) {
0122         newVals.push_back(squash(remerged));
0123       }
0124     }
0125     m_moduleValues = std::move(newVals);
0126   } else {
0127     // no geo clusters
0128     for (std::vector<ModuleValue>& merged : mergeParameters(m_moduleValues)) {
0129       newVals.push_back(squash(merged));
0130     }
0131     m_moduleValues = std::move(newVals);
0132   }
0133 }
0134 
0135 // ATTN: returns vector of index into `indices'
0136 std::vector<std::size_t> ModuleClusters::nonGeoEntries(
0137     std::vector<Acts::BoundIndices>& indices) {
0138   std::vector<std::size_t> retv;
0139   for (std::size_t i = 0; i < indices.size(); i++) {
0140     auto idx = indices.at(i);
0141     if (!rangeContainsValue(m_geoIndices, idx)) {
0142       retv.push_back(i);
0143     }
0144   }
0145   return retv;
0146 }
0147 
0148 // Merging based on parameters
0149 std::vector<std::vector<ModuleValue>> ModuleClusters::mergeParameters(
0150     std::vector<ModuleValue> values) {
0151   std::vector<std::vector<ModuleValue>> retv;
0152 
0153   std::vector<bool> used(values.size(), false);
0154   for (std::size_t i = 0; i < values.size(); i++) {
0155     if (used.at(i)) {
0156       continue;
0157     }
0158 
0159     retv.emplace_back();
0160     std::vector<ModuleValue>& thisvec = retv.back();
0161 
0162     // Value has not yet been claimed, so claim it
0163     thisvec.push_back(std::move(values.at(i)));
0164     used.at(i) = true;
0165 
0166     // Values previously visited by index `i' have already been added
0167     // to a cluster or used to seed a new cluster, so start at the
0168     // next unseen one
0169     for (std::size_t j = i + 1; j < values.size(); j++) {
0170       // Still may have already been used, so check it
0171       if (used.at(j)) {
0172         continue;
0173       }
0174 
0175       // Now look for a match between current cluster and value `j' Consider
0176       // them matched until we find evidence to the contrary. This
0177       // way, merging still works when digitization is done by
0178       // clusters only
0179       bool matched = true;
0180 
0181       // The cluster to be merged into can have more than one
0182       // associated value at this point, so we have to consider them
0183       // all
0184       for (ModuleValue& thisval : thisvec) {
0185         // Loop over non-geometric dimensions
0186         for (auto k : nonGeoEntries(thisval.paramIndices)) {
0187           double p_i = thisval.paramValues.at(k);
0188           double p_j = values.at(j).paramValues.at(k);
0189           double v_i = thisval.paramVariances.at(k);
0190           double v_j = values.at(j).paramVariances.at(k);
0191 
0192           double left = 0, right = 0;
0193           if (p_i < p_j) {
0194             left = p_i + m_nsigma * std::sqrt(v_i);
0195             right = p_j - m_nsigma * std::sqrt(v_j);
0196           } else {
0197             left = p_j + m_nsigma * std::sqrt(v_j);
0198             right = p_i - m_nsigma * std::sqrt(v_i);
0199           }
0200           if (left < right) {
0201             // We know these two don't match, so break out of the
0202             // dimension loop
0203             matched = false;
0204             break;
0205           }
0206         }  // Loop over `k' (non-geo dimensions)
0207         if (matched) {
0208           // The value under consideration matched at least one
0209           // associated to the current cluster so no need to keep
0210           // checking others in current cluster
0211           break;
0212         }
0213       }  // Loop on current cluster
0214       if (matched) {
0215         // Claim value `j'
0216         used.at(j) = true;
0217         thisvec.push_back(std::move(values.at(j)));
0218       }
0219     }  // Loop on `j'
0220   }  // Loop on `i'
0221   return retv;
0222 }
0223 
0224 ModuleValue ModuleClusters::squash(std::vector<ModuleValue>& values) {
0225   ModuleValue mval;
0226   double tot = 0;
0227   double tot2 = 0;
0228   std::vector<double> weights;
0229 
0230   // First, start by computing cell weights
0231   for (ModuleValue& other : values) {
0232     if (std::holds_alternative<Cluster::Cell>(other.value)) {
0233       weights.push_back(std::get<Cluster::Cell>(other.value).activation);
0234     } else {
0235       weights.push_back(1);
0236     }
0237     tot += weights.back();
0238     tot2 += weights.back() * weights.back();
0239   }
0240 
0241   // Now, go over the non-geometric indices
0242   for (std::size_t i = 0; i < values.size(); i++) {
0243     ModuleValue& other = values.at(i);
0244     for (std::size_t j = 0; j < other.paramIndices.size(); j++) {
0245       auto idx = other.paramIndices.at(j);
0246       if (!rangeContainsValue(m_geoIndices, idx)) {
0247         if (!rangeContainsValue(mval.paramIndices, idx)) {
0248           mval.paramIndices.push_back(idx);
0249         }
0250         if (mval.paramValues.size() < (j + 1)) {
0251           mval.paramValues.push_back(0);
0252           mval.paramVariances.push_back(0);
0253         }
0254         double f = weights.at(i) / (tot > 0 ? tot : 1);
0255         double f2 = weights.at(i) * weights.at(i) / (tot2 > 0 ? tot2 : 1);
0256         mval.paramValues.at(j) += f * other.paramValues.at(j);
0257         mval.paramVariances.at(j) += f2 * other.paramVariances.at(j);
0258       }
0259     }
0260   }
0261 
0262   // Now do the geometric indices
0263   Cluster clus;
0264 
0265   const auto& binningData = m_segmentation.binningData();
0266   Acts::Vector2 pos(0., 0.);
0267   Acts::Vector2 var(0., 0.);
0268 
0269   std::size_t b0min = std::numeric_limits<std::size_t>::max();
0270   std::size_t b0max = 0;
0271   std::size_t b1min = std::numeric_limits<std::size_t>::max();
0272   std::size_t b1max = 0;
0273 
0274   for (std::size_t i = 0; i < values.size(); i++) {
0275     ModuleValue& other = values.at(i);
0276     if (!std::holds_alternative<Cluster::Cell>(other.value)) {
0277       continue;
0278     }
0279 
0280     Cluster::Cell ch = std::get<Cluster::Cell>(other.value);
0281     auto bin = ch.bin;
0282 
0283     std::size_t b0 = bin[0];
0284     std::size_t b1 = bin[1];
0285 
0286     b0min = std::min(b0min, b0);
0287     b0max = std::max(b0max, b0);
0288     b1min = std::min(b1min, b1);
0289     b1max = std::max(b1max, b1);
0290 
0291     float p0 = binningData[0].center(b0);
0292     float w0 = binningData[0].width(b0);
0293     float p1 = binningData[1].center(b1);
0294     float w1 = binningData[1].width(b1);
0295 
0296     pos += Acts::Vector2(weights.at(i) * p0, weights.at(i) * p1);
0297     // Assume uniform distribution to compute error
0298     // N.B. This will overestimate the variance
0299     // but it's better than nothing for now
0300     var += Acts::Vector2(weights.at(i) * weights.at(i) * w0 * w0 / 12,
0301                          weights.at(i) * weights.at(i) * w1 * w1 / 12);
0302 
0303     clus.channels.push_back(std::move(ch));
0304 
0305     // Will have the right value at last iteration Do it here to
0306     // avoid having bogus values when there are no clusters
0307     clus.sizeLoc0 = b0max - b0min + 1;
0308     clus.sizeLoc1 = b1max - b1min + 1;
0309   }
0310 
0311   if (tot > 0) {
0312     pos /= tot;
0313     var /= (tot * tot);
0314   }
0315 
0316   for (auto idx : m_geoIndices) {
0317     mval.paramIndices.push_back(idx);
0318     mval.paramValues.push_back(pos[idx]);
0319     mval.paramVariances.push_back(var[idx]);
0320   }
0321 
0322   mval.value = std::move(clus);
0323 
0324   // Finally do the hit association
0325   for (ModuleValue& other : values) {
0326     mval.sources.merge(other.sources);
0327   }
0328 
0329   return mval;
0330 }
0331 
0332 }  // namespace ActsExamples