Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-08-03 07:48:45

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "ActsExamples/MuonSpectrometerMockupDetector/GeoMuonMockupExperiment.hpp"
0010 
0011 #include <iostream>
0012 
0013 #include "GeoGenericFunctions/Variable.h"
0014 #include "GeoModelHelpers/MaterialManager.h"
0015 #include "GeoModelHelpers/defineWorld.h"
0016 #include "GeoModelHelpers/printVolume.h"
0017 #include "GeoModelIOHelpers/GMIO.h"
0018 #include "GeoModelKernel/GeoBox.h"
0019 #include "GeoModelKernel/GeoSerialTransformer.h"
0020 #include "GeoModelKernel/GeoTransform.h"
0021 #include "GeoModelKernel/GeoTrd.h"
0022 #include "GeoModelKernel/GeoTube.h"
0023 #include "GeoModelKernel/GeoXF.h"
0024 #include "GeoModelKernel/throwExcept.h"
0025 
0026 namespace {
0027 constexpr double rot90deg = 90. * GeoModelKernelUnits::deg;
0028 }
0029 namespace ActsExamples {
0030 
0031 std::string to_string(GeoMuonMockupExperiment::MuonLayer layer) {
0032   switch (layer) {
0033     using enum GeoMuonMockupExperiment::MuonLayer;
0034     case Inner:
0035       return "Inner";
0036     case Middle:
0037       return "Middle";
0038     case Outer:
0039       return "Outer";
0040     case nLayers:
0041       return "nLayers";
0042   }
0043   return "UNKNOWN";
0044 }
0045 
0046 using FpvLink = GeoMuonMockupExperiment::FpvLink;
0047 
0048 GeoMuonMockupExperiment::GeoMuonMockupExperiment(
0049     const Config& cfg, std::unique_ptr<const Acts::Logger> logger)
0050     : m_cfg{cfg}, m_logger{std::move(logger)} {}
0051 Acts::GeoModelTree GeoMuonMockupExperiment::constructMS() {
0052   const double worldR = m_cfg.barrelRadii[2] + 0.5 * GeoModelKernelUnits::m;
0053 
0054   const double barrelZ =
0055       (m_cfg.nEtaStations + 1) * (m_chamberLength + m_cfg.stationDistInZ) +
0056       0.5 * GeoModelKernelUnits::m;
0057   const double worldZ =
0058       barrelZ + m_cfg.bigWheelDistZ + 2. * m_stationHeightEndcap;
0059 
0060   PVLink world = createGeoWorld(worldR, worldR, worldZ);
0061 
0062   setupMaterials();
0063 
0064   m_publisher->setName("Muon");
0065 
0066   auto barrelCylinder = make_intrusive<GeoTube>(
0067       (m_cfg.barrelRadii[0] - 0.5 * m_stationHeightBarrel),
0068       (m_cfg.barrelRadii[2] + 0.5 * m_stationHeightBarrel),
0069       (m_cfg.nEtaStations + 1) * (m_chamberLength + m_cfg.stationDistInZ));
0070   auto barrelLogVol = make_intrusive<GeoLogVol>(
0071       "BarrelEnvelope", barrelCylinder,
0072       MaterialManager::getManager()->getMaterial("std::air"));
0073 
0074   auto barrelEnvelope = make_intrusive<GeoPhysVol>(barrelLogVol);
0075 
0076   auto toyBox = make_intrusive<GeoBox>(10. * GeoModelKernelUnits::cm,
0077                                        10. * GeoModelKernelUnits::cm,
0078                                        10. * GeoModelKernelUnits::cm);
0079   auto muonEnvelope = make_intrusive<GeoPhysVol>(make_intrusive<GeoLogVol>(
0080       "MuonEnvelope", cacheShape(toyBox),
0081       MaterialManager::getManager()->getMaterial("special::Ether")));
0082 
0083   for (MuonLayer layer :
0084        {MuonLayer::Inner, MuonLayer::Middle, MuonLayer::Outer}) {
0085     for (unsigned int sector = 1; sector <= m_cfg.nSectors; ++sector) {
0086       for (unsigned int etaIdx = 1; etaIdx <= m_cfg.nEtaStations; ++etaIdx) {
0087         const double z_displacement =
0088             0.25 * m_chamberLength +
0089             etaIdx * (m_chamberLength + m_cfg.stationDistInZ);
0090         const double radius = m_cfg.barrelRadii[static_cast<int>(layer)] +
0091                               0.5 * m_stationHeightBarrel;
0092         barrelEnvelope->add(
0093             makeTransform(GeoTrf::TranslateZ3D(z_displacement) *
0094                           GeoTrf::RotateZ3D(sector * m_sectorSize) *
0095                           GeoTrf::TranslateX3D(radius)));
0096         barrelEnvelope->add(assembleBarrelStation(layer, sector, etaIdx));
0097         ///
0098         barrelEnvelope->add(
0099             makeTransform(GeoTrf::TranslateZ3D(-z_displacement) *
0100                           GeoTrf::RotateZ3D(sector * m_sectorSize) *
0101                           GeoTrf::TranslateX3D(radius) *
0102                           GeoTrf::RotateX3D(180. * GeoModelKernelUnits::deg)));
0103         barrelEnvelope->add(
0104             assembleBarrelStation(layer, sector, -static_cast<int>(etaIdx)));
0105       }
0106     }
0107   }
0108   muonEnvelope->add(barrelEnvelope);
0109   /// Construct the endcaps
0110   if (m_cfg.buildEndcaps) {
0111     const double midWheelZ = barrelZ + 0.5 * m_stationHeightEndcap;
0112     const double outWheelZ =
0113         midWheelZ + m_stationHeightEndcap + m_cfg.bigWheelDistZ;
0114     using enum ActsExamples::GeoMuonMockupExperiment::MuonLayer;
0115     assembleBigWheel(muonEnvelope, Middle, midWheelZ);
0116     assembleBigWheel(muonEnvelope, Middle, -midWheelZ);
0117     assembleBigWheel(muonEnvelope, Outer, outWheelZ);
0118     assembleBigWheel(muonEnvelope, Outer, -outWheelZ);
0119   }
0120   const unsigned nChambers =
0121       2 * m_cfg.nSectors * m_cfg.nEtaStations *
0122       static_cast<unsigned>(MuonLayer::nLayers);  // barrel part
0123   const unsigned nMultiLayers = 2 * nChambers;
0124   const unsigned nTubes = nMultiLayers * m_cfg.nTubeLayers * m_cfg.nTubes;
0125   const unsigned nRpc = 2 * nChambers * m_cfg.nRpcAlongZ * m_cfg.nRpcAlongPhi;
0126   ACTS_INFO("Constructed a muon system with "
0127             << nChambers << " muon stations containing in total "
0128             << nMultiLayers << " Mdt multilayers & " << nRpc
0129             << " Rpc chambers. Total: " << (nMultiLayers + nRpc));
0130   ACTS_INFO("Each multilayer contains "
0131             << m_cfg.nTubeLayers << " tube-layers with " << m_cfg.nTubes
0132             << " tubes each giving in total " << nTubes << " placed tubes.");
0133 
0134   world->add(nameTag(m_publisher->getName()));
0135   world->add(muonEnvelope);
0136 
0137   ACTS_VERBOSE("Printout of the  entire world \n " << printVolume(world));
0138 
0139   clearSharedCaches();
0140   Acts::GeoModelTree outTree{};
0141   outTree.worldVolume = world;
0142 
0143   using VolumeMap_t = Acts::GeoModelTree::VolumePublisher::VolumeMap_t;
0144   VolumeMap_t publishedVol{};
0145   for (const auto& [fpV, pubKey] : m_publisher->getPublishedFPV()) {
0146     try {
0147       const auto key = std::any_cast<std::string>(pubKey);
0148       if (!publishedVol
0149                .insert(std::make_pair(key, static_cast<GeoFullPhysVol*>(fpV)))
0150                .second) {
0151         throw std::invalid_argument("GeoMuonMockupExperiment() - Key " + key +
0152                                     " is no longer unique");
0153       }
0154     } catch (const std::bad_any_cast& e) {
0155       throw std::domain_error(
0156           "GeoMuonMockupExperiment() - Failed to cast the key to string " +
0157           std::string{e.what()});
0158     }
0159   }
0160   outTree.publisher->publishVolumes(m_publisher->getName(),
0161                                     std::move(publishedVol));
0162 
0163   if (m_cfg.dumpTree) {
0164     // open the DB connection
0165     GMDBManager db{m_cfg.dbName};
0166     // check the DB connection
0167     if (!db.checkIsDBOpen()) {
0168       throw std::runtime_error(
0169           "GeoMuonMockupExperiment::constructMS() - It was not possible to "
0170           "open the DB correctly!");
0171     }
0172     // init the GeoModel node action
0173     GeoModelIO::WriteGeoModel writeGeoDB{db};
0174     world->exec(&writeGeoDB);  // visit all GeoModel nodes
0175     writeGeoDB.saveToDB(m_publisher.get());
0176   }
0177 
0178   m_publisher.reset();
0179   return outTree;
0180 }
0181 PVLink GeoMuonMockupExperiment::assembleEndcapStation(const double lowR,
0182                                                       const MuonLayer layer,
0183                                                       const unsigned int sector,
0184                                                       const int etaIdx) {
0185   const double angularScale = 2. * std::sin(0.5 * m_sectorSize);
0186 
0187   const double lowTubeLength = angularScale * lowR;
0188   const double upperTubeLength = angularScale * (lowR + m_chamberLength);
0189   /// std::cout<<"Lol : lowTubeLength: "<<lowTubeLength<<", upperTubeLength:
0190   /// "<<upperTubeLength<<std::endl;
0191 
0192   auto envelopeTrd = make_intrusive<GeoTrd>(
0193       0.5 * m_stationHeightEndcap, 0.5 * m_stationHeightEndcap,
0194       0.5 * lowTubeLength, 0.5 * upperTubeLength, 0.5 * m_chamberLength);
0195   auto logVol = make_intrusive<GeoLogVol>(
0196       "MuonBarrelLogVol", cacheShape(envelopeTrd),
0197       MaterialManager::getManager()->getMaterial("std::air"));
0198   auto envelopeVol = make_intrusive<GeoPhysVol>(cacheVolume(logVol));
0199   double currentX = -envelopeTrd->getXHalfLength1() + 0.5 * m_multiLayerHeight;
0200   envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0201   publishFPV(envelopeVol,
0202              assembleMultilayerEndcap(1, lowTubeLength, upperTubeLength),
0203              to_string(layer) + "_EMDT_" + std::to_string(etaIdx) + "_" +
0204                  std::to_string(sector) + "_1");
0205 
0206   currentX += 0.5 * m_multiLayerHeight + m_cfg.multiLayerSeparation +
0207               0.5 * m_multiLayerHeight;
0208   envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0209   publishFPV(envelopeVol,
0210              assembleMultilayerEndcap(2, lowTubeLength, upperTubeLength),
0211              to_string(layer) + "_EMDT_" + std::to_string(etaIdx) + "_" +
0212                  std::to_string(sector) + "_2");
0213 
0214   return envelopeVol;
0215 }
0216 void GeoMuonMockupExperiment::assembleBigWheel(const PVLink& envelopeVol,
0217                                                const MuonLayer layer,
0218                                                const double wheelZ) {
0219   envelopeVol->add(makeTransform(GeoTrf::TranslateZ3D(wheelZ)));
0220   const double lowR = m_cfg.endCapWheelLowR;
0221   const double effR = (m_chamberLength + m_cfg.stationDistInR);
0222 
0223   const unsigned int nEta =
0224       1 + static_cast<unsigned>(
0225               (m_cfg.barrelRadii[static_cast<int>(MuonLayer::Outer)] - lowR) /
0226               effR);
0227   const double highR = lowR + nEta * effR;
0228   auto envelopeShape =
0229       make_intrusive<GeoTube>(lowR, highR, 0.5 * m_stationHeightEndcap);
0230   auto envelopeLogVol = make_intrusive<GeoLogVol>(
0231       "EndcapEnvelope", cacheShape(envelopeShape),
0232       MaterialManager::getManager()->getMaterial("std::air"));
0233 
0234   auto wheelEnvelope = make_intrusive<GeoPhysVol>(cacheVolume(envelopeLogVol));
0235 
0236   for (unsigned int stationEta = 0; stationEta < nEta; ++stationEta) {
0237     const double radius = lowR + stationEta * effR;
0238     for (unsigned int sector = 1; sector <= m_cfg.nSectors; ++sector) {
0239       if (wheelZ > 0) {
0240         wheelEnvelope->add(
0241             makeTransform(GeoTrf::RotateZ3D(sector * m_sectorSize) *
0242                           GeoTrf::TranslateX3D(radius + 0.5 * m_chamberLength) *
0243                           GeoTrf::RotateY3D(90. * GeoModelKernelUnits::deg) *
0244                           GeoTrf::RotateZ3D(180. * GeoModelKernelUnits::deg)));
0245       } else {
0246         wheelEnvelope->add(
0247             makeTransform(GeoTrf::RotateZ3D(sector * m_sectorSize) *
0248                           GeoTrf::TranslateX3D(radius + 0.5 * m_chamberLength) *
0249                           GeoTrf::RotateY3D(90. * GeoModelKernelUnits::deg)));
0250       }
0251       const int castEta =
0252           (wheelZ > 0 ? 1 : -1) * static_cast<int>(stationEta + 1);
0253       wheelEnvelope->add(assembleEndcapStation(radius, layer, sector, castEta));
0254     }
0255   }
0256   envelopeVol->add(wheelEnvelope);
0257 }
0258 
0259 void GeoMuonMockupExperiment::setupMaterials() {
0260   auto* matMan = MaterialManager::getManager();
0261 
0262   matMan->setMaterialNamespace("std");
0263   ACTS_DEBUG("Create the chemical elements.");
0264   /// Table taken from
0265   /// https://gitlab.cern.ch/atlas/geomodelatlas/GeoModelData/-/blob/master/Materials/elements.xml
0266   matMan->addElement("Carbon", "C", 6, 12.0112);
0267   matMan->addElement("Aluminium", "Al", 13, 26.9815);
0268   matMan->addElement("Iron", "Fe", 26, 55.847);
0269   matMan->addElement("Copper", "Cu", 29, 63.54);
0270   matMan->addElement("Nitrogen", "N", 7.0, 14.0031);
0271   matMan->addElement("Oxygen", "O", 8.0, 15.9949);
0272   matMan->addElement("Argon", "Ar", 18.0, 39.9624);
0273   matMan->addElement("Hydrogen", "H", 1.0, 1.00782503081372);
0274   matMan->addElement("Chlorine", "Cl", 18.0, 35.453);
0275 
0276   using MatComposition_t = std::vector<std::pair<std::string, double>>;
0277 
0278   auto appendMaterial = [matMan, this](const std::string& matName,
0279                                        const MatComposition_t& composition,
0280                                        const double density) {
0281     ACTS_DEBUG("Create new material " << matName << " with density "
0282                                       << density);
0283     matMan->addMaterial(matName, density);
0284     for (const auto& [compName, fraction] : composition) {
0285       ACTS_DEBUG("Try to add new material component "
0286                  << compName << " contributing by " << fraction
0287                  << " parts to the total material");
0288       matMan->addMatComponent(compName, fraction);
0289     }
0290     ACTS_DEBUG("Material defined.");
0291     matMan->lockMaterial();
0292   };
0293   appendMaterial("air",
0294                  {{"Nitrogen", 0.7494},
0295                   {"Oxygen", 0.2369},
0296                   {"Argon", 0.0129},
0297                   {"Hydrogen", 0.0008}},
0298                  0.001290);
0299   appendMaterial("Aluminium", {{"Aluminium", 1.}}, 2.7);
0300   appendMaterial("Copper", {{"Copper", 1.}}, 8.96);
0301   appendMaterial("CO2", {{"Carbon", 1.}, {"Oxygen", 2.}}, 0.00184);
0302   appendMaterial("ArCO2", {{"Argon", .93}, {"std::CO2", .07}}, .0054);
0303 
0304   appendMaterial("Forex",
0305                  {{"Carbon", 0.3843626433635827},
0306                   {"Hydrogen", 0.0483830941493594},
0307                   {"Chlorine", 0.5672542624870579}},
0308                  0.7);
0309   if (logger().level() == Acts::Logging::Level::DEBUG) {
0310     matMan->printAll();
0311   }
0312 }
0313 void GeoMuonMockupExperiment::publishFPV(const PVLink& envelopeVol,
0314                                          const FpvLink& publishMe,
0315                                          const std::string& pubName) {
0316   m_publisher->publishNode(static_cast<GeoVFullPhysVol*>(publishMe.get()),
0317                            pubName);
0318   envelopeVol->add(nameTag(pubName));
0319   envelopeVol->add(publishMe);
0320 }
0321 PVLink GeoMuonMockupExperiment::assembleBarrelStation(const MuonLayer layer,
0322                                                       const unsigned int sector,
0323                                                       const int etaIdx) {
0324   const double envelopeWidth = 2. *
0325                                (m_cfg.barrelRadii[static_cast<int>(layer)] -
0326                                 0.5 * m_stationHeightBarrel) *
0327                                std::sin(0.5 * m_sectorSize);
0328 
0329   auto box = make_intrusive<GeoBox>(
0330       0.5 * m_stationHeightBarrel, 0.5 * envelopeWidth,
0331       0.5 * m_chamberLength + 0.1 * GeoModelKernelUnits::mm);
0332   auto logVol = make_intrusive<GeoLogVol>(
0333       "MuonBarrelLogVol", cacheShape(box),
0334       MaterialManager::getManager()->getMaterial("std::air"));
0335   auto envelopeVol = make_intrusive<GeoPhysVol>(cacheVolume(logVol));
0336 
0337   /// add the rpc at doubletR = 1
0338   auto placeRpc = [&](const double currentX, unsigned dRIdx) {
0339     const double stepdZ = m_chamberLength / m_cfg.nRpcAlongZ;
0340     const double stepdY = envelopeWidth / m_cfg.nRpcAlongPhi;
0341 
0342     for (unsigned int dZ = 0; dZ < m_cfg.nRpcAlongZ; ++dZ) {
0343       for (unsigned int dY = 0; dY < m_cfg.nRpcAlongPhi; ++dY) {
0344         envelopeVol->add(makeTransform(GeoTrf::Translate3D(
0345             currentX, -0.5 * envelopeWidth + stepdY * (dY + 0.5),
0346             -0.5 * m_chamberLength + stepdZ * (dZ + 0.5))));
0347         std::string publishName =
0348             to_string(layer) + "_RPC_" + std::to_string(etaIdx) + "_" +
0349             std::to_string(sector) + "_" + std::to_string(dRIdx) + "_" +
0350             std::to_string(dY) + "_" + std::to_string(dZ);
0351         publishFPV(envelopeVol, assembleRpcChamber(envelopeWidth), publishName);
0352       }
0353     }
0354   };
0355 
0356   double currentX = -box->getXHalfLength();
0357   currentX += 0.5 * m_rpcChamberHeight;
0358   placeRpc(currentX, 1);
0359   currentX += m_rpcChamberHeight + s_rpcMdtDistance;
0360   currentX += 0.5 * m_multiLayerHeight;
0361   envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0362   publishFPV(envelopeVol, assembleMultilayerBarrel(1, envelopeWidth),
0363              to_string(layer) + "_BMDT_" + std::to_string(etaIdx) + "_" +
0364                  std::to_string(sector) + "_1");
0365   currentX += 0.5 * m_multiLayerHeight + m_cfg.multiLayerSeparation +
0366               0.5 * m_multiLayerHeight;
0367   envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0368   publishFPV(envelopeVol, assembleMultilayerBarrel(2, envelopeWidth),
0369              to_string(layer) + "_BMDT_" + std::to_string(etaIdx) + "_" +
0370                  std::to_string(sector) + "_2");
0371   currentX += m_rpcChamberHeight + s_rpcMdtDistance;
0372   currentX += 0.5 * m_multiLayerHeight;
0373   placeRpc(currentX, 2);
0374   return envelopeVol;
0375 }
0376 
0377 PVLink GeoMuonMockupExperiment::assembleTube(const double tubeLength) {
0378   auto* matMan = MaterialManager::getManager();
0379 
0380   auto outerTube =
0381       make_intrusive<GeoTube>(0., m_outerTubeRadius, 0.5 * tubeLength);
0382   auto outerTubeLogVol = make_intrusive<GeoLogVol>(
0383       "MdtDriftWall", outerTube, matMan->getMaterial("std::Aluminium"));
0384   auto outerTubeVol = make_intrusive<GeoPhysVol>(cacheVolume(outerTubeLogVol));
0385   /// Place the drift gas inside the outer tube
0386   auto innerTube =
0387       make_intrusive<GeoTube>(0., m_cfg.innerTubeRadius, 0.5 * tubeLength);
0388   auto innerTubeLogVol = make_intrusive<GeoLogVol>(
0389       "MDTDriftGas", innerTube, matMan->getMaterial("std::ArCO2"));
0390   outerTubeVol->add(make_intrusive<GeoPhysVol>(cacheVolume(innerTubeLogVol)));
0391   return cacheVolume(outerTubeVol);
0392 }
0393 
0394 PVLink GeoMuonMockupExperiment::buildTubes(const double lowerTubeLength,
0395                                            const double upperTubeLength) {
0396   auto* matMan = MaterialManager::getManager();
0397   GeoShapePtr envShape{};
0398   if (std::abs(lowerTubeLength - upperTubeLength) <
0399       std::numeric_limits<double>::epsilon()) {
0400     envShape = make_intrusive<GeoBox>(
0401         0.5 * m_tubeLayersHeight, 0.5 * m_chamberLength, 0.5 * lowerTubeLength);
0402   } else {
0403     envShape = make_intrusive<GeoTrd>(
0404         0.5 * m_tubeLayersHeight, 0.5 * m_tubeLayersHeight,
0405         0.5 * lowerTubeLength, 0.5 * upperTubeLength, 0.5 * m_chamberLength);
0406   }
0407   auto tubeLogVol = make_intrusive<GeoLogVol>(
0408       "MdtTubeEnvelope", cacheShape(envShape), matMan->getMaterial("std::air"));
0409 
0410   auto envelopeVol = make_intrusive<GeoPhysVol>(cacheVolume(tubeLogVol));
0411   /// Place the tubes inside the envelope
0412   const Acts::Vector3 posStag{
0413       m_tubePitch * std::sin(60. * GeoModelKernelUnits::deg),
0414       m_tubePitch * std::cos(60. * GeoModelKernelUnits::deg), 0.};
0415   const Acts::Vector3 negStag{
0416       m_tubePitch * std::sin(60. * GeoModelKernelUnits::deg),
0417       -m_tubePitch * std::cos(60. * GeoModelKernelUnits::deg), 0.};
0418 
0419   Acts::Vector3 firstTubePos{-0.5 * m_tubeLayersHeight + 0.5 * m_tubePitch,
0420                              -0.5 * m_chamberLength + 0.5 * m_tubePitch, 0.};
0421 
0422   //// Put the tube into a separate container
0423   auto toyBox = make_intrusive<GeoBox>(10. * GeoModelKernelUnits::cm,
0424                                        10. * GeoModelKernelUnits::cm,
0425                                        10. * GeoModelKernelUnits::cm);
0426   auto toyBoxLogVol = cacheVolume(
0427       make_intrusive<GeoLogVol>("TubeLayerLog", cacheShape(toyBox),
0428                                 matMan->getMaterial("special::Ether")));
0429 
0430   const double dTube = (upperTubeLength - lowerTubeLength) / (m_cfg.nTubes - 1);
0431 
0432   GeoGenfun::Variable K;
0433   GeoGenfun::GENFUNCTION F = K * m_tubePitch;
0434   GeoXF::TRANSFUNCTION T = GeoXF::Pow(GeoTrf::TranslateY3D(1.0), F);
0435 
0436   auto barrelTubeLayer = make_intrusive<GeoSerialTransformer>(
0437       assembleTube(lowerTubeLength - 1. * GeoModelKernelUnits::cm), &T,
0438       m_cfg.nTubes);
0439 
0440   for (unsigned tL = 0; tL < m_cfg.nTubeLayers; ++tL) {
0441     auto layerVol = make_intrusive<GeoPhysVol>(toyBoxLogVol);
0442     /// For endcap chambers the tube need to be placed individually
0443     if (envShape->typeID() == GeoTrd::getClassTypeID()) {
0444       envelopeVol->add(makeTransform(GeoTrf::RotateX3D(rot90deg)));
0445       for (unsigned t = 0; t < m_cfg.nTubes; ++t) {
0446         layerVol->add(makeTransform(GeoTrf::TranslateY3D(t * m_tubePitch)));
0447         layerVol->add(assembleTube(lowerTubeLength + dTube * t));
0448       }
0449     } else {
0450       /// Simple serial transformer for the barrel
0451       layerVol->add(serialId(tL));
0452       layerVol->add(barrelTubeLayer);
0453     }
0454     envelopeVol->add(makeTransform(GeoTrf::Translate3D(firstTubePos)));
0455     envelopeVol->add(cacheVolume(layerVol));
0456     firstTubePos = firstTubePos + ((tL % 2) != 0 ? posStag : negStag);
0457   }
0458   return cacheVolume(envelopeVol);
0459 }
0460 
0461 PVLink GeoMuonMockupExperiment::buildAbsorber(const double thickness,
0462                                               const double widthS,
0463                                               const double widthL,
0464                                               const double length) {
0465   GeoShapePtr shape{};
0466   if (std::abs(widthS - widthL) < std::numeric_limits<double>::epsilon()) {
0467     shape = make_intrusive<GeoBox>(0.5 * thickness, 0.5 * widthS, 0.5 * length);
0468   } else {
0469     shape = make_intrusive<GeoTrd>(0.5 * thickness, 0.5 * thickness,
0470                                    0.5 * widthS, 0.5 * widthL, 0.5 * length);
0471   }
0472   auto logVol = cacheVolume(make_intrusive<GeoLogVol>(
0473       "PassiveMat", cacheShape(shape),
0474       MaterialManager::getManager()->getMaterial("std::Forex")));
0475   return cacheVolume(make_intrusive<GeoPhysVol>(logVol));
0476 }
0477 
0478 FpvLink GeoMuonMockupExperiment::assembleRpcChamber(const double chamberWidth) {
0479   auto* matMan = MaterialManager::getManager();
0480   constexpr double margin = 0.98;
0481   auto rpcBox = make_intrusive<GeoBox>(
0482       0.5 * m_rpcChamberHeight,
0483       0.5 * chamberWidth / m_cfg.nRpcAlongPhi * margin,
0484       0.5 * (m_chamberLength / m_cfg.nRpcAlongZ * margin));
0485   auto envLogVol = cacheVolume(make_intrusive<GeoLogVol>(
0486       "RpcChamber", cacheShape(rpcBox), matMan->getMaterial("std::Copper")));
0487   auto rpcEnvelope = make_intrusive<GeoFullPhysVol>(envLogVol);
0488   ///
0489   double currentX = -rpcBox->getXHalfLength() + 0.5 * s_rpcGasSingletSeparation;
0490 
0491   for (unsigned int gap = 1; gap <= m_cfg.nRpcGasGaps; ++gap) {
0492     currentX += 0.5 * s_rpcGasHeight;
0493 
0494     auto gasBox =
0495         make_intrusive<GeoBox>(0.5 * s_rpcGasHeight, rpcBox->getYHalfLength(),
0496                                rpcBox->getZHalfLength());
0497     auto gasLogVol = cacheVolume(make_intrusive<GeoLogVol>(
0498         "RpcGasGap", cacheShape(gasBox), matMan->getMaterial("std::ArCO2")));
0499 
0500     rpcEnvelope->add(geoId(gap));
0501     rpcEnvelope->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0502     rpcEnvelope->add(cacheVolume(make_intrusive<GeoPhysVol>(gasLogVol)));
0503 
0504     currentX += 0.5 * s_rpcGasHeight;
0505     if (gap == m_cfg.nRpcGasGaps) {
0506       break;
0507     }
0508     currentX += 0.5 * s_rpcGasSingletSeparation;
0509     rpcEnvelope->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0510     rpcEnvelope->add(buildAbsorber(
0511         s_rpcGasSingletSeparation, 2. * rpcBox->getYHalfLength(),
0512         2. * rpcBox->getYHalfLength(), 2. * rpcBox->getZHalfLength()));
0513     currentX += 0.5 * s_rpcGasSingletSeparation;
0514   }
0515   return rpcEnvelope;
0516 };
0517 FpvLink GeoMuonMockupExperiment::assembleMultilayerEndcap(
0518     const unsigned ml, const double lowerTubeLength,
0519     const double upperTubeLength) {
0520   auto* matMan = MaterialManager::getManager();
0521   auto envelopeTrd = make_intrusive<GeoTrd>(
0522       0.5 * m_multiLayerHeight, 0.5 * m_multiLayerHeight,
0523       0.5 * lowerTubeLength + 0.05 * GeoModelKernelUnits::mm,
0524       0.5 * upperTubeLength + 0.05 * GeoModelKernelUnits::mm,
0525       0.5 * m_chamberLength);
0526 
0527   auto envelopeLogVol =
0528       make_intrusive<GeoLogVol>("MultilayerEnv", cacheShape(envelopeTrd),
0529                                 matMan->getMaterial("std::air"));
0530 
0531   auto envelopeVol =
0532       make_intrusive<GeoFullPhysVol>(cacheVolume(envelopeLogVol));
0533 
0534   double currentX = -envelopeTrd->getXHalfLength1();
0535   if (ml == 1 && m_cfg.mdtFoamThickness > 0.) {
0536     currentX += 0.5 * m_cfg.mdtFoamThickness;
0537     envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0538     envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, lowerTubeLength,
0539                                    upperTubeLength, m_chamberLength));
0540     currentX += 0.5 * m_cfg.mdtFoamThickness + s_mdtFoamTubeDistance;
0541   }
0542   currentX += 0.5 * m_tubeLayersHeight;
0543   envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0544   envelopeVol->add(buildTubes(lowerTubeLength, upperTubeLength));
0545   if (ml == 2 && m_cfg.mdtFoamThickness > 0.) {
0546     currentX += 0.5 * m_tubeLayersHeight + 0.5 * m_cfg.mdtFoamThickness +
0547                 s_mdtFoamTubeDistance;
0548     envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0549     envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, lowerTubeLength,
0550                                    upperTubeLength, m_chamberLength));
0551   }
0552   return envelopeVol;
0553 }
0554 FpvLink GeoMuonMockupExperiment::assembleMultilayerBarrel(
0555     const unsigned ml, const double tubeLength) {
0556   auto* matMan = MaterialManager::getManager();
0557 
0558   const double envelopeWidth = tubeLength + 0.05 * GeoModelKernelUnits::mm;
0559 
0560   auto envelopeBox = make_intrusive<GeoBox>(
0561       0.5 * m_multiLayerHeight, 0.5 * envelopeWidth, 0.5 * m_chamberLength);
0562   auto envelopeLogVol =
0563       make_intrusive<GeoLogVol>("MultilayerEnv", cacheShape(envelopeBox),
0564                                 matMan->getMaterial("std::air"));
0565 
0566   auto envelopeVol =
0567       make_intrusive<GeoFullPhysVol>(cacheVolume(envelopeLogVol));
0568 
0569   double currentX = -envelopeBox->getXHalfLength();
0570   if (ml == 1 && m_cfg.mdtFoamThickness > 0.) {
0571     currentX += 0.5 * m_cfg.mdtFoamThickness;
0572     envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0573     envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, envelopeWidth,
0574                                    envelopeWidth, m_chamberLength));
0575     currentX += 0.5 * m_cfg.mdtFoamThickness + s_mdtFoamTubeDistance;
0576   }
0577   PVLink tubeVol = buildTubes(envelopeWidth, envelopeWidth);
0578   currentX += 0.5 * m_tubeLayersHeight;
0579   envelopeVol->add(makeTransform(GeoTrf::RotateX3D(rot90deg) *
0580                                  GeoTrf::TranslateX3D(currentX)));
0581   envelopeVol->add(tubeVol);
0582   if (ml == 2 && m_cfg.mdtFoamThickness > 0.) {
0583     currentX += 0.5 * m_tubeLayersHeight + 0.5 * m_cfg.mdtFoamThickness +
0584                 s_mdtFoamTubeDistance;
0585     envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0586     envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, envelopeWidth,
0587                                    envelopeWidth, m_chamberLength));
0588   }
0589   return envelopeVol;
0590 }
0591 }  // namespace ActsExamples