File indexing completed on 2026-08-20 08:20:05
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Surfaces/CylinderBounds.hpp"
0013 #include "Acts/Surfaces/CylinderSurface.hpp"
0014 #include "Acts/Surfaces/DiscSurface.hpp"
0015 #include "Acts/Surfaces/PlaneSurface.hpp"
0016 #include "Acts/Surfaces/RadialBounds.hpp"
0017 #include "Acts/Surfaces/RectangleBounds.hpp"
0018 #include "Acts/Surfaces/StrawSurface.hpp"
0019 #include "Acts/Surfaces/TrapezoidBounds.hpp"
0020 #include "Acts/Utilities/AxisDefinitions.hpp"
0021 #include "Acts/Utilities/AxisSpec.hpp"
0022 #include "Acts/Utilities/MultiAxisSpec.hpp"
0023 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0024
0025 #include <array>
0026 #include <numbers>
0027 #include <optional>
0028 #include <stdexcept>
0029
0030 using namespace Acts;
0031
0032 namespace ActsTests {
0033
0034 BOOST_AUTO_TEST_SUITE(UtilitiesSuite)
0035
0036 BOOST_AUTO_TEST_CASE(MultiAxisSpecBasics) {
0037 using enum Acts::AxisBoundaryType;
0038
0039 MultiAxisSpec maf({AxisSpec::Equidistant(10, 0., 1., Bound),
0040 AxisSpec::Variable({0., 1., 3.}, Open)});
0041 BOOST_CHECK_EQUAL(maf.size(), 2);
0042 BOOST_CHECK(!maf.isDeferred());
0043 BOOST_CHECK_EQUAL(maf.axisSpec(0).nBins(), 10);
0044 BOOST_CHECK_EQUAL(maf.axisSpec(1).nBins(), 2);
0045 BOOST_CHECK_THROW(maf.axisSpec(2), std::out_of_range);
0046
0047 auto multiAxis = maf.buildMultiAxis();
0048 BOOST_CHECK_EQUAL(multiAxis->getNAxes(), 2);
0049 BOOST_CHECK(multiAxis->getAxis(0).isEquidistant());
0050 BOOST_CHECK(multiAxis->getAxis(1).isVariable());
0051 BOOST_CHECK_EQUAL(multiAxis->getAxis(0).getNBins(), 10);
0052 BOOST_CHECK_EQUAL(multiAxis->getAxis(1).getNBins(), 2);
0053
0054
0055 BOOST_CHECK_THROW(MultiAxisSpec({}), std::invalid_argument);
0056
0057
0058 MultiAxisSpec mixed({AxisSpec::Equidistant(10, 0., 1., Bound),
0059 AxisSpec::DeferredEquidistant(5)});
0060 BOOST_CHECK(mixed.isDeferred());
0061 BOOST_CHECK_THROW(mixed.buildMultiAxis(), std::domain_error);
0062 }
0063
0064 BOOST_AUTO_TEST_CASE(MultiAxisSpecDeferredResolution) {
0065 using enum Acts::AxisBoundaryType;
0066 using enum Acts::AxisDirection;
0067
0068 MultiAxisSpec maf(
0069 {AxisSpec::DeferredEquidistant(4, AxisRPhi),
0070 AxisSpec::DeferredVariable({0., 0.5, 1.}, std::nullopt, AxisZ)});
0071 BOOST_CHECK(maf.isDeferred());
0072 BOOST_CHECK_THROW(maf.buildMultiAxis(), std::domain_error);
0073
0074 MultiAxisSpec::Options options = {
0075 {.min = -3., .max = 3., .boundaryType = Closed, .direction = AxisRPhi},
0076 {.min = -10., .max = 10., .boundaryType = Bound, .direction = AxisZ}};
0077 auto axes = maf.buildMultiAxis(options);
0078 BOOST_CHECK_EQUAL(axes->getNAxes(), 2);
0079 BOOST_CHECK_EQUAL(axes->getAxis(0).getBoundaryType(), Closed);
0080 BOOST_CHECK_EQUAL(axes->getAxis(0).getNBins(), 4);
0081 BOOST_CHECK(axes->getAxis(0).getDirection() == AxisRPhi);
0082 BOOST_CHECK_EQUAL(axes->getAxis(1).getBoundaryType(), Bound);
0083 CHECK_CLOSE_ABS(axes->getAxis(1).getBinEdges()[1], 0., 1e-15);
0084 BOOST_CHECK(axes->getAxis(1).getDirection() == AxisZ);
0085
0086
0087 BOOST_CHECK_THROW(
0088 maf.buildMultiAxis(
0089 {{.min = -3., .max = 3., .boundaryType = Closed, .direction = AxisZ},
0090 {.min = -10.,
0091 .max = 10.,
0092 .boundaryType = Bound,
0093 .direction = AxisRPhi}}),
0094 std::invalid_argument);
0095
0096
0097 BOOST_CHECK_THROW(
0098 maf.buildMultiAxis({{.min = 0., .max = 1., .boundaryType = Bound}}),
0099 std::invalid_argument);
0100 }
0101
0102 BOOST_AUTO_TEST_CASE(MultiAxisSpecXDApi) {
0103 using enum Acts::AxisBoundaryType;
0104
0105 Acts::MultiAxisSpec1D maf1D({AxisSpec::DeferredEquidistant(8)});
0106 BOOST_CHECK_EQUAL(maf1D.size(), 1);
0107 std::unique_ptr<Acts::IMultiAxis1D> ma1D = maf1D.buildMultiAxis(
0108 {AxisSpec::Options{.min = 0., .max = 4., .boundaryType = Bound}});
0109 BOOST_CHECK_EQUAL(ma1D->getNAxes(), 1);
0110 BOOST_CHECK_EQUAL(ma1D->getAxis(0).getNBins(), 8);
0111
0112 Acts::MultiAxisSpec2D maf2D({AxisSpec::Equidistant(2, 0., 1., Bound),
0113 AxisSpec::Equidistant(3, 0., 1., Bound)});
0114 std::unique_ptr<Acts::IMultiAxis2D> ma2D = maf2D.buildMultiAxis();
0115 BOOST_CHECK_EQUAL(ma2D->getNAxes(), 2);
0116 BOOST_CHECK_EQUAL(ma2D->getNTotalBins(), 6);
0117
0118
0119 const MultiAxisSpec& base = maf2D;
0120 BOOST_CHECK_EQUAL(base.buildMultiAxis()->getNAxes(), 2);
0121 }
0122
0123 BOOST_AUTO_TEST_CASE(MultiAxisSpecEqualityAndStreams) {
0124 using enum Acts::AxisBoundaryType;
0125
0126 MultiAxisSpec a(
0127 {AxisSpec::DeferredEquidistant(4), AxisSpec::DeferredEquidistant(5)});
0128 MultiAxisSpec b(
0129 {AxisSpec::DeferredEquidistant(4), AxisSpec::DeferredEquidistant(5)});
0130 MultiAxisSpec c({AxisSpec::DeferredEquidistant(4)});
0131 BOOST_CHECK(a == b);
0132 BOOST_CHECK(a != c);
0133
0134 BOOST_CHECK_EQUAL(
0135 c.toString(),
0136 "MultiAxisSpec: 1 axes [AxisSpec: 4 bins, equidistant within "
0137 "deferred range, deferred boundary type]");
0138 }
0139
0140 BOOST_AUTO_TEST_CASE(ResolveMultiAxisAgainstSurface) {
0141 using enum AxisDirection;
0142 using enum AxisBoundaryType;
0143
0144 auto cylinder = Surface::makeShared<CylinderSurface>(
0145 Transform3::Identity(), std::make_shared<CylinderBounds>(30., 100.));
0146
0147
0148 MultiAxisSpec2D positional(
0149 {AxisSpec::DeferredEquidistant(10), AxisSpec::DeferredEquidistant(20)});
0150 auto axes = resolveMultiAxis(positional, *cylinder);
0151 BOOST_CHECK_EQUAL(axes->getNAxes(), 2);
0152 BOOST_CHECK(axes->getAxis(0).getDirection() == AxisRPhi);
0153 BOOST_CHECK_EQUAL(axes->getAxis(0).getBoundaryType(), Closed);
0154 BOOST_CHECK_EQUAL(axes->getAxis(0).getNBins(), 10);
0155 BOOST_CHECK(axes->getAxis(1).getDirection() == AxisZ);
0156 BOOST_CHECK_EQUAL(axes->getAxis(1).getBoundaryType(), Bound);
0157 CHECK_CLOSE_ABS(axes->getAxis(1).getMin(), -100., 1e-12);
0158
0159
0160 MultiAxisSpec2D directed({AxisSpec::DeferredEquidistant(10, AxisRPhi),
0161 AxisSpec::DeferredEquidistant(20, AxisZ)});
0162 auto directedAxes = resolveMultiAxis(directed, *cylinder);
0163 BOOST_CHECK_EQUAL(directedAxes->getAxis(0).getNBins(), 10);
0164 BOOST_CHECK_EQUAL(directedAxes->getAxis(1).getNBins(), 20);
0165
0166
0167 MultiAxisSpec2D swapped({AxisSpec::DeferredEquidistant(20, AxisZ),
0168 AxisSpec::DeferredEquidistant(10, AxisRPhi)});
0169 auto reordered = resolveMultiAxis(swapped, *cylinder);
0170 BOOST_CHECK(reordered->getAxis(0).getDirection() == AxisRPhi);
0171 BOOST_CHECK_EQUAL(reordered->getAxis(0).getNBins(), 10);
0172 BOOST_CHECK(reordered->getAxis(1).getDirection() == AxisZ);
0173 BOOST_CHECK_EQUAL(reordered->getAxis(1).getNBins(), 20);
0174
0175
0176
0177 MultiAxisSpec2D oneD({AxisSpec::DeferredEquidistant(1, AxisRPhi),
0178 AxisSpec::DeferredEquidistant(8, AxisZ)});
0179 auto oneDAxes = resolveMultiAxis(oneD, *cylinder);
0180 BOOST_CHECK_EQUAL(oneDAxes->getNAxes(), 2);
0181 BOOST_CHECK_EQUAL(oneDAxes->getAxis(0).getNBins(), 1);
0182 BOOST_CHECK_EQUAL(oneDAxes->getAxis(1).getNBins(), 8);
0183
0184
0185 MultiAxisSpec2D variable(
0186 {AxisSpec::DeferredEquidistant(1, AxisRPhi),
0187 AxisSpec::DeferredVariable({0., 0.25, 1.}, std::nullopt, AxisZ)});
0188 auto variableAxes = resolveMultiAxis(variable, *cylinder);
0189 auto edges = variableAxes->getAxis(1).getBinEdges();
0190 CHECK_CLOSE_ABS(edges[0], -100., 1e-12);
0191 CHECK_CLOSE_ABS(edges[1], -50., 1e-12);
0192 CHECK_CLOSE_ABS(edges[2], 100., 1e-12);
0193
0194
0195 MultiAxisSpec2D wrongDirs({AxisSpec::DeferredEquidistant(10, AxisR),
0196 AxisSpec::DeferredEquidistant(20, AxisPhi)});
0197 BOOST_CHECK_THROW(resolveMultiAxis(wrongDirs, *cylinder),
0198 std::invalid_argument);
0199
0200
0201 MultiAxisSpec2D duplicate({AxisSpec::DeferredEquidistant(10, AxisZ),
0202 AxisSpec::DeferredEquidistant(20, AxisZ)});
0203 BOOST_CHECK_THROW(resolveMultiAxis(duplicate, *cylinder),
0204 std::invalid_argument);
0205
0206
0207 MultiAxisSpec2D mixed({AxisSpec::DeferredEquidistant(10, AxisRPhi),
0208 AxisSpec::DeferredEquidistant(20)});
0209 BOOST_CHECK_THROW(resolveMultiAxis(mixed, *cylinder), std::invalid_argument);
0210
0211
0212 auto straw =
0213 Surface::makeShared<StrawSurface>(Transform3::Identity(), 5., 100.);
0214 MultiAxisSpec2D strawBinning(
0215 {AxisSpec::DeferredEquidistant(4), AxisSpec::DeferredEquidistant(4)});
0216 BOOST_CHECK_THROW(resolveMultiAxis(strawBinning, *straw),
0217 std::invalid_argument);
0218
0219
0220 MultiAxisSpec2D full({AxisSpec::Equidistant(10, 0., 1., Bound),
0221 AxisSpec::DeferredEquidistant(20)});
0222 BOOST_CHECK_THROW(resolveMultiAxis(full, *cylinder), std::invalid_argument);
0223 }
0224
0225 BOOST_AUTO_TEST_CASE(ResolveMultiAxisSurfaceRanges) {
0226 using enum AxisBoundaryType;
0227 using enum AxisDirection;
0228
0229
0230 MultiAxisSpec2D binning(
0231 {AxisSpec::DeferredEquidistant(1), AxisSpec::DeferredEquidistant(1)});
0232
0233
0234 auto disc = Surface::makeShared<DiscSurface>(
0235 Transform3::Identity(), std::make_shared<RadialBounds>(50., 75.));
0236 std::array<AxisDirection, 2> expectedDisc = {AxisR, AxisPhi};
0237 BOOST_CHECK(static_cast<const Surface&>(*disc).localAxes() == expectedDisc);
0238 auto discAxes = resolveMultiAxis(binning, *disc);
0239 CHECK_CLOSE_ABS(discAxes->getAxis(0).getMin(), 50., 1e-12);
0240 CHECK_CLOSE_ABS(discAxes->getAxis(0).getMax(), 75., 1e-12);
0241 BOOST_CHECK_EQUAL(discAxes->getAxis(0).getBoundaryType(), Bound);
0242 BOOST_CHECK_EQUAL(discAxes->getAxis(1).getBoundaryType(), Closed);
0243
0244
0245 auto rectangle = Surface::makeShared<PlaneSurface>(
0246 Transform3::Identity(), std::make_shared<RectangleBounds>(20., 30.));
0247 std::array<AxisDirection, 2> expectedPlane = {AxisX, AxisY};
0248 BOOST_CHECK(static_cast<const Surface&>(*rectangle).localAxes() ==
0249 expectedPlane);
0250 auto rectangleAxes = resolveMultiAxis(binning, *rectangle);
0251 CHECK_CLOSE_ABS(rectangleAxes->getAxis(0).getMin(), -20., 1e-12);
0252 CHECK_CLOSE_ABS(rectangleAxes->getAxis(0).getMax(), 20., 1e-12);
0253 BOOST_CHECK_EQUAL(rectangleAxes->getAxis(0).getBoundaryType(), Bound);
0254
0255
0256 auto trapezoid = Surface::makeShared<PlaneSurface>(
0257 Transform3::Identity(), std::make_shared<TrapezoidBounds>(5., 15., 30.));
0258 auto trapezoidAxes = resolveMultiAxis(binning, *trapezoid);
0259 CHECK_CLOSE_ABS(trapezoidAxes->getAxis(0).getMin(), -15., 1e-12);
0260 CHECK_CLOSE_ABS(trapezoidAxes->getAxis(0).getMax(), 15., 1e-12);
0261 CHECK_CLOSE_ABS(trapezoidAxes->getAxis(1).getMax(), 30., 1e-12);
0262
0263
0264 auto sector = Surface::makeShared<CylinderSurface>(
0265 Transform3::Identity(),
0266 std::make_shared<CylinderBounds>(25., 50., std::numbers::pi / 4.));
0267 auto sectorAxes = resolveMultiAxis(binning, *sector);
0268 CHECK_CLOSE_ABS(sectorAxes->getAxis(0).getMin(), -25. * std::numbers::pi / 4.,
0269 1e-12);
0270 BOOST_CHECK_EQUAL(sectorAxes->getAxis(0).getBoundaryType(), Bound);
0271 }
0272
0273 BOOST_AUTO_TEST_SUITE_END()
0274
0275 }