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