From 7e4add8c3a86cf53a630041ccde8ae03154458f7 Mon Sep 17 00:00:00 2001 From: Emanuele Danovaro Date: Thu, 5 Mar 2026 09:06:44 +0100 Subject: [PATCH] gather levelist value as double - preserve numerical type if gathering native levelist --- src/metkit/codes/GRIBDecoder.cc | 33 +++++++++++- tests/CMakeLists.txt | 3 ++ tests/pl.grib | Bin 0 -> 3120 bytes tests/sol.grib | Bin 0 -> 7200 bytes tests/test_codes_decoder.cc | 88 ++++++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 tests/pl.grib create mode 100644 tests/sol.grib diff --git a/src/metkit/codes/GRIBDecoder.cc b/src/metkit/codes/GRIBDecoder.cc index e66ddb9f..9a4f6a58 100644 --- a/src/metkit/codes/GRIBDecoder.cc +++ b/src/metkit/codes/GRIBDecoder.cc @@ -37,6 +37,13 @@ bool GRIBDecoder::match(const eckit::message::Message& msg) const { (p[0] == 'B' and p[1] == 'U' and p[2] == 'D' and p[3] == 'G')); } +namespace { +bool isInteger(double val) { + double intpart; + return std::modf(val, &intpart) == 0.0; +} +} // namespace + void GRIBDecoder::getMetadata(const eckit::message::Message& msg, eckit::message::MetadataGatherer& gather, const eckit::message::GetMetadataOptions& options) const { @@ -59,14 +66,36 @@ void GRIBDecoder::getMetadata(const eckit::message::Message& msg, eckit::message switch (options.valueRepresentation) { case eckit::message::ValueRepresentation::String: { - gather.setValue(name, k.getString()); + // TODO - remove as soon as https://jira.ecmwf.int/browse/ECC-2113 is fixed + if (k.name() == "levelist") { + double val = k.getDouble(); + if (isInteger(val)) { + gather.setValue(name, eckit::translate(static_cast(val))); + } + else { + gather.setValue(name, eckit::translate(val)); + } + } + else { + gather.setValue(name, k.getString()); + } break; } case eckit::message::ValueRepresentation::Native: { std::visit( [&](auto&& v) { using Type = std::decay_t; - if constexpr (std::is_same_v || std::is_arithmetic_v) { + // TODO - remove as soon as https://jira.ecmwf.int/browse/ECC-2113 is fixed + if (k.name() == "levelist") { + double val = k.getDouble(); + if (isInteger(val)) { + gather.setValue(name, static_cast(val)); + } + else { + gather.setValue(name, val); + } + } + else if constexpr (std::is_same_v || std::is_arithmetic_v) { gather.setValue(name, std::forward(v)); } else if constexpr (std::is_same_v>) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 20e5a00b..719a4663 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,6 +25,9 @@ ecbuild_get_test_multidata( TARGET metkit_get_odb_data multiodb2.odb NOCHECK ) +configure_file(pl.grib pl.grib COPYONLY) +configure_file(sol.grib sol.grib COPYONLY) + add_custom_target(soft_link_expand_test_data ALL COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_SOURCE_DIR}/expand" "${CMAKE_CURRENT_BINARY_DIR}/expand") diff --git a/tests/pl.grib b/tests/pl.grib new file mode 100644 index 0000000000000000000000000000000000000000..1523bf956a91c938b282df8fad47559248567d45 GIT binary patch literal 3120 zcmZ<{@^t$DpMi-144628lqe%Z5(9%G10(w@W)_ezBM=KR0VzfXP8LQ30|P^#fCn>3 zY7vNF_>T&JB0@mS36o+uvIWFsFlYfXM4bOJFdFKDF-TAeYz2_=a z)P14$Q^t+Ii6sPRg}h@?vO#Toon7VCG(E2nGhS0y`zlWrk6#`=b~h~IwX@|D!bQ@P z8=sf?_UX5#&ZT0rD30Ws{7ZvZUoKUUTys3F%LsZ?|#^rUV>r|^Y=LY0X%2G&Hh+mXa zkY$~3UldsB-yl@`sqRV5+{&4y%q6kKJ1XT%b@F~^duQ9_2bOFoTUwpn7}Ap3mecXC z=XCeG_K22Ub!W@Z6zt6Q%Fs>Um^mfasmP%GPn|$>di%fbcYWo3v$}7$O{niO2LkuO zm<~V@J(>Vs8qvZiG9h?|V2czkLfr0(PXgU~82Mi2;3Zv#lgYbT5ymeTni3T971sj#UX=33Jw_@94t6E zI0!d`&Zv+p$aM-g81!j-GSA?|q-=_waom ze){o;*H;nx`#3nn+dHOTh*HD{kFhV`5-H>4=R!YIIC> zI-v%gQj^Z8Md#F}4t1$VeHze^E@(uTG^Q(>&^1lzhGuk2bGoAi-P4jDXhn~-rYG9a zGi~XGc1&!1$T2?RI43ydB&Rsd8P0N!^IYH}m$=Lou5yi!xy~ot;8Sk$8MpYH+uY$U z_qfjk9`Xf`_>#wb#S^~fDc|snZ+Xslyx@CY@&m8h7`O*xa6oJ(6e(v_a{WgtVjkda)vXvLvDXHn9#`H+zn$S>_n$ompG^;tyYe9=z z(y~^xsx>{^jWw1qB|oqJG7V`S=-9q)S2`#$iY zU--x`ee730@oS&@jnDkn=YHo4zxSm-_{txB?N7e(XW#mZ?*a-eI1FOJQ4kLjK^P>1 zRFDobK{m(*`JfOKgHliqDnS*06?`h;Oab5I@QVz7nZgwzu8iXjG2F=l@*VEH#S=Do z(i%@(;mJ#kV1bd$F`^kpHpK`h80i=z9%1A|%%G2%bTOkgX4b+Co0w?>Gp=LiHB_L2 zN|aEM0xFY3g)*pA3Ka`cxi~5qLnY10cBt$Y72crIYgBxN$}iD@1v)WDM`q~E6djtN zQ)6^&gw74o!9F_KMMvA{YzrN3qSFm@ypGP-pnwXLP=X=~P(}_4$v`P7C?{!^vGZx(#Qy;P57#-hkulaDEL0sDK0|5TO7v+8|pCglmFy4G^ym^3_1V3P@N25epz=4us5rlqnE11Ucg%XbdDZB*YG6-GZuGsgVB2M m6I~A?2BRE|_C=$8(P&>Z+82%XML*en(R~i?cTWG)zUW`05$@yw literal 0 HcmV?d00001 diff --git a/tests/test_codes_decoder.cc b/tests/test_codes_decoder.cc index 34c209e4..304ec960 100644 --- a/tests/test_codes_decoder.cc +++ b/tests/test_codes_decoder.cc @@ -16,6 +16,7 @@ #include #include "eckit/config/LocalConfiguration.h" +#include "eckit/io/FileHandle.h" #include "eckit/io/MemoryHandle.h" #include "eckit/message/Message.h" #include "eckit/message/Reader.h" @@ -454,6 +455,93 @@ CASE("test codessplitter unstr_latlot.tmpl String") { } } +CASE("test levelist String") { + eckit::FileHandle f("pl.grib"); + + eckit::message::Reader reader(f); + eckit::message::Message msg; + + std::vector expect{"0", "0.02", "0.2", "2", "20", "200"}; + for (size_t i = 0; i < expect.size(); ++i) { + msg = reader.next(); + EXPECT(msg); + + MetadataSetter md; + eckit::message::TypedSetter gatherer{md}; + eckit::message::GetMetadataOptions mdOpts{}; + mdOpts.valueRepresentation = eckit::message::ValueRepresentation::String; + msg.getMetadata(gatherer, mdOpts); + { MD_EXPECT_STRING(md, "levelist", expect[i]); } + } + msg = reader.next(); + EXPECT(!msg); +} + +CASE("test levelist Native") { + eckit::FileHandle f("pl.grib"); + + eckit::message::Reader reader(f); + eckit::message::Message msg; + + std::vector expect{0, 0.02, 0.2, 2, 20, 200}; + for (size_t i = 0; i < expect.size(); ++i) { + msg = reader.next(); + EXPECT(msg); + + MetadataSetter md; + eckit::message::TypedSetter gatherer{md}; + eckit::message::GetMetadataOptions mdOpts{}; + mdOpts.valueRepresentation = eckit::message::ValueRepresentation::Native; + msg.getMetadata(gatherer, mdOpts); + { MD_EXPECT_DOUBLE(md, "levelist", expect[i]); } + } + msg = reader.next(); + EXPECT(!msg); +} + +CASE("test levelist String") { + eckit::FileHandle f("sol.grib"); + + eckit::message::Reader reader(f); + eckit::message::Message msg; + + std::vector expect{"0.02", "0.2", "2", "20"}; + for (size_t i = 0; i < expect.size(); ++i) { + msg = reader.next(); + EXPECT(msg); + + MetadataSetter md; + eckit::message::TypedSetter gatherer{md}; + eckit::message::GetMetadataOptions mdOpts{}; + mdOpts.valueRepresentation = eckit::message::ValueRepresentation::String; + msg.getMetadata(gatherer, mdOpts); + { MD_EXPECT_STRING(md, "levelist", expect[i]); } + } + msg = reader.next(); + EXPECT(!msg); +} + +CASE("test levelist Native") { + eckit::FileHandle f("sol.grib"); + + eckit::message::Reader reader(f); + eckit::message::Message msg; + + std::vector expect{0.02, 0.2, 2, 20}; + for (size_t i = 0; i < expect.size(); ++i) { + msg = reader.next(); + EXPECT(msg); + + MetadataSetter md; + eckit::message::TypedSetter gatherer{md}; + eckit::message::GetMetadataOptions mdOpts{}; + mdOpts.valueRepresentation = eckit::message::ValueRepresentation::Native; + msg.getMetadata(gatherer, mdOpts); + { MD_EXPECT_DOUBLE(md, "levelist", expect[i]); } + } + msg = reader.next(); + EXPECT(!msg); +} //----------------------------------------------------------------------------------------------------------------------