Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:10:19

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