diff --git a/src/metkit/mars2grib/backend/concepts/AllConcepts.h b/src/metkit/mars2grib/backend/concepts/AllConcepts.h index f4b46c1e..fb9f23c7 100644 --- a/src/metkit/mars2grib/backend/concepts/AllConcepts.h +++ b/src/metkit/mars2grib/backend/concepts/AllConcepts.h @@ -99,6 +99,7 @@ #include "metkit/mars2grib/backend/concepts/destine/destineConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/ensemble/ensembleConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/generating-process/generatingProcessConceptDescriptor.h" +#include "metkit/mars2grib/backend/concepts/iteration/iterationConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/level/levelConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/longrange/longrangeConceptDescriptor.h" #include "metkit/mars2grib/backend/concepts/mars/marsConceptDescriptor.h" @@ -168,10 +169,10 @@ using TypeList = metkit::mars2grib::backend::compile_time_registry_engine::TypeL /// Higher-level code should interact with concepts exclusively through /// registry APIs, not by iterating this list directly. /// -using AllConcepts = - TypeList; +using AllConcepts = TypeList; } // namespace metkit::mars2grib::backend::concepts_::detail \ No newline at end of file diff --git a/src/metkit/mars2grib/backend/concepts/analysis/analysisEncoding.h b/src/metkit/mars2grib/backend/concepts/analysis/analysisEncoding.h index ce8140cc..b47e9c62 100644 --- a/src/metkit/mars2grib/backend/concepts/analysis/analysisEncoding.h +++ b/src/metkit/mars2grib/backend/concepts/analysis/analysisEncoding.h @@ -150,7 +150,7 @@ void AnalysisOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& o MARS2GRIB_LOG_CONCEPT(analysis); // Structural validation - validation::match_LocalDefinitionNumber_or_throw(opt, out, {36L}); + validation::match_LocalDefinitionNumber_or_throw(opt, out, {36L, 38L}); // Deductions long offsetToEndOf4DvarWindowVal = deductions::resolve_offsetToEndOf4DvarWindow_or_throw(mars, par, opt); diff --git a/src/metkit/mars2grib/backend/concepts/iteration/iterationConceptDescriptor.h b/src/metkit/mars2grib/backend/concepts/iteration/iterationConceptDescriptor.h new file mode 100644 index 00000000..6a8777f3 --- /dev/null +++ b/src/metkit/mars2grib/backend/concepts/iteration/iterationConceptDescriptor.h @@ -0,0 +1,160 @@ +/* + * (C) Copyright 2025- ECMWF and individual contributors. + * + * 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. + */ + +/// +/// @file IterationConcept.h +/// @brief Compile-time registry entry for the GRIB `iteration` concept. +/// +/// This header defines `IterationConcept`, the **compile-time descriptor** +/// that registers the GRIB `iteration` concept into the mars2grib +/// compile-time registry engine. +/// +/// The descriptor provides: +/// - The concept name +/// - The mapping between variants and their symbolic names +/// - The set of callbacks associated with each encoding phase +/// - The entry-level matcher used to activate the concept +/// +/// This file contains **no runtime logic**. All decisions are resolved +/// at compile time through template instantiation. +/// +/// @ingroup mars2grib_backend_concepts +/// +#pragma once + +// System include +#include + +// Registry engine +#include "metkit/mars2grib/backend/compile-time-registry-engine/RegisterEntryDescriptor.h" +#include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" +#include "metkit/mars2grib/utils/generalUtils.h" + +// Core concept includes +#include "metkit/mars2grib/backend/concepts/iteration/iterationEncoding.h" +#include "metkit/mars2grib/backend/concepts/iteration/iterationEnum.h" +#include "metkit/mars2grib/backend/concepts/iteration/iterationMatcher.h" + +namespace metkit::mars2grib::backend::concepts_ { + +// Importing the compile-time registry engine namespace locally to avoid +// excessive verbosity in template-heavy code. This is restricted to an +// internal scope and not exposed through public headers. +using namespace metkit::mars2grib::backend::compile_time_registry_engine; + +/// +/// @brief Compile-time descriptor for the `iteration` concept. +/// +/// `IterationConcept` registers the GRIB `iteration` concept into the +/// compile-time registry engine. +/// +/// The descriptor defines: +/// - The canonical concept name +/// - The mapping from variant enum values to symbolic names +/// - The callbacks associated with each encoding phase +/// - The entry-level matcher used to detect applicability +/// +/// All functions in this descriptor are `constexpr` and are evaluated +/// entirely at compile time. +/// +struct IterationConcept : RegisterEntryDescriptor { + + /// + /// @brief Return the canonical name of the concept. + /// + /// This name is used for: + /// - Registry identification + /// - Diagnostics and logging + /// - Debug and introspection facilities + /// + static constexpr std::string_view entryName() { return iterationName; } + + /// + /// @brief Return the symbolic name of a concept variant. + /// + /// @tparam T Variant enumeration value + /// + /// @return String view representing the variant name + /// + template + static constexpr std::string_view variantName() { + return iterationTypeName(); + } + + /// + /// @brief Return the callback associated with a specific encoding phase. + /// + /// This function is queried by the registry engine to obtain the + /// callback implementing the `iteration` concept for a given: + /// + /// - Capability + /// - Encoding stage + /// - GRIB section + /// - Concept variant + /// + /// The function returns: + /// - A valid function pointer if the concept is applicable + /// - `nullptr` otherwise + /// + /// @tparam Capability Encoding capability index + /// @tparam Stage Encoding stage + /// @tparam Sec GRIB section + /// @tparam Variant Concept variant + /// @tparam MarsDict_t Type of MARS dictionary + /// @tparam ParDict_t Type of parameter dictionary + /// @tparam OptDict_t Type of options dictionary + /// @tparam OutDict_t Type of output GRIB dictionary + /// + /// @return Function pointer implementing the phase, or `nullptr` + /// + template + static constexpr Fn phaseCallbacks() { + + if constexpr (Capability == 0) { + + if constexpr (iterationApplicable()) { + return &IterationOp; + } + else { + return nullptr; + } + } + else { + return nullptr; + } + + mars2gribUnreachable(); + } + + /// + /// @brief Variant-specific callbacks (not used for this concept). + /// + template + static constexpr Fn variantCallbacks() { + return nullptr; + } + + /// + /// @brief Entry-level matcher callback. + /// + template + static constexpr Fm entryCallbacks() { + if constexpr (Capability == 0) { + return &iterationMatcher; + } + else { + return nullptr; + } + } +}; + +} // namespace metkit::mars2grib::backend::concepts_ diff --git a/src/metkit/mars2grib/backend/concepts/iteration/iterationEncoding.h b/src/metkit/mars2grib/backend/concepts/iteration/iterationEncoding.h new file mode 100644 index 00000000..cc0900d7 --- /dev/null +++ b/src/metkit/mars2grib/backend/concepts/iteration/iterationEncoding.h @@ -0,0 +1,176 @@ +/* + * (C) Copyright 2025- ECMWF and individual contributors. + * + * 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. + */ + +/// +/// @file iterationOp.h +/// @brief Implementation of the GRIB `iteration` concept operation. +/// +/// This header defines the applicability rules and execution logic for the +/// **iteration concept** within the mars2grib backend. +/// +/// The iteration concept is responsible for encoding GRIB keys associated with +/// *long-range forecast metadata* stored in the Local Use Section, specifically: +/// +/// - `methodNumber` +/// - `systemNumber` +/// +/// These fields are used to identify the forecasting method and system +/// used for long-range or seasonal products. +/// +/// The implementation follows the standard mars2grib concept model: +/// - Compile-time applicability via `iterationApplicable` +/// - Runtime validation of Local Definition Number +/// - Explicit deduction of required values +/// - Strict error handling with contextual concept exceptions +/// +/// @note +/// The namespace name `concepts_` is intentionally used instead of `concepts` +/// to avoid ambiguity and potential conflicts with the C++20 `concept` language +/// feature and related standard headers. +/// +/// This is a deliberate design choice and must not be changed. +/// +/// @ingroup mars2grib_backend_concepts +/// +#pragma once + +// Core concept includes +#include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" +#include "metkit/mars2grib/backend/concepts/iteration/iterationEnum.h" +#include "metkit/mars2grib/utils/generalUtils.h" + +// Deductions +#include "metkit/mars2grib/backend/deductions/iterationNumber.h" +#include "metkit/mars2grib/backend/deductions/totalNumberOfIterations.h" + +// checks +#include "metkit/mars2grib/backend/checks/matchLocalDefinitionNumber.h" + +// Utils +#include "metkit/config/LibMetkit.h" +#include "metkit/mars2grib/utils/logUtils.h" +#include "metkit/mars2grib/utils/mars2gribExceptions.h" + +namespace metkit::mars2grib::backend::concepts_ { + +/// +/// @brief Compile-time applicability predicate for the `iteration` concept. +/// +/// This predicate determines whether the iteration concept is applicable +/// for a given combination of: +/// - encoding stage +/// - GRIB section +/// - concept variant +/// +/// Applicability is evaluated entirely at compile time and is used by the +/// concept dispatcher to control instantiation and execution. +/// +/// @tparam Stage Encoding stage (compile-time constant) +/// @tparam Section GRIB section index (compile-time constant) +/// @tparam Variant Iteration concept variant +/// +/// @return `true` if the concept is applicable for the given parameters, +/// `false` otherwise. +/// +/// @note +/// The default applicability rule enables the concept only when: +/// - `Variant == IterationType::Default` +/// - `Stage == StagePreset` +/// - `Section == SecLocalUseSection` +/// +template +constexpr bool iterationApplicable() { + return ((Variant == IterationType::Default) && (Stage == StagePreset) && (Section == SecLocalUseSection)); +} + + +/// +/// @brief Execute the `iteration` concept operation. +/// +/// This function implements the runtime logic of the GRIB `iteration` concept. +/// When applicable, it: +/// +/// 1. Validates that the Local Use Section matches the expected definition. +/// 2. Deduces the long-range forecasting method and system identifiers. +/// 3. Encodes the corresponding GRIB keys in the output dictionary. +/// +/// If the concept is invoked when not applicable, a +/// `Mars2GribConceptException` is thrown. +/// +/// @tparam Stage Encoding stage (compile-time constant) +/// @tparam Section GRIB section index (compile-time constant) +/// @tparam Variant Iteration concept variant +/// @tparam MarsDict_t Type of the MARS input dictionary +/// @tparam ParDict_t Type of the parameter dictionary +/// @tparam OptDict_t Type of the options dictionary +/// @tparam OutDict_t Type of the GRIB output dictionary +/// +/// @param[in] mars MARS input dictionary +/// @param[in] par Parameter dictionary +/// @param[in] opt Options dictionary +/// @param[out] out Output GRIB dictionary to be populated +/// +/// @throws metkit::mars2grib::utils::exceptions::Mars2GribConceptException +/// If: +/// - the Local Definition Number does not match expectations, +/// - required deductions fail, +/// - any GRIB key cannot be set, +/// - the concept is invoked when not applicable. +/// +/// @note +/// - All runtime errors are wrapped with full concept context +/// (concept name, variant, stage, section). +/// - This concept does not rely on pre-existing GRIB header state. +/// +/// @see iterationApplicable +/// +template +void IterationOp(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt, OutDict_t& out) { + + using metkit::mars2grib::utils::dict_traits::set_or_throw; + using metkit::mars2grib::utils::exceptions::Mars2GribConceptException; + + if constexpr (iterationApplicable()) { + + + try { + + MARS2GRIB_LOG_CONCEPT(iteration); + + // Preconditions / contracts + validation::match_LocalDefinitionNumber_or_throw(opt, out, {20L, 38L}); + + // Deductions + auto iterationNumberVal = deductions::resolve_IterationNumber_or_throw(mars, par, opt); + auto totalNumberOfIterationsVal = deductions::resolve_TotalNumberOfIterations_opt(mars, par, opt); + + // Encoding + set_or_throw(out, "iterationNumber", iterationNumberVal); + if (totalNumberOfIterationsVal.has_value()) { + set_or_throw(out, "totalNumberOfIterations", totalNumberOfIterationsVal.value()); + } + } + catch (...) { + MARS2GRIB_CONCEPT_RETHROW(iteration, "Unable to set `iteration` concept..."); + } + + // Successful operation + return; + } + + // Concept invoked outside its applicability domain + MARS2GRIB_CONCEPT_THROW(iteration, "Concept called when not applicable..."); + + // Remove compiler warning + mars2gribUnreachable(); +} + +} // namespace metkit::mars2grib::backend::concepts_ diff --git a/src/metkit/mars2grib/backend/concepts/iteration/iterationEnum.h b/src/metkit/mars2grib/backend/concepts/iteration/iterationEnum.h new file mode 100644 index 00000000..5a8fae4f --- /dev/null +++ b/src/metkit/mars2grib/backend/concepts/iteration/iterationEnum.h @@ -0,0 +1,136 @@ +/* + * (C) Copyright 2025- ECMWF and individual contributors. + * + * 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. + */ + +/// +/// @file iterationEnum.h +/// @brief Definition of the `iteration` concept variants and compile-time metadata. +/// +/// This header defines the **static description** of the GRIB `iteration` concept +/// used by the mars2grib backend. It contains: +/// +/// - the canonical concept name (`iterationName`) +/// - the enumeration of supported long-range variants (`IterationType`) +/// - a compile-time typelist of all variants (`IterationList`) +/// - a compile-time mapping from variant to string identifier +/// +/// This file intentionally contains **no runtime logic** and **no encoding +/// behavior**. Its sole purpose is to provide compile-time metadata used by: +/// +/// - the concept registry +/// - compile-time table generation +/// - logging and diagnostics +/// - static validation of concept variants +/// +/// @note +/// This header is part of the **concept definition layer**. +/// Runtime behavior is implemented separately in the corresponding +/// `iteration.h` / `iterationOp` implementation. +/// +/// @ingroup mars2grib_backend_concepts +/// +#pragma once + +// System includes +#include +#include + +// Core concept includes +#include "metkit/mars2grib/backend/compile-time-registry-engine/common.h" +#include "metkit/mars2grib/utils/generalUtils.h" + +namespace metkit::mars2grib::backend::concepts_ { + +template +using ValueList = metkit::mars2grib::backend::compile_time_registry_engine::ValueList; + +/// +/// @brief Canonical name of the `iteration` concept. +/// +/// This identifier is used: +/// - as the logical concept key in the concept registry +/// - for logging and debugging output +/// - to associate variants and capabilities with the `iteration` concept +/// +/// The value must remain stable across releases. +/// +inline constexpr std::string_view iterationName{"iteration"}; + + +/// +/// @brief Enumeration of all supported `iteration` concept variants. +/// +/// Each enumerator represents a specific long-range forecasting +/// classification or processing mode handled by the encoder. +/// +/// The numeric values of the enumerators are **not semantically relevant**; +/// they are required only to: +/// - provide a stable compile-time identifier +/// - allow array indexing and table generation +/// +/// @note +/// This enumeration is intentionally minimal. Additional variants may be +/// introduced in the future as the long-range concept evolves. +/// +/// @warning +/// Do not reorder existing enumerators, as they are used in compile-time +/// tables and registries. +/// +enum class IterationType : std::size_t { + Default = 0 +}; + + +/// +/// @brief Compile-time list of all `iteration` concept variants. +/// +/// This typelist is used to: +/// - generate concept capability tables at compile time +/// - register all supported variants in the concept registry +/// - enable static iteration over variants without runtime overhead +/// +/// @note +/// The order of this list must match the intended iteration order +/// for registry construction and diagnostics. +/// +using IterationList = ValueList; + + +/// +/// @brief Compile-time mapping from `IterationType` to human-readable name. +/// +/// This function returns the canonical string identifier associated +/// with a given long-range variant. +/// +/// The returned value is used for: +/// - logging and debugging output +/// - error reporting +/// - concept registry diagnostics +/// +/// @tparam T Long-range variant +/// @return String view identifying the variant +/// +/// @note +/// The returned string must remain stable across releases, as it may +/// appear in logs, tests, and diagnostic output. +/// +template +constexpr std::string_view iterationTypeName(); + +#define DEF(T, NAME) \ + template <> \ + constexpr std::string_view iterationTypeName() { \ + return NAME; \ + } + +DEF(IterationType::Default, "default"); + +#undef DEF + +} // namespace metkit::mars2grib::backend::concepts_ diff --git a/src/metkit/mars2grib/backend/concepts/iteration/iterationMatcher.h b/src/metkit/mars2grib/backend/concepts/iteration/iterationMatcher.h new file mode 100644 index 00000000..39fd3f7d --- /dev/null +++ b/src/metkit/mars2grib/backend/concepts/iteration/iterationMatcher.h @@ -0,0 +1,25 @@ +#pragma once + +// System include +#include + +// Utils +#include "metkit/config/LibMetkit.h" +#include "metkit/mars2grib/backend/concepts/iteration/iterationEnum.h" +#include "metkit/mars2grib/utils/dictionary_traits/dictionary_access_traits.h" +#include "metkit/mars2grib/utils/generalUtils.h" + +namespace metkit::mars2grib::backend::concepts_ { + +template +std::size_t iterationMatcher(const MarsDict_t& mars, const OptDict_t& opt) { + using metkit::mars2grib::utils::dict_traits::has; + + if (has(mars, "iteration")) { + return static_cast(IterationType::Default); + } + + return compile_time_registry_engine::MISSING; +} + +} // namespace metkit::mars2grib::backend::concepts_ diff --git a/src/metkit/mars2grib/backend/deductions/iterationNumber.h b/src/metkit/mars2grib/backend/deductions/iterationNumber.h new file mode 100644 index 00000000..fe023ef8 --- /dev/null +++ b/src/metkit/mars2grib/backend/deductions/iterationNumber.h @@ -0,0 +1,130 @@ +/* + * (C) Copyright 2025- ECMWF and individual contributors. + * + * 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. + */ + +/// +/// @file iterationNumber.h +/// @brief Deduction of the offset to the end of the 4D-Var analysis window. +/// +/// This header defines deduction utilities used by the mars2grib backend +/// to resolve the **offset to the end of the 4D-Var assimilation window** +/// from input dictionaries. +/// +/// The deduction retrieves the offset explicitly from the MARS dictionary. +/// No inference, defaulting, normalization, or validation of temporal +/// semantics is performed. +/// +/// Error handling follows a strict fail-fast strategy: +/// - missing or invalid inputs cause immediate failure +/// - errors are reported using domain-specific deduction exceptions +/// - original errors are preserved via nested exception propagation +/// +/// Logging follows the mars2grib deduction policy: +/// - RESOLVE: value resolved from one or more input dictionaries +/// +/// @section References +/// Concept: +/// - @ref analysisEncoding.h +/// +/// Related deductions: +/// - @ref lengthOfTimeWindow.h +/// +/// @ingroup mars2grib_backend_deductions +/// +#pragma once + +// System includes +#include + +// Core deduction includes +#include "metkit/config/LibMetkit.h" +#include "metkit/mars2grib/utils/generalUtils.h" +#include "metkit/mars2grib/utils/logUtils.h" +#include "metkit/mars2grib/utils/mars2gribExceptions.h" + +namespace metkit::mars2grib::backend::deductions { + +/// +/// @brief Resolve the offset to the end of the 4D-Var analysis window. +/// +/// @section Deduction contract +/// - Reads: `mars["iteration"]` +/// - Writes: none +/// - Side effects: logging (RESOLVE) +/// - Failure mode: throws +/// +/// This deduction resolves the temporal offset between the analysis +/// reference time and the end of the 4D-Var assimilation window. +/// +/// The returned value is treated as an opaque numeric quantity. Its unit +/// and interpretation are defined by upstream MARS/IFS conventions and +/// are not interpreted by this deduction. +/// +/// @tparam MarsDict_t +/// Type of the MARS dictionary. Must provide the key `iteration`. +/// +/// @tparam ParDict_t +/// Type of the parameter dictionary (unused). +/// +/// @tparam OptDict_t +/// Type of the options dictionary (unused). +/// +/// @param[in] mars +/// MARS dictionary from which the offset is resolved. +/// +/// @param[in] par +/// Parameter dictionary (unused). +/// +/// @param[in] opt +/// Options dictionary (unused). +/// +/// @return +/// The offset to the end of the 4D-Var analysis window. +/// +/// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException +/// If the key `iteration` is missing, cannot be converted to `long`, +/// or if any unexpected error occurs during deduction. +/// +/// @note +/// This deduction assumes that the offset is explicitly provided by +/// MARS and does not attempt any inference or defaulting. +/// +template +long resolve_IterationNumber_or_throw(const MarsDict_t& mars, const ParDict_t& par, const OptDict_t& opt) { + + using metkit::mars2grib::utils::dict_traits::get_or_throw; + using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; + + try { + + // Retrieve mandatory MARS iteration + auto iterationNumber = get_or_throw(mars, "iteration"); + + // Emit RESOLVE log entry + MARS2GRIB_LOG_RESOLVE([&]() { + std::string logMsg = "`iterationNumber` resolved from input dictionaries: value='"; + logMsg += std::to_string(iterationNumber) + "'"; + return logMsg; + }()); + + // Success exit point + return iterationNumber; + } + catch (...) { + + // Rethrow nested exceptions + std::throw_with_nested( + Mars2GribDeductionException("Failed to resolve `iterationNumber` from input dictionaries", Here())); + }; + + // Remove compiler warning + mars2gribUnreachable(); +}; + +} // namespace metkit::mars2grib::backend::deductions diff --git a/src/metkit/mars2grib/backend/deductions/totalNumberOfIterations.h b/src/metkit/mars2grib/backend/deductions/totalNumberOfIterations.h new file mode 100644 index 00000000..f15b8328 --- /dev/null +++ b/src/metkit/mars2grib/backend/deductions/totalNumberOfIterations.h @@ -0,0 +1,171 @@ +/* + * (C) Copyright 2025- ECMWF and individual contributors. + * + * 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. + */ + +/// +/// @file totalNumberOfIterations.h +/// @brief Deduction of the GRIB totalNumberOfIterations (in seconds). +/// +/// This header defines the deduction used by the mars2grib backend to resolve +/// the GRIB totalNumberOfIterations key from input dictionaries. +/// +/// The deduction reads the parameter dictionary entry totalNumberOfIterations, +/// interprets it as hours, and converts it to seconds. If the key is missing, +/// the deduction returns `std::nullopt`. +/// +/// Deductions are responsible for: +/// - extracting values from MARS, parameter, and option dictionaries +/// - applying explicit, deterministic deduction logic +/// - returning strongly typed values to concept operations +/// +/// Deductions: +/// - do NOT encode GRIB keys +/// - do NOT infer units or values beyond the documented rule +/// - do NOT perform GRIB table validation +/// +/// Error handling follows a strict fail-fast strategy: +/// - missing or malformed inputs cause immediate failure +/// - errors are reported using domain-specific deduction exceptions +/// - original errors are preserved via nested exception propagation +/// +/// Logging follows the mars2grib deduction policy: +/// - RESOLVE: value derived from the parameter dictionary +/// - DEFAULT: value defaulted to the GRIB missing code +/// +/// @section References +/// Concept: +/// - @ref analysisEncoding.h +/// +/// Related deductions: +/// - @ref offsetToEndOf4DvarWindow.h +/// +/// @ingroup mars2grib_backend_deductions +/// +#pragma once + +// System Include +#include +#include +#include + +// Other project includes +#include "eckit/log/Log.h" + +// Core deduction includes +#include "metkit/config/LibMetkit.h" +#include "metkit/mars2grib/utils/generalUtils.h" +#include "metkit/mars2grib/utils/logUtils.h" +#include "metkit/mars2grib/utils/mars2gribExceptions.h" + + +namespace metkit::mars2grib::backend::deductions { + +/// +/// @brief Resolve the GRIB `totalNumberOfIterations` expressed in seconds. +/// +/// This deduction determines the value of the GRIB `totalNumberOfIterations` +/// (in seconds) based on the parameter dictionary key `totalNumberOfIterations`. +/// +/// The deduction follows these rules: +/// +/// - If the key `totalNumberOfIterations` is present in the parameter dictionary, +/// its value is interpreted as **hours** and converted to seconds. +/// - If the key is absent, `std::nullopt` is used, which should be handled +/// by the encoding layer as the GRIB missing code (0xFFFF). +/// +/// @important +/// This deduction currently relies on **implicit assumptions** about +/// units and defaults that are not explicitly encoded in MARS metadata. +/// These assumptions are documented but not enforced via validation. +/// +/// @assumptions +/// - `par::totalNumberOfIterations` is expressed in **hours** +/// - Default value is `std::nullopt` when the key is missing +/// +/// @warning +/// - These assumptions may not be valid for all datasets. +/// - Relying on implicit defaults may lead to non-reproducible GRIB output +/// if upstream conventions change. +/// +/// @tparam MarsDict_t Type of the MARS dictionary (unused) +/// @tparam ParDict_t Type of the parameter dictionary +/// @tparam OptDict_t Type of the options dictionary (unused) +/// +/// @param[in] mars MARS dictionary (unused) +/// @param[in] par Parameter dictionary +/// @param[in] opt Options dictionary (unused) +/// +/// @return The length of time window in seconds. If `par::totalNumberOfIterations` is missing, +/// returns `std::nullopt`. +/// +/// @throws metkit::mars2grib::utils::exceptions::Mars2GribDeductionException +/// If: +/// - access to the parameter dictionary fails +/// - the retrieved value cannot be interpreted as a valid integer +/// - any unexpected error occurs during deduction +/// +/// @todo [owner: mds,dgov][scope: deduction][reason: correctness][prio: medium] +/// - Make the unit of `totalNumberOfIterations` explicit instead of assuming hours. +/// - Add explicit validation of allowed ranges and units. +/// +/// @note +/// - This deduction does not rely on any pre-existing GRIB header state. +/// - Logging intentionally emits RESOLVE/DEFAULT entries to highlight implicit assumptions. +/// + +template +std::optional resolve_TotalNumberOfIterations_opt(const MarsDict_t& mars, const ParDict_t& par, + const OptDict_t& opt) { + + + using metkit::mars2grib::utils::dict_traits::get_or_throw; + using metkit::mars2grib::utils::dict_traits::has; + using metkit::mars2grib::utils::exceptions::Mars2GribDeductionException; + + try { + + // Big assumption here: + // - totalNumberOfIterations is in hours + if (has(par, "totalNumberOfIterations")) { + long totalNumberOfIterationsVal = get_or_throw(par, "totalNumberOfIterations"); + + // Emit RESOLVE log entry + MARS2GRIB_LOG_RESOLVE([&]() { + std::string logMsg = "`totalNumberOfIterations` resolved from input dictionaries: value='"; + logMsg += std::to_string(totalNumberOfIterationsVal); + return logMsg; + }()); + + // Success exit point + return {totalNumberOfIterationsVal}; // Convert hours to seconds + } + else { + + // Emit DEFAULT log entry + MARS2GRIB_LOG_DEFAULT([&]() { + std::string logMsg = "`totalNumberOfIterations` defaulted to MISSING (nullopt)"; + return logMsg; + }()); + + // Success exit point + return std::nullopt; + } + } + catch (...) { + + // Rethrow nested exceptions + std::throw_with_nested( + Mars2GribDeductionException("Unable to get `totalNumberOfIterations` from Par dictionary", Here())); + }; + + // Remove compiler warning + mars2gribUnreachable(); +} + +} // namespace metkit::mars2grib::backend::deductions diff --git a/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section2Recipes.h b/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section2Recipes.h index 16fcb994..7b70c5db 100644 --- a/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section2Recipes.h +++ b/src/metkit/mars2grib/frontend/resolution/section-recipes/impl/section2Recipes.h @@ -32,6 +32,13 @@ inline const Recipe S2_R15 = Select >(); +// 4i related products +inline const Recipe S2_R20 = + make_recipe<20, + Select, + Select + >(); + // Satellite-related products inline const Recipe S2_R24 = make_recipe<24, @@ -46,6 +53,14 @@ inline const Recipe S2_R36 = Select >(); +// 4i Analysis-related products +inline const Recipe S2_R38 = + make_recipe<38, + Select, + Select, + Select + >(); + //------------------------------------------------------------------------------ // Virtual (encoder-specific) templates //------------------------------------------------------------------------------ @@ -79,8 +94,10 @@ inline const Recipes Section2Recipes{ 2, std::vector{ &S2_R1, &S2_R15, + &S2_R20, &S2_R24, &S2_R36, + &S2_R38, &S2_R1001, &S2_R1002 }