diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h index 0d14edec5ac04..88edd2cb90f1f 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h @@ -70,7 +70,8 @@ class DetID static constexpr ID ACO = 15; #ifdef ENABLE_UPGRADES static constexpr ID IT3 = 16; - static constexpr ID Last = IT3; + static constexpr ID EC0 = 17; + static constexpr ID Last = EC0; #else static constexpr ID Last = ACO; ///< if extra detectors added, update this !!! #endif @@ -132,7 +133,7 @@ class DetID static constexpr const char* sDetNames[nDetectors + 1] = ///< defined detector names #ifdef ENABLE_UPGRADES - {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "ACO", "IT3", nullptr}; + {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "ACO", "IT3", "EC0", nullptr}; #else {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "ACO", nullptr}; #endif @@ -144,7 +145,7 @@ class DetID utils::bit2Mask(ACO) #ifdef ENABLE_UPGRADES , - utils::bit2Mask(IT3) + utils::bit2Mask(IT3), utils::bit2Mask(EC0) #endif }; @@ -155,7 +156,7 @@ class DetID o2h::gDataOriginMID, o2h::gDataOriginZDC, o2h::gDataOriginFT0, o2h::gDataOriginFV0, o2h::gDataOriginFDD, o2h::gDataOriginACO #ifdef ENABLE_UPGRADES , - o2h::gDataOriginIT3 + o2h::gDataOriginIT3, o2h::gDataOriginEC0 #endif }; diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h index 8ae6399a7a065..5b6b9d25c4bcb 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h @@ -35,8 +35,8 @@ class SimTraits // initialization fragile since depends on correct order. Can we do better? // clang-format off - static inline const std::array, DetID::nDetectors> DETECTORBRANCHNAMES = - { /*ITS*/ VS{ "ITSHit" }, + static inline const std::array, DetID::nDetectors> DETECTORBRANCHNAMES = + { /*ITS*/ VS{ "ITSHit" }, /*TPC*/ VS{ "TPCHitsShiftedSector0", "TPCHitsShiftedSector1", "TPCHitsShiftedSector2", @@ -89,7 +89,8 @@ class SimTraits /*ACO*/ VS{ "ACOHit" } #ifdef ENABLE_UPGRADES , - /*IT3*/ VS{ "ITS3Hit" } + /*IT3*/ VS{ "ITS3Hit" }, + /*EC0*/ VS{ "EC0Hit" } #endif }; // clang-format on @@ -158,6 +159,13 @@ namespace tpc class HitGroup; } +#ifdef ENABLE_UPGRADES +namespace endcaps +{ +class Hit; +} +#endif + namespace detectors { @@ -226,6 +234,10 @@ template <> struct DetIDToHitTypes { using HitType = o2::itsmft::Hit; }; +template <> +struct DetIDToHitTypes { + using HitType = o2::endcaps::Hit; +}; #endif } // namespace detectors diff --git a/DataFormats/Detectors/Common/src/DetID.cxx b/DataFormats/Detectors/Common/src/DetID.cxx index 6214caf5c2c0f..d7375b5df79e3 100644 --- a/DataFormats/Detectors/Common/src/DetID.cxx +++ b/DataFormats/Detectors/Common/src/DetID.cxx @@ -29,7 +29,7 @@ constexpr DetID::ID DetID::ITS, DetID::TPC, DetID::TRD, DetID::TOF, DetID::PHS, DetID::HMP, DetID::MFT, DetID::MCH, DetID::MID, DetID::ZDC, DetID::FT0, DetID::FV0, DetID::FDD, DetID::ACO, DetID::First, DetID::Last; #ifdef ENABLE_UPGRADES -constexpr DetID::ID DetID::IT3; +constexpr DetID::ID DetID::IT3, DetID::EC0; #endif constexpr int DetID::nDetectors; diff --git a/DataFormats/Headers/include/Headers/DataHeader.h b/DataFormats/Headers/include/Headers/DataHeader.h index 525b1f377fb91..97a0db835699c 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -634,6 +634,7 @@ constexpr o2::header::DataOrigin gDataOriginTRD{"TRD"}; constexpr o2::header::DataOrigin gDataOriginZDC{"ZDC"}; #ifdef ENABLE_UPGRADES constexpr o2::header::DataOrigin gDataOriginIT3{"IT3"}; +constexpr o2::header::DataOrigin gDataOriginEC0{"EC0"}; #endif //possible data types diff --git a/Detectors/Base/include/DetectorsBase/GeometryManager.h b/Detectors/Base/include/DetectorsBase/GeometryManager.h index 5b340f7487380..f29e795664d3d 100644 --- a/Detectors/Base/include/DetectorsBase/GeometryManager.h +++ b/Detectors/Base/include/DetectorsBase/GeometryManager.h @@ -130,7 +130,7 @@ class GeometryManager : public TObject private: /// sensitive volume identifier composed from (det_mask< +#include +#include +#include +#include "DataFormatsITSMFT/Cluster.h" + +namespace o2 +{ +namespace endcaps +{ +class ClusterTopology; +class TopologyDictionary; +class BuildTopologyDictionary; + +class ClusterPattern +{ + public: + /// Default constructor + ClusterPattern(); + /// Standard constructor + ClusterPattern(int nRow, int nCol, const unsigned char patt[o2::itsmft::Cluster::kMaxPatternBytes]); + /// Constructor from cluster patterns + template + ClusterPattern(iterator& pattIt) + { + mBitmap[0] = *pattIt++; + mBitmap[1] = *pattIt++; + int nbits = mBitmap[0] * mBitmap[1]; + int nBytes = nbits / 8; + if (((nbits) % 8) != 0) + nBytes++; + memcpy(&mBitmap[2], &(*pattIt), nBytes); + pattIt += nBytes; + } + /// Maximum number of bytes for the cluster puttern + 2 bytes respectively for the number of rows and columns of the bounding box + static constexpr int kExtendedPatternBytes = o2::itsmft::Cluster::kMaxPatternBytes + 2; + /// Returns the pattern + std::array getPattern() const { return mBitmap; } + /// Returns a specific byte of the pattern + unsigned char getByte(int n) const; + /// Returns the number of rows + int getRowSpan() const { return (int)mBitmap[0]; } + /// Returns the number of columns + int getColumnSpan() const { return (int)mBitmap[1]; } + /// Returns the number of bytes used for the pattern + int getUsedBytes() const; + /// Prints the pattern + friend std::ostream& operator<<(std::ostream& os, const ClusterPattern& top); + /// Sets the pattern + void setPattern(int nRow, int nCol, const unsigned char patt[o2::itsmft::Cluster::kMaxPatternBytes]); + /// Sets the whole bitmask: the number of rows, the number of columns and the pattern + void setPattern(const unsigned char bitmask[kExtendedPatternBytes]); + /// Static: Compute pattern's COG position. Returns the number of fired pixels + static int getCOG(int nRow, int nCol, const unsigned char patt[o2::itsmft::Cluster::kMaxPatternBytes], float& xCOG, float& zCOG); + /// Compute pattern's COG position. Returns the number of fired pixels + int getCOG(float& xCOG, float& zCOG) const; + + friend ClusterTopology; + friend TopologyDictionary; + friend BuildTopologyDictionary; + + private: + /// Pattern: + /// + /// - 1st byte: number of rows + /// - 2nd byte: number of columns + /// - remainig bytes : pixels of the cluster, where 1 is a fired pixel and 0 + /// is a non-fired pixel. The number of bytes used for the pixels depends on + /// the size of the bounding box + + std::array mBitmap; ///< Cluster pattern: 1 is a fired pixel and 0 is a non-fired pixel + + ClassDefNV(ClusterPattern, 1); +}; +} // namespace endcaps +} // namespace o2 +#endif /* ALICEO2_ENDCAPS_CLUSTERPATTERN_H */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/include/DataFormatsEndCaps/TopologyDictionary.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/include/DataFormatsEndCaps/TopologyDictionary.h new file mode 100644 index 0000000000000..415becc75c6eb --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/include/DataFormatsEndCaps/TopologyDictionary.h @@ -0,0 +1,181 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TopologyDictionary.h +/// \brief Definition of the ClusterTopology class. +/// +/// \author Luca Barioglio, University and INFN of Torino +/// +/// Short TopologyDictionary descritpion +/// +/// The entries of the dictionaries are the cluster topologies, with all the information +/// which is common to the clusters with the same topology: +/// - number of rows +/// - number of columns +/// - pixel bitmask +/// - position of the Centre Of Gravity (COG) wrt the bottom left corner pixel of the bounding box +/// - error associated to the position of the hit point +/// Rare topologies, i.e. with a frequency below a threshold defined a priori, have not their own entries +/// in the dictionaries, but are grouped together with topologies with similar dimensions. +/// For the groups of rare topollogies a dummy bitmask is used. + +#ifndef ALICEO2_ENDCAPS_TOPOLOGYDICTIONARY_H +#define ALICEO2_ENDCAPS_TOPOLOGYDICTIONARY_H +#include "DataFormatsEndCaps/ClusterPattern.h" +#include "Framework/Logger.h" +#include +#include +#include +#include +#include "MathUtils/Cartesian3D.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "TH1F.h" + +//#include "DataFormatsITSMFT/TopologyDictionary.h" +//#include "EndCapsReconstruction/BuildTopologyDictionary.h" + +namespace o2 +{ +namespace endcaps +{ + +class BuildTopologyDictionary; +class LookUp; +class TopologyFastSimulation; + +/// Structure containing the most relevant pieces of information of a topology +struct GroupStruct { + unsigned long mHash; ///< Hashcode + float mErrX; ///< Error associated to the hit point in the x direction. + float mErrZ; ///< Error associated to the hit point in the z direction. + float mErr2X; ///< Squared Error associated to the hit point in the x direction. + float mErr2Z; ///< Squared Error associated to the hit point in the z direction. + float mXCOG; ///< x position of te COG wrt the boottom left corner of the bounding box + float mZCOG; ///< z position of te COG wrt the boottom left corner of the bounding box + int mNpixels; ///< Number of fired pixels + ClusterPattern mPattern; ///< Bitmask of pixels. For groups the biggest bounding box for the group is taken, with all + ///the bits set to 1. + double mFrequency; ///< Frequency of the topology + bool mIsGroup; ///< false: common topology; true: group of rare topologies + ClassDefNV(GroupStruct, 3); +}; + +class TopologyDictionary +{ + public: + /// Default constructor + TopologyDictionary(); + /// Constructor + TopologyDictionary(std::string fileName); + + /// constexpr for the definition of the groups of rare topologies. + /// The attritbution of the group ID is stringly dependent on the following parameters: it must be a power of 2. + static constexpr int RowClassSpan = 4; ///< Row span of the classes of rare topologies + static constexpr int ColClassSpan = 4; ///< Column span of the classes of rare topologies + static constexpr int MinimumClassArea = RowClassSpan * ColClassSpan; ///< Area of the smallest class of rare topologies (used as reference) + static constexpr int MaxNumberOfClasses = o2::itsmft::Cluster::kMaxPatternBits / MinimumClassArea; ///< Maximum number of row/column classes for the groups of rare topologies + static constexpr int NumberOfRareGroups = MaxNumberOfClasses * MaxNumberOfClasses; ///< Number of entries corresponding to groups of rare topologies (those whos matrix exceed the max number of bytes are empty). + /// Prints the dictionary + friend std::ostream& operator<<(std::ostream& os, const TopologyDictionary& dictionary); + /// Prints the dictionary in a binary file + void writeBinaryFile(std::string outputFile); + /// Reads the dictionary from a binary file + int readBinaryFile(std::string fileName); + /// Returns the x position of the COG for the n_th element + inline float getXCOG(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mXCOG; + } + /// Returns the error on the x position of the COG for the n_th element + inline float getErrX(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mErrX; + } + /// Returns the z position of the COG for the n_th element + inline float getZCOG(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mZCOG; + } + /// Returns the error on the z position of the COG for the n_th element + inline float getErrZ(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mErrZ; + } + /// Returns the error^2 on the x position of the COG for the n_th element + inline float getErr2X(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mErr2X; + } + /// Returns the error^2 on the z position of the COG for the n_th element + inline float getErr2Z(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mErr2Z; + } + /// Returns the hash of the n_th element + inline unsigned long getHash(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mHash; + } + /// Returns the number of fired pixels of the n_th element + inline int getNpixels(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mNpixels; + } + /// Returns the frequency of the n_th element; + inline double getFrequency(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mFrequency; + } + /// Returns true if the element corresponds to a group of rare topologies + inline bool isGroup(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mIsGroup; + } + /// Returns the pattern of the topology + inline ClusterPattern getPattern(int n) const + { + assert(n >= 0 || n < (int)mVectorOfIDs.size()); + return mVectorOfIDs[n].mPattern; + } + /// Fills a hostogram with the distribution of the IDs + static void getTopologyDistribution(const TopologyDictionary& dict, TH1F*& histo, const char* histName); + /// Returns the number of elements in the dicionary; + int getSize() const { return (int)mVectorOfIDs.size(); } + ///Returns the local position of a compact cluster + Point3D getClusterCoordinates(const o2::itsmft::CompCluster& cl) const; + ///Returns the local position of a compact cluster + static Point3D getClusterCoordinates(const o2::itsmft::CompCluster& cl, const ClusterPattern& patt); + + friend BuildTopologyDictionary; + friend LookUp; + friend TopologyFastSimulation; + + private: + std::unordered_map mCommonMap; ///< Map of pair + std::unordered_map mGroupMap; ///< Map of pair + int mSmallTopologiesLUT[8 * 255 + 1]; ///< Look-Up Table for the topologies with 1-byte linearised matrix + std::vector mVectorOfIDs; ///< Vector of topologies and groups + + ClassDefNV(TopologyDictionary, 3); +}; +} // namespace endcaps +} // namespace o2 + +#endif // ALICEO2_ENDCAPS_TOPOLOGYDICTIONARY_H diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/ClusterPattern.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/ClusterPattern.cxx new file mode 100644 index 0000000000000..147990d052fa8 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/ClusterPattern.cxx @@ -0,0 +1,148 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ClusterTopology.cxx +/// \brief Implementation of the ClusterPattern class. +/// +/// \author Luca Barioglio, University and INFN of Torino +#include "DataFormatsITSMFT/ClusterTopology.h" +#include "DataFormatsEndCaps/ClusterPattern.h" + +#include "Framework/Logger.h" +#include + +//ClassImp(o2::endcaps::ClusterPattern); + +namespace o2 +{ +namespace endcaps +{ +ClusterPattern::ClusterPattern() : mBitmap{0} {} + +ClusterPattern::ClusterPattern(int nRow, int nCol, const unsigned char patt[o2::itsmft::Cluster::kMaxPatternBytes]) +{ + setPattern(nRow, nCol, patt); +} + +unsigned char ClusterPattern::getByte(int n) const +{ + if (n < 0 || n > o2::itsmft::Cluster::kMaxPatternBytes + 1) { + LOG(ERROR) << "Invalid element of the pattern"; + return -1; + } else { + return mBitmap[n]; + } +} + +int ClusterPattern::getUsedBytes() const +{ + int nBits = (int)mBitmap[0] * (int)mBitmap[1]; + int nBytes = nBits / 8; + if (nBits % 8 != 0) + nBytes++; + return nBytes; +} + +void ClusterPattern::setPattern(int nRow, int nCol, const unsigned char patt[o2::itsmft::Cluster::kMaxPatternBytes]) +{ + mBitmap[0] = (unsigned char)nRow; + mBitmap[1] = (unsigned char)nCol; + int nBytes = nRow * nCol / 8; + if (((nRow * nCol) % 8) != 0) + nBytes++; + memcpy(&mBitmap[2], patt, nBytes); +} + +void ClusterPattern::setPattern(const unsigned char patt[o2::itsmft::ClusterPattern::kExtendedPatternBytes]) +{ + memcpy(&mBitmap[0], patt, ClusterPattern::kExtendedPatternBytes); +} + +std::ostream& operator<<(std::ostream& os, const ClusterPattern& pattern) +{ + os << "rowSpan: " << pattern.getRowSpan() << " columnSpan: " << pattern.getColumnSpan() + << " #bytes: " << pattern.getUsedBytes() << std::endl; + unsigned char tempChar = 0; + int s = 0; + int ic = 0; + for (unsigned int i = 2; i < pattern.getUsedBytes() + 2; i++) { + tempChar = pattern.mBitmap[i]; + s = 128; // 0b10000000 + while (s > 0) { + if (ic % pattern.getColumnSpan() == 0) { + os << "|"; + } + ic++; + if ((tempChar & s) != 0) { + os << '+'; + } else { + os << ' '; + } + s /= 2; + if (ic % pattern.getColumnSpan() == 0) { + os << "|" << std::endl; + } + if (ic == (pattern.getRowSpan() * pattern.getColumnSpan())) { + break; + } + } + if (ic == (pattern.getRowSpan() * pattern.getColumnSpan())) { + break; + } + } + os << std::endl; + return os; +} + +int ClusterPattern::getCOG(int rowSpan, int colSpan, const unsigned char patt[o2::itsmft::Cluster::kMaxPatternBytes], float& xCOG, float& zCOG) +{ + int tempxCOG = 0, tempzCOG = 0, tempFiredPixels = 0, ic = 0, ir = 0; + int nBits = rowSpan * colSpan; + int nBytes = nBits / 8; + if (nBits % 8 != 0) { + nBytes++; + } + for (unsigned int i = 0; i < nBytes; i++) { + unsigned char tempChar = patt[i]; + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) { + tempFiredPixels++; + tempxCOG += ir; + tempzCOG += ic; + } + ic++; + s /= 2; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + } + if ((ir + 1) * ic == nBits) { + break; + } + } + xCOG = float(tempxCOG) / tempFiredPixels; + zCOG = float(tempzCOG) / tempFiredPixels; + + return tempFiredPixels; +} + +int ClusterPattern::getCOG(float& xCOG, float& zCOG) const +{ + auto patt = getPattern(); + return ClusterPattern::getCOG(getRowSpan(), getColumnSpan(), &patt[2], xCOG, zCOG); +} + +} // namespace endcaps +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/DataFormatsEndCapsLinkDef.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/DataFormatsEndCapsLinkDef.h new file mode 100644 index 0000000000000..10f8b6f8bc523 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/DataFormatsEndCapsLinkDef.h @@ -0,0 +1,20 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::endcaps::ClusterPattern + ; +#pragma link C++ class o2::endcaps::TopologyDictionary + ; + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/TopologyDictionary.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/TopologyDictionary.cxx new file mode 100644 index 0000000000000..150f2be822d51 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/DataFormats/src/TopologyDictionary.cxx @@ -0,0 +1,144 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TopologyDictionary.cxx +/// \brief Implementation of the TopologyDictionary class. +/// +/// \author Luca Barioglio, University and INFN of Torino + +#include "DataFormatsEndCaps/TopologyDictionary.h" +#include "DataFormatsITSMFT/ClusterTopology.h" +#include +#include "EndCapsBase/SegmentationAlpide.h" + +using std::cout; +using std::endl; +using std::string; +using std::unordered_map; +using std::vector; + +ClassImp(o2::endcaps::TopologyDictionary); + +namespace o2 +{ +namespace endcaps +{ + +TopologyDictionary::TopologyDictionary() : mSmallTopologiesLUT{-1} {} + +TopologyDictionary::TopologyDictionary(std::string fileName) +{ + readBinaryFile(fileName); +} + +std::ostream& operator<<(std::ostream& os, const TopologyDictionary& dict) +{ + for (auto& p : dict.mVectorOfIDs) { + os << "Hash: " << p.mHash << " ErrX: " << p.mErrX << " ErrZ : " << p.mErrZ << " xCOG: " << p.mXCOG << " zCOG: " << p.mZCOG << " Npixles: " << p.mNpixels << " Frequency: " << p.mFrequency << " isGroup : " << std::boolalpha << p.mIsGroup << std::endl + << p.mPattern << std::endl + << "*********************************************************" << std::endl + << std::endl; + } + return os; +} + +void TopologyDictionary::writeBinaryFile(string outputfile) +{ + std::ofstream file_output(outputfile, std::ios::out | std::ios::binary); + for (auto& p : mVectorOfIDs) { + file_output.write(reinterpret_cast(&p.mHash), sizeof(unsigned long)); + file_output.write(reinterpret_cast(&p.mErrX), sizeof(float)); + file_output.write(reinterpret_cast(&p.mErrZ), sizeof(float)); + file_output.write(reinterpret_cast(&p.mErr2X), sizeof(float)); + file_output.write(reinterpret_cast(&p.mErr2Z), sizeof(float)); + file_output.write(reinterpret_cast(&p.mXCOG), sizeof(float)); + file_output.write(reinterpret_cast(&p.mZCOG), sizeof(float)); + file_output.write(reinterpret_cast(&p.mNpixels), sizeof(int)); + file_output.write(reinterpret_cast(&p.mFrequency), sizeof(double)); + file_output.write(reinterpret_cast(&p.mIsGroup), sizeof(bool)); + file_output.write(reinterpret_cast(&p.mPattern.mBitmap), + sizeof(unsigned char) * (ClusterPattern::kExtendedPatternBytes)); + } + file_output.close(); +} + +int TopologyDictionary::readBinaryFile(string fname) +{ + mVectorOfIDs.clear(); + mCommonMap.clear(); + for (auto& p : mSmallTopologiesLUT) + p = -1; + std::ifstream in(fname.data(), std::ios::in | std::ios::binary); + GroupStruct gr; + int groupID = 0; + if (!in.is_open()) { + LOG(ERROR) << "The file " << fname << " coud not be opened"; + throw std::runtime_error("The file coud not be opened"); + } else { + while (in.read(reinterpret_cast(&gr.mHash), sizeof(unsigned long))) { + in.read(reinterpret_cast(&gr.mErrX), sizeof(float)); + in.read(reinterpret_cast(&gr.mErrZ), sizeof(float)); + in.read(reinterpret_cast(&gr.mErr2X), sizeof(float)); + in.read(reinterpret_cast(&gr.mErr2Z), sizeof(float)); + in.read(reinterpret_cast(&gr.mXCOG), sizeof(float)); + in.read(reinterpret_cast(&gr.mZCOG), sizeof(float)); + in.read(reinterpret_cast(&gr.mNpixels), sizeof(int)); + in.read(reinterpret_cast(&gr.mFrequency), sizeof(double)); + in.read(reinterpret_cast(&gr.mIsGroup), sizeof(bool)); + in.read(reinterpret_cast(&gr.mPattern.mBitmap), sizeof(unsigned char) * (ClusterPattern::kExtendedPatternBytes)); + mVectorOfIDs.push_back(gr); + if (!gr.mIsGroup) { + mCommonMap.insert(std::make_pair(gr.mHash, groupID)); + if (gr.mPattern.getUsedBytes() == 1) + mSmallTopologiesLUT[(gr.mPattern.getColumnSpan() - 1) * 255 + (int)gr.mPattern.mBitmap[2]] = groupID; + } else { + mGroupMap.insert(std::make_pair((int)(gr.mHash >> 32) & 0x00000000ffffffff, groupID)); + } + groupID++; + } + } + in.close(); + return 0; +} + +void TopologyDictionary::getTopologyDistribution(const TopologyDictionary& dict, TH1F*& histo, const char* histName) +{ + int dictSize = (int)dict.getSize(); + if (histo) + delete histo; + histo = new TH1F(histName, ";Topology ID;Frequency", dictSize, -0.5, dictSize - 0.5); + histo->SetFillColor(kRed); + histo->SetFillStyle(3005); + histo->SetDrawOption("histo"); + for (int i = 0; i < dictSize; i++) { + histo->Fill(i, dict.getFrequency(i)); + } +} + +Point3D TopologyDictionary::getClusterCoordinates(const o2::itsmft::CompCluster& cl) const +{ + Point3D locCl; + o2::endcaps::SegmentationAlpide::detectorToLocalUnchecked(cl.getRow(), cl.getCol(), locCl); + locCl.SetX(locCl.X() + this->getXCOG(cl.getPatternID())); + locCl.SetZ(locCl.Z() + this->getZCOG(cl.getPatternID())); + return locCl; +} + +Point3D TopologyDictionary::getClusterCoordinates(const o2::itsmft::CompCluster& cl, const ClusterPattern& patt) +{ + float xCOG = 0, zCOG = 0; + patt.getCOG(xCOG, zCOG); + Point3D locCl; + o2::endcaps::SegmentationAlpide::detectorToLocalUnchecked(cl.getRow() - round(xCOG) + xCOG, cl.getCol() - round(zCOG) + zCOG, locCl); + return locCl; +} + +} // namespace endcaps +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/README.md b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/README.md new file mode 100644 index 0000000000000..26107bfa6a4f7 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/README.md @@ -0,0 +1,48 @@ + + +# ITS + +## Workflow and executables + +* `o2-its-digi2raw`: creation of raw data from MC. Requires digitized ITS data (as well as an access to the GRP data). Allows creation of raw data file per layer (default) or per CRU. The configuration file for the input to the `o2-raw-file-reader-workflow` will be automatically created in the output directory. + +* `o2-its-reco-workflow`: reconstruction of ITS tracks starting from simulated digits. + +* `o2-itsmft-stf-decoder-workflow`: raw data STF decoder and clusterizer. Provides either cluster or digits or both. Supports multi-threading. + +Can be extended to reconstruction from the raw data by disabling the digits reader and piping it to the output of the STF reader: + +```bash +# To decode digits from the raw (simulated) STF and send feed to to the workflow for further clusterization and reconstruction: +o2-raw-file-reader-workflow --loop 5 --delay 3 --conf ITSraw.cfg | o2-itsmft-stf-decoder-workflow --digits --no-clusters | o2-its-reco-workflow --disable-mc --digits-from-upstream +``` + +```bash +# To decode/clusterize the STF and feed directly the clusters into the workflow: +o2-raw-file-reader-workflow --loop 5 --delay 3 --conf ITSraw.cfg | o2-itsmft-stf-decoder-workflow | o2-its-reco-workflow --disable-mc --clusters-from-upstream +``` + +```bash +#If needed, one can request both digits and clusters from the STF decoder: +o2-raw-file-reader-workflow --loop 5 --delay 3 --conf ITSraw.cfg | o2-itsmft-stf-decoder-workflow --digits | o2-its-reco-workflow --disable-mc --digits-from-upstream --clusters-from-upstream +``` + +## Tracking + +```bash +# main tracking workflow, by default run CookedSeed Tracker, with --trackerCA uses CA-tracker +o2-its-reco-workflow +``` + +For the synchronous mode one can apply selection on the ROF multiplicity either in therms of signal clusters and/or number of contributing tracklets in seeding vertices +`FastMultEst` implements the fast estimator of signal clusters multiplicity either via free noise + signal fit or via signal fit with noise level imposed (via `FastMultEstConfig.imposedNoisePerChip`). +Multiplicity is provided as Ncl. per layer modulo acceptance correction from the FastMultEstConfig. +The latter provides the settings for the estimator as well as the low/high cuts on the estimate mult. and (if these cuts are passed) eventual cuts on the vertices multiplicity used to seed the ITS tracks. +For example, the command: +```cpp +o2-its-reco-workflow --configKeyValues "fastMultConfig.cutMultClusLow=50;fastMultConfig.cutMultClusHigh=4000;fastMultConfig.cutMultVtxHigh=1000" +``` +will track only ROFs with N signal clusters between 50 and 4000 and will consider seeding vertices with multiplicity below 1000 tracklets. + diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/CMakeLists.txt new file mode 100644 index 0000000000000..587465773e780 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +o2_add_library(ECLayersBase + SOURCES src/GeometryTGeo.cxx src/ContainerFactory.cxx + src/MisalignmentParameter.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::EndCapsBase) + +o2_target_root_dictionary(ECLayersBase + HEADERS include/ECLayersBase/GeometryTGeo.h + include/ECLayersBase/ContainerFactory.h + include/ECLayersBase/MisalignmentParameter.h) diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/ContainerFactory.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/ContainerFactory.h new file mode 100644 index 0000000000000..12812487abc20 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/ContainerFactory.h @@ -0,0 +1,53 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ContainerFactory.h +/// \brief Definition of the ContainerFactory class + +#ifndef ALICEO2_ENDCAPSLAYERS_CONTAINERFACTORY_H_ +#define ALICEO2_ENDCAPSLAYERS_CONTAINERFACTORY_H_ + +#include "FairContFact.h" // for FairContFact, FairContainer (ptr only) +#include "Rtypes.h" // for ContainerFactory::Class, ClassDef, etc + +class FairParSet; + +class FairContainer; + +namespace o2 +{ +namespace ecl +{ +class ContainerFactory : public FairContFact +{ + private: + /// Creates the Container objects with all accepted + /// contexts and adds them to + /// the list of containers for the O2its library. + void mSetAllContainers(); + + public: + /// Default constructor + ContainerFactory(); + + /// Default destructor + ~ContainerFactory() override = default; + /// Calls the constructor of the corresponding parameter container. + /// For an actual context, which is not an empty string and not + /// the default context + /// of this container, the name is concatinated with the context. + FairParSet* createContainer(FairContainer*) override; + + ClassDefOverride(ContainerFactory, 0); // Factory for all AliceO2 ITS parameter containers +}; +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/GeometryTGeo.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/GeometryTGeo.h new file mode 100644 index 0000000000000..b2e7ad8f42526 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/GeometryTGeo.h @@ -0,0 +1,355 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 GeometryTGeo.h +/// \brief Definition of the GeometryTGeo class +/// \author cvetan.cheshkov@cern.ch - 15/02/2007 +/// \author ruben.shahoyan@cern.ch - adapted to ITSupg 18/07/2012 +/// \author rafael.pezzi@cern.ch - adapted to PostLS4EndCaps 25/06/2020 + +#ifndef ALICEO2_ENDCAPSLAYERS_GEOMETRYTGEO_H_ +#define ALICEO2_ENDCAPSLAYERS_GEOMETRYTGEO_H_ + +#include // for TGeoHMatrix +#include // for TObject +#include +#include +#include +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "EndCapsBase/GeometryTGeo.h" +#include "MathUtils/Utils.h" +#include "Rtypes.h" // for Int_t, Double_t, Bool_t, UInt_t, etc + +class TGeoPNEntry; + +namespace o2 +{ +namespace ecl +{ +/// GeometryTGeo is a simple interface class to TGeoManager. It is used in the simulation +/// and reconstruction in order to query the TGeo EC0 geometry. +/// RS: In order to preserve the static character of the class but make it dynamically access +/// geometry, we need to check in every method if the structures are initialized. To be converted +/// to singleton at later stage. + +class GeometryTGeo : public o2::endcaps::GeometryTGeo +{ + public: + typedef o2::Transform3D Mat3D; + using DetMatrixCache::getMatrixL2G; + using DetMatrixCache::getMatrixT2GRot; + using DetMatrixCache::getMatrixT2L; + // this method is not advised for ITS: for barrel detectors whose tracking frame is just a rotation + // it is cheaper to use T2GRot + using DetMatrixCache::getMatrixT2G; + + static GeometryTGeo* Instance() + { + // get (create if needed) a unique instance of the object + if (!sInstance) + sInstance = std::unique_ptr(new GeometryTGeo(true, 0)); + return sInstance.get(); + } + + // adopt the unique instance from external raw pointer (to be used only to read saved instance from file) + static void adopt(GeometryTGeo* raw); + + // constructor + // ATTENTION: this class is supposed to behave as a singleton, but to make it root-persistent + // we must define public default constructor. + // NEVER use it, it will throw exception if the class instance was already created + // Use GeometryTGeo::Instance() instead + GeometryTGeo(bool build = kFALSE, int loadTrans = 0 + /*o2::base::utils::bit2Mask(o2::TransformType::T2L, // default transformations to load + o2::TransformType::T2G, + o2::TransformType::L2G)*/ + ); + + /// Default destructor + ~GeometryTGeo() override = default; + + GeometryTGeo(const GeometryTGeo& src) = delete; + GeometryTGeo& operator=(const GeometryTGeo& geom) = delete; + + // implement filling of the matrix cache + using o2::endcaps::GeometryTGeo::fillMatrixCache; + void fillMatrixCache(int mask) override; + + // cache parameters of sensors tracking frames + void fillTrackingFramesCache(); + + /// Exract EC0 parameters from TGeo + void Build(int loadTrans = 0) override; + + int getNumberOfChipRowsPerModule(int lay) const { return mNumberOfChipRowsPerModule[lay]; } + int getNumberOfChipColsPerModule(int lay) const + { + return mNumberOfChipRowsPerModule[lay] ? mNumberOfChipsPerModule[lay] / mNumberOfChipRowsPerModule[lay] : -1; + } + + int getNumberOfChipsPerModule(int lay) const { return mNumberOfChipsPerModule[lay]; } + int getNumberOfChipsPerHalfStave(int lay) const { return mNumberOfChipsPerHalfStave[lay]; } + int getNumberOfChipsPerStave(int lay) const { return mNumberOfChipsPerStave[lay]; } + int getNumberOfChipsPerLayer(int lay) const { return mNumberOfChipsPerLayer[lay]; } + int getNumberOfModules(int lay) const { return mNumberOfModules[lay]; } + int getNumberOfHalfStaves(int lay) const { return mNumberOfHalfStaves[lay]; } + int getNumberOfStaves(int lay) const { return mNumberOfStaves[lay]; } + int getNumberOfLayers() const { return mNumberOfLayers; } + int getChipIndex(int lay, int detInLay) const { return getFirstChipIndex(lay) + detInLay; } + /// This routine computes the chip index number from the layer, stave, and chip number in stave + /// \param int lay The layer number. Starting from 0. + /// \param int sta The stave number. Starting from 0 + /// \param int chipInStave The chip number in the stave. Starting from 0 + int getChipIndex(int lay, int sta, int detInSta) const; + + /// This routine computes the chip index number from the layer, stave, substave and chip number + /// in substave + /// \param int lay The layer number. Starting from 0. + /// \param int sta The stave number. Starting from 0 + /// \param int substa The substave number. Starting from 0 + /// \param int chipInSStave The chip number in the sub stave. Starting from 0 + int getChipIndex(int lay, int sta, int subSta, int detInSubSta) const; + + /// This routine computes the chip index number from the layer,stave, substave module and + /// chip number in module. + /// \param int lay The layer number. Starting from 0. + /// \param int sta The stave number. Starting from 0 + /// \param int substa The substave number. Starting from 0 + /// \param int module The module number ... + /// \param int chipInSStave The chip number in the module. Starting from 0 + int getChipIndex(int lay, int sta, int subSta, int md, int detInMod) const; + + /// This routine computes the layer, stave, substave, module and chip number + /// given the chip index number + /// \param int index The chip index number, starting from zero. + /// \param int lay The layer number. Starting from 0 + /// \param int sta The stave number. Starting from 0 + /// \param int ssta The halfstave number. Starting from 0 + /// \param int mod The module number. Starting from 0 + /// \param int chip The detector number. Starting from 0 + bool getChipId(int index, int& lay, int& sta, int& ssta, int& mod, int& chip) const; + + /// Get chip layer, from 0 + int getLayer(int index) const; + + /// Get chip stave, from 0 + int getStave(int index) const; + + /// Get chip substave id in stave, from 0 + int getHalfStave(int index) const; + + /// Get chip module id in substave, from 0 + int getModule(int index) const; + + /// Get chip number within layer, from 0 + int getChipIdInLayer(int index) const; + + /// Get chip number within stave, from 0 + int getChipIdInStave(int index) const; + + /// Get chip number within stave, from 0 + int getChipIdInHalfStave(int index) const; + + /// Get chip number within module, from 0 + int getChipIdInModule(int index) const; + + int getLastChipIndex(int lay) const { return mLastChipIndex[lay]; } + int getFirstChipIndex(int lay) const { return (lay == 0) ? 0 : mLastChipIndex[lay - 1] + 1; } + const char* getSymbolicName(int index) const + { + /// return symbolic name of sensor + return o2::base::GeometryManager::getSymbolicName(getDetID(), index); + } + + const char* getSymbolicName(int lay, int sta, int det) const + { + /// return symbolic name of sensor + return getSymbolicName(getChipIndex(lay, sta, det)); + } + + /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by quering the TGeoManager + TGeoHMatrix* getMatrix(int index) const { return o2::base::GeometryManager::getMatrix(getDetID(), index); } + TGeoHMatrix* getMatrix(int lay, int sta, int sens) const { return getMatrix(getChipIndex(lay, sta, sens)); } + bool getOriginalMatrix(int index, TGeoHMatrix& m) const + { + /// Get the original (ideal geometry) TGeo matrix for a given chip identified by 'index' + /// The method is slow, so it should be used with great care (for caching only) + return o2::base::GeometryManager::getOriginalMatrix(getDetID(), index, m); + } + + bool getOriginalMatrix(int lay, int sta, int det, TGeoHMatrix& m) const + { + /// Get the original (ideal geometry) TGeo matrix for a given chip identified by 'index' + /// The method is slow, so it should be used with great care (for caching only) + return getOriginalMatrix(getChipIndex(lay, sta, det), m); + } + + const Mat3D& getMatrixT2L(int lay, int sta, int det) const { return getMatrixT2L(getChipIndex(lay, sta, det)); } + const Mat3D& getMatrixSensor(int index) const { return getMatrixL2G(index); } + const Mat3D& getMatrixSensor(int lay, int sta, int det) + { + // get positioning matrix of the sensor, alias to getMatrixL2G + return getMatrixSensor(getChipIndex(lay, sta, det)); + } + + const Rot2D& getMatrixT2GRot(int lay, int sta, int sens) + { + /// get matrix for tracking to global frame transformation + return getMatrixT2GRot(getChipIndex(lay, sta, sens)); + } + + bool isTrackingFrameCached() const { return !mCacheRefX.empty(); } + void getSensorXAlphaRefPlane(int index, float& x, float& alpha) const + { + x = getSensorRefX(index); + alpha = getSensorRefAlpha(index); + } + + float getSensorRefX(int isn) const { return mCacheRefX[isn]; } + float getSensorRefAlpha(int isn) const { return mCacheRefAlpha[isn]; } + // Attention: these are transformations wrt sensitive volume! + void localToGlobal(int index, const double* loc, double* glob); + + void localToGlobal(int lay, int sta, int det, const double* loc, double* glob); + + void globalToLocal(int index, const double* glob, double* loc); + + void globalToLocal(int lay, int sta, int det, const double* glob, double* loc); + + void localToGlobalVector(int index, const double* loc, double* glob); + + void globalToLocalVector(int index, const double* glob, double* loc); + + void Print(Option_t* opt = "") const; + + static const char* getEC0VolPattern() { return sVolumeName.c_str(); } + static const char* getEC0LayerPattern() { return sLayerName.c_str(); } + static const char* getEC0WrapVolPattern() { return sWrapperVolumeName.c_str(); } + static const char* getEC0StavePattern() { return sStaveName.c_str(); } + static const char* getEC0HalfStavePattern() { return sHalfStaveName.c_str(); } + static const char* getEC0ModulePattern() { return sModuleName.c_str(); } + static const char* getEC0ChipPattern() { return sChipName.c_str(); } + static const char* getEC0SensorPattern() { return sSensorName.c_str(); } + static void setEC0VolPattern(const char* nm) { sVolumeName = nm; } + static void setEC0LayerPattern(const char* nm) { sLayerName = nm; } + static void setEC0WrapVolPattern(const char* nm) { sWrapperVolumeName = nm; } + static void setEC0StavePattern(const char* nm) { sStaveName = nm; } + static void setEC0HalfStavePattern(const char* nm) { sHalfStaveName = nm; } + static void setEC0ModulePattern(const char* nm) { sModuleName = nm; } + static void setEC0ChipPattern(const char* nm) { sChipName = nm; } + static void setEC0SensorPattern(const char* nm) { sSensorName = nm; } + /// sym name of the layer + static const char* composeSymNameEC0() { return o2::detectors::DetID(o2::detectors::DetID::EC0).getName(); } + /// sym name of the layer + static const char* composeSymNameLayer(int lr); + + /// Sym name of the stave at given layer + static const char* composeSymNameStave(int lr, int sta); + + /// Sym name of the stave at given layer + static const char* composeSymNameHalfStave(int lr, int sta, int ssta); + + /// Sym name of the substave at given layer/stave + static const char* composeSymNameModule(int lr, int sta, int ssta, int mod); + + /// Sym name of the chip in the given layer/stave/substave/module + static const char* composeSymNameChip(int lr, int sta, int ssta, int mod, int chip); + + protected: + /// Get the transformation matrix of the SENSOR (not necessary the same as the chip) + /// for a given chip 'index' by quering the TGeoManager + TGeoHMatrix* extractMatrixSensor(int index) const; + + // create matrix for transformation from sensor local frame to global one + TGeoHMatrix& createT2LMatrix(int isn); + + // get sensor tracking frame alpha and + void extractSensorXAlpha(int isn, float& x, float& alp); + + /// This routine computes the layer number a given the chip index + /// \param int index The chip index number, starting from zero. + /// \param int indexInLr The chip index inside a layer, starting from zero. + /// \param int lay The layer number. Starting from 0. + bool getLayer(int index, int& lay, int& index2) const; + + /// Determines the number of chips per module on the (sub)stave in the Geometry + /// Also extract the layout: span of module centers in Z and X + /// \param lay: layer number from 0 + int extractNumberOfChipsPerModule(int lay, int& nrow) const; + + /// Determines the number of layers in the Geometry + /// \param lay: layer number, starting from 0 + int extractNumberOfStaves(int lay) const; + + /// Determines the number of substaves in the stave of the layer + /// \param lay: layer number, starting from 0 + int extractNumberOfHalfStaves(int lay) const; + + /// Determines the number of modules in substave in the stave of the layer + /// \param lay: layer number, starting from 0 + /// For the setup w/o modules defined the module and the stave or the substave is the same thing + /// Legacy method, keep it just in case... + int extractNumberOfModules(int lay) const; + + /// Determines the layer detector type the Geometry and + /// returns the detector type id for the layer + /// \param lay: layer number from 0 + int extractLayerChipType(int lay) const; + + /// Determines the number of layers in the Geometry + int extractNumberOfLayers(); + + /// Extract number following the prefix in the name string + int extractVolumeCopy(const char* name, const char* prefix) const; + + TGeoPNEntry* getPNEntry(int index) const + { + /// Get a pointer to the TGeoPNEntry of a chip identified by 'index' + /// Returns NULL in case of invalid index, missing TGeoManager or invalid symbolic name + return o2::base::GeometryManager::getPNEntry(getDetID(), index); + } + + protected: + static constexpr int MAXLAYERS = 15; ///< max number of active layers + + Int_t mNumberOfLayers; ///< number of layers + std::vector mNumberOfStaves; ///< number of staves/layer(layer) + std::vector mNumberOfHalfStaves; ///< the number of substaves/stave(layer) + std::vector mNumberOfModules; ///< number of modules/substave(layer) + std::vector mNumberOfChipsPerModule; ///< number of chips per module (group of chips on substaves) + std::vector mNumberOfChipRowsPerModule; ///< number of chips rows per module (relevant for OB modules) + std::vector mNumberOfChipsPerHalfStave; ///< number of chips per substave + std::vector mNumberOfChipsPerStave; ///< number of chips per stave + std::vector mNumberOfChipsPerLayer; ///< number of chips per stave + std::vector mLastChipIndex; ///< max ID of the detctor in the layer + std::array mLayerToWrapper; ///< Layer to wrapper correspondence + + std::vector mCacheRefX; ///< sensors tracking plane reference X + std::vector mCacheRefAlpha; ///< sensors tracking plane reference alpha + + static std::string sVolumeName; ///< Mother volume name + static std::string sLayerName; ///< Layer name + static std::string sStaveName; ///< Stave name + static std::string sHalfStaveName; ///< HalfStave name + static std::string sModuleName; ///< Module name + static std::string sChipName; ///< Chip name + static std::string sSensorName; ///< Sensor name + static std::string sWrapperVolumeName; ///< Wrapper volume name + + private: + static std::unique_ptr sInstance; ///< singletone instance + + ClassDefOverride(GeometryTGeo, 1); // EC0 geometry based on TGeo +}; +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/MisalignmentParameter.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/MisalignmentParameter.h new file mode 100644 index 0000000000000..32a9da5039783 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/include/ECLayersBase/MisalignmentParameter.h @@ -0,0 +1,70 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 MisalignmentParameter.h +/// \brief Definition of the MisalignmentParameter class + +#ifndef ALICEO2_ENDCAPSLAYERS_MISALIGNMENTPARAMETER_H_ +#define ALICEO2_ENDCAPSLAYERS_MISALIGNMENTPARAMETER_H_ + +#include "FairParGenericSet.h" // for FairParGenericSet + +#include "Rtypes.h" // for ClassDef + +#include "TArrayD.h" // for TArrayD + +class FairParamList; + +namespace o2 +{ +namespace ecl +{ +class MisalignmentParameter : public FairParGenericSet +{ + public: + MisalignmentParameter(const char* name = "MisalignmentParameter", + const char* title = "Misalignment parameter for AliceO2ECLHitProducerIdealMisallign Parameters", + const char* context = "TestDefaultContext"); + + ~MisalignmentParameter() override; + + void Clear(); + + void putParams(FairParamList*) override; + + Bool_t getParams(FairParamList*) override; + + TArrayD getShiftX() { return mShiftX; } + TArrayD getShiftY() { return mShiftY; } + TArrayD getShiftZ() { return mShiftZ; } + TArrayD getRotX() { return mRotX; } + TArrayD getRotY() { return mRotY; } + TArrayD getRotZ() { return mRotZ; } + Int_t getNumberOfDetectors() { return mNumberOfDetectors; } + + private: + TArrayD mShiftX; ///< Array to hold the misalignment in x-direction + TArrayD mShiftY; ///< Array to hold the misalignment in y-direction + TArrayD mShiftZ; ///< Array to hold the misalignment in z-direction + TArrayD mRotX; ///< Array to hold the rotation in x-direction + TArrayD mRotY; ///< Array to hold the rotation in y-direction + TArrayD mRotZ; ///< Array to hold the rotation in z-direction + Int_t mNumberOfDetectors; ///< Total number of detectors + + MisalignmentParameter(const MisalignmentParameter&); + + MisalignmentParameter& operator=(const MisalignmentParameter&); + + ClassDefOverride(MisalignmentParameter, 1); +}; +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/ContainerFactory.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/ContainerFactory.cxx new file mode 100644 index 0000000000000..2ccfe02097a81 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/ContainerFactory.cxx @@ -0,0 +1,54 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ContainerFactory.cxx +/// \brief Implementation of the ContainerFactory class + +#include "ECLayersBase/ContainerFactory.h" +#include "FairRuntimeDb.h" // for FairRuntimeDb +#include "TString.h" // for TString + +class FairParSet; + +using namespace o2::ecl; + +ClassImp(o2::ecl::ContainerFactory); + +static ContainerFactory gO2eclContFact; + +ContainerFactory::ContainerFactory() : FairContFact() +{ + fName = "ContainerFactory"; + fTitle = "Factory for parameter containers in libO2ecl"; + mSetAllContainers(); + FairRuntimeDb::instance()->addContFactory(this); +} + +void ContainerFactory::mSetAllContainers() +{ + // FairContainer* p= new FairContainer("O2eclGeoPar", + // "O2ecl Geometry Parameters", + // "TestDefaultContext"); + // p->addContext("TestNonDefaultContext"); + // + // containers->Add(p); +} + +FairParSet* ContainerFactory::createContainer(FairContainer* c) +{ + // const char* name=c->GetName(); + // FairParSet* p=NULL; + // if (strcmp(name,"O2eclGeoPar")==0) { + // p=new O2eclGeoPar(c->getConcatName().Data(), + // c->GetTitle(),c->getContext()); + //} + // return p; + return nullptr; +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/ECLayersBaseLinkDef.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/ECLayersBaseLinkDef.h new file mode 100644 index 0000000000000..42f27ea6f4d07 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/ECLayersBaseLinkDef.h @@ -0,0 +1,21 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::ecl::GeometryTGeo; +#pragma link C++ class o2::ecl::ContainerFactory; +#pragma link C++ class o2::ecl::MisalignmentParameter + ; + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/GeometryTGeo.cxx new file mode 100644 index 0000000000000..f3b0a92aeadd9 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/GeometryTGeo.cxx @@ -0,0 +1,743 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 GeometryTGeo.cxx +/// \brief Implementation of the GeometryTGeo class +/// \author cvetan.cheshkov@cern.ch - 15/02/2007 +/// \author ruben.shahoyan@cern.ch - adapted to ITSupg 18/07/2012 +/// \author rafael.pezzi@cern.ch - adapted to PostLS4EndCaps 25/06/2020 + +// ATTENTION: In opposite to old AliITSgeomTGeo, all indices start from 0, not from 1!!! + +#include "ECLayersBase/GeometryTGeo.h" +#include "DetectorsBase/GeometryManager.h" +#include "EndCapsBase/SegmentationAlpide.h" +#include "MathUtils/Cartesian3D.h" + +#include "FairLogger.h" // for LOG + +#include // for TGeoBBox +#include // for gGeoManager, TGeoManager +#include // for TGeoPNEntry, TGeoPhysicalNode +#include // for TGeoShape +#include // for Nint, ATan2, RadToDeg +#include // for TString, Form +#include "TClass.h" // for TClass +#include "TGeoMatrix.h" // for TGeoHMatrix +#include "TGeoNode.h" // for TGeoNode, TGeoNodeMatrix +#include "TGeoVolume.h" // for TGeoVolume +#include "TMathBase.h" // for Max +#include "TObjArray.h" // for TObjArray +#include "TObject.h" // for TObject + +#include // for isdigit +#include // for snprintf, NULL, printf +#include // for strstr, strlen + +using namespace TMath; +using namespace o2::ecl; +using namespace o2::detectors; +using namespace o2::utils; + +using Segmentation = o2::endcaps::SegmentationAlpide; + +ClassImp(o2::ecl::GeometryTGeo); + +std::unique_ptr GeometryTGeo::sInstance; + +std::string GeometryTGeo::sVolumeName = "EC0V"; ///< Mother volume name +std::string GeometryTGeo::sLayerName = "EC0ULayer"; ///< Layer name +std::string GeometryTGeo::sStaveName = "EC0UStave"; ///< Stave name +std::string GeometryTGeo::sHalfStaveName = "EC0UHalfStave"; ///< HalfStave name +std::string GeometryTGeo::sModuleName = "EC0UModule"; ///< Module name +std::string GeometryTGeo::sChipName = "EC0UChip"; ///< Chip name +std::string GeometryTGeo::sSensorName = "EC0USensor"; ///< Sensor name +std::string GeometryTGeo::sWrapperVolumeName = "EC0UWrapVol"; ///< Wrapper volume name + +//__________________________________________________________________________ +GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : o2::endcaps::GeometryTGeo(DetID::EC0) +{ + // default c-tor, if build is true, the structures will be filled and the transform matrices + // will be cached + if (sInstance) { + LOG(FATAL) << "Invalid use of public constructor: o2::ecl::GeometryTGeo instance exists"; + // throw std::runtime_error("Invalid use of public constructor: o2::ecl::GeometryTGeo instance exists"); + } + + for (int i = MAXLAYERS; i--;) { + mLayerToWrapper[i] = -1; + } + if (build) { + Build(loadTrans); + } +} + +//__________________________________________________________________________ +void GeometryTGeo::adopt(GeometryTGeo* raw) +{ + // adopt the unique instance from external raw pointer (to be used only to read saved instance from file) + if (sInstance) { + LOG(FATAL) << "No adoption: o2::ecl::GeometryTGeo instance exists"; + } + sInstance = std::unique_ptr(raw); +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIndex(int lay, int sta, int chipInStave) const +{ + return getFirstChipIndex(lay) + mNumberOfChipsPerStave[lay] * sta + chipInStave; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIndex(int lay, int sta, int substa, int chipInSStave) const +{ + int n = getFirstChipIndex(lay) + mNumberOfChipsPerStave[lay] * sta + chipInSStave; + if (mNumberOfHalfStaves[lay] && substa > 0) { + n += mNumberOfChipsPerHalfStave[lay] * substa; + } + return n; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIndex(int lay, int sta, int substa, int md, int chipInMod) const +{ + int n = getFirstChipIndex(lay) + mNumberOfChipsPerStave[lay] * sta + chipInMod; + if (mNumberOfHalfStaves[lay] && substa > 0) { + n += mNumberOfChipsPerHalfStave[lay] * substa; + } + if (mNumberOfModules[lay] && md > 0) { + n += mNumberOfChipsPerModule[lay] * md; + } + return n; +} + +//__________________________________________________________________________ +bool GeometryTGeo::getLayer(int index, int& lay, int& indexInLr) const +{ + lay = getLayer(index); + indexInLr = index - getFirstChipIndex(lay); + return kTRUE; +} + +//__________________________________________________________________________ +int GeometryTGeo::getLayer(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + return lay; +} + +//__________________________________________________________________________ +int GeometryTGeo::getStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index / mNumberOfChipsPerStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getHalfStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + if (mNumberOfHalfStaves[lay] < 0) { + return -1; + } + index -= getFirstChipIndex(lay); + index %= mNumberOfChipsPerStave[lay]; + return index / mNumberOfChipsPerHalfStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getModule(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + if (mNumberOfModules[lay] < 0) { + return 0; + } + index -= getFirstChipIndex(lay); + index %= mNumberOfChipsPerStave[lay]; + if (mNumberOfHalfStaves[lay]) { + index %= mNumberOfChipsPerHalfStave[lay]; + } + return index / mNumberOfChipsPerModule[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInLayer(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index % mNumberOfChipsPerStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInHalfStave(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index % mNumberOfChipsPerHalfStave[lay]; +} + +//__________________________________________________________________________ +int GeometryTGeo::getChipIdInModule(int index) const +{ + int lay = 0; + while (index > mLastChipIndex[lay]) { + lay++; + } + index -= getFirstChipIndex(lay); + return index % mNumberOfChipsPerModule[lay]; +} + +//__________________________________________________________________________ +bool GeometryTGeo::getChipId(int index, int& lay, int& sta, int& hsta, int& mod, int& chip) const +{ + lay = getLayer(index); + index -= getFirstChipIndex(lay); + sta = index / mNumberOfChipsPerStave[lay]; + index %= mNumberOfChipsPerStave[lay]; + hsta = mNumberOfHalfStaves[lay] > 0 ? index / mNumberOfChipsPerHalfStave[lay] : -1; + index %= mNumberOfChipsPerHalfStave[lay]; + mod = mNumberOfModules[lay] > 0 ? index / mNumberOfChipsPerModule[lay] : -1; + chip = index % mNumberOfChipsPerModule[lay]; + + return kTRUE; +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameLayer(int lr) +{ + return Form("%s/%s%d", composeSymNameEC0(), getEC0LayerPattern(), lr); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameStave(int lr, int stave) +{ + return Form("%s/%s%d", composeSymNameLayer(lr), getEC0StavePattern(), stave); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameHalfStave(int lr, int stave, int substave) +{ + return substave >= 0 ? Form("%s/%s%d", composeSymNameStave(lr, stave), getEC0HalfStavePattern(), substave) + : composeSymNameStave(lr, stave); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameModule(int lr, int stave, int substave, int mod) +{ + return mod >= 0 ? Form("%s/%s%d", composeSymNameHalfStave(lr, stave, substave), getEC0ModulePattern(), mod) + : composeSymNameHalfStave(lr, stave, substave); +} + +//__________________________________________________________________________ +const char* GeometryTGeo::composeSymNameChip(int lr, int sta, int substave, int mod, int chip) +{ + return Form("%s/%s%d", composeSymNameModule(lr, sta, substave, mod), getEC0ChipPattern(), chip); +} + +//__________________________________________________________________________ +TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const +{ + // extract matrix transforming from the PHYSICAL sensor frame to global one + // Note, the if the effective sensitive layer thickness is smaller than the + // total physical sensor tickness, this matrix is biased and connot be used + // directly for transformation from sensor frame to global one. + // + // Therefore we need to add a shift + + int lay, stav, sstav, mod, chipInMod; + getChipId(index, lay, stav, sstav, mod, chipInMod); + + int wrID = mLayerToWrapper[lay]; + + TString path = Form("/cave_1/barrel_1/%s_2/", GeometryTGeo::getEC0VolPattern()); + + if (wrID >= 0) { + path += Form("%s%d_1/", getEC0WrapVolPattern(), wrID); + } + + path += + Form("%s%d_1/%s%d_%d/", GeometryTGeo::getEC0LayerPattern(), lay, GeometryTGeo::getEC0StavePattern(), lay, stav); + + if (mNumberOfHalfStaves[lay] > 0) { + path += Form("%s%d_%d/", GeometryTGeo::getEC0HalfStavePattern(), lay, sstav); + } + if (mNumberOfModules[lay] > 0) { + path += Form("%s%d_%d/", GeometryTGeo::getEC0ModulePattern(), lay, mod); + } + path += + Form("%s%d_%d/%s%d_1", GeometryTGeo::getEC0ChipPattern(), lay, chipInMod, GeometryTGeo::getEC0SensorPattern(), lay); + + static TGeoHMatrix matTmp; + gGeoManager->PushPath(); + + if (!gGeoManager->cd(path.Data())) { + gGeoManager->PopPath(); + LOG(ERROR) << "Error in cd-ing to " << path.Data(); + return nullptr; + } // end if !gGeoManager + + matTmp = *gGeoManager->GetCurrentMatrix(); // matrix may change after cd + // RSS + // printf("%d/%d/%d %s\n",lay,stav,detInSta,path.Data()); + // mat->Print(); + // Restore the modeler state. + gGeoManager->PopPath(); + + // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor ticknesses + static TGeoTranslation tra(0., 0.5 * (Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff), 0.); + + matTmp *= tra; + + return &matTmp; +} + +//__________________________________________________________________________ +void GeometryTGeo::Build(int loadTrans) +{ + if (isBuilt()) { + LOG(WARNING) << "Already built"; + return; // already initialized + } + + if (!gGeoManager) { + // RSTODO: in future there will be a method to load matrices from the CDB + LOG(FATAL) << "Geometry is not loaded"; + } + + mNumberOfLayers = extractNumberOfLayers(); + if (!mNumberOfLayers) { + return; + } + + mNumberOfStaves.resize(mNumberOfLayers); + mNumberOfHalfStaves.resize(mNumberOfLayers); + mNumberOfModules.resize(mNumberOfLayers); + mNumberOfChipsPerModule.resize(mNumberOfLayers); + mNumberOfChipRowsPerModule.resize(mNumberOfLayers); + mNumberOfChipsPerHalfStave.resize(mNumberOfLayers); + mNumberOfChipsPerStave.resize(mNumberOfLayers); + mNumberOfChipsPerLayer.resize(mNumberOfLayers); + mLastChipIndex.resize(mNumberOfLayers); + int numberOfChips = 0; + + for (int i = 0; i < mNumberOfLayers; i++) { + mNumberOfStaves[i] = extractNumberOfStaves(i); + mNumberOfHalfStaves[i] = extractNumberOfHalfStaves(i); + mNumberOfModules[i] = extractNumberOfModules(i); + mNumberOfChipsPerModule[i] = extractNumberOfChipsPerModule(i, mNumberOfChipRowsPerModule[i]); + mNumberOfChipsPerHalfStave[i] = mNumberOfChipsPerModule[i] * Max(1, mNumberOfModules[i]); + mNumberOfChipsPerStave[i] = mNumberOfChipsPerHalfStave[i] * Max(1, mNumberOfHalfStaves[i]); + mNumberOfChipsPerLayer[i] = mNumberOfChipsPerStave[i] * mNumberOfStaves[i]; + numberOfChips += mNumberOfChipsPerLayer[i]; + mLastChipIndex[i] = numberOfChips - 1; + } + setSize(numberOfChips); + fillTrackingFramesCache(); + // + fillMatrixCache(loadTrans); +} + +//__________________________________________________________________________ +void GeometryTGeo::fillMatrixCache(int mask) +{ + // populate matrix cache for requested transformations + // + if (mSize < 1) { + LOG(WARNING) << "The method Build was not called yet"; + Build(mask); + return; + } + + // build matrices + if ((mask & o2::utils::bit2Mask(o2::TransformType::L2G)) && !getCacheL2G().isFilled()) { + // Matrices for Local (Sensor!!! rather than the full chip) to Global frame transformation + LOG(INFO) << "Loading EC0 L2G matrices from TGeo"; + auto& cacheL2G = getCacheL2G(); + cacheL2G.setSize(mSize); + + for (int i = 0; i < mSize; i++) { + TGeoHMatrix* hm = extractMatrixSensor(i); + cacheL2G.setMatrix(Mat3D(*hm), i); + } + } + + if ((mask & o2::utils::bit2Mask(o2::TransformType::T2L)) && !getCacheT2L().isFilled()) { + // matrices for Tracking to Local (Sensor!!! rather than the full chip) frame transformation + LOG(INFO) << "Loading EC0 T2L matrices from TGeo"; + auto& cacheT2L = getCacheT2L(); + cacheT2L.setSize(mSize); + for (int i = 0; i < mSize; i++) { + TGeoHMatrix& hm = createT2LMatrix(i); + cacheT2L.setMatrix(Mat3D(hm), i); + } + } + + if ((mask & o2::utils::bit2Mask(o2::TransformType::T2G)) && !getCacheT2G().isFilled()) { + LOG(WARNING) << "It is faster to use 2D rotation for T2G instead of full Transform3D matrices"; + // matrices for Tracking to Global frame transformation + LOG(INFO) << "Loading EC0 T2G matrices from TGeo"; + auto& cacheT2G = getCacheT2G(); + cacheT2G.setSize(mSize); + + for (int i = 0; i < mSize; i++) { + TGeoHMatrix& mat = createT2LMatrix(i); + mat.MultiplyLeft(extractMatrixSensor(i)); + cacheT2G.setMatrix(Mat3D(mat), i); + } + } + + if ((mask & o2::utils::bit2Mask(o2::TransformType::T2GRot)) && !getCacheT2GRot().isFilled()) { + // 2D rotation matrices for Tracking frame to Global rotations + LOG(INFO) << "Loading EC0 T2G rotation 2D matrices"; + auto& cacheT2Gr = getCacheT2GRot(); + cacheT2Gr.setSize(mSize); + for (int i = 0; i < mSize; i++) { + cacheT2Gr.setMatrix(Rot2D(getSensorRefAlpha(i)), i); + } + } +} + +//__________________________________________________________________________ +void GeometryTGeo::fillTrackingFramesCache() +{ + // fill for every sensor its tracking frame parameteres + if (!isTrackingFrameCached()) { + // special cache for sensors tracking frame X and alpha params + mCacheRefX.resize(mSize); + mCacheRefAlpha.resize(mSize); + for (int i = 0; i < mSize; i++) { + extractSensorXAlpha(i, mCacheRefX[i], mCacheRefAlpha[i]); + } + } +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfLayers() +{ + int numberOfLayers = 0; + + TGeoVolume* eclV = gGeoManager->GetVolume(getEC0VolPattern()); + if (!eclV) { + LOG(FATAL) << "EC0 volume " << getEC0VolPattern() << " is not in the geometry"; + } + + // Loop on all EC0V nodes, count Layer volumes by checking names + // Build on the fly layer - wrapper correspondence + TObjArray* nodes = eclV->GetNodes(); + int nNodes = nodes->GetEntriesFast(); + + for (int j = 0; j < nNodes; j++) { + int lrID = -1; + TGeoNode* nd = (TGeoNode*)nodes->At(j); + const char* name = nd->GetName(); + + if (strstr(name, getEC0LayerPattern())) { + numberOfLayers++; + if ((lrID = extractVolumeCopy(name, GeometryTGeo::getEC0LayerPattern())) < 0) { + LOG(FATAL) << "Failed to extract layer ID from the " << name; + exit(1); + } + + mLayerToWrapper[lrID] = -1; // not wrapped + } else if (strstr(name, getEC0WrapVolPattern())) { // this is a wrapper volume, may cointain layers + int wrID = -1; + if ((wrID = extractVolumeCopy(name, GeometryTGeo::getEC0WrapVolPattern())) < 0) { + LOG(FATAL) << "Failed to extract wrapper ID from the " << name; + exit(1); + } + + TObjArray* nodesW = nd->GetNodes(); + int nNodesW = nodesW->GetEntriesFast(); + + for (int jw = 0; jw < nNodesW; jw++) { + TGeoNode* ndW = (TGeoNode*)nodesW->At(jw); + if (strstr(ndW->GetName(), getEC0LayerPattern())) { + if ((lrID = extractVolumeCopy(ndW->GetName(), GeometryTGeo::getEC0LayerPattern())) < 0) { + LOG(FATAL) << "Failed to extract layer ID from the " << name; + exit(1); + } + numberOfLayers++; + mLayerToWrapper[lrID] = wrID; + } + } + } + } + return numberOfLayers; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfStaves(int lay) const +{ + int numberOfStaves = 0; + char laynam[30]; + snprintf(laynam, 30, "%s%d", getEC0LayerPattern(), lay); + TGeoVolume* volLr = gGeoManager->GetVolume(laynam); + if (!volLr) { + LOG(FATAL) << "can't find " << laynam << " volume"; + return -1; + } + + // Loop on all layer nodes, count Stave volumes by checking names + int nNodes = volLr->GetNodes()->GetEntries(); + for (int j = 0; j < nNodes; j++) { + // LOG(INFO) << "L" << lay << " " << j << " of " << nNodes << " " + // << volLr->GetNodes()->At(j)->GetName() << " " + // << getEC0StavePattern() << " -> " << numberOfStaves; + if (strstr(volLr->GetNodes()->At(j)->GetName(), getEC0StavePattern())) { + numberOfStaves++; + } + } + return numberOfStaves; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfHalfStaves(int lay) const +{ + if (sHalfStaveName.empty()) { + return 0; // for the setup w/o substave defined the stave and the substave is the same thing + } + int nSS = 0; + char stavnam[30]; + snprintf(stavnam, 30, "%s%d", getEC0StavePattern(), lay); + TGeoVolume* volLd = gGeoManager->GetVolume(stavnam); + if (!volLd) { + LOG(FATAL) << "can't find volume " << stavnam; + } + // Loop on all stave nodes, count Chip volumes by checking names + int nNodes = volLd->GetNodes()->GetEntries(); + for (int j = 0; j < nNodes; j++) { + if (strstr(volLd->GetNodes()->At(j)->GetName(), getEC0HalfStavePattern())) { + nSS++; + } + } + return nSS; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfModules(int lay) const +{ + if (sModuleName.empty()) { + return 0; + } + + char stavnam[30]; + TGeoVolume* volLd = nullptr; + + if (!sHalfStaveName.empty()) { + snprintf(stavnam, 30, "%s%d", getEC0HalfStavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + if (!volLd) { // no substaves, check staves + snprintf(stavnam, 30, "%s%d", getEC0StavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + if (!volLd) { + return 0; + } + + int nMod = 0; + + // Loop on all substave nodes, count module volumes by checking names + int nNodes = volLd->GetNodes()->GetEntries(); + + for (int j = 0; j < nNodes; j++) { + if (strstr(volLd->GetNodes()->At(j)->GetName(), getEC0ModulePattern())) { + nMod++; + } + } + return nMod; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const +{ + int numberOfChips = 0; + char stavnam[30]; + TGeoVolume* volLd = nullptr; + + if (!sModuleName.empty()) { + snprintf(stavnam, 30, "%s%d", getEC0ModulePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + if (!volLd) { // no modules on this layer, check substaves + if (!sHalfStaveName.empty()) { + snprintf(stavnam, 30, "%s%d", getEC0HalfStavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + } + if (!volLd) { // no substaves on this layer, check staves + snprintf(stavnam, 30, "%s%d", getEC0StavePattern(), lay); + volLd = gGeoManager->GetVolume(stavnam); + } + if (!volLd) { + LOG(FATAL) << "can't find volume containing chips on layer " << lay; + } + + // Loop on all stave nodes, count Chip volumes by checking names + int nNodes = volLd->GetNodes()->GetEntries(); + + double xmin = 1e9, xmax = -1e9, zmin = 1e9, zmax = -1e9; + double lab[3], loc[3] = {0, 0, 0}; + double dx = -1, dz = -1; + + for (int j = 0; j < nNodes; j++) { + // AliInfo(Form("L%d %d of %d %s %s -> + // %d",lay,j,nNodes,volLd->GetNodes()->At(j)->GetName(),GetEC0ChipPattern(),numberOfChips)); + TGeoNodeMatrix* node = (TGeoNodeMatrix*)volLd->GetNodes()->At(j); + if (!strstr(node->GetName(), getEC0ChipPattern())) { + continue; + } + node->LocalToMaster(loc, lab); + if (lab[0] > xmax) { + xmax = lab[0]; + } + if (lab[0] < xmin) { + xmin = lab[0]; + } + if (lab[2] > zmax) { + zmax = lab[2]; + } + if (lab[2] < zmin) { + zmin = lab[2]; + } + + numberOfChips++; + + if (dx < 0) { + TGeoShape* chShape = node->GetVolume()->GetShape(); + TGeoBBox* bbox = dynamic_cast(chShape); + if (!bbox) { + LOG(FATAL) << "Chip " << node->GetName() << " volume is of unprocessed shape " << chShape->IsA()->GetName(); + } else { + dx = 2 * bbox->GetDX(); + dz = 2 * bbox->GetDZ(); + } + } + } + + double spanX = xmax - xmin; + double spanZ = zmax - zmin; + nrow = TMath::Nint(spanX / dx + 1); + int ncol = TMath::Nint(spanZ / dz + 1); + if (nrow * ncol != numberOfChips) { + LOG(ERROR) << "Inconsistency between Nchips=" << numberOfChips << " and Nrow*Ncol=" << nrow << "*" << ncol << "->" + << nrow * ncol << "\n" + << "Extracted chip dimensions (x,z): " << dx << " " << dz << " Module Span: " << spanX << " " << spanZ; + } + return numberOfChips; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractLayerChipType(int lay) const +{ + char stavnam[30]; + snprintf(stavnam, 30, "%s%d", getEC0LayerPattern(), lay); + TGeoVolume* volLd = gGeoManager->GetVolume(stavnam); + if (!volLd) { + LOG(FATAL) << "can't find volume " << stavnam; + return -1; + } + return volLd->GetUniqueID(); +} + +//__________________________________________________________________________ +void GeometryTGeo::Print(Option_t*) const +{ + printf("NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); + if (!isBuilt()) + return; + + for (int i = 0; i < mNumberOfLayers; i++) { + printf( + "Lr%2d\tNStav:%2d\tNChips:%2d " + "(%dx%-2d)\tNMod:%d\tNSubSt:%d\tNSt:%3d\tChip#:%5d:%-5d\tWrapVol:%d\n", + i, mNumberOfStaves[i], mNumberOfChipsPerModule[i], mNumberOfChipRowsPerModule[i], + mNumberOfChipRowsPerModule[i] ? mNumberOfChipsPerModule[i] / mNumberOfChipRowsPerModule[i] : 0, + mNumberOfModules[i], mNumberOfHalfStaves[i], mNumberOfStaves[i], getFirstChipIndex(i), getLastChipIndex(i), + mLayerToWrapper[i]); + } +} + +//__________________________________________________________________________ +void GeometryTGeo::extractSensorXAlpha(int isn, float& x, float& alp) +{ + // calculate r and phi of the impact of the normal on the sensor + // (i.e. phi of the tracking frame alpha and X of the sensor in this frame) + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + const TGeoHMatrix* matL2G = extractMatrixSensor(isn); + + matL2G->LocalToMaster(locA, gloA); + matL2G->LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + double xp = gloB[0] - dx * t, yp = gloB[1] - dy * t; + x = Sqrt(xp * xp + yp * yp); + alp = ATan2(yp, xp); + BringTo02Pi(alp); +} + +//__________________________________________________________________________ +TGeoHMatrix& GeometryTGeo::createT2LMatrix(int isn) +{ + // create for sensor isn the TGeo matrix for Tracking to Local frame transformations + static TGeoHMatrix t2l; + float x = 0.f, alp = 0.f; + extractSensorXAlpha(isn, x, alp); + t2l.Clear(); + t2l.RotateZ(alp * RadToDeg()); // rotate in direction of normal to the sensor plane + const TGeoHMatrix* matL2G = extractMatrixSensor(isn); + const TGeoHMatrix& matL2Gi = matL2G->Inverse(); + t2l.MultiplyLeft(&matL2Gi); + return t2l; +} + +//__________________________________________________________________________ +int GeometryTGeo::extractVolumeCopy(const char* name, const char* prefix) const +{ + TString nms = name; + if (!nms.BeginsWith(prefix)) { + return -1; + } + nms.Remove(0, strlen(prefix)); + if (!isdigit(nms.Data()[0])) { + return -1; + } + return nms.Atoi(); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/MisalignmentParameter.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/MisalignmentParameter.cxx new file mode 100644 index 0000000000000..9f8c25d184c77 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/base/src/MisalignmentParameter.cxx @@ -0,0 +1,92 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 MisalignmentParameter.cxx +/// \brief Implementation of the MisalignmentParameter class + +#include "ECLayersBase/MisalignmentParameter.h" + +#include "FairParamList.h" + +using namespace o2::ecl; + +ClassImp(o2::ecl::MisalignmentParameter); + +MisalignmentParameter::MisalignmentParameter(const char* name, const char* title, const char* context) + : FairParGenericSet(name, title, context), + mShiftX(), + mShiftY(), + mShiftZ(), + mRotX(), + mRotY(), + mRotZ(), + mNumberOfDetectors(0) +{ +} + +MisalignmentParameter::~MisalignmentParameter() = default; +void MisalignmentParameter::Clear() {} +void MisalignmentParameter::putParams(FairParamList* list) +{ + if (!list) { + return; + } + + list->add("NumberOfDetectors", mNumberOfDetectors); + list->add("ShiftX", mShiftX); + list->add("ShiftY", mShiftY); + list->add("ShiftZ", mShiftZ); + list->add("RotationX", mRotX); + list->add("RotationY", mRotY); + list->add("RotationZ", mRotZ); +} + +Bool_t MisalignmentParameter::getParams(FairParamList* list) +{ + if (!list) { + return kFALSE; + } + + if (!list->fill("NumberOfDetectors", &mNumberOfDetectors)) { + return kFALSE; + } + + mShiftX.Set(mNumberOfDetectors); + if (!list->fill("ShiftX", &mShiftX)) { + return kFALSE; + } + + mShiftY.Set(mNumberOfDetectors); + if (!list->fill("ShiftY", &mShiftY)) { + return kFALSE; + } + + mShiftZ.Set(mNumberOfDetectors); + if (!list->fill("ShiftZ", &mShiftZ)) { + return kFALSE; + } + + mRotX.Set(mNumberOfDetectors); + if (!list->fill("RotationX", &mRotX)) { + return kFALSE; + } + + mRotY.Set(mNumberOfDetectors); + if (!list->fill("RotationY", &mRotY)) { + return kFALSE; + } + + mRotZ.Set(mNumberOfDetectors); + if (!list->fill("RotationZ", &mRotZ)) { + return kFALSE; + } + + return kTRUE; +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/CMakeLists.txt new file mode 100644 index 0000000000000..ef10fd4b24c1a --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +add_subdirectory(EVE) diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/.o2eve_config b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/.o2eve_config new file mode 100644 index 0000000000000..c123e32ff4efe --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/.o2eve_config @@ -0,0 +1,96 @@ +#===============================================================# +# This is Event Display config file. # +# # +# You can copy it to your home directory and set custom values. # +# # +#===============================================================# + +trigger.class.filter: none + +axes.show: 0 +fullscreen.mode: 0 +background.color: 1 + +camera.3D.rotation.horizontal: -0.4 +camera.3D.rotation.vertical: 1.0 +camera.3D.zoom: 1.0 +camera.R-Phi.zoom: 4.0 +camera.Rho-Z.zoom: 4.0 + +simple.geom.default: R3 +simple.geom.R3.path: ./ +simple.geom.R2.path: ./ + +tracks.width: 2 + +tracks.byType.electron: 600 +tracks.byType.muon: 416 +tracks.byType.pion: 632 +tracks.byType.kaon: 400 +tracks.byType.proton: 797 +tracks.byType.unknown: 920 + +PHS.draw: 0 +TPC.draw: 0 +HMP.draw: 0 +TRD.draw: 0 +MCH.draw: 0 +EMC.draw: 0 +ACO.draw: 0 +ITS.draw: 1 +TOF.draw: 0 +MFT.draw: 0 +T00.draw: 0 +FIT.draw: 0 +AD0.draw: 0 +FMD.draw: 0 + +PHS.color: 53 +TPC.color: 3 +HMP.color: 5 +TRD.color: 921 +MCH.color: 920 +EMC.color: 53 +ACO.color: 920 +ITS.color: 634 +TOF.color: 634 +MFT.color: 920 +T00.color: 2 +FIT.color: 2 +AD0.color: 5 +FMD.color: 906 + +PHS.trans: 70 +TPC.trans: 60 +HMP.trans: 70 +TRD.trans: 80 +MCH.trans: 80 +EMC.trans: 80 +ACO.trans: 50 +ITS.trans: 50 +TOF.trans: 60 +MFT.trans: 80 +T00.trans: 70 +FIT.trans: 40 +AD0.trans: 40 +FMD.trans: 50 + +MCH.line.color: 920 +TPC.line.color: 1 +HMP.line.color: 1 +PHS.line.color: 1 +TRD.line.color: 1 +TOF.line.color: 1 +EMC.line.color: 1 +ITS.line.color: 1 +MFT.line.color: 0 +T00.line.color: 1 +FIT.line.color: 1 +AD0.line.color: 1 +FMD.line.color: 907 + +OCDB.default.path: local:///local/cdb/ + +# +# EOF +# diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/CMakeLists.txt new file mode 100644 index 0000000000000..57ee5b6529bf7 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +o2_add_test_root_macro(DisplayEventsComp.C + PUBLIC_LINK_LIBRARIES O2::EventVisualisationView + O2::ITSMFTReconstruction + O2::ECLayersBase O2::DataFormatsITS + LABELS its) + +o2_add_test_root_macro(DisplayEvents.C + PUBLIC_LINK_LIBRARIES O2::EventVisualisationView + O2::ITSMFTReconstruction + O2::ECLayersBase O2::DataFormatsITS + LABELS its) + +o2_add_test_root_macro(simple_geom_ITS.C + PUBLIC_LINK_LIBRARIES O2::EventVisualisationView + O2::ITSMFTReconstruction + O2::ECLayersBase O2::DataFormatsITS + LABELS its) diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/DisplayEvents.C b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/DisplayEvents.C new file mode 100644 index 0000000000000..72bdbb5ba5e0b --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/DisplayEvents.C @@ -0,0 +1,608 @@ +/// \file DisplayEvents.C +/// \brief Simple macro to display ITS digits, clusters and tracks + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EventVisualisationView/MultiView.h" + +#include "EndCapsReconstruction/ChipMappingEC0.h" +#include "EndCapsReconstruction/DigitPixelReader.h" +#include "EndCapsReconstruction/RawPixelReader.h" +#include "EndCapsBase/SegmentationAlpide.h" +#include "DataFormatsITSMFT/Digit.h" +#include "ECLayersBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/Cluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITS/TrackITS.h" +#endif + +using namespace o2::endcaps; + +extern TEveManager* gEve; +static TEveScene* chipScene; + +static TGNumberEntry* gEntry; +static TGNumberEntry* gChipID; + +class Data +{ + public: + void loadData(int entry); + void displayData(int entry, int chip); + int getLastEvent() const { return mLastEvent; } + void setRawPixelReader(std::string input) + { + auto reader = new RawPixelReader(); + reader->openInput(input); + mPixelReader = reader; + mPixelReader->getNextChipData(mChipData); + mHB = mPixelReader->getInteractionRecordHB(); + mTr = mPixelReader->getInteractionRecord(); + } + void setDigitPixelReader(std::string input) + { + auto reader = new DigitPixelReader(); + reader->openInput(input, o2::detectors::DetID("EC0")); + reader->init(); + reader->readNextEntry(); + mPixelReader = reader; + mPixelReader->getNextChipData(mChipData); + mHB = mPixelReader->getInteractionRecordHB(); + mTr = mPixelReader->getInteractionRecord(); + } + void setDigiTree(TTree* t) { mDigiTree = t; } + void setClusTree(TTree* t); + void setTracTree(TTree* t); + + private: + // Data loading members + int mLastEvent = 0; + PixelReader* mPixelReader = nullptr; + ChipPixelData mChipData; + o2::InteractionRecord mHB; + o2::InteractionRecord mTr; + std::vector mDigits; + std::vector* mClusterBuffer = nullptr; + gsl::span mClusters; + std::vector* mClustersROF = nullptr; + std::vector* mTrackBuffer = nullptr; + std::vector* mClIdxBuffer = nullptr; + gsl::span mTracks; + std::vector* mTracksROF = nullptr; + void loadDigits(); + void loadDigits(int entry); + void loadClusters(int entry); + void loadTracks(int entry); + + TTree* mDigiTree = nullptr; + TTree* mClusTree = nullptr; + TTree* mTracTree = nullptr; + + // TEve-related members + TEveElementList* mEvent = nullptr; + TEveElementList* mChip = nullptr; + TEveElement* getEveChipDigits(int chip); + TEveElement* getEveChipClusters(int chip); + TEveElement* getEveClusters(); + TEveElement* getEveTracks(); +}; + +std::vector evdata; + +void Data::loadDigits() +{ + auto hb = mPixelReader->getInteractionRecordHB(); + auto tr = mPixelReader->getInteractionRecord(); + std::cout << "orbit/crossing: " << ' ' << tr.orbit << '/' << tr.bc << '\n'; + + mDigits.clear(); + + do { + auto chipID = mChipData.getChipID(); + auto pixels = mChipData.getData(); + for (auto& pixel : pixels) { + auto col = pixel.getCol(); + auto row = pixel.getRowDirect(); + mDigits.emplace_back(chipID, row, col, 0); + } + if (!mPixelReader->getNextChipData(mChipData)) + return; + hb = mPixelReader->getInteractionRecordHB(); + tr = mPixelReader->getInteractionRecord(); + } while (mHB == hb && mTr == tr); + mHB = hb; + mTr = tr; + + //std::cout << "Number of EC0Digits: " << mDigits.size() << '\n'; +} + +void Data::loadDigits(int entry) +{ + if (mPixelReader == nullptr) + return; + + for (; mLastEvent < entry; mLastEvent++) { + auto hb = mPixelReader->getInteractionRecordHB(); + auto tr = mPixelReader->getInteractionRecord(); + do { + if (!mPixelReader->getNextChipData(mChipData)) + return; + hb = mPixelReader->getInteractionRecordHB(); + tr = mPixelReader->getInteractionRecord(); + } while (mHB == hb && mTr == tr); + mHB = hb; + mTr = tr; + } + mLastEvent++; + loadDigits(); +} + +void Data::setClusTree(TTree* tree) +{ + if (tree == nullptr) { + std::cerr << "No tree for clusters !\n"; + return; + } + tree->SetBranchAddress("EC0Cluster", &mClusterBuffer); + tree->SetBranchAddress("EC0ClustersROF", &mClustersROF); + tree->GetEntry(0); + mClusTree = tree; +} + +void Data::loadClusters(int entry) +{ + if (mClusTree == nullptr) + return; + + int first = 0, last = mClusterBuffer->size(); + if (!mClustersROF->empty()) { + auto rof = (*mClustersROF)[entry]; + first = rof.getFirstEntry(); + last = first + rof.getNEntries(); + } + mClusters = gsl::make_span(&(*mClusterBuffer)[first], last - first); + + //std::cout << "Number of EC0Clusters: " << mClusters.size() << '\n'; +} + +void Data::setTracTree(TTree* tree) +{ + if (tree == nullptr) { + std::cerr << "No tree for tracks !\n"; + return; + } + tree->SetBranchAddress("EC0Track", &mTrackBuffer); + tree->SetBranchAddress("EC0TrackClusIdx", &mClIdxBuffer); + tree->SetBranchAddress("EC0TracksROF", &mTracksROF); + tree->GetEntry(0); + mTracTree = tree; +} + +void Data::loadTracks(int entry) +{ + static int lastLoaded = -1; + + if (mTracTree == nullptr) + return; + + int first = 0, last = mTrackBuffer->size(); + if (!mTracksROF->empty()) { + auto rof = (*mTracksROF)[entry]; + first = rof.getFirstEntry(); + last = first + rof.getNEntries(); + } + mTracks = gsl::make_span(&(*mTrackBuffer)[first], last - first); + + //std::cout << "Number of EC0Tracks: " << mTracks.size() << '\n'; +} + +void Data::loadData(int entry) +{ + loadDigits(entry); + loadClusters(entry); + loadTracks(entry); +} + +constexpr float sizey = SegmentationAlpide::ActiveMatrixSizeRows; +constexpr float sizex = SegmentationAlpide::ActiveMatrixSizeCols; +constexpr float dy = SegmentationAlpide::PitchRow; +constexpr float dx = SegmentationAlpide::PitchCol; +constexpr float gap = 1e-4; // For a better visualization of pixels + +TEveElement* Data::getEveChipDigits(int chip) +{ + TEveFrameBox* box = new TEveFrameBox(); + box->SetAAQuadXY(0, 0, 0, sizex, sizey); + box->SetFrameColor(kGray); + + // Digits + TEveQuadSet* qdigi = new TEveQuadSet("digits"); + qdigi->SetOwnIds(kTRUE); + qdigi->SetFrame(box); + qdigi->Reset(TEveQuadSet::kQT_RectangleXY, kFALSE, 32); + auto gman = o2::ecl::GeometryTGeo::Instance(); + std::vector occup(gman->getNumberOfChips()); + for (const auto& d : mDigits) { + auto id = d.getChipIndex(); + occup[id]++; + if (id != chip) + continue; + + int row = d.getRow(); + int col = d.getColumn(); + int charge = d.getCharge(); + qdigi->AddQuad(col * dx + gap, row * dy + gap, 0., dx - 2 * gap, dy - 2 * gap); + qdigi->QuadValue(charge); + } + qdigi->RefitPlex(); + TEveTrans& t = qdigi->RefMainTrans(); + t.RotateLF(1, 3, 0.5 * TMath::Pi()); + t.SetPos(0, 0, 0); + + auto most = std::distance(occup.begin(), std::max_element(occup.begin(), occup.end())); + if (occup[most] > 0) { + std::cout << "Most occupied chip: " << most << " (" << occup[most] << " digits)\n"; + } + if (occup[chip] > 0) { + std::cout << "Chip " << chip << " number of digits " << occup[chip] << '\n'; + return qdigi; + } + delete qdigi; + return nullptr; +} + +TEveElement* Data::getEveChipClusters(int chip) +{ + TEveFrameBox* box = new TEveFrameBox(); + box->SetAAQuadXY(0, 0, 0, sizex, sizey); + box->SetFrameColor(kGray); + + // Clusters + TEveQuadSet* qclus = new TEveQuadSet("clusters"); + qclus->SetOwnIds(kTRUE); + qclus->SetFrame(box); + int ncl = 0; + qclus->Reset(TEveQuadSet::kQT_LineXYFixedZ, kFALSE, 32); + auto gman = o2::ecl::GeometryTGeo::Instance(); + for (const auto& c : mClusters) { + auto id = c.getSensorID(); + if (id != chip) + continue; + ncl++; + + auto xyz = c.getXYZLoc(*gman); + int row = 0, col = 0, len = 1, wid = 1; + SegmentationAlpide::localToDetector(xyz.X(), xyz.Z(), row, col); + qclus->AddLine(col * dx, row * dy, len * dx, 0.); + qclus->AddLine(col * dx, row * dy, 0., wid * dy); + qclus->AddLine((col + len) * dx, row * dy, 0., wid * dy); + qclus->AddLine(col * dx, (row + wid) * dy, len * dx, 0.); + } + qclus->RefitPlex(); + TEveTrans& ct = qclus->RefMainTrans(); + ct.RotateLF(1, 3, 0.5 * TMath::Pi()); + ct.SetPos(0, 0, 0); + + if (ncl > 0) { + std::cout << "Chip " << chip << " number of clusters " << ncl << '\n'; + return qclus; + } + delete qclus; + return nullptr; +} + +TEveElement* Data::getEveClusters() +{ + if (mClusters.empty()) + return nullptr; + + auto gman = o2::ecl::GeometryTGeo::Instance(); + TEvePointSet* clusters = new TEvePointSet("clusters"); + clusters->SetMarkerColor(kBlue); + for (const auto& c : mClusters) { + const auto& gloC = c.getXYZGloRot(*gman); + clusters->SetNextPoint(gloC.X(), gloC.Y(), gloC.Z()); + } + return clusters; +} + +TEveElement* Data::getEveTracks() +{ + if (mTracks.empty()) + return nullptr; + + auto gman = o2::ecl::GeometryTGeo::Instance(); + TEveTrackList* tracks = new TEveTrackList("tracks"); + auto prop = tracks->GetPropagator(); + prop->SetMagField(0.5); + prop->SetMaxR(50.); + for (const auto& rec : mTracks) { + std::array p; + rec.getPxPyPzGlo(p); + TEveRecTrackD t; + t.fP = {p[0], p[1], p[2]}; + t.fSign = (rec.getSign() < 0) ? -1 : 1; + TEveTrack* track = new TEveTrack(&t, prop); + track->SetLineColor(kMagenta); + tracks->AddElement(track); + + if (mClusters.empty()) + continue; + TEvePointSet* tpoints = new TEvePointSet("tclusters"); + tpoints->SetMarkerColor(kGreen); + int nc = rec.getNumberOfClusters(); + int idxRef = rec.getFirstClusterEntry(); + while (nc--) { + Int_t idx = (*mClIdxBuffer)[idxRef + nc]; + const Cluster& c = mClusters[idx]; + const auto& gloC = c.getXYZGloRot(*gman); + tpoints->SetNextPoint(gloC.X(), gloC.Y(), gloC.Z()); + } + track->AddElement(tpoints); + } + tracks->MakeTracks(); + + return tracks; +} + +void Data::displayData(int entry, int chip) +{ + std::string ename("Part of Event #"); + ename += std::to_string(entry); + + // Chip display + auto chipDigits = getEveChipDigits(chip); + auto chipClusters = getEveChipClusters(chip); + + delete mChip; + mChip = nullptr; + if (chipDigits || chipClusters) { + std::string cname(ename + " ALPIDE chip #"); + cname += std::to_string(chip); + mChip = new TEveElementList(cname.c_str()); + if (chipDigits) { + mChip->AddElement(chipDigits); + } + if (chipClusters) { + mChip->AddElement(chipClusters); + } + gEve->AddElement(mChip, chipScene); + } + + // Event display + auto clusters = getEveClusters(); + auto tracks = getEveTracks(); + + delete mEvent; + mEvent = nullptr; + if (clusters || tracks) { + mEvent = new TEveElementList(ename.c_str()); + if (clusters) { + mEvent->AddElement(clusters); + } + if (tracks) { + mEvent->AddElement(tracks); + } + auto multi = o2::event_visualisation::MultiView::getInstance(); + multi->registerEvent(mEvent); + } + + gEve->Redraw3D(kFALSE); +} + +void load(int entry, int chip) +{ + std::cout << "\n*** RO frame #" << entry << " ***\n"; + gEntry->SetIntNumber(entry); + gChipID->SetIntNumber(chip); + + for (auto& evdat : evdata) { + int lastEvent = evdat.getLastEvent(); + if (lastEvent > entry) { + std::cerr << "\nERROR: Cannot stay or go back over events. Please increase the event number !\n\n"; + gEntry->SetIntNumber(lastEvent - 1); + continue; + } + + evdat.loadData(entry); + evdat.displayData(entry, chip); + } +} + +void init(int entry = 0, int chip = 13, + std::vector digifiles = {"itsdigits.root"}, + bool rawdata = false, + const std::vector clusfiles = {"data-ep4-link0"}, + std::string tracfile = "o2trac_its.root", + std::string inputGeom = "") +{ + TEveManager::Create(kTRUE, "V"); + TEveBrowser* browser = gEve->GetBrowser(); + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto gman = o2::ecl::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, + o2::TransformType::L2G)); + + // Chip View + browser->GetTabRight()->SetText("Chip View"); + auto chipView = gEve->GetDefaultViewer(); + chipView->SetName("Chip Viewer"); + chipView->DestroyElements(); + chipScene = gEve->SpawnNewScene("Chip View", "Chip view desciption"); + chipView->AddScene(chipScene); + TGLViewer* v = gEve->GetDefaultGLViewer(); + v->SetCurrentCamera(TGLViewer::kCameraOrthoZOY); + TGLCameraOverlay* co = v->GetCameraOverlay(); + co->SetShowOrthographic(kTRUE); + co->SetOrthographicMode(TGLCameraOverlay::kGridFront); + + // Event View + auto multi = o2::event_visualisation::MultiView::getInstance(); + multi->drawGeometryForDetector("EC0"); + + gEve->AddEvent(new TEveEventManager()); + + // Event navigation + browser->StartEmbedding(TRootBrowser::kBottom); + auto frame = new TGMainFrame(gClient->GetRoot(), 1000, 600, kVerticalFrame); + + auto h = new TGHorizontalFrame(frame); + auto b = new TGTextButton(h, "PrevEvnt", "prev()"); + h->AddFrame(b); + gEntry = new TGNumberEntry(h, 0, 5, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative, TGNumberFormat::kNELLimitMinMax, 0, 10000); + gEntry->Connect("ValueSet(Long_t)", 0, 0, "load()"); + h->AddFrame(gEntry); + b = new TGTextButton(h, "NextEvnt", "next()"); + h->AddFrame(b); + frame->AddFrame(h); + + // Chip navigation + h = new TGHorizontalFrame(frame); + b = new TGTextButton(h, "PrevChip", "prevChip()"); + h->AddFrame(b); + gChipID = new TGNumberEntry(h, 0, 5, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative, TGNumberFormat::kNELLimitMinMax, 0, gman->getNumberOfChips()); + gChipID->Connect("ValueSet(Long_t)", 0, 0, "loadChip()"); + h->AddFrame(gChipID); + b = new TGTextButton(h, "NextChip", "nextChip()"); + h->AddFrame(b); + frame->AddFrame(h); + + frame->MapSubwindows(); + frame->MapWindow(); + browser->StopEmbedding("Navigator"); + + TFile* file; + // Data sources + + std::vector tmp(std::max(digifiles.size(), clusfiles.size())); + evdata.swap(tmp); + int i = 0; + for (auto& digifile : digifiles) { + if (rawdata) { + std::ifstream* rawfile = new std::ifstream(digifile.data(), std::ifstream::binary); + if (rawfile->good()) { + delete rawfile; + evdata[i++].setRawPixelReader(digifile.data()); + } else + std::cerr << "\nERROR: Cannot open file: " << digifile << "\n\n"; + } else { + file = TFile::Open(digifile.data()); + if (file && gFile->IsOpen()) { + file->Close(); + evdata[i++].setDigitPixelReader(digifile.data()); + } else + std::cerr << "\nERROR: Cannot open file: " << digifile << "\n\n"; + } + } + + i = 0; + for (auto& clusfile : clusfiles) { + file = TFile::Open(clusfile.data()); + if (file && gFile->IsOpen()) { + evdata[i++].setClusTree((TTree*)gFile->Get("o2sim")); + } else { + std::cerr << "ERROR: Cannot open file: " << clusfile << "\n\n"; + } + } + /* + file = TFile::Open(tracfile.data()); + if (file && gFile->IsOpen()) + evdata.setTracTree((TTree*)gFile->Get("o2sim")); + else + std::cerr << "\nERROR: Cannot open file: " << tracfile << "\n\n"; + */ + std::cout << "\n **** Navigation over events and chips ****\n"; + std::cout << " load(event, chip) \t jump to the specified event and chip\n"; + std::cout << " next() \t\t load next event \n"; + std::cout << " prev() \t\t load previous event \n"; + std::cout << " loadChip(chip) \t jump to the specified chip within the current event \n"; + std::cout << " nextChip() \t\t load the next chip within the current event \n"; + std::cout << " prevChip() \t\t load the previous chip within the current event \n"; + + load(entry, chip); + gEve->Redraw3D(kTRUE); +} + +void load() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + + load(event, chip); +} + +void next() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + event++; + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + load(event, chip); +} + +void prev() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + event--; + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + load(event, chip); +} + +void loadChip() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + for (auto& evdat : evdata) { + evdat.displayData(event, chip); + } +} + +void loadChip(int chip) +{ + gChipID->SetIntNumber(chip); + loadChip(); +} + +void nextChip() +{ + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + chip++; + loadChip(chip); +} + +void prevChip() +{ + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + chip--; + loadChip(chip); +} + +void DisplayEvents() +{ + // A dummy function with the same name as this macro +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/DisplayEventsComp.C b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/DisplayEventsComp.C new file mode 100644 index 0000000000000..0d300e74aed23 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/DisplayEventsComp.C @@ -0,0 +1,594 @@ +/// \file DisplayEvents.C +/// \brief Simple macro to display ITS digits, clusters and tracks + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EventVisualisationView/MultiView.h" + +#include "EndCapsReconstruction/ChipMappingEC0.h" +#include "EndCapsReconstruction/DigitPixelReader.h" +#include "EndCapsReconstruction/RawPixelReader.h" +#include "EndCapsBase/SegmentationAlpide.h" +#include "DataFormatsITSMFT/Digit.h" +#include "ECLayersBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsEndCaps/TopologyDictionary.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITS/TrackITS.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#endif + +using namespace o2::endcaps; + +extern TEveManager* gEve; +static TEveScene* chipScene; + +static TGNumberEntry* gEntry; +static TGNumberEntry* gChipID; + +class Data +{ + public: + void loadData(int entry); + void displayData(int entry, int chip); + int getLastEvent() const { return mLastEvent; } + void setRawPixelReader(std::string input) + { + auto reader = new RawPixelReader(); + reader->openInput(input); + mPixelReader = reader; + mPixelReader->getNextChipData(mChipData); + mIR = mChipData.getInteractionRecord(); + } + void setDigitPixelReader(std::string input) + { + auto reader = new DigitPixelReader(); + reader->openInput(input, o2::detectors::DetID("EC0")); + reader->init(); + reader->readNextEntry(); + mPixelReader = reader; + mPixelReader->getNextChipData(mChipData); + mIR = mChipData.getInteractionRecord(); + } + void setDigiTree(TTree* t) { mDigiTree = t; } + void setClusTree(TTree* t); + void setTracTree(TTree* t); + + private: + // Data loading members + int mLastEvent = 0; + PixelReader* mPixelReader = nullptr; + ChipPixelData mChipData; + o2::InteractionRecord mIR; + std::vector mDigits; + std::vector* mClusterBuffer = nullptr; + gsl::span mClusters; + std::vector* mClustersROF = nullptr; + std::vector* mTrackBuffer = nullptr; + std::vector* mClIdxBuffer = nullptr; + gsl::span mTracks; + std::vector* mTracksROF = nullptr; + void loadDigits(); + void loadDigits(int entry); + void loadClusters(int entry); + void loadTracks(int entry); + + TTree* mDigiTree = nullptr; + TTree* mClusTree = nullptr; + TTree* mTracTree = nullptr; + + // TEve-related members + TEveElementList* mEvent = nullptr; + TEveElementList* mChip = nullptr; + TEveElement* getEveChipDigits(int chip); + TEveElement* getEveChipClusters(int chip); + TEveElement* getEveClusters(); + TEveElement* getEveTracks(); +} evdata; + +TopologyDictionary dict; + +void Data::loadDigits() +{ + auto ir = mChipData.getInteractionRecord(); + std::cout << "orbit/crossing: " << ' ' << ir.orbit << '/' << ir.bc << '\n'; + + mDigits.clear(); + + do { + auto chipID = mChipData.getChipID(); + auto pixels = mChipData.getData(); + for (auto& pixel : pixels) { + auto col = pixel.getCol(); + auto row = pixel.getRowDirect(); + mDigits.emplace_back(chipID, row, col, 0); + } + if (!mPixelReader->getNextChipData(mChipData)) + return; + ir = mChipData.getInteractionRecord(); + } while (mIR == ir); + mIR = ir; + + std::cout << "Number of ITSDigits: " << mDigits.size() << '\n'; +} + +void Data::loadDigits(int entry) +{ + if (mPixelReader == nullptr) + return; + + for (; mLastEvent < entry; mLastEvent++) { + auto ir = mChipData.getInteractionRecord(); + do { + if (!mPixelReader->getNextChipData(mChipData)) + return; + ir = mChipData.getInteractionRecord(); + } while (mIR == ir); + mIR = ir; + } + mLastEvent++; + loadDigits(); +} + +void Data::setClusTree(TTree* tree) +{ + if (tree == nullptr) { + std::cerr << "No tree for clusters !\n"; + return; + } + tree->SetBranchAddress("ITSClusterComp", &mClusterBuffer); + tree->SetBranchAddress("ITSClustersROF", &mClustersROF); + tree->GetEntry(0); + mClusTree = tree; +} + +void Data::loadClusters(int entry) +{ + if (mClusTree == nullptr) + return; + + int first = 0, last = mClusterBuffer->size(); + if (!mClustersROF->empty()) { + auto rof = (*mClustersROF)[entry]; + first = rof.getFirstEntry(); + last = first + rof.getNEntries(); + } + mClusters = gsl::make_span(&(*mClusterBuffer)[first], last - first); + + std::cout << "Number of ITSClusters: " << mClusters.size() << '\n'; +} + +void Data::setTracTree(TTree* tree) +{ + if (tree == nullptr) { + std::cerr << "No tree for tracks !\n"; + return; + } + tree->SetBranchAddress("ITSTrack", &mTrackBuffer); + tree->SetBranchAddress("ITSTrackClusIdx", &mClIdxBuffer); + tree->SetBranchAddress("ITSTracksROF", &mTracksROF); + tree->GetEntry(0); + mTracTree = tree; +} + +void Data::loadTracks(int entry) +{ + static int lastLoaded = -1; + + if (mTracTree == nullptr) + return; + + int first = 0, last = mTrackBuffer->size(); + if (!mTracksROF->empty()) { + auto rof = (*mTracksROF)[entry]; + first = rof.getFirstEntry(); + last = first + rof.getNEntries(); + } + mTracks = gsl::make_span(&(*mTrackBuffer)[first], last - first); + + std::cout << "Number of ITSTracks: " << mTracks.size() << '\n'; +} + +void Data::loadData(int entry) +{ + loadDigits(entry); + loadClusters(entry); + loadTracks(entry); +} + +constexpr float sizey = SegmentationAlpide::ActiveMatrixSizeRows; +constexpr float sizex = SegmentationAlpide::ActiveMatrixSizeCols; +constexpr float dy = SegmentationAlpide::PitchRow; +constexpr float dx = SegmentationAlpide::PitchCol; +constexpr float gap = 1e-4; // For a better visualization of pixels + +TEveElement* Data::getEveChipDigits(int chip) +{ + TEveFrameBox* box = new TEveFrameBox(); + box->SetAAQuadXY(0, 0, 0, sizex, sizey); + box->SetFrameColor(kGray); + + // Digits + TEveQuadSet* qdigi = new TEveQuadSet("digits"); + qdigi->SetOwnIds(kTRUE); + qdigi->SetFrame(box); + qdigi->Reset(TEveQuadSet::kQT_RectangleXY, kFALSE, 32); + auto gman = o2::ecl::GeometryTGeo::Instance(); + std::vector occup(gman->getNumberOfChips()); + for (const auto& d : mDigits) { + auto id = d.getChipIndex(); + occup[id]++; + if (id != chip) + continue; + + int row = d.getRow(); + int col = d.getColumn(); + int charge = d.getCharge(); + qdigi->AddQuad(col * dx + gap, row * dy + gap, 0., dx - 2 * gap, dy - 2 * gap); + qdigi->QuadValue(charge); + } + qdigi->RefitPlex(); + TEveTrans& t = qdigi->RefMainTrans(); + t.RotateLF(1, 3, 0.5 * TMath::Pi()); + t.SetPos(0, 0, 0); + + auto most = std::distance(occup.begin(), std::max_element(occup.begin(), occup.end())); + if (occup[most] > 0) { + std::cout << "Most occupied chip: " << most << " (" << occup[most] << " digits)\n"; + } + if (occup[chip] > 0) { + std::cout << "Chip " << chip << " number of digits " << occup[chip] << '\n'; + return qdigi; + } + delete qdigi; + return nullptr; +} + +TEveElement* Data::getEveChipClusters(int chip) +{ + TEveFrameBox* box = new TEveFrameBox(); + box->SetAAQuadXY(0, 0, 0, sizex, sizey); + box->SetFrameColor(kGray); + + // Clusters + TEveQuadSet* qclus = new TEveQuadSet("clusters"); + qclus->SetOwnIds(kTRUE); + qclus->SetFrame(box); + int ncl = 0; + qclus->Reset(TEveQuadSet::kQT_LineXYFixedZ, kFALSE, 32); + for (const auto& c : mClusters) { + auto id = c.getSensorID(); + if (id != chip) + continue; + ncl++; + + auto pattern = dict.getPattern(c.getPatternID()); + int row = c.getRow(); + int col = c.getCol(); + int len = pattern.getColumnSpan(); + int wid = pattern.getRowSpan(); + qclus->AddLine(col * dx, row * dy, len * dx, 0.); + qclus->AddLine(col * dx, row * dy, 0., wid * dy); + qclus->AddLine((col + len) * dx, row * dy, 0., wid * dy); + qclus->AddLine(col * dx, (row + wid) * dy, len * dx, 0.); + } + qclus->RefitPlex(); + TEveTrans& ct = qclus->RefMainTrans(); + ct.RotateLF(1, 3, 0.5 * TMath::Pi()); + ct.SetPos(0, 0, 0); + + if (ncl > 0) { + std::cout << "Chip " << chip << " number of clusters " << ncl << '\n'; + return qclus; + } + delete qclus; + return nullptr; +} + +TEveElement* Data::getEveClusters() +{ + if (mClusters.empty()) + return nullptr; + + auto gman = o2::ecl::GeometryTGeo::Instance(); + TEvePointSet* clusters = new TEvePointSet("clusters"); + clusters->SetMarkerColor(kBlue); + for (const auto& c : mClusters) { + auto locC = dict.getClusterCoordinates(c); + auto id = c.getSensorID(); + const auto gloC = gman->getMatrixL2G(id) * locC; + clusters->SetNextPoint(gloC.X(), gloC.Y(), gloC.Z()); + } + return clusters; +} + +TEveElement* Data::getEveTracks() +{ + if (mTracks.empty()) + return nullptr; + + auto gman = o2::ecl::GeometryTGeo::Instance(); + TEveTrackList* tracks = new TEveTrackList("tracks"); + auto prop = tracks->GetPropagator(); + prop->SetMagField(0.5); + prop->SetMaxR(50.); + for (const auto& rec : mTracks) { + std::array p; + rec.getPxPyPzGlo(p); + TEveRecTrackD t; + t.fP = {p[0], p[1], p[2]}; + t.fSign = (rec.getSign() < 0) ? -1 : 1; + TEveTrack* track = new TEveTrack(&t, prop); + track->SetLineColor(kMagenta); + tracks->AddElement(track); + + if (mClusters.empty()) + continue; + TEvePointSet* tpoints = new TEvePointSet("tclusters"); + tpoints->SetMarkerColor(kGreen); + int nc = rec.getNumberOfClusters(); + int idxRef = rec.getFirstClusterEntry(); + + while (nc--) { + Int_t idx = (*mClIdxBuffer)[idxRef + nc]; + const CompClusterExt& c = mClusters[idx]; + auto locC = dict.getClusterCoordinates(c); + auto id = c.getSensorID(); + const auto gloC = gman->getMatrixL2G(id) * locC; + tpoints->SetNextPoint(gloC.X(), gloC.Y(), gloC.Z()); + } + track->AddElement(tpoints); + } + tracks->MakeTracks(); + + return tracks; +} + +void Data::displayData(int entry, int chip) +{ + std::string ename("Event #"); + ename += std::to_string(entry); + + // Chip display + auto chipDigits = getEveChipDigits(chip); + auto chipClusters = getEveChipClusters(chip); + delete mChip; + mChip = nullptr; + if (chipDigits || chipClusters) { + std::string cname(ename + " ALPIDE chip #"); + cname += std::to_string(chip); + mChip = new TEveElementList(cname.c_str()); + if (chipDigits) { + mChip->AddElement(chipDigits); + } + if (chipClusters) { + mChip->AddElement(chipClusters); + } + gEve->AddElement(mChip, chipScene); + } + + // Event display + auto clusters = getEveClusters(); + auto tracks = getEveTracks(); + delete mEvent; + mEvent = nullptr; + if (clusters || tracks) { + mEvent = new TEveElementList(ename.c_str()); + if (clusters) + mEvent->AddElement(clusters); + if (tracks) + mEvent->AddElement(tracks); + auto multi = o2::event_visualisation::MultiView::getInstance(); + multi->registerEvent(mEvent); + } + gEve->Redraw3D(kFALSE); +} + +void load(int entry, int chip) +{ + int lastEvent = evdata.getLastEvent(); + if (lastEvent > entry) { + std::cerr << "\nERROR: Cannot stay or go back over events. Please increase the event number !\n\n"; + gEntry->SetIntNumber(lastEvent - 1); + return; + } + + gEntry->SetIntNumber(entry); + gChipID->SetIntNumber(chip); + + std::cout << "\n*** Event #" << entry << " ***\n"; + evdata.loadData(entry); + evdata.displayData(entry, chip); +} + +void init(int entry = 0, int chip = 13, + std::string digifile = "itsdigits.root", + bool rawdata = false, + std::string clusfile = "o2clus_its.root", + std::string tracfile = "o2trac_its.root", + std::string inputGeom = "") +{ + dict.readBinaryFile(o2::base::NameConf::getDictionaryFileName(o2::detectors::DetID::ITS, "", ".bin")); + + TEveManager::Create(kTRUE, "V"); + TEveBrowser* browser = gEve->GetBrowser(); + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto gman = o2::ecl::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L, o2::TransformType::T2GRot, + o2::TransformType::L2G)); + + // Chip View + browser->GetTabRight()->SetText("Chip View"); + auto chipView = gEve->GetDefaultViewer(); + chipView->SetName("Chip Viewer"); + chipView->DestroyElements(); + chipScene = gEve->SpawnNewScene("Chip View", "Chip view desciption"); + chipView->AddScene(chipScene); + TGLViewer* v = gEve->GetDefaultGLViewer(); + v->SetCurrentCamera(TGLViewer::kCameraOrthoZOY); + TGLCameraOverlay* co = v->GetCameraOverlay(); + co->SetShowOrthographic(kTRUE); + co->SetOrthographicMode(TGLCameraOverlay::kGridFront); + + // Event View + auto multi = o2::event_visualisation::MultiView::getInstance(); + multi->drawGeometryForDetector("EC0"); + + gEve->AddEvent(new TEveEventManager()); + + // Event navigation + browser->StartEmbedding(TRootBrowser::kBottom); + auto frame = new TGMainFrame(gClient->GetRoot(), 1000, 600, kVerticalFrame); + + auto h = new TGHorizontalFrame(frame); + auto b = new TGTextButton(h, "PrevEvnt", "prev()"); + h->AddFrame(b); + gEntry = new TGNumberEntry(h, 0, 5, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative, TGNumberFormat::kNELLimitMinMax, 0, 10000); + gEntry->Connect("ValueSet(Long_t)", 0, 0, "load()"); + h->AddFrame(gEntry); + b = new TGTextButton(h, "NextEvnt", "next()"); + h->AddFrame(b); + frame->AddFrame(h); + + // Chip navigation + h = new TGHorizontalFrame(frame); + b = new TGTextButton(h, "PrevChip", "prevChip()"); + h->AddFrame(b); + gChipID = new TGNumberEntry(h, 0, 5, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative, TGNumberFormat::kNELLimitMinMax, 0, gman->getNumberOfChips()); + gChipID->Connect("ValueSet(Long_t)", 0, 0, "loadChip()"); + h->AddFrame(gChipID); + b = new TGTextButton(h, "NextChip", "nextChip()"); + h->AddFrame(b); + frame->AddFrame(h); + + frame->MapSubwindows(); + frame->MapWindow(); + browser->StopEmbedding("Navigator"); + + TFile* file; + + // Data sources + if (rawdata) { + std::ifstream* rawfile = new std::ifstream(digifile.data(), std::ifstream::binary); + if (rawfile->good()) { + delete rawfile; + std::cout << "Running with raw digits...\n"; + evdata.setRawPixelReader(digifile.data()); + } else + std::cerr << "\nERROR: Cannot open file: " << digifile << "\n\n"; + } else { + file = TFile::Open(digifile.data()); + if (file && gFile->IsOpen()) { + file->Close(); + std::cout << "Running with MC digits...\n"; + evdata.setDigitPixelReader(digifile.data()); + //evdata.setDigiTree((TTree*)gFile->Get("o2sim")); + } else + std::cerr << "\nERROR: Cannot open file: " << digifile << "\n\n"; + } + + file = TFile::Open(clusfile.data()); + if (file && gFile->IsOpen()) + evdata.setClusTree((TTree*)gFile->Get("o2sim")); + else + std::cerr << "ERROR: Cannot open file: " << clusfile << "\n\n"; + + file = TFile::Open(tracfile.data()); + if (file && gFile->IsOpen()) + evdata.setTracTree((TTree*)gFile->Get("o2sim")); + else + std::cerr << "\nERROR: Cannot open file: " << tracfile << "\n\n"; + + std::cout << "\n **** Navigation over events and chips ****\n"; + std::cout << " load(event, chip) \t jump to the specified event and chip\n"; + std::cout << " next() \t\t load next event \n"; + std::cout << " prev() \t\t load previous event \n"; + std::cout << " loadChip(chip) \t jump to the specified chip within the current event \n"; + std::cout << " nextChip() \t\t load the next chip within the current event \n"; + std::cout << " prevChip() \t\t load the previous chip within the current event \n"; + + load(entry, chip); + gEve->Redraw3D(kTRUE); +} + +void load() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + + load(event, chip); +} + +void next() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + event++; + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + load(event, chip); +} + +void prev() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + event--; + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + load(event, chip); +} + +void loadChip() +{ + auto event = gEntry->GetNumberEntry()->GetIntNumber(); + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + evdata.displayData(event, chip); +} + +void loadChip(int chip) +{ + gChipID->SetIntNumber(chip); + loadChip(); +} + +void nextChip() +{ + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + chip++; + loadChip(chip); +} + +void prevChip() +{ + auto chip = gChipID->GetNumberEntry()->GetIntNumber(); + chip--; + loadChip(chip); +} + +void DisplayEventsComp() +{ + // A dummy function with the same name as this macro + init(0, 7); + gEve->GetBrowser()->GetTabRight()->SetTab(1); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/geom_list_ITS.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/geom_list_ITS.txt new file mode 100644 index 0000000000000..ec12b17e5b6f6 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/geom_list_ITS.txt @@ -0,0 +1,556 @@ +#/cave_1/barrel_1/ITSV_2 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_0 +/cave_1/barrel_1/barel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_0/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_1 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_1/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_2 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_2/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_3 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_3/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_4 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_4/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_5 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_5/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_6 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_6/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_7 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_7/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_8 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_8/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_9 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_9/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_10 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_10/ITSUHalfStave0_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_11 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer0_1/ITSUStave0_11/ITSUHalfStave0_0 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_0/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_1 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_1/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_2 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_2/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_3 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_3/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_4 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_4/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_5 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_5/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_6 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_6/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_7 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_7/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_8 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_8/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_9 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_9/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_10 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_10/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_11 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_11/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_12 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_12/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_13 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_13/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_14 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_14/ITSUHalfStave1_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_15 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer1_1/ITSUStave1_15/ITSUHalfStave1_0 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_0/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_1 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_1/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_2 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_2/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_3 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_3/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_4 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_4/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_5 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_5/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_6 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_6/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_7 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_7/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_8 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_8/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_9 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_9/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_10 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_10/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_11 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_11/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_12 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_12/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_13 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_13/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_14 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_14/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_15 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_15/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_16 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_16/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_17 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_17/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_18 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_18/ITSUHalfStave2_0 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_19 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITSULayer2_1/ITSUStave2_19/ITSUHalfStave2_0 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_0/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_0/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_1 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_1/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_1/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_2 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_2/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_2/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_3 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_3/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_3/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_4 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_4/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_4/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_5 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_5/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_5/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_6 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_6/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_6/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_7 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_7/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_7/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_8 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_8/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_8/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_9 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_9/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_9/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_10 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_10/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_10/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_11 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_11/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_11/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_12 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_12/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_12/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_13 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_13/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_13/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_14 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_14/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_14/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_15 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_15/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_15/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_16 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_16/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_16/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_17 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_17/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_17/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_18 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_18/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_18/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_19 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_19/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_19/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_20 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_20/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_20/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_21 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_21/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_21/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_22 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_22/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_22/ITSUHalfStave3_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_23 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_23/ITSUHalfStave3_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer3_1/ITSUStave3_23/ITSUHalfStave3_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_0/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_0/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_1 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_1/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_1/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_2 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_2/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_2/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_3 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_3/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_3/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_4 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_4/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_4/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_5 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_5/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_5/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_6 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_6/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_6/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_7 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_7/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_7/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_8 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_8/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_8/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_9 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_9/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_9/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_10 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_10/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_10/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_11 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_11/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_11/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_12 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_12/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_12/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_13 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_13/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_13/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_14 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_14/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_14/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_15 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_15/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_15/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_16 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_16/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_16/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_17 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_17/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_17/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_18 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_18/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_18/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_19 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_19/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_19/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_20 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_20/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_20/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_21 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_21/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_21/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_22 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_22/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_22/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_23 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_23/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_23/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_24 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_24/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_24/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_25 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_25/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_25/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_26 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_26/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_26/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_27 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_27/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_27/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_28 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_28/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_28/ITSUHalfStave4_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_29 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_29/ITSUHalfStave4_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol1_1/ITSULayer4_1/ITSUStave4_29/ITSUHalfStave4_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_0/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_0/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_1 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_1/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_1/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_2 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_2/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_2/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_3 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_3/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_3/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_4 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_4/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_4/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_5 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_5/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_5/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_6 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_6/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_6/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_7 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_7/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_7/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_8 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_8/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_8/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_9 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_9/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_9/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_10 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_10/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_10/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_11 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_11/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_11/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_12 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_12/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_12/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_13 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_13/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_13/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_14 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_14/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_14/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_15 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_15/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_15/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_16 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_16/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_16/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_17 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_17/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_17/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_18 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_18/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_18/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_19 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_19/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_19/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_20 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_20/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_20/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_21 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_21/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_21/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_22 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_22/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_22/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_23 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_23/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_23/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_24 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_24/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_24/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_25 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_25/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_25/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_26 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_26/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_26/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_27 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_27/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_27/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_28 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_28/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_28/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_29 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_29/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_29/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_30 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_30/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_30/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_31 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_31/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_31/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_32 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_32/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_32/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_33 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_33/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_33/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_34 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_34/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_34/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_35 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_35/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_35/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_36 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_36/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_36/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_37 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_37/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_37/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_38 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_38/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_38/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_39 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_39/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_39/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_40 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_40/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_40/ITSUHalfStave5_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_41 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_41/ITSUHalfStave5_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer5_1/ITSUStave5_41/ITSUHalfStave5_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1 +# +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_0/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_0/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_1 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_1/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_1/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_2 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_2/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_2/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_3 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_3/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_3/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_4 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_4/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_4/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_5 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_5/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_5/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_6 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_6/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_6/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_7 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_7/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_7/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_8 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_8/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_8/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_9 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_9/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_9/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_10 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_10/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_10/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_11 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_11/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_11/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_12 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_12/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_12/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_13 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_13/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_13/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_14 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_14/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_14/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_15 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_15/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_15/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_16 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_16/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_16/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_17 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_17/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_17/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_18 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_18/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_18/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_19 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_19/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_19/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_20 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_20/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_20/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_21 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_21/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_21/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_22 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_22/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_22/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_23 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_23/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_23/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_24 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_24/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_24/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_25 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_25/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_25/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_26 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_26/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_26/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_27 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_27/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_27/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_28 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_28/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_28/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_29 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_29/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_29/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_30 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_30/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_30/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_31 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_31/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_31/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_32 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_32/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_32/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_33 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_33/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_33/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_34 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_34/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_34/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_35 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_35/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_35/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_36 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_36/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_36/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_37 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_37/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_37/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_38 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_38/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_38/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_39 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_39/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_39/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_40 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_40/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_40/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_41 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_41/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_41/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_42 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_42/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_42/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_43 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_43/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_43/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_44 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_44/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_44/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_45 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_45/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_45/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_46 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_46/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_46/ITSUHalfStave6_1 +#/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_47 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_47/ITSUHalfStave6_0 +/cave_1/barrel_1/ITSV_2/ITSUWrapVol2_1/ITSULayer6_1/ITSUStave6_47/ITSUHalfStave6_1 + diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/simple_geom_ITS.C b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/simple_geom_ITS.C new file mode 100644 index 0000000000000..3a3152811a020 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/macros/EVE/simple_geom_ITS.C @@ -0,0 +1,107 @@ +// Based on a macro by Jeremi Niedziela on 09/02/2016. + +#if !defined(__CINT__) || defined(__MAKECINT__) + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#endif + +using namespace std; + +void AddNodes(TGeoNode* node, TEveGeoNode* parent, Int_t depth, Int_t depthmax, TObjArray* list); + +void simple_geom_EC0(std::string inputGeom = "o2sim_geometry.root") +{ + const char* currentDetector = "EC0"; + + // load geometry library + gSystem->Load("libGeom"); + + // create visualisation manager + TEveManager::Create(); + + TGeoManager::Import(inputGeom.c_str()); + + // find main node for our detector + TGeoNode* tnode = gGeoManager->GetTopNode(); + tnode->SetVisibility(kFALSE); + + TEveGeoTopNode* eve_tnode = new TEveGeoTopNode(gGeoManager, tnode); + eve_tnode->SetVisLevel(0); + + gEve->AddGlobalElement(eve_tnode); + + std::string listName("geom_list_"); + listName += currentDetector; + listName += ".txt"; + ifstream in(listName, ios::in); + std::cout << "Adding shapes from file:" << listName << '\n'; + + int lineIter = 0; + while (true) { + std::string line; + in >> line; + if (in.eof()) + break; + + if (line[0] == '#') + continue; + + TString path(line); + + if (!path.Contains("cave")) + continue; + + TObjArray* list = path.Tokenize("/"); + Int_t depth = list->GetEntries(); + AddNodes(tnode, eve_tnode, depth, depth, list); + lineIter++; + } + in.close(); + + if (lineIter == 0) { + std::cout << "File for " << currentDetector << " is empty. Skipping...\n"; + } else { + std::string fname("simple_geom_"); + fname += currentDetector; + fname += ".root"; + eve_tnode->SaveExtract(fname.c_str(), currentDetector, kTRUE); + } +} + +void AddNodes(TGeoNode* node, TEveGeoNode* parent, Int_t depth, Int_t depthmax, TObjArray* list) +{ + if (--depth <= 0) + return; + + TObjArray* nlist = node->GetVolume()->GetNodes(); // all nodes in current level + if (!nlist) + return; + + TObjString* nname = (TObjString*)list->At(depthmax - depth); // name of required node in current level + + for (int i = 0; i < nlist->GetEntries(); i++) { // loop over nodes in current level and find the one with matching name + TGeoNode* node2 = (TGeoNode*)nlist->At(i); + + if (strcmp(node2->GetName(), nname->GetString().Data()) == 0) { + TEveGeoNode* son = dynamic_cast(parent->FindChild(nname->GetName())); + if (!son) { + son = new TEveGeoNode(node2); + parent->AddElement(son); + } + AddNodes(node2, son, depth, depthmax, list); + } + } +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..5c60a1c307947 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +o2_add_library( + EC0Reconstruction + SOURCES src/ClustererTask.cxx src/CookedTracker.cxx src/RecoGeomHelper.cxx + src/FastMultEstConfig.cxx src/FastMultEst.cxx + PUBLIC_LINK_LIBRARIES O2::ECLayersBase O2::EndCapsReconstruction O2::DataFormatsITS + O2::CommonUtils) + +o2_target_root_dictionary( + EC0Reconstruction + HEADERS include/EC0Reconstruction/ClustererTask.h + include/EC0Reconstruction/CookedTracker.h + include/EC0Reconstruction/RecoGeomHelper.h + include/EC0Reconstruction/FastMultEst.h + include/EC0Reconstruction/FastMultEstConfig.h + ) diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/ClustererTask.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/ClustererTask.h new file mode 100644 index 0000000000000..f24ad0fa71c27 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/ClustererTask.h @@ -0,0 +1,92 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ClustererTask.h +/// \brief Definition of the ITS cluster finder task + +#ifndef ALICEO2_ENDCAPSLAYERS_CLUSTERERTASK +#define ALICEO2_ENDCAPSLAYERS_CLUSTERERTASK + +#include "ECLayersBase/GeometryTGeo.h" +#include "EndCapsReconstruction/ChipMappingEC0.h" +#include "EndCapsReconstruction/PixelReader.h" +#include "EndCapsReconstruction/RawPixelReader.h" +#include "EndCapsReconstruction/DigitPixelReader.h" +#include "EndCapsReconstruction/Clusterer.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/Cluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include +#include + +namespace o2 +{ +class MCCompLabel; +namespace dataformats +{ +template +class MCTruthContainer; +} + +namespace ecl +{ + +class ClustererTask +{ + using Clusterer = o2::endcaps::Clusterer; + using Cluster = o2::itsmft::Cluster; + using CompCluster = o2::itsmft::CompCluster; + using CompClusterExt = o2::itsmft::CompClusterExt; + using MCTruth = o2::dataformats::MCTruthContainer; + + public: + ClustererTask(bool useMC = true, bool raw = false); + ~ClustererTask(); + + void Init(); + Clusterer& getClusterer() { return mClusterer; } + void run(const std::string inpName, const std::string outName); + o2::endcaps::PixelReader* getReader() const { return (o2::endcaps::PixelReader*)mReader; } + + void loadDictionary(std::string fileName) { mClusterer.loadDictionary(fileName); } + + void writeTree(std::string basename, int i); + void setMaxROframe(int max) { maxROframe = max; } + int getMaxROframe() const { return maxROframe; } + + private: + int maxROframe = std::numeric_limits::max(); ///< maximal number of RO frames per a file + bool mRawDataMode = false; ///< input from raw data or MC digits + bool mUseMCTruth = true; ///< flag to use MCtruth if available + o2::endcaps::PixelReader* mReader = nullptr; ///< Pointer on the relevant Pixel reader + std::unique_ptr mReaderMC; ///< reader for MC data + std::unique_ptr> mReaderRaw; ///< reader for raw data + + const o2::endcaps::GeometryTGeo* mGeometry = nullptr; ///< ITS OR MFT upgrade geometry + Clusterer mClusterer; ///< Cluster finder + + std::vector mFullClus; //!< vector of full clusters + + std::vector mCompClus; //!< vector of compact clusters + + std::vector mROFRecVec; //!< vector of ROFRecord references + + MCTruth mClsLabels; //! MC labels + + std::vector mPatterns; + + ClassDefNV(ClustererTask, 2); +}; +} // namespace ecl +} // namespace o2 + +#endif /* ALICEO2_ENDCAPSLAYERS_CLUSTERERTASK */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/CookedTracker.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/CookedTracker.h new file mode 100644 index 0000000000000..a2db852b3654c --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/CookedTracker.h @@ -0,0 +1,199 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 CookedTracker.h +/// \brief Definition of the "Cooked Matrix" ITS tracker +/// \author iouri.belikov@cern.ch + +#ifndef ALICEO2_ENDCAPSLAYERS_COOKEDTRACKER_H +#define ALICEO2_ENDCAPSLAYERS_COOKEDTRACKER_H + +//------------------------------------------------------------------------- +// A stand-alone ITS tracker +// The pattern recongintion based on the "cooked covariance" approach +//------------------------------------------------------------------------- + +#include +#include +#include "ECLayersBase/GeometryTGeo.h" +#include "MathUtils/Cartesian3D.h" +#include "DataFormatsITSMFT/Cluster.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "ReconstructionDataFormats/Vertex.h" + +namespace o2 +{ +class MCCompLabel; +namespace dataformats +{ +template +class MCTruthContainer; +} +namespace endcaps +{ + +class TopologyDictionary; +} // namespace endcaps + +namespace ecl +{ + +class CookedTracker +{ + using Cluster = o2::itsmft::Cluster; + using CompClusterExt = o2::itsmft::CompClusterExt; + using Vertex = o2::dataformats::Vertex>; + + public: + CookedTracker(Int_t nThreads = 1); + CookedTracker(const CookedTracker&) = delete; + CookedTracker& operator=(const CookedTracker& tr) = delete; + ~CookedTracker() = default; + + void setVertices(const std::vector& vertices) + { + mVertices = &vertices; + } + + Double_t getX() const { return mX; } + Double_t getY() const { return mY; } + Double_t getZ() const { return mZ; } + Double_t getSigmaX() const { return mSigmaX; } + Double_t getSigmaY() const { return mSigmaY; } + Double_t getSigmaZ() const { return mSigmaZ; } + o2::MCCompLabel cookLabel(o2::its::TrackITSExt& t, Float_t wrong) const; + void setExternalIndices(o2::its::TrackITSExt& t) const; + Double_t getBz() const; + void setBz(Double_t bz) { mBz = bz; } + + void setNumberOfThreads(Int_t n) { mNumOfThreads = n; } + Int_t getNumberOfThreads() const { return mNumOfThreads; } + + using TrackInserter = std::function; + // These functions must be implemented + template + void process(gsl::span clusters, gsl::span::iterator& it, const o2::endcaps::TopologyDictionary& dict, U& tracks, V& clusIdx, o2::itsmft::ROFRecord& rof) + { + TrackInserter inserter = [&tracks, &clusIdx, this](const o2::its::TrackITSExt& t) -> int { + // convert internal track to output format + auto& trackNew = tracks.emplace_back(t); + int noc = t.getNumberOfClusters(); + int clEntry = clusIdx.size(); + for (int i = 0; i < noc; i++) { + const Cluster* c = this->getCluster(t.getClusterIndex(i)); + Int_t idx = c - &mClusterCache[0]; // Index of this cluster in event + clusIdx.emplace_back(idx); + } + trackNew.setClusterRefs(clEntry, noc); + return tracks.size(); + }; + process(clusters, it, dict, inserter, rof); + } + void process(gsl::span const& clusters, gsl::span::iterator& it, const o2::endcaps::TopologyDictionary& dict, TrackInserter& inserter, o2::itsmft::ROFRecord& rof); + const Cluster* getCluster(Int_t index) const; + + void setGeometry(o2::ecl::GeometryTGeo* geom); + void setMCTruthContainers(const o2::dataformats::MCTruthContainer* clsLabels, + o2::dataformats::MCTruthContainer* trkLabels) + { + mClsLabels = clsLabels; + mTrkLabels = trkLabels; + } + + void setContinuousMode(bool mode) { mContinuousMode = mode; } + bool getContinuousMode() { return mContinuousMode; } + + static void setMostProbalePt(float pt) { mMostProbablePt = pt; } + static auto getMostProbablePt() { return mMostProbablePt; } + + // internal helper classes + class ThreadData; + class Layer; + + protected: + static constexpr int kNLayers = 7; + int loadClusters(); + void unloadClusters(); + std::tuple processLoadedClusters(TrackInserter& inserter); + + std::vector trackInThread(Int_t first, Int_t last); + void makeSeeds(std::vector& seeds, Int_t first, Int_t last); + void trackSeeds(std::vector& seeds); + + Bool_t attachCluster(Int_t& volID, Int_t nl, Int_t ci, o2::its::TrackITSExt& t, const o2::its::TrackITSExt& o) const; + + void makeBackPropParam(std::vector& seeds) const; + bool makeBackPropParam(o2::its::TrackITSExt& track) const; + + private: + bool mContinuousMode = true; ///< triggered or cont. mode + const o2::ecl::GeometryTGeo* mGeom = nullptr; /// interface to geometry + const o2::dataformats::MCTruthContainer* mClsLabels = nullptr; /// Cluster MC labels + o2::dataformats::MCTruthContainer* mTrkLabels = nullptr; /// Track MC labels + std::uint32_t mFirstInFrame = 0; ///< Index of the 1st cluster of a frame (within the loaded vector of clusters) + + Int_t mNumOfThreads; ///< Number of tracking threads + + Double_t mBz; ///< Effective Z-component of the magnetic field (kG) + + const std::vector* mVertices = nullptr; + Double_t mX = 0.; ///< X-coordinate of the primary vertex + Double_t mY = 0.; ///< Y-coordinate of the primary vertex + Double_t mZ = 0.; ///< Z-coordinate of the primary vertex + + Double_t mSigmaX = 2.; ///< error of the primary vertex position in X + Double_t mSigmaY = 2.; ///< error of the primary vertex position in Y + Double_t mSigmaZ = 2.; ///< error of the primary vertex position in Z + + static Layer sLayers[kNLayers]; ///< Layers filled with clusters + std::vector mSeeds; ///< Track seeds + + std::vector mClusterCache; + + static float mMostProbablePt; ///< settable most probable pt + + ClassDefNV(CookedTracker, 1); +}; + +class CookedTracker::Layer +{ + public: + Layer(); + Layer(const Layer&) = delete; + Layer& operator=(const Layer& tr) = delete; + + void init(); + Bool_t insertCluster(const Cluster* c); + void setR(Double_t r) { mR = r; } + void unloadClusters(); + void selectClusters(std::vector& s, Float_t phi, Float_t dy, Float_t z, Float_t dz); + Int_t findClusterIndex(Float_t z) const; + Float_t getR() const { return mR; } + const Cluster* getCluster(Int_t i) const { return mClusters[i]; } + Float_t getAlphaRef(Int_t i) const { return mAlphaRef[i]; } + Float_t getClusterPhi(Int_t i) const { return mPhi[i]; } + Int_t getNumberOfClusters() const { return mClusters.size(); } + void setGeometry(o2::ecl::GeometryTGeo* geom) { mGeom = geom; } + + protected: + enum { kNSectors = 21 }; + + Float_t mR; ///< mean radius of this layer + const o2::ecl::GeometryTGeo* mGeom = nullptr; ///< interface to geometry + std::vector mClusters; ///< All clusters + std::vector mAlphaRef; ///< alpha of the reference plane + std::vector mPhi; ///< cluster phi + std::vector> mSectors[kNSectors]; ///< Cluster indices sector-by-sector +}; +} // namespace ecl +} // namespace o2 +#endif /* ALICEO2_ENDCAPSLAYERS_COOKEDTRACKER_H */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/FastMultEst.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/FastMultEst.h new file mode 100644 index 0000000000000..9b49d48aa153f --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/FastMultEst.h @@ -0,0 +1,59 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 FastMultEst.h +/// \brief Fast multiplicity estimator for ITS +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ENDCAPSLAYERS_FASTMULTEST_ +#define ALICEO2_ENDCAPSLAYERS_FASTMULTEST_ + +#include "EndCapsReconstruction/ChipMappingEC0.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "EC0Reconstruction/FastMultEstConfig.h" +#include +#include + +namespace o2 +{ +namespace ecl +{ + +struct FastMultEst { + + static constexpr int NLayers = o2::endcaps::ChipMappingEC0::NLayers; + + float mult = 0.; /// estimated signal clusters multipliciy at reference (1st?) layer + float noisePerChip = 0.; /// estimated or imposed noise per chip + float cov[3] = {0.}; /// covariance matrix of estimation + float chi2 = 0.; /// chi2 + int nLayersUsed = 0; /// number of layers actually used + std::array nClPerLayer{0}; // measured N Cl per layer + + void fillNClPerLayer(const gsl::span& clusters); + float process(const std::array ncl) + { + return FastMultEstConfig::Instance().imposeNoisePerChip > 0 ? processNoiseImposed(ncl) : processNoiseFree(ncl); + } + float processNoiseFree(const std::array ncl); + float processNoiseImposed(const std::array ncl); + float process(const gsl::span& clusters) + { + fillNClPerLayer(clusters); + return process(nClPerLayer); + } + + ClassDefNV(FastMultEst, 1); +}; + +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/FastMultEstConfig.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/FastMultEstConfig.h new file mode 100644 index 0000000000000..60fe429adb820 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/FastMultEstConfig.h @@ -0,0 +1,47 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 FastMultEstConfig.h +/// \brief Configuration parameters for ITS fast multiplicity estimator +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ENDCAPSLAYERS_FASTMULTESTCONF_H_ +#define ALICEO2_ENDCAPSLAYERS_FASTMULTESTCONF_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "EndCapsReconstruction/ChipMappingEC0.h" + +namespace o2 +{ +namespace ecl +{ +struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { + static constexpr int NLayers = o2::endcaps::ChipMappingEC0::NLayers; + + /// acceptance correction per layer (relative to 1st one) + float accCorr[NLayers] = {1.f, 0.895, 0.825, 0.803, 0.720, 0.962, 0.911}; + int firstLayer = 3; /// 1st layer to account + int lastLayer = 6; /// last layer to account + float imposeNoisePerChip = 1.e-7 * 1024 * 512; // assumed noise, free parameter if<0 + + // cuts to reject to low or too high mult events + float cutMultClusLow = 0; /// reject ROF with estimated cluster mult. below this value (no cut if <0) + float cutMultClusHigh = -1; /// reject ROF with estimated cluster mult. above this value (no cut if <0) + float cutMultVtxLow = -1; /// reject seed vertex if its multiplicity below this value (no cut if <0) + float cutMultVtxHigh = -1; /// reject seed vertex if its multiplicity above this value (no cut if <0) + + O2ParamDef(FastMultEstConfig, "fastMultConfig"); +}; + +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/RecoGeomHelper.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/RecoGeomHelper.h new file mode 100644 index 0000000000000..78ed5739c3471 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/RecoGeomHelper.h @@ -0,0 +1,152 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 RecoGeomHelper.h +/// \brief Declarations of the helper class for clusters / roadwidth matching +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ENDCAPSLAYERS_RECOGEOMHELPER_H +#define ALICEO2_ENDCAPSLAYERS_RECOGEOMHELPER_H + +#include +#include +#include +#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Utils.h" +#include "MathUtils/Primitive2D.h" +#include "CommonConstants/MathConstants.h" +#include "MathUtils/Bracket.h" +#include "EndCapsReconstruction/ChipMappingEC0.h" +#include "EndCapsBase/SegmentationAlpide.h" + +namespace o2 +{ +namespace ecl +{ +struct RecoGeomHelper { + // + using BracketF = o2::utils::Bracket; + using Vec2D = o2::utils::IntervalXY; + + enum Relation : int { Below = -1, + Inside = 0, + Above = 1 }; + + struct RecoChip { + ///< boundaries + frame data for single chip + uint16_t id = 0xffff; // global chip id + float alp = 0.f, snAlp = 0.f, csAlp = 0.f; // cos and sin of sensor alpha + float xRef = 0.f; // reference X + BracketF yRange = {1.e9, -1.e9}, zRange = {1.e9, -1.e9}; // bounding box in tracking frame + Vec2D xyEdges; + + void updateLimits(const Point3D& pnt); + void print() const; + ClassDefNV(RecoChip, 0); + }; + + struct RecoLadder { + enum Overlap : int8_t { Undefined, + Above, + NoOverlap, + Below }; + ///< group of chips at same (modulo alignment) phi, r (stave in IB, 1/4 stave in OB) + short id = 0; // assigned ladder ID within the layer + Overlap overlapWithNext = Undefined; // special flag saying if for double-hit layers this one is above, excluded or below the next one + BracketF phiRange = {o2::constants::math::TwoPI, 0.}; + BracketF zRange = {1e9, -1e9}; // Z ranges in lab frame + Vec2D xyEdges; // envelop for chip edges + float phiMean = 0., dphiH = 0.; + std::vector chips; + + Relation isPhiOutside(float phi, float toler = 0) const; + void updateLimits(const Point3D& pnt); + void init(); + void print() const; + ClassDefNV(RecoLadder, 0); + }; + + struct RecoLayer { + // navigation over ladders of single layer + int id = 0; // layer ID + int nLadders = 0; // number of ladders + int lastChipInLadder = 0; + float z2chipID = 0; // conversion factor for Z (relative to zmin) to rough chip ID + float rInv = 0.; // inverse mean radius + BracketF rRange = {1e9, 0.}; // min and max radii + BracketF zRange = {1e9, -1e9}; // min and max Z + std::vector ladders; + std::vector phi2ladder; // mapping from phi to ladderID + + const RecoLadder& getLadder(int id) const { return ladders[id % nLadders]; } + void init(); + void updateLimits(const Point3D& pnt); + void print() const; + int getLadderID(float phi) const; + int getChipID(float z) const; + ClassDefNV(RecoLayer, 0); + }; + //---------------------------<< aux classes + + std::array layers; + static constexpr int getNLayers() { return o2::endcaps::ChipMappingEC0::NLayers; } + static constexpr int getNChips() { return o2::endcaps::ChipMappingEC0::getNChips(); } + static constexpr float ladderWidth() { return o2::endcaps::SegmentationAlpide::SensorSizeRows; } + static constexpr float ladderWidthInv() { return 1. / ladderWidth(); } + + void init(); + void print() const; + + ClassDefNV(RecoGeomHelper, 0); +}; + +//_____________________________________________________________________ +inline RecoGeomHelper::Relation RecoGeomHelper::RecoLadder::isPhiOutside(float phi, float toler) const +{ + // check if phi+-toler is out of the limits of the ladder phi. + // return -1 or +1 if phi is above or below the region. If inside, return 0 + float dif = phi - phiMean, difa = fabs(dif); + if (difa < dphiH + toler) { + return RecoGeomHelper::Inside; + } + if (difa > o2::constants::math::PI) { // wraps? + difa = o2::constants::math::TwoPI - difa; + if (difa < dphiH + toler) { + return RecoGeomHelper::Inside; + } + return dif < 0 ? RecoGeomHelper::Above : RecoGeomHelper::Below; + } + return dif < 0 ? RecoGeomHelper::Below : RecoGeomHelper::Above; +} + +//_____________________________________________________________________ +inline int RecoGeomHelper::RecoLayer::getChipID(float z) const +{ + // Get chip ID within the ladder corresponding to this phi + // Note: this is an approximate method, one should check also the neighbouring ladders +/-1 + int ic = (z - zRange.min()) * z2chipID; + return ic < 0 ? 0 : (ic < lastChipInLadder ? ic : lastChipInLadder); +} + +//_____________________________________________________________________ +inline int RecoGeomHelper::RecoLayer::getLadderID(float phi) const +{ + // Get ladder ID corresponding to phi. + // Note: this is an approximate method, precise within 1/3 of average ladder width, + // one should check also the neighbouring ladders +/-1 + o2::utils::BringTo02Pi(phi); + constexpr float PI2Inv = 1.f / o2::constants::math::TwoPI; + return phi2ladder[int(phi * PI2Inv * phi2ladder.size())]; +} + +} // namespace ecl +} // namespace o2 + +#endif /* ALICEO2_ENDCAPSLAYERS_RECOGEOMHELPER_H */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialClusterer.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialClusterer.h new file mode 100644 index 0000000000000..83a1ab4e3adca --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialClusterer.h @@ -0,0 +1,69 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrivialClusterer.h +/// \brief Definition of the ITS cluster finder +#ifndef ALICEO2_ENDCAPSLAYERS_TRIVIALCLUSTERER_H +#define ALICEO2_ENDCAPSLAYERS_TRIVIALCLUSTERER_H + +#include "Rtypes.h" // for TrivialClusterer::Class, Double_t, ClassDef, etc +#include "EndCapsBase/GeometryTGeo.h" + +namespace o2 +{ +namespace endcaps +{ +class Digit; +class Cluster; +} // namespace endcaps +} // namespace o2 + +namespace o2 +{ +class MCCompLabel; +namespace dataformats +{ +template +class MCTruthContainer; +} +namespace ecl +{ +class TrivialClusterer +{ + using Digit = o2::itsmft::Digit; + using Cluster = o2::itsmft::Cluster; + using Label = o2::MCCompLabel; + + public: + TrivialClusterer(); + ~TrivialClusterer(); + + TrivialClusterer(const TrivialClusterer&) = delete; + TrivialClusterer& operator=(const TrivialClusterer&) = delete; + + /// Steer conversion of points to digits + /// @param points Container with ITS points + /// @return digits container + void process(const std::vector* digits, std::vector* clusters); + // provide the common itsmft::GeometryTGeo to access matrices + void setGeometry(const o2::endcaps::GeometryTGeo* gm) { mGeometry = gm; } + void setMCTruthContainer(o2::dataformats::MCTruthContainer* truth) + { + mClsLabels = truth; + } + + protected: + const o2::endcaps::GeometryTGeo* mGeometry = nullptr; ///< ITS OR MFT upgrade geometry + o2::dataformats::MCTruthContainer* mClsLabels = nullptr; // Cluster MC labels +}; +} // namespace ecl +} // namespace o2 + +#endif /* ALICEO2_ENDCAPSLAYERS_TRIVIALCLUSTERER_H */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialClustererTask.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialClustererTask.h new file mode 100644 index 0000000000000..2ff77490237c4 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialClustererTask.h @@ -0,0 +1,63 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrivialClustererTask.h +/// \brief Definition of the ITS cluster finder task + +#ifndef ALICEO2_ENDCAPSLAYERS_TRIVIALCLUSTERERTASK +#define ALICEO2_ENDCAPSLAYERS_TRIVIALCLUSTERERTASK + +#include "FairTask.h" + +#include "ECLayersBase/GeometryTGeo.h" +#include "EC0Reconstruction/TrivialClusterer.h" + +namespace o2 +{ +class MCCompLabel; +namespace dataformats +{ +template +class MCTruthContainer; +} + +namespace endcaps +{ +class Digit; +} + +namespace ecl +{ +class TrivialClustererTask : public FairTask +{ + using Digit = o2::itsmft::Digit; + using Cluster = o2::itsmft::Cluster; + + public: + TrivialClustererTask(Bool_t useMCTruth = kTRUE); + ~TrivialClustererTask() override; + + InitStatus Init() override; + void Exec(Option_t* option) override; + + private: + const o2::endcaps::GeometryTGeo* mGeometry = nullptr; ///< ITS geometry + TrivialClusterer mTrivialClusterer; ///< Cluster finder + + const std::vector* mDigitsArray = nullptr; ///< Array of digits + std::vector* mClustersArray = nullptr; ///< Array of clusters + o2::dataformats::MCTruthContainer* mClsLabels = nullptr; ///< MC labels + + ClassDefOverride(TrivialClustererTask, 2); +}; +} // namespace ecl +} // namespace o2 + +#endif /* ALICEO2_ENDCAPSLAYERS_TRIVIALCLUSTERERTASK */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialVertexer.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialVertexer.h new file mode 100644 index 0000000000000..04ebcf0ae0fa2 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/include/EC0Reconstruction/TrivialVertexer.h @@ -0,0 +1,69 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrivialVertexer.h +/// \brief Definition of the ITS trivial vertex finder +#ifndef ALICEO2_ENDCAPSLAYERS_TRIVIALVERTEXER_H +#define ALICEO2_ENDCAPSLAYERS_TRIVIALVERTEXER_H + +#include + +#include "Rtypes.h" // for TrivialVertexer::Class, Double_t, ClassDef, etc + +class TFile; +class TTree; +class FairMCEventHeader; + +namespace o2 +{ +namespace endcaps +{ +class Cluster; +} +} // namespace o2 + +namespace o2 +{ +class MCCompLabel; +namespace dataformats +{ +template +class MCTruthContainer; +} +namespace ecl +{ +class TrivialVertexer +{ + using Cluster = o2::itsmft::Cluster; + using Label = o2::MCCompLabel; + + public: + TrivialVertexer(); + ~TrivialVertexer(); + + TrivialVertexer(const TrivialVertexer&) = delete; + TrivialVertexer& operator=(const TrivialVertexer&) = delete; + + Bool_t openInputFile(const Char_t*); + + void process(const std::vector& clusters, std::vector>& vertices); + void setMCTruthContainer(const o2::dataformats::MCTruthContainer* truth) { mClsLabels = truth; } + + private: + const o2::dataformats::MCTruthContainer* mClsLabels = nullptr; // Cluster MC labels + + TFile* mFile = nullptr; + TTree* mTree = nullptr; + FairMCEventHeader* mHeader = nullptr; +}; +} // namespace ecl +} // namespace o2 + +#endif /* ALICEO2_ENDCAPSLAYERS_TRIVIALVERTEXER_H */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/ClustererTask.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/ClustererTask.cxx new file mode 100644 index 0000000000000..dc0bd6a1971a1 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/ClustererTask.cxx @@ -0,0 +1,185 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ClustererTask.cxx +/// \brief Implementation of the EC0 cluster finder task + +#include "DetectorsCommonDataFormats/DetID.h" +#include "EC0Reconstruction/ClustererTask.h" +#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Utils.h" +#include "FairLogger.h" +#include +#include + +using namespace o2::ecl; +using namespace o2::base; +using namespace o2::utils; + +//_____________________________________________________________________ +ClustererTask::ClustererTask(bool useMC, bool raw) : mRawDataMode(raw), + mUseMCTruth(useMC && (!raw)) +{ + LOG(INFO) << Class()->GetName() << ": MC digits mode: " << (mRawDataMode ? "OFF" : "ON") + << " | Use MCtruth: " << (mUseMCTruth ? "ON" : "OFF"); + + mClusterer.setNChips(o2::endcaps::ChipMappingEC0::getNChips()); +} + +//_____________________________________________________________________ +ClustererTask::~ClustererTask() +{ + mFullClus.clear(); + mCompClus.clear(); + mClsLabels.clear(); +} + +//_____________________________________________________________________ +void ClustererTask::Init() +{ + /// Inititializes the clusterer and connects input and output container + + if (mReader) { + return; // already initialized + } + + // create reader according to requested raw of MC mode + if (mRawDataMode) { + mReaderRaw = std::make_unique>(); + mReader = mReaderRaw.get(); + } else { // clusterizer of digits + mReaderMC = std::make_unique(); + mReader = mReaderMC.get(); + } + + GeometryTGeo* geom = GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L)); // make sure T2L matrices are loaded + mGeometry = geom; + mClusterer.setGeometry(geom); + + mClusterer.print(); + + return; +} + +//_____________________________________________________________________ +void ClustererTask::run(const std::string inpName, const std::string outName) +{ + // standalone execution + Init(); // create reader, clusterer + + if (mRawDataMode) { + + mReaderRaw->openInput(inpName); + mClusterer.process(1, *mReaderRaw.get(), &mFullClus, &mCompClus, &mPatterns, &mROFRecVec, nullptr); + + auto basename = outName.substr(0, outName.size() - sizeof("root")); + auto nFiles = int(mROFRecVec.size() / maxROframe); + int i = 0; + for (; i < nFiles; i++) { + writeTree(basename, i); + } + writeTree(basename, i); // The remainder + + } else { + + mReaderMC->openInput(inpName, o2::detectors::DetID("EC0")); + + TFile outFile(outName.data(), "new"); + if (!outFile.IsOpen()) { + LOG(FATAL) << "Failed to open output file " << outName; + } + + TTree outTree("o2sim", "EC0 Clusters"); + + auto fullClusPtr = &mFullClus; + if (mClusterer.getWantFullClusters()) { + outTree.Branch("EC0Cluster", &fullClusPtr); + } else { + LOG(INFO) << Class()->GetName() << " output of full clusters is not requested"; + } + + auto compClusPtr = &mCompClus; + outTree.Branch("EC0ClusterComp", &compClusPtr); + + auto rofRecVecPtr = &mROFRecVec; + outTree.Branch("EC0ClustersROF", &rofRecVecPtr); + + auto clsLabelsPtr = &mClsLabels; + if (mUseMCTruth && mReaderMC->getDigitsMCTruth()) { + // digit labels are provided directly to clusterer + outTree.Branch("EC0ClusterMCTruth", &clsLabelsPtr); + } else { + mUseMCTruth = false; + } + LOG(INFO) << Class()->GetName() << " | MCTruth: " << (mUseMCTruth ? "ON" : "OFF"); + + outTree.Branch("EC0ClusterPatt", &mPatterns); + + std::vector mc2rof, *mc2rofPtr = &mc2rof; + if (mUseMCTruth) { + auto mc2rofOrig = mReaderMC->getMC2ROFRecords(); + mc2rof.reserve(mc2rofOrig.size()); + for (const auto& m2r : mc2rofOrig) { // clone from the span + mc2rof.push_back(m2r); + } + outTree.Branch("EC0ClustersMC2ROF", mc2rofPtr); + } + + // loop over entries of the input tree + while (mReaderMC->readNextEntry()) { + mClusterer.process(1, *mReaderMC.get(), &mFullClus, &mCompClus, &mPatterns, &mROFRecVec, &mClsLabels); + } + + outTree.Fill(); + outTree.Write(); + } + + mClusterer.clear(); +} + +void ClustererTask::writeTree(std::string basename, int i) +{ + auto name = basename + std::to_string(i) + ".root"; + TFile outFile(name.data(), "new"); + if (!outFile.IsOpen()) { + LOG(FATAL) << "Failed to open output file " << name; + } + TTree outTree("o2sim", "EC0 Clusters"); + + auto max = (i + 1) * maxROframe; + auto lastf = (max < mROFRecVec.size()) ? mROFRecVec.begin() + max : mROFRecVec.end(); + std::vector rofRecBuffer(mROFRecVec.begin() + i * maxROframe, lastf); + std::vector* rofRecPtr = &rofRecBuffer; + outTree.Branch("EC0ClustersROF", rofRecPtr); + + auto first = rofRecBuffer[0].getFirstEntry(); + auto last = rofRecBuffer.back().getFirstEntry() + rofRecBuffer.back().getNEntries(); + + std::vector fullClusBuffer, *fullClusPtr = &fullClusBuffer; + if (mClusterer.getWantFullClusters()) { + fullClusBuffer.assign(&mFullClus[first], &mFullClus[last]); + outTree.Branch("EC0Cluster", &fullClusPtr); + } else { + LOG(INFO) << Class()->GetName() << " output of full clusters is not requested"; + } + + std::vector compClusBuffer, *compClusPtr = &compClusBuffer; + compClusBuffer.assign(&mCompClus[first], &mCompClus[last]); + outTree.Branch("EC0ClusterComp", &compClusPtr); + outTree.Branch("EC0ClusterPatt", &mPatterns); + + for (auto& rof : rofRecBuffer) { + rof.setFirstEntry(rof.getFirstEntry() - first); + } + + outTree.Fill(); + outTree.Write(); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/CookedTracker.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/CookedTracker.cxx new file mode 100644 index 0000000000000..db43ac21c14e3 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/CookedTracker.cxx @@ -0,0 +1,824 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 CookedTracker.cxx +/// \brief Implementation of the "Cooked Matrix" ITS tracker +/// \author iouri.belikov@cern.ch + +//------------------------------------------------------------------------- +// A stand-alone ITS tracker +// The pattern recongintion based on the "cooked covariance" approach +//------------------------------------------------------------------------- +#include +#include +#include + +#include +#include + +#include "FairLogger.h" + +#include "CommonConstants/MathConstants.h" +#include "DetectorsBase/Propagator.h" +#include "Field/MagneticField.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsEndCaps/TopologyDictionary.h" +#include "EC0Reconstruction/CookedTracker.h" +#include "MathUtils/Utils.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::ecl; +using namespace o2::endcaps; +using namespace o2::constants::math; +using namespace o2::utils; +using o2::field::MagneticField; +using Label = o2::MCCompLabel; +using Point3Df = Point3D; + +//************************************************ +// Constants hardcoded for the moment: +//************************************************ +// seed "windows" in z and phi: makeSeeds +const Float_t kzWin = 0.33; +const Float_t kminPt = 0.05; +// Maximal accepted impact parameters for the seeds +const Float_t kmaxDCAxy = 3.; +const Float_t kmaxDCAz = 3.; +// Layers for the seeding +const Int_t kSeedingLayer1 = 6, kSeedingLayer2 = 4, kSeedingLayer3 = 5; +// Space point resolution +const Float_t kSigma2 = 0.0005 * 0.0005; +// Max accepted chi2 +const Float_t kmaxChi2PerCluster = 20.; +const Float_t kmaxChi2PerTrack = 30.; +// Tracking "road" from layer to layer +const Float_t kRoadY = 0.2; +const Float_t kRoadZ = 0.3; +// Minimal number of attached clusters +const Int_t kminNumberOfClusters = 4; + +const float kPI = 3.14159f; +const float k2PI = 2 * kPI; + +//************************************************ +// TODO: +//************************************************ +// Seeding: +// Precalculate cylidnrical (r,phi) for the clusters; +// use exact r's for the clusters + +CookedTracker::Layer CookedTracker::sLayers[CookedTracker::kNLayers]; +float CookedTracker::mMostProbablePt = o2::track::kMostProbablePt; + +CookedTracker::CookedTracker(Int_t n) : mNumOfThreads(n), mBz(0.) +{ + //-------------------------------------------------------------------- + // This default constructor needs to be provided + //-------------------------------------------------------------------- + const Double_t klRadius[7] = {2.34, 3.15, 3.93, 19.61, 24.55, 34.39, 39.34}; // tdr6 + + for (Int_t i = 0; i < kNLayers; i++) + sLayers[i].setR(klRadius[i]); +} + +//__________________________________________________________________________ +Label CookedTracker::cookLabel(o2::its::TrackITSExt& t, Float_t wrong) const +{ + //-------------------------------------------------------------------- + // This function "cooks" a track label. + // A label<0 indicates that some of the clusters are wrongly assigned. + //-------------------------------------------------------------------- + Int_t noc = t.getNumberOfClusters(); + std::map labelOccurence; + + for (int i = noc; i--;) { + const Cluster* c = getCluster(t.getClusterIndex(i)); + Int_t idx = c - &mClusterCache[0] + mFirstInFrame; // Index of this cluster in event + auto labels = mClsLabels->getLabels(idx); + + for (auto lab : labels) { // check all labels of the cluster + if (lab.isEmpty()) + break; // all following labels will be empty also + // was this label already accounted for ? + labelOccurence[lab]++; + } + } + Label lab; + Int_t maxL = 0; // find most encountered label + for (auto [label, count] : labelOccurence) { + if (count <= maxL) + continue; + maxL = count; + lab = label; + } + + if ((1. - Float_t(maxL) / noc) > wrong) { + // change the track ID to negative + lab.setFakeFlag(); + } + // t.SetFakeRatio((1.- Float_t(maxL)/noc)); + return lab; +} + +Double_t CookedTracker::getBz() const +{ + return mBz; +} + +static Double_t f1(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) +{ + //----------------------------------------------------------------- + // Initial approximation of the track curvature + //----------------------------------------------------------------- + Double_t d = (x2 - x1) * (y3 - y2) - (x3 - x2) * (y2 - y1); + Double_t a = + 0.5 * ((y3 - y2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) - (y2 - y1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2)); + Double_t b = + 0.5 * ((x2 - x1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2) - (x3 - x2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1)); + + Double_t xr = TMath::Abs(d / (d * x1 - a)), yr = TMath::Abs(d / (d * y1 - b)); + + Double_t crv = xr * yr / sqrt(xr * xr + yr * yr); + if (d > 0) + crv = -crv; + + return crv; +} + +static Double_t f2(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) +{ + //----------------------------------------------------------------- + // Initial approximation of the x-coordinate of the center of curvature + //----------------------------------------------------------------- + + Double_t k1 = (y2 - y1) / (x2 - x1), k2 = (y3 - y2) / (x3 - x2); + Double_t x0 = 0.5 * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); + + return x0; +} + +static Double_t f3(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t z1, Double_t z2) +{ + //----------------------------------------------------------------- + // Initial approximation of the tangent of the track dip angle + //----------------------------------------------------------------- + return (z1 - z2) / sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); +} + +static o2::its::TrackITSExt cookSeed(const Point3Df& r1, Point3Df& r2, const Point3Df& tr3, float rad2, float rad3, float_t alpha, float_t bz) +// const Float_t r1[4], const Float_t r2[4], const Float_t tr3[4], Double_t alpha, Double_t bz) +{ + //-------------------------------------------------------------------- + // This is the main cooking function. + // Creates seed parameters out of provided clusters. + //-------------------------------------------------------------------- + + Double_t ca = TMath::Cos(alpha), sa = TMath::Sin(alpha); + Double_t x1 = r1.X() * ca + r1.Y() * sa, y1 = -r1.X() * sa + r1.Y() * ca, z1 = r1.Z(); + Double_t x2 = r2.X() * ca + r2.Y() * sa, y2 = -r2.X() * sa + r2.Y() * ca, z2 = r2.Z(); + Double_t x3 = tr3.X(), y3 = tr3.Y(), z3 = tr3.Z(); + + std::array par; + par[0] = y3; + par[1] = z3; + Double_t crv = f1(x1, y1, x2, y2, x3, y3); // curvature + Double_t x0 = f2(x1, y1, x2, y2, x3, y3); // x-coordinate of the center + Double_t tgl12 = f3(x1, y1, x2, y2, z1, z2); + Double_t tgl23 = f3(x2, y2, x3, y3, z2, z3); + + Double_t sf = crv * (x3 - x0); // FIXME: sf must never be >= kAlmost1 + par[2] = sf; + + par[3] = 0.5 * (tgl12 + tgl23); + par[4] = (TMath::Abs(bz) < Almost0) ? 1 / CookedTracker::getMostProbablePt() : crv / (bz * B2C); + + std::array cov; + /* + for (Int_t i=0; i<15; i++) cov[i]=0.; + cov[0] =kSigma2*10; + cov[2] =kSigma2*10; + cov[5] =0.007*0.007*10; //FIXME all these lines + cov[9] =0.007*0.007*10; + cov[14]=0.1*0.1*10; + */ + const Double_t dlt = 0.0005; + Double_t fy = 1. / (rad2 - rad3); + Double_t tz = fy; + const auto big = sqrt(o2::constants::math::VeryBig); + auto cy = big; + if (TMath::Abs(bz) >= Almost0) { + auto tmp = dlt * bz * B2C; + cy = (f1(x1, y1, x2, y2 + dlt, x3, y3) - crv) / tmp; + cy *= 20; // FIXME: MS contribution to the cov[14] + } + Double_t s2 = kSigma2; + + cov[0] = s2; + cov[1] = 0.; + cov[2] = s2; + cov[3] = s2 * fy; + cov[4] = 0.; + cov[5] = s2 * fy * fy; + cov[6] = 0.; + cov[7] = s2 * tz; + cov[8] = 0.; + cov[9] = s2 * tz * tz; + cov[10] = s2 * cy; + cov[11] = 0.; + cov[12] = s2 * fy * cy; + cov[13] = 0.; + cov[14] = s2 * cy * cy; + + return o2::its::TrackITSExt(x3, alpha, par, cov); +} + +void CookedTracker::makeSeeds(std::vector& seeds, Int_t first, Int_t last) +{ + //-------------------------------------------------------------------- + // This is the main pattern recongition function. + // Creates seeds out of two clusters and another point. + //-------------------------------------------------------------------- + const float zv = getZ(); + + Layer& layer1 = sLayers[kSeedingLayer1]; + Layer& layer2 = sLayers[kSeedingLayer2]; + Layer& layer3 = sLayers[kSeedingLayer3]; + + auto bz = getBz(); + const Double_t maxC = (TMath::Abs(bz) < Almost0) ? 0.03 : TMath::Abs(bz * B2C / kminPt); + const Double_t kpWin = TMath::ASin(0.5 * maxC * layer1.getR()) - TMath::ASin(0.5 * maxC * layer2.getR()); + const float kpWin100 = kpWin / 100; + + // Int_t nClusters1 = layer1.getNumberOfClusters(); + Int_t nClusters2 = layer2.getNumberOfClusters(); + Int_t nClusters3 = layer3.getNumberOfClusters(); + + for (Int_t n1 = first; n1 < last; n1++) { + const Cluster* c1 = layer1.getCluster(n1); + // + //auto lab = (mClsLabels->getLabels(c1 - &mClusterCache[0] + mFirstInFrame))[0]; + // + auto xyz1 = c1->getXYZGloRot(*mGeom); + auto z1 = xyz1.Z(); + auto r1 = xyz1.rho(); + + auto phi1 = layer1.getClusterPhi(n1); + auto tgl = std::abs((z1 - zv) / r1); + + auto zr2 = zv + layer2.getR() / r1 * (z1 - zv); + auto phir2 = phi1; + auto dz2 = kzWin * (1 + 2 * tgl); + + std::vector selected2; + float dy2 = kpWin * layer2.getR(); + layer2.selectClusters(selected2, phir2, dy2, zr2, dz2); + for (auto n2 : selected2) { + const Cluster* c2 = layer2.getCluster(n2); + // + //if ((mClsLabels->getLabels(c2 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; + // + auto xyz2 = c2->getXYZGloRot(*mGeom); + auto z2 = xyz2.Z(); + auto r2 = xyz2.rho(); + + Float_t hcrv = 0.5 * f1(xyz1.X(), xyz1.Y(), xyz2.X(), xyz2.Y(), getX(), getY()); + + auto zr3 = z1 + (layer3.getR() - r1) / (r2 - r1) * (z2 - z1); + auto phir3 = phi1 + hcrv * (layer3.getR() - r1); + auto dz3 = 0.5f * dz2; + + std::vector selected3; + float dy3 = kpWin100 * layer3.getR(); + layer3.selectClusters(selected3, phir3, dy3, zr3, dz3); + for (auto n3 : selected3) { + const Cluster* c3 = layer3.getCluster(n3); + // + //if ((mClsLabels->getLabels(c3 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; + // + auto xyz3 = c3->getXYZGloRot(*mGeom); + auto z3 = xyz3.Z(); + auto r3 = xyz3.rho(); + + zr3 = z1 + (r3 - r1) / (r2 - r1) * (z2 - z1); + if (std::abs(z3 - zr3) > 0.2 * dz3) + continue; + + const Point3Df& txyz2 = c2->getXYZ(); // tracking coordinates + + o2::its::TrackITSExt seed = cookSeed(xyz1, xyz3, txyz2, layer2.getR(), layer3.getR(), layer2.getAlphaRef(n2), getBz()); + + float ip[2]; + seed.getImpactParams(getX(), getY(), getZ(), getBz(), ip); + if (TMath::Abs(ip[0]) > kmaxDCAxy) + continue; + if (TMath::Abs(ip[1]) > kmaxDCAz) + continue; + { + Double_t xx0 = 0.008; // Rough layer thickness + Double_t radl = 9.36; // Radiation length of Si [cm] + Double_t rho = 2.33; // Density of Si [g/cm^3] + if (!seed.correctForMaterial(xx0, xx0 * radl * rho, kTRUE)) + continue; + } + seed.setClusterIndex(kSeedingLayer1, n1); + seed.setClusterIndex(kSeedingLayer3, n3); + seed.setClusterIndex(kSeedingLayer2, n2); + seeds.push_back(seed); + } + } + } + /* + for (Int_t n1 = 0; n1 < nClusters1; n1++) { + Cluster* c1 = layer1.getCluster(n1); + ((Cluster*)c1)->goToFrameTrk(); + } + for (Int_t n2 = 0; n2 < nClusters2; n2++) { + Cluster* c2 = layer2.getCluster(n2); + ((Cluster*)c2)->goToFrameTrk(); + } + for (Int_t n3 = 0; n3 < nClusters3; n3++) { + Cluster* c3 = layer3.getCluster(n3); + ((Cluster*)c3)->goToFrameTrk(); + } + */ +} + +void CookedTracker::trackSeeds(std::vector& seeds) +{ + //-------------------------------------------------------------------- + // Loop over a subset of track seeds + //-------------------------------------------------------------------- + std::vector used[kSeedingLayer2]; + std::vector selec[kSeedingLayer2]; + for (Int_t l = kSeedingLayer2 - 1; l >= 0; l--) { + Int_t n = sLayers[l].getNumberOfClusters(); + used[l].resize(n, false); + selec[l].reserve(n / 100); + } + + for (auto& track : seeds) { + auto x = track.getX(); + auto y = track.getY(); + Float_t phi = track.getAlpha() + TMath::ATan2(y, x); + BringTo02Pi(phi); + + auto z = track.getZ(); + auto crv = track.getCurvature(getBz()); + auto tgl = track.getTgl(); + Float_t r1 = sLayers[kSeedingLayer2].getR(); + + for (Int_t l = kSeedingLayer2 - 1; l >= 0; l--) { + Float_t r2 = sLayers[l].getR(); + if (TMath::Abs(crv) < Almost0) { + z += tgl * (r2 - r1); + } else { + phi += 0.5 * crv * (r2 - r1); + z += tgl / (0.5 * crv) * (TMath::ASin(0.5 * crv * r2) - TMath::ASin(0.5 * crv * r1)); + } + selec[l].clear(); + sLayers[l].selectClusters(selec[l], phi, kRoadY, z, kRoadZ * (1 + 2 * std::abs(tgl))); + r1 = r2; + } + + o2::its::TrackITSExt best(track); + + Int_t volID = -1; + Int_t ci = -1; + o2::its::TrackITSExt t3(track); + for (auto& ci3 : selec[3]) { + if (used[3][ci3]) + continue; + if (!attachCluster(volID, 3, ci3, t3, track)) + continue; + + o2::its::TrackITSExt t2(t3); + for (auto& ci2 : selec[2]) { + if (used[2][ci2]) + continue; + if (!attachCluster(volID, 2, ci2, t2, t3)) + continue; + + o2::its::TrackITSExt t1(t2); + for (auto& ci1 : selec[1]) { + if (used[1][ci1]) + continue; + if (!attachCluster(volID, 1, ci1, t1, t2)) + continue; + + o2::its::TrackITSExt t0(t1); + for (auto& ci0 : selec[0]) { + if (used[0][ci0]) + continue; + if (!attachCluster(volID, 0, ci0, t0, t1)) + continue; + if (t0.isBetter(best, kmaxChi2PerTrack)) { + best = t0; + } + volID = -1; + } + } + } + } + + if (best.getNumberOfClusters() >= kminNumberOfClusters) { + Int_t noc = best.getNumberOfClusters(); + for (Int_t ic = 3; ic < noc; ic++) { + Int_t index = best.getClusterIndex(ic); + Int_t l = (index & 0xf0000000) >> 28, c = (index & 0x0fffffff); + used[l][c] = true; + } + } + track = best; + } +} + +std::vector CookedTracker::trackInThread(Int_t first, Int_t last) +{ + //-------------------------------------------------------------------- + // This function is passed to a tracking thread + //-------------------------------------------------------------------- + std::vector seeds; + seeds.reserve(last - first + 1); + + for (const auto& vtx : *mVertices) { + mX = vtx.getX(); + mY = vtx.getY(); + mZ = vtx.getZ(); + makeSeeds(seeds, first, last); + } + + std::sort(seeds.begin(), seeds.end()); + + trackSeeds(seeds); + + makeBackPropParam(seeds); + + return seeds; +} + +void CookedTracker::process(gsl::span const& clusters, + gsl::span::iterator& pattIt, + const TopologyDictionary& dict, + TrackInserter& inserter, + o2::itsmft::ROFRecord& rof) +{ + //-------------------------------------------------------------------- + // This is the main tracking function + //-------------------------------------------------------------------- + if (mVertices == nullptr || mVertices->empty()) { + LOG(INFO) << "Not a single primary vertex provided. Skipping...\n"; + return; + } + LOG(INFO) << "\n CookedTracker::process(), number of threads: " << mNumOfThreads << '\n'; + + auto start = std::chrono::system_clock::now(); + + mFirstInFrame = rof.getFirstEntry(); + + mClusterCache.reserve(rof.getNEntries()); + auto clusters_in_frame = rof.getROFData(clusters); + for (const auto& comp : clusters_in_frame) { + + auto pattID = comp.getPatternID(); + Point3D locXYZ; + float sigmaY2 = 0.0015 * 0.0015, sigmaZ2 = sigmaY2, sigmaYZ = 0; //Dummy COG errors (about half pixel size) + if (pattID != itsmft::CompCluster::InvalidPatternID) { + sigmaY2 = dict.getErr2X(pattID); + sigmaZ2 = dict.getErr2Z(pattID); + if (!dict.isGroup(pattID)) { + locXYZ = dict.getClusterCoordinates(comp); + } else { + ClusterPattern patt(pattIt); + locXYZ = dict.getClusterCoordinates(comp, patt); + } + } else { + ClusterPattern patt(pattIt); + locXYZ = dict.getClusterCoordinates(comp, patt); + } + auto sensorID = comp.getSensorID(); + // Inverse transformation to the local --> tracking + auto trkXYZ = mGeom->getMatrixT2L(sensorID) ^ locXYZ; + + Cluster c; + c.setSensorID(sensorID); + c.setPos(trkXYZ); + c.setErrors(sigmaY2, sigmaZ2, 0.f); + mClusterCache.push_back(c); + } + + auto nClFrame = loadClusters(); + + auto end = std::chrono::system_clock::now(); + std::chrono::duration diff = end - start; + LOG(INFO) << "Loading clusters: " << nClFrame << " in a single frame : " << diff.count() << " s"; + + start = end; + + auto [first, number] = processLoadedClusters(inserter); + rof.setFirstEntry(first); + rof.setNEntries(number); + + unloadClusters(); + end = std::chrono::system_clock::now(); + diff = end - start; + LOG(INFO) << "Processing time/clusters for single frame : " << diff.count() << " / " << nClFrame << " s"; + + start = end; +} + +std::tuple CookedTracker::processLoadedClusters(TrackInserter& inserter) +{ + //-------------------------------------------------------------------- + // This is the main tracking function for single frame, it is assumed that only clusters + // which may contribute to this frame is loaded + //-------------------------------------------------------------------- + Int_t numOfClusters = sLayers[kSeedingLayer1].getNumberOfClusters(); + if (!numOfClusters) { + return {0, 0}; + } + + std::vector>> futures(mNumOfThreads); + std::vector> seedArray(mNumOfThreads); + + for (Int_t t = 0, first = 0; t < mNumOfThreads; t++) { + Int_t rem = t < (numOfClusters % mNumOfThreads) ? 1 : 0; + Int_t last = first + (numOfClusters / mNumOfThreads) + rem; + futures[t] = std::async(std::launch::async, &CookedTracker::trackInThread, this, first, last); + first = last; + } + Int_t nSeeds = 0, ngood = 0; + int nAllTracks = 0, nTracks = 0; + for (Int_t t = 0; t < mNumOfThreads; t++) { + seedArray[t] = futures[t].get(); + nSeeds += seedArray[t].size(); + for (auto& track : seedArray[t]) { + if (track.getNumberOfClusters() < kminNumberOfClusters) + continue; + nAllTracks = inserter(track); + nTracks++; + if (mTrkLabels) { + Label label = cookLabel(track, 0.); // For comparison only + if (label.getTrackID() >= 0) { + ngood++; + } + // the inserter returns the size of the track vector, the index of the last + // inserted track is thus n - 1 + mTrkLabels->addElement(nAllTracks - 1, label); + } + } + } + + if (nSeeds) { + LOG(INFO) << "CookedTracker::processLoadedClusters(), good_tracks:/seeds: " << ngood << '/' << nSeeds << "-> " + << Float_t(ngood) / nSeeds << '\n'; + } + // returning index of the first track and the number of add tracks + // inserted in this call + return {nAllTracks - nTracks, nTracks}; +} + +//____________________________________________________________ +void CookedTracker::makeBackPropParam(std::vector& seeds) const +{ + // refit in backward direction + for (auto& track : seeds) { + if (track.getNumberOfClusters() < kminNumberOfClusters) { + continue; + } + makeBackPropParam(track); + } +} + +//____________________________________________________________ +bool CookedTracker::makeBackPropParam(o2::its::TrackITSExt& track) const +{ + // refit in backward direction + auto backProp = track.getParamOut(); + backProp = track; + backProp.resetCovariance(); + auto propagator = o2::base::Propagator::Instance(); + + Int_t noc = track.getNumberOfClusters(); + for (int ic = noc; ic--;) { // cluster indices are stored in inward direction + Int_t index = track.getClusterIndex(ic); + const Cluster* c = getCluster(index); + float alpha = mGeom->getSensorRefAlpha(c->getSensorID()); + if (!backProp.rotate(alpha)) { + return false; + } + if (!propagator->PropagateToXBxByBz(backProp, c->getX())) { + return false; + } + if (!backProp.update(static_cast&>(*c))) { + return false; + } + } + track.getParamOut() = backProp; + return true; +} + +int CookedTracker::loadClusters() +{ + //-------------------------------------------------------------------- + // This function reads the ITSU clusters from the tree, + // sort them, distribute over the internal tracker arrays, etc + //-------------------------------------------------------------------- + + if (mClusterCache.empty()) + return 0; + + for (const auto& c : mClusterCache) { + Int_t layer = mGeom->getLayer(c.getSensorID()); + sLayers[layer].insertCluster(&c); + } + + std::vector> fut; + for (Int_t l = 0; l < kNLayers; l += mNumOfThreads) { + for (Int_t t = 0; t < mNumOfThreads; t++) { + if (l + t >= kNLayers) + break; + auto f = std::async(std::launch::async, &CookedTracker::Layer::init, sLayers + (l + t)); + fut.push_back(std::move(f)); + } + for (Int_t t = 0; t < fut.size(); t++) + fut[t].wait(); + } + + return mClusterCache.size(); +} + +void CookedTracker::unloadClusters() +{ + //-------------------------------------------------------------------- + // This function unloads ITSU clusters from the RAM + //-------------------------------------------------------------------- + mClusterCache.clear(); + for (Int_t i = 0; i < kNLayers; i++) + sLayers[i].unloadClusters(); +} + +const o2::itsmft::Cluster* CookedTracker::getCluster(Int_t index) const +{ + //-------------------------------------------------------------------- + // Return pointer to a given cluster + //-------------------------------------------------------------------- + Int_t l = (index & 0xf0000000) >> 28; + Int_t c = (index & 0x0fffffff) >> 00; + return sLayers[l].getCluster(c); +} + +CookedTracker::Layer::Layer() : mR(0) +{ + //-------------------------------------------------------------------- + // This default constructor needs to be provided + //-------------------------------------------------------------------- +} + +void CookedTracker::Layer::init() +{ + //-------------------------------------------------------------------- + // Sort clusters and cache their reference plane info in a thread + //-------------------------------------------------------------------- + std::sort(std::begin(mClusters), std::end(mClusters), + [](const Cluster* c1, const Cluster* c2) { return (c1->getZ() < c2->getZ()); }); + + Double_t r = 0.; + Int_t m = mClusters.size(); + for (Int_t i = 0; i < m; i++) { + const Cluster* c = mClusters[i]; + // Float_t xRef, aRef; + // mGeom->getSensorXAlphaRefPlane(c->getSensorID(),xRef, aRef); + mAlphaRef.push_back(mGeom->getSensorRefAlpha(c->getSensorID())); + auto xyz = c->getXYZGloRot(*mGeom); + r += xyz.rho(); + Float_t phi = xyz.Phi(); + BringTo02Pi(phi); + mPhi.push_back(phi); + Int_t s = phi * kNSectors / k2PI; + mSectors[s < kNSectors ? s : kNSectors - 1].emplace_back(i, c->getZ()); + } + + if (m) + mR = r / m; +} + +void CookedTracker::Layer::unloadClusters() +{ + //-------------------------------------------------------------------- + // Unload clusters from this layer + //-------------------------------------------------------------------- + mClusters.clear(); + mAlphaRef.clear(); + mPhi.clear(); + for (Int_t s = 0; s < kNSectors; s++) + mSectors[s].clear(); +} + +Bool_t CookedTracker::Layer::insertCluster(const Cluster* c) +{ + //-------------------------------------------------------------------- + // This function inserts a cluster to this layer + //-------------------------------------------------------------------- + mClusters.push_back(c); + return kTRUE; +} + +Int_t CookedTracker::Layer::findClusterIndex(Float_t z) const +{ + //-------------------------------------------------------------------- + // This function returns the index of the first cluster with its fZ >= "z". + //-------------------------------------------------------------------- + auto found = std::upper_bound(std::begin(mClusters), std::end(mClusters), z, + [](Float_t zc, const Cluster* c) { return (zc < c->getZ()); }); + return found - std::begin(mClusters); +} + +void CookedTracker::Layer::selectClusters(std::vector& selec, Float_t phi, Float_t dy, Float_t z, Float_t dz) +{ + //-------------------------------------------------------------------- + // This function selects clusters within the "road" + //-------------------------------------------------------------------- + Float_t zMin = z - dz; + Float_t zMax = z + dz; + + BringTo02Pi(phi); + + Float_t dphi = dy / mR; + + int smin = (phi - dphi) / k2PI * kNSectors; + int ds = (phi + dphi) / k2PI * kNSectors - smin + 1; + + smin = (smin + kNSectors) % kNSectors; + + for (int is = 0; is < ds; is++) { + Int_t s = (smin + is) % kNSectors; + + auto cmp = [](Float_t zc, std::pair p) { return (zc < p.second); }; + auto imin = std::upper_bound(std::begin(mSectors[s]), std::end(mSectors[s]), zMin, cmp); + auto imax = std::upper_bound(imin, std::end(mSectors[s]), zMax, cmp); + for (; imin != imax; imin++) { + auto [i, zz] = *imin; + auto cdphi = std::abs(mPhi[i] - phi); + if (cdphi > dphi) { + if (cdphi > kPI) { + cdphi = k2PI - cdphi; + } + if (cdphi > dphi) + continue; // check in Phi + } + selec.push_back(i); + } + } +} + +Bool_t CookedTracker::attachCluster(Int_t& volID, Int_t nl, Int_t ci, o2::its::TrackITSExt& t, const o2::its::TrackITSExt& o) const +{ + //-------------------------------------------------------------------- + // Try to attach a clusters with index ci to running track hypothesis + //-------------------------------------------------------------------- + Layer& layer = sLayers[nl]; + const Cluster* c = layer.getCluster(ci); + + Int_t vid = c->getSensorID(); + + if (vid != volID) { + volID = vid; + t = o; + Double_t alpha = layer.getAlphaRef(ci); + if (!t.propagate(alpha, c->getX(), getBz())) + return kFALSE; + } + + Double_t chi2 = t.getPredictedChi2(*c); + + if (chi2 > kmaxChi2PerCluster) { + return kFALSE; + } + + if (!t.update(*c, chi2)) { + return kFALSE; + } + t.setClusterIndex(nl, ci); + + Double_t xx0 = (nl > 2) ? 0.008 : 0.003; // Rough layer thickness + Double_t x0 = 9.36; // Radiation length of Si [cm] + Double_t rho = 2.33; // Density of Si [g/cm^3] + t.correctForMaterial(xx0, xx0 * x0 * rho, kTRUE); + return kTRUE; +} + +void CookedTracker::setGeometry(o2::ecl::GeometryTGeo* geom) +{ + /// attach geometry interface + mGeom = geom; + for (Int_t i = 0; i < kNLayers; i++) + sLayers[i].setGeometry(geom); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/EC0ReconstructionLinkDef.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/EC0ReconstructionLinkDef.h new file mode 100644 index 0000000000000..2b2a5b7ec92c8 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/EC0ReconstructionLinkDef.h @@ -0,0 +1,26 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::ecl::ClustererTask + ; +#pragma link C++ class o2::ecl::CookedTracker + ; + +#pragma link C++ class o2::ecl::RecoGeomHelper + ; + +#pragma link C++ class o2::ecl::FastMultEst + ; +#pragma link C++ class o2::ecl::FastMultEstConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::ecl::FastMultEstConfig> + ; + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/FastMultEst.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/FastMultEst.cxx new file mode 100644 index 0000000000000..1994b0e1031d5 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/FastMultEst.cxx @@ -0,0 +1,129 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 FastMultEst.h +/// \brief Fast multiplicity estimator for ITS +/// \author ruben.shahoyan@cern.ch + +#include "EC0Reconstruction/FastMultEst.h" +#include + +using namespace o2::ecl; + +///______________________________________________________ +/// find multiplicity for given set of clusters +void FastMultEst::fillNClPerLayer(const gsl::span& clusters) +{ + int lr = FastMultEst::NLayers - 1, nchAcc = o2::endcaps::ChipMappingEC0::getNChips() - o2::endcaps::ChipMappingEC0::getNChipsPerLr(lr); + std::memset(&nClPerLayer[0], 0, sizeof(int) * FastMultEst::NLayers); + for (int i = clusters.size(); i--;) { // profit from clusters being ordered in chip increasing order + while (clusters[i].getSensorID() < nchAcc) { + assert(lr >= 0); + nchAcc -= o2::endcaps::ChipMappingEC0::getNChipsPerLr(--lr); + } + nClPerLayer[lr]++; + } +} + +///______________________________________________________ +/// find multiplicity for given number of clusters per layer +float FastMultEst::processNoiseFree(const std::array ncl) +{ + // we assume that on the used layers the observed number of clusters is defined by the + // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*mAccCorr + const auto& conf = FastMultEstConfig::Instance(); + + float mat[3] = {0}, b[2] = {0}; + nLayersUsed = 0; + for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { + if (ncl[il] > 0) { + int nch = o2::endcaps::ChipMappingEC0::getNChipsPerLr(il); + float err2i = 1. / ncl[il]; + float m2n = nch * err2i; + mat[0] += err2i * conf.accCorr[il] * conf.accCorr[il]; + mat[2] += nch * m2n; + mat[1] += conf.accCorr[il] * m2n; // non-diagonal element + b[0] += conf.accCorr[il]; + b[1] += nch; + nLayersUsed++; + } + } + mult = noisePerChip = chi2 = -1; + float det = mat[0] * mat[2] - mat[1] * mat[1]; + if (nLayersUsed < 2 || std::abs(det) < 1e-15) { + return -1; + } + float detI = 1. / det; + mult = detI * (b[0] * mat[2] - b[1] * mat[1]); + noisePerChip = detI * (b[1] * mat[0] - b[0] * mat[1]); + cov[0] = mat[2] * detI; + cov[2] = mat[0] * detI; + cov[1] = -mat[1] * detI; + chi2 = 0.; + for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { + if (ncl[il] > 0) { + int nch = o2::endcaps::ChipMappingEC0::getNChipsPerLr(il); + float err2i = 1. / ncl[il]; + float diff = mult * conf.accCorr[il] + nch * noisePerChip - ncl[il]; + chi2 += diff * diff / ncl[il]; + } + } + chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; + return mult; +} + +///______________________________________________________ +/// find multiplicity for given number of clusters per layer with mean noise imposed +float FastMultEst::processNoiseImposed(const std::array ncl) +{ + // we assume that on the used layers the observed number of clusters is defined by the + // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*conf.accCorr + // + // minimize the form sum_lr (noise_i - mu nchips_i)^2 / (mu nchips_i) + lambda_i * (noise_i + mult*acc_i - ncl_i) + // whith noise_i being estimate of the noise clusters in nchips_i of layer i, mu is the mean noise per chip, + // mult is the number of signal clusters on the ref. (1st) layer and the acc_i is the acceptance of layer i wrt 1st. + // The lambda_i is hust a Lagrange multiplier. + + const auto& conf = FastMultEstConfig::Instance(); + float w2sum = 0., wnsum = 0., wsum = 0.; + nLayersUsed = 0; + for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { + if (ncl[il] > 0) { + float nchInv = 1. / o2::endcaps::ChipMappingEC0::getNChipsPerLr(il); + w2sum += conf.accCorr[il] * conf.accCorr[il] * nchInv; + wnsum += ncl[il] * nchInv * conf.accCorr[il]; + wsum += conf.accCorr[il]; + nLayersUsed++; + } + } + mult = 0; + chi2 = -1; + noisePerChip = conf.imposeNoisePerChip; + if (nLayersUsed < 1) { + return -1; + } + auto w2sumI = 1. / w2sum; + mult = (wnsum - noisePerChip * wsum) * w2sumI; + cov[0] = wnsum * w2sumI; + cov[2] = 0.; + cov[1] = 0.; + + chi2 = 0.; + for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { + if (ncl[il] > 0) { + int nch = o2::endcaps::ChipMappingEC0::getNChipsPerLr(il); + float noise = ncl[il] - mult * conf.accCorr[il], estNoise = o2::endcaps::ChipMappingEC0::getNChipsPerLr(il) * noisePerChip; + float diff = noise - estNoise; + chi2 += diff * diff / estNoise; + } + } + chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; + return mult; +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/FastMultEstConfig.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/FastMultEstConfig.cxx new file mode 100644 index 0000000000000..e8aef5c52b225 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/FastMultEstConfig.cxx @@ -0,0 +1,13 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 "EC0Reconstruction/FastMultEstConfig.h" + +O2ParamImpl(o2::ecl::FastMultEstConfig); diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/RecoGeomHelper.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/RecoGeomHelper.cxx new file mode 100644 index 0000000000000..5d7e308c02f7f --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/RecoGeomHelper.cxx @@ -0,0 +1,243 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 RecoLayer.cxx +/// \brief Implementation of the Aux. container for clusters, optimized for tracking +/// \author iouri.belikov@cern.ch + +#include "EC0Reconstruction/RecoGeomHelper.h" +#include "EndCapsBase/SegmentationAlpide.h" +#include "ECLayersBase/GeometryTGeo.h" + +using namespace o2::ecl; + +//_____________________________________________________________________ +void RecoGeomHelper::RecoChip::updateLimits(const Point3D& pntTra) +{ + // update limits from the edge point in tracking frame + yRange.update(pntTra.Y()); + zRange.update(pntTra.Z()); +} + +//_____________________________________________________________________ +void RecoGeomHelper::RecoChip::print() const +{ + printf("Ch#%4d Alp: %+.3f X:%5.2f %+6.3f& pntGlo) +{ + // update limits from the point in Global frame + float phi = pntGlo.phi(); // -pi:pi range + o2::utils::BringTo02Pi(phi); // temporary bring to 0:2pi range + o2::utils::BringTo02Pi(phiRange.min()); + o2::utils::BringTo02Pi(phiRange.max()); + phiRange.update(phi); + phiMean = phiRange.mean(); + dphiH = 0.5 * phiRange.delta(); + if (phiRange.delta() > o2::constants::math::PI) { // wrapping, swap + phiRange.set(phiRange.max(), phiRange.min()); // swap + phiMean -= o2::constants::math::PI; + dphiH = o2::constants::math::PI - dphiH; + } + o2::utils::BringToPMPi(phiRange.min()); // -pi:pi range + o2::utils::BringToPMPi(phiRange.max()); + o2::utils::BringToPMPi(phiMean); + // + zRange.update(pntGlo.Z()); +} + +//_____________________________________________________________________ +void RecoGeomHelper::RecoLadder::init() +{ + auto& chip = chips[0].xyEdges; + float x0(chip.getX0()), y0(chip.getY0()), x1(chip.getX1()), y1(chip.getY1()); + for (int i = 1; i < (int)chips.size(); i++) { + chip = chips[i].xyEdges; + x0 = chip.getDX() > 0 ? std::min(x0, chip.getX0()) : std::max(x0, chip.getX0()); + x1 = chip.getDX() > 0 ? std::max(x1, chip.getX1()) : std::min(x1, chip.getX1()); + y0 = chip.getDY() > 0 ? std::min(y0, chip.getY0()) : std::max(y0, chip.getY0()); + y1 = chip.getDY() > 0 ? std::max(y1, chip.getY1()) : std::min(y1, chip.getY1()); + } + xyEdges.setEdges(x0, y0, x1, y1); +} + +//_____________________________________________________________________ +void RecoGeomHelper::RecoLadder::print() const +{ + assert(overlapWithNext != Undefined || chips.size() == 0); // make sure there are no undefined ladders after init is done + printf("Ladder %3d %.3f]<%.3f dPhiH:%.3f | XYEdges: {%+6.3f,%+6.3f}{%+6.3f,%+6.3f} | %3d chips | OvlNext: %s\n", + id, phiRange.min(), phiMean, phiRange.max(), dphiH, + xyEdges.getX0(), xyEdges.getY0(), xyEdges.getX1(), xyEdges.getY1(), (int)chips.size(), + overlapWithNext == Undefined ? "N/A" : ((overlapWithNext == NoOverlap ? "NO" : (overlapWithNext == Above ? "Above" : "Below")))); + for (const auto& ch : chips) { + ch.print(); + } +} + +//_____________________________________________________________________ +void RecoGeomHelper::RecoLayer::init() +{ + auto gm = o2::ecl::GeometryTGeo::Instance(); + gm->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2GRot, o2::TransformType::T2L)); // more matrices ? + + int nHStaves = gm->getNumberOfHalfStaves(id); + int nStaves = gm->getNumberOfStaves(id); + float dxH = o2::endcaps::SegmentationAlpide::SensorSizeRows / 2; // half width in rphi + float dzH = o2::endcaps::SegmentationAlpide::SensorSizeCols / 2; // half width in Z + int nCh = gm->getNumberOfChipsPerLayer(id), chip0 = gm->getFirstChipIndex(id); + int nChMod = gm->getNumberOfChipsPerModule(id), nChModH = nChMod / 2; + nLadders = nStaves * nHStaves * (id > 2 ? 2 : 1); // 2 ladders per h-stave for OB + ladders.resize(nLadders); + + for (auto lad : ladders) { + lad.chips.reserve(gm->getNumberOfChipsPerHalfStave(id) / (id > 2 ? 2 : 1)); // 2 ladders per h-stave for OB + } + for (int ich = 0; ich < nCh; ich++) { + int chipID = chip0 + ich, lay, sta, ssta, mod, chipInMod; + gm->getChipId(chipID, lay, sta, ssta, mod, chipInMod); + int ladID = sta, chipInLadder = nChMod - chipInMod - 1; // count from negative to positive Z, contrary to official chips numbering + if (nHStaves > 1) { // OB + int modUpper = chipInMod / nChModH; + ladID = sta * 4 + ssta * 2 + modUpper; // OB module covers 2 "ladders" + } + auto& ladder = ladders[ladID]; + auto& chip = ladder.chips.emplace_back(); + chip.id = chipID; + gm->getSensorXAlphaRefPlane(chipID, chip.xRef, chip.alp); + o2::utils::sincosf(chip.alp, chip.snAlp, chip.csAlp); + + Point3D edgeLoc(-dxH, 0.f, -dzH); + auto edgeTra = gm->getMatrixT2L(chipID) ^ (edgeLoc); // edge in tracking frame + chip.updateLimits(edgeTra); + auto edgeGloM = gm->getMatrixT2GRot(chipID)(edgeTra); // edge in global frame + updateLimits(edgeGloM); + ladder.updateLimits(edgeGloM); + + edgeLoc.SetXYZ(dxH, 0.f, dzH); + edgeTra = gm->getMatrixT2L(chipID) ^ (edgeLoc); // edge in tracking frame + chip.updateLimits(edgeTra); + auto edgeGloP = gm->getMatrixT2GRot(chipID)(edgeTra); // edge in globalframe + updateLimits(edgeGloP); + ladder.updateLimits(edgeGloP); + chip.xyEdges.setEdges(edgeGloM.X(), edgeGloM.Y(), edgeGloP.X(), edgeGloP.Y()); + } + + // sort according to mean phi (in -pi:pi range!!!) + std::sort(ladders.begin(), ladders.end(), [](auto& a, auto& b) { + float pha = a.phiMean, phb = b.phiMean; + o2::utils::BringTo02Pi(pha); + o2::utils::BringTo02Pi(phb); + return pha < phb; + }); + + // make sure chips within the ladder are ordered in Z, renumber ladders + for (int i = nLadders; i--;) { + auto& lad = ladders[i]; + std::sort(lad.chips.begin(), lad.chips.end(), [](auto& a, auto& b) { return a.zRange.min() < b.zRange.min(); }); + lad.id = i; + lad.init(); + } + // relate to its neighbour: is the egde at higher R than the edge of the next ladder and do we check for overlaps ? + for (int i = nLadders; i--;) { + auto& lad = ladders[i]; + auto& plad = i > 0 ? ladders[i - 1] : ladders[nLadders - 1]; + if (nHStaves == 2) { // on the OB the ladders of the same halfstave do not overlap + int tlay, tsta, tssta, tmod, tchipInMod; + int play, psta, pssta, pmod, pchipInMod; + gm->getChipId(lad.chips.front().id, tlay, tsta, tssta, tmod, tchipInMod); + gm->getChipId(plad.chips.front().id, play, psta, pssta, pmod, pchipInMod); + if (tsta == psta && tssta == pssta) { // these are 2 ladders of the same halfstave, no overlap + plad.overlapWithNext = RecoLadder::NoOverlap; + continue; + } + } + // need to compare the radius of the edge at lowest phi for this ladder with radius at highest phi of prev ladder + // find the radius of the edge at low phi + float phi0 = std::atan2(lad.xyEdges.getY0(), lad.xyEdges.getX0()); + float phi1 = std::atan2(lad.xyEdges.getY1(), lad.xyEdges.getX1()); + o2::utils::BringTo02Pi(phi0); // we don't know a priori if edge0/1 corresponds to low/high phi or vice versa + o2::utils::BringTo02Pi(phi1); + float r2This = (phi0 < phi1 && phi1 - phi0 < o2::constants::math::PI) ? // pick R of lowest angle + lad.xyEdges.getX0() * lad.xyEdges.getX0() + lad.xyEdges.getY0() * lad.xyEdges.getY0() + : lad.xyEdges.getX1() * lad.xyEdges.getX1() + lad.xyEdges.getY1() * lad.xyEdges.getY1(); + // + phi0 = std::atan2(plad.xyEdges.getY0(), plad.xyEdges.getX0()); + phi1 = std::atan2(plad.xyEdges.getY1(), plad.xyEdges.getX1()); + o2::utils::BringTo02Pi(phi0); // we don't know a priori if edge0/1 corresponds to low/high phi or vice versa + o2::utils::BringTo02Pi(phi1); + float r2Prev = (phi0 < phi1 && phi1 - phi0 < o2::constants::math::PI) ? // pick R of highest angle + plad.xyEdges.getX1() * plad.xyEdges.getX1() + plad.xyEdges.getY1() * plad.xyEdges.getY1() + : plad.xyEdges.getX0() * plad.xyEdges.getX0() + plad.xyEdges.getY0() * plad.xyEdges.getY0(); + // + plad.overlapWithNext = r2Prev > r2This ? RecoLadder::Above : RecoLadder::Below; + } + + int ndiv = nLadders * 3; // number of bins for mapping + phi2ladder.resize(ndiv); + float dphi = o2::constants::math::TwoPI / ndiv; + int laddId = 0; + for (int i = 0; i < ndiv; i++) { + float phi = (0.5 + i) * dphi; + o2::utils::BringToPMPi(phi); + while (laddId < nLadders) { + const auto& lad = ladders[laddId]; + auto rel = lad.isPhiOutside(phi); + if (rel != RecoGeomHelper::Above) { + break; + } + laddId++; // laddId was below phi, catch up + } + phi2ladder[i] = laddId % nLadders; + } + lastChipInLadder = ladders[0].chips.size(); + z2chipID = lastChipInLadder / zRange.delta(); + lastChipInLadder--; + rInv = 1. / rRange.mean(); +} + +//_____________________________________________________________________ +void RecoGeomHelper::RecoLayer::updateLimits(const Point3D& pntGlo) +{ + // update limits from the point in global frame + rRange.update(pntGlo.Rho()); + zRange.update(pntGlo.Z()); +} + +//_____________________________________________________________________ +void RecoGeomHelper::RecoLayer::print() const +{ + printf("\nLayer %d %.2f; + +TrivialClusterer::TrivialClusterer() = default; + +TrivialClusterer::~TrivialClusterer() = default; + +void TrivialClusterer::process(const std::vector* digits, std::vector* clusters) +{ + Float_t sigma2 = SegmentationAlpide::PitchRow * SegmentationAlpide::PitchRow / 12.; + + for (const auto& d : *digits) { + Int_t ix = d.getRow(), iz = d.getColumn(); + Float_t x = 0., y = 0., z = 0.; + SegmentationAlpide::detectorToLocal(ix, iz, x, z); + Point3Df loc(x, 0.f, z); + // inverse transform from local to tracking frame + auto tra = mGeometry->getMatrixT2L(d.getChipIndex()) ^ (loc); + + int noc = clusters->size(); + clusters->emplace_back(d.getChipIndex(), tra, sigma2, sigma2, 0.); + (*clusters)[noc].SetUniqueID(noc); // Save the index within the cluster array + if (mClsLabels) { + /* + for (int i=0; iaddElement(noc,lab); + } + */ + } + } +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/TrivialClustererTask.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/TrivialClustererTask.cxx new file mode 100644 index 0000000000000..1ba41112f0125 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/TrivialClustererTask.cxx @@ -0,0 +1,94 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrivialClustererTask.cxx +/// \brief Implementation of the ITS cluster finder task + +#include "EC0Reconstruction/TrivialClustererTask.h" +#include "DataFormatsITSMFT/Digit.h" +#include "DataFormatsITSMFT/Cluster.h" +#include "MathUtils/Cartesian3D.h" +#include "MathUtils/Utils.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +#include "FairLogger.h" // for LOG +#include "FairRootManager.h" // for FairRootManager + +ClassImp(o2::ecl::TrivialClustererTask); + +using namespace o2::ecl; +using namespace o2::base; +using namespace o2::utils; + +//_____________________________________________________________________ +TrivialClustererTask::TrivialClustererTask(Bool_t useMC) : FairTask("EC0TrivialClustererTask") +{ + if (useMC) + mClsLabels = new o2::dataformats::MCTruthContainer; +} + +//_____________________________________________________________________ +TrivialClustererTask::~TrivialClustererTask() +{ + if (mClustersArray) { + mClustersArray->clear(); + delete mClustersArray; + } + if (mClsLabels) { + mClsLabels->clear(); + delete mClsLabels; + } +} + +//_____________________________________________________________________ +/// \brief Init function +/// Inititializes the clusterer and connects input and output container +InitStatus TrivialClustererTask::Init() +{ + FairRootManager* mgr = FairRootManager::Instance(); + if (!mgr) { + LOG(ERROR) << "Could not instantiate FairRootManager. Exiting ..."; + return kERROR; + } + + mDigitsArray = mgr->InitObjectAs*>("EC0Digit"); + if (!mDigitsArray) { + LOG(ERROR) << "EC0 points not registered in the FairRootManager. Exiting ..."; + return kERROR; + } + + // Register output container + mgr->RegisterAny("EC0Cluster", mClustersArray, kTRUE); + + // Register MC Truth container + if (mClsLabels) + mgr->RegisterAny("EC0ClusterMCTruth", mClsLabels, kTRUE); + + GeometryTGeo* geom = GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::utils::bit2Mask(o2::TransformType::T2L)); // make sure T2L matrices are loaded + mGeometry = geom; + mTrivialClusterer.setGeometry(geom); + mTrivialClusterer.setMCTruthContainer(mClsLabels); + + return kSUCCESS; +} + +//_____________________________________________________________________ +void TrivialClustererTask::Exec(Option_t* option) +{ + if (mClustersArray) + mClustersArray->clear(); + if (mClsLabels) + mClsLabels->clear(); + LOG(DEBUG) << "Running clusterization on new event"; + + mTrivialClusterer.process(mDigitsArray, mClustersArray); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/TrivialVertexer.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/TrivialVertexer.cxx new file mode 100644 index 0000000000000..3327e8eaa511b --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/reconstruction/src/TrivialVertexer.cxx @@ -0,0 +1,107 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrivialVertexer.cxx +/// \brief Implementation of the ITS trivial vertex finder + +#include + +#include "TFile.h" +#include "TTree.h" + +#include "FairMCEventHeader.h" +#include "FairLogger.h" + +#include "EC0Reconstruction/TrivialVertexer.h" +#include "DataFormatsITSMFT/Cluster.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::endcaps; +using namespace o2::ecl; + +using Point3Df = Point3D; + +TrivialVertexer::TrivialVertexer() = default; + +TrivialVertexer::~TrivialVertexer() +{ + if (mHeader) + delete mHeader; + if (mTree) + delete mTree; + if (mFile) + delete mFile; +} + +Bool_t TrivialVertexer::openInputFile(const Char_t* fname) +{ + mFile = TFile::Open(fname, "old"); + if (!mFile) { + LOG(ERROR) << "TrivialVertexer::openInputFile() : " + << "Cannot open the input file !"; + return kFALSE; + } + mTree = (TTree*)mFile->Get("o2sim"); + if (!mTree) { + LOG(ERROR) << "TrivialVertexer::openInputFile() : " + << "Cannot get the input tree !"; + return kFALSE; + } + Int_t rc = mTree->SetBranchAddress("MCEventHeader.", &mHeader); + if (rc != 0) { + LOG(ERROR) << "TrivialVertexer::openInputFile() : " + << "Cannot get the input branch ! rc=" << rc; + return kFALSE; + } + return kTRUE; +} + +void TrivialVertexer::process(const std::vector& clusters, std::vector>& vertices) +{ + if (mClsLabels == nullptr) { + LOG(INFO) << "TrivialVertexer::process() : " + << "No cluster labels available ! Running with a default MC vertex..."; + vertices.emplace_back(std::array{0., 0., 0.}); + return; + } + + if (mTree == nullptr) { + LOG(INFO) << "TrivialVertexer::process() : " + << "No MC information available ! Running with a default MC vertex..."; + vertices.emplace_back(std::array{0., 0., 0.}); + return; + } + + Int_t lastEventID = 0; + Int_t firstEventID = std::numeric_limits::max(); + + // Find the first and last MC event within this TF + for (Int_t i = 0; i < clusters.size(); ++i) { + auto mclab = (mClsLabels->getLabels(i))[0]; + if (mclab.getTrackID() == -1) + continue; // noise + auto id = mclab.getEventID(); + if (id < firstEventID) + firstEventID = id; + if (id > lastEventID) + lastEventID = id; + } + + for (Int_t mcEv = firstEventID; mcEv <= lastEventID; ++mcEv) { + mTree->GetEvent(mcEv); + Double_t vx = mHeader->GetX(); + Double_t vy = mHeader->GetY(); + Double_t vz = mHeader->GetZ(); + vertices.emplace_back(std::array{vx, vy, vz}); + LOG(INFO) << "TrivialVertexer::process() : " + << "MC event #" << mcEv << " with vertex (" << vx << ',' << vy << ',' << vz << ')'; + } +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/CMakeLists.txt new file mode 100644 index 0000000000000..9968da9013637 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +o2_add_library(EC0Simulation + SOURCES src/V11Geometry.cxx + src/V1Layer.cxx + src/V3Layer.cxx + src/Detector.cxx + src/V3Services.cxx + PUBLIC_LINK_LIBRARIES O2::ECLayersBase + O2::EndCapsSimulation + O2::EndCapsBase + ROOT::Physics) + +o2_target_root_dictionary(EC0Simulation + HEADERS include/EC0Simulation/Detector.h + include/EC0Simulation/V1Layer.h + include/EC0Simulation/V3Layer.h + include/EC0Simulation/V11Geometry.h + include/EC0Simulation/V3Services.h) + +o2_data_file(COPY data DESTINATION Detectors/EC0/simulation) + +o2_add_executable(digi2raw + COMPONENT_NAME ec0 + SOURCES src/digi2raw.cxx + PUBLIC_LINK_LIBRARIES O2::EndCapsReconstruction + O2::DataFormatsITSMFT + O2::EndCapsBase + O2::EndCapsSimulation + O2::DetectorsRaw + O2::DetectorsCommonDataFormats + O2::CommonUtils + Boost::program_options) diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/data/simcuts.dat b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/data/simcuts.dat new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/Detector.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/Detector.h new file mode 100644 index 0000000000000..c98ac4f856027 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/Detector.h @@ -0,0 +1,354 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Detector.h +/// \brief Definition of the Detector class + +#ifndef ALICEO2_ENDCAPSLAYERS_DETECTOR_H_ +#define ALICEO2_ENDCAPSLAYERS_DETECTOR_H_ + +#include // for vector +#include "DetectorsBase/GeometryManager.h" // for getSensID +#include "DetectorsBase/Detector.h" // for Detector +#include "DetectorsCommonDataFormats/DetID.h" // for Detector +#include "EndCapsSimulation/Hit.h" // for Hit +#include "Rtypes.h" // for Int_t, Double_t, Float_t, Bool_t, etc +#include "TArrayD.h" // for TArrayD +#include "TGeoManager.h" // for gGeoManager, TGeoManager (ptr only) +#include "TLorentzVector.h" // for TLorentzVector +#include "TVector3.h" // for TVector3 + +class FairVolume; +class TGeoVolume; + +class TParticle; + +class TString; + +namespace o2 +{ +namespace endcaps +{ +class Hit; +} +} // namespace o2 + +namespace o2 +{ +namespace ecl +{ +class GeometryTGeo; +} +} // namespace o2 +namespace o2 +{ +namespace ecl +{ +class V3Layer; +} +} // namespace o2 + +namespace o2 +{ +namespace ecl +{ +class V3Layer; +class V3Services; + +class Detector : public o2::base::DetImpl +{ + public: + enum Model { + kIBModelDummy = 0, + kIBModel0 = 1, + kIBModel1 = 2, + kIBModel21 = 3, + kIBModel22 = 4, + kIBModel3 = 5, + kIBModel4 = 10, + kOBModelDummy = 6, + kOBModel0 = 7, + kOBModel1 = 8, + kOBModel2 = 9 + }; + + static constexpr Int_t sNumberLayers = 7; ///< Number of layers in ITSU + static constexpr Int_t sNumberInnerLayers = 3; ///< Number of inner layers in ITSU + static constexpr Int_t sNumberOfWrapperVolumes = 3; ///< Number of wrapper volumes + + /// Name : Detector Name + /// Active: kTRUE for active detectors (ProcessHits() will be called) + /// kFALSE for inactive detectors + Detector(Bool_t active); + + /// Default constructor + Detector(); + + /// Default destructor + ~Detector() override; + + /// Initialization of the detector is done here + void InitializeO2Detector() override; + + /// This method is called for each step during simulation (see FairMCApplication::Stepping()) + Bool_t ProcessHits(FairVolume* v = nullptr) override; + + /// Registers the produced collections in FAIRRootManager + void Register() override; + + /// Gets the produced collections + std::vector* getHits(Int_t iColl) const + { + if (iColl == 0) { + return mHits; + } + return nullptr; + } + + public: + /// Has to be called after each event to reset the containers + void Reset() override; + + /// Base class to create the detector geometry + void ConstructGeometry() override; + + /// Creates the Service Barrel (as a simple cylinder) for IB and OB + /// \param innerBarrel if true, build IB service barrel, otherwise for OB + /// \param dest the mother volume holding the service barrel + /// \param mgr the gGeoManager pointer (used to get the material) + void createServiceBarrel(const Bool_t innerBarrel, TGeoVolume* dest, const TGeoManager* mgr = gGeoManager); + + /// Sets the layer parameters + /// \param nlay layer number + /// \param phi0 layer phi0 + /// \param r layer radius + /// \param nstav number of staves + /// \param nunit IB: number of chips per stave + /// \param OB: number of modules per half stave + /// \param lthick stave thickness (if omitted, defaults to 0) + /// \param dthick detector thickness (if omitted, defaults to 0) + /// \param dettypeID ?? + /// \param buildLevel (if 0, all geometry is build, used for material budget studies) + void defineLayer(Int_t nlay, Double_t phi0, Double_t r, Int_t nladd, Int_t nmod, Double_t lthick = 0., + Double_t dthick = 0., UInt_t detType = 0, Int_t buildFlag = 0) override; + + /// Sets the layer parameters for a "turbo" layer + /// (i.e. a layer whose staves overlap in phi) + /// \param nlay layer number + /// \param phi0 phi of 1st stave + /// \param r layer radius + /// \param nstav number of staves + /// \param nunit IB: number of chips per stave + /// \param OB: number of modules per half stave + /// \param width stave width + /// \param tilt layer tilt angle (degrees) + /// \param lthick stave thickness (if omitted, defaults to 0) + /// \param dthick detector thickness (if omitted, defaults to 0) + /// \param dettypeID ?? + /// \param buildLevel (if 0, all geometry is build, used for material budget studies) + void defineLayerTurbo(Int_t nlay, Double_t phi0, Double_t r, Int_t nladd, Int_t nmod, Double_t width, Double_t tilt, + Double_t lthick = 0., Double_t dthick = 0., UInt_t detType = 0, Int_t buildFlag = 0) override; + + /// Gets the layer parameters + /// \param nlay layer number + /// \param phi0 phi of 1st stave + /// \param r layer radius + /// \param nstav number of staves + /// \param nmod IB: number of chips per stave + /// \param OB: number of modules per half stave + /// \param width stave width + /// \param tilt stave tilt angle + /// \param lthick stave thickness + /// \param dthick detector thickness + /// \param dettype detector type + virtual void getLayerParameters(Int_t nlay, Double_t& phi0, Double_t& r, Int_t& nladd, Int_t& nmod, Double_t& width, + Double_t& tilt, Double_t& lthick, Double_t& mthick, UInt_t& dettype) const; + + /// This method is an example of how to add your own point of type Hit to the clones array + o2::endcaps::Hit* addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, + const TVector3& startMom, double startE, double endTime, double eLoss, + unsigned char startStatus, unsigned char endStatus); + + /// Set per wrapper volume parameters + void defineWrapperVolume(Int_t id, Double_t rmin, Double_t rmax, Double_t zspan) override; + + /// Add alignable top volumes + void addAlignableVolumes() const override; + + /// Add alignable Layer volumes + /// \param lr layer number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesLayer(Int_t lr, TString& parent, Int_t& lastUID) const; + + /// Add alignable Stave volumes + /// \param lr layer number + /// \param st stave number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesStave(Int_t lr, Int_t st, TString& parent, Int_t& lastUID) const; + + /// Add alignable HalfStave volumes + /// \param lr layer number + /// \param st stave number + /// \param hst half stave number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesHalfStave(Int_t lr, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const; + + /// Add alignable Module volumes + /// \param lr layer number + /// \param st stave number + /// \param hst half stave number + /// \param md module number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesModule(Int_t lr, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const; + + /// Add alignable Chip volumes + /// \param lr layer number + /// \param st stave number + /// \param hst half stave number + /// \param md module number + /// \param ch chip number + /// \param parent path of the parent volume + /// \param lastUID on output, UID of the last volume + void addAlignableVolumesChip(Int_t lr, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, + Int_t& lastUID) const; + + /// Return Chip Volume UID + /// \param id volume id + Int_t chipVolUID(Int_t id) const { return o2::base::GeometryManager::getSensID(o2::detectors::DetID::EC0, id); } + + void EndOfEvent() override; + + void FinishPrimary() override { ; } + virtual void finishRun() { ; } + void BeginPrimary() override { ; } + void PostTrack() override { ; } + void PreTrack() override { ; } + /// Prints out the content of this class in ASCII format + /// \param ostream *os The output stream + void Print(std::ostream* os) const; + + /// Reads in the content of this class in the format of Print + /// \param istream *is The input stream + void Read(std::istream* is); + + /// Returns the number of layers + Int_t getNumberOfLayers() const { return sNumberLayers; } + virtual void setStaveModelIB(Model model) { mStaveModelInnerBarrel = model; } + virtual void setStaveModelOB(Model model) { mStaveModelOuterBarrel = model; } + virtual Model getStaveModelIB() const { return mStaveModelInnerBarrel; } + virtual Model getStaveModelOB() const { return mStaveModelOuterBarrel; } + + GeometryTGeo* mGeometryTGeo; //! access to geometry details + + protected: + Int_t mLayerID[sNumberLayers]; //! [sNumberLayers] layer identifier + TString mLayerName[sNumberLayers]; //! [sNumberLayers] layer identifier + + private: + /// this is transient data about track passing the sensor + struct TrackData { // this is transient + bool mHitStarted; //! hit creation started + unsigned char mTrkStatusStart; //! track status flag + TLorentzVector mPositionStart; //! position at entrance + TLorentzVector mMomentumStart; //! momentum + double mEnergyLoss; //! energy loss + } mTrackData; //! + + Int_t mNumberOfDetectors; + + Bool_t mModifyGeometry; + + Double_t mWrapperMinRadius[sNumberOfWrapperVolumes]; //! Min radius of wrapper volume + Double_t mWrapperMaxRadius[sNumberOfWrapperVolumes]; //! Max radius of wrapper volume + Double_t mWrapperZSpan[sNumberOfWrapperVolumes]; //! Z span of wrapper volume + Int_t mWrapperLayerId[sNumberLayers]; //! Id of wrapper layer to which layer belongs (-1 if not wrapped) + + Bool_t mTurboLayer[sNumberLayers]; //! True for "turbo" layers + Double_t mLayerPhi0[sNumberLayers]; //! Vector of layer's 1st stave phi in lab + Double_t mLayerRadii[sNumberLayers]; //! Vector of layer radii + Int_t mStavePerLayer[sNumberLayers]; //! Vector of number of staves per layer + Int_t mUnitPerStave[sNumberLayers]; //! Vector of number of "units" per stave + Double_t mChipThickness[sNumberLayers]; //! Vector of chip thicknesses + Double_t mStaveWidth[sNumberLayers]; //! Vector of stave width (only used for turbo) + Double_t mStaveTilt[sNumberLayers]; //! Vector of stave tilt (only used for turbo) + Double_t mDetectorThickness[sNumberLayers]; //! Vector of detector thicknesses + UInt_t mChipTypeID[sNumberLayers]; //! Vector of detector type id + Int_t mBuildLevel[sNumberLayers]; //! Vector of Material Budget Studies + + /// Container for hit data + std::vector* mHits; + + /// Creates an air-filled wrapper cylindrical volume + TGeoVolume* createWrapperVolume(const Int_t nLay); + + /// Create the detector materials + virtual void createMaterials(); + + /// Construct the detector geometry + void constructDetectorGeometry(); + + /// Define the sensitive volumes of the geometry + void defineSensitiveVolumes(); + + /// Creates the Inner Barrel Services + /// \param motherVolume the TGeoVolume owing the volume structure + void createInnerBarrelServices(TGeoVolume* motherVolume); + + /// Creates the Middle Barrel Services + /// \param motherVolume the TGeoVolume owing the volume structure + void createMiddlBarrelServices(TGeoVolume* motherVolume); + + /// Creates the Outer Barrel Services + /// \param motherVolume the TGeoVolume owing the volume structure + void createOuterBarrelServices(TGeoVolume* motherVolume); + + /// Creates the Outer Barrel Supports + /// \param motherVolume the TGeoVolume owing the volume supports + void createOuterBarrelSupports(TGeoVolume* motherVolume); + + Detector(const Detector&); + + Detector& operator=(const Detector&); + + Model mStaveModelInnerBarrel; //! The stave model for the Inner Barrel + Model mStaveModelOuterBarrel; //! The stave model for the Outer Barrel + V3Layer* mGeometry[sNumberLayers]; //! Geometry + V3Services* mServicesGeometry; //! Services Geometry + + template + friend class o2::base::DetImpl; + ClassDefOverride(Detector, 1); +}; + +// Input and output function for standard C++ input/output. +std::ostream& operator<<(std::ostream& os, Detector& source); + +std::istream& operator>>(std::istream& os, Detector& source); +} // namespace ecl +} // namespace o2 + +#ifdef USESHM +namespace o2 +{ +namespace base +{ +template <> +struct UseShm { + static constexpr bool value = true; +}; +} // namespace base +} // namespace o2 +#endif + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V11Geometry.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V11Geometry.h new file mode 100644 index 0000000000000..4395ce5e67d0f --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V11Geometry.h @@ -0,0 +1,458 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 V11Geometry.h +/// \brief Definition of the V11Geometry class + +#ifndef ALICEO2_ENDCAPSLAYERS_V11GEOMETRY_H_ +#define ALICEO2_ENDCAPSLAYERS_V11GEOMETRY_H_ + +#include // for DegToRad, Cos, Sin, Tan +#include // for TObject +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, V11Geometry::Class, etc + +class TGeoArb8; // lines 11-11 +class TGeoBBox; // lines 16-16 +class TGeoConeSeg; // lines 15-15 +class TGeoPcon; // lines 12-12 +class TGeoTube; // lines 13-13 +class TGeoTubeSeg; // lines 14-14 + +namespace o2 +{ +namespace ecl +{ + +/// This class is a base class for the ITS geometry version 11. It contains common/standard +/// functions used in many places in defining the ITS geometry, version 11. Large posions of +/// the ITS geometry, version 11, should be derived from this class so as to make maximum +/// use of these common functions. This class also defines the proper conversion values such, +/// to cm and degrees, such that the most useful units, those used in the Engineering drawings, +/// can be used. +class V11Geometry : public TObject +{ + + public: + V11Geometry() : mDebug(){}; + + V11Geometry(Int_t debug) : mDebug(debug){}; + + ~V11Geometry() + override = default; + + /// Sets the debug flag for debugging output + void setDebug(Int_t level = 5) + { + mDebug = level; + } + + /// Clears the debug flag so no debugging output will be generated + void setNoDebug() + { + mDebug = 0; + } + + /// Returns the debug flag value + Bool_t getDebug(Int_t level = 1) const + { + return mDebug >= level; + } + + // Static functions + + /// Define Trig functions for use with degrees (standerd TGeo angles). + /// Sine function + Double_t sinD(Double_t deg) const + { + return TMath::Sin(deg * TMath::DegToRad()); + } + + /// Cosine function + Double_t cosD(Double_t deg) const + { + return TMath::Cos(deg * TMath::DegToRad()); + } + + /// Tangent function + Double_t tanD(Double_t deg) const + { + return TMath::Tan(deg * TMath::DegToRad()); + } + + /// Determine the intersection of two lines + /// Given the two lines, one passing by (x0,y0) with slope m and + /// the other passing by (x1,y1) with slope n, returns the coordinates + /// of the intersecting point (xi,yi) + /// \param Double_t m The slope of the first line + /// \param Double_t x0,y0 The x and y coord. of the first point + /// \param Double_t n The slope of the second line + /// \param Double_t x1,y1 The x and y coord. of the second point + /// As an output it gives the coordinates xi and yi of the intersection point + void intersectLines(Double_t m, Double_t x0, Double_t y0, Double_t n, Double_t x1, Double_t y1, Double_t& xi, + Double_t& yi) const; + + /// Determine the intersection of a line and a circle + /// Given a line passing by (x0,y0) with slope m and a circle with + /// radius rr and center (xc,yc), returns the coordinates of the + /// intersecting points (xi1,yi1) and (xi2,yi2) (xi1 > xi2) + /// \param Double_t m The slope of the line + /// \param Double_t x0,y0 The x and y coord. of the point + /// \param Double_t rr The radius of the circle + /// \param Double_t xc,yc The x and y coord. of the center of circle + /// As an output it gives the coordinates xi and yi of the intersection points + /// Returns kFALSE if the line does not intercept the circle, otherwise kTRUE + static Bool_t intersectCircle(Double_t m, Double_t x0, Double_t y0, Double_t rr, Double_t xc, Double_t yc, + Double_t& xi1, Double_t& yi1, Double_t& xi2, Double_t& yi2); + + /// Given the two points (x0,y0) and (x1,y1) and the location x, returns + /// the value y corresponding to that point x on the line defined by the + /// two points. Returns the value y corresponding to the point x on the line defined by + /// the two points (x0,y0) and (x1,y1). + /// \param Double_t x0 The first x value defining the line + /// \param Double_t y0 The first y value defining the line + /// \param Double_t x1 The second x value defining the line + /// \param Double_t y1 The second y value defining the line + /// \param Double_t x The x value for which the y value is wanted. + Double_t yFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x) const; + + /// Given the two points (x0,y0) and (x1,y1) and the location y, returns + /// the value x corresponding to that point y on the line defined by the + /// two points. Returns the value x corresponding to the point y on the line defined by + /// the two points (x0,y0) and (x1,y1). + /// \param Double_t x0 The first x value defining the line + /// \param Double_t y0 The first y value defining the line + /// \param Double_t x1 The second x value defining the line + /// \param Double_t y1 The second y value defining the line + /// \param Double_t y The y value for which the x value is wanted. + Double_t xFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t y) const; + + /// Functions require at parts of Volume A to be already defined. + /// Returns the value of Rmax corresponding to point z alone the line + /// defined by the two points p.Rmax(i1),p-GetZ(i1) and p->GetRmax(i2), + /// p->GetZ(i2). + /// \param TGeoPcon *p The Polycone where the two points come from + /// \param Int_t i1 Point 1 + /// \param Int_t i2 Point 2 + /// \param Double_t z The value of z for which Rmax is to be found + /// \param Double_t Rmx the value corresponding to z + Double_t rMaxFrom2Points(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t z) const; + + /// Returns the value of Rmin corresponding to point z alone the line + /// defined by the two points p->GetRmin(i1),p->GetZ(i1) and + /// p->GetRmin(i2), p->GetZ(i2). + /// \param TGeoPcon *p The Polycone where the two points come from + /// \param Int_t i1 Point 1 + /// \param Int_t i2 Point 2 + /// \param Double_t z The value of z for which Rmax is to be found + /// \param Double_t Rmx the value corresponding to z + Double_t rMinFrom2Points(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t z) const; + + /// Returns the value of Rmin corresponding to point z alone the line + /// defined by the two points p->GetRmin(i1),p->GetZ(i1) and + /// p->GetRmin(i2), p->GetZ(i2). Returns the value r corresponding to z and the + /// line defined by the two points + /// \param Double_t az Array of z values + /// \param Double_t r Array of r values + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t z Value z at which r is to be found + Double_t rFrom2Points(const Double_t* ar, const Double_t* az, Int_t i1, Int_t i2, Double_t z) const; + + /// Returns the value of Z corresponding to point R alone the line + /// defined by the two points p->GetRmin(i1),p->GetZ(i1) and + /// p->GetRmin(i2),p->GetZ(i2). Returns the value z corresponding to r min + /// and the line defined by the two points + /// \param TGeoPcon *p The Poly cone where the two points come from. + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t r Value r min at which z is to be found + Double_t zFrom2MinPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const; + + /// Returns the value of Z corresponding to point R alone the line + /// defined by the two points p->GetRmax(i1),p->GetZ(i1) and + /// p->GetRmax(i2),p->GetZ(i2). Returns the value z corresponding to + /// r max and the line defined by the two points + /// \param TGeoPcon *p The Poly cone where the two points come from. + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t r Value r max at which z is to be found + Double_t zFrom2MaxPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const; + + /// Returns the value of z corresponding to point R alone the line + /// defined by the two points p->GetRmax(i1),p->GetZ(i1) and + /// p->GetRmax(i2),p->GetZ(i2). Returns the value z corresponding to r + /// and the line defined by the two points + /// \param Double_t z Array of z values + /// \param Double_t ar Array of r values + /// \param Int_t i1 First Point in arrays + /// \param Int_t i2 Second Point in arrays + /// \param Double_t r Value r at which z is to be found + Double_t zFrom2Points(const Double_t* az, const Double_t* ar, Int_t i1, Int_t i2, Double_t r) const; + + /// General Outer Cone surface equation Rmax + /// Given 1 point from a TGeoPcon(z and Rmax) the angle tc returns r for + /// a given z, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns the value Rmax corresponding to the line at angle th, offset by + /// th, and the point p->GetZ/Rmin[ip] at the location z. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t z The value of z to compute Rmax from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t rMaxFromZpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t z, Double_t th = 0.0) const; + + // General Cone surface equation R(z). Returns the value R correstponding to the line at + // angle th, offset by th, and the point p->GetZ/Rmax[ip] at the location z. + // \param Double_t ar The array of R values + // \param Double_t az The array of Z values + // \param Int_t ip The index in p to get the point location + // \param Double_t tc The angle of that part of the cone is at + // \param Double_t z The value of z to compute R from + // \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t rFromZpCone(const Double_t* ar, const Double_t* az, int ip, Double_t tc, Double_t z, + Double_t th = 0.0) const; + + /// General Inner Cone surface equation Rmin. + /// Given 1 point from a TGeoPcon(z and Rmin) the angle tc returns r for + /// a given z, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns the value Rmin correstponding to the line at angle th, + /// offset by th, and the point p->GetZ/Rmin[ip] at the location z. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t z The value of z to compute Rmin from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t rMinFromZpCone(const TGeoPcon* p, Int_t ip, Double_t tc, Double_t z, Double_t th = 0.0) const; + + /// General Outer cone Surface equation for z. + /// Given 1 point from a TGeoPcon(z and Rmax) the angle tc returns z for + /// a given Rmax, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns thevalue Z correstponding to the line at angle th, + /// offset by th, and the point p->GetZ/Rmax[ip] at the location r. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t r The value of Rmax to compute z from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t zFromRMaxpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, Double_t th = 0.0) const; + + /// General Outer cone Surface equation for z. + /// Returns the value Z correstponding to the line at angle th, offeset by + /// th, and the point p->GetZ/Rmax[ip] at the locatin r. + /// \param Double_t ar The array of R values + /// \param Double_t az The array of Z values + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t r The value of Rmax to compute z from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t zFromRMaxpCone(const Double_t* ar, const Double_t* az, Int_t ip, Double_t tc, Double_t r, + Double_t th = 0.0) const; + + /// General Inner cone Surface equation for z. + /// Given 1 point from a TGeoPcon(z and Rmin) the angle tc returns z for + /// a given Rmin, an offset (distnace perpendicular to line at angle tc) of + /// th may be applied. Returns the value Z correstponding to the line at angle th, offeset by + /// th, and the point p->GetZ/Rmin[ip] at the location r. + /// \param TGeoPcon *p The poly cone where the initial point comes from + /// \param Int_t ip The index in p to get the point location + /// \param Double_t tc The angle of that part of the cone is at + /// \param Double_t r The value of Rmin to compute z from + /// \param Double_t th The perpendicular distance the parralell line is from the point ip + Double_t zFromRMinpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, Double_t th = 0.0) const; + + /// Given two lines defined by the points i1, i2,i3 in the TGeoPcon + /// class p that intersect at point p->GetZ(i2) return the point z,r + /// that is Cthick away in the TGeoPcon class q. If points i1=i2 + /// and max == kTRUE, then p->GetRmin(i1) and p->GetRmax(i2) are used. + /// if points i2=i3 and max=kTRUE then points p->GetRmax(i2) and + /// p->GetRmin(i3) are used. If i2=i3 and max=kFALSE, then p->GetRmin(i2) + /// and p->GetRmax(i3) are used. + /// \param TGeoPcon *p Class where points i1, i2, and i3 are taken from + /// \param Int_t i1 First point in class p + /// \param Int_t i2 Second point in class p + /// \param Int_t i3 Third point in class p + /// \param Double_t c Distance inside the outer/inner surface that the point j1 + /// is to be computed for + /// \param TGeoPcon *q Pointer to class for results to be put into. + /// \param Int_t j1 Point in class q where data is to be stored. + /// \param Bool_t ma if kTRUE, then a Rmax value is computed, else a Rmin valule is computed + /// \param TGeoPcon *q Pointer to class for results to be put into. + void insidePoint(const TGeoPcon* p, Int_t i1, Int_t i2, Int_t i3, Double_t Cthick, TGeoPcon* q, Int_t j1, + Bool_t max) const; + + /// Given two intersecting lines defined by the points (x0,y0), (x1,y1) and + /// (x1,y1), (x2,y2) {intersecting at (x1,y1)} the point (x,y) a distance + /// c away is returned such that two lines a distance c away from the + /// lines defined above intersect at (x,y). + /// \param Double_t x0 X point on the first intersecting sets of lines + /// \param Double_t y0 Y point on the first intersecting sets of lines + /// \param Double_t x1 X point on the first/second intersecting sets of lines + /// \param Double_t y1 Y point on the first/second intersecting sets of lines + /// \param Double_t x2 X point on the second intersecting sets of lines + /// \param Double_t y2 Y point on the second intersecting sets of lines + /// \param Double_t c Distance the two sets of lines are from each other + /// \param Double_t x X point for the intersecting sets of parellel lines + /// \param Double_t y Y point for the intersecting sets of parellel lines + void insidePoint(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t c, + Double_t& x, Double_t& y) const; + + /// Given an initial point z0,r0, the initial angle theta0, and the radius + /// of curvature, returns the point z1, r1 at the angle theta1. Theta + /// measured from the r axis in the clock wise direction [degrees]. + /// \param Double_t rc The radius of curvature + /// \param Double_t theta0 The starting angle (degrees) + /// \param Double_t z0 The value of z at theta0 + /// \param Double_t r0 The value of r at theta0 + /// \param Double_t theta1 The ending angle (degrees) + /// \param Double_t &z1 The value of z at theta1 + /// \param Double_t &r1 The value of r at theta1 + void radiusOfCurvature(Double_t rc, Double_t theta0, Double_t z0, Double_t r0, Double_t theta1, Double_t& z1, + Double_t& r1) const; + + // Output functions for debugging + + /// Prints out the content of the TGeoArb8 + /// \param TGeoArb8 *a + void printArb8(const TGeoArb8* a) const; + + /// Prints out the contents of the TGeoPcon + /// \param TGeoPcon *a + void printPcon(const TGeoPcon* a) const; + + /// Prints out the contents of the TGeoTube + /// \param TGeoTube *a + void printTube(const TGeoTube* a) const; + + /// Prints out the contents of the TGeoTubeSeg + /// \param TGeoTubeSeg *a + void printTubeSeg(const TGeoTubeSeg* a) const; + + /// Prints out the contents of the TGeoConeSeg + /// \param TGeoConeSeg *a + void printConeSeg(const TGeoConeSeg* a) const; + + /// Prints out the contents of the TGeoBBox + /// \param TGeoBBox *a + void printBBox(const TGeoBBox* a) const; + + /// Draws a cross sectional view of the TGeoPcon, Primarily for debugging. + /// A TCanvas should exist first. + /// \param TGeoPcon *p The TGeoPcon to be "drawn" + /// \param Int_t fillc The fill color to be used + /// \param Int_t fills The fill style to be used + /// \param Int_t linec The line color to be used + /// \param Int_t lines The line style to be used + /// \param Int_t linew The line width to be used + /// \param Int_t markc The markder color to be used + /// \param Int_t marks The markder style to be used + /// \param Float_t marksize The marker size + void drawCrossSection(const TGeoPcon* p, Int_t fillc = 7, Int_t fills = 4050, Int_t linec = 3, Int_t lines = 1, + Int_t linew = 4, Int_t markc = 2, Int_t marks = 4, Float_t marksize = 1.0) const; + + /// Computes the angles, t0 and t1 corresponding to the intersection of + /// the line, defined by {x0,y0} {x1,y1}, and the circle, defined by + /// its center {xc,yc} and radius r. If the line does not intersect the + /// line, function returns kFALSE, otherwise it returns kTRUE. If the + /// line is tangent to the circle, the angles t0 and t1 will be the same. + /// Returns kTRUE if line intersects circle or kFALSE if line does not intersect circle + /// or the line is not properly defined point {x0,y0} and {x1,y1} are the same point. + /// \param Double_t x0 X of first point defining the line + /// \param Double_t y0 Y of first point defining the line + /// \param Double_t x1 X of Second point defining the line + /// \param Double_t y1 Y of Second point defining the line + /// \param Double_t xc X of Circle center point defining the line + /// \param Double_t yc Y of Circle center point defining the line + /// \param Double_t r radius of circle + /// \param Double_t &t0 First angle where line intersects circle + /// \param Double_t &t1 Second angle where line intersects circle + Bool_t angleOfIntersectionWithLine(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t xc, Double_t yc, + Double_t rc, Double_t& t0, Double_t& t1) const; + + /// Function to compute the ending angle, for arc 0, and starting angle, + /// for arc 1, such that a straight line will connect them with no discontinuities. + /// Begin_Html + /* + + */ + // End_Html + /// \param Double_t x0 X Coordinate of arc 0 center. + /// \param Double_t y0 Y Coordinate of arc 0 center. + /// \param Double_t r0 Radius of curvature of arc 0. For signe see figure. + /// \param Double_t x1 X Coordinate of arc 1 center. + /// \param Double_t y1 Y Coordinate of arc 1 center. + /// \param Double_t r1 Radius of curvature of arc 1. For signe see figure. + /// \param Double_t t0 Ending angle of arch 0, with respect to x axis, Degrees. + /// \param Double_t t1 Starting angle of arch 1, with respect to x axis, Degrees. + void anglesForRoundedCorners(Double_t x0, Double_t y0, Double_t r0, Double_t x1, Double_t y1, Double_t r1, + Double_t& t0, Double_t& t1) const; + + /// Define a general createMaterials function here so that if + /// any specific subdetector does not define it this null function + /// will due. This function is not declaired const so that a sub- + /// detector's version may use class variables if they wish. + /// Defined media here should correspond to the one defined in galice.cuts + /// File which is red in (AliMC*) fMCApp::Init() { ReadTransPar(); } + void createDefaultMaterials(); + + virtual void createMaterials(){}; + + /// Function to create the figure describing how the function + /// anglesForRoundedCorners works. + /// \param Double_t x0 X Coordinate of arc 0 center. + /// \param Double_t y0 Y Coordinate of arc 0 center. + /// \param Double_t r0 Radius of curvature of arc 0. For signe see figure. + /// \param Double_t x1 X Coordinate of arc 1 center. + /// \param Double_t y1 Y Coordinate of arc 1 center. + /// \param Double_t r1 Radius of curvature of arc 1. For signe see figure. + void makeFigure1(Double_t x0 = 0.0, Double_t y0 = 0.0, Double_t r0 = 2.0, Double_t x1 = -4.0, Double_t y1 = -2.0, + Double_t r1 = 1.0); + + protected: + // Units, Convert from k?? to cm,degree,GeV,seconds, + static const Double_t sMicron; ///< Convert micron to TGeom's cm. + static const Double_t sMm; ///< Convert mm to TGeom's cm. + static const Double_t sCm; ///< Convert cm to TGeom's cm. + static const Double_t sDegree; ///< Convert degrees to TGeom's degrees + static const Double_t sRadian; ///< To Radians + static const Double_t sGCm3; ///< Density in g/cm^3 + static const Double_t sKgm3; ///< Density in kg/m^3 + static const Double_t sKgdm3; ///< Density in kg/dm^3 + static const Double_t sCelsius; ///< Temperature in degrees Celcius + static const Double_t sPascal; ///< Preasure in Pascal + static const Double_t sKPascal; ///< Preasure in KPascal + static const Double_t sEV; ///< Energy in eV + static const Double_t sKEV; ///< Energy in KeV + static const Double_t sMEV; ///< Energy in MeV + static const Double_t sGEV; ///< Energy in GeV + + private: + /// Basic function used to determine the ending angle and starting angles + /// for rounded corners given the relative distance between the centers + /// of the circles and the difference/sum of their radii. Case 0. Returns the angle in Degrees + /// \param Double_t dx difference in x locations of the circle centers + /// \param Double_t dy difference in y locations of the circle centers + /// \param Double_t sdr difference or sum of the circle radii + Double_t angleForRoundedCorners0(Double_t dx, Double_t dy, Double_t sdr) const; + + /// Basic function used to determine the ending angle and starting angles + /// for rounded corners given the relative distance between the centers + /// of the circles and the difference/sum of their radii. Case 0. Returns the angle in Degrees + /// \param Double_t dx difference in x locations of the circle centers + /// \param Double_t dy difference in y locations of the circle centers + /// \param Double_t sdr difference or sum of the circle radii + Double_t angleForRoundedCorners1(Double_t dx, Double_t dy, Double_t sdr) const; + + Int_t mDebug; //! Debug flag/level + ClassDefOverride(V11Geometry, 1); // Base class for ITS v11 geometry +}; +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V1Layer.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V1Layer.h new file mode 100644 index 0000000000000..75382ffa5ffd2 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V1Layer.h @@ -0,0 +1,420 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 AliITSUv1Layer.cxx +/// \brief Definition of the AliITSUv1Layer class +/// \author Mario Sitta +/// \author Chinorat Kobdaj (kobdaj@g.sut.ac.th) + +#ifndef ALICEO2_ENDCAPSLAYERS_UPGRADEV1LAYER_H_ +#define ALICEO2_ENDCAPSLAYERS_UPGRADEV1LAYER_H_ + +#include // for gGeoManager +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc +#include "EC0Simulation/V11Geometry.h" // for V11Geometry +#include "EC0Simulation/Detector.h" // for Detector, Detector::Model + +class TGeoArb8; + +class TGeoCombiTrans; + +class TGeoVolume; // lines 15-15 + +namespace o2 +{ +namespace ecl +{ + +/// This class defines the Geometry for the ITS using TGeo. This is a work class used +/// to study different configurations during the development of the new ITS structure +class V1Layer : public V11Geometry +{ + + public: + enum { + kStave, + kHalfStave, + kModule, + kChip, + kNHLevels + }; + + // Default constructor + V1Layer(); + + // Constructor setting debugging level + V1Layer(Int_t debug); + + // Constructor setting layer number and debugging level + V1Layer(Int_t lay, Int_t debug); + + /// Constructor setting layer number and debugging level + /// for a "turbo" layer (i.e. where staves overlap in phi) + V1Layer(Int_t lay, Bool_t turbo, Int_t debug); + + /// Copy constructor + V1Layer(const V1Layer& source); + + /// Assignment operator + V1Layer& operator=(const V1Layer& source); + + /// Default destructor + ~V1Layer() override; + + Bool_t isTurbo() const + { + return mIsTurbo; + }; + + Double_t getStaveThick() const + { + return mStaveThickness; + }; + + Double_t getStaveTilt() const + { + return mStaveTilt; + }; + + Double_t getStaveWidth() const + { + return mStaveWidth; + }; + + Double_t getSensorThick() const + { + return mSensorThickness; + }; + + Double_t getNumberOfStaves() const + { + return mNumberOfStaves; + }; + + Double_t getNumberOfChips() const + { + return mNumberOfChips; + }; + + Double_t getRadius() const + { + return mLayerRadius; + }; + + Double_t getPhi0() const + { + return mPhi0; + }; + + Double_t getZLength() const + { + return mZLength; + }; + + Int_t getChipType() const + { + return mChipTypeID; + } + + Int_t getNumberOfStavesPerParent() const + { + return mHierarchy[kStave]; + } + + Int_t getNumberOfHalfStavesPerParent() const + { + return mHierarchy[kHalfStave]; + } + + Int_t getNumberOfModulesPerParent() const + { + return mHierarchy[kModule]; + } + + Int_t getNumberOfChipsPerParent() const + { + return mHierarchy[kChip]; + } + + Detector::Model getStaveModel() const + { + return mStaveModel; + } + + void setStaveThick(Double_t t) + { + mStaveThickness = t; + }; + + /// Sets the Stave tilt angle (for turbo layers only) + /// \param t The stave tilt angle + void setStaveTilt(Double_t t); + + /// Sets the Stave width (for turbo layers only) + /// \param w The stave width + void setStaveWidth(Double_t w); + + void setSensorThick(Double_t t) + { + mSensorThickness = t; + }; + + void setNumberOfStaves(Int_t n) + { + mHierarchy[kStave] = mNumberOfStaves = n; + }; + + /// Sets the number of units in a stave: + /// for the Inner Barrel: the number of chips per stave + /// for the Outer Barrel: the number of modules per half stave + /// \param u the number of units + void setNumberOfUnits(Int_t u); + + void setRadius(Double_t r) + { + mLayerRadius = r; + }; + + void setPhi0(Double_t phi) + { + mPhi0 = phi; + } + + void setZLength(Double_t z) + { + mZLength = z; + }; + + void setChipType(Int_t tp) + { + mChipTypeID = tp; + } + + void setBuildLevel(Int_t buildLevel) + { + mBuildLevel = buildLevel; + } + + void setStaveModel(o2::ecl::Detector::Model model) + { + mStaveModel = model; + } + + /// Creates the actual Layer and places inside its mother volume + /// \param motherVolume the TGeoVolume owing the volume structure + virtual void createLayer(TGeoVolume* motherVolume); + + private: + /// Creates the actual Layer and places inside its mother volume + /// A so-called "turbo" layer is a layer where staves overlap in phi + /// User can set width and tilt angle, no check is performed here + /// to avoid volume overlaps + /// \param motherVolume The TGeoVolume owing the volume structure + void createLayerTurbo(TGeoVolume* motherVolume); + + /// Computes the inner radius of the air container for the Turbo configuration + /// as the radius of either the circle tangent to the stave or the circle + /// passing for the stave's lower vertex. Returns the radius of the container + /// if >0, else flag to use the lower vertex + Double_t radiusOmTurboContainer(); + + /// Creates the actual Stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStave(const TGeoManager* mgr = gGeoManager); + + // TGeoVolume* createChip(Double_t x, Double_t z, const TGeoManager *mgr=gGeoManager); + + /// Creates the IB Module: (only the chips for the time being) + /// Returns the module as a TGeoVolume + /// \param xmod, ymod, zmod X, Y, Z module half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleInnerB(Double_t x, Double_t y, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the actual Chip + /// \param xchip,ychip,zchip The chip dimensions + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createChipInnerB(Double_t x, Double_t y, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the OB Module: HIC + FPC + Carbon plate + /// Returns the module as a TGeoVolume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Inner Barrel(Here we fake the halfstave volume to have the + /// same formal geometry hierarchy as for the Outer Barrel) + /// \param xsta, ysta, zsta X, Y, Z stave half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveInnerB(Double_t x, Double_t y, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveStructInnerB(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create a dummy stave + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerBDummy(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager) const; + + /// Create the mechanical stave structure for Model 0 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB0(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 1 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB1(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 2.1 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB21(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 2.2 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB22(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure for Model 3 of TDR + /// \param xsta X length + /// \param zsta Z length + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB3(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Creation of the mechanical stave structure for the Outer Barrel as in v0 + /// (we fake the module and halfstave volumes to have always + /// the same formal geometry hierarchy) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterB0(const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical half stave structure or the Outer Barrel as in TDR + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterB1(const TGeoManager* mgr = gGeoManager); + + /// Create the space frame for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the space frame for the Outer Barrel (Model 1) + /// Returns a TGeoVolume with the Space Frame of a stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB1(const TGeoManager* mgr = gGeoManager); + + /// Creates the V-shaped sides of the OB space frame (from a similar method with same + /// name and function in V11GeometrySDD class by L.Gaudichet) + TGeoArb8* createStaveSide(const char* name, Double_t dz, Double_t angle, Double_t xSign, Double_t L, Double_t H, + Double_t l); + + /// Help method to create a TGeoCombiTrans matrix from a similar method with same name and + /// function in V11GeometrySDD class by L.Gaudichet) + /// Returns the TGeoCombiTrans which make a translation in y and z and a rotation in phi + /// in the global coord system. If planeSym = true, the rotation places the object + /// symetrically (with respect to the transverse plane) to its position in the + /// case planeSym = false + TGeoCombiTrans* createCombiTrans(const char* name, Double_t dy, Double_t dz, Double_t dphi, + Bool_t planeSym = kFALSE); + + /// Help method to add a translation to a TGeoCombiTrans matrix (from a similar method + /// with same name and function in V11GeometrySDD class by L.Gaudichet) + void addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx = 0, Double_t dy = 0, Double_t dz = 0) const; + + Int_t mLayerNumber; ///< Current layer number + Double_t mPhi0; ///< lab phi of 1st stave, in degrees!!! + Double_t mLayerRadius; ///< Inner radius of this layer + Double_t mZLength; ///< Z length of this layer + Double_t mSensorThickness; ///< Sensor thickness + Double_t mStaveThickness; ///< Stave thickness + Double_t mStaveWidth; ///< Stave width (for turbo layers only) + Double_t mStaveTilt; ///< Stave tilt angle (for turbo layers only) in degrees + Int_t mNumberOfStaves; ///< Number of staves in this layer + Int_t mNumberOfModules; ///< Number of modules per container if defined (HalfStave, Stave, whatever is + ///< container) + Int_t mNumberOfChips; ///< Number chips per container (module, HalfStave, Stave, whatever is + /// container) + Int_t mHierarchy[kNHLevels]; ///< array to query number of staves, hstaves, modules, chips per its parent volume + + UInt_t mChipTypeID; ///< detector type id + Bool_t mIsTurbo; ///< True if this layer is a "turbo" layer + Int_t mBuildLevel; ///< Used for material studies + + Detector::Model mStaveModel; ///< The stave model + + // Parameters for the geometry + + // General Parameters + static const Int_t sNumberOmInnerLayers; ///< Number of IB Layers + + static const Double_t sDefaultSensorThick; ///< Default sensor thickness + static const Double_t sDefaultStaveThick; ///< Default stave thickness + + // Inner Barrel Parameters + static const Int_t sIBChipsPerRow; ///< IB chips per row in module + static const Int_t sIBNChipRows; ///< IB chip rows in module + + // Outer Barrel Parameters + static const Int_t sOBChipsPerRow; ///< OB chips per row in module + static const Int_t sOBNChipRows; ///< OB chip rows in module + + static const Double_t sOBHalfStaveWidth; ///< OB Half Stave Width + static const Double_t sOBModuleWidth; ///< OB Module Width + static const Double_t sOBModuleGap; ///< Gap between OB modules + static const Double_t sOBChipXGap; ///< Gap between OB chips on X + static const Double_t sOBChipZGap; ///< Gap between OB chips on Z + static const Double_t sOBFlexCableAlThick; ///< Thickness of FPC Aluminum + static const Double_t sOBFlexCableKapThick; ///< Thickness of FPC Kapton + static const Double_t sOBBusCableAlThick; ///< Thickness of Bus Aluminum + static const Double_t sOBBusCableKapThick; ///< Thickness of Bus Kapton + static const Double_t sOBCarbonPlateThick; ///< OB Carbon Plate Thickness + static const Double_t sOBColdPlateThick; ///< OB Cold Plate Thickness + static const Double_t sOBGlueThick; ///< OB Glue total Thickness + static const Double_t sOBModuleZLength; ///< OB Chip Length along Z + static const Double_t sOBHalfStaveYTrans; ///< OB half staves Y transl. + static const Double_t sOBHalfStaveXOverlap; ///< OB half staves X overlap + static const Double_t sOBGraphiteFoilThick; ///< OB graphite foil thickness + static const Double_t sOBCoolTubeInnerD; ///< OB cooling inner diameter + static const Double_t sOBCoolTubeThick; ///< OB cooling tube thickness + static const Double_t sOBCoolTubeXDist; ///< OB cooling tube separation + + static const Double_t sOBSpaceFrameWidth; ///< OB Space Frame Width + static const Double_t sOBSpaceFrameTotHigh; ///< OB Total Y Height + static const Double_t sOBSFrameBeamRadius; ///< OB Space Frame Beam Radius + static const Double_t sOBSpaceFrameLa; ///< Parameters defining... + static const Double_t sOBSpaceFrameHa; ///< ...the V side shape... + static const Double_t sOBSpaceFrameLb; ///< ...of the carbon... + static const Double_t sOBSpaceFrameHb; ///< ...OB Space Frame + static const Double_t sOBSpaceFrameL; ///< OB SF + static const Double_t sOBSFBotBeamAngle; ///< OB SF bottom beam angle + static const Double_t sOBSFrameBeamSidePhi; ///< OB SF side beam angle + + ClassDefOverride(V1Layer, 0); // ITS v1 geometry +}; +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V3Layer.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V3Layer.h new file mode 100644 index 0000000000000..3e7851e6a4bf6 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V3Layer.h @@ -0,0 +1,551 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 V3Layer.h +/// \brief Definition of the V3Layer class +/// \author Mario Sitta +/// \author Chinorat Kobdaj (kobdaj@g.sut.ac.th) + +#ifndef ALICEO2_ENDCAPSLAYERS_UPGRADEV3LAYER_H_ +#define ALICEO2_ENDCAPSLAYERS_UPGRADEV3LAYER_H_ + +#include // for gGeoManager +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc +#include "EC0Simulation/V11Geometry.h" // for V11Geometry +#include "EC0Simulation/Detector.h" // for Detector, Detector::Model + +class TGeoXtru; + +class TGeoCombiTrans; + +class TGeoVolume; // lines 15-15 + +namespace o2 +{ +namespace ecl +{ + +/// This class defines the Geometry for the ITS using TGeo. This is a work class used +/// to study different configurations during the development of the new ITS structure +class V3Layer : public V11Geometry +{ + + public: + enum { kStave, + kHalfStave, + kModule, + kChip, + kNHLevels }; + + // Default constructor + V3Layer(); + + /// Constructor setting layer number and debugging level + /// for a "turbo" layer (i.e. where staves overlap in phi) + V3Layer(Int_t lay, Bool_t turbo = kFALSE, Int_t debug = 0); + + /// Copy constructor + V3Layer(const V3Layer&) = default; + + /// Assignment operator + V3Layer& operator=(const V3Layer&) = default; + + /// Default destructor + ~V3Layer() override; + + Bool_t hasGammaConversionRods() const { return mAddGammaConv; }; + + Bool_t isTurbo() const { return mIsTurbo; }; + + Double_t getChipThick() const { return mChipThickness; }; + + Double_t getStaveTilt() const { return mStaveTilt; }; + + Double_t getStaveWidth() const { return mStaveWidth; }; + + Double_t getSensorThick() const { return mSensorThickness; }; + + Double_t getNumberOfStaves() const { return mNumberOfStaves; }; + + Double_t getNumberOfChips() const { return mNumberOfChips; }; + + Double_t getRadius() const { return mLayerRadius; }; + + Double_t getPhi0() const { return mPhi0; }; + + Double_t getIBModuleZLength() const { return mIBModuleZLength; }; + + Double_t getOBModuleZLength() const { return mOBModuleZLength; }; + + Int_t getChipType() const { return mChipTypeID; } + + Int_t getNumberOfStavesPerParent() const { return mHierarchy[kStave]; } + + Int_t getNumberOfHalfStavesPerParent() const { return mHierarchy[kHalfStave]; } + + Int_t getNumberOfModulesPerParent() const { return mHierarchy[kModule]; } + + Int_t getNumberOfChipsPerParent() const { return mHierarchy[kChip]; } + + Int_t getBuildLevel() const { return mBuildLevel; } + + Detector::Model getStaveModel() const { return mStaveModel; } + + void setChipThick(Double_t t) { mChipThickness = t; }; + + /// Gets the Gamma Conversion Rod diameter + Double_t getGammaConversionRodDiam(); + + /// Gets the Gamma Conversion Rod X position + Double_t getGammaConversionRodXPos(); + + /// Sets the Stave tilt angle (for turbo layers only) + /// \param t The stave tilt angle + void setStaveTilt(Double_t t); + + /// Sets the Stave width (for turbo layers only) + /// \param w The stave width + void setStaveWidth(Double_t w); + + void setSensorThick(Double_t t) { mSensorThickness = t; }; + + void setNumberOfStaves(Int_t n) { mHierarchy[kStave] = mNumberOfStaves = n; }; + + /// Sets the number of units in a stave: + /// for the Inner Barrel: the number of chips per stave + /// for the Outer Barrel: the number of modules per half stave + /// \param u the number of units + void setNumberOfUnits(Int_t u); + + void setRadius(Double_t r) { mLayerRadius = r; }; + + void setPhi0(Double_t phi) { mPhi0 = phi; } + + void setChipType(Int_t tp) { mChipTypeID = tp; } + + void setBuildLevel(Int_t buildLevel) { mBuildLevel = buildLevel; } + + void setStaveModel(o2::ecl::Detector::Model model) { mStaveModel = model; } + + /// Adds the Gamma Conversion Rods to the geometry + /// \param diam the diameter of each rod + /// \param xPos the X position of each rod + void addGammaConversionRods(const Double_t diam, const Double_t xPos); + + /// Creates the actual Layer and places inside its mother volume + /// \param motherVolume the TGeoVolume owing the volume structure + virtual void createLayer(TGeoVolume* motherVolume); + + private: + /// Creates the actual Layer and places inside its mother volume + /// A so-called "turbo" layer is a layer where staves overlap in phi + /// User can set width and tilt angle, no check is performed here + /// to avoid volume overlaps + /// \param motherVolume The TGeoVolume owing the volume structure + void createLayerTurbo(TGeoVolume* motherVolume); + + /// Computes the inner radius of the air container for the Turbo configuration + /// as the radius of either the circle tangent to the stave or the circle + /// passing for the stave's lower vertex. Returns the radius of the container + /// if >0, else flag to use the lower vertex + Double_t radiusOmTurboContainer(); + + /// Creates the actual Stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStave(const TGeoManager* mgr = gGeoManager); + + /// Creates the IB Module: (only the chips for the time being) + /// Returns the module as a TGeoVolume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleInnerB(const TGeoManager* mgr = gGeoManager); + + /// Creates the OB Module: HIC + FPC + Carbon plate + /// Returns the module as a TGeoVolume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createModuleOuterB(const TGeoManager* mgr = gGeoManager); + + /// Creates the IB FPC Aluminum Ground layer + /// Returns the layer as a TGeoVolume + /// \param x, z X, Z layer half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBFPCAlGnd(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the IB FPC Aluminum Anode layer + /// Returns the layer as a TGeoVolume + /// \param x, z X, Z layer half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBFPCAlAnode(Double_t x, Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the IB FPC capacitors + /// \param modvol The IB module mother volume + /// \param zchip The chip half Z length + /// \param yzero The Y base position of capacitors + /// \param mgr The GeoManager (used only to get the proper material) + void createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yzero, const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Inner Barrel(Here we fake the halfstave volume to have the + /// same formal geometry hierarchy as for the Outer Barrel) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveInnerB(const TGeoManager* mgr = gGeoManager); + + /// Create the mechanical stave structure + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveStructInnerB(const TGeoManager* mgr = gGeoManager); + + /// Create a dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the mechanical stave structure for Model 4 of TDR + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelInnerB4(const TGeoManager* mgr = gGeoManager); + + /// Create the Inner Barrel End Stave connectors + /// \param mgr The GeoManager (used only to get the proper material) + void createIBConnectors(const TGeoManager* mgr = gGeoManager); + + /// Create the Inner Barrel End Stave connectors on Side A + /// \param mgr The GeoManager (used only to get the proper material) + void createIBConnectorsASide(const TGeoManager* mgr = gGeoManager); + + /// Create the Inner Barrel End Stave connectors on Side C + /// \param mgr The GeoManager (used only to get the proper material) + void createIBConnectorsCSide(const TGeoManager* mgr = gGeoManager); + + /// Creates the OB FPC Copper Ground layer + /// Returns the FPC as a TGeoVolume + /// \param z Z module half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createOBFPCCuGnd(Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the OB FPC Copper Signal layer + /// Returns the FPC as a TGeoVolume + /// \param z Z module half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createOBFPCCuSig(Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Creates the OB Power and Bias Buses + /// Returns a TGeoVolume with both buses + /// \param z Z stave half lengths + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createOBPowerBiasBuses(Double_t z, const TGeoManager* mgr = gGeoManager); + + /// Create the chip stave for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the mechanical half stave structure or the Outer Barrel as in TDR + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createStaveModelOuterB2(const TGeoManager* mgr = gGeoManager); + + /// Create the Cold Plate connectors + void createOBColdPlateConnectors(); + + /// Create the A-Side end-stave connectors for OB staves + void createOBColdPlateConnectorsASide(); + + /// Create the C-Side end-stave connectors for OB staves + void createOBColdPlateConnectorsCSide(); + + /// Create the space frame for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB(const TGeoManager* mgr = gGeoManager); + + /// Create dummy stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterBDummy(const TGeoManager* mgr = gGeoManager) const; + + /// Create the space frame for the Outer Barrel (Model 2) + /// Returns a TGeoVolume with the Space Frame of a stave + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createSpaceFrameOuterB2(const TGeoManager* mgr = gGeoManager); + + /// Create the space frame building blocks for the Outer Barrel + /// \param mgr The GeoManager (used only to get the proper material) + void createOBSpaceFrameObjects(const TGeoManager* mgr = gGeoManager); + + /// Create the space frame connectors for the Outer Barrel + /// \param mother the mother volume where to put the connector + /// \param ymot,zmot the Y and Z position in the mother volume + /// \param sideA true for side A, false for side C + /// \param mgr The GeoManager (used only to get the proper material) + void createOBSpaceFrameConnector(TGeoVolume* mother, const Double_t ymot, const Double_t zmot, const Bool_t sideA, const TGeoManager* mgr = gGeoManager); + + /// Creates the V-shaped sides of the OB space frame (from a similar method with same + /// name and function in V11GeometrySDD class by L.Gaudichet) + /// \param name The volume name + /// \param dz The half Z length + /// \param alpha The first rotation angle + /// \param beta The second rotation angle + /// \param L The stave length + /// \param H The stave height + /// \param top True to create the top corner, False to create the side one + TGeoXtru* createStaveSide(const char* name, Double_t dz, Double_t alpha, Double_t beta, Double_t L, Double_t H, + Bool_t top); + + /// Help method to create a TGeoCombiTrans matrix from a similar method with same name and + /// function in V11GeometrySDD class by L.Gaudichet) + /// Returns the TGeoCombiTrans which make a translation in y and z and a rotation in phi + /// in the global coord system. If planeSym = true, the rotation places the object + /// symetrically (with respect to the transverse plane) to its position in the + /// case planeSym = false + TGeoCombiTrans* createCombiTrans(const char* name, Double_t dy, Double_t dz, Double_t dphi, Bool_t planeSym = kFALSE); + + /// Help method to add a translation to a TGeoCombiTrans matrix (from a similar method + /// with same name and function in V11GeometrySDD class by L.Gaudichet) + void addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx = 0, Double_t dy = 0, Double_t dz = 0) const; + + Int_t mLayerNumber; ///< Current layer number + Double_t mPhi0; ///< lab phi of 1st stave, in degrees!!! + Double_t mLayerRadius; ///< Inner radius of this layer + Double_t mSensorThickness; ///< Sensor thickness + Double_t mChipThickness; ///< Chip thickness + Double_t mStaveWidth; ///< Stave width (for turbo layers only) + Double_t mStaveTilt; ///< Stave tilt angle (for turbo layers only) in degrees + Int_t mNumberOfStaves; ///< Number of staves in this layer + Int_t mNumberOfModules; ///< Number of modules per container if defined (HalfStave, Stave, whatever is + ///< container) + Int_t mNumberOfChips; ///< Number chips per container (module, HalfStave, Stave, whatever is + /// container) + Int_t mHierarchy[kNHLevels]; ///< array to query number of staves, hstaves, modules, chips per its parent volume + + UInt_t mChipTypeID; ///< detector type id + Bool_t mIsTurbo; ///< True if this layer is a "turbo" layer + Int_t mBuildLevel; ///< Used for material studies + + Detector::Model mStaveModel; ///< The stave model + + Bool_t mAddGammaConv; ///< True to add Gamma Conversion Rods + Double_t mGammaConvDiam; ///< Gamma Conversion Rod Diameter + Double_t mGammaConvXPos; ///< Gamma Conversion Rod X Position + + // Dimensions computed during geometry build-up + Double_t mIBModuleZLength; ///< IB Module Length along Z + Double_t mOBModuleZLength; ///< OB Module Length along Z + + // Parameters for the geometry + + // General Parameters + static const Int_t sNumberOfInnerLayers; ///< Number of IB Layers + + static const Double_t sDefaultSensorThick; ///< Default sensor thickness + static const Double_t sMetalLayerThick; ///< Metal layer thickness + + // Inner Barrel Parameters + static const Int_t sIBChipsPerRow; ///< IB chips per row in module + static const Int_t sIBNChipRows; ///< IB chip rows in module + static const Double_t sIBChipZGap; ///< Gap between IB chips on Z + + static const Double_t sIBModuleZLength; ///< IB Module Length along Z + static const Double_t sIBFPCWiderXPlus; ///< FPC protrusion at X>0 + static const Double_t sIBFPCWiderXNeg; ///< FPC protrusion at X<0 + static const Double_t sIBFlexCableAlThick; ///< Thickness of FPC Aluminum + static const Double_t sIBFPCAlGNDWidth; ///< Width of total FPC Al Gnd + static const Double_t sIBFPCAlAnodeWidth1; ///< Width of FPC Al Anode + static const Double_t sIBFPCAlAnodeWidth2; ///< Width of FPC Al Anode + static const Double_t sIBFlexCableKapThick; ///< Thickness of FPC Kapton + static const Double_t sIBFlexCablePolyThick; ///< Thickness of FPC Coverlay + static const Double_t sIBFlexCapacitorXWid; ///< IB capaictor X width + static const Double_t sIBFlexCapacitorYHi; ///< IB capaictor Y height + static const Double_t sIBFlexCapacitorZLen; ///< IB capaictor Z length + static const Double_t sIBColdPlateWidth; ///< IB cold plate X width + static const Double_t sIBColdPlateZLen; ///< IB cold plate Z length + static const Double_t sIBGlueThick; ///< IB glue thickness + static const Double_t sIBCarbonFleeceThick; ///< IB carbon fleece thickness + static const Double_t sIBCarbonPaperThick; ///< IB Carbon Paper Thickness + static const Double_t sIBCarbonPaperWidth; ///< IB Carbon Paper X Width + static const Double_t sIBCarbonPaperZLen; ///< IB Carbon Paper Z Length + static const Double_t sIBK13D2UThick; ///< IB k13d2u prepreg thickness + static const Double_t sIBCoolPipeInnerD; ///< IB cooling inner diameter + static const Double_t sIBCoolPipeThick; ///< IB cooling pipe thickness + static const Double_t sIBCoolPipeXDist; ///< IB cooling pipe separation + static const Double_t sIBCoolPipeZLen; ///< IB cooling pipe length + static const Double_t sIBTopVertexWidth1; ///< IB TopVertex width + static const Double_t sIBTopVertexWidth2; ///< IB TopVertex width + static const Double_t sIBTopVertexHeight; ///< IB TopVertex height + static const Double_t sIBTopVertexAngle; ///< IB TopVertex aperture angle + static const Double_t sIBSideVertexWidth; ///< IB SideVertex width + static const Double_t sIBSideVertexHeight; ///< IB SideVertex height + static const Double_t sIBTopFilamentSide; ///< IB TopFilament side + static const Double_t sIBTopFilamentAlpha; ///< IB TopFilament angle + static const Double_t sIBTopFilamentInterZ; ///< IB TopFilament Z interdist + static const Double_t sIBEndSupportThick; ///< IB end support thickness + static const Double_t sIBEndSupportZLen; ///< IB end support length + static const Double_t sIBEndSupportXUp; ///< IB end support X up wide + static const Double_t sIBEndSupportOpenPhi; ///< IB end support opening phi + + static const Double_t sIBConnectorXWidth; ///< IB Connectors Width + static const Double_t sIBConnectorYTot; ///< IB Connectors total height + static const Double_t sIBConnectBlockZLen; ///< IB Connector Block Z length + static const Double_t sIBConnBodyYHeight; ///< IB Connector Body Y height + static const Double_t sIBConnTailYMid; ///< IB Connector Tail Y mid pt + static const Double_t sIBConnTailYShift; ///< IB Connector Tail Y shift + static const Double_t sIBConnTailZLen; ///< IB Connector Tail Z length + static const Double_t sIBConnTailOpenPhi; ///< IB Connector Tail Angle + static const Double_t sIBConnRoundHoleD; ///< IB Connector Hole diameter + static const Double_t sIBConnRoundHoleZ; ///< IB Connector Hole Z pos + static const Double_t sIBConnSquareHoleX; ///< IB Connector Hole X len + static const Double_t sIBConnSquareHoleZ; ///< IB Connector Hole Z len + static const Double_t sIBConnSquareHoleZPos; ///< IB Connector Hole Z pos + static const Double_t sIBConnInsertHoleD; ///< IB Connector Insert diam + static const Double_t sIBConnInsertHoleZPos; ///< IB Connector Insert Z pos + static const Double_t sIBConnTubeHole1D; ///< IB Connector Tube1 diam + static const Double_t sIBConnTubeHole1ZLen; ///< IB Connector Tube1 Z len + static const Double_t sIBConnTubeHole1ZLen2; ///< IB Conn Tube1 Z len 2'side + static const Double_t sIBConnTubeHole2D; ///< IB Connector Tube2 diam + static const Double_t sIBConnTubeHole3XPos; ///< IB Connector Tube3 X pos + static const Double_t sIBConnTubeHole3ZPos; ///< IB Connector Tube3 Z pos + static const Double_t sIBConnTubesXDist; ///< IB Connector Tubes X dist + static const Double_t sIBConnTubesYPos; ///< IB Connector Tubes Y pos + static const Double_t sIBConnInsertD; ///< IB Connector Insert diam + static const Double_t sIBConnInsertHeight; ///< IB Connector Insert height + static const Double_t sIBConnSideHole1D; ///< IB Conn Side 1st hole D + static const Double_t sIBConnSideHole1YPos; ///< IB Conn Side 1st hole Y pos + static const Double_t sIBConnSideHole1ZPos; ///< IB Conn Side 1st hole Z pos + static const Double_t sIBConnSideHole1XWid; ///< IB Conn Side 1st hole X wid + static const Double_t sIBConnSideHole2YPos; ///< IB Conn Side 2nd hole Y pos + static const Double_t sIBConnSideHole2ZPos; ///< IB Conn Side 2nd hole Z pos + static const Double_t sIBConnSideHole2XWid; ///< IB Conn Side 2nd hole X wid + static const Double_t sIBConnSideHole2YWid; ///< IB Conn Side 2nd hole Y wid + static const Double_t sIBConnSideHole2ZWid; ///< IB Conn Side 2nd hole Z wid + static const Double_t sIBConnectAFitExtD; ///< IB ConnectorA Fitting ext D + static const Double_t sIBConnectAFitIntD; ///< IB ConnectorA Fitting int D + static const Double_t sIBConnectAFitZLen; ///< IB ConnectorA Fitting Z len + static const Double_t sIBConnectAFitZOut; ///< IB ConnectorA Fitting Z Out + static const Double_t sIBConnPlugInnerD; ///< IB Connector Plug int diam + static const Double_t sIBConnPlugTotLen; ///< IB Connector Plug tot len + static const Double_t sIBConnPlugInnerLen; ///< IB Connector Plug int len + + static const Double_t sIBStaveHeight; ///< IB Stave Total Y Height + + // Outer Barrel Parameters + static const Int_t sOBChipsPerRow; ///< OB chips per row in module + static const Int_t sOBNChipRows; ///< OB chip rows in module + + static const Double_t sOBChipThickness; ///< Default OB chip thickness + + static const Double_t sOBHalfStaveWidth; ///< OB Half Stave Width + static const Double_t sOBModuleGap; ///< Gap between OB modules + static const Double_t sOBChipXGap; ///< Gap between OB chips on X + static const Double_t sOBChipZGap; ///< Gap between OB chips on Z + static const Double_t sOBFlexCableXWidth; ///< OB FPC X width + static const Double_t sOBFlexCableAlThick; ///< Thickness of FPC Aluminum + static const Double_t sOBFlexCableKapThick; ///< Thickness of FPC Kapton + static const Double_t sOBFPCSoldMaskThick; ///< Thickness of FPC Solder Mask + static const Double_t sOBFPCCopperThick; ///< Thickness of FPC Copper + static const Double_t sOBFPCCuAreaFracGnd; ///< Fraction of Cu on Gnd FPC + static const Double_t sOBFPCCuAreaFracSig; ///< Fraction of Cu on Sig FPC + static const Double_t sOBGlueFPCThick; ///< Thickness of Glue to FPC + static const Double_t sOBGlueColdPlThick; ///< Thickness of Glue to Cold Pl + static const Double_t sOBPowerBusXWidth; ///< OB Power Bus X width + static const Double_t sOBPowerBusAlThick; ///< OB Power Bus Al thickness + static const Double_t sOBPowerBusAlFrac; ///< Fraction of Al on OB PB + static const Double_t sOBPowerBusDielThick; ///< OB Power Bus Dielectric thick + static const Double_t sOBPowerBusKapThick; ///< OB Power Bus Kapton thick + static const Double_t sOBBiasBusXWidth; ///< OB Bias Bus X width + static const Double_t sOBBiasBusAlThick; ///< OB Bias Bus Al thickness + static const Double_t sOBBiasBusAlFrac; ///< Fraction of Al on OB BB + static const Double_t sOBBiasBusDielThick; ///< OB Bias Bus Dielectric thick + static const Double_t sOBBiasBusKapThick; ///< OB Bias Bus Kapton thick + static const Double_t sOBColdPlateXWidth; ///< OB Cold Plate X width + static const Double_t sOBColdPlateZLenML; ///< OB ML Cold Plate Z length + static const Double_t sOBColdPlateZLenOL; ///< OB OL Cold Plate Z length + static const Double_t sOBColdPlateThick; ///< OB Cold Plate Thickness + static const Double_t sOBHalfStaveYPos; ///< OB half staves Y position + static const Double_t sOBHalfStaveYTrans; ///< OB half staves Y transl. + static const Double_t sOBHalfStaveXOverlap; ///< OB half staves X overlap + static const Double_t sOBGraphiteFoilThick; ///< OB graphite foil thickness + static const Double_t sOBCarbonFleeceThick; ///< OB carbon fleece thickness + static const Double_t sOBCoolTubeInnerD; ///< OB cooling inner diameter + static const Double_t sOBCoolTubeThick; ///< OB cooling tube thickness + static const Double_t sOBCoolTubeXDist; ///< OB cooling tube separation + + static const Double_t sOBCPConnectorXWidth; ///< OB Cold Plate Connect Width + static const Double_t sOBCPConnBlockZLen; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnBlockYHei; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnHollowZLen; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnHollowYHei; ///< OB CP Connect Block Z len + static const Double_t sOBCPConnSquareHoleX; ///< OB Conn Square Hole X len + static const Double_t sOBCPConnSquareHoleZ; ///< OB Conn Square Hole Z len + static const Double_t sOBCPConnSqrHoleZPos; ///< OB Conn Square Hole Z pos + static const Double_t sOBCPConnSqrInsertRZ; ///< OB Conn Square Insert RZpos + static const Double_t sOBCPConnRoundHoleD; ///< OB Conn Round Hole diam + static const Double_t sOBCPConnRndHoleZPos; ///< OB Conn Round Hole Z pos + static const Double_t sOBCPConnTubesXDist; ///< OB Connector Tubes X dist + static const Double_t sOBCPConnTubesYPos; ///< OB Connector Tubes Y pos + static const Double_t sOBCPConnTubeHole1D; ///< OB Connector Tube1 diam + static const Double_t sOBCPConnTubeHole1Z; ///< OB Connector Tube1 Z len + static const Double_t sOBCPConnTubeHole2D; ///< OB Connector Tube2 diam + static const Double_t sOBCPConnFitHoleD; ///< OB Connector Fit Hole diam + static const Double_t sOBCPConnTubeHole3XP; ///< OB Connector Tube3 X pos + static const Double_t sOBCPConnTubeHole3ZP; ///< OB Connector Tube3 Z pos + static const Double_t sOBCPConnInstZThick; ///< OB Connector Insert height + static const Double_t sOBCPConnInsertYHei; ///< OB Connector Insert height + static const Double_t sOBCPConnAFitExtD; ///< OB ConnectorA Fitting ext D + static const Double_t sOBCPConnAFitThick; ///< OB ConnectorA Fitting thick + static const Double_t sOBCPConnAFitZLen; ///< OB ConnectorA Fitting Z len + static const Double_t sOBCPConnAFitZIn; ///< OB ConnectorA Fitting Z ins + static const Double_t sOBCPConnPlugInnerD; ///< OB Connector Plug int diam + static const Double_t sOBCPConnPlugTotLen; ///< OB Connector Plug tot le + static const Double_t sOBCPConnPlugThick; ///< OB Connector Plug thickness + + static const Double_t sOBSpaceFrameZLen[2]; ///< OB Space Frame Length + static const Int_t sOBSpaceFrameNUnits[2]; ///< OB Number of SF Units + static const Double_t sOBSpaceFrameUnitLen; ///< OB Space Frame Unit length + static const Double_t sOBSpaceFrameWidth; ///< OB Space Frame Width + static const Double_t sOBSpaceFrameHeight; ///< OB Space Frame Height + static const Double_t sOBSpaceFrameTopVL; ///< Parameters defining... + static const Double_t sOBSpaceFrameTopVH; ///< ...the Top V shape + static const Double_t sOBSpaceFrameSideVL; ///< Parameters defining... + static const Double_t sOBSpaceFrameSideVH; ///< ...the Side V shape + static const Double_t sOBSpaceFrameVAlpha; ///< Angles of aperture... + static const Double_t sOBSpaceFrameVBeta; ///< ...of the V shapes + static const Double_t sOBSFrameBaseRibDiam; ///< OB SFrame Base Rib Diam + static const Double_t sOBSFrameBaseRibPhi; ///< OB SF base beam angle + static const Double_t sOBSFrameSideRibDiam; ///< OB SFrame Side Rib Diam + static const Double_t sOBSFrameSideRibPhi; ///< OB SF side beam angle + static const Double_t sOBSFrameULegLen; ///< OB SF U-Leg length + static const Double_t sOBSFrameULegWidth; ///< OB SF U-Leg width + static const Double_t sOBSFrameULegHeight1; ///< OB SF U-Leg height + static const Double_t sOBSFrameULegHeight2; ///< OB SF U-Leg height + static const Double_t sOBSFrameULegThick; ///< OB SF U-Leg thickness + static const Double_t sOBSFrameULegXPos; ///< OB SF U-Leg X position + static const Double_t sOBSFrameConnWidth; ///< OB SF Connector width + static const Double_t sOBSFrameConnTotLen; ///< OB SF Connector total length + static const Double_t sOBSFrameConnTotHei; ///< OB SF Connector total height + static const Double_t sOBSFrameConnTopLen; ///< OB SF Connector top length + static const Double_t sOBSFrameConnInsWide; ///< OB SF Connector insert width + static const Double_t sOBSFrameConnInsBase; ///< OB SF Connector insert base + static const Double_t sOBSFrameConnInsHei; ///< OB SF Connector insert height + static const Double_t sOBSFrameConnHoleZPos; ///< OB SF Connector holes Z pos + static const Double_t sOBSFrameConnHoleZDist; ///< OB SF Connector holes Zdist + static const Double_t sOBSFrameConnTopHoleD; ///< OB SF Connec top hole diam + static const Double_t sOBSFrConnTopHoleXDist; ///< OB SF Connec top hole Xdist + static const Double_t sOBSFrameConnAHoleWid; ///< OB SF Connect A hole width + static const Double_t sOBSFrameConnAHoleLen; ///< OB SF Connect A hole length + static const Double_t sOBSFrConnASideHoleD; ///< OB SF Conn A side hole dia + static const Double_t sOBSFrConnASideHoleL; ///< OB SF Conn A side hole len + static const Double_t sOBSFrConnASideHoleY; ///< OB SF Conn A side hole Y pos + static const Double_t sOBSFrameConnCHoleZPos; ///< OB SF Connect C hole Z pos + static const Double_t sOBSFrConnCHoleXDist; ///< OB SF Conn C top hole Xdist + static const Double_t sOBSFrConnCTopHoleD; ///< OB SF Conn C top hole dia + static const Double_t sOBSFrameConnInsHoleD; ///< OB SF Connec insert hole dia + static const Double_t sOBSFrameConnInsHoleX; ///< OB SF Connec insert hole X + + ClassDefOverride(V3Layer, 0); // ITS v3 geometry +}; +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V3Services.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V3Services.h new file mode 100644 index 0000000000000..3960765b6744e --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/include/EC0Simulation/V3Services.h @@ -0,0 +1,184 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 V3Services.h +/// \brief Definition of the V3Services class +/// \author Mario Sitta +/// \author Parinya Namwongsa + +#ifndef ALICEO2_ENDCAPSLAYERS_UPGRADEV3SERVICES_H_ +#define ALICEO2_ENDCAPSLAYERS_UPGRADEV3SERVICES_H_ + +#include // for gGeoManager +#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc +#include "EC0Simulation/V11Geometry.h" // for V11Geometry +#include "EC0Simulation/Detector.h" // for Detector, Detector::Model + +class TGeoXtru; + +class TGeoCombiTrans; + +class TGeoVolume; + +namespace o2 +{ +namespace ecl +{ + +/// This class defines the Geometry for the Services of the ITS Upgrade using TGeo. +class V3Services : public V11Geometry +{ + + public: + // Default constructor + V3Services(); + + /// Copy constructor + V3Services(const V3Services&) = default; + + /// Assignment operator + V3Services& operator=(const V3Services&) = default; + + /// Default destructor + ~V3Services() override; + + /// Creates the Inner Barrel End Wheels on Side A + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBEndWheelsSideA(const TGeoManager* mgr = gGeoManager); + + /// Creates the Inner Barrel End Wheels on Side C + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createIBEndWheelsSideC(const TGeoManager* mgr = gGeoManager); + + /// Creates the CYSS Assembly (i.e. the supporting half cylinder and cone) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createCYSSAssembly(const TGeoManager* mgr = gGeoManager); + + /// Creates the Middle Barrel End Wheels on Side A + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createMBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Middle Barrel End Wheels on Side C + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel End Wheels on Side A + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel End Wheels on Side C + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side A + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBConeSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side C + /// \param mother the TGeoVolume owing the volume structure + /// \param mgr The GeoManager (used only to get the proper material) + void createOBConeSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + private: + /// Creates a single Inner Barrel End Wheel on Side A + /// \param iLay the layer number + /// \param endWheel the End Wheel volume assembly + /// \param mgr The GeoManager (used only to get the proper material) + void ibEndWheelSideA(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr = gGeoManager); + + /// Creates a single Inner Barrel End Wheel on Side C + /// \param iLay the layer number + /// \param endWheel the End Wheel volume assembly + /// \param mgr The GeoManager (used only to get the proper material) + void ibEndWheelSideC(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr = gGeoManager); + + /// Creates the shape of a Rib on Side A + /// \param iLay the layer number + TGeoXtru* ibEndWheelARibShape(const Int_t iLay); + + /// Creates the CYSS cylinder of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssCylinder(const TGeoManager* mgr = gGeoManager); + + /// Creates the CYSS cone of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssCone(const TGeoManager* mgr = gGeoManager); + + /// Creates the CYSS Flange on Side A of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssFlangeSideA(const TGeoManager* mgr = gGeoManager); + + /// Creates the hollows in the CYSS Flange on Side A of the Inner Barrel + /// \param zlen the thickness of the ring where the hollows are + TString ibCreateHollowsCyssFlangeSideA(const Double_t zlen); + + /// Creates the CYSS Flange on Side C of the Inner Barrel + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* ibCyssFlangeSideC(const TGeoManager* mgr = gGeoManager); + + /// Creates a single Middle/Outer Barrel End Wheel on Side A + /// \param iLay the layer number + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obEndWheelSideA(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates a single Middle Barrel End Wheel on Side C + /// \param iLay the layer number + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates a single Outer Barrel End Wheel on Side C + /// \param iLay the layer number + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side A + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obConeSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone on Side C + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obConeSideC(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates the Outer Barrel Cone Trays on Side A + /// \param mother the volume containing the created wheel + /// \param mgr The GeoManager (used only to get the proper material) + void obConeTraysSideA(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + // Parameters + static constexpr Int_t sNumberInnerLayers = 3; ///< Number of inner layers in ITSU + static constexpr Int_t sNumberMiddlLayers = 2; ///< Number of middle layers in ITSU + static constexpr Int_t sNumberOuterLayers = 2; ///< Number of outer layers in ITSU + + // Common parameters for IB services + static const Double_t sIBWheelACZdist; ///< IB Z distance between wheels + static const Double_t sIBCYSSFlangeCZPos; ///< IB Z position of CYSS C Flange + + // Common parameters for OB services + static const Double_t sOBWheelThickness; ///< MB/OB Wheels Thickness + static const Double_t sMBWheelsZpos; ///< MB Wheels Z position + static const Double_t sOBWheelsZpos; ///< OB Wheels Z position + static const Double_t sOBConesZpos; ///< OB Cones A & C Z position + + ClassDefOverride(V3Services, 0); // ITS v3 support geometry +}; +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/Detector.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/Detector.cxx new file mode 100644 index 0000000000000..e65d390f45e95 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/Detector.cxx @@ -0,0 +1,1304 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Detector.cxx +/// \brief Implementation of the Detector class + +#include "EndCapsBase/SegmentationAlpide.h" +#include "EndCapsSimulation/Hit.h" +#include "ECLayersBase/GeometryTGeo.h" +#include "EC0Simulation/Detector.h" +#include "EC0Simulation/V3Layer.h" +#include "EC0Simulation/V3Services.h" + +#include "SimulationDataFormat/Stack.h" +#include "SimulationDataFormat/TrackReference.h" + +// FairRoot includes +#include "FairDetector.h" // for FairDetector +#include "FairLogger.h" // for LOG, LOG_IF +#include "FairRootManager.h" // for FairRootManager +#include "FairRun.h" // for FairRun +#include "FairRuntimeDb.h" // for FairRuntimeDb +#include "FairVolume.h" // for FairVolume +#include "FairRootManager.h" + +#include "TGeoManager.h" // for TGeoManager, gGeoManager +#include "TGeoTube.h" // for TGeoTube +#include "TGeoPcon.h" // for TGeoPcon +#include "TGeoVolume.h" // for TGeoVolume, TGeoVolumeAssembly +#include "TString.h" // for TString, operator+ +#include "TVirtualMC.h" // for gMC, TVirtualMC +#include "TVirtualMCStack.h" // for TVirtualMCStack + +#include // for NULL, snprintf + +class FairModule; + +class TGeoMedium; + +class TParticle; + +using std::cout; +using std::endl; + +using Segmentation = o2::endcaps::SegmentationAlpide; +using namespace o2::ecl; +using o2::endcaps::Hit; + +Detector::Detector() + : o2::base::DetImpl("EC0", kTRUE), + mTrackData(), + /* + mHitStarted(false), + mTrkStatusStart(), + mPositionStart(), + mMomentumStart(), + mEnergyLoss(), + */ + mNumberOfDetectors(-1), + mModifyGeometry(kFALSE), + mHits(o2::utils::createSimVector()), + mStaveModelInnerBarrel(kIBModel0), + mStaveModelOuterBarrel(kOBModel0) +{ +} + +static double radii2Turbo(double rMin, double rMid, double rMax, double sensW) +{ + // compute turbo angle from radii and sensor width + return TMath::ASin((rMax * rMax - rMin * rMin) / (2 * rMid * sensW)) * TMath::RadToDeg(); +} + +static void configEC0(Detector* ecl) +{ + // build EC0 upgrade detector + const int kNLr = 7; + const int kNLrInner = 3; + const int kBuildLevel = 0; + const int kSensTypeID = 0; // dummy id for Alpide sensor + + const float ChipThicknessIB = 50.e-4; + const float ChipThicknessOB = 100.e-4; + + enum { kRmn, + kRmd, + kRmx, + kNModPerStave, + kPhi0, + kNStave, + kNPar }; + // Radii are from last TDR (ALICE-TDR-017.pdf Tab. 1.1, rMid is mean value) + const double tdr5dat[kNLr][kNPar] = { + {2.24, 2.34, 2.67, 9., 16.42, 12}, // for each inner layer: rMin,rMid,rMax,NChip/Stave, phi0, nStaves + {3.01, 3.15, 3.46, 9., 12.18, 16}, + {3.78, 3.93, 4.21, 9., 9.55, 20}, + {-1, 19.6, -1, 4., 0., 24}, // for others: -, rMid, -, NMod/HStave, phi0, nStaves // 24 was 49 + {-1, 24.55, -1, 4., 0., 30}, // 30 was 61 + {-1, 34.39, -1, 7., 0., 42}, // 42 was 88 + {-1, 39.34, -1, 7., 0., 48} // 48 was 100 + }; + const int nChipsPerModule = 7; // For OB: how many chips in a row + const double zChipGap = 0.01; // For OB: gap in Z between chips + const double zModuleGap = 0.01; // For OB: gap in Z between modules + + double dzLr, rLr, phi0, turbo; + int nStaveLr, nModPerStaveLr; + + ecl->setStaveModelIB(o2::ecl::Detector::kIBModel4); + ecl->setStaveModelOB(o2::ecl::Detector::kOBModel2); + + const int kNWrapVol = 3; + const double wrpRMin[kNWrapVol] = {2.1, 19.3, 33.32}; + const double wrpRMax[kNWrapVol] = {15.4, 29.14, 46.0}; + const double wrpZSpan[kNWrapVol] = {70., 93., 163.6}; + + for (int iw = 0; iw < kNWrapVol; iw++) { + ecl->defineWrapperVolume(iw, wrpRMin[iw], wrpRMax[iw], wrpZSpan[iw]); + } + + for (int idLr = 0; idLr < kNLr; idLr++) { + rLr = tdr5dat[idLr][kRmd]; + phi0 = tdr5dat[idLr][kPhi0]; + + nStaveLr = TMath::Nint(tdr5dat[idLr][kNStave]); + nModPerStaveLr = TMath::Nint(tdr5dat[idLr][kNModPerStave]); + int nChipsPerStaveLr = nModPerStaveLr; + if (idLr >= kNLrInner) { + ecl->defineLayer(idLr, phi0, rLr, nStaveLr, nModPerStaveLr, ChipThicknessOB, Segmentation::SensorLayerThickness, + kSensTypeID, kBuildLevel); + } else { + turbo = radii2Turbo(tdr5dat[idLr][kRmn], rLr, tdr5dat[idLr][kRmx], Segmentation::SensorSizeRows); + ecl->defineLayerTurbo(idLr, phi0, rLr, nStaveLr, nChipsPerStaveLr, Segmentation::SensorSizeRows, turbo, + ChipThicknessIB, Segmentation::SensorLayerThickness, kSensTypeID, kBuildLevel); + } + } +} + +Detector::Detector(Bool_t active) + : o2::base::DetImpl("EC0", active), + mTrackData(), + /* + mHitStarted(false), + mTrkStatusStart(), + mPositionStart(), + mMomentumStart(), + mEnergyLoss(), + */ + mNumberOfDetectors(-1), + mModifyGeometry(kFALSE), + mHits(o2::utils::createSimVector()), + mStaveModelInnerBarrel(kIBModel0), + mStaveModelOuterBarrel(kOBModel0) +{ + + for (Int_t j = 0; j < sNumberLayers; j++) { + mLayerName[j].Form("%s%d", GeometryTGeo::getEC0SensorPattern(), j); // See V3Layer + } + + if (sNumberLayers > 0) { // if not, we'll Fatal-ize in CreateGeometry + for (Int_t j = 0; j < sNumberLayers; j++) { + mLayerPhi0[j] = 0; + mLayerRadii[j] = 0.; + mStavePerLayer[j] = 0; + mUnitPerStave[j] = 0; + mChipThickness[j] = 0.; + mStaveWidth[j] = 0.; + mStaveTilt[j] = 0.; + mDetectorThickness[j] = 0.; + mChipTypeID[j] = 0; + mBuildLevel[j] = 0; + mGeometry[j] = nullptr; + } + } + mServicesGeometry = nullptr; + + for (int i = sNumberOfWrapperVolumes; i--;) { + mWrapperMinRadius[i] = mWrapperMaxRadius[i] = mWrapperZSpan[i] = -1; + } + + configEC0(this); +} + +Detector::Detector(const Detector& rhs) + : o2::base::DetImpl(rhs), + mTrackData(), + /* + mHitStarted(false), + mTrkStatusStart(), + mPositionStart(), + mMomentumStart(), + mEnergyLoss(), + */ + mNumberOfDetectors(rhs.mNumberOfDetectors), + mModifyGeometry(rhs.mModifyGeometry), + + /// Container for data points + mHits(o2::utils::createSimVector()), + mStaveModelInnerBarrel(rhs.mStaveModelInnerBarrel), + mStaveModelOuterBarrel(rhs.mStaveModelOuterBarrel) +{ + + for (Int_t j = 0; j < sNumberLayers; j++) { + mLayerName[j].Form("%s%d", GeometryTGeo::getEC0SensorPattern(), j); // See V3Layer + } +} + +Detector::~Detector() +{ + + if (mHits) { + // delete mHits; + o2::utils::freeSimVector(mHits); + } +} + +Detector& Detector::operator=(const Detector& rhs) +{ + // The standard = operator + // Inputs: + // Detector &h the sourse of this copy + // Outputs: + // none. + // Return: + // A copy of the sourse hit h + + if (this == &rhs) { + return *this; + } + + // base class assignment + base::Detector::operator=(rhs); + + mNumberOfDetectors = rhs.mNumberOfDetectors; + + mModifyGeometry = rhs.mModifyGeometry; + + /// Container for data points + mHits = nullptr; + + mStaveModelInnerBarrel = rhs.mStaveModelInnerBarrel; + mStaveModelOuterBarrel = rhs.mStaveModelOuterBarrel; + + for (Int_t j = 0; j < sNumberLayers; j++) { + mLayerName[j].Form("%s%d", GeometryTGeo::getEC0SensorPattern(), j); // See V3Layer + } + + return *this; +} + +void Detector::InitializeO2Detector() +{ + // Define the list of sensitive volumes + defineSensitiveVolumes(); + + for (int i = 0; i < sNumberLayers; i++) { + mLayerID[i] = gMC ? TVirtualMC::GetMC()->VolId(mLayerName[i]) : 0; + } + + mGeometryTGeo = GeometryTGeo::Instance(); + // FairRuntimeDb* rtdb= FairRun::Instance()->GetRuntimeDb(); + // O2itsGeoPar* par=(O2itsGeoPar*)(rtdb->getContainer("O2itsGeoPar")); +} + +Bool_t Detector::ProcessHits(FairVolume* vol) +{ + // This method is called from the MC stepping + if (!(fMC->TrackCharge())) { + return kFALSE; + } + + Int_t lay = 0, volID = vol->getMCid(); + + // FIXME: Determine the layer number. Is this information available directly from the FairVolume? + bool notSens = false; + while ((lay < sNumberLayers) && (notSens = (volID != mLayerID[lay]))) { + ++lay; + } + if (notSens) + return kFALSE; // RS: can this happen? This method must be called for sensors only? + + // Is it needed to keep a track reference when the outer EC0 volume is encountered? + auto stack = (o2::data::Stack*)fMC->GetStack(); + if (fMC->IsTrackExiting() && (lay == 0 || lay == 6)) { + // Keep the track refs for the innermost and outermost layers only + o2::TrackReference tr(*fMC, GetDetId()); + tr.setTrackID(stack->GetCurrentTrackNumber()); + tr.setUserId(lay); + stack->addTrackReference(tr); + } + bool startHit = false, stopHit = false; + unsigned char status = 0; + if (fMC->IsTrackEntering()) { + status |= Hit::kTrackEntering; + } + if (fMC->IsTrackInside()) { + status |= Hit::kTrackInside; + } + if (fMC->IsTrackExiting()) { + status |= Hit::kTrackExiting; + } + if (fMC->IsTrackOut()) { + status |= Hit::kTrackOut; + } + if (fMC->IsTrackStop()) { + status |= Hit::kTrackStopped; + } + if (fMC->IsTrackAlive()) { + status |= Hit::kTrackAlive; + } + + // track is entering or created in the volume + if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) { + startHit = true; + } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) { + stopHit = true; + } + + // increment energy loss at all steps except entrance + if (!startHit) + mTrackData.mEnergyLoss += fMC->Edep(); + if (!(startHit | stopHit)) + return kFALSE; // do noting + + if (startHit) { + mTrackData.mEnergyLoss = 0.; + fMC->TrackMomentum(mTrackData.mMomentumStart); + fMC->TrackPosition(mTrackData.mPositionStart); + mTrackData.mTrkStatusStart = status; + mTrackData.mHitStarted = true; + } + if (stopHit) { + TLorentzVector positionStop; + fMC->TrackPosition(positionStop); + // Retrieve the indices with the volume path + int stave(0), halfstave(0), chipinmodule(0), module; + fMC->CurrentVolOffID(1, chipinmodule); + fMC->CurrentVolOffID(2, module); + fMC->CurrentVolOffID(3, halfstave); + fMC->CurrentVolOffID(4, stave); + int chipindex = mGeometryTGeo->getChipIndex(lay, stave, halfstave, module, chipinmodule); + + Hit* p = addHit(stack->GetCurrentTrackNumber(), chipindex, mTrackData.mPositionStart.Vect(), positionStop.Vect(), + mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), + mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); + // p->SetTotalEnergy(vmc->Etot()); + + // RS: not sure this is needed + // Increment number of Detector det points in TParticle + stack->addHit(GetDetId()); + } + + return kTRUE; +} + +void Detector::createMaterials() +{ + Int_t ifield = 2; + Float_t fieldm = 10.0; + o2::base::Detector::initFieldTrackingParams(ifield, fieldm); + //////////// + + Float_t tmaxfd = 0.1; // 1.0; // Degree + Float_t stemax = 1.0; // cm + Float_t deemax = 0.1; // 30.0; // Fraction of particle's energy 0 wmat contains number of atoms) + o2::base::Detector::Mixture(31, "CERAMIC$", aCeramic, zCeramic, dCeramic, -3, wCeramic); + o2::base::Detector::Medium(31, "CERAMIC$", 31, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // All types of carbon + // Unidirectional prepreg + o2::base::Detector::Material(8, "K13D2U2k$", 12.0107, 6, 1.643, 999, 999); + o2::base::Detector::Medium(8, "K13D2U2k$", 8, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Material(17, "K13D2U120$", 12.0107, 6, 1.583, 999, 999); + o2::base::Detector::Medium(17, "K13D2U120$", 17, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Carbon prepreg woven + o2::base::Detector::Material(18, "F6151B05M$", 12.0107, 6, 2.133, 999, 999); + o2::base::Detector::Medium(18, "F6151B05M$", 18, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Impregnated thread + o2::base::Detector::Material(9, "M60J3K$", 12.0107, 6, 2.21, 999, 999); + o2::base::Detector::Medium(9, "M60J3K$", 9, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Impregnated thread + o2::base::Detector::Material(10, "M55J6K$", 12.0107, 6, 1.63, 999, 999); + o2::base::Detector::Medium(10, "M55J6K$", 10, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Fabric(0/90) + o2::base::Detector::Material(11, "T300$", 12.0107, 6, 1.725, 999, 999); + o2::base::Detector::Medium(11, "T300$", 11, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // AMEC Thermasol + o2::base::Detector::Material(12, "FGS003$", 12.0107, 6, 1.6, 999, 999); + o2::base::Detector::Medium(12, "FGS003$", 12, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Carbon fleece + o2::base::Detector::Material(13, "CarbonFleece$", 12.0107, 6, 0.4, 999, 999); + o2::base::Detector::Medium(13, "CarbonFleece$", 13, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, + stminSi); + // Rohacell + o2::base::Detector::Mixture(32, "ROHACELL$", aRohac, zRohac, dRohac, -4, wRohac); + o2::base::Detector::Medium(32, "ROHACELL$", 32, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // PEEK CF30 + o2::base::Detector::Mixture(19, "PEEKCF30$", aPEEK, zPEEK, dPEEK, -3, wPEEK); + o2::base::Detector::Medium(19, "PEEKCF30$", 19, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Flex cable + Float_t aFCm[5] = {12.0107, 1.00794, 14.0067, 15.9994, 26.981538}; + Float_t zFCm[5] = {6., 1., 7., 8., 13.}; + Float_t wFCm[5] = {0.520088819984, 0.01983871336, 0.0551367996, 0.157399667056, 0.247536}; + // Float_t dFCm = 1.6087; // original + // Float_t dFCm = 2.55; // conform with STAR + Float_t dFCm = 2.595; // conform with Corrado + + o2::base::Detector::Mixture(14, "FLEXCABLE$", aFCm, zFCm, dFCm, 5, wFCm); + o2::base::Detector::Medium(14, "FLEXCABLE$", 14, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + // AliceO2::Base::Detector::Material(7,"GLUE$",0.12011E+02,0.60000E+01,0.1930E+01/2.015,999,999); + // // original + o2::base::Detector::Material(15, "GLUE$", 12.011, 6, 1.93 / 2.015, 999, 999); // conform with ATLAS, Corrado, Stefan + o2::base::Detector::Medium(15, "GLUE$", 15, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + o2::base::Detector::Material(16, "ALUMINUM$", 0.26982E+02, 0.13000E+02, 0.26989E+01, 0.89000E+01, 0.99900E+03); + o2::base::Detector::Medium(16, "ALUMINUM$", 16, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); + + o2::base::Detector::Mixture(20, "TUNGCARB$", aWC, zWC, dWC, 2, wWC); + o2::base::Detector::Medium(20, "TUNGCARB$", 20, 0, ifield, fieldm, tmaxfd, stemax, deemaxSi, epsilSi, stminSi); + + wInox304[3] = 1. - wInox304[0] - wInox304[1] - wInox304[2]; + o2::base::Detector::Mixture(21, "INOX304$", aInox304, zInox304, dInox304, 4, wInox304); + o2::base::Detector::Medium(21, "INOX304$", 21, 0, ifield, fieldm, tmaxfd, stemax, deemaxSi, epsilSi, stminSi); + + // Tungsten (for gamma converter rods) + o2::base::Detector::Material(28, "TUNGSTEN$", 183.84, 74, 19.25, 999, 999); + o2::base::Detector::Medium(28, "TUNGSTEN$", 28, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); +} + +void Detector::EndOfEvent() { Reset(); } + +void Detector::Register() +{ + // This will create a branch in the output tree called Hit, setting the last + // parameter to kFALSE means that this collection will not be written to the file, + // it will exist only during the simulation + + if (FairRootManager::Instance()) { + FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, kTRUE); + } +} + +void Detector::Reset() +{ + if (!o2::utils::ShmManager::Instance().isOperational()) { + mHits->clear(); + } +} + +void Detector::defineWrapperVolume(Int_t id, Double_t rmin, Double_t rmax, Double_t zspan) +{ + // set parameters of id-th wrapper volume + if (id >= sNumberOfWrapperVolumes || id < 0) { + LOG(FATAL) << "id " << id << " of wrapper volume is not in 0-" << sNumberOfWrapperVolumes - 1 << " range"; + } + + mWrapperMinRadius[id] = rmin; + mWrapperMaxRadius[id] = rmax; + mWrapperZSpan[id] = zspan; +} + +void Detector::defineLayer(Int_t nlay, double phi0, Double_t r, Int_t nstav, Int_t nunit, Double_t lthick, + Double_t dthick, UInt_t dettypeID, Int_t buildLevel) +{ + // Sets the layer parameters + // Inputs: + // nlay layer number + // phi0 layer phi0 + // r layer radius + // nstav number of staves + // nunit IB: number of chips per stave + // OB: number of modules per half stave + // lthick stave thickness (if omitted, defaults to 0) + // dthick detector thickness (if omitted, defaults to 0) + // dettypeID ?? + // buildLevel (if 0, all geometry is build, used for material budget studies) + // Outputs: + // none. + // Return: + // none. + + LOG(INFO) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit + << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; + + if (nlay >= sNumberLayers || nlay < 0) { + LOG(ERROR) << "Wrong layer number " << nlay; + return; + } + + mTurboLayer[nlay] = kFALSE; + mLayerPhi0[nlay] = phi0; + mLayerRadii[nlay] = r; + mStavePerLayer[nlay] = nstav; + mUnitPerStave[nlay] = nunit; + mChipThickness[nlay] = lthick; + mDetectorThickness[nlay] = dthick; + mChipTypeID[nlay] = dettypeID; + mBuildLevel[nlay] = buildLevel; +} + +void Detector::defineLayerTurbo(Int_t nlay, Double_t phi0, Double_t r, Int_t nstav, Int_t nunit, Double_t width, + Double_t tilt, Double_t lthick, Double_t dthick, UInt_t dettypeID, Int_t buildLevel) +{ + // Sets the layer parameters for a "turbo" layer + // (i.e. a layer whose staves overlap in phi) + // Inputs: + // nlay layer number + // phi0 phi of 1st stave + // r layer radius + // nstav number of staves + // nunit IB: number of chips per stave + // OB: number of modules per half stave + // width stave width + // tilt layer tilt angle (degrees) + // lthick stave thickness (if omitted, defaults to 0) + // dthick detector thickness (if omitted, defaults to 0) + // dettypeID ?? + // buildLevel (if 0, all geometry is build, used for material budget studies) + // Outputs: + // none. + // Return: + // none. + + LOG(INFO) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit + << " W:" << width << " Tilt:" << tilt << " Lthick:" << lthick << " Dthick:" << dthick + << " DetID:" << dettypeID << " B:" << buildLevel; + + if (nlay >= sNumberLayers || nlay < 0) { + LOG(ERROR) << "Wrong layer number " << nlay; + return; + } + + mTurboLayer[nlay] = kTRUE; + mLayerPhi0[nlay] = phi0; + mLayerRadii[nlay] = r; + mStavePerLayer[nlay] = nstav; + mUnitPerStave[nlay] = nunit; + mChipThickness[nlay] = lthick; + mStaveWidth[nlay] = width; + mStaveTilt[nlay] = tilt; + mDetectorThickness[nlay] = dthick; + mChipTypeID[nlay] = dettypeID; + mBuildLevel[nlay] = buildLevel; +} + +void Detector::getLayerParameters(Int_t nlay, Double_t& phi0, Double_t& r, Int_t& nstav, Int_t& nmod, Double_t& width, + Double_t& tilt, Double_t& lthick, Double_t& dthick, UInt_t& dettype) const +{ + // Gets the layer parameters + // Inputs: + // nlay layer number + // Outputs: + // phi0 phi of 1st stave + // r layer radius + // nstav number of staves + // nmod IB: number of chips per stave + // OB: number of modules per half stave + // width stave width + // tilt stave tilt angle + // lthick stave thickness + // dthick detector thickness + // dettype detector type + // Return: + // none. + + if (nlay >= sNumberLayers || nlay < 0) { + LOG(ERROR) << "Wrong layer number " << nlay; + return; + } + + phi0 = mLayerPhi0[nlay]; + r = mLayerRadii[nlay]; + nstav = mStavePerLayer[nlay]; + nmod = mUnitPerStave[nlay]; + width = mStaveWidth[nlay]; + tilt = mStaveTilt[nlay]; + lthick = mChipThickness[nlay]; + dthick = mDetectorThickness[nlay]; + dettype = mChipTypeID[nlay]; +} + +TGeoVolume* Detector::createWrapperVolume(Int_t id) +{ + // Creates an air-filled wrapper cylindrical volume + // For OB a Pcon is needed to host the support rings + // while avoiding overlaps with MFT structures and OB cones + + const Double_t suppRingAZlen = 4.; + const Double_t coneRingARmax = 33.96; + const Double_t coneRingAZlen = 5.6; + const Double_t suppRingCZlen[3] = {4.8, 4.0, 2.4}; + const Double_t suppRingsRmin[3] = {23.35, 20.05, 35.4}; + + if (mWrapperMinRadius[id] < 0 || mWrapperMaxRadius[id] < 0 || mWrapperZSpan[id] < 0) { + LOG(FATAL) << "Wrapper volume " << id << " was requested but not defined"; + } + + // Now create the actual shape and volume + TGeoShape* tube; + Double_t zlen; + switch (id) { + case 0: // IB Layer 0,1,2: simple cylinder + { + TGeoTube* wrap = new TGeoTube(mWrapperMinRadius[id], mWrapperMaxRadius[id], mWrapperZSpan[id] / 2.); + tube = (TGeoShape*)wrap; + } break; + case 1: // MB Layer 3,4: complex Pcon to avoid MFT overlaps + { + TGeoPcon* wrap = new TGeoPcon(0, 360, 6); + zlen = mWrapperZSpan[id] / 2 + suppRingCZlen[0]; + wrap->DefineSection(0, -zlen, suppRingsRmin[0], mWrapperMaxRadius[id]); + zlen = mWrapperZSpan[id] / 2 + suppRingCZlen[1]; + wrap->DefineSection(1, -zlen, suppRingsRmin[0], mWrapperMaxRadius[id]); + wrap->DefineSection(2, -zlen, suppRingsRmin[1], mWrapperMaxRadius[id]); + wrap->DefineSection(3, -mWrapperZSpan[id] / 2., suppRingsRmin[1], mWrapperMaxRadius[id]); + wrap->DefineSection(4, -mWrapperZSpan[id] / 2., mWrapperMinRadius[id], mWrapperMaxRadius[id]); + zlen = mWrapperZSpan[id] / 2 + suppRingAZlen; + wrap->DefineSection(5, zlen, mWrapperMinRadius[id], mWrapperMaxRadius[id]); + tube = (TGeoShape*)wrap; + } break; + case 2: // OB Layer 5,6: simpler Pcon to avoid OB cones overlaps + { + TGeoPcon* wrap = new TGeoPcon(0, 360, 6); + zlen = mWrapperZSpan[id] / 2; + wrap->DefineSection(0, -zlen, suppRingsRmin[2], mWrapperMaxRadius[id]); + zlen -= suppRingCZlen[2]; + wrap->DefineSection(1, -zlen, suppRingsRmin[2], mWrapperMaxRadius[id]); + wrap->DefineSection(2, -zlen, mWrapperMinRadius[id], mWrapperMaxRadius[id]); + zlen = mWrapperZSpan[id] / 2 - coneRingAZlen; + wrap->DefineSection(3, zlen, mWrapperMinRadius[id], mWrapperMaxRadius[id]); + wrap->DefineSection(4, zlen, coneRingARmax, mWrapperMaxRadius[id]); + wrap->DefineSection(5, mWrapperZSpan[id] / 2, coneRingARmax, mWrapperMaxRadius[id]); + tube = (TGeoShape*)wrap; + } break; + default: // Can never happen, keeps gcc quiet + break; + } + + TGeoMedium* medAir = gGeoManager->GetMedium("EC0_AIR$"); + + char volnam[30]; + snprintf(volnam, 29, "%s%d", GeometryTGeo::getEC0WrapVolPattern(), id); + + auto* wrapper = new TGeoVolume(volnam, tube, medAir); + + return wrapper; +} + +void Detector::ConstructGeometry() +{ + // Create the detector materials + createMaterials(); + + // Construct the detector geometry + constructDetectorGeometry(); +} + +void Detector::constructDetectorGeometry() +{ + // Create the geometry and insert it in the mother volume EC0V + TGeoManager* geoManager = gGeoManager; + + TGeoVolume* vALIC = geoManager->GetVolume("barrel"); + + if (!vALIC) { + LOG(FATAL) << "Could not find the top volume"; + } + + new TGeoVolumeAssembly(GeometryTGeo::getEC0VolPattern()); + TGeoVolume* vEC0V = geoManager->GetVolume(GeometryTGeo::getEC0VolPattern()); + vALIC->AddNode(vEC0V, 2, new TGeoTranslation(0, 30., 0)); // Copy number is 2 to cheat AliGeoManager::CheckSymNamesLUT + + const Int_t kLength = 100; + Char_t vstrng[kLength] = "xxxRS"; //? + vEC0V->SetTitle(vstrng); + + // Check that we have all needed parameters + for (Int_t j = 0; j < sNumberLayers; j++) { + if (mLayerRadii[j] <= 0) { + LOG(FATAL) << "Wrong layer radius for layer " << j << "(" << mLayerRadii[j] << ")"; + } + if (mStavePerLayer[j] <= 0) { + LOG(FATAL) << "Wrong number of staves for layer " << j << "(" << mStavePerLayer[j] << ")"; + } + if (mUnitPerStave[j] <= 0) { + LOG(FATAL) << "Wrong number of chips for layer " << j << "(" << mUnitPerStave[j] << ")"; + } + if (mChipThickness[j] < 0) { + LOG(FATAL) << "Wrong chip thickness for layer " << j << "(" << mChipThickness[j] << ")"; + } + if (mTurboLayer[j] && mStaveWidth[j] <= 0) { + LOG(FATAL) << "Wrong stave width for layer " << j << "(" << mStaveWidth[j] << ")"; + } + if (mDetectorThickness[j] < 0) { + LOG(FATAL) << "Wrong Sensor thickness for layer " << j << "(" << mDetectorThickness[j] << ")"; + } + + if (j > 0) { + if (mLayerRadii[j] <= mLayerRadii[j - 1]) { + LOG(FATAL) << "Layer " << j << " radius (" << mLayerRadii[j] << ") is smaller than layer " << j - 1 + << " radius (" << mLayerRadii[j - 1] << ")"; + } + } + + if (mChipThickness[j] == 0) { + LOG(INFO) << "Chip thickness for layer " << j << " not set, using default"; + } + } + + // Create the wrapper volumes + TGeoVolume** wrapVols = nullptr; + + if (sNumberOfWrapperVolumes) { + wrapVols = new TGeoVolume*[sNumberOfWrapperVolumes]; + for (int id = 0; id < sNumberOfWrapperVolumes; id++) { + wrapVols[id] = createWrapperVolume(id); + vEC0V->AddNode(wrapVols[id], 1, nullptr); + } + } + + // Now create the actual geometry + for (Int_t j = 0; j < sNumberLayers; j++) { + TGeoVolume* dest = vEC0V; + mWrapperLayerId[j] = -1; + + if (mTurboLayer[j]) { + mGeometry[j] = new V3Layer(j, kTRUE, kFALSE); + mGeometry[j]->setStaveWidth(mStaveWidth[j]); + mGeometry[j]->setStaveTilt(mStaveTilt[j]); + } else { + mGeometry[j] = new V3Layer(j, kFALSE); + } + + mGeometry[j]->setPhi0(mLayerPhi0[j]); + mGeometry[j]->setRadius(mLayerRadii[j]); + mGeometry[j]->setNumberOfStaves(mStavePerLayer[j]); + mGeometry[j]->setNumberOfUnits(mUnitPerStave[j]); + mGeometry[j]->setChipType(mChipTypeID[j]); + mGeometry[j]->setBuildLevel(mBuildLevel[j]); + + if (j < sNumberInnerLayers) { + mGeometry[j]->setStaveModel(mStaveModelInnerBarrel); + } else { + mGeometry[j]->setStaveModel(mStaveModelOuterBarrel); + } + + LOG(DEBUG1) << "mBuildLevel: " << mBuildLevel[j]; + + if (mChipThickness[j] != 0) { + mGeometry[j]->setChipThick(mChipThickness[j]); + } + if (mDetectorThickness[j] != 0) { + mGeometry[j]->setSensorThick(mDetectorThickness[j]); + } + + for (int iw = 0; iw < sNumberOfWrapperVolumes; iw++) { + if (mLayerRadii[j] > mWrapperMinRadius[iw] && mLayerRadii[j] < mWrapperMaxRadius[iw]) { + LOG(DEBUG) << "Will embed layer " << j << " in wrapper volume " << iw; + + dest = wrapVols[iw]; + mWrapperLayerId[j] = iw; + break; + } + } + mGeometry[j]->createLayer(dest); + } + + // Finally create the services + mServicesGeometry = new V3Services(); + + createInnerBarrelServices(wrapVols[0]); + createMiddlBarrelServices(wrapVols[1]); + createOuterBarrelServices(wrapVols[2]); + createOuterBarrelSupports(vEC0V); + + // TEMPORARY - These routines will be obsoleted once the new services are completed - TEMPORARY + // createServiceBarrel(kTRUE, wrapVols[0]); + // createServiceBarrel(kFALSE, wrapVols[2]); + + delete[] wrapVols; // delete pointer only, not the volumes +} + +void Detector::createInnerBarrelServices(TGeoVolume* motherVolume) +{ + // + // Creates the Inner Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the services + // + // Output: + // + // Return: + // + // Created: 15 May 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // Updated: 19 Jun 2019 Mario Sitta IB Side A added + // Updated: 21 Oct 2019 Mario Sitta CYSS added + // + + // Create the End Wheels on Side A + TGeoVolume* endWheelsA = mServicesGeometry->createIBEndWheelsSideA(); + + motherVolume->AddNode(endWheelsA, 1, nullptr); + + // Create the End Wheels on Side C + TGeoVolume* endWheelsC = mServicesGeometry->createIBEndWheelsSideC(); + + motherVolume->AddNode(endWheelsC, 1, nullptr); + + // Create the CYSS Assembly (i.e. the supporting half cylinder and cone) + TGeoVolume* cyss = mServicesGeometry->createCYSSAssembly(); + + motherVolume->AddNode(cyss, 1, nullptr); +} + +void Detector::createMiddlBarrelServices(TGeoVolume* motherVolume) +{ + // + // Creates the Middle Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the services + // + // Output: + // + // Return: + // + // Created: 24 Sep 2019 Mario Sitta + // + + // Create the End Wheels on Side A + mServicesGeometry->createMBEndWheelsSideA(motherVolume); + + // Create the End Wheels on Side C + mServicesGeometry->createMBEndWheelsSideC(motherVolume); +} + +void Detector::createOuterBarrelServices(TGeoVolume* motherVolume) +{ + // + // Creates the Outer Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the services + // + // Output: + // + // Return: + // + // Created: 27 Sep 2019 Mario Sitta + // + + // Create the End Wheels on Side A + mServicesGeometry->createOBEndWheelsSideA(motherVolume); + + // Create the End Wheels on Side C + mServicesGeometry->createOBEndWheelsSideC(motherVolume); +} + +void Detector::createOuterBarrelSupports(TGeoVolume* motherVolume) +{ + // + // Creates the Outer Barrel Service structures + // + // Input: + // motherVolume : the volume hosting the supports + // + // Output: + // + // Return: + // + // Created: 26 Jan 2020 Mario Sitta + // + + // Create the Cone on Side A + mServicesGeometry->createOBConeSideA(motherVolume); + + // Create the Cone on Side C + mServicesGeometry->createOBConeSideC(motherVolume); +} + +// Service Barrel +void Detector::createServiceBarrel(const Bool_t innerBarrel, TGeoVolume* dest, const TGeoManager* mgr) +{ + // Creates the Service Barrel (as a simple cylinder) for IB and OB + // Inputs: + // innerBarrel : if true, build IB service barrel, otherwise for OB + // dest : the mother volume holding the service barrel + // mgr : the gGeoManager pointer (used to get the material) + // + + Double_t rminIB = 4.7; + Double_t rminOB = 43.9; + Double_t zLenOB; + Double_t cInt = 0.22; // dimensioni cilindro di supporto interno + Double_t cExt = 1.00; // dimensioni cilindro di supporto esterno + // Double_t phi1 = 180; + // Double_t phi2 = 360; + + TGeoMedium* medCarbonFleece = mgr->GetMedium("EC0_CarbonFleece$"); + + if (innerBarrel) { + zLenOB = ((TGeoTube*)(dest->GetShape()))->GetDz(); + // TGeoTube*ibSuppSh = new TGeoTubeSeg(rminIB,rminIB+cInt,zLenOB,phi1,phi2); + auto* ibSuppSh = new TGeoTube(rminIB, rminIB + cInt, zLenOB); + auto* ibSupp = new TGeoVolume("ibSuppCyl", ibSuppSh, medCarbonFleece); + dest->AddNode(ibSupp, 1); + } else { + zLenOB = ((TGeoTube*)(dest->GetShape()))->GetDz(); + auto* obSuppSh = new TGeoTube(rminOB, rminOB + cExt, zLenOB); + auto* obSupp = new TGeoVolume("obSuppCyl", obSuppSh, medCarbonFleece); + dest->AddNode(obSupp, 1); + } + + return; +} + +void Detector::addAlignableVolumes() const +{ + // + // Creates entries for alignable volumes associating the symbolic volume + // name with the corresponding volume path. + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + LOG(INFO) << "Add EC0 alignable volumes"; + + if (!gGeoManager) { + LOG(FATAL) << "TGeoManager doesn't exist !"; + return; + } + + TString path = Form("/cave_1/barrel_1/%s_2", GeometryTGeo::getEC0VolPattern()); + TString sname = GeometryTGeo::composeSymNameEC0(); + + LOG(DEBUG) << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + Int_t lastUID = 0; + for (Int_t lr = 0; lr < sNumberLayers; lr++) { + addAlignableVolumesLayer(lr, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a Layer and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString wrpV = + mWrapperLayerId[lr] != -1 ? Form("%s%d_1", GeometryTGeo::getEC0WrapVolPattern(), mWrapperLayerId[lr]) : ""; + TString path = Form("%s/%s/%s%d_1", parent.Data(), wrpV.Data(), GeometryTGeo::getEC0LayerPattern(), lr); + TString sname = GeometryTGeo::composeSymNameLayer(lr); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nstaves = lrobj->getNumberOfStavesPerParent(); + for (int st = 0; st < nstaves; st++) { + addAlignableVolumesStave(lr, st, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesStave(Int_t lr, Int_t st, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a Stave and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getEC0StavePattern(), lr, st); + TString sname = GeometryTGeo::composeSymNameStave(lr, st); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nhstave = lrobj->getNumberOfHalfStavesPerParent(); + Int_t start = nhstave > 0 ? 0 : -1; + for (Int_t sst = start; sst < nhstave; sst++) { + addAlignableVolumesHalfStave(lr, st, sst, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a HalfStave (if any) and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString path = parent; + if (hst >= 0) { + path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getEC0HalfStavePattern(), lr, hst); + TString sname = GeometryTGeo::composeSymNameHalfStave(lr, st, hst); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nmodules = lrobj->getNumberOfModulesPerParent(); + Int_t start = nmodules > 0 ? 0 : -1; + for (Int_t md = start; md < nmodules; md++) { + addAlignableVolumesModule(lr, st, hst, md, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesModule(Int_t lr, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const +{ + // + // Add alignable volumes for a Module (if any) and its daughters + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString path = parent; + if (md >= 0) { + path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getEC0ModulePattern(), lr, md); + TString sname = GeometryTGeo::composeSymNameModule(lr, st, hst, md); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + } + + const V3Layer* lrobj = mGeometry[lr]; + Int_t nchips = lrobj->getNumberOfChipsPerParent(); + for (Int_t ic = 0; ic < nchips; ic++) { + addAlignableVolumesChip(lr, st, hst, md, ic, path, lastUID); + } + + return; +} + +void Detector::addAlignableVolumesChip(Int_t lr, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, + Int_t& lastUID) const +{ + // + // Add alignable volumes for a Chip + // + // Created: 06 Mar 2018 Mario Sitta First version (mainly ported from AliRoot) + // + + TString path = Form("%s/%s%d_%d", parent.Data(), GeometryTGeo::getEC0ChipPattern(), lr, ch); + TString sname = GeometryTGeo::composeSymNameChip(lr, st, hst, md, ch); + Int_t modUID = chipVolUID(lastUID++); + + LOG(DEBUG) << "Add " << sname << " <-> " << path; + + if (!gGeoManager->SetAlignableEntry(sname, path.Data(), modUID)) { + LOG(FATAL) << "Unable to set alignable entry ! " << sname << " : " << path; + } + + return; +} + +void Detector::defineSensitiveVolumes() +{ + TGeoManager* geoManager = gGeoManager; + TGeoVolume* v; + + TString volumeName; + + // The names of the EC0 sensitive volumes have the format: EC0USensor(0...sNumberLayers-1) + for (Int_t j = 0; j < sNumberLayers; j++) { + volumeName = GeometryTGeo::getEC0SensorPattern() + TString::Itoa(j, 10); + v = geoManager->GetVolume(volumeName.Data()); + AddSensitiveVolume(v); + } +} + +Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, + const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus, + unsigned char endStatus) +{ + mHits->emplace_back(trackID, detID, startPos, endPos, startMom, startE, endTime, eLoss, startStatus, endStatus); + return &(mHits->back()); +} + +void Detector::Print(std::ostream* os) const +{ + // Standard output format for this class. + // Inputs: + // ostream *os The output stream + // Outputs: + // none. + // Return: + // none. + +#if defined __GNUC__ +#if __GNUC__ > 2 + std::ios::fmtflags fmt; +#else + Int_t fmt; +#endif +#else +#if defined __ICC || defined __ECC || defined __xlC__ + ios::fmtflags fmt; +#else + Int_t fmt; +#endif +#endif + // RS: why do we need to pring this garbage? + + // fmt = os->setf(std::ios::scientific); // set scientific floating point output + // fmt = os->setf(std::ios::hex); // set hex for mStatus only. + // fmt = os->setf(std::ios::dec); // every thing else decimel. + // *os << mModule << " "; + // *os << mEnergyDepositionStep << " " << mTof; + // *os << " " << mStartingStepX << " " << mStartingStepY << " " << mStartingStepZ; + // *os << " " << endl; + // os->flags(fmt); // reset back to old formating. + return; +} + +void Detector::Read(std::istream* is) +{ + // Standard input format for this class. + // Inputs: + // istream *is the input stream + // Outputs: + // none. + // Return: + // none. + // RS no need to read garbage + return; +} + +std::ostream& operator<<(std::ostream& os, Detector& p) +{ + // Standard output streaming function. + // Inputs: + // ostream os The output stream + // Detector p The his to be printed out + // Outputs: + // none. + // Return: + // The input stream + + p.Print(&os); + return os; +} + +std::istream& operator>>(std::istream& is, Detector& r) +{ + // Standard input streaming function. + // Inputs: + // istream is The input stream + // Detector p The Detector class to be filled from this input stream + // Outputs: + // none. + // Return: + // The input stream + + r.Read(&is); + return is; +} + +ClassImp(o2::ecl::Detector); diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/EC0SimulationLinkDef.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/EC0SimulationLinkDef.h new file mode 100644 index 0000000000000..f758da47c9239 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/EC0SimulationLinkDef.h @@ -0,0 +1,24 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::ecl::V11Geometry + ; +#pragma link C++ class o2::ecl::V1Layer + ; +#pragma link C++ class o2::ecl::V3Layer + ; +#pragma link C++ class o2::ecl::V3Services + ; +#pragma link C++ class o2::ecl::Detector + ; +#pragma link C++ class o2::base::DetImpl < o2::ecl::Detector> + ; + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V11Geometry.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V11Geometry.cxx new file mode 100644 index 0000000000000..b86b4ed0f7b67 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V11Geometry.cxx @@ -0,0 +1,1229 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 V11Geometry.cxx +/// \brief Implementation of the V11Geometry class + +#include "EC0Simulation/V11Geometry.h" + +#include "FairLogger.h" // for LOG + +#include // for TArc +#include // for TArrow +#include // for TCanvas +#include // for TGeoArb8 +#include // for TGeoElement +#include // for TGeoMixture, TGeoMaterial, etc +#include // for TGeoPcon +#include // for TGeoConSeg +#include // for TLine +#include // for TPolyLine +#include // for TPolyMarker +#include // for TText +#include "TMath.h" // for DegToRad, Cos, Sqrt, ATan2, Sin, Tan, Pi, etc +#include "TMathBase.h" // for Max, Min, Abs +#include // for TGeoTubeSeg + +#include // for printf, snprintf +#include + +using std::cin; +using std::cout; +using std::endl; + +using namespace o2::ecl; + +ClassImp(o2::ecl::V11Geometry); + +const Double_t V11Geometry::sMicron = 1.0E-4; +const Double_t V11Geometry::sMm = 0.10; +const Double_t V11Geometry::sCm = 1.00; +const Double_t V11Geometry::sDegree = 1.0; +const Double_t V11Geometry::sRadian = 180. / 3.14159265358979323846; +const Double_t V11Geometry::sGCm3 = 1.0; // assume default is g/cm^3 +const Double_t V11Geometry::sKgm3 = 1.0E+3; // assume Kg/m^3 +const Double_t V11Geometry::sKgdm3 = 1.0; // assume Kg/dm^3 +const Double_t V11Geometry::sCelsius = 1.0; // Assume default is C +const Double_t V11Geometry::sPascal = 1.0E-3; // Assume kPascal +const Double_t V11Geometry::sKPascal = 1.0; // Asume kPascal +const Double_t V11Geometry::sEV = 1.0E-9; // GeV default +const Double_t V11Geometry::sKEV = 1.0e-6; // GeV default +const Double_t V11Geometry::sMEV = 1.0e-3; // GeV default +const Double_t V11Geometry::sGEV = 1.0; // GeV default + +void V11Geometry::intersectLines(Double_t m, Double_t x0, Double_t y0, Double_t n, Double_t x1, + Double_t y1, Double_t& xi, Double_t& yi) const +{ + if (TMath::Abs(m - n) < 0.000001) { + LOG(ERROR) << "Lines are parallel: m = " << m << " n = " << n; + return; + } + + xi = (y1 - n * x1 - y0 + m * x0) / (m - n); + yi = y0 + m * (xi - x0); + + return; +} + +Bool_t V11Geometry::intersectCircle(Double_t m, Double_t x0, Double_t y0, Double_t rr, Double_t xc, + Double_t yc, Double_t& xi1, Double_t& yi1, Double_t& xi2, + Double_t& yi2) +{ + Double_t p = m * x0 - y0; + Double_t q = m * m + 1; + + p = p - m * xc + yc; + + Double_t delta = m * m * p * p - q * (p * p - rr * rr); + + if (delta < 0) { + return kFALSE; + } else { + Double_t root = TMath::Sqrt(delta); + xi1 = (m * p + root) / q + xc; + xi2 = (m * p - root) / q + xc; + yi1 = m * (xi1 - x0) + y0; + yi2 = m * (xi2 - x0) + y0; + return kTRUE; + } +} + +Double_t V11Geometry::yFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x) + const +{ + if (x0 == x1 && y0 == y1) { + printf( + "Error: V11Geometry::Yfrom2Ponts The two points are " + "the same (%e,%e) and (%e,%e)", + x0, y0, x1, y1); + return 0.0; + } // end if + if (x0 == x1) { + printf( + "Warning: V11Geometry::yFrom2Points x0=%e == x1=%e. " + "line vertical " + "returning mean y", + x0, x1); + return 0.5 * (y0 + y1); + } // end if x0==x1 + Double_t m = (y0 - y1) / (x0 - x1); + return m * (x - x0) + y0; +} + +Double_t V11Geometry::xFrom2Points(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t y) + const +{ + if (x0 == x1 && y0 == y1) { + printf( + "Error: V11Geometry::Yfrom2Ponts The two points are " + "the same (%e,%e) and (%e,%e)", + x0, y0, x1, y1); + return 0.0; + } // end if + if (y0 == y1) { + printf( + "Warrning: V11Geometry::yFrom2Points y0=%e == y1=%e. " + "line horizontal returning mean x", + y0, y1); + return 0.5 * (x0 + x1); + } // end if y0==y1 + Double_t m = (x0 - x1) / (y0 - y1); + return m * (y - y0) + x0; +} + +Double_t V11Geometry::rMaxFrom2Points(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t z) const +{ + Double_t d0, d1, d2, r; + + d0 = p->GetRmax(i1) - p->GetRmax(i2); // cout <<"L263: d0="<GetZ(i2); // cout <<"L264: d1="<GetZ(i1) - p->GetZ(i2); // cout <<"L265: d2="<GetRmax(i2) + d1 * d0 / d2; // cout <<"L266: r="<GetRmin(i2) + + (p->GetRmin(i1) - p->GetRmin(i2)) * (z - p->GetZ(i2)) / (p->GetZ(i1) - p->GetZ(i2)); +} + +Double_t V11Geometry::rFrom2Points(const Double_t* p, const Double_t* az, Int_t i1, Int_t i2, + Double_t z) const +{ + return p[i2] + (p[i1] - p[i2]) * (z - az[i2]) / (az[i1] - az[i2]); +} + +Double_t V11Geometry::zFrom2MinPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const +{ + return p->GetZ(i2) + + (p->GetZ(i1) - p->GetZ(i2)) * (r - p->GetRmin(i2)) / (p->GetRmin(i1) - p->GetRmin(i2)); +} + +Double_t V11Geometry::zFrom2MaxPoints(const TGeoPcon* p, Int_t i1, Int_t i2, Double_t r) const +{ + return p->GetZ(i2) + + (p->GetZ(i1) - p->GetZ(i2)) * (r - p->GetRmax(i2)) / (p->GetRmax(i1) - p->GetRmax(i2)); +} + +Double_t V11Geometry::zFrom2Points(const Double_t* z, const Double_t* ar, Int_t i1, Int_t i2, + Double_t r) const +{ + return z[i2] + (z[i1] - z[i2]) * (r - ar[i2]) / (ar[i1] - ar[i2]); +} + +Double_t V11Geometry::rMaxFromZpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t z, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return -tantc * (z - p->GetZ(ip)) + p->GetRmax(ip) + th / costc; +} + +Double_t V11Geometry::rFromZpCone(const Double_t* ar, const Double_t* az, int ip, Double_t tc, + Double_t z, Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return -tantc * (z - az[ip]) + ar[ip] + th / costc; +} + +Double_t V11Geometry::rMinFromZpCone(const TGeoPcon* p, Int_t ip, Double_t tc, Double_t z, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return -tantc * (z - p->GetZ(ip)) + p->GetRmin(ip) + th / costc; +} + +Double_t V11Geometry::zFromRMaxpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return p->GetZ(ip) + (p->GetRmax(ip) + th / costc - r) / tantc; +} + +Double_t V11Geometry::zFromRMaxpCone(const Double_t* ar, const Double_t* az, int ip, Double_t tc, + Double_t r, Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return az[ip] + (ar[ip] + th / costc - r) / tantc; +} + +Double_t V11Geometry::zFromRMinpCone(const TGeoPcon* p, int ip, Double_t tc, Double_t r, + Double_t th) const +{ + Double_t tantc = TMath::Tan(tc * TMath::DegToRad()); + Double_t costc = TMath::Cos(tc * TMath::DegToRad()); + + return p->GetZ(ip) + (p->GetRmin(ip) + th / costc - r) / tantc; +} + +void V11Geometry::radiusOfCurvature(Double_t rc, Double_t theta0, Double_t z0, Double_t r0, + Double_t theta1, Double_t& z1, Double_t& r1) const +{ + z1 = rc * (TMath::Sin(theta1 * TMath::DegToRad()) - TMath::Sin(theta0 * TMath::DegToRad())) + z0; + r1 = rc * (TMath::Cos(theta1 * TMath::DegToRad()) - TMath::Cos(theta0 * TMath::DegToRad())) + r0; + return; +} + +void V11Geometry::insidePoint(const TGeoPcon* p, Int_t i1, Int_t i2, Int_t i3, Double_t c, + TGeoPcon* q, Int_t j1, Bool_t max) const +{ + Double_t x0, y0, x1, y1, x2, y2, x, y; + + if (max) { + c = -c; // cout <<"L394 c="<GetRmax(i1); + if (i1 == i2) { + y0 = p->GetRmin(i1); // cout <<"L396 y0="<GetRmax(i2); // cout <<"L397 y1="<GetRmax(i3); // cout <<"L398 y2="<GetRmin(i3); // cout <<"L399 y2="<GetRmin(i1); // cout <<"L401 y0="<GetRmin(i2); // cout <<"L402 y1="<GetRmin(i3); + + if (i2 == i3) { + y2 = p->GetRmax(i3); // cout <<"L404 y2="<GetZ(i1); // cout <<"L406 x0="<GetZ(i2); // cout <<"L407 x1="<GetZ(i3); // cout <<"L408 x2="<Z(j1) = x; + + if (max) { + q->Rmax(j1) = y; + } else { + q->Rmin(j1) = y; + } + return; +} + +void V11Geometry::insidePoint(Double_t x0, Double_t y0, Double_t x1, Double_t y1, Double_t x2, + Double_t y2, Double_t c, Double_t& x, Double_t& y) const +{ + Double_t dx01, dx12, dy01, dy12, r01, r12, m; + + // printf("InsidePoint: x0=% #12.7g y0=% #12.7g x1=% #12.7g y1=% #12.7g " + // "x2=% #12.7g y2=% #12.7g c=% #12.7g ",x0,y0,x1,y2,x2,y2,c); + dx01 = x0 - x1; // cout <<"L410 dx01="<GetName()); + a->InspectShape(); + return; +} + +void V11Geometry::printPcon(const TGeoPcon* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": N=" << a->GetNz() << " Phi1=" << a->GetPhi1() + << ", Dphi=" << a->GetDphi() << endl; + cout << "i\t Z \t Rmin \t Rmax" << endl; + for (Int_t iii = 0; iii < a->GetNz(); iii++) { + cout << iii << "\t" << a->GetZ(iii) << "\t" << a->GetRmin(iii) << "\t" << a->GetRmax(iii) + << endl; + } // end for iii + return; +} + +void V11Geometry::printTube(const TGeoTube* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Rmin=" << a->GetRmin() << " Rmax=" << a->GetRmax() + << " Dz=" << a->GetDz() << endl; + return; +} + +void V11Geometry::printTubeSeg(const TGeoTubeSeg* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Phi1=" << a->GetPhi1() << " Phi2=" << a->GetPhi2() + << " Rmin=" << a->GetRmin() << " Rmax=" << a->GetRmax() << " Dz=" << a->GetDz() << endl; + return; +} + +void V11Geometry::printConeSeg(const TGeoConeSeg* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Phi1=" << a->GetPhi1() << " Phi2=" << a->GetPhi2() + << " Rmin1=" << a->GetRmin1() << " Rmax1=" << a->GetRmax1() << " Rmin2=" << a->GetRmin2() + << " Rmax2=" << a->GetRmax2() << " Dz=" << a->GetDz() << endl; + return; +} + +void V11Geometry::printBBox(const TGeoBBox* a) const +{ + if (!getDebug()) { + return; + } + cout << a->GetName() << ": Dx=" << a->GetDX() << " Dy=" << a->GetDY() << " Dz=" << a->GetDZ() + << endl; + return; +} + +void V11Geometry::createDefaultMaterials() +{ + Int_t i; + Double_t w; + + // Define some elements + auto* eclH = new TGeoElement("EC0_H", "Hydrogen", 1, 1.00794); + auto* eclHe = new TGeoElement("EC0_He", "Helium", 2, 4.002602); + auto* eclC = new TGeoElement("EC0_C", "Carbon", 6, 12.0107); + auto* eclN = new TGeoElement("EC0_N", "Nitrogen", 7, 14.0067); + auto* eclO = new TGeoElement("EC0_O", "Oxygen", 8, 15.994); + auto* eclF = new TGeoElement("EC0_F", "Florine", 9, 18.9984032); + auto* eclNe = new TGeoElement("EC0_Ne", "Neon", 10, 20.1797); + auto* eclMg = new TGeoElement("EC0_Mg", "Magnesium", 12, 24.3050); + auto* eclAl = new TGeoElement("EC0_Al", "Aluminum", 13, 26981538); + auto* eclSi = new TGeoElement("EC0_Si", "Silicon", 14, 28.0855); + auto* eclP = new TGeoElement("EC0_P", "Phosphorous", 15, 30.973761); + auto* eclS = new TGeoElement("EC0_S", "Sulfur", 16, 32.065); + auto* eclAr = new TGeoElement("EC0_Ar", "Argon", 18, 39.948); + auto* eclTi = new TGeoElement("EC0_Ti", "Titanium", 22, 47.867); + auto* eclCr = new TGeoElement("EC0_Cr", "Chromium", 24, 51.9961); + auto* eclMn = new TGeoElement("EC0_Mn", "Manganese", 25, 54.938049); + auto* eclFe = new TGeoElement("EC0_Fe", "Iron", 26, 55.845); + auto* eclCo = new TGeoElement("EC0_Co", "Cobalt", 27, 58.933200); + auto* eclNi = new TGeoElement("EC0_Ni", "Nickrl", 28, 56.6930); + auto* eclCu = new TGeoElement("EC0_Cu", "Copper", 29, 63.546); + auto* eclZn = new TGeoElement("EC0_Zn", "Zinc", 30, 65.39); + auto* eclKr = new TGeoElement("EC0_Kr", "Krypton", 36, 83.80); + auto* eclMo = new TGeoElement("EC0_Mo", "Molylibdium", 42, 95.94); + auto* eclXe = new TGeoElement("EC0_Xe", "Zeon", 54, 131.293); + + // Start with the Materials since for any one material there + // can be defined more than one Medium. + // Air, dry. at 15degree C, 101325Pa at sea-level, % by volume + // (% by weight). Density is 351 Kg/m^3 + // N2 78.084% (75.47%), O2 20.9476% (23.20%), Ar 0.934 (1.28%)%, + // C02 0.0314% (0.0590%), Ne 0.001818% (0.0012%, CH4 0.002% (), + // He 0.000524% (0.00007%), Kr 0.000114% (0.0003%), H2 0.00005% (3.5E-6%), + // Xe 0.0000087% (0.00004 %), H2O 0.0% (dry) + trace amounts at the ppm + // levels. + auto* eclAir = new TGeoMixture("EC0_Air", 9); + w = 75.47E-2; + eclAir->AddElement(eclN, w); // Nitorgen, atomic + w = 23.29E-2 + // O2 + 5.90E-4 * 2. * 15.994 / (12.0107 + 2. * 15.994); // CO2. + eclAir->AddElement(eclO, w); // Oxygen, atomic + w = 1.28E-2; + eclAir->AddElement(eclAr, w); // Argon, atomic + w = 5.90E-4 * 12.0107 / (12.0107 + 2. * 15.994) + // CO2 + 2.0E-5 * 12.0107 / (12.0107 + 4. * 1.00794); // CH4 + eclAir->AddElement(eclC, w); // Carbon, atomic + w = 1.818E-5; + eclAir->AddElement(eclNe, w); // Ne, atomic + w = 3.5E-8; + eclAir->AddElement(eclHe, w); // Helium, atomic + w = 7.0E-7; + eclAir->AddElement(eclKr, w); // Krypton, atomic + w = 3.0E-6; + eclAir->AddElement(eclH, w); // Hydrogen, atomic + w = 4.0E-7; + eclAir->AddElement(eclXe, w); // Xenon, atomic + eclAir->SetDensity(351.0 * sKgm3); + eclAir->SetPressure(101325 * sPascal); + eclAir->SetTemperature(15.0 * sCelsius); + eclAir->SetState(TGeoMaterial::kMatStateGas); + + // Silicone + auto* eclSiDet = new TGeoMaterial("EC0_Si", eclSi, 2.33 * sGCm3); + eclSiDet->SetTemperature(15.0 * sCelsius); + eclSiDet->SetState(TGeoMaterial::kMatStateSolid); + + // Epoxy C18 H19 O3 + auto* eclEpoxy = new TGeoMixture("EC0_Epoxy", 3); + eclEpoxy->AddElement(eclC, 18); + eclEpoxy->AddElement(eclH, 19); + eclEpoxy->AddElement(eclO, 3); + eclEpoxy->SetDensity(1.8 * sGCm3); + eclEpoxy->SetTemperature(15.0 * sCelsius); + eclEpoxy->SetState(TGeoMaterial::kMatStateSolid); + + // Carbon Fiber, M55J, 60% fiber by volume. Fiber density + // 1.91 g/cm^3. See ToryaCA M55J data sheet. + // Begin_Html + /* + Data Sheet + + */ + // End_Html + auto* eclCarbonFiber = new TGeoMixture("EC0_CarbonFiber-M55J", 4); + // Assume that the epoxy fill in the space between the fibers and so + // no change in the total volume. To compute w, assume 1cm^3 total + // volume. + w = 1.91 / (1.91 + (1. - .60) * eclEpoxy->GetDensity()); + eclCarbonFiber->AddElement(eclC, w); + w = (1. - .60) * eclEpoxy->GetDensity() / (1.91 + (1. - .06) * eclEpoxy->GetDensity()); + + for (i = 0; i < eclEpoxy->GetNelements(); i++) { + eclCarbonFiber->AddElement(eclEpoxy->GetElement(i), eclEpoxy->GetWmixt()[i] * w); + } + + eclCarbonFiber->SetDensity((1.91 + (1. - .60) * eclEpoxy->GetDensity()) * sGCm3); + eclCarbonFiber->SetTemperature(22.0 * sCelsius); + eclCarbonFiber->SetState(TGeoMaterial::kMatStateSolid); + + // Rohacell 51A millable foam product. + // C9 H13 N1 O2 52Kg/m^3 + // Elemental composition, Private comunications with + // Bjorn S. Nilsen + // Begin_Html + /* + + Rohacell-A see Properties + + */ + // End_Html + auto* eclFoam = new TGeoMixture("EC0_Foam", 4); + eclFoam->AddElement(eclC, 9); + eclFoam->AddElement(eclH, 13); + eclFoam->AddElement(eclN, 1); + eclFoam->AddElement(eclO, 2); + eclFoam->SetTitle("Rohacell 51 A"); + eclFoam->SetDensity(52. * sKgm3); + eclFoam->SetTemperature(22.0 * sCelsius); + eclFoam->SetState(TGeoMaterial::kMatStateSolid); + + // Kapton % by weight, H 2.6362, C69.1133, N 7.3270, O 20.0235 + // Density 1.42 g/cm^3 + // Begin_Html + /* + + Kapton. also see + + + */ + // End_Html + auto* eclKapton = new TGeoMixture("EC0_Kapton", 4); + eclKapton->AddElement(eclH, 0.026362); + eclKapton->AddElement(eclC, 0.691133); + eclKapton->AddElement(eclN, 0.073270); + eclKapton->AddElement(eclO, 0.200235); + eclKapton->SetTitle("Kapton ribon and cable base"); + eclKapton->SetDensity(1.42 * sGCm3); + eclKapton->SetTemperature(22.0 * sCelsius); + eclKapton->SetState(TGeoMaterial::kMatStateSolid); + + // UPILEX-S C16 H6 O4 N2 polymer (a Kapton like material) + // Density 1.47 g/cm^3 + // Begin_Html + /* + + UPILEX-S. also see + + + */ + // End_Html + auto* eclUpilex = new TGeoMixture("EC0_Upilex", 4); + eclUpilex->AddElement(eclC, 16); + eclUpilex->AddElement(eclH, 6); + eclUpilex->AddElement(eclN, 2); + eclUpilex->AddElement(eclO, 4); + eclUpilex->SetTitle("Upilex ribon, cable, and pcb base"); + eclUpilex->SetDensity(1.47 * sGCm3); + eclUpilex->SetTemperature(22.0 * sCelsius); + eclUpilex->SetState(TGeoMaterial::kMatStateSolid); + + // Aluminum 6061 (Al used by US groups) + // % by weight, Cr 0.04-0.35 range [0.0375 nominal value used] + // Cu 0.15-0.4 [0.275], Fe Max 0.7 [0.35], Mg 0.8-1.2 [1.0], + // Mn Max 0.15 [0.075] Si 0.4-0.8 [0.6], Ti Max 0.15 [0.075], + // Zn Max 0.25 [0.125], Rest Al [97.4625]. Density 2.7 g/cm^3 + // Begin_Html + /* + + Aluminum 6061 specifications + + */ + // End_Html + auto* eclAl6061 = new TGeoMixture("EC0_Al6061", 9); + eclAl6061->AddElement(eclCr, 0.000375); + eclAl6061->AddElement(eclCu, 0.00275); + eclAl6061->AddElement(eclFe, 0.0035); + eclAl6061->AddElement(eclMg, 0.01); + eclAl6061->AddElement(eclMn, 0.00075); + eclAl6061->AddElement(eclSi, 0.006); + eclAl6061->AddElement(eclTi, 0.00075); + eclAl6061->AddElement(eclZn, 0.00125); + eclAl6061->AddElement(eclAl, 0.974625); + eclAl6061->SetTitle("Aluminum Alloy 6061"); + eclAl6061->SetDensity(2.7 * sGCm3); + eclAl6061->SetTemperature(22.0 * sCelsius); + eclAl6061->SetState(TGeoMaterial::kMatStateSolid); + + // Aluminum 7075 (Al used by Italian groups) + // % by weight, Cr 0.18-0.28 range [0.23 nominal value used] + // Cu 1.2-2.0 [1.6], Fe Max 0.5 [0.25], Mg 2.1-2.9 [2.5], + // Mn Max 0.3 [0.125] Si Max 0.4 [0.2], Ti Max 0.2 [0.1], + // Zn 5.1-6.1 [5.6], Rest Al [89.395]. Density 2.81 g/cm^3 + // Begin_Html + /* + + Aluminum 7075 specifications + + */ + // End_Html + auto* eclAl7075 = new TGeoMixture("EC0_Al7075", 9); + eclAl7075->AddElement(eclCr, 0.0023); + eclAl7075->AddElement(eclCu, 0.016); + eclAl7075->AddElement(eclFe, 0.0025); + eclAl7075->AddElement(eclMg, 0.025); + eclAl7075->AddElement(eclMn, 0.00125); + eclAl7075->AddElement(eclSi, 0.002); + eclAl7075->AddElement(eclTi, 0.001); + eclAl7075->AddElement(eclZn, 0.056); + eclAl7075->AddElement(eclAl, 0.89395); + eclAl7075->SetTitle("Aluminum Alloy 7075"); + eclAl7075->SetDensity(2.81 * sGCm3); + eclAl7075->SetTemperature(22.0 * sCelsius); + eclAl7075->SetState(TGeoMaterial::kMatStateSolid); + + // "Ruby" spheres, Al2 O3 + // "Ruby" Sphere posts, Ryton R-4 04 + // Begin_Html + /* + + Ruby Sphere Posts + + */ + // End_Html + auto* eclRuby = new TGeoMixture("EC0_RubySphere", 2); + eclRuby->AddElement(eclAl, 2); + eclRuby->AddElement(eclO, 3); + eclRuby->SetTitle("Ruby reference sphere"); + eclRuby->SetDensity(2.81 * sGCm3); + eclRuby->SetTemperature(22.0 * sCelsius); + eclRuby->SetState(TGeoMaterial::kMatStateSolid); + + // Inox, AISI 304L, compoistion % by weight (assumed) + // C Max 0.03 [0.015], Mn Max 2.00 [1.00], Si Max 1.00 [0.50] + // P Max 0.045 [0.0225], S Max 0.03 [0.015], Ni 8.0-10.5 [9.25] + // Cr 18-20 [19.], Mo 2.-2.5 [2.25], rest Fe: density 7.93 Kg/dm^3 + // Begin_Html + /* + + Stainless steal (INOX) AISI 304L composition + + */ + // End_Html + auto* eclInox304L = new TGeoMixture("EC0_Inox304L", 9); + eclInox304L->AddElement(eclC, 0.00015); + eclInox304L->AddElement(eclMn, 0.010); + eclInox304L->AddElement(eclSi, 0.005); + eclInox304L->AddElement(eclP, 0.000225); + eclInox304L->AddElement(eclS, 0.00015); + eclInox304L->AddElement(eclNi, 0.0925); + eclInox304L->AddElement(eclCr, 0.1900); + eclInox304L->AddElement(eclMo, 0.0225); + eclInox304L->AddElement(eclFe, 0.679475); // Rest Fe + eclInox304L->SetTitle("EC0 Stainless Steal (Inox) type AISI 304L"); + eclInox304L->SetDensity(7.93 * sKgdm3); + eclInox304L->SetTemperature(22.0 * sCelsius); + eclInox304L->SetState(TGeoMaterial::kMatStateSolid); + + // Inox, AISI 316L, composition % by weight (assumed) + // C Max 0.03 [0.015], Mn Max 2.00 [1.00], Si Max 1.00 [0.50] + // P Max 0.045 [0.0225], S Max 0.03 [0.015], Ni 10.0-14. [12.] + // Cr 16-18 [17.], Mo 2-3 [2.5]: density 7.97 Kg/dm^3 + // Begin_Html + /* + + Stainless steal (INOX) AISI 316L composition + + */ + // End_Html + auto* eclInox316L = new TGeoMixture("EC0_Inox316L", 9); + eclInox316L->AddElement(eclC, 0.00015); + eclInox316L->AddElement(eclMn, 0.010); + eclInox316L->AddElement(eclSi, 0.005); + eclInox316L->AddElement(eclP, 0.000225); + eclInox316L->AddElement(eclS, 0.00015); + eclInox316L->AddElement(eclNi, 0.12); + eclInox316L->AddElement(eclCr, 0.17); + eclInox316L->AddElement(eclMo, 0.025); + eclInox316L->AddElement(eclFe, 0.66945); // Rest Fe + eclInox316L->SetTitle("EC0 Stainless Steal (Inox) type AISI 316L"); + eclInox316L->SetDensity(7.97 * sKgdm3); + eclInox316L->SetTemperature(22.0 * sCelsius); + eclInox316L->SetState(TGeoMaterial::kMatStateSolid); + + // Inox, Phynox or Elgiloy AMS 5833, composition % by weight + // C Max 0.15 [0.15], Mn Max 2.00 [2.00], Be max 0.0001 [none] + // Ni 18. [18.], Cr 21.5 [21.5], Mo 7.5 [7.5], Co 42 [42.]: + // density 8.3 Kg/dm^3 + // Begin_Html + /* + + Compostion of Phynox or Elgiloy AMS 5833, also see + + + under corss reference number [0024]. + + */ + // End_Html + auto* eclPhynox = new TGeoMixture("EC0_Phynox", 7); + eclPhynox->AddElement(eclC, 0.0015); + eclPhynox->AddElement(eclMn, 0.020); + eclPhynox->AddElement(eclNi, 0.18); + eclPhynox->AddElement(eclCr, 0.215); + eclPhynox->AddElement(eclMo, 0.075); + eclPhynox->AddElement(eclCo, 0.42); + eclPhynox->AddElement(eclFe, 0.885); + eclPhynox->SetTitle("EC0 Cooling tube alloy"); + eclPhynox->SetDensity(8.3 * sGCm3); + eclPhynox->SetTemperature(22.0 * sCelsius); + eclPhynox->SetState(TGeoMaterial::kMatStateSolid); + + // G10FR4 + + // Demineralized Water H2O SDD & SSD Cooling liquid + auto* eclWater = new TGeoMixture("EC0_Water", 2); + eclWater->AddElement(eclH, 2); + eclWater->AddElement(eclO, 1); + eclWater->SetTitle("EC0 Cooling Water"); + eclWater->SetDensity(1.0 * sGCm3); + eclWater->SetTemperature(22.0 * sCelsius); + eclWater->SetState(TGeoMaterial::kMatStateLiquid); + + // Freon SPD Cooling liquid PerFluorobuthane C4F10 + // Begin_Html + /* + + SPD 2 phase cooling using PerFluorobuthane + + */ + // End_Html + auto* eclFreon = new TGeoMixture("EC0_SPD_Freon", 2); + eclFreon->AddElement(eclC, 4); + eclFreon->AddElement(eclF, 10); + eclFreon->SetTitle("EC0 SPD 2 phase Cooling freon"); + eclFreon->SetDensity(1.52 * sGCm3); + eclFreon->SetTemperature(22.0 * sCelsius); + eclFreon->SetState(TGeoMaterial::kMatStateLiquid); + + // Int_t ifield = ((AliMagF*)TGeoGlobalMagField::Instance()->GetField())->Integ(); + // Float_t fieldm = ((AliMagF*)TGeoGlobalMagField::Instance()->GetField())->Max(); + + // Float_t tmaxfd = 0.1;// 1.0;// Degree + // Float_t stemax = 1.0;// cm + // Float_t deemax = 0.1;// 30.0;// Fraction of particle's energy 0GetNz(); + if (n <= 0) { + return; + } + m = 2 * n + 1; + z = new Double_t[m]; + r = new Double_t[m]; + + for (i = 0; i < n; i++) { + z[i] = p->GetZ(i); + r[i] = p->GetRmax(i); + z[i + n] = p->GetZ(n - 1 - i); + r[i + n] = p->GetRmin(n - 1 - i); + } // end for i + z[n - 1] = z[0]; + r[n - 1] = r[0]; + + line = new TPolyLine(n, z, r); + pts = new TPolyMarker(n, z, r); + + line->SetFillColor(fillc); + line->SetFillStyle(fills); + line->SetLineColor(linec); + line->SetLineStyle(lines); + line->SetLineWidth(linew); + pts->SetMarkerColor(markc); + pts->SetMarkerStyle(marks); + pts->SetMarkerSize(marksize); + + line->Draw("f"); + line->Draw(); + pts->Draw(); + + delete[] z; + delete[] r; + + cout << "Hit Return to continue" << endl; + cin >> n; + delete line; + delete pts; + return; +} + +Bool_t V11Geometry::angleOfIntersectionWithLine(Double_t x0, Double_t y0, Double_t x1, Double_t y1, + Double_t xc, Double_t yc, Double_t rc, Double_t& t0, + Double_t& t1) const +{ + Double_t dx, dy, cx, cy, s2, t[4]; + Double_t a0, b0, c0, a1, b1, c1, sinthp, sinthm, costhp, costhm; + Int_t i, j; + + t0 = 400.0; + t1 = 400.0; + dx = x1 - x0; + dy = y1 - y0; + cx = xc - x0; + cy = yc - y0; + s2 = dx * dx + dy * dy; + if (s2 == 0.0) { + return kFALSE; + } + + a0 = rc * rc * s2; + if (a0 == 0.0) { + return kFALSE; + } + b0 = 2.0 * rc * dx * (dx * cy - cx * dy); + c0 = dx * dx * cy * cy - 2.0 * dy * dx * cy * cx + cx * cx * dy * dy - rc * rc * dy * dy; + c0 = 0.25 * b0 * b0 / (a0 * a0) - c0 / a0; + if (c0 < 0.0) { + return kFALSE; + } + sinthp = -0.5 * b0 / a0 + TMath::Sqrt(c0); + sinthm = -0.5 * b0 / a0 - TMath::Sqrt(c0); + + a1 = rc * rc * s2; + if (a1 == 0.0) { + return kFALSE; + } + b1 = 2.0 * rc * dy * (dy * cx - dx * cy); + c1 = dy * dy * cx * cx - 2.0 * dy * dx * cy * cx + dx * dx * cy * cy - rc * rc * dx * dx; + c1 = 0.25 * b1 * b1 / (a1 * a1) - c1 / a1; + if (c1 < 0.0) { + return kFALSE; + } + costhp = -0.5 * b1 / a1 + TMath::Sqrt(c1); + costhm = -0.5 * b1 / a1 - TMath::Sqrt(c1); + + t[0] = t[1] = t[2] = t[3] = 400.; + a0 = TMath::ATan2(sinthp, costhp); + if (a0 < 0.0) { + a0 += 2.0 * TMath::Pi(); + } + a1 = TMath::ATan2(sinthp, costhm); + if (a1 < 0.0) { + a1 += 2.0 * TMath::Pi(); + } + b0 = TMath::ATan2(sinthm, costhp); + if (b0 < 0.0) { + b0 += 2.0 * TMath::Pi(); + } + b1 = TMath::ATan2(sinthm, costhm); + if (b1 < 0.0) { + b1 += 2.0 * TMath::Pi(); + } + x1 = xc + rc * TMath::Cos(a0); + y1 = yc + rc * TMath::Sin(a0); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[0] = a0 * TMath::RadToDeg(); + } + x1 = xc + rc * TMath::Cos(a1); + y1 = yc + rc * TMath::Sin(a1); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[1] = a1 * TMath::RadToDeg(); + } + x1 = xc + rc * TMath::Cos(b0); + y1 = yc + rc * TMath::Sin(b0); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[2] = b0 * TMath::RadToDeg(); + } + x1 = xc + rc * TMath::Cos(b1); + y1 = yc + rc * TMath::Sin(b1); + s2 = dx * (y1 - y0) - dy * (x1 - x0); + if (s2 * s2 < DBL_EPSILON) { + t[3] = b1 * TMath::RadToDeg(); + } + for (i = 0; i < 4; i++) { + for (j = i + 1; j < 4; j++) { + if (t[i] > t[j]) { + t0 = t[i]; + t[i] = t[j]; + t[j] = t0; + } + } // end for i,j + } + t0 = t[0]; + t1 = t[1]; + + return kTRUE; +} + +Double_t V11Geometry::angleForRoundedCorners0(Double_t dx, Double_t dy, Double_t sdr) const +{ + Double_t a, b; + + b = dy * dy + dx * dx - sdr * sdr; + if (b < 0.0) { + Error("AngleForRoundedCorners0", "dx^2(%e)+dy^2(%e)-sdr^2(%e)=b=%e<0", dx, dy, sdr, b); + } + b = TMath::Sqrt(b); + a = -sdr * dy + dx * b; + b = -sdr * dx - dy * b; + return TMath::ATan2(a, b) * TMath::RadToDeg(); +} + +Double_t V11Geometry::angleForRoundedCorners1(Double_t dx, Double_t dy, Double_t sdr) const +{ + Double_t a, b; + + b = dy * dy + dx * dx - sdr * sdr; + if (b < 0.0) { + Error("AngleForRoundedCorners1", "dx^2(%e)+dy^2(%e)-sdr^2(%e)=b=%e<0", dx, dy, sdr, b); + } + b = TMath::Sqrt(b); + a = -sdr * dy - dx * b; + b = -sdr * dx + dy * b; + return TMath::ATan2(a, b) * TMath::RadToDeg(); +} + +void V11Geometry::anglesForRoundedCorners(Double_t x0, Double_t y0, Double_t r0, Double_t x1, + Double_t y1, Double_t r1, Double_t& t0, Double_t& t1) + const +{ + Double_t t; + + if (r0 >= 0.0 && r1 >= 0.0) { // Inside to inside ++ + t = angleForRoundedCorners1(x1 - x0, y1 - y0, r1 - r0); + t0 = t1 = t; + return; + } else if (r0 >= 0.0 && r1 <= 0.0) { // Inside to Outside +- + r1 = -r1; // make positive + t = angleForRoundedCorners0(x1 - x0, y1 - y0, r1 + r0); + t0 = 180.0 + t; + if (t0 < 0.0) { + t += 360.; + } + if (t < 0.0) { + t += 360.; + } + t1 = t; + return; + } else if (r0 <= 0.0 && r1 >= 0.0) { // Outside to Inside -+ + r0 = -r0; // make positive + t = angleForRoundedCorners1(x1 - x0, y1 - y0, r1 + r0); + t0 = 180.0 + t; + if (t0 > 180.) { + t0 -= 360.; + } + if (t > 180.) { + t -= 360.; + } + t1 = t; + return; + } else if (r0 <= 0.0 && r1 <= 0.0) { // Outside to outside -- + r0 = -r0; // make positive + r1 = -r1; // make positive + t = angleForRoundedCorners0(x1 - x0, y1 - y0, r1 - r0); + t0 = t1 = t; + return; + } + return; +} + +void V11Geometry::makeFigure1(Double_t x0, Double_t y0, Double_t r0, Double_t x1, Double_t y1, + Double_t r1) +{ + Double_t t0[4], t1[4], xa0[4], ya0[4], xa1[4], ya1[4], ra0[4], ra1[4]; + Double_t xmin, ymin, xmax, ymax, h; + Int_t j; + + for (j = 0; j < 4; j++) { + ra0[j] = r0; + if (j % 2) { + ra0[j] = -r0; + } + ra1[j] = r1; + if (j > 1) { + ra1[j] = -r1; + } + anglesForRoundedCorners(x0, y0, ra0[j], x1, y1, ra1[j], t0[j], t1[j]); + xa0[j] = TMath::Abs(r0) * cosD(t0[j]) + x0; + ya0[j] = TMath::Abs(r0) * sinD(t0[j]) + y0; + xa1[j] = TMath::Abs(r1) * cosD(t1[j]) + x1; + ya1[j] = TMath::Abs(r1) * sinD(t1[j]) + y1; + } + if (r0 < 0.0) { + r0 = -r0; + } + if (r1 < 0.0) { + r1 = -r1; + } + xmin = TMath::Min(x0 - r0, x1 - r1); + ymin = TMath::Min(y0 - r0, y1 - r1); + xmax = TMath::Max(x0 + r0, x1 + r1); + ymax = TMath::Max(y0 + r0, y1 + r1); + + for (j = 1; j < 4; j++) { + xmin = TMath::Min(xmin, xa0[j]); + xmin = TMath::Min(xmin, xa1[j]); + ymin = TMath::Min(ymin, ya0[j]); + ymin = TMath::Min(ymin, ya1[j]); + + xmax = TMath::Max(xmax, xa0[j]); + xmax = TMath::Max(xmax, xa1[j]); + ymax = TMath::Max(ymax, ya0[j]); + ymax = TMath::Max(ymax, ya1[j]); + } + if (xmin < 0.0) { + xmin *= 1.1; + } else { + xmin *= 0.9; + } + if (ymin < 0.0) { + ymin *= 1.1; + } else { + ymin *= 0.9; + } + if (xmax < 0.0) { + xmax *= 0.9; + } else { + xmax *= 1.1; + } + if (ymax < 0.0) { + ymax *= 0.9; + } else { + ymax *= 1.1; + } + j = (Int_t)(500.0 * (ymax - ymin) / (xmax - xmin)); + auto* can = + new TCanvas("V11Geometry_AnglesForRoundedCorners", "Figure for V11Geometry", 500, j); + h = ymax - ymin; + if (h < 0) { + h = -h; + } + can->Range(xmin, ymin, xmax, ymax); + auto* c0 = new TArc(x0, y0, r0); + auto* c1 = new TArc(x1, y1, r1); + TLine* line[4]; + TArrow* ar0[4]; + TArrow* ar1[4]; + + for (j = 0; j < 4; j++) { + ar0[j] = new TArrow(x0, y0, xa0[j], ya0[j]); + ar1[j] = new TArrow(x1, y1, xa1[j], ya1[j]); + line[j] = new TLine(xa0[j], ya0[j], xa1[j], ya1[j]); + ar0[j]->SetLineColor(j + 1); + ar0[j]->SetArrowSize(0.1 * r0 / h); + ar1[j]->SetLineColor(j + 1); + ar1[j]->SetArrowSize(0.1 * r1 / h); + line[j]->SetLineColor(j + 1); + } + c0->Draw(); + c1->Draw(); + + for (j = 0; j < 4; j++) { + ar0[j]->Draw(); + ar1[j]->Draw(); + line[j]->Draw(); + } + + auto* t = new TText(); + t->SetTextSize(0.02); + Char_t txt[100]; + snprintf(txt, 99, "(x0=%5.2f,y0=%5.2f)", x0, y0); + t->DrawText(x0, y0, txt); + snprintf(txt, 99, "(x1=%5.2f,y1=%5.2f)", x1, y1); + + for (j = 0; j < 4; j++) { + t->SetTextColor(j + 1); + t->DrawText(x1, y1, txt); + snprintf(txt, 99, "r0=%5.2f", ra0[j]); + t->DrawText(0.5 * (x0 + xa0[j]), 0.5 * (y0 + ya0[j]), txt); + snprintf(txt, 99, "r1=%5.2f", ra1[j]); + t->DrawText(0.5 * (x1 + xa1[j]), 0.5 * (y1 + ya1[j]), txt); + } +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V1Layer.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V1Layer.cxx new file mode 100644 index 0000000000000..b3f31ba0d52b6 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V1Layer.cxx @@ -0,0 +1,2760 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 V1Layer.cxx +/// \brief Implementation of the V1Layer class +/// \author Mario Sitta +/// \author Chinorat Kobdaj (kobdaj@g.sut.ac.th) + +#include "EC0Simulation/V1Layer.h" +#include "ECLayersBase/GeometryTGeo.h" +#include "EC0Simulation/Detector.h" + +#include "FairLogger.h" // for LOG + +#include // for TGeoArb8 +#include // for TGeoBBox +#include // for TGeoConeSeg, TGeoCone +#include // for TGeoManager, gGeoManager +#include // for TGeoCombiTrans, TGeoRotation, etc +#include // for TGeoTrd1 +#include // for TGeoTube, TGeoTubeSeg +#include // for TGeoVolume, TGeoVolumeAssembly +#include // for TGeoXtru +#include "TMathBase.h" // for Abs +#include // for Sin, RadToDeg, DegToRad, Cos, Tan, etc + +#include // for snprintf + +class TGeoMedium; + +using namespace TMath; +using namespace o2::ecl; + +// General Parameters +const Int_t V1Layer::sNumberOmInnerLayers = 3; + +const Double_t V1Layer::sDefaultSensorThick = 300 * sMicron; +const Double_t V1Layer::sDefaultStaveThick = 1 * sCm; + +// Inner Barrel Parameters +const Int_t V1Layer::sIBChipsPerRow = 9; +const Int_t V1Layer::sIBNChipRows = 1; + +// Outer Barrel Parameters +const Int_t V1Layer::sOBChipsPerRow = 7; +const Int_t V1Layer::sOBNChipRows = 2; + +const Double_t V1Layer::sOBHalfStaveWidth = 3.01 * sCm; +const Double_t V1Layer::sOBModuleWidth = sOBHalfStaveWidth; +const Double_t V1Layer::sOBModuleGap = 0.01 * sCm; +const Double_t V1Layer::sOBChipXGap = 0.01 * sCm; +const Double_t V1Layer::sOBChipZGap = 0.01 * sCm; +const Double_t V1Layer::sOBFlexCableAlThick = 0.005 * sCm; +const Double_t V1Layer::sOBFlexCableKapThick = 0.01 * sCm; +const Double_t V1Layer::sOBBusCableAlThick = 0.02 * sCm; +const Double_t V1Layer::sOBBusCableKapThick = 0.02 * sCm; +const Double_t V1Layer::sOBColdPlateThick = 0.012 * sCm; +const Double_t V1Layer::sOBCarbonPlateThick = 0.012 * sCm; +const Double_t V1Layer::sOBGlueThick = 0.03 * sCm; +const Double_t V1Layer::sOBModuleZLength = 21.06 * sCm; +const Double_t V1Layer::sOBHalfStaveYTrans = 1.76 * sMm; +const Double_t V1Layer::sOBHalfStaveXOverlap = 4.3 * sMm; +const Double_t V1Layer::sOBGraphiteFoilThick = 30.0 * sMicron; +const Double_t V1Layer::sOBCoolTubeInnerD = 2.052 * sMm; +const Double_t V1Layer::sOBCoolTubeThick = 32.0 * sMicron; +const Double_t V1Layer::sOBCoolTubeXDist = 11.1 * sMm; + +const Double_t V1Layer::sOBSpaceFrameWidth = 42.0 * sMm; +const Double_t V1Layer::sOBSpaceFrameTotHigh = 43.1 * sMm; +const Double_t V1Layer::sOBSFrameBeamRadius = 0.6 * sMm; +const Double_t V1Layer::sOBSpaceFrameLa = 3.0 * sMm; +const Double_t V1Layer::sOBSpaceFrameHa = 0.721979 * sMm; +const Double_t V1Layer::sOBSpaceFrameLb = 3.7 * sMm; +const Double_t V1Layer::sOBSpaceFrameHb = 0.890428 * sMm; +const Double_t V1Layer::sOBSpaceFrameL = 0.25 * sMm; +const Double_t V1Layer::sOBSFBotBeamAngle = 56.5; +const Double_t V1Layer::sOBSFrameBeamSidePhi = 65.0; + +ClassImp(V1Layer); + +#define SQ(A) (A) * (A) + +V1Layer::V1Layer() + : V11Geometry(), + mLayerNumber(0), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(false), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(Int_t debug) + : V11Geometry(debug), + mLayerNumber(0), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(false), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(Int_t lay, Int_t debug) + : V11Geometry(debug), + mLayerNumber(lay), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(false), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(Int_t lay, Bool_t turbo, Int_t debug) + : V11Geometry(debug), + mLayerNumber(lay), + mPhi0(0), + mLayerRadius(0), + mZLength(0), + mSensorThickness(0), + mStaveThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(turbo), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V1Layer::V1Layer(const V1Layer& s) + : V11Geometry(s.getDebug()), + mLayerNumber(s.mLayerNumber), + mPhi0(s.mPhi0), + mLayerRadius(s.mLayerRadius), + mZLength(s.mZLength), + mSensorThickness(s.mSensorThickness), + mStaveThickness(s.mStaveThickness), + mStaveWidth(s.mStaveWidth), + mStaveTilt(s.mStaveTilt), + mNumberOfStaves(s.mNumberOfStaves), + mNumberOfModules(s.mNumberOfModules), + mNumberOfChips(s.mNumberOfChips), + mChipTypeID(s.mChipTypeID), + mIsTurbo(s.mIsTurbo), + mBuildLevel(s.mBuildLevel), + mStaveModel(s.mStaveModel) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = s.mHierarchy[i]; + } +} + +V1Layer& V1Layer::operator=(const V1Layer& s) +{ + if (&s == this) { + return *this; + } + + mLayerNumber = s.mLayerNumber; + mPhi0 = s.mPhi0; + mLayerRadius = s.mLayerRadius; + mZLength = s.mZLength; + mSensorThickness = s.mSensorThickness; + mStaveThickness = s.mStaveThickness; + mStaveWidth = s.mStaveWidth; + mStaveTilt = s.mStaveTilt; + mNumberOfStaves = s.mNumberOfStaves; + mNumberOfModules = s.mNumberOfModules; + mNumberOfChips = s.mNumberOfChips; + mIsTurbo = s.mIsTurbo; + mChipTypeID = s.mChipTypeID; + mBuildLevel = s.mBuildLevel; + mStaveModel = s.mStaveModel; + for (int i = kNHLevels; i--;) { + mHierarchy[i] = s.mHierarchy[i]; + } + + return *this; +} + +V1Layer::~V1Layer() = default; + +void V1Layer::createLayer(TGeoVolume* motherVolume) +{ + char volumeName[30]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper parameters + if (mLayerRadius <= 0) { + LOG(FATAL) << "Wrong layer radius " << mLayerRadius; + } + + if (mZLength <= 0) { + LOG(FATAL) << "Wrong layer length " << mZLength; + } + + if (mNumberOfStaves <= 0) { + LOG(FATAL) << "Wrong number of staves " << mNumberOfStaves; + } + + if (mNumberOfChips <= 0) { + LOG(FATAL) << "Wrong number of chips " << mNumberOfChips; + } + + if (mLayerNumber >= sNumberOmInnerLayers && mNumberOfModules <= 0) { + LOG(FATAL) << "Wrong number of modules " << mNumberOfModules; + } + + if (mStaveThickness <= 0) { + LOG(INFO) << "Stave thickness wrong or not set " << mStaveThickness << " using default " + << sDefaultStaveThick; + mStaveThickness = sDefaultStaveThick; + } + + if (mSensorThickness <= 0) { + LOG(INFO) << "Sensor thickness wrong or not set " << mSensorThickness << " using default " + << sDefaultSensorThick; + mSensorThickness = sDefaultSensorThick; + } + + if (mSensorThickness > mStaveThickness) { + LOG(WARNING) << "Sensor thickness " << mSensorThickness << " is greater than stave thickness " + << mStaveThickness << " fixing"; + mSensorThickness = mStaveThickness; + } + + // If a Turbo layer is requested, do it and exit + if (mIsTurbo) { + createLayerTurbo(motherVolume); + return; + } + + // First create the stave container + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // mStaveWidth = mLayerRadius*Tan(alpha); + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0LayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + + // layerVolume->SetVisibility(kFALSE); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius + ((TGeoBBox*)stavVol->GetShape())->GetDY(); + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode(stavVol, j, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + // geometry is served + return; +} + +void V1Layer::createLayerTurbo(TGeoVolume* motherVolume) +{ + char volumeName[30]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper (remaining) parameters + if (mStaveWidth <= 0) { + LOG(FATAL) << "Wrong stave width " << mStaveWidth; + } + + if (Abs(mStaveTilt) > 45) { + LOG(WARNING) << "Stave tilt angle (" << mStaveTilt << ") greater than 45deg"; + } + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0LayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius /* +chip thick ?! */; + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode( + stavVol, j, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi - mStaveTilt, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + return; +} + +TGeoVolume* V1Layer::createStave(const TGeoManager* /*mgr*/) +{ + char volumeName[30]; + + Double_t xlen, ylen, zlen; + Double_t xpos, ypos; + Double_t alpha; + + // First create all needed shapes + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // The stave + xlen = mLayerRadius * Tan(alpha); + if (mIsTurbo) { + xlen = 0.5 * mStaveWidth; + } + ylen = 0.5 * mStaveThickness; + zlen = 0.5 * mZLength; + + Double_t yplus = 0.46; + auto* stave = new TGeoXtru(2); // z sections + Double_t xv[5] = {xlen, xlen, 0, -xlen, -xlen}; + Double_t yv[5] = {ylen + 0.09, -0.15, -yplus - mSensorThickness, -0.15, ylen + 0.09}; + stave->DefinePolygon(5, xv, yv); + stave->DefineSection(0, -zlen, 0, 0, 1.); + stave->DefineSection(1, +zlen, 0, 0, 1.); + + // We have all shapes: now create the real volumes + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0StavePattern(), mLayerNumber); + // TGeoVolume *staveVol = new TGeoVolume(volumeName, stave, medAir); + TGeoVolume* staveVol = new TGeoVolumeAssembly(volumeName); + + // staveVol->SetVisibility(kFALSE); + staveVol->SetVisibility(kTRUE); + staveVol->SetLineColor(2); + TGeoVolume* mechStaveVol = nullptr; + + // Now build up the stave + if (mLayerNumber < sNumberOmInnerLayers) { + TGeoVolume* modVol = createStaveInnerB(xlen, ylen, zlen); + staveVol->AddNode(modVol, 0); + mHierarchy[kHalfStave] = 1; + + // Mechanical stave structure + mechStaveVol = createStaveStructInnerB(xlen, zlen); + if (mechStaveVol) { + ypos = ((TGeoBBox*)(modVol->GetShape()))->GetDY() + + ((TGeoBBox*)(mechStaveVol->GetShape()))->GetDY(); + staveVol->AddNode(mechStaveVol, 1, + new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", 0, 0, 180))); + } + } else { + TGeoVolume* hstaveVol = createStaveOuterB(); + if (mStaveModel == Detector::kOBModel0) { // Create simplified stave struct as in v0 + staveVol->AddNode(hstaveVol, 0); + mHierarchy[kHalfStave] = 1; + } else { // (if mStaveModel) Create new stave struct as in TDR + xpos = ((TGeoBBox*)(hstaveVol->GetShape()))->GetDX() - sOBHalfStaveXOverlap / 2; + // ypos is CF height as computed in createSpaceFrameOuterB1 + ypos = (sOBSpaceFrameTotHigh - sOBHalfStaveYTrans) / 2; + staveVol->AddNode(hstaveVol, 0, new TGeoTranslation(-xpos, ypos, 0)); + staveVol->AddNode(hstaveVol, 1, new TGeoTranslation(xpos, ypos + sOBHalfStaveYTrans, 0)); + mHierarchy[kHalfStave] = 2; // RS + mechStaveVol = createSpaceFrameOuterB(); + + if (mechStaveVol) { + staveVol->AddNode(mechStaveVol, 1, + new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", 180, 0, 0))); + } + } + } + // Done, return the stave + return staveVol; +} + +TGeoVolume* V1Layer::createStaveInnerB(const Double_t xsta, const Double_t ysta, + const Double_t zsta, const TGeoManager* mgr) +{ + Double_t xmod, ymod, zmod; + char volumeName[30]; + + // First we create the module (i.e. the HIC with 9 chips) + TGeoVolume* moduleVol = createModuleInnerB(xsta, ysta, zsta); + + // Then we create the fake halfstave and the actual stave + xmod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDX(); + ymod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDY(); + zmod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDZ(); + + auto* hstave = new TGeoBBox(xmod, ymod, zmod); + + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0HalfStavePattern(), mLayerNumber); + auto* hstaveVol = new TGeoVolume(volumeName, hstave, medAir); + + // Finally build it up + hstaveVol->AddNode(moduleVol, 0); + mHierarchy[kModule] = 1; + + // Done, return the stave structure + return hstaveVol; +} + +TGeoVolume* V1Layer::createModuleInnerB(Double_t xmod, Double_t ymod, Double_t zmod, + const TGeoManager* mgr) +{ + Double_t zchip; + Double_t zpos; + char volumeName[30]; + + // First create the single chip + zchip = zmod / sIBChipsPerRow; + TGeoVolume* chipVol = createChipInnerB(xmod, ymod, zchip); + + // Then create the module and populate it with the chips + auto* module = new TGeoBBox(xmod, ymod, zmod); + + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0ModulePattern(), mLayerNumber); + auto* modVol = new TGeoVolume(volumeName, module, medAir); + + // mm (not used) zlen = ((TGeoBBox*)chipVol->GetShape())->GetDZ(); + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + zpos = -zmod + j * 2 * zchip + zchip; + modVol->AddNode(chipVol, j, new TGeoTranslation(0, 0, zpos)); + mHierarchy[kChip]++; + } + // Done, return the module + return modVol; +} + +TGeoVolume* V1Layer::createStaveStructInnerB(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kIBModelDummy: + mechStavVol = createStaveModelInnerBDummy(xsta, zsta, mgr); + break; + case Detector::kIBModel0: + mechStavVol = createStaveModelInnerB0(xsta, zsta, mgr); + break; + case Detector::kIBModel1: + mechStavVol = createStaveModelInnerB1(xsta, zsta, mgr); + break; + case Detector::kIBModel21: + mechStavVol = createStaveModelInnerB21(xsta, zsta, mgr); + break; + case Detector::kIBModel22: + mechStavVol = createStaveModelInnerB22(xsta, zsta, mgr); + break; + case Detector::kIBModel3: + mechStavVol = createStaveModelInnerB3(xsta, zsta, mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelInnerBDummy(const Double_t, const Double_t, + const TGeoManager*) const +{ + // Done, return the stave structur + return nullptr; +} + +TGeoVolume* V1Layer::createStaveModelInnerB0(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("EC0_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("EC0_FLEXCABLE$"); + + // Local parameters + Double_t kConeOutRadius = 0.15 / 2; + Double_t kConeInRadius = 0.1430 / 2; + Double_t kStaveLength = zsta * 2; + Double_t kStaveWidth = xsta * 2 - kConeOutRadius * 2; + Double_t kWidth = kStaveWidth / 4; // 1/2 of kWidth + Double_t kStaveHeight = 0.3; + Double_t kHeight = kStaveHeight / 2; + Double_t kAlpha = 90 - 67; // 90-33.69; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = kWidth / TMath::Sin(kTheta); + Double_t kL1 = kWidth / TMath::Tan(kTheta); + Double_t kS2 = TMath::Sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(the2); + Double_t kThe2 = TMath::ATan(kHeight / kS1); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + // Int_t loop = kStaveLength/(kL1); + // Double_t s3 = kWidth/(2*TMath::Sin(kTheta)); + // Double_t s4 = 3*kWidth/(2*TMath::Sin(kTheta)); + + LOG(DEBUG1) << "BuildLevel " << mBuildLevel; + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", GeometryTGeo::getEC0StavePattern(), + mLayerNumber); + + Double_t z = 0, y = -0.011 + 0.0150, x = 0; + + TGeoVolume* mechStavVol = nullptr; + + if (mBuildLevel < 5) { + + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, kStaveHeight, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // detailed structure ++++++++++++++ + // Pipe Kapton grey-35 + auto* coolTube = new TGeoTube(kConeInRadius, kConeOutRadius, kStaveLength / 2); + auto* volCoolTube = new TGeoVolume("pipe", coolTube, medKapton); + volCoolTube->SetFillColor(35); + volCoolTube->SetLineColor(35); + mechStavVol->AddNode(volCoolTube, 0, new TGeoTranslation(x + (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + mechStavVol->AddNode(volCoolTube, 1, new TGeoTranslation(x - (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + } + + if (mBuildLevel < 4) { + auto* coolTubeW = new TGeoTube(0., kConeInRadius, kStaveLength / 2); + auto* volCoolTubeW = new TGeoVolume("pipeWater", coolTubeW, medWater); + volCoolTubeW->SetFillColor(4); + volCoolTubeW->SetLineColor(4); + mechStavVol->AddNode(volCoolTubeW, 0, new TGeoTranslation(x + (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + mechStavVol->AddNode(volCoolTubeW, 1, new TGeoTranslation(x - (kStaveWidth / 2), y - (kHeight - kConeOutRadius), 0)); + } + + // frequency of filament + // n = 4 means very dense(4 filaments per interval) + // n = 2 means dense(2 filaments per interval) + Int_t n = 4; + Int_t loop = (Int_t)(kStaveLength / (4 * kL1 / n) + 2 / n) - 1; + if (mBuildLevel < 3) { + // Top CFRP Filament black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox(kS2, 0.007 / 2, 0.15 / 2); //(kS2,0.002,0.02); + auto* volT2 = new TGeoVolume("TopFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + + for (int i = 1; i < loop; i++) { // i<60;i++){ + mechStavVol->AddNode( + volT2, 4 * i + 0, + new TGeoCombiTrans( + x + kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 1, + new TGeoCombiTrans( + x - kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 2, + new TGeoCombiTrans( + x + kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 3, + new TGeoCombiTrans( + x - kWidth, y + (2 * kConeOutRadius), + z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + } + + // Bottom CFRP Filament black-12 Carbon structure TGeoBBox (thickness,width,length) + auto* t1 = new TGeoBBox(0.007 / 2, 0.15 / 2, kS1); //(0.002,0.02,kS1); + auto* volT1 = new TGeoVolume("CFRPBottom", t1, medM60J3K); + volT1->SetLineColor(12); + volT1->SetFillColor(12); + + for (int i = 1; i < loop; i++) { + mechStavVol->AddNode( + volT1, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - kHeight, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", -90, kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - kHeight, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", 90, kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - kHeight, z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - kHeight, z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volT1", -90, +kAlpha, 0))); + } + } + + if (mBuildLevel < 2) { + // Glue CFRP-Silicon layers TGeoBBox(thickness,width,kS1); + auto* tG = new TGeoBBox(0.0075 / 2, 0.18 / 2, kS1); + auto* volTG = new TGeoVolume("Glue1", tG, medGlue); + volTG->SetLineColor(5); + volTG->SetFillColor(5); + + for (int i = 1; i < loop; i++) { // i<60;i++){ + mechStavVol->AddNode( + volTG, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - 0.16, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(2*kL1*i), + new TGeoRotation("volTG", -90, kAlpha, 0))); + mechStavVol->AddNode( + volTG, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - 0.16, z - kStaveLength / 2 + ((4 / n) * kL1 * i) + kS1 / 2, // z-14.25+(2*kL1*i), + new TGeoRotation("volTG", 90, kAlpha, 0))); + mechStavVol->AddNode( + volTG, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - 0.16, z - kStaveLength / 2 + ((4 / n) * i * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volTG", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volTG, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - 0.16, z - kStaveLength / 2 + (i * (4 / n) * kL1) + kS1 / 2, // z-14.25+(i*2*kL1), + new TGeoRotation("volTG", -90, +kAlpha, 0))); + } + + auto* glue = new TGeoBBox(xsta, 0.005 / 2, zsta); + auto* volGlue = new TGeoVolume("Glue2", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + // mechStavVol->AddNode(volGlue, 0, new TGeoCombiTrans(x, y-0.16, z, new TGeoRotation("",0, 0, + // 0))); + mechStavVol->AddNode(volGlue, 1, new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005, z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + // Flex cable brown-28 TGeoBBox(width,thickness,length); + auto* kapCable = new TGeoBBox(xsta, 0.01 / 2, zsta); + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005 - 0.01, z, + new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structur + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelInnerB1(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("EC0_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("EC0_FLEXCABLE$"); + + // Local parameters + Double_t kConeOutRadius = 0.15 / 2; + // Double_t kConeInRadius = 0.1430/2; + Double_t kStaveLength = zsta * 2; + // Double_t kStaveWidth = xsta*2-kConeOutRadius*2; + Double_t kStaveWidth = xsta * 2; + Double_t kWidth = kStaveWidth / 4; // 1/2 of kWidth + Double_t kStaveHeight = 0.3; + Double_t kHeight = kStaveHeight / 2; + Double_t kAlpha = 90 - 33.; // 90-30; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = kWidth / TMath::Sin(kTheta); + Double_t kL1 = kWidth / TMath::Tan(kTheta); + Double_t kS2 = TMath::Sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(the2); + Double_t kThe2 = TMath::ATan(kHeight / kS1); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + Int_t loop = (Int_t)((kStaveLength / (2 * kL1)) / 2); + + TGeoVolume* mechStavVol = nullptr; + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", GeometryTGeo::getEC0StavePattern(), + mLayerNumber); + + // detailed structure ++++++++++++++ + Double_t z = 0, y = -0.011 + 0.0150, x = 0; + + // Polimide micro channels numbers + Double_t yMC = y - kHeight + 0.01; + Int_t nb = (Int_t)(kStaveWidth / 0.1) + 1; + Double_t xstaMC = (nb * 0.1 - 0.08) / 2; + + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, kStaveHeight, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Polimide micro channels numbers + auto* tM0 = new TGeoBBox(xstaMC, 0.005 / 2, zsta); + auto* volTM0 = new TGeoVolume("MicroChanCover", tM0, medKapton); + volTM0->SetLineColor(35); + volTM0->SetFillColor(35); + mechStavVol->AddNode(volTM0, 0, + new TGeoCombiTrans(x, -0.0125 + yMC, z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volTM0, 1, + new TGeoCombiTrans(x, +0.0125 + yMC, z, new TGeoRotation("", 0, 0, 0))); + + auto* tM0b = new TGeoBBox(0.02 / 2, 0.02 / 2, zsta); + auto* volTM0b = new TGeoVolume("MicroChanWalls", tM0b, medKapton); + volTM0b->SetLineColor(35); + volTM0b->SetFillColor(35); + for (Int_t ib = 0; ib < nb; ib++) { + mechStavVol->AddNode(volTM0b, ib, new TGeoCombiTrans(x + ib * 0.1 - xstaMC + 0.01, yMC, z, new TGeoRotation("", 0, 0, 0))); + } + } + + if (mBuildLevel < 4) { + // Water in Polimide micro channels + auto* water = new TGeoBBox(0.08 / 2, 0.02 / 2, zsta + 0.1); + auto* volWater = new TGeoVolume("Water", water, medWater); + volWater->SetLineColor(4); + volWater->SetFillColor(4); + for (Int_t ib = 0; ib < (nb - 1); ib++) { + mechStavVol->AddNode(volWater, ib, new TGeoCombiTrans(x + ib * 0.1 - xstaMC + 0.06, yMC, z, new TGeoRotation("", 0, 0, 0))); + } + } + + if (mBuildLevel < 3) { + // Bottom filament CFRP black-12 Carbon structure TGeoBBox (thickness,width,length) + Double_t filWidth = 0.04; + Double_t filHeight = 0.02; + auto* t1 = new TGeoBBox(filHeight / 2, filWidth / 2, kS1); + auto* volT1 = new TGeoVolume("CFRPBottom", t1, medM60J3K); + volT1->SetLineColor(12); + volT1->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode(volT1, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, kAlpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * kL1 * i) + kS1 / 2, + new TGeoRotation("volT1", 90, kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volT1, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, +kAlpha, 0))); + } + + // Top filament CFRP black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox(kS2, filHeight / 2, filWidth / 2); + auto* volT2 = new TGeoVolume("CFRPTop", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode( + volT2, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + } + } + + if (mBuildLevel < 2) { + // Glue between filament and polimide micro channel + auto* t3 = new TGeoBBox(0.01 / 2, 0.04, kS1); + auto* volT3 = new TGeoVolume("FilamentGlue", t3, medGlue); + volT3->SetLineColor(5); + volT3->SetFillColor(5); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode(volT3, 4 * i + 0, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + (4 * kL1 * i) + kS1 / 2, + new TGeoRotation("volT1", -90, kAlpha, 0))); + mechStavVol->AddNode(volT3, 4 * i + 1, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + (4 * kL1 * i) + kS1 / 2, + new TGeoRotation("volT1", 90, kAlpha, 0))); + mechStavVol->AddNode( + volT3, 4 * i + 2, + new TGeoCombiTrans(x + kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, -kAlpha, 0))); + mechStavVol->AddNode( + volT3, 4 * i + 3, + new TGeoCombiTrans(x - kWidth, y - kHeight + 0.0325, + z - kStaveLength / 2 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT1", -90, +kAlpha, 0))); + } + + // Glue microchannel and sensor + auto* glueM = new TGeoBBox(xsta, 0.01 / 2, zsta); + auto* volGlueM = new TGeoVolume("MicroChanGlue", glueM, medGlue); + volGlueM->SetLineColor(5); + volGlueM->SetFillColor(5); + mechStavVol->AddNode(volGlueM, 0, + new TGeoCombiTrans(x, y - 0.16, z, new TGeoRotation("", 0, 0, 0))); + + // Glue sensor and kapton + auto* glue = new TGeoBBox(xsta, 0.005 / 2, zsta); + auto* volGlue = new TGeoVolume("SensorGlue", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + mechStavVol->AddNode(volGlue, 1, new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005, z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + auto* kapCable = new TGeoBBox(xsta, 0.01 / 2, zsta); + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - 0.165 - mSensorThickness - 0.005 - 0.01, z, + new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structur + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelInnerB21(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("EC0_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("EC0_FLEXCABLE$"); + TGeoMedium* medK13D2U2k = mgr->GetMedium("EC0_K13D2U2k$"); + TGeoMedium* medFGS003 = mgr->GetMedium("EC0_FGS003$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("EC0_CarbonFleece$"); + + // Local parameters + Double_t kConeOutRadius = 0.151384 / 2; + Double_t kConeInRadius = 0.145034 / 2; + Double_t kStaveLength = zsta; + Double_t kStaveWidth = xsta * 2; + Double_t kWidth = (kStaveWidth + 0.005) / 4; + Double_t kStaveHeigth = 0.33; // 0.33; + Double_t kHeight = (kStaveHeigth + 0.025) / 2; + Double_t kAlpha = 57; // 56.31; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = (kStaveWidth / 4) / TMath::Sin(kTheta); + Double_t kL1 = (kStaveWidth / 4) / TMath::Tan(kTheta); + Double_t kS2 = sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(the2); + Double_t kThe2 = TMath::ATan(kHeight / kS1); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + // Double_t lay1 = 0.003157; + Double_t kLay1 = 0.003; // Amec carbon + // Double_t lay2 = 0.0043215;//C Fleece carbon + Double_t kLay2 = 0.002; // C Fleece carbon + Double_t kLay3 = 0.007; // K13D2U carbon + Int_t loop = (Int_t)(kStaveLength / (2 * kL1)); + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", GeometryTGeo::getEC0StavePattern(), + mLayerNumber); + + Double_t z = 0, y = -(kConeOutRadius + 0.03) + 0.0385, x = 0; + + TGeoVolume* mechStavVol = nullptr; + + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, kStaveHeigth, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Pipe Kapton grey-35 + auto* cone1 = + new TGeoCone(kStaveLength, kConeInRadius, kConeOutRadius, kConeInRadius, kConeOutRadius); + auto* volCone1 = new TGeoVolume("PolyimidePipe", cone1, medKapton); + volCone1->SetFillColor(35); + volCone1->SetLineColor(35); + mechStavVol->AddNode(volCone1, 1, new TGeoTranslation(x + 0.25, y, z)); + mechStavVol->AddNode(volCone1, 2, new TGeoTranslation(x - 0.25, y, z)); + } + + if (mBuildLevel < 4) { + auto* coolTubeW = new TGeoTube(0., kConeInRadius, kStaveLength); + auto* volCoolTubeW = new TGeoVolume("Water", coolTubeW, medWater); + volCoolTubeW->SetFillColor(4); + volCoolTubeW->SetLineColor(4); + mechStavVol->AddNode(volCoolTubeW, 0, new TGeoTranslation(x - 0.25, y, z)); + mechStavVol->AddNode(volCoolTubeW, 1, new TGeoTranslation(x + 0.25, y, z)); + } + + if (mBuildLevel < 3) { + // top fillament + // Top filament M60J black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = + new TGeoBBox(kS2, 0.02 / 2, 0.04 / 2); // TGeoBBox *t2=new TGeoBBox(kS2,0.01,0.02); + auto* volT2 = new TGeoVolume("TopFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + + for (int i = 0; i < loop; i++) { // i<28;i++){ + mechStavVol->AddNode( + volT2, i * 4 + 1, + new TGeoCombiTrans(x + kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, i * 4 + 2, + new TGeoCombiTrans(x - kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + mechStavVol->AddNode( + volT2, i * 4 + 3, + new TGeoCombiTrans(x + kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + mechStavVol->AddNode( + volT2, i * 4 + 4, + new TGeoCombiTrans(x - kWidth, y + kHeight + (0.12 / 2) - 0.014 + 0.007, + z - kStaveLength + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + // mechStavVol->AddNode(volT2,i*4+1,new + // TGeoCombiTrans(x+kWidth+0.0036,y+kHeight-(0.12/2)+0.072,z+kStaveLength+(i*4*kL1)+kS1/2, new + // TGeoRotation("volT2",90,90-kAlpha,90-kBeta))); + } + + // wall side structure out + auto* box4 = new TGeoBBox(0.03 / 2, 0.12 / 2, kStaveLength - 0.50); + auto* plate4 = new TGeoVolume("WallOut", box4, medM60J3K); + plate4->SetFillColor(35); + plate4->SetLineColor(35); + mechStavVol->AddNode(plate4, 1, + new TGeoCombiTrans(x + (2 * kStaveWidth / 4) - (0.03 / 2), + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate4", 0, 0, 0))); + mechStavVol->AddNode(plate4, 2, + new TGeoCombiTrans(x - (2 * kStaveWidth / 4) + (0.03 / 2), + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate4", 0, 0, 0))); + // wall side in + auto* box5 = new TGeoBBox(0.015 / 2, 0.12 / 2, kStaveLength - 0.50); + auto* plate5 = new TGeoVolume("WallIn", box5, medM60J3K); + plate5->SetFillColor(12); + plate5->SetLineColor(12); + mechStavVol->AddNode(plate5, 1, + new TGeoCombiTrans(x + (2 * kStaveWidth / 4) - 0.03 - 0.015 / 2, + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate5", 0, 0, 0))); + mechStavVol->AddNode(plate5, 2, + new TGeoCombiTrans(x - (2 * kStaveWidth / 4) + 0.03 + 0.015 / 2, + y - 0.0022 - kConeOutRadius + 0.12 / 2 + 0.007, z, + new TGeoRotation("plate5", 0, 0, 0))); + + // Amec Thermasol red-2 cover tube FGS300 + auto* cons1 = + new TGeoConeSeg(kStaveLength - 0.50, kConeOutRadius, kConeOutRadius + kLay1, kConeOutRadius, + kConeOutRadius + kLay1, 0, 180); + auto* cone11 = new TGeoVolume("ThermasolPipeCover", cons1, medFGS003); + cone11->SetFillColor(2); + cone11->SetLineColor(2); + mechStavVol->AddNode(cone11, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("Cone11", 0, 0, 0))); + mechStavVol->AddNode(cone11, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("Cone11", 0, 0, 0))); + + auto* box2 = + new TGeoBBox((0.50 - (2 * kConeOutRadius)) / 2, kLay1 / 2, kStaveLength - 0.50); + auto* plate2 = new TGeoVolume("ThermasolMiddle", box2, medFGS003); + plate2->SetFillColor(2); + plate2->SetLineColor(2); + mechStavVol->AddNode(plate2, 1, new TGeoCombiTrans(x, y - kConeOutRadius + (kLay1 / 2), z, new TGeoRotation("plate2", 0, 0, 0))); + + auto* box21 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - kLay1) / 2, kLay1 / 2, kStaveLength - 0.50); + auto* plate21 = new TGeoVolume("ThermasolLeftRight", box21, medFGS003); + plate21->SetFillColor(2); + plate21->SetLineColor(2); + mechStavVol->AddNode( + plate21, 1, new TGeoCombiTrans(x + 0.25 + kConeOutRadius + (0.75 - 0.25 - kConeOutRadius) / 2 - (kLay1 / 2), y - kConeOutRadius + (kLay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + mechStavVol->AddNode( + plate21, 2, new TGeoCombiTrans(x - 0.25 - kConeOutRadius - (0.75 - 0.25 - kConeOutRadius) / 2 + (kLay1 / 2), y - kConeOutRadius + (kLay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + + auto* box22 = new TGeoBBox((kLay1 / 2), kConeOutRadius / 2, kStaveLength - 0.50); + auto* plate22 = new TGeoVolume("ThermasolVertical", box22, medFGS003); + plate22->SetFillColor(2); + plate22->SetLineColor(2); + mechStavVol->AddNode(plate22, 1, new TGeoCombiTrans(x + 0.25 + kConeOutRadius + (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 2, new TGeoCombiTrans(x + 0.25 - kConeOutRadius - (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 3, new TGeoCombiTrans(x - 0.25 + kConeOutRadius + (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 4, new TGeoCombiTrans(x - 0.25 - kConeOutRadius - (kLay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + + // C Fleece + auto* cons2 = + new TGeoConeSeg(kStaveLength - 0.50, kConeOutRadius + kLay1, kConeOutRadius + kLay1 + kLay2, + kConeOutRadius + kLay1, kConeOutRadius + kLay1 + kLay2, 0, 180); + auto* cone12 = new TGeoVolume("CFleecePipeCover", cons2, medCarbonFleece); + cone12->SetFillColor(28); + cone12->SetLineColor(28); + mechStavVol->AddNode(cone12, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("Cone12", 0, 0, 0))); + mechStavVol->AddNode(cone12, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("Cone12", 0, 0, 0))); + + auto* box3 = + new TGeoBBox((0.50 - (2 * (kConeOutRadius + kLay1))) / 2, kLay2 / 2, kStaveLength - 0.50); + auto* plate3 = new TGeoVolume("CFleeceMiddle", box3, medCarbonFleece); + plate3->SetFillColor(28); + plate3->SetLineColor(28); + mechStavVol->AddNode(plate3, 1, new TGeoCombiTrans(x, y - kConeOutRadius + kLay1 + (kLay2 / 2), z, new TGeoRotation("plate3", 0, 0, 0))); + + auto* box31 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - kLay1) / 2, kLay2 / 2, kStaveLength - 0.50); + auto* plate31 = new TGeoVolume("CFleeceLeftRight", box31, medCarbonFleece); + plate31->SetFillColor(28); + plate31->SetLineColor(28); + mechStavVol->AddNode( + plate31, 1, + new TGeoCombiTrans( + x + 0.25 + kConeOutRadius + kLay1 + (0.75 - 0.25 - kConeOutRadius - kLay1) / 2, + y - kConeOutRadius + kLay1 + (kLay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + mechStavVol->AddNode( + plate31, 2, + new TGeoCombiTrans( + x - 0.25 - kConeOutRadius - kLay1 - (0.75 - 0.25 - kConeOutRadius - kLay1) / 2, + y - kConeOutRadius + kLay1 + (kLay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + + auto* box32 = new TGeoBBox((kLay2 / 2), (kConeOutRadius - kLay1) / 2, kStaveLength - 0.50); + auto* plate32 = new TGeoVolume("CFleeceVertical", box32, medCarbonFleece); + plate32->SetFillColor(28); + plate32->SetLineColor(28); + mechStavVol->AddNode(plate32, 1, + new TGeoCombiTrans(x + 0.25 + kConeOutRadius + kLay1 + (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 2, + new TGeoCombiTrans(x + 0.25 - kConeOutRadius - kLay1 - (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 3, + new TGeoCombiTrans(x - 0.25 + kConeOutRadius + kLay1 + (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 4, + new TGeoCombiTrans(x - 0.25 - kConeOutRadius - kLay1 - (kLay2 / 2), + y + (kLay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + + // K13D2U carbon plate + auto* box1 = new TGeoBBox(2 * kWidth, kLay3 / 2, kStaveLength - 0.50); + auto* plate1 = new TGeoVolume("CarbonPlate", box1, medK13D2U2k); + plate1->SetFillColor(5); + plate1->SetLineColor(5); + mechStavVol->AddNode(plate1, 1, new TGeoCombiTrans(x, y - (kConeOutRadius + (kLay3 / 2)), z, new TGeoRotation("plate1", 0, 0, 0))); + + // C Fleece bottom plate + auto* box6 = new TGeoBBox(2 * kWidth, kLay2 / 2, kStaveLength - 0.50); + auto* plate6 = new TGeoVolume("CFleeceBottom", box6, medCarbonFleece); + plate6->SetFillColor(2); + plate6->SetLineColor(2); + mechStavVol->AddNode(plate6, 1, + new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2)), z, + new TGeoRotation("plate1", 0, 0, 0))); + } + + if (mBuildLevel < 2) { + // Glue layers and kapton + auto* glue = new TGeoBBox(kStaveWidth / 2, 0.005 / 2, zsta); + auto* volGlue = new TGeoVolume("Glue", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + mechStavVol->AddNode( + volGlue, 0, new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2) + (0.01 / 2)), z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volGlue, 1, + new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2) + 0.01 + mSensorThickness + (0.01 / 2)), + z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + auto* kapCable = new TGeoBBox(kStaveWidth / 2, 0.01 / 2, zsta); + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - (kConeOutRadius + kLay3 + (kLay2 / 2) + 0.01 + mSensorThickness + 0.01 + (0.01 / 2)), + z, new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structure + return mechStavVol; +} + +// new model22 +TGeoVolume* V1Layer::createStaveModelInnerB22(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("EC0_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("EC0_FLEXCABLE$"); + TGeoMedium* medK13D2U2k = mgr->GetMedium("EC0_K13D2U2k$"); + TGeoMedium* medFGS003 = mgr->GetMedium("EC0_FGS003$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("EC0_CarbonFleece$"); + + // Local parameters + Double_t kConeOutRadius = (0.1024 + 0.0025) / 2; // 0.107/2; + Double_t kConeInRadius = 0.1024 / 2; // 0.10105/2 + Double_t kStaveLength = zsta; + Double_t kStaveWidth = xsta * 2; + Double_t kWidth = (kStaveWidth) / 4; + Double_t kStaveHeight = 0.283; // 0.33; + Double_t kHeight = (kStaveHeight) / 2; + Double_t kAlpha = 57; // 56.31; + Double_t kTheta = kAlpha * TMath::DegToRad(); + Double_t kS1 = ((kStaveWidth) / 4) / TMath::Sin(kTheta); + Double_t kL1 = (kStaveWidth / 4) / TMath::Tan(kTheta); + Double_t kS2 = sqrt(kHeight * kHeight + kS1 * kS1); // TMath::Sin(kThe2); + Double_t kThe2 = TMath::ATan(kHeight / (0.375 - 0.036)); + Double_t kBeta = kThe2 * TMath::RadToDeg(); + Double_t klay1 = 0.003; // Amec carbon + Double_t klay2 = 0.002; // C Fleece carbon + Double_t klay3 = 0.007; // CFplate K13D2U carbon + Double_t klay4 = 0.007; // GluekStaveLength/2 + Double_t klay5 = 0.01; // Flex cable + Double_t kTopVertexMaxWidth = 0.072; + Double_t kTopVertexHeight = 0.04; + Double_t kSideVertexMWidth = 0.052; + Double_t kSideVertexHeight = 0.11; + + Int_t loop = (Int_t)(kStaveLength / (2 * kL1)); + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", GeometryTGeo::getEC0StavePattern(), + mLayerNumber); + + Double_t z = 0, y = -(2 * kConeOutRadius) + klay1 + klay2 + mSensorThickness / 2 - 0.0004, x = 0; + + TGeoVolume* mechStavVol = nullptr; + + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[6] = { + kStaveWidth / 2, kStaveWidth / 2, 0.012, + -0.012, -kStaveWidth / 2, -kStaveWidth / 2}; + // Double_t yv[6] = {-2*(kConeOutRadius+klay1+1.5*klay2+klay3+klay4+mSensorThickness+klay5), + // 0-0.02,kStaveHeight+0.01,kStaveHeight+0.01,0-0.02, + // -2*(kConeOutRadius+klay1+1.5*klay2+klay3+klay4+mSensorThickness+klay5)}; + // (kConeOutRadius*2)-0.0635 + Double_t yv[6] = { + -(kConeOutRadius * 2) - 0.06395, 0 - 0.02, kStaveHeight + 0.01, + kStaveHeight + 0.01, 0 - 0.02, -(kConeOutRadius * 2) - 0.06395}; // (kConeOutRadius*2)-0.064 + mechStruct->DefinePolygon(6, xv, yv); + mechStruct->DefineSection(0, -kStaveLength, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength, 0, 0, 1.); + + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Polyimide Pipe Kapton grey-35 + auto* cone1 = new TGeoCone(kStaveLength, kConeInRadius, kConeOutRadius - 0.0001, + kConeInRadius, kConeOutRadius - 0.0001); + auto* volCone1 = new TGeoVolume("PolyimidePipe", cone1, medKapton); + volCone1->SetFillColor(35); + volCone1->SetLineColor(35); + mechStavVol->AddNode(volCone1, 1, new TGeoTranslation(x + 0.25, y, z)); + mechStavVol->AddNode(volCone1, 2, new TGeoTranslation(x - 0.25, y, z)); + } + + if (mBuildLevel < 4) { + auto* coolTubeW = new TGeoTube(0., kConeInRadius - 0.0001, kStaveLength); + auto* volCoolTubeW = new TGeoVolume("Water", coolTubeW, medWater); + volCoolTubeW->SetFillColor(4); + volCoolTubeW->SetLineColor(4); + mechStavVol->AddNode(volCoolTubeW, 0, new TGeoTranslation(x - 0.25, y, z)); + mechStavVol->AddNode(volCoolTubeW, 1, new TGeoTranslation(x + 0.25, y, z)); + } + + if (mBuildLevel < 3) { + // top fillament + // Top filament M60J black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox( + kS2 - 0.028, 0.02 / 2, + 0.02 / 2); // 0.04/2//TGeoBBox *t2=new TGeoBBox(kS2,0.01,0.02);//kS2-0.03 old Config.C + auto* volT2 = new TGeoVolume("TopFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<28;i++){ + // 1) Front Left Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 1, + new TGeoCombiTrans(x + kWidth + 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, 90 - kBeta))); + // 2) Front Right Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 2, + new TGeoCombiTrans(x - kWidth - 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, -90 + kBeta))); + // 3) Back Left Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 3, + new TGeoCombiTrans(x + kWidth + 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, -90 + kAlpha, 90 - kBeta))); + // 4) Back Right Top Filament + mechStavVol->AddNode( + volT2, i * 4 + 4, + new TGeoCombiTrans(x - kWidth - 0.0036, y + kHeight + 0.01, + z - kStaveLength + 0.1 + 2 * kL1 + (i * 4 * kL1) + kS1 / 2, + new TGeoRotation("volT2", 90, 90 - kAlpha, -90 + kBeta))); + } + + // Vertex structure + // top ver trd1 + auto* trd1 = new TGeoTrd1(0, kTopVertexMaxWidth / 2, kStaveLength, kTopVertexHeight / 2); + auto* ibdv = new TGeoVolume("TopVertex", trd1, medM60J3K); + ibdv->SetFillColor(12); + ibdv->SetLineColor(12); + mechStavVol->AddNode( + ibdv, 1, new TGeoCombiTrans(x, y + kStaveHeight + 0.03, z, new TGeoRotation("ibdv", 0., -90, 0))); // y+kStaveHeight+0.056 + + // left trd2 + auto* trd2 = new TGeoTrd1(0, kSideVertexMWidth / 2, kStaveLength, kSideVertexHeight / 2); + auto* ibdv2 = new TGeoVolume("LeftVertex", trd2, medM60J3K); + ibdv2->SetFillColor(12); + ibdv2->SetLineColor(12); + mechStavVol->AddNode( + ibdv2, 1, + new TGeoCombiTrans( + x + kStaveWidth / 2 - 0.06, y - 0.0355, z, + new TGeoRotation("ibdv2", -103.3, 90, 0))); // x-kStaveWidth/2-0.09 old Config.C y-0.0355, + + // right trd3 + auto* trd3 = new TGeoTrd1(0, kSideVertexMWidth / 2, kStaveLength, kSideVertexHeight / 2); + auto* ibdv3 = new TGeoVolume("RightVertex", trd3, medM60J3K); + ibdv3->SetFillColor(12); + ibdv3->SetLineColor(12); + mechStavVol->AddNode( + ibdv3, 1, new TGeoCombiTrans(x - kStaveWidth / 2 + 0.06, y - 0.0355, z, new TGeoRotation("ibdv3", 103.3, 90, 0))); // x-kStaveWidth/2+0.09 old Config.C + + // Carbon Fleece + auto* cons2 = + new TGeoConeSeg(zsta, kConeOutRadius + klay1, kConeOutRadius + klay1 + klay2, + kConeOutRadius + klay1, kConeOutRadius + klay1 + klay2, 0, 180); + auto* cone12 = new TGeoVolume("CarbonFleecePipeCover", cons2, medCarbonFleece); + cone12->SetFillColor(28); + cone12->SetLineColor(28); + mechStavVol->AddNode(cone12, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("cone12", 0, 0, 0))); + mechStavVol->AddNode(cone12, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("cone12", 0, 0, 0))); + + auto* box3 = new TGeoBBox((0.50 - (2 * (kConeOutRadius + klay1))) / 2, klay2 / 2, + zsta); // kStaveLength-0.50); + auto* plate3 = new TGeoVolume("CarbonFleeceMiddle", box3, medCarbonFleece); + plate3->SetFillColor(28); + plate3->SetLineColor(28); + mechStavVol->AddNode(plate3, 1, new TGeoCombiTrans(x, y - kConeOutRadius + klay1 + (klay2 / 2), z, new TGeoRotation("plate3", 0, 0, 0))); + + auto* box31 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - klay1) / 2 + 0.0025, klay2 / 2, zsta); + auto* plate31 = new TGeoVolume("CarbonFleeceLeftRight", box31, medCarbonFleece); + plate31->SetFillColor(28); + plate31->SetLineColor(28); + mechStavVol->AddNode( + plate31, 1, + new TGeoCombiTrans( + x + 0.25 + kConeOutRadius + klay1 + (0.75 - 0.25 - kConeOutRadius - klay1) / 2, + y - kConeOutRadius + klay1 + (klay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + mechStavVol->AddNode( + plate31, 2, + new TGeoCombiTrans( + x - 0.25 - kConeOutRadius - klay1 - (0.75 - 0.25 - kConeOutRadius - klay1) / 2, + y - kConeOutRadius + klay1 + (klay2 / 2), z, new TGeoRotation("plate31", 0, 0, 0))); + + auto* box32 = new TGeoBBox((klay2 / 2), (kConeOutRadius - klay1) / 2, zsta); + auto* plate32 = new TGeoVolume("CarbonFleeceVertical", box32, medCarbonFleece); + plate32->SetFillColor(28); + plate32->SetLineColor(28); + mechStavVol->AddNode(plate32, 1, + new TGeoCombiTrans(x + 0.25 + kConeOutRadius + klay1 + (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 2, + new TGeoCombiTrans(x + 0.25 - kConeOutRadius - klay1 - (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 3, + new TGeoCombiTrans(x - 0.25 + kConeOutRadius + klay1 + (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + mechStavVol->AddNode(plate32, 4, + new TGeoCombiTrans(x - 0.25 - kConeOutRadius - klay1 - (klay2 / 2), + y + (klay1 - kConeOutRadius) / 2, z, + new TGeoRotation("plate32", 0, 0, 0))); + + // Amec Thermasol red-2 cover tube FGS300 or Carbon Paper + auto* cons1 = + new TGeoConeSeg(zsta, kConeOutRadius, kConeOutRadius + klay1 - 0.0001, kConeOutRadius, + kConeOutRadius + klay1 - 0.0001, 0, 180); // kConeOutRadius+klay1-0.0001 + auto* cone11 = new TGeoVolume("ThermasolPipeCover", cons1, medFGS003); + cone11->SetFillColor(2); + cone11->SetLineColor(2); + mechStavVol->AddNode(cone11, 1, + new TGeoCombiTrans(x + 0.25, y, z, new TGeoRotation("cone11", 0, 0, 0))); + mechStavVol->AddNode(cone11, 2, + new TGeoCombiTrans(x - 0.25, y, z, new TGeoRotation("cone11", 0, 0, 0))); + + auto* box2 = + new TGeoBBox((0.50 - (2 * kConeOutRadius)) / 2, (klay1 / 2), zsta); // kStaveLength-0.50); + auto* plate2 = new TGeoVolume("ThermasolMiddle", box2, medFGS003); + plate2->SetFillColor(2); + plate2->SetLineColor(2); + mechStavVol->AddNode(plate2, 1, new TGeoCombiTrans(x, y - kConeOutRadius + (klay1 / 2), z, new TGeoRotation("plate2", 0, 0, 0))); + + auto* box21 = + new TGeoBBox((0.75 - 0.25 - kConeOutRadius - klay1) / 2 + 0.0025, (klay1 / 2), zsta); + auto* plate21 = new TGeoVolume("ThermasolLeftRight", box21, medFGS003); + plate21->SetFillColor(2); + plate21->SetLineColor(2); + mechStavVol->AddNode( + plate21, 1, + new TGeoCombiTrans( + x + 0.25 + kConeOutRadius + (0.75 - 0.25 - kConeOutRadius) / 2 - (klay1 / 2) + 0.0025, + y - kConeOutRadius + (klay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + mechStavVol->AddNode( + plate21, 2, + new TGeoCombiTrans( + x - 0.25 - kConeOutRadius - (0.75 - 0.25 - kConeOutRadius) / 2 + (klay1 / 2) - 0.0025, + y - kConeOutRadius + (klay1 / 2), z, new TGeoRotation("plate21", 0, 0, 0))); + + auto* box22 = new TGeoBBox((klay1 / 2), kConeOutRadius / 2, zsta); + auto* plate22 = new TGeoVolume("ThermasolVertical", box22, medFGS003); + plate22->SetFillColor(2); + plate22->SetLineColor(2); + mechStavVol->AddNode(plate22, 1, new TGeoCombiTrans(x + 0.25 + kConeOutRadius + (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 2, new TGeoCombiTrans(x + 0.25 - kConeOutRadius - (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 3, new TGeoCombiTrans(x - 0.25 + kConeOutRadius + (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + mechStavVol->AddNode(plate22, 4, new TGeoCombiTrans(x - 0.25 - kConeOutRadius - (klay1 / 2), y - kConeOutRadius / 2, z, new TGeoRotation("plate22", 0, 0, 0))); + + // K13D2U CF plate + auto* box1 = new TGeoBBox(2 * kWidth, (klay3) / 2, zsta); + auto* plate1 = new TGeoVolume("CFPlate", box1, medK13D2U2k); + plate1->SetFillColor(5); + plate1->SetLineColor(5); + mechStavVol->AddNode(plate1, 1, new TGeoCombiTrans(x, y - (kConeOutRadius + (klay3 / 2)), z, new TGeoRotation("plate1", 0, 0, 0))); + + // C Fleece bottom plate + auto* box6 = new TGeoBBox(2 * kWidth, (klay2) / 2, zsta); + auto* plate6 = new TGeoVolume("CarbonFleeceBottom", box6, medCarbonFleece); + plate6->SetFillColor(2); + plate6->SetLineColor(2); + mechStavVol->AddNode(plate6, 1, + new TGeoCombiTrans(x, y - (kConeOutRadius + klay3 + (klay2 / 2)), z, + new TGeoRotation("plate6", 0, 0, 0))); + } + if (mBuildLevel < 2) { + // Glue klayers and kapton + auto* glue = new TGeoBBox(kStaveWidth / 2, (klay4) / 2, zsta); + auto* volGlue = new TGeoVolume("Glue", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + // mechStavVol->AddNode(volGlue, 0, new + // TGeoCombiTrans(x,y-(kConeOutRadius+klay3+klay2+(klay4/2)), z, new TGeoRotation("",0, 0, 0))); + mechStavVol->AddNode( + volGlue, 0, + new TGeoCombiTrans(x, y - (kConeOutRadius + klay3 + klay2 + (klay4) / 2) + 0.00005, z, + new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + // Flex Cable or Bus + auto* kapCable = new TGeoBBox(kStaveWidth / 2, klay5 / 2, zsta); // klay5/2 + auto* volCable = new TGeoVolume("FlexCable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + // mechStavVol->AddNode(volCable, 0, new TGeoCombiTrans(x, + // y-(kConeOutRadius+klay3+klay2+klay4+mSensorThickness+(klay5)/2)+0.0002, z, new + // TGeoRotation("",0, + // 0, 0))); + mechStavVol->AddNode( + volCable, 0, + new TGeoCombiTrans( + x, y - (kConeOutRadius + klay3 + klay2 + klay4 + mSensorThickness + (klay5) / 2) + 0.01185, + z, new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structe + return mechStavVol; +} + +// model3 +TGeoVolume* V1Layer::createStaveModelInnerB3(const Double_t xsta, const Double_t zsta, + const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + + TGeoMedium* medM60J3K = mgr->GetMedium("EC0_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medFlexCable = mgr->GetMedium("EC0_FLEXCABLE$"); + // TGeoMedium *medK13D2U2k = mgr->GetMedium("EC0_K13D2U2k$"); + // TGeoMedium *medFGS003 = mgr->GetMedium("EC0_FGS003$"); + // TGeoMedium *medCarbonFleece = mgr->GetMedium("EC0_CarbonFleece$"); + + // Local parameters + Double_t kConeOutRadius = 0.15 / 2; + Double_t kStaveLength = zsta * 2; + Double_t kStaveWidth = xsta * 2; + Double_t w = kStaveWidth / 4; // 1/2 of W + Double_t staveHeight = 0.3; + Double_t h = staveHeight / 2; + Double_t alpha = 90 - 33.; // 90-30; + Double_t the1 = alpha * TMath::DegToRad(); + Double_t s1 = w / TMath::Sin(the1); + Double_t l = w / TMath::Tan(the1); + Double_t s2 = TMath::Sqrt(h * h + s1 * s1); // TMath::Sin(the2); + Double_t the2 = TMath::ATan(h / s1); + Double_t beta = the2 * TMath::RadToDeg(); + Double_t klay4 = 0.007; // Glue + Double_t klay5 = 0.01; // Flexcable + Int_t loop = (Int_t)((kStaveLength / (2 * l)) / 2); + Double_t hh = 0.01; + Double_t ang1 = 0 * TMath::DegToRad(); + Double_t ang2 = 0 * TMath::DegToRad(); + Double_t ang3 = 0 * TMath::DegToRad(); + Int_t chips = 4; + Double_t headWidth = 0.25; + Double_t smcLength = kStaveLength / chips - 2 * headWidth; // 6.25; + Double_t smcWidth = kStaveWidth; + Double_t smcSide1Thick = 0.03; + Double_t vaporThick = 0.032; + Double_t liquidThick = 0.028; + Double_t smcSide2Thick = 0.01; + Double_t smcSide3Thick = 0.0055; + Double_t smcSide4Thick = 0.0095; + Double_t smcSide5Thick = 0.0075; + Double_t smcSpace = 0.01; + + char volumeName[30]; + snprintf(volumeName, 30, "%s%d_StaveStruct", GeometryTGeo::getEC0StavePattern(), + mLayerNumber); + + // detailed structure ++++++++++++++ + Double_t z = 0, y = 0 - 0.007, x = 0; + + // Polimide micro channels numbers + Double_t yMC = y - h + 0.01; + Int_t nb = (Int_t)(kStaveWidth / 0.1) + 1; + Double_t xstaMC = (nb * 0.1 - 0.08) / 2; + + TGeoVolume* mechStavVol = nullptr; + if (mBuildLevel < 5) { + // world (trapezoid) + auto* mechStruct = new TGeoXtru(2); // z sections + Double_t xv[5] = { + kStaveWidth / 2 + 0.1, kStaveWidth / 2 + 0.1, 0, -kStaveWidth / 2 - 0.1, + -kStaveWidth / 2 - 0.1}; + Double_t yv[5] = {-kConeOutRadius * 2 - 0.07, 0, staveHeight, 0, -kConeOutRadius * 2 - 0.07}; + mechStruct->DefinePolygon(5, xv, yv); + mechStruct->DefineSection(0, -kStaveLength - 0.1, 0, 0, 1.); + mechStruct->DefineSection(1, kStaveLength + 0.1, 0, 0, 1.); + mechStavVol = new TGeoVolume(volumeName, mechStruct, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kTRUE); + + // Silicon micro channels numbers + + auto* tM0a = new TGeoBBox(smcWidth / 2, 0.003 / 2, headWidth / 2); + auto* volTM0a = new TGeoVolume("microChanTop1", tM0a, medKapton); + volTM0a->SetLineColor(35); + volTM0a->SetFillColor(35); + + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0a, 0, + new TGeoCombiTrans(x, yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + (headWidth / 2), + new TGeoRotation("", ang1, ang2, ang3))); + mechStavVol->AddNode( + volTM0a, 1, + new TGeoCombiTrans(x, yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - (headWidth / 2), + new TGeoRotation("", ang1, ang2, ang3))); + } + auto* tM0c = new TGeoBBox(0.3 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c = new TGeoVolume("microChanTop2", tM0c, medKapton); + volTM0c->SetLineColor(35); + volTM0c->SetFillColor(35); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c, 0, new TGeoCombiTrans(x + (smcWidth / 2) - (0.3 / 2), yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); + mechStavVol->AddNode( + volTM0c, 1, new TGeoCombiTrans(x - (smcWidth / 2) + (0.3 / 2), yMC + 0.03, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0c1 = new TGeoBBox(0.2225 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c1 = new TGeoVolume("microChanBot1", tM0c1, medKapton); + volTM0c1->SetLineColor(6); + volTM0c1->SetFillColor(6); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c1, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick) - (0.2225 / 2), yMC + 0.03 - hh - (0.003), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0c1, 1, new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick) + (smcSide2Thick) + (smcSide4Thick) + (0.2225 / 2), yMC + 0.03 - hh - (0.003), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0c2 = new TGeoBBox(0.072 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c2 = new TGeoVolume("microChanBot2", tM0c2, medKapton); + volTM0c2->SetLineColor(35); + volTM0c2->SetFillColor(35); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c2, 0, new TGeoCombiTrans(x + smcWidth / 2 - (0.072 / 2), yMC + 0.03 - (0.035 + 0.0015) - (0.003) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0c2r = new TGeoBBox(0.068 / 2, 0.003 / 2, smcLength / 2); + auto* volTM0c2r = new TGeoVolume("microChanBot3", tM0c2r, medKapton); + volTM0c2r->SetLineColor(35); + volTM0c2r->SetFillColor(35); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0c2r, 0, new TGeoCombiTrans(x - smcWidth / 2 + (0.068 / 2), yMC + 0.03 - (0.035 + 0.0015) - (0.003) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0d = new TGeoBBox(smcSide1Thick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0d = new TGeoVolume("microChanSide1", tM0d, medKapton); + volTM0d->SetLineColor(12); + volTM0d->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0d, 1, new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + + auto* tM0d1 = new TGeoBBox(smcSide2Thick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0d1 = new TGeoVolume("microChanSide2", tM0d1, medKapton); + volTM0d1->SetLineColor(12); + volTM0d1->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d1, 0, + new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick / 2), + yMC + 0.03 - (0.003 + 0.035) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0d1, 1, + new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick) + (smcSide2Thick / 2), + yMC + 0.03 - (0.003 + 0.035) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0d2 = new TGeoBBox(smcSide3Thick / 2, (hh + 0.003) / 2, smcLength / 2); + auto* volTM0d2 = new TGeoVolume("microChanSide3", tM0d2, medKapton); + volTM0d2->SetLineColor(12); + volTM0d2->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d2, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick / 2), yMC + 0.03 - (0.003 + hh + 0.003) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0d2r = new TGeoBBox(smcSide4Thick / 2, (hh + 0.003) / 2, smcLength / 2); + auto* volTM0d2r = new TGeoVolume("microChanSide4", tM0d2r, medKapton); + volTM0d2r->SetLineColor(12); + volTM0d2r->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0d2r, 0, + new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick) + (smcSide2Thick) + + (smcSide4Thick / 2), + yMC + 0.03 - (0.003 + hh + 0.003) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0e = new TGeoBBox(smcSide5Thick / 2, hh / 2, smcLength / 2); + auto* volTM0e = new TGeoVolume("microChanSide5", tM0e, medKapton); + volTM0e->SetLineColor(12); + volTM0e->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + for (Int_t ie = 0; ie < 11; ie++) { + mechStavVol->AddNode( + volTM0e, 0, + new TGeoCombiTrans(x - (ie * (smcSpace + smcSide5Thick)) + smcWidth / 2 - + (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick)-smcSpace - (smcSide5Thick / 2), + yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0e, 1, + new TGeoCombiTrans(x + (ie * (smcSpace + smcSide5Thick)) - smcWidth / 2 + + (smcSide1Thick) + (liquidThick) + (smcSide2Thick) + (smcSide4Thick) + + smcSpace + (smcSide5Thick / 2), + yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + } + + auto* tM0f = new TGeoBBox(0.02 / 2, hh / 2, smcLength / 2); + auto* volTM0f = new TGeoVolume("microChanTop3", tM0f, medKapton); + // Double_t smcChannels=12; + Double_t smcCloseWallvapor = smcWidth / 2 - smcSide1Thick - vaporThick - smcSide2Thick - + smcSide3Thick - 12 * smcSpace - 11 * smcSide5Thick; + Double_t smcCloseWallliquid = smcWidth / 2 - smcSide1Thick - liquidThick - smcSide2Thick - + smcSide4Thick - 12 * smcSpace - 11 * smcSide5Thick; + volTM0f->SetLineColor(12); + volTM0f->SetFillColor(12); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0f, 0, + new TGeoCombiTrans(x + smcCloseWallvapor - (0.02) / 2, yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0f, 1, + new TGeoCombiTrans(x - smcCloseWallliquid + (0.02) / 2, yMC + 0.03 - (0.003 + hh) / 2, + z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, + new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + // Head(back) microchannel + + auto* tM0hb = new TGeoBBox(smcWidth / 2, 0.025 / 2, headWidth / 2); + auto* volTM0hb = new TGeoVolume("microChanHeadBackBottom1", tM0hb, medKapton); + volTM0hb->SetLineColor(4); + volTM0hb->SetFillColor(4); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0hb, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0145 - (0.025 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + (headWidth / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0hb, 1, new TGeoCombiTrans(x, yMC + 0.03 - 0.0145 - (0.025) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - (headWidth / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0h1 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.05 / 2); + auto* volTM0h1 = new TGeoVolume("microChanHeadBackBottom2", tM0h1, medKapton); + volTM0h1->SetLineColor(5); + volTM0h1->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0h1, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - headWidth + (0.05 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0h2 = new TGeoBBox(smcWidth / 2, 0.003 / 2, 0.18 / 2); + auto* volTM0h2 = new TGeoVolume("microChanHeadBackBottom7", tM0h2, medKapton); + volTM0h2->SetLineColor(6); + volTM0h2->SetFillColor(6); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0h2, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - 0.01 - (0.003 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - 0.02 - (0.18 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0h3 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.02 / 2); + auto* volTM0h3 = new TGeoVolume("microChanHeadBackBottom3", tM0h3, medKapton); + volTM0h3->SetLineColor(5); + volTM0h3->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0h3, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth - smcLength / 2 - (0.02 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0b1 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.03 / 2); + auto* volTM0b1 = new TGeoVolume("microChanHeadBackBottom4", tM0b1, medKapton); + volTM0b1->SetLineColor(5); + volTM0b1->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0b1, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + headWidth - (0.03 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0b2 = new TGeoBBox(smcWidth / 2, 0.003 / 2, 0.2 / 2); + auto* volTM0b2 = new TGeoVolume("microChanHeadBackBottom5", tM0b2, medKapton); + volTM0b2->SetLineColor(6); + volTM0b2->SetFillColor(6); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0b2, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - 0.01 - (0.003 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + 0.02 + (0.2 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0b3 = new TGeoBBox(smcWidth / 2, 0.013 / 2, 0.02 / 2); + auto* volTM0b3 = new TGeoVolume("microChanHeadBackBottom6", tM0b3, medKapton); + volTM0b3->SetLineColor(5); + volTM0b3->SetFillColor(5); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0b3, 0, new TGeoCombiTrans(x, yMC + 0.03 - 0.0015 - (0.013 / 2), z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth + smcLength / 2 + (0.02 / 2), new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + + auto* tM0b = new TGeoBBox(0.02 / 2, 0.02 / 2, zsta); + auto* volTM0b = new TGeoVolume("microChanWalls", tM0b, medKapton); + volTM0b->SetLineColor(35); + volTM0b->SetFillColor(35); + for (Int_t ib = 0; ib < nb; ib++) { + // mechStavVol->AddNode(volTM0b, ib, new TGeoCombiTrans(x+ib*0.1-xstaMC+0.01,yMC, z, new + // TGeoRotation("",0, 0, 0))); + } + } + + if (mBuildLevel < 4) { + // cooling inlet outlet + auto* tM0dv = new TGeoBBox(vaporThick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0dv = new TGeoVolume("microChanVapor", tM0dv, medWater); + volTM0dv->SetLineColor(2); + volTM0dv->SetFillColor(2); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0dv, 0, new TGeoCombiTrans(x + smcWidth / 2 - (smcSide1Thick) - (vaporThick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + auto* tM0dl = new TGeoBBox(liquidThick / 2, 0.035 / 2, smcLength / 2); + auto* volTM0dl = new TGeoVolume("microChanLiquid", tM0dl, medWater); + volTM0dl->SetLineColor(3); + volTM0dl->SetFillColor(3); + for (Int_t mo = 1; mo <= chips; mo++) { + mechStavVol->AddNode( + volTM0dl, 0, new TGeoCombiTrans(x - smcWidth / 2 + (smcSide1Thick) + (liquidThick / 2), yMC + 0.03 - 0.0015 - (0.035) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + // small cooling fluid now using water wait for freeon value + auto* tM0dlq = new TGeoBBox(smcSpace / 2, hh / 2, smcLength / 2); + auto* volTM0dlq = new TGeoVolume("smallLiquid", tM0dlq, medWater); + volTM0dlq->SetLineColor(3); + volTM0dlq->SetFillColor(3); + auto* tM0dvp = new TGeoBBox(smcSpace / 2, hh / 2, smcLength / 2); + auto* volTM0dvp = new TGeoVolume("microChanVapor", tM0dvp, medWater); + volTM0dvp->SetLineColor(2); + volTM0dvp->SetFillColor(2); + for (Int_t mo = 1; mo <= chips; mo++) { + for (Int_t is = 0; is < 12; is++) { + mechStavVol->AddNode( + volTM0dlq, 0, new TGeoCombiTrans(x + (is * (smcSpace + smcSide5Thick)) - smcWidth / 2 + (smcSide1Thick) + (vaporThick) + (smcSide2Thick) + (smcSide3Thick) + smcSpace / 2, yMC + 0.03 - (0.003 + hh) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + mechStavVol->AddNode( + volTM0dvp, 1, new TGeoCombiTrans(x - (is * (smcSpace + smcSide5Thick)) + smcWidth / 2 - (smcSide1Thick) - (vaporThick) - (smcSide2Thick) - (smcSide3Thick)-smcSpace / 2, yMC + 0.03 - (0.003 + hh) / 2, z + (mo - 3) * kStaveLength / 4 + smcLength / 2 + headWidth, new TGeoRotation("", ang1, ang2, ang3))); //("",0, 0, 0))); + } + } + } + + if (mBuildLevel < 3) { + // Bottom filament CFRP black-12 Carbon structure TGeoBBox (thickness,width,length) + Double_t filWidth = 0.04; + Double_t filHeight = 0.02; + auto* t1 = new TGeoBBox(filHeight / 2, filWidth / 2, s1); + auto* volT1 = new TGeoVolume("bottomFilament", t1, medM60J3K); + volT1->SetLineColor(12); + volT1->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode(volT1, 4 * i + 0, + new TGeoCombiTrans(x + w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * l * i) + s1 / 2, + new TGeoRotation("volT1", -90, alpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 1, + new TGeoCombiTrans(x - w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (4 * l * i) + s1 / 2, + new TGeoRotation("volT1", 90, alpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 2, + new TGeoCombiTrans(x + w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT1", -90, -alpha, 0))); + mechStavVol->AddNode(volT1, 4 * i + 3, + new TGeoCombiTrans(x - w, y - h + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT1", -90, +alpha, 0))); + } + + // Top filament CERP black-12 Carbon structure TGeoBBox (length,thickness,width) + auto* t2 = new TGeoBBox(s2, filHeight / 2, filWidth / 2); + auto* volT2 = new TGeoVolume("topFilament", t2, medM60J3K); + volT2->SetLineColor(12); + volT2->SetFillColor(12); + for (int i = 0; i < loop; i++) { // i<30;i++){ + mechStavVol->AddNode( + volT2, 4 * i + 0, new TGeoCombiTrans(x + w, y + 0.04 + filHeight / 2, z - kStaveLength / 2 + (i * 4 * l) + s1 / 2, new TGeoRotation("volT2", 90, 90 - alpha, 90 - beta))); + mechStavVol->AddNode( + volT2, 4 * i + 1, + new TGeoCombiTrans(x - w, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT2", 90, -90 + alpha, -90 + beta))); + mechStavVol->AddNode( + volT2, 4 * i + 2, + new TGeoCombiTrans(x + w, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT2", 90, -90 + alpha, 90 - beta))); + mechStavVol->AddNode( + volT2, 4 * i + 3, + new TGeoCombiTrans(x - w, y + 0.04 + filHeight / 2, + z - kStaveLength / 2 + 2 * l + (i * 4 * l) + s1 / 2, + new TGeoRotation("volT2", 90, 90 - alpha, -90 + beta))); + } + } + + if (mBuildLevel < 2) { + // Glue Filament and Silicon MicroChannel + auto* tM0 = new TGeoBBox(xstaMC / 5, klay4 / 2, zsta); + auto* volTM0 = new TGeoVolume("glueFM", tM0, medGlue); + volTM0->SetLineColor(5); + volTM0->SetFillColor(5); + mechStavVol->AddNode(volTM0, 0, new TGeoCombiTrans(x - xsta / 2 - 0.25, 0.03 + yMC, z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volTM0, 1, new TGeoCombiTrans(x + xsta / 2 + 0.25, 0.03 + yMC, z, new TGeoRotation("", 0, 0, 0))); + + // Glue microchannel and sensor + auto* glueM = new TGeoBBox(xstaMC / 5, klay4 / 2, zsta); + auto* volGlueM = new TGeoVolume("glueMS", glueM, medGlue); + volGlueM->SetLineColor(5); + volGlueM->SetFillColor(5); + mechStavVol->AddNode(volGlueM, 0, new TGeoCombiTrans(x - xsta / 2 - 0.25, yMC - 0.01, z, new TGeoRotation("", 0, 0, 0))); + mechStavVol->AddNode(volGlueM, 1, new TGeoCombiTrans(x + xsta / 2 + 0.25, yMC - 0.01, z, new TGeoRotation("", 0, 0, 0))); + + // Glue sensor and kapton + auto* glue = new TGeoBBox(xsta, klay4 / 2, zsta); + auto* volGlue = new TGeoVolume("glueSensorBus", glue, medGlue); + volGlue->SetLineColor(5); + volGlue->SetFillColor(5); + mechStavVol->AddNode(volGlue, 1, new TGeoCombiTrans(x, y - 0.154 - mSensorThickness - klay4 / 2, z, new TGeoRotation("", 0, 0, 0))); + } + + if (mBuildLevel < 1) { + auto* kapCable = new TGeoBBox(xsta, klay5 / 2, zsta); + auto* volCable = new TGeoVolume("Flexcable", kapCable, medFlexCable); + volCable->SetLineColor(28); + volCable->SetFillColor(28); + mechStavVol->AddNode(volCable, 0, + new TGeoCombiTrans(x, y - 0.154 - mSensorThickness - klay4 - klay5 / 2, z, + new TGeoRotation("", 0, 0, 0))); + } + // Done, return the stave structure + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveOuterB(const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + mechStavVol = createStaveModelOuterBDummy(mgr); + break; + case Detector::kOBModel0: + mechStavVol = createStaveModelOuterB0(mgr); + break; + case Detector::kOBModel1: + mechStavVol = createStaveModelOuterB1(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V1Layer::createStaveModelOuterBDummy(const TGeoManager*) const +{ + // Done, return the stave structure + return nullptr; +} + +TGeoVolume* V1Layer::createStaveModelOuterB0(const TGeoManager* mgr) +{ + Double_t xmod, ymod, zmod; + Double_t xlen, ylen, zlen; + Double_t ypos, zpos; + char volumeName[30]; + + // First create all needed shapes + // The chip + xlen = sOBHalfStaveWidth; + ylen = 0.5 * mStaveThickness; // TO BE CHECKED + zlen = sOBModuleZLength / 2; + + TGeoVolume* chipVol = createChipInnerB(xlen, ylen, zlen); + + xmod = ((TGeoBBox*)chipVol->GetShape())->GetDX(); + ymod = ((TGeoBBox*)chipVol->GetShape())->GetDY(); + zmod = ((TGeoBBox*)chipVol->GetShape())->GetDZ(); + + auto* module = new TGeoBBox(xmod, ymod, zmod); + + zlen = sOBModuleZLength * mNumberOfModules; + auto* hstave = new TGeoBBox(xlen, ylen, zlen / 2); + + // We have all shapes: now create the real volumes + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0ModulePattern(), mLayerNumber); + auto* modVol = new TGeoVolume(volumeName, module, medAir); + modVol->SetVisibility(kTRUE); + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0HalfStavePattern(), mLayerNumber); + auto* hstaveVol = new TGeoVolume(volumeName, hstave, medAir); + + // Finally build it up + modVol->AddNode(chipVol, 0); + mHierarchy[kChip] = 1; + + for (Int_t j = 0; j < mNumberOfModules; j++) { + ypos = 0.021; // Remove small overlap - M.S: 21may13 + zpos = -hstave->GetDZ() + j * 2 * zmod + zmod; + hstaveVol->AddNode(modVol, j, new TGeoTranslation(0, ypos, zpos)); + mHierarchy[kModule]++; + } + // Done, return the stave structure + return hstaveVol; +} + +TGeoVolume* V1Layer::createStaveModelOuterB1(const TGeoManager* mgr) +{ + Double_t yFlex1 = sOBFlexCableAlThick; + Double_t yFlex2 = sOBFlexCableKapThick; + Double_t flexOverlap = 5; // to be checked + Double_t xHalmSt = sOBHalfStaveWidth / 2; + Double_t rCoolMin = sOBCoolTubeInnerD / 2; + Double_t rCoolMax = rCoolMin + sOBCoolTubeThick; + Double_t kLay1 = 0.004; // to be checked + Double_t kLay2 = sOBGraphiteFoilThick; + + Double_t xlen, ylen; + Double_t ymod, zmod; + Double_t xtru[12], ytru[12]; + Double_t xpos, ypos, ypos1, zpos /*, zpos5cm*/; + Double_t zlen; + char volumeName[30]; + + zlen = (mNumberOfModules * sOBModuleZLength + (mNumberOfModules - 1) * sOBModuleGap) / 2; + + // First create all needed shapes + TGeoVolume* moduleVol = createModuleOuterB(); + moduleVol->SetVisibility(kTRUE); + ymod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDY(); + zmod = ((TGeoBBox*)(moduleVol->GetShape()))->GetDZ(); + + auto* busAl = new TGeoBBox("BusAl", xHalmSt, sOBBusCableAlThick / 2, zlen); + auto* busKap = new TGeoBBox("BusKap", xHalmSt, sOBBusCableKapThick / 2, zlen); + + auto* coldPlate = + new TGeoBBox("ColdPlate", sOBHalfStaveWidth / 2, sOBColdPlateThick / 2, zlen); + + auto* coolTube = new TGeoTube("CoolingTube", rCoolMin, rCoolMax, zlen); + auto* coolWater = new TGeoTube("CoolingWater", 0., rCoolMin, zlen); + + xlen = xHalmSt - sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + auto* graphlat = new TGeoBBox("GraphLateral", xlen / 2, kLay2 / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + auto* graphmid = new TGeoBBox("GraphMiddle", xlen, kLay2 / 2, zlen); + + ylen = coolTube->GetRmax() - kLay2; + auto* graphvert = new TGeoBBox("GraphVertical", kLay2 / 2, ylen / 2, zlen); + + auto* graphtub = + new TGeoTubeSeg("GraphTube", rCoolMax, rCoolMax + kLay2, zlen, 180., 360.); + + xlen = xHalmSt - sOBCoolTubeXDist / 2 - coolTube->GetRmax() - kLay2; + auto* fleeclat = new TGeoBBox("FleecLateral", xlen / 2, kLay1 / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax() - kLay2; + auto* fleecmid = new TGeoBBox("FleecMiddle", xlen, kLay1 / 2, zlen); + + ylen = coolTube->GetRmax() - kLay2 - kLay1; + auto* fleecvert = new TGeoBBox("FleecVertical", kLay1 / 2, ylen / 2, zlen); + + auto* fleectub = + new TGeoTubeSeg("FleecTube", rCoolMax + kLay2, rCoolMax + kLay1 + kLay2, zlen, 180., 360.); + + auto* flex1_5cm = new TGeoBBox("Flex1MV_5cm", xHalmSt, yFlex1 / 2, flexOverlap / 2); + auto* flex2_5cm = new TGeoBBox("Flex2MV_5cm", xHalmSt, yFlex2 / 2, flexOverlap / 2); + + // The half stave container (an XTru to avoid overlaps between neightbours) + xtru[0] = xHalmSt; + ytru[0] = 0; + xtru[1] = xtru[0]; + ytru[1] = -2 * (ymod + busAl->GetDY() + busKap->GetDY() + coldPlate->GetDY() + graphlat->GetDY() + + fleeclat->GetDY()); + xtru[2] = sOBCoolTubeXDist / 2 + fleectub->GetRmax(); + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[2] - (coolTube->GetRmax() + fleectub->GetRmax()); + xtru[4] = sOBCoolTubeXDist / 2 - fleectub->GetRmax(); + ytru[4] = ytru[3]; + xtru[5] = xtru[4]; + ytru[5] = ytru[2]; + for (Int_t i = 0; i < 6; i++) { + xtru[6 + i] = -xtru[5 - i]; + ytru[6 + i] = ytru[5 - i]; + } + auto* halmStave = new TGeoXtru(2); + halmStave->DefinePolygon(12, xtru, ytru); + halmStave->DefineSection(0, -mZLength / 2); + halmStave->DefineSection(1, mZLength / 2); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAluminum = mgr->GetMedium("EC0_ALUMINUM$"); + TGeoMedium* medCarbon = mgr->GetMedium("EC0_CARBON$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("EC0_CarbonFleece$"); + TGeoMedium* medFGS003 = mgr->GetMedium("EC0_FGS003$"); // amec thermasol + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + auto* busAlVol = new TGeoVolume("BusAlVol", busAl, medAluminum); + busAlVol->SetLineColor(kCyan); + busAlVol->SetFillColor(busAlVol->GetLineColor()); + busAlVol->SetFillStyle(4000); // 0% transparent + + auto* busKapVol = new TGeoVolume("BusKapVol", busKap, medKapton); + busKapVol->SetLineColor(kBlue); + busKapVol->SetFillColor(busKapVol->GetLineColor()); + busKapVol->SetFillStyle(4000); // 0% transparent + + auto* coldPlateVol = new TGeoVolume("ColdPlateVol", coldPlate, medCarbon); + coldPlateVol->SetLineColor(kYellow - 3); + coldPlateVol->SetFillColor(coldPlateVol->GetLineColor()); + coldPlateVol->SetFillStyle(4000); // 0% transparent + + auto* coolTubeVol = new TGeoVolume("CoolingTubeVol", coolTube, medKapton); + coolTubeVol->SetLineColor(kGray); + coolTubeVol->SetFillColor(coolTubeVol->GetLineColor()); + coolTubeVol->SetFillStyle(4000); // 0% transparent + + auto* coolWaterVol = new TGeoVolume("CoolingWaterVol", coolWater, medWater); + coolWaterVol->SetLineColor(kBlue); + coolWaterVol->SetFillColor(coolWaterVol->GetLineColor()); + coolWaterVol->SetFillStyle(4000); // 0% transparent + + auto* graphlatVol = new TGeoVolume("GraphiteFoilLateral", graphlat, medFGS003); + graphlatVol->SetLineColor(kGreen); + graphlatVol->SetFillColor(graphlatVol->GetLineColor()); + graphlatVol->SetFillStyle(4000); // 0% transparent + + auto* graphmidVol = new TGeoVolume("GraphiteFoilMiddle", graphmid, medFGS003); + graphmidVol->SetLineColor(kGreen); + graphmidVol->SetFillColor(graphmidVol->GetLineColor()); + graphmidVol->SetFillStyle(4000); // 0% transparent + + auto* graphvertVol = new TGeoVolume("GraphiteFoilVertical", graphvert, medFGS003); + graphvertVol->SetLineColor(kGreen); + graphvertVol->SetFillColor(graphvertVol->GetLineColor()); + graphvertVol->SetFillStyle(4000); // 0% transparent + + auto* graphtubVol = new TGeoVolume("GraphiteFoilPipeCover", graphtub, medFGS003); + graphtubVol->SetLineColor(kGreen); + graphtubVol->SetFillColor(graphtubVol->GetLineColor()); + graphtubVol->SetFillStyle(4000); // 0% transparent + + auto* fleeclatVol = new TGeoVolume("CarbonFleeceLateral", fleeclat, medCarbonFleece); + fleeclatVol->SetLineColor(kViolet); + fleeclatVol->SetFillColor(fleeclatVol->GetLineColor()); + fleeclatVol->SetFillStyle(4000); // 0% transparent + + auto* fleecmidVol = new TGeoVolume("CarbonFleeceMiddle", fleecmid, medCarbonFleece); + fleecmidVol->SetLineColor(kViolet); + fleecmidVol->SetFillColor(fleecmidVol->GetLineColor()); + fleecmidVol->SetFillStyle(4000); // 0% transparent + + auto* fleecvertVol = new TGeoVolume("CarbonFleeceVertical", fleecvert, medCarbonFleece); + fleecvertVol->SetLineColor(kViolet); + fleecvertVol->SetFillColor(fleecvertVol->GetLineColor()); + fleecvertVol->SetFillStyle(4000); // 0% transparent + + auto* fleectubVol = new TGeoVolume("CarbonFleecePipeCover", fleectub, medCarbonFleece); + fleectubVol->SetLineColor(kViolet); + fleectubVol->SetFillColor(fleectubVol->GetLineColor()); + fleectubVol->SetFillStyle(4000); // 0% transparent + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0HalfStavePattern(), mLayerNumber); + auto* halmStaveVol = new TGeoVolume(volumeName, halmStave, medAir); + // halmStaveVol->SetLineColor(12); + // halmStaveVol->SetFillColor(12); + // halmStaveVol->SetVisibility(kTRUE); + + auto* flex1_5cmVol = new TGeoVolume("Flex1Vol5cm", flex1_5cm, medAluminum); + auto* flex2_5cmVol = new TGeoVolume("Flex2Vol5cm", flex2_5cm, medKapton); + + flex1_5cmVol->SetLineColor(kRed); + flex2_5cmVol->SetLineColor(kGreen); + + // Now build up the half stave + ypos = -busKap->GetDY(); + halmStaveVol->AddNode(busKapVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos -= (busKap->GetDY() + busAl->GetDY()); + halmStaveVol->AddNode(busAlVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos -= (busAl->GetDY() + ymod); + for (Int_t j = 0; j < mNumberOfModules; j++) { + zpos = -zlen + j * (2 * zmod + sOBModuleGap) + zmod; + halmStaveVol->AddNode(moduleVol, j, new TGeoTranslation(0, ypos, zpos)); + mHierarchy[kModule]++; + } + + ypos -= (ymod + coldPlate->GetDY()); + halmStaveVol->AddNode(coldPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + + coolTubeVol->AddNode(coolWaterVol, 1, nullptr); + + xpos = sOBCoolTubeXDist / 2; + ypos1 = ypos - (coldPlate->GetDY() + coolTube->GetRmax()); + halmStaveVol->AddNode(coolTubeVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(coolTubeVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(graphtubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphtubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(fleectubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleectubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + xpos = xHalmSt - graphlat->GetDX(); + ypos1 = ypos - (coldPlate->GetDY() + graphlat->GetDY()); + halmStaveVol->AddNode(graphlatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphlatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(graphmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = xHalmSt - 2 * graphlat->GetDX() + graphvert->GetDX(); + ypos1 = ypos - (coldPlate->GetDY() + 2 * graphlat->GetDY() + graphvert->GetDY()); + halmStaveVol->AddNode(graphvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = graphmid->GetDX() - graphvert->GetDX(); + halmStaveVol->AddNode(graphvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(graphvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + + xpos = xHalmSt - fleeclat->GetDX(); + ypos1 = ypos - (coldPlate->GetDY() + 2 * graphlat->GetDY() + fleeclat->GetDY()); + halmStaveVol->AddNode(fleeclatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleeclatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halmStaveVol->AddNode(fleecmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = xHalmSt - 2 * fleeclat->GetDX() + fleecvert->GetDX(); + ypos1 = ypos - + (coldPlate->GetDY() + 2 * graphlat->GetDY() + 2 * fleeclat->GetDY() + fleecvert->GetDY()); + halmStaveVol->AddNode(fleecvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleecvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = fleecmid->GetDX() - fleecvert->GetDX(); + halmStaveVol->AddNode(fleecvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halmStaveVol->AddNode(fleecvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + + // THE FOLLOWING IS ONLY A REMINDER FOR WHAT IS STILL MISSING + + // for (Int_t j=0; jAddNode(moduleVol, j, new TGeoTranslation(xPos, -ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + ymod, zpos)); + // halmStaveVol->AddNode(moduleVol, mNumberOfChips+j, new TGeoTranslation(-xPos, -ylen + yPos + // + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + ymod +deltaY, zpos)); + + // if((j+1)!=mNumberOfChips){ + // halmStaveVol->AddNode(flex1_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + 2*ymod + yFlex1 + yFlex2 + yFlex1/2,zpos5cm)); + // halmStaveVol->AddNode(flex1_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + yFlex1 + yFlex2 + yFlex1/2 + // +deltaY,zpos5cm)); + // halmStaveVol->AddNode(flex2_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + 2*ymod + 2*yFlex1 + 3*yFlex2/2,zpos5cm)); + // halmStaveVol->AddNode(flex2_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + 2*yFlex1 + 3*yFlex2/2 +deltaY,zpos5cm)); + // } + // else { + // halmStaveVol->AddNode(flex1_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate + 2*ymod + yFlex1/2,zpos5cm-modGap)); + // halmStaveVol->AddNode(flex1_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + yFlex1/2 +deltaY,zpos5cm-modGap)); + // halmStaveVol->AddNode(flex2_5cmVol,j,new TGeoTranslation(xPos,-ylen + yPos + 2*rCoolMax + + // yCPlate + yGlue + yModPlate +2*ymod + yFlex1 + yFlex2/2,zpos5cm-modGap)); + // halmStaveVol->AddNode(flex2_5cmVol,mNumberOfChips+j,new TGeoTranslation(-xPos,-ylen + + // yPos + + // 2*rCoolMax + yCPlate + yGlue + yModPlate + 2*ymod + yFlex1 + yFlex2/2 +deltaY,zpos5cm-modGap)); + + // } + // } + // Done, return the half stave structure + return halmStaveVol; +} + +TGeoVolume* V1Layer::createSpaceFrameOuterB(const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + case Detector::kOBModel0: + mechStavVol = createSpaceFrameOuterBDummy(mgr); + break; + case Detector::kOBModel1: + mechStavVol = createSpaceFrameOuterB1(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + + return mechStavVol; +} + +TGeoVolume* V1Layer::createSpaceFrameOuterBDummy(const TGeoManager*) const +{ + // Done, return the stave structur + return nullptr; +} + +TGeoVolume* V1Layer::createSpaceFrameOuterB1(const TGeoManager* mgr) +{ + // Materials defined in Detector + TGeoMedium* medCarbon = mgr->GetMedium("EC0_CARBON$"); + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + // Local parameters + Double_t sframeWidth = sOBSpaceFrameWidth; + Double_t sframeHeight = sOBSpaceFrameTotHigh - sOBHalfStaveYTrans; + Double_t staveBeamRadius = sOBSFrameBeamRadius; + Double_t staveLa = sOBSpaceFrameLa; + Double_t staveHa = sOBSpaceFrameHa; + Double_t staveLb = sOBSpaceFrameLb; + Double_t staveHb = sOBSpaceFrameHb; + Double_t stavel = sOBSpaceFrameL; + Double_t bottomBeamAngle = sOBSFBotBeamAngle; + Double_t triangleHeight = sframeHeight - staveBeamRadius; + Double_t halmTheta = TMath::ATan(0.5 * sframeWidth / triangleHeight); + // Double_t alpha = TMath::Pi()*3./4. - halmTheta/2.; + Double_t beta = (TMath::Pi() - 2. * halmTheta) / 4.; + // Double_t distCenterSideDown = 0.5*sframeWidth/TMath::Cos(beta); + + Double_t zlen; + Double_t xpos, ypos, zpos; + Double_t seglen; + char volumeName[30]; + + zlen = mNumberOfModules * sOBModuleZLength + (mNumberOfModules - 1) * sOBModuleGap; + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0HalfStavePattern(), mLayerNumber); + if (gGeoManager->GetVolume(volumeName)) { // Should always be so + sframeHeight -= ((TGeoBBox*)gGeoManager->GetVolume(volumeName)->GetShape())->GetDY() * 2; + zlen = ((TGeoBBox*)gGeoManager->GetVolume(volumeName)->GetShape())->GetDZ() * 2; + } + seglen = zlen / mNumberOfModules; + + // First create all needed shapes and volumes + auto* spaceFrame = new TGeoBBox(sframeWidth / 2, sframeHeight / 2, zlen / 2); + auto* segment = new TGeoBBox(sframeWidth / 2, sframeHeight / 2, seglen / 2); + + auto* spaceFrameVol = new TGeoVolume("CarbonFrameVolume", spaceFrame, medAir); + spaceFrameVol->SetVisibility(kFALSE); + + auto* segmentVol = new TGeoVolume("segmentVol", segment, medAir); + + // SpaceFrame + + //--- the top V of the Carbon Fiber Stave (segment) + TGeoArb8* cmStavTop1 = createStaveSide("CFstavTopCornerVol1shape", seglen / 2., halmTheta, -1, + staveLa, staveHa, stavel); + auto* cmStavTopVol1 = new TGeoVolume("CFstavTopCornerVol1", cmStavTop1, medCarbon); + cmStavTopVol1->SetLineColor(35); + + TGeoArb8* cmStavTop2 = createStaveSide("CFstavTopCornerVol2shape", seglen / 2., halmTheta, 1, + staveLa, staveHa, stavel); + auto* cmStavTopVol2 = new TGeoVolume("CFstavTopCornerVol2", cmStavTop2, medCarbon); + cmStavTopVol2->SetLineColor(35); + + auto* trTop1 = new TGeoTranslation(0, sframeHeight / 2, 0); + + //--- the 2 side V + TGeoArb8* cmStavSide1 = + createStaveSide("CFstavSideCornerVol1shape", seglen / 2., beta, -1, staveLb, staveHb, stavel); + auto* cmStavSideVol1 = new TGeoVolume("CFstavSideCornerVol1", cmStavSide1, medCarbon); + cmStavSideVol1->SetLineColor(35); + + TGeoArb8* cmStavSide2 = + createStaveSide("CFstavSideCornerVol2shape", seglen / 2., beta, 1, staveLb, staveHb, stavel); + auto* cmStavSideVol2 = new TGeoVolume("CFstavSideCornerVol2", cmStavSide2, medCarbon); + cmStavSideVol2->SetLineColor(35); + + xpos = -sframeWidth / 2; + ypos = -sframeHeight / 2 + staveBeamRadius + staveHb * TMath::Sin(beta); + auto* ctSideR = new TGeoCombiTrans( + xpos, ypos, 0, new TGeoRotation("", 180 - 2 * beta * TMath::RadToDeg(), 0, 0)); + auto* ctSideL = new TGeoCombiTrans( + -xpos, ypos, 0, new TGeoRotation("", -180 + 2 * beta * TMath::RadToDeg(), 0, 0)); + + segmentVol->AddNode(cmStavTopVol1, 1, trTop1); + segmentVol->AddNode(cmStavTopVol2, 1, trTop1); + segmentVol->AddNode(cmStavSideVol1, 1, ctSideR); + segmentVol->AddNode(cmStavSideVol1, 2, ctSideL); + segmentVol->AddNode(cmStavSideVol2, 1, ctSideR); + segmentVol->AddNode(cmStavSideVol2, 2, ctSideL); + + //--- The beams + // Beams on the sides + Double_t beamPhiPrime = TMath::ASin( + 1. / TMath::Sqrt((1 + TMath::Sin(2 * beta) * TMath::Sin(2 * beta) / + (tanD(sOBSFrameBeamSidePhi) * tanD(sOBSFrameBeamSidePhi))))); + Double_t beamLength = TMath::Sqrt(sframeHeight * sframeHeight / + (TMath::Sin(beamPhiPrime) * TMath::Sin(beamPhiPrime)) + + sframeWidth * sframeWidth / 4.) - + staveLa / 2 - staveLb / 2; + auto* sideBeam = new TGeoTubeSeg(0, staveBeamRadius, beamLength / 2, 0, 180); + auto* sideBeamVol = new TGeoVolume("CFstavSideBeamVol", sideBeam, medCarbon); + sideBeamVol->SetLineColor(35); + + auto* beamRot1 = new TGeoRotation("", /*90-2*beta*/ halmTheta * TMath::RadToDeg(), + -beamPhiPrime * TMath::RadToDeg(), -90); + auto* beamRot2 = + new TGeoRotation("", 90 - 2. * beta * TMath::RadToDeg(), beamPhiPrime * TMath::RadToDeg(), -90); + auto* beamRot3 = + new TGeoRotation("", 90 + 2. * beta * TMath::RadToDeg(), beamPhiPrime * TMath::RadToDeg(), -90); + auto* beamRot4 = new TGeoRotation("", 90 + 2. * beta * TMath::RadToDeg(), + -beamPhiPrime * TMath::RadToDeg(), -90); + + TGeoCombiTrans* beamTransf[8]; + xpos = 0.49 * triangleHeight * TMath::Tan(halmTheta); // was 0.5, fix small overlap + ypos = staveBeamRadius / 2; + zpos = seglen / 8; + beamTransf[0] = new TGeoCombiTrans(xpos, ypos, -3 * zpos, beamRot1); + + beamTransf[1] = new TGeoCombiTrans(xpos, ypos, -3 * zpos, beamRot1); + addTranslationToCombiTrans(beamTransf[1], 0, 0, seglen / 2); + + beamTransf[2] = new TGeoCombiTrans(xpos, ypos, -zpos, beamRot2); + + beamTransf[3] = new TGeoCombiTrans(xpos, ypos, -zpos, beamRot2); + addTranslationToCombiTrans(beamTransf[3], 0, 0, seglen / 2); + + beamTransf[4] = new TGeoCombiTrans(-xpos, ypos, -3 * zpos, beamRot3); + + beamTransf[5] = new TGeoCombiTrans(-xpos, ypos, -3 * zpos, beamRot3); + addTranslationToCombiTrans(beamTransf[5], 0, 0, seglen / 2); + + beamTransf[6] = new TGeoCombiTrans(-xpos, ypos, -zpos, beamRot4); + beamTransf[7] = new TGeoCombiTrans(-xpos, ypos, 3 * zpos, beamRot4); + + //--- Beams of the bottom + auto* bottomBeam1 = + new TGeoTubeSeg(0, staveBeamRadius, sframeWidth / 2. - staveLb / 3, 0, 180); + auto* bottomBeam1Vol = new TGeoVolume("CFstavBottomBeam1Vol", bottomBeam1, medCarbon); + bottomBeam1Vol->SetLineColor(35); + + auto* bottomBeam2 = + new TGeoTubeSeg(0, staveBeamRadius, sframeWidth / 2. - staveLb / 3, 0, 90); + auto* bottomBeam2Vol = new TGeoVolume("CFstavBottomBeam2Vol", bottomBeam2, medCarbon); + bottomBeam2Vol->SetLineColor(35); + + auto* bottomBeam3 = new TGeoTubeSeg( + 0, staveBeamRadius, 0.5 * sframeWidth / sinD(bottomBeamAngle) - staveLb / 3, 0, 180); + auto* bottomBeam3Vol = new TGeoVolume("CFstavBottomBeam3Vol", bottomBeam3, medCarbon); + bottomBeam3Vol->SetLineColor(35); + + auto* bottomBeamRot1 = new TGeoRotation("", 90, 90, 90); + auto* bottomBeamRot2 = new TGeoRotation("", -90, 90, -90); + + auto* bottomBeamTransf1 = + new TGeoCombiTrans("", 0, -(sframeHeight / 2 - staveBeamRadius), 0, bottomBeamRot1); + auto* bottomBeamTransf2 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), -seglen / 2, bottomBeamRot1); + auto* bottomBeamTransf3 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), seglen / 2, bottomBeamRot2); + // be careful for beams #3: when "reading" from -z to +z and + // from the bottom of the stave, it should draw a Lambda, and not a V + auto* bottomBeamRot4 = new TGeoRotation("", -90, bottomBeamAngle, -90); + auto* bottomBeamRot5 = new TGeoRotation("", -90, -bottomBeamAngle, -90); + + auto* bottomBeamTransf4 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), -seglen / 4, bottomBeamRot4); + auto* bottomBeamTransf5 = + new TGeoCombiTrans(0, -(sframeHeight / 2 - staveBeamRadius), seglen / 4, bottomBeamRot5); + + segmentVol->AddNode(sideBeamVol, 1, beamTransf[0]); + segmentVol->AddNode(sideBeamVol, 2, beamTransf[1]); + segmentVol->AddNode(sideBeamVol, 3, beamTransf[2]); + segmentVol->AddNode(sideBeamVol, 4, beamTransf[3]); + segmentVol->AddNode(sideBeamVol, 5, beamTransf[4]); + segmentVol->AddNode(sideBeamVol, 6, beamTransf[5]); + segmentVol->AddNode(sideBeamVol, 7, beamTransf[6]); + segmentVol->AddNode(sideBeamVol, 8, beamTransf[7]); + segmentVol->AddNode(bottomBeam1Vol, 1, bottomBeamTransf1); + segmentVol->AddNode(bottomBeam2Vol, 1, bottomBeamTransf2); + segmentVol->AddNode(bottomBeam2Vol, 2, bottomBeamTransf3); + segmentVol->AddNode(bottomBeam3Vol, 1, bottomBeamTransf4); + segmentVol->AddNode(bottomBeam3Vol, 2, bottomBeamTransf5); + + // Then build up the space frame + for (Int_t i = 0; i < mNumberOfModules; i++) { + zpos = -spaceFrame->GetDZ() + (1 + 2 * i) * segment->GetDZ(); + spaceFrameVol->AddNode(segmentVol, i, new TGeoTranslation(0, 0, zpos)); + } + + // Done, return the space frame structure + return spaceFrameVol; +} + +TGeoVolume* V1Layer::createChipInnerB(const Double_t xchip, const Double_t ychip, + const Double_t zchip, const TGeoManager* mgr) +{ + char volumeName[30]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // First create all needed shapes + + // The chip + auto* chip = new TGeoBBox(xchip, ychip, zchip); + + // The sensor + xlen = chip->GetDX(); + ylen = 0.5 * mSensorThickness; + zlen = chip->GetDZ(); + auto* sensor = new TGeoBBox(xlen, ylen, zlen); + + // We have all shapes: now create the real volumes + TGeoMedium* medSi = mgr->GetMedium("EC0_SI$"); + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0ChipPattern(), mLayerNumber); + auto* chipVol = new TGeoVolume(volumeName, chip, medSi); + chipVol->SetVisibility(kTRUE); + chipVol->SetLineColor(1); + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0SensorPattern(), mLayerNumber); + auto* sensVol = new TGeoVolume(volumeName, sensor, medSi); + sensVol->SetVisibility(kTRUE); + sensVol->SetLineColor(8); + sensVol->SetLineWidth(1); + sensVol->SetFillColor(sensVol->GetLineColor()); + sensVol->SetFillStyle(4000); // 0% transparent + + // Now build up the chip + xpos = 0.; + ypos = -chip->GetDY() + sensor->GetDY(); + zpos = 0.; + + chipVol->AddNode(sensVol, 1, new TGeoTranslation(xpos, ypos, zpos)); + + // Done, return the chip + return chipVol; +} + +TGeoVolume* V1Layer::createModuleOuterB(const TGeoManager* mgr) +{ + char volumeName[30]; + + Double_t xGap = sOBChipXGap; + Double_t zGap = sOBChipZGap; + + Double_t xchip, ychip, zchip; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // First create all needed shapes + + // The chip (the same as for IB) + xlen = (sOBHalfStaveWidth / 2 - xGap / 2) / sOBNChipRows; + ylen = 0.5 * mStaveThickness; // TO BE CHECKED + zlen = (sOBModuleZLength - (sOBChipsPerRow - 1) * zGap) / (2 * sOBChipsPerRow); + + TGeoVolume* chipVol = createChipInnerB(xlen, ylen, zlen); + + xchip = ((TGeoBBox*)chipVol->GetShape())->GetDX(); + ychip = ((TGeoBBox*)chipVol->GetShape())->GetDY(); + zchip = ((TGeoBBox*)chipVol->GetShape())->GetDZ(); + + // The module carbon plate + xlen = sOBHalfStaveWidth / 2; + ylen = sOBCarbonPlateThick / 2; + zlen = sOBModuleZLength / 2; + auto* modPlate = new TGeoBBox("CarbonPlate", xlen, ylen, zlen); + + // The glue + ylen = sOBGlueThick / 2; + auto* glue = new TGeoBBox("Glue", xlen, ylen, zlen); + + // The flex cables + ylen = sOBFlexCableAlThick / 2; + auto* flexAl = new TGeoBBox("FlexAl", xlen, ylen, zlen); + + ylen = sOBFlexCableKapThick / 2; + auto* flexKap = new TGeoBBox("FlexKap", xlen, ylen, zlen); + + // The module + xlen = sOBHalfStaveWidth / 2; + ylen = ychip + modPlate->GetDY() + glue->GetDY() + flexAl->GetDY() + flexKap->GetDY(); + zlen = sOBModuleZLength / 2; + auto* module = new TGeoBBox("OBModule", xlen, ylen, zlen); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medCarbon = mgr->GetMedium("EC0_CARBON$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medAluminum = mgr->GetMedium("EC0_ALUMINUM$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + + auto* modPlateVol = new TGeoVolume("CarbonPlateVol", modPlate, medCarbon); + modPlateVol->SetLineColor(kMagenta - 8); + modPlateVol->SetFillColor(modPlateVol->GetLineColor()); + modPlateVol->SetFillStyle(4000); // 0% transparent + + auto* glueVol = new TGeoVolume("GlueVol", glue, medGlue); + glueVol->SetLineColor(kBlack); + glueVol->SetFillColor(glueVol->GetLineColor()); + glueVol->SetFillStyle(4000); // 0% transparent + + auto* flexAlVol = new TGeoVolume("FlexAlVol", flexAl, medAluminum); + flexAlVol->SetLineColor(kRed); + flexAlVol->SetFillColor(flexAlVol->GetLineColor()); + flexAlVol->SetFillStyle(4000); // 0% transparent + + auto* flexKapVol = new TGeoVolume("FlexKapVol", flexKap, medKapton); + flexKapVol->SetLineColor(kGreen); + flexKapVol->SetFillColor(flexKapVol->GetLineColor()); + flexKapVol->SetFillStyle(4000); // 0% transparent + + snprintf(volumeName, 30, "%s%d", GeometryTGeo::getEC0ModulePattern(), mLayerNumber); + auto* modVol = new TGeoVolume(volumeName, module, medAir); + modVol->SetVisibility(kTRUE); + + // Now build up the module + ypos = -module->GetDY() + modPlate->GetDY(); + modVol->AddNode(modPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (modPlate->GetDY() + glue->GetDY()); + modVol->AddNode(glueVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = -module->GetDX() + xchip; + ypos += (glue->GetDY() + ychip); + for (Int_t k = 0; k < sOBChipsPerRow; k++) { // put 7x2 chip into one module + zpos = -module->GetDZ() + zchip + k * (2 * zchip + zGap); + modVol->AddNode(chipVol, 2 * k, new TGeoTranslation(xpos, ypos, zpos)); + modVol->AddNode(chipVol, 2 * k + 1, + new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, 180, 180))); + mHierarchy[kChip] += 2; + } + + ypos += (ychip + flexAl->GetDY()); + modVol->AddNode(flexAlVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (flexAl->GetDY() + flexKap->GetDY()); + modVol->AddNode(flexKapVol, 1, new TGeoTranslation(0, ypos, 0)); + + // Done, return the module + return modVol; +} + +Double_t V1Layer::radiusOmTurboContainer() +{ + Double_t rr, delta, z, lstav, rstav; + + if (mStaveThickness > 89.) { // Very big angle: avoid overflows since surely + return -1; // the radius from lower vertex is the right value + } + + rstav = mLayerRadius + 0.5 * mStaveThickness; + delta = (0.5 * mStaveThickness) / cosD(mStaveTilt); + z = (0.5 * mStaveThickness) * tanD(mStaveTilt); + + rr = rstav - delta; + lstav = (0.5 * mStaveWidth) - z; + + if ((rr * sinD(mStaveTilt) < lstav)) { + return (rr * cosD(mStaveTilt)); + } else { + return -1; + } +} + +void V1Layer::setNumberOfUnits(Int_t u) +{ + if (mLayerNumber < sNumberOmInnerLayers) { + mNumberOfChips = u; + } else { + mNumberOfModules = u; + mNumberOfChips = sOBChipsPerRow; + } +} + +void V1Layer::setStaveTilt(const Double_t t) +{ + if (mIsTurbo) { + mStaveTilt = t; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +void V1Layer::setStaveWidth(const Double_t w) +{ + if (mIsTurbo) { + mStaveWidth = w; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +TGeoArb8* V1Layer::createStaveSide(const char* name, Double_t dz, Double_t angle, + Double_t xSign, Double_t L, Double_t H, Double_t l) +{ + // Create one half of the V shape corner of CF stave + + auto* cmStavSide = new TGeoArb8(dz); + cmStavSide->SetName(name); + + // Points must be in clockwise order + cmStavSide->SetVertex(0, 0, 0); + cmStavSide->SetVertex(2, xSign * (L * TMath::Sin(angle) - l * TMath::Cos(angle)), + -L * TMath::Cos(angle) - l * TMath::Sin(angle)); + cmStavSide->SetVertex(4, 0, 0); + cmStavSide->SetVertex(6, xSign * (L * TMath::Sin(angle) - l * TMath::Cos(angle)), + -L * TMath::Cos(angle) - l * TMath::Sin(angle)); + if (xSign < 0) { + cmStavSide->SetVertex(1, 0, -H); + cmStavSide->SetVertex(3, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + cmStavSide->SetVertex(5, 0, -H); + cmStavSide->SetVertex(7, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + } else { + cmStavSide->SetVertex(1, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + cmStavSide->SetVertex(3, 0, -H); + cmStavSide->SetVertex(5, xSign * L * TMath::Sin(angle), -L * TMath::Cos(angle)); + cmStavSide->SetVertex(7, 0, -H); + } + return cmStavSide; +} + +TGeoCombiTrans* V1Layer::createCombiTrans(const char* name, Double_t dy, Double_t dz, + Double_t dphi, Bool_t planeSym) +{ + TGeoTranslation t1(dy * cosD(90. + dphi), dy * sinD(90. + dphi), dz); + TGeoRotation r1("", 0., 0., dphi); + TGeoRotation r2("", 90, 180, -90 - dphi); + + auto* combiTrans1 = new TGeoCombiTrans(name); + combiTrans1->SetTranslation(t1); + if (planeSym) { + combiTrans1->SetRotation(r1); + } else { + combiTrans1->SetRotation(r2); + } + return combiTrans1; +} + +void V1Layer::addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx, Double_t dy, + Double_t dz) const +{ + // Add a dx,dy,dz translation to the initial TGeoCombiTrans + const Double_t* vect = ct->GetTranslation(); + Double_t newVect[3] = {vect[0] + dx, vect[1] + dy, vect[2] + dz}; + ct->SetTranslation(newVect); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V3Layer.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V3Layer.cxx new file mode 100644 index 0000000000000..b4b5e553a1816 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V3Layer.cxx @@ -0,0 +1,3768 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 V3Layer.cxx +/// \brief Implementation of the V3Layer class +/// \author Mario Sitta +/// \author Chinorat Kobdaj (kobdaj@g.sut.ac.th) + +#include "EC0Simulation/V3Layer.h" +#include "ECLayersBase/GeometryTGeo.h" +#include "EC0Simulation/Detector.h" +#include "EndCapsSimulation/AlpideChip.h" +#include "EndCapsBase/SegmentationAlpide.h" + +#include "FairLogger.h" // for LOG + +#include // for TGeoArb8 +#include // for TGeoBBox +#include // for TGeoConeSeg, TGeoCone +#include // for TGeoPcon +#include // for TGeoManager, gGeoManager +#include // for TGeoCombiTrans, TGeoRotation, etc +#include // for TGeoTrd1 +#include // for TGeoTube, TGeoTubeSeg +#include // for TGeoVolume, TGeoVolumeAssembly +#include // for TGeoXtru +#include // for TGeoCompositeShape +#include "TMathBase.h" // for Abs +#include // for Sin, RadToDeg, DegToRad, Cos, Tan, etc + +#include // for snprintf + +class TGeoMedium; + +using namespace TMath; +using namespace o2::ecl; +using namespace o2::endcaps; +using AlpideChip = o2::endcaps::AlpideChip; + +// General Parameters +const Int_t V3Layer::sNumberOfInnerLayers = 3; + +// Inner Barrel Parameters +const Int_t V3Layer::sIBChipsPerRow = 9; +const Int_t V3Layer::sIBNChipRows = 1; +const Double_t V3Layer::sIBChipZGap = 150.0 * sMicron; + +const Double_t V3Layer::sIBModuleZLength = 27.12 * sCm; +const Double_t V3Layer::sIBFPCWiderXPlus = 850.0 * sMicron; +const Double_t V3Layer::sIBFPCWiderXNeg = 300.0 * sMicron; +const Double_t V3Layer::sIBFlexCableAlThick = 25.0 * sMicron; +const Double_t V3Layer::sIBFPCAlGNDWidth = (4.1 + 11.15) * sMm; +const Double_t V3Layer::sIBFPCAlAnodeWidth1 = 13.0 * sMm; +const Double_t V3Layer::sIBFPCAlAnodeWidth2 = 14.7 * sMm; +const Double_t V3Layer::sIBFlexCableKapThick = 75.0 * sMicron; +const Double_t V3Layer::sIBFlexCablePolyThick = 20.0 * sMicron; +const Double_t V3Layer::sIBFlexCapacitorXWid = 0.2 * sMm; +const Double_t V3Layer::sIBFlexCapacitorYHi = 0.2 * sMm; +const Double_t V3Layer::sIBFlexCapacitorZLen = 0.4 * sMm; +const Double_t V3Layer::sIBColdPlateWidth = 15.4 * sMm; +const Double_t V3Layer::sIBColdPlateZLen = 290.0 * sMm; +const Double_t V3Layer::sIBGlueThick = 50.0 * sMicron; +const Double_t V3Layer::sIBCarbonFleeceThick = 20.0 * sMicron; +const Double_t V3Layer::sIBCarbonPaperThick = 30.0 * sMicron; +const Double_t V3Layer::sIBCarbonPaperWidth = 12.5 * sMm; +const Double_t V3Layer::sIBCarbonPaperZLen = 280.0 * sMm; +const Double_t V3Layer::sIBK13D2UThick = 70.0 * sMicron; +const Double_t V3Layer::sIBCoolPipeInnerD = 1.024 * sMm; +const Double_t V3Layer::sIBCoolPipeThick = 25.4 * sMicron; +const Double_t V3Layer::sIBCoolPipeXDist = 5.0 * sMm; +const Double_t V3Layer::sIBCoolPipeZLen = 302.0 * sMm; +const Double_t V3Layer::sIBTopVertexWidth1 = 0.258 * sMm; +const Double_t V3Layer::sIBTopVertexWidth2 = 0.072 * sCm; +const Double_t V3Layer::sIBTopVertexHeight = 0.04 * sCm; +const Double_t V3Layer::sIBTopVertexAngle = 60.0; // Deg +const Double_t V3Layer::sIBSideVertexWidth = 0.05 * sCm; +const Double_t V3Layer::sIBSideVertexHeight = 0.074 * sCm; +const Double_t V3Layer::sIBTopFilamentSide = 0.04 * sCm; +const Double_t V3Layer::sIBTopFilamentAlpha = 109.8; // Deg +const Double_t V3Layer::sIBTopFilamentInterZ = 15.0 * sMm; +const Double_t V3Layer::sIBEndSupportThick = 0.149 * sMm; +const Double_t V3Layer::sIBEndSupportZLen = 2.5 * sMm; +const Double_t V3Layer::sIBEndSupportXUp = 1.0 * sMm; +const Double_t V3Layer::sIBEndSupportOpenPhi = 120.0; // Deg + +const Double_t V3Layer::sIBConnectorXWidth = 10.0 * sMm; +const Double_t V3Layer::sIBConnectorYTot = 4.7 * sMm; +const Double_t V3Layer::sIBConnectBlockZLen = 16.5 * sMm; +const Double_t V3Layer::sIBConnBodyYHeight = 2.5 * sMm; +const Double_t V3Layer::sIBConnTailYShift = 0.9 * sMm; +const Double_t V3Layer::sIBConnTailYMid = 2.5 * sMm; +const Double_t V3Layer::sIBConnTailZLen = 2.5 * sMm; +const Double_t V3Layer::sIBConnTailOpenPhi = 120.0; // Deg +const Double_t V3Layer::sIBConnRoundHoleD = 2.0 * sMm; +const Double_t V3Layer::sIBConnRoundHoleZ = (9.0 - 4.0) * sMm; +const Double_t V3Layer::sIBConnSquareHoleX = 2.0 * sMm; +const Double_t V3Layer::sIBConnSquareHoleZ = 2.8 * sMm; +const Double_t V3Layer::sIBConnSquareHoleZPos = 9.0 * sMm; +const Double_t V3Layer::sIBConnInsertHoleD = 2.0 * sMm; +const Double_t V3Layer::sIBConnInsertHoleZPos = 9.0 * sMm; +const Double_t V3Layer::sIBConnTubeHole1D = 1.6 * sMm; +const Double_t V3Layer::sIBConnTubeHole1ZLen = 3.0 * sMm; +const Double_t V3Layer::sIBConnTubeHole1ZLen2 = 2.7 * sMm; +const Double_t V3Layer::sIBConnTubeHole2D = 1.2 * sMm; +const Double_t V3Layer::sIBConnTubeHole3XPos = 1.0 * sMm; +const Double_t V3Layer::sIBConnTubeHole3ZPos = 14.5 * sMm; +const Double_t V3Layer::sIBConnTubesXDist = 5.0 * sMm; +const Double_t V3Layer::sIBConnTubesYPos = 1.25 * sMm; +const Double_t V3Layer::sIBConnInsertD = 2.0 * sMm; +const Double_t V3Layer::sIBConnInsertHeight = 2.3 * sMm; +const Double_t V3Layer::sIBConnSideHole1D = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole1YPos = 1.25 * sMm; +const Double_t V3Layer::sIBConnSideHole1ZPos = 11.5 * sMm; +const Double_t V3Layer::sIBConnSideHole1XWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2YPos = 1.25 * sMm; +const Double_t V3Layer::sIBConnSideHole2ZPos = 11.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2XWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2YWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnSideHole2ZWid = 1.0 * sMm; +const Double_t V3Layer::sIBConnectAFitExtD = 1.65 * sMm; +const Double_t V3Layer::sIBConnectAFitIntD = 1.19 * sMm; +const Double_t V3Layer::sIBConnectAFitZLen = 12.5 * sMm; +const Double_t V3Layer::sIBConnectAFitZOut = 10.0 * sMm; +const Double_t V3Layer::sIBConnPlugInnerD = 0.8 * sMm; +const Double_t V3Layer::sIBConnPlugTotLen = 1.7 * sMm; +const Double_t V3Layer::sIBConnPlugInnerLen = 1.0 * sMm; + +const Double_t V3Layer::sIBStaveHeight = 0.5 * sCm; + +// Outer Barrel Parameters +const Int_t V3Layer::sOBChipsPerRow = 7; +const Int_t V3Layer::sOBNChipRows = 2; + +const Double_t V3Layer::sOBChipThickness = 100.0 * sMicron; + +const Double_t V3Layer::sOBHalfStaveWidth = 3.01 * sCm; +const Double_t V3Layer::sOBModuleGap = 200.0 * sMicron; +const Double_t V3Layer::sOBChipXGap = 150.0 * sMicron; +const Double_t V3Layer::sOBChipZGap = 150.0 * sMicron; +const Double_t V3Layer::sOBFlexCableXWidth = 3.3 * sCm; +const Double_t V3Layer::sOBFlexCableAlThick = 0.005 * sCm; +const Double_t V3Layer::sOBFlexCableKapThick = 75.0 * sMicron; +const Double_t V3Layer::sOBFPCSoldMaskThick = 30.0 * sMicron; +const Double_t V3Layer::sOBFPCCopperThick = 18.0 * sMicron; +const Double_t V3Layer::sOBFPCCuAreaFracGnd = 0.954; // F.Benotto +const Double_t V3Layer::sOBFPCCuAreaFracSig = 0.617; // F.Benotto +const Double_t V3Layer::sOBGlueFPCThick = 50 * sMicron; +const Double_t V3Layer::sOBGlueColdPlThick = 80 * sMicron; +const Double_t V3Layer::sOBPowerBusXWidth = 3.04 * sCm; +const Double_t V3Layer::sOBPowerBusAlThick = 100.0 * sMicron; +const Double_t V3Layer::sOBPowerBusAlFrac = 0.90; // L.Greiner +const Double_t V3Layer::sOBPowerBusDielThick = 50.0 * sMicron; +const Double_t V3Layer::sOBPowerBusKapThick = 27.5 * sMicron; +const Double_t V3Layer::sOBBiasBusXWidth = 7.7 * sMm; +const Double_t V3Layer::sOBBiasBusAlThick = 25.0 * sMicron; +const Double_t V3Layer::sOBBiasBusAlFrac = 0.90; // L.Greiner +const Double_t V3Layer::sOBBiasBusDielThick = 50.0 * sMicron; +const Double_t V3Layer::sOBBiasBusKapThick = 25.0 * sMicron; +const Double_t V3Layer::sOBColdPlateXWidth = 3.04 * sCm; +const Double_t V3Layer::sOBColdPlateZLenML = 87.55 * sCm; +const Double_t V3Layer::sOBColdPlateZLenOL = 150.15 * sCm; +const Double_t V3Layer::sOBColdPlateThick = 0.012 * sCm; +const Double_t V3Layer::sOBHalfStaveYPos = 2.067 * sCm; +const Double_t V3Layer::sOBHalfStaveYTrans = 1.76 * sMm; +const Double_t V3Layer::sOBHalfStaveXOverlap = 7.2 * sMm; +const Double_t V3Layer::sOBGraphiteFoilThick = 30.0 * sMicron; +const Double_t V3Layer::sOBCarbonFleeceThick = 20.0 * sMicron; +const Double_t V3Layer::sOBCoolTubeInnerD = 2.05 * sMm; +const Double_t V3Layer::sOBCoolTubeThick = 32.0 * sMicron; +const Double_t V3Layer::sOBCoolTubeXDist = 10.0 * sMm; + +const Double_t V3Layer::sOBCPConnectorXWidth = 16.0 * sMm; +const Double_t V3Layer::sOBCPConnBlockZLen = 15.0 * sMm; +const Double_t V3Layer::sOBCPConnBlockYHei = 3.6 * sMm; +const Double_t V3Layer::sOBCPConnHollowZLen = 3.0 * sMm; +const Double_t V3Layer::sOBCPConnHollowYHei = 0.9 * sMm; +const Double_t V3Layer::sOBCPConnSquareHoleX = 4.0 * sMm; +const Double_t V3Layer::sOBCPConnSquareHoleZ = 5.0 * sMm; +const Double_t V3Layer::sOBCPConnSqrHoleZPos = 4.0 * sMm; +const Double_t V3Layer::sOBCPConnSqrInsertRZ = 3.5 * sMm; +const Double_t V3Layer::sOBCPConnRoundHoleD = 4.0 * sMm; +const Double_t V3Layer::sOBCPConnRndHoleZPos = 7.0 * sMm; +const Double_t V3Layer::sOBCPConnTubesXDist = 10.0 * sMm; +const Double_t V3Layer::sOBCPConnTubesYPos = 1.8 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole1D = 2.6 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole1Z = 3.5 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole2D = 2.2 * sMm; +const Double_t V3Layer::sOBCPConnFitHoleD = 2.8 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole3XP = 1.0 * sMm; +const Double_t V3Layer::sOBCPConnTubeHole3ZP = 2.0 * sMm; +const Double_t V3Layer::sOBCPConnInstZThick = 1.0 * sMm; +const Double_t V3Layer::sOBCPConnInsertYHei = 3.4 * sMm; +const Double_t V3Layer::sOBCPConnAFitExtD = 2.8 * sMm; +const Double_t V3Layer::sOBCPConnAFitThick = 0.3 * sMm; +const Double_t V3Layer::sOBCPConnAFitZLen = 17.0 * sMm; +const Double_t V3Layer::sOBCPConnAFitZIn = 3.0 * sMm; +const Double_t V3Layer::sOBCPConnPlugInnerD = 0.8 * sMm; +const Double_t V3Layer::sOBCPConnPlugTotLen = 1.7 * sMm; +const Double_t V3Layer::sOBCPConnPlugThick = 0.5 * sMm; + +const Double_t V3Layer::sOBSpaceFrameZLen[2] = {900.0 * sMm, 1526.0 * sMm}; +const Int_t V3Layer::sOBSpaceFrameNUnits[2] = {23, 39}; +const Double_t V3Layer::sOBSpaceFrameUnitLen = 39.1 * sMm; +const Double_t V3Layer::sOBSpaceFrameWidth = 42.44 * sMm; +const Double_t V3Layer::sOBSpaceFrameHeight = 36.45 * sMm; +const Double_t V3Layer::sOBSpaceFrameTopVL = 4.0 * sMm; +const Double_t V3Layer::sOBSpaceFrameTopVH = 0.35 * sMm; +const Double_t V3Layer::sOBSpaceFrameSideVL = 4.5 * sMm; +const Double_t V3Layer::sOBSpaceFrameSideVH = 0.35 * sMm; +const Double_t V3Layer::sOBSpaceFrameVAlpha = 60.0; // deg +const Double_t V3Layer::sOBSpaceFrameVBeta = 68.0; // deg +const Double_t V3Layer::sOBSFrameBaseRibDiam = 1.33 * sMm; +const Double_t V3Layer::sOBSFrameBaseRibPhi = 54.0; // deg +const Double_t V3Layer::sOBSFrameSideRibDiam = 1.25 * sMm; +const Double_t V3Layer::sOBSFrameSideRibPhi = 70.0; // deg +const Double_t V3Layer::sOBSFrameULegLen = 14.2 * sMm; +const Double_t V3Layer::sOBSFrameULegWidth = 1.5 * sMm; +const Double_t V3Layer::sOBSFrameULegHeight1 = 2.7 * sMm; +const Double_t V3Layer::sOBSFrameULegHeight2 = 5.0 * sMm; +const Double_t V3Layer::sOBSFrameULegThick = 0.3 * sMm; +const Double_t V3Layer::sOBSFrameULegXPos = 12.9 * sMm; +const Double_t V3Layer::sOBSFrameConnWidth = 42.0 * sMm; +const Double_t V3Layer::sOBSFrameConnTotLen = 29.0 * sMm; +const Double_t V3Layer::sOBSFrameConnTotHei = 4.8 * sMm; +const Double_t V3Layer::sOBSFrameConnTopLen = 14.0 * sMm; +const Double_t V3Layer::sOBSFrameConnInsWide = 36.869 * sMm; +const Double_t V3Layer::sOBSFrameConnInsBase = 39.6 * sMm; +const Double_t V3Layer::sOBSFrameConnInsHei = 2.8 * sMm; +const Double_t V3Layer::sOBSFrameConnHoleZPos = 7.0 * sMm; +const Double_t V3Layer::sOBSFrameConnHoleZDist = 15.0 * sMm; +const Double_t V3Layer::sOBSFrameConnTopHoleD = 3.0 * sMm; +const Double_t V3Layer::sOBSFrConnTopHoleXDist = 24.0 * sMm; +const Double_t V3Layer::sOBSFrameConnAHoleWid = 4.0 * sMm; +const Double_t V3Layer::sOBSFrameConnAHoleLen = 5.0 * sMm; +const Double_t V3Layer::sOBSFrConnASideHoleD = 3.0 * sMm; +const Double_t V3Layer::sOBSFrConnASideHoleL = 2.5 * sMm; +const Double_t V3Layer::sOBSFrConnASideHoleY = 2.3 * sMm; +const Double_t V3Layer::sOBSFrameConnCHoleZPos = 3.0 * sMm; +const Double_t V3Layer::sOBSFrConnCHoleXDist = 32.0 * sMm; +const Double_t V3Layer::sOBSFrConnCTopHoleD = 4.0 * sMm; +const Double_t V3Layer::sOBSFrameConnInsHoleD = 5.0 * sMm; +const Double_t V3Layer::sOBSFrameConnInsHoleX = 25.8 * sMm; + +ClassImp(V3Layer); + +#define SQ(A) (A) * (A) + +V3Layer::V3Layer() + : V11Geometry(), + mLayerNumber(0), + mPhi0(0), + mLayerRadius(0), + mSensorThickness(0), + mChipThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(0), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy), + mAddGammaConv(kFALSE), + mGammaConvDiam(0), + mGammaConvXPos(0), + mIBModuleZLength(0), + mOBModuleZLength(0) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V3Layer::V3Layer(Int_t lay, Bool_t turbo, Int_t debug) + : V11Geometry(debug), + mLayerNumber(lay), + mPhi0(0), + mLayerRadius(0), + mSensorThickness(0), + mChipThickness(0), + mStaveWidth(0), + mStaveTilt(0), + mNumberOfStaves(0), + mNumberOfModules(0), + mNumberOfChips(0), + mChipTypeID(0), + mIsTurbo(turbo), + mBuildLevel(0), + mStaveModel(Detector::kIBModelDummy), + mAddGammaConv(kFALSE), + mGammaConvDiam(0), + mGammaConvXPos(0), + mIBModuleZLength(0), + mOBModuleZLength(0) +{ + for (int i = kNHLevels; i--;) { + mHierarchy[i] = 0; + } +} + +V3Layer::~V3Layer() = default; + +void V3Layer::createLayer(TGeoVolume* motherVolume) +{ + const Int_t nameLen = 30; + char volumeName[nameLen]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper parameters + if (mLayerRadius <= 0) { + LOG(FATAL) << "Wrong layer radius " << mLayerRadius; + } + + if (mNumberOfStaves <= 0) { + LOG(FATAL) << "Wrong number of staves " << mNumberOfStaves; + } + + if (mNumberOfChips <= 0) { + LOG(FATAL) << "Wrong number of chips " << mNumberOfChips; + } + + if (mLayerNumber >= sNumberOfInnerLayers && mNumberOfModules <= 0) { + LOG(FATAL) << "Wrong number of modules " << mNumberOfModules; + } + + if (mChipThickness <= 0) { + LOG(FATAL) << "Chip thickness wrong or not set " << mChipThickness; + } + + if (mSensorThickness <= 0) { + LOG(FATAL) << "Sensor thickness wrong or not set " << mSensorThickness; + } + + if (mSensorThickness > mChipThickness) { + LOG(FATAL) << "Sensor thickness " << mSensorThickness << " is greater than chip thickness " << mChipThickness; + } + + // If a Turbo layer is requested, do it and exit + if (mIsTurbo) { + createLayerTurbo(motherVolume); + return; + } + + // First create the stave container + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // mStaveWidth = mLayerRadius*Tan(alpha); + + snprintf(volumeName, nameLen, "%s%d", GeometryTGeo::getEC0LayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + + // layerVolume->SetVisibility(kFALSE); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius + (static_cast(stavVol->GetShape()))->GetDY(); + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode(stavVol, j, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + // geometry is served + return; +} + +void V3Layer::createLayerTurbo(TGeoVolume* motherVolume) +{ + const Int_t nameLen = 30; + char volumeName[nameLen]; + Double_t xpos, ypos, zpos; + Double_t alpha; + + // Check if the user set the proper (remaining) parameters + if (mStaveWidth <= 0) { + LOG(FATAL) << "Wrong stave width " << mStaveWidth; + } + + if (Abs(mStaveTilt) > 45) { + LOG(WARNING) << "Stave tilt angle (" << mStaveTilt << ") greater than 45deg"; + } + + snprintf(volumeName, nameLen, "%s%d", GeometryTGeo::getEC0LayerPattern(), mLayerNumber); + TGeoVolume* layerVolume = new TGeoVolumeAssembly(volumeName); + layerVolume->SetUniqueID(mChipTypeID); + layerVolume->SetVisibility(kTRUE); + layerVolume->SetLineColor(1); + TGeoVolume* stavVol = createStave(); + + // Now build up the layer + alpha = 360. / mNumberOfStaves; + Double_t r = mLayerRadius /* +chip thick ?! */; + for (Int_t j = 0; j < mNumberOfStaves; j++) { + Double_t phi = j * alpha + mPhi0; + xpos = r * cosD(phi); // r*sinD(-phi); + ypos = r * sinD(phi); // r*cosD(-phi); + zpos = 0.; + phi += 90; + layerVolume->AddNode(stavVol, j, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi - mStaveTilt, 0, 0))); + } + + // Finally put everything in the mother volume + motherVolume->AddNode(layerVolume, 1, nullptr); + + return; +} + +TGeoVolume* V3Layer::createStave(const TGeoManager* /*mgr*/) +{ + // + // Creates the actual Stave + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 22 Jun 2011 Mario Sitta + // Updated: 18 Dec 2013 Mario Sitta Handle IB and OB + // Updated: 12 Jan 2015 Mario Sitta Fix overlap with new OB space frame + // (by moving the latter, not the sensors to avoid + // spoiling their position in space) + // Updated: 03 Mar 2015 Mario Sitta Fix chip position + // Updated: 16 Mar 2017 Mario Sitta AliceO2 version + // Updated: 10 Jan 2018 Mario Sitta Compute all dimensions using + // AlpideChip as basis + // + + const Int_t nameLen = 30; + char volumeName[nameLen]; + + Double_t xpos, ypos; + Double_t alpha; + + // First create all needed shapes + alpha = (360. / (2 * mNumberOfStaves)) * DegToRad(); + + // The stave + snprintf(volumeName, nameLen, "%s%d", GeometryTGeo::getEC0StavePattern(), mLayerNumber); + TGeoVolume* staveVol = new TGeoVolumeAssembly(volumeName); + staveVol->SetVisibility(kTRUE); + staveVol->SetLineColor(2); + + TGeoVolume* mechStaveVol = nullptr; + + // Now build up the stave + if (mLayerNumber < sNumberOfInnerLayers) { + TGeoVolume* modVol = createStaveInnerB(); + ypos = (static_cast(modVol->GetShape()))->GetDY() - mChipThickness; // = 0 if not kIBModel4 + staveVol->AddNode(modVol, 0, new TGeoTranslation(0, ypos, 0)); + mHierarchy[kHalfStave] = 1; + + // Mechanical stave structure + mechStaveVol = createStaveStructInnerB(); + if (mechStaveVol) { + ypos = (static_cast(modVol->GetShape()))->GetDY() - ypos; + if (mStaveModel != Detector::kIBModel4) + ypos += (static_cast(mechStaveVol->GetShape()))->GetDY(); + staveVol->AddNode(mechStaveVol, 1, new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", 0, 0, 180))); + } + } else { + TGeoVolume* hstaveVol = createStaveOuterB(); + if (mStaveModel == Detector::kOBModel0) { // Create simplified stave struct as in v0 + staveVol->AddNode(hstaveVol, 0); + mHierarchy[kHalfStave] = 1; + } else { // (if mStaveModel) Create new stave struct as in TDR + xpos = (static_cast(hstaveVol->GetShape()))->GetDX() - sOBHalfStaveXOverlap / 2; + // ypos is now a parameter to avoid HS displacement wrt nominal radii + ypos = sOBHalfStaveYPos; + staveVol->AddNode(hstaveVol, 0, new TGeoTranslation(-xpos, ypos, 0)); + staveVol->AddNode(hstaveVol, 1, new TGeoTranslation(xpos, ypos + sOBHalfStaveYTrans, 0)); + mHierarchy[kHalfStave] = 2; // RS + mechStaveVol = createSpaceFrameOuterB(); + + if (mechStaveVol) { + if (mBuildLevel < 6) // Carbon + staveVol->AddNode(mechStaveVol, 1, + new TGeoCombiTrans(0, -sOBSFrameULegHeight1, 0, new TGeoRotation("", 180, 0, 0))); + } + } + } + + staveVol->GetShape()->ComputeBBox(); // RS: enfore recompting of BBox + + // Done, return the stave + return staveVol; +} + +TGeoVolume* V3Layer::createStaveInnerB(const TGeoManager* mgr) +{ + Double_t xmod, ymod, zmod; + const Int_t nameLen = 30; + char volumeName[nameLen]; + + // First we create the module (i.e. the HIC with 9 chips) + TGeoVolume* moduleVol = createModuleInnerB(); + + // Then we create the fake halfstave and the actual stave + xmod = (static_cast(moduleVol->GetShape()))->GetDX(); + ymod = (static_cast(moduleVol->GetShape()))->GetDY(); + zmod = (static_cast(moduleVol->GetShape()))->GetDZ(); + + TGeoBBox* hstave = new TGeoBBox(xmod, ymod, zmod); + + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + snprintf(volumeName, nameLen, "%s%d", GeometryTGeo::getEC0HalfStavePattern(), mLayerNumber); + TGeoVolume* hstaveVol = new TGeoVolume(volumeName, hstave, medAir); + + // Finally build it up + hstaveVol->AddNode(moduleVol, 0); + mHierarchy[kModule] = 1; + + // Done, return the stave structure + return hstaveVol; +} + +TGeoVolume* V3Layer::createModuleInnerB(const TGeoManager* mgr) +{ + Double_t xtot, ytot, ztot, xchip, zchip, ymod; + Double_t xpos, ypos, zpos; + Bool_t dummyChip; + const Int_t nameLen = 30; + char chipName[nameLen], sensName[nameLen], volumeName[nameLen]; + + // For material budget studies + if (mBuildLevel < 6) + dummyChip = kFALSE; // will be made of Si + else + dummyChip = kTRUE; // will be made of Air + + // First create the single chip + snprintf(chipName, nameLen, "%s%d", GeometryTGeo::getEC0ChipPattern(), mLayerNumber); + snprintf(sensName, nameLen, "%s%d", GeometryTGeo::getEC0SensorPattern(), mLayerNumber); + + ymod = 0.5 * mChipThickness; + + TGeoVolume* chipVol = AlpideChip::createChip(ymod, mSensorThickness / 2, chipName, sensName, dummyChip); + + xchip = (static_cast(chipVol->GetShape()))->GetDX(); + zchip = (static_cast(chipVol->GetShape()))->GetDZ(); + + mIBModuleZLength = 2 * zchip * sIBChipsPerRow + (sIBChipsPerRow - 1) * sIBChipZGap; + + // Then create the Glue, the Kapton and the two Aluminum cables + xtot = xchip + (sIBFPCWiderXPlus + sIBFPCWiderXNeg) / 2; + ztot = mIBModuleZLength / 2; + + TGeoBBox* glue = new TGeoBBox(xchip, sIBGlueThick / 2, ztot); + TGeoBBox* kapCable = new TGeoBBox(xtot, sIBFlexCableKapThick / 2, ztot); + + TGeoVolume* aluGndCableVol = createIBFPCAlGnd(xtot, ztot); + TGeoVolume* aluAnodeCableVol = createIBFPCAlAnode(xtot, ztot); + + // Finally create the module and populate it with the chips + // (and the FPC Kapton and Aluminum in the most recent IB model) + Double_t ygnd = (static_cast(aluGndCableVol->GetShape()))->GetDY(); + Double_t yano = (static_cast(aluAnodeCableVol->GetShape()))->GetDY(); + + ytot = ymod; + if (mStaveModel == Detector::kIBModel4) + ytot += (sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitorYHi / 2); + + TGeoBBox* module = new TGeoBBox(xtot, ytot, ztot); + + // Now the volumes + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE_IBFPC$"); + + snprintf(volumeName, nameLen, "%s%d", GeometryTGeo::getEC0ModulePattern(), mLayerNumber); + TGeoVolume* modVol = new TGeoVolume(volumeName, module, medAir); + + TGeoVolume* glueVol = new TGeoVolume("FPCGlue", glue, medGlue); + glueVol->SetLineColor(kBlack); + glueVol->SetFillColor(kBlack); + + TGeoVolume* kapCableVol = new TGeoVolume("FPCKapton", kapCable, medKapton); + kapCableVol->SetLineColor(kBlue); + kapCableVol->SetFillColor(kBlue); + + // Build up the module + // Chips are rotated by 180deg around Y axis + // in order to have the correct X and Z axis orientation + xpos = -xtot + (static_cast(chipVol->GetShape()))->GetDX() + sIBFPCWiderXNeg; + ypos = -ytot + ymod; // = 0 if not kIBModel4 + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + zpos = ztot - j * (2 * zchip + sIBChipZGap) - zchip; + modVol->AddNode(chipVol, j, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 180, 180))); + mHierarchy[kChip]++; + } + + if (mStaveModel == Detector::kIBModel4) { + ypos += (ymod + glue->GetDY()); + if (mBuildLevel < 2) // Glue + modVol->AddNode(glueVol, 1, new TGeoTranslation(xpos, ypos, 0)); + ypos += glue->GetDY(); + + if (mBuildLevel < 4) { // Kapton + ypos += ygnd; + modVol->AddNode(aluGndCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (ygnd + kapCable->GetDY()); + modVol->AddNode(kapCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (kapCable->GetDY() + yano); + modVol->AddNode(aluAnodeCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += yano; + } + } + + // Add the capacitors + createIBCapacitors(modVol, zchip, ypos); + + // Done, return the module + return modVol; +} + +void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yzero, const TGeoManager* mgr) +{ + // + // Adds the capacitors to the IB FPC + // + // Created: 13 Feb 2018 Mario Sitta + // Updated: 03 Apr 2019 Mario Sitta Fix positions (180' rotation) + // + + // Position of the various capacitors (A.Junique private communication + // where: X_capacitor = Z_module , Y_capacitor = X_module) + // Capacitors (different groups) + const Double_t xGroup1A = 4265.9 * sMicron; + const Double_t zGroup1A[2] = {-7142.9 * sMicron, 7594.1 * sMicron}; + const Double_t xGroup1B = 690.9 * sMicron; + const Double_t zGroup1B = -7142.9 * sMicron; + const Double_t xGroup2 = 6300.0 * sMicron; + const Double_t zGroup2 = 15075.0 * sMicron; + const Double_t xGroup3 = 5575.0 * sMicron; + const Double_t zGroup3 = 131900.0 * sMicron; + const Double_t xGroup4[2] = {5600.0 * sMicron, 5575.0 * sMicron}; + const Double_t zGroup4[sIBChipsPerRow] = {275.0 * sMicron, 250.0 * sMicron, 275.0 * sMicron, + 250.0 * sMicron, 250.0 * sMicron, 300.0 * sMicron, + 250.0 * sMicron, 300.0 * sMicron, 250.0 * sMicron}; + const Int_t nGroup5A = 5, nGroup5B = 4; + const Double_t xGroup5A[2] = {1400.0 * sMicron, 1350.0 * sMicron}; + const Double_t zGroup5A[nGroup5A] = {-112957.5 * sMicron, -82854.5 * sMicron, 7595.5 * sMicron, 37745.5 * sMicron, + 128194.1 * sMicron}; + const Double_t xGroup5B = 1100.0 * sMicron; + const Double_t zGroup5B[nGroup5B] = {-51525.0 * sMicron, -21375.0 * sMicron, 69075.0 * sMicron, 99225.0 * sMicron}; + // Resistors + const Int_t nResist = 2; + const Double_t xResist = -7975.0 * sMicron; + const Double_t zResist[nResist] = {114403.0 * sMicron, 119222.0 * sMicron}; + + Double_t xpos, ypos, zpos; + Int_t nCapacitors; + + TGeoVolume *capacitor, *resistor; + + // Check whether we already have the volume, otherwise create it + // (so as to avoid creating multiple copies of the very same volume + // for each layer) + capacitor = mgr->GetVolume("IBFPCCapacitor"); + + if (!capacitor) { + TGeoBBox* capsh = new TGeoBBox(sIBFlexCapacitorXWid / 2, sIBFlexCapacitorYHi / 2, sIBFlexCapacitorZLen / 2); + + TGeoMedium* medCeramic = mgr->GetMedium("EC0_CERAMIC$"); + + capacitor = new TGeoVolume("IBFPCCapacitor", capsh, medCeramic); + capacitor->SetLineColor(kBlack); + capacitor->SetFillColor(kBlack); + + TGeoBBox* ressh = new TGeoBBox(sIBFlexCapacitorXWid / 2, // Resistors have + sIBFlexCapacitorYHi / 2, // the same dim's + sIBFlexCapacitorZLen / 2); // as capacitors + + resistor = new TGeoVolume("IBFPCResistor", ressh, medCeramic); + resistor->SetLineColor(kBlack); + resistor->SetFillColor(kBlack); + } else { // Volumes already defined, get them + resistor = mgr->GetVolume("IBFPCResistor"); + } + + // Place all the capacitors (they are really a lot...) + ypos = yzero + sIBFlexCapacitorYHi / 2; + + xpos = xGroup1A; + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1A[0]; + modvol->AddNode(capacitor, 2 * j + 1, new TGeoTranslation(-xpos, ypos, -zpos)); + zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1A[1]; + modvol->AddNode(capacitor, 2 * j + 2, new TGeoTranslation(-xpos, ypos, -zpos)); + } + + nCapacitors = 2 * sIBChipsPerRow; + xpos = xGroup1B; + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1B; + modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + } + + nCapacitors += sIBChipsPerRow; + xpos = xGroup2; + // We have only 8 in these group, missing the central one + for (Int_t j = 0; j < sIBChipsPerRow - 1; j++) { + zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup2; + modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + } + + nCapacitors += (sIBChipsPerRow - 1); + xpos = xGroup3; + zpos = zGroup3; + modvol->AddNode(capacitor, 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + + nCapacitors++; + for (Int_t j = 0; j < sIBChipsPerRow; j++) { + if (j == (sIBChipsPerRow - 1)) + xpos = xGroup4[1]; + else + xpos = xGroup4[0]; + zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup4[j]; + modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + } + + nCapacitors += sIBChipsPerRow; + for (Int_t j = 0; j < nGroup5A; j++) { + if (j == 0) + xpos = xGroup5A[0]; + else + xpos = xGroup5A[1]; + zpos = zGroup5A[j]; + modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + } + + nCapacitors += nGroup5A; + xpos = xGroup5B; + for (Int_t j = 0; j < nGroup5B; j++) { + zpos = zGroup5B[j]; + modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + } + + // Place the resistors + xpos = xResist; + for (Int_t j = 0; j < nResist; j++) { + zpos = zResist[j]; + modvol->AddNode(resistor, j + 1, new TGeoTranslation(-xpos, ypos, -zpos)); + } +} + +TGeoVolume* V3Layer::createIBFPCAlGnd(const Double_t xcable, const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the IB FPC Aluminum Ground cable + // + // Created: 20 Oct 2017 Mario Sitta + // + + Double_t ytot, ypos; + + // First create all needed shapes + ytot = sIBFlexCablePolyThick + sIBFlexCableAlThick; + TGeoBBox* coverlay = new TGeoBBox(xcable, ytot / 2, zcable); + TGeoBBox* aluminum = new TGeoBBox(xcable, sIBFlexCableAlThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medAluminum = mgr->GetMedium("EC0_ALUMINUM$"); + + TGeoVolume* coverlayVol = new TGeoVolume("FPCCoverlayGround", coverlay, medKapton); + coverlayVol->SetLineColor(kBlue); + coverlayVol->SetFillColor(kBlue); + + TGeoVolume* aluminumVol = new TGeoVolume("FPCAluminumGround", aluminum, medAluminum); + aluminumVol->SetLineColor(kCyan); + aluminumVol->SetFillColor(kCyan); + + ypos = coverlay->GetDY() - aluminum->GetDY(); + if (mBuildLevel < 1) // Aluminum + coverlayVol->AddNode(aluminumVol, 1, new TGeoTranslation(0, ypos, 0)); + + return coverlayVol; +} + +TGeoVolume* V3Layer::createIBFPCAlAnode(const Double_t xcable, const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the IB FPC Aluminum Anode cable + // + // + // Created: 20 Oct 2017 Mario Sitta + // Updated: 03 Apr 2019 Mario Sitta Fix Al position (180' rotation) + // + + Double_t ytot, ypos; + Double_t xtru[4], ytru[4]; + + // First create all needed shapes + ytot = sIBFlexCablePolyThick + sIBFlexCableAlThick; + TGeoBBox* coverlay = new TGeoBBox(xcable, ytot / 2, zcable); + + // A trapezoid + xtru[0] = -sIBFPCAlAnodeWidth2 / 2; + ytru[0] = -zcable; + xtru[1] = sIBFPCAlAnodeWidth2 / 2; + ytru[1] = ytru[0]; + xtru[2] = xtru[0] + sIBFPCAlAnodeWidth1; + ytru[2] = zcable; + xtru[3] = xtru[0]; + ytru[3] = ytru[2]; + + TGeoXtru* aluminum = new TGeoXtru(2); + aluminum->DefinePolygon(4, xtru, ytru); + aluminum->DefineSection(0, -sIBFlexCableAlThick / 2); + aluminum->DefineSection(1, sIBFlexCableAlThick / 2); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medAluminum = mgr->GetMedium("EC0_ALUMINUM$"); + + TGeoVolume* coverlayVol = new TGeoVolume("FPCCoverlayAnode", coverlay, medKapton); + coverlayVol->SetLineColor(kBlue); + coverlayVol->SetFillColor(kBlue); + + TGeoVolume* aluminumVol = new TGeoVolume("FPCAluminumAnode", aluminum, medAluminum); + aluminumVol->SetLineColor(kCyan); + aluminumVol->SetFillColor(kCyan); + + ypos = -coverlay->GetDY() + aluminum->GetZ(1); + if (mBuildLevel < 1) // Aluminum + coverlayVol->AddNode(aluminumVol, 1, new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", 0, -90, 0))); + + return coverlayVol; +} + +TGeoVolume* V3Layer::createStaveStructInnerB(const TGeoManager* mgr) +{ + // + // Create the mechanical stave structure + // + // Created: 22 Mar 2013 Chinorat Kobdaj + // Updated: 26 Apr 2013 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version - All models obsolete except last one + // Updated: 25 Jan 2018 Mario Sitta Stave width is now a constant + // + + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kIBModelDummy: + mechStavVol = createStaveModelInnerBDummy(mgr); + break; + case Detector::kIBModel0: + case Detector::kIBModel1: + case Detector::kIBModel21: + case Detector::kIBModel22: + case Detector::kIBModel3: + LOG(FATAL) << "Stave model " << mStaveModel << " obsolete and no longer supported"; + break; + case Detector::kIBModel4: + mechStavVol = createStaveModelInnerB4(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V3Layer::createStaveModelInnerBDummy(const TGeoManager*) const +{ + // + // Create dummy stave + // + // Created: 22 Mar 2013 Chinorat Kobdaj + // Updated: 26 Apr 2013 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version + // + + // Done, return the stave structur + return nullptr; +} + +// model4 +//________________________________________________________________________ +TGeoVolume* V3Layer::createStaveModelInnerB4(const TGeoManager* mgr) +{ + // + // Create the mechanical stave structure for Model 4 of TDR + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 04 Dec 2014 Mario Sitta + // Updated: 03 Mar 2015 Mario Sitta FPC in right position (beyond chip) + // Updated: 06 Mar 2015 Mario Sitta Space Frame corrected (C.G. data) + // Updated: 30 Apr 2015 Mario Sitta End-stave connectors added + // Updated: 04 Apr 2017 Mario Sitta O2 version + // Updated: 25 Jan 2018 Mario Sitta Stave width is now a constant + // Updated: 03 Feb 2018 Mario Sitta To last drawings (ALIEC0UP0051) + // + + // Local parameters + const Double_t xstave = sIBColdPlateWidth / 2; + + Double_t layerHeight = 0.; + + Double_t rPipeMin = sIBCoolPipeInnerD / 2; + Double_t rPipeMax = rPipeMin + sIBCoolPipeThick; + + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; // The stave container Xtru + Double_t xlen, ylen, zlen, ztot; + Double_t xpos, ypos, zpos, ylay, yposPipe; + Double_t beta, gamma, theta; + + // First create all needed shapes + ztot = sIBColdPlateZLen / 2; + + TGeoBBox* glue = new TGeoBBox(xstave, sIBGlueThick / 2, ztot); + + TGeoBBox* fleecbot = new TGeoBBox(xstave, sIBCarbonFleeceThick / 2, ztot); + + TGeoBBox* cfplate = new TGeoBBox(xstave, sIBK13D2UThick / 2, ztot); + + TGeoTube* pipe = new TGeoTube(rPipeMin, rPipeMax, sIBCoolPipeZLen / 2); + + TGeoTube* water = new TGeoTube(0., rPipeMin, sIBCoolPipeZLen / 2); + + TGeoTubeSeg* cpaptub = new TGeoTubeSeg(rPipeMax, rPipeMax + sIBCarbonPaperThick, sIBCarbonPaperZLen / 2, 0, 180); + + TGeoBBox* cpapvert = new TGeoBBox(sIBCarbonPaperThick / 2, pipe->GetRmax() / 2, sIBCarbonPaperZLen / 2); + + xlen = sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick; + TGeoBBox* cpapmid = new TGeoBBox(xlen, sIBCarbonPaperThick / 2, sIBCarbonPaperZLen / 2); + + xlen = sIBCarbonPaperWidth / 2 - sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick; + TGeoBBox* cpaplr = new TGeoBBox(xlen / 2, sIBCarbonPaperThick / 2, sIBCarbonPaperZLen / 2); + + TGeoTubeSeg* fleecpipe = new TGeoTubeSeg(cpaptub->GetRmax(), cpaptub->GetRmax() + sIBCarbonFleeceThick, ztot, 0, 180); + + TGeoBBox* fleecvert = new TGeoBBox(sIBCarbonFleeceThick / 2, (pipe->GetRmax() - sIBCarbonPaperThick) / 2, ztot); + + xlen = sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick - sIBCarbonFleeceThick; + TGeoBBox* fleecmid = new TGeoBBox(xlen, sIBCarbonFleeceThick / 2, ztot); + + xlen = xstave - sIBCoolPipeXDist / 2 - pipe->GetRmax() - sIBCarbonPaperThick - sIBCarbonFleeceThick; + TGeoBBox* fleeclr = new TGeoBBox(xlen / 2, sIBCarbonFleeceThick / 2, ztot); + + // The total height of the layer can now be computed + layerHeight = 2 * (glue->GetDY() + fleecbot->GetDY() + cfplate->GetDY() + cpaplr->GetDY() + fleeclr->GetDY()); + + // The spaceframe structure + TGeoTrd1* topv = new TGeoTrd1(sIBTopVertexWidth1 / 2, sIBTopVertexWidth2 / 2, ztot, sIBTopVertexHeight / 2); + + xv[0] = 0; + yv[0] = 0; + xv[1] = sIBSideVertexWidth; + yv[1] = yv[0]; + xv[2] = xv[0]; + yv[2] = sIBSideVertexHeight; + + TGeoXtru* sidev = new TGeoXtru(2); + sidev->DefinePolygon(3, xv, yv); + sidev->DefineSection(0, -ztot); + sidev->DefineSection(1, ztot); + + xv[0] = sIBEndSupportXUp / 2; + yv[0] = sIBStaveHeight - sIBEndSupportThick; + xv[1] = xstave - sIBSideVertexWidth; + yv[1] = layerHeight + sIBSideVertexHeight; + xv[2] = xstave; + yv[2] = layerHeight; + xv[3] = xv[2]; + yv[3] = 0; + xv[4] = xstave + sIBEndSupportThick; + yv[4] = yv[3]; + xv[5] = xv[4]; + yv[5] = yv[2]; + xv[6] = xv[1] + sIBEndSupportThick * sinD(sIBEndSupportOpenPhi / 2); + yv[6] = yv[1] + sIBEndSupportThick * cosD(sIBEndSupportOpenPhi / 2); + xv[7] = xv[0]; + yv[7] = sIBStaveHeight; + for (Int_t i = 0; i < nv / 2; i++) { + xv[8 + i] = -xv[7 - i]; + yv[8 + i] = yv[7 - i]; + } + + TGeoXtru* endsupp = new TGeoXtru(2); + endsupp->DefinePolygon(16, xv, yv); + endsupp->DefineSection(0, -sIBEndSupportZLen / 2); + endsupp->DefineSection(1, sIBEndSupportZLen / 2); + + xlen = TMath::Sqrt((yv[7] - yv[6]) * (yv[7] - yv[6]) + (xv[7] - xv[6]) * (xv[7] - xv[6]) + + sIBTopFilamentInterZ * sIBTopFilamentInterZ / 4); + theta = TMath::ATan((yv[7] - yv[6]) / (xv[7] - xv[6])) * TMath::RadToDeg(); + TGeoBBox* topfil = new TGeoBBox(xlen / 2, sIBTopFilamentSide / 2, sIBTopFilamentSide / 2); + + // The half stave container (an XTru to avoid overlaps between neighbours) + xv[0] = xstave + sIBTopFilamentSide; + yv[0] = 0; + xv[1] = xv[0]; + yv[1] = layerHeight + sIBSideVertexHeight + topfil->GetDZ(); + ; + xv[2] = sIBEndSupportXUp / 2; + yv[2] = sIBStaveHeight + sIBTopFilamentSide / sinD(-theta); // theta is neg + for (Int_t i = 0; i < 3; i++) { + xv[3 + i] = -xv[2 - i]; + yv[3 + i] = yv[2 - i]; + } + + TGeoXtru* mechStruct = new TGeoXtru(2); + mechStruct->DefinePolygon(6, xv, yv); + mechStruct->SetName("mechStruct"); + mechStruct->DefineSection(0, -ztot); + mechStruct->DefineSection(1, ztot); + + // The connectors' containers + zlen = sIBConnectBlockZLen - sIBConnTailZLen + sIBConnectAFitZOut; + TGeoBBox* connAside = new TGeoBBox("connAsideIB", sIBConnectorXWidth / 2, sIBConnBodyYHeight / 2, zlen / 2); + + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + TGeoBBox* connCside = new TGeoBBox("connCsideIB", sIBConnectorXWidth / 2, sIBConnBodyYHeight / 2, zlen / 2); + + // The StaveStruct container, a Composite Shape + yposPipe = 2 * glue->GetDY() + 2 * fleecbot->GetDY() + 2 * cfplate->GetDY() + pipe->GetRmax(); + ypos = connAside->GetDY() - sIBConnTubesYPos + yposPipe; + zpos = ztot + connAside->GetDZ(); + TGeoTranslation* transAside = new TGeoTranslation("transAsideIB", 0, ypos, zpos); + transAside->RegisterYourself(); + + ypos = connCside->GetDY() - sIBConnTubesYPos + yposPipe; + zpos = ztot + connCside->GetDZ(); + TGeoTranslation* transCside = new TGeoTranslation("transCsideIB", 0, ypos, -zpos); + transCside->RegisterYourself(); + + TGeoCompositeShape* mechStavSh = + new TGeoCompositeShape("mechStruct+connAsideIB:transAsideIB+connCsideIB:transCsideIB"); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + TGeoMedium* medM55J6K = mgr->GetMedium("EC0_M55J6K$"); + TGeoMedium* medM60J3K = mgr->GetMedium("EC0_M60J3K$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medK13D2U2k = mgr->GetMedium("EC0_K13D2U2k$"); + TGeoMedium* medFGS003 = mgr->GetMedium("EC0_FGS003$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("EC0_CarbonFleece$"); + + const Int_t nameLen = 30; + char volname[nameLen]; + snprintf(volname, nameLen, "%s%d_StaveStruct", GeometryTGeo::getEC0StavePattern(), mLayerNumber); + TGeoVolume* mechStavVol = new TGeoVolume(volname, mechStavSh, medAir); + mechStavVol->SetLineColor(12); + mechStavVol->SetFillColor(12); + mechStavVol->SetVisibility(kFALSE); + + TGeoVolume* glueVol = new TGeoVolume("Glue", glue, medGlue); + glueVol->SetLineColor(kBlack); + glueVol->SetFillColor(kBlack); + + TGeoVolume* fleecbotVol = new TGeoVolume("CarbonFleeceBottom", fleecbot, medCarbonFleece); + fleecbotVol->SetFillColor(kViolet); + fleecbotVol->SetLineColor(kViolet); + + TGeoVolume* cfplateVol = new TGeoVolume("CFPlate", cfplate, medK13D2U2k); + cfplateVol->SetFillColor(5); // Yellow + cfplateVol->SetLineColor(5); + + TGeoVolume* pipeVol = new TGeoVolume("PolyimidePipe", pipe, medKapton); + pipeVol->SetFillColor(35); // Blue shade + pipeVol->SetLineColor(35); + + TGeoVolume* waterVol = new TGeoVolume("Water", water, medWater); + waterVol->SetFillColor(4); // Bright blue + waterVol->SetLineColor(4); + + TGeoVolume* cpaptubVol = new TGeoVolume("ThermasolPipeCover", cpaptub, medFGS003); + cpaptubVol->SetFillColor(2); // Red + cpaptubVol->SetLineColor(2); + + TGeoVolume* cpapvertVol = new TGeoVolume("ThermasolVertical", cpapvert, medFGS003); + cpapvertVol->SetFillColor(2); // Red + cpapvertVol->SetLineColor(2); + + TGeoVolume* cpapmidVol = new TGeoVolume("ThermasolMiddle", cpapmid, medFGS003); + cpapmidVol->SetFillColor(2); // Red + cpapmidVol->SetLineColor(2); + + TGeoVolume* cpaplrVol = new TGeoVolume("ThermasolLeftRight", cpaplr, medFGS003); + cpaplrVol->SetFillColor(2); // Red + cpaplrVol->SetLineColor(2); + + TGeoVolume* fleecpipeVol = new TGeoVolume("CarbonFleecePipeCover", fleecpipe, medCarbonFleece); + fleecpipeVol->SetFillColor(28); // Brown shade + fleecpipeVol->SetLineColor(28); + + TGeoVolume* fleecvertVol = new TGeoVolume("CarbonFleeceVertical", fleecvert, medCarbonFleece); + fleecvertVol->SetFillColor(28); // Brown shade + fleecvertVol->SetLineColor(28); + + TGeoVolume* fleecmidVol = new TGeoVolume("CarbonFleeceMiddle", fleecmid, medCarbonFleece); + fleecmidVol->SetFillColor(28); // Brown shade + fleecmidVol->SetLineColor(28); + + TGeoVolume* fleeclrVol = new TGeoVolume("CarbonFleeceLeftRight", fleeclr, medCarbonFleece); + fleeclrVol->SetFillColor(28); // Brown shade + fleeclrVol->SetLineColor(28); + + TGeoVolume* topvVol = new TGeoVolume("TopVertex", topv, medM55J6K); + topvVol->SetFillColor(12); // Gray shade + topvVol->SetLineColor(12); + + TGeoVolume* sidevVol = new TGeoVolume("SideVertex", sidev, medM55J6K); + sidevVol->SetFillColor(12); // Gray shade + sidevVol->SetLineColor(12); + + TGeoVolume* topfilVol = new TGeoVolume("TopFilament", topfil, medM60J3K); + topfilVol->SetFillColor(12); // Gray shade + topfilVol->SetLineColor(12); + + TGeoVolume* endsuppVol = new TGeoVolume("EndSupport", endsupp, medM55J6K); + endsuppVol->SetFillColor(12); // Gray shade + endsuppVol->SetLineColor(12); + + // Now build up the half stave + ypos = glue->GetDY(); + if (mBuildLevel < 2) // Glue + mechStavVol->AddNode(glueVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (glue->GetDY() + fleecbot->GetDY()); + if (mBuildLevel < 5) // Carbon + mechStavVol->AddNode(fleecbotVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (fleecbot->GetDY() + cfplate->GetDY()); + if (mBuildLevel < 5) // Carbon + mechStavVol->AddNode(cfplateVol, 1, new TGeoTranslation(0, ypos, 0)); + + ylay = ypos + cfplate->GetDY(); // The level where tubes etc. lay + + xpos = sIBCoolPipeXDist / 2; + ypos = ylay + pipe->GetRmax(); + yposPipe = ypos; // Save for later use + if (mBuildLevel < 4) { // Kapton + mechStavVol->AddNode(pipeVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(pipeVol, 2, new TGeoTranslation(xpos, ypos, 0)); + } + + if (mBuildLevel < 3) { // Water + mechStavVol->AddNode(waterVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(waterVol, 2, new TGeoTranslation(xpos, ypos, 0)); + } + + if (mBuildLevel < 5) { // Carbon (stave components) + mechStavVol->AddNode(cpaptubVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpaptubVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + mechStavVol->AddNode(fleecpipeVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleecpipeVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 - pipe->GetRmax() - cpapvert->GetDX(); + ypos = ylay + cpapvert->GetDY(); + mechStavVol->AddNode(cpapvertVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpapvertVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 + pipe->GetRmax() + cpapvert->GetDX(); + mechStavVol->AddNode(cpapvertVol, 3, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpapvertVol, 4, new TGeoTranslation(xpos, ypos, 0)); + + ypos = ylay + sIBCarbonPaperThick / 2; + mechStavVol->AddNode(cpapmidVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = xstave - cpaplr->GetDX(); + mechStavVol->AddNode(cpaplrVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(cpaplrVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 - pipe->GetRmax() - 2 * cpapvert->GetDX() - fleecvert->GetDX(); + ypos = ylay + sIBCarbonPaperThick + fleecvert->GetDY(); + mechStavVol->AddNode(fleecvertVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleecvertVol, 2, new TGeoTranslation(xpos, ypos, 0)); + + xpos = sIBCoolPipeXDist / 2 + pipe->GetRmax() + 2 * cpapvert->GetDX() + fleecvert->GetDX(); + mechStavVol->AddNode(fleecvertVol, 3, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleecvertVol, 4, new TGeoTranslation(xpos, ypos, 0)); + + ypos = ylay + sIBCarbonPaperThick + sIBCarbonFleeceThick / 2; + mechStavVol->AddNode(fleecmidVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = xstave - fleeclr->GetDX(); + mechStavVol->AddNode(fleeclrVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + mechStavVol->AddNode(fleeclrVol, 2, new TGeoTranslation(xpos, ypos, 0)); + } + + ylay += (sIBCarbonPaperThick + sIBCarbonFleeceThick); + + if (mBuildLevel < 5) { // Carbon (spaceframe) + ypos = sIBStaveHeight - sIBEndSupportThick - topv->GetDz(); // Due to rotation, z is on Y + mechStavVol->AddNode(topvVol, 1, new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", 0, -90, 0))); + + xpos = xstave - sidev->GetX(1); + ypos = ylay; + mechStavVol->AddNode(sidevVol, 1, new TGeoTranslation(xpos, ypos, 0)); + mechStavVol->AddNode(sidevVol, 2, new TGeoCombiTrans(-xpos, ypos, 0, new TGeoRotation("", 90, 180, -90))); + + zpos = ztot - endsupp->GetZ(1); + mechStavVol->AddNode(endsuppVol, 1, new TGeoTranslation(0, 0, zpos)); + mechStavVol->AddNode(endsuppVol, 2, new TGeoTranslation(0, 0, -zpos)); + + gamma = 180. - sIBTopFilamentAlpha; + xpos = xstave / 2 + topfil->GetDZ(); + ypos = (endsupp->GetY(7) + endsupp->GetY(6)) / 2; + Int_t nFilamentGroups = (Int_t)(2 * (ztot - sIBEndSupportZLen) / sIBTopFilamentInterZ); + // theta was computed when filament volume was created + for (int i = 0; i < nFilamentGroups; i++) { // i<19 + // 1) Front Left Top Filament + zpos = -(ztot - sIBEndSupportZLen) + i * sIBTopFilamentInterZ + sIBTopFilamentInterZ / 4; + mechStavVol->AddNode(topfilVol, i * 4 + 1, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90 + theta, gamma / 2, -90))); + // 2) Front Right Top Filament + mechStavVol->AddNode(topfilVol, i * 4 + 2, + new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90 - theta, -gamma / 2, -90))); + // 3) Back Left Top Filament + zpos += sIBTopFilamentInterZ / 2; + mechStavVol->AddNode(topfilVol, i * 4 + 3, + new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90 + theta, -gamma / 2, -90))); + // 4) Back Right Top Filament + mechStavVol->AddNode(topfilVol, i * 4 + 4, + new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90 - theta, gamma / 2, -90))); + } + } + + // Add the end-stave connectors + TGeoVolume *connectorASide, *connectorCSide; + + // Check whether we have already all pieces + // Otherwise create them + connectorASide = mgr->GetVolume("IBConnectorASide"); + + if (!connectorASide) { + createIBConnectors(mgr); + connectorASide = mgr->GetVolume("IBConnectorASide"); + } + connectorCSide = mgr->GetVolume("IBConnectorCSide"); + + ypos = (static_cast(connectorASide->GetShape()))->GetDY() - sIBConnTubesYPos + + yposPipe; // We center the pipe and hole axes + zpos = ztot + (sIBConnectBlockZLen - sIBConnTailZLen + sIBConnectAFitZOut) / 2; + mechStavVol->AddNode(connectorASide, 1, new TGeoTranslation(0, ypos, zpos)); + + zpos = ztot + (sIBConnectBlockZLen - sIBConnTailZLen) / 2; + mechStavVol->AddNode(connectorCSide, 1, new TGeoCombiTrans(0, ypos, -zpos, new TGeoRotation("", 90, 180, -90))); + + // Done, return the stave structure + return mechStavVol; +} + +void V3Layer::createIBConnectors(const TGeoManager* mgr) +{ + // + // Create the end-stave connectors for IB staves + // (simply call the actual creator methods) + // + // Created: 20 Apr 2015 Mario Sitta + // + + createIBConnectorsASide(mgr); + createIBConnectorsCSide(mgr); +} + +void V3Layer::createIBConnectorsASide(const TGeoManager* mgr) +{ + // + // Create the A-Side end-stave connectors for IB staves + // + // Created: 22 Apr 2015 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version + // Updated: 28 Jan 2018 Mario Sitta To last drawings (ALIEC0UP0051) + // Updated: 19 Jun 2019 Mario Sitta Avoid fake overlaps with EndWheels + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("EC0_PEEKCF30$"); + TGeoMedium* medInox304 = mgr->GetMedium("EC0_INOX304$"); + + // First create all elements + // (All measures refer to the blueprint ALIEC0UP0051) + + // The connector block, two Composite Shapes: + // the body... + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + TGeoBBox* connBody = new TGeoBBox("connBodyA", xlen / 2, ylen / 2, zlen / 2); + + TGeoTube* connRoundHole = new TGeoTube("connRoundHoleA", 0., sIBConnRoundHoleD / 2, sIBConnBodyYHeight / 1.5); + + zpos = -connBody->GetDZ() + sIBConnRoundHoleZ; + TGeoCombiTrans* connRoundHoleTrans = + new TGeoCombiTrans("roundHoleTransA", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + connRoundHoleTrans->RegisterYourself(); + + xlen = sIBConnSquareHoleX / 2; + ylen = sIBConnBodyYHeight / 1.5; + zlen = sIBConnSquareHoleZ / 2; + TGeoBBox* connSquareHole = new TGeoBBox("connSquareHoleA", xlen, ylen, zlen); + + zpos = -connBody->GetDZ() + sIBConnSquareHoleZPos; + TGeoTranslation* connSquareHoleTrans = new TGeoTranslation("squareHoleTransA", 0, 0, zpos); + connSquareHoleTrans->RegisterYourself(); + + TGeoTube* connTubeHole2 = new TGeoTube("tube2HoleA", 0, sIBConnTubeHole2D / 2, connBody->GetDZ()); + + xpos = sIBConnTubesXDist / 2; + ypos = -connBody->GetDY() + sIBConnTubesYPos; + + TGeoTranslation* connTubes2Trans1 = new TGeoTranslation("tubes2Trans1A", -xpos, ypos, 0); + connTubes2Trans1->RegisterYourself(); + + TGeoTranslation* connTubes2Trans2 = new TGeoTranslation("tubes2Trans2A", xpos, ypos, 0); + connTubes2Trans2->RegisterYourself(); + + zlen = sIBConnTubeHole1ZLen - sIBConnTailZLen; + TGeoTube* connTubeHole3 = new TGeoTube("tube3HoleA", 0, sIBConnTubeHole1D / 2, zlen); + + zpos = connBody->GetDZ(); + TGeoTranslation* connTubes3Trans1 = new TGeoTranslation("tubes3Trans1A", -xpos, ypos, -zpos); + connTubes3Trans1->RegisterYourself(); + TGeoTranslation* connTubes3Trans2 = new TGeoTranslation("tubes3Trans2A", xpos, ypos, -zpos); + connTubes3Trans2->RegisterYourself(); + + zlen = sIBConnTubeHole1ZLen2; + TGeoTube* connFitHole = new TGeoTube("fitHoleA", 0, sIBConnectAFitExtD / 2, zlen); + + TGeoTranslation* connFitHoleTrans1 = new TGeoTranslation("fitTrans1A", -xpos, ypos, zpos); + connFitHoleTrans1->RegisterYourself(); + TGeoTranslation* connFitHoleTrans2 = new TGeoTranslation("fitTrans2A", xpos, ypos, zpos); + connFitHoleTrans2->RegisterYourself(); + + zlen = sIBConnSideHole1XWid / 1.5; + TGeoTube* sideHole1 = new TGeoTube("sideHole1A", 0, sIBConnSideHole1D / 2, zlen); + + xpos = connBody->GetDX() - sIBConnSideHole1XWid + sideHole1->GetDz(); + ypos = connBody->GetDY() - sIBConnSideHole1YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole1ZPos - sIBConnTailZLen); + TGeoCombiTrans* connSideHole1Trans = + new TGeoCombiTrans("sideHole1TransA", xpos, ypos, zpos, new TGeoRotation("", 90, 90, 0)); + connSideHole1Trans->RegisterYourself(); + + TGeoBBox* sideHole2Box = + new TGeoBBox("sideHole2AB", sIBConnSideHole2XWid, sIBConnSideHole2YWid / 2, sIBConnSideHole2ZWid / 2); + + xpos = -connBody->GetDX(); + ypos = connBody->GetDY() - sIBConnSideHole2YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + sideHole2Box->GetDZ(); + TGeoTranslation* sideHole2BTrans = new TGeoTranslation("sideHole2TransBA", xpos, ypos, zpos); + sideHole2BTrans->RegisterYourself(); + + TGeoTubeSeg* sideHole2TubeSeg = + new TGeoTubeSeg("sideHole2ATS", 0, sIBConnSideHole2YWid / 2, sIBConnSideHole2XWid, 0, 180); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen); + TGeoCombiTrans* sideHole2TSTrans1 = + new TGeoCombiTrans("sideHole2TSTrans1A", xpos, ypos, zpos, new TGeoRotation("", 90, -90, 0)); + sideHole2TSTrans1->RegisterYourself(); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + 2 * sideHole2Box->GetDZ(); + TGeoCombiTrans* sideHole2TSTrans2 = + new TGeoCombiTrans("sideHole2TSTrans2A", xpos, ypos, zpos, new TGeoRotation("", 90, 90, 0)); + sideHole2TSTrans2->RegisterYourself(); + + TGeoCompositeShape* connBodySh = new TGeoCompositeShape( + "connBodyA-connRoundHoleA:roundHoleTransA-connSquareHoleA:squareHoleTransA-tube2HoleA:tubes2Trans1A-tube2HoleA:" + "tubes2Trans2A-fitHoleA:fitTrans1A-fitHoleA:fitTrans2A-tube3HoleA:tubes3Trans1A-tube3HoleA:tubes3Trans2A-" + "sideHole1A:sideHole1TransA-sideHole2AB:sideHole2TransBA-sideHole2ATS:sideHole2TSTrans1A-sideHole2ATS:" + "sideHole2TSTrans2A"); + + TGeoVolume* connBlockBody = new TGeoVolume("IBConnectorBlockBodyASide", connBodySh, medPEEK); + connBlockBody->SetFillColor(42); // Brownish shade + connBlockBody->SetLineColor(42); + + // ...and the tail + xv[0] = sIBConnectorXWidth / 2; + yv[0] = sIBConnTailYShift; + xv[1] = xv[0]; + yv[1] = sIBConnTailYMid; + xv[2] = xv[1] - (sIBConnectorYTot - sIBConnTailYMid) / tanD(90 - sIBConnTailOpenPhi / 2); + yv[2] = sIBConnectorYTot; + + for (Int_t i = 0; i < 3; i++) { + xv[3 + i] = -xv[2 - i]; + yv[3 + i] = yv[2 - i]; + } + + TGeoXtru* connTail = new TGeoXtru(2); + connTail->SetName("connTailA"); + connTail->DefinePolygon(6, xv, yv); + connTail->DefineSection(0, 0); + connTail->DefineSection(1, sIBConnTailZLen); + + TGeoTube* connTubeHole1 = new TGeoTube("tube1HoleA", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 1.5); + + xpos = sIBConnTubesXDist / 2; + ypos = sIBConnTubesYPos; + zpos = connTail->GetZ(1) / 2; + TGeoTranslation* connTubes1Trans1 = new TGeoTranslation("tubes1Trans1A", -xpos, ypos, zpos); + connTubes1Trans1->RegisterYourself(); + TGeoTranslation* connTubes1Trans2 = new TGeoTranslation("tubes1Trans2A", xpos, ypos, zpos); + connTubes1Trans2->RegisterYourself(); + + TGeoCompositeShape* connTailSh = + new TGeoCompositeShape("connTailA-tube1HoleA:tubes1Trans1A-tube1HoleA:tubes1Trans2A"); + + TGeoVolume* connBlockTail = new TGeoVolume("IBConnectorBlockTailASide", connTailSh, medPEEK); + connBlockTail->SetFillColor(42); // Brownish shade + connBlockTail->SetLineColor(42); + + // The fitting tubes, a Tube + TGeoTube* connFitSh = new TGeoTube(sIBConnectAFitIntD / 2, sIBConnectAFitExtD / 2, sIBConnectAFitZLen / 2); + + TGeoVolume* connFit = new TGeoVolume("IBConnectorFitting", connFitSh, medInox304); + connFit->SetFillColor(kGray); + connFit->SetLineColor(kGray); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen + sIBConnectAFitZOut; + + TGeoBBox* connBox = new TGeoBBox("connBoxA", xlen / 2, ylen / 2, zlen / 2); + + ypos = -sIBConnectorYTot / 2 + connBox->GetDY(); + TGeoTranslation* transBodyA = new TGeoTranslation("transBodyA", 0, ypos, 0); + transBodyA->RegisterYourself(); + + ypos = -sIBConnectorYTot / 2; + zpos = -connBox->GetDZ() - connTail->GetZ(1); + TGeoTranslation* transTailA = new TGeoTranslation("transTailA", 0, ypos, zpos); + transTailA->RegisterYourself(); + + TGeoTube* connTubeHollow = new TGeoTube("tubeHollowA", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 2); + + xpos = sIBConnTubesXDist / 2; + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = -connBox->GetDZ() - connTail->GetZ(1) + sIBConnTubeHole1ZLen / 2; + TGeoTranslation* connTubeHollTrans1 = new TGeoTranslation("tubeHollTrans1A", -xpos, ypos, zpos); + connTubeHollTrans1->RegisterYourself(); + TGeoTranslation* connTubeHollTrans2 = new TGeoTranslation("tubeHollTrans2A", xpos, ypos, zpos); + connTubeHollTrans2->RegisterYourself(); + + zpos = -connBox->GetDZ() + connTubeHole2->GetDz() - 2 * connFitHole->GetDz(); + TGeoTranslation* connTubes2Trans1Body = new TGeoTranslation("tubes2Trans1BA", -xpos, ypos, zpos); + connTubes2Trans1Body->RegisterYourself(); + TGeoTranslation* connTubes2Trans2Body = new TGeoTranslation("tubes2Trans2BA", xpos, ypos, zpos); + connTubes2Trans2Body->RegisterYourself(); + + TGeoCompositeShape* connBoxSh = new TGeoCompositeShape( + "connBoxA:transBodyA-tube2HoleA:tubes2Trans1BA-tube2HoleA:tubes2Trans2BA+connTailA:transTailA-tubeHollowA:tubeHollTrans1A-" + "tubeHollowA:tubeHollTrans2A"); + + TGeoVolume* connBoxASide = new TGeoVolume("IBConnectorASide", connBoxSh, medAir); + + // Finally build up the connector + // (NB: the origin is in the connBox, i.e. w/o the tail in Z) + ypos = -sIBConnectorYTot / 2; + zpos = -connBox->GetDZ() - connTail->GetZ(1); + connBoxASide->AddNode(connBlockTail, 1, new TGeoTranslation(0, ypos, zpos)); + + ypos = -sIBConnectorYTot / 2 + connBody->GetDY(); + zpos = -connBox->GetDZ() + connBody->GetDZ(); + connBoxASide->AddNode(connBlockBody, 1, new TGeoTranslation(0, ypos, zpos)); + + xpos = sIBConnTubesXDist / 2; + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = connBox->GetDZ() - connFitSh->GetDz(); + connBoxASide->AddNode(connFit, 1, new TGeoTranslation(xpos, ypos, zpos)); + connBoxASide->AddNode(connFit, 2, new TGeoTranslation(-xpos, ypos, zpos)); +} + +void V3Layer::createIBConnectorsCSide(const TGeoManager* mgr) +{ + // + // Create the C-Side end-stave connectors for IB staves + // + // Created: 05 May 2015 Mario Sitta + // Updated: 04 Apr 2017 Mario Sitta O2 version + // Updated: 28 Jan 2018 Mario Sitta To last drawings (ALIEC0UP0051) + // Updated: 15 May 2019 Mario Sitta Avoid fake overlaps with EndWheels + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("EC0_PEEKCF30$"); + + // First create all elements + // (All measures refer to the blueprint ALIEC0UP0051) + + // The connector block, two Composite Shapes: + // the body... + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + TGeoBBox* connBody = new TGeoBBox("connBodyC", xlen / 2, ylen / 2, zlen / 2); + + TGeoTube* connRoundHole = new TGeoTube("connRoundHoleC", 0., sIBConnRoundHoleD / 2, sIBConnBodyYHeight / 1.5); + + zpos = -connBody->GetDZ() + sIBConnRoundHoleZ; + TGeoCombiTrans* connRoundHoleTrans = + new TGeoCombiTrans("roundHoleTransC", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + connRoundHoleTrans->RegisterYourself(); + + TGeoTube* connInsertHole = new TGeoTube("connInsertHoleC", 0, sIBConnInsertHoleD / 2, sIBConnBodyYHeight / 1.5); + + zpos = -connBody->GetDZ() + sIBConnInsertHoleZPos; + TGeoCombiTrans* connInsertHoleTrans = + new TGeoCombiTrans("insertHoleTransC", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + connInsertHoleTrans->RegisterYourself(); + + TGeoTube* connTubeHole2 = new TGeoTube("tube2HoleC", 0, sIBConnTubeHole2D / 2, connBody->GetDZ()); + + xpos = sIBConnTubesXDist / 2; + ypos = -connBody->GetDY() + sIBConnTubesYPos; + zpos = sIBConnectBlockZLen - sIBConnTubeHole3ZPos; + TGeoTranslation* connTubes2Trans1 = new TGeoTranslation("tubes2Trans1C", -xpos, ypos, -zpos); + connTubes2Trans1->RegisterYourself(); + TGeoTranslation* connTubes2Trans2 = new TGeoTranslation("tubes2Trans2C", xpos, ypos, -zpos); + connTubes2Trans2->RegisterYourself(); + + zlen = sIBConnectorXWidth; + TGeoTube* connTubeHole3 = new TGeoTube("tube3HoleC", 0, sIBConnTubeHole2D / 2, zlen / 2); + + xpos = sIBConnTubeHole3XPos; + zpos = connBody->GetDZ() - (sIBConnectBlockZLen - sIBConnTubeHole3ZPos); + TGeoCombiTrans* connTubes3Trans = + new TGeoCombiTrans("tubes3TransC", xpos, ypos, zpos, new TGeoRotation("", 90, -90, 90)); + connTubes3Trans->RegisterYourself(); + + zlen = sIBConnTubeHole1ZLen - sIBConnTailZLen; + TGeoTube* connTubeHole4 = new TGeoTube("tube4HoleC", 0, sIBConnTubeHole1D / 2, zlen); + + xpos = sIBConnTubesXDist / 2; + zpos = connBody->GetDZ(); + TGeoTranslation* connTubes4Trans1 = new TGeoTranslation("tubes4Trans1C", -xpos, ypos, -zpos); + connTubes4Trans1->RegisterYourself(); + TGeoTranslation* connTubes4Trans2 = new TGeoTranslation("tubes4Trans2C", xpos, ypos, -zpos); + connTubes4Trans2->RegisterYourself(); + + zlen = sIBConnSideHole1XWid / 1.5; + TGeoTube* sideHole1 = new TGeoTube("sideHole1C", 0, sIBConnSideHole1D / 2, zlen); + + xpos = -connBody->GetDX() + sIBConnSideHole1XWid - sideHole1->GetDz(); + ypos = connBody->GetDY() - sIBConnSideHole1YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole1ZPos - sIBConnTailZLen); + TGeoCombiTrans* connSideHole1Trans = + new TGeoCombiTrans("sideHole1TransC", xpos, ypos, zpos, new TGeoRotation("", 90, 90, 0)); + connSideHole1Trans->RegisterYourself(); + + TGeoBBox* sideHole2Box = + new TGeoBBox("sideHole2CB", sIBConnSideHole2XWid, sIBConnSideHole2YWid / 2, sIBConnSideHole2ZWid / 2); + + xpos = connBody->GetDX(); + ypos = connBody->GetDY() - sIBConnSideHole2YPos; + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + sideHole2Box->GetDZ(); + TGeoTranslation* sideHole2BTrans = new TGeoTranslation("sideHole2TransBC", xpos, ypos, zpos); + sideHole2BTrans->RegisterYourself(); + + TGeoTubeSeg* sideHole2TubeSeg = + new TGeoTubeSeg("sideHole2CTS", 0, sIBConnSideHole2YWid / 2, sIBConnSideHole2XWid, 180, 360); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen); + TGeoCombiTrans* sideHole2TSTrans1 = + new TGeoCombiTrans("sideHole2TSTrans1C", xpos, ypos, zpos, new TGeoRotation("", -90, 90, 0)); + sideHole2TSTrans1->RegisterYourself(); + + zpos = -connBody->GetDZ() + (sIBConnSideHole2ZPos - sIBConnTailZLen) + 2 * sideHole2Box->GetDZ(); + TGeoCombiTrans* sideHole2TSTrans2 = + new TGeoCombiTrans("sideHole2TSTrans2C", xpos, ypos, zpos, new TGeoRotation("", -90, -90, 0)); + sideHole2TSTrans2->RegisterYourself(); + + TGeoCompositeShape* connBodySh = new TGeoCompositeShape( + "connBodyC-tube2HoleC:tubes2Trans1C-tube2HoleC:tubes2Trans2C-tube3HoleC:tubes3TransC-tube4HoleC:tubes4Trans1C-" + "tube4HoleC:tubes4Trans2C-sideHole1C:sideHole1TransC-sideHole2CTS:sideHole2TSTrans1C-sideHole2CTS:" + "sideHole2TSTrans2C-sideHole2CB:sideHole2TransBC-connRoundHoleC:roundHoleTransC-connInsertHoleC:insertHoleTransC"); + + TGeoVolume* connBlockBody = new TGeoVolume("IBConnectorBlockBodyCSide", connBodySh, medPEEK); + connBlockBody->SetFillColor(42); // Brownish shade + connBlockBody->SetLineColor(42); + + // ...and the tail + xv[0] = sIBConnectorXWidth / 2; + yv[0] = sIBConnTailYShift; + xv[1] = xv[0]; + yv[1] = sIBConnTailYMid; + xv[2] = xv[1] - (sIBConnectorYTot - sIBConnTailYMid) / tanD(90 - sIBConnTailOpenPhi / 2); + yv[2] = sIBConnectorYTot; + + for (Int_t i = 0; i < 3; i++) { + xv[3 + i] = -xv[2 - i]; + yv[3 + i] = yv[2 - i]; + } + + TGeoXtru* connTail = new TGeoXtru(2); + connTail->SetName("connTailC"); + connTail->DefinePolygon(6, xv, yv); + connTail->DefineSection(0, 0); + connTail->DefineSection(1, sIBConnTailZLen); + + TGeoTube* connTubeHole1 = new TGeoTube("tube1HoleC", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 1.5); + + xpos = sIBConnTubesXDist / 2; + ypos = sIBConnTubesYPos; + zpos = connTail->GetZ(1) / 2; + TGeoTranslation* connTubes1Trans1 = new TGeoTranslation("tubes1Trans1C", -xpos, ypos, zpos); + connTubes1Trans1->RegisterYourself(); + TGeoTranslation* connTubes1Trans2 = new TGeoTranslation("tubes1Trans2C", xpos, ypos, zpos); + connTubes1Trans2->RegisterYourself(); + + TGeoCompositeShape* connTailSh = + new TGeoCompositeShape("connTailC-tube1HoleC:tubes1Trans1C-tube1HoleC:tubes1Trans2C"); + + TGeoVolume* connBlockTail = new TGeoVolume("IBConnectorBlockTailCSide", connTailSh, medPEEK); + connBlockTail->SetFillColor(42); // Brownish shade + connBlockTail->SetLineColor(42); + + // The plug, a Pcon + zlen = sIBConnPlugTotLen - sIBConnPlugInnerLen; + TGeoPcon* connPlugSh = new TGeoPcon(0, 360, 4); + connPlugSh->DefineSection(0, 0., 0., sIBConnTubeHole2D / 2); + connPlugSh->DefineSection(1, zlen, 0., sIBConnTubeHole2D / 2); + connPlugSh->DefineSection(2, zlen, sIBConnPlugInnerD / 2, sIBConnTubeHole2D / 2); + connPlugSh->DefineSection(3, sIBConnPlugTotLen, sIBConnPlugInnerD / 2, sIBConnTubeHole2D / 2); + + TGeoVolume* connPlug = new TGeoVolume("IBConnectorPlugC", connPlugSh, medPEEK); + connPlug->SetFillColor(44); // Brownish shade (a bit darker to spot it) + connPlug->SetLineColor(44); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sIBConnectorXWidth; + ylen = sIBConnBodyYHeight; + zlen = sIBConnectBlockZLen - sIBConnTailZLen; + + TGeoBBox* connBox = new TGeoBBox("connBoxC", xlen / 2, ylen / 2, zlen / 2); + + ypos = -sIBConnectorYTot / 2 + connBox->GetDY(); + TGeoTranslation* transBodyC = new TGeoTranslation("transBodyC", 0, ypos, 0); + transBodyC->RegisterYourself(); + + ypos = -sIBConnectorYTot / 2; + zpos = -connBox->GetDZ() - connTail->GetZ(1); + TGeoTranslation* transTailC = new TGeoTranslation("transTailC", 0, ypos, zpos); + transTailC->RegisterYourself(); + + TGeoTube* connTubeHollow = new TGeoTube("tubeHollowC", 0, sIBConnTubeHole1D / 2, sIBConnTubeHole1ZLen / 2); + + xpos = sIBConnTubesXDist / 2; + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = -connBox->GetDZ() - connTail->GetZ(1) + sIBConnTubeHole1ZLen / 2; + TGeoTranslation* connTubeHollTrans1 = new TGeoTranslation("tubeHollTrans1C", -xpos, ypos, zpos); + connTubeHollTrans1->RegisterYourself(); + TGeoTranslation* connTubeHollTrans2 = new TGeoTranslation("tubeHollTrans2C", xpos, ypos, zpos); + connTubeHollTrans2->RegisterYourself(); + + zpos = connBody->GetDZ() - (sIBConnectBlockZLen - sIBConnTubeHole3ZPos); + TGeoTranslation* connTubes2Trans1Body = new TGeoTranslation("tubes2Trans1BC", -xpos, ypos, -zpos); + connTubes2Trans1Body->RegisterYourself(); + TGeoTranslation* connTubes2Trans2Body = new TGeoTranslation("tubes2Trans2BC", xpos, ypos, -zpos); + connTubes2Trans2Body->RegisterYourself(); + + TGeoCompositeShape* connBoxSh = new TGeoCompositeShape( + "connBoxC:transBodyC-tube2HoleC:tubes2Trans1BC-tube2HoleC:tubes2Trans2BC+connTailC:transTailC-tubeHollowC:tubeHollTrans1C-" + "tubeHollowC:tubeHollTrans2C"); + + TGeoVolume* connBoxCSide = new TGeoVolume("IBConnectorCSide", connBoxSh, medAir); + + // Finally build up the connector + // (NB: the origin is in the connBox, i.e. w/o the tail in Z) + ypos = -connBoxSh->GetDY(); + zpos = -connBodySh->GetDZ() - connTail->GetZ(1); + connBoxCSide->AddNode(connBlockTail, 1, new TGeoTranslation(0, ypos, zpos)); + + ypos = -connBoxSh->GetDY() + connBody->GetDY(); + connBoxCSide->AddNode(connBlockBody, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = connBox->GetDX(); + ypos = -sIBConnectorYTot / 2 + sIBConnTubesYPos; + zpos = connBody->GetDZ() - (sIBConnectBlockZLen - sIBConnTubeHole3ZPos); + connBoxCSide->AddNode(connPlug, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90, -90, 90))); +} + +TGeoVolume* V3Layer::createStaveOuterB(const TGeoManager* mgr) +{ + // Create the chip stave for the Outer Barrel + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 20 Dec 2013 Mario Sitta + // Updated: 12 Mar 2014 Mario Sitta + // Updated: 19 Jul 2017 Mario Sitta O2 version + // + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + mechStavVol = createStaveModelOuterBDummy(mgr); + break; + case Detector::kOBModel0: + case Detector::kOBModel1: + LOG(FATAL) << "Stave model " << mStaveModel << " obsolete and no longer supported"; + break; + case Detector::kOBModel2: + mechStavVol = createStaveModelOuterB2(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + return mechStavVol; +} + +TGeoVolume* V3Layer::createStaveModelOuterBDummy(const TGeoManager*) const +{ + // + // Create dummy stave + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 20 Dec 2013 Mario Sitta + // + + // Done, return the stave structure + return nullptr; +} + +TGeoVolume* V3Layer::createStaveModelOuterB2(const TGeoManager* mgr) +{ + // + // Create the mechanical half stave structure + // for the Outer Barrel as in TDR + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 20 Nov 2013 Anastasia Barbano + // Updated: 16 Jan 2014 Mario Sitta + // Updated: 24 Feb 2014 Mario Sitta + // Updated: 11 Nov 2014 Mario Sitta Model2 + // Updated: 03 Dec 2014 Mario Sitta Revised with C.Gargiulo latest infos + // Updated: 19 Jul 2017 Mario Sitta O2 version + // Updated: 04 Aug 2018 Mario Sitta Updated geometry + // Updated: 25 Aug 2018 Mario Sitta To latest blueprints + // + + // Local parameters + Double_t yFlex1 = sOBFlexCableAlThick; + Double_t yFlex2 = sOBFlexCableKapThick; + Double_t flexOverlap = 5; // to be checked - unused for the time being + Double_t yCFleece = sOBCarbonFleeceThick; + Double_t yGraph = sOBGraphiteFoilThick; + Double_t xHalfSt, yHalfSt; + + Double_t xmod, ymod, zmod, ypowbus; + Double_t xtru[12], ytru[12]; + Double_t xpos, ypos, ypos1, zpos /*, zpos5cm*/; + Double_t xlen, ylen, zlen; + const Int_t nameLen = 30; + char volname[nameLen]; + + Double_t rCoolMin, rCoolMax; + rCoolMin = sOBCoolTubeInnerD / 2; + + rCoolMax = rCoolMin + sOBCoolTubeThick; + + // First create all needed shapes + + TGeoVolume* moduleVol = createModuleOuterB(); + moduleVol->SetVisibility(kTRUE); + xmod = (static_cast(moduleVol->GetShape()))->GetDX(); + ymod = (static_cast(moduleVol->GetShape()))->GetDY(); + zmod = (static_cast(moduleVol->GetShape()))->GetDZ(); + + if (mLayerNumber <= 4) + zlen = sOBColdPlateZLenML / 2; // Middle Layer + else + zlen = sOBColdPlateZLenOL / 2; // Outer Layer + + xlen = sOBColdPlateXWidth / 2; + + TGeoBBox* coldPlate = new TGeoBBox("ColdPlate", xlen, sOBColdPlateThick / 2, zlen); + + TGeoBBox* fleeccent = new TGeoBBox("FleeceCent", xlen, yCFleece / 2, zlen); + + TGeoTube* coolTube = new TGeoTube("CoolingTube", rCoolMin, rCoolMax, zlen); + TGeoTube* coolWater = new TGeoTube("CoolingWater", 0., rCoolMin, zlen); + + xlen = sOBColdPlateXWidth / 2 - sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + TGeoBBox* graphlat = new TGeoBBox("GraphLateral", xlen / 2, yGraph / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax(); + TGeoBBox* graphmid = new TGeoBBox("GraphMiddle", xlen, yGraph / 2, zlen); + + ylen = coolTube->GetRmax() - yGraph; + TGeoBBox* graphvert = new TGeoBBox("GraphVertical", yGraph / 2, ylen / 2, zlen); + + TGeoTubeSeg* graphtub = new TGeoTubeSeg("GraphTube", rCoolMax, rCoolMax + yGraph, zlen, 180., 360.); + + xlen = sOBColdPlateXWidth / 2 - sOBCoolTubeXDist / 2 - coolTube->GetRmax() - yGraph; + TGeoBBox* fleeclat = new TGeoBBox("FleecLateral", xlen / 2, yCFleece / 2, zlen); + + xlen = sOBCoolTubeXDist / 2 - coolTube->GetRmax() - yGraph; + TGeoBBox* fleecmid = new TGeoBBox("FleecMiddle", xlen, yCFleece / 2, zlen); + + ylen = coolTube->GetRmax() - yGraph - yCFleece; + TGeoBBox* fleecvert = new TGeoBBox("FleecVertical", yCFleece / 2, ylen / 2, zlen); + + TGeoTubeSeg* fleectub = + new TGeoTubeSeg("FleecTube", rCoolMax + yGraph, rCoolMax + yCFleece + yGraph, zlen, 180., 360.); + + TGeoTube* gammaConvRod; + if (mAddGammaConv) + gammaConvRod = new TGeoTube("GammaConver", 0, 0.5 * mGammaConvDiam, zlen - sOBCPConnHollowZLen); + + // TGeoBBox* flex1_5cm = new TGeoBBox("Flex1MV_5cm", xHalfSt, yFlex1 / 2, flexOverlap / 2); + // TGeoBBox* flex2_5cm = new TGeoBBox("Flex2MV_5cm", xHalfSt, yFlex2 / 2, flexOverlap / 2); + + // The power bus + TGeoVolume* powerBusVol = createOBPowerBiasBuses(zlen); + powerBusVol->SetVisibility(kTRUE); + ypowbus = (static_cast(powerBusVol->GetShape()))->GetDY(); + + // The half stave container (an XTru to avoid overlaps between neightbours) + xHalfSt = xmod; // add the cross cables when done! + yHalfSt = ypowbus + ymod + coldPlate->GetDY() + 2 * fleeccent->GetDY() + graphlat->GetDY() + fleeclat->GetDY(); + if (mAddGammaConv) + yHalfSt += mGammaConvDiam; + + xtru[0] = xHalfSt; + ytru[0] = 0; + xtru[1] = xtru[0]; + ytru[1] = -2 * yHalfSt; + xtru[2] = sOBCoolTubeXDist / 2 + fleectub->GetRmax(); + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[2] - (coolTube->GetRmax() + fleectub->GetRmax()); + if (mAddGammaConv) + ytru[3] -= mGammaConvDiam; + xtru[4] = sOBCoolTubeXDist / 2 - fleectub->GetRmax(); + ytru[4] = ytru[3]; + xtru[5] = xtru[4]; + ytru[5] = ytru[2]; + for (Int_t i = 0; i < 6; i++) { + xtru[6 + i] = -xtru[5 - i]; + ytru[6 + i] = ytru[5 - i]; + } + TGeoXtru* halfStaveCent = new TGeoXtru(2); + halfStaveCent->DefinePolygon(12, xtru, ytru); + halfStaveCent->DefineSection(0, -zlen); + halfStaveCent->DefineSection(1, zlen); + snprintf(volname, nameLen, "staveCentral%d", mLayerNumber); + halfStaveCent->SetName(volname); + + // The connectors' containers + TGeoBBox* connAside = new TGeoBBox("connAsideOB", sOBCPConnectorXWidth / 2, sOBCPConnBlockYHei / 2, + (sOBCPConnBlockZLen + sOBCPConnAFitZLen - sOBCPConnAFitZIn) / 2); + + TGeoBBox* connCside = + new TGeoBBox("connCsideOB", sOBCPConnectorXWidth / 2, sOBCPConnBlockYHei / 2, sOBCPConnBlockZLen / 2); + + // The StaveStruct container, a Composite Shape + if (mAddGammaConv) + yHalfSt -= mGammaConvDiam; + ypos = 2 * yHalfSt + connAside->GetDY() - sOBCPConnHollowYHei; + zpos = zlen + connAside->GetDZ() - sOBCPConnHollowZLen; + snprintf(volname, nameLen, "transAsideOB%d", mLayerNumber); + TGeoTranslation* transAside = new TGeoTranslation(volname, 0, -ypos, zpos); + transAside->RegisterYourself(); + + zpos = zlen + connCside->GetDZ() - sOBCPConnHollowZLen; + snprintf(volname, nameLen, "transCsideOB%d", mLayerNumber); + TGeoTranslation* transCside = new TGeoTranslation(volname, 0, -ypos, -zpos); + transCside->RegisterYourself(); + + char componame[70]; + snprintf(componame, 70, "staveCentral%d+connAsideOB:transAsideOB%d+connCsideOB:transCsideOB%d", mLayerNumber, + mLayerNumber, mLayerNumber); + + TGeoCompositeShape* halfStave = new TGeoCompositeShape(componame); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAluminum = mgr->GetMedium("EC0_ALUMINUM$"); + TGeoMedium* medK13D2U120 = mgr->GetMedium("EC0_K13D2U120$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medWater = mgr->GetMedium("EC0_WATER$"); + TGeoMedium* medCarbonFleece = mgr->GetMedium("EC0_CarbonFleece$"); + TGeoMedium* medFGS003 = mgr->GetMedium("EC0_FGS003$"); // amec thermasol + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medTungsten = mgr->GetMedium("EC0_TUNGSTEN$"); + + TGeoVolume* coldPlateVol = new TGeoVolume("ColdPlateVol", coldPlate, medK13D2U120); + coldPlateVol->SetLineColor(kYellow - 3); + coldPlateVol->SetFillColor(coldPlateVol->GetLineColor()); + coldPlateVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleeccentVol = new TGeoVolume("CarbonFleeceCentral", fleeccent, medCarbonFleece); + fleeccentVol->SetLineColor(kViolet); + fleeccentVol->SetFillColor(fleeccentVol->GetLineColor()); + fleeccentVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* coolTubeVol = new TGeoVolume("CoolingTubeVol", coolTube, medKapton); + coolTubeVol->SetLineColor(kGray); + coolTubeVol->SetFillColor(coolTubeVol->GetLineColor()); + coolTubeVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* coolWaterVol; + coolWaterVol = new TGeoVolume("CoolingWaterVol", coolWater, medWater); + coolWaterVol->SetLineColor(kBlue); + coolWaterVol->SetFillColor(coolWaterVol->GetLineColor()); + coolWaterVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphlatVol = new TGeoVolume("GraphiteFoilLateral", graphlat, medFGS003); + graphlatVol->SetLineColor(kGreen); + graphlatVol->SetFillColor(graphlatVol->GetLineColor()); + graphlatVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphmidVol = new TGeoVolume("GraphiteFoilMiddle", graphmid, medFGS003); + graphmidVol->SetLineColor(kGreen); + graphmidVol->SetFillColor(graphmidVol->GetLineColor()); + graphmidVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphvertVol = new TGeoVolume("GraphiteFoilVertical", graphvert, medFGS003); + graphvertVol->SetLineColor(kGreen); + graphvertVol->SetFillColor(graphvertVol->GetLineColor()); + graphvertVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* graphtubVol = new TGeoVolume("GraphiteFoilPipeCover", graphtub, medFGS003); + graphtubVol->SetLineColor(kGreen); + graphtubVol->SetFillColor(graphtubVol->GetLineColor()); + graphtubVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleeclatVol = new TGeoVolume("CarbonFleeceLateral", fleeclat, medCarbonFleece); + fleeclatVol->SetLineColor(kViolet); + fleeclatVol->SetFillColor(fleeclatVol->GetLineColor()); + fleeclatVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleecmidVol = new TGeoVolume("CarbonFleeceMiddle", fleecmid, medCarbonFleece); + fleecmidVol->SetLineColor(kViolet); + fleecmidVol->SetFillColor(fleecmidVol->GetLineColor()); + fleecmidVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleecvertVol = new TGeoVolume("CarbonFleeceVertical", fleecvert, medCarbonFleece); + fleecvertVol->SetLineColor(kViolet); + fleecvertVol->SetFillColor(fleecvertVol->GetLineColor()); + fleecvertVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* fleectubVol = new TGeoVolume("CarbonFleecePipeCover", fleectub, medCarbonFleece); + fleectubVol->SetLineColor(kViolet); + fleectubVol->SetFillColor(fleectubVol->GetLineColor()); + fleectubVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* gammaConvRodVol; + if (mAddGammaConv) { + gammaConvRodVol = new TGeoVolume("GammaConversionRod", gammaConvRod, medTungsten); + gammaConvRodVol->SetLineColor(kBlack); + gammaConvRodVol->SetFillColor(gammaConvRodVol->GetLineColor()); + gammaConvRodVol->SetFillStyle(4000); // 0% transparent + } + + snprintf(volname, nameLen, "%s%d", GeometryTGeo::getEC0HalfStavePattern(), mLayerNumber); + TGeoVolume* halfStaveVol = new TGeoVolume(volname, halfStave, medAir); + // halfStaveVol->SetLineColor(12); + // halfStaveVol->SetFillColor(12); + // halfStaveVol->SetVisibility(kTRUE); + + // TGeoVolume* flex1_5cmVol = new TGeoVolume("Flex1Vol5cm", flex1_5cm, medAluminum); + // TGeoVolume* flex2_5cmVol = new TGeoVolume("Flex2Vol5cm", flex2_5cm, medKapton); + + // flex1_5cmVol->SetLineColor(kRed); + // flex2_5cmVol->SetLineColor(kGreen); + + // Now build up the half stave + ypos = -ypowbus; + halfStaveVol->AddNode(powerBusVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos -= (ypowbus + ymod); + for (Int_t j = 0; j < mNumberOfModules; j++) { + zpos = zlen - j * (2 * zmod + sOBModuleGap) - zmod; + halfStaveVol->AddNode(moduleVol, j, new TGeoTranslation(0, ypos, zpos)); + mHierarchy[kModule]++; + } + + ypos -= ymod; + + ypos -= fleeccent->GetDY(); + if (mBuildLevel < 6) // Carbon + halfStaveVol->AddNode(fleeccentVol, 1, new TGeoTranslation(0, ypos, 0)); + ypos -= fleeccent->GetDY(); + + ypos -= coldPlate->GetDY(); + if (mBuildLevel < 6) // Carbon + halfStaveVol->AddNode(coldPlateVol, 1, new TGeoTranslation(0, ypos, 0)); + ypos -= coldPlate->GetDY(); + + ypos -= fleeccent->GetDY(); + if (mBuildLevel < 6) // Carbon + halfStaveVol->AddNode(fleeccentVol, 2, new TGeoTranslation(0, ypos, 0)); + + xpos = sOBCoolTubeXDist / 2; + ypos1 = ypos - (fleeccent->GetDY() + coolTube->GetRmax()); + if (mBuildLevel < 4) { // Water + halfStaveVol->AddNode(coolWaterVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(coolWaterVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + } + + if (mBuildLevel < 5) { // Kapton + halfStaveVol->AddNode(coolTubeVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(coolTubeVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + } + + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(graphtubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphtubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halfStaveVol->AddNode(fleectubVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleectubVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + } + + xpos = sOBColdPlateXWidth / 2 - graphlat->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + graphlat->GetDY()); + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(graphlatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphlatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halfStaveVol->AddNode(graphmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = sOBColdPlateXWidth / 2 - 2 * graphlat->GetDX() + graphvert->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + graphvert->GetDY()); + halfStaveVol->AddNode(graphvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = graphmid->GetDX() - graphvert->GetDX(); + halfStaveVol->AddNode(graphvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(graphvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + } + + xpos = sOBColdPlateXWidth / 2 - fleeclat->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + fleeclat->GetDY()); + if (mBuildLevel < 6) { // Carbon + halfStaveVol->AddNode(fleeclatVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleeclatVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + + halfStaveVol->AddNode(fleecmidVol, 1, new TGeoTranslation(0, ypos1, 0)); + + xpos = sOBColdPlateXWidth / 2 - 2 * fleeclat->GetDX() + fleecvert->GetDX(); + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + 2 * fleeclat->GetDY() + fleecvert->GetDY()); + halfStaveVol->AddNode(fleecvertVol, 1, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleecvertVol, 2, new TGeoTranslation(xpos, ypos1, 0)); + xpos = fleecmid->GetDX() - fleecvert->GetDX(); + halfStaveVol->AddNode(fleecvertVol, 3, new TGeoTranslation(-xpos, ypos1, 0)); + halfStaveVol->AddNode(fleecvertVol, 4, new TGeoTranslation(xpos, ypos1, 0)); + } + + // Add the Gamma Converter Rod (only on Layer 3) - M.S. 17 Oct 2016 + if (mAddGammaConv) { + xpos = mGammaConvXPos; + ypos1 = ypos - (fleeccent->GetDY() + 2 * graphlat->GetDY() + 2 * fleeclat->GetDY() + gammaConvRod->GetRmax()); + halfStaveVol->AddNode(gammaConvRodVol, 1, new TGeoTranslation(xpos, ypos1, 0)); + } + + // Add the end-stave connectors + TGeoVolume *connectorASide, *connectorCSide; + + // Check whether we have already all pieces + // Otherwise create them + connectorASide = mgr->GetVolume("OBColdPlateConnectorASide"); + + if (!connectorASide) { + createOBColdPlateConnectors(); + connectorASide = mgr->GetVolume("OBColdPlateConnectorASide"); + } + connectorCSide = mgr->GetVolume("OBColdPlateConnectorCSide"); + + ypos = 2 * yHalfSt + (static_cast(connectorASide->GetShape()))->GetDY() - sOBCPConnHollowYHei; + zpos = zlen + (static_cast(connectorASide->GetShape()))->GetDZ() - sOBCPConnHollowZLen; + halfStaveVol->AddNode(connectorASide, 1, new TGeoCombiTrans(0, -ypos, zpos, new TGeoRotation("", 180, 0, 0))); + + zpos = zlen + (static_cast(connectorCSide->GetShape()))->GetDZ() - sOBCPConnHollowZLen; + halfStaveVol->AddNode(connectorCSide, 1, new TGeoCombiTrans(0, -ypos, -zpos, new TGeoRotation("", 180, 0, 0))); + + // Done, return the half stave structure + return halfStaveVol; +} + +TGeoVolume* V3Layer::createOBPowerBiasBuses(const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the OB Power Bus and Bias Bus cables + // + // Input: + // zcable : the cable half Z length + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume with both the Power and the Bias Buses + // + // Created: 05 Aug 2018 Mario Sitta + // Updated: 06 Sep 2018 Mario Sitta + // + + Double_t xcable, ytot, ypos; + + // First create all needed shapes + xcable = sOBPowerBusXWidth / 2; + TGeoBBox* gndPB = new TGeoBBox(xcable, sOBPowerBusAlThick / 2, zcable); + TGeoBBox* dielPB = new TGeoBBox(xcable, sOBPowerBusDielThick / 2, zcable); + TGeoBBox* kapPB = new TGeoBBox(xcable, sOBPowerBusKapThick / 2, zcable); + xcable *= sOBPowerBusAlFrac; + TGeoBBox* topPB = new TGeoBBox(xcable, sOBPowerBusAlThick / 2, zcable); + + xcable = sOBBiasBusXWidth / 2; + TGeoBBox* botBB = new TGeoBBox(xcable, sOBBiasBusAlThick / 2, zcable); + TGeoBBox* dielBB = new TGeoBBox(xcable, sOBBiasBusDielThick / 2, zcable); + TGeoBBox* kapBB = new TGeoBBox(xcable, sOBBiasBusKapThick / 2, zcable); + xcable *= sOBBiasBusAlFrac; + TGeoBBox* topBB = new TGeoBBox(xcable, sOBBiasBusAlThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medAluminum = mgr->GetMedium("EC0_ALUMINUM$"); + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + TGeoVolume* gndPBVol = new TGeoVolume("PowerBusGround", gndPB, medAluminum); + gndPBVol->SetLineColor(kCyan); + gndPBVol->SetFillColor(gndPBVol->GetLineColor()); + gndPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* dielPBVol = new TGeoVolume("PowerBusDielectric", dielPB, medKapton); + dielPBVol->SetLineColor(kBlue); + dielPBVol->SetFillColor(dielPBVol->GetLineColor()); + dielPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* kapPBVol = new TGeoVolume("PowerBusKapton", kapPB, medKapton); + kapPBVol->SetLineColor(kBlue); + kapPBVol->SetFillColor(kapPBVol->GetLineColor()); + kapPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* topPBVol = new TGeoVolume("PowerBusTop", topPB, medAluminum); + topPBVol->SetLineColor(kCyan); + topPBVol->SetFillColor(topPBVol->GetLineColor()); + topPBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* botBBVol = new TGeoVolume("BiasBusBottom", botBB, medAluminum); + botBBVol->SetLineColor(kCyan); + botBBVol->SetFillColor(botBBVol->GetLineColor()); + botBBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* dielBBVol = new TGeoVolume("BiasBusDielectric", dielBB, medKapton); + dielBBVol->SetLineColor(kBlue); + dielBBVol->SetFillColor(dielBBVol->GetLineColor()); + dielBBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* kapBBVol = new TGeoVolume("BiasBusKapton", kapBB, medKapton); + kapBBVol->SetLineColor(kBlue); + kapBBVol->SetFillColor(kapBBVol->GetLineColor()); + kapBBVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* topBBVol = new TGeoVolume("BiasBusTop", topBB, medAluminum); + topBBVol->SetLineColor(kCyan); + topBBVol->SetFillColor(topBBVol->GetLineColor()); + topBBVol->SetFillStyle(4000); // 0% transparent + + // Finally the volume containing both the Power Bus and the Bias Bus + xcable = sOBPowerBusXWidth / 2; + ytot = 2 * kapPB->GetDY() + topPB->GetDY() + dielPB->GetDY() + gndPB->GetDY() + 2 * kapBB->GetDY() + topBB->GetDY() + dielBB->GetDY() + botBB->GetDY(); + + TGeoBBox* pnbBus = new TGeoBBox(xcable, ytot, zcable); + + TGeoVolume* pnbBusVol = new TGeoVolume("OBPowerBiasBus", pnbBus, medAir); + + // Volumes are piled up from bottom to top + ypos = -pnbBus->GetDY() + kapPB->GetDY(); + if (mBuildLevel < 5) // Kapton + pnbBusVol->AddNode(kapPBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (kapPB->GetDY() + gndPB->GetDY()); + if (mBuildLevel < 2) // Aluminum + pnbBusVol->AddNode(gndPBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (gndPB->GetDY() + dielPB->GetDY()); + if (mBuildLevel < 5) // Kapton + pnbBusVol->AddNode(dielPBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (dielPB->GetDY() + topPB->GetDY()); + if (mBuildLevel < 2) // Aluminum + pnbBusVol->AddNode(topPBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (topPB->GetDY() + kapPB->GetDY()); + if (mBuildLevel < 5) // Kapton + pnbBusVol->AddNode(kapPBVol, 2, new TGeoTranslation(0, ypos, 0)); + + // + ypos += (kapPB->GetDY() + kapBB->GetDY()); + if (mBuildLevel < 5) // Kapton + pnbBusVol->AddNode(kapBBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (kapBB->GetDY() + botBB->GetDY()); + if (mBuildLevel < 2) // Aluminum + pnbBusVol->AddNode(botBBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (botBB->GetDY() + dielBB->GetDY()); + if (mBuildLevel < 5) // Kapton + pnbBusVol->AddNode(dielBBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (dielBB->GetDY() + topBB->GetDY()); + if (mBuildLevel < 2) // Aluminum + pnbBusVol->AddNode(topBBVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (topBB->GetDY() + kapBB->GetDY()); + if (mBuildLevel < 5) // Kapton + pnbBusVol->AddNode(kapBBVol, 2, new TGeoTranslation(0, ypos, 0)); + + // + return pnbBusVol; +} + +void V3Layer::createOBColdPlateConnectors() +{ + // + // Create the Cold Plate connectors for OB half staves + // (simply call the actual creator methods) + // + // Input: + // + // Output: + // + // Return: + // + // Created: 26 May 2015 Mario Sitta + // + + createOBColdPlateConnectorsASide(); + createOBColdPlateConnectorsCSide(); +} + +void V3Layer::createOBColdPlateConnectorsASide() +{ + // + // Create the A-Side end-stave connectors for IB staves + // + // Input: + // + // Output: + // + // Return: + // + // Created: 26 May 2015 Mario Sitta + // Updated: 20 Jul 2017 Mario Sitta O2 version + // Updated: 15 Oct 2018 Mario Sitta To latest blueprints + // + + // The geoManager + const TGeoManager* mgr = gGeoManager; + + // Local variables + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("EC0_PEEKCF30$"); + TGeoMedium* medInox304 = mgr->GetMedium("EC0_INOX304$"); + + // First create all elements + + // The connector block, a Composite Shape + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen; + TGeoBBox* connBlock = new TGeoBBox("connBlockA", xlen / 2, ylen / 2, zlen / 2); + + xv[0] = sOBCPConnectorXWidth * 0.6; + yv[0] = -sOBCPConnHollowYHei; + xv[1] = xv[0]; + yv[1] = sOBCPConnHollowYHei; + xv[2] = sOBCPConnTubesXDist / 2 + sOBCPConnTubeHole1D / 2; + yv[2] = yv[1]; + xv[3] = xv[2]; + yv[3] = sOBCPConnTubesYPos; + xv[4] = sOBCPConnTubesXDist / 2 - sOBCPConnTubeHole1D / 2; + yv[4] = yv[3]; + xv[5] = xv[4]; + yv[5] = yv[2]; + + for (Int_t i = 0; i < 6; i++) { + xv[6 + i] = -xv[5 - i]; + yv[6 + i] = yv[5 - i]; + } + + TGeoXtru* connBlockHoll = new TGeoXtru(2); + connBlockHoll->SetName("connBlockHollA"); + connBlockHoll->DefinePolygon(12, xv, yv); + connBlockHoll->DefineSection(0, -sOBCPConnHollowZLen); + connBlockHoll->DefineSection(1, sOBCPConnHollowZLen); + + ypos = -connBlock->GetDY(); + zpos = -connBlock->GetDZ(); + TGeoTranslation* transBlockHoll = new TGeoTranslation("transBlockHollA", 0, ypos, zpos); + transBlockHoll->RegisterYourself(); + + xlen = sOBCPConnSquareHoleX / 2; + ylen = sOBCPConnBlockYHei / 1.5; + zlen = sOBCPConnSquareHoleZ / 2; + TGeoBBox* connSquareHole = new TGeoBBox("connASquareHole", xlen, ylen, zlen); + + zpos = + -connBlock->GetDZ() + (sOBCPConnSqrHoleZPos + connSquareHole->GetDZ()); + TGeoTranslation* transSquareHole = new TGeoTranslation("transASquareHole", 0, 0, zpos); + transSquareHole->RegisterYourself(); + + zlen = sOBCPConnTubeHole1Z; + TGeoTube* connTubeHole1 = new TGeoTube("tube1AHole", 0, sOBCPConnTubeHole1D / 2, zlen); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = connBlock->GetDZ(); + TGeoTranslation* trans1Tube1AHole = new TGeoTranslation("trans1Tube1AHole", -xpos, ypos, -zpos); + trans1Tube1AHole->RegisterYourself(); + TGeoTranslation* trans2Tube1AHole = new TGeoTranslation("trans2Tube1AHole", xpos, ypos, -zpos); + trans2Tube1AHole->RegisterYourself(); + + zlen = sOBCPConnBlockZLen; + TGeoTube* connTubeHole2 = new TGeoTube("tube2AHole", 0, sOBCPConnTubeHole2D / 2, zlen); + + TGeoTranslation* trans1Tube2AHole = new TGeoTranslation("trans1Tube2AHole", -xpos, ypos, 0); + trans1Tube2AHole->RegisterYourself(); + TGeoTranslation* trans2Tube2AHole = new TGeoTranslation("trans2Tube2AHole", xpos, ypos, 0); + trans2Tube2AHole->RegisterYourself(); + + zlen = sOBCPConnAFitZIn; + TGeoTube* connFitHole = new TGeoTube("fitAHole", 0, sOBCPConnFitHoleD / 2, zlen); + + TGeoTranslation* trans1FitAHole = new TGeoTranslation("trans1FitAHole", -xpos, ypos, zpos); + trans1FitAHole->RegisterYourself(); + TGeoTranslation* trans2FitAHole = new TGeoTranslation("trans2FitAHole", xpos, ypos, zpos); + trans2FitAHole->RegisterYourself(); + + TGeoCompositeShape* connBlockSh = new TGeoCompositeShape( + "connBlockA-connBlockHollA:transBlockHollA-connASquareHole:transASquareHole-tube1AHole:trans1Tube1AHole-tube1AHole:" + "trans2Tube1AHole-tube2AHole:trans1Tube2AHole-tube2AHole:trans2Tube2AHole-fitAHole:trans1FitAHole-fitAHole:" + "trans2FitAHole"); + + TGeoVolume* connBlockA = new TGeoVolume("OBColdPlateConnectorBlockASide", connBlockSh, medPEEK); + connBlockA->SetFillColor(42); // Brownish shade + connBlockA->SetLineColor(42); + + // The fitting tubes, a Tube + Double_t rmin = sOBCPConnAFitExtD / 2 - sOBCPConnAFitThick; + TGeoTube* connFitSh = new TGeoTube(rmin, sOBCPConnAFitExtD / 2, sOBCPConnAFitZLen / 2); + + TGeoVolume* connFit = new TGeoVolume("OBColdPlateConnectorFitting", connFitSh, medInox304); + connFit->SetFillColor(kGray); + connFit->SetLineColor(kGray); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen + (sOBCPConnAFitZLen - sOBCPConnAFitZIn); + TGeoBBox* connBox = new TGeoBBox("connectorOBCPA", xlen / 2, ylen / 2, zlen / 2); + + ypos = -connBox->GetDY(); + zpos = -connBox->GetDZ(); + TGeoTranslation* transBoxHoll = new TGeoTranslation("transBoxHollA", 0, ypos, zpos); + transBoxHoll->RegisterYourself(); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBox->GetDY() + sOBCPConnTubesYPos; + zpos = connBox->GetDZ(); + TGeoTranslation* trans1BoxHole = new TGeoTranslation("trans1BoxAHole", -xpos, ypos, -zpos); + trans1BoxHole->RegisterYourself(); + TGeoTranslation* trans2BoxHole = new TGeoTranslation("trans2BoxAHole", xpos, ypos, -zpos); + trans2BoxHole->RegisterYourself(); + + TGeoCompositeShape* connectSh = new TGeoCompositeShape( + "connectorOBCPA-connBlockHollA:transBoxHollA-tube1AHole:trans1BoxAHole-tube1AHole:trans2BoxAHole"); + + TGeoVolume* connectorASide = new TGeoVolume("OBColdPlateConnectorASide", connectSh, medAir); + + // Finally build up the connector + zpos = -connectSh->GetDZ() + connBlock->GetDZ(); + connectorASide->AddNode(connBlockA, 1, new TGeoTranslation(0, 0, zpos)); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = connectSh->GetDZ() - connFitSh->GetDz(); + connectorASide->AddNode(connFit, 1, new TGeoTranslation(-xpos, ypos, zpos)); + connectorASide->AddNode(connFit, 2, new TGeoTranslation(xpos, ypos, zpos)); +} + +void V3Layer::createOBColdPlateConnectorsCSide() +{ + // + // Create the C-Side end-stave connectors for IB staves + // + // Input: + // + // Output: + // + // Return: + // + // Created: 29 May 2015 Mario Sitta + // Updated: 20 Jul 2017 Mario Sitta O2 version + // Updated: 15 Oct 2018 Mario Sitta To latest blueprints + // + + // The geoManager + const TGeoManager* mgr = gGeoManager; + + // Local variables + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + // Gather all material pointers + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medPEEK = mgr->GetMedium("EC0_PEEKCF30$"); + + // First create all elements + + // The connector block, a Composite Shape + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen; + TGeoBBox* connBlock = new TGeoBBox("connBlockC", xlen / 2, ylen / 2, zlen / 2); + + xv[0] = sOBCPConnectorXWidth * 0.6; + yv[0] = -sOBCPConnHollowYHei; + xv[1] = xv[0]; + yv[1] = sOBCPConnHollowYHei; + xv[2] = sOBCPConnTubesXDist / 2 + sOBCPConnTubeHole1D / 2; + yv[2] = yv[1]; + xv[3] = xv[2]; + yv[3] = sOBCPConnTubesYPos; + xv[4] = sOBCPConnTubesXDist / 2 - sOBCPConnTubeHole1D / 2; + yv[4] = yv[3]; + xv[5] = xv[4]; + yv[5] = yv[2]; + + for (Int_t i = 0; i < 6; i++) { + xv[6 + i] = -xv[5 - i]; + yv[6 + i] = yv[5 - i]; + } + + TGeoXtru* connBlockHoll = new TGeoXtru(2); + connBlockHoll->SetName("connBlockHollC"); + connBlockHoll->DefinePolygon(12, xv, yv); + connBlockHoll->DefineSection(0, -sOBCPConnHollowZLen); + connBlockHoll->DefineSection(1, sOBCPConnHollowZLen); + + ypos = -connBlock->GetDY(); + zpos = connBlock->GetDZ(); + TGeoTranslation* transBlockHoll = new TGeoTranslation("transBlockHollC", 0, ypos, zpos); + transBlockHoll->RegisterYourself(); + + TGeoTube* connRoundHole = new TGeoTube("connCRoundHole", 0, sOBCPConnRoundHoleD / 2, sOBCPConnBlockYHei / 1.5); + + zpos = connBlock->GetDZ() - sOBCPConnRndHoleZPos; + TGeoCombiTrans* transRoundHole = new TGeoCombiTrans("transCRoundHole", 0, 0, zpos, new TGeoRotation("", 0, 90, 0)); + transRoundHole->RegisterYourself(); + + zlen = sOBCPConnTubeHole1Z; + TGeoTube* connTubeHole1 = new TGeoTube("tube1CHole", 0, sOBCPConnTubeHole1D / 2, zlen); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = connBlock->GetDZ(); + TGeoTranslation* trans1Tube1AHole = new TGeoTranslation("trans1Tube1CHole", -xpos, ypos, zpos); + trans1Tube1AHole->RegisterYourself(); + TGeoTranslation* trans2Tube1AHole = new TGeoTranslation("trans2Tube1CHole", xpos, ypos, zpos); + trans2Tube1AHole->RegisterYourself(); + + TGeoTube* connTubeHole2 = new TGeoTube("tube2CHole", 0, sOBCPConnTubeHole2D / 2, connBlock->GetDZ()); + + zpos = sOBCPConnTubeHole3ZP; + TGeoTranslation* connTubes2Trans1 = new TGeoTranslation("trans1Tube2CHole", -xpos, ypos, zpos); + connTubes2Trans1->RegisterYourself(); + TGeoTranslation* connTubes2Trans2 = new TGeoTranslation("trans2Tube2CHole", xpos, ypos, zpos); + connTubes2Trans2->RegisterYourself(); + + TGeoTube* connTubeHole3 = new TGeoTube("tube3CHole", 0, sOBCPConnTubeHole2D / 2, connBlock->GetDX()); + + xpos = -sOBCPConnTubeHole3XP; + zpos = -connBlock->GetDZ() + sOBCPConnTubeHole3ZP; + TGeoCombiTrans* connTubes3Trans = + new TGeoCombiTrans("transTube3CHole", xpos, ypos, zpos, new TGeoRotation("", 90, -90, 90)); + connTubes3Trans->RegisterYourself(); + + TGeoCompositeShape* connBlockSh = new TGeoCompositeShape( + "connBlockC-connBlockHollC:transBlockHollC-connCRoundHole:transCRoundHole-tube1CHole:trans1Tube1CHole-tube1CHole:" + "trans2Tube1CHole-tube2CHole:trans1Tube2CHole-tube2CHole:trans2Tube2CHole-tube3CHole:transTube3CHole"); + + TGeoVolume* connBlockC = new TGeoVolume("OBColdPlateConnectorBlockCSide", connBlockSh, medPEEK); + connBlockC->SetFillColor(42); // Brownish shade + connBlockC->SetLineColor(42); + + // The plug, a Pcon + TGeoPcon* connPlugSh = new TGeoPcon(0, 360, 4); + connPlugSh->DefineSection(0, 0., 0., sOBCPConnTubeHole2D / 2); + connPlugSh->DefineSection(1, sOBCPConnPlugThick, 0., sOBCPConnTubeHole2D / 2); + connPlugSh->DefineSection(2, sOBCPConnPlugThick, sOBCPConnPlugInnerD / 2, sOBCPConnTubeHole2D / 2); + connPlugSh->DefineSection(3, sOBCPConnPlugTotLen, sOBCPConnPlugInnerD / 2, sOBCPConnTubeHole2D / 2); + + TGeoVolume* connPlug = new TGeoVolume("OBCPConnectorPlugC", connPlugSh, medPEEK); + connPlug->SetFillColor(44); // Brownish shade (a bit darker to spot it) + connPlug->SetLineColor(44); + + // Now create the container: cannot be a simple box + // to avoid fake overlaps with stave elements + xlen = sOBCPConnectorXWidth; + ylen = sOBCPConnBlockYHei; + zlen = sOBCPConnBlockZLen; + TGeoBBox* connBox = new TGeoBBox("connectorOBCPC", xlen / 2, ylen / 2, zlen / 2); + + ypos = -connBox->GetDY(); + zpos = connBox->GetDZ(); + TGeoTranslation* transBoxHoll = new TGeoTranslation("transBoxHollC", 0, ypos, zpos); + transBoxHoll->RegisterYourself(); + + xpos = sOBCPConnTubesXDist / 2; + ypos = -connBox->GetDY() + sOBCPConnTubesYPos; + zpos = connBox->GetDZ(); + TGeoTranslation* trans1BoxHole = new TGeoTranslation("trans1BoxCHole", -xpos, ypos, zpos); + trans1BoxHole->RegisterYourself(); + TGeoTranslation* trans2BoxHole = new TGeoTranslation("trans2BoxCHole", xpos, ypos, zpos); + trans2BoxHole->RegisterYourself(); + + TGeoCompositeShape* connectSh = new TGeoCompositeShape( + "connectorOBCPC-connBlockHollC:transBoxHollC-tube1CHole:trans1BoxCHole-tube1CHole:trans2BoxCHole"); + + TGeoVolume* connectorCSide = new TGeoVolume("OBColdPlateConnectorCSide", connectSh, medAir); + + // Finally build up the connector + connectorCSide->AddNode(connBlockC, 1); + + xpos = -connBlock->GetDX(); + ypos = -connBlock->GetDY() + sOBCPConnTubesYPos; + zpos = -connBlock->GetDZ() + sOBCPConnTubeHole3ZP; + connectorCSide->AddNode(connPlug, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90, 90, 90))); +} + +TGeoVolume* V3Layer::createSpaceFrameOuterB(const TGeoManager* mgr) +{ + TGeoVolume* mechStavVol = nullptr; + + switch (mStaveModel) { + case Detector::kOBModelDummy: + case Detector::kOBModel0: + mechStavVol = createSpaceFrameOuterBDummy(mgr); + break; + case Detector::kOBModel1: + case Detector::kOBModel2: + mechStavVol = createSpaceFrameOuterB2(mgr); + break; + default: + LOG(FATAL) << "Unknown stave model " << mStaveModel; + break; + } + + return mechStavVol; +} + +TGeoVolume* V3Layer::createSpaceFrameOuterBDummy(const TGeoManager*) const +{ + // + // Create dummy stave + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + + // Done, return the stave structur + return nullptr; +} + +TGeoVolume* V3Layer::createSpaceFrameOuterB2(const TGeoManager* mgr) +{ + // + // Create the space frame for the Outer Barrel (Model 2) + // The building blocks are created in another method to avoid + // replicating the same volumes for all OB staves + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume with the Space Frame of a stave + // + // Created: 03 Feb 2015 Mario Sitta + // Updated: 04 Jun 2015 Mario Sitta Change container to avoid overlaps + // Updated: 20 Jul 2017 Mario Sitta O2 version + // + + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + TGeoVolume *unitVol[2], *next2EndVol[2], *endVol[2]; + Double_t *xtru, *ytru; + Double_t zlen, zpos; + Int_t nPoints; + const Int_t nameLen = 30; + char volname[nameLen]; + + // Check whether we have already all pieces + // Otherwise create them + unitVol[0] = mgr->GetVolume("SpaceFrameUnit0"); + + if (!unitVol[0]) { + createOBSpaceFrameObjects(mgr); + unitVol[0] = mgr->GetVolume("SpaceFrameUnit0"); + } + + unitVol[1] = mgr->GetVolume("SpaceFrameUnit1"); + + next2EndVol[0] = mgr->GetVolume("SpaceFrameNext2EndUnit0"); + next2EndVol[1] = mgr->GetVolume("SpaceFrameNext2EndUnit1"); + + endVol[0] = mgr->GetVolume("SpaceFrameEndUnit0"); + endVol[1] = mgr->GetVolume("SpaceFrameEndUnit1"); + + // Get the shape of the units + // and create a similar shape for the Space Frame container + TGeoXtru* volShape = static_cast(unitVol[0]->GetShape()); + + nPoints = volShape->GetNvert(); + xtru = new Double_t[nPoints]; + ytru = new Double_t[nPoints]; + + for (Int_t i = 0; i < nPoints; i++) { + xtru[i] = volShape->GetX(i); + ytru[i] = volShape->GetY(i); + } + + Int_t nUnits = sOBSpaceFrameNUnits[mLayerNumber / 5]; // 3,4 -> 0 - 5,6 -> 1 + zlen = (nUnits - 2) * sOBSpaceFrameUnitLen; // Take end units out + + TGeoXtru* spaceFrameCentral = new TGeoXtru(2); + spaceFrameCentral->DefinePolygon(nPoints, xtru, ytru); + spaceFrameCentral->DefineSection(0, -zlen / 2); + spaceFrameCentral->DefineSection(1, zlen / 2); + snprintf(volname, nameLen, "sframecentral%d", mLayerNumber); + spaceFrameCentral->SetName(volname); + + zpos = zlen / 2 + sOBSpaceFrameUnitLen / 2; + snprintf(volname, nameLen, "endUnit0Trans%d", mLayerNumber); + TGeoCombiTrans* endUnit0Trans = new TGeoCombiTrans(volname, 0, 0, -zpos, new TGeoRotation("", 90, 180, -90)); + endUnit0Trans->RegisterYourself(); + snprintf(volname, nameLen, "endUnit1Trans%d", mLayerNumber); + TGeoTranslation* endUnit1Trans = new TGeoTranslation(volname, 0, 0, zpos); + endUnit1Trans->RegisterYourself(); + + // The Space Frame container: a Composite Shape to avoid overlaps + // between the U-legs space and the end-stave connectors + // ("endunitcontainer" is defined in CreateOBSpaceFrameObjects) + char componame[100]; + snprintf(componame, 100, "sframecentral%d+endunitcontainer:endUnit0Trans%d+endunitcontainer:endUnit1Trans%d", + mLayerNumber, mLayerNumber, mLayerNumber); + + TGeoCompositeShape* spaceFrame = new TGeoCompositeShape(componame); + + snprintf(volname, nameLen, "SpaceFrameVolumeLay%d", mLayerNumber); + TGeoVolume* spaceFrameVol = new TGeoVolume(volname, spaceFrame, medAir); + spaceFrameVol->SetVisibility(kFALSE); + + // Finally build up the space frame + TGeoXtru* frameUnit = static_cast(unitVol[0]->GetShape()); + + zpos = -spaceFrame->GetDZ() + frameUnit->GetDZ() + sOBSFrameConnTopLen; + spaceFrameVol->AddNode(endVol[0], 1, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 90, 180, -90))); + + zpos += (2 * frameUnit->GetDZ()); + spaceFrameVol->AddNode(next2EndVol[0], 1, new TGeoTranslation(0, 0, zpos)); + + for (Int_t i = 2; i < nUnits - 2; i++) { + zpos += (2 * frameUnit->GetDZ()); + Int_t j = i / 2; + Int_t k = i - j * 2; // alternatively 0 or 1 + spaceFrameVol->AddNode(unitVol[k], j, new TGeoTranslation(0, 0, zpos)); + } + + zpos += (2 * frameUnit->GetDZ()); + spaceFrameVol->AddNode(next2EndVol[1], 1, new TGeoTranslation(0, 0, zpos)); + + zpos += (2 * frameUnit->GetDZ()); + spaceFrameVol->AddNode(endVol[1], 1, new TGeoTranslation(0, 0, zpos)); + + // Done, clean up and return the space frame structure + delete[] xtru; + delete[] ytru; + + return spaceFrameVol; +} + +void V3Layer::createOBSpaceFrameObjects(const TGeoManager* mgr) +{ + // + // Create the space frame building blocks for the Outer Barrel + // This method is practically identical to previous versions of + // CreateSpaceFrameOuterB1 + // NB: it is pretty cumbersome, because we don't want to use assemblies + // so we are forced to have well-crafted containers to avoid fake overlaps + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume with the Space Frame of a stave + // + // Created: 03 Feb 2015 Mario Sitta + // Updated: 03 Jun 2015 Mario Sitta End units w/o U-legs + // Updated: 20 Jul 2017 Mario Sitta O2 version + // Updated: 09 Sep 2019 Mario Sitta Connectors added + // Updated: 27 Sep 2019 Mario Sitta New TopV for End Units + // + + // Materials defined in AliEC0Uv2 + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); + TGeoMedium* medF6151B05M = mgr->GetMedium("EC0_F6151B05M$"); + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + + // Local parameters + Double_t halfFrameWidth = sOBSpaceFrameWidth / 2; + Double_t triangleHeight = sOBSpaceFrameHeight; + Double_t sframeHeight = triangleHeight + sOBSFrameBaseRibDiam + sOBSFrameULegHeight2 * 2; + Double_t staveLa = sOBSpaceFrameTopVL; + Double_t staveHa = sOBSpaceFrameTopVH; + Double_t staveLb = sOBSpaceFrameSideVL; + Double_t staveHb = sOBSpaceFrameSideVH; + Double_t alphaDeg = sOBSpaceFrameVAlpha; + Double_t alphaRad = alphaDeg * TMath::DegToRad() / 2; + Double_t beta = sOBSpaceFrameVBeta * TMath::DegToRad() / 2; + Double_t sideRibRadius = sOBSFrameSideRibDiam / 2; + Double_t sidePhiDeg = sOBSFrameSideRibPhi; + Double_t sidePhiRad = sidePhiDeg * TMath::DegToRad(); + Double_t baseRibRadius = sOBSFrameBaseRibDiam / 2; + Double_t basePhiDeg = sOBSFrameBaseRibPhi; + Double_t basePhiRad = basePhiDeg * TMath::DegToRad(); + Double_t ulegHalfLen = sOBSFrameULegLen / 2; + Double_t ulegHalfWidth = sOBSFrameULegWidth / 2; + Double_t ulegHigh1 = sOBSFrameULegHeight1; + Double_t ulegHigh2 = sOBSFrameULegHeight2; + Double_t ulegThick = sOBSFrameULegThick; + Double_t topVFactorEU = 0.60; // Fraction of TopV total length for End Units + + Double_t xlen, zlen; + Double_t xpos, ypos, zpos; + Double_t unitlen; + Double_t xtru[22], ytru[22]; + + unitlen = sOBSpaceFrameUnitLen; + + xlen = halfFrameWidth + sideRibRadius; + + // We need a properly shaped Xtru to accomodate the ribs avoiding + // overlaps with the HalfStave cooling tubes + xtru[0] = sOBSFrameULegXPos - ulegHalfLen; + ytru[0] = -(triangleHeight / 2 + baseRibRadius); + xtru[1] = xtru[0]; + ytru[1] = ytru[0] - ulegHigh1; + xtru[2] = xtru[1] + ulegThick; + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[0] - ulegThick; + xtru[7] = sOBSFrameULegXPos + ulegHalfLen; + ytru[7] = ytru[0]; + xtru[6] = xtru[7]; + ytru[6] = ytru[1]; + xtru[5] = xtru[6] - ulegThick; + ytru[5] = ytru[6]; + xtru[4] = xtru[5]; + ytru[4] = ytru[3]; + xtru[8] = xlen; + ytru[8] = ytru[7]; + xtru[9] = xtru[8]; + ytru[9] = 0.9 * ytru[8]; + xtru[10] = 0.3 * xtru[8]; + ytru[10] = triangleHeight / 2; + for (Int_t i = 0; i < 11; i++) { // Reflect on the X negative side + xtru[i + 11] = -xtru[10 - i]; + ytru[i + 11] = ytru[10 - i]; + } + ytru[15] = ytru[0] - ulegHigh2; // U-legs on negative X are longer + ytru[16] = ytru[15]; + ytru[19] = ytru[15]; + ytru[20] = ytru[15]; + + // The space frame single units + // We need two units because the base ribs are alternately oriented + // The next-to-end units are slightly different + TGeoXtru* frameUnit = new TGeoXtru(2); + frameUnit->DefinePolygon(22, xtru, ytru); + frameUnit->DefineSection(0, -unitlen / 2); + frameUnit->DefineSection(1, unitlen / 2); + + TGeoXtru* next2EndUnit = new TGeoXtru(2); + next2EndUnit->DefinePolygon(22, xtru, ytru); + next2EndUnit->DefineSection(0, -unitlen / 2); + next2EndUnit->DefineSection(1, unitlen / 2); + + // The end units have no U-legs, but they contain the end-stave connectors + // so we build a CompositeShape using two Xtru's + xtru[0] = xlen; + ytru[0] = -(triangleHeight / 2 + baseRibRadius); + xtru[1] = xtru[0]; + ytru[1] = 0.9 * ytru[0]; + xtru[2] = 0.3 * xtru[0]; + ytru[2] = triangleHeight / 2; + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* endUnitBody = new TGeoXtru(2); + endUnitBody->SetName("endunitbody"); + endUnitBody->DefinePolygon(6, xtru, ytru); + endUnitBody->DefineSection(0, -unitlen / 2); + endUnitBody->DefineSection(1, 0.8 * unitlen / 2); + + xtru[2] = 0.25 * (3 * xtru[1] + xtru[2]); + ytru[2] = 0.25 * (3 * ytru[1] + ytru[2]); + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* endUnitBodyLow = new TGeoXtru(2); + endUnitBodyLow->SetName("endunitbodylow"); + endUnitBodyLow->DefinePolygon(6, xtru, ytru); + endUnitBodyLow->DefineSection(0, 0.8 * unitlen / 2); + endUnitBodyLow->DefineSection(1, unitlen / 2); + + // (See createOBSpaceFrameConnector lower down for details) + xtru[0] = sOBSFrameConnWidth / 2.; + ytru[0] = 0.; + xtru[1] = xtru[0]; + ytru[1] = sOBSFrameConnInsHei; + xtru[2] = xtru[1] - sOBSFrameConnTotHei + sOBSFrameConnInsHei; + ytru[2] = sOBSFrameConnTotHei; + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* endUnitConn = new TGeoXtru(2); + endUnitConn->SetName("endunitconn"); + endUnitConn->DefinePolygon(6, xtru, ytru); + endUnitConn->DefineSection(0, 0.); + endUnitConn->DefineSection(1, sOBSFrameConnTopLen); + + // We create a fake side V to have its dimensions, needed for + // the creation of the end unit container + TGeoXtru* vside = + createStaveSide("fakeCornerSide", unitlen / 2., alphaRad, beta, staveLb, staveHb, kFALSE); + + ypos = -triangleHeight / 2 + vside->GetY(3); + TGeoTranslation* endUnitConnTrans = new TGeoTranslation("endunitconntrans", 0, ypos, unitlen / 2); + endUnitConnTrans->RegisterYourself(); + + TGeoCompositeShape* endUnit = new TGeoCompositeShape("endunitbody+endunitbodylow+endunitconn:endunitconntrans"); + endUnit->SetName("endunitcontainer"); // Will be used when create spaceframe + + // The air containers + TGeoVolume* unitVol[2]; + unitVol[0] = new TGeoVolume("SpaceFrameUnit0", frameUnit, medAir); + unitVol[1] = new TGeoVolume("SpaceFrameUnit1", frameUnit, medAir); + unitVol[0]->SetVisibility(kFALSE); + unitVol[1]->SetVisibility(kFALSE); + + TGeoVolume* next2EndVol[2]; + next2EndVol[0] = new TGeoVolume("SpaceFrameNext2EndUnit0", next2EndUnit, medAir); + next2EndVol[1] = new TGeoVolume("SpaceFrameNext2EndUnit1", next2EndUnit, medAir); + next2EndVol[0]->SetVisibility(kFALSE); + next2EndVol[1]->SetVisibility(kFALSE); + + TGeoVolume* endVol[2]; + endVol[0] = new TGeoVolume("SpaceFrameEndUnit0", endUnit, medAir); + endVol[1] = new TGeoVolume("SpaceFrameEndUnit1", endUnit, medAir); + endVol[0]->SetVisibility(kFALSE); + endVol[1]->SetVisibility(kFALSE); + + // The actual volumes + + //--- The top V of the Carbon Fiber Stave (segment) + TGeoXtru* cfStavTop = + createStaveSide("CFstavTopCornerVolshape", unitlen / 2., alphaRad, beta, staveLa, staveHa, kTRUE); + + TGeoVolume* cfStavTopVol = new TGeoVolume("CFstavTopCornerVol", cfStavTop, medCarbon); + cfStavTopVol->SetLineColor(35); + + unitVol[0]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + unitVol[1]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + next2EndVol[0]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + next2EndVol[1]->AddNode(cfStavTopVol, 1, new TGeoTranslation(0, triangleHeight / 2, 0)); + + zlen = topVFactorEU * unitlen; + TGeoXtru* cfStavTopEU = + createStaveSide("CFstavTopCornerEUVolshape", zlen / 2., alphaRad, beta, staveLa, staveHa, kTRUE); + + TGeoVolume* cfStavTopVolEU = new TGeoVolume("CFstavTopCornerEUVol", cfStavTopEU, medCarbon); + cfStavTopVol->SetLineColor(35); + + zpos = endUnitBody->GetDZ() - zlen / 2.; + + endVol[0]->AddNode(cfStavTopVolEU, 1, new TGeoTranslation(0, triangleHeight / 2, -zpos)); + + endVol[1]->AddNode(cfStavTopVolEU, 1, new TGeoTranslation(0, triangleHeight / 2, -zpos)); + + //--- The two side V's + TGeoXtru* cfStavSide = + createStaveSide("CFstavSideCornerVolshape", unitlen / 2., alphaRad, beta, staveLb, staveHb, kFALSE); + + TGeoVolume* cfStavSideVol = new TGeoVolume("CFstavSideCornerVol", cfStavSide, medCarbon); + cfStavSideVol->SetLineColor(35); + + unitVol[0]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + unitVol[0]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + unitVol[1]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + unitVol[1]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + next2EndVol[0]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + next2EndVol[0]->AddNode( + cfStavSideVol, 2, new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + next2EndVol[1]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + next2EndVol[1]->AddNode( + cfStavSideVol, 2, new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + endVol[0]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + endVol[0]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + endVol[1]->AddNode(cfStavSideVol, 1, new TGeoTranslation(halfFrameWidth, -triangleHeight / 2, 0)); + endVol[1]->AddNode(cfStavSideVol, 2, + new TGeoCombiTrans(-halfFrameWidth, -triangleHeight / 2, 0, new TGeoRotation("", 90, 180, -90))); + + //--- The beams + // Ribs on the sides + Double_t ribZProj = triangleHeight / TMath::Tan(sidePhiRad); + Double_t sideRibLen = + TMath::Sqrt(ribZProj * ribZProj + triangleHeight * triangleHeight + halfFrameWidth * halfFrameWidth); + + TGeoTubeSeg* sideRib = new TGeoTubeSeg(0, sideRibRadius, sideRibLen / 2, 0, 180); + TGeoVolume* sideRibVol = new TGeoVolume("CFstavSideBeamVol", sideRib, medCarbon); + sideRibVol->SetLineColor(35); + + TGeoCombiTrans* sideTransf[4]; + xpos = halfFrameWidth / 2 + 0.8 * staveHa * TMath::Cos(alphaRad / 2); + ypos = -sideRibRadius / 2; + zpos = unitlen / 4; + + sideTransf[0] = new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 90 - alphaDeg, -sidePhiDeg, -90)); + sideTransf[1] = new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90 - alphaDeg, sidePhiDeg, -90)); + sideTransf[2] = new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 90 + alphaDeg, sidePhiDeg, -90)); + sideTransf[3] = new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90 + alphaDeg, -sidePhiDeg, -90)); + + unitVol[0]->AddNode(sideRibVol, 1, sideTransf[0]); + unitVol[0]->AddNode(sideRibVol, 2, sideTransf[1]); + unitVol[0]->AddNode(sideRibVol, 3, sideTransf[2]); + unitVol[0]->AddNode(sideRibVol, 4, sideTransf[3]); + + unitVol[1]->AddNode(sideRibVol, 1, sideTransf[0]); + unitVol[1]->AddNode(sideRibVol, 2, sideTransf[1]); + unitVol[1]->AddNode(sideRibVol, 3, sideTransf[2]); + unitVol[1]->AddNode(sideRibVol, 4, sideTransf[3]); + + next2EndVol[0]->AddNode(sideRibVol, 1, sideTransf[0]); + next2EndVol[0]->AddNode(sideRibVol, 2, sideTransf[1]); + next2EndVol[0]->AddNode(sideRibVol, 3, sideTransf[2]); + next2EndVol[0]->AddNode(sideRibVol, 4, sideTransf[3]); + + next2EndVol[1]->AddNode(sideRibVol, 1, sideTransf[0]); + next2EndVol[1]->AddNode(sideRibVol, 2, sideTransf[1]); + next2EndVol[1]->AddNode(sideRibVol, 3, sideTransf[2]); + next2EndVol[1]->AddNode(sideRibVol, 4, sideTransf[3]); + + endVol[0]->AddNode(sideRibVol, 1, sideTransf[0]); + endVol[0]->AddNode(sideRibVol, 2, sideTransf[1]); + endVol[0]->AddNode(sideRibVol, 3, sideTransf[2]); + endVol[0]->AddNode(sideRibVol, 4, sideTransf[3]); + + endVol[1]->AddNode(sideRibVol, 1, sideTransf[0]); + endVol[1]->AddNode(sideRibVol, 2, sideTransf[1]); + endVol[1]->AddNode(sideRibVol, 3, sideTransf[2]); + endVol[1]->AddNode(sideRibVol, 4, sideTransf[3]); + + // Ribs on the bottom + // Rib1 are the inclined ones, Rib2 the straight ones + Double_t baseRibLen = 0.98 * 2 * halfFrameWidth / TMath::Sin(basePhiRad); + + TGeoTubeSeg* baseRib1 = new TGeoTubeSeg(0, baseRibRadius, baseRibLen / 2, 0, 180); + TGeoVolume* baseRib1Vol = new TGeoVolume("CFstavBaseBeam1Vol", baseRib1, medCarbon); + baseRib1Vol->SetLineColor(35); + + TGeoTubeSeg* baseRib2 = new TGeoTubeSeg(0, baseRibRadius, halfFrameWidth, 0, 90); + TGeoVolume* baseRib2Vol = new TGeoVolume("CFstavBaseBeam2Vol", baseRib2, medCarbon); + baseRib2Vol->SetLineColor(35); + + TGeoTubeSeg* baseEndRib = new TGeoTubeSeg(0, baseRibRadius, halfFrameWidth, 0, 180); + TGeoVolume* baseEndRibVol = new TGeoVolume("CFstavBaseEndBeamVol", baseEndRib, medCarbon); + baseEndRibVol->SetLineColor(35); + + TGeoCombiTrans* baseTransf[6]; + ypos = triangleHeight / 2; + zpos = unitlen / 2; + + baseTransf[0] = new TGeoCombiTrans("", 0, -ypos, -zpos, new TGeoRotation("", 90, 90, 90)); + baseTransf[1] = new TGeoCombiTrans("", 0, -ypos, zpos, new TGeoRotation("", -90, 90, -90)); + baseTransf[2] = new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", -90, basePhiDeg, -90)); + baseTransf[3] = new TGeoCombiTrans(0, -ypos, 0, new TGeoRotation("", -90, -basePhiDeg, -90)); + zpos -= baseEndRib->GetRmax(); + baseTransf[4] = new TGeoCombiTrans("", 0, -ypos, -zpos, new TGeoRotation("", 90, 90, 90)); + baseTransf[5] = new TGeoCombiTrans("", 0, -ypos, zpos, new TGeoRotation("", 90, 90, 90)); + + unitVol[0]->AddNode(baseRib2Vol, 1, baseTransf[0]); + unitVol[0]->AddNode(baseRib2Vol, 2, baseTransf[1]); + unitVol[0]->AddNode(baseRib1Vol, 1, baseTransf[2]); + + unitVol[1]->AddNode(baseRib2Vol, 1, baseTransf[0]); + unitVol[1]->AddNode(baseRib2Vol, 2, baseTransf[1]); + unitVol[1]->AddNode(baseRib1Vol, 1, baseTransf[3]); + + next2EndVol[0]->AddNode(baseRib2Vol, 1, baseTransf[0]); + next2EndVol[0]->AddNode(baseRib2Vol, 2, baseTransf[1]); + next2EndVol[0]->AddNode(baseRib1Vol, 1, baseTransf[3]); + + next2EndVol[1]->AddNode(baseRib2Vol, 1, baseTransf[0]); + next2EndVol[1]->AddNode(baseRib2Vol, 2, baseTransf[1]); + next2EndVol[1]->AddNode(baseRib1Vol, 1, baseTransf[3]); + + endVol[0]->AddNode(baseEndRibVol, 1, baseTransf[4]); + endVol[0]->AddNode(baseRib2Vol, 1, baseTransf[1]); + endVol[0]->AddNode(baseRib1Vol, 1, baseTransf[2]); + + endVol[1]->AddNode(baseEndRibVol, 1, baseTransf[5]); + endVol[1]->AddNode(baseRib2Vol, 1, baseTransf[0]); + endVol[1]->AddNode(baseRib1Vol, 1, baseTransf[2]); + + // The Space Frame connectors + ypos = -triangleHeight / 2 + cfStavSide->GetY(3); + zpos = unitlen / 2; + createOBSpaceFrameConnector(endVol[0], ypos, zpos, kFALSE); // Side C + createOBSpaceFrameConnector(endVol[1], ypos, zpos, kTRUE); // Side A + + // U-Legs + // The shorter + xtru[0] = ulegHalfLen; + ytru[0] = 0; + xtru[1] = xtru[0]; + ytru[1] = -ulegHigh1; + xtru[2] = xtru[1] - ulegThick; + ytru[2] = ytru[1]; + xtru[3] = xtru[2]; + ytru[3] = ytru[0] - ulegThick; + for (Int_t i = 0; i < 4; i++) { // Reflect on the X negative side + xtru[i + 4] = -xtru[3 - i]; + ytru[i + 4] = ytru[3 - i]; + } + + TGeoXtru* uleg1full = new TGeoXtru(2); // This will go in the next end units + uleg1full->DefinePolygon(8, xtru, ytru); + uleg1full->DefineSection(0, -ulegHalfWidth); + uleg1full->DefineSection(1, ulegHalfWidth); + + TGeoXtru* uleg1half = new TGeoXtru(2); // This will go in the middle unitys + uleg1half->DefinePolygon(8, xtru, ytru); + uleg1half->DefineSection(0, -ulegHalfWidth / 2); + uleg1half->DefineSection(1, ulegHalfWidth / 2); + + TGeoVolume* uleg1fullVol = new TGeoVolume("CFstavULeg1FullVol", uleg1full, medF6151B05M); + uleg1fullVol->SetLineColor(35); + + TGeoVolume* uleg1halfVol = new TGeoVolume("CFstavULeg1HalfVol", uleg1half, medF6151B05M); + uleg1halfVol->SetLineColor(35); + + // The longer + ytru[1] = -ulegHigh2; + ytru[2] = -ulegHigh2; + ytru[5] = -ulegHigh2; + ytru[6] = -ulegHigh2; + + TGeoXtru* uleg2full = new TGeoXtru(2); // This will go in the next end units + uleg2full->DefinePolygon(8, xtru, ytru); + uleg2full->DefineSection(0, -ulegHalfWidth); + uleg2full->DefineSection(1, ulegHalfWidth); + + TGeoXtru* uleg2half = new TGeoXtru(2); // This will go in the middle unitys + uleg2half->DefinePolygon(8, xtru, ytru); + uleg2half->DefineSection(0, -ulegHalfWidth / 2); + uleg2half->DefineSection(1, ulegHalfWidth / 2); + + TGeoVolume* uleg2fullVol = new TGeoVolume("CFstavULeg2FullVol", uleg2full, medF6151B05M); + uleg2fullVol->SetLineColor(35); + + TGeoVolume* uleg2halfVol = new TGeoVolume("CFstavULeg2HalfVol", uleg2half, medF6151B05M); + uleg2halfVol->SetLineColor(35); + + xpos = sOBSFrameULegXPos; + ypos = triangleHeight / 2 + baseRibRadius; + zpos = unitlen / 2 - uleg1half->GetZ(1); + + unitVol[0]->AddNode(uleg1halfVol, 1, // Shorter on +X + new TGeoTranslation(xpos, -ypos, -zpos)); + unitVol[0]->AddNode(uleg1halfVol, 2, new TGeoTranslation(xpos, -ypos, zpos)); + + unitVol[1]->AddNode(uleg1halfVol, 1, new TGeoTranslation(xpos, -ypos, -zpos)); + unitVol[1]->AddNode(uleg1halfVol, 2, new TGeoTranslation(xpos, -ypos, zpos)); + + unitVol[0]->AddNode(uleg2halfVol, 1, // Longer on -X + new TGeoTranslation(-xpos, -ypos, -zpos)); + unitVol[0]->AddNode(uleg2halfVol, 2, new TGeoTranslation(-xpos, -ypos, zpos)); + + unitVol[1]->AddNode(uleg2halfVol, 1, new TGeoTranslation(-xpos, -ypos, -zpos)); + unitVol[1]->AddNode(uleg2halfVol, 2, new TGeoTranslation(-xpos, -ypos, zpos)); + + next2EndVol[0]->AddNode(uleg1halfVol, 1, new TGeoTranslation(xpos, -ypos, zpos)); + next2EndVol[0]->AddNode(uleg2halfVol, 1, new TGeoTranslation(-xpos, -ypos, zpos)); + + next2EndVol[1]->AddNode(uleg1halfVol, 1, new TGeoTranslation(xpos, -ypos, -zpos)); + next2EndVol[1]->AddNode(uleg2halfVol, 1, new TGeoTranslation(-xpos, -ypos, -zpos)); + + zpos = unitlen / 2 - uleg1full->GetZ(1); + next2EndVol[0]->AddNode(uleg1fullVol, 1, new TGeoTranslation(xpos, -ypos, -zpos)); + next2EndVol[0]->AddNode(uleg2fullVol, 1, new TGeoTranslation(-xpos, -ypos, -zpos)); + + next2EndVol[1]->AddNode(uleg1fullVol, 1, new TGeoTranslation(xpos, -ypos, zpos)); + next2EndVol[1]->AddNode(uleg2fullVol, 1, new TGeoTranslation(-xpos, -ypos, zpos)); + + // Done + return; +} + +void V3Layer::createOBSpaceFrameConnector(TGeoVolume* mother, const Double_t ymot, const Double_t zmot, const Bool_t sideA, const TGeoManager* mgr) +{ + // + // Creates the OB Space Frame Connectors + // (ALIEC0UP0070+ALIEC0UP0069) + // + // Input: + // mother : the SF unit volume to contain the connector + // ymot : the Y position of the connector in the mother volume + // zmot : the Z position of the connector in the mother volume + // sideA : true for Side A, false for Side C + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 09 Sep 2019 M. Sitta + + // Materials defined in AliEC0Uv2 + TGeoMedium* medPEEK = mgr->GetMedium("EC0_PEEKCF30$"); + + // Local parameters + TString connName, compoShape; + + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + Double_t xtru[6], ytru[6]; + + // The external (higher) part: a Xtru + ylen = sOBSFrameConnTotHei - sOBSFrameConnInsHei; + + xtru[0] = sOBSFrameConnWidth / 2.; + ytru[0] = 0.; + xtru[1] = xtru[0]; + ytru[1] = sOBSFrameConnInsHei; + xtru[2] = xtru[1] - ylen; // Because side is at 45' so dx = dy + ytru[2] = sOBSFrameConnTotHei; + for (Int_t i = 0; i < 3; i++) { // Reflect on the X negative side + xtru[i + 3] = -xtru[2 - i]; + ytru[i + 3] = ytru[2 - i]; + } + + TGeoXtru* topConn = new TGeoXtru(2); + topConn->SetName("connectorTop"); + topConn->DefinePolygon(6, xtru, ytru); + topConn->DefineSection(0, 0.); + topConn->DefineSection(1, sOBSFrameConnTopLen); + + // The insert: a Xtru + zlen = sOBSFrameConnTotLen - sOBSFrameConnTopLen; + + xtru[0] = sOBSFrameConnInsBase / 2.; + ytru[0] = 0.; + xtru[1] = sOBSFrameConnInsWide / 2.; + ytru[1] = sOBSFrameConnInsHei; + xtru[2] = -xtru[1]; + ytru[2] = ytru[1]; + xtru[3] = -xtru[0]; + ytru[3] = ytru[0]; + + TGeoXtru* insConn = new TGeoXtru(2); + insConn->SetName("connectorIns"); + insConn->DefinePolygon(4, xtru, ytru); + insConn->DefineSection(0, -zlen); + insConn->DefineSection(1, 0.); + + // The holes in the external (higher) part: Tube's and a BBox + TGeoTube* topHoleR = new TGeoTube("topholer", 0., sOBSFrameConnTopHoleD / 2., 1.1 * sOBSFrameConnTotHei); + + xpos = sOBSFrConnTopHoleXDist / 2.; + ypos = sOBSFrameConnTotHei / 2.; + zpos = sOBSFrameConnTopLen - sOBSFrameConnHoleZPos; + TGeoCombiTrans* topHoleR1Trans = new TGeoCombiTrans("topholer1tr", xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topHoleR1Trans->RegisterYourself(); + + TGeoCombiTrans* topHoleR2Trans = new TGeoCombiTrans("topholer2tr", -xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topHoleR2Trans->RegisterYourself(); + + xpos = sOBSFrConnCHoleXDist / 2.; + zpos = sOBSFrameConnTopLen - sOBSFrameConnCHoleZPos; + TGeoCombiTrans* topCHoleR1Trans = new TGeoCombiTrans("topcholer1tr", xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topCHoleR1Trans->RegisterYourself(); + + TGeoCombiTrans* topCHoleR2Trans = new TGeoCombiTrans("topcholer2tr", -xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topCHoleR2Trans->RegisterYourself(); + + TGeoBBox* topAHole = new TGeoBBox("topahole", sOBSFrameConnAHoleWid / 2., sOBSFrameConnTotHei, sOBSFrameConnAHoleLen / 2.); + + zpos = sOBSFrameConnTopLen - sOBSFrameConnHoleZPos; + TGeoTranslation* topAHoleTrans = new TGeoTranslation("topaholetr", 0, ypos, zpos); + topAHoleTrans->RegisterYourself(); + + TGeoTube* topCHole = new TGeoTube("topchole", 0., sOBSFrConnCTopHoleD / 2., sOBSFrameConnTotHei); + + TGeoCombiTrans* topCHoleTrans = new TGeoCombiTrans("topcholetr", 0, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + topCHoleTrans->RegisterYourself(); + + TGeoTube* topASide = new TGeoTube("topaside", 0., sOBSFrConnASideHoleD / 2., 1.1 * sOBSFrConnASideHoleL); + + zpos = sOBSFrameConnTopLen + topASide->GetDz() - sOBSFrConnASideHoleL; + TGeoTranslation* topASideTrans = new TGeoTranslation("topasidetr", 0, sOBSFrConnASideHoleY, zpos); + topASideTrans->RegisterYourself(); + + // The holes in the insert: a Tube + TGeoTube* insHole = new TGeoTube("inshole", 0., sOBSFrameConnInsHoleD / 2., sOBSFrameConnInsHei); + + xpos = sOBSFrameConnInsHoleX / 2.; + ypos = sOBSFrameConnInsHei / 2.; + zpos = sOBSFrameConnTopLen - sOBSFrameConnHoleZPos - sOBSFrameConnHoleZDist; + TGeoCombiTrans* insHole1Trans = new TGeoCombiTrans("inshole1tr", xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + insHole1Trans->RegisterYourself(); + + TGeoCombiTrans* insHole2Trans = new TGeoCombiTrans("inshole2tr", -xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0)); + insHole2Trans->RegisterYourself(); + + // The connector: a CompositeShape + if (sideA) { + connName = "OBSFConnectorA"; + compoShape = "(connectorTop-topholer:topholer2tr-topholer:topholer1tr-topahole:topaholetr-topaside:topasidetr)+(connectorIns-inshole:inshole1tr-inshole:inshole2tr)"; + } else { + connName = "OBSFConnectorC"; + compoShape = "(connectorTop-topholer:topholer2tr-topholer:topholer1tr-topholer:topcholer1tr-topholer:topcholer2tr-topchole:topcholetr)+(connectorIns-inshole:inshole1tr-inshole:inshole2tr)"; + } + + TGeoCompositeShape* obsfConnSh = new TGeoCompositeShape(compoShape.Data()); + + TGeoVolume* obsfConnVol = new TGeoVolume(connName, obsfConnSh, medPEEK); + + // Finally put the connector into its mother volume + mother->AddNode(obsfConnVol, 1, new TGeoTranslation(0, ymot, zmot)); +} + +TGeoVolume* V3Layer::createModuleOuterB(const TGeoManager* mgr) +{ + // + // Creates the OB Module: HIC + FPC + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the module as a TGeoVolume + // + // Created: 18 Dec 2013 M. Sitta, A. Barbano + // Updated: 26 Feb 2014 M. Sitta + // Updated: 12 Nov 2014 M. Sitta Model2 is w/o Carbon Plate and Glue + // and Cu instead of Al + // Updated: 20 Jul 2017 M. Sitta O2 version + // Updated: 30 Jul 2018 M. Sitta Updated geometry + // + + const Int_t nameLen = 30; + char chipName[nameLen], sensName[nameLen], volName[nameLen]; + + Double_t xGap = sOBChipXGap; + Double_t zGap = sOBChipZGap; + + Double_t xchip, ychip, zchip; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + Bool_t dummyChip; + + // First create all needed shapes + + // For material budget studies + if (mBuildLevel < 7) + dummyChip = kFALSE; // will be made of Si + else + dummyChip = kTRUE; // will be made of Air + + // The chip (the same as for IB) + snprintf(chipName, nameLen, "%s%d", GeometryTGeo::getEC0ChipPattern(), mLayerNumber); + snprintf(sensName, nameLen, "%s%d", GeometryTGeo::getEC0SensorPattern(), mLayerNumber); + + ylen = 0.5 * sOBChipThickness; + + TGeoVolume* chipVol = AlpideChip::createChip(ylen, mSensorThickness / 2, chipName, sensName, dummyChip); + + xchip = (static_cast(chipVol->GetShape()))->GetDX(); + ychip = (static_cast(chipVol->GetShape()))->GetDY(); + zchip = (static_cast(chipVol->GetShape()))->GetDZ(); + + mOBModuleZLength = 2 * zchip * sOBChipsPerRow + (sOBChipsPerRow - 1) * sOBChipZGap; + + zlen = mOBModuleZLength / 2; + + // The glue + xlen = (4 * xchip + xGap) / 2; + ylen = sOBGlueFPCThick / 2; + TGeoBBox* glueFPC = new TGeoBBox("GlueFPC", xlen, ylen, zlen); + + ylen = sOBGlueColdPlThick / 2; + TGeoBBox* glueCP = new TGeoBBox("GlueCP", xlen, ylen, zlen); + + // The FPC cables + xlen = sOBFlexCableXWidth / 2; + ylen = sOBFlexCableKapThick / 2; + TGeoBBox* flexKap = new TGeoBBox("MidFlexKap", xlen, ylen, zlen); + + TGeoVolume* cuGndCableVol = createOBFPCCuGnd(zlen); + TGeoVolume* cuSignalCableVol = createOBFPCCuSig(zlen); + + // The module + Double_t ygnd = (static_cast(cuGndCableVol->GetShape()))->GetDY(); + Double_t ysig = (static_cast(cuSignalCableVol->GetShape()))->GetDY(); + + xlen = (static_cast(cuGndCableVol->GetShape()))->GetDX(); + ylen = glueCP->GetDY() + ychip + glueFPC->GetDY() + ysig + flexKap->GetDY() + ygnd; + TGeoBBox* module = new TGeoBBox("OBModule", xlen, ylen, zlen); + + // We have all shapes: now create the real volumes + + TGeoMedium* medAir = mgr->GetMedium("EC0_AIR$"); + TGeoMedium* medGlue = mgr->GetMedium("EC0_GLUE$"); + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + + TGeoVolume* glueFPCVol = new TGeoVolume("GlueFPCVol", glueFPC, medGlue); + glueFPCVol->SetLineColor(kBlack); + glueFPCVol->SetFillColor(glueFPCVol->GetLineColor()); + glueFPCVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* glueCPVol = new TGeoVolume("GlueColdPlVol", glueCP, medGlue); + glueCPVol->SetLineColor(kBlack); + glueCPVol->SetFillColor(glueCPVol->GetLineColor()); + glueCPVol->SetFillStyle(4000); // 0% transparent + + TGeoVolume* flexKapVol = new TGeoVolume("FPCMidKapVol", flexKap, medKapton); + flexKapVol->SetLineColor(kGreen); + flexKapVol->SetFillColor(flexKapVol->GetLineColor()); + flexKapVol->SetFillStyle(4000); // 0% transparent + + snprintf(volName, nameLen, "%s%d", GeometryTGeo::getEC0ModulePattern(), mLayerNumber); + TGeoVolume* modVol = new TGeoVolume(volName, module, medAir); + modVol->SetVisibility(kTRUE); + + // Now build up the module + ypos = -module->GetDY() + glueCP->GetDY(); + + if (mBuildLevel < 3) // Glue + modVol->AddNode(glueCPVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = xchip + xGap / 2; + ypos += (ychip + glueCP->GetDY()); + // We use two loops here to have the same chip numbering as in HW + // X ^ | 6| 5| 4| 3| 2| 1| 0| + // ----|--------------------------> Z + // | | 7| 8| 9|10|11|12|13| + // + for (Int_t k = 0; k < sOBChipsPerRow; k++) // put first 7 chip row + { + zpos = module->GetDZ() - zchip - k * (2 * zchip + zGap); + modVol->AddNode(chipVol, k, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 180, 180))); + mHierarchy[kChip] += 1; + } + + for (Int_t k = 0; k < sOBChipsPerRow; k++) // put second 7 chip row + { + zpos = -module->GetDZ() + zchip + k * (2 * zchip + zGap); + modVol->AddNode(chipVol, k + sOBChipsPerRow, new TGeoTranslation(-xpos, ypos, zpos)); + mHierarchy[kChip] += 1; + } + + ypos += (ychip + glueFPC->GetDY()); + if (mBuildLevel < 3) // Glue + modVol->AddNode(glueFPCVol, 1, new TGeoTranslation(0, ypos, 0)); + ypos += glueFPC->GetDY(); + + if (mBuildLevel < 5) { // Kapton + ypos += ysig; + modVol->AddNode(cuSignalCableVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (ysig + flexKap->GetDY()); + modVol->AddNode(flexKapVol, 1, new TGeoTranslation(0, ypos, 0)); + + ypos += (flexKap->GetDY() + ygnd); + modVol->AddNode(cuGndCableVol, 1, new TGeoTranslation(0, ypos, 0)); + } + + // Done, return the module + return modVol; +} + +TGeoVolume* V3Layer::createOBFPCCuGnd(const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the OB FPC Copper Ground cable + // + // Input: + // zcable : the cable half Z length + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the FPC cable as a TGeoVolume + // + // Created: 30 Jul 2018 Mario Sitta + // + + Double_t xcable, ytot, ypos; + + // First create all needed shapes + xcable = sOBFlexCableXWidth / 2; + ytot = sOBFPCSoldMaskThick + sOBFPCCopperThick; + TGeoBBox* soldmask = new TGeoBBox(xcable, ytot / 2, zcable); + xcable *= sOBFPCCuAreaFracGnd; + TGeoBBox* copper = new TGeoBBox(xcable, sOBFPCCopperThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medCopper = mgr->GetMedium("EC0_COPPER$"); + + TGeoVolume* soldmaskVol = new TGeoVolume("FPCGndSolderMask", soldmask, medKapton); + soldmaskVol->SetLineColor(kBlue); + soldmaskVol->SetFillColor(kBlue); + + TGeoVolume* copperVol = new TGeoVolume("FPCCopperGround", copper, medCopper); + copperVol->SetLineColor(kCyan); + copperVol->SetFillColor(kCyan); + + ypos = -soldmask->GetDY() + copper->GetDY(); + if (mBuildLevel < 1) // Copper + soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + + return soldmaskVol; +} + +TGeoVolume* V3Layer::createOBFPCCuSig(const Double_t zcable, const TGeoManager* mgr) +{ + // + // Create the OB FPC Copper Signal cable + // + // Input: + // zcable : the cable half Z length + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the FPC cable as a TGeoVolume + // + // Created: 30 Jul 2018 Mario Sitta + // + + Double_t xcable, ytot, ypos; + + // First create all needed shapes + xcable = sOBFlexCableXWidth / 2; + ytot = sOBFPCSoldMaskThick + sOBFPCCopperThick; + TGeoBBox* soldmask = new TGeoBBox(xcable, ytot / 2, zcable); + xcable *= sOBFPCCuAreaFracSig; + TGeoBBox* copper = new TGeoBBox(xcable, sOBFPCCopperThick / 2, zcable); + + // Then the volumes + TGeoMedium* medKapton = mgr->GetMedium("EC0_KAPTON(POLYCH2)$"); + TGeoMedium* medCopper = mgr->GetMedium("EC0_COPPER$"); + + TGeoVolume* soldmaskVol = new TGeoVolume("FPCSigSolderMask", soldmask, medKapton); + soldmaskVol->SetLineColor(kBlue); + soldmaskVol->SetFillColor(kBlue); + + TGeoVolume* copperVol = new TGeoVolume("FPCCopperSignal", copper, medCopper); + copperVol->SetLineColor(kCyan); + copperVol->SetFillColor(kCyan); + + ypos = soldmask->GetDY() - copper->GetDY(); + if (mBuildLevel < 1) // Copper + soldmaskVol->AddNode(copperVol, 1, new TGeoTranslation(0, ypos, 0)); + + return soldmaskVol; +} + +Double_t V3Layer::getGammaConversionRodDiam() +{ + // + // Gets the diameter of the gamma conversion rods, if defined + // + // + // Input: + // + // Output: + // + // Return: + // the diameter of the gamma conversion rods for this layer + // + // Created: 26 Oct 2016 Mario Sitta + // + + if (!mAddGammaConv) { + LOG(WARNING) << "Gamma Conversion rods not defined for this layer"; + } + return mGammaConvDiam; +} + +Double_t V3Layer::getGammaConversionRodXPos() +{ + // + // Gets the X position of the gamma conversion rods, if defined + // + // + // Input: + // + // Output: + // + // Return: + // the X position of the gamma conversion rods for this layer + // in the Half Stave reference system + // + // Created: 26 Oct 2016 Mario Sitta + // + + if (!mAddGammaConv) { + LOG(WARNING) << "Gamma Conversion rods not defined for this layer"; + } + return mGammaConvXPos; +} + +Double_t V3Layer::radiusOmTurboContainer() +{ + Double_t rr, delta, z, lstav, rstav; + + if (mChipThickness > 89.) { // Very big angle: avoid overflows since surely + return -1; // the radius from lower vertex is the right value + } + + rstav = mLayerRadius + 0.5 * mChipThickness; + delta = (0.5 * mChipThickness) / cosD(mStaveTilt); + z = (0.5 * mChipThickness) * tanD(mStaveTilt); + + rr = rstav - delta; + lstav = (0.5 * mStaveWidth) - z; + + if ((rr * sinD(mStaveTilt) < lstav)) { + return (rr * cosD(mStaveTilt)); + } else { + return -1; + } +} + +void V3Layer::setNumberOfUnits(Int_t u) +{ + if (mLayerNumber < sNumberOfInnerLayers) { + mNumberOfChips = u; + } else { + mNumberOfModules = u; + mNumberOfChips = sOBChipsPerRow; + } +} + +void V3Layer::setStaveTilt(const Double_t t) +{ + if (mIsTurbo) { + mStaveTilt = t; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +void V3Layer::setStaveWidth(const Double_t w) +{ + if (mIsTurbo) { + mStaveWidth = w; + } else { + LOG(ERROR) << "Not a Turbo layer"; + } +} + +TGeoXtru* V3Layer::createStaveSide(const char* name, Double_t dz, Double_t alpha, Double_t beta, Double_t L, Double_t H, + Bool_t top) +{ + // + // Creates the V-shaped sides of the OB space frame + // (from a similar method with same name and function + // in AliEC0v11GeometrySDD class by L.Gaudichet) + // + // Updated: 15 Dec 2014 Mario Sitta Rewritten using Xtru + // Updated: 09 Jan 2015 Mario Sitta Rewritten again using different + // aperture angles (info by C.Gargiulo) + // Updated: 21 Jul 2017 Mario Sitta O2 version + // + + // Create the V shape corner of CF stave + + const Int_t nv = 6; + Double_t xv[nv], yv[nv]; + + TGeoXtru* cfStavSide = new TGeoXtru(2); + cfStavSide->SetName(name); + + Double_t theta = TMath::PiOver2() - beta; + Double_t gamma = beta - alpha; + // Points must be in clockwise order + if (top) { // TOP - vertices not in order + xv[3] = 0; + yv[3] = 0; + xv[2] = L * TMath::Sin(alpha); + yv[2] = -L * TMath::Cos(alpha); + xv[1] = xv[2] - H * TMath::Cos(alpha); + yv[1] = yv[2] - H * TMath::Sin(alpha); + xv[0] = 0; + yv[0] = yv[1] + TMath::Tan(theta) * xv[1]; + xv[4] = -xv[2]; // Reflect + yv[4] = yv[2]; + xv[5] = -xv[1]; + yv[5] = yv[1]; + } else { // SIDE + Double_t m = -TMath::Tan(alpha), n = TMath::Tan(gamma); + xv[0] = 0; + yv[0] = 0; + xv[1] = -L * TMath::Cos(2 * alpha); + yv[1] = L * TMath::Sin(2 * alpha); + xv[2] = xv[1] - H * TMath::Sin(2 * alpha); + yv[2] = yv[1] - H * TMath::Cos(2 * alpha); + xv[4] = -L; + yv[4] = H; + xv[5] = xv[4]; + yv[5] = 0; + xv[3] = (yv[4] - n * xv[4]) / (m - n); + yv[3] = m * xv[3]; + } + + cfStavSide->DefinePolygon(nv, xv, yv); + cfStavSide->DefineSection(0, -dz); + cfStavSide->DefineSection(1, dz); + + return cfStavSide; +} + +TGeoCombiTrans* V3Layer::createCombiTrans(const char* name, Double_t dy, Double_t dz, Double_t dphi, Bool_t planeSym) +{ + TGeoTranslation t1(dy * cosD(90. + dphi), dy * sinD(90. + dphi), dz); + TGeoRotation r1("", 0., 0., dphi); + TGeoRotation r2("", 90, 180, -90 - dphi); + + TGeoCombiTrans* combiTrans1 = new TGeoCombiTrans(name); + combiTrans1->SetTranslation(t1); + if (planeSym) { + combiTrans1->SetRotation(r1); + } else { + combiTrans1->SetRotation(r2); + } + return combiTrans1; +} + +void V3Layer::addTranslationToCombiTrans(TGeoCombiTrans* ct, Double_t dx, Double_t dy, Double_t dz) const +{ + // Add a dx,dy,dz translation to the initial TGeoCombiTrans + const Double_t* vect = ct->GetTranslation(); + Double_t newVect[3] = {vect[0] + dx, vect[1] + dy, vect[2] + dz}; + ct->SetTranslation(newVect); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V3Services.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V3Services.cxx new file mode 100644 index 0000000000000..b3e6cc31f208e --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/V3Services.cxx @@ -0,0 +1,2340 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 V3Services.cxx +/// \brief Implementation of the V3Services class +/// \author Mario Sitta +/// \author Parinya Namwongsa + +#include "EC0Simulation/V3Services.h" +#include "EC0Simulation/V11Geometry.h" +#include "ECLayersBase/GeometryTGeo.h" +#include "EC0Simulation/Detector.h" +#include "EndCapsSimulation/AlpideChip.h" + +#include "FairLogger.h" // for LOG + +//#include // for TGeoArb8 +#include // for TGeoBBox +#include // for TGeoConeSeg, TGeoCone +#include // for TGeoPcon +#include // for TGeoManager, gGeoManager +#include // for TGeoCombiTrans, TGeoRotation, etc +//#include // for TGeoTrd1 +#include // for TGeoTube, TGeoTubeSeg +#include // for TGeoVolume, TGeoVolumeAssembly +#include // for TGeoXtru +#include // for TGeoCompositeShape +#include "TMathBase.h" // for Abs +#include // for Sin, RadToDeg, DegToRad, Cos, Tan, etc + +#include // for snprintf + +class TGeoMedium; + +using namespace TMath; +using namespace o2::ecl; + +// Parameters +const Double_t V3Services::sIBWheelACZdist = 306.0 * sMm; +const Double_t V3Services::sIBCYSSFlangeCZPos = 171.5 * sMm; // Computed from different drawings +const Double_t V3Services::sOBWheelThickness = 2.0 * sMm; +const Double_t V3Services::sMBWheelsZpos = 457.0 * sMm; +const Double_t V3Services::sOBWheelsZpos = 770.0 * sMm; +const Double_t V3Services::sOBConesZpos = 798.0 * sMm; + +ClassImp(V3Services); + +#define SQ(A) (A) * (A) + +V3Services::V3Services() + : V11Geometry() +{ +} + +V3Services::~V3Services() = default; + +TGeoVolume* V3Services::createIBEndWheelsSideA(const TGeoManager* mgr) +{ + // + // Creates the Inner Barrel End Wheels on Side A + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume(Assembly) with all the wheels + // + // Created: 19 Jun 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideA"); + endWheelsVol->SetVisibility(kTRUE); + + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) + ibEndWheelSideA(jLay, endWheelsVol, mgr); + + // Return the wheels + return endWheelsVol; +} + +TGeoVolume* V3Services::createIBEndWheelsSideC(const TGeoManager* mgr) +{ + // + // Creates the Inner Barrel End Wheels on Side C + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume(Assembly) with all the wheels + // + // Created: 15 May 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + TGeoVolume* endWheelsVol = new TGeoVolumeAssembly("EndWheelsSideC"); + endWheelsVol->SetVisibility(kTRUE); + + for (Int_t jLay = 0; jLay < sNumberInnerLayers; jLay++) + ibEndWheelSideC(jLay, endWheelsVol, mgr); + + // Return the wheels + return endWheelsVol; +} + +TGeoVolume* V3Services::createCYSSAssembly(const TGeoManager* mgr) +{ + // + // Creates the CYSS Assembly (i.e. the supporting cylinder and cone) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // a TGeoVolume(Assembly) with all the elements + // + // Created: 21 Oct 2019 Mario Sitta + // Updated: 02 Dec 2019 Mario Sitta Full cylinder implemented + // + + static const Double_t sCyssFlangeAZpos = 9.0 * sMm; + static const Double_t sCyssFlangeCZpos = 1.0 * sMm; + + Double_t zlen, zpos; + + TGeoVolume* cyssVol = new TGeoVolumeAssembly("IBCYSSAssembly"); + cyssVol->SetVisibility(kTRUE); + + TGeoVolume* cyssCylinder = ibCyssCylinder(mgr); + zlen = (static_cast(cyssCylinder->GetShape()))->GetDz(); + zpos = sIBCYSSFlangeCZPos - sCyssFlangeCZpos - zlen; + cyssVol->AddNode(cyssCylinder, 1, new TGeoTranslation(0, 0, -zpos)); + cyssVol->AddNode(cyssCylinder, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + TGeoVolume* cyssCone = ibCyssCone(mgr); + zpos = -zpos + zlen - (static_cast(cyssCone->GetShape()))->GetZ(2); + cyssVol->AddNode(cyssCone, 1, new TGeoTranslation(0, 0, zpos)); + cyssVol->AddNode(cyssCone, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + TGeoVolume* cyssFlangeA = ibCyssFlangeSideA(mgr); + Int_t nZPlanes = (static_cast(cyssCone->GetShape()))->GetNz(); + zpos = zpos + (static_cast(cyssCone->GetShape()))->GetZ(nZPlanes - 1) + sCyssFlangeAZpos; + cyssVol->AddNode(cyssFlangeA, 1, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 180, 0))); + cyssVol->AddNode(cyssFlangeA, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 0, 180, 0))); + + TGeoVolume* cyssFlangeC = ibCyssFlangeSideC(mgr); + zpos = sIBCYSSFlangeCZPos; + cyssVol->AddNode(cyssFlangeC, 1, new TGeoTranslation(0, 0, -zpos)); + cyssVol->AddNode(cyssFlangeC, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + // Return the whole assembly + return cyssVol; +} + +void V3Services::createMBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Middle Barrel End Wheels on Side A + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 24 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) + obEndWheelSideA(jLay, mother, mgr); +} + +void V3Services::createMBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Middle Barrel End Wheels on Side C + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberMiddlLayers; jLay++) + mbEndWheelSideC(jLay, mother, mgr); +} + +void V3Services::createOBEndWheelsSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel End Wheels on Side A + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 27 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) + obEndWheelSideA(jLay + sNumberMiddlLayers, mother, mgr); +} + +void V3Services::createOBEndWheelsSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel End Wheels on Side C + // + // Input: + // mother : the volume hosting the wheels + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 27 Sep 2019 Mario Sitta + // + + for (Int_t jLay = 0; jLay < sNumberOuterLayers; jLay++) + obEndWheelSideC(jLay, mother, mgr); +} + +void V3Services::createOBConeSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel Cone on Side A + // + // Input: + // mother : the volume hosting the cones + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 03 Feb 2020 Mario Sitta + // + + obConeSideA(mother, mgr); + obConeTraysSideA(mother, mgr); +} + +void V3Services::createOBConeSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Outer Barrel Cone on Side C + // + // Input: + // mother : the volume hosting the cones + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Jan 2020 Mario Sitta + // + + obConeSideC(mother, mgr); +} + +void V3Services::ibEndWheelSideA(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side A + // for a given layer of the Inner Barrel + // (Layer 0: ALIEC0SUP0183+ALIEC0UP0127) + // (Layer 1: ALIEC0SUP0173+ALIEC0UP0124) + // (Layer 2: ALIEC0SUP0139+ALIEC0UP0125) + // + // Input: + // iLay : the layer number + // endWheel : the whole end wheel volume + // where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 19 Jun 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + // The Basis Cone A Side and the Reinforcement C Side are physically two + // different pieces put together. For sake of simplicity here they are + // made out of the same TGeoPcon volume. Moreover they are two halves, + // so here they are made as a single cone. + static const Double_t sConeATotalLength[3] = {191.0 * sMm, 184.0 * sMm, 177 * sMm}; + static const Double_t sConeAIntSectZlen1[3] = {40.35 * sMm, 39.0 * sMm, 36.0 * sMm}; + static const Double_t sConeAIntSectZlen2[3] = {47.0 * sMm, 44.0 * sMm, 41.0 * sMm}; + static const Double_t sConeAIntSectDmin[3] = {55.8 * sMm, 71.8 * sMm, 87.8 * sMm}; + static const Double_t sConeAIntSectDmax[3] = {57.0 * sMm, 73.0 * sMm, 89.0 * sMm}; + static const Double_t sConeAExtSectZlen1[3] = {60.0 * sMm, 47.0 * sMm, 44.0 * sMm}; + static const Double_t sConeAExtSectZlen2[3] = {66.0 * sMm, 52.0 * sMm, 50.0 * sMm}; + static const Double_t sConeAExtSectDmin[3] = {114.0 * sMm, 174.0 * sMm, 234.0 * sMm}; + static const Double_t sConeAExtSectDmax[3] = {116.0 * sMm, 176.0 * sMm, 236.0 * sMm}; + static const Double_t sConeASectThicker = 0.8 * sMm; + static const Double_t sConeAOpeningAngle[3] = {20.0, 30.0, 40.2}; // Deg + + static const Int_t sConeAWallNHoles[3] = {6, 8, 10}; + static const Double_t sConeAWallHoleD = 4.5 * sMm; + static const Double_t sConeAWallHoleZpos = 4.0 * sMm; + + static const Double_t sConeACentralHole1D = 3.0 * sMm; + static const Double_t sConeACentralHole2D = 3.4 * sMm; + static const Double_t sConeACentralHole3D = 3.0 * sMm; + static const Double_t sConeACentralHole1Z = 20.0 * sMm; + static const Double_t sConeACentralHole2Z = 30.0 * sMm; + static const Double_t sConeACentralHole3Z[3] = {177.0 * sMm, 170.0 * sMm, 163.0 * sMm}; + + // The Cone Reinforcement + static const Double_t sConeARenfDmin[3] = {54.3 * sMm, 69.85 * sMm, 85.0 * sMm}; + static const Double_t sConeARenfZlen = 2.5 * sMm; + static const Double_t sConeARenfZpos = 14.5 * sMm; + + // The Middle Ring + static const Double_t sConeAMidRingDmin[3] = {56.0 * sMm, 116.0 * sMm, 176.0 * sMm}; + static const Double_t sConeAMidRingDmax[3] = {58.0 * sMm, 118.0 * sMm, 178.0 * sMm}; + static const Double_t sConeAMidRingZlen = 42.0 * sMm; + + static const Double_t sConeAMidRingZpos[3] = {5.0 * sMm, 0.0 * sMm, 0.0 * sMm}; + + // The Ribs + static const Int_t sConeANRibs[3] = {6, 8, 10}; + static const Double_t sConeARibsZpos = 17.0 * sMm; + + // The End Wheel Steps + static const Double_t sConeAStepXdispl[3] = {4.0 * sMm, 6.5 * sMm, 8.5 * sMm}; + static const Double_t sConeAStepYdispl[3] = {24.4 * sMm, 32.1 * sMm, 39.6 * sMm}; + static const Double_t sConeAStepR[3] = {27.8 * sMm, 35.8 * sMm, 43.8 * sMm}; + + static const Double_t sConeAStepZlen = 14.0 * sMm; + + static const Double_t sConeAStepHoleXpos = 3.0 * sMm; + static const Double_t sConeAStepHoleZpos = 4.0 * sMm; + static const Double_t sConeAStepHoleZdist = 4.0 * sMm; + + static const Double_t sConeAStepHolePhi[3] = {30.0, 22.5, 18.0}; // Deg + static const Double_t sConeAStepHolePhi0[3] = {0.7, -16.2, -10.5}; // Deg + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, thick, phimin, dphi; + Double_t xpos, ypos, zpos, zref; + + // Create the whole cone (Basic + Reinforcement) as a CompositeShape + // (a single Pcon minus the holes) + TGeoPcon* coneabasis = new TGeoPcon(Form("coneabasis%d", iLay), 0, 360, 15); + + rmin = sConeAIntSectDmin[iLay] / 2; + rmax = sConeAIntSectDmax[iLay] / 2; + coneabasis->DefineSection(0, 0., rmin, rmax); + zpos = sConeARenfZpos; + coneabasis->DefineSection(1, zpos, rmin, rmax); + rmin = sConeARenfDmin[iLay] / 2; + coneabasis->DefineSection(2, zpos, rmin, rmax); + zpos += sConeARenfZlen; + coneabasis->DefineSection(3, zpos, rmin, rmax); + rmin = coneabasis->GetRmin(0); + coneabasis->DefineSection(4, zpos, rmin, rmax); + coneabasis->DefineSection(5, sConeAIntSectZlen1[iLay], rmin, rmax); + rmax += sConeASectThicker; + coneabasis->DefineSection(6, sConeAIntSectZlen1[iLay], rmin, rmax); + coneabasis->DefineSection(7, sConeAIntSectZlen2[iLay], rmin, rmax); + rmin = coneabasis->GetRmax(1); + coneabasis->DefineSection(8, sConeAIntSectZlen2[iLay], rmin, rmax); + rmin = sConeAExtSectDmin[iLay] / 2 - sConeASectThicker; + rmax = sConeAExtSectDmin[iLay] / 2; + zlen = sConeAIntSectZlen2[iLay] + (rmin - coneabasis->GetRmin(4)) / TMath::Tan(sConeAOpeningAngle[iLay] * TMath::DegToRad()); + coneabasis->DefineSection(9, zlen, rmin, rmax); + zlen = sConeATotalLength[iLay] - sConeAExtSectZlen2[iLay]; + coneabasis->DefineSection(10, zlen, rmin, rmax); + rmax = sConeAExtSectDmax[iLay] / 2; + coneabasis->DefineSection(11, zlen, rmin, rmax); + zlen = sConeATotalLength[iLay] - sConeAExtSectZlen1[iLay]; + coneabasis->DefineSection(12, zlen, rmin, rmax); + rmin = sConeAExtSectDmin[iLay] / 2; + coneabasis->DefineSection(13, zlen, rmin, rmax); + coneabasis->DefineSection(14, sConeATotalLength[iLay], rmin, rmax); + + TString coneAComposite = Form("coneabasis%d", iLay); + + // The holes in the vertical wall + thick = coneabasis->GetRmax(0) - coneabasis->GetRmin(0); + TGeoTube* coneawallhole = new TGeoTube(Form("coneawallhole%d", iLay), 0, sConeAWallHoleD / 2, 4 * thick); + + rmin = sConeAIntSectDmax[iLay] / 2 - thick / 2; + zpos = sConeAWallHoleZpos; + dphi = 180. / sConeAWallNHoles[iLay]; + phimin = dphi / 2.; + for (Int_t ihole = 0; ihole < 2 * sConeAWallNHoles[iLay]; ihole++) { + Double_t phi = phimin + ihole * dphi; + xpos = rmin * TMath::Sin(phi * TMath::DegToRad()); + ypos = rmin * TMath::Cos(phi * TMath::DegToRad()); + TGeoCombiTrans* coneawhmat = new TGeoCombiTrans(Form("coneawhmat%dl%d", ihole, iLay), xpos, ypos, zpos, new TGeoRotation("", -phi, 90, 0)); + coneawhmat->RegisterYourself(); + coneAComposite += Form("-coneawallhole%d:coneawhmat%dl%d", iLay, ihole, iLay); + } + + // The central holes + TGeoTube* coneacenthole1 = new TGeoTube(Form("coneacenthole1l%d", iLay), 0, sConeACentralHole1D / 2, 4 * thick); + + TGeoCombiTrans* coneach1mat1 = new TGeoCombiTrans(Form("coneach1mat1l%d", iLay), 0, rmin, sConeACentralHole1Z, new TGeoRotation("", 0, 90, 0)); + coneach1mat1->RegisterYourself(); + TGeoCombiTrans* coneach1mat2 = new TGeoCombiTrans(Form("coneach1mat2l%d", iLay), 0, -rmin, sConeACentralHole1Z, new TGeoRotation("", 0, 90, 0)); + coneach1mat2->RegisterYourself(); + + coneAComposite += Form("-coneacenthole1l%d:coneach1mat1l%d-coneacenthole1l%d:coneach1mat2l%d", iLay, iLay, iLay, iLay); + + TGeoTube* coneacenthole2 = new TGeoTube(Form("coneacenthole2l%d", iLay), 0, sConeACentralHole2D / 2, 4 * thick); + + TGeoCombiTrans* coneach2mat1 = new TGeoCombiTrans(Form("coneach2mat1l%d", iLay), 0, rmin, sConeACentralHole2Z, new TGeoRotation("", 0, 90, 0)); + coneach2mat1->RegisterYourself(); + TGeoCombiTrans* coneach2mat2 = new TGeoCombiTrans(Form("coneach2mat2l%d", iLay), 0, -rmin, sConeACentralHole2Z, new TGeoRotation("", 0, 90, 0)); + coneach2mat2->RegisterYourself(); + + coneAComposite += Form("-coneacenthole2l%d:coneach2mat1l%d-coneacenthole2l%d:coneach2mat2l%d", iLay, iLay, iLay, iLay); + + TGeoTube* coneacenthole3 = new TGeoTube(Form("coneacenthole3l%d", iLay), 0, sConeACentralHole3D / 2, 4 * thick); + + rmin = sConeAExtSectDmax[iLay] / 2 - thick / 2; + TGeoCombiTrans* coneach3mat1 = new TGeoCombiTrans(Form("coneach3mat1l%d", iLay), 0, rmin, sConeACentralHole3Z[iLay], new TGeoRotation("", 0, 90, 0)); + coneach3mat1->RegisterYourself(); + TGeoCombiTrans* coneach3mat2 = new TGeoCombiTrans(Form("coneach3mat2l%d", iLay), 0, -rmin, sConeACentralHole3Z[iLay], new TGeoRotation("", 0, 90, 0)); + coneach3mat2->RegisterYourself(); + + coneAComposite += Form("-coneacenthole3l%d:coneach3mat1l%d-coneacenthole3l%d:coneach3mat2l%d", iLay, iLay, iLay, iLay); + + TGeoCompositeShape* coneABasisSh = new TGeoCompositeShape(coneAComposite.Data()); + + // The Middle Ring (a Tube) + rmin = sConeAMidRingDmin[iLay] / 2; + rmax = sConeAMidRingDmax[iLay] / 2; + zlen = sConeAMidRingZlen / 2; + TGeoTube* midRingSh = new TGeoTube(Form("midRingSh%d", iLay), rmin, rmax, zlen); + + // A Rib (a TGeoXtru) + TGeoXtru* coneARibSh = ibEndWheelARibShape(iLay); + + // Now the Step as a Composite Shape (subtraction of a Pcon from a BBox) + // (cutting volume should be slightly larger than desired region) + rmin = sConeAStepR[iLay]; + + xlen = TMath::Sqrt(rmin * rmin - sConeAStepYdispl[iLay] * sConeAStepYdispl[iLay]) - sConeAStepXdispl[iLay]; + ylen = TMath::Sqrt(rmin * rmin - sConeAStepXdispl[iLay] * sConeAStepXdispl[iLay]) - sConeAStepYdispl[iLay]; + TGeoBBox* stepBoxSh = new TGeoBBox(Form("stepBoxASh%d", iLay), xlen / 2, ylen / 2, sConeAStepZlen / 2); + + xpos = sConeAStepXdispl[iLay] + stepBoxSh->GetDX(); + ypos = sConeAStepYdispl[iLay] + stepBoxSh->GetDY(); + TGeoTranslation* stepBoxTr = new TGeoTranslation(Form("stepBoxATr%d", iLay), xpos, ypos, 0); + stepBoxTr->RegisterYourself(); + + phimin = 90. - TMath::ACos(sConeAStepYdispl[iLay] / rmin) * TMath::RadToDeg() - 5; + dphi = 90. - TMath::ASin(sConeAStepXdispl[iLay] / rmin) * TMath::RadToDeg() - phimin + 10; + rmax = rmin + 2 * stepBoxSh->GetDY(); + + TGeoPcon* stepPconSh = new TGeoPcon(Form("stepPconASh%d", iLay), phimin, dphi, 2); + stepPconSh->DefineSection(0, -1.05 * sConeAStepZlen / 2, rmin, rmax); + stepPconSh->DefineSection(1, 1.05 * sConeAStepZlen / 2, rmin, rmax); + + TGeoCompositeShape* stepASh = new TGeoCompositeShape(Form("stepBoxASh%d:stepBoxATr%d-stepPconASh%d", iLay, iLay, iLay)); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + TGeoMedium* medPEEK = mgr->GetMedium("EC0_PEEKCF30$"); + + TGeoVolume* coneABasisVol = new TGeoVolume(Form("ConeABasis%d", iLay), coneABasisSh, medCarbon); + coneABasisVol->SetFillColor(kBlue); + coneABasisVol->SetLineColor(kBlue); + + TGeoVolume* midRingVol = new TGeoVolume(Form("ConeAMidRing%d", iLay), midRingSh, medCarbon); + coneABasisVol->SetFillColor(kBlue); + coneABasisVol->SetLineColor(kBlue); + + TGeoVolume* coneARibVol = new TGeoVolume(Form("ConeARibVol%d", iLay), coneARibSh, medCarbon); + coneARibVol->SetFillColor(kBlue); + coneARibVol->SetLineColor(kBlue); + + TGeoVolume* stepAVol = new TGeoVolume(Form("ConeAStep%d", iLay), stepASh, medPEEK); + stepAVol->SetFillColor(kBlue); + stepAVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // (origin of local coordinates is at smaller end of Cone Basis) + zref = sIBWheelACZdist / 2 - (sConeAStepHoleZpos + sConeAStepHoleZdist); + + zpos = zref; + endWheel->AddNode(coneABasisVol, 1, new TGeoTranslation(0, 0, zpos)); + + zpos = zref + sConeATotalLength[iLay] - sConeAMidRingZpos[iLay] - midRingSh->GetDz(); + endWheel->AddNode(midRingVol, 1, new TGeoTranslation(0, 0, zpos)); + + rmin = sConeAExtSectDmin[iLay] / 2 - 0.035; + zpos = zref + sConeATotalLength[iLay] - sConeARibsZpos; + dphi = 180. / sConeANRibs[iLay]; + for (Int_t irib = 0; irib < 2 * sConeANRibs[iLay]; irib++) { + Double_t phi = irib * dphi; + xpos = rmin * TMath::Sin(phi * TMath::DegToRad()); + ypos = rmin * TMath::Cos(phi * TMath::DegToRad()); + endWheel->AddNode(coneARibVol, 1, new TGeoCombiTrans(xpos, -ypos, zpos, new TGeoRotation("", 90 + phi, 90, -90))); + } + + // The position of the Steps is given wrt the holes (see eg. ALIEC0UP0187) + dphi = 180. - sConeAStepHolePhi0[iLay]; + + Int_t numberOfStaves = GeometryTGeo::Instance()->getNumberOfStaves(iLay); + zpos = zref + (static_cast(stepAVol->GetShape()))->GetDZ(); + for (Int_t j = 0; j < numberOfStaves; j++) { + Double_t phi = dphi + j * sConeAStepHolePhi[iLay]; + endWheel->AddNode(stepAVol, j + 1, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 180, -90 - phi))); + } +} + +void V3Services::ibEndWheelSideC(const Int_t iLay, TGeoVolume* endWheel, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side C + // for a given layer of the Inner Barrel + // (Layer 0: ALIEC0SUP0186+ALIEC0UP0126) + // (Layer 1: ALIEC0SUP0176+ALIEC0UP0123) + // (Layer 2: ALIEC0SUP0143+ALIEC0UP0121) + // + // Input: + // iLay : the layer number + // endWheel : the whole end wheel volume + // where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 15 May 2019 Mario Sitta + // (partially based on P.Namwongsa implementation in AliRoot) + // + + // The Basis C Side and the Reinforcement C Side are physically two + // different pieces put together. For sake of simplicity here they are + // made out of the same TGeoPcon volume. Moreover they are two halves, + // so here they are made as a single cylinder. + // The End Wheel Basis + static const Double_t sEndWheelCDmax[3] = {57.0 * sMm, 73.0 * sMm, 89.0 * sMm}; + static const Double_t sEndWheelCDmin[3] = {44.5 * sMm, 58.0 * sMm, 74.0 * sMm}; + static const Double_t sEndWheelCHeigh[3] = {25.0 * sMm, 22.5 * sMm, 20.0 * sMm}; + static const Double_t sEndWheelCThick = 0.6 * sMm; + + static const Int_t sEndWCWallNHoles[3] = {6, 8, 10}; + static const Double_t sEndWCWallHoleD = 4.5 * sMm; + static const Double_t sEndWCWallHoleZpos = 4.0 * sMm; + + static const Int_t sEndWCBaseNBigHoles = 5; + static const Int_t sEndWCBaseNSmalHoles = 6; + static const Double_t sEndWCBaseBigHoleD = 3.6 * sMm; + static const Double_t sEndWCBaseSmalHoleD = 2.5 * sMm; + static const Double_t sEndWCBaseHolesDpos[3] = {50.0 * sMm, 64.0 * sMm, 80.0 * sMm}; + static const Double_t sEndWCBaseHolesPhi = 15.0; // Deg + + // The End Wheel Reinforcement + static const Double_t sEndWCRenfDmin[3] = {44.0 * sMm, 58.0 * sMm, 74.0 * sMm}; + static const Double_t sEndWCRenfDint[3] = {55.0 * sMm, 71.0 * sMm, 87.0 * sMm}; + static const Double_t sEndWCRenfHeigh[3] = {4.0 * sMm, 3.0 * sMm, 3.0 * sMm}; + static const Double_t sEndWCRenfThick = 0.6 * sMm; + + static const Double_t sEndWCRenfZpos = 14.2 * sMm; + + static const Int_t sEndWCRenfNSmalHoles[3] = {5, 7, 9}; + + // The End Wheel Steps + static const Double_t sEndWCStepXdispl[3] = {4.0 * sMm, 6.5 * sMm, 8.5 * sMm}; + static const Double_t sEndWCStepYdispl[3] = {24.4 * sMm, 32.1 * sMm, 39.6 * sMm}; + static const Double_t sEndWCStepR[3] = {27.8 * sMm, 35.8 * sMm, 43.8 * sMm}; + + static const Double_t sEndWCStepZlen = 14.0 * sMm; + + static const Double_t sEndWCStepHoleXpos = 3.0 * sMm; + static const Double_t sEndWCStepHoleZpos = 4.0 * sMm; + static const Double_t sEndWCStepHoleZdist = 4.0 * sMm; + + static const Double_t sEndWCStepHolePhi[3] = {30.0, 22.5, 18.0}; // Deg + static const Double_t sEndWCStepHolePhi0[2] = {9.5, 10.5}; // Deg + static const Double_t sEndWCStepYlow = 7.0 * sMm; + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + + // Create the whole wheel (Basic + Reinforcement) as a CompositeShape + // (a single Pcon minus the (copious!) holes) + TGeoPcon* endwcbasis = new TGeoPcon(Form("endwcbasis%d", iLay), 0, 360, 10); + + rmin = sEndWheelCDmax[iLay] / 2 - sEndWheelCThick; + endwcbasis->DefineSection(0, 0., rmin, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(1, sEndWCRenfZpos, rmin, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(2, sEndWCRenfZpos, sEndWCRenfDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + zlen = sEndWCRenfZpos + sEndWCRenfThick; + endwcbasis->DefineSection(3, zlen, sEndWCRenfDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(4, zlen, sEndWCRenfDint[iLay] / 2, sEndWheelCDmax[iLay] / 2); + zlen = sEndWCRenfZpos + sEndWCRenfHeigh[iLay]; + endwcbasis->DefineSection(5, zlen, sEndWCRenfDint[iLay] / 2, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(6, zlen, rmin, sEndWheelCDmax[iLay] / 2); + zlen = sEndWheelCHeigh[iLay] - sEndWheelCThick; + endwcbasis->DefineSection(7, zlen, rmin, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(8, zlen, sEndWheelCDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + endwcbasis->DefineSection(9, sEndWheelCHeigh[iLay], sEndWheelCDmin[iLay] / 2, sEndWheelCDmax[iLay] / 2); + + TString endWheelComposite = Form("endwcbasis%d", iLay); + + // The holes in the vertical wall + TGeoTube* endwcwalhol = new TGeoTube(Form("endwcwalhol%d", iLay), 0, sEndWCWallHoleD / 2, 4 * sEndWheelCThick); + + rmin = sEndWheelCDmax[iLay] / 2 - sEndWheelCThick / 2; + zpos = sEndWCWallHoleZpos; + dphi = 180. / sEndWCWallNHoles[iLay]; + phimin = dphi / 2.; + for (Int_t ihole = 0; ihole < 2 * sEndWCWallNHoles[iLay]; ihole++) { + Double_t phi = phimin + ihole * dphi; + xpos = rmin * TMath::Sin(phi * TMath::DegToRad()); + ypos = rmin * TMath::Cos(phi * TMath::DegToRad()); + TGeoCombiTrans* endwcwhmat = new TGeoCombiTrans(Form("endwcwhmat%dl%d", ihole, iLay), xpos, ypos, zpos, new TGeoRotation("", -phi, 90, 0)); + endwcwhmat->RegisterYourself(); + endWheelComposite += Form("-endwcwalhol%d:endwcwhmat%dl%d", iLay, ihole, iLay); + } + + // The holes in the base + TGeoTube* endwcbasBhol = new TGeoTube(Form("endwcbasBhol%d", iLay), 0, sEndWCBaseBigHoleD / 2, 1.5 * sEndWheelCThick); + + TGeoTube* endwcbasShol = new TGeoTube(Form("endwcbasShol%d", iLay), 0, sEndWCBaseSmalHoleD / 2, 1.5 * sEndWheelCThick); + + rmin = sEndWCBaseHolesDpos[iLay] / 2; + zpos = (endwcbasis->GetZ(8) + endwcbasis->GetZ(9)) / 2; + + char holename[strlen(endwcbasBhol->GetName()) + 1]; + + phimin = 0.; + for (Int_t ihole = 0; ihole < (sEndWCBaseNBigHoles + sEndWCBaseNSmalHoles); ihole++) { + phimin += sEndWCBaseHolesPhi; + xpos = rmin * TMath::Cos(phimin * TMath::DegToRad()); + ypos = rmin * TMath::Sin(phimin * TMath::DegToRad()); + TGeoTranslation* endwcbshmata = new TGeoTranslation(Form("endwcbshmat%dl%da", ihole, iLay), xpos, ypos, zpos); + endwcbshmata->RegisterYourself(); + TGeoTranslation* endwcbshmatb = new TGeoTranslation(Form("endwcbshmat%dl%db", ihole, iLay), -xpos, -ypos, zpos); + endwcbshmatb->RegisterYourself(); + if ((ihole > 1 && ihole < 5) || (ihole > 5 && ihole < 9)) // Small holes + strcpy(holename, endwcbasShol->GetName()); + else + strcpy(holename, endwcbasBhol->GetName()); + endWheelComposite += Form("-%s:endwcbshmat%dl%da-%s:endwcbshmat%dl%db", holename, ihole, iLay, holename, ihole, iLay); + } + + // The holes in the reinforcement + zpos = (endwcbasis->GetZ(2) + endwcbasis->GetZ(3)) / 2; + + phimin = 0.; + dphi = 180. / (sEndWCRenfNSmalHoles[iLay] + 1); + for (Int_t ihole = 0; ihole < sEndWCRenfNSmalHoles[iLay]; ihole++) { + phimin += dphi; + xpos = rmin * TMath::Cos(phimin * TMath::DegToRad()); + ypos = rmin * TMath::Sin(phimin * TMath::DegToRad()); + TGeoTranslation* endwcrshmata = new TGeoTranslation(Form("endwcrshmat%dl%da", ihole, iLay), xpos, ypos, zpos); + endwcrshmata->RegisterYourself(); + TGeoTranslation* endwcrshmatb = new TGeoTranslation(Form("endwcrshmat%dl%db", ihole, iLay), -xpos, -ypos, zpos); + endwcrshmatb->RegisterYourself(); + endWheelComposite += Form("-endwcbasShol%d:endwcrshmat%dl%da-endwcbasShol%d:endwcrshmat%dl%db", iLay, ihole, iLay, iLay, ihole, iLay); + } + + TGeoCompositeShape* endWheelCSh = new TGeoCompositeShape(endWheelComposite.Data()); + + // Now the Step as a Composite Shape (subtraction of a Pcon from a BBox) + // (cutting volume should be slightly larger than desired region) + rmin = sEndWCStepR[iLay]; + + xlen = TMath::Sqrt(rmin * rmin - sEndWCStepYdispl[iLay] * sEndWCStepYdispl[iLay]) - sEndWCStepXdispl[iLay]; + ylen = TMath::Sqrt(rmin * rmin - sEndWCStepXdispl[iLay] * sEndWCStepXdispl[iLay]) - sEndWCStepYdispl[iLay]; + TGeoBBox* stepBoxSh = new TGeoBBox(Form("stepBoxCSh%d", iLay), xlen / 2, ylen / 2, sEndWCStepZlen / 2); + + xpos = sEndWCStepXdispl[iLay] + stepBoxSh->GetDX(); + ypos = sEndWCStepYdispl[iLay] + stepBoxSh->GetDY(); + TGeoTranslation* stepBoxTr = new TGeoTranslation(Form("stepBoxCTr%d", iLay), xpos, ypos, 0); + stepBoxTr->RegisterYourself(); + + phimin = 90. - TMath::ACos(sEndWCStepYdispl[iLay] / rmin) * TMath::RadToDeg() - 5; + dphi = 90. - TMath::ASin(sEndWCStepXdispl[iLay] / rmin) * TMath::RadToDeg() - phimin + 10; + rmax = rmin + 2 * stepBoxSh->GetDY(); + + TGeoPcon* stepPconSh = new TGeoPcon(Form("stepPconCSh%d", iLay), phimin, dphi, 2); + stepPconSh->DefineSection(0, -1.05 * sEndWCStepZlen / 2, rmin, rmax); + stepPconSh->DefineSection(1, 1.05 * sEndWCStepZlen / 2, rmin, rmax); + + TGeoCompositeShape* stepCSh = new TGeoCompositeShape(Form("stepBoxCSh%d:stepBoxCTr%d-stepPconCSh%d", iLay, iLay, iLay)); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + TGeoMedium* medPEEK = mgr->GetMedium("EC0_PEEKCF30$"); + + TGeoVolume* endWheelCVol = new TGeoVolume(Form("EndWheelCBasis%d", iLay), endWheelCSh, medCarbon); + endWheelCVol->SetFillColor(kBlue); + endWheelCVol->SetLineColor(kBlue); + + TGeoVolume* stepCVol = new TGeoVolume(Form("EndWheelCStep%d", iLay), stepCSh, medPEEK); + stepCVol->SetFillColor(kBlue); + stepCVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + zpos = sIBWheelACZdist / 2 - (sEndWCStepHoleZpos + sEndWCStepHoleZdist); + endWheel->AddNode(endWheelCVol, 1, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 0, 180, 0))); + + // The position of the Steps is given wrt the holes (see eg. ALIEC0UP0187) + dphi = sEndWCStepHolePhi0[iLay]; + + Int_t numberOfStaves = GeometryTGeo::Instance()->getNumberOfStaves(iLay); + zpos += (static_cast(stepCVol->GetShape()))->GetDZ(); + for (Int_t j = 0; j < numberOfStaves; j++) { + Double_t phi = dphi + j * sEndWCStepHolePhi[iLay]; + endWheel->AddNode(stepCVol, j + 1, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 180, -90 - phi))); + } +} + +TGeoXtru* V3Services::ibEndWheelARibShape(const Int_t iLay) +{ + // + // Creates the shape of a Rib on Side A cone + // (Layer 0: ALIEC0SUP0182) + // (Layer 1: ALIEC0SUP0172) + // (Layer 2: ALIEC0SUP0136) + // + // Input: + // iLay : the layer number + // + // Output: + // + // Return: + // the Rib shape as a TGeoXtru + // + // Created: 23 Aug 2019 Mario Sitta + // + + static const Int_t sConeARibNVert = 8; + + static const Double_t sConeARibWidth = 27.3 * sMm; + static const Double_t sConeARibTotalLen[3] = {98.03 * sMm, 104.65 * sMm, 101.43 * sMm}; + static const Double_t sConeARibIntLen[3] = {50.0 * sMm, 40.0 * sMm, 28.5 * sMm}; + static const Double_t sConeARibStep[3] = {1.0 * sMm, 1.1 * sMm, 1.0 * sMm}; + static const Double_t sConeARibLenToStep[3] = {42.0 * sMm, 29.0 * sMm, 26.0 * sMm}; + static const Double_t sConeARibLenAfterStep[3] = {50.5 * sMm, 37.0 * sMm, 33.5 * sMm}; + static const Double_t sConeARibVertexPos[3] = {9.0 * sMm, 40.0 * sMm, 58.0 * sMm}; + static const Double_t sConeARibIntAngle = 45.0; // Deg + static const Double_t sConeARibVertexAngle[2] = {20.0, 30.0}; // Deg + + static const Double_t sConeARibThick = 0.9 * sMm; + + // Local variables + Double_t xtru[sConeARibNVert], ytru[sConeARibNVert]; + + // Rib shapes for Layer 0 and Layers 1,2 are different + xtru[0] = 0.; + ytru[0] = 0.; + xtru[1] = sConeARibLenToStep[iLay]; + ytru[1] = 0.; + xtru[2] = xtru[1]; + ytru[2] = sConeARibStep[iLay]; + xtru[3] = sConeARibLenAfterStep[iLay]; + ytru[3] = ytru[2]; + xtru[4] = sConeARibTotalLen[iLay]; + if (iLay == 0) { + ytru[4] = sConeARibWidth - sConeARibVertexPos[iLay]; + xtru[5] = sConeARibIntLen[iLay] + sConeARibVertexPos[iLay] / TMath::Tan(sConeARibIntAngle * TMath::DegToRad()); + } else { + ytru[4] = sConeARibVertexPos[iLay]; + xtru[5] = sConeARibIntLen[iLay] + (sConeARibVertexPos[iLay] - sConeARibWidth) / TMath::Tan(sConeARibVertexAngle[iLay - 1] * TMath::DegToRad()); + } + ytru[5] = ytru[4]; + xtru[6] = sConeARibIntLen[iLay]; + ytru[6] = sConeARibWidth; + xtru[7] = 0.; + ytru[7] = ytru[6]; + + // The actual Xtru + TGeoXtru* ribShape = new TGeoXtru(2); + ribShape->DefinePolygon(sConeARibNVert, xtru, ytru); + ribShape->DefineSection(0, -sConeARibThick / 2); + ribShape->DefineSection(1, sConeARibThick / 2); + + return ribShape; +} + +TGeoVolume* V3Services::ibCyssCylinder(const TGeoManager* mgr) +{ + // + // Creates the cylinder of the Inner Barrel CYSS + // (ALIEC0UP0191) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the cylinder as a TGeoVolume + // + // Created: 21 Oct 2019 Mario Sitta + // + + static const Double_t sCyssCylInnerD = 95.6 * sMm; + static const Double_t sCyssCylOuterD = 100.0 * sMm; + static const Double_t sCyssCylZLength = 353.0 * sMm; + static const Double_t sCyssCylFabricThick = 0.1 * sMm; + + // Local variables + Double_t rmin, rmax, zlen, phimin, phimax, dphi; + + // First create the shapes + rmin = sCyssCylInnerD / 2; + rmax = sCyssCylOuterD / 2; + zlen = sCyssCylZLength / 2; + TGeoTubeSeg* cyssOuterCylSh = new TGeoTubeSeg(rmin, rmax, zlen, 180, 360); + + rmin += sCyssCylFabricThick; + rmax -= sCyssCylFabricThick; + zlen -= sCyssCylFabricThick; + + dphi = TMath::ASin(sCyssCylFabricThick / rmax); + phimin = 180 + dphi * TMath::RadToDeg(); + phimax = 360 - dphi * TMath::RadToDeg(); + + TGeoTubeSeg* cyssInnerCylSh = new TGeoTubeSeg(rmin, rmax, zlen, phimin, phimax); + + // We have all shapes: now create the real volumes + TGeoMedium* medPrepreg = mgr->GetMedium("EC0_F6151B05M$"); + TGeoMedium* medRohacell = mgr->GetMedium("EC0_ROHACELL$"); + + TGeoVolume* cyssOuterCylVol = new TGeoVolume("IBCYSSCylinder", cyssOuterCylSh, medPrepreg); + cyssOuterCylVol->SetLineColor(35); + + TGeoVolume* cyssInnerCylVol = new TGeoVolume("IBCYSSCylinderFoam", cyssInnerCylSh, medRohacell); + cyssInnerCylVol->SetLineColor(kGreen); + + cyssOuterCylVol->AddNode(cyssInnerCylVol, 1, nullptr); + + // Finally return the cylinder volume + return cyssOuterCylVol; +} + +TGeoVolume* V3Services::ibCyssCone(const TGeoManager* mgr) +{ + // + // Creates the cone of the Inner Barrel CYSS + // (ALIEC0UP0190) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the cone as a TGeoVolume + // + // Created: 24 Oct 2019 Mario Sitta + // + + static const Double_t sCyssConeTotalLength = 150.0 * sMm; + + static const Double_t sCyssConeIntSectDmin = 100.0 * sMm; + static const Double_t sCyssConeIntSectDmax = 101.2 * sMm; + static const Double_t sCyssConeIntSectZlen = 23.0 * sMm; + static const Double_t sCyssConeIntCylZlen = 15.0 * sMm; + + static const Double_t sCyssConeExtSectDmin = 246.0 * sMm; + static const Double_t sCyssConeExtSectDmax = 257.2 * sMm; + static const Double_t sCyssConeExtSectZlen = 42.0 * sMm; + static const Double_t sCyssConeExtCylZlen = 40.0 * sMm; + + static const Double_t sCyssConeOpeningAngle = 40.0; // Deg + + static const Double_t sCyssConeFabricThick = 0.3 * sMm; + + // Local variables + Double_t rmin, rmax, zlen1, zlen2, phimin, phirot, dphi; + Double_t x1, y1, x2, y2, x3, y3, m, xin, yin; + + // The CYSS Cone is physically a single piece made by a cylindrical + // section, a conical section, and a second cylindrical section + // The cone and the second cylinder have a foam core + // Both are implemented as two Pcon's + + TGeoPcon* cyssConeSh = new TGeoPcon(180, 180, 6); + + rmin = sCyssConeIntSectDmin / 2; + rmax = sCyssConeIntSectDmax / 2; + cyssConeSh->DefineSection(0, 0, rmin, rmax); + cyssConeSh->DefineSection(1, sCyssConeIntCylZlen, rmin, rmax); + zlen1 = sCyssConeTotalLength - sCyssConeExtSectZlen; + rmax = yFrom2Points(sCyssConeIntCylZlen, sCyssConeIntSectDmax / 2, zlen1, sCyssConeExtSectDmax / 2, sCyssConeIntSectZlen); + cyssConeSh->DefineSection(2, sCyssConeIntSectZlen, rmin, rmax); + zlen2 = sCyssConeTotalLength - sCyssConeExtCylZlen; + rmin = yFrom2Points(sCyssConeIntSectZlen, sCyssConeIntSectDmin / 2, zlen2, sCyssConeExtSectDmin / 2, zlen1); + rmax = sCyssConeExtSectDmax / 2; + cyssConeSh->DefineSection(3, zlen1, rmin, rmax); + rmin = sCyssConeExtSectDmin / 2; + cyssConeSh->DefineSection(4, zlen2, rmin, rmax); + cyssConeSh->DefineSection(5, sCyssConeTotalLength, rmin, rmax); + + dphi = TMath::ASin(sCyssConeFabricThick / (0.5 * sCyssConeIntSectDmax)); + phimin = 180 + dphi * TMath::RadToDeg(); + phirot = 180 - 2 * dphi * TMath::RadToDeg(); + + // The foam cone is built from the points of the outer cone + TGeoPcon* cyssConeFoamSh = new TGeoPcon(phimin, phirot, 5); + + m = TMath::Tan(sCyssConeOpeningAngle * TMath::DegToRad()); + x1 = cyssConeSh->GetZ(2); + y1 = cyssConeSh->GetRmin(2); + x2 = cyssConeSh->GetZ(1); + y2 = cyssConeSh->GetRmin(1); + x3 = x1; + y3 = y2 + m * (x3 - x2); + + insidePoint(x1, y1, x2, y2, x3, y3, -sCyssConeFabricThick, xin, yin); + cyssConeFoamSh->DefineSection(0, xin, yin, yin); + + x3 = cyssConeSh->GetZ(3); + y3 = cyssConeSh->GetRmin(3); + + insidePoint(x3, y3, x1, y1, x2, y2, -sCyssConeFabricThick, xin, yin); + zlen1 = xin; + rmin = yin; + rmax = y2 + m * (zlen1 - x2); + cyssConeFoamSh->DefineSection(1, zlen1, rmin, rmax); + + x1 = cyssConeSh->GetZ(5); + y1 = cyssConeSh->GetRmax(5); + x2 = cyssConeSh->GetZ(3); + y2 = cyssConeSh->GetRmax(3); + x3 = cyssConeSh->GetZ(2); + y3 = cyssConeSh->GetRmax(2); + + insidePoint(x1, y1, x2, y2, x3, y3, -sCyssConeFabricThick, xin, yin); + zlen1 = xin; + rmin = cyssConeFoamSh->GetRmin(1) + m * (zlen1 - cyssConeFoamSh->GetZ(1)); + rmax = sCyssConeExtSectDmax / 2 - sCyssConeFabricThick; + cyssConeFoamSh->DefineSection(2, zlen1, rmin, rmax); + + rmin = sCyssConeExtSectDmin / 2 + sCyssConeFabricThick; + zlen1 = cyssConeSh->GetZ(4); + cyssConeFoamSh->DefineSection(3, zlen1, rmin, rmax); + + zlen1 = sCyssConeTotalLength - sCyssConeFabricThick; + cyssConeFoamSh->DefineSection(4, zlen1, rmin, rmax); + + // We have all shapes: now create the real volumes + TGeoMedium* medPrepreg = mgr->GetMedium("EC0_F6151B05M$"); + TGeoMedium* medRohacell = mgr->GetMedium("EC0_ROHACELL$"); + + TGeoVolume* cyssConeVol = new TGeoVolume("IBCYSSCone", cyssConeSh, medPrepreg); + cyssConeVol->SetLineColor(35); + + TGeoVolume* cyssConeFoamVol = new TGeoVolume("IBCYSSConeFoam", cyssConeFoamSh, medRohacell); + cyssConeFoamVol->SetLineColor(kGreen); + + cyssConeVol->AddNode(cyssConeFoamVol, 1, nullptr); + + // Finally return the cone volume + return cyssConeVol; +} + +TGeoVolume* V3Services::ibCyssFlangeSideA(const TGeoManager* mgr) +{ + // + // Creates the Flange on Side A for the Inner Barrel CYSS + // (ALIEC0UP0189) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the flange as a TGeoVolume + // + // Created: 28 Oct 2019 Mario Sitta + // + + // Radii of the steps + static const Double_t sCyssFlangeAStep1Dmin = 254.1 * sMm; + static const Double_t sCyssFlangeAStep1Dmax = 287.0 * sMm; + static const Double_t sCyssFlangeAStep2Dmax = 259.0 * sMm; + static const Double_t sCyssFlangeAStep3Dmin = 243.0 * sMm; + static const Double_t sCyssFlangeAStep3Dmax = 245.5 * sMm; + static const Double_t sCyssFlangeAStep4Dmax = 239.0 * sMm; + static const Double_t sCyssFlangeAInnerD = 236.0 * sMm; + static const Double_t sCyssFlangeAInRingD = 238.0 * sMm; + + // Heights of the steps + static const Double_t sCyssFlangeATotHei = 39.0 * sMm; + static const Double_t sCyssFlangeAStep1H = 5.5 * sMm; + static const Double_t sCyssFlangeAInRingH = 7.0 * sMm; + static const Double_t sCyssFlangeAInRingUp = 1.0 * sMm; + static const Double_t sCyssFlangeAStep2H = 9.0 * sMm; + static const Double_t sCyssFlangeAStep3H = 10.0 * sMm; + static const Double_t sCyssFlangeAStep4H = 8.5 * sMm; + + // The wings + static const Double_t sCyssFlangeAWingD = 307.0 * sMm; + static const Double_t sCyssFlangeAWingW = 16.0 * sMm; + + // Holes + static const Double_t sCyssFlangeANotchW = 3.0 * sMm; + + static const Double_t sCyssFlangeAHolesDpos = 274.0 * sMm; + + static const Double_t sCyssFlangeAHole1Num = 8; + static const Double_t sCyssFlangeAHole1D = 5.5 * sMm; + static const Double_t sCyssFlangeAHole1Phi0 = 10; // Deg + static const Double_t sCyssFlangeAHole1PhiStep = 20; // Deg + + static const Double_t sCyssFlangeAHole2D = 4.0 * sMm; + static const Double_t sCyssFlangeAHole2Phi = 20; // Deg + + static const Double_t sCyssFlangeAHole3D = 7.0 * sMm; + static const Double_t sCyssFlangeAHole3Phi = 6; // Deg + + static const Double_t sCyssFlangeAWingHoleD = 8.1 * sMm; + static const Double_t sCyssFlangeAWingHoleYpos = 9.0 * sMm; + static const Double_t sCyssFlangeAWingHoleRpos = 146.0 * sMm; + + // Local variables + Double_t rmin, rmax, zlen, phi, dphi; + Double_t xpos, ypos; + + // The CYSS Flange on Side A is physically a single piece. + // It is implemented as a CompositeShape of two Pcon's and one TubeSeg + // minus a huge number of holes + + // The flange body + TGeoPcon* cyssFlangeABody = new TGeoPcon("cyssflangeabody", 180, 180, 12); + + rmin = sCyssFlangeAStep1Dmin / 2; + rmax = sCyssFlangeAStep1Dmax / 2; + cyssFlangeABody->DefineSection(0, 0, rmin, rmax); + cyssFlangeABody->DefineSection(1, sCyssFlangeAStep1H, rmin, rmax); + rmax = sCyssFlangeAStep2Dmax / 2; + cyssFlangeABody->DefineSection(2, sCyssFlangeAStep1H, rmin, rmax); + cyssFlangeABody->DefineSection(3, sCyssFlangeAInRingH, rmin, rmax); + rmin = sCyssFlangeAStep3Dmin / 2; + cyssFlangeABody->DefineSection(4, sCyssFlangeAInRingH, rmin, rmax); + cyssFlangeABody->DefineSection(5, sCyssFlangeAStep2H, rmin, rmax); + rmax = sCyssFlangeAStep3Dmax / 2; + cyssFlangeABody->DefineSection(6, sCyssFlangeAStep2H, rmin, rmax); + zlen = sCyssFlangeATotHei - sCyssFlangeAStep3H; + cyssFlangeABody->DefineSection(7, zlen, rmin, rmax); + rmin = sCyssFlangeAInnerD / 2; + cyssFlangeABody->DefineSection(8, zlen, rmin, rmax); + zlen = sCyssFlangeATotHei - sCyssFlangeAStep4H; + cyssFlangeABody->DefineSection(9, zlen, rmin, rmax); + rmax = sCyssFlangeAStep4Dmax / 2; + cyssFlangeABody->DefineSection(10, zlen, rmin, rmax); + cyssFlangeABody->DefineSection(11, sCyssFlangeATotHei, rmin, rmax); + + // The inner ring + // We define half of it and put two copies to leave the notch space + rmin = sCyssFlangeAStep3Dmin / 2; + phi = 0.5 * (sCyssFlangeANotchW / rmin) * TMath::RadToDeg(); + + TGeoPcon* cyssFlangeAInRing = new TGeoPcon("cflangearing", 180, 90 - phi, 4); + + rmin = sCyssFlangeAInnerD / 2; + rmax = sCyssFlangeAInRingD / 2; + cyssFlangeAInRing->DefineSection(0, sCyssFlangeAInRingUp, rmin, rmax); + cyssFlangeAInRing->DefineSection(1, sCyssFlangeAInRingH, rmin, rmax); + rmax = sCyssFlangeAStep3Dmin / 2; + cyssFlangeAInRing->DefineSection(2, sCyssFlangeAInRingH, rmin, rmax); + cyssFlangeAInRing->DefineSection(3, sCyssFlangeAStep2H, rmin, rmax); + + TGeoRotation* flangeARingRot = new TGeoRotation("cringrot", 90 + phi, 0, 0); + flangeARingRot->RegisterYourself(); + + TString cyssFlangeAComposite = Form("cyssflangeabody+cflangearing+cflangearing:cringrot"); + + // The wings + rmin = sCyssFlangeAStep1Dmax / 2; + rmax = sCyssFlangeAWingD / 2; + zlen = sCyssFlangeAStep1H / 2; + phi = 0.5 * (sCyssFlangeAWingW / rmin) * TMath::RadToDeg(); + + TGeoTubeSeg* cyssFlangeAWing = new TGeoTubeSeg("cflangeawing", rmin, rmax, zlen, 270 - phi, 270 + phi); + + TGeoTranslation* cwingTR1 = new TGeoTranslation("cwingtr1", 0, 0, zlen); + cwingTR1->RegisterYourself(); + + TGeoCombiTrans* cwingCT2 = new TGeoCombiTrans("cwingct2", 0, 0, zlen, new TGeoRotation("", 90 - phi, 0, 0)); + cwingCT2->RegisterYourself(); + + TGeoCombiTrans* cwingCT3 = new TGeoCombiTrans("cwingct3", 0, 0, zlen, new TGeoRotation("", -90 + phi, 0, 0)); + cwingCT3->RegisterYourself(); + + cyssFlangeAComposite += "+cflangeawing:cwingtr1+cflangeawing:cwingct2+cflangeawing:cwingct3"; + + // The (many) holes + zlen = cyssFlangeAWing->GetDz(); + + // The 8 round holes (4 on each side) + rmax = sCyssFlangeAHole1D / 2; + TGeoTube* hole1 = new TGeoTube("hole1", 0, rmax, 2 * zlen); + + for (Int_t i = 0; i < sCyssFlangeAHole1Num / 2; i++) { + Double_t phi = sCyssFlangeAHole1Phi0 + i * sCyssFlangeAHole1PhiStep; + xpos = 0.5 * sCyssFlangeAHolesDpos * TMath::Sin(phi * TMath::DegToRad()); + ypos = 0.5 * sCyssFlangeAHolesDpos * TMath::Cos(phi * TMath::DegToRad()); + TGeoTranslation* hole1Tr1 = new TGeoTranslation(Form("hole1Tr1%d", i), xpos, -ypos, zlen); + hole1Tr1->RegisterYourself(); + TGeoTranslation* hole1Tr2 = new TGeoTranslation(Form("hole1Tr2%d", i), -xpos, -ypos, zlen); + hole1Tr2->RegisterYourself(); + cyssFlangeAComposite += Form("-hole1:hole1Tr1%d-hole1:hole1Tr2%d", i, i); + } + + // The 2 smaller round holes (1 on each side) + rmax = sCyssFlangeAHole2D / 2; + TGeoTube* hole2 = new TGeoTube("hole2", 0, rmax, 2 * zlen); + + xpos = 0.5 * sCyssFlangeAHolesDpos * TMath::Sin(sCyssFlangeAHole2Phi * TMath::DegToRad()); + ypos = 0.5 * sCyssFlangeAHolesDpos * TMath::Cos(sCyssFlangeAHole2Phi * TMath::DegToRad()); + TGeoTranslation* hole2Tr1 = new TGeoTranslation("hole2Tr1", xpos, -ypos, zlen); + hole2Tr1->RegisterYourself(); + TGeoTranslation* hole2Tr2 = new TGeoTranslation("hole2Tr2", -xpos, -ypos, zlen); + hole2Tr2->RegisterYourself(); + + cyssFlangeAComposite += "-hole2:hole2Tr1-hole2:hole2Tr2"; + + // The 2 bigger round holes (1 on each side) + rmax = sCyssFlangeAHole3D / 2; + TGeoTube* hole3 = new TGeoTube("hole3", 0, rmax, 2 * zlen); + + xpos = 0.5 * sCyssFlangeAHolesDpos * TMath::Sin(sCyssFlangeAHole3Phi * TMath::DegToRad()); + ypos = 0.5 * sCyssFlangeAHolesDpos * TMath::Cos(sCyssFlangeAHole3Phi * TMath::DegToRad()); + TGeoTranslation* hole3Tr1 = new TGeoTranslation("hole3Tr1", xpos, -ypos, zlen); + hole3Tr1->RegisterYourself(); + TGeoTranslation* hole3Tr2 = new TGeoTranslation("hole3Tr2", -xpos, -ypos, zlen); + hole3Tr2->RegisterYourself(); + + cyssFlangeAComposite += "-hole3:hole3Tr1-hole3:hole3Tr2"; + + // The holes in the wings + rmax = sCyssFlangeAWingHoleD / 2; + TGeoTube* wingHole = new TGeoTube("wingHole", 0, rmax, 2 * zlen); + + TGeoTranslation* wingHoleTr1 = new TGeoTranslation("wingHoleTr1", 0, -sCyssFlangeAWingHoleRpos, zlen); + wingHoleTr1->RegisterYourself(); + + TGeoTranslation* wingHoleTr2 = new TGeoTranslation("wingHoleTr2", sCyssFlangeAWingHoleRpos, -sCyssFlangeAWingHoleYpos, zlen); + wingHoleTr2->RegisterYourself(); + + TGeoTranslation* wingHoleTr3 = new TGeoTranslation("wingHoleTr3", -sCyssFlangeAWingHoleRpos, -sCyssFlangeAWingHoleYpos, zlen); + wingHoleTr3->RegisterYourself(); + + cyssFlangeAComposite += "-wingHole:wingHoleTr1-wingHole:wingHoleTr2-wingHole:wingHoleTr3"; + + // Lastly the hollows (évidements): a nightmare deserving its own method + TString cyssFlangeAHollows = ibCreateHollowsCyssFlangeSideA(zlen); + + cyssFlangeAComposite += cyssFlangeAHollows.Data(); + + // The final flange shape + TGeoCompositeShape* cyssFlangeASh = new TGeoCompositeShape(cyssFlangeAComposite.Data()); + + // We have all shapes: now create the real volumes + TGeoMedium* medAlu = mgr->GetMedium("EC0_ALUMINUM$"); + + TGeoVolume* cyssFlangeAVol = new TGeoVolume("IBCYSSFlangeA", cyssFlangeASh, medAlu); + cyssFlangeAVol->SetLineColor(kCyan); + cyssFlangeAVol->SetFillColor(kCyan); + + // Finally return the flange volume + return cyssFlangeAVol; +} + +TString V3Services::ibCreateHollowsCyssFlangeSideA(const Double_t zlen) +{ + // + // Creates the very complicate hollow holes in the Flange + // on Side A for the Inner Barrel CYSS + // (ALIEC0UP0189) + // + // Input: + // zlen : the thickness of the ring where the hollows are located + // + // Output: + // + // Return: + // the string describing the holes and their positions + // + // Created: 04 Nov 2019 Mario Sitta + // + + static const Double_t sCyssFlangeAHolesDpos = 274.0 * sMm; + + static const Double_t sCyssFlangeAHole1Phi0 = 10; // Deg + static const Double_t sCyssFlangeAHole1PhiStep = 20; // Deg + + static const Double_t sCyssFlangeAHole2Phi = 20; // Deg + + static const Double_t sCyssFlangeAHollowD = 7.0 * sMm; + static const Double_t sCyssFlangeAHollowPhi0 = 13; // Deg + static const Double_t sCyssFlangeAHollowPhi1 = 8; // Deg + + // Local variables + Double_t rmin, rmax, phi, dphi; + Double_t xpos, ypos; + + TString cyssFlangeAHollows; + + // + rmax = sCyssFlangeAHollowD / 2; + TGeoTubeSeg* roundHalf = new TGeoTubeSeg("roundhalf", 0, rmax, 2 * zlen, 0, 180); + + Double_t rHoles = sCyssFlangeAHolesDpos / 2; + + xpos = rHoles * TMath::Cos(sCyssFlangeAHollowPhi0 * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(sCyssFlangeAHollowPhi0 * TMath::DegToRad()); + TGeoCombiTrans* roundTr1 = new TGeoCombiTrans("roundtr1", xpos, -ypos, zlen, new TGeoRotation("", -sCyssFlangeAHollowPhi0, 0, 0)); + roundTr1->RegisterYourself(); + TGeoCombiTrans* roundTr2 = new TGeoCombiTrans("roundtr2", -xpos, -ypos, zlen, new TGeoRotation("", sCyssFlangeAHollowPhi0, 0, 0)); + roundTr2->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr1-roundhalf:roundtr2"; + + TGeoTranslation* noRot = new TGeoTranslation("norot", 0, 0, zlen); + noRot->RegisterYourself(); + TGeoCombiTrans* yRot180 = new TGeoCombiTrans("yrot180", 0, 0, zlen, new TGeoRotation("", 0, 180, 180)); + yRot180->RegisterYourself(); + + rmin = sCyssFlangeAHolesDpos / 2 - sCyssFlangeAHollowD / 2; + rmax = sCyssFlangeAHolesDpos / 2 + sCyssFlangeAHollowD / 2; + + for (Int_t j = 1; j < 4; j++) { + phi = 90 - (sCyssFlangeAHole1Phi0 + j * sCyssFlangeAHole1PhiStep + 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr3 = new TGeoCombiTrans(Form("roundtr%d", j + 2), xpos, -ypos, zlen, new TGeoRotation("", 180 - phi, 0, 0)); + roundTr3->RegisterYourself(); + TGeoCombiTrans* roundTr4 = new TGeoCombiTrans(Form("roundtr%d", j + 5), -xpos, -ypos, zlen, new TGeoRotation("", phi - 180, 0, 0)); + roundTr4->RegisterYourself(); + + cyssFlangeAHollows += Form("-roundhalf:roundtr%d-roundhalf:roundtr%d", j + 2, j + 5); + + phi = 360 - phi - 0.05; + if (j == 3) + dphi = 360 - sCyssFlangeAHollowPhi0 + 0.05; + else + dphi = phi + (sCyssFlangeAHole1PhiStep - sCyssFlangeAHollowPhi1) + 0.1; + + TGeoTubeSeg* hollow1 = new TGeoTubeSeg(Form("hollow%d", j), rmin, rmax, 2 * zlen, phi, dphi); + + cyssFlangeAHollows += Form("-hollow%d:norot-hollow%d:yrot180", j, j); + + phi = 90 - (sCyssFlangeAHole1Phi0 + j * sCyssFlangeAHole1PhiStep - 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr5 = new TGeoCombiTrans(Form("roundtr%d", j + 8), xpos, -ypos, zlen, new TGeoRotation("", -phi, 0, 0)); + roundTr5->RegisterYourself(); + TGeoCombiTrans* roundTr6 = new TGeoCombiTrans(Form("roundtr%d", j + 11), -xpos, -ypos, zlen, new TGeoRotation("", phi, 0, 0)); + roundTr6->RegisterYourself(); + + cyssFlangeAHollows += Form("-roundhalf:roundtr%d-roundhalf:roundtr%d", j + 8, j + 11); + } + + // + phi = 90 - (sCyssFlangeAHole2Phi + 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr15 = new TGeoCombiTrans("roundtr15", xpos, -ypos, zlen, new TGeoRotation("", 180 - phi, 0, 0)); + roundTr15->RegisterYourself(); + TGeoCombiTrans* roundTr16 = new TGeoCombiTrans("roundtr16", -xpos, -ypos, zlen, new TGeoRotation("", phi - 180, 0, 0)); + roundTr16->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr15-roundhalf:roundtr16"; + + phi = 360 - phi - 0.5; + dphi = phi + (sCyssFlangeAHole1Phi0 + sCyssFlangeAHole1PhiStep - sCyssFlangeAHole2Phi - sCyssFlangeAHollowPhi1) + 0.5; + TGeoTubeSeg* hollow4 = new TGeoTubeSeg("hollow4", rmin, rmax, 2 * zlen, phi, dphi); + + cyssFlangeAHollows += "-hollow4:norot-hollow4:yrot180"; + + // + phi = 90 - (sCyssFlangeAHole2Phi - 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr17 = new TGeoCombiTrans("roundtr17", xpos, -ypos, zlen, new TGeoRotation("", -phi, 0, 0)); + roundTr17->RegisterYourself(); + TGeoCombiTrans* roundTr18 = new TGeoCombiTrans("roundtr18", -xpos, -ypos, zlen, new TGeoRotation("", phi, 0, 0)); + roundTr18->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr17-roundhalf:roundtr18"; + + phi = 90 - (sCyssFlangeAHole1Phi0 + 0.5 * sCyssFlangeAHollowPhi1); + xpos = rHoles * TMath::Cos(phi * TMath::DegToRad()); + ypos = rHoles * TMath::Sin(phi * TMath::DegToRad()); + TGeoCombiTrans* roundTr19 = new TGeoCombiTrans("roundtr19", xpos, -ypos, zlen, new TGeoRotation("", 180 - phi, 0, 0)); + roundTr19->RegisterYourself(); + TGeoCombiTrans* roundTr20 = new TGeoCombiTrans("roundtr20", -xpos, -ypos, zlen, new TGeoRotation("", phi - 180, 0, 0)); + roundTr20->RegisterYourself(); + + cyssFlangeAHollows += "-roundhalf:roundtr19-roundhalf:roundtr20"; + + TGeoCombiTrans* zRotPhi = new TGeoCombiTrans("zrotphi", 0, 0, zlen, new TGeoRotation("", -sCyssFlangeAHole1Phi0, 0, 0)); + zRotPhi->RegisterYourself(); + TGeoCombiTrans* yzRot180Phi = new TGeoCombiTrans("yzrot180phi", 0, 0, zlen, new TGeoRotation("", 0, 180, 180 - sCyssFlangeAHole1Phi0)); + yzRot180Phi->RegisterYourself(); + + cyssFlangeAHollows += "-hollow4:zrotphi-hollow4:yzrot180phi"; + + // Finally we return the string + return cyssFlangeAHollows; +} + +TGeoVolume* V3Services::ibCyssFlangeSideC(const TGeoManager* mgr) +{ + // + // Creates the Flange on Side C for the Inner Barrel CYSS + // (ALIEC0UP0098) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // the flange as a TGeoVolume + // + // Created: 23 Oct 2019 Mario Sitta + // + + // Radii of the rings + static const Double_t sCyssFlangeCDmin1 = 44.0 * sMm; + static const Double_t sCyssFlangeCDmin2 = 57.0 * sMm; + static const Double_t sCyssFlangeCDmin3 = 73.0 * sMm; + + static const Double_t sCyssFlangeCDmax1 = 58.8 * sMm; + static const Double_t sCyssFlangeCDmax2 = 74.8 * sMm; + static const Double_t sCyssFlangeCDmax3 = 94.0 * sMm; + + static const Double_t sCyssFlangeCDWallIn = 89.0 * sMm; + static const Double_t sCyssFlangeCDWallOut = 95.6 * sMm; + + static const Double_t sCyssFlangeCDExt = 100.0 * sMm; + + // Thicknesses and heights + static const Double_t sCyssFlangeCTotH = 10.0 * sMm; + static const Double_t sCyssFlangeCExtThick = 1.0 * sMm; + + static const Double_t sCyssFlangeCHmax1 = 1.5 * sMm; + static const Double_t sCyssFlangeCHmax2 = 4.0 * sMm; + static const Double_t sCyssFlangeCHmax3 = 6.5 * sMm; + + static const Double_t sCyssFlangeCHmin2 = 2.5 * sMm; + static const Double_t sCyssFlangeCHmin3 = 5.0 * sMm; + + // Holes + static const Double_t sHoles22Dia = 2.2 * sMm; + static const Double_t sHoles22Phi = 60; // Deg + + static const Double_t sHoles30Dia = 3.0 * sMm; + static const Double_t sHoles30Phi = 15; // Deg + + static const Double_t sHoles12Dia = 1.2 * sMm; + static const Double_t sHoles12Phi = 75; // Deg + + static const Double_t sHolesDdist[3] = {50.0 * sMm, 64.0 * sMm, 80.0 * sMm}; + + static const Double_t sCyssFlangeCNotchH = 3.2 * sMm; + static const Double_t sCyssFlangeCNotchW = 3.0 * sMm; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos; + + // The CYSS Flange on Side C is physically a single piece. + // It is implemented as a CompositeShape of two Pcon's minus the holes + + // The flange body + TGeoPcon* cyssFlangeCDisks = new TGeoPcon("cyssflangecdisks", 180, 180, 12); + + rmin = sCyssFlangeCDmin1 / 2; + rmax = sCyssFlangeCDmax1 / 2; + cyssFlangeCDisks->DefineSection(0, 0, rmin, rmax); + cyssFlangeCDisks->DefineSection(1, sCyssFlangeCHmax1, rmin, rmax); + rmin = sCyssFlangeCDmin2 / 2; + cyssFlangeCDisks->DefineSection(2, sCyssFlangeCHmax1, rmin, rmax); + cyssFlangeCDisks->DefineSection(3, sCyssFlangeCHmin2, rmin, rmax); + rmax = sCyssFlangeCDmax2 / 2; + cyssFlangeCDisks->DefineSection(4, sCyssFlangeCHmin2, rmin, rmax); + cyssFlangeCDisks->DefineSection(5, sCyssFlangeCHmax2, rmin, rmax); + rmin = sCyssFlangeCDmin3 / 2; + cyssFlangeCDisks->DefineSection(6, sCyssFlangeCHmax2, rmin, rmax); + cyssFlangeCDisks->DefineSection(7, sCyssFlangeCHmin3, rmin, rmax); + rmax = sCyssFlangeCDWallOut / 2; + cyssFlangeCDisks->DefineSection(8, sCyssFlangeCHmin3, rmin, rmax); + cyssFlangeCDisks->DefineSection(9, sCyssFlangeCHmax3, rmin, rmax); + rmin = sCyssFlangeCDWallIn / 2; + cyssFlangeCDisks->DefineSection(10, sCyssFlangeCHmax3, rmin, rmax); + cyssFlangeCDisks->DefineSection(11, sCyssFlangeCTotH, rmin, rmax); + + TGeoPcon* cyssFlangeCExt = new TGeoPcon("cflangecext", 180, 180, 4); + + rmin = sCyssFlangeCDmax3 / 2; + rmax = sCyssFlangeCDExt / 2; + cyssFlangeCExt->DefineSection(0, 0, rmin, rmax); + cyssFlangeCExt->DefineSection(1, sCyssFlangeCExtThick, rmin, rmax); + rmax = sCyssFlangeCDWallOut / 2; + cyssFlangeCExt->DefineSection(2, sCyssFlangeCExtThick, rmin, rmax); + cyssFlangeCExt->DefineSection(3, sCyssFlangeCHmin3, rmin, rmax); + + TString cyssFlangeCComposite = Form("cyssflangecdisks+cflangecext"); + + // The flange holes + rmax = sHoles22Dia / 2; + zlen = sCyssFlangeCTotH / 2; + TGeoTube* hole22 = new TGeoTube("hole22", 0, rmax, 1.1 * zlen); + + for (Int_t j = 0; j < 3; j++) { + ypos = sHolesDdist[j] / 2; + TGeoTranslation* holeCTr = new TGeoTranslation(Form("holeCTr%d", j), 0, -ypos, zlen); + holeCTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole22:holeCTr%d", j); + + xpos = TMath::Sin(sHoles22Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + ypos = TMath::Cos(sHoles22Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + TGeoTranslation* holeLTr = new TGeoTranslation(Form("holeLTr%d", j), xpos, -ypos, zlen); + holeLTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole22:holeLTr%d", j); + + TGeoTranslation* holeRTr = new TGeoTranslation(Form("holeRTr%d", j), -xpos, -ypos, zlen); + holeRTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole22:holeRTr%d", j); + } + + rmax = sHoles30Dia / 2; + TGeoTube* hole30 = new TGeoTube("hole30", 0, rmax, zlen); + + for (Int_t k = 0; k < 3; k++) { + Double_t phi = (k + 1) * sHoles30Phi; + for (Int_t j = 0; j < 3; j++) { + xpos = TMath::Sin(phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + ypos = TMath::Cos(phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + + TGeoTranslation* holeLTr = new TGeoTranslation(Form("holeLTr%d%d", k, j), xpos, -ypos, zlen); + holeLTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole30:holeLTr%d%d", k, j); + + TGeoTranslation* holeRTr = new TGeoTranslation(Form("holeRTr%d%d", k, j), -xpos, -ypos, zlen); + holeRTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole30:holeRTr%d%d", k, j); + } + } + + rmax = sHoles12Dia / 2; + TGeoTube* hole12 = new TGeoTube("hole12", 0, rmax, 1.1 * zlen); + + for (Int_t j = 0; j < 3; j++) { + xpos = TMath::Sin(sHoles12Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + ypos = TMath::Cos(sHoles12Phi * TMath::DegToRad()) * sHolesDdist[j] / 2; + TGeoTranslation* holeLTr = new TGeoTranslation(Form("holeLTrM%d", j), xpos, -ypos, zlen); + holeLTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole12:holeLTrM%d", j); + + TGeoTranslation* holeRTr = new TGeoTranslation(Form("holeRTrM%d", j), -xpos, -ypos, zlen); + holeRTr->RegisterYourself(); + cyssFlangeCComposite += Form("-hole12:holeRTrM%d", j); + } + + TGeoBBox* notch = new TGeoBBox("notch", sCyssFlangeCNotchW / 2, (sCyssFlangeCDWallOut - sCyssFlangeCDWallIn), sCyssFlangeCNotchH); + + ypos = (sCyssFlangeCDWallIn + sCyssFlangeCDWallOut) / 4; + TGeoTranslation* notchTr = new TGeoTranslation("notchTr", 0, -ypos, sCyssFlangeCTotH); + notchTr->RegisterYourself(); + + cyssFlangeCComposite += "-notch:notchTr"; + + // The final flange shape + TGeoCompositeShape* cyssFlangeCSh = new TGeoCompositeShape(cyssFlangeCComposite.Data()); + + // We have all shapes: now create the real volumes + TGeoMedium* medAlu = mgr->GetMedium("EC0_ALUMINUM$"); + + TGeoVolume* cyssFlangeCVol = new TGeoVolume("IBCYSSFlangeC", cyssFlangeCSh, medAlu); + cyssFlangeCVol->SetLineColor(kCyan); + cyssFlangeCVol->SetFillColor(kCyan); + + // Finally return the flange volume + return cyssFlangeCVol; +} + +void V3Services::obEndWheelSideA(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side A + // for a given layer of the Middle and Outer Barrels + // (Layer 3: ALICE-W3-01-Side_A, Layer 4: ALICE-W4-01-Side_A, + // Layer 5: ALICE-W5-01-Side_A Layer 6: ALICE-W6-01-Side_A) + // + // Input: + // iLay : the layer number (0,1: Middle, 2,3: Outer) + // endWheel : the volume where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 24 Sep 2019 Mario Sitta + // Updated: 27 Sep 2019 Mario Sitta + // + + // The support ring + static const Double_t sOBWheelTotZlen = 55.0 * sMm; + static const Double_t sOBWheelSuppZlen = 35.0 * sMm; + + static const Double_t sOBWheelSuppRmin[4] = {204.0 * sMm, 254.0 * sMm, 352.5 * sMm, 402.0 * sMm}; + static const Double_t sOBWheelSuppRmax[4] = {241.0 * sMm, 291.0 * sMm, 389.0 * sMm, 448.5 * sMm}; + + // The support blocks + static const Double_t sOBWheelShelfWide[4] = {56.05 * sMm, 55.15 * sMm, 54.10 * sMm, 53.81 * sMm}; // TO BE CHECKED + static const Double_t sOBWheelShelfHoleZpos = 48.0 * sMm; + + static const Double_t sOBWheelShelfRpos[4] = {213.0 * sMm, 262.5 * sMm, 361.0 * sMm, 410.5 * sMm}; + static const Double_t sOBWheelShelfPhi0[4] = {0.0, 0.0, 0.0, 0.0}; // Deg + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + + // The Support Wheel is physically a single piece, a hollow ring + // plus the stave support shelves + // For the sake of simplicity we build it up with four TGeoTube's + // one per each wall of the ring (inner, outer, lower, upper) plus + // as many TGeoBBox's as needed for the shelves + + // The inner ring + TGeoTube* innerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); + + // The outer ring + TGeoTube* outerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); + + // The lower ring + rmax = sOBWheelSuppRmin[iLay] + sOBWheelThickness; + zlen = sOBWheelSuppZlen - 2 * sOBWheelThickness; + TGeoTube* lowerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], rmax, zlen / 2); + + // The upper ring + rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; + TGeoTube* upperRingSh = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); + + // The shelf support + xlen = sOBWheelShelfWide[iLay]; + ylen = 2 * sOBWheelThickness; + zlen = sOBWheelTotZlen - sOBWheelSuppZlen; + TGeoBBox* shelfSh = new TGeoBBox(xlen / 2, ylen / 2, zlen / 2); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + + Int_t nLay = iLay + sNumberInnerLayers; + + TGeoVolume* ringInnerVol = new TGeoVolume(Form("OBEndWheelAInnerRing%d", nLay), innerRingSh, medCarbon); + ringInnerVol->SetFillColor(kBlue); + ringInnerVol->SetLineColor(kBlue); + + TGeoVolume* ringOuterVol = new TGeoVolume(Form("OBEndWheelAOuterRing%d", nLay), outerRingSh, medCarbon); + ringOuterVol->SetFillColor(kBlue); + ringOuterVol->SetLineColor(kBlue); + + TGeoVolume* ringLowerVol = new TGeoVolume(Form("OBEndWheelALowerRing%d", nLay), lowerRingSh, medCarbon); + ringLowerVol->SetFillColor(kBlue); + ringLowerVol->SetLineColor(kBlue); + + TGeoVolume* ringUpperVol = new TGeoVolume(Form("OBEndWheelAUpperRing%d", nLay), upperRingSh, medCarbon); + ringUpperVol->SetFillColor(kBlue); + ringUpperVol->SetLineColor(kBlue); + + TGeoVolume* shelfVol = new TGeoVolume(Form("OBEndWheelAShelf%d", nLay), shelfSh, medCarbon); + shelfVol->SetFillColor(kBlue); + shelfVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // In blueprints the Z position is given wrt the shelf holes + // First the ring + if (iLay < sNumberMiddlLayers) + zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos; + else + zpos = sOBWheelsZpos + sOBWheelShelfHoleZpos; + + zpos -= outerRingSh->GetDz(); + mother->AddNode(ringOuterVol, 1, new TGeoTranslation(0, 0, zpos)); + + zpos -= (outerRingSh->GetDz() + lowerRingSh->GetDz()); + mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, zpos)); + + zpos -= (lowerRingSh->GetDz() + innerRingSh->GetDz()); + mother->AddNode(ringInnerVol, 1, new TGeoTranslation(0, 0, zpos)); + + // Then the support blocks + Int_t numberOfStaves = GeometryTGeo::Instance()->getNumberOfStaves(nLay); + Double_t alpha = 360. / numberOfStaves; + + rmin = sOBWheelShelfRpos[iLay] + shelfSh->GetDY(); + zpos -= (innerRingSh->GetDz() + shelfSh->GetDZ()); + + for (Int_t j = 0; j < numberOfStaves; j++) { // As in V3Layer::createLayer + Double_t phi = j * alpha + sOBWheelShelfPhi0[iLay]; + xpos = rmin * cosD(phi); + ypos = rmin * sinD(phi); + phi += 90; + mother->AddNode(shelfVol, j, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", phi, 0, 0))); + } +} + +void V3Services::mbEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side C + // for a given layer of the Middle Barrel + // (wheels on Side C are very different for Middle and Outer Barrels, + // so we cannot use a single method for both as for Side A) + // (Layer 3: ALICE-W3-04-Side_C, Layer 4: ALICE-W4-05-Side_C) + // + // Input: + // iLay : the layer number (0,1: Middle, 2,3: Outer) + // endWheel : the volume where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Sep 2019 Mario Sitta + // + + // The support ring + static const Double_t sOBWheelTotZlen[2] = {63.0 * sMm, 55.0 * sMm}; + static const Double_t sOBWheelSuppZlen[2] = {43.0 * sMm, 35.0 * sMm}; + + static const Double_t sOBWheelSuppRmin[2] = {200.5 * sMm, 254.0 * sMm}; + static const Double_t sOBWheelSuppRmax[2] = {237.5 * sMm, 291.0 * sMm}; + static const Double_t sOBWheelFlangeR[2] = {255.0 * sMm, 239.5 * sMm}; + static const Double_t sOBWheelFlangeZlen = 8.0 * sMm; + + // The support blocks + static const Double_t sOBWheelShelfWide[2] = {56.05 * sMm, 55.15 * sMm}; // TO BE CHECKED + static const Double_t sOBWheelShelfHoleZpos[2] = {56.0 * sMm, 48.0 * sMm}; + + static const Double_t sOBWheelShelfRpos[2] = {213.0 * sMm, 262.5 * sMm}; + static const Double_t sOBWheelShelfPhi0[2] = {0.0, 0.0}; // Deg + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + Int_t nsect; + + // The Support Wheel is physically a single piece, a hollow ring + // with a flange plus the stave support shelves + // Unfortunately the flange is on opposite sides on the two layers + // (externally to the ring for layer 3, internally for layer 4) + // For the sake of simplicity we build it up with three TGeoTube's and + // one TGeoPcon for each wall of the ring (inner, outer, lower, upper) + // plus as many TGeoBBox's as needed for the shelves + + // The inner ring + TGeoTube* innerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], sOBWheelSuppRmax[iLay], sOBWheelThickness / 2); + + // The outer ring with the flange + if (iLay == 0) + nsect = 6; + else + nsect = 4; + + TGeoPcon* outerRingSh = new TGeoPcon(0, 360, nsect); + + if (iLay == 0) { + rmin = sOBWheelSuppRmax[0] - 2 * sOBWheelThickness; + outerRingSh->DefineSection(0, 0., rmin, sOBWheelFlangeR[0]); + outerRingSh->DefineSection(1, 2 * sOBWheelThickness, rmin, sOBWheelFlangeR[0]); + outerRingSh->DefineSection(2, 2 * sOBWheelThickness, rmin, sOBWheelSuppRmax[0]); + outerRingSh->DefineSection(3, sOBWheelFlangeZlen, rmin, sOBWheelSuppRmax[0]); + outerRingSh->DefineSection(4, sOBWheelFlangeZlen, sOBWheelSuppRmin[0], sOBWheelSuppRmax[0]); + zlen = sOBWheelFlangeZlen + sOBWheelThickness; + outerRingSh->DefineSection(5, zlen, sOBWheelSuppRmin[0], sOBWheelSuppRmax[0]); + } else { + outerRingSh->DefineSection(0, 0., sOBWheelFlangeR[1], sOBWheelSuppRmax[1]); + outerRingSh->DefineSection(1, sOBWheelThickness, sOBWheelFlangeR[1], sOBWheelSuppRmax[1]); + rmax = sOBWheelSuppRmin[1] + sOBWheelThickness; + outerRingSh->DefineSection(2, sOBWheelThickness, sOBWheelFlangeR[1], rmax); + outerRingSh->DefineSection(3, 2 * sOBWheelThickness, sOBWheelFlangeR[1], rmax); + } + + // The lower ring + if (iLay == 0) + zlen = sOBWheelSuppZlen[iLay] - sOBWheelFlangeZlen - 2 * sOBWheelThickness; + else + zlen = sOBWheelSuppZlen[iLay] - sOBWheelThickness - outerRingSh->GetZ(nsect - 1); + + rmax = sOBWheelSuppRmin[iLay] + sOBWheelThickness; + TGeoTube* lowerRingSh = new TGeoTube(sOBWheelSuppRmin[iLay], rmax, zlen / 2); + + // The upper ring + if (iLay == 1) // For odd layers the upper and lower rings length is the same + zlen = sOBWheelSuppZlen[iLay] - 2 * sOBWheelThickness; + + rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; + TGeoTube* upperRingSh = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); + + // The shelf support + xlen = sOBWheelShelfWide[iLay]; + ylen = 2 * sOBWheelThickness; + zlen = sOBWheelTotZlen[iLay] - sOBWheelSuppZlen[iLay]; + TGeoBBox* shelfSh = new TGeoBBox(xlen / 2, ylen / 2, zlen / 2); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + + Int_t nLay = iLay + sNumberInnerLayers; + + TGeoVolume* ringInnerVol = new TGeoVolume(Form("OBEndWheelCInnerRing%d", nLay), innerRingSh, medCarbon); + ringInnerVol->SetFillColor(kBlue); + ringInnerVol->SetLineColor(kBlue); + + TGeoVolume* ringOuterVol = new TGeoVolume(Form("OBEndWheelCOuterRing%d", nLay), outerRingSh, medCarbon); + ringOuterVol->SetFillColor(kBlue); + ringOuterVol->SetLineColor(kBlue); + + TGeoVolume* ringLowerVol = new TGeoVolume(Form("OBEndWheelCLowerRing%d", nLay), lowerRingSh, medCarbon); + ringLowerVol->SetFillColor(kBlue); + ringLowerVol->SetLineColor(kBlue); + + TGeoVolume* ringUpperVol = new TGeoVolume(Form("OBEndWheelCUpperRing%d", nLay), upperRingSh, medCarbon); + ringUpperVol->SetFillColor(kBlue); + ringUpperVol->SetLineColor(kBlue); + + TGeoVolume* shelfVol = new TGeoVolume(Form("OBEndWheelAShelf%d", nLay), shelfSh, medCarbon); + shelfVol->SetFillColor(kBlue); + shelfVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // In blueprints the Z position is given wrt the shelf holes + // First the ring + zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos[iLay] - sOBWheelSuppZlen[iLay]; + + zpos += innerRingSh->GetDz(); + mother->AddNode(ringInnerVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos += (innerRingSh->GetDz() + upperRingSh->GetDz()); + mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos += (-upperRingSh->GetDz() + lowerRingSh->GetDz()); + mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos += (lowerRingSh->GetDz() + outerRingSh->GetZ(nsect - 1)); + mother->AddNode(ringOuterVol, 1, new TGeoTranslation(0, 0, -zpos)); + + // Then the support blocks + Int_t numberOfStaves = GeometryTGeo::Instance()->getNumberOfStaves(nLay); + Double_t alpha = 360. / numberOfStaves; + + rmin = sOBWheelShelfRpos[iLay] + shelfSh->GetDY(); + zpos = sMBWheelsZpos + sOBWheelShelfHoleZpos[iLay] - sOBWheelSuppZlen[iLay]; + zpos -= shelfSh->GetDZ(); + + for (Int_t j = 0; j < numberOfStaves; j++) { // As in V3Layer::createLayer + Double_t phi = j * alpha + sOBWheelShelfPhi0[iLay]; + xpos = rmin * cosD(phi); + ypos = rmin * sinD(phi); + phi += 90; + mother->AddNode(shelfVol, j, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", phi, 0, 0))); + } +} + +void V3Services::obEndWheelSideC(const Int_t iLay, TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the single End Wheel on Side C + // for a given layer of the Outer Barrel + // (wheels on Side C are very different for Middle and Outer Barrels, + // so we cannot use a single method for both as for Side A) + // (Layer 5: ALICE-W5-04-Side_C, Layer 6: ALICE-W6-04-Side_C) + // + // Input: + // iLay : the layer number (0,1: Middle, 2,3: Outer) + // endWheel : the volume where to place the current created wheel + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 07 Oct 2019 Mario Sitta + // + + // The support ring + static const Double_t sOBWheelTotZlen[2] = {37.0 * sMm, 35.0 * sMm}; + + static const Double_t sOBWheelSuppRmin = 354.0 * sMm; + static const Double_t sOBWheelSuppRmax[2] = {389.5 * sMm, 448.5 * sMm}; + static const Double_t sOBWheelIntFlangeR[2] = {335.0 * sMm, 393.0 * sMm}; + static const Double_t sOBWheelExtFlangeR = 409.0 * sMm; + static const Double_t sOBWheelIntFlangeZ = 4.0 * sMm; // TO BE CHECKED! + + static const Double_t sOBWheelShelfRpos[2] = {361.0 * sMm, 410.5 * sMm}; + static const Double_t sOBWheelShelfHoleZpos[2] = {28.0 * sMm, 26.0 * sMm}; + + // Local variables + Double_t xlen, ylen, zlen; + Double_t rmin, rmax, phimin, dphi; + Double_t xpos, ypos, zpos; + Int_t nsect; + + // The Support Wheels are physically a single piece, a hollow ring + // with one or two flanges + // For the sake of simplicity we build it up with a TGeoTube for the + // external wall and a TGeoPcon for the remaining of the ring for layer 6 + // and with two TGeoPcon's for layer 5 + + // The upper ring: a Pcon for Layer 5, a Tube for Layer 6 + TGeoShape* upperRingSh; + + rmin = sOBWheelSuppRmax[iLay] - sOBWheelThickness; + if (iLay == 0) { + nsect = 4; + TGeoPcon* ring = new TGeoPcon(0, 360, nsect); + ring->DefineSection(0, sOBWheelThickness, rmin, sOBWheelExtFlangeR); + ring->DefineSection(1, 2 * sOBWheelThickness, rmin, sOBWheelExtFlangeR); + ring->DefineSection(2, 2 * sOBWheelThickness, rmin, sOBWheelSuppRmax[iLay]); + zlen = sOBWheelTotZlen[iLay] - sOBWheelThickness; + ring->DefineSection(3, zlen, rmin, sOBWheelSuppRmax[iLay]); + upperRingSh = (TGeoShape*)ring; + } else { + zlen = sOBWheelTotZlen[iLay] - 2 * sOBWheelThickness; + TGeoTube* ring = new TGeoTube(rmin, sOBWheelSuppRmax[iLay], zlen / 2); + upperRingSh = (TGeoShape*)ring; + } + + // The lower ring: a Pcon + TGeoPcon* lowerRingSh; + + if (iLay == 0) { + nsect = 14; + lowerRingSh = new TGeoPcon(0, 360, nsect); + lowerRingSh->DefineSection(0, 0., sOBWheelSuppRmin, sOBWheelExtFlangeR); + lowerRingSh->DefineSection(1, sOBWheelThickness, sOBWheelSuppRmin, sOBWheelExtFlangeR); + rmax = sOBWheelSuppRmin + sOBWheelThickness; + lowerRingSh->DefineSection(2, sOBWheelThickness, sOBWheelSuppRmin, rmax); + lowerRingSh->DefineSection(3, sOBWheelIntFlangeZ, sOBWheelSuppRmin, rmax); + lowerRingSh->DefineSection(4, sOBWheelIntFlangeZ, sOBWheelIntFlangeR[iLay], rmax); + zpos = sOBWheelIntFlangeZ + 2 * sOBWheelThickness; + lowerRingSh->DefineSection(5, zpos, sOBWheelIntFlangeR[iLay], rmax); + lowerRingSh->DefineSection(6, zpos, sOBWheelSuppRmin, rmax); + zpos += sOBWheelIntFlangeZ; + lowerRingSh->DefineSection(7, zpos, sOBWheelSuppRmin, rmax); + rmax = sOBWheelShelfRpos[iLay] + sOBWheelThickness; + lowerRingSh->DefineSection(8, zpos, sOBWheelSuppRmin, rmax); + zpos += sOBWheelThickness; + lowerRingSh->DefineSection(9, zpos, sOBWheelSuppRmin, rmax); + lowerRingSh->DefineSection(10, zpos, sOBWheelShelfRpos[iLay], rmax); + zpos = sOBWheelTotZlen[iLay] - sOBWheelThickness; + lowerRingSh->DefineSection(11, zpos, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(12, zpos, sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + lowerRingSh->DefineSection(13, sOBWheelTotZlen[iLay], sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + } else { + nsect = 10; + lowerRingSh = new TGeoPcon(0, 360, nsect); + lowerRingSh->DefineSection(0, 0., sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + lowerRingSh->DefineSection(1, sOBWheelThickness, sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + rmax = sOBWheelShelfRpos[iLay] + sOBWheelThickness; + lowerRingSh->DefineSection(2, sOBWheelThickness, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(3, sOBWheelIntFlangeZ, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(4, sOBWheelIntFlangeZ, sOBWheelIntFlangeR[iLay], rmax); + zpos = sOBWheelIntFlangeZ + 2 * sOBWheelThickness; + lowerRingSh->DefineSection(5, zpos, sOBWheelIntFlangeR[iLay], rmax); + lowerRingSh->DefineSection(6, zpos, sOBWheelShelfRpos[iLay], rmax); + zpos = sOBWheelTotZlen[iLay] - sOBWheelThickness; + lowerRingSh->DefineSection(7, zpos, sOBWheelShelfRpos[iLay], rmax); + lowerRingSh->DefineSection(8, zpos, sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + lowerRingSh->DefineSection(9, sOBWheelTotZlen[iLay], sOBWheelShelfRpos[iLay], sOBWheelSuppRmax[iLay]); + } + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + + Int_t nLay = iLay + sNumberInnerLayers + sNumberMiddlLayers; + + TGeoVolume* ringUpperVol = new TGeoVolume(Form("OBEndWheelCUpperRing%d", nLay), upperRingSh, medCarbon); + ringUpperVol->SetFillColor(kBlue); + ringUpperVol->SetLineColor(kBlue); + + TGeoVolume* ringLowerVol = new TGeoVolume(Form("OBEndWheelCLowerRing%d", nLay), lowerRingSh, medCarbon); + ringLowerVol->SetFillColor(kBlue); + ringLowerVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + // In blueprints the Z position is given wrt the shelf holes + zpos = sOBWheelsZpos + sOBWheelShelfHoleZpos[iLay]; + + mother->AddNode(ringLowerVol, 1, new TGeoTranslation(0, 0, -zpos)); + + if (iLay == 1) + zpos -= (sOBWheelThickness + (static_cast(upperRingSh))->GetDz()); + mother->AddNode(ringUpperVol, 1, new TGeoTranslation(0, 0, -zpos)); +} + +void V3Services::obConeSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Cone structure on Side A of the Outer Barrel + // (ALICE-W4-04-Cone_4A) + // + // Input: + // mother : the volume where to place the current created cone + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 03 Feb 2020 Mario Sitta + // + + static const Double_t sOBConeATotZlen = 350.0 * sMm; + static const Double_t sOBConeAStartCyl2 = 170.0 * sMm; + static const Double_t sOBConeAEndCyl1 = 160.8 * sMm; + static const Double_t sOBConeAThinCylZ = 36.0 * sMm; + + static const Double_t sOBConeAIntR = 291.5 * sMm; + static const Double_t sOBConeAExtR = 302.5 * sMm; + + static const Double_t sOBConeARingExtR = 339.5 * sMm; + static const Double_t sOBConeARingZlen = 55.0 * sMm; + static const Double_t sOBConeARingZout = 35.0 * sMm; + + static const Double_t sOBConeAThickAll = 2.0 * sMm; + static const Double_t sOBConeAThickThin = 1.0 * sMm; + + static const Double_t sOBConeAReinfZIn = 1.0 * sMm; + static const Double_t sOBConeAReinfRIn = 301.6 * sMm; + + static const Double_t sOBConeAReinfThick = 6.5 * sMm; + + static const Int_t sOBConeARibNVert = 8; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos, zpos; + + // The OB Cone on Side A is physically a single piece. + // It is implemented using two Pcon plus a Xtru for the reinforcements + + Double_t phi = sOBConeAReinfThick / sOBConeAIntR; + phi *= TMath::RadToDeg(); + + // The main cone: a Pcon + TGeoPcon* obConeSh = new TGeoPcon(phi, 180 - 2 * phi, 10); + + rmin = sOBConeAIntR; + rmax = sOBConeAReinfRIn; + obConeSh->DefineSection(0, 0., rmin, rmax); + obConeSh->DefineSection(1, sOBConeAReinfZIn, rmin, rmax); + rmax = rmin + sOBConeAThickThin; + obConeSh->DefineSection(2, sOBConeAReinfZIn, rmin, rmax); + obConeSh->DefineSection(3, sOBConeAThinCylZ, rmin, rmax); + rmax = rmin + sOBConeAThickAll; + obConeSh->DefineSection(4, sOBConeAThinCylZ, rmin, rmax); + zlen = sOBConeATotZlen - sOBConeAStartCyl2; + obConeSh->DefineSection(5, zlen, rmin, rmax); + rmin = sOBConeAExtR; + rmax = rmin + sOBConeAThickAll; + zlen = sOBConeATotZlen - sOBConeAEndCyl1; + obConeSh->DefineSection(6, zlen, rmin, rmax); + zlen = sOBConeATotZlen - sOBConeAThickAll; + obConeSh->DefineSection(7, zlen, rmin, rmax); + rmax = sOBConeARingExtR; + obConeSh->DefineSection(8, zlen, rmin, rmax); + obConeSh->DefineSection(9, sOBConeATotZlen, rmin, rmax); + + // The external ring: a Pcon + TGeoPcon* obConeRingSh = new TGeoPcon(phi, 180 - 2 * phi, 6); + + rmin = obConeSh->GetRmax(7); + rmax = rmin + sOBConeAThickAll; + obConeRingSh->DefineSection(0, 0., rmin, rmax); + zlen = sOBConeARingZlen - sOBConeARingZout; + obConeRingSh->DefineSection(1, zlen, rmin, rmax); + rmax = sOBConeARingExtR; + obConeRingSh->DefineSection(2, zlen, rmin, rmax); + zlen += sOBConeAThickAll; + obConeRingSh->DefineSection(3, zlen, rmin, rmax); + rmin = rmax - sOBConeAThickAll; + obConeRingSh->DefineSection(4, zlen, rmin, rmax); + zlen = sOBConeARingZlen - sOBConeAThickAll; + obConeRingSh->DefineSection(5, zlen, rmin, rmax); + + // The reinforcement rib: a Xtru + Double_t xr[sOBConeARibNVert], yr[sOBConeARibNVert]; + + xr[0] = 0; + yr[0] = 0; + xr[1] = obConeSh->GetRmax(0) - obConeSh->GetRmin(0); + yr[1] = yr[0]; + xr[2] = xr[1]; + yr[2] = obConeSh->GetZ(5); + xr[7] = xr[0]; + yr[7] = yr[2]; + xr[6] = obConeSh->GetRmin(6) - obConeSh->GetRmin(5); + yr[6] = obConeSh->GetZ(6); + xr[3] = xr[6] + (xr[1] - xr[0]); + yr[3] = yr[6]; + xr[5] = xr[6]; + yr[5] = sOBConeATotZlen - sOBConeARingZout + sOBConeAThickAll; + xr[4] = xr[3]; + yr[4] = yr[5]; + + TGeoXtru* obConeRibSh = new TGeoXtru(2); + obConeRibSh->DefinePolygon(sOBConeARibNVert, xr, yr); + obConeRibSh->DefineSection(0, 0); + obConeRibSh->DefineSection(1, sOBConeAThickAll); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + + TGeoVolume* obConeVol = new TGeoVolume("OBConeSideA", obConeSh, medCarbon); + obConeVol->SetFillColor(kBlue); + obConeVol->SetLineColor(kBlue); + + TGeoVolume* obConeRingVol = new TGeoVolume("OBConeRingSideA", obConeRingSh, medCarbon); + obConeRingVol->SetFillColor(kBlue); + obConeRingVol->SetLineColor(kBlue); + + TGeoVolume* obConeRibVol = new TGeoVolume("OBConeRibSideA", obConeRibSh, medCarbon); + obConeRibVol->SetFillColor(kBlue); + obConeRibVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + zpos = sOBConesZpos - sOBConeATotZlen; + + mother->AddNode(obConeVol, 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(obConeVol, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + zpos = sOBConesZpos - sOBConeARingZlen; + + mother->AddNode(obConeRingVol, 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(obConeRingVol, 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + xpos = obConeSh->GetRmin(0); + ypos = sOBConeAReinfThick; + zpos = sOBConesZpos - sOBConeATotZlen; + + mother->AddNode(obConeRibVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + mother->AddNode(obConeRibVol, 4, new TGeoCombiTrans(-xpos, -ypos, zpos, new TGeoRotation("", 0, -90, 180))); + + ypos = sOBConeAReinfThick - sOBConeAThickAll; + + mother->AddNode(obConeRibVol, 3, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, -90, -180))); + mother->AddNode(obConeRibVol, 4, new TGeoCombiTrans(xpos, -ypos, zpos, new TGeoRotation("", 0, 90, 0))); +} + +void V3Services::obConeTraysSideA(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Cone Trays on Side A of the Outer Barrel + // (ALICE-W3-08-vassoio+ALICE-W5-08_vassoio) + // + // Input: + // mother : the volume where to place the current created cone + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 05 Feb 2020 Mario Sitta + // + + static const Double_t sOBTrayZlen[2] = {112.0 * sMm, 115.0 * sMm}; + static const Double_t sOBTrayRmin[2] = {222.0 * sMm, 370.0 * sMm}; + static const Double_t sOBTrayRmax[2] = {240.0 * sMm, 386.0 * sMm}; + + static const Double_t sOBTrayZpos[2] = {181.0 * sMm, 20.0 * sMm}; + + static const Double_t sOBTrayThick = 2.0 * sMm; + + static const Double_t sOBTrayReinfWide[2] = {27.0 * sMm, 24.0 * sMm}; + static const Double_t sOBTrayReinfYpos = 6.0 * sMm; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos, zpos; + + // Each OB Tray on Side A is physically a single piece. + // It is implemented using a Pcon plus a BBox for the reinforcements + + TGeoPcon* obTraySh[2]; + TGeoBBox* obTrayRibSh[2]; + + for (Int_t j = 0; j < 2; j++) { + Double_t phi = (sOBTrayReinfYpos + sOBTrayThick) / sOBTrayRmin[j]; + phi *= TMath::RadToDeg(); + + // The main body: a Pcon + obTraySh[j] = new TGeoPcon(180 + phi, 180 - 2 * phi, 4); + + rmin = sOBTrayRmin[j]; + rmax = sOBTrayRmax[j]; + obTraySh[j]->DefineSection(0, 0., rmin, rmax); + obTraySh[j]->DefineSection(1, sOBTrayThick, rmin, rmax); + rmin = rmax - sOBTrayThick; + obTraySh[j]->DefineSection(2, sOBTrayThick, rmin, rmax); + obTraySh[j]->DefineSection(3, sOBTrayZlen[j], rmin, rmax); + + // The reinforcement rib: a BBox + obTrayRibSh[j] = new TGeoBBox(sOBTrayReinfWide[j] / 2, + sOBTrayThick / 2, + sOBTrayZlen[j] / 2); + } // for (j = 0,1) + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + + TGeoVolume *obTrayVol[2], *obTrayRibVol[2]; + + for (Int_t j = 0; j < 2; j++) { + obTrayVol[j] = new TGeoVolume(Form("OBConeTray%d", j), obTraySh[j], medCarbon); + obTrayVol[j]->SetFillColor(kBlue); + obTrayVol[j]->SetLineColor(kBlue); + + obTrayRibVol[j] = new TGeoVolume(Form("OBConeTrayRib%d", j), obTrayRibSh[j], medCarbon); + obTrayRibVol[j]->SetFillColor(kBlue); + obTrayRibVol[j]->SetLineColor(kBlue); + } + + // Finally put everything in the mother volume + + for (Int_t j = 0; j < 2; j++) { + if (j == 0) + zpos = sOBConesZpos - sOBTrayZpos[j] - sOBTrayZlen[j]; + else + zpos = sOBConesZpos + sOBTrayZpos[j]; + + mother->AddNode(obTrayVol[j], 1, new TGeoTranslation(0, 0, zpos)); + mother->AddNode(obTrayVol[j], 2, new TGeoCombiTrans(0, 0, zpos, new TGeoRotation("", 180, 0, 0))); + + xpos = obTraySh[j]->GetRmin(0) + obTrayRibSh[j]->GetDX(); + ypos = sOBTrayReinfYpos + obTrayRibSh[j]->GetDY(); + zpos += obTrayRibSh[j]->GetDZ(); + + mother->AddNode(obTrayRibVol[j], 1, new TGeoTranslation(xpos, -ypos, zpos)); + mother->AddNode(obTrayRibVol[j], 2, new TGeoTranslation(-xpos, -ypos, zpos)); + mother->AddNode(obTrayRibVol[j], 3, new TGeoTranslation(xpos, ypos, zpos)); + mother->AddNode(obTrayRibVol[j], 4, new TGeoTranslation(-xpos, ypos, zpos)); + } +} + +void V3Services::obConeSideC(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the Cone structure on Side C of the Outer Barrel + // (ALICE-W4-06-Cone_4C) + // + // Input: + // mother : the volume where to place the current created cone + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 26 Jan 2020 Mario Sitta + // + + static const Double_t sOBConeCTotZlen = 332.5 * sMm; + static const Double_t sOBConeCStartCyl2 = 132.8 * sMm; + static const Double_t sOBConeCEndCyl1 = 82.4 * sMm; + static const Double_t sOBConeCThinCylZ = 36.0 * sMm; + + static const Double_t sOBConeCIntR = 291.5 * sMm; + static const Double_t sOBConeCExtR = 315.0 * sMm; + + static const Double_t sOBConeCRingExtR = 333.0 * sMm; + static const Double_t sOBConeCRingZlen = 61.0 * sMm; + static const Double_t sOBConeCRingZout = 42.0 * sMm; + + static const Double_t sOBConeCThickAll = 2.0 * sMm; + static const Double_t sOBConeCThickThin = 1.0 * sMm; + + static const Double_t sOBConeCReinfZIn = 2.0 * sMm; + static const Double_t sOBConeCReinfRIn = 301.6 * sMm; + static const Double_t sOBConeCReinfROut = 351.5 * sMm; + + static const Double_t sOBConeCReinfThick = 6.5 * sMm; + + static const Int_t sOBConeCRibNVert = 8; + + // Local variables + Double_t rmin, rmax, zlen; + Double_t xpos, ypos, zpos; + + // The OB Cone on Side C is physically a single piece. + // It is implemented using two Pcon plus a Xtru for the reinforcements + + Double_t phi = sOBConeCReinfThick / sOBConeCIntR; + phi *= TMath::RadToDeg(); + + // The main cone: a Pcon + TGeoPcon* obConeSh = new TGeoPcon(phi, 180 - 2 * phi, 10); + + rmin = sOBConeCExtR; + rmax = sOBConeCReinfROut; + obConeSh->DefineSection(0, 0., rmin, rmax); + obConeSh->DefineSection(1, sOBConeCThickAll, rmin, rmax); + rmax = rmin + sOBConeCThickAll; + obConeSh->DefineSection(2, sOBConeCThickAll, rmin, rmax); + obConeSh->DefineSection(3, sOBConeCEndCyl1, rmin, rmax); + rmin = sOBConeCIntR; + rmax = rmin + sOBConeCThickAll; + obConeSh->DefineSection(4, sOBConeCStartCyl2, rmin, rmax); + zlen = sOBConeCTotZlen - sOBConeCThinCylZ; + obConeSh->DefineSection(5, zlen, rmin, rmax); + rmax = rmin + sOBConeCThickThin; + obConeSh->DefineSection(6, zlen, rmin, rmax); + zlen = sOBConeCTotZlen - sOBConeCReinfZIn; + obConeSh->DefineSection(7, zlen, rmin, rmax); + rmax = sOBConeCReinfRIn; + obConeSh->DefineSection(8, zlen, rmin, rmax); + obConeSh->DefineSection(9, sOBConeCTotZlen, rmin, rmax); + + // The external ring: a Pcon + TGeoPcon* obConeRingSh = new TGeoPcon(phi, 180 - 2 * phi, 8); + + rmin = sOBConeCRingExtR - sOBConeCThickAll; + rmax = sOBConeCReinfROut; + obConeRingSh->DefineSection(0, 0., rmin, rmax); + obConeRingSh->DefineSection(1, sOBConeCThickAll, rmin, rmax); + rmax = sOBConeCRingExtR; + obConeRingSh->DefineSection(2, sOBConeCThickAll, rmin, rmax); + zlen = sOBConeCRingZout - sOBConeCThickAll; + obConeRingSh->DefineSection(3, zlen, rmin, rmax); + rmin = sOBConeCExtR + sOBConeCThickAll; + obConeRingSh->DefineSection(4, zlen, rmin, rmax); + obConeRingSh->DefineSection(5, sOBConeCRingZout, rmin, rmax); + rmax = rmin + sOBConeCThickAll; + obConeRingSh->DefineSection(6, sOBConeCRingZout, rmin, rmax); + obConeRingSh->DefineSection(7, sOBConeCRingZlen, rmin, rmax); + + // The reinforcement rib: a Xtru + Double_t xr[sOBConeCRibNVert], yr[sOBConeCRibNVert]; + + xr[0] = 0; + yr[0] = 0; + xr[1] = obConeSh->GetRmax(9) - obConeSh->GetRmin(9); + yr[1] = yr[0]; + xr[2] = xr[1]; + yr[2] = obConeSh->GetZ(9) - obConeSh->GetZ(4); + xr[7] = xr[0]; + yr[7] = yr[2]; + xr[6] = obConeSh->GetRmin(3) - obConeSh->GetRmin(4); + yr[6] = obConeSh->GetZ(9) - obConeSh->GetZ(3); + xr[3] = xr[6] + (xr[1] - xr[0]); + yr[3] = yr[6]; + xr[5] = xr[6]; + yr[5] = sOBConeCTotZlen - sOBConeCRingZout; + xr[4] = xr[3]; + yr[4] = yr[5]; + + TGeoXtru* obConeRibSh = new TGeoXtru(2); + obConeRibSh->DefinePolygon(sOBConeCRibNVert, xr, yr); + obConeRibSh->DefineSection(0, 0); + obConeRibSh->DefineSection(1, sOBConeCThickAll); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium("EC0_M55J6K$"); // TO BE CHECKED + + TGeoVolume* obConeVol = new TGeoVolume("OBConeSideC", obConeSh, medCarbon); + obConeVol->SetFillColor(kBlue); + obConeVol->SetLineColor(kBlue); + + TGeoVolume* obConeRingVol = new TGeoVolume("OBConeRingSideC", obConeRingSh, medCarbon); + obConeRingVol->SetFillColor(kBlue); + obConeRingVol->SetLineColor(kBlue); + + TGeoVolume* obConeRibVol = new TGeoVolume("OBConeRibSideC", obConeRibSh, medCarbon); + obConeRibVol->SetFillColor(kBlue); + obConeRibVol->SetLineColor(kBlue); + + // Finally put everything in the mother volume + zpos = sOBConesZpos; + + mother->AddNode(obConeVol, 1, new TGeoTranslation(0, 0, -zpos)); + mother->AddNode(obConeVol, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + zpos -= sOBConeCThickAll; + + mother->AddNode(obConeRingVol, 1, new TGeoTranslation(0, 0, -zpos)); + mother->AddNode(obConeRingVol, 2, new TGeoCombiTrans(0, 0, -zpos, new TGeoRotation("", 180, 0, 0))); + + xpos = obConeSh->GetRmin(9); + ypos = sOBConeCReinfThick; + zpos = sOBConesZpos - obConeSh->GetZ(9); + + mother->AddNode(obConeRibVol, 1, new TGeoCombiTrans(xpos, -ypos, -zpos, new TGeoRotation("", 0, -90, 0))); + mother->AddNode(obConeRibVol, 2, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 180))); + + ypos = sOBConeCReinfThick - sOBConeCThickAll; + + mother->AddNode(obConeRibVol, 3, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 0, -90, 0))); + mother->AddNode(obConeRibVol, 4, new TGeoCombiTrans(-xpos, -ypos, -zpos, new TGeoRotation("", 0, 90, 180))); +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/digi2raw.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/digi2raw.cxx new file mode 100644 index 0000000000000..3e8497e5231f0 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/simulation/src/digi2raw.cxx @@ -0,0 +1,291 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 digi2raw.cxx +/// \author ruben.shahoyan@cern.ch + +#include +#include +#include +#include +#include +#include +#include "Framework/Logger.h" +#include +#include +#include +#include "EndCapsReconstruction/ChipMappingEC0.h" +#include "EndCapsReconstruction/GBTWord.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITSMFT/Digit.h" +#include "EndCapsSimulation/MC2RawEncoder.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/NameConf.h" +#include "CommonUtils/StringUtils.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtils.h" + +/// MC->raw conversion with new (variable page size) format for ITS +using MAP = o2::endcaps::ChipMappingEC0; +namespace bpo = boost::program_options; + +constexpr int DefRDHVersion = o2::raw::RDHUtils::getVersion(); + +void setupLinks(o2::endcaps::MC2RawEncoder& m2r, std::string_view outDir, std::string_view outPrefix, std::string_view fileFor); +void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, + uint32_t rdhV = DefRDHVersion, bool noEmptyHBF = false, + int superPageSizeInB = 1024 * 1024); + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + "Convert ITS digits to CRU raw data\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level [0 = no output]"); + add_option("input-file,i", bpo::value()->default_value("itsdigits.root"), "input ITS digits file"); + add_option("file-for,f", bpo::value()->default_value("layer"), "single file per: all,layer,cru,link"); + add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); + add_option("rdh-version,r", bpo::value()->default_value(DefRDHVersion), "RDH version to use"); + add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); + + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); + + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); + digi2raw(vm["input-file"].as(), + vm["output-dir"].as(), + vm["file-for"].as(), + vm["verbosity"].as(), + vm["rdh-version"].as(), + vm["no-empty-hbf"].as()); + + return 0; +} + +void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, uint32_t rdhV, bool noEmptyHBF, int superPageSizeInB) +{ + TStopwatch swTot; + swTot.Start(); + using ROFR = o2::itsmft::ROFRecord; + using ROFRVEC = std::vector; + const uint8_t ruSWMin = 0, ruSWMax = 0xff; // seq.ID of 1st and last RU (stave) to convert + + LOG(INFO) << "HBFUtil settings:"; + o2::raw::HBFUtils::Instance().print(); + + // if needed, create output directory + if (gSystem->AccessPathName(outDir.data())) { + if (gSystem->mkdir(outDir.data(), kTRUE)) { + LOG(FATAL) << "could not create output directory " << outDir; + } else { + LOG(INFO) << "created output directory " << outDir; + } + } + ///-------> input + std::string digTreeName{o2::base::NameConf::MCTTREENAME.data()}; + TChain digTree(digTreeName.c_str()); + digTree.AddFile(inpName.data()); + digTree.SetBranchStatus("*MCTruth*", 0); // ignore MC info + + std::vector digiVec, *digiVecP = &digiVec; + std::string digBranchName = o2::utils::concat_string(MAP::getName(), "Digit"); + if (!digTree.GetBranch(digBranchName.c_str())) { + LOG(FATAL) << "Failed to find the branch " << digBranchName << " in the tree " << digTreeName; + } + digTree.SetBranchAddress(digBranchName.c_str(), &digiVecP); + + // ROF record entries in the digit tree + ROFRVEC rofRecVec, *rofRecVecP = &rofRecVec; + std::string rofRecName = o2::utils::concat_string(MAP::getName(), "DigitROF"); + if (!digTree.GetBranch(rofRecName.c_str())) { + LOG(FATAL) << "Failed to find the branch " << rofRecName << " in the tree " << digTreeName; + } + digTree.SetBranchAddress(rofRecName.c_str(), &rofRecVecP); + ///-------< input + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + + o2::endcaps::MC2RawEncoder m2r; + m2r.setVerbosity(verbosity); + m2r.setContinuousReadout(grp->isDetContinuousReadOut(MAP::getDetID())); // must be set explicitly + m2r.setDefaultSinkName(o2::utils::concat_string(MAP::getName(), ".raw")); + m2r.setMinMaxRUSW(ruSWMin, ruSWMax); + m2r.getWriter().setSuperPageSize(superPageSizeInB); + m2r.getWriter().useRDHVersion(rdhV); + m2r.getWriter().setDontFillEmptyHBF(noEmptyHBF); + + m2r.setVerbosity(verbosity); + setupLinks(m2r, outDir, MAP::getName(), fileFor); + //-------------------------------------------------------------------------------<<<< + int lastTreeID = -1; + long offs = 0, nEntProc = 0; + for (int i = 0; i < digTree.GetEntries(); i++) { + digTree.GetEntry(i); + for (const auto& rofRec : rofRecVec) { + int nDigROF = rofRec.getNEntries(); + if (verbosity) { + LOG(INFO) << "Processing ROF:" << rofRec.getROFrame() << " with " << nDigROF << " entries"; + rofRec.print(); + } + if (!nDigROF) { + if (verbosity) { + LOG(INFO) << "Frame is empty"; // ?? + } + continue; + } + nEntProc++; + auto dgs = nDigROF ? gsl::span(&digiVec[rofRec.getFirstEntry()], nDigROF) : gsl::span(); + m2r.digits2raw(dgs, rofRec.getBCData()); + } + } // loop over multiple ROFvectors (in case of chaining) + + m2r.getWriter().writeConfFile(MAP::getName(), "RAWDATA", o2::utils::concat_string(outDir, '/', MAP::getName(), "raw.cfg")); + m2r.finalize(); // finish TF and flush data + // + swTot.Stop(); + swTot.Print(); +} + +void setupLinks(o2::endcaps::MC2RawEncoder& m2r, std::string_view outDir, std::string_view outPrefix, std::string_view fileFor) +{ + //------------------------------------------------------------------------------->>>> + // just as an example, we require here that the staves are read via 3 links, with partitioning according to lnkXB below + // while OB staves use only 1 link. + // Note, that if the RU container is not defined, it will be created automatically + // during encoding. + // If the links of the container are not defined, a single link readout will be assigned + + constexpr int MaxLinksPerRU = 3; + constexpr int MaxLinksPerCRU = 16; + const auto& mp = m2r.getMapping(); + int lnkAssign[3][MaxLinksPerRU] = { + // requested link cabling for IB, MB and OB + /* // uncomment this to have 1 link per RU + {9, 0, 0}, // IB + {16, 0, 0}, // MB + {28, 0, 0} // OB + */ + /* // uncomment this to have 3 link per RU + {3, 3, 3}, // IB + {5, 5, 6}, // MB + {9, 9, 10} // OB + */ + // partition according to https://indico.cern.ch/event/907685/contributions/3818967/attachments/2019436/3376161/20200411_SOX_EOX_Proposal.pdf + {3, 3, 3}, // IB + {8, 8, 0}, // MB + {14, 14, 0} // OB + }; + std::vector> defRU{// number of RUs per CRU at each layer + {6, 6}, + {8, 8}, + {5, 5, 5, 5}, + {12, 12}, + {8, 7, 8, 7}, + {11, 10, 11, 10}, + {12, 12, 12, 12}}; + // this is an arbitrary mapping + int nCRU = 0, nRUtot = 0, nRU = 0, nLinks = 0; + int cruID = 0; + std::string outFileLink; + + for (int ilr = 0; ilr < mp.NLayers; ilr++) { + int nruLr = mp.getNStavesOnLr(ilr); + int ruType = mp.getRUType(nRUtot); // IB, MB or OB + int* lnkAs = lnkAssign[ruType]; + // count requested number of links per RU + int nlk = 0; + for (int i = 3; i--;) { + nlk += lnkAs[i] ? 1 : 0; + } + const auto& ruList = defRU[ilr]; + // sanity check + if (std::accumulate(ruList.begin(), ruList.end(), 0) != nruLr) { + throw std::runtime_error("Declared RU partitioning does not add up to expected N staves per layer"); + } + int ruInLr = 0; // counter for RUs within the layer + int nCRUlr = ruList.size(); + for (int iCRU = 0; iCRU < nCRUlr; iCRU++) { + cruID = nCRU * 100 + o2::detectors::DetID::ITS; + int linkID = 0; + for (int irc = 0; irc < ruList[iCRU]; irc++) { + int ruID = nRUtot++; + bool acceptRU = !(ruID < m2r.getRUSWMin() || ruID > m2r.getRUSWMax()); // ignored RUs ? + if (acceptRU) { + m2r.getCreateRUDecode(ruID); // create RU container + nRU++; + } + int accL = 0; + for (int il = 0; il < MaxLinksPerRU; il++) { // create links + if (acceptRU && lnkAs[il]) { + nLinks++; + auto& ru = *m2r.getRUDecode(ruID); + uint32_t lanes = mp.getCablesOnRUType(ru.ruInfo->ruType); // lanes patter of this RU + ru.links[il] = m2r.addGBTLink(); + auto link = m2r.getGBTLink(ru.links[il]); + link->lanes = lanes & ((0x1 << lnkAs[il]) - 1) << (accL); + link->idInCRU = linkID; + link->cruID = cruID; + link->feeID = mp.RUSW2FEEId(ruID, il); + link->endPointID = 0; // 0 or 1 + accL += lnkAs[il]; + if (m2r.getVerbosity()) { + LOG(INFO) << "RU" << ruID << '(' << ruInLr << " on lr " << ilr << ") " << link->describe() + << " -> " << outFileLink; + } + // register the link in the writer, if not done here, its data will be dumped to common default file + + if (fileFor == "all") { // single file for all links + outFileLink = o2::utils::concat_string(outDir, "/", outPrefix, ".raw"); + } else if (fileFor == "layer") { + outFileLink = o2::utils::concat_string(outDir, "/", outPrefix, "_lr", std::to_string(ilr), ".raw"); + } else if (fileFor == "cru") { + outFileLink = o2::utils::concat_string(outDir, "/", outPrefix, "_cru", std::to_string(cruID), ".raw"); + } else if (fileFor == "link") { + outFileLink = o2::utils::concat_string(outDir, "/", outPrefix, "_cru", std::to_string(cruID), + "_link", std::to_string(linkID), "_ep", std::to_string(link->endPointID), ".raw"); + } else { + throw std::runtime_error("invalid option provided for file grouping"); + } + m2r.getWriter().registerLink(link->feeID, link->cruID, link->idInCRU, link->endPointID, outFileLink); + // + linkID++; + } + } // links of RU + ruInLr++; + } // RUs of CRU + nCRU++; + } // CRUs of the layer + } // layers + LOG(INFO) << "Distributed " << nLinks << " links on " << nRU << " RUs in " << nCRU << " CRUs"; +} diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/CMakeLists.txt new file mode 100644 index 0000000000000..bc46b643ef931 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/CMakeLists.txt @@ -0,0 +1,52 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +o2_add_library(EC0tracking + TARGETVARNAME targetName + SOURCES src/ClusterLines.cxx + src/Cluster.cxx + src/ROframe.cxx + src/Graph.cxx + src/DBScan.cxx + src/IOUtils.cxx + src/Label.cxx + src/PrimaryVertexContext.cxx + src/Road.cxx + src/StandaloneDebugger.cxx + src/Tracker.cxx + src/TrackerTraitsEC0CPU.cxx + src/TrackingConfigParam.cxx + src/ClusterLines.cxx + src/Vertexer.cxx + src/VertexerTraitsEC0.cxx + PUBLIC_LINK_LIBRARIES O2::GPUCommon + ms_gsl::ms_gsl + O2::CommonConstants + O2::DataFormatsITSMFT + O2::DataFormatsEndCaps + O2::SimulationDataFormat + O2::ECLayersBase + O2::DataFormatsITS) + +o2_target_root_dictionary(EC0tracking + HEADERS include/EC0tracking/ClusterLines.h + include/EC0tracking/Tracklet.h + include/EC0tracking/DBScan.h + include/EC0tracking/TrackingConfigParam.h + LINKDEF src/TrackingLinkDef.h) + +if(CUDA_ENABLED) + add_subdirectory(cuda) + target_compile_definitions(${targetName} PRIVATE CUDA_ENABLED) +endif() +if(HIP_ENABLED) + add_subdirectory(hip) + target_compile_definitions(${targetName} PRIVATE HIP_ENABLED) +endif() diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/CMakeLists.txt new file mode 100644 index 0000000000000..407e62ef42505 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +message(STATUS "Building EC0 CUDA tracker") + +o2_add_library(EC0trackingCUDA + SOURCES src/ClusterLinesGPU.cu + src/Context.cu + src/DeviceStoreNV.cu + src/DeviceStoreVertexerGPU.cu + src/Stream.cu + src/TrackerTraitsEC0NV.cu + src/VertexerTraitsEC0GPU.cu + src/Utils.cu + PUBLIC_LINK_LIBRARIES O2::EC0tracking + O2::SimConfig + cub::cub + O2::SimulationDataFormat + TARGETVARNAME targetName) + +set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) + +target_compile_definitions( + ${targetName} PRIVATE $) diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Array.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Array.h new file mode 100644 index 0000000000000..4f9bb2d8ae787 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Array.h @@ -0,0 +1,71 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Array.h +/// \brief +/// + +#ifndef TRAKINGEC0_INCLUDE_GPU_ARRAY_H_ +#define TRAKINGEC0_INCLUDE_GPU_ARRAY_H_ + +#include "EC0tracking/Definitions.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +namespace +{ +template +struct ArrayTraits final { + typedef T InternalArray[Size]; + + GPU_HOST_DEVICE static constexpr T& getReference(const InternalArray& internalArray, size_t index) noexcept + { + return const_cast(internalArray[index]); + } + + GPU_HOST_DEVICE static constexpr T* getPointer(const InternalArray& internalArray) noexcept + { + return const_cast(internalArray); + } +}; +} // namespace + +template +struct Array final { + + void copy(const Array& t) + { +#ifdef __OPENCL__ + for (size_t i{0}; i < Size; ++i) { + InternalArray[i] = t[i]; + } +#else + memcpy(InternalArray, t.data(), Size * sizeof(T)); +#endif + } + + GPU_HOST_DEVICE T* data() noexcept { return const_cast(InternalArray); } + GPU_HOST_DEVICE const T* data() const noexcept { return const_cast(InternalArray); } + GPU_HOST_DEVICE T& operator[](const int index) noexcept { return const_cast(InternalArray[index]); } + GPU_HOST_DEVICE constexpr T& operator[](const int index) const noexcept { return const_cast(InternalArray[index]); } + GPU_HOST_DEVICE size_t size() const noexcept { return Size; } + + T InternalArray[Size]; +}; +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* TRAKINGEC0_INCLUDE_GPU_CAGPUVECTOR_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/ClusterLinesGPU.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/ClusterLinesGPU.h new file mode 100644 index 0000000000000..acb60f8240c32 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/ClusterLinesGPU.h @@ -0,0 +1,72 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ClusterLinesGPU.h +/// \brief GPU-compliant version of ClusterLines, for the moment separated, might create a common traits for ClusterLines + later specifications for each arch, later. +/// \ autrhor: mconcas@cern.ch + +#ifndef O2_ENDCAPS_TRACKING_CLUSTERLINESGPU_H_ +#define O2_ENDCAPS_TRACKING_CLUSTERLINESGPU_H_ + +#include "GPUCommonDef.h" +#include "EC0tracking/ClusterLines.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +struct GPUVertex final { + GPUhd() GPUVertex() : realVertex{false} + { + } + + GPUhd() GPUVertex(float x, float y, float z, float eX, float eY, float eZ, int contrib) : xCoord{x}, + yCoord{y}, + zCoord{z}, + errorX{eZ}, + errorY{eY}, + errorZ{eZ}, + contributors{contrib}, + realVertex{true} + { + } + float xCoord; + float yCoord; + float zCoord; + float errorX; + float errorY; + float errorZ; + int contributors; + unsigned char realVertex; +}; + +class ClusterLinesGPU final +{ + public: + GPUd() ClusterLinesGPU(const Line& firstLine, const Line& secondLine); // poor man solution to calculate duplets' centroid + GPUd() void computeClusterCentroid(); + GPUd() inline float* getVertex() { return mVertex; } + + private: + float mAMatrix[6]; // AX=B + float mBMatrix[3]; // AX=B + int mLabels[2]; // labels + float mVertexCandidate[3]; // vertex candidate + float mWeightMatrix[9]; // weight matrix + float mVertex[3]; // cluster centroid position +}; + +} // namespace GPU +} // namespace ecl +} // namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Context.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Context.h new file mode 100644 index 0000000000000..8666f32efa4de --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Context.h @@ -0,0 +1,69 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Context.h +/// \brief +/// + +#ifndef TRAKINGEC0_INCLUDE_GPU_CONTEXT_H_ +#define TRAKINGEC0_INCLUDE_GPU_CONTEXT_H_ + +#include +#include +#include "EC0tracking/Definitions.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +struct DeviceProperties final { + std::string name; + int gpuProcessors; + int cudaCores; + long globalMemorySize; + long constantMemorySize; + long sharedMemorySize; + long maxClockRate; + int busWidth; + long l2CacheSize; + long registersPerBlock; + int warpSize; + int maxThreadsPerBlock; + int maxBlocksPerSM; + dim3 maxThreadsDim; + dim3 maxGridDim; +}; + +class Context final +{ + public: + static Context& getInstance(); + + Context(const Context&); + Context& operator=(const Context&); + + const DeviceProperties& getDeviceProperties(); + const DeviceProperties& getDeviceProperties(const int); + + private: + Context(bool dumpDevices = true); + ~Context() = default; + + int mDevicesNum; + std::vector mDeviceProperties; +}; +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* TRAKINGEC0_INCLUDE_GPU_CONTEXT_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/DeviceStoreNV.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/DeviceStoreNV.h new file mode 100644 index 0000000000000..acfe7c1773f1f --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/DeviceStoreNV.h @@ -0,0 +1,119 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 DeviceStoreNV.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_DEVICESTORENV_H_ +#define TRACKINGEC0__INCLUDE_DEVICESTORENV_H_ + +#include "EC0tracking/Cell.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Constants.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/Tracklet.h" +#include "EC0trackingCUDA/Array.h" +#include "EC0trackingCUDA/UniquePointer.h" +#include "EC0trackingCUDA/Vector.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +class DeviceStoreNV final +{ + public: + DeviceStoreNV(); + + UniquePointer initialise(const float3&, + const std::array, constants::ecl::LayersNumber>&, + const std::array, constants::ecl::TrackletsPerRoad>&, + const std::array, constants::ecl::CellsPerRoad>&, + const std::array, constants::ecl::CellsPerRoad - 1>&); + GPU_DEVICE const float3& getPrimaryVertex(); + GPU_HOST_DEVICE Array, constants::ecl::LayersNumber>& getClusters(); + GPU_DEVICE Array, + constants::ecl::TrackletsPerRoad>& + getIndexTables(); + GPU_HOST_DEVICE Array, constants::ecl::TrackletsPerRoad>& getTracklets(); + GPU_HOST_DEVICE Array, constants::ecl::CellsPerRoad>& getTrackletsLookupTable(); + GPU_HOST_DEVICE Array, constants::ecl::CellsPerRoad>& getTrackletsPerClusterTable(); + GPU_HOST_DEVICE Array, constants::ecl::CellsPerRoad>& getCells(); + GPU_HOST_DEVICE Array, constants::ecl::CellsPerRoad - 1>& getCellsLookupTable(); + GPU_HOST_DEVICE Array, constants::ecl::CellsPerRoad - 1>& getCellsPerTrackletTable(); + Array, constants::ecl::CellsPerRoad>& getTempTableArray(); + + private: + UniquePointer mPrimaryVertex; + Array, constants::ecl::LayersNumber> mClusters; + Array, constants::ecl::TrackletsPerRoad> + mIndexTables; + Array, constants::ecl::TrackletsPerRoad> mTracklets; + Array, constants::ecl::CellsPerRoad> mTrackletsLookupTable; + Array, constants::ecl::CellsPerRoad> mTrackletsPerClusterTable; + Array, constants::ecl::CellsPerRoad> mCells; + Array, constants::ecl::CellsPerRoad - 1> mCellsLookupTable; + Array, constants::ecl::CellsPerRoad - 1> mCellsPerTrackletTable; +}; + +GPU_DEVICE inline const float3& DeviceStoreNV::getPrimaryVertex() { return *mPrimaryVertex; } + +GPU_HOST_DEVICE inline Array, constants::ecl::LayersNumber>& DeviceStoreNV::getClusters() +{ + return mClusters; +} + +GPU_DEVICE inline Array, + constants::ecl::TrackletsPerRoad>& + DeviceStoreNV::getIndexTables() +{ + return mIndexTables; +} + +GPU_DEVICE inline Array, constants::ecl::TrackletsPerRoad>& DeviceStoreNV::getTracklets() +{ + return mTracklets; +} + +GPU_DEVICE inline Array, constants::ecl::CellsPerRoad>& DeviceStoreNV::getTrackletsLookupTable() +{ + return mTrackletsLookupTable; +} + +GPU_DEVICE inline Array, constants::ecl::CellsPerRoad>& DeviceStoreNV::getTrackletsPerClusterTable() +{ + return mTrackletsPerClusterTable; +} + +GPU_HOST_DEVICE inline Array, constants::ecl::CellsPerRoad>& DeviceStoreNV::getCells() +{ + return mCells; +} + +GPU_HOST_DEVICE inline Array, constants::ecl::CellsPerRoad - 1>& DeviceStoreNV::getCellsLookupTable() +{ + return mCellsLookupTable; +} + +GPU_HOST_DEVICE inline Array, constants::ecl::CellsPerRoad - 1>& + DeviceStoreNV::getCellsPerTrackletTable() +{ + return mCellsPerTrackletTable; +} +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/DeviceStoreVertexerGPU.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/DeviceStoreVertexerGPU.h new file mode 100644 index 0000000000000..085aa7ace86eb --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/DeviceStoreVertexerGPU.h @@ -0,0 +1,300 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 DeviceStoreVertexer.h +/// \brief This class serves as memory interface for GPU vertexer. It will access needed data structures from devicestore apis. +/// routines as static as possible. +/// \author matteo.concas@cern.ch + +#ifndef O2_EC0_TRACKING_DEVICE_STORE_VERTEXER_GPU_H_ +#define O2_EC0_TRACKING_DEVICE_STORE_VERTEXER_GPU_H_ + +#include + +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Constants.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Tracklet.h" +#include "EC0tracking/ClusterLines.h" +#include "EC0trackingCUDA/Array.h" +#include "EC0trackingCUDA/ClusterLinesGPU.h" +#include "EC0trackingCUDA/UniquePointer.h" +#include "EC0trackingCUDA/Vector.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +enum class TrackletingLayerOrder { + fromInnermostToMiddleLayer, + fromMiddleToOuterLayer +}; + +enum class VertexerLayerName { + innermostLayer, + middleLayer, + outerLayer +}; + +typedef TrackletingLayerOrder Order; +class DeviceStoreVertexerGPU final +{ + public: + DeviceStoreVertexerGPU(); + ~DeviceStoreVertexerGPU() = default; + + UniquePointer initialise(const std::array, constants::ecl::LayersNumberVertexer>&, + const std::array, + constants::ecl::LayersNumberVertexer>&); + + // RO APIs + GPUd() const Array, constants::ecl::LayersNumberVertexer>& getClusters() + { + return mClusters; + } + GPUd() const Vector& getIndexTable(const VertexerLayerName); + GPUhd() VertexerStoreConfigurationGPU& getConfig() { return mGPUConf; } + + // Writable APIs + GPUd() Vector& getDuplets01() { return mDuplets01; } + GPUd() Vector& getDuplets12() { return mDuplets12; } + GPUd() Vector& getLines() { return mTracklets; } + GPUd() Vector& getBeamPosition() { return mBeamPosition; } + GPUhd() Vector& getVertices() { return mGPUVertices; } + GPUhd() Vector& getNFoundLines() { return mNFoundLines; } + GPUhd() Vector& getNExclusiveFoundLines() { return mNExclusiveFoundLines; } + GPUhd() Vector& getCUBTmpBuffer() { return mCUBTmpBuffer; } + GPUhd() Vector& getXYCentroids() { return mXYCentroids; } + GPUhd() Vector& getZCentroids() { return mZCentroids; } + GPUhd() Array, 3>& getHistogramXYZ() { return mHistogramXYZ; } + GPUhd() Vector>& getTmpVertexPositionBins() { return mTmpVertexPositionBins; } + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + GPUd() Array, 2>& getDupletIndices() + { + return mDupletIndices; + } +#endif + GPUhd() Vector& getNFoundTracklets(Order order) + { + return mNFoundDuplets[static_cast(order)]; + } + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + GPUh() void updateDuplets(const Order, std::vector&); + GPUh() void updateFoundDuplets(const Order, std::vector&); + GPUh() std::vector getNFoundTrackletsFromGPU(const Order); + GPUh() std::vector getRawDupletsFromGPU(const Order); + GPUh() std::vector getDupletsFromGPU(const Order); + GPUh() std::vector getRawLinesFromGPU(); + GPUh() std::vector> getDupletIndicesFromGPU(); + GPUh() std::vector getNFoundLinesFromGPU(); + GPUh() std::array, 2> getHistogramXYFromGPU(); + GPUh() std::vector getHistogramZFromGPU(); +#endif + // This is temporary kept outside the debug region, since it is used to bridge data skimmed on GPU to final vertex calculation on CPU. + // Eventually, all the vertexing will be done on GPU, so this will become a debug API. + GPUh() std::vector getLinesFromGPU(); + + private: + VertexerStoreConfigurationGPU mGPUConf; + Array, constants::ecl::LayersNumberVertexer> mClusters; + Vector mTracklets; + Array, 2> mIndexTables; + Vector mGPUVertices; + + // service buffers + Vector mNFoundLines; + Vector mNExclusiveFoundLines; + Vector mDuplets01; + Vector mDuplets12; + Array, constants::ecl::LayersNumberVertexer - 1> mNFoundDuplets; + Vector mCUBTmpBuffer; + Vector mXYCentroids; + Vector mZCentroids; + Vector mBeamPosition; + Array, 3> mHistogramXYZ; + Vector> mTmpVertexPositionBins; + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + Array, 2> mDupletIndices; + Vector mSizes; +#endif +}; + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +inline std::vector DeviceStoreVertexerGPU::getNFoundTrackletsFromGPU(const Order order) +{ + // Careful: this might lead to large allocations, use debug-purpose only + std::vector sizes; + sizes.resize(constants::ecl::LayersNumberVertexer); + mSizes.copyIntoSizedVector(sizes); + std::vector nFoundDuplets; + nFoundDuplets.resize(sizes[1]); + + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); + } else { + mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); + } + + return nFoundDuplets; +} + +inline std::vector DeviceStoreVertexerGPU::getRawDupletsFromGPU(const Order order) +{ + // Careful: this might lead to large allocations, use debug-purpose only + std::vector sizes; + sizes.resize(constants::ecl::LayersNumberVertexer); + mSizes.copyIntoSizedVector(sizes); + std::vector tmpDuplets; + tmpDuplets.resize(static_cast(mGPUConf.dupletsCapacity)); + std::vector nFoundDuplets; + nFoundDuplets.resize(sizes[1]); + + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); + mDuplets01.copyIntoSizedVector(tmpDuplets); + } else { + mDuplets12.copyIntoSizedVector(tmpDuplets); + mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); + } + + return tmpDuplets; +} + +inline std::vector DeviceStoreVertexerGPU::getDupletsFromGPU(const Order order) +{ + // Careful: this might lead to large allocations, use debug-purpose only + std::vector sizes; + sizes.resize(constants::ecl::LayersNumberVertexer); + mSizes.copyIntoSizedVector(sizes); + std::vector tmpDuplets; + tmpDuplets.resize(static_cast(mGPUConf.dupletsCapacity)); + std::vector nFoundDuplets; + nFoundDuplets.resize(sizes[1]); + std::vector shrinkedDuplets; + + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); + mDuplets01.copyIntoSizedVector(tmpDuplets); + } else { + mDuplets12.copyIntoSizedVector(tmpDuplets); + mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); + } + for (int iCluster{0}; iCluster < sizes[1]; ++iCluster) { + const int stride{iCluster * mGPUConf.maxTrackletsPerCluster}; + for (int iDuplet{0}; iDuplet < nFoundDuplets[iCluster]; ++iDuplet) { + shrinkedDuplets.push_back(tmpDuplets[stride + iDuplet]); + } + } + return shrinkedDuplets; +} + +inline std::vector> DeviceStoreVertexerGPU::getDupletIndicesFromGPU() +{ + // Careful: this might lead to large allocations, use debug-purpose only. + std::array, 2> allowedLines; + std::vector> allowedPairIndices; + int nLines = getNExclusiveFoundLines().getElementFromDevice(mClusters[1].getSizeFromDevice() - 1) + getNFoundLines().getElementFromDevice(mClusters[1].getSizeFromDevice() - 1); + allowedPairIndices.reserve(nLines); + for (int iAllowed{0}; iAllowed < 2; ++iAllowed) { + allowedLines[iAllowed].resize(nLines); + mDupletIndices[iAllowed].resize(nLines); + mDupletIndices[iAllowed].copyIntoSizedVector(allowedLines[iAllowed]); + } + for (size_t iPair{0}; iPair < allowedLines[0].size(); ++iPair) { + allowedPairIndices.emplace_back(std::array{allowedLines[0][iPair], allowedLines[1][iPair]}); + } + return allowedPairIndices; +} + +inline std::array, 2> DeviceStoreVertexerGPU::getHistogramXYFromGPU() +{ + std::array, 2> histoXY; + for (int iHisto{0}; iHisto < 2; ++iHisto) { + histoXY[iHisto].resize(mGPUConf.nBinsXYZ[iHisto] - 1); + mHistogramXYZ[iHisto].copyIntoSizedVector(histoXY[iHisto]); + } + + return histoXY; +} + +inline std::vector DeviceStoreVertexerGPU::getHistogramZFromGPU() +{ + std::vector histoZ; + histoZ.resize(mGPUConf.nBinsXYZ[2] - 1); + mHistogramXYZ[2].copyIntoSizedVector(histoZ); + + return histoZ; +} + +inline void DeviceStoreVertexerGPU::updateDuplets(const Order order, std::vector& duplets) +{ + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mDuplets01.reset(duplets.data(), static_cast(duplets.size())); + } else { + mDuplets12.reset(duplets.data(), static_cast(duplets.size())); + } +} + +inline void DeviceStoreVertexerGPU::updateFoundDuplets(const Order order, std::vector& nDuplets) +{ + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].reset(nDuplets.data(), static_cast(nDuplets.size())); + } else { + mNFoundDuplets[1].reset(nDuplets.data(), static_cast(nDuplets.size())); + } +} + +inline std::vector DeviceStoreVertexerGPU::getRawLinesFromGPU() +{ + std::vector lines; + lines.resize(mGPUConf.processedTrackletsCapacity); + mTracklets.copyIntoSizedVector(lines); + + return lines; +} + +std::vector DeviceStoreVertexerGPU::getNFoundLinesFromGPU() +{ + std::vector nFoundLines; + nFoundLines.resize(mGPUConf.clustersPerLayerCapacity); + mNFoundLines.copyIntoSizedVector(nFoundLines); + + return nFoundLines; +} +#endif +// This is temporary kept outside the debug region, since it is used to bridge data skimmed on GPU to final vertex calculation on CPU. +// Eventually, all the vertexing will be done on GPU, so this will become a debug API. + +inline std::vector DeviceStoreVertexerGPU::getLinesFromGPU() +{ + std::vector lines; + std::vector tmpLines; + tmpLines.resize(mGPUConf.processedTrackletsCapacity); + mTracklets.copyIntoSizedVector(tmpLines); + for (auto& line : tmpLines) { + if (line.isEmpty) { + break; + } + lines.push_back(line); + } + return lines; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 +#endif //O2_EC0_TRACKING_DEVICE_STORE_VERTEXER_GPU_H_ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/PrimaryVertexContextNV.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/PrimaryVertexContextNV.h new file mode 100644 index 0000000000000..312be605f8d6a --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/PrimaryVertexContextNV.h @@ -0,0 +1,137 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 PrimaryVertexContextNVNV.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_PRIMARYVERTEXCONTEXTNVNV_H_ +#define TRACKINGEC0__INCLUDE_PRIMARYVERTEXCONTEXTNVNV_H_ + +#include + +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Constants.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/PrimaryVertexContext.h" +#include "EC0tracking/Road.h" +#include "EC0tracking/Tracklet.h" + +#include "EC0trackingCUDA/DeviceStoreNV.h" +#include "EC0trackingCUDA/UniquePointer.h" + +namespace o2 +{ +namespace ecl +{ + +class PrimaryVertexContextNV final : public PrimaryVertexContext +{ + public: + PrimaryVertexContextNV() = default; + virtual ~PrimaryVertexContextNV() = default; + + virtual void initialise(const MemoryParameters& memParam, const std::array, constants::ecl::LayersNumber>& cl, + const std::array& pv, const int iteration); + + GPU::DeviceStoreNV& getDeviceContext(); + GPU::Array, constants::ecl::LayersNumber>& getDeviceClusters(); + GPU::Array, constants::ecl::TrackletsPerRoad>& getDeviceTracklets(); + GPU::Array, constants::ecl::CellsPerRoad>& getDeviceTrackletsLookupTable(); + GPU::Array, constants::ecl::CellsPerRoad>& getDeviceTrackletsPerClustersTable(); + GPU::Array, constants::ecl::CellsPerRoad>& getDeviceCells(); + GPU::Array, constants::ecl::CellsPerRoad - 1>& getDeviceCellsLookupTable(); + GPU::Array, constants::ecl::CellsPerRoad - 1>& getDeviceCellsPerTrackletTable(); + std::array, constants::ecl::CellsPerRoad>& getTempTableArray(); + std::array, constants::ecl::CellsPerRoad>& getTempTrackletArray(); + std::array, constants::ecl::CellsPerRoad - 1>& getTempCellArray(); + void updateDeviceContext(); + + private: + GPU::DeviceStoreNV mGPUContext; + GPU::UniquePointer mGPUContextDevicePointer; + std::array, constants::ecl::CellsPerRoad> mTempTableArray; + std::array, constants::ecl::CellsPerRoad> mTempTrackletArray; + std::array, constants::ecl::CellsPerRoad - 1> mTempCellArray; +}; + +inline GPU::DeviceStoreNV& PrimaryVertexContextNV::getDeviceContext() +{ + return *mGPUContextDevicePointer; +} + +inline GPU::Array, constants::ecl::LayersNumber>& PrimaryVertexContextNV::getDeviceClusters() +{ + return mGPUContext.getClusters(); +} + +inline GPU::Array, constants::ecl::TrackletsPerRoad>& PrimaryVertexContextNV::getDeviceTracklets() +{ + return mGPUContext.getTracklets(); +} + +inline GPU::Array, constants::ecl::CellsPerRoad>& PrimaryVertexContextNV::getDeviceTrackletsLookupTable() +{ + return mGPUContext.getTrackletsLookupTable(); +} + +inline GPU::Array, constants::ecl::CellsPerRoad>& + PrimaryVertexContextNV::getDeviceTrackletsPerClustersTable() +{ + return mGPUContext.getTrackletsPerClusterTable(); +} + +inline GPU::Array, constants::ecl::CellsPerRoad>& PrimaryVertexContextNV::getDeviceCells() +{ + return mGPUContext.getCells(); +} + +inline GPU::Array, constants::ecl::CellsPerRoad - 1>& PrimaryVertexContextNV::getDeviceCellsLookupTable() +{ + return mGPUContext.getCellsLookupTable(); +} + +inline GPU::Array, constants::ecl::CellsPerRoad - 1>& + PrimaryVertexContextNV::getDeviceCellsPerTrackletTable() +{ + return mGPUContext.getCellsPerTrackletTable(); +} + +inline std::array, constants::ecl::CellsPerRoad>& PrimaryVertexContextNV::getTempTableArray() +{ + return mTempTableArray; +} + +inline std::array, constants::ecl::CellsPerRoad>& PrimaryVertexContextNV::getTempTrackletArray() +{ + return mTempTrackletArray; +} + +inline std::array, constants::ecl::CellsPerRoad - 1>& PrimaryVertexContextNV::getTempCellArray() +{ + return mTempCellArray; +} + +inline void PrimaryVertexContextNV::updateDeviceContext() +{ + mGPUContextDevicePointer = GPU::UniquePointer{mGPUContext}; +} + +inline void PrimaryVertexContextNV::initialise(const MemoryParameters& memParam, const std::array, constants::ecl::LayersNumber>& cl, + const std::array& pv, const int iteration) +{ + this->PrimaryVertexContext::initialise(memParam, cl, pv, iteration); + mGPUContextDevicePointer = mGPUContext.initialise(mPrimaryVertex, mClusters, mTracklets, mCells, mCellsLookupTable); +} + +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_PRIMARYVERTEXCONTEXTNV_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Stream.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Stream.h new file mode 100644 index 0000000000000..70221d9cc3558 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Stream.h @@ -0,0 +1,46 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Stream.h +/// \brief +/// + +#ifndef TRAKINGEC0_INCLUDE_GPU_STREAM_H_ +#define TRAKINGEC0_INCLUDE_GPU_STREAM_H_ + +#include "EC0tracking/Definitions.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +class Stream final +{ + + public: + Stream(); + ~Stream(); + + Stream(const Stream&) = delete; + Stream& operator=(const Stream&) = delete; + + const GPUStream& get() const; + + private: + GPUStream mStream; +}; +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* TRAKINGEC0_INCLUDE_GPU_STREAM_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/TrackerTraitsEC0NV.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/TrackerTraitsEC0NV.h new file mode 100644 index 0000000000000..ca871438c6256 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/TrackerTraitsEC0NV.h @@ -0,0 +1,44 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrackerTraitsEC0NV.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_TRACKERTRAITSNV_H_ +#define TRACKINGEC0__INCLUDE_TRACKERTRAITSNV_H_ + +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/TrackerTraitsEC0.h" + +namespace o2 +{ +namespace ecl +{ + +class PrimaryVertexContext; + +class TrackerTraitsEC0NV : public TrackerTraitsEC0 +{ + public: + TrackerTraitsEC0NV(); + virtual ~TrackerTraitsEC0NV(); + + void computeLayerCells() final; + void computeLayerTracklets() final; + void refitTracks(const std::array, 7>& tf, std::vector& tracks) final; +}; + +extern "C" TrackerTraitsEC0* createTrackerTraitsEC0NV(); +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_TRACKERTRAITS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/UniquePointer.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/UniquePointer.h new file mode 100644 index 0000000000000..7232cfaa4f757 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/UniquePointer.h @@ -0,0 +1,152 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 UniquePointer.h +/// \brief +/// + +#ifndef TRAKINGEC0_INCLUDE_GPU_CAGPUUNIQUE_POINTER_H_ +#define TRAKINGEC0_INCLUDE_GPU_CAGPUUNIQUE_POINTER_H_ + +#include "EC0trackingCUDA/Utils.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +namespace +{ +template +struct UniquePointerTraits final { + typedef T* InternalPointer; + + GPU_HOST_DEVICE static constexpr T& getReference(const InternalPointer& internalPointer) noexcept + { + return const_cast(*internalPointer); + } + + GPU_HOST_DEVICE static constexpr T* getPointer(const InternalPointer& internalPointer) noexcept + { + return const_cast(internalPointer); + } +}; +} // namespace + +template +class UniquePointer final +{ + typedef UniquePointerTraits PointerTraits; + + public: + UniquePointer(); + explicit UniquePointer(const T&); + ~UniquePointer(); + + UniquePointer(const UniquePointer&) = delete; + UniquePointer& operator=(const UniquePointer&) = delete; + + UniquePointer(UniquePointer&&); + UniquePointer& operator=(UniquePointer&&); + + GPU_HOST_DEVICE T* get() noexcept; + GPU_HOST_DEVICE const T* get() const noexcept; + GPU_HOST_DEVICE T& operator*() noexcept; + GPU_HOST_DEVICE const T& operator*() const noexcept; + + protected: + void destroy(); + + private: + typename PointerTraits::InternalPointer mDevicePointer; +}; + +template +UniquePointer::UniquePointer() : mDevicePointer{nullptr} +{ + // Nothing to do +} + +template +UniquePointer::UniquePointer(const T& ref) +{ + try { + + Utils::Host::gpuMalloc(reinterpret_cast(&mDevicePointer), sizeof(T)); + Utils::Host::gpuMemcpyHostToDevice(mDevicePointer, &ref, sizeof(T)); + + } catch (...) { + + destroy(); + + throw; + } +} + +template +UniquePointer::~UniquePointer() +{ + destroy(); +} + +template +UniquePointer::UniquePointer(UniquePointer&& other) : mDevicePointer{other.mDevicePointer} +{ + // Nothing to do +} + +template +UniquePointer& UniquePointer::operator=(UniquePointer&& other) +{ + mDevicePointer = other.mDevicePointer; + other.mDevicePointer = nullptr; + + return *this; +} + +template +void UniquePointer::destroy() +{ + if (mDevicePointer != nullptr) { + + Utils::Host::gpuFree(mDevicePointer); + } +} + +template +GPU_HOST_DEVICE T* UniquePointer::get() noexcept +{ + return PointerTraits::getPointer(mDevicePointer); +} + +template +GPU_HOST_DEVICE const T* UniquePointer::get() const noexcept +{ + return PointerTraits::getPointer(mDevicePointer); +} + +template +GPU_HOST_DEVICE T& UniquePointer::operator*() noexcept +{ + return PointerTraits::getReference(mDevicePointer); +} + +template +GPU_HOST_DEVICE const T& UniquePointer::operator*() const noexcept +{ + return PointerTraits::getReference(mDevicePointer); +} +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* TRAKINGEC0_INCLUDE_GPU_CAGPUUNIQUE_POINTER_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Utils.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Utils.h new file mode 100644 index 0000000000000..44fd9b7e92ce0 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Utils.h @@ -0,0 +1,65 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Utils.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_GPU_UTILS_H_ +#define TRACKINGEC0__INCLUDE_GPU_UTILS_H_ + +#include "GPUCommonDef.h" +#include "EC0trackingCUDA/Stream.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +namespace Utils +{ + +namespace Host +{ + +#ifdef __CUDACC__ +void checkCUDAError(const cudaError_t error, const char* file, const int line); +#endif + +dim3 getBlockSize(const int); +dim3 getBlockSize(const int, const int); +dim3 getBlockSize(const int, const int, const int); +dim3 getBlocksGrid(const dim3&, const int); +dim3 getBlocksGrid(const dim3&, const int, const int); + +void gpuMalloc(void**, const int); +void gpuFree(void*); +void gpuMemset(void*, int, int); +void gpuMemcpyHostToDevice(void*, const void*, int); +void gpuMemcpyHostToDeviceAsync(void*, const void*, int, Stream&); +void gpuMemcpyDeviceToHost(void*, const void*, int); +// void gpuStartProfiler(); +// void gpuStopProfiler(); +} // namespace Host + +namespace Device +{ +GPUd() int getLaneIndex(); +GPUd() int shareToWarp(const int, const int); +GPUd() int gpuAtomicAdd(int*, const int); +} // namespace Device +} // namespace Utils +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_GPU_UTILS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Vector.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Vector.h new file mode 100644 index 0000000000000..27a09ba3952e7 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/Vector.h @@ -0,0 +1,331 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Vector.h +/// \brief +/// + +#ifndef TRAKINGEC0_INCLUDE_GPU_VECTOR_H_ +#define TRAKINGEC0_INCLUDE_GPU_VECTOR_H_ + +#include +#include +#include +#include + +#include "EC0tracking/Definitions.h" +#include "EC0trackingCUDA/Stream.h" +#include "EC0trackingCUDA/Utils.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +template +class Vector final +{ + static_assert(std::is_trivially_destructible::value, "Vector only supports trivially destructible objects."); + + public: + Vector(); + explicit Vector(const int, const int = 0); + Vector(const T* const, const int, const int = 0); + GPU_HOST_DEVICE ~Vector(); + + Vector(const Vector&) = delete; + Vector& operator=(const Vector&) = delete; + + GPU_HOST_DEVICE Vector(Vector&&); + Vector& operator=(Vector&&); + + int getSizeFromDevice() const; + + T getElementFromDevice(const int) const; + + void resize(const int); + void reset(const int, const int = 0); + void reset(const T* const, const int, const int = 0); + void copyIntoVector(std::vector&, const int); + void copyIntoSizedVector(std::vector&); + + GPU_HOST_DEVICE T* get() const; + GPU_HOST_DEVICE int capacity() const; + GPU_HOST_DEVICE Vector getWeakCopy() const; + GPU_DEVICE T& operator[](const int) const; + + GPU_DEVICE int size() const; + GPU_DEVICE int extend(const int) const; + GPU_HOST_DEVICE void dump(); + + template + GPU_DEVICE void emplace(const int, Args&&...); + + protected: + void destroy(); + + private: + GPU_HOST_DEVICE Vector(const Vector&, const bool); + + T* mArrayPointer = nullptr; + int* mDeviceSize = nullptr; + int mCapacity; + bool mIsWeak; +}; + +template +Vector::Vector() : Vector{nullptr, 0} +{ + // Nothing to do +} + +template +Vector::Vector(const int capacity, const int initialSize) : Vector{nullptr, capacity, initialSize} +{ + // Nothing to do +} + +template +Vector::Vector(const T* const source, const int size, const int initialSize) : mCapacity{size}, mIsWeak{false} +{ + if (size > 0) { + try { + + Utils::Host::gpuMalloc(reinterpret_cast(&mArrayPointer), size * sizeof(T)); + Utils::Host::gpuMalloc(reinterpret_cast(&mDeviceSize), sizeof(int)); + + if (source != nullptr) { + + Utils::Host::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); + Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); + + } else { + + Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); + } + + } catch (...) { + + destroy(); + + throw; + } + } +} + +template +Vector::Vector(const Vector& other, const bool isWeak) + : mArrayPointer{other.mArrayPointer}, + mDeviceSize{other.mDeviceSize}, + mCapacity{other.mCapacity}, + mIsWeak{isWeak} +{ + // Nothing to do +} + +template +GPU_HOST_DEVICE Vector::~Vector() +{ + if (mIsWeak) { + + return; + + } else { +#if defined(TRACKINGEC0__GPU_DEVICE) + assert(0); +#else + destroy(); +#endif + } +} + +template +GPU_HOST_DEVICE Vector::Vector(Vector&& other) + : mArrayPointer{other.mArrayPointer}, + mDeviceSize{other.mDeviceSize}, + mCapacity{other.mCapacity}, + mIsWeak{other.mIsWeak} +{ + other.mArrayPointer = nullptr; + other.mDeviceSize = nullptr; +} + +template +Vector& Vector::operator=(Vector&& other) +{ + destroy(); + + mArrayPointer = other.mArrayPointer; + mDeviceSize = other.mDeviceSize; + mCapacity = other.mCapacity; + mIsWeak = other.mIsWeak; + + other.mArrayPointer = nullptr; + other.mDeviceSize = nullptr; + + return *this; +} + +template +int Vector::getSizeFromDevice() const +{ + int size; + Utils::Host::gpuMemcpyDeviceToHost(&size, mDeviceSize, sizeof(int)); + + return size; +} + +template +void Vector::resize(const int size) +{ + Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); +} + +template +void Vector::reset(const int capacity, const int initialSize) +{ + reset(nullptr, capacity, initialSize); +} + +template +void Vector::reset(const T* const source, const int size, const int initialSize) +{ + if (size > mCapacity) { + if (mArrayPointer != nullptr) { + Utils::Host::gpuFree(mArrayPointer); + } + Utils::Host::gpuMalloc(reinterpret_cast(&mArrayPointer), size * sizeof(T)); + mCapacity = size; + } + + if (source != nullptr) { + Utils::Host::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); + Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); + + } else { + if (mDeviceSize == nullptr) { + Utils::Host::gpuMalloc(reinterpret_cast(&mDeviceSize), sizeof(int)); + } + Utils::Host::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); + } +} + +template +void Vector::copyIntoVector(std::vector& destinationVector, const int size) +{ + + T* hostPrimitivePointer = nullptr; + + try { + + hostPrimitivePointer = static_cast(malloc(size * sizeof(T))); + Utils::Host::gpuMemcpyDeviceToHost(hostPrimitivePointer, mArrayPointer, size * sizeof(T)); + + destinationVector = std::move(std::vector(hostPrimitivePointer, hostPrimitivePointer + size)); + + } catch (...) { + + if (hostPrimitivePointer != nullptr) { + + free(hostPrimitivePointer); + } + + throw; + } +} + +template +void Vector::copyIntoSizedVector(std::vector& destinationVector) +{ + Utils::Host::gpuMemcpyDeviceToHost(destinationVector.data(), mArrayPointer, destinationVector.size() * sizeof(T)); +} + +template +inline void Vector::destroy() +{ + if (mArrayPointer != nullptr) { + + Utils::Host::gpuFree(mArrayPointer); + } + + if (mDeviceSize != nullptr) { + + Utils::Host::gpuFree(mDeviceSize); + } +} + +template +GPU_HOST_DEVICE inline T* Vector::get() const +{ + return mArrayPointer; +} + +template +GPU_HOST_DEVICE inline int Vector::capacity() const +{ + return mCapacity; +} + +template +GPU_HOST_DEVICE inline Vector Vector::getWeakCopy() const +{ + return Vector{*this, true}; +} + +template +GPU_DEVICE inline T& Vector::operator[](const int index) const +{ + return mArrayPointer[index]; +} + +template +GPU_HOST inline T Vector::getElementFromDevice(const int index) const +{ + T element; + Utils::Host::gpuMemcpyDeviceToHost(&element, mArrayPointer + index, sizeof(T)); + + return element; +} + +template +GPU_DEVICE inline int Vector::size() const +{ + return *mDeviceSize; +} + +template +GPU_DEVICE int Vector::extend(const int sizeIncrement) const +{ + const int startIndex = Utils::Device::gpuAtomicAdd(mDeviceSize, sizeIncrement); + assert(size() <= mCapacity); + + return startIndex; +} + +template +template +GPU_DEVICE void Vector::emplace(const int index, Args&&... arguments) +{ + new (mArrayPointer + index) T(std::forward(arguments)...); +} + +template +GPU_HOST_DEVICE void Vector::dump() +{ + printf("mArrayPointer = %p\nmDeviceSize = %p\nmCapacity = %d\nmIsWeak = %s\n", + mArrayPointer, mDeviceSize, mCapacity, mIsWeak ? "true" : "false"); +} +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* TRAKINGEC0_INCLUDE_GPU_VECTOR_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/VertexerTraitsEC0GPU.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/VertexerTraitsEC0GPU.h new file mode 100644 index 0000000000000..8aadf17744123 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/include/EC0trackingCUDA/VertexerTraitsEC0GPU.h @@ -0,0 +1,87 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 VertexerTraitsEC0GPU.h +/// \brief +/// \author matteo.concas@cern.ch + +#ifndef O2_EC0_TRACKING_VERTEXER_TRAITS_GPU_H_ +#define O2_EC0_TRACKING_VERTEXER_TRAITS_GPU_H_ + +#include +#include + +#include "EC0tracking/VertexerTraitsEC0.h" +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Constants.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/Tracklet.h" + +#include "EC0trackingCUDA/DeviceStoreVertexerGPU.h" +#include "EC0trackingCUDA/UniquePointer.h" + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +#include "EC0tracking/StandaloneDebugger.h" +#endif + +namespace o2 +{ +namespace ecl +{ +class ROframe; + +using constants::index_table::InversePhiBinSize; + +class VertexerTraitsEC0GPU : public VertexerTraitsEC0 +{ + public: +#ifdef _ALLOW_DEBUG_TREES_ITS_ + VertexerTraitsEC0GPU(); + virtual ~VertexerTraitsEC0GPU(); +#else + VertexerTraitsEC0GPU(); + virtual ~VertexerTraitsEC0GPU() = default; +#endif + void initialise(ROframe*) override; + void computeTracklets() override; + void computeTrackletMatching() override; + void computeVertices() override; +#ifdef _ALLOW_DEBUG_TREES_ITS_ + void computeMCFiltering() override; +#endif + + // GPU-specific getters + GPUd() static const int2 getBinsPhiRectWindow(const Cluster&, float maxdeltaphi); + GPUhd() GPU::DeviceStoreVertexerGPU& getDeviceContext(); + + protected: + GPU::DeviceStoreVertexerGPU mStoreVertexerGPU; + GPU::UniquePointer mStoreVertexerGPUPtr; +}; + +inline GPUd() const int2 VertexerTraitsEC0GPU::getBinsPhiRectWindow(const Cluster& currentCluster, float phiCut) +{ + // This function returns the lowest PhiBin and the number of phi bins to be spanned, In the form int2{phiBinLow, PhiBinSpan} + const int phiBinMin{index_table_utils::getPhiBinIndex( + math_utils::getNormalizedPhiCoordinate(currentCluster.phiCoordinate - phiCut))}; + const int phiBinSpan{static_cast(MATH_CEIL(phiCut * InversePhiBinSize))}; + return int2{phiBinMin, phiBinSpan}; +} + +inline GPU::DeviceStoreVertexerGPU& VertexerTraitsEC0GPU::getDeviceContext() +{ + return *mStoreVertexerGPUPtr; +} + +extern "C" VertexerTraitsEC0* createVertexerTraitsEC0GPU(); + +} // namespace ecl +} // namespace o2 +#endif /* O2_EC0_TRACKING_VERTEXER_TRAITS_GPU_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/ClusterLinesGPU.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/ClusterLinesGPU.cu new file mode 100644 index 0000000000000..0cee6a84c5021 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/ClusterLinesGPU.cu @@ -0,0 +1,134 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 "EC0trackingCUDA/ClusterLinesGPU.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +GPUd() ClusterLinesGPU::ClusterLinesGPU(const Line& firstLine, const Line& secondLine) +{ + float covarianceFirst[3]; + float covarianceSecond[3]; + + for (int i{0}; i < 3; ++i) { + covarianceFirst[i] = 1.f; + covarianceSecond[i] = 1.f; + } + + float determinantFirst = + firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + + firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + + firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; + float determinantSecond = + secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + + secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; + + mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + + firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / + determinantFirst + + (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + + secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / + determinantSecond; + + mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; + + mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; + + mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + + firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / + determinantFirst + + (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / + determinantSecond; + + mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - + secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; + + mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + + firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / + determinantFirst + + (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / + determinantSecond; + + mBMatrix[0] = + (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + + firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / + determinantFirst; + + mBMatrix[0] += + (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + + secondLine.cosinesDirector[2] * covarianceSecond[1] * + (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + + secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / + determinantSecond; + + mBMatrix[1] = + (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + + firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / + determinantFirst; + + mBMatrix[1] += + (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + + secondLine.cosinesDirector[2] * covarianceSecond[0] * + (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + + secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / + determinantSecond; + + mBMatrix[2] = + (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + + firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / + determinantFirst; + + mBMatrix[2] += + (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + + secondLine.cosinesDirector[1] * covarianceSecond[0] * + (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + + secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / + determinantSecond; + + computeClusterCentroid(); +} + +GPUd() void ClusterLinesGPU::computeClusterCentroid() +{ + + float determinant{mAMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - + mAMatrix[1] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + + mAMatrix[2] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])}; + + if (determinant == 0) { + return; + } + + mVertex[0] = -(mBMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - + mAMatrix[1] * (mBMatrix[1] * mAMatrix[5] - mAMatrix[4] * mBMatrix[2]) + + mAMatrix[2] * (mBMatrix[1] * mAMatrix[4] - mBMatrix[2] * mAMatrix[3])) / + determinant; + mVertex[1] = -(mAMatrix[0] * (mBMatrix[1] * mAMatrix[5] - mBMatrix[2] * mAMatrix[4]) - + mBMatrix[0] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + + mAMatrix[2] * (mAMatrix[1] * mBMatrix[2] - mAMatrix[2] * mBMatrix[1])) / + determinant; + mVertex[2] = -(mAMatrix[0] * (mAMatrix[3] * mBMatrix[2] - mBMatrix[1] * mAMatrix[4]) - + mAMatrix[1] * (mAMatrix[1] * mBMatrix[2] - mBMatrix[1] * mAMatrix[2]) + + mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / + determinant; +} +} // namespace GPU +} // namespace ecl +} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Context.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Context.cu new file mode 100644 index 0000000000000..147f107f62bbd --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Context.cu @@ -0,0 +1,174 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Context.cu +/// \brief +/// + +#include "EC0trackingCUDA/Context.h" +#include "EC0trackingCUDA/Utils.h" + +#include +#include + +#include + +namespace +{ + +inline int getCudaCores(const int major, const int minor) +{ + // Defines for GPU Architecture types (using the SM version to determine the # of cores per SM + typedef struct + { + int SM; // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version + int Cores; + } sSMtoCores; + + sSMtoCores nGpuArchCoresPerSM[] = + { + {0x20, 32}, // Fermi Generation (SM 2.0) GF100 class + {0x21, 48}, // Fermi Generation (SM 2.1) GF10x class + {0x30, 192}, // Kepler Generation (SM 3.0) GK10x class + {0x32, 192}, // Kepler Generation (SM 3.2) GK10x class + {0x35, 192}, // Kepler Generation (SM 3.5) GK11x class + {0x37, 192}, // Kepler Generation (SM 3.7) GK21x class + {0x50, 128}, // Maxwell Generation (SM 5.0) GM10x class + {0x52, 128}, // Maxwell Generation (SM 5.2) GM20x class + {0x53, 128}, // Maxwell Generation (SM 5.3) GM20x class + {0x60, 64}, // Pascal Generation (SM 6.0) GP100 class + {0x61, 128}, // Pascal Generation (SM 6.1) GP10x class + {0x62, 128}, // Pascal Generation (SM 6.2) GP10x class + {0x70, 64}, // Volta Generation (SM 7.0) GV100 class + {0x72, 64}, // Volta Generation (SM 7.2) GV10B class + {0x75, 64}, // Turing Generation (SM 7.5) TU1xx class + {-1, -1}}; + + int index = 0; + + while (nGpuArchCoresPerSM[index].SM != -1) { + if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) { + return nGpuArchCoresPerSM[index].Cores; + } + + index++; + } + + // If we don't find the values, we default use the previous one to run properly + printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[index - 1].Cores); + return nGpuArchCoresPerSM[index - 1].Cores; +} + +inline int getMaxThreadsPerSM(const int major, const int minor) +{ + return 8; +} + +} // namespace + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +using Utils::Host::checkCUDAError; + +Context::Context(bool dumpDevices) +{ + checkCUDAError(cudaGetDeviceCount(&mDevicesNum), __FILE__, __LINE__); + + if (mDevicesNum == 0) { + + throw std::runtime_error{"There are no available device(s) that support CUDA\n"}; + } + + mDeviceProperties.resize(mDevicesNum, DeviceProperties{}); + + int currentDeviceIndex; + checkCUDAError(cudaGetDevice(¤tDeviceIndex), __FILE__, __LINE__); + + for (int iDevice{0}; iDevice < mDevicesNum; ++iDevice) { + + cudaDeviceProp deviceProperties; + + checkCUDAError(cudaSetDevice(iDevice), __FILE__, __LINE__); + checkCUDAError(cudaGetDeviceProperties(&deviceProperties, iDevice), __FILE__, __LINE__); + + int major = deviceProperties.major; + int minor = deviceProperties.minor; + + mDeviceProperties[iDevice].name = deviceProperties.name; + mDeviceProperties[iDevice].gpuProcessors = deviceProperties.multiProcessorCount; + mDeviceProperties[iDevice].cudaCores = getCudaCores(major, minor) * deviceProperties.multiProcessorCount; + mDeviceProperties[iDevice].globalMemorySize = deviceProperties.totalGlobalMem; + mDeviceProperties[iDevice].constantMemorySize = deviceProperties.totalConstMem; + mDeviceProperties[iDevice].sharedMemorySize = deviceProperties.sharedMemPerBlock; + mDeviceProperties[iDevice].maxClockRate = deviceProperties.memoryClockRate; + mDeviceProperties[iDevice].busWidth = deviceProperties.memoryBusWidth; + mDeviceProperties[iDevice].l2CacheSize = deviceProperties.l2CacheSize; + mDeviceProperties[iDevice].registersPerBlock = deviceProperties.regsPerBlock; + mDeviceProperties[iDevice].warpSize = deviceProperties.warpSize; + mDeviceProperties[iDevice].maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock; + mDeviceProperties[iDevice].maxBlocksPerSM = getMaxThreadsPerSM(major, minor); + mDeviceProperties[iDevice].maxThreadsDim = dim3{static_cast(deviceProperties.maxThreadsDim[0]), + static_cast(deviceProperties.maxThreadsDim[1]), + static_cast(deviceProperties.maxThreadsDim[2])}; + mDeviceProperties[iDevice].maxGridDim = dim3{static_cast(deviceProperties.maxGridSize[0]), + static_cast(deviceProperties.maxGridSize[1]), + static_cast(deviceProperties.maxGridSize[2])}; + if (dumpDevices) { + std::cout << "################ CUDA DEVICE " << iDevice << " ################" << std::endl; + std::cout << "Name " << mDeviceProperties[iDevice].name << std::endl; + std::cout << "minor " << minor << " major " << major << std::endl; + std::cout << "gpuProcessors " << mDeviceProperties[iDevice].gpuProcessors << std::endl; + std::cout << "cudaCores " << mDeviceProperties[iDevice].cudaCores << std::endl; + std::cout << "globalMemorySize " << mDeviceProperties[iDevice].globalMemorySize << std::endl; + std::cout << "constantMemorySize " << mDeviceProperties[iDevice].constantMemorySize << std::endl; + std::cout << "sharedMemorySize " << mDeviceProperties[iDevice].sharedMemorySize << std::endl; + std::cout << "maxClockRate " << mDeviceProperties[iDevice].maxClockRate << std::endl; + std::cout << "busWidth " << mDeviceProperties[iDevice].busWidth << std::endl; + std::cout << "l2CacheSize " << mDeviceProperties[iDevice].l2CacheSize << std::endl; + std::cout << "registersPerBlock " << mDeviceProperties[iDevice].registersPerBlock << std::endl; + std::cout << "warpSize " << mDeviceProperties[iDevice].warpSize << std::endl; + std::cout << "maxThreadsPerBlock " << mDeviceProperties[iDevice].maxThreadsPerBlock << std::endl; + std::cout << "maxBlocksPerSM " << mDeviceProperties[iDevice].maxBlocksPerSM << std::endl; + std::cout << "maxThreadsDim " << mDeviceProperties[iDevice].maxThreadsDim.x << ", " << mDeviceProperties[iDevice].maxThreadsDim.y << ", " << mDeviceProperties[iDevice].maxThreadsDim.z << std::endl; + std::cout << "maxGridDim " << mDeviceProperties[iDevice].maxGridDim.x << ", " << mDeviceProperties[iDevice].maxGridDim.y << ", " << mDeviceProperties[iDevice].maxGridDim.z << std::endl; + std::cout << std::endl; + } + } + + checkCUDAError(cudaSetDevice(currentDeviceIndex), __FILE__, __LINE__); +} + +Context& Context::getInstance() +{ + static Context gpuContext; + return gpuContext; +} + +const DeviceProperties& Context::getDeviceProperties() +{ + int currentDeviceIndex; + checkCUDAError(cudaGetDevice(¤tDeviceIndex), __FILE__, __LINE__); + + return getDeviceProperties(currentDeviceIndex); +} + +const DeviceProperties& Context::getDeviceProperties(const int deviceIndex) +{ + return mDeviceProperties[deviceIndex]; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/DeviceStoreNV.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/DeviceStoreNV.cu new file mode 100644 index 0000000000000..0f32084fff385 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/DeviceStoreNV.cu @@ -0,0 +1,184 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 DeviceStoreNV.cxx +/// \brief +/// + +#include "EC0trackingCUDA/DeviceStoreNV.h" + +#include + +#include "EC0trackingCUDA/Stream.h" + +namespace +{ + +using namespace o2::ecl; + +__device__ void fillIndexTables(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +{ + + const int currentClusterIndex{static_cast(blockDim.x * blockIdx.x + threadIdx.x)}; + const int nextLayerClustersNum{static_cast(primaryVertexContext.getClusters()[layerIndex + 1].size())}; + + if (currentClusterIndex < nextLayerClustersNum) { + + const int currentBinIndex{ + primaryVertexContext.getClusters()[layerIndex + 1][currentClusterIndex].indexTableBinIndex}; + int previousBinIndex; + + if (currentClusterIndex == 0) { + + primaryVertexContext.getIndexTables()[layerIndex][0] = 0; + previousBinIndex = 0; + + } else { + + previousBinIndex = primaryVertexContext.getClusters()[layerIndex + 1][currentClusterIndex - 1].indexTableBinIndex; + } + + if (currentBinIndex > previousBinIndex) { + + for (int iBin{previousBinIndex + 1}; iBin <= currentBinIndex; ++iBin) { + + primaryVertexContext.getIndexTables()[layerIndex][iBin] = currentClusterIndex; + } + + previousBinIndex = currentBinIndex; + } + + if (currentClusterIndex == nextLayerClustersNum - 1) { + + for (int iBin{currentBinIndex + 1}; iBin <= o2::ecl::constants::index_table::ZBins * o2::ecl::constants::index_table::PhiBins; + iBin++) { + + primaryVertexContext.getIndexTables()[layerIndex][iBin] = nextLayerClustersNum; + } + } + } +} + +__device__ void fillTrackletsPerClusterTables(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +{ + const int currentClusterIndex{static_cast(blockDim.x * blockIdx.x + threadIdx.x)}; + const int clustersSize{static_cast(primaryVertexContext.getClusters()[layerIndex + 1].size())}; + + if (currentClusterIndex < clustersSize) { + + primaryVertexContext.getTrackletsPerClusterTable()[layerIndex][currentClusterIndex] = 0; + } +} + +__device__ void fillCellsPerClusterTables(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +{ + const int totalThreadNum{static_cast(primaryVertexContext.getClusters()[layerIndex + 1].size())}; + const int trackletsSize{static_cast(primaryVertexContext.getTracklets()[layerIndex + 1].capacity())}; + const int trackletsPerThread{1 + (trackletsSize - 1) / totalThreadNum}; + const int firstTrackletIndex{static_cast(blockDim.x * blockIdx.x + threadIdx.x) * trackletsPerThread}; + + if (firstTrackletIndex < trackletsSize) { + + const int trackletsToSet{min(trackletsSize, firstTrackletIndex + trackletsPerThread) - firstTrackletIndex}; + memset(&primaryVertexContext.getCellsPerTrackletTable()[layerIndex][firstTrackletIndex], 0, + trackletsToSet * sizeof(int)); + } +} + +__global__ void fillDeviceStructures(GPU::DeviceStoreNV& primaryVertexContext, const int layerIndex) +{ + fillIndexTables(primaryVertexContext, layerIndex); + + if (layerIndex < o2::ecl::constants::ecl::CellsPerRoad) { + + fillTrackletsPerClusterTables(primaryVertexContext, layerIndex); + } + + if (layerIndex < o2::ecl::constants::ecl::CellsPerRoad - 1) { + + fillCellsPerClusterTables(primaryVertexContext, layerIndex); + } +} +} // namespace + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +DeviceStoreNV::DeviceStoreNV() +{ + // Nothing to do +} + +UniquePointer DeviceStoreNV::initialise(const float3& primaryVertex, + const std::array, constants::ecl::LayersNumber>& clusters, + const std::array, constants::ecl::TrackletsPerRoad>& tracklets, + const std::array, constants::ecl::CellsPerRoad>& cells, + const std::array, constants::ecl::CellsPerRoad - 1>& cellsLookupTable) +{ + mPrimaryVertex = UniquePointer{primaryVertex}; + + for (int iLayer{0}; iLayer < constants::ecl::LayersNumber; ++iLayer) { + + this->mClusters[iLayer] = + Vector{&clusters[iLayer][0], static_cast(clusters[iLayer].size())}; + + if (iLayer < constants::ecl::TrackletsPerRoad) { + this->mTracklets[iLayer].reset(tracklets[iLayer].capacity()); + } + + if (iLayer < constants::ecl::CellsPerRoad) { + + this->mTrackletsLookupTable[iLayer].reset(static_cast(clusters[iLayer + 1].size())); + this->mTrackletsPerClusterTable[iLayer].reset(static_cast(clusters[iLayer + 1].size())); + this->mCells[iLayer].reset(static_cast(cells[iLayer].capacity())); + } + + if (iLayer < constants::ecl::CellsPerRoad - 1) { + + this->mCellsLookupTable[iLayer].reset(static_cast(cellsLookupTable[iLayer].size())); + this->mCellsPerTrackletTable[iLayer].reset(static_cast(cellsLookupTable[iLayer].size())); + } + } + + UniquePointer gpuContextDevicePointer{*this}; + + std::array streamArray; + + for (int iLayer{0}; iLayer < constants::ecl::TrackletsPerRoad; ++iLayer) { + + const int nextLayerClustersNum = static_cast(clusters[iLayer + 1].size()); + + dim3 threadsPerBlock{Utils::Host::getBlockSize(nextLayerClustersNum)}; + dim3 blocksGrid{Utils::Host::getBlocksGrid(threadsPerBlock, nextLayerClustersNum)}; + + fillDeviceStructures<<>>(*gpuContextDevicePointer, iLayer); + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << __FILE__ << ":" << __LINE__ << " CUDA API returned error [" << cudaGetErrorString(error) + << "] (code " << error << ")" << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } + + return gpuContextDevicePointer; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/DeviceStoreVertexerGPU.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/DeviceStoreVertexerGPU.cu new file mode 100644 index 0000000000000..34dab6ac444f8 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/DeviceStoreVertexerGPU.cu @@ -0,0 +1,109 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 DeviceStoreVertexerGPU.cu +/// \brief +/// \author matteo.concas@cern.ch + +#include + +#include "EC0trackingCUDA/DeviceStoreVertexerGPU.h" +#include "EC0tracking/Configuration.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ +GPUg() void defaultInitArrayKernel(int* array, const size_t arraySize, const int initValue = 0) +{ + for (size_t i{blockIdx.x * blockDim.x + threadIdx.x}; i < arraySize; i += blockDim.x * gridDim.x) { + if (i < arraySize) { + array[i] = initValue; + } + } +} + +DeviceStoreVertexerGPU::DeviceStoreVertexerGPU() +{ + mDuplets01 = Vector{mGPUConf.dupletsCapacity, mGPUConf.dupletsCapacity}; // 200 * 4e4 * sizeof(Tracklet) = 128MB + mDuplets12 = Vector{mGPUConf.dupletsCapacity, mGPUConf.dupletsCapacity}; // 200 * 4e4 * sizeof(Tracklet) = 128MB + mTracklets = Vector{mGPUConf.processedTrackletsCapacity, mGPUConf.processedTrackletsCapacity}; // 200 * 4e4 * sizeof(Line) = 296MB + mCUBTmpBuffer = Vector{mGPUConf.tmpCUBBufferSize, mGPUConf.tmpCUBBufferSize}; // 5e3 * sizeof(int) = 20KB + mXYCentroids = Vector{2 * mGPUConf.maxCentroidsXYCapacity, 2 * mGPUConf.maxCentroidsXYCapacity}; // + mZCentroids = Vector{mGPUConf.processedTrackletsCapacity, mGPUConf.processedTrackletsCapacity}; // + mNFoundLines = Vector{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; // 4e4 * sizeof(int) = 160KB + mNExclusiveFoundLines = Vector{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; // 4e4 * sizeof(int) = 160KB, tot = <10MB + mTmpVertexPositionBins = Vector>{3, 3}; + mGPUVertices = Vector{mGPUConf.nMaxVertices, mGPUConf.nMaxVertices}; + mBeamPosition = Vector{2, 2}; + + for (int iTable{0}; iTable < 2; ++iTable) { + mIndexTables[iTable] = Vector{constants::index_table::ZBins * constants::index_table::PhiBins + 1}; // 2*20*20+1 * sizeof(int) = 802B + } + for (int iLayer{0}; iLayer < constants::ecl::LayersNumberVertexer; ++iLayer) { // 4e4 * 3 * sizof(Cluster) = 3.36MB + mClusters[iLayer] = Vector{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; + } + for (int iPair{0}; iPair < constants::ecl::LayersNumberVertexer - 1; ++iPair) { + mNFoundDuplets[iPair] = Vector{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; // 4e4 * 2 * sizeof(int) = 320KB + } + for (int iHisto{0}; iHisto < 3; ++iHisto) { + mHistogramXYZ[iHisto] = Vector{mGPUConf.histConf.nBinsXYZ[iHisto], mGPUConf.histConf.nBinsXYZ[iHisto]}; + } + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + for (int iLayersCouple{0}; iLayersCouple < 2; ++iLayersCouple) { + mDupletIndices[iLayersCouple] = Vector{mGPUConf.processedTrackletsCapacity, mGPUConf.processedTrackletsCapacity}; + } + mSizes = Vector{constants::ecl::LayersNumberVertexer}; +#endif +} // namespace GPU + +UniquePointer DeviceStoreVertexerGPU::initialise(const std::array, constants::ecl::LayersNumberVertexer>& clusters, + const std::array, + constants::ecl::LayersNumberVertexer>& indexTables) +{ +#ifdef _ALLOW_DEBUG_TREES_ITS_ + std::array tmpSizes = {static_cast(clusters[0].size()), + static_cast(clusters[1].size()), + static_cast(clusters[2].size())}; + mSizes.reset(tmpSizes.data(), static_cast(3)); +#endif + for (int iLayer{0}; iLayer < constants::ecl::LayersNumberVertexer; ++iLayer) { + mClusters[iLayer].reset(clusters[iLayer].data(), static_cast(clusters[iLayer].size())); + } + mIndexTables[0].reset(indexTables[0].data(), static_cast(indexTables[0].size())); + mIndexTables[1].reset(indexTables[2].data(), static_cast(indexTables[2].size())); + + const dim3 threadsPerBlock{Utils::Host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + + UniquePointer deviceStoreVertexerPtr{*this}; + + defaultInitArrayKernel<<>>(getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer).get(), + getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer).capacity()); + defaultInitArrayKernel<<>>(getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer).get(), + getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer).capacity()); + + return deviceStoreVertexerPtr; +} + +GPUd() const Vector& DeviceStoreVertexerGPU::getIndexTable(const VertexerLayerName layer) +{ + if (layer == VertexerLayerName::innermostLayer) { + return mIndexTables[0]; + } + return mIndexTables[1]; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Stream.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Stream.cu new file mode 100644 index 0000000000000..16b0e6cd492b7 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Stream.cu @@ -0,0 +1,41 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Stream.cu +/// \brief +/// + +#include "EC0trackingCUDA/Stream.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +Stream::Stream() +{ + cudaStreamCreateWithFlags(&mStream, cudaStreamNonBlocking); +} + +Stream::~Stream() +{ + cudaStreamDestroy(mStream); +} + +const GPUStream& Stream::get() const +{ + return mStream; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/TrackerTraitsEC0NV.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/TrackerTraitsEC0NV.cu new file mode 100644 index 0000000000000..8d7e281db9727 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/TrackerTraitsEC0NV.cu @@ -0,0 +1,497 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrackerTraitsEC0NV.cu +/// \brief +/// + +#include "EC0trackingCUDA/TrackerTraitsEC0NV.h" + +#include +#include +#include + +#include + +#include "cub/cub.cuh" + +#include "EC0tracking/Constants.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/IndexTableUtils.h" +#include "EC0tracking/MathUtils.h" +#include "EC0trackingCUDA/Context.h" +#include "EC0trackingCUDA/DeviceStoreNV.h" +#include "EC0trackingCUDA/PrimaryVertexContextNV.h" +#include "EC0trackingCUDA/Stream.h" +#include "EC0trackingCUDA/Vector.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +__constant__ TrackingParameters kTrkPar; + +__device__ void computeLayerTracklets(DeviceStoreNV& devStore, const int layerIndex, + Vector& trackletsVector) +{ + const int currentClusterIndex = static_cast(blockDim.x * blockIdx.x + threadIdx.x); + int clusterTrackletsNum = 0; + + if (currentClusterIndex < devStore.getClusters()[layerIndex].size()) { + + Vector nextLayerClusters{devStore.getClusters()[layerIndex + 1].getWeakCopy()}; + const Cluster currentCluster{devStore.getClusters()[layerIndex][currentClusterIndex]}; + + /*if (mUsedClustersTable[currentCluster.clusterId] != constants::ecl::UnusedIndex) { + + continue; + }*/ + + const float tanLambda{(currentCluster.zCoordinate - devStore.getPrimaryVertex().z) / currentCluster.rCoordinate}; + const float directionZIntersection{tanLambda * ((constants::ecl::LayersRCoordinate())[layerIndex + 1] - currentCluster.rCoordinate) + currentCluster.zCoordinate}; + + const int4 selectedBinsRect{TrackerTraitsEC0::getBinsRect(currentCluster, layerIndex, directionZIntersection, + kTrkPar.TrackletMaxDeltaZ[layerIndex], kTrkPar.TrackletMaxDeltaPhi)}; + + if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { + + const int nextLayerClustersNum{static_cast(nextLayerClusters.size())}; + int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; + + if (phiBinsNum < 0) { + + phiBinsNum += constants::index_table::PhiBins; + } + + for (int iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; + iPhiBin = ++iPhiBin == constants::index_table::PhiBins ? 0 : iPhiBin, iPhiCount++) { + + const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int firstRowClusterIndex = devStore.getIndexTables()[layerIndex][firstBinIndex]; + const int maxRowClusterIndex = devStore.getIndexTables()[layerIndex][{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}]; + + for (int iNextLayerCluster{firstRowClusterIndex}; + iNextLayerCluster <= maxRowClusterIndex && iNextLayerCluster < nextLayerClustersNum; ++iNextLayerCluster) { + + const Cluster& nextCluster{nextLayerClusters[iNextLayerCluster]}; + + const float deltaZ{gpu::GPUCommonMath::Abs( + tanLambda * (nextCluster.rCoordinate - currentCluster.rCoordinate) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; + const float deltaPhi{gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate)}; + + if (deltaZ < kTrkPar.TrackletMaxDeltaZ[layerIndex] && (deltaPhi < kTrkPar.TrackletMaxDeltaPhi || gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.TrackletMaxDeltaPhi)) { + + cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); + int currentIndex{}; + + if (threadGroup.thread_rank() == 0) { + + currentIndex = trackletsVector.extend(threadGroup.size()); + } + + currentIndex = threadGroup.shfl(currentIndex, 0) + threadGroup.thread_rank(); + + trackletsVector.emplace(currentIndex, currentClusterIndex, iNextLayerCluster, currentCluster, nextCluster); + ++clusterTrackletsNum; + } + } + } + + if (layerIndex > 0) { + + devStore.getTrackletsPerClusterTable()[layerIndex - 1][currentClusterIndex] = clusterTrackletsNum; + } + } + } +} + +__device__ void computeLayerCells(DeviceStoreNV& devStore, const int layerIndex, + Vector& cellsVector) +{ + const int currentTrackletIndex = static_cast(blockDim.x * blockIdx.x + threadIdx.x); + const float3& primaryVertex = devStore.getPrimaryVertex(); + int trackletCellsNum = 0; + + if (currentTrackletIndex < devStore.getTracklets()[layerIndex].size()) { + + const Tracklet& currentTracklet{devStore.getTracklets()[layerIndex][currentTrackletIndex]}; + const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; + const int nextLayerFirstTrackletIndex{ + devStore.getTrackletsLookupTable()[layerIndex][nextLayerClusterIndex]}; + const int nextLayerTrackletsNum{static_cast(devStore.getTracklets()[layerIndex + 1].size())}; + + if (devStore.getTracklets()[layerIndex + 1][nextLayerFirstTrackletIndex].firstClusterIndex == nextLayerClusterIndex) { + + const Cluster& firstCellCluster{ + devStore.getClusters()[layerIndex][currentTracklet.firstClusterIndex]}; + const Cluster& secondCellCluster{ + devStore.getClusters()[layerIndex + 1][currentTracklet.secondClusterIndex]}; + const float firstCellClusterQuadraticRCoordinate{firstCellCluster.rCoordinate * firstCellCluster.rCoordinate}; + const float secondCellClusterQuadraticRCoordinate{secondCellCluster.rCoordinate * secondCellCluster.rCoordinate}; + const float3 firstDeltaVector{secondCellCluster.xCoordinate - firstCellCluster.xCoordinate, + secondCellCluster.yCoordinate - firstCellCluster.yCoordinate, secondCellClusterQuadraticRCoordinate - firstCellClusterQuadraticRCoordinate}; + + for (int iNextLayerTracklet{nextLayerFirstTrackletIndex}; + iNextLayerTracklet < nextLayerTrackletsNum && devStore.getTracklets()[layerIndex + 1][iNextLayerTracklet].firstClusterIndex == nextLayerClusterIndex; ++iNextLayerTracklet) { + + const Tracklet& nextTracklet{devStore.getTracklets()[layerIndex + 1][iNextLayerTracklet]}; + const float deltaTanLambda{gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + const float deltaPhi{gpu::GPUCommonMath::Abs(currentTracklet.phiCoordinate - nextTracklet.phiCoordinate)}; + + if (deltaTanLambda < kTrkPar.CellMaxDeltaTanLambda && (deltaPhi < kTrkPar.CellMaxDeltaPhi || gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.CellMaxDeltaPhi)) { + + const float averageTanLambda{0.5f * (currentTracklet.tanLambda + nextTracklet.tanLambda)}; + const float directionZIntersection{-averageTanLambda * firstCellCluster.rCoordinate + firstCellCluster.zCoordinate}; + const float deltaZ{gpu::GPUCommonMath::Abs(directionZIntersection - primaryVertex.z)}; + + if (deltaZ < kTrkPar.CellMaxDeltaZ[layerIndex]) { + + const Cluster& thirdCellCluster{ + devStore.getClusters()[layerIndex + 2][nextTracklet.secondClusterIndex]}; + + const float thirdCellClusterQuadraticRCoordinate{thirdCellCluster.rCoordinate * thirdCellCluster.rCoordinate}; + + const float3 secondDeltaVector{thirdCellCluster.xCoordinate - firstCellCluster.xCoordinate, + thirdCellCluster.yCoordinate - firstCellCluster.yCoordinate, thirdCellClusterQuadraticRCoordinate - firstCellClusterQuadraticRCoordinate}; + + float3 cellPlaneNormalVector{math_utils::crossProduct(firstDeltaVector, secondDeltaVector)}; + + const float vectorNorm{gpu::GPUCommonMath::Sqrt( + cellPlaneNormalVector.x * cellPlaneNormalVector.x + cellPlaneNormalVector.y * cellPlaneNormalVector.y + cellPlaneNormalVector.z * cellPlaneNormalVector.z)}; + + if (!(vectorNorm < constants::math::FloatMinThreshold || gpu::GPUCommonMath::Abs(cellPlaneNormalVector.z) < constants::math::FloatMinThreshold)) { + + const float inverseVectorNorm{1.0f / vectorNorm}; + const float3 normalizedPlaneVector{cellPlaneNormalVector.x * inverseVectorNorm, cellPlaneNormalVector.y * inverseVectorNorm, cellPlaneNormalVector.z * inverseVectorNorm}; + const float planeDistance{-normalizedPlaneVector.x * (secondCellCluster.xCoordinate - primaryVertex.x) - (normalizedPlaneVector.y * secondCellCluster.yCoordinate - primaryVertex.y) - normalizedPlaneVector.z * secondCellClusterQuadraticRCoordinate}; + const float normalizedPlaneVectorQuadraticZCoordinate{normalizedPlaneVector.z * normalizedPlaneVector.z}; + const float cellTrajectoryRadius{gpu::GPUCommonMath::Sqrt( + (1.0f - normalizedPlaneVectorQuadraticZCoordinate - 4.0f * planeDistance * normalizedPlaneVector.z) / (4.0f * normalizedPlaneVectorQuadraticZCoordinate))}; + const float2 circleCenter{-0.5f * normalizedPlaneVector.x / normalizedPlaneVector.z, -0.5f * normalizedPlaneVector.y / normalizedPlaneVector.z}; + const float distanceOfClosestApproach{gpu::GPUCommonMath::Abs( + cellTrajectoryRadius - gpu::GPUCommonMath::Sqrt(circleCenter.x * circleCenter.x + circleCenter.y * circleCenter.y))}; + + if (distanceOfClosestApproach <= kTrkPar.CellMaxDCA[layerIndex]) { + + cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); + int currentIndex{}; + + if (threadGroup.thread_rank() == 0) { + + currentIndex = cellsVector.extend(threadGroup.size()); + } + + currentIndex = threadGroup.shfl(currentIndex, 0) + threadGroup.thread_rank(); + + cellsVector.emplace(currentIndex, currentTracklet.firstClusterIndex, + nextTracklet.firstClusterIndex, nextTracklet.secondClusterIndex, currentTrackletIndex, + iNextLayerTracklet, normalizedPlaneVector, 1.0f / cellTrajectoryRadius); + ++trackletCellsNum; + } + } + } + } + } + + if (layerIndex > 0) { + + devStore.getCellsPerTrackletTable()[layerIndex - 1][currentTrackletIndex] = trackletCellsNum; + } + } + } +} + +__global__ void layerTrackletsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector trackletsVector) +{ + computeLayerTracklets(devStore, layerIndex, trackletsVector); +} + +__global__ void sortTrackletsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector tempTrackletArray) +{ + const int currentTrackletIndex{static_cast(blockDim.x * blockIdx.x + threadIdx.x)}; + + if (currentTrackletIndex < tempTrackletArray.size()) { + + const int firstClusterIndex = tempTrackletArray[currentTrackletIndex].firstClusterIndex; + const int offset = atomicAdd(&devStore.getTrackletsPerClusterTable()[layerIndex - 1][firstClusterIndex], + -1) - + 1; + const int startIndex = devStore.getTrackletsLookupTable()[layerIndex - 1][firstClusterIndex]; + + memcpy(&devStore.getTracklets()[layerIndex][startIndex + offset], + &tempTrackletArray[currentTrackletIndex], sizeof(Tracklet)); + } +} + +__global__ void layerCellsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector cellsVector) +{ + computeLayerCells(devStore, layerIndex, cellsVector); +} + +__global__ void sortCellsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector tempCellsArray) +{ + const int currentCellIndex = static_cast(blockDim.x * blockIdx.x + threadIdx.x); + + if (currentCellIndex < tempCellsArray.size()) { + + const int firstTrackletIndex = tempCellsArray[currentCellIndex].getFirstTrackletIndex(); + const int offset = atomicAdd(&devStore.getCellsPerTrackletTable()[layerIndex - 1][firstTrackletIndex], + -1) - + 1; + const int startIndex = devStore.getCellsLookupTable()[layerIndex - 1][firstTrackletIndex]; + + memcpy(&devStore.getCells()[layerIndex][startIndex + offset], &tempCellsArray[currentCellIndex], + sizeof(Cell)); + } +} + +} // namespace GPU + +TrackerTraitsEC0* createTrackerTraitsEC0NV() +{ + return new TrackerTraitsEC0NV; +} + +TrackerTraitsEC0NV::TrackerTraitsEC0NV() +{ + mPrimaryVertexContext = new PrimaryVertexContextNV; +} + +TrackerTraitsEC0NV::~TrackerTraitsEC0NV() +{ + delete mPrimaryVertexContext; +} + +void TrackerTraitsEC0NV::computeLayerTracklets() +{ + PrimaryVertexContextNV* primaryVertexContext = static_cast(mPrimaryVertexContext); + + cudaMemcpyToSymbol(GPU::kTrkPar, &mTrkParams, sizeof(TrackingParameters)); + std::array tempSize; + std::array trackletsNum; + std::array streamArray; + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + + tempSize[iLayer] = 0; + primaryVertexContext->getTempTrackletArray()[iLayer].reset( + static_cast(primaryVertexContext->getDeviceTracklets()[iLayer + 1].capacity())); + + cub::DeviceScan::ExclusiveSum(static_cast(NULL), tempSize[iLayer], + primaryVertexContext->getDeviceTrackletsPerClustersTable()[iLayer].get(), + primaryVertexContext->getDeviceTrackletsLookupTable()[iLayer].get(), + primaryVertexContext->getClusters()[iLayer + 1].size()); + + primaryVertexContext->getTempTableArray()[iLayer].reset(static_cast(tempSize[iLayer])); + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::TrackletsPerRoad; ++iLayer) { + + const GPU::DeviceProperties& deviceProperties = GPU::Context::getInstance().getDeviceProperties(); + const int clustersNum{static_cast(primaryVertexContext->getClusters()[iLayer].size())}; + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(clustersNum, 1, 192)}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, clustersNum)}; + + if (iLayer == 0) { + + GPU::layerTrackletsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getDeviceTracklets()[iLayer].getWeakCopy()); + + } else { + + GPU::layerTrackletsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getTempTrackletArray()[iLayer - 1].getWeakCopy()); + } + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + + trackletsNum[iLayer] = primaryVertexContext->getTempTrackletArray()[iLayer].getSizeFromDevice(); + if (trackletsNum[iLayer] == 0) { + continue; + } + primaryVertexContext->getDeviceTracklets()[iLayer + 1].resize(trackletsNum[iLayer]); + + cub::DeviceScan::ExclusiveSum(static_cast(primaryVertexContext->getTempTableArray()[iLayer].get()), tempSize[iLayer], + primaryVertexContext->getDeviceTrackletsPerClustersTable()[iLayer].get(), + primaryVertexContext->getDeviceTrackletsLookupTable()[iLayer].get(), + primaryVertexContext->getClusters()[iLayer + 1].size(), streamArray[iLayer + 1].get()); + + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsNum[iLayer])}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; + + GPU::sortTrackletsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer + 1, primaryVertexContext->getTempTrackletArray()[iLayer].getWeakCopy()); + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } +} + +void TrackerTraitsEC0NV::computeLayerCells() +{ + + PrimaryVertexContextNV* primaryVertexContext = static_cast(mPrimaryVertexContext); + std::array tempSize; + std::array trackletsNum; + std::array cellsNum; + std::array streamArray; + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad - 1; ++iLayer) { + + tempSize[iLayer] = 0; + trackletsNum[iLayer] = primaryVertexContext->getDeviceTracklets()[iLayer + 1].getSizeFromDevice(); + primaryVertexContext->getTempCellArray()[iLayer].reset( + static_cast(primaryVertexContext->getDeviceCells()[iLayer + 1].capacity())); + if (trackletsNum[iLayer] == 0) { + continue; + } + cub::DeviceScan::ExclusiveSum(static_cast(NULL), tempSize[iLayer], + primaryVertexContext->getDeviceCellsPerTrackletTable()[iLayer].get(), + primaryVertexContext->getDeviceCellsLookupTable()[iLayer].get(), trackletsNum[iLayer]); + + primaryVertexContext->getTempTableArray()[iLayer].reset(static_cast(tempSize[iLayer])); + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + const GPU::DeviceProperties& deviceProperties = GPU::Context::getInstance().getDeviceProperties(); + const int trackletsSize = primaryVertexContext->getDeviceTracklets()[iLayer].getSizeFromDevice(); + if (trackletsSize == 0) { + continue; + } + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsSize)}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsSize)}; + + if (iLayer == 0) { + + GPU::layerCellsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getDeviceCells()[iLayer].getWeakCopy()); + + } else { + + GPU::layerCellsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getTempCellArray()[iLayer - 1].getWeakCopy()); + } + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad - 1; ++iLayer) { + cellsNum[iLayer] = primaryVertexContext->getTempCellArray()[iLayer].getSizeFromDevice(); + if (cellsNum[iLayer] == 0) { + continue; + } + primaryVertexContext->getDeviceCells()[iLayer + 1].resize(cellsNum[iLayer]); + + cub::DeviceScan::ExclusiveSum(static_cast(primaryVertexContext->getTempTableArray()[iLayer].get()), tempSize[iLayer], + primaryVertexContext->getDeviceCellsPerTrackletTable()[iLayer].get(), + primaryVertexContext->getDeviceCellsLookupTable()[iLayer].get(), trackletsNum[iLayer], + streamArray[iLayer + 1].get()); + + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsNum[iLayer])}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; + + GPU::sortCellsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer + 1, primaryVertexContext->getTempCellArray()[iLayer].getWeakCopy()); + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + + int cellsSize = 0; + if (iLayer == 0) { + + cellsSize = primaryVertexContext->getDeviceCells()[iLayer].getSizeFromDevice(); + if (cellsSize == 0) + continue; + } else { + + cellsSize = cellsNum[iLayer - 1]; + if (cellsSize == 0) + continue; + primaryVertexContext->getDeviceCellsLookupTable()[iLayer - 1].copyIntoVector( + primaryVertexContext->getCellsLookupTable()[iLayer - 1], trackletsNum[iLayer - 1]); + } + + primaryVertexContext->getDeviceCells()[iLayer].copyIntoVector(primaryVertexContext->getCells()[iLayer], cellsSize); + } +} + +void TrackerTraitsEC0NV::refitTracks(const std::array, 7>& tf, std::vector& tracks) +{ + PrimaryVertexContextNV* pvctx = static_cast(mPrimaryVertexContext); + + std::array cells; + for (int iLayer = 0; iLayer < 5; iLayer++) { + cells[iLayer] = pvctx->getDeviceCells()[iLayer].get(); + } + std::array clusters; + for (int iLayer = 0; iLayer < 7; iLayer++) { + clusters[iLayer] = pvctx->getDeviceClusters()[iLayer].get(); + } + mChainRunEC0TrackFit(*mChain, mPrimaryVertexContext->getRoads(), clusters, cells, tf, tracks); +} +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/TrackerTraitsNV.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/TrackerTraitsNV.cu new file mode 100644 index 0000000000000..60edbb611f90c --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/TrackerTraitsNV.cu @@ -0,0 +1,497 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrackerTraitsEC0NV.cu +/// \brief +/// + +#include "EC0trackingCUDA/TrackerTraitsEC0NV.h" + +#include +#include +#include + +#include + +#include "cub/cub.cuh" + +#include "EC0tracking/Constants.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/IndexTableUtils.h" +#include "EC0tracking/MathUtils.h" +#include "EC0trackingCUDA/Context.h" +#include "EC0trackingCUDA/DeviceStoreNV.h" +#include "EC0trackingCUDA/PrimaryVertexContextNV.h" +#include "EC0trackingCUDA/Stream.h" +#include "EC0trackingCUDA/Vector.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +__constant__ TrackingParameters kTrkPar; + +__device__ void computeLayerTracklets(DeviceStoreNV& devStore, const int layerIndex, + Vector& trackletsVector) +{ + const int currentClusterIndex = static_cast(blockDim.x * blockIdx.x + threadIdx.x); + int clusterTrackletsNum = 0; + + if (currentClusterIndex < devStore.getClusters()[layerIndex].size()) { + + Vector nextLayerClusters{devStore.getClusters()[layerIndex + 1].getWeakCopy()}; + const Cluster currentCluster{devStore.getClusters()[layerIndex][currentClusterIndex]}; + + /*if (mUsedClustersTable[currentCluster.clusterId] != constants::ecl::UnusedIndex) { + + continue; + }*/ + + const float tanLambda{(currentCluster.zCoordinate - devStore.getPrimaryVertex().z) / currentCluster.rCoordinate}; + const float directionZIntersection{tanLambda * ((constants::ecl::LayersRCoordinate())[layerIndex + 1] - currentCluster.rCoordinate) + currentCluster.zCoordinate}; + + const int4 selectedBinsRect{TrackerTraitsEC0::getBinsRect(currentCluster, layerIndex, directionZIntersection, + kTrkPar.TrackletMaxDeltaZ[layerIndex], kTrkPar.TrackletMaxDeltaPhi)}; + + if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { + + const int nextLayerClustersNum{static_cast(nextLayerClusters.size())}; + int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; + + if (phiBinsNum < 0) { + + phiBinsNum += constants::index_table::PhiBins; + } + + for (int iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; + iPhiBin = ++iPhiBin == constants::index_table::PhiBins ? 0 : iPhiBin, iPhiCount++) { + + const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int firstRowClusterIndex = devStore.getIndexTables()[layerIndex][firstBinIndex]; + const int maxRowClusterIndex = devStore.getIndexTables()[layerIndex][{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}]; + + for (int iNextLayerCluster{firstRowClusterIndex}; + iNextLayerCluster <= maxRowClusterIndex && iNextLayerCluster < nextLayerClustersNum; ++iNextLayerCluster) { + + const Cluster& nextCluster{nextLayerClusters[iNextLayerCluster]}; + + const float deltaZ{gpu::GPUCommonMath::Abs( + tanLambda * (nextCluster.rCoordinate - currentCluster.rCoordinate) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; + const float deltaPhi{gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - nextCluster.phiCoordinate)}; + + if (deltaZ < kTrkPar.TrackletMaxDeltaZ[layerIndex] && (deltaPhi < kTrkPar.TrackletMaxDeltaPhi || gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.TrackletMaxDeltaPhi)) { + + cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); + int currentIndex{}; + + if (threadGroup.thread_rank() == 0) { + + currentIndex = trackletsVector.extend(threadGroup.size()); + } + + currentIndex = threadGroup.shfl(currentIndex, 0) + threadGroup.thread_rank(); + + trackletsVector.emplace(currentIndex, currentClusterIndex, iNextLayerCluster, currentCluster, nextCluster); + ++clusterTrackletsNum; + } + } + } + + if (layerIndex > 0) { + + devStore.getTrackletsPerClusterTable()[layerIndex - 1][currentClusterIndex] = clusterTrackletsNum; + } + } + } +} + +__device__ void computeLayerCells(DeviceStoreNV& devStore, const int layerIndex, + Vector& cellsVector) +{ + const int currentTrackletIndex = static_cast(blockDim.x * blockIdx.x + threadIdx.x); + const float3& primaryVertex = devStore.getPrimaryVertex(); + int trackletCellsNum = 0; + + if (currentTrackletIndex < devStore.getTracklets()[layerIndex].size()) { + + const Tracklet& currentTracklet{devStore.getTracklets()[layerIndex][currentTrackletIndex]}; + const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; + const int nextLayerFirstTrackletIndex{ + devStore.getTrackletsLookupTable()[layerIndex][nextLayerClusterIndex]}; + const int nextLayerTrackletsNum{static_cast(devStore.getTracklets()[layerIndex + 1].size())}; + + if (devStore.getTracklets()[layerIndex + 1][nextLayerFirstTrackletIndex].firstClusterIndex == nextLayerClusterIndex) { + + const Cluster& firstCellCluster{ + devStore.getClusters()[layerIndex][currentTracklet.firstClusterIndex]}; + const Cluster& secondCellCluster{ + devStore.getClusters()[layerIndex + 1][currentTracklet.secondClusterIndex]}; + const float firstCellClusterQuadraticRCoordinate{firstCellCluster.rCoordinate * firstCellCluster.rCoordinate}; + const float secondCellClusterQuadraticRCoordinate{secondCellCluster.rCoordinate * secondCellCluster.rCoordinate}; + const float3 firstDeltaVector{secondCellCluster.xCoordinate - firstCellCluster.xCoordinate, + secondCellCluster.yCoordinate - firstCellCluster.yCoordinate, secondCellClusterQuadraticRCoordinate - firstCellClusterQuadraticRCoordinate}; + + for (int iNextLayerTracklet{nextLayerFirstTrackletIndex}; + iNextLayerTracklet < nextLayerTrackletsNum && devStore.getTracklets()[layerIndex + 1][iNextLayerTracklet].firstClusterIndex == nextLayerClusterIndex; ++iNextLayerTracklet) { + + const Tracklet& nextTracklet{devStore.getTracklets()[layerIndex + 1][iNextLayerTracklet]}; + const float deltaTanLambda{gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + const float deltaPhi{gpu::GPUCommonMath::Abs(currentTracklet.phiCoordinate - nextTracklet.phiCoordinate)}; + + if (deltaTanLambda < kTrkPar.CellMaxDeltaTanLambda && (deltaPhi < kTrkPar.CellMaxDeltaPhi || gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < kTrkPar.CellMaxDeltaPhi)) { + + const float averageTanLambda{0.5f * (currentTracklet.tanLambda + nextTracklet.tanLambda)}; + const float directionZIntersection{-averageTanLambda * firstCellCluster.rCoordinate + firstCellCluster.zCoordinate}; + const float deltaZ{gpu::GPUCommonMath::Abs(directionZIntersection - primaryVertex.z)}; + + if (deltaZ < kTrkPar.CellMaxDeltaZ[layerIndex]) { + + const Cluster& thirdCellCluster{ + devStore.getClusters()[layerIndex + 2][nextTracklet.secondClusterIndex]}; + + const float thirdCellClusterQuadraticRCoordinate{thirdCellCluster.rCoordinate * thirdCellCluster.rCoordinate}; + + const float3 secondDeltaVector{thirdCellCluster.xCoordinate - firstCellCluster.xCoordinate, + thirdCellCluster.yCoordinate - firstCellCluster.yCoordinate, thirdCellClusterQuadraticRCoordinate - firstCellClusterQuadraticRCoordinate}; + + float3 cellPlaneNormalVector{math_utils::crossProduct(firstDeltaVector, secondDeltaVector)}; + + const float vectorNorm{gpu::GPUCommonMath::Sqrt( + cellPlaneNormalVector.x * cellPlaneNormalVector.x + cellPlaneNormalVector.y * cellPlaneNormalVector.y + cellPlaneNormalVector.z * cellPlaneNormalVector.z)}; + + if (!(vectorNorm < constants::math::FloatMinThreshold || gpu::GPUCommonMath::Abs(cellPlaneNormalVector.z) < constants::math::FloatMinThreshold)) { + + const float inverseVectorNorm{1.0f / vectorNorm}; + const float3 normalizedPlaneVector{cellPlaneNormalVector.x * inverseVectorNorm, cellPlaneNormalVector.y * inverseVectorNorm, cellPlaneNormalVector.z * inverseVectorNorm}; + const float planeDistance{-normalizedPlaneVector.x * (secondCellCluster.xCoordinate - primaryVertex.x) - (normalizedPlaneVector.y * secondCellCluster.yCoordinate - primaryVertex.y) - normalizedPlaneVector.z * secondCellClusterQuadraticRCoordinate}; + const float normalizedPlaneVectorQuadraticZCoordinate{normalizedPlaneVector.z * normalizedPlaneVector.z}; + const float cellTrajectoryRadius{gpu::GPUCommonMath::Sqrt( + (1.0f - normalizedPlaneVectorQuadraticZCoordinate - 4.0f * planeDistance * normalizedPlaneVector.z) / (4.0f * normalizedPlaneVectorQuadraticZCoordinate))}; + const float2 circleCenter{-0.5f * normalizedPlaneVector.x / normalizedPlaneVector.z, -0.5f * normalizedPlaneVector.y / normalizedPlaneVector.z}; + const float distanceOfClosestApproach{gpu::GPUCommonMath::Abs( + cellTrajectoryRadius - gpu::GPUCommonMath::Sqrt(circleCenter.x * circleCenter.x + circleCenter.y * circleCenter.y))}; + + if (distanceOfClosestApproach <= kTrkPar.CellMaxDCA[layerIndex]) { + + cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); + int currentIndex{}; + + if (threadGroup.thread_rank() == 0) { + + currentIndex = cellsVector.extend(threadGroup.size()); + } + + currentIndex = threadGroup.shfl(currentIndex, 0) + threadGroup.thread_rank(); + + cellsVector.emplace(currentIndex, currentTracklet.firstClusterIndex, + nextTracklet.firstClusterIndex, nextTracklet.secondClusterIndex, currentTrackletIndex, + iNextLayerTracklet, normalizedPlaneVector, 1.0f / cellTrajectoryRadius); + ++trackletCellsNum; + } + } + } + } + } + + if (layerIndex > 0) { + + devStore.getCellsPerTrackletTable()[layerIndex - 1][currentTrackletIndex] = trackletCellsNum; + } + } + } +} + +__global__ void layerTrackletsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector trackletsVector) +{ + computeLayerTracklets(devStore, layerIndex, trackletsVector); +} + +__global__ void sortTrackletsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector tempTrackletArray) +{ + const int currentTrackletIndex{static_cast(blockDim.x * blockIdx.x + threadIdx.x)}; + + if (currentTrackletIndex < tempTrackletArray.size()) { + + const int firstClusterIndex = tempTrackletArray[currentTrackletIndex].firstClusterIndex; + const int offset = atomicAdd(&devStore.getTrackletsPerClusterTable()[layerIndex - 1][firstClusterIndex], + -1) - + 1; + const int startIndex = devStore.getTrackletsLookupTable()[layerIndex - 1][firstClusterIndex]; + + memcpy(&devStore.getTracklets()[layerIndex][startIndex + offset], + &tempTrackletArray[currentTrackletIndex], sizeof(Tracklet)); + } +} + +__global__ void layerCellsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector cellsVector) +{ + computeLayerCells(devStore, layerIndex, cellsVector); +} + +__global__ void sortCellsKernel(DeviceStoreNV& devStore, const int layerIndex, + Vector tempCellsArray) +{ + const int currentCellIndex = static_cast(blockDim.x * blockIdx.x + threadIdx.x); + + if (currentCellIndex < tempCellsArray.size()) { + + const int firstTrackletIndex = tempCellsArray[currentCellIndex].getFirstTrackletIndex(); + const int offset = atomicAdd(&devStore.getCellsPerTrackletTable()[layerIndex - 1][firstTrackletIndex], + -1) - + 1; + const int startIndex = devStore.getCellsLookupTable()[layerIndex - 1][firstTrackletIndex]; + + memcpy(&devStore.getCells()[layerIndex][startIndex + offset], &tempCellsArray[currentCellIndex], + sizeof(Cell)); + } +} + +} // namespace GPU + +TrackerTraitsEC0* createTrackerTraitsEC0NV() +{ + return new TrackerTraitsEC0NV; +} + +TrackerTraitsEC0NV::TrackerTraitsEC0NV() +{ + mPrimaryVertexContext = new PrimaryVertexContextNV; +} + +TrackerTraitsEC0NV::~TrackerTraitsEC0NV() +{ + delete mPrimaryVertexContext; +} + +void TrackerTraitsEC0NV::computeLayerTracklets() +{ + PrimaryVertexContextNV* primaryVertexContext = static_cast(mPrimaryVertexContext); + + cudaMemcpyToSymbol(GPU::kTrkPar, &mTrkParams, sizeof(TrackingParameters)); + std::array tempSize; + std::array trackletsNum; + std::array streamArray; + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + + tempSize[iLayer] = 0; + primaryVertexContext->getTempTrackletArray()[iLayer].reset( + static_cast(primaryVertexContext->getDeviceTracklets()[iLayer + 1].capacity())); + + cub::DeviceScan::ExclusiveSum(static_cast(NULL), tempSize[iLayer], + primaryVertexContext->getDeviceTrackletsPerClustersTable()[iLayer].get(), + primaryVertexContext->getDeviceTrackletsLookupTable()[iLayer].get(), + primaryVertexContext->getClusters()[iLayer + 1].size()); + + primaryVertexContext->getTempTableArray()[iLayer].reset(static_cast(tempSize[iLayer])); + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::TrackletsPerRoad; ++iLayer) { + + const GPU::DeviceProperties& deviceProperties = GPU::Context::getInstance().getDeviceProperties(); + const int clustersNum{static_cast(primaryVertexContext->getClusters()[iLayer].size())}; + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(clustersNum, 1, 192)}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, clustersNum)}; + + if (iLayer == 0) { + + GPU::layerTrackletsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getDeviceTracklets()[iLayer].getWeakCopy()); + + } else { + + GPU::layerTrackletsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getTempTrackletArray()[iLayer - 1].getWeakCopy()); + } + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + + trackletsNum[iLayer] = primaryVertexContext->getTempTrackletArray()[iLayer].getSizeFromDevice(); + if (trackletsNum[iLayer] == 0) { + continue; + } + primaryVertexContext->getDeviceTracklets()[iLayer + 1].resize(trackletsNum[iLayer]); + + cub::DeviceScan::ExclusiveSum(static_cast(primaryVertexContext->getTempTableArray()[iLayer].get()), tempSize[iLayer], + primaryVertexContext->getDeviceTrackletsPerClustersTable()[iLayer].get(), + primaryVertexContext->getDeviceTrackletsLookupTable()[iLayer].get(), + primaryVertexContext->getClusters()[iLayer + 1].size(), streamArray[iLayer + 1].get()); + + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsNum[iLayer])}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; + + GPU::sortTrackletsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer + 1, primaryVertexContext->getTempTrackletArray()[iLayer].getWeakCopy()); + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } +} + +void TrackerTraitsEC0NV::computeLayerCells() +{ + + PrimaryVertexContextNV* primaryVertexContext = static_cast(mPrimaryVertexContext); + std::array tempSize; + std::array trackletsNum; + std::array cellsNum; + std::array streamArray; + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad - 1; ++iLayer) { + + tempSize[iLayer] = 0; + trackletsNum[iLayer] = primaryVertexContext->getDeviceTracklets()[iLayer + 1].getSizeFromDevice(); + primaryVertexContext->getTempCellArray()[iLayer].reset( + static_cast(primaryVertexContext->getDeviceCells()[iLayer + 1].capacity())); + if (trackletsNum[iLayer] == 0) { + continue; + } + cub::DeviceScan::ExclusiveSum(static_cast(NULL), tempSize[iLayer], + primaryVertexContext->getDeviceCellsPerTrackletTable()[iLayer].get(), + primaryVertexContext->getDeviceCellsLookupTable()[iLayer].get(), trackletsNum[iLayer]); + + primaryVertexContext->getTempTableArray()[iLayer].reset(static_cast(tempSize[iLayer])); + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + const GPU::DeviceProperties& deviceProperties = GPU::Context::getInstance().getDeviceProperties(); + const int trackletsSize = primaryVertexContext->getDeviceTracklets()[iLayer].getSizeFromDevice(); + if (trackletsSize == 0) { + continue; + } + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsSize)}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsSize)}; + + if (iLayer == 0) { + + GPU::layerCellsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getDeviceCells()[iLayer].getWeakCopy()); + + } else { + + GPU::layerCellsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer, primaryVertexContext->getTempCellArray()[iLayer - 1].getWeakCopy()); + } + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad - 1; ++iLayer) { + cellsNum[iLayer] = primaryVertexContext->getTempCellArray()[iLayer].getSizeFromDevice(); + if (cellsNum[iLayer] == 0) { + continue; + } + primaryVertexContext->getDeviceCells()[iLayer + 1].resize(cellsNum[iLayer]); + + cub::DeviceScan::ExclusiveSum(static_cast(primaryVertexContext->getTempTableArray()[iLayer].get()), tempSize[iLayer], + primaryVertexContext->getDeviceCellsPerTrackletTable()[iLayer].get(), + primaryVertexContext->getDeviceCellsLookupTable()[iLayer].get(), trackletsNum[iLayer], + streamArray[iLayer + 1].get()); + + dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(trackletsNum[iLayer])}; + dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, trackletsNum[iLayer])}; + + GPU::sortCellsKernel<<>>(primaryVertexContext->getDeviceContext(), + iLayer + 1, primaryVertexContext->getTempCellArray()[iLayer].getWeakCopy()); + + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" + << std::endl; + + throw std::runtime_error{errorString.str()}; + } + } + + cudaDeviceSynchronize(); + + for (int iLayer{0}; iLayer < constants::ecl::CellsPerRoad; ++iLayer) { + + int cellsSize = 0; + if (iLayer == 0) { + + cellsSize = primaryVertexContext->getDeviceCells()[iLayer].getSizeFromDevice(); + if (cellsSize == 0) + continue; + } else { + + cellsSize = cellsNum[iLayer - 1]; + if (cellsSize == 0) + continue; + primaryVertexContext->getDeviceCellsLookupTable()[iLayer - 1].copyIntoVector( + primaryVertexContext->getCellsLookupTable()[iLayer - 1], trackletsNum[iLayer - 1]); + } + + primaryVertexContext->getDeviceCells()[iLayer].copyIntoVector(primaryVertexContext->getCells()[iLayer], cellsSize); + } +} + +void TrackerTraitsEC0NV::refitTracks(const std::array, 7>& tf, std::vector& tracks) +{ + PrimaryVertexContextNV* pvctx = static_cast(mPrimaryVertexContext); + + std::array cells; + for (int iLayer = 0; iLayer < 5; iLayer++) { + cells[iLayer] = pvctx->getDeviceCells()[iLayer].get(); + } + std::array clusters; + for (int iLayer = 0; iLayer < 7; iLayer++) { + clusters[iLayer] = pvctx->getDeviceClusters()[iLayer].get(); + } + mChainRunITSTrackFit(*mChain, mPrimaryVertexContext->getRoads(), clusters, cells, tf, tracks); +} +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Utils.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Utils.cu new file mode 100644 index 0000000000000..cc3a62449b1b7 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/Utils.cu @@ -0,0 +1,193 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Utils.cu +/// \brief +/// + +#include "EC0trackingCUDA/Utils.h" + +#include +#include + +#include +#include + +#include "EC0trackingCUDA/Context.h" + +#include + +namespace +{ + +int roundUp(const int numToRound, const int multiple) +{ + if (multiple == 0) { + + return numToRound; + } + + int remainder{numToRound % multiple}; + + if (remainder == 0) { + + return numToRound; + } + + return numToRound + multiple - remainder; +} + +int findNearestDivisor(const int numToRound, const int divisor) +{ + + if (numToRound > divisor) { + + return divisor; + } + + int result = numToRound; + + while (divisor % result != 0) { + + ++result; + } + + return result; +} + +} // namespace + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +void Utils::Host::checkCUDAError(const cudaError_t error, const char* file, const int line) +{ + if (error != cudaSuccess) { + + std::ostringstream errorString{}; + + errorString << file << ":" << line << " CUDA API returned error [" << cudaGetErrorString(error) << "] (code " + << error << ")" << std::endl; + + throw std::runtime_error{errorString.str()}; + } +} + +dim3 Utils::Host::getBlockSize(const int colsNum) +{ + return getBlockSize(colsNum, 1); +} + +dim3 Utils::Host::getBlockSize(const int colsNum, const int rowsNum) +{ + const DeviceProperties& deviceProperties = Context::getInstance().getDeviceProperties(); + return getBlockSize(colsNum, rowsNum, deviceProperties.cudaCores / deviceProperties.maxBlocksPerSM); +} + +dim3 Utils::Host::getBlockSize(const int colsNum, const int rowsNum, const int maxThreadsPerBlock) +{ + const DeviceProperties& deviceProperties = Context::getInstance().getDeviceProperties(); + int xThreads = max(min(colsNum, deviceProperties.maxThreadsDim.x), 1); + int yThreads = max(min(rowsNum, deviceProperties.maxThreadsDim.y), 1); + const int totalThreads = roundUp(min(xThreads * yThreads, maxThreadsPerBlock), + deviceProperties.warpSize); + + if (xThreads > yThreads) { + + xThreads = findNearestDivisor(xThreads, totalThreads); + yThreads = totalThreads / xThreads; + + } else { + + yThreads = findNearestDivisor(yThreads, totalThreads); + xThreads = totalThreads / yThreads; + } + + return dim3{static_cast(xThreads), static_cast(yThreads)}; +} + +dim3 Utils::Host::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum) +{ + + return getBlocksGrid(threadsPerBlock, rowsNum, 1); +} + +dim3 Utils::Host::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum, const int colsNum) +{ + + return dim3{1 + (rowsNum - 1) / threadsPerBlock.x, 1 + (colsNum - 1) / threadsPerBlock.y}; +} + +void Utils::Host::gpuMalloc(void** p, const int size) +{ + checkCUDAError(cudaMalloc(p, size), __FILE__, __LINE__); +} + +void Utils::Host::gpuFree(void* p) +{ + checkCUDAError(cudaFree(p), __FILE__, __LINE__); +} + +void Utils::Host::gpuMemset(void* p, int value, int size) +{ + checkCUDAError(cudaMemset(p, value, size), __FILE__, __LINE__); +} + +void Utils::Host::gpuMemcpyHostToDevice(void* dst, const void* src, int size) +{ + checkCUDAError(cudaMemcpy(dst, src, size, cudaMemcpyHostToDevice), __FILE__, __LINE__); +} + +void Utils::Host::gpuMemcpyHostToDeviceAsync(void* dst, const void* src, int size, Stream& stream) +{ + checkCUDAError(cudaMemcpyAsync(dst, src, size, cudaMemcpyHostToDevice, stream.get()), __FILE__, __LINE__); +} + +void Utils::Host::gpuMemcpyDeviceToHost(void* dst, const void* src, int size) +{ + checkCUDAError(cudaMemcpy(dst, src, size, cudaMemcpyDeviceToHost), __FILE__, __LINE__); +} + +// void Utils::Host::gpuStartProfiler() +// { +// checkCUDAError(cudaProfilerStart(), __FILE__, __LINE__); +// } + +// void Utils::Host::gpuStopProfiler() +// { +// checkCUDAError(cudaProfilerStop(), __FILE__, __LINE__); +// } + +GPUd() int Utils::Device::getLaneIndex() +{ + uint32_t laneIndex; + asm volatile("mov.u32 %0, %%laneid;" + : "=r"(laneIndex)); + return static_cast(laneIndex); +} + +GPUd() int Utils::Device::shareToWarp(const int value, const int laneIndex) +{ + cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); + return threadGroup.shfl(value, laneIndex); +} + +GPUd() int Utils::Device::gpuAtomicAdd(int* p, const int incrementSize) +{ + return atomicAdd(p, incrementSize); +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/VertexerTraitsEC0GPU.cu b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/VertexerTraitsEC0GPU.cu new file mode 100644 index 0000000000000..b0511c1c587ca --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/cuda/src/VertexerTraitsEC0GPU.cu @@ -0,0 +1,505 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 VertexerTraitsEC0GPU.cu.cu +/// \brief +/// \author matteo.concas@cern.ch + +#include +#include +#include +#include +#include + +#include "EC0tracking/MathUtils.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/ClusterLines.h" +#include "EC0tracking/Tracklet.h" + +#include "EC0trackingCUDA/Utils.h" +#include "EC0trackingCUDA/ClusterLinesGPU.h" +#include "EC0trackingCUDA/Context.h" +#include "EC0trackingCUDA/Stream.h" +#include "EC0trackingCUDA/VertexerTraitsEC0GPU.h" + +namespace o2 +{ +namespace ecl +{ + +using constants::ecl::LayersRCoordinate; +using constants::ecl::LayersZCoordinate; +using constants::ecl::VertexerHistogramVolume; +using constants::index_table::PhiBins; +using constants::index_table::ZBins; +using constants::math::TwoPi; +using index_table_utils::getPhiBinIndex; +using index_table_utils::getZBinIndex; +using math_utils::getNormalizedPhiCoordinate; + +GPUh() void gpuThrowOnError() +{ + cudaError_t error = cudaGetLastError(); + + if (error != cudaSuccess) { + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" << std::endl; + throw std::runtime_error{errorString.str()}; + } +} + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +VertexerTraitsEC0GPU::VertexerTraitsEC0GPU() +{ + setIsGPU(true); + std::cout << "[DEBUG] Creating file: dbg_EC0VertexerGPU.root" << std::endl; + mDebugger = new StandaloneDebugger::StandaloneDebugger("dbg_EC0VertexerGPU.root"); +} + +VertexerTraitsEC0GPU::~VertexerTraitsEC0GPU() +{ + delete mDebugger; +} +#else +VertexerTraitsEC0GPU::VertexerTraitsEC0GPU() +{ + setIsGPU(true); +} + +#endif + +void VertexerTraitsEC0GPU::initialise(ROframe* event) +{ + reset(); + arrangeClusters(event); + mStoreVertexerGPUPtr = mStoreVertexerGPU.initialise(mClusters, mIndexTables); +} + +namespace GPU +{ + +template +GPUd() void printOnThread(const int tId, const char* str, Args... args) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == tId) { + printf(str, args...); + } +} + +GPUd() void printVectorOnThread(const char* name, Vector& vector, size_t size, const int tId = 0) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == tId) { + printf("vector %s :", name); + for (int i{0}; i < size; ++i) { + printf("%d ", vector[i]); + } + printf("\n"); + } +} + +GPUg() void printVectorKernel(DeviceStoreVertexerGPU& store, const int threadId) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == threadId) { + for (int i{0}; i < store.getConfig().histConf.nBinsXYZ[0] - 1; ++i) { + printf("%d: %d\n", i, store.getHistogramXYZ()[0].get()[i]); + } + printf("\n"); + for (int i{0}; i < store.getConfig().histConf.nBinsXYZ[1] - 1; ++i) { + printf("%d: %d\n", i, store.getHistogramXYZ()[1].get()[i]); + } + printf("\n"); + for (int i{0}; i < store.getConfig().histConf.nBinsXYZ[2] - 1; ++i) { + printf("%d: %d\n", i, store.getHistogramXYZ()[2].get()[i]); + } + printf("\n"); + } +} + +GPUg() void dumpMaximaKernel(DeviceStoreVertexerGPU& store, const int threadId) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == threadId) { + printf("XmaxBin: %d at index: %d | YmaxBin: %d at index: %d | ZmaxBin: %d at index: %d\n", + store.getTmpVertexPositionBins()[0].value, store.getTmpVertexPositionBins()[0].key, + store.getTmpVertexPositionBins()[1].value, store.getTmpVertexPositionBins()[1].key, + store.getTmpVertexPositionBins()[2].value, store.getTmpVertexPositionBins()[2].key); + } +} + +GPUg() void trackleterKernel( + DeviceStoreVertexerGPU& store, + const TrackletingLayerOrder layerOrder, + const float phiCut) +{ + const size_t nClustersMiddleLayer = store.getClusters()[1].size(); + for (size_t currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < nClustersMiddleLayer; currentClusterIndex += blockDim.x * gridDim.x) { + if (currentClusterIndex < nClustersMiddleLayer) { + int storedTracklets{0}; + const size_t stride{currentClusterIndex * store.getConfig().maxTrackletsPerCluster}; + const Cluster& currentCluster = store.getClusters()[1][currentClusterIndex]; // assign-constructor may be a problem, check + const VertexerLayerName adjacentLayerIndex{layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer ? VertexerLayerName::innermostLayer : VertexerLayerName::outerLayer}; + const int4 selectedBinsRect{VertexerTraitsEC0::getBinsRect(currentCluster, static_cast(adjacentLayerIndex), 0.f, 50.f, phiCut / 2)}; + if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { + int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; + if (phiBinsNum < 0) { + phiBinsNum += PhiBins; + } + const size_t nClustersAdjacentLayer = store.getClusters()[static_cast(adjacentLayerIndex)].size(); + for (size_t iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) { + const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int firstRowClusterIndex{store.getIndexTable(adjacentLayerIndex)[firstBinIndex]}; + const int maxRowClusterIndex{store.getIndexTable(adjacentLayerIndex)[firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1]}; + for (size_t iAdjacentCluster{firstRowClusterIndex}; iAdjacentCluster < maxRowClusterIndex && iAdjacentCluster < nClustersAdjacentLayer; ++iAdjacentCluster) { + const Cluster& adjacentCluster = store.getClusters()[static_cast(adjacentLayerIndex)][iAdjacentCluster]; // assign-constructor may be a problem, check + if (gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - adjacentCluster.phiCoordinate) < phiCut) { + if (storedTracklets < store.getConfig().maxTrackletsPerCluster) { + if (layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer) { + store.getDuplets01().emplace(stride + storedTracklets, iAdjacentCluster, currentClusterIndex, adjacentCluster, currentCluster); + } else { + store.getDuplets12().emplace(stride + storedTracklets, currentClusterIndex, iAdjacentCluster, currentCluster, adjacentCluster); + } + ++storedTracklets; + } else { + printf("debug: leaving tracklet behind\n"); + } + } + } + } + } + store.getNFoundTracklets(layerOrder).emplace(currentClusterIndex, storedTracklets); + } + } +} + +GPUg() void trackletSelectionKernel( + DeviceStoreVertexerGPU& store, + const unsigned char isInitRun = false, + const float tanLambdaCut = 0.025f, + const float phiCut = 0.002f) +{ + const size_t nClustersMiddleLayer = store.getClusters()[1].size(); + for (size_t currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < nClustersMiddleLayer; currentClusterIndex += blockDim.x * gridDim.x) { + const int stride{static_cast(currentClusterIndex * store.getConfig().maxTrackletsPerCluster)}; + int validTracklets{0}; + for (int iTracklet12{0}; iTracklet12 < store.getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer)[currentClusterIndex]; ++iTracklet12) { + for (int iTracklet01{0}; iTracklet01 < store.getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer)[currentClusterIndex] && validTracklets < store.getConfig().maxTrackletsPerCluster; ++iTracklet01) { + const float deltaTanLambda{gpu::GPUCommonMath::Abs(store.getDuplets01()[stride + iTracklet01].tanLambda - store.getDuplets12()[stride + iTracklet12].tanLambda)}; + const float deltaPhi{gpu::GPUCommonMath::Abs(store.getDuplets01()[stride + iTracklet01].phiCoordinate - store.getDuplets12()[stride + iTracklet12].phiCoordinate)}; + if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != store.getConfig().maxTrackletsPerCluster) { + assert(store.getDuplets01()[stride + iTracklet01].secondClusterIndex == store.getDuplets12()[stride + iTracklet12].firstClusterIndex); + if (!isInitRun) { + store.getLines().emplace(store.getNExclusiveFoundLines()[currentClusterIndex] + validTracklets, store.getDuplets01()[stride + iTracklet01], store.getClusters()[0].get(), store.getClusters()[1].get()); +#ifdef _ALLOW_DEBUG_TREES_ITS_ + store.getDupletIndices()[0].emplace(store.getNExclusiveFoundLines()[currentClusterIndex] + validTracklets, stride + iTracklet01); + store.getDupletIndices()[1].emplace(store.getNExclusiveFoundLines()[currentClusterIndex] + validTracklets, stride + iTracklet12); +#endif + } + ++validTracklets; + } + } + } + if (isInitRun) { + store.getNFoundLines().emplace(currentClusterIndex, validTracklets); + if (validTracklets >= store.getConfig().maxTrackletsPerCluster) { + printf("Warning: not enough space for tracklet selection, some lines will be left behind\n"); + } + } + } + if (blockIdx.x * blockDim.x + threadIdx.x == 0) { + // first thread I want to write an empty line after last found, as debug flag. Might delete later + store.getLines().emplace(store.getNExclusiveFoundLines()[store.getClusters()[1].size() - 1] + store.getNFoundLines()[store.getClusters()[1].size() - 1]); + } +} + +GPUg() void computeCentroidsKernel(DeviceStoreVertexerGPU& store, + const float pairCut) +{ + const int nLines = store.getNExclusiveFoundLines()[store.getClusters()[1].size() - 1] + store.getNFoundLines()[store.getClusters()[1].size() - 1]; + const int maxIterations{nLines * (nLines - 1) / 2}; + for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < maxIterations; currentThreadIndex += blockDim.x * gridDim.x) { + int iFirstLine = currentThreadIndex / nLines; + int iSecondLine = currentThreadIndex % nLines; + if (iSecondLine <= iFirstLine) { + iFirstLine = nLines - iFirstLine - 2; + iSecondLine = nLines - iSecondLine - 1; + } + if (Line::getDCA(store.getLines()[iFirstLine], store.getLines()[iSecondLine]) < pairCut) { + ClusterLinesGPU cluster{store.getLines()[iFirstLine], store.getLines()[iSecondLine]}; + if (cluster.getVertex()[0] * cluster.getVertex()[0] + cluster.getVertex()[1] * cluster.getVertex()[1] < 1.98f * 1.98f) { + // printOnThread(0, "xCentr: %f, yCentr: %f \n", cluster.getVertex()[0], cluster.getVertex()[1]); + store.getXYCentroids().emplace(2 * currentThreadIndex, cluster.getVertex()[0]); + store.getXYCentroids().emplace(2 * currentThreadIndex + 1, cluster.getVertex()[1]); + } else { + // writing some data anyway outside the histogram, they will not be put in the histogram, by construction. + store.getXYCentroids().emplace(2 * currentThreadIndex, 2 * store.getConfig().histConf.lowHistBoundariesXYZ[0]); + store.getXYCentroids().emplace(2 * currentThreadIndex + 1, 2 * store.getConfig().histConf.lowHistBoundariesXYZ[1]); + } + } else { + // writing some data anyway outside the histogram, they will not be put in the histogram, by construction. + store.getXYCentroids().emplace(2 * currentThreadIndex, 2 * store.getConfig().histConf.lowHistBoundariesXYZ[0]); + store.getXYCentroids().emplace(2 * currentThreadIndex + 1, 2 * store.getConfig().histConf.lowHistBoundariesXYZ[1]); + } + } +} + +GPUg() void computeZCentroidsKernel(DeviceStoreVertexerGPU& store, + const float pairCut, const int binOpeningX, const int binOpeningY) +{ + const int nLines = store.getNExclusiveFoundLines()[store.getClusters()[1].size() - 1] + store.getNFoundLines()[store.getClusters()[1].size() - 1]; + for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < nLines; currentThreadIndex += blockDim.x * gridDim.x) { + if (store.getTmpVertexPositionBins()[0].value || store.getTmpVertexPositionBins()[1].value) { + float tmpX{store.getConfig().histConf.lowHistBoundariesXYZ[0] + store.getTmpVertexPositionBins()[0].key * store.getConfig().histConf.binSizeHistX + store.getConfig().histConf.binSizeHistX / 2}; + int sumWX{store.getTmpVertexPositionBins()[0].value}; + float wX{tmpX * store.getTmpVertexPositionBins()[0].value}; + for (int iBin{gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[0].key - binOpeningX)}; iBin < gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[0].key + binOpeningX + 1, store.getConfig().histConf.nBinsXYZ[0] - 1); ++iBin) { + if (iBin != store.getTmpVertexPositionBins()[0].key) { + wX += (store.getConfig().histConf.lowHistBoundariesXYZ[0] + iBin * store.getConfig().histConf.binSizeHistX + store.getConfig().histConf.binSizeHistX / 2) * store.getHistogramXYZ()[0].get()[iBin]; + sumWX += store.getHistogramXYZ()[0].get()[iBin]; + } + } + float tmpY{store.getConfig().histConf.lowHistBoundariesXYZ[1] + store.getTmpVertexPositionBins()[1].key * store.getConfig().histConf.binSizeHistY + store.getConfig().histConf.binSizeHistY / 2}; + int sumWY{store.getTmpVertexPositionBins()[1].value}; + float wY{tmpY * store.getTmpVertexPositionBins()[1].value}; + for (int iBin{gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[1].key - binOpeningY)}; iBin < gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[1].key + binOpeningY + 1, store.getConfig().histConf.nBinsXYZ[1] - 1); ++iBin) { + if (iBin != store.getTmpVertexPositionBins()[1].key) { + wY += (store.getConfig().histConf.lowHistBoundariesXYZ[1] + iBin * store.getConfig().histConf.binSizeHistY + store.getConfig().histConf.binSizeHistY / 2) * store.getHistogramXYZ()[1].get()[iBin]; + sumWY += store.getHistogramXYZ()[1].get()[iBin]; + } + } + store.getBeamPosition().emplace(0, wX / sumWX); + store.getBeamPosition().emplace(1, wY / sumWY); + float fakeBeamPoint1[3] = {store.getBeamPosition()[0], store.getBeamPosition()[1], -1}; // get two points laying at different z, to create line object + float fakeBeamPoint2[3] = {store.getBeamPosition()[0], store.getBeamPosition()[1], 1}; + Line pseudoBeam = Line::Line(fakeBeamPoint1, fakeBeamPoint2); + if (Line::getDCA(store.getLines()[currentThreadIndex], pseudoBeam) < pairCut) { + ClusterLinesGPU cluster{store.getLines()[currentThreadIndex], pseudoBeam}; + store.getZCentroids().emplace(currentThreadIndex, cluster.getVertex()[2]); + } else { + store.getZCentroids().emplace(currentThreadIndex, 2 * store.getConfig().histConf.lowHistBoundariesXYZ[2]); + } + } + } +} + +GPUg() void computeVertexKernel(DeviceStoreVertexerGPU& store, const int vertIndex, const int minContributors, const int binOpeningZ) +{ + for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < binOpeningZ; currentThreadIndex += blockDim.x * gridDim.x) { + if (currentThreadIndex == 0) { + if (store.getTmpVertexPositionBins()[2].value > 1 && (store.getTmpVertexPositionBins()[0].value || store.getTmpVertexPositionBins()[1].value)) { + float z{store.getConfig().histConf.lowHistBoundariesXYZ[2] + store.getTmpVertexPositionBins()[2].key * store.getConfig().histConf.binSizeHistZ + store.getConfig().histConf.binSizeHistZ / 2}; + float ex{0.f}; + float ey{0.f}; + float ez{0.f}; + int sumWZ{store.getTmpVertexPositionBins()[2].value}; + float wZ{z * store.getTmpVertexPositionBins()[2].value}; + for (int iBin{gpu::GPUCommonMath::Max(0, store.getTmpVertexPositionBins()[2].key - binOpeningZ)}; iBin < gpu::GPUCommonMath::Min(store.getTmpVertexPositionBins()[2].key + binOpeningZ + 1, store.getConfig().histConf.nBinsXYZ[2] - 1); ++iBin) { + if (iBin != store.getTmpVertexPositionBins()[2].key) { + wZ += (store.getConfig().histConf.lowHistBoundariesXYZ[2] + iBin * store.getConfig().histConf.binSizeHistZ + store.getConfig().histConf.binSizeHistZ / 2) * store.getHistogramXYZ()[2].get()[iBin]; + sumWZ += store.getHistogramXYZ()[2].get()[iBin]; + } + store.getHistogramXYZ()[2].get()[iBin] = 0; + } + if (sumWZ > minContributors || vertIndex == 0) { + store.getVertices().emplace(vertIndex, store.getBeamPosition()[0], store.getBeamPosition()[1], wZ / sumWZ, ex, ey, ez, sumWZ); + } else { + store.getVertices().emplace(vertIndex); + } + } else { + store.getVertices().emplace(vertIndex); + } + } + } +} +} // namespace GPU + +void VertexerTraitsEC0GPU::computeTracklets() +{ + if (!mClusters[1].size()) { + std::cout << "\t\tno clusters on layer 1. Returning.\n"; + return; + } + const dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + + GPU::trackleterKernel<<>>( + getDeviceContext(), + GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, + mVrtParams.phiCut); + + GPU::trackleterKernel<<>>( + getDeviceContext(), + GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, + mVrtParams.phiCut); + + gpuThrowOnError(); + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { + mDebugger->fillCombinatoricsTree(mClusters, + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mEvent); + } +#endif +} + +void VertexerTraitsEC0GPU::computeTrackletMatching() +{ + if (!mClusters[1].size()) { + std::cout << "\t\tno clusters on layer 1. Returning.\n"; + return; + } + const dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); + + GPU::trackletSelectionKernel<<>>( + getDeviceContext(), + true, // isInitRun + mVrtParams.tanLambdaCut, + mVrtParams.phiCut); + + cub::DeviceScan::ExclusiveSum(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + mStoreVertexerGPU.getNFoundLines().get(), + mStoreVertexerGPU.getNExclusiveFoundLines().get(), + mClusters[1].size()); + + GPU::trackletSelectionKernel<<>>( + getDeviceContext(), + false, // isInitRun + mVrtParams.tanLambdaCut, + mVrtParams.phiCut); + + gpuThrowOnError(); + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + if (isDebugFlag(VertexerDebug::TrackletTreeAll)) { + mDebugger->fillTrackletSelectionTree(mClusters, + mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getDupletIndicesFromGPU(), + mEvent); + } + mTracklets = mStoreVertexerGPU.getLinesFromGPU(); + if (isDebugFlag(VertexerDebug::LineTreeAll)) { + mDebugger->fillPairsInfoTree(mTracklets, mEvent); + } + if (isDebugFlag(VertexerDebug::LineSummaryAll)) { + mDebugger->fillLinesSummaryTree(mTracklets, mEvent); + } +#endif +} + +void VertexerTraitsEC0GPU::computeVertices() +{ + if (!mClusters[1].size()) { + std::cout << "\t\tno clusters on layer 1. Returning.\n"; + return; + } + const dim3 threadsPerBlock{GPU::Utils::Host::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{GPU::Utils::Host::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); + int nLines = mStoreVertexerGPU.getNExclusiveFoundLines().getElementFromDevice(mClusters[1].size() - 1) + mStoreVertexerGPU.getNFoundLines().getElementFromDevice(mClusters[1].size() - 1); + int nCentroids{static_cast(nLines * (nLines - 1) / 2)}; + int* histogramXY[2] = {mStoreVertexerGPU.getHistogramXYZ()[0].get(), mStoreVertexerGPU.getHistogramXYZ()[1].get()}; + float tmpArrayLow[2] = {mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[1]}; + float tmpArrayHigh[2] = {mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[1]}; + GPU::computeCentroidsKernel<<>>(getDeviceContext(), + mVrtParams.histPairCut); + + cub::DeviceHistogram::MultiHistogramEven<2, 2>(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage + bufferSize, // temp_storage_bytes + mStoreVertexerGPU.getXYCentroids().get(), // d_samples + histogramXY, // d_histogram + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ, // num_levels + tmpArrayLow, // lower_level + tmpArrayHigh, // fupper_level + nCentroids); // num_row_pixels + cub::DeviceReduce::ArgMax(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + histogramXY[0], + mStoreVertexerGPU.getTmpVertexPositionBins().get(), + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0]); + cub::DeviceReduce::ArgMax(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + histogramXY[1], + mStoreVertexerGPU.getTmpVertexPositionBins().get() + 1, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0]); + GPU::computeZCentroidsKernel<<>>(getDeviceContext(), mVrtParams.histPairCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[0], mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[1]); + cub::DeviceHistogram::HistogramEven(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage + bufferSize, // temp_storage_bytes + mStoreVertexerGPU.getZCentroids().get(), // d_samples + mStoreVertexerGPU.getHistogramXYZ()[2].get(), // d_histogram + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2], // num_levels + mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[2], // lower_level + mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[2], // fupper_level + nLines); // num_row_pixels + for (int iVertex{0}; iVertex < mStoreVertexerGPU.getConfig().nMaxVertices; ++iVertex) { + cub::DeviceReduce::ArgMax(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + mStoreVertexerGPU.getHistogramXYZ()[2].get(), + mStoreVertexerGPU.getTmpVertexPositionBins().get() + 2, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2]); +#ifdef _ALLOW_DEBUG_TREES_ITS_ + if (isDebugFlag(VertexerDebug::HistCentroids) && !iVertex) { + mDebugger->fillXYZHistogramTree(std::array, 3>{mStoreVertexerGPU.getHistogramXYFromGPU()[0], + mStoreVertexerGPU.getHistogramXYFromGPU()[1], mStoreVertexerGPU.getHistogramZFromGPU()}, + std::array{mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0] - 1, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[1] - 1, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2] - 1}); + } +#endif + GPU::computeVertexKernel<<>>(getDeviceContext(), iVertex, mVrtParams.clusterContributorsCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[2]); + } + std::vector vertices; + vertices.resize(mStoreVertexerGPU.getConfig().nMaxVertices); + mStoreVertexerGPU.getVertices().copyIntoSizedVector(vertices); + + for (auto& vertex : vertices) { + if (vertex.realVertex) { + mVertices.emplace_back(vertex.xCoord, vertex.yCoord, vertex.zCoord, std::array{0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, vertex.contributors, 0.f, -9); + } + } + + gpuThrowOnError(); +} + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +void VertexerTraitsEC0GPU::computeMCFiltering() +{ + std::vector tracklets01 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector tracklets12 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); + std::vector labels01 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector labels12 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); + VertexerStoreConfigurationGPU tmpGPUConf; + const int stride = tmpGPUConf.maxTrackletsPerCluster; + + filterTrackletsWithMC(tracklets01, tracklets12, labels01, labels12, stride); + mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, labels01); + mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, tracklets01); + mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, labels12); + mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, tracklets12); + + if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { + mDebugger->fillCombinatoricsTree(mClusters, + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mEvent); + } +} +#endif + +VertexerTraitsEC0* createVertexerTraitsEC0GPU() +{ + return new VertexerTraitsEC0GPU; +} + +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/CMakeLists.txt b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/CMakeLists.txt new file mode 100644 index 0000000000000..71651a1f8fb28 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# 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. + +set(CMAKE_CXX_COMPILER ${hip_HIPCC_EXECUTABLE}) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(DEFINED HIP_AMDGPUTARGET) + set(TMP_TARGET "(GPU Target ${HIP_AMDGPUTARGET})") +endif() + +message(STATUS "Building EC0 HIP vertexer") +o2_add_library(EC0trackingHIP + SOURCES src/ClusterLinesHIP.hip.cxx + src/ContextHIP.hip.cxx + # src/DeviceStoreHIP.hip.cxx + src/DeviceStoreVertexerHIP.hip.cxx + src/StreamHIP.hip.cxx + # src/TrackerTraitsEC0HIP.hip.cxx + src/VertexerTraitsEC0HIP.hip.cxx + src/UtilsHIP.hip.cxx + PUBLIC_LINK_LIBRARIES O2::EC0tracking + hip::host + hip::device + hip::hipcub + TARGETVARNAME targetName) + +target_compile_definitions( + ${targetName} PRIVATE $) + +if(HIP_AMDGPUTARGET) + target_link_options(${targetName} PUBLIC --amdgpu-target=${HIP_AMDGPUTARGET}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --amdgpu-target=${HIP_AMDGPUTARGET}") +endif() + +target_compile_options(${targetName} + PUBLIC -Wno-invalid-command-line-argument + -Wno-unused-command-line-argument + -Wno-invalid-constexpr + -Wno-ignored-optimization-argument + -Wno-unused-private-field) diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ArrayHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ArrayHIP.h new file mode 100644 index 0000000000000..d08f31bffd74b --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ArrayHIP.h @@ -0,0 +1,73 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ArrayHIP.h +/// \brief +/// + +#ifndef O2_EC0_TRACKING_INCLUDE_ARRAY_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_ARRAY_HIP_H_ + +#include + +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +namespace +{ +template +struct ArrayTraitsHIP final { + typedef T InternalArray[Size]; + + GPUhd() static constexpr T& getReference(const InternalArray& internalArray, size_t index) noexcept + { + return const_cast(internalArray[index]); + } + + GPUhd() static constexpr T* getPointer(const InternalArray& internalArray) noexcept + { + return const_cast(internalArray); + } +}; +} // namespace + +template +struct ArrayHIP final { + + void copy(const ArrayHIP& t) + { +#ifdef __OPENCL__ + for (size_t i{0}; i < Size; ++i) { + InternalArray[i] = t[i]; + } +#else + memcpy(InternalArray, t.data(), Size * sizeof(T)); +#endif + } + + GPUhd() T* data() noexcept { return const_cast(InternalArray); } + GPUhd() const T* data() const noexcept { return const_cast(InternalArray); } + GPUhd() T& operator[](const int index) noexcept { return const_cast(InternalArray[index]); } + GPUhd() constexpr T& operator[](const int index) const noexcept { return const_cast(InternalArray[index]); } + GPUhd() size_t size() const noexcept { return Size; } + + T InternalArray[Size]; +}; +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* O2_EC0_TRACKING_INCLUDE_ARRAY_HIP_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ClusterLinesHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ClusterLinesHIP.h new file mode 100644 index 0000000000000..454359fd26faf --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ClusterLinesHIP.h @@ -0,0 +1,73 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ClusterLinesHIP.h +/// \brief GPU-compliant version of ClusterLines, for the moment separated, might create a common traits for ClusterLines + later specifications for each arch, later. +/// \ author: mconcas@cern.ch + +#ifndef O2_EC0_TRACKING_INCLUDE_CLUSTERLINES_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_CLUSTERLINES_HIP_H_ + +#include +#include "GPUCommonDef.h" +#include "EC0tracking/ClusterLines.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +struct GPUVertex final { + GPUhd() GPUVertex() : realVertex{false} + { + } + + GPUhd() GPUVertex(float x, float y, float z, float eX, float eY, float eZ, int contrib) : xCoord{x}, + yCoord{y}, + zCoord{z}, + errorX{eZ}, + errorY{eY}, + errorZ{eZ}, + contributors{contrib}, + realVertex{true} + { + } + float xCoord; + float yCoord; + float zCoord; + float errorX; + float errorY; + float errorZ; + int contributors; + unsigned char realVertex; +}; + +class ClusterLinesHIP final +{ + public: + GPUd() ClusterLinesHIP(const Line& firstLine, const Line& secondLine); // poor man solution to calculate duplets' centroid + GPUd() void computeClusterCentroid(); + GPUd() inline float* getVertex() { return mVertex; } + + private: + float mAMatrix[6]; // AX=B + float mBMatrix[3]; // AX=B + int mLabels[2]; // labels + float mVertexCandidate[3]; // vertex candidate + float mWeightMatrix[9]; // weight matrix + float mVertex[3]; // cluster centroid position +}; + +} // namespace GPU +} // namespace ecl +} // namespace o2 +#endif /* O2_EC0_TRACKING_INCLUDE_CLUSTERLINES_HIP_H_ */ \ No newline at end of file diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ContextHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ContextHIP.h new file mode 100644 index 0000000000000..2b2afa038a032 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/ContextHIP.h @@ -0,0 +1,71 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ContextHIP.h +/// \brief +/// + +#ifndef O2_EC0_TRACKING_INCLUDE_CONTEXT_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_CONTEXT_HIP_H_ + +#include +#include +// #include "EC0tracking/Definitions.h" +#include +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +struct DeviceProperties final { + std::string name; + int gpuProcessors; + int streamProcessors; + long globalMemorySize; + long constantMemorySize; + long sharedMemorySize; + long maxClockRate; + int busWidth; + long l2CacheSize; + long registersPerBlock; + int warpSize; + int maxThreadsPerBlock; + int maxBlocksPerSM; + dim3 maxThreadsDim; + dim3 maxGridDim; +}; + +class ContextHIP final +{ + public: + static ContextHIP& getInstance(); + + ContextHIP(const ContextHIP&); + ContextHIP& operator=(const ContextHIP&); + + const DeviceProperties& getDeviceProperties(); + const DeviceProperties& getDeviceProperties(const int); + + private: + ContextHIP(bool dumpDevices = true); + ~ContextHIP() = default; + + int mDevicesNum; + std::vector mDeviceProperties; +}; +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* O2_EC0_TRACKING_INCLUDE_CONTEXT_HIP_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/DeviceStoreVertexerHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/DeviceStoreVertexerHIP.h new file mode 100644 index 0000000000000..fd01222570e61 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/DeviceStoreVertexerHIP.h @@ -0,0 +1,297 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 DeviceStoreVertexerHIP.h +/// \brief This class serves as memory interface for GPU vertexer. It will access needed data structures from devicestore apis. +/// routines as static as possible. +/// \author matteo.concas@cern.ch + +#ifndef O2_EC0_TRACKING_INCLUDE_DEVICESTOREVERTEXER_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_DEVICESTOREVERTEXER_HIP_H_ + +#include + +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Constants.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Tracklet.h" +#include "EC0tracking/ClusterLines.h" +#include "EC0trackingHIP/ArrayHIP.h" +#include "EC0trackingHIP/ClusterLinesHIP.h" +#include "EC0trackingHIP/UniquePointerHIP.h" +#include "EC0trackingHIP/VectorHIP.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +enum class TrackletingLayerOrder { + fromInnermostToMiddleLayer, + fromMiddleToOuterLayer +}; + +enum class VertexerLayerName { + innermostLayer, + middleLayer, + outerLayer +}; + +typedef TrackletingLayerOrder Order; +class DeviceStoreVertexerHIP final +{ + public: + DeviceStoreVertexerHIP(); + ~DeviceStoreVertexerHIP() = default; + + UniquePointer initialise(const std::array, constants::ecl::LayersNumberVertexer>&, + const std::array, + constants::ecl::LayersNumberVertexer>&); + + // RO APIs + GPUd() const ArrayHIP, constants::ecl::LayersNumberVertexer>& getClusters() + { + return mClusters; + } + GPUd() const VectorHIP& getIndexTable(const VertexerLayerName); + GPUhd() VertexerStoreConfigurationGPU& getConfig() { return mGPUConf; } + + // Writable APIs + GPUd() VectorHIP& getDuplets01() { return mDuplets01; } + GPUd() VectorHIP& getDuplets12() { return mDuplets12; } + GPUd() VectorHIP& getLines() { return mTracklets; } + GPUd() VectorHIP& getBeamPosition() { return mBeamPosition; } + GPUhd() VectorHIP& getVertices() { return mGPUVertices; } + GPUhd() VectorHIP& getNFoundLines() { return mNFoundLines; } + GPUhd() VectorHIP& getNExclusiveFoundLines() { return mNExclusiveFoundLines; } + GPUhd() VectorHIP& getCUBTmpBuffer() { return mCUBTmpBuffer; } + GPUhd() VectorHIP& getXYCentroids() { return mXYCentroids; } + GPUhd() VectorHIP& getZCentroids() { return mZCentroids; } + GPUhd() ArrayHIP, 3>& getHistogramXYZ() { return mHistogramXYZ; } + GPUhd() VectorHIP>& getTmpVertexPositionBins() { return mTmpVertexPositionBins; } + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + GPUd() ArrayHIP, 2>& getDupletIndices() + { + return mDupletIndices; + } +#endif + GPUhd() VectorHIP& getNFoundTracklets(Order order) + { + return mNFoundDuplets[static_cast(order)]; + } + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + /*GPUh()*/ void updateDuplets(const Order, std::vector&); + /*GPUh()*/ void updateFoundDuplets(const Order, std::vector&); + /*GPUh()*/ std::vector getNFoundTrackletsFromGPU(const Order); + /*GPUh()*/ std::vector getRawDupletsFromGPU(const Order); + /*GPUh()*/ std::vector getDupletsFromGPU(const Order); + /*GPUh()*/ std::vector getRawLinesFromGPU(); + /*GPUh()*/ std::vector> getDupletIndicesFromGPU(); + /*GPUh()*/ std::vector getNFoundLinesFromGPU(); + /*GPUh()*/ std::array, 2> getHistogramXYFromGPU(); + /*GPUh()*/ std::vector getHistogramZFromGPU(); + /*GPUh()*/ std::vector getLinesFromGPU(); +#endif + + private: + VertexerStoreConfigurationGPU mGPUConf; + ArrayHIP, constants::ecl::LayersNumberVertexer> mClusters; + VectorHIP mTracklets; + ArrayHIP, 2> mIndexTables; + VectorHIP mGPUVertices; + + // service buffers + VectorHIP mNFoundLines; + VectorHIP mNExclusiveFoundLines; + VectorHIP mDuplets01; + VectorHIP mDuplets12; + ArrayHIP, constants::ecl::LayersNumberVertexer - 1> mNFoundDuplets; + VectorHIP mCUBTmpBuffer; + VectorHIP mXYCentroids; + VectorHIP mZCentroids; + VectorHIP mBeamPosition; + ArrayHIP, 3> mHistogramXYZ; + VectorHIP> mTmpVertexPositionBins; + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + ArrayHIP, 2> mDupletIndices; + VectorHIP mSizes; +#endif +}; + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +inline std::vector DeviceStoreVertexerHIP::getNFoundTrackletsFromGPU(const Order order) +{ + // Careful: this might lead to large allocations, use debug-purpose only + std::vector sizes; + sizes.resize(constants::ecl::LayersNumberVertexer); + mSizes.copyIntoSizedVector(sizes); + std::vector nFoundDuplets; + nFoundDuplets.resize(sizes[1]); + + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); + } else { + mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); + } + + return nFoundDuplets; +} + +inline std::vector DeviceStoreVertexerHIP::getRawDupletsFromGPU(const Order order) +{ + // Careful: this might lead to large allocations, use debug-purpose only + std::vector sizes; + sizes.resize(constants::ecl::LayersNumberVertexer); + mSizes.copyIntoSizedVector(sizes); + std::vector tmpDuplets; + tmpDuplets.resize(static_cast(mGPUConf.dupletsCapacity)); + std::vector nFoundDuplets; + nFoundDuplets.resize(sizes[1]); + + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); + mDuplets01.copyIntoSizedVector(tmpDuplets); + } else { + mDuplets12.copyIntoSizedVector(tmpDuplets); + mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); + } + + return tmpDuplets; +} + +inline std::vector DeviceStoreVertexerHIP::getDupletsFromGPU(const Order order) +{ + // Careful: this might lead to large allocations, use debug-purpose only + std::vector sizes; + sizes.resize(constants::ecl::LayersNumberVertexer); + mSizes.copyIntoSizedVector(sizes); + std::vector tmpDuplets; + tmpDuplets.resize(static_cast(mGPUConf.dupletsCapacity)); + std::vector nFoundDuplets; + nFoundDuplets.resize(sizes[1]); + std::vector shrinkedDuplets; + + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].copyIntoSizedVector(nFoundDuplets); + mDuplets01.copyIntoSizedVector(tmpDuplets); + } else { + mDuplets12.copyIntoSizedVector(tmpDuplets); + mNFoundDuplets[1].copyIntoSizedVector(nFoundDuplets); + } + + for (int iCluster{0}; iCluster < sizes[1]; ++iCluster) { + const int stride{iCluster * mGPUConf.maxTrackletsPerCluster}; + for (int iDuplet{0}; iDuplet < nFoundDuplets[iCluster]; ++iDuplet) { + shrinkedDuplets.push_back(tmpDuplets[stride + iDuplet]); + } + } + return shrinkedDuplets; +} + +inline std::vector> DeviceStoreVertexerHIP::getDupletIndicesFromGPU() +{ + // Careful: this might lead to large allocations, use debug-purpose only. + std::array, 2> allowedLines; + std::vector> allowedPairIndices; + int nLines = getNExclusiveFoundLines().getElementFromDevice(mClusters[1].getSizeFromDevice() - 1) + getNFoundLines().getElementFromDevice(mClusters[1].getSizeFromDevice() - 1); + allowedPairIndices.reserve(nLines); + for (int iAllowed{0}; iAllowed < 2; ++iAllowed) { + allowedLines[iAllowed].resize(nLines); + mDupletIndices[iAllowed].resize(nLines); + mDupletIndices[iAllowed].copyIntoSizedVector(allowedLines[iAllowed]); + } + for (size_t iPair{0}; iPair < allowedLines[0].size(); ++iPair) { + allowedPairIndices.emplace_back(std::array{allowedLines[0][iPair], allowedLines[1][iPair]}); + } + return allowedPairIndices; +} + +inline std::array, 2> DeviceStoreVertexerHIP::getHistogramXYFromGPU() +{ + std::array, 2> histoXY; + for (int iHisto{0}; iHisto < 2; ++iHisto) { + histoXY[iHisto].resize(mGPUConf.nBinsXYZ[iHisto] - 1); + mHistogramXYZ[iHisto].copyIntoSizedVector(histoXY[iHisto]); + } + + return histoXY; +} + +inline std::vector DeviceStoreVertexerHIP::getHistogramZFromGPU() +{ + std::vector histoZ; + histoZ.resize(mGPUConf.nBinsXYZ[2] - 1); + std::cout << "Size of dest vector to be refined" << std::endl; + mHistogramXYZ[2].copyIntoSizedVector(histoZ); + + return histoZ; +} + +inline void DeviceStoreVertexerHIP::updateDuplets(const Order order, std::vector& duplets) +{ + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mDuplets01.reset(duplets.data(), static_cast(duplets.size())); + } else { + mDuplets12.reset(duplets.data(), static_cast(duplets.size())); + } +} + +inline void DeviceStoreVertexerHIP::updateFoundDuplets(const Order order, std::vector& nDuplets) +{ + if (order == GPU::Order::fromInnermostToMiddleLayer) { + mNFoundDuplets[0].reset(nDuplets.data(), static_cast(nDuplets.size())); + } else { + mNFoundDuplets[1].reset(nDuplets.data(), static_cast(nDuplets.size())); + } +} + +inline std::vector DeviceStoreVertexerHIP::getRawLinesFromGPU() +{ + std::vector lines; + lines.resize(mGPUConf.processedTrackletsCapacity); + mTracklets.copyIntoSizedVector(lines); + + return lines; +} + +inline std::vector DeviceStoreVertexerHIP::getNFoundLinesFromGPU() +{ + std::vector nFoundLines; + nFoundLines.resize(mGPUConf.clustersPerLayerCapacity); + mNFoundLines.copyIntoSizedVector(nFoundLines); + + return nFoundLines; +} + +inline std::vector DeviceStoreVertexerHIP::getLinesFromGPU() +{ + std::vector lines; + std::vector tmpLines; + tmpLines.resize(mGPUConf.processedTrackletsCapacity); + mTracklets.copyIntoSizedVector(tmpLines); + for (auto& line : tmpLines) { + if (line.isEmpty) { + break; + } + lines.push_back(line); + } + return lines; +} +#endif +} // namespace GPU +} // namespace ecl +} // namespace o2 +#endif //O2_EC0_TRACKING_INCLUDE_DEVICESTOREVERTEXER_HIP_H_ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/StreamHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/StreamHIP.h new file mode 100644 index 0000000000000..b61d7f5784487 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/StreamHIP.h @@ -0,0 +1,46 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 StreamHIP.h +/// \brief +/// + +#ifndef O2_EC0_TRACKING_INCLUDE_STREAM_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_STREAM_HIP_H_ + +#include + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +class StreamHIP final +{ + + public: + StreamHIP(); + ~StreamHIP(); + + StreamHIP(const StreamHIP&) = delete; + StreamHIP& operator=(const StreamHIP&) = delete; + + const hipStream_t& get() const; + + private: + hipStream_t mStream; +}; +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* O2_EC0_TRACKING_INCLUDE_STREAM_HIP_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/UniquePointerHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/UniquePointerHIP.h new file mode 100644 index 0000000000000..58a09e221f955 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/UniquePointerHIP.h @@ -0,0 +1,153 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 UniquePointerHIP.h +/// \brief +/// + +#ifndef O2_EC0_TRACKING_INCLUDE_UNIQUEPOINTER_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_UNIQUEPOINTER_HIP_H_ + +#include "GPUCommonDef.h" +#include "EC0trackingHIP/UtilsHIP.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +namespace +{ +template +struct UniquePointerTraits final { + typedef T* InternalPointer; + + GPUhd() static constexpr T& getReference(const InternalPointer& internalPointer) noexcept + { + return const_cast(*internalPointer); + } + + GPUhd() static constexpr T* getPointer(const InternalPointer& internalPointer) noexcept + { + return const_cast(internalPointer); + } +}; +} // namespace + +template +class UniquePointer final +{ + typedef UniquePointerTraits PointerTraits; + + public: + UniquePointer(); + explicit UniquePointer(const T&); + ~UniquePointer(); + + UniquePointer(const UniquePointer&) = delete; + UniquePointer& operator=(const UniquePointer&) = delete; + + UniquePointer(UniquePointer&&); + UniquePointer& operator=(UniquePointer&&); + + GPUhd() T* get() noexcept; + GPUhd() const T* get() const noexcept; + GPUhd() T& operator*() noexcept; + GPUhd() const T& operator*() const noexcept; + + protected: + void destroy(); + + private: + typename PointerTraits::InternalPointer mDevicePointer; +}; + +template +UniquePointer::UniquePointer() : mDevicePointer{nullptr} +{ + // Nothing to do +} + +template +UniquePointer::UniquePointer(const T& ref) +{ + try { + + Utils::HostHIP::gpuMalloc(reinterpret_cast(&mDevicePointer), sizeof(T)); + Utils::HostHIP::gpuMemcpyHostToDevice(mDevicePointer, &ref, sizeof(T)); + + } catch (...) { + + destroy(); + + throw; + } +} + +template +UniquePointer::~UniquePointer() +{ + destroy(); +} + +template +UniquePointer::UniquePointer(UniquePointer&& other) : mDevicePointer{other.mDevicePointer} +{ + // Nothing to do +} + +template +UniquePointer& UniquePointer::operator=(UniquePointer&& other) +{ + mDevicePointer = other.mDevicePointer; + other.mDevicePointer = nullptr; + + return *this; +} + +template +void UniquePointer::destroy() +{ + if (mDevicePointer != nullptr) { + + Utils::HostHIP::gpuFree(mDevicePointer); + } +} + +template +GPUhd() T* UniquePointer::get() noexcept +{ + return PointerTraits::getPointer(mDevicePointer); +} + +template +GPUhd() const T* UniquePointer::get() const noexcept +{ + return PointerTraits::getPointer(mDevicePointer); +} + +template +GPUhd() T& UniquePointer::operator*() noexcept +{ + return PointerTraits::getReference(mDevicePointer); +} + +template +GPUhd() const T& UniquePointer::operator*() const noexcept +{ + return PointerTraits::getReference(mDevicePointer); +} +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* O2_EC0_TRACKING_INCLUDE_UNIQUEPOINTER_HIP_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/UtilsHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/UtilsHIP.h new file mode 100644 index 0000000000000..62806f8f3d2c8 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/UtilsHIP.h @@ -0,0 +1,66 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 UtilsHIP.h +/// \brief +/// + +#ifndef O2_EC0_TRACKING_INCLUDE_UTILS_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_UTILS_HIP_H_ + +#include "GPUCommonDef.h" +#include "EC0trackingHIP/StreamHIP.h" +#include + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +namespace Utils +{ + +namespace HostHIP +{ + +#ifdef __HIPCC__ +void checkHIPError(const hipError_t error, const char* file, const int line); +#endif + +dim3 getBlockSize(const int); +dim3 getBlockSize(const int, const int); +dim3 getBlockSize(const int, const int, const int); +dim3 getBlocksGrid(const dim3&, const int); +dim3 getBlocksGrid(const dim3&, const int, const int); +// +void gpuMalloc(void**, const int); +void gpuFree(void*); +void gpuMemset(void*, int, int); +void gpuMemcpyHostToDevice(void*, const void*, int); +void gpuMemcpyHostToDeviceAsync(void*, const void*, int, hipStream_t&); +void gpuMemcpyDeviceToHost(void*, const void*, int); +// void gpuStartProfiler(); +// void gpuStopProfiler(); +} // namespace HostHIP +// +namespace DeviceHIP +{ +GPUd() int getLaneIndex(); +GPUd() int shareToWarp(const int, const int); +GPUd() int gpuAtomicAdd(int*, const int); +} // namespace DeviceHIP +} // namespace Utils +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* O2_EC0_TRACKING_INCLUDE_UTILS_HIP_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/VectorHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/VectorHIP.h new file mode 100644 index 0000000000000..1443c8a2e12b1 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/VectorHIP.h @@ -0,0 +1,330 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 VectorHIP.h +/// \brief +/// + +#ifndef O2_EC0_TRACKING_INCLUDE_VECTOR_HIP_H_ +#define O2_EC0_TRACKING_INCLUDE_VECTOR_HIP_H_ + +#include +#include +#include +#include + +#include "EC0trackingHIP/StreamHIP.h" +#include "EC0trackingHIP/UtilsHIP.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +template +class VectorHIP final +{ + static_assert(std::is_trivially_destructible::value, "VectorHIP only supports trivially destructible objects."); + + public: + VectorHIP(); + explicit VectorHIP(const int, const int = 0); + VectorHIP(const T* const, const int, const int = 0); + GPUhd() ~VectorHIP(); + + VectorHIP(const VectorHIP&) = delete; + VectorHIP& operator=(const VectorHIP&) = delete; + + GPUhd() VectorHIP(VectorHIP&&); + VectorHIP& operator=(VectorHIP&&); + + int getSizeFromDevice() const; + + T getElementFromDevice(const int) const; + + void resize(const int); + void reset(const int, const int = 0); + void reset(const T* const, const int, const int = 0); + void copyIntoVector(std::vector&, const int); + void copyIntoSizedVector(std::vector&); + + GPUhd() T* get() const; + GPUhd() int capacity() const; + GPUhd() VectorHIP getWeakCopy() const; + GPUhd() T& operator[](const int) const; + + GPUhd() int size() const; + GPUd() int extend(const int) const; + GPUhd() void dump(); + + template + GPUd() void emplace(const int, Args&&...); + + protected: + void destroy(); + + private: + GPUhd() VectorHIP(const VectorHIP&, const bool); + + T* mArrayPointer = nullptr; + int* mDeviceSize = nullptr; + int mCapacity; + bool mIsWeak; +}; + +template +VectorHIP::VectorHIP() : VectorHIP{nullptr, 0} +{ + // Nothing to do +} + +template +VectorHIP::VectorHIP(const int capacity, const int initialSize) : VectorHIP{nullptr, capacity, initialSize} +{ + // Nothing to do +} + +template +VectorHIP::VectorHIP(const T* const source, const int size, const int initialSize) : mCapacity{size}, mIsWeak{false} +{ + if (size > 0) { + try { + + Utils::HostHIP::gpuMalloc(reinterpret_cast(&mArrayPointer), size * sizeof(T)); + Utils::HostHIP::gpuMalloc(reinterpret_cast(&mDeviceSize), sizeof(int)); + + if (source != nullptr) { + + Utils::HostHIP::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); + Utils::HostHIP::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); + + } else { + + Utils::HostHIP::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); + } + + } catch (...) { + + destroy(); + + throw; + } + } +} + +template +GPUhdi() VectorHIP::VectorHIP(const VectorHIP& other, const bool isWeak) + : mArrayPointer{other.mArrayPointer}, + mDeviceSize{other.mDeviceSize}, + mCapacity{other.mCapacity}, + mIsWeak{isWeak} +{ + // Nothing to do +} + +template +GPUhd() VectorHIP::~VectorHIP() +{ + if (mIsWeak) { + + return; + + } else { +#if defined(TRACKINGEC0__GPU_DEVICE) + assert(0); +#else + destroy(); +#endif + } +} + +template +GPUhd() VectorHIP::VectorHIP(VectorHIP&& other) + : mArrayPointer{other.mArrayPointer}, + mDeviceSize{other.mDeviceSize}, + mCapacity{other.mCapacity}, + mIsWeak{other.mIsWeak} +{ + other.mArrayPointer = nullptr; + other.mDeviceSize = nullptr; +} + +template +VectorHIP& VectorHIP::operator=(VectorHIP&& other) +{ + destroy(); + + mArrayPointer = other.mArrayPointer; + mDeviceSize = other.mDeviceSize; + mCapacity = other.mCapacity; + mIsWeak = other.mIsWeak; + + other.mArrayPointer = nullptr; + other.mDeviceSize = nullptr; + + return *this; +} + +template +int VectorHIP::getSizeFromDevice() const +{ + int size; + Utils::HostHIP::gpuMemcpyDeviceToHost(&size, mDeviceSize, sizeof(int)); + + return size; +} + +template +void VectorHIP::resize(const int size) +{ + Utils::HostHIP::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); +} + +template +void VectorHIP::reset(const int capacity, const int initialSize) +{ + reset(nullptr, capacity, initialSize); +} + +template +void VectorHIP::reset(const T* const source, const int size, const int initialSize) +{ + if (size > mCapacity) { + if (mArrayPointer != nullptr) { + Utils::HostHIP::gpuFree(mArrayPointer); + } + + Utils::HostHIP::gpuMalloc(reinterpret_cast(&mArrayPointer), size * sizeof(T)); + mCapacity = size; + } + + if (source != nullptr) { + + Utils::HostHIP::gpuMemcpyHostToDevice(mArrayPointer, source, size * sizeof(T)); + Utils::HostHIP::gpuMemcpyHostToDevice(mDeviceSize, &size, sizeof(int)); + + } else { + Utils::HostHIP::gpuMemcpyHostToDevice(mDeviceSize, &initialSize, sizeof(int)); + } +} + +template +void VectorHIP::copyIntoVector(std::vector& destinationVector, const int size) +{ + + T* hostPrimitivePointer = nullptr; + + try { + + hostPrimitivePointer = static_cast(malloc(size * sizeof(T))); + Utils::HostHIP::gpuMemcpyDeviceToHost(hostPrimitivePointer, mArrayPointer, size * sizeof(T)); + + destinationVector = std::move(std::vector(hostPrimitivePointer, hostPrimitivePointer + size)); + + } catch (...) { + + if (hostPrimitivePointer != nullptr) { + + free(hostPrimitivePointer); + } + + throw; + } +} + +template +void VectorHIP::copyIntoSizedVector(std::vector& destinationVector) +{ + Utils::HostHIP::gpuMemcpyDeviceToHost(destinationVector.data(), mArrayPointer, destinationVector.size() * sizeof(T)); +} + +template +inline void VectorHIP::destroy() +{ + if (mArrayPointer != nullptr) { + + Utils::HostHIP::gpuFree(mArrayPointer); + } + + if (mDeviceSize != nullptr) { + + Utils::HostHIP::gpuFree(mDeviceSize); + } +} + +template +GPUhd() T* VectorHIP::get() const +{ + return mArrayPointer; +} + +template +GPUhd() int VectorHIP::capacity() const +{ + return mCapacity; +} + +template +GPUhd() VectorHIP VectorHIP::getWeakCopy() const +{ + return VectorHIP{*this, true}; +} + +template +GPUhd() T& VectorHIP::operator[](const int index) const +{ + return mArrayPointer[index]; +} + +template +T VectorHIP::getElementFromDevice(const int index) const +{ + T element; + Utils::HostHIP::gpuMemcpyDeviceToHost(&element, mArrayPointer + index, sizeof(T)); + + return element; +} + +template +GPUhd() int VectorHIP::size() const +{ + return *mDeviceSize; +} + +template +GPUd() int VectorHIP::extend(const int sizeIncrement) const +{ + const int startIndex = Utils::DeviceHIP::gpuAtomicAdd(mDeviceSize, sizeIncrement); + assert(size() <= mCapacity); + + return startIndex; +} + +template +template +GPUd() void VectorHIP::emplace(const int index, Args&&... arguments) +{ + new (mArrayPointer + index) T(std::forward(arguments)...); +} + +template +GPUhd() void VectorHIP::dump() +{ + printf("mArrayPointer = %p\nmDeviceSize = %p\nmCapacity = %d\nmIsWeak = %s\n", + mArrayPointer, mDeviceSize, mCapacity, mIsWeak ? "true" : "false"); +} +} // namespace GPU +} // namespace ecl +} // namespace o2 + +#endif /* O2_EC0_TRACKING_INCLUDE_VECTOR_HIP_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/VertexerTraitsHIP.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/VertexerTraitsHIP.h new file mode 100644 index 0000000000000..8560135350dc3 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/include/EC0trackingHIP/VertexerTraitsHIP.h @@ -0,0 +1,96 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 VertexerTraitsEC0HIP.h +/// \brief +/// \author matteo.concas@cern.ch + +#ifndef O2_EC0_TRACKING_VERTEXERTRAITS_HIP_H_ +#define O2_EC0_TRACKING_VERTEXERTRAITS_HIP_H_ + +#include +#include + +#include "EC0tracking/VertexerTraitsEC0.h" +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Constants.h" +// #include "EC0tracking/Definitions.h" +#include "EC0tracking/Tracklet.h" + +#include "EC0trackingHIP/DeviceStoreVertexerHIP.h" +#include "EC0trackingHIP/UniquePointerHIP.h" + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +#include "EC0tracking/StandaloneDebugger.h" +#endif + +namespace o2 +{ +namespace ecl +{ +class ROframe; + +using constants::index_table::InversePhiBinSize; + +class VertexerTraitsEC0HIP : public VertexerTraitsEC0 +{ + public: +#ifdef _ALLOW_DEBUG_TREES_ITS_ + VertexerTraitsEC0HIP(); + ~VertexerTraitsEC0HIP(); +#else + VertexerTraitsEC0HIP(); + ~VertexerTraitsEC0HIP() = default; +#endif + void initialise(ROframe*) override; + void computeTracklets() override; + void computeTrackletMatching() override; + void computeVertices() override; +#ifdef _ALLOW_DEBUG_TREES_ITS_ + void computeMCFiltering() override; +#endif + + // GPU-specific getters + GPUd() static const int2 getBinsPhiRectWindow(const Cluster&, float maxdeltaphi); + GPUhd() GPU::DeviceStoreVertexerHIP& getDeviceContext(); + GPUhd() GPU::DeviceStoreVertexerHIP* getDeviceContextPtr(); + + protected: + // #ifdef _ALLOW_DEBUG_TREES_ITS_ + // StandaloneDebugger* mDebugger; + // #endif + GPU::DeviceStoreVertexerHIP mStoreVertexerGPU; + GPU::UniquePointer mStoreVertexerGPUPtr; +}; + +GPUdi() const int2 VertexerTraitsEC0HIP::getBinsPhiRectWindow(const Cluster& currentCluster, float phiCut) +{ + // This function returns the lowest PhiBin and the number of phi bins to be spanned, In the form int2{phiBinLow, PhiBinSpan} + const int phiBinMin{index_table_utils::getPhiBinIndex( + math_utils::getNormalizedPhiCoordinate(currentCluster.phiCoordinate - phiCut))}; + const int phiBinSpan{static_cast(MATH_CEIL(phiCut * InversePhiBinSize))}; + return int2{phiBinMin, phiBinSpan}; +} + +GPUhdi() GPU::DeviceStoreVertexerHIP& VertexerTraitsEC0HIP::getDeviceContext() +{ + return *mStoreVertexerGPUPtr; +} + +GPUhdi() GPU::DeviceStoreVertexerHIP* VertexerTraitsEC0HIP::getDeviceContextPtr() +{ + return mStoreVertexerGPUPtr.get(); +} + +extern "C" VertexerTraitsEC0* createVertexerTraitsEC0HIP(); + +} // namespace ecl +} // namespace o2 +#endif /* O2_EC0_TRACKING_VERTEXERTRAITS_HIP_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/ClusterLinesHIP.hip.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/ClusterLinesHIP.hip.cxx new file mode 100644 index 0000000000000..6820fa17e7ae1 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/ClusterLinesHIP.hip.cxx @@ -0,0 +1,138 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ClusterLinesHIP.hip.cxx +/// \brief +/// \author matteo.concas@cern.ch + +#include "EC0trackingHIP/ClusterLinesHIP.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +GPUd() ClusterLinesHIP::ClusterLinesHIP(const Line& firstLine, const Line& secondLine) +{ + float covarianceFirst[3]; + float covarianceSecond[3]; + + for (int i{0}; i < 3; ++i) { + covarianceFirst[i] = 1.f; + covarianceSecond[i] = 1.f; + } + + float determinantFirst = + firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + + firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + + firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; + float determinantSecond = + secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + + secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; + + mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + + firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / + determinantFirst + + (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + + secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / + determinantSecond; + + mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; + + mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; + + mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + + firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / + determinantFirst + + (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / + determinantSecond; + + mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - + secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; + + mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + + firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / + determinantFirst + + (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + + secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / + determinantSecond; + + mBMatrix[0] = + (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + + firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / + determinantFirst; + + mBMatrix[0] += + (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + + secondLine.cosinesDirector[2] * covarianceSecond[1] * + (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + + secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / + determinantSecond; + + mBMatrix[1] = + (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + + firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / + determinantFirst; + + mBMatrix[1] += + (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + + secondLine.cosinesDirector[2] * covarianceSecond[0] * + (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + + secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / + determinantSecond; + + mBMatrix[2] = + (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + + firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / + determinantFirst; + + mBMatrix[2] += + (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + + secondLine.cosinesDirector[1] * covarianceSecond[0] * + (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + + secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / + determinantSecond; + + computeClusterCentroid(); +} + +GPUd() void ClusterLinesHIP::computeClusterCentroid() +{ + + float determinant{mAMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - + mAMatrix[1] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + + mAMatrix[2] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])}; + + if (determinant == 0) { + return; + } + + mVertex[0] = -(mBMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - + mAMatrix[1] * (mBMatrix[1] * mAMatrix[5] - mAMatrix[4] * mBMatrix[2]) + + mAMatrix[2] * (mBMatrix[1] * mAMatrix[4] - mBMatrix[2] * mAMatrix[3])) / + determinant; + mVertex[1] = -(mAMatrix[0] * (mBMatrix[1] * mAMatrix[5] - mBMatrix[2] * mAMatrix[4]) - + mBMatrix[0] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + + mAMatrix[2] * (mAMatrix[1] * mBMatrix[2] - mAMatrix[2] * mBMatrix[1])) / + determinant; + mVertex[2] = -(mAMatrix[0] * (mAMatrix[3] * mBMatrix[2] - mBMatrix[1] * mAMatrix[4]) - + mAMatrix[1] * (mAMatrix[1] * mBMatrix[2] - mBMatrix[1] * mAMatrix[2]) + + mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / + determinant; +} +} // namespace GPU +} // namespace ecl +} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/ContextHIP.hip.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/ContextHIP.hip.cxx new file mode 100644 index 0000000000000..6cc2b2a98f7c3 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/ContextHIP.hip.cxx @@ -0,0 +1,134 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ContextHIP.hip.cxx +/// \brief +/// + +#include "EC0trackingHIP/ContextHIP.h" +#include "EC0trackingHIP/UtilsHIP.h" + +#include +#include + +#include + +namespace +{ + +inline int getStreamProcessors(const int major, const int minor) +{ + // Hardcoded result for AMD RADEON WX 9100, to be decided if and how determine this paramter + return 4096; +} + +inline int getMaxThreadsPerComputingUnit(const int major, const int minor) +{ + return 8; +} + +} // namespace + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +using Utils::HostHIP::checkHIPError; + +ContextHIP::ContextHIP(bool dumpDevices) +{ + checkHIPError(hipGetDeviceCount(&mDevicesNum), __FILE__, __LINE__); + if (mDevicesNum == 0) { + throw std::runtime_error{"There are no available device(s) that support HIP\n"}; + } + + mDeviceProperties.resize(mDevicesNum, DeviceProperties{}); + + int currentDeviceIndex; + checkHIPError(hipGetDevice(¤tDeviceIndex), __FILE__, __LINE__); + + for (int iDevice{0}; iDevice < mDevicesNum; ++iDevice) { + + hipDeviceProp_t deviceProperties; + + checkHIPError(hipSetDevice(iDevice), __FILE__, __LINE__); + checkHIPError(hipGetDeviceProperties(&deviceProperties, iDevice), __FILE__, __LINE__); + + int major = deviceProperties.major; // Codacy warning + int minor = deviceProperties.minor; // Codacy warning + + mDeviceProperties[iDevice].name = deviceProperties.name; + mDeviceProperties[iDevice].gpuProcessors = deviceProperties.multiProcessorCount; + mDeviceProperties[iDevice].streamProcessors = getStreamProcessors(major, minor) * deviceProperties.multiProcessorCount; // >>>> alarm + mDeviceProperties[iDevice].globalMemorySize = deviceProperties.totalGlobalMem; + mDeviceProperties[iDevice].constantMemorySize = deviceProperties.totalConstMem; + mDeviceProperties[iDevice].sharedMemorySize = deviceProperties.sharedMemPerBlock; + mDeviceProperties[iDevice].maxClockRate = deviceProperties.memoryClockRate; + mDeviceProperties[iDevice].busWidth = deviceProperties.memoryBusWidth; + mDeviceProperties[iDevice].l2CacheSize = deviceProperties.l2CacheSize; + mDeviceProperties[iDevice].registersPerBlock = deviceProperties.regsPerBlock; + mDeviceProperties[iDevice].warpSize = deviceProperties.warpSize; + mDeviceProperties[iDevice].maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock; + mDeviceProperties[iDevice].maxBlocksPerSM = getMaxThreadsPerComputingUnit(major, minor); + mDeviceProperties[iDevice].maxThreadsDim = dim3{static_cast(deviceProperties.maxThreadsDim[0]), + static_cast(deviceProperties.maxThreadsDim[1]), + static_cast(deviceProperties.maxThreadsDim[2])}; + mDeviceProperties[iDevice].maxGridDim = dim3{static_cast(deviceProperties.maxGridSize[0]), + static_cast(deviceProperties.maxGridSize[1]), + static_cast(deviceProperties.maxGridSize[2])}; + if (dumpDevices) { + std::cout << "################ HIP DEVICE " << iDevice << " ################" << std::endl; + std::cout << "Name " << mDeviceProperties[iDevice].name << std::endl; + std::cout << "gpuProcessors " << mDeviceProperties[iDevice].gpuProcessors << std::endl; + std::cout << "minor " << minor << " major " << major << std::endl; + std::cout << "globalMemorySize " << mDeviceProperties[iDevice].globalMemorySize << std::endl; + std::cout << "constantMemorySize " << mDeviceProperties[iDevice].constantMemorySize << std::endl; + std::cout << "sharedMemorySize " << mDeviceProperties[iDevice].sharedMemorySize << std::endl; + std::cout << "maxClockRate " << mDeviceProperties[iDevice].maxClockRate << std::endl; + std::cout << "busWidth " << mDeviceProperties[iDevice].busWidth << std::endl; + std::cout << "l2CacheSize " << mDeviceProperties[iDevice].l2CacheSize << std::endl; + std::cout << "registersPerBlock " << mDeviceProperties[iDevice].registersPerBlock << std::endl; + std::cout << "warpSize " << mDeviceProperties[iDevice].warpSize << std::endl; + std::cout << "maxThreadsPerBlock " << mDeviceProperties[iDevice].maxThreadsPerBlock << std::endl; + std::cout << "maxBlocksPerSM " << mDeviceProperties[iDevice].maxBlocksPerSM << std::endl; + std::cout << "maxThreadsDim " << mDeviceProperties[iDevice].maxThreadsDim.x << ", " << mDeviceProperties[iDevice].maxThreadsDim.y << ", " << mDeviceProperties[iDevice].maxThreadsDim.z << std::endl; + std::cout << "maxGridDim " << mDeviceProperties[iDevice].maxGridDim.x << ", " << mDeviceProperties[iDevice].maxGridDim.y << ", " << mDeviceProperties[iDevice].maxGridDim.z << std::endl; + std::cout << std::endl; + } + } + + checkHIPError(hipSetDevice(currentDeviceIndex), __FILE__, __LINE__); +} + +ContextHIP& ContextHIP::getInstance() +{ + static ContextHIP gpuContextHIP; + return gpuContextHIP; +} + +const DeviceProperties& ContextHIP::getDeviceProperties() +{ + int currentDeviceIndex; + checkHIPError(hipGetDevice(¤tDeviceIndex), __FILE__, __LINE__); + + return getDeviceProperties(currentDeviceIndex); +} + +const DeviceProperties& ContextHIP::getDeviceProperties(const int deviceIndex) +{ + return mDeviceProperties[deviceIndex]; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/DeviceStoreVertexerHIP.hip.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/DeviceStoreVertexerHIP.hip.cxx new file mode 100644 index 0000000000000..46e1c3ab6c406 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/DeviceStoreVertexerHIP.hip.cxx @@ -0,0 +1,110 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 DeviceStoreVertexerHIP.hip.cxx +/// \brief +/// \author matteo.concas@cern.ch + +#include + +#include "EC0trackingHIP/DeviceStoreVertexerHIP.h" +#include "EC0tracking/Configuration.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ +GPUg() void defaultInitArrayKernel(int* array, const size_t arraySize, const int initValue = 0) +{ + for (size_t i{blockIdx.x * blockDim.x + threadIdx.x}; i < arraySize; i += blockDim.x * gridDim.x) { + if (i < arraySize) { + array[i] = initValue; + } + } +} + +DeviceStoreVertexerHIP::DeviceStoreVertexerHIP() +{ + mDuplets01 = VectorHIP{mGPUConf.dupletsCapacity, mGPUConf.dupletsCapacity}; // 200 * 4e4 * sizeof(Tracklet) = 128MB + mDuplets12 = VectorHIP{mGPUConf.dupletsCapacity, mGPUConf.dupletsCapacity}; // 200 * 4e4 * sizeof(Tracklet) = 128MB + mTracklets = VectorHIP{mGPUConf.processedTrackletsCapacity, mGPUConf.processedTrackletsCapacity}; // 200 * 4e4 * sizeof(Line) = 296MB + mCUBTmpBuffer = VectorHIP{mGPUConf.tmpCUBBufferSize, mGPUConf.tmpCUBBufferSize}; // 5e3 * sizeof(int) = 20KB + mXYCentroids = VectorHIP{2 * mGPUConf.maxCentroidsXYCapacity, 2 * mGPUConf.maxCentroidsXYCapacity}; // + mZCentroids = VectorHIP{mGPUConf.processedTrackletsCapacity, mGPUConf.processedTrackletsCapacity}; // + mNFoundLines = VectorHIP{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; // 4e4 * sizeof(int) = 160KB + mNExclusiveFoundLines = VectorHIP{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; // 4e4 * sizeof(int) = 160KB, tot = <10MB + mTmpVertexPositionBins = VectorHIP>{3, 3}; + mGPUVertices = VectorHIP{mGPUConf.nMaxVertices, mGPUConf.nMaxVertices}; + mBeamPosition = VectorHIP{2, 2}; + + for (int iTable{0}; iTable < 2; ++iTable) { + mIndexTables[iTable] = VectorHIP{constants::index_table::ZBins * constants::index_table::PhiBins + 1}; // 2*20*20+1 * sizeof(int) = 802B + } + for (int iLayer{0}; iLayer < constants::ecl::LayersNumberVertexer; ++iLayer) { // 4e4 * 3 * sizof(Cluster) = 3.36MB + mClusters[iLayer] = VectorHIP{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; + } + for (int iPair{0}; iPair < constants::ecl::LayersNumberVertexer - 1; ++iPair) { + mNFoundDuplets[iPair] = VectorHIP{mGPUConf.clustersPerLayerCapacity, mGPUConf.clustersPerLayerCapacity}; // 4e4 * 2 * sizeof(int) = 320KB + } + for (int iHisto{0}; iHisto < 3; ++iHisto) { + mHistogramXYZ[iHisto] = VectorHIP{mGPUConf.histConf.nBinsXYZ[iHisto], mGPUConf.histConf.nBinsXYZ[iHisto]}; + } + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + for (int iLayersCouple{0}; iLayersCouple < 2; ++iLayersCouple) { + mDupletIndices[iLayersCouple] = VectorHIP{mGPUConf.processedTrackletsCapacity, mGPUConf.processedTrackletsCapacity}; + } + mSizes = VectorHIP{constants::ecl::LayersNumberVertexer}; +#endif +} // namespace GPU + +UniquePointer DeviceStoreVertexerHIP::initialise(const std::array, constants::ecl::LayersNumberVertexer>& clusters, + const std::array, + constants::ecl::LayersNumberVertexer>& indexTables) +{ +#ifdef _ALLOW_DEBUG_TREES_ITS_ + std::array tmpSizes = {static_cast(clusters[0].size()), + static_cast(clusters[1].size()), + static_cast(clusters[2].size())}; + + mSizes.reset(tmpSizes.data(), static_cast(3)); +#endif + for (int iLayer{0}; iLayer < constants::ecl::LayersNumberVertexer; ++iLayer) { + mClusters[iLayer].reset(clusters[iLayer].data(), static_cast(clusters[iLayer].size())); + } + mIndexTables[0].reset(indexTables[0].data(), static_cast(indexTables[0].size())); + mIndexTables[1].reset(indexTables[2].data(), static_cast(indexTables[2].size())); + + const dim3 threadsPerBlock{Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + + UniquePointer deviceStoreVertexerPtr{*this}; + + hipLaunchKernelGGL((defaultInitArrayKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer).get(), + getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer).capacity(), 0); + hipLaunchKernelGGL((defaultInitArrayKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer).get(), + getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer).capacity(), 0); + + return deviceStoreVertexerPtr; +} + +GPUd() const VectorHIP& DeviceStoreVertexerHIP::getIndexTable(const VertexerLayerName layer) +{ + if (layer == VertexerLayerName::innermostLayer) { + return mIndexTables[0]; + } + return mIndexTables[1]; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/StreamHIP.hip.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/StreamHIP.hip.cxx new file mode 100644 index 0000000000000..0d1a698ec1fe0 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/StreamHIP.hip.cxx @@ -0,0 +1,41 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 StreamHIP.hip.cxx +/// \brief +/// + +#include "EC0trackingHIP/StreamHIP.h" + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +StreamHIP::StreamHIP() +{ + (void)hipStreamCreateWithFlags(&mStream, hipStreamNonBlocking); +} + +StreamHIP::~StreamHIP() // NOLINT: clang-tidy doesn't understand hip macro magic, and thinks this is trivial +{ + (void)hipStreamDestroy(mStream); +} + +const hipStream_t& StreamHIP::get() const +{ + return mStream; +} + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/UtilsHIP.hip.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/UtilsHIP.hip.cxx new file mode 100644 index 0000000000000..503b9e11c29ad --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/UtilsHIP.hip.cxx @@ -0,0 +1,172 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 UtilsHIP.hip.cxx +/// \brief +/// + +#include +#include +#include +#include + +#include +#include "EC0trackingHIP/ContextHIP.h" +#include "EC0trackingHIP/UtilsHIP.h" + +namespace +{ + +int roundUp(const int numToRound, const int multiple) +{ + if (multiple == 0) { + return numToRound; + } + int remainder{numToRound % multiple}; + if (remainder == 0) { + return numToRound; + } + return numToRound + multiple - remainder; +} + +int findNearestDivisor(const int numToRound, const int divisor) +{ + if (numToRound > divisor) { + return divisor; + } + int result = numToRound; + while (divisor % result != 0) { + ++result; + } + + return result; +} + +} // namespace + +namespace o2 +{ +namespace ecl +{ +namespace GPU +{ + +void Utils::HostHIP::checkHIPError(const hipError_t error, const char* file, const int line) +{ + if (error != hipSuccess) { + std::ostringstream errorString{}; + errorString << file << ":" << line << " HIP API returned error [" << hipGetErrorString(error) << "] (code " + << error << ")" << std::endl; + throw std::runtime_error{errorString.str()}; + } +} + +dim3 Utils::HostHIP::getBlockSize(const int colsNum) +{ + return getBlockSize(colsNum, 1); +} + +dim3 Utils::HostHIP::getBlockSize(const int colsNum, const int rowsNum) +{ + const DeviceProperties& deviceProperties = ContextHIP::getInstance().getDeviceProperties(); + return getBlockSize(colsNum, rowsNum, deviceProperties.streamProcessors / deviceProperties.maxBlocksPerSM); +} + +dim3 Utils::HostHIP::getBlockSize(const int colsNum, const int rowsNum, const int maxThreadsPerBlock) +{ + const DeviceProperties& deviceProperties = ContextHIP::getInstance().getDeviceProperties(); + int xThreads = std::max(std::min(colsNum, static_cast(deviceProperties.maxThreadsDim.x)), 1); + int yThreads = std::max(std::min(rowsNum, static_cast(deviceProperties.maxThreadsDim.y)), 1); + const int totalThreads = roundUp(std::min(xThreads * yThreads, maxThreadsPerBlock), + static_cast(deviceProperties.warpSize)); + if (xThreads > yThreads) { + xThreads = findNearestDivisor(xThreads, totalThreads); + yThreads = totalThreads / xThreads; + + } else { + yThreads = findNearestDivisor(yThreads, totalThreads); + xThreads = totalThreads / yThreads; + } + + return dim3{static_cast(xThreads), static_cast(yThreads)}; +} + +dim3 Utils::HostHIP::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum) +{ + return getBlocksGrid(threadsPerBlock, rowsNum, 1); +} + +dim3 Utils::HostHIP::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum, const int colsNum) +{ + return dim3{1 + (rowsNum - 1) / threadsPerBlock.x, 1 + (colsNum - 1) / threadsPerBlock.y}; +} + +void Utils::HostHIP::gpuMalloc(void** p, const int size) +{ + checkHIPError(hipMalloc(p, size), __FILE__, __LINE__); +} + +void Utils::HostHIP::gpuFree(void* p) +{ + checkHIPError(hipFree(p), __FILE__, __LINE__); +} + +void Utils::HostHIP::gpuMemset(void* p, int value, int size) +{ + checkHIPError(hipMemset(p, value, size), __FILE__, __LINE__); +} + +void Utils::HostHIP::gpuMemcpyHostToDevice(void* dst, const void* src, int size) +{ + checkHIPError(hipMemcpy(dst, src, size, hipMemcpyHostToDevice), __FILE__, __LINE__); +} + +void Utils::HostHIP::gpuMemcpyHostToDeviceAsync(void* dst, const void* src, int size, hipStream_t& stream) +{ + checkHIPError(hipMemcpyAsync(dst, src, size, hipMemcpyHostToDevice, stream), __FILE__, __LINE__); +} + +void Utils::HostHIP::gpuMemcpyDeviceToHost(void* dst, const void* src, int size) +{ + checkHIPError(hipMemcpy(dst, src, size, hipMemcpyDeviceToHost), __FILE__, __LINE__); +} + +// void Utils::HostHIP::gpuStartProfiler() +// { +// checkHIPError(hipProfilerStart(), __FILE__, __LINE__); +// } + +// void Utils::HostHIP::gpuStopProfiler() +// { +// checkHIPError(hipProfilerStop(), __FILE__, __LINE__); +// } + +GPUd() int Utils::DeviceHIP::getLaneIndex() +{ + uint32_t laneIndex; + asm volatile("mov.u32 %0, %%laneid;" + : "=r"(laneIndex)); + return static_cast(laneIndex); +} + +// GPUd() int Utils::Device::shareToWarp(const int value, const int laneIndex) +// { +// cooperative_groups::coalesced_group threadGroup = cooperative_groups::coalesced_threads(); +// return threadGroup.shfl(value, laneIndex); +// } + +// GPUd() int Utils::Device::gpuAtomicAdd(int* p, const int incrementSize) +// { +// return atomicAdd(p, incrementSize); +// } + +} // namespace GPU +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/VertexerTraitsEC0HIP.hip.cxx b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/VertexerTraitsEC0HIP.hip.cxx new file mode 100644 index 0000000000000..d38c7c9577ea0 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/hip/src/VertexerTraitsEC0HIP.hip.cxx @@ -0,0 +1,510 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 VertexerTraitsEC0HIP.hip.cxx +/// \brief +/// \author matteo.concas@cern.ch + +#include +#include +#include +#include +#include + +#include "EC0tracking/MathUtils.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/ClusterLines.h" +#include "EC0tracking/Tracklet.h" + +#include "EC0trackingHIP/UtilsHIP.h" +#include "EC0trackingHIP/ClusterLinesHIP.h" +#include "EC0trackingHIP/ContextHIP.h" +#include "EC0trackingHIP/StreamHIP.h" +#include "EC0trackingHIP/VertexerTraitsEC0HIP.h" + +namespace o2 +{ +namespace ecl +{ + +using constants::ecl::LayersRCoordinate; +using constants::ecl::LayersZCoordinate; +using constants::ecl::VertexerHistogramVolume; +using constants::index_table::PhiBins; +using constants::index_table::ZBins; +using constants::math::TwoPi; +using index_table_utils::getPhiBinIndex; +using index_table_utils::getZBinIndex; +using math_utils::getNormalizedPhiCoordinate; + +GPUh() void gpuThrowOnError() +{ + hipError_t error = hipGetLastError(); + + if (error != hipSuccess) { + std::ostringstream errorString{}; + errorString << "CUDA API returned error [" << hipGetErrorString(error) << "] (code " << error << ")" << std::endl; + throw std::runtime_error{errorString.str()}; + } +} + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +VertexerTraitsEC0HIP::VertexerTraitsEC0HIP() +{ + setIsGPU(true); + std::cout << "[DEBUG] Creating file: dbg_EC0VertexerHIP.root" << std::endl; + mDebugger = new StandaloneDebugger("dbg_EC0VertexerHIP.root"); +} + +VertexerTraitsEC0HIP::~VertexerTraitsEC0HIP() +{ + delete mDebugger; +} +#else +VertexerTraitsEC0HIP::VertexerTraitsEC0HIP() +{ + setIsGPU(true); +} + +#endif + +void VertexerTraitsEC0HIP::initialise(ROframe* event) +{ + reset(); + arrangeClusters(event); + mStoreVertexerGPUPtr = mStoreVertexerGPU.initialise(mClusters, mIndexTables); +} + +namespace GPU +{ + +template +GPUd() void printOnThread(const unsigned int tId, const char* str, Args... args) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == tId) { + printf(str, args...); + } +} + +GPUd() void printVectorOnThread(const char* name, VectorHIP& vector, size_t size, const unsigned int tId = 0) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == tId) { + printf("vector %s :", name); + for (size_t i{0}; i < size; ++i) { + printf("%d ", vector[i]); + } + printf("\n"); + } +} + +GPUg() void printVectorKernel(DeviceStoreVertexerHIP* store, const unsigned int threadId) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == threadId) { + for (int i{0}; i < store->getConfig().histConf.nBinsXYZ[0] - 1; ++i) { + printf("%d: %d\n", i, store->getHistogramXYZ()[0].get()[i]); + } + printf("\n"); + for (int i{0}; i < store->getConfig().histConf.nBinsXYZ[1] - 1; ++i) { + printf("%d: %d\n", i, store->getHistogramXYZ()[1].get()[i]); + } + printf("\n"); + for (int i{0}; i < store->getConfig().histConf.nBinsXYZ[2] - 1; ++i) { + printf("%d: %d\n", i, store->getHistogramXYZ()[2].get()[i]); + } + printf("\n"); + } +} + +GPUg() void dumpMaximaKernel(DeviceStoreVertexerHIP* store, const unsigned int threadId) +{ + if (blockIdx.x * blockDim.x + threadIdx.x == threadId) { + printf("XmaxBin: %d at index: %d | YmaxBin: %d at index: %d | ZmaxBin: %d at index: %d\n", + store->getTmpVertexPositionBins()[0].value, store->getTmpVertexPositionBins()[0].key, + store->getTmpVertexPositionBins()[1].value, store->getTmpVertexPositionBins()[1].key, + store->getTmpVertexPositionBins()[2].value, store->getTmpVertexPositionBins()[2].key); + } +} + +GPUg() void trackleterKernel( + DeviceStoreVertexerHIP* store, + const TrackletingLayerOrder layerOrder, + const float phiCut) +{ + const size_t nClustersMiddleLayer = store->getClusters()[1].size(); + for (unsigned int currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < nClustersMiddleLayer; currentClusterIndex += blockDim.x * gridDim.x) { + if (currentClusterIndex < nClustersMiddleLayer) { + int storedTracklets{0}; + const unsigned int stride{currentClusterIndex * store->getConfig().maxTrackletsPerCluster}; + const Cluster& currentCluster = store->getClusters()[1][currentClusterIndex]; // assign-constructor may be a problem, check + const VertexerLayerName adjacentLayerIndex{layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer ? VertexerLayerName::innermostLayer : VertexerLayerName::outerLayer}; + const int4 selectedBinsRect{VertexerTraitsEC0::getBinsRect(currentCluster, static_cast(adjacentLayerIndex), 0.f, 50.f, phiCut / 2)}; + if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { + int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; + if (phiBinsNum < 0) { + phiBinsNum += PhiBins; + } + const int nClustersAdjacentLayer = store->getClusters()[static_cast(adjacentLayerIndex)].size(); + for (size_t iPhiBin{static_cast(selectedBinsRect.y)}, iPhiCount{0}; (int)iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) { + const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int firstRowClusterIndex{store->getIndexTable(adjacentLayerIndex)[firstBinIndex]}; + const int maxRowClusterIndex{store->getIndexTable(adjacentLayerIndex)[firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1]}; + for (int iAdjacentCluster{firstRowClusterIndex}; iAdjacentCluster < maxRowClusterIndex && iAdjacentCluster < nClustersAdjacentLayer; ++iAdjacentCluster) { + const Cluster& adjacentCluster = store->getClusters()[static_cast(adjacentLayerIndex)][iAdjacentCluster]; // assign-constructor may be a problem, check + if (gpu::GPUCommonMath::Abs(currentCluster.phiCoordinate - adjacentCluster.phiCoordinate) < phiCut) { + if (storedTracklets < store->getConfig().maxTrackletsPerCluster) { + if (layerOrder == TrackletingLayerOrder::fromInnermostToMiddleLayer) { + store->getDuplets01().emplace(stride + storedTracklets, iAdjacentCluster, currentClusterIndex, adjacentCluster, currentCluster); + } else { + store->getDuplets12().emplace(stride + storedTracklets, currentClusterIndex, iAdjacentCluster, currentCluster, adjacentCluster); + } + ++storedTracklets; + } else { + printf("debug: leaving tracklet behind\n"); + } + } + } + } + } + store->getNFoundTracklets(layerOrder).emplace(currentClusterIndex, storedTracklets); + } + } +} + +GPUg() void trackletSelectionKernel( + DeviceStoreVertexerHIP* store, + const unsigned char isInitRun = false, + const float tanLambdaCut = 0.025f, + const float phiCut = 0.002f) +{ + const size_t nClustersMiddleLayer = store->getClusters()[1].size(); + for (size_t currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < nClustersMiddleLayer; currentClusterIndex += blockDim.x * gridDim.x) { + const int stride{static_cast(currentClusterIndex * store->getConfig().maxTrackletsPerCluster)}; + int validTracklets{0}; + for (int iTracklet12{0}; iTracklet12 < store->getNFoundTracklets(TrackletingLayerOrder::fromMiddleToOuterLayer)[currentClusterIndex]; ++iTracklet12) { + for (int iTracklet01{0}; iTracklet01 < store->getNFoundTracklets(TrackletingLayerOrder::fromInnermostToMiddleLayer)[currentClusterIndex] && validTracklets < store->getConfig().maxTrackletsPerCluster; ++iTracklet01) { + const float deltaTanLambda{gpu::GPUCommonMath::Abs(store->getDuplets01()[stride + iTracklet01].tanLambda - store->getDuplets12()[stride + iTracklet12].tanLambda)}; + const float deltaPhi{gpu::GPUCommonMath::Abs(store->getDuplets01()[stride + iTracklet01].phiCoordinate - store->getDuplets12()[stride + iTracklet12].phiCoordinate)}; + if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != store->getConfig().maxTrackletsPerCluster) { + assert(store->getDuplets01()[stride + iTracklet01].secondClusterIndex == store->getDuplets12()[stride + iTracklet12].firstClusterIndex); + if (!isInitRun) { + store->getLines().emplace(store->getNExclusiveFoundLines()[currentClusterIndex] + validTracklets, store->getDuplets01()[stride + iTracklet01], store->getClusters()[0].get(), store->getClusters()[1].get()); +#ifdef _ALLOW_DEBUG_TREES_ITS_ + store->getDupletIndices()[0].emplace(store->getNExclusiveFoundLines()[currentClusterIndex] + validTracklets, stride + iTracklet01); + store->getDupletIndices()[1].emplace(store->getNExclusiveFoundLines()[currentClusterIndex] + validTracklets, stride + iTracklet12); +#endif + } + ++validTracklets; + } + } + } + if (isInitRun) { + store->getNFoundLines().emplace(currentClusterIndex, validTracklets); + if (validTracklets >= store->getConfig().maxTrackletsPerCluster) { + printf("Warning: not enough space for tracklet selection, some lines will be left behind\n"); + } + } + } + if (blockIdx.x * blockDim.x + threadIdx.x == 0) { + // first thread I want to write an empty line after last found, as adebug flag. Might delete later + store->getLines().emplace(store->getNExclusiveFoundLines()[store->getClusters()[1].size() - 1] + store->getNFoundLines()[store->getClusters()[1].size() - 1]); + } +} + +GPUg() void computeCentroidsKernel(DeviceStoreVertexerHIP* store, + const float pairCut) +{ + const unsigned int nLines = store->getNExclusiveFoundLines()[store->getClusters()[1].size() - 1] + store->getNFoundLines()[store->getClusters()[1].size() - 1]; + const unsigned int maxIterations{nLines * (nLines - 1) / 2}; + for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < maxIterations; currentThreadIndex += blockDim.x * gridDim.x) { + int iFirstLine = currentThreadIndex / nLines; + int iSecondLine = currentThreadIndex % nLines; + if (iSecondLine <= iFirstLine) { + iFirstLine = nLines - iFirstLine - 2; + iSecondLine = nLines - iSecondLine - 1; + } + if (Line::getDCA(store->getLines()[iFirstLine], store->getLines()[iSecondLine]) < pairCut) { + ClusterLinesHIP cluster{store->getLines()[iFirstLine], store->getLines()[iSecondLine]}; + if (cluster.getVertex()[0] * cluster.getVertex()[0] + cluster.getVertex()[1] * cluster.getVertex()[1] < 1.98f * 1.98f) { + // printOnThread(0, "xCentr: %f, yCentr: %f \n", cluster.getVertex()[0], cluster.getVertex()[1]); + store->getXYCentroids().emplace(2 * currentThreadIndex, cluster.getVertex()[0]); + store->getXYCentroids().emplace(2 * currentThreadIndex + 1, cluster.getVertex()[1]); + } else { + // writing some data anyway outside the histogram, they will not be put in the histogram, by construction. + store->getXYCentroids().emplace(2 * currentThreadIndex, 2 * store->getConfig().histConf.lowHistBoundariesXYZ[0]); + store->getXYCentroids().emplace(2 * currentThreadIndex + 1, 2 * store->getConfig().histConf.lowHistBoundariesXYZ[1]); + } + } else { + // writing some data anyway outside the histogram, they will not be put in the histogram, by construction. + store->getXYCentroids().emplace(2 * currentThreadIndex, 2 * store->getConfig().histConf.lowHistBoundariesXYZ[0]); + store->getXYCentroids().emplace(2 * currentThreadIndex + 1, 2 * store->getConfig().histConf.lowHistBoundariesXYZ[1]); + } + } +} + +GPUg() void computeZCentroidsKernel(DeviceStoreVertexerHIP* store, + const float pairCut, const int binOpeningX, const int binOpeningY) +{ + const unsigned int nLines = store->getNExclusiveFoundLines()[store->getClusters()[1].size() - 1] + store->getNFoundLines()[store->getClusters()[1].size() - 1]; + for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < nLines; currentThreadIndex += blockDim.x * gridDim.x) { + // printOnThread(0, "Max X: %d Max Y %d \n", store->getTmpVertexPositionBins()[0].value, store->getTmpVertexPositionBins()[1].value); + if (store->getTmpVertexPositionBins()[0].value || store->getTmpVertexPositionBins()[1].value) { + float tmpX{store->getConfig().histConf.lowHistBoundariesXYZ[0] + store->getTmpVertexPositionBins()[0].key * store->getConfig().histConf.binSizeHistX + store->getConfig().histConf.binSizeHistX / 2}; + int sumWX{store->getTmpVertexPositionBins()[0].value}; + float wX{tmpX * store->getTmpVertexPositionBins()[0].value}; + for (int iBin{gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[0].key - binOpeningX)}; iBin < gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[0].key + binOpeningX + 1, store->getConfig().histConf.nBinsXYZ[0] - 1); ++iBin) { + if (iBin != store->getTmpVertexPositionBins()[0].key) { + wX += (store->getConfig().histConf.lowHistBoundariesXYZ[0] + iBin * store->getConfig().histConf.binSizeHistX + store->getConfig().histConf.binSizeHistX / 2) * store->getHistogramXYZ()[0].get()[iBin]; + sumWX += store->getHistogramXYZ()[0].get()[iBin]; + } + } + float tmpY{store->getConfig().histConf.lowHistBoundariesXYZ[1] + store->getTmpVertexPositionBins()[1].key * store->getConfig().histConf.binSizeHistY + store->getConfig().histConf.binSizeHistY / 2}; + int sumWY{store->getTmpVertexPositionBins()[1].value}; + float wY{tmpY * store->getTmpVertexPositionBins()[1].value}; + for (int iBin{gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[1].key - binOpeningY)}; iBin < gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[1].key + binOpeningY + 1, store->getConfig().histConf.nBinsXYZ[1] - 1); ++iBin) { + if (iBin != store->getTmpVertexPositionBins()[1].key) { + wY += (store->getConfig().histConf.lowHistBoundariesXYZ[1] + iBin * store->getConfig().histConf.binSizeHistY + store->getConfig().histConf.binSizeHistY / 2) * store->getHistogramXYZ()[1].get()[iBin]; + sumWY += store->getHistogramXYZ()[1].get()[iBin]; + } + } + store->getBeamPosition().emplace(0, wX / sumWX); + store->getBeamPosition().emplace(1, wY / sumWY); + float fakeBeamPoint1[3] = {store->getBeamPosition()[0], store->getBeamPosition()[1], -1}; // get two points laying at different z, to create line object + float fakeBeamPoint2[3] = {store->getBeamPosition()[0], store->getBeamPosition()[1], 1}; + Line pseudoBeam{fakeBeamPoint1, fakeBeamPoint2}; + if (Line::getDCA(store->getLines()[currentThreadIndex], pseudoBeam) < pairCut) { + ClusterLinesHIP cluster{store->getLines()[currentThreadIndex], pseudoBeam}; + store->getZCentroids().emplace(currentThreadIndex, cluster.getVertex()[2]); + } else { + store->getZCentroids().emplace(currentThreadIndex, 2 * store->getConfig().histConf.lowHistBoundariesXYZ[2]); + } + } + } +} + +GPUg() void computeVertexKernel(DeviceStoreVertexerHIP* store, const int vertIndex, const int minContributors, const int binOpeningZ) +{ + for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; (int)currentThreadIndex < binOpeningZ; currentThreadIndex += blockDim.x * gridDim.x) { + if (currentThreadIndex == 0) { + if (store->getTmpVertexPositionBins()[2].value > 1 && (store->getTmpVertexPositionBins()[0].value || store->getTmpVertexPositionBins()[1].value)) { + float z{store->getConfig().histConf.lowHistBoundariesXYZ[2] + store->getTmpVertexPositionBins()[2].key * store->getConfig().histConf.binSizeHistZ + store->getConfig().histConf.binSizeHistZ / 2}; + float ex{0.f}; + float ey{0.f}; + float ez{0.f}; + int sumWZ{store->getTmpVertexPositionBins()[2].value}; + float wZ{z * store->getTmpVertexPositionBins()[2].value}; + for (int iBin{gpu::GPUCommonMath::Max(0, store->getTmpVertexPositionBins()[2].key - binOpeningZ)}; iBin < gpu::GPUCommonMath::Min(store->getTmpVertexPositionBins()[2].key + binOpeningZ + 1, store->getConfig().histConf.nBinsXYZ[2] - 1); ++iBin) { + if (iBin != store->getTmpVertexPositionBins()[2].key) { + wZ += (store->getConfig().histConf.lowHistBoundariesXYZ[2] + iBin * store->getConfig().histConf.binSizeHistZ + store->getConfig().histConf.binSizeHistZ / 2) * store->getHistogramXYZ()[2].get()[iBin]; + sumWZ += store->getHistogramXYZ()[2].get()[iBin]; + } + store->getHistogramXYZ()[2].get()[iBin] = 0; + } + if (sumWZ > minContributors || vertIndex == 0) { + store->getVertices().emplace(vertIndex, store->getBeamPosition()[0], store->getBeamPosition()[1], wZ / sumWZ, ex, ey, ez, sumWZ); + } else { + store->getVertices().emplace(vertIndex); + } + } else { + store->getVertices().emplace(vertIndex); + } + } + } +} +} // namespace GPU + +void VertexerTraitsEC0HIP::computeTracklets() +{ + if (!mClusters[1].size()) { + std::cout << "\t\tno clusters on layer 1. Returning.\n"; + return; + } + const dim3 threadsPerBlock{GPU::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{GPU::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + + hipLaunchKernelGGL((GPU::trackleterKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + getDeviceContextPtr(), + GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, + mVrtParams.phiCut); + + hipLaunchKernelGGL((GPU::trackleterKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + getDeviceContextPtr(), + GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, + mVrtParams.phiCut); + + gpuThrowOnError(); + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { + mDebugger->fillCombinatoricsTree(mClusters, + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mEvent); + } +#endif +} + +void VertexerTraitsEC0HIP::computeTrackletMatching() +{ + if (!mClusters[1].size()) { + std::cout << "\t\tno clusters on layer 1. Returning.\n"; + return; + } + const dim3 threadsPerBlock{GPU::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{GPU::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); + + hipLaunchKernelGGL((GPU::trackletSelectionKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + getDeviceContextPtr(), + true, // isInitRun + mVrtParams.tanLambdaCut, + mVrtParams.phiCut); + + (void)hipcub::DeviceScan::ExclusiveSum(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + mStoreVertexerGPU.getNFoundLines().get(), + mStoreVertexerGPU.getNExclusiveFoundLines().get(), + mClusters[1].size()); + + hipLaunchKernelGGL((GPU::trackletSelectionKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, + getDeviceContextPtr(), + false, // isInitRun + mVrtParams.tanLambdaCut, + mVrtParams.phiCut); + + gpuThrowOnError(); + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + if (isDebugFlag(VertexerDebug::TrackletTreeAll)) { + mDebugger->fillTrackletSelectionTree(mClusters, + mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mStoreVertexerGPU.getDupletIndicesFromGPU(), + mEvent); + } + mTracklets = mStoreVertexerGPU.getLinesFromGPU(); + if (isDebugFlag(VertexerDebug::LineTreeAll)) { + mDebugger->fillPairsInfoTree(mTracklets, mEvent); + } + if (isDebugFlag(VertexerDebug::LineSummaryAll)) { + mDebugger->fillLinesSummaryTree(mTracklets, mEvent); + } +#endif +} + +void VertexerTraitsEC0HIP::computeVertices() +{ + if (!mClusters[1].size()) { + std::cout << "\t\tno clusters on layer 1. Returning.\n"; + return; + } + const dim3 threadsPerBlock{GPU::Utils::HostHIP::getBlockSize(mClusters[1].capacity())}; + const dim3 blocksGrid{GPU::Utils::HostHIP::getBlocksGrid(threadsPerBlock, mClusters[1].capacity())}; + size_t bufferSize = mStoreVertexerGPU.getConfig().tmpCUBBufferSize * sizeof(int); + int nLines = mStoreVertexerGPU.getNExclusiveFoundLines().getElementFromDevice(mClusters[1].size() - 1) + mStoreVertexerGPU.getNFoundLines().getElementFromDevice(mClusters[1].size() - 1); + int nCentroids{static_cast(nLines * (nLines - 1) / 2)}; + int* histogramXY[2] = {mStoreVertexerGPU.getHistogramXYZ()[0].get(), mStoreVertexerGPU.getHistogramXYZ()[1].get()}; + float tmpArrayLow[2] = {mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[1]}; + float tmpArrayHigh[2] = {mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[0], mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[1]}; + hipLaunchKernelGGL((GPU::computeCentroidsKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), + mVrtParams.histPairCut); + + (void)hipcub::DeviceHistogram::MultiHistogramEven<2, 2>(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage + bufferSize, // temp_storage_bytes + mStoreVertexerGPU.getXYCentroids().get(), // d_samples + histogramXY, // d_histogram + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ, // num_levels + tmpArrayLow, // lower_level + tmpArrayHigh, // fupper_level + nCentroids); // num_row_pixels + + (void)hipcub::DeviceReduce::ArgMax(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + histogramXY[0], + mStoreVertexerGPU.getTmpVertexPositionBins().get(), + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0]); + (void)hipcub::DeviceReduce::ArgMax(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + histogramXY[1], + mStoreVertexerGPU.getTmpVertexPositionBins().get() + 1, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0]); + + hipLaunchKernelGGL((GPU::computeZCentroidsKernel), dim3(blocksGrid), dim3(threadsPerBlock), 0, 0, getDeviceContextPtr(), mVrtParams.histPairCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[0], mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[1]); + + (void)hipcub::DeviceHistogram::HistogramEven(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), // d_temp_storage + bufferSize, // temp_storage_bytes + mStoreVertexerGPU.getZCentroids().get(), // d_samples + mStoreVertexerGPU.getHistogramXYZ()[2].get(), // d_histogram + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2], // num_levels + mStoreVertexerGPU.getConfig().histConf.lowHistBoundariesXYZ[2], // lower_level + mStoreVertexerGPU.getConfig().histConf.highHistBoundariesXYZ[2], // fupper_level + nLines); // num_row_pixels + + for (int iVertex{0}; iVertex < mStoreVertexerGPU.getConfig().nMaxVertices; ++iVertex) { + (void)hipcub::DeviceReduce::ArgMax(reinterpret_cast(mStoreVertexerGPU.getCUBTmpBuffer().get()), + bufferSize, + mStoreVertexerGPU.getHistogramXYZ()[2].get(), + mStoreVertexerGPU.getTmpVertexPositionBins().get() + 2, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2]); +#ifdef _ALLOW_DEBUG_TREES_ITS_ + if (isDebugFlag(VertexerDebug::HistCentroids) && !iVertex) { + mDebugger->fillXYZHistogramTree(std::array, 3>{mStoreVertexerGPU.getHistogramXYFromGPU()[0], + mStoreVertexerGPU.getHistogramXYFromGPU()[1], mStoreVertexerGPU.getHistogramZFromGPU()}, + std::array{mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[0] - 1, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[1] - 1, + mStoreVertexerGPU.getConfig().histConf.nBinsXYZ[2] - 1}); + } +#endif + hipLaunchKernelGGL((GPU::computeVertexKernel), dim3(blocksGrid), dim3(5), 0, 0, getDeviceContextPtr(), iVertex, mVrtParams.clusterContributorsCut, mStoreVertexerGPU.getConfig().histConf.binSpanXYZ[2]); + } + std::vector vertices; + vertices.resize(mStoreVertexerGPU.getConfig().nMaxVertices); + mStoreVertexerGPU.getVertices().copyIntoSizedVector(vertices); + + for (auto& vertex : vertices) { + if (vertex.realVertex) { + mVertices.emplace_back(vertex.xCoord, vertex.yCoord, vertex.zCoord, std::array{0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, vertex.contributors, 0.f, -9); + } + } + + gpuThrowOnError(); +} + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +void VertexerTraitsEC0HIP::computeMCFiltering() +{ + std::vector tracklets01 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector tracklets12 = mStoreVertexerGPU.getRawDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); + std::vector labels01 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer); + std::vector labels12 = mStoreVertexerGPU.getNFoundTrackletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer); + VertexerStoreConfigurationGPU tmpGPUConf; + const int stride = tmpGPUConf.maxTrackletsPerCluster; + + filterTrackletsWithMC(tracklets01, tracklets12, labels01, labels12, stride); + mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, labels01); + mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer, tracklets01); + mStoreVertexerGPU.updateFoundDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, labels12); + mStoreVertexerGPU.updateDuplets(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer, tracklets12); + + if (isDebugFlag(VertexerDebug::CombinatoricsTreeAll)) { + mDebugger->fillCombinatoricsTree(mClusters, + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromInnermostToMiddleLayer), + mStoreVertexerGPU.getDupletsFromGPU(GPU::TrackletingLayerOrder::fromMiddleToOuterLayer), + mEvent); + } +} +#endif + +VertexerTraitsEC0* createVertexerTraitsEC0HIP() +{ + return new VertexerTraitsEC0HIP; +} + +} // namespace ecl +} // namespace o2 diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ArrayUtils.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ArrayUtils.h new file mode 100644 index 0000000000000..fa45ad26a9cfa --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ArrayUtils.h @@ -0,0 +1,52 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ArrayUtils.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_ARRAYUTILS_H_ +#define TRACKINGEC0__INCLUDE_ARRAYUTILS_H_ + +#include +#include +#include + +namespace o2 +{ +namespace ecl +{ +namespace CA +{ + +namespace ArrayUtils +{ +template +constexpr std::array fillArray(Initializer, std::index_sequence); +template +constexpr std::array fillArray(Initializer); +} // namespace ArrayUtils + +template +constexpr std::array ArrayUtils::fillArray(Initializer initializer, std::index_sequence) +{ + return std::array{{initializer(Is)...}}; +} + +template +constexpr std::array ArrayUtils::fillArray(Initializer initializer) +{ + return ArrayUtils::fillArray(initializer, std::make_index_sequence{}); +} +} // namespace CA +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_ARRAYUTILS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Cell.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Cell.h new file mode 100644 index 0000000000000..8656c979d1ebf --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Cell.h @@ -0,0 +1,91 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Cell.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_CACELL_H_ +#define TRACKINGEC0__INCLUDE_CACELL_H_ + +#ifndef __OPENCL__ +#include +#include +#endif + +#include "EC0tracking/Definitions.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ + +class Cell final +{ + public: + GPU_DEVICE Cell(const int, const int, const int, const int, const int, const float3&, const float); + + GPUhdni() int getFirstClusterIndex() const; + GPUhdni() int getSecondClusterIndex() const; + GPUhdni() int getThirdClusterIndex() const; + GPU_HOST_DEVICE int getFirstTrackletIndex() const; + int getSecondTrackletIndex() const; + int getLevel() const; + float getCurvature() const; + const float3& getNormalVectorCoordinates() const; + void setLevel(const int level); + + private: + const int mFirstClusterIndex; + const int mSecondClusterIndex; + const int mThirdClusterIndex; + const int mFirstTrackletIndex; + const int mSecondTrackletIndex; + const float3 mNormalVectorCoordinates; + const float mCurvature; + int mLevel; +}; + +inline GPU_DEVICE Cell::Cell(const int firstClusterIndex, const int secondClusterIndex, const int thirdClusterIndex, + const int firstTrackletIndex, const int secondTrackletIndex, + const float3& normalVectorCoordinates, const float curvature) + : mFirstClusterIndex{firstClusterIndex}, + mSecondClusterIndex{secondClusterIndex}, + mThirdClusterIndex{thirdClusterIndex}, + mFirstTrackletIndex(firstTrackletIndex), + mSecondTrackletIndex(secondTrackletIndex), + mNormalVectorCoordinates(normalVectorCoordinates), + mCurvature{curvature}, + mLevel{1} +{ + // Nothing to do +} + +GPUhdi() int Cell::getFirstClusterIndex() const { return mFirstClusterIndex; } + +GPUhdi() int Cell::getSecondClusterIndex() const { return mSecondClusterIndex; } + +GPUhdi() int Cell::getThirdClusterIndex() const { return mThirdClusterIndex; } + +GPU_HOST_DEVICE inline int Cell::getFirstTrackletIndex() const { return mFirstTrackletIndex; } + +inline int Cell::getSecondTrackletIndex() const { return mSecondTrackletIndex; } + +inline int Cell::getLevel() const { return mLevel; } + +inline float Cell::getCurvature() const { return mCurvature; } + +inline const float3& Cell::getNormalVectorCoordinates() const { return mNormalVectorCoordinates; } + +inline void Cell::setLevel(const int level) { mLevel = level; } +} // namespace ecl +} // namespace o2 +#endif /* TRACKINGEC0__INCLUDE_CACELL_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Cluster.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Cluster.h new file mode 100644 index 0000000000000..2b59b1e8057a5 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Cluster.h @@ -0,0 +1,62 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Cluster.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_CACLUSTER_H_ +#define TRACKINGEC0__INCLUDE_CACLUSTER_H_ + +#ifndef __OPENCL__ +#include +#endif + +#include "EC0tracking/Definitions.h" +#include "EC0tracking/MathUtils.h" +#include "EC0tracking/IndexTableUtils.h" + +namespace o2 +{ +namespace ecl +{ + +struct Cluster final { + Cluster() = default; + Cluster(const float x, const float y, const float z, const int idx); + Cluster(const int, const Cluster&); + Cluster(const int, const float3&, const Cluster&); + void Init(const int, const float3&, const Cluster&); + + float xCoordinate; // = -999.f; + float yCoordinate; // = -999.f; + float zCoordinate; // = -999.f; + float phiCoordinate; // = -999.f; + float rCoordinate; // = -999.f; + int clusterId; // = -1; + int indexTableBinIndex; // = -1; +}; + +struct TrackingFrameInfo { + TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, GPUArrayEC0&& posTF, GPUArrayEC0&& covTF); + TrackingFrameInfo() = default; + + float xCoordinate; + float yCoordinate; + float zCoordinate; + float xTrackingFrame; + float alphaTrackingFrame; + GPUArrayEC0 positionTrackingFrame = {-1., -1.}; + GPUArrayEC0 covarianceTrackingFrame = {999., 999., 999.}; +}; +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_CACLUSTER_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ClusterLines.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ClusterLines.h new file mode 100644 index 0000000000000..ff1ddddad9f89 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ClusterLines.h @@ -0,0 +1,262 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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_ENDCAPS_TRACKING_LINE_H_ +#define O2_ENDCAPS_TRACKING_LINE_H_ + +#include +#include +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/Tracklet.h" +#include "GPUCommonMath.h" + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +#include +#endif + +namespace o2 +{ +namespace ecl +{ + +struct Line final { + GPUhd() Line(); + GPUhd() Line(const Line&); + Line(std::array firstPoint, std::array secondPoint); + GPUhd() Line(const float firstPoint[3], const float secondPoint[3]); + GPUhd() Line(const Tracklet&, const Cluster*, const Cluster*); + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters, const int evId); +#endif + + inline static float getDistanceFromPoint(const Line& line, const std::array& point); + GPUhd() static float getDistanceFromPoint(const Line& line, const float point[3]); + static std::array getDCAComponents(const Line& line, const std::array point); + GPUhd() static void getDCAComponents(const Line& line, const float point[3], float destArray[6]); + GPUhd() static float getDCA(const Line&, const Line&, const float precision = 1e-14); + static bool areParallel(const Line&, const Line&, const float precision = 1e-14); + + float originPoint[3], cosinesDirector[3]; // std::array originPoint, cosinesDirector; + float weightMatrix[6] = {1., 0., 0., 1., 0., 1.}; // std::array weightMatrix; + unsigned char isEmpty = false; + // weightMatrix is a symmetric matrix internally stored as + // 0 --> row = 0, col = 0 + // 1 --> 0,1 + // 2 --> 0,2 + // 3 --> 1,1 + // 4 --> 1,2 + // 5 --> 2,2 + // Debug quantities +#ifdef _ALLOW_DEBUG_TREES_ITS_ + int evtId; // -1 if fake +#endif +}; + +GPUhdi() Line::Line() : weightMatrix{1., 0., 0., 1., 0., 1.} +{ + isEmpty = true; +} + +GPUhdi() Line::Line(const Line& other) +{ + isEmpty = other.isEmpty; + for (int i{0}; i < 3; ++i) { + originPoint[i] = other.originPoint[i]; + cosinesDirector[i] = other.cosinesDirector[i]; + } + for (int i{0}; i < 6; ++i) { + weightMatrix[i] = other.weightMatrix[i]; + } +#ifdef _ALLOW_DEBUG_TREES_ITS_ + evtId = other.evtId; +#endif +} + +GPUhdi() Line::Line(const float firstPoint[3], const float secondPoint[3]) +{ + for (int i{0}; i < 3; ++i) { + originPoint[i] = firstPoint[i]; + cosinesDirector[i] = secondPoint[i] - firstPoint[i]; + } + + float inverseNorm{1.f / gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + + cosinesDirector[2] * cosinesDirector[2])}; + + for (int index{0}; index < 3; ++index) + cosinesDirector[index] *= inverseNorm; +} + +GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) +{ + originPoint[0] = innerClusters[tracklet.firstClusterIndex].xCoordinate; + originPoint[1] = innerClusters[tracklet.firstClusterIndex].yCoordinate; + originPoint[2] = innerClusters[tracklet.firstClusterIndex].zCoordinate; + + cosinesDirector[0] = outerClusters[tracklet.secondClusterIndex].xCoordinate - innerClusters[tracklet.firstClusterIndex].xCoordinate; + cosinesDirector[1] = outerClusters[tracklet.secondClusterIndex].yCoordinate - innerClusters[tracklet.firstClusterIndex].yCoordinate; + cosinesDirector[2] = outerClusters[tracklet.secondClusterIndex].zCoordinate - innerClusters[tracklet.firstClusterIndex].zCoordinate; + + float inverseNorm{1.f / gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + + cosinesDirector[2] * cosinesDirector[2])}; + + for (int index{0}; index < 3; ++index) + cosinesDirector[index] *= inverseNorm; +} + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters, const int evId) : evtId{evId} +{ + originPoint[0] = innerClusters[tracklet.firstClusterIndex].xCoordinate; + originPoint[1] = innerClusters[tracklet.firstClusterIndex].yCoordinate; + originPoint[2] = innerClusters[tracklet.firstClusterIndex].zCoordinate; + + cosinesDirector[0] = outerClusters[tracklet.secondClusterIndex].xCoordinate - innerClusters[tracklet.firstClusterIndex].xCoordinate; + cosinesDirector[1] = outerClusters[tracklet.secondClusterIndex].yCoordinate - innerClusters[tracklet.firstClusterIndex].yCoordinate; + cosinesDirector[2] = outerClusters[tracklet.secondClusterIndex].zCoordinate - innerClusters[tracklet.firstClusterIndex].zCoordinate; + + float inverseNorm{1.f / gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + + cosinesDirector[2] * cosinesDirector[2])}; + + for (int index{0}; index < 3; ++index) + cosinesDirector[index] *= inverseNorm; +} +#endif + +// static functions +inline float Line::getDistanceFromPoint(const Line& line, const std::array& point) +{ + float DCASquared{0}; + float cdelta{0}; + for (int i{0}; i < 3; ++i) { + cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); + } + for (int i{0}; i < 3; ++i) { + DCASquared += (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta) * + (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta); + } + return gpu::CAMath::Sqrt(DCASquared); +} + +GPUhdi() float Line::getDistanceFromPoint(const Line& line, const float point[3]) +{ + float DCASquared{0}; + float cdelta{0}; + for (int i{0}; i < 3; ++i) + cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); + for (int i{0}; i < 3; ++i) { + DCASquared += (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta) * + (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta); + } + return gpu::CAMath::Sqrt(DCASquared); +} + +GPUhdi() float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) +{ + float normalVector[3]; + normalVector[0] = firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2] - + firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]; + normalVector[1] = -firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2] + + firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]; + normalVector[2] = firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1] - + firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]; + + float norm{0.f}, distance{0.f}; + for (int i{0}; i < 3; ++i) { + norm += normalVector[i] * normalVector[i]; + distance += (secondLine.originPoint[i] - firstLine.originPoint[i]) * normalVector[i]; + } + if (norm > precision) { + return gpu::CAMath::Abs(distance / gpu::CAMath::Sqrt(norm)); + } else { +#if defined(__CUDACC__) || defined(__HIPCC__) + float stdOriginPoint[3]; + for (int i{0}; i < 3; ++i) { + stdOriginPoint[i] = secondLine.originPoint[1]; + } +#else + std::array stdOriginPoint = {}; + std::copy_n(secondLine.originPoint, 3, stdOriginPoint.begin()); +#endif + return getDistanceFromPoint(firstLine, stdOriginPoint); + } +} + +GPUhdi() void Line::getDCAComponents(const Line& line, const float point[3], float destArray[6]) +{ + float cdelta{0.}; + for (int i{0}; i < 3; ++i) + cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); + + destArray[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; + destArray[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; + destArray[5] = line.originPoint[2] - point[2] + line.cosinesDirector[2] * cdelta; + destArray[1] = std::sqrt(destArray[0] * destArray[0] + destArray[3] * destArray[3]); + destArray[2] = std::sqrt(destArray[0] * destArray[0] + destArray[5] * destArray[5]); + destArray[4] = std::sqrt(destArray[3] * destArray[3] + destArray[5] * destArray[5]); +} + +/// + +class ClusterLines final +{ + public: + ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine, + const bool weight = false); + ClusterLines(const Line& firstLine, const Line& secondLine); + void add(const int& lineLabel, const Line& line, const bool& weight = false); + void computeClusterCentroid(); + inline std::vector& getLabels() + { + return mLabels; + } + inline int getSize() const { return mLabels.size(); } + inline std::array getVertex() const { return mVertex; } + inline std::array getRMS2() const { return mRMS2; } + inline float getAvgDistance2() const { return mAvgDistance2; } + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + inline std::vector getLines() + { + return mLines; + } + void vote(const Line& line); + inline int getEventId() const { return mPoll; } + inline float getPurity() + { + auto id = getEventId(); + auto it = mMap.find(id); + assert(it != mMap.end()); + return (float)it->second / (float)mLabels.size(); + } +#endif + + protected: + std::array mAMatrix; // AX=B + std::array mBMatrix; // AX=B + std::vector mLabels; // labels + std::array mVertexCandidate; // vertex candidate + std::array mWeightMatrix; // weight matrix + std::array mVertex; // cluster centroid position + std::array mRMS2; // symmetric matrix: diagonal is RMS2 + float mAvgDistance2; // substitute for chi2 +#ifdef _ALLOW_DEBUG_TREES_ITS_ + std::vector mLines; + int mPoll; + int mNVotes; + std::unordered_map mMap; + int mSwitches; +#endif +}; + +} // namespace ecl +} // namespace o2 +#endif /* O2_ENDCAPS_TRACKING_LINE_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Configuration.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Configuration.h new file mode 100644 index 0000000000000..84840edef397e --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Configuration.h @@ -0,0 +1,196 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Configuration.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_CONFIGURATION_H_ +#define TRACKINGEC0__INCLUDE_CONFIGURATION_H_ + +#include +#include +#include +#include + +#include "EC0tracking/Constants.h" + +namespace o2 +{ +namespace ecl +{ + +template +class Configuration : public Param +{ + public: + static Configuration& getInstance() + { + static Configuration instance; + return instance; + } + Configuration(const Configuration&) = delete; + const Configuration& operator=(const Configuration&) = delete; + + private: + Configuration() = default; +}; + +struct TrackingParameters { + TrackingParameters& operator=(const TrackingParameters& t); + + int CellMinimumLevel(); + + /// General parameters + int ClusterSharing = 0; + int MinTrackLength = 7; + /// Trackleting cuts + float TrackletMaxDeltaPhi = 0.3f; + float TrackletMaxDeltaZ[constants::ecl::TrackletsPerRoad] = {0.1f, 0.1f, 0.3f, 0.3f, 0.3f, 0.3f}; + /// Cell finding cuts + float CellMaxDeltaTanLambda = 0.025f; + float CellMaxDCA[constants::ecl::CellsPerRoad] = {0.05f, 0.04f, 0.05f, 0.2f, 0.4f}; + float CellMaxDeltaPhi = 0.14f; + float CellMaxDeltaZ[constants::ecl::CellsPerRoad] = {0.2f, 0.4f, 0.5f, 0.6f, 3.0f}; + /// Neighbour finding cuts + float NeighbourMaxDeltaCurvature[constants::ecl::CellsPerRoad - 1] = {0.008f, 0.0025f, 0.003f, 0.0035f}; + float NeighbourMaxDeltaN[constants::ecl::CellsPerRoad - 1] = {0.002f, 0.0090f, 0.002f, 0.005f}; +}; + +struct MemoryParameters { + /// Memory coefficients + MemoryParameters& operator=(const MemoryParameters& t); + int MemoryOffset = 256; + float CellsMemoryCoefficients[constants::ecl::CellsPerRoad] = {2.3208e-08f, 2.104e-08f, 1.6432e-08f, 1.2412e-08f, 1.3543e-08f}; + float TrackletsMemoryCoefficients[constants::ecl::TrackletsPerRoad] = {0.0016353f, 0.0013627f, 0.000984f, 0.00078135f, 0.00057934f, 0.00052217f}; +}; + +inline int TrackingParameters::CellMinimumLevel() +{ + return MinTrackLength - constants::ecl::ClustersPerCell + 1; +} + +inline TrackingParameters& TrackingParameters::operator=(const TrackingParameters& t) +{ + this->ClusterSharing = t.ClusterSharing; + this->MinTrackLength = t.MinTrackLength; + /// Trackleting cuts + this->TrackletMaxDeltaPhi = t.TrackletMaxDeltaPhi; + for (int iT = 0; iT < constants::ecl::TrackletsPerRoad; ++iT) + this->TrackletMaxDeltaZ[iT] = t.TrackletMaxDeltaZ[iT]; + /// Cell finding cuts + this->CellMaxDeltaTanLambda = t.CellMaxDeltaTanLambda; + this->CellMaxDeltaPhi = t.CellMaxDeltaPhi; + for (int iC = 0; iC < constants::ecl::CellsPerRoad; ++iC) { + this->CellMaxDCA[iC] = t.CellMaxDCA[iC]; + this->CellMaxDeltaZ[iC] = t.CellMaxDeltaZ[iC]; + } + /// Neighbour finding cuts + for (int iC = 0; iC < constants::ecl::CellsPerRoad - 1; ++iC) { + this->NeighbourMaxDeltaCurvature[iC] = t.NeighbourMaxDeltaCurvature[iC]; + this->NeighbourMaxDeltaN[iC] = t.NeighbourMaxDeltaN[iC]; + } + return *this; +} + +inline MemoryParameters& MemoryParameters::operator=(const MemoryParameters& t) +{ + this->MemoryOffset = t.MemoryOffset; + for (int iC = 0; iC < constants::ecl::CellsPerRoad; ++iC) + this->CellsMemoryCoefficients[iC] = t.CellsMemoryCoefficients[iC]; + for (int iT = 0; iT < constants::ecl::TrackletsPerRoad; ++iT) + this->TrackletsMemoryCoefficients[iT] = t.TrackletsMemoryCoefficients[iT]; + return *this; +} + +struct VertexingParameters { + float zCut = 0.002f; //0.002f + float phiCut = 0.005f; //0.005f + float pairCut = 0.04f; + float clusterCut = 0.8f; + float histPairCut = 0.04f; + float tanLambdaCut = 0.002f; // tanLambda = deltaZ/deltaR + int clusterContributorsCut = 16; + int phiSpan = -1; + int zSpan = -1; +}; + +struct VertexerHistogramsConfiguration { + VertexerHistogramsConfiguration() = default; + VertexerHistogramsConfiguration(int nBins[3], + int binSpan[3], + float lowBoundaries[3], + float highBoundaries[3]); + int nBinsXYZ[3] = {402, 402, 4002}; + int binSpanXYZ[3] = {2, 2, 4}; + float lowHistBoundariesXYZ[3] = {-1.98f, -1.98f, -40.f}; + float highHistBoundariesXYZ[3] = {1.98f, 1.98f, 40.f}; + float binSizeHistX = (highHistBoundariesXYZ[0] - lowHistBoundariesXYZ[0]) / (nBinsXYZ[0] - 1); + float binSizeHistY = (highHistBoundariesXYZ[1] - lowHistBoundariesXYZ[1]) / (nBinsXYZ[1] - 1); + float binSizeHistZ = (highHistBoundariesXYZ[2] - lowHistBoundariesXYZ[2]) / (nBinsXYZ[2] - 1); +}; + +inline VertexerHistogramsConfiguration::VertexerHistogramsConfiguration(int nBins[3], + int binSpan[3], + float lowBoundaries[3], + float highBoundaries[3]) +{ + for (int i{0}; i < 3; ++i) { + nBinsXYZ[i] = nBins[i]; + binSpanXYZ[i] = binSpan[i]; + lowHistBoundariesXYZ[i] = lowBoundaries[i]; + highHistBoundariesXYZ[i] = highBoundaries[i]; + } + + binSizeHistX = (highHistBoundariesXYZ[0] - lowHistBoundariesXYZ[0]) / (nBinsXYZ[0] - 1); + binSizeHistY = (highHistBoundariesXYZ[1] - lowHistBoundariesXYZ[1]) / (nBinsXYZ[1] - 1); + binSizeHistZ = (highHistBoundariesXYZ[2] - lowHistBoundariesXYZ[2]) / (nBinsXYZ[2] - 1); +} + +struct VertexerStoreConfigurationGPU { + VertexerStoreConfigurationGPU() = default; + VertexerStoreConfigurationGPU(int cubBufferSize, + int maxTrkClu, + int cluLayCap, + int maxTrkCap, + int maxVert); + + // o2::ecl::GPU::Vector constructor requires signed size for initialisation + int tmpCUBBufferSize = 25e5; + int maxTrackletsPerCluster = 2e2; + int clustersPerLayerCapacity = 4e4; + int dupletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; + int processedTrackletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; + int maxTrackletCapacity = 2e4; + int maxCentroidsXYCapacity = std::ceil(maxTrackletCapacity * (maxTrackletCapacity - 1) / 2); + int nMaxVertices = 10; + + VertexerHistogramsConfiguration histConf; +}; + +inline VertexerStoreConfigurationGPU::VertexerStoreConfigurationGPU(int cubBufferSize, + int maxTrkClu, + int cluLayCap, + int maxTrkCap, + int maxVert) : tmpCUBBufferSize{cubBufferSize}, + maxTrackletsPerCluster{maxTrkClu}, + clustersPerLayerCapacity{cluLayCap}, + maxTrackletCapacity{maxTrkCap}, + nMaxVertices{maxVert} +{ + maxCentroidsXYCapacity = std::ceil(maxTrackletCapacity * (maxTrackletCapacity - 1) / 2); + dupletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; + processedTrackletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; +} + +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_CONFIGURATION_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Constants.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Constants.h new file mode 100644 index 0000000000000..e16a5926d7cf6 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Constants.h @@ -0,0 +1,89 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Constants.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_CONSTANTS_H_ +#define TRACKINGEC0__INCLUDE_CONSTANTS_H_ + +#ifndef __OPENCL__ +#include +#include +#endif + +#include "EC0tracking/Definitions.h" + +namespace o2 +{ +namespace ecl +{ + +namespace constants +{ + +constexpr bool DoTimeBenchmarks = true; + +namespace math +{ +constexpr float Pi{3.14159265359f}; +constexpr float TwoPi{2.0f * Pi}; +constexpr float FloatMinThreshold{1e-20f}; +} // namespace math + +namespace ecl +{ +constexpr int LayersNumber{7}; +constexpr int LayersNumberVertexer{3}; +constexpr int TrackletsPerRoad{LayersNumber - 1}; +constexpr int CellsPerRoad{LayersNumber - 2}; +constexpr int ClustersPerCell{3}; +constexpr int UnusedIndex{-1}; +constexpr float Resolution{0.0005f}; + +GPU_HOST_DEVICE constexpr GPUArrayEC0 LayersZCoordinate() +{ + constexpr double s = 1.; // safety margin + return GPUArrayEC0{{16.333f + s, 16.333f + s, 16.333f + s, 42.140f + s, 42.140f + s, 73.745f + s, 73.745f + s}}; +} +GPU_HOST_DEVICE constexpr GPUArrayEC0 LayersRCoordinate() +{ + return GPUArrayEC0{{2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}}; +} +GPU_HOST_DEVICE constexpr GPUArrayEC0 VertexerHistogramVolume() +{ + return GPUArrayEC0{{1.98, 1.98, 40.f}}; +} +} // namespace ecl + +namespace index_table +{ +constexpr int ZBins{20}; +constexpr int PhiBins{20}; +constexpr float InversePhiBinSize{constants::index_table::PhiBins / constants::math::TwoPi}; +GPU_HOST_DEVICE constexpr GPUArrayEC0 InverseZBinSize() +{ + constexpr auto zSize = ecl::LayersZCoordinate(); + return GPUArrayEC0{{0.5f * ZBins / (zSize[0]), 0.5f * ZBins / (zSize[1]), 0.5f * ZBins / (zSize[2]), + 0.5f * ZBins / (zSize[3]), 0.5f * ZBins / (zSize[4]), 0.5f * ZBins / (zSize[5]), + 0.5f * ZBins / (zSize[6])}}; +} +} // namespace index_table + +namespace pdgcodes +{ +constexpr int PionCode{211}; +} +} // namespace constants +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/DBScan.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/DBScan.h new file mode 100644 index 0000000000000..e4f67be11f747 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/DBScan.h @@ -0,0 +1,141 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 DBScan.h +/// \brief +/// + +#ifndef O2_EC0_TRACKING_DBSCAN_H_ +#define O2_EC0_TRACKING_DBSCAN_H_ + +#include +#include "EC0tracking/Graph.h" + +namespace o2 +{ +namespace ecl +{ + +typedef std::pair State; + +template +class DBScan : Graph +{ + public: + DBScan() = delete; + explicit DBScan(const size_t nThreads); + void init(std::vector&, std::function); + void classifyVertices(const int); + void classifyVertices(std::function&)> classFunction); + void classifyVertices(std::function&)> classFunction, std::function sortFunction); + std::vector getStates() const { return mStates; } + std::vector getCores(); + std::vector> computeClusters(); + + private: + std::vector mStates; + std::function&)> mClassFunction; +}; + +template +DBScan::DBScan(const size_t nThreads) : Graph(nThreads) +{ +} + +template +void DBScan::init(std::vector& vertices, std::function discFunction) +{ + this->Graph::init(vertices); + this->Graph::computeEdges(discFunction); +} + +template +void DBScan::classifyVertices(const int nContributors) +{ + classifyVertices([nContributors](std::vector& edges) { return edges.size() == 0 ? 0 : edges.size() >= static_cast(nContributors - 1) ? 2 : 1; }, + [](State& s1, State& s2) { return static_cast(s1.second) > static_cast(s2.second); }); +} + +template +void DBScan::classifyVertices(std::function& edges)> classFunction) +{ + mClassFunction = classFunction; + const size_t size = {this->mVertices->size()}; + mStates.resize(size); + + if (!this->isMultiThreading()) { + for (size_t iVertex{0}; iVertex < size; ++iVertex) { + mStates[iVertex] = std::make_pair(iVertex, classFunction(this->getEdges()[iVertex])); + } + } else { + const size_t stride{static_cast(std::ceil(this->mVertices->size() / static_cast(this->mExecutors.size())))}; + for (size_t iExecutor{0}; iExecutor < this->mExecutors.size(); ++iExecutor) { + // We cannot pass a template function to std::thread(), using lambda instead + this->mExecutors[iExecutor] = std::thread( + [iExecutor, stride, this](const auto& classFunction) { + for (size_t iVertex{iExecutor * stride}; iVertex < stride * (iExecutor + 1) && iVertex < this->mVertices->size(); ++iVertex) { + mStates[iVertex] = std::make_pair(iVertex, classFunction(this->getEdges()[iVertex])); + } + }, + mClassFunction); + } + } + for (auto&& thread : this->mExecutors) { + thread.join(); + } +} + +template +void DBScan::classifyVertices(std::function&)> classFunction, std::function sortFunction) +{ + classifyVertices(classFunction); + std::sort(mStates.begin(), mStates.end(), sortFunction); +} + +template +std::vector DBScan::getCores() +{ + std::vector cores; + std::vector coreIndices; + std::copy_if(mStates.begin(), mStates.end(), std::back_inserter(cores), [](const State& state) { return state.second == 2; }); + std::transform(cores.begin(), cores.end(), std::back_inserter(coreIndices), [](const State& state) -> int { return state.first; }); + return coreIndices; +} + +template +std::vector> DBScan::computeClusters() +{ + std::vector> clusters; + std::vector cores = getCores(); + std::vector usedVertices(this->mVertices->size(), false); + + for (size_t core{0}; core < cores.size(); ++core) { + if (!usedVertices[cores[core]]) { + std::vector clusterFlags = this->getCluster(cores[core]); + std::transform(usedVertices.begin(), usedVertices.end(), clusterFlags.begin(), usedVertices.begin(), std::logical_or<>()); + clusters.emplace_back(std::move(this->getClusterIndices(clusterFlags))); + } + } + return clusters; +} + +struct Centroid final { + Centroid() = default; + Centroid(int* indices, float* position); + void init(); + static float ComputeDistance(const Centroid& c1, const Centroid& c2); + + int mIndices[2]; + float mPosition[3]; +}; + +} // namespace ecl +} // namespace o2 +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Definitions.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Definitions.h new file mode 100644 index 0000000000000..e54031ef3e8da --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Definitions.h @@ -0,0 +1,103 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Definitions.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_CADEFINITIONS_H_ +#define TRACKINGEC0__INCLUDE_CADEFINITIONS_H_ + +// #define _ALLOW_DEBUG_TREES_ITS_ // to allow debug (vertexer only) + +#ifndef __OPENCL__ +#include +#endif + +//#define CA_DEBUG + +#ifdef CA_DEBUG +#define CA_DEBUGGER(x) x +#else +#define CA_DEBUGGER(x) \ + do { \ + } while (0) +#ifndef NDEBUG +#define NDEBUG 1 +#endif +#endif + +#if defined(CUDA_ENABLED) +#define TRACKINGEC0__GPU_MODE true +#else +#define TRACKINGEC0__GPU_MODE false +#endif + +#if defined(__CUDACC__) +#define TRACKINGEC0__GPU_COMPILING +#endif + +#if defined(__CUDA_ARCH__) +#define TRACKINGEC0__GPU_DEVICE +#endif + +#if defined(__CUDACC__) + +#define GPU_HOST __host__ +#define GPU_DEVICE __device__ +#define GPU_HOST_DEVICE __host__ __device__ +#define GPU_GLOBAL __global__ +#define GPU_SHARED __shared__ +#define GPU_SYNC __syncthreads() + +#define MATH_CEIL ceil + +#include +#include "EC0trackingCUDA/Array.h" + +template +using GPUArrayEC0 = o2::ecl::GPU::Array; + +typedef cudaStream_t GPUStream; + +#else + +#define GPU_HOST +#define GPU_DEVICE +#define GPU_HOST_DEVICE +#define GPU_GLOBAL +#define GPU_SHARED +#define GPU_SYNC + +#define MATH_CEIL std::ceil + +#ifndef __VECTOR_TYPES_H__ + +#include "GPUCommonDef.h" + +#endif + +#ifndef __OPENCL__ +#include +template +using GPUArrayEC0 = std::array; +#else +#include "EC0trackingCUDA/Array.h" + +template +using GPUArrayEC0 = o2::ecl::GPU::Array; +#endif + +//typedef struct _dummyStream { +//} GPUStream; + +#endif + +#endif /* TRACKINGEC0__INCLUDE_CADEFINITIONS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Graph.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Graph.h new file mode 100644 index 0000000000000..7793ffbeb2be1 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Graph.h @@ -0,0 +1,232 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Graph.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_ALGORITHMS_H_ +#define TRACKINGEC0__INCLUDE_ALGORITHMS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace ecl +{ + +typedef int Edge; + +class Barrier +{ + public: + explicit Barrier(std::size_t count) : count(count) {} + void Wait(); + + private: + std::mutex mutex; + std::condition_variable condition; + std::size_t count; +}; + +template +class Graph +{ + public: + Graph() = delete; + explicit Graph(const size_t nThreads = 1); + void init(std::vector&); + std::vector getCluster(const int); + std::vector getClusterIndices(const int); + std::vector getClusterIndices(const std::vector /* , const int*/); + void computeEdges(std::function); + std::vector> getEdges() const { return mEdges; } + char isMultiThreading() const { return mIsMultiThread; } + + std::vector* mVertices = nullptr; // Observer pointer + std::vector mExecutors; // Difficult to pass + + private: + void findVertexEdges(std::vector& localEdges, const T& vertex, const size_t vId, const size_t size); + + // Multithread block + size_t mNThreads; + char mIsMultiThread; + + // Common data members + std::function mLinkFunction; + std::vector> mEdges; + std::vector mVisited; +}; + +template +Graph::Graph(const size_t nThreads) : mNThreads{nThreads} +{ + mIsMultiThread = nThreads > 1 ? true : false; +} + +template +void Graph::init(std::vector& vertices) +{ + + // Graph initialization + mVertices = &vertices; + if (mIsMultiThread) { + mNThreads = std::min(static_cast(std::thread::hardware_concurrency()), mNThreads); + mExecutors.resize(mNThreads); + } + + mEdges.resize(vertices.size()); + mVisited.resize(vertices.size(), false); +} + +template +void Graph::computeEdges(std::function linkFunction) +{ + mLinkFunction = linkFunction; + int tot_nedges = 0; + const size_t size = {mVertices->size()}; + if (!mIsMultiThread) { + for (size_t iVertex{0}; iVertex < size; ++iVertex) { + findVertexEdges(mEdges[iVertex], (*mVertices)[iVertex], iVertex, size); + tot_nedges += static_cast(mEdges[iVertex].size()); + } + } else { + mNThreads = std::min(static_cast(std::thread::hardware_concurrency()), mNThreads); + mExecutors.resize(mNThreads); + const size_t stride{static_cast(std::ceil(mVertices->size() / static_cast(mExecutors.size())))}; + for (size_t iExecutor{0}; iExecutor < mExecutors.size(); ++iExecutor) { + // We cannot pass a template function to std::thread(), using lambda instead + mExecutors[iExecutor] = std::thread( + [iExecutor, stride, this](const auto& linkFunction) { + for (size_t iVertex1{iExecutor * stride}; iVertex1 < stride * (iExecutor + 1) && iVertex1 < mVertices->size(); ++iVertex1) { + for (size_t iVertex2{0}; iVertex2 < mVertices->size(); ++iVertex2) { + if (iVertex1 != iVertex2 && linkFunction((*mVertices)[iVertex1], (*mVertices)[iVertex2])) { + mEdges[iVertex1].emplace_back(iVertex2); + } + } + } + }, + mLinkFunction); + } + } + for (auto&& thread : mExecutors) { + thread.join(); + } +} +template +void Graph::findVertexEdges(std::vector& localEdges, const T& vertex, const size_t vId, const size_t size) +{ + for (size_t iVertex2{0}; iVertex2 < size; ++iVertex2) { + if (vId != iVertex2 && mLinkFunction(vertex, (*mVertices)[iVertex2])) { + localEdges.emplace_back(iVertex2); + } + } +} + +template +std::vector Graph::getCluster(const int vertexId) +{ + // This method uses a BFS algorithm to return all the graph + // vertex ids belonging to a graph + std::vector indices; + std::vector visited(mVertices->size(), false); + + if (!mIsMultiThread) { + std::queue idQueue; + idQueue.emplace(vertexId); + visited[vertexId] = true; + + // Consume the queue + while (!idQueue.empty()) { + const int id = idQueue.front(); + idQueue.pop(); + for (Edge edge : mEdges[id]) { + if (!visited[edge]) { + idQueue.emplace(edge); + indices.emplace_back(edge); + visited[edge] = true; + } + } + } + } else { + const size_t stride{static_cast(std::ceil(static_cast(this->mVertices->size()) / static_cast(this->mExecutors.size())))}; + std::vector frontier(mVertices->size(), false); + std::vector flags(mVertices->size(), false); + + frontier[vertexId] = true; + int counter{0}; + while (std::any_of(frontier.begin(), frontier.end(), [](const char t) { return t; })) { + flags.resize(mVertices->size(), false); + Barrier barrier(mExecutors.size()); + for (size_t iExecutor{0}; iExecutor < this->mExecutors.size(); ++iExecutor) { + mExecutors[iExecutor] = std::thread( + [&stride, &frontier, &visited, &barrier, &flags, this](const int executorId) { + for (size_t iVertex{executorId * stride}; iVertex < stride * (executorId + 1) && iVertex < this->mVertices->size(); ++iVertex) { + if (frontier[iVertex]) { + flags[iVertex] = true; + frontier[iVertex] = false; + visited[iVertex] = true; + } + } + barrier.Wait(); + for (size_t iVertex{executorId * stride}; iVertex < stride * (executorId + 1) && iVertex < this->mVertices->size(); ++iVertex) { + if (flags[iVertex]) { + for (auto& edge : mEdges[iVertex]) { + if (!visited[edge]) { + frontier[edge] = true; + } + } + } + } + }, + iExecutor); + } + for (auto&& thread : mExecutors) { + thread.join(); + } + } + } + return visited; +} + +template +std::vector Graph::getClusterIndices(const std::vector visited) +{ + // Return a smaller vector only with the int IDs of the vertices belonging to cluster + std::vector indices; + for (size_t iVisited{0}; iVisited < visited.size(); ++iVisited) { + if (visited[iVisited]) { + indices.emplace_back(iVisited); + } + } + return indices; +} + +template +std::vector Graph::getClusterIndices(const int vertexId) +{ + std::vector visited = std::move(getCluster(vertexId)); + return getClusterIndices(visited); +} + +} // namespace ecl +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/IOUtils.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/IOUtils.h new file mode 100644 index 0000000000000..41b82b0176fd8 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/IOUtils.h @@ -0,0 +1,87 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 IOUtils.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_EVENTLOADER_H_ +#define TRACKINGEC0__INCLUDE_EVENTLOADER_H_ + +#include +#include +#include +#include + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsEndCaps/TopologyDictionary.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/ROframe.h" +#include "EC0tracking/Label.h" +#include "EC0tracking/Road.h" +#include "EndCapsBase/SegmentationAlpide.h" +#include "ReconstructionDataFormats/BaseCluster.h" + +namespace o2 +{ + +class MCCompLabel; + +namespace dataformats +{ +template +class MCTruthContainer; +} + +namespace endcaps +{ +//class o2::itsmft::Cluster; +//class o2::itsmft::CompClusterExt; +class TopologyDictionary; +} // namespace endcaps + +namespace ecl +{ + +namespace ioutils +{ +constexpr float DefClusErrorRow = o2::endcaps::SegmentationAlpide::PitchRow * 0.5; +constexpr float DefClusErrorCol = o2::endcaps::SegmentationAlpide::PitchCol * 0.5; +constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; +constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; + +void loadConfigurations(const std::string&); +std::vector loadEventData(const std::string&); +void loadEventData(ROframe& events, gsl::span clusters, + const o2::dataformats::MCTruthContainer* clsLabels = nullptr); +void loadEventData(ROframe& events, gsl::span clusters, + gsl::span::iterator& pattIt, const o2::endcaps::TopologyDictionary& dict, + const dataformats::MCTruthContainer* clsLabels = nullptr); +int loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& events, gsl::span clusters, + const dataformats::MCTruthContainer* mClsLabels = nullptr); +int loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& events, gsl::span clusters, + gsl::span::iterator& pattIt, const o2::endcaps::TopologyDictionary& dict, + const dataformats::MCTruthContainer* mClsLabels = nullptr); +void generateSimpleData(ROframe& event, const int phiDivs, const int zDivs); + +void convertCompactClusters(gsl::span clusters, + gsl::span::iterator& pattIt, + std::vector>& output, + const o2::endcaps::TopologyDictionary& dict); + +std::vector> loadLabels(const int, const std::string&); +void writeRoadsReport(std::ofstream&, std::ofstream&, std::ofstream&, const std::vector>&, + const std::unordered_map&); +} // namespace ioutils +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_EVENTLOADER_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/IndexTableUtils.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/IndexTableUtils.h new file mode 100644 index 0000000000000..09079cebed7d4 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/IndexTableUtils.h @@ -0,0 +1,79 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 IndexTableUtils.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_INDEXTABLEUTILS_H_ +#define TRACKINGEC0__INCLUDE_INDEXTABLEUTILS_H_ + +#ifndef __OPENCL__ +#include +#include +#include +#endif + +#include "EC0tracking/Constants.h" +#include "EC0tracking/Definitions.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ + +namespace index_table_utils +{ +float getInverseZBinSize(const int); +GPUhdi() int getZBinIndex(const int, const float); +GPUhdi() int getPhiBinIndex(const float); +GPUhdi() int getBinIndex(const int, const int); +GPUhdi() int countRowSelectedBins( + const GPUArrayEC0&, const int, const int, + const int); +} // namespace index_table_utils + +inline float getInverseZCoordinate(const int layerIndex) +{ + return 0.5f * constants::index_table::ZBins / constants::ecl::LayersZCoordinate()[layerIndex]; +} + +GPUhdi() int index_table_utils::getZBinIndex(const int layerIndex, const float zCoordinate) +{ + return (zCoordinate + constants::ecl::LayersZCoordinate()[layerIndex]) * + constants::index_table::InverseZBinSize()[layerIndex]; +} + +GPUhdi() int index_table_utils::getPhiBinIndex(const float currentPhi) +{ + return (currentPhi * constants::index_table::InversePhiBinSize); +} + +GPUhdi() int index_table_utils::getBinIndex(const int zIndex, const int phiIndex) +{ + return gpu::GPUCommonMath::Min(phiIndex * constants::index_table::ZBins + zIndex, + constants::index_table::ZBins * constants::index_table::PhiBins - 1); +} + +GPUhdi() int index_table_utils::countRowSelectedBins( + const GPUArrayEC0& indexTable, + const int phiBinIndex, const int minZBinIndex, const int maxZBinIndex) +{ + const int firstBinIndex{getBinIndex(minZBinIndex, phiBinIndex)}; + const int maxBinIndex{firstBinIndex + maxZBinIndex - minZBinIndex + 1}; + + return indexTable[maxBinIndex] - indexTable[firstBinIndex]; +} +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_INDEXTABLEUTILS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Label.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Label.h new file mode 100644 index 0000000000000..be1e4ea9cdc4f --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Label.h @@ -0,0 +1,40 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Label.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_LABEL_H_ +#define TRACKINGEC0__INCLUDE_LABEL_H_ + +#include + +namespace o2 +{ +namespace ecl +{ + +struct Label final { + Label(const int, const float, const float, const float, const int, const int); + + int monteCarloId; + float transverseMomentum; + float phiCoordinate; + float pseudorapidity; + int pdgCode; + int numberOfClusters; + + friend std::ostream& operator<<(std::ostream&, const Label&); +}; +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_LABEL_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/MathUtils.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/MathUtils.h new file mode 100644 index 0000000000000..9a2a37fe99731 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/MathUtils.h @@ -0,0 +1,98 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 MathUtils.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_CAUTILS_H_ +#define TRACKINGEC0__INCLUDE_CAUTILS_H_ + +#ifndef __OPENCL__ +#include +#include +#include +#include +#endif + +#include "MathUtils/Utils.h" +#include "EC0tracking/Constants.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ + +namespace math_utils +{ +GPUhdni() float calculatePhiCoordinate(const float, const float); +GPUhdni() float calculateRCoordinate(const float, const float); +GPUhdni() constexpr float getNormalizedPhiCoordinate(const float); +GPUhdni() constexpr float3 crossProduct(const float3&, const float3&); +GPUhdni() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3); +GPUhdni() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3); +GPUhdni() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2); + +} // namespace math_utils + +GPUhdi() float math_utils::calculatePhiCoordinate(const float xCoordinate, const float yCoordinate) +{ + //return o2::gpu::CAMath::ATan2(-yCoordinate, -xCoordinate) + constants::math::Pi; + return utils::FastATan2(-yCoordinate, -xCoordinate) + constants::math::Pi; +} + +GPUhdi() float math_utils::calculateRCoordinate(const float xCoordinate, const float yCoordinate) +{ + return o2::gpu::CAMath::Sqrt(xCoordinate * xCoordinate + yCoordinate * yCoordinate); +} + +GPUhdi() constexpr float math_utils::getNormalizedPhiCoordinate(const float phiCoordinate) +{ + return (phiCoordinate < 0) + ? phiCoordinate + constants::math::TwoPi + : (phiCoordinate > constants::math::TwoPi) ? phiCoordinate - constants::math::TwoPi : phiCoordinate; +} + +GPUhdi() constexpr float3 math_utils::crossProduct(const float3& firstVector, const float3& secondVector) +{ + + return float3{(firstVector.y * secondVector.z) - (firstVector.z * secondVector.y), + (firstVector.z * secondVector.x) - (firstVector.x * secondVector.z), + (firstVector.x * secondVector.y) - (firstVector.y * secondVector.x)}; +} + +GPUhdi() float math_utils::computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3) +{ + const float d = (x2 - x1) * (y3 - y2) - (x3 - x2) * (y2 - y1); + const float a = + 0.5f * ((y3 - y2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) - (y2 - y1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2)); + const float b = + 0.5f * ((x2 - x1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2) - (x3 - x2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1)); + + return -1.f * d / o2::gpu::CAMath::Sqrt((d * x1 - a) * (d * x1 - a) + (d * y1 - b) * (d * y1 - b)); +} + +GPUhdi() float math_utils::computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3) +{ + const float k1 = (y2 - y1) / (x2 - x1), k2 = (y3 - y2) / (x3 - x2); + return 0.5f * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); +} + +GPUhdi() float math_utils::computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2) +{ + return (z1 - z2) / o2::gpu::CAMath::Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); +} + +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_CAUTILS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/PrimaryVertexContext.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/PrimaryVertexContext.h new file mode 100644 index 0000000000000..6d579286282ca --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/PrimaryVertexContext.h @@ -0,0 +1,158 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 PrimaryVertexContext.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_PRIMARYVERTEXCONTEXT_H_ +#define TRACKINGEC0__INCLUDE_PRIMARYVERTEXCONTEXT_H_ + +#include +#include +#include +#include + +#include "EC0tracking/Cell.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Constants.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/Road.h" +#include "EC0tracking/Tracklet.h" + +namespace o2 +{ +namespace ecl +{ + +class PrimaryVertexContext +{ + public: + PrimaryVertexContext() = default; + + virtual ~PrimaryVertexContext() = default; + + PrimaryVertexContext(const PrimaryVertexContext&) = delete; + PrimaryVertexContext& operator=(const PrimaryVertexContext&) = delete; + + virtual void initialise(const MemoryParameters& memParam, const std::array, constants::ecl::LayersNumber>& cl, + const std::array& pv, const int iteration); + const float3& getPrimaryVertex() const; + std::array, constants::ecl::LayersNumber>& getClusters(); + std::array, constants::ecl::CellsPerRoad>& getCells(); + std::array, constants::ecl::CellsPerRoad - 1>& getCellsLookupTable(); + std::array>, constants::ecl::CellsPerRoad - 1>& getCellsNeighbours(); + std::vector& getRoads(); + + bool isClusterUsed(int layer, int clusterId) const; + void markUsedCluster(int layer, int clusterId); + + std::array, + constants::ecl::TrackletsPerRoad>& + getIndexTables(); + std::array, constants::ecl::TrackletsPerRoad>& getTracklets(); + std::array, constants::ecl::CellsPerRoad>& getTrackletsLookupTable(); + + void initialiseRoadLabels(); + void setRoadLabel(int i, const unsigned long long& lab, bool fake); + const unsigned long long& getRoadLabel(int i) const; + bool isRoadFake(int i) const; + + protected: + float3 mPrimaryVertex; + std::array, constants::ecl::LayersNumber> mUnsortedClusters; + std::array, constants::ecl::LayersNumber> mClusters; + std::array, constants::ecl::LayersNumber> mUsedClusters; + std::array, constants::ecl::CellsPerRoad> mCells; + std::array, constants::ecl::CellsPerRoad - 1> mCellsLookupTable; + std::array>, constants::ecl::CellsPerRoad - 1> mCellsNeighbours; + std::vector mRoads; + + std::array, + constants::ecl::TrackletsPerRoad> + mIndexTables; + std::array, constants::ecl::TrackletsPerRoad> mTracklets; + std::array, constants::ecl::CellsPerRoad> mTrackletsLookupTable; + + std::vector> mRoadLabels; +}; + +inline const float3& PrimaryVertexContext::getPrimaryVertex() const { return mPrimaryVertex; } + +inline std::array, constants::ecl::LayersNumber>& PrimaryVertexContext::getClusters() +{ + return mClusters; +} + +inline std::array, constants::ecl::CellsPerRoad>& PrimaryVertexContext::getCells() { return mCells; } + +inline std::array, constants::ecl::CellsPerRoad - 1>& PrimaryVertexContext::getCellsLookupTable() +{ + return mCellsLookupTable; +} + +inline std::array>, constants::ecl::CellsPerRoad - 1>& + PrimaryVertexContext::getCellsNeighbours() +{ + return mCellsNeighbours; +} + +inline std::vector& PrimaryVertexContext::getRoads() { return mRoads; } + +inline bool PrimaryVertexContext::isClusterUsed(int layer, int clusterId) const +{ + return mUsedClusters[layer][clusterId]; +} + +inline void PrimaryVertexContext::markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } + +inline std::array, + constants::ecl::TrackletsPerRoad>& + PrimaryVertexContext::getIndexTables() +{ + return mIndexTables; +} + +inline std::array, constants::ecl::TrackletsPerRoad>& PrimaryVertexContext::getTracklets() +{ + return mTracklets; +} + +inline std::array, constants::ecl::CellsPerRoad>& PrimaryVertexContext::getTrackletsLookupTable() +{ + return mTrackletsLookupTable; +} + +inline void PrimaryVertexContext::initialiseRoadLabels() +{ + mRoadLabels.clear(); + mRoadLabels.resize(mRoads.size()); +} + +inline void PrimaryVertexContext::setRoadLabel(int i, const unsigned long long& lab, bool fake) +{ + mRoadLabels[i].first = lab; + mRoadLabels[i].second = fake; +} + +inline const unsigned long long& PrimaryVertexContext::getRoadLabel(int i) const +{ + return mRoadLabels[i].first; +} + +inline bool PrimaryVertexContext::isRoadFake(int i) const +{ + return mRoadLabels[i].second; +} + +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_PRIMARYVERTEXCONTEXT_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ROframe.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ROframe.h new file mode 100644 index 0000000000000..c6863271c8cad --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/ROframe.h @@ -0,0 +1,182 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 ROframe.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_ROFRAME_H_ +#define TRACKINGEC0__INCLUDE_ROFRAME_H_ + +#include +#include +#include +#include +#include + +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Constants.h" + +#include "ReconstructionDataFormats/Vertex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2 +{ +namespace ecl +{ + +using Vertex = o2::dataformats::Vertex>; + +class ROframe final +{ + public: + ROframe(int ROframeId); + int getROFrameId() const; + const float3& getPrimaryVertex(const int) const; + int getPrimaryVerticesNum() const; + void addPrimaryVertex(const float, const float, const float); + void addPrimaryVertices(std::vector vertices); + void addPrimaryReconstructedVertex(const float, const float, const float); + void printPrimaryVertices() const; + int getTotalClusters() const; + bool empty() const; + + const std::array, constants::ecl::LayersNumber>& getClusters() const; + const std::vector& getClustersOnLayer(int layerId) const; + const std::vector& getTrackingFrameInfoOnLayer(int layerId) const; + const std::array, constants::ecl::LayersNumber>& getTrackingFrameInfo() const; + + const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; + const MCCompLabel& getClusterLabels(int layerId, const Cluster& cl) const; + const MCCompLabel& getClusterLabels(int layerId, const int clId) const; + int getClusterExternalIndex(int layerId, const int clId) const; + std::vector getTracksId(const int layerId, const std::vector& cl); + + template + void addClusterToLayer(int layer, T&&... args); + template + void addTrackingFrameInfoToLayer(int layer, T&&... args); + void addClusterLabelToLayer(int layer, const MCCompLabel label); + void addClusterExternalIndexToLayer(int layer, const int idx); + bool hasMCinformation() const; + + void clear(); + + private: + const int mROframeId; + std::vector mPrimaryVertices; + std::array, constants::ecl::LayersNumber> mClusters; + std::array, constants::ecl::LayersNumber> mTrackingFrameInfo; + std::array, constants::ecl::LayersNumber> mClusterLabels; + std::array, constants::ecl::LayersNumber> mClusterExternalIndices; +}; + +inline int ROframe::getROFrameId() const { return mROframeId; } + +inline const float3& ROframe::getPrimaryVertex(const int vertexIndex) const { return mPrimaryVertices[vertexIndex]; } + +inline int ROframe::getPrimaryVerticesNum() const { return mPrimaryVertices.size(); } + +inline bool ROframe::empty() const { return getTotalClusters() == 0; } + +inline const std::array, constants::ecl::LayersNumber>& ROframe::getClusters() const +{ + return mClusters; +} + +inline const std::vector& ROframe::getClustersOnLayer(int layerId) const +{ + return mClusters[layerId]; +} + +inline const std::vector& ROframe::getTrackingFrameInfoOnLayer(int layerId) const +{ + return mTrackingFrameInfo[layerId]; +} + +inline const std::array, constants::ecl::LayersNumber>& ROframe::getTrackingFrameInfo() const +{ + return mTrackingFrameInfo; +} + +inline const TrackingFrameInfo& ROframe::getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const +{ + return mTrackingFrameInfo[layerId][cl.clusterId]; +} + +inline const MCCompLabel& ROframe::getClusterLabels(int layerId, const Cluster& cl) const +{ + return mClusterLabels[layerId][cl.clusterId]; +} + +inline const MCCompLabel& ROframe::getClusterLabels(int layerId, const int clId) const +{ + return mClusterLabels[layerId][clId]; +} + +inline int ROframe::getClusterExternalIndex(int layerId, const int clId) const +{ + return mClusterExternalIndices[layerId][clId]; +} + +inline std::vector ROframe::getTracksId(const int layerId, const std::vector& cl) +{ + std::vector tracksId; + for (auto& cluster : cl) { + tracksId.push_back(getClusterLabels(layerId, cluster).isNoise() ? -1 : getClusterLabels(layerId, cluster).getTrackID()); + } + return tracksId; +} + +template +void ROframe::addClusterToLayer(int layer, T&&... values) +{ + mClusters[layer].emplace_back(std::forward(values)...); +} + +template +void ROframe::addTrackingFrameInfoToLayer(int layer, T&&... values) +{ + mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); +} + +inline void ROframe::addClusterLabelToLayer(int layer, const MCCompLabel label) { mClusterLabels[layer].emplace_back(label); } + +inline void ROframe::addClusterExternalIndexToLayer(int layer, const int idx) +{ + mClusterExternalIndices[layer].push_back(idx); +} + +inline void ROframe::clear() +{ + for (int iL = 0; iL < constants::ecl::LayersNumber; ++iL) { + mClusters[iL].clear(); + mTrackingFrameInfo[iL].clear(); + mClusterLabels[iL].clear(); + mClusterExternalIndices[iL].clear(); + } + mPrimaryVertices.clear(); +} + +inline bool ROframe::hasMCinformation() const +{ + for (const auto& vect : mClusterLabels) { + if (!vect.empty()) { + return true; + } + } + return false; +} + +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_ROFRAME_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Road.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Road.h new file mode 100644 index 0000000000000..d57161cd03d9d --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Road.h @@ -0,0 +1,67 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Road.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_ROAD_H_ +#define TRACKINGEC0__INCLUDE_ROAD_H_ + +#ifndef __OPENCL__ +#include +#endif + +#include "EC0tracking/Constants.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ + +class Road final +{ + public: + Road(); + Road(int, int); + + int getRoadSize() const; + int getLabel() const; + void setLabel(const int); + bool isFakeRoad() const; + void setFakeRoad(const bool); + GPUhdni() int& operator[](const int&); + + void resetRoad(); + void addCell(int, int); + + private: + int mCellIds[constants::ecl::CellsPerRoad]; + int mRoadSize; + int mLabel; + bool mIsFakeRoad; +}; + +inline int Road::getRoadSize() const { return mRoadSize; } + +inline int Road::getLabel() const { return mLabel; } + +inline void Road::setLabel(const int label) { mLabel = label; } + +GPUhdi() int& Road::operator[](const int& i) { return mCellIds[i]; } + +inline bool Road::isFakeRoad() const { return mIsFakeRoad; } + +inline void Road::setFakeRoad(const bool isFakeRoad) { mIsFakeRoad = isFakeRoad; } +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_ROAD_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/StandaloneDebugger.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/StandaloneDebugger.h new file mode 100644 index 0000000000000..da76638f234d5 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/StandaloneDebugger.h @@ -0,0 +1,79 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. +/// +/// \fileStandaloneDebugger.h +/// \brief separate TreeStreamerRedirector class to be used with GPU +/// \author matteo.concas@cern.ch + +#ifndef O2_EC0_STANDALONE_DEBUGGER_H_ +#define O2_EC0_STANDALONE_DEBUGGER_H_ + +namespace o2 +{ + +class MCCompLabel; + +namespace utils +{ +class TreeStreamRedirector; +} + +namespace ecl +{ +class Tracklet; +class Line; +class ROframe; + +class StandaloneDebugger +{ + public: + explicit StandaloneDebugger(const std::string debugTreeFileName = "dbg_EC0.root"); + ~StandaloneDebugger(); + void setDebugTreeFileName(std::string); + + // Monte carlo oracle + int getEventId(const int firstClusterId, const int secondClusterId, ROframe* frame); + + // Tree part + const std::string& getDebugTreeFileName() const { return mDebugTreeFileName; } + void fillCombinatoricsTree(std::array, constants::ecl::LayersNumberVertexer>&, + std::vector, + std::vector, + const ROframe*); + void fillCombinatoricsMCTree(std::vector, std::vector); + void fillTrackletSelectionTree(std::array, constants::ecl::LayersNumberVertexer>&, + std::vector comb01, + std::vector comb12, + std::vector>, + const ROframe*); + void fillLinesSummaryTree(std::vector, const ROframe*); + void fillPairsInfoTree(std::vector, const ROframe*); + void fillLineClustersTree(std::vector clusters, const ROframe* event); + void fillXYZHistogramTree(std::array, 3>, const std::array); + void fillVerticesInfoTree(float x, float y, float z, int size, int rId, int eId, float pur); + + static int getBinIndex(const float, const int, const float, const float); + + private: + std::string mDebugTreeFileName = "dbg_EC0.root"; // output filename + o2::utils::TreeStreamRedirector* mTreeStream; // observer +}; + +inline void StandaloneDebugger::setDebugTreeFileName(const std::string name) +{ + if (!name.empty()) { + mDebugTreeFileName = name; + } +} + +} // namespace ecl +} // namespace o2 + +#endif /*O2_EC0_STANDALONE_DEBUGGER_H_*/ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Tracker.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Tracker.h new file mode 100644 index 0000000000000..8780a120b4143 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Tracker.h @@ -0,0 +1,165 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Tracker.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_TRACKER_H_ +#define TRACKINGEC0__INCLUDE_TRACKER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/ROframe.h" +#include "EC0tracking/MathUtils.h" +#include "EC0tracking/PrimaryVertexContext.h" +#include "EC0tracking/Road.h" + +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2 +{ +namespace gpu +{ +class GPUChainEC0; +} +namespace ecl +{ + +class PrimaryVertexContext; +class TrackerTraitsEC0; + +class Tracker +{ + + public: + Tracker(TrackerTraitsEC0* traits); + + Tracker(const Tracker&) = delete; + Tracker& operator=(const Tracker&) = delete; + ~Tracker(); + + void setBz(float bz); + float getBz() const; + + std::vector& getTracks(); + dataformats::MCTruthContainer& getTrackLabels(); + + void clustersToTracks(const ROframe&, std::ostream& = std::cout); + + void setROFrame(std::uint32_t f) { mROFrame = f; } + std::uint32_t getROFrame() const { return mROFrame; } + void setParameters(const std::vector&, const std::vector&); + + private: + track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const Cluster& cluster3, + const TrackingFrameInfo& tf3); + template + void initialisePrimaryVertexContext(T&&... args); + void computeTracklets(); + void computeCells(); + void findCellsNeighbours(int& iteration); + void findRoads(int& iteration); + void findTracks(const ROframe& ev); + bool fitTrack(const ROframe& event, o2::its::TrackITSExt& track, int start, int end, int step); + void traverseCellsTree(const int, const int); + void computeRoadsMClabels(const ROframe&); + void computeTracksMClabels(const ROframe&); + void rectifyClusterIndices(const ROframe& event); + + template + float evaluateTask(void (Tracker::*)(T...), const char*, std::ostream& ostream, T&&... args); + + TrackerTraitsEC0* mTraits = nullptr; /// Observer pointer, not owned by this class + PrimaryVertexContext* mPrimaryVertexContext = nullptr; /// Observer pointer, not owned by this class + + std::vector mMemParams; + std::vector mTrkParams; + + bool mCUDA = false; + float mBz = 5.f; + std::uint32_t mROFrame = 0; + std::vector mTracks; + dataformats::MCTruthContainer mTrackLabels; + o2::gpu::GPUChainEC0* mRecoChain = nullptr; +}; + +inline void Tracker::setParameters(const std::vector& memPars, const std::vector& trkPars) +{ + mMemParams = memPars; + mTrkParams = trkPars; +} + +inline float Tracker::getBz() const +{ + return mBz; +} + +inline void Tracker::setBz(float bz) +{ + mBz = bz; +} + +template +void Tracker::initialisePrimaryVertexContext(T&&... args) +{ + mPrimaryVertexContext->initialise(std::forward(args)...); +} + +inline std::vector& Tracker::getTracks() +{ + return mTracks; +} + +inline dataformats::MCTruthContainer& Tracker::getTrackLabels() +{ + return mTrackLabels; +} + +template +float Tracker::evaluateTask(void (Tracker::*task)(T...), const char* taskName, std::ostream& ostream, + T&&... args) +{ + float diff{0.f}; + + if (constants::DoTimeBenchmarks) { + auto start = std::chrono::high_resolution_clock::now(); + (this->*task)(std::forward(args)...); + auto end = std::chrono::high_resolution_clock::now(); + + std::chrono::duration diff_t{end - start}; + diff = diff_t.count(); + + if (taskName == nullptr) { + ostream << diff << "\t"; + } else { + ostream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms" << std::endl; + } + } else { + (this->*task)(std::forward(args)...); + } + + return diff; +} + +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_TRACKER_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackerTraitsEC0.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackerTraitsEC0.h new file mode 100644 index 0000000000000..d7dd0e894225e --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackerTraitsEC0.h @@ -0,0 +1,110 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrackerTraitsEC0.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_TRACKERTRAITS_H_ +#define TRACKINGEC0__INCLUDE_TRACKERTRAITS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/MathUtils.h" +#include "EC0tracking/PrimaryVertexContext.h" +#include "EC0tracking/Road.h" + +namespace o2 +{ +namespace its +{ +class TrackITSExt; +} // namespace its +} // namespace o2 + +namespace o2 +{ +namespace gpu +{ +class GPUChainEC0; +} +namespace ecl +{ + +class TrackITSExt; +typedef std::function& roads, std::array clusters, std::array cells, const std::array, 7>& tf, std::vector& tracks)> FuncRunEC0TrackFit_t; + +class TrackerTraitsEC0 +{ + public: + virtual ~TrackerTraitsEC0() = default; + + GPU_HOST_DEVICE static constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } + GPU_DEVICE static const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi); + + void SetRecoChain(o2::gpu::GPUChainEC0* chain, FuncRunEC0TrackFit_t&& funcRunEC0TrackFit) + { + mChainRunEC0TrackFit = funcRunEC0TrackFit; + mChain = chain; + } + + virtual void computeLayerTracklets(){}; + virtual void computeLayerCells(){}; + virtual void refitTracks(const std::array, 7>&, std::vector&){}; + + void UpdateTrackingParameters(const TrackingParameters& trkPar); + PrimaryVertexContext* getPrimaryVertexContext() { return mPrimaryVertexContext; } + + protected: + PrimaryVertexContext* mPrimaryVertexContext; + TrackingParameters mTrkParams; + + o2::gpu::GPUChainEC0* mChain = nullptr; + FuncRunEC0TrackFit_t mChainRunEC0TrackFit; +}; + +inline void TrackerTraitsEC0::UpdateTrackingParameters(const TrackingParameters& trkPar) +{ + mTrkParams = trkPar; +} + +inline GPU_DEVICE const int4 TrackerTraitsEC0::getBinsRect(const Cluster& currentCluster, const int layerIndex, + const float directionZIntersection, float maxdeltaz, float maxdeltaphi) +{ + const float zRangeMin = directionZIntersection - 2 * maxdeltaz; + const float phiRangeMin = currentCluster.phiCoordinate - maxdeltaphi; + const float zRangeMax = directionZIntersection + 2 * maxdeltaz; + const float phiRangeMax = currentCluster.phiCoordinate + maxdeltaphi; + + if (zRangeMax < -constants::ecl::LayersZCoordinate()[layerIndex + 1] || + zRangeMin > constants::ecl::LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + + return getEmptyBinsRect(); + } + + return int4{gpu::GPUCommonMath::Max(0, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMin)), + index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMin)), + gpu::GPUCommonMath::Min(constants::index_table::ZBins - 1, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMax)), + index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMax))}; +} +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_TRACKERTRAITS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackerTraitsEC0CPU.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackerTraitsEC0CPU.h new file mode 100644 index 0000000000000..b9861d52b29c3 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackerTraitsEC0CPU.h @@ -0,0 +1,56 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 TrackerTraitsEC0.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_TRACKERTRAITSCPU_H_ +#define TRACKINGEC0__INCLUDE_TRACKERTRAITSCPU_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EC0tracking/TrackerTraitsEC0.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/Definitions.h" +#include "EC0tracking/MathUtils.h" +#include "EC0tracking/PrimaryVertexContext.h" +#include "EC0tracking/Road.h" + +namespace o2 +{ +namespace ecl +{ + +class TrackerTraitsEC0CPU : public TrackerTraitsEC0 +{ + public: + TrackerTraitsEC0CPU() { mPrimaryVertexContext = new PrimaryVertexContext; } + ~TrackerTraitsEC0CPU() override { delete mPrimaryVertexContext; } + + void computeLayerCells() final; + void computeLayerTracklets() final; + void refitTracks(const std::array, 7>& tf, std::vector& tracks) final; + + protected: + std::vector> mTracklets; + std::vector> mCells; +}; +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_TRACKERTRAITS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackingConfigParam.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackingConfigParam.h new file mode 100644 index 0000000000000..f2fdcdbb6d5ca --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/TrackingConfigParam.h @@ -0,0 +1,42 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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_ENDCAPSLAYERSDPLTRACKINGPARAM_H_ +#define ALICEO2_ENDCAPSLAYERSDPLTRACKINGPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace ecl +{ + +class VertexingParameters; +struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { + + // geometrical cuts + float zCut = 0.002f; //0.002f + float phiCut = 0.005f; //0.005f + float pairCut = 0.04f; + float clusterCut = 0.8f; + float histPairCut = 0.04f; + float tanLambdaCut = 0.002f; // tanLambda = deltaZ/deltaR + int clusterContributorsCut = 16; + int phiSpan = -1; + int zSpan = -1; + + O2ParamDef(VertexerParamConfig, "EC0VertexerParam"); +}; + +// VertexerParamConfig VertexerParamConfig::sInstance; +} // namespace ecl +} // namespace o2 +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Tracklet.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Tracklet.h new file mode 100644 index 0000000000000..8cf1b4dc27623 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Tracklet.h @@ -0,0 +1,89 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Tracklet.h +/// \brief +/// + +#ifndef TRACKINGEC0__INCLUDE_TRACKLET_H_ +#define TRACKINGEC0__INCLUDE_TRACKLET_H_ + +#include "EC0tracking/Cluster.h" +#include +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +namespace ecl +{ + +struct Tracklet final { + Tracklet(); + GPUdi() Tracklet(const int, const int, const Cluster&, const Cluster&); +#ifdef _ALLOW_DEBUG_TREES_ITS_ + unsigned char isEmpty() const; + void dump(); + unsigned char operator<(const Tracklet&); +#endif + + int firstClusterIndex; + int secondClusterIndex; + float tanLambda; + float phiCoordinate; +}; + +inline Tracklet::Tracklet() : firstClusterIndex{0}, secondClusterIndex{0}, tanLambda{0.0f}, phiCoordinate{0.0f} +{ + // Nothing to do +} + +GPUdi() Tracklet::Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, + const Cluster& firstCluster, const Cluster& secondCluster) + : firstClusterIndex{firstClusterOrderingIndex}, + secondClusterIndex{secondClusterOrderingIndex}, + tanLambda{(firstCluster.zCoordinate - secondCluster.zCoordinate) / + (firstCluster.rCoordinate - secondCluster.rCoordinate)}, + phiCoordinate{gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, + firstCluster.xCoordinate - secondCluster.xCoordinate)} +{ + // Nothing to do +} + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +inline unsigned char Tracklet::isEmpty() const +{ + return !firstClusterIndex && !secondClusterIndex && !tanLambda && !phiCoordinate; +} + +inline unsigned char Tracklet::operator<(const Tracklet& t) +{ + if (isEmpty() && t.isEmpty()) { + return false; + } else { + if (isEmpty()) + return false; + } + return true; +} + +inline void Tracklet::dump() +{ + std::cout << "firstClusterIndex: " << firstClusterIndex << std::endl; + std::cout << "secondClusterIndex: " << secondClusterIndex << std::endl; + std::cout << "tanLambda: " << tanLambda << std::endl; + std::cout << "phiCoordinate: " << phiCoordinate << std::endl; +} +#endif + +} // namespace ecl +} // namespace o2 + +#endif /* TRACKINGEC0__INCLUDE_TRACKLET_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Vertexer.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Vertexer.h new file mode 100644 index 0000000000000..438373e38447e --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/Vertexer.h @@ -0,0 +1,200 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 Vertexer.h +/// \brief +/// \author matteo.concas@cern.ch + +#ifndef O2_EC0_TRACKING_VERTEXER_H_ +#define O2_EC0_TRACKING_VERTEXER_H_ + +#include +#include +#include +#include +#include + +#include "EC0tracking/ROframe.h" +#include "EC0tracking/Constants.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/VertexerTraitsEC0.h" +#include "ReconstructionDataFormats/Vertex.h" + +#include "EC0tracking/ClusterLines.h" +#include "EC0tracking/Tracklet.h" +#include "EC0tracking/Cluster.h" + +class TTree; + +namespace o2 +{ +namespace ecl +{ + +using Vertex = o2::dataformats::Vertex>; + +class Vertexer +{ + public: + Vertexer(VertexerTraitsEC0* traits); + virtual ~Vertexer() = default; + Vertexer(const Vertexer&) = delete; + Vertexer& operator=(const Vertexer&) = delete; + + void setROframe(const uint32_t ROframe) { mROframe = ROframe; } + void setParameters(const VertexingParameters& verPar); + void getGlobalConfiguration(); + VertexingParameters getVertParameters() const; + + uint32_t getROFrame() const { return mROframe; } + std::vector exportVertices(); + VertexerTraitsEC0* getTraits() const { return mTraits; }; + + float clustersToVertices(ROframe&, const bool useMc = false, std::ostream& = std::cout); + void filterMCTracklets(); + void validateTracklets(); + + template + void findTracklets(T&&... args); + + void findTrivialMCTracklets(); + void findVertices(); + void findHistVertices(); + + template + void initialiseVertexer(T&&... args); + + // Utils + void dumpTraits(); + template + float evaluateTask(void (Vertexer::*)(T...), const char*, std::ostream& ostream, T&&... args); + + // debug + void setDebugCombinatorics(); + void setDebugTrackletSelection(); + void setDebugLines(); + void setDebugSummaryLines(); + void setDebugCentroidsHistograms(); + // \debug + + private: + std::uint32_t mROframe = 0; + VertexerTraitsEC0* mTraits = nullptr; +}; + +#ifdef _ALLOW_DEBUG_TREES_ITS_ +inline void Vertexer::filterMCTracklets() +{ + mTraits->computeMCFiltering(); +} +#endif + +template +void Vertexer::initialiseVertexer(T&&... args) +{ + mTraits->initialise(std::forward(args)...); +} + +template +void Vertexer::findTracklets(T&&... args) +{ + mTraits->computeTracklets(std::forward(args)...); +} + +inline void Vertexer::findTrivialMCTracklets() +{ + mTraits->computeTrackletsPureMontecarlo(); +} + +inline VertexingParameters Vertexer::getVertParameters() const +{ + return mTraits->getVertexingParameters(); +} + +inline void Vertexer::setParameters(const VertexingParameters& verPar) +{ + mTraits->updateVertexingParameters(verPar); +} + +inline void Vertexer::dumpTraits() +{ + mTraits->dumpVertexerTraitsEC0(); +} + +inline void Vertexer::validateTracklets() +{ + mTraits->computeTrackletMatching(); +} + +inline std::vector Vertexer::exportVertices() +{ + std::vector vertices; + for (auto& vertex : mTraits->getVertices()) { + std::cout << "\t\tFound vertex with: " << std::setw(6) << vertex.mContributors << " contributors" << std::endl; + vertices.emplace_back(Point3D(vertex.mX, vertex.mY, vertex.mZ), vertex.mRMS2, vertex.mContributors, vertex.mAvgDistance2); + vertices.back().setTimeStamp(vertex.mTimeStamp); + } + return vertices; +} + +template +float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, std::ostream& ostream, + T&&... args) +{ + float diff{0.f}; + + if (constants::DoTimeBenchmarks) { + auto start = std::chrono::high_resolution_clock::now(); + (this->*task)(std::forward(args)...); + auto end = std::chrono::high_resolution_clock::now(); + + std::chrono::duration diff_t{end - start}; + diff = diff_t.count(); + + if (taskName == nullptr) { + ostream << diff << "\t"; + } else { + ostream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms" << std::endl; + } + } else { + (this->*task)(std::forward(args)...); + } + + return diff; +} + +inline void Vertexer::setDebugCombinatorics() +{ + mTraits->setDebugFlag(VertexerDebug::CombinatoricsTreeAll); +} + +inline void Vertexer::setDebugTrackletSelection() +{ + mTraits->setDebugFlag(VertexerDebug::TrackletTreeAll); +} + +inline void Vertexer::setDebugLines() +{ + mTraits->setDebugFlag(VertexerDebug::LineTreeAll); +} + +inline void Vertexer::setDebugSummaryLines() +{ + mTraits->setDebugFlag(VertexerDebug::LineSummaryAll); +} + +inline void Vertexer::setDebugCentroidsHistograms() +{ + mTraits->setDebugFlag(VertexerDebug::HistCentroids); +} + +} // namespace ecl +} // namespace o2 +#endif diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/VertexerTraitsEC0.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/VertexerTraitsEC0.h new file mode 100644 index 0000000000000..de20db4f2dc6e --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/VertexerTraitsEC0.h @@ -0,0 +1,239 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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 VertexerTraitsEC0.h +/// \brief Class to compute the primary vertex in ITS from tracklets +/// \author matteo.concas@cern.ch + +#ifndef O2_EC0_TRACKING_VERTEXER_TRAITS_H_ +#define O2_EC0_TRACKING_VERTEXER_TRAITS_H_ + +#include +#include +#include + +#include "EC0tracking/Cluster.h" +#include "EC0tracking/Configuration.h" +#include "EC0tracking/ClusterLines.h" +#include "EC0tracking/Definitions.h" +#ifdef _ALLOW_DEBUG_TREES_ITS_ +#include "EC0tracking/StandaloneDebugger.h" +#endif +#include "EC0tracking/Tracklet.h" + +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2 +{ +class MCCompLabel; + +namespace utils +{ +class TreeStreamRedirector; +} + +namespace ecl +{ + +class ROframe; + +using constants::ecl::LayersNumberVertexer; +using constants::index_table::PhiBins; +using constants::index_table::ZBins; + +struct lightVertex { + lightVertex(float x, float y, float z, std::array rms2, int cont, float avgdis2, int stamp); +#ifdef _ALLOW_DEBUG_TREES_ITS_ + lightVertex(float x, float y, float z, std::array rms2, int cont, float avgdis2, int stamp, int eId, float pur); +#endif + float mX; + float mY; + float mZ; + std::array mRMS2; + float mAvgDistance2; + int mContributors; + int mTimeStamp; +#ifdef _ALLOW_DEBUG_TREES_ITS_ + float mPurity; + int mEventId; +#endif +}; + +struct ClusterMCLabelInfo { + int TrackId; + int MotherId; + int EventId; + float Pt; +}; + +enum class VertexerDebug : unsigned int { + TrackletTreeAll = 0x1 << 1, + LineTreeAll = 0x1 << 2, + CombinatoricsTreeAll = 0x1 << 3, + LineSummaryAll = 0x1 << 4, + HistCentroids = 0x1 << 5 +}; + +inline lightVertex::lightVertex(float x, float y, float z, std::array rms2, int cont, float avgdis2, int stamp) : mX{x}, mY{y}, mZ{z}, mRMS2{rms2}, mAvgDistance2{avgdis2}, mContributors{cont}, mTimeStamp{stamp} +{ +} +#ifdef _ALLOW_DEBUG_TREES_ITS_ +inline lightVertex::lightVertex(float x, float y, float z, std::array rms2, int cont, float avgdis2, int stamp, int eId, float pur) : mX{x}, mY{y}, mZ{z}, mRMS2{rms2}, mAvgDistance2{avgdis2}, mContributors{cont}, mTimeStamp{stamp}, mEventId{eId}, mPurity{pur} +{ +} +#endif + +class VertexerTraitsEC0 +{ + public: +#ifdef _ALLOW_DEBUG_TREES_ITS_ + VertexerTraitsEC0(); + virtual ~VertexerTraitsEC0(); +#else + VertexerTraitsEC0(); + virtual ~VertexerTraitsEC0() = default; +#endif + + GPUhd() static constexpr int4 getEmptyBinsRect() + { + return int4{0, 0, 0, 0}; + } + GPUhd() static const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi); + GPUhd() static const int2 getPhiBins(float phi, float deltaPhi); + + // virtual vertexer interface + virtual void reset(); + virtual void initialise(ROframe*); + virtual void computeTracklets(); + virtual void computeTrackletMatching(); +#ifdef _ALLOW_DEBUG_TREES_ITS_ + virtual void computeMCFiltering(); + virtual void filterTrackletsWithMC(std::vector&, + std::vector&, + std::vector&, + std::vector&, + const int); +#endif + virtual void computeTrackletsPureMontecarlo(); + virtual void computeVertices(); + virtual void computeHistVertices(); + + void updateVertexingParameters(const VertexingParameters& vrtPar); + VertexingParameters getVertexingParameters() const { return mVrtParams; } + static const std::vector> selectClusters(const std::array& indexTable, + const std::array& selectedBinsRect); + std::vector getVertices() const { return mVertices; } + + // utils + void setIsGPU(const unsigned char); + void dumpVertexerTraitsEC0(); + void arrangeClusters(ROframe*); + std::vector getMClabelsLayer(const int layer) const; + + void setDebugFlag(VertexerDebug flag, const unsigned char on); + unsigned char isDebugFlag(const VertexerDebug& flags) const; + unsigned int getDebugFlags() const { return static_cast(mDBGFlags); } + + protected: + unsigned char mIsGPU; + + std::vector mTracklets; + std::vector mComb01; + std::vector mComb12; + std::vector mFoundTracklets01; + std::vector mFoundTracklets12; + std::array, constants::ecl::LayersNumberVertexer> mClusters; + + unsigned int mDBGFlags = 0; + +#ifdef _ALLOW_DEBUG_TREES_ITS_ + StandaloneDebugger* mDebugger; + std::vector> mAllowedTrackletPairs; +#endif + + VertexingParameters mVrtParams; + std::array, LayersNumberVertexer> mIndexTables; + std::vector mVertices; + + // Frame related quantities + std::array, 2> mUsedClusters; + ROframe* mEvent; + uint32_t mROframe; + + std::array mAverageClustersRadii; + float mDeltaRadii10, mDeltaRadii21; + float mMaxDirectorCosine3; + std::vector mTrackletClusters; +}; + +inline void VertexerTraitsEC0::initialise(ROframe* event) +{ + reset(); + arrangeClusters(event); + setIsGPU(false); +} + +inline void VertexerTraitsEC0::setIsGPU(const unsigned char isgpu) +{ + mIsGPU = isgpu; +} + +inline void VertexerTraitsEC0::updateVertexingParameters(const VertexingParameters& vrtPar) +{ + mVrtParams = vrtPar; +} + +GPUhdi() const int2 VertexerTraitsEC0::getPhiBins(float phi, float dPhi) +{ + return int2{index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phi - dPhi)), + index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phi + dPhi))}; +} + +GPUhdi() const int4 VertexerTraitsEC0::getBinsRect(const Cluster& currentCluster, const int layerIndex, + const float directionZIntersection, float maxdeltaz, float maxdeltaphi) +{ + const float zRangeMin = directionZIntersection - 2 * maxdeltaz; + const float phiRangeMin = currentCluster.phiCoordinate - maxdeltaphi; + const float zRangeMax = directionZIntersection + 2 * maxdeltaz; + const float phiRangeMax = currentCluster.phiCoordinate + maxdeltaphi; + + if (zRangeMax < -constants::ecl::LayersZCoordinate()[layerIndex + 1] || + zRangeMin > constants::ecl::LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + + return getEmptyBinsRect(); + } + + return int4{gpu::GPUCommonMath::Max(0, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMin)), + index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMin)), + gpu::GPUCommonMath::Min(constants::index_table::ZBins - 1, index_table_utils::getZBinIndex(layerIndex + 1, zRangeMax)), + index_table_utils::getPhiBinIndex(math_utils::getNormalizedPhiCoordinate(phiRangeMax))}; +} + +// debug +inline void VertexerTraitsEC0::setDebugFlag(VertexerDebug flag, const unsigned char on = true) +{ + if (on) { + mDBGFlags |= static_cast(flag); + } else { + mDBGFlags &= ~static_cast(flag); + } +} + +inline unsigned char VertexerTraitsEC0::isDebugFlag(const VertexerDebug& flags) const +{ + return mDBGFlags & static_cast(flags); +} + +extern "C" VertexerTraitsEC0* createVertexerTraitsEC0(); + +} // namespace ecl +} // namespace o2 +#endif /* O2_EC0_TRACKING_VERTEXER_TRAITS_H_ */ diff --git a/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/json.h b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/json.h new file mode 100644 index 0000000000000..1a7efb0394021 --- /dev/null +++ b/Detectors/Upgrades/PostLS4/EndCaps/EndCapsLayers/tracking/include/EC0tracking/json.h @@ -0,0 +1,16311 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// 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. + +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.1.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2018 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 1 +#define NLOHMANN_JSON_VERSION_PATCH 2 + +#include // all_of, find, for_each +#include // assert +#include // and, not, or +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // iterator_traits, random_access_iterator_tag +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap + +// #include +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template