diff --git a/src/multio/action/CMakeLists.txt b/src/multio/action/CMakeLists.txt index 302eabc59..30ec9f22b 100644 --- a/src/multio/action/CMakeLists.txt +++ b/src/multio/action/CMakeLists.txt @@ -13,3 +13,4 @@ add_subdirectory(null) add_subdirectory(interpolate) add_subdirectory(interpolate-fesom) add_subdirectory(select) +add_subdirectory(scale) diff --git a/src/multio/action/scale/CMakeLists.txt b/src/multio/action/scale/CMakeLists.txt new file mode 100644 index 000000000..029fc2b4c --- /dev/null +++ b/src/multio/action/scale/CMakeLists.txt @@ -0,0 +1,24 @@ +ecbuild_add_library( + + TARGET multio-action-scale + + TYPE SHARED # Due to reliance on factory self registration this library cannot be static + + SOURCES + Scale.cc + Scale.h + Mapping.cc + Mapping.h + Scaling.cc + Scaling.h + MetadataUtils.cc + MetadataUtils.h + + PRIVATE_INCLUDES + ${ECKIT_INCLUDE_DIRS} + + CONDITION + + PUBLIC_LIBS + multio +) diff --git a/src/multio/action/scale/Mapping.cc b/src/multio/action/scale/Mapping.cc new file mode 100644 index 000000000..3d6a405a5 --- /dev/null +++ b/src/multio/action/scale/Mapping.cc @@ -0,0 +1,44 @@ +#include "Mapping.h" + +#include +#include + +#include "multio/LibMultio.h" +#include "multio/message/Glossary.h" +#include "multio/util/Substitution.h" +#include "MetadataUtils.h" + + +namespace multio::action { + +using message::glossary; + +ScaleMapping::ScaleMapping(const config::ComponentConfiguration& compConf) : hasMapping_(false), scaleMap_{} { + + const auto mappings = compConf.parsedConfig().has("mapping-definition") + ? compConf.parsedConfig().getSubConfigurations("mapping-definition") + : std::vector{}; + + if (!mappings.empty()) { + hasMapping_ = true; + for (const auto& mapping : mappings) { + auto matcher = mapping.getSubConfiguration("case"); + scaleMap_[matcher.getString("param-is")] = matcher.getString("map-to-param"); + } + } +} + +void ScaleMapping::applyMapping(message::Metadata& md) const { + if (hasMapping_) { + + std::string cparam = extractParam(md); + + auto it = scaleMap_.find(cparam); + if (it != scaleMap_.end()) { + md.set(glossary().paramId, std::stoll(it->second)); + md.set(glossary().param, it->second.c_str()); + } + } +} + +} // namespace multio::action \ No newline at end of file diff --git a/src/multio/action/scale/Mapping.h b/src/multio/action/scale/Mapping.h new file mode 100644 index 000000000..36fb7a58d --- /dev/null +++ b/src/multio/action/scale/Mapping.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "eckit/config/LocalConfiguration.h" +#include "multio/config/ComponentConfiguration.h" +#include "multio/message/Message.h" + +namespace multio::action { + +class ScaleMapping { +private: + bool hasMapping_; + std::map scaleMap_; + +public: + explicit ScaleMapping(const config::ComponentConfiguration& compConf); + void applyMapping(message::Metadata& md) const; +}; + +} // namespace multio::action \ No newline at end of file diff --git a/src/multio/action/scale/MetadataUtils.cc b/src/multio/action/scale/MetadataUtils.cc new file mode 100644 index 000000000..f586e994c --- /dev/null +++ b/src/multio/action/scale/MetadataUtils.cc @@ -0,0 +1,26 @@ + +#include "MetadataUtils.h" +#include "eckit/exception/Exceptions.h" +#include "multio/message/Metadata.h" + + +#include "multio/message/Glossary.h" + + +namespace multio::action { +using message::glossary; + +std::string extractParam(const multio::message::Metadata& md) { + std::string cparam{}; + if (auto param = md.getOpt(glossary().param); param) { + cparam = *param; + } else if (auto paramId = md.getOpt(glossary().paramId); paramId) { + cparam = std::to_string(*paramId); + } + else { + throw eckit::SeriousBug{"param/paramId metadata not present", Here()}; + } + + return cparam; +} +} \ No newline at end of file diff --git a/src/multio/action/scale/MetadataUtils.h b/src/multio/action/scale/MetadataUtils.h new file mode 100644 index 000000000..6b42440c5 --- /dev/null +++ b/src/multio/action/scale/MetadataUtils.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "multio/LibMultio.h" +#include "multio/action/scale/MetadataUtils.h" +#include "multio/message/Metadata.h" + +namespace multio::action { + std::string extractParam(const multio::message::Metadata& md); + +} \ No newline at end of file diff --git a/src/multio/action/scale/Scale.cc b/src/multio/action/scale/Scale.cc new file mode 100644 index 000000000..96ad4984d --- /dev/null +++ b/src/multio/action/scale/Scale.cc @@ -0,0 +1,96 @@ +/* + * (C) Copyright 1996- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation nor + * does it submit to any jurisdiction. + */ + +#include "multio/action/scale/Scale.h" + +#include "eckit/exception/Exceptions.h" +#include "eckit/log/Log.h" + + +#include "multio/LibMultio.h" +#include "multio/config/ComponentConfiguration.h" +#include "multio/message/Glossary.h" +#include "multio/message/Message.h" +#include "multio/util/PrecisionTag.h" + +#include "multio/action/scale/Mapping.h" +#include "multio/action/scale/Scaling.h" +#include "multio/action/scale/MetadataUtils.h" + + +namespace multio::action { + +using message::glossary; + +Scale::Scale(const ComponentConfiguration& compConf) : + ChainedAction(compConf), scaling_{compConf}, mapping_{compConf}, paramsToScale_{} { + + const auto mappings = compConf.parsedConfig().has("mapping-definition") + ? compConf.parsedConfig().getSubConfigurations("mapping-definition") + : throw eckit::SeriousBug{"Scaling information not specified in plan", Here()}; + + if (!mappings.empty()) { + for (const auto& mapping : mappings) { + auto matcher = mapping.getSubConfiguration("case"); + paramsToScale_.insert(matcher.getString("param-is")); + } + } +} + +void Scale::executeImpl(message::Message msg) { + if (msg.tag() != message::Message::Tag::Field) { + executeNext(std::move(msg)); + return; + } + + std::string cparam = extractParam(msg.metadata()); + + // Continue if no scaling definition was specified in the plan. + if (paramsToScale_.find(cparam) == paramsToScale_.end()) { + executeNext(std::move(msg)); + return; + } + //Scale the message + util::dispatchPrecisionTag(msg.precision(), [&](auto pt) { + using Precision = typename decltype(pt)::type; + ScaleMessage(msg); // Modify msg in place + }); + //pass on the modified message + executeNext(std::move(msg)); + return; +} + +template +void Scale::ScaleMessage(message::Message& msg) const { + + LOG_DEBUG_LIB(LibMultio) << "Scale :: Metadata of the input message :: Apply Scaling " << std::endl + << msg.metadata() << std::endl; + msg.acquire(); + + // Potentially work with msg = scaling_.applyScaling(std::move(msg)) + scaling_.applyScaling(msg); + + mapping_.applyMapping(msg.modifyMetadata()); + + return; +} + +void Scale::print(std::ostream& os) const { + os << "Scale Action "; +} + +static ActionBuilder ScaleBuilder("scale"); + + +template void Scale::ScaleMessage(message::Message&) const; +template void Scale::ScaleMessage(message::Message&) const; + + +} // namespace multio::action diff --git a/src/multio/action/scale/Scale.h b/src/multio/action/scale/Scale.h new file mode 100644 index 000000000..26605c6a1 --- /dev/null +++ b/src/multio/action/scale/Scale.h @@ -0,0 +1,28 @@ +#pragma once + +#include "multio/action/ChainedAction.h" +#include "multio/config/ComponentConfiguration.h" + +#include "multio/action/scale/Mapping.h" +#include "multio/action/scale/Scaling.h" + +namespace multio::action { + +class Scale final : public ChainedAction { +public: + explicit Scale(const ComponentConfiguration& compConf); // Constructor declaration + + void executeImpl(message::Message msg) override; + +private: + ScaleScaling scaling_; + ScaleMapping mapping_; + std::set paramsToScale_; + + template + void ScaleMessage(message::Message& msg) const; + + void print(std::ostream&) const override; +}; + +} // namespace multio::action diff --git a/src/multio/action/scale/Scaling.cc b/src/multio/action/scale/Scaling.cc new file mode 100644 index 000000000..59b0a313c --- /dev/null +++ b/src/multio/action/scale/Scaling.cc @@ -0,0 +1,38 @@ +#include "Scaling.h" + +#include +#include + +#include "multio/LibMultio.h" +#include "multio/util/Substitution.h" + + +namespace multio::action { + + +ScaleScaling::ScaleScaling(const config::ComponentConfiguration& compConf) : hasScaling_(false), scaleFactor_{} { + + const auto mappings = compConf.parsedConfig().has("mapping-definition") + ? compConf.parsedConfig().getSubConfigurations("mapping-definition") + : std::vector{}; + + if (!mappings.empty()) { + hasScaling_ = true; + for (const auto& mapping : mappings) { + auto matcher = mapping.getSubConfiguration("case"); + scaleFactor_[matcher.getString("param-is")] = matcher.getDouble("scaling-factor"); + } + } +} +double ScaleScaling::getScalingFactor(const std::string paramID) const { + auto it = scaleFactor_.find(paramID); + if (it != scaleFactor_.end()) { + return it->second; + } + else { + throw eckit::SeriousBug{"Scaling Factor not found paramID" + paramID, Here()}; + } +} + + +} // namespace multio::action \ No newline at end of file diff --git a/src/multio/action/scale/Scaling.h b/src/multio/action/scale/Scaling.h new file mode 100644 index 000000000..52921fb8e --- /dev/null +++ b/src/multio/action/scale/Scaling.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include "eckit/config/LocalConfiguration.h" +#include "multio/config/ComponentConfiguration.h" +#include "multio/message/Glossary.h" +#include "multio/message/Message.h" +#include "multio/action/scale/MetadataUtils.h" + +namespace multio::action { + +using message::glossary; + +class ScaleScaling { +private: + bool hasScaling_; + std::map scaleFactor_; + +public: + explicit ScaleScaling(const config::ComponentConfiguration& compConf); + double getScalingFactor(const std::string paramID) const; + + template + void applyScaling(message::Message& msg) const { + if (hasScaling_) { + std::string cparam = extractParam(msg.metadata()); + // Find the scaling factor + double scaleFactor = getScalingFactor(cparam); + + // Ensure the payload size is compatible with Precision type + size_t size = msg.payload().size() / sizeof(Precision); + if (size == 0) { + throw eckit::SeriousBug{" Payload is empty: Scaling Action: " + msg.metadata().toString(), Here()}; + } + + // Access the payload data + auto data = static_cast(msg.payload().modifyData()); + if (!data) { + throw eckit::SeriousBug{ + " Payload data could not be modified: Scaling Action: " + msg.metadata().toString(), Here()}; + } + // Apply the scaling factor using std::transform + std::transform(data, data + size, data, + [scaleFactor](Precision value) { return static_cast(value * scaleFactor); }); + } + else { + return; + } + } +}; + +} // namespace multio::action diff --git a/src/multio/api/c/CMakeLists.txt b/src/multio/api/c/CMakeLists.txt index 610f28c35..31222e982 100644 --- a/src/multio/api/c/CMakeLists.txt +++ b/src/multio/api/c/CMakeLists.txt @@ -21,6 +21,7 @@ list( APPEND multio_action_plugins multio-action-transport multio-action-renumber-healpix multio-action-interpolate-fesom + multio-action-scale ) if( HAVE_MIR ) diff --git a/src/multio/config/PlanConfiguration.cc b/src/multio/config/PlanConfiguration.cc index 296d76ca4..ad55bab65 100644 --- a/src/multio/config/PlanConfiguration.cc +++ b/src/multio/config/PlanConfiguration.cc @@ -212,7 +212,6 @@ eckit::LocalConfiguration generate_interpolate_action(const metkit::mars::MarsPa return interpolate_action; }; - eckit::LocalConfiguration generate_encode_action(const metkit::mars::MarsParsedRequest& request, const eckit::LocalConfiguration& componentConfig, const std::string& templatesPath) {