Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Geant4Converters.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Geant4Converters.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2022 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 
25 
26 #include <algorithm>
27 #include <cmath>
28 #include <cstdlib>
29 #include <iterator>
30 #include <stdexcept>
31 #include <utility>
32 #include <vector>
33 
34 #include "G4Box.hh"
35 #include "G4LogicalVolume.hh"
36 #include "G4Material.hh"
37 #include "G4Trap.hh"
38 #include "G4Trd.hh"
39 #include "G4Tubs.hh"
40 #include "G4VPhysicalVolume.hh"
41 #include "G4VSolid.hh"
42 
44  const G4ThreeVector& g4Trans) {
45  Transform3 gTransform = Transform3::Identity();
46  Vector3 scaledTrans =
47  Vector3(scale * g4Trans[0], scale * g4Trans[1], scale * g4Trans[2]);
48  gTransform.pretranslate(scaledTrans);
49  return gTransform;
50 }
51 
53  const G4RotationMatrix& g4Rot, const G4ThreeVector& g4Trans) {
54  // Create the translation
55  Vector3 translation(scale * g4Trans[0], scale * g4Trans[1],
56  scale * g4Trans[2]);
57  // And the rotation to it
58  RotationMatrix3 rotation;
59  rotation << g4Rot.xx(), g4Rot.yx(), g4Rot.zx(), g4Rot.xy(), g4Rot.yy(),
60  g4Rot.zy(), g4Rot.xz(), g4Rot.yz(), g4Rot.zz();
61  Transform3 transform = Transform3::Identity();
62  transform.matrix().block(0, 0, 3, 1) = rotation.col(0);
63  transform.matrix().block(0, 1, 3, 1) = rotation.col(1);
64  transform.matrix().block(0, 2, 3, 1) = rotation.col(2);
65  transform.matrix().block(0, 3, 3, 1) = translation;
66  return transform;
67 }
68 
70  const G4Transform3D& g4Trf) {
71  auto g4Rot = g4Trf.getRotation();
72  auto g4Trans = g4Trf.getTranslation();
73  return transform(g4Rot, g4Trans);
74 }
75 
77  const G4VPhysicalVolume& g4PhysVol) {
78  // Get Rotation and translation
79  auto g4Translation = g4PhysVol.GetTranslation();
80  auto g4Rotation = g4PhysVol.GetRotation();
81 
82  G4Transform3D g4Transform =
83  (g4Rotation == nullptr)
84  ? G4Transform3D(CLHEP::HepRotation(), g4Translation)
85  : G4Transform3D(*g4Rotation, g4Translation);
86 
87  return transform(g4Transform);
88 }
89 
90 std::tuple<std::shared_ptr<Acts::CylinderBounds>, Acts::ActsScalar>
92  using B = Acts::CylinderBounds;
93 
94  std::array<Acts::ActsScalar, B::eSize> tArray = {};
95  tArray[B::eR] = static_cast<ActsScalar>(g4Tubs.GetInnerRadius() +
96  g4Tubs.GetOuterRadius()) *
97  0.5;
98  tArray[B::eHalfLengthZ] = static_cast<ActsScalar>(g4Tubs.GetZHalfLength());
99  tArray[B::eHalfPhiSector] =
100  0.5 * static_cast<ActsScalar>(g4Tubs.GetDeltaPhiAngle());
101  // Geant fiddles around with user given values, i.e. it would not
102  // allow [-M_PI, +M_PI) as a full segment (has to be [0, 2PI)])
103  if (std::abs(tArray[B::eHalfPhiSector] - M_PI) <
104  std::numeric_limits<ActsScalar>::epsilon()) {
105  tArray[B::eAveragePhi] = 0.;
106  } else {
107  tArray[B::eAveragePhi] =
108  static_cast<ActsScalar>(g4Tubs.GetStartPhiAngle()) +
109  tArray[B::eHalfPhiSector];
110  }
111  ActsScalar thickness = g4Tubs.GetOuterRadius() - g4Tubs.GetInnerRadius();
112  auto cBounds = std::make_shared<CylinderBounds>(tArray);
113  return std::make_tuple(std::move(cBounds), thickness);
114 }
115 
116 std::tuple<std::shared_ptr<Acts::RadialBounds>, Acts::ActsScalar>
118  using B = Acts::RadialBounds;
119 
120  std::array<ActsScalar, B::eSize> tArray = {};
121  tArray[B::eMinR] = static_cast<ActsScalar>(g4Tubs.GetInnerRadius());
122  tArray[B::eMaxR] = static_cast<ActsScalar>(g4Tubs.GetOuterRadius());
123  tArray[B::eHalfPhiSector] =
124  0.5 * static_cast<ActsScalar>(g4Tubs.GetDeltaPhiAngle());
125  // Geant fiddles around with user given values, i.e. it would not
126  // allow [-M_PI, +M_PI) as a full segment (has to be [0, 2PI)])
127  if (std::abs(tArray[B::eHalfPhiSector] - M_PI) <
128  std::numeric_limits<ActsScalar>::epsilon()) {
129  tArray[B::eAveragePhi] = 0.;
130  } else {
131  tArray[B::eAveragePhi] =
132  static_cast<ActsScalar>(g4Tubs.GetStartPhiAngle()) +
133  tArray[B::eHalfPhiSector];
134  }
135  ActsScalar thickness = g4Tubs.GetZHalfLength() * 2;
136  auto rBounds = std::make_shared<RadialBounds>(tArray);
137  return std::make_tuple(std::move(rBounds), thickness);
138 }
139 
140 std::shared_ptr<Acts::LineBounds> Acts::Geant4ShapeConverter::lineBounds(
141  const G4Tubs& g4Tubs) {
142  auto r = static_cast<ActsScalar>(g4Tubs.GetOuterRadius());
143  auto hlZ = static_cast<ActsScalar>(g4Tubs.GetZHalfLength());
144  return std::make_shared<LineBounds>(r, hlZ);
145 }
146 
147 std::tuple<std::shared_ptr<Acts::RectangleBounds>, std::array<int, 2u>,
150  std::vector<ActsScalar> hG4XYZ = {
151  static_cast<ActsScalar>(g4Box.GetXHalfLength()),
152  static_cast<ActsScalar>(g4Box.GetYHalfLength()),
153  static_cast<ActsScalar>(g4Box.GetZHalfLength())};
154 
155  auto minAt = std::min_element(hG4XYZ.begin(), hG4XYZ.end());
156  std::size_t minPos = std::distance(hG4XYZ.begin(), minAt);
157  ActsScalar thickness = 2. * hG4XYZ[minPos];
158 
159  std::array<int, 2u> rAxes = {};
160  switch (minPos) {
161  case 0: {
162  rAxes = {1, 2};
163  } break;
164  case 1: {
165  if (keepAxisOrder) {
166  rAxes = {0, -2}; // flip for right-handed
167  } else {
168  rAxes = {2, 0}; // cyclic positive
169  }
170  } break;
171  case 2: {
172  rAxes = {0, 1};
173  } break;
174  }
175  auto rBounds = std::make_shared<RectangleBounds>(hG4XYZ[std::abs(rAxes[0u])],
176  hG4XYZ[std::abs(rAxes[1u])]);
177  return std::make_tuple(std::move(rBounds), rAxes, thickness);
178 }
179 
180 std::tuple<std::shared_ptr<Acts::TrapezoidBounds>, std::array<int, 2u>,
183  // primary parameters
184  ActsScalar hlX0 = static_cast<ActsScalar>(g4Trd.GetXHalfLength1());
185  ActsScalar hlX1 = static_cast<ActsScalar>(g4Trd.GetXHalfLength2());
186  ActsScalar hlY0 = static_cast<ActsScalar>(g4Trd.GetYHalfLength1());
187  ActsScalar hlY1 = static_cast<ActsScalar>(g4Trd.GetYHalfLength2());
188  ActsScalar hlZ = static_cast<ActsScalar>(g4Trd.GetZHalfLength());
189 
190  std::vector<ActsScalar> dXYZ = {(hlX0 + hlX1) * 0.5, (hlY0 + hlY1) * 0.5,
191  hlZ};
192 
193  auto minAt = std::min_element(dXYZ.begin(), dXYZ.end());
194  std::size_t minPos = std::distance(dXYZ.begin(), minAt);
195  ActsScalar thickness = 2. * dXYZ[minPos];
196 
197  ActsScalar halfLengthXminY = 0.;
198  ActsScalar halfLengthXmaxY = 0.;
199  ActsScalar halfLengthY = 0.;
200 
201  std::array<int, 2u> rAxes = {};
202  switch (minPos) {
203  case 0: {
204  halfLengthXminY = hlY0;
205  halfLengthXmaxY = hlY1;
206  halfLengthY = hlZ;
207  rAxes = {1, 2};
208  } break;
209  case 1: {
210  halfLengthXminY = hlX0;
211  halfLengthXmaxY = hlX1;
212  halfLengthY = hlZ;
213  rAxes = {0, -2};
214  } break;
215  case 2: {
216  if (std::abs(hlY0 - hlY1) < std::abs(hlX0 - hlX1)) {
217  halfLengthXminY = hlX0;
218  halfLengthXmaxY = hlX1;
219  halfLengthY = (hlY0 + hlY1) * 0.5;
220  rAxes = {0, 1};
221  } else {
222  halfLengthXminY = hlY0;
223  halfLengthXmaxY = hlY1;
224  halfLengthY = (hlX0 + hlX1) * 0.5;
225  rAxes = {-1, 0};
226  }
227  } break;
228  }
229 
230  auto tBounds = std::make_shared<TrapezoidBounds>(
231  halfLengthXminY, halfLengthXmaxY, halfLengthY);
232  return std::make_tuple(std::move(tBounds), rAxes, thickness);
233 }
234 
235 std::tuple<std::shared_ptr<Acts::PlanarBounds>, std::array<int, 2u>,
238  const G4Box* box = dynamic_cast<const G4Box*>(&g4Solid);
239  if (box != nullptr) {
240  auto [rBounds, axes, thickness] = rectangleBounds(*box);
241  return std::make_tuple(std::move(rBounds), axes, thickness);
242  }
243 
244  const G4Trd* trd = dynamic_cast<const G4Trd*>(&g4Solid);
245  if (trd != nullptr) {
246  auto [tBounds, axes, thickness] = trapezoidBounds(*trd);
247  return std::make_tuple(std::move(tBounds), axes, thickness);
248  }
249 
250  std::shared_ptr<Acts::PlanarBounds> pBounds = nullptr;
251  std::array<int, 2u> rAxes = {};
252  ActsScalar rThickness = 0.;
253  return std::make_tuple(std::move(pBounds), rAxes, rThickness);
254 }
255 
256 namespace {
257 Acts::Transform3 axesOriented(const Acts::Transform3& toGlobalOriginal,
258  const std::array<int, 2u>& axes) {
259  auto originalRotation = toGlobalOriginal.rotation();
260  auto colX = originalRotation.col(std::abs(axes[0u]));
261  auto colY = originalRotation.col(std::abs(axes[1u]));
262  colX *= std::copysign(1, axes[0u]);
263  colY *= std::copysign(1, axes[1u]);
264  Acts::Vector3 colZ = colX.cross(colY);
265 
266  Acts::Transform3 orientedTransform = Acts::Transform3::Identity();
267  orientedTransform.matrix().block(0, 0, 3, 1) = colX;
268  orientedTransform.matrix().block(0, 1, 3, 1) = colY;
269  orientedTransform.matrix().block(0, 2, 3, 1) = colZ;
270  orientedTransform.matrix().block(0, 3, 3, 1) = toGlobalOriginal.translation();
271 
272  return orientedTransform;
273 }
274 } // namespace
275 
276 std::shared_ptr<Acts::Surface> Acts::Geant4PhysicalVolumeConverter::surface(
277  const G4VPhysicalVolume& g4PhysVol, const Transform3& toGlobal,
278  bool convertMaterial, ActsScalar compressed) {
279  // Get the logical volume
280  auto g4LogVol = g4PhysVol.GetLogicalVolume();
281  auto g4Solid = g4LogVol->GetSolid();
282 
283  auto assignMaterial = [&](Acts::Surface& sf, ActsScalar moriginal,
284  ActsScalar mcompressed) -> void {
285  auto g4Material = g4LogVol->GetMaterial();
286  if (convertMaterial and g4Material != nullptr) {
287  if (compressed < 0.) {
288  mcompressed = moriginal;
289  }
290  auto surfaceMaterial = Geant4MaterialConverter{}.surfaceMaterial(
291  *g4Material, moriginal, mcompressed);
292  sf.assignSurfaceMaterial(std::move(surfaceMaterial));
293  }
294  };
295 
296  // Dynamic cast chain & conversion
297  std::shared_ptr<Surface> surface = nullptr;
298 
299  // Into a rectangle
300  auto g4Box = dynamic_cast<const G4Box*>(g4Solid);
301  if (g4Box != nullptr) {
302  if (forcedType == Surface::SurfaceType::Other or
303  forcedType == Surface::SurfaceType::Plane) {
304  auto [bounds, axes, original] =
306  auto orientedToGlobal = axesOriented(toGlobal, axes);
307  surface = Acts::Surface::makeShared<PlaneSurface>(orientedToGlobal,
308  std::move(bounds));
309  assignMaterial(*surface.get(), original, compressed);
310  return surface;
311  } else {
312  throw std::runtime_error("Can not convert 'G4Box' into forced shape.");
313  }
314  }
315 
316  // Into a Trapezoid
317  auto g4Trd = dynamic_cast<const G4Trd*>(g4Solid);
318  if (g4Trd != nullptr) {
319  if (forcedType == Surface::SurfaceType::Other or
320  forcedType == Surface::SurfaceType::Plane) {
321  auto [bounds, axes, original] =
323  auto orientedToGlobal = axesOriented(toGlobal, axes);
324  surface = Acts::Surface::makeShared<PlaneSurface>(orientedToGlobal,
325  std::move(bounds));
326  assignMaterial(*surface.get(), original, compressed);
327  return surface;
328  } else {
329  throw std::runtime_error("Can not convert 'G4Trd' into forced shape.");
330  }
331  }
332 
333  // Into a Cylinder, disc or line
334  auto g4Tubs = dynamic_cast<const G4Tubs*>(g4Solid);
335  if (g4Tubs != nullptr) {
336  ActsScalar diffR = g4Tubs->GetOuterRadius() - g4Tubs->GetInnerRadius();
337  ActsScalar diffZ = 2 * g4Tubs->GetZHalfLength();
338  // Detect if cylinder or disc case
339  ActsScalar original = 0.;
340  if (forcedType == Surface::SurfaceType::Cylinder or
341  (diffR < diffZ and forcedType == Surface::SurfaceType::Other)) {
342  auto [bounds, originalT] = Geant4ShapeConverter{}.cylinderBounds(*g4Tubs);
343  original = originalT;
344  surface = Acts::Surface::makeShared<CylinderSurface>(toGlobal,
345  std::move(bounds));
346  } else if (forcedType == Surface::SurfaceType::Disc or
347  forcedType == Surface::SurfaceType::Other) {
348  auto [bounds, originalT] = Geant4ShapeConverter{}.radialBounds(*g4Tubs);
349  original = originalT;
350  surface =
351  Acts::Surface::makeShared<DiscSurface>(toGlobal, std::move(bounds));
352  } else if (forcedType == Surface::SurfaceType::Straw) {
353  auto bounds = Geant4ShapeConverter{}.lineBounds(*g4Tubs);
354  surface =
355  Acts::Surface::makeShared<StrawSurface>(toGlobal, std::move(bounds));
356 
357  } else {
358  throw std::runtime_error("Can not convert 'G4Tubs' into forced shape.");
359  }
360  assignMaterial(*surface.get(), original, compressed);
361  return surface;
362  }
363 
364  return nullptr;
365 }
366 
368  const G4Material& g4Material, ActsScalar compression) {
369  auto X0 = g4Material.GetRadlen();
370  auto L0 = g4Material.GetNuclearInterLength();
371  auto Rho = g4Material.GetDensity();
372 
373  // Get{A,Z} is only meaningful for single-element materials (according to
374  // the Geant4 docs). Need to compute average manually.
375  auto g4Elements = g4Material.GetElementVector();
376  auto g4Fraction = g4Material.GetFractionVector();
377  auto g4NElements = g4Material.GetNumberOfElements();
378  double Ar = 0;
379  double Z = 0;
380  if (g4NElements == 1) {
381  Ar = g4Elements->at(0)->GetN();
382  Z = g4Material.GetZ();
383  } else {
384  for (size_t i = 0; i < g4NElements; i++) {
385  Ar += g4Elements->at(i)->GetN() * g4Fraction[i];
386  Z += g4Elements->at(i)->GetZ() * g4Fraction[i];
387  }
388  }
389 
390  return Material::fromMassDensity(X0 / compression, L0 / compression, Ar, Z,
391  compression * Rho);
392 }
393 
394 std::shared_ptr<Acts::HomogeneousSurfaceMaterial>
396  ActsScalar original,
397  ActsScalar compressed) {
398  ActsScalar compression = original / compressed;
399  return std::make_shared<HomogeneousSurfaceMaterial>(
400  MaterialSlab(material(g4Material, compression), compressed));
401 }
402 
403 std::shared_ptr<Acts::CylinderVolumeBounds>
406 
407  std::array<Acts::ActsScalar, C::eSize> tArray = {};
408  tArray[C::eMinR] = static_cast<ActsScalar>(g4Tubs.GetInnerRadius());
409  tArray[C::eMaxR] = static_cast<ActsScalar>(g4Tubs.GetOuterRadius());
410  tArray[C::eHalfLengthZ] = static_cast<ActsScalar>(g4Tubs.GetZHalfLength());
411  tArray[C::eHalfPhiSector] =
412  0.5 * static_cast<ActsScalar>(g4Tubs.GetDeltaPhiAngle());
413  tArray[C::eAveragePhi] = static_cast<ActsScalar>(g4Tubs.GetStartPhiAngle());
414 
415  return std::make_shared<CylinderVolumeBounds>(tArray);
416 }