Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-19 08:30:08

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 <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Material/GridSurfaceMaterial.hpp"
0012 #include "Acts/Material/Material.hpp"
0013 #include "Acts/Material/MaterialSlab.hpp"
0014 #include "Acts/Surfaces/CylinderBounds.hpp"
0015 #include "Acts/Surfaces/CylinderSurface.hpp"
0016 #include "Acts/Utilities/AxisDefinitions.hpp"
0017 #include "Acts/Utilities/AxisSpec.hpp"
0018 #include "Acts/Utilities/IAxis.hpp"
0019 #include "Acts/Utilities/MultiAxisSpec.hpp"
0020 
0021 #include <numbers>
0022 #include <vector>
0023 
0024 using namespace Acts;
0025 
0026 namespace ActsTests {
0027 
0028 BOOST_AUTO_TEST_SUITE(MaterialSuite)
0029 
0030 // This test covers the grid material with direct storage, built via the
0031 // factory from two axes.
0032 BOOST_AUTO_TEST_CASE(GridMaterialDirectFromAxes) {
0033   std::vector<std::vector<MaterialSlab>> material2x3;
0034   // This is a material matrix 2 bins in x and 3 bins in y
0035   std::vector<MaterialSlab> materialRow0;
0036   materialRow0.emplace_back(Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0),
0037                             1.0);
0038   materialRow0.emplace_back(
0039       Material::fromMolarDensity(11.0, 12.0, 13.0, 14.0, 15.0), 2.0);
0040   materialRow0.emplace_back(
0041       Material::fromMolarDensity(21.0, 22.0, 23.0, 24.0, 25.0), 3.0);
0042   std::vector<MaterialSlab> materialRow1;
0043   materialRow1.emplace_back(Material::fromMolarDensity(2.0, 2.0, 3.0, 4.0, 5.0),
0044                             1.0);
0045   materialRow1.emplace_back(
0046       Material::fromMolarDensity(12.0, 12.0, 13.0, 14.0, 15.0), 2.0);
0047   materialRow1.emplace_back(
0048       Material::fromMolarDensity(22.0, 22.0, 23.0, 24.0, 25.0), 3.0);
0049   // This gives a row major matrix
0050   material2x3.push_back(std::move(materialRow0));
0051   material2x3.push_back(std::move(materialRow1));
0052 
0053   auto axisX = IAxis::createEquidistant(AxisBoundaryType::Bound, -1.0, 1.0, 2);
0054   auto axisY = IAxis::createEquidistant(AxisBoundaryType::Bound, -1.5, 1.5, 3);
0055 
0056   auto ismXY = GridSurfaceMaterial::createDirect(*axisX, *axisY, material2x3);
0057 
0058   BOOST_REQUIRE(ismXY != nullptr);
0059 
0060   // Local access test - local lookup is directly (loc0, loc1) onto the axes
0061   Vector2 l00(-0.5, -1.5);
0062   Vector2 l01(-0.5, 0.);
0063   Vector2 l02(-0.5, 1.5);
0064   Vector2 l10(0.5, -1.5);
0065   Vector2 l11(0.5, 0.);
0066   Vector2 l12(0.5, 1.5);
0067 
0068   BOOST_CHECK_EQUAL(ismXY->materialSlab(l00).material().X0(), 1.);
0069   BOOST_CHECK_EQUAL(ismXY->materialSlab(l01).material().X0(), 11.);
0070   BOOST_CHECK_EQUAL(ismXY->materialSlab(l02).material().X0(), 21.);
0071   BOOST_CHECK_EQUAL(ismXY->materialSlab(l10).material().X0(), 2.);
0072   BOOST_CHECK_EQUAL(ismXY->materialSlab(l11).material().X0(), 12.);
0073   BOOST_CHECK_EQUAL(ismXY->materialSlab(l12).material().X0(), 22.);
0074 
0075   // Scale it - direct storage scales every entry
0076   ismXY->scale(2.);
0077   BOOST_CHECK_EQUAL(ismXY->materialSlab(l00).thickness(), 2.);
0078   BOOST_CHECK_EQUAL(ismXY->materialSlab(l12).thickness(), 6.);
0079 }
0080 
0081 // This test covers building a grid material with direct storage by resolving
0082 // a MultiAxisSpec2D binning against a surface, following the same pattern
0083 // used for ProtoGridSurfaceMaterial (see BinnedSurfaceMaterialAccumulator).
0084 // Binning is restricted to z; loc0 (rPhi) is a single-bin dummy axis that
0085 // should be ignored regardless of its (possibly out-of-range) value.
0086 BOOST_AUTO_TEST_CASE(GridMaterialDirectFromMultiAxisSpec) {
0087   using enum AxisDirection;
0088 
0089   auto cylinder = Surface::makeShared<CylinderSurface>(
0090       Transform3::Identity(), std::make_shared<CylinderBounds>(30., 100.));
0091 
0092   MultiAxisSpec2D binning({AxisSpec::DeferredEquidistant(1, AxisRPhi),
0093                            AxisSpec::DeferredEquidistant(4, AxisZ)});
0094 
0095   std::vector<std::vector<MaterialSlab>> payload = {
0096       {MaterialSlab(Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0), 1.0),
0097        MaterialSlab(Material::fromMolarDensity(11.0, 12.0, 13.0, 14.0, 15.0),
0098                     2.0),
0099        MaterialSlab(Material::fromMolarDensity(21.0, 22.0, 23.0, 24.0, 25.0),
0100                     3.0),
0101        MaterialSlab(Material::fromMolarDensity(31.0, 32.0, 33.0, 34.0, 35.0),
0102                     4.0)}};
0103 
0104   auto ism = GridSurfaceMaterial::createDirect(binning, *cylinder, payload);
0105   BOOST_REQUIRE(ism != nullptr);
0106 
0107   // loc0 (rPhi) is irrelevant - near, far and out-of-range values all land
0108   // on the same single dummy bin
0109   Vector2 lLow(-90., -75.);
0110   Vector2 lMid(1000., -75.);
0111   Vector2 lHigh(90., -75.);
0112   BOOST_CHECK_EQUAL(ism->materialSlab(lLow).material().X0(), 1.);
0113   BOOST_CHECK_EQUAL(ism->materialSlab(lMid).material().X0(), 1.);
0114   BOOST_CHECK_EQUAL(ism->materialSlab(lHigh).material().X0(), 1.);
0115 
0116   // loc1 (z) still resolves the real binning
0117   Vector2 lZ2(0., 25.);
0118   BOOST_CHECK_EQUAL(ism->materialSlab(lZ2).material().X0(), 21.);
0119 }
0120 
0121 // This test covers localAxisDirections(): it reflects whatever direction
0122 // information the binning's axes carry - empty when built from plain,
0123 // undirected axes, and the canonical (surface) directions when built by
0124 // resolving a MultiAxisSpec2D against a surface, since resolveMultiAxis
0125 // always tags the resolved axes with the canonical direction (see
0126 // Surface::assignSurfaceMaterial, which uses this to detect whether the
0127 // grid's axis order needs swapping).
0128 BOOST_AUTO_TEST_CASE(GridMaterialLocalAxisDirections) {
0129   using enum AxisDirection;
0130 
0131   auto axisX = IAxis::createEquidistant(AxisBoundaryType::Bound, -1.0, 1.0, 2);
0132   auto axisY = IAxis::createEquidistant(AxisBoundaryType::Bound, -1.5, 1.5, 3);
0133   std::vector<std::vector<MaterialSlab>> payload2x3 = {
0134       {MaterialSlab::Nothing(), MaterialSlab::Nothing(),
0135        MaterialSlab::Nothing()},
0136       {MaterialSlab::Nothing(), MaterialSlab::Nothing(),
0137        MaterialSlab::Nothing()}};
0138   auto ismUndirected =
0139       GridSurfaceMaterial::createDirect(*axisX, *axisY, payload2x3);
0140   BOOST_CHECK(ismUndirected->localAxisDirections().empty());
0141 
0142   auto cylinder = Surface::makeShared<CylinderSurface>(
0143       Transform3::Identity(), std::make_shared<CylinderBounds>(30., 100.));
0144 
0145   MultiAxisSpec2D binning({AxisSpec::DeferredEquidistant(1, AxisRPhi),
0146                            AxisSpec::DeferredEquidistant(4, AxisZ)});
0147   std::vector<std::vector<MaterialSlab>> payload1x4 = {
0148       {MaterialSlab::Nothing(), MaterialSlab::Nothing(),
0149        MaterialSlab::Nothing(), MaterialSlab::Nothing()}};
0150   auto ismDirected =
0151       GridSurfaceMaterial::createDirect(binning, *cylinder, payload1x4);
0152 
0153   std::vector<AxisDirection> localDirs = ismDirected->localAxisDirections();
0154   BOOST_REQUIRE_EQUAL(localDirs.size(), 2u);
0155   BOOST_CHECK_EQUAL(localDirs[0], AxisRPhi);
0156   BOOST_CHECK_EQUAL(localDirs[1], AxisZ);
0157 }
0158 
0159 // This test covers the locally indexed grid material in 2D: one instance
0160 // built via the factory from two axes, one built directly from a
0161 // MultiAxisSpec2D and a hand-filled Indexed storage.
0162 BOOST_AUTO_TEST_CASE(GridIndexedMaterial2D) {
0163   std::vector<MaterialSlab> material;
0164   material.emplace_back(Material::Vacuum(), 1.0);  // vacuum
0165   material.emplace_back(Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0),
0166                         1.0);
0167   material.emplace_back(
0168       Material::fromMolarDensity(11.0, 12.0, 13.0, 14.0, 15.0), 1.0);
0169   material.emplace_back(
0170       Material::fromMolarDensity(21.0, 22.0, 23.0, 24.0, 25.0), 1.0);
0171 
0172   // 2 bins in z, 4 bins in phi
0173   auto axisZ = IAxis::createEquidistant(AxisBoundaryType::Bound, -1.0, 1.0, 2);
0174   auto axisPhi = IAxis::createEquidistant(
0175       AxisBoundaryType::Closed, -std::numbers::pi, std::numbers::pi, 4);
0176 
0177   std::vector<std::vector<std::size_t>> indexPayload = {
0178       std::vector<std::size_t>{1u, 1u, 0u, 2u},
0179       std::vector<std::size_t>{0u, 3u, 3u, 0u}};
0180 
0181   // Test (1): built via the factory from two axes
0182   auto ismFactory = GridSurfaceMaterial::createIndexed(*axisZ, *axisPhi,
0183                                                        material, indexPayload);
0184 
0185   // Test (2): built directly from a MultiAxisSpec2D and a hand-filled
0186   // Indexed storage, using the same binning
0187   MultiAxisSpec2D binning(
0188       {AxisSpec::Equidistant(2, -1.0, 1.0, AxisBoundaryType::Bound),
0189        AxisSpec::Equidistant(4, -std::numbers::pi, std::numbers::pi,
0190                              AxisBoundaryType::Closed)});
0191   auto multiAxis = binning.buildMultiAxis();
0192 
0193   GridSurfaceMaterial::Indexed storage;
0194   storage.material = material;
0195   storage.indices.resize(multiAxis->getNTotalBins(true), 0u);
0196   for (std::size_t i0 = 0; i0 < indexPayload.size(); ++i0) {
0197     for (std::size_t i1 = 0; i1 < indexPayload[i0].size(); ++i1) {
0198       IMultiAxis2D::LocalBins lbin{i0 + 1, i1 + 1};
0199       storage.indices[multiAxis->getGlobalBinFromLocalBins(lbin)] =
0200           indexPayload[i0][i1];
0201     }
0202   }
0203   GridSurfaceMaterial ism(std::move(binning), std::move(storage));
0204 
0205   // Local access test, both should give material 1
0206   Vector2 l0(-0.5, -std::numbers::pi * 0.75);
0207   BOOST_CHECK_EQUAL(ism.materialSlab(l0).material().X0(), 1.);
0208   BOOST_CHECK_EQUAL(ismFactory->materialSlab(l0).material().X0(), 1.);
0209 
0210   Vector2 l1(0.5, std::numbers::pi / 4.);
0211   BOOST_CHECK_EQUAL(ism.materialSlab(l1).material().X0(), 21.);
0212   BOOST_CHECK_EQUAL(ismFactory->materialSlab(l1).material().X0(), 21.);
0213 
0214   Vector2 l2(-0.5, std::numbers::pi / 4.);
0215   BOOST_CHECK(ism.materialSlab(l2).material().isVacuum());
0216   BOOST_CHECK(ismFactory->materialSlab(l2).material().isVacuum());
0217 
0218   // Indexed storage scales the locally owned material vector once
0219   ism.scale(2.);
0220   BOOST_CHECK_EQUAL(ism.materialSlab(l0).thickness(), 2.);
0221 }
0222 
0223 // This test covers the globally indexed grid material with non-shared
0224 // material. The grid is always 2D; the second axis is a single-bin dummy
0225 // since this test only ever binned in one direction.
0226 BOOST_AUTO_TEST_CASE(GridGloballyIndexedMaterialNonShared) {
0227   auto material = std::make_shared<std::vector<MaterialSlab>>();
0228 
0229   material->emplace_back(Material::Vacuum(), 0.0);  // vacuum
0230   material->emplace_back(Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0),
0231                          1.0);
0232   material->emplace_back(
0233       Material::fromMolarDensity(11.0, 12.0, 13.0, 14.0, 15.0), 2.0);
0234   material->emplace_back(
0235       Material::fromMolarDensity(21.0, 22.0, 23.0, 24.0, 25.0), 3.0);
0236   material->emplace_back(
0237       Material::fromMolarDensity(31.0, 22.0, 23.0, 24.0, 25.0), 4.0);
0238 
0239   auto axis0 = IAxis::createEquidistant(AxisBoundaryType::Bound, 0., 5., 5);
0240   auto axis1 = IAxis::createEquidistant(AxisBoundaryType::Bound, -1., 1., 1);
0241 
0242   std::vector<std::vector<std::size_t>> indexPayload = {
0243       {1u}, {0u}, {2u}, {2u}, {3u}};
0244 
0245   auto ism = GridSurfaceMaterial::createGloballyIndexed(*axis0, *axis1,
0246                                                         material, indexPayload);
0247 
0248   // Local access test
0249   Vector2 l0(0.5, 0.);
0250   Vector2 l1(1.5, 0.);
0251   Vector2 l2(2.5, 0.);
0252   Vector2 l3(3.5, 0.);
0253   Vector2 l4(4.5, 0.);
0254 
0255   BOOST_CHECK_EQUAL(ism->materialSlab(l0).material().X0(), 1.);
0256   BOOST_CHECK(ism->materialSlab(l1).material().isVacuum());
0257   BOOST_CHECK_EQUAL(ism->materialSlab(l2).material().X0(), 11.);
0258   BOOST_CHECK_EQUAL(ism->materialSlab(l3).material().X0(), 11.);
0259   BOOST_CHECK_EQUAL(ism->materialSlab(l4).material().X0(), 21.);
0260 
0261   auto axis0Single =
0262       IAxis::createEquidistant(AxisBoundaryType::Bound, 0., 5., 1);
0263   std::vector<std::vector<std::size_t>> indexPayload1 = {{4u}};
0264 
0265   auto ism1 = GridSurfaceMaterial::createGloballyIndexed(
0266       *axis0Single, *axis1, material, indexPayload1);
0267 
0268   Vector2 l0g1(2.5, 0.);
0269   BOOST_CHECK_EQUAL(ism1->materialSlab(l0g1).material().X0(), 31.);
0270 
0271   // Scale
0272   ism1->scale(2.);
0273   BOOST_CHECK_EQUAL(ism1->materialSlab(l0g1).thickness(), 8.);
0274 
0275   // First one stays unscaled
0276   BOOST_CHECK_EQUAL(ism->materialSlab(l0).thickness(), 1.);
0277 }
0278 
0279 // This test covers the globally indexed grid material with an index shared
0280 // between two grids: scaling one grid scales the shared entry for both,
0281 // since GloballyIndexed carries no ownership/exclusivity guard.
0282 BOOST_AUTO_TEST_CASE(GridGloballyIndexedMaterialShared) {
0283   auto material = std::make_shared<std::vector<MaterialSlab>>();
0284 
0285   material->emplace_back(Material::Vacuum(), 0.0);  // vacuum
0286   material->emplace_back(Material::fromMolarDensity(1.0, 2.0, 3.0, 4.0, 5.0),
0287                          1.0);
0288 
0289   auto axis0 = IAxis::createEquidistant(AxisBoundaryType::Bound, 0., 5., 1);
0290   auto axis1 = IAxis::createEquidistant(AxisBoundaryType::Bound, -1., 1., 1);
0291 
0292   std::vector<std::vector<std::size_t>> indexPayload = {{1u}};
0293 
0294   auto ism0 = GridSurfaceMaterial::createGloballyIndexed(
0295       *axis0, *axis1, material, indexPayload);
0296   auto ism1 = GridSurfaceMaterial::createGloballyIndexed(
0297       *axis0, *axis1, material, indexPayload);
0298 
0299   Vector2 l0(2.5, 0.);
0300 
0301   // check grid material 0
0302   BOOST_CHECK_EQUAL(ism0->materialSlab(l0).material().X0(), 1.);
0303   BOOST_CHECK_EQUAL(ism1->materialSlab(l0).material().X0(), 1.);
0304 
0305   // scaling through ism1 also scales the shared entry as seen through ism0
0306   ism1->scale(2.);
0307   BOOST_CHECK_EQUAL(ism0->materialSlab(l0).thickness(), 2.);
0308   BOOST_CHECK_EQUAL(ism1->materialSlab(l0).thickness(), 2.);
0309 }
0310 
0311 // This test covers the grid material with direct storage and scaling
0312 BOOST_AUTO_TEST_CASE(GridSurfaceMaterialDirectStorageScale) {
0313   auto axis0 = IAxis::createEquidistant(AxisBoundaryType::Bound, 0., 5., 5);
0314   auto axis1 = IAxis::createEquidistant(AxisBoundaryType::Bound, -1., 1., 1);
0315 
0316   std::vector<std::vector<MaterialSlab>> payload = {
0317       {MaterialSlab::Vacuum(0.0)},
0318       {MaterialSlab::Vacuum(1.0)},
0319       {MaterialSlab::Vacuum(2.0)},
0320       {MaterialSlab::Vacuum(3.0)},
0321       {MaterialSlab::Vacuum(4.0)}};
0322 
0323   auto gsm = GridSurfaceMaterial::createDirect(*axis0, *axis1, payload);
0324 
0325   // Local access test
0326   Vector2 l0(0.5, 0.);
0327   Vector2 l1(1.5, 0.);
0328   Vector2 l2(2.5, 0.);
0329   Vector2 l3(3.5, 0.);
0330   Vector2 l4(4.5, 0.);
0331 
0332   BOOST_CHECK_EQUAL(gsm->materialSlab(l0).thickness(), 0.);
0333   BOOST_CHECK_EQUAL(gsm->materialSlab(l1).thickness(), 1.);
0334   BOOST_CHECK_EQUAL(gsm->materialSlab(l2).thickness(), 2.);
0335   BOOST_CHECK_EQUAL(gsm->materialSlab(l3).thickness(), 3.);
0336   BOOST_CHECK_EQUAL(gsm->materialSlab(l4).thickness(), 4.);
0337 
0338   // Now scale it - and access again
0339   gsm->scale(2.);
0340 
0341   BOOST_CHECK_EQUAL(gsm->materialSlab(l0).thickness(), 0.);
0342   BOOST_CHECK_EQUAL(gsm->materialSlab(l1).thickness(), 2.);
0343   BOOST_CHECK_EQUAL(gsm->materialSlab(l2).thickness(), 4.);
0344   BOOST_CHECK_EQUAL(gsm->materialSlab(l3).thickness(), 6.);
0345   BOOST_CHECK_EQUAL(gsm->materialSlab(l4).thickness(), 8.);
0346 }
0347 
0348 // This test covers the storage-size validation in the GridSurfaceMaterial
0349 // constructor: the storage must have exactly one entry (material slab or
0350 // index) per bin implied by the binning, including under-/overflow bins.
0351 BOOST_AUTO_TEST_CASE(GridSurfaceMaterialConstructionValidation) {
0352   MultiAxisSpec2D binning(
0353       {AxisSpec::Equidistant(2, -1.0, 1.0, AxisBoundaryType::Bound),
0354        AxisSpec::Equidistant(1, -1., 1., AxisBoundaryType::Bound)});
0355   auto multiAxis = binning.buildMultiAxis();
0356   std::size_t nBins = multiAxis->getNTotalBins(true);
0357 
0358   // Correctly sized direct storage succeeds
0359   GridSurfaceMaterial::Direct direct(nBins);
0360   BOOST_CHECK_NO_THROW(GridSurfaceMaterial(
0361       MultiAxisSpec2D(binning), GridSurfaceMaterial::Direct(direct)));
0362 
0363   // Undersized direct storage throws
0364   GridSurfaceMaterial::Direct tooSmall(nBins - 1);
0365   BOOST_CHECK_THROW(
0366       GridSurfaceMaterial(MultiAxisSpec2D(binning), std::move(tooSmall)),
0367       std::invalid_argument);
0368 
0369   // Undersized indexed storage throws
0370   GridSurfaceMaterial::Indexed indexedTooSmall;
0371   indexedTooSmall.indices.resize(nBins - 1, 0u);
0372   indexedTooSmall.material = {MaterialSlab::Nothing()};
0373   BOOST_CHECK_THROW(
0374       GridSurfaceMaterial(MultiAxisSpec2D(binning), std::move(indexedTooSmall)),
0375       std::invalid_argument);
0376 }
0377 
0378 BOOST_AUTO_TEST_SUITE_END()
0379 
0380 }  // namespace ActsTests