Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:15:18

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 "Acts/Plugins/Json/PortalJsonConverter.hpp"
0010 
0011 #include "Acts/Detector/DetectorVolume.hpp"
0012 #include "Acts/Detector/Portal.hpp"
0013 #include "Acts/Detector/detail/PortalHelper.hpp"
0014 #include "Acts/Navigation/PortalNavigation.hpp"
0015 #include "Acts/Plugins/Json/DetrayJsonHelper.hpp"
0016 #include "Acts/Plugins/Json/SurfaceJsonConverter.hpp"
0017 #include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp"
0018 #include "Acts/Surfaces/CylinderBounds.hpp"
0019 #include "Acts/Surfaces/CylinderSurface.hpp"
0020 #include "Acts/Surfaces/DiscSurface.hpp"
0021 #include "Acts/Surfaces/RadialBounds.hpp"
0022 #include "Acts/Surfaces/RegularSurface.hpp"
0023 #include "Acts/Surfaces/Surface.hpp"
0024 #include "Acts/Utilities/Enumerate.hpp"
0025 #include "Acts/Utilities/VectorHelpers.hpp"
0026 
0027 #include <algorithm>
0028 #include <iterator>
0029 #include <memory>
0030 #include <vector>
0031 
0032 namespace {
0033 
0034 /// Find the position of the volume to point to
0035 ///
0036 /// @param volume the volume to find
0037 /// @param the collection of volumes
0038 ///
0039 /// @note return -1 if not found, to be interpreted by the caller
0040 int findVolume(
0041     const Acts::Experimental::DetectorVolume* volume,
0042     const std::vector<const Acts::Experimental::DetectorVolume*>& volumes) {
0043   auto candidate = std::ranges::find(volumes, volume);
0044   if (candidate != volumes.end()) {
0045     return std::distance(volumes.begin(), candidate);
0046   }
0047   return -1;
0048 }
0049 }  // namespace
0050 
0051 nlohmann::json Acts::PortalJsonConverter::toJson(
0052     const GeometryContext& gctx, const Experimental::Portal& portal,
0053     const std::vector<const Experimental::DetectorVolume*>& detectorVolumes,
0054     const Options& options) {
0055   // The overall return object
0056   nlohmann::json jPortal;
0057   // Write the surface
0058   jPortal["surface"] = SurfaceJsonConverter::toJson(gctx, portal.surface(),
0059                                                     options.surfaceOptions);
0060   // And the portal specific information
0061   const auto& volumeLinks = portal.portalNavigation();
0062   nlohmann::json jLinks;
0063   for (const auto& vLink : volumeLinks) {
0064     nlohmann::json jLink = toJson(vLink, detectorVolumes);
0065     jLinks.push_back(jLink);
0066   }
0067   jPortal["volume_links"] = jLinks;
0068   // Return the full json object
0069   return jPortal;
0070 }
0071 
0072 std::tuple<std::vector<nlohmann::json>,
0073            std::vector<std::shared_ptr<Acts::Surface>>>
0074 Acts::PortalJsonConverter::toJsonDetray(
0075     const GeometryContext& gctx, const Experimental::Portal& portal,
0076     std::size_t ip, const Experimental::DetectorVolume& volume,
0077     const std::vector<OrientedSurface>& orientedSurfaces,
0078     const std::vector<const Experimental::DetectorVolume*>& detectorVolumes,
0079     const Options& option) {
0080   // The overall return object
0081   std::vector<nlohmann::json> jPortals = {};
0082   const RegularSurface& surface = portal.surface();
0083   const auto& volumeLinks = portal.portalNavigation();
0084 
0085   // First assumption for outside link (along direction)
0086   std::size_t outside = 1u;
0087 
0088   std::vector<std::shared_ptr<Acts::Surface>> subSurfaces = {};
0089 
0090   // Find out if you need to take the outside or inside volume
0091   // for planar surfaces that's easy
0092   if (surface.type() != Acts::Surface::SurfaceType::Cylinder) {
0093     // Get the two volume center
0094     const auto volumeCenter = volume.transform(gctx).translation();
0095     const auto surfaceCenter = surface.center(gctx);
0096     const auto surfaceNormal = surface.normal(gctx, surfaceCenter);
0097     // Get the direction from the volume to the surface, correct link
0098     const auto volumeToSurface = surfaceCenter - volumeCenter;
0099     if (volumeToSurface.dot(surfaceNormal) < 0.) {
0100       outside = 0u;
0101     }
0102   } else {
0103     // This is a cylinder portal, inner cover reverses the normal
0104     if (ip == 3u) {
0105       outside = 0u;
0106     }
0107   }
0108 
0109   const auto& outsideLink = volumeLinks[outside];
0110   // Grab the corresponding volume link
0111   // If it is a single link, we are done
0112   const auto* instance = outsideLink.instance();
0113   // Single link cast
0114   auto singleLink =
0115       dynamic_cast<const Acts::Experimental::SingleDetectorVolumeNavigation*>(
0116           instance);
0117 
0118   auto [surfaceAdjusted, insidePointer] = orientedSurfaces[ip];
0119 
0120   SurfaceJsonConverter::Options surfaceOptions = option.surfaceOptions;
0121   surfaceOptions.portal = true;
0122   // Single link detected - just write it out, we use the oriented surface
0123   // in order to make sure the size is adjusted
0124   if (singleLink != nullptr) {
0125     // Single link can be written out
0126     std::size_t vLink = findVolume(singleLink->object(), detectorVolumes);
0127     auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *surfaceAdjusted,
0128                                                       surfaceOptions);
0129     DetrayJsonHelper::addVolumeLink(jPortal["mask"], vLink);
0130     jPortals.push_back(jPortal);
0131   } else {
0132     // Multi link detected - 1D
0133     auto multiLink1D =
0134         dynamic_cast<const Experimental::BoundVolumesGrid1Navigation*>(
0135             instance);
0136     if (multiLink1D != nullptr) {
0137       // Resolve the multi link 1D
0138       auto boundaries =
0139           multiLink1D->indexedUpdater.grid.axes()[0u]->getBinEdges();
0140       const auto& cast = multiLink1D->indexedUpdater.casts[0u];
0141       const auto& transform = multiLink1D->indexedUpdater.transform;
0142       const auto& volumes = multiLink1D->indexedUpdater.extractor.dVolumes;
0143 
0144       // Apply the correction from local to global boundaries
0145       double gCorr = VectorHelpers::cast(transform.translation(), cast);
0146       std::for_each(boundaries.begin(), boundaries.end(),
0147                     [&gCorr](double& b) { b -= gCorr; });
0148 
0149       // Get the volume indices
0150       auto surfaceType = surfaceAdjusted->type();
0151       std::vector<unsigned int> vIndices = {};
0152       for (const auto& v : volumes) {
0153         vIndices.push_back(findVolume(v, detectorVolumes));
0154       }
0155 
0156       // Pick the surface dimension - via poly
0157       std::array<double, 2u> clipRange = {0., 0.};
0158       std::vector<double> boundValues = surfaceAdjusted->bounds().values();
0159       if (surfaceType == Surface::SurfaceType::Cylinder &&
0160           cast == AxisDirection::AxisZ) {
0161         double zPosition = surfaceAdjusted->center(gctx).z();
0162         clipRange = {
0163             zPosition - boundValues[CylinderBounds::BoundValues::eHalfLengthZ],
0164             zPosition + boundValues[CylinderBounds::BoundValues::eHalfLengthZ]};
0165       } else if (surfaceType == Surface::SurfaceType::Disc &&
0166                  cast == AxisDirection::AxisR) {
0167         clipRange = {boundValues[RadialBounds::BoundValues::eMinR],
0168                      boundValues[RadialBounds::BoundValues::eMaxR]};
0169       } else {
0170         throw std::runtime_error(
0171             "PortalJsonConverter: surface type not (yet) supported for detray "
0172             "conversion, only cylinder and disc are currently supported.");
0173       }
0174 
0175       // Need to clip the parameter space to the surface dimension
0176       std::vector<unsigned int> clippedIndices = {};
0177       std::vector<double> clippedBoundaries = {};
0178       clippedBoundaries.push_back(clipRange[0u]);
0179       for (const auto [ib, b] : enumerate(boundaries)) {
0180         if (ib > 0) {
0181           unsigned int vI = vIndices[ib - 1u];
0182           double highEdge = boundaries[ib];
0183           if (boundaries[ib - 1] >= clipRange[1u]) {
0184             break;
0185           }
0186           if (highEdge <= clipRange[0u] ||
0187               std::abs(highEdge - clipRange[0u]) < 1e-5) {
0188             continue;
0189           }
0190           if (highEdge > clipRange[1u]) {
0191             highEdge = clipRange[1u];
0192           }
0193           clippedIndices.push_back(vI);
0194           clippedBoundaries.push_back(highEdge);
0195         }
0196       }
0197       // Interpret the clipped information
0198       //
0199       // Clipped cylinder case
0200       if (surfaceType == Surface::SurfaceType::Cylinder) {
0201         for (auto [ib, b] : enumerate(clippedBoundaries)) {
0202           if (ib > 0) {
0203             // Create sub surfaces
0204             std::array<double, CylinderBounds::BoundValues::eSize>
0205                 subBoundValues = {};
0206             for (auto [ibv, bv] : enumerate(boundValues)) {
0207               subBoundValues[ibv] = bv;
0208             }
0209             subBoundValues[CylinderBounds::BoundValues::eHalfLengthZ] =
0210                 0.5 * (b - clippedBoundaries[ib - 1u]);
0211             auto subBounds = std::make_shared<CylinderBounds>(subBoundValues);
0212             auto subTransform = Transform3::Identity();
0213             subTransform.pretranslate(Vector3(
0214                 0., 0.,
0215                 clippedBoundaries[ib - 1u] +
0216                     subBoundValues[CylinderBounds::BoundValues::eHalfLengthZ]));
0217             auto subSurface =
0218                 Surface::makeShared<CylinderSurface>(subTransform, subBounds);
0219             subSurfaces.push_back(subSurface);
0220             auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *subSurface,
0221                                                               surfaceOptions);
0222             DetrayJsonHelper::addVolumeLink(jPortal["mask"],
0223                                             clippedIndices[ib - 1u]);
0224             jPortals.push_back(jPortal);
0225           }
0226         }
0227       } else {
0228         for (auto [ib, b] : enumerate(clippedBoundaries)) {
0229           if (ib > 0) {
0230             // Create sub surfaces
0231             std::array<double, RadialBounds::BoundValues::eSize>
0232                 subBoundValues = {};
0233             for (auto [ibv, bv] : enumerate(boundValues)) {
0234               subBoundValues[ibv] = bv;
0235             }
0236             subBoundValues[RadialBounds::BoundValues::eMinR] =
0237                 clippedBoundaries[ib - 1u];
0238             subBoundValues[RadialBounds::BoundValues::eMaxR] = b;
0239             auto subBounds = std::make_shared<RadialBounds>(subBoundValues);
0240             auto subSurface = Surface::makeShared<DiscSurface>(
0241                 portal.surface().transform(gctx), subBounds);
0242             subSurfaces.push_back(subSurface);
0243             auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *subSurface,
0244                                                               surfaceOptions);
0245             DetrayJsonHelper::addVolumeLink(jPortal["mask"],
0246                                             clippedIndices[ib - 1u]);
0247             jPortals.push_back(jPortal);
0248           }
0249         }
0250       }
0251 
0252     } else {
0253       // End of world
0254       // Write surface with invalid link
0255       auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *surfaceAdjusted,
0256                                                         surfaceOptions);
0257       DetrayJsonHelper::addVolumeLink(
0258           jPortal["mask"], std::numeric_limits<std::uint_least16_t>::max());
0259       jPortals.push_back(jPortal);
0260     }
0261   }
0262   return {jPortals, subSurfaces};
0263 }
0264 
0265 nlohmann::json Acts::PortalJsonConverter::toJson(
0266     const Experimental::ExternalNavigationDelegate& updator,
0267     const std::vector<const Experimental::DetectorVolume*>& detectorVolumes) {
0268   nlohmann::json jLink;
0269   if (updator.connected()) {
0270     const auto instance = updator.instance();
0271     // Single link cast
0272     auto singleLink =
0273         dynamic_cast<const Experimental::SingleDetectorVolumeNavigation*>(
0274             instance);
0275     // Single link detected
0276     if (singleLink != nullptr) {
0277       auto vIndex = findVolume(singleLink->object(), detectorVolumes);
0278       if (vIndex < 0) {
0279         throw std::runtime_error(
0280             "PortalJsonConverter: volume not found in the list of volumes.");
0281       }
0282       jLink["single"] = vIndex;
0283     }
0284     // Multi link detected - 1D
0285     auto multiLink1D =
0286         dynamic_cast<const Experimental::BoundVolumesGrid1Navigation*>(
0287             instance);
0288     if (multiLink1D != nullptr) {
0289       nlohmann::json jMultiLink;
0290       const auto& volumes = multiLink1D->indexedUpdater.extractor.dVolumes;
0291       const auto& casts = multiLink1D->indexedUpdater.casts;
0292       nlohmann::json jTransform = Transform3JsonConverter::toJson(
0293           multiLink1D->indexedUpdater.transform);
0294       std::vector<unsigned int> vIndices = {};
0295       for (const auto& v : volumes) {
0296         vIndices.push_back(findVolume(v, detectorVolumes));
0297       }
0298       jMultiLink["boundaries"] =
0299           multiLink1D->indexedUpdater.grid.axes()[0u]->getBinEdges();
0300       jMultiLink["binning"] = casts[0u];
0301       jMultiLink["targets"] = vIndices;
0302       jMultiLink["transform"] = jTransform;
0303       jLink["multi_1D"] = jMultiLink;
0304     }
0305   }
0306   return jLink;
0307 }
0308 
0309 std::shared_ptr<Acts::Experimental::Portal> Acts::PortalJsonConverter::fromJson(
0310     const GeometryContext& gctx, const nlohmann::json& jPortal,
0311     const std::vector<std::shared_ptr<Experimental::DetectorVolume>>&
0312         detectorVolumes) {
0313   // The surface re-creation is trivial
0314   auto surface = SurfaceJsonConverter::fromJson(jPortal["surface"]);
0315   auto regSurface = std::dynamic_pointer_cast<RegularSurface>(surface);
0316   if (!regSurface) {
0317     throw std::runtime_error(
0318         "PortalJsonConverter: surface is not a regular surface.");
0319   }
0320   auto portal = std::make_shared<Experimental::Portal>(regSurface);
0321 
0322   std::array<Acts::Direction, 2> normalDirs = {Direction::Backward(),
0323                                                Direction::Forward()};
0324   // re-create the volume links
0325   auto jLinks = jPortal["volume_links"];
0326   for (auto [ivl, vl] : enumerate(jLinks)) {
0327     if (vl.contains("single")) {
0328       const auto vIndex = vl["single"].get<unsigned int>();
0329       Experimental::detail::PortalHelper::attachExternalNavigationDelegate(
0330           *portal, detectorVolumes[vIndex], normalDirs[ivl]);
0331     } else if (vl.contains("multi_1D")) {
0332       // Resolve the multi link 1D
0333       auto jMultiLink = vl["multi_1D"];
0334       auto boundaries = jMultiLink["boundaries"].get<std::vector<double>>();
0335       auto binning = jMultiLink["binning"].get<AxisDirection>();
0336       auto targets = jMultiLink["targets"].get<std::vector<unsigned int>>();
0337       std::vector<std::shared_ptr<Experimental::DetectorVolume>> targetVolumes;
0338       for (const auto t : targets) {
0339         targetVolumes.push_back(detectorVolumes[t]);
0340       }
0341       Experimental::detail::PortalHelper::attachDetectorVolumesUpdater(
0342           gctx, *portal, targetVolumes, normalDirs[ivl], boundaries, binning);
0343     }
0344   }
0345 
0346   return portal;
0347 }