From f4b03bf3f1cbb290bc4a709e35521e9b83504b75 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 26 Feb 2026 17:41:35 +0100 Subject: [PATCH 1/2] Extra support for dumping ConfigurableParam to ini/json files --- .../include/CommonUtils/ConfigurableParam.h | 3 ++ Common/Utils/include/CommonUtils/NameConf.h | 3 ++ .../Utils/include/CommonUtils/StringUtils.h | 3 ++ Common/Utils/src/ConfigurableParam.cxx | 23 +++++++++++++-- Common/Utils/src/NameConf.cxx | 9 ++++++ Common/Utils/src/StringUtils.cxx | 29 +++++++++++++++++++ 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Common/Utils/include/CommonUtils/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 39b24bbbbd57c..b9234926b7c40 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -187,6 +187,9 @@ class ConfigurableParam // writes a human readable INI file of all parameters static void writeINI(std::string const& filename, std::string const& keyOnly = ""); + // writes a human readable INI or JSON file depending on the extension + static void write(std::string const& filename, std::string const& keyOnly = ""); + // can be used instead of using API on concrete child classes template static T getValueAs(std::string key) diff --git a/Common/Utils/include/CommonUtils/NameConf.h b/Common/Utils/include/CommonUtils/NameConf.h index 8a09a903bf32f..fb10f929c9782 100644 --- a/Common/Utils/include/CommonUtils/NameConf.h +++ b/Common/Utils/include/CommonUtils/NameConf.h @@ -103,6 +103,9 @@ class NameConf : public o2::conf::ConfigurableParamHelper // Default CCDB server static std::string getCCDBServer(); + // create name to dump config file + static std::string getConfigOutputFileName(const std::string& procName, const std::string& confName = "", bool json = true); + protected: // helper method to build filenames static std::string buildFileName(const std::string_view prefix, const std::string_view delimiter, const std::string_view defPrefix, const std::string_view defName, diff --git a/Common/Utils/include/CommonUtils/StringUtils.h b/Common/Utils/include/CommonUtils/StringUtils.h index 710632fc7dbfe..cfe29e065a78e 100644 --- a/Common/Utils/include/CommonUtils/StringUtils.h +++ b/Common/Utils/include/CommonUtils/StringUtils.h @@ -136,6 +136,9 @@ struct Str { // return vector of tokens from the string with provided delimiter. If requested, trim the spaces from tokens static std::vector tokenize(const std::string& src, char delim, bool trimToken = true, bool skipEmpty = true); + // return vector of tokens from the string with provided delimiters. If requested, trim the spaces from tokens + static std::vector tokenize(const std::string& src, const std::string& delim, bool trimToken = true, bool skipEmpty = true); + // concatenate arbitrary number of strings template static std::string concat_string(Ts const&... ts) diff --git a/Common/Utils/src/ConfigurableParam.cxx b/Common/Utils/src/ConfigurableParam.cxx index 8497a485fca39..fd69f51402cd5 100644 --- a/Common/Utils/src/ConfigurableParam.cxx +++ b/Common/Utils/src/ConfigurableParam.cxx @@ -192,6 +192,19 @@ int EnumLegalValues::getIntValue(const std::string& value) const // ----------------------------------------------------------------- +void ConfigurableParam::write(std::string const& filename, std::string const& keyOnly) +{ + if (o2::utils::Str::endsWith(filename, ".ini")) { + writeINI(filename, keyOnly); + } else if (o2::utils::Str::endsWith(filename, ".json")) { + writeJSON(filename, keyOnly); + } else { + throw std::invalid_argument(fmt::format("ConfigurabeParam output file name {} extension is neither .json nor .ini", filename)); + } +} + +// ----------------------------------------------------------------- + void ConfigurableParam::writeINI(std::string const& filename, std::string const& keyOnly) { if (sOutputDir == "/dev/null") { @@ -203,7 +216,10 @@ void ConfigurableParam::writeINI(std::string const& filename, std::string const& if (!keyOnly.empty()) { // write ini for selected key only try { boost::property_tree::ptree kTree; - kTree.add_child(keyOnly, sPtree->get_child(keyOnly)); + auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true); + for (const auto& k : keys) { + kTree.add_child(k, sPtree->get_child(k)); + } boost::property_tree::write_ini(outfilename, kTree); } catch (const boost::property_tree::ptree_bad_path& err) { LOG(fatal) << "non-existing key " << keyOnly << " provided to writeINI"; @@ -284,7 +300,10 @@ void ConfigurableParam::writeJSON(std::string const& filename, std::string const if (!keyOnly.empty()) { // write ini for selected key only try { boost::property_tree::ptree kTree; - kTree.add_child(keyOnly, sPtree->get_child(keyOnly)); + auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true); + for (const auto& k : keys) { + kTree.add_child(k, sPtree->get_child(k)); + } boost::property_tree::write_json(outfilename, kTree); } catch (const boost::property_tree::ptree_bad_path& err) { LOG(fatal) << "non-existing key " << keyOnly << " provided to writeJSON"; diff --git a/Common/Utils/src/NameConf.cxx b/Common/Utils/src/NameConf.cxx index 5a5f644f2da39..45646284a878b 100644 --- a/Common/Utils/src/NameConf.cxx +++ b/Common/Utils/src/NameConf.cxx @@ -111,3 +111,12 @@ std::string NameConf::getCCDBServer() { return Instance().mCCDBServer; } + +std::string NameConf::getConfigOutputFileName(const std::string& procName, const std::string& confName, bool json) +{ + std::string nm = procName; + if (!confName.empty()) { + nm += '_' + confName; + } + return fmt::format("ConfigParam_{}.{}", nm, json ? "json" : "ini"); +} diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 687225d069ed2..29c43ec18375b 100644 --- a/Common/Utils/src/StringUtils.cxx +++ b/Common/Utils/src/StringUtils.cxx @@ -37,6 +37,35 @@ std::vector Str::tokenize(const std::string& src, char delim, bool return tokens; } +std::vector Str::tokenize(const std::string& src, const std::string& delim, bool trimToken, bool skipEmpty) +{ + std::string inptStr{src}; + char* input = inptStr.data(); + auto mystrtok = [&]() -> char* { + input += std::strspn(input, delim.c_str()); + if (*input == '\0') { + return nullptr; + } + char* const token = input; + input += std::strcspn(input, delim.c_str()); + if (*input != '\0') { + *input++ = '\0'; + } + return token; + }; + std::vector tokens; + while (*input != '\0') { + std::string token = mystrtok(); + if (trimToken) { + trim(token); + } + if (!token.empty() || !skipEmpty) { + tokens.push_back(std::move(token)); + } + } + return tokens; +} + // replace all occurencies of from by to, return count int Str::replaceAll(std::string& s, const std::string& from, const std::string& to) { From 0e0e8f6faef35e077362dc7b4678d49bea0eb838 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 26 Feb 2026 17:45:32 +0100 Subject: [PATCH 2/2] Dump uniformly important reco ConfigParams to be collected in AO2D metadata --- .../src/PrimaryVertexingSpec.cxx | 8 ++++++++ .../src/SecondaryVertexingSpec.cxx | 11 +++++++++++ .../GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx | 9 +++++++++ .../src/TPCITSMatchingSpec.cxx | 8 ++++++++ Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx | 9 +++++++++ Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx | 9 +++++++++ .../TRD/workflow/src/TRDGlobalTrackingSpec.cxx | 9 +++++++++ GPU/Workflow/src/GPUWorkflowITS.cxx | 14 +++++++++++++- GPU/Workflow/src/GPUWorkflowSpec.cxx | 7 +++++++ 9 files changed, 83 insertions(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx index d71a4fad7ab78..dc1107bacb18a 100644 --- a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx @@ -197,6 +197,14 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) mVertexer.getTimeReAttach().CpuTime(), mVertexer.getTotTrials(), mVertexer.getNTZClusters(), mVertexer.getMaxTrialsPerCluster(), mVertexer.getLongestClusterTimeMS(), mVertexer.getLongestClusterMult(), mVertexer.getNIniFound(), mVertexer.getNKilledBCValid(), mVertexer.getNKilledIntCand(), mVertexer.getNKilledDebris(), mVertexer.getNKilledQuality(), mVertexer.getNKilledITSOnly()); + + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, PVertexerParams::Instance().getName()), PVertexerParams::Instance().getName()); + } + } } void PrimaryVertexingSpec::endOfStream(EndOfStreamContext& ec) diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index ea566f15a0b59..1b55f9c763e7f 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -128,6 +128,17 @@ void SecondaryVertexingSpec::run(ProcessingContext& pc) mVertexer.getNV0s(), calls[0] - fitCalls[0], mVertexer.getNCascades(), calls[1] - fitCalls[1], mVertexer.getN3Bodies(), calls[2] - fitCalls[2], mVertexer.getNStrangeTracks(), mTimer.CpuTime() - timeCPU0, mTimer.RealTime() - timeReal0); fitCalls = calls; + + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, SVertexerParams::Instance().getName()), SVertexerParams::Instance().getName()); + if (mEnableStrangenessTracking) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::strangeness_tracking::StrangenessTrackingParamConfig::Instance().getName()), o2::strangeness_tracking::StrangenessTrackingParamConfig::Instance().getName()); + } + } + } } void SecondaryVertexingSpec::endOfStream(EndOfStreamContext& ec) diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx index 3f6e79e433635..8081c48e390d3 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -22,6 +22,7 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" +#include "Framework/DeviceSpec.h" #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" @@ -229,6 +230,14 @@ void TOFMatcherSpec::run(ProcessingContext& pc) pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_17", 0}, mMatcher.getMatchedTracksPair(17)); } + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, MatchTOFParams::Instance().getName()), MatchTOFParams::Instance().getName()); + } + } + mTimer.Stop(); } diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index 14af8c12794cc..c333c37ff245b 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -130,6 +130,14 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) mMatching.run(recoData, matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, MatchTPCITSParams::Instance().getName()), MatchTPCITSParams::Instance().getName()); + } + } + mTimer.Stop(); } diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index 12d84ca7ab6ad..3d07048aaf1e6 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx @@ -14,6 +14,7 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITStracking/Definitions.h" #include "ITStracking/TrackingConfigParam.h" @@ -60,6 +61,14 @@ void TrackerDPL::run(ProcessingContext& pc) mITSTrackingInterface.run(pc); mTimer.Stop(); LOGP(info, "CPU Reconstruction time for this TF {:.2f} s (cpu), {:.2f} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::VertexerParamConfig::Instance().getName()), o2::its::VertexerParamConfig::Instance().getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::TrackerParamConfig::Instance().getName()), o2::its::TrackerParamConfig::Instance().getName()); + } + } } void TrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx index d8e15590474ec..3e726fe37c38c 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx @@ -24,6 +24,7 @@ #include "TGeoGlobalMagField.h" +#include "Framework/DeviceSpec.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" @@ -331,6 +332,14 @@ void TrackerDPL::run(ProcessingContext& pc) pc.outputs().snapshot(Output{"MFT", "TRACKSMC2ROF", 0}, mc2rofs); } + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::mft::MFTTrackingParam::Instance().getName()), o2::mft::MFTTrackingParam::Instance().getName()); + } + } + mTimer[SWTot].Stop(); } diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index f2d4aad829fe5..9e7ef089faeef 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -28,6 +28,7 @@ #include "GPUWorkflowHelper/GPUWorkflowHelper.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include "DataFormatsTPC/WorkflowHelper.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "CommonConstants/GeomConstants.h" @@ -554,6 +555,14 @@ void TRDGlobalTracking::run(ProcessingContext& pc) } } + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, "GPU_rec_trd"), "GPU_rec_trd"); + } + } + mTimer.Stop(); } diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx index b1c8d619ec736..587b85df98952 100644 --- a/GPU/Workflow/src/GPUWorkflowITS.cxx +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -18,8 +18,11 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" - +#include "Framework/DeviceSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/NameConf.h" #include "ITStracking/TrackingInterface.h" +#include "ITStracking/TrackingConfigParam.h" #ifdef ENABLE_UPGRADES #include "ITS3Reconstruction/TrackingInterface.h" @@ -33,6 +36,15 @@ int32_t GPURecoWorkflowSpec::runITSTracking(o2::framework::ProcessingContext& pc mITSTimeFrame->setDevicePropagator(mGPUReco->GetDeviceO2Propagator()); LOGP(debug, "GPUChainITS is giving me device propagator: {}", (void*)mGPUReco->GetDeviceO2Propagator()); mITSTrackingInterface->run(pc); + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::VertexerParamConfig::Instance().getName()), o2::its::VertexerParamConfig::Instance().getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::TrackerParamConfig::Instance().getName()), o2::its::TrackerParamConfig::Instance().getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::ITSGpuTrackingParamConfig::Instance().getName()), o2::its::ITSGpuTrackingParamConfig::Instance().getName()); + } + } return 0; } diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index a8f95841a4dc9..48210c440d01e 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -514,6 +514,13 @@ int32_t GPURecoWorkflowSpec::runMain(o2::framework::ProcessingContext* pc, GPUTr if (retVal == 0 && mSpecConfig.runITSTracking) { retVal = runITSTracking(*pc); } + static bool first = true; + if (first) { + first = false; + if (pc->services().get().inputTimesliceId == 0) { // TPC ConfigurableCarams are somewhat special, need to construct by hand + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc->services().get().name, "rec_tpc"), "GPU_rec_tpc,GPU_rec,GPU_proc_param,GPU_proc,GPU_global,trackTuneParams"); + } + } } if (!mSpecConfig.enableDoublePipeline) { // TODO: Why is this needed for double-pipeline?