File indexing completed on 2026-04-07 07:46:30
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/Csv/CsvTrackingGeometryWriter.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Units.hpp"
0013 #include "Acts/Geometry/BoundarySurfaceT.hpp"
0014 #include "Acts/Geometry/GeometryContext.hpp"
0015 #include "Acts/Geometry/GeometryIdentifier.hpp"
0016 #include "Acts/Geometry/Layer.hpp"
0017 #include "Acts/Geometry/TrackingGeometry.hpp"
0018 #include "Acts/Geometry/TrackingVolume.hpp"
0019 #include "Acts/Geometry/Volume.hpp"
0020 #include "Acts/Geometry/VolumeBounds.hpp"
0021 #include "Acts/Surfaces/Surface.hpp"
0022 #include "Acts/Surfaces/SurfaceArray.hpp"
0023 #include "Acts/Surfaces/SurfaceBounds.hpp"
0024 #include "Acts/Utilities/IAxis.hpp"
0025 #include "Acts/Utilities/Logger.hpp"
0026 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0027 #include "ActsExamples/Io/Csv/CsvInputOutput.hpp"
0028 #include "ActsExamples/Utilities/Paths.hpp"
0029
0030 #include <array>
0031 #include <cstddef>
0032 #include <stdexcept>
0033 #include <vector>
0034
0035 #include "CsvOutputData.hpp"
0036
0037 namespace ActsExamples {
0038
0039 CsvTrackingGeometryWriter::CsvTrackingGeometryWriter(const Config& config,
0040 Acts::Logging::Level level)
0041 : m_cfg(config),
0042 m_logger(Acts::getDefaultLogger("CsvTrackingGeometryWriter", level))
0043
0044 {
0045 if (!m_cfg.trackingGeometry) {
0046 throw std::invalid_argument("Missing tracking geometry");
0047 }
0048 m_world = m_cfg.trackingGeometry->highestTrackingVolume();
0049 if (m_world == nullptr) {
0050 throw std::invalid_argument("Could not identify the world volume");
0051 }
0052 }
0053
0054 std::string CsvTrackingGeometryWriter::name() const {
0055 return "CsvTrackingGeometryWriter";
0056 }
0057
0058 namespace {
0059
0060 using SurfaceWriter = BoostDescribeCsvWriter<SurfaceData>;
0061 using SurfaceGridWriter = BoostDescribeCsvWriter<SurfaceGridData>;
0062 using LayerVolumeWriter = BoostDescribeCsvWriter<LayerVolumeData>;
0063 using BoundarySurface = Acts::BoundarySurfaceT<Acts::TrackingVolume>;
0064
0065
0066 void fillSurfaceData(SurfaceData& data, const Acts::Surface& surface,
0067 const Acts::GeometryContext& geoCtx) noexcept(false) {
0068
0069 data.geometry_id = surface.geometryId().value();
0070 data.volume_id = surface.geometryId().volume();
0071 data.boundary_id = surface.geometryId().boundary();
0072 data.layer_id = surface.geometryId().layer();
0073 data.module_id = surface.geometryId().sensitive();
0074 data.extra_id = surface.geometryId().extra();
0075
0076 auto center = surface.center(geoCtx);
0077 data.cx = center.x() / Acts::UnitConstants::mm;
0078 data.cy = center.y() / Acts::UnitConstants::mm;
0079 data.cz = center.z() / Acts::UnitConstants::mm;
0080
0081 auto transform = surface.localToGlobalTransform(geoCtx);
0082 data.rot_xu = transform(0, 0);
0083 data.rot_xv = transform(0, 1);
0084 data.rot_xw = transform(0, 2);
0085 data.rot_yu = transform(1, 0);
0086 data.rot_yv = transform(1, 1);
0087 data.rot_yw = transform(1, 2);
0088 data.rot_zu = transform(2, 0);
0089 data.rot_zv = transform(2, 1);
0090 data.rot_zw = transform(2, 2);
0091
0092 std::array<float*, 7> dataBoundParameters = {
0093 &data.bound_param0, &data.bound_param1, &data.bound_param2,
0094 &data.bound_param3, &data.bound_param4, &data.bound_param5,
0095 &data.bound_param6};
0096
0097 const auto& bounds = surface.bounds();
0098 data.bounds_type = static_cast<int>(bounds.type());
0099 auto boundValues = bounds.values();
0100
0101 if (boundValues.size() > dataBoundParameters.size()) {
0102 throw std::invalid_argument(
0103 "Bound types with too many parameters. Should never happen.");
0104 }
0105
0106 for (std::size_t ipar = 0; ipar < boundValues.size(); ++ipar) {
0107 (*dataBoundParameters[ipar]) = boundValues[ipar];
0108 }
0109
0110 if (surface.isSensitive()) {
0111 data.module_t = surface.thickness() / Acts::UnitConstants::mm;
0112 }
0113 }
0114
0115
0116 void writeSurface(SurfaceWriter& sfWriter, const Acts::Surface& surface,
0117 const Acts::GeometryContext& geoCtx) {
0118 SurfaceData data;
0119 fillSurfaceData(data, surface, geoCtx);
0120 sfWriter.append(data);
0121 }
0122
0123
0124
0125
0126
0127
0128
0129 void writeCylinderLayerVolume(LayerVolumeWriter& lvWriter,
0130 const Acts::Layer& lv,
0131 const Acts::Transform3& transform,
0132 std::vector<double>& representingBoundValues,
0133 std::vector<double>& volumeBoundValues,
0134 std::vector<double>& lastBoundValues, bool last) {
0135
0136 LayerVolumeData lvDims;
0137 lvDims.geometry_id = lv.geometryId().value();
0138 lvDims.volume_id = lv.geometryId().volume();
0139 lvDims.layer_id = lv.geometryId().layer();
0140 bool isCylinderLayer = (lv.surfaceRepresentation().bounds().type() ==
0141 Acts::SurfaceBounds::eCylinder);
0142
0143 auto lTranslation = transform.translation();
0144
0145
0146 representingBoundValues = {
0147 representingBoundValues[0],
0148 representingBoundValues[1],
0149 lTranslation.z() - representingBoundValues[2],
0150 lTranslation.z() + representingBoundValues[2],
0151 representingBoundValues[4] - representingBoundValues[3],
0152 representingBoundValues[4] + representingBoundValues[3]};
0153
0154
0155 lvDims.min_v0 =
0156 isCylinderLayer ? representingBoundValues[0] : volumeBoundValues[0];
0157 lvDims.max_v0 =
0158 isCylinderLayer ? representingBoundValues[1] : volumeBoundValues[1];
0159 lvDims.min_v1 =
0160 isCylinderLayer ? volumeBoundValues[2] : representingBoundValues[2];
0161 lvDims.max_v1 =
0162 isCylinderLayer ? volumeBoundValues[3] : representingBoundValues[3];
0163 lvDims.min_v2 = representingBoundValues[4];
0164 lvDims.max_v2 = representingBoundValues[5];
0165
0166
0167 LayerVolumeData nlvDims;
0168 nlvDims.geometry_id = lv.geometryId().value();
0169 nlvDims.volume_id = lv.geometryId().volume();
0170 nlvDims.layer_id = lv.geometryId().layer() - 1;
0171 if (isCylinderLayer) {
0172 nlvDims.min_v0 = lastBoundValues[0];
0173 nlvDims.max_v0 = representingBoundValues[0];
0174 nlvDims.min_v1 = lastBoundValues[2];
0175 nlvDims.max_v1 = lastBoundValues[3];
0176
0177 lastBoundValues[0] = representingBoundValues[1];
0178 } else {
0179 nlvDims.min_v0 = lastBoundValues[0];
0180 nlvDims.max_v0 = lastBoundValues[1];
0181 nlvDims.min_v1 = lastBoundValues[2];
0182 nlvDims.max_v1 = representingBoundValues[2];
0183
0184 lastBoundValues[2] = representingBoundValues[3];
0185 }
0186 nlvDims.min_v2 = representingBoundValues[4];
0187 nlvDims.max_v2 = representingBoundValues[5];
0188 lvWriter.append(nlvDims);
0189
0190 lvWriter.append(lvDims);
0191
0192
0193 if (last) {
0194
0195 LayerVolumeData llvDims;
0196 llvDims.geometry_id = lv.geometryId().value();
0197 llvDims.volume_id = lv.geometryId().volume();
0198 llvDims.layer_id = lv.geometryId().layer() + 1;
0199 llvDims.min_v0 = lastBoundValues[0];
0200 llvDims.max_v0 = lastBoundValues[1];
0201 llvDims.min_v1 = lastBoundValues[2];
0202 llvDims.max_v1 = lastBoundValues[3];
0203 llvDims.min_v2 = representingBoundValues[4];
0204 llvDims.max_v2 = representingBoundValues[5];
0205
0206 lvWriter.append(llvDims);
0207 }
0208 }
0209
0210
0211 void writeBoundarySurface(SurfaceWriter& writer,
0212 const BoundarySurface& bsurface,
0213 const Acts::GeometryContext& geoCtx) {
0214 SurfaceData data;
0215 fillSurfaceData(data, bsurface.surfaceRepresentation(), geoCtx);
0216 writer.append(data);
0217 }
0218
0219
0220 void writeVolume(SurfaceWriter& sfWriter, SurfaceGridWriter& sfGridWriter,
0221 LayerVolumeWriter& lvWriter,
0222 const Acts::TrackingVolume& volume, bool writeSensitive,
0223 bool writeBoundary, bool writeSurfaceGrid,
0224 bool writeLayerVolume, const Acts::GeometryContext& geoCtx) {
0225
0226 if (volume.confinedLayers() != nullptr) {
0227 const auto& vTransform = volume.localToGlobalTransform(geoCtx);
0228
0229
0230 std::vector<double> volumeBoundValues = volume.volumeBounds().values();
0231 std::vector<double> lastBoundValues;
0232
0233 if (volume.volumeBounds().type() == Acts::VolumeBounds::eCylinder) {
0234 auto vTranslation = vTransform.translation();
0235
0236 volumeBoundValues = {
0237 volumeBoundValues[0],
0238 volumeBoundValues[1],
0239 vTranslation.z() - volumeBoundValues[2],
0240 vTranslation.z() + volumeBoundValues[2],
0241 volumeBoundValues[4] - volumeBoundValues[3],
0242 volumeBoundValues[4] + volumeBoundValues[3],
0243 };
0244 lastBoundValues = volumeBoundValues;
0245 }
0246
0247 unsigned int layerIdx = 0;
0248 const auto& layers = volume.confinedLayers()->arrayObjects();
0249
0250
0251
0252 if (layers.size() == 3 && writeLayerVolume) {
0253 auto slayer = layers[1];
0254 LayerVolumeData plvDims;
0255 plvDims.geometry_id = slayer->geometryId().value();
0256 plvDims.volume_id = slayer->geometryId().volume();
0257 plvDims.layer_id = slayer->geometryId().layer();
0258 plvDims.min_v0 = volumeBoundValues[0];
0259 plvDims.max_v0 = volumeBoundValues[1];
0260 plvDims.min_v1 = volumeBoundValues[2];
0261 plvDims.max_v1 = volumeBoundValues[3];
0262 lvWriter.append(plvDims);
0263 }
0264
0265
0266 for (const auto& layer : layers) {
0267
0268
0269
0270 if (layer->layerType() == Acts::navigation) {
0271 ++layerIdx;
0272
0273 if (writeLayerVolume) {
0274 writeSurface(sfWriter, layer->surfaceRepresentation(), geoCtx);
0275 }
0276 continue;
0277
0278 } else {
0279
0280 const auto* rVolume = layer->representingVolume();
0281
0282
0283 if (rVolume != nullptr && writeLayerVolume && layers.size() > 3) {
0284
0285 std::vector<double> representingBoundValues =
0286 rVolume->volumeBounds().values();
0287 if (rVolume->volumeBounds().type() == Acts::VolumeBounds::eCylinder) {
0288 bool last = (layerIdx + 2 ==
0289 volume.confinedLayers()->arrayObjects().size());
0290 writeCylinderLayerVolume(lvWriter, *layer,
0291 rVolume->localToGlobalTransform(geoCtx),
0292 representingBoundValues, volumeBoundValues,
0293 lastBoundValues, last);
0294 }
0295 }
0296
0297
0298 if (layer->surfaceArray() != nullptr) {
0299 auto sfArray = layer->surfaceArray();
0300
0301
0302 if (writeSurfaceGrid) {
0303 SurfaceGridData sfGrid;
0304 sfGrid.geometry_id = layer->geometryId().value();
0305 sfGrid.volume_id = layer->geometryId().volume();
0306 sfGrid.layer_id = layer->geometryId().layer();
0307
0308
0309 auto binning = sfArray->binningValues();
0310 auto axes = sfArray->getAxes();
0311 if (!binning.empty() && binning.size() == 2 && axes.size() == 2) {
0312 auto loc0Values = axes[0]->getBinEdges();
0313 sfGrid.nbins_loc0 = loc0Values.size() - 1;
0314 sfGrid.type_loc0 = static_cast<int>(binning[0]);
0315 sfGrid.min_loc0 = loc0Values[0];
0316 sfGrid.max_loc0 = loc0Values[loc0Values.size() - 1];
0317
0318 auto loc1Values = axes[1]->getBinEdges();
0319 sfGrid.nbins_loc1 = loc1Values.size() - 1;
0320 sfGrid.type_loc1 = static_cast<int>(binning[1]);
0321 sfGrid.min_loc1 = loc1Values[0];
0322 sfGrid.max_loc1 = loc1Values[loc1Values.size() - 1];
0323 }
0324 sfGridWriter.append(sfGrid);
0325 }
0326
0327
0328 if (writeSensitive) {
0329 for (auto surface : sfArray->surfaces()) {
0330 if (surface != nullptr) {
0331 writeSurface(sfWriter, *surface, geoCtx);
0332 }
0333 }
0334 }
0335 } else {
0336
0337 writeSurface(sfWriter, layer->surfaceRepresentation(), geoCtx);
0338 }
0339 }
0340 ++layerIdx;
0341 }
0342
0343
0344 if (writeBoundary) {
0345 for (const auto& bsurface : volume.boundarySurfaces()) {
0346 writeBoundarySurface(sfWriter, *bsurface, geoCtx);
0347 }
0348 }
0349 }
0350
0351 if (volume.confinedVolumes()) {
0352 for (const auto& confined : volume.confinedVolumes()->arrayObjects()) {
0353 writeVolume(sfWriter, sfGridWriter, lvWriter, *confined, writeSensitive,
0354 writeBoundary, writeSurfaceGrid, writeLayerVolume, geoCtx);
0355 }
0356 }
0357 }
0358 }
0359
0360 ProcessCode CsvTrackingGeometryWriter::write(const AlgorithmContext& ctx) {
0361 if (!m_cfg.writePerEvent) {
0362 return ProcessCode::SUCCESS;
0363 }
0364
0365 SurfaceWriter sfWriter(
0366 perEventFilepath(m_cfg.outputDir, "detectors.csv", ctx.eventNumber),
0367 m_cfg.outputPrecision);
0368
0369 SurfaceGridWriter sfGridWriter(
0370 perEventFilepath(m_cfg.outputDir, "surface-grids.csv", ctx.eventNumber),
0371 m_cfg.outputPrecision);
0372
0373 LayerVolumeWriter lvWriter(
0374 perEventFilepath(m_cfg.outputDir, "layer-volumes.csv", ctx.eventNumber),
0375 m_cfg.outputPrecision);
0376
0377 writeVolume(sfWriter, sfGridWriter, lvWriter, *m_world, m_cfg.writeSensitive,
0378 m_cfg.writeBoundary, m_cfg.writeSurfaceGrid,
0379 m_cfg.writeLayerVolume, ctx.geoContext);
0380 return ProcessCode::SUCCESS;
0381 }
0382
0383 ProcessCode CsvTrackingGeometryWriter::finalize() {
0384 SurfaceWriter sfWriter(joinPaths(m_cfg.outputDir, "detectors.csv"),
0385 m_cfg.outputPrecision);
0386 SurfaceGridWriter sfGridWriter(
0387 joinPaths(m_cfg.outputDir, "surface-grids.csv"), m_cfg.outputPrecision);
0388
0389 LayerVolumeWriter lvWriter(joinPaths(m_cfg.outputDir, "layer-volumes.csv"),
0390 m_cfg.outputPrecision);
0391
0392 writeVolume(sfWriter, sfGridWriter, lvWriter, *m_world, m_cfg.writeSensitive,
0393 m_cfg.writeBoundary, m_cfg.writeSurfaceGrid,
0394 m_cfg.writeLayerVolume,
0395 Acts::GeometryContext::dangerouslyDefaultConstruct());
0396 return ProcessCode::SUCCESS;
0397 }
0398
0399 }