File indexing completed on 2026-07-16 08:09:04
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/Geometry/CuboidVolumeBounds.hpp"
0013 #include "Acts/Geometry/CylinderVolumeBounds.hpp"
0014 #include "Acts/Geometry/GeometryIdentifier.hpp"
0015 #include "Acts/Geometry/Portal.hpp"
0016 #include "Acts/Geometry/PortalLinkBase.hpp"
0017 #include "Acts/Geometry/TrackingGeometry.hpp"
0018 #include "Acts/Geometry/TrackingVolume.hpp"
0019 #include "Acts/Geometry/TrapezoidVolumeBounds.hpp"
0020 #include "Acts/Geometry/TrivialPortalLink.hpp"
0021 #include "Acts/Geometry/VolumeBounds.hpp"
0022 #include "Acts/Surfaces/AnnulusBounds.hpp"
0023 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0024 #include "Acts/Surfaces/CylinderBounds.hpp"
0025 #include "Acts/Surfaces/PlaneSurface.hpp"
0026 #include "Acts/Surfaces/RadialBounds.hpp"
0027 #include "Acts/Surfaces/RectangleBounds.hpp"
0028 #include "Acts/Surfaces/SurfacePlacementBase.hpp"
0029 #include "Acts/Surfaces/TrapezoidBounds.hpp"
0030 #include "Acts/Utilities/Logger.hpp"
0031 #include "Acts/Visualization/ObjVisualization3D.hpp"
0032 #include "ActsPlugins/Detray/DetrayConversionUtils.hpp"
0033 #include "ActsPlugins/Detray/DetrayPayloadConverter.hpp"
0034 #include "ActsTests/CommonHelpers/CylindricalTrackingGeometry.hpp"
0035 #include "ActsTests/CommonHelpers/DetectorElementStub.hpp"
0036 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0037
0038 #include <memory>
0039 #include <numbers>
0040
0041 #include <detray/io/backend/geometry_reader.hpp>
0042 #include <detray/io/backend/geometry_writer.hpp>
0043 #include <detray/io/backend/homogeneous_material_reader.hpp>
0044 #include <detray/io/backend/homogeneous_material_writer.hpp>
0045 #include <detray/io/backend/material_map_reader.hpp>
0046 #include <detray/io/backend/surface_grid_reader.hpp>
0047 #include <detray/io/frontend/definitions.hpp>
0048 #include <detray/io/frontend/detector_writer.hpp>
0049 #include <detray/io/frontend/detector_writer_config.hpp>
0050 #include <detray/io/frontend/payloads.hpp>
0051 #include <detray/io/json/json_io.hpp>
0052 #include <detray/plugins/svgtools/illustrator.hpp>
0053 #include <detray/plugins/svgtools/writer.hpp>
0054 #include <detray/utils/consistency_checker.hpp>
0055 #include <detray/utils/grid/detail/concepts.hpp>
0056 #include <nlohmann/json_fwd.hpp>
0057 #include <vecmem/memory/host_memory_resource.hpp>
0058 #include <vecmem/memory/memory_resource.hpp>
0059
0060 #include "detray/geometry/tracking_volume.hpp"
0061
0062 auto logger = Acts::getDefaultLogger("Test", Acts::Logging::INFO);
0063
0064 using namespace Acts;
0065 using namespace ActsPlugins;
0066
0067 namespace ActsTests {
0068
0069 BOOST_AUTO_TEST_SUITE(DetrayConversion)
0070
0071 BOOST_AUTO_TEST_CASE(DetrayTransformConversion) {
0072 auto transform = Transform3::Identity();
0073 transform.pretranslate(Vector3(1., 2., 3.));
0074 transform.rotate(Eigen::AngleAxisd(std::numbers::pi / 2., Vector3::UnitZ()));
0075
0076 detray::io::transform_payload payload =
0077 DetrayConversionUtils::convertTransform(transform);
0078
0079 CHECK_CLOSE_ABS(payload.tr[0u], 1., std::numeric_limits<double>::epsilon());
0080 CHECK_CLOSE_ABS(payload.tr[1u], 2., std::numeric_limits<double>::epsilon());
0081 CHECK_CLOSE_ABS(payload.tr[2u], 3., std::numeric_limits<double>::epsilon());
0082
0083 RotationMatrix3 rotation = transform.rotation().transpose();
0084 CHECK_CLOSE_ABS(payload.rot[0u], rotation(0, 0),
0085 std::numeric_limits<double>::epsilon());
0086 CHECK_CLOSE_ABS(payload.rot[1u], rotation(0, 1),
0087 std::numeric_limits<double>::epsilon());
0088 CHECK_CLOSE_ABS(payload.rot[2u], rotation(0, 2),
0089 std::numeric_limits<double>::epsilon());
0090 CHECK_CLOSE_ABS(payload.rot[3u], rotation(1, 0),
0091 std::numeric_limits<double>::epsilon());
0092 CHECK_CLOSE_ABS(payload.rot[4u], rotation(1, 1),
0093 std::numeric_limits<double>::epsilon());
0094 CHECK_CLOSE_ABS(payload.rot[5u], rotation(1, 2),
0095 std::numeric_limits<double>::epsilon());
0096 CHECK_CLOSE_ABS(payload.rot[6u], rotation(2, 0),
0097 std::numeric_limits<double>::epsilon());
0098 CHECK_CLOSE_ABS(payload.rot[7u], rotation(2, 1),
0099 std::numeric_limits<double>::epsilon());
0100 CHECK_CLOSE_ABS(payload.rot[8u], rotation(2, 2),
0101 std::numeric_limits<double>::epsilon());
0102 }
0103
0104 BOOST_AUTO_TEST_CASE(DetrayMaskConversion) {
0105
0106 {
0107 Acts::AnnulusBounds annulus(10., 20., 0.5, 1.5, {1., 2.}, 1.);
0108 auto payload = DetrayPayloadConverter::convertMask(annulus, false);
0109 BOOST_CHECK(payload.shape == detray::io::shape_id::annulus2);
0110 using enum detray::annulus2D::boundaries;
0111 CHECK_CLOSE_ABS(payload.boundaries[e_min_r], 10., 1e-10);
0112 CHECK_CLOSE_ABS(payload.boundaries[e_max_r], 20., 1e-10);
0113 CHECK_CLOSE_ABS(payload.boundaries[e_min_phi_rel], 0.5,
0114 1e-10);
0115 CHECK_CLOSE_ABS(payload.boundaries[e_max_phi_rel], 1.5,
0116 1e-10);
0117 CHECK_CLOSE_ABS(payload.boundaries[e_average_phi], 1.,
0118 1e-10);
0119 CHECK_CLOSE_ABS(payload.boundaries[e_shift_x], 1., 1e-10);
0120 CHECK_CLOSE_ABS(payload.boundaries[e_shift_y], 2., 1e-10);
0121 }
0122
0123
0124 {
0125 Acts::RectangleBounds rectangle(5., 10.);
0126 auto payload = DetrayPayloadConverter::convertMask(rectangle, false);
0127 BOOST_CHECK(payload.shape == detray::io::shape_id::rectangle2);
0128 using enum detray::rectangle2D::boundaries;
0129 CHECK_CLOSE_ABS(payload.boundaries[e_half_x], 5., 1e-10);
0130 CHECK_CLOSE_ABS(payload.boundaries[e_half_y], 10., 1e-10);
0131 }
0132
0133
0134 {
0135 Acts::CylinderBounds cylinder(50., 100.);
0136 auto payload = DetrayPayloadConverter::convertMask(cylinder, false);
0137 BOOST_CHECK(payload.shape == detray::io::shape_id::cylinder2);
0138 using enum detray::concentric_cylinder2D::boundaries;
0139 CHECK_CLOSE_ABS(payload.boundaries[e_r], 50., 1e-10);
0140 CHECK_CLOSE_ABS(payload.boundaries[e_lower_z], -100., 1e-10);
0141 CHECK_CLOSE_ABS(payload.boundaries[e_upper_z], 100., 1e-10);
0142
0143
0144 auto portalPayload = DetrayPayloadConverter::convertMask(cylinder, true);
0145 BOOST_CHECK(portalPayload.shape == detray::io::shape_id::portal_cylinder2);
0146 CHECK_CLOSE_ABS(portalPayload.boundaries[e_r], 50., 1e-10);
0147 CHECK_CLOSE_ABS(portalPayload.boundaries[e_lower_z], -100.,
0148 1e-10);
0149 CHECK_CLOSE_ABS(portalPayload.boundaries[e_upper_z], 100.,
0150 1e-10);
0151 }
0152
0153
0154 {
0155 Acts::TrapezoidBounds trapezoid(4., 8., 10.,
0156 0.);
0157 auto payload = DetrayPayloadConverter::convertMask(trapezoid, false);
0158 BOOST_CHECK(payload.shape == detray::io::shape_id::trapezoid2);
0159 using enum detray::trapezoid2D::boundaries;
0160 CHECK_CLOSE_ABS(payload.boundaries[e_half_length_0], 4.,
0161 1e-10);
0162 CHECK_CLOSE_ABS(payload.boundaries[e_half_length_1], 8.,
0163 1e-10);
0164 CHECK_CLOSE_ABS(payload.boundaries[e_half_length_2], 10.,
0165 1e-10);
0166 CHECK_CLOSE_ABS(payload.boundaries[e_divisor], 1. / (2. * 10.),
0167 1e-10);
0168 }
0169
0170
0171 {
0172 Acts::RadialBounds radial(5.,
0173 15);
0174 auto payload = DetrayPayloadConverter::convertMask(radial, false);
0175 BOOST_CHECK(payload.shape == detray::io::shape_id::ring2);
0176 using enum detray::ring2D::boundaries;
0177 CHECK_CLOSE_ABS(payload.boundaries[e_inner_r], 5., 1e-10);
0178 CHECK_CLOSE_ABS(payload.boundaries[e_outer_r], 15., 1e-10);
0179 }
0180 }
0181
0182 BOOST_AUTO_TEST_CASE(DetrayMaskConversionErrors) {
0183
0184
0185
0186 {
0187 std::array<double, Acts::RectangleBounds::eSize> values = {
0188 -2., -3., 5., 4.};
0189 Acts::RectangleBounds asymmetricRect(values);
0190 BOOST_CHECK_THROW(
0191 DetrayPayloadConverter::convertMask(asymmetricRect, false),
0192 std::runtime_error);
0193 }
0194
0195
0196 {
0197 Acts::RadialBounds partialRadial(5., 15., 0.5 * std::numbers::pi);
0198 BOOST_CHECK_THROW(DetrayPayloadConverter::convertMask(partialRadial, false),
0199 std::runtime_error);
0200 }
0201
0202 {
0203 Acts::RadialBounds partialRadial(
0204 5., 15., std::numbers::pi,
0205 0.75 * std::numbers::pi);
0206 BOOST_CHECK_THROW(DetrayPayloadConverter::convertMask(partialRadial, false),
0207 std::runtime_error);
0208 }
0209
0210
0211 {
0212 class MockDiscBounds final : public Acts::SurfaceBounds {
0213 public:
0214 BoundsType type() const final { return BoundsType::eDisc; }
0215 bool isCartesian() const final { return true; }
0216 SquareMatrix2 boundToCartesianJacobian(
0217 const Vector2& ) const final {
0218 return SquareMatrix2::Identity();
0219 }
0220 SquareMatrix2 boundToCartesianMetric(
0221 const Vector2& ) const final {
0222 return SquareMatrix2::Identity();
0223 }
0224 std::vector<double> values() const final { return {}; }
0225 Vector2 center() const final { return Vector2::Zero(); }
0226 bool inside(const Vector2& ) const final { return false; }
0227 Vector2 closestPoint(const Vector2& lposition,
0228 const SquareMatrix2& ) const final {
0229 return lposition;
0230 }
0231 bool inside(const Acts::Vector2& ,
0232 const Acts::BoundaryTolerance& ) const final {
0233 return false;
0234 }
0235 std::ostream& toStream(std::ostream& sl) const final { return sl; }
0236 };
0237 MockDiscBounds mockDisc;
0238 BOOST_CHECK_THROW(DetrayPayloadConverter::convertMask(mockDisc, false),
0239 std::runtime_error);
0240 }
0241
0242
0243 {
0244 class MockUnknownBounds final : public Acts::SurfaceBounds {
0245 public:
0246 BoundsType type() const final {
0247 return static_cast<BoundsType>(999);
0248 }
0249 bool isCartesian() const final { return true; }
0250 SquareMatrix2 boundToCartesianJacobian(
0251 const Vector2& ) const final {
0252 return SquareMatrix2::Identity();
0253 }
0254 SquareMatrix2 boundToCartesianMetric(
0255 const Vector2& ) const final {
0256 return SquareMatrix2::Identity();
0257 }
0258 std::vector<double> values() const final { return {}; }
0259 Vector2 center() const final { return Vector2::Zero(); }
0260 bool inside(const Vector2& ) const final { return false; }
0261 Vector2 closestPoint(const Vector2& lposition,
0262 const SquareMatrix2& ) const final {
0263 return lposition;
0264 }
0265 bool inside(const Acts::Vector2& ,
0266 const Acts::BoundaryTolerance& ) const final {
0267 return false;
0268 }
0269 std::ostream& toStream(std::ostream& sl) const final { return sl; }
0270 };
0271 MockUnknownBounds mockUnknown;
0272 auto payload = DetrayPayloadConverter::convertMask(mockUnknown, false);
0273 BOOST_CHECK(payload.shape == detray::io::shape_id::unknown);
0274 }
0275 }
0276
0277 BOOST_AUTO_TEST_CASE(DetraySurfaceConversionTests) {
0278 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0279
0280
0281 Transform3 transform = Transform3::Identity();
0282 transform.pretranslate(Vector3(1., 2., 3.));
0283 transform.rotate(Eigen::AngleAxisd(0.5 * std::numbers::pi, Vector3::UnitZ()));
0284
0285
0286 auto bounds = std::make_shared<RectangleBounds>(5., 10.);
0287
0288
0289 auto surface = Surface::makeShared<PlaneSurface>(transform, bounds);
0290
0291
0292 {
0293 DetrayPayloadConverter::Config cfg;
0294 cfg.sensitiveStrategy =
0295 DetrayPayloadConverter::Config::SensitiveStrategy::Identifier;
0296
0297 DetrayPayloadConverter converter(cfg);
0298
0299
0300 {
0301 auto payload = converter.convertSurface(gctx, *surface);
0302
0303
0304 BOOST_CHECK(payload.type == detray::surface_id::e_passive);
0305
0306
0307 CHECK_CLOSE_ABS(payload.transform.tr[0], 1., 1e-10);
0308 CHECK_CLOSE_ABS(payload.transform.tr[1], 2., 1e-10);
0309 CHECK_CLOSE_ABS(payload.transform.tr[2], 3., 1e-10);
0310
0311
0312 BOOST_CHECK(payload.masks.at(0).shape ==
0313 detray::io::shape_id::rectangle2);
0314 using enum detray::rectangle2D::boundaries;
0315 CHECK_CLOSE_ABS(payload.masks.at(0).boundaries[e_half_x], 5., 1e-10);
0316 CHECK_CLOSE_ABS(payload.masks.at(0).boundaries[e_half_y], 10., 1e-10);
0317 }
0318
0319
0320 {
0321 auto sensitiveSurface =
0322 Surface::makeShared<PlaneSurface>(transform, bounds);
0323 sensitiveSurface->assignGeometryId(GeometryIdentifier().withSensitive(1));
0324
0325 auto payload = converter.convertSurface(gctx, *sensitiveSurface);
0326
0327 BOOST_CHECK(payload.type == detray::surface_id::e_sensitive);
0328 }
0329 }
0330
0331
0332 {
0333 DetrayPayloadConverter::Config cfg;
0334 cfg.sensitiveStrategy =
0335 DetrayPayloadConverter::Config::SensitiveStrategy::DetectorElement;
0336
0337 DetrayPayloadConverter converter(cfg);
0338
0339
0340 {
0341 auto payload = converter.convertSurface(gctx, *surface);
0342 BOOST_CHECK(payload.type == detray::surface_id::e_passive);
0343 }
0344
0345
0346 {
0347
0348 auto detElement =
0349 std::make_shared<DetectorElementStub>(transform, bounds, 1.0);
0350
0351
0352 auto sensitiveSurface =
0353 Surface::makeShared<PlaneSurface>(bounds, *detElement);
0354
0355 auto payload = converter.convertSurface(gctx, *sensitiveSurface);
0356 BOOST_CHECK(payload.type == detray::surface_id::e_sensitive);
0357 }
0358 }
0359 }
0360
0361 BOOST_AUTO_TEST_CASE(DetrayPortalConversionTests) {
0362 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0363
0364
0365 Transform3 transform = Transform3::Identity();
0366 transform.pretranslate(Vector3(1., 2., 3.));
0367 transform.rotate(Eigen::AngleAxisd(0.5 * std::numbers::pi, Vector3::UnitZ()));
0368
0369
0370 auto bounds = std::make_shared<RectangleBounds>(5., 10.);
0371
0372 auto cvlBounds = std::make_shared<CylinderVolumeBounds>(5., 10., 10.);
0373 TrackingVolume tv(transform, cvlBounds);
0374
0375
0376 auto surface = Surface::makeShared<PlaneSurface>(transform, bounds);
0377
0378
0379 auto portal = std::make_shared<Portal>(Direction::AlongNormal(), surface, tv);
0380
0381
0382 {
0383 DetrayPayloadConverter::Config cfg;
0384 DetrayPayloadConverter converter(cfg);
0385 auto payload = converter.convertSurface(gctx, portal->surface(), true);
0386
0387
0388 BOOST_CHECK(payload.type == detray::surface_id::e_portal);
0389
0390
0391 CHECK_CLOSE_ABS(payload.transform.tr[0], 1., 1e-10);
0392 CHECK_CLOSE_ABS(payload.transform.tr[1], 2., 1e-10);
0393 CHECK_CLOSE_ABS(payload.transform.tr[2], 3., 1e-10);
0394
0395
0396
0397 BOOST_CHECK(payload.masks.at(0).shape == detray::io::shape_id::rectangle2);
0398 using enum detray::rectangle2D::boundaries;
0399 CHECK_CLOSE_ABS(payload.masks.at(0).boundaries[e_half_x], 5., 1e-10);
0400 CHECK_CLOSE_ABS(payload.masks.at(0).boundaries[e_half_y], 10., 1e-10);
0401 }
0402 }
0403
0404 BOOST_AUTO_TEST_CASE(DetrayVolumeConversionTests) {
0405 const auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0406
0407 Transform3 transform = Transform3::Identity();
0408 transform.pretranslate(Vector3(1., 2., 3.));
0409 transform.rotate(Eigen::AngleAxisd(0.5 * std::numbers::pi, Vector3::UnitZ()));
0410
0411 DetrayPayloadConverter::Config cfg;
0412 DetrayPayloadConverter converter(cfg);
0413
0414
0415 {
0416 auto cvlBounds = std::make_shared<CylinderVolumeBounds>(5., 10., 10.);
0417 auto volume =
0418 std::make_shared<TrackingVolume>(transform, cvlBounds, "TestCylinder");
0419
0420 auto payload = converter.convertVolume(gctx, *volume);
0421
0422
0423 BOOST_CHECK(payload.type == detray::volume_id::e_cylinder);
0424
0425
0426 BOOST_CHECK_EQUAL(payload.name, "TestCylinder");
0427
0428
0429 CHECK_CLOSE_ABS(payload.transform.tr[0], 1., 1e-10);
0430 CHECK_CLOSE_ABS(payload.transform.tr[1], 2., 1e-10);
0431 CHECK_CLOSE_ABS(payload.transform.tr[2], 3., 1e-10);
0432 }
0433
0434
0435 {
0436 auto cuboidBounds = std::make_shared<CuboidVolumeBounds>(5., 10., 15.);
0437 auto volume =
0438 std::make_shared<TrackingVolume>(transform, cuboidBounds, "TestCuboid");
0439
0440 auto payload = converter.convertVolume(gctx, *volume);
0441
0442 BOOST_CHECK(payload.type == detray::volume_id::e_cuboid);
0443 BOOST_CHECK_EQUAL(payload.name, "TestCuboid");
0444 }
0445
0446
0447 {
0448 auto trapBounds = std::make_shared<TrapezoidVolumeBounds>(4., 8., 10., 15.);
0449 auto volume = std::make_shared<TrackingVolume>(transform, trapBounds,
0450 "TestTrapezoid");
0451
0452 auto payload = converter.convertVolume(gctx, *volume);
0453
0454 BOOST_CHECK(payload.type == detray::volume_id::e_trapezoid);
0455 BOOST_CHECK_EQUAL(payload.name, "TestTrapezoid");
0456 }
0457
0458
0459 {
0460 class MockVolumeBounds : public VolumeBounds {
0461 public:
0462 BoundsType type() const final { return BoundsType::eOther; }
0463 std::vector<double> values() const final { return {}; }
0464 bool inside(const Vector3& , double ) const final {
0465 return false;
0466 }
0467 std::ostream& toStream(std::ostream& sl) const final { return sl; }
0468
0469 std::vector<OrientedSurface> orientedSurfaces(
0470 const Transform3& ) const final {
0471 return {};
0472 }
0473
0474 Volume::BoundingBox boundingBox(const Transform3* ,
0475 const Vector3& ,
0476 const Volume* ) const final {
0477 return {nullptr, Vector3::Zero(), Vector3::Zero()};
0478 }
0479 };
0480
0481 auto mockBounds = std::make_shared<MockVolumeBounds>();
0482 TrackingVolume tv(transform, mockBounds, "TestUnknown");
0483
0484 auto volume =
0485 std::make_shared<TrackingVolume>(transform, mockBounds, "TestUnknown");
0486
0487 auto payload = converter.convertVolume(gctx, *volume);
0488
0489 BOOST_CHECK(payload.type == detray::volume_id::e_unknown);
0490 BOOST_CHECK_EQUAL(payload.name, "TestUnknown");
0491 }
0492 }
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508
0509
0510
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578 BOOST_AUTO_TEST_CASE(DetrayTrackingGeometryConversionTests) {
0579 auto gctx = GeometryContext::dangerouslyDefaultConstruct();
0580 auto geoLogger = getDefaultLogger("Geo", Logging::VERBOSE);
0581
0582 CylindricalTrackingGeometry cGeometry(gctx, true);
0583 auto tGeometry = cGeometry(*geoLogger);
0584
0585 ObjVisualization3D obj;
0586
0587 tGeometry->visualize(obj, gctx);
0588
0589 obj.write("cylindrical.obj");
0590
0591 vecmem::host_memory_resource mr;
0592
0593 DetrayPayloadConverter::Config cfg;
0594
0595 tGeometry->apply([&cfg](const TrackingVolume& volume) {
0596 if (volume.volumeName() == "Beampipe") {
0597 cfg.beampipeVolume = &volume;
0598 }
0599 });
0600
0601 auto logger = getDefaultLogger("Cnv", Logging::DEBUG);
0602 DetrayPayloadConverter converter(cfg, std::move(logger));
0603 auto payloads = converter.convertTrackingGeometry(gctx, *tGeometry);
0604
0605 const auto& detector = *payloads.detector;
0606 const auto& homogeneousMaterial = *payloads.homogeneousMaterial;
0607 auto& materialGrids = *payloads.materialGrids;
0608 const auto& surfaceGrids = *payloads.surfaceGrids;
0609
0610 BOOST_CHECK_EQUAL(detector.volumes.size(), 6);
0611 for (const auto& volume : detector.volumes) {
0612 BOOST_CHECK(volume.type == detray::volume_id::e_cylinder);
0613 }
0614 BOOST_CHECK_EQUAL(detector.volumes.at(0).name, "Beampipe");
0615 BOOST_CHECK_EQUAL(detector.volumes.at(1).name, "World");
0616 BOOST_CHECK_EQUAL(detector.volumes.at(2).name, "L0");
0617 BOOST_CHECK_EQUAL(detector.volumes.at(3).name, "L1");
0618 BOOST_CHECK_EQUAL(detector.volumes.at(4).name, "L2");
0619 BOOST_CHECK_EQUAL(detector.volumes.at(5).name, "L3");
0620
0621
0622
0623
0624 BOOST_CHECK_EQUAL(homogeneousMaterial.volumes.size(), 6);
0625
0626 for (const auto& volume : detector.volumes) {
0627 auto it = std::ranges::find_if(
0628 homogeneousMaterial.volumes, [&](const auto& hMat) {
0629 return hMat.volume_link.link == volume.index.link;
0630 });
0631
0632 if (it == homogeneousMaterial.volumes.end()) {
0633 BOOST_FAIL("No material slab found for volume: " + volume.name);
0634 continue;
0635 }
0636
0637 auto& hMat = *it;
0638
0639
0640 BOOST_CHECK_EQUAL(volume.surfaces.size(), hMat.mat_slabs.size());
0641 }
0642
0643 BOOST_CHECK_EQUAL(materialGrids.grids.size(), 2);
0644
0645 BOOST_CHECK_EQUAL(surfaceGrids.grids.size(), 4);
0646
0647
0648 const std::vector<std::pair<int, int>> kLayerBinning = {
0649 {16, 14}, {32, 14}, {52, 14}, {78, 14}};
0650
0651 for (const auto& [idx, it] : enumerate(surfaceGrids.grids)) {
0652 const auto [b0, b1] = kLayerBinning.at(idx);
0653
0654 const auto [key, grids] = it;
0655
0656
0657 BOOST_CHECK_EQUAL(grids.size(), 1);
0658
0659 auto& grid = grids.front();
0660
0661
0662 BOOST_CHECK_EQUAL(grid.axes.size(), 2);
0663
0664 BOOST_CHECK_EQUAL(b0, grid.axes.front().bins);
0665 BOOST_CHECK_EQUAL(b1, grid.axes.back().bins);
0666
0667 std::size_t nBinsExp = grid.axes.front().bins * grid.axes.back().bins;
0668 BOOST_CHECK_EQUAL(grid.bins.size(), nBinsExp);
0669 }
0670
0671 BOOST_CHECK_EQUAL(payloads.names.size(), 7);
0672
0673
0674
0675 {
0676 detray::io::geo_header_payload header_data;
0677 header_data.common = detray::io::detail::basic_converter::to_payload(
0678 payloads.names.at(0), detray::io::geometry_writer::tag);
0679 header_data.sub_header.emplace();
0680 auto& geo_sub_header = header_data.sub_header.value();
0681 geo_sub_header.n_volumes = detector.volumes.size();
0682 geo_sub_header.n_surfaces = 0;
0683 for (const auto& volume : detector.volumes) {
0684 geo_sub_header.n_surfaces += volume.surfaces.size();
0685 }
0686
0687 nlohmann::ordered_json out_json;
0688 out_json["header"] = header_data;
0689 out_json["data"] = detector;
0690
0691 std::ofstream ofs{"Detector_geometry_direct.json"};
0692 ofs << out_json.dump(2) << std::endl;
0693 }
0694
0695 {
0696 detray::io::homogeneous_material_header_payload header_data;
0697 header_data.common = detray::io::detail::basic_converter::to_payload(
0698 payloads.names.at(0), detray::io::homogeneous_material_writer::tag);
0699 header_data.sub_header.emplace();
0700 header_data.sub_header->n_rods = 0;
0701 header_data.sub_header->n_slabs = 0;
0702
0703 for (const auto& hVol : homogeneousMaterial.volumes) {
0704 if (hVol.mat_rods.has_value()) {
0705 header_data.sub_header->n_rods += hVol.mat_rods->size();
0706 }
0707 header_data.sub_header->n_slabs += hVol.mat_slabs.size();
0708 }
0709
0710 nlohmann::ordered_json out_json;
0711 out_json["header"] = header_data;
0712 out_json["data"] = homogeneousMaterial;
0713
0714 std::ofstream ofs{"Detector_homogeneous_material_direct.json"};
0715 ofs << out_json.dump(2) << std::endl;
0716 }
0717
0718
0719
0720 using detector_t =
0721 detray::detector<detray::default_metadata<detray::array<double>>>;
0722
0723
0724 detray::detector_builder<detector_t::metadata> detectorBuilder{};
0725
0726 detray::io::geometry_reader::from_payload<detector_t>(detectorBuilder,
0727 detector);
0728
0729 detray::io::homogeneous_material_reader::from_payload<detector_t>(
0730 detectorBuilder, homogeneousMaterial);
0731
0732 detray::io::material_map_reader<std::integral_constant<std::size_t, 2>>::
0733 from_payload<detector_t>(detectorBuilder, std::move(materialGrids));
0734
0735 detray::io::surface_grid_reader<detector_t::surface_type,
0736 std::integral_constant<std::size_t, 0>,
0737 std::integral_constant<std::size_t, 2>>::
0738 from_payload<detector_t>(detectorBuilder, surfaceGrids);
0739
0740 detector_t detrayDetector(detectorBuilder.build(mr));
0741
0742
0743 detray::detail::check_consistency(detrayDetector);
0744
0745 detray::svgtools::illustrator illustrator(detrayDetector, payloads.names);
0746 illustrator.hide_eta_lines(true);
0747 illustrator.show_info(true);
0748
0749 const auto svg_zr = illustrator.draw_detector(actsvg::views::z_r{});
0750 actsvg::style::stroke stroke_black = actsvg::style::stroke();
0751 auto zr_axis = actsvg::draw::x_y_axes("axes", {-250, 250}, {-250, 250},
0752 stroke_black, "z", "r");
0753 detray::svgtools::write_svg("test_svgtools_detector_zr", {
0754 zr_axis,
0755 svg_zr,
0756 });
0757
0758 const auto svg_xy = illustrator.draw_detector(actsvg::views::x_y{});
0759 auto xy_axis = actsvg::draw::x_y_axes("axes", {-250, 250}, {-250, 250},
0760 stroke_black, "x", "y");
0761 detray::svgtools::write_svg("test_svgtools_detector_xy", {
0762 xy_axis,
0763 svg_xy,
0764 });
0765
0766 auto writer_cfg = detray::io::detector_writer_config{}
0767 .format(detray::io::format::json)
0768 .replace_files(true);
0769
0770
0771
0772
0773 detray::io::write_detector(detrayDetector, payloads.names, writer_cfg);
0774 }
0775
0776 BOOST_AUTO_TEST_SUITE_END()
0777 }