File indexing completed on 2025-08-03 07:48:45
0001
0002
0003
0004
0005
0006
0007
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
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);
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
0165 GMDBManager db{m_cfg.dbName};
0166
0167 if (!db.checkIsDBOpen()) {
0168 throw std::runtime_error(
0169 "GeoMuonMockupExperiment::constructMS() - It was not possible to "
0170 "open the DB correctly!");
0171 }
0172
0173 GeoModelIO::WriteGeoModel writeGeoDB{db};
0174 world->exec(&writeGeoDB);
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
0190
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
0265
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
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
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
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
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
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
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 }