File indexing completed on 2025-08-05 08:10:19
0001
0002
0003
0004
0005
0006
0007
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
0035
0036
0037
0038
0039
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 }
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
0056 nlohmann::json jPortal;
0057
0058 jPortal["surface"] = SurfaceJsonConverter::toJson(gctx, portal.surface(),
0059 options.surfaceOptions);
0060
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
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
0079 std::vector<nlohmann::json> jPortals = {};
0080 const RegularSurface& surface = portal.surface();
0081 const auto& volumeLinks = portal.detectorVolumeUpdaters();
0082
0083
0084 std::size_t outside = 1u;
0085
0086
0087
0088 if (surface.type() != Acts::Surface::SurfaceType::Cylinder) {
0089
0090 const auto volumeCenter = volume.transform(gctx).translation();
0091 const auto surfaceCenter = surface.center(gctx);
0092 const auto surfaceNormal = surface.normal(gctx, surfaceCenter);
0093
0094 const auto volumeToSurface = surfaceCenter - volumeCenter;
0095 if (volumeToSurface.dot(surfaceNormal) < 0.) {
0096 outside = 0u;
0097 }
0098 } else {
0099
0100 if (ip == 3u) {
0101 outside = 0u;
0102 }
0103 }
0104
0105 const auto& outsideLink = volumeLinks[outside];
0106
0107
0108 const auto* instance = outsideLink.instance();
0109
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
0119
0120 if (singleLink != nullptr) {
0121
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
0129 auto multiLink1D =
0130 dynamic_cast<const Experimental::BoundVolumesGrid1Impl*>(instance);
0131 if (multiLink1D != nullptr) {
0132
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
0140 ActsScalar gCorr = VectorHelpers::cast(transform.translation(), cast);
0141 std::for_each(boundaries.begin(), boundaries.end(),
0142 [&gCorr](ActsScalar& b) { b -= gCorr; });
0143
0144
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
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
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
0191
0192
0193 if (surfaceType == Surface::SurfaceType::Cylinder) {
0194 for (auto [ib, b] : enumerate(clippedBoundaries)) {
0195 if (ib > 0) {
0196
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
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
0245
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
0263 auto singleLink =
0264 dynamic_cast<const Experimental::SingleDetectorVolumeImpl*>(instance);
0265
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
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
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
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
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 }