File indexing completed on 2025-08-05 08:09:48
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/TrackFinding/HoughTransformSeeder.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/EventData/SourceLink.hpp"
0015 #include "Acts/Geometry/TrackingGeometry.hpp"
0016 #include "Acts/Surfaces/Surface.hpp"
0017 #include "Acts/Utilities/Enumerate.hpp"
0018 #include "ActsExamples/EventData/GeometryContainers.hpp"
0019 #include "ActsExamples/EventData/Index.hpp"
0020 #include "ActsExamples/EventData/IndexSourceLink.hpp"
0021 #include "ActsExamples/EventData/Measurement.hpp"
0022 #include "ActsExamples/EventData/ProtoTrack.hpp"
0023 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0024 #include "ActsExamples/TrackFinding/DefaultHoughFunctions.hpp"
0025 #include "ActsExamples/Utilities/GroupBy.hpp"
0026 #include "ActsExamples/Utilities/Range.hpp"
0027
0028 #include <algorithm>
0029 #include <cmath>
0030 #include <iterator>
0031 #include <ostream>
0032 #include <stdexcept>
0033 #include <variant>
0034
0035 static inline int quant(double min, double max, unsigned nSteps, double val);
0036 static inline double unquant(double min, double max, unsigned nSteps, int step);
0037 template <typename T>
0038 static inline std::string to_string(std::vector<T> v);
0039
0040 ActsExamples::HoughTransformSeeder::HoughTransformSeeder(
0041 ActsExamples::HoughTransformSeeder::Config cfg, Acts::Logging::Level lvl)
0042 : ActsExamples::IAlgorithm("HoughTransformSeeder", lvl),
0043 m_cfg(std::move(cfg)),
0044 m_logger(Acts::getDefaultLogger("HoughTransformSeeder", lvl)) {
0045
0046
0047 bool foundInput = false;
0048 for (const auto& spName : m_cfg.inputSpacePoints) {
0049 if (!(spName.empty())) {
0050 foundInput = true;
0051 }
0052
0053 auto& handle = m_inputSpacePoints.emplace_back(
0054 std::make_unique<ReadDataHandle<SimSpacePointContainer>>(
0055 this,
0056 "InputSpacePoints#" + std::to_string(m_inputSpacePoints.size())));
0057 handle->initialize(spName);
0058 }
0059 if (!(m_cfg.inputMeasurements.empty())) {
0060 foundInput = true;
0061 }
0062
0063 if (!foundInput) {
0064 throw std::invalid_argument(
0065 "HoughTransformSeeder: Missing some kind of input (measurements of "
0066 "spacepoints)");
0067 }
0068
0069 if (m_cfg.outputProtoTracks.empty()) {
0070 throw std::invalid_argument(
0071 "HoughTransformSeeder: Missing hough tracks output collection");
0072 }
0073 if (m_cfg.outputSeeds.empty()) {
0074 throw std::invalid_argument(
0075 "HoughTransformSeeder: Missing hough track seeds output collection");
0076 }
0077
0078 if (m_cfg.inputSourceLinks.empty()) {
0079 throw std::invalid_argument(
0080 "HoughTransformSeeder: Missing source link input collection");
0081 }
0082
0083 m_outputProtoTracks.initialize(m_cfg.outputProtoTracks);
0084 m_inputSourceLinks.initialize(m_cfg.inputSourceLinks);
0085 m_inputMeasurements.initialize(m_cfg.inputMeasurements);
0086
0087 if (!m_cfg.trackingGeometry) {
0088 throw std::invalid_argument(
0089 "HoughTransformSeeder: Missing tracking geometry");
0090 }
0091
0092 if (m_cfg.geometrySelection.empty()) {
0093 throw std::invalid_argument(
0094 "HoughTransformSeeder: Missing geometry selection");
0095 }
0096
0097 for (const auto& geoId : m_cfg.geometrySelection) {
0098 if ((geoId.approach() != 0u) || (geoId.boundary() != 0u) ||
0099 (geoId.sensitive() != 0u)) {
0100 throw std::invalid_argument(
0101 "HoughTransformSeeder: Invalid geometry selection: only volume and "
0102 "layer are allowed to be set");
0103 }
0104 }
0105
0106
0107
0108
0109
0110 auto isDuplicate = [](Acts::GeometryIdentifier ref,
0111 Acts::GeometryIdentifier cmp) {
0112
0113
0114 if (ref.volume() == 0) {
0115 return true;
0116 }
0117
0118 if (ref.volume() != cmp.volume()) {
0119 return false;
0120 }
0121
0122 return (ref.layer() == cmp.layer());
0123 };
0124 auto geoSelBeg = m_cfg.geometrySelection.begin();
0125 auto geoSelEnd = m_cfg.geometrySelection.end();
0126
0127 std::sort(geoSelBeg, geoSelEnd);
0128 auto geoSelLastUnique = std::unique(geoSelBeg, geoSelEnd, isDuplicate);
0129 if (geoSelLastUnique != geoSelEnd) {
0130 ACTS_WARNING("Removed " << std::distance(geoSelLastUnique, geoSelEnd)
0131 << " geometry selection duplicates");
0132 m_cfg.geometrySelection.erase(geoSelLastUnique, geoSelEnd);
0133 }
0134 ACTS_INFO("Hough geometry selection:");
0135 for (const auto& geoId : m_cfg.geometrySelection) {
0136 ACTS_INFO(" " << geoId);
0137 }
0138
0139
0140 m_step_x = (m_cfg.xMax - m_cfg.xMin) / m_cfg.houghHistSize_x;
0141 m_step_y = (m_cfg.yMax - m_cfg.yMin) / m_cfg.houghHistSize_y;
0142 for (unsigned i = 0; i <= m_cfg.houghHistSize_x; i++) {
0143 m_bins_x.push_back(
0144 unquant(m_cfg.xMin, m_cfg.xMax, m_cfg.houghHistSize_x, i));
0145 }
0146 for (unsigned i = 0; i <= m_cfg.houghHistSize_y; i++) {
0147 m_bins_y.push_back(
0148 unquant(m_cfg.yMin, m_cfg.yMax, m_cfg.houghHistSize_y, i));
0149 }
0150
0151 m_cfg.fieldCorrector
0152 .connect<&ActsExamples::DefaultHoughFunctions::fieldCorrectionDefault>();
0153 m_cfg.layerIDFinder
0154 .connect<&ActsExamples::DefaultHoughFunctions::findLayerIDDefault>();
0155 m_cfg.sliceTester
0156 .connect<&ActsExamples::DefaultHoughFunctions::inSliceDefault>();
0157 }
0158
0159 ActsExamples::ProcessCode ActsExamples::HoughTransformSeeder::execute(
0160 const AlgorithmContext& ctx) const {
0161
0162 houghMeasurementStructs.clear();
0163
0164
0165 addSpacePoints(ctx);
0166
0167
0168 addMeasurements(ctx);
0169
0170 static thread_local ProtoTrackContainer protoTracks;
0171 protoTracks.clear();
0172
0173
0174 for (int subregion : m_cfg.subRegions) {
0175 ACTS_DEBUG("Processing subregion " << subregion);
0176 ActsExamples::HoughHist m_houghHist = createHoughHist(subregion);
0177
0178 for (unsigned y = 0; y < m_cfg.houghHistSize_y; y++) {
0179 for (unsigned x = 0; x < m_cfg.houghHistSize_x; x++) {
0180 if (passThreshold(m_houghHist, x, y)) {
0181
0182
0183
0184
0185 std::vector<std::vector<std::vector<Index>>> hitIndicesAll(
0186 m_cfg.nLayers);
0187 std::vector<std::size_t> nHitsPerLayer(m_cfg.nLayers);
0188 for (auto measurementIndex : m_houghHist(y, x).second) {
0189 HoughMeasurementStruct* meas =
0190 houghMeasurementStructs[measurementIndex].get();
0191 hitIndicesAll[meas->layer].push_back(meas->indices);
0192 nHitsPerLayer[meas->layer]++;
0193 }
0194 std::vector<std::vector<int>> combs = getComboIndices(nHitsPerLayer);
0195
0196 for (auto [icomb, hit_indices] :
0197 Acts::enumerate(combs)) {
0198
0199 ProtoTrack protoTrack;
0200 for (unsigned layer = 0; layer < m_cfg.nLayers; layer++) {
0201 if (hit_indices[layer] >= 0) {
0202 for (auto index : hitIndicesAll[layer][hit_indices[layer]]) {
0203 protoTrack.push_back(index);
0204 }
0205 }
0206 }
0207 protoTracks.push_back(protoTrack);
0208 }
0209 }
0210 }
0211 }
0212 }
0213 ACTS_DEBUG("Created " << protoTracks.size() << " proto track");
0214
0215 m_outputProtoTracks(ctx, ProtoTrackContainer{protoTracks});
0216
0217 houghMeasurementStructs.clear();
0218 return ActsExamples::ProcessCode::SUCCESS;
0219 }
0220
0221 ActsExamples::HoughHist
0222 ActsExamples::HoughTransformSeeder::createLayerHoughHist(unsigned layer,
0223 int subregion) const {
0224 ActsExamples::HoughHist houghHist(m_cfg.houghHistSize_y,
0225 m_cfg.houghHistSize_x);
0226
0227 for (unsigned index = 0; index < houghMeasurementStructs.size(); index++) {
0228 HoughMeasurementStruct* meas = houghMeasurementStructs[index].get();
0229 if (meas->layer != layer) {
0230 continue;
0231 }
0232 if (!(m_cfg.sliceTester(meas->z, meas->layer, subregion)).value()) {
0233 continue;
0234 }
0235
0236
0237 for (unsigned y_ = 0; y_ < m_cfg.houghHistSize_y; y_++) {
0238 unsigned y_bin_min = y_;
0239 unsigned y_bin_max = (y_ + 1);
0240
0241
0242 auto xBins =
0243 yToXBins(y_bin_min, y_bin_max, meas->radius, meas->phi, meas->layer);
0244
0245 for (unsigned y = y_bin_min; y < y_bin_max; y++) {
0246 for (unsigned x = xBins.first; x < xBins.second; x++) {
0247 houghHist(y, x).first++;
0248 houghHist(y, x).second.insert(index);
0249 }
0250 }
0251 }
0252 }
0253
0254 return houghHist;
0255 }
0256
0257 ActsExamples::HoughHist ActsExamples::HoughTransformSeeder::createHoughHist(
0258 int subregion) const {
0259 ActsExamples::HoughHist houghHist(m_cfg.houghHistSize_y,
0260 m_cfg.houghHistSize_x);
0261
0262 for (unsigned i = 0; i < m_cfg.nLayers; i++) {
0263 HoughHist layerHoughHist = createLayerHoughHist(i, subregion);
0264 for (unsigned x = 0; x < m_cfg.houghHistSize_x; ++x) {
0265 for (unsigned y = 0; y < m_cfg.houghHistSize_y; ++y) {
0266 if (layerHoughHist(y, x).first > 0) {
0267 houghHist(y, x).first++;
0268 houghHist(y, x).second.insert(layerHoughHist(y, x).second.begin(),
0269 layerHoughHist(y, x).second.end());
0270 }
0271 }
0272 }
0273 }
0274
0275 return houghHist;
0276 }
0277
0278 bool ActsExamples::HoughTransformSeeder::passThreshold(
0279 HoughHist const& houghHist, unsigned x, unsigned y) const {
0280
0281 unsigned width = m_cfg.threshold.size() / 2;
0282 if (x < width || (houghHist.size(1) - x) < width) {
0283 return false;
0284 }
0285 for (unsigned i = 0; i < m_cfg.threshold.size(); i++) {
0286 if (houghHist(y, x - width + i).first < m_cfg.threshold[i]) {
0287 return false;
0288 }
0289 }
0290
0291
0292 if (m_cfg.localMaxWindowSize != 0) {
0293 for (int j = -m_cfg.localMaxWindowSize; j <= m_cfg.localMaxWindowSize;
0294 j++) {
0295 for (int i = -m_cfg.localMaxWindowSize; i <= m_cfg.localMaxWindowSize;
0296 i++) {
0297 if (i == 0 && j == 0) {
0298 continue;
0299 }
0300 if (y + j < houghHist.size(0) && x + i < houghHist.size(1)) {
0301 if (houghHist(y + j, x + i).first > houghHist(y, x).first) {
0302 return false;
0303 }
0304 if (houghHist(y + j, x + i).first == houghHist(y, x).first) {
0305 if (houghHist(y + j, x + i).second.size() >
0306 houghHist(y, x).second.size()) {
0307 return false;
0308 }
0309 if (houghHist(y + j, x + i).second.size() ==
0310 houghHist(y, x).second.size() &&
0311 j <= 0 && i <= 0) {
0312 return false;
0313 }
0314 }
0315 }
0316 }
0317 }
0318 }
0319
0320 return true;
0321 }
0322
0323
0324
0325
0326
0327
0328 static inline int quant(double min, double max, unsigned nSteps, double val) {
0329 return static_cast<int>((val - min) / (max - min) * nSteps);
0330 }
0331
0332
0333 static inline double unquant(double min, double max, unsigned nSteps,
0334 int step) {
0335 return min + (max - min) * step / nSteps;
0336 }
0337
0338 template <typename T>
0339 static inline std::string to_string(std::vector<T> v) {
0340 std::ostringstream oss;
0341 oss << "[";
0342 if (!v.empty()) {
0343 std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(oss, ", "));
0344 oss << v.back();
0345 }
0346 oss << "]";
0347 return oss.str();
0348 }
0349
0350 double ActsExamples::HoughTransformSeeder::yToX(double y, double r,
0351 double phi) const {
0352 double d0 = 0;
0353 double x =
0354 asin(r * ActsExamples::HoughTransformSeeder::m_cfg.kA * y - d0 / r) + phi;
0355
0356 if (m_cfg.fieldCorrector.connected()) {
0357 x += (m_cfg.fieldCorrector(0, y, r)).value();
0358 }
0359
0360 return x;
0361 }
0362
0363
0364
0365
0366 std::pair<unsigned, unsigned> ActsExamples::HoughTransformSeeder::yToXBins(
0367 std::size_t yBin_min, std::size_t yBin_max, double r, double phi,
0368 unsigned layer) const {
0369 double x_min = yToX(m_bins_y[yBin_min], r, phi);
0370 double x_max = yToX(m_bins_y[yBin_max], r, phi);
0371 if (x_min > x_max) {
0372 std::swap(x_min, x_max);
0373 }
0374 if (x_max < m_cfg.xMin || x_min > m_cfg.xMax) {
0375 return {0, 0};
0376 }
0377
0378
0379 int x_bin_min = quant(m_cfg.xMin, m_cfg.xMax, m_cfg.houghHistSize_x, x_min);
0380 int x_bin_max = quant(m_cfg.xMin, m_cfg.xMax, m_cfg.houghHistSize_x, x_max) +
0381 1;
0382
0383
0384 unsigned extend = getExtension(yBin_min, layer);
0385 x_bin_min -= extend;
0386 x_bin_max += extend;
0387
0388
0389 if (x_bin_min < 0) {
0390 x_bin_min = 0;
0391 }
0392 if (x_bin_max > static_cast<int>(m_cfg.houghHistSize_x)) {
0393 x_bin_max = m_cfg.houghHistSize_x;
0394 }
0395
0396 return {x_bin_min, x_bin_max};
0397 }
0398
0399
0400
0401 unsigned ActsExamples::HoughTransformSeeder::getExtension(
0402 unsigned y, unsigned layer) const {
0403 if (m_cfg.hitExtend_x.size() == m_cfg.nLayers) {
0404 return m_cfg.hitExtend_x[layer];
0405 }
0406
0407 if (m_cfg.hitExtend_x.size() == m_cfg.nLayers * 2) {
0408
0409
0410
0411 if (y < m_cfg.houghHistSize_y / 4 || y > 3 * m_cfg.houghHistSize_y / 4) {
0412 return m_cfg.hitExtend_x[layer];
0413 }
0414
0415 return m_cfg.hitExtend_x[m_cfg.nLayers + layer];
0416 }
0417 return 0;
0418 }
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428
0429
0430
0431
0432
0433
0434
0435 std::vector<std::vector<int>>
0436 ActsExamples::HoughTransformSeeder::getComboIndices(
0437 std::vector<std::size_t>& sizes) const {
0438 std::size_t nCombs = 1;
0439 std::vector<std::size_t> nCombs_prior(sizes.size());
0440 std::vector<int> temp(sizes.size(), 0);
0441
0442 for (std::size_t i = 0; i < sizes.size(); i++) {
0443 if (sizes[i] > 0) {
0444 nCombs_prior[i] = nCombs;
0445 nCombs *= sizes[i];
0446 } else {
0447 temp[i] = -1;
0448 }
0449 }
0450
0451 std::vector<std::vector<int>> combos(nCombs, temp);
0452
0453 for (std::size_t icomb = 0; icomb < nCombs; icomb++) {
0454 std::size_t index = icomb;
0455 for (std::size_t isize = sizes.size() - 1; isize < sizes.size(); isize--) {
0456 if (sizes[isize] == 0) {
0457 continue;
0458 }
0459 combos[icomb][isize] = static_cast<int>(index / nCombs_prior[isize]);
0460 index = index % nCombs_prior[isize];
0461 }
0462 }
0463
0464 return combos;
0465 }
0466
0467 void ActsExamples::HoughTransformSeeder::addSpacePoints(
0468 const AlgorithmContext& ctx) const {
0469
0470
0471 for (const auto& isp : m_inputSpacePoints) {
0472 const auto& spContainer = (*isp)(ctx);
0473 ACTS_DEBUG("Inserting " << spContainer.size() << " space points from "
0474 << isp->key());
0475 for (auto& sp : spContainer) {
0476 double r = std::hypot(sp.x(), sp.y());
0477 double z = sp.z();
0478 float phi = std::atan2(sp.y(), sp.x());
0479 ResultUnsigned hitlayer = m_cfg.layerIDFinder(r).value();
0480 if (!(hitlayer.ok())) {
0481 continue;
0482 }
0483 std::vector<Index> indices;
0484 for (const auto& slink : sp.sourceLinks()) {
0485 const auto& islink = slink.get<IndexSourceLink>();
0486 indices.push_back(islink.index());
0487 }
0488
0489 auto meas =
0490 std::shared_ptr<HoughMeasurementStruct>(new HoughMeasurementStruct(
0491 hitlayer.value(), phi, r, z, indices, HoughHitType::SP));
0492 houghMeasurementStructs.push_back(meas);
0493 }
0494 }
0495 }
0496
0497 void ActsExamples::HoughTransformSeeder::addMeasurements(
0498 const AlgorithmContext& ctx) const {
0499 const auto& measurements = m_inputMeasurements(ctx);
0500 const auto& sourceLinks = m_inputSourceLinks(ctx);
0501
0502 ACTS_DEBUG("Inserting " << measurements.size() << " space points from "
0503 << m_cfg.inputMeasurements);
0504
0505 for (Acts::GeometryIdentifier geoId : m_cfg.geometrySelection) {
0506
0507 auto range = selectLowestNonZeroGeometryObject(sourceLinks, geoId);
0508
0509
0510 auto groupedByModule = makeGroupBy(range, detail::GeometryIdGetter());
0511
0512 for (auto [moduleGeoId, moduleSourceLinks] : groupedByModule) {
0513
0514 const Acts::Surface* surface =
0515 m_cfg.trackingGeometry->findSurface(moduleGeoId);
0516 if (surface == nullptr) {
0517 ACTS_ERROR("Could not find surface " << moduleGeoId);
0518 return;
0519 }
0520
0521 for (auto& sourceLink : moduleSourceLinks) {
0522
0523
0524
0525
0526
0527
0528 auto [localPos, localCov] = std::visit(
0529 [](const auto& meas) {
0530 auto expander = meas.expander();
0531 Acts::BoundVector par = expander * meas.parameters();
0532 Acts::BoundSquareMatrix cov =
0533 expander * meas.covariance() * expander.transpose();
0534
0535 Acts::Vector2 lpar(par[Acts::eBoundLoc0], par[Acts::eBoundLoc1]);
0536
0537 Acts::SquareMatrix2 lcov =
0538 cov.block<2, 2>(Acts::eBoundLoc0, Acts::eBoundLoc0);
0539 return std::make_pair(lpar, lcov);
0540 },
0541 measurements[sourceLink.index()]);
0542
0543
0544 Acts::Vector3 globalFakeMom(1, 1, 1);
0545 Acts::Vector3 globalPos =
0546 surface->localToGlobal(ctx.geoContext, localPos, globalFakeMom);
0547 double r = std::hypot(globalPos[Acts::ePos0], globalPos[Acts::ePos1]);
0548 double phi = std::atan2(globalPos[Acts::ePos1], globalPos[Acts::ePos0]);
0549 double z = globalPos[Acts::ePos2];
0550 ResultUnsigned hitlayer = m_cfg.layerIDFinder(r);
0551 if (hitlayer.ok()) {
0552 std::vector<Index> index;
0553 index.push_back(sourceLink.index());
0554 auto meas = std::shared_ptr<HoughMeasurementStruct>(
0555 new HoughMeasurementStruct(hitlayer.value(), phi, r, z, index,
0556 HoughHitType::MEASUREMENT));
0557 houghMeasurementStructs.push_back(meas);
0558 }
0559 }
0560 }
0561 }
0562 }