From dca0e2dfddf22b03fef1a4c3f1850a8acd6efa55 Mon Sep 17 00:00:00 2001 From: Kevin Nobel Date: Wed, 5 Aug 2026 15:41:03 +0000 Subject: [PATCH 1/3] mars2mars,mars2grib: Add CAMS wildfire mappings and composition matchers --- .../concepts/composition/compositionMatcher.h | 10 ++- .../point-in-time/pointInTimeMatcher.h | 2 +- .../concepts/statistics/statisticsMatcher.h | 6 +- .../mars2mars/mappings/rules/chemical.h | 62 +++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/metkit/mars2grib/backend/concepts/composition/compositionMatcher.h b/src/metkit/mars2grib/backend/concepts/composition/compositionMatcher.h index 80e84aac1..8a29c4f69 100644 --- a/src/metkit/mars2grib/backend/concepts/composition/compositionMatcher.h +++ b/src/metkit/mars2grib/backend/concepts/composition/compositionMatcher.h @@ -138,14 +138,20 @@ std::size_t compositionMatcher(const MarsDict_t& mars, const OptDict_t& opt) { return static_cast(CompositionType::Chem); } } + else if (matchAny(param, 435000, 435002, 436000, 436002, 437000, 437002)) { + if (matchAny(chem, 25)) { + return static_cast(CompositionType::Chem); + } + } else if (matchAny(param, 479000)) { if (matchAny(chem, 404)) { return static_cast(CompositionType::Chem); } } else if (matchAny(param, 469000)) { - if (matchAny(chem, 2, 5, 9, 10, 12, 16, 18, 19, 42, range(45, 48), 52, 99, 100, 129, 224, 226, 233, 311, - 933, 934)) { + if (matchAny(chem, 2, 5, 9, 10, 12, 16, 18, 19, 25, 31, 42, range(45, 49), 52, range(98, 100), 124, 126, + 129, 199, 224, 226, 233, 311, 334, 404, range(431, 433), range(438, 441), range(443, 446), + 922, 930, 933, 934, 948, 951)) { return static_cast(CompositionType::ChemicalSource); } } diff --git a/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h b/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h index fb77853c0..3bb8031eb 100644 --- a/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h +++ b/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h @@ -96,7 +96,7 @@ std::size_t pointInTimeMatcher(const MarsDict_t& mars, const OptDict_t& opt) { } // Chemical products - if (matchAny(param, range(228083, 228085), range(400000, 499999))) { + if (matchAny(param, range(228083, 228085)) || (matchAny(param, range(400000, 499999)) && (param % 10 == 0))) { return static_cast(PointInTimeType::Default); } diff --git a/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h b/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h index 984c51de8..be3c2b9c2 100644 --- a/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h +++ b/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h @@ -130,7 +130,11 @@ std::size_t statisticsMatcher(const MarsDict_t& mars, const OptDict_t& opt) { } // Chemical products - if (matchAny(param, range(228080, 228082), range(233032, 233035), range(235062, 235064))) { + // Note: In range 4xxxxy the following typeOfStatisticalProduct can be derived from the last digit: + // y == 1 -> mean; 2 -> accumulation; 3 -> maximum; 4 -> minimum; 5 -> standard deviation + // If the last digit is 0, the param is point-in-time. + if (matchAny(param, range(228080, 228082), range(233032, 233035), range(235062, 235064)) || + (matchAny(param, range(400000, 499999)) && (param % 10 == 2))) { return static_cast(StatisticsType::Accumulation); } diff --git a/src/metkit/mars2mars/mappings/rules/chemical.h b/src/metkit/mars2mars/mappings/rules/chemical.h index 900676b9e..b560564b8 100644 --- a/src/metkit/mars2mars/mappings/rules/chemical.h +++ b/src/metkit/mars2mars/mappings/rules/chemical.h @@ -111,14 +111,32 @@ inline void convertChemical(const InDict_t& in, OutDict_t& out, eckit::LocalConf return setParamChem(out, 400000, 930); case 210074: return setParamChem(out, 400000, 931); + case 210080: + return setParamChem(out, 469000, 25); case 210081: return setParamChem(out, 469000, 2); + case 210082: + return setParamChem(out, 469000, 404); + case 210083: + return setParamChem(out, 469000, 334); + case 210084: + return setParamChem(out, 469000, 199); case 210085: return setParamChem(out, 469000, 129); + case 210086: + return setParamChem(out, 469000, 31); + case 210087: + return setParamChem(out, 469000, 930); + case 210088: + return setParamChem(out, 469000, 922); + case 210089: + return setParamChem(out, 469000, 948); case 210090: return setParamChem(out, 469000, 934); case 210091: return setParamChem(out, 469000, 933); + case 210092: + return setParamChem(out, 469000, 951); case 210102: return setParamChem(out, 469000, 233); case 210103: @@ -133,8 +151,18 @@ inline void convertChemical(const InDict_t& in, OutDict_t& out, eckit::LocalConf return setParamChem(out, 469000, 48); case 210108: return setParamChem(out, 469000, 16); + case 210109: + return setParamChem(out, 469000, 49); + case 210110: + return setParamChem(out, 469000, 99); + case 210111: + return setParamChem(out, 469000, 124); + case 210112: + return setParamChem(out, 469000, 126); case 210113: return setParamChem(out, 469000, 5); + case 210114: + return setParamChem(out, 469000, 98); case 210115: return setParamChem(out, 469000, 52); case 210116: @@ -217,6 +245,28 @@ inline void convertChemical(const InDict_t& in, OutDict_t& out, eckit::LocalConf return setParamChemWavelength(out, 457000, 922, 1640.0); case 210230: return setParamChemWavelength(out, 457000, 922, 2130.0); + case 210231: + return setParamChem(out, 469000, 431); + case 210232: + return setParamChem(out, 469000, 432); + case 210233: + return setParamChem(out, 469000, 433); + case 210234: + return setParamChem(out, 469000, 439); + case 210235: + return setParamChem(out, 469000, 440); + case 210236: + return setParamChem(out, 469000, 441); + case 210237: + return setParamChem(out, 469000, 443); + case 210238: + return setParamChem(out, 469000, 438); + case 210239: + return setParamChem(out, 469000, 444); + case 210240: + return setParamChem(out, 469000, 445); + case 210241: + return setParamChem(out, 469000, 446); case 210247: return setParamChem(out, 402000, 912); case 210248: @@ -1221,6 +1271,18 @@ inline void convertChemical(const InDict_t& in, OutDict_t& out, eckit::LocalConf return setParamChem(out, 444000, 17); case 223033: return setParamChem(out, 444000, 33); + case 228080: + return setParamChem(out, 435002, 25); + case 228081: + return setParamChem(out, 436002, 25); + case 228082: + return setParamChem(out, 437002, 25); + case 228083: + return setParamChem(out, 435000, 25); + case 228084: + return setParamChem(out, 436000, 25); + case 228085: + return setParamChem(out, 437000, 25); default: break; From 2ad7eff02a871046cad406ecf5735e52b559482e Mon Sep 17 00:00:00 2001 From: Kevin Nobel Date: Fri, 7 Aug 2026 15:59:52 +0000 Subject: [PATCH 2/3] mars2grib: Improve point-in-time/statistics matching logic for CAMS params --- .../point-in-time/pointInTimeMatcher.h | 14 +++++++++- .../concepts/statistics/statisticsMatcher.h | 26 ++++++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h b/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h index 3bb8031eb..a0f6d5dcf 100644 --- a/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h +++ b/src/metkit/mars2grib/backend/concepts/point-in-time/pointInTimeMatcher.h @@ -95,8 +95,20 @@ std::size_t pointInTimeMatcher(const MarsDict_t& mars, const OptDict_t& opt) { return static_cast(PointInTimeType::Default); } + // Chemical products which have not been mapped + if (matchAny(param, range(228083, 228085))) { + return static_cast(PointInTimeType::Default); + } + // Chemical products - if (matchAny(param, range(228083, 228085)) || (matchAny(param, range(400000, 499999)) && (param % 10 == 0))) { + if (matchAny(param, range(400000, 499999))) { + if (param % 10 != 0) { + throw utils::exceptions::Mars2GribMatcherException( + "Chemical param " + std::to_string(param) + " with last digit " + std::to_string(param % 10) + + " is not a point-in-time product. Last digit must be 0 for point-in-time products in " + "400000 - 499999 param range.", + Here()); + } return static_cast(PointInTimeType::Default); } diff --git a/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h b/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h index be3c2b9c2..008d395fb 100644 --- a/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h +++ b/src/metkit/mars2grib/backend/concepts/statistics/statisticsMatcher.h @@ -129,13 +129,33 @@ std::size_t statisticsMatcher(const MarsDict_t& mars, const OptDict_t& opt) { return static_cast(StatisticsType::StandardDeviation); } + // Chemical products which have not been mapped + if (matchAny(param, range(228080, 228082), range(233032, 233035), range(235062, 235064))) { + return static_cast(StatisticsType::Accumulation); + } + // Chemical products // Note: In range 4xxxxy the following typeOfStatisticalProduct can be derived from the last digit: // y == 1 -> mean; 2 -> accumulation; 3 -> maximum; 4 -> minimum; 5 -> standard deviation // If the last digit is 0, the param is point-in-time. - if (matchAny(param, range(228080, 228082), range(233032, 233035), range(235062, 235064)) || - (matchAny(param, range(400000, 499999)) && (param % 10 == 2))) { - return static_cast(StatisticsType::Accumulation); + if (matchAny(param, range(400000, 499999))) { + switch (param % 10) { + case 1: + return static_cast(StatisticsType::Average); + case 2: + return static_cast(StatisticsType::Accumulation); + case 3: + return static_cast(StatisticsType::Maximum); + case 4: + return static_cast(StatisticsType::Minimum); + case 5: + return static_cast(StatisticsType::StandardDeviation); + default: + throw utils::exceptions::Mars2GribMatcherException( + "No typeOfStatisticalProcessing defined for chemical param " + std::to_string(param) + + " with last digit " + std::to_string(param % 10), + Here()); + } } // TODO: Don't handle products with timespan as non-statistical if they are not handled above! From af72565f8ee0703a3119fb44136fb69de8ff0b81 Mon Sep 17 00:00:00 2001 From: Kevin Nobel Date: Tue, 11 Aug 2026 07:58:40 +0000 Subject: [PATCH 3/3] grib2mars: Enable chem extractor --- src/metkit/grib2mars/mappings/rules/extract.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/metkit/grib2mars/mappings/rules/extract.h b/src/metkit/grib2mars/mappings/rules/extract.h index 453b922c0..d44c4c490 100644 --- a/src/metkit/grib2mars/mappings/rules/extract.h +++ b/src/metkit/grib2mars/mappings/rules/extract.h @@ -13,6 +13,7 @@ #include "metkit/grib2mars/mappings/rules/activity.h" #include "metkit/grib2mars/mappings/rules/anoffset.h" #include "metkit/grib2mars/mappings/rules/channel.h" +#include "metkit/grib2mars/mappings/rules/chem.h" #include "metkit/grib2mars/mappings/rules/class.h" #include "metkit/grib2mars/mappings/rules/dataset.h" #include "metkit/grib2mars/mappings/rules/date.h" @@ -60,6 +61,7 @@ const std::unordered_map>& extrac {"type", extractType}, {"expver", extractExpver}, {"param", extractParam}, + {"chem", extractChem}, {"levtype", extractLevtype}, {"levelist", extractLevelist}, {"frequency", extractFrequency},