File indexing completed on 2025-12-18 09:27:09
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/MuonSpectrometerMockupDetector/GeoMuonMockupExperiment.hpp"
0010
0011 #include "Acts/Utilities/Helpers.hpp"
0012 #include "Acts/Utilities/MathHelpers.hpp"
0013 #include "Acts/Utilities/StringHelpers.hpp"
0014
0015 #include <format>
0016 #include <iostream>
0017
0018 #include "GeoGenericFunctions/Variable.h"
0019 #include "GeoModelHelpers/MaterialManager.h"
0020 #include "GeoModelHelpers/defineWorld.h"
0021 #include "GeoModelHelpers/printVolume.h"
0022 #include "GeoModelIOHelpers/GMIO.h"
0023 #include "GeoModelKernel/GeoBox.h"
0024 #include "GeoModelKernel/GeoSerialTransformer.h"
0025 #include "GeoModelKernel/GeoTransform.h"
0026 #include "GeoModelKernel/GeoTrd.h"
0027 #include "GeoModelKernel/GeoTube.h"
0028 #include "GeoModelKernel/GeoXF.h"
0029 #include "GeoModelKernel/throwExcept.h"
0030
0031 using namespace Acts;
0032 namespace {
0033 constexpr double rot90deg = 90. * GeoModelKernelUnits::deg;
0034 }
0035 namespace ActsExamples {
0036
0037 std::string to_string(GeoMuonMockupExperiment::MuonLayer layer) {
0038 switch (layer) {
0039 using enum GeoMuonMockupExperiment::MuonLayer;
0040 case Inner:
0041 return "Inner";
0042 case Middle:
0043 return "Middle";
0044 case Outer:
0045 return "Outer";
0046 case nLayers:
0047 return "nLayers";
0048 }
0049 return "UNKNOWN";
0050 }
0051
0052 using FpvLink = GeoMuonMockupExperiment::FpvLink;
0053
0054 GeoMuonMockupExperiment::GeoMuonMockupExperiment(
0055 const Config& cfg, std::unique_ptr<const Acts::Logger> logger)
0056 : m_cfg{cfg}, m_logger{std::move(logger)} {}
0057 ActsPlugins::GeoModelTree GeoMuonMockupExperiment::constructMS() {
0058 const double worldR = m_cfg.barrelRadii[2] + 1. * GeoModelKernelUnits::m;
0059
0060 const double barrelZ =
0061 (m_cfg.nEtaStations + 1) * (m_chamberLength + m_cfg.stationDistInZ) +
0062 0.5 * GeoModelKernelUnits::m;
0063 const double worldZ =
0064 barrelZ + m_cfg.bigWheelDistZ + 2. * m_stationHeightEndcap;
0065
0066 PVLink world = createGeoWorld(worldR, worldR, worldZ);
0067
0068 setupMaterials();
0069
0070 m_publisher->setName("Muon");
0071
0072 const double dX = 2. * (m_cfg.barrelRadii[2] - 0.5 * m_stationHeightBarrel) *
0073 std::sin(0.5 * m_sectorSize);
0074
0075 double outerRadius = Acts::fastHypot(
0076 m_cfg.barrelRadii[2] + 0.5 * m_stationHeightBarrel, 0.5 * dX);
0077
0078 auto barrelCylinder = make_intrusive<GeoTube>(
0079 (m_cfg.barrelRadii[0] - 0.5 * m_stationHeightBarrel), outerRadius,
0080 (m_cfg.nEtaStations + 1) * (m_chamberLength + m_cfg.stationDistInZ));
0081 auto barrelLogVol = make_intrusive<GeoLogVol>(
0082 "BarrelEnvelope", barrelCylinder,
0083 MaterialManager::getManager()->getMaterial("std::air"));
0084
0085 auto barrelEnvelope = make_intrusive<GeoPhysVol>(barrelLogVol);
0086
0087 auto toyBox = make_intrusive<GeoBox>(10. * GeoModelKernelUnits::cm,
0088 10. * GeoModelKernelUnits::cm,
0089 10. * GeoModelKernelUnits::cm);
0090 auto muonEnvelope = make_intrusive<GeoPhysVol>(make_intrusive<GeoLogVol>(
0091 "MuonEnvelope", cacheShape(toyBox),
0092 MaterialManager::getManager()->getMaterial("special::Ether")));
0093
0094
0095
0096
0097
0098
0099 const GeoTrf::Transform3D ActsToGeomodelChamberFrame{
0100 GeoTrf::GeoRotation{rot90deg, rot90deg, 0.}};
0101 for (MuonLayer layer :
0102 {MuonLayer::Inner, MuonLayer::Middle, MuonLayer::Outer}) {
0103 for (unsigned sector = 1; sector <= m_cfg.nSectors; ++sector) {
0104 for (unsigned etaIdx = 1; etaIdx <= m_cfg.nEtaStations; ++etaIdx) {
0105 const double z_displacement =
0106 0.25 * m_chamberLength +
0107 etaIdx * (m_chamberLength + m_cfg.stationDistInZ);
0108 const double radius = m_cfg.barrelRadii[toUnderlying(layer)];
0109 barrelEnvelope->add(makeTransform(
0110 GeoTrf::TranslateZ3D(z_displacement) *
0111 GeoTrf::RotateZ3D(sector * m_sectorSize) *
0112 GeoTrf::TranslateX3D(radius) * ActsToGeomodelChamberFrame));
0113 barrelEnvelope->add(assembleBarrelStation(layer, sector, etaIdx));
0114
0115 barrelEnvelope->add(
0116 makeTransform(GeoTrf::TranslateZ3D(-z_displacement) *
0117 GeoTrf::RotateZ3D(sector * m_sectorSize) *
0118 GeoTrf::TranslateX3D(radius) *
0119 GeoTrf::RotateX3D(180. * GeoModelKernelUnits::deg) *
0120 ActsToGeomodelChamberFrame));
0121 barrelEnvelope->add(
0122 assembleBarrelStation(layer, sector, -static_cast<int>(etaIdx)));
0123 }
0124 }
0125 }
0126 muonEnvelope->add(barrelEnvelope);
0127
0128 if (m_cfg.buildEndcaps) {
0129 const double midWheelZ = barrelZ + 0.5 * m_stationHeightEndcap;
0130 const double outWheelZ =
0131 midWheelZ + m_stationHeightEndcap + m_cfg.bigWheelDistZ;
0132 using enum ActsExamples::GeoMuonMockupExperiment::MuonLayer;
0133 assembleBigWheel(muonEnvelope, Middle, midWheelZ);
0134 assembleBigWheel(muonEnvelope, Middle, -midWheelZ);
0135 assembleBigWheel(muonEnvelope, Outer, outWheelZ);
0136 assembleBigWheel(muonEnvelope, Outer, -outWheelZ);
0137 const double innerWheelZ = 0.8 * barrelZ;
0138 const double innerWheelR =
0139 0.95 * m_cfg.barrelRadii[toUnderlying(MuonLayer::Inner)];
0140 assembleSmallWheel(muonEnvelope, innerWheelR, innerWheelZ);
0141 assembleSmallWheel(muonEnvelope, innerWheelR, -innerWheelZ);
0142 }
0143 const unsigned nChambers =
0144 2 * m_cfg.nSectors * m_cfg.nEtaStations *
0145 static_cast<unsigned>(MuonLayer::nLayers);
0146 const unsigned nMultiLayers = 2 * nChambers;
0147 const unsigned nTubes = nMultiLayers * m_cfg.nTubeLayers * m_cfg.nTubes;
0148 const unsigned nRpc = 2 * nChambers * m_cfg.nRpcAlongZ * m_cfg.nRpcAlongPhi;
0149 ACTS_INFO("Constructed a muon system with "
0150 << nChambers << " muon stations containing in total "
0151 << nMultiLayers << " Mdt multilayers & " << nRpc
0152 << " Rpc chambers. Total: " << (nMultiLayers + nRpc));
0153 ACTS_INFO("Each multilayer contains "
0154 << m_cfg.nTubeLayers << " tube-layers with " << m_cfg.nTubes
0155 << " tubes each giving in total " << nTubes << " placed tubes.");
0156
0157 world->add(nameTag(m_publisher->getName()));
0158 world->add(muonEnvelope);
0159
0160 ACTS_VERBOSE("Printout of the entire world \n " << printVolume(world));
0161
0162 clearSharedCaches();
0163 ActsPlugins::GeoModelTree outTree{};
0164 outTree.worldVolume = world;
0165
0166 using VolumeMap_t = ActsPlugins::GeoModelTree::VolumePublisher::VolumeMap_t;
0167 VolumeMap_t publishedVol{};
0168 for (const auto& [fpV, pubKey] : m_publisher->getPublishedFPV()) {
0169 try {
0170 const auto key = std::any_cast<std::string>(pubKey);
0171 if (!publishedVol
0172 .insert(std::make_pair(key, static_cast<GeoFullPhysVol*>(fpV)))
0173 .second) {
0174 throw std::invalid_argument("GeoMuonMockupExperiment() - Key " + key +
0175 " is no longer unique");
0176 }
0177 } catch (const std::bad_any_cast& e) {
0178 throw std::domain_error(
0179 "GeoMuonMockupExperiment() - Failed to cast the key to string " +
0180 std::string{e.what()});
0181 }
0182 }
0183 outTree.publisher->publishVolumes(m_publisher->getName(),
0184 std::move(publishedVol));
0185
0186 if (m_cfg.dumpTree) {
0187
0188 GMDBManager db{m_cfg.dbName};
0189
0190 if (!db.checkIsDBOpen()) {
0191 throw std::runtime_error(
0192 "GeoMuonMockupExperiment::constructMS() - It was not possible to "
0193 "open the DB correctly!");
0194 }
0195
0196 GeoModelIO::WriteGeoModel writeGeoDB{db};
0197 world->exec(&writeGeoDB);
0198 writeGeoDB.saveToDB(m_publisher.get());
0199 }
0200
0201 m_publisher.reset();
0202 return outTree;
0203 }
0204 PVLink GeoMuonMockupExperiment::assembleEndcapStation(const double lowR,
0205 const MuonLayer layer,
0206 const unsigned sector,
0207 const int etaIdx) {
0208 const double angularScale = 2. * std::sin(0.5 * m_sectorSize);
0209
0210 const double lowTubeLength = angularScale * lowR;
0211 const double upperTubeLength = angularScale * (lowR + m_chamberLength);
0212
0213 auto envelopeTrd = make_intrusive<GeoTrd>(
0214 0.5 * m_stationHeightEndcap, 0.5 * m_stationHeightEndcap,
0215 0.5 * lowTubeLength, 0.5 * upperTubeLength, 0.5 * m_chamberLength);
0216 auto logVol = make_intrusive<GeoLogVol>(
0217 "MuonEndcapStation", cacheShape(envelopeTrd),
0218 MaterialManager::getManager()->getMaterial("std::air"));
0219 auto envelopeVol = make_intrusive<GeoPhysVol>(cacheVolume(logVol));
0220 double currentX = -envelopeTrd->getXHalfLength1() + 0.5 * m_tgcChamberHeight;
0221 if (layer == MuonLayer::Middle) {
0222 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0223 publishFPV(envelopeVol, assembleTgcChamber(lowTubeLength, upperTubeLength),
0224 std::format("TGC_T1E_{:}_{:}", etaIdx, sector));
0225 }
0226 currentX +=
0227 0.5 * m_tgcChamberHeight + s_tgcMdtSeparation + 0.5 * m_multiLayerHeight;
0228
0229 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0230 publishFPV(envelopeVol,
0231 assembleMultilayerEndcap(1, lowTubeLength, upperTubeLength),
0232 std::format("{}_EMDT_{}_{}_1", to_string(layer), etaIdx, sector));
0233
0234 currentX += 0.5 * m_multiLayerHeight + m_cfg.multiLayerSeparation +
0235 0.5 * m_multiLayerHeight;
0236 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0237 publishFPV(envelopeVol,
0238 assembleMultilayerEndcap(2, lowTubeLength, upperTubeLength),
0239 std::format("{}_EMDT_{}_{}_2", to_string(layer), etaIdx, sector));
0240 currentX += s_tgcMdtSeparation + 0.5 * m_tgcChamberHeight;
0241 if (layer == MuonLayer::Middle) {
0242 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0243 publishFPV(envelopeVol, assembleTgcChamber(lowTubeLength, upperTubeLength),
0244 std::format("TGC_T2E_{:}_{:}", etaIdx, sector));
0245 currentX += 0.5 * m_tgcChamberHeight + s_tgcChamberSeparation +
0246 0.5 * m_tgcChamberHeight;
0247 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0248 publishFPV(envelopeVol, assembleTgcChamber(lowTubeLength, upperTubeLength),
0249 std::format("TGC_T3E_{:}_{:}", etaIdx, sector));
0250 }
0251 return envelopeVol;
0252 }
0253 FpvLink GeoMuonMockupExperiment::assembleTgcChamber(const double bottomWidth,
0254 const double topWidth) {
0255 auto envelopeTrd = make_intrusive<GeoTrd>(
0256 0.5 * m_tgcChamberHeight, 0.5 * m_tgcChamberHeight, 0.5 * bottomWidth,
0257 0.5 * topWidth, 0.48 * m_chamberLength);
0258 auto logVol = make_intrusive<GeoLogVol>(
0259 "TgcEnvelope", cacheShape(envelopeTrd),
0260 MaterialManager::getManager()->getMaterial("std::G10"));
0261 auto tgcPhysVol = make_intrusive<GeoFullPhysVol>(cacheVolume(logVol));
0262 double currentX =
0263 -envelopeTrd->getXHalfLength1() + 0.5 * s_tgcGasSingletSeparation;
0264 for (unsigned gap = 1; gap <= m_cfg.nTgcGasGaps; ++gap) {
0265 currentX += 0.5 * s_tgcGasHeight;
0266
0267 auto gasTrd =
0268 make_intrusive<GeoTrd>(0.5 * s_tgcGasHeight, 0.5 * s_tgcGasHeight,
0269 0.95 * envelopeTrd->getYHalfLength1(),
0270 0.95 * envelopeTrd->getYHalfLength2(),
0271 0.95 * envelopeTrd->getZHalfLength());
0272 auto gasLogVol = cacheVolume(make_intrusive<GeoLogVol>(
0273 "TgcGasGap", cacheShape(gasTrd),
0274 MaterialManager::getManager()->getMaterial("std::ArCO2")));
0275
0276 tgcPhysVol->add(geoId(gap));
0277 tgcPhysVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0278 tgcPhysVol->add(cacheVolume(make_intrusive<GeoPhysVol>(gasLogVol)));
0279
0280 currentX += 0.5 * s_tgcGasHeight + s_tgcGasSingletSeparation;
0281 }
0282
0283 return tgcPhysVol;
0284 }
0285
0286 void GeoMuonMockupExperiment::assembleBigWheel(const PVLink& envelopeVol,
0287 const MuonLayer layer,
0288 const double wheelZ) {
0289 envelopeVol->add(makeTransform(GeoTrf::TranslateZ3D(wheelZ)));
0290 const double lowR = m_cfg.endCapWheelLowR;
0291 const double effR = (m_chamberLength + m_cfg.stationDistInR);
0292
0293 const unsigned nEta =
0294 1 +
0295 static_cast<unsigned>(
0296 (m_cfg.barrelRadii[toUnderlying(MuonLayer::Outer)] - lowR) / effR);
0297 const double highR = lowR + nEta * effR;
0298 auto envelopeShape =
0299 make_intrusive<GeoTube>(lowR, highR, 0.5 * m_stationHeightEndcap);
0300 auto envelopeLogVol = make_intrusive<GeoLogVol>(
0301 "EndcapEnvelope", cacheShape(envelopeShape),
0302 MaterialManager::getManager()->getMaterial("std::air"));
0303
0304 auto wheelEnvelope = make_intrusive<GeoPhysVol>(cacheVolume(envelopeLogVol));
0305
0306 for (unsigned stationEta = 0; stationEta < nEta; ++stationEta) {
0307 const double radius = lowR + stationEta * effR;
0308 for (unsigned sector = 1; sector <= m_cfg.nSectors; ++sector) {
0309 if (wheelZ > 0) {
0310 wheelEnvelope->add(
0311 makeTransform(GeoTrf::RotateZ3D(sector * m_sectorSize) *
0312 GeoTrf::TranslateX3D(radius + 0.5 * m_chamberLength) *
0313 GeoTrf::RotateY3D(90. * GeoModelKernelUnits::deg) *
0314 GeoTrf::RotateZ3D(180. * GeoModelKernelUnits::deg)));
0315 } else {
0316 wheelEnvelope->add(
0317 makeTransform(GeoTrf::RotateZ3D(sector * m_sectorSize) *
0318 GeoTrf::TranslateX3D(radius + 0.5 * m_chamberLength) *
0319 GeoTrf::RotateY3D(90. * GeoModelKernelUnits::deg)));
0320 }
0321 const int castEta =
0322 copySign(1, wheelZ) * static_cast<int>(stationEta + 1);
0323 wheelEnvelope->add(assembleEndcapStation(radius, layer, sector, castEta));
0324 }
0325 }
0326 envelopeVol->add(wheelEnvelope);
0327 }
0328
0329 void GeoMuonMockupExperiment::setupMaterials() {
0330 auto* matMan = MaterialManager::getManager();
0331
0332 matMan->setMaterialNamespace("std");
0333 ACTS_DEBUG("Create the chemical elements.");
0334
0335
0336 matMan->addElement("Carbon", "C", 6, 12.0112);
0337 matMan->addElement("Aluminium", "Al", 13, 26.9815);
0338 matMan->addElement("Iron", "Fe", 26, 55.847);
0339 matMan->addElement("Copper", "Cu", 29, 63.54);
0340 matMan->addElement("Nitrogen", "N", 7.0, 14.0031);
0341 matMan->addElement("Oxygen", "O", 8.0, 15.9949);
0342 matMan->addElement("Argon", "Ar", 18.0, 39.9624);
0343 matMan->addElement("Hydrogen", "H", 1.0, 1.00782503081372);
0344 matMan->addElement("Chlorine", "Cl", 17.0, 35.453);
0345 matMan->addElement("Fluorine", "F", 9., 18.9984);
0346 using MatComposition_t = std::vector<std::pair<std::string, double>>;
0347
0348 auto appendMaterial = [matMan, this](const std::string& matName,
0349 const MatComposition_t& composition,
0350 const double density) {
0351 ACTS_DEBUG("Create new material " << matName << " with density "
0352 << density);
0353 matMan->addMaterial(matName, density);
0354 for (const auto& [compName, fraction] : composition) {
0355 ACTS_DEBUG("Try to add new material component "
0356 << compName << " contributing by " << fraction
0357 << " parts to the total material");
0358 matMan->addMatComponent(compName, fraction);
0359 }
0360 ACTS_DEBUG("Material defined.");
0361 matMan->lockMaterial();
0362 };
0363 appendMaterial("air",
0364 {{"Nitrogen", 0.7494},
0365 {"Oxygen", 0.2369},
0366 {"Argon", 0.0129},
0367 {"Hydrogen", 0.0008}},
0368 0.001290);
0369 appendMaterial("Aluminium", {{"Aluminium", 1.}}, 2.7);
0370 appendMaterial("Copper", {{"Copper", 1.}}, 8.96);
0371 appendMaterial("CO2", {{"Carbon", 1.}, {"Oxygen", 2.}}, 0.00184);
0372 appendMaterial("ArCO2", {{"Argon", .93}, {"std::CO2", .07}}, .0054);
0373
0374 appendMaterial("Forex",
0375 {{"Carbon", 0.3843626433635827},
0376 {"Hydrogen", 0.0483830941493594},
0377 {"Chlorine", 0.5672542624870579}},
0378 0.7);
0379 appendMaterial("G10", {{"Carbon", 2}, {"Hydrogen", 2}, {"Fluorine", 4}}, 2);
0380
0381 if (logger().level() == Acts::Logging::Level::DEBUG) {
0382 matMan->printAll();
0383 }
0384 }
0385 void GeoMuonMockupExperiment::publishFPV(const PVLink& envelopeVol,
0386 const FpvLink& publishMe,
0387 const std::string& pubName) {
0388 m_publisher->publishNode(static_cast<GeoVFullPhysVol*>(publishMe.get()),
0389 pubName);
0390 ACTS_DEBUG("Publish new volume " << pubName);
0391 envelopeVol->add(nameTag(pubName));
0392 envelopeVol->add(publishMe);
0393 }
0394 PVLink GeoMuonMockupExperiment::assembleBarrelStation(const MuonLayer layer,
0395 const unsigned sector,
0396 const int etaIdx) {
0397
0398
0399
0400 const double envelopeWidth =
0401 2. *
0402 (m_cfg.barrelRadii[toUnderlying(layer)] - 0.5 * m_stationHeightBarrel) *
0403 std::sin(0.5 * m_sectorSize);
0404
0405 auto box = make_intrusive<GeoBox>(
0406 0.5 * envelopeWidth,
0407 0.5 * m_chamberLength + 0.1 * GeoModelKernelUnits::mm,
0408 0.5 * m_stationHeightBarrel);
0409 auto logVol = make_intrusive<GeoLogVol>(
0410 "MuonBarrelLogVol", cacheShape(box),
0411 MaterialManager::getManager()->getMaterial("std::air"));
0412 auto envelopeVol = make_intrusive<GeoPhysVol>(cacheVolume(logVol));
0413
0414
0415 auto placeRpc = [&](const double currentZ, unsigned dRIdx) {
0416 const double stepdY = m_chamberLength / m_cfg.nRpcAlongZ;
0417 const double stepdX = envelopeWidth / m_cfg.nRpcAlongPhi;
0418
0419 for (unsigned dY = 0; dY < m_cfg.nRpcAlongZ; ++dY) {
0420 for (unsigned dX = 0; dX < m_cfg.nRpcAlongPhi; ++dX) {
0421 envelopeVol->add(makeTransform(GeoTrf::Translate3D(
0422 -0.5 * envelopeWidth + stepdX * (dX + 0.5),
0423 -0.5 * m_chamberLength + stepdY * (dY + 0.5), currentZ)));
0424 publishFPV(envelopeVol, assembleRpcChamber(envelopeWidth),
0425 std::format("{:}_RPC_{:}_{:}_{:}_{:}_{:}", to_string(layer),
0426 etaIdx, sector, dRIdx, dX, dY));
0427 }
0428 }
0429 };
0430
0431 double currentZ = -box->getZHalfLength();
0432 currentZ += 0.5 * m_rpcChamberHeight;
0433 placeRpc(currentZ, 1);
0434 currentZ +=
0435 0.5 * m_rpcChamberHeight + s_rpcMdtDistance + 0.5 * m_multiLayerHeight;
0436 envelopeVol->add(makeTransform(GeoTrf::TranslateZ3D(currentZ)));
0437 publishFPV(envelopeVol, assembleMultilayerBarrel(1, envelopeWidth),
0438 std::format("{}_BMDT_{}_{}_1", to_string(layer), etaIdx, sector));
0439 currentZ += 0.5 * m_multiLayerHeight + m_cfg.multiLayerSeparation +
0440 0.5 * m_multiLayerHeight;
0441 envelopeVol->add(makeTransform(GeoTrf::TranslateZ3D(currentZ)));
0442 publishFPV(envelopeVol, assembleMultilayerBarrel(2, envelopeWidth),
0443 std::format("{}_BMDT_{}_{}_2", to_string(layer), etaIdx, sector));
0444 currentZ +=
0445 0.5 * m_rpcChamberHeight + s_rpcMdtDistance + 0.5 * m_multiLayerHeight;
0446 placeRpc(currentZ, 2);
0447 return envelopeVol;
0448 }
0449
0450 PVLink GeoMuonMockupExperiment::assembleTube(const double tubeLength) {
0451 auto* matMan = MaterialManager::getManager();
0452
0453 auto outerTube =
0454 make_intrusive<GeoTube>(0., m_outerTubeRadius, 0.5 * tubeLength);
0455 auto outerTubeLogVol = make_intrusive<GeoLogVol>(
0456 "MdtDriftWall", outerTube, matMan->getMaterial("std::Aluminium"));
0457 auto outerTubeVol = make_intrusive<GeoPhysVol>(cacheVolume(outerTubeLogVol));
0458
0459 auto innerTube =
0460 make_intrusive<GeoTube>(0., m_cfg.innerTubeRadius, 0.5 * tubeLength);
0461 auto innerTubeLogVol = make_intrusive<GeoLogVol>(
0462 "MDTDriftGas", innerTube, matMan->getMaterial("std::ArCO2"));
0463 outerTubeVol->add(make_intrusive<GeoPhysVol>(cacheVolume(innerTubeLogVol)));
0464 return cacheVolume(outerTubeVol);
0465 }
0466
0467 PVLink GeoMuonMockupExperiment::buildTubes(const double lowerTubeLength,
0468 const double upperTubeLength) {
0469 auto* matMan = MaterialManager::getManager();
0470 GeoShapePtr envShape{};
0471 if (std::abs(lowerTubeLength - upperTubeLength) <
0472 std::numeric_limits<double>::epsilon()) {
0473 envShape = make_intrusive<GeoBox>(
0474 0.5 * m_tubeLayersHeight, 0.5 * m_chamberLength, 0.5 * lowerTubeLength);
0475 } else {
0476 envShape = make_intrusive<GeoTrd>(
0477 0.5 * m_tubeLayersHeight, 0.5 * m_tubeLayersHeight,
0478 0.5 * lowerTubeLength, 0.5 * upperTubeLength, 0.5 * m_chamberLength);
0479 }
0480 auto tubeLogVol = make_intrusive<GeoLogVol>(
0481 "MdtTubeEnvelope", cacheShape(envShape), matMan->getMaterial("std::air"));
0482
0483 auto envelopeVol = make_intrusive<GeoPhysVol>(cacheVolume(tubeLogVol));
0484
0485 const Acts::Vector3 posStag{
0486 m_tubePitch * std::sin(60. * GeoModelKernelUnits::deg),
0487 m_tubePitch * std::cos(60. * GeoModelKernelUnits::deg), 0.};
0488 const Acts::Vector3 negStag{
0489 m_tubePitch * std::sin(60. * GeoModelKernelUnits::deg),
0490 -m_tubePitch * std::cos(60. * GeoModelKernelUnits::deg), 0.};
0491
0492 Acts::Vector3 firstTubePos{-0.5 * m_tubeLayersHeight + 0.5 * m_tubePitch,
0493 -0.5 * m_chamberLength + 0.5 * m_tubePitch, 0.};
0494
0495
0496 auto toyBox = make_intrusive<GeoBox>(10. * GeoModelKernelUnits::cm,
0497 10. * GeoModelKernelUnits::cm,
0498 10. * GeoModelKernelUnits::cm);
0499 auto toyBoxLogVol = cacheVolume(
0500 make_intrusive<GeoLogVol>("TubeLayerLog", cacheShape(toyBox),
0501 matMan->getMaterial("special::Ether")));
0502
0503 const double dTube = (upperTubeLength - lowerTubeLength) / (m_cfg.nTubes - 1);
0504
0505 GeoGenfun::Variable K;
0506 GeoGenfun::GENFUNCTION F = K * m_tubePitch;
0507 GeoXF::TRANSFUNCTION T = GeoXF::Pow(GeoTrf::TranslateY3D(1.0), F);
0508
0509 auto barrelTubeLayer = make_intrusive<GeoSerialTransformer>(
0510 assembleTube(lowerTubeLength - 1. * GeoModelKernelUnits::cm), &T,
0511 m_cfg.nTubes);
0512
0513 for (unsigned tL = 0; tL < m_cfg.nTubeLayers; ++tL) {
0514 auto layerVol = make_intrusive<GeoPhysVol>(toyBoxLogVol);
0515
0516 if (envShape->typeID() == GeoTrd::getClassTypeID()) {
0517 envelopeVol->add(makeTransform(GeoTrf::RotateX3D(rot90deg)));
0518 for (unsigned t = 0; t < m_cfg.nTubes; ++t) {
0519 layerVol->add(makeTransform(GeoTrf::TranslateY3D(t * m_tubePitch)));
0520 layerVol->add(assembleTube(lowerTubeLength + dTube * t));
0521 }
0522 } else {
0523
0524 layerVol->add(serialId(tL));
0525 layerVol->add(barrelTubeLayer);
0526 }
0527 envelopeVol->add(makeTransform(GeoTrf::Translate3D(firstTubePos)));
0528 envelopeVol->add(cacheVolume(layerVol));
0529 firstTubePos = firstTubePos + ((tL % 2) != 1 ? posStag : negStag);
0530 }
0531 return cacheVolume(envelopeVol);
0532 }
0533
0534 PVLink GeoMuonMockupExperiment::buildAbsorber(const double thickness,
0535 const double widthS,
0536 const double widthL,
0537 const double length) {
0538 GeoShapePtr shape{};
0539 if (std::abs(widthS - widthL) < std::numeric_limits<double>::epsilon()) {
0540
0541 shape = make_intrusive<GeoBox>(0.5 * widthS, 0.5 * length, 0.5 * thickness);
0542 } else {
0543
0544 shape = make_intrusive<GeoTrd>(0.5 * thickness, 0.5 * thickness,
0545 0.5 * widthS, 0.5 * widthL, 0.5 * length);
0546 }
0547 auto logVol = cacheVolume(make_intrusive<GeoLogVol>(
0548 "PassiveMat", cacheShape(shape),
0549 MaterialManager::getManager()->getMaterial("std::Forex")));
0550 return cacheVolume(make_intrusive<GeoPhysVol>(logVol));
0551 }
0552
0553 FpvLink GeoMuonMockupExperiment::assembleRpcChamber(const double chamberWidth) {
0554 auto* matMan = MaterialManager::getManager();
0555 constexpr double margin = 0.98;
0556 auto rpcBox = make_intrusive<GeoBox>(
0557 0.5 * (chamberWidth / m_cfg.nRpcAlongPhi) * margin,
0558 0.5 * (m_chamberLength / m_cfg.nRpcAlongZ) * margin,
0559 0.5 * m_rpcChamberHeight);
0560 auto envLogVol = cacheVolume(make_intrusive<GeoLogVol>(
0561 "RpcChamber", cacheShape(rpcBox), matMan->getMaterial("std::Copper")));
0562 auto rpcEnvelope = make_intrusive<GeoFullPhysVol>(envLogVol);
0563
0564 double currentZ = -rpcBox->getZHalfLength() + 0.5 * s_rpcGasSingletSeparation;
0565
0566 for (unsigned gap = 1; gap <= m_cfg.nRpcGasGaps; ++gap) {
0567 currentZ += 0.5 * s_rpcGasHeight;
0568
0569 auto gasBox =
0570 make_intrusive<GeoBox>(rpcBox->getXHalfLength(),
0571 rpcBox->getYHalfLength(), 0.5 * s_rpcGasHeight);
0572 auto gasLogVol = cacheVolume(make_intrusive<GeoLogVol>(
0573 "RpcGasGap", cacheShape(gasBox), matMan->getMaterial("std::ArCO2")));
0574
0575 rpcEnvelope->add(geoId(gap));
0576 rpcEnvelope->add(makeTransform(GeoTrf::TranslateZ3D(currentZ)));
0577 rpcEnvelope->add(cacheVolume(make_intrusive<GeoPhysVol>(gasLogVol)));
0578
0579 currentZ += 0.5 * s_rpcGasHeight;
0580 if (gap == m_cfg.nRpcGasGaps) {
0581 break;
0582 }
0583 currentZ += 0.5 * s_rpcGasSingletSeparation;
0584 rpcEnvelope->add(makeTransform(GeoTrf::TranslateZ3D(currentZ)));
0585 rpcEnvelope->add(buildAbsorber(
0586 s_rpcGasSingletSeparation, 2. * rpcBox->getXHalfLength(),
0587 2. * rpcBox->getXHalfLength(), 2. * rpcBox->getYHalfLength()));
0588 currentZ += 0.5 * s_rpcGasSingletSeparation;
0589 }
0590 return rpcEnvelope;
0591 };
0592 FpvLink GeoMuonMockupExperiment::assembleMultilayerEndcap(
0593 const unsigned ml, const double lowerTubeLength,
0594 const double upperTubeLength) {
0595 auto* matMan = MaterialManager::getManager();
0596 auto envelopeTrd = make_intrusive<GeoTrd>(
0597 0.5 * m_multiLayerHeight, 0.5 * m_multiLayerHeight,
0598 0.5 * lowerTubeLength + 0.05 * GeoModelKernelUnits::mm,
0599 0.5 * upperTubeLength + 0.05 * GeoModelKernelUnits::mm,
0600 0.5 * m_chamberLength);
0601
0602 auto envelopeLogVol =
0603 make_intrusive<GeoLogVol>("MultilayerEnv", cacheShape(envelopeTrd),
0604 matMan->getMaterial("std::air"));
0605
0606 auto envelopeVol =
0607 make_intrusive<GeoFullPhysVol>(cacheVolume(envelopeLogVol));
0608
0609 double currentX = -envelopeTrd->getXHalfLength1();
0610 if (ml == 1 && m_cfg.mdtFoamThickness > 0.) {
0611 currentX += 0.5 * m_cfg.mdtFoamThickness;
0612 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0613 envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, lowerTubeLength,
0614 upperTubeLength, m_chamberLength));
0615 currentX += 0.5 * m_cfg.mdtFoamThickness + s_mdtFoamTubeDistance;
0616 }
0617 currentX += 0.5 * m_tubeLayersHeight;
0618 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0619 envelopeVol->add(buildTubes(lowerTubeLength, upperTubeLength));
0620 if (ml == 2 && m_cfg.mdtFoamThickness > 0.) {
0621 currentX += 0.5 * m_tubeLayersHeight + 0.5 * m_cfg.mdtFoamThickness +
0622 s_mdtFoamTubeDistance;
0623 envelopeVol->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0624 envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, lowerTubeLength,
0625 upperTubeLength, m_chamberLength));
0626 }
0627 return envelopeVol;
0628 }
0629 FpvLink GeoMuonMockupExperiment::assembleMultilayerBarrel(
0630 const unsigned ml, const double tubeLength) {
0631 auto* matMan = MaterialManager::getManager();
0632
0633 const double envelopeWidth = tubeLength;
0634
0635 auto envelopeBox = make_intrusive<GeoBox>(
0636 0.5 * envelopeWidth, 0.5 * m_chamberLength, 0.5 * m_multiLayerHeight);
0637 auto envelopeLogVol =
0638 make_intrusive<GeoLogVol>("MultilayerEnv", cacheShape(envelopeBox),
0639 matMan->getMaterial("std::air"));
0640
0641 auto envelopeVol =
0642 make_intrusive<GeoFullPhysVol>(cacheVolume(envelopeLogVol));
0643
0644 double currentZ = -envelopeBox->getZHalfLength();
0645 if (ml == 1 && m_cfg.mdtFoamThickness > 0.) {
0646 currentZ += 0.5 * m_cfg.mdtFoamThickness;
0647 envelopeVol->add(makeTransform(GeoTrf::TranslateZ3D(currentZ)));
0648 envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, envelopeWidth,
0649 envelopeWidth, m_chamberLength));
0650 currentZ += 0.5 * m_cfg.mdtFoamThickness + s_mdtFoamTubeDistance;
0651 }
0652 PVLink tubeVol = buildTubes(envelopeWidth, envelopeWidth);
0653 currentZ += 0.5 * m_tubeLayersHeight;
0654 envelopeVol->add(makeTransform(GeoTrf::TranslateZ3D(currentZ) *
0655 GeoTrf::RotateY3D(-rot90deg)));
0656 envelopeVol->add(tubeVol);
0657 if (ml == 2 && m_cfg.mdtFoamThickness > 0.) {
0658 currentZ += 0.5 * m_tubeLayersHeight + 0.5 * m_cfg.mdtFoamThickness +
0659 s_mdtFoamTubeDistance;
0660 envelopeVol->add(makeTransform(GeoTrf::TranslateZ3D(currentZ)));
0661 envelopeVol->add(buildAbsorber(m_cfg.mdtFoamThickness, envelopeWidth,
0662 envelopeWidth, m_chamberLength));
0663 }
0664 return envelopeVol;
0665 }
0666 PVLink GeoMuonMockupExperiment::assembleSmallWheelSector(const double wedgeL,
0667 const int etaIdx,
0668 const int sector) {
0669 const double angularScale = 2. * std::sin(0.5 * m_sectorSize);
0670
0671 auto envelopeTrd = make_intrusive<GeoTrd>(
0672 0.5 * m_innerWheelHeight, 0.5 * m_innerWheelHeight,
0673 0.5 * angularScale * m_innerWheelHeight,
0674 0.5 * angularScale * (m_innerWheelHeight + wedgeL), 0.5 * wedgeL);
0675 auto envelopeLog = make_intrusive<GeoLogVol>(
0676 "InnerWheelWedgeLog", cacheShape(envelopeTrd),
0677 MaterialManager::getManager()->getMaterial("std::air"));
0678
0679 auto envelopeWedge = make_intrusive<GeoPhysVol>(cacheVolume(envelopeLog));
0680
0681 double currentX = -envelopeTrd->getXHalfLength1() + 0.5 * m_innerWheelWedgeH;
0682 for (unsigned ml = 1; ml <= m_cfg.nInnerMultiplets; ++ml) {
0683 envelopeWedge->add(makeTransform(GeoTrf::TranslateX3D(currentX)));
0684
0685 auto wedgeTrd = make_intrusive<GeoTrd>(
0686 0.5 * m_innerWheelWedgeH, 0.5 * m_innerWheelWedgeH,
0687 envelopeTrd->getYHalfLength1(), envelopeTrd->getYHalfLength2(),
0688 envelopeTrd->getZHalfLength() - 1. * GeoModelKernelUnits::mm);
0689
0690 auto wedgeLogVol = make_intrusive<GeoLogVol>(
0691 "SmallWheelMultiplet", cacheShape(wedgeTrd),
0692 MaterialManager::getManager()->getMaterial("std::G10"));
0693
0694 auto wedgePhysVol =
0695 make_intrusive<GeoFullPhysVol>(cacheVolume(wedgeLogVol));
0696 publishFPV(envelopeWedge, wedgePhysVol,
0697 std::format("SmallWheel_{}_{}_{}", etaIdx, sector, ml));
0698 currentX += 0.5 * m_innerWheelWedgeH + s_swMultipletSeparation;
0699
0700 double wedgeX = -wedgeTrd->getXHalfLength1() +
0701 0.5 * (s_swlGasGapHeight + s_swGasGapSeparation);
0702 auto gasShape = make_intrusive<GeoTrd>(
0703 0.5 * s_swlGasGapHeight, 0.5 * s_swlGasGapHeight,
0704 wedgeTrd->getYHalfLength1() - 1. * GeoModelKernelUnits::mm,
0705 wedgeTrd->getYHalfLength2() - 1. * GeoModelKernelUnits::mm,
0706 wedgeTrd->getZHalfLength() - 1. * GeoModelKernelUnits::mm);
0707 auto gasLogVol = make_intrusive<GeoLogVol>(
0708 "SmallWheelGasGap", cacheShape(gasShape),
0709 MaterialManager::getManager()->getMaterial("std::ArCO2"));
0710
0711 auto gasGapVol =
0712 cacheVolume(make_intrusive<GeoPhysVol>(cacheVolume(gasLogVol)));
0713
0714 for (unsigned int gasGap = 1; gasGap <= m_cfg.nInnerGasGapsPerMl;
0715 ++gasGap) {
0716 wedgePhysVol->add(makeTransform(GeoTrf::TranslateX3D(wedgeX)));
0717 wedgePhysVol->add(geoId(gasGap));
0718 wedgePhysVol->add(gasGapVol);
0719 wedgeX += 0.5 * (s_swlGasGapHeight + s_swGasGapSeparation);
0720 }
0721 }
0722 return envelopeWedge;
0723 }
0724 void GeoMuonMockupExperiment::assembleSmallWheel(const PVLink& envelope,
0725 const double outerR,
0726 const double wheelZ) {
0727 if (m_cfg.nInnerMultiplets == 0u) {
0728 ACTS_DEBUG("Small wheel will not be assembled");
0729 return;
0730 }
0731
0732 auto envelopeShape = make_intrusive<GeoTube>(m_cfg.endCapWheelLowR, outerR,
0733 0.5 * m_innerWheelHeight);
0734 auto envelopeVol = make_intrusive<GeoLogVol>(
0735 "InnerWheelEnvelope", cacheShape(envelopeShape),
0736 MaterialManager::getManager()->getMaterial("std::air"));
0737 auto envelopeWheel = make_intrusive<GeoPhysVol>(cacheVolume(envelopeVol));
0738
0739 const double wedgeL = envelopeShape->getRMax() - envelopeShape->getRMin();
0740
0741 for (unsigned sector = 1; sector <= m_cfg.nSectors; ++sector) {
0742 envelopeWheel->add(
0743 makeTransform(GeoTrf::RotateZ3D(sector * m_sectorSize) *
0744 GeoTrf::TranslateX3D(0.5 * (envelopeShape->getRMax() +
0745 envelopeShape->getRMin())) *
0746 GeoTrf::RotateY3D(90. * GeoModelKernelUnits::deg)));
0747
0748 envelopeWheel->add(
0749 assembleSmallWheelSector(wedgeL, copySign(1, wheelZ), sector));
0750 }
0751 envelope->add(makeTransform(GeoTrf::TranslateZ3D(wheelZ)));
0752 envelope->add(envelopeWheel);
0753 }
0754 }