From 2871e5f84fde79fe8091cd79582dd0a615c1a712 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Thu, 25 Sep 2025 11:58:51 +0200 Subject: [PATCH 01/99] wip(iris): import stash-pre-rebase changes (cherry picked from commit d291e57a2f121c4f19f1fb7ccd8031328abc9ce8) --- .../TRK/base/include/TRKBase/GeometryTGeo.h | 6 + .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 6 +- .../ALICE3/TRK/simulation/CMakeLists.txt | 4 + .../include/TRKSimulation/Detector.h | 2 +- .../include/TRKSimulation/VDGeometryBuilder.h | 35 + .../include/TRKSimulation/VDLayer.h | 110 +++ .../include/TRKSimulation/VDSensorRegistry.h | 27 + .../ALICE3/TRK/simulation/src/Detector.cxx | 57 +- .../ALICE3/TRK/simulation/src/TRKServices.cxx | 4 +- .../TRK/simulation/src/TRKSimulationLinkDef.h | 1 + .../TRK/simulation/src/VDGeometryBuilder.cxx | 743 ++++++++++++++++++ .../simulation/src/VDGeometryBuilder_com.cxx | 185 +++++ .../ALICE3/TRK/simulation/src/VDLayer.cxx | 314 ++++++++ 13 files changed, 1460 insertions(+), 34 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index a1e4b9321130f..db046b8d8e9eb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -42,12 +42,15 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache }; static const char* getTRKVolPattern() { return sVolumeName.c_str(); } static const char* getTRKLayerPattern() { return sLayerName.c_str(); } + static const char* getVDLayerPattern() { return sVDLayerName.c_str(); } + static const char* getVDDiskPattern() { return sVDDiskName.c_str(); } static const char* getTRKPetalPattern() { return sPetalName.c_str(); } static const char* getTRKPetalDiskPattern() { return sPetalDiskName.c_str(); } static const char* getTRKPetalLayerPattern() { return sPetalLayerName.c_str(); } static const char* getTRKStavePattern() { return sStaveName.c_str(); } static const char* getTRKChipPattern() { return sChipName.c_str(); } static const char* getTRKSensorPattern() { return sSensorName.c_str(); } + static const char* getVDSensorPattern() { return sVDSensorName.c_str(); } static const char* getTRKWrapVolPattern() { return sWrapperVolumeName.c_str(); } int getNumberOfChips() const { return mSize; } @@ -141,6 +144,9 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static std::string sVolumeName; static std::string sLayerName; + static std::string sVDLayerName; + static std::string sVDDiskName; + static std::string sVDSensorName; static std::string sPetalName; static std::string sPetalDiskName; static std::string sPetalLayerName; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 20088179f4dcc..4a22b0e3e2e42 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -24,12 +24,16 @@ std::unique_ptr GeometryTGeo::sInstance; // Names std::string GeometryTGeo::sVolumeName = "TRKV"; std::string GeometryTGeo::sLayerName = "TRKLayer"; -std::string GeometryTGeo::sPetalName = "PETALCASE"; std::string GeometryTGeo::sPetalDiskName = "DISK"; std::string GeometryTGeo::sPetalLayerName = "LAYER"; std::string GeometryTGeo::sStaveName = "TRKStave"; std::string GeometryTGeo::sChipName = "TRKChip"; std::string GeometryTGeo::sSensorName = "TRKSensor"; + +std::string GeometryTGeo::sPetalName = "Petal"; +std::string GeometryTGeo::sVDLayerName = "Layer"; +std::string GeometryTGeo::sVDDiskName = "Disk"; +std::string GeometryTGeo::sVDSensorName = "VDSensor"; std::string GeometryTGeo::sWrapperVolumeName = "TRKUWrapVol"; ///< Wrapper volume name, not implemented at the moment o2::trk::GeometryTGeo::~GeometryTGeo() diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt index ab817a3fdaa0d..1366c42f3fae6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -21,6 +21,8 @@ o2_add_library(TRKSimulation src/TRKPetalCase.cxx src/TRKPetalLayer.cxx src/TRKPetalDisk.cxx + src/VDLayer.cxx + src/VDGeometryBuilder.cxx PUBLIC_LINK_LIBRARIES O2::TRKBase O2::FT3Simulation O2::ITSMFTSimulation @@ -38,4 +40,6 @@ o2_target_root_dictionary(TRKSimulation include/TRKSimulation/TRKPetalCase.h include/TRKSimulation/TRKPetalLayer.h include/TRKSimulation/TRKPetalDisk.h + include/TRKSimulation/VDLayer.h + include/TRKSimulation/VDGeometryBuilder.h include/TRKSimulation/DPLDigitizerParam.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h index 8ed5737abcb35..0fe0d55dccde7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h @@ -71,7 +71,7 @@ class Detector : public o2::base::DetImpl } void configDefault(); - void buildTRKNewVacuumVessel(); + void buildTRKMiddleOuterLayers(); void configFromFile(std::string fileName = "alice3_TRK_layout.txt"); void configToFile(std::string fileName = "alice3_TRK_layout.txt"); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h new file mode 100644 index 0000000000000..0a2cb68f2233a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRK_VDGEOMETRYBUILDER_H +#define O2_TRK_VDGEOMETRYBUILDER_H + +class TGeoVolume; + +#include +#include + +namespace o2::trk +{ + +// Build full VD for each design. +// Each function builds one local petal assembly (walls + layers + disks) +// and then places/rotates the petal once into the mother volume. + +void createIRIS4Geometry(TGeoVolume* motherVolume); // 4 petals, cylindrical L0 +void createIRIS4aGeometry(TGeoVolume* motherVolume); // 3 petals, cylindrical L0 +void createIRIS5Geometry(TGeoVolume* motherVolume); // 4 petals, rectangular L0 + +void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID = 0, int nPetals = 4, bool rectangularL0 = false); + +} // namespace o2::trk + +#endif // O2_TRK_VDGEOMETRYBUILDER_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h new file mode 100644 index 0000000000000..9e9ca2971bc3b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h @@ -0,0 +1,110 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_VD_LAYER_H +#define ALICEO2_VD_LAYER_H + +#include +#include + +class TGeoVolume; +class TGeoMatrix; + +namespace o2 +{ +namespace trk +{ + +// Base class for a VD layer +class VDLayer +{ + public: + VDLayer() = default; + VDLayer(int layerNumber, const std::string& layerName, double layerX2X0); + virtual ~VDLayer() = default; + + // Create the layer (AIR container + sensors) and insert it into mother + virtual void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const = 0; + + double getChipThickness() const { return mChipThickness; } + + protected: + int mLayerNumber{0}; + std::string mLayerName; + double mX2X0{0.f}; // Radiation length in units of X0 + double mChipThickness{0.f}; // thickness derived from X/X0 + double mModuleWidth{4.54f}; // cm + + // ClassDef(VDLayer, 1) +}; + +// Cylindrical segment layer +class VDCylindricalLayer : public VDLayer +{ + public: + VDCylindricalLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double radius, double phiSpanDeg, double lengthZ, double lengthSensZ); + + TGeoVolume* createSensor() const; // builds the sensor volume + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + private: + double mRadius{0.f}; + double mPhiSpanDeg{0.f}; // degrees + double mLengthZ{0.f}; // layer container length in Z + double mLengthSensZ{0.f}; // sensor length in Z + + // ClassDef(VDCylindricalLayer, 1) +}; + +// Rectangular segment layer +class VDRectangularLayer : public VDLayer +{ + public: + VDRectangularLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double width, double lengthZ, double lengthSensZ); + + TGeoVolume* createSensor() const; + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + private: + double mWidth{0.f}; + double mLengthZ{0.f}; + double mLengthSensZ{0.f}; + + // ClassDef(VDRectangularLayer, 1) +}; + +// Disk segment layer +class VDDiskLayer : public VDLayer +{ + public: + VDDiskLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double rMin, double rMax, double phiSpanDeg, double zPos); + + TGeoVolume* createSensor() const; + void createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans = nullptr) const override; + + double getZPosition() const { return mZPos; } + + private: + double mRMin{0.f}; + double mRMax{0.f}; + double mPhiSpanDeg{0.f}; // degrees + double mZPos{0.f}; // placement along Z + + // ClassDef(VDDiskLayer, 1) +}; + +} // namespace trk +} // namespace o2 + +#endif // ALICEO2_VD_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h new file mode 100644 index 0000000000000..116b82bcb4a49 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h @@ -0,0 +1,27 @@ +#ifndef O2_TRK_VDSENSORREGISTRY_H +#define O2_TRK_VDSENSORREGISTRY_H + +#include +#include + +namespace o2::trk +{ + +struct VDSensorDesc { + enum class Kind { Barrel, + Disk }; + std::string name; // sensor volume name + int petal = -1; + Kind kind = Kind::Barrel; + int idx = -1; // layer or disk index +}; + +// Accessor (defined in VDGeometryBuilder.cxx) +std::vector& vdSensorRegistry(); + +// Utilities (defined in VDGeometryBuilder.cxx) +void clearVDSensorRegistry(); +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Kind kind, int idx); + +} // namespace o2::trk +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index f5027310fa66d..03aff419d1a96 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -19,6 +19,8 @@ #include "ITSMFTSimulation/Hit.h" #include "TRKSimulation/Detector.h" #include "TRKBase/TRKBaseParam.h" +#include "TRKSimulation/VDGeometryBuilder.h" +#include "TRKSimulation/VDSensorRegistry.h" using o2::itsmft::Hit; @@ -26,6 +28,7 @@ namespace o2 { namespace trk { + float getDetLengthFromEta(const float eta, const float radius) { return 2. * (10. + radius * std::cos(2 * std::atan(std::exp(-eta)))); @@ -48,7 +51,7 @@ Detector::Detector(bool active) if (trkPars.configFile != "") { configFromFile(trkPars.configFile); } else { - buildTRKNewVacuumVessel(); + buildTRKMiddleOuterLayers(); configToFile(); configServices(); } @@ -115,7 +118,7 @@ void Detector::configDefault() mLayers.emplace_back(7, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(7)}, 80.f, 258.f, 100.e-3); } -void Detector::buildTRKNewVacuumVessel() +void Detector::buildTRKMiddleOuterLayers() { // Build the TRK detector according to changes proposed during // https://indico.cern.ch/event/1407704/ @@ -125,9 +128,6 @@ void Detector::buildTRKNewVacuumVessel() mLayers.clear(); LOGP(warning, "Loading \"After Upgrade Days March 2024\" configuration for ALICE3 TRK"); - // mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 0.5f, 50.f, 100.e-4); - // mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 1.2f, 50.f, 100.e-4); - // mLayers.emplace_back(2, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(2)}, 2.5f, 50.f, 100.e-4); mLayers.emplace_back(0, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}, 7.f, 124.f, 100.e-3); LOGP(info, "TRKLayer created. Name: {}", std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(0)}); mLayers.emplace_back(1, std::string{GeometryTGeo::getTRKLayerPattern() + std::to_string(1)}, 9.f, 124.f, 100.e-3); @@ -260,13 +260,16 @@ void Detector::createGeometry() // Add service for inner tracker mServices.createServices(vTRK); - mPetalCases.clear(); - // Add petal cases (the sensitive layers inside the petal cases get constructed here too) - auto& trkPars = TRKBaseParam::Instance(); - for (Int_t petalCaseNumber = 0; petalCaseNumber < sNumberVDPetalCases; ++petalCaseNumber) { - mPetalCases.emplace_back(petalCaseNumber, vTRK, trkPars.irisOpen); - mServices.excavateFromVacuum(mPetalCases[petalCaseNumber].getFullName()); - } + + // Build the VD using the petal builder + o2::trk::clearVDSensorRegistry(); + + // Choose the VD design (here: IRIS4 by default). + // You can wire this to a parameter in TRKBaseParam if desired. + // Alternatives: createIRIS5Geometry(vVD); createIRIS4aGeometry(vVD); + o2::trk::createIRIS4Geometry(vTRK); + + mServices.excavateFromVacuum("IRIS_CUTOUTsh"); mServices.registerVacuum(vTRK); } @@ -291,26 +294,18 @@ void Detector::defineSensitiveVolumes() TString volumeName; LOGP(info, "Adding TRK Sensitive Volumes"); - // Add petal case sensitive volumes - for (int petalCase = 0; petalCase < sNumberVDPetalCases; ++petalCase) { - // Petal layers - for (int petalLayer = 0; petalLayer < mPetalCases[petalCase].mPetalLayers.size(); ++petalLayer) { - volumeName = mPetalCases[petalCase].mPetalLayers[petalLayer].getSensorName(); - if (petalLayer == 0) { - mFirstOrLastLayers.push_back(volumeName.Data()); - } - LOGP(info, "Trying {}", volumeName.Data()); - v = geoManager->GetVolume(volumeName.Data()); - LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + // Register VD sensors created by VDGeometryBuilder + for (const auto& s : o2::trk::vdSensorRegistry()) { + TGeoVolume* v = gGeoManager->GetVolume(s.name.c_str()); + if (!v) { + LOGP(warning, "VD sensor volume '{}' not found", s.name); + continue; } - // Petal disks - for (int petalDisk = 0; petalDisk < mPetalCases[petalCase].mPetalDisks.size(); ++petalDisk) { - volumeName = mPetalCases[petalCase].mPetalDisks[petalDisk].getSensorName(); - LOGP(info, "Trying {}", volumeName.Data()); - v = geoManager->GetVolume(volumeName.Data()); - LOGP(info, "Adding TRK Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + LOGP(info, "Adding VD Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + // Optionally track first/last layers for TR references: + if (s.kind == o2::trk::VDSensorDesc::Kind::Barrel && (s.idx == 0 /*innermost*/)) { + mFirstOrLastLayers.push_back(s.name); } } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 1fb966425f974..e69ce9d1b4c34 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -173,7 +173,9 @@ void TRKServices::registerVacuum(TGeoVolume* motherVolume) TGeoVolume* vacuumVolume = new TGeoVolume("A3IP_VACUUM", vacuumComposite, kMedVac); // Add the vacuum to the barrel - vacuumVolume->SetLineColor(kGreen - 3); + vacuumVolume->SetLineColor(kAzure + 7); + vacuumVolume->SetTransparency(80); + motherVolume->AddNode(vacuumVolume, 1, new TGeoTranslation(0, 0, 0)); } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h index d80027593cef0..65a31b45bfab8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -19,6 +19,7 @@ #pragma link C++ class o2::trk::TRKLayer + ; #pragma link C++ class o2::trk::TRKPetalLayer + ; #pragma link C++ class o2::trk::TRKPetalDisk + ; +#pragma link C++ class o2::trk::VDLayer + ; #pragma link C++ class o2::trk::TRKServices + ; #pragma link C++ class o2::trk::Detector + ; #pragma link C++ class o2::base::DetImpl < o2::trk::Detector> + ; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx new file mode 100644 index 0000000000000..f52882bdcad37 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -0,0 +1,743 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKSimulation/VDGeometryBuilder.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TGeoManager.h" + +#include "Framework/Logger.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/VDLayer.h" +#include "TRKSimulation/VDSensorRegistry.h" + +namespace o2::trk +{ + +static std::vector gVDSensors; // stays in this TU only +std::vector& vdSensorRegistry() { return gVDSensors; } + +void clearVDSensorRegistry() { gVDSensors.clear(); } + +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Kind kind, int idx) +{ + gVDSensors.push_back({volName, petal, kind, idx}); +} + +static inline std::string makeSensorName(const std::string& layerName, int layerNumber) +{ + return Form("%s_%s%d", layerName.c_str(), o2::trk::GeometryTGeo::getVDSensorPattern(), layerNumber); +} + +namespace +{ + +// Config: which volumes count as SOLIDS to subtract from the vacuum volume +inline bool isSolidToCut(const TGeoVolume* v) +{ + const char* nm = v->GetName(); + const char* med = v->GetMedium() ? v->GetMedium()->GetName() : ""; + // silicon sensors (barrel + disks) + if (med && strcmp(med, "TRK_SILICON$") == 0) + return true; + // walls, sidewalls, cold-plate, service rings (names from your builders) + if (TString(nm).BeginsWith("VD_InnerWallArc")) + return true; + if (TString(nm).BeginsWith("VD_OuterWallArc")) + return true; + if (TString(nm).BeginsWith("VD_SideWall")) + return true; + if (TString(nm).Contains("_Coldplate")) + return true; + if (TString(nm).BeginsWith("IRIS_Service_Neg")) + return true; + if (TString(nm).BeginsWith("IRIS_Service_Pos_InVac")) + return true; + return false; +} + +// Ensure every leaf shape has a stable, informative name +inline const char* ensureShapeName(TGeoVolume* v) +{ + auto* sh = v->GetShape(); + TString nm = sh->GetName(); + if (nm.IsNull() || nm.BeginsWith("TGeo")) { + TString wanted = TString(v->GetName()) + "_sh"; + // avoid collisions + int k = 0; + TString cand = wanted; + auto* shapes = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + while (shapes && shapes->FindObject(cand)) + cand = Form("%s_%d", wanted.Data(), ++k); + sh->SetName(cand); + if (shapes && !shapes->FindObject(cand)) + shapes->Add(sh); + } + return sh->GetName(); +} + +// Recorder state for the petal-local composite +static TString gPetalSolidsFormula; +static int gLocalTrIdx = 0; + +// add "ShapeName:IRIS_LOC_TR_k" to the petal-local formula (no outer rotation) +inline void appendLocalTerm(const char* shapeName, const TGeoHMatrix& H) +{ + auto* ct = new TGeoCombiTrans(H); + ct->SetName(Form("IRIS_LOC_TR_%d", gLocalTrIdx++)); + ct->RegisterYourself(); + if (!gPetalSolidsFormula.IsNull()) + gPetalSolidsFormula += "+"; + gPetalSolidsFormula += TString::Format("%s:%s", shapeName, ct->GetName()); +} + +// DFS: compose LOCAL transforms only (identity prefix), to capture the petal contents +void traversePetalLocal(TGeoVolume* vol, const TGeoHMatrix& prefix) +{ + auto* nodes = vol->GetNodes(); + if (!nodes) + return; + for (int i = 0; i < nodes->GetEntriesFast(); ++i) { + auto* node = (TGeoNode*)nodes->At(i); + auto* childV = node->GetVolume(); + TGeoHMatrix H(prefix); + if (auto* m = node->GetMatrix()) + H.Multiply(m); + + if (isSolidToCut(childV)) { + const char* shapeName = ensureShapeName(childV); + appendLocalTerm(shapeName, H); + } + traversePetalLocal(childV, H); + } +} + +// Build (once) a petal-local composite containing ONLY solids (walls, silicon, coldplate, services, disks) +inline void buildPetalSolidsComposite(TGeoVolume* petalAsm) +{ + // If it already exists, skip + if (gGeoManager && gGeoManager->GetListOfShapes() && gGeoManager->GetListOfShapes()->FindObject("IRIS_PETAL_SOLIDSsh")) + return; + + gPetalSolidsFormula.Clear(); + gLocalTrIdx = 0; + + TGeoHMatrix I; // identity + traversePetalLocal(petalAsm, I); + + if (gPetalSolidsFormula.IsNull()) { + LOGP(error, "IRIS_PETAL_SOLIDSsh formula is empty; did not find solids in petal."); + return; + } + + LOGP(info, "IRIS_PETAL_SOLIDSsh formula: {}", gPetalSolidsFormula.Data()); + new TGeoCompositeShape("IRIS_PETAL_SOLIDSsh", gPetalSolidsFormula.Data()); +} + +// Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase +inline void buildIrisCutoutFromPetalSolid(int nPetals) +{ + // Create n rotation transforms + TString cutFormula; + for (int p = 0; p < nPetals; ++p) { + const double phi = (360.0 / nPetals) * (p + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phi); + auto* RT = new TGeoCombiTrans(0, 0, 0, R); + RT->SetName(Form("IRIS_PETAL_ROT_%d", p)); + RT->RegisterYourself(); + if (p) + cutFormula += "+"; + cutFormula += Form("IRIS_PETAL_SOLIDSsh:%s", RT->GetName()); + } + LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); + new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); + + // --- Sanity check: required matrices & shapes exist + auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() : nullptr; + auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + + if (!mats || !shps) { + LOGP(error, "IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing)."); + } else { + bool ok = true; + + // Check the petal rotations were registered and referenced + for (int p = 0; p < nPetals; ++p) { + const TString name = Form("IRIS_PETAL_ROT_%d", p); + if (!mats->FindObject(name)) { + LOGP(error, "IRIS cutout sanity: missing matrix {}", name.Data()); + ok = false; + } + } + + // Check that the local petal composite exists + if (!shps->FindObject("IRIS_PETAL_SOLIDSsh")) { + LOGP(error, "IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found."); + ok = false; + } + + // Check that the global cutout shape was created + if (!shps->FindObject("IRIS_CUTOUTsh")) { + LOGP(error, "IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found."); + ok = false; + } + + if (ok) { + LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); + } + } +} + +} // namespace + +// =================== Specs & constants (ROOT units: cm) =================== +static constexpr double kX2X0 = 100.e-4f; // all silicon +static constexpr double kLenZ_cm = 50.0f; // L0/L1/L2 Z length + +// Radii (cm) +static constexpr double rL0_cm = 0.5f; // 5 mm +static constexpr double rL1_cm = 1.2f; // 12 mm +static constexpr double rL2_cm = 2.5f; // 25 mm + +// IRIS5 rectangular L0 width (cm) +static constexpr double kL0RectHeight_cm = 0.5f; // 5.0 mm +static constexpr double kL0RectWidth_cm = 0.83f; // 8.3 mm + +// Disks radii (cm) +static constexpr double diskRin_cm = 0.5f; // 5 mm +static constexpr double diskRout_cm = 2.5f; // 25 mm +static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f}; + +// Petal walls specifications (cm) +static constexpr double kPetalZ_cm = 70.0f; // full wall height +static constexpr double kWallThick_cm = 0.015f; // 0.15 mm +static constexpr double kInnerWallRadius_cm = 0.48f; // 4.8 mm (ALWAYS cylindrical) +static constexpr double kOuterWallRadius_cm = 3.0f; // 30 mm (can be changed) +static constexpr double kEps_cm = 1.e-4f; + +// Coldplate specs (cm) +static constexpr double kColdplateRadius_cm = 2.6f; // 26 mm (outer radius) +static constexpr double kColdplateThickness_cm = 0.15f; // 1.5 mm +static constexpr double kColdplateZ_cm = 50.0f; // full length + +// ========== φ-span helpers (gap/arc → degrees) ========== +namespace +{ + +// Convert a linear gap at radius R into an angular gap (deg) +inline double degFromArc(double arc, double radius) +{ + // arc and radius in the SAME units (cm or mm); result in degrees + return (radius > 0.f) ? (arc / radius) * TMath::RadToDeg() : 0.f; +} + +/** + * Compute silicon segment φ-span (degrees) inside one petal, + * when you know the number of petals and the linear gap at a given radius. + * + * All of: gap and radius must be in the SAME units (cm or mm). + * If you use cm everywhere (ROOT default), pass gap_cm and radius_cm. + */ +inline double phiSpanFromGap(int nPetals, double gap, double radius) +{ + if (nPetals <= 0 || radius <= 0.f) + return 0.f; + const double petalPhiDeg = 360.f / nPetals; + const double phi = petalPhiDeg - degFromArc(gap, radius); + return phi > 0.f ? phi : 0.f; +} + +/** + * Compute silicon segment φ-span (degrees) from a known arc length at a given radius. + * arcLen and radius must be in the SAME units (cm or mm). + */ +inline double phiSpanFromArc(double arcLen, double radius) +{ + return (arcLen > 0.f && radius > 0.f) ? degFromArc(arcLen, radius) : 0.f; +} + +inline TGeoCombiTrans rotZ(double phiDeg) +{ + auto* r = new TGeoRotation(); + r->RotateZ(static_cast(phiDeg)); + return TGeoCombiTrans(0., 0., 0., r); +} +} // namespace + +// ============ Petal sub-builders (LOCAL coords only, no rotation) ========= + +// Walls: inner cylindrical arc at r=4.8 mm (always), outer arc wall, and two side plates. +static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_cm = kOuterWallRadius_cm) +{ + if (!petalAsm) { + LOGP(error, "addPetalWalls: petalAsm is null"); + return; + } + + auto& matmgr = o2::base::MaterialManager::Instance(); + const TGeoMedium* med = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083"); + + if (!med) { + LOGP(warning, "Petal walls: ALICE3_TRKSERVICES_ALUMINIUM5083$ not found, walls not created."); + return; + } + + const double halfPhi = 0.5f * (360.f / static_cast(nPetals)); + const double halfZ = 0.5f * kPetalZ_cm; + + // ---- Inner cylindrical wall (always at r=4.8 mm) ---- + { + auto* s = new TGeoTubeSeg(static_cast(kInnerWallRadius_cm), + static_cast(kInnerWallRadius_cm + kWallThick_cm), + static_cast(halfZ), + static_cast(-halfPhi), + static_cast(+halfPhi)); + auto* v = new TGeoVolume("VD_InnerWallArc", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } + + // ---- Outer arc wall ---- + { + auto* s = new TGeoTubeSeg(static_cast(outerRadius_cm), + static_cast(outerRadius_cm + kWallThick_cm), + static_cast(halfZ), + static_cast(-halfPhi), + static_cast(+halfPhi)); + auto* v = new TGeoVolume("VD_OuterWallArc", s, med); + v->SetLineColor(kGray + 2); + v->SetTransparency(70); + petalAsm->AddNode(v, 1); + } + + // ---- Side walls (boxes) at ±halfPhi ---- + const double radialLen = (outerRadius_cm - (kInnerWallRadius_cm + kWallThick_cm)); + auto* sideS = new TGeoBBox(static_cast(0.5f * radialLen), + static_cast(0.5f * kWallThick_cm), + static_cast(halfZ)); + auto* sideV = new TGeoVolume("VD_SideWall", sideS, med); + sideV->SetLineColor(kGray + 2); + sideV->SetTransparency(70); + + for (int sgn : {-1, +1}) { + const double phi = sgn * halfPhi; + const double rMid = kInnerWallRadius_cm + kWallThick_cm + 0.5f * radialLen; + const double rad = static_cast(TMath::DegToRad()); + const double x = rMid * std::cos(phi * rad); + const double y = rMid * std::sin(phi * rad); + auto* rot = new TGeoRotation(); + rot->RotateZ(static_cast(phi)); + auto* tr = new TGeoCombiTrans(static_cast(x), + static_cast(y), + 0.0, rot); + petalAsm->AddNode(sideV, (sgn < 0 ? 1 : 2), tr); + } +} + +// Build inner layers (L0..L2). L0 may be rectangular (IRIS5) or cylindrical. +// φ-spans derive from spec gaps/arc; all local placement (no rotation). +static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0) +{ + if (!petalAsm) { + LOGP(error, "addBarrelLayers: petalAsm is null"); + return; + } + + // Per spec (mm → cm) + constexpr double gapL0_cm = 0.163f; // 1.63 mm + constexpr double gapL1L2_cm = 0.12f; // 1.2 mm + constexpr double arcL0_cm = 0.6247f; // 6.247 mm + + // φ spans + const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); // L0 gap-defined + const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); // L1 gap-defined + const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined + + const std::string nameL0 = + "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "0"; + + if (rectangularL0) { + VDRectangularLayer L0(0, + nameL0, + kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm); + + // Correct translation: move to radius + half width along x + double x = kL0RectHeight_cm + L0.getChipThickness() / 2.; + LOGP(info, "Placing rectangular L0 at r={:.3f} cm (half-width={:.3f} cm)", x, 0.5f * kL0RectWidth_cm); + double y = 0.0; + double z = 0.0; + + // Correct rotation: rotate 90 degrees around z so long side is horizontal + auto* rot = new TGeoRotation(); + rot->RotateZ(90.0); + + auto* tr = new TGeoCombiTrans(x, y, z, rot); + L0.createLayer(petalAsm, tr); + } else { + VDCylindricalLayer L0(0, + nameL0, + kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm); + L0.createLayer(petalAsm, nullptr); + } + + registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 0); + + const std::string nameL1 = + "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "1"; + + VDCylindricalLayer L1(1, + nameL1, + kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm); + L1.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 1); + + const std::string nameL2 = + "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "2"; + + VDCylindricalLayer L2(2, + nameL2, + kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm); + L2.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL2, 2), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 2); +} + +// Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly. +static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) +{ + if (!petalAsm) { + LOGP(error, "addColdPlate: petalAsm is null"); + return; + } + + // Resolve medium: prefer provided medium, otherwise try to fetch from geo manager + const TGeoMedium* med = gGeoManager->GetMedium("ALICE3_TRKSERVICES_CERAMIC"); + if (!med) { + LOGP(error, "addColdPlate: can't find the medium."); + } + + // Angular span for one petal (deg) + constexpr double gapL1L2_cm = 0.12f; // 1.2 mm + + // φ spans + const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined + const double halfPhiDeg = 0.5f * phiSpanColdplate_deg; + const double startPhi = -halfPhiDeg; + const double endPhi = +halfPhiDeg; + + // Build tube segment: inner radius, outer radius = inner + thickness, half-length Z + auto* shape = new TGeoTubeSeg(static_cast(kColdplateRadius_cm), + static_cast(kColdplateRadius_cm + kColdplateThickness_cm), + static_cast(0.5 * kColdplateZ_cm), + static_cast(startPhi), + static_cast(endPhi)); + + TString volName = TString::Format("Petal%d_Coldplate", petalId); + auto* coldVol = new TGeoVolume(volName, shape, med); + coldVol->SetLineColor(kAzure - 3); + coldVol->SetTransparency(70); + + // Place in local petal coordinates (no extra transform); keep object alive by allocating shape/volume on heap. + petalAsm->AddNode(coldVol, 1); + + LOGP(info, "Adding cold plate {} r={:.3f} cm t={:.3f} cm Lz={:.3f} cm φ=[{:.3f}, {:.3f}]", + volName.Data(), kColdplateRadius_cm, kColdplateThickness_cm, kColdplateZ_cm, startPhi, endPhi); +} + +// Add IRIS service module(s) as aluminum annular cylinders placed outside the petals. +// The two modules are placed at z = ±(36 + halfLength). +static void addIRISServiceModules(TGeoVolume* petalAsm, int nPetals) +{ + if (!petalAsm) { + LOGP(error, "addIRISServiceModules: petalAsm is null"); + return; + } + + auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70); + const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl); + + if (!med) { + LOGP(error, "addIRISServiceModules: ALUMINUM medium not found."); + return; + } + + constexpr double radius = 3.2; // cm (inner radius) + constexpr double thickness = 0.133; // cm (radial thickness) + constexpr double halfLength = 19.5; // cm (half-length along Z) + const double rIn = radius; + const double rOut = radius + thickness; + + // Petal angular span. If you have an exact half-φ from your walls, use it here. + const double halfPhi_deg = 0.5 * (360.0 / double(nPetals)); + + // Create shape once and reuse + auto* segSh = new TGeoTubeSeg( + "IRIS_SERVICE_SEGsh", + rIn, rOut, + halfLength, + -halfPhi_deg, halfPhi_deg); + + // Positive Z module + TString namePos = "IRIS_Service_Pos"; + auto* volPos = new TGeoVolume(namePos, segSh, med); + volPos->SetLineColor(kRed + 2); + volPos->SetTransparency(50); + + // Negative Z module: reuse same shape object, give different name + TString nameNeg = "IRIS_Service_Neg"; + auto* volNeg = new TGeoVolume(nameNeg, segSh, med); + volNeg->SetLineColor(kRed + 2); + volNeg->SetTransparency(50); + + // Translations (heap-allocated so ROOT keeps them) + const double zpos = 36.0 + halfLength; + auto* transPos = new TGeoTranslation(0.0, 0.0, static_cast(zpos)); + auto* transNeg = new TGeoTranslation(0.0, 0.0, static_cast(-zpos)); + + // Add to mother volume + petalAsm->AddNode(volPos, 1, transPos); + petalAsm->AddNode(volNeg, 2, transNeg); + + LOGP(info, "Added IRIS service modules at z = ±{} cm, r=[{}, {}] cm", zpos, rIn, rOut); +} + +// Only the A-side "inside vacuum" piece participates in the cutout. +static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) +{ + if (!petalAsm) { + LOGP(error, "addIRISServiceModulesSegmented: petalAsm is null"); + return; + } + + // --- Service geometry (same as your previous values) + constexpr double rIn = 3.2; // cm + constexpr double thickness = 0.133; // cm + constexpr double rOut = rIn + thickness; + constexpr double halfLen = 19.5; // cm + constexpr double z0 = 36.0 + halfLen; // 55.5 cm center of +Z service + const double zMinA = z0 - halfLen; // 36.0 cm + const double zMaxA = z0 + halfLen; // 75.0 cm + + // --- Vacuum vessel window around z∈[-L/2, +L/2] with wall thickness on +Z side + // Keep these in sync with TRKServices::createVacuumCompositeShape() + constexpr double vacuumVesselLength = 76.0; // cm + constexpr double vacuumVesselThickness = 0.08; // cm (0.8 mm) + const double halfVess = 0.5 * vacuumVesselLength; // 38.0 cm + const double gapStart = halfVess; // 38.00 + const double gapEnd = halfVess + vacuumVesselThickness; // 38.08 + + // --- Petal φ-span (segment) + const double halfPhi = 0.5 * (360.0 / double(nPetals)); + + auto* matAl = new TGeoMaterial("ALUMINUM", 26.9815, 13, 2.70); + const TGeoMedium* med = new TGeoMedium("ALUMINUM", 4, matAl); + + if (!med) { + LOGP(error, "addIRISServiceModules: ALUMINUM medium not found."); + return; + } + + // ========================= + // C-side (negative Z) whole + // ========================= + { + auto* sh = new TGeoTubeSeg(rIn, rOut, halfLen, -halfPhi, +halfPhi); + auto* vN = new TGeoVolume("IRIS_Service_Neg", sh, med); + vN->SetLineColor(kRed + 2); + vN->SetTransparency(55); + petalAsm->AddNode(vN, 1, new TGeoTranslation(0., 0., -(z0))); + } + + // ===================================== + // A-side (positive Z): split with a gap + // ===================================== + // Piece 1 (INSIDE vacuum): z ∈ [zMinA, min(zMaxA, gapStart)] → goes into cutout + const double L_inVac = std::max(0.0, std::min(zMaxA, gapStart) - zMinA); // expected ~2.0 cm + if (L_inVac > 0) { + const double dz = 0.5 * L_inVac; + const double zc = zMinA + dz; // center of lower slice, ≈ 37.0 cm + auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, halfPhi); + sh->SetName("IRIS_SERVICE_POS_INVACsh"); + auto* vP = new TGeoVolume("IRIS_Service_Pos_InVac", sh, med); + vP->SetLineColor(kRed + 2); + vP->SetTransparency(55); + petalAsm->AddNode(vP, 1, new TGeoTranslation(0., 0., zc)); + LOGP(info, "IRIS A-side (InVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm", + zc - dz, zc + dz, 2 * dz); + } else { + LOGP(warning, "IRIS A-side (InVac): no overlap with vacuum (L_inVac<=0)"); + } + + // Gap (no material): (gapStart, gapEnd) = (38.00, 38.08) + + // Piece 2 (OUT of vacuum): z ∈ [max(zMinA, gapEnd), zMaxA] → NOT in cutout + const double L_outVac = std::max(0.0, zMaxA - std::max(zMinA, gapEnd)); // expected ~36.92 cm + if (L_outVac > 0) { + const double dz = 0.5 * L_outVac; + const double zc = std::max(zMinA, gapEnd) + dz; // center of upper slice + auto* sh = new TGeoTubeSeg(rIn, rOut, dz, -halfPhi, +halfPhi); + sh->SetName("IRIS_SERVICE_POS_OUTVACsh"); + auto* vP = new TGeoVolume("IRIS_Service_Pos_OutVac", sh, med); + vP->SetLineColor(kRed + 1); + vP->SetTransparency(70); + petalAsm->AddNode(vP, 2, new TGeoTranslation(0., 0., +zc)); + LOGP(info, "IRIS A-side (OutVac): z=[{:.3f},{:.3f}] cm, len={:.3f} cm", + zc - dz, zc + dz, 2 * dz); + } else { + LOGP(warning, "IRIS A-side (OutVac): no upper piece (L_outVac<=0)"); + } +} + +// Build disks in local coords: each disk gets only a local Z translation. +// φ span from gap at rOut. +static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) +{ + + if (!petalAsm) { + LOGP(error, "addDisks: petalAsm is null"); + return; + } + + const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); + + for (int i = 0; i < 6; ++i) { + const std::string nameD = + "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalDiskPattern()) + std::to_string(i); + + VDDiskLayer disk(i, + nameD, + kX2X0, diskRin_cm, diskRout_cm, phiDisk_deg, diskZ_cm[i]); + + // Local Z placement only + auto* tr = new TGeoTranslation(0.0, 0.0, static_cast(disk.getZPosition())); + disk.createLayer(petalAsm, tr); + registerSensor(makeSensorName(nameD, i), petalID, VDSensorDesc::Kind::Disk, /*idx*/ i); + } +} + +// Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords. +static TGeoVolume* buildPetalAssembly(int nPetals, int petalID, bool rectangularL0) +{ + auto* petalAsm = new TGeoVolumeAssembly(Form("Petal%d", petalID)); + addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm); + + // Pass petalID to layers/disks for naming + addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0); + addColdPlate(petalAsm, nPetals, petalID); + addDisks(petalAsm, nPetals, petalID); + addIRISServiceModulesSegmented(petalAsm, nPetals); + + return petalAsm; +} + +// =================== Public entry points =================== + +void createIRIS4Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS4Geometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRIS5Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS5Geometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createIRIS4aGeometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS4aGeometry: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 3; + for (int p = 0; p < nPetals; ++p) { + auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + // Build the petal-local solids composite once from the FIRST petal + if (p == 0) { + buildPetalSolidsComposite(petal); // <-- captures only SOLIDS in local coords + } + const double phiDeg = (360.0 / double(nPetals)) * (double(p) + 0.5); + auto* R = new TGeoRotation(); + R->RotateZ(phiDeg); + auto* T = new TGeoCombiTrans(0, 0, 0, R); + motherVolume->AddNode(petal, p + 1, T); + } + buildIrisCutoutFromPetalSolid(nPetals); +} + +void createSinglePetalDebug(TGeoVolume* motherVolume, int petalID, int nPetals, bool rectangularL0) +{ + auto* petal = buildPetalAssembly(nPetals, petalID, rectangularL0); + + // Optionally rotate the petal for display + const double phiDeg = (360.f / static_cast(nPetals)) * (static_cast(petalID) + 0.5f); + auto* R = new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", phiDeg, 0, 0)); + motherVolume->AddNode(petal, 1, R); + + LOGP(info, "Debug: Added Petal{} to {}", petalID, motherVolume->GetName()); +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx new file mode 100644 index 0000000000000..d7310e63bb3a1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx @@ -0,0 +1,185 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKSimulation/VDGeometryBuilder.h" + +#include +#include + +#include "Framework/Logger.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKSimulation/VDLayer.h" + +namespace o2::trk +{ + +// ---------- Shared constants (specs) ---------- +static constexpr float kX2X0 = 100.e-4f; // all-silicon +static constexpr float kLenZ_cm = 50.0; // cylindrical/rectangular layer length + +// Radii +static constexpr float rL0_cm = 0.5; // 5 mm +static constexpr float rL1_cm = 1.2; // 12 mm +static constexpr float rL2_cm = 2.5; // 25 mm + +// Rectangular L0 width (IRIS5) in cm +static constexpr float kL0RectWidth_cm = 0.83; // 8.3 mm + +// Disk geometry +static constexpr float diskRin_cm = 0.5; // 5 mm +static constexpr float diskRout_cm = 2.5; // 25 mm +static const float diskZ_cm[6] = {-34.0, -30.0, -26.0, 26.0, 30.0, 34.0}; + +// Petal wall parameters (cm) +static constexpr float kPetalZ_cm = 68.0; // full petal length +static constexpr float kWallThick_cm = 0.015; // 0.15 mm +static constexpr float kInnerWallRadius_cm = 0.48; // outer radius of wall arc (example) +static constexpr float kOuterWallRadius_cm = 3.0; // outer radius of wall arc (example) +static constexpr float kEps_cm = 0.0001; // tiny clearance + +// ---------- Helpers ---------- +static TGeoCombiTrans makePetalRotation(float phiDeg) +{ + auto* rot = new TGeoRotation(); + rot->RotateZ(phiDeg); + return TGeoCombiTrans(0.0, 0.0, 0.0, rot); +} + +// Convert a linear gap at radius R into an angular gap (deg) +inline float degFromArc(float arc, float radius) +{ + // arc and radius in the SAME units (cm or mm); result in degrees + return (arc / radius) * TMath::RadToDeg(); +} + +/** + * Compute silicon segment φ-span (degrees) inside one petal, + * when you know the number of petals and the linear gap at a given radius. + * + * All of: gap and radius must be in the SAME units (cm or mm). + * If you use cm everywhere (ROOT default), pass gap_cm and radius_cm. + */ +inline float phiSpanFromGap(int nPetals, float gap, float radius) +{ + if (nPetals <= 0 || radius <= 0.0) + return 0.0; + const float petalPhiDeg = 360.0 / nPetals; + const float gapDeg = degFromArc(gap, radius); + const float phiDeg = petalPhiDeg - gapDeg; + return (phiDeg > 0.0) ? phiDeg : 0.0; +} + +/** + * Compute silicon segment φ-span (degrees) from a known arc length at a given radius. + * arcLen and radius must be in the SAME units (cm or mm). + */ +inline float phiSpanFromArc(float arcLen, float radius) +{ + if (arcLen <= 0.0 || radius <= 0.0) + return 0.0; + return degFromArc(arcLen, radius); +} + +static void buildDisksSinglePetal(TGeoVolume* motherVolume, int petalID, int nPetals) +{ + const float phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); + const float phiHalfDisk = phiDisk_deg / 2.0; + + for (int i = 0; i < 6; ++i) { + VDDiskLayer disk( + /*layerNumber */ i, + /*layerName */ std::string(GeometryTGeo::getTRKPetalDiskPattern()) + std::to_string(i), + /*x2x0 */ kX2X0, + /*rMin */ diskRin_cm, + /*rMax */ diskRout_cm, + /*phiSpanDeg */ phiDisk_deg, + /*zPos */ diskZ_cm[i]); + + // Place each disk with local Z translation only (no rotation). + TGeoTranslation tz(0.0, 0.0, disk.getZPosition()); + disk.createLayer(petalAsm, &tz); + } +} + +static void buildCylLayersForPetal(TGeoVolume* mother, int petalIdx, int nPetals, bool rectangularL0) +{ + const double petalPhiSpan = 360.0 / double(nPetals); + const double rotPhi = petalPhiSpan * (petalIdx + 0.5); + auto combi = makePetalRotation(rotPhi); + + // ----- L0 ----- + if (rectangularL0) { + VDRectangularLayer L0( + /*layerNumber */ 0, + /*layerName */ std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L0", + /*x2x0 */ kX2X0, + /*width */ float(kL0RectWidth_cm), + /*lengthZ */ float(kLenZ_cm), + /*lengthSensZ */ float(kLenZ_cm) // no Z-segmentation now + ); + L0.createLayer(mother, &combi); + } else { + VDCylindricalLayer L0( + /*layerNumber */ 0, + /*layerName */ std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L0", + /*x2x0 */ kX2X0, + /*radius */ float(rL0_cm), + /*phiSpanDeg */ float(petalPhiSpan), + /*lengthZ */ float(kLenZ_cm), + /*lengthSensZ */ float(kLenZ_cm)); + L0.createLayer(mother, &combi); + } + + // ----- L1 ----- + VDCylindricalLayer L1( + 1, std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L1", kX2X0, + float(rL1_cm), float(petalPhiSpan), float(kLenZ_cm), float(kLenZ_cm)); + L1.createLayer(mother, &combi); + + // ----- L2 ----- + VDCylindricalLayer L2( + 2, std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L2", kX2X0, + float(rL2_cm), float(petalPhiSpan), float(kLenZ_cm), float(kLenZ_cm)); + L2.createLayer(mother, &combi); + + // ---------- φ-segmentation hooks (future) ---------- + // Spec notes on arc length and gaps are φ-related: + // * L0 arc length 6.247 mm with 1.63 mm gaps + // * L1/L2 gaps 1.2 mm + // To implement: compute per-sensor Δφ = arcLen_mm / radius_mm (in radians → degrees), + // and Δφ_gap = gap_mm / radius_mm. Then tile sensors around the petal span using + // additional rotations of sensor volumes. Current code builds one continuous segment per petal. +} + +// ---------- Public entry points ---------- +void createIRIS4Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS4Geometry: motherVolume is null"); + return; + } + const int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + buildCylLayersForPetal(motherVolume, p, nPetals, /*rectangularL0*/ false); + buildDisksForPetal(motherVolume, p, nPetals); + } +} + +void createIRIS5Geometry(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRIS5Geometry: motherVolume is null"); + return; + } + const int nPetals = 4; + for (int p = 0; p < nPetals; ++p) { + buildCylLayersForPetal(motherVolume, p, nPetals, /*rectangularL0*/ true); + buildDisk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx new file mode 100644 index 0000000000000..b762ba5c9b36a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -0,0 +1,314 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKSimulation/VDLayer.h" +#include "TRKBase/GeometryTGeo.h" + +#include "Framework/Logger.h" + +#include "TGeoTube.h" +#include "TGeoBBox.h" +#include "TGeoVolume.h" +#include "TGeoMatrix.h" +#include "TGeoManager.h" + +#include "TMath.h" + +namespace o2 +{ +namespace trk +{ +// Base layer constructor +VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0) + : mLayerNumber(layerNumber), mLayerName(layerName), mX2X0(layerX2X0), mModuleWidth(4.54) +{ + constexpr double kSiX0_cm = 9.5; // Radiation length of Silicon in cm + mChipThickness = mX2X0 * kSiX0_cm; +} + +// VDCylindricalLayer constructor +VDCylindricalLayer::VDCylindricalLayer(int layerNumber, const std::string& layerName, double layerX2X0, double radius, + double phiSpanDeg, double lengthZ, double lengthSensZ) + : VDLayer(layerNumber, layerName, layerX2X0), mRadius(radius), mPhiSpanDeg(phiSpanDeg), mLengthZ(lengthZ), mLengthSensZ(lengthSensZ) +{ + LOGP(info, "Creating VD cylindrical layer: id: {} name: {} x2X0: {} radius: {} phiSpanDeg: {} lengthZ: {} lengthSensZ: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, radius, phiSpanDeg, lengthZ, lengthSensZ, mChipThickness); +} + +// VDRectangularLayer constructor +VDRectangularLayer::VDRectangularLayer(int layerNumber, const std::string& layerName, double layerX2X0, + double width, double lengthZ, double lengthSensZ) + : VDLayer(layerNumber, layerName, layerX2X0), mWidth(width), mLengthZ(lengthZ), mLengthSensZ(lengthSensZ) +{ + + if (mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(fatal, "Invalid sensor length: sensZ={} layerZ={}", mLengthSensZ, mLengthZ); + } + LOGP(info, "Creating VD rectangular layer: id: {} name: {} x2X0: {} width: {} lengthZ: {} lengthSensZ: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, width, lengthZ, lengthSensZ, mChipThickness); +} + +// VDDiskLayer constructor +VDDiskLayer::VDDiskLayer(int layerNumber, const std::string& layerName, double layerX2X0, double rMin, double rMax, + double phiSpanDeg, double zPos) + : VDLayer(layerNumber, layerName, layerX2X0), mRMin(rMin), mRMax(rMax), mPhiSpanDeg(phiSpanDeg), mZPos(zPos) +{ + + LOGP(info, "Creating VD disk layer: id: {} name: {} x2X0: {} rMin: {} rMax: {} phiSpanDeg: {} zPos: {} chipThickness = {} cm", + mLayerNumber, layerName, mX2X0, rMin, rMax, phiSpanDeg, zPos, mChipThickness); +} + +/* +** Create sensor +*/ + +TGeoVolume* VDCylindricalLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthSensZ; + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + auto* shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); + vol->SetLineColor(kYellow); + vol->SetTransparency(30); + return vol; +} + +TGeoVolume* VDRectangularLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + auto* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; // thickness in Y + const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer + + auto* shape = new TGeoBBox(hx, hy, hz); + auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); + vol->SetLineColor(kYellow); + vol->SetTransparency(30); + + return vol; +} + +TGeoVolume* VDDiskLayer::createSensor() const +{ + if (!gGeoManager) { + LOGP(error, "gGeoManager is null"); + return nullptr; + } + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + if (!medSi) { + LOGP(error, "Missing medium TRK_SILICON$"); + return nullptr; + } + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk sensor dims: rMin={}, rMax={}, t={}, phiSpanDeg={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return nullptr; + } + std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); + const double halfThickness = 0.5 * mChipThickness; // disk thickness is along Z + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + + // Same geometry as the layer (identical radii + phi span + thickness) + auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + + auto* sensVol = new TGeoVolume(sensName.c_str(), shape, medSi); + sensVol->SetLineColor(kYellow); + sensVol->SetTransparency(30); + + return sensVol; +} + +/* +** Create layer +*/ + +// Cylindrical layer +void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + // Sanity + if (mRadius <= 0 || mChipThickness <= 0 || mLengthZ <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0 || + mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(error, "Invalid cylindrical dimensions: r={}, t={}, Z={}, phi={}, sensZ={}", + mRadius, mChipThickness, mLengthZ, mPhiSpanDeg, mLengthSensZ); + return; + } + + // AIR container (layer) + const double rIn = mRadius; + const double rOut = mRadius + mChipThickness; + const double halfZ = 0.5 * mLengthZ; + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + + auto* layerShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor volume (must use mLengthSensZ internally) + TGeoVolume* sensorVol = VDCylindricalLayer::createSensor(); + if (!sensorVol) { + LOGP(error, "VDCylindricalLayer::createSensor() returned null"); + return; + } + LOGP(info, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); + layerVol->AddNode(sensorVol, 1, nullptr); + + // Tiling: edge-to-edge if sensor shorter than layer; else single centered + // const auto zCenters = (mLengthSensZ < mLengthZ) + // ? centersNoGapZ(mLengthZ, mLengthSensZ) + // : std::vector{0.0}; + // + // int copyNo = 1; + // for (double zc : zCenters) { + // TGeoTranslation tz(0.0, 0.0, zc); + // layerVol->AddNode(sensorVol, copyNo++, (zc == 0.0 && zCenters.size() == 1) ? nullptr : &tz); + // } + + motherVolume->AddNode(layerVol, 1, combiTrans); +} + +// Rectangular layer +void VDRectangularLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + if (mWidth <= 0 || mChipThickness <= 0 || mLengthZ <= 0 || + mLengthSensZ <= 0 || mLengthSensZ > mLengthZ) { + LOGP(error, "Invalid rectangular dims: W={}, t={}, Z={}, sensZ={}", + mWidth, mChipThickness, mLengthZ, mLengthSensZ); + return; + } + + // AIR container (layer) + const double hx = 0.5 * mWidth; + const double hy = 0.5 * mChipThickness; + const double hz = 0.5 * mLengthZ; + + auto* layerShape = new TGeoBBox(hx, hy, hz); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor volume (uses mLengthSensZ internally) + TGeoVolume* sensorVol = VDRectangularLayer::createSensor(); + if (!sensorVol) { + LOGP(error, "VDRectangularLayer::createSensor() returned null"); + return; + } + + LOGP(info, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); + layerVol->AddNode(sensorVol, 1, nullptr); + + // Tiling along Z, edge - to - edge if needed + // const auto zCenters = (mLengthSensZ < mLengthZ) + // ? centersNoGapZ(mLengthZ, mLengthSensZ) + // : std::vector{0.0}; + // + // int copyNo = 1; + // for (double zc : zCenters) { + // TGeoTranslation tz(0.0, 0.0, zc); + // layerVol->AddNode(sensorVol, copyNo++, (zc == 0.0 && zCenters.size() == 1) ? nullptr : &tz); + // } + + motherVolume->AddNode(layerVol, 1, combiTrans); +} + +// Disk layer +void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) const +{ + if (!motherVolume || !gGeoManager) { + LOGP(error, "Null motherVolume or gGeoManager"); + return; + } + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + if (!medAir) { + LOGP(error, "Missing TRK_AIR$"); + return; + } + + if (mRMin < 0 || mRMax <= mRMin || mChipThickness <= 0 || + mPhiSpanDeg <= 0 || mPhiSpanDeg > 360.0) { + LOGP(error, "Invalid disk dims: rMin={}, rMax={}, t={}, phi={}", + mRMin, mRMax, mChipThickness, mPhiSpanDeg); + return; + } + + // For disks the thickness is along Z and equals mChipThickness + const double halfThickness = 0.5 * mChipThickness; + const double halfPhi = 0.5 * mPhiSpanDeg; + + // AIR container (layer) + auto* layerShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); + layerVol->SetLineColor(kYellow); + layerVol->SetTransparency(30); + + // Sensor (same size & shape as the layer for disks) + TGeoVolume* sensorVol = VDDiskLayer::createSensor(); + if (!sensorVol) { + LOGP(error, "VDDiskLayer::createSensor() returned null"); + return; + } + + // Insert single sensor (no Z-segmentation for disks) + layerVol->AddNode(sensorVol, 1, nullptr); + + TGeoTranslation tz(0.0, 0.0, mZPos); + motherVolume->AddNode(layerVol, 1, combiTrans ? combiTrans : &tz); +} + +// ClassImp(VDLayer); +// ClassImp(VDCylindricalLayer); +// ClassImp(VDRectangularLayer); +// ClassImp(VDDiskLayer); + +} // namespace trk +} // namespace o2 \ No newline at end of file From 9863bc258db81a496a90767e98c8d01e677e8772 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Thu, 25 Sep 2025 14:45:13 +0200 Subject: [PATCH 02/99] Cleanup --- .../Upgrades/ALICE3/Passive/src/Pipe.cxx | 3 +- .../TRK/base/include/TRKBase/GeometryTGeo.h | 6 - .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 5 +- .../ALICE3/TRK/simulation/CMakeLists.txt | 7 +- .../include/TRKSimulation/Detector.h | 4 - .../include/TRKSimulation/TRKPetalCase.h | 93 -------- .../include/TRKSimulation/TRKPetalDisk.h | 64 ------ .../include/TRKSimulation/TRKPetalLayer.h | 61 ------ .../ALICE3/TRK/simulation/src/Detector.cxx | 2 +- .../TRK/simulation/src/TRKPetalCase.cxx | 202 ------------------ .../TRK/simulation/src/TRKPetalDisk.cxx | 94 -------- .../TRK/simulation/src/TRKPetalLayer.cxx | 79 ------- .../TRK/simulation/src/TRKSimulationLinkDef.h | 3 - .../TRK/simulation/src/VDGeometryBuilder.cxx | 12 +- 14 files changed, 11 insertions(+), 624 deletions(-) delete mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h delete mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h delete mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h delete mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx delete mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx delete mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx diff --git a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx index 7dfd26a79b38d..180d4fce42c86 100644 --- a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx +++ b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx @@ -122,7 +122,8 @@ void Alice3Pipe::ConstructGeometry() // Add everything to the barrel barrel->AddNode(pipeVolume, 1, new TGeoTranslation(0, 30.f, 0)); - pipeVolume->SetLineColor(kGreen + 3); + pipeVolume->SetLineColor(37); + pipeVolume->SetTransparency(10); } void Alice3Pipe::createMaterials() diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index db046b8d8e9eb..a1e4b9321130f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -42,15 +42,12 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache }; static const char* getTRKVolPattern() { return sVolumeName.c_str(); } static const char* getTRKLayerPattern() { return sLayerName.c_str(); } - static const char* getVDLayerPattern() { return sVDLayerName.c_str(); } - static const char* getVDDiskPattern() { return sVDDiskName.c_str(); } static const char* getTRKPetalPattern() { return sPetalName.c_str(); } static const char* getTRKPetalDiskPattern() { return sPetalDiskName.c_str(); } static const char* getTRKPetalLayerPattern() { return sPetalLayerName.c_str(); } static const char* getTRKStavePattern() { return sStaveName.c_str(); } static const char* getTRKChipPattern() { return sChipName.c_str(); } static const char* getTRKSensorPattern() { return sSensorName.c_str(); } - static const char* getVDSensorPattern() { return sVDSensorName.c_str(); } static const char* getTRKWrapVolPattern() { return sWrapperVolumeName.c_str(); } int getNumberOfChips() const { return mSize; } @@ -144,9 +141,6 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static std::string sVolumeName; static std::string sLayerName; - static std::string sVDLayerName; - static std::string sVDDiskName; - static std::string sVDSensorName; static std::string sPetalName; static std::string sPetalDiskName; static std::string sPetalLayerName; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 4a22b0e3e2e42..55d308c6f23d1 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -24,16 +24,13 @@ std::unique_ptr GeometryTGeo::sInstance; // Names std::string GeometryTGeo::sVolumeName = "TRKV"; std::string GeometryTGeo::sLayerName = "TRKLayer"; +std::string GeometryTGeo::sPetalName = "PETALCASE"; std::string GeometryTGeo::sPetalDiskName = "DISK"; std::string GeometryTGeo::sPetalLayerName = "LAYER"; std::string GeometryTGeo::sStaveName = "TRKStave"; std::string GeometryTGeo::sChipName = "TRKChip"; std::string GeometryTGeo::sSensorName = "TRKSensor"; -std::string GeometryTGeo::sPetalName = "Petal"; -std::string GeometryTGeo::sVDLayerName = "Layer"; -std::string GeometryTGeo::sVDDiskName = "Disk"; -std::string GeometryTGeo::sVDSensorName = "VDSensor"; std::string GeometryTGeo::sWrapperVolumeName = "TRKUWrapVol"; ///< Wrapper volume name, not implemented at the moment o2::trk::GeometryTGeo::~GeometryTGeo() diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt index 1366c42f3fae6..0c3c35d49f722 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -18,9 +18,6 @@ o2_add_library(TRKSimulation src/Digitizer.cxx src/TRKServices.cxx src/DPLDigitizerParam.cxx - src/TRKPetalCase.cxx - src/TRKPetalLayer.cxx - src/TRKPetalDisk.cxx src/VDLayer.cxx src/VDGeometryBuilder.cxx PUBLIC_LINK_LIBRARIES O2::TRKBase @@ -37,9 +34,7 @@ o2_target_root_dictionary(TRKSimulation include/TRKSimulation/Detector.h include/TRKSimulation/TRKLayer.h include/TRKSimulation/TRKServices.h - include/TRKSimulation/TRKPetalCase.h - include/TRKSimulation/TRKPetalLayer.h - include/TRKSimulation/TRKPetalDisk.h include/TRKSimulation/VDLayer.h include/TRKSimulation/VDGeometryBuilder.h + include/TRKSimulation/VDSensorRegistry.h include/TRKSimulation/DPLDigitizerParam.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h index 0fe0d55dccde7..688876af0f762 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h @@ -17,7 +17,6 @@ #include "TRKSimulation/TRKLayer.h" #include "TRKSimulation/TRKServices.h" -#include "TRKSimulation/TRKPetalCase.h" #include "TRKBase/GeometryTGeo.h" #include @@ -92,7 +91,6 @@ class Detector : public o2::base::DetImpl std::vector* mHits; // ITSMFT ones for the moment std::vector mLayers; TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker - std::vector mPetalCases; // Houses the Iris tracker and its services. Created fully in the beam pipe std::vector mFirstOrLastLayers; // Names of the first or last layers bool InsideFirstOrLastLayer(std::string layerName); @@ -106,8 +104,6 @@ class Detector : public o2::base::DetImpl public: static constexpr Int_t sNumberVDPetalCases = 4; //! Number of VD petals int getNumberOfLayers() const { return mLayers.size(); } //! Number of TRK layers - int getNumberOfLayersVD() const { return mPetalCases[0].mPetalLayers.size(); } - int getNumberOfDisksVD() const { return mPetalCases[0].mPetalDisks.size(); } void Print(FairVolume* vol, int volume, int subDetID, int layer, int stave, int halfstave, int chipID) const; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h deleted file mode 100644 index cd45cc98fd177..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalCase.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_TRK_PETALCASE_H -#define ALICEO2_TRK_PETALCASE_H - -#include - -#include "TRKSimulation/TRKPetalLayer.h" -#include "TRKSimulation/TRKPetalDisk.h" -#include "TGeoCompositeShape.h" - -namespace o2 -{ -namespace trk -{ -class TRKPetalCase -{ - public: - TRKPetalCase() = default; - TRKPetalCase(Int_t number, TGeoVolume* motherVolume, Bool_t irisOpen); - ~TRKPetalCase() = default; - - // Sensitive volume list - std::vector mPetalLayers; - std::vector mPetalDisks; - - auto getPetalCaseName() { return mPetalCaseName; } - TString getFullName(); - - private: - void constructCase(TGeoVolume* motherVolume); - void constructColdPlate(TGeoVolume* motherVolume); - void constructDetectionPetals(TGeoVolume* motherVolume); - void addDetectionPetelsToFullComposite(); - - void addToPetalCaseComposite(TString shape) { mFullCompositeFormula += ("+" + shape); } - - Int_t mPetalCaseNumber; // Used to determine rotation and position. 0-3 - Bool_t mOpenState; // At injection energy, the iris tracker is in the open position. During stable beams, it is closed - - TString mPetalCaseName; - TString mFullCompositeFormula; // Used to excavate the petal and all its components from the vacuum - - // Center position of the petal case. 0,0,0 at stable beams (a.k.a. closed state) - Double_t mXPos, mYPos, mZPos; - - Double_t mWallThickness; // cm // Assume all the walls have the same thickness for now - Double_t mRIn; // cm - Double_t mROut; // cm - Double_t mRInOpenState; // cm - Double_t mPetalCaseLength; // cm - - Double_t mAngularCoverageAzimuthalWall; // Rad // Angular coverage of azimuthal part of wall (equivalent to that of the sensitive volumes) - Double_t mAngularCoverageRadialWall; // Rad // Angular coverage of radial part of wall - Double_t mToDeg; - - // Petal case parts -> In one composite shape - TGeoTubeSeg* mInnerAzimuthalWall; - TGeoTubeSeg* mOuterAzimuthalWall; - TGeoTubeSeg* mRadialWall; - TGeoTubeSeg* mForwardWall; - - TGeoRotation* mAzimuthalWallRot; - TGeoRotation* mRadialWall1Rot; - TGeoRotation* mRadialWall2Rot; - - TGeoCombiTrans* mAzimuthalWallCombiTrans; - TGeoCombiTrans* mRadialWall1CombiTrans; - TGeoCombiTrans* mRadialWall2CombiTrans; - TGeoCombiTrans* mForwardWall1CombiTrans; - TGeoCombiTrans* mForwardWall2CombiTrans; - - TGeoVolume* mPetalCaseVolume; - - // Cold plate - TGeoTubeSeg* mColdPlate; - TGeoVolume* mColdPlateVolume; - - ClassDef(TRKPetalCase, 1); -}; - -} // namespace trk -} // namespace o2 -#endif // ALICEO2_TRK_PETALCASE_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h deleted file mode 100644 index 465f52eb8d41b..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalDisk.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TRKPetalDisk.h -/// \brief Definition of the TRKPetalDisk class - -#ifndef ALICEO2_TRK_PETAL_DISK_H_ -#define ALICEO2_TRK_PETAL_DISK_H_ - -#include "TGeoManager.h" // for gGeoManager -#include "Rtypes.h" // for Double_t, Int_t, Bool_t, etc -#include // for LOG - -namespace o2 -{ -namespace trk -{ - -/// This class defines the Geometry for the TRK Disk TGeo. -class TRKPetalDisk -{ - public: - TRKPetalDisk() = default; - TRKPetalDisk(Int_t diskNumber, std::string diskName, Float_t z, Float_t rIn, Float_t rOut, Float_t angularCoverage, Float_t Diskx2X0); - ~TRKPetalDisk() = default; - - auto getInnerRadius() const { return mInnerRadius; } - auto getOuterRadius() const { return mOuterRadius; } - auto getThickness() const { return mChipThickness; } - auto getAngularCoverage() const { return mAngularCoverage; } - auto getZ() const { return mZ; } - auto getx2X0() const { return mx2X0; } - auto getName() const { return mDiskName; } - auto getSensorName() const { return mSensorName; } - - /// Creates the actual Disk and places inside its mother volume - /// \param motherVolume the TGeoVolume owing the volume structure - void createDisk(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans); - - private: - Int_t mDiskNumber = -1; ///< Current disk number - std::string mDiskName; ///< Current disk name - std::string mSensorName; - Double_t mInnerRadius; ///< Inner radius of this disk - Double_t mOuterRadius; ///< Outer radius of this disk - Double_t mAngularCoverage; - Double_t mZ; ///< Z position of the disk - Double_t mChipThickness; ///< Chip thickness - Double_t mx2X0; ///< Disk material budget x/X0 - - ClassDef(TRKPetalDisk, 1); -}; -} // namespace trk -} // namespace o2 - -#endif // ALICEO2_TRK_PETAL_DISK_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h deleted file mode 100644 index 4e7a7735d51f0..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKPetalLayer.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_TRK_PETAL_LAYER_H -#define ALICEO2_TRK_PETAL_LAYER_H - -#include "TGeoManager.h" -#include -#include "TGeoTube.h" - -#include "TRKBase/TRKBaseParam.h" - -namespace o2 -{ -namespace trk -{ -class TRKPetalLayer -{ - public: - TRKPetalLayer() = default; - TRKPetalLayer(Int_t layerNumber, std::string layerName, Float_t rIn, Float_t angularCoverage, Float_t zLength, Float_t layerX2X0); - ~TRKPetalLayer() = default; - - auto getInnerRadius() const { return mInnerRadius; } - auto getAngularCoverage() const { return mAngularCoverage; } - auto getZLength() { return mZ; } - auto getx2X0() const { return mX2X0; } - auto getChipThickness() const { return mChipThickness; } - auto getNumber() const { return mLayerNumber; } - auto getName() const { return mLayerName; } - auto getSensorName() const { return mSensorName; } - - void createLayer(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans); - - private: - Int_t mLayerNumber; - std::string mLayerName; - std::string mSensorName; - Float_t mInnerRadius; - Float_t mZ; - Float_t mX2X0; - Float_t mChipThickness; - Float_t mModuleWidth; // u.m. = cm - Float_t mAngularCoverage; // rad - - TGeoTubeSeg* mLayer; - - ClassDef(TRKPetalLayer, 1); -}; - -} // namespace trk -} // namespace o2 -#endif // ALICEO2_TRK_PETAL_LAYER_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 03aff419d1a96..8c982a51e1b82 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -266,7 +266,7 @@ void Detector::createGeometry() // Choose the VD design (here: IRIS4 by default). // You can wire this to a parameter in TRKBaseParam if desired. - // Alternatives: createIRIS5Geometry(vVD); createIRIS4aGeometry(vVD); + // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); o2::trk::createIRIS4Geometry(vTRK); mServices.excavateFromVacuum("IRIS_CUTOUTsh"); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx deleted file mode 100644 index c729d7d1ec4dd..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalCase.cxx +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TRKSimulation/TRKPetalCase.h" -#include "TRKBase/GeometryTGeo.h" -#include - -#include "Framework/Logger.h" - -#include "TGeoTube.h" -#include "TGeoMatrix.h" -#include "TGeoCompositeShape.h" -#include "TGeoVolume.h" -#include "TString.h" -#include "TMath.h" - -namespace o2 -{ -namespace trk -{ -TRKPetalCase::TRKPetalCase(Int_t number, TGeoVolume* motherVolume, Bool_t irisOpen) : mPetalCaseNumber(number), mOpenState(irisOpen) -{ - - mWallThickness = .15e-1; // cm // Assume all the walls have the same thickness for now. - mRIn = 0.48; // cm - mROut = 3; // cm - mRInOpenState = 1.5; // cm - mPetalCaseLength = 70.; // cm - - // Calculate angular coverages of azimuthal part of wall (equivalent to that of the sensitive volumes) - mAngularCoverageAzimuthalWall = (0.25 * (2 * mRIn * TMath::Pi()) - 2 * mWallThickness) / mRIn; - mAngularCoverageRadialWall = mWallThickness / mRIn; - mToDeg = 180 / TMath::Pi(); - - // Calculate the center of the petal (x_c, y_c, z_c) based on whether it is open or not - mZPos = 0; - if (mOpenState) { - Double_t rHalfPetal = 0.5 * (mRIn + mROut); - Double_t rOpenStateCenter = TMath::Sqrt(rHalfPetal * rHalfPetal + mRInOpenState * mRInOpenState); - mXPos = rOpenStateCenter * TMath::Cos(0.25 * TMath::Pi() + (mPetalCaseNumber - 1) * 0.5 * TMath::Pi()); - mYPos = rOpenStateCenter * TMath::Sin(0.25 * TMath::Pi() + (mPetalCaseNumber - 1) * 0.5 * TMath::Pi()); - } else { - mXPos = 0.; - mYPos = 0.; - } - - // Make the petal case - constructCase(motherVolume); - // Make coldplate - constructColdPlate(motherVolume); - // Add the detection petals (quarter disks and barrel layers) - constructDetectionPetals(motherVolume); -} - -TString TRKPetalCase::getFullName() -{ - TString fullCompositeName = Form("PETALCASE%d_FULLCOMPOSITE", mPetalCaseNumber); - TGeoCompositeShape* fullCompositeShape = new TGeoCompositeShape(fullCompositeName, mFullCompositeFormula); - return fullCompositeName; -} - -void TRKPetalCase::constructCase(TGeoVolume* motherVolume) -{ - - // Petal case parts in TGeoTubeSeg - mInnerAzimuthalWall = new TGeoTubeSeg(Form("PETAL%d_INNER_AZIMUTHAL_WALL", mPetalCaseNumber), mRIn, mRIn + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - mOuterAzimuthalWall = new TGeoTubeSeg(Form("PETAL%d_OUTER_AZIMUTHAL_WALL", mPetalCaseNumber), mROut, mROut + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - mRadialWall = new TGeoTubeSeg(Form("PETAL%d_RADIAL_WALL", mPetalCaseNumber), mRIn, mROut + mWallThickness, mPetalCaseLength / 2., -0.5 * mAngularCoverageRadialWall * mToDeg, 0.5 * mAngularCoverageRadialWall * mToDeg); - mForwardWall = new TGeoTubeSeg(Form("PETAL%d_FORWARD_WALL", mPetalCaseNumber), mRIn, mROut + mWallThickness, mWallThickness / 2., -0.5 * (mAngularCoverageAzimuthalWall + 2 * mAngularCoverageRadialWall) * mToDeg, 0.5 * (mAngularCoverageAzimuthalWall + 2 * mAngularCoverageRadialWall) * mToDeg); - - // Rotate to correct section : 0-3 - mAzimuthalWallRot = new TGeoRotation((TString)Form("PETAL%d_AZIMUTHAL_WALL_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + 0.5 * mAngularCoverageAzimuthalWall + mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mAzimuthalWallRot->RegisterYourself(); - mRadialWall1Rot = new TGeoRotation((TString)Form("PETAL%d_RADIAL_WALL1_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + 0.5 * mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mRadialWall1Rot->RegisterYourself(); - mRadialWall2Rot = new TGeoRotation((TString)Form("PETAL%d_RADIAL_WALL2_ROT", mPetalCaseNumber), (mPetalCaseNumber * 0.5 * TMath::Pi() + mAngularCoverageAzimuthalWall + 1.5 * mAngularCoverageRadialWall) * mToDeg, 0., 0.); - mRadialWall2Rot->RegisterYourself(); - - // Place to correct position (open or closed) - mAzimuthalWallCombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mAzimuthalWallRot); - mAzimuthalWallCombiTrans->RegisterYourself(); - mRadialWall1CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_RADIAL_WALL1_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mRadialWall1Rot); - mRadialWall1CombiTrans->RegisterYourself(); - mRadialWall2CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_RADIAL_WALL2_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, mZPos, mRadialWall2Rot); - mRadialWall2CombiTrans->RegisterYourself(); - mForwardWall1CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_FORWARD_WALL1_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, (mPetalCaseLength + mWallThickness) / 2., mAzimuthalWallRot); - mForwardWall1CombiTrans->RegisterYourself(); - mForwardWall2CombiTrans = new TGeoCombiTrans((TString)Form("PETAL%d_FORWARD_WALL2_COMBITRANS", mPetalCaseNumber), mXPos, mYPos, -(mPetalCaseLength + mWallThickness) / 2., mAzimuthalWallRot); - mForwardWall2CombiTrans->RegisterYourself(); - - TString petalCaseCompositeFormula = (TString)Form("PETAL%d_INNER_AZIMUTHAL_WALL:PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_OUTER_AZIMUTHAL_WALL:PETAL%d_AZIMUTHAL_WALL_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_RADIAL_WALL:PETAL%d_RADIAL_WALL1_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_RADIAL_WALL:PETAL%d_RADIAL_WALL2_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_FORWARD_WALL:PETAL%d_FORWARD_WALL1_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber) + (TString)Form("+PETAL%d_FORWARD_WALL:PETAL%d_FORWARD_WALL2_COMBITRANS", mPetalCaseNumber, mPetalCaseNumber); - - TGeoCompositeShape* petalCaseComposite = new TGeoCompositeShape((TString)Form("PETALCASE%dsh", mPetalCaseNumber), petalCaseCompositeFormula); - mFullCompositeFormula = petalCaseComposite->GetName(); - auto& matmgr = o2::base::MaterialManager::Instance(); - const TGeoMedium* kMedBe = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_BERYLLIUM"); - - mPetalCaseName = Form("PETALCASE%d", mPetalCaseNumber); - mPetalCaseVolume = new TGeoVolume(mPetalCaseName, petalCaseComposite, kMedBe); - mPetalCaseVolume->SetVisibility(1); - mPetalCaseVolume->SetLineColor(kGray); - - LOGP(info, "Creating IRIS Tracker vacuum petal case {}", mPetalCaseNumber); - LOGP(info, "Inserting {} in {} ", mPetalCaseVolume->GetName(), motherVolume->GetName()); - motherVolume->AddNode(mPetalCaseVolume, 1, nullptr); -} - -void TRKPetalCase::constructColdPlate(TGeoVolume* motherVolume) -{ - Double_t coldPlateRadius = 2.6; // cm - Double_t coldPlateThickness = 0.15; // cm - Double_t coldPlateLength = 50.; // cm - - mColdPlate = new TGeoTubeSeg((TString)Form("PETAL%d_COLDPLATE", mPetalCaseNumber), coldPlateRadius, coldPlateRadius + coldPlateThickness, coldPlateLength / 2., -0.5 * mAngularCoverageAzimuthalWall * mToDeg, 0.5 * mAngularCoverageAzimuthalWall * mToDeg); - auto& matmgr = o2::base::MaterialManager::Instance(); - const TGeoMedium* medCeramic = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_CERAMIC"); - mColdPlateVolume = new TGeoVolume(Form("COLDPLATE%d", mPetalCaseNumber), mColdPlate, medCeramic); - - TString coldPlateCompositeFormula = mColdPlate->GetName(); - coldPlateCompositeFormula += ":"; - coldPlateCompositeFormula += mAzimuthalWallCombiTrans->GetName(); - addToPetalCaseComposite(coldPlateCompositeFormula); - - mColdPlateVolume->SetVisibility(1); - mColdPlateVolume->SetLineColor(kGray); - - LOGP(info, "Creating cold plate service"); - LOGP(info, "Inserting {} in {} ", mColdPlateVolume->GetName(), motherVolume->GetName()); - motherVolume->AddNode(mColdPlateVolume, 1, mAzimuthalWallCombiTrans); -} - -void TRKPetalCase::constructDetectionPetals(TGeoVolume* motherVolume) -{ - // Add petal layers - // layerNumber, layerName, rIn, angularCoverage, zLength, layerx2X0 - mPetalLayers.emplace_back(0, Form("%s_LAYER%d", mPetalCaseName.Data(), 0), 0.5f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - mPetalLayers.emplace_back(1, Form("%s_LAYER%d", mPetalCaseName.Data(), 1), 1.2f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - mPetalLayers.emplace_back(2, Form("%s_LAYER%d", mPetalCaseName.Data(), 2), 2.5f, mAngularCoverageAzimuthalWall, 50.f, 1.e-3); - for (Int_t i = 0; i < mPetalLayers.size(); ++i) { - mPetalLayers[i].createLayer(motherVolume, mAzimuthalWallCombiTrans); - } - - // Add petal disks - // diskNumber, diskName, zPos, rIn, rOut, angularCoverage, diskx2X0 - mPetalDisks.emplace_back(0, Form("%s_DISK%d", mPetalCaseName.Data(), 0), 26., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(1, Form("%s_DISK%d", mPetalCaseName.Data(), 1), 30., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(2, Form("%s_DISK%d", mPetalCaseName.Data(), 2), 34., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(3, Form("%s_DISK%d", mPetalCaseName.Data(), 3), -26., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(4, Form("%s_DISK%d", mPetalCaseName.Data(), 4), -30., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - mPetalDisks.emplace_back(5, Form("%s_DISK%d", mPetalCaseName.Data(), 5), -34., .5, 2.5, mAngularCoverageAzimuthalWall, 1.e-3); - for (Int_t i = 0; i < mPetalDisks.size(); ++i) { - mPetalDisks[i].createDisk(motherVolume, mAzimuthalWallCombiTrans); - } - - addDetectionPetelsToFullComposite(); -} - -void TRKPetalCase::addDetectionPetelsToFullComposite() -{ - for (Int_t i = 0; i < mPetalLayers.size(); ++i) { - Double_t zLength = mPetalLayers[i].getZLength(); - Double_t rIn = mPetalLayers[i].getInnerRadius(); - Double_t thickness = mPetalLayers[i].getChipThickness(); - Double_t angularCoverage = mPetalLayers[i].getAngularCoverage(); - TGeoTubeSeg* layerForExcavation = new TGeoTubeSeg(Form("PETALCASE%d_EXCAVATIONLAYER%d", mPetalCaseNumber, i), rIn, rIn + thickness, zLength / 2., -0.5 * angularCoverage * mToDeg, 0.5 * angularCoverage * mToDeg); - - TString layerForExcavationCompositeFormula = layerForExcavation->GetName(); - layerForExcavationCompositeFormula += ":"; - layerForExcavationCompositeFormula += mAzimuthalWallCombiTrans->GetName(); - addToPetalCaseComposite(layerForExcavationCompositeFormula); - } - - for (Int_t i = 0; i < mPetalDisks.size(); ++i) { - Double_t zPos = mPetalDisks[i].getZ(); - Double_t rIn = mPetalDisks[i].getInnerRadius(); - Double_t rOut = mPetalDisks[i].getOuterRadius(); - Double_t thickness = mPetalDisks[i].getThickness(); - Double_t angularCoverage = mPetalDisks[i].getAngularCoverage(); - TGeoTubeSeg* diskForExcavation = new TGeoTubeSeg(Form("PETALCASE%d_EXCAVATIONDISK%d", mPetalCaseNumber, i), rIn, rOut, thickness / 2., -0.5 * angularCoverage * mToDeg, 0.5 * angularCoverage * mToDeg); - TGeoCombiTrans* diskForExcavationCombiTrans = new TGeoCombiTrans(*(mAzimuthalWallCombiTrans->MakeClone())); // Copy from petal case - diskForExcavationCombiTrans->SetName((TString)Form("PETALCASE%d_EXCAVATIONDISK%d_COMBITRANS", mPetalCaseNumber, i)); - diskForExcavationCombiTrans->SetDz(zPos); // Overwrite z location - diskForExcavationCombiTrans->RegisterYourself(); - - TString diskForExcavationCompositeFormula = diskForExcavation->GetName(); - diskForExcavationCompositeFormula += ":"; - diskForExcavationCompositeFormula += diskForExcavationCombiTrans->GetName(); - addToPetalCaseComposite(diskForExcavationCompositeFormula); - } -} - -// ClassImp(TRKPetalCase); -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx deleted file mode 100644 index e24b24b48c882..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalDisk.cxx +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TRKPetalDisk.cxx -/// \brief Implementation of the TRKPetalDisk class - -#include "TRKSimulation/TRKPetalDisk.h" -#include "TRKBase/GeometryTGeo.h" - -#include // for LOG - -#include "TGeoManager.h" // for TGeoManager, gGeoManager -#include "TGeoMatrix.h" // for TGeoCombiTrans, TGeoRotation, etc -#include "TGeoTube.h" // for TGeoTube, TGeoTubeSeg -#include "TGeoVolume.h" // for TGeoVolume, TGeoVolumeAssembly -#include "TGeoCompositeShape.h" // for TGeoCompositeShape -#include "TMathBase.h" // for Abs -#include "TMath.h" // for Sin, RadToDeg, DegToRad, Cos, Tan, etc -#include "TGeoTube.h" - -#include // for snprintf - -namespace o2 -{ -namespace trk -{ - -TRKPetalDisk::TRKPetalDisk(Int_t diskNumber, std::string diskName, Float_t z, Float_t rIn, Float_t rOut, Float_t angularCoverage, Float_t Diskx2X0) -{ - // Creates a simple parametrized petal disk - mDiskNumber = diskNumber; - mDiskName = diskName; - mZ = z; - mAngularCoverage = angularCoverage; - mx2X0 = Diskx2X0; - mInnerRadius = rIn; - mOuterRadius = rOut; - Float_t Si_X0 = 9.5; - mChipThickness = Diskx2X0 * Si_X0; - - LOG(info) << "Creating TRK Disk " << mDiskNumber; - LOG(info) << " Using silicon X0 = " << Si_X0 << " to emulate disk radiation length."; - LOG(info) << " Disk z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness; -} - -void TRKPetalDisk::createDisk(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans) -{ - // Create tube, set sensitive volume, add to mother volume - Double_t toDeg = 180 / TMath::Pi(); - std::string chipName = mDiskName + "_" + o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mDiskNumber), - sensName = mDiskName + "_" + Form("%s%d", GeometryTGeo::getTRKSensorPattern(), mDiskNumber); - - mSensorName = sensName; - - TGeoTubeSeg* sensor = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* chip = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* disk = new TGeoTubeSeg(mInnerRadius, mOuterRadius, mChipThickness / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - sensVol->SetLineColor(kYellow); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - chipVol->SetLineColor(kYellow); - TGeoVolume* diskVol = new TGeoVolume(mDiskName.c_str(), disk, medAir); - diskVol->SetLineColor(kYellow); - - LOG(info) << "Inserting " << sensVol->GetName() << " inside " << chipVol->GetName(); - chipVol->AddNode(sensVol, 1, nullptr); - - LOG(info) << "Inserting " << chipVol->GetName() << " inside " << diskVol->GetName(); - diskVol->AddNode(chipVol, 1, nullptr); - - // Finally put everything in the mother volume - TGeoCombiTrans* fwdPetalCombiTrans = new TGeoCombiTrans(*(combiTrans->MakeClone())); // Copy from petal case - fwdPetalCombiTrans->SetDz(mZ); // Overwrite z location - fwdPetalCombiTrans->RegisterYourself(); - - LOG(info) << "Inserting " << diskVol->GetName() << " inside " << motherVolume->GetName(); - motherVolume->AddNode(diskVol, 1, fwdPetalCombiTrans); -} -// ClassImp(TRKPetalLayer); - -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx deleted file mode 100644 index c8ff0d957bb19..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKPetalLayer.cxx +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TRKSimulation/TRKPetalLayer.h" -#include "TRKBase/GeometryTGeo.h" - -#include "Framework/Logger.h" - -#include "TGeoTube.h" -#include "TGeoBBox.h" -#include "TGeoVolume.h" -#include "TGeoTube.h" -#include "TGeoMatrix.h" - -#include "TMath.h" - -namespace o2 -{ -namespace trk -{ -TRKPetalLayer::TRKPetalLayer(Int_t layerNumber, std::string layerName, Float_t rIn, Float_t angularCoverage, Float_t zLength, Float_t layerX2X0) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rIn), mAngularCoverage(angularCoverage), mZ(zLength), mX2X0(layerX2X0), mModuleWidth(4.54) -{ - Float_t Si_X0 = 9.5f; - mChipThickness = mX2X0 * Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} thickness: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mChipThickness, mZ, mX2X0); -} - -void TRKPetalLayer::createLayer(TGeoVolume* motherVolume, TGeoCombiTrans* combiTrans) -{ - TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - - std::string staveName = mLayerName + "_" + o2::trk::GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber), - chipName = mLayerName + "_" + o2::trk::GeometryTGeo::getTRKChipPattern() + std::to_string(mLayerNumber), - sensName = mLayerName + "_" + Form("%s%d", GeometryTGeo::getTRKSensorPattern(), mLayerNumber); - - mSensorName = sensName; - - Double_t toDeg = 180 / TMath::Pi(); - mLayer = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), mLayer, medAir); - layerVol->SetLineColor(kYellow); - - TGeoTubeSeg* stave = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* chip = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - TGeoTubeSeg* sensor = new TGeoTubeSeg(mInnerRadius, mInnerRadius + mChipThickness, mZ / 2., -0.5 * mAngularCoverage * toDeg, 0.5 * mAngularCoverage * toDeg); - - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - sensVol->SetLineColor(kYellow); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - chipVol->SetLineColor(kYellow); - TGeoVolume* staveVol = new TGeoVolume(staveName.c_str(), stave, medSi); - staveVol->SetLineColor(kYellow); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), staveVol->GetName()); - staveVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, combiTrans); -} -// ClassImp(TRKPetalLayer); - -} // namespace trk -} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h index 65a31b45bfab8..1a2e93636491c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -15,10 +15,7 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::trk::TRKPetalCase + ; #pragma link C++ class o2::trk::TRKLayer + ; -#pragma link C++ class o2::trk::TRKPetalLayer + ; -#pragma link C++ class o2::trk::TRKPetalDisk + ; #pragma link C++ class o2::trk::VDLayer + ; #pragma link C++ class o2::trk::TRKServices + ; #pragma link C++ class o2::trk::Detector + ; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index f52882bdcad37..cda9f960793f0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -42,7 +42,7 @@ void registerSensor(const std::string& volName, int petal, VDSensorDesc::Kind ki static inline std::string makeSensorName(const std::string& layerName, int layerNumber) { - return Form("%s_%s%d", layerName.c_str(), o2::trk::GeometryTGeo::getVDSensorPattern(), layerNumber); + return Form("%s_%s%d", layerName.c_str(), o2::trk::GeometryTGeo::getTRKSensorPattern(), layerNumber); } namespace @@ -372,7 +372,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined const std::string nameL0 = - "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "0"; if (rectangularL0) { @@ -402,7 +402,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 0); const std::string nameL1 = - "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "1"; VDCylindricalLayer L1(1, @@ -412,7 +412,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 1); const std::string nameL2 = - "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "2"; VDCylindricalLayer L2(2, @@ -455,7 +455,7 @@ static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) TString volName = TString::Format("Petal%d_Coldplate", petalId); auto* coldVol = new TGeoVolume(volName, shape, med); coldVol->SetLineColor(kAzure - 3); - coldVol->SetTransparency(70); + coldVol->SetTransparency(10); // Place in local petal coordinates (no extra transform); keep object alive by allocating shape/volume on heap. petalAsm->AddNode(coldVol, 1); @@ -622,7 +622,7 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) for (int i = 0; i < 6; ++i) { const std::string nameD = - "Petal" + std::to_string(petalID) + "_" + + std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + std::string(o2::trk::GeometryTGeo::getTRKPetalDiskPattern()) + std::to_string(i); VDDiskLayer disk(i, From 39720758e5485b3356a82f29246a4404b66afe4b Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Thu, 25 Sep 2025 15:10:07 +0200 Subject: [PATCH 03/99] Cleanup --- .../simulation/src/VDGeometryBuilder_com.cxx | 185 ------------------ 1 file changed, 185 deletions(-) delete mode 100644 Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx deleted file mode 100644 index d7310e63bb3a1..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder_com.cxx +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TRKSimulation/VDGeometryBuilder.h" - -#include -#include - -#include "Framework/Logger.h" -#include "TRKBase/GeometryTGeo.h" -#include "TRKSimulation/VDLayer.h" - -namespace o2::trk -{ - -// ---------- Shared constants (specs) ---------- -static constexpr float kX2X0 = 100.e-4f; // all-silicon -static constexpr float kLenZ_cm = 50.0; // cylindrical/rectangular layer length - -// Radii -static constexpr float rL0_cm = 0.5; // 5 mm -static constexpr float rL1_cm = 1.2; // 12 mm -static constexpr float rL2_cm = 2.5; // 25 mm - -// Rectangular L0 width (IRIS5) in cm -static constexpr float kL0RectWidth_cm = 0.83; // 8.3 mm - -// Disk geometry -static constexpr float diskRin_cm = 0.5; // 5 mm -static constexpr float diskRout_cm = 2.5; // 25 mm -static const float diskZ_cm[6] = {-34.0, -30.0, -26.0, 26.0, 30.0, 34.0}; - -// Petal wall parameters (cm) -static constexpr float kPetalZ_cm = 68.0; // full petal length -static constexpr float kWallThick_cm = 0.015; // 0.15 mm -static constexpr float kInnerWallRadius_cm = 0.48; // outer radius of wall arc (example) -static constexpr float kOuterWallRadius_cm = 3.0; // outer radius of wall arc (example) -static constexpr float kEps_cm = 0.0001; // tiny clearance - -// ---------- Helpers ---------- -static TGeoCombiTrans makePetalRotation(float phiDeg) -{ - auto* rot = new TGeoRotation(); - rot->RotateZ(phiDeg); - return TGeoCombiTrans(0.0, 0.0, 0.0, rot); -} - -// Convert a linear gap at radius R into an angular gap (deg) -inline float degFromArc(float arc, float radius) -{ - // arc and radius in the SAME units (cm or mm); result in degrees - return (arc / radius) * TMath::RadToDeg(); -} - -/** - * Compute silicon segment φ-span (degrees) inside one petal, - * when you know the number of petals and the linear gap at a given radius. - * - * All of: gap and radius must be in the SAME units (cm or mm). - * If you use cm everywhere (ROOT default), pass gap_cm and radius_cm. - */ -inline float phiSpanFromGap(int nPetals, float gap, float radius) -{ - if (nPetals <= 0 || radius <= 0.0) - return 0.0; - const float petalPhiDeg = 360.0 / nPetals; - const float gapDeg = degFromArc(gap, radius); - const float phiDeg = petalPhiDeg - gapDeg; - return (phiDeg > 0.0) ? phiDeg : 0.0; -} - -/** - * Compute silicon segment φ-span (degrees) from a known arc length at a given radius. - * arcLen and radius must be in the SAME units (cm or mm). - */ -inline float phiSpanFromArc(float arcLen, float radius) -{ - if (arcLen <= 0.0 || radius <= 0.0) - return 0.0; - return degFromArc(arcLen, radius); -} - -static void buildDisksSinglePetal(TGeoVolume* motherVolume, int petalID, int nPetals) -{ - const float phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); - const float phiHalfDisk = phiDisk_deg / 2.0; - - for (int i = 0; i < 6; ++i) { - VDDiskLayer disk( - /*layerNumber */ i, - /*layerName */ std::string(GeometryTGeo::getTRKPetalDiskPattern()) + std::to_string(i), - /*x2x0 */ kX2X0, - /*rMin */ diskRin_cm, - /*rMax */ diskRout_cm, - /*phiSpanDeg */ phiDisk_deg, - /*zPos */ diskZ_cm[i]); - - // Place each disk with local Z translation only (no rotation). - TGeoTranslation tz(0.0, 0.0, disk.getZPosition()); - disk.createLayer(petalAsm, &tz); - } -} - -static void buildCylLayersForPetal(TGeoVolume* mother, int petalIdx, int nPetals, bool rectangularL0) -{ - const double petalPhiSpan = 360.0 / double(nPetals); - const double rotPhi = petalPhiSpan * (petalIdx + 0.5); - auto combi = makePetalRotation(rotPhi); - - // ----- L0 ----- - if (rectangularL0) { - VDRectangularLayer L0( - /*layerNumber */ 0, - /*layerName */ std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L0", - /*x2x0 */ kX2X0, - /*width */ float(kL0RectWidth_cm), - /*lengthZ */ float(kLenZ_cm), - /*lengthSensZ */ float(kLenZ_cm) // no Z-segmentation now - ); - L0.createLayer(mother, &combi); - } else { - VDCylindricalLayer L0( - /*layerNumber */ 0, - /*layerName */ std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L0", - /*x2x0 */ kX2X0, - /*radius */ float(rL0_cm), - /*phiSpanDeg */ float(petalPhiSpan), - /*lengthZ */ float(kLenZ_cm), - /*lengthSensZ */ float(kLenZ_cm)); - L0.createLayer(mother, &combi); - } - - // ----- L1 ----- - VDCylindricalLayer L1( - 1, std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L1", kX2X0, - float(rL1_cm), float(petalPhiSpan), float(kLenZ_cm), float(kLenZ_cm)); - L1.createLayer(mother, &combi); - - // ----- L2 ----- - VDCylindricalLayer L2( - 2, std::string(GeometryTGeo::getTRKLayerPattern()) + "VD_L2", kX2X0, - float(rL2_cm), float(petalPhiSpan), float(kLenZ_cm), float(kLenZ_cm)); - L2.createLayer(mother, &combi); - - // ---------- φ-segmentation hooks (future) ---------- - // Spec notes on arc length and gaps are φ-related: - // * L0 arc length 6.247 mm with 1.63 mm gaps - // * L1/L2 gaps 1.2 mm - // To implement: compute per-sensor Δφ = arcLen_mm / radius_mm (in radians → degrees), - // and Δφ_gap = gap_mm / radius_mm. Then tile sensors around the petal span using - // additional rotations of sensor volumes. Current code builds one continuous segment per petal. -} - -// ---------- Public entry points ---------- -void createIRIS4Geometry(TGeoVolume* motherVolume) -{ - if (!motherVolume) { - LOGP(error, "createIRIS4Geometry: motherVolume is null"); - return; - } - const int nPetals = 4; - for (int p = 0; p < nPetals; ++p) { - buildCylLayersForPetal(motherVolume, p, nPetals, /*rectangularL0*/ false); - buildDisksForPetal(motherVolume, p, nPetals); - } -} - -void createIRIS5Geometry(TGeoVolume* motherVolume) -{ - if (!motherVolume) { - LOGP(error, "createIRIS5Geometry: motherVolume is null"); - return; - } - const int nPetals = 4; - for (int p = 0; p < nPetals; ++p) { - buildCylLayersForPetal(motherVolume, p, nPetals, /*rectangularL0*/ true); - buildDisk From d14ef27e16cfe655502aa291625dac8402af882f Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Mon, 20 Oct 2025 15:09:52 +0200 Subject: [PATCH 04/99] Add sensor region and type in the registry --- .../include/TRKSimulation/VDSensorRegistry.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h index 116b82bcb4a49..ca8e5b75e7fd5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDSensorRegistry.h @@ -8,11 +8,15 @@ namespace o2::trk { struct VDSensorDesc { - enum class Kind { Barrel, - Disk }; + enum class Region { Barrel, + Disk }; + enum class Type { Curved, + Plane, + }; std::string name; // sensor volume name int petal = -1; - Kind kind = Kind::Barrel; + Region region = Region::Barrel; + Type type = Type::Curved; int idx = -1; // layer or disk index }; @@ -21,7 +25,7 @@ std::vector& vdSensorRegistry(); // Utilities (defined in VDGeometryBuilder.cxx) void clearVDSensorRegistry(); -void registerSensor(const std::string& volName, int petal, VDSensorDesc::Kind kind, int idx); +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx); } // namespace o2::trk #endif From 251e27e68856853916f812c5e1593812b3dd7038 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Mon, 20 Oct 2025 15:14:46 +0200 Subject: [PATCH 05/99] VD sensor names, num of vols using registry --- .../Upgrades/ALICE3/Passive/src/Pipe.cxx | 2 +- .../include/TRKSimulation/Detector.h | 8 +-- .../ALICE3/TRK/simulation/src/Detector.cxx | 50 +++++++++---------- .../TRK/simulation/src/VDGeometryBuilder.cxx | 16 +++--- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx index 180d4fce42c86..fe0a1c50330fe 100644 --- a/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx +++ b/Detectors/Upgrades/ALICE3/Passive/src/Pipe.cxx @@ -123,7 +123,7 @@ void Alice3Pipe::ConstructGeometry() barrel->AddNode(pipeVolume, 1, new TGeoTranslation(0, 30.f, 0)); pipeVolume->SetLineColor(37); - pipeVolume->SetTransparency(10); + pipeVolume->SetTransparency(0); } void Alice3Pipe::createMaterials() diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h index 688876af0f762..92cebd681176d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h @@ -30,9 +30,6 @@ namespace trk class Detector : public o2::base::DetImpl { public: - static constexpr Int_t mNumberOfVolumes = 44; /// hardcoded for the current geometry = 8 MLOT layers + 36 volumes in the VD. TODO: automatize or change according to the current geometry - static constexpr Int_t mNumberOfVolumesVD = 36; /// hardcoded for the current geometry = 36 volumes in the VD. TODO: automatize or change according to the current geometry - Detector(bool active); Detector(); ~Detector(); @@ -79,6 +76,9 @@ class Detector : public o2::base::DetImpl void createGeometry(); private: + int mNumberOfVolumes; + int mNumberOfVolumesVD; + // Transient data about track passing the sensor struct TrackData { bool mHitStarted; // hit creation started @@ -90,7 +90,7 @@ class Detector : public o2::base::DetImpl GeometryTGeo* mGeometryTGeo; //! std::vector* mHits; // ITSMFT ones for the moment std::vector mLayers; - TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker + TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker std::vector mFirstOrLastLayers; // Names of the first or last layers bool InsideFirstOrLastLayer(std::string layerName); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 8c982a51e1b82..b03f63d92c639 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -56,28 +56,6 @@ Detector::Detector(bool active) configServices(); } - mSensorName.resize(mNumberOfVolumes); // hardcoded. TODO: change size when a different naming scheme for VD is in place. Ideally could be 4 petals + 8 layers = 12 - int VDvolume = 0; - for (int i = 0; i < 4; i++) { /// VD - for (int j = 0; j < 3; j++) { - mSensorName[VDvolume].Form("%s%d_%s%d_%s%d", GeometryTGeo::getTRKPetalPattern(), i, GeometryTGeo::getTRKPetalLayerPattern(), j, GeometryTGeo::getTRKSensorPattern(), j); - VDvolume++; - } - for (int j = 0; j < 6; j++) { - mSensorName[VDvolume].Form("%s%d_%s%d_%s%d", GeometryTGeo::getTRKPetalPattern(), i, GeometryTGeo::getTRKPetalDiskPattern(), j, GeometryTGeo::getTRKSensorPattern(), j); - VDvolume++; - } - } - - for (int i = 0; i < 8; i++) { /// MLOT - mSensorName[VDvolume].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i); - VDvolume++; - } - - for (auto vd : mSensorName) { - std::cout << "Volume name: " << vd << std::endl; - } - LOGP(info, "Summary of TRK configuration:"); for (auto& layer : mLayers) { LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer.getNumber(), layer.getName(), layer.getInnerRadius(), layer.getZ(), layer.getChipThickness()); @@ -262,13 +240,35 @@ void Detector::createGeometry() mServices.createServices(vTRK); // Build the VD using the petal builder - o2::trk::clearVDSensorRegistry(); - // Choose the VD design (here: IRIS4 by default). // You can wire this to a parameter in TRKBaseParam if desired. // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); + + o2::trk::clearVDSensorRegistry(); o2::trk::createIRIS4Geometry(vTRK); + // Fill sensor names from registry right after geometry creation + const auto& regs = o2::trk::vdSensorRegistry(); + mNumberOfVolumesVD = static_cast(regs.size()); + mNumberOfVolumes = mNumberOfVolumesVD + mLayers.size(); + mSensorName.resize(mNumberOfVolumes); + + // Fill VD sensor names from registry + int VDvolume = 0; + for (const auto& sensor : regs) { + mSensorName[VDvolume] = sensor.name; + VDvolume++; + } + + // Add MLOT sensor names + for (int i = 0; i < mLayers.size(); i++) { + mSensorName[VDvolume++].Form("%s%d", GeometryTGeo::getTRKSensorPattern(), i); + } + + for (auto vd : mSensorName) { + std::cout << "Volume name: " << vd << std::endl; + } + mServices.excavateFromVacuum("IRIS_CUTOUTsh"); mServices.registerVacuum(vTRK); } @@ -304,7 +304,7 @@ void Detector::defineSensitiveVolumes() LOGP(info, "Adding VD Sensitive Volume {}", v->GetName()); AddSensitiveVolume(v); // Optionally track first/last layers for TR references: - if (s.kind == o2::trk::VDSensorDesc::Kind::Barrel && (s.idx == 0 /*innermost*/)) { + if (s.region == o2::trk::VDSensorDesc::Region::Barrel && (s.idx == 0 /*innermost*/)) { mFirstOrLastLayers.push_back(s.name); } } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index cda9f960793f0..ae7e893dcf72b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -35,9 +35,9 @@ std::vector& vdSensorRegistry() { return gVDSensors; } void clearVDSensorRegistry() { gVDSensors.clear(); } -void registerSensor(const std::string& volName, int petal, VDSensorDesc::Kind kind, int idx) +void registerSensor(const std::string& volName, int petal, VDSensorDesc::Region region, VDSensorDesc::Type type, int idx) { - gVDSensors.push_back({volName, petal, kind, idx}); + gVDSensors.push_back({volName, petal, region, type, idx}); } static inline std::string makeSensorName(const std::string& layerName, int layerNumber) @@ -208,7 +208,7 @@ inline void buildIrisCutoutFromPetalSolid(int nPetals) } // namespace // =================== Specs & constants (ROOT units: cm) =================== -static constexpr double kX2X0 = 100.e-4f; // all silicon +static constexpr double kX2X0 = 0.001f; // 0.1% X0 per layer static constexpr double kLenZ_cm = 50.0f; // L0/L1/L2 Z length // Radii (cm) @@ -392,15 +392,15 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool auto* tr = new TGeoCombiTrans(x, y, z, rot); L0.createLayer(petalAsm, tr); + registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Plane, /*idx*/ 0); } else { VDCylindricalLayer L0(0, nameL0, kX2X0, rL0_cm, phiL0_deg, kLenZ_cm, kLenZ_cm); L0.createLayer(petalAsm, nullptr); + registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 0); } - registerSensor(makeSensorName(nameL0, 0), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 0); - const std::string nameL1 = std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + std::string(o2::trk::GeometryTGeo::getTRKPetalLayerPattern()) + "1"; @@ -409,7 +409,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool nameL1, kX2X0, rL1_cm, phiL1_deg, kLenZ_cm, kLenZ_cm); L1.createLayer(petalAsm, nullptr); - registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 1); + registerSensor(makeSensorName(nameL1, 1), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 1); const std::string nameL2 = std::string(o2::trk::GeometryTGeo::getTRKPetalPattern()) + std::to_string(petalID) + "_" + @@ -419,7 +419,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool nameL2, kX2X0, rL2_cm, phiL2_deg, kLenZ_cm, kLenZ_cm); L2.createLayer(petalAsm, nullptr); - registerSensor(makeSensorName(nameL2, 2), petalID, VDSensorDesc::Kind::Barrel, /*idx*/ 2); + registerSensor(makeSensorName(nameL2, 2), petalID, VDSensorDesc::Region::Barrel, VDSensorDesc::Type::Curved, /*idx*/ 2); } // Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly. @@ -632,7 +632,7 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) // Local Z placement only auto* tr = new TGeoTranslation(0.0, 0.0, static_cast(disk.getZPosition())); disk.createLayer(petalAsm, tr); - registerSensor(makeSensorName(nameD, i), petalID, VDSensorDesc::Kind::Disk, /*idx*/ i); + registerSensor(makeSensorName(nameD, i), petalID, VDSensorDesc::Region::Disk, VDSensorDesc::Type::Plane, /*idx*/ i); } } From 3c6fbd544c5b5791bc00056c6b9bd8f00281bec3 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Thu, 23 Oct 2025 09:51:46 +0200 Subject: [PATCH 06/99] Changes related to digitizer --- .../TRK/base/include/TRKBase/GeometryTGeo.h | 2 + .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 353 ++++++++++++++---- .../ALICE3/TRK/simulation/src/Detector.cxx | 4 +- .../TRK/simulation/src/VDGeometryBuilder.cxx | 2 +- 4 files changed, 282 insertions(+), 79 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index a1e4b9321130f..8f494c5d41991 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -42,6 +42,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache }; static const char* getTRKVolPattern() { return sVolumeName.c_str(); } static const char* getTRKLayerPattern() { return sLayerName.c_str(); } + static const char* getTRKPetalAssemblyPattern() { return sPetalAssemblyName.c_str(); } static const char* getTRKPetalPattern() { return sPetalName.c_str(); } static const char* getTRKPetalDiskPattern() { return sPetalDiskName.c_str(); } static const char* getTRKPetalLayerPattern() { return sPetalLayerName.c_str(); } @@ -141,6 +142,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static std::string sVolumeName; static std::string sLayerName; + static std::string sPetalAssemblyName; static std::string sPetalName; static std::string sPetalDiskName; static std::string sPetalLayerName; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 55d308c6f23d1..52ce26ab79e6d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -24,6 +24,7 @@ std::unique_ptr GeometryTGeo::sInstance; // Names std::string GeometryTGeo::sVolumeName = "TRKV"; std::string GeometryTGeo::sLayerName = "TRKLayer"; +std::string GeometryTGeo::sPetalAssemblyName = "PETAL"; std::string GeometryTGeo::sPetalName = "PETALCASE"; std::string GeometryTGeo::sPetalDiskName = "DISK"; std::string GeometryTGeo::sPetalLayerName = "LAYER"; @@ -69,9 +70,9 @@ void GeometryTGeo::Build(int loadTrans) } mNumberOfLayersMLOT = extractNumberOfLayersMLOT(); + mNumberOfPetalsVD = extractNumberOfPetalsVD(); mNumberOfActivePartsVD = extractNumberOfActivePartsVD(); mNumberOfLayersVD = extractNumberOfLayersVD(); - mNumberOfPetalsVD = extractNumberOfPetalsVD(); mNumberOfDisksVD = extractNumberOfDisksVD(); mNumberOfStaves.resize(mNumberOfLayersMLOT); @@ -450,118 +451,261 @@ int GeometryTGeo::extractNumberOfLayersMLOT() } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfActivePartsVD() const +int GeometryTGeo::extractNumberOfPetalsVD() const { - // The number of active parts returned here is 36 = 4 petals * (3 layers + 6 disks) - int numberOfParts = 0; + int numberOfPetals = 0; + TGeoVolume* trkV = gGeoManager->GetVolume(getTRKVolPattern()); + if (!trkV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; + } - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + // Loop on all TRKV nodes, count PETAL assemblies and their contents + TObjArray* nodes = trkV->GetNodes(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names - TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + LOGP(info, "Searching for petal assemblies in {} (pattern: {})", + getTRKVolPattern(), getTRKPetalAssemblyPattern()); + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); - if (strstr(name, getTRKPetalPattern()) != nullptr && (strstr(name, getTRKPetalLayerPattern()) != nullptr || strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfParts++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + if (strstr(name, getTRKPetalAssemblyPattern()) != nullptr) { + numberOfPetals++; + LOGP(info, "Found petal assembly: {}", name); + + // Get petal volume and its nodes for debugging + TGeoVolume* petalVol = nd->GetVolume(); + if (petalVol) { + TObjArray* petalNodes = petalVol->GetNodes(); + if (petalNodes) { + LOGP(debug, "Petal {} contains {} child nodes", name, petalNodes->GetEntriesFast()); + // Print all nodes in this petal + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + LOGP(debug, " Node {}: {}", k, petalNode->GetName()); + } + } else { + LOGP(warning, "Petal {} has no child nodes", name); + } + } else { + LOGP(warning, "Petal {} has no volume", name); } } } - return numberOfParts; + + if (numberOfPetals == 0) { + LOGP(warning, "No petal assemblies found in geometry"); + } else { + LOGP(info, "Found {} petal assemblies", numberOfPetals); + } + + return numberOfPetals; } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfDisksVD() const +int GeometryTGeo::extractNumberOfActivePartsVD() const { - // The number of disks returned here is 6 - int numberOfDisks = 0; - + // The number of active parts returned here is 36 = 4 petals * (3 layers + 6 disks) + int numberOfParts = 0; TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal to count its active parts TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } + + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); + if (strstr(name, getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && (strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfDisks++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + petalFound = true; + LOGP(info, "Counting active parts in petal: {}", name); + + // Found a petal, count its layers and disks + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", name); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", name); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + const char* nodeName = petalNode->GetName(); + + if (strstr(nodeName, getTRKPetalLayerPattern()) != nullptr || + strstr(nodeName, getTRKPetalDiskPattern()) != nullptr) { + numberOfParts++; + LOGP(debug, "Found active part in {}: {}", name, nodeName); } } + // We only need to check one petal as they're identical + break; } - return numberOfDisks; + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + return 0; + } + + if (numberOfParts == 0) { + LOGP(warning, "No active parts (layers/disks) found in petal"); + return 0; + } + + // Multiply by number of petals since all petals are identical + int totalParts = numberOfParts * mNumberOfPetalsVD; + LOGP(info, "Total number of active parts: {} ({}*{})", + totalParts, numberOfParts, mNumberOfPetalsVD); + return totalParts; } //__________________________________________________________________________ -int GeometryTGeo::extractNumberOfPetalsVD() const +int GeometryTGeo::extractNumberOfDisksVD() const { - // The number of petals returned here is 4 = number of petals - int numberOfChips = 0; - + // Count disks in the first petal (all petals are identical) + int numberOfDisks = 0; TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); - const char* name = nd->GetName(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } - if (strstr(name, getTRKPetalPattern()) != nullptr && (strstr(name, Form("%s%s", getTRKPetalLayerPattern(), "0")) != nullptr)) { - numberOfChips++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); + if (strstr(nd->GetName(), getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } + + petalFound = true; + LOGP(info, "Counting disks in petal: {}", nd->GetName()); + + // Count disks in this petal + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", nd->GetName()); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", nd->GetName()); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + if (strstr(petalNode->GetName(), getTRKPetalDiskPattern()) != nullptr) { + numberOfDisks++; + LOGP(info, "Found disk in {} : {}", nd->GetName(), petalNode->GetName()); } } + // One petal is enough + break; } - return numberOfChips; + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfDisks == 0) { + LOGP(warning, "No disks found in VD geometry"); + } + + return numberOfDisks; } //__________________________________________________________________________ int GeometryTGeo::extractNumberOfLayersVD() const { - // The number of layers returned here is 3 + // Count layers in the first petal (all petals are identical) int numberOfLayers = 0; - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); - const char* name = nd->GetName(); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && strstr(name, getTRKPetalLayerPattern()) != nullptr) { - numberOfLayers++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); + if (strstr(nd->GetName(), getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } + + petalFound = true; + LOGP(info, "Counting layers in petal: {}", nd->GetName()); + + // Count layers in this petal + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", nd->GetName()); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", nd->GetName()); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + if (strstr(petalNode->GetName(), getTRKPetalLayerPattern()) != nullptr) { + numberOfLayers++; + LOGP(info, "Found layer in {} : {}", nd->GetName(), petalNode->GetName()); } } + // One petal is enough + break; + } + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfLayers == 0) { + LOGP(warning, "No layers found in VD geometry"); } + return numberOfLayers; } @@ -570,27 +714,82 @@ int GeometryTGeo::extractNumberOfChipsPerPetalVD() const { // The number of chips per petal returned here is 9 for each layer = number of layers + number of quarters of disks per petal int numberOfChips = 0; - TGeoVolume* vdV = gGeoManager->GetVolume(getTRKVolPattern()); - if (vdV == nullptr) { - LOG(fatal) << getName() << " volume " << getTRKVolPattern() << " is not in the geometry"; + if (!vdV) { + LOGP(fatal, "{} volume {} is not in the geometry", getName(), getTRKVolPattern()); + return 0; } - // Loop on all TRKV nodes, count Layer volumes by checking names + // Find first petal assembly TObjArray* nodes = vdV->GetNodes(); - int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { - int lrID = -1; - auto nd = dynamic_cast(nodes->At(j)); + if (!nodes) { + LOGP(warning, "{} volume has no child nodes", getTRKVolPattern()); + return 0; + } + + bool petalFound = false; + + for (int j = 0; j < nodes->GetEntriesFast(); j++) { + auto* nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); + if (strstr(name, getTRKPetalAssemblyPattern()) == nullptr) { + continue; + } - if (strstr(name, Form("%s%s", getTRKPetalPattern(), "0")) != nullptr && (strstr(name, getTRKPetalLayerPattern()) != nullptr || strstr(name, getTRKPetalDiskPattern()) != nullptr)) { - numberOfChips++; - if ((lrID = extractVolumeCopy(name, GeometryTGeo::getTRKPetalPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; + petalFound = true; + LOGP(info, "Counting chips in petal: {}", name); + + // Found a petal, count sensors in its layers and disks + TGeoVolume* petalVol = nd->GetVolume(); + if (!petalVol) { + LOGP(warning, "Petal {} has no volume", name); + break; + } + + TObjArray* petalNodes = petalVol->GetNodes(); + if (!petalNodes) { + LOGP(warning, "Petal {} has no child nodes", name); + break; + } + + for (int k = 0; k < petalNodes->GetEntriesFast(); k++) { + auto* petalNode = dynamic_cast(petalNodes->At(k)); + const char* nodeName = petalNode->GetName(); + TGeoVolume* vol = petalNode->GetVolume(); + + if (!vol) { + LOGP(debug, "Node {} has no volume", nodeName); + continue; + } + + // Look for sensors in this volume + TObjArray* subNodes = vol->GetNodes(); + if (!subNodes) { + LOGP(debug, "Node {} has no sub-nodes", nodeName); + continue; + } + + for (int i = 0; i < subNodes->GetEntriesFast(); i++) { + auto* subNode = dynamic_cast(subNodes->At(i)); + if (strstr(subNode->GetName(), getTRKSensorPattern()) != nullptr) { + numberOfChips++; + LOGP(debug, "Found sensor in {}: {}", nodeName, subNode->GetName()); + } } } + // We only need one petal + break; } + + if (!petalFound) { + LOGP(warning, "No petal assembly found matching pattern '{}'", getTRKPetalAssemblyPattern()); + } + + if (numberOfChips == 0) { + LOGP(warning, "No chips/sensors found in VD petal"); + } + + LOGP(info, "Number of chips per petal: {}", numberOfChips); return numberOfChips; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index b03f63d92c639..a4d99ccf9f79f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -282,7 +282,7 @@ void Detector::InitializeO2Detector() mSensorID.resize(mNumberOfVolumes); // hardcoded. TODO: change size when a different namingh scheme for VD is in place. Ideally could be 4 petals + 8 layers = 12 for (int i = 0; i < mNumberOfVolumes; i++) { mSensorID[i] = gMC ? TVirtualMC::GetMC()->VolId(mSensorName[i]) : 0; // Volume ID from the Geant geometry - LOGP(info, "{}: mSensorID={}", i, mSensorID[i]); + LOGP(info, "{}: mSensorID={}, mSensorName={}", i, mSensorID[i], mSensorName[i].Data()); } } @@ -450,6 +450,8 @@ bool Detector::ProcessHits(FairVolume* vol) Print(vol, volume, subDetID, layer, stave, halfstave, chipID); + mGeometryTGeo->Print(); + Hit* p = addHit(stack->GetCurrentTrackNumber(), chipID, mTrackData.mPositionStart.Vect(), positionStop.Vect(), mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index ae7e893dcf72b..af813132ff6cb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -639,7 +639,7 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) // Build one complete petal assembly (walls + L0..L2 + disks) in LOCAL coords. static TGeoVolume* buildPetalAssembly(int nPetals, int petalID, bool rectangularL0) { - auto* petalAsm = new TGeoVolumeAssembly(Form("Petal%d", petalID)); + auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm); // Pass petalID to layers/disks for naming From d16e1a3a488ddfd3af468aba0798bb4cff412187 Mon Sep 17 00:00:00 2001 From: Matthias Kleiner <48915672+matthias-kleiner@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:10:47 +0200 Subject: [PATCH 07/99] TPC: Apply T/P scaling of VDrift only if T/P change is large enough (#14668) * TPC: Apply T/P scaling of VDrift only if T/P change is large enough - suppress error for getting reference T/P in case default VDrift is used * remove info message and print warning only once --- Detectors/TPC/calibration/src/VDriftHelper.cxx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Detectors/TPC/calibration/src/VDriftHelper.cxx b/Detectors/TPC/calibration/src/VDriftHelper.cxx index 2badf3bb510e8..71c4e50a63fcf 100644 --- a/Detectors/TPC/calibration/src/VDriftHelper.cxx +++ b/Detectors/TPC/calibration/src/VDriftHelper.cxx @@ -164,6 +164,10 @@ void VDriftHelper::extractCCDBInputs(ProcessingContext& pc, bool laser, bool its mIsTPScalingPossible = (vd.refTP > 0) || extractTPForVDrift(vd); } if (mIsTPScalingPossible) { + // if no new VDrift object was loaded and if delta TP is small, do not rescale and return + if (!mUpdated && std::abs(tp - vd.refTP) < 1e-5) { + return; + } mUpdated = true; vd.normalize(0, tp); if (vd.creationTime == saveVD.creationTime) { @@ -245,6 +249,15 @@ bool VDriftHelper::extractTPForVDrift(VDriftCorrFact& vdrift, int64_t tsStepMS) const int64_t tsStart = vdrift.firstTime; const int64_t tsEnd = vdrift.lastTime; + if (tsStart == tsEnd) { + static bool warned = false; + if (!warned) { + warned = true; + LOGP(warn, "VDriftHelper: Cannot extract T/P for VDrift with identical start/end time {}!", tsStart); + } + return false; + } + // make sanity check of the time range const auto [minValidTime, maxValidTime] = mPTHelper.getMinMaxTime(); const int64_t minTimeAccepted = static_cast(minValidTime) - 20 * o2::ccdb::CcdbObjectInfo::MINUTE; From 7d533ccd40f0718ffc3de2719ea1f4c01f1189fe Mon Sep 17 00:00:00 2001 From: Fabrizio Chinu <91954233+fchinu@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:29:09 +0200 Subject: [PATCH 08/99] ITS: allow sharing of innermost cluster among tracks (#14684) * feat: allow sharing of first cluster in ITS tracking * Implement modifications from #14432 * Move to bounded_vector --------- Co-authored-by: Maximiliano Puccio --- .../include/ITStracking/Configuration.h | 1 + .../include/ITStracking/TrackingConfigParam.h | 1 + .../ITSMFT/ITS/tracking/src/Configuration.cxx | 1 + .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 44 +++++++++++++++++-- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 2bfa0639ad5a2..d7c4e27add739 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -51,6 +51,7 @@ struct TrackingParameters { float Diamond[3] = {0.f, 0.f, 0.f}; /// General parameters + bool AllowSharingFirstCluster = false; int ClusterSharing = 0; int MinTrackLength = 7; float NSigmaCut = 5; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index d368eb1d1f56a..6c4ecc5ab424d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -104,6 +104,7 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper::max(); bool dropTFUponFailure = false; bool fataliseUponFailure = true; // granular management of the fatalisation in async mode + bool allowSharingFirstCluster = false; // allow first cluster sharing among tracks O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); }; diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index 6af66b18a2878..df736b3c7efdb 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -194,6 +194,7 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode p.DropTFUponFailure = tc.dropTFUponFailure; p.SaveTimeBenchmarks = tc.saveTimeBenchmarks; p.FataliseUponFailure = tc.fataliseUponFailure; + p.AllowSharingFirstCluster = tc.allowSharingFirstCluster; if (tc.useMatCorrTGeo) { p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrTGeo; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 46a64adf5fa2f..3a58ad1c000b7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -718,6 +718,10 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou template void TrackerTraits::findRoads(const int iteration) { + bounded_vector> firstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); + bounded_vector> sharedFirstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); + firstClusters.resize(mTrkParams[iteration].NLayers); + sharedFirstClusters.resize(mTrkParams[iteration].NLayers); for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) { auto seedFilter = [&](const auto& seed) { @@ -827,15 +831,22 @@ void TrackerTraits::findRoads(const int iteration) for (auto& track : tracks) { int nShared = 0; bool isFirstShared{false}; + int firstLayer{-1}, firstCluster{-1}; for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } - nShared += int(mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); - isFirstShared |= !iLayer && mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + bool isShared = mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + nShared += int(isShared); + if (firstLayer < 0) { + firstCluster = track.getClusterIndex(iLayer); + isFirstShared = isShared && mTrkParams[0].AllowSharingFirstCluster && std::find(firstClusters[iLayer].begin(), firstClusters[iLayer].end(), firstCluster) != firstClusters[iLayer].end(); + firstLayer = iLayer; + } } - if (nShared > mTrkParams[0].ClusterSharing) { + /// do not account for the first cluster in the shared clusters number if it is allowed + if (nShared - int(isFirstShared && mTrkParams[0].AllowSharingFirstCluster) > mTrkParams[0].ClusterSharing) { continue; } @@ -864,6 +875,33 @@ void TrackerTraits::findRoads(const int iteration) track.setNextROFbit(); } mTimeFrame->getTracks(o2::gpu::CAMath::Min(rofs[0], rofs[1])).emplace_back(track); + + firstClusters[firstLayer].push_back(firstCluster); + if (isFirstShared) { + sharedFirstClusters[firstLayer].push_back(firstCluster); + } + } + } + + /// Now we have to set the shared cluster flag + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { + std::sort(sharedFirstClusters[iLayer].begin(), sharedFirstClusters[iLayer].end()); + } + + for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { + for (auto& track : mTimeFrame->getTracks(iROF)) { + int firstLayer{mTrkParams[0].NLayers}, firstCluster{constants::UnusedIndex}; + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { + continue; + } + firstLayer = iLayer; + firstCluster = track.getClusterIndex(iLayer); + break; + } + if (std::binary_search(sharedFirstClusters[firstLayer].begin(), sharedFirstClusters[firstLayer].end(), firstCluster)) { + track.setSharedClusters(); + } } } } From 62a71e08215f827355119bc9caddaba8fe6cd643 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 25 Sep 2025 11:32:34 +0200 Subject: [PATCH 09/99] ITS: GPU: improve mm and add tests for resource (#14681) Signed-off-by: Felix Schlepper Co-authored-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/CMakeLists.txt | 2 + .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 142 +++++++------ .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 14 +- .../include/ITStracking/BoundedAllocator.h | 61 +++--- .../tracking/include/ITStracking/Cluster.h | 9 +- .../include/ITStracking/ExternalAllocator.h | 32 +++ .../tracking/include/ITStracking/TimeFrame.h | 46 ++--- .../tracking/include/ITStracking/Tracker.h | 2 +- .../include/ITStracking/TrackerTraits.h | 2 +- .../tracking/include/ITStracking/Vertexer.h | 2 +- .../include/ITStracking/VertexerTraits.h | 2 +- .../ITSMFT/ITS/tracking/src/TimeFrame.cxx | 175 +++++++--------- .../ITSMFT/ITS/tracking/src/Vertexer.cxx | 1 - .../ITSMFT/ITS/tracking/test/CMakeLists.txt | 16 ++ .../test/testBoundedMemoryResource.cxx | 190 ++++++++++++++++++ .../ITS3/reconstruction/src/IOUtils.cxx | 2 +- GPU/GPUTracking/Global/GPUChainITS.cxx | 19 +- GPU/GPUTracking/Global/GPUChainITS.h | 3 +- 18 files changed, 474 insertions(+), 246 deletions(-) create mode 100644 Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt create mode 100644 Detectors/ITSMFT/ITS/tracking/test/testBoundedMemoryResource.cxx diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 291ddffbf9475..001ee537f50d2 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -56,3 +56,5 @@ o2_target_root_dictionary(ITStracking if(CUDA_ENABLED OR HIP_ENABLED) add_subdirectory(GPU) endif() + +add_subdirectory(test) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 4da91522371f8..27bcf04746da5 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -32,7 +32,7 @@ template void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator) { if (extAllocator) { - *ptr = this->mAllocator->allocate(size); + *ptr = this->mExtDeviceAllocator->allocate(size); } else { GPULog("Calling default CUDA allocator"); GPUChkErrS(cudaMallocAsync(reinterpret_cast(ptr), size, stream.get())); @@ -43,7 +43,7 @@ template void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator) { if (extAllocator) { - *ptr = this->mAllocator->allocate(size); + *ptr = this->mExtDeviceAllocator->allocate(size); } else { GPULog("Calling default CUDA allocator"); GPUChkErrS(cudaMalloc(reinterpret_cast(ptr), size)); @@ -56,7 +56,7 @@ void TimeFrameGPU::loadIndexTableUtils(const int iteration) GPUTimer timer("loading indextable utils"); if (!iteration) { GPULog("gpu-allocation: allocating IndexTableUtils buffer, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); - allocMem(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtilsN), this->getExtAllocator()); + allocMem(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtilsN), this->hasExternalDeviceAllocator()); } GPULog("gpu-transfer: loading IndexTableUtils object, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); GPUChkErrS(cudaMemcpy(mIndexTableUtilsDevice, &(this->mIndexTableUtils), sizeof(IndexTableUtilsN), cudaMemcpyHostToDevice)); @@ -67,12 +67,14 @@ void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteratio { if (!iteration) { GPUTimer timer("creating unsorted clusters array"); - allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); mPinnedUnsortedClusters.set(nLayers); - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { - GPUChkErrS(cudaHostRegister(this->mUnsortedClusters[iLayer].data(), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - mPinnedUnsortedClusters.set(iLayer); + if (!this->hasExternalDeviceAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUnsortedClusters[iLayer].data(), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); + mPinnedUnsortedClusters.set(iLayer); + } } } } @@ -83,7 +85,7 @@ void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, cons if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading unsorted clusters", layer); GPULog("gpu-transfer: loading {} unsorted clusters on layer {}, for {:.2f} MB.", this->mUnsortedClusters[layer].size(), layer, this->mUnsortedClusters[layer].size() * sizeof(Cluster) / constants::MB); - allocMemAsync(reinterpret_cast(&mUnsortedClustersDevice[layer]), this->mUnsortedClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mUnsortedClustersDevice[layer]), this->mUnsortedClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(mUnsortedClustersDevice[layer], this->mUnsortedClusters[layer].data(), this->mUnsortedClusters[layer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mUnsortedClustersDeviceArray[layer], &mUnsortedClustersDevice[layer], sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -94,12 +96,14 @@ void TimeFrameGPU::createClustersDeviceArray(const int iteration, const { if (!iteration) { GPUTimer timer("creating sorted clusters array"); - allocMem(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); mPinnedClusters.set(nLayers); - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { - GPUChkErrS(cudaHostRegister(this->mClusters[iLayer].data(), this->mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - mPinnedClusters.set(iLayer); + if (!this->hasExternalDeviceAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mClusters[iLayer].data(), this->mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); + mPinnedClusters.set(iLayer); + } } } } @@ -110,7 +114,7 @@ void TimeFrameGPU::loadClustersDevice(const int iteration, const int la if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); GPULog("gpu-transfer: loading {} clusters on layer {}, for {:.2f} MB.", this->mClusters[layer].size(), layer, this->mClusters[layer].size() * sizeof(Cluster) / constants::MB); - allocMemAsync(reinterpret_cast(&mClustersDevice[layer]), this->mClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mClustersDevice[layer]), this->mClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(mClustersDevice[layer], this->mClusters[layer].data(), this->mClusters[layer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mClustersDeviceArray[layer], &mClustersDevice[layer], sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -121,12 +125,14 @@ void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) { if (!iteration) { GPUTimer timer("creating clustersindextable array"); - allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); mPinnedClustersIndexTables.set(nLayers); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - GPUChkErrS(cudaHostRegister(this->mIndexTables[iLayer].data(), this->mIndexTables[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - mPinnedClustersIndexTables.set(iLayer); + if (!this->hasExternalDeviceAllocator()) { + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mIndexTables[iLayer].data(), this->mIndexTables[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + mPinnedClustersIndexTables.set(iLayer); + } } } } @@ -137,7 +143,7 @@ void TimeFrameGPU::loadClustersIndexTables(const int iteration, const i if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); GPULog("gpu-transfer: loading clusters indextable for layer {} with {} elements, for {:.2f} MB.", layer, this->mIndexTables[layer].size(), this->mIndexTables[layer].size() * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[layer]), this->mIndexTables[layer].size() * sizeof(int), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[layer]), this->mIndexTables[layer].size() * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(mClustersIndexTablesDevice[layer], this->mIndexTables[layer].data(), this->mIndexTables[layer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mClustersIndexTablesDeviceArray[layer], &mClustersIndexTablesDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -148,12 +154,14 @@ void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, c { if (!iteration) { GPUTimer timer("creating used clusters flags"); - allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), nLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); mPinnedUsedClusters.set(nLayers); - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { - GPUChkErrS(cudaHostRegister(this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(uint8_t), cudaHostRegisterPortable)); - mPinnedUsedClusters.set(iLayer); + if (!this->hasExternalDeviceAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(uint8_t), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(iLayer); + } } } } @@ -164,7 +172,7 @@ void TimeFrameGPU::createUsedClustersDevice(const int iteration, const if (!iteration) { GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); GPULog("gpu-transfer: creating {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[layer].size(), layer, this->mUsedClusters[layer].size() * sizeof(unsigned char) / constants::MB); - allocMemAsync(reinterpret_cast(&mUsedClustersDevice[layer]), this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mUsedClustersDevice[layer]), this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemsetAsync(mUsedClustersDevice[layer], 0, this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mUsedClustersDeviceArray[layer], &mUsedClustersDevice[layer], sizeof(unsigned char*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -185,12 +193,14 @@ void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration { if (!iteration) { GPUTimer timer("creating ROFrame clusters array"); - allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), nLayers * sizeof(int*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), nLayers * sizeof(int*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); mPinnedROFramesClusters.set(nLayers); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - GPUChkErrS(cudaHostRegister(this->mROFramesClusters[iLayer].data(), this->mROFramesClusters[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - mPinnedROFramesClusters.set(iLayer); + if (!this->hasExternalDeviceAllocator()) { + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mROFramesClusters[iLayer].data(), this->mROFramesClusters[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + mPinnedROFramesClusters.set(iLayer); + } } } } @@ -201,7 +211,7 @@ void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading ROframe clusters", layer); GPULog("gpu-transfer: loading {} ROframe clusters info on layer {}, for {:.2f} MB.", this->mROFramesClusters[layer].size(), layer, this->mROFramesClusters[layer].size() * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[layer]), this->mROFramesClusters[layer].size() * sizeof(int), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[layer]), this->mROFramesClusters[layer].size() * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(mROFramesClustersDevice[layer], this->mROFramesClusters[layer].data(), this->mROFramesClusters[layer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mROFramesClustersDeviceArray[layer], &mROFramesClustersDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -212,12 +222,14 @@ void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iterati { if (!iteration) { GPUTimer timer("creating trackingframeinfo array"); - allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); mPinnedTrackingFrameInfo.set(nLayers); - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - GPUChkErrS(cudaHostRegister(this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); - mPinnedTrackingFrameInfo.set(iLayer); + if (!this->hasExternalDeviceAllocator()) { + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); + mPinnedTrackingFrameInfo.set(iLayer); + } } } } @@ -228,7 +240,7 @@ void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, con if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading trackingframeinfo", layer); GPULog("gpu-transfer: loading {} tfinfo on layer {}, for {:.2f} MB.", this->mTrackingFrameInfo[layer].size(), layer, this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[layer]), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[layer]), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(mTrackingFrameInfoDevice[layer], this->mTrackingFrameInfo[layer].data(), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mTrackingFrameInfoDeviceArray[layer], &mTrackingFrameInfoDevice[layer], sizeof(TrackingFrameInfo*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -241,7 +253,7 @@ void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) GPUTimer timer("loading multiplicity cut mask"); GPULog("gpu-transfer: iteration {} loading multiplicity cut mask with {} elements, for {:.2f} MB.", iteration, this->mMultiplicityCutMask.size(), this->mMultiplicityCutMask.size() * sizeof(uint8_t) / constants::MB); if (!iteration) { // only allocate on first call - allocMem(reinterpret_cast(&mMultMaskDevice), this->mMultiplicityCutMask.size() * sizeof(uint8_t), this->getExtAllocator()); + allocMem(reinterpret_cast(&mMultMaskDevice), this->mMultiplicityCutMask.size() * sizeof(uint8_t), this->hasExternalDeviceAllocator()); } GPUChkErrS(cudaMemcpy(mMultMaskDevice, this->mMultiplicityCutMask.data(), this->mMultiplicityCutMask.size() * sizeof(uint8_t), cudaMemcpyHostToDevice)); } @@ -253,10 +265,10 @@ void TimeFrameGPU::loadVertices(const int iteration) if (!iteration) { GPUTimer timer("loading seeding vertices"); GPULog("gpu-transfer: loading {} ROframes vertices, for {:.2f} MB.", this->mROFramesPV.size(), this->mROFramesPV.size() * sizeof(int) / constants::MB); - allocMem(reinterpret_cast(&mROFramesPVDevice), this->mROFramesPV.size() * sizeof(int), this->getExtAllocator()); + allocMem(reinterpret_cast(&mROFramesPVDevice), this->mROFramesPV.size() * sizeof(int), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpy(mROFramesPVDevice, this->mROFramesPV.data(), this->mROFramesPV.size() * sizeof(int), cudaMemcpyHostToDevice)); GPULog("gpu-transfer: loading {} seeding vertices, for {:.2f} MB.", this->mPrimaryVertices.size(), this->mPrimaryVertices.size() * sizeof(Vertex) / constants::MB); - allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->getExtAllocator()); + allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpy(mPrimaryVerticesDevice, this->mPrimaryVertices.data(), this->mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice)); } } @@ -265,7 +277,7 @@ template void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) { if (!iteration) { - allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasExternalDeviceAllocator()); } } @@ -276,7 +288,7 @@ void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const const int ncls = this->mClusters[layer].size() + 1; if (!iteration) { GPULog("gpu-allocation: creating tracklets LUT for {} elements on layer {}, for {:.2f} MB.", ncls, layer, ncls * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[layer]), ncls * sizeof(int), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[layer]), ncls * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(&mTrackletsLUTDeviceArray[layer], &mTrackletsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[layer], 0, ncls * sizeof(int), mGpuStreams[layer].get())); @@ -287,7 +299,7 @@ void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating tracklet buffers array"); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasExternalDeviceAllocator()); } } @@ -299,7 +311,7 @@ void TimeFrameGPU::createTrackletsBuffers(const int layer) GPUChkErrS(cudaMemcpyAsync(&mNTracklets[layer], mTrackletsLUTDevice[layer] + this->mClusters[layer].size(), sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); mGpuStreams[layer].sync(); // ensure number of tracklets is correct GPULog("gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer], layer, mNTracklets[layer] * sizeof(Tracklet) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[layer]), mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mTrackletsDevice[layer]), mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(&mTrackletsDeviceArray[layer], &mTrackletsDevice[layer], sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -331,7 +343,7 @@ void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells neighbours", layer); GPULog("gpu-transfer: reserving neighbours LUT for {} elements on layer {}, for {:.2f} MB.", mNCells[layer] + 1, layer, (mNCells[layer] + 1) * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[layer]), (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[layer]), (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[layer], 0, (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); } @@ -340,7 +352,7 @@ void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const uns { GPUTimer timer(mGpuStreams[layer], "reserving neighboursLUT"); GPULog("gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {:.2f} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), mGpuStreams[layer], this->getExtAllocator()); // We need one element more to move exc -> inc + allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); // We need one element more to move exc -> inc GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[layer].get())); } @@ -350,8 +362,8 @@ void TimeFrameGPU::loadCellsDevice() GPUTimer timer(mGpuStreams, "loading cell seeds", nLayers - 2); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { GPULog("gpu-transfer: loading {} cell seeds on layer {}, for {:.2f} MB.", this->mCells[iLayer].size(), iLayer, this->mCells[iLayer].size() * sizeof(CellSeedN) / constants::MB); - allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeedN), mGpuStreams[iLayer], this->getExtAllocator()); - allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer], this->getExtAllocator()); // accessory for the neigh. finding. + allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeedN), mGpuStreams[iLayer], this->hasExternalDeviceAllocator()); + allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer], this->hasExternalDeviceAllocator()); // accessory for the neigh. finding. GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[iLayer], 0, (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer].get())); GPUChkErrS(cudaMemcpyAsync(mCellsDevice[iLayer], this->mCells[iLayer].data(), this->mCells[iLayer].size() * sizeof(CellSeedN), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } @@ -362,7 +374,7 @@ void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells LUTs array"); - allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasExternalDeviceAllocator()); } } @@ -371,7 +383,7 @@ void TimeFrameGPU::createCellsLUTDevice(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells LUTs", layer); GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer] + 1, layer, (mNTracklets[layer] + 1) * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mCellsLUTDevice[layer]), (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mCellsLUTDevice[layer]), (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemsetAsync(mCellsLUTDevice[layer], 0, (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mCellsLUTDeviceArray[layer], &mCellsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -381,7 +393,7 @@ void TimeFrameGPU::createCellsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells buffers array"); - allocMem(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeedN*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeedN*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpy(mCellsDeviceArray, mCellsDevice.data(), mCellsDevice.size() * sizeof(CellSeedN*), cudaMemcpyHostToDevice)); } } @@ -394,7 +406,7 @@ void TimeFrameGPU::createCellsBuffers(const int layer) GPUChkErrS(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[layer], sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); mGpuStreams[layer].sync(); // ensure number of cells is correct GPULog("gpu-transfer: creating cell buffer for {} elements on layer {}, for {:.2f} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeedN) / constants::MB); - allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpyAsync(&mCellsDeviceArray[layer], &mCellsDevice[layer], sizeof(CellSeedN*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } @@ -414,7 +426,7 @@ void TimeFrameGPU::loadRoadsDevice() { GPUTimer timer("loading roads device"); GPULog("gpu-transfer: loading {} roads, for {:.2f} MB.", this->mRoads.size(), this->mRoads.size() * sizeof(Road) / constants::MB); - allocMem(reinterpret_cast(&mRoadsDevice), this->mRoads.size() * sizeof(Road), this->getExtAllocator()); + allocMem(reinterpret_cast(&mRoadsDevice), this->mRoads.size() * sizeof(Road), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpy(mRoadsDevice, this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice)); } @@ -424,7 +436,7 @@ void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seed { GPUTimer timer("loading track seeds"); GPULog("gpu-transfer: loading {} track seeds, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(CellSeedN) / constants::MB); - allocMem(reinterpret_cast(&mTrackSeedsDevice), seeds.size() * sizeof(CellSeedN), this->getExtAllocator()); + allocMem(reinterpret_cast(&mTrackSeedsDevice), seeds.size() * sizeof(CellSeedN), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(seeds.data(), seeds.size() * sizeof(CellSeedN), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpy(mTrackSeedsDevice, seeds.data(), seeds.size() * sizeof(CellSeedN), cudaMemcpyHostToDevice)); } @@ -437,10 +449,10 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) GPUChkErrS(cudaMemcpyAsync(&(this->mNNeighbours[layer]), &(mNeighboursLUTDevice[layer][this->mNCells[layer + 1] - 1]), sizeof(unsigned int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); mGpuStreams[layer].sync(); // ensure number of neighbours is correct GPULog("gpu-allocation: reserving {} neighbours (pairs), for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(gpuPair) / constants::MB); - allocMemAsync(reinterpret_cast(&mNeighbourPairsDevice[layer]), (this->mNNeighbours[layer]) * sizeof(gpuPair), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mNeighbourPairsDevice[layer]), (this->mNNeighbours[layer]) * sizeof(gpuPair), mGpuStreams[layer], this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemsetAsync(mNeighbourPairsDevice[layer], -1, (this->mNNeighbours[layer]) * sizeof(gpuPair), mGpuStreams[layer].get())); GPULog("gpu-allocation: reserving {} neighbours, for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(gpuPair) / constants::MB); - allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), (this->mNNeighbours[layer]) * sizeof(int), mGpuStreams[layer], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), (this->mNNeighbours[layer]) * sizeof(int), mGpuStreams[layer], this->hasExternalDeviceAllocator()); } template @@ -449,7 +461,7 @@ void TimeFrameGPU::createTrackITSExtDevice(bounded_vector& s GPUTimer timer("reserving tracks"); mTrackITSExt = bounded_vector(seeds.size(), {}, this->getMemoryPool().get()); GPULog("gpu-allocation: reserving {} tracks, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(o2::its::TrackITSExt) / constants::MB); - allocMem(reinterpret_cast(&mTrackITSExtDevice), seeds.size() * sizeof(o2::its::TrackITSExt), this->getExtAllocator()); + allocMem(reinterpret_cast(&mTrackITSExtDevice), seeds.size() * sizeof(o2::its::TrackITSExt), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, seeds.size() * sizeof(o2::its::TrackITSExt))); GPUChkErrS(cudaHostRegister(mTrackITSExt.data(), seeds.size() * sizeof(o2::its::TrackITSExt), cudaHostRegisterPortable)); } @@ -462,13 +474,13 @@ void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) for (int32_t iMode{0}; iMode < 2; ++iMode) { if (!iteration) { GPULog("gpu-transfer: creating vertexer tracklets per cluster for {} elements for mode {}, for {:.2f} MB.", ncls, iMode, ncls * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterDevice[iMode]), ncls * sizeof(int32_t), mGpuStreams[iMode], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterDevice[iMode]), ncls * sizeof(int32_t), mGpuStreams[iMode], this->hasExternalDeviceAllocator()); GPULog("gpu-transfer: creating vertexer tracklets per cluster sum for {} elements for mode {}, for {:.2f} MB.", ncls + 1, iMode, (ncls + 1) * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterSumDevice[iMode]), (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterSumDevice[iMode]), (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasExternalDeviceAllocator()); GPULog("gpu-transfer: creating vertexer tracklets per ROF for {} elements for mode {}, for {:.2f} MB.", this->mNrof + 1, iMode, (this->mNrof + 1) * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerROFDevice[iMode]), (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mNTrackletsPerROFDevice[iMode]), (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasExternalDeviceAllocator()); } GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[iMode], 0, ncls * sizeof(int32_t), mGpuStreams[iMode].get())); GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterSumDevice[iMode], 0, (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); @@ -477,13 +489,13 @@ void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) mGpuStreams[0].sync(); mGpuStreams[1].sync(); if (!iteration) { - allocMem(reinterpret_cast(&mNTrackletsPerClusterDeviceArray), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mNTrackletsPerClusterDeviceArray), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterDeviceArray, mNTrackletsPerClusterDevice.data(), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - allocMem(reinterpret_cast(&mNTrackletsPerClusterSumDeviceArray), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mNTrackletsPerClusterSumDeviceArray), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterSumDeviceArray, mNTrackletsPerClusterSumDevice.data(), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaMemcpy(mNTrackletsPerROFDeviceArray, mNTrackletsPerROFDevice.data(), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); } } @@ -496,11 +508,11 @@ void TimeFrameGPU::createVtxTrackletsBuffers(const int32_t iteration) this->mTotalTracklets[iMode] = 0; GPUChkErrS(cudaMemcpyAsync(&(this->mTotalTracklets[iMode]), mNTrackletsPerClusterSumDevice[iMode] + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost, mGpuStreams[iMode].get())); GPULog("gpu-transfer: creating vertexer tracklets buffer for {} elements on layer {}, for {:.2f} MB.", this->mTotalTracklets[iMode], iMode, this->mTotalTracklets[iMode] * sizeof(Tracklet) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[iMode]), this->mTotalTracklets[iMode] * sizeof(Tracklet), mGpuStreams[iMode], this->getExtAllocator()); + allocMemAsync(reinterpret_cast(&mTrackletsDevice[iMode]), this->mTotalTracklets[iMode] * sizeof(Tracklet), mGpuStreams[iMode], this->hasExternalDeviceAllocator()); } mGpuStreams[0].sync(); mGpuStreams[1].sync(); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), 2 * sizeof(Tracklet*), this->getExtAllocator()); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), 2 * sizeof(Tracklet*), this->hasExternalDeviceAllocator()); GPUChkErrS(cudaHostRegister(mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpy(mTrackletsDeviceArray, mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaMemcpyHostToDevice)); } @@ -512,14 +524,14 @@ void TimeFrameGPU::createVtxLinesLUTDevice(const int32_t iteration) const int32_t ncls = this->mClusters[1].size(); GPULog("gpu-transfer: creating vertexer lines per cluster for {} elements , for {:.2f} MB.", ncls, ncls * sizeof(int32_t) / constants::MB); - allocMem(reinterpret_cast(&mNLinesPerClusterDevice), ncls * sizeof(int32_t), this->getExtAllocator()); + allocMem(reinterpret_cast(&mNLinesPerClusterDevice), ncls * sizeof(int32_t), this->hasExternalDeviceAllocator()); GPULog("gpu-transfer: creating vertexer lines per cluster sum for {} elements , for {:.2f} MB.", ncls + 1, (ncls + 1) * sizeof(int32_t) / constants::MB); - allocMem(reinterpret_cast(&mNLinesPerClusterSumDevice), (ncls + 1) * sizeof(int32_t), this->getExtAllocator()); + allocMem(reinterpret_cast(&mNLinesPerClusterSumDevice), (ncls + 1) * sizeof(int32_t), this->hasExternalDeviceAllocator()); const int32_t ntrkls = this->mTotalTracklets[0]; GPULog("gpu-transfer: creating vertexer used tracklets for {} elements , for {:.2f} MB.", ntrkls, ntrkls * sizeof(uint8_t) / constants::MB); - allocMem(reinterpret_cast(&mUsedTrackletsDevice), ntrkls * sizeof(uint8_t), this->getExtAllocator()); + allocMem(reinterpret_cast(&mUsedTrackletsDevice), ntrkls * sizeof(uint8_t), this->hasExternalDeviceAllocator()); } template @@ -530,7 +542,7 @@ void TimeFrameGPU::createVtxLinesBuffer(const int32_t iteration) GPUChkErrS(cudaMemcpy(&nlines, mNLinesPerClusterDevice + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost)); this->mTotalLines = nlines; GPULog("gpu-transfer: creating vertexer lines for {} elements , for {:.2f} MB.", nlines, nlines * sizeof(Line) / constants::MB); - allocMem(reinterpret_cast(&mLinesDevice), nlines * sizeof(Line), this->getExtAllocator()); + allocMem(reinterpret_cast(&mLinesDevice), nlines * sizeof(Line), this->hasExternalDeviceAllocator()); // reset used tracklets GPUChkErrS(cudaMemset(mUsedTrackletsDevice, 0, this->mTotalTracklets[0] * sizeof(uint8_t))); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 047d42d815e99..cca4283c9b77f 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -33,12 +33,14 @@ void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) mTimeFrameGPU->loadVertices(iteration); mTimeFrameGPU->loadIndexTableUtils(iteration); mTimeFrameGPU->loadMultiplicityCutMask(iteration); + // pinned on host mTimeFrameGPU->createUsedClustersDeviceArray(iteration); mTimeFrameGPU->createClustersDeviceArray(iteration); mTimeFrameGPU->createUnsortedClustersDeviceArray(iteration); mTimeFrameGPU->createClustersIndexTablesArray(iteration); mTimeFrameGPU->createTrackingFrameInfoDeviceArray(iteration); mTimeFrameGPU->createROFrameClustersDeviceArray(iteration); + // device array mTimeFrameGPU->createTrackletsLUTDeviceArray(iteration); mTimeFrameGPU->createTrackletsBuffersArray(iteration); mTimeFrameGPU->createCellsBuffersArray(iteration); @@ -106,7 +108,7 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getPositionResolutions(), this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), - mTimeFrameGPU->getExternalAllocator(), + mTimeFrameGPU->getExternalDeviceAllocator(), conf.nBlocksLayerTracklets[iteration], conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); @@ -144,7 +146,7 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getPositionResolutions(), this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), - mTimeFrameGPU->getExternalAllocator(), + mTimeFrameGPU->getExternalDeviceAllocator(), conf.nBlocksLayerTracklets[iteration], conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); @@ -195,7 +197,7 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, this->mTrkParams[iteration].NSigmaCut, - mTimeFrameGPU->getExternalAllocator(), + mTimeFrameGPU->getExternalDeviceAllocator(), conf.nBlocksLayerCells[iteration], conf.nThreadsLayerCells[iteration], mTimeFrameGPU->getStreams()); @@ -251,7 +253,7 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) currentLayerCellsNum, nextLayerCellsNum, 1e2, - mTimeFrameGPU->getExternalAllocator(), + mTimeFrameGPU->getExternalDeviceAllocator(), conf.nBlocksFindNeighbours[iteration], conf.nThreadsFindNeighbours[iteration], mTimeFrameGPU->getStream(iLayer)); @@ -279,7 +281,7 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) mTimeFrameGPU->getDeviceNeighbours(iLayer), mTimeFrameGPU->getArrayNNeighbours()[iLayer], mTimeFrameGPU->getStream(iLayer), - mTimeFrameGPU->getExternalAllocator()); + mTimeFrameGPU->getExternalDeviceAllocator()); } mTimeFrameGPU->syncStreams(false); } @@ -310,7 +312,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].MaxChi2NDF, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getExternalAllocator(), + mTimeFrameGPU->getExternalDeviceAllocator(), conf.nBlocksProcessNeighbours[iteration], conf.nThreadsProcessNeighbours[iteration]); } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h index ac9f72089602d..66634c1a07eea 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h @@ -22,6 +22,8 @@ #include #include +#include "ITStracking/ExternalAllocator.h" + #include "GPUCommonLogger.h" namespace o2::its @@ -56,6 +58,7 @@ class BoundedMemoryResource final : public std::pmr::memory_resource BoundedMemoryResource(size_t maxBytes = std::numeric_limits::max(), std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) : mMaxMemory(maxBytes), mUpstream(upstream) {} + BoundedMemoryResource(ExternalAllocator* alloc) : mAdaptor(std::make_unique(alloc)), mUpstream(mAdaptor.get()) {} void* do_allocate(size_t bytes, size_t alignment) final { @@ -69,7 +72,14 @@ class BoundedMemoryResource final : public std::pmr::memory_resource } while (!mUsedMemory.compare_exchange_weak(current_used, new_used, std::memory_order_acq_rel, std::memory_order_relaxed)); - return mUpstream->allocate(bytes, alignment); + void* p{nullptr}; + try { + p = mUpstream->allocate(bytes, alignment); + } catch (...) { + mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); + throw; + } + return p; } void do_deallocate(void* p, size_t bytes, size_t alignment) final @@ -87,11 +97,12 @@ class BoundedMemoryResource final : public std::pmr::memory_resource size_t getMaxMemory() const noexcept { return mMaxMemory; } void setMaxMemory(size_t max) { - if (mUsedMemory > max) { + size_t used = mUsedMemory.load(std::memory_order_acquire); + if (used > max) { ++mCountThrow; - throw MemoryLimitExceeded(0, mUsedMemory, max); + throw MemoryLimitExceeded(0, used, max); } - mMaxMemory = max; + mMaxMemory.store(max, std::memory_order_release); } void print() const @@ -106,76 +117,74 @@ class BoundedMemoryResource final : public std::pmr::memory_resource } private: - size_t mMaxMemory{std::numeric_limits::max()}; + std::atomic mMaxMemory{std::numeric_limits::max()}; std::atomic mCountThrow{0}; std::atomic mUsedMemory{0}; - std::pmr::memory_resource* mUpstream; + std::unique_ptr mAdaptor{nullptr}; + std::pmr::memory_resource* mUpstream{nullptr}; }; template using bounded_vector = std::pmr::vector; template -void deepVectorClear(std::vector& vec) +inline void deepVectorClear(std::vector& vec) { std::vector().swap(vec); } template -inline void deepVectorClear(bounded_vector& vec, BoundedMemoryResource* bmr = nullptr) +inline void deepVectorClear(bounded_vector& vec, std::pmr::memory_resource* mr = nullptr) { + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); vec.~bounded_vector(); - if (bmr == nullptr) { - auto alloc = vec.get_allocator().resource(); - new (&vec) bounded_vector(alloc); - } else { - new (&vec) bounded_vector(bmr); - } + new (&vec) bounded_vector(std::pmr::polymorphic_allocator{tmr}); } template -void deepVectorClear(std::vector>& vec, BoundedMemoryResource* bmr = nullptr) +inline void deepVectorClear(std::vector>& vec, std::pmr::memory_resource* mr = nullptr) { for (auto& v : vec) { - deepVectorClear(v, bmr); + deepVectorClear(v, mr); } } template -void deepVectorClear(std::array, S>& arr, BoundedMemoryResource* bmr = nullptr) +inline void deepVectorClear(std::array, S>& arr, std::pmr::memory_resource* mr = nullptr) { for (size_t i{0}; i < S; ++i) { - deepVectorClear(arr[i], bmr); + deepVectorClear(arr[i], mr); } } template -void clearResizeBoundedVector(bounded_vector& vec, size_t size, BoundedMemoryResource* bmr, T def = T()) +inline void clearResizeBoundedVector(bounded_vector& vec, size_t sz, std::pmr::memory_resource* mr = nullptr, T def = T()) { + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); vec.~bounded_vector(); - new (&vec) bounded_vector(size, def, bmr); + new (&vec) bounded_vector(sz, def, std::pmr::polymorphic_allocator{tmr}); } template -void clearResizeBoundedVector(std::vector>& vec, size_t size, BoundedMemoryResource* bmr) +inline void clearResizeBoundedVector(std::vector>& vec, size_t size, std::pmr::memory_resource* mr) { vec.clear(); vec.reserve(size); - for (size_t i{0}; i < size; ++i) { - vec.emplace_back(bmr); + for (size_t i = 0; i < size; ++i) { + vec.emplace_back(std::pmr::polymorphic_allocator>{mr}); } } template -void clearResizeBoundedArray(std::array, S>& arr, size_t size, BoundedMemoryResource* bmr, T def = T()) +inline void clearResizeBoundedArray(std::array, S>& arr, size_t size, std::pmr::memory_resource* mr = nullptr, T def = T()) { for (size_t i{0}; i < S; ++i) { - clearResizeBoundedVector(arr[i], size, bmr, def); + clearResizeBoundedVector(arr[i], size, mr, def); } } template -std::vector toSTDVector(const bounded_vector& b) +inline std::vector toSTDVector(const bounded_vector& b) { std::vector t(b.size()); std::copy(b.cbegin(), b.cend(), t.begin()); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h index dd96dc80f2926..b96f0558943a6 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -16,8 +16,9 @@ #ifndef TRACKINGITSU_INCLUDE_CACLUSTER_H_ #define TRACKINGITSU_INCLUDE_CACLUSTER_H_ +#include +#include "ITStracking/Constants.h" #include "GPUCommonRtypes.h" -#include "GPUCommonArray.h" namespace o2::its { @@ -47,8 +48,8 @@ struct Cluster final { float zCoordinate{-999.f}; float phi{-999.f}; float radius{-999.f}; - int clusterId{-1}; - int indexTableBinIndex{-1}; + int clusterId{constants::UnusedIndex}; + int indexTableBinIndex{constants::UnusedIndex}; ClassDefNV(Cluster, 1); }; @@ -70,7 +71,7 @@ struct TrackingFrameInfo final { float zCoordinate{-999.f}; float xTrackingFrame{-999.f}; float alphaTrackingFrame{-999.f}; - std::array positionTrackingFrame = {-1., -1.}; + std::array positionTrackingFrame = {constants::UnusedIndex, constants::UnusedIndex}; std::array covarianceTrackingFrame = {999., 999., 999.}; ClassDefNV(TrackingFrameInfo, 1); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h index 1628bbc52776b..36e78ef24020c 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h @@ -16,6 +16,8 @@ #ifndef TRACKINGITSU_INCLUDE_EXTERNALALLOCATOR_H_ #define TRACKINGITSU_INCLUDE_EXTERNALALLOCATOR_H_ +#include + namespace o2::its { @@ -25,6 +27,36 @@ class ExternalAllocator virtual void* allocate(size_t) = 0; virtual void deallocate(char*, size_t) = 0; }; + +class ExternalAllocatorAdaptor final : public std::pmr::memory_resource +{ + public: + explicit ExternalAllocatorAdaptor(ExternalAllocator* alloc) : mAlloc(alloc) {} + + protected: + void* do_allocate(size_t bytes, size_t alignment) override + { + void* p = mAlloc->allocate(bytes); + if (!p) { + throw std::bad_alloc(); + } + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t) override + { + mAlloc->deallocate(static_cast(p), bytes); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override + { + return this == &other; + } + + private: + ExternalAllocator* mAlloc; +}; + } // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index c34701ce222e2..436ce25336ca7 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -69,8 +69,8 @@ struct TimeFrame { using CellSeedN = CellSeed; friend class gpu::TimeFrameGPU; - TimeFrame(); - virtual ~TimeFrame(); + TimeFrame() = default; + virtual ~TimeFrame() = default; const Vertex& getPrimaryVertex(const int ivtx) const { return mPrimaryVertices[ivtx]; } gsl::span getPrimaryVertices(int rofId) const; @@ -95,7 +95,7 @@ struct TimeFrame { gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels = nullptr); - void resetROFrameData(); + void resetROFrameData(size_t nROFs); int getTotalClusters() const; auto& getTotVertIteration() { return mTotVertPerIteration; } @@ -188,7 +188,7 @@ struct TimeFrame { auto getNumberOfUsedExtendedClusters() const { return mNExtendedUsedClusters; } /// memory management - void setMemoryPool(std::shared_ptr& pool); + void setMemoryPool(std::shared_ptr pool); auto& getMemoryPool() const noexcept { return mMemoryPool; } bool checkMemory(unsigned long max) { return getArtefactsMemory() < max; } unsigned long getArtefactsMemory() const; @@ -233,23 +233,26 @@ struct TimeFrame { void setBz(float bz) { mBz = bz; } float getBz() const { return mBz; } - void setExternalAllocator(ExternalAllocator* allocator) + /// State if memory will be externally managed. + // device + ExternalAllocator* mExtDeviceAllocator{nullptr}; + void setExternalDeviceAllocator(ExternalAllocator* allocator) { mExtDeviceAllocator = allocator; } + ExternalAllocator* getExternalDeviceAllocator() { return mExtDeviceAllocator; } + bool hasExternalDeviceAllocator() const noexcept { return mExtDeviceAllocator != nullptr; } + // host + ExternalAllocator* mExtHostAllocator{nullptr}; + void setExternalHostAllocator(ExternalAllocator* allocator) { - if (isGPU()) { - LOGP(debug, "Setting timeFrame allocator to external"); - mAllocator = allocator; - } else { - LOGP(fatal, "External allocator is currently only supported for GPU"); - } + mExtHostAllocator = allocator; + mExtMemoryPool = std::make_shared(mExtHostAllocator); } - - ExternalAllocator* getExternalAllocator() { return mAllocator; } - - virtual void setDevicePropagator(const o2::base::PropagatorImpl*) - { - return; - }; + ExternalAllocator* getExternalHostAllocator() { return mExtHostAllocator; } + bool hasExternalHostAllocator() const noexcept { return mExtHostAllocator != nullptr; } + std::shared_ptr mExtMemoryPool; + std::pmr::memory_resource* getMaybeExternalHostResource(bool forceHost = false) { return (hasExternalHostAllocator() && !forceHost) ? mExtMemoryPool.get() : mMemoryPool.get(); } + // Propagator const o2::base::PropagatorImpl* getDevicePropagator() const { return mPropagatorDevice; } + virtual void setDevicePropagator(const o2::base::PropagatorImpl*) {}; template void addClusterToLayer(int layer, T&&... args); @@ -257,9 +260,6 @@ struct TimeFrame { void addTrackingFrameInfoToLayer(int layer, T&&... args); void addClusterExternalIndexToLayer(int layer, const int idx) { mClusterExternalIndices[layer].push_back(idx); } - void resetVectors(); - void resetTracklets(); - /// Debug and printing void checkTrackletLUTs(); void printROFoffsets(); @@ -290,10 +290,6 @@ struct TimeFrame { bounded_vector mROFramesPV; bounded_vector mPrimaryVertices; - // State if memory will be externally managed. - ExternalAllocator* mAllocator = nullptr; - bool getExtAllocator() const noexcept { return mAllocator != nullptr; } - std::array, nLayers> mUnsortedClusters; std::vector> mTracklets; std::vector> mCells; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 642717bd09596..4c903ed1f3ca1 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -66,7 +66,7 @@ class Tracker const LogFunc& = [](const std::string& s) { std::cerr << s << '\n'; }); void setParameters(const std::vector& p) { mTrkParams = p; } - void setMemoryPool(std::shared_ptr& pool) { mMemoryPool = pool; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector& getParameters() { return mTrkParams; } void setBz(float bz) { mTraits->setBz(bz); } bool isMatLUT() const { return mTraits->isMatLUT(); } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 9d14bb91635a0..ee64cacb8fa2a 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -70,7 +70,7 @@ class TrackerTraits bool isMatLUT() const; virtual const char* getName() const noexcept { return "CPU"; } virtual bool isGPU() const noexcept { return false; } - void setMemoryPool(std::shared_ptr& pool) noexcept { mMemoryPool = pool; } + void setMemoryPool(std::shared_ptr pool) noexcept { mMemoryPool = pool; } auto getMemoryPool() const noexcept { return mMemoryPool; } // Others diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index ab92e7c1a1523..d66bcd6ee2358 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -52,7 +52,7 @@ class Vertexer auto& getVertParameters() const { return mTraits->getVertexingParameters(); } void setParameters(const std::vector& vertParams) { mVertParams = vertParams; } const auto& getParameters() const noexcept { return mVertParams; } - void setMemoryPool(std::shared_ptr& pool) { mMemoryPool = pool; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector exportVertices(); VertexerTraitsN* getTraits() const { return mTraits; }; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index dda32ddfd5aec..b1422d66e12df 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -82,7 +82,7 @@ class VertexerTraits virtual bool isGPU() const noexcept { return false; } virtual const char* getName() const noexcept { return "CPU"; } virtual bool usesMemoryPool() const noexcept { return true; } - void setMemoryPool(std::shared_ptr& pool) { mMemoryPool = pool; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } static std::pair computeMain(const bounded_vector& elements) { diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 510c66e2420f1..4ea0bedaced5f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -46,18 +46,6 @@ constexpr float DefClusErrorCol = o2::itsmft::SegmentationAlpide::PitchCol * 0.5 constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; -template -TimeFrame::TimeFrame() -{ - resetVectors(); -} - -template -TimeFrame::~TimeFrame() -{ - wipe(); -} - template void TimeFrame::addPrimaryVertices(const bounded_vector& vertices, const int iteration) { @@ -121,14 +109,25 @@ int TimeFrame::loadROFrameData(gsl::span rofs, const itsmft::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels) { - resetROFrameData(); + resetROFrameData(rofs.size()); GeometryTGeo* geom = GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - mNrof = 0; + mNrof = rofs.size(); clearResizeBoundedVector(mClusterSize, clusters.size(), mMemoryPool.get()); - for (auto& rof : rofs) { + std::array clusterCountPerLayer{}; + for (const auto& clus : clusters) { + ++clusterCountPerLayer[geom->getLayer(clus.getSensorID())]; + } + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } + + for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { + const auto& rof = rofs[iRof]; for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { const auto& c = clusters[clusterId]; @@ -164,15 +163,13 @@ int TimeFrame::loadROFrameData(gsl::span rofs, addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), std::array{trkXYZ.y(), trkXYZ.z()}, std::array{sigmaY2, sigmaYZ, sigmaZ2}); - /// Rotate to the global frame addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), mUnsortedClusters[layer].size()); addClusterExternalIndexToLayer(layer, clusterId); } for (unsigned int iL{0}; iL < mUnsortedClusters.size(); ++iL) { - mROFramesClusters[iL].push_back(mUnsortedClusters[iL].size()); + mROFramesClusters[iL][iRof + 1] = mUnsortedClusters[iL].size(); // effectively calculating and exclusive sum } - mNrof++; } for (auto i = 0; i < mNTrackletsPerCluster.size(); ++i) { @@ -185,16 +182,16 @@ int TimeFrame::loadROFrameData(gsl::span rofs, } return mNrof; -} +} // namespace o2::its template -void TimeFrame::resetROFrameData() +void TimeFrame::resetROFrameData(size_t nRofs) { for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - deepVectorClear(mUnsortedClusters[iLayer], mMemoryPool.get()); - deepVectorClear(mTrackingFrameInfo[iLayer], mMemoryPool.get()); + deepVectorClear(mUnsortedClusters[iLayer], getMaybeExternalHostResource()); + deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeExternalHostResource()); + clearResizeBoundedVector(mROFramesClusters[iLayer], nRofs + 1, getMaybeExternalHostResource()); deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); - clearResizeBoundedVector(mROFramesClusters[iLayer], 1, mMemoryPool.get(), 0); if (iLayer < 2) { deepVectorClear(mTrackletsIndexROF[iLayer], mMemoryPool.get()); @@ -298,11 +295,11 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter clearResizeBoundedVector(mBogusClusters, trkParam.NLayers, mMemoryPool.get()); deepVectorClear(mTrackletClusters); for (unsigned int iLayer{0}; iLayer < std::min((int)mClusters.size(), maxLayers); ++iLayer) { - clearResizeBoundedVector(mClusters[iLayer], mUnsortedClusters[iLayer].size(), mMemoryPool.get()); - clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), mMemoryPool.get()); + clearResizeBoundedVector(mClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeExternalHostResource(maxLayers != nLayers)); + clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeExternalHostResource(maxLayers != nLayers)); mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt(0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); } - clearResizeBoundedArray(mIndexTables, mNrof * (trkParam.ZBins * trkParam.PhiBins + 1), mMemoryPool.get()); + clearResizeBoundedArray(mIndexTables, mNrof * (trkParam.ZBins * trkParam.PhiBins + 1), getMaybeExternalHostResource(maxLayers != nLayers)); clearResizeBoundedVector(mLines, mNrof, mMemoryPool.get()); clearResizeBoundedVector(mTrackletClusters, mNrof, mMemoryPool.get()); @@ -315,6 +312,8 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } } + mMinR.fill(10000.); + mMaxR.fill(-1.); } mNTrackletsPerROF.resize(2); for (auto& v : mNTrackletsPerROF) { @@ -453,34 +452,6 @@ void TimeFrame::checkTrackletLUTs() } } -template -void TimeFrame::resetVectors() -{ - mMinR.fill(10000.); - mMaxR.fill(-1.); - for (int iLayers{nLayers}; iLayers--;) { - mClusters[iLayers].clear(); - mUnsortedClusters[iLayers].clear(); - mTrackingFrameInfo[iLayers].clear(); - mClusterExternalIndices[iLayers].clear(); - mUsedClusters[iLayers].clear(); - mROFramesClusters[iLayers].clear(); - mNClustersPerROF[iLayers].clear(); - } - for (int i{2}; i--;) { - mTrackletsIndexROF[i].clear(); - } -} - -template -void TimeFrame::resetTracklets() -{ - for (auto& trkl : mTracklets) { - deepVectorClear(trkl); - } - deepVectorClear(mTrackletsLookupTable); -} - template void TimeFrame::printTrackletLUTonLayer(int i) { @@ -575,75 +546,67 @@ void TimeFrame::printSliceInfo(const int startROF, const int sliceSize) LOG(info) << "Number of seeding vertices: " << getPrimaryVertices(iROF).size(); int iVertex{0}; for (auto& v : getPrimaryVertices(iROF)) { - LOG(info) << "\t vertex " << iVertex++ << ": x=" << v.getX() << " " << " y=" << v.getY() << " z=" << v.getZ() << " has " << v.getNContributors() << " contributors."; + LOG(info) << "\t vertex " << iVertex++ << ": x=" << v.getX() << " " + << " y=" << v.getY() << " z=" << v.getZ() << " has " << v.getNContributors() << " contributors."; } } } template -void TimeFrame::setMemoryPool(std::shared_ptr& pool) +void TimeFrame::setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; - auto initVector = [&](bounded_vector& vec) { - auto alloc = vec.get_allocator().resource(); - if (alloc != mMemoryPool.get()) { - vec = bounded_vector(mMemoryPool.get()); - } - }; - auto initArrays = [&](std::array, S>& arr) { - for (size_t i{0}; i < S; ++i) { - auto alloc = arr[i].get_allocator().resource(); - if (alloc != mMemoryPool.get()) { - arr[i] = bounded_vector(mMemoryPool.get()); - } - } + auto initVector = [&](bounded_vector& vec, bool useExternal = false) { + std::pmr::memory_resource* mr = (useExternal) ? mExtMemoryPool.get() : mMemoryPool.get(); + deepVectorClear(vec, mr); }; - auto initVectors = [&](std::vector>& vec) { - for (size_t i{0}; i < vec.size(); ++i) { - auto alloc = vec[i].get_allocator().resource(); - if (alloc != mMemoryPool.get()) { - vec[i] = bounded_vector(mMemoryPool.get()); - } + + auto initContainers = [&](Container& container, bool useExternal = false) { + for (auto& v : container) { + initVector(v, useExternal); } }; - + // these will only reside on the host for the cpu part initVector(mTotVertPerIteration); - initVector(mPrimaryVertices); - initVector(mROFramesPV); - initArrays(mClusters); - initArrays(mTrackingFrameInfo); - initArrays(mClusterExternalIndices); - initArrays(mROFramesClusters); - initArrays(mNTrackletsPerCluster); - initArrays(mNTrackletsPerClusterSum); - initArrays(mNClustersPerROF); - initArrays(mIndexTables); - initArrays(mUsedClusters); - initArrays(mUnsortedClusters); + initContainers(mClusterExternalIndices); + initContainers(mNTrackletsPerCluster); + initContainers(mNTrackletsPerClusterSum); + initContainers(mNClustersPerROF); initVector(mROFramesPV); initVector(mPrimaryVertices); initVector(mRoads); - initVector(mRoadLabels); initVector(mMSangles); initVector(mPhiCuts); initVector(mPositionResolution); initVector(mClusterSize); initVector(mPValphaX); initVector(mBogusClusters); + initContainers(mTrackletsIndexROF); + initContainers(mTracks); + initContainers(mTracklets); + initContainers(mCells); + initContainers(mCellsNeighbours); + initContainers(mCellsLookupTable); + // MC info (we don't know if we have MC) initVector(mVerticesContributorLabels); - initArrays(mTrackletsIndexROF); - initVectors(mTracks); - initVectors(mTracklets); - initVectors(mCells); - initVectors(mCellsNeighbours); - initVectors(mCellsLookupTable); + initContainers(mLinesLabels); + initContainers(mTrackletLabels); + initContainers(mCellLabels); + initVector(mRoadLabels); + initContainers(mTracksLabel); + // these will use possibly an externally provided allocator + initContainers(mClusters, hasExternalHostAllocator()); + initContainers(mUsedClusters, hasExternalHostAllocator()); + initContainers(mUnsortedClusters, hasExternalHostAllocator()); + initContainers(mIndexTables, hasExternalHostAllocator()); + initContainers(mTrackingFrameInfo, hasExternalHostAllocator()); + initContainers(mROFramesClusters, hasExternalHostAllocator()); } template void TimeFrame::wipe() { - deepVectorClear(mUnsortedClusters); deepVectorClear(mTracks); deepVectorClear(mTracklets); deepVectorClear(mCells); @@ -652,20 +615,12 @@ void TimeFrame::wipe() deepVectorClear(mCellsLookupTable); deepVectorClear(mTotVertPerIteration); deepVectorClear(mPrimaryVertices); - deepVectorClear(mClusters); deepVectorClear(mTrackletsLookupTable); - deepVectorClear(mTrackingFrameInfo); deepVectorClear(mClusterExternalIndices); - deepVectorClear(mROFramesClusters); deepVectorClear(mNTrackletsPerCluster); deepVectorClear(mNTrackletsPerClusterSum); deepVectorClear(mNClustersPerROF); - deepVectorClear(mIndexTables); - deepVectorClear(mUsedClusters); - deepVectorClear(mUnsortedClusters); deepVectorClear(mROFramesPV); - deepVectorClear(mPrimaryVertices); - deepVectorClear(mRoads); deepVectorClear(mMSangles); deepVectorClear(mPhiCuts); deepVectorClear(mPositionResolution); @@ -673,9 +628,19 @@ void TimeFrame::wipe() deepVectorClear(mPValphaX); deepVectorClear(mBogusClusters); deepVectorClear(mTrackletsIndexROF); - deepVectorClear(mPrimaryVertices); deepVectorClear(mTrackletClusters); deepVectorClear(mLines); + // if we use the external host allocator then the assumption is that we + // don't clear the memory ourself + if (!hasExternalHostAllocator()) { + deepVectorClear(mClusters); + deepVectorClear(mUsedClusters); + deepVectorClear(mUnsortedClusters); + deepVectorClear(mIndexTables); + deepVectorClear(mTrackingFrameInfo); + deepVectorClear(mROFramesClusters); + } + // only needed to clear if we have MC info if (hasMCinformation()) { deepVectorClear(mLinesLabels); deepVectorClear(mVerticesContributorLabels); diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index 69dddbf367653..c4b1fb427513f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -54,7 +54,6 @@ float Vertexer::clustersToVertices(LogFunc logger) throw err; } else { LOGP(error, "Dropping this TF!"); - mTimeFrame->resetTracklets(); } }; diff --git a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt new file mode 100644 index 0000000000000..818ad1d667371 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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. + +o2_add_test(boundedmemoryresource + SOURCES testBoundedMemoryResource.cxx + COMPONENT_NAME its-tracking + LABELS "its;tracking" + PUBLIC_LINK_LIBRARIES O2::ITStracking) diff --git a/Detectors/ITSMFT/ITS/tracking/test/testBoundedMemoryResource.cxx b/Detectors/ITSMFT/ITS/tracking/test/testBoundedMemoryResource.cxx new file mode 100644 index 0000000000000..aae28f5cbc36e --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/test/testBoundedMemoryResource.cxx @@ -0,0 +1,190 @@ +// 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. + +#define BOOST_TEST_MODULE Test Flags +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include "ITStracking/BoundedAllocator.h" + +using namespace o2::its; +using Vec = bounded_vector; +auto getRandomInt(int min = -100, int max = 100) +{ + static std::mt19937 gen(std::random_device{}()); // static generator, seeded once + std::uniform_int_distribution<> dist(min, max); + return [&, dist]() mutable { + return dist(gen); + }; +} + +// -------- Throwing upstream resource for testing rollback -------- +class ThrowingResource final : public std::pmr::memory_resource +{ + protected: + void* do_allocate(size_t, size_t) final + { + throw std::bad_alloc(); // always fail + } + void do_deallocate(void*, size_t, size_t) noexcept final + { + // nothing + } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final + { + return this == &other; + } +}; + +// -------- Upstream resource with empty deallocate -------- +class NoDeallocateResource final : public std::pmr::memory_resource +{ + public: + NoDeallocateResource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) + : mUpstream(upstream) {} + + protected: + void* do_allocate(size_t bytes, size_t alignment) final + { + return mUpstream->allocate(bytes, alignment); + } + void do_deallocate(void*, size_t, size_t) noexcept final + { + // nothing + } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final + { + return this == &other; + } + + private: + std::pmr::memory_resource* mUpstream; +}; + +// -------- Tests -------- +BOOST_AUTO_TEST_CASE(allocation_and_clear_updates_used_memory) +{ + BoundedMemoryResource bmr(10 * 1024 * 1024); // 10 MB cap + + Vec v(std::pmr::polymorphic_allocator{&bmr}); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); + + const size_t count = 128; + v.reserve(count); + const size_t expected = count * sizeof(int); + BOOST_CHECK_GE(bmr.getUsedMemory(), expected); + BOOST_CHECK_LE(bmr.getUsedMemory(), expected + 64); + + deepVectorClear(v, &bmr); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(clearResizeBoundedVector_resizes_and_tracks_memory) +{ + BoundedMemoryResource bmr(1024 * 1024); // 1 MB cap + + Vec v(std::pmr::polymorphic_allocator{&bmr}); + v.reserve(200); + const size_t used_before = bmr.getUsedMemory(); + BOOST_CHECK_GT(used_before, 0u); + + clearResizeBoundedVector(v, 50, &bmr, 7); + const size_t used_after = bmr.getUsedMemory(); + BOOST_CHECK_GE(used_after, 50 * sizeof(int)); + BOOST_CHECK_LT(used_after, used_before); + + clearResizeBoundedVector(v, 300, &bmr, 3); + BOOST_CHECK_GE(bmr.getUsedMemory(), 300 * sizeof(int)); +} + +BOOST_AUTO_TEST_CASE(upstream_throw_rolls_back_reservation) +{ + ThrowingResource upstream; + BoundedMemoryResource bmr(std::numeric_limits::max(), &upstream); + const size_t bytes = 1024; + bool threw = false; + void* p{nullptr}; + try { + p = bmr.allocate(bytes, alignof(std::max_align_t)); + } catch (const std::bad_alloc&) { + threw = true; + } + BOOST_CHECK(threw); + BOOST_CHECK_EQUAL(p, nullptr); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(vector_of_bounded_vectors_deep_clear_releases_all) +{ + BoundedMemoryResource bmr(10 * 1024 * 1024); // 10 MB + std::vector outer; + outer.reserve(5); + for (int i = 0; i < 5; ++i) { + outer.emplace_back(std::pmr::polymorphic_allocator{&bmr}); + outer.back().reserve(100); + } + BOOST_CHECK_GT(bmr.getUsedMemory(), 0u); + deepVectorClear(outer, &bmr); // deep clear outer + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(array_of_bounded_vectors_clear_resize_works) +{ + BoundedMemoryResource bmr(10 * 1024 * 1024); + std::array arr{{Vec(std::pmr::polymorphic_allocator{&bmr}), + Vec(std::pmr::polymorphic_allocator{&bmr}), + Vec(std::pmr::polymorphic_allocator{&bmr})}}; + clearResizeBoundedVector(arr[0], 10, &bmr, 1); + clearResizeBoundedVector(arr[1], 20, &bmr, 2); + clearResizeBoundedVector(arr[2], 30, &bmr, 3); + BOOST_CHECK_GT(bmr.getUsedMemory(), 0u); + deepVectorClear(arr, &bmr); // now clear all recursively + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0u); +} + +BOOST_AUTO_TEST_CASE(deepVectorClear_releases_and_reuses_resource) +{ + // Use a small bounded memory resource + BoundedMemoryResource bmr(1024); + bounded_vector vec{std::pmr::polymorphic_allocator{&bmr}}; + vec.resize(100, 42); + BOOST_TEST(bmr.getUsedMemory() > 0); + deepVectorClear(vec, &bmr); + BOOST_TEST(vec.empty()); + BOOST_TEST(vec.get_allocator().resource() == &bmr); + auto usedAfter = bmr.getUsedMemory(); + BOOST_CHECK_EQUAL(bmr.getUsedMemory(), 0); + vec.push_back(7); + BOOST_TEST(vec.size() == 1); + BOOST_TEST(vec[0] == 7); + BOOST_TEST(vec.get_allocator().resource() == &bmr); +} + +BOOST_AUTO_TEST_CASE(clear_with_memory_resource_without_deallocator) +{ + NoDeallocateResource dmr; + Vec v(std::pmr::polymorphic_allocator{&dmr}); + + for (int shift{0}; shift < 12; ++shift) { + const int c{1 << shift}; + v.resize(100); + std::generate(v.begin(), v.end(), getRandomInt()); + // allocate different sizes, which is actually a no-op now + clearResizeBoundedVector(v, c / 2, &dmr, 999); + for (size_t i{0}; i < c / 2; ++i) { // now only the first c/2 elements should be set + BOOST_CHECK_EQUAL(v[i], 999); + } + // try to deepclear + deepVectorClear(v); + } +} diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index acba8022e376f..2fced813efc93 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -64,7 +64,7 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, const its3::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels) { - tf->resetROFrameData(); + tf->resetROFrameData(rofs.size()); auto geom = its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); diff --git a/GPU/GPUTracking/Global/GPUChainITS.cxx b/GPU/GPUTracking/Global/GPUChainITS.cxx index 26dff3710cd4a..a85cdb48c4d1c 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.cxx +++ b/GPU/GPUTracking/Global/GPUChainITS.cxx @@ -17,7 +17,6 @@ #include "DataFormatsITS/TrackITS.h" #include "ITStracking/ExternalAllocator.h" #include "GPUReconstructionIncludesITS.h" -#include using namespace o2::gpu; @@ -26,15 +25,18 @@ namespace o2::its class GPUFrameworkExternalAllocator final : public o2::its::ExternalAllocator { public: + GPUFrameworkExternalAllocator(GPUMemoryResource::MemoryType type) : mType(type) {} + void* allocate(size_t size) override { - return mFWReco->AllocateDirectMemory(size, GPUMemoryResource::MEMORY_GPU); + return mFWReco->AllocateDirectMemory(size, mType); } - void deallocate(char* ptr, size_t) override {} + void deallocate(char* ptr, size_t size) override {} void setReconstructionFramework(o2::gpu::GPUReconstruction* fwr) { mFWReco = fwr; } private: o2::gpu::GPUReconstruction* mFWReco; + GPUMemoryResource::MemoryType mType; }; } // namespace o2::its @@ -71,11 +73,12 @@ o2::its::TimeFrame<7>* GPUChainITS::GetITSTimeframe() } #if !defined(GPUCA_STANDALONE) if (mITSTimeFrame->isGPU()) { - auto doFWExtAlloc = [this](size_t size) -> void* { return rec()->AllocateDirectMemory(size, GPUMemoryResource::MEMORY_GPU); }; - - mFrameworkAllocator.reset(new o2::its::GPUFrameworkExternalAllocator); - mFrameworkAllocator->setReconstructionFramework(rec()); - mITSTimeFrame->setExternalAllocator(mFrameworkAllocator.get()); + mFrameworkDeviceAllocator.reset(new o2::its::GPUFrameworkExternalAllocator(GPUMemoryResource::MEMORY_GPU)); + mFrameworkDeviceAllocator->setReconstructionFramework(rec()); + mITSTimeFrame->setExternalDeviceAllocator(mFrameworkDeviceAllocator.get()); + mFrameworkHostAllocator.reset(new o2::its::GPUFrameworkExternalAllocator(GPUMemoryResource::MEMORY_HOST)); + mFrameworkHostAllocator->setReconstructionFramework(rec()); + mITSTimeFrame->setExternalHostAllocator(mFrameworkHostAllocator.get()); } #endif return mITSTimeFrame.get(); diff --git a/GPU/GPUTracking/Global/GPUChainITS.h b/GPU/GPUTracking/Global/GPUChainITS.h index a607f66322bab..ab693bcef3f8b 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.h +++ b/GPU/GPUTracking/Global/GPUChainITS.h @@ -53,7 +53,8 @@ class GPUChainITS final : public GPUChain std::unique_ptr> mITSTrackerTraits; std::unique_ptr> mITSVertexerTraits; std::unique_ptr> mITSTimeFrame; - std::unique_ptr mFrameworkAllocator; + std::unique_ptr mFrameworkDeviceAllocator; + std::unique_ptr mFrameworkHostAllocator; }; } // namespace o2::gpu From 75b18631b5669c9fea9a34066520f2f272da9b5d Mon Sep 17 00:00:00 2001 From: Francesco Mazzaschi <43742195+fmazzasc@users.noreply.github.com> Date: Thu, 25 Sep 2025 16:43:42 +0200 Subject: [PATCH 10/99] Add ITS fake clusters information to the mcMask (#14666) * Add ITS fake clusters information to the mcMask * remove redundant check --------- Co-authored-by: Francesco Mazzaschi --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 17 ++++++++++++++++- .../Core/include/Framework/AnalysisDataModel.h | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 8247eb3d870c0..90cf420bc9bf6 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -1126,7 +1126,7 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTr if (!needToStore(mGIDToTableID)) { continue; } - if (mcTruth.isValid()) { // if not set, -1 will be stored + if (mcTruth.isValid()) { // if not set, -1 will be stored labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; // defined by TPC if it contributes, otherwise: by ITS if (mcTruth.isFake()) { labelHolder.labelMask |= (0x1 << 15); @@ -1139,6 +1139,21 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTr } } } + if (trackIndex.includesDet(DetID::ITS)) { + auto itsGID = data.getITSContributorGID(trackIndex); + auto itsSource = itsGID.getSource(); + if (itsSource == GIndex::ITS) { + auto& itsTrack = data.getITSTrack(itsGID); + for (unsigned int iL = 0; iL < 7; ++iL) { + if (itsTrack.isFakeOnLayer(iL)) { + labelHolder.labelMask |= (0x1 << iL); + } + } + } else if (itsSource == GIndex::ITSAB) { + labelHolder.labelMask |= (data.getTrackMCLabel(itsGID).isFake() << 12); + } + } + } else if (mcTruth.isNoise()) { labelHolder.labelMask |= (0x1 << 14); } diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index 2a9e1b61ee6df..b174f3858e165 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -2017,7 +2017,7 @@ namespace aod namespace mctracklabel { DECLARE_SOA_INDEX_COLUMN(McParticle, mcParticle); //! MC particle -DECLARE_SOA_COLUMN(McMask, mcMask, uint16_t); //! Bit mask to indicate detector mismatches (bit ON means mismatch). Bit 0-6: mismatch at ITS layer. Bit 7-9: # of TPC mismatches in the ranges 0, 1, 2-3, 4-7, 8-15, 16-31, 32-63, >64. Bit 10: TRD, bit 11: TOF, bit 15: indicates negative label +DECLARE_SOA_COLUMN(McMask, mcMask, uint16_t); //! Bit mask to indicate detector mismatches (bit ON means mismatch). Bit 0-6: mismatch at ITS layer. Bit 12: ITSAB tracklet mismatch. Bit 13: ITS-TPC mismatch. Bit 14: isNoise == True (global track), Bit 15: isFake == True (global track) } // namespace mctracklabel DECLARE_SOA_TABLE(McTrackLabels, "AOD", "MCTRACKLABEL", //! Table joined to the track table containing the MC index From 2b4ac436d29c5c6fb065c0496f855865a11442a6 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 12 Sep 2025 07:42:57 +0200 Subject: [PATCH 11/99] Common: allow literal suffix and add tests for confkey Signed-off-by: Felix Schlepper --- Common/Utils/CMakeLists.txt | 15 ++ .../CommonUtils/ConfigurableParamTest.h | 45 ++++++ Common/Utils/src/CommonUtilsTestLinkDef.h | 21 +++ Common/Utils/src/ConfigurableParam.cxx | 62 +++++++- Common/Utils/src/ConfigurableParamTest.cxx | 13 ++ Common/Utils/test/testConfigurableParam.cxx | 145 ++++++++++++++++++ 6 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 Common/Utils/include/CommonUtils/ConfigurableParamTest.h create mode 100644 Common/Utils/src/CommonUtilsTestLinkDef.h create mode 100644 Common/Utils/src/ConfigurableParamTest.cxx create mode 100644 Common/Utils/test/testConfigurableParam.cxx diff --git a/Common/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index d9954e23ab435..849a3d70f62e1 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -51,6 +51,15 @@ o2_target_root_dictionary(CommonUtils include/CommonUtils/IRFrameSelector.h include/CommonUtils/DebugStreamer.h) +# Extra dictionaries only needed if tests are built +if(BUILD_TESTING) + o2_add_library(CommonUtilsTest + SOURCES src/ConfigurableParamTest.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils) + o2_target_root_dictionary(CommonUtilsTest + HEADERS include/CommonUtils/ConfigurableParamTest.h) +endif() + o2_add_test(TreeStream COMPONENT_NAME CommonUtils LABELS utils @@ -87,6 +96,12 @@ o2_add_test(EnumFlags SOURCES test/testEnumFlags.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils) +o2_add_test(ConfigurableParam + COMPONENT_NAME CommonUtils + LABELS utils + SOURCES test/testConfigurableParam.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtilsTest) + o2_add_executable(treemergertool COMPONENT_NAME CommonUtils SOURCES src/TreeMergerTool.cxx diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamTest.h b/Common/Utils/include/CommonUtils/ConfigurableParamTest.h new file mode 100644 index 0000000000000..547bbf9ba8c38 --- /dev/null +++ b/Common/Utils/include/CommonUtils/ConfigurableParamTest.h @@ -0,0 +1,45 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef COMMON_CONFIGURABLE_PARAM_TEST_H_ +#define COMMON_CONFIGURABLE_PARAM_TEST_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::conf::test +{ +struct TestParam : public o2::conf::ConfigurableParamHelper { + enum TestEnum : uint8_t { + A, + B, + C + }; + + int iValue{42}; + float fValue{3.14}; + double dValue{3.14}; + bool bValue{true}; + unsigned uValue{1}; + long lValue{1}; + unsigned long ulValue{1}; + long long llValue{1}; + unsigned long long ullValue{1}; + std::string sValue = "default"; + int iValueProvenanceTest{0}; + TestEnum eValue = TestEnum::C; + int caValue[3] = {0, 1, 2}; + + O2ParamDef(TestParam, "TestParam"); +}; +} // namespace o2::conf::test + +#endif diff --git a/Common/Utils/src/CommonUtilsTestLinkDef.h b/Common/Utils/src/CommonUtilsTestLinkDef.h new file mode 100644 index 0000000000000..9ee67f62fd7d0 --- /dev/null +++ b/Common/Utils/src/CommonUtilsTestLinkDef.h @@ -0,0 +1,21 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::conf::test::TestParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::test::TestParam> + ; + +#endif diff --git a/Common/Utils/src/ConfigurableParam.cxx b/Common/Utils/src/ConfigurableParam.cxx index 8e242952bd61d..8497a485fca39 100644 --- a/Common/Utils/src/ConfigurableParam.cxx +++ b/Common/Utils/src/ConfigurableParam.cxx @@ -77,6 +77,30 @@ bool keyInTree(boost::property_tree::ptree* pt, const std::string& key) return reply; } +// Convert a type info to the appropiate literal suffix +std::string getLiteralSuffixFromType(const std::type_info& type) +{ + if (type == typeid(float)) { + return "f"; + } + if (type == typeid(long double)) { + return "l"; + } + if (type == typeid(unsigned int)) { + return "u"; + } + if (type == typeid(unsigned long)) { + return "ul"; + } + if (type == typeid(long long)) { + return "ll"; + } + if (type == typeid(unsigned long long)) { + return "ull"; + } + return ""; +} + // ------------------------------------------------------------------ void EnumRegistry::add(const std::string& key, const TDataMember* dm) @@ -204,12 +228,42 @@ void ConfigurableParam::setValue(std::string const& key, std::string const& valu initialize(); } assert(sPtree); + auto setValueImpl = [&](std::string const& value) { + sPtree->put(key, value); + auto changed = updateThroughStorageMapWithConversion(key, value); + if (changed != EParamUpdateStatus::Failed) { + sValueProvenanceMap->find(key)->second = kRT; // set to runtime + } + }; try { if (sPtree->get_optional(key).is_initialized()) { - sPtree->put(key, valuestring); - auto changed = updateThroughStorageMapWithConversion(key, valuestring); - if (changed != EParamUpdateStatus::Failed) { - sValueProvenanceMap->find(key)->second = kRT; // set to runtime + try { + // try first setting value without stripping a literal suffix + setValueImpl(valuestring); + } catch (...) { + // try second stripping the expected literal suffix value for fundamental types + auto iter = sKeyToStorageMap->find(key); + if (iter == sKeyToStorageMap->end()) { + std::cerr << "Error in setValue (string) key is not known\n"; + return; + } + const auto expectedSuffix = getLiteralSuffixFromType(iter->second.first); + if (!expectedSuffix.empty()) { + auto valuestringLower = valuestring; + std::transform(valuestring.cbegin(), valuestring.cend(), valuestringLower.begin(), tolower); + if (valuestringLower.ends_with(expectedSuffix)) { + std::string strippedValue = valuestringLower.substr(0, valuestringLower.length() - expectedSuffix.length()); + setValueImpl(strippedValue); + } else { + // check if it has a different suffix and throw + for (const auto& suffix : {"f", "l", "u", "ul", "ll", "ull"}) { + if (valuestringLower.ends_with(suffix) && suffix != expectedSuffix) { + throw std::invalid_argument("Wrong type suffix: expected " + expectedSuffix + " but got " + suffix); + } + } + throw; // just rethrow the original exception + } + } } } } catch (std::exception const& e) { diff --git a/Common/Utils/src/ConfigurableParamTest.cxx b/Common/Utils/src/ConfigurableParamTest.cxx new file mode 100644 index 0000000000000..5115a8dfe889d --- /dev/null +++ b/Common/Utils/src/ConfigurableParamTest.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParamTest.h" +O2ParamImpl(o2::conf::test::TestParam); diff --git a/Common/Utils/test/testConfigurableParam.cxx b/Common/Utils/test/testConfigurableParam.cxx new file mode 100644 index 0000000000000..3ef177aaca3fe --- /dev/null +++ b/Common/Utils/test/testConfigurableParam.cxx @@ -0,0 +1,145 @@ +// 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. + +#define BOOST_TEST_MODULE Test ConfigurableParams +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include + +#include "CommonUtils/ConfigurableParamTest.h" + +using namespace o2::conf; +using namespace o2::conf::test; + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Basic) +{ + // Tests the default parameters and also getter helpers. + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(param.iValue, 42); + BOOST_CHECK_EQUAL(param.dValue, 3.14); + BOOST_CHECK_EQUAL(param.bValue, true); + BOOST_CHECK_EQUAL(param.sValue, "default"); + BOOST_CHECK_EQUAL(static_cast(param.eValue), 2); + + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.iValue"), 42); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.dValue"), 3.14); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.bValue"), true); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.sValue"), "default"); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_Fundamental) +{ + // tests runtime setting and getting for fundamental types + ConfigurableParam::setValue("TestParam.iValue", "100"); + ConfigurableParam::setValue("TestParam.dValue", "2.718"); + ConfigurableParam::setValue("TestParam.bValue", "0"); + ConfigurableParam::setValue("TestParam.sValue", "modified"); + ConfigurableParam::setValue("TestParam.eValue", "0"); + + auto& param = TestParam::Instance(); + param.printKeyValues(); + BOOST_CHECK_EQUAL(param.iValue, 100); + BOOST_CHECK_EQUAL(param.dValue, 2.718); + BOOST_CHECK_EQUAL(param.bValue, false); + BOOST_CHECK_EQUAL(param.sValue, "modified"); + BOOST_CHECK_EQUAL(static_cast(param.eValue), 0); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_CArray) +{ + // tests setting and getting for a c-style array type + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[0]"), 0); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[1]"), 1); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[2]"), 2); + + ConfigurableParam::setValue("TestParam.caValue[1]", "99"); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[1]"), 99); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Provenance) +{ + // tests correct setting of provenance + BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kCODE); + ConfigurableParam::setValue("TestParam.iValueProvenanceTest", "123"); + BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kRT); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Ini) +{ + // test for ini file serialization + const std::string testFileName = "test_config.ini"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + ConfigurableParam::writeINI(testFileName); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::updateFromFile(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Json) +{ + // test for json file serialization + const std::string testFileName = "test_config.json"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + ConfigurableParam::writeJSON(testFileName); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::updateFromFile(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_ROOT) +{ + // test for root file serialization + const std::string testFileName = "test_config.root"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + TFile* testFile = TFile::Open(testFileName.c_str(), "RECREATE"); + TestParam::Instance().serializeTo(testFile); + testFile->Close(); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::fromCCDB(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Cli) +{ + // test setting values from as a cli arg string + ConfigurableParam::updateFromString("TestParam.iValue=55;TestParam.sValue=cli"); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 55); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, "cli"); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_LiteralSuffix) +{ + // test setting values with the correct literal suffix + ConfigurableParam::updateFromString("TestParam.fValue=42.f"); + BOOST_CHECK_EQUAL(TestParam::Instance().fValue, 42.f); + + ConfigurableParam::setValue("TestParam.ullValue", "999ull"); + BOOST_CHECK_EQUAL(TestParam::Instance().ullValue, 999ULL); + // check using wrong literal suffix fails, prints error to std + ConfigurableParam::setValue("TestParam.ullValue", "888u"); + BOOST_CHECK_NE(TestParam::Instance().ullValue, 888); +} From 968f037bc98f1a85928b713efee6a82c88b9824d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:55:51 +0200 Subject: [PATCH 12/99] DPL: allow closing a signpost interval with an error (#14692) The error will be printed regardless of the signposts being enabled. In case the signposts are actually enabled, the error will be the closing message of the signpost interval. In case the signposts are not enabled, the error will be printed as a standard error. --- Framework/Foundation/include/Framework/Signpost.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Framework/Foundation/include/Framework/Signpost.h b/Framework/Foundation/include/Framework/Signpost.h index 7ed544c529303..51d1b0433b0de 100644 --- a/Framework/Foundation/include/Framework/Signpost.h +++ b/Framework/Foundation/include/Framework/Signpost.h @@ -611,6 +611,16 @@ void o2_debug_log_set_stacktrace(_o2_log_t* log, int stacktrace) } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \ _o2_signpost_interval_end(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \ } +// Print out a message at error level in any case even if the signpost is not enable. +// If it is enabled, behaves like O2_SIGNPOST_END. +#define O2_SIGNPOST_END_WITH_ERROR(log, id, name, format, ...) \ + if (O2_BUILTIN_UNLIKELY(O2_SIGNPOST_ENABLED_MAC(log))) { \ + O2_SIGNPOST_END_MAC(log, id, name, format, ##__VA_ARGS__); \ + } else if (O2_BUILTIN_UNLIKELY(private_o2_log_##log->stacktrace)) { \ + _o2_signpost_interval_end(private_o2_log_##log, id, name, remove_engineering_type(format).data(), ##__VA_ARGS__); \ + } else { \ + O2_LOG_MACRO_RAW(error, remove_engineering_type(format).data(), ##__VA_ARGS__); \ + } #else // This is the release implementation, it does nothing. #define O2_DECLARE_DYNAMIC_LOG(x) #define O2_DECLARE_DYNAMIC_STACKTRACE_LOG(x) From 84b16fc6f0b882896a543f27c878cc06663f46b9 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 26 Sep 2025 14:56:27 +0200 Subject: [PATCH 13/99] ITS3: use const view of ROFs, prepareROFData (#14694) Signed-off-by: Felix Schlepper --- .../tracking/include/ITStracking/TimeFrame.h | 4 +- .../include/ITStracking/TrackingInterface.h | 2 +- .../ITSMFT/ITS/tracking/src/TimeFrame.cxx | 37 +++++++++++-------- .../ITS/tracking/src/TrackingInterface.cxx | 7 ++-- .../include/ITS3Reconstruction/IOUtils.h | 2 +- .../ITS3Reconstruction/TrackingInterface.h | 2 +- .../ITS3/reconstruction/src/IOUtils.cxx | 13 +++---- .../reconstruction/src/TrackingInterface.cxx | 2 +- 8 files changed, 38 insertions(+), 31 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 436ce25336ca7..b324092624a6d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -90,12 +90,14 @@ struct TimeFrame { int loadROFrameData(const o2::itsmft::ROFRecord& rof, gsl::span clusters, const dataformats::MCTruthContainer* mcLabels = nullptr); - int loadROFrameData(gsl::span rofs, + int loadROFrameData(gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels = nullptr); void resetROFrameData(size_t nROFs); + void prepareROFrameData(gsl::span rofs, + gsl::span clusters); int getTotalClusters() const; auto& getTotVertIteration() { return mTotVertPerIteration; } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h index 787f299e15888..491d2df4697ac 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -78,7 +78,7 @@ class ITSTrackingInterface TimeFrameN* mTimeFrame = nullptr; protected: - virtual void loadROF(gsl::span& trackROFspan, + virtual void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels); diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 4ea0bedaced5f..ca28ee227df56 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -103,28 +103,17 @@ void TimeFrame::addPrimaryVerticesContributorLabelsInROF(const bounded_ } template -int TimeFrame::loadROFrameData(gsl::span rofs, +int TimeFrame::loadROFrameData(gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels) { - resetROFrameData(rofs.size()); - GeometryTGeo* geom = GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - mNrof = rofs.size(); - clearResizeBoundedVector(mClusterSize, clusters.size(), mMemoryPool.get()); - std::array clusterCountPerLayer{}; - for (const auto& clus : clusters) { - ++clusterCountPerLayer[geom->getLayer(clus.getSensorID())]; - } - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); - mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); - mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); - } + resetROFrameData(rofs.size()); + prepareROFrameData(rofs, clusters); for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { const auto& rof = rofs[iRof]; @@ -182,7 +171,7 @@ int TimeFrame::loadROFrameData(gsl::span rofs, } return mNrof; -} // namespace o2::its +} template void TimeFrame::resetROFrameData(size_t nRofs) @@ -201,6 +190,24 @@ void TimeFrame::resetROFrameData(size_t nRofs) } } +template +void TimeFrame::prepareROFrameData(gsl::span rofs, + gsl::span clusters) +{ + GeometryTGeo* geom = GeometryTGeo::Instance(); + mNrof = rofs.size(); + clearResizeBoundedVector(mClusterSize, clusters.size(), mMemoryPool.get()); + std::array clusterCountPerLayer{}; + for (const auto& clus : clusters) { + ++clusterCountPerLayer[geom->getLayer(clus.getSensorID())]; + } + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } +} + template void TimeFrame::prepareClusters(const TrackingParameters& trkParam, const int maxLayers) { diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index f673d8f446350..d5f13cd9d25ea 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -139,11 +139,10 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) mTracker->setBz(o2::base::Propagator::Instance()->getNominalBz()); gsl::span::iterator pattIt = patterns.begin(); - - gsl::span trackROFspan(trackROFvec); + gsl::span trackROFspan(trackROFvec); loadROF(trackROFspan, compClusters, pattIt, labels); pattIt = patterns.begin(); - std::vector savedROF; + auto logger = [&](const std::string& s) { LOG(info) << s; }; auto fatalLogger = [&](const std::string& s) { LOG(fatal) << s; }; auto errorLogger = [&](const std::string& s) { LOG(error) << s; }; @@ -406,7 +405,7 @@ void ITSTrackingInterface::setTraitsFromProvider(VertexerTraitsN* vertexerTraits mVertexer->setMemoryPool(mMemoryPool); } -void ITSTrackingInterface::loadROF(gsl::span& trackROFspan, +void ITSTrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels) diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h index 771b13539b759..fa15e73118524 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h @@ -72,7 +72,7 @@ void convertCompactClusters(gsl::span clusters, const its3::TopologyDictionary* dict); int loadROFrameDataITS3(its::TimeFrame<7>* tf, - gsl::span rofs, + gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const its3::TopologyDictionary* dict, diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h index ab2ff0086200b..931628f2cf876 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h @@ -28,7 +28,7 @@ class ITS3TrackingInterface final : public its::ITSTrackingInterface void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj) final; protected: - void loadROF(gsl::span& trackROFspan, + void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels) final; diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index 2fced813efc93..8bfc7eedf2d6f 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -58,22 +58,22 @@ void convertCompactClusters(gsl::span clusters, } int loadROFrameDataITS3(its::TimeFrame<7>* tf, - gsl::span rofs, + gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const its3::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels) { - tf->resetROFrameData(rofs.size()); - auto geom = its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - tf->mNrof = 0; + tf->resetROFrameData(rofs.size()); + tf->prepareROFrameData(rofs, clusters); its::bounded_vector clusterSizeVec(clusters.size(), tf->getMemoryPool().get()); - for (auto& rof : rofs) { + for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { + const auto& rof = rofs[iRof]; for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { auto& c = clusters[clusterId]; auto sensorID = c.getSensorID(); @@ -108,9 +108,8 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, tf->addClusterExternalIndexToLayer(layer, clusterId); } for (unsigned int iL{0}; iL < tf->getUnsortedClusters().size(); ++iL) { - tf->mROFramesClusters[iL].push_back(tf->getUnsortedClusters()[iL].size()); + tf->mROFramesClusters[iL][iRof + 1] = tf->getUnsortedClusters()[iL].size(); } - tf->mNrof++; } tf->setClusterSize(clusterSizeVec); diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx index 3d18ab267bd46..0f5c66a7f9663 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx @@ -74,7 +74,7 @@ void ITS3TrackingInterface::finaliseCCDB(framework::ConcreteDataMatcher& matcher } } -void ITS3TrackingInterface::loadROF(gsl::span& trackROFspan, +void ITS3TrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, const dataformats::MCTruthContainer* mcLabels) From de9994d82f3d9b979d14db770155ab85d5340621 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:48:16 +0200 Subject: [PATCH 14/99] DPL: improve message on quit (#14696) --- Framework/Core/src/runDataProcessing.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index a343637080da1..ae6ea03063dfc 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -2202,8 +2202,11 @@ int runStateMachine(DataProcessorSpecs const& workflow, driverInfo.states.push_back(DriverState::RUNNING); } break; - case DriverState::QUIT_REQUESTED: - LOG(info) << "QUIT_REQUESTED"; + case DriverState::QUIT_REQUESTED: { + std::time_t result = std::time(nullptr); + char buffer[32]; + std::strncpy(buffer, std::ctime(&result), 26); + O2_SIGNPOST_EVENT_EMIT_INFO(driver, sid, "mainloop", "Quit requested at %{public}s", buffer); guiQuitRequested = true; // We send SIGCONT to make sure stopped children are resumed killChildren(infos, SIGCONT); @@ -2215,6 +2218,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, uv_timer_start(&force_step_timer, single_step_callback, 0, 300); driverInfo.states.push_back(DriverState::HANDLE_CHILDREN); break; + } case DriverState::HANDLE_CHILDREN: { // Run any pending libUV event loop, block if // any, so that we do not consume CPU time when the driver is From 44f4161792010d55f0fc5694a65f60d2e5507c7c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 26 Sep 2025 22:06:33 +0200 Subject: [PATCH 15/99] DPL GUI: simplify and speed up (#14700) * Avoid cumbersome retrivial of the lifetime * No need to create a string just to format it. --- Framework/GUISupport/CMakeLists.txt | 1 + .../src/FrameworkGUIDataRelayerUsage.cxx | 2 +- .../src/FrameworkGUIDeviceInspector.cxx | 18 +++------ Framework/GUISupport/src/InspectorHelpers.cxx | 40 +++++++++++++++++++ Framework/GUISupport/src/InspectorHelpers.h | 29 +------------- 5 files changed, 50 insertions(+), 40 deletions(-) create mode 100644 Framework/GUISupport/src/InspectorHelpers.cxx diff --git a/Framework/GUISupport/CMakeLists.txt b/Framework/GUISupport/CMakeLists.txt index 61519c21dc20c..8e67da3e53e15 100644 --- a/Framework/GUISupport/CMakeLists.txt +++ b/Framework/GUISupport/CMakeLists.txt @@ -20,6 +20,7 @@ o2_add_library(FrameworkGUISupport src/PaletteHelpers.cxx src/SpyService.cxx src/SpyServiceHelpers.cxx + src/InspectorHelpers.cxx PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src PUBLIC_LINK_LIBRARIES O2::Framework AliceO2::DebugGUI) diff --git a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx index c39e268fa90a7..1d3b4f24ea34c 100644 --- a/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDataRelayerUsage.cxx @@ -233,7 +233,7 @@ void displayDataRelayer(DeviceMetricsInfo const& /*metrics*/, continue; } if (i == (size_t)row) { - ImGui::Text("%d %.*s (%s)", row, int(end - input), input, InspectorHelpers::getLifeTimeStr(spec.inputs[i].matcher.lifetime).c_str()); + ImGui::Text("%d %.*s (%s)", row, int(end - input), input, InspectorHelpers::getLifeTimeStr(spec.inputs[i].matcher.lifetime)); break; } ++i; diff --git a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx index 9b2a13c07987d..b8c9cc50f0770 100644 --- a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx @@ -79,7 +79,8 @@ void deviceStateTable(DataProcessingStates const& states) } } -void deviceInfoTable(char const* label, ProcessingStateId id, DataProcessingStates const& states, std::variant, std::vector> routes, DeviceMetricsInfo const& metrics) +template +void deviceInfoTable(char const* label, ProcessingStateId id, DataProcessingStates const& states, Routes const& routes, DeviceMetricsInfo const& metrics) { // Find the state spec associated to data_queries auto& view = states.statesViews[(int)id]; @@ -95,17 +96,10 @@ void deviceInfoTable(char const* label, ProcessingStateId id, DataProcessingStat if ((end - input) == 0) { continue; } - auto getLifetime = [&routes, &i]() -> Lifetime { - if (std::get_if>(&routes)) { - return std::get>(routes)[i].matcher.lifetime; - } else { - return std::get>(routes)[i].matcher.lifetime; - } - }; - ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(getLifetime()).c_str()); + ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(routes[i].matcher.lifetime)); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(getLifetime()).c_str()); + ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(routes[i].matcher.lifetime)); ImGui::EndTooltip(); } input = end + 1; @@ -346,8 +340,8 @@ void displayDeviceInspector(DeviceSpec const& spec, } deviceStateTable(states); - deviceInfoTable("Inputs:", ProcessingStateId::DATA_QUERIES, states, std::variant, std::vector>(spec.inputs), metrics); - deviceInfoTable("Outputs:", ProcessingStateId::OUTPUT_MATCHERS, states, std::variant, std::vector>(spec.outputs), metrics); + deviceInfoTable("Inputs:", ProcessingStateId::DATA_QUERIES, states, spec.inputs, metrics); + deviceInfoTable("Outputs:", ProcessingStateId::OUTPUT_MATCHERS, states, spec.outputs, metrics); configurationTable(info.currentConfig, info.currentProvenance); optionsTable("Workflow Options", metadata.workflowOptions, control); if (ImGui::CollapsingHeader("Labels", ImGuiTreeNodeFlags_DefaultOpen)) { diff --git a/Framework/GUISupport/src/InspectorHelpers.cxx b/Framework/GUISupport/src/InspectorHelpers.cxx new file mode 100644 index 0000000000000..23e74c964e531 --- /dev/null +++ b/Framework/GUISupport/src/InspectorHelpers.cxx @@ -0,0 +1,40 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "InspectorHelpers.h" + +namespace o2::framework +{ +char const* InspectorHelpers::getLifeTimeStr(Lifetime lifetime) +{ + switch (lifetime) { + case Lifetime::Timeframe: + return "Timeframe"; + case Lifetime::Condition: + return "Condition"; + case Lifetime::Sporadic: + return "Sporadic"; + case Lifetime::Transient: + return "Transient"; + case Lifetime::Timer: + return "Timer"; + case Lifetime::Enumeration: + return "Enumeration"; + case Lifetime::Signal: + return "Signal"; + case Lifetime::Optional: + return "Optional"; + case Lifetime::OutOfBand: + return "OutOfBand"; + } + return "none"; +}; +} // namespace o2::framework diff --git a/Framework/GUISupport/src/InspectorHelpers.h b/Framework/GUISupport/src/InspectorHelpers.h index 124c714f54df5..193486fc91dbc 100644 --- a/Framework/GUISupport/src/InspectorHelpers.h +++ b/Framework/GUISupport/src/InspectorHelpers.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2025 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. // @@ -11,8 +11,6 @@ #ifndef O2_FRAMEWORK_INSPECTORHELPERS_H_ #define O2_FRAMEWORK_INSPECTORHELPERS_H_ -#include - #include "Framework/Lifetime.h" namespace o2::framework @@ -20,30 +18,7 @@ namespace o2::framework /// A helper class for inpsection of device information struct InspectorHelpers { - static const std::string getLifeTimeStr(Lifetime lifetime) - { - switch (lifetime) { - case Lifetime::Timeframe: - return "Timeframe"; - case Lifetime::Condition: - return "Condition"; - case Lifetime::Sporadic: - return "Sporadic"; - case Lifetime::Transient: - return "Transient"; - case Lifetime::Timer: - return "Timer"; - case Lifetime::Enumeration: - return "Enumeration"; - case Lifetime::Signal: - return "Signal"; - case Lifetime::Optional: - return "Optional"; - case Lifetime::OutOfBand: - return "OutOfBand"; - } - return "none"; - }; + static const char* getLifeTimeStr(Lifetime lifetime); }; } // namespace o2::framework From b67ed5b999391643e3af227a9c40822079eb2c5a Mon Sep 17 00:00:00 2001 From: Evgeny Kryshen Date: Sat, 27 Sep 2025 00:24:15 +0300 Subject: [PATCH 16/99] First version of ECal sim, digitizer and clusterizer (#14697) Co-authored-by: ALICE Action Bot --- Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt | 4 +- .../ECal/DataFormatsECal/CMakeLists.txt | 23 + .../include/DataFormatsECal/Cluster.h | 85 ++++ .../include/DataFormatsECal/Digit.h | 55 +++ .../include/DataFormatsECal/MCLabel.h | 41 ++ .../ECal/DataFormatsECal/src/Cluster.cxx | 56 +++ .../src/DataFormatsECalLinkDef.h | 25 + .../ALICE3/ECal/DataFormatsECal/src/Digit.cxx | 24 + .../ECal/DataFormatsECal/src/MCLabel.cxx | 19 + Detectors/Upgrades/ALICE3/ECal/README.md | 4 +- .../Upgrades/ALICE3/ECal/base/CMakeLists.txt | 10 +- .../base/include/ECalBase/ECalBaseParam.h | 39 +- .../ECal/base/include/ECalBase/Geometry.h | 99 ++++ .../ECal/base/include/ECalBase/GeometryTGeo.h | 17 +- .../ALICE3/ECal/base/include/ECalBase/Hit.h | 85 ++++ .../ALICE3/ECal/base/src/ECalBaseLinkDef.h | 3 + .../ALICE3/ECal/base/src/ECalBaseParam.cxx | 9 +- .../ALICE3/ECal/base/src/Geometry.cxx | 264 ++++++++++ .../ALICE3/ECal/base/src/GeometryTGeo.cxx | 19 +- .../Upgrades/ALICE3/ECal/base/src/Hit.cxx | 34 ++ .../ALICE3/ECal/reconstruction/CMakeLists.txt | 19 + .../include/ECalReconstruction/Clusterizer.h | 75 +++ .../ECal/reconstruction/src/Clusterizer.cxx | 455 ++++++++++++++++++ .../src/ECalReconstructionLinkDef.h | 20 + .../ALICE3/ECal/simulation/CMakeLists.txt | 9 +- .../ALICE3/ECal/simulation/data/simcuts.dat | 14 + .../include/ECalSimulation/Detector.h | 78 +-- .../include/ECalSimulation/Digitizer.h | 58 +++ .../ALICE3/ECal/simulation/src/Detector.cxx | 437 +++++++++++------ .../ALICE3/ECal/simulation/src/Digitizer.cxx | 89 ++++ .../simulation/src/ECalSimulationLinkDef.h | 1 + 31 files changed, 1948 insertions(+), 222 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Cluster.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Digit.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/MCLabel.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/DataFormatsECalLinkDef.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Digit.cxx create mode 100644 Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/MCLabel.cxx create mode 100644 Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Hit.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx create mode 100644 Detectors/Upgrades/ALICE3/ECal/base/src/Hit.cxx create mode 100644 Detectors/Upgrades/ALICE3/ECal/reconstruction/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx create mode 100644 Detectors/Upgrades/ALICE3/ECal/reconstruction/src/ECalReconstructionLinkDef.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/simulation/data/simcuts.dat create mode 100644 Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Digitizer.h create mode 100644 Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx diff --git a/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt index 83838a01d13f1..cc0a7b0337619 100644 --- a/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/ECal/CMakeLists.txt @@ -10,4 +10,6 @@ # or submit itself to any jurisdiction. add_subdirectory(base) -add_subdirectory(simulation) \ No newline at end of file +add_subdirectory(simulation) +add_subdirectory(reconstruction) +add_subdirectory(DataFormatsECal) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/CMakeLists.txt new file mode 100644 index 0000000000000..3448d6b31029d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/CMakeLists.txt @@ -0,0 +1,23 @@ +# 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. + +o2_add_library(DataFormatsECal + SOURCES src/Digit.cxx + SOURCES src/MCLabel.cxx + SOURCES src/Cluster.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::SimulationDataFormat + AliceO2::InfoLogger) + +o2_target_root_dictionary(DataFormatsECal + HEADERS include/DataFormatsECal/Digit.h + HEADERS include/DataFormatsECal/MCLabel.h + HEADERS include/DataFormatsECal/Cluster.h) diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Cluster.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Cluster.h new file mode 100644 index 0000000000000..4a34ef1679f26 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Cluster.h @@ -0,0 +1,85 @@ +// 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 Cluster.h +/// \brief Definition of ECal cluster class +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_CLUSTER_H +#define ALICEO2_ECAL_CLUSTER_H +#include +#include +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Cluster +{ + public: + Cluster() = default; + Cluster(const Cluster& clu) = default; + ~Cluster() = default; + + // setters + void addDigit(int digitIndex, int towerId, double energy); + void setNLM(int nMax) { mNLM = nMax; } + void setE(float energy) { mE = energy; } + void setX(float x) { mX = x; } + void setY(float y) { mY = y; } + void setZ(float z) { mZ = z; } + void setChi2(float chi2) { mChi2 = chi2; } + void setEdgeFlag(bool isEdge) { mEdge = isEdge; } + void addMcTrackID(int mcTrackID, float energy) { mMcTrackEnergy[mcTrackID] += energy; } + + // getters + const std::map& getMcTrackEnergy() { return mMcTrackEnergy; } + int getMultiplicity() const { return mDigitIndex.size(); } + int getDigitIndex(int i) const { return mDigitIndex[i]; } + int getDigitTowerId(int i) const { return mDigitTowerId[i]; } + float getDigitEnergy(int i) const { return mDigitEnergy[i]; } + float getNLM() const { return mNLM; } + float getTime() const { return mTime; } + float getE() const { return mE; } + float getX() const { return mX; } + float getY() const { return mY; } + float getZ() const { return mZ; } + float getR() const { return std::sqrt(mX * mX + mY * mY); } + float getTheta() const { return std::atan2(getR(), mZ); } + float getEta() const { return -std::log(std::tan(getTheta() / 2.)); } + float getPhi() const { return std::atan2(mY, mX); } + float getChi2() const { return mChi2; } + bool isAtTheEdge() const { return mEdge; } + int getMcTrackID() const; + TLorentzVector getMomentum() const; + + private: + std::vector mDigitIndex; // vector of digit indices in digits vector + std::vector mDigitTowerId; // vector of corresponding digit tower Ids + std::vector mDigitEnergy; // vector of corresponding digit energies + std::map mMcTrackEnergy; // MC track indices and corresponding energies + int mNLM = 0; // number of local maxima in the initial cluster + float mTime = 0; // cluster time + float mE = 0; // cluster energy + float mX = 0; // estimated x-coordinate + float mY = 0; // estimated y-ccordinate + float mZ = 0; // estimated z-ccordinate + float mChi2 = 0; // chi2 wrt EM shape + bool mEdge = 0; // set to true if one of cluster digits is at the chamber edge + ClassDefNV(Cluster, 1); +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_CLUSTER_H diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Digit.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Digit.h new file mode 100644 index 0000000000000..cc46a64e2cac0 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/Digit.h @@ -0,0 +1,55 @@ +// 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 Digit.h +/// \brief Definition of ECal digit class +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_DIGIT_H +#define ALICEO2_ECAL_DIGIT_H + +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Digit : public o2::dataformats::TimeStamp +{ + public: + Digit() = default; + Digit(int tower, double amplitudeGeV, double time); + ~Digit() = default; + + // setters + void setTower(int tower) { mTower = tower; } + void setAmplitude(double amplitude) { mAmplitudeGeV = amplitude; } + void setEnergy(double energy) { mAmplitudeGeV = energy; } + void setLabel(int label) { mLabel = label; } + + // getters + int getTower() const { return mTower; } + double getAmplitude() const { return mAmplitudeGeV; } + double getEnergy() const { return mAmplitudeGeV; } + int getLabel() const { return mLabel; } + + private: + double mAmplitudeGeV = 0.; ///< Amplitude (GeV) + int32_t mTower = -1; ///< Tower index (absolute cell ID) + int32_t mLabel = -1; ///< Index of the corresponding entry/entries in the MC label array + ClassDefNV(Digit, 1); +}; + +} // namespace ecal +} // namespace o2 +#endif // ALICEO2_ECAL_DIGIT_H diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/MCLabel.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/MCLabel.h new file mode 100644 index 0000000000000..762779977ca53 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/include/DataFormatsECal/MCLabel.h @@ -0,0 +1,41 @@ +// 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 MCLabel.h +/// \brief MCLabel class to store MC truth info for ECal +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_MCLABEL_H +#define ALICEO2_ECAL_MCLABEL_H + +#include + +namespace o2 +{ +namespace ecal +{ +class MCLabel : public o2::MCCompLabel +{ + public: + MCLabel() = default; + MCLabel(int trackID, int eventID, int srcID, bool fake, float edep) : o2::MCCompLabel(trackID, eventID, srcID, fake), mEdep(edep) {} + float getEdep() const { return mEdep; } + + private: + float mEdep = 0; // deposited energy + + ClassDefNV(MCLabel, 1); +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_MCLABEL_H diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx new file mode 100644 index 0000000000000..77f7d9219ef6b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx @@ -0,0 +1,56 @@ +// 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 Cluster.cxx +/// \brief Implementation of ECal cluster class +/// +/// \author Evgeny Kryshen + +#include +#include +#include +#include + +using namespace o2::ecal; + +ClassImp(Cluster); + +//============================================================================== +void Cluster::addDigit(int digitIndex, int towerId, double energy) +{ + mE += energy; + mDigitIndex.push_back(digitIndex); + mDigitTowerId.push_back(towerId); + mDigitEnergy.push_back(energy); +} + +//============================================================================== +int Cluster::getMcTrackID() const +{ + float maxEnergy = 0; + int maxID = 0; + for (const auto& [mcTrackID, energy] : mMcTrackEnergy) { + if (energy > maxEnergy) { + maxEnergy = energy; + maxID = mcTrackID; + } + } + return maxID; +} + +//============================================================================== +TLorentzVector Cluster::getMomentum() const +{ + double r = std::sqrt(mX * mX + mY * mY + mZ * mZ); + if (r == 0) + return TLorentzVector(); + return TLorentzVector(mE * mX / r, mE * mY / r, mE * mZ / r, mE); +} diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/DataFormatsECalLinkDef.h b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/DataFormatsECalLinkDef.h new file mode 100644 index 0000000000000..5b0190aa10d45 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/DataFormatsECalLinkDef.h @@ -0,0 +1,25 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::ecal::Digit + ; +#pragma link C++ class o2::ecal::MCLabel + ; +#pragma link C++ class o2::ecal::Cluster + ; +#pragma link C++ class std::vector < o2::ecal::Digit> + ; +#pragma link C++ class std::vector < o2::ecal::Cluster> + ; +#include "SimulationDataFormat/MCTruthContainer.h" +#pragma link C++ class o2::dataformats::MCTruthContainer < o2::ecal::MCLabel> + ; +#endif diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Digit.cxx b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Digit.cxx new file mode 100644 index 0000000000000..c339c112c6858 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Digit.cxx @@ -0,0 +1,24 @@ +// 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 Digit.cxx +/// \brief Implementation of ECal digit class +/// +/// \author Evgeny Kryshen + +#include + +using namespace o2::ecal; + +Digit::Digit(int tower, double amplitudeGeV, double time) + : mTower(tower), mAmplitudeGeV(amplitudeGeV), o2::dataformats::TimeStamp(time) +{ +} diff --git a/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/MCLabel.cxx b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/MCLabel.cxx new file mode 100644 index 0000000000000..4dbd2711f1521 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/MCLabel.cxx @@ -0,0 +1,19 @@ +// 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 MCLabel.cxx +/// \brief MCLabel class to store MC truth info for ECal +/// +/// \author Evgeny Kryshen + +#include + +ClassImp(o2::ecal::MCLabel); diff --git a/Detectors/Upgrades/ALICE3/ECal/README.md b/Detectors/Upgrades/ALICE3/ECal/README.md index 288040fbd5fd9..ff58683646409 100644 --- a/Detectors/Upgrades/ALICE3/ECal/README.md +++ b/Detectors/Upgrades/ALICE3/ECal/README.md @@ -1,10 +1,10 @@ # ALICE 3 Electromagnetic Calorimenter -This is top page for the ECL detector documentation. +This is top page for the ECAL detector documentation. \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt index 70017cc051e80..b0e1229662653 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/ECal/base/CMakeLists.txt @@ -10,10 +10,14 @@ # or submit itself to any jurisdiction. o2_add_library(ECalBase - SOURCES src/GeometryTGeo.cxx + SOURCES src/Geometry.cxx + src/GeometryTGeo.cxx src/ECalBaseParam.cxx + src/Hit.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase) o2_target_root_dictionary(ECalBase - HEADERS include/ECalBase/GeometryTGeo.h - include/ECalBase/ECalBaseParam.h) \ No newline at end of file + HEADERS include/ECalBase/Geometry.h + include/ECalBase/GeometryTGeo.h + include/ECalBase/ECalBaseParam.h + include/ECalBase/Hit.h) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h index b8b7c75e2b7d0..aa0de4119914a 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/ECalBaseParam.h @@ -9,22 +9,45 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file ECalBaseParam.h +/// \brief Geometry parameters configurable via o2-sim --configKeyValues +/// +/// \author Evgeny Kryshen + #ifndef O2_ECAL_BASEPARAM_H #define O2_ECAL_BASEPARAM_H -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" +#include +#include namespace o2 { namespace ecal { struct ECalBaseParam : public o2::conf::ConfigurableParamHelper { - float rMin = 125.0; // cm - float rMax = 155.0; // cm - float zLength = 350.0; // cm - - bool enableFwdEndcap = true; + bool enableFwdEndcap = false; + // general ecal barrel settings + double rMin = 125; // cm + double rMax = 155; // cm + double zLength = 350; // cm + int nSuperModules = 4; + // crystal module specification + int nCrystalModulesZ = 31; + int nCrystalModulesPhi = 96; + double crystalAlphaDeg = 0.4; // degrees + double crystalModuleWidth = 1.9; // cm + double crystalModuleLength = 18; // cm + // sampling module specification + int nSamplingModulesZ = 56; + int nSamplingModulesPhi = 67; + double samplingAlphaDeg = 0.4; // degrees + double samplingModuleWidth = 2.7; // cm + double frontPlateThickness = 1.; // cm + double pbLayerThickness = 0.12; // cm + double scLayerThickness = 0.15; // cm + int nSamplingLayers = 80; + // margin in z between crystal modules and sampling modules + double marginCrystalToSampling = 0.1; // cm O2ParamDef(ECalBaseParam, "ECalBase"); }; @@ -32,4 +55,4 @@ struct ECalBaseParam : public o2::conf::ConfigurableParamHelper { } // namespace ecal } // end namespace o2 -#endif \ No newline at end of file +#endif // O2_ECAL_BASEPARAM_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h new file mode 100644 index 0000000000000..ecfcb5b7cbad6 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h @@ -0,0 +1,99 @@ +// 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 Geometry.h +/// \brief Geometry helper class +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_GEOMETRY_H +#define ALICEO2_ECAL_GEOMETRY_H + +#include +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Geometry +{ + public: + static Geometry& instance() + { + static Geometry sGeom; + return sGeom; + } + + int getNcols() const; + int getNrows() const; + std::pair getSectorChamber(int cellId) const; + std::pair getSectorChamber(int iphi, int iz) const; + + void fillFrontFaceCenterCoordinates(); + int getCellID(int moduleId, int sectorId, bool isCrystal); + void detIdToRelIndex(int cellId, int& chamber, int& sector, int& iphi, int& iz) const; + void detIdToGlobalPosition(int detId, double& x, double& y, double& z); + std::pair globalRowColFromIndex(int cellID) const; + bool isCrystal(int cellID); + int areNeighboursVertex(int detId1, int detId2) const; + + double getTanBeta(int i) { return mTanBeta[i]; } + double getFrontFaceZatMinR(int i) { return mFrontFaceZatMinR[i]; } + double getFrontFaceCenterR(int i) { return mFrontFaceCenterR[i]; } + double getFrontFaceCenterZ(int i) { return mFrontFaceCenterZ[i]; } + double getFrontFaceCenterSamplingPhi(int i) { return mFrontFaceCenterSamplingPhi[i]; } + double getFrontFaceCenterCrystalPhi(int i) { return mFrontFaceCenterCrystalPhi[i]; } + double getFrontFaceCenterTheta(int i) { return mFrontFaceCenterTheta[i]; } + double getRMin() { return mRMin; } + double getCrystalModW() { return mCrystalModW; } + double getSamplingModW() { return mSamplingModW; } + double getCrystalAlpha() { return mCrystalAlpha; } + double getSamplingAlpha() { return mSamplingAlpha; } + double getCrystalDeltaPhi() { return 2 * std::atan(mCrystalModW / 2 / mRMin); } + double getSamplingDeltaPhi() { return 2 * std::atan(mSamplingModW / 2 / mRMin); } + double getCrystalPhiMin(); + double getSamplingPhiMin(); + int getNModulesZ() { return mNModulesZ; } + bool isAtTheEdge(int cellId); + + private: + Geometry(); + Geometry(const Geometry&) = delete; + Geometry& operator=(const Geometry&) = delete; + ~Geometry() = default; + double mRMin{0.}; + int mNSuperModules{0}; + int mNCrystalModulesZ{0}; + int mNSamplingModulesZ{0}; + int mNCrystalModulesPhi{0}; + int mNSamplingModulesPhi{0}; + double mCrystalModW{0.}; + double mSamplingModW{0.}; + double mSamplingAlpha{0.}; + double mCrystalAlpha{0.}; + double mMarginCrystalToSampling{0.}; + int mNModulesZ{0}; + std::vector mFrontFaceZatMinR; + std::vector mFrontFaceCenterR; + std::vector mFrontFaceCenterZ; + std::vector mFrontFaceCenterSamplingPhi; + std::vector mFrontFaceCenterCrystalPhi; + std::vector mFrontFaceCenterTheta; + std::vector mTanBeta; + + ClassDefNV(Geometry, 1); +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_GEOMETRY_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h index 1cff6dd7d3313..6975a5378a72f 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/GeometryTGeo.h @@ -9,6 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GeometryTGeo.h +/// \brief Class containing ECal volume naming patterns +/// +/// \author Evgeny Kryshen + #ifndef ALICEO2_ECAL_GEOMETRYTGEO_H #define ALICEO2_ECAL_GEOMETRYTGEO_H @@ -27,21 +32,25 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static GeometryTGeo* Instance(); static const char* getECalVolPattern() { return sVolumeName.c_str(); } - static const char* getECalSensorPattern() { return sSensorName.c_str(); } + static const char* getECalSectorPattern() { return sSectorName.c_str(); } + static const char* getECalModulePattern() { return sModuleName.c_str(); } static const char* composeSymNameECal() { return Form("%s_%d", o2::detectors::DetID(o2::detectors::DetID::ECL).getName(), 0); } - static const char* composeSymNameSensor(); // A single sensor for the moment + static const char* composeSymNameSector(int s); + static const char* composeSymNameModule(int s, int m); protected: static std::string sVolumeName; - static std::string sSensorName; + static std::string sSectorName; + static std::string sModuleName; private: static std::unique_ptr sInstance; }; } // namespace ecal } // namespace o2 -#endif + +#endif // ALICEO2_ECAL_GEOMETRYTGEO_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Hit.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Hit.h new file mode 100644 index 0000000000000..006b2df5949e6 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Hit.h @@ -0,0 +1,85 @@ +// 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 Hit.h +/// \brief MC hit class to store energy loss per cell and per superparent +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_HIT_H +#define ALICEO2_ECAL_HIT_H + +#include +#include + +namespace o2 +{ +namespace ecal +{ +class Hit : public o2::BasicXYZEHit +{ + public: + /// \brief Default constructor + Hit() = default; + + /// \brief Hit constructor + /// + /// Fully defining information of the ECAL point (position, momentum, energy, track, ...) + /// + /// \param trackID Index of the track, defined as parent track entering the ECAL + /// \param cellID ID of the detector cell + /// \param pos Position vector of the point + /// \param mom Momentum vector for the particle at the point + /// \param tof Time of the hit + /// \param eLoss Energy loss + Hit(int trackID, int cellID, const math_utils::Point3D& pos, + const math_utils::Vector3D& mom, float tof, float eLoss) + : o2::BasicXYZEHit(pos.X(), pos.Y(), pos.Z(), tof, eLoss, trackID, 0), + mPvector(mom), + mCellID(cellID) + { + } + + /// \brief Destructor + ~Hit() = default; + + /// \brief Check whether the points are from the same parent and in the same detector volume + /// \return True if points are the same (origin and detector), false otherwise + bool operator==(const Hit& rhs) const; + + /// \brief Sorting points according to parent particle and detector volume + /// \return True if this point is smaller, false otherwise + bool operator<(const Hit& rhs) const; + + /// \brief Get cell ID + /// \return cell ID + int GetCellID() const { return mCellID; } + + private: + math_utils::Vector3D mPvector; ///< Momentum vector + int32_t mCellID; ///< Cell ID (used instead of short detID) + ClassDefNV(Hit, 1); +}; + +} // namespace ecal +} // namespace o2 + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; +} // namespace std +#endif + +#endif // ALICEO2_ECAL_HIT_H diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h index 3bf7ccd32460c..0f0c0637ce2c1 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseLinkDef.h @@ -15,8 +15,11 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::ecal::Geometry + ; #pragma link C++ class o2::ecal::GeometryTGeo + #pragma link C++ class o2::ecal::ECalBaseParam + ; +#pragma link C++ class o2::ecal::Hit + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::ecal::ECalBaseParam> + ; +#pragma link C++ class std::vector < o2::ecal::Hit> + ; #endif \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx index 54eb2860526b3..6847f42e26346 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/ECalBaseParam.cxx @@ -9,6 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ECalBase/ECalBaseParam.h" +/// \file ECalBaseParam.cxx +/// \brief Geometry parameters configurable via o2-sim --configKeyValues +/// +/// \author Evgeny Kryshen -O2ParamImpl(o2::ecal::ECalBaseParam); \ No newline at end of file +#include + +O2ParamImpl(o2::ecal::ECalBaseParam); diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx new file mode 100644 index 0000000000000..9483b83f19f49 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx @@ -0,0 +1,264 @@ +// 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 Geometry.cxx +/// \brief Geometry helper class +/// +/// \author Evgeny Kryshen + +#include "TMath.h" +#include +#include +#include +#include +#include "CommonConstants/MathConstants.h" +using namespace o2::ecal; +using o2::constants::math::PIHalf; +using o2::constants::math::TwoPI; + +//============================================================================== +Geometry::Geometry() +{ + auto& pars = ECalBaseParam::Instance(); + pars.updateFromFile("o2sim_configuration.ini", "ECalBase"); + pars.printKeyValues(false, true, true, false); + mRMin = pars.rMin; + mNSuperModules = pars.nSuperModules; + mNCrystalModulesZ = pars.nCrystalModulesZ; + mNSamplingModulesZ = pars.nSamplingModulesZ; + mNCrystalModulesPhi = pars.nCrystalModulesPhi; + mNSamplingModulesPhi = pars.nSamplingModulesPhi; + mCrystalModW = pars.crystalModuleWidth; + mSamplingModW = pars.samplingModuleWidth; + mMarginCrystalToSampling = pars.marginCrystalToSampling; + mCrystalAlpha = pars.crystalAlphaDeg * TMath::DegToRad(); + mSamplingAlpha = pars.samplingAlphaDeg * TMath::DegToRad(); + mNModulesZ = 2 * mNSamplingModulesZ + 2 * mNCrystalModulesZ; + fillFrontFaceCenterCoordinates(); +} + +//============================================================================== +int Geometry::getNcols() const +{ + return mNModulesZ; +} + +//============================================================================== +int Geometry::getNrows() const +{ + return mNSuperModules * (mNCrystalModulesPhi > mNSamplingModulesPhi ? mNCrystalModulesPhi : mNSamplingModulesPhi); +} + +//============================================================================== +double Geometry::getCrystalPhiMin() +{ + double superModuleDeltaPhi = TwoPI / mNSuperModules; + double crystalDeltaPhi = getCrystalDeltaPhi(); + return (superModuleDeltaPhi - crystalDeltaPhi * mNCrystalModulesPhi) / 2.; +} + +//============================================================================== +double Geometry::getSamplingPhiMin() +{ + double superModuleDeltaPhi = TwoPI / mNSuperModules; + double samplingDeltaPhi = getSamplingDeltaPhi(); + return (superModuleDeltaPhi - samplingDeltaPhi * mNSamplingModulesPhi) / 2.; +} + +//============================================================================== +void Geometry::fillFrontFaceCenterCoordinates() +{ + if (mFrontFaceCenterR.size() > 0) + return; + mFrontFaceCenterTheta.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceZatMinR.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceCenterR.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceCenterZ.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mTanBeta.resize(mNCrystalModulesZ + mNSamplingModulesZ); + mFrontFaceCenterSamplingPhi.resize(mNSuperModules * mNSamplingModulesPhi); + mFrontFaceCenterCrystalPhi.resize(mNSuperModules * mNCrystalModulesPhi); + + double superModuleDeltaPhi = TwoPI / mNSuperModules; + double crystalDeltaPhi = getCrystalDeltaPhi(); + double samplingDeltaPhi = getSamplingDeltaPhi(); + double crystalPhiMin = getCrystalPhiMin(); + double samplingPhiMin = getSamplingPhiMin(); + for (int ism = 0; ism < mNSuperModules; ism++) { + // crystal + for (int i = 0; i < mNCrystalModulesPhi; i++) { + double phi0 = superModuleDeltaPhi * ism + crystalPhiMin + crystalDeltaPhi * i; + mFrontFaceCenterCrystalPhi[ism * mNCrystalModulesPhi + i] = phi0; + } + // sampling + for (int i = 0; i < mNSamplingModulesPhi; i++) { + double phi0 = superModuleDeltaPhi * ism + samplingPhiMin + samplingDeltaPhi * i; + mFrontFaceCenterSamplingPhi[ism * mNSamplingModulesPhi + i] = phi0; + } + } + + double theta0 = PIHalf - mCrystalAlpha; + double zAtMinR = mCrystalModW * std::cos(mCrystalAlpha); + + for (int m = 0; m < mNCrystalModulesZ; m++) { + mTanBeta[m] = std::sin(theta0 - mCrystalAlpha) * mCrystalModW / 2 / mRMin; + ROOT::Math::Polar2DVector vMid21(mCrystalModW / 2., PIHalf + theta0); + ROOT::Math::XYPoint pAtMinR(zAtMinR, mRMin); + ROOT::Math::XYPoint pc = pAtMinR + vMid21; + mFrontFaceZatMinR[m] = zAtMinR; + mFrontFaceCenterZ[m] = pc.x(); + mFrontFaceCenterR[m] = pc.y(); + mFrontFaceCenterTheta[m] = theta0; + theta0 -= 2 * mCrystalAlpha; + zAtMinR += mCrystalModW * std::cos(mCrystalAlpha) / std::sin(theta0 + mCrystalAlpha); + } + + theta0 = mFrontFaceCenterTheta[mNCrystalModulesZ - 1] - mCrystalAlpha - mSamplingAlpha; + zAtMinR = mFrontFaceZatMinR[mNCrystalModulesZ - 1]; + zAtMinR += mSamplingModW * std::cos(mSamplingAlpha) / std::sin(theta0 + mSamplingAlpha); + zAtMinR += mMarginCrystalToSampling; + + for (int m = 0; m < mNSamplingModulesZ; m++) { + int i = m + mNCrystalModulesZ; + mTanBeta[i] = std::sin(theta0 - mSamplingAlpha) * mSamplingModW / 2 / mRMin; + ROOT::Math::Polar2DVector vMid21(mSamplingModW / 2., PIHalf + theta0); + ROOT::Math::XYPoint pAtMinR(zAtMinR, mRMin); + ROOT::Math::XYPoint pc = pAtMinR + vMid21; + mFrontFaceZatMinR[i] = zAtMinR; + mFrontFaceCenterZ[i] = pc.x(); + mFrontFaceCenterR[i] = pc.y(); + mFrontFaceCenterTheta[i] = theta0; + theta0 -= 2 * mSamplingAlpha; + zAtMinR += mSamplingModW * std::cos(mSamplingAlpha) / std::sin(theta0 + mSamplingAlpha); + } +} + +int Geometry::getCellID(int moduleId, int sectorId, bool isCrystal) +{ + int cellID = 0; + if (isCrystal) { + if (moduleId % 2 == 0) { // crystal at positive eta + cellID = sectorId * mNModulesZ + moduleId / 2 + mNSamplingModulesZ + mNCrystalModulesZ; + } else { // crystal at negative eta + cellID = sectorId * mNModulesZ - moduleId / 2 + mNSamplingModulesZ + mNCrystalModulesZ - 1; + } + } else { + if (sectorId % 2 == 0) { // sampling at positive eta + cellID = sectorId / 2 * mNModulesZ + moduleId + mNSamplingModulesZ + mNCrystalModulesZ * 2; + } else { // sampling at negative eta + cellID = sectorId / 2 * mNModulesZ - moduleId + mNSamplingModulesZ; + } + } + return cellID; +} + +//============================================================================== +std::pair Geometry::globalRowColFromIndex(int cellID) const +{ + int ip = cellID / mNModulesZ; // row + int iz = cellID % mNModulesZ; // col + return {ip, iz}; +} + +//============================================================================== +bool Geometry::isCrystal(int cellID) +{ + auto [row, col] = globalRowColFromIndex(cellID); + return (col >= mNSamplingModulesZ && col < mNSamplingModulesZ + 2 * mNCrystalModulesZ); +} + +//============================================================================== +std::pair Geometry::getSectorChamber(int cellId) const +{ + int iphi = cellId / mNModulesZ; + int iz = cellId % mNModulesZ; + return getSectorChamber(iphi, iz); +} + +//============================================================================== +std::pair Geometry::getSectorChamber(int iphi, int iz) const +{ + int chamber = iz < mNSamplingModulesZ ? 0 : (iz < mNSamplingModulesZ + 2 * mNCrystalModulesZ ? 1 : 2); + int sector = iphi / (chamber == 1 ? mNCrystalModulesPhi : mNSamplingModulesPhi); + return {sector, chamber}; +} + +//============================================================================== +void Geometry::detIdToRelIndex(int cellId, int& chamber, int& sector, int& iphi, int& iz) const +{ + // 3 chambers - sampling/crystal/sampling + iphi = cellId / mNModulesZ; + iz = cellId % mNModulesZ; + auto pair = getSectorChamber(iphi, iz); + sector = pair.first; + chamber = pair.second; +} + +//============================================================================== +void Geometry::detIdToGlobalPosition(int detId, double& x, double& y, double& z) +{ + int chamber, sector, iphi, iz; + detIdToRelIndex(detId, chamber, sector, iphi, iz); + if (iz < mNSamplingModulesZ + mNCrystalModulesZ) { + z = -mFrontFaceCenterZ[mNSamplingModulesZ + mNCrystalModulesZ - iz - 1]; + } else { + z = +mFrontFaceCenterZ[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; + } + double phi = chamber == 1 ? mFrontFaceCenterCrystalPhi[iphi] : mFrontFaceCenterSamplingPhi[iphi]; + double r = mFrontFaceCenterR[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; + x = r * std::cos(phi); + y = r * std::sin(phi); +} + +//============================================================================== +int Geometry::areNeighboursVertex(int detId1, int detId2) const +{ + int ch1, sector1, iphi1, iz1; + int ch2, sector2, iphi2, iz2; + detIdToRelIndex(detId1, ch1, sector1, iphi1, iz1); + detIdToRelIndex(detId2, ch2, sector2, iphi2, iz2); + if (sector1 != sector2 || ch1 != ch2) + return 0; + if (std::abs(iphi1 - iphi2) <= 1 && std::abs(iz1 - iz2) <= 1) + return 1; + return 0; +} + +//============================================================================== +bool Geometry::isAtTheEdge(int cellId) +{ + auto [row, col] = globalRowColFromIndex(cellId); + if (col == 0) + return 1; + if (col == mNSamplingModulesZ) + return 1; + if (col == mNSamplingModulesZ - 1) + return 1; + if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ) + return 1; + if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ - 1) + return 1; + if (col == mNModulesZ - 1) + return 1; + for (int m = 0; m <= mNSuperModules; m++) { + if (isCrystal(cellId)) { + if (row == m * mNCrystalModulesPhi) + return 1; + if (row == m * mNCrystalModulesPhi - 1) + return 1; + } else { + if (row == m * mNSamplingModulesPhi) + return 1; + if (row == m * mNSamplingModulesPhi - 1) + return 1; + } + } + return 0; +} diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx index 49f57d8a8c5cc..aca4f5548dc51 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/GeometryTGeo.cxx @@ -9,6 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GeometryTGeo.cxx +/// \brief Class containing ECal volume naming patterns +/// +/// \author Evgeny Kryshen + #include #include @@ -19,7 +24,8 @@ namespace ecal std::unique_ptr GeometryTGeo::sInstance; std::string GeometryTGeo::sVolumeName = "ECALV"; -std::string GeometryTGeo::sSensorName = "ECALSensor"; +std::string GeometryTGeo::sSectorName = "ECALSector"; +std::string GeometryTGeo::sModuleName = "ECALModule"; GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : DetMatrixCache() { @@ -57,10 +63,15 @@ GeometryTGeo* GeometryTGeo::Instance() return sInstance.get(); } -const char* GeometryTGeo::composeSymNameSensor() +const char* GeometryTGeo::composeSymNameSector(int s) +{ + return Form("%s/%s_%d", composeSymNameECal(), getECalSectorPattern(), s); +} + +const char* GeometryTGeo::composeSymNameModule(int s, int m) { - return Form("%s/%d", composeSymNameECal(), 0); + return Form("%s/%s_%d", composeSymNameSector(s), getECalModulePattern(), m); } } // namespace ecal -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/Hit.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/Hit.cxx new file mode 100644 index 0000000000000..ee2034314d2d8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/Hit.cxx @@ -0,0 +1,34 @@ +// 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 Hit.cxx +/// \brief MC hit class to store energy loss per cell and per superparent +/// +/// \author Evgeny Kryshen + +#include + +ClassImp(o2::ecal::Hit); + +using namespace o2::ecal; + +bool Hit::operator<(const Hit& rhs) const +{ + if (GetTrackID() != rhs.GetTrackID()) { + return GetTrackID() < rhs.GetTrackID(); + } + return GetCellID() < rhs.GetCellID(); +} + +bool Hit::operator==(const Hit& rhs) const +{ + return (GetCellID() == rhs.GetCellID()) && (GetTrackID() == rhs.GetTrackID()); +} diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..f51a9c067d6b3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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. + +o2_add_library(ECalReconstruction + SOURCES src/Clusterizer.cxx + PUBLIC_LINK_LIBRARIES O2::ECalBase + O2::DataFormatsECal + AliceO2::InfoLogger) + +o2_target_root_dictionary(ECalReconstruction + HEADERS include/ECalReconstruction/Clusterizer.h) diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h b/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h new file mode 100644 index 0000000000000..3bb7cab4b11e3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h @@ -0,0 +1,75 @@ +// 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 Clusterizer.h +/// \brief Class for cluster finding and unfolding +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_CLUSTERIZER_H +#define ALICEO2_ECAL_CLUSTERIZER_H + +#include +#include +#include +#include + +using o2::ecal::Cluster; +using o2::ecal::Digit; +class TF1; + +namespace o2 +{ +namespace ecal +{ +class Clusterizer +{ + public: + Clusterizer(bool applyCorrectionZ = 1, bool applyCorrectionE = 1); + ~Clusterizer() = default; + void initialize() {}; + void addDigitToCluster(Cluster& cluster, int row, int col, const gsl::span& digits); + void findClusters(const gsl::span& digits, std::vector& foundClusters, std::vector& unfoldedClusters); + void makeClusters(const gsl::span& digits, std::vector& clusters); + void makeUnfoldings(std::vector& foundClusters, std::vector& unfoldedClusters); + void unfoldOneCluster(Cluster* iniClu, int nMax, int* digitId, float* maxAtEnergy, std::vector& unfoldedClusters); + void evalClusters(std::vector& clusters); + int getNumberOfLocalMax(Cluster& clu, int* maxAt, float* maxAtEnergy); + double showerShape(double dx, double dz, bool isCrystal); + void setLogWeight(double logWeight) { mLogWeight = logWeight; } + void setClusteringThreshold(double threshold) { mClusteringThreshold = threshold; } + void setCrystalDigitThreshold(double threshold) { mCrystalDigitThreshold = threshold; } + void setSamplingDigitThreshold(double threshold) { mSamplingDigitThreshold = threshold; } + + private: + std::vector> mDigitIndices; // 2D map of digit indices used for recursive cluster finding + bool mUnfoldClusters = true; // to perform cluster unfolding + double mCrystalDigitThreshold = 0.040; // minimal energy of crystal digit + double mSamplingDigitThreshold = 0.100; // minimal energy of sampling digit + double mClusteringThreshold = 0.050; // minimal energy of digit to start clustering (GeV) + double mClusteringTimeGate = 1e9; // maximal time difference between digits to be accepted to clusters (in ns) + int mNLMMax = 30; // maximal number of local maxima in unfolding + double mLogWeight = 4.; // cutoff used in log. weight calculation + double mUnfogingEAccuracy = 1.e-4; // accuracy of energy calculation in unfoding prosedure (GeV) + double mUnfogingXZAccuracy = 1.e-2; // accuracy of position calculation in unfolding procedure (cm) + int mNMaxIterations = 100; // maximal number of iterations in unfolding procedure + double mLocalMaximumCut = 0.015; // minimal height of local maximum over neighbours + bool mApplyCorrectionZ = 1; // z-correction + bool mApplyCorrectionE = 1; // energy-correction + TF1* fCrystalShowerShape; //! Crystal shower shape + TF1* fSamplingShowerShape; //! Sampling shower shape + TF1* fCrystalRMS; //! Crystal RMS +}; + +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_CLUSTERIZER_H diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx new file mode 100644 index 0000000000000..c84f62b60ec38 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx @@ -0,0 +1,455 @@ +// 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 Clusterizer.cxx +/// \brief Class for cluster finding and unfolding +/// +/// \author Evgeny Kryshen + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::ecal; + +//============================================================================== +Clusterizer::Clusterizer(bool applyCorrectionZ, bool applyCorrectionE) +{ + auto& geo = Geometry::instance(); + mDigitIndices.resize(geo.getNrows(), std::vector(geo.getNcols(), -1)); + mApplyCorrectionZ = applyCorrectionZ; + mApplyCorrectionE = applyCorrectionE; + fCrystalShowerShape = new TF1("fCrystal", "x<[1] ? [0]*exp([3]*x+[4]*x*x+[5]*x*x*x) : (x<[2] ? [0]*[6]*exp([7]*x+[8]*x*x) : [0]*[9]*exp([10]*x+[11]*x*x))", 0, 15); + double pc[12]; + pc[0] = 1. / 13.15; + pc[1] = 2.2; + pc[2] = 5; + pc[3] = 4.38969; + pc[4] = -5.15975; + pc[5] = 1.18978; + pc[6] = 1.48726; + pc[7] = -1.54621; + pc[8] = 0.0814617; + pc[9] = 0.0369055; + pc[10] = -0.174372; + pc[11] = -0.0455978; + + fCrystalShowerShape->SetParameters(pc); + + fSamplingShowerShape = new TF1("fSampling", "x<[1] ? [0]*exp([3]*x+[4]*x*x+[5]*x*x*x) : (x<[2] ? [0]*[6]*exp([7]*x+[8]*x*x) : [0]*[9]*exp([10]*x+[11]*x*x))", 0, 15); + double ps[12]; + ps[0] = 1 / 35.6; + ps[1] = 3.2; + ps[2] = 6; + ps[3] = 3.06543; + ps[4] = -2.23235; + ps[5] = 0.325344; + ps[6] = 6.0733; + ps[7] = -1.62713; + ps[8] = 0.0965569; + ps[9] = 0.0765706; + ps[10] = -0.217398; + ps[11] = -0.0204646; + fSamplingShowerShape->SetParameters(ps); + + fCrystalRMS = new TF1("fCrystalRMS", "[0]*x*exp([1]*x+[2]*x*x+[3]*x*x*x)", 0, 2.2); + double p[4]; + p[0] = 1.39814; + p[1] = -6.05426; + p[2] = 6.26678; + p[3] = -1.97092; + fCrystalRMS->SetParameters(p); +} + +//============================================================================== +void Clusterizer::findClusters(const gsl::span& digits, std::vector& foundClusters, std::vector& unfoldedClusters) +{ + foundClusters.clear(); + unfoldedClusters.clear(); + + // Collect list of clusters + makeClusters(digits, foundClusters); + + // Split clusters with several local maxima if necessary + makeUnfoldings(foundClusters, unfoldedClusters); + + // Evaluate cluster position, dispersion etc. + evalClusters(foundClusters); + evalClusters(unfoldedClusters); +} + +//============================================================================== +void Clusterizer::addDigitToCluster(Cluster& cluster, int row, int col, const gsl::span& digits) +{ + auto& geo = Geometry::instance(); + if (row < 0 || row >= geo.getNrows() || col < 0 || col >= geo.getNcols()) + return; + int digitIndex = mDigitIndices[row][col]; + LOGP(debug, " checking row={} and col={} digitIndex={}", row, col, digitIndex); + if (digitIndex < 0) + return; + + const Digit& digit = digits[digitIndex]; + if (cluster.getMultiplicity() > 0) { + // check if new digit is in the same chamber and sector + const Digit& digit2 = digits[cluster.getDigitIndex(0)]; + auto [sector1, ch1] = geo.getSectorChamber(digit.getTower()); + auto [sector2, ch2] = geo.getSectorChamber(digit2.getTower()); + LOGP(debug, " checking if sector and chamber are the same: ({},{}) ({},{})", sector1, ch1, sector2, ch2); + if (sector1 != sector2 || ch1 != ch2) + return; + } + + mDigitIndices[row][col] = -1; + cluster.addDigit(digitIndex, digit.getTower(), digit.getEnergy()); + LOGP(debug, " adding new digit at row={} and col={}", row, col); + addDigitToCluster(cluster, row - 1, col, digits); + addDigitToCluster(cluster, row + 1, col, digits); + addDigitToCluster(cluster, row, col - 1, digits); + addDigitToCluster(cluster, row, col + 1, digits); +} + +//============================================================================== +void Clusterizer::makeClusters(const gsl::span& digits, std::vector& clusters) +{ + // Combine digits into cluster + + int nDigits = digits.size(); + + // reset mDigitIndices + for (auto& rows : mDigitIndices) { + rows.assign(rows.size(), -1); + } + + // fill mDigitIndices + auto& geo = Geometry::instance(); + for (int i = 0; i < nDigits; i++) { + const Digit& digit = digits[i]; + auto [row, col] = geo.globalRowColFromIndex(digit.getTower()); + bool isCrystal = geo.isCrystal(digit.getTower()); + if (isCrystal) { + if (digit.getEnergy() < mCrystalDigitThreshold) + continue; + } else { + if (digit.getEnergy() < mSamplingDigitThreshold) + continue; + } + mDigitIndices[row][col] = i; + } + + // add digit seeds to clusters and recursively add neighbours + for (int i = 0; i < nDigits; i++) { + const Digit& digitSeed = digits[i]; + auto [row, col] = geo.globalRowColFromIndex(digitSeed.getTower()); + if (mDigitIndices[row][col] < 0) + continue; // digit was already added in one of the clusters + if (digitSeed.getEnergy() < mClusteringThreshold) + continue; + LOGP(debug, " starting new cluster at row={} and col={}", row, col); + auto& cluster = clusters.emplace_back(); + addDigitToCluster(cluster, row, col, digits); + } + + LOGP(debug, "made {} clusters from {} digits", clusters.size(), nDigits); +} + +//============================================================================== +void Clusterizer::makeUnfoldings(std::vector& foundClusters, std::vector& unfoldedClusters) +{ + // Split cluster if several local maxima are found + if (!mUnfoldClusters) { + return; + } + + int* maxAt = new int[mNLMMax]; + float* maxAtEnergy = new float[mNLMMax]; + + for (auto& clu : foundClusters) { + int nMax = getNumberOfLocalMax(clu, maxAt, maxAtEnergy); + if (nMax > 1) { + unfoldOneCluster(&clu, nMax, maxAt, maxAtEnergy, unfoldedClusters); + } else { + clu.setNLM(1); + unfoldedClusters.emplace_back(clu); + } + } + delete[] maxAt; + delete[] maxAtEnergy; +} + +//============================================================================== +void Clusterizer::unfoldOneCluster(Cluster* iniClu, int nMax, int* digitId, float* maxAtEnergy, std::vector& unfoldedClusters) +{ + // Based on MpdEmcClusterizerKI::UnfoldOneCluster by D. Peresunko + // Performs the unfolding of a cluster with nMax overlapping showers + // Parameters: iniClu cluster to be unfolded + // nMax number of local maxima found (this is the number of new clusters) + // digitId: index of digits, corresponding to local maxima + // maxAtEnergy: energies of digits, corresponding to local maxima + + // Take initial cluster and calculate local coordinates of digits + // To avoid multiple re-calculation of same parameters + int mult = iniClu->getMultiplicity(); + std::vector x(mult); + std::vector y(mult); + std::vector z(mult); + std::vector e(mult); + std::vector> eInClusters(mult, std::vector(nMax)); + + auto& geo = Geometry::instance(); + bool isCrystal = geo.isCrystal(iniClu->getDigitTowerId(0)); + + for (int idig = 0; idig < mult; idig++) { + e[idig] = iniClu->getDigitEnergy(idig); + geo.detIdToGlobalPosition(iniClu->getDigitTowerId(idig), x[idig], y[idig], z[idig]); + } + + // Coordinates of centers of clusters + std::vector xMax(nMax); + std::vector yMax(nMax); + std::vector zMax(nMax); + std::vector eMax(nMax); + + for (int iclu = 0; iclu < nMax; iclu++) { + xMax[iclu] = x[digitId[iclu]]; + yMax[iclu] = y[digitId[iclu]]; + zMax[iclu] = z[digitId[iclu]]; + eMax[iclu] = e[digitId[iclu]]; + } + + std::vector prop(nMax); // proportion of clusters in the current digit + + // Try to decompose cluster to contributions + int nIterations = 0; + bool insuficientAccuracy = true; + + while (insuficientAccuracy && nIterations < mNMaxIterations) { + // Loop over all digits of parent cluster and split their energies between daughter clusters + // according to shower shape + for (int idig = 0; idig < mult; idig++) { + double eEstimated = 0; + for (int iclu = 0; iclu < nMax; iclu++) { + prop[iclu] = eMax[iclu] * showerShape(std::sqrt((x[idig] - xMax[iclu]) * (x[idig] - xMax[iclu]) + + (y[idig] - yMax[iclu]) * (y[idig] - yMax[iclu])), + z[idig] - zMax[iclu], isCrystal); + eEstimated += prop[iclu]; + } + if (eEstimated == 0.) { // numerical accuracy + continue; + } + // Split energy of digit according to contributions + for (int iclu = 0; iclu < nMax; iclu++) { + eInClusters[idig][iclu] = e[idig] * prop[iclu] / eEstimated; + } + } + // Recalculate parameters of clusters and check relative variation of energy and absolute of position + insuficientAccuracy = false; // will be true if at least one parameter changed too much + for (int iclu = 0; iclu < nMax; iclu++) { + double oldX = xMax[iclu]; + double oldY = yMax[iclu]; + double oldZ = zMax[iclu]; + double oldE = eMax[iclu]; + // new energy, need for weight + eMax[iclu] = 0; + for (int idig = 0; idig < mult; idig++) { + eMax[iclu] += eInClusters[idig][iclu]; + } + xMax[iclu] = 0; + yMax[iclu] = 0; + zMax[iclu] = 0; + double wtot = 0.; + for (int idig = 0; idig < mult; idig++) { + double w = std::max(std::log(eInClusters[idig][iclu] / eMax[iclu]) + mLogWeight, 0.); + xMax[iclu] += x[idig] * w; + yMax[iclu] += y[idig] * w; + zMax[iclu] += z[idig] * w; + wtot += w; + } + if (wtot > 0.) { + xMax[iclu] /= wtot; + yMax[iclu] /= wtot; + zMax[iclu] /= wtot; + } + // Compare variation of parameters + insuficientAccuracy += (std::abs(eMax[iclu] - oldE) > mUnfogingEAccuracy); + insuficientAccuracy += (std::abs(xMax[iclu] - oldX) > mUnfogingXZAccuracy); + insuficientAccuracy += (std::abs(yMax[iclu] - oldY) > mUnfogingXZAccuracy); + insuficientAccuracy += (std::abs(zMax[iclu] - oldZ) > mUnfogingXZAccuracy); + } + nIterations++; + } + + // Iterations finished, add new clusters + for (int iclu = 0; iclu < nMax; iclu++) { + auto& clu = unfoldedClusters.emplace_back(); + clu.setNLM(nMax); + for (int idig = 0; idig < mult; idig++) { + int jdigit = iniClu->getDigitIndex(idig); + int towerId = iniClu->getDigitTowerId(idig); + clu.addDigit(jdigit, towerId, eInClusters[idig][iclu]); + } + } +} + +//============================================================================== +void Clusterizer::evalClusters(std::vector& clusters) +{ + auto& geo = Geometry::instance(); + for (auto& cluster : clusters) { + double x = 0; + double y = 0; + double z = 0; + double wtot = 0; + double etot = cluster.getE(); + for (size_t i = 0; i < cluster.getMultiplicity(); i++) { + float energy = cluster.getDigitEnergy(i); + int towerId = cluster.getDigitTowerId(i); + double xi, yi, zi; + geo.detIdToGlobalPosition(towerId, xi, yi, zi); + double w = std::max(0., mLogWeight + std::log(energy / etot)); + x += w * xi; + y += w * yi; + z += w * zi; + wtot += w; + } + if (wtot != 0) { + x /= wtot; + y /= wtot; + z /= wtot; + } + cluster.setX(x); + cluster.setY(y); + cluster.setZ(z); + + // cluster shape + float chi2 = 0; + int ndf = 0; + float ee = cluster.getE(); + for (size_t i = 0; i < cluster.getMultiplicity(); i++) { + float energy = cluster.getDigitEnergy(i); + int towerId = cluster.getDigitTowerId(i); + double xi, yi, zi; + geo.detIdToGlobalPosition(towerId, xi, yi, zi); + double r = std::sqrt((x - xi) * (x - xi) + (y - yi) * (y - yi) + (z - zi) * (z - zi)); + if (r > 2.2) + continue; + double frac = fCrystalShowerShape->Eval(r); + double rms = fCrystalRMS->Eval(r); + chi2 += std::pow((energy / ee - frac) / rms, 2.); + ndf++; + } + cluster.setChi2(chi2 / ndf); + + // correct cluster energy and z position + float eta = std::abs(cluster.getEta()); + float eCor = 1; + float zCor = 0; + bool isCrystal = geo.isCrystal(cluster.getDigitTowerId(0)); + if (isCrystal) { + eCor = 0.00444 * std::pow(ee, -1.322) + (1.021 + 0.0018 * eta); + if (mApplyCorrectionE) + ee *= eCor; + if (mApplyCorrectionZ) + zCor = (-0.00518682 + 0.730052 * eta - 0.73817 * eta * eta); + } else { + eCor = 0.0033 * std::pow(ee, -2.09) + (1.007 + 0.0667 * eta - 0.108 * eta * eta + 0.0566 * eta * eta * eta); + if (mApplyCorrectionE) + ee *= eCor; + if (mApplyCorrectionZ) + zCor = (-2.13679 + 6.40009 * eta - 3.34233 * eta * eta) + (-0.136425 + 0.401887 * eta - 0.196851 * eta * eta) * ee + (0.00822276 - 0.0242512 * eta + 0.0118986 * eta * eta) * ee * ee; + } + + cluster.setE(ee); + cluster.setZ(cluster.getZ() - zCor); + + // check if cluster is at the edge of detector module + bool isEdge = 0; + for (size_t i = 0; i < cluster.getMultiplicity(); i++) { + int towerId = cluster.getDigitTowerId(i); + if (!geo.isAtTheEdge(towerId)) + continue; + isEdge = 1; + break; + } + cluster.setEdgeFlag(isEdge); + + LOGF(debug, "Cluster coordinates: (%6.2f,%6.2f,%6.2f), eCor=%6.2f zCor=%6.2f", cluster.getX(), cluster.getY(), cluster.getZ(), eCor, zCor); + } +} + +//============================================================================== +int Clusterizer::getNumberOfLocalMax(Cluster& clu, int* maxAt, float* maxAtEnergy) +{ + // Based on MpdEmcClusterizerKI::GetNumberOfLocalMax by D. Peresunko + // Calculates the number of local maxima in the cluster using LocalMaxCut as the minimum + // energy difference between maximum and surrounding digits + auto& geo = Geometry::instance(); + int n = clu.getMultiplicity(); + bool isCrystal = geo.isCrystal(clu.getDigitTowerId(0)); + bool* isLocalMax = new bool[n]; + + for (int i = 0; i < n; i++) { + isLocalMax[i] = false; + float en1 = clu.getDigitEnergy(i); + if (en1 > mClusteringThreshold) + isLocalMax[i] = true; + } + + for (int i = 0; i < n; i++) { + int detId1 = clu.getDigitTowerId(i); + float en1 = clu.getDigitEnergy(i); + for (int j = i + 1; j < n; j++) { + int detId2 = clu.getDigitTowerId(j); + float en2 = clu.getDigitEnergy(j); + if (geo.areNeighboursVertex(detId1, detId2) == 1) { + if (en1 > en2) { + isLocalMax[j] = false; + // but may be digit too is not local max ? + if (en2 > en1 - mLocalMaximumCut) { + isLocalMax[i] = false; + } + } else { + isLocalMax[i] = false; + // but may be digitN is not local max too? + if (en1 > en2 - mLocalMaximumCut) { + isLocalMax[j] = false; + } + } + } // if neighbours + } // digit j + } // digit i + + int iDigitN = 0; + for (int i = 0; i < n; i++) { + if (isLocalMax[i]) { + maxAt[iDigitN] = i; + maxAtEnergy[iDigitN] = clu.getDigitEnergy(i); + iDigitN++; + if (iDigitN >= mNLMMax) { // Note that size of output arrays is limited: + LOGP(error, "Too many local maxima, cluster multiplicity {} region={}", n, isCrystal ? "crystal" : "sampling"); + return 0; + } + } + } + delete[] isLocalMax; + return iDigitN; +} + +//============================================================================== +double Clusterizer::showerShape(double dx, double dz, bool isCrystal) +{ + double x = std::sqrt(dx * dx + dz * dz); + return isCrystal ? fCrystalShowerShape->Eval(x) : fSamplingShowerShape->Eval(x); +} diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/ECalReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/ECalReconstructionLinkDef.h new file mode 100644 index 0000000000000..d69cd8164e717 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/ECalReconstructionLinkDef.h @@ -0,0 +1,20 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::ecal::Clusterizer + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt index 8c8c5a6bba15f..83de48e38db3a 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/CMakeLists.txt @@ -11,8 +11,13 @@ o2_add_library(ECalSimulation SOURCES src/Detector.cxx + src/Digitizer.cxx PUBLIC_LINK_LIBRARIES O2::ECalBase - O2::ITSMFTSimulation) + O2::DataFormatsECal) o2_target_root_dictionary(ECalSimulation - HEADERS include/ECalSimulation/Detector.h) \ No newline at end of file + HEADERS include/ECalSimulation/Detector.h + include/ECalSimulation/Digitizer.h + ) + +o2_data_file(COPY data DESTINATION Detectors/ECL/simulation) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/data/simcuts.dat b/Detectors/Upgrades/ALICE3/ECal/simulation/data/simcuts.dat new file mode 100644 index 0000000000000..81aa69990f222 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/data/simcuts.dat @@ -0,0 +1,14 @@ +* ECAL +* ==== +* +* Med GAM ELEC NHAD CHAD MUON EBREM MUHAB EDEL MUDEL MUPA ANNI BREM COMP DCAY DRAY HADR LOSS MULS PAIR PHOT RAYL STRA +* Air +ECL 0 5.e-5 1.e-4 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. 1 1 1 1 1 1 1 1 1 1 1 -1 +* Lead +ECL 1 1.e-4 1.e-4 1.e-3 1.e-3 1.e-3 1.e-4 1.e-4 1.e-4 1.e-4 -1. 1 1 1 1 1 1 3 1 1 1 1 -1 +* Scintillator +ECL 2 1.e-4 1.e-4 1.e-3 1.e-3 1.e-3 1.e-4 1.e-4 1.e-4 1.e-4 -1. 1 1 1 1 1 1 3 1 1 1 1 -1 +* Aluminium +ECL 3 1.e-4 1.e-4 1.e-3 1.e-3 1.e-3 1.e-4 1.e-4 1.e-4 1.e-4 -1. 1 1 1 1 1 1 3 1 1 1 1 -1 +* PWO +ECL 4 5.e-5 1.e-4 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. 1 1 1 1 1 1 1 1 1 1 1 -1 diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h index 14664092a8718..849dd69f85f2b 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Detector.h @@ -8,18 +8,18 @@ // 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. -// -// Design and equations: Nicola Nicassio nicola.nicassio@cern.ch -// + +/// \file Detector.h +/// \brief ECal geometry creation and hit processing +/// +/// \author Evgeny Kryshen #ifndef ALICEO2_ECAL_DETECTOR_H #define ALICEO2_ECAL_DETECTOR_H -#include "DetectorsBase/Detector.h" -#include "ITSMFTSimulation/Hit.h" - -#include "ECalBase/GeometryTGeo.h" - +#include +#include +#include #include #include @@ -27,62 +27,35 @@ namespace o2 { namespace ecal { - class Detector : public o2::base::DetImpl { public: - Detector(bool active); - Detector(); + Detector(bool active = 1); ~Detector(); - void ConstructGeometry() override; - - o2::itsmft::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); - // Mandatory overrides - void BeginPrimary() override { ; } - void FinishPrimary() override { ; } + void ConstructGeometry() override; + void BeginPrimary() override {} + void FinishPrimary() override {} void InitializeO2Detector() override; - void PostTrack() override { ; } - void PreTrack() override { ; } + void PostTrack() override {} + void PreTrack() override {} bool ProcessHits(FairVolume* v = nullptr) override; - void EndOfEvent() override; + void EndOfEvent() override { Reset(); } void Register() override; void Reset() override; + std::vector* getHits(int iColl) const { return !iColl ? mHits : nullptr; } - // Custom memer functions - std::vector* getHits(int iColl) const - { - if (!iColl) { - return mHits; - } - return nullptr; - } - + private: void createMaterials(); void createGeometry(); - - private: - // Transient data about track passing the sensor - struct TrackData { - bool mHitStarted; // hit creation started - unsigned char mTrkStatusStart; // track status flag - TLorentzVector mPositionStart; // position at entrance - TLorentzVector mMomentumStart; // momentum - double mEnergyLoss; // energy loss - } mTrackData; //! transient data - - GeometryTGeo* mGeometryTGeo; //! - std::vector* mHits; // ITSMFT ones for the moment - - void defineSensitiveVolumes(); - float mInnerRadius; - float mOuterRadius; - float mLength; - - bool mEnableEndcap{true}; + void defineSamplingFactor(); + std::unordered_map mSuperParentIndices; //! Super parent indices (track index - superparent index) + int currentTrackId = -1; // current track index + int superparentId = -1; // superparent index + GeometryTGeo* mGeometryTGeo; //! + std::vector* mHits; //! + double mSamplingFactorTransportModel = 1.; protected: template @@ -104,4 +77,5 @@ struct UseShm { } // namespace base } // namespace o2 #endif -#endif \ No newline at end of file + +#endif // ALICEO2_ECAL_DETECTOR_H \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Digitizer.h new file mode 100644 index 0000000000000..91213fa90b63a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/include/ECalSimulation/Digitizer.h @@ -0,0 +1,58 @@ +// 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 Digitizer.h +/// \brief Digitization of ECal MC information +/// +/// \author Evgeny Kryshen + +#ifndef ALICEO2_ECAL_DIGITIZER_H +#define ALICEO2_ECAL_DIGITIZER_H +#include +#include +#include +#include +#include + +using o2::ecal::Digit; +using o2::ecal::Hit; +using o2::ecal::MCLabel; + +namespace o2 +{ +namespace ecal +{ +class Digitizer +{ + public: + Digitizer(); + ~Digitizer() = default; + Digitizer(const Digitizer&) = delete; + Digitizer& operator=(const Digitizer&) = delete; + void init() {} + void finish() {} + void processHits(const std::vector* mHits, std::vector& digits, o2::dataformats::MCTruthContainer& labels, int collId); + void setThreshold(double threshold) { mThreshold = threshold; } + void setSmearCrystal(bool smearCrystal) { mSmearCrystal = smearCrystal; } + void setSamplingFraction(double fraction) { mSamplingFraction = fraction; } + void setCrystalPePerGeV(double pePerGeV) { mCrystalPePerGeV = pePerGeV; } + + private: + std::vector mArrayD; + bool mSmearCrystal = 0; + double mThreshold = 0.001; + double mSamplingFraction = 9.8; + double mCrystalPePerGeV = 4000; +}; +} // namespace ecal +} // namespace o2 + +#endif // ALICEO2_ECAL_DIGITIZER_H diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx index aeb58649fa4c5..93089bb8ced14 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx @@ -9,45 +9,40 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include +/// \file Detector.cxx +/// \brief ECal geometry creation and hit processing +/// +/// \author Evgeny Kryshen +#include #include #include +#include #include #include -#include - -#include "DetectorsBase/Stack.h" -#include "ITSMFTSimulation/Hit.h" -#include "ECalSimulation/Detector.h" -#include "ECalBase/ECalBaseParam.h" - -using o2::itsmft::Hit; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using o2::ecal::Hit; namespace o2 { namespace ecal { - -Detector::Detector() - : o2::base::DetImpl("ECL", true), - mTrackData(), - mHits(o2::utils::createSimVector()) -{ -} - Detector::Detector(bool active) - : o2::base::DetImpl("ECL", true), - mTrackData(), - mHits(o2::utils::createSimVector()) + : o2::base::DetImpl("ECL", active), + mHits(o2::utils::createSimVector()) { - auto& ecalPars = ECalBaseParam::Instance(); - mInnerRadius = ecalPars.rMin; - mOuterRadius = ecalPars.rMax; - mLength = ecalPars.zLength; - mEnableEndcap = ecalPars.enableFwdEndcap; } +//============================================================================== Detector::~Detector() { if (mHits) { @@ -55,190 +50,344 @@ Detector::~Detector() } } +//============================================================================== void Detector::ConstructGeometry() { createMaterials(); createGeometry(); + defineSamplingFactor(); } +//============================================================================== +void Detector::defineSamplingFactor() +{ + TString mcname = TVirtualMC::GetMC()->GetName(); + TString mctitle = TVirtualMC::GetMC()->GetTitle(); + LOGP(info, "Defining sampling factor for mc={}' and title='{}'", mcname.Data(), mctitle.Data()); + if (mcname.Contains("Geant3")) { + mSamplingFactorTransportModel = 0.983; + } else if (mcname.Contains("Geant4")) { + mSamplingFactorTransportModel = 1.; + } +} + +//============================================================================== void Detector::createMaterials() { - int ifield = 2; // ? - float fieldm = 10.0; // ? + LOGP(info, "Creating materials for ECL"); + + // Air + float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948}; + float zAir[4] = {6., 7., 8., 18.}; + float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827}; + float dAir = 1.20479E-3; + Mixture(0, "Air", aAir, zAir, dAir, 4, wAir); + + // Pb + Material(1, "Pb", 207.2, 82, 11.35, 0.56, 0., nullptr, 0); + + // Polysterene scintillator (CH) + float aP[2] = {12.011, 1.00794}; + float zP[2] = {6.0, 1.0}; + float wP[2] = {1.0, 1.0}; + float dP = 1.032; + Mixture(2, "Scintillator", aP, zP, dP, -2, wP); + + // Al + Material(3, "Al", 26.98, 13., 2.7, 8.9, 999., nullptr, 0); + + // PWO crystals + float aX[3] = {207.19, 183.85, 16.0}; + float zX[3] = {82.0, 74.0, 8.0}; + float wX[3] = {1.0, 1.0, 4.0}; + float dX = 8.28; + Mixture(4, "PbWO4", aX, zX, dX, -3, wX); + + int ifield = 2; // magnetic field flag + float fieldm = 10.; // maximum field value (in Kilogauss) + float deemax = 0.1; // maximum fractional energy loss in one step (0 < deemax <=1) + float tmaxfd = 10.; // maximum angle due to field permitted in one step (in degrees) o2::base::Detector::initFieldTrackingParams(ifield, fieldm); - - float tmaxfdLead = 0.1; // .10000E+01; // Degree - float stemaxLead = .10000E+01; // cm - float deemaxLead = 0.1; // 0.30000E-02; // Fraction of particle's energy 0clear(); + } + mSuperParentIndices.clear(); + currentTrackId = -1; + superparentId = -1; } -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 - + LOGP(info, "Registering hits"); if (FairRootManager::Instance()) { FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true); } } +//============================================================================== void Detector::createGeometry() { LOGP(info, "Creating ECal geometry"); - TGeoManager* geoManager = gGeoManager; - TGeoVolume* vALIC = geoManager->GetVolume("barrel"); + TGeoVolume* vALIC = gGeoManager->GetVolume("barrel"); if (!vALIC) { LOGP(fatal, "Could not find barrel volume while constructing ECal geometry"); } new TGeoVolumeAssembly(GeometryTGeo::getECalVolPattern()); - TGeoVolume* vECal = geoManager->GetVolume(GeometryTGeo::getECalVolPattern()); + TGeoVolume* vECal = gGeoManager->GetVolume(GeometryTGeo::getECalVolPattern()); vALIC->AddNode(vECal, 2, new TGeoTranslation(0, 30., 0)); + vECal->SetTitle("ECalVol"); + + TGeoMedium* medAir = gGeoManager->GetMedium("ECL_Air"); + TGeoMedium* medPb = gGeoManager->GetMedium("ECL_Pb"); + TGeoMedium* medAl = gGeoManager->GetMedium("ECL_Al"); + TGeoMedium* medSc = gGeoManager->GetMedium("ECL_Scintillator"); + TGeoMedium* medPWO = gGeoManager->GetMedium("ECL_Crystal"); + + // Get relevant parameters + auto& pars = ECalBaseParam::Instance(); + auto& geo = Geometry::instance(); + + double rMin = pars.rMin; + double rMax = pars.rMax; + double layerThickness = pars.pbLayerThickness + pars.scLayerThickness; + double samplingModL = pars.frontPlateThickness + layerThickness * pars.nSamplingLayers - pars.pbLayerThickness; + double crystalAlpha = geo.getCrystalAlpha(); + double samplingAlpha = geo.getSamplingAlpha(); + double tanCrystalAlpha = std::tan(crystalAlpha); + double tanSamplingAlpha = std::tan(samplingAlpha); + + double sectorL = rMax - rMin; + double crystalThetaMin = geo.getFrontFaceCenterTheta(pars.nCrystalModulesZ - 1) - crystalAlpha; + double crystalHlMin = geo.getFrontFaceZatMinR(pars.nCrystalModulesZ - 1); + double crystalHlMax = crystalHlMin + sectorL / std::tan(crystalThetaMin); + double crystalHwMin = geo.getCrystalModW() / 2.; + double crystalHwMax = crystalHwMin * rMax / rMin; + auto crystalSectorShape = new TGeoTrap(sectorL / 2., 0, 0, crystalHlMin, crystalHwMin, crystalHwMin, 0, crystalHlMax, crystalHwMax, crystalHwMax, 0); + auto crystalSectorVolume = new TGeoVolume("crystalSectorVolume", crystalSectorShape, medAir); + AddSensitiveVolume(crystalSectorVolume); + crystalSectorVolume->SetLineColor(kCyan + 1); + crystalSectorVolume->SetTransparency(0); + + double samplingThetaAtMinZ = geo.getFrontFaceCenterTheta(pars.nCrystalModulesZ) + samplingAlpha; + double samplingThetaAtMaxZ = geo.getFrontFaceCenterTheta(pars.nCrystalModulesZ + pars.nSamplingModulesZ - 1) - samplingAlpha; + double samplingMinZatMinR = geo.getFrontFaceZatMinR(pars.nCrystalModulesZ) - geo.getSamplingModW() / std::sin(samplingThetaAtMinZ); + double samplingMaxZatMinR = geo.getFrontFaceZatMinR(pars.nCrystalModulesZ + pars.nSamplingModulesZ - 1); + double samplingMinZatMaxR = samplingMinZatMinR + sectorL / std::tan(samplingThetaAtMinZ); + double samplingMaxZatMaxR = samplingMaxZatMinR + sectorL / std::tan(samplingThetaAtMaxZ); + double hlMin = (samplingMaxZatMinR - samplingMinZatMinR) / 2.; + double hlMax = (samplingMaxZatMaxR - samplingMinZatMaxR) / 2.; + double zCenterMin = (samplingMaxZatMinR + samplingMinZatMinR) / 2.; + double zCenterMax = (samplingMaxZatMaxR + samplingMinZatMaxR) / 2.; + double zCenter = (zCenterMax + zCenterMin) / 2.; + double thetaCenter = std::atan((zCenterMax - zCenterMin) / sectorL) * TMath::RadToDeg(); + double samplingHwMin = geo.getSamplingModW() / 2.; + double samplingHwMax = samplingHwMin * rMax / rMin; + auto samplingSectorShape = new TGeoTrap(sectorL / 2., thetaCenter, 90, hlMin, samplingHwMin, samplingHwMin, 0, hlMax, samplingHwMax, samplingHwMax, 0); + auto samplingSectorVolume = new TGeoVolume("samplingSectorVolume", samplingSectorShape, medAir); + AddSensitiveVolume(samplingSectorVolume); + samplingSectorVolume->SetLineColor(kBlue + 1); + samplingSectorVolume->SetTransparency(0); + + double sectorR = rMin + sectorL / 2.; + for (int ism = 0; ism < pars.nSuperModules; ism++) { + // crystal + for (int i = 0; i < pars.nCrystalModulesPhi; i++) { + int row = ism * pars.nCrystalModulesPhi + i; + double phi0 = geo.getFrontFaceCenterCrystalPhi(row); + double x = sectorR * std::cos(phi0); + double y = sectorR * std::sin(phi0); + auto rot = new TGeoRotation(Form("ecalcrystalsecrot%d", row), 90 + phi0 * TMath::RadToDeg(), 90, 0); + vECal->AddNode(crystalSectorVolume, row, new TGeoCombiTrans(x, y, 0., rot)); + } + // sampling + for (int i = 0; i < pars.nSamplingModulesPhi; i++) { + int row = ism * pars.nSamplingModulesPhi + i; + double phi0 = geo.getFrontFaceCenterSamplingPhi(row); + double x = sectorR * std::cos(phi0); + double y = sectorR * std::sin(phi0); + auto rot1 = new TGeoRotation(Form("ecalsamplingsec1rot%d", row), 90 + phi0 * TMath::RadToDeg(), 90, 0.); + auto rot2 = new TGeoRotation(Form("ecalsamplingsec2rot%d", row), 90 + phi0 * TMath::RadToDeg(), 90, 180); + vECal->AddNode(samplingSectorVolume, 2 * row + 0, new TGeoCombiTrans(x, y, zCenter, rot1)); + vECal->AddNode(samplingSectorVolume, 2 * row + 1, new TGeoCombiTrans(x, y, -zCenter, rot2)); + } + } - char vstrng[100] = "ECalVol"; - vECal->SetTitle(vstrng); + for (int m = 0; m < pars.nCrystalModulesZ; m++) { + double tanBeta = geo.getTanBeta(m); + double dx1 = crystalHwMin; + double dx2 = crystalHwMin + pars.crystalModuleLength * tanCrystalAlpha; + double dy1 = crystalHwMin; + double dy2 = crystalHwMin + pars.crystalModuleLength * tanBeta; + double dz = pars.crystalModuleLength / 2.; + auto crystalModuleShape = new TGeoTrd2(dx1, dx2, dy1, dy2, dz); + auto crystalModuleVolume = new TGeoVolume(Form("crystalmodule%d", m), crystalModuleShape, medPWO); + AddSensitiveVolume(crystalModuleVolume); + crystalModuleVolume->SetLineColor(kCyan + 1); + crystalModuleVolume->SetTransparency(0); + double theta = geo.getFrontFaceCenterTheta(m); + double r = geo.getFrontFaceCenterR(m); + double z = geo.getFrontFaceCenterZ(m); + ROOT::Math::XYPoint pFrontFace(z, r - sectorR); + ROOT::Math::Polar2DVector vFrontFaceToCenter(dz, theta); + ROOT::Math::XYPoint pc = pFrontFace + vFrontFaceToCenter; + auto rot1 = new TGeoRotation(Form("ecalcrystalrot%d", 2 * m), 0, 270 + theta * TMath::RadToDeg(), 90); + crystalSectorVolume->AddNode(crystalModuleVolume, 2 * m, new TGeoCombiTrans(0, pc.x(), pc.y(), rot1)); + auto rot2 = new TGeoRotation(Form("ecalcrystalrot%d", 2 * m + 1), 0, 90 - theta * TMath::RadToDeg(), 90); + crystalSectorVolume->AddNode(crystalModuleVolume, 2 * m + 1, new TGeoCombiTrans(0, -pc.x(), pc.y(), rot2)); + } - // Build the ECal cylinder - auto& matmgr = o2::base::MaterialManager::Instance(); - TGeoMedium* medPb = matmgr.getTGeoMedium("ECL_LEAD"); - TGeoTube* ecalShape = new TGeoTube("ECLsh", mInnerRadius, mOuterRadius, mLength); - TGeoVolume* ecalVol = new TGeoVolume("ECL", ecalShape, medPb); - ecalVol->SetLineColor(kAzure - 9); - ecalVol->SetTransparency(0); - vECal->AddNode(ecalVol, 1, nullptr); + for (int m = 0; m < pars.nSamplingModulesZ; m++) { + int k = pars.nCrystalModulesZ + m; + double tanBeta = geo.getTanBeta(k); + double dx1 = samplingHwMin; + double dx2 = samplingHwMin + samplingModL * tanSamplingAlpha; + double dy1 = samplingHwMin; + double dy2 = samplingHwMin + samplingModL * tanBeta; + double dz = samplingModL / 2.; + auto samplingModuleShape = new TGeoTrd2(dx1, dx2, dy1, dy2, dz); + auto samplingModuleVolume = new TGeoVolume(Form("samplingmodule%d", m), samplingModuleShape, medSc); + AddSensitiveVolume(samplingModuleVolume); + samplingModuleVolume->SetLineColor(kAzure - 9); + samplingModuleVolume->SetTransparency(0); + double theta = geo.getFrontFaceCenterTheta(k); + double r = geo.getFrontFaceCenterR(k); + double z = geo.getFrontFaceCenterZ(k); + ROOT::Math::XYPoint pFrontFace(z - zCenter, r - sectorR); + ROOT::Math::Polar2DVector vFrontFaceToCenter(dz, theta); + ROOT::Math::XYPoint pc = pFrontFace + vFrontFaceToCenter; + auto rot1 = new TGeoRotation(Form("ecalsamplingrot%d", m), 0, 270 + theta * TMath::RadToDeg(), 90); + samplingSectorVolume->AddNode(samplingModuleVolume, m, new TGeoCombiTrans(0, pc.x(), pc.y(), rot1)); + + // adding front aluminium plate into the volume + double fdx1 = dx1; + double fdx2 = dx1 + pars.frontPlateThickness * tanSamplingAlpha; + double fdy1 = dy1; + double fdy2 = fdy1 + pars.frontPlateThickness * tanBeta; + double fdz = pars.frontPlateThickness / 2.; + auto frontShape = new TGeoTrd2(fdx1, fdx2, fdy1, fdy2, fdz); + auto frontVolume = new TGeoVolume(Form("front%d", m), frontShape, medAl); + samplingModuleVolume->AddNode(frontVolume, 0, new TGeoTranslation(0., 0., -dz + pars.frontPlateThickness / 2.)); + AddSensitiveVolume(frontVolume); + frontVolume->SetLineColor(kAzure - 7); + frontVolume->SetTransparency(0); + + // adding lead plates + for (int i = 0; i < pars.nSamplingLayers - 1; i++) { + double lz1 = pars.frontPlateThickness + pars.scLayerThickness + layerThickness * i; + double lz2 = lz1 + pars.pbLayerThickness; + double lzc = -dz + (lz1 + lz2) / 2.; + double ldx1 = dx1 + lz1 * tanSamplingAlpha; + double ldx2 = dx1 + lz2 * tanSamplingAlpha; + double ldy1 = dy1 + lz1 * tanBeta; + double ldy2 = dy1 + lz2 * tanBeta; + double ldz = pars.pbLayerThickness / 2.; + auto leadShape = new TGeoTrd2(ldx1, ldx2, ldy1, ldy2, ldz); + auto leadVolume = new TGeoVolume(Form("lead%d_%d", m, i), leadShape, medPb); + samplingModuleVolume->AddNode(leadVolume, i, new TGeoTranslation(0., 0., lzc)); + AddSensitiveVolume(leadVolume); + leadVolume->SetLineColor(kAzure - 7); + leadVolume->SetTransparency(0); + } + } - if (mEnableEndcap) { + if (pars.enableFwdEndcap) { // Build the ecal endcap - TGeoTube* ecalEndcapShape = new TGeoTube("ECLECsh", 15.f, 160.f, 0.5 * (mOuterRadius - mInnerRadius)); + TGeoTube* ecalEndcapShape = new TGeoTube("ECLECsh", 15.f, 160.f, 0.5 * (rMax - rMin)); TGeoVolume* ecalEndcapVol = new TGeoVolume("ECLEC", ecalEndcapShape, medPb); ecalEndcapVol->SetLineColor(kAzure - 9); ecalEndcapVol->SetTransparency(0); vECal->AddNode(ecalEndcapVol, 1, new TGeoTranslation(0, 0, -450.f)); } + // gGeoManager->CloseGeometry(); + // gGeoManager->CheckOverlaps(0.0001); } -void Detector::Reset() -{ - if (!o2::utils::ShmManager::Instance().isOperational()) { - mHits->clear(); - } -} - +//============================================================================== bool Detector::ProcessHits(FairVolume* vol) { - // This method is called from the MC stepping - if (!(fMC->TrackCharge())) { - return false; - } - - int lay = vol->getVolumeId(); - int volID = vol->getMCid(); - - // Is it needed to keep a track reference when the outer ITS volume is encountered? + LOGP(debug, "Processing hits"); auto stack = (o2::data::Stack*)fMC->GetStack(); - if (fMC->IsTrackExiting() && (lay == 0)) { - 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; + int trackId = stack->GetCurrentTrackNumber(); + int parentId = stack->GetCurrentParentTrackNumber(); + + if (trackId != currentTrackId) { + auto superparentIndexIt = mSuperParentIndices.find(parentId); + if (superparentIndexIt != mSuperParentIndices.end()) { + superparentId = superparentIndexIt->second; + mSuperParentIndices[trackId] = superparentIndexIt->second; + } else { + // for new incoming tracks the superparent index is equal to the track ID (for recursion) + mSuperParentIndices[trackId] = trackId; + superparentId = trackId; + } + currentTrackId = trackId; } - // 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; + double eloss = fMC->Edep(); + if (eloss < DBL_EPSILON) { + return false; } - // increment energy loss at all steps except entrance - if (!startHit) { - mTrackData.mEnergyLoss += fMC->Edep(); - } - if (!(startHit | stopHit)) { - return false; // do noting + TString volName = vol->GetName(); + bool isCrystal = volName.Contains("crystalmodule"); + bool isSampling = volName.Contains("samplingmodule"); + + if (!isCrystal && !isSampling) { + return false; } - if (startHit) { - mTrackData.mEnergyLoss = 0.; - fMC->TrackMomentum(mTrackData.mMomentumStart); - fMC->TrackPosition(mTrackData.mPositionStart); - mTrackData.mTrkStatusStart = status; - mTrackData.mHitStarted = true; + if (isCrystal) + LOGP(debug, "Processing crystal {}", volName.Data()); + else { + eloss *= mSamplingFactorTransportModel; + LOGP(debug, "Processing scintillator {}", volName.Data()); } - 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); - - Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, 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 + int sectorId, moduleId; + fMC->CurrentVolID(moduleId); + fMC->CurrentVolOffID(1, sectorId); + int cellID = Geometry::instance().getCellID(moduleId, sectorId, isCrystal); + LOGP(debug, "isCrystal={} sectorId={} moduleId={} cellID={} eloss={}", isCrystal, sectorId, moduleId, cellID, eloss); + + int trackID = superparentId; + auto hit = std::find_if(mHits->begin(), mHits->end(), [cellID, trackID](const Hit& hit) { return hit.GetTrackID() == trackID && hit.GetCellID() == cellID; }); + if (hit == mHits->end()) { + float posX, posY, posZ, momX, momY, momZ, energy; + fMC->TrackPosition(posX, posY, posZ); + fMC->TrackMomentum(momX, momY, momZ, energy); + auto pos = math_utils::Point3D(posX, posY, posZ); + auto mom = math_utils::Vector3D(momX, momY, momZ); + float time = fMC->TrackTime() * 1e9; // time in ns + mHits->emplace_back(trackID, cellID, pos, mom, time, eloss); stack->addHit(GetDetId()); + } else { + hit->SetEnergyLoss(hit->GetEnergyLoss() + eloss); } - return true; } -o2::itsmft::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()); -} - } // namespace ecal } // namespace o2 -ClassImp(o2::ecal::Detector); \ No newline at end of file +ClassImp(o2::ecal::Detector); diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx new file mode 100644 index 0000000000000..f213ba563d86d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx @@ -0,0 +1,89 @@ +// 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 Digitizer.cxx +/// \brief Digitization of ECal MC information +/// +/// \author Evgeny Kryshen + +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2::ecal; + +//============================================================================== +Digitizer::Digitizer() +{ + auto& geo = Geometry::instance(); + mArrayD.resize(geo.getNrows() * geo.getNcols()); +} + +//============================================================================== +void Digitizer::processHits(const std::vector* hits, std::vector& digits, o2::dataformats::MCTruthContainer& labels, int collId) +{ + digits.clear(); + labels.clear(); + + LOGP(debug, "nHits = {}", hits->size()); + auto& geo = Geometry::instance(); + + for (int i = 0; i < mArrayD.size(); i++) { + mArrayD[i].setAmplitude(0); + mArrayD[i].setTimeStamp(1000); + mArrayD[i].setTower(i); + mArrayD[i].setLabel(-1); + // TODO: simulate noise + } + + for (auto& hit : *hits) { + int cellID = hit.GetCellID(); + double eloss = hit.GetEnergyLoss(); + double t = hit.GetTime(); + double elossSmeared = eloss; + bool isCrystal = geo.isCrystal(cellID); + if (isCrystal) { // crystal + double elossSmearedNpe = gRandom->Poisson(eloss * mCrystalPePerGeV) / mCrystalPePerGeV; + if (mSmearCrystal) + elossSmeared = elossSmearedNpe * gRandom->Gaus(1, 0.007); // light attenuation in crystals + } else { // sampling + elossSmeared *= mSamplingFraction; + } + + Digit& digit = mArrayD[cellID]; + digit.setAmplitude(digit.getAmplitude() + elossSmeared); + if (t < digit.getTimeStamp()) + digit.setTimeStamp(t); // setting earliest time, TODO: add time smearing + LOGF(debug, " crystal: %d cellID = %5d, eloss = %8.5f elossSmeared = %8.5f time = %8.5f", isCrystal, cellID, eloss, elossSmeared, t); + + // Adding MC info + MCLabel label(hit.GetTrackID(), collId, 0, false, hit.GetEnergyLoss()); + int labelIndex = digit.getLabel(); + if (labelIndex == -1) { + labelIndex = labels.getIndexedSize(); + labels.addElement(labelIndex, label); + digit.setLabel(labelIndex); + } else { + labels.addElementRandomAccess(labelIndex, label); + } + } // hits + + for (int i = 0; i < mArrayD.size(); i++) { + if (mArrayD[i].getAmplitude() > mThreshold) { + digits.push_back(mArrayD[i]); + } + } +} diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h index 167342773f196..5d7383f086362 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/ECalSimulationLinkDef.h @@ -17,5 +17,6 @@ #pragma link C++ class o2::ecal::Detector + ; #pragma link C++ class o2::base::DetImpl < o2::ecal::Detector> + ; +#pragma link C++ class o2::ecal::Digitizer + ; #endif From dcbe2c1cfef582255d949ef31b07c9bfe3ae9a41 Mon Sep 17 00:00:00 2001 From: Nicolas Elliot Poffley Date: Sat, 27 Sep 2025 07:52:31 +0200 Subject: [PATCH 17/99] DPL Websocket: Add protocol param to encode_websocket_handshake_reply which allows a response Sec-WebSocket-Accept in handshake. (#14687) DPL Websocket: Add overloaded encode_websocket_handshake_reply which allows a response Sec-WebSocket-Accept in handshake. --- Framework/Core/src/DPLWebSocket.cxx | 2 +- Framework/Core/src/HTTPParser.cxx | 5 +++-- Framework/Core/src/HTTPParser.h | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Framework/Core/src/DPLWebSocket.cxx b/Framework/Core/src/DPLWebSocket.cxx index a39e98c6f5310..d9b6594d5f07c 100644 --- a/Framework/Core/src/DPLWebSocket.cxx +++ b/Framework/Core/src/DPLWebSocket.cxx @@ -276,7 +276,7 @@ void WSDPLHandler::endHeaders() } /// Create an appropriate reply LOG(debug) << "Got upgrade request with nonce " << mHeaders["sec-websocket-key"].c_str(); - std::string reply = encode_websocket_handshake_reply(mHeaders["sec-websocket-key"].c_str()); + std::string reply = encode_websocket_handshake_reply(mHeaders["sec-websocket-key"].c_str(), "dpl"); mHandshaken = true; uv_buf_t bfr = uv_buf_init(strdup(reply.data()), reply.size()); diff --git a/Framework/Core/src/HTTPParser.cxx b/Framework/Core/src/HTTPParser.cxx index 04ca6e8fdce55..fa2ba91722eb0 100644 --- a/Framework/Core/src/HTTPParser.cxx +++ b/Framework/Core/src/HTTPParser.cxx @@ -214,15 +214,16 @@ std::string HTTPParserHelpers::calculateAccept(const char* nonce) return fmt::format("{}", base); } -std::string encode_websocket_handshake_reply(char const* nonce) +std::string encode_websocket_handshake_reply(char const* nonce, const char* protocol) { constexpr auto res = "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Access-Control-Allow-Origin: \"*\"\r\n" + "{}" "Sec-WebSocket-Accept: {}\r\n\r\n"; - return fmt::format(res, HTTPParserHelpers::calculateAccept(nonce)); + return fmt::format(res, protocol && protocol[0] ? fmt::format("Sec-WebSocket-Protocol: {}\r\n", protocol) : "", HTTPParserHelpers::calculateAccept(nonce)); } void parse_http_request(char* start, size_t size, HTTPParser* parser) diff --git a/Framework/Core/src/HTTPParser.h b/Framework/Core/src/HTTPParser.h index b4d92393ca5c9..a3253c7ca3d39 100644 --- a/Framework/Core/src/HTTPParser.h +++ b/Framework/Core/src/HTTPParser.h @@ -125,7 +125,8 @@ std::string encode_websocket_handshake_request(const char* path, const char* pro /// Encodes the server reply for a given websocket connection /// @a nonce the nonce of the request. -std::string encode_websocket_handshake_reply(char const* nonce); +/// @a protocol the websocket subprotocol to confirm (optional) +std::string encode_websocket_handshake_reply(char const* nonce, char const* protocol = ""); /// Encodes the buffer @a src which is @a size long to a number of buffers suitable to be sent via libuv. /// If @a binary is provided the binary bit is set. From ea94a3ed159bd8b7a4449844448cea83e7d5afa8 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 25 Sep 2025 23:25:54 +0400 Subject: [PATCH 18/99] Methods for layer-dependend mat.LUT rescaling See the macro O2/Detectors/Base/test/rescaleLUT.C as an example, which writes a rescaled LUT to the _rescaled.root file. The rescaling parameters are provided as a set of RescRange structs: radial range + scaling coefficient. It uses a method o2::base::MatLayerCylSet::scaleLayersByR(rmin,rmax, factor, bool scaleX2X0=true, bool scaleRho=true). All LUT layers overlapping with rmin:rmax range will be rescaled. Alternatively, one can use directly the method o2::base::MatLayerCylSet::scaleLayersByID(lrIDmin,lrIDmax, factor, bool scaleX2X0=true, bool scaleRho=true) to scale a set of layers [lrIDmin:lrIDmax]. --- Detectors/Base/CMakeLists.txt | 5 ++ .../Base/include/DetectorsBase/MatLayerCyl.h | 4 +- .../include/DetectorsBase/MatLayerCylSet.h | 4 + Detectors/Base/src/MatLayerCyl.cxx | 15 ++++ Detectors/Base/src/MatLayerCylSet.cxx | 27 ++++++ Detectors/Base/test/rescaleLUT.C | 82 +++++++++++++++++++ 6 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 Detectors/Base/test/rescaleLUT.C diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 0ba2905ab02ec..3f8b2f5343fd4 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -87,6 +87,7 @@ endif() install(FILES test/buildMatBudLUT.C test/extractLUTLayers.C + test/rescaleLUT.C DESTINATION share/macro/) o2_add_test_root_macro(test/buildMatBudLUT.C @@ -96,3 +97,7 @@ o2_add_test_root_macro(test/buildMatBudLUT.C o2_add_test_root_macro(test/extractLUTLayers.C PUBLIC_LINK_LIBRARIES O2::DetectorsBase LABELS detectorsbase) + +o2_add_test_root_macro(test/rescaleLUT.C + PUBLIC_LINK_LIBRARIES O2::DetectorsBase + LABELS detectorsbase) diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h index ca015fa457a1a..e63de51e0a6ca 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h @@ -93,10 +93,12 @@ class MatLayerCyl : public o2::gpu::FlatObject GPUd() const MatCell& getCell(int iphiSlice, int iz) const { return mCells[getCellID(iphiSlice, iz)]; } #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version - GPUd() MatCell& getCellPhiBin(int iphi, int iz) + MatCell& getCellPhiBin(int iphi, int iz) { return mCells[getCellIDPhiBin(iphi, iz)]; } + + void scale(float factor, bool _x2x0 = true, bool _rho = true); #endif // ---------------------- Z slice manipulation diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index c74ce365d378f..0a53ab00b16f2 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h @@ -98,6 +98,10 @@ class MatLayerCylSet : public o2::gpu::FlatObject // get material budget traversed on the line between point0 and point1 return getMatBudget(point0.X(), point0.Y(), point0.Z(), point1.X(), point1.Y(), point1.Z()); } + + void scaleLayersByID(int lrFrom, int lrTo, float factor, bool _x2x0 = true, bool _rho = true); + void scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0 = true, bool _rho = true); + #endif // !GPUCA_ALIGPUCODE GPUd() MatBudget getMatBudget(float x0, float y0, float z0, float x1, float y1, float z1) const; diff --git a/Detectors/Base/src/MatLayerCyl.cxx b/Detectors/Base/src/MatLayerCyl.cxx index 2346946ea6a8a..29bed111b3584 100644 --- a/Detectors/Base/src/MatLayerCyl.cxx +++ b/Detectors/Base/src/MatLayerCyl.cxx @@ -319,9 +319,24 @@ void MatLayerCyl::flatten(char* newPtr) mConstructionMask = Constructed; } +//________________________________________________________________________________ +void MatLayerCyl::scale(float factor, bool _x2x0, bool _rho) +{ + LOGP(info, "Scaling layer {:.3f}mNLayers - 1)); + lrTo = std::max(0, std::min(lrTo, get()->mNLayers - 1)); + int dir = lrFrom >= lrTo ? -1 : 1; + lrTo += dir; + for (int i = lrFrom; i != lrTo; i += dir) { + get()->mLayers[i].scale(factor, _x2x0, _rho); + } +} + +//________________________________________________________________________________ +void MatLayerCylSet::scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0, bool _rho) +{ + if (rFrom > rTo) { + std::swap(rFrom, rTo); + } + Ray ray(std::max(getRMin(), rFrom), 0., 0., std::min(getRMax(), rTo), 0., 0.); + short lmin, lmax; + if (!getLayersRange(ray, lmin, lmax)) { + LOGP(warn, "No layers found for {} < r < {}", rFrom, rTo); + return; + } + scaleLayersByID(lmin, lmax, factor, _x2x0, _rho); +} + #endif //!GPUCA_ALIGPUCODE #ifndef GPUCA_GPUCODE diff --git a/Detectors/Base/test/rescaleLUT.C b/Detectors/Base/test/rescaleLUT.C new file mode 100644 index 0000000000000..9e25c796e43d1 --- /dev/null +++ b/Detectors/Base/test/rescaleLUT.C @@ -0,0 +1,82 @@ +// 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. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/MatLayerCylSet.h" +#include "Framework/Logger.h" +#include "CCDB/BasicCCDBManager.h" +#include +#endif + +// Macro to extract layers covering selected radial range into the separate LUT file. + +void rescaleLUT(o2::base::MatLayerCylSet* src, const std::string& outName) +{ + struct RescRange { + float rMin, rMax, factor; + }; + std::vector task = { + // put here radial ranges in increasing order with corresponding factors to rescale + {0.1f, 6.f, 1.05}, // e.g. rescale layers covering 0.1 rmax) { + LOGP(error, "rMax={:.2f} of range {} is larger then rMin={:.2f} of range {}, must be in increasing order", rmin, il - 1, rmax, il); + return; + } + o2::base::Ray ray(std::max(src->getRMin(), rmin), 0., 0., std::min(src->getRMax(), rmax), 0., 0.); + if (!src->getLayersRange(ray, lmin, lmax)) { + LOGP(error, "No layers found for {:.2f} < r < {:.2f}", rmin, rmax); + return; + } + if (lmin == lmax) { + LOGP(error, "rMax={:.2f} of range {} and rMin={:.2f} of range {}, correspond to the same slice {} with {:.2f}getLayer(lmin).getRMin(), src->getLayer(lmin).getRMax()); + return; + } + } + + for (size_t il = 0; il < task.size(); il++) { + src->scaleLayersByR(task[il].rMin, task[il].rMax, task[il].factor); + } + if (outName.size()) { + src->writeToFile(outName); + } +} + +void rescaleLUT(const std::string& fname) +{ + auto src = o2::base::MatLayerCylSet::loadFromFile(fname); + if (!src) { + LOGP(error, "failed to open source LUT from {}", fname); + return; + } + auto fnameOut = std::regex_replace(fname, std::regex(R"(.root)"), "_rescaled.root"); + rescaleLUT(src, fnameOut); +} + +void rescaleLUT(long timestamp = -1) +{ + auto& mg = o2::ccdb::BasicCCDBManager::instance(); + mg.setTimestamp(timestamp); + auto src = o2::base::MatLayerCylSet::rectifyPtrFromFile(mg.get("GLO/Param/MatLUT")); + if (!src) { + LOGP(error, "failed to open load LUT from CCDB for timestamp {}", timestamp); + return; + } + auto fnameOut = fmt::format("matbudLUT_ts{}_rescaled.root", timestamp); + rescaleLUT(src, fnameOut); +} From 48b5f61aeecd8b14596dd45a1f6da6de76ebb491 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 27 Sep 2025 16:45:20 +0200 Subject: [PATCH 19/99] DPL: Make RawParser errorMode settable --- Framework/Utils/include/DPLUtils/DPLRawParser.h | 1 + Framework/Utils/include/DPLUtils/RawParser.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Framework/Utils/include/DPLUtils/DPLRawParser.h b/Framework/Utils/include/DPLUtils/DPLRawParser.h index e1732ef70550a..5fa0775025deb 100644 --- a/Framework/Utils/include/DPLUtils/DPLRawParser.h +++ b/Framework/Utils/include/DPLUtils/DPLRawParser.h @@ -76,6 +76,7 @@ class DPLRawParser void setMaxFailureMessages(size_t n) { mMaxFailureMessages = n; } void setExtFailureCounter(size_t* cnt) { mExtFailureCounter = cnt; } static void setCheckIncompleteHBF(bool v) { rawparser_type::setCheckIncompleteHBF(v); } + static void setErrorMode(int v) { rawparser_type::setErrorMode(v); } // this is a dummy default buffer used to initialize the RawParser in the iterator // constructor diff --git a/Framework/Utils/include/DPLUtils/RawParser.h b/Framework/Utils/include/DPLUtils/RawParser.h index c1ba1ef4802b2..fa45cf79b7568 100644 --- a/Framework/Utils/include/DPLUtils/RawParser.h +++ b/Framework/Utils/include/DPLUtils/RawParser.h @@ -649,6 +649,11 @@ class RawParser raw_parser::RawParserHelper::sCheckIncompleteHBF = v; } + static void setErrorMode(int v) + { + raw_parser::RawParserHelper::sErrorMode = v; + } + private: raw_parser::ConcreteParserVariants mParser; }; From 76c213e6e03a2a56268477bb973dd813031de245 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 28 Sep 2025 14:35:41 +0200 Subject: [PATCH 20/99] Custom streamer for std::vector * Add support for EXTRA_PATCH in root dictionary generation --- DataFormats/Detectors/TPC/CMakeLists.txt | 1 + .../Detectors/TPC/src/DataFormatsTPCLinkDef.h | 2 + .../TPC/src/VectorPadflagsCustomStreamer.cxx | 56 +++++++++++++++++++ cmake/AddRootDictionary.cmake | 9 ++- cmake/O2TargetRootDictionary.cmake | 16 +++--- cmake/rootcling_wrapper.sh.in | 23 ++++++++ 6 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 DataFormats/Detectors/TPC/src/VectorPadflagsCustomStreamer.cxx diff --git a/DataFormats/Detectors/TPC/CMakeLists.txt b/DataFormats/Detectors/TPC/CMakeLists.txt index 2cc69e16001a6..5aed01a9772c1 100644 --- a/DataFormats/Detectors/TPC/CMakeLists.txt +++ b/DataFormats/Detectors/TPC/CMakeLists.txt @@ -40,6 +40,7 @@ o2_add_library( o2_target_root_dictionary( DataFormatsTPC + EXTRA_PATCH src/VectorPadflagsCustomStreamer.cxx HEADERS include/DataFormatsTPC/ClusterGroupAttribute.h include/DataFormatsTPC/ClusterNative.h include/DataFormatsTPC/ClusterNativeHelper.h diff --git a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h index fd5abca99cb0f..8659e6a2e43eb 100644 --- a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h +++ b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h @@ -22,6 +22,7 @@ #pragma link C++ class o2::tpc::ClusterHardwareContainerFixedSize < 8192> + ; #pragma link C++ class o2::tpc::ClusterNativeContainer + ; #pragma link C++ class o2::tpc::Digit + ; +// pragma link C++ enum o2::tpc::PadFlags +; // enum itself #pragma link C++ class o2::tpc::ZeroSuppressedContainer8kb + ; #pragma link C++ class std::vector < o2::tpc::ClusterNative> + ; #pragma link C++ class std::vector < o2::tpc::ClusterNativeContainer> + ; @@ -29,6 +30,7 @@ #pragma link C++ class std::vector < o2::tpc::ClusterHardwareContainerFixedSize < 8192>> + ; #pragma link C++ class std::vector < o2::tpc::ClusterHardwareContainer8kb> + ; #pragma link C++ class std::vector < o2::tpc::Digit> + ; +// pragma link C++ class std::vector < o2::tpc::PadFlags> + ; #pragma link C++ class std::vector < o2::tpc::ZeroSuppressedContainer8kb> + ; #pragma link C++ class o2::tpc::TrackTPC + ; #pragma link C++ class o2::tpc::LaserTrack + ; diff --git a/DataFormats/Detectors/TPC/src/VectorPadflagsCustomStreamer.cxx b/DataFormats/Detectors/TPC/src/VectorPadflagsCustomStreamer.cxx new file mode 100644 index 0000000000000..f7cb9285b8884 --- /dev/null +++ b/DataFormats/Detectors/TPC/src/VectorPadflagsCustomStreamer.cxx @@ -0,0 +1,56 @@ +// Copyright 2019-2025 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. + +using std::vector; + +void VectorPadFlagsStreamer(TBuffer& R__b, void* objp) +{ + std::vector* obj = static_cast*>(objp); + if (R__b.IsReading()) { + std::vector R__stl; + R__stl.clear(); + int R__n; + R__b >> R__n; + R__stl.reserve(R__n); + for (int R__i = 0; R__i < R__n; R__i++) { + Int_t readtemp; + R__b >> readtemp; + R__stl.push_back(readtemp); + } + auto data = reinterpret_cast(R__stl.data()); + for (int i = 0; i < R__n; ++i) { + obj->push_back(static_cast(data[i])); + } + } else { + // We always save things with the old format. + R__b << (int)obj->size() / 2; + for (size_t i = 0; i < obj->size(); i++) { + R__b << (short)obj->at(i); + } + } +} + +#define RootStreamerLocal(name, STREAMER) \ + namespace ROOT \ + { \ + \ + /** \cond HIDDEN_SYMBOLS */ \ + static auto _R__UNIQUE_(R__dummyStreamer) = \ + []() { TClass::GetClass()->SetStreamerFunc(STREAMER); return 0; }(); \ + /** \endcond */ \ + R__UseDummy(_R__UNIQUE_(R__dummyStreamer)); \ + } + +// Let's not try to fix the old ROOT version, so that we can build +// the new ROOT with the patched code in the CI. +#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 33, 00) +RootStreamerLocal(vector, VectorPadFlagsStreamer); +#endif diff --git a/cmake/AddRootDictionary.cmake b/cmake/AddRootDictionary.cmake index 0dd68a352d1ce..17fdd2bd286c0 100644 --- a/cmake/AddRootDictionary.cmake +++ b/cmake/AddRootDictionary.cmake @@ -51,7 +51,7 @@ function(add_root_dictionary target) 1 A "" - "LINKDEF" + "LINKDEF;EXTRA_PATCH" "HEADERS;BASENAME") if(A_UNPARSED_ARGUMENTS) message( @@ -112,7 +112,7 @@ function(add_root_dictionary target) set(pcmBase ${dictionary}_rdict.pcm) set(pcmFile ${lib_output_dir}/${pcmBase}) set(rootmapFile ${lib_output_dir}/lib${basename}.rootmap) - + set(O2_TARGETPCMMAP_TARGET "${O2_TARGETPCMMAP_TARGET};${target}" CACHE INTERNAL "target/PCM map (target)") set(O2_TARGETPCMMAP_PCM "${O2_TARGETPCMMAP_PCM};${pcmFile}" CACHE INTERNAL "target/PCM map (pcm)") @@ -132,6 +132,7 @@ function(add_root_dictionary target) set(includeDirs $) set(includeDirs $) + list(LENGTH A_EXTRA_PATCH hasExtraPatch) # add a custom command to generate the dictionary using rootcling # cmake-format: off add_custom_command( @@ -146,11 +147,13 @@ function(add_root_dictionary target) --include_dirs -I$-I> $<$:--compile_defs> $<$:-D$-D>> + $<$:--extra-patch> + $<$:${CMAKE_CURRENT_LIST_DIR}/${A_EXTRA_PATCH}> --pcmdeps "$" --headers "${headers}" COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/${pcmBase} ${pcmFile} - DEPENDS ${headers} "$") + DEPENDS ${headers} "$" ${A_EXTRA_PATCH}) # cmake-format: on # add dictionary source to the target sources diff --git a/cmake/O2TargetRootDictionary.cmake b/cmake/O2TargetRootDictionary.cmake index f5d630dd10569..0b91d751a4802 100644 --- a/cmake/O2TargetRootDictionary.cmake +++ b/cmake/O2TargetRootDictionary.cmake @@ -55,14 +55,9 @@ function(o2_target_root_dictionary baseTargetName) 1 A "" - "LINKDEF" + "LINKDEF;EXTRA_PATCH" "HEADERS") - if(A_UNPARSED_ARGUMENTS) - message( - FATAL_ERROR "Unexpected unparsed arguments: ${A_UNPARSED_ARGUMENTS}") - endif() - if(${ARGC} LESS 1) message( FATAL_ERROR @@ -96,6 +91,13 @@ function(o2_target_root_dictionary baseTargetName) # now that we have the O2 specific stuff computed, delegate the actual work to # the add_root_dictionary function - add_root_dictionary(${target} HEADERS ${A_HEADERS} LINKDEF ${A_LINKDEF}) +if(NOT A_EXTRA_PATCH) + add_root_dictionary(${target} HEADERS ${A_HEADERS} LINKDEF ${A_LINKDEF}) +else() + add_root_dictionary(${target} + EXTRA_PATCH ${A_EXTRA_PATCH} + HEADERS ${A_HEADERS} + LINKDEF ${A_LINKDEF}) +endif() endfunction() diff --git a/cmake/rootcling_wrapper.sh.in b/cmake/rootcling_wrapper.sh.in index 76ce8c8115ca9..d5417c867bc38 100755 --- a/cmake/rootcling_wrapper.sh.in +++ b/cmake/rootcling_wrapper.sh.in @@ -41,6 +41,10 @@ while [[ $# -gt 0 ]]; do PCMDEPS="$2" shift 2 ;; + --extra-patch) + EXTRA_PATCH="$2" + shift 2 + ;; *) if [[ -z "$1" ]]; then shift @@ -82,6 +86,18 @@ esac LOGFILE=${DICTIONARY_FILE}.log +echo @CMAKE_COMMAND@ -E env "LD_LIBRARY_PATH=$libpath" @ROOT_rootcling_CMD@ \ + -f $DICTIONARY_FILE \ + -inlineInputHeader \ + -noGlobalUsingStd \ + -rmf ${ROOTMAP_FILE} \ + -rml ${ROOTMAP_LIBRARY_NAME} \ + ${INCLUDE_DIRS//;/ } \ + ${COMPILE_DEFINITIONS//;/ } \ + ${PCMDEPS:+-m }${PCMDEPS//;/ -m } \ + ${HEADERS//;/ } \ + > ${LOGFILE} 2>&1 || ROOTCLINGRETVAL=$? + @CMAKE_COMMAND@ -E env "LD_LIBRARY_PATH=$libpath" @ROOT_rootcling_CMD@ \ -f $DICTIONARY_FILE \ -inlineInputHeader \ @@ -94,6 +110,13 @@ LOGFILE=${DICTIONARY_FILE}.log ${HEADERS//;/ } \ > ${LOGFILE} 2>&1 || ROOTCLINGRETVAL=$? +# Add the extra patch file at the end of the generated dictionary. +# This is needed to inject custom streamers (e.g. for std::vector) +# to our dictionary. +if [ ! X"${EXTRA_PATCH}" = X ]; then + cat $EXTRA_PATCH >> ${DICTIONARY_FILE} +fi + if [[ ${ROOTCLINGRETVAL:-0} != "0" ]]; then cat ${LOGFILE} >&2 rm -f $DICTIONARY_FILE From 85026dbab3113c5a3f69e271bea0b1aa3b136a73 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 26 Sep 2025 15:51:05 +0200 Subject: [PATCH 21/99] dpl-workflow.sh: add env variable to use full MI100 serialization in online --- prodtests/full-system-test/dpl-workflow.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 996ab70da8f6c..9fc6ce5507168 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -278,8 +278,13 @@ if [[ $GPUTYPE == "HIP" ]]; then GPU_CONFIG+=" --environment \"ROCR_VISIBLE_DEVICES={timeslice${TIMESLICEOFFSET}}\"" fi # serialization workaround for MI100 nodes: remove it again if the problem will be fixed in ROCm, then also remove the DISABLE_MI100_SERIALIZATION flag in the O2DPG parse script - [[ $EPNSYNCMODE == 1 ]] && [[ ${EPN_NODE_MI100:-} == "1" ]] && [[ ${DISABLE_MI100_SERIALIZATION:-0} != 1 ]] && GPU_CONFIG_KEY+="GPU_proc.amdMI100SerializationWorkaround=1;" - [[ -n ${OPTIMIZED_PARALLEL_ASYNC:-} ]] && [[ ${EPN_NODE_MI100:-} == "1" ]] && [[ ${DISABLE_MI100_SERIALIZATION:-0} != 1 ]] && GPU_CONFIG_KEY+="GPU_proc.serializeGPU=3;" + if [[ ${EPN_NODE_MI100:-} == "1" && ${DISABLE_MI100_SERIALIZATION:-0} != 1 ]]; then + if [[ -n ${OPTIMIZED_PARALLEL_ASYNC:-} ]] || [[ $EPNSYNCMODE == 1 && ${FULL_MI100_SERIALIZATION:-0} == 1 ]]; then + GPU_CONFIG_KEY+="GPU_proc.serializeGPU=3;" + elif [[ $EPNSYNCMODE == 1 ]]; then + GPU_CONFIG_KEY+="GPU_proc.amdMI100SerializationWorkaround=1;" + fi + fi #export HSA_TOOLS_LIB=/opt/rocm/lib/librocm-debug-agent.so.2 else GPU_CONFIG_KEY+="GPU_proc.deviceNum=-2;" From 3b0db2f9de69784a45b5710f2f4dade6d541ab50 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 5 Sep 2025 10:27:21 +0200 Subject: [PATCH 22/99] Common: Minor cleanup of flag helper class Signed-off-by: Felix Schlepper --- Common/Utils/include/CommonUtils/EnumFlags.h | 98 +++++++++++++------- Common/Utils/test/testEnumFlags.cxx | 83 +++++++++-------- 2 files changed, 107 insertions(+), 74 deletions(-) diff --git a/Common/Utils/include/CommonUtils/EnumFlags.h b/Common/Utils/include/CommonUtils/EnumFlags.h index 9a8960f612553..4bd1a9e641056 100644 --- a/Common/Utils/include/CommonUtils/EnumFlags.h +++ b/Common/Utils/include/CommonUtils/EnumFlags.h @@ -57,6 +57,7 @@ concept EnumFlagHelper = requires { template struct FlagsHelper final { using U = std::underlying_type_t; + using UMax = uint64_t; // max represetable type static constexpr bool isScoped() noexcept { @@ -144,8 +145,8 @@ struct FlagsHelper final { { constexpr std::array valid{isValid(MinScan + I)>()...}; constexpr auto count{std::count_if(valid.cbegin(), valid.cend(), [](bool v) noexcept { return v; })}; - static_assert(count > 0, "Requiring non-empty enum!"); - static_assert(count <= MaxUnderScan, "Underlying type of enum has less digits than given expected!"); + static_assert(count > 0, "EnumFlag requires at least one enum value. Check that your enum has consecutive values starting from 0."); + static_assert(count <= MaxUnderScan, "Too many enum values for underlying type. Consider using a larger underlying type or fewer enum values."); std::array values{}; for (size_t idx{}, n{}; n < count; ++idx) { if (valid[idx]) { @@ -161,8 +162,16 @@ struct FlagsHelper final { static constexpr auto Min_u_v{static_cast(Min_v)}; // Enum first entry as size_t static constexpr auto Max_u_v{static_cast(Max_v)}; // Enum last entry as size_t static_assert(Max_u_v < std::numeric_limits::digits, "Max Bit is beyond allow range defered from underlying type"); - static constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous - static constexpr auto MaxRep{((1ULL << (static_cast(Max_u_v - Min_u_v) + 1ULL)) - 1ULL) << Min_u_v}; // largest representable value + static constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous + static constexpr UMax makeMaxRep(size_t min, size_t max) + { + const size_t width = max - min + 1; + if (width >= std::numeric_limits::digits) { + return std::numeric_limits::max(); + } + return ((UMax(1) << width) - 1) << min; + } + static constexpr auto MaxRep{makeMaxRep(Min_u_v, Max_u_v)}; // largest representable value template static constexpr std::string_view getName() @@ -173,7 +182,7 @@ struct FlagsHelper final { } if constexpr (tpeek_v[tp + getSpec().size()] == getSpec()) { #if defined __clang__ - if constexpr (tpeek_v[tp + getSpec().size() + 1] == getSpec()) { + if constexpr (tpeek_v[tp + getSpec().size() + 1] == getSpec()) { return {}; } #endif @@ -215,7 +224,7 @@ struct FlagsHelper final { template static constexpr auto getNameValue{getName()}; - template + template static constexpr auto getNames(std::index_sequence /*unused*/) { if constexpr (with_scope) { @@ -248,7 +257,7 @@ struct FlagsHelper final { static constexpr std::optional fromString(std::string_view str) noexcept { - for (std::size_t i{0}; i < count(); ++i) { + for (size_t i{0}; i < count(); ++i) { if (Names[i] == str || NamesScoped[i] == str) { return Values[i]; } @@ -325,7 +334,7 @@ concept EnumFlag = requires { /** * \brief Classs to aggregate and manage enum-based on-off flags. * - * This class manages flags as bits in the underlying type of an enum, allowing + * This class manages flags as bits in the underlying type of an enum (upto 64 bits), allowing * manipulation via enum member names. It supports operations akin to std::bitset * but is fully constexpr and is ideal for aggregating multiple on-off booleans, * e.g., enabling/disabling algorithm features. @@ -371,13 +380,18 @@ class EnumFlags constexpr EnumFlags(const EnumFlags&) = default; // Move constructor. constexpr EnumFlags(EnumFlags&&) = default; - // Constructor to initialize with the underlyiny type. + // Constructor to initialize with the underlying type. constexpr explicit EnumFlags(U u) : mBits(u) {} // Initialize with a list of flags. constexpr EnumFlags(std::initializer_list flags) noexcept { std::for_each(flags.begin(), flags.end(), [this](const E f) noexcept { mBits |= to_bit(f); }); } + // Init from a string. + EnumFlags(const std::string& str) + { + set(str); + } // Destructor. constexpr ~EnumFlags() = default; @@ -415,7 +429,7 @@ class EnumFlags } } // Returns the raw bitset value. - constexpr auto value() const noexcept + [[nodiscard]] constexpr auto value() const noexcept { return mBits; } @@ -442,6 +456,13 @@ class EnumFlags return (mBits & to_bit(t)) != None; } + // Tests if all specified flags are set. + template + [[nodiscard]] constexpr bool test(Ts... flags) const noexcept + { + return ((test(flags) && ...)); + } + // Sets a specific flag. template requires std::is_same_v @@ -464,6 +485,12 @@ class EnumFlags return mBits != None; } + // Checks if all flags are set. + [[nodiscard]] constexpr bool all() const noexcept + { + return mBits == All; + } + // Returns the bitset as a binary string. [[nodiscard]] std::string string() const { @@ -505,7 +532,7 @@ class EnumFlags } // Checks if any flag is set (Boolean context). - constexpr explicit operator bool() const noexcept + [[nodiscard]] constexpr explicit operator bool() const noexcept { return any(); } @@ -513,19 +540,19 @@ class EnumFlags // Check if given flag is set. template requires std::is_same_v - constexpr bool operator[](const T t) noexcept + [[nodiscard]] constexpr bool operator[](const T t) const noexcept { return test(t); } // Checks if two flag sets are equal. - constexpr bool operator==(const EnumFlags& o) const noexcept + [[nodiscard]] constexpr bool operator==(const EnumFlags& o) const noexcept { return mBits == o.mBits; } // Checks if two flag sets are not equal. - constexpr bool operator!=(const EnumFlags& o) const noexcept + [[nodiscard]] constexpr bool operator!=(const EnumFlags& o) const noexcept { return mBits != o.mBits; } @@ -584,7 +611,13 @@ class EnumFlags // Performs a bitwise XOR with another flag set. constexpr EnumFlags operator^(const EnumFlags& o) const noexcept { - return Flags(mBits ^ o.mBits); + return EnumFlags(mBits ^ o.mBits); + } + + // Performs a bitwise and with another flag set. + constexpr EnumFlags operator&(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits & o.mBits); } // Performs a bitwise XOR assignment. @@ -596,14 +629,14 @@ class EnumFlags // Checks if all specified flags are set. template - constexpr bool all_of(Ts... flags) const noexcept + [[nodiscard]] constexpr bool all_of(Ts... flags) const noexcept { - return ((test(flags) && ...)); + return test(flags...); } // Checks if none of the specified flags are set. template - constexpr bool none_of(Ts... flags) const noexcept + [[nodiscard]] constexpr bool none_of(Ts... flags) const noexcept { return (!(test(flags) || ...)); } @@ -617,7 +650,7 @@ class EnumFlags // Deserializes a string into the flag set. void deserialize(const std::string& data) { - uint64_t v = std::stoul(data); + typename H::UMax v = std::stoul(data); if (v > H::MaxRep) { throw std::out_of_range("Values exceeds enum range."); } @@ -627,35 +660,29 @@ class EnumFlags // Counts the number of set bits (active flags). [[nodiscard]] constexpr size_t count() const noexcept { - size_t c{0}; - for (size_t i{H::Min_u_v}; i < H::Max_u_v; ++i) { - if ((mBits & (U(1) << i)) != U(0)) { - ++c; - } - } - return c; + return std::popcount(mBits); } // Returns the union of two flag sets. - constexpr EnumFlags union_with(const EnumFlags& o) const noexcept + [[nodiscard]] constexpr EnumFlags union_with(const EnumFlags& o) const noexcept { return EnumFlags(mBits | o.mBits); } // Returns the intersection of two flag sets. - constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept + [[nodiscard]] constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept { return EnumFlags(mBits & o.mBits); } // Checks if all flags in another Flags object are present in the current object. - constexpr bool contains(const EnumFlags& other) const noexcept + [[nodiscard]] constexpr bool contains(const EnumFlags& other) const noexcept { return (mBits & other.mBits) == other.mBits; } private: - // Set implemnetation, bits was zeroed before. + // Set implementation, bits was zeroed before. void setImpl(const std::string& s, int base = 2) { if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); })) { @@ -664,12 +691,12 @@ class EnumFlags throw std::invalid_argument("Invalid binary string."); } } - uint64_t v = std::stoul(s, nullptr, base); + typename H::UMax v = std::stoul(s, nullptr, base); if (v > H::MaxRep) { throw std::out_of_range("Values exceeds enum range."); } mBits = static_cast(v); - } else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ','; })) { + } else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ',' || c == ';'; })) { std::string cs{s}; std::transform(cs.begin(), cs.end(), cs.begin(), [](unsigned char c) { return std::tolower(c); }); if (cs == H::All) { @@ -677,7 +704,12 @@ class EnumFlags } else if (cs == H::None) { mBits = None; } else { - char token = (s.find(',') != std::string::npos) ? ',' : '|'; + // accept as delimiter ' ', '|', ';', ',' + char token = ' '; + std::string::size_type pos = s.find_first_of(",|;"); + if (pos != std::string::npos) { + token = s[pos]; + } for (const auto& tok : Str::tokenize(s, token)) { if (auto e = H::fromString(tok)) { mBits |= to_bit(*e); diff --git a/Common/Utils/test/testEnumFlags.cxx b/Common/Utils/test/testEnumFlags.cxx index 41b43bc4218ff..80f85c847653b 100644 --- a/Common/Utils/test/testEnumFlags.cxx +++ b/Common/Utils/test/testEnumFlags.cxx @@ -14,6 +14,9 @@ #define BOOST_TEST_DYN_LINK #include +#include +#include + #include #include @@ -21,7 +24,7 @@ // Example enum to use with EnumFlags enum class TestEnum : uint8_t { - Bit1, + Bit1 = 0, Bit2, Bit3, Bit4, @@ -29,44 +32,16 @@ enum class TestEnum : uint8_t { }; // Very long enum -// to test that it works beyond 32 bits +// to test that it works beyond 32 bits upto 64 bits +#define ENUM_BIT_NAME(n) Bit##n +#define ENUM_BIT_NAME_EXPAND(n) ENUM_BIT_NAME(n) +#define ENUM_BIT(z, n, _) ENUM_BIT_NAME_EXPAND(BOOST_PP_INC(n)) = (n), enum class TestEnumLong : uint64_t { - Bit1, - Bit2, - Bit3, - Bit4, - Bit5, - Bit6, - Bit7, - Bit8, - Bit9, - Bit10, - Bit11, - Bit12, - Bit13, - Bit14, - Bit15, - Bit16, - Bit17, - Bit18, - Bit19, - Bit20, - Bit21, - Bit22, - Bit23, - Bit24, - Bit25, - Bit26, - Bit27, - Bit28, - Bit29, - Bit30, - Bit31, - Bit32, - Bit33, - Bit34, - // ... + BOOST_PP_REPEAT(64, ENUM_BIT, _) }; +#undef ENUM_BIT +#undef ENUM_BIT_NAME +#undef ENUM_BIT_NAME_EXPAND BOOST_AUTO_TEST_CASE(Flags_test) { @@ -181,7 +156,7 @@ BOOST_AUTO_TEST_CASE(Flags_test) BOOST_TEST(flags.test(TestEnum::Bit4)); } - { // test with different delimiter + { // test with , delimiter std::string str = "Bit4,TestEnum::Bit2 , Bit1 "; flags.set(str); BOOST_TEST(flags.test(TestEnum::Bit1)); @@ -190,6 +165,15 @@ BOOST_AUTO_TEST_CASE(Flags_test) BOOST_TEST(flags.test(TestEnum::Bit4)); } + { // test with ; delimiter + std::string str = "Bit4;TestEnum::Bit2 ; Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + { // throw test with mixed delimiter std::string str = "Bit4|TestEnum::Bit2 , Bit1 "; BOOST_CHECK_THROW(flags.set(str), std::invalid_argument); @@ -275,6 +259,14 @@ BOOST_AUTO_TEST_CASE(Flags_test) EFlags flags3{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; EFlags flags4{TestEnum::Bit2, TestEnum::Bit3, TestEnum::Bit4}; + // test xor + auto flagsXOR = flags3 ^ flags4; + BOOST_CHECK(flagsXOR.test(TestEnum::Bit1, TestEnum::Bit4)); + + // test and + auto flagsAND = flags3 & flags4; + BOOST_CHECK(flagsAND.test(TestEnum::Bit2, TestEnum::Bit3)); + // Perform an intersection operation EFlags intersectionFlags = flags3.intersection_with(flags4); BOOST_CHECK(intersectionFlags.test(TestEnum::Bit2)); @@ -284,6 +276,14 @@ BOOST_AUTO_TEST_CASE(Flags_test) BOOST_CHECK_EQUAL(intersectionFlags.value(), 6); // 0110 in binary } + { + // Check special flag names. + EFlags flag("all"); + BOOST_CHECK(flag.all()); + flag.set("none"); + BOOST_CHECK(!flag.any()); + } + { // Create two flag sets EFlags flags1{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; @@ -300,8 +300,9 @@ BOOST_AUTO_TEST_CASE(Flags_test) { // Test compilation using an enum with more than 32 bits - o2::utils::EnumFlags test; - test.set("Bit32"); - BOOST_CHECK(test.test(TestEnumLong::Bit32)); + // Also tests space delimiter and construction from string. + o2::utils::EnumFlags test("Bit32 Bit34"); + BOOST_CHECK(test.test(TestEnumLong::Bit32, TestEnumLong::Bit34)); + BOOST_CHECK(!test.test(TestEnumLong::Bit1, TestEnumLong::Bit23)); } } From e644ae915068f061cae5253be17ef1df20f6c188 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 21 Sep 2025 19:03:01 +0400 Subject: [PATCH 23/99] Robust propagation to certain R o2::base::Propagator got method propagateToR(track, r, bool bzOnly, maxSnp ...) which propagates the track (either TrackPar or TrackParCov) to requested radius (not X!) if it is reachable in any tracking frame. The rotations to avoid track snp goind outside of the allowed range are done automatically. Moved HelixHelper to ReconstructionDataFormats. --- Common/DCAFitter/CMakeLists.txt | 3 +- .../DCAFitter/include/DCAFitter/DCAFitterN.h | 2 +- .../include/DCAFitter/FwdDCAFitterN.h | 2 +- Common/DCAFitter/src/DCAFitterLinkDef.h | 3 - Common/Field/include/Field/MagFieldFast.h | 4 +- Common/Field/src/MagFieldFast.cxx | 2 +- DataFormats/Reconstruction/CMakeLists.txt | 1 + .../ReconstructionDataFormats}/HelixHelper.h | 0 .../src/ReconstructionDataFormatsLinkDef.h | 3 + .../src/TrackParametrization.cxx | 68 ++++----- Detectors/Base/CMakeLists.txt | 1 + .../Base/include/DetectorsBase/Propagator.h | 12 +- Detectors/Base/src/Propagator.cxx | 139 ++++++++++++++++++ .../DetectorsVertexing/FwdDCAFitterN.h | 2 +- .../include/DetectorsVertexing/HelixHelper.h | 115 +++++++++------ 15 files changed, 268 insertions(+), 89 deletions(-) rename {Common/DCAFitter/include/DCAFitter => DataFormats/Reconstruction/include/ReconstructionDataFormats}/HelixHelper.h (100%) diff --git a/Common/DCAFitter/CMakeLists.txt b/Common/DCAFitter/CMakeLists.txt index 5c3a93aa7fa74..c0b2d0dca1026 100644 --- a/Common/DCAFitter/CMakeLists.txt +++ b/Common/DCAFitter/CMakeLists.txt @@ -22,8 +22,7 @@ o2_add_library(DCAFitter O2::DetectorsBase) o2_target_root_dictionary(DCAFitter - HEADERS include/DCAFitter/HelixHelper.h - include/DCAFitter/DCAFitterN.h + HEADERS include/DCAFitter/DCAFitterN.h include/DCAFitter/FwdDCAFitterN.h) if (OpenMP_CXX_FOUND) diff --git a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index df732bd4bde63..1adf7a9ae7329 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -17,7 +17,7 @@ #ifndef _ALICEO2_DCA_FITTERN_ #define _ALICEO2_DCA_FITTERN_ -#include "DCAFitter/HelixHelper.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include "DetectorsBase/Propagator.h" #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/Track.h" diff --git a/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h b/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h index cd1742e24fa72..d5bc6631575af 100644 --- a/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h @@ -20,7 +20,7 @@ #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/TrackFwd.h" #include "ReconstructionDataFormats/Track.h" -#include "DCAFitter/HelixHelper.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GeometryManager.h" diff --git a/Common/DCAFitter/src/DCAFitterLinkDef.h b/Common/DCAFitter/src/DCAFitterLinkDef.h index 3589ffe559e96..6883369c1b9b6 100644 --- a/Common/DCAFitter/src/DCAFitterLinkDef.h +++ b/Common/DCAFitter/src/DCAFitterLinkDef.h @@ -18,9 +18,6 @@ #pragma link C++ class o2::vertexing::DCAFitterN < 2, o2::track::TrackParCov> + ; #pragma link C++ class o2::vertexing::DCAFitterN < 3, o2::track::TrackParCov> + ; -#pragma link C++ class o2::track::TrackAuxPar + ; -#pragma link C++ class o2::track::CrossInfo + ; - #pragma link C++ function o2::vertexing::DCAFitter2::process(const o2::track::TrackParCov&, const o2::track::TrackParCov&); #pragma link C++ function o2::vertexing::DCAFitter3::process(const o2::track::TrackParCov&, const o2::track::TrackParCov&, const o2::track::TrackParCov&); diff --git a/Common/Field/include/Field/MagFieldFast.h b/Common/Field/include/Field/MagFieldFast.h index acff8f528ad06..ae6431a477923 100644 --- a/Common/Field/include/Field/MagFieldFast.h +++ b/Common/Field/include/Field/MagFieldFast.h @@ -57,7 +57,7 @@ class MagFieldFast bool Field(const math_utils::Point3D xyz, double bxyz[3]) const; bool GetBcomp(EDim comp, const double xyz[3], double& b) const; bool GetBcomp(EDim comp, const float xyz[3], float& b) const; - bool GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const; + bool GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const; bool GetBcomp(EDim comp, const math_utils::Point3D xyz, float& b) const; bool GetBx(const double xyz[3], double& bx) const { return GetBcomp(kX, xyz, bx); } @@ -66,6 +66,8 @@ class MagFieldFast bool GetBy(const float xyz[3], float& by) const { return GetBcomp(kY, xyz, by); } bool GetBz(const double xyz[3], double& bz) const { return GetBcomp(kZ, xyz, bz); } bool GetBz(const float xyz[3], float& bz) const { return GetBcomp(kZ, xyz, bz); } + bool GetBz(const math_utils::Point3D xyz, double& bz) const { return GetBcomp(kZ, xyz, bz); } + bool GetBz(const math_utils::Point3D xyz, float& bz) const { return GetBcomp(kZ, xyz, bz); } void setFactorSol(float v = 1.f) { mFactorSol = v; } float getFactorSol() const { return mFactorSol; } diff --git a/Common/Field/src/MagFieldFast.cxx b/Common/Field/src/MagFieldFast.cxx index 5caad34d56dd4..02ef9c153d189 100644 --- a/Common/Field/src/MagFieldFast.cxx +++ b/Common/Field/src/MagFieldFast.cxx @@ -145,7 +145,7 @@ bool MagFieldFast::GetBcomp(EDim comp, const double xyz[3], double& b) const } //_______________________________________________________________________ -bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const +bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const { // get field int zSeg, rSeg, quadrant; diff --git a/DataFormats/Reconstruction/CMakeLists.txt b/DataFormats/Reconstruction/CMakeLists.txt index 86c0831d2134e..ffd88df2412f9 100644 --- a/DataFormats/Reconstruction/CMakeLists.txt +++ b/DataFormats/Reconstruction/CMakeLists.txt @@ -73,6 +73,7 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/BCRange.h include/ReconstructionDataFormats/TrackHMP.h include/ReconstructionDataFormats/MatchInfoHMP.h + include/ReconstructionDataFormats/HelixHelper.h ) o2_add_test(Vertex diff --git a/Common/DCAFitter/include/DCAFitter/HelixHelper.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h similarity index 100% rename from Common/DCAFitter/include/DCAFitter/HelixHelper.h rename to DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h diff --git a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h index 6cd72e8668cc1..b386830d9872d 100644 --- a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h +++ b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h @@ -117,4 +117,7 @@ #pragma link C++ class o2::dataformats::StrangeTrack + ; #pragma link C++ class std::vector < o2::dataformats::StrangeTrack> + ; +#pragma link C++ class o2::track::TrackAuxPar + ; +#pragma link C++ class o2::track::CrossInfo + ; + #endif diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx index b685a1549dd31..0539278acb20b 100644 --- a/DataFormats/Reconstruction/src/TrackParametrization.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx @@ -651,7 +651,7 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val // DirOutward (==1) - go along the track (increasing mX) // DirInward (==-1) - go backward (decreasing mX) // - const auto fy = mP[0], sn = mP[2]; + const double fy = mP[0], sn = mP[2]; const value_t kEps = 1.e-6; // if (gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) { @@ -670,18 +670,18 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val if (r0 <= constants::math::Almost0) { return false; // the track is concentric to circle } - value_t tR2r0 = 1.f, g = 0.f, tmp = 0.f; + double tR2r0 = 1., g = 0., tmp = 0.; if (gpu::CAMath::Abs(circle.rC - r0) > kEps) { tR2r0 = circle.rC / r0; g = 0.5f * (r * r / (r0 * circle.rC) - tR2r0 - 1.f / tR2r0); tmp = 1.f + g * tR2r0; } else { tR2r0 = 1.0; - g = 0.5f * r * r / (r0 * circle.rC) - 1.f; - tmp = 0.5f * r * r / (r0 * r0); + g = 0.5 * r * r / (r0 * circle.rC) - 1.; + tmp = 0.5 * r * r / (r0 * r0); } - value_t det = (1.f - g) * (1.f + g); - if (det < 0.f) { + auto det = (1. - g) * (1. + g); + if (det < 0.) { return false; // does not reach raduis r } det = gpu::CAMath::Sqrt(det); @@ -691,25 +691,26 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val // where s0 and c0 make direction for the circle center (=circle.xC/r0 and circle.yC/r0) // x = circle.xC * tmp; - value_t y = circle.yC * tmp; + auto y = circle.yC * tmp; if (gpu::CAMath::Abs(circle.yC) > constants::math::Almost0) { // when circle.yC==0 the x,y is unique - value_t dfx = tR2r0 * gpu::CAMath::Abs(circle.yC) * det; - value_t dfy = tR2r0 * circle.xC * (circle.yC > 0.f ? det : -det); + auto dfx = tR2r0 * gpu::CAMath::Abs(circle.yC) * det; + auto dfy = tR2r0 * circle.xC * (circle.yC > 0. ? det : -det); if (dir == DirAuto) { // chose the one which corresponds to smallest step - value_t delta = (x - mX) * dfx - (y - fy) * dfy; // the choice of + in C will lead to smaller step if delta<0 - x += delta < 0.f ? dfx : -dfx; + auto delta = (x - mX) * dfx - (y - fy) * dfy; // the choice of + in C will lead to smaller step if delta<0 + x += delta < 0. ? dfx : -dfx; } else if (dir == DirOutward) { // along track direction: x must be > mX x -= dfx; // try the smallest step (dfx is positive) - value_t dfeps = mX - x; // handle special case of very small step + auto dfeps = mX - x; // handle special case of very small step if (dfeps < -kEps) { return true; } if (gpu::CAMath::Abs(dfeps) < kEps && gpu::CAMath::Abs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r? - return mX; + x = mX; + return true; } x += dfx + dfx; - value_t dxm = x - mX; - if (dxm > 0.f) { + auto dxm = x - mX; + if (dxm > 0.) { return true; } else if (dxm < -kEps) { return false; @@ -717,16 +718,17 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val x = mX; // don't move } else { // backward: x must be < mX x += dfx; // try the smallest step (dfx is positive) - value_t dfeps = x - mX; // handle special case of very small step + auto dfeps = x - mX; // handle special case of very small step if (dfeps < -kEps) { return true; } if (gpu::CAMath::Abs(dfeps) < kEps && gpu::CAMath::Abs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r? - return mX; + x = mX; + return true; } x -= dfx + dfx; - value_t dxm = x - mX; - if (dxm < 0.f) { + auto dxm = x - mX; + if (dxm < 0.) { return true; } if (dxm > kEps) { @@ -739,11 +741,11 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val return false; } } - return x; + return true; } // this is a straight track if (gpu::CAMath::Abs(sn) >= constants::math::Almost1) { // || to Y axis - value_t det = (r - mX) * (r + mX); + double det = (r - mX) * (r + mX); if (det < 0.f) { return false; // does not reach raduis r } @@ -753,7 +755,7 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } det = gpu::CAMath::Sqrt(det); if (dir == DirOutward) { // along the track direction - if (sn > 0.f) { + if (sn > 0.) { if (fy > det) { return false; // track is along Y axis and above the circle } @@ -763,7 +765,7 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } } } else if (dir == DirInward) { // against track direction - if (sn > 0.f) { + if (sn > 0.) { if (fy < -det) { return false; // track is along Y axis } @@ -772,13 +774,13 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } } } else if (gpu::CAMath::Abs(sn) <= constants::math::Almost0) { // || to X axis - value_t det = (r - fy) * (r + fy); - if (det < 0.f) { + double det = (r - fy) * (r + fy); + if (det < 0.) { return false; // does not reach raduis r } det = gpu::CAMath::Sqrt(det); if (dir == DirAuto) { - x = mX > 0.f ? det : -det; // choose the solution requiring the smalest step + x = mX > 0. ? det : -det; // choose the solution requiring the smalest step return true; } else if (dir == DirOutward) { // along the track direction if (mX > det) { @@ -794,17 +796,17 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } } } else { // general case of straight line - value_t cs = gpu::CAMath::Sqrt((1.f - sn) * (1.f + sn)); - value_t xsyc = mX * sn - fy * cs; - value_t det = (r - xsyc) * (r + xsyc); - if (det < 0.f) { + auto cs = gpu::CAMath::Sqrt((1. - sn) * (1. + sn)); + auto xsyc = mX * sn - fy * cs; + auto det = (r - xsyc) * (r + xsyc); + if (det < 0.) { return false; // does not reach raduis r } det = gpu::CAMath::Sqrt(det); - value_t xcys = mX * cs + fy * sn; - value_t t = -xcys; + auto xcys = mX * cs + fy * sn; + auto t = -xcys; if (dir == DirAuto) { - t += t > 0.f ? -det : det; // chose the solution requiring the smalest step + t += t > 0. ? -det : det; // chose the solution requiring the smalest step } else if (dir > 0) { // go in increasing mX direction. ( t+-det > 0) if (t >= -det) { t += det; // take minimal step giving t>0 diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 3f8b2f5343fd4..30ab4c4fe8a40 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -8,6 +8,7 @@ # 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_compile_options(-O0 -g -fPIC) o2_add_library(DetectorsBase SOURCES src/Detector.cxx diff --git a/Detectors/Base/include/DetectorsBase/Propagator.h b/Detectors/Base/include/DetectorsBase/Propagator.h index dbdef47e4edc0..d9b1522f4295b 100644 --- a/Detectors/Base/include/DetectorsBase/Propagator.h +++ b/Detectors/Base/include/DetectorsBase/Propagator.h @@ -92,13 +92,17 @@ class PropagatorImpl GPUd() bool propagateTo(track_T& track, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const { - return bzOnly ? propagateToX(track, x, getNominalBz(), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); + return bzOnly ? propagateToX(track, x, getBz(track.getXYZGlo()), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); } template GPUd() bool propagateToAlphaX(track_T& track, value_type alpha, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, int minSteps = 1, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + template + GPUd() bool propagateToR(track_T& track, value_type r, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, + MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::track::TrackParametrizationWithError& track, value_type bZ, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, o2::dataformats::DCA* dcaInfo = nullptr, track::TrackLTIntegral* tofInfo = nullptr, @@ -157,6 +161,10 @@ class PropagatorImpl GPUd() void getFieldXYZ(const math_utils::Point3D xyz, double* bxyz) const; + GPUd() float getBz(const math_utils::Point3D xyz) const; + + GPUd() double getBz(const math_utils::Point3D xyz) const; + private: #ifndef GPUCA_GPUCODE PropagatorImpl(bool uninitialized = false); @@ -165,6 +173,8 @@ class PropagatorImpl static constexpr value_type Epsilon = 0.00001; // precision of propagation to X template GPUd() void getFieldXYZImpl(const math_utils::Point3D xyz, T* bxyz) const; + template + GPUd() T getBzImpl(const math_utils::Point3D xyz) const; const o2::field::MagFieldFast* mFieldFast = nullptr; ///< External fast field map (barrel only for the moment) o2::field::MagneticField* mField = nullptr; ///< External nominal field map diff --git a/Detectors/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index 1c44cea65c69c..b6112cd5ba32e 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -15,6 +15,7 @@ #include "GPUCommonMath.h" #include "GPUTPCGMPolynomialField.h" #include "MathUtils/Utils.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include "ReconstructionDataFormats/Vertex.h" using namespace o2::base; @@ -418,6 +419,101 @@ GPUd() bool PropagatorImpl::propagateToX(TrackPar_t& track, value_type return true; } +//_______________________________________________________________________ +template +template +GPUd() bool PropagatorImpl::propagateToR(track_T& track, value_type r, bool bzOnly, value_type maxSnp, value_type maxStep, + MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + const value_T MaxPhiLoc = math_utils::detail::asin(maxSnp), MaxPhiLocSafe = 0.95 * MaxPhiLoc; + auto bz = getNominalBz(); + if (math_utils::detail::abs(bz) > constants::math::Almost0) { + o2::track::TrackAuxPar traux(track, bz); + o2::track::TrackAuxPar crad; + value_type r0 = math_utils::detail::sqrt(track.getX() * track.getX() + track.getY() * track.getY()); + value_type dr = (r - r0); + value_type rTmp = r - (math_utils::detail::abs(dr) > 1. ? (dr > 0 ? 0.5 : -0.5) : 0.5 * dr); // 1st propagate a few mm short of the targer R + crad.rC = rTmp; + crad.c = crad.cc = 1.f; + crad.s = crad.ss = crad.cs = 0.f; + o2::track::CrossInfo cross; + cross.circlesCrossInfo(crad, traux, 0.); + if (cross.nDCA < 1) { + return false; + } + double phiCross[2] = {}, dphi[2] = {}; + auto curv = track.getCurvature(bz); + bool clockwise = curv < 0; // q+ in B+ or q- in B- goes clockwise + auto phiLoc = math_utils::detail::asin(track.getSnp()); + auto phi0 = phiLoc + track.getAlpha(); + o2::math_utils::detail::bringTo02Pi(phi0); + for (int i = 0; i < cross.nDCA; i++) { + // track pT direction angle at crossing points: + // == angle of the tangential to track circle at the crossing point X,Y + // == normal to the radial vector from the track circle center {X-cX, Y-cY} + // i.e. the angle of the vector {Y-cY, -(X-cx)} + auto normX = double(cross.yDCA[i]) - double(traux.yC), normY = -(double(cross.xDCA[i]) - double(traux.xC)); + if (!clockwise) { + normX = -normX; + normY = -normY; + } + phiCross[i] = math_utils::detail::atan2(normY, normX); + o2::math_utils::detail::bringTo02Pi(phiCross[i]); + dphi[i] = phiCross[i] - phi0; + if (dphi[i] > o2::constants::math::PI) { + dphi[i] -= o2::constants::math::TwoPI; + } else if (dphi[i] < -o2::constants::math::PI) { + dphi[i] += o2::constants::math::TwoPI; + } + } + int sel = cross.nDCA == 1 ? 0 : (clockwise ? (dphi[0] < dphi[1] ? 0 : 1) : (dphi[1] < dphi[0] ? 0 : 1)); + auto deltaPhi = dphi[sel]; + + while (1) { + auto phiLocFin = phiLoc + deltaPhi; + // case1 + if (math_utils::detail::abs(phiLocFin) < MaxPhiLocSafe) { // just 1 step propagation + auto deltaX = (math_utils::detail::sin(phiLocFin) - track.getSnp()) / track.getCurvature(bz); + if (!track.propagateTo(track.getX() + deltaX, bz)) { + return false; + } + break; + } + if (math_utils::detail::abs(deltaPhi) < (2 * MaxPhiLocSafe)) { // still can go in 1 step with one extra rotation + auto rot = phiLoc + 0.5 * deltaPhi; + if (!track.rotate(track.getAlpha() + rot)) { + return false; + } + phiLoc -= rot; + continue; // should be ok for the case 1 now. + } + + auto rot = phiLoc + (deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe); + if (!track.rotate(track.getAlpha() + rot)) { + return false; + } + phiLoc -= rot; // = +- MaxPhiLocSafe + + // propagate to phiLoc = +-MaxPhiLocSafe + auto tgtPhiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; + auto deltaX = (math_utils::detail::sin(tgtPhiLoc) - track.getSnp()) / track.getCurvature(bz); + if (!track.propagateTo(track.getX() + deltaX, bz)) { + return false; + } + deltaPhi -= tgtPhiLoc - phiLoc; + phiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; + continue; // should be of for the case 1 now. + } + bz = getBz(math_utils::Point3D{value_type(cross.xDCA[sel]), value_type(cross.yDCA[sel]), value_type(track.getZ())}); + } + // do final step till target R, also covers Bz = 0; + value_type xfin; + if (!track.getXatLabR(r, xfin, bz)) { + return false; + } + return propagateToX(track, xfin, bzOnly, maxSnp, maxStep, matCorr, tofInfo, signCorr); +} + //_______________________________________________________________________ template template @@ -772,6 +868,35 @@ GPUd() void PropagatorImpl::getFieldXYZImpl(const math_utils::Point3D +template +GPUd() T PropagatorImpl::getBzImpl(const math_utils::Point3D xyz) const +{ + T bz = 0; + if (mGPUField) { +#if defined(GPUCA_GPUCODE_DEVICE) && defined(GPUCA_HAS_GLOBAL_SYMBOL_CONSTANT_MEM) + const auto* f = &GPUCA_CONSMEM.param.polynomialField; // Access directly from constant memory on GPU (copied here to avoid complicated header dependencies) +#else + const auto* f = mGPUField; +#endif + constexpr value_type kCLight1 = 1. / o2::gpu::gpu_common_constants::kCLight; + bz = f->GetFieldBz(xyz.X(), xyz.Y(), xyz.Z()) * kCLight1; + } else { +#ifndef GPUCA_GPUCODE + if (mFieldFast) { + mFieldFast->GetBz(xyz, bz); // Must not call the host-only function in GPU compilation + } else { +#ifdef GPUCA_STANDALONE + LOG(fatal) << "Normal field cannot be used in standalone benchmark"; +#else + bz = mField->GetBz(xyz.X(), xyz.Y(), xyz.Z()); +#endif + } +#endif + } + return bz; +} + template GPUd() void PropagatorImpl::getFieldXYZ(const math_utils::Point3D xyz, float* bxyz) const { @@ -784,12 +909,26 @@ GPUd() void PropagatorImpl::getFieldXYZ(const math_utils::Point3D(xyz, bxyz); } +template +GPUd() float PropagatorImpl::getBz(const math_utils::Point3D xyz) const +{ + return getBzImpl(xyz); +} + +template +GPUd() double PropagatorImpl::getBz(const math_utils::Point3D xyz) const +{ + return getBzImpl(xyz); +} + namespace o2::base { #if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code. template class PropagatorImpl; template bool GPUdni() PropagatorImpl::propagateToAlphaX::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; template bool GPUdni() PropagatorImpl::propagateToAlphaX::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToR::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, bool, float, float, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToR::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, bool, float, float, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; #endif #ifndef GPUCA_GPUCODE template class PropagatorImpl; diff --git a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h b/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h index cd1742e24fa72..d5bc6631575af 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h @@ -20,7 +20,7 @@ #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/TrackFwd.h" #include "ReconstructionDataFormats/Track.h" -#include "DCAFitter/HelixHelper.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GeometryManager.h" diff --git a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h b/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h index 72066250f1053..d197cba256c0e 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h @@ -30,18 +30,18 @@ namespace track struct TrackAuxPar : public o2::math_utils::CircleXYf_t { float c, s, cc, ss, cs; // cos ans sin of track alpha and their products - TrackAuxPar() = default; + GPUdDefault() TrackAuxPar() = default; template - TrackAuxPar(const T& trc, float bz) + GPUd() TrackAuxPar(const T& trc, float bz) { set(trc, bz); } - float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) - float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) + GPUd() float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) + GPUd() float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) template - void set(const T& trc, float bz) + GPUd() void set(const T& trc, float bz) { trc.getCircleParams(bz, *this, s, c); cc = c * c; @@ -59,13 +59,14 @@ struct CrossInfo { float yDCA[2] = {}; int nDCA = 0; - int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef) + GPUd() int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) { const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; + nDCA = 0; float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; - float dist2 = xDist * xDist + yDist * yDist, dist = std::sqrt(dist2), rsum = trcA.rC + trcB.rC; - if (std::abs(dist) < 1e-12) { + float dist2 = xDist * xDist + yDist * yDist, dist = o2::gpu::GPUCommonMath::Sqrt(dist2), rsum = trcA.rC + trcB.rC; + if (dist < 1e-12) { return nDCA; // circles are concentric? } if (dist > rsum) { // circles don't touch, chose a point in between @@ -74,18 +75,32 @@ struct CrossInfo { if (dist - rsum > maxDistXY) { // too large distance return nDCA; } - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } else if (dist + trcB.rC < trcA.rC) { // the small circle is nestled into large one w/o touching - // select the point of closest approach of 2 circles - notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC); + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC, isCollinear); + } else if (auto dfr = dist + trcB.rC - trcA.rC; dfr < 0.) { // the small circle is nestled into large one w/o touching + if (dfr > -maxDistXY) { + // select the point of closest approach of 2 circles + notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC, isCollinear); + } else { + return nDCA; + } } else { // 2 intersection points - // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that - // the 1st one is centered in origin - if (std::abs(xDist) < std::abs(yDist)) { + if (isCollinear) { + /// collinear tracks, e.g. electrons from photon conversion + /// if there are 2 crossings of the circle it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + trcB.rC; + float r1_r = trcA.rC / r2r; + float r2_r = trcB.rC / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * trcB.xC; + yDCA[0] = r2_r * trcA.yC + r1_r * trcB.yC; + nDCA = 1; + } else if (o2::gpu::GPUCommonMath::Abs(xDist) < o2::gpu::GPUCommonMath::Abs(yDist)) { + // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that + // the 1st one is centered in origin float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); if (det > 0.) { - det = std::sqrt(det); + det = o2::gpu::GPUCommonMath::Sqrt(det); xDCA[0] = (-ab + det) / (1. + b * b); yDCA[0] = a + b * xDCA[0] + trcA.yC; xDCA[0] += trcA.xC; @@ -100,7 +115,7 @@ struct CrossInfo { float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); if (det > 0.) { - det = std::sqrt(det); + det = o2::gpu::GPUCommonMath::Sqrt(det); yDCA[0] = (-ab + det) / (1. + bb); xDCA[0] = a + b * yDCA[0] + trcA.xC; yDCA[0] += trcA.yC; @@ -116,23 +131,33 @@ struct CrossInfo { return nDCA; } - void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign) + GPUd() void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign, bool isCollinear = false) { - // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: - // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist - // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... - // There are 2 special cases: - // (a) small circle is inside the large one: provide rBSign as -trcB.rC - // (b) circle are side by side: provide rBSign as trcB.rC + if (isCollinear) { + /// for collinear tracks it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + rBSign; + float r1_r = trcA.rC / r2r; + float r2_r = rBSign / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * (xDist + trcA.xC); + yDCA[0] = r2_r * trcA.yC + r1_r * (yDist + trcA.yC); + } else { + // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: + // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist + // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... + // There are 2 special cases: + // (a) small circle is inside the large one: provide rBSign as -trcB.rC + // (b) circle are side by side: provide rBSign as trcB.rC + auto t2d = (dist + trcA.rC - rBSign) / dist; + xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); + yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); + } nDCA = 1; - auto t2d = (dist + trcA.rC - rBSign) / dist; - xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); - yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); } template - int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + GPUd() int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) { /// closest approach of 2 straight lines /// TrackParam propagation can be parameterized in lab in a form @@ -147,19 +172,19 @@ struct CrossInfo { /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) /// zL(t) = zL + t Kz; Kz = tgl / csp /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 - + nDCA = 0; float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! float dy = trax1.yC - trax0.yC; // float dz = tr1.getZ() - tr0.getZ(); auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 - auto csp0i = std::sqrt(csp0i2); + auto csp0i = o2::gpu::GPUCommonMath::Sqrt(csp0i2); auto tgp0 = tr0.getSnp() * csp0i; float kx0 = trax0.c - trax0.s * tgp0; float ky0 = trax0.s + trax0.c * tgp0; float kz0 = tr0.getTgl() * csp0i; auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 - auto csp1i = std::sqrt(csp1i2); - auto tgp1 = tr1.getSnp() * std::sqrt(csp1i2); + auto csp1i = o2::gpu::GPUCommonMath::Sqrt(csp1i2); + auto tgp1 = tr1.getSnp() * o2::gpu::GPUCommonMath::Sqrt(csp1i2); float kx1 = trax1.c - trax1.s * tgp1; float ky1 = trax1.s + trax1.c * tgp1; float kz1 = tr1.getTgl() * csp1i; @@ -174,7 +199,7 @@ struct CrossInfo { float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; - if (std::abs(det) > o2::constants::math::Almost0) { + if (o2::gpu::GPUCommonMath::Sqrt(det) > o2::constants::math::Almost0) { auto detI = 1. / det; auto t0 = det0 * detI; auto t1 = det1 * detI; @@ -192,8 +217,8 @@ struct CrossInfo { } template - int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + GPUd() int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) { /// closest approach of line and circle /// TrackParam propagation can be parameterized in lab in a form @@ -218,14 +243,14 @@ struct CrossInfo { float dy = traxL.yC - traxH.yC; // Y... // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 - auto cspi = std::sqrt(cspi2); + auto cspi = o2::gpu::GPUCommonMath::Sqrt(cspi2); auto tgp = trcL.getSnp() * cspi; float kx = traxL.c - traxL.s * tgp; float ky = traxL.s + traxL.c * tgp; double dk = dx * kx + dy * ky; double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); if (det > 0) { // 2 crossings - det = std::sqrt(det); + det = o2::gpu::GPUCommonMath::Sqrt(det); float t0 = (-dk + det) * cspi2; float t1 = (-dk - det) * cspi2; xDCA[0] = traxL.xC + kx * t0; @@ -236,8 +261,8 @@ struct CrossInfo { } else { // there is no crossing, find the point of the closest approach on the line which is closest to the circle center float t = -dk * cspi2; - float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle - float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = std::sqrt(dxc * dxc + dyc * dyc); + float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle + float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = o2::gpu::GPUCommonMath::Sqrt(dxc * dxc + dyc * dyc); if (dist - traxH.rC > maxDistXY) { // too large distance return nDCA; } @@ -251,12 +276,12 @@ struct CrossInfo { } template - int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + GPUd() int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) { // calculate up to 2 crossings between 2 circles nDCA = 0; if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines - nDCA = circlesCrossInfo(trax0, trax1, maxDistXY); + nDCA = circlesCrossInfo(trax0, trax1, maxDistXY, isCollinear); } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); } else { @@ -266,12 +291,12 @@ struct CrossInfo { return nDCA; } - CrossInfo() = default; + GPUdDefault() CrossInfo() = default; template - CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + GPUd() CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) { - set(trax0, tr0, trax1, tr1, maxDistXY); + set(trax0, tr0, trax1, tr1, maxDistXY, isCollinear); } ClassDefNV(CrossInfo, 1); }; From 922357e8384784fae9a192f59aa4bbe9ce0ceaf9 Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 29 Sep 2025 17:51:21 +0400 Subject: [PATCH 24/99] Temporarily keep reduntant HelixHelper in DCAFitter and DetectorsVertexing for O2Physics --- .../DCAFitter/include/DCAFitter/HelixHelper.h | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 Common/DCAFitter/include/DCAFitter/HelixHelper.h diff --git a/Common/DCAFitter/include/DCAFitter/HelixHelper.h b/Common/DCAFitter/include/DCAFitter/HelixHelper.h new file mode 100644 index 0000000000000..d197cba256c0e --- /dev/null +++ b/Common/DCAFitter/include/DCAFitter/HelixHelper.h @@ -0,0 +1,307 @@ +// 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 HelixHelper.h +/// \brief Helper classes for helical tracks manipulations +/// \author ruben.shahoyan@cern.ch + +#ifndef _ALICEO2_HELIX_HELPER_ +#define _ALICEO2_HELIX_HELPER_ + +#include "CommonConstants/MathConstants.h" +#include "MathUtils/Utils.h" +#include "MathUtils/Primitive2D.h" + +namespace o2 +{ +namespace track +{ + +///__________________________________________________________________________ +//< precalculated track radius, center, alpha sin,cos and their combinations +struct TrackAuxPar : public o2::math_utils::CircleXYf_t { + float c, s, cc, ss, cs; // cos ans sin of track alpha and their products + + GPUdDefault() TrackAuxPar() = default; + + template + GPUd() TrackAuxPar(const T& trc, float bz) + { + set(trc, bz); + } + GPUd() float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) + GPUd() float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) + + template + GPUd() void set(const T& trc, float bz) + { + trc.getCircleParams(bz, *this, s, c); + cc = c * c; + ss = s * s; + cs = c * s; + } + ClassDefNV(TrackAuxPar, 1); +}; + +//__________________________________________________________ +//< crossing coordinates of 2 circles +struct CrossInfo { + static constexpr float MaxDistXYDef = 10.; + float xDCA[2] = {}; + float yDCA[2] = {}; + int nDCA = 0; + + GPUd() int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A + const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; + nDCA = 0; + float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; + float dist2 = xDist * xDist + yDist * yDist, dist = o2::gpu::GPUCommonMath::Sqrt(dist2), rsum = trcA.rC + trcB.rC; + if (dist < 1e-12) { + return nDCA; // circles are concentric? + } + if (dist > rsum) { // circles don't touch, chose a point in between + // the parametric equation of lines connecting the centers is + // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) + if (dist - rsum > maxDistXY) { // too large distance + return nDCA; + } + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC, isCollinear); + } else if (auto dfr = dist + trcB.rC - trcA.rC; dfr < 0.) { // the small circle is nestled into large one w/o touching + if (dfr > -maxDistXY) { + // select the point of closest approach of 2 circles + notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC, isCollinear); + } else { + return nDCA; + } + } else { // 2 intersection points + if (isCollinear) { + /// collinear tracks, e.g. electrons from photon conversion + /// if there are 2 crossings of the circle it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + trcB.rC; + float r1_r = trcA.rC / r2r; + float r2_r = trcB.rC / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * trcB.xC; + yDCA[0] = r2_r * trcA.yC + r1_r * trcB.yC; + nDCA = 1; + } else if (o2::gpu::GPUCommonMath::Abs(xDist) < o2::gpu::GPUCommonMath::Abs(yDist)) { + // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that + // the 1st one is centered in origin + float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; + float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); + if (det > 0.) { + det = o2::gpu::GPUCommonMath::Sqrt(det); + xDCA[0] = (-ab + det) / (1. + b * b); + yDCA[0] = a + b * xDCA[0] + trcA.yC; + xDCA[0] += trcA.xC; + xDCA[1] = (-ab - det) / (1. + b * b); + yDCA[1] = a + b * xDCA[1] + trcA.yC; + xDCA[1] += trcA.xC; + nDCA = 2; + } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); + } + } else { + float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; + float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); + if (det > 0.) { + det = o2::gpu::GPUCommonMath::Sqrt(det); + yDCA[0] = (-ab + det) / (1. + bb); + xDCA[0] = a + b * yDCA[0] + trcA.xC; + yDCA[0] += trcA.yC; + yDCA[1] = (-ab - det) / (1. + bb); + xDCA[1] = a + b * yDCA[1] + trcA.xC; + yDCA[1] += trcA.yC; + nDCA = 2; + } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); + } + } + } + return nDCA; + } + + GPUd() void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign, bool isCollinear = false) + { + if (isCollinear) { + /// for collinear tracks it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + rBSign; + float r1_r = trcA.rC / r2r; + float r2_r = rBSign / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * (xDist + trcA.xC); + yDCA[0] = r2_r * trcA.yC + r1_r * (yDist + trcA.yC); + } else { + // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: + // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist + // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... + // There are 2 special cases: + // (a) small circle is inside the large one: provide rBSign as -trcB.rC + // (b) circle are side by side: provide rBSign as trcB.rC + auto t2d = (dist + trcA.rC - rBSign) / dist; + xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); + yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); + } + nDCA = 1; + } + + template + GPUd() int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + { + /// closest approach of 2 straight lines + /// TrackParam propagation can be parameterized in lab in a form + /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) + /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) + /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp + /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab + /// frame (filled by TrackAuxPar for straight line tracks). + /// + /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) + /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) + /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) + /// zL(t) = zL + t Kz; Kz = tgl / csp + /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 + nDCA = 0; + float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! + float dy = trax1.yC - trax0.yC; // + float dz = tr1.getZ() - tr0.getZ(); + auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 + auto csp0i = o2::gpu::GPUCommonMath::Sqrt(csp0i2); + auto tgp0 = tr0.getSnp() * csp0i; + float kx0 = trax0.c - trax0.s * tgp0; + float ky0 = trax0.s + trax0.c * tgp0; + float kz0 = tr0.getTgl() * csp0i; + auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 + auto csp1i = o2::gpu::GPUCommonMath::Sqrt(csp1i2); + auto tgp1 = tr1.getSnp() * o2::gpu::GPUCommonMath::Sqrt(csp1i2); + float kx1 = trax1.c - trax1.s * tgp1; + float ky1 = trax1.s + trax1.c * tgp1; + float kz1 = tr1.getTgl() * csp1i; + /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach + /// Leads to system + /// A Dx = B with Dx = {dx0, dx1} + /// with A = + /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... + /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 + /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } + /// + float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); + float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); + float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; + if (o2::gpu::GPUCommonMath::Sqrt(det) > o2::constants::math::Almost0) { + auto detI = 1. / det; + auto t0 = det0 * detI; + auto t1 = det1 * detI; + float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; + dx += addx1 - addx0; // recalculate XY distance at DCA + dy += addy1 - addy0; + if (dx * dx + dy * dy > maxDistXY * maxDistXY) { + return nDCA; + } + xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; + yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; + nDCA = 1; + } + return nDCA; + } + + template + GPUd() int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + { + /// closest approach of line and circle + /// TrackParam propagation can be parameterized in lab in a form + /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) + /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) + /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp + /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab + /// frame (filled by TrackAuxPar for straight line tracks). + /// + /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) + /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) + /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) + /// zL(t) = zL + t Kz; Kz = tgl / csp + /// Note that Kx^2 + Ky^2 = 1 / csp^2 + + const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) + const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line + const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line + + // solve quadratic equation of line crossing the circle + float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center + float dy = traxL.yC - traxH.yC; // Y... + // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 + auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 + auto cspi = o2::gpu::GPUCommonMath::Sqrt(cspi2); + auto tgp = trcL.getSnp() * cspi; + float kx = traxL.c - traxL.s * tgp; + float ky = traxL.s + traxL.c * tgp; + double dk = dx * kx + dy * ky; + double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); + if (det > 0) { // 2 crossings + det = o2::gpu::GPUCommonMath::Sqrt(det); + float t0 = (-dk + det) * cspi2; + float t1 = (-dk - det) * cspi2; + xDCA[0] = traxL.xC + kx * t0; + yDCA[0] = traxL.yC + ky * t0; + xDCA[1] = traxL.xC + kx * t1; + yDCA[1] = traxL.yC + ky * t1; + nDCA = 2; + } else { + // there is no crossing, find the point of the closest approach on the line which is closest to the circle center + float t = -dk * cspi2; + float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle + float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = o2::gpu::GPUCommonMath::Sqrt(dxc * dxc + dyc * dyc); + if (dist - traxH.rC > maxDistXY) { // too large distance + return nDCA; + } + float drcf = traxH.rC / dist; // radius / distance to circle center + float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; + xDCA[0] = (xL + xH) * 0.5; + yDCA[0] = (yL + yH) * 0.5; + nDCA = 1; + } + return nDCA; + } + + template + GPUd() int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + // calculate up to 2 crossings between 2 circles + nDCA = 0; + if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines + nDCA = circlesCrossInfo(trax0, trax1, maxDistXY, isCollinear); + } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines + nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); + } else { + nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); + } + // + return nDCA; + } + + GPUdDefault() CrossInfo() = default; + + template + GPUd() CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + set(trax0, tr0, trax1, tr1, maxDistXY, isCollinear); + } + ClassDefNV(CrossInfo, 1); +}; + +} // namespace track +} // namespace o2 + +#endif From f26a285ab2a9a666cac23b0d7069a1cd9aea63b1 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Tue, 30 Sep 2025 08:31:39 +0200 Subject: [PATCH 25/99] Add possibility to apply signal filtering for MC with embedding (#14698) * Add possibility to apply signal filtering for MC with embedding * Add protection for signal filtering to be enabled only with emdedding --- .../AODProducerWorkflowSpec.h | 1 + Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 44 +++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 62b99e98f990d..615a7f96de13e 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -256,6 +256,7 @@ class AODProducerWorkflowDPL : public Task int mNThreads = 1; bool mUseMC = true; + bool mUseSigFiltMC = false; // enable signal filtering for MC with embedding bool mEnableSV = true; // enable secondary vertices bool mEnableFITextra = false; bool mFieldON = false; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 90cf420bc9bf6..b841943b05031 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -947,13 +947,17 @@ void clearMCKeepStore(std::vector>>& st } // helper function to add a particle/track to the MC keep store -void keepMCParticle(std::vector>>& store, int source, int event, int track, int value = 1) +void keepMCParticle(std::vector>>& store, int source, int event, int track, int value = 1, bool useSigFilt = false) { if (track < 0) { LOG(warn) << "trackID is smaller than 0. Neglecting"; return; } - store[source][event][track] = value; + if (useSigFilt && source == 0) { + store[source][event][track] = -1; + } else { + store[source][event][track] = value; + } } void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, @@ -982,7 +986,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcLabel.isValid()) { return; } - keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID()); + keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID(), 1, mUseSigFiltMC); }; // mark reconstructed MC particles to store them into the table @@ -997,7 +1001,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcTruth.isValid()) { continue; } - keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID()); + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); // treating contributors of global tracks auto contributorsGID = data.getSingleDetectorRefs(trackIndex); if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { @@ -1012,7 +1016,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcLabel.isValid()) { continue; } - keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID()); + keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID(), 1, mUseSigFiltMC); } } } @@ -1026,7 +1030,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcTruth.isValid()) { continue; } - keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID()); + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); } } if (mInputSources[GIndex::PHS]) { @@ -1035,7 +1039,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcTruth.isValid()) { continue; } - keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID()); + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); } } using namespace aodmchelpers; @@ -1743,6 +1747,8 @@ void AODProducerWorkflowDPL::init(InitContext& ic) LOG(info) << "The Run number will be obtained from DPL headers"; } + mUseSigFiltMC = ic.options().get("mc-signal-filt"); + // set no truncation if selected by user if (mTruncate != 1) { LOG(info) << "Truncation is not used!"; @@ -2061,6 +2067,24 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) int totalNParts = 0; for (int iCol = 0; iCol < nMCCollisions; iCol++) { totalNParts += mcParts[iCol].size(); + + // if signal filtering enabled, let's check if there are more than one source; otherwise fatalise + if (mUseSigFiltMC) { + std::vector sourceIDs{}; + auto& colParts = mcParts[iCol]; + for (auto colPart : colParts) { + int sourceID = colPart.sourceID; + if (std::find(sourceIDs.begin(), sourceIDs.end(), sourceID) == sourceIDs.end()) { + sourceIDs.push_back(sourceID); + } + if (sourceIDs.size() > 1) { // we found more than one, exit + break; + } + } + if (sourceIDs.size() <= 1) { + LOGP(fatal, "Signal filtering cannot be enabled without embedding. Please fix the configuration either enabling the embedding, or turning off the signal filtering."); + } + } } mcCollisionsCursor.reserve(totalNParts); @@ -2098,7 +2122,9 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) 0, sourceID); } - mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision + if (sourceID != 0 || !mUseSigFiltMC) { + mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision + } } } } @@ -3326,7 +3352,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"trackqc-tpc-pt", VariantType::Float, 0.2f, {"Keep TPC standalone track with this pt"}}, ConfigParamSpec{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}}, ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}}, - }}; + ConfigParamSpec{"mc-signal-filt", VariantType::Bool, false, {"Enable usage of signal filtering (only for MC with embedding)"}}}}; } } // namespace o2::aodproducer From 15b0fa3fd57dc7b9e5581eb0c02e5fae7161738a Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:31:50 +0200 Subject: [PATCH 26/99] DPL Analysis: write HistogramRegistry incrementally This should reduce big spikes at the end of the processing when a large HistogramRegistry is serialised. --- .../AnalysisSupport/src/AODWriterHelpers.cxx | 137 ++++++++++++------ .../Core/include/Framework/AnalysisManagers.h | 6 +- .../include/Framework/HistogramRegistry.h | 14 +- .../Core/include/Framework/OutputObjHeader.h | 2 + Framework/Core/src/HistogramRegistry.cxx | 94 +++--------- .../TestWorkflows/src/o2TestHistograms.cxx | 46 ++++++ 6 files changed, 167 insertions(+), 132 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index 475e65bf9212b..27dad43480913 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -22,14 +22,20 @@ #include "Framework/DataOutputDirector.h" #include "Framework/TableTreeHelpers.h" #include "Framework/Monitoring.h" +#include "Framework/Signpost.h" #include +#include #include #include #include #include #include #include +#include +#include + +O2_DECLARE_DYNAMIC_LOG(histogram_registry); namespace o2::framework::writers { @@ -46,6 +52,7 @@ struct InputObjectRoute { struct InputObject { TClass* kind = nullptr; void* obj = nullptr; + std::string container; std::string name; int count = -1; }; @@ -273,24 +280,30 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) callbacks.set(endofdatacb); return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { auto mergePart = [&inputObjects, &objmap, &tskmap, &pc](DataRef const& ref) { + O2_SIGNPOST_ID_GENERATE(hid, histogram_registry); + O2_SIGNPOST_START(histogram_registry, hid, "mergePart", "Merging histogram"); if (!ref.header) { - LOG(error) << "Header not found"; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "Header not found."); return; } auto datah = o2::header::get(ref.header); if (!datah) { - LOG(error) << "No data header in stack"; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No data header in stack"); return; } if (!ref.payload) { - LOGP(error, "Payload not found for {}/{}/{}", datah->dataOrigin.as(), datah->dataDescription.as(), datah->subSpecification); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "Payload not found for %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); return; } auto objh = o2::header::get(ref.header); if (!objh) { - LOGP(error, "No output object header in stack of {}/{}/{}", datah->dataOrigin.as(), datah->dataDescription.as(), datah->subSpecification); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No output object header in stack of %{public}s/%{public}s/%d.", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); return; } @@ -300,48 +313,73 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) obj.kind = tm.ReadClass(); tm.SetBufferOffset(0); tm.ResetMap(); + O2_SIGNPOST_ID_GENERATE(did, histogram_registry); + O2_SIGNPOST_START(histogram_registry, did, "initialising root", "Starting deserialization of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); if (obj.kind == nullptr) { - LOGP(error, "Cannot read class info from buffer of {}/{}/{}", datah->dataOrigin.as(), datah->dataDescription.as(), datah->subSpecification); + O2_SIGNPOST_END(histogram_registry, did, "initialising root", "Failed to deserialise"); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "Cannot read class info from buffer of %{public}s/%{public}s/%d.", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); return; } + O2_SIGNPOST_END(histogram_registry, did, "initialising root", "Done init."); auto policy = objh->mPolicy; auto sourceType = objh->mSourceType; auto hash = objh->mTaskHash; + O2_SIGNPOST_START(histogram_registry, did, "deserialization", "Starting deserialization of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); obj.obj = tm.ReadObjectAny(obj.kind); auto* named = static_cast(obj.obj); obj.name = named->GetName(); + O2_SIGNPOST_END(histogram_registry, did, "deserialization", "Done deserialization."); + // If we have a folder, we assume the first element of the path + // to be the name of the registry. + if (sourceType == HistogramRegistrySource) { + obj.container = objh->containerName; + } else { + obj.container = obj.name; + } auto hpos = std::find_if(tskmap.begin(), tskmap.end(), [&](auto&& x) { return x.id == hash; }); if (hpos == tskmap.end()) { - LOG(error) << "No task found for hash " << hash; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No task found for hash %d.", hash); return; } auto taskname = hpos->name; auto opos = std::find_if(objmap.begin(), objmap.end(), [&](auto&& x) { return x.id == hash; }); if (opos == objmap.end()) { - LOG(error) << "No object list found for task " << taskname << " (hash=" << hash << ")"; + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No object list found for task %{public}s (hash=%d).", + taskname.c_str(), hash); return; } auto objects = opos->bindings; - if (std::find(objects.begin(), objects.end(), obj.name) == objects.end()) { - LOG(error) << "No object " << obj.name << " in map for task " << taskname; + if (std::find(objects.begin(), objects.end(), obj.container) == objects.end()) { + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "mergePart", "No container %{public}s in map for task %{public}s.", + obj.container.c_str(), taskname.c_str()); return; } auto nameHash = runtime_hash(obj.name.c_str()); InputObjectRoute key{obj.name, nameHash, taskname, hash, policy, sourceType}; auto existing = std::find_if(inputObjects->begin(), inputObjects->end(), [&](auto&& x) { return (x.first.uniqueId == nameHash) && (x.first.taskHash == hash); }); // If it's the first one, we just add it to the list. + O2_SIGNPOST_START(histogram_registry, did, "merging", "Starting merging of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); if (existing == inputObjects->end()) { obj.count = objh->mPipelineSize; - inputObjects->push_back(std::make_pair(key, obj)); + inputObjects->emplace_back(key, obj); existing = inputObjects->end() - 1; } else { obj.count = existing->second.count; // Otherwise, we merge it with the existing one. auto merger = existing->second.kind->GetMerge(); if (!merger) { - LOG(error) << "Already one unmergeable object found for " << obj.name; + O2_SIGNPOST_END(histogram_registry, did, "merging", "Unabled to merge"); + O2_SIGNPOST_END_WITH_ERROR(histogram_registry, hid, "merging", "Already one unmergeable object found for %{public}s", obj.name.c_str()); return; } TList coll; @@ -353,15 +391,22 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) existing->second.count -= 1; if (existing->second.count != 0) { + O2_SIGNPOST_END(histogram_registry, did, "merging", "Done partial merging."); + O2_SIGNPOST_END(histogram_registry, hid, "mergePart", "Pipeline lanes still missing."); return; } + O2_SIGNPOST_END(histogram_registry, did, "merging", "Done merging."); // Write the object here. auto route = existing->first; auto entry = existing->second; auto file = ROOTfileNames.find(route.policy); if (file == ROOTfileNames.end()) { + O2_SIGNPOST_END(histogram_registry, hid, "mergePart", "Not matching any file."); return; } + O2_SIGNPOST_START(histogram_registry, did, "writing", "Starting writing of %{public}s/%{public}s/%d", + datah->dataOrigin.as().c_str(), datah->dataDescription.as().c_str(), + datah->subSpecification); auto filename = file->second; if (f[route.policy] == nullptr) { f[route.policy] = TFile::Open(filename.c_str(), "RECREATE"); @@ -375,53 +420,53 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) currentFile = filename; } - // translate the list-structure created by the registry into a directory structure within the file - std::function writeListToFile; - writeListToFile = [&](TList* list, TDirectory* parentDir) { - TIter next(list); - TObject* object = nullptr; - while ((object = next())) { - if (object->InheritsFrom(TList::Class())) { - writeListToFile(static_cast(object), parentDir->mkdir(object->GetName(), object->GetName(), true)); + // FIXME: handle folders + f[route.policy]->cd("/"); + auto* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); + // The name contains a path... + int objSize = 0; + if (sourceType == HistogramRegistrySource) { + TDirectory* currentFolder = currentDir; + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Toplevel folder is %{public}s.", + currentDir->GetName()); + std::string objName = entry.name; + auto lastSlash = entry.name.rfind('/'); + + if (lastSlash != std::string::npos) { + auto dirname = entry.name.substr(0, lastSlash); + objName = entry.name.substr(lastSlash + 1); + currentFolder = currentDir->GetDirectory(dirname.c_str()); + if (!currentFolder) { + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Creating folder %{public}s", + dirname.c_str()); + currentFolder = currentDir->mkdir(dirname.c_str(), "", kTRUE); } else { - int objSize = parentDir->WriteObjectAny(object, object->Class(), object->GetName()); - static int maxSizeWritten = 0; - if (objSize > maxSizeWritten) { - auto& monitoring = pc.services().get(); - maxSizeWritten = objSize; - monitoring.send(Metric{fmt::format("{}/{}:{}", object->ClassName(), object->GetName(), objSize), "aod-largest-object-written"}.addTag(tags::Key::Subsystem, tags::Value::DPL)); - } - auto* written = list->Remove(object); - delete written; + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Folder %{public}s already there.", + currentFolder->GetName()); } } - }; - - TDirectory* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); - if (route.sourceType == OutputObjSourceType::HistogramRegistrySource) { - auto* outputList = static_cast(entry.obj); - outputList->SetOwner(false); - - // if registry should live in dedicated folder a TNamed object is appended to the list - if (outputList->Last() && outputList->Last()->IsA() == TNamed::Class()) { - delete outputList->Last(); - outputList->RemoveLast(); - currentDir = currentDir->mkdir(outputList->GetName(), outputList->GetName(), true); - } - - writeListToFile(outputList, currentDir); - outputList->SetOwner(); - delete outputList; + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Writing %{public}s of kind %{public}s in %{public}s", + entry.name.c_str(), entry.kind->GetName(), currentDir->GetName()); + objSize = currentFolder->WriteObjectAny(entry.obj, entry.kind, objName.c_str()); + O2_SIGNPOST_END(histogram_registry, did, "writing", "End writing %{public}s", entry.name.c_str()); + delete (TObject*)entry.obj; entry.obj = nullptr; } else { - currentDir->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); + O2_SIGNPOST_EVENT_EMIT(histogram_registry, hid, "mergePart", "Writing %{public}s of kind %{public}s in %{public}s", + entry.name.c_str(), entry.kind->GetName(), currentDir->GetName()); + objSize = currentDir->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); + O2_SIGNPOST_END(histogram_registry, did, "writing", "End writing %{public}s", entry.name.c_str()); delete (TObject*)entry.obj; entry.obj = nullptr; } + O2_SIGNPOST_END(histogram_registry, hid, "mergePart", "Done merging object of %d bytes.", objSize); }; + O2_SIGNPOST_ID_GENERATE(rid, histogram_registry); + O2_SIGNPOST_START(histogram_registry, rid, "processParts", "Start merging %zu parts received together.", pc.inputs().getNofParts(0)); for (int pi = 0; pi < pc.inputs().getNofParts(0); ++pi) { mergePart(pc.inputs().get("x", pi)); } + O2_SIGNPOST_END(histogram_registry, rid, "processParts", "Done histograms in multipart message."); }; }}; } diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index 6c43bf3eebebb..596f3da6a557a 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -11,6 +11,7 @@ #ifndef FRAMEWORK_ANALYSISMANAGERS_H #define FRAMEWORK_ANALYSISMANAGERS_H +#include "DataAllocator.h" #include "Framework/AnalysisHelpers.h" #include "Framework/DataSpecUtils.h" #include "Framework/GroupedCombinations.h" @@ -247,7 +248,10 @@ template bool postRunOutput(EndOfStreamContext& context, T& hr) { auto& deviceSpec = context.services().get(); - context.outputs().snapshot(hr.ref(deviceSpec.inputTimesliceId, deviceSpec.maxInputTimeslices), *(hr.getListOfHistograms())); + auto sendHistos = [deviceSpec, &context](HistogramRegistry const& self, TNamed* obj) mutable { + context.outputs().snapshot(self.ref(deviceSpec.inputTimesliceId, deviceSpec.maxInputTimeslices), *obj); + }; + hr.apply(sendHistos); hr.clean(); return true; } diff --git a/Framework/Core/include/Framework/HistogramRegistry.h b/Framework/Core/include/Framework/HistogramRegistry.h index 6db4bd0a2d0e2..49ef006f84a79 100644 --- a/Framework/Core/include/Framework/HistogramRegistry.h +++ b/Framework/Core/include/Framework/HistogramRegistry.h @@ -173,16 +173,15 @@ class HistogramRegistry template std::shared_ptr operator()(const HistName& histName); + // Apply @a callback on every single entry in the registry + void apply(std::function callback) const; // return the OutputSpec associated to the HistogramRegistry OutputSpec const spec(); - OutputRef ref(uint16_t idx, uint16_t pipelineSize); + OutputRef ref(uint16_t idx, uint16_t pipelineSize) const; void setHash(uint32_t hash); - /// returns the list of histograms, properly sorted for writing. - TList* getListOfHistograms(); - /// deletes all the histograms from the registry void clean(); @@ -220,16 +219,13 @@ class HistogramRegistry // helper function to find the histogram position in the registry template - uint32_t getHistIndex(const T& histName); + uint32_t getHistIndex(const T& histName) const; constexpr uint32_t imask(uint32_t i) const { return i & REGISTRY_BITMASK; } - // helper function to create resp. find the subList defined by path - TList* getSubList(TList* list, std::deque& path); - // helper function to split user defined path/to/hist/name string std::deque splitPath(const std::string& pathAndNameUser); @@ -431,7 +427,7 @@ std::shared_ptr HistogramRegistry::operator()(const HistName& histName) } template -uint32_t HistogramRegistry::getHistIndex(const T& histName) +uint32_t HistogramRegistry::getHistIndex(const T& histName) const { if (O2_BUILTIN_LIKELY(histName.hash == mRegistryKey[histName.idx])) { return histName.idx; diff --git a/Framework/Core/include/Framework/OutputObjHeader.h b/Framework/Core/include/Framework/OutputObjHeader.h index 6e665bb697572..f1c284d564f15 100644 --- a/Framework/Core/include/Framework/OutputObjHeader.h +++ b/Framework/Core/include/Framework/OutputObjHeader.h @@ -44,6 +44,8 @@ struct OutputObjHeader : public BaseHeader { uint32_t mTaskHash; uint16_t mPipelineIndex = 0; uint16_t mPipelineSize = 1; + // Name of the actual container for the object, e.g. the HistogramRegistry name + char containerName[64] = {0}; constexpr OutputObjHeader() : BaseHeader(sizeof(OutputObjHeader), sHeaderType, sSerializationMethod, sVersion), diff --git a/Framework/Core/src/HistogramRegistry.cxx b/Framework/Core/src/HistogramRegistry.cxx index 0a0cc1fc3a690..5e39fbe7181e7 100644 --- a/Framework/Core/src/HistogramRegistry.cxx +++ b/Framework/Core/src/HistogramRegistry.cxx @@ -51,9 +51,12 @@ OutputSpec const HistogramRegistry::spec() return OutputSpec{OutputLabel{mName}, "ATSK", desc, 0, Lifetime::QA}; } -OutputRef HistogramRegistry::ref(uint16_t pipelineIndex, uint16_t pipelineSize) +OutputRef HistogramRegistry::ref(uint16_t pipelineIndex, uint16_t pipelineSize) const { - return OutputRef{std::string{mName}, 0, o2::header::Stack{OutputObjHeader{mPolicy, OutputObjSourceType::HistogramRegistrySource, mTaskHash, pipelineIndex, pipelineSize}}}; + OutputObjHeader header{mPolicy, OutputObjSourceType::HistogramRegistrySource, mTaskHash, pipelineIndex, pipelineSize}; + // Copy the name of the registry to the haeder. + strncpy(header.containerName, mName.data(), 64); + return OutputRef{std::string{mName}, 0, o2::header::Stack{header}}; } void HistogramRegistry::setHash(uint32_t hash) @@ -282,87 +285,26 @@ void HistogramRegistry::print(bool showAxisDetails) LOGF(info, ""); } -// create output structure will be propagated to file-sink -TList* HistogramRegistry::getListOfHistograms() +void HistogramRegistry::apply(std::function callback) const { - TList* list = new TList(); - list->SetName(mName.data()); - + // Keep the list sorted as originally done to avoid hidden dependency on the order, for now , for now. + auto finalList = mRegisteredNames; + auto caseInsensitiveCompare = [](const std::string& s1, const std::string& s2) { + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return std::tolower(static_cast(c1)) < std::tolower(static_cast(c2)); }); + }; if (mSortHistos) { - auto caseInsensitiveCompare = [](const std::string& s1, const std::string& s2) { - return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), - [](char c1, char c2) { return std::tolower(static_cast(c1)) < std::tolower(static_cast(c2)); }); - }; - std::sort(mRegisteredNames.begin(), mRegisteredNames.end(), caseInsensitiveCompare); + std::sort(finalList.begin(), finalList.end(), caseInsensitiveCompare); } - - for (auto& curHistName : mRegisteredNames) { + for (auto& curHistName : finalList) { TNamed* rawPtr = nullptr; std::visit([&](const auto& sharedPtr) { rawPtr = (TNamed*)sharedPtr.get(); }, mRegistryValue[getHistIndex(HistName{curHistName.data()})]); - if (rawPtr) { - std::deque path = splitPath(rawPtr->GetName()); - std::string name = path.back(); - path.pop_back(); - TList* targetList{getSubList(list, path)}; - if (targetList) { - rawPtr->SetName(name.data()); - targetList->Add(rawPtr); - } else { - LOGF(fatal, "Specified subfolder could not be created."); - } - } - } - - // place lists always at the top - std::function moveListsToTop; - moveListsToTop = [&](TList* list) { - TIter next(list); - TNamed* subList = nullptr; - std::vector subLists; - while ((subList = (TNamed*)next())) { - if (subList->InheritsFrom(TList::Class())) { - subLists.push_back(subList); - moveListsToTop((TList*)subList); - } - } - std::reverse(subLists.begin(), subLists.end()); - for (auto curList : subLists) { - list->Remove(curList); - list->AddFirst(curList); - } - }; - moveListsToTop(list); - - // create dedicated directory containing all of the registrys histograms - if (mCreateRegistryDir) { - // propagate this to the writer by adding a 'flag' to the output list - list->AddLast(new TNamed("createFolder", "")); - } - return list; -} - -// helper function to create resp. find the subList defined by path -TList* HistogramRegistry::getSubList(TList* list, std::deque& path) -{ - if (path.empty()) { - return list; - } - TList* targetList{nullptr}; - std::string nextList = path[0]; - path.pop_front(); - if (auto subList = (TList*)list->FindObject(nextList.data())) { - if (subList->InheritsFrom(TList::Class())) { - targetList = getSubList((TList*)subList, path); - } else { - return nullptr; + if (!rawPtr) { + // Skipping empty histograms + continue; } - } else { - subList = new TList(); - subList->SetName(nextList.data()); - list->Add(subList); - targetList = getSubList(subList, path); + callback(*this, rawPtr); } - return targetList; } // helper function to split user defined path/to/hist/name string diff --git a/Framework/TestWorkflows/src/o2TestHistograms.cxx b/Framework/TestWorkflows/src/o2TestHistograms.cxx index 61710e1f63d5f..ae3610ca01e67 100644 --- a/Framework/TestWorkflows/src/o2TestHistograms.cxx +++ b/Framework/TestWorkflows/src/o2TestHistograms.cxx @@ -43,6 +43,16 @@ struct EtaAndClsHistogramsSimple { Configurable trackFilterString{"track-filter", "o2::aod::track::pt < 10.f", "Track filter string"}; Filter trackFilter = o2::aod::track::pt < 10.f; + HistogramRegistry registry{ + "registry", + { + {"a/b/eta", "#Eta", {HistType::kTH1F, {{100, -2.0, 2.0}}}}, // + {"a/phi", "#Phi", {HistType::kTH1D, {{102, 0, 2 * M_PI}}}}, // + {"c/pt", "p_{T}", {HistType::kTH1D, {{1002, -0.01, 50.1}}}}, // + {"ptToPt", "#ptToPt", {HistType::kTH2F, {{100, -0.01, 10.01}, {100, -0.01, 10.01}}}} // + } // + }; + void init(InitContext&) { if (!trackFilterString->empty()) { @@ -56,6 +66,11 @@ struct EtaAndClsHistogramsSimple { for (auto& track : tracks) { etaClsH->Fill(track.eta(), track.pt()); skimEx(track.pt(), track.eta()); + + registry.fill(HIST("a/b/eta"), track.eta()); + registry.fill(HIST("a/phi"), track.phi()); + registry.fill(HIST("c/pt"), track.pt()); + registry.fill(HIST("ptToPt"), track.pt(), track.signed1Pt()); } } }; @@ -66,6 +81,16 @@ struct EtaAndClsHistogramsIUSimple { Configurable trackFilterString{"track-filter", "o2::aod::track::pt < 10.f", "Track filter string"}; Filter trackFilter = o2::aod::track::pt < 10.f; + HistogramRegistry registry{ + "registry", + { + {"a/b/eta", "#Eta", {HistType::kTH1F, {{100, -2.0, 2.0}}}}, // + {"a/phi", "#Phi", {HistType::kTH1D, {{102, 0, 2 * M_PI}}}}, // + {"c/pt", "p_{T}", {HistType::kTH1D, {{1002, -0.01, 50.1}}}}, // + {"ptToPt", "#ptToPt", {HistType::kTH2F, {{100, -0.01, 10.01}, {100, -0.01, 10.01}}}} // + } // + }; + void init(InitContext&) { if (!trackFilterString->empty()) { @@ -79,12 +104,28 @@ struct EtaAndClsHistogramsIUSimple { for (auto& track : tracks) { etaClsH->Fill(track.eta(), track.pt()); skimEx(track.pt(), track.eta()); + + registry.fill(HIST("a/b/eta"), track.eta()); + registry.fill(HIST("a/phi"), track.phi()); + registry.fill(HIST("c/pt"), track.pt()); + registry.fill(HIST("ptToPt"), track.pt(), track.signed1Pt()); } } }; struct EtaAndClsHistogramsFull { OutputObj etaClsH{TH3F("eta_vs_cls_vs_sigmapT", "#eta vs N_{cls} vs sigma_{1/pT}", 102, -2.01, 2.01, 160, -0.5, 159.5, 100, 0, 10)}; + + HistogramRegistry registry{ + "registry", + { + {"a/b/eta", "#Eta", {HistType::kTH1F, {{100, -2.0, 2.0}}}}, // + {"a/phi", "#Phi", {HistType::kTH1D, {{102, 0, 2 * M_PI}}}}, // + {"c/pt", "p_{T}", {HistType::kTH1D, {{1002, -0.01, 50.1}}}}, // + {"ptToPt", "#ptToPt", {HistType::kTH2F, {{100, -0.01, 10.01}, {100, -0.01, 10.01}}}} // + } // + }; + Configurable trackFilterString{"track-filter", "o2::aod::track::pt < 10.f", "Track filter string"}; Filter trackFilter = o2::aod::track::pt < 10.f; @@ -100,6 +141,11 @@ struct EtaAndClsHistogramsFull { LOGP(info, "Invoking the run 3 one"); for (auto& track : tracks) { etaClsH->Fill(track.eta(), track.tpcNClsFindable(), track.sigma1Pt()); + + registry.fill(HIST("a/b/eta"), track.eta()); + registry.fill(HIST("a/phi"), track.phi()); + registry.fill(HIST("c/pt"), track.pt()); + registry.fill(HIST("ptToPt"), track.pt(), track.signed1Pt()); } } }; From 94c006a4946475d385465fad782e0b15060f7ee8 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:24:31 +0200 Subject: [PATCH 27/99] DPL: fix typo in format --- Framework/Core/src/ArrowSupport.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 0e524da280598..a289980349924 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -399,7 +399,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() } if (forwarded) { O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Message %{public}.4s/%{public}16.s is forwarded so we are not returning its memory.", + "Message %{public}.4s/%{public}.16s is forwarded so we are not returning its memory.", dh->dataOrigin.str, dh->dataDescription.str); continue; } From c5585235d0e987665993324bda2d8f2afe5fc6ad Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:21:21 +0200 Subject: [PATCH 28/99] DPL: improve debugging of ComputingQuotaEvaluator * Use signposts rather than debug log. --- .../Core/src/ComputingQuotaEvaluator.cxx | 64 ++++++++++++------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/Framework/Core/src/ComputingQuotaEvaluator.cxx b/Framework/Core/src/ComputingQuotaEvaluator.cxx index 56b7f4a59be88..717a59f5f5372 100644 --- a/Framework/Core/src/ComputingQuotaEvaluator.cxx +++ b/Framework/Core/src/ComputingQuotaEvaluator.cxx @@ -13,17 +13,17 @@ #include "Framework/DataProcessingStats.h" #include "Framework/ServiceRegistryRef.h" #include "Framework/DeviceState.h" -#include "Framework/DriverClient.h" -#include "Framework/Monitoring.h" -#include "Framework/Logger.h" +#include "Framework/Signpost.h" #include #include #include #include +#include +#include +#include -#define LOGLEVEL debug - +O2_DECLARE_DYNAMIC_LOG(quota); namespace o2::framework { @@ -64,6 +64,8 @@ struct QuotaEvaluatorStats { bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& selector, uint64_t now) { + O2_SIGNPOST_ID_GENERATE(qid, quota); + auto selectOffer = [&offers = this->mOffers, &infos = this->mInfos, task](int ref, uint64_t now) { auto& selected = offers[ref]; auto& info = infos[ref]; @@ -89,28 +91,36 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& // LOG(LOGLEVEL) << "No particular resource was requested, so we schedule task anyways"; return enough; } + O2_SIGNPOST_ID_GENERATE(sid, quota); if (enough) { - LOGP(LOGLEVEL, "{} offers were selected for a total of: cpu {}, memory {}, shared memory {}", result.size(), totalOffer.cpu, totalOffer.memory, totalOffer.sharedMemory); - //LOG(LOGLEVEL) << " The following offers were selected for computation: {} " << fmt::join(result, ", "); + O2_SIGNPOST_START(quota, sid, "summary", "%zu offers were selected for a total of: cpu %d, memory %lli, shared memory %lli", + result.size(), totalOffer.cpu, totalOffer.memory, totalOffer.sharedMemory); + for (auto& offer : result) { + // We pretend each offer id is a pointer, to have a unique id. + O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(offer*8)); + O2_SIGNPOST_START(quota, oid, "offers", "Offer %d has been selected.", offer); + } dpStats.updateStats({static_cast(ProcessingStatsId::RESOURCES_SATISFACTORY), DataProcessingStats::Op::Add, 1}); } else { + O2_SIGNPOST_START(quota, sid, "summary", "Not enough resources to select offers."); dpStats.updateStats({static_cast(ProcessingStatsId::RESOURCES_MISSING), DataProcessingStats::Op::Add, 1}); if (result.size()) { dpStats.updateStats({static_cast(ProcessingStatsId::RESOURCES_INSUFFICIENT), DataProcessingStats::Op::Add, 1}); } } if (stats.invalidOffers.size()) { - // LOGP(LOGLEVEL, " The following offers were invalid: {}", fmt::join(stats.invalidOffers, ", ")); + O2_SIGNPOST_EVENT_EMIT(quota, sid, "summary", "The following offers were invalid: %s", fmt::format("{}", fmt::join(stats.invalidOffers, ", ")).c_str()); } if (stats.otherUser.size()) { - // LOGP(LOGLEVEL, " The following offers were owned by other users: {}", fmt::join(stats.otherUser, ", ")); + O2_SIGNPOST_EVENT_EMIT(quota, sid, "summary", "The following offers were owned by other users: %s", fmt::format("{}", fmt::join(stats.otherUser, ", ")).c_str()); } if (stats.expired.size()) { - // LOGP(LOGLEVEL, " The following offers are expired: {}", fmt::join(stats.expired, ", ")); + O2_SIGNPOST_EVENT_EMIT(quota, sid, "summary", "The following offers are expired: %s", fmt::format("{}", fmt::join(stats.expired, ", ")).c_str()); } if (stats.unexpiring.size() > 1) { - // LOGP(LOGLEVEL, " The following offers will never expire: {}", fmt::join(stats.unexpiring, ", ")); + O2_SIGNPOST_EVENT_EMIT(quota, sid, "summary", "The following offers will never expire: %s", fmt::format("{}", fmt::join(stats.unexpiring, ", ")).c_str()); } + O2_SIGNPOST_END(quota, sid, "summary", "Done selecting offers."); return enough; }; @@ -139,16 +149,18 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& if (offer.runtime < 0) { stats.unexpiring.push_back(i); } else if (offer.runtime + info.received < now) { - LOGP(LOGLEVEL, "Offer {} expired since {} milliseconds and holds {}MB", i, now - offer.runtime - info.received, offer.sharedMemory / 1000000); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d expired since %llu milliseconds and holds %llu MB", + i, now - offer.runtime - info.received, offer.sharedMemory / 1000000); mExpiredOffers.push_back(ComputingQuotaOfferRef{i}); stats.expired.push_back(i); continue; } else { - LOGP(LOGLEVEL, "Offer {} still valid for {} milliseconds, providing {}MB", i, offer.runtime + info.received - now, offer.sharedMemory / 1000000); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Offer %d still valid for %llu milliseconds, providing %llu MB", + i, offer.runtime + info.received - now, offer.sharedMemory / 1000000); if (minValidity == 0) { minValidity = offer.runtime + info.received - now; } - minValidity = std::min(minValidity,(int64_t)(offer.runtime + info.received - now)); + minValidity = std::min(minValidity, (int64_t)(offer.runtime + info.received - now)); } /// We then check if the offer is suitable assert(offer.sharedMemory >= 0); @@ -177,11 +189,10 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& } if (minValidity != 0) { - LOGP(LOGLEVEL, "Next offer to expire in {} milliseconds", minValidity); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "select", "Next offer to expire in %llu milliseconds", minValidity); uv_timer_start(mTimer, [](uv_timer_t* handle) { - LOGP(LOGLEVEL, "Offer should be expired by now, checking again"); - }, - minValidity + 100, 0); + O2_SIGNPOST_ID_GENERATE(tid, quota); + O2_SIGNPOST_EVENT_EMIT(quota, tid, "select", "Offer should be expired by now, checking again."); }, minValidity + 100, 0); } // If we get here it means we never got enough offers, so we return false. return summarizeWhatHappended(enough, stats.selectedOffers, accumulated, stats); @@ -213,6 +224,8 @@ void ComputingQuotaEvaluator::dispose(int taskId) continue; } if (offer.sharedMemory <= 0) { + O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(oi*8)); + O2_SIGNPOST_END(quota, oid, "offers", "Offer %d back to not needed.", oi); offer.valid = false; offer.score = OfferScore::Unneeded; } @@ -242,34 +255,37 @@ void ComputingQuotaEvaluator::updateOffers(std::vector& pen void ComputingQuotaEvaluator::handleExpired(std::function expirator) { static int nothingToDoCount = mExpiredOffers.size(); + O2_SIGNPOST_ID_GENERATE(qid, quota); if (mExpiredOffers.size()) { - LOGP(LOGLEVEL, "Handling {} expired offers", mExpiredOffers.size()); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "handleExpired", "Handling %zu expired offers", mExpiredOffers.size()); nothingToDoCount = 0; } else { if (nothingToDoCount == 0) { nothingToDoCount++; - LOGP(LOGLEVEL, "No expired offers"); + O2_SIGNPOST_EVENT_EMIT(quota, qid, "handleExpired", "No expired offers"); } } /// Whenever an offer is expired, we give back the resources /// to the driver. for (auto& ref : mExpiredOffers) { auto& offer = mOffers[ref.index]; + O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(ref.index*8)); if (offer.sharedMemory < 0) { - LOGP(LOGLEVEL, "Offer {} does not have any more memory. Marking it as invalid.", ref.index); + O2_SIGNPOST_END(quota, oid, "handleExpired", "Offer %d does not have any more memory. Marking it as invalid.", ref.index); offer.valid = false; offer.score = OfferScore::Unneeded; continue; } // FIXME: offers should go through the driver client, not the monitoring // api. - LOGP(LOGLEVEL, "Offer {} expired. Giving back {}MB and {} cores", ref.index, offer.sharedMemory / 1000000, offer.cpu); + O2_SIGNPOST_END(quota, oid, "handleExpired", "Offer %d expired. Giving back %llu MB and %d cores", + ref.index, offer.sharedMemory / 1000000, offer.cpu); assert(offer.sharedMemory >= 0); mStats.totalExpiredBytes += offer.sharedMemory; mStats.totalExpiredOffers++; expirator(offer, mStats); - //driverClient.tell("expired shmem {}", offer.sharedMemory); - //driverClient.tell("expired cpu {}", offer.cpu); + // driverClient.tell("expired shmem {}", offer.sharedMemory); + // driverClient.tell("expired cpu {}", offer.cpu); offer.sharedMemory = -1; offer.valid = false; offer.score = OfferScore::Unneeded; From 233814cd56a197d850a6d51919d85632a193f141 Mon Sep 17 00:00:00 2001 From: Matteo Concas Date: Tue, 30 Sep 2025 16:56:24 +0200 Subject: [PATCH 29/99] GPU: Prevent CUDA 13 from breaking the GPUbenchmark (#14706) * GPU: Prevent CUDA 13 from breaking the GPUbenchmark Some features are moved out from the `cudaDeviceProp` struct starting from CUDA 13. * Please consider the following formatting changes (#14707) * Add cooperativeMultiDevice variable in Kernels.cu --------- Co-authored-by: ALICE Builder --- GPU/GPUbenchmark/cuda/Kernels.cu | 33 ++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/GPU/GPUbenchmark/cuda/Kernels.cu b/GPU/GPUbenchmark/cuda/Kernels.cu index c309e7b2dbc5d..16dc138ae466f 100644 --- a/GPU/GPUbenchmark/cuda/Kernels.cu +++ b/GPU/GPUbenchmark/cuda/Kernels.cu @@ -301,6 +301,21 @@ void printDeviceProp(int32_t deviceId) cudaDeviceProp props; GPUCHECK(cudaGetDeviceProperties(&props, deviceId)); + int32_t clockRateKHz = 0; + int32_t memoryClockRateKHz = 0; + int32_t computeMode = 0; + int32_t cooperativeMultiDevice = 0; + +#if (CUDART_VERSION >= 13000) + GPUCHECK(cudaDeviceGetAttribute(&clockRateKHz, cudaDevAttrClockRate, deviceId)); + GPUCHECK(cudaDeviceGetAttribute(&memoryClockRateKHz, cudaDevAttrMemoryClockRate, deviceId)); + GPUCHECK(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, deviceId)); +#else + clockRateKHz = props.clockRate; + memoryClockRateKHz = props.memoryClockRate; + computeMode = props.computeMode; + cooperativeMultiDevice = props.cooperativeMultiDeviceLaunch; +#endif std::cout << std::setw(w1) << "Name: " << props.name << std::endl; std::cout << std::setw(w1) << "pciBusID: " << props.pciBusID << std::endl; std::cout << std::setw(w1) << "pciDeviceID: " << props.pciDeviceID << std::endl; @@ -309,11 +324,16 @@ void printDeviceProp(int32_t deviceId) std::cout << std::setw(w1) << "maxThreadsPerMultiProcessor: " << props.maxThreadsPerMultiProcessor << std::endl; std::cout << std::setw(w1) << "isMultiGpuBoard: " << props.isMultiGpuBoard << std::endl; - std::cout << std::setw(w1) << "clockRate: " << (float)props.clockRate / 1000.0 << " Mhz" << std::endl; - std::cout << std::setw(w1) << "memoryClockRate: " << (float)props.memoryClockRate / 1000.0 << " Mhz" + + // Use the variables we populated above for the moved properties + std::cout << std::setw(w1) << "clockRate: " << (float)clockRateKHz / 1000.0 << " Mhz" << std::endl; + std::cout << std::setw(w1) << "memoryClockRate: " << (float)memoryClockRateKHz / 1000.0 << " Mhz" << std::endl; + std::cout << std::setw(w1) << "memoryBusWidth: " << props.memoryBusWidth << std::endl; - std::cout << std::setw(w1) << "clockInstructionRate: " << (float)props.clockRate / 1000.0 + + // clockInstructionRate is just another name for clockRate in this context + std::cout << std::setw(w1) << "clockInstructionRate: " << (float)clockRateKHz / 1000.0 << " Mhz" << std::endl; std::cout << std::setw(w1) << "totalGlobalMem: " << std::fixed << std::setprecision(2) << bytesToGB(props.totalGlobalMem) << " GB" << std::endl; @@ -332,7 +352,10 @@ void printDeviceProp(int32_t deviceId) std::cout << std::setw(w1) << "regsPerBlock: " << props.regsPerBlock << std::endl; std::cout << std::setw(w1) << "warpSize: " << props.warpSize << std::endl; std::cout << std::setw(w1) << "l2CacheSize: " << props.l2CacheSize << std::endl; - std::cout << std::setw(w1) << "computeMode: " << props.computeMode << std::endl; + + // Use the variable for computeMode + std::cout << std::setw(w1) << "computeMode: " << computeMode << std::endl; + std::cout << std::setw(w1) << "maxThreadsPerBlock: " << props.maxThreadsPerBlock << std::endl; std::cout << std::setw(w1) << "maxThreadsDim.x: " << props.maxThreadsDim[0] << std::endl; std::cout << std::setw(w1) << "maxThreadsDim.y: " << props.maxThreadsDim[1] << std::endl; @@ -343,8 +366,6 @@ void printDeviceProp(int32_t deviceId) std::cout << std::setw(w1) << "major: " << props.major << std::endl; std::cout << std::setw(w1) << "minor: " << props.minor << std::endl; std::cout << std::setw(w1) << "concurrentKernels: " << props.concurrentKernels << std::endl; - std::cout << std::setw(w1) << "cooperativeLaunch: " << props.cooperativeLaunch << std::endl; - std::cout << std::setw(w1) << "cooperativeMultiDeviceLaunch: " << props.cooperativeMultiDeviceLaunch << std::endl; #if defined(__HIPCC__) std::cout << std::setw(w1) << "arch.hasGlobalInt32Atomics: " << props.arch.hasGlobalInt32Atomics << std::endl; std::cout << std::setw(w1) << "arch.hasGlobalFloatAtomicExch: " << props.arch.hasGlobalFloatAtomicExch From 5032354d2492e04244dbeb68958448edb1526bb9 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 25 Sep 2025 10:04:47 +0200 Subject: [PATCH 30/99] Demote errors to warnings --- Detectors/TPC/workflow/src/IDCToVectorSpec.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx b/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx index 27dbcf5d85bbf..9d704d425f1da 100644 --- a/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx +++ b/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx @@ -405,7 +405,7 @@ class IDCToVectorDevice : public o2::framework::Task for (const auto& inf : infVec) { if (!inf.hasBothEPs()) { - LOGP(error, "IDC CRU {:3}: data missing at ({:8}, {:4}) for one or both end points {:02b} in TF {}", cru, inf.heartbeatOrbit, inf.heartbeatBC, inf.epSeen, tfCounter); + LOGP(warning, "IDC CRU {:3}: data missing at ({:8}, {:4}) for one or both end points {:02b} in TF {}", cru, inf.heartbeatOrbit, inf.heartbeatBC, inf.epSeen, tfCounter); hasErrors = true; } } @@ -422,7 +422,7 @@ class IDCToVectorDevice : public o2::framework::Task } if (!std::equal(infVecComp->begin(), infVecComp->end(), infVec.begin())) { - LOGP(error, "IDC CRU {:3}: mismatch in orbit numbers", cru); + LOGP(warning, "IDC CRU {:3}: mismatch in orbit numbers", cru); hasErrors = true; } } From 8efebc210d60ef8b03d6fd9959ff87d2536aa46a Mon Sep 17 00:00:00 2001 From: wiechula Date: Fri, 6 Jun 2025 11:58:16 +0200 Subject: [PATCH 31/99] Extend time gain calibration - allow for using TPC tracks from global track matches - use DCA from global track in case global track match is required - improve method for reading back a previous calibration --- Detectors/TPC/calibration/src/CalibdEdx.cxx | 21 ++- Detectors/TPC/workflow/CMakeLists.txt | 2 +- .../include/TPCWorkflow/MIPTrackFilterSpec.h | 4 +- .../TPC/workflow/src/MIPTrackFilterSpec.cxx | 137 +++++++++++++----- .../TPC/workflow/src/tpc-miptrack-filter.cxx | 17 ++- 5 files changed, 135 insertions(+), 46 deletions(-) diff --git a/Detectors/TPC/calibration/src/CalibdEdx.cxx b/Detectors/TPC/calibration/src/CalibdEdx.cxx index e1081335c04cb..4eb29c8833565 100644 --- a/Detectors/TPC/calibration/src/CalibdEdx.cxx +++ b/Detectors/TPC/calibration/src/CalibdEdx.cxx @@ -754,17 +754,28 @@ void CalibdEdx::dumpToFile(const char* outFile) CalibdEdx CalibdEdx::readFromFile(const char* inFile) { - TFile f(inFile, "READ"); - auto* obj = (CalibdEdx*)f.Get("calib"); + std::unique_ptr f(TFile::Open(inFile)); + if (!f || f->IsZombie()) { + LOGP(error, "Could not open file: {}", inFile); + CalibdEdx calTmp; + return calTmp; + } + + auto obj = f->Get("calib"); if (!obj) { + LOGP(error, "Could not read CalibdEdx object from file: {}", inFile); CalibdEdx calTmp; return calTmp; } + + THnF* hTmp = f->Get("histogram_data"); + CalibdEdx cal(*obj); - THnF* hTmp = (THnF*)f.Get("histogram_data"); + delete obj; + if (!hTmp) { - CalibdEdx calTmp; - return calTmp; + LOGP(warning, "Could not read histogram from file: {}. Returning empty histogram", inFile); + return cal; } cal.setFromRootHist(hTmp); return cal; diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index 48ebb54ac4070..6930f332bfbf1 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -200,7 +200,7 @@ o2_add_executable(idc-test-ft o2_add_executable(miptrack-filter COMPONENT_NAME tpc SOURCES src/tpc-miptrack-filter.cxx - PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow O2::GlobalTrackingWorkflow) o2_add_executable(track-and-cluster-filter COMPONENT_NAME tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h index 05024baad37b3..45406e6c01bbd 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/MIPTrackFilterSpec.h @@ -17,6 +17,8 @@ #define O2_TPC_MIPTRACKFILTERSPEC_H_ #include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +using GID = o2::dataformats::GlobalTrackID; using namespace o2::framework; @@ -24,7 +26,7 @@ namespace o2::tpc { /// create a processor spec -o2::framework::DataProcessorSpec getMIPTrackFilterSpec(); +o2::framework::DataProcessorSpec getMIPTrackFilterSpec(GID::mask_t srcTracks = GID::getSourcesMask("TPC")); } // namespace o2::tpc diff --git a/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx b/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx index 33b9039298264..eff1a694a4727 100644 --- a/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx +++ b/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx @@ -16,7 +16,6 @@ #include "TPCWorkflow/MIPTrackFilterSpec.h" #include -#include #include #include #include @@ -24,7 +23,7 @@ // o2 includes #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/TrackCuts.h" -#include "DetectorsCalibration/Utils.h" +#include "Framework/CCDBParamSpec.h" #include "Framework/Logger.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/Task.h" @@ -33,8 +32,14 @@ #include "Framework/ConfigParamRegistry.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "Headers/DataHeader.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" using namespace o2::framework; +using DataRequest = o2::globaltracking::DataRequest; +using GID = o2::dataformats::GlobalTrackID; namespace o2::tpc { @@ -42,7 +47,8 @@ namespace o2::tpc class MIPTrackFilterDevice : public Task { public: - MIPTrackFilterDevice(std::shared_ptr gr) : mGRPGeomRequest(gr) {} + MIPTrackFilterDevice(std::shared_ptr gr, std::shared_ptr dr, GID::mask_t trackSourcesMask) + : mGRPGeomRequest(gr), mDataRequest(dr), mTrackSourcesMask(trackSourcesMask) {} void init(framework::InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -53,16 +59,20 @@ class MIPTrackFilterDevice : public Task void sendOutput(DataAllocator& output); std::shared_ptr mGRPGeomRequest; - TrackCuts mCuts{}; ///< Tracks cuts object - std::vector mMIPTracks; ///< Filtered MIP tracks - unsigned int mProcessEveryNthTF{1}; ///< process every Nth TF only - int mMaxTracksPerTF{-1}; ///< max number of MIP tracks processed per TF - uint32_t mTFCounter{0}; ///< counter to keep track of the TFs - int mProcessNFirstTFs{0}; ///< number of first TFs which are not sampled - float mDCACut{-1}; ///< DCA cut - bool mSendDummy{false}; ///< send empty data in case TF is skipped - - bool acceptDCA(const TrackTPC& track); + std::shared_ptr mDataRequest; + GID::mask_t mTrackSourcesMask; + TrackCuts mCuts{}; ///< Tracks cuts object + std::vector mMIPTracks; ///< Filtered MIP tracks + o2::dataformats::MeanVertexObject mVtx; ///< Mean vertex object + unsigned int mProcessEveryNthTF{1}; ///< process every Nth TF only + int mMaxTracksPerTF{-1}; ///< max number of MIP tracks processed per TF + uint32_t mTFCounter{0}; ///< counter to keep track of the TFs + int mProcessNFirstTFs{0}; ///< number of first TFs which are not sampled + float mDCACut{-1}; ///< DCA cut + float mDCAZCut{-1}; ///< DCA z cut + bool mSendDummy{false}; ///< send empty data in case TF is skipped + + bool acceptDCA(o2::track::TrackPar propTrack, o2::math_utils::Point3D refPoint, bool useDCAz = false); }; void MIPTrackFilterDevice::init(framework::InitContext& ic) @@ -100,6 +110,7 @@ void MIPTrackFilterDevice::init(framework::InitContext& ic) mCuts.setCutLooper(cutLoopers); mDCACut = ic.options().get("dca-cut"); + mDCAZCut = ic.options().get("dca-z-cut"); o2::base::GRPGeomHelper::instance().setRequest(mGRPGeomRequest); } @@ -107,6 +118,8 @@ void MIPTrackFilterDevice::init(framework::InitContext& ic) void MIPTrackFilterDevice::run(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + pc.inputs().get("meanvtx"); + const auto currentTF = processing_helpers::getCurrentTF(pc); if ((mTFCounter++ % mProcessEveryNthTF) && (currentTF >= mProcessNFirstTFs)) { LOGP(info, "Skipping TF {}", currentTF); @@ -117,19 +130,60 @@ void MIPTrackFilterDevice::run(ProcessingContext& pc) return; } - const auto tracks = pc.inputs().get>("tracks"); - const auto nTracks = tracks.size(); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest); + const auto tracksTPC = recoData.getTPCTracks(); + const auto nTracks = tracksTPC.size(); + + // indices to good tracks + std::vector indices; + indices.reserve(nTracks); + + const auto useGlobalTracks = mTrackSourcesMask[GID::ITSTPC]; + o2::math_utils::Point3D vertex = mVtx.getXYZ(); + + if (useGlobalTracks) { + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + std::vector selSrc{GID::ITSTPC, GID::ITSTPCTRD, GID::ITSTPCTRDTOF}; // for Instance + // LOGP(info, "Number of vertex tracks: {}", vtxRefs.size()); + const auto nv = (vtxRefs.size() > 0) ? vtxRefs.size() - 1 : 0; // note: the last entry groups the tracks which were not related to any vertex, to skip them, use vtxRefs.size()-1 + + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + // LOGP(info, "Processing vertex {} with {} tracks", iv, vtref.getEntries()); + vertex = recoData.getPrimaryVertex(iv).getXYZ(); + // LOGP(info, "Vertex position: x={} y={} z={}", vertex.x(), vertex.y(), vertex.z()); + + for (auto src : selSrc) { + int idMin = vtxRefs[iv].getFirstEntryOfSource(src), idMax = idMin + vtxRefs[iv].getEntriesOfSource(src); + // LOGP(info, "Source {}: idMin={} idMax={}", GID::getSourceName(src), idMin, idMax); + + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + const auto& track = recoData.getTrackParam(vid); // this is a COPY of the track param which we will modify during DCA calculation + auto gidTPC = recoData.getTPCContributorGID(vid); + if (gidTPC.isSourceSet()) { + const auto idxTPC = gidTPC.getIndex(); + if (mCuts.goodTrack(tracksTPC[idxTPC]) && acceptDCA(tracksTPC[idxTPC], vertex, true)) { + indices.emplace_back(idxTPC); + } + } + } + } + } - if ((mMaxTracksPerTF != -1) && (nTracks > mMaxTracksPerTF)) { - // indices to good tracks - std::vector indices; - indices.reserve(nTracks); + } else { for (size_t i = 0; i < nTracks; ++i) { - if (mCuts.goodTrack(tracks[i]) && acceptDCA(tracks[i])) { + if (mCuts.goodTrack(tracksTPC[i]) && acceptDCA(tracksTPC[i], vertex)) { indices.emplace_back(i); } } + } + + size_t nTracksSel = indices.size(); + if ((mMaxTracksPerTF != -1) && (nTracksSel > mMaxTracksPerTF)) { // in case no good tracks have been found if (indices.empty()) { mMIPTracks.clear(); @@ -144,15 +198,14 @@ void MIPTrackFilterDevice::run(ProcessingContext& pc) std::shuffle(indices.begin(), indices.end(), rng); // copy good tracks - const int loopEnd = (mMaxTracksPerTF > indices.size()) ? indices.size() : mMaxTracksPerTF; - for (int i = 0; i < loopEnd; ++i) { - mMIPTracks.emplace_back(tracks[indices[i]]); - } - } else { - std::copy_if(tracks.begin(), tracks.end(), std::back_inserter(mMIPTracks), [this](const auto& track) { return mCuts.goodTrack(track) && acceptDCA(track); }); + nTracksSel = (mMaxTracksPerTF > indices.size()) ? indices.size() : mMaxTracksPerTF; + } + + for (int i = 0; i < nTracksSel; ++i) { + mMIPTracks.emplace_back(tracksTPC[indices[i]]); } - LOGP(info, "Filtered {} MIP tracks out of {} total tpc tracks", mMIPTracks.size(), tracks.size()); + LOGP(info, "Filtered {} / {} MIP tracks out of {} total tpc tracks, using {}", mMIPTracks.size(), indices.size(), tracksTPC.size(), useGlobalTracks ? "global tracks" : "TPC only tracks"); sendOutput(pc.outputs()); mMIPTracks.clear(); } @@ -162,6 +215,11 @@ void MIPTrackFilterDevice::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { return; } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Setting new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mVtx = *(const o2::dataformats::MeanVertexObject*)obj; + return; + } } void MIPTrackFilterDevice::sendOutput(DataAllocator& output) { output.snapshot(Output{header::gDataOriginTPC, "MIPS", 0}, mMIPTracks); } @@ -171,7 +229,7 @@ void MIPTrackFilterDevice::endOfStream(EndOfStreamContext& eos) LOG(info) << "Finalizig MIP Tracks filter"; } -bool MIPTrackFilterDevice::acceptDCA(const TrackTPC& track) +bool MIPTrackFilterDevice::acceptDCA(o2::track::TrackPar propTrack, o2::math_utils::Point3D refPoint, bool useDCAz) { if (mDCACut < 0) { return true; @@ -179,21 +237,21 @@ bool MIPTrackFilterDevice::acceptDCA(const TrackTPC& track) auto propagator = o2::base::Propagator::Instance(); std::array dca; - const o2::math_utils::Point3D refPoint{0, 0, 0}; - o2::track::TrackPar propTrack(track); const auto ok = propagator->propagateToDCABxByBz(refPoint, propTrack, 2., o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dca); const auto dcar = std::abs(dca[0]); - return ok && (dcar < mDCACut); + return ok && (dcar < mDCACut) && (!useDCAz || (std::abs(dca[1]) < mDCAZCut)); } -DataProcessorSpec getMIPTrackFilterSpec() +DataProcessorSpec getMIPTrackFilterSpec(GID::mask_t srcTracks) { std::vector outputs; outputs.emplace_back(header::gDataOriginTPC, "MIPS", 0, Lifetime::Sporadic); - std::vector inputs; - inputs.emplace_back("tracks", "TPC", "TRACKS"); + const auto useMC = false; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestPrimaryVertices(useMC); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true @@ -201,14 +259,16 @@ DataProcessorSpec getMIPTrackFilterSpec() true, // GRPMagField true, // askMatLUT o2::base::GRPGeomRequest::Aligned, // geometry - inputs, + dataRequest->inputs, true); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, o2::framework::ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + return DataProcessorSpec{ "tpc-miptrack-filter", - inputs, + dataRequest->inputs, outputs, - adaptFromTask(ggRequest), + adaptFromTask(ggRequest, dataRequest, srcTracks), Options{ {"min-momentum", VariantType::Double, 0.35, {"minimum momentum cut"}}, {"max-momentum", VariantType::Double, 0.55, {"maximum momentum cut"}}, @@ -220,7 +280,8 @@ DataProcessorSpec getMIPTrackFilterSpec() {"process-first-n-TFs", VariantType::Int, 1, {"Number of first TFs which are not sampled"}}, {"send-dummy-data", VariantType::Bool, false, {"Send empty data in case TF is skipped"}}, {"dont-cut-loopers", VariantType::Bool, false, {"Do not cut loopers by comparing zout-zin"}}, - {"dca-cut", VariantType::Float, 3.f, {"DCA cut in cm, < 0 to disable"}}, + {"dca-cut", VariantType::Float, 3.f, {"DCA cut in xy (cm), < 0 to disable cut in xy and z"}}, + {"dca-z-cut", VariantType::Float, 5.f, {"DCA cut in z (cm)"}}, }}; } diff --git a/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx b/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx index 112e8ff2cd3a4..ae05b0c431626 100644 --- a/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx +++ b/Detectors/TPC/workflow/src/tpc-miptrack-filter.cxx @@ -13,6 +13,10 @@ #include "TPCWorkflow/MIPTrackFilterSpec.h" #include "Framework/ConfigParamSpec.h" #include "DataFormatsTPC/TrackTPC.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +using GID = o2::dataformats::GlobalTrackID; template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; @@ -21,6 +25,8 @@ void customize(std::vector& workflowOptions) { std::vector options{ {"enable-writer", VariantType::Bool, false, {"selection string input specs"}}, + {"use-global-tracks", VariantType::Bool, false, {"use global matched tracks instead of TPC only"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, }; std::swap(workflowOptions, options); @@ -33,8 +39,17 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) { using namespace o2::tpc; + const auto useGlobal = config.options().get("use-global-tracks"); WorkflowSpec workflow; - workflow.emplace_back(getMIPTrackFilterSpec()); + + const auto useMC = false; + auto srcTracks = GID::getSourcesMask("TPC"); + const auto srcCls = GID::getSourcesMask(""); + if (useGlobal) { + srcTracks = GID::getSourcesMask("ITS,TPC,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + } + + workflow.emplace_back(getMIPTrackFilterSpec(srcTracks)); if (config.options().get("enable-writer")) { const char* processName = "tpc-mips-writer"; From 9c3daa9cb2df5823d8cb57fcb58f5e44c09a9d12 Mon Sep 17 00:00:00 2001 From: wiechula Date: Tue, 16 Sep 2025 13:28:32 +0200 Subject: [PATCH 32/99] Negative binning omits drawing the 1D distributions --- Detectors/TPC/base/src/Painter.cxx | 36 +++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/Detectors/TPC/base/src/Painter.cxx b/Detectors/TPC/base/src/Painter.cxx index 9f143d3fa45ce..fffe195f8bfb6 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/base/src/Painter.cxx @@ -291,6 +291,8 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float const Mapper& mapper = Mapper::instance(); + const bool draw1D = nbins1D > 0; + // ===| name and title |====================================================== std::string title = calDet.getName(); std::string name = calDet.getName(); @@ -305,11 +307,13 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float const int bufferSize = TH1::GetDefaultBufferSize(); TH1::SetDefaultBufferSize(Sector::MAXSECTOR * mapper.getPadsInSector()); - auto hAside1D = new TH1F(fmt::format("h_Aside_1D_{}", name).data(), fmt::format("{0} (A-Side);{0}", title).data(), - nbins1D, xMin1D, xMax1D); // TODO: modify ranges + auto hAside1D = draw1D ? new TH1F(fmt::format("h_Aside_1D_{}", name).data(), fmt::format("{0} (A-Side);{0}", title).data(), + nbins1D, xMin1D, xMax1D) + : nullptr; // TODO: modify ranges - auto hCside1D = new TH1F(fmt::format("h_Cside_1D_{}", name).data(), fmt::format("{0} (C-Side);{0}", title).data(), - nbins1D, xMin1D, xMax1D); // TODO: modify ranges + auto hCside1D = draw1D ? new TH1F(fmt::format("h_Cside_1D_{}", name).data(), fmt::format("{0} (C-Side);{0}", title).data(), + nbins1D, xMin1D, xMax1D) + : nullptr; // TODO: modify ranges auto hAside2D = new TH2F(fmt::format("h_Aside_2D_{}", name).data(), fmt::format("{0} (A-Side);#it{{x}} (cm);#it{{y}} (cm);{0}", title).data(), 330, -270, 270, 330, -270, 270); @@ -336,7 +340,9 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float if (!hist2D->GetBinContent(bin)) { hist2D->SetBinContent(bin, double(val)); } - hist1D->Fill(double(val)); + if (draw1D) { + hist1D->Fill(double(val)); + } } } } @@ -352,13 +358,13 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float gStyle->SetOptStat("mr"); auto c = outputCanvas; if (!c) { - c = new TCanvas(fmt::format("c_{}", name).data(), title.data(), 1000, 1000); + c = new TCanvas(fmt::format("c_{}", name).data(), title.data(), 1000, draw1D ? 1000 : 500); } gStyle->SetStatX(1. - gPad->GetRightMargin()); gStyle->SetStatY(1. - gPad->GetTopMargin()); c->Clear(); - c->Divide(2, 2); + c->Divide(2, draw1D ? 2 : 1); c->cd(1); hAside2D->Draw("colz"); @@ -376,18 +382,22 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float adjustPalette(hCside2D, 0.92); drawSectorsXY(Side::C); - c->cd(3); - hAside1D->Draw(); + if (draw1D) { + c->cd(3); + hAside1D->Draw(); - c->cd(4); - hCside1D->Draw(); + c->cd(4); + hCside1D->Draw(); + + // associate histograms to canvas + hAside1D->SetBit(TObject::kCanDelete); + hCside1D->SetBit(TObject::kCanDelete); + } // reset the buffer size TH1::SetDefaultBufferSize(bufferSize); // associate histograms to canvas - hAside1D->SetBit(TObject::kCanDelete); - hCside1D->SetBit(TObject::kCanDelete); hAside2D->SetBit(TObject::kCanDelete); hCside2D->SetBit(TObject::kCanDelete); From b7838ed00b13060df4918f30601dd096fa0b85ae Mon Sep 17 00:00:00 2001 From: wiechula Date: Tue, 16 Sep 2025 13:29:34 +0200 Subject: [PATCH 33/99] Add possibility to add a comment for the CCDB upload --- .../TPC/dcs/macro/makeTPCCCDBEntryForDCS.C | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C b/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C index edcb69907b3e5..d488aba14e264 100644 --- a/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C +++ b/Detectors/TPC/dcs/macro/makeTPCCCDBEntryForDCS.C @@ -14,6 +14,7 @@ #include #include "TFile.h" #include "CCDB/CcdbApi.h" +#include "CommonUtils/StringUtils.h" #include "DetectorsDCS/AliasExpander.h" #include "DetectorsDCS/DeliveryType.h" #include "DetectorsDCS/DataPointIdentifier.h" @@ -24,9 +25,10 @@ #include using DPID = o2::dcs::DataPointIdentifier; +using namespace o2::utils; /// macro to populate CCDB for TPC with the configuration for DCS -int makeTPCCCDBEntryForDCS(const std::string url = "http://localhost:8080") +int makeTPCCCDBEntryForDCS(const std::string url = "http://localhost:8080", std::string comment = "") { std::unordered_map dpid2DataDesc; @@ -64,9 +66,23 @@ int makeTPCCCDBEntryForDCS(const std::string url = "http://localhost:8080") o2::ccdb::CcdbApi api; api.init(url); // or http://localhost:8080 for a local installation - std::map md; + std::map meta; + + auto toKeyValPairs = [&meta](std::vector const& tokens) { + for (auto& token : tokens) { + auto keyval = Str::tokenize(token, '=', false); + if (keyval.size() != 2) { + LOG(error) << "Illegal command-line key/value string: " << token; + continue; + } + Str::trim(keyval[1]); + meta[keyval[0]] = keyval[1]; + } + }; + toKeyValPairs(Str::tokenize(comment, ';', true)); + long ts = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - api.storeAsTFileAny(&dpid2DataDesc, "TPC/Config/DCSDPconfig", md, ts, 99999999999999); + api.storeAsTFileAny(&dpid2DataDesc, "TPC/Config/DCSDPconfig", meta, ts, 99999999999999); return 0; } From 591b53a31437f004f3551d6b60bb4152810d7910 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 25 Sep 2025 10:05:53 +0200 Subject: [PATCH 34/99] Add voxel map binning --- Detectors/TPC/base/include/TPCBase/Painter.h | 11 ++- Detectors/TPC/base/src/Painter.cxx | 84 ++++++++++++++++++-- 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/Detectors/TPC/base/include/TPCBase/Painter.h b/Detectors/TPC/base/include/TPCBase/Painter.h index 976fe2846ce0c..5cf8691635b1f 100644 --- a/Detectors/TPC/base/include/TPCBase/Painter.h +++ b/Detectors/TPC/base/include/TPCBase/Painter.h @@ -53,7 +53,8 @@ struct painter { enum class Type : int { Pad, ///< drawing pads Stack, ///< drawing stacks - FEC ///< drawing of FECs + FEC, ///< drawing of FECs + SCD, ///< drawing of FECs }; static std::array colors; @@ -87,8 +88,10 @@ struct painter { /// create a vector of FEC corner coordinates for one full sector static std::vector getFECCoordinatesSector(); + static std::vector getSCDY2XCoordinatesSector(std::string binningStr); + /// \return returns coordinates for given type - static std::vector getCoordinates(const Type type); + static std::vector getCoordinates(const Type type, std::string binningStr = ""); /// binning vector with radial pad-row positions (in cm) /// \param roc roc number (0-35 IROC, 36-71 OROC, >=72 full sector) @@ -143,11 +146,11 @@ struct painter { /// \param yMin minimum y coordinate of the histogram /// \param yMax maximum y coordinate of the histogram /// \param type granularity of the histogram (per pad or per stack) - static TH2Poly* makeSectorHist(const std::string_view name = "hSector", const std::string_view title = "Sector;local #it{x} (cm);local #it{y} (cm)", const float xMin = 83.65f, const float xMax = 247.7f, const float yMin = -43.7f, const float yMax = 43.7f, const Type type = Type::Pad); + static TH2Poly* makeSectorHist(const std::string_view name = "hSector", const std::string_view title = "Sector;local #it{x} (cm);local #it{y} (cm)", const float xMin = 83.65f, const float xMax = 247.7f, const float yMin = -43.7f, const float yMax = 43.7f, const Type type = Type::Pad, std::string binningStr = ""); /// make a side-wise histogram with correct pad corners /// \param type granularity of the histogram (per pad or per stack) - static TH2Poly* makeSideHist(Side side, const Type type = Type::Pad); + static TH2Poly* makeSideHist(Side side, const Type type = Type::Pad, std::string binningStr = ""); /// fill existing TH2Poly histogram for CalDet object /// \param h2D histogram to fill diff --git a/Detectors/TPC/base/src/Painter.cxx b/Detectors/TPC/base/src/Painter.cxx index fffe195f8bfb6..ffbc149225212 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/base/src/Painter.cxx @@ -31,7 +31,9 @@ #include "TPaveText.h" #include "TPaletteAxis.h" #include "TObjArray.h" +#include "TMath.h" +#include "Algorithm/RangeTokenizer.h" #include "CommonUtils/StringUtils.h" #include "DataFormatsTPC/Defs.h" #include "TPCBase/ROC.h" @@ -223,7 +225,77 @@ std::vector painter::getFECCoordinatesSector() return padCoords; } -std::vector painter::getCoordinates(const Type type) +std::vector painter::getSCDY2XCoordinatesSector(std::string binningStr) +{ + const float deadZone = 1.5; + const float secPhi = 20.0 * TMath::DegToRad(); + std::vector padCoords; + const Mapper& mapper = Mapper::instance(); + const auto nPadRows = Mapper::PADROWS; + std::vector maxY2X(nPadRows); + auto binCenters = o2::RangeTokenizer::tokenize(binningStr); + size_t nY2XBins = 20; + std::vector halfBinWidth; + + auto setUniformBinning = [&binCenters, &halfBinWidth](int nY2XBins) { + binCenters.resize(nY2XBins); + halfBinWidth.resize(nY2XBins); + for (int i = 0; i < nY2XBins; ++i) { + const auto binWidth = 2.f / nY2XBins; + halfBinWidth[i] = binWidth / 2.f; + binCenters[i] = -1.f + (i + 0.5f) * binWidth; + } + }; + + if (binCenters.size() == 0) { + LOGP(info, "Empty binning provided, will use default uniform y/x binning with {} bins", nY2XBins); + setUniformBinning(nY2XBins); + } else if (binCenters.size() == 1) { + nY2XBins = static_cast(binCenters.at(0)); + LOGP(info, "Setting uniform binning for y/x with {} bins", nY2XBins); + setUniformBinning(nY2XBins); + } else { + nY2XBins = binCenters.size() - 1; + if (std::abs(binCenters[0] + 1.f) > 1e-6 || std::abs(binCenters[nY2XBins] - 1.f) > 1e-6) { + LOG(error) << "Provided binning for y/x not in range -1 to 1: " << binCenters[0] << " - " << binCenters[nY2XBins] << ". Using default uniform binning with " << nY2XBins << " bins"; + setUniformBinning(nY2XBins); + } else { + LOGP(info, "Setting custom binning for y/x with {} bins", nY2XBins); + halfBinWidth.reserve(nY2XBins); + halfBinWidth.clear(); + for (int i = 0; i < nY2XBins; ++i) { + halfBinWidth.push_back(.5f * (binCenters[i + 1] - binCenters[i])); + binCenters[i] = .5f * (binCenters[i] + binCenters[i + 1]); + } + binCenters.resize(nY2XBins); + } + } + + for (int irow = 0; irow < nPadRows; ++irow) { + const auto x = mapper.getPadCentre(PadPos(irow, 0)).X(); + maxY2X[irow] = std::tan(.5f * secPhi) - deadZone / x; + const auto region = Mapper::REGION[irow]; + const auto ph = mapper.getPadRegionInfo(region).getPadHeight(); + const auto xPadBottom = x - ph / 2; + const auto xPadTop = x + ph / 2; + for (int iy2x = 0; iy2x < nY2XBins; ++iy2x) { + auto& padCoord = padCoords.emplace_back(); + float yPadRight = 0; + if (iy2x == 0) { + yPadRight = maxY2X[irow] * (binCenters[iy2x] - halfBinWidth[iy2x]); + } else { + yPadRight = maxY2X[irow] * (binCenters[iy2x - 1] + halfBinWidth[iy2x - 1]); + } + const auto yPadLeft = maxY2X[irow] * (binCenters[iy2x] + halfBinWidth[iy2x]); + padCoord.xVals = {xPadBottom, xPadTop, xPadTop, xPadBottom}; + padCoord.yVals = {yPadRight * xPadBottom, yPadRight * xPadTop, yPadLeft * xPadTop, yPadLeft * xPadBottom}; + } + } + + return padCoords; +} + +std::vector painter::getCoordinates(const Type type, std::string binningStr) { if (type == Type::Pad) { return painter::getPadCoordinatesSector(); @@ -231,6 +303,8 @@ std::vector painter::getCoordinates(const Type return painter::getStackCoordinatesSector(); } else if (type == Type::FEC) { return painter::getFECCoordinatesSector(); + } else if (type == Type::SCD) { + return painter::getSCDY2XCoordinatesSector(binningStr); } else { LOGP(warning, "Wrong Type provided!"); return std::vector(); @@ -805,11 +879,11 @@ std::vector painter::makeSummaryCanvases(const std::string_view fileNa } //______________________________________________________________________________ -TH2Poly* painter::makeSectorHist(const std::string_view name, const std::string_view title, const float xMin, const float xMax, const float yMin, const float yMax, const Type type) +TH2Poly* painter::makeSectorHist(const std::string_view name, const std::string_view title, const float xMin, const float xMax, const float yMin, const float yMax, const Type type, std::string binningStr) { auto poly = new TH2Poly(name.data(), title.data(), xMin, xMax, yMin, yMax); - auto coords = painter::getCoordinates(type); + auto coords = painter::getCoordinates(type, binningStr); for (const auto& coord : coords) { poly->AddBin(coord.xVals.size(), coord.xVals.data(), coord.yVals.data()); } @@ -818,12 +892,12 @@ TH2Poly* painter::makeSectorHist(const std::string_view name, const std::string_ } //______________________________________________________________________________ -TH2Poly* painter::makeSideHist(Side side, const Type type) +TH2Poly* painter::makeSideHist(Side side, const Type type, std::string binningStr) { const auto s = (side == Side::A) ? "A" : "C"; auto poly = new TH2Poly(fmt::format("hSide_{}", s).data(), fmt::format("{}-Side;#it{{x}} (cm);#it{{y}} (cm)", s).data(), -270., 270., -270., 270.); - auto coords = painter::getCoordinates(type); + auto coords = painter::getCoordinates(type, binningStr); for (int isec = 0; isec < 18; ++isec) { const float angDeg = 10.f + isec * 20; for (auto coord : coords) { From e700e7333a49b6b61698d1bc3f34ec4b8dbef377 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 2 Oct 2025 12:07:45 +0200 Subject: [PATCH 35/99] Move FragmentToBatch to separate class --- .../src/StandaloneAODProducerSpec.cxx | 3 +- Detectors/Filtering/src/FilteringSpec.cxx | 1 - .../src/StandaloneAODProducerSpec.cxx | 3 +- .../AnalysisSupport/src/DataInputDirector.cxx | 2 +- Framework/AnalysisSupport/src/Plugin.cxx | 6 ++ Framework/Core/CMakeLists.txt | 1 + .../Core/include/Framework/FragmentToBatch.h | 51 +++++++++++++++++ .../Core/include/Framework/TableTreeHelpers.h | 24 -------- Framework/Core/src/AnalysisSupportHelpers.cxx | 1 - Framework/Core/src/DataAllocator.cxx | 2 +- Framework/Core/src/FragmentToBatch.cxx | 57 +++++++++++++++++++ Framework/Core/src/TableTreeHelpers.cxx | 27 --------- 12 files changed, 119 insertions(+), 59 deletions(-) create mode 100644 Framework/Core/include/Framework/FragmentToBatch.h create mode 100644 Framework/Core/src/FragmentToBatch.cxx diff --git a/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx b/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx index 73987ce6d1c1b..227fc373bf20c 100644 --- a/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx +++ b/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx @@ -17,7 +17,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "MathUtils/Utils.h" using namespace o2::framework; @@ -105,7 +104,7 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) o2::math_utils::detail::truncateFloatFraction(cell.getTimeStamp(), mCaloTime), cell.getType(), 1); // hard coded for emcal (-1 would be undefined, 0 phos) - } // end of cell loop + } // end of cell loop // filled only with BCID, rest dummy for no2 caloCellsTRGTableCursor(0, diff --git a/Detectors/Filtering/src/FilteringSpec.cxx b/Detectors/Filtering/src/FilteringSpec.cxx index 847fa2cf7e1e5..bcf3c6c3539d4 100644 --- a/Detectors/Filtering/src/FilteringSpec.cxx +++ b/Detectors/Filtering/src/FilteringSpec.cxx @@ -38,7 +38,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "Framework/CCDBParamSpec.h" #include "FDDBase/Constants.h" #include "FT0Base/Geometry.h" diff --git a/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx b/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx index 454be7a5fcb83..06baf889b662f 100644 --- a/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx +++ b/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx @@ -19,7 +19,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "MathUtils/Utils.h" using namespace o2::framework; @@ -106,7 +105,7 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) o2::math_utils::detail::truncateFloatFraction(c.getTime(), mCaloTime), c.getType(), // HG/LG 0); // hard coded for phos (-1 would be undefined, 0 phos) - } // end of cell loop + } // end of cell loop auto bcID = tr.getBCData().toLong(); bcCursor(0, diff --git a/Framework/AnalysisSupport/src/DataInputDirector.cxx b/Framework/AnalysisSupport/src/DataInputDirector.cxx index 7cc0134a27968..2bc6c5613f065 100644 --- a/Framework/AnalysisSupport/src/DataInputDirector.cxx +++ b/Framework/AnalysisSupport/src/DataInputDirector.cxx @@ -16,8 +16,8 @@ #include "Framework/AnalysisDataModelHelpers.h" #include "Framework/Output.h" #include "Framework/Signpost.h" +#include "Framework/FragmentToBatch.h" #include "Headers/DataHeader.h" -#include "Framework/TableTreeHelpers.h" #include "Monitoring/Tags.h" #include "Monitoring/Metric.h" #include "Monitoring/Monitoring.h" diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index e39e76f01dbdd..5f61a236cbd58 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -27,6 +27,12 @@ O2_DECLARE_DYNAMIC_LOG(analysis_support); +struct ROOTTypeInfo { + EDataType type; + char suffix[3]; + int size; +}; + struct ROOTFileReader : o2::framework::AlgorithmPlugin { o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& config) override { diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 936d8874179a5..43571526855cc 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -87,6 +87,7 @@ o2_add_library(Framework src/FairMQDeviceProxy.cxx src/FairMQResizableBuffer.cxx src/FairOptionsRetriever.cxx + src/FragmentToBatch.cxx src/ConfigurationOptionsRetriever.cxx src/FreePortFinder.cxx src/GraphvizHelpers.cxx diff --git a/Framework/Core/include/Framework/FragmentToBatch.h b/Framework/Core/include/Framework/FragmentToBatch.h new file mode 100644 index 0000000000000..3a600d71452b9 --- /dev/null +++ b/Framework/Core/include/Framework/FragmentToBatch.h @@ -0,0 +1,51 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_FRAGMENT_TO_BATCH_H_ +#define O2_FRAMEWORK_FRAGMENT_TO_BATCH_H_ + +#include +#include +#include +#include +#include + +// ============================================================================= +namespace o2::framework +{ +class FragmentToBatch +{ + public: + // The function to be used to create the required stream. + using StreamerCreator = std::function(std::shared_ptr, const std::shared_ptr& buffer)>; + + FragmentToBatch(StreamerCreator, std::shared_ptr, arrow::MemoryPool* pool = arrow::default_memory_pool()); + void setLabel(const char* label); + void fill(std::shared_ptr dataSetSchema, std::shared_ptr); + std::shared_ptr finalize(); + + std::shared_ptr streamer(std::shared_ptr buffer) + { + return mCreator(mFragment, buffer); + } + + private: + std::shared_ptr mFragment; + arrow::MemoryPool* mArrowMemoryPool = nullptr; + std::string mTableLabel; + std::shared_ptr mRecordBatch; + StreamerCreator mCreator; +}; + +// ----------------------------------------------------------------------------- +} // namespace o2::framework + +// ============================================================================= +#endif // O2_FRAMEWORK_FRAGMENT_TO_BATCH_H_ diff --git a/Framework/Core/include/Framework/TableTreeHelpers.h b/Framework/Core/include/Framework/TableTreeHelpers.h index 3f76298a5bbd4..0a163d59aecb0 100644 --- a/Framework/Core/include/Framework/TableTreeHelpers.h +++ b/Framework/Core/include/Framework/TableTreeHelpers.h @@ -91,30 +91,6 @@ class TableToTree std::vector> mColumnReaders; }; -class FragmentToBatch -{ - public: - // The function to be used to create the required stream. - using StreamerCreator = std::function(std::shared_ptr, const std::shared_ptr& buffer)>; - - FragmentToBatch(StreamerCreator, std::shared_ptr, arrow::MemoryPool* pool = arrow::default_memory_pool()); - void setLabel(const char* label); - void fill(std::shared_ptr dataSetSchema, std::shared_ptr); - std::shared_ptr finalize(); - - std::shared_ptr streamer(std::shared_ptr buffer) - { - return mCreator(mFragment, buffer); - } - - private: - std::shared_ptr mFragment; - arrow::MemoryPool* mArrowMemoryPool = nullptr; - std::string mTableLabel; - std::shared_ptr mRecordBatch; - StreamerCreator mCreator; -}; - // ----------------------------------------------------------------------------- } // namespace o2::framework diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index e8c2d7acab5d2..7cfab22885671 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -15,7 +15,6 @@ #include "Framework/ControlService.h" #include "Framework/EndOfStreamContext.h" #include "Framework/DeviceSpec.h" -#include "Framework/TableTreeHelpers.h" #include "Framework/PluginManager.h" #include "Framework/ConfigContext.h" #include "WorkflowHelpers.h" diff --git a/Framework/Core/src/DataAllocator.cxx b/Framework/Core/src/DataAllocator.cxx index 4b559ef26191e..f0de6a40935b7 100644 --- a/Framework/Core/src/DataAllocator.cxx +++ b/Framework/Core/src/DataAllocator.cxx @@ -11,7 +11,6 @@ #include "Framework/CompilerBuiltins.h" #include "Framework/Lifetime.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "Framework/DataAllocator.h" #include "Framework/MessageContext.h" #include "Framework/ArrowContext.h" @@ -19,6 +18,7 @@ #include "Framework/DataProcessingHeader.h" #include "Framework/FairMQResizableBuffer.h" #include "Framework/DataProcessingContext.h" +#include "Framework/FragmentToBatch.h" #include "Framework/DeviceSpec.h" #include "Framework/StreamContext.h" #include "Framework/Signpost.h" diff --git a/Framework/Core/src/FragmentToBatch.cxx b/Framework/Core/src/FragmentToBatch.cxx new file mode 100644 index 0000000000000..88b4f42a8f220 --- /dev/null +++ b/Framework/Core/src/FragmentToBatch.cxx @@ -0,0 +1,57 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include "Framework/FragmentToBatch.h" +#include "Framework/Logger.h" +#include "Framework/Endian.h" +#include "Framework/Signpost.h" + +#include +#include +#include +#include +#include + +#include +#include + +O2_DECLARE_DYNAMIC_LOG(tabletree_helpers); + +namespace o2::framework +{ + +FragmentToBatch::FragmentToBatch(StreamerCreator creator, std::shared_ptr fragment, arrow::MemoryPool* pool) + : mFragment{std::move(fragment)}, + mArrowMemoryPool{pool}, + mCreator{std::move(creator)} +{ +} + +void FragmentToBatch::setLabel(const char* label) +{ + mTableLabel = label; +} + +void FragmentToBatch::fill(std::shared_ptr schema, std::shared_ptr format) +{ + auto options = std::make_shared(); + options->dataset_schema = schema; + auto scanner = format->ScanBatchesAsync(options, mFragment); + auto batch = (*scanner)(); + mRecordBatch = *batch.result(); + // Notice that up to here the buffer was not yet filled. +} + +std::shared_ptr FragmentToBatch::finalize() +{ + return mRecordBatch; +} + +} // namespace o2::framework diff --git a/Framework/Core/src/TableTreeHelpers.cxx b/Framework/Core/src/TableTreeHelpers.cxx index 92231cb9ce069..800a31e8ecac3 100644 --- a/Framework/Core/src/TableTreeHelpers.cxx +++ b/Framework/Core/src/TableTreeHelpers.cxx @@ -296,31 +296,4 @@ struct BranchInfo { }; } // namespace -FragmentToBatch::FragmentToBatch(StreamerCreator creator, std::shared_ptr fragment, arrow::MemoryPool* pool) - : mFragment{std::move(fragment)}, - mArrowMemoryPool{pool}, - mCreator{std::move(creator)} -{ -} - -void FragmentToBatch::setLabel(const char* label) -{ - mTableLabel = label; -} - -void FragmentToBatch::fill(std::shared_ptr schema, std::shared_ptr format) -{ - auto options = std::make_shared(); - options->dataset_schema = schema; - auto scanner = format->ScanBatchesAsync(options, mFragment); - auto batch = (*scanner)(); - mRecordBatch = *batch.result(); - // Notice that up to here the buffer was not yet filled. -} - -std::shared_ptr FragmentToBatch::finalize() -{ - return mRecordBatch; -} - } // namespace o2::framework From b8189f414f63a094e9d8ceab289dc27ba3af8bcd Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Thu, 2 Oct 2025 12:15:10 +0200 Subject: [PATCH 36/99] GPU TPC dEdx: fixing bug in padPos calculation --- GPU/GPUTracking/dEdx/GPUdEdx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/dEdx/GPUdEdx.h b/GPU/GPUTracking/dEdx/GPUdEdx.h index 9e1727d4988ad..b711f1a685a4c 100644 --- a/GPU/GPUTracking/dEdx/GPUdEdx.h +++ b/GPU/GPUTracking/dEdx/GPUdEdx.h @@ -118,7 +118,7 @@ GPUdnii() void GPUdEdx::fillCluster(float qtot, float qmax, int32_t padRow, uint const float tanTheta = CAMath::Sqrt(tgl2 * sec2); // getting the topology correction - const uint32_t padPos = CAMath::Max(GPUTPCGeometry::NPads(padRow) - 1, CAMath::Float2UIntRn(pad)); // position of the pad is shifted half a pad ( pad=3 -> centre position of third pad) + const uint32_t padPos = CAMath::Min(GPUTPCGeometry::NPads(padRow) - 1, CAMath::Float2UIntRn(pad)); // position of the pad is shifted half a pad ( pad=3 -> centre position of third pad) const float absRelPad = CAMath::Abs(pad - padPos); const int32_t region = geo.GetRegion(padRow); z = CAMath::Abs(z); From 1b5d3d8b3df8b03b36b8ae17990401a203562a3b Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 2 Oct 2025 23:26:18 +0200 Subject: [PATCH 37/99] ITS: fix truth seeding for only signal (#14715) --- .../ITS/tracking/src/VertexerTraits.cxx | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index b8b30e515d1ca..a546056075700 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -514,34 +514,34 @@ void VertexerTraits::addTruthSeedingVertices() bounded_vector events; }; std::map vertices; - for (int iSrc{0}; iSrc < mcReader.getNSources(); ++iSrc) { - auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); - for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { - const auto& ir = irs[eveId2colId[iEve]]; - if (!ir.isDummy()) { // do we need this, is this for diffractive events? - const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); - int rofId = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) / roFrameLengthInBC; - if (!vertices.contains(rofId)) { - vertices[rofId] = { - .vertices = bounded_vector(mMemoryPool.get()), - .srcs = bounded_vector(mMemoryPool.get()), - .events = bounded_vector(mMemoryPool.get()), - }; - } - Vertex vert; - vert.setTimeStamp(rofId); - vert.setNContributors(std::ranges::count_if(mcReader.getTracks(iSrc, iEve), [](const auto& trk) { - return trk.isPrimary() && trk.GetPt() > 0.2 && std::abs(trk.GetEta()) < 1.3; - })); - vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); - vert.setChi2(1); - constexpr float cov = 50e-9; - vert.setCov(cov, cov, cov, cov, cov, cov); - vertices[rofId].vertices.push_back(vert); - vertices[rofId].srcs.push_back(iSrc); - vertices[rofId].events.push_back(iEve); + const int iSrc = 0; // take only events from collision generator + auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); + for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { + const auto& ir = irs[eveId2colId[iEve]]; + if (!ir.isDummy()) { // do we need this, is this for diffractive events? + const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); + int rofId = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) / roFrameLengthInBC; + if (!vertices.contains(rofId)) { + vertices[rofId] = { + .vertices = bounded_vector(mMemoryPool.get()), + .srcs = bounded_vector(mMemoryPool.get()), + .events = bounded_vector(mMemoryPool.get()), + }; } + Vertex vert; + vert.setTimeStamp(rofId); + vert.setNContributors(std::ranges::count_if(mcReader.getTracks(iSrc, iEve), [](const auto& trk) { + return trk.isPrimary() && trk.GetPt() > 0.2 && std::abs(trk.GetEta()) < 1.3; + })); + vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); + vert.setChi2(1); + constexpr float cov = 50e-9; + vert.setCov(cov, cov, cov, cov, cov, cov); + vertices[rofId].vertices.push_back(vert); + vertices[rofId].srcs.push_back(iSrc); + vertices[rofId].events.push_back(iEve); } + mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); } size_t nVerts{0}; for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { From 25c253f522bd026f1a23e574f4ace9df263408bc Mon Sep 17 00:00:00 2001 From: Evgeny Kryshen Date: Fri, 3 Oct 2025 14:39:09 +0300 Subject: [PATCH 38/99] Fixes for modules at negative eta (#14716) * Fixes for modules at negative eta * Add braces in if statements * retrigger checks --- .../ECal/base/include/ECalBase/Geometry.h | 1 + .../ALICE3/ECal/base/src/Geometry.cxx | 46 +++++--- .../include/ECalReconstruction/Clusterizer.h | 42 ++++--- .../ECal/reconstruction/src/Clusterizer.cxx | 103 ++++++++++++------ .../ALICE3/ECal/simulation/src/Detector.cxx | 4 +- .../ALICE3/ECal/simulation/src/Digitizer.cxx | 8 +- 6 files changed, 134 insertions(+), 70 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h index ecfcb5b7cbad6..a780e36f45938 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h +++ b/Detectors/Upgrades/ALICE3/ECal/base/include/ECalBase/Geometry.h @@ -61,6 +61,7 @@ class Geometry double getSamplingAlpha() { return mSamplingAlpha; } double getCrystalDeltaPhi() { return 2 * std::atan(mCrystalModW / 2 / mRMin); } double getSamplingDeltaPhi() { return 2 * std::atan(mSamplingModW / 2 / mRMin); } + double getFrontFaceMaxEta(int i); double getCrystalPhiMin(); double getSamplingPhiMin(); int getNModulesZ() { return mNModulesZ; } diff --git a/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx b/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx index 9483b83f19f49..2d6bdf160f393 100644 --- a/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/base/src/Geometry.cxx @@ -73,11 +73,18 @@ double Geometry::getSamplingPhiMin() return (superModuleDeltaPhi - samplingDeltaPhi * mNSamplingModulesPhi) / 2.; } +double Geometry::getFrontFaceMaxEta(int i) +{ + double theta = std::atan(mRMin / getFrontFaceZatMinR(i)); + return -std::log(std::tan(theta / 2.)); +} + //============================================================================== void Geometry::fillFrontFaceCenterCoordinates() { - if (mFrontFaceCenterR.size() > 0) + if (mFrontFaceCenterR.size() > 0) { return; + } mFrontFaceCenterTheta.resize(mNCrystalModulesZ + mNSamplingModulesZ); mFrontFaceZatMinR.resize(mNCrystalModulesZ + mNSamplingModulesZ); mFrontFaceCenterR.resize(mNCrystalModulesZ + mNSamplingModulesZ); @@ -153,7 +160,7 @@ int Geometry::getCellID(int moduleId, int sectorId, bool isCrystal) if (sectorId % 2 == 0) { // sampling at positive eta cellID = sectorId / 2 * mNModulesZ + moduleId + mNSamplingModulesZ + mNCrystalModulesZ * 2; } else { // sampling at negative eta - cellID = sectorId / 2 * mNModulesZ - moduleId + mNSamplingModulesZ; + cellID = sectorId / 2 * mNModulesZ - moduleId + mNSamplingModulesZ - 1; } } return cellID; @@ -206,13 +213,15 @@ void Geometry::detIdToGlobalPosition(int detId, double& x, double& y, double& z) { int chamber, sector, iphi, iz; detIdToRelIndex(detId, chamber, sector, iphi, iz); + double r = 0; if (iz < mNSamplingModulesZ + mNCrystalModulesZ) { z = -mFrontFaceCenterZ[mNSamplingModulesZ + mNCrystalModulesZ - iz - 1]; + r = mFrontFaceCenterR[mNSamplingModulesZ + mNCrystalModulesZ - iz - 1]; } else { - z = +mFrontFaceCenterZ[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; + z = mFrontFaceCenterZ[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; + r = mFrontFaceCenterR[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; } double phi = chamber == 1 ? mFrontFaceCenterCrystalPhi[iphi] : mFrontFaceCenterSamplingPhi[iphi]; - double r = mFrontFaceCenterR[iz % (mNSamplingModulesZ + mNCrystalModulesZ)]; x = r * std::cos(phi); y = r * std::sin(phi); } @@ -224,10 +233,12 @@ int Geometry::areNeighboursVertex(int detId1, int detId2) const int ch2, sector2, iphi2, iz2; detIdToRelIndex(detId1, ch1, sector1, iphi1, iz1); detIdToRelIndex(detId2, ch2, sector2, iphi2, iz2); - if (sector1 != sector2 || ch1 != ch2) + if (sector1 != sector2 || ch1 != ch2) { return 0; - if (std::abs(iphi1 - iphi2) <= 1 && std::abs(iz1 - iz2) <= 1) + } + if (std::abs(iphi1 - iphi2) <= 1 && std::abs(iz1 - iz2) <= 1) { return 1; + } return 0; } @@ -235,29 +246,32 @@ int Geometry::areNeighboursVertex(int detId1, int detId2) const bool Geometry::isAtTheEdge(int cellId) { auto [row, col] = globalRowColFromIndex(cellId); - if (col == 0) + if (col == 0) { return 1; - if (col == mNSamplingModulesZ) + } else if (col == mNSamplingModulesZ) { return 1; - if (col == mNSamplingModulesZ - 1) + } else if (col == mNSamplingModulesZ - 1) { return 1; - if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ) + } else if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ) { return 1; - if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ - 1) + } else if (col == mNSamplingModulesZ + 2 * mNCrystalModulesZ - 1) { return 1; - if (col == mNModulesZ - 1) + } else if (col == mNModulesZ - 1) { return 1; + } for (int m = 0; m <= mNSuperModules; m++) { if (isCrystal(cellId)) { - if (row == m * mNCrystalModulesPhi) + if (row == m * mNCrystalModulesPhi) { return 1; - if (row == m * mNCrystalModulesPhi - 1) + } else if (row == m * mNCrystalModulesPhi - 1) { return 1; + } } else { - if (row == m * mNSamplingModulesPhi) + if (row == m * mNSamplingModulesPhi) { return 1; - if (row == m * mNSamplingModulesPhi - 1) + } else if (row == m * mNSamplingModulesPhi - 1) { return 1; + } } } return 0; diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h b/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h index 3bb7cab4b11e3..5e4d36f831360 100644 --- a/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/include/ECalReconstruction/Clusterizer.h @@ -48,25 +48,33 @@ class Clusterizer void setClusteringThreshold(double threshold) { mClusteringThreshold = threshold; } void setCrystalDigitThreshold(double threshold) { mCrystalDigitThreshold = threshold; } void setSamplingDigitThreshold(double threshold) { mSamplingDigitThreshold = threshold; } + void setCrystalEnergyCorrectionPars(std::vector pars) { mCrystalEnergyCorrectionPars = pars; } + void setSamplingEnergyCorrectionPars(std::vector pars) { mSamplingEnergyCorrectionPars = pars; } + void setCrystalZCorrectionPars(std::vector pars) { mCrystalZCorrectionPars = pars; } + void setSamplingZCorrectionPars(std::vector pars) { mSamplingZCorrectionPars = pars; } private: - std::vector> mDigitIndices; // 2D map of digit indices used for recursive cluster finding - bool mUnfoldClusters = true; // to perform cluster unfolding - double mCrystalDigitThreshold = 0.040; // minimal energy of crystal digit - double mSamplingDigitThreshold = 0.100; // minimal energy of sampling digit - double mClusteringThreshold = 0.050; // minimal energy of digit to start clustering (GeV) - double mClusteringTimeGate = 1e9; // maximal time difference between digits to be accepted to clusters (in ns) - int mNLMMax = 30; // maximal number of local maxima in unfolding - double mLogWeight = 4.; // cutoff used in log. weight calculation - double mUnfogingEAccuracy = 1.e-4; // accuracy of energy calculation in unfoding prosedure (GeV) - double mUnfogingXZAccuracy = 1.e-2; // accuracy of position calculation in unfolding procedure (cm) - int mNMaxIterations = 100; // maximal number of iterations in unfolding procedure - double mLocalMaximumCut = 0.015; // minimal height of local maximum over neighbours - bool mApplyCorrectionZ = 1; // z-correction - bool mApplyCorrectionE = 1; // energy-correction - TF1* fCrystalShowerShape; //! Crystal shower shape - TF1* fSamplingShowerShape; //! Sampling shower shape - TF1* fCrystalRMS; //! Crystal RMS + std::vector> mDigitIndices; // 2D map of digit indices used for recursive cluster finding + bool mUnfoldClusters = true; // to perform cluster unfolding + double mCrystalDigitThreshold = 0.040; // minimal energy of crystal digit + double mSamplingDigitThreshold = 0.100; // minimal energy of sampling digit + double mClusteringThreshold = 0.050; // minimal energy of digit to start clustering (GeV) + double mClusteringTimeGate = 1e9; // maximal time difference between digits to be accepted to clusters (in ns) + int mNLMMax = 30; // maximal number of local maxima in unfolding + double mLogWeight = 4.; // cutoff used in log. weight calculation + double mUnfogingEAccuracy = 1.e-4; // accuracy of energy calculation in unfoding prosedure (GeV) + double mUnfogingXZAccuracy = 1.e-2; // accuracy of position calculation in unfolding procedure (cm) + int mNMaxIterations = 100; // maximal number of iterations in unfolding procedure + double mLocalMaximumCut = 0.015; // minimal height of local maximum over neighbours + bool mApplyCorrectionZ = 1; // apply z-correction + bool mApplyCorrectionE = 1; // apply energy-correction + TF1* fCrystalShowerShape; //! Crystal shower shape + TF1* fSamplingShowerShape; //! Sampling shower shape + TF1* fCrystalRMS; //! Crystal RMS + std::vector mCrystalEnergyCorrectionPars; // crystal energy-correction parameters + std::vector mSamplingEnergyCorrectionPars; // sampling energy-correction parameters + std::vector mCrystalZCorrectionPars; // crystal z-correction parameters + std::vector mSamplingZCorrectionPars; // sampling z-correction parameters }; } // namespace ecal diff --git a/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx index c84f62b60ec38..28efa78059dc1 100644 --- a/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/reconstruction/src/Clusterizer.cxx @@ -31,6 +31,45 @@ Clusterizer::Clusterizer(bool applyCorrectionZ, bool applyCorrectionE) mDigitIndices.resize(geo.getNrows(), std::vector(geo.getNcols(), -1)); mApplyCorrectionZ = applyCorrectionZ; mApplyCorrectionE = applyCorrectionE; + + mCrystalEnergyCorrectionPars.reserve(6); + mCrystalEnergyCorrectionPars[0] = 0.00444; + mCrystalEnergyCorrectionPars[1] = -1.322; + mCrystalEnergyCorrectionPars[2] = 1.021; + mCrystalEnergyCorrectionPars[3] = 0.0018; + mCrystalEnergyCorrectionPars[4] = 0.; + mCrystalEnergyCorrectionPars[5] = 0.; + + mSamplingEnergyCorrectionPars.reserve(6); + mSamplingEnergyCorrectionPars[0] = 0.0033; + mSamplingEnergyCorrectionPars[1] = -2.09; + mSamplingEnergyCorrectionPars[2] = 1.007; + mSamplingEnergyCorrectionPars[3] = 0.0667; + mSamplingEnergyCorrectionPars[4] = -0.108; + mSamplingEnergyCorrectionPars[5] = 0.0566; + + mCrystalZCorrectionPars.reserve(9); + mCrystalZCorrectionPars[0] = -0.005187; + mCrystalZCorrectionPars[1] = 0.7301; + mCrystalZCorrectionPars[2] = -0.7382; + mCrystalZCorrectionPars[3] = 0.; + mCrystalZCorrectionPars[4] = 0.; + mCrystalZCorrectionPars[5] = 0.; + mCrystalZCorrectionPars[6] = 0.; + mCrystalZCorrectionPars[7] = 0.; + mCrystalZCorrectionPars[8] = 0.; + + mSamplingZCorrectionPars.reserve(9); + mSamplingZCorrectionPars[0] = -2.137; + mSamplingZCorrectionPars[1] = 6.400; + mSamplingZCorrectionPars[2] = -3.342; + mSamplingZCorrectionPars[3] = -0.1364; + mSamplingZCorrectionPars[4] = 0.4019; + mSamplingZCorrectionPars[5] = -0.1969; + mSamplingZCorrectionPars[6] = 0.008223; + mSamplingZCorrectionPars[7] = -0.02425; + mSamplingZCorrectionPars[8] = 0.01190; + fCrystalShowerShape = new TF1("fCrystal", "x<[1] ? [0]*exp([3]*x+[4]*x*x+[5]*x*x*x) : (x<[2] ? [0]*[6]*exp([7]*x+[8]*x*x) : [0]*[9]*exp([10]*x+[11]*x*x))", 0, 15); double pc[12]; pc[0] = 1. / 13.15; @@ -94,13 +133,14 @@ void Clusterizer::findClusters(const gsl::span& digits, std::vector void Clusterizer::addDigitToCluster(Cluster& cluster, int row, int col, const gsl::span& digits) { auto& geo = Geometry::instance(); - if (row < 0 || row >= geo.getNrows() || col < 0 || col >= geo.getNcols()) + if (row < 0 || row >= geo.getNrows() || col < 0 || col >= geo.getNcols()) { return; + } int digitIndex = mDigitIndices[row][col]; LOGP(debug, " checking row={} and col={} digitIndex={}", row, col, digitIndex); - if (digitIndex < 0) + if (digitIndex < 0) { return; - + } const Digit& digit = digits[digitIndex]; if (cluster.getMultiplicity() > 0) { // check if new digit is in the same chamber and sector @@ -108,8 +148,9 @@ void Clusterizer::addDigitToCluster(Cluster& cluster, int row, int col, const gs auto [sector1, ch1] = geo.getSectorChamber(digit.getTower()); auto [sector2, ch2] = geo.getSectorChamber(digit2.getTower()); LOGP(debug, " checking if sector and chamber are the same: ({},{}) ({},{})", sector1, ch1, sector2, ch2); - if (sector1 != sector2 || ch1 != ch2) + if (sector1 != sector2 || ch1 != ch2) { return; + } } mDigitIndices[row][col] = -1; @@ -140,11 +181,13 @@ void Clusterizer::makeClusters(const gsl::span& digits, std::vector auto [row, col] = geo.globalRowColFromIndex(digit.getTower()); bool isCrystal = geo.isCrystal(digit.getTower()); if (isCrystal) { - if (digit.getEnergy() < mCrystalDigitThreshold) + if (digit.getEnergy() < mCrystalDigitThreshold) { continue; + } } else { - if (digit.getEnergy() < mSamplingDigitThreshold) + if (digit.getEnergy() < mSamplingDigitThreshold) { continue; + } } mDigitIndices[row][col] = i; } @@ -153,10 +196,12 @@ void Clusterizer::makeClusters(const gsl::span& digits, std::vector for (int i = 0; i < nDigits; i++) { const Digit& digitSeed = digits[i]; auto [row, col] = geo.globalRowColFromIndex(digitSeed.getTower()); - if (mDigitIndices[row][col] < 0) + if (mDigitIndices[row][col] < 0) { continue; // digit was already added in one of the clusters - if (digitSeed.getEnergy() < mClusteringThreshold) + } + if (digitSeed.getEnergy() < mClusteringThreshold) { continue; + } LOGP(debug, " starting new cluster at row={} and col={}", row, col); auto& cluster = clusters.emplace_back(); addDigitToCluster(cluster, row, col, digits); @@ -343,8 +388,9 @@ void Clusterizer::evalClusters(std::vector& clusters) double xi, yi, zi; geo.detIdToGlobalPosition(towerId, xi, yi, zi); double r = std::sqrt((x - xi) * (x - xi) + (y - yi) * (y - yi) + (z - zi) * (z - zi)); - if (r > 2.2) + if (r > 2.2) { continue; + } double frac = fCrystalShowerShape->Eval(r); double rms = fCrystalRMS->Eval(r); chi2 += std::pow((energy / ee - frac) / rms, 2.); @@ -354,38 +400,30 @@ void Clusterizer::evalClusters(std::vector& clusters) // correct cluster energy and z position float eta = std::abs(cluster.getEta()); - float eCor = 1; - float zCor = 0; bool isCrystal = geo.isCrystal(cluster.getDigitTowerId(0)); - if (isCrystal) { - eCor = 0.00444 * std::pow(ee, -1.322) + (1.021 + 0.0018 * eta); - if (mApplyCorrectionE) - ee *= eCor; - if (mApplyCorrectionZ) - zCor = (-0.00518682 + 0.730052 * eta - 0.73817 * eta * eta); - } else { - eCor = 0.0033 * std::pow(ee, -2.09) + (1.007 + 0.0667 * eta - 0.108 * eta * eta + 0.0566 * eta * eta * eta); - if (mApplyCorrectionE) - ee *= eCor; - if (mApplyCorrectionZ) - zCor = (-2.13679 + 6.40009 * eta - 3.34233 * eta * eta) + (-0.136425 + 0.401887 * eta - 0.196851 * eta * eta) * ee + (0.00822276 - 0.0242512 * eta + 0.0118986 * eta * eta) * ee * ee; + if (mApplyCorrectionE) { + std::vector& pe = isCrystal ? mCrystalEnergyCorrectionPars : mSamplingEnergyCorrectionPars; + ee *= pe[0] * std::pow(ee, pe[1]) + pe[2] + pe[3] * eta + pe[4] * eta * eta + pe[5] * eta * eta * eta; + cluster.setE(ee); + } + if (mApplyCorrectionZ) { + std::vector& pz = isCrystal ? mCrystalZCorrectionPars : mSamplingZCorrectionPars; + float zCor = (pz[0] + pz[1] * eta + pz[2] * eta * eta) + (pz[3] + pz[4] * eta + pz[5] * eta * eta) * ee + (pz[6] + pz[7] * eta + pz[8] * eta * eta) * ee * ee; + cluster.setZ(z > 0 ? z - zCor : z + zCor); } - - cluster.setE(ee); - cluster.setZ(cluster.getZ() - zCor); // check if cluster is at the edge of detector module bool isEdge = 0; for (size_t i = 0; i < cluster.getMultiplicity(); i++) { int towerId = cluster.getDigitTowerId(i); - if (!geo.isAtTheEdge(towerId)) - continue; - isEdge = 1; - break; + if (geo.isAtTheEdge(towerId)) { + isEdge = 1; + break; + } } cluster.setEdgeFlag(isEdge); - LOGF(debug, "Cluster coordinates: (%6.2f,%6.2f,%6.2f), eCor=%6.2f zCor=%6.2f", cluster.getX(), cluster.getY(), cluster.getZ(), eCor, zCor); + LOGF(debug, "Cluster coordinates: (%6.2f,%6.2f,%6.2f)", cluster.getX(), cluster.getY(), cluster.getZ()); } } @@ -403,8 +441,9 @@ int Clusterizer::getNumberOfLocalMax(Cluster& clu, int* maxAt, float* maxAtEnerg for (int i = 0; i < n; i++) { isLocalMax[i] = false; float en1 = clu.getDigitEnergy(i); - if (en1 > mClusteringThreshold) + if (en1 > mClusteringThreshold) { isLocalMax[i] = true; + } } for (int i = 0; i < n; i++) { diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx index 93089bb8ced14..f0de8aa4022a6 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Detector.cxx @@ -358,9 +358,9 @@ bool Detector::ProcessHits(FairVolume* vol) return false; } - if (isCrystal) + if (isCrystal) { LOGP(debug, "Processing crystal {}", volName.Data()); - else { + } else { eloss *= mSamplingFactorTransportModel; LOGP(debug, "Processing scintillator {}", volName.Data()); } diff --git a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx index f213ba563d86d..42c1908a29d18 100644 --- a/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/ECal/simulation/src/Digitizer.cxx @@ -57,16 +57,18 @@ void Digitizer::processHits(const std::vector* hits, std::vector& di bool isCrystal = geo.isCrystal(cellID); if (isCrystal) { // crystal double elossSmearedNpe = gRandom->Poisson(eloss * mCrystalPePerGeV) / mCrystalPePerGeV; - if (mSmearCrystal) + if (mSmearCrystal) { elossSmeared = elossSmearedNpe * gRandom->Gaus(1, 0.007); // light attenuation in crystals - } else { // sampling + } + } else { // sampling elossSmeared *= mSamplingFraction; } Digit& digit = mArrayD[cellID]; digit.setAmplitude(digit.getAmplitude() + elossSmeared); - if (t < digit.getTimeStamp()) + if (t < digit.getTimeStamp()) { digit.setTimeStamp(t); // setting earliest time, TODO: add time smearing + } LOGF(debug, " crystal: %d cellID = %5d, eloss = %8.5f elossSmeared = %8.5f time = %8.5f", isCrystal, cellID, eloss, elossSmeared, t); // Adding MC info From 0975fce4307d9b9f883cf8c84a97839075c0592d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 3 Oct 2025 14:11:44 +0200 Subject: [PATCH 39/99] DPL improve signposts when flushing metrics (#14711) --- Framework/Core/src/CommonServices.cxx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index 22324cd84b390..5a2876e074d29 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -826,30 +826,34 @@ auto flushMetrics(ServiceRegistryRef registry, DataProcessingStats& stats) -> vo auto& relayer = registry.get(); // Send all the relevant metrics for the relayer to update the GUI - stats.flushChangedMetrics([&monitoring](DataProcessingStats::MetricSpec const& spec, int64_t timestamp, int64_t value) mutable -> void { + stats.flushChangedMetrics([&monitoring, sid](DataProcessingStats::MetricSpec const& spec, int64_t timestamp, int64_t value) mutable -> void { // convert timestamp to a time_point auto tp = std::chrono::time_point(std::chrono::milliseconds(timestamp)); auto metric = o2::monitoring::Metric{spec.name, Metric::DefaultVerbosity, tp}; if (spec.kind == DataProcessingStats::Kind::UInt64) { if (value < 0) { - LOG(debug) << "Value for " << spec.name << " is negative, setting to 0"; + O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Value for %{public}s is negative, setting to 0", + spec.name.c_str()); value = 0; } metric.addValue((uint64_t)value, "value"); } else { if (value > (int64_t)std::numeric_limits::max()) { - LOG(warning) << "Value for " << spec.name << " is too large, setting to INT_MAX"; + O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Value for %{public}s is too large, setting to INT_MAX", + spec.name.c_str()); value = (int64_t)std::numeric_limits::max(); } if (value < (int64_t)std::numeric_limits::min()) { + O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Value for %{public}s is too small, setting to INT_MIN", + spec.name.c_str()); value = (int64_t)std::numeric_limits::min(); - LOG(warning) << "Value for " << spec.name << " is too small, setting to INT_MIN"; } metric.addValue((int)value, "value"); } if (spec.scope == DataProcessingStats::Scope::DPL) { metric.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL); } + O2_SIGNPOST_EVENT_EMIT(monitoring_service, sid, "flushChangedMetrics", "Flushing metric %{public}s", spec.name.c_str()); monitoring.send(std::move(metric)); }); relayer.sendContextState(); From dc893c95bbda1521e797e4e7833dee545b45344c Mon Sep 17 00:00:00 2001 From: amorsch Date: Thu, 2 Oct 2025 11:31:09 +0200 Subject: [PATCH 40/99] extension of barrel volume to avoid extrusion of FOCAL --- Detectors/Passive/src/Cave.cxx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Detectors/Passive/src/Cave.cxx b/Detectors/Passive/src/Cave.cxx index e2ea513095c72..208084a335ab5 100644 --- a/Detectors/Passive/src/Cave.cxx +++ b/Detectors/Passive/src/Cave.cxx @@ -85,23 +85,27 @@ void Cave::ConstructGeometry() shCaveTR1->DefineSection(0, -706. - 8.6, 0., 790.5); shCaveTR1->DefineSection(1, 707. + 7.6, 0., 790.5); TGeoTube* shCaveTR2 = new TGeoTube("shCaveTR2", 0., 150., 110.); + TGeoTube* shCaveTR3 = new TGeoTube("shCaveTR3", 0., 80., 75.); TGeoTranslation* transCaveTR2 = new TGeoTranslation("transTR2", 0, 30., -505. - 110.); + TGeoTranslation* transCaveTR3 = new TGeoTranslation("transTR3", 0, 30., 714.6 + 75.); transCaveTR2->RegisterYourself(); - TGeoCompositeShape* shCaveTR = new TGeoCompositeShape("shCaveTR", "shCaveTR1-shCaveTR2:transTR2"); + transCaveTR3->RegisterYourself(); + + TGeoCompositeShape* shCaveTR = new TGeoCompositeShape("shCaveTR", "shCaveTR1-shCaveTR2:transTR2+shCaveTR3:transTR3"); TGeoVolume* voBarrel = new TGeoVolume("barrel", shCaveTR, kMedAir); cavevol->AddNode(voBarrel, 1, new TGeoTranslation(0., -30., 0.)); if (mHasRB24) { // should be not true only for alice 3 // mother volume for RB24 side (FDD, Compensator) - const Float_t kRB24CL = 2. * 597.9; + const Float_t kRB24CL = 2. * 597.9 - 150.; auto shCaveRB24 = new TGeoPcon(0., 360., 6); - Float_t z0 = kRB24CL / 2 + 714.6; + Float_t z0 = kRB24CL / 2 + 714.6 + 150.; shCaveRB24->DefineSection(0, -kRB24CL / 2., 0., 105.); shCaveRB24->DefineSection(1, -z0 + 1705., 0., 105.); shCaveRB24->DefineSection(2, -z0 + 1705., 0., 14.5); - shCaveRB24->DefineSection(3, -z0 + 1880., 0., 14.5); - shCaveRB24->DefineSection(4, -z0 + 1880., 0., 40.0); - shCaveRB24->DefineSection(5, kRB24CL / 2, 0., 40.0); + shCaveRB24->DefineSection(3, -z0 + 1878, 0., 14.5); + shCaveRB24->DefineSection(4, -z0 + 1878., 0., 40.0); + shCaveRB24->DefineSection(5, kRB24CL / 2., 0., 40.0); TGeoVolume* caveRB24 = new TGeoVolume("caveRB24", shCaveRB24, kMedAir); caveRB24->SetVisibility(0); From 185a2cd7a687f2a3f2387e24dd595c29ed561567 Mon Sep 17 00:00:00 2001 From: amorsch Date: Thu, 2 Oct 2025 11:34:00 +0200 Subject: [PATCH 41/99] beampipes adapted to new barrel and corrections: overlaps, gaps, Alu instead of vaccum was used in front of FOCAL --- .../include/DetectorsPassive/PipeRun4.h | 2 +- Detectors/Passive/src/Pipe.cxx | 6 +- Detectors/Passive/src/PipeRun4.cxx | 117 ++++++++---------- 3 files changed, 56 insertions(+), 69 deletions(-) diff --git a/Detectors/Passive/include/DetectorsPassive/PipeRun4.h b/Detectors/Passive/include/DetectorsPassive/PipeRun4.h index 1943bb25a802f..5eadb7af1003c 100644 --- a/Detectors/Passive/include/DetectorsPassive/PipeRun4.h +++ b/Detectors/Passive/include/DetectorsPassive/PipeRun4.h @@ -46,7 +46,7 @@ class PipeRun4 : public PassiveBase TGeoPcon* makeMotherFromTemplate(const TGeoPcon* shape, int imin = -1, int imax = -1, float r0 = 0., int nz = -1); TGeoPcon* makeInsulationFromTemplate(TGeoPcon* shape); - TGeoVolume* makeBellow(const char* ext, int nc, float rMin, float rMax, float dU, float rPlie, + TGeoVolume* makeBellow(const char* ext, int nc, float rMin, float rMax, float rPlie, float dPlie); TGeoVolume* makeBellowCside(const char* ext, int nc, float rMin, float rMax, float rPlie, float dPlie); diff --git a/Detectors/Passive/src/Pipe.cxx b/Detectors/Passive/src/Pipe.cxx index 56f6429bc73c8..56ccfc45f0b89 100644 --- a/Detectors/Passive/src/Pipe.cxx +++ b/Detectors/Passive/src/Pipe.cxx @@ -786,13 +786,13 @@ void Pipe::ConstructGeometry() // Copper Tube RB24/1 const Float_t kRB24CuTubeL = 381.5; - const Float_t kRB24cCuTubeL = 155.775; + const Float_t kRB24cCuTubeL = 155.775 - 150.; const Float_t kRB24bCuTubeL = kRB24CuTubeL - kRB24cCuTubeL; const Float_t kRB24CuTubeRi = 8.0 / 2.; const Float_t kRB24CuTubeRo = 8.4 / 2.; const Float_t kRB24CuTubeFRo = 7.6; const Float_t kRB24CuTubeFL = 1.86; - const Float_t kRB24CL = 2. * 597.9; + const Float_t kRB24CL = 2. * 597.9 - 150.; // // introduce cut at end of barrel 714.6m // @@ -812,7 +812,7 @@ void Pipe::ConstructGeometry() voRB24cCuTubeM->AddNode(voRB24cCuTube, 1, gGeoIdentity); // Air outside tube with higher transport cuts - TGeoVolume* voRB24CuTubeA = new TGeoVolume("voRB24CuTubeA", new TGeoTube(80., 81., kRB24bCuTubeL / 2.), kMedAirHigh); + TGeoVolume* voRB24CuTubeA = new TGeoVolume("voRB24CuTubeA", new TGeoTube(79., 80., kRB24bCuTubeL / 2.), kMedAirHigh); voRB24CuTubeA->SetVisibility(0); // Simplified DN 100 Flange diff --git a/Detectors/Passive/src/PipeRun4.cxx b/Detectors/Passive/src/PipeRun4.cxx index 0403624f23b4c..5aa0b63a6ac78 100644 --- a/Detectors/Passive/src/PipeRun4.cxx +++ b/Detectors/Passive/src/PipeRun4.cxx @@ -642,7 +642,7 @@ void PipeRun4::ConstructGeometry() // Drawings from C. Gargiulo : // \\cern.ch\dfs\Workspaces\c\cgargiul\EXPERIMENT\ALICE\ALICE_MECHANICS\ALICE_DATA_PACKAGE\IN\DETECTORS\ITS_UPGRADE\1-DESIGN\0-IF_Control_Drawing\20140207_ICD_ITS_MFT_BP ///////////////////////////////////////////////////////////////////// - + TGeoVolumeAssembly* beamPipeAsideSection = new TGeoVolumeAssembly("BeamPipeAsideSection"); float kConicalBerilliumMinThickness = 0.08; float kConicalBerilliumMaxThickness = 0.1; float kFlangeZ = 483.75; @@ -656,33 +656,34 @@ void PipeRun4::ConstructGeometry() float kConicalBePipeEndOuterRadius = 3.0; TGeoPcon* tube0 = new TGeoPcon(0., 360., 5); - tube0->DefineSection(0, kFlangeZ - kFlangeWidth / 2, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - tube0->DefineSection(1, kConicalBerylliumEnd, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); + tube0->DefineSection(0, kFlangeZ - kFlangeWidth / 2, kConicalBePipeEndOuterRadius - kConicalBerilliumMinThickness, kConicalBePipeEndOuterRadius); + tube0->DefineSection(1, kConicalBerylliumEnd, kConicalBePipeEndOuterRadius - kConicalBerilliumMinThickness, kConicalBePipeEndOuterRadius); tube0->DefineSection(2, kSupport1 + kSupportWidth, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness, kPipeRadiusAtSupport1); tube0->DefineSection(3, kSupport1, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness, kPipeRadiusAtSupport1); tube0->DefineSection(4, kBeryliumSectionZmax, kBeryliumSectionOuterRadius - kConicalBerilliumMinThickness, kBeryliumSectionOuterRadius); // need a transition to kConicalBerilliumMaxThickness - TGeoPcon* tube0vide = new TGeoPcon(0., 360., 5); - tube0vide->DefineSection(0, kFlangeZ - kFlangeWidth / 2, 0., kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - tube0vide->DefineSection(1, kConicalBerylliumEnd, 0., kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - tube0vide->DefineSection(2, kSupport1 + kSupportWidth, 0, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness - 0.01); - tube0vide->DefineSection(3, kSupport1, 0, kPipeRadiusAtSupport1 - kConicalBerilliumMinThickness - 0.01); - tube0vide->DefineSection(4, kBeryliumSectionZmax, 0., kBeryliumSectionOuterRadius - kConicalBerilliumMinThickness - 0.01); + TGeoPcon* tube0Mo = new TGeoPcon(0., 360., 5); + tube0Mo->DefineSection(0, kFlangeZ - kFlangeWidth / 2, 0., kConicalBePipeEndOuterRadius); + tube0Mo->DefineSection(1, kConicalBerylliumEnd, 0., kConicalBePipeEndOuterRadius); + tube0Mo->DefineSection(2, kSupport1 + kSupportWidth, 0, kPipeRadiusAtSupport1); + tube0Mo->DefineSection(3, kSupport1, 0, kPipeRadiusAtSupport1); + tube0Mo->DefineSection(4, kBeryliumSectionZmax, 0., kBeryliumSectionOuterRadius); TGeoVolume* votube0 = new TGeoVolume("votube0", tube0, kMedBe); votube0->SetLineColor(kRed); - TGeoVolume* votube0vide = new TGeoVolume("votube0vide", tube0vide, kMedVac); - votube0vide->SetLineColor(kGreen); + TGeoVolume* votube0Mo = new TGeoVolume("votube0Mo", tube0Mo, kMedVac); + votube0Mo->AddNode(votube0, 1, gGeoIdentity); + votube0Mo->SetLineColor(kGreen); - barrel->AddNode(votube0, 1, new TGeoTranslation(0., 30., 0.)); - barrel->AddNode(votube0vide, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(votube0Mo, 1, gGeoIdentity); - TGeoVolume* beampipeSupportA1 = makeSupportBar("A1", kPipeRadiusAtSupport1 + 0.01, kPipeRadiusAtSupport1 + 0.38, 20.67, 14.25); - barrel->AddNode(beampipeSupportA1, 1, new TGeoTranslation(0., 30, kSupport1 + kSupportWidth / 2.)); + // already defined in IT3 + // TGeoVolume* beampipeSupportA1 = makeSupportBar("A1", kPipeRadiusAtSupport1 + 0.01, kPipeRadiusAtSupport1 + 0.38, 20.67, 14.25); + // beamPipeAsideSection->AddNode(beampipeSupportA1, 1, new TGeoTranslation(0., 0., kSupport1 + kSupportWidth / 2.)); // Length is approximate TGeoVolume* beampipeSupportA2 = makeSupportBar("A2", kConicalBePipeEndOuterRadius, kConicalBePipeEndOuterRadius + 0.38, 44, 37.5); - barrel->AddNode(beampipeSupportA2, 1, new TGeoTranslation(0., 30, kConicalBerylliumEnd + kSupportWidth / 2.)); + beamPipeAsideSection->AddNode(beampipeSupportA2, 1, new TGeoTranslation(0., 0., kConicalBerylliumEnd + kSupportWidth / 2.)); TGeoPcon* Bolt1 = new TGeoPcon(0., 360, 8); Bolt1->DefineSection(0, 0, 0, 0.5); @@ -734,7 +735,7 @@ void PipeRun4::ConstructGeometry() Bolts->AddNode(volBolt1, 7, t7); Bolts->AddNode(volBolt1, 8, t8); - barrel->AddNode(Bolts, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(Bolts, 1, gGeoIdentity); TGeoTranslation* Tflange = new TGeoTranslation(0, 0, kFlangeZ); Tflange->SetName("Tflange"); @@ -753,53 +754,40 @@ void PipeRun4::ConstructGeometry() TGeoVolume* volflange = new TGeoVolume("voFlangeHoles", FlangeWithHoles, kMedAlBe); volflange->SetLineWidth(2); volflange->SetLineColor(kGray); - - barrel->AddNode(volflange, 1, new TGeoTranslation(0., 30., 0.)); - - TGeoPcon* pipeSamell = new TGeoPcon(0., 360., 2); - pipeSamell->DefineSection(0, kFlangeZ + kFlangeWidth / 2, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - pipeSamell->DefineSection(1, kFlangeZ + 5.13 + 0.435 + 0.4 + 0.08, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - pipeSamell->SetName("pipeSamell"); - - TGeoVolume* VolpipeSmall = new TGeoVolume("voPipeSmallVac", pipeSamell, kMedAlu2219); - VolpipeSmall->SetLineWidth(2); - barrel->AddNode(VolpipeSmall, 1, new TGeoTranslation(0., 30., 0.)); - - TGeoPcon* pipeSmallVac = new TGeoPcon(0., 360., 2); - pipeSmallVac->DefineSection(0, kFlangeZ + kFlangeWidth / 2, 0, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - pipeSmallVac->DefineSection(1, kFlangeZ + 5.13 + 0.435 + 0.4 + 0.08, 0, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness - 0.01); - TGeoVolume* vopipeSmallVac = new TGeoVolume("voPipeSmallVac", pipeSmallVac, kMedVac); - vopipeSmallVac->SetLineColor(kGreen); - - barrel->AddNode(vopipeSmallVac, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(volflange, 1, gGeoIdentity); // -- Bellows on A side - // float plieradius = (3.72 + (2. * 7 - 2.) * 0.03) / (4. * 7); // radius of bellows "plis" float plieradiusA = 0.2; // radius of bellow plies - // ------------------ First Bellow -------------------- // Inner: 3.0 cm, outer 3.97 cm length 8.47 cm with 10 wiggles - // check meaning of dU ; it is probably the total length, see also below - TGeoVolume* vobellows1A = makeBellow("bellows1A", 10, 3.0, 3.97, 8.47, plieradiusA, 0.03); - // Z position is rough for now. - barrel->AddNode(vobellows1A, 1, new TGeoTranslation(0., 30., kFlangeZ + 10)); + TGeoVolume* vobellows1A = makeBellow("bellows1A", 10, 3.0, 3.97, plieradiusA, 0.03); + Float_t dU = (static_cast(vobellows1A->GetShape()))->GetDZ(); + beamPipeAsideSection->AddNode(vobellows1A, 1, new TGeoTranslation(0., 0., kFlangeZ + 2. * dU)); // Comments: removing 1/2 plie (see makeBellow): 0.31= 2*0.17-0.03 and 0.08: free space + Float_t pipeSmallDz = (dU - kFlangeWidth / 2.) / 2.; + TGeoTube* pipeSmall = new TGeoTube("pipeSmall", kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius, pipeSmallDz); + TGeoVolume* vopipeSmall = new TGeoVolume("voPipeSmall", pipeSmall, kMedAlu2219); + vopipeSmall->SetLineWidth(2); - // ------------------ Outer pipe after flange -------------------- - TGeoPcon* pipeOut = new TGeoPcon(0., 360., 2); - pipeOut->DefineSection(0, kFlangeZ + 13.6 - 0.08, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); - pipeOut->DefineSection(1, 714.6, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius); + TGeoTube* pipeSmallMo = new TGeoTube(0., kConicalBePipeEndOuterRadius, pipeSmallDz); + TGeoVolume* vopipeSmallMo = new TGeoVolume("voPipeSmallMo", pipeSmallMo, kMedVac); + vopipeSmallMo->SetLineColor(kGreen); + vopipeSmallMo->AddNode(vopipeSmall, 1, gGeoIdentity); - TGeoVolume* OuterPIPE = new TGeoVolume("pipeOut", pipeOut, kMedAlu2219); - barrel->AddNode(OuterPIPE, 1, new TGeoTranslation(0., 30., 0.)); + beamPipeAsideSection->AddNode(vopipeSmallMo, 1, new TGeoTranslation(0., 0., kFlangeZ + kFlangeWidth / 2. + pipeSmallDz)); - // The end of the barrel volume is at 714.6 cm, after that we start with RB24 volume - TGeoPcon* pipeOutVac = new TGeoPcon(0., 360., 2); - pipeOutVac->DefineSection(0, kFlangeZ + 13.6 - 0.08, 0, kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness); - pipeOutVac->DefineSection(1, 714.6, 0., kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness); + // ------------------ Outer pipe after flange -------------------- + // The end of the barrel volume is at 864.6 cm, after that we start with RB24 volume + Float_t pipeEndZ = 864.6; + Float_t pipeOutDz = (pipeEndZ - (kFlangeZ + 3. * dU)) / 2.; + TGeoTube* pipeOut = new TGeoTube(kConicalBePipeEndOuterRadius - kConicalBerilliumMaxThickness, kConicalBePipeEndOuterRadius, pipeOutDz); + TGeoVolume* OuterPIPE = new TGeoVolume("pipeOut", pipeOut, kMedAlu2219); - TGeoVolume* OuterPIPEVac = new TGeoVolume("pipeOutVac", pipeOutVac, kMedAlu2219); - barrel->AddNode(OuterPIPEVac, 1, new TGeoTranslation(0., 30., 0.)); + TGeoTube* pipeOutMo = new TGeoTube(0., kConicalBePipeEndOuterRadius, pipeOutDz); + TGeoVolume* OuterPIPEMo = new TGeoVolume("pipeOutMo", pipeOutMo, kMedVac); + OuterPIPEMo->AddNode(OuterPIPE, 1, gGeoIdentity); + beamPipeAsideSection->AddNode(OuterPIPEMo, 1, new TGeoTranslation(0., 0., pipeEndZ - pipeOutDz)); + barrel->AddNode(beamPipeAsideSection, 1, new TGeoTranslation(0., 30., 0.)); //------------------------------------------------- @@ -822,19 +810,19 @@ void PipeRun4::ConstructGeometry() // Copper Tube RB24/1 const float kRB24CuTubeL = 381.5; - const float kRB24cCuTubeL = 155.775 + (28.375 - 18.135); + const float kRB24cCuTubeL = 155.775 - 150.; const float kRB24bCuTubeL = kRB24CuTubeL - kRB24cCuTubeL; const float kRB24CuTubeRi = 5.8 / 2.; const float kRB24CuTubeRo = 6.0 / 2.; const float kRB24CuTubeFRo = 7.6; const float kRB24CuTubeFL = 1.86; - const float kRB24CL = 2. * 597.9; + const float kRB24CL = 2. * 597.9 - 150.; // // introduce cut at end of barrel 714.6m // // outside barrel - TGeoVolume* voRB24cCuTubeM = new TGeoVolume("voRB24cCuTubeM", new TGeoTube(0., kRB24CuTubeRi, kRB24cCuTubeL / 2.), kMedVacNFHC); + TGeoVolume* voRB24cCuTubeM = new TGeoVolume("voRB24cCuTubeM", new TGeoTube(0., kRB24CuTubeRo, kRB24cCuTubeL / 2.), kMedVacNFHC); TGeoVolume* voRB24cCuTube = new TGeoVolume("voRB24cCuTube", new TGeoTube(kRB24CuTubeRi, kRB24CuTubeRo, kRB24cCuTubeL / 2.), kMedAlu2219); voRB24cCuTubeM->AddNode(voRB24cCuTube, 1, gGeoIdentity); @@ -876,7 +864,7 @@ void PipeRun4::ConstructGeometry() const float kRB24B1PlieThickness = 0.015; // Plie thickness const float kRB24B1PlieRadius = - (kRB24B1BellowUndL + (2. * kRB24B1NumberOfPlies - 2.) * kRB24B1PlieThickness) / (4. * kRB24B1NumberOfPlies); + (kRB24B1BellowUndL + 2. * kRB24B1NumberOfPlies * kRB24B1PlieThickness) / (4. * kRB24B1NumberOfPlies + 2.); const float kRB24B1ProtTubeThickness = 0.02; // Thickness of the protection tube const float kRB24B1ProtTubeLength = 4.2; // Length of the protection tube @@ -892,7 +880,7 @@ void PipeRun4::ConstructGeometry() // // Bellow Section TGeoVolume* voRB24B1Bellow = makeBellow("RB24B1", kRB24B1NumberOfPlies, kRB24B1BellowRi, kRB24B1BellowRo, - kRB24B1BellowUndL, kRB24B1PlieRadius, kRB24B1PlieThickness); + kRB24B1PlieRadius, kRB24B1PlieThickness); voRB24B1Bellow->SetVisibility(0); float newRB24B1BellowUndL = 2 * (static_cast(voRB24B1Bellow->GetShape()))->GetDz(); @@ -2840,13 +2828,11 @@ TGeoPcon* PipeRun4::makeInsulationFromTemplate(TGeoPcon* shape) return insu; } -TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax, float dU, float rPlie, - float dPlie) +TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax, float rPlie, float dPlie) { // nc Number of convolution // rMin Inner radius of the bellow // rMax Outer radius of the bellow - // dU Undulation length // rPlie Plie radius // dPlie Plie thickness auto& matmgr = o2::base::MaterialManager::Instance(); @@ -2896,10 +2882,10 @@ TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax asWiggle->AddNode(voWiggleL, 1, new TGeoTranslation(0., 0., z0)); asWiggle->GetShape()->ComputeBBox(); // enforce recomputing of BBox // - float zBellowTot = nc * (static_cast(asWiggle->GetShape()))->GetDZ(); - TGeoVolume* voBellow = new TGeoVolume(fmt::format("{:s}BellowUS", ext).c_str(), new TGeoTube(rMin, rMax, zBellowTot), kMedVac); + float zBellowTot = nc * (2. * (static_cast(asWiggle->GetShape()))->GetDZ() - dPlie) + 2. * rPlie; + TGeoVolume* voBellow = new TGeoVolume(fmt::format("{:s}BellowUS", ext).c_str(), new TGeoTube(rMin, rMax, zBellowTot / 2.), kMedVac); // Positioning of the volumes - z0 = -dU / 2. + rPlie; + z0 = -zBellowTot / 2. + rPlie; voBellow->AddNode(voWiggleL, 2, new TGeoTranslation(0., 0., z0)); z0 += rPlie; float zsh = 4. * rPlie - 2. * dPlie; @@ -2907,6 +2893,7 @@ TGeoVolume* PipeRun4::makeBellow(const char* ext, int nc, float rMin, float rMax float zpos = z0 + iw * zsh; voBellow->AddNode(asWiggle, iw + 1, new TGeoTranslation(0., 0., zpos - dPlie)); } + return voBellow; } From 1f14cea91374253c3672bc55a988663474352f9c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 24 Sep 2025 21:34:38 +0200 Subject: [PATCH 42/99] TPC: Add option to write reduced clusterNative, removing rejected clusters --- .../DataCompression/GPUTPCCompression.h | 1 + .../GPUTPCCompressionKernels.cxx | 53 +++++++++-------- GPU/GPUTracking/DataTypes/GPUDataTypes.h | 1 + GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + GPU/GPUTracking/Global/GPUChainTracking.cxx | 8 +++ GPU/GPUTracking/Global/GPUChainTracking.h | 3 +- .../Global/GPUChainTrackingClusterizer.cxx | 8 +-- .../Global/GPUChainTrackingCompression.cxx | 58 +++++++++++++++++++ .../Global/GPUChainTrackingMerger.cxx | 4 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 3 + 10 files changed, 107 insertions(+), 33 deletions(-) diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompression.h b/GPU/GPUTracking/DataCompression/GPUTPCCompression.h index 52585b4c08b24..1dafffaeea043 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompression.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompression.h @@ -89,6 +89,7 @@ class GPUTPCCompression : public GPUProcessor void SetPointersCompressedClusters(void*& mem, T& c, uint32_t nClA, uint32_t nTr, uint32_t nClU, bool reducedClA); template GPUd() static void truncateSignificantBits(T& val, uint32_t nBits, uint32_t max); + GPUd() bool rejectCluster(int32_t idx, GPUParam& param, const GPUTrackingInOutPointers& ioPtrs); int16_t mMemoryResOutputHost = -1; int16_t mMemoryResOutputGPU = -1; diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx index 5503eeb30cdd6..d5567bb5148d9 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCCompressionKernels.cxx @@ -183,6 +183,31 @@ GPUd() bool GPUTPCCompressionKernels::GPUTPCCompressionKernels_Compare<4>::opera return mClsPtr[a].qTot < mClsPtr[b].qTot; } +GPUd() bool GPUTPCCompression::rejectCluster(int32_t idx, GPUParam& GPUrestrict() param, const GPUTrackingInOutPointers& GPUrestrict() ioPtrs) +{ + if (mClusterStatus[idx]) { + return true; + } + int32_t attach = ioPtrs.mergedTrackHitAttachment[idx]; + bool unattached = attach == 0; + + if (unattached) { + if (param.rec.tpc.rejectionStrategy >= GPUSettings::RejectionStrategyB) { + return true; + } + } else if (param.rec.tpc.rejectionStrategy >= GPUSettings::RejectionStrategyA) { + if (GPUTPCClusterRejection::GetIsRejected(attach)) { + return true; + } + int32_t id = attach & gputpcgmmergertypes::attachTrackMask; + auto& trk = ioPtrs.mergedTracks[id]; + if (CAMath::Abs(trk.GetParam().GetQPt() * param.qptB5Scaler) > param.rec.tpc.rejectQPtB5 || trk.MergedLooper()) { + return true; + } + } + return false; +} + template <> GPUdii() void GPUTPCCompressionKernels::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& GPUrestrict() processors) { @@ -208,33 +233,7 @@ GPUdii() void GPUTPCCompressionKernels::Thread(clusters->nClusters[iSector][iRow]); for (uint32_t i = iThread; i < nn + nThreads; i += nThreads) { const int32_t idx = idOffset + i; - int32_t storeCluster = 0; - do { - if (i >= clusters->nClusters[iSector][iRow]) { - break; - } - if (compressor.mClusterStatus[idx]) { - break; - } - int32_t attach = ioPtrs.mergedTrackHitAttachment[idx]; - bool unattached = attach == 0; - - if (unattached) { - if (processors.param.rec.tpc.rejectionStrategy >= GPUSettings::RejectionStrategyB) { - break; - } - } else if (processors.param.rec.tpc.rejectionStrategy >= GPUSettings::RejectionStrategyA) { - if (GPUTPCClusterRejection::GetIsRejected(attach)) { - break; - } - int32_t id = attach & gputpcgmmergertypes::attachTrackMask; - auto& trk = ioPtrs.mergedTracks[id]; - if (CAMath::Abs(trk.GetParam().GetQPt() * processors.param.qptB5Scaler) > processors.param.rec.tpc.rejectQPtB5 || trk.MergedLooper()) { - break; - } - } - storeCluster = 1; - } while (false); + int32_t storeCluster = i < clusters->nClusters[iSector][iRow] && !compressor.rejectCluster(idx, param, ioPtrs); GPUbarrier(); int32_t myIndex = work_group_scan_inclusive_add(storeCluster); diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypes.h b/GPU/GPUTracking/DataTypes/GPUDataTypes.h index 801c60f6b02ba..967d6a73914dd 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypes.h +++ b/GPU/GPUTracking/DataTypes/GPUDataTypes.h @@ -245,6 +245,7 @@ struct GPUTrackingInOutPointers { uint32_t nOutputClusRefsTPCO2 = 0; const o2::MCCompLabel* outputTracksTPCO2MC = nullptr; const o2::tpc::CompressedClustersFlat* tpcCompressedClusters = nullptr; + const o2::tpc::ClusterNativeAccess* clustersNativeReduced = nullptr; // TPC links int32_t* tpcLinkITS = nullptr; diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 8b5f70f25a4d9..30477d67fdc4f 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -361,6 +361,7 @@ AddOption(tpcMaxAttachedClustersPerSectorRow, uint32_t, 51000, "", 0, "Maximum n AddOption(tpcUseOldCPUDecoding, bool, false, "", 0, "Enable old CPU-based TPC decoding") AddOption(tpcApplyCFCutsAtDecoding, bool, false, "", 0, "Apply cluster cuts from clusterization during decoding of compressed clusters") AddOption(tpcApplyClusterFilterOnCPU, uint8_t, 0, "", 0, "Apply custom cluster filter of GPUTPCClusterFilter class, 0: off, 1: debug, 2: PbPb23") +AddOption(tpcWriteClustersAfterRejection, bool, false, "", 0, "Apply TPC rejection strategy before writing clusters") AddOption(oclPlatformNum, int32_t, -1, "", 0, "Platform to use, in case the backend provides multiple platforms (OpenCL only, -1 = auto-select, -2 query all platforms (also incompatible))") AddOption(oclCompileFromSources, bool, false, "", 0, "Compile OpenCL binary from included source code instead of using included spirv code") AddOption(oclOverrideSourceBuildFlags, std::string, "", "", 0, "Override OCL build flags for compilation from source, put a space for empty options") diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index f47c6923a6be7..91870f981d542 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -273,6 +273,10 @@ bool GPUChainTracking::ValidateSettings() GPUError("Clusterizer and merger Sanity checks only supported when not running on GPU"); return false; } + if (GetProcessingSettings().tpcWriteClustersAfterRejection && (mRec->IsGPU() || param().rec.tpc.compressionTypeMask || !(GetRecoSteps() & GPUDataTypes::RecoStep::TPCCompression))) { + GPUError("tpcWriteClustersAfterRejection requires compressionTypeMask = 0, no GPU usage, and compression enabled"); + return false; + } if (GetProcessingSettings().doublePipeline) { if (!GetRecoStepsOutputs().isOnlySet(GPUDataTypes::InOutType::TPCMergedTracks, GPUDataTypes::InOutType::TPCCompressedClusters, GPUDataTypes::InOutType::TPCClusters)) { GPUError("Invalid outputs for double pipeline mode 0x%x", (uint32_t)GetRecoStepsOutputs()); @@ -543,6 +547,10 @@ void GPUChainTracking::ClearIOPointers() std::memset((void*)&mIOPtrs, 0, sizeof(mIOPtrs)); mIOMem.~InOutMemory(); new (&mIOMem) InOutMemory; + mClusterNativeAccessReduced.reset(nullptr); + if (mClusterNativeAccess.get()) { + memset((void*)mClusterNativeAccess.get(), 0, sizeof(*mClusterNativeAccess)); + } } void GPUChainTracking::AllocateIOMemory() diff --git a/GPU/GPUTracking/Global/GPUChainTracking.h b/GPU/GPUTracking/Global/GPUChainTracking.h index 5c85147494711..8de49cc954e35 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -265,7 +265,7 @@ class GPUChainTracking : public GPUChain std::unique_ptr mTRDGeometryU; // TRD Geometry // Ptrs to internal buffers - std::unique_ptr mClusterNativeAccess; + std::unique_ptr mClusterNativeAccess, mClusterNativeAccessReduced; std::array mSubOutputControls = {nullptr}; std::unique_ptr mTriggerBuffer; @@ -305,6 +305,7 @@ class GPUChainTracking : public GPUChain void RunTPCTrackingMerger_Resolve(int8_t useOrigTrackParam, int8_t mergeAll, GPUReconstruction::krnlDeviceType deviceType); void RunTPCClusterFilter(o2::tpc::ClusterNativeAccess* clusters, std::function allocator, bool applyClusterCuts); bool NeedTPCClustersOnGPU(); + void WriteReducedClusters(); template int32_t RunTRDTrackingInternal(); uint32_t StreamForSector(uint32_t sector) const; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 619940ff6d3dd..b0d466f13e5ef 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -772,7 +772,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) GPUFatal("Cannot use waitForFinalInput callback without delayed output"); } if (!GetProcessingSettings().tpcApplyClusterFilterOnCPU) { - AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); + AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, GetProcessingSettings().tpcWriteClustersAfterRejection ? nullptr : mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); tmpNativeClusters = mInputsHost->mPclusterNativeOutput; } else { tmpNativeClusterBuffer = std::make_unique(mInputsHost->mNClusterNative); @@ -1269,7 +1269,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) // TODO: write to buffer directly o2::dataformats::MCTruthContainer mcLabels; std::pair buffer; - if (mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)] && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]->useExternal()) { + if (!GetProcessingSettings().tpcWriteClustersAfterRejection && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)] && mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]->useExternal()) { if (!mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]->allocator) { throw std::runtime_error("Cluster MC Label buffer missing"); } @@ -1293,7 +1293,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (buildNativeHost && buildNativeGPU && GetProcessingSettings().delayedOutput) { mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = nClsTotal; - AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); + AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, GetProcessingSettings().tpcWriteClustersAfterRejection ? nullptr : mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); tmpNativeClusters = mInputsHost->mPclusterNativeOutput; for (uint32_t i = outputQueueStart; i < mOutputQueue.size(); i++) { mOutputQueue[i].dst = (char*)tmpNativeClusters + (size_t)mOutputQueue[i].dst; @@ -1308,7 +1308,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (GetProcessingSettings().tpcApplyClusterFilterOnCPU) { auto allocator = [this, &tmpNativeClusters](size_t size) { this->mInputsHost->mNClusterNative = size; - this->AllocateRegisteredMemory(this->mInputsHost->mResourceClusterNativeOutput, this->mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); + this->AllocateRegisteredMemory(this->mInputsHost->mResourceClusterNativeOutput, this->GetProcessingSettings().tpcWriteClustersAfterRejection ? nullptr : this->mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); return (tmpNativeClusters = this->mInputsHost->mPclusterNativeOutput); }; RunTPCClusterFilter(tmpNativeAccess, allocator, false); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index 3bcd2390eae52..e06699c0918b8 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -23,6 +23,8 @@ #include "GPUConstantMem.h" // TODO: Try to get rid of as many GPUConstantMem includes as possible! #include "GPUTPCCompressionKernels.h" #include "GPUTPCDecompressionKernels.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" #include "utils/strtag.h" #include @@ -52,6 +54,9 @@ int32_t GPUChainTracking::RunTPCCompression() TransferMemoryResourcesToGPU(myStep, &Compressor, 0); runKernel(GetGridAutoStep(0, RecoStep::TPCCompression), CompressorShadow.mClusterStatus, Compressor.mMaxClusters * sizeof(CompressorShadow.mClusterStatus[0])); runKernel(GetGridAuto(0)); + if (GetProcessingSettings().tpcWriteClustersAfterRejection) { + WriteReducedClusters(); + } runKernel(GetGridAuto(0)); TransferMemoryResourcesToHost(myStep, &Compressor, 0); #ifdef GPUCA_TPC_GEOMETRY_O2 @@ -434,3 +439,56 @@ int32_t GPUChainTracking::RunTPCDecompression() DoDebugDump(GPUChainTrackingDebugFlags::TPCDecompressedClusters, &GPUChainTracking::DumpClusters, *mDebugFile, mIOPtrs.clustersNative); return 0; } + +void GPUChainTracking::WriteReducedClusters() +{ + GPUTPCCompression& Compressor = processors()->tpcCompressor; + mClusterNativeAccessReduced = std::make_unique(); + uint32_t nOutput = 0; + for (uint32_t iSec = 0; iSec < GPUCA_NSECTORS; iSec++) { + for (uint32_t iRow = 0; iRow < GPUCA_ROW_COUNT; iRow++) { + mClusterNativeAccessReduced->nClusters[iSec][iRow] = 0; + for (uint32_t i = 0; i < mIOPtrs.clustersNative->nClusters[iSec][iRow]; i++) { + mClusterNativeAccessReduced->nClusters[iSec][iRow] += !Compressor.rejectCluster(mIOPtrs.clustersNative->clusterOffset[iSec][iRow] + i, param(), mIOPtrs); + } + nOutput += mClusterNativeAccessReduced->nClusters[iSec][iRow]; + } + } + + GPUOutputControl* clOutput = mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]; + if (!clOutput || !clOutput->allocator) { + throw std::runtime_error("No output allocator for clusterNative available"); + } + auto* clBuffer = (ClusterNative*)clOutput->allocator(nOutput * sizeof(ClusterNative)); + mClusterNativeAccessReduced->clustersLinear = clBuffer; + mClusterNativeAccessReduced->setOffsetPtrs(); + + std::pair labelBuffer; + if (mIOPtrs.clustersNative->clustersMCTruth) { + GPUOutputControl* labelOutput = mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)]; + if (!labelOutput || !labelOutput->allocator) { + throw std::runtime_error("No output allocator for clusterNative labels available"); + } + ClusterNativeAccess::ConstMCLabelContainerViewWithBuffer* labelContainer = reinterpret_cast(labelOutput->allocator(0)); + labelBuffer = {&labelContainer->first, &labelContainer->second}; + } + + nOutput = 0; + o2::dataformats::MCLabelContainer tmpContainer; + for (uint32_t i = 0; i < mIOPtrs.clustersNative->nClustersTotal; i++) { + if (!Compressor.rejectCluster(i, param(), mIOPtrs)) { + if (mIOPtrs.clustersNative->clustersMCTruth) { + for (const auto& element : mIOPtrs.clustersNative->clustersMCTruth->getLabels(i)) { + tmpContainer.addElement(nOutput, element); + } + } + clBuffer[nOutput++] = mIOPtrs.clustersNative->clustersLinear[i]; + } + } + mIOPtrs.clustersNativeReduced = mClusterNativeAccessReduced.get(); + if (mIOPtrs.clustersNative->clustersMCTruth) { + tmpContainer.flatten_to(*labelBuffer.first); + *labelBuffer.second = *labelBuffer.first; + mClusterNativeAccessReduced->clustersMCTruth = labelBuffer.second; + } +} diff --git a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx index a9d4304d77c83..5ab4b963d7330 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx @@ -293,7 +293,9 @@ int32_t GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) mRec->PushNonPersistentMemory(qStr2Tag("TPCMERG2")); AllocateRegisteredMemory(Merger.MemoryResOutputO2Scratch()); WriteToConstantMemory(RecoStep::TPCMerging, (char*)&processors()->tpcMerger - (char*)processors(), &MergerShadow, sizeof(MergerShadow), 0); - runKernel(GetGridAuto(0, deviceType)); + if (!GetProcessingSettings().tpcWriteClustersAfterRejection) { + runKernel(GetGridAuto(0, deviceType)); + } TransferMemoryResourceLinkToHost(RecoStep::TPCMerging, Merger.MemoryResMemory(), 0, &mEvents->single); runKernel(GetGridAuto(0, deviceType)); mRec->ReturnVolatileDeviceMemory(); diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 68f7be8fb6330..353af32558c7d 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -889,6 +889,9 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) // ------------------------------ Varios postprocessing steps ------------------------------ + if (mConfig->configProcessing.tpcWriteClustersAfterRejection) { + ptrs.clustersNative = ptrs.clustersNativeReduced; + } bool createEmptyOutput = false; if (retVal != 0) { if (retVal == 3 && mConfig->configProcessing.ignoreNonFatalGPUErrors) { From 3f5adb66f5ad56770cc36ea82f2e543f982bc316 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 4 Oct 2025 22:27:44 +0200 Subject: [PATCH 43/99] TPC Workflow: Clean up CompCluster options --- .../include/TPCWorkflow/RecoWorkflow.h | 21 +++++------ Detectors/TPC/workflow/src/RecoWorkflow.cxx | 35 +++++++++++-------- .../TPC/workflow/src/tpc-reco-workflow.cxx | 4 +-- .../include/GPUWorkflow/GPUWorkflowSpec.h | 2 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 8 ++--- GPU/Workflow/src/gpu-reco-workflow.cxx | 17 ++++----- prodtests/full-system-test/dpl-workflow.sh | 6 ++-- 7 files changed, 51 insertions(+), 42 deletions(-) diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h index f86afc310b04c..a5368f451a820 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h @@ -35,15 +35,15 @@ struct CorrectionMapsLoaderGloOpts; namespace reco_workflow { /// define input and output types of the workflow -enum struct InputType { PassThrough, // No processing, just pass through available inputs to the writers, defined by the OutputType - Digitizer, // directly read digits from channel {TPC:DIGITS} - Digits, // read digits from file - ClustersHardware, // read hardware clusters in raw page format from file - Clusters, // read native clusters from file - CompClusters, // read compressed cluster container - CompClustersCTF, // compressed clusters from CTF, as flat format - CompClustersFlat, // compressed clusters in flat format, used as input for the entropy encoder - EncodedClusters, // read encoded clusters +enum struct InputType { PassThrough, // No processing, just pass through available inputs to the writers, defined by the OutputType + Digitizer, // directly read digits from channel {TPC:DIGITS} + Digits, // read digits from file + ClustersHardware, // read hardware clusters in raw page format from file + Clusters, // read native clusters from file + CompClustersRoot, // read compressed cluster in ROOT format + CompClustersFlat, // compressed clusters from flat format (e.g. from CTF) + CompClustersFlatForEncode, // compressed clusters in flat format, used as input for the entropy encoder, no gpu-reco + EncodedClusters, // read encoded clusters ZSRaw, }; @@ -59,7 +59,8 @@ enum struct OutputType { Digits, ClustersHardware, Clusters, Tracks, - CompClusters, + CompClustersRoot, + CompClustersFlat, EncodedClusters, DisableWriter, SendClustersPerSector, diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 3e35f416373a1..e4969700bdf5d 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -80,16 +80,17 @@ const std::unordered_map InputMap{ {"clustershardware", InputType::ClustersHardware}, {"clusters", InputType::Clusters}, {"zsraw", InputType::ZSRaw}, - {"compressed-clusters", InputType::CompClusters}, - {"compressed-clusters-ctf", InputType::CompClustersCTF}, - {"compressed-clusters-flat", InputType::CompClustersFlat}}; + {"compressed-clusters-root", InputType::CompClustersRoot}, + {"compressed-clusters-flat", InputType::CompClustersFlat}, + {"compressed-clusters-flat-for-encode", InputType::CompClustersFlatForEncode}}; const std::unordered_map OutputMap{ {"digits", OutputType::Digits}, {"clustershardware", OutputType::ClustersHardware}, {"clusters", OutputType::Clusters}, {"tracks", OutputType::Tracks}, - {"compressed-clusters", OutputType::CompClusters}, + {"compressed-clusters-root", OutputType::CompClustersRoot}, + {"compressed-clusters-flat", OutputType::CompClustersFlat}, {"encoded-clusters", OutputType::EncodedClusters}, {"disable-writer", OutputType::DisableWriter}, {"send-clusters-per-sector", OutputType::SendClustersPerSector}, @@ -122,14 +123,19 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto throw std::invalid_argument("filtered-input option must be provided only with pass-through input and clusters,tracks,send-clusters-per-sector output"); } - bool decompressTPC = inputType == InputType::CompClustersCTF || inputType == InputType::CompClusters; + bool decompressTPC = inputType == InputType::CompClustersFlat || inputType == InputType::CompClustersRoot; // Disable not applicable settings depending on TPC input, no need to disable manually if (decompressTPC && (isEnabled(OutputType::Clusters) || isEnabled(OutputType::Tracks))) { caClusterer = false; zsOnTheFly = false; propagateMC = false; } - if (inputType == InputType::ZSRaw || inputType == InputType::CompClustersFlat) { + if (inputType == InputType::CompClustersFlatForEncode || inputType == InputType::CompClustersRoot || inputType == InputType::CompClustersFlat) { + caClusterer = false; + zsOnTheFly = false; + propagateMC = false; + } + if (inputType == InputType::ZSRaw) { caClusterer = true; zsOnTheFly = false; propagateMC = false; @@ -225,7 +231,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto if (sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader specs.emplace_back(o2::ctp::getDigitsReaderSpec(false)); } - } else if (inputType == InputType::CompClusters) { + } else if (inputType == InputType::CompClustersRoot) { // TODO: need to check if we want to store the MC labels alongside with compressed clusters // for the moment reading of labels is disabled (last parameter is false) // TODO: make a different publisher spec for only one output spec, for now using the @@ -248,8 +254,9 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // output matrix // Note: the ClusterHardware format is probably a deprecated legacy format and also the // ClusterDecoderRawSpec - bool produceCompClusters = isEnabled(OutputType::CompClusters); - bool runGPUReco = (produceTracks || produceCompClusters || (isEnabled(OutputType::Clusters) && caClusterer) || inputType == InputType::CompClustersCTF) && inputType != InputType::CompClustersFlat; + bool produceCompClustersRoot = isEnabled(OutputType::CompClustersRoot); + bool produceCompClustersFlat = isEnabled(OutputType::CompClustersFlat); + bool runGPUReco = (produceTracks || produceCompClustersRoot || produceCompClustersFlat || (isEnabled(OutputType::Clusters) && caClusterer) || inputType == InputType::CompClustersFlat) && inputType != InputType::CompClustersFlatForEncode; bool runHWDecoder = !caClusterer && (runGPUReco || isEnabled(OutputType::Clusters)); bool runClusterer = !caClusterer && (runHWDecoder || isEnabled(OutputType::ClustersHardware)); bool zsDecoder = inputType == InputType::ZSRaw; @@ -460,13 +467,13 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto cfg.enableMShape = sclOpts.enableMShapeCorrection; cfg.enableCTPLumi = sclOpts.requestCTPLumi; cfg.decompressTPC = decompressTPC; - cfg.decompressTPCFromROOT = decompressTPC && inputType == InputType::CompClusters; + cfg.decompressTPCFromROOT = decompressTPC && inputType == InputType::CompClustersRoot; cfg.caClusterer = caClusterer; cfg.zsDecoder = zsDecoder; cfg.zsOnTheFly = zsOnTheFly; cfg.outputTracks = produceTracks; - cfg.outputCompClusters = produceCompClusters; - cfg.outputCompClustersFlat = runClusterEncoder; + cfg.outputCompClustersRoot = produceCompClustersRoot; + cfg.outputCompClustersFlat = produceCompClustersFlat || runClusterEncoder; cfg.outputCAClusters = isEnabled(OutputType::Clusters) && (caClusterer || decompressTPC); cfg.outputQA = isEnabled(OutputType::QA); cfg.outputSharedClusterMap = (isEnabled(OutputType::Clusters) || inputType == InputType::Clusters) && isEnabled(OutputType::Tracks) && !isEnabled(OutputType::NoSharedClusterMap); @@ -500,7 +507,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // // selected by output type 'encoded-clusters' if (runClusterEncoder) { - specs.emplace_back(o2::tpc::getEntropyEncoderSpec(!runGPUReco && inputType != InputType::CompClustersFlat, selIR)); + specs.emplace_back(o2::tpc::getEntropyEncoderSpec(!runGPUReco && inputType != InputType::CompClustersFlatForEncode, selIR)); } ////////////////////////////////////////////////////////////////////////////////////////////// @@ -547,7 +554,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // a writer process for compressed clusters container // // selected by output type 'compressed-clusters' - if (produceCompClusters && !isEnabled(OutputType::DisableWriter)) { + if (produceCompClustersRoot && !isEnabled(OutputType::DisableWriter)) { // defining the track writer process using the generic RootTreeWriter and generator tool // // defaults diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 9d7ab63b0c2a0..efb78c248e98c 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -57,8 +57,8 @@ void customize(std::vector& workflowOptions) using namespace o2::framework; std::vector options{ - {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clusters, compressed-clusters, compressed-clusters-ctf, pass-through"}}, - {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clusters, tracks, compressed-clusters, encoded-clusters, disable-writer, send-clusters-per-sector, qa, no-shared-cluster-map, tpc-triggers"}}, + {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clusters, compressed-clusters-root, compressed-clusters-ctf, compressed-clusters-flat-for-encode, pass-through"}}, + {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clusters, tracks, compressed-clusters-root, compressed-clusters-flat, encoded-clusters, disable-writer, send-clusters-per-sector, qa, no-shared-cluster-map, tpc-triggers"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"no-ca-clusterer", VariantType::Bool, false, {"Use HardwareClusterer instead of clusterer of GPUCATracking"}}, {"disable-mc", VariantType::Bool, false, {"disable sending of MC information"}}, diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index ad424cc226499..8be69c2a0fd7c 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -117,7 +117,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool zsDecoder = false; bool zsOnTheFly = false; bool outputTracks = false; - bool outputCompClusters = false; + bool outputCompClustersRoot = false; bool outputCompClustersFlat = false; bool outputCAClusters = false; bool outputQA = false; diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 353af32558c7d..be5b182dad99f 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -236,14 +236,14 @@ void GPURecoWorkflowSpec::init(InitContext& ic) } // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) - if (mSpecConfig.outputTracks || mSpecConfig.outputCompClusters || mSpecConfig.outputCompClustersFlat) { + if (mSpecConfig.outputTracks || mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat) { mConfig->configWorkflow.steps.set(GPUDataTypes::RecoStep::TPCConversion, GPUDataTypes::RecoStep::TPCSectorTracking, GPUDataTypes::RecoStep::TPCMerging); mConfig->configWorkflow.outputs.set(GPUDataTypes::InOutType::TPCMergedTracks); mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, mConfParam->rundEdx == -1 ? !mConfParam->synchronousProcessing : mConfParam->rundEdx); } - if (mSpecConfig.outputCompClusters || mSpecConfig.outputCompClustersFlat) { + if (mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat) { mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, true); mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, true); } @@ -966,7 +966,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) LOG(info) << "found " << ptrs.nOutputTracksTPCO2 << " track(s)"; } - if (mSpecConfig.outputCompClusters) { + if (mSpecConfig.outputCompClustersRoot) { o2::tpc::CompressedClustersROOT compressedClusters = *ptrs.tpcCompressedClusters; pc.outputs().snapshot(Output{gDataOriginTPC, "COMPCLUSTERS", 0}, ROOTSerialized(compressedClusters)); } @@ -1259,7 +1259,7 @@ Outputs GPURecoWorkflowSpec::outputs() if (mSpecConfig.processMC && mSpecConfig.outputTracks) { outputSpecs.emplace_back(gDataOriginTPC, "TRACKSMCLBL", 0, Lifetime::Timeframe); } - if (mSpecConfig.outputCompClusters) { + if (mSpecConfig.outputCompClustersRoot) { outputSpecs.emplace_back(gDataOriginTPC, "COMPCLUSTERS", 0, Lifetime::Timeframe); } if (mSpecConfig.outputCompClustersFlat) { diff --git a/GPU/Workflow/src/gpu-reco-workflow.cxx b/GPU/Workflow/src/gpu-reco-workflow.cxx index 561d537b5c251..5ae31554c173d 100644 --- a/GPU/Workflow/src/gpu-reco-workflow.cxx +++ b/GPU/Workflow/src/gpu-reco-workflow.cxx @@ -51,8 +51,8 @@ void customize(std::vector& workflowOptions) { std::vector options{ - {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, zsonthefly, clustersnative, compressed-clusters-root, compressed-clusters-ctf, trd-tracklets, its-clusters"}}, - {"output-type", VariantType::String, "tracks", {"clustersnative, tracks, compressed-clusters-ctf, qa, no-shared-cluster-map, send-clusters-per-sector, trd-tracks, error-qa, tpc-triggers, its-tracks"}}, + {"input-type", VariantType::String, "digits", {"digits, zsraw, zsonthefly, clusters, compressed-clusters-root, compressed-clusters-flat, trd-tracklets, its-clusters, its-mean-vertex"}}, + {"output-type", VariantType::String, "tracks", {"cluster, tracks, compressed-clusters-root, compressed-clusters-flat, qa, error-qa, no-shared-cluster-map, send-clusters-per-sector, trd-tracks, tpc-triggers, its-tracks"}}, {"corrmap-lumi-mode", VariantType::Int, 0, {"scaling mode: (default) 0 = static + scale * full; 1 = full + scale * derivative"}}, {"disable-root-input", VariantType::Bool, true, {"disable root-files input reader"}}, {"disable-mc", VariantType::Bool, false, {"disable sending of MC information"}}, @@ -98,7 +98,7 @@ enum struct ioType { Digits, ZSRaw, ZSRawOTF, CompClustROOT, - CompClustCTF, + CompClustFlat, Tracks, QA, ErrorQA, @@ -117,7 +117,7 @@ static const std::unordered_map InputMap{ {"zsraw", ioType::ZSRaw}, {"zsonthefly", ioType::ZSRawOTF}, {"compressed-clusters-root", ioType::CompClustROOT}, - {"compressed-clusters-ctf", ioType::CompClustCTF}, + {"compressed-clusters-flat", ioType::CompClustFlat}, {"trd-tracklets", ioType::TRDTracklets}, {"its-clusters", ioType::ITSClusters}, {"its-mean-vertex", ioType::MeanVertex}, @@ -126,7 +126,8 @@ static const std::unordered_map InputMap{ static const std::unordered_map OutputMap{ {"clusters", ioType::Clusters}, {"tracks", ioType::Tracks}, - {"compressed-clusters-ctf", ioType::CompClustCTF}, + {"compressed-clusters-flat", ioType::CompClustFlat}, + {"compressed-clusters-root", ioType::CompClustROOT}, {"qa", ioType::QA}, {"error-qa", ioType::ErrorQA}, {"no-shared-cluster-map", ioType::NoSharedMap}, @@ -167,13 +168,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) cfg.enableMShape = sclOpt.enableMShapeCorrection; cfg.enableCTPLumi = sclOpt.requestCTPLumi; cfg.decompressTPCFromROOT = isEnabled(inputTypes, ioType::CompClustROOT); - cfg.decompressTPC = isEnabled(inputTypes, ioType::CompClustCTF) || cfg.decompressTPCFromROOT; + cfg.decompressTPC = isEnabled(inputTypes, ioType::CompClustFlat) || cfg.decompressTPCFromROOT; cfg.zsDecoder = isEnabled(inputTypes, ioType::ZSRaw); cfg.zsOnTheFly = isEnabled(inputTypes, ioType::ZSRawOTF); cfg.caClusterer = cfg.zsDecoder || cfg.zsOnTheFly || isEnabled(inputTypes, ioType::Digits); cfg.outputTracks = isEnabled(outputTypes, ioType::Tracks); - cfg.outputCompClusters = isEnabled(outputTypes, ioType::CompClustROOT); - cfg.outputCompClustersFlat = isEnabled(outputTypes, ioType::CompClustCTF); + cfg.outputCompClustersRoot = isEnabled(outputTypes, ioType::CompClustROOT); + cfg.outputCompClustersFlat = isEnabled(outputTypes, ioType::CompClustFlat); cfg.outputCAClusters = isEnabled(outputTypes, ioType::Clusters); cfg.outputQA = isEnabled(outputTypes, ioType::QA); cfg.outputErrorQA = isEnabled(outputTypes, ioType::ErrorQA); diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 9fc6ce5507168..2f0e761366e18 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -205,7 +205,7 @@ workflow_has_parameter CALIB && [[ $CALIB_TPC_VDRIFTTGL == 1 ]] && SEND_ITSTPC_D PVERTEXING_CONFIG_KEY+="${ITSMFT_STROBES};" -has_processing_step ENTROPY_ENCODER && has_detector_ctf TPC && GPU_OUTPUT+=",compressed-clusters-ctf" +has_processing_step ENTROPY_ENCODER && has_detector_ctf TPC && GPU_OUTPUT+=",compressed-clusters-flat" if [[ $SYNCMODE == 1 ]] && workflow_has_parameter QC && has_detector_qc TPC; then GPU_OUTPUT+=",qa,error-qa" @@ -443,7 +443,7 @@ fi if [[ -n $INPUT_DETECTOR_LIST ]]; then if [[ $CTFINPUT == 1 ]]; then - GPU_INPUT=compressed-clusters-ctf + GPU_INPUT=compressed-clusters-flat TOF_INPUT=digits CTFName=`ls -t $RAWINPUTDIR/o2_ctf_*.root 2> /dev/null | head -n1` [[ -z $CTFName && $WORKFLOWMODE == "print" ]] && CTFName='$CTFName' @@ -652,7 +652,7 @@ if has_processing_step ENTROPY_ENCODER && [[ -n "$WORKFLOW_DETECTORS_CTF" ]] && has_detector_ctf TOF && add_W o2-tof-entropy-encoder-workflow "$RANS_OPT --mem-factor ${TOF_ENC_MEMFACT:-1.5} --pipeline $(get_N tof-entropy-encoder TOF CTF 1)" has_detector_ctf ITS && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${ITS_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder ITS CTF 1)" has_detector_ctf TRD && add_W o2-trd-entropy-encoder-workflow "$RANS_OPT --mem-factor ${TRD_ENC_MEMFACT:-1.5} --pipeline $(get_N trd-entropy-encoder TRD CTF 1 TRDENT)" - has_detector_ctf TPC && add_W o2-tpc-reco-workflow " $RANS_OPT --mem-factor ${TPC_ENC_MEMFACT:-1.} --input-type compressed-clusters-flat --output-type encoded-clusters,disable-writer --pipeline $(get_N tpc-entropy-encoder TPC CTF 1 TPCENT)" + has_detector_ctf TPC && add_W o2-tpc-reco-workflow " $RANS_OPT --mem-factor ${TPC_ENC_MEMFACT:-1.} --input-type compressed-clusters-flat-for-encode --output-type encoded-clusters,disable-writer --pipeline $(get_N tpc-entropy-encoder TPC CTF 1 TPCENT)" has_detector_ctf CTP && add_W o2-ctp-entropy-encoder-workflow "$RANS_OPT --mem-factor ${CTP_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder CTP CTF 1)" if [[ $CREATECTFDICT == 1 && $WORKFLOWMODE == "run" ]] ; then From f8ca3cf9fb2b00b25462e59bda2ca0b3bb833151 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 4 Oct 2025 22:37:36 +0200 Subject: [PATCH 44/99] TPC Workflow: Remove dispatch trigger for invalid input type (probably obsolete) --- Detectors/TPC/workflow/src/tpc-reco-workflow.cxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index efb78c248e98c..b440f6e8d359f 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -155,8 +155,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "DIGITS"}; } else if (inputType == "clustershw") { gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "CLUSTERHW"}; - } else if (inputType == "clustersnative") { - gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}; } else if (inputType == "zsraw") { gDispatchTrigger = o2::framework::ConcreteDataTypeMatcher{"TPC", "RAWDATA"}; } From f6202a90cae15843b1159efcdf08f07ad87d01cf Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 4 Oct 2025 22:57:58 +0200 Subject: [PATCH 45/99] GPU Workfow: Add option to use filtered output specs --- Detectors/TPC/workflow/src/RecoWorkflow.cxx | 4 +-- .../include/GPUWorkflow/GPUWorkflowSpec.h | 1 + GPU/Workflow/src/GPUWorkflowSpec.cxx | 36 +++++++++---------- GPU/Workflow/src/gpu-reco-workflow.cxx | 2 ++ 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index e4969700bdf5d..3fdff02dd69cc 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -119,8 +119,8 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto return std::find(outputTypes.begin(), outputTypes.end(), type) != outputTypes.end(); }; - if (filteredInp && !(inputType == InputType::PassThrough && isEnabled(OutputType::Tracks) && isEnabled(OutputType::Clusters) && isEnabled(OutputType::SendClustersPerSector))) { - throw std::invalid_argument("filtered-input option must be provided only with pass-through input and clusters,tracks,send-clusters-per-sector output"); + if (filteredInp && !(inputType == InputType::PassThrough)) { + throw std::invalid_argument("filtered-input option must be provided only with pass-through input"); } bool decompressTPC = inputType == InputType::CompClustersFlat || inputType == InputType::CompClustersRoot; diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index 8be69c2a0fd7c..4f62f07593bff 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -134,6 +134,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool itsOverrBeamEst = false; bool tpcTriggerHandling = false; bool isITS3 = false; + bool useFilteredOutputSpecs = false; }; GPURecoWorkflowSpec(CompletionPolicyData* policyData, Config const& specconfig, std::vector const& tpcsectors, uint64_t tpcSectorMask, std::shared_ptr& ggr, std::function** gPolicyOrder = nullptr); diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index be5b182dad99f..6c76f13c9bbd0 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -112,8 +112,8 @@ namespace o2::gpu GPURecoWorkflowSpec::GPURecoWorkflowSpec(GPURecoWorkflowSpec::CompletionPolicyData* policyData, Config const& specconfig, std::vector const& tpcsectors, uint64_t tpcSectorMask, std::shared_ptr& ggr, std::function** gPolicyOrder) : o2::framework::Task(), mPolicyData(policyData), mTPCSectorMask(tpcSectorMask), mTPCSectors(tpcsectors), mSpecConfig(specconfig), mGGR(ggr) { - if (mSpecConfig.outputCAClusters && !mSpecConfig.caClusterer && !mSpecConfig.decompressTPC) { - throw std::runtime_error("inconsistent configuration: cluster output is only possible if CA clusterer is activated"); + if (mSpecConfig.outputCAClusters && !mSpecConfig.caClusterer && !mSpecConfig.decompressTPC && !mSpecConfig.useFilteredOutputSpecs) { + throw std::runtime_error("inconsistent configuration: cluster output is only possible if CA clusterer or CompCluster decompression is activated"); } mConfig.reset(new GPUO2InterfaceConfiguration); @@ -800,15 +800,15 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) }; setOutputAllocator("COMPCLUSTERSFLAT", mSpecConfig.outputCompClustersFlat, outputRegions.compressedClusters, std::make_tuple(gDataOriginTPC, (DataDescription) "COMPCLUSTERSFLAT", 0)); - setOutputAllocator("CLUSTERNATIVE", mClusterOutputIds.size() > 0, outputRegions.clustersNative, std::make_tuple(gDataOriginTPC, mSpecConfig.sendClustersPerSector ? (DataDescription) "CLUSTERNATIVETMP" : (DataDescription) "CLUSTERNATIVE", NSectors, clusterOutputSectorHeader), sizeof(o2::tpc::ClusterCountIndex)); + setOutputAllocator("CLUSTERNATIVE", mClusterOutputIds.size() > 0, outputRegions.clustersNative, std::make_tuple(gDataOriginTPC, mSpecConfig.sendClustersPerSector ? (DataDescription) "CLUSTERNATIVETMP" : (mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "CLUSTERNATIVEF" : (DataDescription) "CLUSTERNATIVE"), NSectors, clusterOutputSectorHeader), sizeof(o2::tpc::ClusterCountIndex)); setOutputAllocator("CLSHAREDMAP", mSpecConfig.outputSharedClusterMap, outputRegions.sharedClusterMap, std::make_tuple(gDataOriginTPC, (DataDescription) "CLSHAREDMAP", 0)); setOutputAllocator("TPCOCCUPANCYMAP", mSpecConfig.outputSharedClusterMap, outputRegions.tpcOccupancyMap, std::make_tuple(gDataOriginTPC, (DataDescription) "TPCOCCUPANCYMAP", 0)); - setOutputAllocator("TRACKS", mSpecConfig.outputTracks, outputRegions.tpcTracksO2, std::make_tuple(gDataOriginTPC, (DataDescription) "TRACKS", 0)); - setOutputAllocator("CLUSREFS", mSpecConfig.outputTracks, outputRegions.tpcTracksO2ClusRefs, std::make_tuple(gDataOriginTPC, (DataDescription) "CLUSREFS", 0)); - setOutputAllocator("TRACKSMCLBL", mSpecConfig.outputTracks && mSpecConfig.processMC, outputRegions.tpcTracksO2Labels, std::make_tuple(gDataOriginTPC, (DataDescription) "TRACKSMCLBL", 0)); + setOutputAllocator("TRACKS", mSpecConfig.outputTracks, outputRegions.tpcTracksO2, std::make_tuple(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "TRACKSF" : (DataDescription) "TRACKS", 0)); + setOutputAllocator("CLUSREFS", mSpecConfig.outputTracks, outputRegions.tpcTracksO2ClusRefs, std::make_tuple(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "CLUSREFSF" : (DataDescription) "CLUSREFS", 0)); + setOutputAllocator("TRACKSMCLBL", mSpecConfig.outputTracks && mSpecConfig.processMC, outputRegions.tpcTracksO2Labels, std::make_tuple(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "TRACKSMCLBLF" : (DataDescription) "TRACKSMCLBL", 0)); setOutputAllocator("TRIGGERWORDS", mSpecConfig.caClusterer && mConfig->configProcessing.param.tpcTriggerHandling, outputRegions.tpcTriggerWords, std::make_tuple(gDataOriginTPC, (DataDescription) "TRIGGERWORDS", 0)); o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer clustersMCBuffer; - if (mSpecConfig.processMC && mSpecConfig.caClusterer) { + if (mSpecConfig.processMC && (mSpecConfig.caClusterer || mSpecConfig.useFilteredOutputSpecs)) { outputRegions.clusterLabels.allocator = [&clustersMCBuffer](size_t size) -> void* { return &clustersMCBuffer; }; } @@ -979,7 +979,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) if (mTPCSectorMask & (1ul << i)) { DataHeader::SubSpecificationType subspec = i; clusterOutputSectorHeader.sectorBits = (1ul << i); - char* buffer = pc.outputs().make({gDataOriginTPC, "CLUSTERNATIVE", subspec, {clusterOutputSectorHeader}}, accessIndex.nClustersSector[i] * sizeof(*accessIndex.clustersLinear) + sizeof(o2::tpc::ClusterCountIndex)).data(); + char* buffer = pc.outputs().make({gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "CLUSTERNATIVEF" : (DataDescription) "CLUSTERNATIVE", subspec, {clusterOutputSectorHeader}}, accessIndex.nClustersSector[i] * sizeof(*accessIndex.clustersLinear) + sizeof(o2::tpc::ClusterCountIndex)).data(); o2::tpc::ClusterCountIndex* outIndex = reinterpret_cast(buffer); memset(outIndex, 0, sizeof(*outIndex)); for (int32_t j = 0; j < o2::tpc::constants::MAXGLOBALPADROW; j++) { @@ -996,7 +996,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } ConstMCLabelContainer contflat; cont.flatten_to(contflat); - pc.outputs().snapshot({gDataOriginTPC, "CLNATIVEMCLBL", subspec, {clusterOutputSectorHeader}}, contflat); + pc.outputs().snapshot({gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? DataDescription("CLNATIVEMCLBLF") : DataDescription("CLNATIVEMCLBL"), subspec, {clusterOutputSectorHeader}}, contflat); } } } @@ -1006,8 +1006,8 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) o2::tpc::ClusterCountIndex* outIndex = reinterpret_cast(outputBuffers[outputRegions.getIndex(outputRegions.clustersNative)].second); static_assert(sizeof(o2::tpc::ClusterCountIndex) == sizeof(accessIndex.nClusters)); memcpy(outIndex, &accessIndex.nClusters[0][0], sizeof(o2::tpc::ClusterCountIndex)); - if (mSpecConfig.processMC && mSpecConfig.caClusterer && accessIndex.clustersMCTruth) { - pc.outputs().snapshot({gDataOriginTPC, "CLNATIVEMCLBL", subspec, {clusterOutputSectorHeader}}, clustersMCBuffer.first); + if (mSpecConfig.processMC && (mSpecConfig.caClusterer || mSpecConfig.useFilteredOutputSpecs) && accessIndex.clustersMCTruth) { + pc.outputs().snapshot({gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? DataDescription("CLNATIVEMCLBLF") : DataDescription("CLNATIVEMCLBL"), subspec, {clusterOutputSectorHeader}}, clustersMCBuffer.first); } } } @@ -1253,11 +1253,11 @@ Outputs GPURecoWorkflowSpec::outputs() return outputSpecs; } if (mSpecConfig.outputTracks) { - outputSpecs.emplace_back(gDataOriginTPC, "TRACKS", 0, Lifetime::Timeframe); - outputSpecs.emplace_back(gDataOriginTPC, "CLUSREFS", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "TRACKSF" : (DataDescription) "TRACKS", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "CLUSREFSF" : (DataDescription) "CLUSREFS", 0, Lifetime::Timeframe); } if (mSpecConfig.processMC && mSpecConfig.outputTracks) { - outputSpecs.emplace_back(gDataOriginTPC, "TRACKSMCLBL", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "TRACKSMCLBLF" : (DataDescription) "TRACKSMCLBL", 0, Lifetime::Timeframe); } if (mSpecConfig.outputCompClustersRoot) { outputSpecs.emplace_back(gDataOriginTPC, "COMPCLUSTERS", 0, Lifetime::Timeframe); @@ -1272,18 +1272,18 @@ Outputs GPURecoWorkflowSpec::outputs() if (mSpecConfig.sendClustersPerSector) { outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVETMP", NSectors, Lifetime::Timeframe); // Dummy buffer the TPC tracker writes the inital linear clusters to for (const auto sector : mTPCSectors) { - outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVE", sector, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "CLUSTERNATIVEF" : (DataDescription) "CLUSTERNATIVE", sector, Lifetime::Timeframe); } } else { - outputSpecs.emplace_back(gDataOriginTPC, "CLUSTERNATIVE", NSectors, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? (DataDescription) "CLUSTERNATIVEF" : (DataDescription) "CLUSTERNATIVE", NSectors, Lifetime::Timeframe); } if (mSpecConfig.processMC) { if (mSpecConfig.sendClustersPerSector) { for (const auto sector : mTPCSectors) { - outputSpecs.emplace_back(gDataOriginTPC, "CLNATIVEMCLBL", sector, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? DataDescription("CLNATIVEMCLBLF") : DataDescription("CLNATIVEMCLBL"), sector, Lifetime::Timeframe); } } else { - outputSpecs.emplace_back(gDataOriginTPC, "CLNATIVEMCLBL", NSectors, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginTPC, mSpecConfig.useFilteredOutputSpecs ? DataDescription("CLNATIVEMCLBLF") : DataDescription("CLNATIVEMCLBL"), NSectors, Lifetime::Timeframe); } } } diff --git a/GPU/Workflow/src/gpu-reco-workflow.cxx b/GPU/Workflow/src/gpu-reco-workflow.cxx index 5ae31554c173d..e620d013cc925 100644 --- a/GPU/Workflow/src/gpu-reco-workflow.cxx +++ b/GPU/Workflow/src/gpu-reco-workflow.cxx @@ -62,6 +62,7 @@ void customize(std::vector& workflowOptions) {"enableDoublePipeline", VariantType::Bool, false, {"enable GPU double pipeline mode"}}, {"tpc-deadMap-sources", VariantType::Int, -1, {"Sources to consider for TPC dead channel map creation; -1=all, 0=deactivated"}}, {"tpc-mc-time-gain", VariantType::Bool, false, {"use time gain calibration for MC (true) or for data (false)"}}, + {"filtered-output-specs", VariantType::Bool, false, {"use filtered output specs for output DataDescriptions"}}, }; o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); @@ -190,6 +191,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) cfg.tpcUseMCTimeGain = cfgc.options().get("tpc-mc-time-gain"); cfg.runITSTracking = isEnabled(outputTypes, ioType::ITSTracks); cfg.itsOverrBeamEst = isEnabled(inputTypes, ioType::MeanVertex); + cfg.useFilteredOutputSpecs = cfgc.options().get("filtered-output-specs"); Inputs ggInputs; auto ggRequest = std::make_shared(false, true, false, true, true, o2::base::GRPGeomRequest::Aligned, ggInputs, true); From 2e1b417de613f8cd503f1e4991615d716cc9358f Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 1 Oct 2025 14:20:45 +0200 Subject: [PATCH 46/99] Suppress reduntant versions of HelixHelper --- .../DCAFitter/include/DCAFitter/HelixHelper.h | 307 ------------------ .../include/DetectorsVertexing/HelixHelper.h | 307 ------------------ 2 files changed, 614 deletions(-) delete mode 100644 Common/DCAFitter/include/DCAFitter/HelixHelper.h delete mode 100644 Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h diff --git a/Common/DCAFitter/include/DCAFitter/HelixHelper.h b/Common/DCAFitter/include/DCAFitter/HelixHelper.h deleted file mode 100644 index d197cba256c0e..0000000000000 --- a/Common/DCAFitter/include/DCAFitter/HelixHelper.h +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file HelixHelper.h -/// \brief Helper classes for helical tracks manipulations -/// \author ruben.shahoyan@cern.ch - -#ifndef _ALICEO2_HELIX_HELPER_ -#define _ALICEO2_HELIX_HELPER_ - -#include "CommonConstants/MathConstants.h" -#include "MathUtils/Utils.h" -#include "MathUtils/Primitive2D.h" - -namespace o2 -{ -namespace track -{ - -///__________________________________________________________________________ -//< precalculated track radius, center, alpha sin,cos and their combinations -struct TrackAuxPar : public o2::math_utils::CircleXYf_t { - float c, s, cc, ss, cs; // cos ans sin of track alpha and their products - - GPUdDefault() TrackAuxPar() = default; - - template - GPUd() TrackAuxPar(const T& trc, float bz) - { - set(trc, bz); - } - GPUd() float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) - GPUd() float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) - - template - GPUd() void set(const T& trc, float bz) - { - trc.getCircleParams(bz, *this, s, c); - cc = c * c; - ss = s * s; - cs = c * s; - } - ClassDefNV(TrackAuxPar, 1); -}; - -//__________________________________________________________ -//< crossing coordinates of 2 circles -struct CrossInfo { - static constexpr float MaxDistXYDef = 10.; - float xDCA[2] = {}; - float yDCA[2] = {}; - int nDCA = 0; - - GPUd() int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) - { - const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A - const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; - nDCA = 0; - float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; - float dist2 = xDist * xDist + yDist * yDist, dist = o2::gpu::GPUCommonMath::Sqrt(dist2), rsum = trcA.rC + trcB.rC; - if (dist < 1e-12) { - return nDCA; // circles are concentric? - } - if (dist > rsum) { // circles don't touch, chose a point in between - // the parametric equation of lines connecting the centers is - // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) - if (dist - rsum > maxDistXY) { // too large distance - return nDCA; - } - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC, isCollinear); - } else if (auto dfr = dist + trcB.rC - trcA.rC; dfr < 0.) { // the small circle is nestled into large one w/o touching - if (dfr > -maxDistXY) { - // select the point of closest approach of 2 circles - notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC, isCollinear); - } else { - return nDCA; - } - } else { // 2 intersection points - if (isCollinear) { - /// collinear tracks, e.g. electrons from photon conversion - /// if there are 2 crossings of the circle it is better to take - /// a weighted average of the crossing points as a radius - float r2r = trcA.rC + trcB.rC; - float r1_r = trcA.rC / r2r; - float r2_r = trcB.rC / r2r; - xDCA[0] = r2_r * trcA.xC + r1_r * trcB.xC; - yDCA[0] = r2_r * trcA.yC + r1_r * trcB.yC; - nDCA = 1; - } else if (o2::gpu::GPUCommonMath::Abs(xDist) < o2::gpu::GPUCommonMath::Abs(yDist)) { - // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that - // the 1st one is centered in origin - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = o2::gpu::GPUCommonMath::Sqrt(det); - xDCA[0] = (-ab + det) / (1. + b * b); - yDCA[0] = a + b * xDCA[0] + trcA.yC; - xDCA[0] += trcA.xC; - xDCA[1] = (-ab - det) / (1. + b * b); - yDCA[1] = a + b * xDCA[1] + trcA.yC; - xDCA[1] += trcA.xC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } else { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = o2::gpu::GPUCommonMath::Sqrt(det); - yDCA[0] = (-ab + det) / (1. + bb); - xDCA[0] = a + b * yDCA[0] + trcA.xC; - yDCA[0] += trcA.yC; - yDCA[1] = (-ab - det) / (1. + bb); - xDCA[1] = a + b * yDCA[1] + trcA.xC; - yDCA[1] += trcA.yC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } - } - return nDCA; - } - - GPUd() void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign, bool isCollinear = false) - { - if (isCollinear) { - /// for collinear tracks it is better to take - /// a weighted average of the crossing points as a radius - float r2r = trcA.rC + rBSign; - float r1_r = trcA.rC / r2r; - float r2_r = rBSign / r2r; - xDCA[0] = r2_r * trcA.xC + r1_r * (xDist + trcA.xC); - yDCA[0] = r2_r * trcA.yC + r1_r * (yDist + trcA.yC); - } else { - // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: - // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist - // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... - // There are 2 special cases: - // (a) small circle is inside the large one: provide rBSign as -trcB.rC - // (b) circle are side by side: provide rBSign as trcB.rC - auto t2d = (dist + trcA.rC - rBSign) / dist; - xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); - yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); - } - nDCA = 1; - } - - template - GPUd() int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of 2 straight lines - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 - nDCA = 0; - float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! - float dy = trax1.yC - trax0.yC; // - float dz = tr1.getZ() - tr0.getZ(); - auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 - auto csp0i = o2::gpu::GPUCommonMath::Sqrt(csp0i2); - auto tgp0 = tr0.getSnp() * csp0i; - float kx0 = trax0.c - trax0.s * tgp0; - float ky0 = trax0.s + trax0.c * tgp0; - float kz0 = tr0.getTgl() * csp0i; - auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 - auto csp1i = o2::gpu::GPUCommonMath::Sqrt(csp1i2); - auto tgp1 = tr1.getSnp() * o2::gpu::GPUCommonMath::Sqrt(csp1i2); - float kx1 = trax1.c - trax1.s * tgp1; - float ky1 = trax1.s + trax1.c * tgp1; - float kz1 = tr1.getTgl() * csp1i; - /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach - /// Leads to system - /// A Dx = B with Dx = {dx0, dx1} - /// with A = - /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... - /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 - /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } - /// - float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); - float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); - float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; - if (o2::gpu::GPUCommonMath::Sqrt(det) > o2::constants::math::Almost0) { - auto detI = 1. / det; - auto t0 = det0 * detI; - auto t1 = det1 * detI; - float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; - dx += addx1 - addx0; // recalculate XY distance at DCA - dy += addy1 - addy0; - if (dx * dx + dy * dy > maxDistXY * maxDistXY) { - return nDCA; - } - xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; - yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - GPUd() int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of line and circle - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 = 1 / csp^2 - - const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) - const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line - const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line - - // solve quadratic equation of line crossing the circle - float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center - float dy = traxL.yC - traxH.yC; // Y... - // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 - auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 - auto cspi = o2::gpu::GPUCommonMath::Sqrt(cspi2); - auto tgp = trcL.getSnp() * cspi; - float kx = traxL.c - traxL.s * tgp; - float ky = traxL.s + traxL.c * tgp; - double dk = dx * kx + dy * ky; - double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); - if (det > 0) { // 2 crossings - det = o2::gpu::GPUCommonMath::Sqrt(det); - float t0 = (-dk + det) * cspi2; - float t1 = (-dk - det) * cspi2; - xDCA[0] = traxL.xC + kx * t0; - yDCA[0] = traxL.yC + ky * t0; - xDCA[1] = traxL.xC + kx * t1; - yDCA[1] = traxL.yC + ky * t1; - nDCA = 2; - } else { - // there is no crossing, find the point of the closest approach on the line which is closest to the circle center - float t = -dk * cspi2; - float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle - float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = o2::gpu::GPUCommonMath::Sqrt(dxc * dxc + dyc * dyc); - if (dist - traxH.rC > maxDistXY) { // too large distance - return nDCA; - } - float drcf = traxH.rC / dist; // radius / distance to circle center - float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; - xDCA[0] = (xL + xH) * 0.5; - yDCA[0] = (yL + yH) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - GPUd() int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) - { - // calculate up to 2 crossings between 2 circles - nDCA = 0; - if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines - nDCA = circlesCrossInfo(trax0, trax1, maxDistXY, isCollinear); - } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines - nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } else { - nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } - // - return nDCA; - } - - GPUdDefault() CrossInfo() = default; - - template - GPUd() CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) - { - set(trax0, tr0, trax1, tr1, maxDistXY, isCollinear); - } - ClassDefNV(CrossInfo, 1); -}; - -} // namespace track -} // namespace o2 - -#endif diff --git a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h b/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h deleted file mode 100644 index d197cba256c0e..0000000000000 --- a/Detectors/Vertexing/include/DetectorsVertexing/HelixHelper.h +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file HelixHelper.h -/// \brief Helper classes for helical tracks manipulations -/// \author ruben.shahoyan@cern.ch - -#ifndef _ALICEO2_HELIX_HELPER_ -#define _ALICEO2_HELIX_HELPER_ - -#include "CommonConstants/MathConstants.h" -#include "MathUtils/Utils.h" -#include "MathUtils/Primitive2D.h" - -namespace o2 -{ -namespace track -{ - -///__________________________________________________________________________ -//< precalculated track radius, center, alpha sin,cos and their combinations -struct TrackAuxPar : public o2::math_utils::CircleXYf_t { - float c, s, cc, ss, cs; // cos ans sin of track alpha and their products - - GPUdDefault() TrackAuxPar() = default; - - template - GPUd() TrackAuxPar(const T& trc, float bz) - { - set(trc, bz); - } - GPUd() float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) - GPUd() float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) - - template - GPUd() void set(const T& trc, float bz) - { - trc.getCircleParams(bz, *this, s, c); - cc = c * c; - ss = s * s; - cs = c * s; - } - ClassDefNV(TrackAuxPar, 1); -}; - -//__________________________________________________________ -//< crossing coordinates of 2 circles -struct CrossInfo { - static constexpr float MaxDistXYDef = 10.; - float xDCA[2] = {}; - float yDCA[2] = {}; - int nDCA = 0; - - GPUd() int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) - { - const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A - const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; - nDCA = 0; - float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; - float dist2 = xDist * xDist + yDist * yDist, dist = o2::gpu::GPUCommonMath::Sqrt(dist2), rsum = trcA.rC + trcB.rC; - if (dist < 1e-12) { - return nDCA; // circles are concentric? - } - if (dist > rsum) { // circles don't touch, chose a point in between - // the parametric equation of lines connecting the centers is - // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) - if (dist - rsum > maxDistXY) { // too large distance - return nDCA; - } - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC, isCollinear); - } else if (auto dfr = dist + trcB.rC - trcA.rC; dfr < 0.) { // the small circle is nestled into large one w/o touching - if (dfr > -maxDistXY) { - // select the point of closest approach of 2 circles - notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC, isCollinear); - } else { - return nDCA; - } - } else { // 2 intersection points - if (isCollinear) { - /// collinear tracks, e.g. electrons from photon conversion - /// if there are 2 crossings of the circle it is better to take - /// a weighted average of the crossing points as a radius - float r2r = trcA.rC + trcB.rC; - float r1_r = trcA.rC / r2r; - float r2_r = trcB.rC / r2r; - xDCA[0] = r2_r * trcA.xC + r1_r * trcB.xC; - yDCA[0] = r2_r * trcA.yC + r1_r * trcB.yC; - nDCA = 1; - } else if (o2::gpu::GPUCommonMath::Abs(xDist) < o2::gpu::GPUCommonMath::Abs(yDist)) { - // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that - // the 1st one is centered in origin - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = o2::gpu::GPUCommonMath::Sqrt(det); - xDCA[0] = (-ab + det) / (1. + b * b); - yDCA[0] = a + b * xDCA[0] + trcA.yC; - xDCA[0] += trcA.xC; - xDCA[1] = (-ab - det) / (1. + b * b); - yDCA[1] = a + b * xDCA[1] + trcA.yC; - xDCA[1] += trcA.xC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } else { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = o2::gpu::GPUCommonMath::Sqrt(det); - yDCA[0] = (-ab + det) / (1. + bb); - xDCA[0] = a + b * yDCA[0] + trcA.xC; - yDCA[0] += trcA.yC; - yDCA[1] = (-ab - det) / (1. + bb); - xDCA[1] = a + b * yDCA[1] + trcA.xC; - yDCA[1] += trcA.yC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } - } - return nDCA; - } - - GPUd() void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign, bool isCollinear = false) - { - if (isCollinear) { - /// for collinear tracks it is better to take - /// a weighted average of the crossing points as a radius - float r2r = trcA.rC + rBSign; - float r1_r = trcA.rC / r2r; - float r2_r = rBSign / r2r; - xDCA[0] = r2_r * trcA.xC + r1_r * (xDist + trcA.xC); - yDCA[0] = r2_r * trcA.yC + r1_r * (yDist + trcA.yC); - } else { - // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: - // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist - // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... - // There are 2 special cases: - // (a) small circle is inside the large one: provide rBSign as -trcB.rC - // (b) circle are side by side: provide rBSign as trcB.rC - auto t2d = (dist + trcA.rC - rBSign) / dist; - xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); - yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); - } - nDCA = 1; - } - - template - GPUd() int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of 2 straight lines - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 - nDCA = 0; - float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! - float dy = trax1.yC - trax0.yC; // - float dz = tr1.getZ() - tr0.getZ(); - auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 - auto csp0i = o2::gpu::GPUCommonMath::Sqrt(csp0i2); - auto tgp0 = tr0.getSnp() * csp0i; - float kx0 = trax0.c - trax0.s * tgp0; - float ky0 = trax0.s + trax0.c * tgp0; - float kz0 = tr0.getTgl() * csp0i; - auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 - auto csp1i = o2::gpu::GPUCommonMath::Sqrt(csp1i2); - auto tgp1 = tr1.getSnp() * o2::gpu::GPUCommonMath::Sqrt(csp1i2); - float kx1 = trax1.c - trax1.s * tgp1; - float ky1 = trax1.s + trax1.c * tgp1; - float kz1 = tr1.getTgl() * csp1i; - /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach - /// Leads to system - /// A Dx = B with Dx = {dx0, dx1} - /// with A = - /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... - /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 - /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } - /// - float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); - float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); - float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; - if (o2::gpu::GPUCommonMath::Sqrt(det) > o2::constants::math::Almost0) { - auto detI = 1. / det; - auto t0 = det0 * detI; - auto t1 = det1 * detI; - float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; - dx += addx1 - addx0; // recalculate XY distance at DCA - dy += addy1 - addy0; - if (dx * dx + dy * dy > maxDistXY * maxDistXY) { - return nDCA; - } - xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; - yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - GPUd() int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of line and circle - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 = 1 / csp^2 - - const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) - const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line - const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line - - // solve quadratic equation of line crossing the circle - float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center - float dy = traxL.yC - traxH.yC; // Y... - // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 - auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 - auto cspi = o2::gpu::GPUCommonMath::Sqrt(cspi2); - auto tgp = trcL.getSnp() * cspi; - float kx = traxL.c - traxL.s * tgp; - float ky = traxL.s + traxL.c * tgp; - double dk = dx * kx + dy * ky; - double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); - if (det > 0) { // 2 crossings - det = o2::gpu::GPUCommonMath::Sqrt(det); - float t0 = (-dk + det) * cspi2; - float t1 = (-dk - det) * cspi2; - xDCA[0] = traxL.xC + kx * t0; - yDCA[0] = traxL.yC + ky * t0; - xDCA[1] = traxL.xC + kx * t1; - yDCA[1] = traxL.yC + ky * t1; - nDCA = 2; - } else { - // there is no crossing, find the point of the closest approach on the line which is closest to the circle center - float t = -dk * cspi2; - float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle - float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = o2::gpu::GPUCommonMath::Sqrt(dxc * dxc + dyc * dyc); - if (dist - traxH.rC > maxDistXY) { // too large distance - return nDCA; - } - float drcf = traxH.rC / dist; // radius / distance to circle center - float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; - xDCA[0] = (xL + xH) * 0.5; - yDCA[0] = (yL + yH) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - GPUd() int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) - { - // calculate up to 2 crossings between 2 circles - nDCA = 0; - if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines - nDCA = circlesCrossInfo(trax0, trax1, maxDistXY, isCollinear); - } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines - nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } else { - nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } - // - return nDCA; - } - - GPUdDefault() CrossInfo() = default; - - template - GPUd() CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) - { - set(trax0, tr0, trax1, tr1, maxDistXY, isCollinear); - } - ClassDefNV(CrossInfo, 1); -}; - -} // namespace track -} // namespace o2 - -#endif From d0d0ba6a0b29909bcec01f83493e1b59b7dbacf2 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 6 Oct 2025 00:53:12 +0200 Subject: [PATCH 47/99] DPL: refactor resource offers to allow multi-resource ones (#14717) Preliminary work to properly avoid race conditions between memory and timeslice rate limiting and in general to support multi source rate limiting. --- Framework/Core/src/ArrowSupport.cxx | 82 ++++++++++++++++++----------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index a289980349924..94764571840f4 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -288,63 +288,85 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() if (maxTimeframes && (totalTimeframesRead - totalTimeframesConsumed) > maxTimeframes) { return; } + struct ResourceState { + int64_t available; + int64_t offered = 0; + int64_t lastDeviceOffered = 0; + }; + struct ResourceStats { + int64_t enoughCount; + int64_t lowCount; + }; + struct ResourceSpec{ + int64_t maxAvailable; + int64_t maxQuantum; + int64_t minQuantum; + }; + static const ResourceSpec resourceSpec{ + .maxAvailable = (int64_t)calculateAvailableSharedMemory(registry), + .maxQuantum = 100, + .minQuantum = 50, + }; + static ResourceState resourceState{ + .available = resourceSpec.maxAvailable, + }; + static ResourceStats resourceStats{ + .enoughCount = resourceState.available - resourceSpec.minQuantum > 0 ? 1 : 0, + .lowCount = resourceState.available - resourceSpec.minQuantum > 0 ? 0 : 1 + }; - static int64_t MAX_SHARED_MEMORY = calculateAvailableSharedMemory(registry); - constexpr int64_t MAX_QUANTUM_SHARED_MEMORY = 100; - constexpr int64_t MIN_QUANTUM_SHARED_MEMORY = 50; - - static int64_t availableSharedMemory = MAX_SHARED_MEMORY; - static int64_t offeredSharedMemory = 0; - static int64_t lastDeviceOffered = 0; /// We loop over the devices, starting from where we stopped last time /// offering MIN_QUANTUM_SHARED_MEMORY of shared memory to each reader. int64_t lastCandidate = -1; - static int enoughSharedMemoryCount = availableSharedMemory - MIN_QUANTUM_SHARED_MEMORY > 0 ? 1 : 0; - static int lowSharedMemoryCount = availableSharedMemory - MIN_QUANTUM_SHARED_MEMORY > 0 ? 0 : 1; - int64_t possibleOffer = MIN_QUANTUM_SHARED_MEMORY; + int64_t possibleOffer = resourceSpec.minQuantum; + for (size_t di = 0; di < specs.size(); di++) { - if (availableSharedMemory < possibleOffer) { - if (lowSharedMemoryCount == 0) { + if (resourceState.available < possibleOffer) { + if (resourceStats.lowCount == 0) { O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "not enough", "We do not have enough shared memory (%{bytes}llu MB) to offer %{bytes}llu MB. Total offerings %{bytes}llu", - availableSharedMemory, possibleOffer, offeredSharedMemory); + resourceState.available, possibleOffer, resourceState.offered); } - lowSharedMemoryCount++; - enoughSharedMemoryCount = 0; + resourceStats.lowCount++; + resourceStats.enoughCount = 0; break; } else { - if (enoughSharedMemoryCount == 0) { + if (resourceStats.enoughCount == 0) { O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "enough", - "We are back in a state where we enough shared memory: %{bytes}llu MB", availableSharedMemory); + "We are back in a state where we enough shared memory: %{bytes}llu MB", resourceState.available); } - enoughSharedMemoryCount++; - lowSharedMemoryCount = 0; + resourceStats.lowCount = 0; + resourceStats.enoughCount++; } - size_t candidate = (lastDeviceOffered + di) % specs.size(); + size_t candidate = (resourceState.lastDeviceOffered + di) % specs.size(); auto& info = infos[candidate]; // Do not bother for inactive devices // FIXME: there is probably a race condition if the device died and we did not // took notice yet... if (info.active == false || info.readyToQuit) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Device %s is inactive not offering memory to it.", specs[candidate].name.c_str()); continue; } if (specs[candidate].name != "internal-dpl-aod-reader") { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Device %s is not a reader. Not offering memory to it.", specs[candidate].name.c_str()); continue; } - possibleOffer = std::min(MAX_QUANTUM_SHARED_MEMORY, availableSharedMemory); + possibleOffer = std::min(resourceSpec.maxQuantum, resourceState.available); O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", "Offering %{bytes}llu MB out of %{bytes}llu to %{public}s", - possibleOffer, availableSharedMemory, specs[candidate].id.c_str()); + possibleOffer, resourceState.available, specs[candidate].id.c_str()); manager.queueMessage(specs[candidate].id.c_str(), fmt::format("/shm-offer {}", possibleOffer).data()); - availableSharedMemory -= possibleOffer; - offeredSharedMemory += possibleOffer; + resourceState.available -= possibleOffer; + resourceState.offered += possibleOffer; lastCandidate = candidate; } // We had at least a valid candidate, so // next time we offer to the next device. if (lastCandidate >= 0) { - lastDeviceOffered = lastCandidate + 1; + resourceState.lastDeviceOffered = lastCandidate + 1; } // unusedOfferedSharedMemory is the amount of memory which was offered and which we know it was @@ -357,21 +379,21 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() "Offer consumed so far %{bytes}llu", shmOfferBytesConsumed); lastShmOfferConsumed = shmOfferBytesConsumed; } - int unusedOfferedMemory = (offeredSharedMemory - (totalBytesExpired + shmOfferBytesConsumed) / 1000000); + int unusedOfferedMemory = (resourceState.offered - (totalBytesExpired + shmOfferBytesConsumed) / 1000000); if (lastUnusedOfferedMemory != unusedOfferedMemory) { O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", "unusedOfferedMemory:%{bytes}d = offered:%{bytes}llu - (expired:%{bytes}llu + consumed:%{bytes}llu) / 1000000", - unusedOfferedMemory, offeredSharedMemory, totalBytesExpired / 1000000, shmOfferBytesConsumed / 1000000); + unusedOfferedMemory, resourceState.offered, totalBytesExpired / 1000000, shmOfferBytesConsumed / 1000000); lastUnusedOfferedMemory = unusedOfferedMemory; } // availableSharedMemory is the amount of memory which we know is available to be offered. // We subtract the amount which we know was already offered but it's unused and we then balance how // much was created with how much was destroyed. - availableSharedMemory = MAX_SHARED_MEMORY + ((totalBytesDestroyed - totalBytesCreated) / 1000000) - unusedOfferedMemory; - availableSharedMemoryMetric(driverMetrics, availableSharedMemory, timestamp); + resourceState.available = resourceSpec.maxAvailable + ((totalBytesDestroyed - totalBytesCreated) / 1000000) - unusedOfferedMemory; + availableSharedMemoryMetric(driverMetrics, resourceState.available, timestamp); unusedOfferedSharedMemoryMetric(driverMetrics, unusedOfferedMemory, timestamp); - offeredSharedMemoryMetric(driverMetrics, offeredSharedMemory, timestamp); }, + offeredSharedMemoryMetric(driverMetrics, resourceState.offered, timestamp); }, .postDispatching = [](ProcessingContext& ctx, void* service) { using DataHeader = o2::header::DataHeader; auto* arrow = reinterpret_cast(service); From fb2b5d9b99c948a7b2214962ca9e041bb99f589d Mon Sep 17 00:00:00 2001 From: Fabrizio Grosa Date: Sun, 5 Oct 2025 11:08:55 +0200 Subject: [PATCH 48/99] Fix protection of MC signal filtering to work with any embedPatt --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index b841943b05031..b8d295a4393e4 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -2063,16 +2063,11 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords(); const auto& mcParts = mcReader->getDigitizationContext()->getEventParts(); - // count all parts - int totalNParts = 0; - for (int iCol = 0; iCol < nMCCollisions; iCol++) { - totalNParts += mcParts[iCol].size(); - - // if signal filtering enabled, let's check if there are more than one source; otherwise fatalise - if (mUseSigFiltMC) { - std::vector sourceIDs{}; - auto& colParts = mcParts[iCol]; - for (auto colPart : colParts) { + // if signal filtering enabled, let's check if there are more than one source; otherwise fatalise + if (mUseSigFiltMC) { + std::vector sourceIDs{}; + for (int iCol = 0; iCol < nMCCollisions; iCol++) { + for (auto const& colPart : mcParts[iCol]) { int sourceID = colPart.sourceID; if (std::find(sourceIDs.begin(), sourceIDs.end(), sourceID) == sourceIDs.end()) { sourceIDs.push_back(sourceID); @@ -2081,10 +2076,19 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) break; } } - if (sourceIDs.size() <= 1) { - LOGP(fatal, "Signal filtering cannot be enabled without embedding. Please fix the configuration either enabling the embedding, or turning off the signal filtering."); + if (sourceIDs.size() > 1) { // we found more than one, exit + break; } } + if (sourceIDs.size() <= 1) { + LOGP(fatal, "Signal filtering cannot be enabled without embedding. Please fix the configuration either enabling the embedding, or turning off the signal filtering."); + } + } + + // count all parts + int totalNParts = 0; + for (int iCol = 0; iCol < nMCCollisions; iCol++) { + totalNParts += mcParts[iCol].size(); } mcCollisionsCursor.reserve(totalNParts); From 551492b5a10030b344c3c1014d9c3f81679bcce8 Mon Sep 17 00:00:00 2001 From: wiechula Date: Sun, 5 Oct 2025 23:39:18 +0200 Subject: [PATCH 49/99] Silence by default IncompleteHBF check --- Detectors/TPC/workflow/src/IDCToVectorSpec.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx b/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx index 9d704d425f1da..da8de5f262cdf 100644 --- a/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx +++ b/Detectors/TPC/workflow/src/IDCToVectorSpec.cxx @@ -72,6 +72,7 @@ class IDCToVectorDevice : public o2::framework::Task mWriteDebugOnError = ic.options().get("write-debug-on-error"); mWriteRawDataOnError = ic.options().get("write-raw-data-on-error"); mRawDataType = ic.options().get("raw-data-type"); + o2::framework::RawParser<>::setCheckIncompleteHBF(ic.options().get("check-incomplete-hbf")); mDebugStreamFileName = ic.options().get("debug-file-name").data(); mRawOutputFileName = ic.options().get("raw-file-name").data(); @@ -606,9 +607,10 @@ o2::framework::DataProcessorSpec getIDCToVectorSpec(const std::string inputSpec, {"write-raw-data-on-error", VariantType::Bool, false, {"dump raw data in case errors occurred"}}, {"raw-file-name", VariantType::String, "/tmp/idc_debug.{run}.{raw_type}", {"name of the raw output file"}}, {"raw-data-type", VariantType::Int, 0, {"Which raw data to dump: 0-full TPC with DH, 1-full TPC with DH skip empty, 2-full TPC no DH, 3-full TPC no DH skip empty, 4-IDC raw only"}}, + {"check-incomplete-hbf", VariantType::Bool, false, {"false: don't chck; true: check and report"}}, {"pedestal-url", VariantType::String, "ccdb-default", {"ccdb-default: load from NameConf::getCCDBServer() OR ccdb url (must contain 'ccdb' OR pedestal file name"}}, {"swap-links", VariantType::Bool, false, {"swap links to circumvent bug in FW"}}, } // end Options - }; // end DataProcessorSpec + }; // end DataProcessorSpec } } // namespace o2::tpc From 1c0aac4930ac1237476d2d2570b474708b19bec2 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 6 Oct 2025 14:11:58 +0200 Subject: [PATCH 50/99] Glo: only initialzed AB geom helper up to lowest layer We can skip initializing the layers below the lowest allowed AB layer. This allows to run the AB for ITS3 otherwise the initialization of the RecoGeomHelper is not well defined and crashed for layers < 3 since the chip mapping is not well defined. Currently we anyways allow AB tracks only to go down to layer 3 by default and this might not change for Run 4 anytime soon. If it does the RecoGeomHelper class has to be adapted then. Signed-off-by: Felix Schlepper --- Detectors/GlobalTracking/src/MatchTPCITS.cxx | 4 ++-- .../reconstruction/include/ITSReconstruction/RecoGeomHelper.h | 2 +- Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index e16031f641829..7e3f8d9f78fc9 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -245,8 +245,8 @@ void MatchTPCITS::init() } #endif - if (mParams->runAfterBurner) { // only used in AfterBurner - mRGHelper.init(); // prepare helper for TPC track / ITS clusters matching + if (mParams->runAfterBurner) { // only used in AfterBurner + mRGHelper.init(mParams->lowestLayerAB); // prepare helper for TPC track / ITS clusters matching } clear(); diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h index f9d3f1ae46752..a7d814f02d011 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h @@ -103,7 +103,7 @@ struct RecoGeomHelper { static constexpr float ladderWidth() { return o2::itsmft::SegmentationAlpide::SensorSizeRows; } static constexpr float ladderWidthInv() { return 1. / ladderWidth(); } - void init(); + void init(int minLayer = 0, int maxLayer = getNLayers()); void print() const; ClassDefNV(RecoGeomHelper, 0); diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx index 8f2efef0b34cd..712ec6a022d16 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx @@ -229,9 +229,9 @@ void RecoGeomHelper::RecoLayer::print() const } //_____________________________________________________________________ -void RecoGeomHelper::init() +void RecoGeomHelper::init(int minLayer, int maxLayer) { - for (int il = int(layers.size()); il--;) { + for (int il = maxLayer; --il >= minLayer;) { auto& lr = layers[il]; lr.id = il; lr.init(); From c773a338aad60ffeb1f864539721883edd4ab460 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 6 Oct 2025 18:13:22 +0200 Subject: [PATCH 51/99] TPC Workflow: Fix option name in help text and comment --- Detectors/TPC/workflow/src/RecoWorkflow.cxx | 2 +- Detectors/TPC/workflow/src/tpc-reco-workflow.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 3fdff02dd69cc..98a9841fac8b2 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -553,7 +553,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto // // a writer process for compressed clusters container // - // selected by output type 'compressed-clusters' + // selected by output type 'compressed-clusters-root' if (produceCompClustersRoot && !isEnabled(OutputType::DisableWriter)) { // defining the track writer process using the generic RootTreeWriter and generator tool // diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index b440f6e8d359f..3c8804de8b536 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -57,7 +57,7 @@ void customize(std::vector& workflowOptions) using namespace o2::framework; std::vector options{ - {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clusters, compressed-clusters-root, compressed-clusters-ctf, compressed-clusters-flat-for-encode, pass-through"}}, + {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clusters, compressed-clusters-root, compressed-clusters-flat, compressed-clusters-flat-for-encode, pass-through"}}, {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clusters, tracks, compressed-clusters-root, compressed-clusters-flat, encoded-clusters, disable-writer, send-clusters-per-sector, qa, no-shared-cluster-map, tpc-triggers"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"no-ca-clusterer", VariantType::Bool, false, {"Use HardwareClusterer instead of clusterer of GPUCATracking"}}, From 7c22e8315c14628df50435dadf5745157aaafcfc Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 7 Oct 2025 23:21:17 +0200 Subject: [PATCH 52/99] Account for rejected clusters in unbinned residuals counters --- .../TPC/calibration/SpacePoints/src/TrackInterpolation.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index d89b3c28f1a0d..f9861bb26ff93 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -640,7 +640,6 @@ void TrackInterpolation::interpolateTrack(int iSeed) // skip masked cluster residual continue; } - ++nClValidated; const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp)); const auto dy = clusterResiduals[iCl].dy; const auto dz = clusterResiduals[iCl].dz; @@ -649,6 +648,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) const auto sec = clusterResiduals[iCl].sec; if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, sec); + ++nClValidated; } else { ++mRejectedResiduals; } @@ -875,7 +875,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) TrackParams params; // for refitted track parameters and flagging rejected clusters if (clusterResiduals.size() > constants::MAXGLOBALPADROW) { - LOGP(warn, "Extrapolated ITS-TPC track and found more reesiduals than possible ({})", clusterResiduals.size()); + LOGP(warn, "Extrapolated ITS-TPC track and found more residuals than possible ({})", clusterResiduals.size()); return; } @@ -899,7 +899,6 @@ void TrackInterpolation::extrapolateTrack(int iSeed) if (iRow < param::NPadRows && params.flagRej[iCl]) { // skip masked cluster residual continue; } - ++nClValidated; const float tgPhi = clusterResiduals[iCl].snp / std::sqrt((1.f - clusterResiduals[iCl].snp) * (1.f + clusterResiduals[iCl].snp)); const auto dy = clusterResiduals[iCl].dy; const auto dz = clusterResiduals[iCl].dz; @@ -907,6 +906,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) const auto z = clusterResiduals[iCl].z; if ((std::abs(dy) < param::MaxResid) && (std::abs(dz) < param::MaxResid) && (std::abs(y) < param::MaxY) && (std::abs(z) < param::MaxZ) && (std::abs(tgPhi) < param::MaxTgSlp)) { mClRes.emplace_back(dy, dz, tgPhi, y, z, iRow, clusterResiduals[iCl].sec); + ++nClValidated; } else { ++mRejectedResiduals; } From 66699adb397893343abf1262a93bbd408ef55ada Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 9 Oct 2025 20:09:48 +0200 Subject: [PATCH 53/99] DPL: print an error when the configuration is not parsed correctly (#14727) This should probably be a fatal error not sure why the rethrown exception is ignored (and where). --- Framework/Core/src/PropertyTreeHelpers.cxx | 43 ++++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/Framework/Core/src/PropertyTreeHelpers.cxx b/Framework/Core/src/PropertyTreeHelpers.cxx index 3f2356eb37824..18055edb635b4 100644 --- a/Framework/Core/src/PropertyTreeHelpers.cxx +++ b/Framework/Core/src/PropertyTreeHelpers.cxx @@ -14,12 +14,15 @@ #include "Framework/VariantPropertyTreeHelpers.h" #include "Framework/RuntimeError.h" #include "Framework/VariantJSONHelpers.h" +#include "Framework/Signpost.h" #include #include #include +O2_DECLARE_DYNAMIC_LOG(configuration); + namespace o2::framework { namespace @@ -37,6 +40,8 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s boost::property_tree::ptree& pt, boost::property_tree::ptree& provenance) { + O2_SIGNPOST_ID_GENERATE(cid, configuration); + O2_SIGNPOST_START(configuration, cid, "populateDefaults", "Filling with defaults"); for (auto const& spec : schema) { std::string key = spec.name.substr(0, spec.name.find(',')); try { @@ -77,9 +82,12 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s case VariantType::String: pt.put(key, spec.defaultValue.get()); break; - case VariantType::Bool: - pt.put(key, spec.defaultValue.get()); + case VariantType::Bool: { + bool value = spec.defaultValue.get(); + O2_SIGNPOST_EVENT_EMIT(configuration, cid, "populateDefaults", "Setting %{public}s: %{public}s", key.c_str(), value ? "true" : "false"); + pt.put(key, value); break; + } case VariantType::Dict: pt.put_child(key, boost::property_tree::ptree{}); break; @@ -126,13 +134,17 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s } provenance.put(key, "default"); } catch (std::runtime_error& re) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populateDefaults", "Aborting because of runtime_error %{public}s", re.what()); throw; } catch (std::exception& e) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populateDefaults", "Missing option %{public}s (%{public}s)", key.c_str(), e.what()); throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populateDefaults", "Aborting because of missing option %{public}s", key.c_str()); throw std::invalid_argument(std::string("missing option: ") + key); } } + O2_SIGNPOST_END(configuration, cid, "populateDefaults", "Done"); } void PropertyTreeHelpers::populate(std::vector const& schema, @@ -140,6 +152,8 @@ void PropertyTreeHelpers::populate(std::vector const& schema, boost::program_options::variables_map const& vmap, boost::property_tree::ptree& provenance) { + O2_SIGNPOST_ID_GENERATE(cid, configuration); + O2_SIGNPOST_START(configuration, cid, "populate", "Filling parameters from variables_map"); for (auto const& spec : schema) { // strip short version to get the correct key std::string key = spec.name.substr(0, spec.name.find(',')); @@ -183,9 +197,11 @@ void PropertyTreeHelpers::populate(std::vector const& schema, pt.put(key, *v); } break; - case VariantType::Bool: - pt.put(key, vmap[key].as()); - break; + case VariantType::Bool: { + auto v = vmap[key].as(); + O2_SIGNPOST_EVENT_EMIT(configuration, cid, "populate", "Setting %{public}s: %{public}s", key.c_str(), v ? "true" : "false"); + pt.put(key, v); + } break; case VariantType::ArrayInt: { auto v = fromString(vmap[key].as()); pt.put_child(key, vectorToBranch(v.get(), v.size())); @@ -243,13 +259,17 @@ void PropertyTreeHelpers::populate(std::vector const& schema, } provenance.put(key, "fairmq"); } catch (std::runtime_error& re) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting because of runtime_error %{public}s", re.what()); throw; } catch (std::exception& e) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Missing option %{public}s (%{public}s)", key.c_str(), e.what()); throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting because of missing option %{public}s", key.c_str()); throw std::invalid_argument(std::string("missing option: ") + key); } } + O2_SIGNPOST_END(configuration, cid, "populate", "Done"); } template @@ -273,6 +293,8 @@ void PropertyTreeHelpers::populate(std::vector const& schema, boost::property_tree::ptree& provenance, std::string const& provenanceLabel) { + O2_SIGNPOST_ID_GENERATE(cid, configuration); + O2_SIGNPOST_START(configuration, cid, "populate", "Filling parameters from ptree"); for (auto const& spec : schema) { // strip short version to get the correct key std::string key = spec.name.substr(0, spec.name.find(',')); @@ -318,9 +340,11 @@ void PropertyTreeHelpers::populate(std::vector const& schema, case VariantType::String: pt.put(key, (*it).get_value()); break; - case VariantType::Bool: + case VariantType::Bool: { + auto v = (*it).get_value(); + O2_SIGNPOST_EVENT_EMIT(configuration, cid, "populate", "Setting %{public}s: %{public}s", key.c_str(), v ? "true" : "false"); pt.put(key, (*it).get_value()); - break; + } break; case VariantType::Dict: case VariantType::ArrayInt: case VariantType::ArrayFloat: @@ -371,13 +395,18 @@ void PropertyTreeHelpers::populate(std::vector const& schema, } provenance.put(key, provenanceLabel); } catch (std::runtime_error& re) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting during processing of %{public}s because of runtime_error %{public}s", + key.c_str(), re.what()); throw; } catch (std::exception& e) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Missing option %{public}s (%{public}s)", key.c_str(), e.what()); throw std::invalid_argument(std::string("missing option: ") + key + " (" + e.what() + ")"); } catch (...) { + O2_SIGNPOST_END_WITH_ERROR(configuration, cid, "populate", "Aborting because of missing option %{public}s", key.c_str()); throw std::invalid_argument(std::string("missing option: ") + key); } } + O2_SIGNPOST_END(configuration, cid, "populate", "Done"); } namespace From 0409ca3e4dafba15278104ae62fa8be2b1a66bd0 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 10 Oct 2025 09:28:30 +0200 Subject: [PATCH 54/99] DPL: support submitting directly to slurm (#14728) Without this, different jobs on the same machine will cross-talk due to possible lack of isolation in linux abstract sockets. --- Framework/Core/src/ChannelSpecHelpers.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Framework/Core/src/ChannelSpecHelpers.cxx b/Framework/Core/src/ChannelSpecHelpers.cxx index c66a1964c12a0..0578c51403b26 100644 --- a/Framework/Core/src/ChannelSpecHelpers.cxx +++ b/Framework/Core/src/ChannelSpecHelpers.cxx @@ -339,6 +339,10 @@ std::string ChannelSpecHelpers::defaultIPCFolder() if (channelPrefix) { return fmt::format("@dpl_{}_", channelPrefix); } + channelPrefix = getenv("SLURM_JOB_ID"); + if (channelPrefix) { + return fmt::format("@dpl_{}_", channelPrefix); + } return "@"; #else /// Find out a place where we can write the sockets From ec32dbaf91b7ba34f8aef98909df243601c6436b Mon Sep 17 00:00:00 2001 From: Marvin Hemmer Date: Tue, 9 Sep 2025 14:27:01 +0200 Subject: [PATCH 55/99] [Common] Add eta, omega and eta' to PhysicsConstants.h - Add eta, omega and etaPrime meson to the PhysicsConstants.h with PDG value and mass by updating make_pdg_header.py and running the script and copying the output to the header file. --- Common/Constants/include/CommonConstants/PhysicsConstants.h | 6 ++++++ Common/Constants/include/CommonConstants/make_pdg_header.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/Common/Constants/include/CommonConstants/PhysicsConstants.h b/Common/Constants/include/CommonConstants/PhysicsConstants.h index f0198f7a7f61d..46aeff98d6033 100644 --- a/Common/Constants/include/CommonConstants/PhysicsConstants.h +++ b/Common/Constants/include/CommonConstants/PhysicsConstants.h @@ -31,6 +31,9 @@ namespace o2::constants::physics /// \note Follow kCamelCase naming convention /// \link https://root.cern/doc/master/TPDGCode_8h.html enum Pdg { + kEta = 221, + kOmega = 223, + kEtaPrime = 331, kB0 = 511, kB0Bar = -511, kBPlus = 521, @@ -93,6 +96,9 @@ enum Pdg { }; /// \brief Declarations of masses for additional particles +constexpr double MassEta = 0.547862; +constexpr double MassOmega = 0.78266; +constexpr double MassEtaPrime = 0.95778; constexpr double MassB0 = 5.27966; constexpr double MassB0Bar = 5.27966; constexpr double MassBPlus = 5.27934; diff --git a/Common/Constants/include/CommonConstants/make_pdg_header.py b/Common/Constants/include/CommonConstants/make_pdg_header.py index ad24d66e2c3a5..a94450e659acd 100755 --- a/Common/Constants/include/CommonConstants/make_pdg_header.py +++ b/Common/Constants/include/CommonConstants/make_pdg_header.py @@ -86,6 +86,9 @@ class PdgROOT(Enum): # Enum of additional particles class Pdg(Enum): + kEta = 221 + kOmega = 223 + kEtaPrime = 331 kB0 = 511 kB0Bar = -511 kBPlus = 521 From 756c43f3721983f25a538848558988ee5bd87f8d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:29:01 +0200 Subject: [PATCH 56/99] DPL: cleanup gsl usage in DataRelayer --- Framework/Core/src/DataRelayer.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 092e8340a934a..06e920112649e 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -52,7 +52,7 @@ #endif #include #include -#include +#include #include using namespace o2::framework::data_matcher; @@ -191,7 +191,7 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector gsl::span { + auto getPartialRecord = [&cache = mCache, numInputTypes = mDistinctRoutesIndex.size()](int li) -> std::span { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; @@ -710,7 +710,7 @@ void DataRelayer::getReadyToProcess(std::vector& comp // // We use this to bail out early from the check as soon as we find something // which we know is not complete. - auto getPartialRecord = [&cache, &numInputTypes](int li) -> gsl::span { + auto getPartialRecord = [&cache, &numInputTypes](int li) -> std::span { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; From ff6f4cf5b38e5c9eda4b00767703c698b5b7e285 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:16:55 +0200 Subject: [PATCH 57/99] DPL: refactor resource offering to be a function --- Framework/Core/src/ArrowSupport.cxx | 249 ++++++++++++++++------------ 1 file changed, 144 insertions(+), 105 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 94764571840f4..397a6f5113d13 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -13,17 +13,13 @@ #include "Framework/AODReaderHelpers.h" #include "Framework/ArrowContext.h" #include "Framework/ArrowTableSlicingCache.h" -#include "Framework/SliceCache.h" #include "Framework/DataProcessor.h" #include "Framework/DataProcessingStats.h" #include "Framework/ServiceRegistry.h" #include "Framework/ConfigContext.h" -#include "Framework/CommonDataProcessors.h" #include "Framework/DataSpecUtils.h" #include "Framework/DataSpecViews.h" #include "Framework/DeviceSpec.h" -#include "Framework/EndOfStreamContext.h" -#include "Framework/Tracing.h" #include "Framework/DeviceMetricsInfo.h" #include "Framework/DeviceMetricsHelper.h" #include "Framework/DeviceInfo.h" @@ -41,7 +37,6 @@ #include "CommonMessageBackendsHelpers.h" #include #include "Headers/DataHeader.h" -#include "Headers/DataHeaderHelpers.h" #include #include @@ -108,6 +103,135 @@ uint64_t calculateAvailableSharedMemory(ServiceRegistryRef registry) return registry.get().maxMemory; } +struct ResourceState { + int64_t available; + int64_t offered = 0; + int64_t lastDeviceOffered = 0; +}; +struct ResourceStats { + int64_t enoughCount; /// How many times the resources were enough + int64_t lowCount; /// How many times the resources were not enough +}; +struct ResourceSpec { + char const* name; + char const* unit; + char const* api; /// The callback to give resources to a device + int64_t maxAvailable; /// Maximum available quantity for a resource + int64_t maxQuantum; /// Largest offer which can be given + int64_t minQuantum; /// Smallest offer which can be given + int64_t metricOfferScaleFactor; /// The scale factor between the metric accounting and offers accounting +}; + +auto offerResources(ResourceState& resourceState, + ResourceSpec const& resourceSpec, + ResourceStats& resourceStats, + std::vector const& specs, + std::vector const& infos, + DevicesManager& manager, + int64_t offerConsumedCurrentValue, + int64_t offerExpiredCurrentValue, + int64_t acquiredResourceCurrentValue, + int64_t disposedResourceCurrentValue, + size_t timestamp, + DeviceMetricsInfo& driverMetrics, + std::function& availableResourceMetric, + std::function& unusedOfferedResourceMetric, + std::function& offeredResourceMetric, + void* signpostId) -> void +{ + O2_SIGNPOST_ID_FROM_POINTER(sid, rate_limiting, signpostId); + /// We loop over the devices, starting from where we stopped last time + /// offering the minimum offer to each one + int64_t lastCandidate = -1; + int64_t possibleOffer = resourceSpec.minQuantum; + + for (size_t di = 0; di < specs.size(); di++) { + if (resourceState.available < possibleOffer) { + if (resourceStats.lowCount == 0) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "not enough", + "We do not have enough %{public}s (%llu %{public}s) to offer %llu %{public}s. Total offerings %{bytes}llu %{string}s.", + resourceSpec.name, resourceState.available, resourceSpec.unit, + possibleOffer, resourceSpec.unit, + resourceState.offered, resourceSpec.unit); + } + resourceStats.lowCount++; + resourceStats.enoughCount = 0; + break; + } else { + if (resourceStats.enoughCount == 0) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "enough", + "We are back in a state where we enough %{public}s: %llu %{public}s", + resourceSpec.name, + resourceState.available, + resourceSpec.unit); + } + resourceStats.lowCount = 0; + resourceStats.enoughCount++; + } + size_t candidate = (resourceState.lastDeviceOffered + di) % specs.size(); + + auto& info = infos[candidate]; + // Do not bother for inactive devices + // FIXME: there is probably a race condition if the device died and we did not + // took notice yet... + if (info.active == false || info.readyToQuit) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Device %s is inactive not offering %{public}s to it.", + specs[candidate].name.c_str(), resourceSpec.name); + continue; + } + if (specs[candidate].name != "internal-dpl-aod-reader") { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Device %s is not a reader. Not offering %{public}s to it.", + specs[candidate].name.c_str(), + resourceSpec.name); + continue; + } + possibleOffer = std::min(resourceSpec.maxQuantum, resourceState.available); + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Offering %llu %{public}s out of %llu to %{public}s", + possibleOffer, resourceSpec.unit, resourceState.available, specs[candidate].id.c_str()); + manager.queueMessage(specs[candidate].id.c_str(), fmt::format(fmt::runtime(resourceSpec.api), possibleOffer).data()); + resourceState.available -= possibleOffer; + resourceState.offered += possibleOffer; + lastCandidate = candidate; + } + // We had at least a valid candidate, so + // next time we offer to the next device. + if (lastCandidate >= 0) { + resourceState.lastDeviceOffered = lastCandidate + 1; + } + + // unusedOfferedSharedMemory is the amount of memory which was offered and which we know it was + // not used so far. So we need to account for the amount which got actually read (readerBytesCreated) + // and the amount which we know was given back. + static int64_t lastShmOfferConsumed = 0; + static int64_t lastUnusedOfferedMemory = 0; + if (offerConsumedCurrentValue != lastShmOfferConsumed) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "Offer consumed so far %llu", offerConsumedCurrentValue); + lastShmOfferConsumed = offerConsumedCurrentValue; + } + int unusedOfferedMemory = (resourceState.offered - (offerExpiredCurrentValue + offerConsumedCurrentValue) / resourceSpec.metricOfferScaleFactor); + if (lastUnusedOfferedMemory != unusedOfferedMemory) { + O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", + "unusedOfferedMemory:%{bytes}d = offered:%{bytes}llu - (expired:%{bytes}llu + consumed:%{bytes}llu) / %lli", + unusedOfferedMemory, resourceState.offered, + offerExpiredCurrentValue / resourceSpec.metricOfferScaleFactor, + offerConsumedCurrentValue / resourceSpec.metricOfferScaleFactor, + resourceSpec.metricOfferScaleFactor); + lastUnusedOfferedMemory = unusedOfferedMemory; + } + // availableSharedMemory is the amount of memory which we know is available to be offered. + // We subtract the amount which we know was already offered but it's unused and we then balance how + // much was created with how much was destroyed. + resourceState.available = resourceSpec.maxAvailable + ((disposedResourceCurrentValue - acquiredResourceCurrentValue) / resourceSpec.metricOfferScaleFactor) - unusedOfferedMemory; + availableResourceMetric(driverMetrics, resourceState.available, timestamp); + unusedOfferedResourceMetric(driverMetrics, unusedOfferedMemory, timestamp); + + offeredResourceMetric(driverMetrics, resourceState.offered, timestamp); +}; + o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() { using o2::monitoring::Metric; @@ -138,7 +262,6 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() auto &allDeviceMetrics = sm.deviceMetricsInfos; auto &specs = sm.deviceSpecs; auto &infos = sm.deviceInfos; - O2_SIGNPOST_ID_FROM_POINTER(sid, rate_limiting, &sm); static auto stateMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "rate-limit-state"); static auto totalBytesCreatedMetric = DeviceMetricsHelper::createNumericMetric(driverMetrics, "total-arrow-bytes-created"); @@ -288,112 +411,28 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() if (maxTimeframes && (totalTimeframesRead - totalTimeframesConsumed) > maxTimeframes) { return; } - struct ResourceState { - int64_t available; - int64_t offered = 0; - int64_t lastDeviceOffered = 0; - }; - struct ResourceStats { - int64_t enoughCount; - int64_t lowCount; - }; - struct ResourceSpec{ - int64_t maxAvailable; - int64_t maxQuantum; - int64_t minQuantum; - }; - static const ResourceSpec resourceSpec{ + static const ResourceSpec shmResourceSpec{ + .name = "shared memory", + .unit = "MB", + .api = "/shm-offer {}", .maxAvailable = (int64_t)calculateAvailableSharedMemory(registry), .maxQuantum = 100, .minQuantum = 50, + .metricOfferScaleFactor = 1000000, }; - static ResourceState resourceState{ - .available = resourceSpec.maxAvailable, + static ResourceState shmResourceState{ + .available = shmResourceSpec.maxAvailable, }; - static ResourceStats resourceStats{ - .enoughCount = resourceState.available - resourceSpec.minQuantum > 0 ? 1 : 0, - .lowCount = resourceState.available - resourceSpec.minQuantum > 0 ? 0 : 1 + static ResourceStats shmResourceStats{ + .enoughCount = shmResourceState.available - shmResourceSpec.minQuantum > 0 ? 1 : 0, + .lowCount = shmResourceState.available - shmResourceSpec.minQuantum > 0 ? 0 : 1 }; - /// We loop over the devices, starting from where we stopped last time - /// offering MIN_QUANTUM_SHARED_MEMORY of shared memory to each reader. - int64_t lastCandidate = -1; - int64_t possibleOffer = resourceSpec.minQuantum; - - for (size_t di = 0; di < specs.size(); di++) { - if (resourceState.available < possibleOffer) { - if (resourceStats.lowCount == 0) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "not enough", - "We do not have enough shared memory (%{bytes}llu MB) to offer %{bytes}llu MB. Total offerings %{bytes}llu", - resourceState.available, possibleOffer, resourceState.offered); - } - resourceStats.lowCount++; - resourceStats.enoughCount = 0; - break; - } else { - if (resourceStats.enoughCount == 0) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "enough", - "We are back in a state where we enough shared memory: %{bytes}llu MB", resourceState.available); - } - resourceStats.lowCount = 0; - resourceStats.enoughCount++; - } - size_t candidate = (resourceState.lastDeviceOffered + di) % specs.size(); - - auto& info = infos[candidate]; - // Do not bother for inactive devices - // FIXME: there is probably a race condition if the device died and we did not - // took notice yet... - if (info.active == false || info.readyToQuit) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Device %s is inactive not offering memory to it.", specs[candidate].name.c_str()); - continue; - } - if (specs[candidate].name != "internal-dpl-aod-reader") { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Device %s is not a reader. Not offering memory to it.", specs[candidate].name.c_str()); - continue; - } - possibleOffer = std::min(resourceSpec.maxQuantum, resourceState.available); - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Offering %{bytes}llu MB out of %{bytes}llu to %{public}s", - possibleOffer, resourceState.available, specs[candidate].id.c_str()); - manager.queueMessage(specs[candidate].id.c_str(), fmt::format("/shm-offer {}", possibleOffer).data()); - resourceState.available -= possibleOffer; - resourceState.offered += possibleOffer; - lastCandidate = candidate; - } - // We had at least a valid candidate, so - // next time we offer to the next device. - if (lastCandidate >= 0) { - resourceState.lastDeviceOffered = lastCandidate + 1; - } - - // unusedOfferedSharedMemory is the amount of memory which was offered and which we know it was - // not used so far. So we need to account for the amount which got actually read (readerBytesCreated) - // and the amount which we know was given back. - static int64_t lastShmOfferConsumed = 0; - static int64_t lastUnusedOfferedMemory = 0; - if (shmOfferBytesConsumed != lastShmOfferConsumed) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "Offer consumed so far %{bytes}llu", shmOfferBytesConsumed); - lastShmOfferConsumed = shmOfferBytesConsumed; - } - int unusedOfferedMemory = (resourceState.offered - (totalBytesExpired + shmOfferBytesConsumed) / 1000000); - if (lastUnusedOfferedMemory != unusedOfferedMemory) { - O2_SIGNPOST_EVENT_EMIT(rate_limiting, sid, "offer", - "unusedOfferedMemory:%{bytes}d = offered:%{bytes}llu - (expired:%{bytes}llu + consumed:%{bytes}llu) / 1000000", - unusedOfferedMemory, resourceState.offered, totalBytesExpired / 1000000, shmOfferBytesConsumed / 1000000); - lastUnusedOfferedMemory = unusedOfferedMemory; - } - // availableSharedMemory is the amount of memory which we know is available to be offered. - // We subtract the amount which we know was already offered but it's unused and we then balance how - // much was created with how much was destroyed. - resourceState.available = resourceSpec.maxAvailable + ((totalBytesDestroyed - totalBytesCreated) / 1000000) - unusedOfferedMemory; - availableSharedMemoryMetric(driverMetrics, resourceState.available, timestamp); - unusedOfferedSharedMemoryMetric(driverMetrics, unusedOfferedMemory, timestamp); - - offeredSharedMemoryMetric(driverMetrics, resourceState.offered, timestamp); }, + offerResources(shmResourceState, shmResourceSpec, shmResourceStats, + specs, infos, manager, shmOfferBytesConsumed, totalBytesExpired, + totalBytesCreated, totalBytesDestroyed, timestamp, driverMetrics, + availableSharedMemoryMetric, unusedOfferedSharedMemoryMetric, offeredSharedMemoryMetric, + (void*)&sm); }, .postDispatching = [](ProcessingContext& ctx, void* service) { using DataHeader = o2::header::DataHeader; auto* arrow = reinterpret_cast(service); From 987751624a001157f756139bf1bc7ffb59552cb9 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 22 Sep 2025 13:35:46 +0200 Subject: [PATCH 58/99] ITS3: add some services material and update MatLUT macro Signed-off-by: Felix Schlepper --- .../ITS3/base/include/ITS3Base/SpecsV2.h | 21 ++- .../ITS3/macros/test/buildMatBudLUT.C | 124 +++++++++--------- .../DescriptorInnerBarrelITS3.h | 7 +- .../src/DescriptorInnerBarrelITS3.cxx | 3 - .../ITS3/simulation/src/ITS3Layer.cxx | 2 +- .../ITS3/simulation/src/ITS3Services.cxx | 21 ++- 6 files changed, 99 insertions(+), 79 deletions(-) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index cb6af1dcfc5b7..6626650359dd3 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -28,9 +28,9 @@ // color: for visulisation namespace o2::its3::constants { -constexpr double cm{1e+2}; // This is the default unit of TGeo so we use this as scale -constexpr double mu{1e-6 * cm}; -constexpr double mm{1e-3 * cm}; +constexpr double cm{1.0}; // This is the default unit of TGeo so we use this as scale +constexpr double mu{1e-4 * cm}; +constexpr double mm{1e-1 * cm}; namespace pixelarray { constexpr double width{9.197 * mm}; @@ -108,7 +108,8 @@ constexpr double HringLength{6.0 * mm}; // fr constexpr double edgeBetwChipAndFoam{1.0 * mm}; // from blueprint but not used cause forms are already overlapping constexpr double gapBetwHringsLongerons{0.05 * mm}; // from blueprint constexpr std::array nHoles{11, 11, 11}; // how many holes for each layer? -constexpr std::array radiusHoles{1.0 * mm, 1.0 * mm, 2.0 * mm}; // what is the radius of the holes for each layer? +constexpr std::array radiusHoles{1.0 * mm, 1.0 * mm, 2.0 * mm}; // TODO what is the radius of the holes for each layer? +constexpr double thicknessOuterFoam{7 * mm}; // TODO: lack of carbon foam radius for layer 2, use 0.7 cm as a temporary value constexpr EColor color{kGray}; } // namespace carbonfoam namespace metalstack @@ -212,6 +213,18 @@ inline bool isDetITS3(T detID) } } // namespace detID + +// services +namespace services +{ +// FIXME these value are hallucinated since this not yet defined +constexpr double thickness{2.2 * mm}; // thickness of structure +constexpr double radiusInner{radiiOuter[2] + carbonfoam::thicknessOuterFoam}; // inner radius of services +constexpr double radiusOuter{radiusInner + thickness}; // outer radius of services +constexpr double length{20 * cm}; // length +constexpr EColor color{kBlue}; +} // namespace services + } // namespace o2::its3::constants #endif diff --git a/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C b/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C index bfa2f3bede70d..6341338835b51 100644 --- a/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C +++ b/Detectors/Upgrades/ITS3/macros/test/buildMatBudLUT.C @@ -18,6 +18,7 @@ #include "DetectorsBase/GeometryManager.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" +#include "ITS3Base/SpecsV2.h" #include "CommonUtils/NameConf.h" #include #include @@ -30,7 +31,7 @@ o2::base::MatLayerCylSet mbLUT; bool testMBLUT(const std::string& lutFile = "matbud.root"); -bool buildMatBudLUT(int nTst = 30, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomName = ""); +bool buildMatBudLUT(int nTst = 60, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomName = ""); struct LrData { float rMin = 0.f; @@ -50,7 +51,7 @@ bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std:: auto geomName = o2::base::NameConf::getGeomFileName(geomNameInput); if (gSystem->AccessPathName(geomName.c_str())) { // if needed, create geometry std::cout << geomName << " does not exist. Will create it\n"; - gSystem->Exec("$O2_ROOT/bin/o2-sim -n 0"); + gSystem->Exec("$O2_ROOT/bin/o2-sim -n 0 --detectorList ALICE2.1"); } o2::base::GeometryManager::loadGeometry(geomNameInput); configLayers(); @@ -62,7 +63,7 @@ bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std:: } for (int i = 0; i < maxLr; i++) { auto& l = lrData[i]; - printf("L:%3d %6.2f247 rphiBin = 2.; zBin = 3.; - lrData.emplace_back(LrData(lrData.back().rMax, 258., zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, 258., zSpanH, zBin, rphiBin); zSpanH = 247.f; // ignore large lumps of material at |z|>247 rphiBin = 2.; zBin = 999.; // no segmentation in Z - lrData.emplace_back(LrData(lrData.back().rMax, 280., zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, 280., zSpanH, zBin, rphiBin); // TRD @@ -376,7 +372,7 @@ void configLayers() do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin); } while (lrData.back().rMax < 370); // TOF @@ -387,7 +383,7 @@ void configLayers() do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin); } while (lrData.back().rMax < 400); // rest @@ -398,7 +394,7 @@ void configLayers() zSpanH = lrData.back().rMax; auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); + lrData.emplace_back(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin); } while (lrData.back().rMax < 500); } diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h index d1b54f81face4..7a7d2215eb67c 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h @@ -45,9 +45,10 @@ class DescriptorInnerBarrelITS3 : public o2::its::DescriptorInnerBarrel int mNumLayers{constants::nLayers}; // wrapper volume properties - static constexpr double mWrapperMinRadiusITS3{1.8}; - static constexpr double mWrapperMaxRadiusITS3{4.}; - static constexpr double mWrapperZSpanITS3{constants::segment::length + 5.}; + static constexpr double mTolerance{1e-3}; + static constexpr double mWrapperMinRadiusITS3{constants::radiiInner[0] - mTolerance}; + static constexpr double mWrapperMaxRadiusITS3{constants::services::radiusOuter + mTolerance}; + static constexpr double mWrapperZSpanITS3{constants::services::length + mTolerance}; private: std::array, constants::nLayers> mIBLayers; diff --git a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx index 540e1d41f1c62..04f244284d5b6 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx @@ -10,7 +10,6 @@ // or submit itself to any jurisdiction. #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" -#include "fairlogger/Logger.h" using namespace o2::its3; @@ -18,14 +17,12 @@ ClassImp(DescriptorInnerBarrelITS3); void DescriptorInnerBarrelITS3::createLayer(int iLayer, TGeoVolume* dest) { - LOGP(debug, "ITS3-IB: Creating Layer {}", iLayer); mIBLayers[iLayer] = std::make_unique(iLayer); mIBLayers[iLayer]->createLayer(dest); } void DescriptorInnerBarrelITS3::createServices(TGeoVolume* dest) { - LOGP(debug, "ITS3-IB: Creating Services"); mServices = std::make_unique(); mServices->createCYSSAssembly(dest); } diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index 8dc94e339c793..3bf29b17fa1f1 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx @@ -293,7 +293,7 @@ void ITS3Layer::createCarbonForm() if (mNLayer < 2) { dRadius = constants::radii[mNLayer + 1] - constants::radii[mNLayer] - constants::totalThickness; } else { - dRadius = 0.7; // TODO: lack of carbon foam radius for layer 2, use 0.7mm as a temporary value + dRadius = constants::carbonfoam::thicknessOuterFoam; // TODO: lack of carbon foam radius for layer 2, use 0.7 cm as a temporary value } double phiSta = edgeBetwChipAndFoam / (0.5 * constants::radii[mNLayer + 1] + constants::radii[mNLayer]) * o2m::Rad2Deg; double phiEnd = (constants::nSegments[mNLayer] * constants::segment::width) / constants::radii[mNLayer] * o2m::Rad2Deg - phiSta; diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx index cc2255a2b2085..e5ad6a4a1e034 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Services.cxx @@ -13,17 +13,30 @@ /// \brief Definition of the ITS3Services class /// \author Fabrizio Grosa -#include "ITS3Simulation/ITS3Services.h" +#include +#include +#include -#include // for LOG +#include "ITS3Simulation/ITS3Services.h" +#include "ITS3Base/SpecsV2.h" namespace o2::its3 { void ITS3Services::createCYSSAssembly(TGeoVolume* motherVolume) { - // Return the whole assembly - LOGP(info, "Creating CYSS Assembly and attaching to {}", motherVolume->GetName()); + auto cyssVol = new TGeoVolumeAssembly("IBCYSSAssembly"); + cyssVol->SetVisibility(kTRUE); + motherVolume->AddNode(cyssVol, 1., nullptr); + + // Cylinder + auto cyssInnerCylSh = new TGeoTubeSeg(constants::services::radiusInner, constants::services::radiusOuter, constants::services::length, 180, 360); + auto medPrepreg = gGeoManager->GetMedium("IT3_AS4C200$"); + auto cyssInnerCylShVol = new TGeoVolume("IBCYSSCylinder", cyssInnerCylSh, medPrepreg); + cyssVol->AddNode(cyssInnerCylShVol, 1, new TGeoTranslation(0, 0, 0)); + cyssVol->AddNode(cyssInnerCylShVol, 2, new TGeoCombiTrans(0, 0, 0, new TGeoRotation("", 180, 0, 0))); + + // TODO Cone } } // namespace o2::its3 From f609c3dcd3f1151742afde7ab8eaed86164e899b Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 23 Sep 2025 16:18:26 +0200 Subject: [PATCH 59/99] ITS3: ensure matrices are cached Signed-off-by: Felix Schlepper --- Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index 8bfc7eedf2d6f..8af893267b510 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -33,6 +33,8 @@ void convertCompactClusters(gsl::span clusters, const its3::TopologyDictionary* dict) { auto geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + bool applyMisalignment = false; const auto& conf = o2::its::TrackerParamConfig::Instance(); for (int il = 0; il < geom->getNumberOfLayers(); ++il) { From cef07ed479c0cd230496e796083462cc4ac5cef6 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 25 Sep 2025 15:58:33 +0200 Subject: [PATCH 60/99] ITS3: remove unnecessary recalculation of trackingframe Signed-off-by: Felix Schlepper --- .../ITS/base/include/ITSBase/GeometryTGeo.h | 3 --- Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx | 13 ------------- .../ITS3/macros/align/CheckResidualsITS3.C | 11 +++-------- .../ITS3/reconstruction/src/IOUtils.cxx | 17 +++++------------ 4 files changed, 8 insertions(+), 36 deletions(-) diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index fcdc978fa64f0..934c927ac3059 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -333,9 +333,6 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo /// Sym name of the chip in the given layer/halfbarrel/stave/substave/module static const char* composeSymNameChip(int lr, int hba, int sta, int ssta, int mod, int chip, bool isITS3 = false); - // create matrix for transformation from tracking frame to local one for ITS3 - const Mat3D getT2LMatrixITS3(int isn, float alpha); - TString getMatrixPath(int index) const; /// Get the transformation matrix of the SENSOR (not necessary the same as the chip) diff --git a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index 89b4d63729543..60570b2f204c5 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -899,19 +899,6 @@ TGeoHMatrix& GeometryTGeo::createT2LMatrix(int isn) return t2l; } -//__________________________________________________________________________ -const o2::math_utils::Transform3D GeometryTGeo::getT2LMatrixITS3(int isn, float alpha) -{ - // create for sensor isn the TGeo matrix for Tracking to Local frame transformations with correction for effective thickness - static TGeoHMatrix t2l; - t2l.Clear(); - t2l.RotateZ(alpha * RadToDeg()); // rotate in direction of normal to the tangent to the cylinder - const TGeoHMatrix& matL2G = getMatrixL2G(isn); - const auto& matL2Gi = matL2G.Inverse(); - t2l.MultiplyLeft(&matL2Gi); - return Mat3D(t2l); -} - //__________________________________________________________________________ int GeometryTGeo::extractVolumeCopy(const char* name, const char* prefix) const { diff --git a/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C b/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C index 9d352393d6fd9..88b342683ca44 100644 --- a/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C +++ b/Detectors/Upgrades/ITS3/macros/align/CheckResidualsITS3.C @@ -66,16 +66,11 @@ std::optional propagateTo(Track& trk, const o2::itsmft::CompClusterExt& ++cTotal; auto chipID = clus.getSensorID(); float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; + auto isITS3 = o2::its3::constants::detID::isDetITS3(chipID); const float alpha = o2::its::GeometryTGeo::Instance()->getSensorRefAlpha(clus.getSensorID()); // alpha for the tracking frame const auto locC = o2::its3::ioutils::extractClusterData(clus, pattIt, mDict, sigmaY2, sigmaZ2); // get cluster in sensor local frame with errors - Point3D trkC; - auto isITS3 = o2::its3::constants::detID::isDetITS3(chipID); - if (isITS3) { - trkC = o2::its::GeometryTGeo::Instance()->getT2LMatrixITS3(chipID, alpha) ^ (locC); // cluster position in the tracking frame - } else { - trkC = o2::its::GeometryTGeo::Instance()->getMatrixT2L(chipID) ^ (locC); // cluster position in the tracking frame - } - const auto gloC = o2::its::GeometryTGeo::Instance()->getMatrixL2G(chipID)(locC); // global cluster position + Point3D trkC = o2::its::GeometryTGeo::Instance()->getMatrixT2L(chipID) ^ (locC); // cluster position in the tracking frame + const auto gloC = o2::its::GeometryTGeo::Instance()->getMatrixL2G(chipID)(locC); // global cluster position const auto bz = o2::base::Propagator::Instance()->getNominalBz(); // rotate the parameters to the tracking frame then propagate to the clusters'x diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index 8af893267b510..d7ba4d48dbce4 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -48,8 +48,7 @@ void convertCompactClusters(gsl::span clusters, float sigmaY2, sigmaZ2, sigmaYZ = 0; auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2); const auto detID = c.getSensorID(); - auto& cl3d = output.emplace_back(detID, - (its3::constants::detID::isDetITS3(detID) ? geom->getT2LMatrixITS3(detID, geom->getSensorRefAlpha(detID)) : geom->getMatrixT2L(detID)) ^ locXYZ); // local --> tracking + auto& cl3d = output.emplace_back(detID, geom->getMatrixT2L(detID) ^ locXYZ); // local --> tracking if (applyMisalignment) { auto lrID = geom->getLayer(detID); sigmaY2 += conf.sysErrY2[lrID]; @@ -79,7 +78,6 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { auto& c = clusters[clusterId]; auto sensorID = c.getSensorID(); - auto isITS3 = its3::constants::detID::isDetITS3(sensorID); auto layer = geom->getLayer(sensorID); float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; @@ -90,16 +88,11 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, // Transformation to the local --> global auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - // for cylindrical layers we have a different alpha for each cluster, for regular silicon detectors instead a single alpha for the whole sensor + // Inverse transformation to the local --> tracking + o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; + + // Tracking alpha angle float alpha = geom->getSensorRefAlpha(sensorID); - o2::math_utils::Point3D trkXYZ; - if (isITS3) { - // Inverse transformation to the local --> tracking - trkXYZ = geom->getT2LMatrixITS3(sensorID, alpha) ^ locXYZ; - } else { - // Inverse transformation to the local --> tracking - trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; - } tf->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, std::array{trkXYZ.y(), trkXYZ.z()}, From 25e9acf823facfefafffee714019e181004bebd6 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 2 Oct 2025 09:21:38 +0200 Subject: [PATCH 61/99] ITS3: fix longeron length to not clip into Hring Signed-off-by: Felix Schlepper --- Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h | 4 ++-- Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index 6626650359dd3..b56bb3fa2bd98 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -102,9 +102,9 @@ constexpr double lengthSensitive{nRSUs * rsu::length}; namespace carbonfoam { // TODO: Waiting for the further information from WP5(Corrado) -constexpr double longeronsWidth{2.0 * mm}; // what is the height of the longerons? -constexpr double longeronsLength{263 * mm}; // from blueprint constexpr double HringLength{6.0 * mm}; // from blueprint +constexpr double longeronsWidth{2.0 * mm}; // what is the height of the longerons? +constexpr double longeronsLength{segment::length - 2 * HringLength}; // 263mm from blueprint; overrriden to be consitent constexpr double edgeBetwChipAndFoam{1.0 * mm}; // from blueprint but not used cause forms are already overlapping constexpr double gapBetwHringsLongerons{0.05 * mm}; // from blueprint constexpr std::array nHoles{11, 11, 11}; // how many holes for each layer? diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index 3bf29b17fa1f1..e0be011096450 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx @@ -312,8 +312,8 @@ void ITS3Layer::createCarbonForm() auto zMoveHringA = new TGeoTranslation(0, 0, -constants::segment::lec::length + HringLength / 2. + constants::segment::length - HringLength); // Longerons are made by same material - [[maybe_unused]] auto longeronR = new TGeoTubeSeg(Form("longeronR%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2, phiSta, phiSta + phiLongeronsCover); - [[maybe_unused]] auto longeronL = new TGeoTubeSeg(Form("longeronL%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2, phiEnd - phiLongeronsCover, phiEnd); + [[maybe_unused]] auto longeronR = new TGeoTubeSeg(Form("longeronR%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiSta, phiSta + phiLongeronsCover); + [[maybe_unused]] auto longeronL = new TGeoTubeSeg(Form("longeronL%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiEnd - phiLongeronsCover, phiEnd); TString nameLongerons = Form("longeronR%d + longeronL%d", mNLayer, mNLayer); auto longerons = new TGeoCompositeShape(nameLongerons); auto longeronsVol = new TGeoVolume(Form("longerons%d", mNLayer), longerons, mCarbon); From dc4d11145225aef5bc77cf287c2cb373cd0acebb Mon Sep 17 00:00:00 2001 From: ddobrigk Date: Fri, 10 Oct 2025 23:13:12 +0200 Subject: [PATCH 62/99] Set default DCA in case of propagate call fail (#14729) * Set default DCA in case of propagate call fail * Please consider the following formatting changes * Add missing setter lines for propagateToDCAs * Please consider the following formatting changes --------- Co-authored-by: David Dobrigkeit Chinellato Co-authored-by: ALICE Action Bot --- .../TrackParametrization.h | 3 ++ .../src/TrackParametrization.cxx | 8 +++++ .../src/TrackParametrizationWithError.cxx | 8 +++++ Detectors/Base/src/Propagator.cxx | 32 +++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h index 87cd059e0b325..8cb22efd39e38 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h @@ -119,6 +119,9 @@ constexpr float MaxPT = 100000.; // do not allow pTs exceeding constexpr float MinPTInv = 1. / MaxPT; // do not allow q/pTs less this value (to avoid NANs) constexpr float ELoss2EKinThreshInv = 1. / 0.025; // do not allow E.Loss correction step with dE/Ekin above the inverse of this value constexpr int MaxELossIter = 50; // max number of iteration for the ELoss to account for BB dependence on beta*gamma +constexpr float DefaultDCA = 999.f; // default DCA value +constexpr float DefaultDCACov = 999.f; // default DCA cov value + // uncomment this to enable correction for BB dependence on beta*gamma via BB derivative // #define _BB_NONCONST_CORR_ diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx index 0539278acb20b..7086e4d93cec8 100644 --- a/DataFormats/Reconstruction/src/TrackParametrization.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx @@ -378,6 +378,10 @@ GPUd() bool TrackParametrization::propagateParamToDCA(const math_utils: // Estimate the impact parameter neglecting the track curvature value_t d = gpu::CAMath::Abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } value_t crv = getCurvature(b); @@ -399,6 +403,10 @@ GPUd() bool TrackParametrization::propagateParamToDCA(const math_utils: #else LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z(); #endif + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } *this = tmpT; diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx index aee24238f1247..01849bd0c9e8f 100644 --- a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx @@ -227,6 +227,10 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat // Estimate the impact parameter neglecting the track curvature value_t d = gpu::CAMath::Abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } value_t crv = this->getCurvature(b); @@ -245,6 +249,10 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat #if !defined(GPUCA_ALIGPUCODE) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: " << tmpT.asString(); #endif + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } *this = tmpT; diff --git a/Detectors/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index b6112cd5ba32e..0763eb48ff474 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -564,6 +564,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const o2::dataformats::Verte // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } value_type crv = track.getCurvature(bZ); @@ -584,6 +588,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const o2::dataformats::Verte #elif !defined(GPUCA_NO_FMT) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx; #endif + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } track = tmpT; @@ -613,6 +621,10 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } value_type crv = track.getCurvature(mNominalBz); @@ -633,6 +645,10 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: #elif !defined(GPUCA_NO_FMT) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx; #endif + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } track = tmpT; @@ -662,6 +678,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } value_type crv = track.getCurvature(bZ); @@ -683,6 +703,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D::propagateToDCABxByBz(const math_utils::Poin // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } value_type crv = track.getCurvature(mNominalBz); @@ -731,6 +759,10 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Poin #else LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z(); #endif + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } track = tmpT; From fbec135146466349c9a5ad6b6effed2b1ae70007 Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Mon, 13 Oct 2025 21:14:51 +0200 Subject: [PATCH 63/99] ALICE3-TRK: several fixes in the digitization code (#14733) * ALICE3-TRK: deal with ML/OT L4 mixed lenght and shape * ALICE3-TRK: adjust ML/OT staves length according to the current geometry * ALICE3-TRK: increase number of steps into silicon to 25. Setting temporarly the threshold to 1 electron * ALICE3-TRK: fix digits distribution along columns by removing the rescaling of the silicon depth * ALICE3-TRK: considering the OT half-stave as the smallest elemenent for the digitization in the OT --- .../base/include/TRKBase/SegmentationChip.h | 70 ++++++++++++------- .../ALICE3/TRK/base/include/TRKBase/Specs.h | 19 +++-- .../include/TRKSimulation/DPLDigitizerParam.h | 6 +- .../include/TRKSimulation/DigiParams.h | 6 +- .../ALICE3/TRK/simulation/src/Digitizer.cxx | 43 ++++-------- 5 files changed, 80 insertions(+), 64 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h index 100af5be1b4d0..e2a60e8a3b576 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h @@ -35,18 +35,18 @@ class SegmentationChip // The "detector coordinate system" refers to the hit position in row,col inside the sensor // This class provides the transformations from the local and detector coordinate systems // The conversion between global and local coordinate systems is operated by the transformation matrices - // For the curved VD layers there exist three coordinate systems and one is transient. + // For the curved VD layers there exist four coordinate systems. // 1. The global (curved) coordinate system. The chip's center of coordinate system is // defined at the the mid-point of the detector. - // 2. The local (flat) coordinate system. This is the tube segment projected onto a flat - // surface. In the projection we implicitly assume that the inner and outer - // stretch does not depend on the radius. - // 3. The detector coordinate system. Defined by the row and column segmentation - // defined at the upper edge in the flat coord. + // 2. The local (curved) coordinate system, centered in 0,0,0. + // 3. The local (flat) coordinate system. This is the tube segment projected onto a flat + // surface, centered in the middle of the chip, with the y axis pointing towards the interaction point. + // In the projection we implicitly assume that the inner and outer stretch does not depend on the radius. + // 4. The detector coordinate system. Defined by the row and column segmentation. // For the flat ML and OT layers, there exist two coordinate systems: // 1. The global (flat) coordinate system. The chip's center of coordinate system is // defined at the the mid-point of the detector. - // 2. The detector coordinate system. Defined by the row and column segmentation + // 2. The detector coordinate system. Defined by the row and column segmentation. // TODO: add segmentation for VD disks public: @@ -121,15 +121,20 @@ class SegmentationChip pitchCol = PitchColMLOT; maxWidth = constants::ML::width; maxLength = constants::ML::length; - } else if (subDetID == 1 && layer >= 4) { // OT + } else if (subDetID == 1 && layer == 4) { // ML/OT (mixed layer, length = ML but staggered as OT) pitchRow = PitchRowMLOT; pitchCol = PitchColMLOT; - maxWidth = constants::OT::width; - maxLength = constants::OT::length; + maxWidth = constants::OT::halfstave::width; + maxLength = constants::ML::length; + } else if (subDetID == 1 && layer > 4) { // OT + pitchRow = PitchRowMLOT; + pitchCol = PitchColMLOT; + maxWidth = constants::OT::halfstave::width; + maxLength = constants::OT::halfstave::length; } // convert to row/col - iRow = static_cast(std::floor((maxWidth / 2 - xRow) / pitchRow)); - iCol = static_cast(std::floor((zCol + maxLength / 2) / pitchCol)); + iRow = static_cast(((maxWidth / 2 - xRow) / pitchRow)); + iCol = static_cast(((zCol + maxLength / 2) / pitchCol)); }; // Check local coordinates (cm) validity. @@ -143,9 +148,12 @@ class SegmentationChip } else if (subDetID == 1 && layer <= 3) { // ML maxWidth = constants::ML::width; maxLength = constants::ML::length; - } else if (subDetID == 1 && layer >= 4) { // OT - maxWidth = constants::OT::width; - maxLength = constants::OT::length; + } else if (subDetID == 1 && layer == 4) { // ML/OT (mixed layer, length = ML but staggered as OT) + maxWidth = constants::OT::halfstave::width; + maxLength = constants::ML::length; + } else if (subDetID == 1 && layer > 4) { // OT + maxWidth = constants::OT::halfstave::width; + maxLength = constants::OT::halfstave::length; } return (-maxWidth / 2 < x && x < maxWidth / 2 && -maxLength / 2 < z && z < maxLength / 2); } @@ -162,9 +170,12 @@ class SegmentationChip } else if (subDetID == 1 && layer <= 3) { // ML nRows = constants::ML::nRows; nCols = constants::ML::nCols; - } else if (subDetID == 1 && layer >= 4) { // OT - nRows = constants::OT::nRows; - nCols = constants::OT::nCols; + } else if (subDetID == 1 && layer == 4) { // ML/OT (mixed layer, length = ML but staggered as OT) + nRows = constants::OT::halfstave::nRows; + nCols = constants::ML::nCols; + } else if (subDetID == 1 && layer > 4) { // OT + nRows = constants::OT::halfstave::nRows; + nCols = constants::OT::halfstave::nCols; } return (row >= 0 && row < static_cast(nRows) && col >= 0 && col < static_cast(nCols)); } @@ -210,9 +221,12 @@ class SegmentationChip } else if (subDetID == 1 && layer <= 3) { // ML xRow = 0.5 * (constants::ML::width - PitchRowMLOT) - (row * PitchRowMLOT); zCol = col * PitchRowMLOT + 0.5 * (PitchRowMLOT - constants::ML::length); - } else if (subDetID == 1 && layer >= 4) { // OT - xRow = 0.5 * (constants::OT::width - PitchRowMLOT) - (row * PitchRowMLOT); - zCol = col * PitchColMLOT + 0.5 * (PitchColMLOT - constants::OT::length); + } else if (subDetID == 1 && layer == 4) { // ML/OT (mixed layer, length = ML but staggered as OT) + xRow = 0.5 * (constants::OT::halfstave::width - PitchRowMLOT) - (row * PitchRowMLOT); + zCol = col * PitchRowMLOT + 0.5 * (PitchRowMLOT - constants::ML::length); + } else if (subDetID == 1 && layer > 4) { // OT + xRow = 0.5 * (constants::OT::halfstave::width - PitchRowMLOT) - (row * PitchRowMLOT); + zCol = col * PitchColMLOT + 0.5 * (PitchColMLOT - constants::OT::halfstave::length); } } @@ -263,17 +277,25 @@ class SegmentationChip } /// Print segmentation info - static const void Print() noexcept + static void Print() noexcept { LOG(info) << "Number of rows:\nVD L0: " << constants::VD::petal::layer::nRows[0] << "\nVD L1: " << constants::VD::petal::layer::nRows[1] << "\nVD L2: " << constants::VD::petal::layer::nRows[2] << "\nML stave: " << constants::ML::nRows - << "\nOT stave: " << constants::OT::nRows; + << "\nOT half stave: " << constants::OT::halfstave::nRows; LOG(info) << "Number of cols:\nVD: " << constants::VD::petal::layer::nCols << "\nML stave: " << constants::ML::nCols - << "\nOT stave: " << constants::OT::nCols; + << "\nOT half stave: " << constants::OT::halfstave::nCols; + + LOG(info) << "Pitch rows [cm]:\nVD: " << PitchRowVD + << "\nML stave: " << PitchRowMLOT + << "\nOT stave: " << PitchRowMLOT; + + LOG(info) << "Pitch cols [cm]:\nVD: " << PitchColVD + << "\nML stave: " << PitchColMLOT + << "\nOT stave: " << PitchColMLOT; } }; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index 373e9d972656b..bd95e5207b7ee 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -102,16 +102,25 @@ constexpr int nCols{static_cast(length / chip::pitchZ)}; namespace ML { -constexpr double width{constants::moduleMLOT::width * 1}; // width of the stave -constexpr double length{constants::moduleMLOT::length * 10}; // length of the stave +constexpr double width{constants::moduleMLOT::width * 1}; // width of the stave +// constexpr double length{constants::moduleMLOT::length * 10}; // length of the stave +constexpr double length{124 * cm}; // length of the stave, hardcoded to fit the implemented geometry constexpr int nRows{static_cast(width / constants::moduleMLOT::chip::pitchX)}; // number of rows in the stave constexpr int nCols{static_cast(length / constants::moduleMLOT::chip::pitchZ)}; // number of columns in the stave } // namespace ML namespace OT -{ //// TODO: add shorter lenght of the stave of L4 -constexpr double width{moduleMLOT::width * 2}; // width of the stave -constexpr double length{moduleMLOT::length * 20}; // length of the stave +{ +namespace halfstave +{ +constexpr double width{moduleMLOT::width * 1}; // width of the half stave +// constexpr double length{moduleMLOT::length * 20}; // length of the halfstave +constexpr double length{258 * cm}; // length of the halfstave, hardcoded to fit the implemented geometry +constexpr int nRows{static_cast(width / moduleMLOT::chip::pitchX)}; // number of rows in the halfstave +constexpr int nCols{static_cast(length / moduleMLOT::chip::pitchZ)}; // number of columns in the halfstave +} // namespace halfstave +constexpr double width{halfstave::width * 2}; // width of the stave +constexpr double length{halfstave::length}; // length of the stave constexpr int nRows{static_cast(width / moduleMLOT::chip::pitchX)}; // number of rows in the stave constexpr int nCols{static_cast(length / moduleMLOT::chip::pitchZ)}; // number of columns in the stave } // namespace OT diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h index 584ffaa3aff75..179b666a159d6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h @@ -37,9 +37,9 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelpergetDepthMax(); - LOG(debug) << " Depth min: " << mChipSimRespVD->getDepthMin(); + LOG(info) << " Depth max VD: " << mChipSimRespVD->getDepthMax(); + LOG(info) << " Depth min VD: " << mChipSimRespVD->getDepthMin(); + + LOG(info) << " Depth max MLOT: " << mChipSimRespMLOT->getDepthMax(); + LOG(info) << " Depth min MLOT: " << mChipSimRespMLOT->getDepthMin(); float thicknessVD = 0.0095; // cm --- hardcoded based on geometry currently present float thicknessMLOT = 0.1; // cm --- hardcoded based on geometry currently present mSimRespVDScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowVD; mSimRespVDScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColVD; - mSimRespVDScaleDepth = o2::trk::constants::apts::thickness / (thicknessVD); /// introducing this scaling factor because the silicon thickness for the moment is 1 mm -> rescale to 45 um which is the depth of the APTS response - // mSimRespVDShift = mChipSimRespVD->getDepthMax() - thicknessVD * mSimRespVDScaleDepth / 2.f; // the shift should be done considering the rescaling done to adapt to the wrong silicon thickness. TODO: remove the scaling factor for the depth when the silicon thickness match the simulated response - mSimRespVDShift = mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -45. Must add 10 um (= max depth) to match the APTS response. + mSimRespVDShift = -mChipSimRespVD->getDepthMax(); // the curved, rescaled, sensors have a width from 0 to -45. Must add 10 um (= max depth) to match the APTS response. mSimRespMLOTScaleX = o2::trk::constants::apts::pitchX / o2::trk::SegmentationChip::PitchRowMLOT; mSimRespMLOTScaleZ = o2::trk::constants::apts::pitchZ / o2::trk::SegmentationChip::PitchColMLOT; - mSimRespMLOTScaleDepth = o2::trk::constants::apts::thickness / (thicknessMLOT); /// introducing this scaling factor because the silicon thickness for the moment is 1 mm -> rescale to 45 um which is the depth of the APTS response - mSimRespMLOTShift = mChipSimRespMLOT->getDepthMax() - thicknessMLOT * mSimRespMLOTScaleDepth / 2.f; // the shift should be done considering the rescaling done to adapt to the wrong silicon thickness. TODO: remove the scaling factor for the depth when the silicon thickness match the simulated response + mSimRespMLOTShift = mChipSimRespMLOT->getDepthMax() - thicknessMLOT / 2.f; // the shift should be done considering the rescaling done to adapt to the wrong silicon thickness. TODO: remove the scaling factor for the depth when the silicon thickness match the simulated response mSimRespOrientation = false; // importing the parameters from DPLDigitizerParam.h @@ -84,7 +84,7 @@ void Digitizer::init() LOGP(info, "TRK Digitizer is initialised."); mParams.print(); - LOGP(info, "VD shift = {} ; ML/OT shift = {} = {} - {}", mSimRespVDShift, mSimRespMLOTShift, mChipSimRespMLOT->getDepthMax(), thicknessMLOT * mSimRespMLOTScaleDepth / 2.f); + LOGP(info, "VD shift = {} ; ML/OT shift = {} = {} - {}", mSimRespVDShift, mSimRespMLOTShift, mChipSimRespMLOT->getDepthMax(), thicknessMLOT / 2.f); LOGP(info, "VD pixel scale on x = {} ; z = {}", mSimRespVDScaleX, mSimRespVDScaleZ); LOGP(info, "ML/OT pixel scale on x = {} ; z = {}", mSimRespMLOTScaleX, mSimRespMLOTScaleZ); LOGP(info, "Response orientation: {}", mSimRespOrientation ? "flipped" : "normal"); @@ -115,8 +115,8 @@ void Digitizer::process(const std::vector* hits, int evID, int srcID) << " cont.mode: " << isContinuous() << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; - std::cout << "Printing segmentation info: " << std::endl; - SegmentationChip::Print(); + // std::cout << "Printing segmentation info: " << std::endl; + // SegmentationChip::Print(); // // is there something to flush ? if (mNewROFrame > mROFrameMin) { @@ -335,13 +335,9 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID //// adapting the depth (Y) of the chip to the APTS response maximum depth LOG(debug) << "local original: startPos = " << xyzLocS << ", endPos = " << xyzLocE << std::endl; - if (subDetID == 0) { - xyzLocS.SetY(xyzLocS.Y() * mSimRespVDScaleDepth); - xyzLocE.SetY(xyzLocE.Y() * mSimRespVDScaleDepth); - } else { - xyzLocS.SetY(xyzLocS.Y() * mSimRespMLOTScaleDepth); - xyzLocE.SetY(xyzLocE.Y() * mSimRespMLOTScaleDepth); - } + xyzLocS.SetY(xyzLocS.Y()); + xyzLocE.SetY(xyzLocE.Y()); + LOG(debug) << "rescaled Y: startPos = " << xyzLocS << ", endPos = " << xyzLocE << std::endl; math_utils::Vector3D step(xyzLocE); @@ -449,17 +445,6 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID rspmat = resp->getResponse(mSimRespMLOTScaleX * (xyzLocS.X() - cRowPix), mSimRespMLOTScaleZ * (xyzLocS.Z() - cColPix), xyzLocS.Y(), flipRow, flipCol, rowMax, colMax); } - float tempPitchX = 0, tempPitchZ = 0; - if (subDetID == 0) { - tempPitchX = Segmentation::PitchRowVD; - tempPitchZ = Segmentation::PitchColVD; - } else { - tempPitchX = Segmentation::PitchRowMLOT; - tempPitchZ = Segmentation::PitchColMLOT; - } - LOG(debug) << "X and Z inside pixel at start = " << (xyzLocS.X() - cRowPix) << " , " << (xyzLocS.Z() - cColPix) << ", rescaled: " << mSimRespMLOTScaleX * (xyzLocS.X() - cRowPix) << " , " << mSimRespMLOTScaleZ * (xyzLocS.Z() - cColPix); - LOG(debug) << "Hit inside pitch? X: " << ((xyzLocS.X() - cRowPix) < tempPitchX) << " Z: " << ((xyzLocS.Z() - cColPix) < tempPitchZ); - xyzLocS += step; if (rspmat == nullptr) { @@ -479,7 +464,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID if (colDest < 0 || colDest >= colSpan) { continue; } - respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, mSimRespOrientation ? !flipRow : flipRow, flipCol); + respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, mSimRespOrientation ? !flipRow : flipRow, !flipCol); } } } From 467c968df7324fd798d26566d453cc009f218325 Mon Sep 17 00:00:00 2001 From: vikasssinghal <54980686+vikasssinghal@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:02:24 +0530 Subject: [PATCH 64/99] GPU: Wshadow compiler directive not needed in HIP Includes System (#14738) * TODO: Wshadow compiler directive not needed * Removed all three lines push, pragma diagnostic, pop --- GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h b/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h index 0228f993aaee3..389c79c0e4eb9 100644 --- a/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h +++ b/GPU/GPUTracking/Base/hip/GPUReconstructionHIPIncludesSystem.h @@ -22,11 +22,8 @@ #include #include #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" // FIXME: Is this still needed? #include #include #include -#pragma GCC diagnostic pop #endif // O2_GPU_RECONSTRUCTIONHIPINCLUDESSYSTEM_H From 7ae55ac1a7e9ff73af121d14f2cf006e37b0497e Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 15 Oct 2025 12:48:42 +0200 Subject: [PATCH 65/99] Fix precalculated sector cos/sin values in TOF Geo --- Detectors/TOF/base/src/Geo.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/TOF/base/src/Geo.cxx b/Detectors/TOF/base/src/Geo.cxx index 08cda68c6d12e..7df4c3a3537e1 100644 --- a/Detectors/TOF/base/src/Geo.cxx +++ b/Detectors/TOF/base/src/Geo.cxx @@ -1049,8 +1049,8 @@ void Geo::rotateToSector(Float_t* xyz, Int_t isector) void Geo::alignedToNominalSector(Float_t* xyz, Int_t isector) { // rotate from the aligned sector frame coordinates to nominal ones (i.e. alpha=20*sector+10 deg.) - constexpr float CS[18] = {.848077e-01, 8.660254e-01, 6.427876e-01, 3.420202e-01, -4.371139e-08, -3.420201e-01, -6.427876e-01, -8.660254e-01, -9.848077e-01, -9.848077e-01, -8.660254e-01, -6.427875e-01, -3.420201e-01, 1.192488e-08, 3.420201e-01, 6.427875e-01, 8.660253e-01, 9.848078e-01}; - constexpr float SN[18] = {1.736482e-01, 5.000000e-01, 7.660444e-01, 9.396926e-01, 1.000000e+00, 9.396926e-01, 7.660444e-01, 5.000001e-01, 1.736483e-01, -1.736482e-01, -5.000000e-01, -7.660446e-01, -9.396927e-01, -1.000000e+00, -9.396926e-01, -7.660445e-01, -5.000002e-01, -1.736480e-01}; + constexpr float CS[18] = {+9.848078e-01, +8.660254e-01, +6.427876e-01, +3.420201e-01, +6.123234e-17, -3.420201e-01, -6.427876e-01, -8.660254e-01, -9.848078e-01, -9.848078e-01, -8.660254e-01, -6.427876e-01, -3.420201e-01, -1.836970e-16, +3.420201e-01, +6.427876e-01, +8.660254e-01, +9.848078e-01}; + constexpr float SN[18] = {+1.736482e-01, +5.000000e-01, +7.660444e-01, +9.396926e-01, +1.000000e+00, +9.396926e-01, +7.660444e-01, +5.000000e-01, +1.736482e-01, -1.736482e-01, -5.000000e-01, -7.660444e-01, -9.396926e-01, -1.000000e+00, -9.396926e-01, -7.660444e-01, -5.000000e-01, -1.736482e-01}; Float_t xyzDummy[3] = {xyz[1], xyz[2], xyz[0]}; // go to twisted coordinates... o2::tof::Geo::antiRotateToSector(xyzDummy, isector); // lab coordinates xyz[0] = xyzDummy[0] * CS[isector] + xyzDummy[1] * SN[isector]; From c5ad2d0136ab6dc56a69a1c44392e7d5c1039ad8 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 15 Oct 2025 13:35:18 +0200 Subject: [PATCH 66/99] fix methods is/setInNominalSector of TOF cluster --- .../Detectors/TOF/include/DataFormatsTOF/Cluster.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h index 2f15923419795..37d3ca23ddb35 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -49,9 +49,8 @@ class Cluster : public o2::BaseCluster kDownRight = 4, // 2^4, 5th bit kDown = 5, // 2^5, 6th bit kDownLeft = 6, // 2^6, 7th bit - kLeft = 7, // 2^7, 8th bit - // - FrameBit = 6 }; // this bit set means that the cluster is in the nominal (alpha=20*sector+10 deg.) sector frame rather than aligned + kLeft = 7 // 2^7, 8th bit + }; Cluster() = default; @@ -59,8 +58,8 @@ class Cluster : public o2::BaseCluster ~Cluster() = default; - bool isInNominalSector() const { return isBitSet(FrameBit); } - void setInNominalSector() { setBit(FrameBit); } + bool isInNominalSector() const { return mInNominalSector; } + void setInNominalSector(bool v = true) { mInNominalSector = v; } std::int8_t getSector() const { return getCount(); } void setSector(std::int8_t value) { setCount(value); } @@ -163,9 +162,10 @@ class Cluster : public o2::BaseCluster double mDigitInfoT[6] = {0., 0., 0., 0., 0., 0.}; float mDigitInfoTOT[6] = {0., 0., 0., 0., 0., 0.}; float mTgeant = 0.0; + bool mInNominalSector = false; double mT0true = 0.0; - ClassDefNV(Cluster, 5); + ClassDefNV(Cluster, 6); }; #ifndef GPUCA_GPUCODE From 272e45a959c5ebcfa322ed1165781ace6babde95 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 15 Oct 2025 11:31:35 +0200 Subject: [PATCH 67/99] GPU Benchmark: Remove unused variable to fix compiler warning --- GPU/GPUbenchmark/cuda/Kernels.cu | 2 -- 1 file changed, 2 deletions(-) diff --git a/GPU/GPUbenchmark/cuda/Kernels.cu b/GPU/GPUbenchmark/cuda/Kernels.cu index 16dc138ae466f..b8dedfd8145b1 100644 --- a/GPU/GPUbenchmark/cuda/Kernels.cu +++ b/GPU/GPUbenchmark/cuda/Kernels.cu @@ -304,7 +304,6 @@ void printDeviceProp(int32_t deviceId) int32_t clockRateKHz = 0; int32_t memoryClockRateKHz = 0; int32_t computeMode = 0; - int32_t cooperativeMultiDevice = 0; #if (CUDART_VERSION >= 13000) GPUCHECK(cudaDeviceGetAttribute(&clockRateKHz, cudaDevAttrClockRate, deviceId)); @@ -314,7 +313,6 @@ void printDeviceProp(int32_t deviceId) clockRateKHz = props.clockRate; memoryClockRateKHz = props.memoryClockRate; computeMode = props.computeMode; - cooperativeMultiDevice = props.cooperativeMultiDeviceLaunch; #endif std::cout << std::setw(w1) << "Name: " << props.name << std::endl; std::cout << std::setw(w1) << "pciBusID: " << props.pciBusID << std::endl; From f35d87b4672d4d8d8b3888205924ddbd0790b8b3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 15 Oct 2025 14:04:21 +0200 Subject: [PATCH 68/99] DPL Analysis: fix multiple HistogramRegistry instances per task In particular when one of them needs to have its own folder. --- Framework/AnalysisSupport/src/AODWriterHelpers.cxx | 12 ++++++++++++ Framework/Core/include/Framework/OutputObjHeader.h | 5 ++++- Framework/Core/src/HistogramRegistry.cxx | 3 ++- Framework/TestWorkflows/src/o2TestHistograms.cxx | 12 ++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index 27dad43480913..bcf27d0be5ba3 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -339,7 +339,9 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) O2_SIGNPOST_END(histogram_registry, did, "deserialization", "Done deserialization."); // If we have a folder, we assume the first element of the path // to be the name of the registry. + bool folderForContainer = false; if (sourceType == HistogramRegistrySource) { + folderForContainer = objh->createContainer != 0; obj.container = objh->containerName; } else { obj.container = obj.name; @@ -423,6 +425,16 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) // FIXME: handle folders f[route.policy]->cd("/"); auto* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); + + // In case we need a folder for the registry, let's create it. + if (folderForContainer) { + auto* histogramRegistryFolder = currentDir->GetDirectory(obj.container.data()); + if (!histogramRegistryFolder) { + histogramRegistryFolder = currentDir->mkdir(obj.container.c_str(), "", kTRUE); + } + currentDir = histogramRegistryFolder; + } + // The name contains a path... int objSize = 0; if (sourceType == HistogramRegistrySource) { diff --git a/Framework/Core/include/Framework/OutputObjHeader.h b/Framework/Core/include/Framework/OutputObjHeader.h index f1c284d564f15..801642ec6af4f 100644 --- a/Framework/Core/include/Framework/OutputObjHeader.h +++ b/Framework/Core/include/Framework/OutputObjHeader.h @@ -37,6 +37,7 @@ enum OutputObjSourceType : unsigned int { /// @brief O2 header for OutputObj metadata struct OutputObjHeader : public BaseHeader { constexpr static const uint32_t sVersion = 1; + constexpr static const uint32_t MAX_REGISTRY_NAME_SIZE = 128; constexpr static const o2::header::HeaderType sHeaderType = "OutObjMD"; constexpr static const o2::header::SerializationMethod sSerializationMethod = o2::header::gSerializationMethodNone; OutputObjHandlingPolicy mPolicy; @@ -45,7 +46,9 @@ struct OutputObjHeader : public BaseHeader { uint16_t mPipelineIndex = 0; uint16_t mPipelineSize = 1; // Name of the actual container for the object, e.g. the HistogramRegistry name - char containerName[64] = {0}; + char containerName[MAX_REGISTRY_NAME_SIZE] = {0}; + // Wether or not the container should have a name + char createContainer = false; constexpr OutputObjHeader() : BaseHeader(sizeof(OutputObjHeader), sHeaderType, sSerializationMethod, sVersion), diff --git a/Framework/Core/src/HistogramRegistry.cxx b/Framework/Core/src/HistogramRegistry.cxx index 5e39fbe7181e7..9caa7cbd1f48e 100644 --- a/Framework/Core/src/HistogramRegistry.cxx +++ b/Framework/Core/src/HistogramRegistry.cxx @@ -55,7 +55,8 @@ OutputRef HistogramRegistry::ref(uint16_t pipelineIndex, uint16_t pipelineSize) { OutputObjHeader header{mPolicy, OutputObjSourceType::HistogramRegistrySource, mTaskHash, pipelineIndex, pipelineSize}; // Copy the name of the registry to the haeder. - strncpy(header.containerName, mName.data(), 64); + strncpy(header.containerName, mName.data(), OutputObjHeader::MAX_REGISTRY_NAME_SIZE); + header.createContainer = mCreateRegistryDir ? 1 : 0; return OutputRef{std::string{mName}, 0, o2::header::Stack{header}}; } diff --git a/Framework/TestWorkflows/src/o2TestHistograms.cxx b/Framework/TestWorkflows/src/o2TestHistograms.cxx index ae3610ca01e67..640a165fb91ff 100644 --- a/Framework/TestWorkflows/src/o2TestHistograms.cxx +++ b/Framework/TestWorkflows/src/o2TestHistograms.cxx @@ -53,6 +53,18 @@ struct EtaAndClsHistogramsSimple { } // }; + HistogramRegistry registry2{ + "registry2", + { + {"a/foo/b/eta", "#Eta", {HistType::kTH1F, {{100, -2.0, 2.0}}}}, // + {"fii/c/hpt", "p_{T}", {HistType::kTH1D, {{1002, -0.01, 50.1}}}}, // + {"a/foobar/phi", "#Phi", {HistType::kTH1D, {{102, 0, 2 * M_PI}}}}, // + {"fifi/ptToPt", "#ptToPt", {HistType::kTH2F, {{100, -0.01, 10.01}, {100, -0.01, 10.01}}}} // + }, + OutputObjHandlingPolicy::AnalysisObject, + false, + true}; + void init(InitContext&) { if (!trackFilterString->empty()) { From ac23cbf7eedbc27f441b80023c710cacb88422cd Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Wed, 15 Oct 2025 21:07:16 +0200 Subject: [PATCH 69/99] DPL: add command line option to print error instead of warning when exit transition timer expires --- Framework/Core/src/DataProcessingDevice.cxx | 8 ++++---- Framework/Core/src/DeviceSpecHelpers.cxx | 2 ++ Framework/Core/src/runDataProcessing.cxx | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 5564f68d8f8ce..a41aa3a886d55 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -139,11 +139,11 @@ void on_transition_requested_expired(uv_timer_t* handle) // Check if this is a source device O2_SIGNPOST_ID_FROM_POINTER(cid, device, handle); auto& spec = ref->get(); - if (hasOnlyGenerated(spec)) { - O2_SIGNPOST_EVENT_EMIT_ERROR(calibration, cid, "callback", "DPL exit transition grace period for source expired. Exiting."); + std::string messageOnExpire = hasOnlyGenerated(spec) ? "DPL exit transition grace period for source expired. Exiting." : fmt::format("DPL exit transition grace period for {} expired. Exiting.", state.allowedProcessing == DeviceState::CalibrationOnly ? "calibration" : "data & calibration").c_str(); + if (!ref->get().device()->GetConfig()->GetValue("error-on-exit-transition-timeout")) { + O2_SIGNPOST_EVENT_EMIT_WARN(calibration, cid, "callback", "%{public}s", messageOnExpire.c_str()); } else { - O2_SIGNPOST_EVENT_EMIT_ERROR(calibration, cid, "callback", "DPL exit transition grace period for %{public}s expired. Exiting.", - state.allowedProcessing == DeviceState::CalibrationOnly ? "calibration" : "data & calibration"); + O2_SIGNPOST_EVENT_EMIT_ERROR(calibration, cid, "callback", "%{public}s", messageOnExpire.c_str()); } state.transitionHandling = TransitionHandlingState::Expired; } diff --git a/Framework/Core/src/DeviceSpecHelpers.cxx b/Framework/Core/src/DeviceSpecHelpers.cxx index ec0a40e44ac31..88e5269482ebd 100644 --- a/Framework/Core/src/DeviceSpecHelpers.cxx +++ b/Framework/Core/src/DeviceSpecHelpers.cxx @@ -1541,6 +1541,7 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, realOdesc.add_options()("child-driver", bpo::value()); realOdesc.add_options()("rate", bpo::value()); realOdesc.add_options()("exit-transition-timeout", bpo::value()); + realOdesc.add_options()("error-on-exit-transition-timeout", bpo::value()->zero_tokens()); realOdesc.add_options()("data-processing-timeout", bpo::value()); realOdesc.add_options()("expected-region-callbacks", bpo::value()); realOdesc.add_options()("timeframes-rate-limit", bpo::value()); @@ -1728,6 +1729,7 @@ boost::program_options::options_description DeviceSpecHelpers::getForwardedDevic ("control-port", bpo::value(), "Utility port to be used by O2 Control") // ("rate", bpo::value(), "rate for a data source device (Hz)") // ("exit-transition-timeout", bpo::value(), "timeout before switching to READY state") // + ("error-on-exit-transition-timeout", bpo::value()->zero_tokens(), "print error instead of warning when exit transition timer expires") // ("data-processing-timeout", bpo::value(), "timeout after which only calibration can happen") // ("expected-region-callbacks", bpo::value(), "region callbacks to expect before starting") // ("timeframes-rate-limit", bpo::value()->default_value("0"), "how many timeframes can be in fly") // diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index ae6ea03063dfc..1611eb8605134 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -1052,6 +1052,7 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, ("signposts", bpo::value()->default_value(defaultSignposts ? defaultSignposts : ""), "comma separated list of signposts to enable") // ("expected-region-callbacks", bpo::value()->default_value("0"), "how many region callbacks we are expecting") // ("exit-transition-timeout", bpo::value()->default_value(defaultExitTransitionTimeout), "how many second to wait before switching from RUN to READY") // + ("error-on-exit-transition-timeout", bpo::value()->zero_tokens()->default_value(false), "print error instead of warning when exit transition timer expires") // ("data-processing-timeout", bpo::value()->default_value(defaultDataProcessingTimeout), "how many second to wait before stopping data processing and allowing data calibration") // ("timeframes-rate-limit", bpo::value()->default_value("0"), "how many timeframe can be in fly at the same moment (0 disables)") // ("configuration,cfg", bpo::value()->default_value("command-line"), "configuration backend") // From 69f0e4135b97d44e04b28ce445ff21b5f6b77db4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 15 Oct 2025 15:03:24 +0200 Subject: [PATCH 70/99] GPU Standalone: Make setO2Settings compatible with debug mode --- GPU/GPUTracking/Standalone/Benchmark/standalone.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index 1fa41d55ebbec..f9c53e3ffd59c 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -214,11 +214,11 @@ int32_t ReadConfiguration(int argc, char** argv) } } if (configStandalone.setO2Settings) { - if (!(configStandalone.inputcontrolmem && configStandalone.outputcontrolmem)) { - printf("setO2Settings requires the usage of --inputMemory and --outputMemory as in O2\n"); - return 1; - } - if (configStandalone.runGPU) { + if (configStandalone.runGPU && configStandalone.proc.debugLevel <= 1) { + if (!(configStandalone.inputcontrolmem && configStandalone.outputcontrolmem)) { + printf("setO2Settings requires the usage of --inputMemory and --outputMemory as in O2\n"); + return 1; + } configStandalone.proc.forceHostMemoryPoolSize = 1024 * 1024 * 1024; } configStandalone.rec.tpc.trackReferenceX = 83; From 08a10d5bf6c130c5b9f530332ffc1356b9eb152a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 15 Oct 2025 17:23:02 +0200 Subject: [PATCH 71/99] GPU RTC: Add overrideWarpSize option --- GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu | 4 ++-- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index 9e7cfa5495040..62b490a59d0dc 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -113,7 +113,7 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() constexpr int32_t reqVerMin = 0; #endif if (GetProcessingSettings().rtc.enable && GetProcessingSettings().rtctech.runTest == 2) { - mWarpSize = GPUCA_WARP_SIZE; + mWarpSize = GetProcessingSettings().rtc.overrideWarpSize != -1 ? GetProcessingSettings().rtc.overrideWarpSize : GPUCA_WARP_SIZE; genAndLoadRTC(); exit(0); } @@ -245,7 +245,7 @@ int32_t GPUReconstructionCUDA::InitDevice_Runtime() GPUInfo("\ttextureAlignment = %ld", (uint64_t)deviceProp.textureAlignment); GPUInfo(" "); } - if (deviceProp.warpSize != GPUCA_WARP_SIZE && !GetProcessingSettings().rtc.enable) { + if (GetProcessingSettings().rtc.enable ? (GetProcessingSettings().rtc.overrideWarpSize != -1 && deviceProp.warpSize != GetProcessingSettings().rtc.overrideWarpSize) : (deviceProp.warpSize != GPUCA_WARP_SIZE)) { throw std::runtime_error("Invalid warp size on GPU"); } mWarpSize = deviceProp.warpSize; diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 30477d67fdc4f..bde082b8a10c4 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -216,6 +216,7 @@ AddOption(optSpecialCode, int8_t, -1, "", 0, "Insert GPUCA_RTC_SPECIAL_CODE spec AddOption(deterministic, bool, false, "", 0, "Compile RTC in deterministic mode, with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define") AddOption(compilePerKernel, bool, true, "", 0, "Run one RTC compilation per kernel") AddOption(enable, bool, false, "", 0, "Use RTC to optimize GPU code") +AddOption(overrideWarpSize, int32_t, -1, "", 0, "Override the warp size to be used for RTC") AddHelp("help", 'h') EndConfig() From 0f9c660a9a7d6307d3eb9dfc9f8bb9d2823bf01a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 15 Oct 2025 19:14:42 +0200 Subject: [PATCH 72/99] GPU Workflow: Add dumpFirst and dumpLast options --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 3 ++ GPU/GPUTracking/Interface/GPUO2Interface.cxx | 23 +++++++------- GPU/GPUTracking/Interface/GPUO2Interface.h | 4 +-- .../include/GPUWorkflow/GPUWorkflowSpec.h | 1 + GPU/Workflow/src/GPUWorkflowInternal.h | 1 + GPU/Workflow/src/GPUWorkflowPipeline.cxx | 4 +-- GPU/Workflow/src/GPUWorkflowSpec.cxx | 31 ++++++++++++++++--- prodtests/full-system-test/dpl-workflow.sh | 3 ++ 8 files changed, 50 insertions(+), 20 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index bde082b8a10c4..fc08b063ff16a 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -625,6 +625,9 @@ AddOption(deviceType, std::string, "CPU", "", 0, "Device type, CPU | CUDA | HIP AddOption(forceDeviceType, bool, true, "", 0, "force device type, otherwise allows fall-back to CPU") AddOption(synchronousProcessing, bool, false, "", 0, "Apply performance shortcuts for synchronous processing, disable unneeded steps") AddOption(dump, int32_t, 0, "", 0, "Dump events for standalone benchmark: 1 = dump events, 2 = dump events and skip processing in workflow") +AddOption(dumpFirst, int32_t, 0, "", 0, "First event to dump (referring to tfCounter)") +AddOption(dumpLast, int32_t, -1, "", 0, "Last event to dump (-1 = all)") +AddOption(dumpFolder, std::string, "", "", 0, "Folder to which to write dump files, [P] is replaced by process id") AddOption(display, bool, false, "", 0, "Enable standalone gpu tracking visualizaion") AddOption(rundEdx, int32_t, -1, "", 0, "Enable/disable dEdx processing (-1 for autoselect)") AddOption(dEdxSplineTopologyCorrFile, std::string, "", "", 0, "File name of the dE/dx spline track topology correction file") diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.cxx b/GPU/GPUTracking/Interface/GPUO2Interface.cxx index 65907528a3dba..d04db5e9bf271 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.cxx +++ b/GPU/GPUTracking/Interface/GPUO2Interface.cxx @@ -137,29 +137,30 @@ void GPUO2Interface::Deinitialize() mNContexts = 0; } -void GPUO2Interface::DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data) +void GPUO2Interface::DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data, uint32_t iThread, const char* dir) { - mCtx[0].mChain->ClearIOPointers(); - mCtx[0].mChain->mIOPtrs = *data; + const auto oldPtrs = mCtx[iThread].mChain->mIOPtrs; + mCtx[iThread].mChain->mIOPtrs = *data; char fname[1024]; - snprintf(fname, 1024, "event.%d.dump", nEvent); - mCtx[0].mChain->DumpData(fname); + snprintf(fname, 1024, "%sevent.%d.dump", dir, nEvent); + mCtx[iThread].mChain->DumpData(fname); if (nEvent == 0) { #ifdef GPUCA_BUILD_QA if (mConfig->configProcessing.runMC) { - mCtx[0].mChain->ForceInitQA(); + mCtx[iThread].mChain->ForceInitQA(); snprintf(fname, 1024, "mc.%d.dump", nEvent); - mCtx[0].mChain->GetQA()->UpdateChain(mCtx[0].mChain); - mCtx[0].mChain->GetQA()->DumpO2MCData(fname); + mCtx[iThread].mChain->GetQA()->UpdateChain(mCtx[iThread].mChain); + mCtx[iThread].mChain->GetQA()->DumpO2MCData(fname); } #endif } + mCtx[iThread].mChain->mIOPtrs = oldPtrs; } -void GPUO2Interface::DumpSettings() +void GPUO2Interface::DumpSettings(uint32_t iThread, const char* dir) { - mCtx[0].mChain->DoQueuedUpdates(-1); - mCtx[0].mRec->DumpSettings(); + mCtx[iThread].mChain->DoQueuedUpdates(-1); + mCtx[iThread].mRec->DumpSettings(dir); } int32_t GPUO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs, uint32_t iThread, GPUInterfaceInputUpdate* inputUpdateCallback) diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.h b/GPU/GPUTracking/Interface/GPUO2Interface.h index 9b7390f2ed663..0e2020b306984 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.h +++ b/GPU/GPUTracking/Interface/GPUO2Interface.h @@ -77,8 +77,8 @@ class GPUO2Interface int32_t RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs = nullptr, uint32_t iThread = 0, GPUInterfaceInputUpdate* inputUpdateCallback = nullptr); void Clear(bool clearOutputs, uint32_t iThread = 0); - void DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data); - void DumpSettings(); + void DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data, uint32_t iThread, const char* dir = ""); + void DumpSettings(uint32_t iThread, const char* dir = ""); void GetITSTraits(o2::its::TrackerTraits<7>*& trackerTraits, o2::its::VertexerTraits<7>*& vertexerTraits, o2::its::TimeFrame<7>*& timeFrame); const o2::base::Propagator* GetDeviceO2Propagator(int32_t iThread = 0) const; diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index 4f62f07593bff..160efd4048af0 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -225,6 +225,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task int64_t mCreationForCalib = -1; ///< creation time for calib manipulation int32_t mVerbosity = 0; uint32_t mNTFs = 0; + uint32_t mNTFDumps = 0; uint32_t mNDebugDumps = 0; uint32_t mNextThreadIndex = 0; bool mUpdateGainMapCCDB = true; diff --git a/GPU/Workflow/src/GPUWorkflowInternal.h b/GPU/Workflow/src/GPUWorkflowInternal.h index 7ac9c60048e20..73d3676f3d84a 100644 --- a/GPU/Workflow/src/GPUWorkflowInternal.h +++ b/GPU/Workflow/src/GPUWorkflowInternal.h @@ -47,6 +47,7 @@ struct GPURecoWorkflow_QueueObject { bool jobSubmitted = false; bool jobFinished = false; int32_t jobReturnValue = 0; + volatile int32_t jobThreadIndex = -1; std::mutex jobFinishedMutex; std::condition_variable jobFinishedNotify; bool jobInputFinal = false; diff --git a/GPU/Workflow/src/GPUWorkflowPipeline.cxx b/GPU/Workflow/src/GPUWorkflowPipeline.cxx index 8867b6c336f97..ba395cd98d64d 100644 --- a/GPU/Workflow/src/GPUWorkflowPipeline.cxx +++ b/GPU/Workflow/src/GPUWorkflowPipeline.cxx @@ -90,6 +90,7 @@ void GPURecoWorkflowSpec::RunWorkerThread(int32_t id) context = workerContext.inputQueue.front(); workerContext.inputQueue.pop(); } + context->jobThreadIndex = id; context->jobReturnValue = runMain(nullptr, context->jobPtrs, context->jobOutputRegions, id, context->jobInputUpdateCallback.get()); { std::lock_guard lk(context->jobFinishedMutex); @@ -179,8 +180,7 @@ int32_t GPURecoWorkflowSpec::handlePipeline(ProcessingContext& pc, GPUTrackingIn } mPipeline->completionPolicyQueue.pop(); } - } - if (mSpecConfig.enableDoublePipeline == 2) { + } else if (mSpecConfig.enableDoublePipeline == 2) { auto prepareDummyMessage = pc.outputs().make>(Output{gDataOriginGPU, "PIPELINEPREPARE", 0}, 0u); size_t ptrsTotal = 0; diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 6c76f13c9bbd0..d3d3eb14869e0 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -825,11 +825,31 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) lockDecodeInput.reset(); + uint32_t threadIndex; if (mConfParam->dump) { - if (mNTFs == 1) { - mGPUReco->DumpSettings(); + if (mSpecConfig.enableDoublePipeline && pipelineContext->jobSubmitted) { + while (pipelineContext->jobThreadIndex == -1) { + } + threadIndex = pipelineContext->jobThreadIndex; + } else { + threadIndex = 0; // TODO: Not sure if this is safe, but it is not yet known which threadIndex will pick up the enqueued job + } + + std::string dir = ""; + if (mConfParam->dumpFolder != "") { + dir = std::regex_replace(mConfParam->dumpFolder, std::regex("\\[P\\]"), std::to_string(getpid())); + if (mNTFs == 1) { + mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + } + dir += "/"; + } + if (mNTFs == 1) { // Must dump with first TF, since will enforce enqueued calib updates + mGPUReco->DumpSettings(threadIndex, dir.c_str()); + } + if (tinfo.tfCounter >= mConfParam->dumpFirst && (mConfParam->dumpLast == -1 || tinfo.tfCounter <= mConfParam->dumpLast)) { + mGPUReco->DumpEvent(mNTFDumps, &ptrs, threadIndex, dir.c_str()); + mNTFDumps++; } - mGPUReco->DumpEvent(mNTFs - 1, &ptrs); } std::unique_ptr ptrsDump; if (mConfParam->dumpBadTFMode == 2) { @@ -847,9 +867,10 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) std::unique_lock lk(pipelineContext->jobFinishedMutex); pipelineContext->jobFinishedNotify.wait(lk, [context = pipelineContext.get()]() { return context->jobFinished; }); retVal = pipelineContext->jobReturnValue; + threadIndex = pipelineContext->jobThreadIndex; } else { // uint32_t threadIndex = pc.services().get().threadIndex; - uint32_t threadIndex = mNextThreadIndex; + threadIndex = mNextThreadIndex; if (mConfig->configProcessing.doublePipeline) { mNextThreadIndex = (mNextThreadIndex + 1) % 2; } @@ -879,7 +900,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } fclose(fp); } else if (mConfParam->dumpBadTFMode == 2) { - mGPUReco->DumpEvent(mNDebugDumps - 1, ptrsDump.get()); + mGPUReco->DumpEvent(mNDebugDumps - 1, ptrsDump.get(), threadIndex); } } diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 2f0e761366e18..5d47ae84b130b 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -235,6 +235,9 @@ if [[ $EPNSYNCMODE == 1 ]]; then fi fi fi +if [[ $GPUTYPE != "CPU" && $NGPUS > 1 ]]; then + GPU_CONFIG_KEY+="GPU_global.dumpFolder=gpu_dump_[P];" +fi if [[ $SYNCRAWMODE == 1 ]]; then GPU_CONFIG_KEY+="GPU_proc.tpcIncreasedMinClustersPerRow=500000;GPU_proc.ignoreNonFatalGPUErrors=1;GPU_proc.throttleAlarms=1;" if [[ $RUNTYPE == "PHYSICS" || $RUNTYPE == "COSMICS" || $RUNTYPE == "TECHNICAL" ]]; then From 260bf85a87a4321309c14d604d634d68441a2166 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Thu, 16 Oct 2025 13:50:36 +0200 Subject: [PATCH 73/99] Fix unwanted behaviour in signal filtering with embedding pattern different from (#14735) --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index b8d295a4393e4..4ef53a406497b 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -2126,9 +2126,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) 0, sourceID); } - if (sourceID != 0 || !mUseSigFiltMC) { - mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision - } + mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision } } } From b382734dbb5c628ed9a9297421193772ae3b58f2 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Thu, 16 Oct 2025 10:43:06 +0200 Subject: [PATCH 74/99] aggregator.sh: print error when exit transition timer expires on aggregator nodes --- prodtests/full-system-test/aggregator-workflow.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prodtests/full-system-test/aggregator-workflow.sh b/prodtests/full-system-test/aggregator-workflow.sh index f90decefa3f2f..a0d091a98d193 100755 --- a/prodtests/full-system-test/aggregator-workflow.sh +++ b/prodtests/full-system-test/aggregator-workflow.sh @@ -14,6 +14,9 @@ source $O2DPG_ROOT/DATA/common/setenv.sh || { echo "setenv.sh failed" 1>&2 && ex source $O2DPG_ROOT/DATA/common/getCommonArgs.sh || { echo "getCommonArgs.sh failed" 1>&2 && exit 1; } source $O2DPG_ROOT/DATA/common/setenv_calib.sh || { echo "setenv_calib.sh failed" 1>&2 && exit 1; } +# print an error (instead of warning) when exit transition timer expires, only for tasks on aggregator nodes +ARGS_ALL+=" --error-on-exit-transition-timeout" + # if the populator for DCS CCDB is needed, set it to non-0 : ${NEED_DCS_CCDB_POPULATOR:=0} From 21accf317911cd36628c68471e840c5169d0d5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 17 Oct 2025 09:05:30 +0200 Subject: [PATCH 75/99] TOF: param utilities are separated in a dedicated library for O2Physics usage (#14730) * Revert "TOF Param container: move to header only" This reverts commit 1aa2c1409988e1054730c7cccdc987fbe1db7506. * Add DataFormatsParamTOF library to CMakeLists --- DataFormats/Detectors/TOF/CMakeLists.txt | 10 ++- .../DataFormatsTOF/ParameterContainers.h | 51 +++------------ .../TOF/src/DataFormatsParamTOFLinkDef.h | 17 +++++ .../Detectors/TOF/src/DataFormatsTOFLinkDef.h | 2 - .../Detectors/TOF/src/ParameterContainers.cxx | 62 +++++++++++++++++++ .../src/make-parameter-collection.cxx | 2 +- 6 files changed, 98 insertions(+), 46 deletions(-) create mode 100644 DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h create mode 100644 DataFormats/Detectors/TOF/src/ParameterContainers.cxx diff --git a/DataFormats/Detectors/TOF/CMakeLists.txt b/DataFormats/Detectors/TOF/CMakeLists.txt index 8a55e531287e1..4d41167f7bf1d 100644 --- a/DataFormats/Detectors/TOF/CMakeLists.txt +++ b/DataFormats/Detectors/TOF/CMakeLists.txt @@ -9,6 +9,14 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +o2_add_library(DataFormatsParamTOF + SOURCES src/ParameterContainers.cxx + PUBLIC_LINK_LIBRARIES O2::FrameworkLogger) + + +o2_target_root_dictionary(DataFormatsParamTOF + HEADERS include/DataFormatsTOF/ParameterContainers.h) + o2_add_library(DataFormatsTOF SOURCES src/Cluster.cxx src/CalibInfoTOFshort.cxx @@ -22,6 +30,7 @@ o2_add_library(DataFormatsTOF src/TOFFEElightInfo.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats O2::GPUCommon + O2::DataFormatsParamTOF Boost::serialization) o2_target_root_dictionary(DataFormatsTOF @@ -33,7 +42,6 @@ o2_target_root_dictionary(DataFormatsTOF include/DataFormatsTOF/RawDataFormat.h include/DataFormatsTOF/CompressedDataFormat.h include/DataFormatsTOF/CTF.h - include/DataFormatsTOF/ParameterContainers.h include/DataFormatsTOF/CalibInfoCluster.h include/DataFormatsTOF/CosmicInfo.h include/DataFormatsTOF/TOFFEElightInfo.h diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h index c9d910d8345e5..e64bf8aa3e276 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h @@ -18,10 +18,10 @@ #ifndef O2_TOF_PARAMCONTAINER_H #define O2_TOF_PARAMCONTAINER_H -#include "TNamed.h" -#include "TFile.h" -#include "Framework/Logger.h" -#include "map" +#include +#include +#include +#include namespace o2 { @@ -37,7 +37,7 @@ class Parameters Parameters(std::array parNames, std::string name) : mName{name}, mPar{}, mParNames{parNames} {}; /// Default destructor - virtual ~Parameters() = default; // Ensure proper cleanup in derived classes + ~Parameters() = default; /// Setter for the parameter at position iparam /// \param iparam index in the array of the parameters @@ -183,27 +183,10 @@ class ParameterCollection : public TNamed /// @param value parameter to add to the stored information /// @param pass key to look for in the stored information e.g. pass /// @return true if found and configured false if not fully configured - bool addParameter(const std::string& pass, const std::string& parName, float value) - { - const bool alreadyPresent = hasKey(pass); - if (alreadyPresent) { - LOG(debug) << "Changing parametrization corresponding to key " << pass << " from size " << mParameters[pass].size() << " to " << parName; - } else { - mParameters[pass] = std::unordered_map{}; - LOG(debug) << "Adding new parametrization corresponding to key " << pass << ": " << parName; - } - mParameters[pass][parName] = value; - return true; - } + bool addParameter(const std::string& pass, const std::string& parName, float value); /// @return the size of the container i.e. the number of stored keys (or passes) - int getSize(const std::string& pass) const - { - if (!hasKey(pass)) { - return -1; - } - return mParameters.at(pass).size(); - } + int getSize(const std::string& pass) const; /// @brief Function to push the parameters from the sub container into the collection and store it under a given key /// @tparam ParType type of the parameter container @@ -231,26 +214,10 @@ class ParameterCollection : public TNamed /// @brief printing function for the content of the pass /// @param pass pass to print - void print(const std::string& pass) const - { - const auto& size = getSize(pass); - if (size < 0) { - LOG(info) << "empty pass: " << pass; - return; - } - LOG(info) << "Pass \"" << pass << "\" with size " << size; - for (const auto& [par, value] : mParameters.at(pass)) { - LOG(info) << "par name = " << par << ", value = " << value; - } - } + void print(const std::string& pass) const; /// @brief printing function for the full content of the container - void print() const - { - for (const auto& [pass, pars] : mParameters) { - print(pass); - } - } + void print() const; /// @brief Getter of the full map of parameters stored in the container /// @return returns the full map of parameters diff --git a/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h b/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h new file mode 100644 index 0000000000000..2d6ee84bedb92 --- /dev/null +++ b/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h @@ -0,0 +1,17 @@ +// 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. + +#ifdef __CLING__ + +#pragma link C++ class o2::tof::Parameters < 5> + ; +#pragma link C++ class o2::tof::ParameterCollection + ; + +#endif diff --git a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h index 55d1fd3973e70..03004e4c22afa 100644 --- a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h +++ b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h @@ -33,8 +33,6 @@ #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOFshort> + ; #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOF> + ; -#pragma link C++ class o2::tof::Parameters < 5> + ; -#pragma link C++ class o2::tof::ParameterCollection + ; #pragma link C++ class o2::tof::CTFHeader + ; #pragma link C++ class o2::tof::CompressedInfos + ; diff --git a/DataFormats/Detectors/TOF/src/ParameterContainers.cxx b/DataFormats/Detectors/TOF/src/ParameterContainers.cxx new file mode 100644 index 0000000000000..91f723873e9cd --- /dev/null +++ b/DataFormats/Detectors/TOF/src/ParameterContainers.cxx @@ -0,0 +1,62 @@ +// 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 ParameterContainers.h +/// \author Francesco Noferini +/// \author Nicolò Jacazio nicolo.jacazio@cern.ch +/// @since 2022-11-08 +/// \brief Implementation of the containers for the general parameters + +#include "DataFormatsTOF/ParameterContainers.h" + +// ClassImp(o2::tof::Parameters); +using namespace o2::tof; + +bool ParameterCollection::addParameter(const std::string& pass, const std::string& parName, float value) +{ + const bool alreadyPresent = hasKey(pass); + if (alreadyPresent) { + LOG(debug) << "Changing parametrization corresponding to key " << pass << " from size " << mParameters[pass].size() << " to " << parName; + } else { + mParameters[pass] = std::unordered_map{}; + LOG(debug) << "Adding new parametrization corresponding to key " << pass << ": " << parName; + } + mParameters[pass][parName] = value; + return true; +} + +int ParameterCollection::getSize(const std::string& pass) const +{ + if (!hasKey(pass)) { + return -1; + } + return mParameters.at(pass).size(); +} + +void ParameterCollection::print() const +{ + for (const auto& [pass, pars] : mParameters) { + print(pass); + } +} + +void ParameterCollection::print(const std::string& pass) const +{ + const auto& size = getSize(pass); + if (size < 0) { + LOG(info) << "empty pass: " << pass; + return; + } + LOG(info) << "Pass \"" << pass << "\" with size " << size; + for (const auto& [par, value] : mParameters.at(pass)) { + LOG(info) << "par name = " << par << ", value = " << value; + } +} diff --git a/Detectors/TOF/workflow/src/make-parameter-collection.cxx b/Detectors/TOF/workflow/src/make-parameter-collection.cxx index c90f417639212..3a210df3fcad8 100644 --- a/Detectors/TOF/workflow/src/make-parameter-collection.cxx +++ b/Detectors/TOF/workflow/src/make-parameter-collection.cxx @@ -63,7 +63,7 @@ class ParamExample : public Parameters<5> public: ParamExample() : Parameters(std::array{"p0", "p1", "p2", "p3", "p4"}, "ParamExample") { setParameters(std::array{0, 1, 2, 3, 4}); }; // Default constructor with default parameters - ~ParamExample() override = default; + ~ParamExample() = default; }; int main(int argc, char* argv[]) From f8b9265bbbe90b7787511cbf7ba6fc8867875fa0 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador <92120560+cima22@users.noreply.github.com> Date: Sat, 18 Oct 2025 09:19:33 +0200 Subject: [PATCH 76/99] GPU TPC: added dynamic buffer allocation during track-model decoding (#14747) * GPU TPC: added dynamic buffer allocation during track-model decoding * GPU TPC: improvement for dynamic buffer size for track-model decoding * GPU TPC: increased margins for track-model decoding buffers --- GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx index fd0c929dd2ba7..7e7ee86623099 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx @@ -18,6 +18,7 @@ #include "GPUO2DataTypes.h" #include "GPUMemorySizeScalers.h" #include "GPULogging.h" +#include using namespace o2::gpu; @@ -116,5 +117,10 @@ void GPUTPCDecompression::RegisterMemoryAllocation() void GPUTPCDecompression::SetMaxData(const GPUTrackingInOutPointers& io) { - mMaxNativeClustersPerBuffer = mRec->GetProcessingSettings().tpcMaxAttachedClustersPerSectorRow; + uint32_t maxAttachedClsMargin1 = *std::max_element(mInputGPU.nSliceRowClusters, mInputGPU.nSliceRowClusters + mInputGPU.nSliceRows); + float clsRatio1 = (mInputGPU.nUnattachedClusters > 0 ? float(mInputGPU.nAttachedClusters) / float(mInputGPU.nUnattachedClusters) : 1.0f) * 1.5f; + maxAttachedClsMargin1 *= clsRatio1; + uint32_t maxAttachedClsMargin2 = mInputGPU.nAttachedClusters / mInputGPU.nSliceRows * 3.5; // mean #attached cls per SectorRow multiplied by 3.5 (tuned) + mMaxNativeClustersPerBuffer = std::max({maxAttachedClsMargin1, maxAttachedClsMargin2, 1000u}); // take biggest margin, 1000 clusters minimum + mMaxNativeClustersPerBuffer = std::min(mMaxNativeClustersPerBuffer, mRec->GetProcessingSettings().tpcMaxAttachedClustersPerSectorRow); // upperbound given by configurable param } From a8ad17d8c02caea186159eb346f435d7e6897a33 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 16 Oct 2025 14:01:40 +0200 Subject: [PATCH 77/99] GPU: Make memoryStat work from GPUWorkflow --- GPU/GPUTracking/Base/GPUReconstruction.cxx | 2 +- GPU/GPUTracking/Base/GPUReconstructionCPU.cxx | 28 +++++++++++-------- GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 +- GPU/GPUTracking/Global/GPUChainTracking.cxx | 4 +++ .../GPUChainTrackingDebugAndProfiling.cxx | 26 +++++++++++------ .../Standalone/Benchmark/standalone.cxx | 8 ------ 6 files changed, 39 insertions(+), 31 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index 6d64fb3daca6a..e24b76678e710 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -999,7 +999,7 @@ void GPUReconstruction::PrintMemoryStatistics() } printf("%59s CPU / %9s GPU\n", "", ""); for (auto it = sizes.begin(); it != sizes.end(); it++) { - printf("Allocation %30s %s: Size %'14zu / %'14zu\n", it->first.c_str(), it->second[2] ? "P" : " ", it->second[0], it->second[1]); + printf("Allocation %50s %s: Size %'14zu / %'14zu\n", it->first.c_str(), it->second[2] ? "P" : " ", it->second[0], it->second[1]); } PrintMemoryOverview(); for (uint32_t i = 0; i < mChains.size(); i++) { diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx index 641b0a2d095ca..bdf1ade37868c 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx @@ -231,26 +231,24 @@ int32_t GPUReconstructionCPU::RunChains() } mTimerTotal.Start(); const std::clock_t cpuTimerStart = std::clock(); + int32_t retVal = 0; if (GetProcessingSettings().doublePipeline) { - int32_t retVal = EnqueuePipeline(); - if (retVal) { - return retVal; - } + retVal = EnqueuePipeline(); } else { if (mSlaves.size() || mMaster) { WriteConstantParams(); // Reinitialize // TODO: Get this in sync with GPUChainTracking::DoQueuedUpdates, and consider the doublePipeline } for (uint32_t i = 0; i < mChains.size(); i++) { - int32_t retVal = mChains[i]->RunChain(); - if (retVal) { - return retVal; - } - } - if (GetProcessingSettings().tpcFreeAllocatedMemoryAfterProcessing) { - ClearAllocatedMemory(); + retVal = mChains[i]->RunChain(); } } + if (retVal != 0 && retVal != 2) { + return retVal; + } mTimerTotal.Stop(); + if (GetProcessingSettings().tpcFreeAllocatedMemoryAfterProcessing) { + ClearAllocatedMemory(); + } mStatCPUTime += (double)(std::clock() - cpuTimerStart) / CLOCKS_PER_SEC; if (GetProcessingSettings().debugLevel >= 3 || GetProcessingSettings().allocDebugLevel) { GPUInfo("Allocated memory when ending processing %36s", ""); @@ -339,7 +337,13 @@ int32_t GPUReconstructionCPU::RunChains() mTimerTotal.Reset(); } - return 0; + if (GetProcessingSettings().memoryStat) { + PrintMemoryStatistics(); + } else if (GetProcessingSettings().debugLevel >= 2) { + PrintMemoryOverview(); + } + + return retVal; } void GPUReconstructionCPU::ResetDeviceProcessorTypes() diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index fc08b063ff16a..8cf6b29a43d96 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -377,6 +377,7 @@ AddOption(debugOnFailureMaxFiles, uint32_t, 0, "", 0, "Max number of files to ha AddOption(debugOnFailureMaxSize, uint32_t, 0, "", 0, "Max size of existing dumps in the target folder in GB") AddOption(debugOnFailureDirectory, std::string, ".", "", 0, "Target folder for debug / dump") AddOption(amdMI100SerializationWorkaround, bool, false, "", 0, "Enable workaround that mitigates MI100 serialization bug") +AddOption(memoryStat, bool, false, "", 0, "Print memory statistics") AddVariable(eventDisplay, o2::gpu::GPUDisplayFrontendInterface*, nullptr) AddSubConfig(GPUSettingsProcessingRTC, rtc) AddSubConfig(GPUSettingsProcessingRTCtechnical, rtctech) @@ -587,7 +588,6 @@ AddOption(zsVersion, int32_t, 2, "", 0, "ZS Version: 1 = 10-bit ADC row based, 2 AddOption(dumpEvents, bool, false, "", 0, "Dump events (after transformation such as encodeZS") AddOption(stripDumpedEvents, bool, false, "", 0, "Remove redundant inputs (e.g. digits and ZS) before dumping") AddOption(printSettings, int32_t, 0, "", 0, "Print all settings", def(1)) -AddOption(memoryStat, bool, false, "", 0, "Print memory statistics") AddOption(testSyncAsync, bool, false, "syncAsync", 0, "Test first synchronous and then asynchronous processing") AddOption(testSync, bool, false, "sync", 0, "Test settings for synchronous phase") AddOption(timeFrameTime, bool, false, "tfTime", 0, "Print some debug information about time frame processing time") diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 91870f981d542..14d0e04eb4dd3 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -278,6 +278,10 @@ bool GPUChainTracking::ValidateSettings() return false; } if (GetProcessingSettings().doublePipeline) { + if (GetProcessingSettings().tpcFreeAllocatedMemoryAfterProcessing) { + GPUError("Cannot use double pipeline with tpcFreeAllocatedMemoryAfterProcessing"); + return false; + } if (!GetRecoStepsOutputs().isOnlySet(GPUDataTypes::InOutType::TPCMergedTracks, GPUDataTypes::InOutType::TPCCompressedClusters, GPUDataTypes::InOutType::TPCClusters)) { GPUError("Invalid outputs for double pipeline mode 0x%x", (uint32_t)GetRecoStepsOutputs()); return false; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx index 15846246bca0a..fab7179876c04 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx @@ -142,8 +142,10 @@ void GPUChainTracking::PrintMemoryStatistics() std::map usageMap; for (int32_t i = 0; i < NSECTORS; i++) { #ifdef GPUCA_TPC_GEOMETRY_O2 - addToMap("TPC Clusterer Sector Peaks", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nPeaks, processors()->tpcClusterer[i].mNMaxPeaks); - addToMap("TPC Clusterer Sector Clusters", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nClusters, processors()->tpcClusterer[i].mNMaxClusters); + if (processors()->tpcClusterer[i].mPmemory) { + addToMap("TPC Clusterer Sector Peaks", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nPeaks, processors()->tpcClusterer[i].mNMaxPeaks); + addToMap("TPC Clusterer Sector Clusters", usageMap, processors()->tpcClusterer[i].mPmemory->counters.nClusters, processors()->tpcClusterer[i].mNMaxClusters); + } #endif addToMap("TPC Sector Start Hits", usageMap, *processors()->tpcTrackers[i].NStartHits(), processors()->tpcTrackers[i].NMaxStartHits()); addToMap("TPC Sector Tracklets", usageMap, *processors()->tpcTrackers[i].NTracklets(), processors()->tpcTrackers[i].NMaxTracklets()); @@ -152,8 +154,10 @@ void GPUChainTracking::PrintMemoryStatistics() addToMap("TPC Sector TrackHits", usageMap, *processors()->tpcTrackers[i].NTrackHits(), processors()->tpcTrackers[i].NMaxTrackHits()); } addToMap("TPC Clusterer Clusters", usageMap, mRec->MemoryScalers()->nTPCHits, mRec->MemoryScalers()->NTPCClusters(mRec->MemoryScalers()->nTPCdigits)); - addToMap("TPC Tracks", usageMap, processors()->tpcMerger.NMergedTracks(), processors()->tpcMerger.NMaxTracks()); - addToMap("TPC TrackHits", usageMap, processors()->tpcMerger.NMergedTrackClusters(), processors()->tpcMerger.NMaxMergedTrackClusters()); + if (processors()->tpcMerger.Memory()) { + addToMap("TPC Tracks", usageMap, processors()->tpcMerger.NMergedTracks(), processors()->tpcMerger.NMaxTracks()); + addToMap("TPC TrackHits", usageMap, processors()->tpcMerger.NMergedTrackClusters(), processors()->tpcMerger.NMaxMergedTrackClusters()); + } if (mRec->GetProcessingSettings().createO2Output) { addToMap("TPC O2 Tracks", usageMap, processors()->tpcMerger.NOutputTracksTPCO2(), processors()->tpcMerger.NOutputTracksTPCO2()); @@ -161,9 +165,11 @@ void GPUChainTracking::PrintMemoryStatistics() } #ifdef GPUCA_TPC_GEOMETRY_O2 - addToMap("TPC ComprCache HitsAttached", usageMap, processors()->tpcCompressor.mOutput->nAttachedClusters, processors()->tpcCompressor.mMaxTrackClusters); - addToMap("TPC ComprCache HitsUnattached", usageMap, processors()->tpcCompressor.mOutput->nUnattachedClusters, processors()->tpcCompressor.mMaxClustersInCache); - addToMap("TPC ComprCache Tracks", usageMap, processors()->tpcCompressor.mOutput->nTracks, processors()->tpcCompressor.mMaxTracks); + if (processors()->tpcCompressor.mOutput) { + addToMap("TPC ComprCache HitsAttached", usageMap, processors()->tpcCompressor.mOutput->nAttachedClusters, processors()->tpcCompressor.mMaxTrackClusters); + addToMap("TPC ComprCache HitsUnattached", usageMap, processors()->tpcCompressor.mOutput->nUnattachedClusters, processors()->tpcCompressor.mMaxClustersInCache); + addToMap("TPC ComprCache Tracks", usageMap, processors()->tpcCompressor.mOutput->nTracks, processors()->tpcCompressor.mMaxTracks); + } #endif for (auto& elem : usageMap) { @@ -180,8 +186,10 @@ void GPUChainTracking::PrintMemoryRelations() GPUInfo("MEMREL SectorTracks NCl %d NTrk %d", processors()->tpcTrackers[i].NHitsTotal(), *processors()->tpcTrackers[i].NTracks()); GPUInfo("MEMREL SectorTrackHits NCl %d NTrkH %d", processors()->tpcTrackers[i].NHitsTotal(), *processors()->tpcTrackers[i].NTrackHits()); } - GPUInfo("MEMREL Tracks NCl %d NTrk %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTracks()); - GPUInfo("MEMREL TrackHitss NCl %d NTrkH %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTrackClusters()); + if (processors()->tpcMerger.Memory()) { + GPUInfo("MEMREL Tracks NCl %d NTrk %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTracks()); + GPUInfo("MEMREL TrackHitss NCl %d NTrkH %d", processors()->tpcMerger.NMaxClusters(), processors()->tpcMerger.NMergedTrackClusters()); + } } void GPUChainTracking::PrepareKernelDebugOutput() diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index f9c53e3ffd59c..4fe1691afef50 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -649,11 +649,6 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU if (tmpRetVal == 0 || tmpRetVal == 2) { OutputStat(chainTrackingUse, iRun == 0 ? nTracksTotal : nullptr, iRun == 0 ? nClustersTotal : nullptr); - if (configStandalone.memoryStat) { - recUse->PrintMemoryStatistics(); - } else if (configStandalone.proc.debugLevel >= 2) { - recUse->PrintMemoryOverview(); - } } if (tmpRetVal == 0 && configStandalone.testSyncAsync) { @@ -685,9 +680,6 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU tmpRetVal = recAsync->RunChains(); if (tmpRetVal == 0 || tmpRetVal == 2) { OutputStat(chainTrackingAsync, nullptr, nullptr); - if (configStandalone.memoryStat) { - recAsync->PrintMemoryStatistics(); - } } recAsync->ClearAllocatedMemory(); } From fee164c1e97ebf3f0f3224167a61b0cb3bac56e0 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 16 Oct 2025 15:06:20 +0200 Subject: [PATCH 78/99] GPU TPC Decompression: all temporary memory should go to the stack and be freed --- GPU/GPUTracking/Base/GPUReconstruction.cxx | 6 +++++- .../DataCompression/GPUTPCDecompression.cxx | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index e24b76678e710..a05736d519bd0 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -639,7 +639,7 @@ void GPUReconstruction::AllocateRegisteredMemoryInternal(GPUMemoryResource* res, res->mPtr = GPUProcessor::alignPointer(res->mPtrDevice); res->SetPointers(res->mPtr); if (GetProcessingSettings().allocDebugLevel >= 2) { - std::cout << (res->mReuse >= 0 ? "Reused " : "Allocated ") << res->mName << ": " << res->mSize << "\n"; + std::cout << (res->mReuse >= 0 ? "Reused " : "Allocated ") << res->mName << ": " << res->mSize << " (individual" << ((res->mType & GPUMemoryResource::MEMORY_STACK) ? " stack" : "") << ")\n"; } if (res->mType & GPUMemoryResource::MEMORY_STACK) { mNonPersistentIndividualAllocations.emplace_back(res); @@ -896,8 +896,12 @@ void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag) } mHostMemoryPoolEnd = std::get<0>(mNonPersistentMemoryStack.back()); mDeviceMemoryPoolEnd = std::get<1>(mNonPersistentMemoryStack.back()); + std::cout << "FOOOO POP " << std::get<2>(mNonPersistentMemoryStack.back()) << " - " << mNonPersistentIndividualAllocations.size(); for (uint32_t i = std::get<2>(mNonPersistentMemoryStack.back()); i < mNonPersistentIndividualAllocations.size(); i++) { GPUMemoryResource* res = mNonPersistentIndividualAllocations[i]; + if (GetProcessingSettings().allocDebugLevel >= 2 && (res->mPtr || res->mPtrDevice)) { + std::cout << "Freeing NonPersistent " << res->mName << ": size " << res->mSize << " (reused " << res->mReuse << ")\n"; + } if (res->mReuse < 0) { operator delete(res->mPtrDevice, std::align_val_t(GPUCA_BUFFER_ALIGNMENT)); } diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx index 7e7ee86623099..7f5e485c54409 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx @@ -107,12 +107,12 @@ void GPUTPCDecompression::RegisterMemoryAllocation() { AllocateAndInitializeLate(); mMemoryResInputGPU = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersInputGPU, GPUMemoryResource::MEMORY_INPUT_FLAG | GPUMemoryResource::MEMORY_GPU | GPUMemoryResource::MEMORY_EXTERNAL | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionInput"); - mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersGPU, GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersGPU"); - mResourceTmpIndexes = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersOutput, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersOutput"); - mResourceTmpClustersOffsets = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersInput, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersInput"); - mResourceTmpBufferBeforeFiltering = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpClusterNativeAccessForFiltering, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBufferForFiltering"); - mResourceClusterNativeAccess = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersInputClusterNativeAccess, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpClusterAccessForFiltering"); - mResourceNClusterPerSectorRow = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersNClusterPerSectorRow, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpClusterCountForFiltering"); + mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersGPU, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBuffersGPU"); + mResourceTmpIndexes = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersOutput, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBuffersOutput"); + mResourceTmpClustersOffsets = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersInput, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBuffersInput"); + mResourceTmpBufferBeforeFiltering = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpClusterNativeAccessForFiltering, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpBufferForFiltering"); + mResourceClusterNativeAccess = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersInputClusterNativeAccess, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpClusterAccessForFiltering"); + mResourceNClusterPerSectorRow = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersNClusterPerSectorRow, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_STACK, "TPCDecompressionTmpClusterCountForFiltering"); } void GPUTPCDecompression::SetMaxData(const GPUTrackingInOutPointers& io) From 8d20554968cf3c2a9e8afcd34db69ce48ecaac33 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 17 Oct 2025 11:25:19 +0200 Subject: [PATCH 79/99] GPU TPC: Tracklet memory during seeding when running on the host should be on the stack --- GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx index 41530cb629ce8..7897de4f2002e 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx @@ -102,9 +102,9 @@ void GPUTPCTracker::RegisterMemoryAllocation() uint32_t type = GPUMemoryResource::MEMORY_SCRATCH; if (mRec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { // For individual scheme, we allocate tracklets separately, and change the type for the following allocations to custom type |= GPUMemoryResource::MEMORY_CUSTOM; - mMemoryResTracklets = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersTracklets, type, "TPCTrackerTracklets"); + mMemoryResTracklets = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersTracklets, type | GPUMemoryResource::MEMORY_STACK, "TPCTrackerTracklets"); } - mMemoryResOutput = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersOutput, type, "TPCTrackerTracks"); + mMemoryResOutput = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersOutput, type, "TPCTrackerTracks"); // TODO: Ideally this should eventually go on the stack, so that we can free it after the first phase of track merging } GPUhd() void* GPUTPCTracker::SetPointersTracklets(void* mem) From 0b14fcdef0314f43eba5a1117d378e812ea02eae Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 17 Oct 2025 11:25:52 +0200 Subject: [PATCH 80/99] GPU: Add option to free individual stacked allocations per processor on the host --- GPU/GPUTracking/Base/GPUReconstruction.cxx | 23 ++++++++++++++-------- GPU/GPUTracking/Base/GPUReconstruction.h | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index a05736d519bd0..f00c856ad1ff2 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -877,8 +877,11 @@ void GPUReconstruction::PushNonPersistentMemory(uint64_t tag) mNonPersistentMemoryStack.emplace_back(mHostMemoryPoolEnd, mDeviceMemoryPoolEnd, mNonPersistentIndividualAllocations.size(), mNonPersistentIndividualDirectAllocations.size(), tag); } -void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag) +void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag, const GPUProcessor* proc) { + if (proc && GetProcessingSettings().memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + GPUFatal("Processor-depending memory-free works only with allocation strategy ALLOCATION_INDIVIDUAL"); + } if (GetProcessingSettings().keepDisplayMemory || GetProcessingSettings().disableMemoryReuse) { return; } @@ -888,17 +891,17 @@ void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag) if (tag != 0 && std::get<4>(mNonPersistentMemoryStack.back()) != tag) { GPUFatal("Tag mismatch when popping non persistent memory from stack : pop %s vs on stack %s", qTag2Str(tag).c_str(), qTag2Str(std::get<4>(mNonPersistentMemoryStack.back())).c_str()); } - if ((GetProcessingSettings().debugLevel >= 3 || GetProcessingSettings().allocDebugLevel) && (IsGPU() || GetProcessingSettings().forceHostMemoryPoolSize)) { + if (!proc && (GetProcessingSettings().debugLevel >= 3 || GetProcessingSettings().allocDebugLevel) && (IsGPU() || GetProcessingSettings().forceHostMemoryPoolSize)) { printf("Allocated memory after %30s (%8s) (Stack %zu): ", GPUDataTypes::RECO_STEP_NAMES[getRecoStepNum(step, true)], qTag2Str(std::get<4>(mNonPersistentMemoryStack.back())).c_str(), mNonPersistentMemoryStack.size()); PrintMemoryOverview(); printf("%76s", ""); PrintMemoryMax(); } - mHostMemoryPoolEnd = std::get<0>(mNonPersistentMemoryStack.back()); - mDeviceMemoryPoolEnd = std::get<1>(mNonPersistentMemoryStack.back()); - std::cout << "FOOOO POP " << std::get<2>(mNonPersistentMemoryStack.back()) << " - " << mNonPersistentIndividualAllocations.size(); for (uint32_t i = std::get<2>(mNonPersistentMemoryStack.back()); i < mNonPersistentIndividualAllocations.size(); i++) { GPUMemoryResource* res = mNonPersistentIndividualAllocations[i]; + if (proc && res->mProcessor != proc) { + continue; + } if (GetProcessingSettings().allocDebugLevel >= 2 && (res->mPtr || res->mPtrDevice)) { std::cout << "Freeing NonPersistent " << res->mName << ": size " << res->mSize << " (reused " << res->mReuse << ")\n"; } @@ -908,9 +911,13 @@ void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag) res->mPtr = nullptr; res->mPtrDevice = nullptr; } - mNonPersistentIndividualAllocations.resize(std::get<2>(mNonPersistentMemoryStack.back())); - mNonPersistentIndividualDirectAllocations.resize(std::get<3>(mNonPersistentMemoryStack.back())); - mNonPersistentMemoryStack.pop_back(); + if (!proc) { + mHostMemoryPoolEnd = std::get<0>(mNonPersistentMemoryStack.back()); + mDeviceMemoryPoolEnd = std::get<1>(mNonPersistentMemoryStack.back()); + mNonPersistentIndividualAllocations.resize(std::get<2>(mNonPersistentMemoryStack.back())); + mNonPersistentIndividualDirectAllocations.resize(std::get<3>(mNonPersistentMemoryStack.back())); + mNonPersistentMemoryStack.pop_back(); + } } void GPUReconstruction::BlockStackedMemory(GPUReconstruction* rec) diff --git a/GPU/GPUTracking/Base/GPUReconstruction.h b/GPU/GPUTracking/Base/GPUReconstruction.h index 420e602e61352..b98f5660a933e 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -179,7 +179,7 @@ class GPUReconstruction void ReturnVolatileMemory(); ThrustVolatileAllocator getThrustVolatileDeviceAllocator(); void PushNonPersistentMemory(uint64_t tag); - void PopNonPersistentMemory(RecoStep step, uint64_t tag); + void PopNonPersistentMemory(RecoStep step, uint64_t tag, const GPUProcessor* proc = nullptr); void BlockStackedMemory(GPUReconstruction* rec); void UnblockStackedMemory(); void ResetRegisteredMemoryPointers(GPUProcessor* proc); From e0ecb515ee62c579daab794482dd52e766c8afa1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 17 Oct 2025 16:26:51 +0200 Subject: [PATCH 81/99] GPU: Make memory allocation and freeing of individual stacked memory thread-safe --- GPU/GPUTracking/Base/GPUReconstruction.cxx | 7 ++++ GPU/GPUTracking/Base/GPUReconstruction.h | 2 + GPU/GPUTracking/utils/stdspinlock.h | 44 ++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 GPU/GPUTracking/utils/stdspinlock.h diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index f00c856ad1ff2..5129ccc4becf1 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -40,6 +40,7 @@ #include "GPULogging.h" #include "utils/strtag.h" +#include "utils/stdspinlock.h" #ifdef GPUCA_O2_LIB #include "GPUO2InterfaceConfiguration.h" @@ -589,6 +590,7 @@ size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, throw std::bad_alloc(); } size_t retVal; + stdspinlock spinlock(mMemoryMutex); if ((res->mType & GPUMemoryResource::MEMORY_STACK) && memorypoolend) { retVal = ptrDiff((res->*setPtr)((char*)1), (char*)(1)); memorypoolend = (void*)((char*)memorypoolend - GPUProcessor::getAlignmentMod(memorypoolend)); @@ -642,6 +644,7 @@ void GPUReconstruction::AllocateRegisteredMemoryInternal(GPUMemoryResource* res, std::cout << (res->mReuse >= 0 ? "Reused " : "Allocated ") << res->mName << ": " << res->mSize << " (individual" << ((res->mType & GPUMemoryResource::MEMORY_STACK) ? " stack" : "") << ")\n"; } if (res->mType & GPUMemoryResource::MEMORY_STACK) { + stdspinlock spinlock(mMemoryMutex); mNonPersistentIndividualAllocations.emplace_back(res); } if ((size_t)res->mPtr % GPUCA_BUFFER_ALIGNMENT) { @@ -722,6 +725,7 @@ size_t GPUReconstruction::AllocateRegisteredMemory(int16_t ires, GPUOutputContro void* GPUReconstruction::AllocateDirectMemory(size_t size, int32_t type) { + stdspinlock spinlock(mMemoryMutex); if (GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { char* retVal = new (std::align_val_t(GPUCA_BUFFER_ALIGNMENT)) char[size]; if ((type & GPUMemoryResource::MEMORY_STACK)) { @@ -763,6 +767,7 @@ void* GPUReconstruction::AllocateDirectMemory(size_t size, int32_t type) void* GPUReconstruction::AllocateVolatileDeviceMemory(size_t size) { + stdspinlock spinlock(mMemoryMutex); if (mVolatileMemoryStart == nullptr) { mVolatileMemoryStart = mDeviceMemoryPool; } @@ -788,6 +793,7 @@ void* GPUReconstruction::AllocateVolatileMemory(size_t size, bool device) return AllocateVolatileDeviceMemory(size); } char* retVal = new (std::align_val_t(GPUCA_BUFFER_ALIGNMENT)) char[size]; + stdspinlock spinlock(mMemoryMutex); mVolatileChunks.emplace_back(retVal, alignedDeleter()); return retVal; } @@ -912,6 +918,7 @@ void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag, cons res->mPtrDevice = nullptr; } if (!proc) { + stdspinlock spinlock(mMemoryMutex); mHostMemoryPoolEnd = std::get<0>(mNonPersistentMemoryStack.back()); mDeviceMemoryPoolEnd = std::get<1>(mNonPersistentMemoryStack.back()); mNonPersistentIndividualAllocations.resize(std::get<2>(mNonPersistentMemoryStack.back())); diff --git a/GPU/GPUTracking/Base/GPUReconstruction.h b/GPU/GPUTracking/Base/GPUReconstruction.h index b98f5660a933e..b7eda77aeb9fe 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "GPUDataTypes.h" #include "GPUMemoryResource.h" @@ -390,6 +391,7 @@ class GPUReconstruction std::vector> mNonPersistentIndividualDirectAllocations; std::vector> mDirectMemoryChunks; std::vector> mVolatileChunks; + std::atomic_flag mMemoryMutex = ATOMIC_FLAG_INIT; std::unique_ptr mPipelineContext; diff --git a/GPU/GPUTracking/utils/stdspinlock.h b/GPU/GPUTracking/utils/stdspinlock.h new file mode 100644 index 0000000000000..14bf95c45968e --- /dev/null +++ b/GPU/GPUTracking/utils/stdspinlock.h @@ -0,0 +1,44 @@ +// 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 stdspinlock.h +/// \author David Rohr + +#ifndef Q_STDSPINLOCK_H +#define Q_STDSPINLOCK_H + +#include + +class stdspinlock +{ + public: + stdspinlock(std::atomic_flag& flag) : mFlag(&flag) + { + while (flag.test_and_set(std::memory_order_acquire)) { + } + } + void release() + { + if (mFlag) { + mFlag->clear(std::memory_order_release); + mFlag = nullptr; + } + } + ~stdspinlock() + { + release(); + } + + private: + std::atomic_flag* mFlag; +}; + +#endif // Q_STDSPINLOCK_H From 93c791a075348739f01bd3ef0daa420069072d4f Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 17 Oct 2025 11:26:17 +0200 Subject: [PATCH 82/99] GPU TPC: Free sector tracking memory earlier --- GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx | 3 +++ GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx | 7 ++++++- GPU/GPUTracking/SectorTracker/GPUTPCTracker.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx b/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx index d13e8d5544631..7ab2cfeccee80 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx @@ -224,6 +224,9 @@ int32_t GPUChainTracking::RunTPCTrackingSectors_internal() GPUInfo("Sector %u, Number of tracks: %d", iSector, *trk.NTracks()); } DoDebugAndDump(RecoStep::TPCSectorTracking, GPUChainTrackingDebugFlags::TPCSectorTracks, trk, &GPUTPCTracker::DumpTrackHits, *mDebugFile); + if (GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL && !trk.MemoryReuseAllowed()) { + mRec->PopNonPersistentMemory(RecoStep::TPCSectorTracking, qStr2Tag("TPCSLTRK"), &trk); + } }); mRec->SetNActiveThreadsOuterLoop(1); if (error) { diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx index 7897de4f2002e..c5e6a21460a36 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx @@ -84,10 +84,15 @@ void* GPUTPCTracker::SetPointersCommon(void* mem) return mem; } +bool GPUTPCTracker::MemoryReuseAllowed() +{ + return !mRec->GetProcessingSettings().keepDisplayMemory && ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSectorTracking) || mRec->GetProcessingSettings().inKernelParallel == 1 || mRec->GetProcessingSettings().nHostThreads == 1); +} + void GPUTPCTracker::RegisterMemoryAllocation() { AllocateAndInitializeLate(); - bool reuseCondition = !mRec->GetProcessingSettings().keepDisplayMemory && ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSectorTracking) || mRec->GetProcessingSettings().inKernelParallel == 1 || mRec->GetProcessingSettings().nHostThreads == 1); + bool reuseCondition = MemoryReuseAllowed(); GPUMemoryReuse reLinks{reuseCondition, GPUMemoryReuse::REUSE_1TO1, GPUMemoryReuse::TrackerDataLinks, (uint16_t)(mISector % mRec->GetProcessingSettings().nStreams)}; mMemoryResLinks = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataLinks, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK, "TPCSectorLinks", reLinks); mMemoryResSectorScratch = mRec->RegisterMemoryAllocation(this, &GPUTPCTracker::SetPointersDataScratch, GPUMemoryResource::MEMORY_SCRATCH | GPUMemoryResource::MEMORY_STACK | GPUMemoryResource::MEMORY_CUSTOM, "TPCSectorScratch"); diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h index 2667da4a53977..aee429c959e98 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h @@ -103,6 +103,7 @@ class GPUTPCTracker : public GPUProcessor void* SetPointersTracklets(void* mem); void* SetPointersOutput(void* mem); void RegisterMemoryAllocation(); + bool MemoryReuseAllowed(); int16_t MemoryResLinks() const { return mMemoryResLinks; } int16_t MemoryResScratchHost() const { return mMemoryResScratchHost; } From 17eca6998bfb7b7be2ec90a00628a85b7c8d0dbb Mon Sep 17 00:00:00 2001 From: amorsch Date: Thu, 16 Oct 2025 20:44:09 +0200 Subject: [PATCH 83/99] corrected positions of volumes in RB24 after recent Cave updates --- Detectors/FIT/FDD/base/src/Geometry.cxx | 2 +- Detectors/Passive/src/Compensator.cxx | 2 +- Detectors/ZDC/simulation/src/Detector.cxx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/FIT/FDD/base/src/Geometry.cxx b/Detectors/FIT/FDD/base/src/Geometry.cxx index 441a30fdda44f..149c086f4fc81 100644 --- a/Detectors/FIT/FDD/base/src/Geometry.cxx +++ b/Detectors/FIT/FDD/base/src/Geometry.cxx @@ -152,7 +152,7 @@ void Geometry::buildGeometry() if (!vCaveRB24) { LOG(fatal) << "Could not find the top volume for A-side"; } - const Float_t kPosFDA = 1696.67 - 1313.347; // z-center of assembly (cm) + const Float_t kPosFDA = 1696.67 - 1313.347 - 75.; // z-center of assembly (cm) vCaveRB24->AddNode(vFDAarray, 1, new TGeoTranslation(0., 0., kPosFDA - kFDACelldz / 2. - 0.1)); vCaveRB24->AddNode(vFDAarray, 2, new TGeoTranslation(0., 0., kPosFDA + kFDACelldz / 2. + 0.1)); diff --git a/Detectors/Passive/src/Compensator.cxx b/Detectors/Passive/src/Compensator.cxx index 68e344495aaab..25b3e2a475340 100644 --- a/Detectors/Passive/src/Compensator.cxx +++ b/Detectors/Passive/src/Compensator.cxx @@ -110,7 +110,7 @@ void Compensator::ConstructGeometry() void Compensator::createCompensator() { auto top = gGeoManager->GetVolume("caveRB24"); - top->AddNode(createMagnetYoke(), 1, new TGeoTranslation(0., 0., 1075. - 1313.347)); + top->AddNode(createMagnetYoke(), 1, new TGeoTranslation(0., 0., 1000. - 1313.347)); } TGeoVolume* Compensator::createMagnetYoke() diff --git a/Detectors/ZDC/simulation/src/Detector.cxx b/Detectors/ZDC/simulation/src/Detector.cxx index c6f91d9c2164f..b8b81379a4dff 100644 --- a/Detectors/ZDC/simulation/src/Detector.cxx +++ b/Detectors/ZDC/simulation/src/Detector.cxx @@ -2355,7 +2355,7 @@ void Detector::createDetectors() // --- Positioning the ZEM into the ZDC - rotation for 90 degrees // NB -> ZEM is positioned in cave volume - const float z0 = 1313.3475; // center of caveRB24 mother volume + const float z0 = 1313.3475 + 75.; // center of caveRB24 mother volume TVirtualMC::GetMC()->Gspos("ZEM ", 1, "caveRB24", -Geometry::ZEMPOSITION[0], Geometry::ZEMPOSITION[1], Geometry::ZEMPOSITION[2] + Geometry::ZEMDIMENSION[0] - z0, irotzem1, "ONLY"); // Second EM ZDC (same side w.r.t. IP, just on the other side w.r.t. beam pipe) From 91be07d539f81a90aa024b7f005859e4b4f67ef5 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Sun, 12 Oct 2025 17:38:00 +0200 Subject: [PATCH 84/99] Example to run HERWIG7 with o2-sim --- run/SimExamples/HepMC_HERWIG7/LHC.in | 49 +++++++++ run/SimExamples/HepMC_HERWIG7/README.md | 25 +++++ run/SimExamples/HepMC_HERWIG7/runo2sim.sh | 124 ++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 run/SimExamples/HepMC_HERWIG7/LHC.in create mode 100755 run/SimExamples/HepMC_HERWIG7/README.md create mode 100755 run/SimExamples/HepMC_HERWIG7/runo2sim.sh diff --git a/run/SimExamples/HepMC_HERWIG7/LHC.in b/run/SimExamples/HepMC_HERWIG7/LHC.in new file mode 100644 index 0000000000000..ef3641bf7b174 --- /dev/null +++ b/run/SimExamples/HepMC_HERWIG7/LHC.in @@ -0,0 +1,49 @@ +# -*- ThePEG-repository -*- +################################################################################ +# This file contains our best tune to UE data from ATLAS at 7 TeV. More recent +# tunes and tunes for other centre-of-mass energies as well as more usage +# instructions can be obtained from this Herwig wiki page: +# http://projects.hepforge.org/herwig/trac/wiki/MB_UE_tunes +# The model for soft interactions and diffractions is explained in +# [S. Gieseke, P. Kirchgaesser, F. Loshaj, arXiv:1612.04701] +################################################################################ + +read snippets/PPCollider.in + +################################################## +# Technical parameters for this run +################################################## +cd /Herwig/Generators +################################################## +# LHC physics parameters (override defaults here) +################################################## +set EventGenerator:EventHandler:LuminosityFunction:Energy 13600.0 + +# Minimum Bias +read snippets/MB.in + +# Recommended set of parameters for MB/UE runs + +set /Herwig/Hadronization/ColourReconnector:ReconnectionProbability 0.5 +set /Herwig/UnderlyingEvent/MPIHandler:pTmin0 3.502683 +set /Herwig/UnderlyingEvent/MPIHandler:InvRadius 1.402055 +set /Herwig/UnderlyingEvent/MPIHandler:Power 0.416852 +set /Herwig/Partons/RemnantDecayer:ladderPower -0.08 +set /Herwig/Partons/RemnantDecayer:ladderNorm 0.95 + +################################################## +# Analyses +################################################## +## Hepmc file creation +create ThePEG::HepMCFile /Herwig/Analysis/HepMC HepMCAnalysis.so +set /Herwig/Analysis/HepMC:PrintEvent 10 +set /Herwig/Analysis/HepMC:Format GenEvent +set /Herwig/Analysis/HepMC:Units GeV_mm +set /Herwig/Analysis/HepMC:Filename herwig.hepmc +insert /Herwig/Generators/EventGenerator:AnalysisHandlers 0 /Herwig/Analysis/HepMC + + +################################################## +# Save run for later usage with 'Herwig run' +################################################## +saverun LHC EventGenerator \ No newline at end of file diff --git a/run/SimExamples/HepMC_HERWIG7/README.md b/run/SimExamples/HepMC_HERWIG7/README.md new file mode 100755 index 0000000000000..6366c1f076672 --- /dev/null +++ b/run/SimExamples/HepMC_HERWIG7/README.md @@ -0,0 +1,25 @@ + + +The usage of HERWIG7 with the O2 machinery is presented in this short manual. +The example generates HEPMC3 data using the Herwig executable and then +reads the data via the hepmc generator defined in o2-sim. + +# Files description + +Two files are provided in the folder: +- **runo2sim.sh** → allows the generation of events using o2-sim +- **LHC.in** → example input file for the configuration of the HERWIG generator + +## runo2sim.sh + +The script works after loading any O2sim version containing HERWIG7 as a package (dependence of AliGenO2). + +If no parameters are provided, the script will run with default values (energy and nEvents provided in the LHC.in file), but few flags are available to change the settings on-the-fly: +- **-m , --more** → feeds the simulation with advanced parameters provided to the configuration key flags +- **-n , --nevents** → changes the number of events in the .in file or gets the one in the file if no events are provided +- **-i , --input** → .in filename for HERWIG7 configuration +- **-j , --jobs** → sets the number of workers (2 jobs by default) +- **-e , --ecm** → sets the center-of-mass energy in the input file +- **-h , --help** → prints usage instructions \ No newline at end of file diff --git a/run/SimExamples/HepMC_HERWIG7/runo2sim.sh b/run/SimExamples/HepMC_HERWIG7/runo2sim.sh new file mode 100755 index 0000000000000..536a47de01d5c --- /dev/null +++ b/run/SimExamples/HepMC_HERWIG7/runo2sim.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# +# This is a simple simulation example on how to generate HEPMC3 data from +# HERWIG7 and run an ALICE simulation using the o2-sim executable +# In the script we assume that the .run file has the same name of the input file, so change it accordingly. +# This script works only with AliGenO2 version containing the HERWIG7 generator + +# HERWIG7 and O2 must be loaded +set -x +if [ ! "${HERWIG_ROOT}" ]; then + echo "This needs HERWIG7 loaded; alienv enter ..." + exit 1 +fi + +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 2 + +NEV=-1 +more="" +input="LHC" +eCM=-1 +JOBS=2 + +usage() +{ + cat </dev/stderr + exit 3 + ;; + esac + shift +done + +echo "Input file: $input" + +if [ ! -f $input.in ]; then + echo "Error: Input file $input.in not found" + exit 4 +else + if grep -Fq "saverun" $input.in; then + sed -i "/saverun/c\saverun $input EventGenerator" $input.in + else + echo "saverun $input EventGenerator" >> $input.in + fi +fi + +# Set number of events to write in HepMC in input file +if [ ! $NEV -eq -1 ]; then + echo "Setting number of events to $NEV" + if grep -Fq "PrintEvent" $input.in; then + sed -i "/PrintEvent/c\set /Herwig/Analysis/HepMC:PrintEvent $NEV" $input.in + else + echo "set /Herwig/Analysis/HepMC:PrintEvent $NEV" >> $input.in + fi +else + echo "Number of events not set, checking input file..." + if grep -Fq "PrintEvent" $input.in; then + NEV=$(grep -F "PrintEvent" $input.in | awk '{print $3}') + echo "Number of events set to $NEV" + else + echo "Error: Number of events not set in HERWIG7" + exit 5 + fi +fi + +# Set ECM + +if [ ! $eCM -eq -1 ]; then + echo "Setting eCM to $eCM" + if grep -Fq "Energy" $input.in; then + sed -i "/Energy/c\set EventGenerator:EventHandler:LuminosityFunction:Energy $eCM" $input.in + else + echo "set EventGenerator:EventHandler:LuminosityFunction:Energy $eCM" >> $input.in + fi +else + echo "Energy not set, checking input file..." + if grep -Fq "Energy" $input.in; then + eCM=$(grep -F "Energy" $input.in | awk '{print $3}') + echo "Energy set to $eCM" + else + echo "Error: eCM not set in HERWIG7" + exit 6 + fi +fi + +# Generating events using HERWIG7 +Herwig read --repo=${HERWIG_ROOT}/share/Herwig/HerwigDefaults.rpo $input.in +Herwig run -N $NEV $input.run + +# Starting simulation with o2-sim +o2-sim -j $JOBS -n ${NEV} -g hepmc \ + --configKeyValues "GeneratorFileOrCmd.fileNames=herwig.hepmc;${more}" From 6e0035f12a1e29a8be68b7c4bdc8c1deb9283036 Mon Sep 17 00:00:00 2001 From: swenzel Date: Fri, 9 May 2025 12:14:57 +0200 Subject: [PATCH 85/99] ability to take inject external vertices in collision context an important step for the mc-on-data embedding --- .../DigitizationContext.h | 6 ++ .../simulation/src/DigitizationContext.cxx | 11 +++ Steer/src/CollisionContextTool.cxx | 89 ++++++++++++++++++- 3 files changed, 105 insertions(+), 1 deletion(-) diff --git a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h index b718b2d5eb804..0dc3806e52cf2 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h +++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h @@ -141,6 +141,12 @@ class DigitizationContext // have to have same vertex, as well as event ids associated to same collision. void sampleInteractionVertices(o2::dataformats::MeanVertexObject const& v); + // Function allowing to inject interaction vertixes from the outside. + // Useful when this is given from data for instance. The vertex vector needs to be of same + // size as the interaction record. + // Returns 0 if success. 1 if there is a problem. + int setInteractionVertices(std::vector> const& vertices); + // helper functions to save and load a context void saveToFile(std::string_view filename) const; diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index dc3c560a1485b..9e8a125c06fa4 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -635,6 +635,17 @@ struct pair_hash { }; } // namespace +int DigitizationContext::setInteractionVertices(std::vector> const& external_vertices) +{ + if (external_vertices.size() != mEventRecords.size()) { + LOG(error) << "Size mismatch with event record"; + return 1; + } + mInteractionVertices.clear(); + std::copy(external_vertices.begin(), external_vertices.end(), std::back_inserter(mInteractionVertices)); + return 0; +} + void DigitizationContext::sampleInteractionVertices(o2::dataformats::MeanVertexObject const& meanv) { // mapping of source x event --> index into mInteractionVertices diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index 9fc3e548ff213..1733caaa92eed 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -28,6 +28,7 @@ #include #include "DataFormatsParameters/GRPLHCIFData.h" #include "SimConfig/SimConfig.h" +#include // // Created by Sandro Wenzel on 13.07.21. @@ -59,6 +60,10 @@ struct Options { // format is path prefix std::string vertexModeString{"kNoVertex"}; // Vertex Mode; vertices will be assigned to collisions of mode != kNoVertex o2::conf::VertexMode vertexMode = o2::conf::VertexMode::kNoVertex; + std::string external_path = ""; // optional external path where we can directly take the collision contexts + // This is useful when someone else is creating the contexts (MC-data embedding) and we + // merely want to pass these through. If this is given, we simply take the timeframe ID, number of orbits + // and copy the right amount of timeframes into the destination folder (implies individualTFextraction) }; enum class InteractionLockMode { @@ -210,7 +215,9 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) "with-vertices", bpo::value(&optvalues.vertexModeString)->default_value("kNoVertex"), "Assign vertices to collisions. Argument is the vertex mode. Defaults to no vertexing applied")( "timestamp", bpo::value(&optvalues.timestamp)->default_value(-1L), "Timestamp for CCDB queries / anchoring")( "extract-per-timeframe", bpo::value(&optvalues.individualTFextraction)->default_value(""), - "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]"); + "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]")( + "import-external", bpo::value(&optvalues.external_path)->default_value(""), + "Take collision contexts (per timeframe) from external files for instance for data-anchoring use-case. Needs timeframeID and number of orbits to be given as well."); options.add_options()("help,h", "Produce help message."); @@ -249,6 +256,47 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) return true; } +bool copy_collision_context(const std::string& external_path, int this_tf_id, int target_tf_id) +{ + namespace fs = std::filesystem; + try { + // Construct source file path + fs::path filename = fs::path(external_path) / ("collission_context_" + std::to_string(this_tf_id) + ".root"); + + LOG(info) << "Checking existence of file: " << filename; + + if (fs::exists(filename)) { + // Build destination path + std::string path_prefix = "tf"; // Can be made configurable + std::stringstream destination_path_stream; + destination_path_stream << path_prefix << (target_tf_id) << "/collisioncontext.root"; + fs::path destination_path = destination_path_stream.str(); + + // Ensure parent directory exists + fs::path destination_dir = destination_path.parent_path(); + if (!fs::exists(destination_dir)) { + fs::create_directories(destination_dir); + LOG(info) << "Created directory: " << destination_dir; + } + + // Copy file + fs::copy_file(filename, destination_path, fs::copy_options::overwrite_existing); + LOG(info) << "Copied file to: " << destination_path; + return true; + } else { + LOG(warning) << "Source file does not exist: " << filename; + return false; + } + } catch (const fs::filesystem_error& e) { + LOG(error) << "Filesystem error: " << e.what(); + return false; + } catch (const std::exception& e) { + LOG(error) << "Unexpected error: " << e.what(); + return false; + } + return true; +} + int main(int argc, char* argv[]) { Options options; @@ -259,6 +307,45 @@ int main(int argc, char* argv[]) // init params o2::conf::ConfigurableParam::updateFromString(options.configKeyValues); + // See if this is external mode, which simplifies things + if (options.external_path.size() > 0) { + // in this mode, we don't actually have to do much work. + // all we do is to + // - determine how many timeframes are asked + // - check if the right files are present in the external path (someone else needs to create/put them there) + // - check if the given contexts are consistent with options given (orbitsPerTF, ...) + // - copy the files into the MC destination folder (this implies timeframeextraction mode) + // - return + + if (options.orbits < 0) { + LOG(error) << "External mode; orbits need to be given"; + return 1; + } + + if (options.orbitsPerTF == 0) { + LOG(error) << "External mode; need to have orbitsPerTF"; + return 1; + } + + if (options.individualTFextraction.size() == 0) { + LOG(error) << "External mode: This requires --extract-per-timeframe"; + return 1; + } + + // calculate number of timeframes + auto num_timeframes = options.orbits / options.orbitsPerTF; + LOG(info) << "External mode for " << num_timeframes << " consecutive timeframes; starting from " << options.tfid; + + // loop over all timeframe ids - check if file is present - (check consistency) - copy to final destination + for (int i = 0; i < num_timeframes; ++i) { + auto this_tf_id = options.tfid + i; + if (!copy_collision_context(options.external_path, this_tf_id, i + 1)) { + return 1; + } + } + return 0; + } + // init random generator gRandom->SetSeed(options.seed); From 26a12a65dd85f6aa405c6dea065093693889cb1b Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 10 Oct 2025 16:31:33 +0200 Subject: [PATCH 86/99] ITS3: remove unused files Signed-off-by: Felix Schlepper --- .../include/ITS3Reconstruction/FastMultEst.h | 66 ------ .../ITS3Reconstruction/FastMultEstConfig.h | 57 ----- .../ITS3/reconstruction/src/FastMultEst.cxx | 207 ------------------ .../reconstruction/src/FastMultEstConfig.cxx | 22 -- .../ITS3/workflow/src/TrackerSpec.cxx | 7 - 5 files changed, 359 deletions(-) delete mode 100644 Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEst.h delete mode 100644 Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEstConfig.h delete mode 100644 Detectors/Upgrades/ITS3/reconstruction/src/FastMultEst.cxx delete mode 100644 Detectors/Upgrades/ITS3/reconstruction/src/FastMultEstConfig.cxx diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEst.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEst.h deleted file mode 100644 index e9da619c0efbf..0000000000000 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEst.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ITS3_FASTMULTEST_ -#define ALICEO2_ITS3_FASTMULTEST_ - -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITS3/CompCluster.h" -#include -#include "ITS3Reconstruction/FastMultEstConfig.h" -#include -#include - -namespace o2 -{ -namespace its3 -{ - -struct FastMultEst { - - static constexpr int NLayers = o2::itsmft::ChipMappingITS::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 - uint32_t lastRandomSeed = 0; /// state of the gRandom before - - std::array nClPerLayer{0}; // measured N Cl per layer selectROFs - FastMultEst(); - - static uint32_t getCurrentRandomSeed(); - int selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel); - - 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); - } - static bool sSeedSet; - - ClassDefNV(FastMultEst, 1); -}; - -} // namespace its3 -} // namespace o2 - -#endif diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEstConfig.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEstConfig.h deleted file mode 100644 index 1857176b19f1f..0000000000000 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/FastMultEstConfig.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FastMultEstConfig.h -/// \brief Configuration parameters for ITS fast multiplicity estimator -/// \author ruben.shahoyan@cern.ch - -#ifndef ALICEO2_ITS_FASTMULTESTCONF_H_ -#define ALICEO2_ITS_FASTMULTESTCONF_H_ - -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" - -namespace o2 -{ -namespace its -{ -struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { - static constexpr int NLayers = 7; // FIXME - - /// 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) - float cutRandomFraction = -1.; /// apply random cut rejecting requested fraction - int randomSeed = 0; /// 0 - do not seet seed, >0 : set as is, <0 : use current time - bool preferTriggered = true; /// prefer ROFs with highest number of physics triggers - - bool isMultCutRequested() const { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; }; - bool isVtxMultCutRequested() const { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; }; - bool isPassingRandomRejection() const; - bool isPassingMultCut(float mult) const { return mult >= cutMultClusLow && (mult <= cutMultClusHigh || cutMultClusHigh <= 0.f); } - bool isPassingVtxMultCut(int mult) const { return mult >= cutMultVtxLow && (mult <= cutMultVtxHigh || cutMultVtxHigh <= 0.f); } - - O2ParamDef(FastMultEstConfig, "fastMultConfig"); -}; - -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEst.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEst.cxx deleted file mode 100644 index fa2ce319328b5..0000000000000 --- a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEst.cxx +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITS3Reconstruction/FastMultEst.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "Framework/Logger.h" -#include -#include -#include - -using namespace o2::its; - -bool FastMultEst::sSeedSet = false; - -///______________________________________________________ -FastMultEst::FastMultEst() -{ - if (!sSeedSet && FastMultEstConfig::Instance().cutRandomFraction > 0.f) { - sSeedSet = true; - if (FastMultEstConfig::Instance().randomSeed > 0) { - gRandom->SetSeed(FastMultEstConfig::Instance().randomSeed); - } else if (FastMultEstConfig::Instance().randomSeed < 0) { - gRandom->SetSeed(std::time(nullptr) % 0xffff); - } - } -} - -///______________________________________________________ -/// find multiplicity for given set of clusters -void FastMultEst::fillNClPerLayer(const gsl::span& clusters) -{ - int lr = FastMultEst::NLayers - 1, nchAcc = o2::itsmft::ChipMappingITS::getNChips() - o2::itsmft::ChipMappingITS::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::itsmft::ChipMappingITS::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::itsmft::ChipMappingITS::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::itsmft::ChipMappingITS::getNChipsPerLr(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 > 0 ? mult : 0; -} - -///______________________________________________________ -/// 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::itsmft::ChipMappingITS::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) { - float noise = ncl[il] - mult * conf.accCorr[il], estNoise = o2::itsmft::ChipMappingITS::getNChipsPerLr(il) * noisePerChip; - float diff = noise - estNoise; - chi2 += diff * diff / estNoise; - } - } - chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; - return mult > 0 ? mult : 0; -} - -int FastMultEst::selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel) -{ - int nrof = rofs.size(), nsel = 0; - const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - sel.clear(); - sel.resize(nrof, true); // by default select all - lastRandomSeed = gRandom->GetSeed(); - if (multEstConf.isMultCutRequested()) { - for (uint32_t irof = 0; irof < nrof; irof++) { - nsel += sel[irof] = multEstConf.isPassingMultCut(process(rofs[irof].getROFData(clus))); - } - } else { - nsel = nrof; - } - using IdNT = std::pair; - if (multEstConf.cutRandomFraction > 0.) { - int ntrig = trig.size(), currTrig = 0; - if (multEstConf.preferTriggered) { - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - std::vector nTrigROF; - nTrigROF.reserve(nrof); - for (uint32_t irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - if (nsel && gRandom->Rndm() < multEstConf.cutRandomFraction) { - nsel--; - } - auto irROF = rofs[irof].getBCData(); - while (currTrig < ntrig && trig[currTrig].ir < irROF) { // triggers are sorted, jump to 1st one not less than current ROF - currTrig++; - } - auto& trof = nTrigROF.emplace_back(irof, 0); - irROF += alpParams.roFrameLengthInBC; - while (currTrig < ntrig && trig[currTrig].ir < irROF) { - trof.second++; - currTrig++; - } - } - } - if (nsel > 0) { - sort(nTrigROF.begin(), nTrigROF.end(), [](const IdNT& a, const IdNT& b) { return a.second > b.second; }); // order in number of triggers - auto last = nTrigROF.begin() + nsel; - sort(nTrigROF.begin(), last, [](const IdNT& a, const IdNT& b) { return a.first < b.first; }); // order in ROF ID first nsel ROFs - } - for (int i = nsel; i < int(nTrigROF.size()); i++) { // reject ROFs in the tail - sel[nTrigROF[i].first] = false; - } - } else { // dummy random rejection - for (int irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - float sr = gRandom->Rndm(); - if (gRandom->Rndm() < multEstConf.cutRandomFraction) { - sel[irof] = false; - nsel--; - } - } - } - } - } - LOGP(debug, "NSel = {} of {} rofs Seeds: before {} after {}", nsel, nrof, lastRandomSeed, gRandom->GetSeed()); - - return nsel; -} diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEstConfig.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEstConfig.cxx deleted file mode 100644 index 4f3a8a44b0391..0000000000000 --- a/Detectors/Upgrades/ITS3/reconstruction/src/FastMultEstConfig.cxx +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITS3Reconstruction/FastMultEstConfig.h" -#include "TRandom.h" - -O2ParamImpl(o2::its::FastMultEstConfig); - -using namespace o2::its; - -bool FastMultEstConfig::isPassingRandomRejection() const -{ - return (cutRandomFraction <= 0. || gRandom->Rndm() > cutRandomFraction) ? true : false; -} diff --git a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx index 7945f8e0af1df..216056153d095 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx @@ -29,18 +29,11 @@ #include "CommonDataFormat/IRFrame.h" #include "DataFormatsTRD/TriggerRecord.h" #include "ITS3Reconstruction/IOUtils.h" -#include "ITSReconstruction/FastMultEstConfig.h" #include "ITS3Base/SpecsV2.h" namespace o2 { using namespace framework; -using its::FastMultEstConfig; -using its::TimeFrame; -using its::Tracker; -using its::TrackingParameters; -using its::TrackITSExt; -using its::Vertexer; namespace its3 { From 7146960b9a9bd3fe869ee9e1e7dabd6c5b8f487f Mon Sep 17 00:00:00 2001 From: Podist Kurashvili Date: Mon, 20 Oct 2025 19:52:19 +0200 Subject: [PATCH 87/99] FD detector (#13476) * basic geometry and data format for new detector * fixed geometry and json * fixed hits * updated geometry and detector source code * updated geometry and detector source code * minor fixes * sensitive volumes * geometry update * geometry update * enable hits * enable hits * enable hits * fix channel ids * More flexible confgurable parameters * More flexible confgurable parameters * update parameters and geometry * fixed ring sizes * merge conflict * GPU * fix conflicts * resolve conflicts * head * GPU * added Constants.h to store constants * Adding Constants.h and correcting formatting issues * Adding Constants.h and correcting formatting issues * Adding Constants.h and correcting formatting issues * Adding Constants.h and correcting formatting issues * Adding Constants.h and correcting formatting issues * Adding Constants.h and correcting formatting issues * created new namespace * created new namespace * created new namespace * change name of the detector * change name of the detector * check errors * check errors * Flexible ring radii, a new Boolean switch for module A, module coverage in eta independent of their position * correct default number of ring of A side * Modified default parameters * Hits from charged tracks only * Fixed hit merging * return to the previous choice * updated eta min/max definitions * updated eta definitions * remove spurious files * Update CMake * removed GPU file * Add FD detector * including aluminium containers * including aluminium containers * including aluminium containers * including aluminium containers * including aluminium containers * Please consider the following formatting changes * Updated Readme * introduced FD hit data format * introduced FD hit data format * corrected copyright notice * corrected copyright notice * corrected copyright notice * Finding detId from fMC * colors * change detector name * change detector name * change detector name * change detector name * Fix formatting in CMakeLists.txt for FD3Simulation --------- Co-authored-by: ALICE Action Bot --- Common/SimConfig/src/SimConfig.cxx | 9 +- .../DetectorsCommonDataFormats/DetID.h | 10 +- .../DetectorsCommonDataFormats/SimTraits.h | 3 +- .../Detectors/Upgrades/ALICE3/CMakeLists.txt | 12 + .../Upgrades/ALICE3/FD3/CMakeLists.txt | 23 + .../ALICE3/FD3/include/DataFormatsFD3/Hit.h | 125 ++++++ .../ALICE3/FD3/src/DataFormatsFD3LinkDef.h | 21 + .../Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx | 35 ++ DataFormats/Detectors/Upgrades/CMakeLists.txt | 3 +- .../Headers/include/Headers/DataHeader.h | 1 + Detectors/Upgrades/ALICE3/CMakeLists.txt | 3 +- Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt | 13 + Detectors/Upgrades/ALICE3/FD3/README.md | 10 + .../Upgrades/ALICE3/FD3/base/CMakeLists.txt | 20 + .../FD3/base/include/FD3Base/Constants.h | 38 ++ .../FD3/base/include/FD3Base/FD3BaseParam.h | 41 ++ .../FD3/base/include/FD3Base/GeometryTGeo.h | 54 +++ .../ALICE3/FD3/base/src/FD3BaseLinkDef.h | 23 + .../ALICE3/FD3/base/src/FD3BaseParam.cxx | 14 + .../ALICE3/FD3/base/src/GeometryTGeo.cxx | 66 +++ .../ALICE3/FD3/simulation/CMakeLists.txt | 19 + .../include/FD3Simulation/Detector.h | 150 +++++++ .../ALICE3/FD3/simulation/src/Detector.cxx | 413 ++++++++++++++++++ .../FD3/simulation/src/FD3SimulationLinkDef.h | 21 + macro/build_geometry.C | 6 + run/CMakeLists.txt | 3 +- run/O2HitMerger.h | 5 + run/o2simdefaultdetectorlist.json | 1 + 28 files changed, 1131 insertions(+), 11 deletions(-) create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx create mode 100644 Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/FD3/README.md create mode 100644 Detectors/Upgrades/ALICE3/FD3/base/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h create mode 100644 Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/FD3BaseParam.h create mode 100644 Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/GeometryTGeo.h create mode 100644 Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h create mode 100644 Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseParam.cxx create mode 100644 Detectors/Upgrades/ALICE3/FD3/base/src/GeometryTGeo.cxx create mode 100644 Detectors/Upgrades/ALICE3/FD3/simulation/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/FD3/simulation/include/FD3Simulation/Detector.h create mode 100644 Detectors/Upgrades/ALICE3/FD3/simulation/src/Detector.cxx create mode 100644 Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 9407a3c556179..2c28497fa4237 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -98,7 +98,8 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules[i] != "TF3" && activeModules[i] != "RCH" && activeModules[i] != "MI3" && - activeModules[i] != "ECL") { + activeModules[i] != "ECL" && + activeModules[i] != "FD3") { LOGP(fatal, "List of active modules contains {}, which is not a module from the upgrades.", activeModules[i]); } } @@ -112,7 +113,8 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules[i] == "TF3" || activeModules[i] == "RCH" || activeModules[i] == "MI3" || - activeModules[i] == "ECL") { + activeModules[i] == "ECL" || + activeModules[i] == "FD3") { LOGP(fatal, "List of active modules contains {}, which is not a run 3 module", activeModules[i]); } } @@ -130,6 +132,7 @@ void SimConfig::determineActiveModules(std::vector const& inputargs d == DetID::TF3 || d == DetID::RCH || d == DetID::ECL || + d == DetID::FD3 || d == DetID::MI3) { activeModules.emplace_back(DetID::getName(d)); } @@ -149,7 +152,7 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules.emplace_back("SHIL"); for (int d = DetID::First; d <= DetID::Last; ++d) { #ifdef ENABLE_UPGRADES - if (d != DetID::IT3 && d != DetID::TRK && d != DetID::FT3 && d != DetID::FCT && d != DetID::TF3 && d != DetID::RCH && d != DetID::ECL && d != DetID::MI3) { + if (d != DetID::IT3 && d != DetID::TRK && d != DetID::FT3 && d != DetID::FCT && d != DetID::TF3 && d != DetID::RCH && d != DetID::ECL && d != DetID::FD3 && d != DetID::MI3) { activeModules.emplace_back(DetID::getName(d)); } } diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h index a2767c7620cdd..2d2383783cfc3 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h @@ -87,7 +87,8 @@ class DetID static constexpr ID RCH = 23; static constexpr ID MI3 = 24; static constexpr ID ECL = 25; - static constexpr ID Last = ECL; + static constexpr ID FD3 = 26; + static constexpr ID Last = FD3; #else static constexpr ID Last = FOC; ///< if extra detectors added, update this !!! #endif @@ -181,7 +182,7 @@ class DetID // detector names, will be defined in DataSources 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", "TST", "CTP", "FOC", "IT3", "TRK", "FT3", "FCT", "TF3", "RCH", "MI3", "ECL", nullptr}; + {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", "IT3", "TRK", "FT3", "FCT", "TF3", "RCH", "MI3", "ECL", "FD3", nullptr}; #else {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", nullptr}; #endif @@ -195,7 +196,7 @@ class DetID #ifdef ENABLE_UPGRADES , o2h::gDataOriginIT3, o2h::gDataOriginTRK, o2h::gDataOriginFT3, o2h::gDataOriginFCT, o2h::gDataOriginTF3, - o2h::gDataOriginRCH, o2h::gDataOriginMI3, o2h::gDataOriginECL + o2h::gDataOriginRCH, o2h::gDataOriginMI3, o2h::gDataOriginECL, o2h::gDataOriginFD3 #endif }; #endif // GPUCA_GPUCODE_DEVICE @@ -211,10 +212,11 @@ GPUconstexpr() DetID::mask_t sMasks[DetID::nDetectors] = ///< detectot masks DetID::mask_t(math_utils::bit2Mask(DetID::CPV)), DetID::mask_t(math_utils::bit2Mask(DetID::EMC)), DetID::mask_t(math_utils::bit2Mask(DetID::HMP)), DetID::mask_t(math_utils::bit2Mask(DetID::MFT)), DetID::mask_t(math_utils::bit2Mask(DetID::MCH)), DetID::mask_t(math_utils::bit2Mask(DetID::MID)), DetID::mask_t(math_utils::bit2Mask(DetID::ZDC)), DetID::mask_t(math_utils::bit2Mask(DetID::FT0)), DetID::mask_t(math_utils::bit2Mask(DetID::FV0)), DetID::mask_t(math_utils::bit2Mask(DetID::FDD)), DetID::mask_t(math_utils::bit2Mask(DetID::TST)), DetID::mask_t(math_utils::bit2Mask(DetID::CTP)), DetID::mask_t(math_utils::bit2Mask(DetID::FOC)) + #ifdef ENABLE_UPGRADES , DetID::mask_t(math_utils::bit2Mask(DetID::IT3)), DetID::mask_t(math_utils::bit2Mask(DetID::TRK)), DetID::mask_t(math_utils::bit2Mask(DetID::FT3)), DetID::mask_t(math_utils::bit2Mask(DetID::FCT)), DetID::mask_t(math_utils::bit2Mask(DetID::TF3)), - DetID::mask_t(math_utils::bit2Mask(DetID::RCH)), DetID::mask_t(math_utils::bit2Mask(DetID::MI3)), DetID::mask_t(math_utils::bit2Mask(DetID::ECL)) + DetID::mask_t(math_utils::bit2Mask(DetID::RCH)), DetID::mask_t(math_utils::bit2Mask(DetID::MI3)), DetID::mask_t(math_utils::bit2Mask(DetID::ECL)), DetID::mask_t(math_utils::bit2Mask(DetID::FD3)) #endif }; } // namespace detid_internal diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h index 8f9cbcfbdba43..37c4b790d181b 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h @@ -99,7 +99,8 @@ class SimTraits /*TF3*/ VS{ "TF3Hit" }, /*RCH*/ VS{ "RCHHit" }, /*MI3*/ VS{ "MI3Hit" }, - /*ECL*/ VS{ "ECLHit" } + /*ECL*/ VS{ "ECLHit" }, + /*FD */ VS{ "FDHit" } #endif }; // clang-format on diff --git a/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt new file mode 100644 index 0000000000000..b3944c2e502d8 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright 2019-2025 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. + +add_subdirectory(FD3) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt new file mode 100644 index 0000000000000..e2219bb893612 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt @@ -0,0 +1,23 @@ +# 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. + +o2_add_library(DataFormatsFD3 + SOURCES src/Hit.cxx + PUBLIC_LINK_LIBRARIES O2::FD3Base + O2::SimulationDataFormat + O2::CommonDataFormat + Microsoft.GSL::GSL + O2::DetectorsCommonDataFormats +) + +o2_target_root_dictionary(DataFormatsFD3 + HEADERS include/DataFormatsFD3/Hit.h +) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h b/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h new file mode 100644 index 0000000000000..4fde2f6cde6b4 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h @@ -0,0 +1,125 @@ +// 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 Hit.h +/// \brief Definition of the FD3 Hit class (based on ITSMFT and FV0) + +#ifndef ALICEO2_FVD_HIT_H_ +#define ALICEO2_FVD_HIT_H_ + +#include +#include "SimulationDataFormat/BaseHits.h" // for BasicXYZEHit +#include "Rtypes.h" // for Bool_t, Double_t, int, Double32_t, etc +#include "TVector3.h" // for TVector3 +#include "CommonUtils/ShmAllocator.h" + +namespace o2 +{ +namespace fd3 +{ + +class Hit : public o2::BasicXYZEHit +{ + public: + /// Default constructor + Hit() = default; + + /// Class Constructor + /// \param trackID Index of MCTrack + /// \param cellID Cell ID + /// \param startPos Coordinates at entrance to active volume [cm] + /// \param endPos Coordinates to active volume [cm] + /// \param startMom Momentum of track at entrance [GeV] + /// \param startE Energy of track at entrance [GeV] + /// \param endTime Final time [ns] + /// \param eLoss Energy deposit [GeV] + /// \param particlePdg PDG code of the partcile associated with the track + inline Hit(int trackID, + int cellID, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, + double startE, + double endTime, + double eLoss, + int particlePdg); + + // Entrance position getters + math_utils::Point3D const& GetPosStart() const { return mPositionStart; } + float GetStartX() const { return mPositionStart.X(); } + float GetStartY() const { return mPositionStart.Y(); } + float GetStartZ() const { return mPositionStart.Z(); } + template + void GetStartPosition(F& x, F& y, F& z) const + { + x = GetStartX(); + y = GetStartY(); + z = GetStartZ(); + } + + // Momentum getters + math_utils::Vector3D const& GetMomentum() const { return mMomentumStart; } + math_utils::Vector3D& GetMomentum() { return mMomentumStart; } + float GetPx() const { return mMomentumStart.X(); } + float GetPy() const { return mMomentumStart.Y(); } + float GetPz() const { return mMomentumStart.Z(); } + float GetE() const { return mEnergyStart; } + float GetTotalEnergyAtEntrance() const { return GetE(); } + int GetParticlePdg() const { return mParticlePdg; } + + void Print(const Option_t* opt) const; + + private: + math_utils::Vector3D mMomentumStart; ///< momentum at entrance + math_utils::Point3D mPositionStart; ///< position at entrance (base mPos give position on exit) + float mEnergyStart; ///< total energy at entrance + int mParticlePdg; ///< PDG code of the particle associated with this track + + ClassDefNV(Hit, 1); +}; + +Hit::Hit(int trackID, + int detID, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, + double startE, + double endTime, + double eLoss, + int particlePdg) + : BasicXYZEHit(endPos.X(), + endPos.Y(), + endPos.Z(), + endTime, + eLoss, + trackID, + detID), + mMomentumStart(startMom.X(), startMom.Y(), startMom.Z()), + mPositionStart(startPos.X(), startPos.Y(), startPos.Z()), + mEnergyStart(startE), + mParticlePdg(particlePdg) +{ +} + +} // namespace fd3 +} // namespace o2 + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; + +} // namespace std +#endif /* USESHM */ +#endif /* ALICEO2_FD3_HIT_H_ */ diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h new file mode 100644 index 0000000000000..1014b3d8c704e --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h @@ -0,0 +1,21 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fd3::Hit + ; +#pragma link C++ class vector < o2::fd3::Hit> + ; + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx new file mode 100644 index 0000000000000..403a3402bd30c --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Hit.cxx +/// \brief Implementation of the Hit class + +#include "DataFormatsFD3/Hit.h" +#include + +ClassImp(o2::fd3::Hit); + +namespace o2 +{ +namespace fd3 +{ + +void Hit::Print(const Option_t* opt) const +{ + printf( + "Det: %5d Track: %6d E.loss: %.3e P: %+.3e %+.3e %+.3e\n" + "PosIn: %+.3e %+.3e %+.3e PosOut: %+.3e %+.3e %+.3e\n", + GetDetectorID(), GetTrackID(), GetEnergyLoss(), GetPx(), GetPy(), GetPz(), + GetStartX(), GetStartY(), GetStartZ(), GetX(), GetY(), GetZ()); +} + +} // namespace fd3 +} // namespace o2 diff --git a/DataFormats/Detectors/Upgrades/CMakeLists.txt b/DataFormats/Detectors/Upgrades/CMakeLists.txt index a2d470b8ff6d5..0dfe07dc2827d 100644 --- a/DataFormats/Detectors/Upgrades/CMakeLists.txt +++ b/DataFormats/Detectors/Upgrades/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# Copyright 2019-2025 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. # @@ -10,3 +10,4 @@ # or submit itself to any jurisdiction. message(STATUS "Building dataformats for upgrades") +add_subdirectory(ALICE3) diff --git a/DataFormats/Headers/include/Headers/DataHeader.h b/DataFormats/Headers/include/Headers/DataHeader.h index 2dbfbd67d8d6c..b44f41c5d3cb3 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -588,6 +588,7 @@ constexpr o2::header::DataOrigin gDataOriginTF3{"TF3"}; constexpr o2::header::DataOrigin gDataOriginRCH{"RCH"}; constexpr o2::header::DataOrigin gDataOriginMI3{"MI3"}; constexpr o2::header::DataOrigin gDataOriginECL{"ECL"}; // upgrades +constexpr o2::header::DataOrigin gDataOriginFD3{"FD3"}; // upgrades constexpr o2::header::DataOrigin gDataOriginGPU{"GPU"}; diff --git a/Detectors/Upgrades/ALICE3/CMakeLists.txt b/Detectors/Upgrades/ALICE3/CMakeLists.txt index 0c2bbe5e02a47..0335e85007c01 100644 --- a/Detectors/Upgrades/ALICE3/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -12,10 +12,11 @@ add_subdirectory(Passive) add_subdirectory(TRK) add_subdirectory(ECal) +add_subdirectory(FD3) add_subdirectory(FT3) add_subdirectory(FCT) add_subdirectory(AOD) add_subdirectory(IOTOF) add_subdirectory(RICH) add_subdirectory(MID) -add_subdirectory(macros) \ No newline at end of file +add_subdirectory(macros) diff --git a/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt b/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt new file mode 100644 index 0000000000000..d9ea4b632952c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright 2019-2025 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. + +add_subdirectory(base) +add_subdirectory(simulation) diff --git a/Detectors/Upgrades/ALICE3/FD3/README.md b/Detectors/Upgrades/ALICE3/FD3/README.md new file mode 100644 index 0000000000000..54c1fd37b2590 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/README.md @@ -0,0 +1,10 @@ + + +# ALICE 3 FORWARD DETECTOR + +This is top page for the FD3 detector documentation. + + diff --git a/Detectors/Upgrades/ALICE3/FD3/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/FD3/base/CMakeLists.txt new file mode 100644 index 0000000000000..c76665da1344f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright 2019-2025 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. + +o2_add_library(FD3Base + SOURCES src/GeometryTGeo.cxx + src/FD3BaseParam.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsBase) + +o2_target_root_dictionary(FD3Base + HEADERS include/FD3Base/GeometryTGeo.h + include/FD3Base/Constants.h + include/FD3Base/FD3BaseParam.h) diff --git a/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h new file mode 100644 index 0000000000000..428a7a1f6d179 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/Constants.h @@ -0,0 +1,38 @@ +// 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 Constants.h +/// \brief General constants in FV0 +/// +/// \author Maciej Slupecki, University of Jyvaskyla, Finland + +#ifndef ALICEO2_FD3_CONSTANTS_ +#define ALICEO2_FD3_CONSTANTS_ + +namespace o2 +{ +namespace fd3 +{ +struct Constants { + static constexpr unsigned int nsect = 8; + static constexpr unsigned int nringsA = 5; + static constexpr unsigned int nringsC = 6; + + static constexpr float etaMax = 7.0f; + static constexpr float etaMin = 4.0f; + + static constexpr unsigned int nringsA_withMG = 3; + static constexpr float etaMinA_withMG = 5.0f; +}; + +} // namespace fd3 +} // namespace o2 +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/FD3BaseParam.h b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/FD3BaseParam.h new file mode 100644 index 0000000000000..9836cebbfa760 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/FD3BaseParam.h @@ -0,0 +1,41 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FD3_FD3BASEPARAM_ +#define ALICEO2_FD3_FD3BASEPARAM_ + +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/Constants.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace fd3 +{ +struct FD3BaseParam : public o2::conf::ConfigurableParamHelper { + + float zmodA = 1700.0f; + float zmodC = -1850.0f; + float dzscint = 4.0f; + + bool withMG = false; // modified geometry with 3 rings on A side + + bool plateBehindA = false; + bool fullContainer = false; + float dzplate = 1.0f; // Aluminium plate width + + O2ParamDef(FD3BaseParam, "FD3Base"); +}; + +} // namespace fd3 +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/GeometryTGeo.h new file mode 100644 index 0000000000000..0e38bd4ccd21f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/include/FD3Base/GeometryTGeo.h @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FD3_GEOMETRYTGEO_H_ +#define ALICEO2_FD3_GEOMETRYTGEO_H_ + +#include + +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace fd3 +{ + +/// FD3 Geometry type +class GeometryTGeo : public o2::detectors::DetMatrixCache +{ + public: + GeometryTGeo(bool build = false, int loadTrans = 0); + + void Build(int loadTrans); + void fillMatrixCache(int mask); + virtual ~GeometryTGeo(); + + static GeometryTGeo* Instance(); + + void getGlobalPosition(float& x, float& y, float& z); + + static constexpr o2::detectors::DetID::ID getDetID() { return o2::detectors::DetID::FD3; } + + private: + static std::unique_ptr sInstance; + + ClassDefNV(GeometryTGeo, 1); +}; +} // namespace fd3 +} // namespace o2 +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h new file mode 100644 index 0000000000000..8475ef2c77313 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseLinkDef.h @@ -0,0 +1,23 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fd3::Constants + ; +#pragma link C++ class o2::fd3::GeometryTGeo + ; +#pragma link C++ class o2::fd3::FD3BaseParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::fd3::FD3BaseParam> + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseParam.cxx b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseParam.cxx new file mode 100644 index 0000000000000..74b45962b3f39 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/src/FD3BaseParam.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FD3Base/FD3BaseParam.h" + +O2ParamImpl(o2::fd3::FD3BaseParam); diff --git a/Detectors/Upgrades/ALICE3/FD3/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/FD3/base/src/GeometryTGeo.cxx new file mode 100644 index 0000000000000..16788cb8944e3 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/base/src/GeometryTGeo.cxx @@ -0,0 +1,66 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/FD3BaseParam.h" + +#include + +#include + +using namespace o2::fd3; +namespace o2 +{ +namespace fd3 +{ + +std::unique_ptr GeometryTGeo::sInstance; + +GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : DetMatrixCache() +{ + if (sInstance) { + LOGP(fatal, "Invalid use of public constructor: o2::fd3::GeometryTGeo instance exists"); + } + if (build) { + Build(loadTrans); + } +} + +GeometryTGeo::~GeometryTGeo() = default; + +GeometryTGeo* GeometryTGeo::Instance() +{ + if (!sInstance) { + sInstance = std::unique_ptr(new GeometryTGeo(true, 0)); + } + return sInstance.get(); +} + +void GeometryTGeo::Build(int loadTrans) +{ + if (isBuilt()) { + LOGP(warning, "Already built"); + return; // already initialized + } + + if (!gGeoManager) { + LOGP(fatal, "Geometry is not loaded"); + } + + fillMatrixCache(loadTrans); +} + +void GeometryTGeo::fillMatrixCache(int mask) +{ +} + +} // namespace fd3 +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/FD3/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/FD3/simulation/CMakeLists.txt new file mode 100644 index 0000000000000..38886ec5fbe07 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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. + +o2_add_library(FD3Simulation + SOURCES src/Detector.cxx + PUBLIC_LINK_LIBRARIES O2::FD3Base + O2::DataFormatsFD3 + ROOT::Physics) + +o2_target_root_dictionary(FD3Simulation + HEADERS include/FD3Simulation/Detector.h) diff --git a/Detectors/Upgrades/ALICE3/FD3/simulation/include/FD3Simulation/Detector.h b/Detectors/Upgrades/ALICE3/FD3/simulation/include/FD3Simulation/Detector.h new file mode 100644 index 0000000000000..2d17acbd4a0e8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/include/FD3Simulation/Detector.h @@ -0,0 +1,150 @@ +// 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 Detector.h +/// \brief Definition of the Detector class + +#ifndef ALICEO2_FD3_DETECTOR_H_ +#define ALICEO2_FD3_DETECTOR_H_ + +#include "SimulationDataFormat/BaseHits.h" +#include "DetectorsBase/Detector.h" +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/FD3BaseParam.h" +#include "DataFormatsFD3/Hit.h" +#include "Rtypes.h" +#include "TGeoManager.h" +#include "TLorentzVector.h" +#include "TVector3.h" +#include + +class FairVolume; +class TGeoVolume; + +namespace o2 +{ +namespace fd3 +{ +class GeometryTGeo; +} +} // namespace o2 + +namespace o2 +{ +namespace fd3 +{ + +class Detector : public o2::base::DetImpl +{ + public: + Detector(bool Active); + + Detector() = default; + + ~Detector() override; + + void ConstructGeometry() override; + + /// This method is an example of how to add your own point of type Hit to the clones array + o2::fd3::Hit* addHit(int trackId, unsigned int detId, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, double startE, + double endTime, double eLoss, int particlePdg); + // unsigned int startStatus, + // unsigned int endStatus); + + std::vector* getHits(Int_t iColl) + { + if (iColl == 0) { + return mHits; + } + return nullptr; + } + + // Mandatory overrides + void BeginPrimary() override { ; } + void FinishPrimary() override { ; } + void InitializeO2Detector() override; + void PostTrack() override { ; } + void PreTrack() override { ; } + bool ProcessHits(FairVolume* v = nullptr) override; + void EndOfEvent() override; + void Register() override; + void Reset() override; + + void createMaterials(); + void buildModules(); + + enum EMedia { + Scintillator, + Aluminium + }; + + private: + Detector(const Detector&); + Detector& operator=(const Detector&); + + std::vector* mHits = nullptr; + GeometryTGeo* mGeometryTGeo = nullptr; + + TGeoVolumeAssembly* buildModuleA(); + TGeoVolumeAssembly* buildModuleC(); + + float ringSize(float zmod, float eta); + + unsigned int mNumberOfRingsA, mNumberOfRingsC, mNumberOfSectors; + float mDzScint, mDzPlate; + + std::vector mRingSizesA = {}, mRingSizesC = {}; + + float mEtaMaxA, mEtaMaxC, mEtaMinA, mEtaMinC; + float mZA, mZC; + + bool mPlateBehindA, mFullContainer; + + void defineSensitiveVolumes(); + void definePassiveVolumes(); + + /// Transient data about track passing the sensor, needed by ProcessHits() + 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; //! + + 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 fd3 +} // 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/ALICE3/FD3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FD3/simulation/src/Detector.cxx new file mode 100644 index 0000000000000..bd79b1deaad80 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/src/Detector.cxx @@ -0,0 +1,413 @@ +// 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 Detector.cxx +/// \brief Implementation of the Detector class + +#include "DataFormatsFD3/Hit.h" +#include "FD3Simulation/Detector.h" +#include "FD3Base/GeometryTGeo.h" +#include "FD3Base/FD3BaseParam.h" +#include "FD3Base/Constants.h" + +#include "DetectorsBase/Stack.h" +#include "SimulationDataFormat/TrackReference.h" +#include "Field/MagneticField.h" + +// FairRoot includes +#include "FairDetector.h" +#include +#include "FairRootManager.h" +#include "FairRun.h" +#include "FairRuntimeDb.h" +#include "FairVolume.h" +#include "FairRootManager.h" + +#include "TVirtualMC.h" +#include "TLorentzVector.h" +#include "TVector3.h" +#include +#include +#include +#include +#include +#include +#include "TRandom.h" +#include + +class FairModule; + +class TGeoMedium; + +using namespace o2::fd3; +using o2::fd3::Hit; + +Detector::Detector(bool active) + : o2::base::DetImpl("FD3", true), + mHits(o2::utils::createSimVector()), + mGeometryTGeo(nullptr), + mTrackData() +{ + mNumberOfRingsC = Constants::nringsC; + mNumberOfSectors = Constants::nsect; + + mEtaMinA = Constants::etaMin; + mEtaMaxA = Constants::etaMax; + mEtaMinC = -Constants::etaMax; + mEtaMaxC = -Constants::etaMin; + + auto& baseParam = FD3BaseParam::Instance(); + + if (baseParam.withMG) { + mNumberOfRingsA = Constants::nringsA_withMG; + mEtaMinA = Constants::etaMinA_withMG; + } else { + mNumberOfRingsA = Constants::nringsA; + mEtaMinA = Constants::etaMin; + } + + mDzScint = baseParam.dzscint / 2; + mDzPlate = baseParam.dzplate; + + mPlateBehindA = baseParam.plateBehindA; + mFullContainer = baseParam.fullContainer; + + mZA = baseParam.zmodA; + mZC = baseParam.zmodC; + + for (int i = 0; i <= mNumberOfRingsA + 1; i++) { + float eta = mEtaMaxA - i * (mEtaMaxA - mEtaMinA) / mNumberOfRingsA; + float r = ringSize(mZA, eta); + mRingSizesA.emplace_back(r); + } + + for (int i = 0; i <= mNumberOfRingsC + 1; i++) { + float eta = mEtaMinC + i * (mEtaMaxC - mEtaMinC) / mNumberOfRingsC; + float r = ringSize(mZC, eta); + mRingSizesC.emplace_back(r); + } +} + +Detector::Detector(const Detector& rhs) + : o2::base::DetImpl(rhs), + mTrackData(), + mHits(o2::utils::createSimVector()) +{ +} + +Detector& Detector::operator=(const Detector& rhs) +{ + + if (this == &rhs) { + return *this; + } + // base class assignment + base::Detector::operator=(rhs); + mTrackData = rhs.mTrackData; + + mHits = nullptr; + return *this; +} + +Detector::~Detector() +{ + + if (mHits) { + o2::utils::freeSimVector(mHits); + } +} + +void Detector::InitializeO2Detector() +{ + LOG(info) << "Initialize Forward Detector"; + mGeometryTGeo = GeometryTGeo::Instance(); + defineSensitiveVolumes(); +} + +bool Detector::ProcessHits(FairVolume* vol) +{ + // This method is called from the MC stepping + if (!(fMC->TrackCharge())) { + return kFALSE; + } + + int detId; + int volID = fMC->CurrentVolID(detId); + + auto stack = (o2::data::Stack*)fMC->GetStack(); + + // Check track status to define when hit is started and when it is stopped + int particlePdg = fMC->TrackPid(); + bool startHit = false, stopHit = false; + if ((fMC->IsTrackEntering()) || (fMC->IsTrackInside() && !mTrackData.mHitStarted)) { + startHit = true; + } else if ((fMC->IsTrackExiting() || fMC->IsTrackOut() || fMC->IsTrackStop())) { + 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.mHitStarted = true; + mTrackData.mEnergyLoss = 0.; + fMC->TrackMomentum(mTrackData.mMomentumStart); + fMC->TrackPosition(mTrackData.mPositionStart); + mTrackData.mTrkStatusStart = true; + } + + if (stopHit) { + TLorentzVector positionStop; + fMC->TrackPosition(positionStop); + int trackId = stack->GetCurrentTrackNumber(); + + math_utils::Point3D posStart(mTrackData.mPositionStart.X(), mTrackData.mPositionStart.Y(), mTrackData.mPositionStart.Z()); + math_utils::Point3D posStop(positionStop.X(), positionStop.Y(), positionStop.Z()); + math_utils::Vector3D momStart(mTrackData.mMomentumStart.Px(), mTrackData.mMomentumStart.Py(), mTrackData.mMomentumStart.Pz()); + + Hit* p = addHit(trackId, detId, posStart, posStop, + momStart, mTrackData.mMomentumStart.E(), + positionStop.T(), mTrackData.mEnergyLoss, particlePdg); + stack->addHit(GetDetId()); + } else { + return false; // do nothing more + } + return true; +} + +o2::fd3::Hit* Detector::addHit(int trackId, unsigned int detId, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, + double startE, + double endTime, + double eLoss, + int particlePdg) +{ + mHits->emplace_back(trackId, detId, startPos, + endPos, startMom, startE, endTime, eLoss, particlePdg); + return &(mHits->back()); +} + +void Detector::ConstructGeometry() +{ + createMaterials(); + buildModules(); +} + +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::createMaterials() +{ + float density, as[11], zs[11], ws[11]; + double radLength, absLength, a_ad, z_ad; + int id; + + // EJ-204 scintillator, based on polyvinyltoluene + const int nScint = 2; + float aScint[nScint] = {1.00784, 12.0107}; + float zScint[nScint] = {1, 6}; + float wScint[nScint] = {0.07085, 0.92915}; // based on EJ-204 datasheet: n_atoms/cm3 + const float dScint = 1.023; + + // Aluminium + Float_t aAlu = 26.981; + Float_t zAlu = 13; + Float_t dAlu = 2.7; + + int matId = 0; // tmp material id number + const int unsens = 0, sens = 1; // sensitive or unsensitive medium + // + int fieldType = 3; // Field type + float maxField = 5.0; // Field max. + + float tmaxfd3 = -10.0; // max deflection angle due to magnetic field in one step + float stemax = 0.1; // max step allowed [cm] + float deemax = 1.0; // maximum fractional energy loss in one step 0GetVolume("cave"); + + if (!vCave) { + LOG(fatal) << "Could not find the top volume!"; + } + + TGeoVolumeAssembly* vFD3A = buildModuleA(); + TGeoVolumeAssembly* vFD3C = buildModuleC(); + + vCave->AddNode(vFD3A, 1, new TGeoTranslation(0., 0., mZA)); + vCave->AddNode(vFD3C, 2, new TGeoTranslation(0., 0., mZC)); +} + +TGeoVolumeAssembly* Detector::buildModuleA() +{ + TGeoVolumeAssembly* mod = new TGeoVolumeAssembly("FD3A"); + + const TGeoMedium* medium = gGeoManager->GetMedium("FD3_Scintillator"); + + float dphiDeg = 360. / mNumberOfSectors; + + for (int ir = 0; ir < mNumberOfRingsA; ir++) { + std::string rName = "fd3_ring" + std::to_string(ir + 1); + TGeoVolumeAssembly* ring = new TGeoVolumeAssembly(rName.c_str()); + float rmin = mRingSizesA[ir]; + float rmax = mRingSizesA[ir + 1]; + LOG(info) << "ring" << ir << ": from " << rmin << " to " << rmax; + for (int ic = 0; ic < mNumberOfSectors; ic++) { + int cellId = ic + mNumberOfSectors * ir; + std::string nodeName = "fd3_node" + std::to_string(cellId); + float phimin = dphiDeg * ic; + float phimax = dphiDeg * (ic + 1); + auto tbs = new TGeoTubeSeg("tbs", rmin, rmax, mDzScint, phimin, phimax); + auto nod = new TGeoVolume(nodeName.c_str(), tbs, medium); + if ((ir + ic) % 2 == 0) { + nod->SetLineColor(kRed); + } else { + nod->SetLineColor(kRed - 7); + } + ring->AddNode(nod, cellId); + } + mod->AddNode(ring, ir + 1); + } + + // Aluminium plates on one or both sides of the A side module + if (mPlateBehindA || mFullContainer) { + LOG(info) << "adding container on A side"; + auto pmed = (TGeoMedium*)gGeoManager->GetMedium("FD3_Aluminium"); + auto pvol = new TGeoTube("pvol_fd3a", mRingSizesA[0], mRingSizesA[mNumberOfRingsA], mDzPlate); + auto pnod1 = new TGeoVolume("pnod1_FD3A", pvol, pmed); + double dpz = 2. + mDzPlate / 2; + mod->AddNode(pnod1, 1, new TGeoTranslation(0, 0, dpz)); + + if (mFullContainer) { + auto pnod2 = new TGeoVolume("pnod2_FD3A", pvol, pmed); + mod->AddNode(pnod2, 1, new TGeoTranslation(0, 0, -dpz)); + } + } + return mod; +} + +TGeoVolumeAssembly* Detector::buildModuleC() +{ + TGeoVolumeAssembly* mod = new TGeoVolumeAssembly("FD3C"); + + const TGeoMedium* medium = gGeoManager->GetMedium("FD3_Scintillator"); + + float dphiDeg = 360. / mNumberOfSectors; + + for (int ir = 0; ir < mNumberOfRingsC; ir++) { + std::string rName = "fd3_ring" + std::to_string(ir + 1 + mNumberOfRingsA); + TGeoVolumeAssembly* ring = new TGeoVolumeAssembly(rName.c_str()); + float rmin = mRingSizesC[ir]; + float rmax = mRingSizesC[ir + 1]; + LOG(info) << "ring" << ir + mNumberOfRingsA << ": from " << rmin << " to " << rmax; + for (int ic = 0; ic < mNumberOfSectors; ic++) { + int cellId = ic + mNumberOfSectors * (ir + mNumberOfRingsA); + std::string nodeName = "fd3_node" + std::to_string(cellId); + float phimin = dphiDeg * ic; + float phimax = dphiDeg * (ic + 1); + auto tbs = new TGeoTubeSeg("tbs", rmin, rmax, mDzScint, phimin, phimax); + auto nod = new TGeoVolume(nodeName.c_str(), tbs, medium); + if ((ir + ic) % 2 == 0) { + nod->SetLineColor(kBlue); + } else { + nod->SetLineColor(kBlue - 7); + } + ring->AddNode(nod, cellId); + } + mod->AddNode(ring, ir + 1); + } + + // Aluminium plates on both sides of the C side module + if (mFullContainer) { + LOG(info) << "adding container on C side"; + auto pmed = (TGeoMedium*)gGeoManager->GetMedium("FD3_Aluminium"); + auto pvol = new TGeoTube("pvol_fd3c", mRingSizesC[0], mRingSizesC[mNumberOfRingsC], mDzPlate); + auto pnod1 = new TGeoVolume("pnod1_FD3C", pvol, pmed); + auto pnod2 = new TGeoVolume("pnod2_FD3C", pvol, pmed); + double dpz = mDzScint / 2 + mDzPlate / 2; + + mod->AddNode(pnod1, 1, new TGeoTranslation(0, 0, dpz)); + mod->AddNode(pnod2, 2, new TGeoTranslation(0, 0, -dpz)); + } + + return mod; +} + +void Detector::defineSensitiveVolumes() +{ + LOG(info) << "Adding FD3 Sentitive Volumes"; + + TGeoVolume* v; + TString volumeName; + + int nCellsA = mNumberOfRingsA * mNumberOfSectors; + int nCellsC = mNumberOfRingsC * mNumberOfSectors; + + LOG(info) << "number of A rings = " << mNumberOfRingsA << " number of cells = " << nCellsA; + LOG(info) << "number of C rings = " << mNumberOfRingsC << " number of cells = " << nCellsC; + + for (int iv = 0; iv < nCellsA + nCellsC; iv++) { + volumeName = "fd3_node" + std::to_string(iv); + v = gGeoManager->GetVolume(volumeName); + LOG(info) << "Adding sensitive volume => " << v->GetName(); + AddSensitiveVolume(v); + } +} + +float Detector::ringSize(float z, float eta) +{ + return z * TMath::Tan(2 * TMath::ATan(TMath::Exp(-eta))); +} + +ClassImp(o2::fd3::Detector); diff --git a/Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h b/Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h new file mode 100644 index 0000000000000..83df03490ebd7 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FD3/simulation/src/FD3SimulationLinkDef.h @@ -0,0 +1,21 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fd3::Detector + ; +#pragma link C++ class o2::base::DetImpl < o2::fd3::Detector> + ; + +#endif diff --git a/macro/build_geometry.C b/macro/build_geometry.C index fde043256046a..6b13f2eac2766 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -264,6 +265,11 @@ void build_geometry(FairRunSim* run = nullptr) addReadoutDetector(new o2::ecal::Detector(isReadout("ECL"))); } + if (isActivated("FD3")) { + // ALICE3 FD3 + addReadoutDetector(new o2::fd3::Detector(isReadout("FD3"))); + } + if (isActivated("MI3")) { // ALICE 3 MID addReadoutDetector(new o2::mi3::Detector(isReadout("MI3"))); diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt index fd43207f92d1e..474aa7e41eb7c 100644 --- a/run/CMakeLists.txt +++ b/run/CMakeLists.txt @@ -46,6 +46,7 @@ target_link_libraries(allsim $<$:O2::IOTOFSimulation> $<$:O2::RICHSimulation> $<$:O2::ECalSimulation> + $<$:O2::FD3Simulation> $<$:O2::MI3Simulation> O2::Generators) @@ -340,4 +341,4 @@ install(FILES o2-sim-client.py PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUT install(DIRECTORY SimExamples/ DESTINATION examples PATTERN * - PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ) \ No newline at end of file + PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ) diff --git a/run/O2HitMerger.h b/run/O2HitMerger.h index 520873e7aaafe..d32f6370ca2db 100644 --- a/run/O2HitMerger.h +++ b/run/O2HitMerger.h @@ -78,6 +78,7 @@ #include #include #include +#include #endif #include @@ -1009,6 +1010,10 @@ void O2HitMerger::initDetInstances() mDetectorInstances[i] = std::move(std::make_unique(true)); counter++; } + if (i == DetID::FD3) { + mDetectorInstances[i] = std::move(std::make_unique(true)); + counter++; + } #endif } if (counter != DetID::nDetectors) { diff --git a/run/o2simdefaultdetectorlist.json b/run/o2simdefaultdetectorlist.json index 697fafcba5872..2a7e977be741d 100644 --- a/run/o2simdefaultdetectorlist.json +++ b/run/o2simdefaultdetectorlist.json @@ -58,6 +58,7 @@ "RCH", "MI3", "ECL", + "FD3", "HALL", "MAG", "A3IP", From f6fd937cbce51006d02c231920643b6a145b11cb Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sun, 5 Oct 2025 07:26:05 +0200 Subject: [PATCH 88/99] Generators: Embedd into correct event when using collision-context This fixes a problem with embedding and when option `--embedIntoFile` is used. Here, we had an index problem in the primary generator class, which was not taking into account the actual event index according the collision context. This could lead to wrong vertices or using wrong MCHeader information for the embedding decision. --- .../include/Generators/PrimaryGenerator.h | 3 +++ Generators/src/PrimaryGenerator.cxx | 25 +++++++++--------- run/O2PrimaryServerDevice.h | 26 +++++++++++++++++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/Generators/include/Generators/PrimaryGenerator.h b/Generators/include/Generators/PrimaryGenerator.h index 097bfabf4ef57..cd4d842761418 100644 --- a/Generators/include/Generators/PrimaryGenerator.h +++ b/Generators/include/Generators/PrimaryGenerator.h @@ -80,6 +80,9 @@ class PrimaryGenerator : public FairPrimaryGenerator /** Public embedding methods **/ Bool_t embedInto(TString fname); + /// sets the embedding index + void setEmbedIndex(int idx) { mEmbedIndex = idx; } + void setExternalVertexForNextEvent(double x, double y, double z); // sets the vertex mode; if mode is kCCDB, a valid MeanVertexObject pointer must be given at the same time diff --git a/Generators/src/PrimaryGenerator.cxx b/Generators/src/PrimaryGenerator.cxx index ee7c6a16330f9..e7b70108a5d4b 100644 --- a/Generators/src/PrimaryGenerator.cxx +++ b/Generators/src/PrimaryGenerator.cxx @@ -81,7 +81,7 @@ Bool_t PrimaryGenerator::GenerateEvent(FairGenericStack* pStack) /** generate event **/ /** normal generation if no embedding **/ - if (!mEmbedTree) { + if (!mEmbedTree || mEmbedIndex < 0) { fixInteractionVertex(); // <-- always fixes vertex outside of FairROOT auto ret = FairPrimaryGenerator::GenerateEvent(pStack); if (ret) { @@ -91,17 +91,18 @@ Bool_t PrimaryGenerator::GenerateEvent(FairGenericStack* pStack) } /** this is for embedding **/ - - /** setup interaction vertex **/ - mEmbedTree->GetEntry(mEmbedIndex); - setInteractionVertex(mEmbedEvent); - - /** notify event generators **/ - auto genList = GetListOfGenerators(); - for (int igen = 0; igen < genList->GetEntries(); ++igen) { - auto o2gen = dynamic_cast(genList->At(igen)); - if (o2gen) { - o2gen->notifyEmbedding(mEmbedEvent); + if (mEmbedIndex >= 0) { + /** setup interaction vertex **/ + mEmbedTree->GetEntry(mEmbedIndex); + setInteractionVertex(mEmbedEvent); + + /** notify event generators **/ + auto genList = GetListOfGenerators(); + for (int igen = 0; igen < genList->GetEntries(); ++igen) { + auto o2gen = dynamic_cast(genList->At(igen)); + if (o2gen) { + o2gen->notifyEmbedding(mEmbedEvent); + } } } diff --git a/run/O2PrimaryServerDevice.h b/run/O2PrimaryServerDevice.h index ece3747b2c94a..4eecf79cffdb6 100644 --- a/run/O2PrimaryServerDevice.h +++ b/run/O2PrimaryServerDevice.h @@ -46,6 +46,7 @@ #include #include #include +#include namespace o2 { @@ -135,6 +136,17 @@ class O2PrimaryServerDevice final : public fair::mq::Device auto embedinto_filename = conf.getEmbedIntoFileName(); if (!embedinto_filename.empty()) { + // determine the sim prefix from the embedding filename + // the filename should be an MCHeader file ... so it should match SOME_PATH/prefix_MCHeader.root + std::regex re(R"((.*/)?([^/]+)_MCHeader\.root$)"); + std::smatch match; + + if (std::regex_search(embedinto_filename, match, re)) { + std::cout << "Extracted embedding prefix : " << match[2] << '\n'; + mEmbeddIntoPrefix = match[2]; + } else { + LOG(fatal) << "Embedding asked but no suitable embedding prefix extractable from " << embedinto_filename; + } mPrimGen->embedInto(embedinto_filename); } @@ -197,6 +209,19 @@ class O2PrimaryServerDevice final : public fair::mq::Device auto& vertex = vertices.at(collisionindex); LOG(info) << "Setting vertex " << vertex << " for event " << mEventCounter << " for prefix " << mSimConfig.getOutPrefix() << " from CollContext"; mPrimGen->setExternalVertexForNextEvent(vertex.X(), vertex.Y(), vertex.Z()); + + // set correct embedding index for PrimaryGenerator ... based on collision context for embedding + auto& collisionParts = mCollissionContext->getEventParts()[collisionindex]; + int background_index = -1; // -1 means no embedding taking place for this signal + + // find the part that corresponds to the event embeded into + for (auto& part : collisionParts) { + if (mCollissionContext->getSimPrefixes()[part.sourceID] == mEmbeddIntoPrefix) { + background_index = part.entryID; + LOG(info) << "Setting embedding index to " << background_index; + } + } + mPrimGen->setEmbedIndex(background_index); } } mPrimGen->GenerateEvent(mStack); @@ -696,6 +721,7 @@ class O2PrimaryServerDevice final : public fair::mq::Device // some information specific to use case when we have a collision context o2::steer::DigitizationContext* mCollissionContext = nullptr; //! std::unordered_map mEventID_to_CollID; //! + std::string mEmbeddIntoPrefix; //! sim prefix of background events TRandom3 mSeedGenerator; //! specific random generator for seed generation for work chunks }; From 2fc63940872fb4d8ce54b8bf6274aec92303a110 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Fri, 17 Oct 2025 15:31:27 +0200 Subject: [PATCH 89/99] GPU: Replace assertions with error counters in ZS decoding. --- GPU/GPUTracking/Global/GPUErrorCodes.h | 6 +- .../TPCClusterFinder/GPUTPCCFDecodeZS.cxx | 156 ++++++++++++------ .../TPCClusterFinder/GPUTPCCFDecodeZS.h | 9 +- GPU/GPUTracking/kernels.cmake | 2 +- 4 files changed, 116 insertions(+), 57 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUErrorCodes.h b/GPU/GPUTracking/Global/GPUErrorCodes.h index 8fec23be00a09..a4921f478b107 100644 --- a/GPU/GPUTracking/Global/GPUErrorCodes.h +++ b/GPU/GPUTracking/Global/GPUErrorCodes.h @@ -47,6 +47,10 @@ GPUCA_ERROR_CODE(26, ERROR_TPCZS_INVALID_ROW, SectorRow) GPUCA_ERROR_CODE(27, ERROR_TPCZS_INVALID_NADC, SectorCRU, SamplesInPage, SamplesWritten) // Invalid number of ADC samples in header, existing samples were decoded GPUCA_ERROR_CODE(28, ERROR_TPCZS_INCOMPLETE_HBF, SectorCRU, PacketCount, NextPacketCount) // Part of HBF is missing, decoding incomplete GPUCA_ERROR_CODE(29, ERROR_TPCZS_INVALID_OFFSET, SectorEndpoint, Value, Expected) // Raw page is skipped since it contains invalid payload offset -GPUCA_ERROR_CODE(29, MAX_GPUCA_ERROR_NUMBER) +GPUCA_ERROR_CODE(30, ERROR_TPCZS_INVALID_MAGIC_WORD, Value) // ZS header contains wrong magic word +GPUCA_ERROR_CODE(31, ERROR_TPCZS_PAGE_OVERFLOW, Position, PageEnd) // Ran out of page to decode +GPUCA_ERROR_CODE(32, ERROR_TPCZS_VERSION_MISMATCH, Value, Expected) // ZS decoder received page with wrong version +GPUCA_ERROR_CODE(33, ERROR_TPCZS_UNKNOWN, ErrorCode) // Unkown or invalid error code raised in decoder +GPUCA_ERROR_CODE(33, MAX_GPUCA_ERROR_NUMBER) // #define GPUCA_CHECK_TPCZS_CORRUPTION diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index f7bb64106fe4f..a548217e26b64 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx @@ -604,61 +604,107 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro const auto* decHeader = Peek(page, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2)); ConsumeHeader(page); - assert(decHeader->version >= ZSVersionDenseLinkBased); - assert(decHeader->magicWord == tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader); - uint16_t nSamplesWritten = 0; const uint16_t nSamplesInPage = decHeader->nADCsamples; const auto* payloadEnd = Peek(pageStart, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2) - ((decHeader->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0)); const auto* nextPage = Peek(pageStart, TPCZSHDR::TPC_ZS_PAGE_SIZE); + const bool extendsToNextPage = decHeader->flags & TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage; + ConsumeBytes(page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader)); - for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) { + int err = GPUErrors::ERROR_NONE; - [[maybe_unused]] ptrdiff_t sizeLeftInPage = payloadEnd - page; - assert(sizeLeftInPage > 0); + if (decHeader->version < ZSVersionDenseLinkBased) { + err = GPUErrors::ERROR_TPCZS_VERSION_MISMATCH; + } - uint16_t nSamplesWrittenTB = 0; + if (decHeader->magicWord != zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader) { + err = GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD; + } + + for (uint16_t i = 0; i < decHeader->nTimebinHeaders && !err; i++) { + + ptrdiff_t sizeLeftInPage = payloadEnd - page; + if (sizeLeftInPage <= 0) { + err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; + break; + } + + int16_t nSamplesWrittenTB = 0; + uint16_t nSamplesLeftInPage = nSamplesInPage - nSamplesWritten; + + if (i == decHeader->nTimebinHeaders - 1 && extendsToNextPage) { + if (raw::RDHUtils::getMemorySize(*rawDataHeader) != TPCZSHDR::TPC_ZS_PAGE_SIZE) { + err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; + break; + } - if (i == decHeader->nTimebinHeaders - 1 && decHeader->flags & o2::tpc::TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage) { - assert(o2::raw::RDHUtils::getMemorySize(*rawDataHeader) == TPCZSHDR::TPC_ZS_PAGE_SIZE); if ((uint16_t)(raw::RDHUtils::getPageCounter(rawDataHeader) + 1) == raw::RDHUtils::getPageCounter(nextPage)) { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); } else { - nSamplesWrittenTB = FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); -#ifdef GPUCA_CHECK_TPCZS_CORRUPTION - if (iThread == 0) { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); - } -#endif + err = GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF; + break; } } else { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + } + + // Abort decoding the page if an error was detected. + if (nSamplesWrittenTB < 0) { + err = -nSamplesWrittenTB; + break; } - assert(nSamplesWritten <= nSamplesInPage); nSamplesWritten += nSamplesWrittenTB; pageDigitOffset += nSamplesWrittenTB; } // for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) -#ifdef GPUCA_CHECK_TPCZS_CORRUPTION - if (iThread == 0 && nSamplesWritten != nSamplesInPage) { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_INVALID_NADC, clusterer.mISector * 1000 + decHeader->cruID, nSamplesInPage, nSamplesWritten); - /*#ifndef GPUCA_GPUCODE - FILE* foo = fopen("dump.bin", "w+b"); - fwrite(pageSrc, 1, o2::raw::RDHUtils::getMemorySize(*rdHdr), foo); - fclose(foo); - #endif*/ + if (nSamplesWritten != nSamplesInPage) { + if (nSamplesWritten < nSamplesInPage) { + pageDigitOffset += FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); + } + err = !err ? GPUErrors::ERROR_TPCZS_INVALID_NADC : err; // Ensure we don't overwrite any previous error } + + if (iThread == 0 && err) { + [[maybe_unused]] bool dumpPage = false; + + if (err == GPUErrors::ERROR_TPCZS_VERSION_MISMATCH) { + clusterer.raiseError(err, decHeader->version, ZSVersionDenseLinkBased); + } else if (err == GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD) { + clusterer.raiseError(err, decHeader->magicWord); + } else if (err == GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF) { + clusterer.raiseError(err, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); + } else if (err == GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW) { + clusterer.raiseError(err, extendsToNextPage); + dumpPage = true; + } else if (err == GPUErrors::ERROR_TPCZS_INVALID_NADC) { + clusterer.raiseError(err, nSamplesInPage, nSamplesWritten, extendsToNextPage); + dumpPage = true; + } else { + clusterer.raiseError(GPUErrors::ERROR_TPCZS_UNKNOWN, err); + } + +#ifdef GPUCA_CHECK_TPCZS_CORRUPTION +#ifndef GPUCA_GPUCODE + if (dumpPage) { + // allocate more space on the stack for fname, so it can be overwritten by hand in a debugger. + const char fname[64] = "dump00.bin"; + FILE* foo = fopen(fname, "w+b"); + fwrite(pageStart, 1, TPCZSHDR::TPC_ZS_PAGE_SIZE, foo); + fclose(foo); + } +#endif #endif + } return pageDigitOffset; } template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( processorType& clusterer, [[maybe_unused]] GPUSharedMemory& smem, int32_t iThread, @@ -667,23 +713,24 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) + uint16_t nSamplesLeftInPage, + const uint8_t* payloadEnd, + const uint8_t* nextPage) { if constexpr (DecodeInParallel) { - return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); + return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); } else { - uint16_t nSamplesWritten = 0; + int16_t nSamplesWritten = 0; if (iThread == 0) { - nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); + nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); } return warp_broadcast(nSamplesWritten, 0); } } template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, @@ -692,8 +739,9 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) + uint16_t nSamplesLeftInPage, + const uint8_t* payloadEnd, + const uint8_t* nextPage) { #define MAYBE_PAGE_OVERFLOW(pagePtr) \ if constexpr (PayloadExtendsToNextPage) { \ @@ -703,7 +751,9 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ } \ } else { \ - assert(pagePtr <= payloadEnd); \ + if (pagePtr > payloadEnd) { \ + return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ + } \ } #define PEEK_OVERFLOW(pagePtr, offset) \ @@ -728,7 +778,7 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( uint16_t linkBC = (tbbHdr & 0xFFF0) >> 4; int32_t timeBin = (linkBC + (uint64_t)(raw::RDHUtils::getHeartBeatOrbit(*rawDataHeader) - firstHBF) * constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; - uint16_t nSamplesInTB = 0; + int16_t nSamplesInTB = 0; // Read timebin link headers for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) { @@ -747,7 +797,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( } int32_t nBytesBitmask = CAMath::Popcount(bitmaskL2); - assert(nBytesBitmask <= 10); for (int32_t chan = iThread; chan < CAMath::nextMultipleOf(80); chan += NTHREADS) { int32_t chanL2Idx = chan / 8; @@ -756,7 +805,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( int32_t chanByteOffset = nBytesBitmask - 1 - CAMath::Popcount(bitmaskL2 >> (chanL2Idx + 1)); uint8_t myChannelHasData = (chan < 80 && l2 ? TEST_BIT(PEEK_OVERFLOW(page, chanByteOffset), chan % 8) : 0); - assert(myChannelHasData == 0 || myChannelHasData == 1); int32_t nSamplesStep; int32_t threadSampleOffset = CfUtils::warpPredicateScan(myChannelHasData, &nSamplesStep); @@ -779,13 +827,17 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( GPUbarrierWarp(); // Ensure all writes to shared memory are finished, before reading it - const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); // TODO: We don't need this check? + if (nSamplesInTB > nSamplesLeftInPage) { + return -GPUErrors::ERROR_TPCZS_INVALID_NADC; + } if (not fragment.contains(timeBin)) { return FillWithInvalid(clusterer, iThread, NTHREADS, pageDigitOffset, nSamplesInTB); } + const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); + MAYBE_PAGE_OVERFLOW(page); + // Unpack ADC int32_t iLink = 0; for (uint16_t sample = iThread; sample < nSamplesInTB; sample += NTHREADS) { @@ -821,9 +873,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( GPUbarrierWarp(); // Ensure all reads to shared memory are finished, before decoding next header into shmem - assert(PayloadExtendsToNextPage || adcData <= page); - assert(PayloadExtendsToNextPage || page <= payloadEnd); - return nSamplesInTB; #undef TEST_BIT @@ -832,13 +881,14 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( } template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, + uint16_t nSamplesLeftInPage, [[maybe_unused]] const uint8_t* payloadEnd, [[maybe_unused]] const uint8_t* nextPage) { @@ -850,7 +900,9 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ } \ } else { \ - assert(pagePtr <= payloadEnd); \ + if (pagePtr > payloadEnd) { \ + return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ + } \ } using zerosupp_link_based::ChannelPerTBHeader; @@ -898,14 +950,18 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( } // for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) - const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); + if (nSamplesInTB > nSamplesLeftInPage) { + return -GPUErrors::ERROR_TPCZS_INVALID_NADC; + } if (not fragment.contains(timeBin)) { FillWithInvalid(clusterer, 0, 1, pageDigitOffset, nSamplesInTB); return nSamplesInTB; } + const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); + MAYBE_PAGE_OVERFLOW(page); + // Unpack ADC uint32_t byte = 0, bits = 0; uint16_t rawFECChannel = 0; @@ -937,10 +993,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( } // while (bits >= DECODE_BITS) } // while (nSamplesWritten < nAdc) - assert(PayloadExtendsToNextPage || adcData <= page); - assert(PayloadExtendsToNextPage || page <= payloadEnd); - assert(nSamplesWritten == nSamplesInTB); - return nSamplesWritten; #undef MAYBE_PAGE_OVERFLOW diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index e476674e030f9..4697462a8c504 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -167,14 +167,17 @@ class GPUTPCCFDecodeZSDenseLink : public GPUTPCCFDecodeZSLinkBase GPUd() static bool ChannelIsActive(const uint8_t* chan, uint16_t chanIndex); + // Decode a single timebin within an 8kb page. + // Returns the number of samples decoded from the page + // or negative value to indicate an error (no samples are written in this case) template - GPUd() static uint16_t DecodeTB(processorType& clusterer, GPUSharedMemory& smem, int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTB(processorType& clusterer, GPUSharedMemory& smem, int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); template - GPUd() static uint16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); template - GPUd() static uint16_t DecodeTBMultiThread(processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTBMultiThread(processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/kernels.cmake b/GPU/GPUTracking/kernels.cmake index c8ddcd2e9d81d..84726ea9fb8d0 100644 --- a/GPU/GPUTracking/kernels.cmake +++ b/GPU/GPUTracking/kernels.cmake @@ -120,7 +120,7 @@ o2_gpu_add_kernel("GPUTPCCFStreamCompaction, scanDown" "= TPC o2_gpu_add_kernel("GPUTPCCFStreamCompaction, compactDigits" "= TPCCLUSTERFINDER" LB int32_t iBuf int32_t stage CfChargePos* in CfChargePos* out) o2_gpu_add_kernel("GPUTPCCFDecodeZS" "= TPCCLUSTERFINDER" LB int32_t firstHBF) o2_gpu_add_kernel("GPUTPCCFDecodeZSLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) -o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) +o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS ERRORS" LB int32_t firstHBF) o2_gpu_add_kernel("GPUTPCCFGather" "=" LB o2::tpc::ClusterNative* dest) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode0asGPU" "= GLOBALREFIT " LB) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode1asTrackParCov" "= GLOBALREFIT " LB) From ecd32e5d9f470613d32aa1b077f458cf047e9a50 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 21 Oct 2025 09:45:13 +0200 Subject: [PATCH 90/99] Revert "GPU: Replace assertions with error counters in ZS decoding." This reverts commit 92548325d37460f34df364291b4d1f15ebc9215d. --- GPU/GPUTracking/Global/GPUErrorCodes.h | 6 +- .../TPCClusterFinder/GPUTPCCFDecodeZS.cxx | 156 ++++++------------ .../TPCClusterFinder/GPUTPCCFDecodeZS.h | 9 +- GPU/GPUTracking/kernels.cmake | 2 +- 4 files changed, 57 insertions(+), 116 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUErrorCodes.h b/GPU/GPUTracking/Global/GPUErrorCodes.h index a4921f478b107..8fec23be00a09 100644 --- a/GPU/GPUTracking/Global/GPUErrorCodes.h +++ b/GPU/GPUTracking/Global/GPUErrorCodes.h @@ -47,10 +47,6 @@ GPUCA_ERROR_CODE(26, ERROR_TPCZS_INVALID_ROW, SectorRow) GPUCA_ERROR_CODE(27, ERROR_TPCZS_INVALID_NADC, SectorCRU, SamplesInPage, SamplesWritten) // Invalid number of ADC samples in header, existing samples were decoded GPUCA_ERROR_CODE(28, ERROR_TPCZS_INCOMPLETE_HBF, SectorCRU, PacketCount, NextPacketCount) // Part of HBF is missing, decoding incomplete GPUCA_ERROR_CODE(29, ERROR_TPCZS_INVALID_OFFSET, SectorEndpoint, Value, Expected) // Raw page is skipped since it contains invalid payload offset -GPUCA_ERROR_CODE(30, ERROR_TPCZS_INVALID_MAGIC_WORD, Value) // ZS header contains wrong magic word -GPUCA_ERROR_CODE(31, ERROR_TPCZS_PAGE_OVERFLOW, Position, PageEnd) // Ran out of page to decode -GPUCA_ERROR_CODE(32, ERROR_TPCZS_VERSION_MISMATCH, Value, Expected) // ZS decoder received page with wrong version -GPUCA_ERROR_CODE(33, ERROR_TPCZS_UNKNOWN, ErrorCode) // Unkown or invalid error code raised in decoder -GPUCA_ERROR_CODE(33, MAX_GPUCA_ERROR_NUMBER) +GPUCA_ERROR_CODE(29, MAX_GPUCA_ERROR_NUMBER) // #define GPUCA_CHECK_TPCZS_CORRUPTION diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index a548217e26b64..f7bb64106fe4f 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx @@ -604,107 +604,61 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro const auto* decHeader = Peek(page, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2)); ConsumeHeader(page); + assert(decHeader->version >= ZSVersionDenseLinkBased); + assert(decHeader->magicWord == tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader); + uint16_t nSamplesWritten = 0; const uint16_t nSamplesInPage = decHeader->nADCsamples; const auto* payloadEnd = Peek(pageStart, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2) - ((decHeader->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0)); const auto* nextPage = Peek(pageStart, TPCZSHDR::TPC_ZS_PAGE_SIZE); - const bool extendsToNextPage = decHeader->flags & TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage; - ConsumeBytes(page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader)); - int err = GPUErrors::ERROR_NONE; + for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) { - if (decHeader->version < ZSVersionDenseLinkBased) { - err = GPUErrors::ERROR_TPCZS_VERSION_MISMATCH; - } + [[maybe_unused]] ptrdiff_t sizeLeftInPage = payloadEnd - page; + assert(sizeLeftInPage > 0); - if (decHeader->magicWord != zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader) { - err = GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD; - } - - for (uint16_t i = 0; i < decHeader->nTimebinHeaders && !err; i++) { - - ptrdiff_t sizeLeftInPage = payloadEnd - page; - if (sizeLeftInPage <= 0) { - err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; - break; - } - - int16_t nSamplesWrittenTB = 0; - uint16_t nSamplesLeftInPage = nSamplesInPage - nSamplesWritten; - - if (i == decHeader->nTimebinHeaders - 1 && extendsToNextPage) { - if (raw::RDHUtils::getMemorySize(*rawDataHeader) != TPCZSHDR::TPC_ZS_PAGE_SIZE) { - err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; - break; - } + uint16_t nSamplesWrittenTB = 0; + if (i == decHeader->nTimebinHeaders - 1 && decHeader->flags & o2::tpc::TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage) { + assert(o2::raw::RDHUtils::getMemorySize(*rawDataHeader) == TPCZSHDR::TPC_ZS_PAGE_SIZE); if ((uint16_t)(raw::RDHUtils::getPageCounter(rawDataHeader) + 1) == raw::RDHUtils::getPageCounter(nextPage)) { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); } else { - err = GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF; - break; + nSamplesWrittenTB = FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); +#ifdef GPUCA_CHECK_TPCZS_CORRUPTION + if (iThread == 0) { + clusterer.raiseError(GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); + } +#endif } } else { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); - } - - // Abort decoding the page if an error was detected. - if (nSamplesWrittenTB < 0) { - err = -nSamplesWrittenTB; - break; + nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); } + assert(nSamplesWritten <= nSamplesInPage); nSamplesWritten += nSamplesWrittenTB; pageDigitOffset += nSamplesWrittenTB; } // for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) - if (nSamplesWritten != nSamplesInPage) { - if (nSamplesWritten < nSamplesInPage) { - pageDigitOffset += FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); - } - err = !err ? GPUErrors::ERROR_TPCZS_INVALID_NADC : err; // Ensure we don't overwrite any previous error - } - - if (iThread == 0 && err) { - [[maybe_unused]] bool dumpPage = false; - - if (err == GPUErrors::ERROR_TPCZS_VERSION_MISMATCH) { - clusterer.raiseError(err, decHeader->version, ZSVersionDenseLinkBased); - } else if (err == GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD) { - clusterer.raiseError(err, decHeader->magicWord); - } else if (err == GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF) { - clusterer.raiseError(err, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); - } else if (err == GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW) { - clusterer.raiseError(err, extendsToNextPage); - dumpPage = true; - } else if (err == GPUErrors::ERROR_TPCZS_INVALID_NADC) { - clusterer.raiseError(err, nSamplesInPage, nSamplesWritten, extendsToNextPage); - dumpPage = true; - } else { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_UNKNOWN, err); - } - #ifdef GPUCA_CHECK_TPCZS_CORRUPTION -#ifndef GPUCA_GPUCODE - if (dumpPage) { - // allocate more space on the stack for fname, so it can be overwritten by hand in a debugger. - const char fname[64] = "dump00.bin"; - FILE* foo = fopen(fname, "w+b"); - fwrite(pageStart, 1, TPCZSHDR::TPC_ZS_PAGE_SIZE, foo); - fclose(foo); - } -#endif -#endif + if (iThread == 0 && nSamplesWritten != nSamplesInPage) { + clusterer.raiseError(GPUErrors::ERROR_TPCZS_INVALID_NADC, clusterer.mISector * 1000 + decHeader->cruID, nSamplesInPage, nSamplesWritten); + /*#ifndef GPUCA_GPUCODE + FILE* foo = fopen("dump.bin", "w+b"); + fwrite(pageSrc, 1, o2::raw::RDHUtils::getMemorySize(*rdHdr), foo); + fclose(foo); + #endif*/ } +#endif return pageDigitOffset; } template -GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( +GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( processorType& clusterer, [[maybe_unused]] GPUSharedMemory& smem, int32_t iThread, @@ -713,24 +667,23 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, - uint16_t nSamplesLeftInPage, - const uint8_t* payloadEnd, - const uint8_t* nextPage) + [[maybe_unused]] const uint8_t* payloadEnd, + [[maybe_unused]] const uint8_t* nextPage) { if constexpr (DecodeInParallel) { - return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); + return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); } else { - int16_t nSamplesWritten = 0; + uint16_t nSamplesWritten = 0; if (iThread == 0) { - nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); + nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); } return warp_broadcast(nSamplesWritten, 0); } } template -GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( +GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, @@ -739,9 +692,8 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, - uint16_t nSamplesLeftInPage, - const uint8_t* payloadEnd, - const uint8_t* nextPage) + [[maybe_unused]] const uint8_t* payloadEnd, + [[maybe_unused]] const uint8_t* nextPage) { #define MAYBE_PAGE_OVERFLOW(pagePtr) \ if constexpr (PayloadExtendsToNextPage) { \ @@ -751,9 +703,7 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ } \ } else { \ - if (pagePtr > payloadEnd) { \ - return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ - } \ + assert(pagePtr <= payloadEnd); \ } #define PEEK_OVERFLOW(pagePtr, offset) \ @@ -778,7 +728,7 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( uint16_t linkBC = (tbbHdr & 0xFFF0) >> 4; int32_t timeBin = (linkBC + (uint64_t)(raw::RDHUtils::getHeartBeatOrbit(*rawDataHeader) - firstHBF) * constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; - int16_t nSamplesInTB = 0; + uint16_t nSamplesInTB = 0; // Read timebin link headers for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) { @@ -797,6 +747,7 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( } int32_t nBytesBitmask = CAMath::Popcount(bitmaskL2); + assert(nBytesBitmask <= 10); for (int32_t chan = iThread; chan < CAMath::nextMultipleOf(80); chan += NTHREADS) { int32_t chanL2Idx = chan / 8; @@ -805,6 +756,7 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( int32_t chanByteOffset = nBytesBitmask - 1 - CAMath::Popcount(bitmaskL2 >> (chanL2Idx + 1)); uint8_t myChannelHasData = (chan < 80 && l2 ? TEST_BIT(PEEK_OVERFLOW(page, chanByteOffset), chan % 8) : 0); + assert(myChannelHasData == 0 || myChannelHasData == 1); int32_t nSamplesStep; int32_t threadSampleOffset = CfUtils::warpPredicateScan(myChannelHasData, &nSamplesStep); @@ -827,17 +779,13 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( GPUbarrierWarp(); // Ensure all writes to shared memory are finished, before reading it - if (nSamplesInTB > nSamplesLeftInPage) { - return -GPUErrors::ERROR_TPCZS_INVALID_NADC; - } + const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); + MAYBE_PAGE_OVERFLOW(page); // TODO: We don't need this check? if (not fragment.contains(timeBin)) { return FillWithInvalid(clusterer, iThread, NTHREADS, pageDigitOffset, nSamplesInTB); } - const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); - // Unpack ADC int32_t iLink = 0; for (uint16_t sample = iThread; sample < nSamplesInTB; sample += NTHREADS) { @@ -873,6 +821,9 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( GPUbarrierWarp(); // Ensure all reads to shared memory are finished, before decoding next header into shmem + assert(PayloadExtendsToNextPage || adcData <= page); + assert(PayloadExtendsToNextPage || page <= payloadEnd); + return nSamplesInTB; #undef TEST_BIT @@ -881,14 +832,13 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( } template -GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( +GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, - uint16_t nSamplesLeftInPage, [[maybe_unused]] const uint8_t* payloadEnd, [[maybe_unused]] const uint8_t* nextPage) { @@ -900,9 +850,7 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ } \ } else { \ - if (pagePtr > payloadEnd) { \ - return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ - } \ + assert(pagePtr <= payloadEnd); \ } using zerosupp_link_based::ChannelPerTBHeader; @@ -950,18 +898,14 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( } // for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) - if (nSamplesInTB > nSamplesLeftInPage) { - return -GPUErrors::ERROR_TPCZS_INVALID_NADC; - } + const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); + MAYBE_PAGE_OVERFLOW(page); if (not fragment.contains(timeBin)) { FillWithInvalid(clusterer, 0, 1, pageDigitOffset, nSamplesInTB); return nSamplesInTB; } - const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); - // Unpack ADC uint32_t byte = 0, bits = 0; uint16_t rawFECChannel = 0; @@ -993,6 +937,10 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( } // while (bits >= DECODE_BITS) } // while (nSamplesWritten < nAdc) + assert(PayloadExtendsToNextPage || adcData <= page); + assert(PayloadExtendsToNextPage || page <= payloadEnd); + assert(nSamplesWritten == nSamplesInTB); + return nSamplesWritten; #undef MAYBE_PAGE_OVERFLOW diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index 4697462a8c504..e476674e030f9 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -167,17 +167,14 @@ class GPUTPCCFDecodeZSDenseLink : public GPUTPCCFDecodeZSLinkBase GPUd() static bool ChannelIsActive(const uint8_t* chan, uint16_t chanIndex); - // Decode a single timebin within an 8kb page. - // Returns the number of samples decoded from the page - // or negative value to indicate an error (no samples are written in this case) template - GPUd() static int16_t DecodeTB(processorType& clusterer, GPUSharedMemory& smem, int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static uint16_t DecodeTB(processorType& clusterer, GPUSharedMemory& smem, int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); template - GPUd() static int16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static uint16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); template - GPUd() static int16_t DecodeTBMultiThread(processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static uint16_t DecodeTBMultiThread(processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/kernels.cmake b/GPU/GPUTracking/kernels.cmake index 84726ea9fb8d0..c8ddcd2e9d81d 100644 --- a/GPU/GPUTracking/kernels.cmake +++ b/GPU/GPUTracking/kernels.cmake @@ -120,7 +120,7 @@ o2_gpu_add_kernel("GPUTPCCFStreamCompaction, scanDown" "= TPC o2_gpu_add_kernel("GPUTPCCFStreamCompaction, compactDigits" "= TPCCLUSTERFINDER" LB int32_t iBuf int32_t stage CfChargePos* in CfChargePos* out) o2_gpu_add_kernel("GPUTPCCFDecodeZS" "= TPCCLUSTERFINDER" LB int32_t firstHBF) o2_gpu_add_kernel("GPUTPCCFDecodeZSLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) -o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS ERRORS" LB int32_t firstHBF) +o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) o2_gpu_add_kernel("GPUTPCCFGather" "=" LB o2::tpc::ClusterNative* dest) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode0asGPU" "= GLOBALREFIT " LB) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode1asTrackParCov" "= GLOBALREFIT " LB) From 195a56c4109e63a46e9baf4469f19a8f4fba237c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 20 Oct 2025 11:52:50 +0200 Subject: [PATCH 91/99] GPU Standalone: Add setMaxTimeBin debug option --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + GPU/GPUTracking/Standalone/Benchmark/standalone.cxx | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 8cf6b29a43d96..957631e571b76 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -573,6 +573,7 @@ AddOption(noEvents, bool, false, "", 0, "Run without data (e.g. for field visual AddOption(eventDisplay, int32_t, 0, "display", 'd', "Show standalone event display", def(1)) AddOption(eventGenerator, bool, false, "", 0, "Run event generator") AddOption(cont, bool, false, "", 0, "Process continuous timeframe data, even if input is triggered") +AddOption(setMaxTimeBin, int32_t, -2, "", 0, "maximum time bin of continuous data, 0 for triggered events, -1 for automatic continuous mode, -2 for automatic continuous / triggered") AddOption(outputcontrolmem, uint64_t, 0, "outputMemory", 0, "Use predefined output buffer of this size", min(0ul), message("Using %s bytes as output memory")) AddOption(inputcontrolmem, uint64_t, 0, "inputMemory", 0, "Use predefined input buffer of this size", min(0ul), message("Using %s bytes as input memory")) AddOption(cpuAffinity, int32_t, -1, "", 0, "Pin CPU affinity to this CPU core", min(-1)) diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index 4fe1691afef50..11d32394ccfb9 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -331,9 +331,15 @@ int32_t SetupReconstruction() grp.grpContinuousMaxTimeBin = configStandalone.TF.timeFrameLen * ((double)GPUReconstructionTimeframe::TPCZ / (double)GPUReconstructionTimeframe::DRIFT_TIME) / chainTracking->GetTPCTransformHelper()->getCorrMap()->getVDrift(); } } - if (configStandalone.cont && grp.grpContinuousMaxTimeBin == 0) { + if (configStandalone.setMaxTimeBin != -2) { + grp.grpContinuousMaxTimeBin = configStandalone.setMaxTimeBin; + } else if (configStandalone.cont && grp.grpContinuousMaxTimeBin == 0) { grp.grpContinuousMaxTimeBin = -1; } + if (grp.grpContinuousMaxTimeBin < -1 && !configStandalone.noEvents) { + printf("Invalid maxTimeBin %d\n", grp.grpContinuousMaxTimeBin); + return 1; + } if (rec->GetDeviceType() == GPUReconstruction::DeviceType::CPU) { printf("Standalone Test Framework for CA Tracker - Using CPU\n"); } else { From d8b41643f670c5d16bbc05f426e5474761bf6ef1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 20 Oct 2025 11:54:12 +0200 Subject: [PATCH 92/99] GPU TPC: Fix track Z offset in triggered mode --- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 4 ++-- GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx | 4 ++-- GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx | 2 +- GPU/GPUTracking/SectorTracker/GPUTPCTracker.h | 12 ++++++------ .../SectorTracker/GPUTPCTrackletConstructor.cxx | 10 +++++++--- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 9a4b129f751a4..16d96dad17d16 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -561,7 +561,7 @@ GPUd() int32_t GPUTPCGMMerger::RefitSectorTrack(GPUTPCGMSectorTrack& sectorTrack trk.SinPhi() = inTrack->Param().GetSinPhi(); trk.DzDs() = inTrack->Param().GetDzDs(); trk.QPt() = inTrack->Param().GetQPt(); - trk.TOffset() = GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, inTrack->Param().GetZOffset(), Param().continuousMaxTimeBin); + trk.TOffset() = Param().par.continuousTracking ? GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, inTrack->Param().GetZOffset(), Param().continuousMaxTimeBin) : 0; const auto tmp = sectorTrack.ClusterTN() > sectorTrack.ClusterT0() ? std::array{sectorTrack.ClusterTN(), sectorTrack.ClusterT0()} : std::array{sectorTrack.ClusterT0(), sectorTrack.ClusterTN()}; trk.ShiftZ(this, sector, tmp[0], tmp[1], inTrack->Param().GetX()); // We do not store the inner / outer cluster X, so we just use the track X instead sectorTrack.SetX2(0.f); @@ -1939,7 +1939,7 @@ GPUd() void GPUTPCGMMerger::MergeLoopersInit(int32_t nBlocks, int32_t nThreads, const float qptabs = CAMath::Abs(p.GetQPt()); if (trk.OK() && trk.NClusters() && trk.Leg() == 0 && qptabs * Param().qptB5Scaler > 5.f && qptabs * Param().qptB5Scaler <= lowPtThresh) { const int32_t sector = mClusters[trk.FirstClusterRef() + trk.NClusters() - 1].sector; - const float refz = p.GetZ() + GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, p.GetTOffset(), Param().continuousMaxTimeBin) + (trk.CSide() ? -100 : 100); + const float refz = p.GetZ() + (Param().par.continuousTracking ? GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, p.GetTOffset(), Param().continuousMaxTimeBin) : 0) + (trk.CSide() ? -100 : 100); float sinA, cosA; CAMath::SinCos(trk.GetAlpha(), sinA, cosA); float gx = cosA * p.GetX() - sinA * p.GetY(); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx index bce70ea79f322..1c3d842b0419a 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx @@ -323,7 +323,7 @@ GPUd() bool GPUTPCGMSectorTrack::TransportToX(GPUTPCGMMerger* merger, float x, f b.SetPar(2, ey1); b.SetPar(3, param.mDzDs); b.SetPar(4, param.mQPt); - b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin)); + b.SetZOffsetLinear(merger->Param().par.continuousTracking ? merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin) : 0); if (!doCov) { return (1); @@ -478,7 +478,7 @@ GPUd() bool GPUTPCGMSectorTrack::TransportToXAlpha(GPUTPCGMMerger* merger, float b.SetPar(2, ey1); b.SetPar(3, dzds); b.SetPar(4, qpt); - b.SetZOffsetLinear(merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin)); + b.SetZOffsetLinear(merger->Param().par.continuousTracking ? merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(mSector, mTOffset, merger->Param().continuousMaxTimeBin) : 0); b.SetCov(0, c00 + h2 * h2c22 + h4 * h4c44 + 2.f * (h2 * c20ph4c42 + h4 * c40)); b.SetCov(1, c11 + dS * (c31 + n7)); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx index d865a3b6899b4..970b42f6ee78a 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx @@ -506,7 +506,7 @@ GPUd() float GPUTPCGMTrackParam::AttachClusters(const GPUTPCGMMerger* GPUrestric return -1e6f; } - const float zOffset = Merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, mTOffset, Merger->Param().continuousMaxTimeBin); + const float zOffset = Merger->Param().par.continuousTracking ? Merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convVertexTimeToZOffset(sector, mTOffset, Merger->Param().continuousMaxTimeBin) : 0; const float y0 = row.Grid().YMin(); const float stepY = row.HstepY(); const float z0 = row.Grid().ZMin() - zOffset; // We can use our own ZOffset, since this is only used temporarily anyway diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h index aee429c959e98..5efd3ca845410 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.h @@ -77,19 +77,19 @@ class GPUTPCTracker : public GPUProcessor GPUdi() static void GetErrors2Seeding(const GPUParam& param, char sector, int32_t iRow, const GPUTPCTrackParam& t, float time, float& ErrY2, float& ErrZ2) { - // param.GetClusterErrors2(sector, iRow, param.GetContinuousTracking() != 0. ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); - param.GetClusterErrorsSeeding2(sector, iRow, param.par.continuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); + // param.GetClusterErrors2(sector, iRow, param.continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); + param.GetClusterErrorsSeeding2(sector, iRow, param.par.continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); } GPUdi() void GetErrors2Seeding(int32_t iRow, const GPUTPCTrackParam& t, float time, float& ErrY2, float& ErrZ2) const { - // Param().GetClusterErrors2(mISector, iRow, Param().GetContinuousTracking() != 0. ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); - Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking != 0.f ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); + // Param().GetClusterErrors2(mISector, iRow, Param().continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, 0.f, 0.f, ErrY2, ErrZ2); + Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking ? 125.f : t.Z(), t.SinPhi(), t.DzDs(), time, ErrY2, ErrZ2); } GPUdi() void GetErrors2Seeding(int32_t iRow, float z, float sinPhi, float DzDs, float time, float& ErrY2, float& ErrZ2) const { - // Param().GetClusterErrors2(mISector, iRow, Param().GetContinuousTracking() != 0. ? 125.f : z, sinPhi, DzDs, time, 0.f, 0.f, ErrY2, ErrZ2); - Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking != 0.f ? 125.f : z, sinPhi, DzDs, time, ErrY2, ErrZ2); + // Param().GetClusterErrors2(mISector, iRow, Param().continuousTracking ? 125.f : z, sinPhi, DzDs, time, 0.f, 0.f, ErrY2, ErrZ2); + Param().GetClusterErrorsSeeding2(mISector, iRow, Param().par.continuousTracking ? 125.f : z, sinPhi, DzDs, time, ErrY2, ErrZ2); } void SetupCommonMemory(); diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx index 0b22bfa57c89e..567e4e93cc524 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTrackletConstructor.cxx @@ -131,8 +131,10 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, float x = row.X(); float y = y0 + hh.x * stepY; float z = z0 + hh.y * stepZ; - if (iRow != r.mStartRow || !tracker.Param().par.continuousTracking) { - tParam.ConstrainZ(z, tracker.ISector(), z0, r.mLastZ); + if (iRow != r.mStartRow) { + if (tracker.Param().par.continuousTracking) { + tParam.ConstrainZ(z, tracker.ISector(), z0, r.mLastZ); + } tracker.GetConstantMem()->calibObjects.fastTransformHelper->TransformXYZ(tracker.ISector(), iRow, x, y, z); } if (iRow == r.mStartRow) { @@ -262,7 +264,9 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, rowHit = CALINK_INVAL; break; } - tParam.ConstrainZ(tmpZ, tracker.ISector(), z0, r.mLastZ); + if (tracker.Param().par.continuousTracking) { + tParam.ConstrainZ(tmpZ, tracker.ISector(), z0, r.mLastZ); + } tracker.GetConstantMem()->calibObjects.fastTransformHelper->InverseTransformYZtoX(tracker.ISector(), iRow, tmpY, tmpZ, x); } From 6dddef7f2691e045209004d45172a2fede5ef3b8 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 21 Oct 2025 15:27:05 +0200 Subject: [PATCH 93/99] GPU: Fix TPC Z Offset computation in triggered mode in one more place --- GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx index 1c3d842b0419a..6042dec7d4931 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.cxx @@ -37,7 +37,7 @@ GPUd() void GPUTPCGMSectorTrack::Set(const GPUTPCGMMerger* merger, const GPUTPCT mParam.mSecPhi = 1.f / mParam.mCosPhi; mAlpha = alpha; mSector = sector; - mTOffset = merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, t.GetZOffset(), merger->Param().continuousMaxTimeBin); + mTOffset = merger->Param().par.continuousTracking ? merger->GetConstantMem()->calibObjects.fastTransformHelper->getCorrMap()->convZOffsetToVertexTime(sector, t.GetZOffset(), merger->Param().continuousMaxTimeBin) : 0; mNClusters = sectorTr->NHits(); } From 0ac71401a794821e2030a387b1d532b193f98a72 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Fri, 17 Oct 2025 15:31:27 +0200 Subject: [PATCH 94/99] GPU: Replace assertions with error counters in ZS decoding. --- GPU/GPUTracking/Global/GPUErrorCodes.h | 6 +- .../TPCClusterFinder/GPUTPCCFDecodeZS.cxx | 155 ++++++++++++------ .../TPCClusterFinder/GPUTPCCFDecodeZS.h | 9 +- GPU/GPUTracking/kernels.cmake | 2 +- 4 files changed, 116 insertions(+), 56 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUErrorCodes.h b/GPU/GPUTracking/Global/GPUErrorCodes.h index 8fec23be00a09..a4921f478b107 100644 --- a/GPU/GPUTracking/Global/GPUErrorCodes.h +++ b/GPU/GPUTracking/Global/GPUErrorCodes.h @@ -47,6 +47,10 @@ GPUCA_ERROR_CODE(26, ERROR_TPCZS_INVALID_ROW, SectorRow) GPUCA_ERROR_CODE(27, ERROR_TPCZS_INVALID_NADC, SectorCRU, SamplesInPage, SamplesWritten) // Invalid number of ADC samples in header, existing samples were decoded GPUCA_ERROR_CODE(28, ERROR_TPCZS_INCOMPLETE_HBF, SectorCRU, PacketCount, NextPacketCount) // Part of HBF is missing, decoding incomplete GPUCA_ERROR_CODE(29, ERROR_TPCZS_INVALID_OFFSET, SectorEndpoint, Value, Expected) // Raw page is skipped since it contains invalid payload offset -GPUCA_ERROR_CODE(29, MAX_GPUCA_ERROR_NUMBER) +GPUCA_ERROR_CODE(30, ERROR_TPCZS_INVALID_MAGIC_WORD, Value) // ZS header contains wrong magic word +GPUCA_ERROR_CODE(31, ERROR_TPCZS_PAGE_OVERFLOW, Position, PageEnd) // Ran out of page to decode +GPUCA_ERROR_CODE(32, ERROR_TPCZS_VERSION_MISMATCH, Value, Expected) // ZS decoder received page with wrong version +GPUCA_ERROR_CODE(33, ERROR_TPCZS_UNKNOWN, ErrorCode) // Unkown or invalid error code raised in decoder +GPUCA_ERROR_CODE(33, MAX_GPUCA_ERROR_NUMBER) // #define GPUCA_CHECK_TPCZS_CORRUPTION diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index f7bb64106fe4f..54af72f08a432 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx @@ -604,61 +604,107 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro const auto* decHeader = Peek(page, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2)); ConsumeHeader(page); - assert(decHeader->version >= ZSVersionDenseLinkBased); - assert(decHeader->magicWord == tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader); - uint16_t nSamplesWritten = 0; const uint16_t nSamplesInPage = decHeader->nADCsamples; const auto* payloadEnd = Peek(pageStart, raw::RDHUtils::getMemorySize(*rawDataHeader) - sizeof(TPCZSHDRV2) - ((decHeader->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0)); const auto* nextPage = Peek(pageStart, TPCZSHDR::TPC_ZS_PAGE_SIZE); + const bool extendsToNextPage = decHeader->flags & TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage; + ConsumeBytes(page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader)); - for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) { + int err = GPUErrors::ERROR_NONE; + + if (decHeader->version < ZSVersionDenseLinkBased) { + err = GPUErrors::ERROR_TPCZS_VERSION_MISMATCH; + } - [[maybe_unused]] ptrdiff_t sizeLeftInPage = payloadEnd - page; - assert(sizeLeftInPage > 0); + if (decHeader->magicWord != zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader) { + err = GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD; + } - uint16_t nSamplesWrittenTB = 0; + for (uint16_t i = 0; i < decHeader->nTimebinHeaders && !err; i++) { + + ptrdiff_t sizeLeftInPage = payloadEnd - page; + if (sizeLeftInPage <= 0) { + err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; + break; + } + + int16_t nSamplesWrittenTB = 0; + uint16_t nSamplesLeftInPage = nSamplesInPage - nSamplesWritten; + + if (i == decHeader->nTimebinHeaders - 1 && extendsToNextPage) { + if (raw::RDHUtils::getMemorySize(*rawDataHeader) != TPCZSHDR::TPC_ZS_PAGE_SIZE) { + err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; + break; + } - if (i == decHeader->nTimebinHeaders - 1 && decHeader->flags & o2::tpc::TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage) { - assert(o2::raw::RDHUtils::getMemorySize(*rawDataHeader) == TPCZSHDR::TPC_ZS_PAGE_SIZE); if ((uint16_t)(raw::RDHUtils::getPageCounter(rawDataHeader) + 1) == raw::RDHUtils::getPageCounter(nextPage)) { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); } else { - nSamplesWrittenTB = FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); -#ifdef GPUCA_CHECK_TPCZS_CORRUPTION - if (iThread == 0) { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); - } -#endif + err = GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF; + break; } } else { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + } + + // Abort decoding the page if an error was detected. + if (nSamplesWrittenTB < 0) { + err = -nSamplesWrittenTB; + break; } - assert(nSamplesWritten <= nSamplesInPage); nSamplesWritten += nSamplesWrittenTB; pageDigitOffset += nSamplesWrittenTB; } // for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) -#ifdef GPUCA_CHECK_TPCZS_CORRUPTION - if (iThread == 0 && nSamplesWritten != nSamplesInPage) { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_INVALID_NADC, clusterer.mISector * 1000 + decHeader->cruID, nSamplesInPage, nSamplesWritten); - /*#ifndef GPUCA_GPUCODE - FILE* foo = fopen("dump.bin", "w+b"); - fwrite(pageSrc, 1, o2::raw::RDHUtils::getMemorySize(*rdHdr), foo); - fclose(foo); - #endif*/ + if (nSamplesWritten != nSamplesInPage) { + if (nSamplesWritten < nSamplesInPage) { + pageDigitOffset += FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); + } + err = !err ? GPUErrors::ERROR_TPCZS_INVALID_NADC : err; // Ensure we don't overwrite any previous error } + + if (iThread == 0 && err) { + [[maybe_unused]] bool dumpPage = false; + + if (err == GPUErrors::ERROR_TPCZS_VERSION_MISMATCH) { + clusterer.raiseError(err, decHeader->version, ZSVersionDenseLinkBased); + } else if (err == GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD) { + clusterer.raiseError(err, decHeader->magicWord); + } else if (err == GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF) { + clusterer.raiseError(err, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); + } else if (err == GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW) { + clusterer.raiseError(err, extendsToNextPage); + dumpPage = true; + } else if (err == GPUErrors::ERROR_TPCZS_INVALID_NADC) { + clusterer.raiseError(err, nSamplesInPage, nSamplesWritten, extendsToNextPage); + dumpPage = true; + } else { + clusterer.raiseError(GPUErrors::ERROR_TPCZS_UNKNOWN, err); + } + +#ifdef GPUCA_CHECK_TPCZS_CORRUPTION +#ifndef GPUCA_GPUCODE + if (dumpPage) { + // allocate more space on the stack for fname, so it can be overwritten by hand in a debugger. + const char fname[64] = "dump00.bin"; + FILE* foo = fopen(fname, "w+b"); + fwrite(pageStart, 1, TPCZSHDR::TPC_ZS_PAGE_SIZE, foo); + fclose(foo); + } #endif +#endif + } return pageDigitOffset; } template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( processorType& clusterer, [[maybe_unused]] GPUSharedMemory& smem, int32_t iThread, @@ -667,23 +713,24 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) + uint16_t nSamplesLeftInPage, + const uint8_t* payloadEnd, + const uint8_t* nextPage) { if constexpr (DecodeInParallel) { - return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); + return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); } else { - uint16_t nSamplesWritten = 0; + int16_t nSamplesWritten = 0; if (iThread == 0) { - nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, payloadEnd, nextPage); + nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); } return warp_broadcast(nSamplesWritten, 0); } } template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, @@ -692,8 +739,9 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) + uint16_t nSamplesLeftInPage, + const uint8_t* payloadEnd, + const uint8_t* nextPage) { #define MAYBE_PAGE_OVERFLOW(pagePtr) \ if constexpr (PayloadExtendsToNextPage) { \ @@ -703,7 +751,9 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ } \ } else { \ - assert(pagePtr <= payloadEnd); \ + if (pagePtr > payloadEnd) { \ + return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ + } \ } #define PEEK_OVERFLOW(pagePtr, offset) \ @@ -728,7 +778,7 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( uint16_t linkBC = (tbbHdr & 0xFFF0) >> 4; int32_t timeBin = (linkBC + (uint64_t)(raw::RDHUtils::getHeartBeatOrbit(*rawDataHeader) - firstHBF) * constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; - uint16_t nSamplesInTB = 0; + int16_t nSamplesInTB = 0; // Read timebin link headers for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) { @@ -747,7 +797,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( } int32_t nBytesBitmask = CAMath::Popcount(bitmaskL2); - assert(nBytesBitmask <= 10); for (int32_t chan = iThread; chan < CAMath::nextMultipleOf(80); chan += NTHREADS) { int32_t chanL2Idx = chan / 8; @@ -756,7 +805,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( int32_t chanByteOffset = nBytesBitmask - 1 - CAMath::Popcount(bitmaskL2 >> (chanL2Idx + 1)); uint8_t myChannelHasData = (chan < 80 && l2 ? TEST_BIT(PEEK_OVERFLOW(page, chanByteOffset), chan % 8) : 0); - assert(myChannelHasData == 0 || myChannelHasData == 1); int32_t nSamplesStep; int32_t threadSampleOffset = CfUtils::warpPredicateScan(myChannelHasData, &nSamplesStep); @@ -779,8 +827,14 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( GPUbarrierWarp(); // Ensure all writes to shared memory are finished, before reading it + if (nSamplesInTB > nSamplesLeftInPage) { + return -GPUErrors::ERROR_TPCZS_INVALID_NADC; + } + + // This needs to happen BEFORE checking if the timebin is in fragment + // to ensure ADC bytes are always consumed, even if data isn't decoded const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); // TODO: We don't need this check? + MAYBE_PAGE_OVERFLOW(page); if (not fragment.contains(timeBin)) { return FillWithInvalid(clusterer, iThread, NTHREADS, pageDigitOffset, nSamplesInTB); @@ -821,9 +875,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( GPUbarrierWarp(); // Ensure all reads to shared memory are finished, before decoding next header into shmem - assert(PayloadExtendsToNextPage || adcData <= page); - assert(PayloadExtendsToNextPage || page <= payloadEnd); - return nSamplesInTB; #undef TEST_BIT @@ -832,13 +883,14 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( } template -GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, + uint16_t nSamplesLeftInPage, [[maybe_unused]] const uint8_t* payloadEnd, [[maybe_unused]] const uint8_t* nextPage) { @@ -850,7 +902,9 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( ConsumeBytes(pagePtr, sizeof(header::RAWDataHeader) + diff); \ } \ } else { \ - assert(pagePtr <= payloadEnd); \ + if (pagePtr > payloadEnd) { \ + return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ + } \ } using zerosupp_link_based::ChannelPerTBHeader; @@ -898,12 +952,15 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( } // for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) + if (nSamplesInTB > nSamplesLeftInPage) { + return -GPUErrors::ERROR_TPCZS_INVALID_NADC; + } + const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); MAYBE_PAGE_OVERFLOW(page); if (not fragment.contains(timeBin)) { - FillWithInvalid(clusterer, 0, 1, pageDigitOffset, nSamplesInTB); - return nSamplesInTB; + return FillWithInvalid(clusterer, 0, 1, pageDigitOffset, nSamplesInTB); } // Unpack ADC @@ -937,10 +994,6 @@ GPUd() uint16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( } // while (bits >= DECODE_BITS) } // while (nSamplesWritten < nAdc) - assert(PayloadExtendsToNextPage || adcData <= page); - assert(PayloadExtendsToNextPage || page <= payloadEnd); - assert(nSamplesWritten == nSamplesInTB); - return nSamplesWritten; #undef MAYBE_PAGE_OVERFLOW diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index e476674e030f9..4697462a8c504 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -167,14 +167,17 @@ class GPUTPCCFDecodeZSDenseLink : public GPUTPCCFDecodeZSLinkBase GPUd() static bool ChannelIsActive(const uint8_t* chan, uint16_t chanIndex); + // Decode a single timebin within an 8kb page. + // Returns the number of samples decoded from the page + // or negative value to indicate an error (no samples are written in this case) template - GPUd() static uint16_t DecodeTB(processorType& clusterer, GPUSharedMemory& smem, int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTB(processorType& clusterer, GPUSharedMemory& smem, int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); template - GPUd() static uint16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); template - GPUd() static uint16_t DecodeTBMultiThread(processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTBMultiThread(processorType& clusterer, GPUSharedMemory& smem, const int32_t iThread, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/kernels.cmake b/GPU/GPUTracking/kernels.cmake index c8ddcd2e9d81d..84726ea9fb8d0 100644 --- a/GPU/GPUTracking/kernels.cmake +++ b/GPU/GPUTracking/kernels.cmake @@ -120,7 +120,7 @@ o2_gpu_add_kernel("GPUTPCCFStreamCompaction, scanDown" "= TPC o2_gpu_add_kernel("GPUTPCCFStreamCompaction, compactDigits" "= TPCCLUSTERFINDER" LB int32_t iBuf int32_t stage CfChargePos* in CfChargePos* out) o2_gpu_add_kernel("GPUTPCCFDecodeZS" "= TPCCLUSTERFINDER" LB int32_t firstHBF) o2_gpu_add_kernel("GPUTPCCFDecodeZSLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) -o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS" LB int32_t firstHBF) +o2_gpu_add_kernel("GPUTPCCFDecodeZSDenseLink" "GPUTPCCFDecodeZS ERRORS" LB int32_t firstHBF) o2_gpu_add_kernel("GPUTPCCFGather" "=" LB o2::tpc::ClusterNative* dest) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode0asGPU" "= GLOBALREFIT " LB) o2_gpu_add_kernel("GPUTrackingRefitKernel, mode1asTrackParCov" "= GLOBALREFIT " LB) From f9d6270171d3fb299837bac4a507581ad53ada14 Mon Sep 17 00:00:00 2001 From: Hadi Hassan Date: Wed, 22 Oct 2025 18:05:19 +0900 Subject: [PATCH 95/99] Adding cuts for air (#14755) --- Detectors/FOCAL/simulation/data/simcuts.dat | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Detectors/FOCAL/simulation/data/simcuts.dat b/Detectors/FOCAL/simulation/data/simcuts.dat index 744f67c3c81f4..870e38182f01c 100644 --- a/Detectors/FOCAL/simulation/data/simcuts.dat +++ b/Detectors/FOCAL/simulation/data/simcuts.dat @@ -14,3 +14,5 @@ FOC 3 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. - FOC 6 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 * Aluminium FOC 11 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Air +FOC 13 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 From d137552a437a2d0f93bdc0c2736a519b9b2112af Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Wed, 22 Oct 2025 21:07:22 +0200 Subject: [PATCH 96/99] ALICE3-TRK: adding macro to check digitization output (#14736) * ALICE3-TRK: adding macro to check digitization output * Fixing build issues --- Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt | 1 + .../TRK/base/include/TRKBase/GeometryTGeo.h | 4 + .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 2 + .../ALICE3/TRK/base/src/TRKBaseLinkDef.h | 4 +- .../Upgrades/ALICE3/TRK/macros/CMakeLists.txt | 12 + .../ALICE3/TRK/macros/test/CMakeLists.txt | 21 + .../ALICE3/TRK/macros/test/CheckDigits.C | 387 ++++++++++++++++++ .../ALICE3/TRK/macros/test/run_test.sh | 10 + 8 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh diff --git a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt index 645e3149e4ab7..e623239122658 100644 --- a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -10,5 +10,6 @@ # or submit itself to any jurisdiction. add_subdirectory(base) +add_subdirectory(macros) add_subdirectory(simulation) add_subdirectory(workflow) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index 8f494c5d41991..0e9ff8727a977 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -14,6 +14,7 @@ #include #include +#include "DetectorsCommonDataFormats/DetID.h" namespace o2 { @@ -128,10 +129,13 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache TString getMatrixPath(int index) const; +#ifdef ENABLE_UPGRADES static const char* composeSymNameTRK(int d) { return Form("%s_%d", o2::detectors::DetID(o2::detectors::DetID::TRK).getName(), d); } +#endif + static const char* composeSymNameLayer(int d, int layer); static const char* composeSymNameStave(int d, int layer); static const char* composeSymNameChip(int d, int lr); diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 52ce26ab79e6d..9325f5079375d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -369,10 +369,12 @@ void GeometryTGeo::fillMatrixCache(int mask) //__________________________________________________________________________ +#ifdef ENABLE_UPGRADES const char* GeometryTGeo::composeSymNameLayer(int d, int lr) { return Form("%s/%s%d", composeSymNameTRK(d), getTRKLayerPattern(), lr); } +#endif const char* GeometryTGeo::composeSymNameStave(int d, int lr) { diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h index f29dcd302537d..eee9a23eaf5e7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h @@ -15,8 +15,10 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::TRKBaseParam> + ; + #pragma link C++ class o2::trk::GeometryTGeo + #pragma link C++ class o2::trk::TRKBaseParam + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::TRKBaseParam> + ; +#pragma link C++ class o2::trk::SegmentationChip + ; #endif \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt new file mode 100644 index 0000000000000..9a2194afd3999 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/CMakeLists.txt @@ -0,0 +1,12 @@ +# 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. + +add_subdirectory(test) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt new file mode 100644 index 0000000000000..379207eb07481 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -0,0 +1,21 @@ +# 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. + +o2_add_test_root_macro(CheckDigits.C + PUBLIC_LINK_LIBRARIES O2::ITSMFTBase + O2::ITSMFTSimulation + O2::TRKBase + O2::TRKSimulation + O2::MathUtils + O2::SimulationDataFormat + O2::DetectorsBase + O2::Steer + LABELS trk COMPILE_ONLY) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C new file mode 100644 index 0000000000000..5d60592a96f41 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C @@ -0,0 +1,387 @@ +// 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 CheckDigits.C +/// \brief Simple macro to check TRK digits + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include + +#include "TRKBase/SegmentationChip.h" +#include "TRKBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/Digit.h" +#include "ITSMFTSimulation/Hit.h" +#include "MathUtils/Utils.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DetectorsBase/GeometryManager.h" + +#include "DataFormatsITSMFT/ROFRecord.h" + +#endif + +#define ENABLE_UPGRADES + +void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = "o2sim_HitsTRK.root", std::string inputGeom = "", std::string paramfile = "o2sim_par.root") +{ + + using namespace o2::base; + using namespace o2::trk; + + using o2::itsmft::Digit; + using o2::itsmft::Hit; + + using o2::trk::SegmentationChip; + + TFile* f = TFile::Open("CheckDigits.root", "recreate"); + + TNtuple* nt = new TNtuple("ntd", "digit ntuple", "id:x:y:z:rowD:colD:rowH:colH:xlH:zlH:xlcH:zlcH:dx:dz"); + TNtuple* nt2 = new TNtuple("ntd2", "digit ntuple", "id:z:dxH:dzH"); /// maximum number of elements in a tuple = 15: doing a new tuple to store more variables + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto* gman = o2::trk::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + SegmentationChip seg; + // seg.Print(); + + // Hits + TFile* hitFile = TFile::Open(hitfile.data()); + TTree* hitTree = (TTree*)hitFile->Get("o2sim"); + int nevH = hitTree->GetEntries(); // hits are stored as one event per entry + std::vector*> hitArray(nevH, nullptr); + + std::vector> mc2hitVec(nevH); + + // Digits + TFile* digFile = TFile::Open(digifile.data()); + TTree* digTree = (TTree*)digFile->Get("o2sim"); + + std::vector* digArr = nullptr; + digTree->SetBranchAddress("TRKDigit", &digArr); + + o2::dataformats::IOMCTruthContainerView* plabels = nullptr; + digTree->SetBranchAddress("TRKDigitMCTruth", &plabels); + + // Get Read Out Frame arrays + std::vector* ROFRecordArrray = nullptr; + digTree->SetBranchAddress("TRKDigitROF", &ROFRecordArrray); + std::vector& ROFRecordArrrayRef = *ROFRecordArrray; + + std::vector* MC2ROFRecordArrray = nullptr; + digTree->SetBranchAddress("TRKDigitMC2ROF", &MC2ROFRecordArrray); + std::vector& MC2ROFRecordArrrayRef = *MC2ROFRecordArrray; + + digTree->GetEntry(0); + + int nROFRec = (int)ROFRecordArrrayRef.size(); + std::vector mcEvMin(nROFRec, hitTree->GetEntries()); + std::vector mcEvMax(nROFRec, -1); + o2::dataformats::ConstMCTruthContainer labels; + plabels->copyandflatten(labels); + delete plabels; + + // >> build min and max MC events used by each ROF + for (int imc = MC2ROFRecordArrrayRef.size(); imc--;) { + const auto& mc2rof = MC2ROFRecordArrrayRef[imc]; + // printf("MCRecord: "); + // mc2rof.print(); + + if (mc2rof.rofRecordID < 0) { + continue; // this MC event did not contribute to any ROF + } + + for (int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + + int irof = mc2rof.rofRecordID + irfd; + + if (irof >= nROFRec) { + LOG(error) << "ROF=" << irof << " from MC2ROF record is >= N ROFs=" << nROFRec; + } + if (mcEvMin[irof] > imc) { + mcEvMin[irof] = imc; + } + if (mcEvMax[irof] < imc) { + mcEvMax[irof] = imc; + } + } + } // << build min and max MC events used by each ROF + + unsigned int rofIndex = 0; + unsigned int rofNEntries = 0; + + // LOOP on : ROFRecord array + for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { + + rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); + rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); + + // >> read and map MC events contributing to this ROF + for (int im = mcEvMin[iROF]; im <= mcEvMax[iROF]; im++) { + + if (!hitArray[im]) { + + hitTree->SetBranchAddress("TRKHit", &hitArray[im]); + hitTree->GetEntry(im); + + auto& mc2hit = mc2hitVec[im]; + + for (int ih = hitArray[im]->size(); ih--;) { + + const auto& hit = (*hitArray[im])[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } + } + } + + // LOOP on : digits array + for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { + // if (iDigit % 10000 != 0) /// looking only at a small sample + // continue; + + if (iDigit % 1000 == 0) + std::cout << "Reading digit " << iDigit << " / " << digArr->size() << std::endl; + + Int_t ix = (*digArr)[iDigit].getRow(), iz = (*digArr)[iDigit].getColumn(); + Int_t iDetID = (*digArr)[iDigit].getChipIndex(); + Int_t layer = gman->getLayer(iDetID); + Int_t disk = gman->getDisk(iDetID); + Int_t subDetID = gman->getSubDetID(iDetID); + Int_t petalCase = gman->getPetalCase(iDetID); + Int_t stave = gman->getStave(iDetID); + Int_t halfstave = gman->getHalfStave(iDetID); + + Float_t x = 0.f, y = 0.f, z = 0.f; + Float_t x_flat = 0.f, z_flat = 0.f; + + if (disk != -1) { + continue; // skip disks for the moment + } + + if (subDetID != 0) { + seg.detectorToLocal(ix, iz, x, z, subDetID, layer, disk); + } else if (subDetID == 0) { + seg.detectorToLocal(ix, iz, x_flat, z_flat, subDetID, layer, disk); + o2::math_utils::Vector2D xyCurved = seg.flatToCurved(layer, x_flat, 0.); + x = xyCurved.X(); + y = xyCurved.Y(); + z = z_flat; + } + + o2::math_utils::Point3D locD(x, y, z); // local Digit curved + o2::math_utils::Point3D locDF(-1, -1, -1); // local Digit flat + + Int_t chipID = (*digArr)[iDigit].getChipIndex(); + auto lab = (labels.getLabels(iDigit))[0]; + + int trID = lab.getTrackID(); + + if (!lab.isValid()) { // not a noise + continue; + } + + const auto gloD = gman->getMatrixL2G(chipID)(locD); // convert to global + + std::unordered_map* mc2hit = &mc2hitVec[lab.getEventID()]; + + // get MC info + uint64_t key = (uint64_t(trID) << 32) + chipID; + auto hitEntry = mc2hit->find(key); + + if (hitEntry == mc2hit->end()) { + + LOG(error) << "Failed to find MC hit entry for Tr" << trID << " chipID" << chipID; + continue; + } + + ////// HITS + Hit& hit = (*hitArray[lab.getEventID()])[hitEntry->second]; + + auto xyzLocE = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local + auto xyzLocS = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); + + o2::math_utils::Vector3D locH; /// Hit, average between start and end pos + locH.SetCoordinates(0.5f * (xyzLocE.X() + xyzLocS.X()), 0.5f * (xyzLocE.Y() + xyzLocS.Y()), 0.5f * (xyzLocE.Z() + xyzLocS.Z())); + o2::math_utils::Vector3D locHS; /// Hit, start pos + locHS.SetCoordinates(xyzLocS.X(), xyzLocS.Y(), xyzLocS.Z()); + o2::math_utils::Vector3D locHE; /// Hit, end pos + locHE.SetCoordinates(xyzLocE.X(), xyzLocE.Y(), xyzLocE.Z()); + o2::math_utils::Vector3D locHF; + + int row = 0, col = 0; + float xlc = 0., zlc = 0.; + + if (subDetID == 0) { + Float_t x_flat = 0.f, y_flat = 0.f; + o2::math_utils::Vector2D xyFlatH = seg.curvedToFlat(layer, locH.X(), locH.Y()); + o2::math_utils::Vector2D xyFlatD = seg.curvedToFlat(layer, locD.X(), locD.Y()); + locDF.SetCoordinates(xyFlatD.X(), xyFlatD.Y(), locD.Z()); + locHF.SetCoordinates(xyFlatH.X(), xyFlatH.Y(), locH.Z()); + seg.localToDetector(locHF.X(), locHF.Z(), row, col, subDetID, layer, disk); + } + + else { + seg.localToDetector(locH.X(), locH.Z(), row, col, subDetID, layer, disk); + } + + seg.detectorToLocal(row, col, xlc, zlc, subDetID, layer, disk); + + if (subDetID == 0) { + nt->Fill(chipID, /// detector ID + gloD.X(), gloD.Y(), gloD.Z(), /// global position retrieved from the digit: digit (row, col) ->local position -> global potision + ix, iz, /// row and column of the digit + row, col, /// row and col retrieved from the hit: hit global position -> hit local position -> detector position (row, col) + locH.X(), locH.Z(), /// x and z of the hit in the local reference frame: hit global position -> hit local position + xlc, zlc, /// x and z of the hit in the local frame: hit global position -> hit local position -> detector position (row, col) -> local position + locHF.X() - locDF.X(), locHF.Z() - locDF.Z()); /// difference in x and z between the hit and the digit in the local frame + + nt2->Fill(chipID, gloD.Z(), locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// differences between local hit start and hit end positions + } else { + + nt->Fill(chipID, /// detector ID + gloD.X(), gloD.Y(), gloD.Z(), /// global position retrieved from the digit: digit (row, col) ->local position -> global potision + ix, iz, /// row and column of the digit + row, col, /// row and col retrieved from the hit: hit global position -> hit local position -> detector position (row, col) + locH.X(), locH.Z(), /// x and z of the hit in the local reference frame: hit global position -> hit local position + xlc, zlc, /// x and z of the hit in the local frame: hit global position -> hit local position -> detector position (row, col) -> local position + locH.X() - locD.X(), locH.Z() - locD.Z()); /// difference in x and z between the hit and the digit in the local frame + // locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// difference in x and z between the hit and the digit in the local frame + nt2->Fill(chipID, gloD.Z(), locHS.X() - locHE.X(), locHS.Z() - locHE.Z()); /// differences between local hit start and hit end positions + } + + } // end loop on digits array + + } // end loop on ROFRecords array + + // digit maps in the xy and yz planes + auto canvXY = new TCanvas("canvXY", "", 1600, 2400); + canvXY->Divide(2, 3); + canvXY->cd(1); + nt->Draw("y:x >>h_y_vs_x_VD(1000, -3, 3, 1000, -3, 3)", "id < 36 ", "colz"); + canvXY->cd(2); + nt->Draw("y:z>>h_y_vs_z_VD(1000, -26, 26, 1000, -3, 3)", "id < 36 ", "colz"); + canvXY->cd(3); + nt->Draw("y:x>>h_y_vs_x_ML(1000, -25, 25, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + canvXY->cd(4); + nt->Draw("y:z>>h_y_vs_z_ML(1000, -70, 70, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + canvXY->cd(5); + nt->Draw("y:x>>h_y_vs_x_OT(1000, -85, 85, 1000, -85, 85)", "id >= 106 ", "colz"); + canvXY->cd(6); + nt->Draw("y:z>>h_y_vs_z_OT(1000, -85, 85, 1000, -130, 130)", "id >= 106 ", "colz"); + canvXY->SaveAs("trkdigits_y_vs_x_vs_z.pdf"); + + // z distributions + auto canvZ = new TCanvas("canvZ", "", 800, 2400); + canvZ->Divide(1, 3); + canvZ->cd(1); + nt->Draw("z>>h_z_VD(500, -26, 26)", "id < 36 "); + canvZ->cd(2); + nt->Draw("z>>h_z_ML(500, -70, 70)", "id >= 36 && id < 106 "); + canvZ->cd(3); + nt->Draw("z>>h_z_OT(500, -85, 85)", "id >= 106 "); + canvZ->SaveAs("trkdigits_z.pdf"); + + // dz distributions (difference between local position of digits and hits in x and z) + auto canvdZ = new TCanvas("canvdZ", "", 800, 2400); + canvdZ->Divide(1, 3); + canvdZ->cd(1); + nt->Draw("dz>>h_dz_VD(500, -0.05, 0.05)", "id < 36 "); + canvdZ->cd(2); + nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 36 && id < 106 "); + canvdZ->cd(3); + nt->Draw("dz>>h_dz_OT(500, -0.05, 0.05)", "id >= 106 "); + canvdZ->SaveAs("trkdigits_dz.pdf"); + + // distributions of differences between local positions of digits and hits in x and z + auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 2400); + canvdXdZ->Divide(2, 3); + canvdXdZ->cd(1); + nt->Draw("dx:dz>>h_dx_vs_dz_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36", "colz"); + auto h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_VD"); + LOG(info) << "dx, dz"; + Info("VD", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(2); + nt->Draw("dx:dz>>h_dx_vs_dz_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_VD_z"); + Info("VD |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(3); + nt->Draw("dx:dz>>h_dx_vs_dz_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_ML"); + Info("ML", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(4); + nt->Draw("dx:dz>>h_dx_vs_dz_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_ML_z"); + Info("ML |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->SaveAs("trkdigits_dx_vs_dz.pdf"); + canvdXdZ->cd(5); + nt->Draw("dx:dz>>h_dx_vs_dz_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OT"); + Info("OT", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(6); + nt->Draw("dx:dz>>h_dx_vs_dz_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OT_z"); + Info("OT |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->SaveAs("trkdigits_dx_vs_dz.pdf"); + + // distribution of differences between hit start and hit end in local coordinates + auto canvdXdZHit = new TCanvas("canvdXdZHit", "", 1600, 2400); + canvdXdZHit->Divide(2, 3); + canvdXdZHit->cd(1); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36", "colz"); + LOG(info) << "dxH, dzH"; + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_VD"); + Info("VD", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(2); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 36 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_VD_z"); + Info("VD |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(3); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_ML"); + Info("ML", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(4); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 36 && id < 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_ML_z"); + Info("ML |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("ML |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->SaveAs("trkdigits_dxH_vs_dzH.pdf"); + canvdXdZHit->cd(5); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_OT"); + Info("OT", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->cd(6); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 106 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dxH_vs_dzH_OT_z"); + Info("OT |z|<2", "RMS(dxH)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OT |z|<2", "RMS(dzH)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZHit->SaveAs("trkdigits_dxH_vs_dzH.pdf"); + + f->Write(); + f->Close(); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh new file mode 100644 index 0000000000000..797d1d12af4ab --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh @@ -0,0 +1,10 @@ +#Number of events to simulate +nEvents=10 + +# Simulating +o2-sim-serial-run5 -n $nEvents -g pythia8hi -m TRK --configKeyValues "TRKBase.layoutML=kTurboStaves;TRKBase.layoutOL=kStaggered;">& sim_TRK.log + +# Digitizing +o2-sim-digitizer-workflow -b >& digiTRK.log + +root.exe -b -q CheckDigits.C+ >& CheckDigits.log From ddba9e0746cf9d24262210ceabf216fa84ec8e60 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 22 Oct 2025 15:49:01 +0200 Subject: [PATCH 97/99] Redefing BasicXYZVHit::mDetectorID from short to ushort --- .../simulation/include/SimulationDataFormat/BaseHits.h | 8 ++++---- Detectors/ITSMFT/common/simulation/src/Digitizer.cxx | 2 +- Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h index b9ed356ec8b5a..b527de341dae6 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h +++ b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h @@ -55,7 +55,7 @@ class BasicXYZVHit : public BaseHit BasicXYZVHit() = default; // for ROOT IO // constructor - BasicXYZVHit(T x, T y, T z, E time, V val, int trackid, short did) + BasicXYZVHit(T x, T y, T z, E time, V val, int trackid, unsigned short did) : mPos(x, y, z), mTime(time), mHitValue(val), BaseHit(trackid), mDetectorID(did) { } @@ -70,12 +70,12 @@ class BasicXYZVHit : public BaseHit // getting the time E GetTime() const { return mTime; } // get detector + track information - short GetDetectorID() const { return mDetectorID; } + unsigned short GetDetectorID() const { return mDetectorID; } // modifiers void SetTime(E time) { mTime = time; } void SetHitValue(V val) { mHitValue = val; } - void SetDetectorID(short detID) { mDetectorID = detID; } + void SetDetectorID(unsigned short detID) { mDetectorID = detID; } void SetX(T x) { mPos.SetX(x); } void SetY(T y) { mPos.SetY(y); } void SetZ(T z) { mPos.SetZ(z); } @@ -87,7 +87,7 @@ class BasicXYZVHit : public BaseHit } void SetPos(math_utils::Point3D const& p) { mPos = p; } - ClassDefNV(BasicXYZVHit, 1); + ClassDefNV(BasicXYZVHit, 2); }; // Class for a hit containing energy loss as hit value diff --git a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx index 382fa769a94c7..e5dd35e6a084d 100644 --- a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx +++ b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx @@ -272,7 +272,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID) { // convert single hit to digits - int chipID = hit.GetDetectorID(); + auto chipID = hit.GetDetectorID(); auto& chip = mChips[chipID]; if (chip.isDisabled()) { LOG(debug) << "skip disabled chip " << chipID; diff --git a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx index b91e17890a6d8..7dd7110801f4a 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx @@ -234,7 +234,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID) { // convert single hit to digits - int chipID = hit.GetDetectorID(); + auto chipID = hit.GetDetectorID(); auto& chip = mChips[chipID]; if (chip.isDisabled()) { return; From 8a3ddd154d67f20a498ddfd464053b976b9722dd Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Thu, 23 Oct 2025 10:47:28 +0200 Subject: [PATCH 98/99] clang-format --- Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx | 2 +- .../Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index e69ce9d1b4c34..51eea905c436a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -175,7 +175,7 @@ void TRKServices::registerVacuum(TGeoVolume* motherVolume) // Add the vacuum to the barrel vacuumVolume->SetLineColor(kAzure + 7); vacuumVolume->SetTransparency(80); - + motherVolume->AddNode(vacuumVolume, 1, new TGeoTranslation(0, 0, 0)); } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index af813132ff6cb..54567a50fa4cf 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -208,7 +208,7 @@ inline void buildIrisCutoutFromPetalSolid(int nPetals) } // namespace // =================== Specs & constants (ROOT units: cm) =================== -static constexpr double kX2X0 = 0.001f; // 0.1% X0 per layer +static constexpr double kX2X0 = 0.001f; // 0.1% X0 per layer static constexpr double kLenZ_cm = 50.0f; // L0/L1/L2 Z length // Radii (cm) From 5396e1636325459e1a85cdbd3601fc325da00a83 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Thu, 23 Oct 2025 08:52:25 +0000 Subject: [PATCH 99/99] Please consider the following formatting changes --- Framework/Core/src/ComputingQuotaEvaluator.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Framework/Core/src/ComputingQuotaEvaluator.cxx b/Framework/Core/src/ComputingQuotaEvaluator.cxx index 717a59f5f5372..5e2620f273e11 100644 --- a/Framework/Core/src/ComputingQuotaEvaluator.cxx +++ b/Framework/Core/src/ComputingQuotaEvaluator.cxx @@ -97,7 +97,7 @@ bool ComputingQuotaEvaluator::selectOffer(int task, ComputingQuotaRequest const& result.size(), totalOffer.cpu, totalOffer.memory, totalOffer.sharedMemory); for (auto& offer : result) { // We pretend each offer id is a pointer, to have a unique id. - O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(offer*8)); + O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(offer * 8)); O2_SIGNPOST_START(quota, oid, "offers", "Offer %d has been selected.", offer); } dpStats.updateStats({static_cast(ProcessingStatsId::RESOURCES_SATISFACTORY), DataProcessingStats::Op::Add, 1}); @@ -224,7 +224,7 @@ void ComputingQuotaEvaluator::dispose(int taskId) continue; } if (offer.sharedMemory <= 0) { - O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(oi*8)); + O2_SIGNPOST_ID_FROM_POINTER(oid, quota, (void*)(int64_t)(oi * 8)); O2_SIGNPOST_END(quota, oid, "offers", "Offer %d back to not needed.", oi); offer.valid = false; offer.score = OfferScore::Unneeded; @@ -269,7 +269,7 @@ void ComputingQuotaEvaluator::handleExpired(std::function