Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CsvTrackingGeometryWriter.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file CsvTrackingGeometryWriter.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2017 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 
20 #include "Acts/Geometry/Layer.hpp"
30 #include "Acts/Utilities/IAxis.hpp"
34 
35 #include <array>
36 #include <cstddef>
37 #include <stdexcept>
38 #include <utility>
39 #include <vector>
40 
41 #include <dfe/dfe_io_dsv.hpp>
42 
43 #include "CsvOutputData.hpp"
44 
45 using namespace ActsExamples;
46 
49  : m_cfg(config),
50  m_logger(Acts::getDefaultLogger("CsvTrackingGeometryWriter", level))
51 
52 {
53  if (not m_cfg.trackingGeometry) {
54  throw std::invalid_argument("Missing tracking geometry");
55  }
56  m_world = m_cfg.trackingGeometry->highestTrackingVolume();
57  if (m_world == nullptr) {
58  throw std::invalid_argument("Could not identify the world volume");
59  }
60 }
61 
63  return "CsvTrackingGeometryWriter";
64 }
65 
66 namespace {
67 
68 using SurfaceWriter = dfe::NamedTupleCsvWriter<SurfaceData>;
69 using SurfaceGridWriter = dfe::NamedTupleCsvWriter<SurfaceGridData>;
70 using LayerVolumeWriter = dfe::NamedTupleCsvWriter<LayerVolumeData>;
72 
74 void fillSurfaceData(SurfaceData& data, const Acts::Surface& surface,
75  const Acts::GeometryContext& geoCtx) noexcept(false) {
76  // encoded and partially decoded geometry identifier
77  data.geometry_id = surface.geometryId().value();
78  data.volume_id = surface.geometryId().volume();
79  data.boundary_id = surface.geometryId().boundary();
80  data.layer_id = surface.geometryId().layer();
81  data.module_id = surface.geometryId().sensitive();
82  // center position
83  auto center = surface.center(geoCtx);
84  data.cx = center.x() / Acts::UnitConstants::mm;
85  data.cy = center.y() / Acts::UnitConstants::mm;
86  data.cz = center.z() / Acts::UnitConstants::mm;
87  // rotation matrix components are unit-less
88  auto transform = surface.transform(geoCtx);
89  data.rot_xu = transform(0, 0);
90  data.rot_xv = transform(0, 1);
91  data.rot_xw = transform(0, 2);
92  data.rot_yu = transform(1, 0);
93  data.rot_yv = transform(1, 1);
94  data.rot_yw = transform(1, 2);
95  data.rot_zu = transform(2, 0);
96  data.rot_zv = transform(2, 1);
97  data.rot_zw = transform(2, 2);
98 
99  std::array<float*, 7> dataBoundParameters = {
100  &data.bound_param0, &data.bound_param1, &data.bound_param2,
101  &data.bound_param3, &data.bound_param4, &data.bound_param5,
102  &data.bound_param6};
103 
104  const auto& bounds = surface.bounds();
105  data.bounds_type = static_cast<int>(bounds.type());
106  auto boundValues = bounds.values();
107 
108  if (boundValues.size() > dataBoundParameters.size()) {
109  throw std::invalid_argument(
110  "Bound types with too many parameters. Should never happen.");
111  }
112 
113  for (size_t ipar = 0; ipar < boundValues.size(); ++ipar) {
114  (*dataBoundParameters[ipar]) = boundValues[ipar];
115  }
116 
117  if (surface.associatedDetectorElement() != nullptr) {
118  data.module_t = surface.associatedDetectorElement()->thickness() /
120 
121  const auto* detElement =
122  dynamic_cast<const Acts::IdentifiedDetectorElement*>(
123  surface.associatedDetectorElement());
124 
125  if (detElement != nullptr and detElement->digitizationModule()) {
126  auto dModule = detElement->digitizationModule();
127  // dynamic_cast to CartesianSegmentation
128  const auto* cSegmentation =
129  dynamic_cast<const Acts::CartesianSegmentation*>(
130  &(dModule->segmentation()));
131  if (cSegmentation != nullptr) {
132  auto pitch = cSegmentation->pitch();
133  data.pitch_u = pitch.first / Acts::UnitConstants::mm;
134  data.pitch_v = pitch.second / Acts::UnitConstants::mm;
135  }
136  }
137  }
138 }
139 
141 void writeSurface(SurfaceWriter& sfWriter, const Acts::Surface& surface,
142  const Acts::GeometryContext& geoCtx) {
144  fillSurfaceData(data, surface, geoCtx);
145  sfWriter.append(data);
146 }
147 
154 void writeCylinderLayerVolume(
155  LayerVolumeWriter& lvWriter, const Acts::Layer& lv,
157  std::vector<Acts::ActsScalar>& representingBoundValues,
158  std::vector<Acts::ActsScalar>& volumeBoundValues,
159  std::vector<Acts::ActsScalar>& lastBoundValues, bool last) {
160  // The layer volume to be written
161  LayerVolumeData lvDims;
162  lvDims.geometry_id = lv.geometryId().value();
163  lvDims.volume_id = lv.geometryId().volume();
164  lvDims.layer_id = lv.geometryId().layer();
165  bool isCylinderLayer = (lv.surfaceRepresentation().bounds().type() ==
167 
168  auto lTranslation = transform.translation();
169  // Change volume Bound values to r min, r max, z min, z max, phi min,
170  // phi max
171  representingBoundValues = {
172  representingBoundValues[0],
173  representingBoundValues[1],
174  lTranslation.z() - representingBoundValues[2],
175  lTranslation.z() + representingBoundValues[2],
176  representingBoundValues[4] - representingBoundValues[3],
177  representingBoundValues[4] + representingBoundValues[3]};
178 
179  // Synchronize
180  lvDims.min_v0 =
181  isCylinderLayer ? representingBoundValues[0] : volumeBoundValues[0];
182  lvDims.max_v0 =
183  isCylinderLayer ? representingBoundValues[1] : volumeBoundValues[1];
184  lvDims.min_v1 =
185  isCylinderLayer ? volumeBoundValues[2] : representingBoundValues[2];
186  lvDims.max_v1 =
187  isCylinderLayer ? volumeBoundValues[3] : representingBoundValues[3];
188  lvDims.min_v2 = representingBoundValues[4];
189  lvDims.max_v2 = representingBoundValues[5];
190 
191  // Write the prior navigation layer
192  LayerVolumeData nlvDims;
193  nlvDims.geometry_id = lv.geometryId().value();
194  nlvDims.volume_id = lv.geometryId().volume();
195  nlvDims.layer_id = lv.geometryId().layer() - 1;
196  if (isCylinderLayer) {
197  nlvDims.min_v0 = lastBoundValues[0];
198  nlvDims.max_v0 = representingBoundValues[0];
199  nlvDims.min_v1 = lastBoundValues[2];
200  nlvDims.max_v1 = lastBoundValues[3];
201  // Reset the r min boundary
202  lastBoundValues[0] = representingBoundValues[1];
203  } else {
204  nlvDims.min_v0 = lastBoundValues[0];
205  nlvDims.max_v0 = lastBoundValues[1];
206  nlvDims.min_v1 = lastBoundValues[2];
207  nlvDims.max_v1 = representingBoundValues[2];
208  // Reset the r min boundary
209  lastBoundValues[2] = representingBoundValues[3];
210  }
211  nlvDims.min_v2 = representingBoundValues[4];
212  nlvDims.max_v2 = representingBoundValues[5];
213  lvWriter.append(nlvDims);
214  // Write the volume dimensions for the sensitive layer
215  lvWriter.append(lvDims);
216 
217  // Write last if needed
218  if (last) {
219  // Write the last navigation layer volume
220  LayerVolumeData llvDims;
221  llvDims.geometry_id = lv.geometryId().value();
222  llvDims.volume_id = lv.geometryId().volume();
223  llvDims.layer_id = lv.geometryId().layer() + 1;
224  llvDims.min_v0 = lastBoundValues[0];
225  llvDims.max_v0 = lastBoundValues[1];
226  llvDims.min_v1 = lastBoundValues[2];
227  llvDims.max_v1 = lastBoundValues[3];
228  llvDims.min_v2 = representingBoundValues[4];
229  llvDims.max_v2 = representingBoundValues[5];
230  // Close up volume
231  lvWriter.append(llvDims);
232  }
233 }
234 
236 void writeBoundarySurface(SurfaceWriter& writer,
237  const BoundarySurface& bsurface,
238  const Acts::GeometryContext& geoCtx) {
240  fillSurfaceData(data, bsurface.surfaceRepresentation(), geoCtx);
241  writer.append(data);
242 }
243 
245 void writeVolume(SurfaceWriter& sfWriter, SurfaceGridWriter& sfGridWriter,
246  LayerVolumeWriter& lvWriter,
247  const Acts::TrackingVolume& volume, bool writeSensitive,
248  bool writeBoundary, bool writeSurfaceGrid,
249  bool writeLayerVolume, const Acts::GeometryContext& geoCtx) {
250  // process all layers that are directly stored within this volume
251  if (volume.confinedLayers() != nullptr) {
252  const auto& vTransform = volume.transform();
253 
254  // Get the values of the volume boundaries
255  std::vector<Acts::ActsScalar> volumeBoundValues =
256  volume.volumeBounds().values();
257  std::vector<Acts::ActsScalar> lastBoundValues;
258 
259  if (volume.volumeBounds().type() == Acts::VolumeBounds::eCylinder) {
260  auto vTranslation = vTransform.translation();
261  // values to r min, r max, z min, z max, phi min, phi max
262  volumeBoundValues = {
263  volumeBoundValues[0],
264  volumeBoundValues[1],
265  vTranslation.z() - volumeBoundValues[2],
266  vTranslation.z() + volumeBoundValues[2],
267  volumeBoundValues[4] - volumeBoundValues[3],
268  volumeBoundValues[4] + volumeBoundValues[3],
269  };
270  lastBoundValues = volumeBoundValues;
271  }
272 
273  unsigned int layerIdx = 0;
274  const auto& layers = volume.confinedLayers()->arrayObjects();
275 
276  // If we only have three layers, then the volume is the layer volume
277  // so let's write it - this case will be excluded afterwards
278  if (layers.size() == 3 and writeLayerVolume) {
279  auto slayer = layers[1];
280  LayerVolumeData plvDims;
281  plvDims.geometry_id = slayer->geometryId().value();
282  plvDims.volume_id = slayer->geometryId().volume();
283  plvDims.layer_id = slayer->geometryId().layer();
284  plvDims.min_v0 = volumeBoundValues[0];
285  plvDims.max_v0 = volumeBoundValues[1];
286  plvDims.min_v1 = volumeBoundValues[2];
287  plvDims.max_v1 = volumeBoundValues[3];
288  lvWriter.append(plvDims);
289  }
290 
291  // Now loop over the layer and write them
292  for (const auto& layer : layers) {
293  // We skip over navigation layers for layer volume writing
294  // they will be written with the sensitive/passive parts for
295  // synchronization
296  if (layer->layerType() == Acts::navigation) {
297  ++layerIdx;
298  // For a correct layer volume setup, we need the navigation layers
299  if (writeLayerVolume) {
300  writeSurface(sfWriter, layer->surfaceRepresentation(), geoCtx);
301  }
302  continue;
303 
304  } else {
305  // Get the representing volume
306  const auto* rVolume = layer->representingVolume();
307 
308  // Write the layer volume, exclude single layer volumes (written above)
309  if (rVolume != nullptr and writeLayerVolume and layers.size() > 3) {
310  // Get the values of the representing volume
311  std::vector<Acts::ActsScalar> representingBoundValues =
312  rVolume->volumeBounds().values();
313  if (rVolume->volumeBounds().type() == Acts::VolumeBounds::eCylinder) {
314  bool last = (layerIdx + 2 ==
315  volume.confinedLayers()->arrayObjects().size());
316  writeCylinderLayerVolume(lvWriter, *layer, rVolume->transform(),
317  representingBoundValues, volumeBoundValues,
318  lastBoundValues, last);
319  }
320  }
321 
322  // Surface has sub surfaces
323  if (layer->surfaceArray() != nullptr) {
324  auto sfArray = layer->surfaceArray();
325 
326  // Write the surface grid itself if configured
327  if (writeSurfaceGrid) {
328  SurfaceGridData sfGrid;
329  sfGrid.geometry_id = layer->geometryId().value();
330  sfGrid.volume_id = layer->geometryId().volume();
331  sfGrid.layer_id = layer->geometryId().layer();
332 
333  // Draw the grid itself
334  auto binning = sfArray->binningValues();
335  auto axes = sfArray->getAxes();
336  if (not binning.empty() and binning.size() == 2 and
337  axes.size() == 2) {
338  auto loc0Values = axes[0]->getBinEdges();
339  sfGrid.nbins_loc0 = loc0Values.size() - 1;
340  sfGrid.type_loc0 = int(binning[0]);
341  sfGrid.min_loc0 = loc0Values[0];
342  sfGrid.max_loc0 = loc0Values[loc0Values.size() - 1];
343 
344  auto loc1Values = axes[1]->getBinEdges();
345  sfGrid.nbins_loc1 = loc1Values.size() - 1;
346  sfGrid.type_loc1 = int(binning[1]);
347  sfGrid.min_loc1 = loc1Values[0];
348  sfGrid.max_loc1 = loc1Values[loc1Values.size() - 1];
349  }
350  sfGridWriter.append(sfGrid);
351  }
352 
353  // Write the sensitive surface if configured
354  if (writeSensitive) {
355  for (auto surface : sfArray->surfaces()) {
356  if (surface != nullptr) {
357  writeSurface(sfWriter, *surface, geoCtx);
358  }
359  }
360  }
361  } else {
362  // Write the passive surface
363  writeSurface(sfWriter, layer->surfaceRepresentation(), geoCtx);
364  }
365  }
366  ++layerIdx;
367  } // end of layer loop
368 
369  // This is a navigation volume, write the boundaries
370  if (writeBoundary) {
371  for (const auto& bsurface : volume.boundarySurfaces()) {
372  writeBoundarySurface(sfWriter, *bsurface, geoCtx);
373  }
374  }
375  }
376  // step down into hierarchy to process all child volumnes
377  if (volume.confinedVolumes()) {
378  for (const auto& confined : volume.confinedVolumes()->arrayObjects()) {
379  writeVolume(sfWriter, sfGridWriter, lvWriter, *confined.get(),
380  writeSensitive, writeBoundary, writeSurfaceGrid,
381  writeLayerVolume, geoCtx);
382  }
383  }
384 }
385 } // namespace
386 
388  if (not m_cfg.writePerEvent) {
389  return ProcessCode::SUCCESS;
390  }
391 
392  SurfaceWriter sfWriter(
393  perEventFilepath(m_cfg.outputDir, "detectors.csv", ctx.eventNumber),
395 
396  SurfaceGridWriter sfGridWriter(
397  perEventFilepath(m_cfg.outputDir, "surface-grids.csv", ctx.eventNumber),
399 
400  LayerVolumeWriter lvWriter(
401  perEventFilepath(m_cfg.outputDir, "layer-volumes.csv", ctx.eventNumber),
403 
404  writeVolume(sfWriter, sfGridWriter, lvWriter, *m_world, m_cfg.writeSensitive,
407  return ProcessCode::SUCCESS;
408 }
409 
411  SurfaceWriter sfWriter(joinPaths(m_cfg.outputDir, "detectors.csv"),
413  SurfaceGridWriter sfGridWriter(
414  joinPaths(m_cfg.outputDir, "surface-grids.csv"), m_cfg.outputPrecision);
415 
416  LayerVolumeWriter lvWriter(joinPaths(m_cfg.outputDir, "layer-volumes.csv"),
418 
419  writeVolume(sfWriter, sfGridWriter, lvWriter, *m_world, m_cfg.writeSensitive,
422  return ProcessCode::SUCCESS;
423 }