Skip to content

Commit 684cad7

Browse files
authored
Add barrel reco configParams to AOD metadata. (#15645)
* Method to extract ConfigurableParam as JSON string and update from such string * Add all barrel tracking configurables to AOD metadata Add all barrel tracking configurables to AOD metadata: write them as ConfigParam_<device>_<configname>.json files from every device (1st lane only in pipelined) Also, send them as META/<PROCNAME>/0 sporadic DPL output only once for the direct collection by the metadata writer (when it will be supported by the DPL). * Option to create configParams metadata from json dumps With the option `--collect-config-files` provided, the AODProducer will collect the json dumps of ConfigParams made by the upstream processes and will add them as `<configName> : <configParam json>` TObjString pairs in the AOD metadata. Once the DPL supports collecting META/<PROCNAME>/0 sporadic inputs, the metadata will be created directly from these inputs, so this option will not be needed for the data processing. But in the case of MC, collecting json files via this option is the only way to add configs to the AOD metadata (as it is done in the sim_challenge.sh).
1 parent 3af231b commit 684cad7

22 files changed

Lines changed: 437 additions & 55 deletions

File tree

Common/Utils/include/CommonUtils/ConfigurableParam.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ class ConfigurableParam
407407
// writes a human readable INI or JSON file depending on the extension
408408
static void write(std::string const& filename, std::string const& keyOnly = "");
409409

410+
static std::string asJSON(std::string const& keyOnly = "");
411+
410412
// can be used instead of using API on concrete child classes
411413
template <typename T>
412414
static T getValueAs(std::string key)
@@ -494,6 +496,9 @@ class ConfigurableParam
494496
// be updated, absence of data for any of requested params will lead to fatal
495497
static void updateFromFile(std::string const&, std::string const& paramsList = "", bool unchangedOnly = false);
496498

499+
// update from a JSON string with the same filtering semantics as updateFromFile
500+
static void updateFromJSONString(std::string const&, std::string const& paramsList = "", bool unchangedOnly = false);
501+
497502
// interface for use from the CCDB API; allows to sync objects read from CCDB with the information
498503
// stored in the registry; modifies given object as well as registry
499504
virtual void syncCCDBandRegistry(void* obj) = 0;

Common/Utils/src/ConfigurableParam.cxx

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#endif
3838
#include <cassert>
3939
#include <iostream>
40+
#include <sstream>
4041
#include <string>
4142
#include <fairlogger/Logger.h>
4243
#include <typeindex>
@@ -754,6 +755,29 @@ void ConfigurableParam::writeJSON(std::string const& filename, std::string const
754755

755756
// ------------------------------------------------------------------
756757

758+
std::string ConfigurableParam::asJSON(std::string const& keyOnly)
759+
{
760+
initPropertyTree(); // update the boost tree before writing
761+
std::ostringstream os;
762+
if (!keyOnly.empty()) { // write ini for selected key only
763+
try {
764+
boost::property_tree::ptree kTree;
765+
auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true);
766+
for (const auto& k : keys) {
767+
kTree.add_child(k, sPtree->get_child(k));
768+
}
769+
boost::property_tree::write_json(os, kTree);
770+
} catch (const boost::property_tree::ptree_bad_path& err) {
771+
LOG(fatal) << "non-existing key " << keyOnly << " provided to writeJSON";
772+
}
773+
} else {
774+
boost::property_tree::write_json(os, *sPtree);
775+
}
776+
return os.str();
777+
}
778+
779+
// ------------------------------------------------------------------
780+
757781
void ConfigurableParam::initPropertyTree()
758782
{
759783
sPtree->clear();
@@ -870,26 +894,10 @@ void ConfigurableParam::printAllRegisteredParamNames()
870894

871895
// ------------------------------------------------------------------
872896

873-
// Update the storage map of params from the given configuration file.
874-
// It can be in JSON or INI format.
875-
// If nonempty comma-separated paramsList is provided, only those params will
876-
// be updated, absence of data for any of requested params will lead to fatal
877-
// If unchangedOnly is true, then only those parameters whose provenance is kCODE will be updated
878-
// (to allow preference of run-time settings)
879-
void ConfigurableParam::updateFromFile(std::string const& configFile, std::string const& paramsList, bool unchangedOnly)
897+
namespace
898+
{
899+
void updateFromPropertyTree(boost::property_tree::ptree const& pt, std::string const& source, std::string const& paramsList, bool unchangedOnly)
880900
{
881-
if (!sIsFullyInitialized) {
882-
initialize();
883-
}
884-
885-
auto cfgfile = o2::utils::Str::trim_copy(configFile);
886-
887-
if (cfgfile.length() == 0) {
888-
return;
889-
}
890-
891-
boost::property_tree::ptree pt = ConfigurableParamReaders::readConfigFile(cfgfile);
892-
893901
std::vector<std::pair<std::string, std::string>> keyValPairs;
894902
auto request = o2::utils::Str::tokenize(paramsList, ',', true);
895903
std::unordered_map<std::string, int> requestMap;
@@ -913,7 +921,7 @@ void ConfigurableParam::updateFromFile(std::string const& configFile, std::strin
913921
auto name = subKey.first;
914922
auto value = subKey.second.get_value<std::string>();
915923
std::string key = mainKey + "." + name;
916-
if (!unchangedOnly || getProvenance(key) == kCODE) {
924+
if (!unchangedOnly || ConfigurableParam::getProvenance(key) == ConfigurableParam::kCODE) {
917925
std::pair<std::string, std::string> pair = std::make_pair(key, o2::utils::Str::trim_copy(value));
918926
keyValPairs.push_back(pair);
919927
}
@@ -928,16 +936,62 @@ void ConfigurableParam::updateFromFile(std::string const& configFile, std::strin
928936
// make sure all requested params were retrieved
929937
for (const auto& req : requestMap) {
930938
if (req.second == 0) {
931-
throw std::runtime_error(fmt::format("Param {:s} was not found in {:s}", req.first, configFile));
939+
throw std::runtime_error(fmt::format("Param {:s} was not found in {:s}", req.first, source));
932940
}
933941
}
934942

935943
try {
936-
setValues(keyValPairs);
944+
ConfigurableParam::setValues(keyValPairs);
937945
} catch (std::exception const& error) {
938946
LOG(error) << "Error while setting values " << error.what();
939947
}
940948
}
949+
} // namespace
950+
951+
// Update the storage map of params from the given configuration file.
952+
// It can be in JSON or INI format.
953+
// If nonempty comma-separated paramsList is provided, only those params will
954+
// be updated, absence of data for any of requested params will lead to fatal
955+
// If unchangedOnly is true, then only those parameters whose provenance is kCODE will be updated
956+
// (to allow preference of run-time settings)
957+
void ConfigurableParam::updateFromFile(std::string const& configFile, std::string const& paramsList, bool unchangedOnly)
958+
{
959+
if (!sIsFullyInitialized) {
960+
initialize();
961+
}
962+
963+
auto cfgfile = o2::utils::Str::trim_copy(configFile);
964+
965+
if (cfgfile.length() == 0) {
966+
return;
967+
}
968+
969+
updateFromPropertyTree(ConfigurableParamReaders::readConfigFile(cfgfile), configFile, paramsList, unchangedOnly);
970+
}
971+
972+
// ------------------------------------------------------------------
973+
974+
void ConfigurableParam::updateFromJSONString(std::string const& configJSON, std::string const& paramsList, bool unchangedOnly)
975+
{
976+
if (!sIsFullyInitialized) {
977+
initialize();
978+
}
979+
980+
auto json = o2::utils::Str::trim_copy(configJSON);
981+
if (json.length() == 0) {
982+
return;
983+
}
984+
985+
boost::property_tree::ptree pt;
986+
std::istringstream input(json);
987+
try {
988+
boost::property_tree::read_json(input, pt);
989+
} catch (const boost::property_tree::ptree_error& e) {
990+
LOG(fatal) << "Failed to read JSON config string (" << e.what() << ")";
991+
}
992+
993+
updateFromPropertyTree(pt, "provided JSON string", paramsList, unchangedOnly);
994+
}
941995

942996
// ------------------------------------------------------------------
943997
// ------------------------------------------------------------------

Common/Utils/test/testConfigurableParam.cxx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,42 @@ BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Json)
151151
std::remove(testFileName.c_str());
152152
}
153153

154+
BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_UnchangedOnly)
155+
{
156+
ConfigurableParam::setValue("TestParam.ulValue", "2");
157+
ConfigurableParam::setProvenance("TestParam", "lValue", ConfigurableParam::kCODE);
158+
ConfigurableParam::setProvenance("TestParam", "ulValue", ConfigurableParam::kRT);
159+
ConfigurableParam::updateFromJSONString(R"json({"TestParam":{"lValue":"77","ulValue":"88"}})json", "TestParam", true);
160+
161+
BOOST_CHECK_EQUAL(TestParam::Instance().lValue, 77);
162+
BOOST_CHECK_EQUAL(TestParam::Instance().ulValue, 2);
163+
}
164+
165+
BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_FromAsJSON)
166+
{
167+
ConfigurableParam::setValue("TestParam.iValue", "321");
168+
ConfigurableParam::setValue("TestParam.sValue", "json-source");
169+
ConfigurableParam::setValues({{"TestParam.map", "{7:8,9:10}"}});
170+
const auto mapBefore = TestParam::Instance().map;
171+
const auto json = ConfigurableParam::asJSON("TestParam");
172+
173+
ConfigurableParam::setValue("TestParam.iValue", "999");
174+
ConfigurableParam::setValue("TestParam.sValue", "json-modified");
175+
ConfigurableParam::setValues({{"TestParam.map", "{1:2}"}});
176+
ConfigurableParam::updateFromJSONString(json, "TestParam");
177+
178+
BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 321);
179+
BOOST_CHECK_EQUAL(TestParam::Instance().sValue, "json-source");
180+
BOOST_CHECK_EQUAL(TestParam::Instance().map.size(), mapBefore.size());
181+
BOOST_CHECK_EQUAL(TestParam::Instance().map.at(7), mapBefore.at(7));
182+
BOOST_CHECK_EQUAL(TestParam::Instance().map.at(9), mapBefore.at(9));
183+
}
184+
185+
BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_ParamsListMissing)
186+
{
187+
BOOST_CHECK_THROW(ConfigurableParam::updateFromJSONString(ConfigurableParam::asJSON("TestParam"), "MissingParam"), std::runtime_error);
188+
}
189+
154190
BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_ROOT)
155191
{
156192
// test for root file serialization

Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ class AODProducerWorkflowDPL : public Task
246246
return std::uint64_t(mStartIR.toLong()) + relativeTime_to_LocalBC(relativeTimeStampInNS);
247247
}
248248

249+
bool collectConfigFiles(std::vector<TString>& keys, std::vector<TString>& values, int indent = -1);
250+
249251
bool mThinTracks{false};
250252
bool mPropTracks{false};
251253
bool mPropMuons{false};
@@ -280,6 +282,7 @@ class AODProducerWorkflowDPL : public Task
280282
bool mEnableFITextra = false;
281283
bool mEnableTRDextra = false;
282284
bool mFieldON = false;
285+
bool mCollectConfigFiles = false;
283286
const float cSpeed = 0.029979246f; // speed of light in TOF units
284287

285288
GID::mask_t mInputSources;

Detectors/AOD/src/AODProducerWorkflowSpec.cxx

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
#include "Framework/TableBuilder.h"
5555
#include "Framework/CCDBParamSpec.h"
5656
#include "CommonUtils/TreeStreamRedirector.h"
57+
#include "CommonUtils/KeyValParam.h"
58+
#include "CommonUtils/NameConf.h"
5759
#include "FT0Base/Geometry.h"
5860
#include "GlobalTracking/MatchTOF.h"
5961
#include "ReconstructionDataFormats/Cascade.h"
@@ -88,6 +90,7 @@
8890
#include "MathUtils/Utils.h"
8991
#include "Math/SMatrix.h"
9092
#include "TString.h"
93+
#include <fnmatch.h>
9194
#include <limits>
9295
#include <map>
9396
#include <numeric>
@@ -1902,6 +1905,8 @@ void AODProducerWorkflowDPL::init(InitContext& ic)
19021905

19031906
mUseSigFiltMC = ic.options().get<bool>("mc-signal-filt");
19041907

1908+
mCollectConfigFiles = ic.options().get<bool>("collect-config-files");
1909+
19051910
// set no truncation if selected by user
19061911
if (mTruncate != 1) {
19071912
LOG(info) << "Truncation is not used!";
@@ -2670,6 +2675,10 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc)
26702675
mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag, mUser};
26712676
add_additional_meta_info(mMetaDataKeys, mMetaDataVals);
26722677

2678+
if (mCollectConfigFiles) {
2679+
collectConfigFiles(mMetaDataKeys, mMetaDataVals);
2680+
}
2681+
26732682
pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, mMetaDataKeys);
26742683
pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, mMetaDataVals);
26752684

@@ -3405,6 +3414,102 @@ std::vector<uint8_t> AODProducerWorkflowDPL::fillBCFlags(const o2::globaltrackin
34053414
return flags;
34063415
}
34073416

3417+
bool AODProducerWorkflowDPL::collectConfigFiles(std::vector<TString>& keys, std::vector<TString>& values, int indent)
3418+
{
3419+
// collect JSON-files of ConfigParams dumped by different upstream processors and add to medata
3420+
static std::string pattern, directory;
3421+
static size_t cachedNumberOfFiles = 0, cachedTotalFileSize = 0;
3422+
static bool first = true, discard = false;
3423+
if (discard) {
3424+
return false;
3425+
}
3426+
std::error_code ec;
3427+
if (first) {
3428+
first = false;
3429+
pattern = o2::base::NameConf::Instance().getConfigOutputFileName("*");
3430+
auto dir = o2::conf::KeyValParam::Instance().getOutputDir();
3431+
if (dir == "/dev/null") {
3432+
LOGP(warn, "ConfigParams output is disabled, abandoning {} files collection for metadata", pattern);
3433+
discard = true;
3434+
return false;
3435+
}
3436+
directory = (dir.empty() || dir == "none") ? "." : dir;
3437+
if (!std::filesystem::is_directory(directory, ec)) {
3438+
LOGP(error, R"(No directory "{}" is found to look for {} configuration files)", directory, pattern);
3439+
discard = true;
3440+
return false;
3441+
}
3442+
}
3443+
static std::unordered_map<std::string, std::string> cachedMap;
3444+
std::vector<std::filesystem::path> files;
3445+
size_t currentTotalFileSize = 0;
3446+
3447+
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
3448+
if (!entry.is_regular_file()) {
3449+
continue;
3450+
}
3451+
const std::string fileName = entry.path().filename().string();
3452+
if (fnmatch(pattern.c_str(), fileName.c_str(), 0) != 0) {
3453+
continue;
3454+
}
3455+
const auto fileSize = entry.file_size(ec);
3456+
if (ec) {
3457+
LOGP(error, "Cannot determine size of file {}, reason: {}", entry.path().string(), ec.message());
3458+
}
3459+
files.push_back(entry.path());
3460+
currentTotalFileSize += static_cast<size_t>(fileSize);
3461+
}
3462+
3463+
if (files.size() != cachedNumberOfFiles || currentTotalFileSize != cachedTotalFileSize) { // need to create a new map
3464+
cachedNumberOfFiles = files.size();
3465+
cachedTotalFileSize = currentTotalFileSize;
3466+
cachedMap.clear();
3467+
}
3468+
3469+
if (!files.empty() && cachedMap.empty()) {
3470+
for (const auto& fname : files) {
3471+
std::ifstream input(fname);
3472+
if (!input) {
3473+
LOGP(error, "Cannot open JSON file {}", fname.string());
3474+
cachedTotalFileSize = 0; // will trigger a new trial next time
3475+
continue;
3476+
}
3477+
nlohmann::json document;
3478+
try {
3479+
input >> document;
3480+
} catch (const nlohmann::json::parse_error& e) {
3481+
LOGP(error, "Cannot parse JSON file {}, reason: {}", fname.string(), e.what());
3482+
cachedTotalFileSize = 0; // will trigger a new trial next time
3483+
continue;
3484+
}
3485+
3486+
if (!document.is_object()) {
3487+
LOGP(error, "Top-level JSON value is not an object in file: {}", fname.string());
3488+
cachedTotalFileSize = 0; // will trigger a new trial next time
3489+
continue;
3490+
}
3491+
3492+
for (auto it = document.begin(); it != document.end(); ++it) {
3493+
const std::string& key = it.key();
3494+
if (cachedMap.find(key) != cachedMap.end()) {
3495+
LOGP(error, "Duplicate top-level key {} in file {}", key, fname.string());
3496+
continue;
3497+
}
3498+
LOGP(info, "Adding json config {} from file {} to AOD metadata", key, fname.string());
3499+
nlohmann::json valueDocument = nlohmann::json::object();
3500+
valueDocument[key] = it.value();
3501+
cachedMap[key] = valueDocument.dump(indent);
3502+
}
3503+
}
3504+
}
3505+
3506+
for (const auto& kv : cachedMap) {
3507+
keys.push_back(kv.first.c_str());
3508+
values.push_back(kv.second.c_str());
3509+
}
3510+
return true;
3511+
}
3512+
34083513
void AODProducerWorkflowDPL::endOfStream(EndOfStreamContext& /*ec*/)
34093514
{
34103515
LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots",
@@ -3565,7 +3670,9 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo
35653670
ConfigParamSpec{"trackqc-tpc-pt", VariantType::Float, 0.2f, {"Keep TPC standalone track with this pt"}},
35663671
ConfigParamSpec{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}},
35673672
ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}},
3568-
ConfigParamSpec{"mc-signal-filt", VariantType::Bool, false, {"Enable usage of signal filtering (only for MC with embedding)"}}}};
3673+
ConfigParamSpec{"mc-signal-filt", VariantType::Bool, false, {"Enable usage of signal filtering (only for MC with embedding)"}},
3674+
ConfigParamSpec{"collect-config-files", VariantType::Bool, false, {"Collect ConfigParams json files written by upsteam processors"}},
3675+
}};
35693676
}
35703677

35713678
} // namespace o2::aodproducer

Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class StrangenessTrackerSpec : public framework::Task
5050

5151
private:
5252
void updateTimeDependentParams(framework::ProcessingContext& pc);
53+
void storeConfigs(framework::ProcessingContext& pc);
5354

5455
bool mUseMC = false;
5556
TStopwatch mTimer;

0 commit comments

Comments
 (0)