File indexing completed on 2025-08-05 08:09:38
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Geometry/TrackingVolume.hpp"
0010
0011 #include "Acts/Definitions/Direction.hpp"
0012 #include "Acts/Geometry/GeometryIdentifier.hpp"
0013 #include "Acts/Geometry/GlueVolumesDescriptor.hpp"
0014 #include "Acts/Geometry/VolumeBounds.hpp"
0015 #include "Acts/Material/IMaterialDecorator.hpp"
0016 #include "Acts/Material/IVolumeMaterial.hpp"
0017 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0018 #include "Acts/Propagator/Navigator.hpp"
0019 #include "Acts/Surfaces/RegularSurface.hpp"
0020 #include "Acts/Surfaces/Surface.hpp"
0021 #include "Acts/Utilities/BinningType.hpp"
0022 #include "Acts/Utilities/TransformRange.hpp"
0023
0024 #include <algorithm>
0025 #include <ostream>
0026 #include <string>
0027 #include <tuple>
0028 #include <utility>
0029
0030 namespace Acts {
0031 class ISurfaceMaterial;
0032
0033
0034 TrackingVolume::TrackingVolume(
0035 const Transform3& transform,
0036 std::shared_ptr<const VolumeBounds> volumeBounds,
0037 std::shared_ptr<const IVolumeMaterial> volumeMaterial,
0038 std::unique_ptr<const LayerArray> staticLayerArray,
0039 std::shared_ptr<const TrackingVolumeArray> containedVolumeArray,
0040 MutableTrackingVolumeVector denseVolumeVector,
0041 const std::string& volumeName)
0042 : Volume(transform, std::move(volumeBounds)),
0043 m_confinedLayers(std::move(staticLayerArray)),
0044 m_confinedVolumes(std::move(containedVolumeArray)),
0045 m_confinedDenseVolumes({}),
0046 m_volumeMaterial(std::move(volumeMaterial)),
0047 m_name(volumeName) {
0048 createBoundarySurfaces();
0049 interlinkLayers();
0050 connectDenseBoundarySurfaces(denseVolumeVector);
0051 }
0052
0053 TrackingVolume::TrackingVolume(const Volume& volume,
0054 const std::string& volumeName)
0055 : TrackingVolume(volume.transform(), volume.volumeBoundsPtr(), nullptr,
0056 volumeName) {}
0057
0058 TrackingVolume::TrackingVolume(
0059 const Transform3& transform, std::shared_ptr<const VolumeBounds> volbounds,
0060 const std::shared_ptr<const TrackingVolumeArray>& containedVolumeArray,
0061 const std::string& volumeName)
0062 : TrackingVolume(transform, std::move(volbounds), nullptr, nullptr,
0063 containedVolumeArray, {}, volumeName) {}
0064
0065 TrackingVolume::~TrackingVolume() {
0066 delete m_glueVolumeDescriptor;
0067 }
0068
0069 const TrackingVolume* TrackingVolume::lowestTrackingVolume(
0070 const GeometryContext& , const Vector3& position,
0071 const double tol) const {
0072
0073 if (m_confinedVolumes) {
0074 return (m_confinedVolumes->object(position).get());
0075 }
0076
0077
0078 if (!m_confinedDenseVolumes.empty()) {
0079 for (auto& denseVolume : m_confinedDenseVolumes) {
0080 if (denseVolume->inside(position, tol)) {
0081 return denseVolume.get();
0082 }
0083 }
0084 }
0085
0086
0087 return this;
0088 }
0089
0090 const TrackingVolumeBoundaries& TrackingVolume::boundarySurfaces() const {
0091 return (m_boundarySurfaces);
0092 }
0093
0094 void TrackingVolume::connectDenseBoundarySurfaces(
0095 MutableTrackingVolumeVector& confinedDenseVolumes) {
0096 if (!confinedDenseVolumes.empty()) {
0097 Direction dir = Direction::Positive;
0098
0099 for (auto& confDenseVol : confinedDenseVolumes) {
0100
0101 auto& boundSur = confDenseVol->boundarySurfaces();
0102 for (unsigned int i = 0; i < boundSur.size(); i++) {
0103
0104
0105 if (boundSur.at(i) == nullptr) {
0106 continue;
0107 }
0108
0109
0110
0111 auto mutableBs =
0112 std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(
0113 boundSur.at(i));
0114 if (mutableBs->m_oppositeVolume != nullptr &&
0115 mutableBs->m_alongVolume == nullptr) {
0116 dir = Direction::Positive;
0117 mutableBs->attachVolume(this, dir);
0118 } else {
0119 if (mutableBs->m_oppositeVolume == nullptr &&
0120 mutableBs->m_alongVolume != nullptr) {
0121 dir = Direction::Negative;
0122 mutableBs->attachVolume(this, dir);
0123 }
0124 }
0125
0126
0127 confDenseVol->updateBoundarySurface((BoundarySurfaceFace)i, mutableBs);
0128 }
0129
0130 m_confinedDenseVolumes.push_back(std::move(confDenseVol));
0131 }
0132 }
0133 }
0134
0135 void TrackingVolume::createBoundarySurfaces() {
0136 using Boundary = BoundarySurfaceT<TrackingVolume>;
0137
0138
0139 auto orientedSurfaces = Volume::volumeBounds().orientedSurfaces(m_transform);
0140
0141 m_boundarySurfaces.reserve(orientedSurfaces.size());
0142 for (auto& osf : orientedSurfaces) {
0143 TrackingVolume* opposite = nullptr;
0144 TrackingVolume* along = nullptr;
0145 if (osf.direction == Direction::OppositeNormal) {
0146 opposite = this;
0147 } else {
0148 along = this;
0149 }
0150 m_boundarySurfaces.push_back(std::make_shared<const Boundary>(
0151 std::move(osf.surface), opposite, along));
0152 }
0153 }
0154
0155 void TrackingVolume::glueTrackingVolume(const GeometryContext& gctx,
0156 BoundarySurfaceFace bsfMine,
0157 TrackingVolume* neighbor,
0158 BoundarySurfaceFace bsfNeighbor) {
0159
0160
0161 Vector3 bPosition(binningPosition(gctx, binR));
0162 Vector3 distance = Vector3(neighbor->binningPosition(gctx, binR) - bPosition);
0163
0164 std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0165 boundarySurfaces().at(bsfMine);
0166
0167
0168
0169
0170 Vector3 nvector =
0171 bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0172
0173 Direction dir = Direction::fromScalar(nvector.dot(distance));
0174
0175
0176 if ((m_glueVolumeDescriptor == nullptr) ||
0177 m_glueVolumeDescriptor->glueVolumes(bsfMine) == nullptr) {
0178
0179 auto mutableBSurfaceMine =
0180 std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0181 mutableBSurfaceMine->attachVolume(neighbor, dir);
0182
0183 const Surface& neighborSurface =
0184 neighbor->m_boundarySurfaces.at(bsfNeighbor)->surfaceRepresentation();
0185 auto neighborMaterial = neighborSurface.surfaceMaterialSharedPtr();
0186 const Surface& mySurface = bSurfaceMine->surfaceRepresentation();
0187 auto myMaterial = mySurface.surfaceMaterialSharedPtr();
0188
0189 if (myMaterial == nullptr && neighborMaterial != nullptr) {
0190 Surface* myMutbableSurface = const_cast<Surface*>(&mySurface);
0191 myMutbableSurface->assignSurfaceMaterial(neighborMaterial);
0192 }
0193
0194 (neighbor->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0195 }
0196 }
0197
0198 void TrackingVolume::glueTrackingVolumes(
0199 const GeometryContext& gctx, BoundarySurfaceFace bsfMine,
0200 const std::shared_ptr<TrackingVolumeArray>& neighbors,
0201 BoundarySurfaceFace bsfNeighbor) {
0202
0203
0204 std::shared_ptr<const TrackingVolume> nRefVolume =
0205 neighbors->arrayObjects().at(0);
0206
0207 Vector3 bPosition(binningPosition(gctx, binR));
0208 Vector3 distance =
0209 Vector3(nRefVolume->binningPosition(gctx, binR) - bPosition);
0210
0211 std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bSurfaceMine =
0212 boundarySurfaces().at(bsfMine);
0213
0214
0215
0216
0217 Vector3 nvector =
0218 bSurfaceMine->surfaceRepresentation().normal(gctx, bPosition);
0219
0220 Direction dir = Direction::fromScalar(nvector.dot(distance));
0221
0222
0223 if ((m_glueVolumeDescriptor == nullptr) ||
0224 !m_glueVolumeDescriptor->glueVolumes(bsfMine)) {
0225
0226 auto mutableBSurfaceMine =
0227 std::const_pointer_cast<BoundarySurfaceT<TrackingVolume>>(bSurfaceMine);
0228 mutableBSurfaceMine->attachVolumeArray(neighbors, dir);
0229
0230 for (auto& nVolume : neighbors->arrayObjects()) {
0231 auto mutableNVolume = std::const_pointer_cast<TrackingVolume>(nVolume);
0232 (mutableNVolume->m_boundarySurfaces).at(bsfNeighbor) = bSurfaceMine;
0233 }
0234 }
0235 }
0236
0237 void TrackingVolume::assignBoundaryMaterial(
0238 std::shared_ptr<const ISurfaceMaterial> surfaceMaterial,
0239 BoundarySurfaceFace bsFace) {
0240 auto bSurface = m_boundarySurfaces.at(bsFace);
0241 RegularSurface* surface =
0242 const_cast<RegularSurface*>(&bSurface->surfaceRepresentation());
0243 surface->assignSurfaceMaterial(std::move(surfaceMaterial));
0244 }
0245
0246 void TrackingVolume::updateBoundarySurface(
0247 BoundarySurfaceFace bsf,
0248 std::shared_ptr<const BoundarySurfaceT<TrackingVolume>> bs,
0249 bool checkmaterial) {
0250 if (checkmaterial) {
0251 auto cMaterialPtr = m_boundarySurfaces.at(bsf)
0252 ->surfaceRepresentation()
0253 .surfaceMaterialSharedPtr();
0254 auto bsMaterial = bs->surfaceRepresentation().surfaceMaterial();
0255 if (cMaterialPtr != nullptr && bsMaterial == nullptr) {
0256 RegularSurface* surface =
0257 const_cast<RegularSurface*>(&bs->surfaceRepresentation());
0258 surface->assignSurfaceMaterial(cMaterialPtr);
0259 }
0260 }
0261 m_boundarySurfaces.at(bsf) = std::move(bs);
0262 }
0263
0264 void TrackingVolume::registerGlueVolumeDescriptor(GlueVolumesDescriptor* gvd) {
0265 delete m_glueVolumeDescriptor;
0266 m_glueVolumeDescriptor = gvd;
0267 }
0268
0269 GlueVolumesDescriptor& TrackingVolume::glueVolumesDescriptor() {
0270 if (m_glueVolumeDescriptor == nullptr) {
0271 m_glueVolumeDescriptor = new GlueVolumesDescriptor;
0272 }
0273 return (*m_glueVolumeDescriptor);
0274 }
0275
0276 void TrackingVolume::synchronizeLayers(double envelope) const {
0277
0278
0279
0280
0281 if (m_confinedLayers) {
0282
0283
0284
0285 for (auto& clayIter : m_confinedLayers->arrayObjects()) {
0286 if (clayIter) {
0287
0288
0289
0290
0291
0292
0293 }
0294
0295
0296 }
0297 }
0298
0299
0300 if (m_confinedVolumes) {
0301
0302
0303
0304 for (auto& cVolumesIter : m_confinedVolumes->arrayObjects()) {
0305 cVolumesIter->synchronizeLayers(envelope);
0306 }
0307 }
0308 }
0309
0310 void TrackingVolume::interlinkLayers() {
0311 if (m_confinedLayers) {
0312 auto& layers = m_confinedLayers->arrayObjects();
0313
0314
0315
0316 const Layer* lastLayer = nullptr;
0317 for (auto& layerPtr : layers) {
0318
0319 Layer& mutableLayer = *(std::const_pointer_cast<Layer>(layerPtr));
0320
0321 mutableLayer.m_nextLayerUtility = m_confinedLayers->binUtility();
0322 mutableLayer.m_nextLayers.first = lastLayer;
0323
0324 mutableLayer.encloseTrackingVolume(*this);
0325
0326 lastLayer = &mutableLayer;
0327 }
0328
0329 lastLayer = nullptr;
0330 for (auto layerIter = layers.rbegin(); layerIter != layers.rend();
0331 ++layerIter) {
0332
0333 Layer& mutableLayer = *(std::const_pointer_cast<Layer>(*layerIter));
0334 mutableLayer.m_nextLayers.second = lastLayer;
0335 lastLayer = &mutableLayer;
0336 }
0337 }
0338 }
0339
0340 void TrackingVolume::closeGeometry(
0341 const IMaterialDecorator* materialDecorator,
0342 std::unordered_map<GeometryIdentifier, const TrackingVolume*>& volumeMap,
0343 std::size_t& vol, const GeometryIdentifierHook& hook,
0344 const Logger& logger) {
0345
0346 auto volumeID = GeometryIdentifier().setVolume(++vol);
0347
0348 auto thisVolume = const_cast<TrackingVolume*>(this);
0349 thisVolume->assignGeometryId(volumeID);
0350 ACTS_DEBUG("volumeID: " << volumeID << ", name: " << volumeName());
0351
0352 volumeMap[volumeID] = thisVolume;
0353
0354
0355 if (materialDecorator != nullptr) {
0356 materialDecorator->decorate(*thisVolume);
0357 }
0358 if (thisVolume->volumeMaterial() == nullptr &&
0359 thisVolume->motherVolume() != nullptr &&
0360 thisVolume->motherVolume()->volumeMaterial() != nullptr) {
0361 auto protoMaterial = dynamic_cast<const ProtoVolumeMaterial*>(
0362 thisVolume->motherVolume()->volumeMaterial());
0363 if (protoMaterial == nullptr) {
0364 thisVolume->assignVolumeMaterial(
0365 thisVolume->motherVolume()->volumeMaterialSharedPtr());
0366 }
0367 }
0368
0369 this->assignGeometryId(volumeID);
0370
0371 GeometryIdentifier::Value iboundary = 0;
0372
0373 for (auto& bSurfIter : boundarySurfaces()) {
0374
0375 auto& bSurface = bSurfIter->surfaceRepresentation();
0376
0377 auto boundaryID = GeometryIdentifier(volumeID).setBoundary(++iboundary);
0378
0379 auto& mutableBSurface = *(const_cast<RegularSurface*>(&bSurface));
0380 mutableBSurface.assignGeometryId(boundaryID);
0381
0382 if (materialDecorator != nullptr) {
0383 materialDecorator->decorate(mutableBSurface);
0384 }
0385 }
0386
0387
0388 if (!m_confinedVolumes) {
0389
0390 if (m_confinedLayers) {
0391 GeometryIdentifier::Value ilayer = 0;
0392
0393 for (auto& layerPtr : m_confinedLayers->arrayObjects()) {
0394
0395 auto layerID = GeometryIdentifier(volumeID).setLayer(++ilayer);
0396
0397 auto mutableLayerPtr = std::const_pointer_cast<Layer>(layerPtr);
0398 mutableLayerPtr->closeGeometry(materialDecorator, layerID, hook,
0399 logger);
0400 }
0401 }
0402 } else {
0403
0404
0405 for (auto& volumesIter : m_confinedVolumes->arrayObjects()) {
0406 auto mutableVolumesIter =
0407 std::const_pointer_cast<TrackingVolume>(volumesIter);
0408 mutableVolumesIter->setMotherVolume(this);
0409 mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
0410 logger);
0411 }
0412 }
0413
0414 if (!m_confinedDenseVolumes.empty()) {
0415 for (auto& volumesIter : m_confinedDenseVolumes) {
0416 auto mutableVolumesIter =
0417 std::const_pointer_cast<TrackingVolume>(volumesIter);
0418 mutableVolumesIter->setMotherVolume(this);
0419 mutableVolumesIter->closeGeometry(materialDecorator, volumeMap, vol, hook,
0420 logger);
0421 }
0422 }
0423 }
0424
0425
0426 boost::container::small_vector<BoundaryIntersection, 4>
0427 TrackingVolume::compatibleBoundaries(const GeometryContext& gctx,
0428 const Vector3& position,
0429 const Vector3& direction,
0430 const NavigationOptions<Surface>& options,
0431 const Logger& logger) const {
0432 ACTS_VERBOSE("Finding compatibleBoundaries");
0433
0434 auto excludeObject = options.startObject;
0435 boost::container::small_vector<BoundaryIntersection, 4> bIntersections;
0436
0437
0438 double nearLimit = 0;
0439 double farLimit = options.farLimit;
0440
0441
0442 auto checkIntersection =
0443 [&](SurfaceMultiIntersection& smIntersection,
0444 const BoundarySurface* bSurface) -> BoundaryIntersection {
0445 for (const auto& sIntersection : smIntersection.split()) {
0446 if (!sIntersection) {
0447 continue;
0448 }
0449
0450 if (options.forceIntersectBoundaries) {
0451 const bool coCriterion =
0452 std::abs(sIntersection.pathLength()) < std::abs(nearLimit);
0453 ACTS_VERBOSE("Forcing intersection with surface "
0454 << bSurface->surfaceRepresentation().geometryId());
0455 if (coCriterion) {
0456 ACTS_VERBOSE("Intersection forced successfully ");
0457 ACTS_VERBOSE("- intersection path length "
0458 << std::abs(sIntersection.pathLength())
0459 << " < overstep limit " << std::abs(nearLimit));
0460 return BoundaryIntersection(sIntersection, bSurface);
0461 }
0462 ACTS_VERBOSE("Can't force intersection: ");
0463 ACTS_VERBOSE("- intersection path length "
0464 << std::abs(sIntersection.pathLength())
0465 << " > overstep limit " << std::abs(nearLimit));
0466 }
0467
0468 ACTS_VERBOSE("Check intersection with surface "
0469 << bSurface->surfaceRepresentation().geometryId());
0470 if (detail::checkIntersection(sIntersection.intersection(), nearLimit,
0471 farLimit, logger)) {
0472 return BoundaryIntersection(sIntersection, bSurface);
0473 }
0474 }
0475
0476 ACTS_VERBOSE("No intersection accepted");
0477 return BoundaryIntersection(SurfaceIntersection::invalid(), bSurface);
0478 };
0479
0480
0481 auto processBoundaries =
0482 [&](const TrackingVolumeBoundaries& bSurfaces) -> void {
0483 ACTS_VERBOSE("Processing boundaries");
0484
0485 for (auto& bsIter : bSurfaces) {
0486
0487 const auto& bSurfaceRep = bsIter->surfaceRepresentation();
0488 ACTS_VERBOSE("Consider boundary surface " << bSurfaceRep.geometryId()
0489 << " :\n"
0490 << std::tie(bSurfaceRep, gctx));
0491
0492
0493 if (excludeObject != &bSurfaceRep) {
0494 auto bCandidate = bSurfaceRep.intersect(gctx, position, direction,
0495 options.boundaryCheck);
0496
0497 auto bIntersection = checkIntersection(bCandidate, bsIter.get());
0498 if (bIntersection.first) {
0499 ACTS_VERBOSE(" - Proceed with surface");
0500 bIntersections.push_back(bIntersection);
0501 } else {
0502 ACTS_VERBOSE(" - Surface intersecion invalid");
0503 }
0504 } else {
0505 ACTS_VERBOSE(" - Surface is excluded surface");
0506 }
0507 }
0508 };
0509
0510
0511 auto& bSurfaces = boundarySurfaces();
0512 ACTS_VERBOSE("Volume reports " << bSurfaces.size() << " boundary surfaces");
0513 processBoundaries(bSurfaces);
0514
0515
0516 auto confinedDenseVolumes = denseVolumes();
0517 ACTS_VERBOSE("Volume reports " << confinedDenseVolumes.size()
0518 << " confined dense volumes");
0519 for (const auto& dv : confinedDenseVolumes) {
0520 auto& bSurfacesConfined = dv->boundarySurfaces();
0521 ACTS_VERBOSE(" -> " << bSurfacesConfined.size() << " boundary surfaces");
0522 processBoundaries(bSurfacesConfined);
0523 }
0524
0525 auto comparator = [](double a, double b) {
0526
0527 if ((a > 0 && b > 0) || (a < 0 && b < 0)) {
0528 return a < b;
0529 }
0530 if (a > 0) {
0531 return true;
0532 }
0533 return false;
0534 };
0535
0536 std::sort(bIntersections.begin(), bIntersections.end(),
0537 [&](const BoundaryIntersection& a, const BoundaryIntersection& b) {
0538 return comparator(a.first.pathLength(), b.first.pathLength());
0539 });
0540 return bIntersections;
0541 }
0542
0543 boost::container::small_vector<LayerIntersection, 10>
0544 TrackingVolume::compatibleLayers(
0545 const GeometryContext& gctx, const Vector3& position,
0546 const Vector3& direction, const NavigationOptions<Layer>& options) const {
0547
0548 boost::container::small_vector<LayerIntersection, 10> lIntersections;
0549
0550
0551 if (m_confinedLayers != nullptr) {
0552
0553 const Layer* tLayer = options.startObject != nullptr
0554 ? options.startObject
0555 : associatedLayer(gctx, position);
0556 while (tLayer != nullptr) {
0557
0558
0559
0560
0561
0562 if (tLayer != options.startObject && tLayer->resolve(options)) {
0563
0564
0565 auto atIntersection =
0566 tLayer->surfaceOnApproach(gctx, position, direction, options);
0567 auto path = atIntersection.pathLength();
0568 bool withinLimit = std::abs(path) <= std::abs(options.farLimit);
0569
0570 if (atIntersection && withinLimit) {
0571
0572 lIntersections.push_back(LayerIntersection(atIntersection, tLayer));
0573 }
0574 }
0575
0576 tLayer = (tLayer == options.endObject)
0577 ? nullptr
0578 : tLayer->nextLayer(gctx, position, direction);
0579 }
0580 std::sort(lIntersections.begin(), lIntersections.end(),
0581 [](const LayerIntersection& a, const LayerIntersection& b) {
0582 return SurfaceIntersection::pathLengthOrder(a.first, b.first);
0583 });
0584 }
0585
0586 return lIntersections;
0587 }
0588
0589 const std::string& TrackingVolume::volumeName() const {
0590 return m_name;
0591 }
0592
0593 const IVolumeMaterial* TrackingVolume::volumeMaterial() const {
0594 return m_volumeMaterial.get();
0595 }
0596
0597 const std::shared_ptr<const IVolumeMaterial>&
0598 TrackingVolume::volumeMaterialSharedPtr() const {
0599 return m_volumeMaterial;
0600 }
0601
0602 void TrackingVolume::assignVolumeMaterial(
0603 std::shared_ptr<const IVolumeMaterial> material) {
0604 m_volumeMaterial = std::move(material);
0605 }
0606
0607 const LayerArray* TrackingVolume::confinedLayers() const {
0608 return m_confinedLayers.get();
0609 }
0610
0611 const MutableTrackingVolumeVector TrackingVolume::denseVolumes() const {
0612 return m_confinedDenseVolumes;
0613 }
0614
0615 std::shared_ptr<const TrackingVolumeArray> TrackingVolume::confinedVolumes()
0616 const {
0617 return m_confinedVolumes;
0618 }
0619
0620 const TrackingVolume* TrackingVolume::motherVolume() const {
0621 return m_motherVolume;
0622 }
0623
0624 TrackingVolume* TrackingVolume::motherVolume() {
0625 return m_motherVolume;
0626 }
0627
0628 void TrackingVolume::setMotherVolume(TrackingVolume* mvol) {
0629 m_motherVolume = mvol;
0630 }
0631
0632 const Acts::Layer* TrackingVolume::associatedLayer(
0633 const GeometryContext& , const Vector3& position) const {
0634
0635 if (m_confinedLayers != nullptr) {
0636 return (m_confinedLayers->object(position).get());
0637 }
0638
0639
0640 return nullptr;
0641 }
0642
0643 TrackingVolume::VolumeRange TrackingVolume::volumes() const {
0644 return VolumeRange{m_volumes};
0645 }
0646
0647 TrackingVolume::MutableVolumeRange TrackingVolume::volumes() {
0648 return MutableVolumeRange{m_volumes};
0649 }
0650
0651 TrackingVolume& TrackingVolume::addVolume(
0652 std::unique_ptr<TrackingVolume> volume) {
0653 if (volume->motherVolume() != nullptr) {
0654 throw std::invalid_argument("Volume already has a mother volume");
0655 }
0656
0657 volume->setMotherVolume(this);
0658 m_volumes.push_back(std::move(volume));
0659 return *m_volumes.back();
0660 }
0661
0662 }