File indexing completed on 2025-01-18 09:11:22
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Definitions/Tolerance.hpp"
0010 #include "Acts/Geometry/CompositePortalLink.hpp"
0011 #include "Acts/Geometry/GridPortalLink.hpp"
0012 #include "Acts/Surfaces/CylinderSurface.hpp"
0013 #include "Acts/Surfaces/DiscSurface.hpp"
0014 #include "Acts/Surfaces/PlaneSurface.hpp"
0015 #include "Acts/Surfaces/RadialBounds.hpp"
0016
0017 #include <algorithm>
0018 #include <cassert>
0019 #include <limits>
0020 #include <memory>
0021 #include <stdexcept>
0022
0023 namespace Acts {
0024
0025 namespace {
0026
0027 template <typename... Args>
0028 std::unique_ptr<GridPortalLink> makeGrid(
0029 const std::shared_ptr<RegularSurface>& surface, AxisDirection direction,
0030 const Logger& logger, std::tuple<Args...> args, const IAxis* otherAxis,
0031 bool prepend) {
0032
0033
0034 ACTS_VERBOSE("Make resulting merged grid");
0035
0036
0037 auto axisFactory = []<typename... Ts>(Ts&&... axisArgs) {
0038 return Axis{std::forward<Ts>(axisArgs)...};
0039 };
0040
0041
0042 auto makeGrid = [&](auto boundaryType) -> std::unique_ptr<GridPortalLink> {
0043 auto axisArgs = std::tuple_cat(std::tuple{boundaryType}, std::move(args));
0044 auto merged = std::apply(axisFactory, std::move(axisArgs));
0045
0046 if (otherAxis == nullptr) {
0047
0048 ACTS_VERBOSE(" ~> merged axis: " << merged);
0049 return GridPortalLink::make(surface, direction, std::move(merged));
0050 } else {
0051 return otherAxis->visit(
0052 [&](const auto& axis) -> std::unique_ptr<GridPortalLink> {
0053 if (prepend) {
0054 ACTS_VERBOSE(" ~> other axis (prepend): " << axis);
0055 ACTS_VERBOSE(" ~> merged axis: " << merged);
0056 return GridPortalLink::make(surface, axis, std::move(merged));
0057 } else {
0058 ACTS_VERBOSE(" ~> merged axis: " << merged);
0059 ACTS_VERBOSE(" ~> other axis (append): " << axis);
0060 return GridPortalLink::make(surface, std::move(merged), axis);
0061 }
0062 });
0063 }
0064 };
0065
0066
0067
0068 if (direction == AxisDirection::AxisPhi ||
0069 direction == AxisDirection::AxisRPhi) {
0070 if (const auto* cylinder =
0071 dynamic_cast<const CylinderSurface*>(surface.get());
0072 cylinder != nullptr) {
0073 if (cylinder->bounds().coversFullAzimuth()) {
0074 return makeGrid(AxisClosed);
0075 }
0076 } else if (const auto* disc =
0077 dynamic_cast<const DiscSurface*>(surface.get());
0078 disc != nullptr) {
0079 const auto& radialBounds =
0080 dynamic_cast<const RadialBounds&>(disc->bounds());
0081 if (radialBounds.coversFullAzimuth()) {
0082 return makeGrid(AxisClosed);
0083 }
0084 }
0085 }
0086
0087 return makeGrid(AxisBound);
0088 }
0089
0090 std::unique_ptr<GridPortalLink> mergeVariable(
0091 const std::shared_ptr<RegularSurface>& mergedSurface, const IAxis& axisA,
0092 const IAxis& axisB, double , AxisDirection direction,
0093 const Logger& logger, const IAxis* otherAxis, bool prepend) {
0094 ACTS_VERBOSE("Variable merge: direction is " << direction);
0095
0096 ACTS_VERBOSE("~> axis a: " << axisA);
0097 ACTS_VERBOSE("~> axis b: " << axisB);
0098
0099 std::vector<double> binEdges;
0100
0101 binEdges.reserve(axisA.getNBins() + axisB.getNBins() + 1);
0102
0103 auto edgesA = axisA.getBinEdges();
0104
0105 if (direction == AxisDirection::AxisR) {
0106 ACTS_VERBOSE("Performing asymmetric merge");
0107 std::ranges::copy(edgesA, std::back_inserter(binEdges));
0108
0109 } else {
0110 ACTS_VERBOSE("Performing symmetrized merge");
0111 double halfWidth =
0112 (axisA.getMax() - axisA.getMin() + axisB.getMax() - axisB.getMin()) /
0113 2.0;
0114 ACTS_VERBOSE(" ~> half width: " << halfWidth);
0115
0116 double shift = axisA.getMax() - halfWidth;
0117 ACTS_VERBOSE(" ~> shift: " << shift);
0118
0119 std::ranges::transform(edgesA, std::back_inserter(binEdges),
0120 [&](double edge) { return edge + shift; });
0121 }
0122
0123 double stitchPoint = binEdges.back();
0124 auto edgesB = axisB.getBinEdges();
0125 std::transform(
0126 std::next(edgesB.begin()), edgesB.end(), std::back_inserter(binEdges),
0127 [&](double edge) { return edge - axisB.getMin() + stitchPoint; });
0128
0129 return makeGrid(mergedSurface, direction, logger,
0130 std::tuple{std::move(binEdges)}, otherAxis, prepend);
0131 }
0132
0133 std::unique_ptr<GridPortalLink> mergeEquidistant(
0134 const std::shared_ptr<RegularSurface>& mergedSurface, const IAxis& axisA,
0135 const IAxis& axisB, double tolerance, AxisDirection direction,
0136 const Logger& logger, const IAxis* otherAxis, bool prepend) {
0137 ACTS_VERBOSE("===> potentially equidistant merge: checking bin widths");
0138
0139 ACTS_VERBOSE("~> axis a: " << axisA);
0140 ACTS_VERBOSE("~> axis b: " << axisB);
0141
0142 double binsWidthA =
0143 (axisA.getMax() - axisA.getMin()) / static_cast<double>(axisA.getNBins());
0144 double binsWidthB =
0145 (axisB.getMax() - axisB.getMin()) / static_cast<double>(axisB.getNBins());
0146
0147 ACTS_VERBOSE(" ~> binWidths: " << binsWidthA << " vs " << binsWidthB);
0148
0149 if (std::abs(binsWidthA - binsWidthB) < tolerance) {
0150 ACTS_VERBOSE("==> binWidths same: " << binsWidthA);
0151
0152 double min = std::numeric_limits<double>::signaling_NaN();
0153 double max = std::numeric_limits<double>::signaling_NaN();
0154
0155 if (direction == AxisDirection::AxisR) {
0156 ACTS_VERBOSE("Performing asymmetric merge");
0157 min = axisA.getMin();
0158 max = axisB.getMax();
0159 } else {
0160 ACTS_VERBOSE("Performing symmetrized merge");
0161
0162 double halfWidth =
0163 (axisA.getMax() - axisA.getMin() + axisB.getMax() - axisB.getMin()) /
0164 2.0;
0165
0166 min = -halfWidth;
0167 max = halfWidth;
0168 }
0169
0170 return makeGrid(mergedSurface, direction, logger,
0171 std::tuple{min, max, axisA.getNBins() + axisB.getNBins()},
0172 otherAxis, prepend);
0173
0174 } else {
0175 ACTS_VERBOSE("==> binWidths differ: " << binsWidthA << " vs " << binsWidthB
0176 << " ~> variable merge");
0177
0178 std::unique_ptr<GridPortalLink> mergedPortalLink =
0179 mergeVariable(mergedSurface, axisA, axisB, tolerance, direction, logger,
0180 otherAxis, prepend);
0181 return mergedPortalLink;
0182 }
0183 }
0184
0185 std::unique_ptr<GridPortalLink> colinearMerge(
0186 const std::shared_ptr<RegularSurface>& mergedSurface, const IAxis& axisA,
0187 const IAxis& axisB, double tolerance, AxisDirection direction,
0188 const Logger& logger, const IAxis* otherAxis, bool prepend) {
0189 AxisType aType = axisA.getType();
0190 AxisType bType = axisB.getType();
0191 if (axisA.getBoundaryType() != axisB.getBoundaryType()) {
0192 ACTS_WARNING("AxisBoundaryTypes are different");
0193 return nullptr;
0194 }
0195
0196 if (axisA.getBoundaryType() != AxisBoundaryType::Bound) {
0197 ACTS_WARNING("AxisBoundaryType is not Bound, cannot do colinear merge");
0198 return nullptr;
0199 }
0200
0201
0202 auto mergeVariableLocal = [&] {
0203 return mergeVariable(mergedSurface, axisA, axisB, tolerance, direction,
0204 logger, otherAxis, prepend);
0205 };
0206
0207 if (aType == AxisType::Equidistant && bType == AxisType::Equidistant) {
0208 auto mergedPortalLink =
0209 mergeEquidistant(mergedSurface, axisA, axisB, tolerance, direction,
0210 logger, otherAxis, prepend);
0211 return mergedPortalLink;
0212 } else if (aType == AxisType::Variable && bType == AxisType::Variable) {
0213 ACTS_VERBOSE("===> variable merge");
0214 auto mergedPortalLink = mergeVariableLocal();
0215 return mergedPortalLink;
0216 } else if (aType == AxisType::Equidistant && bType == AxisType::Variable) {
0217 ACTS_VERBOSE("===> mixed merged");
0218 auto mergedPortalLink = mergeVariableLocal();
0219 return mergedPortalLink;
0220 } else {
0221 ACTS_VERBOSE("===> mixed merged");
0222 auto mergedPortalLink = mergeVariableLocal();
0223 return mergedPortalLink;
0224 }
0225 }
0226
0227 std::unique_ptr<PortalLinkBase> mergeGridPortals(
0228 const GridPortalLink* a, const GridPortalLink* b,
0229 const RegularSurface* surfaceA, const RegularSurface* surfaceB,
0230 AxisDirection loc0, AxisDirection loc1, AxisDirection direction,
0231 const Logger& logger) {
0232 assert(surfaceA != nullptr);
0233 assert(surfaceB != nullptr);
0234 assert(surfaceA->type() == surfaceB->type());
0235 assert(a->dim() == 2 || a->dim() == 1);
0236 assert(a->dim() == b->dim());
0237
0238 ACTS_VERBOSE(" - a: " << a->surface().bounds());
0239 ACTS_VERBOSE(" - b: " << b->surface().bounds());
0240
0241
0242
0243
0244 constexpr auto tolerance = s_onSurfaceTolerance;
0245
0246 std::shared_ptr<RegularSurface> mergedSurface = nullptr;
0247 bool reversed = false;
0248
0249 if (const auto* cylinderA = dynamic_cast<const CylinderSurface*>(surfaceA);
0250 cylinderA != nullptr) {
0251 std::tie(mergedSurface, reversed) =
0252 cylinderA->mergedWith(dynamic_cast<const CylinderSurface&>(*surfaceB),
0253 direction, true, logger);
0254 } else if (const auto* discA = dynamic_cast<const DiscSurface*>(surfaceA);
0255 discA != nullptr) {
0256 std::tie(mergedSurface, reversed) = discA->mergedWith(
0257 dynamic_cast<const DiscSurface&>(*surfaceB), direction, true, logger);
0258 } else if (const auto* planeA = dynamic_cast<const PlaneSurface*>(surfaceA);
0259 planeA != nullptr) {
0260 throw std::logic_error{"Plane surfaces not implemented yet"};
0261 } else {
0262 throw std::invalid_argument{"Unsupported surface type"};
0263 }
0264
0265 ACTS_VERBOSE("Merged surface: " << mergedSurface->bounds());
0266 ACTS_VERBOSE("~> reversed? " << std::boolalpha << reversed);
0267
0268
0269
0270 if (reversed) {
0271 ACTS_VERBOSE("Swapping grid portals and surfaces after merging");
0272 std::swap(surfaceA, surfaceB);
0273 std::swap(a, b);
0274 }
0275
0276
0277
0278
0279 auto fillGrid = [&](std::unique_ptr<GridPortalLink> merged) {
0280 if (merged != nullptr) {
0281 ACTS_VERBOSE("Post processing merged grid: " << merged->grid());
0282 GridPortalLink::fillMergedGrid(*a, *b, *merged, direction, logger);
0283 }
0284 return merged;
0285 };
0286
0287 if (a->dim() == 1) {
0288 ACTS_VERBOSE("Merge two 1D GridPortalLinks on " << surfaceA->name()
0289 << "s in " << direction);
0290 if (a->direction() != b->direction()) {
0291 ACTS_VERBOSE("GridPortalLinks have different directions");
0292 ACTS_VERBOSE("=> cross merge");
0293
0294
0295 const auto* aligned = a->direction() == direction ? a : b;
0296 const auto* alignedSurface =
0297 a->direction() == direction ? surfaceA : surfaceB;
0298 const auto* other = a->direction() == direction ? b : a;
0299 const auto* otherSurface =
0300 a->direction() == direction ? surfaceB : surfaceA;
0301
0302
0303 auto aligned2D = aligned->extendTo2d(other->grid().axes().front());
0304
0305 auto other2D = other->extendTo2d(nullptr);
0306
0307 assert(aligned2D != nullptr);
0308 assert(other2D != nullptr);
0309
0310 ACTS_VERBOSE("Expanded grids:");
0311 ACTS_VERBOSE(" - aligned: " << aligned2D->grid());
0312 ACTS_VERBOSE(" - other: " << other2D->grid());
0313
0314
0315 if (a->direction() == direction) {
0316 return mergeGridPortals(aligned2D.get(), other2D.get(), alignedSurface,
0317 otherSurface, loc0, loc1, direction, logger);
0318 } else {
0319 return mergeGridPortals(other2D.get(), aligned2D.get(), otherSurface,
0320 alignedSurface, loc0, loc1, direction, logger);
0321 }
0322 }
0323
0324 const auto& axisA = *a->grid().axes().front();
0325 const auto& axisB = *b->grid().axes().front();
0326
0327 if (direction == loc0) {
0328 ACTS_VERBOSE("Grids are binned along " << a->direction());
0329 if (a->direction() == loc0) {
0330 ACTS_VERBOSE("=> colinear merge");
0331
0332 return fillGrid(colinearMerge(mergedSurface, axisA, axisB, tolerance,
0333 loc0, logger, nullptr, false));
0334
0335 } else {
0336 ACTS_VERBOSE("=> parallel merge");
0337
0338 auto a2D = a->extendTo2d(nullptr);
0339 auto b2D = b->extendTo2d(nullptr);
0340 assert(a2D != nullptr);
0341 assert(b2D != nullptr);
0342
0343 ACTS_VERBOSE("Expanded grids:");
0344 ACTS_VERBOSE(" - a: " << a2D->grid());
0345 ACTS_VERBOSE(" - b: " << b2D->grid());
0346
0347 return mergeGridPortals(a2D.get(), b2D.get(), surfaceA, surfaceB, loc0,
0348 loc1, direction, logger);
0349 }
0350 } else if (direction == loc1) {
0351 ACTS_VERBOSE("Grids are binned along " << a->direction());
0352 if (a->direction() == loc1) {
0353 ACTS_VERBOSE("=> colinear merge");
0354
0355 return fillGrid(colinearMerge(mergedSurface, axisA, axisB, tolerance,
0356 loc1, logger, nullptr, false));
0357
0358 } else {
0359 ACTS_VERBOSE("=> parallel merge");
0360 auto a2D = a->extendTo2d(nullptr);
0361 auto b2D = b->extendTo2d(nullptr);
0362 assert(a2D != nullptr);
0363 assert(b2D != nullptr);
0364
0365 ACTS_VERBOSE("Expanded grids:");
0366 ACTS_VERBOSE(" - a: " << a2D->grid());
0367 ACTS_VERBOSE(" - b: " << b2D->grid());
0368 return mergeGridPortals(a2D.get(), b2D.get(), surfaceA, surfaceB, loc0,
0369 loc1, direction, logger);
0370 }
0371
0372 } else {
0373 ACTS_ERROR("Invalid binning direction: " << direction);
0374 throw std::invalid_argument{"Invalid binning direction"};
0375 }
0376 } else {
0377 ACTS_VERBOSE("Merging two 2D GridPortalLinks on "
0378 << surfaceA->name() << "s in " << direction << " direction");
0379
0380 const auto& loc0AxisA = *a->grid().axes().front();
0381 const auto& loc1AxisA = *a->grid().axes().back();
0382 const auto& loc0AxisB = *b->grid().axes().front();
0383 const auto& loc1AxisB = *b->grid().axes().back();
0384
0385 if (direction == loc0) {
0386 ACTS_VERBOSE("=> colinear merge along " << loc0);
0387 ACTS_VERBOSE("--> Checking if " << loc1 << " axes are identical");
0388
0389 if (loc1AxisA != loc1AxisB) {
0390 ACTS_WARNING(" ~> "
0391 << loc1
0392 << " axes are not identical, falling back to composite "
0393 "merging");
0394 return nullptr;
0395 }
0396 ACTS_VERBOSE(" ~> they are!");
0397
0398 ACTS_VERBOSE(" ~> " << loc1 << " axis: " << loc1AxisA);
0399 return fillGrid(colinearMerge(mergedSurface, loc0AxisA, loc0AxisB,
0400 tolerance, loc0, logger, &loc1AxisA,
0401 false));
0402
0403 } else if (direction == loc1) {
0404 ACTS_VERBOSE("=> colinear merge along " << loc1);
0405 ACTS_VERBOSE("--> Checking if " << loc0 << " axes are identical");
0406
0407 if (loc0AxisA != loc0AxisB) {
0408 ACTS_WARNING(" ~> "
0409 << loc0
0410 << " axes are not identical, falling back to composite "
0411 "merging");
0412 return nullptr;
0413 }
0414 ACTS_VERBOSE(" ~> they are!");
0415
0416 ACTS_VERBOSE(" ~> " << loc0 << " axis: " << loc0AxisA);
0417 return fillGrid(colinearMerge(mergedSurface, loc1AxisA, loc1AxisB,
0418 tolerance, loc1, logger, &loc0AxisA, true));
0419
0420 } else {
0421 ACTS_ERROR("Invalid binning direction: " << a->direction());
0422 throw std::invalid_argument{"Invalid binning direction"};
0423 }
0424 }
0425 }
0426
0427 std::unique_ptr<PortalLinkBase> mergeGridPortals(const GridPortalLink* a,
0428 const GridPortalLink* b,
0429 AxisDirection direction,
0430 const Logger& logger) {
0431 using enum AxisDirection;
0432 assert(a->dim() == 2 || a->dim() == 1);
0433 assert(b->dim() == 2 || b->dim() == 1);
0434
0435 if (a->dim() < b->dim()) {
0436 return mergeGridPortals(b, a, direction, logger);
0437 }
0438
0439 ACTS_VERBOSE("Merging GridPortalLinks along " << direction << ":");
0440 ACTS_VERBOSE(" - a: " << a->grid() << " along: " << a->direction());
0441 ACTS_VERBOSE(" - b: " << b->grid() << " along: " << b->direction());
0442
0443 const auto* cylinder = dynamic_cast<const CylinderSurface*>(&a->surface());
0444 const auto* disc = dynamic_cast<const DiscSurface*>(&a->surface());
0445
0446 if (a->dim() == b->dim()) {
0447 ACTS_VERBOSE("Grid both have same dimension: " << a->dim());
0448
0449 if (cylinder != nullptr) {
0450 return mergeGridPortals(
0451 a, b, cylinder, &dynamic_cast<const CylinderSurface&>(b->surface()),
0452 AxisRPhi, AxisZ, direction, logger);
0453 } else if (disc != nullptr) {
0454 return mergeGridPortals(a, b, disc,
0455 &dynamic_cast<const DiscSurface&>(b->surface()),
0456 AxisR, AxisPhi, direction, logger);
0457 } else {
0458
0459 ACTS_VERBOSE("Surface type is not supported here, falling back");
0460 return nullptr;
0461 }
0462 } else {
0463 ACTS_VERBOSE("Grids have different dimension, extending rhs to 2D");
0464 const IAxis* otherAxis = nullptr;
0465
0466 if (b->direction() == direction) {
0467 ACTS_VERBOSE("1D grid is binned in merging direction " << direction);
0468 ACTS_VERBOSE("~> Adding complementary axis");
0469
0470 if (cylinder != nullptr) {
0471 otherAxis = direction == AxisRPhi ? a->grid().axes().back()
0472 : a->grid().axes().front();
0473 } else if (disc != nullptr) {
0474 otherAxis = direction == AxisR ? a->grid().axes().back()
0475 : a->grid().axes().front();
0476 } else {
0477 ACTS_VERBOSE("Surface type is not supported here, falling back");
0478 return nullptr;
0479 }
0480 } else {
0481 ACTS_VERBOSE("1D grid is binned in complementary direction");
0482 }
0483
0484 auto b2D = b->extendTo2d(otherAxis);
0485 ACTS_VERBOSE("-> new grid: " << b2D->grid());
0486 return mergeGridPortals(a, b2D.get(), direction, logger);
0487 }
0488 }
0489
0490 }
0491
0492 void GridPortalLink::fillMergedGrid(const GridPortalLink& a,
0493 const GridPortalLink& b,
0494 GridPortalLink& merged,
0495 AxisDirection direction,
0496 const Logger& logger) {
0497 ACTS_VERBOSE("Filling merged grid");
0498 assert(a.dim() == b.dim());
0499 assert(a.direction() == b.direction());
0500 const auto locBinsA = a.numLocalBins();
0501 const auto locBinsB = b.numLocalBins();
0502
0503 ACTS_VERBOSE("a: " << a.grid());
0504 ACTS_VERBOSE("b: " << b.grid());
0505 ACTS_VERBOSE("merged: " << merged.grid());
0506
0507 if (a.dim() == 1) {
0508 std::size_t nBinsA = locBinsA.at(0);
0509 std::size_t nBinsB = locBinsB.at(0);
0510
0511 for (std::size_t i = 1; i <= nBinsA; ++i) {
0512 merged.atLocalBins({i}) = a.atLocalBins({i});
0513 }
0514 for (std::size_t i = 1; i <= nBinsB; ++i) {
0515 merged.atLocalBins({nBinsA + i}) = b.atLocalBins({i});
0516 }
0517 } else {
0518 if (a.direction() == direction) {
0519 std::size_t nBinsA = locBinsA.at(0);
0520 std::size_t nBinsB = locBinsB.at(0);
0521 std::size_t nBinsCommon = locBinsB.at(1);
0522
0523 for (std::size_t i = 1; i <= nBinsA; ++i) {
0524 for (std::size_t j = 1; j <= nBinsCommon; ++j) {
0525 merged.atLocalBins({i, j}) = a.atLocalBins({i, j});
0526 }
0527 }
0528
0529 for (std::size_t i = 1; i <= nBinsB; ++i) {
0530 for (std::size_t j = 1; j <= nBinsCommon; ++j) {
0531 std::size_t ti = i + nBinsA;
0532 merged.atLocalBins({ti, j}) = b.atLocalBins({i, j});
0533 }
0534 }
0535 } else {
0536 std::size_t nBinsA = locBinsA.at(1);
0537 std::size_t nBinsB = locBinsB.at(1);
0538 std::size_t nBinsCommon = locBinsB.at(0);
0539
0540 for (std::size_t i = 1; i <= nBinsCommon; ++i) {
0541 for (std::size_t j = 1; j <= nBinsA; ++j) {
0542 merged.atLocalBins({i, j}) = a.atLocalBins({i, j});
0543 }
0544 }
0545
0546 for (std::size_t i = 1; i <= nBinsCommon; ++i) {
0547 for (std::size_t j = 1; j <= nBinsB; ++j) {
0548 std::size_t tj = j + nBinsA;
0549 merged.atLocalBins({i, tj}) = b.atLocalBins({i, j});
0550 }
0551 }
0552 }
0553 }
0554 }
0555
0556 std::unique_ptr<PortalLinkBase> GridPortalLink::merge(const GridPortalLink& a,
0557 const GridPortalLink& b,
0558 AxisDirection direction,
0559 const Logger& logger) {
0560 ACTS_VERBOSE("Merging two GridPortalLinks");
0561
0562 checkMergePreconditions(a, b, direction);
0563
0564 auto merged = mergeGridPortals(&a, &b, direction, logger);
0565 if (merged == nullptr) {
0566 ACTS_WARNING("Grid merging failed returning nullptr");
0567 return nullptr;
0568 }
0569
0570 return merged;
0571 }
0572
0573 }