Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PortalJsonConverter.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PortalJsonConverter.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2023 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
12 #include "Acts/Detector/Portal.hpp"
24 
25 #include <algorithm>
26 #include <iterator>
27 #include <vector>
28 
29 namespace {
30 
37 int findVolume(
39  const std::vector<const Acts::Experimental::DetectorVolume*>& volumes) {
40  auto candidate = std::find(volumes.begin(), volumes.end(), volume);
41  if (candidate != volumes.end()) {
42  return std::distance(volumes.begin(), candidate);
43  }
44  return -1;
45 }
46 } // namespace
47 
49  const GeometryContext& gctx, const Experimental::Portal& portal,
50  const std::vector<const Experimental::DetectorVolume*>& detectorVolumes,
51  const Options& options) {
52  // The overall return object
53  nlohmann::json jPortal;
54  // Write the surface
55  jPortal["surface"] = SurfaceJsonConverter::toJson(gctx, portal.surface(),
56  options.surfaceOptions);
57  // And the portal specific information
58  const auto& volumeLinks = portal.detectorVolumeUpdators();
59  nlohmann::json jLinks;
60  for (const auto& vLink : volumeLinks) {
61  nlohmann::json jLink = toJson(vLink, detectorVolumes);
62  jLinks.push_back(jLink);
63  }
64  jPortal["volume_links"] = jLinks;
65  // Return the full json object
66  return jPortal;
67 }
68 
69 std::vector<nlohmann::json> Acts::PortalJsonConverter::toJsonDetray(
70  const GeometryContext& gctx, const Experimental::Portal& portal,
71  std::size_t ip, const Experimental::DetectorVolume& volume,
72  const OrientedSurfaces& orientedSurfaces,
73  const std::vector<const Experimental::DetectorVolume*>& detectorVolumes,
74  const Options& option) {
75  // The overall return object
76  std::vector<nlohmann::json> jPortals = {};
77  const Surface& surface = portal.surface();
78  const auto& volumeLinks = portal.detectorVolumeUpdators();
79 
80  // First assumption for outside link (along direction)
81  std::size_t outside = 1u;
82 
83  // Find out if you need to take the outside or inside volume
84  // for planar surfaces that's easy
85  if (surface.type() != Acts::Surface::SurfaceType::Cylinder) {
86  // Get the two volume center
87  const auto volumeCenter = volume.transform(gctx).translation();
88  const auto surfaceCenter = surface.center(gctx);
89  const auto surfaceNormal = surface.normal(gctx);
90  // Get the direction from the volume to the surface, correct link
91  const auto volumeToSurface = surfaceCenter - volumeCenter;
92  if (volumeToSurface.dot(surfaceNormal) < 0) {
93  outside = 0u;
94  }
95  } else {
96  // This is a cylinder portal, inner cover reverses the normal
97  if (ip == 3u) {
98  outside = 0u;
99  }
100  }
101 
102  const auto& outsideLink = volumeLinks[outside];
103  // Grab the corresponding volume link
104  // If it is a single link, we are done
105  const auto* instance = outsideLink.instance();
106  // Single link cast
107  auto singleLink =
109  instance);
110 
111  auto [surfaceAdjusted, insidePointer] = orientedSurfaces[ip];
112 
113  SurfaceJsonConverter::Options surfaceOptions = option.surfaceOptions;
114  surfaceOptions.portal = true;
115  // Single link detected - just write it out, we use the oriented surface
116  // in order to make sure the size is adjusted
117  if (singleLink != nullptr) {
118  // Single link can be written out
119  std::size_t vLink = findVolume(singleLink->dVolume, detectorVolumes);
120  auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *surfaceAdjusted,
121  surfaceOptions);
122  DetrayJsonHelper::addVolumeLink(jPortal["mask"], vLink);
123  jPortals.push_back(jPortal);
124  } else {
125  // Multi link detected - 1D
126  auto multiLink1D =
127  dynamic_cast<const Experimental::BoundVolumesGrid1Impl*>(instance);
128  if (multiLink1D != nullptr) {
129  // Resolve the multi link 1D
130  auto boundaries =
131  multiLink1D->indexedUpdator.grid.axes()[0u]->getBinEdges();
132  const auto& cast = multiLink1D->indexedUpdator.casts[0u];
133  const auto& transform = multiLink1D->indexedUpdator.transform;
134  const auto& volumes = multiLink1D->indexedUpdator.extractor.dVolumes;
135  if (not transform.isApprox(Transform3::Identity())) {
136  std::runtime_error(
137  "PortalJsonConverter: transformed boundary link implementation not "
138  "(yet) supported");
139  }
140 
141  // Get the volume indices
142  auto surfaceType = surfaceAdjusted->type();
143  std::vector<unsigned int> vIndices = {};
144  for (const auto& v : volumes) {
145  vIndices.push_back(findVolume(v, detectorVolumes));
146  }
147 
148  // Pick the surface dimension - via poly
149  std::array<ActsScalar, 2u> clipRange = {0., 0.};
150  std::vector<ActsScalar> boundValues = surfaceAdjusted->bounds().values();
151  if (surfaceType == Surface::SurfaceType::Cylinder and cast == binZ) {
152  ActsScalar zPosition = surfaceAdjusted->center(gctx).z();
153  clipRange = {
154  zPosition - boundValues[CylinderBounds::BoundValues::eHalfLengthZ],
155  zPosition + boundValues[CylinderBounds::BoundValues::eHalfLengthZ]};
156  } else if (surfaceType == Surface::SurfaceType::Disc and cast == binR) {
157  clipRange = {boundValues[RadialBounds::BoundValues::eMinR],
158  boundValues[RadialBounds::BoundValues::eMaxR]};
159  } else {
160  throw std::runtime_error(
161  "PortalJsonConverter: surface type not (yet) supported for detray "
162  "conversion, only cylinder and disc are currently supported.");
163  }
164 
165  // Need to clip the parameter space to the surface dimension
166  std::vector<unsigned int> clippedIndices = {};
167  std::vector<ActsScalar> clippedBoundaries = {};
168  clippedBoundaries.push_back(clipRange[0u]);
169  for (const auto [ib, b] : enumerate(boundaries)) {
170  if (ib > 0) {
171  unsigned int vI = vIndices[ib - 1u];
172  ActsScalar highEdge = boundaries[ib];
173  if (boundaries[ib - 1] >= clipRange[1u]) {
174  break;
175  }
176  if (highEdge <= clipRange[0u]) {
177  continue;
178  }
179  if (highEdge > clipRange[1u]) {
180  highEdge = clipRange[1u];
181  }
182  clippedIndices.push_back(vI);
183  clippedBoundaries.push_back(highEdge);
184  }
185  }
186 
187  // Interpret the clipped information
188  //
189  // Clipped cylinder case
190  if (surfaceType == Surface::SurfaceType::Cylinder) {
191  for (auto [ib, b] : enumerate(clippedBoundaries)) {
192  if (ib > 0) {
193  // Create sub surfaces
194  std::array<ActsScalar, CylinderBounds::BoundValues::eSize>
195  subBoundValues = {};
196  for (auto [ibv, bv] : enumerate(boundValues)) {
197  subBoundValues[ibv] = bv;
198  }
199  subBoundValues[CylinderBounds::BoundValues::eHalfLengthZ] =
200  0.5 * (b - clippedBoundaries[ib - 1u]);
201  auto subBounds = std::make_shared<CylinderBounds>(subBoundValues);
202  auto subTransform = Transform3::Identity();
203  subTransform.pretranslate(Vector3(
204  0., 0.,
205  clippedBoundaries[ib - 1u] +
206  subBoundValues[CylinderBounds::BoundValues::eHalfLengthZ]));
207  auto subSurface =
208  Surface::makeShared<CylinderSurface>(subTransform, subBounds);
209  auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *subSurface,
210  surfaceOptions);
211  DetrayJsonHelper::addVolumeLink(jPortal["mask"],
212  clippedIndices[ib - 1u]);
213  jPortals.push_back(jPortal);
214  }
215  }
216  } else {
217  for (auto [ib, b] : enumerate(clippedBoundaries)) {
218  if (ib > 0) {
219  // Create sub surfaces
220  std::array<ActsScalar, RadialBounds::BoundValues::eSize>
221  subBoundValues = {};
222  for (auto [ibv, bv] : enumerate(boundValues)) {
223  subBoundValues[ibv] = bv;
224  }
225  subBoundValues[RadialBounds::BoundValues::eMinR] =
226  clippedBoundaries[ib - 1u];
227  subBoundValues[RadialBounds::BoundValues::eMaxR] = b;
228  auto subBounds = std::make_shared<RadialBounds>(subBoundValues);
229  auto subSurface = Surface::makeShared<DiscSurface>(
230  portal.surface().transform(gctx), subBounds);
231  auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *subSurface,
232  surfaceOptions);
233  DetrayJsonHelper::addVolumeLink(jPortal["mask"],
234  clippedIndices[ib - 1u]);
235  jPortals.push_back(jPortal);
236  }
237  }
238  }
239 
240  } else {
241  // End of world
242  // Write surface with invalid link
243  auto jPortal = SurfaceJsonConverter::toJsonDetray(gctx, *surfaceAdjusted,
244  surfaceOptions);
246  jPortal["mask"], std::numeric_limits<std::uint_least16_t>::max());
247  jPortals.push_back(jPortal);
248  }
249  }
250  return jPortals;
251 }
252 
255  const std::vector<const Experimental::DetectorVolume*>& detectorVolumes) {
256  nlohmann::json jLink;
257  if (updator.connected()) {
258  const auto instance = updator.instance();
259  // Single link cast
260  auto singleLink =
261  dynamic_cast<const Experimental::SingleDetectorVolumeImpl*>(instance);
262  // Single link detected
263  if (singleLink != nullptr) {
264  auto vIndex = findVolume(singleLink->dVolume, detectorVolumes);
265  if (vIndex < 0) {
266  throw std::runtime_error(
267  "PortalJsonConverter: volume not found in the list of volumes.");
268  }
269  jLink["single"] = vIndex;
270  }
271  // Multi link detected - 1D
272  auto multiLink1D =
273  dynamic_cast<const Experimental::BoundVolumesGrid1Impl*>(instance);
274  if (multiLink1D != nullptr) {
275  nlohmann::json jMultiLink;
276  const auto& volumes = multiLink1D->indexedUpdator.extractor.dVolumes;
277  const auto& casts = multiLink1D->indexedUpdator.casts;
278  nlohmann::json jTransform = Transform3JsonConverter::toJson(
279  multiLink1D->indexedUpdator.transform);
280  std::vector<unsigned int> vIndices = {};
281  for (const auto& v : volumes) {
282  vIndices.push_back(findVolume(v, detectorVolumes));
283  }
284  jMultiLink["boundaries"] =
285  multiLink1D->indexedUpdator.grid.axes()[0u]->getBinEdges();
286  jMultiLink["binning"] = casts[0u];
287  jMultiLink["targets"] = vIndices;
288  jMultiLink["transform"] = jTransform;
289  jLink["multi_1D"] = jMultiLink;
290  }
291  }
292  return jLink;
293 }
294 
295 std::shared_ptr<Acts::Experimental::Portal> Acts::PortalJsonConverter::fromJson(
296  const GeometryContext& gctx, const nlohmann::json& jPortal,
297  const std::vector<std::shared_ptr<Experimental::DetectorVolume>>&
298  detectorVolumes) {
299  // The surface re-creation is trivial
300  auto surface = SurfaceJsonConverter::fromJson(jPortal["surface"]);
301  auto portal = Experimental::Portal::makeShared(surface);
302 
303  std::array<Acts::Direction, 2> normalDirs = {Direction::Backward,
304  Direction::Forward};
305  // re-create the volume links
306  auto jLinks = jPortal["volume_links"];
307  for (auto [ivl, vl] : enumerate(jLinks)) {
308  if (vl.contains("single")) {
309  const auto vIndex = vl["single"].get<unsigned int>();
311  *portal, detectorVolumes[vIndex], normalDirs[ivl]);
312  } else if (vl.contains("multi_1D")) {
313  // Resolve the multi link 1D
314  auto jMultiLink = vl["multi_1D"];
315  auto boundaries = jMultiLink["boundaries"].get<std::vector<double>>();
316  auto binning = jMultiLink["binning"].get<BinningValue>();
317  auto targets = jMultiLink["targets"].get<std::vector<unsigned int>>();
318  std::vector<std::shared_ptr<Experimental::DetectorVolume>> targetVolumes;
319  for (const auto t : targets) {
320  targetVolumes.push_back(detectorVolumes[t]);
321  }
323  gctx, *portal, targetVolumes, normalDirs[ivl], boundaries, binning);
324  }
325  }
326 
327  return portal;
328 }