From 87130c28b2255791ece3389823eb0209cee7e5ee Mon Sep 17 00:00:00 2001 From: David Dobrigkeit Chinellato Date: Thu, 30 Oct 2025 10:47:06 -0300 Subject: [PATCH 1/4] [Common] Add autodetection of TPC-only PIDTPC Nsigma need and switch evaluation on/off --- Common/Tools/PID/pidTPCModule.h | 51 ++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index 1c6a35048f7..0eb32c507ba 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -91,7 +92,7 @@ struct pidTPCConfigurables : o2::framework::ConfigurableGroup { // Parameters for loading network from a file / downloading the file o2::framework::Configurable useNetworkCorrection{"useNetworkCorrection", 0, "(bool) Wether or not to use the network correction for the TPC dE/dx signal"}; o2::framework::Configurable autofetchNetworks{"autofetchNetworks", 1, "(bool) Automatically fetches networks from CCDB for the correct run number"}; - o2::framework::Configurable skipTPCOnly{"skipTPCOnly", false, "Flag to skip TPC only tracks (faster but affects the analyses that use TPC only tracks)"}; + o2::framework::Configurable skipTPCOnly{"skipTPCOnly", -1, "Flag to skip TPC only tracks (faster but affects the analyses that use TPC only tracks). 0: do not skip, 1: skip, -1: check if needed by specific tasks"}; o2::framework::Configurable networkPathLocally{"networkPathLocally", "network.onnx", "(std::string) Path to the local .onnx file. If autofetching is enabled, then this is where the files will be downloaded"}; o2::framework::Configurable networkPathCCDB{"networkPathCCDB", "Analysis/PID/TPC/ML", "Path on CCDB"}; o2::framework::Configurable enableNetworkOptimizations{"enableNetworkOptimizations", 1, "(bool) If the neural network correction is used, this enables GraphOptimizationLevel::ORT_ENABLE_EXTENDED in the ONNX session"}; @@ -244,6 +245,54 @@ class pidTPCModule LOGF(info, "***************************************************"); } + if (pidTPCopts.skipTPCOnly.value == -1) { + LOGF(info, "***************************************************"); + LOGF(info, " the skipTPConly flag has a value of -1! "); + LOGF(info, " ---> autodetecting TPC-only track necessity now "); + LOGF(info, "***************************************************"); + + // assume that TPC tracks are not needed, but check if tasks + // requiring them are present in the chain + pidTPCopts.skipTPCOnly.value = 1; + + // loop over devices in this execution + auto& workflows = context.services().template get(); + for (o2::framework::DeviceSpec const& device : workflows.devices) { + + // Check 1: the photon builder (in any configuration) needs TPC only + if (device.name.compare("photon-conversion-builder") == 0) { + LOGF(info, " ---> photon conversion builder detected! "); + LOGF(info, " ---> enabling TPC only track TPC PID calculations now."); + pidTPCopts.skipTPCOnly.value = 0; + } + + // Check 2: propagation service with generation of photons + if (device.name.compare("propagation-service") == 0) { + LOGF(info, " ---> propagation service detected, checking if photons enabled..."); + for (auto const& option : device.options) { + // check for photon generation enabled or not + if (option.name.compare("v0BuilderOpts.generatePhotonCandidates") == 0) { + if (option.defaultValue.get()) { + LOGF(info, " ---> propagation service: photons enabled, will calculate TPC PID for TPC only tracks."); + pidTPCopts.skipTPCOnly.value = 0; + } else { + LOGF(info, " ---> propagation service: photons disabled, TPC PID not required for TPC-only tracks"); + } + } + } + } + + // if extra tasks require TPC PID and enabling is to be automatic, + // this is the place where one should add the conditionals. + } + + if (pidTPCopts.skipTPCOnly.value == 1) { + LOGF(info, "No need for TPC only information detected. Will not generate Nsigma for TPC only tracks"); + LOGF(info, "If this is unexpected behaviour and a necessity was not identified, please add the"); + LOGF(info, "corresponding task to the list in pidTPCModule::Init()."); + } + } + // initialize PID response response = new o2::pid::tpc::Response(); From f6623eaaf45f16d400053feea978d0517687a317 Mon Sep 17 00:00:00 2001 From: David Dobrigkeit Chinellato Date: Thu, 30 Oct 2025 23:03:14 -0300 Subject: [PATCH 2/4] Small simplifications --- Common/Tools/PID/pidTPCModule.h | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index 0eb32c507ba..656053a8cd4 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -258,15 +258,7 @@ class pidTPCModule // loop over devices in this execution auto& workflows = context.services().template get(); for (o2::framework::DeviceSpec const& device : workflows.devices) { - - // Check 1: the photon builder (in any configuration) needs TPC only - if (device.name.compare("photon-conversion-builder") == 0) { - LOGF(info, " ---> photon conversion builder detected! "); - LOGF(info, " ---> enabling TPC only track TPC PID calculations now."); - pidTPCopts.skipTPCOnly.value = 0; - } - - // Check 2: propagation service with generation of photons + // Look for propagation service if (device.name.compare("propagation-service") == 0) { LOGF(info, " ---> propagation service detected, checking if photons enabled..."); for (auto const& option : device.options) { @@ -282,15 +274,31 @@ class pidTPCModule } } - // if extra tasks require TPC PID and enabling is to be automatic, + // if extra tasks require TPC PID for TPC-only tracks and enabling is to be automatic, // this is the place where one should add the conditionals. + // + // note: this should be the device name (the name that gets printed in the + // logs when executing the task) and it is sufficient to just extend this array + // with the corresponding string. + std::vector devicesRequiringTPCOnlyPID = {"photon-conversion-builder"}; + + // Check 2: specific tasks that require TPC PID + for (const std::string requiringDevice : devicesRequiringTPCOnlyPID) { + if (device.name.compare(requiringDevice) == 0) { + LOGF(info, " ---> %s detected! ", requiringDevice); + LOGF(info, " ---> enabling TPC only track TPC PID calculations now."); + pidTPCopts.skipTPCOnly.value = 0; + } + } } if (pidTPCopts.skipTPCOnly.value == 1) { + LOGF(info, "***************************************************"); LOGF(info, "No need for TPC only information detected. Will not generate Nsigma for TPC only tracks"); LOGF(info, "If this is unexpected behaviour and a necessity was not identified, please add the"); - LOGF(info, "corresponding task to the list in pidTPCModule::Init()."); + LOGF(info, "corresponding task to the list 'devicesRequiringTPCOnlyPID' in pidTPCModule::Init()"); } + LOGF(info, "***************************************************"); } // initialize PID response From 55f3eea38f562bd17a4a1a3852537d53534eede2 Mon Sep 17 00:00:00 2001 From: David Dobrigkeit Chinellato Date: Thu, 30 Oct 2025 23:14:44 -0300 Subject: [PATCH 3/4] Fix linter warning --- Common/Tools/PID/pidTPCModule.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index 656053a8cd4..a7576dea869 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -283,7 +283,7 @@ class pidTPCModule std::vector devicesRequiringTPCOnlyPID = {"photon-conversion-builder"}; // Check 2: specific tasks that require TPC PID - for (const std::string requiringDevice : devicesRequiringTPCOnlyPID) { + for (const std::string& requiringDevice : devicesRequiringTPCOnlyPID) { if (device.name.compare(requiringDevice) == 0) { LOGF(info, " ---> %s detected! ", requiringDevice); LOGF(info, " ---> enabling TPC only track TPC PID calculations now."); From d6172fdcba63c3a0df6c8dcd7e3b14c53682656a Mon Sep 17 00:00:00 2001 From: David Dobrigkeit Chinellato Date: Fri, 31 Oct 2025 02:36:36 -0300 Subject: [PATCH 4/4] Configurable list of TPC only requirers, fix run 2 bug --- Common/Tools/PID/pidTPCModule.h | 88 +++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index a7576dea869..7ceb7504881 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -93,6 +93,7 @@ struct pidTPCConfigurables : o2::framework::ConfigurableGroup { o2::framework::Configurable useNetworkCorrection{"useNetworkCorrection", 0, "(bool) Wether or not to use the network correction for the TPC dE/dx signal"}; o2::framework::Configurable autofetchNetworks{"autofetchNetworks", 1, "(bool) Automatically fetches networks from CCDB for the correct run number"}; o2::framework::Configurable skipTPCOnly{"skipTPCOnly", -1, "Flag to skip TPC only tracks (faster but affects the analyses that use TPC only tracks). 0: do not skip, 1: skip, -1: check if needed by specific tasks"}; + o2::framework::Configurable> devicesRequiringTPCOnlyPID{"devicesRequiringTPCOnlyPID", std::vector{"photon-conversion-builder"}, "List of device names of tasks requiring TPC-only tracks to have TPC PID calculated"}; o2::framework::Configurable networkPathLocally{"networkPathLocally", "network.onnx", "(std::string) Path to the local .onnx file. If autofetching is enabled, then this is where the files will be downloaded"}; o2::framework::Configurable networkPathCCDB{"networkPathCCDB", "Analysis/PID/TPC/ML", "Path on CCDB"}; o2::framework::Configurable enableNetworkOptimizations{"enableNetworkOptimizations", 1, "(bool) If the neural network correction is used, this enables GraphOptimizationLevel::ORT_ENABLE_EXTENDED in the ONNX session"}; @@ -250,6 +251,11 @@ class pidTPCModule LOGF(info, " the skipTPConly flag has a value of -1! "); LOGF(info, " ---> autodetecting TPC-only track necessity now "); LOGF(info, "***************************************************"); + // print list of devices that are being checked for + for (std::size_t devIdx{0}; devIdx < pidTPCopts.devicesRequiringTPCOnlyPID->size(); devIdx++) { + LOGF(info, "Will search for #%i device requiring TPC PID for TPC only: %s", devIdx, pidTPCopts.devicesRequiringTPCOnlyPID->at(devIdx)); + } + LOGF(info, "***************************************************"); // assume that TPC tracks are not needed, but check if tasks // requiring them are present in the chain @@ -274,18 +280,10 @@ class pidTPCModule } } - // if extra tasks require TPC PID for TPC-only tracks and enabling is to be automatic, - // this is the place where one should add the conditionals. - // - // note: this should be the device name (the name that gets printed in the - // logs when executing the task) and it is sufficient to just extend this array - // with the corresponding string. - std::vector devicesRequiringTPCOnlyPID = {"photon-conversion-builder"}; - - // Check 2: specific tasks that require TPC PID - for (const std::string& requiringDevice : devicesRequiringTPCOnlyPID) { - if (device.name.compare(requiringDevice) == 0) { - LOGF(info, " ---> %s detected! ", requiringDevice); + // Check 2: specific tasks that require TPC PID based on configurable + for (std::size_t devIdx{0}; devIdx < pidTPCopts.devicesRequiringTPCOnlyPID->size(); devIdx++) { + if (device.name.compare(pidTPCopts.devicesRequiringTPCOnlyPID->at(devIdx)) == 0) { + LOGF(info, " ---> %s detected! ", pidTPCopts.devicesRequiringTPCOnlyPID->at(devIdx)); LOGF(info, " ---> enabling TPC only track TPC PID calculations now."); pidTPCopts.skipTPCOnly.value = 0; } @@ -387,12 +385,16 @@ class pidTPCModule LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << time << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"]; metadata["RecoPassName"] = headers["RecoPassName"]; // Force pass number for NN request to match retrieved BB o2::parameters::GRPLHCIFData* grpo = ccdb->template getForTimeStamp(pidTPCopts.cfgPathGrpLhcIf.value, time); - LOG(info) << " collision type::" << CollisionSystemType::getCollisionTypeFromGrp(grpo); - collsys = CollisionSystemType::getCollisionTypeFromGrp(grpo); - if (collsys == CollisionSystemType::kCollSyspp) { - irSource = std::string("T0VTX"); + if (grpo) { + LOG(info) << " collision type::" << CollisionSystemType::getCollisionTypeFromGrp(grpo); + collsys = CollisionSystemType::getCollisionTypeFromGrp(grpo); + if (collsys == CollisionSystemType::kCollSyspp) { + irSource = std::string("T0VTX"); + } else { + irSource = std::string("ZNC hadronic"); + } } else { - irSource = std::string("ZNC hadronic"); + LOGF(info, "No grpo object found. irSource will remain undefined."); } response->PrintAll(); } @@ -469,12 +471,16 @@ class pidTPCModule LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << bc.timestamp() << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"]; metadata["RecoPassName"] = headers["RecoPassName"]; // Force pass number for NN request to match retrieved BB o2::parameters::GRPLHCIFData* grpo = ccdb->template getForTimeStamp(pidTPCopts.cfgPathGrpLhcIf.value, bc.timestamp()); - LOG(info) << "Collision type::" << CollisionSystemType::getCollisionTypeFromGrp(grpo); - collsys = CollisionSystemType::getCollisionTypeFromGrp(grpo); - if (collsys == CollisionSystemType::kCollSyspp) { - irSource = std::string("T0VTX"); + if (grpo) { + LOG(info) << "Collision type::" << CollisionSystemType::getCollisionTypeFromGrp(grpo); + collsys = CollisionSystemType::getCollisionTypeFromGrp(grpo); + if (collsys == CollisionSystemType::kCollSyspp) { + irSource = std::string("T0VTX"); + } else { + irSource = std::string("ZNC hadronic"); + } } else { - irSource = std::string("ZNC hadronic"); + LOGF(info, "No grpo object found. irSource will remain undefined."); } response->PrintAll(); } @@ -515,11 +521,19 @@ class pidTPCModule size_t i = 0; for (const auto& collision : collisions) { const auto& bc = collision.template bc_as(); - hadronicRateForCollision[i] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + if (irSource.compare("") != 0) { + hadronicRateForCollision[i] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + } else { + hadronicRateForCollision[i] = 0.0f; + } i++; } auto bc = bcs.begin(); - hadronicRateBegin = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; // kHz + if (irSource.compare("") != 0) { + hadronicRateBegin = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; // kHz + } else { + hadronicRateBegin = 0.0f; + } // Filling a std::vector to be evaluated by the network // Evaluation on single tracks brings huge overhead: Thus evaluation is done on one large vector @@ -742,11 +756,19 @@ class pidTPCModule size_t i = 0; for (const auto& collision : cols) { const auto& bc = collision.template bc_as(); - hadronicRateForCollision[i] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + if (irSource.compare("") != 0) { + hadronicRateForCollision[i] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + } else { + hadronicRateForCollision[i] = 0.0f; + } i++; } auto bc = bcs.begin(); - hadronicRateBegin = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; // kHz + if (irSource.compare("") != 0) { + hadronicRateBegin = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; // kHz + } else { + hadronicRateBegin = 0.0f; + } } for (auto const& trk : tracks) { @@ -854,12 +876,16 @@ class pidTPCModule } LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << bc.timestamp() << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"]; o2::parameters::GRPLHCIFData* grpo = ccdb->template getForTimeStamp(pidTPCopts.cfgPathGrpLhcIf.value, bc.timestamp()); - LOG(info) << "Collisions type::" << CollisionSystemType::getCollisionTypeFromGrp(grpo); - collsys = CollisionSystemType::getCollisionTypeFromGrp(grpo); - if (collsys == CollisionSystemType::kCollSyspp) { - irSource = std::string("T0VTX"); + if (grpo) { + LOG(info) << "Collisions type::" << CollisionSystemType::getCollisionTypeFromGrp(grpo); + collsys = CollisionSystemType::getCollisionTypeFromGrp(grpo); + if (collsys == CollisionSystemType::kCollSyspp) { + irSource = std::string("T0VTX"); + } else { + irSource = std::string("ZNC hadronic"); + } } else { - irSource = std::string("ZNC hadronic"); + LOGF(info, "No grpo object found. irSource will remain undefined."); } response->PrintAll(); }