From 7fec663084b2296ac31af680013cd0e1c973005c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 18 Dec 2025 12:38:40 +0100 Subject: [PATCH 1/2] Use A3 geo provider in OTF --- ALICE3/Core/DelphesO2TrackSmearer.cxx | 1 + ALICE3/Core/FastTracker.cxx | 181 +++++++------- ALICE3/Core/FastTracker.h | 73 +++++- ALICE3/Core/FastTrackerLinkDef.h | 1 + ALICE3/Macros/CMakeLists.txt | 2 +- ALICE3/Macros/Configuration/a3geometry_v3.ini | 157 ++++++++++++ ALICE3/TableProducer/OTF/CMakeLists.txt | 13 +- .../OTF/onTheFlyDetectorGeometryProvider.cxx | 111 +++++++++ ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx | 108 +++++---- ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx | 129 ++++++---- ALICE3/TableProducer/OTF/onTheFlyTracker.cxx | 223 ++++++++---------- .../TableProducer/OTF/onTheFlyTrackerPid.cxx | 21 +- ALICE3/Tasks/alice3SeparationPower.cxx | 26 +- 13 files changed, 709 insertions(+), 337 deletions(-) create mode 100644 ALICE3/Macros/Configuration/a3geometry_v3.ini create mode 100644 ALICE3/TableProducer/OTF/onTheFlyDetectorGeometryProvider.cxx diff --git a/ALICE3/Core/DelphesO2TrackSmearer.cxx b/ALICE3/Core/DelphesO2TrackSmearer.cxx index 6a0b20a8199..7eacb473d74 100644 --- a/ALICE3/Core/DelphesO2TrackSmearer.cxx +++ b/ALICE3/Core/DelphesO2TrackSmearer.cxx @@ -66,6 +66,7 @@ bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) std::string path = std::string(filename).substr(5); // Remove "ccdb:" prefix const std::string outPath = "/tmp/LUTs/"; filename = Form("%s/%s/snapshot.root", outPath.c_str(), path.c_str()); + LOG(info) << " --- Local LUT filename will be: " << filename; std::ifstream checkFile(filename); // Check if file already exists if (!checkFile.is_open()) { // File does not exist, retrieve from CCDB LOG(info) << " --- CCDB source detected for PDG " << pdg << ": " << path; diff --git a/ALICE3/Core/FastTracker.cxx b/ALICE3/Core/FastTracker.cxx index 50a59c92fb8..41c6637d59b 100644 --- a/ALICE3/Core/FastTracker.cxx +++ b/ALICE3/Core/FastTracker.cxx @@ -11,15 +11,18 @@ #include "FastTracker.h" -#include "ReconstructionDataFormats/TrackParametrization.h" +#include "Common/Core/TableHelper.h" + +#include -#include "TMath.h" -#include "TMatrixD.h" -#include "TMatrixDSymEigen.h" -#include "TRandom.h" #include #include +#include +#include +#include #include +#include +#include #include #include @@ -31,6 +34,81 @@ namespace o2 namespace fastsim { +std::map> GeometryContainer::parseTEnvConfiguration(std::string filename, std::vector& layers) +{ + std::map> configMap; + filename = gSystem->ExpandPathName(filename.c_str()); + TEnv env(filename.c_str()); + THashList* table = env.GetTable(); + layers.clear(); + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + // key should contain exactly one dot + if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { + LOG(fatal) << "Key " << key << " does not contain exactly one dot"; + continue; + } + const std::string firstPart = key.substr(0, key.find('.')); + if (std::find(layers.begin(), layers.end(), firstPart) == layers.end()) { + layers.push_back(firstPart); + } + } + env.Print(); + // Layers + for (const auto& layer : layers) { + LOG(info) << " Reading layer " << layer; + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + if (key.find(layer + ".") == 0) { + const std::string paramName = key.substr(key.find('.') + 1); + const std::string value = env.GetValue(key.c_str(), ""); + configMap[layer][paramName] = value; + } + } + } + return configMap; +} + +void GeometryContainer::init(o2::framework::InitContext& initContext) +{ + std::vector detectorConfiguration; + const bool found = common::core::getTaskOptionValue(initContext, "on-the-fly-detector-geometry-provider", "detectorConfiguration", detectorConfiguration, false); + if (!found) { + LOG(fatal) << "Could not retrieve detector configuration from OnTheFlyDetectorGeometryProvider task."; + return; + } + LOG(info) << "Size of detector configuration: " << detectorConfiguration.size(); + for (const auto& configFile : detectorConfiguration) { + LOG(info) << "Detector geometry configuration file used: " << configFile; + addEntry(configFile); + } +} + +std::map GeometryContainer::GeometryEntry::getConfiguration(const std::string& layerName) const +{ + auto it = mConfigurations.find(layerName); + if (it != mConfigurations.end()) { + return it->second; + } else { + LOG(fatal) << "Layer " << layerName << " not found in geometry configurations."; + return {}; + } +} + +std::string GeometryContainer::GeometryEntry::getValue(const std::string& layerName, const std::string& key, bool require) const +{ + auto layer = getConfiguration(layerName); + auto entry = layer.find(key); + if (entry != layer.end()) { + return layer.at(key); + } else if (require) { + LOG(fatal) << "Key " << key << " not found in layer " << layerName << " configurations."; + return ""; + } else { + return ""; + } +} + // +-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+ DetLayer* FastTracker::AddLayer(TString name, float r, float z, float x0, float xrho, float resRPhi, float resZ, float eff, int type) @@ -236,88 +314,31 @@ void FastTracker::AddTPC(float phiResMean, float zResMean) } } -std::map> FastTracker::parseTEnvConfiguration(std::string filename) +void FastTracker::AddGenericDetector(GeometryContainer::GeometryEntry configMap, o2::ccdb::BasicCCDBManager* ccdbManager) { - std::map> configMap; - - TEnv env(filename.c_str()); - THashList* table = env.GetTable(); - std::vector layers; - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - // key should contain exactly one dot - if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { - LOG(fatal) << "Key " << key << " does not contain exactly one dot"; + // Layers + for (const auto& layer : configMap.getLayerNames()) { + if (layer.find("global") != std::string::npos) { // Layers with global tag are skipped + LOG(info) << " Skipping global configuration entry " << layer; continue; } - const std::string firstPart = key.substr(0, key.find('.')); - if (std::find(layers.begin(), layers.end(), firstPart) == layers.end()) { - layers.push_back(firstPart); - } - } - env.Print(); - // Layers - for (const auto& layer : layers) { LOG(info) << " Reading layer " << layer; - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - if (key.find(layer + ".") == 0) { - const std::string paramName = key.substr(key.find('.') + 1); - const std::string value = env.GetValue(key.c_str(), ""); - configMap[layer][paramName] = value; - } - } - } - return configMap; -} - -void FastTracker::AddGenericDetector(std::string filename, o2::ccdb::BasicCCDBManager* ccdbManager) -{ - LOG(info) << " Adding generic detector from file " << filename; - // If the filename starts with ccdb: then take the file from the ccdb - if (filename.rfind("ccdb:", 0) == 0) { - std::string ccdbPath = filename.substr(5); // remove "ccdb:" prefix - if (ccdbManager == nullptr) { - LOG(fatal) << "CCDB manager is null, cannot retrieve file " << ccdbPath; - return; - } - const std::string outPath = "/tmp/DetGeo/"; - filename = Form("%s/%s/snapshot.root", outPath.c_str(), ccdbPath.c_str()); - std::ifstream checkFile(filename); // Check if file already exists - if (!checkFile.is_open()) { // File does not exist, retrieve from CCDB - LOG(info) << " --- CCDB source detected for detector geometry " << filename; - std::map metadata; - ccdbManager->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); - // Add CCDB handling logic here if needed - LOG(info) << " --- Now retrieving geometry configuration from CCDB to: " << filename; - } else { // File exists, proceed to load - LOG(info) << " --- Geometry configuration file already exists: " << filename << ". Skipping download."; - checkFile.close(); - } - AddGenericDetector(filename, nullptr); - return; - } - - std::map> configMap = parseTEnvConfiguration(filename); - // Layers - for (const auto& layer : configMap) { - LOG(info) << " Reading layer " << layer.first; - const float r = std::stof(layer.second.at("r")); - LOG(info) << " Layer " << layer.first << " has radius " << r; - const float z = std::stof(layer.second.at("z")); - const float x0 = std::stof(layer.second.at("x0")); - const float xrho = std::stof(layer.second.at("xrho")); - const float resRPhi = std::stof(layer.second.at("resRPhi")); - const float resZ = std::stof(layer.second.at("resZ")); - const float eff = std::stof(layer.second.at("eff")); - const int type = std::stoi(layer.second.at("type")); - const std::string deadPhiRegions = layer.second.at("deadPhiRegions"); + const float r = configMap.getFloatValue(layer, "r"); + LOG(info) << " Layer " << layer << " has radius " << r; + const float z = configMap.getFloatValue(layer, "z"); + const float x0 = configMap.getFloatValue(layer, "x0"); + const float xrho = configMap.getFloatValue(layer, "xrho"); + const float resRPhi = configMap.getFloatValue(layer, "resRPhi"); + const float resZ = configMap.getFloatValue(layer, "resZ"); + const float eff = configMap.getFloatValue(layer, "eff"); + const int type = configMap.getIntValue(layer, "type"); + const std::string deadPhiRegions = configMap.getValue(layer, "deadPhiRegions", false); // void AddLayer(TString name, float r, float z, float x0, float xrho, float resRPhi = 0.0f, float resZ = 0.0f, float eff = 0.0f, int type = 0); - LOG(info) << " Adding layer " << layer.first << " r=" << r << " z=" << z << " x0=" << x0 << " xrho=" << xrho << " resRPhi=" << resRPhi << " resZ=" << resZ << " eff=" << eff << " type=" << type << " deadPhiRegions=" << deadPhiRegions; + LOG(info) << " Adding layer " << layer << " r=" << r << " z=" << z << " x0=" << x0 << " xrho=" << xrho << " resRPhi=" << resRPhi << " resZ=" << resZ << " eff=" << eff << " type=" << type << " deadPhiRegions=" << deadPhiRegions; - DetLayer* addedLayer = AddLayer(layer.first.c_str(), r, z, x0, xrho, resRPhi, resZ, eff, type); + DetLayer* addedLayer = AddLayer(layer.c_str(), r, z, x0, xrho, resRPhi, resZ, eff, type); if (!deadPhiRegions.empty()) { // Taking it as ccdb path or local file // Check if it begins with ccdb: if (std::string(deadPhiRegions).rfind("ccdb:", 0) == 0) { @@ -340,7 +361,7 @@ void FastTracker::AddGenericDetector(std::string filename, o2::ccdb::BasicCCDBMa addedLayer->setDeadPhiRegions(g); } } else { - LOG(debug) << " No dead phi regions for layer " << layer.first; + LOG(debug) << " No dead phi regions for layer " << layer; } } } diff --git a/ALICE3/Core/FastTracker.h b/ALICE3/Core/FastTracker.h index b104563fa7c..8703269296e 100644 --- a/ALICE3/Core/FastTracker.h +++ b/ALICE3/Core/FastTracker.h @@ -15,10 +15,10 @@ #include "DetLayer.h" #include +#include +#include #include -#include - #include #include #include @@ -28,6 +28,63 @@ namespace o2 namespace fastsim { +class GeometryContainer +{ + public: + GeometryContainer() = default; + virtual ~GeometryContainer() = default; + + void init(o2::framework::InitContext& initContext); + + /** + * @brief Parses a TEnv configuration file and returns the key-value pairs split per entry + * @param filename Path to the TEnv configuration file + * @param layers Vector to store the order of the layers as they appear in the file + * @return A map where each key is a layer name and the value is another map of key-value pairs for that layer + */ + static std::map> parseTEnvConfiguration(std::string filename, std::vector& layers); + + // A container for the geometry info + struct GeometryEntry { + // Default constructor + GeometryEntry() = default; + explicit GeometryEntry(std::string filename) : name(filename) + { + mConfigurations = GeometryContainer::parseTEnvConfiguration(filename, layerNames); + } + std::map> getConfigurations() const { return mConfigurations; } + std::map getConfiguration(const std::string& layerName) const; + std::vector getLayerNames() const { return layerNames; } + std::string getValue(const std::string& layerName, const std::string& key, bool require = true) const; + float getFloatValue(const std::string& layerName, const std::string& key) const { return std::stof(getValue(layerName, key)); } + int getIntValue(const std::string& layerName, const std::string& key) const { return std::stoi(getValue(layerName, key)); } + + private: + std::string name; // Filename of the geometry + std::map> mConfigurations; + std::vector layerNames; // Ordered names of the layers + }; + + // Add a geometry entry from a configuration file + void addEntry(const std::string& filename) { entries.emplace_back(filename); } + + // Getters + int getNumberOfConfigurations() const { return entries.size(); } + const std::vector& getEntries() const { return entries; } + const GeometryEntry& getEntry(const int id) const { return entries.at(id); } + GeometryEntry getGeometryEntry(const int id) const { return entries.at(id); } + + // Get configuration maps + std::map> getConfigurations(const int id) const { return entries.at(id).getConfigurations(); } + std::map getConfiguration(const int id, const std::string& layerName) const { return entries.at(id).getConfiguration(layerName); } + + // Get specific values + float getFloatValue(const int id, const std::string& layerName, const std::string& key) const { return entries.at(id).getFloatValue(layerName, key); } + + private: + std::vector entries; +}; + // +-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+ // this class implements a synthetic smearer that allows @@ -70,13 +127,6 @@ class FastTracker void AddSiliconALICE3(float scaleX0VD, std::vector pixelResolution); void AddTPC(float phiResMean, float zResMean); - /** - * @brief Parses a TEnv configuration file and returns the key-value pairs split per entry - * @param filename Path to the TEnv configuration file - * @return A map where each key is a layer name and the value is another map of key-value pairs for that layer - */ - std::map> parseTEnvConfiguration(std::string filename); - /** * @brief Adds a generic detector configuration from the specified file. * @@ -84,10 +134,9 @@ class FastTracker * using the provided filename. The file should contain the necessary parameters * and settings for the detector to be added. * - * @param filename Path to the configuration file describing the detector. - * @param ccdbManager Pointer to a BasicCCDBManager instance for database access (if needed). + * @param configMap Configuration map describing the detector. */ - void AddGenericDetector(std::string filename, o2::ccdb::BasicCCDBManager* ccdbManager = nullptr); + void AddGenericDetector(GeometryContainer::GeometryEntry configMap, o2::ccdb::BasicCCDBManager* ccdbManager = nullptr); void Print(); diff --git a/ALICE3/Core/FastTrackerLinkDef.h b/ALICE3/Core/FastTrackerLinkDef.h index a5441d81cde..4986a1879f7 100644 --- a/ALICE3/Core/FastTrackerLinkDef.h +++ b/ALICE3/Core/FastTrackerLinkDef.h @@ -16,6 +16,7 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::fastsim::GeometryContainer + ; #pragma link C++ class o2::fastsim::FastTracker + ; #pragma link C++ class o2::fastsim::DelphesO2LutWriter + ; diff --git a/ALICE3/Macros/CMakeLists.txt b/ALICE3/Macros/CMakeLists.txt index 5c81b2bb012..c7690d75e79 100644 --- a/ALICE3/Macros/CMakeLists.txt +++ b/ALICE3/Macros/CMakeLists.txt @@ -11,7 +11,7 @@ install(FILES Configuration/a3geo.ini Configuration/a3geometry_v2.ini + Configuration/a3geometry_v3.ini Configuration/a3geometry_v4.ini PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ DESTINATION share/alice3/) - diff --git a/ALICE3/Macros/Configuration/a3geometry_v3.ini b/ALICE3/Macros/Configuration/a3geometry_v3.ini new file mode 100644 index 00000000000..0b005aa4759 --- /dev/null +++ b/ALICE3/Macros/Configuration/a3geometry_v3.ini @@ -0,0 +1,157 @@ +# Format: +# .: +# Example: +# layer1.r: 0.5 # radius in cm +# layer1.z: 250 # half-length in cm +# layer1.x0: 0.001 # radiation length +# layer1.xrho: 0 # density in g/cm^2 +# layer1.resRPhi: 0.0001 # resolution in R-Phi in cm +# layer1.resZ: 0.0001 # resolution in Z in cm +# layer1.eff: 1.0 # efficiency (0 to 1) +# layer1.type: 1 # type of layer (0: passive, 1: active) +# layer1.deadPhiRegions: /path/to/dead_regions.root # optional dead regions file + +bpipe0.r: 0.48 +bpipe0.z: 250 +bpipe0.x0: 0.001592 +bpipe0.xrho: 0 +bpipe0.resRPhi: 0.0f +bpipe0.resZ: 0.0f +bpipe0.eff: 0.0f +bpipe0.type: 0 + +B00.r: 0.5 +B00.z: 250 +B00.x0: 0.00076 +B00.xrho: 0 +B00.resRPhi: 0.00025 +B00.resZ: 0.00025 +B00.eff: 1.00 +B00.type: 1 + +B01.r 1.2 +B01.z 250 +B01.x0 0.00096 +B01.xrho 0 +B01.resRPhi 0.00025 +B01.resZ 0.00025 +B01.eff 1.00 +B01.type 1 + +B02.r: 2.5 +B02.z: 250 +B02.x0: 0.00167 +B02.xrho: 0 +B02.resRPhi: 0.00025 +B02.resZ: 0.00025 +B02.eff: 1.00 +B02.type: 1 + +coldplate.r: 2.6 +coldplate.z: 250 +coldplate.x0: 0.02f +coldplate.xrho: 0 +coldplate.resRPhi: 0.0f +coldplate.resZ: 0.0f +coldplate.eff: 0.0f +coldplate.type: 0 + +bpipe1.r: 5.7 +bpipe1.z: 250 +bpipe1.x0: 0.0023f +bpipe1.xrho: 0 +bpipe1.resRPhi: 0.0f +bpipe1.resZ: 0.0f +bpipe1.eff: 0.0f +bpipe1.type: 0 + +B03.r: 7. +B03.z: 250 +B03.x0: 0.01 +B03.xrho: 2.3292e-01 +B03.resRPhi: 0.001 +B03.resZ: 0.001 +B03.eff: 1.00 +B03.type: 1 + +B04.r: 9. +B04.z: 250 +B04.x0: 0.01 +B04.xrho: 2.3292e-01 +B04.resRPhi: 0.001 +B04.resZ: 0.001 +B04.eff: 1.00 +B04.type: 1 + +B05.r: 12. +B05.z: 250 +B05.x0: 0.01 +B05.xrho: 2.3292e-01 +B05.resRPhi: 0.001 +B05.resZ: 0.001 +B05.eff: 1.00 +B05.type: 1 + +iTOF.r: 19 +iTOF.z: 250 +iTOF.x0: 0.01*3. +iTOF.xrho: 0.03 +iTOF.resRPhi: 0.001 +iTOF.resZ: 0.001 +iTOF.eff: 1.00 +iTOF.type: 0 + +B06.r: 20. +B06.z: 250 +B06.x0: 0.01 +B06.xrho: 2.3292e-01 +B06.resRPhi: 0.001 +B06.resZ: 0.001 +B06.eff: 1.00 +B06.type: 1 + +B07.r: 30. +B07.z: 250 +B07.x0: 0.01 +B07.xrho: 2.3292e-01 +B07.resRPhi: 0.001 +B07.resZ: 0.001 +B07.eff: 1.00 +B07.type: 1 + +B08.r: 45. +B08.z: 250 +B08.x0: 0.01 +B08.xrho: 2.3292e-01 +B08.resRPhi: 0.001 +B08.resZ: 0.001 +B08.eff: 1.00 +B08.type: 1 + +B09.r: 60. +B09.z: 250 +B09.x0: 0.01 +B09.xrho: 2.3292e-01 +B09.resRPhi: 0.001 +B09.resZ: 0.001 +B09.eff: 1.00 +B09.type: 1 + +B10.r: 80. +B10.z: 250 +B10.x0: 0.01 +B10.xrho: 2.3292e-01 +B10.resRPhi: 0.001 +B10.resZ: 0.001 +B10.eff: 1.00 +B10.type: 1 + +# Lookup tables +global.lutEl: ccdb:/Users/j/jekarlss/LookUpTables/NoEloss/el +global.lutMu: ccdb:/Users/j/jekarlss/LookUpTables/NoEloss/mu +global.lutPi: ccdb:/Users/j/jekarlss/LookUpTables/NoEloss/pi +global.lutKa: ccdb:/Users/j/jekarlss/LookUpTables/NoEloss/ka +global.lutPr: ccdb:/Users/j/jekarlss/LookUpTables/NoEloss/pr + +# in kGauss +global.magneticfield: 20 diff --git a/ALICE3/TableProducer/OTF/CMakeLists.txt b/ALICE3/TableProducer/OTF/CMakeLists.txt index 69a4d2ec722..b75101f977f 100644 --- a/ALICE3/TableProducer/OTF/CMakeLists.txt +++ b/ALICE3/TableProducer/OTF/CMakeLists.txt @@ -11,20 +11,25 @@ o2physics_add_dpl_workflow(onthefly-tracker SOURCES onTheFlyTracker.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing O2::DCAFitter O2Physics::ALICE3Core O2Physics::FastTracker + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing O2::DCAFitter O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(onthefly-tofpid SOURCES onTheFlyTofPid.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(onthefly-richpid SOURCES onTheFlyRichPid.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(on-the-fly-tracker-pid SOURCES onTheFlyTrackerPid.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(on-the-fly-detector-geometry-provider + SOURCES onTheFlyDetectorGeometryProvider.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) diff --git a/ALICE3/TableProducer/OTF/onTheFlyDetectorGeometryProvider.cxx b/ALICE3/TableProducer/OTF/onTheFlyDetectorGeometryProvider.cxx new file mode 100644 index 00000000000..680a79e2dcd --- /dev/null +++ b/ALICE3/TableProducer/OTF/onTheFlyDetectorGeometryProvider.cxx @@ -0,0 +1,111 @@ +// 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 onTheFlyDetectorGeometryProvider.cxx +/// +/// \brief Reader and and provider of the detector geometry for on-the-fly simulation +/// +/// \author Nicolò Jacazio , Universita del Piemonte Orientale (IT) +/// + +#include "ALICE3/Core/FastTracker.h" + +#include +#include +#include + +#include +#include +#include + +struct OnTheFlyDetectorGeometryProvider { + + o2::framework::Configurable> detectorConfiguration{"detectorConfiguration", + std::vector{"$O2PHYSICS_ROOT/share/alice3/a3geometry_v3.ini"}, + "Paths of the detector geometry configuration files"}; + o2::framework::Service ccdb; + void init(o2::framework::InitContext&) + { + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setTimestamp(-1); + } + void run(o2::framework::ProcessingContext& pc) + { + o2::fastsim::GeometryContainer geometryContainer; // Checking that the geometry files can be accessed and loaded + LOG(info) << "On-the-fly detector geometry provider running."; + if (detectorConfiguration.value.empty()) { + LOG(fatal) << "No detector configuration files provided."; + return; + } + int idx = 0; + for (auto& configFile : detectorConfiguration.value) { + LOG(info) << "Loading detector geometry from configuration file: " << configFile; + // If the filename starts with ccdb: then take the file from the ccdb + if (configFile.rfind("ccdb:", 0) == 0) { + std::string ccdbPath = configFile.substr(5); // remove "ccdb:" prefix + const std::string outPath = "/tmp/DetGeo/"; + configFile = Form("%s/%s/snapshot.root", outPath.c_str(), ccdbPath.c_str()); + std::ifstream checkFile(configFile); // Check if file already exists + if (!checkFile.is_open()) { // File does not exist, retrieve from CCDB + LOG(info) << " --- CCDB source detected for detector geometry " << configFile; + std::map metadata; + ccdb->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); + LOG(info) << " --- Now retrieving geometry configuration from CCDB to: " << configFile; + } else { // File exists, proceed to load + LOG(info) << " --- Geometry configuration file already exists: " << configFile << ". Skipping download."; + checkFile.close(); + } + detectorConfiguration.value[idx] = configFile; // Update the filename to the local file + } + geometryContainer.addEntry(configFile); + idx++; + } + + // First we check that the magnetic field is consistent + const int nGeometries = geometryContainer.getNumberOfConfigurations(); + const float mMagneticField = geometryContainer.getFloatValue(0, "global", "magneticfield"); + for (int icfg = 0; icfg < nGeometries; ++icfg) { + const float cfgBfield = geometryContainer.getFloatValue(icfg, "global", "magneticfield"); + if (std::abs(cfgBfield - mMagneticField) > 1e-3) { + LOG(fatal) << "Inconsistent magnetic field values between configurations 0 and " << icfg << ": " << mMagneticField << " vs " << cfgBfield; + } + } + + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(o2::framework::QuitRequest::Me); + } +}; + +// #define VERIFY_ALICE3 +#ifdef VERIFY_ALICE3 +struct OnTheFlyDetectorGeometryUser { + void init(o2::framework::InitContext& initContext) + { + o2::fastsim::GeometryContainer geometryContainer; // Checking that the configuration can be inherited + geometryContainer.init(initContext); + } + void run(o2::framework::ProcessingContext& pc) + { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(o2::framework::QuitRequest::Me); + } +}; +#endif + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + o2::framework::WorkflowSpec spec; + spec.push_back(adaptAnalysisTask(cfgc)); +#ifdef VERIFY_ALICE3 + spec.push_back(adaptAnalysisTask(cfgc)); +#endif + return spec; +} diff --git a/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx index 357e208f95f..4563e9dbe8b 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx @@ -30,11 +30,11 @@ /// \since May 22, 2024 /// -#include "TableHelper.h" - #include "ALICE3/Core/DelphesO2TrackSmearer.h" +#include "ALICE3/Core/FastTracker.h" #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFRICH.h" +#include "ALICE3/DataModel/OTFTracks.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -83,9 +83,6 @@ struct OnTheFlyRichPid { // Necessary for LUTs Service ccdb; - // master setting: magnetic field - Configurable magneticField{"magneticField", 0, "magnetic field (kilogauss) if 0, taken from the tracker task"}; - // add rich-specific configurables here Configurable bRichNumberOfSectors{"bRichNumberOfSectors", 21, "barrel RICH number of sectors"}; Configurable bRichPhotodetectorCentralModuleHalfLength{"bRichPhotodetectorCentralModuleHalfLength", 18.4 / 2.0, "barrel RICH photodetector central module half length (cm)"}; @@ -132,11 +129,12 @@ struct OnTheFlyRichPid { Configurable bRichRefractiveIndexSector20{"bRichRefractiveIndexSector20", 1.03, "barrel RICH refractive index central(s)-20 and central(s)+20"}; // central(s)-20 and central(s)+20 Configurable bRICHPixelSize{"bRICHPixelSize", 0.1, "barrel RICH pixel size (cm)"}; Configurable bRichGapRefractiveIndex{"bRichGapRefractiveIndex", 1.000283, "barrel RICH gap refractive index"}; + Configurable cleanLutWhenLoaded{"cleanLutWhenLoaded", true, "clean LUTs after being loaded to save disk space"}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - // Track smearer (here used to get relative pt and eta uncertainties) - o2::delphes::DelphesO2TrackSmearer mSmearer; + // Track smearer array, one per geometry + std::vector> mSmearer; // needed: random number generator for smearing TRandom3 pRandomNumberGenerator; @@ -286,40 +284,68 @@ struct OnTheFlyRichPid { // std::cout << std::endl << std::endl; } + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; void init(o2::framework::InitContext& initContext) { + mGeoContainer.init(initContext); + + const int nGeometries = mGeoContainer.getNumberOfConfigurations(); + mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); + pRandomNumberGenerator.SetSeed(0); // fully randomize - if (magneticField.value < o2::constants::math::Epsilon) { - LOG(info) << "Getting the magnetic field from the on-the-fly tracker task"; - if (!getTaskOptionValue(initContext, "on-the-fly-tracker", magneticField, false)) { - LOG(fatal) << "Could not get Bz from on-the-fly-tracker task"; - } - LOG(info) << "Bz = " << magneticField.value << " T"; - } + for (int icfg = 0; icfg < nGeometries; ++icfg) { + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; + mSmearer.emplace_back(std::make_unique()); + mSmearer[icfg]->setCleanupDownloadedFile(cleanLutWhenLoaded.value); + mSmearer[icfg]->setCcdbManager(ccdb.operator->()); + std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); + for (const auto& entry : globalConfiguration) { + int pdg = 0; + if (entry.first.find("lut") != 0) { + continue; + } + if (entry.first.find("lutEl") != std::string::npos) { + pdg = kElectron; + } else if (entry.first.find("lutMu") != std::string::npos) { + pdg = kMuonMinus; + } else if (entry.first.find("lutPi") != std::string::npos) { + pdg = kPiPlus; + } else if (entry.first.find("lutKa") != std::string::npos) { + pdg = kKPlus; + } else if (entry.first.find("lutPr") != std::string::npos) { + pdg = kProton; + } else if (entry.first.find("lutDe") != std::string::npos) { + pdg = o2::constants::physics::kDeuteron; + } else if (entry.first.find("lutTr") != std::string::npos) { + pdg = o2::constants::physics::kTriton; + } else if (entry.first.find("lutHe3") != std::string::npos) { + pdg = o2::constants::physics::kHelium3; + } else if (entry.first.find("lutAl") != std::string::npos) { + pdg = o2::constants::physics::kAlpha; + } - // Load LUT for pt and eta smearing - if (flagIncludeTrackAngularRes && flagRICHLoadDelphesLUTs) { - mSmearer.setCcdbManager(ccdb.operator->()); - auto loadLUT = [&](int pdg, const std::string& cfgNameToInherit) { - std::string lut = "none"; - if (!getTaskOptionValue(initContext, "on-the-fly-tracker", cfgNameToInherit, lut, false)) { - LOG(fatal) << "Could not get " << cfgNameToInherit << " from on-the-fly-tracker task"; + std::string filename = entry.second; + if (pdg == 0) { + LOG(fatal) << "Unknown LUT entry " << entry.first << " for global configuration"; } - bool success = mSmearer.loadTable(pdg, lut.c_str()); - if (!success && !lut.empty()) { - LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << lut; + LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided file '" << filename << "'"; + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; } - }; - loadLUT(11, "lutEl"); - loadLUT(13, "lutMu"); - loadLUT(211, "lutPi"); - loadLUT(321, "lutKa"); - loadLUT(2212, "lutPr"); - loadLUT(1000010020, "lutDe"); - loadLUT(1000010030, "lutTr"); - loadLUT(1000020030, "lutHe3"); - loadLUT(1000020040, "lutAl"); + // strip from leading/trailing spaces + filename.erase(0, filename.find_first_not_of(" ")); + filename.erase(filename.find_last_not_of(" ") + 1); + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + bool success = mSmearer[icfg]->loadTable(pdg, filename.c_str()); + if (!success) { + LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << filename; + } + } } if (doQAplots) { @@ -691,7 +717,7 @@ struct OnTheFlyRichPid { return trackAngularResolution; } - void process(soa::Join::iterator const& collision, + void process(soa::Join::iterator const& collision, soa::Join const& tracks, aod::McParticles const&, aod::McCollisions const&) @@ -751,7 +777,7 @@ struct OnTheFlyRichPid { o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdg); // float xPv = kErrorValue; - if (o2track.propagateToDCA(mcPvVtx, magneticField)) { + if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { // xPv = o2track.getX(); } @@ -781,7 +807,7 @@ struct OnTheFlyRichPid { const float projectiveRadiatorRadius = radiusRipple(o2track.getEta(), iSecor); bool flagReachesRadiator = false; if (projectiveRadiatorRadius > kErrorValue + 1.) { - flagReachesRadiator = checkMagfieldLimit(o2track, projectiveRadiatorRadius, magneticField); + flagReachesRadiator = checkMagfieldLimit(o2track, projectiveRadiatorRadius, mMagneticField); } /// DISCLAIMER: Exact extrapolation of angular resolution would require track propagation /// to the RICH radiator (accounting sector inclination) in terms of (R,z). @@ -794,7 +820,7 @@ struct OnTheFlyRichPid { // Now we calculate the expected Cherenkov angle following certain mass hypotheses // and the (imperfect!) reconstructed track parametrizations auto recoTrack = getTrackParCov(track); - if (recoTrack.propagateToDCA(pvVtx, magneticField)) { + if (recoTrack.propagateToDCA(pvVtx, mMagneticField)) { // xPv = recoTrack.getX(); } @@ -844,9 +870,9 @@ struct OnTheFlyRichPid { double ptResolution = transverseMomentum * transverseMomentum * std::sqrt(recoTrack.getSigma1Pt2()); double etaResolution = std::fabs(std::sin(2.0 * std::atan(std::exp(-recoTrack.getEta())))) * std::sqrt(recoTrack.getSigmaTgl2()); if (flagRICHLoadDelphesLUTs) { - if (mSmearer.hasTable(kParticlePdgs[ii])) { - ptResolution = mSmearer.getAbsPtRes(kParticlePdgs[ii], dNdEta, recoTrack.getEta(), transverseMomentum); - etaResolution = mSmearer.getAbsEtaRes(kParticlePdgs[ii], dNdEta, recoTrack.getEta(), transverseMomentum); + if (mSmearer[collision.lutConfigId()]->hasTable(kParticlePdgs[ii])) { + ptResolution = mSmearer[collision.lutConfigId()]->getAbsPtRes(kParticlePdgs[ii], dNdEta, recoTrack.getEta(), transverseMomentum); + etaResolution = mSmearer[collision.lutConfigId()]->getAbsEtaRes(kParticlePdgs[ii], dNdEta, recoTrack.getEta(), transverseMomentum); } } // cout << endl << "Pt resolution: " << ptResolution << ", Eta resolution: " << etaResolution << endl << endl; diff --git a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx index f7091d7f26b..e408b59d784 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx @@ -24,11 +24,11 @@ /// \since May 22, 2024 /// -#include "TableHelper.h" - #include "ALICE3/Core/DelphesO2TrackSmearer.h" +#include "ALICE3/Core/FastTracker.h" #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/OTFTracks.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -89,7 +89,6 @@ struct OnTheFlyTofPid { // more could be added (especially a disk TOF at a certain z?) // in the evolution of this effort struct : ConfigurableGroup { - Configurable magneticField{"magneticField", 0, "magnetic field (kilogauss) if 0, taken from the tracker task"}; Configurable considerEventTime{"considerEventTime", true, "flag to consider event time"}; Configurable innerTOFRadius{"innerTOFRadius", 20, "barrel inner TOF radius (cm)"}; Configurable outerTOFRadius{"outerTOFRadius", 80, "barrel outer TOF radius (cm)"}; @@ -129,11 +128,12 @@ struct OnTheFlyTofPid { Configurable nBinsMult{"nBinsMult", 200, "number of bins in multiplicity"}; Configurable maxMultRange{"maxMultRange", 1000.f, "upper limit in multiplicity plots"}; } plotsConfig; + Configurable cleanLutWhenLoaded{"cleanLutWhenLoaded", true, "clean LUTs after being loaded to save disk space"}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - // Track smearer (here used to get absolute pt and eta uncertainties if simConfig.flagTOFLoadDelphesLUTs is true) - o2::delphes::DelphesO2TrackSmearer mSmearer; + // Track smearer array, one per geometry + std::vector> mSmearer; // needed: random number generator for smearing TRandom3 pRandomNumberGenerator; @@ -143,39 +143,68 @@ struct OnTheFlyTofPid { OutputObj listEfficiency{"efficiency"}; static constexpr int kParticles = 9; + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; void init(o2::framework::InitContext& initContext) { + mGeoContainer.init(initContext); + + const int nGeometries = mGeoContainer.getNumberOfConfigurations(); + mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); + pRandomNumberGenerator.SetSeed(0); // fully randomize - if (simConfig.magneticField.value < o2::constants::math::Epsilon) { - LOG(info) << "Getting the magnetic field from the on-the-fly tracker task"; - if (!getTaskOptionValue(initContext, "on-the-fly-tracker", simConfig.magneticField, false)) { - LOG(fatal) << "Could not get Bz from on-the-fly-tracker task"; - } - LOG(info) << "Bz = " << simConfig.magneticField.value << " T"; - } - // Load LUT for pt and eta smearing - if (simConfig.flagIncludeTrackTimeRes && simConfig.flagTOFLoadDelphesLUTs) { - mSmearer.setCcdbManager(ccdb.operator->()); - auto loadLUT = [&](int pdg, const std::string& cfgNameToInherit) { - std::string lut = "none"; - if (!getTaskOptionValue(initContext, "on-the-fly-tracker", cfgNameToInherit, lut, false)) { - LOG(fatal) << "Could not get " << cfgNameToInherit << " from on-the-fly-tracker task"; + for (int icfg = 0; icfg < nGeometries; ++icfg) { + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; + mSmearer.emplace_back(std::make_unique()); + mSmearer[icfg]->setCleanupDownloadedFile(cleanLutWhenLoaded.value); + mSmearer[icfg]->setCcdbManager(ccdb.operator->()); + std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); + for (const auto& entry : globalConfiguration) { + int pdg = 0; + if (entry.first.find("lut") != 0) { + continue; } - bool success = mSmearer.loadTable(pdg, lut.c_str()); - if (!success && !lut.empty()) { - LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << lut; + if (entry.first.find("lutEl") != std::string::npos) { + pdg = kElectron; + } else if (entry.first.find("lutMu") != std::string::npos) { + pdg = kMuonMinus; + } else if (entry.first.find("lutPi") != std::string::npos) { + pdg = kPiPlus; + } else if (entry.first.find("lutKa") != std::string::npos) { + pdg = kKPlus; + } else if (entry.first.find("lutPr") != std::string::npos) { + pdg = kProton; + } else if (entry.first.find("lutDe") != std::string::npos) { + pdg = o2::constants::physics::kDeuteron; + } else if (entry.first.find("lutTr") != std::string::npos) { + pdg = o2::constants::physics::kTriton; + } else if (entry.first.find("lutHe3") != std::string::npos) { + pdg = o2::constants::physics::kHelium3; + } else if (entry.first.find("lutAl") != std::string::npos) { + pdg = o2::constants::physics::kAlpha; } - }; - loadLUT(11, "lutEl"); - loadLUT(13, "lutMu"); - loadLUT(211, "lutPi"); - loadLUT(321, "lutKa"); - loadLUT(2212, "lutPr"); - loadLUT(1000010020, "lutDe"); - loadLUT(1000010030, "lutTr"); - loadLUT(1000020030, "lutHe3"); - loadLUT(1000020040, "lutAl"); + + std::string filename = entry.second; + if (pdg == 0) { + LOG(fatal) << "Unknown LUT entry " << entry.first << " for global configuration"; + } + LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided file '" << filename << "'"; + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + // strip from leading/trailing spaces + filename.erase(0, filename.find_first_not_of(" ")); + filename.erase(filename.find_last_not_of(" ") + 1); + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + bool success = mSmearer[icfg]->loadTable(pdg, filename.c_str()); + if (!success) { + LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << filename; + } + } } if (plotsConfig.doQAplots) { @@ -403,13 +432,13 @@ struct OnTheFlyTofPid { bool isInInnerTOFActiveArea(const o2::track::TrackParCov& track) { - static TOFLayerEfficiency innerTOFLayer(simConfig.innerTOFRadius, simConfig.innerTOFLength, simConfig.innerTOFPixelDimension, simConfig.innerTOFFractionOfInactiveArea, simConfig.magneticField); + static TOFLayerEfficiency innerTOFLayer(simConfig.innerTOFRadius, simConfig.innerTOFLength, simConfig.innerTOFPixelDimension, simConfig.innerTOFFractionOfInactiveArea, mMagneticField); return innerTOFLayer.isTrackInActiveArea(track); } bool isInOuterTOFActiveArea(const o2::track::TrackParCov& track) { - static TOFLayerEfficiency outerTOFLayer(simConfig.outerTOFRadius, simConfig.outerTOFLength, simConfig.outerTOFPixelDimension, simConfig.outerTOFFractionOfInactiveArea, simConfig.magneticField); + static TOFLayerEfficiency outerTOFLayer(simConfig.outerTOFRadius, simConfig.outerTOFLength, simConfig.outerTOFPixelDimension, simConfig.outerTOFFractionOfInactiveArea, mMagneticField); return outerTOFLayer.isTrackInActiveArea(track); } @@ -594,7 +623,7 @@ struct OnTheFlyTofPid { return trackTimeResolution; } - void process(soa::Join::iterator const& collision, + void process(soa::Join::iterator const& collision, soa::Join const& tracks, aod::McParticles const&, aod::McCollisions const&) @@ -656,13 +685,13 @@ struct OnTheFlyTofPid { float xPv = -100.f; static constexpr float kTrkXThreshold = -99.f; // Threshold to consider a good propagation of the track - if (o2track.propagateToDCA(mcPvVtx, simConfig.magneticField)) { + if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { xPv = o2track.getX(); } float trackLengthInnerTOF = -1, trackLengthOuterTOF = -1; if (xPv > kTrkXThreshold) { - trackLengthInnerTOF = computeTrackLength(o2track, simConfig.innerTOFRadius, simConfig.magneticField); - trackLengthOuterTOF = computeTrackLength(o2track, simConfig.outerTOFRadius, simConfig.magneticField); + trackLengthInnerTOF = computeTrackLength(o2track, simConfig.innerTOFRadius, mMagneticField); + trackLengthOuterTOF = computeTrackLength(o2track, simConfig.outerTOFRadius, mMagneticField); } // Check if the track hit a sensitive area of the TOF @@ -671,8 +700,8 @@ struct OnTheFlyTofPid { trackLengthInnerTOF = -999.f; } else { float x = 0.f; - o2track.getXatLabR(simConfig.innerTOFRadius, x, simConfig.magneticField); - histos.fill(HIST("iTOF/h2HitMap"), o2track.getZAt(x, simConfig.magneticField), simConfig.innerTOFRadius * o2track.getPhiAt(x, simConfig.magneticField)); + o2track.getXatLabR(simConfig.innerTOFRadius, x, mMagneticField); + histos.fill(HIST("iTOF/h2HitMap"), o2track.getZAt(x, mMagneticField), simConfig.innerTOFRadius * o2track.getPhiAt(x, mMagneticField)); } const bool activeOuterTOF = isInOuterTOFActiveArea(o2track); @@ -680,8 +709,8 @@ struct OnTheFlyTofPid { trackLengthOuterTOF = -999.f; } else { float x = 0.f; - o2track.getXatLabR(simConfig.outerTOFRadius, x, simConfig.magneticField); - histos.fill(HIST("oTOF/h2HitMap"), o2track.getZAt(x, simConfig.magneticField), simConfig.outerTOFRadius * o2track.getPhiAt(x, simConfig.magneticField)); + o2track.getXatLabR(simConfig.outerTOFRadius, x, mMagneticField); + histos.fill(HIST("oTOF/h2HitMap"), o2track.getZAt(x, mMagneticField), simConfig.outerTOFRadius * o2track.getPhiAt(x, mMagneticField)); } // get mass to calculate velocity @@ -705,12 +734,12 @@ struct OnTheFlyTofPid { float trackLengthRecoInnerTOF = -1, trackLengthRecoOuterTOF = -1; auto recoTrack = getTrackParCov(track); xPv = -100.f; - if (recoTrack.propagateToDCA(pvVtx, simConfig.magneticField)) { + if (recoTrack.propagateToDCA(pvVtx, mMagneticField)) { xPv = recoTrack.getX(); } if (xPv > kTrkXThreshold) { - trackLengthRecoInnerTOF = computeTrackLength(recoTrack, simConfig.innerTOFRadius, simConfig.magneticField); - trackLengthRecoOuterTOF = computeTrackLength(recoTrack, simConfig.outerTOFRadius, simConfig.magneticField); + trackLengthRecoInnerTOF = computeTrackLength(recoTrack, simConfig.innerTOFRadius, mMagneticField); + trackLengthRecoOuterTOF = computeTrackLength(recoTrack, simConfig.outerTOFRadius, mMagneticField); } // cache the track info needed for the event time calculation @@ -847,13 +876,13 @@ struct OnTheFlyTofPid { double ptResolution = transverseMomentum * transverseMomentum * std::sqrt(trkWithTime.mMomentum.second); double etaResolution = std::fabs(std::sin(2.0 * std::atan(std::exp(-pseudorapidity)))) * std::sqrt(trkWithTime.mPseudorapidity.second); if (simConfig.flagTOFLoadDelphesLUTs) { - if (mSmearer.hasTable(kParticlePdgs[ii])) { // Only if the LUT for this particle was loaded - ptResolution = mSmearer.getAbsPtRes(kParticlePdgs[ii], dNdEta, pseudorapidity, transverseMomentum); - etaResolution = mSmearer.getAbsEtaRes(kParticlePdgs[ii], dNdEta, pseudorapidity, transverseMomentum); + if (mSmearer[collision.lutConfigId()]->hasTable(kParticlePdgs[ii])) { // Only if the LUT for this particle was loaded + ptResolution = mSmearer[collision.lutConfigId()]->getAbsPtRes(kParticlePdgs[ii], dNdEta, pseudorapidity, transverseMomentum); + etaResolution = mSmearer[collision.lutConfigId()]->getAbsEtaRes(kParticlePdgs[ii], dNdEta, pseudorapidity, transverseMomentum); } } - const float innerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, kParticleMasses[ii], simConfig.innerTOFRadius, simConfig.magneticField); - const float outerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, kParticleMasses[ii], simConfig.outerTOFRadius, simConfig.magneticField); + const float innerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, kParticleMasses[ii], simConfig.innerTOFRadius, mMagneticField); + const float outerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, kParticleMasses[ii], simConfig.outerTOFRadius, mMagneticField); innerTotalTimeReso = std::hypot(simConfig.innerTOFTimeReso, innerTrackTimeReso); outerTotalTimeReso = std::hypot(simConfig.outerTOFTimeReso, outerTrackTimeReso); diff --git a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx index d5be2903215..0239be242d3 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx @@ -66,9 +66,13 @@ using namespace o2; using namespace o2::framework; using std::array; -#define getHist(type, name) std::get>(histPointers[name]) -#define fillHist(type, name, ...) \ - std::get>(histPointers[name])->Fill(__VA_ARGS__); +#define getHist(type, name) \ + std::get>(histPointers[name]) +#define fillHist(type, name, ...) \ + if (histPointers.find(name) != histPointers.end()) \ + std::get>(histPointers[name])->Fill(__VA_ARGS__); \ + else \ + LOG(fatal) << "Histogram " << name << " not found!"; #define insertHist(name, ...) histPointers[name] = histos.add((name).c_str(), __VA_ARGS__); struct OnTheFlyTracker { @@ -94,7 +98,6 @@ struct OnTheFlyTracker { Produces tableTrackSelectionExtension; Configurable seed{"seed", 0, "TGenPhaseSpace seed"}; - Configurable magneticField{"magneticField", 20.0f, "magnetic field in kG"}; Configurable maxEta{"maxEta", 1.5, "maximum eta to consider viable"}; Configurable multEtaRange{"multEtaRange", 0.8, "eta range to compute the multiplicity"}; Configurable minPt{"minPt", 0.1, "minimum pt to consider viable"}; @@ -112,19 +115,7 @@ struct OnTheFlyTracker { Configurable processUnreconstructedTracks{"processUnreconstructedTracks", false, "process (smear) unreco-ed tracks"}; Configurable doExtraQA{"doExtraQA", false, "do extra 2D QA plots"}; Configurable extraQAwithoutDecayDaughters{"extraQAwithoutDecayDaughters", false, "remove decay daughters from qa plots (yes/no)"}; - - struct : ConfigurableGroup { - std::string prefix = "lookUpTables"; // JSON group name - Configurable> lutEl{"lutEl", std::vector{"lutCovm.el.dat "}, "LUT for electrons (if emtpy no LUT is taken)"}; - Configurable> lutMu{"lutMu", std::vector{"lutCovm.mu.dat "}, "LUT for muons (if emtpy no LUT is taken)"}; - Configurable> lutPi{"lutPi", std::vector{"lutCovm.pi.dat "}, "LUT for pions (if emtpy no LUT is taken)"}; - Configurable> lutKa{"lutKa", std::vector{"lutCovm.ka.dat "}, "LUT for kaons (if emtpy no LUT is taken)"}; - Configurable> lutPr{"lutPr", std::vector{"lutCovm.pr.dat "}, "LUT for protons (if emtpy no LUT is taken)"}; - Configurable> lutDe{"lutDe", std::vector{" "}, "LUT for deuterons (if emtpy no LUT is taken)"}; - Configurable> lutTr{"lutTr", std::vector{" "}, "LUT for tritons (if emtpy no LUT is taken)"}; - Configurable> lutHe3{"lutHe3", std::vector{" "}, "LUT for Helium-3 (if emtpy no LUT is taken)"}; - Configurable> lutAl{"lutAl", std::vector{" "}, "LUT for Alphas (if emtpy no LUT is taken)"}; - } lookUpTables; + Configurable cleanLutWhenLoaded{"cleanLutWhenLoaded", true, "clean LUTs after being loaded to save disk space"}; struct : ConfigurableGroup { ConfigurableAxis axisMomentum{"axisMomentum", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "#it{p} (GeV/#it{c})"}; @@ -152,7 +143,6 @@ struct OnTheFlyTracker { Configurable minSiliconHits{"minSiliconHits", 6, "minimum number of silicon hits to accept track"}; Configurable minSiliconHitsIfTPCUsed{"minSiliconHitsIfTPCUsed", 2, "minimum number of silicon hits to accept track in case TPC info is present"}; Configurable minTPCClusters{"minTPCClusters", 70, "minimum number of TPC hits necessary to consider minSiliconHitsIfTPCUsed"}; - Configurable> alice3geo{"alice3geo", std::vector{"2"}, "0: ALICE 3 v1, 1: ALICE 3 v4, 2: ALICE 3 Sep 2025, or path to ccdb with a3 geo (ccdb:Users/u/user/)"}; Configurable applyZacceptance{"applyZacceptance", false, "apply z limits to detector layers or not"}; Configurable applyMSCorrection{"applyMSCorrection", true, "apply ms corrections for secondaries or not"}; Configurable applyElossCorrection{"applyElossCorrection", true, "apply eloss corrections for secondaries or not"}; @@ -165,7 +155,6 @@ struct OnTheFlyTracker { std::string prefix = "fastPrimaryTrackerSettings"; Configurable fastTrackPrimaries{"fastTrackPrimaries", false, "Use fasttracker for primary tracks. Enable with care"}; Configurable minSiliconHits{"minSiliconHits", 4, "minimum number of silicon hits to accept track"}; - Configurable alice3geo{"alice3geo", "2", "0: ALICE 3 v1, 1: ALICE 3 v4, 2: ALICE 3 Sep 2025, or path to ccdb with a3 geo"}; Configurable applyZacceptance{"applyZacceptance", false, "apply z limits to detector layers or not"}; Configurable applyMSCorrection{"applyMSCorrection", true, "apply ms corrections for secondaries or not"}; Configurable applyElossCorrection{"applyElossCorrection", true, "apply eloss corrections for secondaries or not"}; @@ -193,15 +182,11 @@ struct OnTheFlyTracker { o2::vertexing::DCAFitterN<2> fitter; // FastTracker machinery - // o2::fastsim::FastTracker fastTracker; std::vector> fastTracker; - o2::fastsim::FastTracker fastPrimaryTracker; // V0 names for filling histograms static constexpr int NtypesV0 = 3; static constexpr std::string_view NameV0s[NtypesV0] = {"K0", "Lambda", "AntiLambda"}; - static constexpr int NtypesDetectors = 4; - static constexpr std::string_view NameDetectors[NtypesDetectors] = {"0", "1", "2", "3"}; // Class to hold the track information for the O2 vertexing class TrackAlice3 : public o2::track::TrackParCov @@ -305,74 +290,76 @@ struct OnTheFlyTracker { TRandom3 rand; Service ccdb; - void init(o2::framework::InitContext&) + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; + void init(o2::framework::InitContext& initContext) { LOG(info) << "Initializing OnTheFlyTracker task"; ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setTimestamp(-1); + mGeoContainer.init(initContext); - auto loadLUT = [&](int icfg, int pdg, const std::vector& tables) { - LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided tables with size " << tables.size(); - if (tables.empty()) { - LOG(debug) << "No LUT file passed for pdg " << pdg << ", skipping."; - return false; - } - const bool foundNewCfg = static_cast(icfg) < tables.size(); - std::string lutFile = foundNewCfg ? tables[icfg] : tables.front(); - LOG(info) << "Loading LUT for pdg " << pdg << " from file " << lutFile << " for config " << icfg; - // strip from leading/trailing spaces - lutFile.erase(0, lutFile.find_first_not_of(" ")); - lutFile.erase(lutFile.find_last_not_of(" ") + 1); - if (lutFile.empty()) { - LOG(info) << "Empty LUT file name for pdg " << pdg << ", skipping."; - return false; - } - bool success = mSmearer[icfg]->loadTable(pdg, lutFile.c_str()); - if (!success) { - LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << lutFile; - } - - return foundNewCfg; - }; - int nGeometries = static_cast(fastTrackerSettings.alice3geo->size()); - if (enablePrimarySmearing) - nGeometries = static_cast(lookUpTables.lutPi->size()); - if (enablePrimarySmearing && enableSecondarySmearing) { - if (static_cast(lookUpTables.lutPi->size()) != static_cast(fastTrackerSettings.alice3geo->size())) { - LOG(fatal) << "When enabling both primary and secondary smearing, the number of LUTs provided must match the number of geometries provided!"; - } - } - + const int nGeometries = mGeoContainer.getNumberOfConfigurations(); + mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); for (int icfg = 0; icfg < nGeometries; ++icfg) { - std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; mSmearer.emplace_back(std::make_unique()); + mSmearer[icfg]->setCleanupDownloadedFile(cleanLutWhenLoaded.value); mSmearer[icfg]->setCcdbManager(ccdb.operator->()); + std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); if (enablePrimarySmearing) { - // check if more configs were provided, fall back to first entry - bool newLUTLoaded = false; - newLUTLoaded |= loadLUT(icfg, kElectron, lookUpTables.lutEl.value); - newLUTLoaded |= loadLUT(icfg, kMuonMinus, lookUpTables.lutMu.value); - newLUTLoaded |= loadLUT(icfg, kPiPlus, lookUpTables.lutPi.value); - newLUTLoaded |= loadLUT(icfg, kKPlus, lookUpTables.lutKa.value); - newLUTLoaded |= loadLUT(icfg, kProton, lookUpTables.lutPr.value); - newLUTLoaded |= loadLUT(icfg, o2::constants::physics::kDeuteron, lookUpTables.lutDe.value); - newLUTLoaded |= loadLUT(icfg, o2::constants::physics::kTriton, lookUpTables.lutTr.value); - newLUTLoaded |= loadLUT(icfg, o2::constants::physics::kHelium3, lookUpTables.lutHe3.value); - newLUTLoaded |= loadLUT(icfg, o2::constants::physics::kAlpha, lookUpTables.lutAl.value); - bool smearerOK = true; - if (!newLUTLoaded) { - mSmearer.pop_back(); - // break; - smearerOK = false; + // load LUTs for primaries + for (const auto& entry : globalConfiguration) { + int pdg = 0; + if (entry.first.find("lut") != 0) { + continue; + } + if (entry.first.find("lutEl") != std::string::npos) { + pdg = kElectron; + } else if (entry.first.find("lutMu") != std::string::npos) { + pdg = kMuonMinus; + } else if (entry.first.find("lutPi") != std::string::npos) { + pdg = kPiPlus; + } else if (entry.first.find("lutKa") != std::string::npos) { + pdg = kKPlus; + } else if (entry.first.find("lutPr") != std::string::npos) { + pdg = kProton; + } else if (entry.first.find("lutDe") != std::string::npos) { + pdg = o2::constants::physics::kDeuteron; + } else if (entry.first.find("lutTr") != std::string::npos) { + pdg = o2::constants::physics::kTriton; + } else if (entry.first.find("lutHe3") != std::string::npos) { + pdg = o2::constants::physics::kHelium3; + } else if (entry.first.find("lutAl") != std::string::npos) { + pdg = o2::constants::physics::kAlpha; + } + + std::string filename = entry.second; + if (pdg == 0) { + LOG(fatal) << "Unknown LUT entry " << entry.first << " for global configuration"; + } + LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided file '" << filename << "'"; + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + // strip from leading/trailing spaces + filename.erase(0, filename.find_first_not_of(" ")); + filename.erase(filename.find_last_not_of(" ") + 1); + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + bool success = mSmearer[icfg]->loadTable(pdg, filename.c_str()); + if (!success) { + LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << filename; + } } // interpolate efficiencies if requested to do so - if (smearerOK) { - mSmearer[icfg]->interpolateEfficiency(static_cast(interpolateLutEfficiencyVsNch)); + mSmearer[icfg]->interpolateEfficiency(interpolateLutEfficiencyVsNch.value); - // smear un-reco'ed tracks if asked to do so - mSmearer[icfg]->skipUnreconstructed(static_cast(!processUnreconstructedTracks)); - } + // smear un-reco'ed tracks if asked to do so + mSmearer[icfg]->skipUnreconstructed(!processUnreconstructedTracks.value); insertHist(histPath + "hPtGenerated", "hPtGenerated", {kTH1D, {{axes.axisMomentum}}}); insertHist(histPath + "hPhiGenerated", "hPhiGenerated", {kTH1D, {{100, 0.0f, 2 * M_PI, "#phi (rad)"}}}); @@ -395,20 +382,11 @@ struct OnTheFlyTracker { if (enableSecondarySmearing) { fastTracker.emplace_back(std::make_unique()); - fastTracker[icfg]->SetMagneticField(magneticField); + fastTracker[icfg]->SetMagneticField(mMagneticField); fastTracker[icfg]->SetApplyZacceptance(fastTrackerSettings.applyZacceptance); fastTracker[icfg]->SetApplyMSCorrection(fastTrackerSettings.applyMSCorrection); fastTracker[icfg]->SetApplyElossCorrection(fastTrackerSettings.applyElossCorrection); - if (fastTrackerSettings.alice3geo.value[icfg] == "0") { - fastTracker[icfg]->AddSiliconALICE3v2(fastTrackerSettings.pixelRes); - } else if (fastTrackerSettings.alice3geo.value[icfg] == "1") { - fastTracker[icfg]->AddSiliconALICE3v4(fastTrackerSettings.pixelRes); - fastTracker[icfg]->AddTPC(0.1, 0.1); - } else if (fastTrackerSettings.alice3geo.value[icfg] == "2") { - fastTracker[icfg]->AddSiliconALICE3(fastTrackerSettings.scaleVD, fastTrackerSettings.pixelRes); - } else { - fastTracker[icfg]->AddGenericDetector(fastTrackerSettings.alice3geo.value[icfg], ccdb.operator->()); - } + fastTracker[icfg]->AddGenericDetector(mGeoContainer.getEntry(icfg), ccdb.operator->()); // print fastTracker settings fastTracker[icfg]->Print(); if (cascadeDecaySettings.doXiQA) @@ -538,10 +516,10 @@ struct OnTheFlyTracker { } } - LOGF(info, "Initializing magnetic field to value: %.3f kG", static_cast(magneticField)); + LOG(info) << "Initializing magnetic field to value: " << mMagneticField << " kG"; o2::parameters::GRPMagField grpmag; grpmag.setFieldUniformity(true); - grpmag.setL3Current(30000.f * (magneticField / 5.0f)); + grpmag.setL3Current(30000.f * (mMagneticField / 5.0f)); auto field = grpmag.getNominalL3Field(); o2::base::Propagator::initFieldFromGRP(&grpmag); @@ -582,28 +560,7 @@ struct OnTheFlyTracker { // Set seed for TGenPhaseSpace rand.SetSeed(seed); - - // Configure FastTracker for primaries - if (fastPrimaryTrackerSettings.fastTrackPrimaries) { - fastPrimaryTracker.SetMagneticField(magneticField); - fastPrimaryTracker.SetApplyZacceptance(fastPrimaryTrackerSettings.applyZacceptance); - fastPrimaryTracker.SetApplyMSCorrection(fastPrimaryTrackerSettings.applyMSCorrection); - fastPrimaryTracker.SetApplyElossCorrection(fastPrimaryTrackerSettings.applyElossCorrection); - - if (fastPrimaryTrackerSettings.alice3geo.value == "0") { - fastPrimaryTracker.AddSiliconALICE3v2({0.00025, 0.00025, 0.001, 0.001}); - } else if (fastPrimaryTrackerSettings.alice3geo.value == "1") { - fastPrimaryTracker.AddSiliconALICE3v4({0.00025, 0.00025, 0.001, 0.001}); - fastPrimaryTracker.AddTPC(0.1, 0.1); - } else if (fastPrimaryTrackerSettings.alice3geo.value == "2") { - fastPrimaryTracker.AddSiliconALICE3(1., {0.00025, 0.00025, 0.001, 0.001}); - } else { - fastPrimaryTracker.AddGenericDetector(fastPrimaryTrackerSettings.alice3geo, ccdb.operator->()); - } - - // print fastTracker settings - fastPrimaryTracker.Print(); - } + gRandom->SetSeed(seed); } /// Function to decay the xi @@ -622,7 +579,7 @@ struct OnTheFlyTracker { float sna, csa; o2::math_utils::CircleXYf_t circleXi; - track.getCircleParams(magneticField, circleXi, sna, csa); + track.getCircleParams(mMagneticField, circleXi, sna, csa); const double rxyXi = rxyzXi / std::sqrt(1. + track.getTgl() * track.getTgl()); const double theta = rxyXi / circleXi.rC; const double newX = ((particle.vx() - circleXi.xC) * std::cos(theta) - (particle.vy() - circleXi.yC) * std::sin(theta)) + circleXi.xC; @@ -769,15 +726,21 @@ struct OnTheFlyTracker { uint32_t multiplicityCounter = 0; getHist(TH1, histPath + "hLUTMultiplicity")->Fill(dNdEta); - gRandom->SetSeed(seed); - + // Now that the multiplicity is known, we can process the particles to smear them + double xiDecayRadius2D = 0; + double laDecayRadius2D = 0; + double v0DecayRadius2D = 0; + std::vector decayProducts; + std::vector v0DecayProducts; + std::vector xiDecayVertex, laDecayVertex, v0DecayVertex; for (const auto& mcParticle : mcParticles) { - double xiDecayRadius2D = 0; - double laDecayRadius2D = 0; - double v0DecayRadius2D = 0; - std::vector decayProducts; - std::vector v0DecayProducts; - std::vector xiDecayVertex, laDecayVertex, v0DecayVertex; + xiDecayRadius2D = 0; + laDecayRadius2D = 0; + v0DecayRadius2D = 0; + xiDecayVertex.clear(); + laDecayVertex.clear(); + v0DecayVertex.clear(); + if (cascadeDecaySettings.decayXi) { if (mcParticle.pdgCode() == kXiMinus) { o2::track::TrackParCov xiTrackParCov; @@ -837,7 +800,7 @@ struct OnTheFlyTracker { if (mcParticle.pdgCode() != v0PDGs[indexV0]) { continue; } - for (int indexDetector = 0; indexDetector < NtypesDetectors; indexDetector++) { + for (int indexDetector = 0; indexDetector < mGeoContainer.getNumberOfConfigurations(); indexDetector++) { std::string path = Form("V0Building_Configuration_%i/%s/", indexDetector, NameV0s[indexV0].data()); fillHist(TH2, path + "hGen", v0DecayRadius2D, mcParticle.pt()); fillHist(TH2, path + "hGenNegDaughterFromV0", v0DecayRadius2D, v0DecayProducts[0].Pt()); @@ -1059,11 +1022,11 @@ struct OnTheFlyTracker { // find perfect intercept XYZ float targetX = 1e+3; - trackParCov.getXatLabR(layer.getRadius(), targetX, magneticField); + trackParCov.getXatLabR(layer.getRadius(), targetX, mMagneticField); if (targetX > 999) continue; // failed to find intercept - if (!trackParCov.propagateTo(targetX, magneticField)) { + if (!trackParCov.propagateTo(targetX, mMagneticField)) { continue; // failed to propagate } @@ -1090,7 +1053,7 @@ struct OnTheFlyTracker { -TMath::Sin(alpha) * posClusterCandidate[0] + TMath::Cos(alpha) * posClusterCandidate[1], posClusterCandidate[2]}; - if (!(cascadeTrack.propagateTo(xyz1[0], magneticField))) + if (!(cascadeTrack.propagateTo(xyz1[0], mMagneticField))) continue; const o2::track::TrackParametrization::dim2_t hitpoint = {static_cast(xyz1[1]), static_cast(xyz1[2])}; const o2::track::TrackParametrization::dim3_t hitpointcov = {layer.getResolutionRPhi() * layer.getResolutionRPhi(), 0.f, layer.getResolutionZ() * layer.getResolutionZ()}; @@ -1319,7 +1282,7 @@ struct OnTheFlyTracker { } else if (fastPrimaryTrackerSettings.fastTrackPrimaries) { o2::track::TrackParCov o2Track; o2::upgrade::convertMCParticleToO2Track(mcParticle, o2Track, pdgDB); - int nHits = fastPrimaryTracker.FastTrack(o2Track, trackParCov, dNdEta); + const int nHits = fastTracker[icfg]->FastTrack(o2Track, trackParCov, dNdEta); if (nHits < fastPrimaryTrackerSettings.minSiliconHits) { reconstructed = false; } @@ -1430,6 +1393,7 @@ struct OnTheFlyTracker { eventCollisionTimeNS, 0.f); // For the moment the event collision time is taken as the "GEANT" time, the computation of the event time is done a posteriori from the tracks in the OTF TOF PID task tableMcCollisionLabels(mcCollision.globalIndex(), 0); tableCollisionsAlice3(dNdEta); + tableOTFLUTConfigId(icfg); // Populate OTF LUT configuration ID // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* @@ -1441,7 +1405,7 @@ struct OnTheFlyTracker { if (populateTracksDCA) { float dcaXY = 1e+10, dcaZ = 1e+10; o2::track::TrackParCov trackParametrization(trackParCov); - if (trackParametrization.propagateToDCA(primaryVertex, magneticField, &dcaInfo)) { + if (trackParametrization.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { dcaXY = dcaInfo.getY(); dcaZ = dcaInfo.getZ(); } @@ -1473,7 +1437,6 @@ struct OnTheFlyTracker { tableTracksDCACov(dcaInfo.getSigmaY2(), dcaInfo.getSigmaZ2()); } } - tableOTFLUTConfigId(icfg); tableStoredTracks(tableCollisions.lastIndex(), trackType, trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt()); tableTracksExtension(trackParCov.getPt(), trackParCov.getP(), trackParCov.getEta(), trackParCov.getPhi()); @@ -1509,7 +1472,7 @@ struct OnTheFlyTracker { if (populateTracksDCA) { float dcaXY = 1e+10, dcaZ = 1e+10; o2::track::TrackParCov trackParametrization(trackParCov); - if (trackParametrization.propagateToDCA(primaryVertex, magneticField, &dcaInfo)) { + if (trackParametrization.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { dcaXY = dcaInfo.getY(); dcaZ = dcaInfo.getZ(); } diff --git a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx index 815612f9509..32e62f2d68f 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx @@ -20,11 +20,11 @@ /// \since May 22, 2025 /// -#include "TableHelper.h" - #include "ALICE3/Core/DelphesO2TrackSmearer.h" +#include "ALICE3/Core/FastTracker.h" #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFPIDTrk.h" +#include "ALICE3/DataModel/OTFTracks.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -46,6 +45,7 @@ #include #include #include +#include #include #include @@ -406,7 +406,6 @@ struct OnTheFlyTrackerPid { Configurable lutTotHe{"lutTotHe", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_1000020030/", "ToT LUT for helium-3"}; Configurable lutTotAl{"lutTotAl", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_1000020040/", "ToT LUT for alphas"}; - Configurable dBz{"dBz", 20, "magnetic field (kilogauss) for track propagation"}; Configurable maxBarrelLayers{"maxBarrelLayers", 11, "Maximum number of barrel layers"}; Configurable numLogBins{"numLogBins", 200, "Number of logarithmic momentum bins"}; Configurable analysisEtaMin{"analysisEtaMin", 0.0f, "Minimum |eta| for LUT loading optimization"}; @@ -428,8 +427,16 @@ struct OnTheFlyTrackerPid { 1000020040 // Alpha }; - void init(o2::framework::InitContext&) + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; + void init(o2::framework::InitContext& initContext) { + mGeoContainer.init(initContext); + + const int nGeometries = mGeoContainer.getNumberOfConfigurations(); + mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); + ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setTimestamp(-1); @@ -608,14 +615,14 @@ struct OnTheFlyTrackerPid { float xPv = -100.f; static constexpr float kTrkXThreshold = -99.f; - if (o2track.propagateToDCA(mcPvVtx, dBz)) { + if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { xPv = o2track.getX(); } if (xPv > kTrkXThreshold) { for (int layer = 0; layer < maxBarrelLayers.value; ++layer) { float layerRadius = kTrackerRadii[layer]; - float trackLength = computeTrackLength(o2track, layerRadius, dBz); + float trackLength = computeTrackLength(o2track, layerRadius, mMagneticField); if (trackLength > 0) { hitMap |= (1 << layer); diff --git a/ALICE3/Tasks/alice3SeparationPower.cxx b/ALICE3/Tasks/alice3SeparationPower.cxx index ae5b52d0692..2c7a829f5bb 100644 --- a/ALICE3/Tasks/alice3SeparationPower.cxx +++ b/ALICE3/Tasks/alice3SeparationPower.cxx @@ -17,21 +17,23 @@ /// \since May 13, 2025 /// -#include +#include "ALICE3/DataModel/OTFRICH.h" +#include "ALICE3/DataModel/OTFTOF.h" + +#include +#include +#include +#include +#include + +#include +#include + #include #include +#include #include -#include "Framework/AnalysisDataModel.h" -#include "Framework/AnalysisTask.h" -#include "Framework/runDataProcessing.h" -#include "Framework/RunningWorkflowInfo.h" -#include "Framework/HistogramRegistry.h" -#include "TProfile2D.h" -#include "THashList.h" -#include "ALICE3/DataModel/OTFTOF.h" -#include "ALICE3/DataModel/OTFRICH.h" - using namespace o2; using namespace o2::framework; @@ -73,7 +75,7 @@ struct alice3SeparationPower { // Check that all the nsigmas are numbers (sanity check) for (int i = 0; i < 5; i++) { if (std::isnan(track.nSigmaInnerTOF(i)) || std::isnan(track.nSigmaOuterTOF(i))) { - LOG(fatal) << "Unrecognized nsigma for " << i << " " << track.nSigmaInnerTOF(i) << " " << track.nSigmaOuterTOF(i); + LOG(warning) << "Unrecognized nsigma for " << i << " " << track.nSigmaInnerTOF(i) << " " << track.nSigmaOuterTOF(i); } } From 10bbab2c2b66c1304964d9abce817889df7e8642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 24 Dec 2025 19:12:31 +0100 Subject: [PATCH 2/2] Update CMakeLists.txt --- ALICE3/TableProducer/OTF/CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ALICE3/TableProducer/OTF/CMakeLists.txt b/ALICE3/TableProducer/OTF/CMakeLists.txt index 83474b6fc06..b75101f977f 100644 --- a/ALICE3/TableProducer/OTF/CMakeLists.txt +++ b/ALICE3/TableProducer/OTF/CMakeLists.txt @@ -33,8 +33,3 @@ o2physics_add_dpl_workflow(on-the-fly-detector-geometry-provider SOURCES onTheFlyDetectorGeometryProvider.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(on-the-fly-detector-geometry-provider - SOURCES onTheFlyDetectorGeometryProvider.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::ALICE3Core O2Physics::FastTracker - COMPONENT_NAME Analysis)