diff --git a/Analysis/Tutorials/src/configurableObjects.cxx b/Analysis/Tutorials/src/configurableObjects.cxx index e28f71bb0713b..61f57ad5085c7 100644 --- a/Analysis/Tutorials/src/configurableObjects.cxx +++ b/Analysis/Tutorials/src/configurableObjects.cxx @@ -52,6 +52,7 @@ auto printMatrix(Array2D const& m) } static constexpr float defaultm[3][4] = {{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}; +static LabeledArray la{&defaultm[0][0], 3, 4, {"r1", "r2", "r3"}, {"c1", "c2", "c3", "c4"}}; struct ConfigurableObjectDemo { Configurable cut{"cut", {0.5, 1, true}, "generic cut"}; @@ -60,6 +61,7 @@ struct ConfigurableObjectDemo { // note that size is fixed by this declaration - externally supplied vector needs to be the same size! Configurable> array{"array", {0, 0, 0, 0, 0, 0, 0}, "generic array"}; Configurable> vmatrix{"matrix", {&defaultm[0][0], 3, 4}, "generic matrix"}; + Configurable> vla{"vla", {defaultm[0], 3, 4, {"r1", "r2", "r3"}, {"c1", "c2", "c3", "c4"}}, "labeled array"}; void init(InitContext const&){}; void process(aod::Collision const&, aod::Tracks const& tracks) diff --git a/Framework/Core/include/Framework/Array2D.h b/Framework/Core/include/Framework/Array2D.h index 316773a2e89ab..84e1acc4521c2 100644 --- a/Framework/Core/include/Framework/Array2D.h +++ b/Framework/Core/include/Framework/Array2D.h @@ -11,6 +11,10 @@ #define FRAMEWORK_ARRAY2D_H #include #include +#include +#include +#include +#include namespace o2::framework { @@ -33,7 +37,7 @@ struct Array2D { data = new T[rows * cols]; for (auto i = 0U; i < rows; ++i) { for (auto j = 0U; j < cols; ++j) { - data[i * cols + j] = *(data_ + (i * cols + j)); + data[i * cols + j] = data_[i * cols + j]; } } } @@ -55,7 +59,7 @@ struct Array2D { data = new T[rows * cols]; for (auto i = 0U; i < rows; ++i) { for (auto j = 0U; j < cols; ++j) { - data[i * cols + j] = *(other.data + (i * cols + j)); + data[i * cols + j] = other.data[i * cols + j]; } } } @@ -114,6 +118,146 @@ struct Array2D { uint32_t rows; uint32_t cols; }; + +static constexpr const char* const labels_rows_str = "labels_rows"; +static constexpr const char* const labels_cols_str = "labels_cols"; + +template +class LabeledArray +{ + public: + using element_t = T; + + LabeledArray() + : values{}, + labels_rows{}, + labels_cols{}, + rowmap{}, + colmap{} + { + } + + LabeledArray(T const* data, uint32_t rows, uint32_t cols, std::vector labels_rows_ = {}, std::vector labels_cols_ = {}) + : values{data, rows, cols}, + labels_rows{labels_rows_}, + labels_cols{labels_cols_}, + rowmap{}, + colmap{} + { + if (labels_rows.empty() == false) { + assert(labels_rows.size() == rows); + for (auto i = 0u; i < labels_rows.size(); ++i) { + rowmap.emplace(labels_rows[i], (uint32_t)i); + } + } + if (labels_cols.empty() == false) { + assert(labels_cols.size() == cols); + for (auto i = 0u; i < labels_cols.size(); ++i) { + colmap.emplace(labels_cols[i], (uint32_t)i); + } + } + } + + LabeledArray(T const* data, uint32_t size, std::vector labels_ = {}) + : values{data, 1, size}, + labels_rows{}, + labels_cols{labels_}, + rowmap{}, + colmap{} + { + if (labels_cols.empty() == false) { + assert(labels_cols.size() == size); + for (auto i = 0u; i < labels_cols.size(); ++i) { + colmap.emplace(labels_cols[i], (uint32_t)i); + } + } + } + + LabeledArray(Array2D const& data, std::vector labels_rows_ = {}, std::vector labels_cols_ = {}) + : values{data}, + labels_rows{labels_rows_}, + labels_cols{labels_cols_}, + rowmap{}, + colmap{} + { + if (labels_rows.empty() == false) { + assert(labels_rows.size() == values.rows); + for (auto i = 0u; i < labels_rows.size(); ++i) { + rowmap.emplace(labels_rows[i], (uint32_t)i); + } + } + if (labels_cols.empty() == false) { + assert(labels_cols.size() == values.cols); + for (auto i = 0u; i < labels_cols.size(); ++i) { + colmap.emplace(labels_cols[i], (uint32_t)i); + } + } + } + + LabeledArray(LabeledArray const& other) = default; + LabeledArray(LabeledArray&& other) = default; + LabeledArray& operator=(LabeledArray const& other) = default; + LabeledArray& operator=(LabeledArray&& other) = default; + + ~LabeledArray() = default; + + T get(uint32_t y, uint32_t x) const + { + return values(y, x); + } + + T get(std::string y, std::string x) const + { + return values(rowmap.find(y)->second, colmap.find(x)->second); + } + + T get(uint32_t x) const + { + return values[0][x]; + } + + T getRow(u_int32_t y) const + { + return values[y]; + } + + T* operator[](uint32_t y) const + { + return values[y]; + } + + auto getLabelsRows() + { + return labels_rows; + } + + auto getLabelsCols() + { + return labels_cols; + } + + auto getData() + { + return values; + } + + auto rows() + { + return values.rows; + } + + auto cols() + { + return values.cols; + } + + private: + Array2D values; + std::vector labels_rows; + std::vector labels_cols; + std::unordered_map rowmap; + std::unordered_map colmap; +}; } // namespace o2::framework #endif // FRAMEWORK_ARRAY2D_H diff --git a/Framework/Core/include/Framework/ConfigParamRegistry.h b/Framework/Core/include/Framework/ConfigParamRegistry.h index cc5cfd58e585a..306a77bf06f15 100644 --- a/Framework/Core/include/Framework/ConfigParamRegistry.h +++ b/Framework/Core/include/Framework/ConfigParamRegistry.h @@ -68,6 +68,8 @@ class ConfigParamRegistry return vectorFromBranch(mStore->store().get_child(key)); } else if constexpr (is_base_of_template::value) { return array2DFromBranch(mStore->store().get_child(key)); + } else if constexpr (is_base_of_template::value) { + return labeledArrayFromBranch(mStore->store().get_child(key)); } else if constexpr (std::is_same_v) { return mStore->store().get_child(key); } else if constexpr (std::is_constructible_v) { diff --git a/Framework/Core/include/Framework/ConfigParamSpec.h b/Framework/Core/include/Framework/ConfigParamSpec.h index 7065ab0a53d3e..c4d153c424894 100644 --- a/Framework/Core/include/Framework/ConfigParamSpec.h +++ b/Framework/Core/include/Framework/ConfigParamSpec.h @@ -46,7 +46,9 @@ struct ConfigParamSpec { ConfigParamSpec(std::string _name, ParamType _type, Variant _defaultValue, HelpString _help) - : name(_name), type(_type), defaultValue(_defaultValue), help(_help) {} + : name(_name), type(_type), defaultValue(_defaultValue), help(_help) + { + } /// config spec without default value, explicitely marked as 'empty' /// and will be ignored at other places diff --git a/Framework/Core/include/Framework/Configurable.h b/Framework/Core/include/Framework/Configurable.h index 118e7afa99d93..49efb3a0a1f82 100644 --- a/Framework/Core/include/Framework/Configurable.h +++ b/Framework/Core/include/Framework/Configurable.h @@ -15,7 +15,7 @@ namespace o2::framework template struct ConfigurableBase { ConfigurableBase(std::string const& name, T&& defaultValue, std::string const& help) - : name(name), value{std::move(defaultValue)}, help(help) + : name(name), value{std::forward(defaultValue)}, help(help) { } using type = T; diff --git a/Framework/Core/include/Framework/Variant.h b/Framework/Core/include/Framework/Variant.h index ada0c839c151c..6d61eb6bbfaea 100644 --- a/Framework/Core/include/Framework/Variant.h +++ b/Framework/Core/include/Framework/Variant.h @@ -39,6 +39,9 @@ enum class VariantType : int { Int = 0, Array2DInt, Array2DFloat, Array2DDouble, + LabeledArrayInt, + LabeledArrayFloat, + LabeledArrayDouble, Empty, Unknown }; @@ -54,6 +57,12 @@ constexpr auto isArray2D() return (V == VariantType::Array2DInt || V == VariantType::Array2DFloat || V == VariantType::Array2DDouble); } +template +constexpr auto isLabeledArray() +{ + return (V == VariantType::LabeledArrayInt || V == VariantType::LabeledArrayFloat || V == VariantType::LabeledArrayDouble); +} + template struct variant_trait : std::integral_constant { }; @@ -93,6 +102,10 @@ DECLARE_VARIANT_TRAIT(Array2D, Array2DInt); DECLARE_VARIANT_TRAIT(Array2D, Array2DFloat); DECLARE_VARIANT_TRAIT(Array2D, Array2DDouble); +DECLARE_VARIANT_TRAIT(LabeledArray, LabeledArrayInt); +DECLARE_VARIANT_TRAIT(LabeledArray, LabeledArrayFloat); +DECLARE_VARIANT_TRAIT(LabeledArray, LabeledArrayDouble); + template struct variant_array_symbol { constexpr static char symbol = 'u'; @@ -153,6 +166,10 @@ DECLARE_VARIANT_TYPE(Array2D, Array2DInt); DECLARE_VARIANT_TYPE(Array2D, Array2DFloat); DECLARE_VARIANT_TYPE(Array2D, Array2DDouble); +DECLARE_VARIANT_TYPE(LabeledArray, LabeledArrayInt); +DECLARE_VARIANT_TYPE(LabeledArray, LabeledArrayFloat); +DECLARE_VARIANT_TYPE(LabeledArray, LabeledArrayDouble); + template struct variant_array_element_type { }; @@ -172,6 +189,13 @@ DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(double, Array2DDouble); DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(bool, ArrayBool); DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(std::string, ArrayString); +DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(int, LabeledArrayInt); +DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(float, LabeledArrayFloat); +DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(double, LabeledArrayDouble); + +template +using variant_array_element_type_t = typename variant_array_element_type::type; + template struct variant_helper { static void set(S* store, T value) @@ -183,10 +207,6 @@ struct variant_helper { { *reinterpret_cast(store) = reinterpret_cast(std::memcpy(std::malloc(size * sizeof(std::remove_pointer_t)), reinterpret_cast(values), size * sizeof(std::remove_pointer_t))); } - static void reset(S* store, T values, size_t) - { - *reinterpret_cast(store) = values; - } static T get(const S* store) { return *(reinterpret_cast(store)); } }; @@ -215,7 +235,10 @@ struct variant_helper { /// Variant for configuration parameter storage. Owns stored data. class Variant { - using storage_t = std::aligned_union<8, int, int64_t, const char*, float, double, bool, int*, float*, double*, bool*, Array2D, Array2D, Array2D>::type; + using storage_t = std::aligned_union<8, int, int64_t, const char*, float, double, bool, + int*, float*, double*, bool*, + Array2D, Array2D, Array2D, + LabeledArray, LabeledArray, LabeledArray>::type; public: Variant(VariantType type = VariantType::Unknown) : mType{type}, mSize{1} {} diff --git a/Framework/Core/include/Framework/VariantJSONHelpers.h b/Framework/Core/include/Framework/VariantJSONHelpers.h index 1a21f212f269b..a5a7ec4cc07a6 100644 --- a/Framework/Core/include/Framework/VariantJSONHelpers.h +++ b/Framework/Core/include/Framework/VariantJSONHelpers.h @@ -42,7 +42,9 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va }; VariantReader() - : states{} + : states{}, + rows{0}, + cols{0} { debug << "Start" << std::endl; states.push(State::IN_START); @@ -61,8 +63,7 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va debug << "In ERROR state" << std::endl; return false; } - if constexpr (!(V == VariantType::ArrayInt || V == VariantType::Array2DInt)) { - debug << "Integer value in non-integer variant" << std::endl; + if constexpr (!std::is_same_v>) { states.push(State::IN_ERROR); return true; } else { @@ -101,16 +102,16 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va debug << "In ERROR state" << std::endl; return false; } - if constexpr (!(V == VariantType::ArrayDouble || V == VariantType::Array2DDouble || V == VariantType::ArrayFloat || V == VariantType::Array2DFloat)) { + if constexpr (!(std::is_same_v> || std::is_same_v>)) { states.push(State::IN_ERROR); return true; } if (states.top() == State::IN_ARRAY || states.top() == State::IN_ROW) { - if constexpr (V == VariantType::ArrayDouble || V == VariantType::Array2DDouble) { + if constexpr (std::is_same_v>) { debug << "added to array as double" << std::endl; accumulatedData.push_back(d); return true; - } else if constexpr (V == VariantType::ArrayFloat || V == VariantType::Array2DFloat) { + } else if constexpr (std::is_same_v>) { debug << "added to array as float" << std::endl; accumulatedData.push_back(static_cast(d)); return true; @@ -127,15 +128,17 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va debug << "In ERROR state" << std::endl; return false; } - if constexpr (V != VariantType::ArrayBool) { + if constexpr (!std::is_same_v>) { states.push(State::IN_ERROR); return false; } else { - if (states.top() == State::IN_ARRAY || states.top() == State::IN_ROW) { + if (states.top() == State::IN_ARRAY) { debug << "added to array" << std::endl; accumulatedData.push_back(b); return true; } + states.push(State::IN_ERROR); + return true; } } @@ -146,13 +149,26 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va debug << "In ERROR state" << std::endl; return false; } - if constexpr (V != VariantType::ArrayString) { + if constexpr (!(V == VariantType::ArrayString || isLabeledArray())) { states.push(State::IN_ERROR); return true; } else { - if (states.top() == State::IN_ARRAY || states.top() == State::IN_ROW) { + if (states.top() == State::IN_ARRAY) { debug << "added to array" << std::endl; - accumulatedData.push_back(str); + if constexpr (isLabeledArray()) { + if (currentKey == labels_rows_str) { + labels_rows.push_back(str); + return true; + } else if (currentKey == labels_cols_str) { + labels_cols.push_back(str); + return true; + } else { + states.push(State::IN_ERROR); + return true; + } + } else { + accumulatedData.push_back(str); + } return true; } states.push(State::IN_ERROR); @@ -180,13 +196,25 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va debug << "Key(" << str << ")" << std::endl; if (states.top() == State::IN_ERROR) { debug << "In ERROR state" << std::endl; + currentKey = str; return false; } - if (states.top() == State::IN_DATA || states.top() == State::IN_KEY) { + if (states.top() == State::IN_DATA) { + //no previous keys states.push(State::IN_KEY); currentKey = str; return true; } + if (states.top() == State::IN_KEY) { + currentKey = str; + if constexpr (!isLabeledArray()) { + debug << "extra keys in a single-key variant" << std::endl; + states.push(State::IN_ERROR); + return true; + } + return true; + } + currentKey = str; states.push(State::IN_ERROR); return true; } @@ -199,13 +227,23 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va return false; } if (states.top() == State::IN_KEY) { - // finish up key if constexpr (isArray()) { debug << "creating 1d-array variant" << std::endl; result = Variant(accumulatedData); } else if constexpr (isArray2D()) { debug << "creating 2d-array variant" << std::endl; + assert(accumulatedData.size() == rows * cols); result = Variant(Array2D{accumulatedData, rows, cols}); + } else if constexpr (isLabeledArray()) { + debug << "creating labeled array variant" << std::endl; + assert(accumulatedData.size() == rows * cols); + if (labels_rows.empty() == false) { + assert(labels_rows.size() == rows); + } + if (labels_cols.empty() == false) { + assert(labels_cols.size() == cols); + } + result = Variant(LabeledArray{Array2D{accumulatedData, rows, cols}, labels_rows, labels_cols}); } states.push(State::IN_STOP); return true; @@ -223,12 +261,10 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va } if (states.top() == State::IN_KEY) { states.push(State::IN_ARRAY); - rows = 1; return true; } else if (states.top() == State::IN_ARRAY) { - if constexpr (isArray2D()) { + if constexpr (isArray2D() || isLabeledArray()) { states.push(State::IN_ROW); - ++rows; return true; } } @@ -246,18 +282,16 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va if (states.top() == State::IN_ARRAY) { //finish up array states.pop(); - if constexpr (isArray()) { - cols = elementCount; - } else if constexpr (isArray2D()) { + if constexpr (isArray2D() || isLabeledArray()) { rows = elementCount; } return true; } else if (states.top() == State::IN_ROW) { - if constexpr (isArray2D()) { - cols = elementCount; - } //finish up row states.pop(); + if constexpr (isArray2D() || isLabeledArray()) { + cols = elementCount; + } return true; } states.push(State::IN_ERROR); @@ -270,15 +304,17 @@ struct VariantReader : public rapidjson::BaseReaderHandler, Va uint32_t rows; uint32_t cols; std::string currentKey; - std::vector::type> accumulatedData; + std::vector> accumulatedData; + std::vector labels_rows; + std::vector labels_cols; Variant result; }; template void writeVariant(std::ostream& o, Variant const& v) { - if constexpr (isArray() || isArray2D()) { - using type = typename variant_array_element_type::type; + if constexpr (isArray() || isArray2D() || isLabeledArray()) { + using type = variant_array_element_type_t; rapidjson::OStreamWrapper osw(o); rapidjson::Writer w(osw); @@ -290,7 +326,7 @@ void writeVariant(std::ostream& o, Variant const& v) w.Int(values[i]); } else if constexpr (std::is_same_v || std::is_same_v) { w.Double(values[i]); - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { w.Bool(values[i]); } else if constexpr (std::is_same_v) { w.String(values[i].c_str()); @@ -299,6 +335,10 @@ void writeVariant(std::ostream& o, Variant const& v) w.EndArray(); }; + auto writeVector = [&](auto&& vector) { + return writeArray(vector.data(), vector.size()); + }; + auto writeArray2D = [&](auto&& array2d) { using T = typename std::decay_t::element_t; w.StartArray(); @@ -316,12 +356,24 @@ void writeVariant(std::ostream& o, Variant const& v) w.EndArray(); }; + auto writeLabeledArray = [&](auto&& array) { + w.Key(labels_rows_str); + writeVector(array.getLabelsRows()); + w.Key(labels_cols_str); + writeVector(array.getLabelsCols()); + w.Key("values"); + writeArray2D(array.getData()); + }; + w.StartObject(); - w.Key("values"); if constexpr (isArray()) { + w.Key("values"); writeArray(v.get(), v.size()); } else if constexpr (isArray2D()) { + w.Key("values"); writeArray2D(v.get>()); + } else if constexpr (isLabeledArray()) { + writeLabeledArray(v.get>()); } w.EndObject(); } @@ -336,6 +388,7 @@ struct VariantJSONHelpers { rapidjson::IStreamWrapper isw(s); VariantReader vreader; bool ok = reader.Parse(isw, vreader); + if (ok == false) { std::stringstream error; error << "Cannot parse serialized Variant, error: " << rapidjson::GetParseError_En(reader.GetParseErrorCode()) << " at offset: " << reader.GetErrorOffset(); @@ -372,6 +425,15 @@ struct VariantJSONHelpers { case VariantType::Array2DDouble: writeVariant(o, v); break; + case VariantType::LabeledArrayInt: + writeVariant(o, v); + break; + case VariantType::LabeledArrayFloat: + writeVariant(o, v); + break; + case VariantType::LabeledArrayDouble: + writeVariant(o, v); + break; default: break; } diff --git a/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h b/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h index 48880841bed31..5bc5f78310cc4 100644 --- a/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h +++ b/Framework/Core/include/Framework/VariantPropertyTreeHelpers.h @@ -88,6 +88,27 @@ auto array2DFromBranch(boost::property_tree::ptree const& branch) } return Array2D{cache, nrows, ncols}; } + +template +auto labeledArrayFromBranch(boost::property_tree::ptree const& tree) +{ + auto labels_rows = vectorFromBranch(tree.get_child("labels_rows")); + auto labels_cols = vectorFromBranch(tree.get_child("labels_cols")); + auto values = array2DFromBranch(tree.get_child("values")); + + return LabeledArray{values, labels_rows, labels_cols}; +} + +template +auto labeledArrayToBranch(LabeledArray&& array) +{ + boost::property_tree::ptree subtree; + subtree.put_child("labels_rows", vectorToBranch(array.getLabelsRows())); + subtree.put_child("lanels_cols", vectorToBranch(array.getLabelsCols())); + subtree.put_child("values", array2DToBranch(array.getData())); + + return subtree; +} } // namespace o2::framework #endif // FRAMEWORK_VARIANTPTREEHELPERS_H diff --git a/Framework/Core/src/BoostOptionsRetriever.cxx b/Framework/Core/src/BoostOptionsRetriever.cxx index 0746fd5bf8231..d1a330c5c9f51 100644 --- a/Framework/Core/src/BoostOptionsRetriever.cxx +++ b/Framework/Core/src/BoostOptionsRetriever.cxx @@ -74,6 +74,9 @@ void BoostOptionsRetriever::update(std::vector const& specs, case VariantType::Array2DDouble: options = options(name, bpo::value()->default_value(spec.defaultValue.asString()), help); break; + case VariantType::LabeledArrayInt: + case VariantType::LabeledArrayFloat: + case VariantType::LabeledArrayDouble: case VariantType::Unknown: case VariantType::Empty: break; diff --git a/Framework/Core/src/ConfigParamsHelper.cxx b/Framework/Core/src/ConfigParamsHelper.cxx index ac38f5ce4144b..4ee2ae1fa994d 100644 --- a/Framework/Core/src/ConfigParamsHelper.cxx +++ b/Framework/Core/src/ConfigParamsHelper.cxx @@ -79,6 +79,9 @@ void ConfigParamsHelper::populateBoostProgramOptions( case VariantType::Array2DDouble: addConfigSpecOption(spec, options); break; + case VariantType::LabeledArrayInt: + case VariantType::LabeledArrayFloat: + case VariantType::LabeledArrayDouble: case VariantType::Unknown: case VariantType::Empty: break; diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 04cebf5d94b4a..359066ebe9fbb 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -230,7 +230,7 @@ void DataProcessingDevice::Init() for (auto& entry : configStore->store()) { std::stringstream ss; std::string str; - if (entry.second.size() != 0) { + if (entry.second.empty() == false) { boost::property_tree::json_parser::write_json(ss, entry.second, false); str = ss.str(); str.pop_back(); //remove EoL diff --git a/Framework/Core/src/DeviceSpecHelpers.cxx b/Framework/Core/src/DeviceSpecHelpers.cxx index 89ed2328e2ca8..15dda47c4c6e5 100644 --- a/Framework/Core/src/DeviceSpecHelpers.cxx +++ b/Framework/Core/src/DeviceSpecHelpers.cxx @@ -923,20 +923,16 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, /// Lookup the executable name in the metadata associated with the workflow. /// If we find it, we rewrite the command line arguments to be processed /// so that they look like the ones passed to the merged workflow. - for (auto& processorInfo : processorInfos) { - if (processorInfo.name == spec.id) { - argc = processorInfo.cmdLineArgs.size() + 1; - argv = (char**)malloc(sizeof(char**) * (argc + 1)); - argv[0] = strdup(processorInfo.executable.data()); - for (size_t ai = 0; ai < processorInfo.cmdLineArgs.size(); ++ai) { - auto& arg = processorInfo.cmdLineArgs[ai]; - argv[ai + 1] = strdup(arg.data()); - } - argv[argc] = nullptr; - workflowOptions = processorInfo.workflowOptions; - break; - } + auto pi = std::find_if(processorInfos.begin(), processorInfos.end(), [&](auto const& x) { return x.name == spec.id; }); + argc = pi->cmdLineArgs.size() + 1; + argv = (char**)malloc(sizeof(char**) * (argc + 1)); + argv[0] = strdup(pi->executable.data()); + for (size_t ai = 0; ai < pi->cmdLineArgs.size(); ++ai) { + auto const& arg = pi->cmdLineArgs[ai]; + argv[ai + 1] = strdup(arg.data()); } + argv[argc] = nullptr; + workflowOptions = pi->workflowOptions; // We duplicate the list of options, filtering only those // which are actually relevant for the given device. The additional diff --git a/Framework/Core/src/PropertyTreeHelpers.cxx b/Framework/Core/src/PropertyTreeHelpers.cxx index fbcb4a31d6c24..f965a8579e3d8 100644 --- a/Framework/Core/src/PropertyTreeHelpers.cxx +++ b/Framework/Core/src/PropertyTreeHelpers.cxx @@ -74,6 +74,15 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s case VariantType::Array2DDouble: pt.put_child(key, array2DToBranch(spec.defaultValue.get>())); break; + case VariantType::LabeledArrayInt: + pt.put_child(key, labeledArrayToBranch(spec.defaultValue.get>())); + break; + case VariantType::LabeledArrayFloat: + pt.put_child(key, labeledArrayToBranch(spec.defaultValue.get>())); + break; + case VariantType::LabeledArrayDouble: + pt.put_child(key, labeledArrayToBranch(spec.defaultValue.get>())); + break; case VariantType::Unknown: case VariantType::Empty: default: @@ -209,6 +218,9 @@ void PropertyTreeHelpers::populate(std::vector const& schema, case VariantType::Array2DInt: case VariantType::Array2DFloat: case VariantType::Array2DDouble: + case VariantType::LabeledArrayInt: + case VariantType::LabeledArrayFloat: + case VariantType::LabeledArrayDouble: pt.put_child(key, *it); break; case VariantType::Unknown: diff --git a/Framework/Core/src/Variant.cxx b/Framework/Core/src/Variant.cxx index 2da40cc4ccc86..00d35401b9b86 100644 --- a/Framework/Core/src/Variant.cxx +++ b/Framework/Core/src/Variant.cxx @@ -28,7 +28,7 @@ void printArray(std::ostream& oss, T* array, size_t size) } template -void printMatrix(std::ostream& oss, Array2D m) +void printMatrix(std::ostream& oss, Array2D const& m) { oss << variant_array_symbol::symbol << "[["; oss << m(0, 0); diff --git a/Framework/Core/src/WorkflowSerializationHelpers.cxx b/Framework/Core/src/WorkflowSerializationHelpers.cxx index 344a97708646c..0e0a567dab445 100644 --- a/Framework/Core/src/WorkflowSerializationHelpers.cxx +++ b/Framework/Core/src/WorkflowSerializationHelpers.cxx @@ -311,6 +311,15 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler, case VariantType::Array2DDouble: opt = std::make_unique(optionName, optionType, VariantJSONHelpers::read(is), HelpString{optionHelp}); break; + case VariantType::LabeledArrayInt: + opt = std::make_unique(optionName, optionType, VariantJSONHelpers::read(is), HelpString{optionHelp}); + break; + case VariantType::LabeledArrayFloat: + opt = std::make_unique(optionName, optionType, VariantJSONHelpers::read(is), HelpString{optionHelp}); + break; + case VariantType::LabeledArrayDouble: + opt = std::make_unique(optionName, optionType, VariantJSONHelpers::read(is), HelpString{optionHelp}); + break; default: opt = std::make_unique(optionName, optionType, optionDefault, HelpString{optionHelp}); } @@ -728,6 +737,9 @@ void WorkflowSerializationHelpers::dump(std::ostream& out, case VariantType::Array2DInt: case VariantType::Array2DFloat: case VariantType::Array2DDouble: + case VariantType::LabeledArrayInt: + case VariantType::LabeledArrayFloat: + case VariantType::LabeledArrayDouble: VariantJSONHelpers::write(oss, option.defaultValue); break; default: diff --git a/Framework/Core/test/test_Variants.cxx b/Framework/Core/test/test_Variants.cxx index cfd24db512395..b340a10507790 100644 --- a/Framework/Core/test/test_Variants.cxx +++ b/Framework/Core/test/test_Variants.cxx @@ -27,30 +27,6 @@ bool unknown_type(RuntimeErrorRef const& ref) return strcmp(err.what, "Mismatch between types") == 0; } -BOOST_AUTO_TEST_CASE(Array2DTest) -{ - float m[3][4] = {{0.1, 0.2, 0.3, 0.4}, {0.5, 0.6, 0.7, 0.8}, {0.9, 1.0, 1.1, 1.2}}; - Array2D mm(&m[0][0], 3, 4); - for (auto i = 0u; i < 3; ++i) { - for (auto j = 0u; j < 4; ++j) { - BOOST_CHECK(mm(i, j) == m[i][j]); - } - } - std::vector v = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2}; - Array2D mv(v, 3, 4); - for (auto i = 0u; i < 3; ++i) { - for (auto j = 0u; j < 4; ++j) { - BOOST_CHECK(mm(i, j) == v[i * 4 + j]); - } - } - for (auto i = 0u; i < 3; ++i) { - auto const& vv = mm[i]; - for (auto j = 0u; j < 4; ++j) { - BOOST_CHECK(vv[j] == mm(i, j)); - } - } -} - BOOST_AUTO_TEST_CASE(VariantTest) { std::ostringstream ss{}; @@ -185,6 +161,73 @@ BOOST_AUTO_TEST_CASE(VariantTest) std::stringstream ssm; ssm << vmma; BOOST_CHECK(ssm.str() == "f[[0.1, 0.2, 0.3, 0.4], [0.5, 0.6, 0.7, 0.8], [0.9, 1, 1.1, 1.2]]"); + + LabeledArray laf{&m[0][0], 3, 4, {"r1", "r2", "r3"}, {"c1", "c2", "c3", "c4"}}; + Variant vlaf(laf); + auto const& lafc = vlaf.get>(); + for (auto i = 0u; i < 3; ++i) { + for (auto j = 0u; j < 4; ++j) { + BOOST_CHECK(laf.get(i, j) == lafc.get(i, j)); + } + } + + Variant vlafc(vlaf); // Copy constructor + Variant vlafm(std::move(vlaf)); // Move constructor + Variant vlafa = vlafm; // Copy assignment + auto const& lafc2 = vlafc.get>(); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(lafc2.get(i, j) == mm(i, j)); + } + } + auto const& lafc3 = vlafa.get>(); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(lafc3.get(i, j) == mm(i, j)); + } + } + + std::vector collection; + collection.push_back(vlafc); + collection.push_back(vlafm); + collection.push_back(vlafa); +} + +BOOST_AUTO_TEST_CASE(Array2DTest) +{ + float m[3][4] = {{0.1, 0.2, 0.3, 0.4}, {0.5, 0.6, 0.7, 0.8}, {0.9, 1.0, 1.1, 1.2}}; + Array2D mm(&m[0][0], 3, 4); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(mm(i, j) == m[i][j]); + } + } + std::vector v = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2}; + Array2D mv(v, 3, 4); + for (auto i = 0U; i < 3; ++i) { + for (auto j = 0U; j < 4; ++j) { + BOOST_CHECK(mm(i, j) == v[i * 4 + j]); + } + } + for (auto i = 0U; i < 3; ++i) { + auto const& vv = mm[i]; + for (auto j = 0u; j < 4; ++j) { + BOOST_CHECK(vv[j] == mm(i, j)); + } + } +} + +BOOST_AUTO_TEST_CASE(LabeledArrayTest) +{ + float m[3][4] = {{0.1, 0.2, 0.3, 0.4}, {0.5, 0.6, 0.7, 0.8}, {0.9, 1.0, 1.1, 1.2}}; + std::string xl[] = {"c1", "c2", "c3", "c4"}; + std::string yl[] = {"r1", "r2", "r3"}; + LabeledArray laf{&m[0][0], 3, 4, {"r1", "r2", "r3"}, {"c1", "c2", "c3", "c4"}}; + for (auto i = 0u; i < 3; ++i) { + for (auto j = 0u; j < 4; ++j) { + BOOST_CHECK(laf.get(yl[i], xl[j]) == laf.get(i, j)); + } + } } BOOST_AUTO_TEST_CASE(VariantConversionsTest) @@ -207,6 +250,7 @@ BOOST_AUTO_TEST_CASE(VariantConversionsTest) Variant vmm(mm); std::stringstream osm; VariantJSONHelpers::write(osm, vmm); + std::stringstream ism; ism.str(osm.str()); auto vm = VariantJSONHelpers::read(ism); @@ -216,4 +260,19 @@ BOOST_AUTO_TEST_CASE(VariantConversionsTest) BOOST_CHECK_EQUAL(vmm.get>()(i, j), vm.get>()(i, j)); } } + + LabeledArray laf{&m[0][0], 3, 4, {"r1", "r2", "r3"}, {"c1", "c2", "c3", "c4"}}; + Variant vlaf(laf); + std::stringstream osl; + VariantJSONHelpers::write(osl, vlaf); + + std::stringstream isl; + isl.str(osl.str()); + auto vlafc = VariantJSONHelpers::read(isl); + + for (auto i = 0u; i < vlafc.get>().rows(); ++i) { + for (auto j = 0u; j < vlafc.get>().cols(); ++j) { + BOOST_CHECK_EQUAL(vlaf.get>().get(i, j), vlafc.get>().get(i, j)); + } + } }