diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h index 577bd1bcabaf1..4dbdb49d4822d 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h @@ -22,6 +22,9 @@ namespace iotof class GeometryTGeo : public o2::detectors::DetMatrixCache { public: + using Mat3D = o2::math_utils::Transform3D; + using DetMatrixCache::getMatrixL2G; + GeometryTGeo(bool build = false, int loadTrans = 0); void Build(int loadTrans); void fillMatrixCache(int mask); @@ -79,7 +82,25 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static const char* composeBTOFSymNameChip(int d, int lr); static const char* composeBTOFSymNameSensor(int d, int layer); + int getIOTOFFirstChipIndex(int lay) const; + int getIOTOFLayer(int index) const; + int getIOTOFChipIndex(int lay, int sta, int mod, int chip) const; + bool getIOTOFChipId(int index, int& lay, int& sta, int& mod, int& chip) const; + + /// Get the transformation matrix of the SENSOR (not necessary the same as the chip) + /// for a given chip 'index' by querying the TGeoManager + TGeoHMatrix* extractMatrixSensor(int index) const; + + TString getMatrixPath(int index) const; + protected: + // Determine the number of active parts in the geometry + int extractNumberOfStavesIOTOF(int lay) const; + int extractNumberOfModulesIOTOF(int lay) const; + int extractNumberOfChipsPerModuleIOTOF(int lay) const; + int extractNumberOfChipsFTOF() const; + int extractNumberOfChipsBTOF() const; + // i/oTOF mother volume static std::string sIOTOFVolumeName; @@ -107,10 +128,24 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static std::string sBTOFChipName; static std::string sBTOFSensorName; + // Inner/outer TOF + int mNumberOfStavesIOTOF[2]; + int mNumberOfModulesIOTOF[2]; + int mNumberOfChipsPerModuleIOTOF[2]; + int mNumberOfChipsPerStaveIOTOF[2]; + int mNumberOfChipsIOTOF[2]; + int mLastChipIndex[2]; + + // Forward TOF + int mNumberOfChipsFTOF; + + // Backward TOF + int mNumberOfChipsBTOF; + private: static std::unique_ptr sInstance; }; } // namespace iotof } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index c1a9578484c17..c4cf5fd8844a8 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -20,6 +20,24 @@ namespace o2 namespace iotof { +struct ChipSpecifics { + int NCols = 0; + int NRows = 0; + float PitchCol = 0.; + float PitchRow = 0.; + float PassiveEdgeReadOut = 0.; + float PassiveEdgeTop = 0.; + float PassiveEdgeSide = 0.; + float SensorLayerThicknessEff = 0.; + float SensorLayerThickness = 0.; + + int NPixels() const { return NCols * NRows; } + float ActiveMatrixSizeCols() const { return PitchCol * NCols; } + float ActiveMatrixSizeRows() const { return PitchRow * NRows; } + float SensorSizeCols() const { return ActiveMatrixSizeCols() + 2 * PassiveEdgeSide; } + float SensorSizeRows() const { return ActiveMatrixSizeRows() + PassiveEdgeTop + PassiveEdgeReadOut; } +}; + struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper { bool enableInnerTOF = true; // Enable Inner TOF layer bool enableOuterTOF = true; // Enable Outer TOF layer @@ -31,6 +49,9 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment float sensorThickness = 0.0050f; // thickness of the sensor in cm, for all layers for the moment, the default is set to 50 microns + ChipSpecifics iTofChipSpecifics{258, 271, 250.00e-4, 100.00e-4, 0.00f, 0.00e-4, 0.00e-4, 50.e-4, 50.e-4}; + ChipSpecifics oTofChipSpecifics{251, 487, 250.00e-4, 100.00e-4, 0.00f, 0.00e-4, 106.48e-4, 50.e-4, 50.e-4}; + O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx index f7d0eb135a27a..61ea960409e7c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include +#include #include namespace o2 @@ -55,6 +56,171 @@ GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : DetMatrixCache() } } +int GeometryTGeo::extractNumberOfStavesIOTOF(int lay) const +{ + int numberOfStaves{0}; + + std::string layName = lay == 0 ? GeometryTGeo::getITOFLayerPattern() : GeometryTGeo::getOTOFLayerPattern(); + TGeoVolume* layV = gGeoManager->GetVolume(layName.c_str()); + if (layV == nullptr) { + LOG(fatal) << "Can't find volume " << layName; + return -1; + } + + TObjArray* nodes = layV->GetNodes(); + int nNodes = nodes->GetEntriesFast(); + + for (int j{0}; j < nNodes; ++j) { + if (strstr(nodes->At(j)->GetName(), lay == 0 ? GeometryTGeo::getITOFStavePattern() : GeometryTGeo::getOTOFStavePattern()) != nullptr) { + numberOfStaves++; + } + } + + return numberOfStaves; +} + +int GeometryTGeo::extractNumberOfModulesIOTOF(int lay) const +{ + int numberOfModules{0}; + + std::string staveName = lay == 0 ? GeometryTGeo::getITOFStavePattern() : GeometryTGeo::getOTOFStavePattern(); + TGeoVolume* staveV = gGeoManager->GetVolume(staveName.c_str()); + if (staveV == nullptr) { + LOG(fatal) << "Can't find volume " << staveName; + return -1; + } + + TObjArray* nodes = staveV->GetNodes(); + int nNodes = nodes->GetEntriesFast(); + + for (int j{0}; j < nNodes; ++j) { + if (strstr(nodes->At(j)->GetName(), lay == 0 ? GeometryTGeo::getITOFModulePattern() : GeometryTGeo::getOTOFModulePattern()) != nullptr) { + numberOfModules++; + } + } + + return numberOfModules; +} + +int GeometryTGeo::extractNumberOfChipsPerModuleIOTOF(int lay) const +{ + int numberOfChips{0}; + + std::string moduleName = lay == 0 ? GeometryTGeo::getITOFModulePattern() : GeometryTGeo::getOTOFModulePattern(); + TGeoVolume* moduleV = gGeoManager->GetVolume(moduleName.c_str()); + if (moduleV == nullptr) { + LOG(fatal) << "Can't find volume " << moduleName; + return -1; + } + + TObjArray* nodes = moduleV->GetNodes(); + int nNodes = nodes->GetEntriesFast(); + + for (int j{0}; j < nNodes; ++j) { + if (strstr(nodes->At(j)->GetName(), lay == 0 ? GeometryTGeo::getITOFChipPattern() : GeometryTGeo::getOTOFChipPattern()) != nullptr) { + numberOfChips++; + } + } + + return numberOfChips; +} + +int GeometryTGeo::extractNumberOfChipsFTOF() const +{ + return 0; +} + +int GeometryTGeo::extractNumberOfChipsBTOF() const +{ + return 0; +} + +int GeometryTGeo::getIOTOFFirstChipIndex(int lay) const +{ + return lay == 0 ? 0 : mLastChipIndex[0] + 1; +} + +int GeometryTGeo::getIOTOFLayer(int index) const +{ + if (index < 0 || index > mLastChipIndex[1]) { + LOG(fatal) << "Invalid chip index " << index; + return -1; + } + return index > mLastChipIndex[0] ? 1 : 0; +} + +int GeometryTGeo::getIOTOFChipIndex(int lay, int sta, int mod, int chip) const +{ + return getIOTOFFirstChipIndex(lay) + (sta - 1) * mNumberOfChipsPerStaveIOTOF[lay] + (mod - 1) * mNumberOfChipsPerModuleIOTOF[lay] + (chip - 1); +} + +bool GeometryTGeo::getIOTOFChipId(int index, int& lay, int& sta, int& mod, int& chip) const +{ + lay = getIOTOFLayer(index); + index -= getIOTOFFirstChipIndex(lay); + sta = mNumberOfStavesIOTOF[lay] > 0 ? index / mNumberOfChipsPerStaveIOTOF[lay] : -1; + index %= mNumberOfChipsPerStaveIOTOF[lay]; + mod = mNumberOfModulesIOTOF[lay] > 0 ? index / mNumberOfChipsPerModuleIOTOF[lay] : -1; + chip = index % mNumberOfChipsPerModuleIOTOF[lay]; + return true; +} + +TString GeometryTGeo::getMatrixPath(int index) const +{ + int lay, sta, mod, chip; + getIOTOFChipId(index, lay, sta, mod, chip); + + TString path = Form("/cave_1/barrel_1/%s_2/", GeometryTGeo::getIOTOFVolPattern()); + sta += 1; + mod += 1; + chip += 1; + + if (lay == 0) { + path += Form("%s_1/", GeometryTGeo::getITOFLayerPattern()); + if (mNumberOfStavesIOTOF[lay] > 0) + path += Form("%s_%d/", GeometryTGeo::getITOFStavePattern(), sta); + if (mNumberOfModulesIOTOF[lay] > 0) + path += Form("%s_%d/", GeometryTGeo::getITOFModulePattern(), mod); + if (mNumberOfChipsPerModuleIOTOF[lay] > 0) + path += Form("%s_%d/%s_1", GeometryTGeo::getITOFChipPattern(), chip, GeometryTGeo::getITOFSensorPattern()); + } else { + path += Form("%s_1/", GeometryTGeo::getOTOFLayerPattern()); + if (mNumberOfStavesIOTOF[lay] > 0) + path += Form("%s_%d/", GeometryTGeo::getOTOFStavePattern(), sta); + if (mNumberOfModulesIOTOF[lay] > 0) + path += Form("%s_%d/", GeometryTGeo::getOTOFModulePattern(), mod); + if (mNumberOfChipsPerModuleIOTOF[lay] > 0) + path += Form("%s_%d/%s_1", GeometryTGeo::getOTOFChipPattern(), chip, GeometryTGeo::getOTOFSensorPattern()); + } + + return path; +} + +TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const +{ + auto path = getMatrixPath(index); + + static TGeoHMatrix matTmp; + gGeoManager->PushPath(); + + if (!gGeoManager->cd(path.Data())) { + gGeoManager->PopPath(); + LOG(error) << "Error in cd-ing to " << path.Data(); + return nullptr; + } + + matTmp = *gGeoManager->GetCurrentMatrix(); + // LOG(info) << "Path = " << path.Data(); + + // Restore the modeler state + gGeoManager->PopPath(); + + // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor thicknesses + // TODO: apply translation by the effective sensor thickness, not yet done (see ITS) + + return &matTmp; +} + void GeometryTGeo::Build(int loadTrans) { if (isBuilt()) { @@ -66,11 +232,58 @@ void GeometryTGeo::Build(int loadTrans) LOGP(fatal, "Geometry is not loaded"); } + auto& iotofPars = IOTOFBaseParam::Instance(); + if (!iotofPars.segmentedInnerTOF && !iotofPars.segmentedOuterTOF) { + return; + } + + // Inner/outer TOF + for (int j{0}; j < 2; ++j) { + mNumberOfStavesIOTOF[j] = extractNumberOfStavesIOTOF(j); + mNumberOfModulesIOTOF[j] = extractNumberOfModulesIOTOF(j); + mNumberOfChipsPerModuleIOTOF[j] = extractNumberOfChipsPerModuleIOTOF(j); + } + + // Forward TOF + mNumberOfChipsFTOF = extractNumberOfChipsFTOF(); + + // Backward TOF + mNumberOfChipsBTOF = extractNumberOfChipsBTOF(); + + int numberOfChips{0}; + for (int j{0}; j < 2; ++j) { + mNumberOfChipsPerStaveIOTOF[j] = mNumberOfModulesIOTOF[j] * mNumberOfChipsPerModuleIOTOF[j]; + mNumberOfChipsIOTOF[j] = mNumberOfStavesIOTOF[j] * mNumberOfChipsPerStaveIOTOF[j]; + numberOfChips += mNumberOfChipsIOTOF[j]; + mLastChipIndex[j] = numberOfChips - 1; + } + + LOG(info) << "numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" << mNumberOfChipsPerStaveIOTOF[0]; + + setSize(numberOfChips); fillMatrixCache(loadTrans); + // fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); } void GeometryTGeo::fillMatrixCache(int mask) { + if (mSize < 1) { + LOG(warning) << "The method Build was not called yet"; + Build(mask); + return; + } + + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)) && !getCacheL2G().isFilled()) { + // Matrices for Local (Sensor!!! rather than the full chip) to Global frame transformation + LOG(info) << "Loading " << getName() << " L2G matrices from TGeo; there are " << mSize << " matrices"; + auto& cacheL2G = getCacheL2G(); + cacheL2G.setSize(mSize); + + for (int i = 0; i < mSize; i++) { + TGeoHMatrix* hm = extractMatrixSensor(i); + cacheL2G.setMatrix(Mat3D(*hm), i); + } + } } GeometryTGeo* GeometryTGeo::Instance() diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt index f3418d9065fcb..25d623c0047a9 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt @@ -14,6 +14,7 @@ o2_add_library(IOTOFSimulation src/Detector.cxx src/Digitizer.cxx # src/IOTOFServices.cxx + src/Segmentation.cxx PUBLIC_LINK_LIBRARIES O2::IOTOFBase O2::DataFormatsIOTOF O2::ITSMFTSimulation) @@ -21,5 +22,7 @@ o2_add_library(IOTOFSimulation o2_target_root_dictionary(IOTOFSimulation HEADERS include/IOTOFSimulation/Detector.h include/IOTOFSimulation/Layer.h - include/IOTOFSimulation/Digitizer.h) - # include/IOTOFSimulation/IOTOFServices.h) \ No newline at end of file + include/IOTOFSimulation/Digitizer.h + # include/IOTOFSimulation/IOTOFServices.h + include/IOTOFSimulation/Segmentation.h + ) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h index 8964e33f8a1b6..aae989248f07e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h @@ -27,6 +27,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "IOTOFBase/GeometryTGeo.h" +#include "IOTOFSimulation/Segmentation.h" namespace o2::iotof { @@ -105,6 +106,8 @@ class Digitizer float mTimeResolution = 0.020f; ///< time resolution sigma in ns (20 ps default) float mEfficiency = 0.98f; ///< detection efficiency float mEnergyToCharge = 3.6e-9f; ///< energy loss to electrons conversion (3.6 eV per e-h pair in Si) + + static o2::iotof::Segmentation* sSegmentation; ///< IOTOF segmentation instance (singleton) }; } // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h new file mode 100644 index 0000000000000..efa68dba6de1c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Segmentation.h @@ -0,0 +1,215 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Segmentation.h +/// \brief Definition of the Segmentation class +/// \author Giorgio Alberto Lucia: giorgio.alberto.lucia@cern.ch + +#ifndef ALICEO2_IOTOF_SEGMENTATION_H +#define ALICEO2_IOTOF_SEGMENTATION_H + +#include +#include +#include "MathUtils/Cartesian.h" +#include "IOTOFBase/IOTOFBaseParam.h" + +namespace o2 +{ +namespace iotof +{ + +/// Segmentation and response for pixels in inner and outer TOF of the ALICE 3 apparatus +/// Questions to solve: +class Segmentation +{ + private: + Segmentation(); + static std::unique_ptr sInstance; + + public: + ChipSpecifics mITofSpecsConfig; + ChipSpecifics mOTofSpecsConfig; + static Segmentation* Instance(); + + ~Segmentation() = default; + + void configChip(const int nCols, const int nRows, const float pitchCol, const float pitchRow, const float passiveEdgeReadOut, const float passiveEdgeTop, + const float passiveEdgeSide, const float sensorLayerThicknessEff, const float sensorLayerThickness, const int subDetectorID); + void configChip(const ChipSpecifics& specsConfig, const int subDetectorID); + + /// Transformation from Geant detector centered local coordinates (cm) to + /// Pixel cell numbers iRow and iCol. + /// Returns kTRUE if point x,z is inside sensitive volume, kFALSE otherwise. + /// A value of -1 for iRow or iCol indicates that this point is outside of the + /// detector segmentation as defined. + /// \param float x Detector local coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param float z Detector local coordinate z in cm with respect to + /// the center of the sensitive volulme. + /// \param int iRow Detector x cell coordinate. Has the range 0 <= iRow < mNumberOfRows + /// \param int iCol Detector z cell coordinate. Has the range 0 <= iCol < mNumberOfColumns + bool localToDetector(float x, float z, int& iRow, int& iCol, const int subDetectorID); + /// same but w/o check for row/column range + void localToDetectorUnchecked(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID); + + /// Transformation from Detector cell coordiantes to Geant detector centered + /// local coordinates (cm) + /// \param int iRow Detector x cell coordinate. Has the range 0 <= iRow < mNumberOfRows + /// \param int iCol Detector z cell coordinate. Has the range 0 <= iCol < mNumberOfColumns + /// \param float x Detector local coordinate x in cm with respect to the + /// center of the sensitive volume. + /// \param float z Detector local coordinate z in cm with respect to the + /// center of the sensitive volulme. + /// If iRow and or iCol is outside of the segmentation range a value of -0.5*Dx() + /// or -0.5*Dz() is returned. + + // w/o check for row/col range + template + void detectorToLocalUnchecked(L row, L col, T& xRow, T& zCol, const int subDetectorID) + { + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + xRow = getFirstRowCoordinate(subDetectorID) - row * specsConfig.PitchRow; + zCol = col * specsConfig.PitchCol + getFirstColCoordinate(subDetectorID); + } + template + void detectorToLocalUnchecked(L row, L col, math_utils::Point3D& loc, const int subDetectorID) + { + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + loc.SetCoordinates(getFirstRowCoordinate(subDetectorID) - row * specsConfig.PitchRow, T(0.), col * specsConfig.PitchCol + getFirstColCoordinate(subDetectorID)); + } + template + void detectorToLocalUnchecked(L row, L col, std::array& loc, const int subDetectorID) + { + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + loc[0] = getFirstRowCoordinate(subDetectorID) - row * specsConfig.PitchRow; + loc[1] = T(0); + loc[2] = col * specsConfig.PitchCol + getFirstColCoordinate(subDetectorID); + } + + // same but with check for row/col range + + template + bool detectorToLocal(L row, L col, T& xRow, T& zCol, const int subDetectorID) + { + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + if (row < 0 || row >= specsConfig.NRows || col < 0 || col >= specsConfig.NCols) { + return false; + } + detectorToLocalUnchecked(row, col, xRow, zCol, subDetectorID); + return true; + } + + template + bool detectorToLocal(L row, L col, math_utils::Point3D& loc, const int subDetectorID) + { + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + if (row < 0 || row >= specsConfig.NRows || col < 0 || col >= specsConfig.NCols) { + return false; + } + detectorToLocalUnchecked(row, col, loc, subDetectorID); + return true; + } + template + bool detectorToLocal(L row, L col, std::array& loc, const int subDetectorID) + { + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + if (row < 0 || row >= specsConfig.NRows || col < 0 || col >= specsConfig.NCols) { + return false; + } + detectorToLocalUnchecked(row, col, loc, subDetectorID); + return true; + } + + float getFirstRowCoordinate(const int subDetectorID) + { + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + return 0.5 * ((specsConfig.ActiveMatrixSizeRows() - specsConfig.PassiveEdgeTop + specsConfig.PassiveEdgeReadOut) - specsConfig.PitchRow); + } + float getFirstColCoordinate(const int subDetectorID) + { + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + return 0.5 * (specsConfig.PitchCol - specsConfig.ActiveMatrixSizeCols()); + } + + void print(); + + ClassDefNV(Segmentation, 1); // Segmentation class upgrade pixels +}; + +//_________________________________________________________________________________________________ +inline void Segmentation::localToDetectorUnchecked(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID) +{ + // convert to row/col w/o over/underflow check + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + xRow = 0.5 * (specsConfig.ActiveMatrixSizeRows() - specsConfig.PassiveEdgeTop + specsConfig.PassiveEdgeReadOut) - xRow; // coordinate wrt top edge of Active matrix + zCol += 0.5 * specsConfig.ActiveMatrixSizeCols(); // coordinate wrt left edge of Active matrix + iRow = int(xRow / specsConfig.PitchRow); + iCol = int(zCol / specsConfig.PitchCol); + if (xRow < 0) { + iRow -= 1; + } + if (zCol < 0) { + iCol -= 1; + } +} + +//_________________________________________________________________________________________________ +inline bool Segmentation::localToDetector(float xRow, float zCol, int& iRow, int& iCol, const int subDetectorID) +{ + // convert to row/col + if (subDetectorID != 0 && subDetectorID != 1) { + iRow = iCol = -1; + return false; + } + const ChipSpecifics& specsConfig = (subDetectorID == 0) ? mITofSpecsConfig : mOTofSpecsConfig; + xRow = 0.5 * (specsConfig.ActiveMatrixSizeRows() - specsConfig.PassiveEdgeTop + specsConfig.PassiveEdgeReadOut) - xRow; // coordinate wrt top edge of Active matrix + zCol += 0.5 * specsConfig.ActiveMatrixSizeCols(); // coordinate wrt left edge of Active matrix + if (xRow < 0 || xRow >= specsConfig.ActiveMatrixSizeRows() || zCol < 0 || zCol >= specsConfig.ActiveMatrixSizeCols()) { + iRow = iCol = -1; + return false; + } + iRow = int(xRow / specsConfig.PitchRow); + iCol = int(zCol / specsConfig.PitchCol); + return true; +} + +} // namespace iotof +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 59b914a3dd076..bed8cbfd6dfac 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -314,13 +314,29 @@ bool Detector::ProcessHits(FairVolume* vol) TLorentzVector positionStop; fMC->TrackPosition(positionStop); // Retrieve the indices with the volume path - int stave(0), halfstave(0), chipinmodule(0), module; + int stave(0), chipinmodule(0), module(0); fMC->CurrentVolOffID(1, chipinmodule); fMC->CurrentVolOffID(2, module); - fMC->CurrentVolOffID(3, halfstave); - fMC->CurrentVolOffID(4, stave); + fMC->CurrentVolOffID(3, stave); - o2::itsmft::Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), + int sensorID = lay; + auto& iotofPars = IOTOFBaseParam::Instance(); + + int layN = -1; + if (strstr(vol->GetName(), GeometryTGeo::getITOFSensorPattern()) != nullptr) { + layN = 0; + } else if (strstr(vol->GetName(), GeometryTGeo::getOTOFSensorPattern())) { + layN = 1; + } + if (iotofPars.segmentedInnerTOF && iotofPars.segmentedOuterTOF) { + if (layN > -1) { + sensorID = mGeometryTGeo->getIOTOFChipIndex(layN, stave, module, chipinmodule); + } else { + sensorID += (mGeometryTGeo->getSize() - 1); // temporary as f/b tof is not yet segmented + } + } + + o2::itsmft::Hit* p = addHit(stack->GetCurrentTrackNumber(), sensorID, mTrackData.mPositionStart.Vect(), positionStop.Vect(), mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx index b865d6958ecfd..8e5e74dd1f0ca 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx @@ -29,6 +29,8 @@ namespace o2::iotof { +o2::iotof::Segmentation* Digitizer::sSegmentation = nullptr; + //_______________________________________________________________________ void Digitizer::init() { @@ -37,6 +39,7 @@ void Digitizer::init() LOG(info) << " Charge threshold: " << mChargeThreshold << " electrons"; LOG(info) << " Detection efficiency: " << mEfficiency * 100 << " %"; LOG(info) << " Continuous mode: " << (mContinuous ? "ON" : "OFF"); + sSegmentation = o2::iotof::Segmentation::Instance(); } //_______________________________________________________________________ @@ -102,12 +105,27 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) // For now, use simple row/col mapping from detector ID // TODO: Implement proper segmentation when geometry is finalized uint16_t chipIndex = static_cast(detID); - uint16_t row = 0; // Will be determined from hit position - uint16_t col = 0; // Will be determined from hit position + + if (detID > mGeometry->getSize() || mGeometry->getSize() < 1) { + LOG(debug) << "Invalid detector ID: " << detID; + return; // invalid detector ID + } + const auto& matrix = mGeometry->getMatrixL2G(hit.GetDetectorID()); + + math_utils::Vector3D xyzPositionStart(matrix ^ (hit.GetPosStart())); // start position in sensor frame + // math_utils::Vector3D xyzPositionEnd(matrix ^ (hit.GetPos())); // end position in sensor frame + + int row = 0; // Will be determined from start hit position + int col = 0; // Will be determined from start hit position + + if (!sSegmentation->localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), row, col, mGeometry->getIOTOFLayer(detID))) { + LOG(debug) << "Hit position out of bounds for detector ID " << detID; + return; // hit is outside the active area + } // Create the digit with time information int digID = mDigits->size(); - mDigits->emplace_back(chipIndex, row, col, charge, smearedTime); + mDigits->emplace_back(chipIndex, static_cast(row), static_cast(col), charge, smearedTime); LOG(debug) << "Created digit #" << digID << " chip=" << chipIndex << " charge=" << charge << " time=" << smearedTime << " ns"; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index b603d2a4a423b..4f76d71b63aa3 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -197,8 +197,8 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) setChipStyle(chipVol); // Finally we create the volume of the sensor, which is the same for all chips - const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction - const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction + const int sensorsPerChipX = 1; // we assume that each chip is divided in 2 sensors along the x direction + const int sensorsPerChipZ = 1; // we assume that each chip is divided in 2 sensors along the z direction const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm const double sensorSizeY = mSensorThickness; // cm const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm @@ -331,8 +331,8 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) setChipStyle(chipVol); // Finally we create the volume of the sensor, which is the same for all chips - const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction - const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction + const int sensorsPerChipX = 1; // we assume that each chip is divided in 2 sensors along the x direction + const int sensorsPerChipZ = 1; // we assume that each chip is divided in 2 sensors along the z direction const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm const double sensorSizeY = mSensorThickness; // cm const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Segmentation.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Segmentation.cxx new file mode 100644 index 0000000000000..bbfb60234210d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Segmentation.cxx @@ -0,0 +1,90 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Segmentation.cxx +/// \brief Implementation of the Segmentation class + +#include "IOTOFSimulation/Segmentation.h" +#include "IOTOFBase/IOTOFBaseParam.h" +#include + +namespace o2 +{ + +namespace iotof +{ + +std::unique_ptr Segmentation::sInstance; + +Segmentation* Segmentation::Instance() +{ + if (!sInstance) { + sInstance = std::unique_ptr(new Segmentation()); + } + return sInstance.get(); +} + +Segmentation::Segmentation() +{ + if (sInstance) { + printf("Invalid use of public constructor: o2::iotof::Segmentation instance exists\n"); + } else { + auto& iotofPars = IOTOFBaseParam::Instance(); + const ChipSpecifics& mITofChipPars = iotofPars.iTofChipSpecifics; + const ChipSpecifics& mOTofChipPars = iotofPars.oTofChipSpecifics; + + configChip(mITofChipPars, 0 /* subDetectorID for iTOF */); + configChip(mOTofChipPars, 1 /* subDetectorID for oTOF */); + } +} + +void Segmentation::configChip(const int nCols, const int nRows, const float pitchCol, const float pitchRow, const float passiveEdgeReadOut, + const float passiveEdgeTop, const float passiveEdgeSide, const float sensorLayerThicknessEff, const float sensorLayerThickness, const int subDetectorID) +{ + if (subDetectorID == 0) { + mITofSpecsConfig = ChipSpecifics(nCols, nRows, pitchCol, pitchRow, passiveEdgeReadOut, passiveEdgeTop, passiveEdgeSide, sensorLayerThicknessEff, sensorLayerThickness); + } else if (subDetectorID == 1) { + mOTofSpecsConfig = ChipSpecifics(nCols, nRows, pitchCol, pitchRow, passiveEdgeReadOut, passiveEdgeTop, passiveEdgeSide, sensorLayerThicknessEff, sensorLayerThickness); + } else { + printf("Invalid subDetectorID %d. Must be 0 (iTOF) or 1 (oTOF). No configuration applied.\n", subDetectorID); + } +} + +void Segmentation::configChip(const ChipSpecifics& specsConfig, const int subDetectorID) +{ + if (subDetectorID == 0) { + mITofSpecsConfig = specsConfig; + } else if (subDetectorID == 1) { + mOTofSpecsConfig = specsConfig; + } else { + printf("Invalid subDetectorID %d. Must be 0 (iTOF) or 1 (oTOF). No configuration applied.\n", subDetectorID); + } +} + +void Segmentation::print() +{ + // iTOF specs + printf("iTOF specs:\n"); + printf("Pixel size: %.2f (along %d rows) %.2f (along %d columns) microns\n", mITofSpecsConfig.PitchRow * 1e4, mITofSpecsConfig.NRows, mITofSpecsConfig.PitchCol * 1e4, mITofSpecsConfig.NCols); + printf("Passive edges: bottom: %.2f, top: %.2f, left/right: %.2f microns\n", mITofSpecsConfig.PassiveEdgeReadOut * 1e4, mITofSpecsConfig.PassiveEdgeTop * 1e4, mITofSpecsConfig.PassiveEdgeSide * 1e4); + printf("Active/Total size: %.6f/%.6f (rows) %.6f/%.6f (cols) cm\n", mITofSpecsConfig.ActiveMatrixSizeRows(), mITofSpecsConfig.SensorSizeRows(), mITofSpecsConfig.ActiveMatrixSizeCols(), mITofSpecsConfig.SensorSizeCols()); + + // oTOF specs + printf("oTOF specs:\n"); + printf("Pixel size: %.2f (along %d rows) %.2f (along %d columns) microns\n", mOTofSpecsConfig.PitchRow * 1e4, mOTofSpecsConfig.NRows, mOTofSpecsConfig.PitchCol * 1e4, mOTofSpecsConfig.NCols); + printf("Passive edges: bottom: %.2f, top: %.2f, left/right: %.2f microns\n", mOTofSpecsConfig.PassiveEdgeReadOut * 1e4, mOTofSpecsConfig.PassiveEdgeTop * 1e4, mOTofSpecsConfig.PassiveEdgeSide * 1e4); + printf("Active/Total size: %.6f/%.6f (rows) %.6f/%.6f (cols) cm\n", mOTofSpecsConfig.ActiveMatrixSizeRows(), mOTofSpecsConfig.SensorSizeRows(), mOTofSpecsConfig.ActiveMatrixSizeCols(), mOTofSpecsConfig.SensorSizeCols()); +} + +} // namespace iotof +} // namespace o2 + +ClassImp(o2::iotof::Segmentation);