diff --git a/src/cpp/CMakeLists.txt b/src/cpp/CMakeLists.txt index 6c304cb38..ac35da22f 100644 --- a/src/cpp/CMakeLists.txt +++ b/src/cpp/CMakeLists.txt @@ -460,6 +460,7 @@ ADD_LCIO_EXAMPLE( stdhepjob ) ADD_LCIO_EXAMPLE( stdhepjob_new ) ADD_LCIO_EXAMPLE( addRandomAccess ) ADD_LCIO_EXAMPLE( check_missing_cols ) +ADD_LCIO_EXAMPLE( check_col_params ) ADD_LCIO_EXAMPLE( patch_missing_cols ) ADD_LCIO_EXAMPLE( lcio_event_counter ) ADD_LCIO_EXAMPLE( lcio_check_col_elements ) diff --git a/src/cpp/include/IMPL/LCParametersImpl.h b/src/cpp/include/IMPL/LCParametersImpl.h index 2d157ec23..e5bd1481b 100644 --- a/src/cpp/include/IMPL/LCParametersImpl.h +++ b/src/cpp/include/IMPL/LCParametersImpl.h @@ -44,7 +44,7 @@ namespace IMPL { LCParametersImpl() ; /// Destructor. - virtual ~LCParametersImpl() { /* nop */; } + virtual ~LCParametersImpl() = default; /** Returns the first integer value for the given key. */ diff --git a/src/cpp/include/UTIL/CheckCollections.h b/src/cpp/include/UTIL/CheckCollections.h index f15a374b8..daa253e4a 100644 --- a/src/cpp/include/UTIL/CheckCollections.h +++ b/src/cpp/include/UTIL/CheckCollections.h @@ -2,6 +2,7 @@ #define CheckCollections_h 1 #include "lcio.h" +#include "IMPL/LCParametersImpl.h" #include #include @@ -77,6 +78,21 @@ class PIDHandler; */ Vector getConsistentCollections() const ; + using CollectedParameters = + std::unordered_map; + + /// Walk all events in fileNames and capture the first observed value of + /// each key in paramNames for every collection. The first value observed + /// per (collection, key) pair is retained. Collections where none of the + /// requested keys are present still appear in the result with an empty + /// LCParametersImpl. + void checkParameters(const std::vector& fileNames, + const std::vector& paramNames); + + /// Captured parameter values for every collection seen. Collections with + /// no captured values still appear with an empty LCParametersImpl. + const CollectedParameters& getCollectedParameters() const; + /** Add a collection with (name,type) that should be added to events in patchEvent(). * * Depending on the contents of name and type one of the following things @@ -141,6 +157,7 @@ class PIDHandler; /// meta information std::unordered_map> _particleIDMetas{}; CollectionVector _patchCols{}; + CollectedParameters _collectedParams{}; }; // class diff --git a/src/cpp/include/pre-generated/EVENT/LCParameters.h b/src/cpp/include/pre-generated/EVENT/LCParameters.h index 8205c7f80..6e1368d7a 100644 --- a/src/cpp/include/pre-generated/EVENT/LCParameters.h +++ b/src/cpp/include/pre-generated/EVENT/LCParameters.h @@ -29,7 +29,7 @@ class LCParameters { public: /// Destructor. - virtual ~LCParameters() { /* nop */; } + virtual ~LCParameters() = default; /** Returns the first integer value for the given key. */ @@ -67,6 +67,25 @@ class LCParameters { */ virtual StringVec & getStringVals(const std::string & key, StringVec & values) const = 0; + /** + * Get all values for the given key and type + */ + template + std::vector getVals(const std::string &key) const { + std::vector values; + if constexpr (std::is_same_v) { + getIntVals(key, values); + } else if constexpr (std::is_same_v) { + getFloatVals(key, values); + } else if constexpr (std::is_same_v) { + getDoubleVals(key, values); + } else { + getStringVals(key, values); + } + + return values; + } + /** Returns a list of all keys of integer parameters. */ virtual const StringVec & getIntKeys(StringVec & keys) const = 0; diff --git a/src/cpp/src/EXAMPLE/check_col_params.cc b/src/cpp/src/EXAMPLE/check_col_params.cc new file mode 100644 index 000000000..0c7bfc2de --- /dev/null +++ b/src/cpp/src/EXAMPLE/check_col_params.cc @@ -0,0 +1,177 @@ +#include "EVENT/LCParameters.h" +#include "UTIL/BitField64.h" +#include "UTIL/CheckCollections.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +void jsonEscape(std::ostream &os, const std::string &s) { + os << '"'; + for (char c : s) { + switch (c) { + case '"': + os << "\\\""; + break; + case '\\': + os << "\\\\"; + break; + case '\b': + os << "\\b"; + break; + case '\f': + os << "\\f"; + break; + case '\n': + os << "\\n"; + break; + case '\r': + os << "\\r"; + break; + case '\t': + os << "\\t"; + break; + default: + if (static_cast(c) < 0x20) { + os << "\\u" << std::hex << std::setw(4) << std::setfill('0') + << static_cast(c) << std::dec << std::setfill(' '); + } else { + os << c; + } + } + } + os << '"'; +} + +template +void emitArray(std::ostream &os, const std::vector &v) { + os << '['; + if constexpr (std::is_floating_point_v) { + os << std::setprecision(std::numeric_limits::max_digits10); + } + for (size_t i = 0; i < v.size(); ++i) { + if (i) { + os << ", "; + } + if constexpr (std::is_same_v) { + jsonEscape(os, v[i]); + } else { + os << v[i]; + } + } + os << ']'; +} + +template +bool emitGroup(std::ostream &os, const std::string &label, + const EVENT::LCParameters ¶ms, const EVENT::StringVec &keys, + bool needsComma) { + if (keys.empty()) { + return needsComma; + } + if (needsComma) { + os << ",\n"; + } + os << " \"" << label << "\": {"; + for (size_t i = 0; i < keys.size(); ++i) { + os << (i ? ",\n " : "\n "); + jsonEscape(os, keys[i]); + os << ": "; + emitArray(os, params.getVals(keys[i])); + } + os << "\n }"; + return true; +} + +void usage() { + std::cout + << "usage: check_col_params --output --params " + " [input-file2 ...]\n"; +} + +} // namespace + +int main(int argc, char **argv) { + std::string outputPath; + std::vector paramNames; + std::vector inputs; + + for (int i = 1; i < argc; ++i) { + std::string a = argv[i]; + if ((a == "--output" || a == "-o") && i + 1 < argc) { + outputPath = argv[++i]; + } else if ((a == "--params" || a == "-p") && i + 1 < argc) { + const std::string ps = argv[++i]; + std::for_each(ps.begin(), ps.end(), + UTIL::LCTokenizer(paramNames, ',')); + } else if (a == "-h" || a == "--help") { + usage(); + return 0; + } else { + inputs.push_back(a); + } + } + + if (outputPath.empty() || paramNames.empty() || inputs.empty()) { + usage(); + return 1; + } + + UTIL::CheckCollections cc; + cc.checkParameters(inputs, paramNames); + const auto &collected = cc.getCollectedParameters(); + + std::ofstream out(outputPath); + if (!out) { + std::cerr << "error: cannot open '" << outputPath << "' for writing\n"; + return 1; + } + + out << "{"; + bool firstColl = true; + for (const auto &[name, vals] : collected) { + EVENT::StringVec intKeys, floatKeys, doubleKeys, stringKeys; + vals.getIntKeys(intKeys); + vals.getFloatKeys(floatKeys); + vals.getDoubleKeys(doubleKeys); + vals.getStringKeys(stringKeys); + + if (intKeys.empty() && floatKeys.empty() && doubleKeys.empty() && + stringKeys.empty()) { + continue; + } + + if (!firstColl) { + out << ","; + } + firstColl = false; + out << "\n \"" << name << "\": {"; + + bool needsComma = false; + + needsComma = + emitGroup(out, "int-params", vals, intKeys, needsComma); + needsComma = + emitGroup(out, "float-params", vals, floatKeys, needsComma); + needsComma = emitGroup(out, "double-params", vals, doubleKeys, + needsComma); + needsComma = emitGroup(out, "string-params", vals, + stringKeys, needsComma); + + if (needsComma) { + out << "\n "; + } + out << "}"; + } + out << "\n}\n"; + + return 0; +} diff --git a/src/cpp/src/UTIL/CheckCollections.cc b/src/cpp/src/UTIL/CheckCollections.cc index fa605fd31..56f4323a9 100644 --- a/src/cpp/src/UTIL/CheckCollections.cc +++ b/src/cpp/src/UTIL/CheckCollections.cc @@ -9,6 +9,31 @@ #include #include +#include + +namespace { +bool captureParam(const EVENT::LCParameters &src, const std::string &key, + IMPL::LCParametersImpl &dest) { + bool found = false; + if (const auto vals = src.getVals(key); !vals.empty()) { + dest.setValues(key, vals); + found = true; + } + if (const auto vals = src.getVals(key); !vals.empty()) { + dest.setValues(key, vals); + found = true; + } + if (const auto vals = src.getVals(key); !vals.empty()) { + dest.setValues(key, vals); + found = true; + } + if (const auto vals = src.getVals(key); !vals.empty()) { + dest.setValues(key, vals); + found = true; + } + return found; +} +} // namespace namespace UTIL { @@ -114,6 +139,43 @@ void CheckCollections::insertParticleIDMetas(const UTIL::PIDHandler &pidHandler, } } +void CheckCollections::checkParameters( + const std::vector &fileNames, + const std::vector ¶mNames) { + // keep track of the parameters for each collection that we have already + // recorded. Only record the first value (and simply assume they are always + // the same) + std::unordered_map> recorded; + for (const auto &fileName : fileNames) { + MT::LCReader lcReader(0); + lcReader.open(fileName); + while (const auto evt = lcReader.readNextEvent()) { + for (const auto &name : *evt->getCollectionNames()) { + auto &dest = _collectedParams[name]; + auto &done = recorded[name]; + if (done.size() == paramNames.size()) { + continue; + } + const auto ¶ms = evt->getCollection(name)->getParameters(); + for (const auto &pname : paramNames) { + if (done.count(pname)) { + continue; + } + if (captureParam(params, pname, dest)) { + done.insert(pname); + } + } + } + } + lcReader.close(); + } +} + +const CheckCollections::CollectedParameters& +CheckCollections::getCollectedParameters() const { + return _collectedParams; +} + CheckCollections::Vector CheckCollections::getMissingCollections() const { Vector s; for (const auto &[name, coll] : _map) {