File indexing completed on 2025-01-30 09:15:18
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/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
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::ranges::find(volumes, 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.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
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
0081 std::vector<nlohmann::json> jPortals = {};
0082 const RegularSurface& surface = portal.surface();
0083 const auto& volumeLinks = portal.portalNavigation();
0084
0085
0086 std::size_t outside = 1u;
0087
0088 std::vector<std::shared_ptr<Acts::Surface>> subSurfaces = {};
0089
0090
0091
0092 if (surface.type() != Acts::Surface::SurfaceType::Cylinder) {
0093
0094 const auto volumeCenter = volume.transform(gctx).translation();
0095 const auto surfaceCenter = surface.center(gctx);
0096 const auto surfaceNormal = surface.normal(gctx, surfaceCenter);
0097
0098 const auto volumeToSurface = surfaceCenter - volumeCenter;
0099 if (volumeToSurface.dot(surfaceNormal) < 0.) {
0100 outside = 0u;
0101 }
0102 } else {
0103
0104 if (ip == 3u) {
0105 outside = 0u;
0106 }
0107 }
0108
0109 const auto& outsideLink = volumeLinks[outside];
0110
0111
0112 const auto* instance = outsideLink.instance();
0113
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
0123
0124 if (singleLink != nullptr) {
0125
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
0133 auto multiLink1D =
0134 dynamic_cast<const Experimental::BoundVolumesGrid1Navigation*>(
0135 instance);
0136 if (multiLink1D != nullptr) {
0137
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
0145 double gCorr = VectorHelpers::cast(transform.translation(), cast);
0146 std::for_each(boundaries.begin(), boundaries.end(),
0147 [&gCorr](double& b) { b -= gCorr; });
0148
0149
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
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
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
0198
0199
0200 if (surfaceType == Surface::SurfaceType::Cylinder) {
0201 for (auto [ib, b] : enumerate(clippedBoundaries)) {
0202 if (ib > 0) {
0203
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
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
0254
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
0272 auto singleLink =
0273 dynamic_cast<const Experimental::SingleDetectorVolumeNavigation*>(
0274 instance);
0275
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
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
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
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
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 }