diff --git a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx index 7dfd26a79b38d..fe0a1c50330fe 100644 --- a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx +++ b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx @@ -122,7 +122,8 @@ void Alice3Pipe::ConstructGeometry() // Add everything to the barrel barrel->AddNode(pipeVolume, 1, new TGeoTranslation(0, 30.f, 0)); - pipeVolume->SetLineColor(kGreen + 3); + pipeVolume->SetLineColor(37); + pipeVolume->SetTransparency(0); } void Alice3Pipe::createMaterials() diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index b838940860e47..0e9ff8727a977 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -43,6 +43,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache }; static const char* getTRKVolPattern() { return sVolumeName.c_str(); } static const char* getTRKLayerPattern() { return sLayerName.c_str(); } + static const char* getTRKPetalAssemblyPattern() { return sPetalAssemblyName.c_str(); } static const char* getTRKPetalPattern() { return sPetalName.c_str(); } static const char* getTRKPetalDiskPattern() { return sPetalDiskName.c_str(); } static const char* getTRKPetalLayerPattern() { return sPetalLayerName.c_str(); } @@ -145,6 +146,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static std::string sVolumeName; static std::string sLayerName; + static std::string sPetalAssemblyName; static std::string sPetalName; static std::string sPetalDiskName; static std::string sPetalLayerName; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index e8907efb190da..9325f5079375d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -24,12 +24,14 @@ std::unique_ptr GeometryTGeo::sInstance; // Names std::string GeometryTGeo::sVolumeName = "TRKV"; std::string GeometryTGeo::sLayerName = "TRKLayer"; +std::string GeometryTGeo::sPetalAssemblyName = "PETAL"; std::string GeometryTGeo::sPetalName = "PETALCASE"; std::string GeometryTGeo::sPetalDiskName = "DISK"; std::string GeometryTGeo::sPetalLayerName = "LAYER"; std::string GeometryTGeo::sStaveName = "TRKStave"; std::string GeometryTGeo::sChipName = "TRKChip"; std::string GeometryTGeo::sSensorName = "TRKSensor"; + std::string GeometryTGeo::sWrapperVolumeName = "TRKUWrapVol"; ///< Wrapper volume name, not implemented at the moment o2::trk::GeometryTGeo::~GeometryTGeo() @@ -68,9 +70,9 @@ void GeometryTGeo::Build(int loadTrans) } mNumberOfLayersMLOT = extractNumberOfLayersMLOT(); + mNumberOfPetalsVD = extractNumberOfPetalsVD(); mNumberOfActivePartsVD = extractNumberOfActivePartsVD(); mNumberOfLayersVD = extractNumberOfLayersVD(); - mNumberOfPetalsVD = extractNumberOfPetalsVD(); mNumberOfDisksVD = extractNumberOfDisksVD(); mNumberOfStaves.resize(mNumberOfLayersMLOT); @@ -451,118 +453,261 @@ int GeometryTGeo::extractNumberOfLayersMLOT() } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfActivePartsVD() const +int GeometryTGeo::extractNumberOfPetalsVD() const { - // The number of active parts returned here is 36 = 4 petals * (3 layers + 6 disks) - int numberOfParts = 0; + int numberOfPetals = 0; + TGeoVolume* trkV = gGeoManager->GetVolume(getTRKVolPattern()); + if (!trkV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; + } - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + // Loop on all TRKV nodes, count PETAL assemblies and their contents + TObjArray* nodes = trkV->GetNodes(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names - TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + LOGP(info, "Searching for petal assemblies in {} (pattern: {})", + getTRKVolPattern(), getTRKPetalAssemblyPattern()); + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); - if (strstr(name, getTRKPetalPattern()) != nullptr && (strstr(name, getTRKPetalLayerPattern()) != nullptr || strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfParts++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + if (strstr(name, getTRKPetalAssemblyPattern()) != nullptr) { + numberOfPetals++; + LOGP(info, "Found petal assembly: {}", name); + + // Get petal volume and its nodes for debugging + TGeoVolume* petalVol = nd->GetVolume(); + if (petalVol) { + TObjArray* petalNodes = petalVol->GetNodes(); + if (petalNodes) { + LOGP(debug, "Petal {} contains {} child nodes", name, petalNodes->GetEntriesFast()); + // Print all nodes in this petal + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + LOGP(debug, " Node {}: {}", k, petalNode->GetName()); + } + } else { + LOGP(warning, "Petal {} has no child nodes", name); + } + } else { + LOGP(warning, "Petal {} has no volume", name); } } } - return numberOfParts; + + if (numberOfPetals == 0) { + LOGP(warning, "No petal assemblies found in geometry"); + } else { + LOGP(info, "Found {} petal assemblies", numberOfPetals); + } + + return numberOfPetals; } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfDisksVD() const +int GeometryTGeo::extractNumberOfActivePartsVD() const { - // The number of disks returned here is 6 - int numberOfDisks = 0; - + // The number of active parts returned here is 36 = 4 petals * (3 layers + 6 disks) + int numberOfParts = 0; TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal to count its active parts TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } + + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); + if (strstr(name, getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && (strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfDisks++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + petalFound = true; + LOGP(info, "Counting active parts in petal: {}", name); + + // Found a petal, count its layers and disks + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", name); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", name); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + const char* nodeName = petalNode->GetName(); + + if (strstr(nodeName, getTRKPetalLayerPattern()) != nullptr || + strstr(nodeName, getTRKPetalDiskPattern()) != nullptr) { + numberOfParts++; + LOGP(debug, "Found active part in {}: {}", name, nodeName); } } + // We only need to check one petal as they're identical + break; } - return numberOfDisks; + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + return 0; + } + + if (numberOfParts == 0) { + LOGP(warning, "No active parts (layers/disks) found in petal"); + return 0; + } + + // Multiply by number of petals since all petals are identical + int totalParts = numberOfParts * mNumberOfPetalsVD; + LOGP(info, "Total number of active parts: {} ({}*{})", + totalParts, numberOfParts, mNumberOfPetalsVD); + return totalParts; } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfPetalsVD() const +int GeometryTGeo::extractNumberOfDisksVD() const { - // The number of petals returned here is 4 = number of petals - int numberOfChips = 0; - + // Count disks in the first petal (all petals are identical) + int numberOfDisks = 0; TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); - const char* name = nd->GetName(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } - if (strstr(name, getTRKPetalPattern()) != nullptr && (strstr(name, Form("%s%s", getTRKPetalLayerPattern(), "0")) != nullptr)) { - numberOfChips++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); + if (strstr(nd->GetName(), getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } + + petalFound = true; + LOGP(info, "Counting disks in petal: {}", nd->GetName()); + + // Count disks in this petal + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", nd->GetName()); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", nd->GetName()); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + if (strstr(petalNode->GetName(), getTRKPetalDiskPattern()) != nullptr) { + numberOfDisks++; + LOGP(info, "Found disk in {} : {}", nd->GetName(), petalNode->GetName()); } } + // One petal is enough + break; } - return numberOfChips; + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfDisks == 0) { + LOGP(warning, "No disks found in VD geometry"); + } + + return numberOfDisks; } //__________________________________________________________________________ int GeometryTGeo::extractNumberOfLayersVD() const { - // The number of layers returned here is 3 + // Count layers in the first petal (all petals are identical) int numberOfLayers = 0; - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); - const char* name = nd->GetName(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && strstr(name, getTRKPetalLayerPattern()) != nullptr) { - numberOfLayers++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); + if (strstr(nd->GetName(), getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } + + petalFound = true; + LOGP(info, "Counting layers in petal: {}", nd->GetName()); + + // Count layers in this petal + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", nd->GetName()); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", nd->GetName()); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + if (strstr(petalNode->GetName(), getTRKPetalLayerPattern()) != nullptr) { + numberOfLayers++; + LOGP(info, "Found layer in {} : {}", nd->GetName(), petalNode->GetName()); } } + // One petal is enough + break; + } + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfLayers == 0) { + LOGP(warning, "No layers found in VD geometry"); } + return numberOfLayers; } @@ -571,27 +716,82 @@ int GeometryTGeo::extractNumberOfChipsPerPetalVD() const { // The number of chips per petal returned here is 9 for each layer = number of layers + number of quarters of disks per petal int numberOfChips = 0; - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal assembly TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } + + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); + if (strstr(name, getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && (strstr(name, getTRKPetalLayerPattern()) != nullptr || strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfChips++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + petalFound = true; + LOGP(info, "Counting chips in petal: {}", name); + + // Found a petal, count sensors in its layers and disks + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", name); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", name); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + const char* nodeName = petalNode->GetName(); + TGeoVolume* vol = petalNode->GetVolume(); + + if (!vol) { + LOGP(debug, "Node {} has no volume", nodeName); + continue; + } + + // Look for sensors in this volume + TObjArray* subNodes = vol->GetNodes(); + if (!subNodes) { + LOGP(debug, "Node {} has no sub-nodes", nodeName); + continue; + } + + for (int i = 0; i < subNodes->GetEntriesFast(); i++) { + auto* subNode = dynamic_cast(subNodes->At(i)); + if (strstr(subNode->GetName(), getTRKSensorPattern()) != nullptr) { + numberOfChips++; + LOGP(debug, "Found sensor in {}: {}", nodeName, subNode->GetName()); + } } } + // We only need one petal + break; } + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfChips == 0) { + LOGP(warning, "No chips/sensors found in VD petal"); + } + + LOGP(info, "Number of chips per petal: {}", numberOfChips); return numberOfChips; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt index ab817a3fdaa0d..0c3c35d49f722 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -18,9 +18,8 @@ o2_add_library(TRKSimulation src/Digitizer.cxx src/TRKServices.cxx src/DPLDigitizerParam.cxx - src/TRKPetalCase.cxx - src/TRKPetalLayer.cxx - src/TRKPetalDisk.cxx + src/VDLayer.cxx + src/VDGeometryBuilder.cxx PUBLIC_LINK_LIBRARIES O2::TRKBase O2::FT3Simulation O2::ITSMFTSimulation @@ -35,7 +34,7 @@ o2_target_root_dictionary(TRKSimulation include/TRKSimulation/Detector.h include/TRKSimulation/TRKLayer.h include/TRKSimulation/TRKServices.h - include/TRKSimulation/TRKPetalCase.h - include/TRKSimulation/TRKPetalLayer.h - include/TRKSimulation/TRKPetalDisk.h + include/TRKSimulation/VDLayer.h + include/TRKSimulation/VDGeometryBuilder.h + include/TRKSimulation/VDSensorRegistry.h include/TRKSimulation/DPLDigitizerParam.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h index 8ed5737abcb35..92cebd681176d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h @@ -17,7 +17,6 @@ #include "TRKSimulation/TRKLayer.h" #include "TRKSimulation/TRKServices.h" -#include "TRKSimulation/TRKPetalCase.h" #include "TRKBase/GeometryTGeo.h" #include @@ -31,9 +30,6 @@ namespace trk class Detector : public o2::base::DetImpl { public: - static constexpr Int_t mNumberOfVolumes = 44; /// hardcoded for the current geometry = 8 MLOT layers + 36 volumes in the VD. TODO: automatize or change according to the current geometry - static constexpr Int_t mNumberOfVolumesVD = 36; /// hardcoded for the current geometry = 36 volumes in the VD. TODO: automatize or change according to the current geometry - Detector(bool active); Detector(); ~Detector(); @@ -71,7 +67,7 @@ class Detector : public o2::base::DetImpl } void configDefault(); - void buildTRKNewVacuumVessel(); + void buildTRKMiddleOuterLayers(); void configFromFile(std::string fileName = "alice3_TRK_layout.txt"); void configToFile(std::string fileName = "alice3_TRK_layout.txt"); @@ -80,6 +76,9 @@ class Detector : public o2::base::DetImpl void createGeometry(); private: + int mNumberOfVolumes; + int mNumberOfVolumesVD; + // Transient data about track passing the sensor struct TrackData { bool mHitStarted; // hit creation started @@ -91,8 +90,7 @@ class Detector : public o2::base::DetImpl GeometryTGeo* mGeometryTGeo; //! std::vector* mHits; // ITSMFT ones for the moment std::vector mLayers; - TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker - std::vector mPetalCases; // Houses the Iris tracker and its services. Created fully in the beam pipe + TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker std::vector mFirstOrLastLayers; // Names of the first or last layers bool InsideFirstOrLastLayer(std::string layerName); @@ -106,8 +104,6 @@ class Detector : public o2::base::DetImpl public: static constexpr Int_t sNumberVDPetalCases = 4; //! Number of VD petals int getNumberOfLayers() const { return mLayers.size(); } //! Number of TRK layers - int getNumberOfLayersVD() const { return mPetalCases[0].mPetalLayers.size(); } - int getNumberOfDisksVD() const { return mPetalCases[0].mPetalDisks.size(); } void Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int chipID) const; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h deleted file mode 100644 index cd45cc98fd177..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_TRK_PETALCASE_H -#define ALICEO2_TRK_PETALCASE_H - -#include - -#include "TRKSimulation/TRKPetalLayer.h" -#include "TRKSimulation/TRKPetalDisk.h" -#include "TGeoCompositeShape.h" - -namespace o2 -{ -namespace trk -{ -class TRKPetalCase -{ - public: - TRKPetalCase() = default; - TRKPetalCase(Int_t number, TGeoVolume* motherVolume, Bool_t irisOpen); - ~TRKPetalCase() = default; - - // Sensitive volume list - std::vector mPetalLayers; - std::vector mPetalDisks; - - auto getPetalCaseName() { return mPetalCaseName; } - TString getFullName(); - - private: - void constructCase(TGeoVolume* motherVolume); - void constructColdPlate(TGeoVolume* motherVolume); - void constructDetectionPetals(TGeoVolume* motherVolume); - void addDetectionPetelsToFullComposite(); - - void addToPetalCaseComposite(TString shape) { mFullCompositeFormula += ("+" + shape); } - - Int_t mPetalCaseNumber; // Used to determine rotation and position. 0-3 - Bool_t mOpenState; // At injection energy, the iris tracker is in the open position. During stable beams, it is closed - - TString mPetalCaseName; - TString mFullCompositeFormula; // Used to excavate the petal and all its components from the vacuum - - // Center position of the petal case. 0,0,0 at stable beams (a.k.a. closed state) - Double_t mXPos, mYPos, mZPos; - - Double_t mWallThickness; // cm // Assume all the walls have the same thickness for now - Double_t mRIn; // cm - Double_t mROut; // cm - Double_t mRInOpenState; // cm - Double_t mPetalCaseLength; // cm - - Double_t mAngularCoverageAzimuthalWall; // Rad // Angular coverage of azimuthal part of wall (equivalent to that of the sensitive volumes) - Double_t mAngularCoverageRadialWall; // Rad // Angular coverage of radial part of wall - Double_t mToDeg; - - // Petal case parts -> In one composite shape - TGeoTubeSeg* mInnerAzimuthalWall; - TGeoTubeSeg* mOuterAzimuthalWall; - TGeoTubeSeg* mRadialWall; - TGeoTubeSeg* mForwardWall; - - TGeoRotation* mAzimuthalWallRot; - TGeoRotation* mRadialWall1Rot; - TGeoRotation* mRadialWall2Rot; - - TGeoCombiTrans* mAzimuthalWallCombiTrans; - TGeoCombiTrans* mRadialWall1CombiTrans; - TGeoCombiTrans* mRadialWall2CombiTrans; - TGeoCombiTrans* mForwardWall1CombiTrans; - TGeoCombiTrans* mForwardWall2CombiTrans; - - TGeoVolume* mPetalCaseVolume; - - // Cold plate - TGeoTubeSeg* mColdPlate; - TGeoVolume* mColdPlateVolume; - - ClassDef(TRKPetalCase, 1); -}; - -} // namespace trk -} // namespace o2 -#endif // ALICEO2_TRK_PETALCASE_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h deleted file mode 100644 index 465f52eb8d41b..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TRKPetalDisk.h -/// \brief Definition of the TRKPetalDisk class - -#ifndef ALICEO2_TRK_PETAL_DISK_H_ -#define ALICEO2_TRK_PETAL_DISK_H_ - -#include "TGeoManager.h" // for gGeoManager -#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc -#include // for LOG - -namespace o2 -{ -namespace trk -{ - -/// This class defines the Geometry for the TRK Disk TGeo. -class TRKPetalDisk -{ - public: - TRKPetalDisk() = default; - TRKPetalDisk(Int_t diskNumber, std::string diskName, Float_t z, Float_t rIn, Float_t rOut, Float_t angularCoverage, Float_t Diskx2X0); - ~TRKPetalDisk() = default; - - auto getInnerRadius() const { return mInnerRadius; } - auto getOuterRadius() const { return mOuterRadius; } - auto getThickness() const { return mChipThickness; } - auto getAngularCoverage() const { return mAngularCoverage; } - auto getZ() const { return mZ; } - auto getx2X0() const { return mx2X0; } - auto getName() const { return mDiskName; } - auto getSensorName() const { return mSensorName; } - - /// Creates the actual Disk and places inside its mother volume - /// \param motherVolume the TGeoVolume owing the volume structure - void createDisk(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans); - - private: - Int_t mDiskNumber = -1; ///< Current disk number - std::string mDiskName; ///< Current disk name - std::string mSensorName; - Double_t mInnerRadius; ///< Inner radius of this disk - Double_t mOuterRadius; ///< Outer radius of this disk - Double_t mAngularCoverage; - Double_t mZ; ///< Z position of the disk - Double_t mChipThickness; ///< Chip thickness - Double_t mx2X0; ///< Disk material budget x/X0 - - ClassDef(TRKPetalDisk, 1); -}; -} // namespace trk -} // namespace o2 - -#endif // ALICEO2_TRK_PETAL_DISK_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h deleted file mode 100644 index 4e7a7735d51f0..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_TRK_PETAL_LAYER_H -#define ALICEO2_TRK_PETAL_LAYER_H - -#include "TGeoManager.h" -#include -#include "TGeoTube.h" - -#include "TRKBase/TRKBaseParam.h" - -namespace o2 -{ -namespace trk -{ -class TRKPetalLayer -{ - public: - TRKPetalLayer() = default; - TRKPetalLayer(Int_t layerNumber, std::string layerName, Float_t rIn, Float_t angularCoverage, Float_t zLength, Float_t layerX2X0); - ~TRKPetalLayer() = default; - - auto getInnerRadius() const { return mInnerRadius; } - auto getAngularCoverage() const { return mAngularCoverage; } - auto getZLength() { return mZ; } - auto getx2X0() const { return mX2X0; } - auto getChipThickness() const { return mChipThickness; } - auto getNumber() const { return mLayerNumber; } - auto getName() const { return mLayerName; } - auto getSensorName() const { return mSensorName; } - - void createLayer(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans); - - private: - Int_t mLayerNumber; - std::string mLayerName; - std::string mSensorName; - Float_t mInnerRadius; - Float_t mZ; - Float_t mX2X0; - Float_t mChipThickness; - Float_t mModuleWidth; // u.m. = cm - Float_t mAngularCoverage; // rad - - TGeoTubeSeg* mLayer; - - ClassDef(TRKPetalLayer, 1); -}; - -} // namespace trk -} // namespace o2 -#endif // ALICEO2_TRK_PETAL_LAYER_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h new file mode 100644 index 0000000000000..0a2cb68f2233a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRK_VDGEOMETRYBUILDER_H +#define O2_TRK_VDGEOMETRYBUILDER_H + +class TGeoVolume; + +#include +#include + +namespace o2::trk +{ + +// Build full VD for each design. +// Each function builds one local petal assembly (walls + layers + disks) +// and then places/rotates the petal once into the mother volume. + +void createIRIS4Geometry(TGeoVolume* motherVolume); // 4 petals, cylindrical L0 +void createIRIS4aGeometry(TGeoVolume* motherVolume); // 3 petals, cylindrical L0 +void createIRIS5Geometry(TGeoVolume* motherVolume); // 4 petals, rectangular L0 + +void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID = 0, int nPetals = 4, bool rectangularL0 = false); + +} // namespace o2::trk + +#endif // O2_TRK_VDGEOMETRYBUILDER_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h new file mode 100644 index 0000000000000..9e9ca2971bc3b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h @@ -0,0 +1,110 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_VD_LAYER_H +#define ALICEO2_VD_LAYER_H + +#include +#include + +class TGeoVolume; +class TGeoMatrix; + +namespace o2 +{ +namespace trk +{ + +// Base class for a VD layer +class VDLayer +{ + public: + VDLayer() = default; + VDLayer(int layerNumber, const std::string& layerName, double layerX2X0); + virtual ~VDLayer() = default; + + // Create the layer (AIR container + sensors) and insert it into mother + virtual void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const = 0; + + double getChipThickness() const { return mChipThickness; } + + protected: + int mLayerNumber{0}; + std::string mLayerName; + double mX2X0{0.f}; // Radiation length in units of X0 + double mChipThickness{0.f}; // thickness derived from X/X0 + double mModuleWidth{4.54f}; // cm + + // ClassDef(VDLayer, 1) +}; + +// Cylindrical segment layer +class VDCylindricalLayer : public VDLayer +{ + public: + VDCylindricalLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double radius, double phiSpanDeg, double lengthZ, double lengthSensZ); + + TGeoVolume* createSensor() const; // builds the sensor volume + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + private: + double mRadius{0.f}; + double mPhiSpanDeg{0.f}; // degrees + double mLengthZ{0.f}; // layer container length in Z + double mLengthSensZ{0.f}; // sensor length in Z + + // ClassDef(VDCylindricalLayer, 1) +}; + +// Rectangular segment layer +class VDRectangularLayer : public VDLayer +{ + public: + VDRectangularLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double width, double lengthZ, double lengthSensZ); + + TGeoVolume* createSensor() const; + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + private: + double mWidth{0.f}; + double mLengthZ{0.f}; + double mLengthSensZ{0.f}; + + // ClassDef(VDRectangularLayer, 1) +}; + +// Disk segment layer +class VDDiskLayer : public VDLayer +{ + public: + VDDiskLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double rMin, double rMax, double phiSpanDeg, double zPos); + + TGeoVolume* createSensor() const; + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + double getZPosition() const { return mZPos; } + + private: + double mRMin{0.f}; + double mRMax{0.f}; + double mPhiSpanDeg{0.f}; // degrees + double mZPos{0.f}; // placement along Z + + // ClassDef(VDDiskLayer, 1) +}; + +} // namespace trk +} // namespace o2 + +#endif // ALICEO2_VD_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h new file mode 100644 index 0000000000000..c4fa222e1f4ef --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h @@ -0,0 +1,42 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRK_VDSENSORREGISTRY_H +#define O2_TRK_VDSENSORREGISTRY_H + +#include +#include + +namespace o2::trk +{ + +struct VDSensorDesc { + enum class Region { Barrel, + Disk }; + enum class Type { Curved, + Plane, + }; + std::string name; // sensor volume name + int petal = -1; + Region region = Region::Barrel; + Type type = Type::Curved; + int idx = -1; // layer or disk index +}; + +// Accessor (defined in VDGeometryBuilder.cxx) +std::vector& vdSensorRegistry(); + +// Utilities (defined in VDGeometryBuilder.cxx) +void clearVDSensorRegistry(); +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx); + +} // namespace o2::trk +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index f5027310fa66d..a4d99ccf9f79f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -19,6 +19,8 @@ #include "ITSMFTSimulation/Hit.h" #include "TRKSimulation/Detector.h" #include "TRKBase/TRKBaseParam.h" +#include "TRKSimulation/VDGeometryBuilder.h" +#include "TRKSimulation/VDSensorRegistry.h" using o2::itsmft::Hit; @@ -26,6 +28,7 @@ namespace o2 { namespace trk { + float getDetLengthFromEta(const float eta, const float radius) { return 2. * (10. + radius * std::cos(2 * std::atan(std::exp(-eta)))); @@ -48,33 +51,11 @@ Detector::Detector(bool active) if (trkPars.configFile != "") { configFromFile(trkPars.configFile); } else { - buildTRKNewVacuumVessel(); + buildTRKMiddleOuterLayers(); configToFile(); configServices(); } - mSensorName.resize(mNumberOfVolumes); // hardcoded. TODO: change size when a different naming scheme for VD is in place. Ideally could be 4 petals + 8 layers = 12 - int VDvolume = 0; - for (int i = 0; i < 4; i++) { /// VD - for (int j = 0; j < 3; j++) { - mSensorName[VDvolume].Form("%s%d_%s%d_%s%d", GeometryTGeo::getTRKPetalPattern(), i, GeometryTGeo::getTRKPetalLayerPattern(), j, GeometryTGeo::getTRKSensorPattern(), j); - VDvolume++; - } - for (int j = 0; j < 6; j++) { - mSensorName[VDvolume].Form("%s%d_%s%d_%s%d", GeometryTGeo::getTRKPetalPattern(), i, GeometryTGeo::getTRKPetalDiskPattern(), j, GeometryTGeo::getTRKSensorPattern(), j); - VDvolume++; - } - } - - for (int i = 0; i < 8; i++) { /// MLOT - mSensorName[VDvolume].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i); - VDvolume++; - } - - for (auto vd : mSensorName) { - std::cout << "Volume name: " << vd << std::endl; - } - LOGP(info, "Summary of TRK configuration:"); for (auto& layer : mLayers) { LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer.getNumber(), layer.getName(), layer.getInnerRadius(), layer.getZ(), layer.getChipThickness()); @@ -115,7 +96,7 @@ void Detector::configDefault() mLayers.emplace_back(7, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(7)}, 80.f, 258.f, 100.e-3); } -void Detector::buildTRKNewVacuumVessel() +void Detector::buildTRKMiddleOuterLayers() { // Build the TRK detector according to changes proposed during // https://indico.cern.ch/event/1407704/ @@ -125,9 +106,6 @@ void Detector::buildTRKNewVacuumVessel() mLayers.clear(); LOGP(warning, "Loading \"After Upgrade Days March 2024\" configuration for ALICE3 TRK"); - // mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 0.5f, 50.f, 100.e-4); - // mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 1.2f, 50.f, 100.e-4); - // mLayers.emplace_back(2, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(2)}, 2.5f, 50.f, 100.e-4); mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 7.f, 124.f, 100.e-3); LOGP(info, "TRKLayer created. Name: {}", std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}); mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 9.f, 124.f, 100.e-3); @@ -260,13 +238,38 @@ void Detector::createGeometry() // Add service for inner tracker mServices.createServices(vTRK); - mPetalCases.clear(); - // Add petal cases (the sensitive layers inside the petal cases get constructed here too) - auto& trkPars = TRKBaseParam::Instance(); - for (Int_t petalCaseNumber = 0; petalCaseNumber < sNumberVDPetalCases; ++petalCaseNumber) { - mPetalCases.emplace_back(petalCaseNumber, vTRK, trkPars.irisOpen); - mServices.excavateFromVacuum(mPetalCases[petalCaseNumber].getFullName()); + + // Build the VD using the petal builder + // Choose the VD design (here: IRIS4 by default). + // You can wire this to a parameter in TRKBaseParam if desired. + // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); + + o2::trk::clearVDSensorRegistry(); + o2::trk::createIRIS4Geometry(vTRK); + + // Fill sensor names from registry right after geometry creation + const auto& regs = o2::trk::vdSensorRegistry(); + mNumberOfVolumesVD = static_cast(regs.size()); + mNumberOfVolumes = mNumberOfVolumesVD + mLayers.size(); + mSensorName.resize(mNumberOfVolumes); + + // Fill VD sensor names from registry + int VDvolume = 0; + for (const auto& sensor : regs) { + mSensorName[VDvolume] = sensor.name; + VDvolume++; + } + + // Add MLOT sensor names + for (int i = 0; i < mLayers.size(); i++) { + mSensorName[VDvolume++].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i); } + + for (auto vd : mSensorName) { + std::cout << "Volume name: " << vd << std::endl; + } + + mServices.excavateFromVacuum("IRIS_CUTOUTsh"); mServices.registerVacuum(vTRK); } @@ -279,7 +282,7 @@ void Detector::InitializeO2Detector() mSensorID.resize(mNumberOfVolumes); // hardcoded. TODO: change size when a different namingh scheme for VD is in place. Ideally could be 4 petals + 8 layers = 12 for (int i = 0; i < mNumberOfVolumes; i++) { mSensorID[i] = gMC ? TVirtualMC::GetMC()->VolId(mSensorName[i]) : 0; // Volume ID from the Geant geometry - LOGP(info, "{}: mSensorID={}", i, mSensorID[i]); + LOGP(info, "{}: mSensorID={}, mSensorName={}", i, mSensorID[i], mSensorName[i].Data()); } } @@ -291,26 +294,18 @@ void Detector::defineSensitiveVolumes() TString volumeName; LOGP(info, "Adding TRK Sensitive Volumes"); - // Add petal case sensitive volumes - for (int petalCase = 0; petalCase < sNumberVDPetalCases; ++petalCase) { - // Petal layers - for (int petalLayer = 0; petalLayer < mPetalCases[petalCase].mPetalLayers.size(); ++petalLayer) { - volumeName = mPetalCases[petalCase].mPetalLayers[petalLayer].getSensorName(); - if (petalLayer == 0) { - mFirstOrLastLayers.push_back(volumeName.Data()); - } - LOGP(info, "Trying {}", volumeName.Data()); - v = geoManager->GetVolume(volumeName.Data()); - LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + // Register VD sensors created by VDGeometryBuilder + for (const auto& s : o2::trk::vdSensorRegistry()) { + TGeoVolume* v = gGeoManager->GetVolume(s.name.c_str()); + if (!v) { + LOGP(warning, "VD sensor volume '{}' not found", s.name); + continue; } - // Petal disks - for (int petalDisk = 0; petalDisk < mPetalCases[petalCase].mPetalDisks.size(); ++petalDisk) { - volumeName = mPetalCases[petalCase].mPetalDisks[petalDisk].getSensorName(); - LOGP(info, "Trying {}", volumeName.Data()); - v = geoManager->GetVolume(volumeName.Data()); - LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + LOGP(info, "Adding VD Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + // Optionally track first/last layers for TR references: + if (s.region == o2::trk::VDSensorDesc::Region::Barrel && (s.idx == 0 /*innermost*/)) { + mFirstOrLastLayers.push_back(s.name); } } @@ -455,6 +450,8 @@ bool Detector::ProcessHits(FairVolume* vol) Print(vol, volume, subDetID, layer, stave, halfstave, chipID); + mGeometryTGeo->Print(); + Hit* p = addHit(stack->GetCurrentTrackNumber(), chipID, mTrackData.mPositionStart.Vect(), positionStop.Vect(), mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx deleted file mode 100644 index c729d7d1ec4dd..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TRKSimulation/TRKPetalCase.h" -#include "TRKBase/GeometryTGeo.h" -#include - -#include "Framework/Logger.h" - -#include "TGeoTube.h" -#include "TGeoMatrix.h" -#include "TGeoCompositeShape.h" -#include "TGeoVolume.h" -#include "TString.h" -#include "TMath.h" - -namespace o2 -{ -namespace trk -{ -TRKPetalCase::TRKPetalCase(Int_t number, TGeoVolume* motherVolume, Bool_t irisOpen) : mPetalCaseNumber(number), mOpenState(irisOpen) -{ - - mWallThickness = .15e-1; // cm // Assume all the walls have the same thickness for now. - mRIn = 0.48; // cm - mROut = 3; // cm - mRInOpenState = 1.5; // cm - mPetalCaseLength = 70.; // cm - - // Calculate angular coverages of azimuthal part of wall (equivalent to that of the sensitive volumes) - mAngularCoverageAzimuthalWall = (0.25 * (2 * mRIn * TMath::Pi()) - 2 * mWallThickness) / mRIn; - mAngularCoverageRadialWall = mWallThickness / mRIn; - mToDeg = 180 / TMath::Pi(); - - // Calculate the center of the petal (x_c, y_c, z_c) based on whether it is open or not - mZPos = 0; - if (mOpenState) { - Double_t rHalfPetal = 0.5 * (mRIn + mROut); - Double_t rOpenStateCenter = TMath::Sqrt(rHalfPetal * rHalfPetal + mRInOpenState * mRInOpenState); - mXPos = rOpenStateCenter * TMath::Cos(0.25 * TMath::Pi() + (mPetalCaseNumber - 1) * 0.5 * TMath::Pi()); - mYPos = rOpenStateCenter * TMath::Sin(0.25 * TMath::Pi() + (mPetalCaseNumber - 1) * 0.5 * TMath::Pi()); - } else { - mXPos = 0.; - mYPos = 0.; - } - - // Make the petal case - constructCase(motherVolume); - // Make coldplate - constructColdPlate(motherVolume); - // Add the detection petals (quarter disks and barrel layers) - constructDetectionPetals(motherVolume); -} - -TString TRKPetalCase::getFullName() -{ - TString fullCompositeName = Form("PETALCASE%d_FULLCOMPOSITE", mPetalCaseNumber); - TGeoCompositeShape* fullCompositeShape = new TGeoCompositeShape(fullCompositeName, mFullCompositeFormula); - return fullCompositeName; -} - -void TRKPetalCase::constructCase(TGeoVolume* motherVolume) -{ - - // Petal case parts in TGeoTubeSeg - mInnerAzimuthalWall = new TGeoTubeSeg(Form("PETAL%d_INNER_AZIMUTHAL_WALL", mPetalCaseNumber), mRIn, mRIn + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - mOuterAzimuthalWall = new TGeoTubeSeg(Form("PETAL%d_OUTER_AZIMUTHAL_WALL", mPetalCaseNumber), mROut, mROut + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - mRadialWall = new TGeoTubeSeg(Form("PETAL%d_RADIAL_WALL", mPetalCaseNumber), mRIn, mROut + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageRadialWall * mToDeg, 0.5 * mAngularCoverageRadialWall * mToDeg); - mForwardWall = new TGeoTubeSeg(Form("PETAL%d_FORWARD_WALL", mPetalCaseNumber), mRIn, mROut + mWallThickness, mWallThickness / 2., -0.5 * (mAngularCoverageAzimuthalWall + 2 * mAngularCoverageRadialWall) * mToDeg, 0.5 * (mAngularCoverageAzimuthalWall + 2 * mAngularCoverageRadialWall) * mToDeg); - - // Rotate to correct section : 0-3 - mAzimuthalWallRot = new TGeoRotation((TString)Form("PETAL%d_AZIMUTHAL_WALL_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + 0.5 * mAngularCoverageAzimuthalWall + mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mAzimuthalWallRot->RegisterYourself(); - mRadialWall1Rot = new TGeoRotation((TString)Form("PETAL%d_RADIAL_WALL1_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + 0.5 * mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mRadialWall1Rot->RegisterYourself(); - mRadialWall2Rot = new TGeoRotation((TString)Form("PETAL%d_RADIAL_WALL2_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + mAngularCoverageAzimuthalWall + 1.5 * mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mRadialWall2Rot->RegisterYourself(); - - // Place to correct position (open or closed) - mAzimuthalWallCombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mAzimuthalWallRot); - mAzimuthalWallCombiTrans->RegisterYourself(); - mRadialWall1CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_RADIAL_WALL1_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mRadialWall1Rot); - mRadialWall1CombiTrans->RegisterYourself(); - mRadialWall2CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_RADIAL_WALL2_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mRadialWall2Rot); - mRadialWall2CombiTrans->RegisterYourself(); - mForwardWall1CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_FORWARD_WALL1_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, (mPetalCaseLength + mWallThickness) / 2., mAzimuthalWallRot); - mForwardWall1CombiTrans->RegisterYourself(); - mForwardWall2CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_FORWARD_WALL2_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, -(mPetalCaseLength + mWallThickness) / 2., mAzimuthalWallRot); - mForwardWall2CombiTrans->RegisterYourself(); - - TString petalCaseCompositeFormula = (TString)Form("PETAL%d_INNER_AZIMUTHAL_WALL:PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_OUTER_AZIMUTHAL_WALL:PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_RADIAL_WALL:PETAL%d_RADIAL_WALL1_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_RADIAL_WALL:PETAL%d_RADIAL_WALL2_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_FORWARD_WALL:PETAL%d_FORWARD_WALL1_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_FORWARD_WALL:PETAL%d_FORWARD_WALL2_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber); - - TGeoCompositeShape* petalCaseComposite = new TGeoCompositeShape((TString)Form("PETALCASE%dsh", mPetalCaseNumber), petalCaseCompositeFormula); - mFullCompositeFormula = petalCaseComposite->GetName(); - auto& matmgr = o2::base::MaterialManager::Instance(); - const TGeoMedium* kMedBe = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_BERYLLIUM"); - - mPetalCaseName = Form("PETALCASE%d", mPetalCaseNumber); - mPetalCaseVolume = new TGeoVolume(mPetalCaseName, petalCaseComposite, kMedBe); - mPetalCaseVolume->SetVisibility(1); - mPetalCaseVolume->SetLineColor(kGray); - - LOGP(info, "Creating IRIS Tracker vacuum petal case {}", mPetalCaseNumber); - LOGP(info, "Inserting {} in {} ", mPetalCaseVolume->GetName(), motherVolume->GetName()); - motherVolume->AddNode(mPetalCaseVolume, 1, nullptr); -} - -void TRKPetalCase::constructColdPlate(TGeoVolume* motherVolume) -{ - Double_t coldPlateRadius = 2.6; // cm - Double_t coldPlateThickness = 0.15; // cm - Double_t coldPlateLength = 50.; // cm - - mColdPlate = new TGeoTubeSeg((TString)Form("PETAL%d_COLDPLATE", mPetalCaseNumber), coldPlateRadius, coldPlateRadius + coldPlateThickness, coldPlateLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - auto& matmgr = o2::base::MaterialManager::Instance(); - const TGeoMedium* medCeramic = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_CERAMIC"); - mColdPlateVolume = new TGeoVolume(Form("COLDPLATE%d", mPetalCaseNumber), mColdPlate, medCeramic); - - TString coldPlateCompositeFormula = mColdPlate->GetName(); - coldPlateCompositeFormula += ":"; - coldPlateCompositeFormula += mAzimuthalWallCombiTrans->GetName(); - addToPetalCaseComposite(coldPlateCompositeFormula); - - mColdPlateVolume->SetVisibility(1); - mColdPlateVolume->SetLineColor(kGray); - - LOGP(info, "Creating cold plate service"); - LOGP(info, "Inserting {} in {} ", mColdPlateVolume->GetName(), motherVolume->GetName()); - motherVolume->AddNode(mColdPlateVolume, 1, mAzimuthalWallCombiTrans); -} - -void TRKPetalCase::constructDetectionPetals(TGeoVolume* motherVolume) -{ - // Add petal layers - // layerNumber, layerName, rIn, angularCoverage, zLength, layerx2X0 - mPetalLayers.emplace_back(0, Form("%s_LAYER%d", mPetalCaseName.Data(), 0), 0.5f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - mPetalLayers.emplace_back(1, Form("%s_LAYER%d", mPetalCaseName.Data(), 1), 1.2f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - mPetalLayers.emplace_back(2, Form("%s_LAYER%d", mPetalCaseName.Data(), 2), 2.5f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - for (Int_t i = 0; i < mPetalLayers.size(); ++i) { - mPetalLayers[i].createLayer(motherVolume, mAzimuthalWallCombiTrans); - } - - // Add petal disks - // diskNumber, diskName, zPos, rIn, rOut, angularCoverage, diskx2X0 - mPetalDisks.emplace_back(0, Form("%s_DISK%d", mPetalCaseName.Data(), 0), 26., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(1, Form("%s_DISK%d", mPetalCaseName.Data(), 1), 30., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(2, Form("%s_DISK%d", mPetalCaseName.Data(), 2), 34., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(3, Form("%s_DISK%d", mPetalCaseName.Data(), 3), -26., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(4, Form("%s_DISK%d", mPetalCaseName.Data(), 4), -30., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(5, Form("%s_DISK%d", mPetalCaseName.Data(), 5), -34., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - for (Int_t i = 0; i < mPetalDisks.size(); ++i) { - mPetalDisks[i].createDisk(motherVolume, mAzimuthalWallCombiTrans); - } - - addDetectionPetelsToFullComposite(); -} - -void TRKPetalCase::addDetectionPetelsToFullComposite() -{ - for (Int_t i = 0; i < mPetalLayers.size(); ++i) { - Double_t zLength = mPetalLayers[i].getZLength(); - Double_t rIn = mPetalLayers[i].getInnerRadius(); - Double_t thickness = mPetalLayers[i].getChipThickness(); - Double_t angularCoverage = mPetalLayers[i].getAngularCoverage(); - TGeoTubeSeg* layerForExcavation = new TGeoTubeSeg(Form("PETALCASE%d_EXCAVATIONLAYER%d", mPetalCaseNumber, i), rIn, rIn + thickness, zLength / 2., -0.5 * angularCoverage * mToDeg, 0.5 * angularCoverage * mToDeg); - - TString layerForExcavationCompositeFormula = layerForExcavation->GetName(); - layerForExcavationCompositeFormula += ":"; - layerForExcavationCompositeFormula += mAzimuthalWallCombiTrans->GetName(); - addToPetalCaseComposite(layerForExcavationCompositeFormula); - } - - for (Int_t i = 0; i < mPetalDisks.size(); ++i) { - Double_t zPos = mPetalDisks[i].getZ(); - Double_t rIn = mPetalDisks[i].getInnerRadius(); - Double_t rOut = mPetalDisks[i].getOuterRadius(); - Double_t thickness = mPetalDisks[i].getThickness(); - Double_t angularCoverage = mPetalDisks[i].getAngularCoverage(); - TGeoTubeSeg* diskForExcavation = new TGeoTubeSeg(Form("PETALCASE%d_EXCAVATIONDISK%d", mPetalCaseNumber, i), rIn, rOut, thickness / 2., -0.5 * angularCoverage * mToDeg, 0.5 * angularCoverage * mToDeg); - TGeoCombiTrans* diskForExcavationCombiTrans = new TGeoCombiTrans(*(mAzimuthalWallCombiTrans->MakeClone())); // Copy from petal case - diskForExcavationCombiTrans->SetName((TString)Form("PETALCASE%d_EXCAVATIONDISK%d_COMBITRANS", mPetalCaseNumber, i)); - diskForExcavationCombiTrans->SetDz(zPos); // Overwrite z location - diskForExcavationCombiTrans->RegisterYourself(); - - TString diskForExcavationCompositeFormula = diskForExcavation->GetName(); - diskForExcavationCompositeFormula += ":"; - diskForExcavationCompositeFormula += diskForExcavationCombiTrans->GetName(); - addToPetalCaseComposite(diskForExcavationCompositeFormula); - } -} - -// ClassImp(TRKPetalCase); -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx deleted file mode 100644 index e24b24b48c882..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TRKPetalDisk.cxx -/// \brief Implementation of the TRKPetalDisk class - -#include "TRKSimulation/TRKPetalDisk.h" -#include "TRKBase/GeometryTGeo.h" - -#include // for LOG - -#include "TGeoManager.h" // for TGeoManager, gGeoManager -#include "TGeoMatrix.h" // for TGeoCombiTrans, TGeoRotation, etc -#include "TGeoTube.h" // for TGeoTube, TGeoTubeSeg -#include "TGeoVolume.h" // for TGeoVolume, TGeoVolumeAssembly -#include "TGeoCompositeShape.h" // for TGeoCompositeShape -#include "TMathBase.h" // for Abs -#include "TMath.h" // for Sin, RadToDeg, DegToRad, Cos, Tan, etc -#include "TGeoTube.h" - -#include // for snprintf - -namespace o2 -{ -namespace trk -{ - -TRKPetalDisk::TRKPetalDisk(Int_t diskNumber, std::string diskName, Float_t z, Float_t rIn, Float_t rOut, Float_t angularCoverage, Float_t Diskx2X0) -{ - // Creates a simple parametrized petal disk - mDiskNumber = diskNumber; - mDiskName = diskName; - mZ = z; - mAngularCoverage = angularCoverage; - mx2X0 = Diskx2X0; - mInnerRadius = rIn; - mOuterRadius = rOut; - Float_t Si_X0 = 9.5; - mChipThickness = Diskx2X0 * Si_X0; - - LOG(info) << "Creating TRK Disk " << mDiskNumber; - LOG(info) << " Using silicon X0 = " << Si_X0 << " to emulate disk radiation length."; - LOG(info) << " Disk z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness; -} - -void TRKPetalDisk::createDisk(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans) -{ - // Create tube, set sensitive volume, add to mother volume - Double_t toDeg = 180 / TMath::Pi(); - std::string chipName = mDiskName + "_" + o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mDiskNumber), - sensName = mDiskName + "_" + Form("%s%d", GeometryTGeo::getTRKSensorPattern(), mDiskNumber); - - mSensorName = sensName; - - TGeoTubeSeg* sensor = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* chip = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* disk = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - sensVol->SetLineColor(kYellow); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - chipVol->SetLineColor(kYellow); - TGeoVolume* diskVol = new TGeoVolume(mDiskName.c_str(), disk, medAir); - diskVol->SetLineColor(kYellow); - - LOG(info) << "Inserting " << sensVol->GetName() << " inside " << chipVol->GetName(); - chipVol->AddNode(sensVol, 1, nullptr); - - LOG(info) << "Inserting " << chipVol->GetName() << " inside " << diskVol->GetName(); - diskVol->AddNode(chipVol, 1, nullptr); - - // Finally put everything in the mother volume - TGeoCombiTrans* fwdPetalCombiTrans = new TGeoCombiTrans(*(combiTrans->MakeClone())); // Copy from petal case - fwdPetalCombiTrans->SetDz(mZ); // Overwrite z location - fwdPetalCombiTrans->RegisterYourself(); - - LOG(info) << "Inserting " << diskVol->GetName() << " inside " << motherVolume->GetName(); - motherVolume->AddNode(diskVol, 1, fwdPetalCombiTrans); -} -// ClassImp(TRKPetalLayer); - -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx deleted file mode 100644 index c8ff0d957bb19..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TRKSimulation/TRKPetalLayer.h" -#include "TRKBase/GeometryTGeo.h" - -#include "Framework/Logger.h" - -#include "TGeoTube.h" -#include "TGeoBBox.h" -#include "TGeoVolume.h" -#include "TGeoTube.h" -#include "TGeoMatrix.h" - -#include "TMath.h" - -namespace o2 -{ -namespace trk -{ -TRKPetalLayer::TRKPetalLayer(Int_t layerNumber, std::string layerName, Float_t rIn, Float_t angularCoverage, Float_t zLength, Float_t layerX2X0) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rIn), mAngularCoverage(angularCoverage), mZ(zLength), mX2X0(layerX2X0), mModuleWidth(4.54) -{ - Float_t Si_X0 = 9.5f; - mChipThickness = mX2X0 * Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} thickness: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mChipThickness, mZ, mX2X0); -} - -void TRKPetalLayer::createLayer(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans) -{ - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - - std::string staveName = mLayerName + "_" + o2::trk::GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber), - chipName = mLayerName + "_" + o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mLayerNumber), - sensName = mLayerName + "_" + Form("%s%d", GeometryTGeo::getTRKSensorPattern(), mLayerNumber); - - mSensorName = sensName; - - Double_t toDeg = 180 / TMath::Pi(); - mLayer = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), mLayer, medAir); - layerVol->SetLineColor(kYellow); - - TGeoTubeSeg* stave = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* chip = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* sensor = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - sensVol->SetLineColor(kYellow); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - chipVol->SetLineColor(kYellow); - TGeoVolume* staveVol = new TGeoVolume(staveName.c_str(), stave, medSi); - staveVol->SetLineColor(kYellow); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), staveVol->GetName()); - staveVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, combiTrans); -} -// ClassImp(TRKPetalLayer); - -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 1fb966425f974..51eea905c436a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -173,7 +173,9 @@ void TRKServices::registerVacuum(TGeoVolume* motherVolume) TGeoVolume* vacuumVolume = new TGeoVolume("A3IP_VACUUM", vacuumComposite, kMedVac); // Add the vacuum to the barrel - vacuumVolume->SetLineColor(kGreen - 3); + vacuumVolume->SetLineColor(kAzure + 7); + vacuumVolume->SetTransparency(80); + motherVolume->AddNode(vacuumVolume, 1, new TGeoTranslation(0, 0, 0)); } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h index d80027593cef0..1a2e93636491c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -15,10 +15,8 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::trk::TRKPetalCase + ; #pragma link C++ class o2::trk::TRKLayer + ; -#pragma link C++ class o2::trk::TRKPetalLayer + ; -#pragma link C++ class o2::trk::TRKPetalDisk + ; +#pragma link C++ class o2::trk::VDLayer + ; #pragma link C++ class o2::trk::TRKServices + ; #pragma link C++ class o2::trk::Detector + ; #pragma link C++ class o2::base::DetImpl < o2::trk::Detector> + ; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx new file mode 100644 index 0000000000000..54567a50fa4cf --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -0,0 +1,743 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKSimulation/VDGeometryBuilder.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TGeoManager.h" + +#include "Framework/Logger.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/VDLayer.h" +#include "TRKSimulation/VDSensorRegistry.h" + +namespace o2::trk +{ + +static std::vector gVDSensors; // stays in this TU only +std::vector& vdSensorRegistry() { return gVDSensors; } + +void clearVDSensorRegistry() { gVDSensors.clear(); } + +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx) +{ + gVDSensors.push_back({volName, petal, region, type, idx}); +} + +static inline std::string makeSensorName(const std::string& layerName, int layerNumber) +{ + return Form("%s_%s%d", layerName.c_str(), o2::trk::GeometryTGeo::getTRKSensorPattern(), layerNumber); +} + +namespace +{ + +// Config: which volumes count as SOLIDS to subtract from the vacuum volume +inline bool isSolidToCut(const TGeoVolume* v) +{ + const char* nm = v->GetName(); + const char* med = v->GetMedium() ? v->GetMedium()->GetName() : ""; + // silicon sensors (barrel + disks) + if (med && strcmp(med, "TRK_SILICON$") == 0) + return true; + // walls, sidewalls, cold-plate, service rings (names from your builders) + if (TString(nm).BeginsWith("VD_InnerWallArc")) + return true; + if (TString(nm).BeginsWith("VD_OuterWallArc")) + return true; + if (TString(nm).BeginsWith("VD_SideWall")) + return true; + if (TString(nm).Contains("_Coldplate")) + return true; + if (TString(nm).BeginsWith("IRIS_Service_Neg")) + return true; + if (TString(nm).BeginsWith("IRIS_Service_Pos_InVac")) + return true; + return false; +} + +// Ensure every leaf shape has a stable, informative name +inline const char* ensureShapeName(TGeoVolume* v) +{ + auto* sh = v->GetShape(); + TString nm = sh->GetName(); + if (nm.IsNull() || nm.BeginsWith("TGeo")) { + TString wanted = TString(v->GetName()) + "_sh"; + // avoid collisions + int k = 0; + TString cand = wanted; + auto* shapes = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + while (shapes && shapes->FindObject(cand)) + cand = Form("%s_%d", wanted.Data(), ++k); + sh->SetName(cand); + if (shapes && !shapes->FindObject(cand)) + shapes->Add(sh); + } + return sh->GetName(); +} + +// Recorder state for the petal-local composite +static TString gPetalSolidsFormula; +static int gLocalTrIdx = 0; + +// add "ShapeName:IRIS_LOC_TR_k" to the petal-local formula (no outer rotation) +inline void appendLocalTerm(const char* shapeName, const TGeoHMatrix& H) +{ + auto* ct = new TGeoCombiTrans(H); + ct->SetName(Form("IRIS_LOC_TR_%d", gLocalTrIdx++)); + ct->RegisterYourself(); + if (!gPetalSolidsFormula.IsNull()) + gPetalSolidsFormula += "+"; + gPetalSolidsFormula += TString::Format("%s:%s", shapeName, ct->GetName()); +} + +// DFS: compose LOCAL transforms only (identity prefix), to capture the petal contents +void traversePetalLocal(TGeoVolume* vol, const TGeoHMatrix& prefix) +{ + auto* nodes = vol->GetNodes(); + if (!nodes) + return; + for (int i = 0; i < nodes->GetEntriesFast(); ++i) { + auto* node = (TGeoNode*)nodes->At(i); + auto* childV = node->GetVolume(); + TGeoHMatrix H(prefix); + if (auto* m = node->GetMatrix()) + H.Multiply(m); + + if (isSolidToCut(childV)) { + const char* shapeName = ensureShapeName(childV); + appendLocalTerm(shapeName, H); + } + traversePetalLocal(childV, H); + } +} + +// Build (once) a petal-local composite containing ONLY solids (walls, silicon, coldplate, services, disks) +inline void buildPetalSolidsComposite(TGeoVolume* petalAsm) +{ + // If it already exists, skip + if (gGeoManager && gGeoManager->GetListOfShapes() && gGeoManager->GetListOfShapes()->FindObject("IRIS_PETAL_SOLIDSsh")) + return; + + gPetalSolidsFormula.Clear(); + gLocalTrIdx = 0; + + TGeoHMatrix I; // identity + traversePetalLocal(petalAsm, I); + + if (gPetalSolidsFormula.IsNull()) { + LOGP(error, "IRIS_PETAL_SOLIDSsh formula is empty; did not find solids in petal."); + return; + } + + LOGP(info, "IRIS_PETAL_SOLIDSsh formula: {}", gPetalSolidsFormula.Data()); + new TGeoCompositeShape("IRIS_PETAL_SOLIDSsh", gPetalSolidsFormula.Data()); +} + +// Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase +inline void buildIrisCutoutFromPetalSolid(int nPetals) +{ + // Create n rotation transforms + TString cutFormula; + for (int p = 0; p < nPetals; ++p) { + const double phi = (360.0 / nPetals) * (p + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phi); + auto* RT = new TGeoCombiTrans(0, 0, 0, R); + RT->SetName(Form("IRIS_PETAL_ROT_%d", p)); + RT->RegisterYourself(); + if (p) + cutFormula += "+"; + cutFormula += Form("IRIS_PETAL_SOLIDSsh:%s", RT->GetName()); + } + LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); + new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); + + // --- Sanity check: required matrices & shapes exist + auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() : nullptr; + auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + + if (!mats || !shps) { + LOGP(error, "IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing)."); + } else { + bool ok = true; + + // Check the petal rotations were registered and referenced + for (int p = 0; p < nPetals; ++p) { + const TString name = Form("IRIS_PETAL_ROT_%d", p); + if (!mats->FindObject(name)) { + LOGP(error, "IRIS cutout sanity: missing matrix {}", name.Data()); + ok = false; + } + } + + // Check that the local petal composite exists + if (!shps->FindObject("IRIS_PETAL_SOLIDSsh")) { + LOGP(error, "IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found."); + ok = false; + } + + // Check that the global cutout shape was created + if (!shps->FindObject("IRIS_CUTOUTsh")) { + LOGP(error, "IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found."); + ok = false; + } + + if (ok) { + LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); + } + } +} + +} // namespace + +// =================== Specs & constants (ROOT units: cm) =================== +static constexpr double kX2X0 = 0.001f; // 0.1% X0 per layer +static constexpr double kLenZ_cm = 50.0f; // L0/L1/L2 Z length + +// Radii (cm) +static constexpr double rL0_cm = 0.5f; // 5 mm +static constexpr double rL1_cm = 1.2f; // 12 mm +static constexpr double rL2_cm = 2.5f; // 25 mm + +// IRIS5 rectangular L0 width (cm) +static constexpr double kL0RectHeight_cm = 0.5f; // 5.0 mm +static constexpr double kL0RectWidth_cm = 0.83f; // 8.3 mm + +// Disks radii (cm) +static constexpr double diskRin_cm = 0.5f; // 5 mm +static constexpr double diskRout_cm = 2.5f; // 25 mm +static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f}; + +// Petal walls specifications (cm) +static constexpr double kPetalZ_cm = 70.0f; // full wall height +static constexpr double kWallThick_cm = 0.015f; // 0.15 mm +static constexpr double kInnerWallRadius_cm = 0.48f; // 4.8 mm (ALWAYS cylindrical) +static constexpr double kOuterWallRadius_cm = 3.0f; // 30 mm (can be changed) +static constexpr double kEps_cm = 1.e-4f; + +// Coldplate specs (cm) +static constexpr double kColdplateRadius_cm = 2.6f; // 26 mm (outer radius) +static constexpr double kColdplateThickness_cm = 0.15f; // 1.5 mm +static constexpr double kColdplateZ_cm = 50.0f; // full length + +// ========== φ-span helpers (gap/arc → degrees) ========== +namespace +{ + +// Convert a linear gap at radius R into an angular gap (deg) +inline double degFromArc(double arc, double radius) +{ + // arc and radius in the SAME units (cm or mm); result in degrees + return (radius > 0.f) ? (arc / radius) * TMath::RadToDeg() : 0.f; +} + +/** + * Compute silicon segment φ-span (degrees) inside one petal, + * when you know the number of petals and the linear gap at a given radius. + * + * All of: gap and radius must be in the SAME units (cm or mm). + * If you use cm everywhere (ROOT default), pass gap_cm and radius_cm. + */ +inline double phiSpanFromGap(int nPetals, double gap, double radius) +{ + if (nPetals <= 0 || radius <= 0.f) + return 0.f; + const double petalPhiDeg = 360.f / nPetals; + const double phi = petalPhiDeg - degFromArc(gap, radius); + return phi > 0.f ? phi : 0.f; +} + +/** + * Compute silicon segment φ-span (degrees) from a known arc length at a given radius. + * arcLen and radius must be in the SAME units (cm or mm). + */ +inline double phiSpanFromArc(double arcLen, double radius) +{ + return (arcLen > 0.f && radius > 0.f) ? degFromArc(arcLen, radius) : 0.f; +} + +inline TGeoCombiTrans rotZ(double phiDeg) +{ + auto* r = new TGeoRotation(); + r->RotateZ(static_cast(phiDeg)); + return TGeoCombiTrans(0., 0., 0., r); +} +} // namespace + +// ============ Petal sub-builders (LOCAL coords only, no rotation) ========= + +// Walls: inner cylindrical arc at r=4.8 mm (always), outer arc wall, and two side plates. +static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_cm = kOuterWallRadius_cm) +{ + if (!petalAsm) { + LOGP(error, "addPetalWalls: petalAsm is null"); + return; + } + + auto& matmgr = o2::base::MaterialManager::Instance(); + const TGeoMedium* med = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083"); + + if (!med) { + LOGP(warning, "Petal walls: ALICE3_TRKSERVICES_ALUMINIUM5083$ not found, walls not created."); + return; + } + + const double halfPhi = 0.5f * (360.f / static_cast(nPetals)); + const double halfZ = 0.5f * kPetalZ_cm; + + // ---- Inner cylindrical wall (always at r=4.8 mm) ---- + { + auto* s = new TGeoTubeSeg(static_cast(kInnerWallRadius_cm), + static_cast(kInnerWallRadius_cm + kWallThick_cm), + static_cast(halfZ), + static_cast(-halfPhi), + static_cast(+halfPhi)); + auto* v = new TGeoVolume("VD_InnerWallArc", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } + + // ---- Outer arc wall ---- + { + auto* s = new TGeoTubeSeg(static_cast(outerRadius_cm), + static_cast(outerRadius_cm + kWallThick_cm), + static_cast(halfZ), + static_cast(-halfPhi), + static_cast(+halfPhi)); + auto* v = new TGeoVolume("VD_OuterWallArc", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } + + // ---- Side walls (boxes) at ±halfPhi ---- + const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm)); + auto* sideS = new TGeoBBox(static_cast(0.5f * radialLen), + static_cast(0.5f * kWallThick_cm), + static_cast(halfZ)); + auto* sideV = new TGeoVolume("VD_SideWall", sideS, med); + sideV->SetLineColor(kGray + 2); + sideV->SetTransparency(70); + + for (int sgn : {-1, +1}) { + const double phi = sgn * halfPhi; + const double rMid = kInnerWallRadius_cm + kWallThick_cm + 0.5f * radialLen; + const double rad = static_cast(TMath::DegToRad()); + const double x = rMid * std::cos(phi * rad); + const double y = rMid * std::sin(phi * rad); + auto* rot = new TGeoRotation(); + rot->RotateZ(static_cast(phi)); + auto* tr = new TGeoCombiTrans(static_cast(x), + static_cast(y), + 0.0, rot); + petalAsm->AddNode(sideV, (sgn < 0 ? 1 : 2), tr); + } +} + +// Build inner layers (L0..L2). L0 may be rectangular (IRIS5) or cylindrical. +// φ-spans derive from spec gaps/arc; all local placement (no rotation). +static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0) +{ + if (!petalAsm) { + LOGP(error, "addBarrelLayers: petalAsm is null"); + return; + } + + // Per spec (mm → cm) + constexpr double gapL0_cm = 0.163f; // 1.63 mm + constexpr double gapL1L2_cm = 0.12f; // 1.2 mm + constexpr double arcL0_cm = 0.6247f; // 6.247 mm + + // φ spans + const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); // L0 gap-defined + const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); // L1 gap-defined + const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined + + const std::string nameL0 = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "0"; + + if (rectangularL0) { + VDRectangularLayer L0(0, + nameL0, + kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm); + + // Correct translation: move to radius + half width along x + double x = kL0RectHeight_cm + L0.getChipThickness() / 2.; + LOGP(info, "Placing rectangular L0 at r={:.3f} cm (half-width={:.3f} cm)", x, 0.5f * kL0RectWidth_cm); + double y = 0.0; + double z = 0.0; + + // Correct rotation: rotate 90 degrees around z so long side is horizontal + auto* rot = new TGeoRotation(); + rot->RotateZ(90.0); + + auto* tr = new TGeoCombiTrans(x, y, z, rot); + L0.createLayer(petalAsm, tr); + registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Plane, /*idx*/ 0); + } else { + VDCylindricalLayer L0(0, + nameL0, + kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm); + L0.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 0); + } + + const std::string nameL1 = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "1"; + + VDCylindricalLayer L1(1, + nameL1, + kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm); + L1.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 1); + + const std::string nameL2 = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "2"; + + VDCylindricalLayer L2(2, + nameL2, + kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm); + L2.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL2, 2), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 2); +} + +// Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly. +static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) +{ + if (!petalAsm) { + LOGP(error, "addColdPlate: petalAsm is null"); + return; + } + + // Resolve medium: prefer provided medium, otherwise try to fetch from geo manager + const TGeoMedium* med = gGeoManager->GetMedium("ALICE3_TRKSERVICES_CERAMIC"); + if (!med) { + LOGP(error, "addColdPlate: can't find the medium."); + } + + // Angular span for one petal (deg) + constexpr double gapL1L2_cm = 0.12f; // 1.2 mm + + // φ spans + const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined + const double halfPhiDeg = 0.5f * phiSpanColdplate_deg; + const double startPhi = -halfPhiDeg; + const double endPhi = +halfPhiDeg; + + // Build tube segment: inner radius, outer radius = inner + thickness, half-length Z + auto* shape = new TGeoTubeSeg(static_cast(kColdplateRadius_cm), + static_cast(kColdplateRadius_cm + kColdplateThickness_cm), + static_cast(0.5 * kColdplateZ_cm), + static_cast(startPhi), + static_cast(endPhi)); + + TString volName = TString::Format("Petal%d_Coldplate", petalId); + auto* coldVol = new TGeoVolume(volName, shape, med); + coldVol->SetLineColor(kAzure - 3); + coldVol->SetTransparency(10); + + // Place in local petal coordinates (no extra transform); keep object alive by allocating shape/volume on heap. + petalAsm->AddNode(coldVol, 1); + + LOGP(info, "Adding cold plate {} r={:.3f} cm t={:.3f} cm Lz={:.3f} cm φ=[{:.3f}, {:.3f}]", + volName.Data(), kColdplateRadius_cm, kColdplateThickness_cm, kColdplateZ_cm, startPhi, endPhi); +} + +// Add IRIS service module(s) as aluminum annular cylinders placed outside the petals. +// The two modules are placed at z = ±(36 + halfLength). +static void addIRISServiceModules(TGeoVolume* petalAsm, int nPetals) +{ + if (!petalAsm) { + LOGP(error, "addIRISServiceModules: petalAsm is null"); + return; + } + + auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70); + const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl); + + if (!med) { + LOGP(error, "addIRISServiceModules: ALUMINUM medium not found."); + return; + } + + constexpr double radius = 3.2; // cm (inner radius) + constexpr double thickness = 0.133; // cm (radial thickness) + constexpr double halfLength = 19.5; // cm (half-length along Z) + const double rIn = radius; + const double rOut = radius + thickness; + + // Petal angular span. If you have an exact half-φ from your walls, use it here. + const double halfPhi_deg = 0.5 * (360.0 / double(nPetals)); + + // Create shape once and reuse + auto* segSh = new TGeoTubeSeg( + "IRIS_SERVICE_SEGsh", + rIn, rOut, + halfLength, + -halfPhi_deg, halfPhi_deg); + + // Positive Z module + TString namePos = "IRIS_Service_Pos"; + auto* volPos = new TGeoVolume(namePos, segSh, med); + volPos->SetLineColor(kRed + 2); + volPos->SetTransparency(50); + + // Negative Z module: reuse same shape object, give different name + TString nameNeg = "IRIS_Service_Neg"; + auto* volNeg = new TGeoVolume(nameNeg, segSh, med); + volNeg->SetLineColor(kRed + 2); + volNeg->SetTransparency(50); + + // Translations (heap-allocated so ROOT keeps them) + const double zpos = 36.0 + halfLength; + auto* transPos = new TGeoTranslation(0.0, 0.0, static_cast(zpos)); + auto* transNeg = new TGeoTranslation(0.0, 0.0, static_cast(-zpos)); + + // Add to mother volume + petalAsm->AddNode(volPos, 1, transPos); + petalAsm->AddNode(volNeg, 2, transNeg); + + LOGP(info, "Added IRIS service modules at z = ±{} cm, r=[{}, {}] cm", zpos, rIn, rOut); +} + +// Only the A-side "inside vacuum" piece participates in the cutout. +static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) +{ + if (!petalAsm) { + LOGP(error, "addIRISServiceModulesSegmented: petalAsm is null"); + return; + } + + // --- Service geometry (same as your previous values) + constexpr double rIn = 3.2; // cm + constexpr double thickness = 0.133; // cm + constexpr double rOut = rIn + thickness; + constexpr double halfLen = 19.5; // cm + constexpr double z0 = 36.0 + halfLen; // 55.5 cm center of +Z service + const double zMinA = z0 - halfLen; // 36.0 cm + const double zMaxA = z0 + halfLen; // 75.0 cm + + // --- Vacuum vessel window around z∈[-L/2, +L/2] with wall thickness on +Z side + // Keep these in sync with TRKServices::createVacuumCompositeShape() + constexpr double vacuumVesselLength = 76.0; // cm + constexpr double vacuumVesselThickness = 0.08; // cm (0.8 mm) + const double halfVess = 0.5 * vacuumVesselLength; // 38.0 cm + const double gapStart = halfVess; // 38.00 + const double gapEnd = halfVess + vacuumVesselThickness; // 38.08 + + // --- Petal φ-span (segment) + const double halfPhi = 0.5 * (360.0 / double(nPetals)); + + auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70); + const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl); + + if (!med) { + LOGP(error, "addIRISServiceModules: ALUMINUM medium not found."); + return; + } + + // ========================= + // C-side (negative Z) whole + // ========================= + { + auto* sh = new TGeoTubeSeg(rIn, rOut, halfLen, -halfPhi, +halfPhi); + auto* vN = new TGeoVolume("IRIS_Service_Neg", sh, med); + vN->SetLineColor(kRed + 2); + vN->SetTransparency(55); + petalAsm->AddNode(vN, 1, new TGeoTranslation(0., 0., -(z0))); + } + + // ===================================== + // A-side (positive Z): split with a gap + // ===================================== + // Piece 1 (INSIDE vacuum): z ∈ [zMinA, min(zMaxA, gapStart)] → goes into cutout + const double L_inVac = std::max(0.0, std::min(zMaxA, gapStart) - zMinA); // expected ~2.0 cm + if (L_inVac > 0) { + const double dz = 0.5 * L_inVac; + const double zc = zMinA + dz; // center of lower slice, ≈ 37.0 cm + auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, halfPhi); + sh->SetName("IRIS_SERVICE_POS_INVACsh"); + auto* vP = new TGeoVolume("IRIS_Service_Pos_InVac", sh, med); + vP->SetLineColor(kRed + 2); + vP->SetTransparency(55); + petalAsm->AddNode(vP, 1, new TGeoTranslation(0., 0., zc)); + LOGP(info, "IRIS A-side (InVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm", + zc - dz, zc + dz, 2 * dz); + } else { + LOGP(warning, "IRIS A-side (InVac): no overlap with vacuum (L_inVac<=0)"); + } + + // Gap (no material): (gapStart, gapEnd) = (38.00, 38.08) + + // Piece 2 (OUT of vacuum): z ∈ [max(zMinA, gapEnd), zMaxA] → NOT in cutout + const double L_outVac = std::max(0.0, zMaxA - std::max(zMinA, gapEnd)); // expected ~36.92 cm + if (L_outVac > 0) { + const double dz = 0.5 * L_outVac; + const double zc = std::max(zMinA, gapEnd) + dz; // center of upper slice + auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, +halfPhi); + sh->SetName("IRIS_SERVICE_POS_OUTVACsh"); + auto* vP = new TGeoVolume("IRIS_Service_Pos_OutVac", sh, med); + vP->SetLineColor(kRed + 1); + vP->SetTransparency(70); + petalAsm->AddNode(vP, 2, new TGeoTranslation(0., 0., +zc)); + LOGP(info, "IRIS A-side (OutVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm", + zc - dz, zc + dz, 2 * dz); + } else { + LOGP(warning, "IRIS A-side (OutVac): no upper piece (L_outVac<=0)"); + } +} + +// Build disks in local coords: each disk gets only a local Z translation. +// φ span from gap at rOut. +static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) +{ + + if (!petalAsm) { + LOGP(error, "addDisks: petalAsm is null"); + return; + } + + const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); + + for (int i = 0; i < 6; ++i) { + const std::string nameD = + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalDiskPattern()) + std::to_string(i); + + VDDiskLayer disk(i, + nameD, + kX2X0, diskRin_cm, diskRout_cm, phiDisk_deg, diskZ_cm[i]); + + // Local Z placement only + auto* tr = new TGeoTranslation(0.0, 0.0, static_cast(disk.getZPosition())); + disk.createLayer(petalAsm, tr); + registerSensor(makeSensorName(nameD, i), petalID, VDSensorDesc::Region::Disk, VDSensorDesc::Type::Plane, /*idx*/ i); + } +} + +// Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords. +static TGeoVolume* buildPetalAssembly(int nPetals, int petalID, bool rectangularL0) +{ + auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); + addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm); + + // Pass petalID to layers/disks for naming + addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0); + addColdPlate(petalAsm, nPetals, petalID); + addDisks(petalAsm, nPetals, petalID); + addIRISServiceModulesSegmented(petalAsm, nPetals); + + return petalAsm; +} + +// =================== Public entry points =================== + +void createIRIS4Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS4Geometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRIS5Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS5Geometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRIS4aGeometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS4aGeometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 3; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID, int nPetals, bool rectangularL0) +{ + auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0); + + // Optionally rotate the petal for display + const double phiDeg = (360.f / static_cast(nPetals)) * (static_cast(petalID) + 0.5f); + auto* R = new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", phiDeg, 0, 0)); + motherVolume->AddNode(petal, 1, R); + + LOGP(info, "Debug: Added Petal{} to {}", petalID, motherVolume->GetName()); +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx new file mode 100644 index 0000000000000..b762ba5c9b36a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -0,0 +1,314 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKSimulation/VDLayer.h" +#include "TRKBase/GeometryTGeo.h" + +#include "Framework/Logger.h" + +#include "TGeoTube.h" +#include "TGeoBBox.h" +#include "TGeoVolume.h" +#include "TGeoMatrix.h" +#include "TGeoManager.h" + +#include "TMath.h" + +namespace o2 +{ +namespace trk +{ +// Base layer constructor +VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0) + : mLayerNumber(layerNumber), mLayerName(layerName), mX2X0(layerX2X0), mModuleWidth(4.54) +{ + constexpr double kSiX0_cm = 9.5; // Radiation length of Silicon in cm + mChipThickness = mX2X0 * kSiX0_cm; +} + +// VDCylindricalLayer constructor +VDCylindricalLayer::VDCylindricalLayer(int layerNumber, const std::string& layerName, double layerX2X0, double radius, + double phiSpanDeg, double lengthZ, double lengthSensZ) + : VDLayer(layerNumber, layerName, layerX2X0), mRadius(radius), mPhiSpanDeg(phiSpanDeg), mLengthZ(lengthZ), mLengthSensZ(lengthSensZ) +{ + LOGP(info, "Creating VD cylindrical layer: id: {} name: {} x2X0: {} radius: {} phiSpanDeg: {} lengthZ: {} lengthSensZ: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, radius, phiSpanDeg, lengthZ, lengthSensZ, mChipThickness); +} + +// VDRectangularLayer constructor +VDRectangularLayer::VDRectangularLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double width, double lengthZ, double lengthSensZ) + : VDLayer(layerNumber, layerName, layerX2X0), mWidth(width), mLengthZ(lengthZ), mLengthSensZ(lengthSensZ) +{ + + if (mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(fatal, "Invalid sensor length: sensZ={} layerZ={}", mLengthSensZ, mLengthZ); + } + LOGP(info, "Creating VD rectangular layer: id: {} name: {} x2X0: {} width: {} lengthZ: {} lengthSensZ: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, width, lengthZ, lengthSensZ, mChipThickness); +} + +// VDDiskLayer constructor +VDDiskLayer::VDDiskLayer(int layerNumber, const std::string& layerName, double layerX2X0, double rMin, double rMax, + double phiSpanDeg, double zPos) + : VDLayer(layerNumber, layerName, layerX2X0), mRMin(rMin), mRMax(rMax), mPhiSpanDeg(phiSpanDeg), mZPos(zPos) +{ + + LOGP(info, "Creating VD disk layer: id: {} name: {} x2X0: {} rMin: {} rMax: {} phiSpanDeg: {} zPos: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, rMin, rMax, phiSpanDeg, zPos, mChipThickness); +} + +/* +** Create sensor +*/ + +TGeoVolume* VDCylindricalLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); + vol->SetLineColor(kYellow); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDRectangularLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; // thickness in Y + const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer + + auto* shape = new TGeoBBox(hx, hy, hz); + auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); + vol->SetLineColor(kYellow); + vol->SetTransparency(30); + + return vol; +} + +TGeoVolume* VDDiskLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk sensor dims: rMin={}, rMax={}, t={}, phiSpanDeg={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double halfThickness = 0.5 * mChipThickness; // disk thickness is along Z + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + + // Same geometry as the layer (identical radii + phi span + thickness) + auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + + auto* sensVol = new TGeoVolume(sensName.c_str(), shape, medSi); + sensVol->SetLineColor(kYellow); + sensVol->SetTransparency(30); + + return sensVol; +} + +/* +** Create layer +*/ + +// Cylindrical layer +void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + // Sanity + if (mRadius <= 0 || mChipThickness <= 0 || mLengthZ <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0 || + mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(error, "Invalid cylindrical dimensions: r={}, t={}, Z={}, phi={}, sensZ={}", + mRadius, mChipThickness, mLengthZ, mPhiSpanDeg, mLengthSensZ); + return; + } + + // AIR container (layer) + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthZ; + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + + auto* layerShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor volume (must use mLengthSensZ internally) + TGeoVolume* sensorVol = VDCylindricalLayer::createSensor(); + if (!sensorVol) { + LOGP(error, "VDCylindricalLayer::createSensor() returned null"); + return; + } + LOGP(info, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); + layerVol->AddNode(sensorVol, 1, nullptr); + + // Tiling: edge-to-edge if sensor shorter than layer; else single centered + // const auto zCenters = (mLengthSensZ < mLengthZ) + // ? centersNoGapZ(mLengthZ, mLengthSensZ) + // : std::vector{0.0}; + // + // int copyNo = 1; + // for (double zc : zCenters) { + // TGeoTranslation tz(0.0, 0.0, zc); + // layerVol->AddNode(sensorVol, copyNo++, (zc == 0.0 && zCenters.size() == 1) ? nullptr : &tz); + // } + + motherVolume->AddNode(layerVol, 1, combiTrans); +} + +// Rectangular layer +void VDRectangularLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + if (mWidth <= 0 || mChipThickness <= 0 || mLengthZ <= 0 || + mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(error, "Invalid rectangular dims: W={}, t={}, Z={}, sensZ={}", + mWidth, mChipThickness, mLengthZ, mLengthSensZ); + return; + } + + // AIR container (layer) + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; + const double hz = 0.5 * mLengthZ; + + auto* layerShape = new TGeoBBox(hx, hy, hz); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor volume (uses mLengthSensZ internally) + TGeoVolume* sensorVol = VDRectangularLayer::createSensor(); + if (!sensorVol) { + LOGP(error, "VDRectangularLayer::createSensor() returned null"); + return; + } + + LOGP(info, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); + layerVol->AddNode(sensorVol, 1, nullptr); + + // Tiling along Z, edge - to - edge if needed + // const auto zCenters = (mLengthSensZ < mLengthZ) + // ? centersNoGapZ(mLengthZ, mLengthSensZ) + // : std::vector{0.0}; + // + // int copyNo = 1; + // for (double zc : zCenters) { + // TGeoTranslation tz(0.0, 0.0, zc); + // layerVol->AddNode(sensorVol, copyNo++, (zc == 0.0 && zCenters.size() == 1) ? nullptr : &tz); + // } + + motherVolume->AddNode(layerVol, 1, combiTrans); +} + +// Disk layer +void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk dims: rMin={}, rMax={}, t={}, phi={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return; + } + + // For disks the thickness is along Z and equals mChipThickness + const double halfThickness = 0.5 * mChipThickness; + const double halfPhi = 0.5 * mPhiSpanDeg; + + // AIR container (layer) + auto* layerShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor (same size & shape as the layer for disks) + TGeoVolume* sensorVol = VDDiskLayer::createSensor(); + if (!sensorVol) { + LOGP(error, "VDDiskLayer::createSensor() returned null"); + return; + } + + // Insert single sensor (no Z-segmentation for disks) + layerVol->AddNode(sensorVol, 1, nullptr); + + TGeoTranslation tz(0.0, 0.0, mZPos); + motherVolume->AddNode(layerVol, 1, combiTrans ? combiTrans : &tz); +} + +// ClassImp(VDLayer); +// ClassImp(VDCylindricalLayer); +// ClassImp(VDRectangularLayer); +// ClassImp(VDDiskLayer); + +} // namespace trk +} // namespace o2 \ No newline at end of file