From 0993fcac3f5edb27e15152d1c43ae13779b0bf4c Mon Sep 17 00:00:00 2001
From: Andreas Salzburger <Andreas.Salzburger@cern.ch>
Date: Mon, 10 Feb 2020 13:17:35 +0100
Subject: [PATCH] updated TGeo and Obj writer

---
 .../ACTFW/TGeoDetector/BuildTGeoDetector.hpp  |  41 +++-
 .../TGeoDetector/TGeoDetectorOptions.hpp      | 217 ++++++++++++++----
 .../include/ACTFW/Plugins/Obj/ObjHelper.hpp   |  12 +
 Plugins/Obj/src/ObjHelper.cpp                 |  78 +++++++
 Plugins/Obj/src/ObjSurfaceWriter.cpp          |  44 ++--
 Plugins/Obj/src/ObjTrackingGeometryWriter.cpp |   5 +-
 external/acts-core                            |   2 +-
 7 files changed, 329 insertions(+), 70 deletions(-)

diff --git a/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/BuildTGeoDetector.hpp b/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/BuildTGeoDetector.hpp
index b24d7ea43..fb72dd5b8 100644
--- a/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/BuildTGeoDetector.hpp
+++ b/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/BuildTGeoDetector.hpp
@@ -25,8 +25,11 @@
 #include "Acts/Material/Material.hpp"
 #include "Acts/Material/MaterialProperties.hpp"
 #include "Acts/Plugins/TGeo/TGeoDetectorElement.hpp"
+#include "Acts/Utilities/Units.hpp"
 #include "TGeoManager.h"
 
+using namespace Acts::UnitLiterals;
+
 namespace FW {
 namespace TGeo {
 
@@ -93,11 +96,39 @@ namespace TGeo {
 
     std::string rootFileName
         = vm["geo-tgeo-filename"].template as<std::string>();
+
+    // Create a beam pipe if configured to do so
+    auto beamPipeParameters 
+        = vm["geo-tgeo-bp-parameters"].template as<read_range>();
+    if (beamPipeParameters.size() > 2){
+
+    /// configure the beam pipe layer builder
+    Acts::PassiveLayerBuilder::Config bplConfig;
+    bplConfig.layerIdentification     = "BeamPipe";
+    bplConfig.centralLayerRadii       = std::vector<double>(1, beamPipeParameters[0]);
+    bplConfig.centralLayerHalflengthZ = std::vector<double>(1, beamPipeParameters[1]);
+    bplConfig.centralLayerThickness   = std::vector<double>(1, beamPipeParameters[2]);
+    auto beamPipeBuilder = std::make_shared<const Acts::PassiveLayerBuilder>(
+        bplConfig, Acts::getDefaultLogger("BeamPipeLayerBuilder", layerLogLevel));
+    // create the volume for the beam pipe
+    Acts::CylinderVolumeBuilder::Config bpvConfig;
+    bpvConfig.trackingVolumeHelper = cylinderVolumeHelper;
+    bpvConfig.volumeName           = "BeamPipe";
+    bpvConfig.layerBuilder         = beamPipeBuilder;
+    bpvConfig.layerEnvelopeR = {1. * Acts::units::_mm, 1. * Acts::units::_mm};
+    bpvConfig.buildToRadiusZero = true;
+    auto beamPipeVolumeBuilder
+        = std::make_shared<const Acts::CylinderVolumeBuilder>(
+            bpvConfig,
+            Acts::getDefaultLogger("BeamPipeVolumeBuilder", volumeLogLevel));
+    // add to the list of builders
+    volumeBuilders.push_back(beamPipeVolumeBuilder);
+
+    }
+
     // import the file from
     TGeoManager::Import(rootFileName.c_str());
 
-    bool firstOne = true;
-
     auto layerBuilderConfigs
         = FW::Options::readTGeoLayerBuilderConfigs<variable_maps_t>(
             vm, layerCreator);
@@ -116,7 +147,9 @@ namespace TGeo {
       Acts::CylinderVolumeBuilder::Config volumeConfig;
       volumeConfig.trackingVolumeHelper = cylinderVolumeHelper;
       volumeConfig.volumeName           = lbc.configurationName;
-      volumeConfig.buildToRadiusZero    = firstOne;
+      volumeConfig.checkRingLayout      = lbc.checkRingLayout;
+      volumeConfig.ringTolerance        = lbc.ringTolerance;
+      volumeConfig.buildToRadiusZero    = (volumeBuilders.size() == 0);
       volumeConfig.layerEnvelopeR
           = {1. * Acts::units::_mm, 5. * Acts::units::_mm};
       volumeConfig.layerBuilder    = layerBuilder;
@@ -127,8 +160,6 @@ namespace TGeo {
                                  volumeLogLevel));
       // add to the list of builders
       volumeBuilders.push_back(volumeBuilder);
-      // remember that you've built to the beam pipe already
-      firstOne = false;
     }
 
     //-------------------------------------------------------------------------------------
diff --git a/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/TGeoDetectorOptions.hpp b/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/TGeoDetectorOptions.hpp
index 7d7d1c49d..e85969ccf 100644
--- a/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/TGeoDetectorOptions.hpp
+++ b/Detectors/TGeoDetector/include/ACTFW/TGeoDetector/TGeoDetectorOptions.hpp
@@ -42,6 +42,9 @@ namespace Options {
         "geo-tgeo-unitScalor",
         po::value<double>()->default_value(10.),
         "Unit scalor from ROOT to Acts.")(
+        "geo-tgeo-bp-parameters",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Potential beam pipe parameters {r, z, t} in [mm].")(
         "geo-tgeo-nlayers",
         po::value<read_series>()->multitoken()->default_value({}),
         "Number of layers on the negative side.")(
@@ -51,6 +54,12 @@ namespace Options {
         "geo-tgeo-players",
         po::value<read_series>()->multitoken()->default_value({}),
         "Number of layers on the positive side.")(
+        "geo-tgeo-ringlayout",
+        po::value<read_series>()->multitoken()->default_value({}),
+        "Indicator if ring layout is present.")(
+        "geo-tgeo-ringtolerance",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Tolerance for ring layout detection in [mm].")(
         "geo-tgeo-nlayernames",
         po::value<read_strings>()->multitoken()->default_value({}),
         "Name identifier for negative layer objects, odered along the series.")(
@@ -74,10 +83,54 @@ namespace Options {
                    po::value<read_strings>()->multitoken()->default_value({}),
                    "Axes definition for negative sensitive objects, odered "
                    "along the series.")(
-        "geo-tgeo-clayersplits",
+        "geo-tgeo-nlayer-rmin",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Cylindrical boundariy min r for negative layers "
+        "to restrict the module parsing (optional).")(
+        "geo-tgeo-nlayer-rmax",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Cylindrical boundariy max r for negative layers "
+        "to restrict the module parsing (optional).")(
+        "geo-tgeo-clayer-rmin",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Cylindrical boundariy min r for central layers "
+        "to restrict the module parsing (optional).")(
+        "geo-tgeo-clayer-rmax",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Cylindrical boundariy max r for central layers "
+        "to restrict the module parsing (optional).")(
+        "geo-tgeo-player-rmin",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Cylindrical boundariy min r for central layers "
+        "to restrict the module parsing (optional).")(
+        "geo-tgeo-player-rmax",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Cylindrical boundariy max r for positive layers "
+        "to restrict the module parsing (optional).")(
+        "geo-tgeo-nlayer-rsplit",
         po::value<read_range>()->multitoken()->default_value({}),
-        "Radial tolerances (if different from 0.) that triggers splitting "
-        " of collected surfaces into different layers.")(
+        "R-tolerances (if > 0.) that triggers splitting "
+        " of collected surfaces into different negative layers.")(
+        "geo-tgeo-nlayer-zsplit",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Z-tolerances (if > 0.) that triggers splitting "
+        " of collected surfaces into different negative layers.")(
+        "geo-tgeo-clayer-rsplit",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "R-tolerances (if > 0.) that triggers splitting "
+        " of collected surfaces into different central layers.")(
+        "geo-tgeo-clayer-zsplit",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Z-tolerances (if > 0.) that triggers splitting "
+        " of collected surfaces into different central layers.")(
+        "geo-tgeo-player-rsplit",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "R-tolerances (if > 0.) that triggers splitting "
+        " of collected surfaces into different positive layers.")(
+        "geo-tgeo-player-zsplit",
+        po::value<read_range>()->multitoken()->default_value({}),
+        "Z-tolerances (if > 0.) that triggers splitting "
+        " of collected surfaces into different positive layers.")(
         "geo-tgeo-cmoduleaxes",
         po::value<read_strings>()->multitoken()->default_value({}),
         "Axes definition for central sensitive objects, odered along the "
@@ -103,83 +156,151 @@ namespace Options {
   {
     std::vector<Acts::TGeoLayerBuilder::Config> detLayerConfigs;
 
-    // general stuff
+    // General: subdetector naming
     read_strings subdetectors
         = vm["geo-subdetectors"].template as<read_strings>();
     double unitScalor = vm["geo-tgeo-unitScalor"].template as<double>();
-    // these define a series, such as 1, 3, 4
+
+    // The number of layers, can be set 1 with splitting detection
     read_series nlayers = vm["geo-tgeo-nlayers"].template as<read_series>();
     read_series clayers = vm["geo-tgeo-clayers"].template as<read_series>();
     read_series players = vm["geo-tgeo-players"].template as<read_series>();
-    //
 
-    // these are going continously through the series, such as i in [ 1 + 3 + 4
-    // ]
+    std::array<read_series, 3> layers = {nlayers, clayers, players};
+
+    std::array<size_t, 3> series_size
+        = {nlayers.size(), clayers.size(), players.size()};
+
+    read_series ringlayout
+        = vm["geo-tgeo-ringlayout"].template as<read_series>();
+
+    read_range ringtolerance
+        = vm["geo-tgeo-ringtolerance"].template as<read_range>();
+
+    // The layer names to parse for in the TGeo
     read_strings nlayernames
         = vm["geo-tgeo-nlayernames"].template as<read_strings>();
     read_strings clayernames
         = vm["geo-tgeo-clayernames"].template as<read_strings>();
     read_strings playernames
         = vm["geo-tgeo-playernames"].template as<read_strings>();
+
+    std::array<read_strings, 3> layernames
+        = {nlayernames, clayernames, playernames};
+
     read_strings nsensitivenames
         = vm["geo-tgeo-nmodulenames"].template as<read_strings>();
     read_strings csensitivenames
         = vm["geo-tgeo-cmodulenames"].template as<read_strings>();
     read_strings psensitivenames
         = vm["geo-tgeo-pmodulenames"].template as<read_strings>();
+
+    // The sensitive names to parse for in the TGeo
+    std::array<read_strings, 3> sensitivenames
+        = {nsensitivenames, csensitivenames, psensitivenames};
+
     read_strings nsensitiveaxes
         = vm["geo-tgeo-nmoduleaxes"].template as<read_strings>();
     read_strings csensitiveaxes
         = vm["geo-tgeo-cmoduleaxes"].template as<read_strings>();
     read_strings psensitiveaxes
         = vm["geo-tgeo-pmoduleaxes"].template as<read_strings>();
-    // the layer splits
-    read_range clayersplits
-        = vm["geo-tgeo-clayersplits"].template as<read_range>();
 
-    // total coutners
-    int tin = 0;
-    int tic = 0;
-    int tip = 0;
-    // prepare the TGeoLayerBuilder::Configs
-    for (size_t idet = 0; idet < clayers.size(); ++idet) {
+    std::array<read_strings, 3> sensitiveaxes
+        = {nsensitiveaxes, csensitiveaxes, psensitiveaxes};
 
-      Acts::TGeoLayerBuilder::Config layerBuilderConfig;
-      // get the current numbers
-      int cn = nlayers[idet];
-      int cc = clayers[idet];
-      int cp = players[idet];
-      // fill the configs - for negative
-      for (int in = 0; in < cn; ++in, ++tin) {
-        Acts::TGeoLayerBuilder::LayerConfig lConfig;
-        lConfig.layerName  = nlayernames[tin];
-        lConfig.sensorName = nsensitivenames[tin];
-        lConfig.localAxes  = nsensitiveaxes[tin];
-        layerBuilderConfig.negativeLayerConfigs.push_back(lConfig);
-      }
-      // fill the configs - for barrel
-      for (int ic = 0; ic < cc; ++ic, ++tic) {
-        Acts::TGeoLayerBuilder::LayerConfig lConfig;
-        lConfig.layerName                    = clayernames[tic];
-        lConfig.sensorName                   = csensitivenames[tic];
-        lConfig.localAxes                    = csensitiveaxes[tic];
-        layerBuilderConfig.centralLayerSplit = clayersplits[tic];
-        layerBuilderConfig.centralLayerConfigs.push_back(lConfig);
+    // The parse radii in r
+    read_range nrmin = vm["geo-tgeo-nlayer-rmin"].template as<read_range>();
+    read_range nrmax = vm["geo-tgeo-nlayer-rmax"].template as<read_range>();
+    read_range crmin = vm["geo-tgeo-clayer-rmin"].template as<read_range>();
+    read_range crmax = vm["geo-tgeo-clayer-rmax"].template as<read_range>();
+    read_range prmin = vm["geo-tgeo-player-rmin"].template as<read_range>();
+    read_range prmax = vm["geo-tgeo-player-rmax"].template as<read_range>();
+
+    std::array<read_range, 3> rmin = {nrmin, crmin, prmin};
+    std::array<read_range, 3> rmax = {nrmax, crmax, prmax};
+
+    // The split tolerances in r and z
+    read_range nlayersplitr
+        = vm["geo-tgeo-nlayer-rsplit"].template as<read_range>();
+    read_range nlayersplitz
+        = vm["geo-tgeo-nlayer-zsplit"].template as<read_range>();
+    read_range clayersplitr
+        = vm["geo-tgeo-clayer-rsplit"].template as<read_range>();
+    read_range clayersplitz
+        = vm["geo-tgeo-clayer-zsplit"].template as<read_range>();
+    read_range playersplitr
+        = vm["geo-tgeo-player-rsplit"].template as<read_range>();
+    read_range playersplitz
+        = vm["geo-tgeo-player-zsplit"].template as<read_range>();
+
+    std::array<read_range, 3> splittolr
+        = {nlayersplitr, clayersplitr, playersplitr};
+
+    std::array<read_range, 3> splittolz
+        = {nlayersplitz, clayersplitz, playersplitz};
+
+    // The maximum series and total counter for access of nonsplit layers
+    size_t max_series
+        = *std::max_element(series_size.begin(), series_size.end());
+    std::array<size_t, 3> ti = {0, 0, 0};
+
+
+      // If a beam pipe is present, shift the sub detector names by one
+      // Create a beam pipe if configured to do so
+      int idetaddon = 0;
+      auto beamPipeParameters 
+        = vm["geo-tgeo-bp-parameters"].template as<read_range>();
+      if (beamPipeParameters.size() > 2){
+         ++idetaddon;
       }
-      // fill the configs - for positive
-      for (int ip = 0; ip < cp; ++ip, ++tip) {
-        Acts::TGeoLayerBuilder::LayerConfig lConfig;
-        lConfig.layerName  = playernames[tip];
-        lConfig.sensorName = psensitivenames[tip];
-        lConfig.localAxes  = psensitiveaxes[tip];
-        layerBuilderConfig.positiveLayerConfigs.push_back(lConfig);
+
+    // Prepare the TGeoLayerBuilder::Configs
+    for (size_t idet = 0; idet < max_series; ++idet) {
+      // Each detector needs a layer builder
+      Acts::TGeoLayerBuilder::Config layerBuilderConfig;
+      // Loop over the | n | c | p | configuration
+      for (unsigned int ncp = 0; ncp < 3; ++ncp) {
+        // number of layers of this configuration
+        unsigned int nl = layers[ncp].size() > idet ? layers[ncp][idet] : 0;
+        for (int in = 0; in < nl; ++in, ++ti[ncp]) {
+          // Create the layer config object and fill it
+          Acts::TGeoLayerBuilder::LayerConfig lConfig;
+          lConfig.layerName  = layernames[ncp][ti[ncp]];
+          lConfig.sensorName = sensitivenames[ncp][ti[ncp]];
+          lConfig.localAxes  = sensitiveaxes[ncp][ti[ncp]];
+          // Fill the parsing restrictions in r
+          auto crmin = rmin[ncp];
+          auto crmax = rmax[ncp];
+          if (crmin.size() > ti[ncp] and crmax.size() > ti[ncp]) {
+            lConfig.parseRangeR = {crmin[ti[ncp]], crmax[ti[ncp]]};
+          }
+          // Fill the layer splitting parameters in r
+          if (splittolr[ncp].size() > ti[ncp]) {
+            layerBuilderConfig.layerSplitToleranceR[ncp]
+                = splittolr[ncp][ti[ncp]];
+          }
+          // Fill the layer splitting parameters in z
+          if (splittolz[ncp].size() > ti[ncp]) {
+            layerBuilderConfig.layerSplitToleranceZ[ncp]
+                = splittolz[ncp][ti[ncp]];
+          }
+          layerBuilderConfig.layerConfigurations[ncp].push_back(lConfig);
+          // Set the ring layout if configured to do so
+          if (ringlayout.size() > idet and ringtolerance.size() > idet) {
+            layerBuilderConfig.checkRingLayout = (ringlayout[idet] != 0);
+            layerBuilderConfig.ringTolerance   = ringtolerance[idet];
+          }
+        }
       }
-      // set the scale and the layer creator
-      layerBuilderConfig.configurationName = subdetectors[idet];
+
+
+      // Set the scale and the layer creator
+      layerBuilderConfig.configurationName = subdetectors[idet+idetaddon];
       layerBuilderConfig.unit              = unitScalor;
       layerBuilderConfig.layerCreator      = layerCreator;
 
-      // now add it to the configs
+      // Now add it to the configs
       detLayerConfigs.push_back(layerBuilderConfig);
     }
     return detLayerConfigs;
diff --git a/Plugins/Obj/include/ACTFW/Plugins/Obj/ObjHelper.hpp b/Plugins/Obj/include/ACTFW/Plugins/Obj/ObjHelper.hpp
index 472fefa8f..33dcab9c9 100644
--- a/Plugins/Obj/include/ACTFW/Plugins/Obj/ObjHelper.hpp
+++ b/Plugins/Obj/include/ACTFW/Plugins/Obj/ObjHelper.hpp
@@ -58,6 +58,18 @@ namespace Obj {
                   double                             thickness = 0.,
                   const std::vector<unsigned int>&   vsides    = {});
 
+  /// This will write a disc surface with annulus bounds
+  ///
+  /// @param stream is the stream where to write to
+  void
+  writeAnnulusDisc(std::ofstream&                     stream,
+                   VtnCounter&                        vtnCounter,
+                   double                             scalor,
+                   unsigned int                       nSegments,
+                   const Acts::Transform3D&           transform,
+                   const std::vector<Acts::Vector2D>& vertices,
+                   double                             thickness = 0.);
+
   /// This will write a cylindrical object
   ///
   /// @param stream is the stream where to write to
diff --git a/Plugins/Obj/src/ObjHelper.cpp b/Plugins/Obj/src/ObjHelper.cpp
index 3c614c8ad..650c734fd 100644
--- a/Plugins/Obj/src/ObjHelper.cpp
+++ b/Plugins/Obj/src/ObjHelper.cpp
@@ -7,7 +7,9 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include "ACTFW/Plugins/Obj/ObjHelper.hpp"
+#include <iostream>
 #include <vector>
+#include "Acts/Utilities/Helpers.hpp"
 
 void
 FW::Obj::writeVTN(std::ofstream&        stream,
@@ -105,6 +107,82 @@ FW::Obj::writePlanarFace(std::ofstream&                     stream,
   constructVerticalFaces(stream, fvertex, vsides);
 }
 
+void
+FW::Obj::writeAnnulusDisc(std::ofstream&                     stream,
+                          VtnCounter&                        vtnCounter,
+                          double                             scalor,
+                          unsigned int                       nSegments,
+                          const Acts::Transform3D&           transform,
+                          const std::vector<Acts::Vector2D>& vertices,
+                          double                             thickness)
+{
+
+  unsigned int cvc = vtnCounter.vcounter;
+
+  std::vector<Acts::Vector3D> gVertices;
+  // Write the vertices
+  for (auto& v : vertices) {
+    gVertices.push_back(transform * Acts::Vector3D(v.x(), v.y(), 0.));
+  }
+
+  // Complete if there are segments defined for a bow
+  if (nSegments > 1) {
+
+    /// Fill the vertices in betwen
+    auto fillBow
+        = [&](const Acts::Vector3D& first,
+              const Acts::Vector3D& second) -> std::vector<Acts::Vector3D> {
+      // The completed list of vertices
+      std::vector<Acts::Vector3D> completed3D;
+
+      double oseg = 1. / nSegments;
+      double phif = Acts::VectorHelpers::phi(first);
+      double phis = Acts::VectorHelpers::phi(second);
+      double phiD = (phis - phif);
+
+      if (std::abs(phiD) > M_PI and phif * phis < 0.) {
+        phiD += phiD < 0. ? 2 * M_PI : -2 * M_PI;
+      }
+
+      double rf = Acts::VectorHelpers::perp(first);
+      double rs = Acts::VectorHelpers::perp(second);
+      double zf = first.z();
+      double zs = second.z();
+
+      phiD *= oseg;
+      double rD = (rs - rf) * oseg;
+      double zD = (zs - zf) * oseg;
+
+      for (unsigned int is = 0; is < nSegments + 1; ++is) {
+        double r   = rf + is * rD;
+        double phi = phif + is * phiD;
+        double z   = zf + is * zD;
+        completed3D.push_back(Acts::Vector3D(r * cos(phi), r * sin(phi), z));
+      }
+      // Reassing the global points
+      return completed3D;
+    };
+    // Fill the bows
+    auto completedBow1 = fillBow(gVertices[0], gVertices[1]);
+    auto completedBow2 = fillBow(gVertices[2], gVertices[3]);
+    // Clear the vertices
+    gVertices = completedBow1;
+    gVertices.insert(
+        gVertices.end(), completedBow2.begin(), completedBow2.end());
+  }
+
+  // Write the vertices
+  for (const auto& gv : gVertices) {
+    writeVTN(stream, vtnCounter, scalor, gv, "v");
+  }
+  // Write the faces
+  stream << "f";
+  for (unsigned int iv = 0; iv < gVertices.size(); ++iv) {
+    stream << " " << cvc + iv + 1;
+  }
+  stream << std::endl;
+}
+
 void
 FW::Obj::writeTube(std::ofstream&           stream,
                    VtnCounter&              vtnCounter,
diff --git a/Plugins/Obj/src/ObjSurfaceWriter.cpp b/Plugins/Obj/src/ObjSurfaceWriter.cpp
index d9506661c..472013dc7 100644
--- a/Plugins/Obj/src/ObjSurfaceWriter.cpp
+++ b/Plugins/Obj/src/ObjSurfaceWriter.cpp
@@ -14,6 +14,7 @@
 
 #include <Acts/Geometry/GeometryID.hpp>
 #include <Acts/Geometry/Layer.hpp>
+#include <Acts/Surfaces/AnnulusBounds.hpp>
 #include <Acts/Surfaces/CylinderBounds.hpp>
 #include <Acts/Surfaces/PlanarBounds.hpp>
 #include <Acts/Surfaces/RadialBounds.hpp>
@@ -31,7 +32,6 @@ FW::Obj::ObjSurfaceWriter::ObjSurfaceWriter(
   } else if (!m_cfg.outputStream) {
     throw std::invalid_argument("Missing output stream");
   }
-
   // Write down the file prefix
   (*(m_cfg.outputStream)) << m_cfg.filePrefix << '\n';
 }
@@ -51,20 +51,19 @@ FW::Obj::ObjSurfaceWriter::write(const AlgorithmContext& context,
   ACTS_DEBUG(">>Obj: Writer for Surface object called.");
 
   auto scalor = m_cfg.outputScalor;
-  // let's get the bounds & the transform
-  const Acts::SurfaceBounds& surfaceBounds = surface.bounds();
-  auto                       sTransform = surface.transform(context.geoContext);
 
-  // dynamic_cast to PlanarBounds
-  const Acts::PlanarBounds* planarBounds
-      = dynamic_cast<const Acts::PlanarBounds*>(&surfaceBounds);
-  // only continue if the cast worked
+  // Bounds and transform defines the full 3D object
+  const auto& surfaceBounds = surface.bounds();
+  auto        sTransform    = surface.transform(context.geoContext);
+
+  // Dynamic_cast to PlanarBounds
+  auto planarBounds = dynamic_cast<const Acts::PlanarBounds*>(&surfaceBounds);
   if (planarBounds && m_cfg.outputSensitive) {
     ACTS_VERBOSE(">>Obj: Writing out a PlaneSurface");
-    // set the precision - just to be sure
+    // Set the precision
     (*(m_cfg.outputStream)) << '\n';
     (*(m_cfg.outputStream)) << std::setprecision(m_cfg.outputPrecision);
-    // get the vertices
+    // Get the vertices
     auto planarVertices = planarBounds->vertices();
     // loop over the vertices
     std::vector<Acts::Vector3D> vertices;
@@ -92,9 +91,26 @@ FW::Obj::ObjSurfaceWriter::write(const AlgorithmContext& context,
     (*(m_cfg.outputStream)) << '\n';
   }
 
-  // check if you have layer and check what your have
-  // dynamic cast to CylinderBounds work the same
-  const Acts::CylinderBounds* cylinderBounds
+  // Dynamic cast to AnnulusBounds
+  auto annulusBounds = dynamic_cast<const Acts::AnnulusBounds*>(&surfaceBounds);
+  if (annulusBounds != nullptr and m_cfg.outputSensitive) {
+    ACTS_VERBOSE(">>Obj: Writing out a Annulus bounded disc");
+    // Set the precision
+    (*(m_cfg.outputStream)) << '\n';
+    (*(m_cfg.outputStream)) << std::setprecision(m_cfg.outputPrecision);
+    // Get the vertices
+    auto annulusVertices = annulusBounds->vertices();
+    Obj::writeAnnulusDisc(*(m_cfg.outputStream),
+                          m_vtnCounter,
+                          scalor,
+                          3,
+                          sTransform,
+                          annulusVertices);
+    (*(m_cfg.outputStream)) << '\n';
+  }
+
+  // Dynamic cast to CylinderBounds work the same
+  auto cylinderBounds
       = dynamic_cast<const Acts::CylinderBounds*>(&surfaceBounds);
   if (cylinderBounds && m_cfg.outputLayerSurface) {
     ACTS_VERBOSE(">>Obj: Writing out a CylinderSurface with r = "
@@ -115,7 +131,7 @@ FW::Obj::ObjSurfaceWriter::write(const AlgorithmContext& context,
     (*(m_cfg.outputStream)) << '\n';
   }
 
-  ////dynamic cast to RadialBounds or disc bounds work the same
+  /// Dynamic cast to RadialBounds or disc bounds work the same
   const Acts::RadialBounds* radialBounds
       = dynamic_cast<const Acts::RadialBounds*>(&surfaceBounds);
   if (radialBounds && m_cfg.outputLayerSurface) {
diff --git a/Plugins/Obj/src/ObjTrackingGeometryWriter.cpp b/Plugins/Obj/src/ObjTrackingGeometryWriter.cpp
index 3ce37ea20..0daae73fc 100644
--- a/Plugins/Obj/src/ObjTrackingGeometryWriter.cpp
+++ b/Plugins/Obj/src/ObjTrackingGeometryWriter.cpp
@@ -74,8 +74,9 @@ FW::Obj::ObjTrackingGeometryWriter::write(const AlgorithmContext&     context,
       if (!surfaceWriter) return;
       // layer prefix
       surfaceWriter->write(m_cfg.layerPrefix);
-      // try to write the material surface as well
-      if (layer->surfaceRepresentation().surfaceMaterial()) {
+      // Try to write the material surface as well
+      if (layer->surfaceRepresentation().surfaceMaterial()
+          or layer->surfaceArray() == nullptr) {
         surfaceWriter->write(context, layer->surfaceRepresentation());
       }
       // the the approaching surfaces and check if they have material
diff --git a/external/acts-core b/external/acts-core
index 8a6f7485e..325dff6d4 160000
--- a/external/acts-core
+++ b/external/acts-core
@@ -1 +1 @@
-Subproject commit 8a6f7485eb2b4674ec1073fb5dd58ff5d8547301
+Subproject commit 325dff6d42223fcc092256b56ebe9f855570a715
-- 
GitLab