From 47e534ee42d56793e070159db00c99d99bfbf6f2 Mon Sep 17 00:00:00 2001 From: EleniXoch <123328574+EleniXoch@users.noreply.github.com> Date: Thu, 4 Jul 2024 22:04:39 +0200 Subject: [PATCH] feat: Detray plugin geometry (#3299) --- Examples/Python/CMakeLists.txt | 7 + Examples/Python/src/Detray.cpp | 64 ++++ Examples/Python/src/DetrayStub.cpp | 13 + Examples/Python/src/ModuleEntry.cpp | 2 + Examples/Scripts/Python/detector_creation.py | 2 + Plugins/CMakeLists.txt | 1 + Plugins/Detray/CMakeLists.txt | 34 ++ .../Plugins/Detray/DetrayConversionHelper.hpp | 117 +++++++ .../Acts/Plugins/Detray/DetrayConverter.hpp | 144 ++++++++ Plugins/Detray/src/DetrayConverter.cpp | 318 ++++++++++++++++++ 10 files changed, 702 insertions(+) create mode 100644 Examples/Python/src/Detray.cpp create mode 100644 Examples/Python/src/DetrayStub.cpp create mode 100644 Plugins/Detray/CMakeLists.txt create mode 100644 Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionHelper.hpp create mode 100644 Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp create mode 100644 Plugins/Detray/src/DetrayConverter.cpp diff --git a/Examples/Python/CMakeLists.txt b/Examples/Python/CMakeLists.txt index ba8672c1d74..d15a8cf8b7f 100644 --- a/Examples/Python/CMakeLists.txt +++ b/Examples/Python/CMakeLists.txt @@ -84,6 +84,13 @@ else() target_sources(ActsPythonBindings PRIVATE src/GeoModelStub.cpp) endif() +if(ACTS_BUILD_PLUGIN_DETRAY) + target_link_libraries(ActsPythonBindings PUBLIC ActsPluginDetray) + target_sources(ActsPythonBindings PRIVATE src/Detray.cpp) +else() + target_sources(ActsPythonBindings PRIVATE src/DetrayStub.cpp) +endif() + if(ACTS_BUILD_PLUGIN_ACTSVG) target_link_libraries(ActsPythonBindings PUBLIC ActsExamplesIoSvg) target_sources(ActsPythonBindings PRIVATE src/Svg.cpp) diff --git a/Examples/Python/src/Detray.cpp b/Examples/Python/src/Detray.cpp new file mode 100644 index 00000000000..34d23aad495 --- /dev/null +++ b/Examples/Python/src/Detray.cpp @@ -0,0 +1,64 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Detector/Detector.hpp" +#include "Acts/Plugins/Detray/DetrayConverter.hpp" +#include "Acts/Plugins/Python/Utilities.hpp" + +#include +#include + +#include +#include +#include + +#include "detray/builders/detector_builder.hpp" +#include "detray/io/frontend/detector_reader_config.hpp" + +namespace py = pybind11; +using namespace pybind11::literals; + +using namespace Acts; +using namespace detray; +using namespace detray::io::detail; + +namespace Acts::Python { + +void addDetray(Context& ctx) { + auto [m, mex] = ctx.get("main", "examples"); + + { + py::class_, + std::shared_ptr>>(m, + "detray_detector"); + } + + { mex.def("writeToJson", &DetrayConverter::writeToJson); } + /** + { + /// @brief Converts an Acts::Detector to a detray::detector + mex.def("convertDetectorToDetray", + [](const Acts::GeometryContext& gctx, + const Acts::Experimental::Detector& acts_detector, + const std::string& name) -> auto + {//detector + + // Create a host memory resource + vecmem::host_memory_resource host_mr; + // Convert Acts detector to detray detector using the + detray converter function auto det_tuple = + DetrayConverter::detrayConvert(acts_detector, gctx, host_mr); + + return true; //TO DO:: cannot return tuple + + }); + } + + **/ +} +} // namespace Acts::Python diff --git a/Examples/Python/src/DetrayStub.cpp b/Examples/Python/src/DetrayStub.cpp new file mode 100644 index 00000000000..2ade0dd8bc5 --- /dev/null +++ b/Examples/Python/src/DetrayStub.cpp @@ -0,0 +1,13 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Plugins/Python/Utilities.hpp" + +namespace Acts::Python { +void addDetray(Context& /*ctx*/) {} +} // namespace Acts::Python diff --git a/Examples/Python/src/ModuleEntry.cpp b/Examples/Python/src/ModuleEntry.cpp index cc6442fe285..8b129e37fae 100644 --- a/Examples/Python/src/ModuleEntry.cpp +++ b/Examples/Python/src/ModuleEntry.cpp @@ -71,6 +71,7 @@ void addDigitization(Context& ctx); void addPythia8(Context& ctx); void addGeoModel(Context& ctx); void addJson(Context& ctx); +void addDetray(Context& ctx); void addHepMC3(Context& ctx); void addExaTrkXTrackFinding(Context& ctx); void addSvg(Context& ctx); @@ -132,6 +133,7 @@ PYBIND11_MODULE(ActsPythonBindings, m) { addPythia8(ctx); addJson(ctx); addGeoModel(ctx); + addDetray(ctx); addHepMC3(ctx); addExaTrkXTrackFinding(ctx); addObj(ctx); diff --git a/Examples/Scripts/Python/detector_creation.py b/Examples/Scripts/Python/detector_creation.py index b3d2e0f039f..6939ae9cb20 100644 --- a/Examples/Scripts/Python/detector_creation.py +++ b/Examples/Scripts/Python/detector_creation.py @@ -92,3 +92,5 @@ ) acts.examples.writeDetectorToJsonDetray(geoContext, detector, "odd-detray") + + # det_detector = acts.examples.DetrayConverter(geoContext, detector,"odd-detray") diff --git a/Plugins/CMakeLists.txt b/Plugins/CMakeLists.txt index 93fa92a23e8..c961f82fd7f 100644 --- a/Plugins/CMakeLists.txt +++ b/Plugins/CMakeLists.txt @@ -10,6 +10,7 @@ add_component_if(Json PluginJson ACTS_BUILD_PLUGIN_JSON) add_component_if(Legacy PluginLegacy ACTS_BUILD_PLUGIN_LEGACY) add_component_if(Onnx PluginOnnx ACTS_BUILD_PLUGIN_ONNX) add_component_if(ExaTrkX PluginExaTrkX ACTS_BUILD_PLUGIN_EXATRKX) +add_component_if(Detray PluginDetray ACTS_BUILD_PLUGIN_DETRAY) # dependent plugins. depend either on a independent plugins or on one another add_component_if(TGeo PluginTGeo ACTS_BUILD_PLUGIN_TGEO) diff --git a/Plugins/Detray/CMakeLists.txt b/Plugins/Detray/CMakeLists.txt new file mode 100644 index 00000000000..a2a1585063d --- /dev/null +++ b/Plugins/Detray/CMakeLists.txt @@ -0,0 +1,34 @@ +add_library( + ActsPluginDetray SHARED + src/DetrayConverter.cpp) + +add_dependencies(ActsPluginDetray + detray::core + covfie::core + vecmem::core) + +target_include_directories( + ActsPluginDetray + PUBLIC + $ + $) + +target_link_libraries( + ActsPluginDetray + PUBLIC + ActsCore + detray::core + detray::core_array + detray::io + detray::utils + detray::svgtools + vecmem::core) + +install( + TARGETS ActsPluginDetray + EXPORT ActsPluginDetrayTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +install( + DIRECTORY include/Acts + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionHelper.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionHelper.hpp new file mode 100644 index 00000000000..5fdbd2d7002 --- /dev/null +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConversionHelper.hpp @@ -0,0 +1,117 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2023 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Surfaces/SurfaceBounds.hpp" +#include "Acts/Utilities/BinningData.hpp" + +#include +#include + +#include + +#include "detray/io/frontend/definitions.hpp" + +namespace Acts::DetrayConversionHelper { + +/// @brief Helper function to switch keys from ACTS to detray +/// +/// DETRAY types @todo change to detray imports when available +/// annulus2 = 0u, +/// cuboid3 = 1u, +/// cylinder2 = 2u, +/// cylinder3 = 3u, +/// portal_cylinder2 = 4u, +/// rectangle2 = 5u, +/// ring2 = 6u, +/// trapezoid2 = 7u, +/// cell_wire = 8u, +/// straw_wire = 9u, +/// single1 = 10u, +/// single2 = 11u, +/// single3 = 12u, +/// unknown = 13u +/// +/// @param sBounds is the surface bounds type +/// @param portal is the flag for conversion into detray portal format +/// +/// @return type and value array in detray format +inline static std::tuple> maskFromBounds( + const Acts::SurfaceBounds& sBounds, bool portal = false) { + auto bType = sBounds.type(); + auto bValues = sBounds.values(); + // Return value + unsigned int type = 13u; + std::vector boundaries = bValues; + // Special treatment for some portals + if (portal && bType == SurfaceBounds::BoundsType::eCylinder) { + boundaries = {bValues[0u], -bValues[1u], bValues[1u]}; + type = 4u; + } else { + switch (bType) { + case SurfaceBounds::BoundsType::eAnnulus: { + type = 0u; + } break; + case SurfaceBounds::BoundsType::eRectangle: { + type = 5u; + // ACTS: eMinX = 0, eMinY = 1, eMaxX = 2, eMaxY = 3, + // detray: e_half_x, e_half_y + boundaries = {0.5 * (bValues[2] - bValues[0]), + 0.5 * (bValues[3] - bValues[1])}; + } break; + case SurfaceBounds::BoundsType::eCylinder: { + boundaries = {bValues[0u], -bValues[1u], bValues[1u]}; + type = 2u; + } break; + case SurfaceBounds::BoundsType::eTrapezoid: { + type = 7u; + boundaries = {bValues[0u], bValues[1u], bValues[2u], + 1 / (2 * bValues[2u])}; + } break; + case SurfaceBounds::BoundsType::eDisc: { + boundaries = {bValues[0u], bValues[1u]}; + type = 6u; + } break; + default: + break; + } + } + return std::tie(type, boundaries); +} + +/// Determine the acceleration link from a grid +/// +/// @param casts are the grid axes cast types +/// +/// @return the acceleration link idnetifier +template +inline static std::size_t accelerationLink(const binning_values_t& casts) { + // Default is `brute_force` + std::size_t accLink = detray::io::accel_id::brute_force; + if (casts.size() == 2u) { + if (casts[0u] == binX && casts[1u] == binY) { + accLink = detray::io::accel_id::cartesian2_grid; + } else if (casts[0u] == binR && casts[1u] == binPhi) { + accLink = detray::io::accel_id::polar2_grid; + } else if (casts[0u] == binZ && casts[1u] == binPhi) { + accLink = detray::io::accel_id::cylinder2_grid; + } else if (casts[0u] == binZ && casts[1u] == binR) { + accLink = detray::io::accel_id::cylinder3_grid; + } + } else if (casts.size() == 3u) { + if (casts[0u] == binX && casts[1u] == binY && casts[2u] == binZ) { + accLink = detray::io::accel_id::cuboid3_grid; + } else if (casts[0u] == binZ && casts[1u] == binPhi && casts[2u] == binR) { + accLink = detray::io::accel_id::cylinder3_grid; + } + } + return accLink; +} + +} // namespace Acts::DetrayConversionHelper diff --git a/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp new file mode 100644 index 00000000000..4de8a8bf5ac --- /dev/null +++ b/Plugins/Detray/include/Acts/Plugins/Detray/DetrayConverter.hpp @@ -0,0 +1,144 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/VolumeBounds.hpp" + +#include + +#include "detray/builders/detector_builder.hpp" +#include "detray/core/detector.hpp" +#include "detray/definitions/geometry.hpp" +#include "detray/io/common/geometry_reader.hpp" +#include "detray/io/frontend/detector_writer.hpp" +#include "detray/io/frontend/payloads.hpp" +#include "detray/utils/consistency_checker.hpp" + +namespace Acts { + +class Surface; +class SurfaceBounds; + +namespace Experimental { +class Detector; +class DetectorVolume; +class Portal; +} // namespace Experimental + +namespace DetrayConverter { + +using DetrayDetector = detray::detector; + +/// Write the detector to json output +/// +/// @param dDetector is the detray detector (converted) +/// @param names a name map for the detector volumes +/// @param writer_cfg the writer configuration +void writeToJson(const DetrayDetector& dDetector, + const typename DetrayDetector::name_map& names = {}, + detray::io::detector_writer_config writer_cfg = {}); + +/// Conversion method for transform objects to detray::transform payloads +/// +/// @param t the transform to be converted +/// +/// @return the transform_payload(translation, rotation) +detray::io::transform_payload convertTransform(const Transform3& t); + +/// Conversion method for surface bounds to detray::mask payloads +/// +/// @param bounds the bounds object to be converted +/// @param portal the flag for conversion into detray portal format +/// +/// @return the mask_payload representing the bounds +detray::io::mask_payload convertMask(const SurfaceBounds& bounds, + bool portal = false); + +/// Conversion method for surface objects to detray::surface payloads +/// +/// @param gctx the geometry context +/// @param surface the surface to be converted +/// @param portal the flag for conversion into detray portal format +/// +/// @return the surface_payload for portals and volumes by @param Surface acts object +detray::io::surface_payload convertSurface(const GeometryContext& gctx, + const Surface& surface, + bool portal = false); +/// Conversion method for Portal object to detray::portal payloads +/// +/// @param gctx the geometry context +/// @param portal the portal to be converted +/// @param ip the portal index +/// @param volume the volume to which the portal belongs +/// @param orientedSurfaces the oriented surfaces of the portal +/// @param detectorVolumes the detector volumes for the link lookup +/// +/// @note due to portal splitting this can add up in N portals for one initial one +/// +/// @brief convert the acts portal to detray surface payload and populate the payload +std::vector convertPortal( + const GeometryContext& gctx, const Experimental::Portal& portal, + std::size_t ip, const Experimental::DetectorVolume& volume, + const std::vector& orientedSurfaces, + const std::vector& detectorVolumes); + +/// Conversion method for volume objects to detray::volume payloads +/// +/// @param gctx the geometry context +/// @param volume the volume to be converted +/// @param detectorVolumes the detector volumes for the link lookup +/// +/// @return the volume_payload for portals and volumes by @param volume acts object +detray::io::volume_payload convertVolume( + const GeometryContext& gctx, const Experimental::DetectorVolume& volume, + const std::vector& detectorVolumes); + +/// Conversion method for (common) header payload +/// +/// @param detector is the detector to be converted +/// +/// @return a geometry header payload +detray::io::geo_header_payload convertHead( + const Acts::Experimental::Detector& detector); + +/// Convert an Acts::Experimental::Detector to a detray::detector object +/// +/// @param gctx the geometry context +/// @param detector the detector to be converted +/// @param mr the memory resource to be used +/// +/// @returns a detector of requested return type +template +std::tuple convertDetector( + const Acts::GeometryContext& gctx, + const Acts::Experimental::Detector& detector, vecmem::memory_resource& mr) { + detray::io::detector_payload detectorPayload; + for (const auto volume : detector.volumes()) { + detectorPayload.volumes.push_back( + convertVolume(gctx, *volume, detector.volumes())); + } + typename detector_t::name_map names = {{0u, detector.name()}}; + + // build detector + detray::detector_builder detectorBuilder{}; + detray::io::geometry_reader::convert(detectorBuilder, names, + detectorPayload); + detector_t detrayDetector(detectorBuilder.build(mr)); + + // checks and print + detray::detail::check_consistency(detrayDetector); + converterPrint(detrayDetector, names); + + return {std::move(detrayDetector), mr}; +} + +} // namespace DetrayConverter +} // namespace Acts diff --git a/Plugins/Detray/src/DetrayConverter.cpp b/Plugins/Detray/src/DetrayConverter.cpp new file mode 100644 index 00000000000..d3ce397923c --- /dev/null +++ b/Plugins/Detray/src/DetrayConverter.cpp @@ -0,0 +1,318 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Acts/Plugins/Detray/DetrayConverter.hpp" + +#include "Acts/Detector/Detector.hpp" +#include "Acts/Detector/DetectorVolume.hpp" +#include "Acts/Detector/Portal.hpp" +#include "Acts/Navigation/PortalNavigation.hpp" +#include "Acts/Plugins/Detray/DetrayConversionHelper.hpp" +#include "Acts/Surfaces/CylinderBounds.hpp" +#include "Acts/Surfaces/CylinderSurface.hpp" +#include "Acts/Surfaces/DiscSurface.hpp" +#include "Acts/Surfaces/RadialBounds.hpp" +#include "Acts/Surfaces/RegularSurface.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Surfaces/SurfaceBounds.hpp" + +#include "detray/io/frontend/detector_writer.hpp" + +using namespace detray; + +namespace { + +/// Find the position of the volume to point to +/// +/// @param volume the volume to find +/// @param the collection of volumes +/// +/// @note return -1 if not found, to be interpreted by the caller +int findVolume( + const Acts::Experimental::DetectorVolume* volume, + const std::vector& volumes) { + auto candidate = std::find(volumes.begin(), volumes.end(), volume); + if (candidate != volumes.end()) { + return std::distance(volumes.begin(), candidate); + } + return -1; +} +} // namespace + +/// detray geometry writer function, debug purposes +void Acts::DetrayConverter::writeToJson( + const DetrayDetector& dDetector, + const typename DetrayDetector::name_map& names, + detray::io::detector_writer_config writer_cfg) { + writer_cfg.format(detray::io::format::json); + detray::io::write_detector(dDetector, names, writer_cfg); +} + +detray::io::transform_payload Acts::DetrayConverter::convertTransform( + const Transform3& t) { + detray::io::transform_payload tfPayload; + auto translation = t.translation(); + tfPayload.tr = {translation.x(), translation.y(), translation.z()}; + + const auto rotation = t.rotation(); + tfPayload.rot = {rotation(0, 0), rotation(0, 1), rotation(0, 2), + rotation(1, 0), rotation(1, 1), rotation(1, 2), + rotation(2, 0), rotation(2, 1), rotation(2, 2)}; + return tfPayload; +} + +detray::io::mask_payload Acts::DetrayConverter::convertMask( + const Acts::SurfaceBounds& bounds, bool portal) { + detray::io::mask_payload maskPayload; + auto [shape, boundaries] = + DetrayConversionHelper::maskFromBounds(bounds, portal); + maskPayload.shape = static_cast(shape); + maskPayload.boundaries = static_cast>(boundaries); + // default maskPayload.volume_link + + return maskPayload; +} + +detray::io::surface_payload Acts::DetrayConverter::convertSurface( + const Acts::GeometryContext& gctx, const Surface& surface, bool portal) { + using material_link_payload = + detray::io::typed_link_payload; + + detray::io::surface_payload surfacePayload; + + surfacePayload.transform = convertTransform(surface.transform(gctx)); + surfacePayload.source = surface.geometryId().value(); + surfacePayload.barcode = std::nullopt; + surfacePayload.type = static_cast( + portal ? surface_id::e_portal + : (surface.geometryId().sensitive() > 0 + ? detray::surface_id::e_sensitive + : detray::surface_id::e_passive)); + surfacePayload.mask = convertMask(surface.bounds()); + return surfacePayload; +} + +std::vector Acts::DetrayConverter::convertPortal( + const GeometryContext& gctx, const Experimental::Portal& portal, + std::size_t ip, const Experimental::DetectorVolume& volume, + const std::vector& orientedSurfaces, + const std::vector& detectorVolumes) { + std::vector portals{}; + + const RegularSurface& surface = portal.surface(); + const auto& volumeLinks = portal.portalNavigation(); + + // First assumption for outside link (along direction) + std::size_t outside = 1u; + + // Find out if you need to take the outside or inside volume + // for planar surfaces that's easy + if (surface.type() != Acts::Surface::SurfaceType::Cylinder) { + // Get the two volume center + const auto volumeCenter = volume.transform(gctx).translation(); + const auto surfaceCenter = surface.center(gctx); + const auto surfaceNormal = surface.normal(gctx, surfaceCenter); + // Get the direction from the volume to the surface, correct link + const auto volumeToSurface = surfaceCenter - volumeCenter; + if (volumeToSurface.dot(surfaceNormal) < 0.) { + outside = 0u; + } + } else { + // This is a cylinder portal, inner cover reverses the normal + if (ip == 3u) { + outside = 0u; + } + } + + const auto& outsideLink = volumeLinks[outside]; + // Grab the corresponding volume link + // If it is a single link, we are done + const auto* instance = outsideLink.instance(); + // Single link cast + auto singleLink = + dynamic_cast( + instance); + + auto [surfaceAdjusted, insidePointer] = orientedSurfaces[ip]; + + // Single link detected - just write it out, we use the oriented surface + // in order to make sure the size is adjusted + if (singleLink != nullptr) { + // Single link can be written out + std::size_t vLink = findVolume(singleLink->object(), detectorVolumes); + auto portalPayload = convertSurface(gctx, *surfaceAdjusted, true); + portalPayload.mask.volume_link.link = vLink; + portals.push_back(portalPayload); + } else { + // Multi link detected - 1D + auto multiLink1D = + dynamic_cast( + instance); + if (multiLink1D != nullptr) { + // Resolve the multi link 1D + auto boundaries = + multiLink1D->indexedUpdater.grid.axes()[0u]->getBinEdges(); + const auto& cast = multiLink1D->indexedUpdater.casts[0u]; + const auto& transform = multiLink1D->indexedUpdater.transform; + const auto& volumes = multiLink1D->indexedUpdater.extractor.dVolumes; + + // Apply the correction from local to global boundaries + ActsScalar gCorr = VectorHelpers::cast(transform.translation(), cast); + std::for_each(boundaries.begin(), boundaries.end(), + [&gCorr](ActsScalar& b) { b -= gCorr; }); + + // Get the volume indices + auto surfaceType = surfaceAdjusted->type(); + std::vector vIndices = {}; + for (const auto& v : volumes) { + vIndices.push_back(findVolume(v, detectorVolumes)); + } + + // Pick the surface dimension - via poly + std::array clipRange = {0., 0.}; + std::vector boundValues = surfaceAdjusted->bounds().values(); + if (surfaceType == Surface::SurfaceType::Cylinder && cast == binZ) { + ActsScalar zPosition = surfaceAdjusted->center(gctx).z(); + clipRange = { + zPosition - boundValues[CylinderBounds::BoundValues::eHalfLengthZ], + zPosition + boundValues[CylinderBounds::BoundValues::eHalfLengthZ]}; + } else if (surfaceType == Surface::SurfaceType::Disc && cast == binR) { + clipRange = {boundValues[RadialBounds::BoundValues::eMinR], + boundValues[RadialBounds::BoundValues::eMaxR]}; + } else { + throw std::runtime_error( + "PortalDetrayConverter: surface type not (yet) supported for " + "detray " + "conversion, only cylinder and disc are currently supported."); + } + + // Need to clip the parameter space to the surface dimension + std::vector clippedIndices = {}; + std::vector clippedBoundaries = {}; + clippedBoundaries.push_back(clipRange[0u]); + for (const auto [ib, b] : enumerate(boundaries)) { + if (ib > 0) { + unsigned int vI = vIndices[ib - 1u]; + ActsScalar highEdge = boundaries[ib]; + if (boundaries[ib - 1] >= clipRange[1u]) { + break; + } + if (highEdge <= clipRange[0u] || + std::abs(highEdge - clipRange[0u]) < 1e-5) { + continue; + } + if (highEdge > clipRange[1u]) { + highEdge = clipRange[1u]; + } + clippedIndices.push_back(vI); + clippedBoundaries.push_back(highEdge); + } + } + // Interpret the clipped information + // + // Clipped cylinder case + if (surfaceType == Surface::SurfaceType::Cylinder) { + for (auto [ib, b] : enumerate(clippedBoundaries)) { + if (ib > 0) { + // Create sub surfaces + std::array + subBoundValues = {}; + for (auto [ibv, bv] : enumerate(boundValues)) { + subBoundValues[ibv] = bv; + } + subBoundValues[CylinderBounds::BoundValues::eHalfLengthZ] = + 0.5 * (b - clippedBoundaries[ib - 1u]); + auto subBounds = std::make_shared(subBoundValues); + auto subTransform = Transform3::Identity(); + subTransform.pretranslate(Vector3( + 0., 0., + clippedBoundaries[ib - 1u] + + subBoundValues[CylinderBounds::BoundValues::eHalfLengthZ])); + auto subSurface = + Surface::makeShared(subTransform, subBounds); + + auto portalPayload = convertSurface(gctx, *subSurface, true); + portalPayload.mask.volume_link.link = clippedIndices[ib - 1u]; + portals.push_back(portalPayload); + } + } + } else { + for (auto [ib, b] : enumerate(clippedBoundaries)) { + if (ib > 0) { + // Create sub surfaces + std::array + subBoundValues = {}; + for (auto [ibv, bv] : enumerate(boundValues)) { + subBoundValues[ibv] = bv; + } + subBoundValues[RadialBounds::BoundValues::eMinR] = + clippedBoundaries[ib - 1u]; + subBoundValues[RadialBounds::BoundValues::eMaxR] = b; + auto subBounds = std::make_shared(subBoundValues); + auto subSurface = Surface::makeShared( + portal.surface().transform(gctx), subBounds); + + auto portalPayload = convertSurface(gctx, *subSurface, true); + portalPayload.mask.volume_link.link = clippedIndices[ib - 1u]; + portals.push_back(portalPayload); + } + } + } + + } else { + // End of world portal + // Write surface with invalid link + auto portalPayload = convertSurface(gctx, *surfaceAdjusted, true); + using NavigationLink = + typename DetrayDetector::surface_type::navigation_link; + portalPayload.mask.volume_link.link = + std::numeric_limits::max(); + + portals.push_back(portalPayload); + } + } + + return portals; +} + +detray::io::volume_payload Acts::DetrayConverter::convertVolume( + const Acts::GeometryContext& gctx, + const Acts::Experimental::DetectorVolume& volume, + const std::vector& detectorVolumes) { + detray::io::volume_payload volumePayload; + volumePayload.name = volume.name(); + volumePayload.index.link = findVolume(&volume, detectorVolumes); + volumePayload.transform = convertTransform(volume.transform(gctx)); + + // iterate over surfaces and portals keeping the same surf_pd.index_in_coll + std::size_t sIndex = 0; + for (const auto surface : volume.surfaces()) { + io::surface_payload surfacePayload = convertSurface(gctx, *surface, false); + + surfacePayload.index_in_coll = sIndex++; + surfacePayload.mask.volume_link.link = + volumePayload.index.link; // link surface' mask to volume + volumePayload.surfaces.push_back(surfacePayload); + } + + auto orientedSurfaces = + volume.volumeBounds().orientedSurfaces(volume.transform(gctx)); + + int portalCounter = 0; + for (const auto& [ip, p] : enumerate(volume.portals())) { + auto portals = + convertPortal(gctx, *p, ip, volume, orientedSurfaces, detectorVolumes); + std::for_each(portals.begin(), portals.end(), [&](auto& portalPayload) { + portalPayload.index_in_coll = sIndex++; + volumePayload.surfaces.push_back(portalPayload); + portalCounter++; + }); + } + + return volumePayload; +}